# 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 