@jsenv/dom 0.17.32 → 0.17.34
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 +89 -28
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -4896,9 +4896,18 @@ const findFocusable = (element, { exclude } = {}) => {
|
|
|
4896
4896
|
return focusableDescendant;
|
|
4897
4897
|
};
|
|
4898
4898
|
|
|
4899
|
-
//
|
|
4900
|
-
//
|
|
4901
|
-
//
|
|
4899
|
+
// `hidden` and `clip` both cut the content off and are two different things
|
|
4900
|
+
// here, which is what `includeHidden` is about:
|
|
4901
|
+
// - `hidden` IS a scroll container. Nothing scrolls it by hand, but keyboard
|
|
4902
|
+
// arrows and scrollTop/scrollLeft do, so whoever cares where an element can
|
|
4903
|
+
// be brought into view has to count it.
|
|
4904
|
+
// - `clip` is not one. It clips and stops there: it has no scroll box at all,
|
|
4905
|
+
// and scrollTop/scrollLeft on it read back 0 whatever they are set to. That
|
|
4906
|
+
// is the whole reason a layout reaches for it (see navi's
|
|
4907
|
+
// docs/MOBILE_LAYOUT_PITFALLS.md), so it is never included, whatever the
|
|
4908
|
+
// caller asks for.
|
|
4909
|
+
// `visible` is the other non-scrolling value, and the only one on which the
|
|
4910
|
+
// content still reaches the ancestors.
|
|
4902
4911
|
const isScrollable = (element, { includeHidden } = {}) => {
|
|
4903
4912
|
if (canHaveVerticalScroll(element, { includeHidden })) {
|
|
4904
4913
|
return true;
|
|
@@ -4940,7 +4949,10 @@ const canHaveVerticalScroll = (element, { includeHidden }) => {
|
|
|
4940
4949
|
}
|
|
4941
4950
|
return false;
|
|
4942
4951
|
}
|
|
4943
|
-
if (verticalOverflow === "
|
|
4952
|
+
if (verticalOverflow === "clip") {
|
|
4953
|
+
return false;
|
|
4954
|
+
}
|
|
4955
|
+
if (verticalOverflow === "hidden") {
|
|
4944
4956
|
return includeHidden;
|
|
4945
4957
|
}
|
|
4946
4958
|
const overflow = getStyle(element, "overflow");
|
|
@@ -4951,7 +4963,10 @@ const canHaveVerticalScroll = (element, { includeHidden }) => {
|
|
|
4951
4963
|
}
|
|
4952
4964
|
return false;
|
|
4953
4965
|
}
|
|
4954
|
-
if (overflow === "
|
|
4966
|
+
if (overflow === "clip") {
|
|
4967
|
+
return false;
|
|
4968
|
+
}
|
|
4969
|
+
if (overflow === "hidden") {
|
|
4955
4970
|
return includeHidden;
|
|
4956
4971
|
}
|
|
4957
4972
|
return true; // "auto", "scroll"
|
|
@@ -4965,7 +4980,10 @@ const canHaveHorizontalScroll = (element, { includeHidden }) => {
|
|
|
4965
4980
|
}
|
|
4966
4981
|
return false;
|
|
4967
4982
|
}
|
|
4968
|
-
if (horizontalOverflow === "
|
|
4983
|
+
if (horizontalOverflow === "clip") {
|
|
4984
|
+
return false;
|
|
4985
|
+
}
|
|
4986
|
+
if (horizontalOverflow === "hidden") {
|
|
4969
4987
|
return includeHidden;
|
|
4970
4988
|
}
|
|
4971
4989
|
const overflow = getStyle(element, "overflow");
|
|
@@ -4976,7 +4994,10 @@ const canHaveHorizontalScroll = (element, { includeHidden }) => {
|
|
|
4976
4994
|
}
|
|
4977
4995
|
return false;
|
|
4978
4996
|
}
|
|
4979
|
-
if (overflow === "
|
|
4997
|
+
if (overflow === "clip") {
|
|
4998
|
+
return false;
|
|
4999
|
+
}
|
|
5000
|
+
if (overflow === "hidden") {
|
|
4980
5001
|
return includeHidden;
|
|
4981
5002
|
}
|
|
4982
5003
|
return true; // "auto", "scroll"
|
|
@@ -15686,7 +15707,7 @@ const visibleRectEffect = (
|
|
|
15686
15707
|
onNaviPositionChange,
|
|
15687
15708
|
);
|
|
15688
15709
|
// Dispatched by applyNewPosition's own notifyPositionTransition
|
|
15689
|
-
// around this ancestor's own
|
|
15710
|
+
// around this ancestor's own placement animation (distinct from
|
|
15690
15711
|
// navi_position_change, fired once with the final target, not per
|
|
15691
15712
|
// frame). The anchor this element is positioned against may live
|
|
15692
15713
|
// inside that ancestor and be moving right now — rather than hiding
|
|
@@ -16008,7 +16029,10 @@ const toContainerAlignedPosition = (value) => {
|
|
|
16008
16029
|
* axis never reads the attribute back itself (`positionAreaFixed` always wins).
|
|
16009
16030
|
*
|
|
16010
16031
|
* @param {HTMLElement} element - The element to position (position: absolute or
|
|
16011
|
-
* fixed — detected from its own computed style, see the scroll offset comment below)
|
|
16032
|
+
* fixed — detected from its own computed style, see the scroll offset comment below),
|
|
16033
|
+
* laid out at its containing block's own origin: the returned `left`/`top` are meant
|
|
16034
|
+
* to be applied as a translate from there, see `applyNewPosition` for the whole
|
|
16035
|
+
* contract and for why the placement may not go through `left`/`top` themselves
|
|
16012
16036
|
* @param {HTMLElement} [anchor] - The anchor element to position against. Omit (or pass
|
|
16013
16037
|
* `null`/`undefined`) when there's no real anchor to dock `element` against a *container*
|
|
16014
16038
|
* instead — see `container` below; in that mode, "top"/"bottom"/"left"/"right" are
|
|
@@ -16206,12 +16230,12 @@ const pickPositionRelativeTo = (
|
|
|
16206
16230
|
const clampLeftBound = availableLeft;
|
|
16207
16231
|
const clampRightBound = availableRight;
|
|
16208
16232
|
// offsetWidth/offsetHeight (layout box), not getBoundingClientRect() (the
|
|
16209
|
-
// painted/transformed box): the element being positioned
|
|
16210
|
-
//
|
|
16211
|
-
//
|
|
16212
|
-
// instant
|
|
16213
|
-
//
|
|
16214
|
-
//
|
|
16233
|
+
// painted/transformed box): the element being positioned is moved by
|
|
16234
|
+
// transforms — its own placement translate (see applyNewPosition), plus
|
|
16235
|
+
// whatever `scale` an entrance animation happens to be playing at that
|
|
16236
|
+
// instant (a popover using animation="scaling"/"expand-*") — so
|
|
16237
|
+
// getBoundingClientRect() answers where it is painted, at its *shrunk*
|
|
16238
|
+
// mid-animation size, instead of the box being measured here.
|
|
16215
16239
|
const elementWidth = element.offsetWidth;
|
|
16216
16240
|
const elementHeight = element.offsetHeight;
|
|
16217
16241
|
const anchorWidth = anchorRight - anchorLeft;
|
|
@@ -16720,11 +16744,32 @@ const notifyPositionTransition = (element, animation) => {
|
|
|
16720
16744
|
};
|
|
16721
16745
|
|
|
16722
16746
|
/**
|
|
16723
|
-
* Applies a `pickPositionRelativeTo` result to `element
|
|
16724
|
-
*
|
|
16725
|
-
*
|
|
16726
|
-
*
|
|
16727
|
-
*
|
|
16747
|
+
* Applies a `pickPositionRelativeTo` result to `element`, as a `translate`.
|
|
16748
|
+
*
|
|
16749
|
+
* The two halves of that contract, which a caller must honor:
|
|
16750
|
+
*
|
|
16751
|
+
* 1. `element` stays laid out at its containing block's own origin — its CSS
|
|
16752
|
+
* must say `left: 0; top: 0` (and leave right/bottom `auto`). The computed
|
|
16753
|
+
* `left`/`top` are then applied as a translate from there, which lands the
|
|
16754
|
+
* box in exactly the same place `left: Npx` would have. The reason to go
|
|
16755
|
+
* through a transform at all is the *measuring*: an out-of-flow box with a
|
|
16756
|
+
* shrink-to-fit width can never be wider than "containing block width -
|
|
16757
|
+
* left", so a box placed with `left` reports a width that depends on where
|
|
16758
|
+
* it currently stands — and `pickPositionRelativeTo` decides which side to
|
|
16759
|
+
* place it on from that very width. Feeding a placement back into the
|
|
16760
|
+
* decision that produced it makes the element alternate sides forever, one
|
|
16761
|
+
* flip per reposition. Laid out at the origin, the measured width only ever
|
|
16762
|
+
* depends on the content and on the size caps below.
|
|
16763
|
+
* 2. The `translate` property belongs to this function alone. An animation on
|
|
16764
|
+
* the same element uses `scale` (which composes: individual transform
|
|
16765
|
+
* properties apply translate, then rotate, then scale, then `transform`, so
|
|
16766
|
+
* a scale never rescales the placement) or `transform` — see popup_css.js,
|
|
16767
|
+
* whose slide entrances translate through `transform` for that reason.
|
|
16768
|
+
*
|
|
16769
|
+
* The translate is set instantly (a scroll-triggered reposition should never
|
|
16770
|
+
* lag its target); when `shouldTransition` is set (a resize-triggered
|
|
16771
|
+
* reposition), the visual move is played out via `element.animate()` instead —
|
|
16772
|
+
* kept independent of Popover/Dialog/Callout's own opacity/scale/display CSS
|
|
16728
16773
|
* transition on the same element, so neither can clobber the other (see
|
|
16729
16774
|
* notifyPositionTransition's own doc for why a dedicated Animation over a
|
|
16730
16775
|
* CSS one). Duration comes from `--popup-position-transition-duration`
|
|
@@ -16796,18 +16841,19 @@ const applyNewPosition = (
|
|
|
16796
16841
|
// A single implicit keyframe turned out not to work here: the WAAPI
|
|
16797
16842
|
// "neutral" start keyframe isn't frozen at `animate()` call time, it's
|
|
16798
16843
|
// resolved from the underlying value when the animation is first
|
|
16799
|
-
// *sampled* (the next frame) — by then `element.style.
|
|
16844
|
+
// *sampled* (the next frame) — by then `element.style.translate` below
|
|
16800
16845
|
// has already been overwritten with the new target, so start === end and
|
|
16801
16846
|
// nothing visibly moves (observed as the dialog just jumping). Reading
|
|
16802
16847
|
// the previous value ourselves, before overwriting it, and passing both
|
|
16803
16848
|
// keyframes explicitly sidesteps that entirely.
|
|
16804
|
-
const
|
|
16805
|
-
const
|
|
16849
|
+
const previousTranslate = parseTranslate(element.style.translate);
|
|
16850
|
+
const previousLeft = previousTranslate ? previousTranslate.x : left;
|
|
16851
|
+
const previousTop = previousTranslate ? previousTranslate.y : top;
|
|
16806
16852
|
if (shouldTransition) {
|
|
16807
16853
|
const animation = element.animate(
|
|
16808
16854
|
[
|
|
16809
|
-
{
|
|
16810
|
-
{
|
|
16855
|
+
{ translate: `${previousLeft}px ${previousTop}px` },
|
|
16856
|
+
{ translate: `${left}px ${top}px` },
|
|
16811
16857
|
],
|
|
16812
16858
|
{
|
|
16813
16859
|
duration: parseTransitionDurationMs(
|
|
@@ -16820,16 +16866,31 @@ const applyNewPosition = (
|
|
|
16820
16866
|
);
|
|
16821
16867
|
notifyPositionTransition(element, animation);
|
|
16822
16868
|
}
|
|
16823
|
-
// The specified
|
|
16869
|
+
// The specified translate is set to its final target right away,
|
|
16824
16870
|
// regardless of `shouldTransition` — the animation above only plays the
|
|
16825
16871
|
// visual move from the old position, it never becomes the actual
|
|
16826
16872
|
// specified style (see notifyPositionTransition's own commitStyles for
|
|
16827
16873
|
// why that matters once it ends).
|
|
16828
|
-
element.style.
|
|
16829
|
-
element.style.top = `${top}px`;
|
|
16874
|
+
element.style.translate = `${left}px ${top}px`;
|
|
16830
16875
|
dispatchCustomEvent(element, "navi_position_change");
|
|
16831
16876
|
};
|
|
16832
16877
|
|
|
16878
|
+
// "42px 100px" as { x, y }. Anything else — unset, "none", a single-value
|
|
16879
|
+
// shorthand — reads as "never placed yet" (null), so a first placement has no
|
|
16880
|
+
// stale point to be animated from.
|
|
16881
|
+
const parseTranslate = (translate) => {
|
|
16882
|
+
if (!translate) {
|
|
16883
|
+
return null;
|
|
16884
|
+
}
|
|
16885
|
+
const [x, y] = translate.split(" ");
|
|
16886
|
+
const xNumber = parseFloat(x);
|
|
16887
|
+
const yNumber = parseFloat(y);
|
|
16888
|
+
if (Number.isNaN(xNumber) || Number.isNaN(yNumber)) {
|
|
16889
|
+
return null;
|
|
16890
|
+
}
|
|
16891
|
+
return { x: xNumber, y: yNumber };
|
|
16892
|
+
};
|
|
16893
|
+
|
|
16833
16894
|
const [publishDebugger, subscribeDebugger] = createPubSub();
|
|
16834
16895
|
|
|
16835
16896
|
const notifyDebuggerStart = () => {
|