diff --git a/frontend/src/modules/Gefest/README.md b/frontend/src/modules/Gefest/README.md index d0f2563..ea6744d 100644 --- a/frontend/src/modules/Gefest/README.md +++ b/frontend/src/modules/Gefest/README.md @@ -5,13 +5,16 @@ Gefest is a lightweight, TypeScript-based framework for building and managing HT ## Features - **Class-based Element Creation**: Define HTML elements as TypeScript classes -- **Attribute Management**: Easy setting, getting, and removing of HTML attributes -- **CSS Class Handling**: Add and remove classes dynamically -- **Style System**: Extensible styling with custom style classes -- **Event Handling**: Built-in click event management with automatic re-rendering -- **Element Registration**: Automatic ID generation and DOM integration -- **Primitive Components**: Pre-built HTML element wrappers -- **Dynamic Updates**: Re-render elements on the fly +- **Attribute Management**: Easy setting, getting, and removing of HTML attributes with reserved attribute validation +- **CSS Class Handling**: Add and remove classes dynamically with full control over class lists +- **Style System**: Extensible styling with custom style classes and inline personal styles +- **Event Handling**: Multiple event listeners with custom event emission and data passing +- **Element Registration**: Automatic ID generation and DOM integration with unique identifiers +- **Primitive Components**: Pre-built HTML element wrappers for common elements +- **Dynamic Updates**: Re-render elements on the fly with automatic DOM synchronization +- **HTML Parsing**: Convert HTML strings to Gefest elements with `fromHTML()` +- **Personal Inline Styling**: Set and manage inline CSS styles directly on elements +- **Async Event System**: Promise-based event handling with custom event data ## Installation @@ -217,6 +220,117 @@ Convert existing HTML strings to Gefest elements: const element = GefestElement.fromHTML(''); ``` +### Personal Styling + +Set inline styles directly on elements: + +```typescript +const button = new GefestButton("Styled"); +button.setPersonalStyle('background-color: blue; color: white; padding: 10px;'); +``` + +Remove personal styles: + +```typescript +button.setPersonalStyle(null); // Removes inline styles +``` + +### Event System + +#### Using onClick Handler + +```typescript +const button = new GefestButton("Click me"); +button.onClick = () => { + console.log('Button was clicked!'); +}; +``` + +#### Registering Multiple Event Listeners + +Use the `on()` method to register multiple event handlers for the same event: + +```typescript +const button = new GefestButton("Multi-Listener"); + +// First listener +button.on("onClick", (data) => { + console.log('Handler 1 called', data); +}); + +// Second listener +button.on("onClick", (data) => { + console.log('Handler 2 called', data); +}); +``` + +#### Registering One-Time Event Listeners + +Use the `once()` method to register an event handler that only fires once: + +```typescript +const button = new GefestButton("One-Time"); + +// This handler will only execute on the first click +button.once("onClick", (data) => { + console.log('First click only!', data); + // Handler is automatically removed after first execution +}); +``` + +#### Emitting Events + +Emit custom events with optional data: + +```typescript +const button = new GefestButton("Action"); + +button.on("onClick", (data) => { + console.log('Click event with data:', data); +}); + +button.onClick = () => { + // onClick handler is automatically emitted with event data + button.emit("onClick", { timestamp: Date.now(), action: 'clicked' }); +}; +``` + +### Element Re-rendering + +Trigger manual re-renders when element state changes: + +```typescript +const button = new GefestButton("Click me"); +let clickCount = 0; + +button.onClick = () => { + clickCount++; + // Update content or attributes + button.update(); // Re-renders in the DOM +}; +``` + +### Attribute Management + +Full control over element attributes: + +```typescript +const button = new GefestButton("Submit"); + +// Set attribute +button.setAttribute('type', 'submit'); +button.setAttribute('aria-label', 'Submit form'); + +// Get attribute +const type = button.getAttribute('type'); // 'submit' + +// Remove attribute +button.removeAttribute('disabled'); + +// Check for reserved attributes (will throw error) +// button.setAttribute('class', 'btn'); // Error: 'class' is reserved +``` + ## API Reference ### GefestElement @@ -237,7 +351,11 @@ const element = GefestElement.fromHTML(''); - `addClass(className: string): void` - `removeClass(className: string): void` - `getClassList(): string[]` -- `setPersonalStyle(style: string | null): void` +- `setPersonalStyle(style: string | null): void` - Set inline styles on the element +- `emit(event: GefestElementEvents, data?: unknown): void` - Emit custom events +- `on(event: GefestElementEvents, handler: (data: unknown) => void): void` - Register event listener +- `once(event: GefestElementEvents, handler: (data: unknown) => void): void` - Register one-time event listener +- `checkReservedAttribute(key: string): void` - Validate if attribute key is reserved #### Static Methods - `fromHTML(html: string): GefestElement` diff --git a/frontend/src/modules/Gefest/element.ts b/frontend/src/modules/Gefest/element.ts index 37c3fb8..f9c4021 100644 --- a/frontend/src/modules/Gefest/element.ts +++ b/frontend/src/modules/Gefest/element.ts @@ -2,7 +2,7 @@ import { GefestEngine } from './engine'; import { GefestStyle } from './style'; // The general class of Gefest -type gefestElementEvents = "onClick"; +type GefestElementEvents = "onClick"; export abstract class GefestElement { style: GefestStyle | null = null; isHidden: boolean = false; @@ -11,16 +11,34 @@ export abstract class GefestElement { protected content: (GefestElement | string)[] | string; protected attributes: Record = {}; protected classList: Set = new Set(); - //protected eventHandlers: Record void> = {}; - protected eventHandlers: ((eventType: string, eventData: unknown) => void)[] = []; + protected eventHandlers: ((eventType: GefestElementEvents, eventData: unknown) => void)[] = []; protected clickHandler: (() => void) | null = null; - emit(event: gefestElementEvents, data: unknown = undefined) { + emit(event: GefestElementEvents, data: unknown = undefined) { for (let handler of this.eventHandlers) new Promise((rs) => rs(handler(event, data))); } + on(event: GefestElementEvents, handler: (data : unknown) => void) { + this.eventHandlers.push((calledEvent: GefestElementEvents, calledData: unknown) => { + if (calledEvent === event) + return handler(calledData); + }); + } + + once(event: GefestElementEvents, handler: (data : unknown) => void) { + const addedHandler = (calledEvent: GefestElementEvents, calledData: unknown) => { + if (calledEvent === event) { + const index = this.eventHandlers.indexOf(addedHandler); + this.eventHandlers.splice(index, 1); + return handler(calledData); + } + }; + + this.eventHandlers.push(addedHandler); + } + set onClick(handler: (() => void) | null) { if (handler) { this.clickHandler = handler;