@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/jsenv_navi.js
CHANGED
|
@@ -27373,7 +27373,7 @@ const FURNITURE_SELECTOR = `.navi_fixed_bar, .navi_popover:is(${TOP_LAYER_SELECT
|
|
|
27373
27373
|
// owns it). Written out rather than imported: importing the module that owns
|
|
27374
27374
|
// it back into this one would close a cycle.
|
|
27375
27375
|
const TRANSITION_ATTRIBUTE$1 = "data-navi-route-transition";
|
|
27376
|
-
const NAME_PROPERTY = "view-transition-name";
|
|
27376
|
+
const NAME_PROPERTY$1 = "view-transition-name";
|
|
27377
27377
|
const FURNITURE_NAME_PREFIX = "navi-transition-furniture-";
|
|
27378
27378
|
|
|
27379
27379
|
const nameByElement = new WeakMap();
|
|
@@ -27458,7 +27458,7 @@ const nameFurnitureAround = (areaElement) => {
|
|
|
27458
27458
|
name = `${FURNITURE_NAME_PREFIX}${nameCount}`;
|
|
27459
27459
|
nameByElement.set(element, name);
|
|
27460
27460
|
}
|
|
27461
|
-
element.style.setProperty(NAME_PROPERTY, name);
|
|
27461
|
+
element.style.setProperty(NAME_PROPERTY$1, name);
|
|
27462
27462
|
namedElements.add(element);
|
|
27463
27463
|
namesAdded.push(name);
|
|
27464
27464
|
}
|
|
@@ -27494,7 +27494,7 @@ const releaseTransitionFurniture = (owner) => {
|
|
|
27494
27494
|
}
|
|
27495
27495
|
furnitureOwner = null;
|
|
27496
27496
|
for (const element of namedElements) {
|
|
27497
|
-
element.style.removeProperty(NAME_PROPERTY);
|
|
27497
|
+
element.style.removeProperty(NAME_PROPERTY$1);
|
|
27498
27498
|
}
|
|
27499
27499
|
namedElements = new Set();
|
|
27500
27500
|
if (travelStyleElement) {
|
|
@@ -44470,6 +44470,43 @@ const createOpenController = (
|
|
|
44470
44470
|
document.addEventListener("click", onCaptureClick, { capture: true });
|
|
44471
44471
|
};
|
|
44472
44472
|
|
|
44473
|
+
// The DOM change a popup asked to have photographed (see
|
|
44474
|
+
// controller.transitionChange), waiting for the browser to take the picture
|
|
44475
|
+
// of the state being left. Anything the controller is asked to do meanwhile
|
|
44476
|
+
// happens after it: the change is run on the spot, and the transition holding
|
|
44477
|
+
// it finds nothing left to do.
|
|
44478
|
+
let changeAwaitingTransition = null;
|
|
44479
|
+
const flushChangeAwaitingTransition = () => {
|
|
44480
|
+
const change = changeAwaitingTransition;
|
|
44481
|
+
if (!change) {
|
|
44482
|
+
return;
|
|
44483
|
+
}
|
|
44484
|
+
changeAwaitingTransition = null;
|
|
44485
|
+
change();
|
|
44486
|
+
};
|
|
44487
|
+
const runChange = (change, { opened, event }) => {
|
|
44488
|
+
const { transitionChange } = controller;
|
|
44489
|
+
if (!transitionChange) {
|
|
44490
|
+
change();
|
|
44491
|
+
return;
|
|
44492
|
+
}
|
|
44493
|
+
// What the controller answers about itself does not wait for the picture:
|
|
44494
|
+
// whoever just asked reads `opened` on the spot (see
|
|
44495
|
+
// useOpenPropsEffectOnOpenController, which writes it back into the
|
|
44496
|
+
// caller's own signal), and a request arriving before the change lands
|
|
44497
|
+
// runs it first rather than reading a DOM that disagrees.
|
|
44498
|
+
controller.opened = opened;
|
|
44499
|
+
changeAwaitingTransition = change;
|
|
44500
|
+
transitionChange(
|
|
44501
|
+
() => {
|
|
44502
|
+
if (changeAwaitingTransition === change) {
|
|
44503
|
+
flushChangeAwaitingTransition();
|
|
44504
|
+
}
|
|
44505
|
+
},
|
|
44506
|
+
{ opened, event },
|
|
44507
|
+
);
|
|
44508
|
+
};
|
|
44509
|
+
|
|
44473
44510
|
const performClose = (closeEvent) => {
|
|
44474
44511
|
controller.opened = false;
|
|
44475
44512
|
// Read before any close effect touches the DOM: closing a native <dialog>
|
|
@@ -44552,18 +44589,23 @@ const createOpenController = (
|
|
|
44552
44589
|
}
|
|
44553
44590
|
}
|
|
44554
44591
|
|
|
44555
|
-
|
|
44556
|
-
|
|
44557
|
-
|
|
44558
|
-
|
|
44559
|
-
|
|
44560
|
-
|
|
44561
|
-
|
|
44562
|
-
|
|
44563
|
-
|
|
44564
|
-
|
|
44565
|
-
|
|
44566
|
-
|
|
44592
|
+
runChange(
|
|
44593
|
+
() => {
|
|
44594
|
+
// Sync the DOM closed first (releasing the focus trap) — only then run
|
|
44595
|
+
// the owner's own reaction (onClose may restore focus to an element
|
|
44596
|
+
// outside the popup, which the focus trap would otherwise fight while
|
|
44597
|
+
// still active).
|
|
44598
|
+
openEffectCleanup?.(closeEvent);
|
|
44599
|
+
openEffectCleanup = null;
|
|
44600
|
+
closeHandlers?.onClose?.(closeEvent);
|
|
44601
|
+
closeHandlers = null;
|
|
44602
|
+
// Last: the close effects above are what starts the exit transition the
|
|
44603
|
+
// content must outlive (see popup_content_mount.js).
|
|
44604
|
+
controller.unmountContent?.();
|
|
44605
|
+
controller.onOpenedChange?.(false, closeEvent);
|
|
44606
|
+
},
|
|
44607
|
+
{ opened: false, event: closeEvent },
|
|
44608
|
+
);
|
|
44567
44609
|
};
|
|
44568
44610
|
const controller = {
|
|
44569
44611
|
opened: false,
|
|
@@ -44582,6 +44624,15 @@ const createOpenController = (
|
|
|
44582
44624
|
// The counterpart, set only when the popup was told to throw its content
|
|
44583
44625
|
// away on close (`mount="while-opened"`). Called from performClose above.
|
|
44584
44626
|
unmountContent: null,
|
|
44627
|
+
// Set by the controlled element when the DOM change that opens or closes
|
|
44628
|
+
// it has to be photographed by the browser on both sides — a document view
|
|
44629
|
+
// transition, whose update callback is the only place that change can
|
|
44630
|
+
// happen (Dialog's animation="growing", see popup_grow.js). Called with
|
|
44631
|
+
// the change and where it leads; running it is its job, and it may run it
|
|
44632
|
+
// a frame later than it was asked for. That delay is the reason a popup
|
|
44633
|
+
// cannot do this from the outside: `--navi-open` runs when navi runs it,
|
|
44634
|
+
// and a close arrives once the DOM already holds it.
|
|
44635
|
+
transitionChange: null,
|
|
44585
44636
|
// Told whenever `opened` actually changes, whatever asked for it — an
|
|
44586
44637
|
// interaction, a command, a prop — with the event that asked. What lets a
|
|
44587
44638
|
// `signal` prop reflect the popup's real state, and a `navState` prop write
|
|
@@ -44589,6 +44640,7 @@ const createOpenController = (
|
|
|
44589
44640
|
// called once the open/close has fully happened rather than mid-sequence.
|
|
44590
44641
|
onOpenedChange: null,
|
|
44591
44642
|
open: (e, detail) => {
|
|
44643
|
+
flushChangeAwaitingTransition();
|
|
44592
44644
|
if (controller.opened || !controller.openEffect) {
|
|
44593
44645
|
return;
|
|
44594
44646
|
}
|
|
@@ -44664,38 +44716,44 @@ const createOpenController = (
|
|
|
44664
44716
|
}
|
|
44665
44717
|
};
|
|
44666
44718
|
};
|
|
44667
|
-
|
|
44668
|
-
|
|
44669
|
-
|
|
44670
|
-
|
|
44671
|
-
|
|
44672
|
-
|
|
44673
|
-
|
|
44674
|
-
|
|
44675
|
-
|
|
44676
|
-
|
|
44677
|
-
|
|
44678
|
-
|
|
44679
|
-
|
|
44680
|
-
|
|
44681
|
-
|
|
44682
|
-
|
|
44683
|
-
|
|
44684
|
-
|
|
44685
|
-
|
|
44686
|
-
|
|
44687
|
-
|
|
44688
|
-
|
|
44689
|
-
|
|
44690
|
-
|
|
44691
|
-
|
|
44692
|
-
|
|
44693
|
-
|
|
44719
|
+
runChange(
|
|
44720
|
+
() => {
|
|
44721
|
+
// Before mountContent, which builds the content, and before
|
|
44722
|
+
// openEffect, which shows it: what the popup opens ON has to be
|
|
44723
|
+
// known before either (see `onOpen` above).
|
|
44724
|
+
controller.onOpen?.(requestOpenEvent);
|
|
44725
|
+
// After prepareFocusTransfer, which has to record what held the
|
|
44726
|
+
// focus before anything inside the popup can claim it, and before
|
|
44727
|
+
// openEffect, which measures the popup to place it.
|
|
44728
|
+
controller.mountContent?.();
|
|
44729
|
+
// Only now — after the content has been built, before openEffect
|
|
44730
|
+
// shows it. Dialog/Popover recompute aria-expanded and navi-hidden
|
|
44731
|
+
// from this flag on every render, and mountContent above renders
|
|
44732
|
+
// synchronously: flipping it any earlier commits an already-open DOM
|
|
44733
|
+
// (aria-expanded "true", navi-hidden gone) before openEffect has run
|
|
44734
|
+
// a single statement, so the "closed" frame it pins to transition
|
|
44735
|
+
// from is in fact the open one and the entrance animation has
|
|
44736
|
+
// nothing to play. It also gives the content it just built the
|
|
44737
|
+
// opening it is documented to observe — mounted while the popup
|
|
44738
|
+
// reads as closed, told it opened right after (see
|
|
44739
|
+
// popup_content_mount.js and use_displayed_layout_effect.js).
|
|
44740
|
+
controller.opened = true;
|
|
44741
|
+
const openEffectReturnValue =
|
|
44742
|
+
controller.openEffect(requestOpenEvent) || null;
|
|
44743
|
+
openEffectCleanup = (closeEvent) => {
|
|
44744
|
+
openEffectReturnValue?.(closeEvent);
|
|
44745
|
+
};
|
|
44746
|
+
closeHandlers = openHandler(requestOpenEvent) || null;
|
|
44747
|
+
controller.onOpenedChange?.(true, requestOpenEvent);
|
|
44748
|
+
},
|
|
44749
|
+
{ opened: true, event: requestOpenEvent },
|
|
44750
|
+
);
|
|
44694
44751
|
},
|
|
44695
44752
|
requestClose: (
|
|
44696
44753
|
e = new CustomEvent("programmatic", { detail: {} }),
|
|
44697
44754
|
detail,
|
|
44698
44755
|
) => {
|
|
44756
|
+
flushChangeAwaitingTransition();
|
|
44699
44757
|
if (!controller.opened) {
|
|
44700
44758
|
return true;
|
|
44701
44759
|
}
|
|
@@ -44718,6 +44776,7 @@ const createOpenController = (
|
|
|
44718
44776
|
return true;
|
|
44719
44777
|
},
|
|
44720
44778
|
close: (e = new CustomEvent("programmatic", { detail: {} }), detail) => {
|
|
44779
|
+
flushChangeAwaitingTransition();
|
|
44721
44780
|
if (!controller.opened) {
|
|
44722
44781
|
return;
|
|
44723
44782
|
}
|
|
@@ -44768,6 +44827,11 @@ const useOpenController = (openHandler) => {
|
|
|
44768
44827
|
// no choice to leave open — close it for real.
|
|
44769
44828
|
useLayoutEffect(() => {
|
|
44770
44829
|
return () => {
|
|
44830
|
+
// Nothing to photograph on the way out: the popup is leaving the
|
|
44831
|
+
// document, so a movement between its box and anything else would be
|
|
44832
|
+
// played on an element already detached by the time the browser gets to
|
|
44833
|
+
// it (see controller.transitionChange).
|
|
44834
|
+
controllerRef.current.transitionChange = null;
|
|
44771
44835
|
controllerRef.current.close();
|
|
44772
44836
|
};
|
|
44773
44837
|
}, []);
|
|
@@ -56710,6 +56774,146 @@ const translateOf = (axis, distance) =>
|
|
|
56710
56774
|
? `translate(${distance}px, 0px)`
|
|
56711
56775
|
: `translate(0px, ${distance}px)`;
|
|
56712
56776
|
|
|
56777
|
+
/**
|
|
56778
|
+
* A popup that is the anchor, continued.
|
|
56779
|
+
*
|
|
56780
|
+
* Opening a surface out of the element that asked for it is not the surface
|
|
56781
|
+
* appearing while that element stays put: it is one box becoming another. The
|
|
56782
|
+
* browser draws exactly that on its own — the same `view-transition-name` on
|
|
56783
|
+
* the anchor before the change and on the popup after it, and it morphs the
|
|
56784
|
+
* first box into the second — so all this file does is hand that name over at
|
|
56785
|
+
* the two instants it can be handed over, and give it back afterwards.
|
|
56786
|
+
*
|
|
56787
|
+
* The pictures are taken around a DOM change, which is why the movement cannot
|
|
56788
|
+
* be written from outside: what a caller can reach — a command, `onClose` — is
|
|
56789
|
+
* either before the change or after it, never around it. The change that opens
|
|
56790
|
+
* a popup, and above all the one that closes it, belongs to the open
|
|
56791
|
+
* controller, which is what `transitionChange` (open_controller.js) exists to
|
|
56792
|
+
* let a popup wrap.
|
|
56793
|
+
*
|
|
56794
|
+
* One name serves the whole movement, because only one of the two boxes is on
|
|
56795
|
+
* screen at a time: it names the anchor while the popup is closed, and the
|
|
56796
|
+
* popup while it is open. What continues the anchor INSIDE the popup is
|
|
56797
|
+
* whatever carries `data-grow`, and the popup itself when nothing does — a
|
|
56798
|
+
* dialog holding the grown card plus buttons around it grows the card, a
|
|
56799
|
+
* dialog that IS the grown thing grows whole.
|
|
56800
|
+
*
|
|
56801
|
+
* The page around stays out of the picture (`view-transition-name: none` on
|
|
56802
|
+
* the root, in dialog.jsx): a captured element is not painted where it stands
|
|
56803
|
+
* and cannot be pointed at either, so photographing the whole document would
|
|
56804
|
+
* leave the page frozen and unpressable for the length of every opening — and
|
|
56805
|
+
* of every closing, which is the moment the user is coming back to it.
|
|
56806
|
+
*/
|
|
56807
|
+
|
|
56808
|
+
|
|
56809
|
+
// The name the two boxes take turns wearing. A single literal one is enough,
|
|
56810
|
+
// and unique by construction: a document has one view transition, so it has at
|
|
56811
|
+
// most one popup growing, and the movement being replaced gives the name back
|
|
56812
|
+
// before the next one takes it (see releaseGrowInProgress).
|
|
56813
|
+
const NAME = "navi-popup-grow";
|
|
56814
|
+
const NAME_PROPERTY = "view-transition-name";
|
|
56815
|
+
// Worn by the root for the length of the movement — what the CSS keys the
|
|
56816
|
+
// page's own opt-out on, and the movement's only trace in the document.
|
|
56817
|
+
const ROOT_ATTRIBUTE = "data-navi-popup-grow";
|
|
56818
|
+
// Inside the popup, the one node that IS the anchor once it has grown.
|
|
56819
|
+
const TARGET_SELECTOR = "[data-grow]";
|
|
56820
|
+
// The popup's own animation duration, published on the root because the
|
|
56821
|
+
// ::view-transition tree hangs off it and inherits from nowhere else.
|
|
56822
|
+
const DURATION_PROPERTY = "--navi-popup-grow-duration";
|
|
56823
|
+
|
|
56824
|
+
let releaseGrowInProgress = null;
|
|
56825
|
+
|
|
56826
|
+
/**
|
|
56827
|
+
* Runs `applyChange` — the DOM change that opens or closes `popupEl` — inside
|
|
56828
|
+
* a view transition morphing the anchor's box into the popup's, or back.
|
|
56829
|
+
*
|
|
56830
|
+
* `opened` says which way: the box being left is the anchor when the popup is
|
|
56831
|
+
* opening and the popup when it is closing, and the arriving one is only known
|
|
56832
|
+
* once the change has been made (the content a popup grows into is built by
|
|
56833
|
+
* that very change).
|
|
56834
|
+
*/
|
|
56835
|
+
const growPopupFromAnchor = (
|
|
56836
|
+
popupEl,
|
|
56837
|
+
anchorElement,
|
|
56838
|
+
applyChange,
|
|
56839
|
+
{ opened },
|
|
56840
|
+
) => {
|
|
56841
|
+
const startViewTransition = ensureDocumentStartViewTransition();
|
|
56842
|
+
// A movement still wearing the name would make the name two elements wide,
|
|
56843
|
+
// and a name belonging to two elements aborts the transition for the whole
|
|
56844
|
+
// document.
|
|
56845
|
+
releaseGrowInProgress?.();
|
|
56846
|
+
|
|
56847
|
+
const elementLeaving = opened ? anchorElement : resolveGrowTarget(popupEl);
|
|
56848
|
+
const giveBackNameLeaving = wearGrowName(elementLeaving);
|
|
56849
|
+
const root = document.documentElement;
|
|
56850
|
+
root.setAttribute(ROOT_ATTRIBUTE, "");
|
|
56851
|
+
const duration = getComputedStyle(popupEl)
|
|
56852
|
+
.getPropertyValue("--popup-animation-duration")
|
|
56853
|
+
.trim();
|
|
56854
|
+
if (duration) {
|
|
56855
|
+
// Empty would substitute into `animation-duration:` as nothing at all,
|
|
56856
|
+
// which computes to 0s — a movement nobody sees rather than one at the
|
|
56857
|
+
// browser's own pace.
|
|
56858
|
+
root.style.setProperty(DURATION_PROPERTY, duration);
|
|
56859
|
+
}
|
|
56860
|
+
|
|
56861
|
+
let giveBackNameArriving = null;
|
|
56862
|
+
const release = () => {
|
|
56863
|
+
if (releaseGrowInProgress !== release) {
|
|
56864
|
+
return;
|
|
56865
|
+
}
|
|
56866
|
+
releaseGrowInProgress = null;
|
|
56867
|
+
giveBackNameLeaving();
|
|
56868
|
+
giveBackNameArriving?.();
|
|
56869
|
+
root.removeAttribute(ROOT_ATTRIBUTE);
|
|
56870
|
+
root.style.removeProperty(DURATION_PROPERTY);
|
|
56871
|
+
};
|
|
56872
|
+
releaseGrowInProgress = release;
|
|
56873
|
+
|
|
56874
|
+
const viewTransition = startViewTransition(() => {
|
|
56875
|
+
// The name is the arriving box's from here on: worn by both, it is worn by
|
|
56876
|
+
// neither. Written rather than removed, so a name the element also has
|
|
56877
|
+
// from a stylesheet cannot resurface for the length of the movement.
|
|
56878
|
+
elementLeaving.style.setProperty(NAME_PROPERTY, "none");
|
|
56879
|
+
applyChange();
|
|
56880
|
+
const elementArriving = opened
|
|
56881
|
+
? resolveGrowTarget(popupEl)
|
|
56882
|
+
: // Gone from the document while the popup was open (the row it stood
|
|
56883
|
+
// in was removed): nothing to arrive at, and the browser plays the
|
|
56884
|
+
// popup's picture out on its own.
|
|
56885
|
+
anchorElement.isConnected
|
|
56886
|
+
? anchorElement
|
|
56887
|
+
: null;
|
|
56888
|
+
if (elementArriving) {
|
|
56889
|
+
giveBackNameArriving = wearGrowName(elementArriving);
|
|
56890
|
+
}
|
|
56891
|
+
});
|
|
56892
|
+
viewTransition.finished.then(release, release);
|
|
56893
|
+
};
|
|
56894
|
+
|
|
56895
|
+
const resolveGrowTarget = (popupEl) => {
|
|
56896
|
+
const declaredTarget = popupEl.querySelector(TARGET_SELECTOR);
|
|
56897
|
+
if (declaredTarget) {
|
|
56898
|
+
return declaredTarget;
|
|
56899
|
+
}
|
|
56900
|
+
return popupEl;
|
|
56901
|
+
};
|
|
56902
|
+
|
|
56903
|
+
// Wears the movement's name, and gives back whatever the element had written
|
|
56904
|
+
// inline of its own once the movement is over.
|
|
56905
|
+
const wearGrowName = (element) => {
|
|
56906
|
+
const nameBefore = element.style.getPropertyValue(NAME_PROPERTY);
|
|
56907
|
+
element.style.setProperty(NAME_PROPERTY, NAME);
|
|
56908
|
+
return () => {
|
|
56909
|
+
if (nameBefore) {
|
|
56910
|
+
element.style.setProperty(NAME_PROPERTY, nameBefore);
|
|
56911
|
+
return;
|
|
56912
|
+
}
|
|
56913
|
+
element.style.removeProperty(NAME_PROPERTY);
|
|
56914
|
+
};
|
|
56915
|
+
};
|
|
56916
|
+
|
|
56713
56917
|
const PopupClose = ({
|
|
56714
56918
|
label,
|
|
56715
56919
|
iconSize,
|
|
@@ -56759,12 +56963,14 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
56759
56963
|
* popup_shared.js), even though several combinations land identically here
|
|
56760
56964
|
* since Dialog is never really anchored — kept distinct anyway because
|
|
56761
56965
|
* `positionArea` still picks which animation direction plays. `anchor` is
|
|
56762
|
-
* inert here unless
|
|
56763
|
-
*
|
|
56764
|
-
*
|
|
56765
|
-
* reaches the `--anchor-width`/`--anchor-height` CSS vars and becomes a
|
|
56766
|
-
* min-width/min-height floor.
|
|
56767
|
-
*
|
|
56966
|
+
* inert here unless something asks for it: a dialog is a surface of its own,
|
|
56967
|
+
* sized by its content, not a panel grown out of the control that opened it —
|
|
56968
|
+
* that is Popover's job. Two props ask. With `sizeFromAnchor`, the anchor's
|
|
56969
|
+
* box reaches the `--anchor-width`/`--anchor-height` CSS vars and becomes a
|
|
56970
|
+
* min-width/min-height floor. With `animation="growing"`, the anchor's box is
|
|
56971
|
+
* where the dialog comes from: the two are photographed and morphed into each
|
|
56972
|
+
* other, and back on close (popup_grow.js). Either way Dialog's own
|
|
56973
|
+
* positioning is never relative to the anchor, unlike Popover.
|
|
56768
56974
|
*
|
|
56769
56975
|
* Two rendering strategies, picked via `layer`: `DialogAsModal` (a real
|
|
56770
56976
|
* `<dialog>`, `showModal()`, top layer — native focus trap,
|
|
@@ -57209,6 +57415,27 @@ const css$E = /* css */`
|
|
|
57209
57415
|
}
|
|
57210
57416
|
}
|
|
57211
57417
|
|
|
57418
|
+
/* While a dialog is growing out of the element that opened it
|
|
57419
|
+
(popup_grow.js). The page around is deliberately NOT taken as a picture,
|
|
57420
|
+
against the browser's own default: a captured element is not painted where
|
|
57421
|
+
it stands and cannot be pointed at either, so photographing the document
|
|
57422
|
+
would leave the whole page frozen and unpressable for the length of every
|
|
57423
|
+
opening — and of every closing, which is the moment the user is coming
|
|
57424
|
+
back to it. Nothing shows through where the movement is: the two boxes it
|
|
57425
|
+
plays between are captured, and their pictures cover the rectangle between
|
|
57426
|
+
them at every moment. */
|
|
57427
|
+
:root[data-navi-popup-grow] {
|
|
57428
|
+
view-transition-name: none;
|
|
57429
|
+
|
|
57430
|
+
/* The popup's own pace, published on the root by popup_grow.js because the
|
|
57431
|
+
::view-transition tree hangs off it and inherits from nowhere else. */
|
|
57432
|
+
&::view-transition-group(navi-popup-grow),
|
|
57433
|
+
&::view-transition-old(navi-popup-grow),
|
|
57434
|
+
&::view-transition-new(navi-popup-grow) {
|
|
57435
|
+
animation-duration: var(--navi-popup-grow-duration, 0.25s);
|
|
57436
|
+
}
|
|
57437
|
+
}
|
|
57438
|
+
|
|
57212
57439
|
${surfaceTextCss}
|
|
57213
57440
|
${popupCss}
|
|
57214
57441
|
`;
|
|
@@ -57302,16 +57529,25 @@ const css$E = /* css */`
|
|
|
57302
57529
|
* scroll while open (its backdrop only covers the scrollport, so scrolling
|
|
57303
57530
|
* there would reveal uncovered content); this prop extends the lock to the
|
|
57304
57531
|
* whole page. Defaults to `true` for a dialog docked by `dockedOnSmallTouchScreen`.
|
|
57305
|
-
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`} [props.animation]
|
|
57532
|
+
* @param {boolean|"auto"|"fading"|"scaling"|"sliding"|"growing"|`slide-from-${string}`} [props.animation]
|
|
57306
57533
|
* - `true`/`"auto"` resolves to `"scaling"` for a centered `positionArea`,
|
|
57307
57534
|
* or a concrete `"slide-from-*"` direction otherwise. Any other explicit
|
|
57308
|
-
* value is used as-is.
|
|
57535
|
+
* value is used as-is. `"growing"` is the odd one out: every other kind
|
|
57536
|
+
* moves the dialog on its own box (it scales from its own centre, slides in
|
|
57537
|
+
* from its own edge), while this one is the browser morphing the ANCHOR's
|
|
57538
|
+
* box into the dialog's, and the dialog's back into the anchor's on close —
|
|
57539
|
+
* for a dialog that is what the anchor became rather than a surface shown
|
|
57540
|
+
* over it. It needs an anchor (whatever opened the dialog, or the `anchor`
|
|
57541
|
+
* prop) and grows into whatever inside the dialog carries `data-grow`, the
|
|
57542
|
+
* dialog itself when nothing does. `"auto"` never picks it: only the caller
|
|
57543
|
+
* knows the two boxes are one object. See `popup_grow.js`.
|
|
57309
57544
|
* @param {string} [props.animationDuration] - Maps to
|
|
57310
57545
|
* `--popup-animation-duration`.
|
|
57311
57546
|
* @param {Element|{current: Element}|string} [props.anchor] - Never used for
|
|
57312
57547
|
* positioning (see this file's top comment), and ignored entirely unless
|
|
57313
|
-
* `sizeFromAnchor`
|
|
57314
|
-
* `--anchor-width`/`--anchor-height` CSS vars
|
|
57548
|
+
* `sizeFromAnchor` or `animation="growing"` asks for it — the first sizes
|
|
57549
|
+
* the dialog via the `--anchor-width`/`--anchor-height` CSS vars, the second
|
|
57550
|
+
* makes the dialog come out of the anchor's own box. Defaults to whatever
|
|
57315
57551
|
* triggered the open (`e.detail.anchor`), if any. A string is resolved via
|
|
57316
57552
|
* `document.getElementById` when the dialog opens — see popover.jsx's own
|
|
57317
57553
|
* `anchor` doc for why (mainly `defaultOpen`).
|
|
@@ -57708,6 +57944,8 @@ const useDialogProps = props => {
|
|
|
57708
57944
|
marginWithContainer
|
|
57709
57945
|
};
|
|
57710
57946
|
const repositionRef = useRef(null);
|
|
57947
|
+
// The element this dialog came out of, held from the opening to the closing.
|
|
57948
|
+
const anchorElementRef = useRef(null);
|
|
57711
57949
|
useEffect(() => {
|
|
57712
57950
|
repositionRef.current?.(new CustomEvent("position_props_change", {
|
|
57713
57951
|
detail: {}
|
|
@@ -57766,20 +58004,84 @@ const useDialogProps = props => {
|
|
|
57766
58004
|
flushEdges.right = expandX || x === "right" || x === "inset-right";
|
|
57767
58005
|
}
|
|
57768
58006
|
const isAutoAnimation = animation === true || animation === "auto";
|
|
57769
|
-
//
|
|
57770
|
-
//
|
|
57771
|
-
//
|
|
58007
|
+
// The dialog and the anchor are one box, and what plays between them is the
|
|
58008
|
+
// browser's own morph (popup_grow.js) — nothing this dialog does to its own
|
|
58009
|
+
// box. So it arms no CSS transition of its own, which is not merely useless
|
|
58010
|
+
// here but wrong: the shared [navi-animation] rule transitions `display`
|
|
58011
|
+
// with allow-discrete, and a dialog kept rendered for the length of its exit
|
|
58012
|
+
// is exactly what the picture taken of the state it closes into must not
|
|
58013
|
+
// show.
|
|
58014
|
+
const growing = animation === "growing";
|
|
58015
|
+
// Dialog never has a real anchor to POSITION against (see this file's top
|
|
58016
|
+
// comment), so this is always the "no anchor" path — the same one Popover's
|
|
58017
|
+
// own custom renderer falls into when it has no real anchor either.
|
|
57772
58018
|
const resolvedAnimationKind = isAutoAnimation ? resolveAutoAnimationKind(undefined, parsedPositionArea) : animation;
|
|
57773
58019
|
// Not gated on isAutoAnimation — an explicit animation="sliding" needs a
|
|
57774
58020
|
// concrete direction just as much as an auto-resolved one does (same as
|
|
57775
58021
|
// Popover's own "sliding"/"expanding" resolution step in openEffect).
|
|
57776
|
-
let resolvedAnimation = resolvedAnimationKind;
|
|
58022
|
+
let resolvedAnimation = growing ? undefined : resolvedAnimationKind;
|
|
57777
58023
|
if (resolvedAnimationKind === "sliding") {
|
|
57778
58024
|
resolvedAnimation = resolveDirectionValue(parsedPositionArea.y, parsedPositionArea.x, {
|
|
57779
58025
|
prefix: "slide-from"
|
|
57780
58026
|
}) ?? "slide-from-top";
|
|
57781
58027
|
}
|
|
57782
58028
|
|
|
58029
|
+
// What this dialog was opened out of, resolved at open time rather than at
|
|
58030
|
+
// render time — mainly for defaultOpen, where there's no triggering
|
|
58031
|
+
// event/ref yet to read a real element from, only an id known up front. See
|
|
58032
|
+
// popover.jsx's own anchor handling for the full reasoning, mirrored here
|
|
58033
|
+
// identically.
|
|
58034
|
+
const resolveAnchorElement = e => {
|
|
58035
|
+
if (typeof anchor === "string") {
|
|
58036
|
+
const anchorElementById = document.getElementById(anchor);
|
|
58037
|
+
if (!anchorElementById) {
|
|
58038
|
+
console.warn(`Dialog: anchor="${anchor}" did not match any element`);
|
|
58039
|
+
}
|
|
58040
|
+
return anchorElementById;
|
|
58041
|
+
}
|
|
58042
|
+
if (anchor) {
|
|
58043
|
+
// anchor prop is a ref or a DOM element — always a real anchor,
|
|
58044
|
+
// regardless of anchorCustomEventDetail. A ref is unwrapped even when it
|
|
58045
|
+
// holds nothing: falling back to the ref object itself would pass the
|
|
58046
|
+
// "is there an anchor?" tests downstream with something that has no box.
|
|
58047
|
+
return "current" in anchor ? anchor.current : anchor;
|
|
58048
|
+
}
|
|
58049
|
+
if (anchorCustomEventDetail === "override") {
|
|
58050
|
+
// e.g. the button that triggered a --navi-toggle/--navi-open command,
|
|
58051
|
+
// already resolved from detail.anchor/detail.source by the caller
|
|
58052
|
+
// (see UncontrolledDialog's onnavi_request_open).
|
|
58053
|
+
return e.detail.anchor;
|
|
58054
|
+
}
|
|
58055
|
+
return undefined;
|
|
58056
|
+
};
|
|
58057
|
+
|
|
58058
|
+
// The dialog and the anchor are the same box at two sizes, so the opening
|
|
58059
|
+
// and the closing are one becoming the other. The browser draws that itself
|
|
58060
|
+
// provided the change happens between its two pictures, which is what
|
|
58061
|
+
// handing it to the controller buys (see popup_grow.js and
|
|
58062
|
+
// open_controller.js's own transitionChange).
|
|
58063
|
+
openController.transitionChange = growing ? (applyChange, {
|
|
58064
|
+
opened,
|
|
58065
|
+
event
|
|
58066
|
+
}) => {
|
|
58067
|
+
const dialogEl = ref.current;
|
|
58068
|
+
// A mount-time opening was never seen closed (see openEffect's own
|
|
58069
|
+
// `silent`): there is no box it comes from, because nothing was shown
|
|
58070
|
+
// before it.
|
|
58071
|
+
if (!dialogEl || opened && event.detail.silent) {
|
|
58072
|
+
applyChange();
|
|
58073
|
+
return;
|
|
58074
|
+
}
|
|
58075
|
+
const anchorElement = opened ? resolveAnchorElement(event) : anchorElementRef.current;
|
|
58076
|
+
if (!anchorElement) {
|
|
58077
|
+
applyChange();
|
|
58078
|
+
return;
|
|
58079
|
+
}
|
|
58080
|
+
growPopupFromAnchor(dialogEl, anchorElement, applyChange, {
|
|
58081
|
+
opened
|
|
58082
|
+
});
|
|
58083
|
+
} : null;
|
|
58084
|
+
|
|
57783
58085
|
// Sync the DOM open and return how to sync it back closed, fresh on every
|
|
57784
58086
|
// render so it closes over the latest props (scrollLock, etc.). The
|
|
57785
58087
|
// controller (owned by the caller, or by UncontrolledDialog) decides
|
|
@@ -57806,28 +58108,10 @@ const useDialogProps = props => {
|
|
|
57806
58108
|
// real, meaningful ancestor beyond it.
|
|
57807
58109
|
const positionedAncestor = isModal ? document.documentElement : getPositionedParent(dialogEl.parentElement /* dialogEl is inside the clip_wrapper */);
|
|
57808
58110
|
const [cleanup, addCleanup] = createPubSub(true);
|
|
57809
|
-
|
|
57810
|
-
|
|
57811
|
-
|
|
57812
|
-
|
|
57813
|
-
// real element from, only an id known up front. See popover.jsx's own
|
|
57814
|
-
// anchor handling for the full reasoning, mirrored here identically.
|
|
57815
|
-
anchorElement = document.getElementById(anchor);
|
|
57816
|
-
if (!anchorElement) {
|
|
57817
|
-
console.warn(`Dialog: anchor="${anchor}" did not match any element`);
|
|
57818
|
-
}
|
|
57819
|
-
} else if (anchor) {
|
|
57820
|
-
// anchor prop is a ref or a DOM element — always a real anchor,
|
|
57821
|
-
// regardless of anchorCustomEventDetail. A ref is unwrapped even when it
|
|
57822
|
-
// holds nothing: falling back to the ref object itself would pass the
|
|
57823
|
-
// "is there an anchor?" test below with something that has no box.
|
|
57824
|
-
anchorElement = "current" in anchor ? anchor.current : anchor;
|
|
57825
|
-
} else if (anchorCustomEventDetail === "override") {
|
|
57826
|
-
// e.g. the button that triggered a --navi-toggle/--navi-open command,
|
|
57827
|
-
// already resolved from detail.anchor/detail.source by the caller
|
|
57828
|
-
// (see UncontrolledDialog's onnavi_request_open).
|
|
57829
|
-
anchorElement = e.detail.anchor;
|
|
57830
|
-
}
|
|
58111
|
+
const anchorElement = resolveAnchorElement(e);
|
|
58112
|
+
// Kept for the closing, which has no opening event to read it back from:
|
|
58113
|
+
// the box the dialog shrinks back into is the one it came out of.
|
|
58114
|
+
anchorElementRef.current = anchorElement;
|
|
57831
58115
|
debugPopup(`"${e.type}" on ${getElementSignature(e.target)} -> openDialog`);
|
|
57832
58116
|
if (!isModal) {
|
|
57833
58117
|
// see openLocalDialogCount's own comment
|