ui-svelte 0.2.17 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/control/Audio.svelte +3 -2
- package/dist/control/Button.svelte +11 -2
- package/dist/control/Button.svelte.d.ts +1 -0
- package/dist/control/Fab.svelte +11 -2
- package/dist/control/Fab.svelte.d.ts +1 -0
- package/dist/control/IconButton.svelte +5 -2
- package/dist/control/IconButton.svelte.d.ts +1 -0
- package/dist/control/Image.svelte +2 -0
- package/dist/control/Record.svelte +8 -0
- package/dist/control/ToggleGroup.svelte +6 -3
- package/dist/control/ToggleGroup.svelte.d.ts +1 -0
- package/dist/control/ToggleTheme.svelte +1 -0
- package/dist/control/Video.svelte +4 -0
- package/dist/css/base.css +3 -0
- package/dist/display/Icon.svelte +6 -1
- package/dist/form/Select.svelte +27 -4
- package/dist/form/TextField.svelte +3 -1
- package/dist/form/Toggle.svelte +2 -0
- package/dist/layout/Provider.svelte +1 -1
- package/dist/navigation/NavMenu.svelte +46 -1
- package/package.json +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { PauseFilledIcon, PlayFilledIcon } from '../icons/index.js';
|
|
3
|
+
import { Avatar, IconButton } from '../index.js';
|
|
3
4
|
import { cn } from '../utils/class-names.js';
|
|
4
|
-
import IconButton from './IconButton.svelte';
|
|
5
|
-
import Avatar from '../display/Avatar.svelte';
|
|
6
5
|
|
|
7
6
|
type Props = {
|
|
8
7
|
class?: string;
|
|
@@ -207,6 +206,7 @@
|
|
|
207
206
|
variant={variant === 'solid' ? 'soft' : 'solid'}
|
|
208
207
|
{color}
|
|
209
208
|
onclick={togglePlay}
|
|
209
|
+
ariaLabel={isPlaying ? 'Pause audio' : 'Play audio'}
|
|
210
210
|
/>
|
|
211
211
|
|
|
212
212
|
<div class="media-content">
|
|
@@ -218,6 +218,7 @@
|
|
|
218
218
|
aria-valuenow={Math.round((currentTime / duration) * 100) || 0}
|
|
219
219
|
aria-valuemin="0"
|
|
220
220
|
aria-valuemax="100"
|
|
221
|
+
aria-label="Audio progress"
|
|
221
222
|
>
|
|
222
223
|
<div class="media-bars" class:loading={isAnalyzing} class:loaded={!isAnalyzing}>
|
|
223
224
|
{#if isAnalyzing}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
isWide?: boolean;
|
|
22
22
|
isDisabled?: boolean;
|
|
23
23
|
isSolid?: boolean;
|
|
24
|
+
ariaLabel?: string;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
const {
|
|
@@ -37,7 +38,8 @@
|
|
|
37
38
|
endIcon,
|
|
38
39
|
isLoading,
|
|
39
40
|
isWide,
|
|
40
|
-
isDisabled
|
|
41
|
+
isDisabled,
|
|
42
|
+
ariaLabel
|
|
41
43
|
}: Props = $props();
|
|
42
44
|
|
|
43
45
|
const colors = {
|
|
@@ -82,7 +84,13 @@
|
|
|
82
84
|
{/snippet}
|
|
83
85
|
|
|
84
86
|
{#if href}
|
|
85
|
-
<a
|
|
87
|
+
<a
|
|
88
|
+
class={baseClasses}
|
|
89
|
+
{href}
|
|
90
|
+
{target}
|
|
91
|
+
aria-label={ariaLabel}
|
|
92
|
+
aria-disabled={isDisabled || undefined}
|
|
93
|
+
>
|
|
86
94
|
{@render content()}
|
|
87
95
|
</a>
|
|
88
96
|
{:else}
|
|
@@ -92,6 +100,7 @@
|
|
|
92
100
|
disabled={isDisabled || isLoading}
|
|
93
101
|
class={baseClasses}
|
|
94
102
|
aria-busy={isLoading}
|
|
103
|
+
aria-label={ariaLabel}
|
|
95
104
|
>
|
|
96
105
|
{#if isLoading}
|
|
97
106
|
<span class="btn-loading">
|
package/dist/control/Fab.svelte
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
offsetY?: string;
|
|
27
27
|
onclick?: () => void;
|
|
28
28
|
children?: Snippet;
|
|
29
|
+
ariaLabel?: string;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const {
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
offsetX,
|
|
41
42
|
offsetY,
|
|
42
43
|
onclick,
|
|
43
|
-
children
|
|
44
|
+
children,
|
|
45
|
+
ariaLabel
|
|
44
46
|
}: Props = $props();
|
|
45
47
|
|
|
46
48
|
let isOpen = $state(false);
|
|
@@ -97,7 +99,14 @@
|
|
|
97
99
|
{#if children}
|
|
98
100
|
{@render children()}
|
|
99
101
|
{:else}
|
|
100
|
-
<IconButton
|
|
102
|
+
<IconButton
|
|
103
|
+
{icon}
|
|
104
|
+
{color}
|
|
105
|
+
{variant}
|
|
106
|
+
{size}
|
|
107
|
+
onclick={handleTriggerClick}
|
|
108
|
+
ariaLabel={ariaLabel || (actions.length > 0 ? 'Open actions menu' : undefined)}
|
|
109
|
+
/>
|
|
101
110
|
{/if}
|
|
102
111
|
</div>
|
|
103
112
|
</div>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
isLoading?: boolean;
|
|
17
17
|
icon: IconData;
|
|
18
18
|
isDisabled?: boolean;
|
|
19
|
+
ariaLabel?: string;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
const {
|
|
@@ -29,7 +30,8 @@
|
|
|
29
30
|
class: className,
|
|
30
31
|
icon,
|
|
31
32
|
isLoading,
|
|
32
|
-
isDisabled
|
|
33
|
+
isDisabled,
|
|
34
|
+
ariaLabel
|
|
33
35
|
}: Props = $props();
|
|
34
36
|
|
|
35
37
|
const colors = {
|
|
@@ -68,7 +70,7 @@
|
|
|
68
70
|
{/snippet}
|
|
69
71
|
|
|
70
72
|
{#if href}
|
|
71
|
-
<a class={baseClasses} {href} {target}>
|
|
73
|
+
<a class={baseClasses} {href} {target} aria-label={ariaLabel}>
|
|
72
74
|
{@render content()}
|
|
73
75
|
</a>
|
|
74
76
|
{:else}
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
disabled={isDisabled || isLoading}
|
|
79
81
|
class={baseClasses}
|
|
80
82
|
aria-busy={isLoading}
|
|
83
|
+
aria-label={ariaLabel}
|
|
81
84
|
>
|
|
82
85
|
{#if isLoading}
|
|
83
86
|
<span class="btn-loading">
|
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
{color}
|
|
110
110
|
variant="overlay"
|
|
111
111
|
size="sm"
|
|
112
|
+
ariaLabel="Download image"
|
|
112
113
|
/>
|
|
113
114
|
<IconButton
|
|
114
115
|
onclick={handleToggleMaximize}
|
|
@@ -116,6 +117,7 @@
|
|
|
116
117
|
{color}
|
|
117
118
|
variant="overlay"
|
|
118
119
|
size="sm"
|
|
120
|
+
ariaLabel="Toggle fullscreen"
|
|
119
121
|
/>
|
|
120
122
|
</div>
|
|
121
123
|
</div>
|
|
@@ -357,6 +357,7 @@
|
|
|
357
357
|
{color}
|
|
358
358
|
variant={variant === 'solid' ? 'soft' : 'solid'}
|
|
359
359
|
size="md"
|
|
360
|
+
ariaLabel={isPlaying ? 'Pause playback' : 'Play recording'}
|
|
360
361
|
/>
|
|
361
362
|
|
|
362
363
|
<div class="media-content">
|
|
@@ -382,6 +383,7 @@
|
|
|
382
383
|
color="danger"
|
|
383
384
|
variant="ghost"
|
|
384
385
|
size="sm"
|
|
386
|
+
ariaLabel="Discard recording"
|
|
385
387
|
/>
|
|
386
388
|
<IconButton
|
|
387
389
|
onclick={confirmRecording}
|
|
@@ -390,6 +392,7 @@
|
|
|
390
392
|
variant="ghost"
|
|
391
393
|
size="sm"
|
|
392
394
|
isLoading={isUploading}
|
|
395
|
+
ariaLabel="Confirm recording"
|
|
393
396
|
/>
|
|
394
397
|
</div>
|
|
395
398
|
{:else if !isRecording}
|
|
@@ -399,6 +402,7 @@
|
|
|
399
402
|
{color}
|
|
400
403
|
variant={variant === 'solid' ? 'soft' : 'solid'}
|
|
401
404
|
size="md"
|
|
405
|
+
ariaLabel="Start recording"
|
|
402
406
|
/>
|
|
403
407
|
|
|
404
408
|
<div class="media-content">
|
|
@@ -421,6 +425,7 @@
|
|
|
421
425
|
{color}
|
|
422
426
|
variant={variant === 'solid' ? 'soft' : 'solid'}
|
|
423
427
|
size="md"
|
|
428
|
+
ariaLabel="Resume recording"
|
|
424
429
|
/>
|
|
425
430
|
{:else}
|
|
426
431
|
<IconButton
|
|
@@ -429,6 +434,7 @@
|
|
|
429
434
|
{color}
|
|
430
435
|
variant={variant === 'solid' ? 'soft' : 'solid'}
|
|
431
436
|
size="md"
|
|
437
|
+
ariaLabel="Pause recording"
|
|
432
438
|
/>
|
|
433
439
|
{/if}
|
|
434
440
|
|
|
@@ -456,6 +462,7 @@
|
|
|
456
462
|
color="danger"
|
|
457
463
|
variant="ghost"
|
|
458
464
|
size="sm"
|
|
465
|
+
ariaLabel="Discard recording"
|
|
459
466
|
/>
|
|
460
467
|
<IconButton
|
|
461
468
|
onclick={stopRecording}
|
|
@@ -463,6 +470,7 @@
|
|
|
463
470
|
{color}
|
|
464
471
|
variant="ghost"
|
|
465
472
|
size="sm"
|
|
473
|
+
ariaLabel="Stop recording"
|
|
466
474
|
/>
|
|
467
475
|
</div>
|
|
468
476
|
{/if}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
isWide?: boolean;
|
|
21
21
|
isVertical?: boolean;
|
|
22
22
|
isDisabled?: boolean;
|
|
23
|
+
ariaLabel?: string;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
let {
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
class: className,
|
|
34
35
|
isWide,
|
|
35
36
|
isVertical,
|
|
36
|
-
isDisabled
|
|
37
|
+
isDisabled,
|
|
38
|
+
ariaLabel
|
|
37
39
|
}: Props = $props();
|
|
38
40
|
|
|
39
41
|
const colors = {
|
|
@@ -78,14 +80,15 @@
|
|
|
78
80
|
}
|
|
79
81
|
</script>
|
|
80
82
|
|
|
81
|
-
<div class={groupClasses} role="
|
|
83
|
+
<div class={groupClasses} role="radiogroup" aria-label={ariaLabel}>
|
|
82
84
|
{#each items as item}
|
|
83
85
|
<button
|
|
84
86
|
type="button"
|
|
85
87
|
class={cn('toggle-group-item', sizes[size], value === item.id && 'is-active')}
|
|
86
88
|
onclick={() => handleClick(item.id)}
|
|
87
89
|
disabled={isDisabled}
|
|
88
|
-
|
|
90
|
+
role="radio"
|
|
91
|
+
aria-checked={value === item.id}
|
|
89
92
|
>
|
|
90
93
|
{#if item.icon}
|
|
91
94
|
<Icon icon={item.icon} />
|
|
@@ -181,6 +181,7 @@
|
|
|
181
181
|
{color}
|
|
182
182
|
variant="overlay"
|
|
183
183
|
size="sm"
|
|
184
|
+
ariaLabel={videoParams.paused ? 'Play video' : 'Pause video'}
|
|
184
185
|
/>
|
|
185
186
|
|
|
186
187
|
<Chip variant="overlay" {color}>
|
|
@@ -217,6 +218,7 @@
|
|
|
217
218
|
{color}
|
|
218
219
|
variant="overlay"
|
|
219
220
|
size="sm"
|
|
221
|
+
ariaLabel={videoParams.muted ? 'Unmute video' : 'Mute video'}
|
|
220
222
|
/>
|
|
221
223
|
</div>
|
|
222
224
|
<IconButton
|
|
@@ -225,6 +227,7 @@
|
|
|
225
227
|
{color}
|
|
226
228
|
variant="overlay"
|
|
227
229
|
size="sm"
|
|
230
|
+
ariaLabel="Toggle picture-in-picture"
|
|
228
231
|
/>
|
|
229
232
|
<IconButton
|
|
230
233
|
onclick={handleToggleMaximize}
|
|
@@ -232,6 +235,7 @@
|
|
|
232
235
|
{color}
|
|
233
236
|
variant="overlay"
|
|
234
237
|
size="sm"
|
|
238
|
+
ariaLabel="Toggle fullscreen"
|
|
235
239
|
/>
|
|
236
240
|
</div>
|
|
237
241
|
</div>
|
package/dist/css/base.css
CHANGED
package/dist/display/Icon.svelte
CHANGED
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
const { icon, class: className }: Props = $props();
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
|
-
<svg
|
|
18
|
+
<svg
|
|
19
|
+
viewBox={icon.viewbox}
|
|
20
|
+
class={cn('icon', className)}
|
|
21
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
>
|
|
19
24
|
{@html icon.body}
|
|
20
25
|
</svg>
|
package/dist/form/Select.svelte
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
import { cn } from '../utils/class-names.js';
|
|
5
5
|
import { onMount } from 'svelte';
|
|
6
6
|
|
|
7
|
+
const instanceId = Math.random().toString(36).substring(2, 9);
|
|
8
|
+
const listboxId = `select-listbox-${instanceId}`;
|
|
9
|
+
const labelId = `select-label-${instanceId}`;
|
|
10
|
+
const helpTextId = `select-help-${instanceId}`;
|
|
11
|
+
|
|
7
12
|
type Option = {
|
|
8
13
|
id: string | number;
|
|
9
14
|
label: string;
|
|
@@ -273,7 +278,7 @@
|
|
|
273
278
|
<input type="text" {name} bind:value hidden />
|
|
274
279
|
|
|
275
280
|
{#if !isFloatLabel && label}
|
|
276
|
-
<div class="field-label">{label}</div>
|
|
281
|
+
<div class="field-label" id={labelId}>{label}</div>
|
|
277
282
|
{/if}
|
|
278
283
|
|
|
279
284
|
<button
|
|
@@ -291,6 +296,16 @@
|
|
|
291
296
|
onclick={toggleDropdown}
|
|
292
297
|
onmouseenter={() => (isActive = true)}
|
|
293
298
|
onmouseleave={() => (isActive = false)}
|
|
299
|
+
role="combobox"
|
|
300
|
+
aria-haspopup="listbox"
|
|
301
|
+
aria-expanded={isOpen}
|
|
302
|
+
aria-controls={listboxId}
|
|
303
|
+
aria-activedescendant={isOpen && focusedIndex >= 0
|
|
304
|
+
? `${listboxId}-option-${focusedIndex}`
|
|
305
|
+
: undefined}
|
|
306
|
+
aria-labelledby={label ? labelId : undefined}
|
|
307
|
+
aria-label={!label ? placeholder : undefined}
|
|
308
|
+
aria-describedby={errorText || helpText ? helpTextId : undefined}
|
|
294
309
|
>
|
|
295
310
|
{#if isFloatLabel && label}
|
|
296
311
|
<span
|
|
@@ -325,13 +340,21 @@
|
|
|
325
340
|
</button>
|
|
326
341
|
|
|
327
342
|
{#if errorText || helpText}
|
|
328
|
-
<div class={cn('field-help', errorText && 'is-danger')}>
|
|
343
|
+
<div id={helpTextId} class={cn('field-help', errorText && 'is-danger')}>
|
|
344
|
+
{errorText || helpText}
|
|
345
|
+
</div>
|
|
329
346
|
{/if}
|
|
330
347
|
|
|
331
348
|
<div class:is-active={isOpen} class="select-popover" bind:this={contentEl} {style}>
|
|
332
|
-
<ul
|
|
349
|
+
<ul
|
|
350
|
+
class="select-options"
|
|
351
|
+
bind:this={optionsEl}
|
|
352
|
+
role="listbox"
|
|
353
|
+
id={listboxId}
|
|
354
|
+
aria-label={label}
|
|
355
|
+
>
|
|
333
356
|
{#each options as item, index}
|
|
334
|
-
<li>
|
|
357
|
+
<li id={`${listboxId}-option-${index}`} role="option" aria-selected={value === item.id}>
|
|
335
358
|
<Item
|
|
336
359
|
label={item.label}
|
|
337
360
|
src={item.src}
|
|
@@ -165,6 +165,8 @@
|
|
|
165
165
|
oninput={(e) => oninput?.((e.target as HTMLInputElement).value)}
|
|
166
166
|
onfocusin={() => (isFocused = true)}
|
|
167
167
|
onfocusout={() => (isFocused = false)}
|
|
168
|
+
aria-describedby={errorText || helpText ? `${uid}-help` : undefined}
|
|
169
|
+
aria-invalid={!!errorText}
|
|
168
170
|
/>
|
|
169
171
|
|
|
170
172
|
{#if endContent}
|
|
@@ -181,7 +183,7 @@
|
|
|
181
183
|
</label>
|
|
182
184
|
|
|
183
185
|
{#if errorText || helpText}
|
|
184
|
-
<div class={cn('field-help', errorText && 'is-danger')}>
|
|
186
|
+
<div id={`${uid}-help`} class={cn('field-help', errorText && 'is-danger')}>
|
|
185
187
|
{errorText || helpText}
|
|
186
188
|
</div>
|
|
187
189
|
{/if}
|
package/dist/form/Toggle.svelte
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<svelte:head>
|
|
50
50
|
<meta
|
|
51
51
|
name="viewport"
|
|
52
|
-
content="width=device-width, initial-scale=1, maximum-scale=
|
|
52
|
+
content="width=device-width, initial-scale=1, maximum-scale=5, viewport-fit=cover, interactive-widget=resizes-content"
|
|
53
53
|
/>
|
|
54
54
|
<script>
|
|
55
55
|
let themeState = 'light';
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
let openSubmenuIndex = $state<number | null>(null);
|
|
49
49
|
let triggerElements = $state<Record<number, HTMLElement>>({});
|
|
50
50
|
let popoverElement = $state<HTMLElement>();
|
|
51
|
+
let activeHash = $state<string | null>(null);
|
|
52
|
+
let sectionObserver: IntersectionObserver | null = null;
|
|
51
53
|
let position = $state({
|
|
52
54
|
top: 0,
|
|
53
55
|
left: 0,
|
|
@@ -84,6 +86,11 @@
|
|
|
84
86
|
|
|
85
87
|
function isItemActive(href?: string): boolean {
|
|
86
88
|
if (!href) return false;
|
|
89
|
+
|
|
90
|
+
if (href.startsWith('#')) {
|
|
91
|
+
return activeHash === href;
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
return page.url.pathname === href || page.url.pathname.startsWith(href + '/');
|
|
88
95
|
}
|
|
89
96
|
|
|
@@ -191,7 +198,38 @@
|
|
|
191
198
|
}
|
|
192
199
|
|
|
193
200
|
onMount(() => {
|
|
194
|
-
|
|
201
|
+
const hashLinks = items
|
|
202
|
+
.flatMap((item) => [item.href, ...(item.subitems?.map((s) => s.href) || [])])
|
|
203
|
+
.filter((href): href is string => !!href && href.startsWith('#'))
|
|
204
|
+
.map((href) => href.slice(1));
|
|
205
|
+
|
|
206
|
+
if (hashLinks.length > 0) {
|
|
207
|
+
sectionObserver = new IntersectionObserver(
|
|
208
|
+
(entries) => {
|
|
209
|
+
const visibleEntries = entries.filter((e) => e.isIntersecting);
|
|
210
|
+
if (visibleEntries.length > 0) {
|
|
211
|
+
const mostVisible = visibleEntries.reduce((prev, curr) =>
|
|
212
|
+
curr.intersectionRatio > prev.intersectionRatio ? curr : prev
|
|
213
|
+
);
|
|
214
|
+
activeHash = '#' + mostVisible.target.id;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
threshold: [0.1, 0.5, 0.9],
|
|
219
|
+
rootMargin: '-10% 0px -10% 0px'
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
hashLinks.forEach((id) => {
|
|
224
|
+
const el = document.getElementById(id);
|
|
225
|
+
if (el) sectionObserver!.observe(el);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return () => {
|
|
230
|
+
sectionObserver?.disconnect();
|
|
231
|
+
stopEventListeners();
|
|
232
|
+
};
|
|
195
233
|
});
|
|
196
234
|
</script>
|
|
197
235
|
|
|
@@ -216,6 +254,9 @@
|
|
|
216
254
|
)}
|
|
217
255
|
bind:this={triggerElements[index]}
|
|
218
256
|
onclick={() => handleItemClick(item, index)}
|
|
257
|
+
aria-haspopup={!!(item.subitems || item.megamenu)}
|
|
258
|
+
aria-expanded={openSubmenuIndex === index}
|
|
259
|
+
aria-controls={openSubmenuIndex === index ? `navmenu-popover-${index}` : undefined}
|
|
219
260
|
>
|
|
220
261
|
{#if item.icon}
|
|
221
262
|
<Icon icon={item.icon} class="navmenu-icon" />
|
|
@@ -235,6 +276,7 @@
|
|
|
235
276
|
{#if openSubmenuIndex !== null}
|
|
236
277
|
{@const currentItem = items[openSubmenuIndex]}
|
|
237
278
|
<div
|
|
279
|
+
id={`navmenu-popover-${openSubmenuIndex}`}
|
|
238
280
|
class={cn(
|
|
239
281
|
'navmenu-popover',
|
|
240
282
|
sizeClasses[size],
|
|
@@ -243,6 +285,7 @@
|
|
|
243
285
|
)}
|
|
244
286
|
bind:this={popoverElement}
|
|
245
287
|
{style}
|
|
288
|
+
role="menu"
|
|
246
289
|
>
|
|
247
290
|
{#if currentItem?.megamenu}
|
|
248
291
|
{@render currentItem.megamenu()}
|
|
@@ -257,6 +300,7 @@
|
|
|
257
300
|
openSubmenuIndex = null;
|
|
258
301
|
stopEventListeners();
|
|
259
302
|
}}
|
|
303
|
+
role="menuitem"
|
|
260
304
|
>
|
|
261
305
|
{#if subitem.icon}
|
|
262
306
|
<Icon icon={subitem.icon} class="navmenu-submenu-icon" />
|
|
@@ -273,6 +317,7 @@
|
|
|
273
317
|
type="button"
|
|
274
318
|
class="navmenu-submenu-item"
|
|
275
319
|
onclick={() => handleSubmenuItemClick(subitem)}
|
|
320
|
+
role="menuitem"
|
|
276
321
|
>
|
|
277
322
|
{#if subitem.icon}
|
|
278
323
|
<Icon icon={subitem.icon} class="navmenu-submenu-icon" />
|