On this page
components
introduction
dframework ships a set of web components and a performance tailored base class for authoring custom ones. components are standard custom elements with no virtual dom, no compiler, no build step. the framework compiles and bundles component files in public/js/components/ automatically.
built in components
d-checkbox
1<d-checkbox text="enable notifications" checked></d-checkbox>| attribute | description |
|---|---|
text |
label text |
checked |
initial checked state |
events: change
d-color-picker
1<d-color-picker value="#ff0000" name="theme color"></d-color-picker>methods: toggle(), setHex(hex)
events: change
d-combobox
a searchable, extensible select component.
1<d-combobox[object Object]>2 options='[{"value":"a","text":"option A"},{"value":"b","text":"option B"}]'[object Object]>3 placeholder="select an option"[object Object]>4 allow-search>5</d-combobox>| attribute | description |
|---|---|
options |
json array of {value, text} objects |
placeholder |
placeholder text |
allow-search |
enable search filtering |
allow-input |
allow custom values not in the options list |
methods: addOption(value, text), removeOption(value), clearOptions()
events: change, add, open, close
d-context-menu
renders a context menu on right click within the parent element:
1<div>2 right-click anywhere here3 <d-context-menu>4 <span>copy</span>5 <span>paste</span>6 <span>delete</span>7 </d-context-menu>8</div>d-drawer
1<d-drawer direction="left" opened>2 <nav>navigation content</nav>3</d-drawer>| attribute | description |
|---|---|
direction |
top, right, bottom, left |
opened |
initial open state |
methods: toggle()
d-dropdown
1<d-dropdown header="account">2 <a href="/profile">profile</a>3 <a href="/settings">settings</a>4 <a href="/logout" full>log out</a>5</d-dropdown>d-hold-button
fires click only after the user holds for the specified duration. prevents accidental activation of destructive actions.
1<d-hold-button delay="2000">2 delete3</d-hold-button>d-icon-button
1<d-icon-button icon="dstrn-heart" size="2em" color="red"></d-icon-button>d-image-input
1<d-image-input accept="image/png,image/jpeg" name="avatar" fit="contain"></d-image-input>| attribute | description |
|---|---|
accept |
allowed file types (default image/png, image/jpeg, image/gif) |
icon |
placeholder icon class (default dstrn-picture) |
name |
input name |
id |
input id |
placeholder |
primary dropzone label text (default drag & drop image) |
subtitle |
secondary dropzone helper text (default or click to browse) |
replace-text |
label text for the replace button (default replace) |
delete-text |
label text for the delete button (default delete) |
no-replace |
disables replace action (default false) |
no-delete |
disables delete action (default false) |
fit |
object fit property applied to preview image (default contain) |
value |
file object or url string path |
methods: reset()
events: change
d-file-input
1<d-file-input accept="*/*" name="document" multiple compact></d-file-input>| attribute | description |
|---|---|
accept |
allowed file types (default */*) |
icon |
placeholder icon class (default dstrn-folder) |
name |
input name |
id |
input id |
placeholder |
primary dropzone label text (default drag & drop file) |
subtitle |
secondary dropzone helper text (default or click to browse) |
compact |
render as a slim horizontal bar (default true) |
multiple |
allow selecting multiple files (default false) |
value |
file object, url string path, or array of files/urls |
methods: reset()
events: change
d-loader
1<d-loader></d-loader>d-modal
1<d-modal>2 <h2>confirm action</h2>3 <p>this cannot be undone.</p>4 <button>confirm</button>5</d-modal>methods: open(), close()
d-notification
1<d-notification timer="4000">2 <p>your changes have been saved.</p>3</d-notification>methods: show(), hide(), destroy()
d-skeleton
placeholder shapes rendered during content loading:
1<d-skeleton type="text" lines="3"></d-skeleton>2<d-skeleton type="circle" size="3em"></d-skeleton>3<d-skeleton type="rect" width="100%" height="200px"></d-skeleton>4<d-skeleton type="card"></d-skeleton>| attribute | default | description |
|---|---|---|
type |
— | text, circle, rect, card |
lines |
1 |
number of lines (for text type) |
size |
— | shorthand for equal width and height |
width / height |
— | explicit dimensions |
radius |
— | border radius override |
d-slider
1<d-slider min="0" max="100" value="50" step="1"></d-slider>methods: getValue(), setValue(value)
events: change
d-text-input
1<d-text-input placeholder="search" icon="dstrn-search" shortcut="cmd+k"></d-text-input>| attribute | description |
|---|---|
placeholder |
placeholder text |
icon |
input icon class |
type |
input type (text, password, search, number) |
shortcut |
keyboard shortcut to quickly focus the input |
autocomplete |
enable browser autocomplete (default false) |
disabled |
disables the input element (default false) |
readonly |
sets the input to read only (default false) |
d-toggle
renders a segmented toggle between two or more options. mark the default with default:
1<d-toggle>2 <div default>option 1</div>3 <div>option 2</div>4</d-toggle>events: change
d-morph
standalone morphing elements. the dMotion singleton engine orchestrates physics based animated transitions between named elements placed anywhere in the document.
1<d-morph name="compact" d-action="long-press" d-to="expanded" d-duration="400" d-visible>2 <div class="compact-card">compact view</div>3</d-morph>4 5<d-morph name="expanded" d-action="click" d-to="compact" d-duration="300">6 <div class="expanded-card">expanded view</div>7</d-morph>| attribute | description |
|---|---|
name |
unique identifier for this morph element |
d-visible |
marks this element as initially visible |
d-action |
trigger type: click, long-press or click-out |
d-to |
name of the target morph element to transition to |
d-duration |
transition duration in milliseconds |
methods: show(), hide()
place d-no-morph on child elements to exempt them from the transition and allow standard interaction.
window.dMotion api:
1dMotion.get('compact') // get a morph instance by name2dMotion.getPrevious() // returns the immediately previous morph, null if no transition has occurred3dMotion.transition('compact', 'expanded', triggerNode) // trigger a transition programmatically4dMotion.listen('compact', 'expanded', fn, 'before') // hook: 'before' or 'after'5dMotion.unlisten('compact', 'expanded', fn, 'before')
dComponent (base class)
dComponent is the authoring base class for all custom components. it provides property reflection, reactive state, form integration, lifecycle management, and automatic memory cleanup. it does not use a virtual dom, rendering is surgical by design.
1class dCounter extends dComponent {2 static tag = 'd-counter'3 static form = true // opt into native form element integration4 5 static props = {6 value: { type: 'number', default: 0, form: true },7 step: { type: 'number', default: 1 },8 disabled: { type: 'boolean', default: false },9 label: { type: 'string', default: 'count' },10 }11 12 template() {13 // called once on mount and returns initial innerHTML14 return `[object Object]>15 <span class="label">${this.label}</span>[object Object]>16 <button class="dec">−</button>[object Object]>17 <span class="value">${this.value}</span>[object Object]>18 <button class="inc">+</button>[object Object]>19 `;20 }21 22 mount() {23 // called once after template initialisation24 // good for effects that depend on reactive state25 this.effect(() => {26 // reading this.state.value registers it as a dependency27 // the effect reruns whenever state.value changes28 document.title = `count: ${this.state.value}`;29 return () => { document.title = 'myapp'; }; // optional teardown30 });31 }32 33 render() {34 // called automatically on every prop or state change35 // use for surgical dom updates only (never modify this.state here)36 this.refs('.value')[0].textContent = this.state.value ?? this.value;37 this.refs('button.dec')[0].disabled = this.disabled;38 this.refs('button.inc')[0].disabled = this.disabled;39 40 // listeners are cleared and rebound between renders (no duplicate handlers)41 this.listen(this.refs('button.dec')[0], 'click', () => this.decrement());42 this.listen(this.refs('button.inc')[0], 'click', () => this.increment());43 }44 45 onPropChanged(name, oldVal, newVal) {46 if (name === 'value') Log.debug('value changed', oldVal, '->', newVal);47 }48 49 increment() {50 this.setState(s => { s.value = (s.value ?? this.value) + this.step; });51 }52 53 decrement() {54 this.setState(s => { s.value = (s.value ?? this.value) - this.step; });55 }56}57 58dComponent.define(dCounter);rendering strategy
dComponent does not provide a virtual dom. structure belongs in template(). all dom mutations belong in render() or onPropChanged(). this eliminates reflows, preserves listener state, and prevents flicker. any error thrown inside render() is caught and logged without crashing the component.
this.state cannot be modified inside render(), the engine ignores such writes to prevent infinite loops.
reactive state
1// update state and trigger render()2this.setState({ active: true });3this.setState(s => { s.count++; s.lastUpdated = Date.now(); });4 5// update state without triggering render() but still triggers effect() loops6this.setState({ timer: Date.now() }, false);auto cleanup api
use these instead of native browser apis to prevent memory leaks when the component is removed from the dom:
1this.listen(target, event, callback, opts?) // addEventListener with cleanup2this.listenAll(targets, event, callback) // attach to multiple elements3this.setTimeout(callback, ms)4this.setInterval(callback, ms)5this.requestAnimationFrame(callback)dom caching
1this.refs(selector) // cached querySelectorAll, returns an arrayinner content capture
1this.originalChildren // array of cloned light dom nodes from before template()2this.originalHTML // original innerHTML as a stringinternal event emitter
1this.on(event, callback)2this.emit(event, ...args)websocket
1this.wire(event, payload) // emit a d-wire event through the global socket loop
