feature/framework (#1)

Reviewed-on: #1
This commit is contained in:
2025-08-28 10:21:14 +02:00
parent e23b40e0cb
commit 6c58e479af
26 changed files with 7056 additions and 1361 deletions

203
docs/FRAMEWORK_README.md Normal file
View File

@@ -0,0 +1,203 @@
# SPORE UI Framework
A clean, component-based frontend framework with pub/sub communication and view models.
## Architecture Overview
The framework follows a clean architecture pattern with the following layers:
1. **Components** - Handle UI rendering and user interactions
2. **View Models** - Hold data and business logic
3. **API Client** - Communicate with the backend
4. **Event Bus** - Pub/sub communication between components
5. **Framework Core** - Base classes and application management
## Key Features
- **Component-based architecture** - Reusable, self-contained UI components
- **View Models** - Data flows from backend → view model → UI rendering
- **Pub/Sub system** - Components communicate through events
- **Automatic cleanup** - Event listeners and subscriptions are automatically cleaned up
- **Type-safe property access** - View models provide get/set methods with change notifications
- **Routing** - Built-in navigation between views
## File Structure
```
public/
├── framework.js # Core framework classes
├── api-client.js # Backend API communication
├── view-models.js # View models for each component
├── components.js # UI components
├── app.js # Main application setup
├── index.html # HTML template
└── styles.css # Styling
```
## Usage
### 1. Creating a View Model
```javascript
class MyViewModel extends ViewModel {
constructor() {
super();
this.setMultiple({
data: [],
isLoading: false,
error: null
});
}
async loadData() {
try {
this.set('isLoading', true);
const data = await window.apiClient.getData();
this.set('data', data);
} catch (error) {
this.set('error', error.message);
} finally {
this.set('isLoading', false);
}
}
}
```
### 2. Creating a Component
```javascript
class MyComponent extends Component {
constructor(container, viewModel, eventBus) {
super(container, viewModel, eventBus);
}
setupEventListeners() {
const button = this.findElement('.my-button');
if (button) {
this.addEventListener(button, 'click', this.handleClick.bind(this));
}
}
setupViewModelListeners() {
this.subscribeToProperty('data', this.render.bind(this));
this.subscribeToProperty('isLoading', this.render.bind(this));
this.subscribeToProperty('error', this.render.bind(this));
}
render() {
const data = this.viewModel.get('data');
const isLoading = this.viewModel.get('isLoading');
const error = this.viewModel.get('error');
if (isLoading) {
this.setHTML('', '<div>Loading...</div>');
return;
}
if (error) {
this.setHTML('', `<div class="error">${error}</div>`);
return;
}
this.setHTML('', `<div>${this.renderData(data)}</div>`);
}
renderData(data) {
return data.map(item => `<div>${item.name}</div>`).join('');
}
handleClick() {
// Handle user interaction
this.viewModel.loadData();
}
}
```
### 3. Using the Event Bus
```javascript
// Subscribe to events
this.subscribeToEvent('user-logged-in', (userData) => {
console.log('User logged in:', userData);
});
// Publish events
this.viewModel.publish('data-updated', { timestamp: Date.now() });
```
### 4. Component Helper Methods
The framework provides several helper methods for common DOM operations:
```javascript
// Find elements
const element = this.findElement('.my-class');
const elements = this.findAllElements('.my-class');
// Update content
this.setHTML('.my-container', '<div>New content</div>');
this.setText('.my-text', 'New text');
// Manage classes
this.setClass('.my-element', 'active', true);
this.setClass('.my-element', 'hidden', false);
// Show/hide elements
this.setVisible('.my-element', false);
this.setEnabled('.my-button', false);
// Manage styles
this.setStyle('.my-element', 'color', 'red');
```
### 5. Registering Routes
```javascript
// In app.js
window.app.registerRoute('my-view', MyComponent, 'my-view-container');
```
### 6. Navigation
```javascript
// Navigate to a route
window.app.navigateTo('my-view');
```
## Data Flow
1. **User Interaction** → Component handles event
2. **Component** → Calls view model method
3. **View Model** → Makes API call or processes data
4. **View Model** → Updates properties (triggers change notifications)
5. **Component** → Receives change notification and re-renders
6. **UI** → Updates to reflect new data
## Best Practices
1. **Keep components focused** - Each component should have a single responsibility
2. **Use view models for business logic** - Don't put API calls or data processing in components
3. **Subscribe to specific properties** - Only listen to properties your component needs
4. **Use the event bus sparingly** - Prefer direct view model communication for related components
5. **Clean up resources** - The framework handles most cleanup automatically, but be mindful of custom event listeners
6. **Error handling** - Always handle errors in async operations and update the view model accordingly
## Migration from Old Code
The old monolithic `script.js` has been broken down into:
- **API calls** → `api-client.js`
- **Data management** → `view-models.js`
- **UI logic** → `components.js`
- **Application setup** → `app.js`
Each piece is now more focused, testable, and maintainable.
## Benefits
- **Maintainability** - Smaller, focused files are easier to understand and modify
- **Testability** - View models can be tested independently of UI
- **Reusability** - Components can be reused across different views
- **Scalability** - Easy to add new features without affecting existing code
- **Debugging** - Clear separation of concerns makes issues easier to track down
- **Performance** - Components only re-render when their specific data changes