@14ch/svelte-ui 0.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.
Files changed (109) hide show
  1. package/README.md +359 -0
  2. package/dist/assets/styles/README.md +144 -0
  3. package/dist/assets/styles/core.scss +61 -0
  4. package/dist/assets/styles/import.scss +11 -0
  5. package/dist/assets/styles/optional/fonts.scss +23 -0
  6. package/dist/assets/styles/optional/reset.scss +230 -0
  7. package/dist/assets/styles/variables.scss +805 -0
  8. package/dist/components/Button.svelte +574 -0
  9. package/dist/components/Button.svelte.d.ts +56 -0
  10. package/dist/components/COMPONENT_DESIGN_GUIDELINES.md +127 -0
  11. package/dist/components/Checkbox.svelte +523 -0
  12. package/dist/components/Checkbox.svelte.d.ts +42 -0
  13. package/dist/components/CheckboxGroup.svelte +82 -0
  14. package/dist/components/CheckboxGroup.svelte.d.ts +13 -0
  15. package/dist/components/ColorPicker.svelte +496 -0
  16. package/dist/components/ColorPicker.svelte.d.ts +45 -0
  17. package/dist/components/Combobox.svelte +576 -0
  18. package/dist/components/Combobox.svelte.d.ts +52 -0
  19. package/dist/components/ConfirmDialog.svelte +116 -0
  20. package/dist/components/ConfirmDialog.svelte.d.ts +20 -0
  21. package/dist/components/Datepicker.svelte +578 -0
  22. package/dist/components/Datepicker.svelte.d.ts +72 -0
  23. package/dist/components/DatepickerCalendar.svelte +925 -0
  24. package/dist/components/DatepickerCalendar.svelte.d.ts +31 -0
  25. package/dist/components/Dialog.svelte +245 -0
  26. package/dist/components/Dialog.svelte.d.ts +38 -0
  27. package/dist/components/Drawer.svelte +383 -0
  28. package/dist/components/Drawer.svelte.d.ts +39 -0
  29. package/dist/components/Fab.svelte +486 -0
  30. package/dist/components/Fab.svelte.d.ts +51 -0
  31. package/dist/components/FileUploader.svelte +456 -0
  32. package/dist/components/FileUploader.svelte.d.ts +36 -0
  33. package/dist/components/Icon.svelte +167 -0
  34. package/dist/components/Icon.svelte.d.ts +21 -0
  35. package/dist/components/IconButton.svelte +557 -0
  36. package/dist/components/IconButton.svelte.d.ts +60 -0
  37. package/dist/components/ImageUploader.svelte +516 -0
  38. package/dist/components/ImageUploader.svelte.d.ts +37 -0
  39. package/dist/components/ImageUploaderPreview.svelte +157 -0
  40. package/dist/components/ImageUploaderPreview.svelte.d.ts +13 -0
  41. package/dist/components/Input.svelte +885 -0
  42. package/dist/components/Input.svelte.d.ts +75 -0
  43. package/dist/components/LoadingSpinner.svelte +116 -0
  44. package/dist/components/LoadingSpinner.svelte.d.ts +10 -0
  45. package/dist/components/Modal.svelte +313 -0
  46. package/dist/components/Modal.svelte.d.ts +34 -0
  47. package/dist/components/Pagination.svelte +276 -0
  48. package/dist/components/Pagination.svelte.d.ts +14 -0
  49. package/dist/components/Popup.svelte +676 -0
  50. package/dist/components/Popup.svelte.d.ts +40 -0
  51. package/dist/components/PopupMenu.svelte +421 -0
  52. package/dist/components/PopupMenu.svelte.d.ts +24 -0
  53. package/dist/components/PopupMenuButton.svelte +365 -0
  54. package/dist/components/PopupMenuButton.svelte.d.ts +42 -0
  55. package/dist/components/Radio.svelte +548 -0
  56. package/dist/components/Radio.svelte.d.ts +42 -0
  57. package/dist/components/RadioGroup.svelte +74 -0
  58. package/dist/components/RadioGroup.svelte.d.ts +14 -0
  59. package/dist/components/Select.svelte +479 -0
  60. package/dist/components/Select.svelte.d.ts +47 -0
  61. package/dist/components/Slider.svelte +473 -0
  62. package/dist/components/Slider.svelte.d.ts +46 -0
  63. package/dist/components/Snackbar.svelte +124 -0
  64. package/dist/components/Snackbar.svelte.d.ts +9 -0
  65. package/dist/components/SnackbarItem.svelte +423 -0
  66. package/dist/components/SnackbarItem.svelte.d.ts +21 -0
  67. package/dist/components/Switch.svelte +454 -0
  68. package/dist/components/Switch.svelte.d.ts +40 -0
  69. package/dist/components/Tab.svelte +193 -0
  70. package/dist/components/Tab.svelte.d.ts +14 -0
  71. package/dist/components/TabItem.svelte +140 -0
  72. package/dist/components/TabItem.svelte.d.ts +17 -0
  73. package/dist/components/Textarea.svelte +702 -0
  74. package/dist/components/Textarea.svelte.d.ts +64 -0
  75. package/dist/components/skeleton/Skeleton.svelte +235 -0
  76. package/dist/components/skeleton/Skeleton.svelte.d.ts +13 -0
  77. package/dist/components/skeleton/SkeletonAvatar.svelte +97 -0
  78. package/dist/components/skeleton/SkeletonAvatar.svelte.d.ts +8 -0
  79. package/dist/components/skeleton/SkeletonBox.svelte +105 -0
  80. package/dist/components/skeleton/SkeletonBox.svelte.d.ts +12 -0
  81. package/dist/components/skeleton/SkeletonButton.svelte +71 -0
  82. package/dist/components/skeleton/SkeletonButton.svelte.d.ts +8 -0
  83. package/dist/components/skeleton/SkeletonHeading.svelte +49 -0
  84. package/dist/components/skeleton/SkeletonHeading.svelte.d.ts +8 -0
  85. package/dist/components/skeleton/SkeletonMedia.svelte +115 -0
  86. package/dist/components/skeleton/SkeletonMedia.svelte.d.ts +9 -0
  87. package/dist/components/skeleton/SkeletonText.svelte +75 -0
  88. package/dist/components/skeleton/SkeletonText.svelte.d.ts +8 -0
  89. package/dist/index.d.ts +42 -0
  90. package/dist/index.js +43 -0
  91. package/dist/types/icon.d.ts +4 -0
  92. package/dist/types/icon.js +2 -0
  93. package/dist/types/menuItem.d.ts +8 -0
  94. package/dist/types/menuItem.js +1 -0
  95. package/dist/types/options.d.ts +6 -0
  96. package/dist/types/options.js +4 -0
  97. package/dist/types/skeleton.d.ts +77 -0
  98. package/dist/types/skeleton.js +19 -0
  99. package/dist/utils/accessibility.d.ts +48 -0
  100. package/dist/utils/accessibility.js +87 -0
  101. package/dist/utils/formatText.d.ts +4 -0
  102. package/dist/utils/formatText.js +44 -0
  103. package/dist/utils/mobile.d.ts +9 -0
  104. package/dist/utils/mobile.js +47 -0
  105. package/dist/utils/snackbar.svelte.d.ts +51 -0
  106. package/dist/utils/snackbar.svelte.js +107 -0
  107. package/dist/utils/style.d.ts +17 -0
  108. package/dist/utils/style.js +22 -0
  109. package/package.json +102 -0
