@jsenv/navi 0.29.119 → 0.29.121
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 +579 -38
- package/dist/jsenv_navi.js.map +52 -20
- package/docs/create_and_edit.md +2 -2
- package/docs/drag_to_travel.md +13 -0
- package/docs/navigation.md +62 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -4256,6 +4256,53 @@ const findControlProxy = (el) => {
|
|
|
4256
4256
|
return firstProxy;
|
|
4257
4257
|
};
|
|
4258
4258
|
|
|
4259
|
+
/**
|
|
4260
|
+
* Typography reset shared by the surfaces a control opens: callout, popover,
|
|
4261
|
+
* dialog.
|
|
4262
|
+
*
|
|
4263
|
+
* A surface is painted on top of the page but lives in the DOM subtree of the
|
|
4264
|
+
* element it opens from, so every inherited text property of that element
|
|
4265
|
+
* reaches it. The ink an element chose for its own background — centered,
|
|
4266
|
+
* shadowed, uppercase, letter-spaced, kept on one line — arrives on paper that
|
|
4267
|
+
* has none of that background, and the caller discovers it as a symptom
|
|
4268
|
+
* (a blurred tooltip, a centered message) with nothing on screen pointing back
|
|
4269
|
+
* to the rule three elements up. A surface writes its own text, on its own
|
|
4270
|
+
* paper, in the document's terms.
|
|
4271
|
+
*
|
|
4272
|
+
* Every one of them is set to its initial value rather than `revert`: only
|
|
4273
|
+
* `color` and `background-color` are declared by the UA on a `[popover]`/
|
|
4274
|
+
* `<dialog>`, so `revert` on any of these would roll back to the inherited
|
|
4275
|
+
* value — the very thing to stop. `text-align: initial` is `start`, which
|
|
4276
|
+
* still reads the surface's own `direction`, so an RTL document keeps its
|
|
4277
|
+
* side.
|
|
4278
|
+
*
|
|
4279
|
+
* `font-family` is deliberately absent: a surface keeps the face of what
|
|
4280
|
+
* opened it, so a section written in a display font gets its tooltips in that
|
|
4281
|
+
* font. `color`, `font-size` and `font-weight` are absent too — each surface
|
|
4282
|
+
* answers those its own way (a popup writes in --navi-popup-color and follows
|
|
4283
|
+
* the size of what opened it; a callout reverts to the document's ink and
|
|
4284
|
+
* size).
|
|
4285
|
+
*
|
|
4286
|
+
* In `@layer navi` so an app that wants one of these back says so on the
|
|
4287
|
+
* surface and wins, without having to out-specify anything.
|
|
4288
|
+
*/
|
|
4289
|
+
const surfaceTextCss = /* css */ `
|
|
4290
|
+
@layer navi {
|
|
4291
|
+
.navi_callout,
|
|
4292
|
+
.navi_popover,
|
|
4293
|
+
.navi_dialog {
|
|
4294
|
+
font-style: initial;
|
|
4295
|
+
text-align: initial;
|
|
4296
|
+
text-indent: initial;
|
|
4297
|
+
text-transform: initial;
|
|
4298
|
+
text-shadow: none;
|
|
4299
|
+
white-space: initial;
|
|
4300
|
+
word-spacing: initial;
|
|
4301
|
+
letter-spacing: initial;
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
`;
|
|
4305
|
+
|
|
4259
4306
|
const CalloutContext = createContext();
|
|
4260
4307
|
const useCalloutRequestClose = () => {
|
|
4261
4308
|
return useContext(CalloutContext)?.requestClose;
|
|
@@ -4601,7 +4648,6 @@ const css$11 = /* css */`
|
|
|
4601
4648
|
box-sizing: border-box;
|
|
4602
4649
|
box-decoration-break: clone;
|
|
4603
4650
|
align-self: center;
|
|
4604
|
-
white-space: normal; /* Override in case ancetor sets nowrap */
|
|
4605
4651
|
word-break: break-word;
|
|
4606
4652
|
overflow-wrap: anywhere;
|
|
4607
4653
|
|
|
@@ -4670,6 +4716,8 @@ const css$11 = /* css */`
|
|
|
4670
4716
|
}
|
|
4671
4717
|
}
|
|
4672
4718
|
}
|
|
4719
|
+
|
|
4720
|
+
${surfaceTextCss}
|
|
4673
4721
|
`;
|
|
4674
4722
|
|
|
4675
4723
|
/**
|
|
@@ -38845,7 +38893,6 @@ installImportMetaCssBuild(import.meta);const css$W = /* css */`
|
|
|
38845
38893
|
--link-text-decoration: underline;
|
|
38846
38894
|
--link-text-decoration-hover: var(--link-text-decoration);
|
|
38847
38895
|
--link-cursor: pointer;
|
|
38848
|
-
--link-loading-outline-size: 1px;
|
|
38849
38896
|
--link-outline-width: 2px;
|
|
38850
38897
|
|
|
38851
38898
|
--link-current-indicator-size: 2px;
|
|
@@ -38904,12 +38951,7 @@ installImportMetaCssBuild(import.meta);const css$W = /* css */`
|
|
|
38904
38951
|
aspect-ratio: inherit;
|
|
38905
38952
|
padding-top: var(--x-link-padding-top);
|
|
38906
38953
|
padding-right: var(--x-link-padding-right);
|
|
38907
|
-
|
|
38908
|
-
the bottom on top of whatever padding was asked for, so a link that
|
|
38909
|
-
starts loading never reflows. */
|
|
38910
|
-
padding-bottom: calc(
|
|
38911
|
-
var(--x-link-padding-bottom) + var(--link-loading-outline-size)
|
|
38912
|
-
);
|
|
38954
|
+
padding-bottom: var(--x-link-padding-bottom);
|
|
38913
38955
|
padding-left: var(--x-link-padding-left);
|
|
38914
38956
|
color: var(--x-link-color);
|
|
38915
38957
|
text-decoration: var(--x-link-text-decoration);
|
|
@@ -39608,6 +39650,155 @@ const LinkCurrentIndicator = () => {
|
|
|
39608
39650
|
};
|
|
39609
39651
|
markAsOutsideTextFlow(LinkCurrentIndicator);
|
|
39610
39652
|
|
|
39653
|
+
/**
|
|
39654
|
+
* What a SlideContainer is doing, read from outside it.
|
|
39655
|
+
*
|
|
39656
|
+
* Only slides go in the box, so everything drawn AROUND the travel is written
|
|
39657
|
+
* around it — a chevron pinned to the edge of a full-screen viewer, a "3 / 8"
|
|
39658
|
+
* counter, a bar. Those need to know things the box alone knows: where one is,
|
|
39659
|
+
* and whether there is anywhere to go that way. A container driven by nobody
|
|
39660
|
+
* (no `current`, no `signal`) knows them and no one else does, so this is also
|
|
39661
|
+
* what keeps such a container usable without lifting its state out of it.
|
|
39662
|
+
*
|
|
39663
|
+
* Two channels, and they answer two different questions. The attributes the box
|
|
39664
|
+
* paints on itself are the STATE: they are there for whoever arrives later, and
|
|
39665
|
+
* CSS draws with them without any of this ([data-slide-ways~="right"]). The
|
|
39666
|
+
* event it dispatches is the NEWS: it says WHEN something changed, which no
|
|
39667
|
+
* attribute can say. So this reads the first and listens to the second.
|
|
39668
|
+
*
|
|
39669
|
+
* Not watching the DOM for it, which is the tempting third way and the wrong
|
|
39670
|
+
* one: a write is not a change. Under a finger the box writes where the picture
|
|
39671
|
+
* leans on every frame, with the same value in it more often than not, and a
|
|
39672
|
+
* watcher is woken for every one of them — while the box itself knows perfectly
|
|
39673
|
+
* well whether anything is different, and says so once.
|
|
39674
|
+
*
|
|
39675
|
+
* Nothing is told to the box in return, and nothing has to be wired: it
|
|
39676
|
+
* announces to itself and to its followers, so the thing doing the reading sits
|
|
39677
|
+
* anywhere on the page — in a fixed bar, in the frame around the box, in a
|
|
39678
|
+
* dialog holding it — and a container which never re-renders (a travel it drove
|
|
39679
|
+
* itself) is still followed.
|
|
39680
|
+
*
|
|
39681
|
+
* Reading only. Asking for a travel is what it always was: a command aimed at
|
|
39682
|
+
* the box (`commandFor={id}` + `--navi-right`, or triggerNaviCommand) — one way
|
|
39683
|
+
* of asking, wherever the asking is written.
|
|
39684
|
+
*/
|
|
39685
|
+
|
|
39686
|
+
|
|
39687
|
+
// The vocabulary the box publishes, in one place: written by slide_container,
|
|
39688
|
+
// read here and by anything styling in CSS alone
|
|
39689
|
+
// ([data-slide-ways~="right"]).
|
|
39690
|
+
const SLIDE_CURRENT_ATTRIBUTE = "data-slide-current";
|
|
39691
|
+
const SLIDE_TOWARD_ATTRIBUTE = "data-slide-travel-toward";
|
|
39692
|
+
// Where a travel WOULD go right now: there is a slide that way and the one on
|
|
39693
|
+
// screen lets go of it.
|
|
39694
|
+
const SLIDE_WAYS_ATTRIBUTE = "data-slide-ways";
|
|
39695
|
+
// …and where there is a slide that way but the one on screen holds on to the
|
|
39696
|
+
// user (preventNav, or a `required` step still unanswered). Two facts, not one:
|
|
39697
|
+
// a way out leading nowhere is not there, while a way out being held is there
|
|
39698
|
+
// and says no — which is why it stays visible and explainable rather than
|
|
39699
|
+
// hidden.
|
|
39700
|
+
const SLIDE_HELD_ATTRIBUTE = "data-slide-held";
|
|
39701
|
+
// …and the one word said out loud, on the box and on every follower of it, when
|
|
39702
|
+
// any of the above has actually changed. It carries the whole state as its
|
|
39703
|
+
// detail, so `onnavi_slide_state` on the box is a way of hearing it too.
|
|
39704
|
+
const SLIDE_STATE_EVENT = "navi_slide_state";
|
|
39705
|
+
|
|
39706
|
+
const NO_WAYS = [];
|
|
39707
|
+
const NOTHING = {
|
|
39708
|
+
current: undefined,
|
|
39709
|
+
toward: undefined,
|
|
39710
|
+
areas: NO_WAYS,
|
|
39711
|
+
ways: NO_WAYS,
|
|
39712
|
+
held: NO_WAYS,
|
|
39713
|
+
};
|
|
39714
|
+
|
|
39715
|
+
const wordsOf = (element, attribute) => {
|
|
39716
|
+
const value = element.getAttribute(attribute);
|
|
39717
|
+
return value ? value.split(" ") : NO_WAYS;
|
|
39718
|
+
};
|
|
39719
|
+
|
|
39720
|
+
const readSlideContainerState = (element) => ({
|
|
39721
|
+
current: element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined,
|
|
39722
|
+
toward: element.getAttribute(SLIDE_TOWARD_ATTRIBUTE) ?? undefined,
|
|
39723
|
+
// In DOM order, which is the order of the walk for a line and the order the
|
|
39724
|
+
// areas were written in for a map — the same order everything else reads
|
|
39725
|
+
// (see readMap).
|
|
39726
|
+
areas: Array.from(
|
|
39727
|
+
element.querySelectorAll(":scope > [data-slide-track] > [data-slide]"),
|
|
39728
|
+
(slideElement) =>
|
|
39729
|
+
slideElement.getAttribute("data-slide-area") || slideElement.id || "",
|
|
39730
|
+
),
|
|
39731
|
+
ways: wordsOf(element, SLIDE_WAYS_ATTRIBUTE),
|
|
39732
|
+
held: wordsOf(element, SLIDE_HELD_ATTRIBUTE),
|
|
39733
|
+
});
|
|
39734
|
+
|
|
39735
|
+
const sameSlideContainerState = (a, b) =>
|
|
39736
|
+
a.current === b.current &&
|
|
39737
|
+
a.toward === b.toward &&
|
|
39738
|
+
a.areas.join(" ") === b.areas.join(" ") &&
|
|
39739
|
+
a.ways.join(" ") === b.ways.join(" ") &&
|
|
39740
|
+
a.held.join(" ") === b.held.join(" ");
|
|
39741
|
+
|
|
39742
|
+
/**
|
|
39743
|
+
* @param {string|Element|{current: Element}} [target] - the container: its id
|
|
39744
|
+
* (the way everything else addresses one), the element, or a ref to it. Not a
|
|
39745
|
+
* follower — a follower is painted for CSS to draw with, the box is what holds
|
|
39746
|
+
* the walk. Nothing at all is allowed and answers "no container": a component
|
|
39747
|
+
* that may or may not be wired to one calls this unconditionally, like every
|
|
39748
|
+
* hook.
|
|
39749
|
+
* @returns {{
|
|
39750
|
+
* current: string|undefined,
|
|
39751
|
+
* toward: string|undefined,
|
|
39752
|
+
* areas: string[],
|
|
39753
|
+
* can: (direction: "left"|"right"|"up"|"down") => boolean,
|
|
39754
|
+
* held: (direction: "left"|"right"|"up"|"down") => boolean,
|
|
39755
|
+
* }} where the box stands. `current` is the slide on screen — the one being
|
|
39756
|
+
* travelled TO while a travel plays, because that is what one is looking at;
|
|
39757
|
+
* `toward` is the other slide in the frame while the picture is between two,
|
|
39758
|
+
* and nothing at rest. `can` is "a travel that way would happen", `held` is
|
|
39759
|
+
* "there is a slide that way and this one says no" — a chevron is dead in
|
|
39760
|
+
* both cases and only the second is worth explaining.
|
|
39761
|
+
*/
|
|
39762
|
+
const useSlideContainer = (target) => {
|
|
39763
|
+
const [state, setState] = useState(NOTHING);
|
|
39764
|
+
|
|
39765
|
+
useLayoutEffect(() => {
|
|
39766
|
+
const element =
|
|
39767
|
+
typeof target === "string"
|
|
39768
|
+
? document.getElementById(target)
|
|
39769
|
+
: target && "current" in target
|
|
39770
|
+
? target.current
|
|
39771
|
+
: target;
|
|
39772
|
+
if (!element) {
|
|
39773
|
+
setState(NOTHING);
|
|
39774
|
+
return undefined;
|
|
39775
|
+
}
|
|
39776
|
+
// Read off the DOM rather than taken from the event's detail, even though
|
|
39777
|
+
// the two say the same thing: the first read has no event to take it from
|
|
39778
|
+
// (the box was already standing somewhere when this mounted), and one way
|
|
39779
|
+
// of reading is one thing that can be wrong.
|
|
39780
|
+
const read = () => {
|
|
39781
|
+
const nextState = readSlideContainerState(element);
|
|
39782
|
+
setState((previous) =>
|
|
39783
|
+
sameSlideContainerState(previous, nextState) ? previous : nextState,
|
|
39784
|
+
);
|
|
39785
|
+
};
|
|
39786
|
+
read();
|
|
39787
|
+
element.addEventListener(SLIDE_STATE_EVENT, read);
|
|
39788
|
+
return () => {
|
|
39789
|
+
element.removeEventListener(SLIDE_STATE_EVENT, read);
|
|
39790
|
+
};
|
|
39791
|
+
}, [target]);
|
|
39792
|
+
|
|
39793
|
+
return {
|
|
39794
|
+
current: state.current,
|
|
39795
|
+
toward: state.toward,
|
|
39796
|
+
areas: state.areas,
|
|
39797
|
+
can: (direction) => state.ways.includes(direction),
|
|
39798
|
+
held: (direction) => state.held.includes(direction),
|
|
39799
|
+
};
|
|
39800
|
+
};
|
|
39801
|
+
|
|
39611
39802
|
installImportMetaCssBuild(import.meta);/**
|
|
39612
39803
|
* TabList component with support for horizontal and vertical layouts
|
|
39613
39804
|
* https://dribbble.com/search/tabs
|
|
@@ -40006,19 +40197,17 @@ const Nav = ({
|
|
|
40006
40197
|
}
|
|
40007
40198
|
slideContainerElementRef.current = containerElement;
|
|
40008
40199
|
const readContainer = () => {
|
|
40009
|
-
setCurrentSlideArea(containerElement.getAttribute(
|
|
40200
|
+
setCurrentSlideArea(containerElement.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined);
|
|
40010
40201
|
paintIndicatorGeometryRef.current();
|
|
40011
40202
|
};
|
|
40012
40203
|
readContainer();
|
|
40013
|
-
//
|
|
40014
|
-
//
|
|
40015
|
-
//
|
|
40016
|
-
// rather than
|
|
40017
|
-
|
|
40018
|
-
|
|
40019
|
-
|
|
40020
|
-
attributeFilter: ["data-slide-current", "data-slide-travel-toward"]
|
|
40021
|
-
});
|
|
40204
|
+
// Where one is and what the picture leans on are written on the container,
|
|
40205
|
+
// and the container says out loud when either has changed: this row is not
|
|
40206
|
+
// in the box (it sits above it, in a fixed bar, anywhere), so it reads the
|
|
40207
|
+
// first and listens to the second. Listening rather than watching the DOM
|
|
40208
|
+
// because a write is not a change — the attribute the trait follows is
|
|
40209
|
+
// written on every frame of a gesture with the same value in it.
|
|
40210
|
+
containerElement.addEventListener(SLIDE_STATE_EVENT, readContainer);
|
|
40022
40211
|
// A row whose tabs changed width — a badge count, a font that just
|
|
40023
40212
|
// arrived, a window resized — is measured again: what was written is
|
|
40024
40213
|
// pixels, and pixels go stale.
|
|
@@ -40027,7 +40216,7 @@ const Nav = ({
|
|
|
40027
40216
|
});
|
|
40028
40217
|
sizeObserver.observe(navRef.current);
|
|
40029
40218
|
return () => {
|
|
40030
|
-
|
|
40219
|
+
containerElement.removeEventListener(SLIDE_STATE_EVENT, readContainer);
|
|
40031
40220
|
sizeObserver.disconnect();
|
|
40032
40221
|
slideContainerElementRef.current = null;
|
|
40033
40222
|
};
|
|
@@ -49856,7 +50045,7 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
49856
50045
|
* it says where one starts, not where one is — say `current` for that.
|
|
49857
50046
|
* Without it the first slide is the one shown, the way a stack of pages opens
|
|
49858
50047
|
* on its first page.
|
|
49859
|
-
* @param {(area: string, detail: {cause: "drag"|"keyboard"|"command"|"code", event: Event}) => void|false|Promise<void|false>} [props.onCurrentChange]
|
|
50048
|
+
* @param {(area: string, detail: {cause: "drag"|"keyboard"|"command"|"code"|"url", event: Event}) => void|false|Promise<void|false>} [props.onCurrentChange]
|
|
49860
50049
|
* - the slide being shown has changed. `cause` says what asked for it, which
|
|
49861
50050
|
* is what tells a place browsed past from a place aimed at: a caller writing
|
|
49862
50051
|
* this into the URL pushes a history entry for a tab that was pressed and
|
|
@@ -49866,6 +50055,8 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
49866
50055
|
* from — a guard that says no, a session that is gone. A promise refuses it
|
|
49867
50056
|
* late, once whatever it had to ask has answered; the travel plays meanwhile
|
|
49868
50057
|
* and is undone if the answer is no.
|
|
50058
|
+
* `cause: "url"` is the address asking (see `urlParam`) — a load on a link, a
|
|
50059
|
+
* traversal — rather than anything done inside the box.
|
|
49869
50060
|
* @param {"now"|"rest"} [props.commit="now"] - when the change is told.
|
|
49870
50061
|
* "rest" waits for the travel to be over, and lets the container hold the
|
|
49871
50062
|
* slide it is going to meanwhile: the picture moves with the finger and the
|
|
@@ -49883,6 +50074,33 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
49883
50074
|
* one that just travelled there. Called once the travel is over, and in the
|
|
49884
50075
|
* same render as the return to rest — anything later shows the old content
|
|
49885
50076
|
* for a frame.
|
|
50077
|
+
* @param {string|{name: string, history?: "replace"|"push"}} [props.urlParam]
|
|
50078
|
+
* - the search param this container owns, and where it stands is written
|
|
50079
|
+
* into: `urlParam="step"` puts `?step=<area>` in the address on every travel,
|
|
50080
|
+
* and opens on the area it names on a load, a bookmark, a traversal, a link
|
|
50081
|
+
* from anywhere else in the application.
|
|
50082
|
+
* It is the answer to "the step should be readable and should survive a
|
|
50083
|
+
* reload" WITHOUT one route per slide: no route to declare, no route guard to
|
|
50084
|
+
* re-express the walk's own rules in, and no page transition — what travels
|
|
50085
|
+
* is the box, and the address is a label on where the box stands. Say it on a
|
|
50086
|
+
* container whose slides are places one may be sent to; a walk nobody links
|
|
50087
|
+
* into keeps its position to itself.
|
|
50088
|
+
* Written by REPLACEMENT: four steps that each stacked an entry would turn
|
|
50089
|
+
* one back-press into four, and the back arrow of a page means "leave this
|
|
50090
|
+
* page", not "one question back". `history: "push"` says the opposite for the
|
|
50091
|
+
* containers that mean it — slides that ARE places one came from, a gallery
|
|
50092
|
+
* one browses — and even there a slide reached by dragging replaces, so
|
|
50093
|
+
* swiping back and forth does not bury the way out.
|
|
50094
|
+
* The address is READ through the walk, not jumped to: it comes from outside
|
|
50095
|
+
* the box, so every slide between here and there is asked to let go the way a
|
|
50096
|
+
* key going that way would ask it, and the first one that holds
|
|
50097
|
+
* (`preventNav`, or a `required` step still unanswered) is where one stops —
|
|
50098
|
+
* `?step=done` cannot open a confirmation screen for something nobody sent.
|
|
50099
|
+
* Whatever comes of it, the address is then rewritten with the area actually
|
|
50100
|
+
* shown: it says where one IS, never where one asked to be. A step the app
|
|
50101
|
+
* knows is already answered on a reload is told so by its own `required`
|
|
50102
|
+
* (`required={!alreadyFilled}`) — the container remembers nothing across a
|
|
50103
|
+
* load, and cannot.
|
|
49886
50104
|
* Each way of travelling can be shut off, or narrowed to one axis: `true`
|
|
49887
50105
|
* (every axis the map has), `false`, `"x"`, `"y"`, `"xy"`.
|
|
49888
50106
|
*
|
|
@@ -49893,6 +50111,13 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
49893
50111
|
* screens are steps rather than places — so the keys keep the meaning the
|
|
49894
50112
|
* content gives them, and travelling stays something one asks for (a button,
|
|
49895
50113
|
* a command).
|
|
50114
|
+
* WHERE they are heard is a separate question, and it is not answered here: a
|
|
50115
|
+
* key only ever reaches what has the focus, so this box hears the ones pressed
|
|
50116
|
+
* inside it — and every follower of it hears the rest. A chevron pinned to the
|
|
50117
|
+
* edge of a full-screen surface is outside the box by necessity (only slides
|
|
50118
|
+
* go in it), so the surface says `data-slide-container-follows={id}` and the
|
|
50119
|
+
* arrows keep walking wherever the keyboard is in it. See the paragraph above
|
|
50120
|
+
* about what is drawn AROUND the travel.
|
|
49896
50121
|
* @param {boolean|"x"|"y"|"xy"} [props.travelByDrag=true] - whether a pointer
|
|
49897
50122
|
* dragging the slides travels. On by default: slides side by side are
|
|
49898
50123
|
* something one expects to push around with a thumb. Off where the gesture
|
|
@@ -49928,6 +50153,7 @@ const SlideContainer = ({
|
|
|
49928
50153
|
commit = "now",
|
|
49929
50154
|
loop,
|
|
49930
50155
|
onLoop,
|
|
50156
|
+
urlParam,
|
|
49931
50157
|
travelByKeyboard = true,
|
|
49932
50158
|
travelByDrag = true,
|
|
49933
50159
|
travelByScroll = "x",
|
|
@@ -50023,6 +50249,51 @@ const SlideContainer = ({
|
|
|
50023
50249
|
const followerElementsRef = useRef([]);
|
|
50024
50250
|
const currentFromCaller = currentSignal ? currentSignal.value : currentProp;
|
|
50025
50251
|
const current = rollingArea ?? provisionalArea ?? currentFromCaller ?? currentAreaState;
|
|
50252
|
+
// The search param this container owns, when it owns one: the name it is
|
|
50253
|
+
// written under, and whether going somewhere is somewhere one CAME from.
|
|
50254
|
+
const urlParamName = typeof urlParam === "string" ? urlParam : urlParam?.name;
|
|
50255
|
+
const urlParamHistory = urlParam?.history || "replace";
|
|
50256
|
+
// What the address says right now. Read from the signal rather than from
|
|
50257
|
+
// window.location so that a traversal — the browser's own back and forward —
|
|
50258
|
+
// is a render like any other: the address moved, and the slides follow it.
|
|
50259
|
+
// Read only when there is a param to read, so a container that owns none is
|
|
50260
|
+
// not re-rendered by every navigation in the application.
|
|
50261
|
+
const areaInUrl = urlParamName ? new URL(documentUrlSignal.value).searchParams.get(urlParamName) : null;
|
|
50262
|
+
// The last thing this container knows the address said, whether it wrote it
|
|
50263
|
+
// or read it. What makes a two-way binding stop turning: the address is only
|
|
50264
|
+
// an INSTRUCTION when it changed by itself (a load, a traversal, a link), and
|
|
50265
|
+
// what this container put there is not news. Without it, a container ahead of
|
|
50266
|
+
// its own URL (commit="rest", where the picture arrives before the address is
|
|
50267
|
+
// written) would read its own lateness as an order to go back.
|
|
50268
|
+
const areaInUrlSeenRef = useRef(undefined);
|
|
50269
|
+
// Where the container stands, said in the address. `replace` amends the entry
|
|
50270
|
+
// one is on: a walk of four steps must not turn one back-press into four, and
|
|
50271
|
+
// the back arrow of a page keeps meaning "leave this page". `push` is for
|
|
50272
|
+
// slides that ARE places one came from — and even there a slide reached by
|
|
50273
|
+
// DRAGGING replaces, because browsing back and forth with a thumb is not a
|
|
50274
|
+
// trail one wants to walk home along.
|
|
50275
|
+
const writeUrlParam = (area, cause, {
|
|
50276
|
+
replace = false
|
|
50277
|
+
} = {}) => {
|
|
50278
|
+
if (!urlParamName) {
|
|
50279
|
+
return;
|
|
50280
|
+
}
|
|
50281
|
+
areaInUrlSeenRef.current = area;
|
|
50282
|
+
const urlObject = new URL(window.location.href);
|
|
50283
|
+
if (urlObject.searchParams.get(urlParamName) === area) {
|
|
50284
|
+
return;
|
|
50285
|
+
}
|
|
50286
|
+
urlObject.searchParams.set(urlParamName, area);
|
|
50287
|
+
navTo(urlObject.href, {
|
|
50288
|
+
replace: replace || urlParamHistory !== "push" || cause === "drag" ||
|
|
50289
|
+
// The address already says it: reading it back is not a place one went.
|
|
50290
|
+
cause === "url",
|
|
50291
|
+
// What travels is the box, not the page: the address is a label on where
|
|
50292
|
+
// the box stands, and a route transition would move the whole document
|
|
50293
|
+
// for a slide that has already travelled by itself.
|
|
50294
|
+
routeTransition: "none"
|
|
50295
|
+
});
|
|
50296
|
+
};
|
|
50026
50297
|
const vertical = layout === "column";
|
|
50027
50298
|
// What the map has, and what each way of asking is allowed to use of it.
|
|
50028
50299
|
const mapAxes = travelAxesOf(layout);
|
|
@@ -50167,7 +50438,7 @@ const SlideContainer = ({
|
|
|
50167
50438
|
// there is nothing else for it to read — a slide the container holds by
|
|
50168
50439
|
// itself is known to no one else.
|
|
50169
50440
|
const paintCurrentArea = area => {
|
|
50170
|
-
containerRef.current?.setAttribute(
|
|
50441
|
+
containerRef.current?.setAttribute(SLIDE_CURRENT_ATTRIBUTE, area);
|
|
50171
50442
|
};
|
|
50172
50443
|
const markAnswered = area => {
|
|
50173
50444
|
const order = readMap().slideElements.map(readArea);
|
|
@@ -50490,6 +50761,14 @@ const SlideContainer = ({
|
|
|
50490
50761
|
if (!stageRef.current) {
|
|
50491
50762
|
drawnAreaRef.current = currentArea;
|
|
50492
50763
|
}
|
|
50764
|
+
// Said last, and after data-current has been written: what a travel would do
|
|
50765
|
+
// is read off the map from the slide that is now current, and off the locks
|
|
50766
|
+
// that slide is wearing in this very commit.
|
|
50767
|
+
paintWays(currentElement);
|
|
50768
|
+
// …and once everything this commit had to write is written, the box says so
|
|
50769
|
+
// — a single announcement for a render that may have changed several of the
|
|
50770
|
+
// facts at once.
|
|
50771
|
+
announceState();
|
|
50493
50772
|
// The finger has the last word: everything above drew the map at rest, and
|
|
50494
50773
|
// where the track actually is right now is where the gesture put it.
|
|
50495
50774
|
paintDrag();
|
|
@@ -50625,7 +50904,7 @@ const SlideContainer = ({
|
|
|
50625
50904
|
}
|
|
50626
50905
|
const leftArea = readArea(currentElement);
|
|
50627
50906
|
setCurrentAreaState(area);
|
|
50628
|
-
if (!onCurrentChange && !currentSignal) {
|
|
50907
|
+
if (!onCurrentChange && !currentSignal && !urlParamName) {
|
|
50629
50908
|
return true;
|
|
50630
50909
|
}
|
|
50631
50910
|
// What asked for this, read off the interaction rather than carried down
|
|
@@ -50660,6 +50939,12 @@ const SlideContainer = ({
|
|
|
50660
50939
|
// `onCurrentChange` is called, in that order, so a caller reading the signal
|
|
50661
50940
|
// from inside its own handler reads where it now is.
|
|
50662
50941
|
const tellCurrentChange = (area, detail, leftArea) => {
|
|
50942
|
+
// The address first, because it is the one thing that must never disagree
|
|
50943
|
+
// with the picture — and it is told here rather than by the caller so that
|
|
50944
|
+
// it is told about the travels that HAPPENED and about no others: the ones
|
|
50945
|
+
// a lock refused never reach this point, and one refused late is written
|
|
50946
|
+
// back below (see goBackToRefusedArea).
|
|
50947
|
+
writeUrlParam(area, detail.cause);
|
|
50663
50948
|
if (currentSignal) {
|
|
50664
50949
|
currentSignal.value = area;
|
|
50665
50950
|
}
|
|
@@ -50693,8 +50978,62 @@ const SlideContainer = ({
|
|
|
50693
50978
|
if (currentSignal) {
|
|
50694
50979
|
currentSignal.value = leftArea;
|
|
50695
50980
|
}
|
|
50981
|
+
// Written over rather than stacked on, whatever this container does with
|
|
50982
|
+
// the history otherwise: the entry the refused travel wrote is the one
|
|
50983
|
+
// being corrected, and a refusal is not a place one was.
|
|
50984
|
+
writeUrlParam(leftArea, "code", {
|
|
50985
|
+
replace: true
|
|
50986
|
+
});
|
|
50696
50987
|
};
|
|
50697
50988
|
|
|
50989
|
+
// The address asks for a slide. Read on every render it changes on rather
|
|
50990
|
+
// than at mount alone, because the browser's own arrows are exactly that —
|
|
50991
|
+
// the address moving by itself — and a link from elsewhere in the application
|
|
50992
|
+
// is too. What follows is a travel like any other: the slides move, the
|
|
50993
|
+
// caller is told, and the address ends up saying where the box actually
|
|
50994
|
+
// stands.
|
|
50995
|
+
useLayoutEffect(() => {
|
|
50996
|
+
if (!urlParamName) {
|
|
50997
|
+
return;
|
|
50998
|
+
}
|
|
50999
|
+
if (areaInUrl === areaInUrlSeenRef.current) {
|
|
51000
|
+
// Not news: either nothing moved, or this container is reading back what
|
|
51001
|
+
// it wrote itself.
|
|
51002
|
+
return;
|
|
51003
|
+
}
|
|
51004
|
+
areaInUrlSeenRef.current = areaInUrl;
|
|
51005
|
+
if (areaInUrl === null) {
|
|
51006
|
+
// An address that says nothing is not an address saying "the first
|
|
51007
|
+
// slide": a container opened without the param opens where it would have
|
|
51008
|
+
// opened anyway, and the param appears the first time one travels.
|
|
51009
|
+
return;
|
|
51010
|
+
}
|
|
51011
|
+
// Where the box IS, read off the DOM: `current` is undefined until someone
|
|
51012
|
+
// names a slide, and the container standing on its first one is a fact only
|
|
51013
|
+
// the map knows (see the layout effect that paints it).
|
|
51014
|
+
const areaOnScreen = containerRef.current?.getAttribute("data-slide-current");
|
|
51015
|
+
if (!areaOnScreen || areaInUrl === areaOnScreen) {
|
|
51016
|
+
return;
|
|
51017
|
+
}
|
|
51018
|
+
const reached = reachableTowards(areaOnScreen, areaInUrl);
|
|
51019
|
+
if (reached && reached !== areaOnScreen) {
|
|
51020
|
+
// Told rather than asked: the walk above has already put every lock on
|
|
51021
|
+
// the way the question goToArea would have put the first one, and this
|
|
51022
|
+
// travel has no interaction behind it to hand a focus to — the slide
|
|
51023
|
+
// arriving is handed the keyboard by the layout effect, the way it is for
|
|
51024
|
+
// any travel nobody pressed anything for.
|
|
51025
|
+
setCurrentAreaState(reached);
|
|
51026
|
+
tellCurrentChange(reached, {
|
|
51027
|
+
cause: "url"
|
|
51028
|
+
}, areaOnScreen);
|
|
51029
|
+
return;
|
|
51030
|
+
}
|
|
51031
|
+
// The param names nowhere this map knows, or somewhere the walk is not
|
|
51032
|
+
// allowed to reach: the address is put back on the slide one is actually
|
|
51033
|
+
// looking at, rather than left saying one is somewhere one is not.
|
|
51034
|
+
writeUrlParam(areaOnScreen, "url");
|
|
51035
|
+
}, [urlParamName, areaInUrl]);
|
|
51036
|
+
|
|
50698
51037
|
// The press kept during a roll, taken once the window rests and the travel is
|
|
50699
51038
|
// given back (noTravel off): by direction when there was one, so it is read
|
|
50700
51039
|
// against the map as it is NOW — the content moved one step under the window
|
|
@@ -50788,20 +51127,25 @@ const SlideContainer = ({
|
|
|
50788
51127
|
};
|
|
50789
51128
|
|
|
50790
51129
|
/**
|
|
51130
|
+
* @param {number} dx
|
|
51131
|
+
* @param {number} dy
|
|
51132
|
+
* @param {string} [fromArea] - where to step from. The slide on screen when
|
|
51133
|
+
* nothing says otherwise; named only by a walk that is not standing there
|
|
51134
|
+
* (see reachableTowards).
|
|
50791
51135
|
* @returns {string|undefined} the area one step that way, if there is one.
|
|
50792
51136
|
* Nothing there means the direction is simply not offered — no wrapping, no
|
|
50793
51137
|
* nearest-match: a map is read as a map, and a move landing nowhere would
|
|
50794
51138
|
* break that reading. Walks over its own cells first, so a spanning area
|
|
50795
51139
|
* leaves by its far edge rather than onto itself.
|
|
50796
51140
|
*/
|
|
50797
|
-
const areaTowards = (dx, dy) => {
|
|
51141
|
+
const areaTowards = (dx, dy, fromArea) => {
|
|
50798
51142
|
const {
|
|
50799
51143
|
slideElements,
|
|
50800
51144
|
areaAt,
|
|
50801
51145
|
placeOf
|
|
50802
51146
|
} = readMap();
|
|
50803
51147
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
50804
|
-
const currentArea = readArea(currentElement);
|
|
51148
|
+
const currentArea = fromArea ?? readArea(currentElement);
|
|
50805
51149
|
let {
|
|
50806
51150
|
x,
|
|
50807
51151
|
y
|
|
@@ -50860,6 +51204,64 @@ const SlideContainer = ({
|
|
|
50860
51204
|
const moveNext = (event, options) => vertical ? move(0, 1, event, options) || move(1, 0, event, options) : move(1, 0, event, options) || move(0, 1, event, options);
|
|
50861
51205
|
const movePrevious = (event, options) => vertical ? move(0, -1, event, options) || move(-1, 0, event, options) : move(-1, 0, event, options) || move(0, -1, event, options);
|
|
50862
51206
|
|
|
51207
|
+
/**
|
|
51208
|
+
* How far the map lets one get towards an area, walking from where the
|
|
51209
|
+
* container stands.
|
|
51210
|
+
*
|
|
51211
|
+
* What the ADDRESS asks for goes through here rather than straight to
|
|
51212
|
+
* goToArea: a URL comes from OUTSIDE the walk — typed, shared, kept from a
|
|
51213
|
+
* session that has moved on — so every slide on the way is asked to let go,
|
|
51214
|
+
* exactly as a hand or a key going that way would ask it, and the first one
|
|
51215
|
+
* that holds is where one stops. Jumped to instead, an address would open the
|
|
51216
|
+
* one screen the walk itself cannot reach: the confirmation of something
|
|
51217
|
+
* nobody sent. Stopping short is not a failure either — a wizard reopens as
|
|
51218
|
+
* far along as it is allowed to, which is where the reader left off.
|
|
51219
|
+
*
|
|
51220
|
+
* @returns {string|undefined} the area one ends up on, undefined for a name
|
|
51221
|
+
* this map does not know.
|
|
51222
|
+
*/
|
|
51223
|
+
const reachableTowards = (fromArea, targetArea) => {
|
|
51224
|
+
const {
|
|
51225
|
+
slideElements,
|
|
51226
|
+
placeOf
|
|
51227
|
+
} = readMap();
|
|
51228
|
+
const target = placeOf.get(targetArea);
|
|
51229
|
+
if (!target || !placeOf.has(fromArea)) {
|
|
51230
|
+
return undefined;
|
|
51231
|
+
}
|
|
51232
|
+
if (loop) {
|
|
51233
|
+
// A window has no walls to walk into: its slides are one endless line,
|
|
51234
|
+
// and what they show is whoever owns the content to place.
|
|
51235
|
+
return targetArea;
|
|
51236
|
+
}
|
|
51237
|
+
const holds = (area, forward) => {
|
|
51238
|
+
const slideElement = slideElements.find(slideElement => readArea(slideElement) === area);
|
|
51239
|
+
return slideElement?.hasAttribute(forward ? "data-prevent-nav-next" : "data-prevent-nav-previous");
|
|
51240
|
+
};
|
|
51241
|
+
let area = fromArea;
|
|
51242
|
+
// A walk crosses each slide at most once — beyond that a map is reading
|
|
51243
|
+
// itself in circles.
|
|
51244
|
+
let stepsLeft = slideElements.length;
|
|
51245
|
+
while (area !== targetArea && stepsLeft--) {
|
|
51246
|
+
const place = placeOf.get(area);
|
|
51247
|
+
// Along the row first, then down: the order a map is read in.
|
|
51248
|
+
const dx = Math.sign(target.x - place.x);
|
|
51249
|
+
const dy = dx ? 0 : Math.sign(target.y - place.y);
|
|
51250
|
+
if (!dx && !dy) {
|
|
51251
|
+
break;
|
|
51252
|
+
}
|
|
51253
|
+
if (holds(area, dx > 0 || dy > 0)) {
|
|
51254
|
+
break;
|
|
51255
|
+
}
|
|
51256
|
+
const next = areaTowards(dx, dy, area);
|
|
51257
|
+
if (!next) {
|
|
51258
|
+
break;
|
|
51259
|
+
}
|
|
51260
|
+
area = next;
|
|
51261
|
+
}
|
|
51262
|
+
return area;
|
|
51263
|
+
};
|
|
51264
|
+
|
|
50863
51265
|
// Where the track is right now, as the gesture left it: the resting place of
|
|
50864
51266
|
// the slide being dragged, plus what the pointer has pulled since.
|
|
50865
51267
|
const paintDrag = () => {
|
|
@@ -50894,10 +51296,93 @@ const SlideContainer = ({
|
|
|
50894
51296
|
const paintTravelToward = area => {
|
|
50895
51297
|
for (const element of travelPainters()) {
|
|
50896
51298
|
if (area) {
|
|
50897
|
-
element.setAttribute(
|
|
51299
|
+
element.setAttribute(SLIDE_TOWARD_ATTRIBUTE, area);
|
|
51300
|
+
} else {
|
|
51301
|
+
element.removeAttribute(SLIDE_TOWARD_ATTRIBUTE);
|
|
51302
|
+
}
|
|
51303
|
+
}
|
|
51304
|
+
// The one fact that changes without a render behind it: under a finger this
|
|
51305
|
+
// is written per frame, so the announcement is left to say whether any of
|
|
51306
|
+
// those writes was a change.
|
|
51307
|
+
announceState();
|
|
51308
|
+
};
|
|
51309
|
+
|
|
51310
|
+
// What a travel would do right now, said in two lists because they are two
|
|
51311
|
+
// different facts: `ways` is where one WOULD go — there is a slide that way
|
|
51312
|
+
// and the one on screen lets go of it — and `held` is where there is a slide
|
|
51313
|
+
// that way and this one holds on to the user (preventNav, a `required` step
|
|
51314
|
+
// still unanswered). A way out leading nowhere is not there; a way out being
|
|
51315
|
+
// held is there and says no, which is why it stays visible and explainable.
|
|
51316
|
+
//
|
|
51317
|
+
// Painted rather than rendered, and on the followers too: only slides go in
|
|
51318
|
+
// this box, so the chevrons, the counters and the bars that go with it are
|
|
51319
|
+
// written AROUND it — in CSS off these attributes
|
|
51320
|
+
// ([data-slide-ways~="right"]), or through useSlideContainer, which watches
|
|
51321
|
+
// them. Nothing is told, everything is read: it is what lets them sit
|
|
51322
|
+
// anywhere, and what lets a container nobody drives still be followed.
|
|
51323
|
+
const paintWays = currentElement => {
|
|
51324
|
+
const ways = [];
|
|
51325
|
+
const held = [];
|
|
51326
|
+
for (const direction of Object.keys(DIRECTIONS)) {
|
|
51327
|
+
const {
|
|
51328
|
+
dx,
|
|
51329
|
+
dy
|
|
51330
|
+
} = DIRECTIONS[direction];
|
|
51331
|
+
if (!mapAxes?.includes(dx ? "x" : "y")) {
|
|
51332
|
+
// Not an axis this map has: a row has no up and no down, and saying so
|
|
51333
|
+
// would be saying it about every row on the page.
|
|
51334
|
+
continue;
|
|
51335
|
+
}
|
|
51336
|
+
if (!areaTowards(dx, dy)) {
|
|
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);
|
|
51342
|
+
}
|
|
51343
|
+
for (const element of travelPainters()) {
|
|
51344
|
+
if (ways.length) {
|
|
51345
|
+
element.setAttribute(SLIDE_WAYS_ATTRIBUTE, ways.join(" "));
|
|
50898
51346
|
} else {
|
|
50899
|
-
element.removeAttribute(
|
|
51347
|
+
element.removeAttribute(SLIDE_WAYS_ATTRIBUTE);
|
|
50900
51348
|
}
|
|
51349
|
+
if (held.length) {
|
|
51350
|
+
element.setAttribute(SLIDE_HELD_ATTRIBUTE, held.join(" "));
|
|
51351
|
+
} else {
|
|
51352
|
+
element.removeAttribute(SLIDE_HELD_ATTRIBUTE);
|
|
51353
|
+
}
|
|
51354
|
+
}
|
|
51355
|
+
};
|
|
51356
|
+
|
|
51357
|
+
// The news, as opposed to the state. Everything painted above IS the state:
|
|
51358
|
+
// it is what anything arriving later reads, and what CSS draws with. What an
|
|
51359
|
+
// attribute cannot say is WHEN it changed — and watching it is the wrong way
|
|
51360
|
+
// to ask, because a write is not a change: `data-slide-travel-toward` is
|
|
51361
|
+
// written on every frame of a gesture with the same value in it, and every
|
|
51362
|
+
// watcher would be woken sixty times a second for nothing. So the box says it
|
|
51363
|
+
// itself, once, and only when something is actually different.
|
|
51364
|
+
//
|
|
51365
|
+
// Said to its followers as well as to itself, exactly as the painting is: a
|
|
51366
|
+
// frame drawn around the box hears what the box is doing without knowing its
|
|
51367
|
+
// id, and a row of tabs beside it listens on the box by id. Not bubbling, like
|
|
51368
|
+
// every other navi event — what is announced is about THIS box, and a box
|
|
51369
|
+
// inside another must not be heard as the one around it.
|
|
51370
|
+
const announcedRef = useRef(null);
|
|
51371
|
+
const announceState = () => {
|
|
51372
|
+
const containerEl = containerRef.current;
|
|
51373
|
+
if (!containerEl) {
|
|
51374
|
+
return;
|
|
51375
|
+
}
|
|
51376
|
+
const state = readSlideContainerState(containerEl);
|
|
51377
|
+
const announced = announcedRef.current;
|
|
51378
|
+
if (announced && sameSlideContainerState(announced, state)) {
|
|
51379
|
+
return;
|
|
51380
|
+
}
|
|
51381
|
+
announcedRef.current = state;
|
|
51382
|
+
for (const element of travelPainters()) {
|
|
51383
|
+
dispatchCustomEvent(element, SLIDE_STATE_EVENT, {
|
|
51384
|
+
...state
|
|
51385
|
+
});
|
|
50901
51386
|
}
|
|
50902
51387
|
};
|
|
50903
51388
|
|
|
@@ -51113,8 +51598,8 @@ const SlideContainer = ({
|
|
|
51113
51598
|
sign,
|
|
51114
51599
|
target
|
|
51115
51600
|
}) => {
|
|
51116
|
-
|
|
51117
|
-
|
|
51601
|
+
let areaBack = axis === "x" ? areaTowards(-1, 0) : areaTowards(0, -1);
|
|
51602
|
+
let areaOn = axis === "x" ? areaTowards(1, 0) : areaTowards(0, 1);
|
|
51118
51603
|
// Everything positional is read HERE rather than when the pointer
|
|
51119
51604
|
// landed: the travel that was playing then may have arrived since, and
|
|
51120
51605
|
// it is what the slides are doing at the moment the gesture takes them
|
|
@@ -51125,6 +51610,24 @@ const SlideContainer = ({
|
|
|
51125
51610
|
placeOf
|
|
51126
51611
|
} = readMap();
|
|
51127
51612
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
51613
|
+
// The hold goToArea reads at the release, read again HERE, off the same
|
|
51614
|
+
// slide and the same attribute: a slide that will refuse the arrival
|
|
51615
|
+
// must not offer the journey. A locked direction simply has nowhere to
|
|
51616
|
+
// go for the length of this gesture — the one case the gesture already
|
|
51617
|
+
// knows, being the last slide of a walk. The hand then gets the wall it
|
|
51618
|
+
// can lean on and never walk through (see drag_to_travel), the slide
|
|
51619
|
+
// behind it stays offstage instead of being read on the way, and the
|
|
51620
|
+
// release has nothing left to refuse.
|
|
51621
|
+
// Nothing here about `released` (--navi-done): that is one particular
|
|
51622
|
+
// departure letting go, decided as it happens, and a gesture armed
|
|
51623
|
+
// before it has no such thing to read — the attribute as rendered is
|
|
51624
|
+
// what the hand is answered from.
|
|
51625
|
+
if (currentElement?.hasAttribute("data-prevent-nav-previous")) {
|
|
51626
|
+
areaBack = undefined;
|
|
51627
|
+
}
|
|
51628
|
+
if (currentElement?.hasAttribute("data-prevent-nav-next")) {
|
|
51629
|
+
areaOn = undefined;
|
|
51630
|
+
}
|
|
51128
51631
|
const box = track.getBoundingClientRect();
|
|
51129
51632
|
if (!areaBack && !areaOn || !currentElement || !box.width || !box.height ||
|
|
51130
51633
|
// Something else with a better claim on the gesture: a scroller
|
|
@@ -51792,7 +52295,14 @@ const SlideMove = ({
|
|
|
51792
52295
|
label
|
|
51793
52296
|
} = DIRECTIONS[direction];
|
|
51794
52297
|
const forward = dx > 0 || dy > 0;
|
|
51795
|
-
|
|
52298
|
+
// The same fact, read from wherever this way out is written. Inside a slide it
|
|
52299
|
+
// comes down as context — the slide holding the user is this button's
|
|
52300
|
+
// ancestor. Written AROUND the box (only slides go in it, so a chevron pinned
|
|
52301
|
+
// to the edge of a full-screen surface has to be), it is not, so it is read
|
|
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.
|
|
52304
|
+
const slides = useSlideContainer(rest.commandFor);
|
|
52305
|
+
const locked = (forward ? locks?.preventNavNext : locks?.preventNavPrevious) || slides.held(direction);
|
|
51796
52306
|
return jsx(SlideNavButton, {
|
|
51797
52307
|
command: command,
|
|
51798
52308
|
locked: locked,
|
|
@@ -54345,6 +54855,7 @@ const css$E = /* css */`
|
|
|
54345
54855
|
}
|
|
54346
54856
|
}
|
|
54347
54857
|
|
|
54858
|
+
${surfaceTextCss}
|
|
54348
54859
|
${popupCss}
|
|
54349
54860
|
`;
|
|
54350
54861
|
|
|
@@ -55842,6 +56353,7 @@ const css$D = /* css */`
|
|
|
55842
56353
|
}
|
|
55843
56354
|
}
|
|
55844
56355
|
|
|
56356
|
+
${surfaceTextCss}
|
|
55845
56357
|
${popupCss}
|
|
55846
56358
|
`;
|
|
55847
56359
|
|
|
@@ -65962,6 +66474,20 @@ installImportMetaCssBuild(import.meta);const css$t = /* css */`
|
|
|
65962
66474
|
pointer-events: none;
|
|
65963
66475
|
user-select: none;
|
|
65964
66476
|
|
|
66477
|
+
/* The value the picker draws itself is the control's own text, at the
|
|
66478
|
+
control's font size: it keeps the control line, snapped to the pixel
|
|
66479
|
+
like the field around it. A caller's "ui" is the caller's own drawing
|
|
66480
|
+
of the control, and it is written on the page's line, as the number,
|
|
66481
|
+
so each text it holds keeps a line relative to its own size. The
|
|
66482
|
+
control line is a length (--navi-control-line-height), and inherited
|
|
66483
|
+
it would arrive as the control's pixels: a 14px label under an 18px
|
|
66484
|
+
picker's 23px line carries ~5px of leading above and below that no
|
|
66485
|
+
glyph occupies and nothing on screen explains. Same reason a popup
|
|
66486
|
+
takes the number — see .navi_popup in popup.jsx. */
|
|
66487
|
+
&[data-picker-facade] {
|
|
66488
|
+
line-height: var(--navi-line-height);
|
|
66489
|
+
}
|
|
66490
|
+
|
|
65965
66491
|
&[navi-placeholder] {
|
|
65966
66492
|
color: var(--picker-placeholder-color);
|
|
65967
66493
|
font-style: var(--picker-placeholder-font-style);
|
|
@@ -66101,6 +66627,16 @@ installImportMetaCssBuild(import.meta);const css$t = /* css */`
|
|
|
66101
66627
|
--x-corner-bottom-left-radius: initial;
|
|
66102
66628
|
|
|
66103
66629
|
display: contents;
|
|
66630
|
+
/* What opens from a control is a page of its own, not part of that
|
|
66631
|
+
control's type scale: it is written at the control font by name,
|
|
66632
|
+
rather than by inheriting the size the picker itself is drawn at. A
|
|
66633
|
+
caller who sizes a picker to the façade it holds (so the padding, the
|
|
66634
|
+
corners and the chevron the picker draws in em land on the text the
|
|
66635
|
+
caller actually wrote) would otherwise take the popup down with it:
|
|
66636
|
+
its title, and everything in it that does not state a size of its
|
|
66637
|
+
own. Same reason the popup is written on the page's line rather than
|
|
66638
|
+
the control's — see .navi_popup in popup.jsx. */
|
|
66639
|
+
font-size: var(--navi-control-font-size);
|
|
66104
66640
|
text-align: initial; /* Don't inherit picker text align */
|
|
66105
66641
|
}
|
|
66106
66642
|
|
|
@@ -66504,6 +67040,12 @@ const PickerButton = props => {
|
|
|
66504
67040
|
}
|
|
66505
67041
|
}), variant === "headless" || ui === "default" ? null : jsx(Text, {
|
|
66506
67042
|
className: "navi_picker_value"
|
|
67043
|
+
// Tells the caller's own drawing of the control from the value
|
|
67044
|
+
// the picker draws itself, so each is written on its own line
|
|
67045
|
+
// (see .navi_picker_value in the CSS above).
|
|
67046
|
+
,
|
|
67047
|
+
|
|
67048
|
+
"data-picker-facade": ui === undefined ? undefined : ""
|
|
66507
67049
|
// A button's label is not a placeholder, however empty the
|
|
66508
67050
|
// picker behind it is.
|
|
66509
67051
|
,
|
|
@@ -78693,8 +79235,8 @@ const StepList = ({
|
|
|
78693
79235
|
}
|
|
78694
79236
|
const rootElement = rootRef.current;
|
|
78695
79237
|
const read = () => {
|
|
78696
|
-
const currentArea = containerElement.getAttribute(
|
|
78697
|
-
const towardArea = containerElement.getAttribute(
|
|
79238
|
+
const currentArea = containerElement.getAttribute(SLIDE_CURRENT_ATTRIBUTE);
|
|
79239
|
+
const towardArea = containerElement.getAttribute(SLIDE_TOWARD_ATTRIBUTE);
|
|
78698
79240
|
setContainerCurrent(currentArea ?? undefined);
|
|
78699
79241
|
const currentIdx = currentArea === null ? -1 : indexOf(currentArea);
|
|
78700
79242
|
if (currentIdx === -1) {
|
|
@@ -78718,13 +79260,12 @@ const StepList = ({
|
|
|
78718
79260
|
rootElement.style.setProperty("--step-list-pos-dx", dx);
|
|
78719
79261
|
};
|
|
78720
79262
|
read();
|
|
78721
|
-
|
|
78722
|
-
|
|
78723
|
-
|
|
78724
|
-
|
|
78725
|
-
});
|
|
79263
|
+
// Read off the container, and re-read when it says something has changed:
|
|
79264
|
+
// a write is not a change, and the attribute the halo follows is written on
|
|
79265
|
+
// every frame of a gesture with the same value in it.
|
|
79266
|
+
containerElement.addEventListener(SLIDE_STATE_EVENT, read);
|
|
78726
79267
|
return () => {
|
|
78727
|
-
|
|
79268
|
+
containerElement.removeEventListener(SLIDE_STATE_EVENT, read);
|
|
78728
79269
|
};
|
|
78729
79270
|
// width: the dots move when the room does, and the written positions are
|
|
78730
79271
|
// pixels of those dots.
|
|
@@ -79555,5 +80096,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
79555
80096
|
})
|
|
79556
80097
|
});
|
|
79557
80098
|
|
|
79558
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
80099
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideContainer, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
79559
80100
|
//# sourceMappingURL=jsenv_navi.js.map
|