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;