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