Skip to content

Extension Anatomy โ€‹

Let's dive deep into the fundamental components that make up a KIMU extension.

๐Ÿ”ง The Three Pillars โ€‹

Every KIMU extension consists of three main elements:

  1. @KimuComponent Decorator - Metadata and configuration
  2. Component Class - Logic, state, and lifecycle
  3. HTML Template - Dynamic user interface

1. ๐Ÿ“‹ @KimuComponent Decorator โ€‹

The decorator is the heart of configuration. It defines all extension metadata:

typescript
@KimuComponent({
  tag: 'my-extension',          // โœ… Required: unique Web Component name
  name: 'My Extension',         // โœ… UI name
  version: '1.0.0',           // โœ… Semantic versioning
  description: 'Description',  // Feature description
  icon: '๐ŸŽฏ',                  // Emoji/unicode icon
  author: 'Developer',         // Author or team
  path: 'my-extension',        // Folder path (default: tag)
  internal: false,             // true = system, false = user
  kimuVersion: '1.0.0',        // Required KIMU version
  dependencies: ['child-ext-1', 'child-ext-2'] // Tags of child extensions
})

Decorator Properties โ€‹

PropertyTypeRequiredDescription
tagstringโœ…Unique Web Component name (kebab-case)
namestringโœ…Descriptive name for the interface
versionstringโœ…Semantic version (e.g., "1.2.3")
descriptionstringโŒFeature description
iconstringโŒEmoji or unicode icon
authorstringโŒAuthor or development team
pathstringโŒFolder path (default: tag)
internalbooleanโŒIf true, hidden from users
kimuVersionstringโŒMinimum required KIMU version
dependenciesstring[]โŒArray of child extension tags

๐Ÿ”— Metadata Dependencies โ€‹

The dependencies metadata is essential for building composite extensions. It is an array of strings containing the tags of child extensions included in the parent extension.

How it works:

  • If your extension is a "parent" and contains other extensions as components, specify their tags in the dependencies field.
  • Child extensions will be automatically loaded and made available in the HTML template (view.html).
  • You can use child extensions as regular HTML tags inside your template.

Practical example:

typescript
@KimuComponent({
  tag: 'dashboard-parent',
  name: 'Complete Dashboard',
  version: '1.0.0',
  dependencies: ['chart-widget', 'data-table', 'filter-panel'] // Child extensions
})
export class DashboardParent extends KimuComponentElement {
  // Parent component logic
}

In the view.html template:

html
<div class="dashboard">
  <h2>Interactive Dashboard</h2>
  <!-- Use child extensions as HTML tags -->
  <chart-widget data="${chartData}"></chart-widget>
  <data-table items="${tableItems}"></data-table>
  <filter-panel @filter="${onFilter}"></filter-panel>
</div>

Advantages:

  • โœ… Modularity: each component is independent
  • โœ… Reusability: child extensions can be used elsewhere
  • โœ… Maintainability: separate updates for each module
  • โœ… Automatic loading: no need to manually manage dependencies

Best practices:

  • Include only necessary dependencies
  • Always document the role of each child extension
  • Use descriptive tag names for dependencies

2. ๐Ÿš€ Component Class โ€‹

The main logic extends KimuComponentElement and implements the extension behavior:

typescript
import { KimuComponentElement } from '../core/kimu-component-element';

@KimuComponent({
  tag: 'my-calculator',
  name: 'Smart Calculator',
  version: '1.0.0',
  description: 'Calculator with history',
  icon: '๐Ÿงฎ',
  author: 'KIMU Team'
})
export class CalculatorExtension extends KimuComponentElement {
  // Private properties
  private result: number = 0;
  private history: string[] = [];
  
  // Component lifecycle
  onInit() {
    console.log('Calculator initialized');
    this.loadSettings();
  }
  
  onRender() {
    this.updateDisplay();
    this.bindEventListeners();
  }
  
  onDestroy() {
    this.saveSettings();
  }
  
  // Utility methods
  private loadSettings() {
    const saved = localStorage.getItem('calc-settings');
    if (saved) {
      this.history = JSON.parse(saved);
    }
  }
  
  private saveSettings() {
    localStorage.setItem('calc-settings', JSON.stringify(this.history));
  }
  
  private updateDisplay() {
    const display = this.$('#display');
    if (display) {
      display.textContent = this.result.toString();
    }
  }
  
  private bindEventListeners() {
    this.$('#btn-add')?.addEventListener('click', () => this.add());
    this.$('#btn-clear')?.addEventListener('click', () => this.clear());
  }
  
  // Public methods
  add() {
    // Addition logic
    this.result += 1;
    this.onRender();
  }
  
  clear() {
    this.result = 0;
    this.history = [];
    this.onRender();
  }
}

Component Properties โ€‹

The class can contain various types of properties:

TypePurposeExample
Private PropertiesInternal stateprivate count: number = 0
Public PropertiesExternal APIpublic isVisible: boolean = true
Static PropertiesShared constantsstatic readonly MAX_VALUE = 100
Getters/SettersComputed propertiesget formattedValue() { return... }

