Ooidget Component Format 0.1Editor's Draft
Behaviours
Behaviour source
A widget’s behaviour, when a string, is ECMAScript source of an Expression that evaluates to an object, the
handler object. Each of its members that is a function is the handler for the event of that name:
({
click(s) { if (s.toggle) return { on: !s.on } },
drag(s0, dx, dy, ui) { return { value: Math.min(1, Math.max(0, s0.value - dy / 300)) } },
wheel(s, notches) { return { value: Math.min(1, Math.max(0, s.value + notches / 50)) } }
})The source is evaluated once per instance, in the sandbox. Handlers MUST be treated as pure functions of their arguments: a runtime MAY call them again with the same arguments and MAY run them on another thread.
Events
| Handler | Arguments | Called when |
|---|---|---|
press(s, ui) | view, pointer state | A pointer or touch goes down on the instance. |
release(s, ui) | view, pointer state | The press that began on the instance ends, wherever it ends. |
click(s, ui) | view, pointer state | A press is released over the instance without having been a drag. |
dblclick(s, ui) | view, pointer state | A second click within the platform’s double-click interval. |
drag(s0, dx, dy, ui, s) | view as it was at the press, offsets since the press in design units, pointer state, the view now | The pointer moves while pressed, after the platform’s drag threshold. A drag that adds up as it goes (a knob turned past half a circle) keeps its running total in state and reads it from s. |
wheel(s, notches, ui) | view, notches (positive: away from the user / up), pointer state | A wheel or trackpad scroll over the instance. |
rotate(s, detents, ui) | view, detents (positive: clockwise), pointer state | A rotary input (crown, bezel, encoder) turns while the instance has focus. Absent: wheel is called with the same value. |
key(s, k) | view, key event | A key is pressed while the instance has focus. k is { key, code, shift, ctrl, alt, meta } with the values of the DOM KeyboardEvent [UIEVENTS]. |
focus(s), blur(s) | view | The instance gains or loses focus. |
context(s, ui) | view, pointer state | A secondary press or long press on the instance. |
A handler returns a state patch, or undefined for no change. A returned value that is not an
object is ignored. Events are delivered to the topmost instance under the pointer whose behaviour has a handler for
them; an instance without one lets the event pass to its parent.
Commands
A state patch MAY carry a command for the host: the members $cmd (a string) and $args (any JSON value).
The runtime removes them from the patch, applies the rest, and passes the host { "method": $cmd, "params": $args, "from": path }. A host that does not know the command ignores it.
Shared behaviours
A behaviour used by several components is written once, in a file of its own in the project folder,
name.behaviour.js: an ECMAScript module whose default export is a function from settings to a handler object.
/** A value dragged along an axis, with marks it can rest on: a knob's, a slider's, a fader's. */
export default (o) => ({
drag(s0, dx, dy, ui) { /* … */ },
release(s) { /* … */ },
wheel(s, notches) { /* … */ },
});In a bundle the same behaviour travels as a component file whose module member is the
module’s source: { "about": "…", "module": "export default (o) => ({ … })" }. A behaviour with no imports MAY instead
travel as "behaviour": "(o) => ({ … })", its default export as source.
A widget uses it with an object: "behaviour": { "use": "drag", "axis": "y", "steps": 11 }. The runtime calls the
module’s default export with settings, the object without use, and uses the result as the widget’s handler object.
A setting that is a string MAY name a view member that holds its value; this is the shared behaviour’s own
convention, not the runtime’s.
Composing behaviours
A widget’s behaviour MAY be an array: several behaviours, each a source, a reference or { "use": … }, applied
together. A knob that is dragged and turned by the wheel takes one of each:
"behaviour": [
{ "use": "drag", "axis": "both", "value": "volume", "steps": "ticks" },
{ "use": "wheel", "value": "volume", "steps": "ticks" }
]The composed handler object has every handler any of them has. When several have the same handler, a runtime MUST call them in array order with the same arguments and merge the state patches they return, a later one’s members replacing an earlier one’s; a command from any of them is passed on.
Behaviours that work alike share their code as a code module they import, not as a
behaviour: drag.behaviour.js and wheel.behaviour.js both import { detents } from "./detents.js".