# 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('', '
Loading...
'); return; } if (error) { this.setHTML('', `
${error}
`); return; } this.setHTML('', `
${this.renderData(data)}
`); } renderData(data) { return data.map(item => `
${item.name}
`).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', '
New content
'); 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