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:
@KimuComponentDecorator - Metadata and configuration- Component Class - Logic, state, and lifecycle
- HTML Template - Dynamic user interface
1. ๐ @KimuComponent Decorator โ
The decorator is the heart of configuration. It defines all extension metadata:
@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 โ
| Property | Type | Required | Description |
|---|---|---|---|
tag | string | โ | Unique Web Component name (kebab-case) |
name | string | โ | Descriptive name for the interface |
version | string | โ | Semantic version (e.g., "1.2.3") |
description | string | โ | Feature description |
icon | string | โ | Emoji or unicode icon |
author | string | โ | Author or development team |
path | string | โ | Folder path (default: tag) |
internal | boolean | โ | If true, hidden from users |
kimuVersion | string | โ | Minimum required KIMU version |
dependencies | string[] | โ | 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
dependenciesfield. - 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:
@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:
<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:
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:
| Type | Purpose | Example |
|---|---|---|
| Private Properties | Internal state | private count: number = 0 |
| Public Properties | External API | public isVisible: boolean = true |
| Static Properties | Shared constants | static readonly MAX_VALUE = 100 |
| Getters/Setters | Computed properties | get formattedValue() { return... } |
Lifecycle Methods โ
| Method | When Called | Purpose |
|---|---|---|
onInit() | Component creation | Initialization, load settings |
onRender() | DOM update | Update interface, bind events |
onDestroy() | Component removal | Cleanup, save settings |
Utility Methods โ
KIMU provides built-in utility methods for common operations:
| Method | Purpose | Example |
|---|---|---|
this.$('#id') | Element selection | this.$('#button') |
this.$$('.class') | Multiple selection | this.$$('.item') |
this.on('event', handler) | Event registration | this.on('click', this.onClick) |
this.off('event', handler) | Event removal | this.off('click', this.onClick) |
this.emit('event', data) | Event emission | this.emit('update', { value: 10 }) |
this.render() | Force re-render | this.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:
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:
<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:
| Syntax | Purpose | Example |
|---|---|---|
${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:
<div class="item ${isActive ? 'active' : 'inactive'}">
Content
</div>Dynamic attributes:
<input
type="text"
disabled="${isReadonly}"
placeholder="${placeholderText}"
/>Complex iterations:
${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: โ
@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: โ
<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 โ
- ๐ท๏ธ Registration:
@KimuComponentregisters the extension in the KIMU system - ๐๏ธ Instantiation: KIMU creates a class instance when the extension is requested
- ๐จ Rendering: HTML template is populated with data from
getData() - โก Interaction: Event handlers manage user actions and update the state
- ๐ Updates:
refresh()re-renders the interface when state changes - ๐งน 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:
- Development Patterns - Common patterns for different extension types
- Advanced Templates - Advanced templating techniques and optimizations
- Lifecycle Management - In-depth component lifecycle and state management
- Component Communication - How components interact with each other