@jsenv/navi 0.29.151 → 0.29.153
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/jsenv_navi.js +107 -10
- package/dist/jsenv_navi.js.map +7 -5
- package/docs/AI_INSTRUCTIONS.md +11 -0
- package/docs/error_handling.md +52 -1
- package/docs/popup_open.md +39 -8
- package/package.json +2 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -138,6 +138,17 @@ consistency across the app, not from any single call site.
|
|
|
138
138
|
`smallTouchScreenSignal` in an app to change a dialog's size, before passing
|
|
139
139
|
`expandX={false}` to stop a dialog sprawling, and before writing CSS to make a
|
|
140
140
|
dialog fit the screen.
|
|
141
|
+
- `docs/popup_open.md` — what opens a `Dialog`/`Popover` and who owns the fact
|
|
142
|
+
that it is open: why a popup with no `open` prop is the default (it refuses to
|
|
143
|
+
close while a control inside it is mid-action), the attributes that open one
|
|
144
|
+
(`command` + `commandfor`, `value` saying what it is opened ON, reaching
|
|
145
|
+
`onOpen` before the content is built), and `triggerNaviCommand` as the LAST
|
|
146
|
+
RESORT for a decision that is not a press — with the rule that decides most
|
|
147
|
+
reviews: the `event` it takes is the gesture you were handed, threaded down to
|
|
148
|
+
the call, never a `new CustomEvent()` built on the spot (that is for a timer,
|
|
149
|
+
an action settling, a signal changing, and nothing else). Read it before
|
|
150
|
+
calling `triggerNaviCommand`, before passing `open` to a popup, and before an
|
|
151
|
+
`onOpen`/`onClose` writing a signal.
|
|
141
152
|
- `docs/autofocus.md` — who gets the keyboard when a popup opens or a slide
|
|
142
153
|
arrives, and whether a ring shows on it: the ladder navi walks, what
|
|
143
154
|
`autoFocus` means on a surface (`true` = the surface takes it, for a popup
|
package/docs/error_handling.md
CHANGED
|
@@ -30,7 +30,15 @@ validation message on the **requester** (the submit button, not the form):
|
|
|
30
30
|
|
|
31
31
|
- `errorMapping(error)` turns the raw error into what to show: a string, an
|
|
32
32
|
`Error`, an element, or `{ message, target }` to point the message at another
|
|
33
|
-
element.
|
|
33
|
+
element. Two returns matter as much as those:
|
|
34
|
+
- **`return error` — the error untouched — is how a mapping says "not mine"**.
|
|
35
|
+
It is then shown exactly as if there were no mapping at all, so a mapping
|
|
36
|
+
only has to know about the cases it dresses.
|
|
37
|
+
- **returning nothing displays nothing**, on purpose: that is how one
|
|
38
|
+
particular error is swallowed, per error, where `actionErrorEffect="none"`
|
|
39
|
+
says it once for the whole control. Beware of it as a default — a mapping
|
|
40
|
+
whose last branch falls off the end swallows everything it did not
|
|
41
|
+
recognize, and the person sees the button return to idle with no message.
|
|
34
42
|
- The message is a constraint on the control (`navi_action_error`) rather than a
|
|
35
43
|
render of its own, and it **auto-resets on the next action**: hitting submit
|
|
36
44
|
again clears it and re-submits, instead of the form being stuck invalid.
|
|
@@ -47,6 +55,49 @@ validation message on the **requester** (the submit button, not the form):
|
|
|
47
55
|
`action` — `onActionStart`, `onActionEnd`, `onActionAborted` — are in
|
|
48
56
|
[actions.md](./actions.md#the-instance-a-control-runs).
|
|
49
57
|
|
|
58
|
+
#### A refusal that carries a link
|
|
59
|
+
|
|
60
|
+
The callout is not limited to a sentence. When the refusal has somewhere to go —
|
|
61
|
+
"this slot is already taken by that game" — the mapping returns an element and
|
|
62
|
+
the callout holds it, link included:
|
|
63
|
+
|
|
64
|
+
```jsx
|
|
65
|
+
<Form
|
|
66
|
+
action={GAME.POST.bindParams(values)}
|
|
67
|
+
errorMapping={(error) => {
|
|
68
|
+
const conflict = error.conflict;
|
|
69
|
+
if (!conflict) return error; // not mine: shown as-is
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
Ce créneau est déjà pris par{" "}
|
|
73
|
+
<Link route={GAME_ROUTE} routeParams={{ id: conflict.game_id }}>
|
|
74
|
+
cette partie
|
|
75
|
+
</Link>
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
That is the whole thing: the message arrives on the button that was pressed, it
|
|
83
|
+
survives whatever ran before it (a `command` waits for its own action), and it
|
|
84
|
+
clears itself on the next submit. A refusal with a destination does **not** need
|
|
85
|
+
a render of its own in the page.
|
|
86
|
+
|
|
87
|
+
Two consequences of the message being an element:
|
|
88
|
+
|
|
89
|
+
- it is rendered as a **preact tree of its own** into the callout, not as part
|
|
90
|
+
of the app tree, so it receives no context from the app. Anything reading
|
|
91
|
+
global signals — a navi `<Link route>`, a store — works; anything expecting an
|
|
92
|
+
app `Context.Provider` above it does not, and has to be given what it needs
|
|
93
|
+
through props;
|
|
94
|
+
- it has no one-line form, so nothing derives a `title` from it — the callout is
|
|
95
|
+
the only place it is shown. A string or an `Error` message still writes the
|
|
96
|
+
control's `title` when the control has none of its own.
|
|
97
|
+
|
|
98
|
+
[form_error_mapping_jsx demo](../src/control/demos/form_error_mapping_jsx/demo.html)
|
|
99
|
+
shows a mapping answering with a different component per error kind.
|
|
100
|
+
|
|
50
101
|
### A screen's data failed — a route action
|
|
51
102
|
|
|
52
103
|
Nobody clicked; the page simply cannot be drawn. It is the page, or a piece of
|
package/docs/popup_open.md
CHANGED
|
@@ -5,6 +5,7 @@ What opens a `Dialog` or a `Popover`, and who owns the fact that it is open.
|
|
|
5
5
|
- [The popup owns its open state](#the-popup-owns-its-open-state)
|
|
6
6
|
- [A button opens it: the attributes](#a-button-opens-it-the-attributes)
|
|
7
7
|
- [Something else opens it: `triggerNaviCommand`](#something-else-opens-it-triggernavicommand)
|
|
8
|
+
- [The event is forwarded, not invented](#the-event-is-forwarded-not-invented)
|
|
8
9
|
- [Which element receives the command](#which-element-receives-the-command)
|
|
9
10
|
- [The anchor](#the-anchor)
|
|
10
11
|
- [Opening it ON something](#opening-it-on-something)
|
|
@@ -75,10 +76,15 @@ press:
|
|
|
75
76
|
|
|
76
77
|
## Something else opens it: `triggerNaviCommand`
|
|
77
78
|
|
|
78
|
-
The attributes fire on every click of the element that carries them
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
The attributes fire on every click of the element that carries them, and they
|
|
80
|
+
cover more than "this button opens that dialog": `commandfor` says to whom,
|
|
81
|
+
`value` says what it is about, `--navi-x:argument` says how. Before writing any
|
|
82
|
+
JS here, check none of those is the answer — `triggerNaviCommand` is the last
|
|
83
|
+
resort, not the general way to run a command.
|
|
84
|
+
|
|
85
|
+
What it is for is a decision rather than a click — a long press, the end of a
|
|
86
|
+
drag, a double-click, a keyboard shortcut, a server answer, an
|
|
87
|
+
`IntersectionObserver`:
|
|
82
88
|
|
|
83
89
|
```jsx
|
|
84
90
|
import { triggerNaviCommand } from "@jsenv/navi";
|
|
@@ -100,16 +106,41 @@ This is the same entry point the attributes go through: same target resolution,
|
|
|
100
106
|
same command proxies, same events. The popup stays uncontrolled, and keeps its
|
|
101
107
|
say over closing.
|
|
102
108
|
|
|
109
|
+
### The event is forwarded, not invented
|
|
110
|
+
|
|
103
111
|
`event` is what caused the decision, and it is mandatory — triggering a command
|
|
104
112
|
without one throws. It is chained into the request event, and that chain is what
|
|
105
113
|
lets the popup handle focus correctly (which element to give focus back to,
|
|
106
114
|
whether a mousedown's click must be swallowed) and what the debug panel groups
|
|
107
115
|
the whole sequence under.
|
|
108
116
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
So the event to pass is **the one that is already there**, threaded down through
|
|
118
|
+
every function between the handler and the call. A `CustomEvent` built on the
|
|
119
|
+
spot satisfies the signature and defeats its purpose: the origin is a name
|
|
120
|
+
instead of a gesture, and everything read off the real event is gone.
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
// ✗ the handler drops the event, the command is told a story instead
|
|
124
|
+
const openMenu = () => {
|
|
125
|
+
const openEvent = new CustomEvent("open");
|
|
126
|
+
triggerNaviCommand(popoverRef.current, "--navi-open", openEvent);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// ✓ the handler passes on what it was given
|
|
130
|
+
const openMenu = (event) => {
|
|
131
|
+
triggerNaviCommand(popoverRef.current, "--navi-open", event);
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
A helper several presses share takes the event as a parameter like any other;
|
|
136
|
+
the same holds one layer deeper, when the decision is taken by something navi
|
|
137
|
+
handed an event to — an `interactions` detector, an `onOpen`, an action's
|
|
138
|
+
callback. Follow it back: there is a gesture at the start of nearly every
|
|
139
|
+
sequence.
|
|
140
|
+
|
|
141
|
+
The exception is the sequence that genuinely started on its own — a timer
|
|
142
|
+
firing, an action settling, a signal changing. Then say what happened with a
|
|
143
|
+
`CustomEvent` named after it, rather than leaving the origin unsaid:
|
|
113
144
|
|
|
114
145
|
```js
|
|
115
146
|
import { chainEvent, triggerNaviCommand } from "@jsenv/navi";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.153",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Library of components including navigation to create frontend applications",
|
|
6
6
|
"repository": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"sideEffects": [
|
|
52
|
+
"./src/navi_instance.js",
|
|
52
53
|
"./src/navi_css_vars.js",
|
|
53
54
|
"./src/navi_z_indexes.js",
|
|
54
55
|
"./src/control/interaction/*.js",
|