@jsenv/navi 0.29.416 → 0.29.418
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/dist/dev/jsenv_navi.js +1595 -1103
- package/dist/dev/jsenv_navi.js.map +19 -17
- package/dist/jsenv_navi.js +1566 -1088
- package/dist/jsenv_navi.js.map +19 -17
- package/docs/AI_INSTRUCTIONS.md +13 -6
- package/docs/drag_interactions.md +297 -401
- package/docs/drag_to_travel.md +157 -754
- package/docs/interactions.md +170 -328
- package/docs/mobile_touch.md +35 -0
- package/docs/resource.md +13 -0
- package/docs/route_transitions.md +35 -7
- package/docs/scroll.md +23 -5
- package/docs/state_binding.md +39 -84
- package/package.json +2 -2
package/docs/mobile_touch.md
CHANGED
|
@@ -19,6 +19,7 @@ changes, measure again before trusting a number here.
|
|
|
19
19
|
- [Only the start is contested](#only-the-start-is-contested)
|
|
20
20
|
- [The click is the browser's call as much as ours](#the-click-is-the-browsers-call-as-much-as-ours)
|
|
21
21
|
- [A tap dropped after a touch drag](#a-tap-dropped-after-a-touch-drag)
|
|
22
|
+
- [A finger is captured to what it touched, inside shadow trees too](#a-finger-is-captured-to-what-it-touched-inside-shadow-trees-too)
|
|
22
23
|
- [A finger never makes a wheel event](#a-finger-never-makes-a-wheel-event)
|
|
23
24
|
- [Verifying without a device](#verifying-without-a-device)
|
|
24
25
|
|
|
@@ -160,6 +161,40 @@ Chromium's `gesture_provider.cc` (`ignore_single_tap_`, reset on the next down)
|
|
|
160
161
|
and `tap_suppression_controller.cc` (the tap that stops a fling, 180ms) for the
|
|
161
162
|
two mechanisms that do NOT explain it.
|
|
162
163
|
|
|
164
|
+
## A finger is captured to what it touched, inside shadow trees too
|
|
165
|
+
|
|
166
|
+
A touch pointer is captured the moment it lands, by the browser, to the element
|
|
167
|
+
it landed on — nobody calls `setPointerCapture`. A mouse and a pen are not.
|
|
168
|
+
That capture is announced like any other, with a `gotpointercapture` just
|
|
169
|
+
before the first pointer event that follows, and a finger resting still sends
|
|
170
|
+
one soon: its contact changes, so `pointermove`s with unchanged coordinates
|
|
171
|
+
come within the first 100ms or so. Nearly every touch press therefore sees a
|
|
172
|
+
`gotpointercapture` it never asked for. Code that reads it as "another gesture
|
|
173
|
+
took this press" gives up on almost every finger.
|
|
174
|
+
|
|
175
|
+
Recognising that capture is where Chrome misleads: **it goes to the deepest
|
|
176
|
+
element under the finger, shadow trees included.** On an `<input>` or a
|
|
177
|
+
`<textarea>` that is the inner editor, inside the field's own shadow tree; in a
|
|
178
|
+
web component, whatever its shadow root holds there. `hasPointerCapture` on
|
|
179
|
+
what the page sees — the field, the host — answers `false` at `pointerdown`,
|
|
180
|
+
after `gotpointercapture`, and for the whole press. Yet the `gotpointercapture`
|
|
181
|
+
reaches the page retargeted to that same element. An open shadow root shows
|
|
182
|
+
both halves: the host answers `false`, the element inside it `true`.
|
|
183
|
+
|
|
184
|
+
So the browser's capture is recognised by where it lands, never asked for: on
|
|
185
|
+
a touch press, a capture announced on the press's own target is the browser's,
|
|
186
|
+
whenever it arrives. Compare both events from the same tree: each is
|
|
187
|
+
retargeted to the shadow host from outside. A gesture that takes the press onto
|
|
188
|
+
that same element cannot be told apart from it, so it has to say so another way.
|
|
189
|
+
|
|
190
|
+
Measured in Chromium 153 through `Input.dispatchTouchEvent`. The capture does
|
|
191
|
+
not depend on the slop, so the desktop path shows it; see
|
|
192
|
+
[Verifying without a device](#verifying-without-a-device). The same `false` on
|
|
193
|
+
a field shows on an Android phone (Chrome 153). Not measured in Safari.
|
|
194
|
+
|
|
195
|
+
Reference: `implicitCaptureHolder` and `takePress` in `@jsenv/dom`'s
|
|
196
|
+
`press_held.js`.
|
|
197
|
+
|
|
163
198
|
## A finger never makes a wheel event
|
|
164
199
|
|
|
165
200
|
No mobile browser turns a touch into `wheel` events: those come from a mouse
|
package/docs/resource.md
CHANGED
|
@@ -300,6 +300,19 @@ store are written, and re-renders when its row is — `useArray` to the whole
|
|
|
300
300
|
store. An entry of `useAllByIds` given by a unique key rather than by id can
|
|
301
301
|
only be found by reading the whole store, and that read follows it too.
|
|
302
302
|
|
|
303
|
+
A row counts as written only when something in it changed:
|
|
304
|
+
|
|
305
|
+
- **An answer** — what a callback returns — is compared to the row held in
|
|
306
|
+
depth. Parsed JSON makes every object nested in a row a new one; a
|
|
307
|
+
revalidation returning the same rows keeps every one of them, and their
|
|
308
|
+
readers stay still.
|
|
309
|
+
- **A relation** changes its row when the row points at other children. What
|
|
310
|
+
happens to the children themselves is followed through the relation:
|
|
311
|
+
`game.place.label` read in a render re-renders it when the place is written,
|
|
312
|
+
and the game row stays the one it was.
|
|
313
|
+
- **A value written with `store.upsert()`** is compared by reference: a new
|
|
314
|
+
object is a change, whatever it holds.
|
|
315
|
+
|
|
303
316
|
## Relations: pick one of the four methods
|
|
304
317
|
|
|
305
318
|
A backend sub-route (`/games/:id/candidates`, `/games/:id/candidates/:userId/seen`)
|
|
@@ -552,9 +552,11 @@ Measured on that page, first frame of the movement: 63 → 45 ms at full speed,
|
|
|
552
552
|
What that means for a page and for the application around it:
|
|
553
553
|
|
|
554
554
|
- **It is still mounted for the length of the movement.** Its effects keep
|
|
555
|
-
running, and it still reads the address:
|
|
556
|
-
|
|
557
|
-
[What a transition costs](#what-a-transition-costs)).
|
|
555
|
+
running, and it still reads the address: a component reading the URL, a
|
|
556
|
+
route or a signal directly re-renders in it too, hidden (see
|
|
557
|
+
[What a transition costs](#what-a-transition-costs)). What it reads through
|
|
558
|
+
`useAsyncData` does not: navi leaves a page alone as soon as its container
|
|
559
|
+
would no longer show it.
|
|
558
560
|
- **It is taken down after the page arriving is up.** A cleanup that puts back
|
|
559
561
|
what it found — a document title, a class on `<body>`, a value in a shared
|
|
560
562
|
store — puts a stale value back over the new page's. Such a registration is
|
|
@@ -646,10 +648,36 @@ elements):
|
|
|
646
648
|
thirty cards: 90 re-renders gone, first frame 58 → 41 ms). A row read with
|
|
647
649
|
`RESOURCE.useById` follows that row only, so the page arriving loading rows
|
|
648
650
|
of its own into the same store leaves the cards of the page being left as
|
|
649
|
-
they were. What
|
|
650
|
-
— its params, its data and its state all
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
they were. What moves last is the page's own reading of its route's action
|
|
652
|
+
— its params, its data and its state all change as the route stops
|
|
653
|
+
matching. `useAsyncData` gives no render to a page its container no longer
|
|
654
|
+
shows: the page is about to be taken down, or hidden until the movement is
|
|
655
|
+
over (on the list of thirty cards: about 450 component renders gone from the
|
|
656
|
+
callback, first frame 10–20 ms sooner at 4× CPU, nothing measurable at full
|
|
657
|
+
speed). A component reading the action's signals itself
|
|
658
|
+
(`action.dataSignal.value`) does not get that, and re-renders in the hidden
|
|
659
|
+
page.
|
|
660
|
+
- **Restyling the page arriving more than once.** Built inside the update
|
|
661
|
+
callback, it is styled as it lands; anything written afterwards that its
|
|
662
|
+
elements inherit, or that re-selects them, restyles all of them again. On the
|
|
663
|
+
list of thirty cards (~7,000 elements) three did, 16–19 ms each at full
|
|
664
|
+
speed: the height of a list group's sticky label, written on the group once
|
|
665
|
+
measured (navi keeps it from being inherited, which took the movement 17 ms
|
|
666
|
+
sooner); the room a fixed bar takes (`--navi-fixed-bar-space-*`, feeding the
|
|
667
|
+
inherited `--navi-safe-area-inset-*`), which changes on the root when a bar
|
|
668
|
+
arrives or leaves with the page; and an app's `:has()` rule on the list
|
|
669
|
+
container over its rows (`.navi_list_container:has(.card) …`), evaluated
|
|
670
|
+
again as the rows land. An attribute the app sets on the container says the
|
|
671
|
+
same thing as that `:has()`, for nothing.
|
|
672
|
+
- **Building rows the first picture does not show.** A list coming back
|
|
673
|
+
where it was draws, in the update callback, the rows that were on screen
|
|
674
|
+
then — the `visibleCount` of the position it is handed back (see
|
|
675
|
+
[scroll.md](./scroll.md#where-the-list-opens-and-where-it-is)) — and the
|
|
676
|
+
rest of its window once the movement has started. The movement does not wait
|
|
677
|
+
for them: it plays on the compositor, which keeps presenting frames while the
|
|
678
|
+
main thread builds the rows. On the list of thirty cards, handed its whole
|
|
679
|
+
position back: the movement starts 70–85 ms after the click instead of
|
|
680
|
+
185–270.
|
|
653
681
|
- **Re-rendering what did not change, on the press itself.** A component that
|
|
654
682
|
only wants a yes or a no reads a computed flag, not the signal it is derived
|
|
655
683
|
from: a list whose rows each read the count of runs in flight re-rendered
|
package/docs/scroll.md
CHANGED
|
@@ -157,13 +157,25 @@ picked up then. When it is still not the box you mean, say so explicitly with
|
|
|
157
157
|
- **`scrolled`** is the controlled form of the same thing — same pair as
|
|
158
158
|
`open`/`defaultOpen` elsewhere in navi. The list goes back there every time it
|
|
159
159
|
changes, even after the user scrolled.
|
|
160
|
-
- **`onScrolledChange`** gives `{id, index, offset}` as the user
|
|
160
|
+
- **`onScrolledChange`** gives `{id, index, offset, visibleCount}` as the user
|
|
161
|
+
scrolls. `visibleCount` is how many rows were on screen from that one on:
|
|
162
|
+
handed back whole (`defaultScrolled={position}`), the position also says what
|
|
163
|
+
to draw first — the list's first commit draws those rows (and the one above,
|
|
164
|
+
when it stood partly in view) and the rest of its window once painted. An app
|
|
165
|
+
keeping only the `id` (in its url, say) keeps the place and drops that: the
|
|
166
|
+
list then draws its whole window before its first picture.
|
|
161
167
|
- A list with an `id` **comes back where it was** when its screen is left and
|
|
162
168
|
come back to, the way the page does: the position is kept under the list's
|
|
163
169
|
`id` and the page's url, for the session, and put back by name the way
|
|
164
|
-
`defaultScrolled={{id, offset}}` is
|
|
165
|
-
|
|
170
|
+
`defaultScrolled={{id, offset}}` is — `visibleCount` included, and kept even
|
|
171
|
+
when the list was never scrolled. A fresh arrival at the page still opens at
|
|
172
|
+
`defaultScrolled`. **`scrollResetOnNavigation`** opts out: the list then
|
|
166
173
|
opens the same way every time.
|
|
174
|
+
- A list scrolling the document and opening on a row **places the document
|
|
175
|
+
itself**, on the row: the offset the page kept for its url is not put back
|
|
176
|
+
over it. Those are pixels of the page as it was drawn, and the rows the
|
|
177
|
+
window holds off screen are fillers of an estimated height — the same pixels
|
|
178
|
+
now fall on other rows.
|
|
167
179
|
|
|
168
180
|
### A search moves the list, and gives it back
|
|
169
181
|
|
|
@@ -390,10 +402,16 @@ scrolls: a `maxHeight` on a list that IS the scrolling area,
|
|
|
390
402
|
### The first paint of a list that opens in a click
|
|
391
403
|
|
|
392
404
|
A popup's content is built in the click that opens it (see `popup_open.md`),
|
|
393
|
-
and rows below the fold cost the same there as rows on screen.
|
|
405
|
+
and rows below the fold cost the same there as rows on screen. A page coming
|
|
406
|
+
back in a route transition is the same case: it is built in the transition's
|
|
407
|
+
update callback, and every row drawn there delays the movement. `renderBudget`
|
|
394
408
|
takes `{ initial, after }` for exactly this: `initial` rows in the commit the
|
|
395
409
|
browser paints first — what a phone screen shows, plus a few — and `after` from
|
|
396
|
-
the paint on.
|
|
410
|
+
the paint on. They are counted from the row the list opens on: the first
|
|
411
|
+
picture of a list opening on a row shows that row and what is below it, and
|
|
412
|
+
the window is centered on it only from the paint on. A position handed back
|
|
413
|
+
with its `visibleCount` sizes that first window itself, whatever `initial`
|
|
414
|
+
guessed. The switch waits for the paint itself, not for an effect: preact
|
|
397
415
|
runs a component's pending effects early when that component renders again,
|
|
398
416
|
and something always re-renders before a popup has painted. Do not rebuild
|
|
399
417
|
this by hand with a `useEffect` that widens a slice: that effect is one preact
|
package/docs/state_binding.md
CHANGED
|
@@ -32,8 +32,6 @@ was missed.
|
|
|
32
32
|
|
|
33
33
|
## The callback that only remembers
|
|
34
34
|
|
|
35
|
-
The same mistake, in four places:
|
|
36
|
-
|
|
37
35
|
```jsx
|
|
38
36
|
// ✗ the app is told where the slides went, and writes it back down
|
|
39
37
|
<SlideContainer
|
|
@@ -45,18 +43,6 @@ The same mistake, in four places:
|
|
|
45
43
|
// ✓
|
|
46
44
|
<SlideContainer signal={stepSignal} />
|
|
47
45
|
|
|
48
|
-
// ✗ the control is told what it now holds, and the app writes it back down
|
|
49
|
-
<Picker value={side} uiAction={(value) => (sideSignal.value = value)} />
|
|
50
|
-
// ✓
|
|
51
|
-
<Picker signal={sideSignal} />
|
|
52
|
-
|
|
53
|
-
// ✗ a button proposing a value to a control, straight into the signal
|
|
54
|
-
<Button onClick={() => (durationSignal.value = ninetyMinutes)}>1h30</Button>
|
|
55
|
-
// ✓
|
|
56
|
-
<Button command="--navi-update" commandFor="duration" value={ninetyMinutes}>
|
|
57
|
-
1h30
|
|
58
|
-
</Button>
|
|
59
|
-
|
|
60
46
|
// ✗ the popup is told it opened, and the app writes it back down
|
|
61
47
|
<Dialog
|
|
62
48
|
open={isOpen}
|
|
@@ -69,7 +55,8 @@ The same mistake, in four places:
|
|
|
69
55
|
|
|
70
56
|
In each pair the second line is not a tidier spelling of the first: it is the
|
|
71
57
|
binding the component already implements, and the first is a partial
|
|
72
|
-
re-implementation of it.
|
|
58
|
+
re-implementation of it. The same pair for a control's value, and for a button
|
|
59
|
+
proposing one, is in [control_value.md](./control_value.md).
|
|
73
60
|
|
|
74
61
|
## Why it is not merely shorter
|
|
75
62
|
|
|
@@ -77,22 +64,20 @@ A binding is written by **everything that moves the state**, not only by the
|
|
|
77
64
|
gesture the caller thought of. A `SlideContainer`'s signal is written by a drag,
|
|
78
65
|
by an arrow key, by a `--navi-right` command sent from a chevron outside the
|
|
79
66
|
box, by the address when the signal is a route's own, by a step refusing to be
|
|
80
|
-
left. A control's
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
added.
|
|
67
|
+
left. A control's signal is written by the user typing, by a group placing its
|
|
68
|
+
children, by a picker filling its popup or putting back what it held on Escape,
|
|
69
|
+
by a `--navi-update` from a shortcut button. A hand-written callback catches the
|
|
70
|
+
cases it was written for and silently misses the rest — and the drift only shows
|
|
71
|
+
on the screen where a second gesture was added.
|
|
86
72
|
|
|
87
73
|
It reads in the other direction too, which no callback gives at all: writing the
|
|
88
74
|
signal MOVES the component. The wheels roll, the slides travel, the popup opens.
|
|
89
75
|
That is what replaces the `key` or the `value` prop pushed down to force a
|
|
90
76
|
component into a state.
|
|
91
77
|
|
|
92
|
-
And
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
allowed to change.
|
|
78
|
+
And a `command` goes through navi's interaction gate, which an `onClick` writing
|
|
79
|
+
the same state does not (see
|
|
80
|
+
[control_value.md](./control_value.md#a-button-that-proposes-a-value-is---navi-update)).
|
|
96
81
|
|
|
97
82
|
## What is left for the callback
|
|
98
83
|
|
|
@@ -113,13 +98,10 @@ Binding the state does not remove the callback — it removes one job from it:
|
|
|
113
98
|
## The callback also fires for the state's own changes
|
|
114
99
|
|
|
115
100
|
A bound container walks to whatever its signal holds, and tells the callback
|
|
116
|
-
about that walk — `cause: "state"`, `event: null`.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
flows back INTO the signal — a server whose response is upserted into the store,
|
|
121
|
-
a `useSignalSync` on the row that re-renders from it — makes every answer a
|
|
122
|
-
change, and every change a write:
|
|
101
|
+
about that walk — `cause: "state"`, `event: null`. The trap is a callback that
|
|
102
|
+
writes somewhere whose answer flows back INTO the signal — a server whose
|
|
103
|
+
response is upserted into the store, a `useSignalSync` on the row that
|
|
104
|
+
re-renders from it:
|
|
123
105
|
|
|
124
106
|
```jsx
|
|
125
107
|
// ✗ the answer of the write feeds the signal the write came from
|
|
@@ -130,49 +112,29 @@ const styleSignal = useSignalSync(user.share_image_style);
|
|
|
130
112
|
/>;
|
|
131
113
|
```
|
|
132
114
|
|
|
133
|
-
|
|
134
|
-
already holds
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
t=0 notch → B signal=B PUT B ──────────────┐
|
|
139
|
-
t=200 notch → A signal=A PUT A ────────────┐ │
|
|
140
|
-
t=300 B answers, store=B, signal=B, the box │ │
|
|
141
|
-
walks to B, onCurrentChange(B, "state"), │ │
|
|
142
|
-
save(B) → PUT B ───────────────────────┐ │ │
|
|
143
|
-
t=500 A answers … save(A) → PUT A │ │ │
|
|
144
|
-
t=600 B answers … save(B) → PUT B │ │ │
|
|
145
|
-
…
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Nothing was reordered — it is enough that an answer lands after a newer write
|
|
149
|
-
was sent. Every answer is then the answer to a request the user has already
|
|
150
|
-
moved past, the container obeys the model, the callback saves what it was just
|
|
151
|
-
told, and that write's answer starts the next turn.
|
|
115
|
+
One write at a time shows nothing: the answer carries the value the signal
|
|
116
|
+
already holds. Two in flight — two notches in quick succession — and every
|
|
117
|
+
answer is to a request the user has already moved past: the container walks to
|
|
118
|
+
it, the callback saves it, and that write's answer starts the next turn. The
|
|
119
|
+
loop never closes.
|
|
152
120
|
|
|
153
121
|
Decide what the signal IS:
|
|
154
122
|
|
|
155
123
|
- **The signal is the choice, the server only confirms it.** Seed it once with
|
|
156
|
-
`useSignal(model)` and never sync it
|
|
157
|
-
picture
|
|
158
|
-
|
|
159
|
-
|
|
124
|
+
`useSignal(model)` and never sync it: a late answer changes the store, not the
|
|
125
|
+
picture, and a write that fails is not undone on screen unless the callback
|
|
126
|
+
undoes it. Right for a setting saved on the gesture, with no "Save" button.
|
|
127
|
+
That is a `SlideContainer`'s shape; a control says it with
|
|
128
|
+
`defaultValue={model}` and an `action` (see
|
|
129
|
+
[control_value.md](./control_value.md#a-defaultvalue-follows-what-it-was-read-from)).
|
|
160
130
|
- **The model is the truth, the control only proposes.** Keep `useSignalSync`
|
|
161
|
-
and ignore `cause: "state"`
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
131
|
+
and ignore `cause: "state"` — a change the state asked for is not a choice to
|
|
132
|
+
save. A late answer still pulls the picture back for one round trip. Right
|
|
133
|
+
when another client or a background refresh may change the row while it is on
|
|
134
|
+
screen.
|
|
165
135
|
- **Never two writes in flight for one field.** Debounce the write, or cancel
|
|
166
|
-
the one before it (`createRequestCanceller`), so no answer
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Measured on the same screen, the same two notches, a 300ms round trip:
|
|
170
|
-
|
|
171
|
-
| what the signal is | writes sent | slide changes |
|
|
172
|
-
| ----------------------------------------- | --------------- | ------------- |
|
|
173
|
-
| `useSignalSync` + save every change | 39, still going | 39 |
|
|
174
|
-
| `useSignal`, seeded once | 2 | 2 |
|
|
175
|
-
| `useSignalSync` + ignore `cause: "state"` | 2 | 4 |
|
|
136
|
+
the one before it (`createRequestCanceller`), so no answer is older than the
|
|
137
|
+
latest intent. Right when the write is the expensive part.
|
|
176
138
|
|
|
177
139
|
## What binds itself
|
|
178
140
|
|
|
@@ -189,13 +151,10 @@ Measured on the same screen, the same two notches, a 300ms round trip:
|
|
|
189
151
|
|
|
190
152
|
"Who writes the score" has three answers — a player of the game, the organizer
|
|
191
153
|
alone, nobody — and only two of them are rows: "nobody" is a switch under the
|
|
192
|
-
list.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
The finding is elsewhere: **the list is not asking that question**. It asks
|
|
197
|
-
"which of these two", the switch asks "anybody at all", and the answer the
|
|
198
|
-
screen sends is made of both:
|
|
154
|
+
list. Bound to the state holding all three, the list shows nothing whenever the
|
|
155
|
+
switch is on. The fix is not `value` + `uiAction`: **the list is not asking that
|
|
156
|
+
question**. It asks "which of these two", the switch asks "anybody at all", and
|
|
157
|
+
the answer the screen sends is made of both:
|
|
199
158
|
|
|
200
159
|
```jsx
|
|
201
160
|
const scorerSignal = signal("player"); // what the list asks: one of ITS rows
|
|
@@ -210,13 +169,10 @@ const whoWritesSignal = computed(() =>
|
|
|
210
169
|
```
|
|
211
170
|
|
|
212
171
|
Each control binds the state it is about, and the value that spans them is
|
|
213
|
-
computed from those — never held a second time
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The rule underneath: a control shows values, so the state it binds is the one
|
|
218
|
-
whose values it can show. A state holding more than that is more than one
|
|
219
|
-
question.
|
|
172
|
+
computed from those — never held a second time (the row chosen before the switch
|
|
173
|
+
went on is still chosen when it goes off: nothing cleared it). A control binds
|
|
174
|
+
the state whose values it can show; a state holding more than that is more than
|
|
175
|
+
one question.
|
|
220
176
|
|
|
221
177
|
## When there is no binding
|
|
222
178
|
|
|
@@ -235,7 +191,6 @@ the next screen copies, and by then it is the app's pattern.
|
|
|
235
191
|
|
|
236
192
|
- [control_value.md](./control_value.md) — who holds a control's value, and what
|
|
237
193
|
a bound signal holds control by control
|
|
238
|
-
- [actions.md](./actions.md#action-or-uiaction) — `action` or `uiAction`
|
|
239
194
|
- [popup_open.md](./popup_open.md) — who owns the fact that a popup is open
|
|
240
195
|
- [interactions.md](./interactions.md) — a gesture is named, not read by hand:
|
|
241
196
|
the same rule, one layer down
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.418",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Library of components including navigation to create frontend applications",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"preact": "^11.0.0-rc.2"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@jsenv/dom": "0.17.
|
|
37
|
+
"@jsenv/dom": "0.17.84",
|
|
38
38
|
"@jsenv/humanize": "1.8.6",
|
|
39
39
|
"@jsenv/validity": "0.4.5"
|
|
40
40
|
},
|