Update README.md for Gefest. And.. I meant at the last commit: I got tired for describing all changes
This commit is contained in:
parent
a4c5b441f6
commit
56a5639c57
@ -1,361 +1,100 @@
|
||||
# Gefest Framework
|
||||
|
||||
Gefest is a lightweight, TypeScript-based framework for building and managing HTML elements programmatically. It provides a class-based approach to create reusable UI components with built-in support for attributes, classes, styles, and event handling.
|
||||
Gefest is a lightweight TypeScript framework for building HTML UI components with a class-based API. It makes it easy to create reusable elements, bind events, manage styles and classes, and rerender elements dynamically.
|
||||
|
||||
## Features
|
||||
## Overview
|
||||
|
||||
- **Class-based Element Creation**: Define HTML elements as TypeScript classes
|
||||
- **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
|
||||
Gefest provides:
|
||||
- A base `GefestElement` class for elements with nested content and attributes
|
||||
- Automatic `data-gefest-id` registration for DOM element tracking
|
||||
- Class and style management via helper methods
|
||||
- Event handling with `onClick`, `on`, `once`, and `emit`
|
||||
- Expression of UI elements as TypeScript classes instead of raw HTML strings
|
||||
- A simple engine that binds click handlers and supports rerendering
|
||||
|
||||
## Installation
|
||||
|
||||
Gefest is included as a module in this project. To use it in your TypeScript files:
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { GefestEngine, GefestElement } from './engine';
|
||||
import { GefestEngine } from './engine';
|
||||
import { GefestButton } from './primitives/button';
|
||||
// ... other imports as needed
|
||||
|
||||
async function main() {
|
||||
const button = new GefestButton('Click me');
|
||||
button.addClass('btn');
|
||||
button.onClick = () => {
|
||||
alert('Button clicked');
|
||||
};
|
||||
|
||||
document.body.innerHTML = button.build();
|
||||
}
|
||||
|
||||
GefestEngine.main(main);
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### GefestElement
|
||||
|
||||
The base abstract class for all Gefest elements. It provides:
|
||||
|
||||
- Content management (strings or nested elements)
|
||||
- Attribute handling
|
||||
- Class management
|
||||
- Style application
|
||||
- Event binding
|
||||
- Automatic re-rendering on updates
|
||||
The base class for all Gefest components. It handles:
|
||||
- Content rendering from strings or nested `GefestElement` instances
|
||||
- HTML attributes and reserved attribute protection
|
||||
- CSS class list management
|
||||
- Inline personal styles
|
||||
- Event registration and emission
|
||||
- Unique element registration via `GefestEngine`
|
||||
- Automatic rerendering when `update()` is called
|
||||
|
||||
### GefestEngine
|
||||
|
||||
Manages the lifecycle of Gefest elements:
|
||||
|
||||
- Registers elements with unique IDs
|
||||
- Activates event handlers on DOM elements
|
||||
- Provides the main entry point for applications
|
||||
- Handles re-rendering of updated elements
|
||||
The runtime engine that manages registered elements.
|
||||
- `register(element)` assigns a unique `data-gefest-id`
|
||||
- `activateOnClick()` binds click handlers for registered elements
|
||||
- `main(mainFn)` runs your app and activates click bindings
|
||||
- `render(gefestId)` rerenders one or all registered elements
|
||||
|
||||
### GefestStyle
|
||||
|
||||
Abstract base class for styling systems. Implement custom styles by extending this class.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Elements
|
||||
|
||||
```typescript
|
||||
import { GefestButton } from './primitives/button';
|
||||
|
||||
// Create a simple button
|
||||
const button = new GefestButton("Click me!");
|
||||
console.log(button.build()); // <button data-gefest-id="gefest-abc123">Click me!</button>
|
||||
```
|
||||
|
||||
### Setting Attributes
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Submit");
|
||||
button.setAttribute('type', 'submit');
|
||||
button.setAttribute('disabled', 'true');
|
||||
```
|
||||
|
||||
### Managing Classes
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Button");
|
||||
button.addClass('btn');
|
||||
button.addClass('btn-primary');
|
||||
button.removeClass('btn');
|
||||
console.log(button.getClassList()); // ['btn-primary']
|
||||
```
|
||||
|
||||
### Hiding Elements
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Hidden Button");
|
||||
button.isHidden = true; // Sets the 'hidden' attribute on the element
|
||||
```
|
||||
|
||||
### Event Handling
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Click me!");
|
||||
button.onClick = () => {
|
||||
console.log('Button clicked!');
|
||||
// The framework automatically re-renders after click
|
||||
};
|
||||
```
|
||||
|
||||
### Updating Elements
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Initial");
|
||||
button.update(); // Re-renders the element in the DOM
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
|
||||
```typescript
|
||||
import { GefestEngine } from './engine';
|
||||
|
||||
async function main() {
|
||||
// Create and configure your elements here
|
||||
const button = new GefestButton("Hello World");
|
||||
button.onClick = () => alert('Hello!');
|
||||
|
||||
// Insert into DOM
|
||||
document.body.innerHTML = button.build();
|
||||
}
|
||||
|
||||
GefestEngine.main(main);
|
||||
```
|
||||
A base class for custom styling strategies. Extend `GefestStyle` and implement `applyStyle(html, element)` to transform rendered HTML.
|
||||
|
||||
## Primitives
|
||||
|
||||
Gefest includes several pre-built primitive elements:
|
||||
|
||||
### GefestButton
|
||||
|
||||
Wraps HTML `<button>` elements.
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Click me");
|
||||
button.setAttribute('type', 'submit');
|
||||
```
|
||||
|
||||
### GefestA
|
||||
|
||||
Wraps HTML `<a>` elements.
|
||||
|
||||
```typescript
|
||||
const link = new GefestA("Visit site", "https://example.com");
|
||||
link.setAttribute('target', '_blank');
|
||||
```
|
||||
|
||||
### GefestImg
|
||||
|
||||
Wraps HTML `<img>` elements.
|
||||
|
||||
```typescript
|
||||
const image = new GefestImg("", "path/to/image.jpg");
|
||||
image.setAttribute('alt', 'Description');
|
||||
```
|
||||
|
||||
### Other Primitives
|
||||
|
||||
- `GefestP`: Paragraph (`<p>`)
|
||||
- `GefestSpan`: Span (`<span>`)
|
||||
- `GefestHeader`: Header (`<h1>`)
|
||||
- `GefestI`: Italic (`<i>`)
|
||||
- `GefestSmall`: Small text (`<small>`)
|
||||
- `GefestCenter`: Centered content (`<center>`)
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Nested Elements
|
||||
|
||||
```typescript
|
||||
const link = new GefestA("Read more", "#");
|
||||
const button = new GefestButton([link, " about this"]);
|
||||
```
|
||||
|
||||
### Custom Elements
|
||||
|
||||
Create your own elements by extending `GefestElement`:
|
||||
|
||||
```typescript
|
||||
import { GefestElement } from './element';
|
||||
|
||||
export class GefestDiv extends GefestElement {
|
||||
protected wrapHTML(content: string): string {
|
||||
const attrString = this.attributesToString();
|
||||
return `<div ${attrString}>${content}</div>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Implement custom styling by extending `GefestStyle`:
|
||||
|
||||
```typescript
|
||||
import { GefestStyle, GefestElement } from './style';
|
||||
|
||||
export class BootstrapStyle extends GefestStyle {
|
||||
applyStyle(html: string, element?: GefestElement): string {
|
||||
// Apply Bootstrap classes or inline styles
|
||||
if (element?.getClassList().includes('btn')) {
|
||||
return html.replace('<button', '<button class="btn btn-primary"');
|
||||
}
|
||||
return html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Apply styles to elements:
|
||||
|
||||
```typescript
|
||||
const button = new GefestButton("Styled Button");
|
||||
button.style = new BootstrapStyle();
|
||||
```
|
||||
|
||||
### Parsing HTML
|
||||
|
||||
Convert existing HTML strings to Gefest elements:
|
||||
|
||||
```typescript
|
||||
const element = GefestElement.fromHTML('<button class="btn">Click</button>');
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
Gefest ships with simple element wrappers:
|
||||
- `GefestButton` — `<button>`
|
||||
- `GefestA` — `<a>`
|
||||
- `GefestImg` — `<img>`
|
||||
- `GefestP` — `<p>`
|
||||
- `GefestSpan` — `<span>`
|
||||
- `GefestHeader` — `<h1>`–`<h6>`
|
||||
- `GefestI` — `<i>`
|
||||
- `GefestSmall` — `<small>`
|
||||
- `GefestCenter` — `<center>`
|
||||
|
||||
## API Reference
|
||||
|
||||
### GefestElement
|
||||
|
||||
#### Properties
|
||||
- `gefestId: string` - Unique identifier for the element (readonly)
|
||||
- `style: GefestStyle | null` - Style to apply to the element
|
||||
- `onClick: (() => void) | null` - Click event handler (getter/setter)
|
||||
- `isHidden: boolean` - Whether the element should be hidden (sets the 'hidden' attribute when true)
|
||||
- `gefestId: string` — auto-generated unique identifier
|
||||
- `style: GefestStyle | (() => GefestStyle) | null`
|
||||
- `onClick: (() => void) | null`
|
||||
- `isHidden: boolean`
|
||||
- `id: string | null`
|
||||
|
||||
#### Methods
|
||||
- `constructor(content: (GefestElement | string)[] | string)`
|
||||
- `constructor(content?: (GefestElement | string)[] | string)`
|
||||
- `build(isFromStyle?: boolean): string`
|
||||
- `update(): void` - Re-renders the element in the DOM
|
||||
- `setAttribute(key: string, value: string): void`
|
||||
- `getAttribute(key: string): string | undefined`
|
||||
- `removeAttribute(key: string): void`
|
||||
- `addClass(className: string): void`
|
||||
- `removeClass(className: string): void`
|
||||
- `update(): void`
|
||||
- `setAttribute(key, value): void`
|
||||
- `getAttribute(key): string | undefined`
|
||||
- `removeAttribute(key): void`
|
||||
- `addClass(className): void`
|
||||
- `removeClass(className): void`
|
||||
- `getClassList(): string[]`
|
||||
- `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
|
||||
- `setPersonalStyle(style: string | null): void`
|
||||
- `emit(event, data?): void`
|
||||
- `on(event, handler): void`
|
||||
- `once(event, handler): void`
|
||||
- `checkReservedAttribute(key): void`
|
||||
|
||||
#### Static Methods
|
||||
- `fromHTML(html: string): GefestElement`
|
||||
@ -366,69 +105,132 @@ button.removeAttribute('disabled');
|
||||
- `register(element: GefestElement): string`
|
||||
- `activateOnClick(): Promise<void>`
|
||||
- `main(main: () => Promise<void>): Promise<void>`
|
||||
- `reRenderByGI(gefestId: string): void`
|
||||
- `render(gefestId?: string | string[] | null): void`
|
||||
|
||||
### GefestStyle
|
||||
|
||||
#### Methods
|
||||
- `applyStyle(html: string, element?: GefestElement): string`
|
||||
|
||||
## Examples
|
||||
## Usage Examples
|
||||
|
||||
### Complete Application
|
||||
### Create a button
|
||||
|
||||
```typescript
|
||||
import { GefestButton } from './primitives/button';
|
||||
|
||||
const button = new GefestButton('Submit');
|
||||
button.addClass('btn');
|
||||
button.setAttribute('type', 'submit');
|
||||
button.onClick = () => {
|
||||
console.log('clicked');
|
||||
};
|
||||
```
|
||||
|
||||
### Apply inline styles
|
||||
|
||||
```typescript
|
||||
button.setPersonalStyle('background-color: blue; color: white; padding: 10px;');
|
||||
```
|
||||
|
||||
### Hide an element
|
||||
|
||||
```typescript
|
||||
button.isHidden = true;
|
||||
```
|
||||
|
||||
### Nested elements
|
||||
|
||||
```typescript
|
||||
import { GefestA } from './primitives/a';
|
||||
|
||||
const link = new GefestA('Read more', '#');
|
||||
const button = new GefestButton([link, ' more']);
|
||||
```
|
||||
|
||||
### Custom element
|
||||
|
||||
```typescript
|
||||
import { GefestElement } from './element';
|
||||
|
||||
class GefestDiv extends GefestElement {
|
||||
protected wrapHTML(content: string): string {
|
||||
const attrString = this.attributesToString();
|
||||
return `<div ${attrString}>${content}</div>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom style
|
||||
|
||||
```typescript
|
||||
import { GefestStyle } from './style';
|
||||
|
||||
class BootstrapStyle extends GefestStyle {
|
||||
applyStyle(html: string, element?: GefestElement): string {
|
||||
if (element?.getClassList().includes('btn')) {
|
||||
return html.replace('<button', '<button class="btn btn-primary"');
|
||||
}
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
button.style = new BootstrapStyle();
|
||||
```
|
||||
|
||||
### Event listeners
|
||||
|
||||
```typescript
|
||||
button.on('onClick', (data) => {
|
||||
console.log('click data', data);
|
||||
});
|
||||
|
||||
button.once('onClick', () => {
|
||||
console.log('once only');
|
||||
});
|
||||
|
||||
button.onClick = () => {
|
||||
button.emit('onClick', { value: 1 });
|
||||
};
|
||||
```
|
||||
|
||||
### Rerendering
|
||||
|
||||
```typescript
|
||||
button.update();
|
||||
```
|
||||
|
||||
### Complete app
|
||||
|
||||
```typescript
|
||||
import { GefestEngine } from './engine';
|
||||
import { GefestButton, GefestP } from './primitives';
|
||||
import { GefestButton, GefestHeader, GefestP } from './primitives';
|
||||
|
||||
async function main() {
|
||||
const title = new GefestP("Welcome to Gefest!");
|
||||
title.addClass('title');
|
||||
|
||||
const button = new GefestButton("Get Started");
|
||||
button.addClass('start-btn');
|
||||
button.onClick = () => {
|
||||
alert('Welcome!');
|
||||
};
|
||||
|
||||
// For custom elements, define them as shown above
|
||||
const container = new (class extends GefestElement {
|
||||
protected wrapHTML(content: string): string {
|
||||
const attrString = this.attributesToString();
|
||||
return `<div ${attrString}>${content}</div>`;
|
||||
}
|
||||
})([title, button]);
|
||||
container.addClass('container');
|
||||
|
||||
document.body.innerHTML = container.build();
|
||||
const title = new GefestHeader('Welcome', 2);
|
||||
const button = new GefestButton('Start');
|
||||
button.onClick = () => alert('Hello');
|
||||
document.body.innerHTML = title.build() + button.build();
|
||||
}
|
||||
|
||||
GefestEngine.main(main);
|
||||
```
|
||||
|
||||
### Form Creation
|
||||
## Notes
|
||||
|
||||
```typescript
|
||||
import { GefestButton } from './primitives/button';
|
||||
|
||||
const submitBtn = new GefestButton("Submit");
|
||||
submitBtn.setAttribute('type', 'submit');
|
||||
submitBtn.addClass('btn-submit');
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.innerHTML = submitBtn.build();
|
||||
document.body.appendChild(form);
|
||||
```
|
||||
- `class`, `style`, and `data-gefest-id` are reserved.
|
||||
- Use `addClass` / `removeClass` for CSS classes.
|
||||
- Use `setPersonalStyle` for inline styles.
|
||||
- `GefestElement.fromHTML()` is experimental and may not preserve all behavior.
|
||||
|
||||
## Contributing
|
||||
|
||||
To add new primitives or extend the framework:
|
||||
|
||||
1. Create new classes extending `GefestElement`
|
||||
2. Implement the `wrapHTML` method to return the appropriate HTML tag
|
||||
3. Add event handling if needed
|
||||
4. Test with `GefestEngine.main()`
|
||||
To extend Gefest:
|
||||
1. Add a class extending `GefestElement`
|
||||
2. Implement `wrapHTML()` with the desired tag
|
||||
3. Add custom logic, classes, or events
|
||||
4. Test with `GefestEngine.main()` in the browser
|
||||
|
||||
## License
|
||||
|
||||
This framework is part of the AmeVox project. See project license for details.
|
||||
This framework is part of the AmeVox project. See the project license for details.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user