@jsenv/navi 0.30.4 → 0.30.5
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/dev/jsenv_navi.js +188 -25
- package/dist/dev/jsenv_navi.js.map +7 -7
- package/dist/jsenv_navi.js +188 -25
- package/dist/jsenv_navi.js.map +7 -7
- package/docs/drag_to_travel.md +3 -1
- package/docs/navigation.md +50 -0
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -36505,9 +36505,23 @@ const collectRoutePages = children => {
|
|
|
36505
36505
|
* Whether the next container down is a row a hand scrolls: every page has a
|
|
36506
36506
|
* place on it from the start, and the browser moves between them (see
|
|
36507
36507
|
* RouteTravel's `nativeScroll`). Told to one container, not to the pages it holds.
|
|
36508
|
+
* False, or `{ renderNeighbour }`.
|
|
36508
36509
|
*/
|
|
36509
36510
|
const RouteRowContext = createContext(false);
|
|
36510
36511
|
|
|
36512
|
+
/**
|
|
36513
|
+
* What stands in the pane of a neighbour until its page does. A component of its
|
|
36514
|
+
* own: what the application calls in there (a hook, a signal) answers to it, however
|
|
36515
|
+
* many neighbours the row has at that moment.
|
|
36516
|
+
*/
|
|
36517
|
+
const RouteRowNeighbour = ({
|
|
36518
|
+
renderNeighbour,
|
|
36519
|
+
page
|
|
36520
|
+
}) => jsx("div", {
|
|
36521
|
+
"data-route-row-neighbour": "",
|
|
36522
|
+
children: renderNeighbour(page)
|
|
36523
|
+
});
|
|
36524
|
+
|
|
36511
36525
|
// A container as a row: one pane per leaf, in the order they are written. A page
|
|
36512
36526
|
// still builds when its route first matches, since a route's actions only ask
|
|
36513
36527
|
// while it does: before that its pane is empty. Once built it stays, with what
|
|
@@ -36520,6 +36534,9 @@ const RouteRowContext = createContext(false);
|
|
|
36520
36534
|
const RouteRowContainer = ({
|
|
36521
36535
|
children
|
|
36522
36536
|
}) => {
|
|
36537
|
+
const {
|
|
36538
|
+
renderNeighbour
|
|
36539
|
+
} = useContext(RouteRowContext);
|
|
36523
36540
|
const {
|
|
36524
36541
|
activeBranch,
|
|
36525
36542
|
leaves
|
|
@@ -36568,16 +36585,19 @@ const RouteRowContainer = ({
|
|
|
36568
36585
|
shown: true
|
|
36569
36586
|
}, "fallback");
|
|
36570
36587
|
}
|
|
36571
|
-
|
|
36588
|
+
const shownIndex = activeBranch ? leaves.findIndex(leaf => leaf.id === activeBranch.id) : -1;
|
|
36589
|
+
return leaves.map((leaf, index) => jsx(RouteRowPane, {
|
|
36572
36590
|
branch: leaf,
|
|
36573
36591
|
page: pageOf(leaf),
|
|
36574
|
-
shown:
|
|
36592
|
+
shown: index === shownIndex,
|
|
36593
|
+
renderNeighbour: shownIndex !== -1 && (index === shownIndex - 1 || index === shownIndex + 1) ? renderNeighbour : undefined
|
|
36575
36594
|
}, leaf.id));
|
|
36576
36595
|
};
|
|
36577
36596
|
const RouteRowPane = ({
|
|
36578
36597
|
branch,
|
|
36579
36598
|
page,
|
|
36580
|
-
shown
|
|
36599
|
+
shown,
|
|
36600
|
+
renderNeighbour
|
|
36581
36601
|
}) => {
|
|
36582
36602
|
// The very vnode rendered last, while not on screen: Preact does not look into
|
|
36583
36603
|
// it, and a page nobody can see costs nothing when the address moves.
|
|
@@ -36596,7 +36616,10 @@ const RouteRowPane = ({
|
|
|
36596
36616
|
"data-route-row-pane": "",
|
|
36597
36617
|
"data-built": contentRef.current ? "" : undefined,
|
|
36598
36618
|
inert: !shown,
|
|
36599
|
-
children: contentRef.current
|
|
36619
|
+
children: contentRef.current || renderNeighbour && jsx(RouteRowNeighbour, {
|
|
36620
|
+
renderNeighbour: renderNeighbour,
|
|
36621
|
+
page: branchRoutePage(branch)
|
|
36622
|
+
})
|
|
36600
36623
|
});
|
|
36601
36624
|
};
|
|
36602
36625
|
|
|
@@ -40412,7 +40435,9 @@ const readRowStretch = (scroller, pages, page) => {
|
|
|
40412
40435
|
/**
|
|
40413
40436
|
* @param {Element} scroller
|
|
40414
40437
|
* @param {object} options
|
|
40415
|
-
* @param {() => void} options.onScroll - the row moved.
|
|
40438
|
+
* @param {(move: {finger: boolean}) => void} options.onScroll - the row moved.
|
|
40439
|
+
* `finger`: a touch has come since the row last stopped, so a lift will say where
|
|
40440
|
+
* it goes (`onLeftToSnap`). Without one (a trackpad, a wheel) nothing will.
|
|
40416
40441
|
* @param {(event: Event|null) => void} options.onLand - the row may have stopped.
|
|
40417
40442
|
* Read `pageAtRest` to know whether it stopped on a page. Heard more than once
|
|
40418
40443
|
* for one landing, so what it does must find nothing to do the second time.
|
|
@@ -40428,11 +40453,13 @@ const watchRowScroll = (
|
|
|
40428
40453
|
) => {
|
|
40429
40454
|
let stoppedTimeout = null;
|
|
40430
40455
|
let touching = false;
|
|
40456
|
+
let touchedSinceStop = false;
|
|
40431
40457
|
let liftedAt = null;
|
|
40432
40458
|
const landOnSilence = () => {
|
|
40433
40459
|
liftedAt = null;
|
|
40434
40460
|
// A finger holding the row still: nothing has stopped.
|
|
40435
40461
|
if (!touching) {
|
|
40462
|
+
touchedSinceStop = false;
|
|
40436
40463
|
onLand(null);
|
|
40437
40464
|
}
|
|
40438
40465
|
};
|
|
@@ -40441,7 +40468,7 @@ const watchRowScroll = (
|
|
|
40441
40468
|
stoppedTimeout = setTimeout(landOnSilence, STOPPED_SILENCE);
|
|
40442
40469
|
};
|
|
40443
40470
|
const onScrollEvent = () => {
|
|
40444
|
-
onScroll();
|
|
40471
|
+
onScroll({ finger: touchedSinceStop });
|
|
40445
40472
|
armSilence();
|
|
40446
40473
|
if (!liftedAt) {
|
|
40447
40474
|
return;
|
|
@@ -40457,6 +40484,7 @@ const watchRowScroll = (
|
|
|
40457
40484
|
};
|
|
40458
40485
|
const onTouchStart = () => {
|
|
40459
40486
|
touching = true;
|
|
40487
|
+
touchedSinceStop = true;
|
|
40460
40488
|
liftedAt = null;
|
|
40461
40489
|
clearTimeout(stoppedTimeout);
|
|
40462
40490
|
};
|
|
@@ -40484,6 +40512,8 @@ const watchRowScroll = (
|
|
|
40484
40512
|
|
|
40485
40513
|
installImportMetaCssBuild(import.meta);
|
|
40486
40514
|
const css$16 = /* css */`.navi_route_travel {
|
|
40515
|
+
flex-direction: column;
|
|
40516
|
+
display: flex;
|
|
40487
40517
|
position: relative;
|
|
40488
40518
|
}
|
|
40489
40519
|
|
|
@@ -40535,6 +40565,15 @@ const css$16 = /* css */`.navi_route_travel {
|
|
|
40535
40565
|
overflow: clip;
|
|
40536
40566
|
}
|
|
40537
40567
|
|
|
40568
|
+
& > [data-route-row-pane] > [data-route-row-neighbour] {
|
|
40569
|
+
contain: layout paint;
|
|
40570
|
+
visibility: visible;
|
|
40571
|
+
flex-direction: column;
|
|
40572
|
+
display: flex;
|
|
40573
|
+
position: absolute;
|
|
40574
|
+
inset: 0;
|
|
40575
|
+
}
|
|
40576
|
+
|
|
40538
40577
|
& > [data-route-row-live] {
|
|
40539
40578
|
flex-direction: column;
|
|
40540
40579
|
grid-row: 1;
|
|
@@ -40567,6 +40606,7 @@ const SET_OFF_PULL = 16;
|
|
|
40567
40606
|
* routes?: Array<object|{route: object, params?: object}>,
|
|
40568
40607
|
* travelByDrag?: boolean,
|
|
40569
40608
|
* onTravel?: (detail: {route: object, params: object|undefined, cause: string}) => void|Promise<void>,
|
|
40609
|
+
* renderNeighbour?: (page: {route: object, params: object|undefined}) => import("preact").ComponentChildren,
|
|
40570
40610
|
* }>}
|
|
40571
40611
|
* @param {Array<object|{route: object, params?: object}>} [props.routes] - a window
|
|
40572
40612
|
* over something longer, in order. Read from the <Route> children by default,
|
|
@@ -40583,6 +40623,13 @@ const SET_OFF_PULL = 16;
|
|
|
40583
40623
|
* of the page under six entries. A tab pressed goes the way its <Link> says —
|
|
40584
40624
|
* `<Link replace>` for a row of tabs, so a press and a swipe towards the same
|
|
40585
40625
|
* neighbour write the same history.
|
|
40626
|
+
* @param {(page: {route: object, params: object|undefined}) => import("preact").ComponentChildren} [props.renderNeighbour]
|
|
40627
|
+
* - what stands in the pane of a page next to the one on screen, until the hand
|
|
40628
|
+
* has pulled far enough to bring that page: its background, its skeleton, what the
|
|
40629
|
+
* list already knows. A `<Route>` page already built shows itself. Receives
|
|
40630
|
+
* `{ route, params }`, or the entry of `routes` as it was given, and is a column
|
|
40631
|
+
* the height of the row, as the page is. Its route does not match while it stands
|
|
40632
|
+
* there: what it reads is not asked for.
|
|
40586
40633
|
*
|
|
40587
40634
|
* Every other prop lands on the box itself (`id`, `data-testid`, `className`, …).
|
|
40588
40635
|
* The box has to outlive every page of its row: around those <Route>s, or around
|
|
@@ -40598,6 +40645,7 @@ const RouteTravel = ({
|
|
|
40598
40645
|
replace: true,
|
|
40599
40646
|
routeTransition: "none"
|
|
40600
40647
|
}),
|
|
40648
|
+
renderNeighbour,
|
|
40601
40649
|
className,
|
|
40602
40650
|
children,
|
|
40603
40651
|
...rest
|
|
@@ -40658,9 +40706,7 @@ const RouteTravel = ({
|
|
|
40658
40706
|
travelling: false,
|
|
40659
40707
|
pictures: new Map(),
|
|
40660
40708
|
resumeScrollRecording: null,
|
|
40661
|
-
scrollsDocument:
|
|
40662
|
-
includeHidden: true
|
|
40663
|
-
}) === document.documentElement,
|
|
40709
|
+
scrollsDocument: rowFollowsDocument(row),
|
|
40664
40710
|
...fields
|
|
40665
40711
|
};
|
|
40666
40712
|
movementRef.current = movement;
|
|
@@ -40996,6 +41042,30 @@ const RouteTravel = ({
|
|
|
40996
41042
|
Promise.resolve().then(endMovement);
|
|
40997
41043
|
}
|
|
40998
41044
|
}, [currentIndex]);
|
|
41045
|
+
|
|
41046
|
+
// A neighbour shows from the first frame the compositor moves the row, before
|
|
41047
|
+
// any scroll event: a finger landing is the last word before that.
|
|
41048
|
+
const paintsNeighbours = Boolean(renderNeighbour);
|
|
41049
|
+
useLayoutEffect(() => {
|
|
41050
|
+
if (!paintsNeighbours) {
|
|
41051
|
+
return undefined;
|
|
41052
|
+
}
|
|
41053
|
+
const row = rowRef.current;
|
|
41054
|
+
const onTouchStart = () => {
|
|
41055
|
+
if (!movementRef.current && rowFollowsDocument(row)) {
|
|
41056
|
+
row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
|
|
41057
|
+
}
|
|
41058
|
+
};
|
|
41059
|
+
row.addEventListener("touchstart", onTouchStart, {
|
|
41060
|
+
passive: true
|
|
41061
|
+
});
|
|
41062
|
+
return () => {
|
|
41063
|
+
row.removeEventListener("touchstart", onTouchStart);
|
|
41064
|
+
};
|
|
41065
|
+
}, [paintsNeighbours]);
|
|
41066
|
+
const rowContext = useMemo(() => ({
|
|
41067
|
+
renderNeighbour
|
|
41068
|
+
}), [renderNeighbour]);
|
|
40999
41069
|
const rowKind = pages.length < 2 && windowed ? "still" : travelByDrag ? "" : "code";
|
|
41000
41070
|
return jsx("div", {
|
|
41001
41071
|
...rest,
|
|
@@ -41008,12 +41078,20 @@ const RouteTravel = ({
|
|
|
41008
41078
|
,
|
|
41009
41079
|
"data-travel-by-drag": travelByDrag && rowKind !== "still" ? "x" : undefined,
|
|
41010
41080
|
children: windowed ? jsxs(Fragment, {
|
|
41011
|
-
children: [pages.map((page, index) =>
|
|
41012
|
-
|
|
41013
|
-
|
|
41014
|
-
|
|
41015
|
-
|
|
41016
|
-
|
|
41081
|
+
children: [pages.map((page, index) => {
|
|
41082
|
+
const holdsNeighbour = paintsNeighbours && isNeighbourPane(index, liveIndex, movementRef.current);
|
|
41083
|
+
return jsx("div", {
|
|
41084
|
+
"data-route-row-pane": "",
|
|
41085
|
+
inert: holdsNeighbour,
|
|
41086
|
+
style: {
|
|
41087
|
+
gridColumn: index + 1
|
|
41088
|
+
},
|
|
41089
|
+
children: holdsNeighbour && jsx(RouteRowNeighbour, {
|
|
41090
|
+
renderNeighbour: renderNeighbour,
|
|
41091
|
+
page: page
|
|
41092
|
+
})
|
|
41093
|
+
}, index);
|
|
41094
|
+
}), jsx("div", {
|
|
41017
41095
|
ref: liveRef,
|
|
41018
41096
|
"data-route-row-live": "",
|
|
41019
41097
|
style: {
|
|
@@ -41025,13 +41103,25 @@ const RouteTravel = ({
|
|
|
41025
41103
|
"data-route-row-pictures": ""
|
|
41026
41104
|
})]
|
|
41027
41105
|
}) : jsx(RouteRowContext.Provider, {
|
|
41028
|
-
value:
|
|
41106
|
+
value: rowContext,
|
|
41029
41107
|
children: children
|
|
41030
41108
|
})
|
|
41031
41109
|
})
|
|
41032
41110
|
});
|
|
41033
41111
|
};
|
|
41034
41112
|
|
|
41113
|
+
// The panes on each side of the live page. Not one holding a picture of the page
|
|
41114
|
+
// left there: a page with no background of its own would show it through.
|
|
41115
|
+
const isNeighbourPane = (index, liveIndex, movement) => {
|
|
41116
|
+
if (index !== liveIndex - 1 && index !== liveIndex + 1) {
|
|
41117
|
+
return false;
|
|
41118
|
+
}
|
|
41119
|
+
return !movement || !movement.pictures.has(index);
|
|
41120
|
+
};
|
|
41121
|
+
const rowFollowsDocument = row => getScrollContainer(row.parentElement, {
|
|
41122
|
+
includeHidden: true
|
|
41123
|
+
}) === document.documentElement;
|
|
41124
|
+
|
|
41035
41125
|
// How far down the row the reader is looking, for a page brought in: a page on
|
|
41036
41126
|
// screen scrolls with the document, and one arriving has to start from there.
|
|
41037
41127
|
const pinOf = row => {
|
|
@@ -56513,6 +56603,10 @@ const SLIDE_TOWARD_ATTRIBUTE = "data-slide-travel-toward";
|
|
|
56513
56603
|
// one: a hand on a row the browser scrolls can carry it past several slides, and
|
|
56514
56604
|
// what follows it goes on between the two it is standing between.
|
|
56515
56605
|
const SLIDE_BASE_ATTRIBUTE = "data-slide-travel-base";
|
|
56606
|
+
// The slide the row is on its way to while the current one has not changed yet,
|
|
56607
|
+
// because that only happens at rest: nothing is rendered and no address is written
|
|
56608
|
+
// under a snap, and what says where one is should not wait for it.
|
|
56609
|
+
const SLIDE_HEADING_ATTRIBUTE = "data-slide-heading";
|
|
56516
56610
|
// Every way out that would DO something right now, in the words the commands
|
|
56517
56611
|
// use: a direction ("left", "right", "up", "down") when there is a slide that
|
|
56518
56612
|
// way, plus "first" and "last" when one is not already standing at that end of
|
|
@@ -56547,9 +56641,16 @@ const wordsOf = (element, attribute) => {
|
|
|
56547
56641
|
return value ? value.split(" ") : NO_WAYS;
|
|
56548
56642
|
};
|
|
56549
56643
|
|
|
56644
|
+
// The slide to say one is on: for whatever tells the reader where they are, as
|
|
56645
|
+
// opposed to data-slide-current, which is what a travel departs from.
|
|
56646
|
+
const readSlideOnScreen = (element) =>
|
|
56647
|
+
element.getAttribute(SLIDE_HEADING_ATTRIBUTE) ??
|
|
56648
|
+
element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ??
|
|
56649
|
+
undefined;
|
|
56650
|
+
|
|
56550
56651
|
const readSlideContainerState = (element) => ({
|
|
56551
56652
|
known: true,
|
|
56552
|
-
current: element
|
|
56653
|
+
current: readSlideOnScreen(element),
|
|
56553
56654
|
toward: element.getAttribute(SLIDE_TOWARD_ATTRIBUTE) ?? undefined,
|
|
56554
56655
|
// In DOM order — which is the order of the WALK for a line, and not
|
|
56555
56656
|
// necessarily for a map: there the order is the one the areas are written in
|
|
@@ -56587,11 +56688,14 @@ const sameSlideContainerState = (a, b) =>
|
|
|
56587
56688
|
* can: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
|
|
56588
56689
|
* held: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
|
|
56589
56690
|
* }} where the box stands. `current` is the slide on screen — the one being
|
|
56590
|
-
* travelled TO while a travel plays,
|
|
56691
|
+
* travelled TO while a travel plays, and the one the row is going to once a hand
|
|
56692
|
+
* has let go, because that is what one is looking at. It can come before the
|
|
56693
|
+
* slide's own signal and `onCurrentChange`, which wait for the row to rest.
|
|
56591
56694
|
* `toward` is the other slide in the frame while the picture is between two,
|
|
56592
|
-
* and nothing at rest. `can` is "asking for it would do something",
|
|
56593
|
-
*
|
|
56594
|
-
* only the second is worth a word to
|
|
56695
|
+
* and nothing at rest. `can` is "asking for it would do something", from the
|
|
56696
|
+
* slide a travel would depart from, `held` is "it is there and this slide says
|
|
56697
|
+
* no" — a way out is dead in both cases, and only the second is worth a word to
|
|
56698
|
+
* the reader.
|
|
56595
56699
|
* `known` is false until the box has been read: no box was named and none is
|
|
56596
56700
|
* above, the id names nothing, or — for one commit — this mounted before the
|
|
56597
56701
|
* box had painted. Until then `can` answers YES, because it is the answer that
|
|
@@ -57445,7 +57549,7 @@ const Nav = ({
|
|
|
57445
57549
|
}
|
|
57446
57550
|
slideContainerElementRef.current = containerElement;
|
|
57447
57551
|
const readContainer = () => {
|
|
57448
|
-
setCurrentSlideArea(containerElement
|
|
57552
|
+
setCurrentSlideArea(readSlideOnScreen(containerElement));
|
|
57449
57553
|
paintIndicatorGeometryRef.current();
|
|
57450
57554
|
scheduleTabScrollSyncRef.current();
|
|
57451
57555
|
};
|
|
@@ -68444,6 +68548,20 @@ const SlideContainer = ({
|
|
|
68444
68548
|
const paintCurrentArea = area => {
|
|
68445
68549
|
containerRef.current?.setAttribute(SLIDE_CURRENT_ATTRIBUTE, area);
|
|
68446
68550
|
};
|
|
68551
|
+
|
|
68552
|
+
// Where the row is going, for as long as the current slide has not changed:
|
|
68553
|
+
// anything saying where one is reads this first (see readSlideOnScreen). Left to
|
|
68554
|
+
// the caller to announce, a landing having more to say in the same breath.
|
|
68555
|
+
const writeHeading = headingElement => {
|
|
68556
|
+
const containerEl = containerRef.current;
|
|
68557
|
+
const area = headingElement ? readArea(headingElement) : null;
|
|
68558
|
+
// A window does not change slide, it rolls: it has nowhere to be heading.
|
|
68559
|
+
if (loop || area === null || area === containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE)) {
|
|
68560
|
+
containerEl.removeAttribute(SLIDE_HEADING_ATTRIBUTE);
|
|
68561
|
+
return;
|
|
68562
|
+
}
|
|
68563
|
+
containerEl.setAttribute(SLIDE_HEADING_ATTRIBUTE, area);
|
|
68564
|
+
};
|
|
68447
68565
|
const markAnswered = area => {
|
|
68448
68566
|
const order = readMap().slideElements.map(readArea);
|
|
68449
68567
|
const rank = order.indexOf(area);
|
|
@@ -68649,6 +68767,10 @@ const SlideContainer = ({
|
|
|
68649
68767
|
slideElements[0];
|
|
68650
68768
|
const currentArea = readArea(currentElement);
|
|
68651
68769
|
paintCurrentArea(currentArea);
|
|
68770
|
+
// In the commit the slide changes in, so nothing reads the slide left in between.
|
|
68771
|
+
if (!handMovingRef.current) {
|
|
68772
|
+
writeHeading(null);
|
|
68773
|
+
}
|
|
68652
68774
|
const realPlaceOf = area => placeOf.get(area) || {
|
|
68653
68775
|
x: 0,
|
|
68654
68776
|
y: 0
|
|
@@ -68681,6 +68803,8 @@ const SlideContainer = ({
|
|
|
68681
68803
|
const drawnArea = stage ? stage.area : drawnAreaRef.current;
|
|
68682
68804
|
const travelStarts = !still && durationMs > 0 && drawnArea !== undefined && drawnArea !== currentArea && slideElements.some(slideElement => readArea(slideElement) === drawnArea);
|
|
68683
68805
|
if (travelStarts) {
|
|
68806
|
+
// Asked for over a row still snapping: the travel is what one will see.
|
|
68807
|
+
writeHeading(null);
|
|
68684
68808
|
// Where the slide being left IS — the place the stage gave it if a travel
|
|
68685
68809
|
// was already playing (a press landing mid-travel departs from where the
|
|
68686
68810
|
// eye is, not from the map), its own place otherwise.
|
|
@@ -69447,7 +69571,9 @@ const SlideContainer = ({
|
|
|
69447
69571
|
// Where a hand has the row, in the terms of --slide-travel-progress: from the
|
|
69448
69572
|
// current slide while it stands next to it, and past that between the two it
|
|
69449
69573
|
// stands between, as far as the hand carries it.
|
|
69450
|
-
const paintNativeRow = (
|
|
69574
|
+
const paintNativeRow = ({
|
|
69575
|
+
finger
|
|
69576
|
+
}) => {
|
|
69451
69577
|
if (trackAnimationRef.current?.playState === "running") {
|
|
69452
69578
|
return;
|
|
69453
69579
|
}
|
|
@@ -69459,6 +69585,9 @@ const SlideContainer = ({
|
|
|
69459
69585
|
}
|
|
69460
69586
|
if (restsOn(containerEl, currentElement)) {
|
|
69461
69587
|
if (handMovingRef.current) {
|
|
69588
|
+
if (!finger) {
|
|
69589
|
+
writeHeading(null);
|
|
69590
|
+
}
|
|
69462
69591
|
paintTravelProgress();
|
|
69463
69592
|
}
|
|
69464
69593
|
return;
|
|
@@ -69479,6 +69608,12 @@ const SlideContainer = ({
|
|
|
69479
69608
|
}
|
|
69480
69609
|
element.style.setProperty("--slide-travel-progress", forward ? -fraction : fraction);
|
|
69481
69610
|
}
|
|
69611
|
+
// Nothing will say when a trackpad's fingers leave, and a long stroke would
|
|
69612
|
+
// keep the first tab lit for the whole trip: the nearest slide answers. A finger
|
|
69613
|
+
// has a lift to wait for (headNativeRow), and until then the slide held stands.
|
|
69614
|
+
if (!finger) {
|
|
69615
|
+
writeHeading(fraction >= 0.5 && toward ? toward : base);
|
|
69616
|
+
}
|
|
69482
69617
|
paintTravelToward(toward ? readArea(toward) : null);
|
|
69483
69618
|
// What the hand reaches ahead of the neighbours built at rest is asked for on
|
|
69484
69619
|
// the way: an empty slide goes by otherwise.
|
|
@@ -69486,6 +69621,28 @@ const SlideContainer = ({
|
|
|
69486
69621
|
alongside: true
|
|
69487
69622
|
});
|
|
69488
69623
|
};
|
|
69624
|
+
|
|
69625
|
+
// A finger has lifted and the row moves: on past the slide it stands after, or
|
|
69626
|
+
// back to it. Already standing on one, it has arrived, whichever way it came.
|
|
69627
|
+
const headNativeRow = movedForward => {
|
|
69628
|
+
if (!handMovingRef.current || trackAnimationRef.current?.playState === "running") {
|
|
69629
|
+
return;
|
|
69630
|
+
}
|
|
69631
|
+
const slideElements = Array.from(trackRef.current.children);
|
|
69632
|
+
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
|
|
69633
|
+
if (!currentElement) {
|
|
69634
|
+
return;
|
|
69635
|
+
}
|
|
69636
|
+
const {
|
|
69637
|
+
base,
|
|
69638
|
+
toward,
|
|
69639
|
+
fraction,
|
|
69640
|
+
forward
|
|
69641
|
+
} = readRowStretch(containerRef.current, slideElements, currentElement);
|
|
69642
|
+
const goesOn = fraction > 0 && movedForward === forward;
|
|
69643
|
+
writeHeading(goesOn && toward ? toward : base);
|
|
69644
|
+
announceState();
|
|
69645
|
+
};
|
|
69489
69646
|
const landNativeRow = scrollEndEvent => {
|
|
69490
69647
|
if (!handMovingRef.current) {
|
|
69491
69648
|
return;
|
|
@@ -69500,13 +69657,15 @@ const SlideContainer = ({
|
|
|
69500
69657
|
return;
|
|
69501
69658
|
}
|
|
69502
69659
|
handMovingRef.current = false;
|
|
69503
|
-
paintTravelProgress();
|
|
69504
69660
|
const area = readArea(landedElement);
|
|
69505
69661
|
const currentArea = containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE);
|
|
69506
69662
|
if (area === currentArea) {
|
|
69663
|
+
writeHeading(null);
|
|
69664
|
+
paintTravelProgress();
|
|
69507
69665
|
settleTravel();
|
|
69508
69666
|
return;
|
|
69509
69667
|
}
|
|
69668
|
+
paintTravelProgress();
|
|
69510
69669
|
const event = scrollEndEvent ?? new Event("scrollend");
|
|
69511
69670
|
if (loop) {
|
|
69512
69671
|
// A window does not change slide, it rolls: back at rest in the render the
|
|
@@ -69537,6 +69696,8 @@ const SlideContainer = ({
|
|
|
69537
69696
|
}
|
|
69538
69697
|
// Refused: the row is put back in view of the slide that stays.
|
|
69539
69698
|
handLandedRef.current = null;
|
|
69699
|
+
writeHeading(null);
|
|
69700
|
+
announceState();
|
|
69540
69701
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
|
|
69541
69702
|
containerEl.scrollTo({
|
|
69542
69703
|
left: currentElement.offsetLeft,
|
|
@@ -69550,11 +69711,13 @@ const SlideContainer = ({
|
|
|
69550
69711
|
const nativeRowHandlersRef = useRef(null);
|
|
69551
69712
|
nativeRowHandlersRef.current = {
|
|
69552
69713
|
paintNativeRow,
|
|
69714
|
+
headNativeRow,
|
|
69553
69715
|
landNativeRow
|
|
69554
69716
|
};
|
|
69555
69717
|
useLayoutEffect(() => {
|
|
69556
69718
|
return watchRowScroll(containerRef.current, {
|
|
69557
|
-
onScroll:
|
|
69719
|
+
onScroll: move => nativeRowHandlersRef.current.paintNativeRow(move),
|
|
69720
|
+
onLeftToSnap: forward => nativeRowHandlersRef.current.headNativeRow(forward),
|
|
69558
69721
|
onLand: event => nativeRowHandlersRef.current.landNativeRow(event)
|
|
69559
69722
|
});
|
|
69560
69723
|
}, []);
|