@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 +21 -0
- package/README.md +108 -2
- package/dist/index.d.ts +562 -0
- package/dist/index.js +502 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -4
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
|
-
#
|
|
1
|
+
# @weasel-js/ui-logic
|
|
2
2
|
|
|
3
|
-
|
|
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
|