@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,548 @@
1
+ <!-- Radio.svelte -->
2
+
3
+ <script lang="ts">
4
+ import { type Snippet } from 'svelte';
5
+ import type { HTMLInputAttributes } from 'svelte/elements';
6
+
7
+ // =========================================================================
8
+ // Props, States & Constants
9
+ // =========================================================================
10
+ let {
11
+ // Snippet
12
+ children,
13
+
14
+ // 基本プロパティ
15
+ name = '',
16
+ value = '',
17
+ currentValue = $bindable(null),
18
+
19
+ // HTML属性系
20
+ id = `radio-${Math.random().toString(36).substring(2, 15)}`,
21
+ inputAttributes,
22
+
23
+ // スタイル/レイアウト
24
+ size = 'medium',
25
+
26
+ // 状態/動作
27
+ disabled = false,
28
+ required = false,
29
+ reducedMotion = false,
30
+
31
+ // 入力イベント
32
+ onchange = () => {}, // No params for type inference
33
+
34
+ // フォーカスイベント
35
+ onfocus = () => {}, // No params for type inference
36
+ onblur = () => {}, // No params for type inference
37
+
38
+ // キーボードイベント
39
+ onkeydown = () => {}, // No params for type inference
40
+ onkeyup = () => {}, // No params for type inference
41
+
42
+ // マウスイベント
43
+ onclick = () => {}, // No params for type inference
44
+ onmousedown = () => {}, // No params for type inference
45
+ onmouseup = () => {}, // No params for type inference
46
+ onmouseenter = () => {}, // No params for type inference
47
+ onmouseleave = () => {}, // No params for type inference
48
+ onmouseover = () => {}, // No params for type inference
49
+ onmouseout = () => {}, // No params for type inference
50
+ oncontextmenu = () => {}, // No params for type inference
51
+ onauxclick = () => {}, // No params for type inference
52
+
53
+ // タッチイベント
54
+ ontouchstart = () => {}, // No params for type inference
55
+ ontouchend = () => {}, // No params for type inference
56
+ ontouchmove = () => {}, // No params for type inference
57
+ ontouchcancel = () => {}, // No params for type inference
58
+
59
+ // ポインターイベント
60
+ onpointerdown = () => {}, // No params for type inference
61
+ onpointerup = () => {}, // No params for type inference
62
+ onpointerenter = () => {}, // No params for type inference
63
+ onpointerleave = () => {}, // No params for type inference
64
+ onpointermove = () => {}, // No params for type inference
65
+ onpointercancel = () => {}, // No params for type inference
66
+
67
+ // その他
68
+ ...restProps
69
+ }: {
70
+ // Snippet
71
+ children?: Snippet;
72
+
73
+ // 基本プロパティ
74
+ name: string;
75
+ value: string | number | boolean;
76
+ currentValue: string | number | boolean | null;
77
+
78
+ // HTML属性系
79
+ id?: string;
80
+ inputAttributes?: HTMLInputAttributes | undefined;
81
+
82
+ // スタイル/レイアウト
83
+ size?: 'small' | 'medium' | 'large';
84
+
85
+ // 状態/動作
86
+ disabled?: boolean;
87
+ required?: boolean;
88
+ reducedMotion?: boolean;
89
+
90
+ // 入力イベント
91
+ onchange?: (value: any) => void;
92
+
93
+ // フォーカスイベント
94
+ onfocus?: Function; // No params for type inference
95
+ onblur?: Function; // No params for type inference
96
+
97
+ // キーボードイベント
98
+ onkeydown?: Function; // No params for type inference
99
+ onkeyup?: Function; // No params for type inference
100
+
101
+ // マウスイベント
102
+ onclick?: Function; // No params for type inference
103
+ onmousedown?: Function; // No params for type inference
104
+ onmouseup?: Function; // No params for type inference
105
+ onmouseenter?: Function; // No params for type inference
106
+ onmouseleave?: Function; // No params for type inference
107
+ onmouseover?: Function; // No params for type inference
108
+ onmouseout?: Function; // No params for type inference
109
+ oncontextmenu?: Function; // No params for type inference
110
+ onauxclick?: Function; // No params for type inference
111
+
112
+ // タッチイベント
113
+ ontouchstart?: Function; // No params for type inference
114
+ ontouchend?: Function; // No params for type inference
115
+ ontouchmove?: Function; // No params for type inference
116
+ ontouchcancel?: Function; // No params for type inference
117
+
118
+ // ポインターイベント
119
+ onpointerdown?: Function; // No params for type inference
120
+ onpointerup?: Function; // No params for type inference
121
+ onpointerenter?: Function; // No params for type inference
122
+ onpointerleave?: Function; // No params for type inference
123
+ onpointermove?: Function; // No params for type inference
124
+ onpointercancel?: Function; // No params for type inference
125
+
126
+ // その他
127
+ [key: string]: any;
128
+ } = $props();
129
+
130
+ // =========================================================================
131
+
132
+ // Methods
133
+ // =========================================================================
134
+ const handleFocus = (event: FocusEvent) => {
135
+ if (disabled) return;
136
+ onfocus(event);
137
+ };
138
+
139
+ const handleBlur = (event: FocusEvent) => {
140
+ if (disabled) return;
141
+ onblur(event);
142
+ };
143
+
144
+ const handleKeydown = (event: KeyboardEvent) => {
145
+ if (disabled) return;
146
+
147
+ if (
148
+ event.key === 'ArrowUp' ||
149
+ event.key === 'ArrowDown' ||
150
+ event.key === 'ArrowLeft' ||
151
+ event.key === 'ArrowRight'
152
+ ) {
153
+ const radioInputs = document.querySelectorAll(`input[type="radio"][name="${name}"]`);
154
+ const currentIndex = Array.from(radioInputs).findIndex((input) => input === event.target);
155
+
156
+ if (currentIndex !== -1) {
157
+ event.preventDefault();
158
+ let nextIndex;
159
+
160
+ if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
161
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : radioInputs.length - 1;
162
+ } else {
163
+ nextIndex = currentIndex < radioInputs.length - 1 ? currentIndex + 1 : 0;
164
+ }
165
+
166
+ const nextInput = radioInputs[nextIndex] as HTMLInputElement;
167
+ if (nextInput && !nextInput.disabled) {
168
+ nextInput.focus();
169
+ nextInput.click();
170
+ }
171
+ }
172
+ }
173
+
174
+ onkeydown(event);
175
+ };
176
+
177
+ const handleKeyup = (event: KeyboardEvent) => {
178
+ onkeyup(event);
179
+ };
180
+
181
+ // マウスイベント
182
+ const handleClick = (event: MouseEvent) => {
183
+ if (disabled) return;
184
+ onclick?.(event);
185
+ };
186
+
187
+ const handleMouseDown = (event: MouseEvent) => {
188
+ if (disabled) return;
189
+ onmousedown?.(event);
190
+ };
191
+
192
+ const handleMouseUp = (event: MouseEvent) => {
193
+ if (disabled) return;
194
+ onmouseup?.(event);
195
+ };
196
+
197
+ const handleMouseEnter = (event: MouseEvent) => {
198
+ if (disabled) return;
199
+ onmouseenter?.(event);
200
+ };
201
+
202
+ const handleMouseLeave = (event: MouseEvent) => {
203
+ if (disabled) return;
204
+ onmouseleave?.(event);
205
+ };
206
+
207
+ const handleMouseOver = (event: MouseEvent) => {
208
+ if (disabled) return;
209
+ onmouseover?.(event);
210
+ };
211
+
212
+ const handleMouseOut = (event: MouseEvent) => {
213
+ if (disabled) return;
214
+ onmouseout?.(event);
215
+ };
216
+
217
+ const handleContextMenu = (event: MouseEvent) => {
218
+ if (disabled) return;
219
+ oncontextmenu?.(event);
220
+ };
221
+
222
+ const handleAuxClick = (event: MouseEvent) => {
223
+ if (disabled) return;
224
+ onauxclick?.(event);
225
+ };
226
+
227
+ // タッチイベント
228
+ const handleTouchStart = (event: TouchEvent) => {
229
+ if (disabled) return;
230
+ ontouchstart?.(event);
231
+ };
232
+
233
+ const handleTouchEnd = (event: TouchEvent) => {
234
+ if (disabled) return;
235
+ ontouchend?.(event);
236
+ };
237
+
238
+ const handleTouchMove = (event: TouchEvent) => {
239
+ if (disabled) return;
240
+ ontouchmove?.(event);
241
+ };
242
+
243
+ const handleTouchCancel = (event: TouchEvent) => {
244
+ if (disabled) return;
245
+ ontouchcancel?.(event);
246
+ };
247
+
248
+ // ポインターイベント
249
+ const handlePointerDown = (event: PointerEvent) => {
250
+ if (disabled) return;
251
+ onpointerdown?.(event);
252
+ };
253
+
254
+ const handlePointerUp = (event: PointerEvent) => {
255
+ if (disabled) return;
256
+ onpointerup?.(event);
257
+ };
258
+
259
+ const handlePointerEnter = (event: PointerEvent) => {
260
+ if (disabled) return;
261
+ onpointerenter?.(event);
262
+ };
263
+
264
+ const handlePointerLeave = (event: PointerEvent) => {
265
+ if (disabled) return;
266
+ onpointerleave?.(event);
267
+ };
268
+
269
+ const handlePointerMove = (event: PointerEvent) => {
270
+ if (disabled) return;
271
+ onpointermove?.(event);
272
+ };
273
+
274
+ const handlePointerCancel = (event: PointerEvent) => {
275
+ if (disabled) return;
276
+ onpointercancel?.(event);
277
+ };
278
+
279
+ // 変更イベント
280
+ const handleChange = (event: Event) => {
281
+ if (disabled) return;
282
+
283
+ const target = event.target as HTMLInputElement;
284
+ if (target.checked) {
285
+ currentValue = value;
286
+ onchange(value);
287
+ }
288
+ };
289
+
290
+ // =========================================================================
291
+ // $derived
292
+ // =========================================================================
293
+ const isChecked: boolean = $derived(currentValue === value);
294
+
295
+ const containerClasses = $derived(
296
+ ['radio', `radio--${size}`, disabled && 'radio--disabled', reducedMotion && 'radio--no-motion']
297
+ .filter(Boolean)
298
+ .join(' ')
299
+ );
300
+ </script>
301
+
302
+ <div class={containerClasses} data-testid="radio">
303
+ <input
304
+ type="radio"
305
+ checked={isChecked}
306
+ {id}
307
+ {name}
308
+ {value}
309
+ {disabled}
310
+ {required}
311
+ aria-describedby={undefined}
312
+ onfocus={handleFocus}
313
+ onblur={handleBlur}
314
+ onkeydown={handleKeydown}
315
+ onkeyup={handleKeyup}
316
+ onclick={handleClick}
317
+ onmousedown={handleMouseDown}
318
+ onmouseup={handleMouseUp}
319
+ onmouseenter={handleMouseEnter}
320
+ onmouseleave={handleMouseLeave}
321
+ onmouseover={handleMouseOver}
322
+ onmouseout={handleMouseOut}
323
+ oncontextmenu={handleContextMenu}
324
+ onauxclick={handleAuxClick}
325
+ ontouchstart={handleTouchStart}
326
+ ontouchend={handleTouchEnd}
327
+ ontouchmove={handleTouchMove}
328
+ ontouchcancel={handleTouchCancel}
329
+ onpointerdown={handlePointerDown}
330
+ onpointerup={handlePointerUp}
331
+ onpointerenter={handlePointerEnter}
332
+ onpointerleave={handlePointerLeave}
333
+ onpointermove={handlePointerMove}
334
+ onpointercancel={handlePointerCancel}
335
+ onchange={handleChange}
336
+ {...inputAttributes}
337
+ {...restProps}
338
+ />
339
+ <label for={id} class="radio__icon"></label>
340
+
341
+ {#if children}
342
+ <label for={id} class="radio__label">
343
+ {@render children()}
344
+ </label>
345
+ {/if}
346
+ </div>
347
+
348
+ <style>
349
+ /* =============================================
350
+ * Base Styles
351
+ * ============================================= */
352
+
353
+ .radio {
354
+ display: flex;
355
+ align-items: center;
356
+ width: fit-content;
357
+ }
358
+
359
+ .radio input[type='radio'] {
360
+ position: absolute;
361
+ width: 16px;
362
+ height: 16px;
363
+ margin: 0;
364
+ line-height: 1px;
365
+ opacity: 0;
366
+ cursor: pointer;
367
+ }
368
+
369
+ /* Label */
370
+ .radio__label {
371
+ display: flex;
372
+ align-items: center;
373
+ white-space: nowrap;
374
+ font-size: inherit;
375
+ color: inherit;
376
+ cursor: pointer;
377
+ min-height: var(--svelte-ui-checkbox-min-height);
378
+ }
379
+
380
+ /* Icon */
381
+ .radio__icon {
382
+ position: relative;
383
+ display: flex;
384
+ align-items: center;
385
+ padding: var(--svelte-ui-radio-padding);
386
+ white-space: nowrap;
387
+ font-size: inherit;
388
+ color: inherit;
389
+ cursor: pointer;
390
+ min-height: var(--svelte-ui-radio-min-height);
391
+ }
392
+
393
+ .radio__icon::before,
394
+ .radio__icon::after {
395
+ position: absolute;
396
+ content: '';
397
+ display: block;
398
+ top: 50%;
399
+ transform: translateY(-50%);
400
+ }
401
+
402
+ /* Radio button outer circle */
403
+ .radio__icon::after {
404
+ left: 0;
405
+ width: var(--svelte-ui-radio-size);
406
+ height: var(--svelte-ui-radio-size);
407
+ border: var(--svelte-ui-radio-border-width) solid var(--svelte-ui-radio-border-color);
408
+ border-radius: var(--svelte-ui-radio-border-radius);
409
+ background-color: transparent;
410
+ transition-property: border-color, background-color;
411
+ transition-duration: var(--svelte-ui-transition-duration);
412
+ }
413
+
414
+ /* Radio button inner dot */
415
+ .radio__icon::before {
416
+ left: calc(var(--svelte-ui-radio-size) / 2);
417
+ width: 0;
418
+ height: 0;
419
+ background-color: var(--svelte-ui-radio-bg-checked);
420
+ border-radius: var(--svelte-ui-radio-border-radius);
421
+ transition-property: left, top, width, height;
422
+ transition-duration: var(--svelte-ui-transition-duration);
423
+ }
424
+
425
+ /* Checked state */
426
+ input[type='radio']:checked + .radio__icon::before {
427
+ left: calc((var(--svelte-ui-radio-size) - var(--svelte-ui-radio-dot-size)) / 2);
428
+ width: var(--svelte-ui-radio-dot-size);
429
+ height: var(--svelte-ui-radio-dot-size);
430
+ }
431
+
432
+ /* =============================================
433
+ * Status
434
+ * ============================================= */
435
+ .radio--disabled input[type='radio'] {
436
+ cursor: not-allowed;
437
+ }
438
+
439
+ .radio--disabled .radio__label {
440
+ opacity: var(--svelte-ui-button-disabled-opacity);
441
+ cursor: not-allowed;
442
+ }
443
+
444
+ /* Hover states */
445
+ @media (hover: hover) {
446
+ .radio:not(.radio--disabled):hover .radio__icon::after,
447
+ .radio:not(.radio--disabled):hover .radio__icon::before {
448
+ border-color: var(--svelte-ui-radio-hover-color);
449
+ }
450
+ }
451
+
452
+ /* Checked state */
453
+ input[type='radio']:checked + .radio__icon::after {
454
+ border-color: var(--svelte-ui-radio-hover-color);
455
+ }
456
+
457
+ /* Focus state */
458
+ input[type='radio']:focus-visible + .radio__icon::after {
459
+ outline: var(--svelte-ui-focus-outline-outer);
460
+ outline-offset: var(--svelte-ui-focus-outline-offset-outer);
461
+ }
462
+
463
+ /* =========================================================================
464
+ * Size Variants
465
+ * ========================================================================= */
466
+
467
+ /* Size variants */
468
+ .radio--small {
469
+ font-size: inherit;
470
+ }
471
+
472
+ .radio--small .radio__icon {
473
+ padding: var(--svelte-ui-radio-padding-sm);
474
+ min-height: var(--svelte-ui-radio-min-height-sm);
475
+ }
476
+
477
+ .radio--small .radio__icon::after {
478
+ width: var(--svelte-ui-radio-size-sm);
479
+ height: var(--svelte-ui-radio-size-sm);
480
+ }
481
+
482
+ .radio--small .radio__icon::before {
483
+ left: calc(var(--svelte-ui-radio-size-sm) / 2);
484
+ }
485
+
486
+ .radio--small input[type='radio']:checked + .radio__icon::before {
487
+ left: calc((var(--svelte-ui-radio-size-sm) - var(--svelte-ui-radio-dot-size-sm)) / 2);
488
+ width: var(--svelte-ui-radio-dot-size-sm);
489
+ height: var(--svelte-ui-radio-dot-size-sm);
490
+ }
491
+
492
+ .radio--large {
493
+ font-size: inherit;
494
+ }
495
+
496
+ .radio--large .radio__icon {
497
+ padding: var(--svelte-ui-radio-padding-lg);
498
+ min-height: var(--svelte-ui-radio-min-height-lg);
499
+ }
500
+
501
+ .radio--large .radio__icon::after {
502
+ width: var(--svelte-ui-radio-size-lg);
503
+ height: var(--svelte-ui-radio-size-lg);
504
+ }
505
+
506
+ .radio--large .radio__icon::before {
507
+ left: calc(var(--svelte-ui-radio-size-lg) / 2);
508
+ }
509
+
510
+ .radio--large input[type='radio']:checked + .radio__icon::before {
511
+ left: calc((var(--svelte-ui-radio-size-lg) - var(--svelte-ui-radio-dot-size-lg)) / 2);
512
+ width: var(--svelte-ui-radio-dot-size-lg);
513
+ height: var(--svelte-ui-radio-dot-size-lg);
514
+ }
515
+
516
+ /* =========================================================================
517
+ * Motion & Media Queries
518
+ * ========================================================================= */
519
+
520
+ /* Mobile touch targets */
521
+ @media (hover: none) and (pointer: coarse) {
522
+ .radio__label {
523
+ min-height: var(--svelte-ui-touch-target);
524
+ }
525
+
526
+ .radio--small .radio__label {
527
+ min-height: var(--svelte-ui-touch-target-sm);
528
+ }
529
+
530
+ .radio--large .radio__label {
531
+ min-height: var(--svelte-ui-touch-target-lg);
532
+ }
533
+ }
534
+
535
+ /* Reduced motion */
536
+ .radio--no-motion .radio__icon::before,
537
+ .radio--no-motion .radio__icon::after {
538
+ transition-duration: 0.01s;
539
+ }
540
+
541
+ /* Prefers reduced motion */
542
+ @media (prefers-reduced-motion: reduce) {
543
+ .radio__icon::before,
544
+ .radio__icon::after {
545
+ transition-duration: 0.01s;
546
+ }
547
+ }
548
+ </style>
@@ -0,0 +1,42 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ type $$ComponentProps = {
4
+ children?: Snippet;
5
+ name: string;
6
+ value: string | number | boolean;
7
+ currentValue: string | number | boolean | null;
8
+ id?: string;
9
+ inputAttributes?: HTMLInputAttributes | undefined;
10
+ size?: 'small' | 'medium' | 'large';
11
+ disabled?: boolean;
12
+ required?: boolean;
13
+ reducedMotion?: boolean;
14
+ onchange?: (value: any) => void;
15
+ onfocus?: Function;
16
+ onblur?: Function;
17
+ onkeydown?: Function;
18
+ onkeyup?: Function;
19
+ onclick?: Function;
20
+ onmousedown?: Function;
21
+ onmouseup?: Function;
22
+ onmouseenter?: Function;
23
+ onmouseleave?: Function;
24
+ onmouseover?: Function;
25
+ onmouseout?: Function;
26
+ oncontextmenu?: Function;
27
+ onauxclick?: Function;
28
+ ontouchstart?: Function;
29
+ ontouchend?: Function;
30
+ ontouchmove?: Function;
31
+ ontouchcancel?: Function;
32
+ onpointerdown?: Function;
33
+ onpointerup?: Function;
34
+ onpointerenter?: Function;
35
+ onpointerleave?: Function;
36
+ onpointermove?: Function;
37
+ onpointercancel?: Function;
38
+ [key: string]: any;
39
+ };
40
+ declare const Radio: import("svelte").Component<$$ComponentProps, {}, "currentValue">;
41
+ type Radio = ReturnType<typeof Radio>;
42
+ export default Radio;
@@ -0,0 +1,74 @@
1
+ <script lang="ts">
2
+ import type { Option, OptionValue } from '../types/options';
3
+ import Radio from './Radio.svelte';
4
+ import { getStyleFromNumber } from '../utils/style';
5
+
6
+ let {
7
+ // 基本プロパティ
8
+ name = `radio-${Math.random().toString(36).substring(2, 15)}`,
9
+ options,
10
+ value = $bindable(),
11
+
12
+ // スタイル/レイアウト
13
+ direction = 'vertical',
14
+ gap = '0',
15
+ wrap = false,
16
+ minOptionWidth,
17
+
18
+ // 入力イベント
19
+ onchange = () => {} // No params for type inference
20
+ }: {
21
+ // 基本プロパティ
22
+ name?: string;
23
+ options: Option[];
24
+ value: OptionValue;
25
+
26
+ // スタイル/レイアウト
27
+ direction?: 'vertical' | 'horizontal';
28
+ gap?: string | number;
29
+ wrap?: boolean;
30
+ minOptionWidth?: string | number;
31
+
32
+ // 入力イベント
33
+ onchange?: (value: OptionValue) => void;
34
+ } = $props();
35
+ let localValues: Record<string, boolean> = $state({});
36
+
37
+ const handleChange = () => {
38
+ onchange(value);
39
+ };
40
+
41
+ const gapStyle = getStyleFromNumber(gap);
42
+
43
+ const minOptionWidthStyle = getStyleFromNumber(minOptionWidth);
44
+ </script>
45
+
46
+ <ul
47
+ class="checkbox-group"
48
+ style="--flex-direction: {direction === 'vertical' ? 'column' : 'row'};
49
+ --gap: {gapStyle};
50
+ --wrap: {wrap ? 'wrap' : 'none'};
51
+ --min-option-width: {minOptionWidthStyle}
52
+ "
53
+ >
54
+ {#each options as option (option.value)}
55
+ <li class="checkbox-group__option">
56
+ <Radio {name} bind:currentValue={value} value={option.value} onchange={handleChange}>
57
+ {option.label}
58
+ </Radio>
59
+ </li>
60
+ {/each}
61
+ </ul>
62
+
63
+ <style>
64
+ .checkbox-group {
65
+ display: flex;
66
+ flex-direction: var(--flex-direction);
67
+ gap: var(--gap);
68
+ flex-wrap: var(--wrap);
69
+ }
70
+
71
+ .checkbox-group__option {
72
+ min-width: var(--min-option-width);
73
+ }
74
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Option, OptionValue } from '../types/options';
2
+ type $$ComponentProps = {
3
+ name?: string;
4
+ options: Option[];
5
+ value: OptionValue;
6
+ direction?: 'vertical' | 'horizontal';
7
+ gap?: string | number;
8
+ wrap?: boolean;
9
+ minOptionWidth?: string | number;
10
+ onchange?: (value: OptionValue) => void;
11
+ };
12
+ declare const RadioGroup: import("svelte").Component<$$ComponentProps, {}, "value">;
13
+ type RadioGroup = ReturnType<typeof RadioGroup>;
14
+ export default RadioGroup;