@jarenjs/app 0.67.0 → 0.72.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/README.md
CHANGED
|
@@ -168,6 +168,11 @@ Jaren owns state and orchestration; the widget owns its DOM. Its `props` come fr
|
|
|
168
168
|
Two IDE-shaped primitives ship ready to bind, so a two-pane surface (the studio, the play playground) doesn't re-implement them:
|
|
169
169
|
|
|
170
170
|
- **`createSplitterWidget({ action, grid, rail, cssVar, min, max, step })`** — a drag handle over a pane boundary. It drives a CSS ratio variable *live* during a drag (no per-move dispatch — that would flood the transaction log and undo) and commits the ratio through `action` on pointer-up only, plus keyboard resize as an ARIA separator. Register it like any widget; parameterize the grid/rail selectors, the CSS variable and the commit action so each surface binds its own.
|
|
171
|
+
|
|
172
|
+
Widget props accept `axis: 'x' | 'y'` (default `'x'`) for side-by-side or
|
|
173
|
+
stacked panes. The widget also writes `${cssVar}-first` and
|
|
174
|
+
`${cssVar}-rest` as fractional grid tracks. Pointer cancellation restores
|
|
175
|
+
the starting ratio without committing; vertical resizing uses Up/Down.
|
|
171
176
|
- **`createDocStore({ storage, key })`** — a keyed `save`/`load`/`remove`/`names`/`all` CRUD over an injected `storage` (`localStorage` in the browser, an in-memory object in tests), so the package never touches `localStorage` itself. Paired with **`encodeShare(snapshot)`** / **`decodeShare(token)`**, a Unicode-safe base64url share-link codec (a corrupt token decodes to `null`, never a throw), it is the new/save/load/delete/share pattern behind the studio and play surfaces.
|
|
172
177
|
|
|
173
178
|
Collection keys and document names such as `__proto__` are ordinary own
|
|
@@ -251,6 +256,7 @@ Every subpath a consumer can import, derived from the manifest by
|
|
|
251
256
|
| Import | Kind | Declarations |
|
|
252
257
|
|---|---|---|
|
|
253
258
|
| `@jarenjs/app` | JavaScript | declared |
|
|
259
|
+
| `@jarenjs/app/schemas/jaren-app.authoring.schema.json` | schema | — |
|
|
254
260
|
| `@jarenjs/app/schemas/jaren-app.draft-07.schema.json` | schema | — |
|
|
255
261
|
| `@jarenjs/app/schemas/jaren-app.schema.json` | schema | — |
|
|
256
262
|
| `@jarenjs/app/package.json` | metadata | — |
|
package/dist/types/splitter.d.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* DOM call is guarded so it mounts inertly over a headless stub (there the
|
|
7
7
|
* live drag is browser-verified). The host binds it as a widget and
|
|
8
8
|
* parameterizes the grid/rail selectors, the CSS variable and the commit
|
|
9
|
-
* action
|
|
9
|
+
* action. Widget props `{ ratio, axis: 'x' | 'y' }` select the live direction;
|
|
10
|
+
* the axis defaults to x. The ratio's `-first`/`-rest` CSS variables carry
|
|
11
|
+
* fractional tracks for stacked grids after their fixed toolbar and gaps.
|
|
10
12
|
*/
|
|
11
13
|
/**
|
|
12
14
|
* @param {Object} opts
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jarenjs/app",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.72.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./dist/types/index.d.ts",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"prepack": "npm run build:types"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@jarenjs/core": "^0.
|
|
54
|
-
"@jarenjs/json": "^0.
|
|
55
|
-
"@jarenjs/view": "^0.
|
|
53
|
+
"@jarenjs/core": "^0.72.2",
|
|
54
|
+
"@jarenjs/json": "^0.72.2",
|
|
55
|
+
"@jarenjs/view": "^0.72.2"
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-app/0.1/authoring",
|
|
4
|
+
"title": "Jaren app document 0.1 (authoring profile)",
|
|
5
|
+
"description": "Authoring profile of the canonical grammar: the document SHAPE only, with queryDocument, stylesheetDocument left open. Constrains decoding on small models, where the full grammar does not decode at all. It is deliberately WEAKER than the canonical schema - a document valid here may be nonsense - so the engine compiler is the gate that makes it safe, and generated documents must be compiled before use.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$app": {
|
|
9
|
+
"description": "The app format version; absent implies 0.1.",
|
|
10
|
+
"const": "0.1"
|
|
11
|
+
},
|
|
12
|
+
"state": {
|
|
13
|
+
"description": "The initial state: any JSON value, treated as immutable by every boundary."
|
|
14
|
+
},
|
|
15
|
+
"view": {
|
|
16
|
+
"description": "The view: a JSLT stylesheet document (bare rule array or envelope form) whose evaluation against the state must produce a single vnode.",
|
|
17
|
+
"allOf": [
|
|
18
|
+
{
|
|
19
|
+
"$ref": "#/$defs/stylesheetDocument"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"actions": {
|
|
24
|
+
"description": "Named action documents: Jaren JSON Query documents evaluated with $ bound to the state and the externals $event and $payload.",
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": {
|
|
27
|
+
"$ref": "#/$defs/queryDocument"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"subs": {
|
|
31
|
+
"description": "Subscription entries reconciled against the state after every transition.",
|
|
32
|
+
"type": "array",
|
|
33
|
+
"items": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"run": {
|
|
37
|
+
"description": "The registered subscription handler name.",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"minLength": 1
|
|
40
|
+
},
|
|
41
|
+
"with": {
|
|
42
|
+
"description": "Handler props, passed verbatim (never evaluated). Mutually exclusive with \"withQuery\" and \"for\"."
|
|
43
|
+
},
|
|
44
|
+
"when": {
|
|
45
|
+
"description": "Liveness query, asserted by effective boolean value against the state; absent means always live.",
|
|
46
|
+
"allOf": [
|
|
47
|
+
{
|
|
48
|
+
"$ref": "#/$defs/queryDocument"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"withQuery": {
|
|
53
|
+
"description": "Dynamic props: a query evaluated against the state per reconciliation; the subscription restarts when its resolved key changes. Under \"for\", \"$item\" is bound per instance. Mutually exclusive with \"with\".",
|
|
54
|
+
"allOf": [
|
|
55
|
+
{
|
|
56
|
+
"$ref": "#/$defs/queryDocument"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"key": {
|
|
61
|
+
"description": "Explicit restart key: a query whose resolved value keys the subscription (by value) instead of the resolved props. Requires \"withQuery\" or \"for\"; \"$item\" is bound per instance under \"for\".",
|
|
62
|
+
"allOf": [
|
|
63
|
+
{
|
|
64
|
+
"$ref": "#/$defs/queryDocument"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"for": {
|
|
69
|
+
"description": "Fan-out: a query whose result is the item set — one running instance per item key, reconciled per state change. \"with\" is not allowed beside it.",
|
|
70
|
+
"allOf": [
|
|
71
|
+
{
|
|
72
|
+
"$ref": "#/$defs/queryDocument"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"required": [
|
|
78
|
+
"run"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": [
|
|
84
|
+
"view"
|
|
85
|
+
],
|
|
86
|
+
"$defs": {
|
|
87
|
+
"stylesheetDocument": {
|
|
88
|
+
"description": "A JSLT stylesheet; validate and compile locally."
|
|
89
|
+
},
|
|
90
|
+
"queryDocument": {
|
|
91
|
+
"description": "A Jaren query expression; validate against the full grammar and compile locally."
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"description": "The view: a JSLT stylesheet document (bare rule array or envelope form) whose evaluation against the state must produce a single vnode.",
|
|
17
17
|
"allOf": [
|
|
18
18
|
{
|
|
19
|
-
"$ref": "
|
|
19
|
+
"$ref": "#/definitions/stylesheetDocument"
|
|
20
20
|
}
|
|
21
21
|
]
|
|
22
22
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"description": "Named action documents: Jaren JSON Query documents evaluated with $ bound to the state and the externals $event and $payload.",
|
|
25
25
|
"type": "object",
|
|
26
26
|
"additionalProperties": {
|
|
27
|
-
"$ref": "
|
|
27
|
+
"$ref": "#/definitions/queryDocument"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"subs": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"description": "Liveness query, asserted by effective boolean value against the state; absent means always live.",
|
|
46
46
|
"allOf": [
|
|
47
47
|
{
|
|
48
|
-
"$ref": "
|
|
48
|
+
"$ref": "#/definitions/queryDocument"
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"description": "Dynamic props: a query evaluated against the state per reconciliation; the subscription restarts when its resolved key changes. Under \"for\", \"$item\" is bound per instance. Mutually exclusive with \"with\".",
|
|
54
54
|
"allOf": [
|
|
55
55
|
{
|
|
56
|
-
"$ref": "
|
|
56
|
+
"$ref": "#/definitions/queryDocument"
|
|
57
57
|
}
|
|
58
58
|
]
|
|
59
59
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"description": "Explicit restart key: a query whose resolved value keys the subscription (by value) instead of the resolved props. Requires \"withQuery\" or \"for\"; \"$item\" is bound per instance under \"for\".",
|
|
62
62
|
"allOf": [
|
|
63
63
|
{
|
|
64
|
-
"$ref": "
|
|
64
|
+
"$ref": "#/definitions/queryDocument"
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
67
|
},
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"description": "Fan-out: a query whose result is the item set — one running instance per item key, reconciled per state change. \"with\" is not allowed beside it.",
|
|
70
70
|
"allOf": [
|
|
71
71
|
{
|
|
72
|
-
"$ref": "
|
|
72
|
+
"$ref": "#/definitions/queryDocument"
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
}
|
|
@@ -82,5 +82,13 @@
|
|
|
82
82
|
},
|
|
83
83
|
"required": [
|
|
84
84
|
"view"
|
|
85
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"definitions": {
|
|
87
|
+
"stylesheetDocument": {
|
|
88
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-jslt/0.1/draft-07"
|
|
89
|
+
},
|
|
90
|
+
"queryDocument": {
|
|
91
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-query/0.1/draft-07"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
86
94
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://jarenjs.dev/schemas/jaren-app/0.1",
|
|
4
4
|
"title": "Jaren app document 0.1",
|
|
5
|
-
"description": "Structural grammar of a complete Jaren application document (see packages/app/docs/APP-FORMAT.md): initial state, a JSLT view stylesheet, named action documents and subscription entries. Composes the published Jaren query and JSLT grammars by reference
|
|
5
|
+
"description": "Structural grammar of a complete Jaren application document (see packages/app/docs/APP-FORMAT.md): initial state, a JSLT view stylesheet, named action documents and subscription entries. Composes the published Jaren query and JSLT grammars by reference — register those artifacts alongside this one. The view's OUTPUT vocabulary (vnodes) is published separately as the jaren-vnode schema in @jarenjs/view. This schema is structural validation only: the runtime remains authoritative for semantic rules it cannot express — the transition-object shape an action RETURNS ({state?, patch?, effects?}) is the action's runtime result, not its document form; unknown top-level members are ignored for forward compatibility (APP-FORMAT section 2).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
8
|
"$app": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"description": "The view: a JSLT stylesheet document (bare rule array or envelope form) whose evaluation against the state must produce a single vnode.",
|
|
17
17
|
"allOf": [
|
|
18
18
|
{
|
|
19
|
-
"$ref": "
|
|
19
|
+
"$ref": "#/$defs/stylesheetDocument"
|
|
20
20
|
}
|
|
21
21
|
]
|
|
22
22
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"description": "Named action documents: Jaren JSON Query documents evaluated with $ bound to the state and the externals $event and $payload.",
|
|
25
25
|
"type": "object",
|
|
26
26
|
"additionalProperties": {
|
|
27
|
-
"$ref": "
|
|
27
|
+
"$ref": "#/$defs/queryDocument"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"subs": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"description": "Liveness query, asserted by effective boolean value against the state; absent means always live.",
|
|
46
46
|
"allOf": [
|
|
47
47
|
{
|
|
48
|
-
"$ref": "
|
|
48
|
+
"$ref": "#/$defs/queryDocument"
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"description": "Dynamic props: a query evaluated against the state per reconciliation; the subscription restarts when its resolved key changes. Under \"for\", \"$item\" is bound per instance. Mutually exclusive with \"with\".",
|
|
54
54
|
"allOf": [
|
|
55
55
|
{
|
|
56
|
-
"$ref": "
|
|
56
|
+
"$ref": "#/$defs/queryDocument"
|
|
57
57
|
}
|
|
58
58
|
]
|
|
59
59
|
},
|
|
@@ -61,15 +61,15 @@
|
|
|
61
61
|
"description": "Explicit restart key: a query whose resolved value keys the subscription (by value) instead of the resolved props. Requires \"withQuery\" or \"for\"; \"$item\" is bound per instance under \"for\".",
|
|
62
62
|
"allOf": [
|
|
63
63
|
{
|
|
64
|
-
"$ref": "
|
|
64
|
+
"$ref": "#/$defs/queryDocument"
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
68
|
"for": {
|
|
69
|
-
"description": "Fan-out: a query whose result is the item set
|
|
69
|
+
"description": "Fan-out: a query whose result is the item set — one running instance per item key, reconciled per state change. \"with\" is not allowed beside it.",
|
|
70
70
|
"allOf": [
|
|
71
71
|
{
|
|
72
|
-
"$ref": "
|
|
72
|
+
"$ref": "#/$defs/queryDocument"
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
}
|
|
@@ -82,5 +82,13 @@
|
|
|
82
82
|
},
|
|
83
83
|
"required": [
|
|
84
84
|
"view"
|
|
85
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"$defs": {
|
|
87
|
+
"stylesheetDocument": {
|
|
88
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-jslt/0.1"
|
|
89
|
+
},
|
|
90
|
+
"queryDocument": {
|
|
91
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-query/0.1"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
86
94
|
}
|
package/src/splitter.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* DOM call is guarded so it mounts inertly over a headless stub (there the
|
|
8
8
|
* live drag is browser-verified). The host binds it as a widget and
|
|
9
9
|
* parameterizes the grid/rail selectors, the CSS variable and the commit
|
|
10
|
-
* action
|
|
10
|
+
* action. Widget props `{ ratio, axis: 'x' | 'y' }` select the live direction;
|
|
11
|
+
* the axis defaults to x. The ratio's `-first`/`-rest` CSS variables carry
|
|
12
|
+
* fractional tracks for stacked grids after their fixed toolbar and gaps.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -44,22 +46,30 @@ export function createSplitterWidget(opts) {
|
|
|
44
46
|
const gridOf = () => (typeof host.closest === 'function' ? host.closest(gridSel) : null);
|
|
45
47
|
const applyRatio = (r) => {
|
|
46
48
|
gridOf()?.style?.setProperty?.(cssVar, String(r));
|
|
49
|
+
gridOf()?.style?.setProperty?.(cssVar + '-first', `${r}fr`);
|
|
50
|
+
gridOf()?.style?.setProperty?.(cssVar + '-rest', `${1 - r}fr`);
|
|
47
51
|
host.setAttribute?.('aria-valuenow', String(Math.round(r * 100)));
|
|
52
|
+
host.setAttribute?.('aria-orientation', handle.axis === 'y' ? 'horizontal' : 'vertical');
|
|
48
53
|
};
|
|
49
|
-
//
|
|
50
|
-
|
|
54
|
+
// A rail spans the content rows, so its top excludes the toolbar
|
|
55
|
+
// when the same content is stacked vertically.
|
|
56
|
+
const ratioAt = (clientX, clientY) => {
|
|
51
57
|
const g = gridOf();
|
|
52
58
|
if (g === null) return handle.ratio;
|
|
53
59
|
const rail = railSel !== null && typeof g.querySelector === 'function' ? g.querySelector(railSel) : null;
|
|
54
60
|
const box = g.getBoundingClientRect();
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
const railBox = rail?.getBoundingClientRect();
|
|
62
|
+
const vertical = handle.axis === 'y';
|
|
63
|
+
const start = vertical ? (railBox?.top ?? box.top) : (railBox?.right ?? box.left);
|
|
64
|
+
const end = vertical ? (railBox?.bottom ?? box.bottom) : box.right;
|
|
65
|
+
const point = vertical ? clientY : clientX;
|
|
66
|
+
if (!(end > start) || !Number.isFinite(point)) return handle.ratio;
|
|
67
|
+
return clamp((point - start) / (end - start));
|
|
58
68
|
};
|
|
59
69
|
const onMove = (e) => {
|
|
60
70
|
if (!handle.dragging) return;
|
|
61
71
|
e.preventDefault?.();
|
|
62
|
-
handle.ratio = ratioAt(e.clientX);
|
|
72
|
+
handle.ratio = ratioAt(e.clientX, e.clientY);
|
|
63
73
|
applyRatio(handle.ratio); // live only — the commit is on pointer-up
|
|
64
74
|
};
|
|
65
75
|
const onUp = () => {
|
|
@@ -67,20 +77,32 @@ export function createSplitterWidget(opts) {
|
|
|
67
77
|
handle.dragging = false;
|
|
68
78
|
doc?.removeEventListener?.('pointermove', onMove);
|
|
69
79
|
doc?.removeEventListener?.('pointerup', onUp);
|
|
80
|
+
doc?.removeEventListener?.('pointercancel', onCancel);
|
|
70
81
|
emit({ action, with: handle.ratio });
|
|
71
82
|
};
|
|
83
|
+
const onCancel = () => {
|
|
84
|
+
if (!handle.dragging) return;
|
|
85
|
+
handle.dragging = false;
|
|
86
|
+
handle.ratio = handle.startRatio;
|
|
87
|
+
applyRatio(handle.ratio);
|
|
88
|
+
doc?.removeEventListener?.('pointermove', onMove);
|
|
89
|
+
doc?.removeEventListener?.('pointerup', onUp);
|
|
90
|
+
doc?.removeEventListener?.('pointercancel', onCancel);
|
|
91
|
+
};
|
|
72
92
|
const onDown = (e) => {
|
|
73
93
|
if (e.button !== undefined && e.button !== 0) return;
|
|
74
94
|
handle.dragging = true;
|
|
95
|
+
handle.startRatio = handle.ratio;
|
|
75
96
|
e.preventDefault?.();
|
|
76
97
|
doc?.addEventListener?.('pointermove', onMove);
|
|
77
98
|
doc?.addEventListener?.('pointerup', onUp);
|
|
99
|
+
doc?.addEventListener?.('pointercancel', onCancel);
|
|
78
100
|
};
|
|
79
101
|
const onKey = (e) => {
|
|
80
102
|
const s = e.shiftKey ? fineStep : step;
|
|
81
103
|
let next = null;
|
|
82
|
-
if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') next = clamp(handle.ratio - s);
|
|
83
|
-
else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') next = clamp(handle.ratio + s);
|
|
104
|
+
if (e.key === 'ArrowLeft' || e.key === (handle.axis === 'y' ? 'ArrowUp' : 'ArrowDown')) next = clamp(handle.ratio - s);
|
|
105
|
+
else if (e.key === 'ArrowRight' || e.key === (handle.axis === 'y' ? 'ArrowDown' : 'ArrowUp')) next = clamp(handle.ratio + s);
|
|
84
106
|
else if (e.key === 'Home') next = min;
|
|
85
107
|
else if (e.key === 'End') next = max;
|
|
86
108
|
if (next === null) return;
|
|
@@ -90,7 +112,9 @@ export function createSplitterWidget(opts) {
|
|
|
90
112
|
emit({ action, with: next });
|
|
91
113
|
};
|
|
92
114
|
|
|
93
|
-
const handle = { host, doc, ratio: clamp(Number(props?.ratio ?? 0.5)),
|
|
115
|
+
const handle = { host, doc, ratio: clamp(Number(props?.ratio ?? 0.5)),
|
|
116
|
+
axis: props?.axis === 'y' ? 'y' : 'x', startRatio: 0.5,
|
|
117
|
+
dragging: false, applyRatio, onMove, onUp, onCancel };
|
|
94
118
|
applyRatio(handle.ratio);
|
|
95
119
|
host.addEventListener?.('pointerdown', onDown);
|
|
96
120
|
host.addEventListener?.('keydown', onKey);
|
|
@@ -98,6 +122,12 @@ export function createSplitterWidget(opts) {
|
|
|
98
122
|
return handle;
|
|
99
123
|
},
|
|
100
124
|
update(handle, props) {
|
|
125
|
+
const axis = props?.axis === 'y' ? 'y' : 'x';
|
|
126
|
+
if (axis !== handle.axis) {
|
|
127
|
+
handle.onCancel();
|
|
128
|
+
handle.axis = axis;
|
|
129
|
+
handle.applyRatio(handle.ratio);
|
|
130
|
+
}
|
|
101
131
|
const r = clamp(Number(props?.ratio ?? 0.5));
|
|
102
132
|
if (r !== handle.ratio && !handle.dragging) {
|
|
103
133
|
handle.ratio = r;
|
|
@@ -108,6 +138,7 @@ export function createSplitterWidget(opts) {
|
|
|
108
138
|
for (const [type, fn] of handle.hostListeners) handle.host.removeEventListener?.(type, fn);
|
|
109
139
|
handle.doc?.removeEventListener?.('pointermove', handle.onMove);
|
|
110
140
|
handle.doc?.removeEventListener?.('pointerup', handle.onUp);
|
|
141
|
+
handle.doc?.removeEventListener?.('pointercancel', handle.onCancel);
|
|
111
142
|
},
|
|
112
143
|
};
|
|
113
144
|
}
|