@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,454 @@
|
|
|
1
|
+
<!-- Switch.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
|
+
value = $bindable(false),
|
|
16
|
+
|
|
17
|
+
// HTML属性系
|
|
18
|
+
id = `switch-${Math.random().toString(36).substring(2, 15)}`,
|
|
19
|
+
inputAttributes,
|
|
20
|
+
|
|
21
|
+
// スタイル/レイアウト
|
|
22
|
+
size = 'medium',
|
|
23
|
+
|
|
24
|
+
// 状態/動作
|
|
25
|
+
disabled = false,
|
|
26
|
+
required = false,
|
|
27
|
+
reducedMotion = false,
|
|
28
|
+
|
|
29
|
+
// フォーカスイベント
|
|
30
|
+
onfocus = () => {}, // No params for type inference
|
|
31
|
+
onblur = () => {}, // No params for type inference
|
|
32
|
+
|
|
33
|
+
// キーボードイベント
|
|
34
|
+
onkeydown = () => {}, // No params for type inference
|
|
35
|
+
onkeyup = () => {}, // No params for type inference
|
|
36
|
+
|
|
37
|
+
// マウスイベント
|
|
38
|
+
onclick = () => {}, // No params for type inference
|
|
39
|
+
onmousedown = () => {}, // No params for type inference
|
|
40
|
+
onmouseup = () => {}, // No params for type inference
|
|
41
|
+
onmouseenter = () => {}, // No params for type inference
|
|
42
|
+
onmouseleave = () => {}, // No params for type inference
|
|
43
|
+
onmouseover = () => {}, // No params for type inference
|
|
44
|
+
onmouseout = () => {}, // No params for type inference
|
|
45
|
+
oncontextmenu = () => {}, // No params for type inference
|
|
46
|
+
onauxclick = () => {}, // No params for type inference
|
|
47
|
+
|
|
48
|
+
// タッチイベント
|
|
49
|
+
ontouchstart = () => {}, // No params for type inference
|
|
50
|
+
ontouchend = () => {}, // No params for type inference
|
|
51
|
+
ontouchmove = () => {}, // No params for type inference
|
|
52
|
+
ontouchcancel = () => {}, // No params for type inference
|
|
53
|
+
|
|
54
|
+
// ポインターイベント
|
|
55
|
+
onpointerdown = () => {}, // No params for type inference
|
|
56
|
+
onpointerup = () => {}, // No params for type inference
|
|
57
|
+
onpointerenter = () => {}, // No params for type inference
|
|
58
|
+
onpointerleave = () => {}, // No params for type inference
|
|
59
|
+
onpointermove = () => {}, // No params for type inference
|
|
60
|
+
onpointercancel = () => {}, // No params for type inference
|
|
61
|
+
|
|
62
|
+
// 変更イベント
|
|
63
|
+
onchange = (value: boolean) => {},
|
|
64
|
+
|
|
65
|
+
// その他
|
|
66
|
+
...restProps
|
|
67
|
+
}: {
|
|
68
|
+
// 基本プロパティ
|
|
69
|
+
value?: boolean;
|
|
70
|
+
|
|
71
|
+
// ラベル
|
|
72
|
+
children?: Snippet;
|
|
73
|
+
|
|
74
|
+
// HTML属性系
|
|
75
|
+
id?: string;
|
|
76
|
+
inputAttributes?: HTMLInputAttributes | undefined;
|
|
77
|
+
|
|
78
|
+
// サイズ
|
|
79
|
+
size?: 'small' | 'medium' | 'large';
|
|
80
|
+
|
|
81
|
+
// 無効状態
|
|
82
|
+
disabled?: boolean;
|
|
83
|
+
|
|
84
|
+
// 必須項目
|
|
85
|
+
required?: boolean;
|
|
86
|
+
|
|
87
|
+
// アニメーション無効化
|
|
88
|
+
reducedMotion?: boolean;
|
|
89
|
+
|
|
90
|
+
// フォーカスイベント
|
|
91
|
+
onfocus?: Function; // No params for type inference
|
|
92
|
+
onblur?: Function; // No params for type inference
|
|
93
|
+
|
|
94
|
+
// キーボードイベント
|
|
95
|
+
onkeydown?: Function; // No params for type inference
|
|
96
|
+
onkeyup?: Function; // No params for type inference
|
|
97
|
+
|
|
98
|
+
// マウスイベント
|
|
99
|
+
onclick?: Function; // No params for type inference
|
|
100
|
+
onmousedown?: Function; // No params for type inference
|
|
101
|
+
onmouseup?: Function; // No params for type inference
|
|
102
|
+
onmouseenter?: Function; // No params for type inference
|
|
103
|
+
onmouseleave?: Function; // No params for type inference
|
|
104
|
+
onmouseover?: Function; // No params for type inference
|
|
105
|
+
onmouseout?: Function; // No params for type inference
|
|
106
|
+
oncontextmenu?: Function; // No params for type inference
|
|
107
|
+
onauxclick?: Function; // No params for type inference
|
|
108
|
+
|
|
109
|
+
// タッチイベント
|
|
110
|
+
ontouchstart?: Function; // No params for type inference
|
|
111
|
+
ontouchend?: Function; // No params for type inference
|
|
112
|
+
ontouchmove?: Function; // No params for type inference
|
|
113
|
+
ontouchcancel?: Function; // No params for type inference
|
|
114
|
+
|
|
115
|
+
// ポインターイベント
|
|
116
|
+
onpointerdown?: Function; // No params for type inference
|
|
117
|
+
onpointerup?: Function; // No params for type inference
|
|
118
|
+
onpointerenter?: Function; // No params for type inference
|
|
119
|
+
onpointerleave?: Function; // No params for type inference
|
|
120
|
+
onpointermove?: Function; // No params for type inference
|
|
121
|
+
onpointercancel?: Function; // No params for type inference
|
|
122
|
+
|
|
123
|
+
// 変更イベント
|
|
124
|
+
onchange?: (value: boolean) => void;
|
|
125
|
+
|
|
126
|
+
// その他
|
|
127
|
+
[key: string]: any;
|
|
128
|
+
} = $props();
|
|
129
|
+
|
|
130
|
+
let inputRef: HTMLInputElement | undefined = $state();
|
|
131
|
+
let errorId = `switch-error-${Math.random().toString(36).substring(2, 15)}`;
|
|
132
|
+
|
|
133
|
+
// =========================================================================
|
|
134
|
+
|
|
135
|
+
// =========================================================================
|
|
136
|
+
// Methods
|
|
137
|
+
// =========================================================================
|
|
138
|
+
const handleFocus = (event: FocusEvent) => {
|
|
139
|
+
if (disabled) return;
|
|
140
|
+
onfocus?.(event);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const handleBlur = (event: FocusEvent) => {
|
|
144
|
+
if (disabled) return;
|
|
145
|
+
onblur?.(event);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
149
|
+
if (disabled) return;
|
|
150
|
+
onkeydown?.(event);
|
|
151
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
value = !value;
|
|
154
|
+
onchange?.(value);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleKeyUp = (event: KeyboardEvent) => {
|
|
159
|
+
if (disabled) return;
|
|
160
|
+
onkeyup?.(event);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleClick = (event: MouseEvent) => {
|
|
164
|
+
if (disabled) return;
|
|
165
|
+
onclick?.(event);
|
|
166
|
+
// スイッチの状態を切り替え
|
|
167
|
+
value = !value;
|
|
168
|
+
onchange?.(value);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleMouseDown = (event: MouseEvent) => {
|
|
172
|
+
if (disabled) return;
|
|
173
|
+
onmousedown?.(event);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const handleMouseUp = (event: MouseEvent) => {
|
|
177
|
+
if (disabled) return;
|
|
178
|
+
onmouseup?.(event);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleMouseEnter = (event: MouseEvent) => {
|
|
182
|
+
if (disabled) return;
|
|
183
|
+
onmouseenter?.(event);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handleMouseLeave = (event: MouseEvent) => {
|
|
187
|
+
if (disabled) return;
|
|
188
|
+
onmouseleave?.(event);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const handleMouseOver = (event: MouseEvent) => {
|
|
192
|
+
if (disabled) return;
|
|
193
|
+
onmouseover?.(event);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const handleMouseOut = (event: MouseEvent) => {
|
|
197
|
+
if (disabled) return;
|
|
198
|
+
onmouseout?.(event);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const handleContextMenu = (event: MouseEvent) => {
|
|
202
|
+
if (disabled) return;
|
|
203
|
+
oncontextmenu?.(event);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const handleAuxClick = (event: MouseEvent) => {
|
|
207
|
+
if (disabled) return;
|
|
208
|
+
onauxclick?.(event);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const handleTouchStart = (event: TouchEvent) => {
|
|
212
|
+
if (disabled) return;
|
|
213
|
+
ontouchstart?.(event);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const handleTouchEnd = (event: TouchEvent) => {
|
|
217
|
+
if (disabled) return;
|
|
218
|
+
ontouchend?.(event);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const handleTouchMove = (event: TouchEvent) => {
|
|
222
|
+
if (disabled) return;
|
|
223
|
+
ontouchmove?.(event);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const handleTouchCancel = (event: TouchEvent) => {
|
|
227
|
+
if (disabled) return;
|
|
228
|
+
ontouchcancel?.(event);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const handlePointerDown = (event: PointerEvent) => {
|
|
232
|
+
if (disabled) return;
|
|
233
|
+
onpointerdown?.(event);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const handlePointerUp = (event: PointerEvent) => {
|
|
237
|
+
if (disabled) return;
|
|
238
|
+
onpointerup?.(event);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const handlePointerEnter = (event: PointerEvent) => {
|
|
242
|
+
if (disabled) return;
|
|
243
|
+
onpointerenter?.(event);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const handlePointerLeave = (event: PointerEvent) => {
|
|
247
|
+
if (disabled) return;
|
|
248
|
+
onpointerleave?.(event);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const handlePointerMove = (event: PointerEvent) => {
|
|
252
|
+
if (disabled) return;
|
|
253
|
+
onpointermove?.(event);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const handlePointerCancel = (event: PointerEvent) => {
|
|
257
|
+
if (disabled) return;
|
|
258
|
+
onpointercancel?.(event);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const handleChange = (event: Event) => {
|
|
262
|
+
if (disabled) return;
|
|
263
|
+
const target = event.target as HTMLInputElement;
|
|
264
|
+
value = target.checked;
|
|
265
|
+
onchange?.(value);
|
|
266
|
+
};
|
|
267
|
+
</script>
|
|
268
|
+
|
|
269
|
+
<div
|
|
270
|
+
class="switch"
|
|
271
|
+
class:switch--small={size === 'small'}
|
|
272
|
+
class:switch--medium={size === 'medium'}
|
|
273
|
+
class:switch--large={size === 'large'}
|
|
274
|
+
class:switch--disabled={disabled}
|
|
275
|
+
class:switch--checked={value}
|
|
276
|
+
class:switch--reduced-motion={reducedMotion}
|
|
277
|
+
data-testid="switch"
|
|
278
|
+
>
|
|
279
|
+
<input
|
|
280
|
+
bind:this={inputRef}
|
|
281
|
+
bind:checked={value}
|
|
282
|
+
type="checkbox"
|
|
283
|
+
class="switch-input"
|
|
284
|
+
{disabled}
|
|
285
|
+
{required}
|
|
286
|
+
{id}
|
|
287
|
+
{...inputAttributes}
|
|
288
|
+
onchange={handleChange}
|
|
289
|
+
onfocus={handleFocus}
|
|
290
|
+
onblur={handleBlur}
|
|
291
|
+
onkeydown={handleKeyDown}
|
|
292
|
+
onkeyup={handleKeyUp}
|
|
293
|
+
onclick={handleClick}
|
|
294
|
+
onmousedown={handleMouseDown}
|
|
295
|
+
onmouseup={handleMouseUp}
|
|
296
|
+
onmouseenter={handleMouseEnter}
|
|
297
|
+
onmouseleave={handleMouseLeave}
|
|
298
|
+
onmouseover={handleMouseOver}
|
|
299
|
+
onmouseout={handleMouseOut}
|
|
300
|
+
oncontextmenu={handleContextMenu}
|
|
301
|
+
onauxclick={handleAuxClick}
|
|
302
|
+
ontouchstart={handleTouchStart}
|
|
303
|
+
ontouchend={handleTouchEnd}
|
|
304
|
+
ontouchmove={handleTouchMove}
|
|
305
|
+
ontouchcancel={handleTouchCancel}
|
|
306
|
+
onpointerdown={handlePointerDown}
|
|
307
|
+
onpointerup={handlePointerUp}
|
|
308
|
+
onpointerenter={handlePointerEnter}
|
|
309
|
+
onpointerleave={handlePointerLeave}
|
|
310
|
+
onpointermove={handlePointerMove}
|
|
311
|
+
onpointercancel={handlePointerCancel}
|
|
312
|
+
{...restProps}
|
|
313
|
+
/>
|
|
314
|
+
|
|
315
|
+
<label for={id} class="switch__track">
|
|
316
|
+
<span class="switch-thumb"></span>
|
|
317
|
+
</label>
|
|
318
|
+
|
|
319
|
+
<label for={id} class="switch__label" class:switch__label--disabled={disabled}>
|
|
320
|
+
{#if children}
|
|
321
|
+
{@render children()}
|
|
322
|
+
{/if}
|
|
323
|
+
</label>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<style>@charset "UTF-8";
|
|
327
|
+
/* =============================================
|
|
328
|
+
* Base Styles
|
|
329
|
+
* ============================================= */
|
|
330
|
+
.switch {
|
|
331
|
+
display: inline-flex;
|
|
332
|
+
align-items: center;
|
|
333
|
+
font-family: var(--svelte-ui-font-family);
|
|
334
|
+
font-size: inherit;
|
|
335
|
+
line-height: var(--svelte-ui-line-height);
|
|
336
|
+
color: var(--svelte-ui-text-color);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.switch-input {
|
|
340
|
+
position: absolute;
|
|
341
|
+
opacity: 0;
|
|
342
|
+
width: 1px;
|
|
343
|
+
height: 1px;
|
|
344
|
+
margin: -1px;
|
|
345
|
+
padding: 0;
|
|
346
|
+
overflow: hidden;
|
|
347
|
+
clip: rect(0, 0, 0, 0);
|
|
348
|
+
white-space: nowrap;
|
|
349
|
+
border: 0;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.switch__label {
|
|
353
|
+
display: inline-flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
padding-left: var(--svelte-ui-switch-gap);
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
user-select: none;
|
|
358
|
+
}
|
|
359
|
+
.switch__label--disabled {
|
|
360
|
+
cursor: not-allowed;
|
|
361
|
+
opacity: 0.5;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.switch__track {
|
|
365
|
+
position: relative;
|
|
366
|
+
width: var(--switch-width);
|
|
367
|
+
height: var(--switch-height);
|
|
368
|
+
background-color: var(--svelte-ui-switch-inactive-color);
|
|
369
|
+
border-radius: var(--switch-border-radius);
|
|
370
|
+
transition: background-color var(--svelte-ui-transition-duration) ease, filter var(--svelte-ui-transition-duration) ease;
|
|
371
|
+
flex-shrink: 0;
|
|
372
|
+
cursor: pointer;
|
|
373
|
+
}
|
|
374
|
+
.switch--checked .switch__track {
|
|
375
|
+
background-color: var(--switch-active-color, var(--svelte-ui-switch-active-color));
|
|
376
|
+
}
|
|
377
|
+
.switch--disabled .switch__track {
|
|
378
|
+
opacity: var(--svelte-ui-switch-disabled-opacity);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.switch-thumb {
|
|
382
|
+
position: absolute;
|
|
383
|
+
top: var(--switch-thumb-margin);
|
|
384
|
+
left: var(--switch-thumb-margin);
|
|
385
|
+
width: var(--switch-thumb-size);
|
|
386
|
+
height: var(--switch-thumb-size);
|
|
387
|
+
background-color: var(--svelte-ui-switch-thumb-color);
|
|
388
|
+
border-radius: var(--switch-thumb-border-radius);
|
|
389
|
+
transition: transform var(--svelte-ui-transition-duration) ease;
|
|
390
|
+
}
|
|
391
|
+
.switch--checked .switch-thumb {
|
|
392
|
+
transform: translateX(calc(var(--switch-width) - var(--switch-thumb-size) - var(--switch-thumb-margin) * 2));
|
|
393
|
+
}
|
|
394
|
+
.switch--disabled .switch-thumb {
|
|
395
|
+
opacity: var(--svelte-ui-switch-disabled-opacity);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* =========================================================================
|
|
399
|
+
* Size Variants
|
|
400
|
+
* ========================================================================= */
|
|
401
|
+
.switch--small {
|
|
402
|
+
--switch-width: var(--svelte-ui-switch-width-sm);
|
|
403
|
+
--switch-height: var(--svelte-ui-switch-height-sm);
|
|
404
|
+
--switch-thumb-size: var(--svelte-ui-switch-thumb-size-sm);
|
|
405
|
+
--switch-thumb-margin: var(--svelte-ui-switch-thumb-margin);
|
|
406
|
+
--switch-border-radius: var(--svelte-ui-switch-border-radius);
|
|
407
|
+
--switch-thumb-border-radius: var(--svelte-ui-switch-thumb-border-radius);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.switch--medium {
|
|
411
|
+
--switch-width: var(--svelte-ui-switch-width);
|
|
412
|
+
--switch-height: var(--svelte-ui-switch-height);
|
|
413
|
+
--switch-thumb-size: var(--svelte-ui-switch-thumb-size);
|
|
414
|
+
--switch-thumb-margin: var(--svelte-ui-switch-thumb-margin);
|
|
415
|
+
--switch-border-radius: var(--svelte-ui-switch-border-radius);
|
|
416
|
+
--switch-thumb-border-radius: var(--svelte-ui-switch-thumb-border-radius);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.switch--large {
|
|
420
|
+
--switch-width: var(--svelte-ui-switch-width-lg);
|
|
421
|
+
--switch-height: var(--svelte-ui-switch-height-lg);
|
|
422
|
+
--switch-thumb-size: var(--svelte-ui-switch-thumb-size-lg);
|
|
423
|
+
--switch-thumb-margin: var(--svelte-ui-switch-thumb-margin);
|
|
424
|
+
--switch-border-radius: var(--svelte-ui-switch-border-radius);
|
|
425
|
+
--switch-thumb-border-radius: var(--svelte-ui-switch-thumb-border-radius);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* =============================================
|
|
429
|
+
* フォーカス状態
|
|
430
|
+
* ============================================= */
|
|
431
|
+
.switch-input:focus-visible + .switch__track {
|
|
432
|
+
outline: 2px solid var(--svelte-ui-focus-color);
|
|
433
|
+
outline-offset: 2px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/* =========================================================================
|
|
437
|
+
* Motion & Media Queries
|
|
438
|
+
* ========================================================================= */
|
|
439
|
+
/* Mobile touch targets */
|
|
440
|
+
@media (hover: none) and (pointer: coarse) {
|
|
441
|
+
.switch__label {
|
|
442
|
+
min-height: var(--svelte-ui-touch-target);
|
|
443
|
+
}
|
|
444
|
+
.switch--small .switch__label {
|
|
445
|
+
min-height: var(--svelte-ui-touch-target-sm);
|
|
446
|
+
}
|
|
447
|
+
.switch--large .switch__label {
|
|
448
|
+
min-height: var(--svelte-ui-touch-target-lg);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
.switch--reduced-motion * {
|
|
452
|
+
transition: none !important;
|
|
453
|
+
animation: none !important;
|
|
454
|
+
}</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
value?: boolean;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
id?: string;
|
|
7
|
+
inputAttributes?: HTMLInputAttributes | undefined;
|
|
8
|
+
size?: 'small' | 'medium' | 'large';
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
reducedMotion?: boolean;
|
|
12
|
+
onfocus?: Function;
|
|
13
|
+
onblur?: Function;
|
|
14
|
+
onkeydown?: Function;
|
|
15
|
+
onkeyup?: Function;
|
|
16
|
+
onclick?: Function;
|
|
17
|
+
onmousedown?: Function;
|
|
18
|
+
onmouseup?: Function;
|
|
19
|
+
onmouseenter?: Function;
|
|
20
|
+
onmouseleave?: Function;
|
|
21
|
+
onmouseover?: Function;
|
|
22
|
+
onmouseout?: Function;
|
|
23
|
+
oncontextmenu?: Function;
|
|
24
|
+
onauxclick?: Function;
|
|
25
|
+
ontouchstart?: Function;
|
|
26
|
+
ontouchend?: Function;
|
|
27
|
+
ontouchmove?: Function;
|
|
28
|
+
ontouchcancel?: Function;
|
|
29
|
+
onpointerdown?: Function;
|
|
30
|
+
onpointerup?: Function;
|
|
31
|
+
onpointerenter?: Function;
|
|
32
|
+
onpointerleave?: Function;
|
|
33
|
+
onpointermove?: Function;
|
|
34
|
+
onpointercancel?: Function;
|
|
35
|
+
onchange?: (value: boolean) => void;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
};
|
|
38
|
+
declare const Switch: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
39
|
+
type Switch = ReturnType<typeof Switch>;
|
|
40
|
+
export default Switch;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
<!-- Tab.svelte -->
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import TabItem from './TabItem.svelte';
|
|
5
|
+
import type { MenuItem } from '../types/menuItem';
|
|
6
|
+
|
|
7
|
+
// =========================================================================
|
|
8
|
+
// Props, States & Constants
|
|
9
|
+
// =========================================================================
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
// 基本プロパティ
|
|
13
|
+
tabItems = [],
|
|
14
|
+
pathPrefix = '',
|
|
15
|
+
customPathMatcher,
|
|
16
|
+
|
|
17
|
+
// スタイル/レイアウト
|
|
18
|
+
textColor = 'var(--svelte-ui-text-subtle-color)',
|
|
19
|
+
selectedTextColor = 'var(--svelte-ui-primary-color)',
|
|
20
|
+
selectedBarColor = 'var(--svelte-ui-primary-color)',
|
|
21
|
+
|
|
22
|
+
// ARIA/アクセシビリティ
|
|
23
|
+
ariaLabel = 'Tabs',
|
|
24
|
+
ariaLabelledby
|
|
25
|
+
}: {
|
|
26
|
+
// 基本プロパティ
|
|
27
|
+
tabItems: MenuItem[];
|
|
28
|
+
pathPrefix?: string;
|
|
29
|
+
customPathMatcher?: (currentPath: string, itemHref: string, item: MenuItem) => boolean;
|
|
30
|
+
|
|
31
|
+
// スタイル/レイアウト
|
|
32
|
+
textColor?: string;
|
|
33
|
+
selectedTextColor?: string;
|
|
34
|
+
selectedBarColor?: string;
|
|
35
|
+
|
|
36
|
+
// ARIA/アクセシビリティ
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
ariaLabelledby?: string;
|
|
39
|
+
} = $props();
|
|
40
|
+
|
|
41
|
+
// =========================================================================
|
|
42
|
+
// Methods
|
|
43
|
+
// =========================================================================
|
|
44
|
+
|
|
45
|
+
// ブラウザ標準APIを使用した現在パス取得
|
|
46
|
+
const getCurrentPath = () => {
|
|
47
|
+
if (typeof window !== 'undefined') {
|
|
48
|
+
return window.location.pathname;
|
|
49
|
+
}
|
|
50
|
+
return '';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// パスの正規化処理
|
|
54
|
+
const normalizePath = (path: string): string => {
|
|
55
|
+
if (!pathPrefix) return path;
|
|
56
|
+
|
|
57
|
+
// pathPrefixが設定されている場合、それを除去
|
|
58
|
+
if (path.startsWith(pathPrefix)) {
|
|
59
|
+
const normalized = path.substring(pathPrefix.length);
|
|
60
|
+
return normalized.startsWith('/') ? normalized : '/' + normalized;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return path;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// パスマッチング関数
|
|
67
|
+
const matchPath = (currentPath: string, itemHref: string, item: MenuItem): boolean => {
|
|
68
|
+
if (customPathMatcher) {
|
|
69
|
+
return customPathMatcher(currentPath, itemHref, item);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const normalizedCurrentPath = normalizePath(currentPath);
|
|
73
|
+
|
|
74
|
+
// matchingPathのチェック
|
|
75
|
+
if (item.matchingPath?.some((href) => normalizedCurrentPath.startsWith(href))) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// strictMatchの場合
|
|
80
|
+
if (item.strictMatch) {
|
|
81
|
+
return normalizedCurrentPath === itemHref;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ルートパス (/) の特別な処理
|
|
85
|
+
if (itemHref === '/') {
|
|
86
|
+
return normalizedCurrentPath === '/';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// その他のパス
|
|
90
|
+
return normalizedCurrentPath !== '' && normalizedCurrentPath.startsWith(itemHref);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// シンプルなキーボードナビゲーション
|
|
94
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
95
|
+
if (tabItems.length === 0) return;
|
|
96
|
+
|
|
97
|
+
const tabList = event.currentTarget as HTMLElement;
|
|
98
|
+
const tabs = Array.from(tabList.querySelectorAll('a[role="tab"]')) as HTMLElement[];
|
|
99
|
+
const currentTab = event.target as HTMLElement;
|
|
100
|
+
const currentIndex = tabs.indexOf(currentTab);
|
|
101
|
+
|
|
102
|
+
if (currentIndex === -1) return;
|
|
103
|
+
|
|
104
|
+
let nextIndex = currentIndex;
|
|
105
|
+
|
|
106
|
+
switch (event.key) {
|
|
107
|
+
case 'ArrowLeft':
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
|
|
110
|
+
break;
|
|
111
|
+
case 'ArrowRight':
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
|
|
114
|
+
break;
|
|
115
|
+
case 'Home':
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
nextIndex = 0;
|
|
118
|
+
break;
|
|
119
|
+
case 'End':
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
nextIndex = tabs.length - 1;
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
tabs[nextIndex]?.focus();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// =========================================================================
|
|
131
|
+
// $derived
|
|
132
|
+
// =========================================================================
|
|
133
|
+
|
|
134
|
+
// 現在のパスを取得
|
|
135
|
+
const currentPath = $derived(getCurrentPath());
|
|
136
|
+
|
|
137
|
+
// アクティブなタブのインデックスを現在のパスに基づいて計算
|
|
138
|
+
const selectedTabIndex = $derived.by(() => {
|
|
139
|
+
for (let i = 0; i < tabItems.length; i++) {
|
|
140
|
+
const item = tabItems[i];
|
|
141
|
+
if (!item.href) continue;
|
|
142
|
+
|
|
143
|
+
if (matchPath(currentPath, item.href, item)) {
|
|
144
|
+
return i;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return -1;
|
|
148
|
+
});
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<div
|
|
152
|
+
class="tab"
|
|
153
|
+
role="tablist"
|
|
154
|
+
aria-label={ariaLabelledby ? undefined : ariaLabel}
|
|
155
|
+
aria-labelledby={ariaLabelledby}
|
|
156
|
+
tabindex="-1"
|
|
157
|
+
onkeydown={handleKeyDown}
|
|
158
|
+
data-testid="tab"
|
|
159
|
+
>
|
|
160
|
+
{#each tabItems as tabItem, index}
|
|
161
|
+
<div class="tab__item">
|
|
162
|
+
<TabItem
|
|
163
|
+
{tabItem}
|
|
164
|
+
isSelected={index === selectedTabIndex}
|
|
165
|
+
{textColor}
|
|
166
|
+
{selectedTextColor}
|
|
167
|
+
{selectedBarColor}
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
{/each}
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<style>.tab {
|
|
174
|
+
display: flex;
|
|
175
|
+
justify-content: start;
|
|
176
|
+
width: 100%;
|
|
177
|
+
height: 100%;
|
|
178
|
+
overflow-x: auto;
|
|
179
|
+
overflow-y: visible;
|
|
180
|
+
-ms-overflow-style: none;
|
|
181
|
+
overscroll-behavior: contain;
|
|
182
|
+
box-sizing: border-box;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.tab::-webkit-scrollbar {
|
|
186
|
+
display: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.tab__item {
|
|
190
|
+
display: block;
|
|
191
|
+
height: 100%;
|
|
192
|
+
flex-shrink: 0;
|
|
193
|
+
}</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MenuItem } from '../types/menuItem';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
tabItems: MenuItem[];
|
|
4
|
+
pathPrefix?: string;
|
|
5
|
+
customPathMatcher?: (currentPath: string, itemHref: string, item: MenuItem) => boolean;
|
|
6
|
+
textColor?: string;
|
|
7
|
+
selectedTextColor?: string;
|
|
8
|
+
selectedBarColor?: string;
|
|
9
|
+
ariaLabel?: string;
|
|
10
|
+
ariaLabelledby?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const Tab: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type Tab = ReturnType<typeof Tab>;
|
|
14
|
+
export default Tab;
|