@weasel-js/ui-input 0.0.0-stage → 1.18.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/LICENSE +21 -0
- package/README.md +143 -2
- package/dist/index.d.ts +619 -0
- package/dist/index.js +756 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -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,144 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @weasel-js/ui-input
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
What a key or a pointer does to a control, as pure functions over numbers, keys and boxes: arrow steps, sliders
|
|
4
|
+
and windowed tracks, a list's roving highlight and typeahead, a tree's keys, selection, grid tiling, reorder and
|
|
5
|
+
drop gaps, edge scroll, focus order and directional focus, how a text field's edit ends. A DOM component and a
|
|
6
|
+
canvas-drawn widget run the same rule and each draws the result its own way. 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-input).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @weasel-js/ui-input
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { EMPTY_TYPEAHEAD, rovingKey, typeaheadStep } from '@weasel-js/ui-input';
|
|
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
|
+
`tabSort(targets, declared)` is Tab order, targets that declare a place first, and `tabStep` is one Tab step
|
|
59
|
+
through it. `@weasel-js/ui-logic`'s `FocusScope` moves focus by them.
|
|
60
|
+
|
|
61
|
+
Targets placed freely, with no row or grid to walk, take the arrows by where they sit. `nearestToward(from, targets,
|
|
62
|
+
heading)` is the index of the target focus moves to: the cheapest by `headingCost`, which adds the travel along the
|
|
63
|
+
heading to twice the drift across it, so a target straight ahead beats a nearer one off to the side. `headingOf(key)`
|
|
64
|
+
reads the heading off an arrow key.
|
|
65
|
+
|
|
66
|
+
## Selection and grids
|
|
67
|
+
|
|
68
|
+
`selection(spec, values)` is which of a run of items are on: one value or `null` (`mode: 'single'`, which may
|
|
69
|
+
`allowDeselect` and may not follow focus), or a list of them (`'multiple'`), either with `mixedValues`, where the
|
|
70
|
+
sources disagree. `selectionOfEach(items)` is the same over items that each own a boolean. The model says each
|
|
71
|
+
item's state (`'on'`, `'off'`, `'mixed'`), what a press does, and runs an `edit`, a run of changes from its own
|
|
72
|
+
copy that it can revert; `sweepFrom` is a sweep's, setting each item it reaches to the opposite of whether the
|
|
73
|
+
pressed one was on. `selectionKeys` says what the keyboard does with it.
|
|
74
|
+
|
|
75
|
+
`tileGrid(count, { columns, lastRow })` lays a run of same-size items out as a grid, a short last row at its start,
|
|
76
|
+
centered, at its end, or stretched across (`'fill'`): its tracks, each item's span, and which of the grid's outer
|
|
77
|
+
corners each item sits on. `rovingGridTarget` walks such a grid with the arrows, Up and Down landing on the item
|
|
78
|
+
drawn under this one, by `gridLayout`'s per-row offsets and widths; `tileKey` does the same for `count` items
|
|
79
|
+
tiled by a spec. `@weasel-js/ui`'s `ToggleBar`, `OptionsBar`,
|
|
80
|
+
`ButtonBar`, `SwatchGrid` and `ToolGroup` run on these.
|
|
81
|
+
|
|
82
|
+
## Steps and sliders
|
|
83
|
+
|
|
84
|
+
`arrowSteps`, `arrowMove` and `rangeKey` are what an arrow key asks for: one step, or `COARSE_STEPS`
|
|
85
|
+
(ten) with Shift or a Page key, and Home and End for a range's ends. `sliderKey` turns one key into
|
|
86
|
+
a thumb's next value, by step or by whole stops, across an endless end and inside its neighbors;
|
|
87
|
+
`@weasel-js/ui`'s `Slider` runs on it.
|
|
88
|
+
|
|
89
|
+
`sliderAt(f, shape)` is the pointer's half: the value picked at fraction `f` of the track, on the
|
|
90
|
+
step grid, inside `min` to `max`, and pulled onto a stop within `pull` unless `free`. The shape
|
|
91
|
+
carries a `TrackMap`, the two-way map between a value and its place along the track, so the rule
|
|
92
|
+
does not care what the track is. `spanMap(span)` lays a `Span` along it linearly, `evenMap(stops)`
|
|
93
|
+
gives every gap between stops the same length, and a caller with a scale of its own writes the two
|
|
94
|
+
functions itself. `attract` is the pull alone: the nearest stop within a tolerance, measured along
|
|
95
|
+
the track with a map or in the value's own units without one.
|
|
96
|
+
|
|
97
|
+
A track need not show its whole range. A `Span` is a stretch of values, and a track that shows
|
|
98
|
+
one span of a longer range is a windowed track: `spanPan` and `spanZoom` move the window inside
|
|
99
|
+
its bounds, `spanZoom` about a value that keeps its place, and `spanPlace` and `spanShare` put a
|
|
100
|
+
value and a length at their fractions of it. Values outside the window map to places off the
|
|
101
|
+
track's ends, so a thumb can sit out of view and a drag can carry one there.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const window = { from: 2000, to: 3000 };
|
|
105
|
+
const track = { min: 0, max: Infinity, ...spanMap(window) };
|
|
106
|
+
sliderAt(0.5, track); // 2500
|
|
107
|
+
sliderAt(0.52, { ...track, stops: [2510], pull: 6 / 400 }); // 2510, within 6px on a 400px track
|
|
108
|
+
spanZoom(window, 2500, 0.5, { from: 0, to: 10_000 }); // { from: 2250, to: 2750 }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`@weasel-js/ui`'s `Timeline` runs its lanes and ruler on these: a lane is a track windowed onto the
|
|
112
|
+
timeline, its keys the thumbs and every other key's time a stop.
|
|
113
|
+
|
|
114
|
+
## Text fields
|
|
115
|
+
|
|
116
|
+
`editEnd(cause, shape)` says how a text field's edit ends: Enter and blur commit, Escape cancels.
|
|
117
|
+
A field's shape can make Escape commit too, and a `multiline` one leaves Shift+Enter to type a
|
|
118
|
+
line break. Caret movement stays with each field: a DOM field gets it from the browser.
|
|
119
|
+
|
|
120
|
+
## Reorder
|
|
121
|
+
|
|
122
|
+
Moving items within a list, whether it is drawn in the DOM or on a canvas. A destination is a gap counted before
|
|
123
|
+
anything is taken out: 0 before the first item, `length` after the last. `moveItems(items, at, to)` moves the items at
|
|
124
|
+
indices `at` into gap `to`; `isNoopDrop` says whether that leaves them where they are; `nudgeGap` is the gap a
|
|
125
|
+
one-place move takes them to, and `nudgeOf` reads the Alt+arrow chord that asks for one. A pointer finds its gap by
|
|
126
|
+
`gapByMidpoint`, for items side by side, each split at its middle, or by `nearestGap`, for gaps drawn at places of
|
|
127
|
+
their own. `dropBand(f, band)` says which part of one item the pointer is over: before it, after it, or, with a
|
|
128
|
+
`band` under a half, into it.
|
|
129
|
+
|
|
130
|
+
A drag near the edge of a scrolling box scrolls it. `edgePush(span, p, margin)` is how hard, from -1 to 1: nothing
|
|
131
|
+
until the pointer is within `margin` of an end, full strength at the end and past it. `edgeScroll(bounds, point)`
|
|
132
|
+
reads both axes of a box; multiply by a top speed.
|
|
133
|
+
|
|
134
|
+
## Hold-to-repeat
|
|
135
|
+
|
|
136
|
+
`createStepRepeat(step, spent, opts)` is a held stepper: `start(key)` steps once, then after
|
|
137
|
+
`REPEAT_DELAY_MS` once every `REPEAT_EVERY_MS` until `stop()`, or until `spent(key)` says the value
|
|
138
|
+
has nowhere left to go. Time comes from a `Scheduler` (`now`, and `set` returning a cancel), by
|
|
139
|
+
default `timerScheduler`; pass your own to drive it from another clock or a test. `@weasel-js/ui`'s
|
|
140
|
+
number field steppers and `@weasel-js/hud`'s menu number chips both hold through it.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|