@jsenv/navi 0.29.363 → 0.29.365
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 +311 -94
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +291 -89
- package/dist/jsenv_navi.js.map +6 -6
- package/docs/AI_INSTRUCTIONS.md +3 -1
- package/docs/popup_lift.md +72 -1
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -55432,6 +55432,10 @@ const SlideContainer = ({
|
|
|
55432
55432
|
// one being travelled TO while the track moves. What the next travel departs
|
|
55433
55433
|
// from, because it is what one is looking at.
|
|
55434
55434
|
const drawnAreaRef = useRef(undefined);
|
|
55435
|
+
// The movement the track is playing, while it plays: the slide it is leaving
|
|
55436
|
+
// and the slide it is going to. A hand landing on the box takes it over from
|
|
55437
|
+
// its origin (see onStart in createTravelHandlers).
|
|
55438
|
+
const movingRef = useRef(null);
|
|
55435
55439
|
// Which way the travel about to be drawn goes, when whatever asked for it
|
|
55436
55440
|
// knows: a window stepping off its last slide comes back on its first, and
|
|
55437
55441
|
// only the press says that is a step forward — the map, read between those
|
|
@@ -55693,6 +55697,7 @@ const SlideContainer = ({
|
|
|
55693
55697
|
}
|
|
55694
55698
|
stageRef.current = null;
|
|
55695
55699
|
trackAnimationRef.current = null;
|
|
55700
|
+
movingRef.current = null;
|
|
55696
55701
|
// At rest the picture IS the current slide, whatever the last gesture wrote
|
|
55697
55702
|
// there: an indicator has nothing left to lean towards.
|
|
55698
55703
|
paintTravelProgress(0);
|
|
@@ -55887,7 +55892,7 @@ const SlideContainer = ({
|
|
|
55887
55892
|
// there, and when it is over this is what holds. Except under a hand-off
|
|
55888
55893
|
// still waiting for its travel: the track stays where the finger left it,
|
|
55889
55894
|
// until the render that moves it or the frame after which the gesture gives
|
|
55890
|
-
// up on it (see
|
|
55895
|
+
// up on it (see settleTowards in onEnd).
|
|
55891
55896
|
if (!travelFromRef.current) {
|
|
55892
55897
|
track.style.setProperty("--slide-container-offset", offset);
|
|
55893
55898
|
}
|
|
@@ -55922,6 +55927,10 @@ const SlideContainer = ({
|
|
|
55922
55927
|
duration: durationMs * travelRatio,
|
|
55923
55928
|
easing
|
|
55924
55929
|
});
|
|
55930
|
+
movingRef.current = {
|
|
55931
|
+
from: drawnArea,
|
|
55932
|
+
to: currentArea
|
|
55933
|
+
};
|
|
55925
55934
|
// The trait travels with the slides: from where the gesture left it when
|
|
55926
55935
|
// there was one, from a whole box away when the travel was asked for.
|
|
55927
55936
|
const progressFrom = travelProgressFromRef.current ?? (travelStep ? travelStep.x || travelStep.y : 0);
|
|
@@ -56707,7 +56716,7 @@ const SlideContainer = ({
|
|
|
56707
56716
|
// The two slides the gesture can bring in, placed one box either side of the
|
|
56708
56717
|
// one being dragged — the same stage a travel builds, except that both ends
|
|
56709
56718
|
// are set up at once because the finger has not said yet which way it goes.
|
|
56710
|
-
const stageDrag = drag => {
|
|
56719
|
+
const stageDrag = (drag, currentArea) => {
|
|
56711
56720
|
const {
|
|
56712
56721
|
slideElements,
|
|
56713
56722
|
placeOf
|
|
@@ -56735,7 +56744,7 @@ const SlideContainer = ({
|
|
|
56735
56744
|
}
|
|
56736
56745
|
stageRef.current = {
|
|
56737
56746
|
placeByArea,
|
|
56738
|
-
area:
|
|
56747
|
+
area: currentArea
|
|
56739
56748
|
};
|
|
56740
56749
|
for (const slideElement of slideElements) {
|
|
56741
56750
|
const area = readArea(slideElement);
|
|
@@ -56751,33 +56760,63 @@ const SlideContainer = ({
|
|
|
56751
56760
|
}
|
|
56752
56761
|
};
|
|
56753
56762
|
|
|
56754
|
-
// Let go of
|
|
56755
|
-
//
|
|
56756
|
-
//
|
|
56757
|
-
|
|
56763
|
+
// Let go of, and the slides go where the gesture leaves them: back on the
|
|
56764
|
+
// slide being dragged when nothing travels, on to the slide arriving when a
|
|
56765
|
+
// travel caught in flight is let carry on. Over the distance left to cover,
|
|
56766
|
+
// so a slide barely moved snaps back and one dragged most of the way there
|
|
56767
|
+
// takes its time.
|
|
56768
|
+
const settleTowards = (drag, area) => {
|
|
56758
56769
|
const track = trackRef.current;
|
|
56759
56770
|
if (!track) {
|
|
56760
56771
|
return;
|
|
56761
56772
|
}
|
|
56762
|
-
const
|
|
56773
|
+
const stage = stageRef.current;
|
|
56774
|
+
const {
|
|
56775
|
+
placeOf
|
|
56776
|
+
} = readMap();
|
|
56777
|
+
const place = stage?.placeByArea.get(area) || placeOf.get(area) || {
|
|
56778
|
+
x: 0,
|
|
56779
|
+
y: 0
|
|
56780
|
+
};
|
|
56781
|
+
const restPx = {
|
|
56782
|
+
x: -place.x * drag.box.width,
|
|
56783
|
+
y: -place.y * drag.box.height
|
|
56784
|
+
};
|
|
56785
|
+
const nowPx = {
|
|
56786
|
+
x: drag.baseOffset.x + drag.pull.x,
|
|
56787
|
+
y: drag.baseOffset.y + drag.pull.y
|
|
56788
|
+
};
|
|
56789
|
+
const restOffset = `${restPx.x}px ${restPx.y}px`;
|
|
56763
56790
|
const durationMs = durationToMs(duration);
|
|
56764
|
-
const pulled = Math.abs(drag.pull[drag.axis]);
|
|
56765
56791
|
const size = drag.axis === "x" ? drag.box.width : drag.box.height;
|
|
56792
|
+
const left = Math.abs(restPx[drag.axis] - nowPx[drag.axis]);
|
|
56766
56793
|
trackAnimationRef.current?.cancel();
|
|
56767
56794
|
trackAnimationRef.current = null;
|
|
56768
56795
|
track.style.setProperty("--slide-container-offset", restOffset);
|
|
56769
|
-
if (!durationMs || !
|
|
56796
|
+
if (!durationMs || !left) {
|
|
56770
56797
|
paintTravelProgress(0);
|
|
56771
56798
|
settleTravel();
|
|
56772
56799
|
return;
|
|
56773
56800
|
}
|
|
56774
|
-
|
|
56801
|
+
// Which other slide the picture leans on while it arrives: the one being
|
|
56802
|
+
// pulled in when it comes back on the dragged slide, the dragged slide
|
|
56803
|
+
// itself when it goes on to the one arriving — and the trait is then a
|
|
56804
|
+
// box short of that one, which is what it has to close.
|
|
56805
|
+
const arrivesOnBase = area === drag.area;
|
|
56806
|
+
const sign = drag.pull[drag.axis] > 0 ? 1 : -1;
|
|
56807
|
+
const progressFrom = arrivesOnBase ? drag.progress : drag.progress - sign;
|
|
56808
|
+
const otherArea = arrivesOnBase ? drag.areaPulled : drag.area;
|
|
56809
|
+
movingRef.current = {
|
|
56810
|
+
from: otherArea,
|
|
56811
|
+
to: area
|
|
56812
|
+
};
|
|
56813
|
+
animateTravelProgress(progressFrom, durationMs * (left / size), "ease-out", otherArea);
|
|
56775
56814
|
const animation = track.animate([{
|
|
56776
|
-
translate:
|
|
56815
|
+
translate: `${nowPx.x}px ${nowPx.y}px`
|
|
56777
56816
|
}, {
|
|
56778
56817
|
translate: restOffset
|
|
56779
56818
|
}], {
|
|
56780
|
-
duration: durationMs * (
|
|
56819
|
+
duration: durationMs * (left / size),
|
|
56781
56820
|
easing: "ease-out"
|
|
56782
56821
|
});
|
|
56783
56822
|
trackAnimationRef.current = animation;
|
|
@@ -56808,11 +56847,14 @@ const SlideContainer = ({
|
|
|
56808
56847
|
// value underneath it, which is the far end of the travel — the very jump
|
|
56809
56848
|
// this is about.
|
|
56810
56849
|
trackElement.style.setProperty("--slide-container-offset", offsetOnScreen);
|
|
56850
|
+
const moving = movingRef.current || {};
|
|
56811
56851
|
return {
|
|
56812
56852
|
trackElement,
|
|
56813
56853
|
onScreenPx,
|
|
56814
56854
|
offsetOnScreen,
|
|
56815
|
-
offsetTarget: offsetRef.current
|
|
56855
|
+
offsetTarget: offsetRef.current,
|
|
56856
|
+
from: moving.from,
|
|
56857
|
+
to: moving.to
|
|
56816
56858
|
};
|
|
56817
56859
|
};
|
|
56818
56860
|
// Which way a caught travel was going: a hand reaching for something moving
|
|
@@ -56859,8 +56901,6 @@ const SlideContainer = ({
|
|
|
56859
56901
|
sign,
|
|
56860
56902
|
target
|
|
56861
56903
|
}) => {
|
|
56862
|
-
let areaBack = axis === "x" ? areaTowards(-1, 0) : areaTowards(0, -1);
|
|
56863
|
-
let areaOn = axis === "x" ? areaTowards(1, 0) : areaTowards(0, 1);
|
|
56864
56904
|
// Everything positional is read HERE rather than when the pointer
|
|
56865
56905
|
// landed: the travel that was playing then may have arrived since, and
|
|
56866
56906
|
// it is what the slides are doing at the moment the gesture takes them
|
|
@@ -56871,39 +56911,66 @@ const SlideContainer = ({
|
|
|
56871
56911
|
placeOf
|
|
56872
56912
|
} = readMap();
|
|
56873
56913
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
56874
|
-
// The hold goToArea reads at the release, read again HERE, off the same
|
|
56875
|
-
// slide and the same attribute: a slide that will refuse the arrival
|
|
56876
|
-
// must not offer the journey. A locked direction simply has nowhere to
|
|
56877
|
-
// go for the length of this gesture — the one case the gesture already
|
|
56878
|
-
// knows, being the last slide of a walk. The hand then gets the wall it
|
|
56879
|
-
// can lean on and never walk through (see drag_to_travel), the slide
|
|
56880
|
-
// behind it stays offstage instead of being read on the way, and the
|
|
56881
|
-
// release has nothing left to refuse.
|
|
56882
|
-
// Nothing here about `released` (--navi-done): that is one particular
|
|
56883
|
-
// departure letting go, decided as it happens, and a gesture armed
|
|
56884
|
-
// before it has no such thing to read — the attribute as rendered is
|
|
56885
|
-
// what the hand is answered from.
|
|
56886
|
-
if (currentElement?.hasAttribute("data-prevent-nav-previous")) {
|
|
56887
|
-
areaBack = undefined;
|
|
56888
|
-
}
|
|
56889
|
-
if (currentElement?.hasAttribute("data-prevent-nav-next")) {
|
|
56890
|
-
areaOn = undefined;
|
|
56891
|
-
}
|
|
56892
56914
|
const box = track.getBoundingClientRect();
|
|
56893
|
-
if (!
|
|
56894
|
-
// Something else with a better claim on the gesture: a scroller
|
|
56895
|
-
// between the finger and the slide, with room left that way.
|
|
56896
|
-
scrollRoomTowards(target, currentElement, axis, sign)) {
|
|
56915
|
+
if (!currentElement || !box.width || !box.height) {
|
|
56897
56916
|
return false;
|
|
56898
56917
|
}
|
|
56899
|
-
const
|
|
56900
|
-
// Where the slide being dragged stands: where the stage put it while a
|
|
56901
|
-
// travel is playing, its place on the map otherwise.
|
|
56918
|
+
const currentArea = readArea(currentElement);
|
|
56902
56919
|
const stage = stageRef.current;
|
|
56903
|
-
|
|
56920
|
+
// Where a slide stands: where the stage put it while a travel is
|
|
56921
|
+
// playing, its place on the map otherwise.
|
|
56922
|
+
const placeOfArea = someArea => stage?.placeByArea.get(someArea) || placeOf.get(someArea) || {
|
|
56904
56923
|
x: 0,
|
|
56905
56924
|
y: 0
|
|
56906
56925
|
};
|
|
56926
|
+
// A travel caught in flight is measured from the slide it SET OFF from,
|
|
56927
|
+
// with the slide it was going to as the one thing the hand can bring
|
|
56928
|
+
// in — the way drag_to_travel reads a `slack`: how far the box already
|
|
56929
|
+
// sits from its resting place, towards what is being pulled in. That is
|
|
56930
|
+
// what makes its answer "carry on" mean arriving. Based on the slide
|
|
56931
|
+
// arriving instead, the slack would point back at the origin, and a
|
|
56932
|
+
// hand that merely touched the box would send it all the way back.
|
|
56933
|
+
// A movement with no origin of its own (the track re-placed without a
|
|
56934
|
+
// travel) is dragged from the slide shown, like any slide at rest.
|
|
56935
|
+
const origin = caughtTravel && caughtTravel.from !== caughtTravel.to ? caughtTravel.from : null;
|
|
56936
|
+
let area;
|
|
56937
|
+
let areaBack;
|
|
56938
|
+
let areaOn;
|
|
56939
|
+
if (origin) {
|
|
56940
|
+
area = origin;
|
|
56941
|
+
const towardsEnd = axis === "x" ? placeOfArea(currentArea).x > placeOfArea(origin).x : placeOfArea(currentArea).y > placeOfArea(origin).y;
|
|
56942
|
+
areaBack = towardsEnd ? undefined : currentArea;
|
|
56943
|
+
areaOn = towardsEnd ? currentArea : undefined;
|
|
56944
|
+
} else {
|
|
56945
|
+
area = currentArea;
|
|
56946
|
+
areaBack = axis === "x" ? areaTowards(-1, 0) : areaTowards(0, -1);
|
|
56947
|
+
areaOn = axis === "x" ? areaTowards(1, 0) : areaTowards(0, 1);
|
|
56948
|
+
// The hold goToArea reads at the release, read again HERE, off the
|
|
56949
|
+
// same slide and the same attribute: a slide that will refuse the
|
|
56950
|
+
// arrival must not offer the journey. A locked direction simply has
|
|
56951
|
+
// nowhere to go for the length of this gesture — the one case the
|
|
56952
|
+
// gesture already knows, being the last slide of a walk. The hand
|
|
56953
|
+
// then gets the wall it can lean on and never walk through (see
|
|
56954
|
+
// drag_to_travel), the slide behind it stays offstage instead of
|
|
56955
|
+
// being read on the way, and the release has nothing left to refuse.
|
|
56956
|
+
// Nothing here about `released` (--navi-done): that is one particular
|
|
56957
|
+
// departure letting go, decided as it happens, and a gesture armed
|
|
56958
|
+
// before it has no such thing to read — the attribute as rendered is
|
|
56959
|
+
// what the hand is answered from.
|
|
56960
|
+
if (currentElement.hasAttribute("data-prevent-nav-previous")) {
|
|
56961
|
+
areaBack = undefined;
|
|
56962
|
+
}
|
|
56963
|
+
if (currentElement.hasAttribute("data-prevent-nav-next")) {
|
|
56964
|
+
areaOn = undefined;
|
|
56965
|
+
}
|
|
56966
|
+
if (!areaBack && !areaOn ||
|
|
56967
|
+
// Something else with a better claim on the gesture: a scroller
|
|
56968
|
+
// between the finger and the slide, with room left that way.
|
|
56969
|
+
scrollRoomTowards(target, currentElement, axis, sign)) {
|
|
56970
|
+
return false;
|
|
56971
|
+
}
|
|
56972
|
+
}
|
|
56973
|
+
const basePlace = placeOfArea(area);
|
|
56907
56974
|
const baseOffset = {
|
|
56908
56975
|
x: -basePlace.x * box.width,
|
|
56909
56976
|
y: -basePlace.y * box.height
|
|
@@ -56934,7 +57001,8 @@ const SlideContainer = ({
|
|
|
56934
57001
|
[axis]: slack
|
|
56935
57002
|
};
|
|
56936
57003
|
drag.progress = slack / size;
|
|
56937
|
-
|
|
57004
|
+
drag.areaPulled = slack > 0 ? areaBack : slack < 0 ? areaOn : null;
|
|
57005
|
+
stageDrag(drag, currentArea);
|
|
56938
57006
|
// From here the box is busy, whichever input asked: a wheel gesture and
|
|
56939
57007
|
// a press must not both be moving the same track.
|
|
56940
57008
|
dragRef.current = drag;
|
|
@@ -56965,24 +57033,50 @@ const SlideContainer = ({
|
|
|
56965
57033
|
event
|
|
56966
57034
|
}) => {
|
|
56967
57035
|
dragRef.current = null;
|
|
56968
|
-
|
|
56969
|
-
|
|
57036
|
+
// Where the gesture leaves the slides: on the one it was pulling in
|
|
57037
|
+
// when it says so, back on the one it was dragging otherwise.
|
|
57038
|
+
const destination = travels ? sign > 0 ? drag.areaBack : drag.areaOn : drag.area;
|
|
57039
|
+
const {
|
|
57040
|
+
slideElements,
|
|
57041
|
+
placeOf
|
|
57042
|
+
} = readMap();
|
|
57043
|
+
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
57044
|
+
const currentArea = currentElement && readArea(currentElement);
|
|
57045
|
+
if (!destination || destination === currentArea) {
|
|
57046
|
+
// Nothing to ask of the state: the slide it names is already the one
|
|
57047
|
+
// shown — a drag let go of short of a travel, a travel caught in
|
|
57048
|
+
// flight and let carry on. The track goes there on its own.
|
|
57049
|
+
settleTowards(drag, destination || drag.area);
|
|
56970
57050
|
return;
|
|
56971
57051
|
}
|
|
56972
57052
|
// Where the slide is being left, for the travel to depart from instead
|
|
56973
57053
|
// of from the map.
|
|
56974
57054
|
travelFromRef.current = drag.offset;
|
|
56975
57055
|
// …and where the indicator is being left, said about the slide that is
|
|
56976
|
-
// ARRIVING:
|
|
56977
|
-
//
|
|
56978
|
-
travelProgressFromRef.current = drag.progress - sign;
|
|
56979
|
-
|
|
57056
|
+
// ARRIVING: a box short of the slide being pulled in, right where the
|
|
57057
|
+
// finger left it when going back to the slide being dragged.
|
|
57058
|
+
travelProgressFromRef.current = travels ? drag.progress - sign : drag.progress;
|
|
57059
|
+
// Which way the travel goes, read off where the two slides stand
|
|
57060
|
+
// rather than off the pull: a travel caught in flight and put back
|
|
57061
|
+
// goes the way it came, whatever the finger did meanwhile.
|
|
57062
|
+
const stage = stageRef.current;
|
|
57063
|
+
const placeOfArea = someArea => stage?.placeByArea.get(someArea) || placeOf.get(someArea) || {
|
|
57064
|
+
x: 0,
|
|
57065
|
+
y: 0
|
|
57066
|
+
};
|
|
57067
|
+
const towards = Math.sign(placeOfArea(destination)[axis] - placeOfArea(currentArea)[axis]);
|
|
57068
|
+
const moved = goToArea(destination, {
|
|
57069
|
+
forward: towards > 0,
|
|
57070
|
+
event,
|
|
57071
|
+
dx: axis === "x" ? towards : 0,
|
|
57072
|
+
dy: axis === "y" ? towards : 0
|
|
57073
|
+
});
|
|
56980
57074
|
if (!moved) {
|
|
56981
57075
|
// Nowhere to go after all — a slide holding on to the user
|
|
56982
57076
|
// (preventNav), or a caller that refused the change.
|
|
56983
57077
|
travelFromRef.current = null;
|
|
56984
57078
|
travelProgressFromRef.current = null;
|
|
56985
|
-
|
|
57079
|
+
settleTowards(drag, currentArea);
|
|
56986
57080
|
return;
|
|
56987
57081
|
}
|
|
56988
57082
|
// A container whose `current` is held outside and was not moved: no
|
|
@@ -56993,7 +57087,7 @@ const SlideContainer = ({
|
|
|
56993
57087
|
if (travelFromRef.current) {
|
|
56994
57088
|
travelFromRef.current = null;
|
|
56995
57089
|
travelProgressFromRef.current = null;
|
|
56996
|
-
|
|
57090
|
+
settleTowards(drag, currentArea);
|
|
56997
57091
|
}
|
|
56998
57092
|
});
|
|
56999
57093
|
},
|
|
@@ -57008,9 +57102,15 @@ const SlideContainer = ({
|
|
|
57008
57102
|
trackElement: trackCaught,
|
|
57009
57103
|
offsetOnScreen,
|
|
57010
57104
|
offsetTarget,
|
|
57011
|
-
onScreenPx: caughtOnScreenPx
|
|
57105
|
+
onScreenPx: caughtOnScreenPx,
|
|
57106
|
+
from,
|
|
57107
|
+
to
|
|
57012
57108
|
} = caughtTravel;
|
|
57013
57109
|
caughtTravel = null;
|
|
57110
|
+
movingRef.current = {
|
|
57111
|
+
from,
|
|
57112
|
+
to
|
|
57113
|
+
};
|
|
57014
57114
|
if (offsetOnScreen === offsetTarget) {
|
|
57015
57115
|
settleTravel();
|
|
57016
57116
|
return;
|
|
@@ -59290,6 +59390,13 @@ const clipOf = (side, distance) => {
|
|
|
59290
59390
|
* (TARGET_WAIT_MS), on the half-strength frame where the anchor is still
|
|
59291
59391
|
* readable, and lifts the moment it is there.
|
|
59292
59392
|
*
|
|
59393
|
+
* A closing may land where no opening took off: a dialog opened the plain way
|
|
59394
|
+
* (`animation={{ open, close: "lifting" }}` in dialog.jsx), closing into a box the close
|
|
59395
|
+
* itself brings — the state `onClose` writes renders the place the lifted node
|
|
59396
|
+
* belongs to. So the box a closing comes back to is read once the change is
|
|
59397
|
+
* made, inside the transition, and when the caller named it (`liftAnchor`) it
|
|
59398
|
+
* is waited for (LANDING_WAIT_MS): the new picture is taken once it is there.
|
|
59399
|
+
*
|
|
59293
59400
|
* One name serves the whole movement, because only one of the two boxes is on
|
|
59294
59401
|
* screen at a time: it names the anchor while the popup is closed, and the
|
|
59295
59402
|
* lifted node while it is open.
|
|
@@ -59332,6 +59439,12 @@ const ARRIVING_ATTRIBUTE = "data-navi-popup-lift-arriving";
|
|
|
59332
59439
|
// stands, without a movement, so a target that never comes cannot keep it
|
|
59333
59440
|
// unpainted.
|
|
59334
59441
|
const TARGET_WAIT_MS = 1000;
|
|
59442
|
+
// How long a closing waits for the box it comes back to, when that box is
|
|
59443
|
+
// brought by the close (see this file's top comment). The page is frozen on
|
|
59444
|
+
// the picture of the open popup meanwhile, so the wait is short: what it
|
|
59445
|
+
// covers is a render, not a fetch. Past it the popup's picture plays out on
|
|
59446
|
+
// its own.
|
|
59447
|
+
const LANDING_WAIT_MS = 300;
|
|
59335
59448
|
// The popup's own animation duration, published on the root because the
|
|
59336
59449
|
// ::view-transition tree hangs off it and inherits from nowhere else.
|
|
59337
59450
|
const DURATION_PROPERTY = "--navi-popup-lift-duration";
|
|
@@ -59372,14 +59485,16 @@ let releaseScrollHold = null;
|
|
|
59372
59485
|
* a view transition morphing the anchor's box into the lifted node's, or back.
|
|
59373
59486
|
*
|
|
59374
59487
|
* `opened` says which way: the box being left is the anchor when the popup is
|
|
59375
|
-
* opening and the lifted node when it is closing. `
|
|
59376
|
-
*
|
|
59488
|
+
* opening and the lifted node when it is closing. `resolveAnchor` is read on
|
|
59489
|
+
* the spot for an opening, and once the change is made for a closing;
|
|
59490
|
+
* `waitForAnchor` has a closing wait for it when it is not there yet. `lift`
|
|
59491
|
+
* is Dialog's own prop of that name.
|
|
59377
59492
|
*/
|
|
59378
59493
|
const liftPopupFromAnchor = (
|
|
59379
59494
|
popupEl,
|
|
59380
|
-
|
|
59495
|
+
resolveAnchor,
|
|
59381
59496
|
applyChange,
|
|
59382
|
-
{ opened, lift },
|
|
59497
|
+
{ opened, lift, waitForAnchor },
|
|
59383
59498
|
) => {
|
|
59384
59499
|
const startViewTransition = ensureDocumentStartViewTransition();
|
|
59385
59500
|
// A movement still wearing the name would make the name two elements wide,
|
|
@@ -59387,7 +59502,7 @@ const liftPopupFromAnchor = (
|
|
|
59387
59502
|
// document.
|
|
59388
59503
|
releaseLiftInProgress?.();
|
|
59389
59504
|
|
|
59390
|
-
const elementLeaving = opened ?
|
|
59505
|
+
const elementLeaving = opened ? resolveAnchor() : resolveLiftTarget(popupEl);
|
|
59391
59506
|
// Read before the first write: the read brings the style up to date, and a
|
|
59392
59507
|
// write before it would make it bring it up to date once more.
|
|
59393
59508
|
const duration = getComputedStyle(popupEl)
|
|
@@ -59411,12 +59526,14 @@ const liftPopupFromAnchor = (
|
|
|
59411
59526
|
|
|
59412
59527
|
let giveBackNameArriving = null;
|
|
59413
59528
|
let stopWaitingForTarget = null;
|
|
59529
|
+
let stopWaitingForAnchor = null;
|
|
59414
59530
|
const release = () => {
|
|
59415
59531
|
if (releaseLiftInProgress !== release) {
|
|
59416
59532
|
return;
|
|
59417
59533
|
}
|
|
59418
59534
|
releaseLiftInProgress = null;
|
|
59419
59535
|
stopWaitingForTarget?.();
|
|
59536
|
+
stopWaitingForAnchor?.();
|
|
59420
59537
|
boxAnimationInProgress?.cancel();
|
|
59421
59538
|
boxAnimationInProgress = null;
|
|
59422
59539
|
releaseScrollHold?.();
|
|
@@ -59442,13 +59559,18 @@ const liftPopupFromAnchor = (
|
|
|
59442
59559
|
const boxLeaving = room ? elementLeaving.getBoundingClientRect() : null;
|
|
59443
59560
|
let boxArriving = null;
|
|
59444
59561
|
let cornersArriving = null;
|
|
59445
|
-
const viewTransition = startViewTransition(() => {
|
|
59562
|
+
const viewTransition = startViewTransition(async () => {
|
|
59446
59563
|
// The name is the arriving box's from here on: worn by both, it is worn
|
|
59447
59564
|
// by neither. Written rather than removed, so a name the element also
|
|
59448
59565
|
// has from a stylesheet cannot resurface for the length of the movement.
|
|
59449
59566
|
elementLeaving.style.setProperty(NAME_PROPERTY, "none");
|
|
59450
59567
|
change();
|
|
59451
|
-
const elementArriving = resolveElementArriving();
|
|
59568
|
+
const elementArriving = await resolveElementArriving();
|
|
59569
|
+
// Replaced while waiting for its landing: the movement replacing it
|
|
59570
|
+
// holds the name now.
|
|
59571
|
+
if (releaseLiftInProgress !== release) {
|
|
59572
|
+
return;
|
|
59573
|
+
}
|
|
59452
59574
|
if (elementArriving) {
|
|
59453
59575
|
giveBackNameArriving = wearLiftName(elementArriving);
|
|
59454
59576
|
cornersArriving = readCorners(elementArriving);
|
|
@@ -59473,12 +59595,24 @@ const liftPopupFromAnchor = (
|
|
|
59473
59595
|
};
|
|
59474
59596
|
|
|
59475
59597
|
if (!opened) {
|
|
59476
|
-
startMovement(applyChange, () =>
|
|
59598
|
+
startMovement(applyChange, () => {
|
|
59599
|
+
const anchorElement = resolveAnchor();
|
|
59600
|
+
if (anchorElement?.isConnected) {
|
|
59601
|
+
return anchorElement;
|
|
59602
|
+
}
|
|
59477
59603
|
// Gone from the document while the popup was open (the row it stood in
|
|
59478
59604
|
// was removed): nothing to arrive at, and the browser plays the popup's
|
|
59479
59605
|
// picture out on its own.
|
|
59480
|
-
|
|
59481
|
-
|
|
59606
|
+
if (!waitForAnchor) {
|
|
59607
|
+
return null;
|
|
59608
|
+
}
|
|
59609
|
+
return new Promise((resolve) => {
|
|
59610
|
+
stopWaitingForAnchor = whenAnchorAppears(resolveAnchor, (element) => {
|
|
59611
|
+
stopWaitingForAnchor = null;
|
|
59612
|
+
resolve(element);
|
|
59613
|
+
});
|
|
59614
|
+
});
|
|
59615
|
+
});
|
|
59482
59616
|
return;
|
|
59483
59617
|
}
|
|
59484
59618
|
|
|
@@ -59612,6 +59746,40 @@ const whenLiftTargetAppears = (popupEl, callback) => {
|
|
|
59612
59746
|
return stop;
|
|
59613
59747
|
};
|
|
59614
59748
|
|
|
59749
|
+
// Calls `callback` with the anchor once `resolveAnchor` finds it in the
|
|
59750
|
+
// document, or with null past LANDING_WAIT_MS — or when stopped, since the
|
|
59751
|
+
// transition's update is waiting on it. Returns how to stop.
|
|
59752
|
+
const whenAnchorAppears = (resolveAnchor, callback) => {
|
|
59753
|
+
const observer = new MutationObserver(() => {
|
|
59754
|
+
const anchorElement = resolveAnchor();
|
|
59755
|
+
if (anchorElement?.isConnected) {
|
|
59756
|
+
stop(anchorElement);
|
|
59757
|
+
}
|
|
59758
|
+
});
|
|
59759
|
+
observer.observe(document.documentElement, {
|
|
59760
|
+
childList: true,
|
|
59761
|
+
subtree: true,
|
|
59762
|
+
attributes: true,
|
|
59763
|
+
attributeFilter: ["id"],
|
|
59764
|
+
});
|
|
59765
|
+
const timeout = setTimeout(() => {
|
|
59766
|
+
stop(null);
|
|
59767
|
+
}, LANDING_WAIT_MS);
|
|
59768
|
+
let stopped = false;
|
|
59769
|
+
const stop = (anchorElement = null) => {
|
|
59770
|
+
if (stopped) {
|
|
59771
|
+
return;
|
|
59772
|
+
}
|
|
59773
|
+
stopped = true;
|
|
59774
|
+
observer.disconnect();
|
|
59775
|
+
clearTimeout(timeout);
|
|
59776
|
+
callback(anchorElement);
|
|
59777
|
+
};
|
|
59778
|
+
return () => {
|
|
59779
|
+
stop(null);
|
|
59780
|
+
};
|
|
59781
|
+
};
|
|
59782
|
+
|
|
59615
59783
|
const ignore = () => {};
|
|
59616
59784
|
|
|
59617
59785
|
// The room the fixed bars leave, in viewport coordinates; null without bars.
|
|
@@ -60277,6 +60445,18 @@ const css$E = /* css */`
|
|
|
60277
60445
|
opacity: 0;
|
|
60278
60446
|
}
|
|
60279
60447
|
|
|
60448
|
+
/* A closing lift is the dialog leaving as a picture: whatever exit its
|
|
60449
|
+
opening animation arms (animation={{ open, close: "lifting" }}, see
|
|
60450
|
+
popup_css.js) would
|
|
60451
|
+
keep it rendered into the picture of the state it closes into. */
|
|
60452
|
+
:root[data-navi-popup-lift="closing"] {
|
|
60453
|
+
.navi_dialog,
|
|
60454
|
+
.navi_dialog::backdrop,
|
|
60455
|
+
.navi_dialog_backdrop {
|
|
60456
|
+
transition: none;
|
|
60457
|
+
}
|
|
60458
|
+
}
|
|
60459
|
+
|
|
60280
60460
|
/* While a dialog is lifting out of the element that opened it
|
|
60281
60461
|
(popup_lift.js). The page around IS taken as a picture, the browser's own
|
|
60282
60462
|
default, and on purpose: the wall and what the dialog holds around the
|
|
@@ -60519,7 +60699,7 @@ const css$E = /* css */`
|
|
|
60519
60699
|
* scroll while open (its backdrop only covers the scrollport, so scrolling
|
|
60520
60700
|
* there would reveal uncovered content); this prop extends the lock to the
|
|
60521
60701
|
* whole page. Defaults to `true` for a dialog docked by `dockedOnSmallTouchScreen`.
|
|
60522
|
-
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"lifting"|`slide-from-${string}
|
|
60702
|
+
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"lifting"|`slide-from-${string}`|{open: boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`, close: "lifting"}} [props.animation]
|
|
60523
60703
|
* - `true`/`"auto"` resolves to `"scaling"` for a centered `positionArea`,
|
|
60524
60704
|
* or a concrete `"slide-from-*"` direction otherwise. Any other explicit
|
|
60525
60705
|
* value is used as-is. `"lifting"` is the odd one out: every other kind
|
|
@@ -60538,6 +60718,15 @@ const css$E = /* css */`
|
|
|
60538
60718
|
* being what the movement leaves rather than a context to keep readable;
|
|
60539
60719
|
* `backdropVariant="discrete"` asks for the light wash back. See
|
|
60540
60720
|
* `popup_lift.js`.
|
|
60721
|
+
* - `{ open, close: "lifting" }`: the close alone lifts. The dialog opens
|
|
60722
|
+
* with `open` (any value above but `"lifting"`), and its `data-lift` node
|
|
60723
|
+
* travels into `liftAnchor` on close — for a dialog that did not come out
|
|
60724
|
+
* of what it lands in (a banner opens a full-screen reveal, closing it puts
|
|
60725
|
+
* the crest in its place on the plate that replaces the banner). The box it
|
|
60726
|
+
* lands in may be rendered by the close itself: `liftAnchor` is read once
|
|
60727
|
+
* `onClose` has run, and waited for a moment when it is not there yet.
|
|
60728
|
+
* `"lifting"` is the only `close` that differs from the opening: every
|
|
60729
|
+
* other kind closes by playing its opening backwards.
|
|
60541
60730
|
* @param {"box"|"scene"} [props.lift="box"] - Under `animation="lifting"`,
|
|
60542
60731
|
* what the anchor and what it becomes are to each other, which decides
|
|
60543
60732
|
* how their pictures sit in the box moving between them. `"box"`: one
|
|
@@ -60562,14 +60751,15 @@ const css$E = /* css */`
|
|
|
60562
60751
|
* `document.getElementById` when the dialog opens — see popover.jsx's own
|
|
60563
60752
|
* `anchor` doc for why (mainly `defaultOpen`).
|
|
60564
60753
|
* @param {Element|{current: Element}|string} [props.liftAnchor] - Under
|
|
60565
|
-
* `animation="lifting"
|
|
60566
|
-
* is
|
|
60567
|
-
* shown one at a time) has something else in
|
|
60568
|
-
* and the box would otherwise fly back to the
|
|
60569
|
-
* grammar as `anchor` (element, ref or id),
|
|
60570
|
-
*
|
|
60571
|
-
*
|
|
60572
|
-
* opening. Left out, the box comes back to the anchor
|
|
60754
|
+
* `animation="lifting"` or `animation={{ open, close: "lifting" }}`, where the closing
|
|
60755
|
+
* brings the box back to, when that is not where it came from: a popup one
|
|
60756
|
+
* walks through (a row of cards shown one at a time) has something else in
|
|
60757
|
+
* front by the time it closes, and the box would otherwise fly back to the
|
|
60758
|
+
* card the press opened on. Same grammar as `anchor` (element, ref or id),
|
|
60759
|
+
* resolved once the close is made — after `onClose` — so whatever names the
|
|
60760
|
+
* card currently in front, or the box the close itself renders, is read
|
|
60761
|
+
* then and not at the opening. Left out, the box comes back to the anchor
|
|
60762
|
+
* it came out of.
|
|
60573
60763
|
* @param {boolean} [props.sizeFromAnchor=false] - Whether the dialog takes the
|
|
60574
60764
|
* anchor's width/height as a min-width/min-height floor
|
|
60575
60765
|
* (`--anchor-width`/`--anchor-height`). Off by default: unlike a popover,
|
|
@@ -61052,7 +61242,15 @@ const useDialogProps = props => {
|
|
|
61052
61242
|
flushEdges.left = expandX || x === "left" || x === "inset-left";
|
|
61053
61243
|
flushEdges.right = expandX || x === "right" || x === "inset-right";
|
|
61054
61244
|
}
|
|
61055
|
-
|
|
61245
|
+
|
|
61246
|
+
// `{ open, close }` says each way on its own; a single value says both.
|
|
61247
|
+
const {
|
|
61248
|
+
open: openAnimation,
|
|
61249
|
+
close: closeAnimation = openAnimation
|
|
61250
|
+
} = animation !== null && typeof animation === "object" ? animation : {
|
|
61251
|
+
open: animation
|
|
61252
|
+
};
|
|
61253
|
+
const isAutoAnimation = openAnimation === true || openAnimation === "auto";
|
|
61056
61254
|
// The dialog and the anchor are one box, and what plays between them is the
|
|
61057
61255
|
// browser's own morph (popup_lift.js) — nothing this dialog does to its own
|
|
61058
61256
|
// box. So it arms no CSS transition of its own, which is not merely useless
|
|
@@ -61060,11 +61258,12 @@ const useDialogProps = props => {
|
|
|
61060
61258
|
// with allow-discrete, and a dialog kept rendered for the length of its exit
|
|
61061
61259
|
// is exactly what the picture taken of the state it closes into must not
|
|
61062
61260
|
// show.
|
|
61063
|
-
const lifting =
|
|
61261
|
+
const lifting = openAnimation === "lifting";
|
|
61262
|
+
const liftsOnClose = closeAnimation === "lifting";
|
|
61064
61263
|
// Dialog never has a real anchor to POSITION against (see this file's top
|
|
61065
61264
|
// comment), so this is always the "no anchor" path — the same one Popover's
|
|
61066
61265
|
// own custom renderer falls into when it has no real anchor either.
|
|
61067
|
-
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) :
|
|
61266
|
+
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) : openAnimation;
|
|
61068
61267
|
// Not gated on isAutoAnimation — an explicit animation="sliding" needs a
|
|
61069
61268
|
// concrete direction just as much as an auto-resolved one does (same as
|
|
61070
61269
|
// Popover's own "sliding"/"expanding" resolution step in openEffect).
|
|
@@ -61113,13 +61312,11 @@ const useDialogProps = props => {
|
|
|
61113
61312
|
// is in front NOW, which only the caller knows. Resolved at the close for
|
|
61114
61313
|
// that reason: the element it names changes while the popup is open, so
|
|
61115
61314
|
// anything read at the opening would be the walk's starting point again.
|
|
61315
|
+
// Silent when it names nothing: the close may be what renders it, and
|
|
61316
|
+
// popup_lift.js asks again until it is there (and warns past that).
|
|
61116
61317
|
const resolveLiftAnchorElement = () => {
|
|
61117
61318
|
if (typeof liftAnchor === "string") {
|
|
61118
|
-
|
|
61119
|
-
if (!liftAnchorElementById) {
|
|
61120
|
-
console.warn(`Dialog: liftAnchor="${liftAnchor}" did not match any element`);
|
|
61121
|
-
}
|
|
61122
|
-
return liftAnchorElementById;
|
|
61319
|
+
return document.getElementById(liftAnchor);
|
|
61123
61320
|
}
|
|
61124
61321
|
// A ref is unwrapped even when it holds nothing, the same way `anchor` is:
|
|
61125
61322
|
// the ref object itself has no box to come back to.
|
|
@@ -61131,7 +61328,7 @@ const useDialogProps = props => {
|
|
|
61131
61328
|
// provided the change happens between its two pictures, which is what
|
|
61132
61329
|
// handing it to the controller buys (see popup_lift.js and
|
|
61133
61330
|
// open_controller.js's own transitionChange).
|
|
61134
|
-
openController.transitionChange =
|
|
61331
|
+
openController.transitionChange = liftsOnClose ? (applyChange, {
|
|
61135
61332
|
opened,
|
|
61136
61333
|
event
|
|
61137
61334
|
}) => {
|
|
@@ -61139,23 +61336,26 @@ const useDialogProps = props => {
|
|
|
61139
61336
|
// A mount-time opening was never seen closed (see openEffect's own
|
|
61140
61337
|
// `silent`): there is no box it comes from, because nothing was shown
|
|
61141
61338
|
// before it.
|
|
61142
|
-
if (!dialogEl || opened && event.detail.silent) {
|
|
61339
|
+
if (!dialogEl || opened && (!lifting || event.detail.silent)) {
|
|
61143
61340
|
applyChange();
|
|
61144
61341
|
return;
|
|
61145
61342
|
}
|
|
61146
|
-
|
|
61147
|
-
|
|
61148
|
-
|
|
61149
|
-
|
|
61150
|
-
|
|
61151
|
-
|
|
61152
|
-
|
|
61343
|
+
if (!opened) {
|
|
61344
|
+
// Read once the close is made (popup_lift.js): the box it lands in
|
|
61345
|
+
// may be one the close itself renders.
|
|
61346
|
+
liftPopupFromAnchor(dialogEl, liftAnchor ? resolveLiftAnchorElement : () => anchorElementRef.current, applyChange, {
|
|
61347
|
+
opened,
|
|
61348
|
+
lift,
|
|
61349
|
+
waitForAnchor: Boolean(liftAnchor)
|
|
61350
|
+
});
|
|
61351
|
+
return;
|
|
61153
61352
|
}
|
|
61353
|
+
const anchorElement = resolveAnchorElement(event);
|
|
61154
61354
|
if (!anchorElement) {
|
|
61155
61355
|
applyChange();
|
|
61156
61356
|
return;
|
|
61157
61357
|
}
|
|
61158
|
-
liftPopupFromAnchor(dialogEl, anchorElement, applyChange, {
|
|
61358
|
+
liftPopupFromAnchor(dialogEl, () => anchorElement, applyChange, {
|
|
61159
61359
|
opened,
|
|
61160
61360
|
lift
|
|
61161
61361
|
});
|
|
@@ -64496,6 +64696,7 @@ const PickerContentInsidePopup = props => {
|
|
|
64496
64696
|
popupWidthFitContent,
|
|
64497
64697
|
animation,
|
|
64498
64698
|
lift,
|
|
64699
|
+
liftAnchor,
|
|
64499
64700
|
animationDuration,
|
|
64500
64701
|
// mode="callout": what the callout says about what it holds, and paints
|
|
64501
64702
|
// in its border and icon — "none" for a plain tooltip (see the callout
|
|
@@ -64592,6 +64793,7 @@ const PickerContentInsidePopup = props => {
|
|
|
64592
64793
|
dockedOnSmallTouchScreen: isPopover ? undefined : dockedOnSmallTouchScreen,
|
|
64593
64794
|
sizeFromAnchor: isPopover ? undefined : dialogSizeFromAnchor,
|
|
64594
64795
|
lift: isPopover ? undefined : lift,
|
|
64796
|
+
liftAnchor: isPopover ? undefined : liftAnchor,
|
|
64595
64797
|
children: jsx(PopupModeContext.Provider, {
|
|
64596
64798
|
value: mode,
|
|
64597
64799
|
children: children
|