accented 0.0.0-20250424114613 → 0.0.0-20250701143712

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.
Files changed (207) hide show
  1. package/README.md +44 -193
  2. package/dist/accented.d.ts +7 -7
  3. package/dist/accented.d.ts.map +1 -1
  4. package/dist/accented.js +30 -27
  5. package/dist/accented.js.map +1 -1
  6. package/dist/common/tokens.d.ts +2 -0
  7. package/dist/common/tokens.d.ts.map +1 -0
  8. package/dist/common/tokens.js +2 -0
  9. package/dist/common/tokens.js.map +1 -0
  10. package/dist/constants.d.ts +1 -1
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/constants.js.map +1 -1
  14. package/dist/dom-updater.d.ts +1 -1
  15. package/dist/dom-updater.d.ts.map +1 -1
  16. package/dist/dom-updater.js +14 -13
  17. package/dist/dom-updater.js.map +1 -1
  18. package/dist/elements/accented-dialog.d.ts +2 -3
  19. package/dist/elements/accented-dialog.d.ts.map +1 -1
  20. package/dist/elements/accented-dialog.js +14 -8
  21. package/dist/elements/accented-dialog.js.map +1 -1
  22. package/dist/elements/accented-trigger.d.ts +3 -4
  23. package/dist/elements/accented-trigger.d.ts.map +1 -1
  24. package/dist/elements/accented-trigger.js +10 -10
  25. package/dist/elements/accented-trigger.js.map +1 -1
  26. package/dist/fullscreen-listener.d.ts +1 -1
  27. package/dist/fullscreen-listener.d.ts.map +1 -1
  28. package/dist/fullscreen-listener.js +3 -4
  29. package/dist/fullscreen-listener.js.map +1 -1
  30. package/dist/intersection-observer.d.ts +1 -1
  31. package/dist/intersection-observer.d.ts.map +1 -1
  32. package/dist/intersection-observer.js +12 -6
  33. package/dist/intersection-observer.js.map +1 -1
  34. package/dist/log-and-rethrow.d.ts +1 -1
  35. package/dist/log-and-rethrow.d.ts.map +1 -1
  36. package/dist/log-and-rethrow.js +2 -3
  37. package/dist/log-and-rethrow.js.map +1 -1
  38. package/dist/logger.d.ts +1 -1
  39. package/dist/logger.d.ts.map +1 -1
  40. package/dist/logger.js +2 -2
  41. package/dist/logger.js.map +1 -1
  42. package/dist/register-elements.d.ts +1 -1
  43. package/dist/register-elements.d.ts.map +1 -1
  44. package/dist/register-elements.js +6 -7
  45. package/dist/register-elements.js.map +1 -1
  46. package/dist/resize-listener.d.ts +1 -1
  47. package/dist/resize-listener.d.ts.map +1 -1
  48. package/dist/resize-listener.js +3 -4
  49. package/dist/resize-listener.js.map +1 -1
  50. package/dist/scanner.d.ts +2 -2
  51. package/dist/scanner.d.ts.map +1 -1
  52. package/dist/scanner.js +36 -36
  53. package/dist/scanner.js.map +1 -1
  54. package/dist/scroll-listeners.d.ts +1 -1
  55. package/dist/scroll-listeners.d.ts.map +1 -1
  56. package/dist/scroll-listeners.js +3 -4
  57. package/dist/scroll-listeners.js.map +1 -1
  58. package/dist/state.d.ts +1 -1
  59. package/dist/state.d.ts.map +1 -1
  60. package/dist/state.js +4 -5
  61. package/dist/state.js.map +1 -1
  62. package/dist/task-queue.d.ts +2 -2
  63. package/dist/task-queue.d.ts.map +1 -1
  64. package/dist/task-queue.js +1 -1
  65. package/dist/task-queue.js.map +1 -1
  66. package/dist/types.d.ts +102 -46
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js.map +1 -1
  69. package/dist/utils/are-elements-with-issues-equal.d.ts +2 -2
  70. package/dist/utils/are-elements-with-issues-equal.d.ts.map +1 -1
  71. package/dist/utils/are-elements-with-issues-equal.js +3 -3
  72. package/dist/utils/are-elements-with-issues-equal.js.map +1 -1
  73. package/dist/utils/are-issue-sets-equal.d.ts +2 -2
  74. package/dist/utils/are-issue-sets-equal.d.ts.map +1 -1
  75. package/dist/utils/are-issue-sets-equal.js +3 -3
  76. package/dist/utils/are-issue-sets-equal.js.map +1 -1
  77. package/dist/utils/containing-blocks.d.ts.map +1 -1
  78. package/dist/utils/containing-blocks.js +1 -1
  79. package/dist/utils/containing-blocks.js.map +1 -1
  80. package/dist/utils/contains.d.ts +1 -1
  81. package/dist/utils/contains.d.ts.map +1 -1
  82. package/dist/utils/contains.js +1 -1
  83. package/dist/utils/contains.js.map +1 -1
  84. package/dist/utils/deduplicate-nodes.js +0 -1
  85. package/dist/utils/deduplicate-nodes.js.map +1 -1
  86. package/dist/utils/deep-merge.d.ts +1 -1
  87. package/dist/utils/deep-merge.d.ts.map +1 -1
  88. package/dist/utils/deep-merge.js +8 -5
  89. package/dist/utils/deep-merge.js.map +1 -1
  90. package/dist/utils/dom-helpers.d.ts.map +1 -1
  91. package/dist/utils/dom-helpers.js +4 -2
  92. package/dist/utils/dom-helpers.js.map +1 -1
  93. package/dist/utils/ensure-non-empty.d.ts +1 -1
  94. package/dist/utils/ensure-non-empty.d.ts.map +1 -1
  95. package/dist/utils/ensure-non-empty.js +2 -2
  96. package/dist/utils/ensure-non-empty.js.map +1 -1
  97. package/dist/utils/get-element-html.d.ts +1 -1
  98. package/dist/utils/get-element-html.d.ts.map +1 -1
  99. package/dist/utils/get-element-html.js +4 -2
  100. package/dist/utils/get-element-html.js.map +1 -1
  101. package/dist/utils/get-element-position.d.ts +2 -2
  102. package/dist/utils/get-element-position.d.ts.map +1 -1
  103. package/dist/utils/get-element-position.js +21 -25
  104. package/dist/utils/get-element-position.js.map +1 -1
  105. package/dist/utils/get-parent.d.ts +1 -1
  106. package/dist/utils/get-parent.d.ts.map +1 -1
  107. package/dist/utils/get-parent.js +1 -1
  108. package/dist/utils/get-parent.js.map +1 -1
  109. package/dist/utils/get-scan-context.d.ts +2 -2
  110. package/dist/utils/get-scan-context.d.ts.map +1 -1
  111. package/dist/utils/get-scan-context.js +9 -9
  112. package/dist/utils/get-scan-context.js.map +1 -1
  113. package/dist/utils/get-scrollable-ancestors.d.ts +1 -1
  114. package/dist/utils/get-scrollable-ancestors.d.ts.map +1 -1
  115. package/dist/utils/get-scrollable-ancestors.js +5 -5
  116. package/dist/utils/get-scrollable-ancestors.js.map +1 -1
  117. package/dist/utils/is-node-in-scan-context.d.ts +2 -2
  118. package/dist/utils/is-node-in-scan-context.d.ts.map +1 -1
  119. package/dist/utils/is-node-in-scan-context.js +5 -5
  120. package/dist/utils/is-node-in-scan-context.js.map +1 -1
  121. package/dist/utils/is-non-empty.d.ts +2 -0
  122. package/dist/utils/is-non-empty.d.ts.map +1 -0
  123. package/dist/utils/is-non-empty.js +4 -0
  124. package/dist/utils/is-non-empty.js.map +1 -0
  125. package/dist/utils/normalize-context.d.ts +2 -2
  126. package/dist/utils/normalize-context.d.ts.map +1 -1
  127. package/dist/utils/normalize-context.js +10 -8
  128. package/dist/utils/normalize-context.js.map +1 -1
  129. package/dist/utils/recalculate-positions.d.ts +1 -1
  130. package/dist/utils/recalculate-positions.d.ts.map +1 -1
  131. package/dist/utils/recalculate-positions.js +5 -5
  132. package/dist/utils/recalculate-positions.js.map +1 -1
  133. package/dist/utils/recalculate-scrollable-ancestors.d.ts +1 -1
  134. package/dist/utils/recalculate-scrollable-ancestors.d.ts.map +1 -1
  135. package/dist/utils/recalculate-scrollable-ancestors.js +4 -4
  136. package/dist/utils/recalculate-scrollable-ancestors.js.map +1 -1
  137. package/dist/utils/shadow-dom-aware-mutation-observer.d.ts +1 -1
  138. package/dist/utils/shadow-dom-aware-mutation-observer.d.ts.map +1 -1
  139. package/dist/utils/shadow-dom-aware-mutation-observer.js +19 -22
  140. package/dist/utils/shadow-dom-aware-mutation-observer.js.map +1 -1
  141. package/dist/utils/supports-anchor-positioning.d.ts +1 -1
  142. package/dist/utils/supports-anchor-positioning.d.ts.map +1 -1
  143. package/dist/utils/supports-anchor-positioning.js +1 -1
  144. package/dist/utils/supports-anchor-positioning.js.map +1 -1
  145. package/dist/utils/transform-violations.d.ts +2 -2
  146. package/dist/utils/transform-violations.d.ts.map +1 -1
  147. package/dist/utils/transform-violations.js +9 -9
  148. package/dist/utils/transform-violations.js.map +1 -1
  149. package/dist/utils/update-elements-with-issues.d.ts +3 -3
  150. package/dist/utils/update-elements-with-issues.d.ts.map +1 -1
  151. package/dist/utils/update-elements-with-issues.js +34 -29
  152. package/dist/utils/update-elements-with-issues.js.map +1 -1
  153. package/dist/validate-options.d.ts +2 -2
  154. package/dist/validate-options.d.ts.map +1 -1
  155. package/dist/validate-options.js +24 -23
  156. package/dist/validate-options.js.map +1 -1
  157. package/package.json +7 -4
  158. package/src/accented.test.ts +2 -2
  159. package/src/accented.ts +39 -32
  160. package/src/common/tokens.ts +1 -0
  161. package/src/constants.ts +1 -1
  162. package/src/dom-updater.ts +26 -19
  163. package/src/elements/accented-dialog.ts +69 -43
  164. package/src/elements/accented-trigger.ts +54 -43
  165. package/src/fullscreen-listener.ts +15 -11
  166. package/src/intersection-observer.ts +27 -16
  167. package/src/log-and-rethrow.ts +2 -3
  168. package/src/logger.ts +8 -6
  169. package/src/register-elements.ts +7 -7
  170. package/src/resize-listener.ts +15 -11
  171. package/src/scanner.ts +66 -50
  172. package/src/scroll-listeners.ts +27 -19
  173. package/src/state.ts +24 -21
  174. package/src/task-queue.test.ts +5 -4
  175. package/src/task-queue.ts +2 -2
  176. package/src/types.ts +151 -95
  177. package/src/utils/are-elements-with-issues-equal.ts +7 -5
  178. package/src/utils/are-issue-sets-equal.test.ts +10 -6
  179. package/src/utils/are-issue-sets-equal.ts +8 -6
  180. package/src/utils/containing-blocks.ts +6 -3
  181. package/src/utils/contains.test.ts +2 -2
  182. package/src/utils/contains.ts +1 -1
  183. package/src/utils/deduplicate-nodes.ts +1 -1
  184. package/src/utils/deep-merge.test.ts +8 -1
  185. package/src/utils/deep-merge.ts +14 -8
  186. package/src/utils/dom-helpers.ts +6 -2
  187. package/src/utils/ensure-non-empty.ts +2 -2
  188. package/src/utils/get-element-html.ts +4 -2
  189. package/src/utils/get-element-position.ts +37 -24
  190. package/src/utils/get-parent.ts +1 -1
  191. package/src/utils/get-scan-context.test.ts +14 -8
  192. package/src/utils/get-scan-context.ts +12 -15
  193. package/src/utils/get-scrollable-ancestors.ts +8 -5
  194. package/src/utils/is-node-in-scan-context.test.ts +3 -3
  195. package/src/utils/is-node-in-scan-context.ts +6 -6
  196. package/src/utils/is-non-empty.ts +3 -0
  197. package/src/utils/normalize-context.test.ts +9 -9
  198. package/src/utils/normalize-context.ts +17 -10
  199. package/src/utils/recalculate-positions.ts +5 -5
  200. package/src/utils/recalculate-scrollable-ancestors.ts +4 -4
  201. package/src/utils/shadow-dom-aware-mutation-observer.ts +21 -24
  202. package/src/utils/supports-anchor-positioning.ts +3 -3
  203. package/src/utils/transform-violations.test.ts +22 -20
  204. package/src/utils/transform-violations.ts +14 -10
  205. package/src/utils/update-elements-with-issues.test.ts +49 -49
  206. package/src/utils/update-elements-with-issues.ts +96 -71
  207. package/src/validate-options.ts +91 -38
