@jsenv/dom 0.17.1 → 0.17.2
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/jsenv_dom.js +400 -71
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -146,6 +146,14 @@ const getElementSignature = (element) => {
|
|
|
146
146
|
return `${tagName}#${elementId}`;
|
|
147
147
|
}
|
|
148
148
|
if (tagName === "button") {
|
|
149
|
+
// The label BEFORE the text: an icon button has no text worth reading
|
|
150
|
+
// (an svg, a zero-width space), and its aria-label is the one thing that
|
|
151
|
+
// says which button it is — which is the whole point of a signature in a
|
|
152
|
+
// log. A labelled button says so even when it also has text.
|
|
153
|
+
const label = element.getAttribute("aria-label");
|
|
154
|
+
if (label) {
|
|
155
|
+
return `button[aria-label="${label}"]`;
|
|
156
|
+
}
|
|
149
157
|
const text = element.textContent.trim();
|
|
150
158
|
if (text) {
|
|
151
159
|
const excerpt = text.length > 10 ? `${text.slice(0, 10)}…` : text;
|
|
@@ -3904,6 +3912,14 @@ const getLuminance = (r, g, b) => {
|
|
|
3904
3912
|
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
3905
3913
|
};
|
|
3906
3914
|
|
|
3915
|
+
/**
|
|
3916
|
+
* First ancestor of `node` matching `predicate`, walking parent by parent.
|
|
3917
|
+
* Starts at the parent — `node` itself is never a candidate.
|
|
3918
|
+
*
|
|
3919
|
+
* @param {Node} node
|
|
3920
|
+
* @param {(ancestor: Node) => boolean} predicate
|
|
3921
|
+
* @returns {Node|null}
|
|
3922
|
+
*/
|
|
3907
3923
|
const findAncestor = (node, predicate) => {
|
|
3908
3924
|
let ancestor = node.parentNode;
|
|
3909
3925
|
while (ancestor) {
|
|
@@ -3915,6 +3931,21 @@ const findAncestor = (node, predicate) => {
|
|
|
3915
3931
|
return null;
|
|
3916
3932
|
};
|
|
3917
3933
|
|
|
3934
|
+
/**
|
|
3935
|
+
* First descendant of `rootNode` matching `fn`, in document order (depth
|
|
3936
|
+
* first). The walk is bounded to the subtree: `rootNode` itself is not a
|
|
3937
|
+
* candidate, and a root with no children yields nothing — never the root's
|
|
3938
|
+
* siblings.
|
|
3939
|
+
*
|
|
3940
|
+
* @param {Node} rootNode
|
|
3941
|
+
* @param {(node: Node, skip: () => void) => boolean} fn - Return true to stop
|
|
3942
|
+
* on `node`. Call `skip()` to not descend into `node`'s children (the walk
|
|
3943
|
+
* goes on with its siblings).
|
|
3944
|
+
* @param {object} [options]
|
|
3945
|
+
* @param {Node} [options.skipRoot] - A subtree to leave out entirely, itself
|
|
3946
|
+
* included.
|
|
3947
|
+
* @returns {Node|null}
|
|
3948
|
+
*/
|
|
3918
3949
|
const findDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
3919
3950
|
const iterator = createNextNodeIterator(rootNode, rootNode, skipRoot);
|
|
3920
3951
|
let { done, value: node } = iterator.next();
|
|
@@ -3935,6 +3966,18 @@ const findDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
|
3935
3966
|
return null;
|
|
3936
3967
|
};
|
|
3937
3968
|
|
|
3969
|
+
/**
|
|
3970
|
+
* Last descendant of `rootNode` matching `fn` in document order — the walk
|
|
3971
|
+
* starts at the subtree's deepest final node and moves backwards, so the
|
|
3972
|
+
* first match it meets is the last one the document holds.
|
|
3973
|
+
*
|
|
3974
|
+
* @param {Node} rootNode
|
|
3975
|
+
* @param {(node: Node) => boolean} fn
|
|
3976
|
+
* @param {object} [options]
|
|
3977
|
+
* @param {Node} [options.skipRoot] - A subtree to leave out entirely, itself
|
|
3978
|
+
* included.
|
|
3979
|
+
* @returns {Node|null}
|
|
3980
|
+
*/
|
|
3938
3981
|
const findLastDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
3939
3982
|
const deepestNode = getDeepestNode(rootNode, skipRoot);
|
|
3940
3983
|
if (deepestNode) {
|
|
@@ -3954,6 +3997,23 @@ const findLastDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
|
3954
3997
|
return null;
|
|
3955
3998
|
};
|
|
3956
3999
|
|
|
4000
|
+
/**
|
|
4001
|
+
* First node after `from` in document order matching `predicate`. Unlike
|
|
4002
|
+
* findDescendant this is anchored to a position, not a container: the walk
|
|
4003
|
+
* leaves `from`'s subtree and goes on through its siblings and its ancestors'
|
|
4004
|
+
* siblings, until `root`'s subtree is exhausted.
|
|
4005
|
+
*
|
|
4006
|
+
* @param {Node} from - The position to search from; not a candidate itself.
|
|
4007
|
+
* @param {(node: Node) => boolean} predicate
|
|
4008
|
+
* @param {object} [options]
|
|
4009
|
+
* @param {Node} [options.root] - Bounds the walk to its subtree; null walks to
|
|
4010
|
+
* the end of the tree `from` belongs to.
|
|
4011
|
+
* @param {Node} [options.skipRoot] - A subtree to leave out entirely, itself
|
|
4012
|
+
* included. A `from` inside it starts right after it.
|
|
4013
|
+
* @param {boolean} [options.skipChildren] - Do not look inside `from`; start
|
|
4014
|
+
* at what follows it.
|
|
4015
|
+
* @returns {Node|null}
|
|
4016
|
+
*/
|
|
3957
4017
|
const findAfter = (
|
|
3958
4018
|
from,
|
|
3959
4019
|
predicate,
|
|
@@ -3970,6 +4030,21 @@ const findAfter = (
|
|
|
3970
4030
|
return null;
|
|
3971
4031
|
};
|
|
3972
4032
|
|
|
4033
|
+
/**
|
|
4034
|
+
* First node before `from` in reverse document order matching `predicate` —
|
|
4035
|
+
* what findAfter is to "next", this is to "previous". A step back lands on
|
|
4036
|
+
* the previous sibling's DEEPEST last node (document order walked backwards),
|
|
4037
|
+
* not on the sibling itself.
|
|
4038
|
+
*
|
|
4039
|
+
* @param {Node} from - The position to search from; not a candidate itself.
|
|
4040
|
+
* @param {(node: Node) => boolean} predicate
|
|
4041
|
+
* @param {object} [options]
|
|
4042
|
+
* @param {Node} [options.root] - Bounds the walk to its subtree; null walks
|
|
4043
|
+
* back to the start of the tree `from` belongs to.
|
|
4044
|
+
* @param {Node} [options.skipRoot] - A subtree to leave out entirely, itself
|
|
4045
|
+
* included. A `from` inside it starts right before it.
|
|
4046
|
+
* @returns {Node|null}
|
|
4047
|
+
*/
|
|
3973
4048
|
const findBefore = (
|
|
3974
4049
|
from,
|
|
3975
4050
|
predicate,
|
|
@@ -4002,6 +4077,15 @@ const getNextNode = (node, rootNode, skipChild = false, skipRoot = null) => {
|
|
|
4002
4077
|
}
|
|
4003
4078
|
}
|
|
4004
4079
|
|
|
4080
|
+
// The traversal is bounded to rootNode's subtree: the root's own siblings
|
|
4081
|
+
// are not part of it. Without this, a rootNode with no children (asking
|
|
4082
|
+
// findDescendant about an <input>, say) steps to its next sibling and walks
|
|
4083
|
+
// the rest of the document from there — the parentNode guard below never
|
|
4084
|
+
// catches it because the walk is already outside the root.
|
|
4085
|
+
if (node === rootNode) {
|
|
4086
|
+
return null;
|
|
4087
|
+
}
|
|
4088
|
+
|
|
4005
4089
|
const nextSibling = node.nextSibling;
|
|
4006
4090
|
if (nextSibling) {
|
|
4007
4091
|
// If next sibling is skipRoot, skip it entirely
|
|
@@ -4410,8 +4494,13 @@ const canInteract = (element) => {
|
|
|
4410
4494
|
if (element.disabled) {
|
|
4411
4495
|
return false;
|
|
4412
4496
|
}
|
|
4413
|
-
|
|
4414
|
-
|
|
4497
|
+
// closest, not hasAttribute: inert is inherited by the whole subtree — the
|
|
4498
|
+
// element itself may carry nothing and still be untouchable because something
|
|
4499
|
+
// above it is inert (a slide waiting off screen, the page behind a modal).
|
|
4500
|
+
// Focusing one of those does nothing at all, silently: the browser refuses and
|
|
4501
|
+
// the focus stays where it was, which reads as "the popup opened on nothing".
|
|
4502
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/inert
|
|
4503
|
+
if (element.closest("[inert]")) {
|
|
4415
4504
|
return false;
|
|
4416
4505
|
}
|
|
4417
4506
|
return true;
|
|
@@ -4509,7 +4598,12 @@ const findFocusable = (element, { exclude } = {}) => {
|
|
|
4509
4598
|
* - `"value_change"` — key increments/decrements the field value (range, number, date…)
|
|
4510
4599
|
* - `"cursor_move"` — key moves the text cursor within the field
|
|
4511
4600
|
* - `"type"` — key produces or deletes text content
|
|
4512
|
-
* - `"scroll"` — key
|
|
4601
|
+
* - `"scroll"` — key would scroll the page: nothing on the element itself
|
|
4602
|
+
* claims it, so it is safe to intercept
|
|
4603
|
+
* - `"scroll_self"` — the focused element scrolls ITSELF that way (it really
|
|
4604
|
+
* overflows on that axis): the key is spoken for, and
|
|
4605
|
+
* taking it would leave a scrollable region no way to be
|
|
4606
|
+
* scrolled from the keyboard
|
|
4513
4607
|
* - `""` — no meaningful browser default; safe to intercept freely
|
|
4514
4608
|
*/
|
|
4515
4609
|
const normalizeKeyboardKey = (rawKey) => {
|
|
@@ -4583,6 +4677,23 @@ const isTypingIntent = (e) => {
|
|
|
4583
4677
|
return false;
|
|
4584
4678
|
};
|
|
4585
4679
|
|
|
4680
|
+
// Whether this element is what scrolls on that axis: it says it may (overflow)
|
|
4681
|
+
// and it has somewhere to go (it overflows). Both are needed — an "auto" box
|
|
4682
|
+
// whose content fits scrolls nothing, and the keys are then free.
|
|
4683
|
+
const canScrollSelf = (element, axis) => {
|
|
4684
|
+
if (!element || element.nodeType !== 1) {
|
|
4685
|
+
return false;
|
|
4686
|
+
}
|
|
4687
|
+
const style = getComputedStyle(element);
|
|
4688
|
+
const overflow = axis === "x" ? style.overflowX : style.overflowY;
|
|
4689
|
+
if (overflow !== "auto" && overflow !== "scroll") {
|
|
4690
|
+
return false;
|
|
4691
|
+
}
|
|
4692
|
+
return axis === "x"
|
|
4693
|
+
? element.scrollWidth > element.clientWidth
|
|
4694
|
+
: element.scrollHeight > element.clientHeight;
|
|
4695
|
+
};
|
|
4696
|
+
|
|
4586
4697
|
const DEFAULT_BEHAVIORS = [
|
|
4587
4698
|
{
|
|
4588
4699
|
test: () => true,
|
|
@@ -4753,6 +4864,31 @@ const DEFAULT_BEHAVIORS = [
|
|
|
4753
4864
|
escape: "dismiss",
|
|
4754
4865
|
},
|
|
4755
4866
|
},
|
|
4867
|
+
{
|
|
4868
|
+
// An element that really scrolls — a slide's own body, a scrollable panel:
|
|
4869
|
+
// the browser gives it the arrows (and Home/End/PageUp/PageDown) so it can
|
|
4870
|
+
// be read from the keyboard, and that is not a key to take. Asked per axis
|
|
4871
|
+
// and per element, not from a class or an attribute: what makes it true is
|
|
4872
|
+
// that it overflows right now.
|
|
4873
|
+
test: (el) => canScrollSelf(el, "y") || canScrollSelf(el, "x"),
|
|
4874
|
+
keys: {
|
|
4875
|
+
arrowup: (e) =>
|
|
4876
|
+
canScrollSelf(e.target, "y") ? "scroll_self" : undefined,
|
|
4877
|
+
arrowdown: (e) =>
|
|
4878
|
+
canScrollSelf(e.target, "y") ? "scroll_self" : undefined,
|
|
4879
|
+
arrowleft: (e) =>
|
|
4880
|
+
canScrollSelf(e.target, "x") ? "scroll_self" : undefined,
|
|
4881
|
+
arrowright: (e) =>
|
|
4882
|
+
canScrollSelf(e.target, "x") ? "scroll_self" : undefined,
|
|
4883
|
+
pageup: (e) => (canScrollSelf(e.target, "y") ? "scroll_self" : undefined),
|
|
4884
|
+
pagedown: (e) =>
|
|
4885
|
+
canScrollSelf(e.target, "y") ? "scroll_self" : undefined,
|
|
4886
|
+
home: (e) => (canScrollSelf(e.target, "y") ? "scroll_self" : undefined),
|
|
4887
|
+
end: (e) => (canScrollSelf(e.target, "y") ? "scroll_self" : undefined),
|
|
4888
|
+
space: (e) => (canScrollSelf(e.target, "y") ? "scroll_self" : undefined),
|
|
4889
|
+
},
|
|
4890
|
+
// no fallback: only these keys are claimed, everything else keeps looking
|
|
4891
|
+
},
|
|
4756
4892
|
{
|
|
4757
4893
|
// Non-interactive elements: browser scrolls on Space and arrow keys
|
|
4758
4894
|
test: () => true,
|
|
@@ -6033,9 +6169,15 @@ const trapFocusInside = (
|
|
|
6033
6169
|
// A backdrop click is detected when the target is a <dialog> element —
|
|
6034
6170
|
// the ::backdrop pseudo-element is not in the DOM, so the event target
|
|
6035
6171
|
// becomes the dialog element itself when its content area is not hit.
|
|
6172
|
+
// Read through getAttribute rather than .className: on an SVG element
|
|
6173
|
+
// className is an SVGAnimatedString, not a string, and asking it for
|
|
6174
|
+
// .includes throws — which is how clicking an icon inside the trap
|
|
6175
|
+
// used to break. Still a substring test, because the real class names
|
|
6176
|
+
// are navi_dialog_backdrop / navi_popover_backdrop / ….
|
|
6177
|
+
const targetClass = event.target.getAttribute?.("class") || "";
|
|
6036
6178
|
const isBackdropClick =
|
|
6037
6179
|
event.target.tagName === "DIALOG" ||
|
|
6038
|
-
|
|
6180
|
+
targetClass.includes("backdrop");
|
|
6039
6181
|
if (!isBackdropClick) {
|
|
6040
6182
|
event.stopImmediatePropagation();
|
|
6041
6183
|
}
|
|
@@ -7229,9 +7371,14 @@ const getPaddingSizes = (element) => {
|
|
|
7229
7371
|
*
|
|
7230
7372
|
* @param {HTMLElement} element - The overlay element being shown. Its preceding
|
|
7231
7373
|
* siblings and all ancestor scroll containers will be scroll-locked.
|
|
7374
|
+
* @param {Object} [options]
|
|
7375
|
+
* @param {HTMLElement} [options.boundaryElement] - Only lock scroll containers
|
|
7376
|
+
* inside this element (itself included). For an overlay confined to a local
|
|
7377
|
+
* container rather than the viewport: the container's own scroll must stop,
|
|
7378
|
+
* the rest of the page keeps scrolling as usual.
|
|
7232
7379
|
* @returns {() => void} Cleanup function that restores all modified styles.
|
|
7233
7380
|
*/
|
|
7234
|
-
const trapScrollInside = (element) => {
|
|
7381
|
+
const trapScrollInside = (element, { boundaryElement } = {}) => {
|
|
7235
7382
|
const cleanupCallbackSet = new Set();
|
|
7236
7383
|
|
|
7237
7384
|
// Collect every element to lock first (preceding scrollable siblings + all
|
|
@@ -7245,7 +7392,11 @@ const trapScrollInside = (element) => {
|
|
|
7245
7392
|
previous = previous.previousSibling;
|
|
7246
7393
|
}
|
|
7247
7394
|
for (const selfOrAncestorScroll of getSelfAndAncestorScrolls(element)) {
|
|
7248
|
-
|
|
7395
|
+
const { scrollContainer } = selfOrAncestorScroll;
|
|
7396
|
+
if (boundaryElement && !boundaryElement.contains(scrollContainer)) {
|
|
7397
|
+
continue;
|
|
7398
|
+
}
|
|
7399
|
+
elementsToLock.push(scrollContainer);
|
|
7249
7400
|
}
|
|
7250
7401
|
|
|
7251
7402
|
// Phase 1 — MEASURE. Batch every layout/style read (scrollTop, scrollbar
|
|
@@ -10635,47 +10786,127 @@ const moveCSSVars = (vars, fromEl, toEl) => {
|
|
|
10635
10786
|
};
|
|
10636
10787
|
|
|
10637
10788
|
installImportMetaCssBuild(import.meta);const css$1 = /* css */`
|
|
10789
|
+
/* IN THE PAGE, NOT IN THE LIST: the hint lands on the edge of a row, which
|
|
10790
|
+
for the last one is the very bottom of the scroll area — drawn inside it,
|
|
10791
|
+
the line would push the scrollable area a few pixels further and make a
|
|
10792
|
+
scrollbar appear (or hide the hint under it) exactly when one is trying to
|
|
10793
|
+
drop at the end. Placed in the body and positioned in viewport
|
|
10794
|
+
coordinates, it can sit anywhere, overhang the list, and cost nothing to
|
|
10795
|
+
the layout. Fixed, like the clone it accompanies. */
|
|
10638
10796
|
.navi_drop_hint {
|
|
10639
|
-
|
|
10797
|
+
/* A popover, so it lands in the top layer: no z-index to bid against the
|
|
10798
|
+
page, and nothing it can be hidden behind. Shown BEFORE the clone, which
|
|
10799
|
+
is what puts the clone above it — the top layer stacks in the order
|
|
10800
|
+
things are shown, and the item being carried should pass over the line
|
|
10801
|
+
rather than under it. The UA styles for [popover] have to be undone:
|
|
10802
|
+
inset:0, margin:auto, a border and a background of its own. */
|
|
10803
|
+
position: fixed;
|
|
10804
|
+
inset: auto;
|
|
10640
10805
|
top: var(--drop-hint-y);
|
|
10641
10806
|
left: calc(var(--drop-target-left) + var(--drop-hint-margin-x, 0px));
|
|
10642
|
-
z-index: 10;
|
|
10643
10807
|
display: none;
|
|
10808
|
+
box-sizing: border-box;
|
|
10644
10809
|
width: calc(var(--drop-target-width) - 2 * var(--drop-hint-margin-x, 0px));
|
|
10645
10810
|
height: var(--drop-hint-size, 3px);
|
|
10811
|
+
margin: 0;
|
|
10812
|
+
padding: 0;
|
|
10813
|
+
color: inherit;
|
|
10646
10814
|
background: var(--drop-hint-background-color, #4476ff);
|
|
10815
|
+
border: none;
|
|
10647
10816
|
border-radius: var(--drop-hint-border-radius, 2px);
|
|
10648
10817
|
transform: translateY(-50%);
|
|
10649
10818
|
pointer-events: none;
|
|
10819
|
+
overflow: visible;
|
|
10650
10820
|
}
|
|
10651
|
-
[data-drop-edge
|
|
10821
|
+
.navi_drop_hint[data-drop-edge]:popover-open {
|
|
10652
10822
|
display: block;
|
|
10823
|
+
}
|
|
10824
|
+
.navi_drop_hint[data-drop-edge="top"] {
|
|
10653
10825
|
--drop-hint-y: calc(
|
|
10654
10826
|
var(--drop-target-top) - var(--drop-hint-margin-y, 0px)
|
|
10655
10827
|
);
|
|
10656
10828
|
}
|
|
10657
|
-
[data-drop-edge="bottom"]
|
|
10658
|
-
display: block;
|
|
10829
|
+
.navi_drop_hint[data-drop-edge="bottom"] {
|
|
10659
10830
|
--drop-hint-y: calc(
|
|
10660
10831
|
var(--drop-target-bottom) + var(--drop-hint-margin-y, 0px)
|
|
10661
10832
|
);
|
|
10662
10833
|
}
|
|
10834
|
+
/* A chevron at each end, pointing in: the line alone is easy to lose against
|
|
10835
|
+
a list of borders and separators, two arrows read as "here" at a glance
|
|
10836
|
+
(same idea as the table's column drop preview). They overhang the line,
|
|
10837
|
+
which costs nothing now that the hint is out of the scrollable area — and
|
|
10838
|
+
the more they stick out, the easier they are to spot. */
|
|
10839
|
+
.navi_drop_hint_cap {
|
|
10840
|
+
position: absolute;
|
|
10841
|
+
top: 50%;
|
|
10842
|
+
display: flex;
|
|
10843
|
+
color: var(--drop-hint-background-color, #4476ff);
|
|
10844
|
+
translate: 0 -50%;
|
|
10845
|
+
}
|
|
10846
|
+
.navi_drop_hint_cap svg {
|
|
10847
|
+
width: var(--drop-hint-arrow-size, 11px);
|
|
10848
|
+
height: var(--drop-hint-arrow-size, 11px);
|
|
10849
|
+
}
|
|
10850
|
+
.navi_drop_hint_cap[data-side="start"] {
|
|
10851
|
+
left: calc(-1 * var(--drop-hint-arrow-size, 11px));
|
|
10852
|
+
rotate: -90deg;
|
|
10853
|
+
}
|
|
10854
|
+
.navi_drop_hint_cap[data-side="end"] {
|
|
10855
|
+
right: calc(-1 * var(--drop-hint-arrow-size, 11px));
|
|
10856
|
+
rotate: 90deg;
|
|
10857
|
+
}
|
|
10858
|
+
|
|
10859
|
+
/* WHO CAN START A DRAG, said in the cursor.
|
|
10860
|
+
A handle drags on the spot, so it shows the hand. A source only drags once
|
|
10861
|
+
the pointer has travelled a few pixels — a plain click stays a click — but
|
|
10862
|
+
the text inside it can no longer be selected (the gesture takes the
|
|
10863
|
+
pointer), so an I-beam over it would promise something that does not
|
|
10864
|
+
happen: it reads as a plain surface instead. An opted-out area keeps both
|
|
10865
|
+
its cursor and its selection, and never starts a drag (see the check in
|
|
10866
|
+
startDragToReorder).
|
|
10867
|
+
Controls inside a source keep their own cursor: cursor is inherited, and
|
|
10868
|
+
anything setting its own (a button's pointer) wins on itself.
|
|
10869
|
+
Only the resting cursor is set here: what it becomes once a drag is under
|
|
10870
|
+
way belongs to the gesture (see the backdrop in drag_gesture.js), the only
|
|
10871
|
+
thing that knows a drag actually started. */
|
|
10872
|
+
[data-drag-handle] {
|
|
10873
|
+
cursor: grab;
|
|
10874
|
+
}
|
|
10875
|
+
[data-drag-source] {
|
|
10876
|
+
cursor: default;
|
|
10877
|
+
user-select: none;
|
|
10878
|
+
}
|
|
10879
|
+
[data-drag-ignore] {
|
|
10880
|
+
cursor: auto;
|
|
10881
|
+
user-select: auto;
|
|
10882
|
+
}
|
|
10663
10883
|
|
|
10664
10884
|
[navi-drag-clone-source] {
|
|
10665
10885
|
visibility: hidden;
|
|
10666
10886
|
}
|
|
10667
10887
|
|
|
10668
10888
|
[navi-drag-clone-wrapper] {
|
|
10669
|
-
|
|
10889
|
+
/* Also a popover (see .navi_drop_hint): in the top layer it is over the
|
|
10890
|
+
page whatever the page's own stacking is, and the coordinates it is
|
|
10891
|
+
given are viewport ones — which is what the pointer carrying it works
|
|
10892
|
+
in. Same UA-style reset as the hint. */
|
|
10893
|
+
position: fixed;
|
|
10894
|
+
inset: auto;
|
|
10670
10895
|
top: var(--clone-top);
|
|
10671
10896
|
left: var(--clone-left);
|
|
10672
|
-
|
|
10897
|
+
box-sizing: border-box;
|
|
10673
10898
|
width: var(--clone-width);
|
|
10674
10899
|
height: var(--clone-height);
|
|
10900
|
+
margin: 0;
|
|
10901
|
+
padding: 0;
|
|
10902
|
+
color: inherit;
|
|
10903
|
+
background: transparent;
|
|
10904
|
+
border: none;
|
|
10675
10905
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
|
|
10676
10906
|
opacity: 0.95;
|
|
10677
10907
|
transition: box-shadow 0.15s ease;
|
|
10678
10908
|
pointer-events: none;
|
|
10909
|
+
overflow: visible;
|
|
10679
10910
|
}
|
|
10680
10911
|
|
|
10681
10912
|
[navi-drag-clone] {
|
|
@@ -10694,7 +10925,10 @@ installImportMetaCssBuild(import.meta);const css$1 = /* css */`
|
|
|
10694
10925
|
}
|
|
10695
10926
|
}
|
|
10696
10927
|
`;
|
|
10697
|
-
|
|
10928
|
+
// At module scope, not inside startDragToReorder: the cursor rules above say who
|
|
10929
|
+
// can start a drag, and they have to be true BEFORE anyone drags anything.
|
|
10930
|
+
import.meta.css = [css$1, "@jsenv/dom/src/interaction/drag/drag_to_reorder.js"];
|
|
10931
|
+
const dragCSSVars = ["--drop-hint-size", "--drop-hint-background-color", "--drop-hint-border-radius", "--drop-hint-margin-x", "--drop-hint-margin-y", "--drop-hint-arrow-size", "--drag-clone-scale"];
|
|
10698
10932
|
|
|
10699
10933
|
/**
|
|
10700
10934
|
* Starts a drag-to-reorder interaction on a list item.
|
|
@@ -10760,7 +10994,11 @@ const startDragToReorder = (event, {
|
|
|
10760
10994
|
},
|
|
10761
10995
|
...options
|
|
10762
10996
|
}) => {
|
|
10763
|
-
|
|
10997
|
+
// An area that opted out of dragging (a text one wants to select, a control
|
|
10998
|
+
// that owns the gesture): the press there is none of our business.
|
|
10999
|
+
if (event.target.closest && event.target.closest("[data-drag-ignore]")) {
|
|
11000
|
+
return undefined;
|
|
11001
|
+
}
|
|
10764
11002
|
event.preventDefault();
|
|
10765
11003
|
return dragAfterThreshold(event, () => {
|
|
10766
11004
|
const cloneWrapper = createDragClone(draggedElement, event);
|
|
@@ -10780,21 +11018,23 @@ const startDragToReorder = (event, {
|
|
|
10780
11018
|
// getDropTargetInfo uses gestureInfo.elementImpacted to compute the dragged rect.
|
|
10781
11019
|
// Point it at the clone so drop detection tracks the clone's current position.
|
|
10782
11020
|
dragGesture.gestureInfo.elementImpacted = cloneWrapper;
|
|
10783
|
-
const
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
11021
|
+
const dropHintEl = createDropHint();
|
|
11022
|
+
document.body.appendChild(dropHintEl);
|
|
11023
|
+
// The hint first, the clone second: that order is what stacks them in the
|
|
11024
|
+
// top layer.
|
|
11025
|
+
dropHintEl.showPopover();
|
|
11026
|
+
cloneWrapper.showPopover();
|
|
10787
11027
|
|
|
10788
11028
|
// currentBeforeElement: element before which the grabbed item will be inserted (null = end)
|
|
10789
11029
|
// currentReleaseElement: the actual hovered drop target — used to snap the clone on release
|
|
10790
11030
|
let currentBeforeElement;
|
|
10791
11031
|
let currentReleaseElement;
|
|
10792
11032
|
const clearDropHintDOM = () => {
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
11033
|
+
dropHintEl.removeAttribute("data-drop-edge");
|
|
11034
|
+
dropHintEl.style.removeProperty("--drop-target-top");
|
|
11035
|
+
dropHintEl.style.removeProperty("--drop-target-bottom");
|
|
11036
|
+
dropHintEl.style.removeProperty("--drop-target-left");
|
|
11037
|
+
dropHintEl.style.removeProperty("--drop-target-width");
|
|
10798
11038
|
};
|
|
10799
11039
|
const clearDropHint = () => {
|
|
10800
11040
|
currentBeforeElement = undefined;
|
|
@@ -10845,16 +11085,15 @@ const startDragToReorder = (event, {
|
|
|
10845
11085
|
// beforeElement = X → insert before X (hint at top edge of X)
|
|
10846
11086
|
const anchorEl = beforeElement || items[items.length - 1];
|
|
10847
11087
|
const anchorEdge = beforeElement !== null ? "top" : "bottom";
|
|
10848
|
-
|
|
11088
|
+
// Viewport coordinates, straight from the anchor row: the hint is fixed
|
|
11089
|
+
// in the page (see its CSS), so there is no container box to be relative
|
|
11090
|
+
// to and no scroll offset to add back.
|
|
10849
11091
|
const anchorRect = anchorEl.getBoundingClientRect();
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
scrollContainer.style.setProperty("--drop-target-bottom", `${anchorRect.bottom - containerRect.top + scrollOffsetTop}px`);
|
|
10856
|
-
scrollContainer.style.setProperty("--drop-target-left", `${anchorRect.left - containerRect.left + scrollOffsetLeft}px`);
|
|
10857
|
-
scrollContainer.style.setProperty("--drop-target-width", `${anchorRect.width}px`);
|
|
11092
|
+
dropHintEl.setAttribute("data-drop-edge", anchorEdge);
|
|
11093
|
+
dropHintEl.style.setProperty("--drop-target-top", `${anchorRect.top}px`);
|
|
11094
|
+
dropHintEl.style.setProperty("--drop-target-bottom", `${anchorRect.bottom}px`);
|
|
11095
|
+
dropHintEl.style.setProperty("--drop-target-left", `${anchorRect.left}px`);
|
|
11096
|
+
dropHintEl.style.setProperty("--drop-target-width", `${anchorRect.width}px`);
|
|
10858
11097
|
});
|
|
10859
11098
|
dragGesture.addReleaseCallback(async gestureInfo => {
|
|
10860
11099
|
clearDropHintDOM();
|
|
@@ -10864,7 +11103,7 @@ const startDragToReorder = (event, {
|
|
|
10864
11103
|
const clone = cloneWrapper.firstElementChild;
|
|
10865
11104
|
// Bake the current visual position (transform included) into the CSS vars
|
|
10866
11105
|
// so the clone stays where the user released it when we clear the transform.
|
|
10867
|
-
|
|
11106
|
+
setCloneViewportRect(cloneWrapper, cloneWrapper);
|
|
10868
11107
|
gestureInfo.cancelPosition();
|
|
10869
11108
|
const fromId = getItemId(draggedElement);
|
|
10870
11109
|
const toId = currentBeforeElement ? getItemId(currentBeforeElement) : null;
|
|
@@ -10873,7 +11112,7 @@ const startDragToReorder = (event, {
|
|
|
10873
11112
|
const syncCloneWithDropTarget = () => {
|
|
10874
11113
|
// Snap the CSS-var position to the drop target rect so the browser
|
|
10875
11114
|
// captures the "new" state at the landing position.
|
|
10876
|
-
|
|
11115
|
+
setCloneViewportRect(cloneWrapper, currentReleaseElement);
|
|
10877
11116
|
// Removing this attr drops the CSS scale(1.15), so the browser
|
|
10878
11117
|
// captures the clone at scale 1 as the "new" state.
|
|
10879
11118
|
clone.removeAttribute("navi-drag-clone");
|
|
@@ -10887,15 +11126,13 @@ const startDragToReorder = (event, {
|
|
|
10887
11126
|
});
|
|
10888
11127
|
};
|
|
10889
11128
|
|
|
10890
|
-
// getBoundingClientRect
|
|
10891
|
-
//
|
|
10892
|
-
//
|
|
10893
|
-
const
|
|
11129
|
+
// Viewport coordinates, as getBoundingClientRect gives them: the clone is a
|
|
11130
|
+
// fixed-position popover, so that is the space it lives in — and the one the
|
|
11131
|
+
// pointer dragging it works in too.
|
|
11132
|
+
const setCloneViewportRect = (cloneWrapper, el) => {
|
|
10894
11133
|
const rect = el.getBoundingClientRect();
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
cloneWrapper.style.setProperty("--clone-top", `${rect.top + scrollTop}px`);
|
|
10898
|
-
cloneWrapper.style.setProperty("--clone-left", `${rect.left + scrollLeft}px`);
|
|
11134
|
+
cloneWrapper.style.setProperty("--clone-top", `${rect.top}px`);
|
|
11135
|
+
cloneWrapper.style.setProperty("--clone-left", `${rect.left}px`);
|
|
10899
11136
|
cloneWrapper.style.setProperty("--clone-width", `${rect.width}px`);
|
|
10900
11137
|
cloneWrapper.style.setProperty("--clone-height", `${rect.height}px`);
|
|
10901
11138
|
};
|
|
@@ -10914,12 +11151,43 @@ const setCloneDocumentRect = (cloneWrapper, el) => {
|
|
|
10914
11151
|
// so the element expands naturally from where the user clicked.
|
|
10915
11152
|
// On release, the `navi-drag-clone` attribute is removed inside
|
|
10916
11153
|
// startViewTransition to drop the scale back to 1 as the "new" state.
|
|
11154
|
+
// The chevron is the one the table's column drop preview uses, rotated by the
|
|
11155
|
+
// CSS above so each cap points into the line.
|
|
11156
|
+
const dropHintTemplate = /* html */`
|
|
11157
|
+
<div
|
|
11158
|
+
class="navi_drop_hint"
|
|
11159
|
+
popover="manual"
|
|
11160
|
+
>
|
|
11161
|
+
<span class="navi_drop_hint_cap" data-side="start">
|
|
11162
|
+
<svg fill="currentColor" viewBox="0 0 30.727 30.727">
|
|
11163
|
+
<path
|
|
11164
|
+
d="M29.994,10.183L15.363,24.812L0.733,10.184c-0.977-0.978-0.977-2.561,0-3.536c0.977-0.977,2.559-0.976,3.536,0l11.095,11.093L26.461,6.647c0.977-0.976,2.559-0.976,3.535,0C30.971,7.624,30.971,9.206,29.994,10.183z"
|
|
11165
|
+
/>
|
|
11166
|
+
</svg>
|
|
11167
|
+
</span>
|
|
11168
|
+
<span class="navi_drop_hint_cap" data-side="end">
|
|
11169
|
+
<svg fill="currentColor" viewBox="0 0 30.727 30.727">
|
|
11170
|
+
<path
|
|
11171
|
+
d="M29.994,10.183L15.363,24.812L0.733,10.184c-0.977-0.978-0.977-2.561,0-3.536c0.977-0.977,2.559-0.976,3.536,0l11.095,11.093L26.461,6.647c0.977-0.976,2.559-0.976,3.535,0C30.971,7.624,30.971,9.206,29.994,10.183z"
|
|
11172
|
+
/>
|
|
11173
|
+
</svg>
|
|
11174
|
+
</span>
|
|
11175
|
+
</div>
|
|
11176
|
+
`;
|
|
11177
|
+
const createDropHint = () => {
|
|
11178
|
+
const div = document.createElement("div");
|
|
11179
|
+
div.innerHTML = dropHintTemplate.trim();
|
|
11180
|
+
return div.firstElementChild;
|
|
11181
|
+
};
|
|
10917
11182
|
const createDragClone = (element, pointerEvent) => {
|
|
10918
11183
|
const rect = element.getBoundingClientRect();
|
|
10919
11184
|
const wrapper = document.createElement("div");
|
|
10920
11185
|
wrapper.setAttribute("navi-drag-clone-wrapper", "");
|
|
11186
|
+
// Manual: it is opened and closed with the drag, and must survive an Escape
|
|
11187
|
+
// or a click elsewhere (light dismiss would take it away mid-gesture).
|
|
11188
|
+
wrapper.setAttribute("popover", "manual");
|
|
10921
11189
|
wrapper.viewTransitionName = "navi-drag-clone-wrapper";
|
|
10922
|
-
|
|
11190
|
+
setCloneViewportRect(wrapper, element);
|
|
10923
11191
|
// Grab point within the element — used as transform-origin so the
|
|
10924
11192
|
// scale(1.15) expands from where the user clicked, not the element center.
|
|
10925
11193
|
// These offsets are element-relative so viewport coords are correct here.
|
|
@@ -11013,7 +11281,19 @@ const getResizeDirection = (element) => {
|
|
|
11013
11281
|
// directions: hide when a container closes, recheck when it reopens) — the
|
|
11014
11282
|
// selector/open-detection/timing primitives are identical for both, only
|
|
11015
11283
|
// what each does with a transition differs.
|
|
11016
|
-
const
|
|
11284
|
+
const OPENABLE_SELECTOR = "dialog, details, [popover], [aria-expanded]";
|
|
11285
|
+
|
|
11286
|
+
// An element that IS openable is closed in exactly the same way an element
|
|
11287
|
+
// inside one is — which matters for anything positioned against it, e.g. a
|
|
11288
|
+
// callout anchored to a dialog rather than to a field inside it. Same selector
|
|
11289
|
+
// as the walk up: whatever counts as openable above counts as openable here,
|
|
11290
|
+
// custom [aria-expanded] nodes included.
|
|
11291
|
+
const selfOrClosestOpenableAncestor = (element) => {
|
|
11292
|
+
if (element.matches?.(OPENABLE_SELECTOR)) {
|
|
11293
|
+
return element;
|
|
11294
|
+
}
|
|
11295
|
+
return closestOpenableAncestor(element);
|
|
11296
|
+
};
|
|
11017
11297
|
|
|
11018
11298
|
const closestOpenableAncestor = (element) => {
|
|
11019
11299
|
const parentElement = element.parentElement;
|
|
@@ -11023,7 +11303,7 @@ const closestOpenableAncestor = (element) => {
|
|
|
11023
11303
|
if (!parentElement.closest) {
|
|
11024
11304
|
return null;
|
|
11025
11305
|
}
|
|
11026
|
-
return parentElement.closest(
|
|
11306
|
+
return parentElement.closest(OPENABLE_SELECTOR);
|
|
11027
11307
|
};
|
|
11028
11308
|
|
|
11029
11309
|
const isAncestorOpen = (ancestor) => {
|
|
@@ -11575,7 +11855,10 @@ const MIN_CONTENT_VISIBILITY_RATIO = 0.6;
|
|
|
11575
11855
|
*/
|
|
11576
11856
|
// The event type observeSize() reports with — recognized by check() as "the
|
|
11577
11857
|
// change is in another element, not in the tracked rect".
|
|
11578
|
-
|
|
11858
|
+
// Exported: a caller that resized the element itself (a callout whose message
|
|
11859
|
+
// just changed, say) has to re-check with this rather than with nothing — its
|
|
11860
|
+
// own rect may not have moved at all, and the dedup would drop the check.
|
|
11861
|
+
const ELEMENT_SIZE_CHANGE = "observed_element_size_change";
|
|
11579
11862
|
|
|
11580
11863
|
const visibleRectEffect = (
|
|
11581
11864
|
element,
|
|
@@ -11645,7 +11928,12 @@ const visibleRectEffect = (
|
|
|
11645
11928
|
resizeWatchingPaused = false;
|
|
11646
11929
|
publishResizeWatchingPausedChange(false);
|
|
11647
11930
|
};
|
|
11648
|
-
|
|
11931
|
+
// Only so the reads below have something to read: a caller that describes
|
|
11932
|
+
// nothing gets no special treatment, it goes through the same dedup as any
|
|
11933
|
+
// other check. A caller that needs the dedup bypassed says so by passing the
|
|
11934
|
+
// event that means it (ELEMENT_SIZE_CHANGE).
|
|
11935
|
+
const UNSET_EVENT = { type: "unset" };
|
|
11936
|
+
const check = (event = UNSET_EVENT) => {
|
|
11649
11937
|
|
|
11650
11938
|
// visualViewport, not window.innerWidth/Height: the layout viewport
|
|
11651
11939
|
// doesn't shrink when the on-screen keyboard opens (same reasoning as
|
|
@@ -11825,7 +12113,7 @@ const visibleRectEffect = (
|
|
|
11825
12113
|
// defeating the whole point of observeSize (a popover reconsidering its
|
|
11826
12114
|
// placement once its own content shrinks/grows, a callout re-measuring
|
|
11827
12115
|
// against its message body).
|
|
11828
|
-
if (event.type ===
|
|
12116
|
+
if (event.type === ELEMENT_SIZE_CHANGE) {
|
|
11829
12117
|
lastVisibleRect = visibleRect;
|
|
11830
12118
|
lastViewportRect = viewportRect;
|
|
11831
12119
|
notify();
|
|
@@ -12062,7 +12350,12 @@ const visibleRectEffect = (
|
|
|
12062
12350
|
});
|
|
12063
12351
|
}
|
|
12064
12352
|
{
|
|
12065
|
-
|
|
12353
|
+
// Self-inclusive on the first step only: `element` can itself be the
|
|
12354
|
+
// dialog/popover that closes (a callout anchored to a dialog rather than
|
|
12355
|
+
// to a field inside it), and its own close hides it just as much as an
|
|
12356
|
+
// ancestor's would. The walk up below starts from parentElement, so the
|
|
12357
|
+
// chain still advances.
|
|
12358
|
+
let currentOpenableAncestor = selfOrClosestOpenableAncestor(element);
|
|
12066
12359
|
while (currentOpenableAncestor) {
|
|
12067
12360
|
const openableAncestor = currentOpenableAncestor;
|
|
12068
12361
|
if (!isAncestorOpen(openableAncestor)) {
|
|
@@ -12235,7 +12528,7 @@ const visibleRectEffect = (
|
|
|
12235
12528
|
pendingFrame = requestAnimationFrame(() => {
|
|
12236
12529
|
pendingFrame = null;
|
|
12237
12530
|
check(
|
|
12238
|
-
new CustomEvent(
|
|
12531
|
+
new CustomEvent(ELEMENT_SIZE_CHANGE, {
|
|
12239
12532
|
detail: { width, height },
|
|
12240
12533
|
}),
|
|
12241
12534
|
);
|
|
@@ -12497,7 +12790,7 @@ const toContainerAlignedPosition = (value) => {
|
|
|
12497
12790
|
* edges instead of the page viewport's, on both axes (the Y axis otherwise has no such
|
|
12498
12791
|
* clamp at all — see the clamp's own comment) — that part *is* gated on `hasValidAnchor`,
|
|
12499
12792
|
* unlike the coordinate-space conversion itself.
|
|
12500
|
-
* @returns {{ hasValidAnchor, shouldTransition, positionX, positionY, left, top, width, height, anchorLeft, anchorTop, anchorRight, anchorBottom, spaceLeft, spaceRight, spaceAbove, spaceBelow }}
|
|
12793
|
+
* @returns {{ hasValidAnchor, shouldTransition, positionX, positionY, left, top, width, height, anchorLeft, anchorTop, anchorRight, anchorBottom, spaceLeft, spaceRight, spaceAbove, spaceBelow, containerWidthAvailable, containerHeightAvailable }}
|
|
12501
12794
|
*/
|
|
12502
12795
|
const pickPositionRelativeTo = (
|
|
12503
12796
|
element,
|
|
@@ -13010,22 +13303,34 @@ const pickPositionRelativeTo = (
|
|
|
13010
13303
|
// so the usable space includes the anchor dimension.
|
|
13011
13304
|
// marginWithAnchor (gap between anchor and element) and marginWithContainer are subtracted
|
|
13012
13305
|
// so callers get the net usable space directly.
|
|
13013
|
-
const
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
const
|
|
13022
|
-
|
|
13023
|
-
(
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13306
|
+
const containerWidthAvailable = availableWidth - 2 * marginWithContainer;
|
|
13307
|
+
const containerHeightAvailable = availableHeight - 2 * marginWithContainer;
|
|
13308
|
+
// Docked to a container (no real anchor): the element is kept inside the
|
|
13309
|
+
// container's margin on BOTH sides — that is what the !hasValidAnchor clamp
|
|
13310
|
+
// above enforces — so what it has to work with is the container net of both.
|
|
13311
|
+
// The anchor-relative formulas below count the margin once, which is right
|
|
13312
|
+
// when the space really is bounded by the anchor on the other side, and
|
|
13313
|
+
// wrong here: it would let the far edge grow flush against the container.
|
|
13314
|
+
const effectiveSpaceAbove = !hasValidAnchor
|
|
13315
|
+
? containerHeightAvailable
|
|
13316
|
+
: (finalY === "inset-bottom" ? spaceAbove + anchorHeight : spaceAbove) -
|
|
13317
|
+
(finalY === "top" ? marginWithAnchor : 0) -
|
|
13318
|
+
marginWithContainer;
|
|
13319
|
+
const effectiveSpaceBelow = !hasValidAnchor
|
|
13320
|
+
? containerHeightAvailable
|
|
13321
|
+
: (finalY === "inset-top" ? spaceBelow + anchorHeight : spaceBelow) -
|
|
13322
|
+
(finalY === "bottom" ? marginWithAnchor : 0) -
|
|
13323
|
+
marginWithContainer;
|
|
13324
|
+
const effectiveSpaceLeft = !hasValidAnchor
|
|
13325
|
+
? containerWidthAvailable
|
|
13326
|
+
: (finalX === "inset-right" ? spaceLeft + anchorWidth : spaceLeft) -
|
|
13327
|
+
(finalX === "left" ? marginWithAnchor : 0) -
|
|
13328
|
+
marginWithContainer;
|
|
13329
|
+
const effectiveSpaceRight = !hasValidAnchor
|
|
13330
|
+
? containerWidthAvailable
|
|
13331
|
+
: (finalX === "inset-left" ? spaceRight + anchorWidth : spaceRight) -
|
|
13332
|
+
(finalX === "right" ? marginWithAnchor : 0) -
|
|
13333
|
+
marginWithContainer;
|
|
13029
13334
|
|
|
13030
13335
|
return {
|
|
13031
13336
|
// Whether a real anchor actually ended up used — false when there's no
|
|
@@ -13048,6 +13353,12 @@ const pickPositionRelativeTo = (
|
|
|
13048
13353
|
spaceRight: effectiveSpaceRight,
|
|
13049
13354
|
spaceAbove: effectiveSpaceAbove,
|
|
13050
13355
|
spaceBelow: effectiveSpaceBelow,
|
|
13356
|
+
// What a centered axis has to work with: the whole container, net of the
|
|
13357
|
+
// margin kept on both sides. spaceLeft/spaceRight can't answer that — they
|
|
13358
|
+
// are measured from the anchor, which for a container-docked element is
|
|
13359
|
+
// the container itself, so they collapse to -marginWithContainer.
|
|
13360
|
+
containerWidthAvailable,
|
|
13361
|
+
containerHeightAvailable,
|
|
13051
13362
|
};
|
|
13052
13363
|
};
|
|
13053
13364
|
|
|
@@ -13168,8 +13479,16 @@ const applyNewPosition = (
|
|
|
13168
13479
|
spaceRight,
|
|
13169
13480
|
spaceAbove,
|
|
13170
13481
|
spaceBelow,
|
|
13482
|
+
containerWidthAvailable,
|
|
13483
|
+
containerHeightAvailable,
|
|
13171
13484
|
},
|
|
13172
13485
|
) => {
|
|
13486
|
+
// A centered axis is published too, from the container's own extent: leaving
|
|
13487
|
+
// the property unset lets the consumer's size cap fall back to its viewport
|
|
13488
|
+
// default, which overflows any container smaller than the viewport (a
|
|
13489
|
+
// dialog/popover confined to a positioned ancestor). It stays a "remaining
|
|
13490
|
+
// space" either way — docked: what is left on that side, centered: the whole
|
|
13491
|
+
// container minus its margins.
|
|
13173
13492
|
if (positionY === "top" || positionY === "inset-bottom") {
|
|
13174
13493
|
element.style.setProperty(
|
|
13175
13494
|
"--container-position-remaining-height",
|
|
@@ -13180,8 +13499,13 @@ const applyNewPosition = (
|
|
|
13180
13499
|
"--container-position-remaining-height",
|
|
13181
13500
|
`${spaceBelow}px`,
|
|
13182
13501
|
);
|
|
13183
|
-
} else {
|
|
13502
|
+
} else if (containerHeightAvailable === undefined) {
|
|
13184
13503
|
element.style.removeProperty("--container-position-remaining-height");
|
|
13504
|
+
} else {
|
|
13505
|
+
element.style.setProperty(
|
|
13506
|
+
"--container-position-remaining-height",
|
|
13507
|
+
`${containerHeightAvailable}px`,
|
|
13508
|
+
);
|
|
13185
13509
|
}
|
|
13186
13510
|
if (positionX === "left" || positionX === "inset-right") {
|
|
13187
13511
|
element.style.setProperty(
|
|
@@ -13193,8 +13517,13 @@ const applyNewPosition = (
|
|
|
13193
13517
|
"--container-position-remaining-width",
|
|
13194
13518
|
`${spaceRight}px`,
|
|
13195
13519
|
);
|
|
13196
|
-
} else {
|
|
13520
|
+
} else if (containerWidthAvailable === undefined) {
|
|
13197
13521
|
element.style.removeProperty("--container-position-remaining-width");
|
|
13522
|
+
} else {
|
|
13523
|
+
element.style.setProperty(
|
|
13524
|
+
"--container-position-remaining-width",
|
|
13525
|
+
`${containerWidthAvailable}px`,
|
|
13526
|
+
);
|
|
13198
13527
|
}
|
|
13199
13528
|
|
|
13200
13529
|
// A single implicit keyframe turned out not to work here: the WAAPI
|
|
@@ -16484,4 +16813,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
|
|
|
16484
16813
|
};
|
|
16485
16814
|
};
|
|
16486
16815
|
|
|
16487
|
-
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, captureScrollState, chainEvent, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isSameColor, isScrollable, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, setAttribute, setAttributes, setStyles, snapToPixel, startDragToReorder, startDragToResizeGesture, stickyAsRelativeCoords, stringifyStyle, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|
|
16816
|
+
export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, captureScrollState, chainEvent, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isSameColor, isScrollable, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, setAttribute, setAttributes, setStyles, snapToPixel, startDragToReorder, startDragToResizeGesture, stickyAsRelativeCoords, stringifyStyle, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|