@jsenv/navi 0.29.151 → 0.29.152
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 +68 -5
- package/dist/jsenv_navi.js.map +4 -2
- package/docs/AI_INSTRUCTIONS.md +11 -0
- package/docs/popup_open.md +39 -8
- package/package.json +2 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -15,6 +15,45 @@ import { parseDuration, durationContainsNaN, compareTwoDurations, durationToSeco
|
|
|
15
15
|
export { compareTwoDurations, durationContainsNaN, durationToHours, durationToISOString, durationToMinutes, durationToNumber, durationToSeconds, durationToString, parseDuration } from "@jsenv/validity";
|
|
16
16
|
import { Suspense, createPortal, forwardRef } from "preact/compat";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* One page, one navi.
|
|
20
|
+
*
|
|
21
|
+
* navi keeps contexts, registries and singletons at module scope, so a second
|
|
22
|
+
* copy of the library in the same page is a second of each, and nothing
|
|
23
|
+
* crosses between them. Every symptom of that lands far from the cause and
|
|
24
|
+
* reads as an app bug: a <Loading> mounted by one copy is invisible to the
|
|
25
|
+
* hook reading the other's context and reports itself missing, an action
|
|
26
|
+
* registered in one is unknown to the other, focus and z-index each get a
|
|
27
|
+
* second owner. So the copies are detected here, when the second one is
|
|
28
|
+
* imported, and that import fails — the first thing that goes wrong says what
|
|
29
|
+
* is wrong.
|
|
30
|
+
*
|
|
31
|
+
* A copy is a module evaluation, which is what the object added to the set
|
|
32
|
+
* stands for; the urls are only there to tell the reader which two files to
|
|
33
|
+
* compare. Two urls for one file on disk is the usual shape (a dev server
|
|
34
|
+
* stamping a package version into specifiers, a bundle embedding navi next to
|
|
35
|
+
* a copy resolved separately); two versions installed in node_modules is the
|
|
36
|
+
* other.
|
|
37
|
+
*
|
|
38
|
+
* This module must stay in the entry chunk: the whole point is that it is
|
|
39
|
+
* evaluated once per copy of navi, and a shared chunk would be evaluated once
|
|
40
|
+
* for both.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
// Symbol.for so the set is the same one across copies — a copy cannot see the
|
|
44
|
+
// other's module scope, which is the very thing being detected.
|
|
45
|
+
const NAVI_INSTANCE_SET = Symbol.for("navi_instance_set");
|
|
46
|
+
|
|
47
|
+
const instanceSet = globalThis[NAVI_INSTANCE_SET] || new Set();
|
|
48
|
+
globalThis[NAVI_INSTANCE_SET] = instanceSet;
|
|
49
|
+
instanceSet.add({ url: import.meta.url });
|
|
50
|
+
if (instanceSet.size > 1) {
|
|
51
|
+
const urlList = Array.from(instanceSet, ({ url }) => `- ${url}`).join("\n");
|
|
52
|
+
throw new Error(`@jsenv/navi is loaded ${instanceSet.size} times in this page:
|
|
53
|
+
${urlList}
|
|
54
|
+
Each one builds its own contexts, stores and registries, so what one holds is invisible to the components using another.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
18
57
|
installImportMetaCssBuild(import.meta);/**
|
|
19
58
|
* Every z-index navi can be seen competing on, in one place, ordered.
|
|
20
59
|
*
|
|
@@ -30169,16 +30208,40 @@ const anyMatchingRouteSignal = (routes) => {
|
|
|
30169
30208
|
};
|
|
30170
30209
|
|
|
30171
30210
|
/**
|
|
30211
|
+
* Runs a navi command from JS. It is the LAST RESORT: the attributes say the
|
|
30212
|
+
* same thing declaratively and go through this very function, so a press that
|
|
30213
|
+
* opens, closes, sends, updates or travels needs none of this.
|
|
30214
|
+
*
|
|
30215
|
+
* ```html
|
|
30216
|
+
* <button command="--navi-open" commandfor="note-dialog" value="42">
|
|
30217
|
+
* ```
|
|
30218
|
+
*
|
|
30219
|
+
* `command` names it (`--navi-x:argument` when it needs an argument),
|
|
30220
|
+
* `commandfor` / `navi-command-target` says to whom, the source's own `value`
|
|
30221
|
+
* says what it is about (`command-value` when the source has a value meaning
|
|
30222
|
+
* something else). Reach for `triggerNaviCommand` only when what decides is not
|
|
30223
|
+
* a press on the element carrying those attributes: a long press, the end of a
|
|
30224
|
+
* drag, a double-click, a keyboard shortcut, a server answer, an observer.
|
|
30225
|
+
*
|
|
30172
30226
|
* @param {Element} element The element asking — the command's source, and the
|
|
30173
30227
|
* anchor a popup opens on unless `anchor` says otherwise.
|
|
30174
30228
|
* @param {string} command
|
|
30175
30229
|
* @param {Event} event What caused this. Mandatory: it is what makes a command
|
|
30176
30230
|
* traceable back to its origin — the debug panel groups everything a gesture
|
|
30177
30231
|
* set off under it, and the gates below read it to know whether the default
|
|
30178
|
-
* was already prevented and which mouse button was pressed.
|
|
30179
|
-
*
|
|
30180
|
-
*
|
|
30181
|
-
*
|
|
30232
|
+
* was already prevented and which mouse button was pressed.
|
|
30233
|
+
*
|
|
30234
|
+
* **Forward the event you were handed**, down through every function between
|
|
30235
|
+
* the handler and this call, so what arrives here is the real gesture. Almost
|
|
30236
|
+
* every command has one: a person did something to the page, or the browser
|
|
30237
|
+
* did. Building a `CustomEvent` at the call site instead of taking `event` as
|
|
30238
|
+
* a parameter looks like it satisfies the requirement, and throws away
|
|
30239
|
+
* exactly what makes it useful — the chain a popup reads to give the focus
|
|
30240
|
+
* back, the button that was pressed, a default already prevented.
|
|
30241
|
+
*
|
|
30242
|
+
* The `CustomEvent` is for the genuinely originless case — a timer firing, an
|
|
30243
|
+
* action settling, a signal changing — and even there it names what happened
|
|
30244
|
+
* and chains to whatever preceded it:
|
|
30182
30245
|
* const expiredEvent = new CustomEvent("session_expired");
|
|
30183
30246
|
* chainEvent(expiredEvent, causeEvent); // when something did precede it
|
|
30184
30247
|
* triggerNaviCommand(dialogEl, "--navi-open", expiredEvent);
|
|
@@ -30200,7 +30263,7 @@ const triggerNaviCommand = (
|
|
|
30200
30263
|
) => {
|
|
30201
30264
|
if (!event) {
|
|
30202
30265
|
throw new Error(
|
|
30203
|
-
`"${command}" triggered without an event: it is mandatory, a command must say what caused it.
|
|
30266
|
+
`"${command}" triggered without an event: it is mandatory, a command must say what caused it. Forward the gesture that led here, or a CustomEvent naming the cause when no gesture did — see triggerNaviCommand's jsdoc.`,
|
|
30204
30267
|
);
|
|
30205
30268
|
}
|
|
30206
30269
|
const naviCommand =
|