@14ch/svelte-ui 0.0.72 → 0.0.73
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.
|
@@ -506,6 +506,10 @@
|
|
|
506
506
|
return `calendar-date-${date.format('YYYY-MM-DD')}`;
|
|
507
507
|
};
|
|
508
508
|
|
|
509
|
+
// id 未指定でも aria の参照先が作れるようフォールバックを持つ
|
|
510
|
+
const fallbackId = `datepicker-calendar-${Math.random().toString(36).substring(2, 15)}`;
|
|
511
|
+
const monthLabelId = $derived(`${id ?? fallbackId}-month-label`);
|
|
512
|
+
|
|
509
513
|
const handleMouseEnter = (date: dayjs.Dayjs) => {
|
|
510
514
|
if (mode !== 'range' || isOutOfRange(date)) return;
|
|
511
515
|
hoveredDate = date;
|
|
@@ -584,6 +588,7 @@
|
|
|
584
588
|
</div>
|
|
585
589
|
<button
|
|
586
590
|
type="button"
|
|
591
|
+
id={monthLabelId}
|
|
587
592
|
class="datepicker-calendar__header__month-label-button"
|
|
588
593
|
aria-live="polite"
|
|
589
594
|
aria-atomic="true"
|
|
@@ -632,7 +637,7 @@
|
|
|
632
637
|
{/each}
|
|
633
638
|
</div>
|
|
634
639
|
{:else}
|
|
635
|
-
<div class="datepicker-calendar__date-selection" role="grid" aria-labelledby=
|
|
640
|
+
<div class="datepicker-calendar__date-selection" role="grid" aria-labelledby={monthLabelId}>
|
|
636
641
|
<div class="datepicker-calendar__day-list" role="row">
|
|
637
642
|
{#each DAY_ARRAY as day}
|
|
638
643
|
<div class="datepicker-calendar__day-item" role="columnheader">
|
|
@@ -143,9 +143,19 @@
|
|
|
143
143
|
|
|
144
144
|
const dialogClasses = $derived('');
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
// id 未指定でも aria の参照先が作れるようフォールバックを持つ
|
|
147
|
+
const fallbackId = `dialog-${Math.random().toString(36).substring(2, 15)}`;
|
|
148
|
+
const baseId = $derived(id ?? fallbackId);
|
|
149
|
+
const titleId = $derived(`${baseId}-dialog-title`);
|
|
150
|
+
const descriptionId = $derived(`${baseId}-dialog-description`);
|
|
151
|
+
|
|
152
|
+
// 名前は 1 つだけ与える。aria-labelledby は aria-label より優先されるため、両方は付けない。
|
|
153
|
+
// header を差し替えるとタイトル要素が描画されないので、そのときは title を aria-label で渡す
|
|
154
|
+
const hasTitleElement = $derived(!!title && !header);
|
|
155
|
+
const ariaLabelValue = $derived(ariaLabel || (title && !hasTitleElement ? title : undefined));
|
|
156
|
+
const ariaLabelledby = $derived(!ariaLabelValue && hasTitleElement ? titleId : undefined);
|
|
147
157
|
const ariaDescribedbyValue = $derived(
|
|
148
|
-
ariaDescribedby || (description ?
|
|
158
|
+
ariaDescribedby || (description ? descriptionId : undefined)
|
|
149
159
|
);
|
|
150
160
|
</script>
|
|
151
161
|
|
|
@@ -158,7 +168,7 @@
|
|
|
158
168
|
{onOpen}
|
|
159
169
|
{onClose}
|
|
160
170
|
componentType="Dialog"
|
|
161
|
-
{
|
|
171
|
+
ariaLabel={ariaLabelValue}
|
|
162
172
|
{ariaLabelledby}
|
|
163
173
|
ariaDescribedby={ariaDescribedbyValue}
|
|
164
174
|
customClass={dialogClasses}
|
|
@@ -171,14 +181,14 @@
|
|
|
171
181
|
{#if header}
|
|
172
182
|
{@render header()}
|
|
173
183
|
{:else}
|
|
174
|
-
<div class="dialog__title" id={
|
|
184
|
+
<div class="dialog__title" id={titleId}>
|
|
175
185
|
{title || ''}
|
|
176
186
|
</div>
|
|
177
187
|
{/if}
|
|
178
188
|
</div>
|
|
179
189
|
{/if}
|
|
180
190
|
{#if description}
|
|
181
|
-
<div class="dialog__description" id={
|
|
191
|
+
<div class="dialog__description" id={descriptionId}>
|
|
182
192
|
{description}
|
|
183
193
|
</div>
|
|
184
194
|
{/if}
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
focusFirstOnOpen = false,
|
|
94
94
|
|
|
95
95
|
// ARIA/アクセシビリティ
|
|
96
|
-
ariaLabel
|
|
96
|
+
ariaLabel,
|
|
97
97
|
ariaDescribedby,
|
|
98
98
|
|
|
99
99
|
// イベントハンドラー
|
|
@@ -108,17 +108,17 @@
|
|
|
108
108
|
// =========================================================================
|
|
109
109
|
/** Opens the drawer. */
|
|
110
110
|
export const open = (): void => {
|
|
111
|
-
modalRef?.open(title ||
|
|
111
|
+
modalRef?.open(title || ariaLabelValue);
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
/** Closes the drawer. */
|
|
115
115
|
export const close = (): void => {
|
|
116
|
-
modalRef?.close(title ||
|
|
116
|
+
modalRef?.close(title || ariaLabelValue);
|
|
117
117
|
};
|
|
118
118
|
|
|
119
119
|
/** Toggles between open and closed. */
|
|
120
120
|
export const toggle = (): void => {
|
|
121
|
-
modalRef?.toggle(title ||
|
|
121
|
+
modalRef?.toggle(title || ariaLabelValue);
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
/** @internal Completes the close animation. Called automatically. */
|
|
@@ -157,9 +157,19 @@
|
|
|
157
157
|
['drawer-wrapper', `drawer-wrapper--${position}`].filter(Boolean).join(' ')
|
|
158
158
|
);
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
// id 未指定でも aria の参照先が作れるようフォールバックを持つ
|
|
161
|
+
const fallbackId = `drawer-${Math.random().toString(36).substring(2, 15)}`;
|
|
162
|
+
const baseId = $derived(id ?? fallbackId);
|
|
163
|
+
const titleId = $derived(`${baseId}-drawer-title`);
|
|
164
|
+
const descriptionId = $derived(`${baseId}-drawer-description`);
|
|
165
|
+
|
|
166
|
+
// 名前は 1 つだけ与える。aria-labelledby は aria-label より優先されるため、両方は付けない。
|
|
167
|
+
// header を差し替えるとタイトル要素が描画されないので、そのときは title を aria-label で渡す
|
|
168
|
+
const hasTitleElement = $derived(!!title && !header);
|
|
169
|
+
const ariaLabelValue = $derived(ariaLabel ?? (hasTitleElement ? undefined : title || 'Drawer'));
|
|
170
|
+
const ariaLabelledby = $derived(!ariaLabelValue && hasTitleElement ? titleId : undefined);
|
|
161
171
|
const ariaDescribedbyValue = $derived(
|
|
162
|
-
ariaDescribedby || (description ?
|
|
172
|
+
ariaDescribedby || (description ? descriptionId : undefined)
|
|
163
173
|
);
|
|
164
174
|
</script>
|
|
165
175
|
|
|
@@ -172,27 +182,30 @@
|
|
|
172
182
|
{onOpen}
|
|
173
183
|
{onClose}
|
|
174
184
|
componentType="Drawer"
|
|
175
|
-
{
|
|
185
|
+
ariaLabel={ariaLabelValue}
|
|
176
186
|
{ariaLabelledby}
|
|
177
187
|
ariaDescribedby={ariaDescribedbyValue}
|
|
178
188
|
customClass={drawerClasses}
|
|
179
189
|
customStyles={drawerStyles()}
|
|
180
190
|
id={id ? `${id}-modal` : undefined}
|
|
181
191
|
>
|
|
182
|
-
<div
|
|
192
|
+
<div
|
|
193
|
+
class="drawer drawer--{position} {scrollable ? 'drawer--scrollable' : ''}"
|
|
194
|
+
data-testid="drawer"
|
|
195
|
+
>
|
|
183
196
|
{#if header || title}
|
|
184
197
|
<div class="drawer__header">
|
|
185
198
|
{#if header}
|
|
186
199
|
{@render header()}
|
|
187
200
|
{:else}
|
|
188
|
-
<div class="drawer__title" id={
|
|
201
|
+
<div class="drawer__title" id={titleId}>
|
|
189
202
|
{title || ''}
|
|
190
203
|
</div>
|
|
191
204
|
{/if}
|
|
192
205
|
</div>
|
|
193
206
|
{/if}
|
|
194
207
|
{#if description}
|
|
195
|
-
<div class="drawer__description" id={
|
|
208
|
+
<div class="drawer__description" id={descriptionId}>
|
|
196
209
|
{description}
|
|
197
210
|
</div>
|
|
198
211
|
{/if}
|
|
@@ -305,10 +305,13 @@
|
|
|
305
305
|
}
|
|
306
306
|
}}
|
|
307
307
|
aria-label={t('fileUploader.uploadFile')}
|
|
308
|
-
aria-describedby={`${id}-
|
|
308
|
+
aria-describedby={`${id}-description`}
|
|
309
309
|
>
|
|
310
310
|
{#if value && value.length > 0}
|
|
311
|
-
<div
|
|
311
|
+
<div
|
|
312
|
+
id={`${id}-description`}
|
|
313
|
+
class="file-uploader__description file-uploader__description--with-file"
|
|
314
|
+
>
|
|
312
315
|
<Icon
|
|
313
316
|
size={iconSize}
|
|
314
317
|
filled={iconFilled}
|
|
@@ -340,7 +343,7 @@
|
|
|
340
343
|
{/each}
|
|
341
344
|
</ul>
|
|
342
345
|
{:else}
|
|
343
|
-
<div class="file-uploader__description">
|
|
346
|
+
<div id={`${id}-description`} class="file-uploader__description">
|
|
344
347
|
<Icon
|
|
345
348
|
size={iconSize}
|
|
346
349
|
filled={iconFilled}
|