accented 0.0.0-20250404114312 → 0.0.0-20250618181418
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/README.md +0 -207
- package/dist/accented.d.ts +2 -2
- package/dist/accented.d.ts.map +1 -1
- package/dist/accented.js +29 -23
- package/dist/accented.js.map +1 -1
- package/dist/common/tokens.d.ts +2 -0
- package/dist/common/tokens.d.ts.map +1 -0
- package/dist/common/tokens.js +2 -0
- package/dist/common/tokens.js.map +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/dom-updater.d.ts +1 -1
- package/dist/dom-updater.d.ts.map +1 -1
- package/dist/dom-updater.js +43 -16
- package/dist/dom-updater.js.map +1 -1
- package/dist/elements/accented-dialog.d.ts +13 -10
- package/dist/elements/accented-dialog.d.ts.map +1 -1
- package/dist/elements/accented-dialog.js +46 -69
- package/dist/elements/accented-dialog.js.map +1 -1
- package/dist/elements/accented-trigger.d.ts +12 -9
- package/dist/elements/accented-trigger.d.ts.map +1 -1
- package/dist/elements/accented-trigger.js +14 -15
- package/dist/elements/accented-trigger.js.map +1 -1
- package/dist/fullscreen-listener.d.ts +1 -1
- package/dist/fullscreen-listener.d.ts.map +1 -1
- package/dist/fullscreen-listener.js +3 -4
- package/dist/fullscreen-listener.js.map +1 -1
- package/dist/intersection-observer.d.ts +1 -1
- package/dist/intersection-observer.d.ts.map +1 -1
- package/dist/intersection-observer.js +12 -6
- package/dist/intersection-observer.js.map +1 -1
- package/dist/log-and-rethrow.d.ts +1 -1
- package/dist/log-and-rethrow.d.ts.map +1 -1
- package/dist/log-and-rethrow.js +2 -3
- package/dist/log-and-rethrow.js.map +1 -1
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +6 -3
- package/dist/logger.js.map +1 -1
- package/dist/register-elements.d.ts +1 -1
- package/dist/register-elements.d.ts.map +1 -1
- package/dist/register-elements.js +6 -7
- package/dist/register-elements.js.map +1 -1
- package/dist/resize-listener.d.ts +1 -1
- package/dist/resize-listener.d.ts.map +1 -1
- package/dist/resize-listener.js +3 -4
- package/dist/resize-listener.js.map +1 -1
- package/dist/scanner.d.ts +2 -2
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +39 -36
- package/dist/scanner.js.map +1 -1
- package/dist/scroll-listeners.d.ts +1 -1
- package/dist/scroll-listeners.d.ts.map +1 -1
- package/dist/scroll-listeners.js +3 -4
- package/dist/scroll-listeners.js.map +1 -1
- package/dist/state.d.ts +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +4 -5
- package/dist/state.js.map +1 -1
- package/dist/task-queue.d.ts +4 -4
- package/dist/task-queue.d.ts.map +1 -1
- package/dist/task-queue.js +3 -2
- package/dist/task-queue.js.map +1 -1
- package/dist/types.d.ts +28 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/are-elements-with-issues-equal.d.ts +2 -2
- package/dist/utils/are-elements-with-issues-equal.d.ts.map +1 -1
- package/dist/utils/are-elements-with-issues-equal.js +3 -3
- package/dist/utils/are-elements-with-issues-equal.js.map +1 -1
- package/dist/utils/are-issue-sets-equal.d.ts +2 -2
- package/dist/utils/are-issue-sets-equal.d.ts.map +1 -1
- package/dist/utils/are-issue-sets-equal.js +3 -3
- package/dist/utils/are-issue-sets-equal.js.map +1 -1
- package/dist/utils/containing-blocks.d.ts +3 -0
- package/dist/utils/containing-blocks.d.ts.map +1 -0
- package/dist/utils/containing-blocks.js +46 -0
- package/dist/utils/containing-blocks.js.map +1 -0
- package/dist/utils/contains.d.ts +2 -0
- package/dist/utils/contains.d.ts.map +1 -0
- package/dist/utils/contains.js +19 -0
- package/dist/utils/contains.js.map +1 -0
- package/dist/utils/deduplicate-nodes.d.ts +2 -0
- package/dist/utils/deduplicate-nodes.d.ts.map +1 -0
- package/dist/utils/deduplicate-nodes.js +4 -0
- package/dist/utils/deduplicate-nodes.js.map +1 -0
- package/dist/utils/deep-merge.d.ts +1 -1
- package/dist/utils/deep-merge.d.ts.map +1 -1
- package/dist/utils/deep-merge.js +6 -5
- package/dist/utils/deep-merge.js.map +1 -1
- package/dist/utils/dom-helpers.d.ts +3 -0
- package/dist/utils/dom-helpers.d.ts.map +1 -1
- package/dist/utils/dom-helpers.js +15 -0
- package/dist/utils/dom-helpers.js.map +1 -1
- package/dist/utils/ensure-non-empty.d.ts +2 -0
- package/dist/utils/ensure-non-empty.d.ts.map +1 -0
- package/dist/utils/ensure-non-empty.js +7 -0
- package/dist/utils/ensure-non-empty.js.map +1 -0
- package/dist/utils/get-element-html.d.ts +1 -1
- package/dist/utils/get-element-html.d.ts.map +1 -1
- package/dist/utils/get-element-html.js +4 -2
- package/dist/utils/get-element-html.js.map +1 -1
- package/dist/utils/get-element-position.d.ts +10 -2
- package/dist/utils/get-element-position.d.ts.map +1 -1
- package/dist/utils/get-element-position.js +34 -23
- package/dist/utils/get-element-position.js.map +1 -1
- package/dist/utils/get-parent.d.ts +1 -1
- package/dist/utils/get-parent.d.ts.map +1 -1
- package/dist/utils/get-parent.js +1 -1
- package/dist/utils/get-parent.js.map +1 -1
- package/dist/utils/get-scan-context.d.ts +3 -0
- package/dist/utils/get-scan-context.d.ts.map +1 -0
- package/dist/utils/get-scan-context.js +28 -0
- package/dist/utils/get-scan-context.js.map +1 -0
- package/dist/utils/get-scrollable-ancestors.d.ts +1 -1
- package/dist/utils/get-scrollable-ancestors.d.ts.map +1 -1
- package/dist/utils/get-scrollable-ancestors.js +5 -5
- package/dist/utils/get-scrollable-ancestors.js.map +1 -1
- package/dist/utils/is-node-in-scan-context.d.ts +3 -0
- package/dist/utils/is-node-in-scan-context.d.ts.map +1 -0
- package/dist/utils/is-node-in-scan-context.js +26 -0
- package/dist/utils/is-node-in-scan-context.js.map +1 -0
- package/dist/utils/is-non-empty.d.ts +2 -0
- package/dist/utils/is-non-empty.d.ts.map +1 -0
- package/dist/utils/is-non-empty.js +4 -0
- package/dist/utils/is-non-empty.js.map +1 -0
- package/dist/utils/normalize-context.d.ts +3 -0
- package/dist/utils/normalize-context.d.ts.map +1 -0
- package/dist/utils/normalize-context.js +59 -0
- package/dist/utils/normalize-context.js.map +1 -0
- package/dist/utils/recalculate-positions.d.ts +1 -1
- package/dist/utils/recalculate-positions.d.ts.map +1 -1
- package/dist/utils/recalculate-positions.js +5 -5
- package/dist/utils/recalculate-positions.js.map +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.d.ts +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.d.ts.map +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.js +4 -4
- package/dist/utils/recalculate-scrollable-ancestors.js.map +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.d.ts +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.d.ts.map +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.js +19 -22
- package/dist/utils/shadow-dom-aware-mutation-observer.js.map +1 -1
- package/dist/utils/supports-anchor-positioning.d.ts +1 -1
- package/dist/utils/supports-anchor-positioning.d.ts.map +1 -1
- package/dist/utils/supports-anchor-positioning.js +1 -1
- package/dist/utils/supports-anchor-positioning.js.map +1 -1
- package/dist/utils/transform-violations.d.ts +2 -2
- package/dist/utils/transform-violations.d.ts.map +1 -1
- package/dist/utils/transform-violations.js +9 -9
- package/dist/utils/transform-violations.js.map +1 -1
- package/dist/utils/update-elements-with-issues.d.ts +11 -5
- package/dist/utils/update-elements-with-issues.d.ts.map +1 -1
- package/dist/utils/update-elements-with-issues.js +54 -26
- package/dist/utils/update-elements-with-issues.js.map +1 -1
- package/dist/validate-options.d.ts +2 -2
- package/dist/validate-options.d.ts.map +1 -1
- package/dist/validate-options.js +91 -4
- package/dist/validate-options.js.map +1 -1
- package/package.json +11 -5
- package/src/accented.test.ts +2 -2
- package/src/accented.ts +38 -28
- package/src/common/tokens.ts +1 -0
- package/src/dom-updater.ts +59 -22
- package/src/elements/accented-dialog.ts +102 -106
- package/src/elements/accented-trigger.ts +58 -48
- package/src/fullscreen-listener.ts +15 -11
- package/src/intersection-observer.ts +27 -16
- package/src/log-and-rethrow.ts +2 -3
- package/src/logger.ts +14 -4
- package/src/register-elements.ts +7 -7
- package/src/resize-listener.ts +15 -11
- package/src/scanner.ts +70 -50
- package/src/scroll-listeners.ts +27 -19
- package/src/state.ts +24 -21
- package/src/task-queue.test.ts +5 -4
- package/src/task-queue.ts +8 -6
- package/src/types.ts +74 -42
- package/src/utils/are-elements-with-issues-equal.ts +7 -5
- package/src/utils/are-issue-sets-equal.test.ts +10 -6
- package/src/utils/are-issue-sets-equal.ts +8 -6
- package/src/utils/containing-blocks.ts +60 -0
- package/src/utils/contains.test.ts +55 -0
- package/src/utils/contains.ts +19 -0
- package/src/utils/deduplicate-nodes.ts +3 -0
- package/src/utils/deep-merge.test.ts +8 -1
- package/src/utils/deep-merge.ts +11 -8
- package/src/utils/dom-helpers.ts +20 -0
- package/src/utils/ensure-non-empty.ts +6 -0
- package/src/utils/get-element-html.ts +4 -2
- package/src/utils/get-element-position.ts +51 -22
- package/src/utils/get-parent.ts +1 -1
- package/src/utils/get-scan-context.test.ts +85 -0
- package/src/utils/get-scan-context.ts +36 -0
- package/src/utils/get-scrollable-ancestors.ts +8 -5
- package/src/utils/is-node-in-scan-context.test.ts +70 -0
- package/src/utils/is-node-in-scan-context.ts +29 -0
- package/src/utils/is-non-empty.ts +3 -0
- package/src/utils/normalize-context.test.ts +105 -0
- package/src/utils/normalize-context.ts +65 -0
- package/src/utils/recalculate-positions.ts +5 -5
- package/src/utils/recalculate-scrollable-ancestors.ts +4 -4
- package/src/utils/shadow-dom-aware-mutation-observer.ts +21 -24
- package/src/utils/supports-anchor-positioning.ts +3 -3
- package/src/utils/transform-violations.test.ts +22 -20
- package/src/utils/transform-violations.ts +14 -10
- package/src/utils/update-elements-with-issues.test.ts +102 -49
- package/src/utils/update-elements-with-issues.ts +122 -58
- package/src/validate-options.ts +154 -14
package/src/accented.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import setupResizeListener from './resize-listener.js';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
1
|
+
import { createDomUpdater } from './dom-updater.js';
|
|
2
|
+
import { setupResizeListener as setupFullscreenListener } from './fullscreen-listener.js';
|
|
3
|
+
import { setupIntersectionObserver } from './intersection-observer.js';
|
|
4
|
+
import { logAndRethrow } from './log-and-rethrow.js';
|
|
5
|
+
import { createLogger } from './logger.js';
|
|
6
|
+
import { registerElements } from './register-elements.js';
|
|
7
|
+
import { setupResizeListener } from './resize-listener.js';
|
|
8
|
+
import { createScanner } from './scanner.js';
|
|
9
|
+
import { setupScrollListeners } from './scroll-listeners.js';
|
|
10
10
|
import { enabled, extendedElementsWithIssues } from './state.js';
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import supportsAnchorPositioning from './utils/supports-anchor-positioning.js';
|
|
15
|
-
import
|
|
11
|
+
import type { AccentedOptions, DisableAccented } from './types.ts';
|
|
12
|
+
import { initializeContainingBlockSupportSet } from './utils/containing-blocks.js';
|
|
13
|
+
import { deepMerge } from './utils/deep-merge.js';
|
|
14
|
+
import { supportsAnchorPositioning } from './utils/supports-anchor-positioning.js';
|
|
15
|
+
import { validateOptions } from './validate-options.js';
|
|
16
16
|
|
|
17
17
|
export type { AccentedOptions, DisableAccented };
|
|
18
18
|
|
|
@@ -41,24 +41,25 @@ export type { AccentedOptions, DisableAccented };
|
|
|
41
41
|
* }
|
|
42
42
|
* });
|
|
43
43
|
*/
|
|
44
|
-
export
|
|
45
|
-
|
|
44
|
+
export function accented(options: AccentedOptions = {}): DisableAccented {
|
|
46
45
|
validateOptions(options);
|
|
47
46
|
|
|
48
47
|
try {
|
|
49
48
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
50
|
-
console.warn(
|
|
49
|
+
console.warn(
|
|
50
|
+
'Accented: this script can only run in the browser, and it’s likely running on the server now. Exiting.',
|
|
51
|
+
);
|
|
51
52
|
console.trace();
|
|
52
53
|
return () => {};
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
const defaultOutput: Required<AccentedOptions['output']> = {
|
|
56
|
-
console: true
|
|
57
|
+
console: true,
|
|
57
58
|
};
|
|
58
59
|
|
|
59
60
|
const defaultThrottle: Required<AccentedOptions['throttle']> = {
|
|
60
61
|
wait: 1000,
|
|
61
|
-
leading: true
|
|
62
|
+
leading: true,
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
// IMPORTANT: when changing any of the properties or values, also do the following:
|
|
@@ -67,36 +68,45 @@ export default function accented(options: AccentedOptions = {}): DisableAccented
|
|
|
67
68
|
// * update examples in the accented() function JSDoc;
|
|
68
69
|
// * update examples in the Readme.
|
|
69
70
|
const defaultOptions: Required<AccentedOptions> = {
|
|
70
|
-
|
|
71
|
+
context: document,
|
|
71
72
|
axeOptions: {},
|
|
72
73
|
name: 'accented',
|
|
73
74
|
output: defaultOutput,
|
|
74
75
|
throttle: defaultThrottle,
|
|
75
|
-
callback: () => {}
|
|
76
|
+
callback: () => {},
|
|
76
77
|
};
|
|
77
78
|
|
|
78
|
-
const {
|
|
79
|
+
const { context, axeOptions, name, output, throttle, callback } = deepMerge(
|
|
80
|
+
defaultOptions,
|
|
81
|
+
options,
|
|
82
|
+
);
|
|
79
83
|
|
|
80
84
|
if (enabled.value) {
|
|
81
85
|
// Add link to the recipes section of the docs (#56).
|
|
82
86
|
console.warn(
|
|
83
87
|
'You are trying to run the Accented library more than once. ' +
|
|
84
|
-
|
|
88
|
+
'This will likely lead to errors.',
|
|
85
89
|
);
|
|
86
90
|
console.trace();
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
enabled.value = true;
|
|
90
94
|
|
|
95
|
+
initializeContainingBlockSupportSet();
|
|
91
96
|
registerElements(name);
|
|
92
97
|
|
|
93
|
-
const {disconnect: cleanupIntersectionObserver, intersectionObserver } =
|
|
94
|
-
|
|
98
|
+
const { disconnect: cleanupIntersectionObserver, intersectionObserver } =
|
|
99
|
+
setupIntersectionObserver();
|
|
100
|
+
const cleanupScanner = createScanner(name, context, axeOptions, throttle, callback);
|
|
95
101
|
const cleanupDomUpdater = createDomUpdater(name, intersectionObserver);
|
|
96
102
|
const cleanupLogger = output.console ? createLogger() : () => {};
|
|
97
|
-
const cleanupScrollListeners =
|
|
98
|
-
const cleanupResizeListener = supportsAnchorPositioning(window)
|
|
99
|
-
|
|
103
|
+
const cleanupScrollListeners = setupScrollListeners();
|
|
104
|
+
const cleanupResizeListener = supportsAnchorPositioning(window)
|
|
105
|
+
? () => {}
|
|
106
|
+
: setupResizeListener();
|
|
107
|
+
const cleanupFullscreenListener = supportsAnchorPositioning(window)
|
|
108
|
+
? () => {}
|
|
109
|
+
: setupFullscreenListener();
|
|
100
110
|
|
|
101
111
|
return () => {
|
|
102
112
|
try {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const primaryColor = 'oklch(0.5 0.3 0)';
|
package/src/dom-updater.ts
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
import { effect } from '@preact/signals-core';
|
|
2
|
+
import { primaryColor } from './common/tokens.js';
|
|
2
3
|
import { extendedElementsWithIssues, rootNodes } from './state.js';
|
|
3
|
-
import type { ExtendedElementWithIssues } from './types';
|
|
4
|
-
import areElementsWithIssuesEqual from './utils/are-elements-with-issues-equal.js';
|
|
5
|
-
import supportsAnchorPositioning from './utils/supports-anchor-positioning.js';
|
|
4
|
+
import type { ExtendedElementWithIssues } from './types.ts';
|
|
5
|
+
import { areElementsWithIssuesEqual } from './utils/are-elements-with-issues-equal.js';
|
|
6
6
|
import { isDocument, isDocumentFragment, isShadowRoot } from './utils/dom-helpers.js';
|
|
7
|
-
import getParent from './utils/get-parent.js';
|
|
7
|
+
import { getParent } from './utils/get-parent.js';
|
|
8
|
+
import { supportsAnchorPositioning } from './utils/supports-anchor-positioning.js';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const shouldInsertTriggerInsideElement = (element: Element): boolean => {
|
|
11
|
+
/**
|
|
12
|
+
* No parent means that the element is a root node,
|
|
13
|
+
* which cannot have siblings.
|
|
14
|
+
*/
|
|
15
|
+
const noParent = !getParent(element);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Table cells get a special treatment because if a sibling to a TH or TD is inserted,
|
|
19
|
+
* it alters the table layout, no matter how that sibling is positioned.
|
|
20
|
+
* We don't want tables to look broken, so we're inserting the trigger inside the table cell.
|
|
21
|
+
*/
|
|
22
|
+
const isTableCell = element.nodeName === 'TH' || element.nodeName === 'TD';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* We want to put the trigger inside the <summary> element,
|
|
26
|
+
* because otherwise it will be hidden by the browser when the <details> element is collapsed
|
|
27
|
+
* (since none of the siblings of <summary> are visible then).
|
|
28
|
+
*/
|
|
29
|
+
const isSummary = element.nodeName === 'SUMMARY';
|
|
30
|
+
|
|
31
|
+
return noParent || isTableCell || isSummary;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function createDomUpdater(name: string, intersectionObserver?: IntersectionObserver) {
|
|
10
35
|
const attrName = `data-${name}`;
|
|
11
36
|
|
|
12
|
-
function getAnchorNames
|
|
37
|
+
function getAnchorNames(anchorNameValue: string) {
|
|
13
38
|
return anchorNameValue
|
|
14
39
|
.split(',')
|
|
15
|
-
.map(anchorName => anchorName.trim())
|
|
16
|
-
.filter(anchorName => anchorName.startsWith('--'));
|
|
40
|
+
.map((anchorName) => anchorName.trim())
|
|
41
|
+
.filter((anchorName) => anchorName.startsWith('--'));
|
|
17
42
|
}
|
|
18
43
|
|
|
19
|
-
function setAnchorName
|
|
44
|
+
function setAnchorName(elementWithIssues: ExtendedElementWithIssues) {
|
|
20
45
|
const { element, id, anchorNameValue } = elementWithIssues;
|
|
21
46
|
const anchorNames = getAnchorNames(anchorNameValue);
|
|
22
47
|
if (anchorNames.length > 0) {
|
|
@@ -26,7 +51,7 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
26
51
|
}
|
|
27
52
|
}
|
|
28
53
|
|
|
29
|
-
function removeAnchorName
|
|
54
|
+
function removeAnchorName(elementWithIssues: ExtendedElementWithIssues) {
|
|
30
55
|
const { element, anchorNameValue } = elementWithIssues;
|
|
31
56
|
const anchorNames = getAnchorNames(anchorNameValue);
|
|
32
57
|
if (anchorNames.length > 0) {
|
|
@@ -36,17 +61,20 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
36
61
|
}
|
|
37
62
|
}
|
|
38
63
|
|
|
39
|
-
function setIssues
|
|
64
|
+
function setIssues(extendedElementsWithIssues: Array<ExtendedElementWithIssues>) {
|
|
40
65
|
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
66
|
+
if (elementWithIssues.skipRender) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
41
69
|
elementWithIssues.element.setAttribute(attrName, elementWithIssues.id.toString());
|
|
42
70
|
if (supportsAnchorPositioning(window)) {
|
|
43
71
|
setAnchorName(elementWithIssues);
|
|
44
72
|
}
|
|
45
73
|
|
|
46
|
-
if (
|
|
47
|
-
elementWithIssues.element.insertAdjacentElement('afterend', elementWithIssues.trigger);
|
|
48
|
-
} else {
|
|
74
|
+
if (shouldInsertTriggerInsideElement(elementWithIssues.element)) {
|
|
49
75
|
elementWithIssues.element.insertAdjacentElement('beforeend', elementWithIssues.trigger);
|
|
76
|
+
} else {
|
|
77
|
+
elementWithIssues.element.insertAdjacentElement('afterend', elementWithIssues.trigger);
|
|
50
78
|
}
|
|
51
79
|
if (intersectionObserver) {
|
|
52
80
|
intersectionObserver.observe(elementWithIssues.element);
|
|
@@ -54,8 +82,11 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
54
82
|
}
|
|
55
83
|
}
|
|
56
84
|
|
|
57
|
-
function removeIssues
|
|
85
|
+
function removeIssues(extendedElementsWithIssues: Array<ExtendedElementWithIssues>) {
|
|
58
86
|
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
87
|
+
if (elementWithIssues.skipRender) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
59
90
|
elementWithIssues.element.removeAttribute(attrName);
|
|
60
91
|
if (supportsAnchorPositioning(window)) {
|
|
61
92
|
removeAnchorName(elementWithIssues);
|
|
@@ -73,7 +104,7 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
73
104
|
:root {
|
|
74
105
|
/* Ensure that the primary / secondary color combination meets WCAG 1.4.3 Contrast (Minimum) */
|
|
75
106
|
/* OKLCH stuff: https://oklch.com/ */
|
|
76
|
-
--${name}-primary-color:
|
|
107
|
+
--${name}-primary-color: ${primaryColor};
|
|
77
108
|
--${name}-secondary-color: oklch(0.98 0 0);
|
|
78
109
|
--${name}-outline-width: 2px;
|
|
79
110
|
--${name}-outline-style: solid;
|
|
@@ -94,8 +125,10 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
94
125
|
|
|
95
126
|
const disposeOfStyleSheetsEffect = effect(() => {
|
|
96
127
|
const newRootNodes = rootNodes.value;
|
|
97
|
-
const addedRootNodes = [...newRootNodes].filter(rootNode => !previousRootNodes.has(rootNode));
|
|
98
|
-
const removedRootNodes = [...previousRootNodes].filter(
|
|
128
|
+
const addedRootNodes = [...newRootNodes].filter((rootNode) => !previousRootNodes.has(rootNode));
|
|
129
|
+
const removedRootNodes = [...previousRootNodes].filter(
|
|
130
|
+
(rootNode) => !newRootNodes.has(rootNode),
|
|
131
|
+
);
|
|
99
132
|
for (const rootNode of addedRootNodes) {
|
|
100
133
|
if (isDocument(rootNode) || (isDocumentFragment(rootNode) && isShadowRoot(rootNode))) {
|
|
101
134
|
rootNode.adoptedStyleSheets.push(stylesheet);
|
|
@@ -110,11 +143,15 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
110
143
|
});
|
|
111
144
|
|
|
112
145
|
const disposeOfElementsEffect = effect(() => {
|
|
113
|
-
const added = extendedElementsWithIssues.value.filter(elementWithIssues => {
|
|
114
|
-
return !previousExtendedElementsWithIssues.some(previousElementWithIssues =>
|
|
146
|
+
const added = extendedElementsWithIssues.value.filter((elementWithIssues) => {
|
|
147
|
+
return !previousExtendedElementsWithIssues.some((previousElementWithIssues) =>
|
|
148
|
+
areElementsWithIssuesEqual(previousElementWithIssues, elementWithIssues),
|
|
149
|
+
);
|
|
115
150
|
});
|
|
116
|
-
const removed = previousExtendedElementsWithIssues.filter(previousElementWithIssues => {
|
|
117
|
-
return !extendedElementsWithIssues.value.some(elementWithIssues =>
|
|
151
|
+
const removed = previousExtendedElementsWithIssues.filter((previousElementWithIssues) => {
|
|
152
|
+
return !extendedElementsWithIssues.value.some((elementWithIssues) =>
|
|
153
|
+
areElementsWithIssuesEqual(elementWithIssues, previousElementWithIssues),
|
|
154
|
+
);
|
|
118
155
|
});
|
|
119
156
|
removeIssues(removed);
|
|
120
157
|
setIssues(added);
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import type { Issue } from '../types';
|
|
2
1
|
import type { Signal } from '@preact/signals-core';
|
|
3
|
-
import { effect } from '@preact/signals-core';
|
|
4
|
-
import getElementHtml from '../utils/get-element-html.js';
|
|
5
2
|
import { accentedUrl } from '../constants.js';
|
|
6
|
-
import logAndRethrow from '../log-and-rethrow.js';
|
|
3
|
+
import { logAndRethrow } from '../log-and-rethrow.js';
|
|
4
|
+
import type { Issue } from '../types.ts';
|
|
5
|
+
import { getElementHtml } from '../utils/get-element-html.js';
|
|
6
|
+
import { isNonEmpty } from '../utils/is-non-empty.js';
|
|
7
7
|
|
|
8
8
|
export interface AccentedDialog extends HTMLElement {
|
|
9
9
|
issues: Signal<Array<Issue>> | undefined;
|
|
10
10
|
element: Element | undefined;
|
|
11
11
|
showModal: () => void;
|
|
12
|
+
open: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
// We want Accented to not throw an error in Node, and use static imports,
|
|
15
16
|
// so we can't export `class extends HTMLElement` because HTMLElement is not available in Node.
|
|
16
|
-
export
|
|
17
|
+
export const getAccentedDialog = () => {
|
|
17
18
|
const dialogTemplate = document.createElement('template');
|
|
18
19
|
dialogTemplate.innerHTML = `
|
|
19
20
|
<dialog dir="ltr" lang="en" aria-labelledby="title">
|
|
@@ -241,19 +242,17 @@ export default () => {
|
|
|
241
242
|
`);
|
|
242
243
|
|
|
243
244
|
return class extends HTMLElement implements AccentedDialog {
|
|
244
|
-
#disposeOfEffect: (() => void) | undefined;
|
|
245
|
-
|
|
246
245
|
#abortController: AbortController | undefined;
|
|
247
246
|
|
|
248
247
|
issues: Signal<Array<Issue>> | undefined;
|
|
249
248
|
|
|
250
249
|
element: Element | undefined;
|
|
251
250
|
|
|
252
|
-
|
|
251
|
+
open = false;
|
|
253
252
|
|
|
254
253
|
constructor() {
|
|
254
|
+
super();
|
|
255
255
|
try {
|
|
256
|
-
super();
|
|
257
256
|
this.attachShadow({ mode: 'open' });
|
|
258
257
|
const content = dialogTemplate.content.cloneNode(true);
|
|
259
258
|
if (this.shadowRoot) {
|
|
@@ -272,106 +271,104 @@ export default () => {
|
|
|
272
271
|
const dialog = shadowRoot.querySelector('dialog');
|
|
273
272
|
const closeButton = shadowRoot.querySelector('#close');
|
|
274
273
|
this.#abortController = new AbortController();
|
|
275
|
-
closeButton?.addEventListener(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
dialog?.addEventListener('click', (event) => {
|
|
284
|
-
try {
|
|
285
|
-
this.#onDialogClick(event);
|
|
286
|
-
} catch (error) {
|
|
287
|
-
logAndRethrow(error);
|
|
288
|
-
}
|
|
289
|
-
}, { signal: this.#abortController.signal });
|
|
290
|
-
|
|
291
|
-
dialog?.addEventListener('keydown', (event) => {
|
|
292
|
-
try {
|
|
293
|
-
if (event.key === 'Escape') {
|
|
294
|
-
event.stopPropagation();
|
|
274
|
+
closeButton?.addEventListener(
|
|
275
|
+
'click',
|
|
276
|
+
() => {
|
|
277
|
+
try {
|
|
278
|
+
dialog?.close();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
logAndRethrow(error);
|
|
295
281
|
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
282
|
+
},
|
|
283
|
+
{ signal: this.#abortController.signal },
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
dialog?.addEventListener(
|
|
287
|
+
'click',
|
|
288
|
+
(event) => {
|
|
289
|
+
try {
|
|
290
|
+
this.#onDialogClick(event);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logAndRethrow(error);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{ signal: this.#abortController.signal },
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
dialog?.addEventListener(
|
|
299
|
+
'keydown',
|
|
300
|
+
(event) => {
|
|
301
|
+
try {
|
|
302
|
+
if (event.key === 'Escape') {
|
|
303
|
+
event.stopPropagation();
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
logAndRethrow(error);
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{ signal: this.#abortController.signal },
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
if (this.issues) {
|
|
313
|
+
const issues = this.issues.value;
|
|
314
|
+
const issuesList = shadowRoot.getElementById('issues');
|
|
315
|
+
if (issuesList) {
|
|
316
|
+
issuesList.innerHTML = '';
|
|
317
|
+
for (const issue of issues) {
|
|
318
|
+
const issueContent = issueTemplate.content.cloneNode(true) as Element;
|
|
319
|
+
const title = issueContent.querySelector('a');
|
|
320
|
+
const impact = issueContent.querySelector('.impact');
|
|
321
|
+
const description = issueContent.querySelector('.description');
|
|
322
|
+
if (title && impact && description) {
|
|
323
|
+
title.textContent = `${issue.title} (${issue.id})`;
|
|
324
|
+
title.href = issue.url;
|
|
325
|
+
|
|
326
|
+
impact.textContent = `User impact: ${issue.impact}`;
|
|
327
|
+
impact.setAttribute('data-impact', String(issue.impact));
|
|
328
|
+
|
|
329
|
+
const descriptionItems = issue.description.split(/\n\s*/);
|
|
330
|
+
const descriptionContent = descriptionTemplate.content.cloneNode(true) as Element;
|
|
331
|
+
const descriptionTitle = descriptionContent.querySelector('span');
|
|
332
|
+
const descriptionList = descriptionContent.querySelector('ul');
|
|
333
|
+
if (
|
|
334
|
+
descriptionTitle &&
|
|
335
|
+
descriptionList &&
|
|
336
|
+
isNonEmpty(descriptionItems) &&
|
|
337
|
+
descriptionItems.length > 1
|
|
338
|
+
) {
|
|
339
|
+
descriptionTitle.textContent = descriptionItems[0];
|
|
340
|
+
for (const descriptionItem of descriptionItems.slice(1)) {
|
|
341
|
+
const li = document.createElement('li');
|
|
342
|
+
li.textContent = descriptionItem;
|
|
343
|
+
descriptionList.appendChild(li);
|
|
331
344
|
}
|
|
345
|
+
description.appendChild(descriptionContent);
|
|
332
346
|
}
|
|
333
|
-
issuesList.appendChild(issueContent);
|
|
334
347
|
}
|
|
348
|
+
issuesList.appendChild(issueContent);
|
|
335
349
|
}
|
|
336
350
|
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const updateElementHtml = () => {
|
|
340
|
-
if (this.element) {
|
|
341
|
-
const elementHtmlContainer = shadowRoot.getElementById('element-html');
|
|
342
|
-
if (elementHtmlContainer) {
|
|
343
|
-
elementHtmlContainer.textContent = getElementHtml(this.element);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
updateElementHtml();
|
|
351
|
+
}
|
|
349
352
|
|
|
350
|
-
this.#elementMutationObserver = new MutationObserver(() => {
|
|
351
|
-
try {
|
|
352
|
-
updateElementHtml();
|
|
353
|
-
} catch (error) {
|
|
354
|
-
logAndRethrow(error);
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
353
|
if (this.element) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.#elementMutationObserver.observe(this.element, {
|
|
363
|
-
attributes: true,
|
|
364
|
-
childList: true
|
|
365
|
-
});
|
|
354
|
+
const elementHtmlContainer = shadowRoot.getElementById('element-html');
|
|
355
|
+
if (elementHtmlContainer) {
|
|
356
|
+
elementHtmlContainer.textContent = getElementHtml(this.element);
|
|
357
|
+
}
|
|
366
358
|
}
|
|
367
359
|
|
|
368
|
-
dialog?.addEventListener(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
360
|
+
dialog?.addEventListener(
|
|
361
|
+
'close',
|
|
362
|
+
() => {
|
|
363
|
+
try {
|
|
364
|
+
this.open = false;
|
|
365
|
+
this.dispatchEvent(new Event('close'));
|
|
366
|
+
} catch (error) {
|
|
367
|
+
logAndRethrow(error);
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
{ signal: this.#abortController.signal },
|
|
371
|
+
);
|
|
375
372
|
}
|
|
376
373
|
} catch (error) {
|
|
377
374
|
logAndRethrow(error);
|
|
@@ -380,15 +377,9 @@ export default () => {
|
|
|
380
377
|
|
|
381
378
|
disconnectedCallback() {
|
|
382
379
|
try {
|
|
383
|
-
if (this.#disposeOfEffect) {
|
|
384
|
-
this.#disposeOfEffect();
|
|
385
|
-
}
|
|
386
380
|
if (this.#abortController) {
|
|
387
381
|
this.#abortController.abort();
|
|
388
382
|
}
|
|
389
|
-
if (this.#elementMutationObserver) {
|
|
390
|
-
this.#elementMutationObserver.disconnect();
|
|
391
|
-
}
|
|
392
383
|
} catch (error) {
|
|
393
384
|
logAndRethrow(error);
|
|
394
385
|
}
|
|
@@ -399,13 +390,18 @@ export default () => {
|
|
|
399
390
|
const dialog = this.shadowRoot.querySelector('dialog');
|
|
400
391
|
if (dialog) {
|
|
401
392
|
dialog.showModal();
|
|
393
|
+
this.open = true;
|
|
402
394
|
}
|
|
403
395
|
}
|
|
404
396
|
}
|
|
405
397
|
|
|
406
398
|
#onDialogClick(event: MouseEvent) {
|
|
407
399
|
const dialog = event.currentTarget as HTMLDialogElement;
|
|
408
|
-
if (
|
|
400
|
+
if (
|
|
401
|
+
!dialog ||
|
|
402
|
+
typeof dialog.getBoundingClientRect !== 'function' ||
|
|
403
|
+
typeof dialog.close !== 'function'
|
|
404
|
+
) {
|
|
409
405
|
return;
|
|
410
406
|
}
|
|
411
407
|
const rect = dialog.getBoundingClientRect();
|