@glissade/scene 0.35.0 → 0.36.0-pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/component.d.ts +66 -0
- package/dist/component.js +82 -0
- package/dist/describe.d.ts +19 -1
- package/dist/describe.js +20 -1
- package/package.json +6 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { B as Node, a as Group } from "./nodes.js";
|
|
2
|
+
|
|
3
|
+
//#region src/component.d.ts
|
|
4
|
+
|
|
5
|
+
/** One prop in a component's public surface — a manifest type + optionality. */
|
|
6
|
+
interface ComponentPropSpec {
|
|
7
|
+
/** the §2.2 value-type / shape id this prop accepts (e.g. 'number', 'color', 'string', 'Node[]'). */
|
|
8
|
+
type: string;
|
|
9
|
+
/** true when the factory REQUIRES it (default false). */
|
|
10
|
+
required?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** The props every component instance carries in addition to its own — a stable
|
|
13
|
+
* id (REQUIRED, the collision-safety anchor) plus the base Group passthrough. */
|
|
14
|
+
interface ComponentBaseProps {
|
|
15
|
+
/** Stable id — REQUIRED; every internal node is namespaced under it. */
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
/** What an instance returns: the subtree + its id + the child-id helpers. */
|
|
19
|
+
interface ComponentInstance {
|
|
20
|
+
/** the built subtree (a Group whose id === the instance id). */
|
|
21
|
+
readonly node: Group;
|
|
22
|
+
/** the instance id (its own namespace root). */
|
|
23
|
+
readonly id: string;
|
|
24
|
+
/** namespace a child id: `childId('bar')` → `'<id>/bar'`; no arg → the root id. */
|
|
25
|
+
childId(sub?: string): string;
|
|
26
|
+
/** ready-to-bind track targets for a child's prop: `['<id>/<child>/<prop>']`. */
|
|
27
|
+
targets(child: string, prop: string): string[];
|
|
28
|
+
}
|
|
29
|
+
interface ComponentDef<P extends Record<string, unknown> = Record<string, unknown>> {
|
|
30
|
+
/** the component's type name — surfaced in describe().components, must be unique. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** the public prop surface (names → type/optionality) for describe(). */
|
|
33
|
+
props: {
|
|
34
|
+
readonly [prop: string]: ComponentPropSpec;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* PURE build: given the instance props (incl. the required `id`) and a
|
|
38
|
+
* child-id namer, return the subtree. Runs ONCE at construction; must not
|
|
39
|
+
* read the playhead or any cross-frame state (bind children with tracks/
|
|
40
|
+
* PropInit closures like any node).
|
|
41
|
+
*/
|
|
42
|
+
build(props: P & ComponentBaseProps, childId: (sub?: string) => string): Group;
|
|
43
|
+
}
|
|
44
|
+
declare class ComponentError extends Error {
|
|
45
|
+
constructor(message: string);
|
|
46
|
+
}
|
|
47
|
+
/** Join an instance id + a child sub-id into a namespaced target root. */
|
|
48
|
+
declare function childId(id: string, sub?: string): string;
|
|
49
|
+
interface RegisteredComponent {
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly props: {
|
|
52
|
+
readonly [prop: string]: ComponentPropSpec;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Every component defined so far, in definition order — the seam describe()
|
|
56
|
+
* reads to list `components` from the LIVE registry (can't drift). Pure read. */
|
|
57
|
+
declare function listComponents(): RegisteredComponent[];
|
|
58
|
+
/**
|
|
59
|
+
* Define a reusable component. Returns a factory `(props) → ComponentInstance`.
|
|
60
|
+
* Registers the component's name + prop surface so `describe().components` lists
|
|
61
|
+
* it. A duplicate name throws (a component library can't silently shadow).
|
|
62
|
+
*/
|
|
63
|
+
declare function defineComponent<P extends Record<string, unknown> = Record<string, unknown>>(def: ComponentDef<P>): (props: P & ComponentBaseProps) => ComponentInstance;
|
|
64
|
+
/** re-export Node/Group types consumers of this entry name for their `build`. */
|
|
65
|
+
//#endregion
|
|
66
|
+
export { ComponentBaseProps, ComponentDef, ComponentError, ComponentInstance, ComponentPropSpec, Group, type Node, RegisteredComponent, childId, defineComponent, listComponents };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { r as Group } from "./nodes.js";
|
|
2
|
+
//#region src/component.ts
|
|
3
|
+
/**
|
|
4
|
+
* `@glissade/scene/component` — `defineComponent()` (0.36): reusable, typed,
|
|
5
|
+
* describe()-legible animated subscenes. The missing COMPOSITION unit — the
|
|
6
|
+
* user-defined generalization of the built-in `Grid()`/`Chart()`/`splitText()`
|
|
7
|
+
* factories (all already "props → subtree" pure build-time functions).
|
|
8
|
+
*
|
|
9
|
+
* const LowerThird = defineComponent({
|
|
10
|
+
* name: 'LowerThird',
|
|
11
|
+
* props: { name: { type: 'string' }, title: { type: 'string' }, accent: { type: 'color' } },
|
|
12
|
+
* build: ({ name, title, accent }, cid) => new Group({ id: cid(), children: [
|
|
13
|
+
* new Rect({ id: cid('bar'), width: 6, height: 40, fill: accent }),
|
|
14
|
+
* new Text({ id: cid('name'), text: name, ... }),
|
|
15
|
+
* new Text({ id: cid('title'), text: title, fill: accent, ... }),
|
|
16
|
+
* ]}),
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* const lt = LowerThird({ id: 'intro', name: 'Ada', title: 'Engineer', accent: '#4ea1ff' });
|
|
20
|
+
* // scene children: [lt.node]
|
|
21
|
+
* // animate a child: tl.to(lt.childId('bar') + '/height', 40, { from: 0 })
|
|
22
|
+
* // or the ready ids: tl.stagger(lt.targets('bar', 'opacity'), …)
|
|
23
|
+
*
|
|
24
|
+
* PURE build-time fan-out: `build` runs at construction and emits ordinary nodes,
|
|
25
|
+
* so `evaluate()` stays a pure function of time and the goldens hold by
|
|
26
|
+
* construction. Nothing executes at play time.
|
|
27
|
+
*
|
|
28
|
+
* ID-SCOPING is the whole safety story: every instance carries a required stable
|
|
29
|
+
* `id`, and every internal node id is stamped through `cid(sub) = '<id>/<sub>'`,
|
|
30
|
+
* so instancing the SAME component N times (distinct ids) can't collide track
|
|
31
|
+
* targets. Two instances built with the SAME id DO collide — the same footgun
|
|
32
|
+
* `Grid`/`Chart` have; give each instance a unique id.
|
|
33
|
+
*
|
|
34
|
+
* On its OWN tree-shakeable subpath (off the base embed) + re-exported onto the
|
|
35
|
+
* `@glissade/browser` IIFE. `describe().components` lists every component defined
|
|
36
|
+
* so far (side-effect registration on definition — the value-type/examples
|
|
37
|
+
* pattern), so an agent or the studio sees each component's typed prop surface.
|
|
38
|
+
*/
|
|
39
|
+
var ComponentError = class extends Error {
|
|
40
|
+
constructor(message) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "ComponentError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/** Join an instance id + a child sub-id into a namespaced target root. */
|
|
46
|
+
function childId(id, sub) {
|
|
47
|
+
return sub === void 0 || sub === "" ? id : `${id}/${sub}`;
|
|
48
|
+
}
|
|
49
|
+
const registry = /* @__PURE__ */ new Map();
|
|
50
|
+
/** Every component defined so far, in definition order — the seam describe()
|
|
51
|
+
* reads to list `components` from the LIVE registry (can't drift). Pure read. */
|
|
52
|
+
function listComponents() {
|
|
53
|
+
return [...registry.values()];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Define a reusable component. Returns a factory `(props) → ComponentInstance`.
|
|
57
|
+
* Registers the component's name + prop surface so `describe().components` lists
|
|
58
|
+
* it. A duplicate name throws (a component library can't silently shadow).
|
|
59
|
+
*/
|
|
60
|
+
function defineComponent(def) {
|
|
61
|
+
if (registry.has(def.name)) throw new ComponentError(`a component named '${def.name}' is already defined`);
|
|
62
|
+
for (const [prop, spec] of Object.entries(def.props)) if (spec === null || typeof spec !== "object" || typeof spec.type !== "string") throw new ComponentError(`${def.name}: prop '${prop}' must be { type: string, required? }, got ${JSON.stringify(spec)}`);
|
|
63
|
+
registry.set(def.name, {
|
|
64
|
+
name: def.name,
|
|
65
|
+
props: def.props
|
|
66
|
+
});
|
|
67
|
+
return (props) => {
|
|
68
|
+
if (props.id === void 0 || props.id === "") throw new ComponentError(`${def.name}(...) needs a stable id — every instance namespaces its children under it (so N instances don't collide track targets)`);
|
|
69
|
+
const id = props.id;
|
|
70
|
+
const cid = (sub) => childId(id, sub);
|
|
71
|
+
const node = def.build(props, cid);
|
|
72
|
+
if (node.id !== id) throw new ComponentError(`${def.name}(...).build must return a Group with id === childId() ('${id}'); got '${node.id ?? "<unset>"}'. Use \`new Group({ id: cid(), … })\`.`);
|
|
73
|
+
return {
|
|
74
|
+
node,
|
|
75
|
+
id,
|
|
76
|
+
childId: cid,
|
|
77
|
+
targets: (child, prop) => [`${cid(child)}/${prop}`]
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { ComponentError, Group, childId, defineComponent, listComponents };
|
package/dist/describe.d.ts
CHANGED
|
@@ -72,6 +72,21 @@ interface DescribedNode {
|
|
|
72
72
|
*/
|
|
73
73
|
subpath?: string;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* One user-defined `defineComponent()` in the manifest (0.36) — a reusable
|
|
77
|
+
* animated subscene's public prop surface, so an agent/studio sees what it
|
|
78
|
+
* accepts. Generated from the LIVE component registry, so it can't drift.
|
|
79
|
+
*/
|
|
80
|
+
interface DescribedComponent {
|
|
81
|
+
name: string;
|
|
82
|
+
/** the component's public props: name → { type, required? } (construction-time). */
|
|
83
|
+
props: {
|
|
84
|
+
[prop: string]: {
|
|
85
|
+
type: string;
|
|
86
|
+
required?: boolean;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
}
|
|
75
90
|
interface DescribedBuilderMethod {
|
|
76
91
|
name: string;
|
|
77
92
|
signature: string;
|
|
@@ -117,6 +132,9 @@ interface ApiManifest {
|
|
|
117
132
|
* `name` is also a `window.glissade.<name>` global on the IIFE.
|
|
118
133
|
*/
|
|
119
134
|
helpers: DescribedHelper[];
|
|
135
|
+
/** user-defined components registered via defineComponent() (0.36); present
|
|
136
|
+
* from describe() (possibly empty), absent on manifests captured before 0.36. */
|
|
137
|
+
components?: DescribedComponent[];
|
|
120
138
|
createScene: string;
|
|
121
139
|
subpaths: {
|
|
122
140
|
[entry: string]: string;
|
|
@@ -147,4 +165,4 @@ declare function registerExamples(corpus: {
|
|
|
147
165
|
}): void;
|
|
148
166
|
declare function describe(opts?: DescribeOptions): ApiManifest;
|
|
149
167
|
//#endregion
|
|
150
|
-
export { ApiManifest, DescribeOptions, DescribedBuilderMethod, DescribedHelper, DescribedNode, DescribedProp, describe, registerExamples };
|
|
168
|
+
export { ApiManifest, DescribeOptions, DescribedBuilderMethod, DescribedComponent, DescribedHelper, DescribedNode, DescribedProp, describe, registerExamples };
|
package/dist/describe.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { D as NODE_CONSTRUCTION_PROP_NAMES, E as BASE_CONSTRUCTION_PROP_NAMES, a as Path, c as Video, i as ImageNode, o as Rect, r as Group, s as Text, t as Circle } from "./nodes.js";
|
|
2
|
+
import { listComponents } from "./component.js";
|
|
2
3
|
import { easings, listValueTypes } from "@glissade/core";
|
|
3
4
|
//#region src/describe.ts
|
|
4
5
|
/**
|
|
@@ -22,7 +23,7 @@ import { easings, listValueTypes } from "@glissade/core";
|
|
|
22
23
|
* never pulled onto the base embed path — a scene that never calls `describe()`
|
|
23
24
|
* pays zero bytes for it.
|
|
24
25
|
*/
|
|
25
|
-
const RAW_VERSION = "0.
|
|
26
|
+
const RAW_VERSION = "0.36.0-pre.1";
|
|
26
27
|
const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
|
|
27
28
|
/** Arity of a value type's numeric repr: vec2/vec2-arc → 2, number → 1; others (color/paint/path/string/boolean) carry no scalar arity. */
|
|
28
29
|
function arityOf(type) {
|
|
@@ -428,6 +429,12 @@ const HELPERS = [
|
|
|
428
429
|
import: "@glissade/scene/chart",
|
|
429
430
|
usage: "Chart({ id, data: Row[], xKey, yKey, width, height, yScale?, bandPadding?, fill?: string | ColorScale }): { node: Group, bars: Rect[], targets(prop): string[] }"
|
|
430
431
|
},
|
|
432
|
+
{
|
|
433
|
+
name: "defineComponent",
|
|
434
|
+
summary: "Define a reusable, typed, describe()-legible animated subscene — the user-defined generalization of Grid/Chart. Returns a factory (props & { id }) => { node, childId, targets }; each instance namespaces its children under the required id so N instances never collide track targets. Pure build-time. describe().components lists every one defined. On the @glissade/scene/component subpath.",
|
|
435
|
+
import: "@glissade/scene/component",
|
|
436
|
+
usage: "defineComponent({ name, props: { <p>: { type, required? } }, build(props, childId): Group }): (props & { id }) => { node: Group, id, childId(sub?), targets(child, prop) }"
|
|
437
|
+
},
|
|
431
438
|
{
|
|
432
439
|
name: "linearScale",
|
|
433
440
|
summary: "A serializable linear scale (value axis): maps a numeric domain onto a pixel/unit range. Pair with Chart({ yScale }). On the @glissade/scene/chart subpath.",
|
|
@@ -493,6 +500,14 @@ let exampleCorpus = {};
|
|
|
493
500
|
function registerExamples(corpus) {
|
|
494
501
|
exampleCorpus = corpus;
|
|
495
502
|
}
|
|
503
|
+
function mapComponentProps(props) {
|
|
504
|
+
const out = {};
|
|
505
|
+
for (const [k, v] of Object.entries(props)) out[k] = {
|
|
506
|
+
type: v.type,
|
|
507
|
+
...v.required ? { required: true } : {}
|
|
508
|
+
};
|
|
509
|
+
return out;
|
|
510
|
+
}
|
|
496
511
|
function describe(opts = {}) {
|
|
497
512
|
const withEx = opts.examples === true && Object.keys(exampleCorpus).length > 0;
|
|
498
513
|
const ex = (key) => {
|
|
@@ -530,6 +545,10 @@ function describe(opts = {}) {
|
|
|
530
545
|
...h,
|
|
531
546
|
...ex(h.name)
|
|
532
547
|
})) : HELPERS,
|
|
548
|
+
components: listComponents().map((c) => ({
|
|
549
|
+
name: c.name,
|
|
550
|
+
props: mapComponentProps(c.props)
|
|
551
|
+
})),
|
|
533
552
|
createScene: "createScene({ size: { w, h }, children: Node[] }): Scene — media assets are declared on the Timeline document: timeline({ assets: { <id>: { kind: 'image'|'video', url } } }); an Image/Video node's `assetId` names an entry here.",
|
|
534
553
|
subpaths: SUBPATHS
|
|
535
554
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0-pre.1",
|
|
4
4
|
"description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"types": "./dist/chart.d.ts",
|
|
32
32
|
"default": "./dist/chart.js"
|
|
33
33
|
},
|
|
34
|
+
"./component": {
|
|
35
|
+
"types": "./dist/component.d.ts",
|
|
36
|
+
"default": "./dist/component.js"
|
|
37
|
+
},
|
|
34
38
|
"./path": {
|
|
35
39
|
"types": "./dist/path.d.ts",
|
|
36
40
|
"default": "./dist/path.js"
|
|
@@ -69,7 +73,7 @@
|
|
|
69
73
|
],
|
|
70
74
|
"dependencies": {
|
|
71
75
|
"yoga-layout": "^3.2.1",
|
|
72
|
-
"@glissade/core": "0.
|
|
76
|
+
"@glissade/core": "0.36.0-pre.1"
|
|
73
77
|
},
|
|
74
78
|
"repository": {
|
|
75
79
|
"type": "git",
|