@jsenv/navi 0.29.121 → 0.29.123
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
CHANGED
|
@@ -4536,7 +4536,11 @@ const css$11 = /* css */`
|
|
|
4536
4536
|
height: auto; /* User agent reset */
|
|
4537
4537
|
margin: 0;
|
|
4538
4538
|
padding: 0; /* User agent reset */
|
|
4539
|
-
|
|
4539
|
+
/* The UA's ink for a [popover] element (CanvasText), not the element's:
|
|
4540
|
+
a callout sits inside the element but writes on its own paper. The
|
|
4541
|
+
color keywords are re-declared against that same ink in
|
|
4542
|
+
navi_css_vars.js (.navi_callout). */
|
|
4543
|
+
color: revert;
|
|
4540
4544
|
font-weight: initial; /* Callout fells disconnected from the element, font weight should be predictible and stable */
|
|
4541
4545
|
font-size: initial; /* Callout fells disconnected from the element, font size should be predictible and stable */
|
|
4542
4546
|
line-height: var(
|
|
@@ -39650,6 +39654,17 @@ const LinkCurrentIndicator = () => {
|
|
|
39650
39654
|
};
|
|
39651
39655
|
markAsOutsideTextFlow(LinkCurrentIndicator);
|
|
39652
39656
|
|
|
39657
|
+
/**
|
|
39658
|
+
* What the container tells what is inside it: which way it travels so a button
|
|
39659
|
+
* can point the right way without being told twice, what a travel handed to a
|
|
39660
|
+
* slide, and the box itself — which is how a way out written inside reads the
|
|
39661
|
+
* same facts a way out written outside reads by id (see useSlideContainer).
|
|
39662
|
+
*
|
|
39663
|
+
* Its own module, tiny on purpose: the hook that reads a container and the
|
|
39664
|
+
* container that fills this in would otherwise have to import each other.
|
|
39665
|
+
*/
|
|
39666
|
+
const SlideContainerContext = createContext(null);
|
|
39667
|
+
|
|
39653
39668
|
/**
|
|
39654
39669
|
* What a SlideContainer is doing, read from outside it.
|
|
39655
39670
|
*
|
|
@@ -39689,14 +39704,16 @@ markAsOutsideTextFlow(LinkCurrentIndicator);
|
|
|
39689
39704
|
// ([data-slide-ways~="right"]).
|
|
39690
39705
|
const SLIDE_CURRENT_ATTRIBUTE = "data-slide-current";
|
|
39691
39706
|
const SLIDE_TOWARD_ATTRIBUTE = "data-slide-travel-toward";
|
|
39692
|
-
//
|
|
39693
|
-
//
|
|
39707
|
+
// Every way out that would DO something right now, in the words the commands
|
|
39708
|
+
// use: a direction ("left", "right", "up", "down") when there is a slide that
|
|
39709
|
+
// way, plus "first" and "last" when one is not already standing at that end of
|
|
39710
|
+
// the walk — and, in all cases, the slide on screen letting go.
|
|
39694
39711
|
const SLIDE_WAYS_ATTRIBUTE = "data-slide-ways";
|
|
39695
|
-
// …and
|
|
39696
|
-
// user (preventNav, or a `required` step still unanswered). Two
|
|
39697
|
-
// a way out leading nowhere is not there, while a way
|
|
39698
|
-
// and says no — which is why it stays visible and
|
|
39699
|
-
// hidden.
|
|
39712
|
+
// …and the ways out that are there and refused: the slide on screen holds on to
|
|
39713
|
+
// the user that way (preventNav, or a `required` step still unanswered). Two
|
|
39714
|
+
// facts, not one: a way out leading nowhere is not there at all, while a way
|
|
39715
|
+
// out being held IS there and says no — which is why it stays visible and
|
|
39716
|
+
// explainable rather than hidden. A way out is dead in both cases.
|
|
39700
39717
|
const SLIDE_HELD_ATTRIBUTE = "data-slide-held";
|
|
39701
39718
|
// …and the one word said out loud, on the box and on every follower of it, when
|
|
39702
39719
|
// any of the above has actually changed. It carries the whole state as its
|
|
@@ -39704,7 +39721,11 @@ const SLIDE_HELD_ATTRIBUTE = "data-slide-held";
|
|
|
39704
39721
|
const SLIDE_STATE_EVENT = "navi_slide_state";
|
|
39705
39722
|
|
|
39706
39723
|
const NO_WAYS = [];
|
|
39724
|
+
// No box to read: not "a box with nothing in it". The difference is the whole
|
|
39725
|
+
// point — see `can` below, which must not answer "there is nowhere to go" to
|
|
39726
|
+
// the question "where can one go" when it has not been told anything yet.
|
|
39707
39727
|
const NOTHING = {
|
|
39728
|
+
known: false,
|
|
39708
39729
|
current: undefined,
|
|
39709
39730
|
toward: undefined,
|
|
39710
39731
|
areas: NO_WAYS,
|
|
@@ -39718,11 +39739,13 @@ const wordsOf = (element, attribute) => {
|
|
|
39718
39739
|
};
|
|
39719
39740
|
|
|
39720
39741
|
const readSlideContainerState = (element) => ({
|
|
39742
|
+
known: true,
|
|
39721
39743
|
current: element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined,
|
|
39722
39744
|
toward: element.getAttribute(SLIDE_TOWARD_ATTRIBUTE) ?? undefined,
|
|
39723
|
-
// In DOM order
|
|
39724
|
-
//
|
|
39725
|
-
// (see
|
|
39745
|
+
// In DOM order — which is the order of the WALK for a line, and not
|
|
39746
|
+
// necessarily for a map: there the order is the one the areas are written in
|
|
39747
|
+
// (see parseAreas). Ask `can("first")` / `can("last")` about the ends rather
|
|
39748
|
+
// than the two ends of this list.
|
|
39726
39749
|
areas: Array.from(
|
|
39727
39750
|
element.querySelectorAll(":scope > [data-slide-track] > [data-slide]"),
|
|
39728
39751
|
(slideElement) =>
|
|
@@ -39733,6 +39756,7 @@ const readSlideContainerState = (element) => ({
|
|
|
39733
39756
|
});
|
|
39734
39757
|
|
|
39735
39758
|
const sameSlideContainerState = (a, b) =>
|
|
39759
|
+
a.known === b.known &&
|
|
39736
39760
|
a.current === b.current &&
|
|
39737
39761
|
a.toward === b.toward &&
|
|
39738
39762
|
a.areas.join(" ") === b.areas.join(" ") &&
|
|
@@ -39743,33 +39767,50 @@ const sameSlideContainerState = (a, b) =>
|
|
|
39743
39767
|
* @param {string|Element|{current: Element}} [target] - the container: its id
|
|
39744
39768
|
* (the way everything else addresses one), the element, or a ref to it. Not a
|
|
39745
39769
|
* follower — a follower is painted for CSS to draw with, the box is what holds
|
|
39746
|
-
* the walk.
|
|
39747
|
-
*
|
|
39748
|
-
*
|
|
39770
|
+
* the walk. Left out, it is the box this is written INSIDE, if any: a way out
|
|
39771
|
+
* reads the same facts on either side of the box, which is the whole point of
|
|
39772
|
+
* there being one answer to "what would this do".
|
|
39749
39773
|
* @returns {{
|
|
39774
|
+
* known: boolean,
|
|
39750
39775
|
* current: string|undefined,
|
|
39751
39776
|
* toward: string|undefined,
|
|
39752
39777
|
* areas: string[],
|
|
39753
|
-
* can: (
|
|
39754
|
-
* held: (
|
|
39778
|
+
* can: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
|
|
39779
|
+
* held: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
|
|
39755
39780
|
* }} where the box stands. `current` is the slide on screen — the one being
|
|
39756
39781
|
* travelled TO while a travel plays, because that is what one is looking at;
|
|
39757
39782
|
* `toward` is the other slide in the frame while the picture is between two,
|
|
39758
|
-
* and nothing at rest. `can` is "
|
|
39759
|
-
* "
|
|
39760
|
-
*
|
|
39783
|
+
* and nothing at rest. `can` is "asking for it would do something", `held` is
|
|
39784
|
+
* "it is there and this slide says no" — a way out is dead in both cases, and
|
|
39785
|
+
* only the second is worth a word to the reader.
|
|
39786
|
+
* `known` is false until the box has been read: no box was named and none is
|
|
39787
|
+
* above, the id names nothing, or — for one commit — this mounted before the
|
|
39788
|
+
* box had painted. Until then `can` answers YES, because it is the answer that
|
|
39789
|
+
* degrades well: a way out offered for one frame and then taken away is a
|
|
39790
|
+
* button that did nothing once, while the reverse hides every way out of every
|
|
39791
|
+
* box that this cannot see and says nothing about it.
|
|
39761
39792
|
*/
|
|
39762
39793
|
const useSlideContainer = (target) => {
|
|
39763
39794
|
const [state, setState] = useState(NOTHING);
|
|
39795
|
+
// The box this is written inside, when nothing names one. Read
|
|
39796
|
+
// unconditionally, like every hook, and used only as a fallback.
|
|
39797
|
+
const containerInContext = useContext(SlideContainerContext);
|
|
39798
|
+
const fallbackRef = containerInContext?.containerRef;
|
|
39764
39799
|
|
|
39765
39800
|
useLayoutEffect(() => {
|
|
39801
|
+
const resolved = target ?? fallbackRef;
|
|
39766
39802
|
const element =
|
|
39767
|
-
typeof
|
|
39768
|
-
? document.getElementById(
|
|
39769
|
-
:
|
|
39770
|
-
?
|
|
39771
|
-
:
|
|
39803
|
+
typeof resolved === "string"
|
|
39804
|
+
? document.getElementById(resolved)
|
|
39805
|
+
: resolved && "current" in resolved
|
|
39806
|
+
? resolved.current
|
|
39807
|
+
: resolved;
|
|
39772
39808
|
if (!element) {
|
|
39809
|
+
if (typeof resolved === "string") {
|
|
39810
|
+
console.warn(
|
|
39811
|
+
`useSlideContainer("${resolved}") but no element with that id found`,
|
|
39812
|
+
);
|
|
39813
|
+
}
|
|
39773
39814
|
setState(NOTHING);
|
|
39774
39815
|
return undefined;
|
|
39775
39816
|
}
|
|
@@ -39788,14 +39829,15 @@ const useSlideContainer = (target) => {
|
|
|
39788
39829
|
return () => {
|
|
39789
39830
|
element.removeEventListener(SLIDE_STATE_EVENT, read);
|
|
39790
39831
|
};
|
|
39791
|
-
}, [target]);
|
|
39832
|
+
}, [target, fallbackRef]);
|
|
39792
39833
|
|
|
39793
39834
|
return {
|
|
39835
|
+
known: state.known,
|
|
39794
39836
|
current: state.current,
|
|
39795
39837
|
toward: state.toward,
|
|
39796
39838
|
areas: state.areas,
|
|
39797
|
-
can: (
|
|
39798
|
-
held: (
|
|
39839
|
+
can: (wayOut) => !state.known || state.ways.includes(wayOut),
|
|
39840
|
+
held: (wayOut) => state.held.includes(wayOut),
|
|
39799
39841
|
};
|
|
39800
39842
|
};
|
|
39801
39843
|
|
|
@@ -49901,9 +49943,6 @@ const isWayOut = element => Boolean(element && element.closest && element.closes
|
|
|
49901
49943
|
// empty map, and a re-render changes nothing for them.
|
|
49902
49944
|
const EMPTY_VALUE_BY_AREA = {};
|
|
49903
49945
|
|
|
49904
|
-
// What the container tells what is inside it: which way it travels, so a button
|
|
49905
|
-
// can point the right way without being told twice.
|
|
49906
|
-
const SlideContainerContext = createContext(null);
|
|
49907
49946
|
// What a slide tells what is inside IT: whether leaving it is allowed right
|
|
49908
49947
|
// now, so its own prev/next buttons say so instead of failing when pressed.
|
|
49909
49948
|
const SlideContext = createContext(null);
|
|
@@ -51323,6 +51362,16 @@ const SlideContainer = ({
|
|
|
51323
51362
|
const paintWays = currentElement => {
|
|
51324
51363
|
const ways = [];
|
|
51325
51364
|
const held = [];
|
|
51365
|
+
// Whether the slide being left says no, this way. The one gate again, read
|
|
51366
|
+
// exactly as goToArea reads it at the moment of the travel — so what is
|
|
51367
|
+
// published here and what would happen cannot disagree.
|
|
51368
|
+
const holdsTowards = forward => currentElement?.hasAttribute(forward ? "data-prevent-nav-next" : "data-prevent-nav-previous");
|
|
51369
|
+
const say = (name, offered, forward) => {
|
|
51370
|
+
if (!offered) {
|
|
51371
|
+
return;
|
|
51372
|
+
}
|
|
51373
|
+
(holdsTowards(forward) ? held : ways).push(name);
|
|
51374
|
+
};
|
|
51326
51375
|
for (const direction of Object.keys(DIRECTIONS)) {
|
|
51327
51376
|
const {
|
|
51328
51377
|
dx,
|
|
@@ -51333,13 +51382,18 @@ const SlideContainer = ({
|
|
|
51333
51382
|
// would be saying it about every row on the page.
|
|
51334
51383
|
continue;
|
|
51335
51384
|
}
|
|
51336
|
-
|
|
51337
|
-
continue;
|
|
51338
|
-
}
|
|
51339
|
-
const forward = dx > 0 || dy > 0;
|
|
51340
|
-
const isHeld = currentElement?.hasAttribute(forward ? "data-prevent-nav-next" : "data-prevent-nav-previous");
|
|
51341
|
-
(isHeld ? held : ways).push(direction);
|
|
51385
|
+
say(direction, Boolean(areaTowards(dx, dy)), dx > 0 || dy > 0);
|
|
51342
51386
|
}
|
|
51387
|
+
// The two ends are ways out like the others — "all the way that way", said
|
|
51388
|
+
// by the map's own order rather than by a direction (see goToEnd, and
|
|
51389
|
+
// SlideContainer.First / .Last). Offered while one is not already standing
|
|
51390
|
+
// there, and held by the same lock the direction they lie in would be.
|
|
51391
|
+
const {
|
|
51392
|
+
order
|
|
51393
|
+
} = readMap();
|
|
51394
|
+
const currentArea = currentElement ? readArea(currentElement) : undefined;
|
|
51395
|
+
say("first", order.length > 0 && order[0] !== currentArea, false);
|
|
51396
|
+
say("last", order.length > 0 && order[order.length - 1] !== currentArea, true);
|
|
51343
51397
|
for (const element of travelPainters()) {
|
|
51344
51398
|
if (ways.length) {
|
|
51345
51399
|
element.setAttribute(SLIDE_WAYS_ATTRIBUTE, ways.join(" "));
|
|
@@ -52082,7 +52136,11 @@ const SlideContainer = ({
|
|
|
52082
52136
|
answeredAreas,
|
|
52083
52137
|
done,
|
|
52084
52138
|
valueByArea,
|
|
52085
|
-
settleFocus
|
|
52139
|
+
settleFocus,
|
|
52140
|
+
// The box itself, for what is written INSIDE it to read the same
|
|
52141
|
+
// facts what is written outside reads by id (useSlideContainer):
|
|
52142
|
+
// a way out is a way out on either side of the box.
|
|
52143
|
+
containerRef
|
|
52086
52144
|
},
|
|
52087
52145
|
children: children
|
|
52088
52146
|
})
|
|
@@ -52295,14 +52353,19 @@ const SlideMove = ({
|
|
|
52295
52353
|
label
|
|
52296
52354
|
} = DIRECTIONS[direction];
|
|
52297
52355
|
const forward = dx > 0 || dy > 0;
|
|
52298
|
-
//
|
|
52299
|
-
//
|
|
52300
|
-
//
|
|
52301
|
-
//
|
|
52302
|
-
// off the box this button already names to ask for the travel: one prop, and
|
|
52303
|
-
// the way out behaves the same on either side of the box.
|
|
52356
|
+
// What this way out would DO, read from wherever it is written: off the box it
|
|
52357
|
+
// names when it is drawn around the box (only slides go in it, so a chevron
|
|
52358
|
+
// pinned to the edge of a full-screen surface has to be), off the box above it
|
|
52359
|
+
// otherwise. One answer on either side of the box.
|
|
52304
52360
|
const slides = useSlideContainer(rest.commandFor);
|
|
52305
|
-
|
|
52361
|
+
// Two reasons to be dead, and they are not the same thing to a reader: this
|
|
52362
|
+
// slide holding on to them (worth explaining — see readOnly on SlideNavButton)
|
|
52363
|
+
// and there being nothing that way at all (the end of the walk, which explains
|
|
52364
|
+
// itself). The lock also comes down as context when this is written inside a
|
|
52365
|
+
// slide, which is the same fact one commit earlier: the box publishes it after
|
|
52366
|
+
// the render, and there is no frame where the way out is live for nothing.
|
|
52367
|
+
const held = (forward ? locks?.preventNavNext : locks?.preventNavPrevious) || slides.held(direction);
|
|
52368
|
+
const locked = held || !slides.can(direction);
|
|
52306
52369
|
return jsx(SlideNavButton, {
|
|
52307
52370
|
command: command,
|
|
52308
52371
|
locked: locked,
|
|
@@ -52314,16 +52377,24 @@ const SlideMove = ({
|
|
|
52314
52377
|
|
|
52315
52378
|
// "All the way that way": the first slide of the walk, or the last one. Not a
|
|
52316
52379
|
// direction — a map reads its own order — so it is its own component rather
|
|
52317
|
-
// than a fifth arrow.
|
|
52380
|
+
// than a fifth arrow. Which is exactly why it cannot work out on its own whether
|
|
52381
|
+
// it would do anything: only the box knows its order, so only the box can say
|
|
52382
|
+
// that one is already standing at that end. It says it in the same breath as the
|
|
52383
|
+
// rest (see paintWays).
|
|
52318
52384
|
const SlideEnd = ({
|
|
52319
52385
|
last,
|
|
52320
52386
|
...rest
|
|
52321
|
-
}) =>
|
|
52322
|
-
|
|
52323
|
-
|
|
52324
|
-
|
|
52325
|
-
|
|
52326
|
-
|
|
52387
|
+
}) => {
|
|
52388
|
+
const slides = useSlideContainer(rest.commandFor);
|
|
52389
|
+
const wayOut = last ? "last" : "first";
|
|
52390
|
+
return jsx(SlideNavButton, {
|
|
52391
|
+
command: last ? "--navi-last" : "--navi-first",
|
|
52392
|
+
locked: slides.held(wayOut) || !slides.can(wayOut),
|
|
52393
|
+
ChevronSvg: last ? ChevronLastSvg : ChevronFirstSvg,
|
|
52394
|
+
"aria-label": last ? "Last slide" : "First slide",
|
|
52395
|
+
...rest
|
|
52396
|
+
});
|
|
52397
|
+
};
|
|
52327
52398
|
const SlideFirst = props => jsx(SlideEnd, {
|
|
52328
52399
|
...props,
|
|
52329
52400
|
last: false
|