package/src/scanner.ts CHANGED
@@ -1,19 +1,25 @@
1
1
  import axe from 'axe-core';
2
- import TaskQueue from './task-queue.js';
3
- import { elementsWithIssues, enabled, extendedElementsWithIssues } from './state.js';
4
- import type { AxeOptions, Throttle, Callback, Context } from './types';
5
- import updateElementsWithIssues from './utils/update-elements-with-issues.js';
6
- import recalculatePositions from './utils/recalculate-positions.js';
7
- import recalculateScrollableAncestors from './utils/recalculate-scrollable-ancestors.js';
8
- import supportsAnchorPositioning from './utils/supports-anchor-positioning.js';
9
2
  import { getAccentedElementNames, issuesUrl } from './constants.js';
10
- import logAndRethrow from './log-and-rethrow.js';
11
- import createShadowDOMAwareMutationObserver from './utils/shadow-dom-aware-mutation-observer.js';
12
- import getScanContext from './utils/get-scan-context.js';
13
-
14
- export default function createScanner(name: string, context: Context, axeOptions: AxeOptions, throttle: Required<Throttle>, callback: Callback) {
3
+ import { logAndRethrow } from './log-and-rethrow.js';
4
+ import { elementsWithIssues, enabled, extendedElementsWithIssues } from './state.js';
5
+ import { TaskQueue } from './task-queue.js';
6
+ import type { AxeOptions, Callback, Context, Throttle } from './types.ts';
7
+ import { getScanContext } from './utils/get-scan-context.js';
8
+ import { recalculatePositions } from './utils/recalculate-positions.js';
9
+ import { recalculateScrollableAncestors } from './utils/recalculate-scrollable-ancestors.js';
10
+ import { createShadowDOMAwareMutationObserver } from './utils/shadow-dom-aware-mutation-observer.js';
11
+ import { supportsAnchorPositioning } from './utils/supports-anchor-positioning.js';
12
+ import { updateElementsWithIssues } from './utils/update-elements-with-issues.js';
13
+
14
+ export function createScanner(
15
+ name: string,
16
+ context: Context,
17
+ axeOptions: AxeOptions,
18
+ throttle: Required<Throttle>,
19
+ callback: Callback,
20
+ ) {
15
21
  const axeRunningWindowProp = `__${name}_axe_running__`;
16
- const win: Record<string, any> = window;
22
+ const win = window as unknown as Record<string, boolean>;
17
23
  const taskQueue = new TaskQueue<Node>(async (nodes) => {
18
24
  // We may see errors coming from axe-core when Accented is toggled off and on in qiuck succession,
19
25
  // which I've seen happen with hot reloading of a React application.
@@ -23,43 +29,41 @@ export default function createScanner(name: string, context: Context, axeOptions
23
29
  }
24
30
 
25
31
  try {
26
-
27
32
  performance.mark('scan-start');
28
33
 
29
34
  win[axeRunningWindowProp] = true;
30
35
 
31
36
  const scanContext = getScanContext(nodes, context);
32
37
 
33
- let result;
38
+ let result: axe.AxeResults | undefined;
34
39
 
35
40
  try {
36
41
  result = await axe.run(scanContext, {
37
42
  elementRef: true,
38
- // Although axe-core can perform iframe scanning, I haven't succeeded in it,
39
- // and the docs suggest that the axe-core script should be explicitly included
40
- // in each of the iframed documents anyway.
41
- // It seems preferable to disallow iframe scanning and not report issues in elements within iframes
42
- // in the case that such issues are for some reason reported by axe-core.
43
- // A consumer of Accented can instead scan the iframed document by calling Accented initialization from that document.
43
+ /**
44
+ * Although axe-core can perform iframe scanning, I haven't succeeded in it,
45
+ * and the docs suggest that the axe-core script should be explicitly included
46
+ * in each of the iframed documents anyway.
47
+ * It seems preferable to disallow iframe scanning and not report issues in elements within iframes
48
+ * in the case that such issues are for some reason reported by axe-core.
49
+ * A consumer of Accented can instead scan the iframed document by calling Accented initialization from that document.
50
+ */
44
51
  iframes: false,
45
52
  resultTypes: ['violations'],
46
- ...axeOptions
53
+ ...axeOptions,
47
54
  });
48
55
  } catch (error) {
49
56
  console.error(
50
- 'Accented: axe-core (the accessibility testing engine) threw an error. ' +
51
- 'Check the `axeOptions` property that you’re passing to Accented. ' +
52
- `If you still think it’s a bug in Accented, file an issue at ${issuesUrl}.\n`,
53
- error
57
+ `Accented: axe-core (the accessibility testing engine) threw an error. Check the \`axeOptions\` property that you’re passing to Accented. If you still think it’s a bug in Accented, file an issue at ${issuesUrl}.\n`,
58
+ error,
54
59
  );
55
- result = { violations: [] };
56
60
  }
57
61
  win[axeRunningWindowProp] = false;
58
62
 
59
63
  const scanMeasure = performance.measure('scan', 'scan-start');
60
64
  const scanDuration = Math.round(scanMeasure.duration);
61
65
 
62
- if (!enabled.value) {
66
+ if (!enabled.value || !result) {
63
67
  return;
64
68
  }
65
69
 
@@ -70,23 +74,23 @@ export default function createScanner(name: string, context: Context, axeOptions
70
74
  scanContext,
71
75
  violations: result.violations,
72
76
  win: window,
73
- name
77
+ name,
74
78
  });
75
79
 
76
80
  const domUpdateMeasure = performance.measure('dom-update', 'dom-update-start');
77
81
  const domUpdateDuration = Math.round(domUpdateMeasure.duration);
78
82
 
79
83
  callback({
84
+ // Assuming that the {include, exclude} shape of the context object will be used less often
85
+ // than other variants, we'll output just the `include` array in case nothing is excluded
86
+ // in the scan.
87
+ scanContext: scanContext.exclude.length > 0 ? scanContext : scanContext.include,
80
88
  elementsWithIssues: elementsWithIssues.value,
81
89
  performance: {
82
90
  totalBlockingTime: scanDuration + domUpdateDuration,
83
91
  scan: scanDuration,
84
92
  domUpdate: domUpdateDuration,
85
- // Assuming that the {include, exclude} shape of the context object will be used less often
86
- // than other variants, we'll output just the `include` array in case nothing is excluded
87
- // in the scan.
88
- scanContext: scanContext.exclude.length > 0 ? scanContext : scanContext.include
89
- }
93
+ },
90
94
  });
91
95
  } catch (error) {
92
96
  win[axeRunningWindowProp] = false;
@@ -97,15 +101,21 @@ export default function createScanner(name: string, context: Context, axeOptions
97
101
  taskQueue.add(document);
98
102
 
99
103
  const accentedElementNames = getAccentedElementNames(name);
100
- const mutationObserver = createShadowDOMAwareMutationObserver(name, mutationList => {
104
+ const mutationObserver = createShadowDOMAwareMutationObserver(name, (mutationList) => {
101
105
  try {
102
106
  // We're not interested in mutations that are caused exclusively by the custom elements
103
107
  // introduced by Accented.
104
- const listWithoutAccentedElements = mutationList.filter(mutationRecord => {
105
- const onlyAccentedElementsAddedOrRemoved = mutationRecord.type === 'childList' &&
106
- [...mutationRecord.addedNodes].every(node => accentedElementNames.includes(node.nodeName.toLowerCase())) &&
107
- [...mutationRecord.removedNodes].every(node => accentedElementNames.includes(node.nodeName.toLowerCase()));
108
- const accentedElementChanged = mutationRecord.type === 'attributes' &&
108
+ const listWithoutAccentedElements = mutationList.filter((mutationRecord) => {
109
+ const onlyAccentedElementsAddedOrRemoved =
110
+ mutationRecord.type === 'childList' &&
111
+ [...mutationRecord.addedNodes].every((node) =>
112
+ accentedElementNames.includes(node.nodeName.toLowerCase()),
113
+ ) &&
114
+ [...mutationRecord.removedNodes].every((node) =>
115
+ accentedElementNames.includes(node.nodeName.toLowerCase()),
116
+ );
117
+ const accentedElementChanged =
118
+ mutationRecord.type === 'attributes' &&
109
119
  accentedElementNames.includes(mutationRecord.target.nodeName.toLowerCase());
110
120
  return !(onlyAccentedElementsAddedOrRemoved || accentedElementChanged);
111
121
  });
@@ -125,18 +135,24 @@ export default function createScanner(name: string, context: Context, axeOptions
125
135
  // If we simply exclude all mutations where attributeName = `data-${name}`,
126
136
  // we may miss other mutations on those same elements caused by Accented,
127
137
  // leading to extra runs of the mutation observer.
128
- const elementsWithAccentedAttributeChanges = listWithoutAccentedElements.reduce((nodes, mutationRecord) => {
129
- if (mutationRecord.type === 'attributes' && mutationRecord.attributeName === `data-${name}`) {
130
- nodes.add(mutationRecord.target);
131
- }
132
- return nodes;
133
- }, new Set<Node>());
134
-
135
- const filteredMutationList = listWithoutAccentedElements.filter(mutationRecord => {
138
+ const elementsWithAccentedAttributeChanges = listWithoutAccentedElements.reduce(
139
+ (nodes, mutationRecord) => {
140
+ if (
141
+ mutationRecord.type === 'attributes' &&
142
+ mutationRecord.attributeName === `data-${name}`
143
+ ) {
144
+ nodes.add(mutationRecord.target);
145
+ }
146
+ return nodes;
147
+ },
148
+ new Set<Node>(),
149
+ );
150
+
151
+ const filteredMutationList = listWithoutAccentedElements.filter((mutationRecord) => {
136
152
  return !elementsWithAccentedAttributeChanges.has(mutationRecord.target);
137
153
  });
138
154
 
139
- const nodes = filteredMutationList.map(mutationRecord => mutationRecord.target);
155
+ const nodes = filteredMutationList.map((mutationRecord) => mutationRecord.target);
140
156
  taskQueue.addMultiple(nodes);
141
157
  } catch (error) {
142
158
  logAndRethrow(error);
@@ -147,7 +163,7 @@ export default function createScanner(name: string, context: Context, axeOptions
147
163
  subtree: true,
148
164
  childList: true,
149
165
  attributes: true,
150
- characterData: true
166
+ characterData: true,
151
167
  });
152
168
 
153
169
  return () => {
@@ -1,37 +1,45 @@
1
1
  import { effect } from '@preact/signals-core';
2
- import recalculatePositions from './utils/recalculate-positions.js';
2
+ import { logAndRethrow } from './log-and-rethrow.js';
3
3
  import { scrollableAncestors } from './state.js';
4
- import logAndRethrow from './log-and-rethrow.js';
4
+ import { recalculatePositions } from './utils/recalculate-positions.js';
5
5
 
6
- export default function setupScrollListeners() {
6
+ export function setupScrollListeners() {
7
7
  const documentAbortController = new AbortController();
8
- document.addEventListener('scroll', () => {
9
- try {
10
- recalculatePositions();
11
- } catch (error) {
12
- logAndRethrow(error);
13
- }
14
- }, { signal: documentAbortController.signal });
8
+ document.addEventListener(
9
+ 'scroll',
10
+ () => {
11
+ try {
12
+ recalculatePositions();
13
+ } catch (error) {
14
+ logAndRethrow(error);
15
+ }
16
+ },
17
+ { signal: documentAbortController.signal },
18
+ );
15
19
 
16
20
  const disposeOfEffect = effect(() => {
17
21
  // TODO: optimize performance, issue #81
18
22
  const elementAbortController = new AbortController();
19
23
  for (const scrollableAncestor of scrollableAncestors.value) {
20
- scrollableAncestor.addEventListener('scroll', () => {
21
- try {
22
- recalculatePositions();
23
- } catch (error) {
24
- logAndRethrow(error);
25
- }
26
- }, { signal: elementAbortController.signal });
24
+ scrollableAncestor.addEventListener(
25
+ 'scroll',
26
+ () => {
27
+ try {
28
+ recalculatePositions();
29
+ } catch (error) {
30
+ logAndRethrow(error);
31
+ }
32
+ },
33
+ { signal: elementAbortController.signal },
34
+ );
27
35
  }
28
36
  return () => {
29
37
  elementAbortController.abort();
30
- }
38
+ };
31
39
  });
32
40
 
33
41
  return () => {
34
42
  documentAbortController.abort();
35
43
  disposeOfEffect();
36
44
  };
37
- };
45
+ }
package/src/state.ts CHANGED
@@ -1,32 +1,35 @@
1
- import { signal, computed } from '@preact/signals-core';
1
+ import { computed, signal } from '@preact/signals-core';
2
2
 
3
- import type { ElementWithIssues, ExtendedElementWithIssues } from './types';
3
+ import type { ElementWithIssues, ExtendedElementWithIssues } from './types.ts';
4
4
 
5
5
  export const enabled = signal(false);
6
6
 
7
7
  export const extendedElementsWithIssues = signal<Array<ExtendedElementWithIssues>>([]);
8
8
 
9
- export const elementsWithIssues = computed<Array<ElementWithIssues>>(() => extendedElementsWithIssues.value.map(extendedElementWithIssues => ({
10
- element: extendedElementWithIssues.element,
11
- rootNode: extendedElementWithIssues.rootNode,
12
- issues: extendedElementWithIssues.issues.value
13
- })));
9
+ export const elementsWithIssues = computed<Array<ElementWithIssues>>(() =>
10
+ extendedElementsWithIssues.value.map((extendedElementWithIssues) => ({
11
+ element: extendedElementWithIssues.element,
12
+ rootNode: extendedElementWithIssues.rootNode,
13
+ issues: extendedElementWithIssues.issues.value,
14
+ })),
15
+ );
14
16
 
15
- export const rootNodes = computed<Set<Node>>(() =>
16
- new Set(
17
- (enabled.value ? [document as Node] : [])
18
- .concat(...(extendedElementsWithIssues.value.map(extendedElementWithIssues => extendedElementWithIssues.rootNode)))
19
- )
17
+ export const rootNodes = computed<Set<Node>>(
18
+ () =>
19
+ new Set(
20
+ (enabled.value ? [document as Node] : []).concat(
21
+ ...extendedElementsWithIssues.value.map(
22
+ (extendedElementWithIssues) => extendedElementWithIssues.rootNode,
23
+ ),
24
+ ),
25
+ ),
20
26
  );
21
27
 
22
28
  export const scrollableAncestors = computed<Set<Element>>(() =>
23
- extendedElementsWithIssues.value.reduce(
24
- (scrollableAncestors, extendedElementWithIssues) => {
25
- for (const scrollableAncestor of extendedElementWithIssues.scrollableAncestors.value) {
26
- scrollableAncestors.add(scrollableAncestor);
27
- }
28
- return scrollableAncestors;
29
- },
30
- new Set<Element>()
31
- )
29
+ extendedElementsWithIssues.value.reduce((scrollableAncestors, extendedElementWithIssues) => {
30
+ for (const scrollableAncestor of extendedElementWithIssues.scrollableAncestors.value) {
31
+ scrollableAncestors.add(scrollableAncestor);
32
+ }
33
+ return scrollableAncestors;
34
+ }, new Set<Element>()),
32
35
  );
@@ -1,11 +1,12 @@
1
1
  import assert from 'node:assert/strict';
2
- import {mock, suite, test} from 'node:test';
2
+ import { mock, suite, test } from 'node:test';
3
3
 
4
- import TaskQueue from './task-queue.js';
4
+ import { TaskQueue } from './task-queue';
5
5
 
6
- const wait = (duration: number) => new Promise(resolve => setTimeout(resolve, duration));
6
+ const wait = (duration: number) => new Promise((resolve) => setTimeout(resolve, duration));
7
7
 
8
- const createAsyncCallback = (duration: number) => mock.fn(() => new Promise(resolve => setTimeout(resolve, duration)));
8
+ const createAsyncCallback = (duration: number) =>
9
+ mock.fn(() => new Promise((resolve) => setTimeout(resolve, duration)));
9
10
 
10
11
  suite('TaskQueue', () => {
11
12
  test('callback is not called after a TaskQueue is created, even after a timeout', async () => {
package/src/task-queue.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Throttle } from './types';
1
+ import type { Throttle } from './types.ts';
2
2
 
3
3
  type TaskCallback<T> = (items: Array<T>) => void;
4
4
 
5
- export default class TaskQueue<T> {
5
+ export class TaskQueue<T> {
6
6
  #throttle: Throttle;
7
7
  #asyncCallback: TaskCallback<T> | null = null;
8
8