Lifecycle Methods โ€‹

MethodWhen CalledPurpose
onInit()Component creationInitialization, load settings
onRender()DOM updateUpdate interface, bind events
onDestroy()Component removalCleanup, save settings

Utility Methods โ€‹

KIMU provides built-in utility methods for common operations:

MethodPurposeExample
this.$('#id')Element selectionthis.$('#button')
this.$$('.class')Multiple selectionthis.$$('.item')
this.on('event', handler)Event registrationthis.on('click', this.onClick)
this.off('event', handler)Event removalthis.off('click', this.onClick)
this.emit('event', data)Event emissionthis.emit('update', { value: 10 })
this.render()Force re-renderthis.render()

๐Ÿ“Š Data Binding with getData() โ€‹

The getData() method is the heart of data binding. It returns an object containing all data to pass to the HTML template:

typescript
export class MyExtension extends KimuComponentElement {
  
  // ๐Ÿ” Private extension state
  private counter = 0;
  private isActive = true;

  // ๐Ÿ“Š Main method: connects data to template
  getData() {
    return {
      // Simple data
      counter: this.counter,
      isActive: this.isActive,
      
      // Event handlers
      onIncrement: () => {
        this.counter++;
        this.refresh(); // Re-render extension
      },
      
      onToggle: () => {
        this.isActive = !this.isActive;
        this.refresh();
      }
    };
  }

  // ๐Ÿ”„ Lifecycle hooks
  onInit(): void {
    console.log('Extension initialized');
  }

  onRender(): void {
    console.log('Extension rendered');
  }

  onDestroy(): void {
    console.log('Extension disposed');
  }
}

Key benefits of getData():

  • โœ… Reactive: data changes trigger automatic re-renders
  • โœ… Clean: separates data from template logic
  • โœ… Flexible: supports any JavaScript data type
  • โœ… Safe: data is encapsulated in the component

3. ๐ŸŽจ HTML Template โ€‹

The HTML template (view.html) defines the dynamic user interface with special template syntax:

html
<div class="extension-container">
  <!-- ๐Ÿ“ Simple interpolation -->
  <h3>${name}</h3>
  <p>Counter value: ${counter}</p>
  
  <!-- ๐Ÿ”˜ Event binding -->
  <button @click=${onIncrement} class="btn-primary">
    + Increment
  </button>
  
  <button @click=${onToggle} class="btn-secondary">
    Toggle: ${isActive ? 'ON' : 'OFF'}
  </button>
  
  <!-- ๐Ÿ”€ Conditional rendering -->
  ${isActive ? `
    <div class="status-active">
      <h4>๐ŸŸข Status: Active</h4>
      <p>The component is currently active and processing data.</p>
      
      <!-- ๐Ÿ” Lists and iteration -->
      <ul class="feature-list">
        ${features.map(feature => `
          <li class="feature-item">
            <span class="icon">${feature.icon}</span>
            <span class="name">${feature.name}</span>
          </li>
        `).join('')}
      </ul>
    </div>
  ` : `
    <div class="status-inactive">
      <h4>๐Ÿ”ด Status: Inactive</h4>
      <p>The component is currently inactive.</p>
    </div>
  `}
  
  <!-- ๐Ÿ“‹ Form inputs with two-way binding -->
  <div class="input-section">
    <label for="user-input">Enter value:</label>
    <input 
      id="user-input"
      type="text" 
      value="${inputValue}"
      @input=${onInputChange}
      placeholder="Type something..."
    />
  </div>
  
  <!-- ๐ŸŽฏ Child components usage -->
  ${dependencies.length > 0 ? `
    <div class="child-components">
      <h4>Child Components:</h4>
      <!-- Use child extensions as HTML tags -->
      <chart-widget data="${chartData}"></chart-widget>
      <data-table items="${tableItems}"></data-table>
    </div>
  ` : ''}
</div>

Template Syntax โ€‹

KIMU uses a modern, powerful template syntax:

SyntaxPurposeExample
${expression}Data interpolation${user.name}
@event=${handler}Event binding@click=${onClick}
${condition ? `html` : `other`}Conditional rendering${isVisible ? 'Show' : 'Hide'}
${array.map(...).join('')}List rendering${items.map(item => ...)}
<child-tag></child-tag>Child components<my-widget></my-widget>

Advanced Template Examples โ€‹

Conditional classes:

html
<div class="item ${isActive ? 'active' : 'inactive'}">
  Content
</div>

Dynamic attributes:

html
<input 
  type="text" 
  disabled="${isReadonly}"
  placeholder="${placeholderText}"
/>

Complex iterations:

html
${users.filter(u => u.active).map(user => `
  <div class="user-card">
    <img src="${user.avatar}" alt="${user.name}" />
    <h4>${user.name}</h4>
    <p>${user.role}</p>
    <button @click=${() => onUserSelect(user.id)}>
      Select
    </button>
  </div>
`).join('')}

