@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.
- package/README.md +359 -0
- package/dist/assets/styles/README.md +144 -0
- package/dist/assets/styles/core.scss +61 -0
- package/dist/assets/styles/import.scss +11 -0
- package/dist/assets/styles/optional/fonts.scss +23 -0
- package/dist/assets/styles/optional/reset.scss +230 -0
- package/dist/assets/styles/variables.scss +805 -0
- package/dist/components/Button.svelte +574 -0
- package/dist/components/Button.svelte.d.ts +56 -0
- package/dist/components/COMPONENT_DESIGN_GUIDELINES.md +127 -0
- package/dist/components/Checkbox.svelte +523 -0
- package/dist/components/Checkbox.svelte.d.ts +42 -0
- package/dist/components/CheckboxGroup.svelte +82 -0
- package/dist/components/CheckboxGroup.svelte.d.ts +13 -0
- package/dist/components/ColorPicker.svelte +496 -0
- package/dist/components/ColorPicker.svelte.d.ts +45 -0
- package/dist/components/Combobox.svelte +576 -0
- package/dist/components/Combobox.svelte.d.ts +52 -0
- package/dist/components/ConfirmDialog.svelte +116 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +20 -0
- package/dist/components/Datepicker.svelte +578 -0
- package/dist/components/Datepicker.svelte.d.ts +72 -0
- package/dist/components/DatepickerCalendar.svelte +925 -0
- package/dist/components/DatepickerCalendar.svelte.d.ts +31 -0
- package/dist/components/Dialog.svelte +245 -0
- package/dist/components/Dialog.svelte.d.ts +38 -0
- package/dist/components/Drawer.svelte +383 -0
- package/dist/components/Drawer.svelte.d.ts +39 -0
- package/dist/components/Fab.svelte +486 -0
- package/dist/components/Fab.svelte.d.ts +51 -0
- package/dist/components/FileUploader.svelte +456 -0
- package/dist/components/FileUploader.svelte.d.ts +36 -0
- package/dist/components/Icon.svelte +167 -0
- package/dist/components/Icon.svelte.d.ts +21 -0
- package/dist/components/IconButton.svelte +557 -0
- package/dist/components/IconButton.svelte.d.ts +60 -0
- package/dist/components/ImageUploader.svelte +516 -0
- package/dist/components/ImageUploader.svelte.d.ts +37 -0
- package/dist/components/ImageUploaderPreview.svelte +157 -0
- package/dist/components/ImageUploaderPreview.svelte.d.ts +13 -0
- package/dist/components/Input.svelte +885 -0
- package/dist/components/Input.svelte.d.ts +75 -0
- package/dist/components/LoadingSpinner.svelte +116 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +10 -0
- package/dist/components/Modal.svelte +313 -0
- package/dist/components/Modal.svelte.d.ts +34 -0
- package/dist/components/Pagination.svelte +276 -0
- package/dist/components/Pagination.svelte.d.ts +14 -0
- package/dist/components/Popup.svelte +676 -0
- package/dist/components/Popup.svelte.d.ts +40 -0
- package/dist/components/PopupMenu.svelte +421 -0
- package/dist/components/PopupMenu.svelte.d.ts +24 -0
- package/dist/components/PopupMenuButton.svelte +365 -0
- package/dist/components/PopupMenuButton.svelte.d.ts +42 -0
- package/dist/components/Radio.svelte +548 -0
- package/dist/components/Radio.svelte.d.ts +42 -0
- package/dist/components/RadioGroup.svelte +74 -0
- package/dist/components/RadioGroup.svelte.d.ts +14 -0
- package/dist/components/Select.svelte +479 -0
- package/dist/components/Select.svelte.d.ts +47 -0
- package/dist/components/Slider.svelte +473 -0
- package/dist/components/Slider.svelte.d.ts +46 -0
- package/dist/components/Snackbar.svelte +124 -0
- package/dist/components/Snackbar.svelte.d.ts +9 -0
- package/dist/components/SnackbarItem.svelte +423 -0
- package/dist/components/SnackbarItem.svelte.d.ts +21 -0
- package/dist/components/Switch.svelte +454 -0
- package/dist/components/Switch.svelte.d.ts +40 -0
- package/dist/components/Tab.svelte +193 -0
- package/dist/components/Tab.svelte.d.ts +14 -0
- package/dist/components/TabItem.svelte +140 -0
- package/dist/components/TabItem.svelte.d.ts +17 -0
- package/dist/components/Textarea.svelte +702 -0
- package/dist/components/Textarea.svelte.d.ts +64 -0
- package/dist/components/skeleton/Skeleton.svelte +235 -0
- package/dist/components/skeleton/Skeleton.svelte.d.ts +13 -0
- package/dist/components/skeleton/SkeletonAvatar.svelte +97 -0
- package/dist/components/skeleton/SkeletonAvatar.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonBox.svelte +105 -0
- package/dist/components/skeleton/SkeletonBox.svelte.d.ts +12 -0
- package/dist/components/skeleton/SkeletonButton.svelte +71 -0
- package/dist/components/skeleton/SkeletonButton.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonHeading.svelte +49 -0
- package/dist/components/skeleton/SkeletonHeading.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonMedia.svelte +115 -0
- package/dist/components/skeleton/SkeletonMedia.svelte.d.ts +9 -0
- package/dist/components/skeleton/SkeletonText.svelte +75 -0
- package/dist/components/skeleton/SkeletonText.svelte.d.ts +8 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +43 -0
- package/dist/types/icon.d.ts +4 -0
- package/dist/types/icon.js +2 -0
- package/dist/types/menuItem.d.ts +8 -0
- package/dist/types/menuItem.js +1 -0
- package/dist/types/options.d.ts +6 -0
- package/dist/types/options.js +4 -0
- package/dist/types/skeleton.d.ts +77 -0
- package/dist/types/skeleton.js +19 -0
- package/dist/utils/accessibility.d.ts +48 -0
- package/dist/utils/accessibility.js +87 -0
- package/dist/utils/formatText.d.ts +4 -0
- package/dist/utils/formatText.js +44 -0
- package/dist/utils/mobile.d.ts +9 -0
- package/dist/utils/mobile.js +47 -0
- package/dist/utils/snackbar.svelte.d.ts +51 -0
- package/dist/utils/snackbar.svelte.js +107 -0
- package/dist/utils/style.d.ts +17 -0
- package/dist/utils/style.js +22 -0
- 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;
|