@@ -0,0 +1,116 @@
1
+ <!-- ConfirmDialog.svelte -->
2
+
3
+ <script lang="ts">
4
+ import Dialog from './Dialog.svelte';
5
+ import Button from './Button.svelte';
6
+ import { convertToHtml } from '../utils/formatText';
7
+ import type { SvelteComponent } from 'svelte';
8
+
9
+ // =========================================================================
10
+ // Props, States & Constants
11
+ // =========================================================================
12
+ let {
13
+ // 基本プロパティ
14
+ title = 'Confirm',
15
+ description = 'Are you sure?',
16
+ confirmLabel = 'Confirm',
17
+ cancelLabel = 'Cancel',
18
+
19
+ // HTML属性
20
+ id,
21
+
22
+ // スタイル/レイアウト
23
+ variant = 'info',
24
+ width = 400,
25
+
26
+ // 状態/動作
27
+ isOpen = $bindable(false),
28
+ closeIfClickOutside = true,
29
+
30
+ // イベントハンドラー
31
+ onConfirm = () => {}, // No params for type inference
32
+ onCancel = () => {} // No params for type inference
33
+ }: {
34
+ // 基本プロパティ
35
+ title?: string;
36
+ description?: string;
37
+ confirmLabel?: string;
38
+ cancelLabel?: string;
39
+
40
+ // HTML属性
41
+ id?: string;
42
+
43
+ // スタイル/レイアウト
44
+ variant?: 'info' | 'warning' | 'danger';
45
+ width?: string | number;
46
+
47
+ // 状態/動作
48
+ isOpen?: boolean;
49
+ closeIfClickOutside?: boolean;
50
+
51
+ // イベントハンドラー
52
+ onConfirm?: () => void;
53
+ onCancel?: () => void;
54
+ } = $props();
55
+
56
+ let dialogRef: SvelteComponent | undefined = $state();
57
+
58
+ // =========================================================================
59
+ // Methods
60
+ // =========================================================================
61
+ const handleConfirm = (): void => {
62
+ onConfirm();
63
+ close();
64
+ };
65
+
66
+ const handleCancel = (): void => {
67
+ onCancel();
68
+ close();
69
+ };
70
+
71
+ export const open = (): void => {
72
+ isOpen = true;
73
+ };
74
+
75
+ export const close = (): void => {
76
+ isOpen = false;
77
+ };
78
+
79
+ export const toggle = (): void => {
80
+ isOpen = !isOpen;
81
+ };
82
+ </script>
83
+
84
+ <Dialog
85
+ bind:this={dialogRef}
86
+ bind:isOpen
87
+ {title}
88
+ {width}
89
+ {closeIfClickOutside}
90
+ id={id ? `${id}-dialog` : undefined}
91
+ >
92
+ <div class="confirm-dialog-message">
93
+ {@html convertToHtml(description)}
94
+ </div>
95
+
96
+ {#snippet footer()}
97
+ <Button variant="ghost" color="var(--svelte-ui-text-color)" onclick={handleCancel}>
98
+ {cancelLabel}
99
+ </Button>
100
+ <Button
101
+ variant="filled"
102
+ color={variant === 'danger'
103
+ ? 'var(--svelte-ui-danger-color)'
104
+ : variant === 'warning'
105
+ ? 'var(--svelte-ui-warning-color)'
106
+ : undefined}
107
+ onclick={handleConfirm}
108
+ >
109
+ {confirmLabel}
110
+ </Button>
111
+ {/snippet}
112
+ </Dialog>
113
+
114
+ <style>.confirm-dialog-message {
115
+ padding: 16px 0;
116
+ }</style>
@@ -0,0 +1,20 @@
1
+ type $$ComponentProps = {
2
+ title?: string;
3
+ description?: string;
4
+ confirmLabel?: string;
5
+ cancelLabel?: string;
6
+ id?: string;
7
+ variant?: 'info' | 'warning' | 'danger';
8
+ width?: string | number;
9
+ isOpen?: boolean;
10
+ closeIfClickOutside?: boolean;
11
+ onConfirm?: () => void;
12
+ onCancel?: () => void;
13
+ };
14
+ declare const ConfirmDialog: import("svelte").Component<$$ComponentProps, {
15
+ open: () => void;
16
+ close: () => void;
17
+ toggle: () => void;
18
+ }, "isOpen">;
19
+ type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
20
+ export default ConfirmDialog;
@@ -0,0 +1,578 @@
1
+ <!-- Datepicker.svelte -->
2
+
3
+ <script lang="ts">
4
+ import { type SvelteComponent } from 'svelte';
5
+ import dayjs from 'dayjs';
6
+ import localeData from 'dayjs/plugin/localeData';
7
+ import 'dayjs/locale/ja';
8
+ import 'dayjs/locale/en';
9
+ import 'dayjs/locale/fr';
10
+ import 'dayjs/locale/de';
11
+ import 'dayjs/locale/es';
12
+ import 'dayjs/locale/zh-cn';
13
+ import Input from './Input.svelte';
14
+ import Popup from './Popup.svelte';
15
+ import DatepickerCalendar from './DatepickerCalendar.svelte';
16
+ import { announceToScreenReader } from '../utils/accessibility';
17
+ import type { HTMLInputAttributes } from 'svelte/elements';
18
+ import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
19
+
20
+ dayjs.extend(localeData);
21
+
22
+ // =========================================================================
23
+ // Props, States & Constants
24
+ // =========================================================================
25
+
26
+ let {
27
+ // 基本プロパティ
28
+ value = $bindable(),
29
+ format,
30
+ nullString = '',
31
+ locale = 'en',
32
+ rangeSeparator = ' - ',
33
+
34
+ // HTML属性系
35
+ id = `datepicker-${Math.random().toString(36).substring(2, 15)}`,
36
+ inputAttributes,
37
+
38
+ // スタイル/レイアウト
39
+ variant = 'default',
40
+ focusStyle = 'outline',
41
+ fullWidth = false,
42
+ rounded = false,
43
+
44
+ // アイコン関連
45
+ hasIcon = false,
46
+ iconFilled = false,
47
+ iconWeight = 300,
48
+ iconGrade = 0,
49
+ iconOpticalSize = 24,
50
+ iconVariant = 'outlined',
51
+
52
+ // 状態/動作
53
+ disabled = false,
54
+ mode = 'single',
55
+ allowDirectInput = false,
56
+ openIfClicked = true,
57
+ minDate,
58
+ maxDate,
59
+
60
+ // 入力イベント
61
+ onchange = () => {}, // No params for type inference
62
+ oninput = () => {}, // No params for type inference
63
+
64
+ // フォーカスイベント
65
+ onfocus = () => {}, // No params for type inference
66
+ onblur = () => {}, // No params for type inference
67
+
68
+ // キーボードイベント
69
+ onkeydown = () => {}, // No params for type inference
70
+ onkeyup = () => {}, // No params for type inference
71
+
72
+ // マウスイベント
73
+ onclick = () => {}, // No params for type inference
74
+ onmousedown = () => {}, // No params for type inference
75
+ onmouseup = () => {}, // No params for type inference
76
+ onmouseenter = () => {}, // No params for type inference
77
+ onmouseleave = () => {}, // No params for type inference
78
+ onmouseover = () => {}, // No params for type inference
79
+ onmouseout = () => {}, // No params for type inference
80
+ oncontextmenu = () => {}, // No params for type inference
81
+ onauxclick = () => {}, // No params for type inference
82
+
83
+ // タッチイベント
84
+ ontouchstart = () => {}, // No params for type inference
85
+ ontouchend = () => {}, // No params for type inference
86
+ ontouchmove = () => {}, // No params for type inference
87
+ ontouchcancel = () => {}, // No params for type inference
88
+
89
+ // ポインターイベント
90
+ onpointerdown = () => {}, // No params for type inference
91
+ onpointerup = () => {}, // No params for type inference
92
+ onpointerenter = () => {}, // No params for type inference
93
+ onpointerleave = () => {}, // No params for type inference
94
+ onpointermove = () => {}, // No params for type inference
95
+ onpointercancel = () => {}, // No params for type inference
96
+
97
+ // その他
98
+ ...restProps
99
+ }: {
100
+ // 基本プロパティ
101
+ value: Date | { start: Date; end: Date } | undefined;
102
+ format?: string;
103
+ nullString?: string;
104
+ locale?: 'en' | 'ja' | 'fr' | 'de' | 'es' | 'zh-cn';
105
+ rangeSeparator?: string;
106
+
107
+ // HTML属性系
108
+ id?: string;
109
+ inputAttributes?: HTMLInputAttributes | undefined;
110
+
111
+ // スタイル/レイアウト
112
+ variant?: 'default' | 'inline';
113
+ focusStyle?: 'background' | 'outline' | 'none';
114
+ fullWidth?: boolean;
115
+ rounded?: boolean;
116
+
117
+ // アイコン関連
118
+ hasIcon?: boolean;
119
+ iconFilled?: boolean;
120
+ iconWeight?: IconWeight;
121
+ iconGrade?: IconGrade;
122
+ iconOpticalSize?: IconOpticalSize;
123
+ iconVariant?: IconVariant;
124
+
125
+ // 状態/動作
126
+ disabled?: boolean;
127
+ mode?: 'single' | 'range';
128
+ allowDirectInput?: boolean;
129
+ openIfClicked?: boolean;
130
+ minDate?: Date;
131
+ maxDate?: Date;
132
+
133
+ // 入力イベント
134
+ onchange?: (value: Date | { start: Date; end: Date } | undefined) => void;
135
+ oninput?: (value: string) => void;
136
+
137
+ // フォーカスイベント
138
+ onfocus?: Function; // No params for type inference
139
+ onblur?: Function; // No params for type inference
140
+ onkeydown?: Function; // No params for type inference
141
+ onkeyup?: Function; // No params for type inference
142
+ onclick?: Function; // No params for type inference
143
+ onmousedown?: Function; // No params for type inference
144
+ onmouseup?: Function; // No params for type inference
145
+ onmouseenter?: Function; // No params for type inference
146
+ onmouseleave?: Function; // No params for type inference
147
+ onmouseover?: Function; // No params for type inference
148
+ onmouseout?: Function; // No params for type inference
149
+ oncontextmenu?: Function; // No params for type inference
150
+ onauxclick?: Function; // No params for type inference
151
+ ontouchstart?: Function; // No params for type inference
152
+ ontouchend?: Function; // No params for type inference
153
+ ontouchmove?: Function; // No params for type inference
154
+ ontouchcancel?: Function; // No params for type inference
155
+ onpointerdown?: Function; // No params for type inference
156
+ onpointerup?: Function; // No params for type inference
157
+ onpointerenter?: Function; // No params for type inference
158
+ onpointerleave?: Function; // No params for type inference
159
+ onpointermove?: Function; // No params for type inference
160
+ onpointercancel?: Function; // No params for type inference
161
+
162
+ // その他
163
+ [key: string]: any;
164
+ } = $props();
165
+
166
+ // =========================================================================
167
+ // Props, States & Constants
168
+ // =========================================================================
169
+ let inputRef: any = $state();
170
+ let containerElement: HTMLDivElement | undefined = $state();
171
+ let popupRef: SvelteComponent | undefined = $state();
172
+ let datapickerCalendarRef: SvelteComponent | undefined = $state();
173
+ let openedViaKeyboard: boolean = $state(false);
174
+ let displayValue = $state('');
175
+
176
+ const calendarId = `${id}-calendar`;
177
+
178
+ // =========================================================================
179
+
180
+ const localeConfig = {
181
+ en: {
182
+ defaultFormat: 'MM/DD/YYYY (ddd)',
183
+ rangeFormat: 'MM/DD/YYYY',
184
+ selectDateLabel: 'Select a date. Current value:',
185
+ notSelected: 'Not selected',
186
+ directInputPlaceholder: 'Enter date'
187
+ },
188
+ ja: {
189
+ defaultFormat: 'YYYY/M/D(ddd)',
190
+ rangeFormat: 'YYYY/M/D',
191
+ selectDateLabel: '日付を選択してください。現在の値:',
192
+ notSelected: '未選択',
193
+ directInputPlaceholder: '日付を入力してください'
194
+ },
195
+ fr: {
196
+ defaultFormat: 'DD/MM/YYYY (ddd)',
197
+ rangeFormat: 'DD/MM/YYYY',
198
+ selectDateLabel: 'Sélectionnez une date. Valeur actuelle :',
199
+ notSelected: 'Non sélectionné',
200
+ directInputPlaceholder: 'Saisir la date'
201
+ },
202
+ de: {
203
+ defaultFormat: 'DD.MM.YYYY (ddd)',
204
+ rangeFormat: 'DD.MM.YYYY',
205
+ selectDateLabel: 'Wählen Sie ein Datum. Aktueller Wert:',
206
+ notSelected: 'Nicht ausgewählt',
207
+ directInputPlaceholder: 'Datum eingeben'
208
+ },
209
+ es: {
210
+ defaultFormat: 'DD/MM/YYYY (ddd)',
211
+ rangeFormat: 'DD/MM/YYYY',
212
+ selectDateLabel: 'Seleccione una fecha. Valor actual:',
213
+ notSelected: 'No seleccionado',
214
+ directInputPlaceholder: 'Introducir fecha'
215
+ },
216
+ 'zh-cn': {
217
+ defaultFormat: 'YYYY/M/D(ddd)',
218
+ rangeFormat: 'YYYY/M/D',
219
+ selectDateLabel: '请选择日期。当前值:',
220
+ notSelected: '未选择',
221
+ directInputPlaceholder: '请输入日期'
222
+ }
223
+ };
224
+
225
+ // =========================================================================
226
+ // Effects
227
+ // =========================================================================
228
+ $effect(() => {
229
+ dayjs.locale(locale);
230
+ });
231
+
232
+ $effect(() => {
233
+ const formatWithLocale = (date: Date) => dayjs(date).locale(locale).format(finalFormat);
234
+
235
+ if (mode === 'range' && value && 'start' in value && 'end' in value) {
236
+ displayValue = `${formatWithLocale(value.start)}${rangeSeparator}${formatWithLocale(value.end)}`;
237
+ } else if (mode === 'single' && value && value instanceof Date) {
238
+ displayValue = formatWithLocale(value);
239
+ } else {
240
+ displayValue = '';
241
+ }
242
+ });
243
+
244
+ // =========================================================================
245
+ // Methods
246
+ // =========================================================================
247
+ const handleChange = () => {
248
+ // スクリーンリーダーアナウンス
249
+ if (value) {
250
+ if (mode === 'range' && typeof value === 'object' && 'start' in value && 'end' in value) {
251
+ const startDate = dayjs(value.start).format(finalFormat);
252
+ const endDate = dayjs(value.end).format(finalFormat);
253
+ announceToScreenReader(`Date range selected: ${startDate} to ${endDate}`);
254
+ } else if (value instanceof Date) {
255
+ const formattedDate = dayjs(value).format(finalFormat);
256
+ announceToScreenReader(`Date selected: ${formattedDate}`);
257
+ }
258
+ }
259
+
260
+ onchange(value);
261
+
262
+ // 単一日付選択時、または範囲選択時の終了日選択時にポップアップを閉じる
263
+ if (
264
+ mode === 'single' ||
265
+ (mode === 'range' &&
266
+ value &&
267
+ typeof value === 'object' &&
268
+ 'start' in value &&
269
+ 'end' in value &&
270
+ value.end)
271
+ ) {
272
+ popupRef?.close();
273
+ }
274
+ };
275
+
276
+ const handleClick = (event: MouseEvent) => {
277
+ if (disabled) return;
278
+ if (openIfClicked) {
279
+ openedViaKeyboard = false;
280
+ open();
281
+ }
282
+ onclick?.(event);
283
+ };
284
+
285
+ const handleKeyDown = (event: KeyboardEvent) => {
286
+ if (disabled) return;
287
+ onkeydown(event);
288
+
289
+ switch (event.key) {
290
+ case 'Enter':
291
+ case ' ':
292
+ // 直接入力が許可されている場合はEnterキーで入力を確定
293
+ if (allowDirectInput && event.key === 'Enter') {
294
+ return; // Inputコンポーネントの処理に任せる
295
+ }
296
+ event?.preventDefault?.();
297
+ openedViaKeyboard = true;
298
+ open();
299
+ break;
300
+ case 'ArrowDown':
301
+ // ポップアップが既に開いている場合は何もしない(DatepickerCalendarのキーボード処理に任せる)
302
+ if (popupRef?.getIsOpen && popupRef.getIsOpen()) {
303
+ return;
304
+ }
305
+ event.preventDefault();
306
+ openedViaKeyboard = true;
307
+ open();
308
+ break;
309
+ case 'Escape':
310
+ event.preventDefault();
311
+ close();
312
+ break;
313
+ }
314
+ };
315
+
316
+ const handleKeyup = (event: KeyboardEvent) => {
317
+ if (disabled) return;
318
+ onkeyup(event);
319
+ };
320
+
321
+ const handleInput = (event: Event) => {
322
+ if (disabled) return;
323
+ const target = event.target as HTMLInputElement;
324
+ oninput?.(target.value);
325
+ };
326
+
327
+ // マウスイベント
328
+ const handleMouseDown = (event: MouseEvent) => {
329
+ if (disabled) return;
330
+ onmousedown?.(event);
331
+ };
332
+
333
+ const handleMouseUp = (event: MouseEvent) => {
334
+ if (disabled) return;
335
+ onmouseup?.(event);
336
+ };
337
+
338
+ const handleMouseEnter = (event: MouseEvent) => {
339
+ if (disabled) return;
340
+ onmouseenter?.(event);
341
+ };
342
+
343
+ const handleMouseLeave = (event: MouseEvent) => {
344
+ if (disabled) return;
345
+ onmouseleave?.(event);
346
+ };
347
+
348
+ const handleMouseOver = (event: MouseEvent) => {
349
+ if (disabled) return;
350
+ onmouseover?.(event);
351
+ };
352
+
353
+ const handleMouseOut = (event: MouseEvent) => {
354
+ if (disabled) return;
355
+ onmouseout?.(event);
356
+ };
357
+
358
+ const handleContextMenu = (event: MouseEvent) => {
359
+ if (disabled) return;
360
+ oncontextmenu?.(event);
361
+ };
362
+
363
+ const handleAuxClick = (event: MouseEvent) => {
364
+ if (disabled) return;
365
+ onauxclick?.(event);
366
+ };
367
+
368
+ // タッチイベント
369
+ const handleTouchStart = (event: TouchEvent) => {
370
+ if (disabled) return;
371
+ ontouchstart?.(event);
372
+ };
373
+
374
+ const handleTouchEnd = (event: TouchEvent) => {
375
+ if (disabled) return;
376
+ ontouchend?.(event);
377
+ };
378
+
379
+ const handleTouchMove = (event: TouchEvent) => {
380
+ if (disabled) return;
381
+ ontouchmove?.(event);
382
+ };
383
+
384
+ const handleTouchCancel = (event: TouchEvent) => {
385
+ if (disabled) return;
386
+ ontouchcancel?.(event);
387
+ };
388
+
389
+ // ポインターイベント
390
+ const handlePointerDown = (event: PointerEvent) => {
391
+ if (disabled) return;
392
+ onpointerdown?.(event);
393
+ };
394
+
395
+ const handlePointerUp = (event: PointerEvent) => {
396
+ if (disabled) return;
397
+ onpointerup?.(event);
398
+ };
399
+
400
+ const handlePointerEnter = (event: PointerEvent) => {
401
+ if (disabled) return;
402
+ onpointerenter?.(event);
403
+ };
404
+
405
+ const handlePointerLeave = (event: PointerEvent) => {
406
+ if (disabled) return;
407
+ onpointerleave?.(event);
408
+ };
409
+
410
+ const handlePointerMove = (event: PointerEvent) => {
411
+ if (disabled) return;
412
+ onpointermove?.(event);
413
+ };
414
+
415
+ const handlePointerCancel = (event: PointerEvent) => {
416
+ if (disabled) return;
417
+ onpointercancel?.(event);
418
+ };
419
+
420
+ const handlePopupOpen = () => {
421
+ // DatepickerCalendarのイベントハンドラーを有効にする
422
+ datapickerCalendarRef?.handlePopupOpen();
423
+ // キーボードで開いた場合は、最初のキーボード操作時にフォーカス表示が有効になる
424
+ };
425
+
426
+ const handlePopupClose = () => {
427
+ // DatepickerCalendarのイベントハンドラーを無効にする
428
+ datapickerCalendarRef?.handlePopupClose();
429
+ // 状態をリセット
430
+ openedViaKeyboard = false;
431
+ };
432
+
433
+ const handleFocus = (event: FocusEvent) => {
434
+ if (disabled) return;
435
+ // キーボードでのフォーカス時のみPopupを開く
436
+ // クリック時は handleClick で処理
437
+ onfocus(event);
438
+ };
439
+
440
+ const handleBlur = (event: FocusEvent) => {
441
+ if (disabled) return;
442
+ // Popupの外側クリック機能に依存するため、ここでは自動クローズしない
443
+ // フォーカスイベントのみをハンドルする
444
+ onblur(event);
445
+ };
446
+
447
+ const handleInputChange = (inputValue: string | number | undefined) => {
448
+ if (disabled) return;
449
+ if (!allowDirectInput) return;
450
+
451
+ const inputStr = String(inputValue || '');
452
+ if (!inputStr) {
453
+ value = undefined;
454
+ onchange(value);
455
+ return;
456
+ }
457
+
458
+ // 日付の解析を試行
459
+ const parsedDate = dayjs(inputStr, finalFormat, locale, true);
460
+ if (parsedDate.isValid()) {
461
+ value = parsedDate.toDate();
462
+ onchange(value);
463
+ }
464
+ };
465
+
466
+ export const open = () => {
467
+ datapickerCalendarRef?.reset();
468
+ popupRef?.open();
469
+ };
470
+
471
+ export const close = () => {
472
+ popupRef?.close();
473
+ };
474
+
475
+ export const toggle = () => {
476
+ datapickerCalendarRef?.reset();
477
+ popupRef?.toggle();
478
+ };
479
+
480
+ // =========================================================================
481
+ // $derived
482
+ // =========================================================================
483
+
484
+ const currentLocaleConfig = $derived(localeConfig[locale]);
485
+ const finalFormat = $derived(
486
+ format ||
487
+ (mode === 'range' ? currentLocaleConfig.rangeFormat : currentLocaleConfig.defaultFormat)
488
+ );
489
+ const placeholderText = $derived(
490
+ allowDirectInput
491
+ ? nullString || currentLocaleConfig.directInputPlaceholder
492
+ : nullString || currentLocaleConfig.notSelected
493
+ );
494
+ </script>
495
+
496
+ <div
497
+ bind:this={containerElement}
498
+ class="datepicker"
499
+ class:datepicker--full-width={fullWidth}
500
+ data-testid="datepicker"
501
+ >
502
+ <Input
503
+ bind:this={inputRef}
504
+ value={displayValue}
505
+ {variant}
506
+ {focusStyle}
507
+ {fullWidth}
508
+ {rounded}
509
+ {disabled}
510
+ readonly={!allowDirectInput}
511
+ placeholder={placeholderText}
512
+ rightIcon={hasIcon ? (mode === 'range' ? 'date_range' : 'calendar_today') : undefined}
513
+ {iconFilled}
514
+ {iconWeight}
515
+ {iconGrade}
516
+ {iconOpticalSize}
517
+ {iconVariant}
518
+ onRightIconClick={handleClick}
519
+ onclick={handleClick}
520
+ onfocus={handleFocus}
521
+ onblur={handleBlur}
522
+ onchange={handleInputChange}
523
+ oninput={handleInput}
524
+ onkeydown={handleKeyDown}
525
+ onkeyup={handleKeyup}
526
+ onmousedown={handleMouseDown}
527
+ onmouseup={handleMouseUp}
528
+ onmouseenter={handleMouseEnter}
529
+ onmouseleave={handleMouseLeave}
530
+ onmouseover={handleMouseOver}
531
+ onmouseout={handleMouseOut}
532
+ oncontextmenu={handleContextMenu}
533
+ onauxclick={handleAuxClick}
534
+ ontouchstart={handleTouchStart}
535
+ ontouchend={handleTouchEnd}
536
+ ontouchmove={handleTouchMove}
537
+ ontouchcancel={handleTouchCancel}
538
+ onpointerdown={handlePointerDown}
539
+ onpointerup={handlePointerUp}
540
+ onpointerenter={handlePointerEnter}
541
+ onpointerleave={handlePointerLeave}
542
+ onpointermove={handlePointerMove}
543
+ onpointercancel={handlePointerCancel}
544
+ {id}
545
+ {inputAttributes}
546
+ {...restProps}
547
+ />
548
+ </div>
549
+ <Popup
550
+ bind:this={popupRef}
551
+ anchorElement={containerElement}
552
+ position="bottom"
553
+ margin={4}
554
+ onOpen={handlePopupOpen}
555
+ onClose={handlePopupClose}
556
+ id={id ? `${id}-popup` : undefined}
557
+ >
558
+ <DatepickerCalendar
559
+ bind:this={datapickerCalendarRef}
560
+ bind:value
561
+ {mode}
562
+ onchange={handleChange}
563
+ {minDate}
564
+ {maxDate}
565
+ {locale}
566
+ id={calendarId}
567
+ />
568
+ </Popup>
569
+
570
+ <style>.datepicker {
571
+ position: relative;
572
+ display: inline-block;
573
+ width: auto;
574
+ }
575
+ .datepicker.datepicker--full-width {
576
+ display: block;
577
+ width: 100%;
578
+ }</style>