๐Ÿ”— Complete Integration Example โ€‹

Here's how all three components work together in a practical calculator:

Decorator + Class: โ€‹

typescript
@KimuComponent({
  tag: 'smart-calculator',
  name: 'Smart Calculator',
  version: '2.1.0',
  description: 'Calculator with history and memory',
  icon: '๐Ÿงฎ',
  author: 'KIMU Team'
})
export class SmartCalculator extends KimuComponentElement {
  private currentValue: number = 0;
  private operation: string = '';
  private previousValue: number = 0;
  private history: string[] = [];

  getData() {
    return {
      // Data
      currentValue: this.currentValue,
      previousValue: this.previousValue,
      operation: this.operation,
      history: this.history,
      hasHistory: this.history.length > 0,
      
      // Event handlers
      onNumberClick: (num: number) => {
        this.currentValue = this.currentValue * 10 + num;
        this.refresh();
      },
      
      onOperationClick: (op: string) => {
        this.operation = op;
        this.previousValue = this.currentValue;
        this.currentValue = 0;
        this.refresh();
      },
      
      onEqualsClick: () => {
        const result = this.calculate();
        this.history.push(`${this.previousValue} ${this.operation} ${this.currentValue} = ${result}`);
        this.currentValue = result;
        this.operation = '';
        this.previousValue = 0;
        this.refresh();
      },
      
      onClearClick: () => {
        this.currentValue = 0;
        this.operation = '';
        this.previousValue = 0;
        this.refresh();
      },
      
      onClearHistoryClick: () => {
        this.history = [];
        this.refresh();
      }
    };
  }
  
  private calculate(): number {
    switch (this.operation) {
      case '+': return this.previousValue + this.currentValue;
      case '-': return this.previousValue - this.currentValue;
      case '*': return this.previousValue * this.currentValue;
      case '/': return this.currentValue !== 0 ? this.previousValue / this.currentValue : 0;
      default: return this.currentValue;
    }
  }
}

Template: โ€‹

html
<div class="smart-calculator">
  <!-- Display -->
  <div class="display">
    <div class="current-value">${currentValue}</div>
    <div class="operation-display">
      ${operation ? `${previousValue} ${operation}` : ''}
    </div>
  </div>
  
  <!-- Number buttons -->
  <div class="buttons-grid">
    ${[7,8,9,4,5,6,1,2,3,0].map(num => `
      <button class="btn-number" @click=${() => onNumberClick(num)}>
        ${num}
      </button>
    `).join('')}
    
    <!-- Operation buttons -->
    <button class="btn-operation" @click=${() => onOperationClick('+')}>+</button>
    <button class="btn-operation" @click=${() => onOperationClick('-')}>-</button>
    <button class="btn-operation" @click=${() => onOperationClick('*')}>ร—</button>
    <button class="btn-operation" @click=${() => onOperationClick('/')}>/</button>
    
    <!-- Action buttons -->
    <button class="btn-action" @click=${onEqualsClick}>=</button>
    <button class="btn-action" @click=${onClearClick}>C</button>
  </div>
  
  <!-- History -->
  ${hasHistory ? `
    <div class="history-section">
      <h4>Calculation History</h4>
      <ul class="history-list">
        ${history.map(entry => `
          <li class="history-item">${entry}</li>
        `).join('')}
      </ul>
      <button class="btn-clear-history" @click=${onClearHistoryClick}>
        Clear History
      </button>
    </div>
  ` : ''}
</div>

๐Ÿ”— How It All Works Together โ€‹

  1. ๐Ÿท๏ธ Registration: @KimuComponent registers the extension in the KIMU system
  2. ๐Ÿ—๏ธ Instantiation: KIMU creates a class instance when the extension is requested
  3. ๐ŸŽจ Rendering: HTML template is populated with data from getData()
  4. โšก Interaction: Event handlers manage user actions and update the state
  5. ๐Ÿ”„ Updates: refresh() re-renders the interface when state changes
  6. ๐Ÿงน Cleanup: onDestroy() is called when the extension is removed

โœจ Best Practices โ€‹

Structure โ€‹

  • โœ… Keep the decorator metadata complete and descriptive
  • โœ… Use TypeScript for type safety in the component class
  • โœ… Separate concerns: logic in class, presentation in template
  • โœ… Use meaningful names for properties and methods

Performance โ€‹

  • โœ… Minimize the frequency of refresh() calls
  • โœ… Use conditional rendering for complex sections
  • โœ… Avoid heavy computations in getData()
  • โœ… Implement proper cleanup in onDestroy()

Maintainability โ€‹

  • โœ… Document complex logic with comments
  • โœ… Use consistent naming conventions
  • โœ… Break down large components into smaller ones
  • โœ… Test extension behavior thoroughly

๐Ÿ“š Next Steps โ€‹

Now that you understand the complete anatomy, explore:

Released under Creative Commons Attribution 4.0 International (CC BY 4.0)