@jsenv/navi 0.30.30 → 0.30.32
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 +244 -116
- package/dist/dev/jsenv_navi.js.map +7 -5
- package/dist/jsenv_navi.js +244 -116
- package/dist/jsenv_navi.js.map +7 -5
- package/docs/drag_to_travel.md +4 -4
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -38156,6 +38156,28 @@ const pageIsCurrent = ({ route, params }) => {
|
|
|
38156
38156
|
return params ? route.matchesParams(params) : true;
|
|
38157
38157
|
};
|
|
38158
38158
|
|
|
38159
|
+
// The same question about an address the router does not have yet (a navigation
|
|
38160
|
+
// about to be routed, a link pressed): its path, and every param the page names at
|
|
38161
|
+
// the value the page gives it. The same path under another search param is another
|
|
38162
|
+
// page — a person opened from another list — and a param the page does not name is
|
|
38163
|
+
// state on top of it, as it is for matchesParams.
|
|
38164
|
+
const pageIsAt = ({ route, params }, url) => {
|
|
38165
|
+
const destination = new URL(url, window.location.href);
|
|
38166
|
+
const own = new URL(route.buildUrl(params), window.location.href);
|
|
38167
|
+
if (own.pathname !== destination.pathname) {
|
|
38168
|
+
return false;
|
|
38169
|
+
}
|
|
38170
|
+
if (!params) {
|
|
38171
|
+
return true;
|
|
38172
|
+
}
|
|
38173
|
+
for (const name of Object.keys(params)) {
|
|
38174
|
+
if (own.searchParams.get(name) !== destination.searchParams.get(name)) {
|
|
38175
|
+
return false;
|
|
38176
|
+
}
|
|
38177
|
+
}
|
|
38178
|
+
return true;
|
|
38179
|
+
};
|
|
38180
|
+
|
|
38159
38181
|
/**
|
|
38160
38182
|
* The page a <Route> container shows when none of its branches matched, given
|
|
38161
38183
|
* a name.
|
|
@@ -40391,6 +40413,45 @@ const readBand = () => {
|
|
|
40391
40413
|
};
|
|
40392
40414
|
};
|
|
40393
40415
|
|
|
40416
|
+
/**
|
|
40417
|
+
* CSS durations and easing curves, for a movement played from JS that has to look
|
|
40418
|
+
* like one played by CSS: a row scrolled frame by frame next to a track animated by
|
|
40419
|
+
* the browser, a view transition walked back from where its curve has it.
|
|
40420
|
+
*/
|
|
40421
|
+
|
|
40422
|
+
// A CSS duration read as a number of milliseconds.
|
|
40423
|
+
const durationToMs = (duration) => {
|
|
40424
|
+
const number = parseFloat(duration);
|
|
40425
|
+
if (Number.isNaN(number)) {
|
|
40426
|
+
return 0;
|
|
40427
|
+
}
|
|
40428
|
+
return String(duration).trimEnd().endsWith("ms") ? number : number * 1000;
|
|
40429
|
+
};
|
|
40430
|
+
|
|
40431
|
+
const CSS_EASE = [0.25, 0.1, 0.25, 1];
|
|
40432
|
+
const CSS_EASE_OUT = [0, 0, 0.58, 1];
|
|
40433
|
+
|
|
40434
|
+
// A cubic-bezier curve, evaluated: time in, distance out. Solved numerically
|
|
40435
|
+
// because the curve is parametric — two cubics sharing a parameter, with no closed
|
|
40436
|
+
// form for one against the other. Twenty halvings put the answer well under a pixel
|
|
40437
|
+
// of a screen-wide movement.
|
|
40438
|
+
const easedProgress = (x, [x1, y1, x2, y2]) => {
|
|
40439
|
+
let low = 0;
|
|
40440
|
+
let high = 1;
|
|
40441
|
+
for (let i = 0; i < 20; i++) {
|
|
40442
|
+
const mid = (low + high) / 2;
|
|
40443
|
+
if (bezierAxis(mid, x1, x2) < x) {
|
|
40444
|
+
low = mid;
|
|
40445
|
+
} else {
|
|
40446
|
+
high = mid;
|
|
40447
|
+
}
|
|
40448
|
+
}
|
|
40449
|
+
return bezierAxis((low + high) / 2, y1, y2);
|
|
40450
|
+
};
|
|
40451
|
+
|
|
40452
|
+
const bezierAxis = (s, a, b) =>
|
|
40453
|
+
3 * (1 - s) * (1 - s) * s * a + 3 * (1 - s) * s * s * b + s * s * s;
|
|
40454
|
+
|
|
40394
40455
|
/**
|
|
40395
40456
|
* A view transition already playing, walked back to where it started.
|
|
40396
40457
|
*
|
|
@@ -40420,6 +40481,7 @@ const readBand = () => {
|
|
|
40420
40481
|
* there while the clock counts down.
|
|
40421
40482
|
*/
|
|
40422
40483
|
|
|
40484
|
+
|
|
40423
40485
|
const viewTransitionAnimations = () => {
|
|
40424
40486
|
const animations = [];
|
|
40425
40487
|
for (const animation of document.getAnimations()) {
|
|
@@ -40544,27 +40606,6 @@ const whenPicturesArrived = (animations) => {
|
|
|
40544
40606
|
);
|
|
40545
40607
|
};
|
|
40546
40608
|
|
|
40547
|
-
// CSS `ease`, evaluated: time in, distance out. Solved numerically because the
|
|
40548
|
-
// curve is parametric — two cubics sharing a parameter, with no closed form for
|
|
40549
|
-
// one against the other. Twenty halvings put the answer well under a pixel of a
|
|
40550
|
-
// screen-wide movement.
|
|
40551
|
-
const CSS_EASE = [0.25, 0.1, 0.25, 1];
|
|
40552
|
-
const bezierAxis = (s, a, b) =>
|
|
40553
|
-
3 * (1 - s) * (1 - s) * s * a + 3 * (1 - s) * s * s * b + s * s * s;
|
|
40554
|
-
const easedProgress = (x, [x1, y1, x2, y2]) => {
|
|
40555
|
-
let low = 0;
|
|
40556
|
-
let high = 1;
|
|
40557
|
-
for (let i = 0; i < 20; i++) {
|
|
40558
|
-
const mid = (low + high) / 2;
|
|
40559
|
-
if (bezierAxis(mid, x1, x2) < x) {
|
|
40560
|
-
low = mid;
|
|
40561
|
-
} else {
|
|
40562
|
-
high = mid;
|
|
40563
|
-
}
|
|
40564
|
-
}
|
|
40565
|
-
return bezierAxis((low + high) / 2, y1, y2);
|
|
40566
|
-
};
|
|
40567
|
-
|
|
40568
40609
|
installImportMetaCssBuild(import.meta);
|
|
40569
40610
|
const startViewTransition = ensureDocumentStartViewTransition();
|
|
40570
40611
|
const TRANSITION_ATTRIBUTE = "data-navi-route-transition";
|
|
@@ -42594,11 +42635,11 @@ const css$16 = /* css */`.navi_route_travel {
|
|
|
42594
42635
|
overflow: visible;
|
|
42595
42636
|
}
|
|
42596
42637
|
|
|
42597
|
-
&[data-route-row-
|
|
42638
|
+
&[data-route-row-travelling] {
|
|
42598
42639
|
scroll-snap-type: none;
|
|
42599
42640
|
}
|
|
42600
42641
|
|
|
42601
|
-
&[data-route-row-taken
|
|
42642
|
+
&[data-route-row-taken] {
|
|
42602
42643
|
overflow: clip;
|
|
42603
42644
|
|
|
42604
42645
|
& > [data-route-row-pane], & > [data-route-row-live], & > [data-route-row-pictures] > * {
|
|
@@ -42617,12 +42658,9 @@ const css$16 = /* css */`.navi_route_travel {
|
|
|
42617
42658
|
display: flex;
|
|
42618
42659
|
}
|
|
42619
42660
|
|
|
42620
|
-
&:not([data-moving]) > [data-route-row-pane][inert] {
|
|
42621
|
-
visibility: hidden;
|
|
42622
|
-
}
|
|
42623
|
-
|
|
42624
42661
|
& > [data-route-row-pane][inert] {
|
|
42625
42662
|
top: var(--navi-route-row-pin, 0);
|
|
42663
|
+
contain: paint;
|
|
42626
42664
|
max-height: 100dvh;
|
|
42627
42665
|
position: relative;
|
|
42628
42666
|
overflow: clip;
|
|
@@ -42630,7 +42668,6 @@ const css$16 = /* css */`.navi_route_travel {
|
|
|
42630
42668
|
|
|
42631
42669
|
& > [data-route-row-pane] > [data-route-row-neighbour] {
|
|
42632
42670
|
contain: layout paint;
|
|
42633
|
-
visibility: visible;
|
|
42634
42671
|
flex-direction: column;
|
|
42635
42672
|
display: flex;
|
|
42636
42673
|
position: absolute;
|
|
@@ -42668,6 +42705,9 @@ const SET_OFF_PULL = 16;
|
|
|
42668
42705
|
const ROW_READING = {
|
|
42669
42706
|
sideways: true
|
|
42670
42707
|
};
|
|
42708
|
+
// How far a scroll position written can read back from what was written: rounded to
|
|
42709
|
+
// the device's pixels.
|
|
42710
|
+
const WRITE_ROUNDING = 1;
|
|
42671
42711
|
|
|
42672
42712
|
/**
|
|
42673
42713
|
* @type {import("preact").FunctionComponent<{
|
|
@@ -42676,6 +42716,7 @@ const ROW_READING = {
|
|
|
42676
42716
|
* onTravel?: (detail: {route: object, params: object|undefined, cause: string}) => void|Promise<void>,
|
|
42677
42717
|
* renderNeighbour?: (page: {route: object, params: object|undefined}) => import("preact").ComponentChildren,
|
|
42678
42718
|
* prefetch?: boolean,
|
|
42719
|
+
* duration?: string,
|
|
42679
42720
|
* }>}
|
|
42680
42721
|
* @param {Array<object|{route: object, params?: object}>} [props.routes] - a window
|
|
42681
42722
|
* over something longer, in order. Read from the <Route> children by default,
|
|
@@ -42705,6 +42746,9 @@ const ROW_READING = {
|
|
|
42705
42746
|
* `route.preload(params)`. A swipe then builds a page whose answers are already
|
|
42706
42747
|
* there, with no wait to show. Off by default: every page one stops on costs the
|
|
42707
42748
|
* reads of two more.
|
|
42749
|
+
* @param {string} [props.duration="300ms"] - how long a travel asked for takes: a
|
|
42750
|
+
* tab pressed, the back button. One box, on the curve a SlideContainer plays, so
|
|
42751
|
+
* two rows of tabs in one app move alike. A hand goes at its own pace.
|
|
42708
42752
|
*
|
|
42709
42753
|
* Every other prop lands on the box itself (`id`, `data-testid`, `className`, …).
|
|
42710
42754
|
* The box has to outlive every page of its row: around those <Route>s, or around
|
|
@@ -42722,6 +42766,7 @@ const RouteTravel = ({
|
|
|
42722
42766
|
}),
|
|
42723
42767
|
renderNeighbour,
|
|
42724
42768
|
prefetch = false,
|
|
42769
|
+
duration = "300ms",
|
|
42725
42770
|
className,
|
|
42726
42771
|
children,
|
|
42727
42772
|
...rest
|
|
@@ -42762,7 +42807,8 @@ const RouteTravel = ({
|
|
|
42762
42807
|
currentIndex,
|
|
42763
42808
|
liveIndex,
|
|
42764
42809
|
onTravel,
|
|
42765
|
-
travelByDrag
|
|
42810
|
+
travelByDrag,
|
|
42811
|
+
durationMs: durationToMs(duration)
|
|
42766
42812
|
};
|
|
42767
42813
|
|
|
42768
42814
|
// The page this box asked for: routing lands later, and read back as somebody
|
|
@@ -42782,9 +42828,9 @@ const RouteTravel = ({
|
|
|
42782
42828
|
Object.assign(movementRef.current, fields);
|
|
42783
42829
|
return movementRef.current;
|
|
42784
42830
|
}
|
|
42785
|
-
row.setAttribute("data-moving", "");
|
|
42786
42831
|
const movement = {
|
|
42787
42832
|
travelling: false,
|
|
42833
|
+
playing: null,
|
|
42788
42834
|
pictures: new Map(),
|
|
42789
42835
|
resumeScrollRecording: null,
|
|
42790
42836
|
scrollsDocument: rowFollowsDocument(row),
|
|
@@ -42880,11 +42926,11 @@ const RouteTravel = ({
|
|
|
42880
42926
|
};
|
|
42881
42927
|
|
|
42882
42928
|
// A travel no hand set off is one box long however far the page is, as in a
|
|
42883
|
-
// SlideContainer: the
|
|
42884
|
-
// So the page asked for takes the column next to what is in the frame, on
|
|
42885
|
-
// it already stands, which moves nothing on screen, and endMovement puts
|
|
42886
|
-
// back. Columns rather than offsets: every page stays a place to snap
|
|
42887
|
-
// has to be hidden.
|
|
42929
|
+
// SlideContainer: a scroll to where the page stands would fly past every page in
|
|
42930
|
+
// between. So the page asked for takes the column next to what is in the frame, on
|
|
42931
|
+
// the side it already stands, which moves nothing on screen, and endMovement puts
|
|
42932
|
+
// the columns back. Columns rather than offsets: every page stays a place to snap
|
|
42933
|
+
// to, and none has to be hidden.
|
|
42888
42934
|
const travelRowTo = (index, movement) => {
|
|
42889
42935
|
const row = rowRef.current;
|
|
42890
42936
|
const panes = panesOf(row);
|
|
@@ -42929,10 +42975,85 @@ const RouteTravel = ({
|
|
|
42929
42975
|
}
|
|
42930
42976
|
});
|
|
42931
42977
|
}
|
|
42932
|
-
|
|
42933
|
-
|
|
42934
|
-
|
|
42935
|
-
|
|
42978
|
+
playTravel(pane, movement);
|
|
42979
|
+
};
|
|
42980
|
+
|
|
42981
|
+
// A travel asked for, in `duration`, on SlideContainer's curve. Not the browser's
|
|
42982
|
+
// smooth scroll, which has no duration to ask for and a curve of its own (Chrome's
|
|
42983
|
+
// hesitates, then peaks at 1.65 times this one's speed): two rows of tabs in one
|
|
42984
|
+
// app would not move alike. The scroll position is written at every frame, so the
|
|
42985
|
+
// row stays a scroller all the way: what follows it (a Nav's bar) reads it as it
|
|
42986
|
+
// reads a hand, and a finger landing finds it where it is. The clock starts at the
|
|
42987
|
+
// first frame, after what the press itself costs, as an animation's does.
|
|
42988
|
+
const playTravel = (pane, movement) => {
|
|
42989
|
+
const row = rowRef.current;
|
|
42990
|
+
// Already moving, so there is no ease-in to play: it would stall the row for an
|
|
42991
|
+
// instant right where the eye is following it.
|
|
42992
|
+
const replaced = movement.playing;
|
|
42993
|
+
const inFlight = Boolean(replaced) || Boolean(movement.rowTaken);
|
|
42994
|
+
if (replaced) {
|
|
42995
|
+
// Its frames only: the row stays travelling across the hand-over. Let go of,
|
|
42996
|
+
// it snaps to the nearest page at the read below, and the travel starts there.
|
|
42997
|
+
replaced.cancelFrame();
|
|
42998
|
+
}
|
|
42999
|
+
const from = row.scrollLeft;
|
|
43000
|
+
const {
|
|
43001
|
+
durationMs
|
|
43002
|
+
} = latestRef.current;
|
|
43003
|
+
// One box in `duration` is the pace: what is left of a travel picked up on the
|
|
43004
|
+
// way takes what is left of the time. Never slower: two boxes are not made to drag.
|
|
43005
|
+
const boxes = Math.abs(pane.offsetLeft - from) / row.clientWidth;
|
|
43006
|
+
const travelMs = boxes < 1 ? durationMs * boxes : durationMs;
|
|
43007
|
+
if (!travelMs) {
|
|
43008
|
+
row.scrollLeft = pane.offsetLeft;
|
|
43009
|
+
// A write that changes nothing is followed by no scroll event to say the row
|
|
43010
|
+
// stopped. After the commit that asked, which goes on to say the row moves.
|
|
43011
|
+
Promise.resolve().then(landRow);
|
|
43012
|
+
return;
|
|
43013
|
+
}
|
|
43014
|
+
const curve = inFlight ? CSS_EASE_OUT : CSS_EASE;
|
|
43015
|
+
let start = null;
|
|
43016
|
+
let frame = null;
|
|
43017
|
+
const playing = {
|
|
43018
|
+
// Where this travel put the row, for a scroll event to tell it from a hand.
|
|
43019
|
+
written: from,
|
|
43020
|
+
cancelFrame: () => {
|
|
43021
|
+
cancelAnimationFrame(frame);
|
|
43022
|
+
},
|
|
43023
|
+
stop: () => {
|
|
43024
|
+
playing.cancelFrame();
|
|
43025
|
+
if (movement.playing === playing) {
|
|
43026
|
+
movement.playing = null;
|
|
43027
|
+
row.removeAttribute("data-route-row-travelling");
|
|
43028
|
+
}
|
|
43029
|
+
}
|
|
43030
|
+
};
|
|
43031
|
+
const step = now => {
|
|
43032
|
+
if (start === null) {
|
|
43033
|
+
// When this frame runs, not when it began: under load it began while the
|
|
43034
|
+
// press was still being handled, and that time is no travel.
|
|
43035
|
+
start = performance.now();
|
|
43036
|
+
}
|
|
43037
|
+
// The next frame may have begun while this first one was still being handled.
|
|
43038
|
+
const elapsed = now > start ? (now - start) / travelMs : 0;
|
|
43039
|
+
// Read at every frame: the layout may move under the travel (a scrollbar
|
|
43040
|
+
// coming in narrows the row), and it goes where the page stands.
|
|
43041
|
+
const to = pane.offsetLeft;
|
|
43042
|
+
const arrived = elapsed >= 1;
|
|
43043
|
+
playing.written = arrived ? to : from + (to - from) * easedProgress(elapsed, curve);
|
|
43044
|
+
row.scrollLeft = playing.written;
|
|
43045
|
+
if (!arrived) {
|
|
43046
|
+
frame = requestAnimationFrame(step);
|
|
43047
|
+
return;
|
|
43048
|
+
}
|
|
43049
|
+
playing.stop();
|
|
43050
|
+
// Said here rather than waited for: the last frames of a curve can move the row
|
|
43051
|
+
// by less than a pixel, and nothing then says it has stopped.
|
|
43052
|
+
landRow();
|
|
43053
|
+
};
|
|
43054
|
+
movement.playing = playing;
|
|
43055
|
+
row.setAttribute("data-route-row-travelling", "");
|
|
43056
|
+
frame = requestAnimationFrame(step);
|
|
42936
43057
|
};
|
|
42937
43058
|
|
|
42938
43059
|
// Every column back where the map says, and the row on the page of the address, in
|
|
@@ -42956,9 +43077,8 @@ const RouteTravel = ({
|
|
|
42956
43077
|
|
|
42957
43078
|
// A tab pressed over a row a hand has left moving, which the browser plays on and
|
|
42958
43079
|
// overwrites a travel with once that travel is over: the snap is ended first. Given
|
|
42959
|
-
// back, the row stands where it stood.
|
|
42960
|
-
//
|
|
42961
|
-
// sees nothing to do.
|
|
43080
|
+
// back, the row stands where it stood. The travel a frame later, because until then
|
|
43081
|
+
// WebKit still has the row where a box that is no scroller stood, at 0.
|
|
42962
43082
|
const takeRowFromHand = movement => {
|
|
42963
43083
|
const row = rowRef.current;
|
|
42964
43084
|
const position = row.scrollLeft;
|
|
@@ -42966,12 +43086,14 @@ const RouteTravel = ({
|
|
|
42966
43086
|
// A travel no hand set off holds nothing: it goes where it goes.
|
|
42967
43087
|
releaseHeldPage();
|
|
42968
43088
|
row.style.setProperty("--navi-route-row-stand", `${-position}px`);
|
|
42969
|
-
row.setAttribute("data-route-row-taken", "
|
|
43089
|
+
row.setAttribute("data-route-row-taken", "");
|
|
42970
43090
|
afterFrames(2, () => {
|
|
42971
43091
|
if (movementRef.current !== movement) {
|
|
42972
43092
|
return;
|
|
42973
43093
|
}
|
|
42974
|
-
|
|
43094
|
+
// A scroller again, standing where no page does: not snapped meanwhile.
|
|
43095
|
+
row.setAttribute("data-route-row-travelling", "");
|
|
43096
|
+
row.removeAttribute("data-route-row-taken");
|
|
42975
43097
|
row.style.removeProperty("--navi-route-row-stand");
|
|
42976
43098
|
afterFrames(1, () => {
|
|
42977
43099
|
if (movementRef.current === movement) {
|
|
@@ -42990,8 +43112,11 @@ const RouteTravel = ({
|
|
|
42990
43112
|
return;
|
|
42991
43113
|
}
|
|
42992
43114
|
movementRef.current = null;
|
|
43115
|
+
if (movement.playing) {
|
|
43116
|
+
movement.playing.stop();
|
|
43117
|
+
}
|
|
42993
43118
|
putRowBack(movement);
|
|
42994
|
-
row.removeAttribute("data-
|
|
43119
|
+
row.removeAttribute("data-route-row-travelling");
|
|
42995
43120
|
row.removeAttribute("data-route-row-taken");
|
|
42996
43121
|
publishRouteRowMove(null);
|
|
42997
43122
|
releaseHeldPage();
|
|
@@ -43020,6 +43145,45 @@ const RouteTravel = ({
|
|
|
43020
43145
|
render();
|
|
43021
43146
|
};
|
|
43022
43147
|
|
|
43148
|
+
// The row may have stopped: on the page of the address, the movement is over; on
|
|
43149
|
+
// another, a hand put it there and the address follows.
|
|
43150
|
+
const landRow = () => {
|
|
43151
|
+
const row = rowRef.current;
|
|
43152
|
+
const movement = movementRef.current;
|
|
43153
|
+
// A travel playing says itself when it is over.
|
|
43154
|
+
if (!movement || movement.playing || isRowTaken(row)) {
|
|
43155
|
+
return;
|
|
43156
|
+
}
|
|
43157
|
+
const panes = panesOf(row);
|
|
43158
|
+
const pane = pageAtRest(row, panes, ROW_READING);
|
|
43159
|
+
if (!pane) {
|
|
43160
|
+
// A travel a hand took over (a trackpad, a wheel) and left between two pages:
|
|
43161
|
+
// sent again, the row then rests.
|
|
43162
|
+
const {
|
|
43163
|
+
currentIndex
|
|
43164
|
+
} = latestRef.current;
|
|
43165
|
+
if (movement.travelling && panes[currentIndex]) {
|
|
43166
|
+
travelRowTo(currentIndex, movement);
|
|
43167
|
+
}
|
|
43168
|
+
return;
|
|
43169
|
+
}
|
|
43170
|
+
movement.landed = true;
|
|
43171
|
+
const {
|
|
43172
|
+
pages
|
|
43173
|
+
} = latestRef.current;
|
|
43174
|
+
const page = pages[panes.indexOf(pane)];
|
|
43175
|
+
if (!page || pageIsCurrent(page)) {
|
|
43176
|
+
if (!askedPageRef.current || samePage(askedPageRef.current, page)) {
|
|
43177
|
+
endMovement();
|
|
43178
|
+
}
|
|
43179
|
+
return;
|
|
43180
|
+
}
|
|
43181
|
+
if (samePage(askedPageRef.current, page)) {
|
|
43182
|
+
return;
|
|
43183
|
+
}
|
|
43184
|
+
askForPageOfRow(panes.indexOf(pane), movement);
|
|
43185
|
+
};
|
|
43186
|
+
|
|
43023
43187
|
// The address and the row, in step. The opening and a row given again around the
|
|
43024
43188
|
// page on screen have nothing to show for it; somebody else moving the address
|
|
43025
43189
|
// (a tab pressed, the back button) is a travel.
|
|
@@ -43040,6 +43204,13 @@ const RouteTravel = ({
|
|
|
43040
43204
|
}
|
|
43041
43205
|
answeredPageRef.current = page;
|
|
43042
43206
|
if (!page || !pane) {
|
|
43207
|
+
// The address left the row while a travel nobody's hand is on was playing (a
|
|
43208
|
+
// link to a page off the row): there is nothing left to travel to, and the
|
|
43209
|
+
// landing would send the address back to the page the row rests on.
|
|
43210
|
+
const movement = movementRef.current;
|
|
43211
|
+
if (!page && movement?.travelling && !movement.byHand) {
|
|
43212
|
+
Promise.resolve().then(endMovement);
|
|
43213
|
+
}
|
|
43043
43214
|
return;
|
|
43044
43215
|
}
|
|
43045
43216
|
const asked = askedPageRef.current;
|
|
@@ -43069,7 +43240,7 @@ const RouteTravel = ({
|
|
|
43069
43240
|
takeRowFromHand(movement);
|
|
43070
43241
|
return;
|
|
43071
43242
|
}
|
|
43072
|
-
if (row.
|
|
43243
|
+
if (!row.hasAttribute("data-route-row-taken")) {
|
|
43073
43244
|
travelRowTo(currentIndex, movement);
|
|
43074
43245
|
}
|
|
43075
43246
|
// In this very commit: the tab pressed already wears its bar, and the first
|
|
@@ -43094,8 +43265,7 @@ const RouteTravel = ({
|
|
|
43094
43265
|
if (asked || currentIndex === -1) {
|
|
43095
43266
|
return;
|
|
43096
43267
|
}
|
|
43097
|
-
|
|
43098
|
-
if (pathOf(pages[currentIndex]) === path || !pages.some(page => pathOf(page) === path)) {
|
|
43268
|
+
if (pageIsAt(pages[currentIndex], url) || !pages.some(page => pageIsAt(page, url))) {
|
|
43099
43269
|
return;
|
|
43100
43270
|
}
|
|
43101
43271
|
const movement = beginMovement({
|
|
@@ -43106,7 +43276,6 @@ const RouteTravel = ({
|
|
|
43106
43276
|
}, [windowed]);
|
|
43107
43277
|
useLayoutEffect(() => {
|
|
43108
43278
|
const row = rowRef.current;
|
|
43109
|
-
const isClipped = () => row.getAttribute("data-route-row-taken") === "clip";
|
|
43110
43279
|
const onScroll = ({
|
|
43111
43280
|
finger,
|
|
43112
43281
|
lifted
|
|
@@ -43117,10 +43286,15 @@ const RouteTravel = ({
|
|
|
43117
43286
|
travelByDrag
|
|
43118
43287
|
} = latestRef.current;
|
|
43119
43288
|
const panes = panesOf(row);
|
|
43120
|
-
if (panes.length < 2 ||
|
|
43289
|
+
if (panes.length < 2 || isRowTaken(row)) {
|
|
43121
43290
|
return;
|
|
43122
43291
|
}
|
|
43123
43292
|
const moving = movementRef.current;
|
|
43293
|
+
// Not where the travel put it: a hand has taken the row (a trackpad, a wheel),
|
|
43294
|
+
// and the travel lets it go, snapping included, as the browser's own scroll does.
|
|
43295
|
+
if (moving?.playing && Math.abs(row.scrollLeft - moving.playing.written) > WRITE_ROUNDING) {
|
|
43296
|
+
moving.playing.stop();
|
|
43297
|
+
}
|
|
43124
43298
|
const pane = panes[currentIndex];
|
|
43125
43299
|
if (!moving && pane && restsOn(row, pane, ROW_READING)) {
|
|
43126
43300
|
// Written by this box, at rest: nobody is moving anything.
|
|
@@ -43200,60 +43374,20 @@ const RouteTravel = ({
|
|
|
43200
43374
|
// address ran ahead to, even for a render.
|
|
43201
43375
|
releaseHeldPage();
|
|
43202
43376
|
};
|
|
43203
|
-
const onLand = () => {
|
|
43204
|
-
const movement = movementRef.current;
|
|
43205
|
-
if (!movement || isClipped()) {
|
|
43206
|
-
return;
|
|
43207
|
-
}
|
|
43208
|
-
const panes = panesOf(row);
|
|
43209
|
-
const pane = pageAtRest(row, panes, ROW_READING);
|
|
43210
|
-
if (!pane) {
|
|
43211
|
-
// A travel stopped short of its page: the layout moved under it (a scrollbar
|
|
43212
|
-
// coming in narrows the row), and where it was sent is not where the page
|
|
43213
|
-
// stands. Sent again: the row then rests, or does not move and is not heard.
|
|
43214
|
-
const target = panes[latestRef.current.currentIndex];
|
|
43215
|
-
if (movement.travelling && !isClipped() && target) {
|
|
43216
|
-
row.scrollTo({
|
|
43217
|
-
left: target.offsetLeft,
|
|
43218
|
-
behavior: "smooth"
|
|
43219
|
-
});
|
|
43220
|
-
}
|
|
43221
|
-
return;
|
|
43222
|
-
}
|
|
43223
|
-
movement.landed = true;
|
|
43224
|
-
const {
|
|
43225
|
-
pages
|
|
43226
|
-
} = latestRef.current;
|
|
43227
|
-
const page = pages[panes.indexOf(pane)];
|
|
43228
|
-
if (!page || pageIsCurrent(page)) {
|
|
43229
|
-
if (!askedPageRef.current || samePage(askedPageRef.current, page)) {
|
|
43230
|
-
endMovement();
|
|
43231
|
-
}
|
|
43232
|
-
return;
|
|
43233
|
-
}
|
|
43234
|
-
if (samePage(askedPageRef.current, page)) {
|
|
43235
|
-
return;
|
|
43236
|
-
}
|
|
43237
|
-
askForPageOfRow(panes.indexOf(pane), movement);
|
|
43238
|
-
};
|
|
43239
43377
|
const stopWatching = watchRowScroll(row, {
|
|
43240
43378
|
onScroll,
|
|
43241
|
-
onLand,
|
|
43379
|
+
onLand: landRow,
|
|
43242
43380
|
onLeftToSnap
|
|
43243
43381
|
});
|
|
43244
43382
|
// A finger on a travel still playing: the row is the finger's from here, and it
|
|
43245
|
-
// stands where the travel was going, as in a SlideContainer.
|
|
43246
|
-
//
|
|
43383
|
+
// stands where the travel was going, as in a SlideContainer. Not a wheel: a page
|
|
43384
|
+
// scrolled vertically over the row is no hand on it, and one scrolling it sideways
|
|
43385
|
+
// is told apart by the travel itself (onScroll).
|
|
43247
43386
|
const onTouchOnTravel = () => {
|
|
43248
|
-
if (movementRef.current?.travelling && !
|
|
43387
|
+
if (movementRef.current?.travelling && !isRowTaken(row)) {
|
|
43249
43388
|
endMovement();
|
|
43250
43389
|
}
|
|
43251
43390
|
};
|
|
43252
|
-
const onWheelOnTravel = () => {
|
|
43253
|
-
if (row.getAttribute("data-route-row-taken") === "") {
|
|
43254
|
-
row.removeAttribute("data-route-row-taken");
|
|
43255
|
-
}
|
|
43256
|
-
};
|
|
43257
43391
|
// A press for the page the address is already on changes nothing the router can
|
|
43258
43392
|
// tell, and a stroke no lift ends leaves the address where it was: it is heard here,
|
|
43259
43393
|
// the way a tab says which page it is through its address.
|
|
@@ -43268,7 +43402,7 @@ const RouteTravel = ({
|
|
|
43268
43402
|
currentIndex
|
|
43269
43403
|
} = latestRef.current;
|
|
43270
43404
|
const page = pages[currentIndex];
|
|
43271
|
-
if (!link || !page ||
|
|
43405
|
+
if (!link || !page || !pageIsAt(page, link.href)) {
|
|
43272
43406
|
return;
|
|
43273
43407
|
}
|
|
43274
43408
|
movement.travelling = true;
|
|
@@ -43277,20 +43411,20 @@ const RouteTravel = ({
|
|
|
43277
43411
|
row.addEventListener("touchstart", onTouchOnTravel, {
|
|
43278
43412
|
passive: true
|
|
43279
43413
|
});
|
|
43280
|
-
row.addEventListener("wheel", onWheelOnTravel, {
|
|
43281
|
-
passive: true
|
|
43282
|
-
});
|
|
43283
43414
|
document.addEventListener("click", onPress, {
|
|
43284
43415
|
capture: true
|
|
43285
43416
|
});
|
|
43286
43417
|
return () => {
|
|
43287
43418
|
stopWatching();
|
|
43288
43419
|
row.removeEventListener("touchstart", onTouchOnTravel);
|
|
43289
|
-
row.removeEventListener("wheel", onWheelOnTravel);
|
|
43290
43420
|
document.removeEventListener("click", onPress, {
|
|
43291
43421
|
capture: true
|
|
43292
43422
|
});
|
|
43293
|
-
movementRef.current
|
|
43423
|
+
const movement = movementRef.current;
|
|
43424
|
+
if (movement) {
|
|
43425
|
+
movement.playing?.stop();
|
|
43426
|
+
movement.resumeScrollRecording?.();
|
|
43427
|
+
}
|
|
43294
43428
|
movementRef.current = null;
|
|
43295
43429
|
publishRouteRowMove(null);
|
|
43296
43430
|
releaseHeldPage();
|
|
@@ -43350,12 +43484,10 @@ const RouteTravel = ({
|
|
|
43350
43484
|
}, [prefetch, neighbours, restCount]);
|
|
43351
43485
|
|
|
43352
43486
|
// A neighbour shows from the first frame the compositor moves the row, before
|
|
43353
|
-
// any scroll event
|
|
43354
|
-
|
|
43487
|
+
// any scroll event, so it has to stand where the reader is looking by then: a
|
|
43488
|
+
// finger landing is the last word before that. A trackpad says nothing first, and
|
|
43489
|
+
// the pin comes with its first scroll event (beginMovement).
|
|
43355
43490
|
useLayoutEffect(() => {
|
|
43356
|
-
if (!paintsNeighbours) {
|
|
43357
|
-
return undefined;
|
|
43358
|
-
}
|
|
43359
43491
|
const row = rowRef.current;
|
|
43360
43492
|
const onTouchStart = () => {
|
|
43361
43493
|
if (!movementRef.current && rowFollowsDocument(row)) {
|
|
@@ -43368,7 +43500,8 @@ const RouteTravel = ({
|
|
|
43368
43500
|
return () => {
|
|
43369
43501
|
row.removeEventListener("touchstart", onTouchStart);
|
|
43370
43502
|
};
|
|
43371
|
-
}, [
|
|
43503
|
+
}, []);
|
|
43504
|
+
const paintsNeighbours = Boolean(renderNeighbour);
|
|
43372
43505
|
const rowContext = useMemo(() => ({
|
|
43373
43506
|
renderNeighbour
|
|
43374
43507
|
}), [renderNeighbour]);
|
|
@@ -43452,6 +43585,10 @@ const copyScrollPositions = (from, to) => {
|
|
|
43452
43585
|
}
|
|
43453
43586
|
}
|
|
43454
43587
|
};
|
|
43588
|
+
|
|
43589
|
+
// A tab pressed over a row a hand has left moving holds it still (takeRowFromHand):
|
|
43590
|
+
// what the row says meanwhile is not a hand moving it.
|
|
43591
|
+
const isRowTaken = row => row.hasAttribute("data-route-row-taken");
|
|
43455
43592
|
const afterFrames = (count, callback) => {
|
|
43456
43593
|
if (count === 0) {
|
|
43457
43594
|
callback();
|
|
@@ -70783,15 +70920,6 @@ const lineAreas = (slideElements, layout) => {
|
|
|
70783
70920
|
return parseAreas(layout === "column" ? names : [names.join(" ")]);
|
|
70784
70921
|
};
|
|
70785
70922
|
|
|
70786
|
-
// A CSS duration read as a number, so a window knows when its travel is over.
|
|
70787
|
-
const durationToMs = duration => {
|
|
70788
|
-
const number = parseFloat(duration);
|
|
70789
|
-
if (Number.isNaN(number)) {
|
|
70790
|
-
return 0;
|
|
70791
|
-
}
|
|
70792
|
-
return String(duration).trimEnd().endsWith("ms") ? number : number * 1000;
|
|
70793
|
-
};
|
|
70794
|
-
|
|
70795
70923
|
// What asked for a travel, read off the event that carried it: the hand said
|
|
70796
70924
|
// "somewhere over there" (a gesture one browses with), or it said a name (a tab
|
|
70797
70925
|
// pressed, a key, a command — a place aimed at). Nothing at all when the travel
|