el-maker
v0.0.2
Published
An abstract custom element base class (`ElementMaker`) that bundles a catalog of composable features behind async lazy-loading. Concrete elements are defined declaratively — picking which features to activate and providing per-element configuration — with
Readme
element-maker
An abstract custom element base class (ElementMaker) that bundles a catalog of composable features behind async lazy-loading. Concrete elements are defined declaratively — picking which features to activate and providing per-element configuration — without writing any JavaScript class code.
How It Works
ElementMaker extends HTMLElement and declares static supportedFeatures with async fallbackSpawn functions for each feature. Feature implementations are only imported when a derived element actually uses them, so unused features add zero overhead.
Concrete elements are created via defineWithFeatures, which:
- Waits for the base class to be defined
- Resolves async fallback spawns in parallel (cached per base class)
- Creates a subclass dynamically
- Calls
assignFeatureswith the resolved spawns + JSON config - Registers the element
This enables fully declarative element definition from JSON — including from mount-observer cede scripts embedded in HTML.
Usage
From JavaScript
import { defineWithFeatures } from 'assign-gingerly/defineWithFeatures.js';
await defineWithFeatures('time-ticker', 'el-maker', {
assignFeatures: {
roundabout: {
customData: { raConfig: { actions: {...}, compacts: {...} } },
withAttrs: { base: 'tt', duration: '${base}-duration', _duration: { instanceOf: 'Number' } },
callbackForwarding: ['connectedCallback']
},
truthSourcer: {
callbackForwarding: ['connectedCallback', 'attributeChangedCallback']
},
faceUp: {
customData: { integrateWithRoundabout: true },
callbackForwarding: ['connectedCallback', 'formDisabledCallback', 'formResetCallback', 'formStateRestoreCallback']
}
}
});From a cede script in HTML
<time-ticker>
<script type="cede" data-extends="el-maker">{
"assignFeatures": {
"roundabout": {
"customData": { "raConfig": { ... } },
"withAttrs": { "base": "tt", "duration": "${base}-duration" },
"callbackForwarding": ["connectedCallback"]
},
"truthSourcer": {
"callbackForwarding": ["connectedCallback", "attributeChangedCallback"]
}
}
}</script>
</time-ticker>What the Base Class Provides
ElementMaker sets up shared infrastructure that features depend on:
| Resource | Purpose |
|----------|---------|
| propagator (EventTarget) | Property change event bus — used by truthSourcer and reflector to observe value changes |
| #internals (ElementInternals) | Shared via getSharedContext with faceUp (form control), reflector (custom states), and roundabout |
| static featuresConfig | Installs whenFeatureReady() for awaiting async feature resolution |
Because all spawns are async, defining 10 elements that extend el-maker only imports each feature module once — results are cached per base class.
Feature Catalog
| Package | Description | Source | |---------|-------------|--------| | truth-sourcer | Attribute/property binding and truth-sourcing for custom elements | GitHub | | be-reflective | CSS custom state reflection from computed styles | GitHub | | face-up | Form Associated Custom Element behavior via ElementInternals | GitHub | | roundabout-lib | Reactive view-model binding with template rendering and computed property orchestration | GitHub | | templ-maker | Extracts a DOM fragment into a reusable template and clones it per instance (works with cede scripts) | GitHub |
Example: time-ticker
time-ticker demonstrates a fully feature-based component with no code in the element class itself — all behavior comes from the roundabout and timeTicker features wired via assignFeatures.
Elements Extending ElementMaker
| Package | Description | Source | |---------|-------------|--------| | time-ticker | Web component that fires events periodically | GitHub |
Viewing Demos Locally
- Install git
- Fork/clone this repo
- Install node.js
- Open command window to folder where you cloned this repo
git submodule add https://github.com/bahrus/types.git types
git submodule update --init --recursive
npm install
npm run serve
- Open http://localhost:8000/demo/ in a modern browser
Running Tests
> npm run test