@realitycollective/webxr-uiextensions 0.1.0-preview.0 → 0.1.0-preview.2
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/CHANGELOG.md +21 -0
- package/README.md +14 -1
- package/dist/adapter.d.ts +84 -0
- package/dist/adapter.js.map +1 -1
- package/dist/controls/element.d.ts +17 -5
- package/dist/controls/element.js +12 -2
- package/dist/controls/element.js.map +1 -1
- package/dist/controls/expandable-label.d.ts +7 -7
- package/dist/controls/expandable-label.js +8 -8
- package/dist/controls/expandable-label.js.map +1 -1
- package/dist/controls/log-view.d.ts +12 -12
- package/dist/controls/log-view.js +12 -12
- package/dist/controls/log-view.js.map +1 -1
- package/dist/controls/stepper.d.ts +8 -8
- package/dist/controls/stepper.js +8 -8
- package/dist/controls/stepper.js.map +1 -1
- package/dist/controls/toggle.d.ts +6 -6
- package/dist/controls/toggle.js +6 -6
- package/dist/controls/toggle.js.map +1 -1
- package/dist/controls/upgrade.d.ts +2 -2
- package/dist/controls/upgrade.js +36 -11
- package/dist/controls/upgrade.js.map +1 -1
- package/dist/scene.d.ts +6 -4
- package/dist/scene.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -13,5 +13,26 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
13
13
|
- `@realitycollective/xrblocks-uiextensions` - EXPERIMENTAL Google XR Blocks / plain three.js adapter: panel document, window host, follow and scale maths, desktop controls and locomotion, pointer forwarding.
|
|
14
14
|
- `@realitycollective/uix-devtools` - dev-only tooling: edit-session launch gate, runtime UIKitML compilation, and the `uix-dev` CLI (Cloudflare quick tunnel, QR onboarding, environment doctor).
|
|
15
15
|
- Demo clients: the IWSDK showcase, the devtools playground, and the multiplatform lab that picks its pipeline from the hardware.
|
|
16
|
+
- `uixComponentSet`, exported from `@realitycollective/iwsdk-uiextensions`. Pass it to `spatialUI.componentSets` so the IWSDK 0.5 parser accepts the `<uix-*>` control elements. Without it a panel using any control fails to parse and never attaches. No equivalent is needed on the three.js or XR Blocks adapters.
|
|
17
|
+
- `WindowHandle` and `WindowOptionsBase` in `@realitycollective/webxr-uiextensions`. `WindowHandle` is what every adapter's `createWindow` hands back: an `id`, a `panel` that is `undefined` until the adapter has attached the document, and `onReady(listener)`, which runs once and fires immediately if the panel is already there. `WindowOptionsBase` holds the options every adapter understands, and both adapters' option types now extend it, so an option name means the same thing on each. `createWindow` itself stays adapter-specific, because the `config` payload differs per engine.
|
|
18
|
+
- `WindowHost.supportsStandalonePanels`. It says whether `createPanel` works on a host, rather than leaving callers to find out by catching. `false` on the IWSDK host, where the ECS owns panel lifecycles and `createPanel` throws; `true` on the three.js / XR Blocks host.
|
|
19
|
+
- `PanelReadyEvent.kind`, either `window` or `panel`. `window` means the panel came from the adapter's window factory and is managed by the window manager. `panel` means a bare panel the adapter noticed, whose `id` is then the adapter's best stable identifier for it - the config path on IWSDK. The field is optional, so existing listeners keep compiling.
|
|
20
|
+
- `createWindow` on the IWSDK scene host, returning an `IwsdkWindowHandle`: a `WindowHandle` plus the ECS `entity`. It spawns through `createUIWindow`, names the window `uix-window-<n>` when no id is given, and resolves `panel` and `onReady` once the document attaches, so wiring one window needs neither an ECS query nor a filtered `onPanelReady`. `createUIWindow` is unchanged and still returns the entity.
|
|
21
|
+
- `getPanelHandle(entity)` in `@realitycollective/iwsdk-uiextensions`: the panel handle for an entity you already hold, or `undefined` while IWSDK is still loading its document.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Controls are declared as custom elements rather than attributes.** `<uix-stepper>` replaces `<div data-uix="stepper">`, and each part is its own element: `<uix-value>`, `<uix-line>`, `<uix-label>` and so on, replacing `data-uix-role`. Parameters are unchanged and stay `data-uix-*`. This is what makes one markup file portable: IWSDK 0.5 parses with `@drawcall/uikitml`, which rejects every `data-*` attribute on a built-in tag but accepts custom tags declared in a component set, while the three.js and XR Blocks parser accepts custom tags with no registration at all. Both expose the declared tag on `userData.customElement.componentName`, so the engine-free upgraders read one contract on every adapter.
|
|
26
|
+
- The expandable label's `toggle` part is now `<uix-more>`. As an element the old name would have collided with the `<uix-toggle>` control.
|
|
27
|
+
- `@realitycollective/iwsdk-uiextensions` targets **IWSDK 0.5.x**: peer range `>=0.5.0 <0.6.0`, developed against `@iwsdk/core` 0.5.3. `PanelUI` in 0.5 declares a single field, `config`, holding the URL of the UIKitML **source**; the compiled JSON it took on 0.4.x, and the `@iwsdk/vite-plugin-uikitml` step that produced it, are both gone. `maxWidth` and `maxHeight` were removed from the component schema, and ECS ignores unknown fields silently, so a panel that still writes them is mis-sized with no error. Size through `createUIWindow`, which routes to `UIWindow.targetWidth/Height`.
|
|
28
|
+
- `UixWindowHost` resolves a window's `config` by fetching the `.uikitml` source and parsing it, where it previously fetched compiled JSON. Supplying `loadConfig` is no longer necessary for an ordinary panel; override it only for an unusual transport.
|
|
29
|
+
- `compilePanelSource` in `@realitycollective/uix-devtools` serves the **source** on its `blob:` URL rather than compiled JSON, matching what 0.5 fetches. `CompiledPanel.json` is now `CompiledPanel.source`, and the `resolveFile` option is gone because the 0.5 parser has no `<link ref>` stylesheet resolution; put shared rules in a `<style>` block, of which several are allowed and merge. Validation now runs on `@drawcall/uikitml`, pinned to the version `@iwsdk/core` depends on, so a source that passes here is one `PanelUISystem` will accept and no second parser enters the tree.
|
|
30
|
+
- Example and demo markup uses `rgba()` or eight-digit hex rather than `background-opacity`, which the 0.5 parser removed. Both parsers accept the replacement, so one stylesheet serves every adapter.
|
|
31
|
+
- The IWSDK component kit is selected by name, `spatialUI: { kit: 'horizon' }`, rather than by passing an imported kit module. The kits now live inside the parser.
|
|
32
|
+
- `@types/three` is pinned to `0.184.0` across the workspace. IWSDK 0.5.3 and xrblocks resolved different copies, and two copies of the three typings make structurally identical `Object3D` types mutually unassignable, which surfaces as spurious errors far from their cause.
|
|
33
|
+
- `onPanelReady` on the IWSDK host announces bare panels as well as managed windows. A `PanelUI` entity with no `UIWindow`, or with an empty `windowId`, used to be dropped silently; it is now announced with `kind: 'panel'` and its config path as the id. Listeners that only want managed windows should check `kind`.
|
|
34
|
+
- `id` is optional in the XR Blocks host's `CreateWindowOptions`; a window created without one is named `uix-window-<n>`, as on IWSDK. The options also accept `movable` for parity, but this host has no title-bar drag of its own yet, so the flag is recorded and not acted on.
|
|
35
|
+
- The XR Blocks window handle gains `panel` - the document, which already implements `PanelHandle` - and `onReady`, which fires synchronously because uikitml interprets the markup during `createWindow`. The interface is now named `XrBlocksWindowHandle`, with `WindowHandle` kept as an alias, because the core exports a `WindowHandle` of its own.
|
|
36
|
+
- Coverage gates `packages/iwsdk-uiextensions/src/factory.ts` and `src/scene-host.ts` at the same 100% as the core. `new World()` from `@iwsdk/core` constructs headlessly - no renderer, no WebGL, no XR session - so the factories, the scene host and its ECS query all run for real in a node test. The per-frame ECS systems still need a live world and stay outside the gate.
|
|
16
37
|
|
|
17
38
|
[0.1.0]: https://github.com/realitycollective/WebXR-UIExtensions/commits/main
|
package/README.md
CHANGED
|
@@ -32,7 +32,8 @@ src/controls/ data-uix markup upgraders, driven through the structural
|
|
|
32
32
|
src/chrome/ window chrome conventions: contractual element ids
|
|
33
33
|
(uix-titlebar, uix-pin, ...) + reference UIKitML snippet
|
|
34
34
|
src/adapter.ts the platform-adapter contract: PanelHost, PanelHandle,
|
|
35
|
-
|
|
35
|
+
WindowHost, WindowHandle, WindowOptionsBase, HeadPoseSource,
|
|
36
|
+
PointerInputSource (plain tuples, no engine)
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
## Writing an adapter
|
|
@@ -45,6 +46,18 @@ An adapter supplies three capabilities and drives the core from its frame loop:
|
|
|
45
46
|
|
|
46
47
|
The IWSDK adapter is the reference implementation; the XR Blocks adapter shows the same contract bound without an ECS.
|
|
47
48
|
|
|
49
|
+
### The window surface
|
|
50
|
+
|
|
51
|
+
`WindowHost` is what app code writes against once panels exist. Four members carry the whole contract, and both shipped adapters honour all four:
|
|
52
|
+
|
|
53
|
+
- `supportsStandalonePanels: boolean` - whether `createPanel` works here. IWSDK reports `false` because the ECS owns panel lifecycles and `createPanel` throws; the three.js host reports `true`. Check it rather than guessing, and spawn a window when it is `false`.
|
|
54
|
+
- `onPanelReady(listener)` - fires as each panel becomes wireable and replays the ones already live, so wiring order never matters. The event's `kind` says what became ready: `window` for one created through the window factory, `panel` for a bare panel the adapter noticed. A bare panel's `id` is the adapter's best stable identifier for it, which on IWSDK is the config path. Only a host that discovers panels the app created outside the window factory ever reports `panel`; the three.js and XR Blocks host reports `window` only, because a standalone `createPanel` document is handed back to the caller and never announced.
|
|
55
|
+
- `createWindow(options)` - adapter-specific, because the `config` payload differs per engine, but it always returns a `WindowHandle`.
|
|
56
|
+
- `WindowHandle` - `id`, `panel` (`undefined` until the document is attached) and `onReady(listener)`, which runs once and fires immediately if the panel is already there. It is the per-window form of `onPanelReady`, for when you hold a handle and want only that window.
|
|
57
|
+
- Getting the panel later, when you did not keep the handle - on IWSDK call `getPanelHandle(entity)` with the window's entity; on the three.js and XR Blocks host call `host.window(id)?.panel`. Both return the same `PanelHandle`.
|
|
58
|
+
|
|
59
|
+
Options are shared even though `createWindow` is not: every adapter's option type extends `WindowOptionsBase` (`id`, `title`, `dockMode`, `position`, `maxWidth`/`maxHeight`, `movable`, `closable`, `minimizable`, `pinnable`, `followOffset`/`followSpeed`/`followTolerance`, `region`). An option means the same thing everywhere, so one `SceneWindow` maps onto every adapter with no translation table.
|
|
60
|
+
|
|
48
61
|
## Testing
|
|
49
62
|
|
|
50
63
|
```bash
|
package/dist/adapter.d.ts
CHANGED
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
* - {@link PointerInputSource}: deliver ray/pointer press-move-release
|
|
11
11
|
* - {@link HeadPoseSource}: the viewer pose, for follow mode & body-lock
|
|
12
12
|
*
|
|
13
|
+
* On top of those, {@link WindowHost} adds the portable window surface:
|
|
14
|
+
* {@link WindowHost.onPanelReady} for readiness, {@link PanelReadyEvent.kind}
|
|
15
|
+
* to tell a managed window from a bare panel, and
|
|
16
|
+
* {@link WindowHost.supportsStandalonePanels} to say whether `createPanel` is
|
|
17
|
+
* usable at all. Each adapter's own `createWindow` returns a
|
|
18
|
+
* {@link WindowHandle} and takes options extending {@link WindowOptionsBase},
|
|
19
|
+
* so window code reads the same on every engine even though the `config`
|
|
20
|
+
* payload does not.
|
|
21
|
+
*
|
|
13
22
|
* Known adapters:
|
|
14
23
|
* - `@realitycollective/iwsdk-uiextensions` - Meta IWSDK (ECS systems bind
|
|
15
24
|
* these capabilities to `@iwsdk/core` components)
|
|
@@ -18,6 +27,7 @@
|
|
|
18
27
|
*
|
|
19
28
|
* The interfaces use plain tuples/records only - no engine, no three.js.
|
|
20
29
|
*/
|
|
30
|
+
import type { DockModeValue } from './core/dock-state.js';
|
|
21
31
|
import type { UixElement } from './controls/element.js';
|
|
22
32
|
/** Position as [x, y, z] in meters, world space unless stated otherwise. */
|
|
23
33
|
export type Vec3Tuple = [number, number, number];
|
|
@@ -66,6 +76,18 @@ export interface PanelReadyEvent {
|
|
|
66
76
|
id: string;
|
|
67
77
|
/** The live panel - traverse it, or look elements up by markup id. */
|
|
68
78
|
panel: PanelHandle;
|
|
79
|
+
/**
|
|
80
|
+
* What became ready.
|
|
81
|
+
*
|
|
82
|
+
* - `window` - created through the adapter's window factory and managed by
|
|
83
|
+
* the window manager, so `id` is the id the caller asked for.
|
|
84
|
+
* - `panel` - a bare panel the adapter noticed. `id` is then the adapter's
|
|
85
|
+
* best stable identifier for it, which on IWSDK is the panel's config
|
|
86
|
+
* path.
|
|
87
|
+
*
|
|
88
|
+
* Left optional so existing listeners keep compiling; adapters set it.
|
|
89
|
+
*/
|
|
90
|
+
kind?: 'window' | 'panel';
|
|
69
91
|
}
|
|
70
92
|
/**
|
|
71
93
|
* The engine-agnostic surface an app needs to build a UI: spawn windows and
|
|
@@ -80,12 +102,74 @@ export interface PanelReadyEvent {
|
|
|
80
102
|
* ready before the listener was registered, so wiring order never matters.
|
|
81
103
|
*/
|
|
82
104
|
export interface WindowHost extends PanelHost {
|
|
105
|
+
/**
|
|
106
|
+
* Whether {@link PanelHost.createPanel} works on this host. When `false`
|
|
107
|
+
* the method is not available and throws; spawn a window instead, so the
|
|
108
|
+
* engine owns the panel lifecycle. IWSDK is `false`, three.js/XR Blocks is
|
|
109
|
+
* `true`.
|
|
110
|
+
*/
|
|
111
|
+
readonly supportsStandalonePanels: boolean;
|
|
83
112
|
/**
|
|
84
113
|
* Subscribe to panel readiness. Late subscribers are replayed the windows
|
|
85
114
|
* that are already live. Returns an unsubscribe function.
|
|
86
115
|
*/
|
|
87
116
|
onPanelReady(listener: (event: PanelReadyEvent) => void): () => void;
|
|
88
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* A window an adapter spawned, before its panel necessarily exists.
|
|
120
|
+
*
|
|
121
|
+
* `createWindow` itself stays adapter-specific because the `config` payload
|
|
122
|
+
* differs per engine, but what it hands back is the same everywhere: an id, a
|
|
123
|
+
* panel once there is one, and a one-shot readiness callback.
|
|
124
|
+
*/
|
|
125
|
+
export interface WindowHandle {
|
|
126
|
+
/** The window's id - the one passed in, or one the adapter generated. */
|
|
127
|
+
readonly id: string;
|
|
128
|
+
/**
|
|
129
|
+
* The live panel, or `undefined` until the adapter has attached the
|
|
130
|
+
* document. IWSDK loads and parses the markup over later frames; the
|
|
131
|
+
* three.js host interprets it during `createWindow`, so there it is set
|
|
132
|
+
* straight away.
|
|
133
|
+
*/
|
|
134
|
+
readonly panel: PanelHandle | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Run `listener` once, when the panel is attached. Fires immediately if it
|
|
137
|
+
* already is, so wiring order never matters. Returns an unsubscribe
|
|
138
|
+
* function for the case where the caller gives up first.
|
|
139
|
+
*/
|
|
140
|
+
onReady(listener: (panel: PanelHandle) => void): () => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The window options every adapter understands.
|
|
144
|
+
*
|
|
145
|
+
* An adapter's own `CreateWindowOptions` extends this and adds only what its
|
|
146
|
+
* engine needs - chiefly `config`, whose type differs (IWSDK takes a source
|
|
147
|
+
* path, the three.js host takes parsed markup). Keeping the rest here is what
|
|
148
|
+
* lets one `SceneWindow` map onto every adapter without a translation table.
|
|
149
|
+
*/
|
|
150
|
+
export interface WindowOptionsBase {
|
|
151
|
+
/** Stable window id. Adapters generate one when it is absent. */
|
|
152
|
+
id?: string;
|
|
153
|
+
/** Title text written into the window chrome's title element. */
|
|
154
|
+
title?: string;
|
|
155
|
+
dockMode?: DockModeValue;
|
|
156
|
+
/** World position for world-locked windows. */
|
|
157
|
+
position?: Vec3Tuple;
|
|
158
|
+
/** Fit the panel into this box in meters, preserving aspect ratio. */
|
|
159
|
+
maxWidth?: number;
|
|
160
|
+
maxHeight?: number;
|
|
161
|
+
/** Whether the title bar drags the window. */
|
|
162
|
+
movable?: boolean;
|
|
163
|
+
closable?: boolean;
|
|
164
|
+
minimizable?: boolean;
|
|
165
|
+
pinnable?: boolean;
|
|
166
|
+
/** Head-relative offset used in body-follow mode (meters). */
|
|
167
|
+
followOffset?: Vec3Tuple;
|
|
168
|
+
followSpeed?: number;
|
|
169
|
+
followTolerance?: number;
|
|
170
|
+
/** Dock straight into this region on spawn. */
|
|
171
|
+
region?: string;
|
|
172
|
+
}
|
|
89
173
|
/** One pointer/ray interaction stream, engine-normalised. */
|
|
90
174
|
export interface PointerSample {
|
|
91
175
|
/** Pointer world position (ray origin or touch point). */
|
package/dist/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Platform-adapter contract.\n *\n * The core package owns every UX decision - window lifecycle, dock state,\n * region slot math, drag math, control models - and knows nothing about any\n * engine. An engine adapter supplies the three capabilities the core cannot\n * provide for itself, and drives the core from its own frame loop:\n *\n * - {@link PanelHost}: turn compiled UIKitML JSON into a live spatial panel\n * - {@link PointerInputSource}: deliver ray/pointer press-move-release\n * - {@link HeadPoseSource}: the viewer pose, for follow mode & body-lock\n *\n * Known adapters:\n * - `@realitycollective/iwsdk-uiextensions` - Meta IWSDK (ECS systems bind\n * these capabilities to `@iwsdk/core` components)\n * - `@realitycollective/xrblocks-uiextensions` - Google XR Blocks / plain\n * three.js (experimental)\n *\n * The interfaces use plain tuples/records only - no engine, no three.js.\n */\nimport type { UixElement } from './controls/element.js';\n\n/** Position as [x, y, z] in meters, world space unless stated otherwise. */\nexport type Vec3Tuple = [number, number, number];\n\n/** Orientation quaternion as [x, y, z, w]. */\nexport type QuatTuple = [number, number, number, number];\n\n/** A viewer (head) pose sample. */\nexport interface HeadPose {\n position: Vec3Tuple;\n quaternion: QuatTuple;\n}\n\n/** Supplies the viewer pose each frame - camera on desktop, HMD in XR. */\nexport interface HeadPoseSource {\n getHeadPose(): HeadPose;\n}\n\n/**\n * A live spatial panel created from compiled UIKitML JSON.\n * The `root` is traversable with the core's `walk`/`findRole` helpers and\n * the `data-uix` control upgraders - identical markup works on every\n * adapter.\n */\nexport interface PanelHandle {\n /** Root element of the interpreted panel (UixElement-conformant). */\n readonly root: UixElement;\n /** Look up an element by its markup `id`. */\n getElementById(id: string): UixElement | undefined;\n /** Constrain the panel to fit within width × height meters. */\n setTargetDimensions(width: number, height: number): void;\n /** Release panel resources. */\n dispose(): void;\n}\n\n/** Creates spatial panels - the engine-specific half of UIKitML rendering. */\nexport interface PanelHost {\n /**\n * Create a panel from compiled UIKitML JSON (the `{ element, classes }`\n * shape produced by the build plugin or by\n * `@realitycollective/uix-devtools`' `compilePanelSource`).\n */\n createPanel(configJson: unknown): PanelHandle;\n}\n\n/**\n * A window whose panel has finished loading and is ready to be wired.\n * Delivered by {@link WindowHost.onPanelReady}.\n */\nexport interface PanelReadyEvent {\n /** The window's id, as given to the scene descriptor / create call. */\n id: string;\n /** The live panel - traverse it, or look elements up by markup id. */\n panel: PanelHandle;\n}\n\n/**\n * The engine-agnostic surface an app needs to build a UI: spawn windows and\n * regions from portable data, observe when panels become wireable, and reach\n * the shared `WindowManager`.\n *\n * Panels load asynchronously on every adapter (IWSDK fetches the config;\n * uikit lays out over following frames), so app code must never assume a\n * panel exists immediately after creating its window. {@link onPanelReady}\n * is the portable answer - it replaces engine-specific discovery (ECS\n * queries on IWSDK, polling anywhere else) and fires for panels that became\n * ready before the listener was registered, so wiring order never matters.\n */\nexport interface WindowHost extends PanelHost {\n /**\n * Subscribe to panel readiness. Late subscribers are replayed the windows\n * that are already live. Returns an unsubscribe function.\n */\n onPanelReady(listener: (event: PanelReadyEvent) => void): () => void;\n}\n\n/** One pointer/ray interaction stream, engine-normalised. */\nexport interface PointerSample {\n /** Pointer world position (ray origin or touch point). */\n origin: Vec3Tuple;\n /** Normalised pointing direction. */\n direction: Vec3Tuple;\n}\n\n/**\n * Delivers press-move-release for one interaction source (a controller ray,\n * a hand pinch, a mouse). The core's `hold-to-drag` and `drag-math` consume\n * these; the adapter decides what constitutes press/release.\n */\nexport interface PointerInputSource {\n onPress(listener: (sample: PointerSample) => void): () => void;\n onMove(listener: (sample: PointerSample) => void): () => void;\n onRelease(listener: (sample: PointerSample) => void): () => void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Platform-adapter contract.\n *\n * The core package owns every UX decision - window lifecycle, dock state,\n * region slot math, drag math, control models - and knows nothing about any\n * engine. An engine adapter supplies the three capabilities the core cannot\n * provide for itself, and drives the core from its own frame loop:\n *\n * - {@link PanelHost}: turn compiled UIKitML JSON into a live spatial panel\n * - {@link PointerInputSource}: deliver ray/pointer press-move-release\n * - {@link HeadPoseSource}: the viewer pose, for follow mode & body-lock\n *\n * On top of those, {@link WindowHost} adds the portable window surface:\n * {@link WindowHost.onPanelReady} for readiness, {@link PanelReadyEvent.kind}\n * to tell a managed window from a bare panel, and\n * {@link WindowHost.supportsStandalonePanels} to say whether `createPanel` is\n * usable at all. Each adapter's own `createWindow` returns a\n * {@link WindowHandle} and takes options extending {@link WindowOptionsBase},\n * so window code reads the same on every engine even though the `config`\n * payload does not.\n *\n * Known adapters:\n * - `@realitycollective/iwsdk-uiextensions` - Meta IWSDK (ECS systems bind\n * these capabilities to `@iwsdk/core` components)\n * - `@realitycollective/xrblocks-uiextensions` - Google XR Blocks / plain\n * three.js (experimental)\n *\n * The interfaces use plain tuples/records only - no engine, no three.js.\n */\nimport type { DockModeValue } from './core/dock-state.js';\nimport type { UixElement } from './controls/element.js';\n\n/** Position as [x, y, z] in meters, world space unless stated otherwise. */\nexport type Vec3Tuple = [number, number, number];\n\n/** Orientation quaternion as [x, y, z, w]. */\nexport type QuatTuple = [number, number, number, number];\n\n/** A viewer (head) pose sample. */\nexport interface HeadPose {\n position: Vec3Tuple;\n quaternion: QuatTuple;\n}\n\n/** Supplies the viewer pose each frame - camera on desktop, HMD in XR. */\nexport interface HeadPoseSource {\n getHeadPose(): HeadPose;\n}\n\n/**\n * A live spatial panel created from compiled UIKitML JSON.\n * The `root` is traversable with the core's `walk`/`findRole` helpers and\n * the `data-uix` control upgraders - identical markup works on every\n * adapter.\n */\nexport interface PanelHandle {\n /** Root element of the interpreted panel (UixElement-conformant). */\n readonly root: UixElement;\n /** Look up an element by its markup `id`. */\n getElementById(id: string): UixElement | undefined;\n /** Constrain the panel to fit within width × height meters. */\n setTargetDimensions(width: number, height: number): void;\n /** Release panel resources. */\n dispose(): void;\n}\n\n/** Creates spatial panels - the engine-specific half of UIKitML rendering. */\nexport interface PanelHost {\n /**\n * Create a panel from compiled UIKitML JSON (the `{ element, classes }`\n * shape produced by the build plugin or by\n * `@realitycollective/uix-devtools`' `compilePanelSource`).\n */\n createPanel(configJson: unknown): PanelHandle;\n}\n\n/**\n * A window whose panel has finished loading and is ready to be wired.\n * Delivered by {@link WindowHost.onPanelReady}.\n */\nexport interface PanelReadyEvent {\n /** The window's id, as given to the scene descriptor / create call. */\n id: string;\n /** The live panel - traverse it, or look elements up by markup id. */\n panel: PanelHandle;\n /**\n * What became ready.\n *\n * - `window` - created through the adapter's window factory and managed by\n * the window manager, so `id` is the id the caller asked for.\n * - `panel` - a bare panel the adapter noticed. `id` is then the adapter's\n * best stable identifier for it, which on IWSDK is the panel's config\n * path.\n *\n * Left optional so existing listeners keep compiling; adapters set it.\n */\n kind?: 'window' | 'panel';\n}\n\n/**\n * The engine-agnostic surface an app needs to build a UI: spawn windows and\n * regions from portable data, observe when panels become wireable, and reach\n * the shared `WindowManager`.\n *\n * Panels load asynchronously on every adapter (IWSDK fetches the config;\n * uikit lays out over following frames), so app code must never assume a\n * panel exists immediately after creating its window. {@link onPanelReady}\n * is the portable answer - it replaces engine-specific discovery (ECS\n * queries on IWSDK, polling anywhere else) and fires for panels that became\n * ready before the listener was registered, so wiring order never matters.\n */\nexport interface WindowHost extends PanelHost {\n /**\n * Whether {@link PanelHost.createPanel} works on this host. When `false`\n * the method is not available and throws; spawn a window instead, so the\n * engine owns the panel lifecycle. IWSDK is `false`, three.js/XR Blocks is\n * `true`.\n */\n readonly supportsStandalonePanels: boolean;\n /**\n * Subscribe to panel readiness. Late subscribers are replayed the windows\n * that are already live. Returns an unsubscribe function.\n */\n onPanelReady(listener: (event: PanelReadyEvent) => void): () => void;\n}\n\n/**\n * A window an adapter spawned, before its panel necessarily exists.\n *\n * `createWindow` itself stays adapter-specific because the `config` payload\n * differs per engine, but what it hands back is the same everywhere: an id, a\n * panel once there is one, and a one-shot readiness callback.\n */\nexport interface WindowHandle {\n /** The window's id - the one passed in, or one the adapter generated. */\n readonly id: string;\n /**\n * The live panel, or `undefined` until the adapter has attached the\n * document. IWSDK loads and parses the markup over later frames; the\n * three.js host interprets it during `createWindow`, so there it is set\n * straight away.\n */\n readonly panel: PanelHandle | undefined;\n /**\n * Run `listener` once, when the panel is attached. Fires immediately if it\n * already is, so wiring order never matters. Returns an unsubscribe\n * function for the case where the caller gives up first.\n */\n onReady(listener: (panel: PanelHandle) => void): () => void;\n}\n\n/**\n * The window options every adapter understands.\n *\n * An adapter's own `CreateWindowOptions` extends this and adds only what its\n * engine needs - chiefly `config`, whose type differs (IWSDK takes a source\n * path, the three.js host takes parsed markup). Keeping the rest here is what\n * lets one `SceneWindow` map onto every adapter without a translation table.\n */\nexport interface WindowOptionsBase {\n /** Stable window id. Adapters generate one when it is absent. */\n id?: string;\n /** Title text written into the window chrome's title element. */\n title?: string;\n dockMode?: DockModeValue;\n /** World position for world-locked windows. */\n position?: Vec3Tuple;\n /** Fit the panel into this box in meters, preserving aspect ratio. */\n maxWidth?: number;\n maxHeight?: number;\n /** Whether the title bar drags the window. */\n movable?: boolean;\n closable?: boolean;\n minimizable?: boolean;\n pinnable?: boolean;\n /** Head-relative offset used in body-follow mode (meters). */\n followOffset?: Vec3Tuple;\n followSpeed?: number;\n followTolerance?: number;\n /** Dock straight into this region on spawn. */\n region?: string;\n}\n\n/** One pointer/ray interaction stream, engine-normalised. */\nexport interface PointerSample {\n /** Pointer world position (ray origin or touch point). */\n origin: Vec3Tuple;\n /** Normalised pointing direction. */\n direction: Vec3Tuple;\n}\n\n/**\n * Delivers press-move-release for one interaction source (a controller ray,\n * a hand pinch, a mouse). The core's `hold-to-drag` and `drag-math` consume\n * these; the adapter decides what constitutes press/release.\n */\nexport interface PointerInputSource {\n onPress(listener: (sample: PointerSample) => void): () => void;\n onMove(listener: (sample: PointerSample) => void): () => void;\n onRelease(listener: (sample: PointerSample) => void): () => void;\n}\n"]}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Structural view of a uikit element as the controls layer needs it.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Controls and their parts are declared as CUSTOM ELEMENTS - `<uix-stepper>`,
|
|
5
|
+
* `<uix-value>` - rather than as attributes on a `<div>`. That is what makes
|
|
6
|
+
* one markup file portable across every adapter: the IWSDK 0.5 parser rejects
|
|
7
|
+
* unknown attributes on built-in tags, but accepts a custom tag declared in a
|
|
8
|
+
* component set, and the three.js / XR Blocks parser accepts custom tags with
|
|
9
|
+
* no registration at all.
|
|
10
|
+
*
|
|
11
|
+
* Both paths expose the declared tag the same way, on
|
|
12
|
+
* `userData.customElement.componentName`. Parameters stay `data-uix-*`
|
|
13
|
+
* attributes, which land in `userData` camelCased with `data-` stripped:
|
|
14
|
+
* `data-uix-min="0"` → `userData.uixMin`.
|
|
8
15
|
*/
|
|
9
16
|
export interface UixElement {
|
|
10
17
|
userData: Record<string, unknown>;
|
|
@@ -14,7 +21,12 @@ export interface UixElement {
|
|
|
14
21
|
}
|
|
15
22
|
/** Depth-first traversal over uikit element children. */
|
|
16
23
|
export declare function walk(element: UixElement, visit: (element: UixElement) => void): void;
|
|
17
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The custom-element tag this element was declared with, lowercased, or
|
|
26
|
+
* undefined for a plain built-in element.
|
|
27
|
+
*/
|
|
28
|
+
export declare function tagOf(element: UixElement): string | undefined;
|
|
29
|
+
/** All descendants (including self) declared as `<uix-{role}>`. */
|
|
18
30
|
export declare function findRoles(root: UixElement, role: string): UixElement[];
|
|
19
31
|
export declare function findRole(root: UixElement, role: string): UixElement | undefined;
|
|
20
32
|
export declare function attrString(element: UixElement, key: string): string | undefined;
|
package/dist/controls/element.js
CHANGED
|
@@ -5,11 +5,21 @@ export function walk(element, visit) {
|
|
|
5
5
|
walk(child, visit);
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* The custom-element tag this element was declared with, lowercased, or
|
|
10
|
+
* undefined for a plain built-in element.
|
|
11
|
+
*/
|
|
12
|
+
export function tagOf(element) {
|
|
13
|
+
const custom = element.userData?.['customElement'];
|
|
14
|
+
const name = custom?.componentName;
|
|
15
|
+
return typeof name === 'string' && name.length > 0 ? name.toLowerCase() : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** All descendants (including self) declared as `<uix-{role}>`. */
|
|
9
18
|
export function findRoles(root, role) {
|
|
19
|
+
const tag = `uix-${role}`;
|
|
10
20
|
const found = [];
|
|
11
21
|
walk(root, (element) => {
|
|
12
|
-
if (element
|
|
22
|
+
if (tagOf(element) === tag) {
|
|
13
23
|
found.push(element);
|
|
14
24
|
}
|
|
15
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element.js","sourceRoot":"","sources":["../../src/controls/element.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"element.js","sourceRoot":"","sources":["../../src/controls/element.ts"],"names":[],"mappings":"AAsBA,yDAAyD;AACzD,MAAM,UAAU,IAAI,CAAC,OAAmB,EAAE,KAAoC;IAC5E,KAAK,CAAC,OAAO,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,OAAmB;IACvC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,eAAe,CAEpC,CAAC;IACd,MAAM,IAAI,GAAG,MAAM,EAAE,aAAa,CAAC;IACnC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACtF,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,SAAS,CAAC,IAAgB,EAAE,IAAY;IACtD,MAAM,GAAG,GAAG,OAAO,IAAI,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;QACrB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAgB,EAAE,IAAY;IACrD,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAmB,EAAE,GAAW;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAmB,EAAE,GAAW;IACzD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAmB,EAAE,GAAW;IAC1D,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,IAAI,CAAC;AACvD,CAAC","sourcesContent":["/**\n * Structural view of a uikit element as the controls layer needs it.\n *\n * Controls and their parts are declared as CUSTOM ELEMENTS - `<uix-stepper>`,\n * `<uix-value>` - rather than as attributes on a `<div>`. That is what makes\n * one markup file portable across every adapter: the IWSDK 0.5 parser rejects\n * unknown attributes on built-in tags, but accepts a custom tag declared in a\n * component set, and the three.js / XR Blocks parser accepts custom tags with\n * no registration at all.\n *\n * Both paths expose the declared tag the same way, on\n * `userData.customElement.componentName`. Parameters stay `data-uix-*`\n * attributes, which land in `userData` camelCased with `data-` stripped:\n * `data-uix-min=\"0\"` → `userData.uixMin`.\n */\nexport interface UixElement {\n userData: Record<string, unknown>;\n children: UixElement[];\n addEventListener(type: string, listener: (event?: unknown) => void): void;\n setProperties(props: Record<string, unknown>): void;\n}\n\n/** Depth-first traversal over uikit element children. */\nexport function walk(element: UixElement, visit: (element: UixElement) => void): void {\n visit(element);\n for (const child of element.children ?? []) {\n walk(child, visit);\n }\n}\n\n/**\n * The custom-element tag this element was declared with, lowercased, or\n * undefined for a plain built-in element.\n */\nexport function tagOf(element: UixElement): string | undefined {\n const custom = element.userData?.['customElement'] as\n | { componentName?: unknown }\n | undefined;\n const name = custom?.componentName;\n return typeof name === 'string' && name.length > 0 ? name.toLowerCase() : undefined;\n}\n\n/** All descendants (including self) declared as `<uix-{role}>`. */\nexport function findRoles(root: UixElement, role: string): UixElement[] {\n const tag = `uix-${role}`;\n const found: UixElement[] = [];\n walk(root, (element) => {\n if (tagOf(element) === tag) {\n found.push(element);\n }\n });\n return found;\n}\n\nexport function findRole(root: UixElement, role: string): UixElement | undefined {\n return findRoles(root, role)[0];\n}\n\nexport function attrString(element: UixElement, key: string): string | undefined {\n const value = element.userData?.[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nexport function attrNumber(element: UixElement, key: string): number | undefined {\n const raw = attrString(element, key);\n if (raw === undefined) {\n return undefined;\n }\n const value = Number(raw);\n return Number.isFinite(value) ? value : undefined;\n}\n\nexport function attrBoolean(element: UixElement, key: string): boolean | undefined {\n const raw = attrString(element, key);\n if (raw === undefined) {\n return undefined;\n }\n return raw === 'true' || raw === '1' || raw === 'on';\n}\n"]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Expandable label -
|
|
2
|
+
* Expandable label - `<uix-expandable-label>`.
|
|
3
3
|
*
|
|
4
4
|
* A multi-line label that collapses to a character budget with an ellipsis
|
|
5
5
|
* and an inline more/less affordance.
|
|
6
6
|
*
|
|
7
7
|
* ```html
|
|
8
|
-
* <
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* <
|
|
12
|
-
* <
|
|
13
|
-
* </
|
|
8
|
+
* <uix-expandable-label data-uix-id="lore"
|
|
9
|
+
* data-uix-lines="2" data-uix-chars-per-line="40"
|
|
10
|
+
* data-uix-text="Long text..." class="my-label">
|
|
11
|
+
* <uix-text>.</uix-text>
|
|
12
|
+
* <uix-more>more</uix-more>
|
|
13
|
+
* </uix-expandable-label>
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
import { Emitter } from '../core/events.js';
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Expandable label -
|
|
2
|
+
* Expandable label - `<uix-expandable-label>`.
|
|
3
3
|
*
|
|
4
4
|
* A multi-line label that collapses to a character budget with an ellipsis
|
|
5
5
|
* and an inline more/less affordance.
|
|
6
6
|
*
|
|
7
7
|
* ```html
|
|
8
|
-
* <
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* <
|
|
12
|
-
* <
|
|
13
|
-
* </
|
|
8
|
+
* <uix-expandable-label data-uix-id="lore"
|
|
9
|
+
* data-uix-lines="2" data-uix-chars-per-line="40"
|
|
10
|
+
* data-uix-text="Long text..." class="my-label">
|
|
11
|
+
* <uix-text>.</uix-text>
|
|
12
|
+
* <uix-more>more</uix-more>
|
|
13
|
+
* </uix-expandable-label>
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
import { Emitter } from '../core/events.js';
|
|
@@ -55,7 +55,7 @@ export function upgradeExpandableLabel(root) {
|
|
|
55
55
|
...(lines !== undefined && { collapsedLines: lines }),
|
|
56
56
|
...(charsPerLine !== undefined && { charsPerLine }),
|
|
57
57
|
});
|
|
58
|
-
const toggle = findRole(root, '
|
|
58
|
+
const toggle = findRole(root, 'more');
|
|
59
59
|
const handle = new ExpandableLabelHandle(model, findRole(root, 'text'), toggle);
|
|
60
60
|
toggle?.addEventListener('click', () => handle.toggle());
|
|
61
61
|
return handle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expandable-label.js","sourceRoot":"","sources":["../../src/controls/expandable-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAc,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE5E,MAAM,OAAO,qBAAqB;IAIrB;IACD;IACA;IALD,MAAM,GAAG,IAAI,OAAO,EAAuB,CAAC;IAErD,YACW,KAAsB,EACvB,WAAmC,EACnC,aAAqC;QAFpC,UAAK,GAAL,KAAK,CAAiB;QACvB,gBAAW,GAAX,WAAW,CAAwB;QACnC,kBAAa,GAAb,aAAa,CAAwB;QAE7C,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAClE,wEAAwE;QACxE,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC;YAChC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW;YAC5B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;SAChD,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAgB;IACrD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC;QAChC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE;QACvC,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QACrD,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;KACpD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"expandable-label.js","sourceRoot":"","sources":["../../src/controls/expandable-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAc,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE5E,MAAM,OAAO,qBAAqB;IAIrB;IACD;IACA;IALD,MAAM,GAAG,IAAI,OAAO,EAAuB,CAAC;IAErD,YACW,KAAsB,EACvB,WAAmC,EACnC,aAAqC;QAFpC,UAAK,GAAL,KAAK,CAAiB;QACvB,gBAAW,GAAX,WAAW,CAAwB;QACnC,kBAAa,GAAb,aAAa,CAAwB;QAE7C,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAClE,wEAAwE;QACxE,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC;YAChC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW;YAC5B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;SAChD,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAgB;IACrD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC;QAChC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE;QACvC,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QACrD,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;KACpD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,qBAAqB,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAChF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Expandable label - `<uix-expandable-label>`.\n *\n * A multi-line label that collapses to a character budget with an ellipsis\n * and an inline more/less affordance.\n *\n * ```html\n * <uix-expandable-label data-uix-id=\"lore\"\n * data-uix-lines=\"2\" data-uix-chars-per-line=\"40\"\n * data-uix-text=\"Long text...\" class=\"my-label\">\n * <uix-text>.</uix-text>\n * <uix-more>more</uix-more>\n * </uix-expandable-label>\n * ```\n */\nimport { Emitter } from '../core/events.js';\nimport { ExpandableModel } from '../core/expandable-model.js';\nimport { UixElement, attrNumber, attrString, findRole } from './element.js';\n\nexport class ExpandableLabelHandle {\n readonly events = new Emitter<{ change: boolean }>();\n\n constructor(\n readonly model: ExpandableModel,\n private textElement: UixElement | undefined,\n private toggleElement: UixElement | undefined,\n ) {\n this.render();\n }\n\n get expanded(): boolean {\n return this.model.expanded;\n }\n\n setText(text: string): void {\n this.model.setText(text);\n this.render();\n }\n\n toggle(): void {\n this.events.emit('change', this.model.toggle());\n this.render();\n }\n\n private render(): void {\n this.textElement?.setProperties({ text: this.model.displayText });\n // Hide the affordance entirely when the text fits the collapsed budget.\n this.toggleElement?.setProperties({\n text: this.model.toggleLabel,\n display: this.model.overflows ? 'flex' : 'none',\n });\n }\n}\n\nexport function upgradeExpandableLabel(root: UixElement): ExpandableLabelHandle {\n const lines = attrNumber(root, 'uixLines');\n const charsPerLine = attrNumber(root, 'uixCharsPerLine');\n const model = new ExpandableModel({\n text: attrString(root, 'uixText') ?? '',\n ...(lines !== undefined && { collapsedLines: lines }),\n ...(charsPerLine !== undefined && { charsPerLine }),\n });\n const toggle = findRole(root, 'more');\n const handle = new ExpandableLabelHandle(model, findRole(root, 'text'), toggle);\n toggle?.addEventListener('click', () => handle.toggle());\n return handle;\n}\n"]}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Log view -
|
|
2
|
+
* Log view - `<uix-log-view>`.
|
|
3
3
|
*
|
|
4
4
|
* A console-style scrolling list rendered into a fixed pool of row elements
|
|
5
5
|
* (UIKitML's text rule: each row needs a literal placeholder child). Rows
|
|
6
|
-
* are the descendants
|
|
7
|
-
*
|
|
6
|
+
* are the `<uix-line>` descendants, in document order; their count is the
|
|
7
|
+
* viewport height.
|
|
8
8
|
*
|
|
9
9
|
* ```html
|
|
10
|
-
* <
|
|
10
|
+
* <uix-log-view data-uix-id="combat" data-uix-capacity="200">
|
|
11
11
|
* <div class="rows">
|
|
12
|
-
* <
|
|
13
|
-
* <
|
|
14
|
-
* <
|
|
12
|
+
* <uix-line>.</uix-line>
|
|
13
|
+
* <uix-line>.</uix-line>
|
|
14
|
+
* <uix-line>.</uix-line>
|
|
15
15
|
* </div>
|
|
16
16
|
* <div class="log-buttons">
|
|
17
|
-
* <
|
|
18
|
-
* <
|
|
19
|
-
* <
|
|
17
|
+
* <uix-up>▲</uix-up>
|
|
18
|
+
* <uix-down>▼</uix-down>
|
|
19
|
+
* <uix-clear>clear</uix-clear>
|
|
20
20
|
* </div>
|
|
21
|
-
* <
|
|
22
|
-
* </
|
|
21
|
+
* <uix-status>.</uix-status>
|
|
22
|
+
* </uix-log-view>
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
import { LogModel } from '../core/log-model.js';
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Log view -
|
|
2
|
+
* Log view - `<uix-log-view>`.
|
|
3
3
|
*
|
|
4
4
|
* A console-style scrolling list rendered into a fixed pool of row elements
|
|
5
5
|
* (UIKitML's text rule: each row needs a literal placeholder child). Rows
|
|
6
|
-
* are the descendants
|
|
7
|
-
*
|
|
6
|
+
* are the `<uix-line>` descendants, in document order; their count is the
|
|
7
|
+
* viewport height.
|
|
8
8
|
*
|
|
9
9
|
* ```html
|
|
10
|
-
* <
|
|
10
|
+
* <uix-log-view data-uix-id="combat" data-uix-capacity="200">
|
|
11
11
|
* <div class="rows">
|
|
12
|
-
* <
|
|
13
|
-
* <
|
|
14
|
-
* <
|
|
12
|
+
* <uix-line>.</uix-line>
|
|
13
|
+
* <uix-line>.</uix-line>
|
|
14
|
+
* <uix-line>.</uix-line>
|
|
15
15
|
* </div>
|
|
16
16
|
* <div class="log-buttons">
|
|
17
|
-
* <
|
|
18
|
-
* <
|
|
19
|
-
* <
|
|
17
|
+
* <uix-up>▲</uix-up>
|
|
18
|
+
* <uix-down>▼</uix-down>
|
|
19
|
+
* <uix-clear>clear</uix-clear>
|
|
20
20
|
* </div>
|
|
21
|
-
* <
|
|
22
|
-
* </
|
|
21
|
+
* <uix-status>.</uix-status>
|
|
22
|
+
* </uix-log-view>
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
import { LogModel } from '../core/log-model.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-view.js","sourceRoot":"","sources":["../../src/controls/log-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Log view -
|
|
1
|
+
{"version":3,"file":"log-view.js","sourceRoot":"","sources":["../../src/controls/log-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Log view - `<uix-log-view>`.\n *\n * A console-style scrolling list rendered into a fixed pool of row elements\n * (UIKitML's text rule: each row needs a literal placeholder child). Rows\n * are the `<uix-line>` descendants, in document order; their count is the\n * viewport height.\n *\n * ```html\n * <uix-log-view data-uix-id=\"combat\" data-uix-capacity=\"200\">\n * <div class=\"rows\">\n * <uix-line>.</uix-line>\n * <uix-line>.</uix-line>\n * <uix-line>.</uix-line>\n * </div>\n * <div class=\"log-buttons\">\n * <uix-up>▲</uix-up>\n * <uix-down>▼</uix-down>\n * <uix-clear>clear</uix-clear>\n * </div>\n * <uix-status>.</uix-status>\n * </uix-log-view>\n * ```\n */\nimport { LogModel } from '../core/log-model.js';\nimport { UixElement, attrNumber, findRole, findRoles } from './element.js';\n\nexport class LogViewHandle {\n constructor(\n readonly model: LogModel,\n private rows: UixElement[],\n private statusElement: UixElement | undefined,\n ) {\n this.render();\n }\n\n push(message: string, level = 'info'): void {\n this.model.push(message, level);\n this.render();\n }\n\n clear(): void {\n this.model.clear();\n this.render();\n }\n\n scrollUp(rows = 1): void {\n this.model.scrollUp(rows);\n this.render();\n }\n\n scrollDown(rows = 1): void {\n this.model.scrollDown(rows);\n this.render();\n }\n\n private render(): void {\n const visible = this.model.visible();\n this.rows.forEach((row, index) => {\n const entry = visible[index];\n row.setProperties({\n // UIKitML renders \"\" as empty text - exactly what vacant rows need.\n text: entry ? entry.message : '',\n });\n });\n if (this.statusElement) {\n const suffix = this.model.isFollowing ? '' : ' (scrolled)';\n this.statusElement.setProperties({\n text: `${this.model.count} entries${suffix}`,\n });\n }\n }\n}\n\nexport function upgradeLogView(root: UixElement): LogViewHandle {\n const rows = findRoles(root, 'line');\n const capacity = attrNumber(root, 'uixCapacity');\n const model = new LogModel({\n visibleRows: Math.max(1, rows.length),\n ...(capacity !== undefined && { capacity }),\n });\n const handle = new LogViewHandle(model, rows, findRole(root, 'status'));\n findRole(root, 'up')?.addEventListener('click', () => handle.scrollUp());\n findRole(root, 'down')?.addEventListener('click', () => handle.scrollDown());\n findRole(root, 'clear')?.addEventListener('click', () => handle.clear());\n return handle;\n}\n"]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stepper control -
|
|
2
|
+
* Stepper control - `<uix-stepper>`.
|
|
3
3
|
*
|
|
4
|
-
* Markup contract (
|
|
4
|
+
* Markup contract (parts are `<uix-*>` elements; params are `data-uix-*`):
|
|
5
5
|
* ```html
|
|
6
|
-
* <
|
|
7
|
-
*
|
|
8
|
-
* <
|
|
9
|
-
* <
|
|
10
|
-
* <
|
|
11
|
-
* </
|
|
6
|
+
* <uix-stepper data-uix-id="count" data-uix-min="0" data-uix-max="10"
|
|
7
|
+
* data-uix-step="1" data-uix-value="5" class="my-stepper">
|
|
8
|
+
* <uix-decrement>-</uix-decrement>
|
|
9
|
+
* <uix-value>.</uix-value>
|
|
10
|
+
* <uix-increment>+</uix-increment>
|
|
11
|
+
* </uix-stepper>
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
import { Emitter } from '../core/events.js';
|
package/dist/controls/stepper.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stepper control -
|
|
2
|
+
* Stepper control - `<uix-stepper>`.
|
|
3
3
|
*
|
|
4
|
-
* Markup contract (
|
|
4
|
+
* Markup contract (parts are `<uix-*>` elements; params are `data-uix-*`):
|
|
5
5
|
* ```html
|
|
6
|
-
* <
|
|
7
|
-
*
|
|
8
|
-
* <
|
|
9
|
-
* <
|
|
10
|
-
* <
|
|
11
|
-
* </
|
|
6
|
+
* <uix-stepper data-uix-id="count" data-uix-min="0" data-uix-max="10"
|
|
7
|
+
* data-uix-step="1" data-uix-value="5" class="my-stepper">
|
|
8
|
+
* <uix-decrement>-</uix-decrement>
|
|
9
|
+
* <uix-value>.</uix-value>
|
|
10
|
+
* <uix-increment>+</uix-increment>
|
|
11
|
+
* </uix-stepper>
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
import { Emitter } from '../core/events.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stepper.js","sourceRoot":"","sources":["../../src/controls/stepper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAuB,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAc,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,OAAO,aAAa;IAIb;IACD;IACA;IACA;IAND,MAAM,GAAG,IAAI,OAAO,EAAsB,CAAC;IAEpD,YACW,KAAmB,EACpB,YAAoC,EACpC,gBAAwC,EACxC,gBAAwC;QAHvC,UAAK,GAAL,KAAK,CAAc;QACpB,iBAAY,GAAZ,YAAY,CAAwB;QACpC,qBAAgB,GAAhB,gBAAgB,CAAwB;QACxC,qBAAgB,GAAhB,gBAAgB,CAAwB;QAEhD,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,KAAa;QACf,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,SAAS;QACP,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,SAAS;QACP,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC;YACnC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB;SACxD,CAAC,CAAC;QACH,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC;YACnC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB;SACxD,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,IAAgB;IAC7C,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;IACzC,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;IACzC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACvF,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Stepper control -
|
|
1
|
+
{"version":3,"file":"stepper.js","sourceRoot":"","sources":["../../src/controls/stepper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stepper control - `<uix-stepper>`.\n *\n * Markup contract (parts are `<uix-*>` elements; params are `data-uix-*`):\n * ```html\n * <uix-stepper data-uix-id=\"count\" data-uix-min=\"0\" data-uix-max=\"10\"\n * data-uix-step=\"1\" data-uix-value=\"5\" class=\"my-stepper\">\n * <uix-decrement>-</uix-decrement>\n * <uix-value>.</uix-value>\n * <uix-increment>+</uix-increment>\n * </uix-stepper>\n * ```\n */\nimport { Emitter } from '../core/events.js';\nimport { StepperModel, type StepperOptions } from '../core/stepper-model.js';\nimport { UixElement, attrNumber, findRole } from './element.js';\n\nconst DISABLED_OPACITY = 0.35;\n\nexport class StepperHandle {\n readonly events = new Emitter<{ change: number }>();\n\n constructor(\n readonly model: StepperModel,\n private valueElement: UixElement | undefined,\n private decrementElement: UixElement | undefined,\n private incrementElement: UixElement | undefined,\n ) {\n this.render();\n }\n\n get value(): number {\n return this.model.value;\n }\n\n set(value: number): void {\n if (this.model.set(value)) {\n this.render();\n this.events.emit('change', this.model.value);\n }\n }\n\n increment(): void {\n this.set(this.model.value + this.model.step);\n }\n\n decrement(): void {\n this.set(this.model.value - this.model.step);\n }\n\n private render(): void {\n this.valueElement?.setProperties({ text: String(this.model.value) });\n this.decrementElement?.setProperties({\n opacity: this.model.canDecrement ? 1 : DISABLED_OPACITY,\n });\n this.incrementElement?.setProperties({\n opacity: this.model.canIncrement ? 1 : DISABLED_OPACITY,\n });\n }\n}\n\nexport function upgradeStepper(root: UixElement): StepperHandle {\n const options: StepperOptions = {};\n const min = attrNumber(root, 'uixMin');\n const max = attrNumber(root, 'uixMax');\n const step = attrNumber(root, 'uixStep');\n const value = attrNumber(root, 'uixValue');\n if (min !== undefined) options.min = min;\n if (max !== undefined) options.max = max;\n if (step !== undefined) options.step = step;\n if (value !== undefined) options.value = value;\n const model = new StepperModel(options);\n const decrement = findRole(root, 'decrement');\n const increment = findRole(root, 'increment');\n const handle = new StepperHandle(model, findRole(root, 'value'), decrement, increment);\n decrement?.addEventListener('click', () => handle.decrement());\n increment?.addEventListener('click', () => handle.increment());\n return handle;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Toggle control -
|
|
2
|
+
* Toggle control - `<uix-toggle>`.
|
|
3
3
|
*
|
|
4
4
|
* The whole control is the click target. Optional attributes:
|
|
5
5
|
* `data-uix-on` (initial state), `data-uix-on-text` / `data-uix-off-text`
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* `data-uix-off-color` (background color applied to the control root).
|
|
8
8
|
*
|
|
9
9
|
* ```html
|
|
10
|
-
* <
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* <
|
|
14
|
-
* </
|
|
10
|
+
* <uix-toggle data-uix-id="shield" data-uix-on="true"
|
|
11
|
+
* data-uix-on-text="SHIELD ON" data-uix-off-text="SHIELD OFF"
|
|
12
|
+
* data-uix-on-color="#2e7d32" data-uix-off-color="#5d4037" class="my-toggle">
|
|
13
|
+
* <uix-label>.</uix-label>
|
|
14
|
+
* </uix-toggle>
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
import { Emitter } from '../core/events.js';
|
package/dist/controls/toggle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Toggle control -
|
|
2
|
+
* Toggle control - `<uix-toggle>`.
|
|
3
3
|
*
|
|
4
4
|
* The whole control is the click target. Optional attributes:
|
|
5
5
|
* `data-uix-on` (initial state), `data-uix-on-text` / `data-uix-off-text`
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* `data-uix-off-color` (background color applied to the control root).
|
|
8
8
|
*
|
|
9
9
|
* ```html
|
|
10
|
-
* <
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* <
|
|
14
|
-
* </
|
|
10
|
+
* <uix-toggle data-uix-id="shield" data-uix-on="true"
|
|
11
|
+
* data-uix-on-text="SHIELD ON" data-uix-off-text="SHIELD OFF"
|
|
12
|
+
* data-uix-on-color="#2e7d32" data-uix-off-color="#5d4037" class="my-toggle">
|
|
13
|
+
* <uix-label>.</uix-label>
|
|
14
|
+
* </uix-toggle>
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
import { Emitter } from '../core/events.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toggle.js","sourceRoot":"","sources":["../../src/controls/toggle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAc,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,OAAO,YAAY;IAIZ;IACD;IACA;IALD,MAAM,GAAG,IAAI,OAAO,EAAuB,CAAC;IAErD,YACW,KAAkB,EACnB,IAAgB,EAChB,YAAoC;QAFnC,UAAK,GAAL,KAAK,CAAa;QACnB,SAAI,GAAJ,IAAI,CAAY;QAChB,iBAAY,GAAZ,YAAY,CAAwB;QAE5C,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,KAAc;QAChB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAEO,MAAM;QACZ,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC5B,MAAM,IAAI,GAAG,EAAE;YACb,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC;YAC9C,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,EAAE;YACd,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;YACrC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACzC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Toggle control -
|
|
1
|
+
{"version":3,"file":"toggle.js","sourceRoot":"","sources":["../../src/controls/toggle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAc,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,OAAO,YAAY;IAIZ;IACD;IACA;IALD,MAAM,GAAG,IAAI,OAAO,EAAuB,CAAC;IAErD,YACW,KAAkB,EACnB,IAAgB,EAChB,YAAoC;QAFnC,UAAK,GAAL,KAAK,CAAa;QACnB,SAAI,GAAJ,IAAI,CAAY;QAChB,iBAAY,GAAZ,YAAY,CAAwB;QAE5C,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,KAAc;QAChB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAEO,MAAM;QACZ,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC5B,MAAM,IAAI,GAAG,EAAE;YACb,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC;YAC9C,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,EAAE;YACd,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;YACrC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACzC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Toggle control - `<uix-toggle>`.\n *\n * The whole control is the click target. Optional attributes:\n * `data-uix-on` (initial state), `data-uix-on-text` / `data-uix-off-text`\n * (label written into the `label` role), `data-uix-on-color` /\n * `data-uix-off-color` (background color applied to the control root).\n *\n * ```html\n * <uix-toggle data-uix-id=\"shield\" data-uix-on=\"true\"\n * data-uix-on-text=\"SHIELD ON\" data-uix-off-text=\"SHIELD OFF\"\n * data-uix-on-color=\"#2e7d32\" data-uix-off-color=\"#5d4037\" class=\"my-toggle\">\n * <uix-label>.</uix-label>\n * </uix-toggle>\n * ```\n */\nimport { Emitter } from '../core/events.js';\nimport { ToggleModel } from '../core/toggle-model.js';\nimport { UixElement, attrBoolean, attrString, findRole } from './element.js';\n\nexport class ToggleHandle {\n readonly events = new Emitter<{ change: boolean }>();\n\n constructor(\n readonly model: ToggleModel,\n private root: UixElement,\n private labelElement: UixElement | undefined,\n ) {\n this.render();\n }\n\n get value(): boolean {\n return this.model.value;\n }\n\n set(value: boolean): void {\n if (this.model.set(value)) {\n this.render();\n this.events.emit('change', this.model.value);\n }\n }\n\n toggle(): void {\n this.set(!this.model.value);\n }\n\n private render(): void {\n const on = this.model.value;\n const text = on\n ? (attrString(this.root, 'uixOnText') ?? 'ON')\n : (attrString(this.root, 'uixOffText') ?? 'OFF');\n this.labelElement?.setProperties({ text });\n const color = on\n ? attrString(this.root, 'uixOnColor')\n : attrString(this.root, 'uixOffColor');\n if (color) {\n this.root.setProperties({ backgroundColor: color });\n }\n }\n}\n\nexport function upgradeToggle(root: UixElement): ToggleHandle {\n const model = new ToggleModel(attrBoolean(root, 'uixOn') ?? false);\n const handle = new ToggleHandle(model, root, findRole(root, 'label'));\n root.addEventListener('click', () => handle.toggle());\n return handle;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Panel upgrade - scans a loaded panel for
|
|
3
|
-
* matching
|
|
2
|
+
* Panel upgrade - scans a loaded panel for `<uix-*>` control elements and
|
|
3
|
+
* wires the matching behaviour. App code retrieves the resulting handles via
|
|
4
4
|
* `panelControlsFor(document)` and the control's `data-uix-id`.
|
|
5
5
|
*/
|
|
6
6
|
import { UixElement } from './element.js';
|
package/dist/controls/upgrade.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Panel upgrade - scans a loaded panel for
|
|
3
|
-
* matching
|
|
2
|
+
* Panel upgrade - scans a loaded panel for `<uix-*>` control elements and
|
|
3
|
+
* wires the matching behaviour. App code retrieves the resulting handles via
|
|
4
4
|
* `panelControlsFor(document)` and the control's `data-uix-id`.
|
|
5
5
|
*/
|
|
6
|
-
import { attrString, walk } from './element.js';
|
|
6
|
+
import { attrString, tagOf, walk } from './element.js';
|
|
7
7
|
import { StepperHandle, upgradeStepper } from './stepper.js';
|
|
8
8
|
import { ToggleHandle, upgradeToggle } from './toggle.js';
|
|
9
9
|
import { ExpandableLabelHandle, upgradeExpandableLabel } from './expandable-label.js';
|
|
@@ -48,12 +48,35 @@ export class PanelControls {
|
|
|
48
48
|
return handle;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Control tags. These are the custom elements an author writes; every part
|
|
53
|
+
* inside them (`<uix-value>`, `<uix-line>`, …) is a role, resolved by the
|
|
54
|
+
* individual upgraders through `findRole`.
|
|
55
|
+
*/
|
|
51
56
|
const UPGRADERS = {
|
|
52
|
-
stepper: upgradeStepper,
|
|
53
|
-
toggle: upgradeToggle,
|
|
54
|
-
'expandable-label': upgradeExpandableLabel,
|
|
55
|
-
'log-view': upgradeLogView,
|
|
57
|
+
'uix-stepper': upgradeStepper,
|
|
58
|
+
'uix-toggle': upgradeToggle,
|
|
59
|
+
'uix-expandable-label': upgradeExpandableLabel,
|
|
60
|
+
'uix-log-view': upgradeLogView,
|
|
56
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Parts claimed by the control that owns them, resolved through `findRole`.
|
|
64
|
+
* Listed here only so a genuine typo in a control tag still gets a warning
|
|
65
|
+
* instead of being silently mistaken for a role.
|
|
66
|
+
*/
|
|
67
|
+
const ROLE_TAGS = new Set([
|
|
68
|
+
'uix-decrement',
|
|
69
|
+
'uix-value',
|
|
70
|
+
'uix-increment',
|
|
71
|
+
'uix-label',
|
|
72
|
+
'uix-line',
|
|
73
|
+
'uix-up',
|
|
74
|
+
'uix-down',
|
|
75
|
+
'uix-clear',
|
|
76
|
+
'uix-status',
|
|
77
|
+
'uix-text',
|
|
78
|
+
'uix-more',
|
|
79
|
+
]);
|
|
57
80
|
const registry = new WeakMap();
|
|
58
81
|
/**
|
|
59
82
|
* Upgrade every `data-uix` control under `root` and remember the result
|
|
@@ -66,13 +89,15 @@ export function upgradePanel(documentKey, root) {
|
|
|
66
89
|
}
|
|
67
90
|
const controls = new PanelControls();
|
|
68
91
|
walk(root, (element) => {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
92
|
+
const tag = tagOf(element);
|
|
93
|
+
if (tag === undefined || !tag.startsWith('uix-')) {
|
|
71
94
|
return;
|
|
72
95
|
}
|
|
73
|
-
const upgrader = UPGRADERS[
|
|
96
|
+
const upgrader = UPGRADERS[tag];
|
|
74
97
|
if (!upgrader) {
|
|
75
|
-
|
|
98
|
+
if (!ROLE_TAGS.has(tag)) {
|
|
99
|
+
console.warn(`[uix] unknown control element <${tag}> - skipped`);
|
|
100
|
+
}
|
|
76
101
|
return;
|
|
77
102
|
}
|
|
78
103
|
controls.add(attrString(element, 'uixId'), upgrader(element));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/controls/upgrade.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAc,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/controls/upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Panel upgrade - scans a loaded panel for `<uix-*>` control elements and\n * wires the matching behaviour. App code retrieves the resulting handles via\n * `panelControlsFor(document)` and the control's `data-uix-id`.\n */\nimport { UixElement, attrString, tagOf, walk } from './element.js';\nimport { StepperHandle, upgradeStepper } from './stepper.js';\nimport { ToggleHandle, upgradeToggle } from './toggle.js';\nimport { ExpandableLabelHandle, upgradeExpandableLabel } from './expandable-label.js';\nimport { LogViewHandle, upgradeLogView } from './log-view.js';\n\nexport type ControlHandle =\n | StepperHandle\n | ToggleHandle\n | ExpandableLabelHandle\n | LogViewHandle;\n\nexport class PanelControls {\n private handles = new Map<string, ControlHandle>();\n private anonymous = 0;\n\n add(id: string | undefined, handle: ControlHandle): void {\n const key = id ?? `uix-control-${this.anonymous++}`;\n if (this.handles.has(key)) {\n throw new Error(`[uix] duplicate control id \"${key}\" in panel`);\n }\n this.handles.set(key, handle);\n }\n\n /** Typed accessors - throw on wrong id so mistakes surface immediately. */\n stepper(id: string): StepperHandle {\n return this.expect(id, StepperHandle);\n }\n\n toggle(id: string): ToggleHandle {\n return this.expect(id, ToggleHandle);\n }\n\n expandableLabel(id: string): ExpandableLabelHandle {\n return this.expect(id, ExpandableLabelHandle);\n }\n\n logView(id: string): LogViewHandle {\n return this.expect(id, LogViewHandle);\n }\n\n get(id: string): ControlHandle | undefined {\n return this.handles.get(id);\n }\n\n ids(): string[] {\n return [...this.handles.keys()];\n }\n\n private expect<T extends ControlHandle>(\n id: string,\n kind: abstract new (...args: never[]) => T,\n ): T {\n const handle = this.handles.get(id);\n if (!handle) {\n throw new Error(`[uix] no control \"${id}\" in panel (have: ${this.ids().join(', ') || 'none'})`);\n }\n if (!(handle instanceof kind)) {\n throw new Error(`[uix] control \"${id}\" is not a ${kind.name}`);\n }\n return handle;\n }\n}\n\n/**\n * Control tags. These are the custom elements an author writes; every part\n * inside them (`<uix-value>`, `<uix-line>`, …) is a role, resolved by the\n * individual upgraders through `findRole`.\n */\nconst UPGRADERS: Record<string, (root: UixElement) => ControlHandle> = {\n 'uix-stepper': upgradeStepper,\n 'uix-toggle': upgradeToggle,\n 'uix-expandable-label': upgradeExpandableLabel,\n 'uix-log-view': upgradeLogView,\n};\n\n/**\n * Parts claimed by the control that owns them, resolved through `findRole`.\n * Listed here only so a genuine typo in a control tag still gets a warning\n * instead of being silently mistaken for a role.\n */\nconst ROLE_TAGS = new Set([\n 'uix-decrement',\n 'uix-value',\n 'uix-increment',\n 'uix-label',\n 'uix-line',\n 'uix-up',\n 'uix-down',\n 'uix-clear',\n 'uix-status',\n 'uix-text',\n 'uix-more',\n]);\n\nconst registry = new WeakMap<object, PanelControls>();\n\n/**\n * Upgrade every `data-uix` control under `root` and remember the result\n * against `documentKey` (the panel's `UIKitDocument`). Idempotent per key.\n */\nexport function upgradePanel(documentKey: object, root: UixElement): PanelControls {\n const existing = registry.get(documentKey);\n if (existing) {\n return existing;\n }\n const controls = new PanelControls();\n walk(root, (element) => {\n const tag = tagOf(element);\n if (tag === undefined || !tag.startsWith('uix-')) {\n return;\n }\n const upgrader = UPGRADERS[tag];\n if (!upgrader) {\n if (!ROLE_TAGS.has(tag)) {\n console.warn(`[uix] unknown control element <${tag}> - skipped`);\n }\n return;\n }\n controls.add(attrString(element, 'uixId'), upgrader(element));\n });\n registry.set(documentKey, controls);\n return controls;\n}\n\n/** Controls of an upgraded panel; undefined before `UIControlsSystem` ran. */\nexport function panelControlsFor(documentKey: object): PanelControls | undefined {\n return registry.get(documentKey);\n}\n"]}
|
package/dist/scene.d.ts
CHANGED
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* applyScene(iwsdkTarget, PLAYGROUND); // ECS entities
|
|
10
10
|
* applyScene(vanillaHost, PLAYGROUND); // three.js groups
|
|
11
11
|
*
|
|
12
|
-
* Panel config paths stay strings (`./ui/foo.
|
|
13
|
-
* resolves them
|
|
14
|
-
*
|
|
12
|
+
* Panel config paths stay opaque strings (`./ui/foo.uikitml`) because each
|
|
13
|
+
* adapter resolves them its own way. IWSDK 0.5 fetches the source and parses
|
|
14
|
+
* it inside PanelUI; the vanilla and XR Blocks hosts parse it through the
|
|
15
|
+
* `loadConfig` resolver and are handed the resulting AST. The descriptor makes
|
|
16
|
+
* no claim about the artefact behind the path.
|
|
15
17
|
*/
|
|
16
18
|
import type { DockModeValue } from './core/dock-state.js';
|
|
17
19
|
import type { RegionFlow, Vec3 } from './core/region-layout.js';
|
|
@@ -19,7 +21,7 @@ import type { RegionFlow, Vec3 } from './core/region-layout.js';
|
|
|
19
21
|
export interface SceneWindow {
|
|
20
22
|
id: string;
|
|
21
23
|
title: string;
|
|
22
|
-
/** Path to the
|
|
24
|
+
/** Path to the UIKitML source, e.g. `./ui/clicker.uikitml`. */
|
|
23
25
|
config: string;
|
|
24
26
|
/** World position for world-locked windows. */
|
|
25
27
|
position?: Vec3;
|
package/dist/scene.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene.js","sourceRoot":"","sources":["../src/scene.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scene.js","sourceRoot":"","sources":["../src/scene.ts"],"names":[],"mappings":"AAyEA;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,KAAsB;IACpE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACzC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAsB;IAClD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACjE,QAAQ,CAAC,IAAI,CACX,WAAW,MAAM,CAAC,EAAE,gCAAgC,MAAM,CAAC,MAAM,GAAG,CACrE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Portable scene descriptors.\n *\n * A scene is plain DATA - which windows exist, where they sit, which dock\n * regions they can drop into - with no engine types anywhere. Each adapter\n * knows how to apply the same descriptor through its own factories, so one\n * playground definition drives IWSDK, XR Blocks and plain three.js alike:\n *\n * applyScene(iwsdkTarget, PLAYGROUND); // ECS entities\n * applyScene(vanillaHost, PLAYGROUND); // three.js groups\n *\n * Panel config paths stay opaque strings (`./ui/foo.uikitml`) because each\n * adapter resolves them its own way. IWSDK 0.5 fetches the source and parses\n * it inside PanelUI; the vanilla and XR Blocks hosts parse it through the\n * `loadConfig` resolver and are handed the resulting AST. The descriptor makes\n * no claim about the artefact behind the path.\n */\nimport type { DockModeValue } from './core/dock-state.js';\nimport type { RegionFlow, Vec3 } from './core/region-layout.js';\n\n/** One window in a scene. */\nexport interface SceneWindow {\n id: string;\n title: string;\n /** Path to the UIKitML source, e.g. `./ui/clicker.uikitml`. */\n config: string;\n /** World position for world-locked windows. */\n position?: Vec3;\n maxWidth?: number;\n maxHeight?: number;\n dockMode?: DockModeValue;\n /** Dock straight into this region on spawn. */\n region?: string;\n followOffset?: Vec3;\n followSpeed?: number;\n followTolerance?: number;\n movable?: boolean;\n closable?: boolean;\n minimizable?: boolean;\n pinnable?: boolean;\n}\n\n/** One dock region in a scene. */\nexport interface SceneRegion {\n id: string;\n flow?: RegionFlow;\n pitch?: number;\n columns?: number;\n capacity?: number;\n snapRadius?: number;\n position?: Vec3;\n /** Body-lock the region so it follows the viewer. */\n follow?: boolean;\n followOffset?: Vec3;\n}\n\n/** A complete, engine-free scene definition. */\nexport interface SceneDescriptor {\n name?: string;\n regions?: readonly SceneRegion[];\n windows: readonly SceneWindow[];\n}\n\n/**\n * What an adapter must provide for {@link applyScene} to build a scene.\n * Both shipped adapters implement this; a new adapter only needs these two\n * methods to gain full scene portability.\n */\nexport interface SceneTarget {\n spawnRegion(region: SceneRegion): void;\n spawnWindow(window: SceneWindow): void;\n}\n\n/**\n * Apply a descriptor to an adapter. Regions are created before windows so a\n * window that spawns docked (`region: 'x'`) always finds its region.\n */\nexport function applyScene(target: SceneTarget, scene: SceneDescriptor): void {\n for (const region of scene.regions ?? []) {\n target.spawnRegion(region);\n }\n for (const window of scene.windows) {\n target.spawnWindow(window);\n }\n}\n\n/** Every distinct panel config path in a scene (for preloading). */\nexport function sceneConfigPaths(scene: SceneDescriptor): string[] {\n return [...new Set(scene.windows.map((window) => window.config))];\n}\n\n/**\n * Validate a descriptor: unique window/region ids, and every `region`\n * reference resolvable. Returns the problems found (empty = valid) rather\n * than throwing, so callers can report them all at once.\n */\nexport function validateScene(scene: SceneDescriptor): string[] {\n const problems: string[] = [];\n const regionIds = new Set<string>();\n for (const region of scene.regions ?? []) {\n if (regionIds.has(region.id)) {\n problems.push(`duplicate region id \"${region.id}\"`);\n }\n regionIds.add(region.id);\n }\n const windowIds = new Set<string>();\n for (const window of scene.windows) {\n if (windowIds.has(window.id)) {\n problems.push(`duplicate window id \"${window.id}\"`);\n }\n windowIds.add(window.id);\n if (window.region !== undefined && !regionIds.has(window.region)) {\n problems.push(\n `window \"${window.id}\" docks into unknown region \"${window.region}\"`,\n );\n }\n }\n return problems;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/webxr-uiextensions",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.2",
|
|
4
4
|
"description": "Engine-free core of the Reality Collective UI Extensions: windowing, docking, layout regions and control models for WebXR spatial UI, driven through platform-adapter interfaces. Pair with an engine adapter - @realitycollective/iwsdk-uiextensions (Meta IWSDK) or @realitycollective/xrblocks-uiextensions (Google XR Blocks).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realitycollective",
|