@jsenv/dom 0.17.2 → 0.17.4
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 +297 -255
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -2051,16 +2051,12 @@ const parseCSSTransform = (transformString, normalize) => {
|
|
|
2051
2051
|
|
|
2052
2052
|
const transformObj = {};
|
|
2053
2053
|
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
while ((match = transformPattern.exec(transformString)) !== null) {
|
|
2059
|
-
const [, functionName, value] = match;
|
|
2060
|
-
|
|
2054
|
+
for (const { functionName, value, source } of readTransformFunctions(
|
|
2055
|
+
transformString,
|
|
2056
|
+
)) {
|
|
2061
2057
|
// Handle matrix functions specially
|
|
2062
2058
|
if (functionName === "matrix" || functionName === "matrix3d") {
|
|
2063
|
-
const matrixComponents = parseMatrixTransform(
|
|
2059
|
+
const matrixComponents = parseMatrixTransform(source);
|
|
2064
2060
|
if (matrixComponents) {
|
|
2065
2061
|
// Only add non-default values to preserve original information
|
|
2066
2062
|
Object.assign(transformObj, matrixComponents);
|
|
@@ -2079,6 +2075,42 @@ const parseCSSTransform = (transformString, normalize) => {
|
|
|
2079
2075
|
// Return undefined if no properties were extracted (preserves original information)
|
|
2080
2076
|
return Object.keys(transformObj).length > 0 ? transformObj : undefined;
|
|
2081
2077
|
};
|
|
2078
|
+
|
|
2079
|
+
// Cuts "translateX(10px) translateY(env(safe-area-inset-top))" into its
|
|
2080
|
+
// functions. Parentheses are counted rather than matched with a regex: a
|
|
2081
|
+
// transform value may hold calc(), env(), min()… and stopping at the first ")"
|
|
2082
|
+
// truncates them.
|
|
2083
|
+
const TRANSFORM_FUNCTION_START_REGEX = /(\w+)\(/g;
|
|
2084
|
+
const readTransformFunctions = (transformString) => {
|
|
2085
|
+
const transformFunctions = [];
|
|
2086
|
+
TRANSFORM_FUNCTION_START_REGEX.lastIndex = 0;
|
|
2087
|
+
let match;
|
|
2088
|
+
while ((match = TRANSFORM_FUNCTION_START_REGEX.exec(transformString))) {
|
|
2089
|
+
const valueStart = match.index + match[0].length;
|
|
2090
|
+
let depth = 1;
|
|
2091
|
+
let index = valueStart;
|
|
2092
|
+
while (index < transformString.length && depth > 0) {
|
|
2093
|
+
const char = transformString[index];
|
|
2094
|
+
if (char === "(") {
|
|
2095
|
+
depth++;
|
|
2096
|
+
} else if (char === ")") {
|
|
2097
|
+
depth--;
|
|
2098
|
+
}
|
|
2099
|
+
index++;
|
|
2100
|
+
}
|
|
2101
|
+
if (depth > 0) {
|
|
2102
|
+
// Unbalanced: nothing reliable left to read after this point.
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2105
|
+
transformFunctions.push({
|
|
2106
|
+
functionName: match[1],
|
|
2107
|
+
value: transformString.slice(valueStart, index - 1),
|
|
2108
|
+
source: transformString.slice(match.index, index),
|
|
2109
|
+
});
|
|
2110
|
+
TRANSFORM_FUNCTION_START_REGEX.lastIndex = index;
|
|
2111
|
+
}
|
|
2112
|
+
return transformFunctions;
|
|
2113
|
+
};
|
|
2082
2114
|
// Parse a matrix transform and extract simple transform components when possible
|
|
2083
2115
|
const parseMatrixTransform = (matrixString) => {
|
|
2084
2116
|
// Match matrix() or matrix3d() functions
|
|
@@ -2324,33 +2356,6 @@ const globalCSSKeywordSet = new Set([
|
|
|
2324
2356
|
"unset",
|
|
2325
2357
|
"revert",
|
|
2326
2358
|
]);
|
|
2327
|
-
// Keywords that should NOT get automatic units when used with properties from:
|
|
2328
|
-
// - pxPropertySet (width, height, fontSize, etc.)
|
|
2329
|
-
// - degPropertySet (rotate, skew, etc.)
|
|
2330
|
-
// - unitlessPropertySet (opacity, zIndex, etc.)
|
|
2331
|
-
// This prevents auto-unit addition: e.g., width: "auto" stays "auto", not "autopx"
|
|
2332
|
-
const unitlessKeywordSet = new Set([
|
|
2333
|
-
...globalCSSKeywordSet,
|
|
2334
|
-
// Size/dimension keywords for pxPropertySet properties
|
|
2335
|
-
"fit-content",
|
|
2336
|
-
"min-content",
|
|
2337
|
-
"max-content",
|
|
2338
|
-
// Font size keywords for fontSize
|
|
2339
|
-
"medium",
|
|
2340
|
-
"small",
|
|
2341
|
-
"large",
|
|
2342
|
-
"x-small",
|
|
2343
|
-
"x-large",
|
|
2344
|
-
"xx-small",
|
|
2345
|
-
"xx-large",
|
|
2346
|
-
"smaller",
|
|
2347
|
-
"larger",
|
|
2348
|
-
// Border width keywords for borderWidth properties
|
|
2349
|
-
"thin",
|
|
2350
|
-
"thick",
|
|
2351
|
-
// Line height keyword (though lineHeight is handled specially)
|
|
2352
|
-
"normal",
|
|
2353
|
-
]);
|
|
2354
2359
|
// Keywords for backgroundImage property that should NOT be wrapped in url()
|
|
2355
2360
|
// Used to prevent: background: "none" becoming background: "url(none)"
|
|
2356
2361
|
const backgroundKeywordSet = new Set([
|
|
@@ -2387,10 +2392,28 @@ const getUnit = (value) => {
|
|
|
2387
2392
|
}
|
|
2388
2393
|
return "";
|
|
2389
2394
|
};
|
|
2390
|
-
// Check if value already has a unit
|
|
2391
|
-
const isUnitless = (value) => getUnit(value) === "";
|
|
2392
2395
|
const hasCSSSizeUnit = (value) => cssSizeUnitSet.has(getUnit(value));
|
|
2393
2396
|
|
|
2397
|
+
// A single number and nothing else — the only shape a unit may be appended to.
|
|
2398
|
+
// Everything else already says what it is: a unit ("2em"), a keyword ("auto"),
|
|
2399
|
+
// a CSS expression ("env(safe-area-inset-top)", "calc(…)") or a list of those
|
|
2400
|
+
// ("0 auto", "10px env(safe-area-inset-right)"). Asking "is this one number"
|
|
2401
|
+
// covers them all at once; listing what must be left alone (keywords, then
|
|
2402
|
+
// functions, then lists…) only ever covers what someone thought of.
|
|
2403
|
+
const isBareNumber = (value) => {
|
|
2404
|
+
if (value === "") {
|
|
2405
|
+
return false;
|
|
2406
|
+
}
|
|
2407
|
+
return !isNaN(Number(value));
|
|
2408
|
+
};
|
|
2409
|
+
// The same number, carrying exactly the unit asked for, and nothing else.
|
|
2410
|
+
const isBareNumberWithUnit = (value, unit) => {
|
|
2411
|
+
if (!value.endsWith(unit)) {
|
|
2412
|
+
return false;
|
|
2413
|
+
}
|
|
2414
|
+
return isBareNumber(value.slice(0, -unit.length));
|
|
2415
|
+
};
|
|
2416
|
+
|
|
2394
2417
|
// url(
|
|
2395
2418
|
// linear-gradient(
|
|
2396
2419
|
// radial-gradient(
|
|
@@ -2650,23 +2673,21 @@ const isCSSKeyword = (value) => {
|
|
|
2650
2673
|
};
|
|
2651
2674
|
const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
|
|
2652
2675
|
if (typeof value === "string") {
|
|
2653
|
-
|
|
2654
|
-
if (isCSSFunction(value)) {
|
|
2655
|
-
return value;
|
|
2656
|
-
}
|
|
2657
|
-
// Keep strings as-is (including %, em, rem, auto, none, etc.)
|
|
2676
|
+
value = value.trim();
|
|
2658
2677
|
if (preferedType === "string") {
|
|
2659
|
-
|
|
2678
|
+
// Everything that is not a lone number already carries its own meaning
|
|
2679
|
+
// and goes to the DOM untouched: "2em", "auto", "env(safe-area-inset-top)",
|
|
2680
|
+
// "calc(…)", "0 auto", "10px env(safe-area-inset-right)".
|
|
2681
|
+
if (unit && isBareNumber(value)) {
|
|
2660
2682
|
return `${value}${unit}`;
|
|
2661
2683
|
}
|
|
2662
2684
|
return value;
|
|
2663
2685
|
}
|
|
2664
|
-
//
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
}
|
|
2686
|
+
// A number to work with, only when the value is exactly one:
|
|
2687
|
+
// "12px" -> 12, "0.5" -> 0.5. "10px 20px" is a list and "calc(…)" is an
|
|
2688
|
+
// expression — parseFloat would silently keep their first number.
|
|
2689
|
+
if (unit ? isBareNumberWithUnit(value, unit) : isBareNumber(value)) {
|
|
2690
|
+
return parseFloat(value);
|
|
2670
2691
|
}
|
|
2671
2692
|
return value;
|
|
2672
2693
|
}
|
|
@@ -4587,6 +4608,185 @@ const findFocusable = (element, { exclude } = {}) => {
|
|
|
4587
4608
|
return focusableDescendant;
|
|
4588
4609
|
};
|
|
4589
4610
|
|
|
4611
|
+
// note: keep in mind that an element with overflow: 'hidden' is scrollable
|
|
4612
|
+
// it can be scrolled using keyboard arrows or JavaScript properties such as scrollTop, scrollLeft
|
|
4613
|
+
// the only overflow that prevents scroll is "visible"
|
|
4614
|
+
const isScrollable = (element, { includeHidden } = {}) => {
|
|
4615
|
+
if (canHaveVerticalScroll(element, { includeHidden })) {
|
|
4616
|
+
return true;
|
|
4617
|
+
}
|
|
4618
|
+
if (canHaveHorizontalScroll(element, { includeHidden })) {
|
|
4619
|
+
return true;
|
|
4620
|
+
}
|
|
4621
|
+
return false;
|
|
4622
|
+
};
|
|
4623
|
+
|
|
4624
|
+
// Whether this element is what scrolls on that axis: it says it may (overflow)
|
|
4625
|
+
// and it has somewhere to go (it overflows). Both are needed — an "auto" box
|
|
4626
|
+
// whose content fits scrolls nothing, and `overflow-x: auto` alone makes the
|
|
4627
|
+
// COMPUTED overflow-y auto too (CSS does not let one axis stay visible next to
|
|
4628
|
+
// a scrolling one), so a box scrolling sideways declares a vertical scroll it
|
|
4629
|
+
// will never do.
|
|
4630
|
+
const canScroll = (element, axis) => {
|
|
4631
|
+
if (!element || element.nodeType !== 1) {
|
|
4632
|
+
return false;
|
|
4633
|
+
}
|
|
4634
|
+
const style = getComputedStyle(element);
|
|
4635
|
+
const overflow = axis === "x" ? style.overflowX : style.overflowY;
|
|
4636
|
+
if (overflow !== "auto" && overflow !== "scroll") {
|
|
4637
|
+
return false;
|
|
4638
|
+
}
|
|
4639
|
+
const scrollSize = axis === "x" ? element.scrollWidth : element.scrollHeight;
|
|
4640
|
+
const clientSize = axis === "x" ? element.clientWidth : element.clientHeight;
|
|
4641
|
+
// A pixel of slack: subpixel content rounds scrollSize up on boxes that have
|
|
4642
|
+
// nowhere to scroll to.
|
|
4643
|
+
return scrollSize - clientSize > 1;
|
|
4644
|
+
};
|
|
4645
|
+
|
|
4646
|
+
const canHaveVerticalScroll = (element, { includeHidden }) => {
|
|
4647
|
+
const verticalOverflow = getStyle(element, "overflow-y");
|
|
4648
|
+
if (verticalOverflow === "visible") {
|
|
4649
|
+
// browser returns "visible" on documentElement even if it is scrollable
|
|
4650
|
+
if (isDocumentElement(element)) {
|
|
4651
|
+
return true;
|
|
4652
|
+
}
|
|
4653
|
+
return false;
|
|
4654
|
+
}
|
|
4655
|
+
if (verticalOverflow === "hidden" || verticalOverflow === "clip") {
|
|
4656
|
+
return includeHidden;
|
|
4657
|
+
}
|
|
4658
|
+
const overflow = getStyle(element, "overflow");
|
|
4659
|
+
if (overflow === "visible") {
|
|
4660
|
+
// browser returns "visible" on documentElement even if it is scrollable
|
|
4661
|
+
if (isDocumentElement(element)) {
|
|
4662
|
+
return true;
|
|
4663
|
+
}
|
|
4664
|
+
return false;
|
|
4665
|
+
}
|
|
4666
|
+
if (overflow === "hidden" || overflow === "clip") {
|
|
4667
|
+
return includeHidden;
|
|
4668
|
+
}
|
|
4669
|
+
return true; // "auto", "scroll"
|
|
4670
|
+
};
|
|
4671
|
+
const canHaveHorizontalScroll = (element, { includeHidden }) => {
|
|
4672
|
+
const horizontalOverflow = getStyle(element, "overflow-x");
|
|
4673
|
+
if (horizontalOverflow === "visible") {
|
|
4674
|
+
// browser returns "visible" on documentElement even if it is scrollable
|
|
4675
|
+
if (isDocumentElement(element)) {
|
|
4676
|
+
return true;
|
|
4677
|
+
}
|
|
4678
|
+
return false;
|
|
4679
|
+
}
|
|
4680
|
+
if (horizontalOverflow === "hidden" || horizontalOverflow === "clip") {
|
|
4681
|
+
return includeHidden;
|
|
4682
|
+
}
|
|
4683
|
+
const overflow = getStyle(element, "overflow");
|
|
4684
|
+
if (overflow === "visible") {
|
|
4685
|
+
if (isDocumentElement(element)) {
|
|
4686
|
+
// browser returns "visible" on documentElement even if it is scrollable
|
|
4687
|
+
return true;
|
|
4688
|
+
}
|
|
4689
|
+
return false;
|
|
4690
|
+
}
|
|
4691
|
+
if (overflow === "hidden" || overflow === "clip") {
|
|
4692
|
+
return includeHidden;
|
|
4693
|
+
}
|
|
4694
|
+
return true; // "auto", "scroll"
|
|
4695
|
+
};
|
|
4696
|
+
|
|
4697
|
+
const getScrollingElement = (document) => {
|
|
4698
|
+
const { scrollingElement } = document;
|
|
4699
|
+
if (scrollingElement) {
|
|
4700
|
+
return scrollingElement;
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
if (isCompliant(document)) {
|
|
4704
|
+
return document.documentElement;
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
const body = document.body;
|
|
4708
|
+
const isFrameset = body && !/body/i.test(body.tagName);
|
|
4709
|
+
const possiblyScrollingElement = isFrameset ? getNextBodyElement(body) : body;
|
|
4710
|
+
|
|
4711
|
+
// If `body` is itself scrollable, it is not the `scrollingElement`.
|
|
4712
|
+
return possiblyScrollingElement && bodyIsScrollable(possiblyScrollingElement)
|
|
4713
|
+
? null
|
|
4714
|
+
: possiblyScrollingElement;
|
|
4715
|
+
};
|
|
4716
|
+
|
|
4717
|
+
const isHidden = (element) => {
|
|
4718
|
+
const display = getStyle(element, "display");
|
|
4719
|
+
if (display === "none") {
|
|
4720
|
+
return false;
|
|
4721
|
+
}
|
|
4722
|
+
|
|
4723
|
+
if (
|
|
4724
|
+
display === "table-row" ||
|
|
4725
|
+
display === "table-group" ||
|
|
4726
|
+
display === "table-column"
|
|
4727
|
+
) {
|
|
4728
|
+
return getStyle(element, "visibility") !== "collapsed";
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
return true;
|
|
4732
|
+
};
|
|
4733
|
+
const isCompliant = (document) => {
|
|
4734
|
+
// Note: document.compatMode can be toggle at runtime by document.write
|
|
4735
|
+
const isStandardsMode = /^CSS1/.test(document.compatMode);
|
|
4736
|
+
if (isStandardsMode) {
|
|
4737
|
+
return testScrollCompliance(document);
|
|
4738
|
+
}
|
|
4739
|
+
return false;
|
|
4740
|
+
};
|
|
4741
|
+
const testScrollCompliance = (document) => {
|
|
4742
|
+
const iframe = document.createElement("iframe");
|
|
4743
|
+
iframe.style.height = "1px";
|
|
4744
|
+
const parentNode = document.body || document.documentElement || document;
|
|
4745
|
+
parentNode.appendChild(iframe);
|
|
4746
|
+
const iframeDocument = iframe.contentWindow.document;
|
|
4747
|
+
iframeDocument.write('<!DOCTYPE html><div style="height:9999em">x</div>');
|
|
4748
|
+
iframeDocument.close();
|
|
4749
|
+
const scrollComplianceResult =
|
|
4750
|
+
iframeDocument.documentElement.scrollHeight >
|
|
4751
|
+
iframeDocument.body.scrollHeight;
|
|
4752
|
+
iframe.parentNode.removeChild(iframe);
|
|
4753
|
+
return scrollComplianceResult;
|
|
4754
|
+
};
|
|
4755
|
+
const getNextBodyElement = (frameset) => {
|
|
4756
|
+
// We use this function to be correct per spec in case `document.body` is
|
|
4757
|
+
// a `frameset` but there exists a later `body`. Since `document.body` is
|
|
4758
|
+
// a `frameset`, we know the root is an `html`, and there was no `body`
|
|
4759
|
+
// before the `frameset`, so we just need to look at siblings after the
|
|
4760
|
+
// `frameset`.
|
|
4761
|
+
let current = frameset;
|
|
4762
|
+
while ((current = current.nextSibling)) {
|
|
4763
|
+
if (current.nodeType === 1 && isBodyElement(current)) {
|
|
4764
|
+
return current;
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
return null;
|
|
4768
|
+
};
|
|
4769
|
+
const isBodyElement = (element) => element.ownerDocument.body === element;
|
|
4770
|
+
const bodyIsScrollable = (body) => {
|
|
4771
|
+
// a body element is scrollable if body and html are scrollable and rendered
|
|
4772
|
+
if (!isScrollable(body)) {
|
|
4773
|
+
return false;
|
|
4774
|
+
}
|
|
4775
|
+
if (isHidden(body)) {
|
|
4776
|
+
return false;
|
|
4777
|
+
}
|
|
4778
|
+
|
|
4779
|
+
const documentElement = body.ownerDocument.documentElement;
|
|
4780
|
+
if (!isScrollable(documentElement)) {
|
|
4781
|
+
return false;
|
|
4782
|
+
}
|
|
4783
|
+
if (isHidden(documentElement)) {
|
|
4784
|
+
return false;
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
return true;
|
|
4788
|
+
};
|
|
4789
|
+
|
|
4590
4790
|
/**
|
|
4591
4791
|
* Returns the browser's default action for a keyboard event on its target element.
|
|
4592
4792
|
*
|
|
@@ -4677,23 +4877,6 @@ const isTypingIntent = (e) => {
|
|
|
4677
4877
|
return false;
|
|
4678
4878
|
};
|
|
4679
4879
|
|
|
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
|
-
|
|
4697
4880
|
const DEFAULT_BEHAVIORS = [
|
|
4698
4881
|
{
|
|
4699
4882
|
test: () => true,
|
|
@@ -4870,22 +5053,17 @@ const DEFAULT_BEHAVIORS = [
|
|
|
4870
5053
|
// be read from the keyboard, and that is not a key to take. Asked per axis
|
|
4871
5054
|
// and per element, not from a class or an attribute: what makes it true is
|
|
4872
5055
|
// that it overflows right now.
|
|
4873
|
-
test: (el) =>
|
|
5056
|
+
test: (el) => canScroll(el, "y") || canScroll(el, "x"),
|
|
4874
5057
|
keys: {
|
|
4875
|
-
arrowup: (e) =>
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
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),
|
|
5058
|
+
arrowup: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5059
|
+
arrowdown: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5060
|
+
arrowleft: (e) => (canScroll(e.target, "x") ? "scroll_self" : undefined),
|
|
5061
|
+
arrowright: (e) => (canScroll(e.target, "x") ? "scroll_self" : undefined),
|
|
5062
|
+
pageup: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5063
|
+
pagedown: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5064
|
+
home: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5065
|
+
end: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
5066
|
+
space: (e) => (canScroll(e.target, "y") ? "scroll_self" : undefined),
|
|
4889
5067
|
},
|
|
4890
5068
|
// no fallback: only these keys are claimed, everything else keeps looking
|
|
4891
5069
|
},
|
|
@@ -6345,163 +6523,6 @@ const captureScrollState = (element) => {
|
|
|
6345
6523
|
};
|
|
6346
6524
|
};
|
|
6347
6525
|
|
|
6348
|
-
// note: keep in mind that an element with overflow: 'hidden' is scrollable
|
|
6349
|
-
// it can be scrolled using keyboard arrows or JavaScript properties such as scrollTop, scrollLeft
|
|
6350
|
-
// the only overflow that prevents scroll is "visible"
|
|
6351
|
-
const isScrollable = (element, { includeHidden } = {}) => {
|
|
6352
|
-
if (canHaveVerticalScroll(element, { includeHidden })) {
|
|
6353
|
-
return true;
|
|
6354
|
-
}
|
|
6355
|
-
if (canHaveHorizontalScroll(element, { includeHidden })) {
|
|
6356
|
-
return true;
|
|
6357
|
-
}
|
|
6358
|
-
return false;
|
|
6359
|
-
};
|
|
6360
|
-
|
|
6361
|
-
const canHaveVerticalScroll = (element, { includeHidden }) => {
|
|
6362
|
-
const verticalOverflow = getStyle(element, "overflow-y");
|
|
6363
|
-
if (verticalOverflow === "visible") {
|
|
6364
|
-
// browser returns "visible" on documentElement even if it is scrollable
|
|
6365
|
-
if (isDocumentElement(element)) {
|
|
6366
|
-
return true;
|
|
6367
|
-
}
|
|
6368
|
-
return false;
|
|
6369
|
-
}
|
|
6370
|
-
if (verticalOverflow === "hidden" || verticalOverflow === "clip") {
|
|
6371
|
-
return includeHidden;
|
|
6372
|
-
}
|
|
6373
|
-
const overflow = getStyle(element, "overflow");
|
|
6374
|
-
if (overflow === "visible") {
|
|
6375
|
-
// browser returns "visible" on documentElement even if it is scrollable
|
|
6376
|
-
if (isDocumentElement(element)) {
|
|
6377
|
-
return true;
|
|
6378
|
-
}
|
|
6379
|
-
return false;
|
|
6380
|
-
}
|
|
6381
|
-
if (overflow === "hidden" || overflow === "clip") {
|
|
6382
|
-
return includeHidden;
|
|
6383
|
-
}
|
|
6384
|
-
return true; // "auto", "scroll"
|
|
6385
|
-
};
|
|
6386
|
-
const canHaveHorizontalScroll = (element, { includeHidden }) => {
|
|
6387
|
-
const horizontalOverflow = getStyle(element, "overflow-x");
|
|
6388
|
-
if (horizontalOverflow === "visible") {
|
|
6389
|
-
// browser returns "visible" on documentElement even if it is scrollable
|
|
6390
|
-
if (isDocumentElement(element)) {
|
|
6391
|
-
return true;
|
|
6392
|
-
}
|
|
6393
|
-
return false;
|
|
6394
|
-
}
|
|
6395
|
-
if (horizontalOverflow === "hidden" || horizontalOverflow === "clip") {
|
|
6396
|
-
return includeHidden;
|
|
6397
|
-
}
|
|
6398
|
-
const overflow = getStyle(element, "overflow");
|
|
6399
|
-
if (overflow === "visible") {
|
|
6400
|
-
if (isDocumentElement(element)) {
|
|
6401
|
-
// browser returns "visible" on documentElement even if it is scrollable
|
|
6402
|
-
return true;
|
|
6403
|
-
}
|
|
6404
|
-
return false;
|
|
6405
|
-
}
|
|
6406
|
-
if (overflow === "hidden" || overflow === "clip") {
|
|
6407
|
-
return includeHidden;
|
|
6408
|
-
}
|
|
6409
|
-
return true; // "auto", "scroll"
|
|
6410
|
-
};
|
|
6411
|
-
|
|
6412
|
-
const getScrollingElement = (document) => {
|
|
6413
|
-
const { scrollingElement } = document;
|
|
6414
|
-
if (scrollingElement) {
|
|
6415
|
-
return scrollingElement;
|
|
6416
|
-
}
|
|
6417
|
-
|
|
6418
|
-
if (isCompliant(document)) {
|
|
6419
|
-
return document.documentElement;
|
|
6420
|
-
}
|
|
6421
|
-
|
|
6422
|
-
const body = document.body;
|
|
6423
|
-
const isFrameset = body && !/body/i.test(body.tagName);
|
|
6424
|
-
const possiblyScrollingElement = isFrameset ? getNextBodyElement(body) : body;
|
|
6425
|
-
|
|
6426
|
-
// If `body` is itself scrollable, it is not the `scrollingElement`.
|
|
6427
|
-
return possiblyScrollingElement && bodyIsScrollable(possiblyScrollingElement)
|
|
6428
|
-
? null
|
|
6429
|
-
: possiblyScrollingElement;
|
|
6430
|
-
};
|
|
6431
|
-
|
|
6432
|
-
const isHidden = (element) => {
|
|
6433
|
-
const display = getStyle(element, "display");
|
|
6434
|
-
if (display === "none") {
|
|
6435
|
-
return false;
|
|
6436
|
-
}
|
|
6437
|
-
|
|
6438
|
-
if (
|
|
6439
|
-
display === "table-row" ||
|
|
6440
|
-
display === "table-group" ||
|
|
6441
|
-
display === "table-column"
|
|
6442
|
-
) {
|
|
6443
|
-
return getStyle(element, "visibility") !== "collapsed";
|
|
6444
|
-
}
|
|
6445
|
-
|
|
6446
|
-
return true;
|
|
6447
|
-
};
|
|
6448
|
-
const isCompliant = (document) => {
|
|
6449
|
-
// Note: document.compatMode can be toggle at runtime by document.write
|
|
6450
|
-
const isStandardsMode = /^CSS1/.test(document.compatMode);
|
|
6451
|
-
if (isStandardsMode) {
|
|
6452
|
-
return testScrollCompliance(document);
|
|
6453
|
-
}
|
|
6454
|
-
return false;
|
|
6455
|
-
};
|
|
6456
|
-
const testScrollCompliance = (document) => {
|
|
6457
|
-
const iframe = document.createElement("iframe");
|
|
6458
|
-
iframe.style.height = "1px";
|
|
6459
|
-
const parentNode = document.body || document.documentElement || document;
|
|
6460
|
-
parentNode.appendChild(iframe);
|
|
6461
|
-
const iframeDocument = iframe.contentWindow.document;
|
|
6462
|
-
iframeDocument.write('<!DOCTYPE html><div style="height:9999em">x</div>');
|
|
6463
|
-
iframeDocument.close();
|
|
6464
|
-
const scrollComplianceResult =
|
|
6465
|
-
iframeDocument.documentElement.scrollHeight >
|
|
6466
|
-
iframeDocument.body.scrollHeight;
|
|
6467
|
-
iframe.parentNode.removeChild(iframe);
|
|
6468
|
-
return scrollComplianceResult;
|
|
6469
|
-
};
|
|
6470
|
-
const getNextBodyElement = (frameset) => {
|
|
6471
|
-
// We use this function to be correct per spec in case `document.body` is
|
|
6472
|
-
// a `frameset` but there exists a later `body`. Since `document.body` is
|
|
6473
|
-
// a `frameset`, we know the root is an `html`, and there was no `body`
|
|
6474
|
-
// before the `frameset`, so we just need to look at siblings after the
|
|
6475
|
-
// `frameset`.
|
|
6476
|
-
let current = frameset;
|
|
6477
|
-
while ((current = current.nextSibling)) {
|
|
6478
|
-
if (current.nodeType === 1 && isBodyElement(current)) {
|
|
6479
|
-
return current;
|
|
6480
|
-
}
|
|
6481
|
-
}
|
|
6482
|
-
return null;
|
|
6483
|
-
};
|
|
6484
|
-
const isBodyElement = (element) => element.ownerDocument.body === element;
|
|
6485
|
-
const bodyIsScrollable = (body) => {
|
|
6486
|
-
// a body element is scrollable if body and html are scrollable and rendered
|
|
6487
|
-
if (!isScrollable(body)) {
|
|
6488
|
-
return false;
|
|
6489
|
-
}
|
|
6490
|
-
if (isHidden(body)) {
|
|
6491
|
-
return false;
|
|
6492
|
-
}
|
|
6493
|
-
|
|
6494
|
-
const documentElement = body.ownerDocument.documentElement;
|
|
6495
|
-
if (!isScrollable(documentElement)) {
|
|
6496
|
-
return false;
|
|
6497
|
-
}
|
|
6498
|
-
if (isHidden(documentElement)) {
|
|
6499
|
-
return false;
|
|
6500
|
-
}
|
|
6501
|
-
|
|
6502
|
-
return true;
|
|
6503
|
-
};
|
|
6504
|
-
|
|
6505
6526
|
// https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container
|
|
6506
6527
|
|
|
6507
6528
|
|
|
@@ -7008,24 +7029,45 @@ const getScrollport = (scrollBox, scrollContainer) => {
|
|
|
7008
7029
|
};
|
|
7009
7030
|
};
|
|
7010
7031
|
|
|
7011
|
-
|
|
7032
|
+
/**
|
|
7033
|
+
* Returns [verticalScrollbarWidth, horizontalScrollbarHeight] as currently
|
|
7034
|
+
* rendered by `scrollableElement`, in px. Returns zeros when the element has no
|
|
7035
|
+
* classic scrollbar: no overflow, overlay scrollbars, `scrollbar-width: none`,
|
|
7036
|
+
* or a `::-webkit-scrollbar { display: none }` rule.
|
|
7037
|
+
*
|
|
7038
|
+
* The measurement is taken on the element itself (its own border box versus its
|
|
7039
|
+
* own content box) rather than on a probe node appended inside it. A probe
|
|
7040
|
+
* cannot answer this question: `scrollbar-width` and `::-webkit-scrollbar` are
|
|
7041
|
+
* not inherited, so a probe reports the platform default scrollbar size even
|
|
7042
|
+
* when the element renders no scrollbar at all.
|
|
7043
|
+
*/
|
|
7012
7044
|
const measureScrollbar = (scrollableElement) => {
|
|
7013
|
-
|
|
7014
|
-
scrollableElement
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
scrollableElement
|
|
7045
|
+
if (
|
|
7046
|
+
scrollableElement === document.documentElement ||
|
|
7047
|
+
scrollableElement === document.scrollingElement
|
|
7048
|
+
) {
|
|
7049
|
+
// documentElement.clientWidth/Height report the viewport minus its
|
|
7050
|
+
// scrollbars, not this element's own box (which `max-width` can shrink), so
|
|
7051
|
+
// the border box to compare against is the window itself.
|
|
7052
|
+
return [
|
|
7053
|
+
snapToPixel(window.innerWidth - document.documentElement.clientWidth),
|
|
7054
|
+
snapToPixel(window.innerHeight - document.documentElement.clientHeight),
|
|
7055
|
+
];
|
|
7056
|
+
}
|
|
7057
|
+
const { left, right, top, bottom } = getBorderSizes(scrollableElement);
|
|
7058
|
+
const scrollbarWidth =
|
|
7059
|
+
scrollableElement.offsetWidth -
|
|
7060
|
+
scrollableElement.clientWidth -
|
|
7061
|
+
left -
|
|
7062
|
+
right;
|
|
7063
|
+
const scrollbarHeight =
|
|
7064
|
+
scrollableElement.offsetHeight -
|
|
7065
|
+
scrollableElement.clientHeight -
|
|
7066
|
+
top -
|
|
7067
|
+
bottom;
|
|
7026
7068
|
return [
|
|
7027
|
-
|
|
7028
|
-
|
|
7069
|
+
scrollbarWidth > 0 ? snapToPixel(scrollbarWidth) : 0,
|
|
7070
|
+
scrollbarHeight > 0 ? snapToPixel(scrollbarHeight) : 0,
|
|
7029
7071
|
];
|
|
7030
7072
|
};
|
|
7031
7073
|
|
|
@@ -16813,4 +16855,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
|
|
|
16813
16855
|
};
|
|
16814
16856
|
};
|
|
16815
16857
|
|
|
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 };
|
|
16858
|
+
export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, 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 };
|