accented 0.0.0-20250124142030 → 0.0.0-20250303013509
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 +55 -3
- package/dist/accented.d.ts +3 -1
- package/dist/accented.d.ts.map +1 -1
- package/dist/accented.js +69 -50
- package/dist/accented.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/dom-updater.d.ts +1 -1
- package/dist/dom-updater.d.ts.map +1 -1
- package/dist/dom-updater.js +26 -19
- package/dist/dom-updater.js.map +1 -1
- package/dist/elements/{accented-container.d.ts → accented-dialog.d.ts} +10 -4
- package/dist/elements/accented-dialog.d.ts.map +1 -0
- package/dist/elements/accented-dialog.js +371 -0
- package/dist/elements/accented-dialog.js.map +1 -0
- package/dist/elements/accented-trigger.d.ts +361 -0
- package/dist/elements/accented-trigger.d.ts.map +1 -0
- package/dist/elements/accented-trigger.js +188 -0
- package/dist/elements/accented-trigger.js.map +1 -0
- package/dist/intersection-observer.d.ts +5 -0
- package/dist/intersection-observer.d.ts.map +1 -0
- package/dist/intersection-observer.js +28 -0
- package/dist/intersection-observer.js.map +1 -0
- package/dist/log-and-rethrow.d.ts +2 -0
- package/dist/log-and-rethrow.d.ts.map +1 -0
- package/dist/log-and-rethrow.js +7 -0
- package/dist/log-and-rethrow.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -5
- package/dist/logger.js.map +1 -1
- package/dist/register-elements.d.ts +2 -0
- package/dist/register-elements.d.ts.map +1 -0
- package/dist/register-elements.js +21 -0
- package/dist/register-elements.js.map +1 -0
- package/dist/resize-listener.d.ts +2 -0
- package/dist/resize-listener.d.ts.map +1 -0
- package/dist/resize-listener.js +18 -0
- package/dist/resize-listener.js.map +1 -0
- package/dist/scanner.d.ts +2 -2
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +97 -33
- package/dist/scanner.js.map +1 -1
- package/dist/scroll-listeners.d.ts +2 -0
- package/dist/scroll-listeners.d.ts.map +1 -0
- package/dist/scroll-listeners.js +38 -0
- package/dist/scroll-listeners.js.map +1 -0
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +6 -0
- package/dist/state.js.map +1 -1
- package/dist/types.d.ts +71 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/deep-merge.js +1 -1
- package/dist/utils/deep-merge.js.map +1 -1
- package/dist/utils/get-element-html.d.ts +2 -0
- package/dist/utils/get-element-html.d.ts.map +1 -0
- package/dist/utils/get-element-html.js +14 -0
- package/dist/utils/get-element-html.js.map +1 -0
- package/dist/utils/get-element-position.d.ts +3 -0
- package/dist/utils/get-element-position.d.ts.map +1 -0
- package/dist/utils/get-element-position.js +58 -0
- package/dist/utils/get-element-position.js.map +1 -0
- package/dist/utils/get-scrollable-ancestors.d.ts +2 -0
- package/dist/utils/get-scrollable-ancestors.d.ts.map +1 -0
- package/dist/utils/get-scrollable-ancestors.js +15 -0
- package/dist/utils/get-scrollable-ancestors.js.map +1 -0
- package/dist/utils/is-html-element.d.ts +2 -0
- package/dist/utils/is-html-element.d.ts.map +1 -0
- package/dist/utils/is-html-element.js +7 -0
- package/dist/utils/is-html-element.js.map +1 -0
- package/dist/utils/recalculate-positions.d.ts +2 -0
- package/dist/utils/recalculate-positions.d.ts.map +1 -0
- package/dist/utils/recalculate-positions.js +27 -0
- package/dist/utils/recalculate-positions.js.map +1 -0
- package/dist/utils/recalculate-scrollable-ancestors.d.ts +2 -0
- package/dist/utils/recalculate-scrollable-ancestors.d.ts.map +1 -0
- package/dist/utils/recalculate-scrollable-ancestors.js +13 -0
- package/dist/utils/recalculate-scrollable-ancestors.js.map +1 -0
- package/dist/utils/supports-anchor-positioning.d.ts +6 -0
- package/dist/utils/supports-anchor-positioning.d.ts.map +1 -0
- package/dist/utils/supports-anchor-positioning.js +4 -0
- package/dist/utils/supports-anchor-positioning.js.map +1 -0
- package/dist/utils/transform-violations.d.ts.map +1 -1
- package/dist/utils/transform-violations.js +9 -0
- package/dist/utils/transform-violations.js.map +1 -1
- package/dist/utils/update-elements-with-issues.d.ts +3 -1
- package/dist/utils/update-elements-with-issues.d.ts.map +1 -1
- package/dist/utils/update-elements-with-issues.js +25 -7
- package/dist/utils/update-elements-with-issues.js.map +1 -1
- package/dist/validate-options.d.ts +3 -0
- package/dist/validate-options.d.ts.map +1 -0
- package/dist/validate-options.js +42 -0
- package/dist/validate-options.js.map +1 -0
- package/package.json +2 -1
- package/src/accented.ts +78 -58
- package/src/constants.ts +2 -0
- package/src/dom-updater.ts +26 -18
- package/src/elements/accented-dialog.ts +394 -0
- package/src/elements/accented-trigger.ts +214 -0
- package/src/intersection-observer.ts +28 -0
- package/src/log-and-rethrow.ts +9 -0
- package/src/logger.ts +11 -6
- package/src/register-elements.ts +21 -0
- package/src/resize-listener.ts +17 -0
- package/src/scanner.ts +108 -37
- package/src/scroll-listeners.ts +37 -0
- package/src/state.ts +12 -0
- package/src/types.ts +78 -19
- package/src/utils/deep-merge.test.ts +7 -0
- package/src/utils/deep-merge.ts +1 -1
- package/src/utils/get-element-html.ts +13 -0
- package/src/utils/get-element-position.ts +59 -0
- package/src/utils/get-scrollable-ancestors.ts +14 -0
- package/src/utils/is-html-element.ts +6 -0
- package/src/utils/recalculate-positions.ts +27 -0
- package/src/utils/recalculate-scrollable-ancestors.ts +13 -0
- package/src/utils/supports-anchor-positioning.ts +7 -0
- package/src/utils/transform-violations.ts +12 -1
- package/src/utils/update-elements-with-issues.test.ts +91 -16
- package/src/utils/update-elements-with-issues.ts +40 -20
- package/src/validate-options.ts +44 -0
- package/dist/elements/accented-container.d.ts.map +0 -1
- package/dist/elements/accented-container.js +0 -131
- package/dist/elements/accented-container.js.map +0 -1
- package/src/elements/accented-container.ts +0 -147
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { AxeResults } from 'axe-core';
|
|
1
|
+
import type { AxeResults, ImpactValue } from 'axe-core';
|
|
2
2
|
import type { Issue, ElementWithIssues } from '../types';
|
|
3
3
|
|
|
4
|
+
function impactCompare(a: ImpactValue, b: ImpactValue) {
|
|
5
|
+
const impactOrder = [null, 'minor', 'moderate', 'serious', 'critical'];
|
|
6
|
+
return impactOrder.indexOf(a) - impactOrder.indexOf(b);
|
|
7
|
+
}
|
|
8
|
+
|
|
4
9
|
export default function transformViolations(violations: typeof AxeResults.violations) {
|
|
5
10
|
const elementsWithIssues: Array<ElementWithIssues> = [];
|
|
6
11
|
|
|
@@ -41,5 +46,11 @@ export default function transformViolations(violations: typeof AxeResults.violat
|
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
for (const elementWithIssues of elementsWithIssues) {
|
|
50
|
+
elementWithIssues.issues.sort((a, b) => {
|
|
51
|
+
return -impactCompare(a.impact, b.impact) || a.id.localeCompare(b.id);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
return elementsWithIssues;
|
|
45
56
|
}
|
|
@@ -6,11 +6,11 @@ import type { ExtendedElementWithIssues, Issue } from '../types';
|
|
|
6
6
|
import updateElementsWithIssues from './update-elements-with-issues';
|
|
7
7
|
|
|
8
8
|
import type { AxeResults, ImpactValue } from 'axe-core';
|
|
9
|
-
import type {
|
|
9
|
+
import type { AccentedTrigger } from '../elements/accented-trigger';
|
|
10
10
|
type Violation = AxeResults['violations'][number];
|
|
11
11
|
type Node = Violation['nodes'][number];
|
|
12
12
|
|
|
13
|
-
const win: Window = {
|
|
13
|
+
const win: Window & { CSS: typeof CSS } = {
|
|
14
14
|
document: {
|
|
15
15
|
// @ts-expect-error the return value is of incorrect type.
|
|
16
16
|
createElement: () => ({
|
|
@@ -22,16 +22,36 @@ const win: Window = {
|
|
|
22
22
|
},
|
|
23
23
|
// @ts-expect-error we're missing a lot of properties
|
|
24
24
|
getComputedStyle: () => ({
|
|
25
|
-
zIndex: ''
|
|
26
|
-
|
|
25
|
+
zIndex: '',
|
|
26
|
+
direction: 'ltr'
|
|
27
|
+
}),
|
|
28
|
+
// @ts-expect-error we're missing a lot of properties
|
|
29
|
+
CSS: {
|
|
30
|
+
supports: () => true
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
const getBoundingClientRect = () => ({});
|
|
35
|
+
|
|
36
|
+
// @ts-expect-error element is not HTMLElement
|
|
37
|
+
const element1: HTMLElement = {getBoundingClientRect, isConnected: true};
|
|
29
38
|
// @ts-expect-error element is not HTMLElement
|
|
30
|
-
const
|
|
39
|
+
const element2: HTMLElement = {getBoundingClientRect, isConnected: true};
|
|
31
40
|
// @ts-expect-error element is not HTMLElement
|
|
32
|
-
const
|
|
41
|
+
const element3: HTMLElement = {getBoundingClientRect, isConnected: false};
|
|
42
|
+
|
|
43
|
+
const trigger = win.document.createElement('accented-trigger') as AccentedTrigger;
|
|
44
|
+
|
|
45
|
+
const position = signal({
|
|
46
|
+
left: 0,
|
|
47
|
+
width: 100,
|
|
48
|
+
top: 0,
|
|
49
|
+
height: 100
|
|
50
|
+
});
|
|
33
51
|
|
|
34
|
-
const
|
|
52
|
+
const visible = signal(true);
|
|
53
|
+
|
|
54
|
+
const scrollableAncestors = signal(new Set<HTMLElement>());
|
|
35
55
|
|
|
36
56
|
const commonNodeProps = {
|
|
37
57
|
html: '<div></div>',
|
|
@@ -51,6 +71,11 @@ const node2: Node = {
|
|
|
51
71
|
element: element2,
|
|
52
72
|
};
|
|
53
73
|
|
|
74
|
+
const node3: Node = {
|
|
75
|
+
...commonNodeProps,
|
|
76
|
+
element: element3,
|
|
77
|
+
};
|
|
78
|
+
|
|
54
79
|
const commonViolationProps = {
|
|
55
80
|
help: 'help',
|
|
56
81
|
helpUrl: 'http://example.com',
|
|
@@ -77,6 +102,12 @@ const violation3: Violation = {
|
|
|
77
102
|
nodes: [node2]
|
|
78
103
|
};
|
|
79
104
|
|
|
105
|
+
const violation4: Violation = {
|
|
106
|
+
...commonViolationProps,
|
|
107
|
+
id: 'id4',
|
|
108
|
+
nodes: [node3]
|
|
109
|
+
};
|
|
110
|
+
|
|
80
111
|
const commonIssueProps = {
|
|
81
112
|
title: 'help',
|
|
82
113
|
description: 'description',
|
|
@@ -105,13 +136,19 @@ suite('updateElementsWithIssues', () => {
|
|
|
105
136
|
{
|
|
106
137
|
id: 1,
|
|
107
138
|
element: element1,
|
|
108
|
-
|
|
139
|
+
position,
|
|
140
|
+
visible,
|
|
141
|
+
trigger,
|
|
142
|
+
scrollableAncestors,
|
|
109
143
|
issues: signal([issue1])
|
|
110
144
|
},
|
|
111
145
|
{
|
|
112
146
|
id: 2,
|
|
113
147
|
element: element2,
|
|
114
|
-
|
|
148
|
+
position,
|
|
149
|
+
visible,
|
|
150
|
+
trigger,
|
|
151
|
+
scrollableAncestors,
|
|
115
152
|
issues: signal([issue2])
|
|
116
153
|
}
|
|
117
154
|
]);
|
|
@@ -128,13 +165,19 @@ suite('updateElementsWithIssues', () => {
|
|
|
128
165
|
{
|
|
129
166
|
id: 1,
|
|
130
167
|
element: element1,
|
|
131
|
-
|
|
168
|
+
position,
|
|
169
|
+
visible,
|
|
170
|
+
trigger,
|
|
171
|
+
scrollableAncestors,
|
|
132
172
|
issues: signal([issue1])
|
|
133
173
|
},
|
|
134
174
|
{
|
|
135
175
|
id: 2,
|
|
136
176
|
element: element2,
|
|
137
|
-
|
|
177
|
+
position,
|
|
178
|
+
visible,
|
|
179
|
+
trigger,
|
|
180
|
+
scrollableAncestors,
|
|
138
181
|
issues: signal([issue2])
|
|
139
182
|
}
|
|
140
183
|
]);
|
|
@@ -151,13 +194,19 @@ suite('updateElementsWithIssues', () => {
|
|
|
151
194
|
{
|
|
152
195
|
id: 1,
|
|
153
196
|
element: element1,
|
|
154
|
-
|
|
197
|
+
position,
|
|
198
|
+
visible,
|
|
199
|
+
trigger,
|
|
200
|
+
scrollableAncestors,
|
|
155
201
|
issues: signal([issue1])
|
|
156
202
|
},
|
|
157
203
|
{
|
|
158
204
|
id: 2,
|
|
159
205
|
element: element2,
|
|
160
|
-
|
|
206
|
+
position,
|
|
207
|
+
visible,
|
|
208
|
+
trigger,
|
|
209
|
+
scrollableAncestors,
|
|
161
210
|
issues: signal([issue2, issue3])
|
|
162
211
|
}
|
|
163
212
|
]);
|
|
@@ -174,7 +223,10 @@ suite('updateElementsWithIssues', () => {
|
|
|
174
223
|
{
|
|
175
224
|
id: 1,
|
|
176
225
|
element: element1,
|
|
177
|
-
|
|
226
|
+
position,
|
|
227
|
+
visible,
|
|
228
|
+
trigger,
|
|
229
|
+
scrollableAncestors,
|
|
178
230
|
issues: signal([issue1])
|
|
179
231
|
}
|
|
180
232
|
]);
|
|
@@ -186,18 +238,41 @@ suite('updateElementsWithIssues', () => {
|
|
|
186
238
|
assert.equal(extendedElementsWithIssues.value[1]?.issues.value.length, 1);
|
|
187
239
|
});
|
|
188
240
|
|
|
241
|
+
test('one disconnected element added', () => {
|
|
242
|
+
const extendedElementsWithIssues: Signal<Array<ExtendedElementWithIssues>> = signal([
|
|
243
|
+
{
|
|
244
|
+
id: 1,
|
|
245
|
+
element: element1,
|
|
246
|
+
position,
|
|
247
|
+
visible,
|
|
248
|
+
trigger,
|
|
249
|
+
scrollableAncestors,
|
|
250
|
+
issues: signal([issue1])
|
|
251
|
+
}
|
|
252
|
+
]);
|
|
253
|
+
updateElementsWithIssues(extendedElementsWithIssues, [violation1, violation4], win, 'accented');
|
|
254
|
+
assert.equal(extendedElementsWithIssues.value.length, 1);
|
|
255
|
+
assert.equal(extendedElementsWithIssues.value[0]?.element, element1);
|
|
256
|
+
});
|
|
257
|
+
|
|
189
258
|
test('one element removed', () => {
|
|
190
259
|
const extendedElementsWithIssues: Signal<Array<ExtendedElementWithIssues>> = signal([
|
|
191
260
|
{
|
|
192
261
|
id: 1,
|
|
193
262
|
element: element1,
|
|
194
|
-
|
|
263
|
+
position,
|
|
264
|
+
visible,
|
|
265
|
+
trigger,
|
|
266
|
+
scrollableAncestors,
|
|
195
267
|
issues: signal([issue1])
|
|
196
268
|
},
|
|
197
269
|
{
|
|
198
270
|
id: 2,
|
|
199
271
|
element: element2,
|
|
200
|
-
|
|
272
|
+
position,
|
|
273
|
+
visible,
|
|
274
|
+
trigger,
|
|
275
|
+
scrollableAncestors,
|
|
201
276
|
issues: signal([issue2])
|
|
202
277
|
}
|
|
203
278
|
]);
|
|
@@ -4,11 +4,15 @@ import { batch, signal } from '@preact/signals-core';
|
|
|
4
4
|
import type { ExtendedElementWithIssues } from '../types';
|
|
5
5
|
import transformViolations from './transform-violations.js';
|
|
6
6
|
import areIssueSetsEqual from './are-issue-sets-equal.js';
|
|
7
|
-
import type {
|
|
7
|
+
import type { AccentedTrigger } from '../elements/accented-trigger';
|
|
8
|
+
import type { AccentedDialog } from '../elements/accented-dialog';
|
|
9
|
+
import getElementPosition from './get-element-position.js';
|
|
10
|
+
import getScrollableAncestors from './get-scrollable-ancestors.js';
|
|
11
|
+
import supportsAnchorPositioning from './supports-anchor-positioning.js';
|
|
8
12
|
|
|
9
13
|
let count = 0;
|
|
10
14
|
|
|
11
|
-
export default function updateElementsWithIssues(extendedElementsWithIssues: Signal<Array<ExtendedElementWithIssues>>, violations: typeof AxeResults.violations, win: Window, name: string) {
|
|
15
|
+
export default function updateElementsWithIssues(extendedElementsWithIssues: Signal<Array<ExtendedElementWithIssues>>, violations: typeof AxeResults.violations, win: Window & { CSS: typeof CSS }, name: string) {
|
|
12
16
|
const updatedElementsWithIssues = transformViolations(violations);
|
|
13
17
|
|
|
14
18
|
batch(() => {
|
|
@@ -32,24 +36,40 @@ export default function updateElementsWithIssues(extendedElementsWithIssues: Sig
|
|
|
32
36
|
.filter(extendedElementWithIssues => {
|
|
33
37
|
return !removedElementsWithIssues.some(removedElementWithIssues => removedElementWithIssues.element === extendedElementWithIssues.element);
|
|
34
38
|
})
|
|
35
|
-
.concat(addedElementsWithIssues
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
.concat(addedElementsWithIssues
|
|
40
|
+
.filter(addedElementWithIssues => addedElementWithIssues.element.isConnected)
|
|
41
|
+
.map(addedElementWithIssues => {
|
|
42
|
+
const id = count++;
|
|
43
|
+
const trigger = win.document.createElement(`${name}-trigger`) as AccentedTrigger;
|
|
44
|
+
const elementZIndex = parseInt(win.getComputedStyle(addedElementWithIssues.element).zIndex, 10);
|
|
45
|
+
if (!isNaN(elementZIndex)) {
|
|
46
|
+
trigger.style.setProperty('z-index', (elementZIndex + 1).toString(), 'important');
|
|
47
|
+
}
|
|
48
|
+
trigger.style.setProperty('position-anchor', `--${name}-anchor-${id}`, 'important');
|
|
49
|
+
trigger.dataset.id = id.toString();
|
|
50
|
+
const accentedDialog = win.document.createElement(`${name}-dialog`) as AccentedDialog;
|
|
51
|
+
trigger.dialog = accentedDialog;
|
|
52
|
+
const position = getElementPosition(addedElementWithIssues.element, win);
|
|
53
|
+
trigger.position = signal(position);
|
|
54
|
+
trigger.visible = signal(true);
|
|
55
|
+
trigger.element = addedElementWithIssues.element;
|
|
56
|
+
const scrollableAncestors = supportsAnchorPositioning(win) ?
|
|
57
|
+
new Set<HTMLElement>() :
|
|
58
|
+
getScrollableAncestors(addedElementWithIssues.element, win);
|
|
59
|
+
const issues = signal(addedElementWithIssues.issues);
|
|
60
|
+
accentedDialog.issues = issues;
|
|
61
|
+
accentedDialog.element = addedElementWithIssues.element;
|
|
62
|
+
return {
|
|
63
|
+
id,
|
|
64
|
+
element: addedElementWithIssues.element,
|
|
65
|
+
visible: trigger.visible,
|
|
66
|
+
position: trigger.position,
|
|
67
|
+
scrollableAncestors: signal(scrollableAncestors),
|
|
68
|
+
trigger,
|
|
69
|
+
issues
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
);
|
|
53
73
|
}
|
|
54
74
|
});
|
|
55
75
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AccentedOptions } from './types';
|
|
2
|
+
import { allowedAxeOptions } from './types.js';
|
|
3
|
+
|
|
4
|
+
// The space of valid CSS and HTML names is wider than this,
|
|
5
|
+
// but with Unicode it gets complicated quickly, so I'm sticking to only allowing
|
|
6
|
+
// lowercase alphanumeric names that possibly contain dashes that start with a letter.
|
|
7
|
+
const nameRegex = /^[a-z]([a-z0-9]|-)+$/;
|
|
8
|
+
|
|
9
|
+
export default function validateOptions(options: AccentedOptions) {
|
|
10
|
+
if (typeof options !== 'object' || options === null) {
|
|
11
|
+
throw new TypeError(`Accented: invalid argument. The options parameter must be an object if provided. It’s currently set to ${options}.`);
|
|
12
|
+
}
|
|
13
|
+
if (options.throttle !== undefined) {
|
|
14
|
+
if (typeof options.throttle !== 'object' || options.throttle === null) {
|
|
15
|
+
throw new TypeError(`Accented: invalid argument. \`throttle\` option must be an object if provided. It’s currently set to ${options.throttle}.`);
|
|
16
|
+
}
|
|
17
|
+
if (options.throttle.wait !== undefined && (typeof options.throttle.wait !== 'number' || options.throttle.wait < 0)) {
|
|
18
|
+
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}.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (options.output !== undefined) {
|
|
22
|
+
if (typeof options.output !== 'object' || options.output === null) {
|
|
23
|
+
throw new TypeError(`Accented: invalid argument. \`output\` option must be an object if provided. It’s currently set to ${options.output}.`);
|
|
24
|
+
}
|
|
25
|
+
if (options.output.console !== undefined && typeof options.output.console !== 'boolean') {
|
|
26
|
+
console.warn(`Accented: invalid argument. \`output.console\` option is expected to be a boolean. It’s currently set to ${options.output.console}.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (options.callback !== undefined && typeof options.callback !== 'function') {
|
|
30
|
+
throw new TypeError(`Accented: invalid argument. \`callback\` option must be a function if provided. It’s currently set to ${options.callback}.`);
|
|
31
|
+
}
|
|
32
|
+
if (options.name !== undefined && (typeof options.name !== 'string' || !options.name.match(nameRegex))) {
|
|
33
|
+
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}.`);
|
|
34
|
+
}
|
|
35
|
+
if (options.axeOptions !== undefined) {
|
|
36
|
+
if (typeof options.axeOptions !== 'object' || options.axeOptions === null) {
|
|
37
|
+
throw new TypeError(`Accented: invalid argument. \`axeOptions\` option must be an object if provided. It’s currently set to ${options.axeOptions}.`);
|
|
38
|
+
}
|
|
39
|
+
const unsupportedKeys = Object.keys(options.axeOptions).filter(key => !(allowedAxeOptions as unknown as Array<string>).includes(key));
|
|
40
|
+
if (unsupportedKeys.length > 0) {
|
|
41
|
+
throw new TypeError(`Accented: invalid argument. \`axeOptions\` contains the following unsupported keys: ${unsupportedKeys.join(', ')}. Valid options are: ${allowedAxeOptions.join(', ')}.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"accented-container.d.ts","sourceRoot":"","sources":["../../src/elements/accented-container.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;CAC1C;+BAIqB,MAAM;;iCAkEN,eAAe,GAAG,SAAS;iCAE3B,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;gBAElC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAtE5C,wBAwIE"}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { effect } from '@preact/signals-core';
|
|
2
|
-
// We want Accented to not throw an error in Node, and use static imports,
|
|
3
|
-
// so we can't export `class extends HTMLElement` because HTMLElement is not available in Node.
|
|
4
|
-
export default (name) => {
|
|
5
|
-
const containerTemplate = document.createElement('template');
|
|
6
|
-
containerTemplate.innerHTML = `
|
|
7
|
-
<button id="trigger">⚠</button>
|
|
8
|
-
<dialog dir="ltr" aria-labelledby="title">
|
|
9
|
-
<h2 id="title">Issues</h2>
|
|
10
|
-
<ul id="issues"></ul>
|
|
11
|
-
</dialog>
|
|
12
|
-
`;
|
|
13
|
-
const issueTemplate = document.createElement('template');
|
|
14
|
-
issueTemplate.innerHTML = `
|
|
15
|
-
<li>
|
|
16
|
-
<a></a>
|
|
17
|
-
<div></div>
|
|
18
|
-
</li>
|
|
19
|
-
`;
|
|
20
|
-
const descriptionTemplate = document.createElement('template');
|
|
21
|
-
descriptionTemplate.innerHTML = `
|
|
22
|
-
<span></span>
|
|
23
|
-
<ul></ul>
|
|
24
|
-
`;
|
|
25
|
-
const stylesheet = new CSSStyleSheet();
|
|
26
|
-
stylesheet.replaceSync(`
|
|
27
|
-
:host {
|
|
28
|
-
position: absolute;
|
|
29
|
-
inset-inline-end: anchor(end);
|
|
30
|
-
inset-block-end: anchor(end);
|
|
31
|
-
|
|
32
|
-
/* Popover-specific stuff */
|
|
33
|
-
border: none;
|
|
34
|
-
padding: 0;
|
|
35
|
-
margin-inline-end: 0;
|
|
36
|
-
margin-block-end: 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
#trigger {
|
|
40
|
-
box-sizing: border-box;
|
|
41
|
-
font-size: 1rem;
|
|
42
|
-
inline-size: max(32px, 2rem);
|
|
43
|
-
block-size: max(32px, 2rem);
|
|
44
|
-
|
|
45
|
-
/* Make it look better in forced-colors mode, */
|
|
46
|
-
border: 2px solid transparent;
|
|
47
|
-
|
|
48
|
-
background-color: var(--${name}-primary-color);
|
|
49
|
-
color: var(--${name}-secondary-color);
|
|
50
|
-
|
|
51
|
-
outline-offset: -4px;
|
|
52
|
-
outline-color: var(--${name}-secondary-color);
|
|
53
|
-
|
|
54
|
-
&:focus-visible {
|
|
55
|
-
outline-width: 2px;
|
|
56
|
-
outline-style: solid;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
&:hover:not(:focus-visible) {
|
|
60
|
-
outline-width: 2px;
|
|
61
|
-
outline-style: dashed;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
`);
|
|
65
|
-
return class AccentedContainerLocal extends HTMLElement {
|
|
66
|
-
#abortController;
|
|
67
|
-
#disposeOfEffect;
|
|
68
|
-
issues;
|
|
69
|
-
constructor() {
|
|
70
|
-
super();
|
|
71
|
-
this.attachShadow({ mode: 'open' });
|
|
72
|
-
const content = containerTemplate.content.cloneNode(true);
|
|
73
|
-
if (this.shadowRoot) {
|
|
74
|
-
this.shadowRoot.adoptedStyleSheets.push(stylesheet);
|
|
75
|
-
this.shadowRoot.append(content);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
connectedCallback() {
|
|
79
|
-
if (this.shadowRoot) {
|
|
80
|
-
const { shadowRoot } = this;
|
|
81
|
-
const trigger = shadowRoot.getElementById('trigger');
|
|
82
|
-
const dialog = shadowRoot.querySelector('dialog');
|
|
83
|
-
this.#abortController = new AbortController();
|
|
84
|
-
trigger?.addEventListener('click', () => {
|
|
85
|
-
dialog?.showModal();
|
|
86
|
-
}, { signal: this.#abortController.signal });
|
|
87
|
-
this.#disposeOfEffect = effect(() => {
|
|
88
|
-
if (this.issues) {
|
|
89
|
-
const issues = this.issues.value;
|
|
90
|
-
const issuesList = shadowRoot.getElementById('issues');
|
|
91
|
-
if (issuesList) {
|
|
92
|
-
issuesList.innerHTML = '';
|
|
93
|
-
for (const issue of issues) {
|
|
94
|
-
const issueContent = issueTemplate.content.cloneNode(true);
|
|
95
|
-
const a = issueContent.querySelector('a');
|
|
96
|
-
const div = issueContent.querySelector('div');
|
|
97
|
-
if (a && div) {
|
|
98
|
-
a.textContent = issue.title;
|
|
99
|
-
a.href = issue.url;
|
|
100
|
-
const descriptionItems = issue.description.split(/\n\s*/);
|
|
101
|
-
const descriptionContent = descriptionTemplate.content.cloneNode(true);
|
|
102
|
-
const descriptionTitle = descriptionContent.querySelector('span');
|
|
103
|
-
const descriptionList = descriptionContent.querySelector('ul');
|
|
104
|
-
if (descriptionTitle && descriptionList && descriptionItems.length > 1) {
|
|
105
|
-
descriptionTitle.textContent = descriptionItems[0];
|
|
106
|
-
for (const descriptionItem of descriptionItems.slice(1)) {
|
|
107
|
-
const li = document.createElement('li');
|
|
108
|
-
li.textContent = descriptionItem;
|
|
109
|
-
descriptionList.appendChild(li);
|
|
110
|
-
}
|
|
111
|
-
div.appendChild(descriptionContent);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
issuesList.appendChild(issueContent);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
disconnectedCallback() {
|
|
122
|
-
if (this.#abortController) {
|
|
123
|
-
this.#abortController.abort();
|
|
124
|
-
}
|
|
125
|
-
if (this.#disposeOfEffect) {
|
|
126
|
-
this.#disposeOfEffect();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
//# sourceMappingURL=accented-container.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"accented-container.js","sourceRoot":"","sources":["../../src/elements/accented-container.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import type { Issue } from '../types';
|
|
2
|
-
import type { Signal } from '@preact/signals-core';
|
|
3
|
-
import { effect } from '@preact/signals-core';
|
|
4
|
-
|
|
5
|
-
export interface AccentedContainer extends HTMLElement {
|
|
6
|
-
issues: Signal<Array<Issue>> | undefined;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// We want Accented to not throw an error in Node, and use static imports,
|
|
10
|
-
// so we can't export `class extends HTMLElement` because HTMLElement is not available in Node.
|
|
11
|
-
export default (name: string) => {
|
|
12
|
-
const containerTemplate = document.createElement('template');
|
|
13
|
-
containerTemplate.innerHTML = `
|
|
14
|
-
<button id="trigger">⚠</button>
|
|
15
|
-
<dialog dir="ltr" aria-labelledby="title">
|
|
16
|
-
<h2 id="title">Issues</h2>
|
|
17
|
-
<ul id="issues"></ul>
|
|
18
|
-
</dialog>
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
const issueTemplate = document.createElement('template');
|
|
22
|
-
issueTemplate.innerHTML = `
|
|
23
|
-
<li>
|
|
24
|
-
<a></a>
|
|
25
|
-
<div></div>
|
|
26
|
-
</li>
|
|
27
|
-
`;
|
|
28
|
-
|
|
29
|
-
const descriptionTemplate = document.createElement('template');
|
|
30
|
-
descriptionTemplate.innerHTML = `
|
|
31
|
-
<span></span>
|
|
32
|
-
<ul></ul>
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
const stylesheet = new CSSStyleSheet();
|
|
36
|
-
stylesheet.replaceSync(`
|
|
37
|
-
:host {
|
|
38
|
-
position: absolute;
|
|
39
|
-
inset-inline-end: anchor(end);
|
|
40
|
-
inset-block-end: anchor(end);
|
|
41
|
-
|
|
42
|
-
/* Popover-specific stuff */
|
|
43
|
-
border: none;
|
|
44
|
-
padding: 0;
|
|
45
|
-
margin-inline-end: 0;
|
|
46
|
-
margin-block-end: 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
#trigger {
|
|
50
|
-
box-sizing: border-box;
|
|
51
|
-
font-size: 1rem;
|
|
52
|
-
inline-size: max(32px, 2rem);
|
|
53
|
-
block-size: max(32px, 2rem);
|
|
54
|
-
|
|
55
|
-
/* Make it look better in forced-colors mode, */
|
|
56
|
-
border: 2px solid transparent;
|
|
57
|
-
|
|
58
|
-
background-color: var(--${name}-primary-color);
|
|
59
|
-
color: var(--${name}-secondary-color);
|
|
60
|
-
|
|
61
|
-
outline-offset: -4px;
|
|
62
|
-
outline-color: var(--${name}-secondary-color);
|
|
63
|
-
|
|
64
|
-
&:focus-visible {
|
|
65
|
-
outline-width: 2px;
|
|
66
|
-
outline-style: solid;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
&:hover:not(:focus-visible) {
|
|
70
|
-
outline-width: 2px;
|
|
71
|
-
outline-style: dashed;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
`);
|
|
75
|
-
|
|
76
|
-
return class AccentedContainerLocal extends HTMLElement implements AccentedContainer {
|
|
77
|
-
#abortController: AbortController | undefined;
|
|
78
|
-
|
|
79
|
-
#disposeOfEffect: (() => void) | undefined;
|
|
80
|
-
|
|
81
|
-
issues: Signal<Array<Issue>> | undefined;
|
|
82
|
-
|
|
83
|
-
constructor() {
|
|
84
|
-
super();
|
|
85
|
-
this.attachShadow({ mode: 'open' });
|
|
86
|
-
const content = containerTemplate.content.cloneNode(true);
|
|
87
|
-
if (this.shadowRoot) {
|
|
88
|
-
this.shadowRoot.adoptedStyleSheets.push(stylesheet);
|
|
89
|
-
this.shadowRoot.append(content);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
connectedCallback() {
|
|
94
|
-
if (this.shadowRoot) {
|
|
95
|
-
const { shadowRoot } = this;
|
|
96
|
-
const trigger = shadowRoot.getElementById('trigger');
|
|
97
|
-
const dialog = shadowRoot.querySelector('dialog');
|
|
98
|
-
this.#abortController = new AbortController();
|
|
99
|
-
trigger?.addEventListener('click', () => {
|
|
100
|
-
dialog?.showModal();
|
|
101
|
-
}, { signal: this.#abortController.signal });
|
|
102
|
-
|
|
103
|
-
this.#disposeOfEffect = effect(() => {
|
|
104
|
-
if (this.issues) {
|
|
105
|
-
const issues = this.issues.value;
|
|
106
|
-
const issuesList = shadowRoot.getElementById('issues');
|
|
107
|
-
if (issuesList) {
|
|
108
|
-
issuesList.innerHTML = '';
|
|
109
|
-
for (const issue of issues) {
|
|
110
|
-
const issueContent = issueTemplate.content.cloneNode(true) as Element;
|
|
111
|
-
const a = issueContent.querySelector('a');
|
|
112
|
-
const div = issueContent.querySelector('div');
|
|
113
|
-
if (a && div) {
|
|
114
|
-
a.textContent = issue.title;
|
|
115
|
-
a.href = issue.url;
|
|
116
|
-
const descriptionItems = issue.description.split(/\n\s*/);
|
|
117
|
-
const descriptionContent = descriptionTemplate.content.cloneNode(true) as Element;
|
|
118
|
-
const descriptionTitle = descriptionContent.querySelector('span');
|
|
119
|
-
const descriptionList = descriptionContent.querySelector('ul');
|
|
120
|
-
if (descriptionTitle && descriptionList && descriptionItems.length > 1) {
|
|
121
|
-
descriptionTitle.textContent = descriptionItems[0]!;
|
|
122
|
-
for (const descriptionItem of descriptionItems.slice(1)) {
|
|
123
|
-
const li = document.createElement('li');
|
|
124
|
-
li.textContent = descriptionItem;
|
|
125
|
-
descriptionList.appendChild(li);
|
|
126
|
-
}
|
|
127
|
-
div.appendChild(descriptionContent);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
issuesList.appendChild(issueContent);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
disconnectedCallback() {
|
|
139
|
-
if (this.#abortController) {
|
|
140
|
-
this.#abortController.abort();
|
|
141
|
-
}
|
|
142
|
-
if (this.#disposeOfEffect) {
|
|
143
|
-
this.#disposeOfEffect();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
};
|