Skip to content

Repository files navigation

Element Framework (Muffin)

A lightweight, vanilla JavaScript framework for building modern User Interfaces with message-driven component-based architecture and flexible data management.

Note: This core library is extended by atom-websdk which is recommended for use in projects as it contains enhancements and many abstractions over this core library.

Overview

Element Framework (internally called "Muffin") is a minimalistic frontend framework that provides:

  • Component-based architecture with Web Components
  • Client-side routing for SPAs
  • Real-time data synchronization via WebSockets
  • Local storage integration with IndexedDB
  • Template inclusion system for modular HTML
  • Event-driven communication between components

Quick Start

Installation

Install in your project:

npm i --save github:FootLooseLabs/element

Then include the framework in your index.src.html:

<scriptsrc="./node_modules/muffin/dist/muffin.min.js"></script>

Project structure:

my-project/
├── src/
│ ├── assets/
│ ├── components/
│ ├── pages/
│ ├── index.src.html
│ └── sw.js
├── gulpfile.js
├── package.json
├── .gitignore
└── .babelrc

Core Concepts

1. Components (DOMComponent)

Components are custom HTML elements that encapsulate data, markup, and behavior.

classContactCardextendsMuffin.DOMComponent{staticdomElName="contact-card"staticschema={"name": "","email": "","phone_no": ""}staticmarkupFunc=(data)=>{return` <h1>${data.name}</h1> <h3 class="contact">${data.email}<br/>${data.phone_no} </h3> <button on-click="handleEdit">Edit Contact</button> `}handleEdit(srcEl,event){console.log("Editing contact:",this.data.name);console.log("Source element:",srcEl);// Handle edit logic}}

Key Properties:

  • domElName: HTML tag name for the component
  • schema: Default data structure
  • markupFunc: Function that renders data into HTML
  • stateSpace: Component state management
  • interfaces: External API definitions

2. Data Management (DataSource)

DataSource handles data persistence, real-time updates, and local caching.

<contact-card><component-datasocket="websocket-name" label="contact-data">
{
"name": "John Doe",
"email": "john@example.com",
"phone_no": "+1234567890"
}
</component-data></contact-card>

Features:

  • Automatic IndexedDB persistence
  • WebSocket real-time updates
  • JSON fixture loading
  • Data normalization and validation

3. Routing (Router)

Client-side routing for single-page applications with nested route support.

<divroute="home"><h1>Home Page</h1><buttononclick="Muffin._router.go('about')">About</button></div><divroute="about"><h1>About Page</h1><buttononclick="Muffin._router.go('home')">Home</button></div><script>Muffin._router=newMuffin.Router();document.addEventListener('DOMContentLoaded',()=>{Muffin._router.go('home');});</script>

Router Features:

  • Declarative route definitions with route attributes
  • Nested routing with sub-route
  • URL parameter handling
  • History API integration
  • Programmatic navigation

4. Real-time Communication (PostOffice)

Event-driven messaging system supporting WebSockets and local events.

// Create a WebSocket connectionvarsocket=PostOffice.addSocket(WebSocket,"api","ws://localhost:8080");// Listen for messagessocket.addListener("user-update",(data)=>{console.log("User updated:",data);});// Send messagessocket.sendMsg({lexemeName: "updateUser",msg: {id: 1,name: "Jane Doe"}});

5. Template Inclusion

Modular HTML templates for better code organization.

<!-- index.src.html --><divroute="contact"><includesrc="pages/contact.html"></include></div><!-- pages/contact.html --><template><style>
.contact-container { padding:20px; }
</style><divclass="contact-container"><contact-card><component-datalabel="contact-info">
{"name": "Alice", "email": "alice@example.com"}
</component-data></contact-card></div></template>

6. Event Handling

Components support on-<eventname> attributes in markup for direct event binding.

classInteractiveCardextendsMuffin.DOMComponent{staticdomElName="interactive-card"staticschema={title: "",count: 0}staticmarkupFunc=(data)=>{return` <div class="card"> <h2>${data.title}</h2> <p>Count: ${data.count}</p> <button on-click="increment">+</button> <button on-click="decrement">-</button> <input on-input="updateTitle" placeholder="Update title" /> <div on-mouseenter="highlight" on-mouseleave="unhighlight"> Hover me! </div> </div> `}increment(srcEl,event){this.data.count++;this.render();}decrement(srcEl,event){this.data.count--;this.render();}updateTitle(srcEl,event){this.data.title=event.target.value;}highlight(srcEl,event){srcEl.style.backgroundColor='#f0f0f0';}unhighlight(srcEl,event){srcEl.style.backgroundColor='';}}

Built-in Elements

Custom Tags

  • <include src="path/to/template.html"> - Include external templates
  • <component-data socket="name" label="key"> - Component data binding

HTML Attributes

  • route="route-name" - Define routes for SPA navigation
  • sub-route - Mark nested routes
  • on-<eventname>="methodName" - Bind DOM events to component methods

CSS Classes

  • .page - Default page styling
  • ._active - Active route indicator (customizable)

Advanced Features

Component Lifecycle

Components have built-in lifecycle methods:

classMyComponentextendsMuffin.DOMComponent{staticdomElName="my-component"connectedCallback(){// Called when component is added to DOMconsole.log("Component mounted");}onDomContentLoaded(){// Called when DOM is readythis.initializeComponent();}switchState(newState){// Handle state transitionsconsole.log("State changed to:",newState);}}

Interface System

Components can expose interfaces for external communication:

classAPIComponentextendsMuffin.DOMComponent{staticdomElName="api-component"staticadvertiseAs="UserAPI"staticLEXICON={getUser: {schema: {subscribe: true},inflect: (data)=>({action: "get_user", ...data})},updateUser: {schema: {},inflect: (data)=>({action: "update_user", ...data})}}getUser(request){// Handle get user requests}updateUser(request){// Handle update user requests}}

State Management

Components can define state spaces and transitions:

classStatefulComponentextendsMuffin.DOMComponent{staticdomElName="stateful-component"staticstateSpace={"loading": {apriori: ["idle"]},"loaded": {apriori: ["loading"]},"error": {apriori: ["loading"]}}asyncloadData(){this.switchState("loading");try{constdata=awaitthis.fetchData();this.data=data;this.switchState("loaded");}catch(error){this.switchState("error");}}}

Configuration

Framework configuration in your application:

// Custom configurationwindow.Muffin={DEBUG_SCOPE: {_debugCmp: null},DB_NAME: "MyApp",DB_VERSION: 1.0};

Development Workflow

  1. Create components in src/components/ directory
  2. Define pages in src/pages/ directory using templates
  3. Set up routes in your main HTML file
  4. Configure data sources with WebSocket connections
  5. Build and test your application

Browser Support

  • Modern browsers supporting Web Components
  • ES6+ features required
  • IndexedDB for local storage
  • WebSocket API for real-time features

Architecture Benefits

  • Lightweight: Minimal framework overhead
  • Modular: Component-based architecture
  • Flexible: No rigid conventions, adaptable to needs
  • Real-time: Built-in WebSocket support
  • Offline-capable: Local storage integration
  • SEO-friendly: Server-side rendering possible

Global API Reference

The framework exposes the window.Muffin namespace containing:

  • window.Muffin.DOMComponent - Base component class
  • window.Muffin.Router - Client-side router
  • window.Muffin.PostOffice - Messaging system
  • window.Muffin.DataSource - Data management
  • window.Muffin.Lexeme - Message structure system
  • window.Muffin.Introspector - Component introspection utilities
  • window.Muffin.DOMComponentRegistry - Component registration system

Legacy globals (for backward compatibility):

  • window.Router - Alias for window.Muffin.Router
  • window.PostOffice - Alias for window.Muffin.PostOffice
  • window.DataSource - Alias for window.Muffin.DataSource
  • window.DOMComponent - Alias for window.Muffin.DOMComponent

Element Framework is designed to be implementation-flexible and lightweight. When contributing:

  1. Maintain minimal dependencies
  2. Preserve vanilla JavaScript approach
  3. Ensure backward compatibility
  4. Add comprehensive tests

About

(DRAFT) framework for composing decoupled & stateful web interfaces/ontologies based on atom principles (used internally @footloose.io)

Resources

Stars

3 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages