@jsenv/dom 0.4.0 → 0.5.0

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 CHANGED
@@ -100,6 +100,38 @@ const createPubSub = (clearOnPublish = false) => {
100
100
  return [publish, subscribe, clear];
101
101
  };
102
102
 
103
+ const createValueEffect = (value) => {
104
+ const callbackSet = new Set();
105
+ const previousValueCleanupSet = new Set();
106
+
107
+ const updateValue = (newValue) => {
108
+ if (newValue === value) {
109
+ return;
110
+ }
111
+ for (const cleanup of previousValueCleanupSet) {
112
+ cleanup();
113
+ }
114
+ previousValueCleanupSet.clear();
115
+ const oldValue = value;
116
+ value = newValue;
117
+ for (const callback of callbackSet) {
118
+ const returnValue = callback(newValue, oldValue);
119
+ if (typeof returnValue === "function") {
120
+ previousValueCleanupSet.add(returnValue);
121
+ }
122
+ }
123
+ };
124
+
125
+ const addEffect = (callback) => {
126
+ callbackSet.add(callback);
127
+ return () => {
128
+ callbackSet.delete(callback);
129
+ };
130
+ };
131
+
132
+ return [updateValue, addEffect];
133
+ };
134
+
103
135
  // https://github.com/davidtheclark/tabbable/blob/master/index.js
104
136
  const isDocumentElement = (node) =>
105
137
  node === node.ownerDocument.documentElement;
@@ -2057,12 +2089,21 @@ const addActiveElementEffect = (callback) => {
2057
2089
  return remove;
2058
2090
  };
2059
2091
 
