@jsenv/navi 0.30.29 → 0.30.31
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 +235 -116
- package/dist/dev/jsenv_navi.js.map +7 -5
- package/dist/jsenv_navi.js +235 -116
- package/dist/jsenv_navi.js.map +7 -5
- package/docs/drag_to_travel.md +4 -4
- package/package.json +2 -2
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,76 @@ 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 inFlight = Boolean(movement.playing) || Boolean(movement.rowTaken);
|
|
42993
|
+
if (movement.playing) {
|
|
42994
|
+
movement.playing.stop();
|
|
42995
|
+
}
|
|
42996
|
+
const from = row.scrollLeft;
|
|
42997
|
+
const {
|
|
42998
|
+
durationMs
|
|
42999
|
+
} = latestRef.current;
|
|
43000
|
+
// One box in `duration` is the pace: what is left of a travel picked up on the
|
|
43001
|
+
// way takes what is left of the time. Never slower: two boxes are not made to drag.
|
|
43002
|
+
const boxes = Math.abs(pane.offsetLeft - from) / row.clientWidth;
|
|
43003
|
+
const travelMs = boxes < 1 ? durationMs * boxes : durationMs;
|
|
43004
|
+
if (!travelMs) {
|
|
43005
|
+
row.scrollLeft = pane.offsetLeft;
|
|
43006
|
+
// A write that changes nothing is followed by no scroll event to say the row
|
|
43007
|
+
// stopped. After the commit that asked, which goes on to say the row moves.
|
|
43008
|
+
Promise.resolve().then(landRow);
|
|
43009
|
+
return;
|
|
43010
|
+
}
|
|
43011
|
+
const curve = inFlight ? CSS_EASE_OUT : CSS_EASE;
|
|
43012
|
+
let start = null;
|
|
43013
|
+
let frame = null;
|
|
43014
|
+
const playing = {
|
|
43015
|
+
// Where this travel put the row, for a scroll event to tell it from a hand.
|
|
43016
|
+
written: from,
|
|
43017
|
+
stop: () => {
|
|
43018
|
+
cancelAnimationFrame(frame);
|
|
43019
|
+
if (movement.playing === playing) {
|
|
43020
|
+
movement.playing = null;
|
|
43021
|
+
row.removeAttribute("data-route-row-travelling");
|
|
43022
|
+
}
|
|
43023
|
+
}
|
|
43024
|
+
};
|
|
43025
|
+
const step = now => {
|
|
43026
|
+
if (start === null) {
|
|
43027
|
+
start = now;
|
|
43028
|
+
}
|
|
43029
|
+
const elapsed = (now - start) / travelMs;
|
|
43030
|
+
// Read at every frame: the layout may move under the travel (a scrollbar
|
|
43031
|
+
// coming in narrows the row), and it goes where the page stands.
|
|
43032
|
+
const to = pane.offsetLeft;
|
|
43033
|
+
const arrived = elapsed >= 1;
|
|
43034
|
+
playing.written = arrived ? to : from + (to - from) * easedProgress(elapsed, curve);
|
|
43035
|
+
row.scrollLeft = playing.written;
|
|
43036
|
+
if (!arrived) {
|
|
43037
|
+
frame = requestAnimationFrame(step);
|
|
43038
|
+
return;
|
|
43039
|
+
}
|
|
43040
|
+
playing.stop();
|
|
43041
|
+
// Said here rather than waited for: the last frames of a curve can move the row
|
|
43042
|
+
// by less than a pixel, and nothing then says it has stopped.
|
|
43043
|
+
landRow();
|
|
43044
|
+
};
|
|
43045
|
+
movement.playing = playing;
|
|
43046
|
+
row.setAttribute("data-route-row-travelling", "");
|
|
43047
|
+
frame = requestAnimationFrame(step);
|
|
42936
43048
|
};
|
|
42937
43049
|
|
|
42938
43050
|
// Every column back where the map says, and the row on the page of the address, in
|
|
@@ -42956,9 +43068,8 @@ const RouteTravel = ({
|
|
|
42956
43068
|
|
|
42957
43069
|
// A tab pressed over a row a hand has left moving, which the browser plays on and
|
|
42958
43070
|
// 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.
|
|
43071
|
+
// back, the row stands where it stood. The travel a frame later, because until then
|
|
43072
|
+
// WebKit still has the row where a box that is no scroller stood, at 0.
|
|
42962
43073
|
const takeRowFromHand = movement => {
|
|
42963
43074
|
const row = rowRef.current;
|
|
42964
43075
|
const position = row.scrollLeft;
|
|
@@ -42966,12 +43077,14 @@ const RouteTravel = ({
|
|
|
42966
43077
|
// A travel no hand set off holds nothing: it goes where it goes.
|
|
42967
43078
|
releaseHeldPage();
|
|
42968
43079
|
row.style.setProperty("--navi-route-row-stand", `${-position}px`);
|
|
42969
|
-
row.setAttribute("data-route-row-taken", "
|
|
43080
|
+
row.setAttribute("data-route-row-taken", "");
|
|
42970
43081
|
afterFrames(2, () => {
|
|
42971
43082
|
if (movementRef.current !== movement) {
|
|
42972
43083
|
return;
|
|
42973
43084
|
}
|
|
42974
|
-
|
|
43085
|
+
// A scroller again, standing where no page does: not snapped meanwhile.
|
|
43086
|
+
row.setAttribute("data-route-row-travelling", "");
|
|
43087
|
+
row.removeAttribute("data-route-row-taken");
|
|
42975
43088
|
row.style.removeProperty("--navi-route-row-stand");
|
|
42976
43089
|
afterFrames(1, () => {
|
|
42977
43090
|
if (movementRef.current === movement) {
|
|
@@ -42990,8 +43103,11 @@ const RouteTravel = ({
|
|
|
42990
43103
|
return;
|
|
42991
43104
|
}
|
|
42992
43105
|
movementRef.current = null;
|
|
43106
|
+
if (movement.playing) {
|
|
43107
|
+
movement.playing.stop();
|
|
43108
|
+
}
|
|
42993
43109
|
putRowBack(movement);
|
|
42994
|
-
row.removeAttribute("data-
|
|
43110
|
+
row.removeAttribute("data-route-row-travelling");
|
|
42995
43111
|
row.removeAttribute("data-route-row-taken");
|
|
42996
43112
|
publishRouteRowMove(null);
|
|
42997
43113
|
releaseHeldPage();
|
|
@@ -43020,6 +43136,45 @@ const RouteTravel = ({
|
|
|
43020
43136
|
render();
|
|
43021
43137
|
};
|
|
43022
43138
|
|
|
43139
|
+
// The row may have stopped: on the page of the address, the movement is over; on
|
|
43140
|
+
// another, a hand put it there and the address follows.
|
|
43141
|
+
const landRow = () => {
|
|
43142
|
+
const row = rowRef.current;
|
|
43143
|
+
const movement = movementRef.current;
|
|
43144
|
+
// A travel playing says itself when it is over.
|
|
43145
|
+
if (!movement || movement.playing || isRowTaken(row)) {
|
|
43146
|
+
return;
|
|
43147
|
+
}
|
|
43148
|
+
const panes = panesOf(row);
|
|
43149
|
+
const pane = pageAtRest(row, panes, ROW_READING);
|
|
43150
|
+
if (!pane) {
|
|
43151
|
+
// A travel a hand took over (a trackpad, a wheel) and left between two pages:
|
|
43152
|
+
// sent again, the row then rests.
|
|
43153
|
+
const {
|
|
43154
|
+
currentIndex
|
|
43155
|
+
} = latestRef.current;
|
|
43156
|
+
if (movement.travelling && panes[currentIndex]) {
|
|
43157
|
+
travelRowTo(currentIndex, movement);
|
|
43158
|
+
}
|
|
43159
|
+
return;
|
|
43160
|
+
}
|
|
43161
|
+
movement.landed = true;
|
|
43162
|
+
const {
|
|
43163
|
+
pages
|
|
43164
|
+
} = latestRef.current;
|
|
43165
|
+
const page = pages[panes.indexOf(pane)];
|
|
43166
|
+
if (!page || pageIsCurrent(page)) {
|
|
43167
|
+
if (!askedPageRef.current || samePage(askedPageRef.current, page)) {
|
|
43168
|
+
endMovement();
|
|
43169
|
+
}
|
|
43170
|
+
return;
|
|
43171
|
+
}
|
|
43172
|
+
if (samePage(askedPageRef.current, page)) {
|
|
43173
|
+
return;
|
|
43174
|
+
}
|
|
43175
|
+
askForPageOfRow(panes.indexOf(pane), movement);
|
|
43176
|
+
};
|
|
43177
|
+
|
|
43023
43178
|
// The address and the row, in step. The opening and a row given again around the
|
|
43024
43179
|
// page on screen have nothing to show for it; somebody else moving the address
|
|
43025
43180
|
// (a tab pressed, the back button) is a travel.
|
|
@@ -43040,6 +43195,13 @@ const RouteTravel = ({
|
|
|
43040
43195
|
}
|
|
43041
43196
|
answeredPageRef.current = page;
|
|
43042
43197
|
if (!page || !pane) {
|
|
43198
|
+
// The address left the row while a travel nobody's hand is on was playing (a
|
|
43199
|
+
// link to a page off the row): there is nothing left to travel to, and the
|
|
43200
|
+
// landing would send the address back to the page the row rests on.
|
|
43201
|
+
const movement = movementRef.current;
|
|
43202
|
+
if (!page && movement?.travelling && !movement.byHand) {
|
|
43203
|
+
Promise.resolve().then(endMovement);
|
|
43204
|
+
}
|
|
43043
43205
|
return;
|
|
43044
43206
|
}
|
|
43045
43207
|
const asked = askedPageRef.current;
|
|
@@ -43069,7 +43231,7 @@ const RouteTravel = ({
|
|
|
43069
43231
|
takeRowFromHand(movement);
|
|
43070
43232
|
return;
|
|
43071
43233
|
}
|
|
43072
|
-
if (row.
|
|
43234
|
+
if (!row.hasAttribute("data-route-row-taken")) {
|
|
43073
43235
|
travelRowTo(currentIndex, movement);
|
|
43074
43236
|
}
|
|
43075
43237
|
// In this very commit: the tab pressed already wears its bar, and the first
|
|
@@ -43094,8 +43256,7 @@ const RouteTravel = ({
|
|
|
43094
43256
|
if (asked || currentIndex === -1) {
|
|
43095
43257
|
return;
|
|
43096
43258
|
}
|
|
43097
|
-
|
|
43098
|
-
if (pathOf(pages[currentIndex]) === path || !pages.some(page => pathOf(page) === path)) {
|
|
43259
|
+
if (pageIsAt(pages[currentIndex], url) || !pages.some(page => pageIsAt(page, url))) {
|
|
43099
43260
|
return;
|
|
43100
43261
|
}
|
|
43101
43262
|
const movement = beginMovement({
|
|
@@ -43106,7 +43267,6 @@ const RouteTravel = ({
|
|
|
43106
43267
|
}, [windowed]);
|
|
43107
43268
|
useLayoutEffect(() => {
|
|
43108
43269
|
const row = rowRef.current;
|
|
43109
|
-
const isClipped = () => row.getAttribute("data-route-row-taken") === "clip";
|
|
43110
43270
|
const onScroll = ({
|
|
43111
43271
|
finger,
|
|
43112
43272
|
lifted
|
|
@@ -43117,10 +43277,15 @@ const RouteTravel = ({
|
|
|
43117
43277
|
travelByDrag
|
|
43118
43278
|
} = latestRef.current;
|
|
43119
43279
|
const panes = panesOf(row);
|
|
43120
|
-
if (panes.length < 2 ||
|
|
43280
|
+
if (panes.length < 2 || isRowTaken(row)) {
|
|
43121
43281
|
return;
|
|
43122
43282
|
}
|
|
43123
43283
|
const moving = movementRef.current;
|
|
43284
|
+
// Not where the travel put it: a hand has taken the row (a trackpad, a wheel),
|
|
43285
|
+
// and the travel lets it go, snapping included, as the browser's own scroll does.
|
|
43286
|
+
if (moving?.playing && Math.abs(row.scrollLeft - moving.playing.written) > WRITE_ROUNDING) {
|
|
43287
|
+
moving.playing.stop();
|
|
43288
|
+
}
|
|
43124
43289
|
const pane = panes[currentIndex];
|
|
43125
43290
|
if (!moving && pane && restsOn(row, pane, ROW_READING)) {
|
|
43126
43291
|
// Written by this box, at rest: nobody is moving anything.
|
|
@@ -43200,60 +43365,20 @@ const RouteTravel = ({
|
|
|
43200
43365
|
// address ran ahead to, even for a render.
|
|
43201
43366
|
releaseHeldPage();
|
|
43202
43367
|
};
|
|
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
43368
|
const stopWatching = watchRowScroll(row, {
|
|
43240
43369
|
onScroll,
|
|
43241
|
-
onLand,
|
|
43370
|
+
onLand: landRow,
|
|
43242
43371
|
onLeftToSnap
|
|
43243
43372
|
});
|
|
43244
43373
|
// 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
|
-
//
|
|
43374
|
+
// stands where the travel was going, as in a SlideContainer. Not a wheel: a page
|
|
43375
|
+
// scrolled vertically over the row is no hand on it, and one scrolling it sideways
|
|
43376
|
+
// is told apart by the travel itself (onScroll).
|
|
43247
43377
|
const onTouchOnTravel = () => {
|
|
43248
|
-
if (movementRef.current?.travelling && !
|
|
43378
|
+
if (movementRef.current?.travelling && !isRowTaken(row)) {
|
|
43249
43379
|
endMovement();
|
|
43250
43380
|
}
|
|
43251
43381
|
};
|
|
43252
|
-
const onWheelOnTravel = () => {
|
|
43253
|
-
if (row.getAttribute("data-route-row-taken") === "") {
|
|
43254
|
-
row.removeAttribute("data-route-row-taken");
|
|
43255
|
-
}
|
|
43256
|
-
};
|
|
43257
43382
|
// A press for the page the address is already on changes nothing the router can
|
|
43258
43383
|
// tell, and a stroke no lift ends leaves the address where it was: it is heard here,
|
|
43259
43384
|
// the way a tab says which page it is through its address.
|
|
@@ -43268,7 +43393,7 @@ const RouteTravel = ({
|
|
|
43268
43393
|
currentIndex
|
|
43269
43394
|
} = latestRef.current;
|
|
43270
43395
|
const page = pages[currentIndex];
|
|
43271
|
-
if (!link || !page ||
|
|
43396
|
+
if (!link || !page || !pageIsAt(page, link.href)) {
|
|
43272
43397
|
return;
|
|
43273
43398
|
}
|
|
43274
43399
|
movement.travelling = true;
|
|
@@ -43277,20 +43402,20 @@ const RouteTravel = ({
|
|
|
43277
43402
|
row.addEventListener("touchstart", onTouchOnTravel, {
|
|
43278
43403
|
passive: true
|
|
43279
43404
|
});
|
|
43280
|
-
row.addEventListener("wheel", onWheelOnTravel, {
|
|
43281
|
-
passive: true
|
|
43282
|
-
});
|
|
43283
43405
|
document.addEventListener("click", onPress, {
|
|
43284
43406
|
capture: true
|
|
43285
43407
|
});
|
|
43286
43408
|
return () => {
|
|
43287
43409
|
stopWatching();
|
|
43288
43410
|
row.removeEventListener("touchstart", onTouchOnTravel);
|
|
43289
|
-
row.removeEventListener("wheel", onWheelOnTravel);
|
|
43290
43411
|
document.removeEventListener("click", onPress, {
|
|
43291
43412
|
capture: true
|
|
43292
43413
|
});
|
|
43293
|
-
movementRef.current
|
|
43414
|
+
const movement = movementRef.current;
|
|
43415
|
+
if (movement) {
|
|
43416
|
+
movement.playing?.stop();
|
|
43417
|
+
movement.resumeScrollRecording?.();
|
|
43418
|
+
}
|
|
43294
43419
|
movementRef.current = null;
|
|
43295
43420
|
publishRouteRowMove(null);
|
|
43296
43421
|
releaseHeldPage();
|
|
@@ -43350,12 +43475,10 @@ const RouteTravel = ({
|
|
|
43350
43475
|
}, [prefetch, neighbours, restCount]);
|
|
43351
43476
|
|
|
43352
43477
|
// A neighbour shows from the first frame the compositor moves the row, before
|
|
43353
|
-
// any scroll event
|
|
43354
|
-
|
|
43478
|
+
// any scroll event, so it has to stand where the reader is looking by then: a
|
|
43479
|
+
// finger landing is the last word before that. A trackpad says nothing first, and
|
|
43480
|
+
// the pin comes with its first scroll event (beginMovement).
|
|
43355
43481
|
useLayoutEffect(() => {
|
|
43356
|
-
if (!paintsNeighbours) {
|
|
43357
|
-
return undefined;
|
|
43358
|
-
}
|
|
43359
43482
|
const row = rowRef.current;
|
|
43360
43483
|
const onTouchStart = () => {
|
|
43361
43484
|
if (!movementRef.current && rowFollowsDocument(row)) {
|
|
@@ -43368,7 +43491,8 @@ const RouteTravel = ({
|
|
|
43368
43491
|
return () => {
|
|
43369
43492
|
row.removeEventListener("touchstart", onTouchStart);
|
|
43370
43493
|
};
|
|
43371
|
-
}, [
|
|
43494
|
+
}, []);
|
|
43495
|
+
const paintsNeighbours = Boolean(renderNeighbour);
|
|
43372
43496
|
const rowContext = useMemo(() => ({
|
|
43373
43497
|
renderNeighbour
|
|
43374
43498
|
}), [renderNeighbour]);
|
|
@@ -43452,6 +43576,10 @@ const copyScrollPositions = (from, to) => {
|
|
|
43452
43576
|
}
|
|
43453
43577
|
}
|
|
43454
43578
|
};
|
|
43579
|
+
|
|
43580
|
+
// A tab pressed over a row a hand has left moving holds it still (takeRowFromHand):
|
|
43581
|
+
// what the row says meanwhile is not a hand moving it.
|
|
43582
|
+
const isRowTaken = row => row.hasAttribute("data-route-row-taken");
|
|
43455
43583
|
const afterFrames = (count, callback) => {
|
|
43456
43584
|
if (count === 0) {
|
|
43457
43585
|
callback();
|
|
@@ -70783,15 +70911,6 @@ const lineAreas = (slideElements, layout) => {
|
|
|
70783
70911
|
return parseAreas(layout === "column" ? names : [names.join(" ")]);
|
|
70784
70912
|
};
|
|
70785
70913
|
|
|
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
70914
|
// What asked for a travel, read off the event that carried it: the hand said
|
|
70796
70915
|
// "somewhere over there" (a gesture one browses with), or it said a name (a tab
|
|
70797
70916
|
// pressed, a key, a command — a place aimed at). Nothing at all when the travel
|