@jsenv/navi 0.29.186 → 0.29.187
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 +365 -78
- package/dist/dev/jsenv_navi.js.map +48 -44
- package/dist/dev/jsenv_navi_side_effects.js.map +6 -5
- package/dist/jsenv_navi.js +362 -78
- package/dist/jsenv_navi.js.map +46 -42
- package/dist/jsenv_navi_format_time.js.map +4 -3
- package/dist/jsenv_navi_side_effects.js.map +6 -5
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -28906,7 +28906,7 @@ const FURNITURE_SELECTOR = `.navi_fixed_bar, .navi_popover:is(${TOP_LAYER_SELECT
|
|
|
28906
28906
|
// owns it). Written out rather than imported: importing the module that owns
|
|
28907
28907
|
// it back into this one would close a cycle.
|
|
28908
28908
|
const TRANSITION_ATTRIBUTE$1 = "data-navi-route-transition";
|
|
28909
|
-
const NAME_PROPERTY = "view-transition-name";
|
|
28909
|
+
const NAME_PROPERTY$1 = "view-transition-name";
|
|
28910
28910
|
const FURNITURE_NAME_PREFIX = "navi-transition-furniture-";
|
|
28911
28911
|
|
|
28912
28912
|
const nameByElement = new WeakMap();
|
|
@@ -28991,7 +28991,7 @@ const nameFurnitureAround = (areaElement) => {
|
|
|
28991
28991
|
name = `${FURNITURE_NAME_PREFIX}${nameCount}`;
|
|
28992
28992
|
nameByElement.set(element, name);
|
|
28993
28993
|
}
|
|
28994
|
-
element.style.setProperty(NAME_PROPERTY, name);
|
|
28994
|
+
element.style.setProperty(NAME_PROPERTY$1, name);
|
|
28995
28995
|
namedElements.add(element);
|
|
28996
28996
|
namesAdded.push(name);
|
|
28997
28997
|
}
|
|
@@ -29027,7 +29027,7 @@ const releaseTransitionFurniture = (owner) => {
|
|
|
29027
29027
|
}
|
|
29028
29028
|
furnitureOwner = null;
|
|
29029
29029
|
for (const element of namedElements) {
|
|
29030
|
-
element.style.removeProperty(NAME_PROPERTY);
|
|
29030
|
+
element.style.removeProperty(NAME_PROPERTY$1);
|
|
29031
29031
|
}
|
|
29032
29032
|
namedElements = new Set();
|
|
29033
29033
|
if (travelStyleElement) {
|
|
@@ -46134,6 +46134,43 @@ const createOpenController = (
|
|
|
46134
46134
|
document.addEventListener("click", onCaptureClick, { capture: true });
|
|
46135
46135
|
};
|
|
46136
46136
|
|
|
46137
|
+
// The DOM change a popup asked to have photographed (see
|
|
46138
|
+
// controller.transitionChange), waiting for the browser to take the picture
|
|
46139
|
+
// of the state being left. Anything the controller is asked to do meanwhile
|
|
46140
|
+
// happens after it: the change is run on the spot, and the transition holding
|
|
46141
|
+
// it finds nothing left to do.
|
|
46142
|
+
let changeAwaitingTransition = null;
|
|
46143
|
+
const flushChangeAwaitingTransition = () => {
|
|
46144
|
+
const change = changeAwaitingTransition;
|
|
46145
|
+
if (!change) {
|
|
46146
|
+
return;
|
|
46147
|
+
}
|
|
46148
|
+
changeAwaitingTransition = null;
|
|
46149
|
+
change();
|
|
46150
|
+
};
|
|
46151
|
+
const runChange = (change, { opened, event }) => {
|
|
46152
|
+
const { transitionChange } = controller;
|
|
46153
|
+
if (!transitionChange) {
|
|
46154
|
+
change();
|
|
46155
|
+
return;
|
|
46156
|
+
}
|
|
46157
|
+
// What the controller answers about itself does not wait for the picture:
|
|
46158
|
+
// whoever just asked reads `opened` on the spot (see
|
|
46159
|
+
// useOpenPropsEffectOnOpenController, which writes it back into the
|
|
46160
|
+
// caller's own signal), and a request arriving before the change lands
|
|
46161
|
+
// runs it first rather than reading a DOM that disagrees.
|
|
46162
|
+
controller.opened = opened;
|
|
46163
|
+
changeAwaitingTransition = change;
|
|
46164
|
+
transitionChange(
|
|
46165
|
+
() => {
|
|
46166
|
+
if (changeAwaitingTransition === change) {
|
|
46167
|
+
flushChangeAwaitingTransition();
|
|
46168
|
+
}
|
|
46169
|
+
},
|
|
46170
|
+
{ opened, event },
|
|
46171
|
+
);
|
|
46172
|
+
};
|
|
46173
|
+
|
|
46137
46174
|
const performClose = (closeEvent) => {
|
|
46138
46175
|
controller.opened = false;
|
|
46139
46176
|
// Read before any close effect touches the DOM: closing a native <dialog>
|
|
@@ -46216,18 +46253,23 @@ const createOpenController = (
|
|
|
46216
46253
|
}
|
|
46217
46254
|
}
|
|
46218
46255
|
|
|
46219
|
-
|
|
46220
|
-
|
|
46221
|
-
|
|
46222
|
-
|
|
46223
|
-
|
|
46224
|
-
|
|
46225
|
-
|
|
46226
|
-
|
|
46227
|
-
|
|
46228
|
-
|
|
46229
|
-
|
|
46230
|
-
|
|
46256
|
+
runChange(
|
|
46257
|
+
() => {
|
|
46258
|
+
// Sync the DOM closed first (releasing the focus trap) — only then run
|
|
46259
|
+
// the owner's own reaction (onClose may restore focus to an element
|
|
46260
|
+
// outside the popup, which the focus trap would otherwise fight while
|
|
46261
|
+
// still active).
|
|
46262
|
+
openEffectCleanup?.(closeEvent);
|
|
46263
|
+
openEffectCleanup = null;
|
|
46264
|
+
closeHandlers?.onClose?.(closeEvent);
|
|
46265
|
+
closeHandlers = null;
|
|
46266
|
+
// Last: the close effects above are what starts the exit transition the
|
|
46267
|
+
// content must outlive (see popup_content_mount.js).
|
|
46268
|
+
controller.unmountContent?.();
|
|
46269
|
+
controller.onOpenedChange?.(false, closeEvent);
|
|
46270
|
+
},
|
|
46271
|
+
{ opened: false, event: closeEvent },
|
|
46272
|
+
);
|
|
46231
46273
|
};
|
|
46232
46274
|
const controller = {
|
|
46233
46275
|
opened: false,
|
|
@@ -46246,6 +46288,15 @@ const createOpenController = (
|
|
|
46246
46288
|
// The counterpart, set only when the popup was told to throw its content
|
|
46247
46289
|
// away on close (`mount="while-opened"`). Called from performClose above.
|
|
46248
46290
|
unmountContent: null,
|
|
46291
|
+
// Set by the controlled element when the DOM change that opens or closes
|
|
46292
|
+
// it has to be photographed by the browser on both sides — a document view
|
|
46293
|
+
// transition, whose update callback is the only place that change can
|
|
46294
|
+
// happen (Dialog's animation="growing", see popup_grow.js). Called with
|
|
46295
|
+
// the change and where it leads; running it is its job, and it may run it
|
|
46296
|
+
// a frame later than it was asked for. That delay is the reason a popup
|
|
46297
|
+
// cannot do this from the outside: `--navi-open` runs when navi runs it,
|
|
46298
|
+
// and a close arrives once the DOM already holds it.
|
|
46299
|
+
transitionChange: null,
|
|
46249
46300
|
// Told whenever `opened` actually changes, whatever asked for it — an
|
|
46250
46301
|
// interaction, a command, a prop — with the event that asked. What lets a
|
|
46251
46302
|
// `signal` prop reflect the popup's real state, and a `navState` prop write
|
|
@@ -46253,6 +46304,7 @@ const createOpenController = (
|
|
|
46253
46304
|
// called once the open/close has fully happened rather than mid-sequence.
|
|
46254
46305
|
onOpenedChange: null,
|
|
46255
46306
|
open: (e, detail) => {
|
|
46307
|
+
flushChangeAwaitingTransition();
|
|
46256
46308
|
if (controller.opened || !controller.openEffect) {
|
|
46257
46309
|
return;
|
|
46258
46310
|
}
|
|
@@ -46328,38 +46380,44 @@ const createOpenController = (
|
|
|
46328
46380
|
}
|
|
46329
46381
|
};
|
|
46330
46382
|
};
|
|
46331
|
-
|
|
46332
|
-
|
|
46333
|
-
|
|
46334
|
-
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
|
|
46339
|
-
|
|
46340
|
-
|
|
46341
|
-
|
|
46342
|
-
|
|
46343
|
-
|
|
46344
|
-
|
|
46345
|
-
|
|
46346
|
-
|
|
46347
|
-
|
|
46348
|
-
|
|
46349
|
-
|
|
46350
|
-
|
|
46351
|
-
|
|
46352
|
-
|
|
46353
|
-
|
|
46354
|
-
|
|
46355
|
-
|
|
46356
|
-
|
|
46357
|
-
|
|
46383
|
+
runChange(
|
|
46384
|
+
() => {
|
|
46385
|
+
// Before mountContent, which builds the content, and before
|
|
46386
|
+
// openEffect, which shows it: what the popup opens ON has to be
|
|
46387
|
+
// known before either (see `onOpen` above).
|
|
46388
|
+
controller.onOpen?.(requestOpenEvent);
|
|
46389
|
+
// After prepareFocusTransfer, which has to record what held the
|
|
46390
|
+
// focus before anything inside the popup can claim it, and before
|
|
46391
|
+
// openEffect, which measures the popup to place it.
|
|
46392
|
+
controller.mountContent?.();
|
|
46393
|
+
// Only now — after the content has been built, before openEffect
|
|
46394
|
+
// shows it. Dialog/Popover recompute aria-expanded and navi-hidden
|
|
46395
|
+
// from this flag on every render, and mountContent above renders
|
|
46396
|
+
// synchronously: flipping it any earlier commits an already-open DOM
|
|
46397
|
+
// (aria-expanded "true", navi-hidden gone) before openEffect has run
|
|
46398
|
+
// a single statement, so the "closed" frame it pins to transition
|
|
46399
|
+
// from is in fact the open one and the entrance animation has
|
|
46400
|
+
// nothing to play. It also gives the content it just built the
|
|
46401
|
+
// opening it is documented to observe — mounted while the popup
|
|
46402
|
+
// reads as closed, told it opened right after (see
|
|
46403
|
+
// popup_content_mount.js and use_displayed_layout_effect.js).
|
|
46404
|
+
controller.opened = true;
|
|
46405
|
+
const openEffectReturnValue =
|
|
46406
|
+
controller.openEffect(requestOpenEvent) || null;
|
|
46407
|
+
openEffectCleanup = (closeEvent) => {
|
|
46408
|
+
openEffectReturnValue?.(closeEvent);
|
|
46409
|
+
};
|
|
46410
|
+
closeHandlers = openHandler(requestOpenEvent) || null;
|
|
46411
|
+
controller.onOpenedChange?.(true, requestOpenEvent);
|
|
46412
|
+
},
|
|
46413
|
+
{ opened: true, event: requestOpenEvent },
|
|
46414
|
+
);
|
|
46358
46415
|
},
|
|
46359
46416
|
requestClose: (
|
|
46360
46417
|
e = new CustomEvent("programmatic", { detail: {} }),
|
|
46361
46418
|
detail,
|
|
46362
46419
|
) => {
|
|
46420
|
+
flushChangeAwaitingTransition();
|
|
46363
46421
|
if (!controller.opened) {
|
|
46364
46422
|
return true;
|
|
46365
46423
|
}
|
|
@@ -46382,6 +46440,7 @@ const createOpenController = (
|
|
|
46382
46440
|
return true;
|
|
46383
46441
|
},
|
|
46384
46442
|
close: (e = new CustomEvent("programmatic", { detail: {} }), detail) => {
|
|
46443
|
+
flushChangeAwaitingTransition();
|
|
46385
46444
|
if (!controller.opened) {
|
|
46386
46445
|
return;
|
|
46387
46446
|
}
|
|
@@ -46432,6 +46491,11 @@ const useOpenController = (openHandler) => {
|
|
|
46432
46491
|
// no choice to leave open — close it for real.
|
|
46433
46492
|
useLayoutEffect(() => {
|
|
46434
46493
|
return () => {
|
|
46494
|
+
// Nothing to photograph on the way out: the popup is leaving the
|
|
46495
|
+
// document, so a movement between its box and anything else would be
|
|
46496
|
+
// played on an element already detached by the time the browser gets to
|
|
46497
|
+
// it (see controller.transitionChange).
|
|
46498
|
+
controllerRef.current.transitionChange = null;
|
|
46435
46499
|
controllerRef.current.close();
|
|
46436
46500
|
};
|
|
46437
46501
|
}, []);
|
|
@@ -58463,6 +58527,146 @@ const translateOf = (axis, distance) =>
|
|
|
58463
58527
|
? `translate(${distance}px, 0px)`
|
|
58464
58528
|
: `translate(0px, ${distance}px)`;
|
|
58465
58529
|
|
|
58530
|
+
/**
|
|
58531
|
+
* A popup that is the anchor, continued.
|
|
58532
|
+
*
|
|
58533
|
+
* Opening a surface out of the element that asked for it is not the surface
|
|
58534
|
+
* appearing while that element stays put: it is one box becoming another. The
|
|
58535
|
+
* browser draws exactly that on its own — the same `view-transition-name` on
|
|
58536
|
+
* the anchor before the change and on the popup after it, and it morphs the
|
|
58537
|
+
* first box into the second — so all this file does is hand that name over at
|
|
58538
|
+
* the two instants it can be handed over, and give it back afterwards.
|
|
58539
|
+
*
|
|
58540
|
+
* The pictures are taken around a DOM change, which is why the movement cannot
|
|
58541
|
+
* be written from outside: what a caller can reach — a command, `onClose` — is
|
|
58542
|
+
* either before the change or after it, never around it. The change that opens
|
|
58543
|
+
* a popup, and above all the one that closes it, belongs to the open
|
|
58544
|
+
* controller, which is what `transitionChange` (open_controller.js) exists to
|
|
58545
|
+
* let a popup wrap.
|
|
58546
|
+
*
|
|
58547
|
+
* One name serves the whole movement, because only one of the two boxes is on
|
|
58548
|
+
* screen at a time: it names the anchor while the popup is closed, and the
|
|
58549
|
+
* popup while it is open. What continues the anchor INSIDE the popup is
|
|
58550
|
+
* whatever carries `data-grow`, and the popup itself when nothing does — a
|
|
58551
|
+
* dialog holding the grown card plus buttons around it grows the card, a
|
|
58552
|
+
* dialog that IS the grown thing grows whole.
|
|
58553
|
+
*
|
|
58554
|
+
* The page around stays out of the picture (`view-transition-name: none` on
|
|
58555
|
+
* the root, in dialog.jsx): a captured element is not painted where it stands
|
|
58556
|
+
* and cannot be pointed at either, so photographing the whole document would
|
|
58557
|
+
* leave the page frozen and unpressable for the length of every opening — and
|
|
58558
|
+
* of every closing, which is the moment the user is coming back to it.
|
|
58559
|
+
*/
|
|
58560
|
+
|
|
58561
|
+
|
|
58562
|
+
// The name the two boxes take turns wearing. A single literal one is enough,
|
|
58563
|
+
// and unique by construction: a document has one view transition, so it has at
|
|
58564
|
+
// most one popup growing, and the movement being replaced gives the name back
|
|
58565
|
+
// before the next one takes it (see releaseGrowInProgress).
|
|
58566
|
+
const NAME = "navi-popup-grow";
|
|
58567
|
+
const NAME_PROPERTY = "view-transition-name";
|
|
58568
|
+
// Worn by the root for the length of the movement — what the CSS keys the
|
|
58569
|
+
// page's own opt-out on, and the movement's only trace in the document.
|
|
58570
|
+
const ROOT_ATTRIBUTE = "data-navi-popup-grow";
|
|
58571
|
+
// Inside the popup, the one node that IS the anchor once it has grown.
|
|
58572
|
+
const TARGET_SELECTOR = "[data-grow]";
|
|
58573
|
+
// The popup's own animation duration, published on the root because the
|
|
58574
|
+
// ::view-transition tree hangs off it and inherits from nowhere else.
|
|
58575
|
+
const DURATION_PROPERTY = "--navi-popup-grow-duration";
|
|
58576
|
+
|
|
58577
|
+
let releaseGrowInProgress = null;
|
|
58578
|
+
|
|
58579
|
+
/**
|
|
58580
|
+
* Runs `applyChange` — the DOM change that opens or closes `popupEl` — inside
|
|
58581
|
+
* a view transition morphing the anchor's box into the popup's, or back.
|
|
58582
|
+
*
|
|
58583
|
+
* `opened` says which way: the box being left is the anchor when the popup is
|
|
58584
|
+
* opening and the popup when it is closing, and the arriving one is only known
|
|
58585
|
+
* once the change has been made (the content a popup grows into is built by
|
|
58586
|
+
* that very change).
|
|
58587
|
+
*/
|
|
58588
|
+
const growPopupFromAnchor = (
|
|
58589
|
+
popupEl,
|
|
58590
|
+
anchorElement,
|
|
58591
|
+
applyChange,
|
|
58592
|
+
{ opened },
|
|
58593
|
+
) => {
|
|
58594
|
+
const startViewTransition = ensureDocumentStartViewTransition();
|
|
58595
|
+
// A movement still wearing the name would make the name two elements wide,
|
|
58596
|
+
// and a name belonging to two elements aborts the transition for the whole
|
|
58597
|
+
// document.
|
|
58598
|
+
releaseGrowInProgress?.();
|
|
58599
|
+
|
|
58600
|
+
const elementLeaving = opened ? anchorElement : resolveGrowTarget(popupEl);
|
|
58601
|
+
const giveBackNameLeaving = wearGrowName(elementLeaving);
|
|
58602
|
+
const root = document.documentElement;
|
|
58603
|
+
root.setAttribute(ROOT_ATTRIBUTE, "");
|
|
58604
|
+
const duration = getComputedStyle(popupEl)
|
|
58605
|
+
.getPropertyValue("--popup-animation-duration")
|
|
58606
|
+
.trim();
|
|
58607
|
+
if (duration) {
|
|
58608
|
+
// Empty would substitute into `animation-duration:` as nothing at all,
|
|
58609
|
+
// which computes to 0s — a movement nobody sees rather than one at the
|
|
58610
|
+
// browser's own pace.
|
|
58611
|
+
root.style.setProperty(DURATION_PROPERTY, duration);
|
|
58612
|
+
}
|
|
58613
|
+
|
|
58614
|
+
let giveBackNameArriving = null;
|
|
58615
|
+
const release = () => {
|
|
58616
|
+
if (releaseGrowInProgress !== release) {
|
|
58617
|
+
return;
|
|
58618
|
+
}
|
|
58619
|
+
releaseGrowInProgress = null;
|
|
58620
|
+
giveBackNameLeaving();
|
|
58621
|
+
giveBackNameArriving?.();
|
|
58622
|
+
root.removeAttribute(ROOT_ATTRIBUTE);
|
|
58623
|
+
root.style.removeProperty(DURATION_PROPERTY);
|
|
58624
|
+
};
|
|
58625
|
+
releaseGrowInProgress = release;
|
|
58626
|
+
|
|
58627
|
+
const viewTransition = startViewTransition(() => {
|
|
58628
|
+
// The name is the arriving box's from here on: worn by both, it is worn by
|
|
58629
|
+
// neither. Written rather than removed, so a name the element also has
|
|
58630
|
+
// from a stylesheet cannot resurface for the length of the movement.
|
|
58631
|
+
elementLeaving.style.setProperty(NAME_PROPERTY, "none");
|
|
58632
|
+
applyChange();
|
|
58633
|
+
const elementArriving = opened
|
|
58634
|
+
? resolveGrowTarget(popupEl)
|
|
58635
|
+
: // Gone from the document while the popup was open (the row it stood
|
|
58636
|
+
// in was removed): nothing to arrive at, and the browser plays the
|
|
58637
|
+
// popup's picture out on its own.
|
|
58638
|
+
anchorElement.isConnected
|
|
58639
|
+
? anchorElement
|
|
58640
|
+
: null;
|
|
58641
|
+
if (elementArriving) {
|
|
58642
|
+
giveBackNameArriving = wearGrowName(elementArriving);
|
|
58643
|
+
}
|
|
58644
|
+
});
|
|
58645
|
+
viewTransition.finished.then(release, release);
|
|
58646
|
+
};
|
|
58647
|
+
|
|
58648
|
+
const resolveGrowTarget = (popupEl) => {
|
|
58649
|
+
const declaredTarget = popupEl.querySelector(TARGET_SELECTOR);
|
|
58650
|
+
if (declaredTarget) {
|
|
58651
|
+
return declaredTarget;
|
|
58652
|
+
}
|
|
58653
|
+
return popupEl;
|
|
58654
|
+
};
|
|
58655
|
+
|
|
58656
|
+
// Wears the movement's name, and gives back whatever the element had written
|
|
58657
|
+
// inline of its own once the movement is over.
|
|
58658
|
+
const wearGrowName = (element) => {
|
|
58659
|
+
const nameBefore = element.style.getPropertyValue(NAME_PROPERTY);
|
|
58660
|
+
element.style.setProperty(NAME_PROPERTY, NAME);
|
|
58661
|
+
return () => {
|
|
58662
|
+
if (nameBefore) {
|
|
58663
|
+
element.style.setProperty(NAME_PROPERTY, nameBefore);
|
|
58664
|
+
return;
|
|
58665
|
+
}
|
|
58666
|
+
element.style.removeProperty(NAME_PROPERTY);
|
|
58667
|
+
};
|
|
58668
|
+
};
|
|
58669
|
+
|
|
58466
58670
|
const PopupClose = ({
|
|
58467
58671
|
label,
|
|
58468
58672
|
iconSize,
|
|
@@ -58512,12 +58716,14 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
58512
58716
|
* popup_shared.js), even though several combinations land identically here
|
|
58513
58717
|
* since Dialog is never really anchored — kept distinct anyway because
|
|
58514
58718
|
* `positionArea` still picks which animation direction plays. `anchor` is
|
|
58515
|
-
* inert here unless
|
|
58516
|
-
*
|
|
58517
|
-
*
|
|
58518
|
-
* reaches the `--anchor-width`/`--anchor-height` CSS vars and becomes a
|
|
58519
|
-
* min-width/min-height floor.
|
|
58520
|
-
*
|
|
58719
|
+
* inert here unless something asks for it: a dialog is a surface of its own,
|
|
58720
|
+
* sized by its content, not a panel grown out of the control that opened it —
|
|
58721
|
+
* that is Popover's job. Two props ask. With `sizeFromAnchor`, the anchor's
|
|
58722
|
+
* box reaches the `--anchor-width`/`--anchor-height` CSS vars and becomes a
|
|
58723
|
+
* min-width/min-height floor. With `animation="growing"`, the anchor's box is
|
|
58724
|
+
* where the dialog comes from: the two are photographed and morphed into each
|
|
58725
|
+
* other, and back on close (popup_grow.js). Either way Dialog's own
|
|
58726
|
+
* positioning is never relative to the anchor, unlike Popover.
|
|
58521
58727
|
*
|
|
58522
58728
|
* Two rendering strategies, picked via `layer`: `DialogAsModal` (a real
|
|
58523
58729
|
* `<dialog>`, `showModal()`, top layer — native focus trap,
|
|
@@ -58962,6 +59168,27 @@ const css$E = /* css */`
|
|
|
58962
59168
|
}
|
|
58963
59169
|
}
|
|
58964
59170
|
|
|
59171
|
+
/* While a dialog is growing out of the element that opened it
|
|
59172
|
+
(popup_grow.js). The page around is deliberately NOT taken as a picture,
|
|
59173
|
+
against the browser's own default: a captured element is not painted where
|
|
59174
|
+
it stands and cannot be pointed at either, so photographing the document
|
|
59175
|
+
would leave the whole page frozen and unpressable for the length of every
|
|
59176
|
+
opening — and of every closing, which is the moment the user is coming
|
|
59177
|
+
back to it. Nothing shows through where the movement is: the two boxes it
|
|
59178
|
+
plays between are captured, and their pictures cover the rectangle between
|
|
59179
|
+
them at every moment. */
|
|
59180
|
+
:root[data-navi-popup-grow] {
|
|
59181
|
+
view-transition-name: none;
|
|
59182
|
+
|
|
59183
|
+
/* The popup's own pace, published on the root by popup_grow.js because the
|
|
59184
|
+
::view-transition tree hangs off it and inherits from nowhere else. */
|
|
59185
|
+
&::view-transition-group(navi-popup-grow),
|
|
59186
|
+
&::view-transition-old(navi-popup-grow),
|
|
59187
|
+
&::view-transition-new(navi-popup-grow) {
|
|
59188
|
+
animation-duration: var(--navi-popup-grow-duration, 0.25s);
|
|
59189
|
+
}
|
|
59190
|
+
}
|
|
59191
|
+
|
|
58965
59192
|
${surfaceTextCss}
|
|
58966
59193
|
${popupCss}
|
|
58967
59194
|
`;
|
|
@@ -59055,16 +59282,25 @@ const css$E = /* css */`
|
|
|
59055
59282
|
* scroll while open (its backdrop only covers the scrollport, so scrolling
|
|
59056
59283
|
* there would reveal uncovered content); this prop extends the lock to the
|
|
59057
59284
|
* whole page. Defaults to `true` for a dialog docked by `dockedOnSmallTouchScreen`.
|
|
59058
|
-
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`} [props.animation]
|
|
59285
|
+
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"growing"|`slide-from-${string}`} [props.animation]
|
|
59059
59286
|
* - `true`/`"auto"` resolves to `"scaling"` for a centered `positionArea`,
|
|
59060
59287
|
* or a concrete `"slide-from-*"` direction otherwise. Any other explicit
|
|
59061
|
-
* value is used as-is.
|
|
59288
|
+
* value is used as-is. `"growing"` is the odd one out: every other kind
|
|
59289
|
+
* moves the dialog on its own box (it scales from its own centre, slides in
|
|
59290
|
+
* from its own edge), while this one is the browser morphing the ANCHOR's
|
|
59291
|
+
* box into the dialog's, and the dialog's back into the anchor's on close —
|
|
59292
|
+
* for a dialog that is what the anchor became rather than a surface shown
|
|
59293
|
+
* over it. It needs an anchor (whatever opened the dialog, or the `anchor`
|
|
59294
|
+
* prop) and grows into whatever inside the dialog carries `data-grow`, the
|
|
59295
|
+
* dialog itself when nothing does. `"auto"` never picks it: only the caller
|
|
59296
|
+
* knows the two boxes are one object. See `popup_grow.js`.
|
|
59062
59297
|
* @param {string} [props.animationDuration] - Maps to
|
|
59063
59298
|
* `--popup-animation-duration`.
|
|
59064
59299
|
* @param {Element|{current: Element}|string} [props.anchor] - Never used for
|
|
59065
59300
|
* positioning (see this file's top comment), and ignored entirely unless
|
|
59066
|
-
* `sizeFromAnchor`
|
|
59067
|
-
* `--anchor-width`/`--anchor-height` CSS vars
|
|
59301
|
+
* `sizeFromAnchor` or `animation="growing"` asks for it — the first sizes
|
|
59302
|
+
* the dialog via the `--anchor-width`/`--anchor-height` CSS vars, the second
|
|
59303
|
+
* makes the dialog come out of the anchor's own box. Defaults to whatever
|
|
59068
59304
|
* triggered the open (`e.detail.anchor`), if any. A string is resolved via
|
|
59069
59305
|
* `document.getElementById` when the dialog opens — see popover.jsx's own
|
|
59070
59306
|
* `anchor` doc for why (mainly `defaultOpen`).
|
|
@@ -59461,6 +59697,8 @@ const useDialogProps = props => {
|
|
|
59461
59697
|
marginWithContainer
|
|
59462
59698
|
};
|
|
59463
59699
|
const repositionRef = useRef(null);
|
|
59700
|
+
// The element this dialog came out of, held from the opening to the closing.
|
|
59701
|
+
const anchorElementRef = useRef(null);
|
|
59464
59702
|
useEffect(() => {
|
|
59465
59703
|
repositionRef.current?.(new CustomEvent("position_props_change", {
|
|
59466
59704
|
detail: {}
|
|
@@ -59519,20 +59757,87 @@ const useDialogProps = props => {
|
|
|
59519
59757
|
flushEdges.right = expandX || x === "right" || x === "inset-right";
|
|
59520
59758
|
}
|
|
59521
59759
|
const isAutoAnimation = animation === true || animation === "auto";
|
|
59522
|
-
//
|
|
59523
|
-
//
|
|
59524
|
-
//
|
|
59760
|
+
// The dialog and the anchor are one box, and what plays between them is the
|
|
59761
|
+
// browser's own morph (popup_grow.js) — nothing this dialog does to its own
|
|
59762
|
+
// box. So it arms no CSS transition of its own, which is not merely useless
|
|
59763
|
+
// here but wrong: the shared [navi-animation] rule transitions `display`
|
|
59764
|
+
// with allow-discrete, and a dialog kept rendered for the length of its exit
|
|
59765
|
+
// is exactly what the picture taken of the state it closes into must not
|
|
59766
|
+
// show.
|
|
59767
|
+
const growing = animation === "growing";
|
|
59768
|
+
// Dialog never has a real anchor to POSITION against (see this file's top
|
|
59769
|
+
// comment), so this is always the "no anchor" path — the same one Popover's
|
|
59770
|
+
// own custom renderer falls into when it has no real anchor either.
|
|
59525
59771
|
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) : animation;
|
|
59526
59772
|
// Not gated on isAutoAnimation — an explicit animation="sliding" needs a
|
|
59527
59773
|
// concrete direction just as much as an auto-resolved one does (same as
|
|
59528
59774
|
// Popover's own "sliding"/"expanding" resolution step in openEffect).
|
|
59529
|
-
let resolvedAnimation = resolvedAnimationKind;
|
|
59775
|
+
let resolvedAnimation = growing ? undefined : resolvedAnimationKind;
|
|
59530
59776
|
if (resolvedAnimationKind === "sliding") {
|
|
59531
59777
|
resolvedAnimation = resolveDirectionValue(parsedPositionArea.y, parsedPositionArea.x, {
|
|
59532
59778
|
prefix: "slide-from"
|
|
59533
59779
|
}) ?? "slide-from-top";
|
|
59534
59780
|
}
|
|
59535
59781
|
|
|
59782
|
+
// What this dialog was opened out of, resolved at open time rather than at
|
|
59783
|
+
// render time — mainly for defaultOpen, where there's no triggering
|
|
59784
|
+
// event/ref yet to read a real element from, only an id known up front. See
|
|
59785
|
+
// popover.jsx's own anchor handling for the full reasoning, mirrored here
|
|
59786
|
+
// identically.
|
|
59787
|
+
const resolveAnchorElement = e => {
|
|
59788
|
+
if (typeof anchor === "string") {
|
|
59789
|
+
const anchorElementById = document.getElementById(anchor);
|
|
59790
|
+
if (!anchorElementById) {
|
|
59791
|
+
console.warn(`Dialog: anchor="${anchor}" did not match any element`);
|
|
59792
|
+
}
|
|
59793
|
+
return anchorElementById;
|
|
59794
|
+
}
|
|
59795
|
+
if (anchor) {
|
|
59796
|
+
// anchor prop is a ref or a DOM element — always a real anchor,
|
|
59797
|
+
// regardless of anchorCustomEventDetail. A ref is unwrapped even when it
|
|
59798
|
+
// holds nothing: falling back to the ref object itself would pass the
|
|
59799
|
+
// "is there an anchor?" tests downstream with something that has no box.
|
|
59800
|
+
return "current" in anchor ? anchor.current : anchor;
|
|
59801
|
+
}
|
|
59802
|
+
if (anchorCustomEventDetail === "override") {
|
|
59803
|
+
// e.g. the button that triggered a --navi-toggle/--navi-open command,
|
|
59804
|
+
// already resolved from detail.anchor/detail.source by the caller
|
|
59805
|
+
// (see UncontrolledDialog's onnavi_request_open).
|
|
59806
|
+
return e.detail.anchor;
|
|
59807
|
+
}
|
|
59808
|
+
return undefined;
|
|
59809
|
+
};
|
|
59810
|
+
|
|
59811
|
+
// The dialog and the anchor are the same box at two sizes, so the opening
|
|
59812
|
+
// and the closing are one becoming the other. The browser draws that itself
|
|
59813
|
+
// provided the change happens between its two pictures, which is what
|
|
59814
|
+
// handing it to the controller buys (see popup_grow.js and
|
|
59815
|
+
// open_controller.js's own transitionChange).
|
|
59816
|
+
openController.transitionChange = growing ? (applyChange, {
|
|
59817
|
+
opened,
|
|
59818
|
+
event
|
|
59819
|
+
}) => {
|
|
59820
|
+
const dialogEl = ref.current;
|
|
59821
|
+
// A mount-time opening was never seen closed (see openEffect's own
|
|
59822
|
+
// `silent`): there is no box it comes from, because nothing was shown
|
|
59823
|
+
// before it.
|
|
59824
|
+
if (!dialogEl || opened && event.detail.silent) {
|
|
59825
|
+
applyChange();
|
|
59826
|
+
return;
|
|
59827
|
+
}
|
|
59828
|
+
const anchorElement = opened ? resolveAnchorElement(event) : anchorElementRef.current;
|
|
59829
|
+
if (!anchorElement) {
|
|
59830
|
+
if (opened) {
|
|
59831
|
+
console.warn(`[navi] Dialog has animation="growing" and no anchor to grow 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.`);
|
|
59832
|
+
}
|
|
59833
|
+
applyChange();
|
|
59834
|
+
return;
|
|
59835
|
+
}
|
|
59836
|
+
growPopupFromAnchor(dialogEl, anchorElement, applyChange, {
|
|
59837
|
+
opened
|
|
59838
|
+
});
|
|
59839
|
+
} : null;
|
|
59840
|
+
|
|
59536
59841
|
// Sync the DOM open and return how to sync it back closed, fresh on every
|
|
59537
59842
|
// render so it closes over the latest props (scrollLock, etc.). The
|
|
59538
59843
|
// controller (owned by the caller, or by UncontrolledDialog) decides
|
|
@@ -59562,28 +59867,10 @@ const useDialogProps = props => {
|
|
|
59562
59867
|
// real, meaningful ancestor beyond it.
|
|
59563
59868
|
const positionedAncestor = isModal ? document.documentElement : getPositionedParent(dialogEl.parentElement /* dialogEl is inside the clip_wrapper */);
|
|
59564
59869
|
const [cleanup, addCleanup] = createPubSub(true);
|
|
59565
|
-
|
|
59566
|
-
|
|
59567
|
-
|
|
59568
|
-
|
|
59569
|
-
// real element from, only an id known up front. See popover.jsx's own
|
|
59570
|
-
// anchor handling for the full reasoning, mirrored here identically.
|
|
59571
|
-
anchorElement = document.getElementById(anchor);
|
|
59572
|
-
if (!anchorElement) {
|
|
59573
|
-
console.warn(`Dialog: anchor="${anchor}" did not match any element`);
|
|
59574
|
-
}
|
|
59575
|
-
} else if (anchor) {
|
|
59576
|
-
// anchor prop is a ref or a DOM element — always a real anchor,
|
|
59577
|
-
// regardless of anchorCustomEventDetail. A ref is unwrapped even when it
|
|
59578
|
-
// holds nothing: falling back to the ref object itself would pass the
|
|
59579
|
-
// "is there an anchor?" test below with something that has no box.
|
|
59580
|
-
anchorElement = "current" in anchor ? anchor.current : anchor;
|
|
59581
|
-
} else if (anchorCustomEventDetail === "override") {
|
|
59582
|
-
// e.g. the button that triggered a --navi-toggle/--navi-open command,
|
|
59583
|
-
// already resolved from detail.anchor/detail.source by the caller
|
|
59584
|
-
// (see UncontrolledDialog's onnavi_request_open).
|
|
59585
|
-
anchorElement = e.detail.anchor;
|
|
59586
|
-
}
|
|
59870
|
+
const anchorElement = resolveAnchorElement(e);
|
|
59871
|
+
// Kept for the closing, which has no opening event to read it back from:
|
|
59872
|
+
// the box the dialog shrinks back into is the one it came out of.
|
|
59873
|
+
anchorElementRef.current = anchorElement;
|
|
59587
59874
|
debugPopup(`"${e.type}" on ${getElementSignature(e.target)} -> openDialog`);
|
|
59588
59875
|
if (!isModal) {
|
|
59589
59876
|
// see openLocalDialogCount's own comment
|