@weasel-js/ui-logic 0.0.0-stage → 1.15.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 orochi235
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,109 @@
1
- # Temporary Holding Version
1
+ # @weasel-js/ui-logic
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Component behavior with no substrate: what a DOM component and a canvas-drawn widget both run,
4
+ each drawing it its own way. Today that is a list's roving highlight and typeahead, a tree's
5
+ expand and collapse keys, keyboard focus as one contract, how a text field's edit ends, and the
6
+ measure/draw contract a button and a row list implement on both targets. No React, no DOM.
7
+
8
+ Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
9
+ scene-graph canvas kit for React. See the
10
+ [API reference](https://orochi235.github.io/weasel/#pkg/ui-logic).
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install @weasel-js/ui-logic
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { EMPTY_TYPEAHEAD, rovingKey, typeaheadStep } from '@weasel-js/ui-logic';
22
+
23
+ type Row = { name: string; heading?: boolean };
24
+ const shape = { canStop: (r: Row) => !r.heading, label: (r: Row) => r.name };
25
+
26
+ let at: number | null = null;
27
+ let typed = EMPTY_TYPEAHEAD;
28
+
29
+ function onKey(rows: readonly Row[], key: string): void {
30
+ const r = rovingKey(rows, at, key, shape); // arrows, Home, End
31
+ if (r.handled) { at = r.at; return; }
32
+ if (key.length === 1) {
33
+ const t = typeaheadStep(rows, at, typed, key, Date.now(), shape);
34
+ typed = t.buffer;
35
+ if (t.hit !== null) at = t.hit;
36
+ }
37
+ }
38
+ ```
39
+
40
+ A list's shape says which items the highlight can land on (`canStop`), which arrows move it
41
+ (`orientation`, vertical by default, or `'both'` for all four), whether Left and Right swap for a
42
+ right-to-left layout (`dir`), and whether it wraps at the ends (`wrap`, on by default).
43
+ `@weasel-js/menu` runs every menu level on these.
44
+
45
+ ## Trees and rows
46
+
47
+ `treeKey` is a tree's Right and Left, the WAI-ARIA way: Right opens a closed branch or steps to
48
+ its first child, Left closes an open branch or steps to the parent. Its `TreeShape` says which
49
+ items are branches, which are open, and which is whose child.
50
+
51
+ `rowKey` is every key a list of rows answers, in one place: Cmd/Ctrl+A, the arrows with Home and
52
+ End (Shift extends), a tree's Right and Left, Enter and Space, and typeahead. It returns what the
53
+ key asks for (`selectAll`, `move`, `toggle`, `activate`, `type`) and the caller carries it out.
54
+ `@weasel-js/ui`'s `useRowNavigation` runs on it.
55
+
56
+ ## Focus
57
+
58
+ `FocusScope<T>` is keyboard focus over a set of targets, whatever draws them: which one is
59
+ focused, whether its ring shows, `focus`, `moveFocus` through a `tabOrder`, and
60
+ `subscribeFocus`. A `@weasel-js/hud` HUD is one over its widgets, and `@weasel-js/ui`'s
61
+ `domFocusScope(root)` is one over the elements under `root`, so code that moves focus is
62
+ written once for both. `tabSort` and `tabStep` are the order and the step both use.
63
+
64
+ ## Steps and sliders
65
+
66
+ `arrowSteps`, `arrowMove` and `rangeKey` are what an arrow key asks for: one step, or `COARSE_STEPS`
67
+ (ten) with Shift or a Page key, and Home and End for a range's ends. `sliderKey` turns one key into
68
+ a thumb's next value, by step or by whole stops, across an endless end and inside its neighbors;
69
+ `@weasel-js/ui`'s `Slider` runs on it.
70
+
71
+ ## Text fields
72
+
73
+ `editEnd(cause, shape)` says how a text field's edit ends: Enter and blur commit, Escape cancels.
74
+ A field's shape can make Escape commit too, and a `multiline` one leaves Shift+Enter to type a
75
+ line break. Caret movement stays with each field: a DOM field gets it from the browser.
76
+
77
+ ## Reorder
78
+
79
+ Moving items within a list, whether it is drawn in the DOM or on a canvas. A destination is a gap counted before
80
+ anything is taken out: 0 before the first item, `length` after the last. `moveItems(items, at, to)` moves the items at
81
+ indices `at` into gap `to`; `isNoopDrop` says whether that leaves them where they are; `nudgeGap` is the gap a
82
+ one-place move takes them to, and `nudgeOf` reads the Alt+arrow chord that asks for one. A pointer finds its gap by
83
+ `gapByMidpoint`, for items side by side, each split at its middle, or by `nearestGap`, for gaps drawn at places of
84
+ their own.
85
+
86
+ ## Measure and draw
87
+
88
+ `HeadlessComponent<S, L, T, O>` is the contract a control implements on every target: `measure(state,
89
+ constraints, env)` gives its box (width, height, first baseline) and where its parts sit, and `draw(state,
90
+ target)` renders it in the target's own output. `measure` is shared: it reads only the state and a
91
+ `MeasureEnv`, the target's tokens and text metrics, so a DOM component and a canvas widget in one theme
92
+ measure alike. `draw` is per target and does not take the measured box: a target that positions by
93
+ coordinates measures with `exactly(w, h)` itself, and the DOM lays out by CSS from the same tokens.
94
+
95
+ `measureButton` and `measureRowList` are the two so far, with `buttonParts` and `listMode` saying which
96
+ parts and roles a state shows; `pressStep` is a press's hover-and-hold state machine. `@weasel-js/ui`'s
97
+ `domButton` and `domItemList` and `@weasel-js/hud`'s `hudButton` and `hudList` implement them.
98
+
99
+ ## Hold-to-repeat
100
+
101
+ `createStepRepeat(step, spent, opts)` is a held stepper: `start(key)` steps once, then after
102
+ `REPEAT_DELAY_MS` once every `REPEAT_EVERY_MS` until `stop()`, or until `spent(key)` says the value
103
+ has nowhere left to go. Time comes from a `Scheduler` (`now`, and `set` returning a cancel), by
104
+ default `timerScheduler`; pass your own to drive it from another clock or a test. `@weasel-js/ui`'s
105
+ number field steppers and `@weasel-js/hud`'s menu number chips both hold through it.
106
+
107
+ ## License
108
+
109
+ MIT