@jarenjs/app 0.72.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 +5 -0
- package/dist/types/splitter.d.ts +3 -1
- package/package.json +4 -4
- package/src/splitter.js +41 -10
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
|
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.72.
|
|
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.72.
|
|
54
|
-
"@jarenjs/json": "^0.72.
|
|
55
|
-
"@jarenjs/view": "^0.72.
|
|
53
|
+
"@jarenjs/core": "^0.72.2",
|
|
54
|
+
"@jarenjs/json": "^0.72.2",
|
|
55
|
+
"@jarenjs/view": "^0.72.2"
|
|
56
56
|
}
|
|
57
57
|
}
|
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
|
}
|