@14ch/svelte-ui 0.0.72 → 0.0.74

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="month-label">
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
- const ariaLabelledby = $derived(title ? 'dialog-title' : undefined);
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 ? 'dialog-description' : undefined)
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
- {ariaLabel}
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={id ? `${id}-dialog-title` : undefined}>
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={id ? `${id}-dialog-description` : undefined}>
191
+ <div class="dialog__description" id={descriptionId}>
182
192
  {description}
183
193
  </div>
184
194
  {/if}
@@ -211,7 +221,6 @@
211
221
  gap: var(--svelte-ui-dialog-gap);
212
222
  align-items: center;
213
223
  justify-content: stretch;
214
- min-height: var(--svelte-ui-dialog-header-height);
215
224
  padding: var(--svelte-ui-dialog-padding);
216
225
  }
217
226
  .dialog__header .dialog__title {
@@ -248,8 +257,12 @@
248
257
  overflow: hidden;
249
258
  }
250
259
 
260
+ /* scrollable では線で区切られた固定バーになるため、高さは変数で決める。
261
+ 明示した min-height はフレックスアイテムの自動最小サイズを打ち消すので縮小を止める */
251
262
  .dialog--scrollable .dialog__header {
252
- margin-bottom: 0;
263
+ flex-shrink: 0;
264
+ min-height: var(--svelte-ui-dialog-header-height);
265
+ padding-block: 0;
253
266
  border-bottom: solid 1px var(--svelte-ui-border-color);
254
267
  }
255
268
 
@@ -93,7 +93,7 @@
93
93
  focusFirstOnOpen = false,
94
94
 
95
95
  // ARIA/アクセシビリティ
96
- ariaLabel = 'Drawer',
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 || ariaLabel);
111
+ modalRef?.open(title || ariaLabelValue);
112
112
  };
113
113
 
114
114
  /** Closes the drawer. */
115
115
  export const close = (): void => {
116
- modalRef?.close(title || ariaLabel);
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 || ariaLabel);
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
- const ariaLabelledby = $derived(title ? 'drawer-title' : undefined);
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 ? 'drawer-description' : undefined)
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
- {ariaLabel}
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 class="drawer drawer--{position} {scrollable ? 'drawer--scrollable' : ''}" data-testid="drawer">
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={id ? `${id}-drawer-title` : undefined}>
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={id ? `${id}-drawer-description` : undefined}>
208
+ <div class="drawer__description" id={descriptionId}>
196
209
  {description}
197
210
  </div>
198
211
  {/if}
@@ -329,7 +342,6 @@
329
342
  gap: var(--svelte-ui-drawer-gap);
330
343
  align-items: center;
331
344
  justify-content: stretch;
332
- min-height: var(--svelte-ui-drawer-header-height);
333
345
  padding: var(--svelte-ui-drawer-header-padding);
334
346
  }
335
347
  .drawer__header .drawer__title {
@@ -362,8 +374,14 @@
362
374
  border-bottom: 1px solid var(--svelte-ui-border-weak-color);
363
375
  }
364
376
 
377
+ .drawer--scrollable {
378
+ /* 線で区切られた固定バーになるため、高さは変数で決める。
379
+ 明示した min-height はフレックスアイテムの自動最小サイズを打ち消すので縮小を止める */
380
+ }
365
381
  .drawer--scrollable .drawer__header {
366
- margin-bottom: 0;
382
+ flex-shrink: 0;
383
+ min-height: var(--svelte-ui-drawer-header-height);
384
+ padding-block: 0;
367
385
  border-bottom: solid var(--svelte-ui-border-width, 1px) var(--svelte-ui-border-weak-color);
368
386
  }
369
387
  .drawer--scrollable .drawer__body {
@@ -305,10 +305,13 @@
305
305
  }
306
306
  }}
307
307
  aria-label={t('fileUploader.uploadFile')}
308
- aria-describedby={`${id}-help`}
308
+ aria-describedby={`${id}-description`}
309
309
  >
310
310
  {#if value && value.length > 0}
311
- <div class="file-uploader__description file-uploader__description--with-file">
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}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@14ch/svelte-ui",
3
3
  "description": "Modern Svelte UI components library with TypeScript support",
4
4
  "private": false,
5
- "version": "0.0.72",
5
+ "version": "0.0.74",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "svelte",