Ooidget Component Format 0.1Editor's Draft
Documents and components
The component file
A component file is a JSON text [RFC8259] encoded in UTF-8 whose top-level value is an
object. Its media type is application/ooidget+json; its file name extension SHOULD be .json, and a bundle of
several components SHOULD use .ooidget.json.
| Property | Type | Required | Description |
|---|---|---|---|
spec | string | no | The version of this specification the file was written for, as "major.minor". Absent: "0.1". |
about | string | no | A human-readable description of the component. Runtimes MUST NOT interpret it. |
folder | string | no | A grouping for authoring tools. Runtimes MUST NOT interpret it. |
widget | Widget | no | The component’s drawing, state, children and behaviour. |
interface | object | no | Its props, events and methods for a host. |
tests | array | no | Tests it carries, runnable by any runtime. |
behaviour | string | no | Present instead of widget: the source of a shared behaviour. |
extensionsUsed | array of string | no | The extensions the file uses. |
extensionsRequired | array of string | no | The extensions without which the file cannot be rendered. |
extensions | object | no | Extension-specific data, by extension name. |
A file MUST contain widget or behaviour, and MUST NOT contain both.
Names and namespaces
Components, shared behaviours and code are referred to by name, never copied. A name is a path of segments
separated by /, each segment ASCII letters, digits, _ and -, beginning with a letter; names are
case-sensitive. The last segment names the thing; the segments before it are its namespace, which is the folder
it lives in, relative to the project’s root:
| Name | Refers to |
|---|---|
knob | knob.json (or knob.behaviour.js) at the project’s root |
controls/knob | controls/knob.json |
@ooidget/controls/knob | knob.json in the package @ooidget/controls, as npm names packages |
A function is referred to as { "fn": "module#export" }: the export export of the code module module.js, named
as above. Without #, { "fn": "export" } is an export of the referring component’s own code module.
| Reference | Function |
|---|---|
{ "fn": "road" } | road exported by this component’s name.js |
{ "fn": "geometry#arc" } | arc exported by geometry.js at the project’s root |
{ "fn": "@ooidget/std/colour#mix" } | mix exported by colour.js in the package @ooidget/std |
Names resolve within the project first, then in its packages; a project MUST NOT define a name twice. Where a
document refers to a component (a use layer, a child, a
shared behaviour), the runtime resolves its name through the host’s
component resolver. A name that does not resolve is not an error: the referring layer or child is skipped, and a
runtime SHOULD report it to the host as a diagnostic.
Both forms are valid everywhere code is accepted: JavaScript inline in a string, for quick expressions and small
behaviours (s.on && !ui.down, `${s.tempo} bpm`, ({ click(s) { return { on: !s.on } } })), or a reference to a
function in a code module. Authoring tools SHOULD move code longer than a line or two into a code module, where it is
written, read and checked as JavaScript.
Project folders
A component’s source is kept as a project folder, one file per reusable thing, each referred to by name and never copied into the files that use it:
| File | Holds | Referred to by |
|---|---|---|
name.json | a component file: data only | use layers, children, "behaviour": { "use": name } |
name.js | a code module: the component’s own beside its name.json, or a shared one | { "fn": "export" } in its component, { "fn": "name#export" } anywhere |
name.behaviour.js | a shared behaviour: an ECMAScript module whose default export is a function from settings to a handler object | "behaviour": { "use": name, ...settings } |
styles.json | styles by name | a layer’s style |
tokens.json | design tokens in the DTCG format | token references |
assets/ | images and fonts | image layers, fonts |
Authoring tools SHOULD write projects this way. A name MUST NOT be defined by more than one file of a project. A bundle is the packed form of a project folder, for distribution: tools pack and unpack between the two without loss.
Code modules
Quick code may be written inline, as JavaScript in a string (expressions, a short
behaviour). Longer code (a behaviour with several handlers, a computed drawing, a painter) belongs
in the component’s code module: an ECMAScript module name.js beside name.json.
| Export | Is |
|---|---|
behaviour | the component’s handler object, used when its widget has no behaviour member |
| any other name | a function a value of the component refers to |
Wherever this specification accepts an Expression<T>, a document MAY instead give { "fn": "name" }: the code
module’s export of that name, called with the expression’s parameters (s, ui, m, env) and returning the
value. For example, a road drawn from state:
{ "svgExpr": { "fn": "road" } }/** The road ahead, its dashes moving with the distance driven. */
export function road(s, ui) {
let o = "";
for (let i = 0; i < 12; i++) o += dash(i, s.trip);
return o;
}A code module MUST NOT have side effects when imported, and its exports MUST behave as
expressions and behaviours do: no I/O, no time, no randomness. Runtimes load it in the
same sandbox. Because a code module is already code, a runtime needs no eval for it.
A code module MAY import other code modules of the project, and only those, by relative path with the .js
extension (import { detents } from "./detents.js"), resolved as names are: relative to the
importing module’s folder. Imports of anything else (packages, URLs, the host’s modules) are not allowed.
In a bundle a component’s code module travels as the member module of its component file: the
module’s source text.
Bundles
A bundle is a single JSON object that carries several components and the resources they share, for distribution and for the web:
| Property | Type | Required | Description |
|---|---|---|---|
spec | string | no | As for a component file. |
components | object | yes | Component files by name. |
styles | object | no | Styles by name. |
tokens | object | no | Design tokens, in the DTCG format. |
assets | object | no | Asset contents by name, as data: URLs [RFC2397]. |
root | string | no | The component a host shows by default. |
A runtime given a bundle MUST resolve component names against its components before asking the host.
The widget object
| Property | Type | Required | Description |
|---|---|---|---|
size | [number, number] | yes | The component’s design size, [width, height], in design units. Both MUST be positive. |
state | object | no | The initial state. Absent: {}. |
layers | array of Layer | no | Drawn in order, first at the back. |
children | array of Child | no | Components contained in this one. |
layout | Layout | no | How children are arranged. Absent: each child is placed by its own frame. |
behaviour | string or object | no | The behaviour: its source, or a reference to a shared one. |
regions | object | no | Named regions for hit-testing. |
tooltip | Expression<string> | no | Text the host MAY show while the pointer rests on the instance. |
focusable | boolean | no | Whether the instance takes keyboard focus. Absent: false. |
clip | boolean | no | Whether layers and children are clipped to the instance’s box. Absent: false. |
fit | string | no | How a root instance fills a host area larger or smaller than its size: "contain" (default), "fill". |
scroll | object | no | { "x": Expression<number>, "y": Expression<number> }: offsets subtracted from children’s positions. |
defs | string | no | An SVG fragment of definitions (gradients, patterns, filters) shared by this widget’s SVG layers. |
role | string | no | Its WAI-ARIA role. |
label | Expression<string> | no | Its accessible name. |
minSize, maxSize, aspect | no | The smallest and largest layout of a root instance, and the proportions it keeps; see Adapting to the container. | |
scaling | object | no | { min, max, base? }: how far it scales as a whole with its area. |
Members not defined here or by an extension MUST be ignored by runtimes and SHOULD be reported by validators.