accented 0.0.2 → 1.0.1
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/NOTICE +14 -0
- package/README.md +44 -187
- package/dist/accented.d.ts +8 -8
- package/dist/accented.d.ts.map +1 -1
- package/dist/accented.js +37 -30
- package/dist/accented.js.map +1 -1
- package/dist/common/strings.d.ts +2 -0
- package/dist/common/strings.d.ts.map +1 -0
- package/dist/common/strings.js +2 -0
- package/dist/common/strings.js.map +1 -0
- package/dist/common/tokens.d.ts +7 -0
- package/dist/common/tokens.d.ts.map +1 -0
- package/dist/common/tokens.js +8 -0
- package/dist/common/tokens.js.map +1 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.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 +73 -31
- package/dist/dom-updater.js.map +1 -1
- package/dist/elements/accented-dialog.d.ts +18 -10
- package/dist/elements/accented-dialog.d.ts.map +1 -1
- package/dist/elements/accented-dialog.js +116 -95
- package/dist/elements/accented-dialog.js.map +1 -1
- package/dist/elements/accented-trigger.d.ts +14 -9
- package/dist/elements/accented-trigger.d.ts.map +1 -1
- package/dist/elements/accented-trigger.js +83 -24
- package/dist/elements/accented-trigger.js.map +1 -1
- package/dist/fullscreen-listener.d.ts +2 -0
- package/dist/fullscreen-listener.d.ts.map +1 -0
- package/dist/fullscreen-listener.js +17 -0
- package/dist/fullscreen-listener.js.map +1 -0
- 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 +4 -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 +76 -43
- 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 +3 -2
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +5 -3
- 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 +140 -49
- 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 +3 -0
- package/dist/utils/are-elements-with-issues-equal.d.ts.map +1 -0
- package/dist/utils/are-elements-with-issues-equal.js +5 -0
- package/dist/utils/are-elements-with-issues-equal.js.map +1 -0
- 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 +8 -5
- package/dist/utils/deep-merge.js.map +1 -1
- package/dist/utils/dom-helpers.d.ts +9 -0
- package/dist/utils/dom-helpers.d.ts.map +1 -0
- package/dist/utils/dom-helpers.js +34 -0
- package/dist/utils/dom-helpers.js.map +1 -0
- 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 +64 -16
- package/dist/utils/get-element-position.js.map +1 -1
- package/dist/utils/get-parent.d.ts +2 -0
- package/dist/utils/get-parent.d.ts.map +1 -0
- package/dist/utils/get-parent.js +12 -0
- package/dist/utils/get-parent.js.map +1 -0
- 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 +10 -6
- 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 +10 -0
- package/dist/utils/shadow-dom-aware-mutation-observer.d.ts.map +1 -0
- package/dist/utils/shadow-dom-aware-mutation-observer.js +61 -0
- package/dist/utils/shadow-dom-aware-mutation-observer.js.map +1 -0
- 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 +15 -2
- 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 +25 -10
- 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 +56 -24
- 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 +16 -8
- package/src/accented.test.ts +2 -2
- package/src/accented.ts +45 -34
- package/src/common/strings.ts +2 -0
- package/src/common/tokens.ts +10 -0
- package/src/constants.ts +2 -1
- package/src/dom-updater.ts +87 -34
- package/src/elements/accented-dialog.ts +163 -123
- package/src/elements/accented-trigger.ts +128 -50
- package/src/fullscreen-listener.ts +21 -0
- 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 +113 -57
- package/src/scroll-listeners.ts +27 -19
- package/src/state.ts +27 -16
- package/src/task-queue.test.ts +5 -4
- package/src/task-queue.ts +8 -6
- package/src/types.ts +179 -76
- package/src/utils/are-elements-with-issues-equal.ts +11 -0
- package/src/utils/are-issue-sets-equal.test.ts +6 -7
- package/src/utils/are-issue-sets-equal.ts +8 -6
- package/src/utils/containing-blocks.ts +60 -0
- package/src/utils/contains.test.ts +54 -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 +14 -8
- package/src/utils/dom-helpers.ts +42 -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 +84 -16
- package/src/utils/get-parent.ts +14 -0
- 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 +15 -7
- 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 +75 -0
- package/src/utils/supports-anchor-positioning.ts +19 -3
- package/src/utils/transform-violations.test.ts +29 -25
- package/src/utils/transform-violations.ts +32 -12
- package/src/utils/update-elements-with-issues.test.ts +145 -53
- package/src/utils/update-elements-with-issues.ts +123 -54
- package/src/validate-options.ts +154 -14
package/dist/validate-options.js
CHANGED
|
@@ -1,9 +1,91 @@
|
|
|
1
1
|
import { allowedAxeOptions } from './types.js';
|
|
2
|
+
import { isNode, isNodeList } from './utils/dom-helpers.js';
|
|
3
|
+
function isSelector(contextFragment) {
|
|
4
|
+
return (typeof contextFragment === 'string' ||
|
|
5
|
+
isNode(contextFragment) ||
|
|
6
|
+
'fromShadowDom' in contextFragment);
|
|
7
|
+
}
|
|
8
|
+
function validateSelector(selector) {
|
|
9
|
+
if (typeof selector === 'string') {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (isNode(selector)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if ('fromShadowDom' in selector) {
|
|
16
|
+
if (!Array.isArray(selector.fromShadowDom) ||
|
|
17
|
+
selector.fromShadowDom.length < 2 ||
|
|
18
|
+
!selector.fromShadowDom.every((item) => typeof item === 'string')) {
|
|
19
|
+
throw new TypeError(`Accented: invalid argument. \`fromShadowDom\` must be an array of strings with at least 2 elements. It’s currently set to ${selector.fromShadowDom}.`);
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const neverSelector = selector;
|
|
24
|
+
throw new TypeError(`Accented: invalid argument. The selector must be one of: string, Node, or an object with a \`fromShadowDom\` property. It’s currently set to ${neverSelector}.`);
|
|
25
|
+
}
|
|
26
|
+
function isSelectorList(contextFragment) {
|
|
27
|
+
return ((typeof contextFragment === 'object' && isNodeList(contextFragment)) ||
|
|
28
|
+
(Array.isArray(contextFragment) && contextFragment.every((item) => isSelector(item))));
|
|
29
|
+
}
|
|
30
|
+
function validateSelectorList(selectorList) {
|
|
31
|
+
if (isNodeList(selectorList)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(selectorList)) {
|
|
35
|
+
for (const selector of selectorList) {
|
|
36
|
+
validateSelector(selector);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const neverSelectorList = selectorList;
|
|
41
|
+
throw new TypeError(`Accented: invalid argument. The selector list must either be a NodeList or an array. It’s currently set to ${neverSelectorList}.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isContextProp(contextFragment) {
|
|
45
|
+
return isSelector(contextFragment) || isSelectorList(contextFragment);
|
|
46
|
+
}
|
|
47
|
+
function validateContextProp(context) {
|
|
48
|
+
if (isSelector(context)) {
|
|
49
|
+
validateSelector(context);
|
|
50
|
+
}
|
|
51
|
+
else if (isSelectorList(context)) {
|
|
52
|
+
validateSelectorList(context);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const neverContext = context;
|
|
56
|
+
throw new TypeError(`Accented: invalid argument. The context property must either be a selector or a selector list. It’s currently set to ${neverContext}.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function isContextObject(contextFragment) {
|
|
60
|
+
return (typeof contextFragment === 'object' &&
|
|
61
|
+
contextFragment !== null &&
|
|
62
|
+
('include' in contextFragment || 'exclude' in contextFragment));
|
|
63
|
+
}
|
|
64
|
+
function validateContextObject(contextObject) {
|
|
65
|
+
if ('include' in contextObject && contextObject.include !== undefined) {
|
|
66
|
+
validateContextProp(contextObject.include);
|
|
67
|
+
}
|
|
68
|
+
if ('exclude' in contextObject && contextObject.exclude !== undefined) {
|
|
69
|
+
validateContextProp(contextObject.exclude);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function validateContext(context) {
|
|
73
|
+
if (isContextProp(context)) {
|
|
74
|
+
validateContextProp(context);
|
|
75
|
+
}
|
|
76
|
+
else if (isContextObject(context)) {
|
|
77
|
+
validateContextObject(context);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const neverContext = context;
|
|
81
|
+
throw new TypeError(`Accented: invalid context argument. It’s currently set to ${neverContext}.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
2
84
|
// The space of valid CSS and HTML names is wider than this,
|
|
3
85
|
// but with Unicode it gets complicated quickly, so I'm sticking to only allowing
|
|
4
86
|
// lowercase alphanumeric names that possibly contain dashes that start with a letter.
|
|
5
87
|
const nameRegex = /^[a-z]([a-z0-9]|-)+$/;
|
|
6
|
-
export
|
|
88
|
+
export function validateOptions(options) {
|
|
7
89
|
if (typeof options !== 'object' || options === null) {
|
|
8
90
|
throw new TypeError(`Accented: invalid argument. The options parameter must be an object if provided. It’s currently set to ${options}.`);
|
|
9
91
|
}
|
|
@@ -11,7 +93,8 @@ export default function validateOptions(options) {
|
|
|
11
93
|
if (typeof options.throttle !== 'object' || options.throttle === null) {
|
|
12
94
|
throw new TypeError(`Accented: invalid argument. \`throttle\` option must be an object if provided. It’s currently set to ${options.throttle}.`);
|
|
13
95
|
}
|
|
14
|
-
if (options.throttle.wait !== undefined &&
|
|
96
|
+
if (options.throttle.wait !== undefined &&
|
|
97
|
+
(typeof options.throttle.wait !== 'number' || options.throttle.wait < 0)) {
|
|
15
98
|
throw new TypeError(`Accented: invalid argument. \`throttle.wait\` option must be a non-negative number if provided. It’s currently set to ${options.throttle.wait}.`);
|
|
16
99
|
}
|
|
17
100
|
}
|
|
@@ -26,17 +109,21 @@ export default function validateOptions(options) {
|
|
|
26
109
|
if (options.callback !== undefined && typeof options.callback !== 'function') {
|
|
27
110
|
throw new TypeError(`Accented: invalid argument. \`callback\` option must be a function if provided. It’s currently set to ${options.callback}.`);
|
|
28
111
|
}
|
|
29
|
-
if (options.name !== undefined &&
|
|
112
|
+
if (options.name !== undefined &&
|
|
113
|
+
(typeof options.name !== 'string' || !options.name.match(nameRegex))) {
|
|
30
114
|
throw new TypeError(`Accented: invalid argument. \`name\` option must be a string that starts with a lowercase letter and only contains lowercase alphanumeric characters and dashes. It’s currently set to ${options.name}.`);
|
|
31
115
|
}
|
|
32
116
|
if (options.axeOptions !== undefined) {
|
|
33
117
|
if (typeof options.axeOptions !== 'object' || options.axeOptions === null) {
|
|
34
118
|
throw new TypeError(`Accented: invalid argument. \`axeOptions\` option must be an object if provided. It’s currently set to ${options.axeOptions}.`);
|
|
35
119
|
}
|
|
36
|
-
const unsupportedKeys = Object.keys(options.axeOptions).filter(key => !allowedAxeOptions.includes(key));
|
|
120
|
+
const unsupportedKeys = Object.keys(options.axeOptions).filter((key) => !allowedAxeOptions.includes(key));
|
|
37
121
|
if (unsupportedKeys.length > 0) {
|
|
38
122
|
throw new TypeError(`Accented: invalid argument. \`axeOptions\` contains the following unsupported keys: ${unsupportedKeys.join(', ')}. Valid options are: ${allowedAxeOptions.join(', ')}.`);
|
|
39
123
|
}
|
|
40
124
|
}
|
|
125
|
+
if (options.context !== undefined) {
|
|
126
|
+
validateContext(options.context);
|
|
127
|
+
}
|
|
41
128
|
}
|
|
42
129
|
//# sourceMappingURL=validate-options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-options.js","sourceRoot":"","sources":["../src/validate-options.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate-options.js","sourceRoot":"","sources":["../src/validate-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAS/C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE5D,SAAS,UAAU,CAAC,eAAwB;IAC1C,OAAO,CACL,OAAO,eAAe,KAAK,QAAQ;QACnC,MAAM,CAAC,eAAe,CAAC;QACvB,eAAe,IAAI,eAAe,CACnC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAkB;IAC1C,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO;IACT,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,eAAe,IAAI,QAAQ,EAAE,CAAC;QAChC,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;YACtC,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;YACjC,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EACjE,CAAC;YACD,MAAM,IAAI,SAAS,CACjB,6HAA6H,QAAQ,CAAC,aAAa,GAAG,CACvJ,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,aAAa,GAAU,QAAQ,CAAC;IACtC,MAAM,IAAI,SAAS,CACjB,gJAAgJ,aAAa,GAAG,CACjK,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,eAAwB;IAC9C,OAAO,CACL,CAAC,OAAO,eAAe,KAAK,QAAQ,IAAI,UAAU,CAAC,eAAe,CAAC,CAAC;QACpE,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CACtF,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,YAA0B;IACtD,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;YACpC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,iBAAiB,GAAU,YAAY,CAAC;QAC9C,MAAM,IAAI,SAAS,CACjB,8GAA8G,iBAAiB,GAAG,CACnI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,eAAwB;IAC7C,OAAO,UAAU,CAAC,eAAe,CAAC,IAAI,cAAc,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAgC;IAC3D,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;SAAM,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAAU,OAAO,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,wHAAwH,YAAY,GAAG,CACxI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,eAAwB;IAC/C,OAAO,CACL,OAAO,eAAe,KAAK,QAAQ;QACnC,eAAe,KAAK,IAAI;QACxB,CAAC,SAAS,IAAI,eAAe,IAAI,SAAS,IAAI,eAAe,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,aAA4B;IACzD,IAAI,SAAS,IAAI,aAAa,IAAI,aAAa,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACtE,mBAAmB,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,SAAS,IAAI,aAAa,IAAI,aAAa,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACtE,mBAAmB,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAgB;IACvC,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;SAAM,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAAU,OAAO,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,6DAA6D,YAAY,GAAG,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,4DAA4D;AAC5D,iFAAiF;AACjF,sFAAsF;AACtF,MAAM,SAAS,GAAG,sBAAsB,CAAC;AAEzC,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CACjB,0GAA0G,OAAO,GAAG,CACrH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtE,MAAM,IAAI,SAAS,CACjB,wGAAwG,OAAO,CAAC,QAAQ,GAAG,CAC5H,CAAC;QACJ,CAAC;QACD,IACE,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS;YACnC,CAAC,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,SAAS,CACjB,yHAAyH,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,CAClJ,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YAClE,MAAM,IAAI,SAAS,CACjB,sGAAsG,OAAO,CAAC,MAAM,GAAG,CACxH,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACxF,OAAO,CAAC,IAAI,CACV,4GAA4G,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,CACtI,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC7E,MAAM,IAAI,SAAS,CACjB,yGAAyG,OAAO,CAAC,QAAQ,GAAG,CAC7H,CAAC;IACJ,CAAC;IACD,IACE,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EACpE,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,0LAA0L,OAAO,CAAC,IAAI,GAAG,CAC1M,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC1E,MAAM,IAAI,SAAS,CACjB,0GAA0G,OAAO,CAAC,UAAU,GAAG,CAChI,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAC5D,CAAC,GAAG,EAAE,EAAE,CAAC,CAAE,iBAA8C,CAAC,QAAQ,CAAC,GAAG,CAAC,CACxE,CAAC;QACF,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,SAAS,CACjB,uFAAuF,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACzK,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "accented",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A frontend library for continuous accessibility testing and issue highlighting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/accented.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
-
"src"
|
|
9
|
+
"src",
|
|
10
|
+
"NOTICE"
|
|
10
11
|
],
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"keywords": [
|
|
16
17
|
"accessibility",
|
|
17
18
|
"a11y",
|
|
19
|
+
"a11y-testing",
|
|
18
20
|
"axe",
|
|
19
21
|
"axe-core"
|
|
20
22
|
],
|
|
@@ -23,17 +25,23 @@
|
|
|
23
25
|
"bugs": {
|
|
24
26
|
"url": "https://github.com/pomerantsev/accented/issues"
|
|
25
27
|
},
|
|
26
|
-
"homepage": "https://
|
|
28
|
+
"homepage": "https://accented.dev",
|
|
27
29
|
"dependencies": {
|
|
28
|
-
"@preact/signals-core": "^1.
|
|
29
|
-
"axe-core": "^4.10.
|
|
30
|
+
"@preact/signals-core": "^1.11.0",
|
|
31
|
+
"axe-core": "^4.10.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jsdom": "^21.1.7",
|
|
35
|
+
"jsdom": "^26.1.0"
|
|
30
36
|
},
|
|
31
37
|
"scripts": {
|
|
32
|
-
"build": "tsc",
|
|
38
|
+
"build": "pnpm copyCommon && tsc",
|
|
39
|
+
"copyCommon": "node --import tsx ./scripts/copy-common.ts",
|
|
40
|
+
"watchCommon": "node --import tsx ./scripts/watch-common.ts",
|
|
33
41
|
"checkBuiltFiles": "node --import tsx ./scripts/check-built-files.ts",
|
|
34
42
|
"checkImportsInBuiltFiles": "node ./dist/accented.js",
|
|
35
43
|
"typecheckTests": "tsc -p ./tsconfig.test.json",
|
|
36
|
-
"watch": "tsc --watch",
|
|
44
|
+
"watch": "pnpm copyCommon && (pnpm watchCommon & tsc --watch)",
|
|
37
45
|
"test": "node --test --import tsx \"./**/*.test.ts\""
|
|
38
46
|
}
|
|
39
47
|
}
|
package/src/accented.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import {suite, test} from 'node:test';
|
|
3
2
|
import type { Mock } from 'node:test';
|
|
3
|
+
import { suite, test } from 'node:test';
|
|
4
4
|
|
|
5
|
-
import accented from './accented
|
|
5
|
+
import { accented } from './accented';
|
|
6
6
|
|
|
7
7
|
suite('Accented', () => {
|
|
8
8
|
test('runs without errors in NodeJS and issues a warning and a trace (it’s meant to be a no-op on the server side)', (t) => {
|
package/src/accented.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import setupResizeListener from './resize-listener.js';
|
|
8
|
-
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';
|
|
9
10
|
import { enabled, extendedElementsWithIssues } from './state.js';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import supportsAnchorPositioning from './utils/supports-anchor-positioning.js';
|
|
14
|
-
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';
|
|
15
16
|
|
|
16
17
|
export type { AccentedOptions, DisableAccented };
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Enables highlighting of
|
|
20
|
+
* Enables the continuous scanning and highlighting of accessibility issues on the page.
|
|
20
21
|
*
|
|
21
|
-
* @param {AccentedOptions} options - The options object.
|
|
22
|
+
* @param {AccentedOptions} options - The options object (optional).
|
|
22
23
|
*
|
|
23
|
-
* @returns A `disable` function that
|
|
24
|
+
* @returns A `disable` function that takes no parameters. When called, disables the scanning and highlighting, and cleans up any changes that Accented has made to the page.
|
|
24
25
|
*
|
|
25
26
|
* @example
|
|
26
27
|
* accented();
|
|
@@ -34,30 +35,31 @@ export type { AccentedOptions, DisableAccented };
|
|
|
34
35
|
* wait: 500,
|
|
35
36
|
* leading: false
|
|
36
37
|
* },
|
|
37
|
-
* callback: ({ elementsWithIssues,
|
|
38
|
-
*
|
|
39
|
-
*
|
|
38
|
+
* callback: ({ elementsWithIssues, performance }) => {
|
|
39
|
+
* console.log('Elements with issues:', elementsWithIssues);
|
|
40
|
+
* console.log('Total blocking time:', performance.totalBlockingTime);
|
|
40
41
|
* }
|
|
41
42
|
* });
|
|
42
43
|
*/
|
|
43
|
-
export
|
|
44
|
-
|
|
44
|
+
export function accented(options: AccentedOptions = {}): DisableAccented {
|
|
45
45
|
validateOptions(options);
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
48
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
49
|
-
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
|
+
);
|
|
50
52
|
console.trace();
|
|
51
53
|
return () => {};
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
const defaultOutput: Required<AccentedOptions['output']> = {
|
|
55
|
-
console: true
|
|
57
|
+
console: true,
|
|
56
58
|
};
|
|
57
59
|
|
|
58
60
|
const defaultThrottle: Required<AccentedOptions['throttle']> = {
|
|
59
61
|
wait: 1000,
|
|
60
|
-
leading: true
|
|
62
|
+
leading: true,
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
// IMPORTANT: when changing any of the properties or values, also do the following:
|
|
@@ -66,35 +68,43 @@ export default function accented(options: AccentedOptions = {}): DisableAccented
|
|
|
66
68
|
// * update examples in the accented() function JSDoc;
|
|
67
69
|
// * update examples in the Readme.
|
|
68
70
|
const defaultOptions: Required<AccentedOptions> = {
|
|
69
|
-
|
|
71
|
+
context: document,
|
|
70
72
|
axeOptions: {},
|
|
71
73
|
name: 'accented',
|
|
72
74
|
output: defaultOutput,
|
|
73
75
|
throttle: defaultThrottle,
|
|
74
|
-
callback: () => {}
|
|
76
|
+
callback: () => {},
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
const {
|
|
79
|
+
const { context, axeOptions, name, output, throttle, callback } = deepMerge(
|
|
80
|
+
defaultOptions,
|
|
81
|
+
options,
|
|
82
|
+
);
|
|
78
83
|
|
|
79
84
|
if (enabled.value) {
|
|
80
|
-
// Add link to the recipes section of the docs (#56).
|
|
81
85
|
console.warn(
|
|
82
|
-
'You are trying to run the Accented library more than once. '
|
|
83
|
-
'This will likely lead to errors.'
|
|
86
|
+
'You are trying to run the Accented library more than once, which may lead to errors. See https://accented.dev/getting-started#run-once-per-page',
|
|
84
87
|
);
|
|
85
88
|
console.trace();
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
enabled.value = true;
|
|
89
92
|
|
|
93
|
+
initializeContainingBlockSupportSet();
|
|
90
94
|
registerElements(name);
|
|
91
95
|
|
|
92
|
-
const {disconnect: cleanupIntersectionObserver, intersectionObserver } =
|
|
93
|
-
|
|
96
|
+
const { disconnect: cleanupIntersectionObserver, intersectionObserver } =
|
|
97
|
+
setupIntersectionObserver();
|
|
98
|
+
const cleanupScanner = createScanner(name, context, axeOptions, throttle, callback);
|
|
94
99
|
const cleanupDomUpdater = createDomUpdater(name, intersectionObserver);
|
|
95
100
|
const cleanupLogger = output.console ? createLogger() : () => {};
|
|
96
|
-
const cleanupScrollListeners =
|
|
97
|
-
const cleanupResizeListener = supportsAnchorPositioning(window)
|
|
101
|
+
const cleanupScrollListeners = setupScrollListeners();
|
|
102
|
+
const cleanupResizeListener = supportsAnchorPositioning(window)
|
|
103
|
+
? () => {}
|
|
104
|
+
: setupResizeListener();
|
|
105
|
+
const cleanupFullscreenListener = supportsAnchorPositioning(window)
|
|
106
|
+
? () => {}
|
|
107
|
+
: setupFullscreenListener();
|
|
98
108
|
|
|
99
109
|
return () => {
|
|
100
110
|
try {
|
|
@@ -105,6 +115,7 @@ export default function accented(options: AccentedOptions = {}): DisableAccented
|
|
|
105
115
|
cleanupLogger();
|
|
106
116
|
cleanupScrollListeners();
|
|
107
117
|
cleanupResizeListener();
|
|
118
|
+
cleanupFullscreenListener();
|
|
108
119
|
if (cleanupIntersectionObserver) {
|
|
109
120
|
cleanupIntersectionObserver();
|
|
110
121
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const primaryColorDark = 'oklch(0.5 0.3 0)';
|
|
2
|
+
export const primaryColorLight = 'oklch(0.8 0.2 0)';
|
|
3
|
+
export const colorLight = 'oklch(0.98 0 0)';
|
|
4
|
+
export const colorDark = 'oklch(0.22 0 0)';
|
|
5
|
+
|
|
6
|
+
/* https://systemfontstack.com/ */
|
|
7
|
+
export const fontSystemSans =
|
|
8
|
+
'-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, Adwaita Sans, Cantarell, Ubuntu, roboto, noto, helvetica, arial, sans-serif';
|
|
9
|
+
export const fontSystemMono =
|
|
10
|
+
'Menlo, Consolas, Monaco, Adwaita Mono, Liberation Mono, Lucida Console, monospace';
|
package/src/constants.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export const accentedUrl = 'https://
|
|
1
|
+
export const accentedUrl = 'https://accented.dev';
|
|
2
2
|
export const issuesUrl = 'https://github.com/pomerantsev/accented/issues';
|
|
3
|
+
export const getAccentedElementNames = (name: string) => [`${name}-trigger`, `${name}-dialog`];
|
package/src/dom-updater.ts
CHANGED
|
@@ -1,20 +1,48 @@
|
|
|
1
1
|
import { effect } from '@preact/signals-core';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { primaryColorDark } from './common/tokens.js';
|
|
3
|
+
import { extendedElementsWithIssues, rootNodes } from './state.js';
|
|
4
|
+
import type { ExtendedElementWithIssues } from './types.ts';
|
|
5
|
+
import { areElementsWithIssuesEqual } from './utils/are-elements-with-issues-equal.js';
|
|
6
|
+
import { isDocument, isDocumentFragment, isShadowRoot } from './utils/dom-helpers.js';
|
|
7
|
+
import { getParent } from './utils/get-parent.js';
|
|
8
|
+
import { supportsAnchorPositioning } from './utils/supports-anchor-positioning.js';
|
|
5
9
|
|
|
6
|
-
|
|
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) {
|
|
7
35
|
const attrName = `data-${name}`;
|
|
8
36
|
|
|
9
|
-
function getAnchorNames
|
|
37
|
+
function getAnchorNames(anchorNameValue: string) {
|
|
10
38
|
return anchorNameValue
|
|
11
39
|
.split(',')
|
|
12
|
-
.map(anchorName => anchorName.trim())
|
|
13
|
-
.filter(anchorName => anchorName.startsWith('--'));
|
|
40
|
+
.map((anchorName) => anchorName.trim())
|
|
41
|
+
.filter((anchorName) => anchorName.startsWith('--'));
|
|
14
42
|
}
|
|
15
43
|
|
|
16
|
-
function setAnchorName
|
|
17
|
-
const anchorNameValue =
|
|
44
|
+
function setAnchorName(elementWithIssues: ExtendedElementWithIssues) {
|
|
45
|
+
const { element, id, anchorNameValue } = elementWithIssues;
|
|
18
46
|
const anchorNames = getAnchorNames(anchorNameValue);
|
|
19
47
|
if (anchorNames.length > 0) {
|
|
20
48
|
element.style.setProperty('anchor-name', `${anchorNameValue}, --${name}-anchor-${id}`);
|
|
@@ -23,28 +51,30 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
23
51
|
}
|
|
24
52
|
}
|
|
25
53
|
|
|
26
|
-
function removeAnchorName
|
|
27
|
-
const anchorNameValue =
|
|
54
|
+
function removeAnchorName(elementWithIssues: ExtendedElementWithIssues) {
|
|
55
|
+
const { element, anchorNameValue } = elementWithIssues;
|
|
28
56
|
const anchorNames = getAnchorNames(anchorNameValue);
|
|
29
|
-
|
|
30
|
-
|
|
57
|
+
if (anchorNames.length > 0) {
|
|
58
|
+
element.style.setProperty('anchor-name', anchorNames.join(', '));
|
|
59
|
+
} else {
|
|
31
60
|
element.style.removeProperty('anchor-name');
|
|
32
|
-
} else if (anchorNames.length > 1 && index > -1) {
|
|
33
|
-
element.style.setProperty('anchor-name', anchorNames.filter((_, i) => i !== index).join(', '));
|
|
34
61
|
}
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
function setIssues
|
|
64
|
+
function setIssues(extendedElementsWithIssues: Array<ExtendedElementWithIssues>) {
|
|
38
65
|
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
66
|
+
if (elementWithIssues.skipRender) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
39
69
|
elementWithIssues.element.setAttribute(attrName, elementWithIssues.id.toString());
|
|
40
70
|
if (supportsAnchorPositioning(window)) {
|
|
41
|
-
setAnchorName(elementWithIssues
|
|
71
|
+
setAnchorName(elementWithIssues);
|
|
42
72
|
}
|
|
43
73
|
|
|
44
|
-
if (elementWithIssues.element
|
|
45
|
-
elementWithIssues.element.insertAdjacentElement('afterend', elementWithIssues.trigger);
|
|
46
|
-
} else {
|
|
74
|
+
if (shouldInsertTriggerInsideElement(elementWithIssues.element)) {
|
|
47
75
|
elementWithIssues.element.insertAdjacentElement('beforeend', elementWithIssues.trigger);
|
|
76
|
+
} else {
|
|
77
|
+
elementWithIssues.element.insertAdjacentElement('afterend', elementWithIssues.trigger);
|
|
48
78
|
}
|
|
49
79
|
if (intersectionObserver) {
|
|
50
80
|
intersectionObserver.observe(elementWithIssues.element);
|
|
@@ -52,11 +82,14 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
52
82
|
}
|
|
53
83
|
}
|
|
54
84
|
|
|
55
|
-
function removeIssues
|
|
85
|
+
function removeIssues(extendedElementsWithIssues: Array<ExtendedElementWithIssues>) {
|
|
56
86
|
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
87
|
+
if (elementWithIssues.skipRender) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
57
90
|
elementWithIssues.element.removeAttribute(attrName);
|
|
58
91
|
if (supportsAnchorPositioning(window)) {
|
|
59
|
-
removeAnchorName(elementWithIssues
|
|
92
|
+
removeAnchorName(elementWithIssues);
|
|
60
93
|
}
|
|
61
94
|
elementWithIssues.trigger.remove();
|
|
62
95
|
if (intersectionObserver) {
|
|
@@ -69,8 +102,10 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
69
102
|
stylesheet.replaceSync(`
|
|
70
103
|
@layer ${name} {
|
|
71
104
|
:root {
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
/* Ensure that the primary / secondary color combination meets WCAG 1.4.3 Contrast (Minimum) */
|
|
106
|
+
/* OKLCH stuff: https://oklch.com/ */
|
|
107
|
+
--${name}-primary-color: ${primaryColorDark};
|
|
108
|
+
--${name}-secondary-color: oklch(0.98 0 0);
|
|
74
109
|
--${name}-outline-width: 2px;
|
|
75
110
|
--${name}-outline-style: solid;
|
|
76
111
|
}
|
|
@@ -86,19 +121,37 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
86
121
|
|
|
87
122
|
let previousExtendedElementsWithIssues: Array<ExtendedElementWithIssues> = [];
|
|
88
123
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
124
|
+
let previousRootNodes: Set<Node> = new Set();
|
|
125
|
+
|
|
126
|
+
const disposeOfStyleSheetsEffect = effect(() => {
|
|
127
|
+
const newRootNodes = rootNodes.value;
|
|
128
|
+
const addedRootNodes = [...newRootNodes].filter((rootNode) => !previousRootNodes.has(rootNode));
|
|
129
|
+
const removedRootNodes = [...previousRootNodes].filter(
|
|
130
|
+
(rootNode) => !newRootNodes.has(rootNode),
|
|
131
|
+
);
|
|
132
|
+
for (const rootNode of addedRootNodes) {
|
|
133
|
+
if (isDocument(rootNode) || (isDocumentFragment(rootNode) && isShadowRoot(rootNode))) {
|
|
134
|
+
rootNode.adoptedStyleSheets.push(stylesheet);
|
|
135
|
+
}
|
|
93
136
|
}
|
|
94
|
-
|
|
137
|
+
for (const rootNode of removedRootNodes) {
|
|
138
|
+
if (isDocument(rootNode) || (isDocumentFragment(rootNode) && isShadowRoot(rootNode))) {
|
|
139
|
+
rootNode.adoptedStyleSheets.splice(rootNode.adoptedStyleSheets.indexOf(stylesheet), 1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
previousRootNodes = newRootNodes;
|
|
143
|
+
});
|
|
95
144
|
|
|
96
145
|
const disposeOfElementsEffect = effect(() => {
|
|
97
|
-
const added = extendedElementsWithIssues.value.filter(elementWithIssues => {
|
|
98
|
-
return !previousExtendedElementsWithIssues.some(previousElementWithIssues =>
|
|
146
|
+
const added = extendedElementsWithIssues.value.filter((elementWithIssues) => {
|
|
147
|
+
return !previousExtendedElementsWithIssues.some((previousElementWithIssues) =>
|
|
148
|
+
areElementsWithIssuesEqual(previousElementWithIssues, elementWithIssues),
|
|
149
|
+
);
|
|
99
150
|
});
|
|
100
|
-
const removed = previousExtendedElementsWithIssues.filter(previousElementWithIssues => {
|
|
101
|
-
return !extendedElementsWithIssues.value.some(elementWithIssues =>
|
|
151
|
+
const removed = previousExtendedElementsWithIssues.filter((previousElementWithIssues) => {
|
|
152
|
+
return !extendedElementsWithIssues.value.some((elementWithIssues) =>
|
|
153
|
+
areElementsWithIssuesEqual(elementWithIssues, previousElementWithIssues),
|
|
154
|
+
);
|
|
102
155
|
});
|
|
103
156
|
removeIssues(removed);
|
|
104
157
|
setIssues(added);
|
|
@@ -106,7 +159,7 @@ export default function createDomUpdater(name: string, intersectionObserver?: In
|
|
|
106
159
|
});
|
|
107
160
|
|
|
108
161
|
return () => {
|
|
109
|
-
|
|
162
|
+
disposeOfStyleSheetsEffect();
|
|
110
163
|
disposeOfElementsEffect();
|
|
111
164
|
};
|
|
112
165
|
}
|