2060
- const elementIsVisible = (node) => {
2092
+ const elementIsVisibleForFocus = (node) => {
2093
+ return getFocusVisibilityInfo(node).visible;
2094
+ };
2095
+ const getFocusVisibilityInfo = (node) => {
2061
2096
  if (isDocumentElement(node)) {
2062
- return true;
2097
+ return { visible: true, reason: "is document" };
2098
+ }
2099
+ if (node.hasAttribute("hidden")) {
2100
+ return { visible: false, reason: "has hidden attribute" };
2063
2101
  }
2064
2102
  if (getStyle(node, "visibility") === "hidden") {
2065
- return false;
2103
+ return { visible: false, reason: "uses visiblity: hidden" };
2104
+ }
2105
+ if (node.tagName === "INPUT" && node.type === "hidden") {
2106
+ return { visible: false, reason: "input type hidden" };
2066
2107
  }
2067
2108
  let nodeOrAncestor = node;
2068
2109
  while (nodeOrAncestor) {
@@ -2070,19 +2111,87 @@ const elementIsVisible = (node) => {
2070
2111
  break;
2071
2112
  }
2072
2113
  if (getStyle(nodeOrAncestor, "display") === "none") {
2073
- return false;
2114
+ return { visible: false, reason: "ancestor uses display: none" };
2074
2115
  }
2075
2116
  // Check if element is inside a closed details element
2076
2117
  if (elementIsDetails(nodeOrAncestor) && !nodeOrAncestor.open) {
2077
2118
  // Special case: summary elements are visible even when their parent details is closed
2078
2119
  // But only if this details element is the direct parent of the summary
2079
- if (elementIsSummary(node) && node.parentElement === nodeOrAncestor) ; else {
2080
- return false;
2120
+ if (!elementIsSummary(node) || node.parentElement !== nodeOrAncestor) {
2121
+ return { visible: false, reason: "inside closed details element" };
2081
2122
  }
2123
+ // Continue checking ancestors
2082
2124
  }
2083
2125
  nodeOrAncestor = nodeOrAncestor.parentNode;
2084
2126
  }
2085
- return true;
2127
+ return { visible: true, reason: "no reason to be hidden" };
2128
+ };
2129
+
2130
+ const elementIsVisuallyVisible = (node, options = {}) => {
2131
+ return getVisuallyVisibleInfo(node, options).visible;
2132
+ };
2133
+ const getVisuallyVisibleInfo = (
2134
+ node,
2135
+ { countOffscreenAsVisible = false } = {},
2136
+ ) => {
2137
+ // First check all the focusable visibility conditions
2138
+ const focusVisibilityInfo = getFocusVisibilityInfo(node);
2139
+ if (!focusVisibilityInfo.visible) {
2140
+ return focusVisibilityInfo;
2141
+ }
2142
+
2143
+ // Additional visual visibility checks
2144
+ if (getStyle(node, "opacity") === "0") {
2145
+ return { visible: false, reason: "uses opacity: 0" };
2146
+ }
2147
+
2148
+ const rect = node.getBoundingClientRect();
2149
+ if (rect.width === 0 && rect.height === 0) {
2150
+ return { visible: false, reason: "has zero dimensions" };
2151
+ }
2152
+
2153
+ // Check for clipping
2154
+ const clipStyle = getStyle(node, "clip");
2155
+ if (clipStyle && clipStyle !== "auto" && clipStyle.includes("rect(0")) {
2156
+ return { visible: false, reason: "clipped with clip property" };
2157
+ }
2158
+
2159
+ const clipPathStyle = getStyle(node, "clip-path");
2160
+ if (clipPathStyle && clipPathStyle.includes("inset(100%")) {
2161
+ return { visible: false, reason: "clipped with clip-path" };
2162
+ }
2163
+
2164
+ // Check if positioned off-screen (unless option says to count as visible)
2165
+ if (!countOffscreenAsVisible) {
2166
+ if (
2167
+ rect.right < 0 ||
2168
+ rect.bottom < 0 ||
2169
+ rect.left > window.innerWidth ||
2170
+ rect.top > window.innerHeight
2171
+ ) {
2172
+ return { visible: false, reason: "positioned off-screen" };
2173
+ }
2174
+ }
2175
+
2176
+ // Check for transform scale(0)
2177
+ const transformStyle = getStyle(node, "transform");
2178
+ if (transformStyle && transformStyle.includes("scale(0")) {
2179
+ return { visible: false, reason: "scaled to zero with transform" };
2180
+ }
2181
+
2182
+ return { visible: true, reason: "visually visible" };
2183
+ };
2184
+ const getFirstVisuallyVisibleAncestor = (node, options = {}) => {
2185
+ let ancestorCandidate = node.parentNode;
2186
+ while (ancestorCandidate) {
2187
+ const visibilityInfo = getVisuallyVisibleInfo(ancestorCandidate, options);
2188
+ if (visibilityInfo.visible) {
2189
+ return ancestorCandidate;
2190
+ }
2191
+ ancestorCandidate = ancestorCandidate.parentElement;
2192
+ }
2193
+ // This shouldn't happen in normal cases since document element is always visible
2194
+ return null;
2086
2195
  };
2087
2196
 
2088
2197
  const elementIsFocusable = (node) => {
@@ -2098,34 +2207,34 @@ const elementIsFocusable = (node) => {
2098
2207
  if (node.type === "hidden") {
2099
2208
  return false;
2100
2209
  }
2101
- return elementIsVisible(node);
2210
+ return elementIsVisibleForFocus(node);
2102
2211
  }
2103
2212
  if (
2104
2213
  ["button", "select", "datalist", "iframe", "textarea"].indexOf(nodeName) >
2105
2214
  -1
2106
2215
  ) {
2107
- return elementIsVisible(node);
2216
+ return elementIsVisibleForFocus(node);
2108
2217
  }
2109
2218
  if (["a", "area"].indexOf(nodeName) > -1) {
2110
2219
  if (node.hasAttribute("href") === false) {
2111
2220
  return false;
2112
2221
  }
2113
- return elementIsVisible(node);
2222
+ return elementIsVisibleForFocus(node);
2114
2223
  }
2115
2224
  if (["audio", "video"].indexOf(nodeName) > -1) {
2116
2225
  if (node.hasAttribute("controls") === false) {
2117
2226
  return false;
2118
2227
  }
2119
- return elementIsVisible(node);
2228
+ return elementIsVisibleForFocus(node);
2120
2229
  }
2121
2230
  if (nodeName === "summary") {
2122
- return elementIsVisible(node);
2231
+ return elementIsVisibleForFocus(node);
2123
2232
  }
2124
2233
  if (node.hasAttribute("tabindex") || node.hasAttribute("tabIndex")) {
2125
- return elementIsVisible(node);
2234
+ return elementIsVisibleForFocus(node);
2126
2235
  }
2127
2236
  if (node.hasAttribute("draggable")) {
2128
- return elementIsVisible(node);
2237
+ return elementIsVisibleForFocus(node);
2129
2238
  }
2130
2239
  return false;
2131
2240
  };
@@ -3558,11 +3667,12 @@ const allowWheelThrough = (element, connectedElement) => {
3558
3667
  wheelEvent.clientY,
3559
3668
  );
3560
3669
  for (const elementBehindMouse of elementsBehindMouse) {
3561
- const belongsToElement = isElementOrDescendant(elementBehindMouse);
3562
3670
  // try to scroll element itself
3563
3671
  if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
3564
3672
  return;
3565
3673
  }
3674
+ const belongsToElement = isElementOrDescendant(elementBehindMouse);
3675
+ // try to scroll what is behind
3566
3676
  if (!belongsToElement) {
3567
3677
  break;
3568
3678
  }
@@ -3581,17 +3691,16 @@ const allowWheelThrough = (element, connectedElement) => {
3581
3691
  wheelEvent.clientY,
3582
3692
  );
3583
3693
  for (const elementBehindMouse of elementsBehindMouse) {
3584
- const belongsToElement = isElementOrDescendant(elementBehindMouse);
3585
3694
  // try to scroll element itself
3586
3695
  if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
3587
3696
  return;
3588
3697
  }
3698
+ const belongsToElement = isElementOrDescendant(elementBehindMouse);
3589
3699
  if (belongsToElement) {
3590
- // the element is not scrollable and we don't care about his
3591
- // scrollable parent (because we know it's going to be the document)
3592
- // we search for scrollable container that might be behind it
3700
+ // keep searching if something in our element is scrollable
3593
3701
  continue;
3594
3702
  }
3703
+ // our element is not scrollable, try to scroll the container behind the mouse
3595
3704
  const scrollContainer = getScrollContainer(elementBehindMouse);
3596
3705
  if (tryToScrollOne(scrollContainer, wheelEvent)) {
3597
3706
  return;
@@ -11802,4 +11911,4 @@ const crossFade = {
11802
11911
  },
11803
11912
  };
11804
11913
 
11805
- export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
11914
+ export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // state management
2
2
  export { createIterableWeakSet } from "./src/iterable_weak_set.js";
3
3
  export { createPubSub } from "./src/pub_sub.js";
4
+ export { createValueEffect } from "./src/value_effect.js";
4
5
 
5
6
  // style
6
7
  export { addWillChange, getStyle, setStyles } from "./src/style/dom_styles.js";
@@ -37,7 +38,13 @@ export {
37
38
  useActiveElement,
38
39
  } from "./src/interaction/focus/active_element.js";
39
40
  export { elementIsFocusable } from "./src/interaction/focus/element_is_focusable.js";
40
- export { elementIsVisible } from "./src/interaction/focus/element_is_visible.js";
41
+ export {
42
+ elementIsVisibleForFocus,
43
+ elementIsVisuallyVisible,
44
+ getFirstVisuallyVisibleAncestor,
45
+ getFocusVisibilityInfo,
46
+ getVisuallyVisibleInfo,
47
+ } from "./src/interaction/focus/element_visibility.js";
41
48
  export { findFocusable } from "./src/interaction/focus/find_focusable.js";
42
49
  export { initFocusGroup } from "./src/interaction/focus/focus_group.js";
43
50
  export { preventFocusNavViaKeyboard } from "./src/interaction/focus/focus_nav.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "DOM utilities for writing frontend code",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,4 +1,4 @@
1
- import { elementIsVisible } from "./element_is_visible.js";
1
+ import { elementIsVisibleForFocus } from "./element_visibility.js";
2
2
 
3
3
  export const elementIsFocusable = (node) => {
4
4
  // only element node can be focused, document, textNodes etc cannot
@@ -13,34 +13,34 @@ export const elementIsFocusable = (node) => {
13
13
  if (node.type === "hidden") {
14
14
  return false;
15
15
  }
16
- return elementIsVisible(node);
16
+ return elementIsVisibleForFocus(node);
17
17
  }
18
18
  if (
19
19
  ["button", "select", "datalist", "iframe", "textarea"].indexOf(nodeName) >
20
20
  -1
21
21
  ) {
22
- return elementIsVisible(node);
22
+ return elementIsVisibleForFocus(node);
23
23
  }
24
24
  if (["a", "area"].indexOf(nodeName) > -1) {
25
25
  if (node.hasAttribute("href") === false) {
26
26
  return false;
27
27
  }
28
- return elementIsVisible(node);
28
+ return elementIsVisibleForFocus(node);
29
29
  }
30
30
  if (["audio", "video"].indexOf(nodeName) > -1) {
31
31
  if (node.hasAttribute("controls") === false) {
32
32
  return false;
33
33
  }
34
- return elementIsVisible(node);
34
+ return elementIsVisibleForFocus(node);
35
35
  }
36
36
  if (nodeName === "summary") {
37
- return elementIsVisible(node);
37
+ return elementIsVisibleForFocus(node);
38
38
  }
39
39
  if (node.hasAttribute("tabindex") || node.hasAttribute("tabIndex")) {
40
- return elementIsVisible(node);
40
+ return elementIsVisibleForFocus(node);
41
41
  }
42
42
  if (node.hasAttribute("draggable")) {
43
- return elementIsVisible(node);
43
+ return elementIsVisibleForFocus(node);
44
44
  }
45
45
  return false;
46
46
  };
@@ -0,0 +1,111 @@
1
+ import { getStyle } from "../../style/dom_styles.js";
2
+ import {
3
+ elementIsDetails,
4
+ elementIsSummary,
5
+ isDocumentElement,
6
+ } from "../../utils.js";
7
+
8
+ export const elementIsVisibleForFocus = (node) => {
9
+ return getFocusVisibilityInfo(node).visible;
10
+ };
11
+ export const getFocusVisibilityInfo = (node) => {
12
+ if (isDocumentElement(node)) {
13
+ return { visible: true, reason: "is document" };
14
+ }
15
+ if (node.hasAttribute("hidden")) {
16
+ return { visible: false, reason: "has hidden attribute" };
17
+ }
18
+ if (getStyle(node, "visibility") === "hidden") {
19
+ return { visible: false, reason: "uses visiblity: hidden" };
20
+ }
21
+ if (node.tagName === "INPUT" && node.type === "hidden") {
22
+ return { visible: false, reason: "input type hidden" };
23
+ }
24
+ let nodeOrAncestor = node;
25
+ while (nodeOrAncestor) {
26
+ if (isDocumentElement(nodeOrAncestor)) {
27
+ break;
28
+ }
29
+ if (getStyle(nodeOrAncestor, "display") === "none") {
30
+ return { visible: false, reason: "ancestor uses display: none" };
31
+ }
32
+ // Check if element is inside a closed details element
33
+ if (elementIsDetails(nodeOrAncestor) && !nodeOrAncestor.open) {
34
+ // Special case: summary elements are visible even when their parent details is closed
35
+ // But only if this details element is the direct parent of the summary
36
+ if (!elementIsSummary(node) || node.parentElement !== nodeOrAncestor) {
37
+ return { visible: false, reason: "inside closed details element" };
38
+ }
39
+ // Continue checking ancestors
40
+ }
41
+ nodeOrAncestor = nodeOrAncestor.parentNode;
42
+ }
43
+ return { visible: true, reason: "no reason to be hidden" };
44
+ };
45
+
46
+ export const elementIsVisuallyVisible = (node, options = {}) => {
47
+ return getVisuallyVisibleInfo(node, options).visible;
48
+ };
49
+ export const getVisuallyVisibleInfo = (
50
+ node,
51
+ { countOffscreenAsVisible = false } = {},
52
+ ) => {
53
+ // First check all the focusable visibility conditions
54
+ const focusVisibilityInfo = getFocusVisibilityInfo(node);
55
+ if (!focusVisibilityInfo.visible) {
56
+ return focusVisibilityInfo;
57
+ }
58
+
59
+ // Additional visual visibility checks
60
+ if (getStyle(node, "opacity") === "0") {
61
+ return { visible: false, reason: "uses opacity: 0" };
62
+ }
63
+
64
+ const rect = node.getBoundingClientRect();
65
+ if (rect.width === 0 && rect.height === 0) {
66
+ return { visible: false, reason: "has zero dimensions" };
67
+ }
68
+
69
+ // Check for clipping
70
+ const clipStyle = getStyle(node, "clip");
71
+ if (clipStyle && clipStyle !== "auto" && clipStyle.includes("rect(0")) {
72
+ return { visible: false, reason: "clipped with clip property" };
73
+ }
74
+
75
+ const clipPathStyle = getStyle(node, "clip-path");
76
+ if (clipPathStyle && clipPathStyle.includes("inset(100%")) {
77
+ return { visible: false, reason: "clipped with clip-path" };
78
+ }
79
+
80
+ // Check if positioned off-screen (unless option says to count as visible)
81
+ if (!countOffscreenAsVisible) {
82
+ if (
83
+ rect.right < 0 ||
84
+ rect.bottom < 0 ||
85
+ rect.left > window.innerWidth ||
86
+ rect.top > window.innerHeight
87
+ ) {
88
+ return { visible: false, reason: "positioned off-screen" };
89
+ }
90
+ }
91
+
92
+ // Check for transform scale(0)
93
+ const transformStyle = getStyle(node, "transform");
94
+ if (transformStyle && transformStyle.includes("scale(0")) {
95
+ return { visible: false, reason: "scaled to zero with transform" };
96
+ }
97
+
98
+ return { visible: true, reason: "visually visible" };
99
+ };
100
+ export const getFirstVisuallyVisibleAncestor = (node, options = {}) => {
101
+ let ancestorCandidate = node.parentNode;
102
+ while (ancestorCandidate) {
103
+ const visibilityInfo = getVisuallyVisibleInfo(ancestorCandidate, options);
104
+ if (visibilityInfo.visible) {
105
+ return ancestorCandidate;
106
+ }
107
+ ancestorCandidate = ancestorCandidate.parentElement;
108
+ }
109
+ // This shouldn't happen in normal cases since document element is always visible
110
+ return null;
111
+ };
@@ -56,11 +56,12 @@ export const allowWheelThrough = (element, connectedElement) => {
56
56
  wheelEvent.clientY,
57
57
  );
58
58
  for (const elementBehindMouse of elementsBehindMouse) {
59
- const belongsToElement = isElementOrDescendant(elementBehindMouse);
60
59
  // try to scroll element itself
61
60
  if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
62
61
  return;
63
62
  }
63
+ const belongsToElement = isElementOrDescendant(elementBehindMouse);
64
+ // try to scroll what is behind
64
65
  if (!belongsToElement) {
65
66
  break;
66
67
  }
@@ -79,17 +80,16 @@ export const allowWheelThrough = (element, connectedElement) => {
79
80
  wheelEvent.clientY,
80
81
  );
81
82
  for (const elementBehindMouse of elementsBehindMouse) {
82
- const belongsToElement = isElementOrDescendant(elementBehindMouse);
83
83
  // try to scroll element itself
84
84
  if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
85
85
  return;
86
86
  }
87
+ const belongsToElement = isElementOrDescendant(elementBehindMouse);
87
88
  if (belongsToElement) {
88
- // the element is not scrollable and we don't care about his
89
- // scrollable parent (because we know it's going to be the document)
90
- // we search for scrollable container that might be behind it
89
+ // keep searching if something in our element is scrollable
91
90
  continue;
92
91
  }
92
+ // our element is not scrollable, try to scroll the container behind the mouse
93
93
  const scrollContainer = getScrollContainer(elementBehindMouse);
94
94
  if (tryToScrollOne(scrollContainer, wheelEvent)) {
95
95
  return;
@@ -0,0 +1,31 @@
1
+ export const createValueEffect = (value) => {
2
+ const callbackSet = new Set();
3
+ const previousValueCleanupSet = new Set();
4
+
5
+ const updateValue = (newValue) => {
6
+ if (newValue === value) {
7
+ return;
8
+ }
9
+ for (const cleanup of previousValueCleanupSet) {
10
+ cleanup();
11
+ }
12
+ previousValueCleanupSet.clear();
13
+ const oldValue = value;
14
+ value = newValue;
15
+ for (const callback of callbackSet) {
16
+ const returnValue = callback(newValue, oldValue);
17
+ if (typeof returnValue === "function") {
18
+ previousValueCleanupSet.add(returnValue);
19
+ }
20
+ }
21
+ };
22
+
23
+ const addEffect = (callback) => {
24
+ callbackSet.add(callback);
25
+ return () => {
26
+ callbackSet.delete(callback);
27
+ };
28
+ };
29
+
30
+ return [updateValue, addEffect];
31
+ };
@@ -1,36 +0,0 @@
1
- import { getStyle } from "../../style/dom_styles.js";
2
- import {
3
- elementIsDetails,
4
- elementIsSummary,
5
- isDocumentElement,
6
- } from "../../utils.js";
7
-
8
- export const elementIsVisible = (node) => {
9
- if (isDocumentElement(node)) {
10
- return true;
11
- }
12
- if (getStyle(node, "visibility") === "hidden") {
13
- return false;
14
- }
15
- let nodeOrAncestor = node;
16
- while (nodeOrAncestor) {
17
- if (isDocumentElement(nodeOrAncestor)) {
18
- break;
19
- }
20
- if (getStyle(nodeOrAncestor, "display") === "none") {
21
- return false;
22
- }
23
- // Check if element is inside a closed details element
24
- if (elementIsDetails(nodeOrAncestor) && !nodeOrAncestor.open) {
25
- // Special case: summary elements are visible even when their parent details is closed
26
- // But only if this details element is the direct parent of the summary
27
- if (elementIsSummary(node) && node.parentElement === nodeOrAncestor) {
28
- // Continue checking ancestors, don't return false yet
29
- } else {
30
- return false;
31
- }
32
- }
33
- nodeOrAncestor = nodeOrAncestor.parentNode;
34
- }
35
- return true;
36
- };