@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/dev/jsenv_navi.js
CHANGED
|
@@ -56555,6 +56555,10 @@ const SlideContainer = ({
|
|
|
56555
56555
|
// one being travelled TO while the track moves. What the next travel departs
|
|
56556
56556
|
// from, because it is what one is looking at.
|
|
56557
56557
|
const drawnAreaRef = useRef(undefined);
|
|
56558
|
+
// The movement the track is playing, while it plays: the slide it is leaving
|
|
56559
|
+
// and the slide it is going to. A hand landing on the box takes it over from
|
|
56560
|
+
// its origin (see onStart in createTravelHandlers).
|
|
56561
|
+
const movingRef = useRef(null);
|
|
56558
56562
|
// Which way the travel about to be drawn goes, when whatever asked for it
|
|
56559
56563
|
// knows: a window stepping off its last slide comes back on its first, and
|
|
56560
56564
|
// only the press says that is a step forward — the map, read between those
|
|
@@ -56823,6 +56827,7 @@ const SlideContainer = ({
|
|
|
56823
56827
|
}
|
|
56824
56828
|
stageRef.current = null;
|
|
56825
56829
|
trackAnimationRef.current = null;
|
|
56830
|
+
movingRef.current = null;
|
|
56826
56831
|
// At rest the picture IS the current slide, whatever the last gesture wrote
|
|
56827
56832
|
// there: an indicator has nothing left to lean towards.
|
|
56828
56833
|
paintTravelProgress(0);
|
|
@@ -57017,7 +57022,7 @@ const SlideContainer = ({
|
|
|
57017
57022
|
// there, and when it is over this is what holds. Except under a hand-off
|
|
57018
57023
|
// still waiting for its travel: the track stays where the finger left it,
|
|
57019
57024
|
// until the render that moves it or the frame after which the gesture gives
|
|
57020
|
-
// up on it (see
|
|
57025
|
+
// up on it (see settleTowards in onEnd).
|
|
57021
57026
|
if (!travelFromRef.current) {
|
|
57022
57027
|
track.style.setProperty("--slide-container-offset", offset);
|
|
57023
57028
|
}
|
|
@@ -57052,6 +57057,10 @@ const SlideContainer = ({
|
|
|
57052
57057
|
duration: durationMs * travelRatio,
|
|
57053
57058
|
easing
|
|
57054
57059
|
});
|
|
57060
|
+
movingRef.current = {
|
|
57061
|
+
from: drawnArea,
|
|
57062
|
+
to: currentArea
|
|
57063
|
+
};
|
|
57055
57064
|
// The trait travels with the slides: from where the gesture left it when
|
|
57056
57065
|
// there was one, from a whole box away when the travel was asked for.
|
|
57057
57066
|
const progressFrom = travelProgressFromRef.current ?? (travelStep ? travelStep.x || travelStep.y : 0);
|
|
@@ -57837,7 +57846,7 @@ const SlideContainer = ({
|
|
|
57837
57846
|
// The two slides the gesture can bring in, placed one box either side of the
|
|
57838
57847
|
// one being dragged — the same stage a travel builds, except that both ends
|
|
57839
57848
|
// are set up at once because the finger has not said yet which way it goes.
|
|
57840
|
-
const stageDrag = drag => {
|
|
57849
|
+
const stageDrag = (drag, currentArea) => {
|
|
57841
57850
|
const {
|
|
57842
57851
|
slideElements,
|
|
57843
57852
|
placeOf
|
|
@@ -57865,7 +57874,7 @@ const SlideContainer = ({
|
|
|
57865
57874
|
}
|
|
57866
57875
|
stageRef.current = {
|
|
57867
57876
|
placeByArea,
|
|
57868
|
-
area:
|
|
57877
|
+
area: currentArea
|
|
57869
57878
|
};
|
|
57870
57879
|
for (const slideElement of slideElements) {
|
|
57871
57880
|
const area = readArea(slideElement);
|
|
@@ -57881,33 +57890,63 @@ const SlideContainer = ({
|
|
|
57881
57890
|
}
|
|
57882
57891
|
};
|
|
57883
57892
|
|
|
57884
|
-
// Let go of
|
|
57885
|
-
//
|
|
57886
|
-
//
|
|
57887
|
-
|
|
57893
|
+
// Let go of, and the slides go where the gesture leaves them: back on the
|
|
57894
|
+
// slide being dragged when nothing travels, on to the slide arriving when a
|
|
57895
|
+
// travel caught in flight is let carry on. Over the distance left to cover,
|
|
57896
|
+
// so a slide barely moved snaps back and one dragged most of the way there
|
|
57897
|
+
// takes its time.
|
|
57898
|
+
const settleTowards = (drag, area) => {
|
|
57888
57899
|
const track = trackRef.current;
|
|
57889
57900
|
if (!track) {
|
|
57890
57901
|
return;
|
|
57891
57902
|
}
|
|
57892
|
-
const
|
|
57903
|
+
const stage = stageRef.current;
|
|
57904
|
+
const {
|
|
57905
|
+
placeOf
|
|
57906
|
+
} = readMap();
|
|
57907
|
+
const place = stage?.placeByArea.get(area) || placeOf.get(area) || {
|
|
57908
|
+
x: 0,
|
|
57909
|
+
y: 0
|
|
57910
|
+
};
|
|
57911
|
+
const restPx = {
|
|
57912
|
+
x: -place.x * drag.box.width,
|
|
57913
|
+
y: -place.y * drag.box.height
|
|
57914
|
+
};
|
|
57915
|
+
const nowPx = {
|
|
57916
|
+
x: drag.baseOffset.x + drag.pull.x,
|
|
57917
|
+
y: drag.baseOffset.y + drag.pull.y
|
|
57918
|
+
};
|
|
57919
|
+
const restOffset = `${restPx.x}px ${restPx.y}px`;
|
|
57893
57920
|
const durationMs = durationToMs(duration);
|
|
57894
|
-
const pulled = Math.abs(drag.pull[drag.axis]);
|
|
57895
57921
|
const size = drag.axis === "x" ? drag.box.width : drag.box.height;
|
|
57922
|
+
const left = Math.abs(restPx[drag.axis] - nowPx[drag.axis]);
|
|
57896
57923
|
trackAnimationRef.current?.cancel();
|
|
57897
57924
|
trackAnimationRef.current = null;
|
|
57898
57925
|
track.style.setProperty("--slide-container-offset", restOffset);
|
|
57899
|
-
if (!durationMs || !
|
|
57926
|
+
if (!durationMs || !left) {
|
|
57900
57927
|
paintTravelProgress(0);
|
|
57901
57928
|
settleTravel();
|
|
57902
57929
|
return;
|
|
57903
57930
|
}
|
|
57904
|
-
|
|
57931
|
+
// Which other slide the picture leans on while it arrives: the one being
|
|
57932
|
+
// pulled in when it comes back on the dragged slide, the dragged slide
|
|
57933
|
+
// itself when it goes on to the one arriving — and the trait is then a
|
|
57934
|
+
// box short of that one, which is what it has to close.
|
|
57935
|
+
const arrivesOnBase = area === drag.area;
|
|
57936
|
+
const sign = drag.pull[drag.axis] > 0 ? 1 : -1;
|
|
57937
|
+
const progressFrom = arrivesOnBase ? drag.progress : drag.progress - sign;
|
|
57938
|
+
const otherArea = arrivesOnBase ? drag.areaPulled : drag.area;
|
|
57939
|
+
movingRef.current = {
|
|
57940
|
+
from: otherArea,
|
|
57941
|
+
to: area
|
|
57942
|
+
};
|
|
57943
|
+
animateTravelProgress(progressFrom, durationMs * (left / size), "ease-out", otherArea);
|
|
57905
57944
|
const animation = track.animate([{
|
|
57906
|
-
translate:
|
|
57945
|
+
translate: `${nowPx.x}px ${nowPx.y}px`
|
|
57907
57946
|
}, {
|
|
57908
57947
|
translate: restOffset
|
|
57909
57948
|
}], {
|
|
57910
|
-
duration: durationMs * (
|
|
57949
|
+
duration: durationMs * (left / size),
|
|
57911
57950
|
easing: "ease-out"
|
|
57912
57951
|
});
|
|
57913
57952
|
trackAnimationRef.current = animation;
|
|
@@ -57938,11 +57977,14 @@ const SlideContainer = ({
|
|
|
57938
57977
|
// value underneath it, which is the far end of the travel — the very jump
|
|
57939
57978
|
// this is about.
|
|
57940
57979
|
trackElement.style.setProperty("--slide-container-offset", offsetOnScreen);
|
|
57980
|
+
const moving = movingRef.current || {};
|
|
57941
57981
|
return {
|
|
57942
57982
|
trackElement,
|
|
57943
57983
|
onScreenPx,
|
|
57944
57984
|
offsetOnScreen,
|
|
57945
|
-
offsetTarget: offsetRef.current
|
|
57985
|
+
offsetTarget: offsetRef.current,
|
|
57986
|
+
from: moving.from,
|
|
57987
|
+
to: moving.to
|
|
57946
57988
|
};
|
|
57947
57989
|
};
|
|
57948
57990
|
// Which way a caught travel was going: a hand reaching for something moving
|
|
@@ -57989,8 +58031,6 @@ const SlideContainer = ({
|
|
|
57989
58031
|
sign,
|
|
57990
58032
|
target
|
|
57991
58033
|
}) => {
|
|
57992
|
-
let areaBack = axis === "x" ? areaTowards(-1, 0) : areaTowards(0, -1);
|
|
57993
|
-
let areaOn = axis === "x" ? areaTowards(1, 0) : areaTowards(0, 1);
|
|
57994
58034
|
// Everything positional is read HERE rather than when the pointer
|
|
57995
58035
|
// landed: the travel that was playing then may have arrived since, and
|
|
57996
58036
|
// it is what the slides are doing at the moment the gesture takes them
|
|
@@ -58001,39 +58041,66 @@ const SlideContainer = ({
|
|
|
58001
58041
|
placeOf
|
|
58002
58042
|
} = readMap();
|
|
58003
58043
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
58004
|
-
// The hold goToArea reads at the release, read again HERE, off the same
|
|
58005
|
-
// slide and the same attribute: a slide that will refuse the arrival
|
|
58006
|
-
// must not offer the journey. A locked direction simply has nowhere to
|
|
58007
|
-
// go for the length of this gesture — the one case the gesture already
|
|
58008
|
-
// knows, being the last slide of a walk. The hand then gets the wall it
|
|
58009
|
-
// can lean on and never walk through (see drag_to_travel), the slide
|
|
58010
|
-
// behind it stays offstage instead of being read on the way, and the
|
|
58011
|
-
// release has nothing left to refuse.
|
|
58012
|
-
// Nothing here about `released` (--navi-done): that is one particular
|
|
58013
|
-
// departure letting go, decided as it happens, and a gesture armed
|
|
58014
|
-
// before it has no such thing to read — the attribute as rendered is
|
|
58015
|
-
// what the hand is answered from.
|
|
58016
|
-
if (currentElement?.hasAttribute("data-prevent-nav-previous")) {
|
|
58017
|
-
areaBack = undefined;
|
|
58018
|
-
}
|
|
58019
|
-
if (currentElement?.hasAttribute("data-prevent-nav-next")) {
|
|
58020
|
-
areaOn = undefined;
|
|
58021
|
-
}
|
|
58022
58044
|
const box = track.getBoundingClientRect();
|
|
58023
|
-
if (!
|
|
58024
|
-
// Something else with a better claim on the gesture: a scroller
|
|
58025
|
-
// between the finger and the slide, with room left that way.
|
|
58026
|
-
scrollRoomTowards(target, currentElement, axis, sign)) {
|
|
58045
|
+
if (!currentElement || !box.width || !box.height) {
|
|
58027
58046
|
return false;
|
|
58028
58047
|
}
|
|
58029
|
-
const
|
|
58030
|
-
// Where the slide being dragged stands: where the stage put it while a
|
|
58031
|
-
// travel is playing, its place on the map otherwise.
|
|
58048
|
+
const currentArea = readArea(currentElement);
|
|
58032
58049
|
const stage = stageRef.current;
|
|
58033
|
-
|
|
58050
|
+
// Where a slide stands: where the stage put it while a travel is
|
|
58051
|
+
// playing, its place on the map otherwise.
|
|
58052
|
+
const placeOfArea = someArea => stage?.placeByArea.get(someArea) || placeOf.get(someArea) || {
|
|
58034
58053
|
x: 0,
|
|
58035
58054
|
y: 0
|
|
58036
58055
|
};
|
|
58056
|
+
// A travel caught in flight is measured from the slide it SET OFF from,
|
|
58057
|
+
// with the slide it was going to as the one thing the hand can bring
|
|
58058
|
+
// in — the way drag_to_travel reads a `slack`: how far the box already
|
|
58059
|
+
// sits from its resting place, towards what is being pulled in. That is
|
|
58060
|
+
// what makes its answer "carry on" mean arriving. Based on the slide
|
|
58061
|
+
// arriving instead, the slack would point back at the origin, and a
|
|
58062
|
+
// hand that merely touched the box would send it all the way back.
|
|
58063
|
+
// A movement with no origin of its own (the track re-placed without a
|
|
58064
|
+
// travel) is dragged from the slide shown, like any slide at rest.
|
|
58065
|
+
const origin = caughtTravel && caughtTravel.from !== caughtTravel.to ? caughtTravel.from : null;
|
|
58066
|
+
let area;
|
|
58067
|
+
let areaBack;
|
|
58068
|
+
let areaOn;
|
|
58069
|
+
if (origin) {
|
|
58070
|
+
area = origin;
|
|
58071
|
+
const towardsEnd = axis === "x" ? placeOfArea(currentArea).x > placeOfArea(origin).x : placeOfArea(currentArea).y > placeOfArea(origin).y;
|
|
58072
|
+
areaBack = towardsEnd ? undefined : currentArea;
|
|
58073
|
+
areaOn = towardsEnd ? currentArea : undefined;
|
|
58074
|
+
} else {
|
|
58075
|
+
area = currentArea;
|
|
58076
|
+
areaBack = axis === "x" ? areaTowards(-1, 0) : areaTowards(0, -1);
|
|
58077
|
+
areaOn = axis === "x" ? areaTowards(1, 0) : areaTowards(0, 1);
|
|
58078
|
+
// The hold goToArea reads at the release, read again HERE, off the
|
|
58079
|
+
// same slide and the same attribute: a slide that will refuse the
|
|
58080
|
+
// arrival must not offer the journey. A locked direction simply has
|
|
58081
|
+
// nowhere to go for the length of this gesture — the one case the
|
|
58082
|
+
// gesture already knows, being the last slide of a walk. The hand
|
|
58083
|
+
// then gets the wall it can lean on and never walk through (see
|
|
58084
|
+
// drag_to_travel), the slide behind it stays offstage instead of
|
|
58085
|
+
// being read on the way, and the release has nothing left to refuse.
|
|
58086
|
+
// Nothing here about `released` (--navi-done): that is one particular
|
|
58087
|
+
// departure letting go, decided as it happens, and a gesture armed
|
|
58088
|
+
// before it has no such thing to read — the attribute as rendered is
|
|
58089
|
+
// what the hand is answered from.
|
|
58090
|
+
if (currentElement.hasAttribute("data-prevent-nav-previous")) {
|
|
58091
|
+
areaBack = undefined;
|
|
58092
|
+
}
|
|
58093
|
+
if (currentElement.hasAttribute("data-prevent-nav-next")) {
|
|
58094
|
+
areaOn = undefined;
|
|
58095
|
+
}
|
|
58096
|
+
if (!areaBack && !areaOn ||
|
|
58097
|
+
// Something else with a better claim on the gesture: a scroller
|
|
58098
|
+
// between the finger and the slide, with room left that way.
|
|
58099
|
+
scrollRoomTowards(target, currentElement, axis, sign)) {
|
|
58100
|
+
return false;
|
|
58101
|
+
}
|
|
58102
|
+
}
|
|
58103
|
+
const basePlace = placeOfArea(area);
|
|
58037
58104
|
const baseOffset = {
|
|
58038
58105
|
x: -basePlace.x * box.width,
|
|
58039
58106
|
y: -basePlace.y * box.height
|
|
@@ -58064,7 +58131,8 @@ const SlideContainer = ({
|
|
|
58064
58131
|
[axis]: slack
|
|
58065
58132
|
};
|
|
58066
58133
|
drag.progress = slack / size;
|
|
58067
|
-
|
|
58134
|
+
drag.areaPulled = slack > 0 ? areaBack : slack < 0 ? areaOn : null;
|
|
58135
|
+
stageDrag(drag, currentArea);
|
|
58068
58136
|
// From here the box is busy, whichever input asked: a wheel gesture and
|
|
58069
58137
|
// a press must not both be moving the same track.
|
|
58070
58138
|
dragRef.current = drag;
|
|
@@ -58095,24 +58163,50 @@ const SlideContainer = ({
|
|
|
58095
58163
|
event
|
|
58096
58164
|
}) => {
|
|
58097
58165
|
dragRef.current = null;
|
|
58098
|
-
|
|
58099
|
-
|
|
58166
|
+
// Where the gesture leaves the slides: on the one it was pulling in
|
|
58167
|
+
// when it says so, back on the one it was dragging otherwise.
|
|
58168
|
+
const destination = travels ? sign > 0 ? drag.areaBack : drag.areaOn : drag.area;
|
|
58169
|
+
const {
|
|
58170
|
+
slideElements,
|
|
58171
|
+
placeOf
|
|
58172
|
+
} = readMap();
|
|
58173
|
+
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
58174
|
+
const currentArea = currentElement && readArea(currentElement);
|
|
58175
|
+
if (!destination || destination === currentArea) {
|
|
58176
|
+
// Nothing to ask of the state: the slide it names is already the one
|
|
58177
|
+
// shown — a drag let go of short of a travel, a travel caught in
|
|
58178
|
+
// flight and let carry on. The track goes there on its own.
|
|
58179
|
+
settleTowards(drag, destination || drag.area);
|
|
58100
58180
|
return;
|
|
58101
58181
|
}
|
|
58102
58182
|
// Where the slide is being left, for the travel to depart from instead
|
|
58103
58183
|
// of from the map.
|
|
58104
58184
|
travelFromRef.current = drag.offset;
|
|
58105
58185
|
// …and where the indicator is being left, said about the slide that is
|
|
58106
|
-
// ARRIVING:
|
|
58107
|
-
//
|
|
58108
|
-
travelProgressFromRef.current = drag.progress - sign;
|
|
58109
|
-
|
|
58186
|
+
// ARRIVING: a box short of the slide being pulled in, right where the
|
|
58187
|
+
// finger left it when going back to the slide being dragged.
|
|
58188
|
+
travelProgressFromRef.current = travels ? drag.progress - sign : drag.progress;
|
|
58189
|
+
// Which way the travel goes, read off where the two slides stand
|
|
58190
|
+
// rather than off the pull: a travel caught in flight and put back
|
|
58191
|
+
// goes the way it came, whatever the finger did meanwhile.
|
|
58192
|
+
const stage = stageRef.current;
|
|
58193
|
+
const placeOfArea = someArea => stage?.placeByArea.get(someArea) || placeOf.get(someArea) || {
|
|
58194
|
+
x: 0,
|
|
58195
|
+
y: 0
|
|
58196
|
+
};
|
|
58197
|
+
const towards = Math.sign(placeOfArea(destination)[axis] - placeOfArea(currentArea)[axis]);
|
|
58198
|
+
const moved = goToArea(destination, {
|
|
58199
|
+
forward: towards > 0,
|
|
58200
|
+
event,
|
|
58201
|
+
dx: axis === "x" ? towards : 0,
|
|
58202
|
+
dy: axis === "y" ? towards : 0
|
|
58203
|
+
});
|
|
58110
58204
|
if (!moved) {
|
|
58111
58205
|
// Nowhere to go after all — a slide holding on to the user
|
|
58112
58206
|
// (preventNav), or a caller that refused the change.
|
|
58113
58207
|
travelFromRef.current = null;
|
|
58114
58208
|
travelProgressFromRef.current = null;
|
|
58115
|
-
|
|
58209
|
+
settleTowards(drag, currentArea);
|
|
58116
58210
|
return;
|
|
58117
58211
|
}
|
|
58118
58212
|
// A container whose `current` is held outside and was not moved: no
|
|
@@ -58123,7 +58217,7 @@ const SlideContainer = ({
|
|
|
58123
58217
|
if (travelFromRef.current) {
|
|
58124
58218
|
travelFromRef.current = null;
|
|
58125
58219
|
travelProgressFromRef.current = null;
|
|
58126
|
-
|
|
58220
|
+
settleTowards(drag, currentArea);
|
|
58127
58221
|
}
|
|
58128
58222
|
});
|
|
58129
58223
|
},
|
|
@@ -58138,9 +58232,15 @@ const SlideContainer = ({
|
|
|
58138
58232
|
trackElement: trackCaught,
|
|
58139
58233
|
offsetOnScreen,
|
|
58140
58234
|
offsetTarget,
|
|
58141
|
-
onScreenPx: caughtOnScreenPx
|
|
58235
|
+
onScreenPx: caughtOnScreenPx,
|
|
58236
|
+
from,
|
|
58237
|
+
to
|
|
58142
58238
|
} = caughtTravel;
|
|
58143
58239
|
caughtTravel = null;
|
|
58240
|
+
movingRef.current = {
|
|
58241
|
+
from,
|
|
58242
|
+
to
|
|
58243
|
+
};
|
|
58144
58244
|
if (offsetOnScreen === offsetTarget) {
|
|
58145
58245
|
settleTravel();
|
|
58146
58246
|
return;
|
|
@@ -60467,6 +60567,13 @@ const clipOf = (side, distance) => {
|
|
|
60467
60567
|
* (TARGET_WAIT_MS), on the half-strength frame where the anchor is still
|
|
60468
60568
|
* readable, and lifts the moment it is there.
|
|
60469
60569
|
*
|
|
60570
|
+
* A closing may land where no opening took off: a dialog opened the plain way
|
|
60571
|
+
* (`animation={{ open, close: "lifting" }}` in dialog.jsx), closing into a box the close
|
|
60572
|
+
* itself brings — the state `onClose` writes renders the place the lifted node
|
|
60573
|
+
* belongs to. So the box a closing comes back to is read once the change is
|
|
60574
|
+
* made, inside the transition, and when the caller named it (`liftAnchor`) it
|
|
60575
|
+
* is waited for (LANDING_WAIT_MS): the new picture is taken once it is there.
|
|
60576
|
+
*
|
|
60470
60577
|
* One name serves the whole movement, because only one of the two boxes is on
|
|
60471
60578
|
* screen at a time: it names the anchor while the popup is closed, and the
|
|
60472
60579
|
* lifted node while it is open.
|
|
@@ -60509,6 +60616,12 @@ const ARRIVING_ATTRIBUTE = "data-navi-popup-lift-arriving";
|
|
|
60509
60616
|
// stands, without a movement, so a target that never comes cannot keep it
|
|
60510
60617
|
// unpainted.
|
|
60511
60618
|
const TARGET_WAIT_MS = 1000;
|
|
60619
|
+
// How long a closing waits for the box it comes back to, when that box is
|
|
60620
|
+
// brought by the close (see this file's top comment). The page is frozen on
|
|
60621
|
+
// the picture of the open popup meanwhile, so the wait is short: what it
|
|
60622
|
+
// covers is a render, not a fetch. Past it the popup's picture plays out on
|
|
60623
|
+
// its own.
|
|
60624
|
+
const LANDING_WAIT_MS = 300;
|
|
60512
60625
|
// The popup's own animation duration, published on the root because the
|
|
60513
60626
|
// ::view-transition tree hangs off it and inherits from nowhere else.
|
|
60514
60627
|
const DURATION_PROPERTY = "--navi-popup-lift-duration";
|
|
@@ -60549,14 +60662,16 @@ let releaseScrollHold = null;
|
|
|
60549
60662
|
* a view transition morphing the anchor's box into the lifted node's, or back.
|
|
60550
60663
|
*
|
|
60551
60664
|
* `opened` says which way: the box being left is the anchor when the popup is
|
|
60552
|
-
* opening and the lifted node when it is closing. `
|
|
60553
|
-
*
|
|
60665
|
+
* opening and the lifted node when it is closing. `resolveAnchor` is read on
|
|
60666
|
+
* the spot for an opening, and once the change is made for a closing;
|
|
60667
|
+
* `waitForAnchor` has a closing wait for it when it is not there yet. `lift`
|
|
60668
|
+
* is Dialog's own prop of that name.
|
|
60554
60669
|
*/
|
|
60555
60670
|
const liftPopupFromAnchor = (
|
|
60556
60671
|
popupEl,
|
|
60557
|
-
|
|
60672
|
+
resolveAnchor,
|
|
60558
60673
|
applyChange,
|
|
60559
|
-
{ opened, lift },
|
|
60674
|
+
{ opened, lift, waitForAnchor },
|
|
60560
60675
|
) => {
|
|
60561
60676
|
const startViewTransition = ensureDocumentStartViewTransition();
|
|
60562
60677
|
// A movement still wearing the name would make the name two elements wide,
|
|
@@ -60564,7 +60679,7 @@ const liftPopupFromAnchor = (
|
|
|
60564
60679
|
// document.
|
|
60565
60680
|
releaseLiftInProgress?.();
|
|
60566
60681
|
|
|
60567
|
-
const elementLeaving = opened ?
|
|
60682
|
+
const elementLeaving = opened ? resolveAnchor() : resolveLiftTarget(popupEl);
|
|
60568
60683
|
// Read before the first write: the read brings the style up to date, and a
|
|
60569
60684
|
// write before it would make it bring it up to date once more.
|
|
60570
60685
|
const duration = getComputedStyle(popupEl)
|
|
@@ -60588,12 +60703,14 @@ const liftPopupFromAnchor = (
|
|
|
60588
60703
|
|
|
60589
60704
|
let giveBackNameArriving = null;
|
|
60590
60705
|
let stopWaitingForTarget = null;
|
|
60706
|
+
let stopWaitingForAnchor = null;
|
|
60591
60707
|
const release = () => {
|
|
60592
60708
|
if (releaseLiftInProgress !== release) {
|
|
60593
60709
|
return;
|
|
60594
60710
|
}
|
|
60595
60711
|
releaseLiftInProgress = null;
|
|
60596
60712
|
stopWaitingForTarget?.();
|
|
60713
|
+
stopWaitingForAnchor?.();
|
|
60597
60714
|
boxAnimationInProgress?.cancel();
|
|
60598
60715
|
boxAnimationInProgress = null;
|
|
60599
60716
|
releaseScrollHold?.();
|
|
@@ -60619,13 +60736,18 @@ const liftPopupFromAnchor = (
|
|
|
60619
60736
|
const boxLeaving = room ? elementLeaving.getBoundingClientRect() : null;
|
|
60620
60737
|
let boxArriving = null;
|
|
60621
60738
|
let cornersArriving = null;
|
|
60622
|
-
const viewTransition = startViewTransition(() => {
|
|
60739
|
+
const viewTransition = startViewTransition(async () => {
|
|
60623
60740
|
// The name is the arriving box's from here on: worn by both, it is worn
|
|
60624
60741
|
// by neither. Written rather than removed, so a name the element also
|
|
60625
60742
|
// has from a stylesheet cannot resurface for the length of the movement.
|
|
60626
60743
|
elementLeaving.style.setProperty(NAME_PROPERTY, "none");
|
|
60627
60744
|
change();
|
|
60628
|
-
const elementArriving = resolveElementArriving();
|
|
60745
|
+
const elementArriving = await resolveElementArriving();
|
|
60746
|
+
// Replaced while waiting for its landing: the movement replacing it
|
|
60747
|
+
// holds the name now.
|
|
60748
|
+
if (releaseLiftInProgress !== release) {
|
|
60749
|
+
return;
|
|
60750
|
+
}
|
|
60629
60751
|
if (elementArriving) {
|
|
60630
60752
|
giveBackNameArriving = wearLiftName(elementArriving);
|
|
60631
60753
|
cornersArriving = readCorners(elementArriving);
|
|
@@ -60650,12 +60772,33 @@ const liftPopupFromAnchor = (
|
|
|
60650
60772
|
};
|
|
60651
60773
|
|
|
60652
60774
|
if (!opened) {
|
|
60653
|
-
startMovement(applyChange, () =>
|
|
60775
|
+
startMovement(applyChange, () => {
|
|
60776
|
+
const anchorElement = resolveAnchor();
|
|
60777
|
+
if (anchorElement?.isConnected) {
|
|
60778
|
+
return anchorElement;
|
|
60779
|
+
}
|
|
60654
60780
|
// Gone from the document while the popup was open (the row it stood in
|
|
60655
60781
|
// was removed): nothing to arrive at, and the browser plays the popup's
|
|
60656
60782
|
// picture out on its own.
|
|
60657
|
-
|
|
60658
|
-
|
|
60783
|
+
if (!waitForAnchor) {
|
|
60784
|
+
return null;
|
|
60785
|
+
}
|
|
60786
|
+
return new Promise((resolve) => {
|
|
60787
|
+
stopWaitingForAnchor = whenAnchorAppears(resolveAnchor, (element) => {
|
|
60788
|
+
stopWaitingForAnchor = null;
|
|
60789
|
+
// Not when stopped by a movement replacing this one.
|
|
60790
|
+
if (
|
|
60791
|
+
!element &&
|
|
60792
|
+
releaseLiftInProgress === release
|
|
60793
|
+
) {
|
|
60794
|
+
console.warn(
|
|
60795
|
+
`[navi] Dialog's "liftAnchor" named nothing on screen within ${LANDING_WAIT_MS}ms of the close, so the dialog closes without landing. The element it names is where the box comes back to: in the document at the close, or rendered by what the close changes (onClose).`,
|
|
60796
|
+
);
|
|
60797
|
+
}
|
|
60798
|
+
resolve(element);
|
|
60799
|
+
});
|
|
60800
|
+
});
|
|
60801
|
+
});
|
|
60659
60802
|
return;
|
|
60660
60803
|
}
|
|
60661
60804
|
|
|
@@ -60815,6 +60958,40 @@ const whenLiftTargetAppears = (popupEl, callback) => {
|
|
|
60815
60958
|
return stop;
|
|
60816
60959
|
};
|
|
60817
60960
|
|
|
60961
|
+
// Calls `callback` with the anchor once `resolveAnchor` finds it in the
|
|
60962
|
+
// document, or with null past LANDING_WAIT_MS — or when stopped, since the
|
|
60963
|
+
// transition's update is waiting on it. Returns how to stop.
|
|
60964
|
+
const whenAnchorAppears = (resolveAnchor, callback) => {
|
|
60965
|
+
const observer = new MutationObserver(() => {
|
|
60966
|
+
const anchorElement = resolveAnchor();
|
|
60967
|
+
if (anchorElement?.isConnected) {
|
|
60968
|
+
stop(anchorElement);
|
|
60969
|
+
}
|
|
60970
|
+
});
|
|
60971
|
+
observer.observe(document.documentElement, {
|
|
60972
|
+
childList: true,
|
|
60973
|
+
subtree: true,
|
|
60974
|
+
attributes: true,
|
|
60975
|
+
attributeFilter: ["id"],
|
|
60976
|
+
});
|
|
60977
|
+
const timeout = setTimeout(() => {
|
|
60978
|
+
stop(null);
|
|
60979
|
+
}, LANDING_WAIT_MS);
|
|
60980
|
+
let stopped = false;
|
|
60981
|
+
const stop = (anchorElement = null) => {
|
|
60982
|
+
if (stopped) {
|
|
60983
|
+
return;
|
|
60984
|
+
}
|
|
60985
|
+
stopped = true;
|
|
60986
|
+
observer.disconnect();
|
|
60987
|
+
clearTimeout(timeout);
|
|
60988
|
+
callback(anchorElement);
|
|
60989
|
+
};
|
|
60990
|
+
return () => {
|
|
60991
|
+
stop(null);
|
|
60992
|
+
};
|
|
60993
|
+
};
|
|
60994
|
+
|
|
60818
60995
|
const ignore = () => {};
|
|
60819
60996
|
|
|
60820
60997
|
// The room the fixed bars leave, in viewport coordinates; null without bars.
|
|
@@ -61480,6 +61657,18 @@ const css$E = /* css */`
|
|
|
61480
61657
|
opacity: 0;
|
|
61481
61658
|
}
|
|
61482
61659
|
|
|
61660
|
+
/* A closing lift is the dialog leaving as a picture: whatever exit its
|
|
61661
|
+
opening animation arms (animation={{ open, close: "lifting" }}, see
|
|
61662
|
+
popup_css.js) would
|
|
61663
|
+
keep it rendered into the picture of the state it closes into. */
|
|
61664
|
+
:root[data-navi-popup-lift="closing"] {
|
|
61665
|
+
.navi_dialog,
|
|
61666
|
+
.navi_dialog::backdrop,
|
|
61667
|
+
.navi_dialog_backdrop {
|
|
61668
|
+
transition: none;
|
|
61669
|
+
}
|
|
61670
|
+
}
|
|
61671
|
+
|
|
61483
61672
|
/* While a dialog is lifting out of the element that opened it
|
|
61484
61673
|
(popup_lift.js). The page around IS taken as a picture, the browser's own
|
|
61485
61674
|
default, and on purpose: the wall and what the dialog holds around the
|
|
@@ -61722,7 +61911,7 @@ const css$E = /* css */`
|
|
|
61722
61911
|
* scroll while open (its backdrop only covers the scrollport, so scrolling
|
|
61723
61912
|
* there would reveal uncovered content); this prop extends the lock to the
|
|
61724
61913
|
* whole page. Defaults to `true` for a dialog docked by `dockedOnSmallTouchScreen`.
|
|
61725
|
-
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"lifting"|`slide-from-${string}
|
|
61914
|
+
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"lifting"|`slide-from-${string}`|{open: boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`, close: "lifting"}} [props.animation]
|
|
61726
61915
|
* - `true`/`"auto"` resolves to `"scaling"` for a centered `positionArea`,
|
|
61727
61916
|
* or a concrete `"slide-from-*"` direction otherwise. Any other explicit
|
|
61728
61917
|
* value is used as-is. `"lifting"` is the odd one out: every other kind
|
|
@@ -61741,6 +61930,15 @@ const css$E = /* css */`
|
|
|
61741
61930
|
* being what the movement leaves rather than a context to keep readable;
|
|
61742
61931
|
* `backdropVariant="discrete"` asks for the light wash back. See
|
|
61743
61932
|
* `popup_lift.js`.
|
|
61933
|
+
* - `{ open, close: "lifting" }`: the close alone lifts. The dialog opens
|
|
61934
|
+
* with `open` (any value above but `"lifting"`), and its `data-lift` node
|
|
61935
|
+
* travels into `liftAnchor` on close — for a dialog that did not come out
|
|
61936
|
+
* of what it lands in (a banner opens a full-screen reveal, closing it puts
|
|
61937
|
+
* the crest in its place on the plate that replaces the banner). The box it
|
|
61938
|
+
* lands in may be rendered by the close itself: `liftAnchor` is read once
|
|
61939
|
+
* `onClose` has run, and waited for a moment when it is not there yet.
|
|
61940
|
+
* `"lifting"` is the only `close` that differs from the opening: every
|
|
61941
|
+
* other kind closes by playing its opening backwards.
|
|
61744
61942
|
* @param {"box"|"scene"} [props.lift="box"] - Under `animation="lifting"`,
|
|
61745
61943
|
* what the anchor and what it becomes are to each other, which decides
|
|
61746
61944
|
* how their pictures sit in the box moving between them. `"box"`: one
|
|
@@ -61765,14 +61963,15 @@ const css$E = /* css */`
|
|
|
61765
61963
|
* `document.getElementById` when the dialog opens — see popover.jsx's own
|
|
61766
61964
|
* `anchor` doc for why (mainly `defaultOpen`).
|
|
61767
61965
|
* @param {Element|{current: Element}|string} [props.liftAnchor] - Under
|
|
61768
|
-
* `animation="lifting"
|
|
61769
|
-
* is
|
|
61770
|
-
* shown one at a time) has something else in
|
|
61771
|
-
* and the box would otherwise fly back to the
|
|
61772
|
-
* grammar as `anchor` (element, ref or id),
|
|
61773
|
-
*
|
|
61774
|
-
*
|
|
61775
|
-
* opening. Left out, the box comes back to the anchor
|
|
61966
|
+
* `animation="lifting"` or `animation={{ open, close: "lifting" }}`, where the closing
|
|
61967
|
+
* brings the box back to, when that is not where it came from: a popup one
|
|
61968
|
+
* walks through (a row of cards shown one at a time) has something else in
|
|
61969
|
+
* front by the time it closes, and the box would otherwise fly back to the
|
|
61970
|
+
* card the press opened on. Same grammar as `anchor` (element, ref or id),
|
|
61971
|
+
* resolved once the close is made — after `onClose` — so whatever names the
|
|
61972
|
+
* card currently in front, or the box the close itself renders, is read
|
|
61973
|
+
* then and not at the opening. Left out, the box comes back to the anchor
|
|
61974
|
+
* it came out of.
|
|
61776
61975
|
* @param {boolean} [props.sizeFromAnchor=false] - Whether the dialog takes the
|
|
61777
61976
|
* anchor's width/height as a min-width/min-height floor
|
|
61778
61977
|
* (`--anchor-width`/`--anchor-height`). Off by default: unlike a popover,
|
|
@@ -62260,7 +62459,18 @@ const useDialogProps = props => {
|
|
|
62260
62459
|
flushEdges.left = expandX || x === "left" || x === "inset-left";
|
|
62261
62460
|
flushEdges.right = expandX || x === "right" || x === "inset-right";
|
|
62262
62461
|
}
|
|
62263
|
-
|
|
62462
|
+
|
|
62463
|
+
// `{ open, close }` says each way on its own; a single value says both.
|
|
62464
|
+
const {
|
|
62465
|
+
open: openAnimation,
|
|
62466
|
+
close: closeAnimation = openAnimation
|
|
62467
|
+
} = animation !== null && typeof animation === "object" ? animation : {
|
|
62468
|
+
open: animation
|
|
62469
|
+
};
|
|
62470
|
+
if (closeAnimation !== openAnimation && closeAnimation !== "lifting") {
|
|
62471
|
+
console.warn(`[navi] Dialog animation={{ close: ${JSON.stringify(closeAnimation)} }}: "lifting" is the only close that differs from the opening; every other kind closes by playing the opening backwards.`);
|
|
62472
|
+
}
|
|
62473
|
+
const isAutoAnimation = openAnimation === true || openAnimation === "auto";
|
|
62264
62474
|
// The dialog and the anchor are one box, and what plays between them is the
|
|
62265
62475
|
// browser's own morph (popup_lift.js) — nothing this dialog does to its own
|
|
62266
62476
|
// box. So it arms no CSS transition of its own, which is not merely useless
|
|
@@ -62268,11 +62478,12 @@ const useDialogProps = props => {
|
|
|
62268
62478
|
// with allow-discrete, and a dialog kept rendered for the length of its exit
|
|
62269
62479
|
// is exactly what the picture taken of the state it closes into must not
|
|
62270
62480
|
// show.
|
|
62271
|
-
const lifting =
|
|
62481
|
+
const lifting = openAnimation === "lifting";
|
|
62482
|
+
const liftsOnClose = closeAnimation === "lifting";
|
|
62272
62483
|
// Dialog never has a real anchor to POSITION against (see this file's top
|
|
62273
62484
|
// comment), so this is always the "no anchor" path — the same one Popover's
|
|
62274
62485
|
// own custom renderer falls into when it has no real anchor either.
|
|
62275
|
-
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) :
|
|
62486
|
+
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) : openAnimation;
|
|
62276
62487
|
// Not gated on isAutoAnimation — an explicit animation="sliding" needs a
|
|
62277
62488
|
// concrete direction just as much as an auto-resolved one does (same as
|
|
62278
62489
|
// Popover's own "sliding"/"expanding" resolution step in openEffect).
|
|
@@ -62321,13 +62532,11 @@ const useDialogProps = props => {
|
|
|
62321
62532
|
// is in front NOW, which only the caller knows. Resolved at the close for
|
|
62322
62533
|
// that reason: the element it names changes while the popup is open, so
|
|
62323
62534
|
// anything read at the opening would be the walk's starting point again.
|
|
62535
|
+
// Silent when it names nothing: the close may be what renders it, and
|
|
62536
|
+
// popup_lift.js asks again until it is there (and warns past that).
|
|
62324
62537
|
const resolveLiftAnchorElement = () => {
|
|
62325
62538
|
if (typeof liftAnchor === "string") {
|
|
62326
|
-
|
|
62327
|
-
if (!liftAnchorElementById) {
|
|
62328
|
-
console.warn(`Dialog: liftAnchor="${liftAnchor}" did not match any element`);
|
|
62329
|
-
}
|
|
62330
|
-
return liftAnchorElementById;
|
|
62539
|
+
return document.getElementById(liftAnchor);
|
|
62331
62540
|
}
|
|
62332
62541
|
// A ref is unwrapped even when it holds nothing, the same way `anchor` is:
|
|
62333
62542
|
// the ref object itself has no box to come back to.
|
|
@@ -62339,7 +62548,7 @@ const useDialogProps = props => {
|
|
|
62339
62548
|
// provided the change happens between its two pictures, which is what
|
|
62340
62549
|
// handing it to the controller buys (see popup_lift.js and
|
|
62341
62550
|
// open_controller.js's own transitionChange).
|
|
62342
|
-
openController.transitionChange =
|
|
62551
|
+
openController.transitionChange = liftsOnClose ? (applyChange, {
|
|
62343
62552
|
opened,
|
|
62344
62553
|
event
|
|
62345
62554
|
}) => {
|
|
@@ -62347,29 +62556,29 @@ const useDialogProps = props => {
|
|
|
62347
62556
|
// A mount-time opening was never seen closed (see openEffect's own
|
|
62348
62557
|
// `silent`): there is no box it comes from, because nothing was shown
|
|
62349
62558
|
// before it.
|
|
62350
|
-
if (!dialogEl || opened && event.detail.silent) {
|
|
62559
|
+
if (!dialogEl || opened && (!lifting || event.detail.silent)) {
|
|
62351
62560
|
applyChange();
|
|
62352
62561
|
return;
|
|
62353
62562
|
}
|
|
62354
|
-
|
|
62355
|
-
|
|
62356
|
-
|
|
62357
|
-
|
|
62358
|
-
|
|
62359
|
-
|
|
62360
|
-
|
|
62563
|
+
if (!opened) {
|
|
62564
|
+
// Read once the close is made (popup_lift.js): the box it lands in
|
|
62565
|
+
// may be one the close itself renders.
|
|
62566
|
+
liftPopupFromAnchor(dialogEl, liftAnchor ? resolveLiftAnchorElement : () => anchorElementRef.current, applyChange, {
|
|
62567
|
+
opened,
|
|
62568
|
+
lift,
|
|
62569
|
+
waitForAnchor: Boolean(liftAnchor)
|
|
62570
|
+
});
|
|
62571
|
+
return;
|
|
62361
62572
|
}
|
|
62573
|
+
const anchorElement = resolveAnchorElement(event);
|
|
62362
62574
|
if (!anchorElement) {
|
|
62363
|
-
|
|
62575
|
+
{
|
|
62364
62576
|
console.warn(`[navi] Dialog has animation="lifting" and no anchor to lift out of, so it simply appears. The anchor is whatever opened it — a <Button command="--navi-open">, the "source" given to triggerNaviCommand — or the "anchor" prop.`);
|
|
62365
62577
|
}
|
|
62366
|
-
if (!opened && liftAnchor) {
|
|
62367
|
-
console.warn(`[navi] Dialog has animation="lifting" and a "liftAnchor" naming nothing on screen, so it simply closes. The element it names is where the box comes back to, and it has to be in the document at the close.`);
|
|
62368
|
-
}
|
|
62369
62578
|
applyChange();
|
|
62370
62579
|
return;
|
|
62371
62580
|
}
|
|
62372
|
-
liftPopupFromAnchor(dialogEl, anchorElement, applyChange, {
|
|
62581
|
+
liftPopupFromAnchor(dialogEl, () => anchorElement, applyChange, {
|
|
62373
62582
|
opened,
|
|
62374
62583
|
lift
|
|
62375
62584
|
});
|
|
@@ -65804,6 +66013,7 @@ const PickerContentInsidePopup = props => {
|
|
|
65804
66013
|
popupWidthFitContent,
|
|
65805
66014
|
animation,
|
|
65806
66015
|
lift,
|
|
66016
|
+
liftAnchor,
|
|
65807
66017
|
animationDuration,
|
|
65808
66018
|
// mode="callout": what the callout says about what it holds, and paints
|
|
65809
66019
|
// in its border and icon — "none" for a plain tooltip (see the callout
|
|
@@ -65900,6 +66110,7 @@ const PickerContentInsidePopup = props => {
|
|
|
65900
66110
|
dockedOnSmallTouchScreen: isPopover ? undefined : dockedOnSmallTouchScreen,
|
|
65901
66111
|
sizeFromAnchor: isPopover ? undefined : dialogSizeFromAnchor,
|
|
65902
66112
|
lift: isPopover ? undefined : lift,
|
|
66113
|
+
liftAnchor: isPopover ? undefined : liftAnchor,
|
|
65903
66114
|
children: jsx(PopupModeContext.Provider, {
|
|
65904
66115
|
value: mode,
|
|
65905
66116
|
children: children
|
|
@@ -75371,8 +75582,9 @@ const PickerFirstResolver = props => {
|
|
|
75371
75582
|
* children?: import("ignore:preact").ComponentChildren,
|
|
75372
75583
|
* mode?: "popover" | "dialog" | "callout",
|
|
75373
75584
|
* openOn?: "press" | "longpress" | "contextmenu" | string | string[],
|
|
75374
|
-
* animation?: boolean | "auto" | "fading" | "scaling" | "sliding" | "lifting" | `slide-from-${string}`,
|
|
75585
|
+
* animation?: boolean | "auto" | "fading" | "scaling" | "sliding" | "lifting" | `slide-from-${string}` | { open: boolean | "auto" | "fading" | "scaling" | "sliding" | `slide-from-${string}`, close: "lifting" },
|
|
75375
75586
|
* lift?: "box" | "scene",
|
|
75587
|
+
* liftAnchor?: Element | { current: Element } | string,
|
|
75376
75588
|
* animationDuration?: string,
|
|
75377
75589
|
* calloutStatus?: "info" | "warning" | "error" | "success" | "none",
|
|
75378
75590
|
* calloutIcon?: boolean,
|
|
@@ -75743,6 +75955,11 @@ const PickerFirstResolver = props => {
|
|
|
75743
75955
|
* that opens precisely to get bigger. It brings an opaque, blurred backdrop
|
|
75744
75956
|
* with it (`--navi-backdrop-lift-*`); `backdropVariant="discrete"` asks for
|
|
75745
75957
|
* the light wash back.
|
|
75958
|
+
* @param {Element|{current: Element}|string} [liftAnchor] Dialog mode,
|
|
75959
|
+
* Dialog's own: where a lifting close lands when that is not the trigger
|
|
75960
|
+
* (`animation="lifting"`, or `animation={{ open, close: "lifting" }}` for a
|
|
75961
|
+
* popup that opens the plain way and only lands on close),
|
|
75962
|
+
* read once the close is made (after `onClose`).
|
|
75746
75963
|
* @param {"box"|"scene"} [lift="box"] Dialog's own, under `animation="lifting"`:
|
|
75747
75964
|
* `"box"` for a card that extends (its top stays, the box uncovers the rest),
|
|
75748
75965
|
* `"scene"` for a thumbnail that is a band cut from the middle of the bigger
|