@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,127 @@
|
|
|
1
|
+
# コンポーネント設計ガイドライン
|
|
2
|
+
|
|
3
|
+
## ID命名ルール
|
|
4
|
+
|
|
5
|
+
### コンポーネント内包時のID受け渡しルール
|
|
6
|
+
|
|
7
|
+
#### 基本ルール
|
|
8
|
+
|
|
9
|
+
- **親コンポーネント**: `id`プロパティを受け取る
|
|
10
|
+
- **子コンポーネント**: 受け取った`id`にsuffixを付けて自身のIDを生成
|
|
11
|
+
- **内部要素**: 受け取った`id`にsuffixを付けてIDを生成
|
|
12
|
+
|
|
13
|
+
#### 命名パターン
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// 親 → 子コンポーネント
|
|
17
|
+
<ChildComponent id={id ? `${id}-child-suffix` : undefined} />
|
|
18
|
+
|
|
19
|
+
// 内部要素
|
|
20
|
+
<div id={id ? `${id}-element-suffix` : undefined}>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
#### 具体例
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// ConfirmDialog → Dialog → Modal
|
|
27
|
+
<Dialog id={id ? `${id}-dialog` : undefined} />
|
|
28
|
+
<Modal id={id ? `${id}-modal` : undefined} />
|
|
29
|
+
|
|
30
|
+
// 内部要素
|
|
31
|
+
<div id={id ? `${id}-dialog-title` : undefined}>
|
|
32
|
+
<div id={id ? `${id}-modal-description` : undefined}>
|
|
33
|
+
|
|
34
|
+
// 他の例
|
|
35
|
+
<Input id={id ? `${id}-input` : undefined} />
|
|
36
|
+
<Popup id={id ? `${id}-popup` : undefined} />
|
|
37
|
+
<DatepickerCalendar id={id ? `${id}-calendar` : undefined} />
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### 判断基準
|
|
41
|
+
|
|
42
|
+
- **✅ IDを渡すべき**: 1対1の親子関係、アクセシビリティ上重要な要素、テストで個別特定が必要
|
|
43
|
+
- **❌ IDを渡さない**: 同じコンポーネントが複数存在、内部実装の詳細、動的生成要素
|
|
44
|
+
|
|
45
|
+
### 理由
|
|
46
|
+
|
|
47
|
+
- **階層構造の明確化**: `${id}-suffix`パターンにより、コンポーネントの階層関係が分かりやすい
|
|
48
|
+
- **グローバル一意性**: 親のIDをベースにすることで、DOM全体で一意なIDを保証
|
|
49
|
+
- **アクセシビリティ**: ARIA属性との連携で、スクリーンリーダーなどの支援技術に対応
|
|
50
|
+
- **テストの安定性**: 一意なIDにより、テストでの要素特定が確実
|
|
51
|
+
|
|
52
|
+
## コンポーネント設計原則
|
|
53
|
+
|
|
54
|
+
### プロパティ設計
|
|
55
|
+
|
|
56
|
+
- **基本プロパティ**: 必須の機能に関わるプロパティ
|
|
57
|
+
- **HTML属性**: 標準的なHTML属性(id, class, style等)
|
|
58
|
+
- **スタイル/レイアウト**: 見た目やレイアウトに関わるプロパティ
|
|
59
|
+
- **状態/動作**: コンポーネントの動作状態に関わるプロパティ
|
|
60
|
+
- **ARIA/アクセシビリティ**: アクセシビリティに関わるプロパティ
|
|
61
|
+
- **イベントハンドラー**: イベント処理に関わるプロパティ
|
|
62
|
+
|
|
63
|
+
### イベントハンドラー設計
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Svelte 5の推奨パターン
|
|
67
|
+
onclick = () => {}, // パラメータなしで型推論を可能にする
|
|
68
|
+
onchange = () => {}, // パラメータなしで型推論を可能にする
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### データテストID
|
|
72
|
+
|
|
73
|
+
- 最上位のDOM要素に`data-testid`を設定
|
|
74
|
+
- テストでの要素特定を容易にする
|
|
75
|
+
- コンポーネント名をベースに命名(例: `data-testid="dialog"`)
|
|
76
|
+
|
|
77
|
+
## 実装例
|
|
78
|
+
|
|
79
|
+
### 基本的なコンポーネント構造
|
|
80
|
+
|
|
81
|
+
```svelte
|
|
82
|
+
<script lang="ts">
|
|
83
|
+
// Props, States & Constants
|
|
84
|
+
let {
|
|
85
|
+
// 基本プロパティ
|
|
86
|
+
title = 'Default Title',
|
|
87
|
+
|
|
88
|
+
// HTML属性
|
|
89
|
+
id,
|
|
90
|
+
|
|
91
|
+
// スタイル/レイアウト
|
|
92
|
+
variant = 'default',
|
|
93
|
+
|
|
94
|
+
// 状態/動作
|
|
95
|
+
isOpen = $bindable(false),
|
|
96
|
+
|
|
97
|
+
// イベントハンドラー
|
|
98
|
+
onclick = () => {}
|
|
99
|
+
}: {
|
|
100
|
+
// 型定義
|
|
101
|
+
title?: string;
|
|
102
|
+
id?: string;
|
|
103
|
+
variant?: 'default' | 'primary';
|
|
104
|
+
isOpen?: boolean;
|
|
105
|
+
onclick?: () => void;
|
|
106
|
+
} = $props();
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<div class="component" {id} data-testid="component">
|
|
110
|
+
<!-- コンテンツ -->
|
|
111
|
+
</div>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 内包コンポーネントの例
|
|
115
|
+
|
|
116
|
+
```svelte
|
|
117
|
+
<script lang="ts">
|
|
118
|
+
let { id }: { id?: string } = $props();
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<div class="parent" {id} data-testid="parent">
|
|
122
|
+
<ChildComponent id={id ? `${id}-child` : undefined} />
|
|
123
|
+
<div id={id ? `${id}-element` : undefined}>
|
|
124
|
+
<!-- 内部要素 -->
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
```
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
<!-- Checkbox.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 = $bindable(false),
|
|
17
|
+
indeterminate = $bindable(false),
|
|
18
|
+
|
|
19
|
+
// HTML属性系
|
|
20
|
+
id = `checkbox-${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: boolean;
|
|
76
|
+
indeterminate?: boolean;
|
|
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: boolean) => 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
|
+
// $derived
|
|
132
|
+
// =========================================================================
|
|
133
|
+
const containerClasses = $derived(
|
|
134
|
+
[
|
|
135
|
+
'checkbox',
|
|
136
|
+
`checkbox--${size}`,
|
|
137
|
+
disabled && 'checkbox--disabled',
|
|
138
|
+
reducedMotion && 'checkbox--no-motion'
|
|
139
|
+
]
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
.join(' ')
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// =========================================================================
|
|
145
|
+
|
|
146
|
+
// Methods
|
|
147
|
+
// =========================================================================
|
|
148
|
+
const handleFocus = (event: FocusEvent) => {
|
|
149
|
+
if (disabled) return;
|
|
150
|
+
onfocus(event);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const handleBlur = (event: FocusEvent) => {
|
|
154
|
+
if (disabled) return;
|
|
155
|
+
onblur(event);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleKeydown = (event: KeyboardEvent) => {
|
|
159
|
+
if (disabled) return;
|
|
160
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
161
|
+
event.preventDefault();
|
|
162
|
+
value = !value;
|
|
163
|
+
onchange(value);
|
|
164
|
+
}
|
|
165
|
+
onkeydown(event);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const handleKeyup = (event: KeyboardEvent) => {
|
|
169
|
+
onkeyup(event);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// マウスイベント
|
|
173
|
+
const handleClick = (event: MouseEvent) => {
|
|
174
|
+
if (disabled) return;
|
|
175
|
+
onclick?.(event);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const handleMouseDown = (event: MouseEvent) => {
|
|
179
|
+
if (disabled) return;
|
|
180
|
+
onmousedown?.(event);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const handleMouseUp = (event: MouseEvent) => {
|
|
184
|
+
if (disabled) return;
|
|
185
|
+
onmouseup?.(event);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const handleMouseEnter = (event: MouseEvent) => {
|
|
189
|
+
if (disabled) return;
|
|
190
|
+
onmouseenter?.(event);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const handleMouseLeave = (event: MouseEvent) => {
|
|
194
|
+
if (disabled) return;
|
|
195
|
+
onmouseleave?.(event);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const handleMouseOver = (event: MouseEvent) => {
|
|
199
|
+
if (disabled) return;
|
|
200
|
+
onmouseover?.(event);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const handleMouseOut = (event: MouseEvent) => {
|
|
204
|
+
if (disabled) return;
|
|
205
|
+
onmouseout?.(event);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const handleContextMenu = (event: MouseEvent) => {
|
|
209
|
+
if (disabled) return;
|
|
210
|
+
oncontextmenu?.(event);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const handleAuxClick = (event: MouseEvent) => {
|
|
214
|
+
if (disabled) return;
|
|
215
|
+
onauxclick?.(event);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// タッチイベント
|
|
219
|
+
const handleTouchStart = (event: TouchEvent) => {
|
|
220
|
+
if (disabled) return;
|
|
221
|
+
ontouchstart?.(event);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const handleTouchEnd = (event: TouchEvent) => {
|
|
225
|
+
if (disabled) return;
|
|
226
|
+
ontouchend?.(event);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const handleTouchMove = (event: TouchEvent) => {
|
|
230
|
+
if (disabled) return;
|
|
231
|
+
ontouchmove?.(event);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const handleTouchCancel = (event: TouchEvent) => {
|
|
235
|
+
if (disabled) return;
|
|
236
|
+
ontouchcancel?.(event);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// ポインターイベント
|
|
240
|
+
const handlePointerDown = (event: PointerEvent) => {
|
|
241
|
+
if (disabled) return;
|
|
242
|
+
onpointerdown?.(event);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const handlePointerUp = (event: PointerEvent) => {
|
|
246
|
+
if (disabled) return;
|
|
247
|
+
onpointerup?.(event);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const handlePointerEnter = (event: PointerEvent) => {
|
|
251
|
+
if (disabled) return;
|
|
252
|
+
onpointerenter?.(event);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const handlePointerLeave = (event: PointerEvent) => {
|
|
256
|
+
if (disabled) return;
|
|
257
|
+
onpointerleave?.(event);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const handlePointerMove = (event: PointerEvent) => {
|
|
261
|
+
if (disabled) return;
|
|
262
|
+
onpointermove?.(event);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const handlePointerCancel = (event: PointerEvent) => {
|
|
266
|
+
if (disabled) return;
|
|
267
|
+
onpointercancel?.(event);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// 変更イベント
|
|
271
|
+
const handleChange = (event: Event) => {
|
|
272
|
+
onchange(value);
|
|
273
|
+
};
|
|
274
|
+
</script>
|
|
275
|
+
|
|
276
|
+
<div class={containerClasses} data-testid="checkbox">
|
|
277
|
+
<input
|
|
278
|
+
type="checkbox"
|
|
279
|
+
bind:checked={value}
|
|
280
|
+
bind:indeterminate
|
|
281
|
+
{id}
|
|
282
|
+
{name}
|
|
283
|
+
{disabled}
|
|
284
|
+
{required}
|
|
285
|
+
aria-invalid={false}
|
|
286
|
+
aria-required={required ? 'true' : 'false'}
|
|
287
|
+
aria-describedby={undefined}
|
|
288
|
+
onfocus={handleFocus}
|
|
289
|
+
onblur={handleBlur}
|
|
290
|
+
onkeydown={handleKeydown}
|
|
291
|
+
onkeyup={handleKeyup}
|
|
292
|
+
onclick={handleClick}
|
|
293
|
+
onmousedown={handleMouseDown}
|
|
294
|
+
onmouseup={handleMouseUp}
|
|
295
|
+
onmouseenter={handleMouseEnter}
|
|
296
|
+
onmouseleave={handleMouseLeave}
|
|
297
|
+
onmouseover={handleMouseOver}
|
|
298
|
+
onmouseout={handleMouseOut}
|
|
299
|
+
oncontextmenu={handleContextMenu}
|
|
300
|
+
onauxclick={handleAuxClick}
|
|
301
|
+
ontouchstart={handleTouchStart}
|
|
302
|
+
ontouchend={handleTouchEnd}
|
|
303
|
+
ontouchmove={handleTouchMove}
|
|
304
|
+
ontouchcancel={handleTouchCancel}
|
|
305
|
+
onpointerdown={handlePointerDown}
|
|
306
|
+
onpointerup={handlePointerUp}
|
|
307
|
+
onpointerenter={handlePointerEnter}
|
|
308
|
+
onpointerleave={handlePointerLeave}
|
|
309
|
+
onpointermove={handlePointerMove}
|
|
310
|
+
onpointercancel={handlePointerCancel}
|
|
311
|
+
onchange={handleChange}
|
|
312
|
+
{...inputAttributes}
|
|
313
|
+
{...restProps}
|
|
314
|
+
/>
|
|
315
|
+
<label for={id} class="checkbox__icon"></label>
|
|
316
|
+
|
|
317
|
+
{#if children}
|
|
318
|
+
<label for={id} class="checkbox__label">
|
|
319
|
+
{@render children()}
|
|
320
|
+
</label>
|
|
321
|
+
{/if}
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
<style>
|
|
325
|
+
/* =========================================================================
|
|
326
|
+
* Base Styles
|
|
327
|
+
* ========================================================================= */
|
|
328
|
+
|
|
329
|
+
.checkbox {
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
width: fit-content;
|
|
333
|
+
contain: layout;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.checkbox input[type='checkbox'] {
|
|
337
|
+
position: absolute;
|
|
338
|
+
width: 16px;
|
|
339
|
+
height: 16px;
|
|
340
|
+
margin: 0;
|
|
341
|
+
opacity: 0;
|
|
342
|
+
cursor: pointer;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* Label */
|
|
346
|
+
.checkbox__label {
|
|
347
|
+
display: flex;
|
|
348
|
+
align-items: center;
|
|
349
|
+
min-height: var(--svelte-ui-checkbox-min-height);
|
|
350
|
+
padding-left: var(--svelte-ui-checkbox-gap);
|
|
351
|
+
white-space: nowrap;
|
|
352
|
+
font-size: inherit;
|
|
353
|
+
color: inherit;
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Checkbox box */
|
|
358
|
+
.checkbox__icon {
|
|
359
|
+
position: relative;
|
|
360
|
+
width: var(--svelte-ui-checkbox-size);
|
|
361
|
+
height: var(--svelte-ui-checkbox-size);
|
|
362
|
+
border: var(--svelte-ui-checkbox-border-width) solid var(--svelte-ui-checkbox-border-color);
|
|
363
|
+
border-radius: var(--svelte-ui-checkbox-border-radius);
|
|
364
|
+
background-color: transparent;
|
|
365
|
+
transition-property: background-color, border-color, opacity;
|
|
366
|
+
transition-duration: var(--svelte-ui-transition-duration);
|
|
367
|
+
flex-shrink: 0;
|
|
368
|
+
cursor: pointer;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* Check mark */
|
|
372
|
+
.checkbox__icon::after {
|
|
373
|
+
content: 'check';
|
|
374
|
+
position: absolute;
|
|
375
|
+
top: 50%;
|
|
376
|
+
left: 50%;
|
|
377
|
+
transform: translate(-50%, -50%);
|
|
378
|
+
width: fit-content;
|
|
379
|
+
clip-path: inset(0 100% 0 0);
|
|
380
|
+
font-size: var(--svelte-ui-checkbox-icon-size);
|
|
381
|
+
font-family: 'Material Symbols Outlined';
|
|
382
|
+
font-weight: bold;
|
|
383
|
+
font-style: normal;
|
|
384
|
+
color: var(--svelte-ui-checkbox-icon-color);
|
|
385
|
+
text-align: center;
|
|
386
|
+
line-height: 1;
|
|
387
|
+
letter-spacing: normal;
|
|
388
|
+
text-transform: none;
|
|
389
|
+
display: inline-block;
|
|
390
|
+
white-space: nowrap;
|
|
391
|
+
word-wrap: normal;
|
|
392
|
+
direction: ltr;
|
|
393
|
+
font-feature-settings: 'liga';
|
|
394
|
+
-webkit-font-smoothing: antialiased;
|
|
395
|
+
opacity: 0;
|
|
396
|
+
transition-property: clip-path, opacity;
|
|
397
|
+
transition-delay: 0s, 0s;
|
|
398
|
+
transition-duration: var(--svelte-ui-transition-duration), var(--svelte-ui-transition-duration);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* =========================================================================
|
|
402
|
+
State Modifiers
|
|
403
|
+
========================================================================= */
|
|
404
|
+
|
|
405
|
+
/* Disabled state */
|
|
406
|
+
.checkbox--disabled input[type='checkbox'] {
|
|
407
|
+
cursor: not-allowed;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.checkbox--disabled .checkbox__label {
|
|
411
|
+
opacity: var(--svelte-ui-button-disabled-opacity);
|
|
412
|
+
cursor: not-allowed;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* Checked state */
|
|
416
|
+
input[type='checkbox']:checked + .checkbox__icon::after {
|
|
417
|
+
width: var(--svelte-ui-checkbox-icon-width);
|
|
418
|
+
clip-path: inset(0 0 0 0);
|
|
419
|
+
opacity: 1;
|
|
420
|
+
transition-delay:
|
|
421
|
+
var(--svelte-ui-transition-duration-fast), var(--svelte-ui-transition-duration-fast);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* Indeterminate state */
|
|
425
|
+
input[type='checkbox']:indeterminate + .checkbox__icon::after {
|
|
426
|
+
content: 'remove';
|
|
427
|
+
width: var(--svelte-ui-checkbox-icon-width);
|
|
428
|
+
opacity: 1;
|
|
429
|
+
transition-delay:
|
|
430
|
+
var(--svelte-ui-transition-duration-fast), var(--svelte-ui-transition-duration-fast);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* Hover states */
|
|
434
|
+
@media (hover: hover) {
|
|
435
|
+
.checkbox:not(.checkbox--disabled):hover .checkbox__icon {
|
|
436
|
+
border-color: var(--svelte-ui-checkbox-hover-color);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Focus states */
|
|
441
|
+
input[type='checkbox']:focus-visible + .checkbox__icon {
|
|
442
|
+
outline: var(--svelte-ui-focus-outline-outer);
|
|
443
|
+
outline-offset: var(--svelte-ui-focus-outline-offset-outer);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* Checked/Indeterminate background */
|
|
447
|
+
input[type='checkbox']:checked + .checkbox__icon,
|
|
448
|
+
input[type='checkbox']:indeterminate + .checkbox__icon {
|
|
449
|
+
background-color: var(--svelte-ui-checkbox-bg-checked);
|
|
450
|
+
border-color: transparent;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* =========================================================================
|
|
454
|
+
* Size Variants
|
|
455
|
+
* ========================================================================= */
|
|
456
|
+
|
|
457
|
+
.checkbox--small {
|
|
458
|
+
font-size: inherit;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.checkbox--small .checkbox__label {
|
|
462
|
+
min-height: var(--svelte-ui-checkbox-min-height-sm);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.checkbox--small .checkbox__icon {
|
|
466
|
+
width: var(--svelte-ui-checkbox-size-sm);
|
|
467
|
+
height: var(--svelte-ui-checkbox-size-sm);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.checkbox--small .checkbox__icon::after {
|
|
471
|
+
font-size: var(--svelte-ui-checkbox-icon-size-sm);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.checkbox--large {
|
|
475
|
+
font-size: inherit;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.checkbox--large .checkbox__label {
|
|
479
|
+
min-height: var(--svelte-ui-checkbox-min-height-lg);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.checkbox--large .checkbox__icon {
|
|
483
|
+
width: var(--svelte-ui-checkbox-size-lg);
|
|
484
|
+
height: var(--svelte-ui-checkbox-size-lg);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.checkbox--large .checkbox__icon::after {
|
|
488
|
+
font-size: var(--svelte-ui-checkbox-icon-size-lg);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* =========================================================================
|
|
492
|
+
* Motion & Media Queries
|
|
493
|
+
* ========================================================================= */
|
|
494
|
+
|
|
495
|
+
/* Mobile touch targets */
|
|
496
|
+
@media (hover: none) and (pointer: coarse) {
|
|
497
|
+
.checkbox__label {
|
|
498
|
+
min-height: var(--svelte-ui-touch-target);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.checkbox--small .checkbox__label {
|
|
502
|
+
min-height: var(--svelte-ui-touch-target-sm);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.checkbox--large .checkbox__label {
|
|
506
|
+
min-height: var(--svelte-ui-touch-target-lg);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* Reduced motion */
|
|
511
|
+
.checkbox--no-motion .checkbox__icon,
|
|
512
|
+
.checkbox--no-motion .checkbox__icon::after {
|
|
513
|
+
transition-duration: 0.01s;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/* Prefers reduced motion */
|
|
517
|
+
@media (prefers-reduced-motion: reduce) {
|
|
518
|
+
.checkbox__icon,
|
|
519
|
+
.checkbox__icon::after {
|
|
520
|
+
transition-duration: 0.01s;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
</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: boolean;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
id?: string;
|
|
9
|
+
inputAttributes?: HTMLInputAttributes | undefined;
|
|
10
|
+
size?: 'small' | 'medium' | 'large';
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
reducedMotion?: boolean;
|
|
14
|
+
onchange?: (value: boolean) => 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 Checkbox: import("svelte").Component<$$ComponentProps, {}, "value" | "indeterminate">;
|
|
41
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
42
|
+
export default Checkbox;
|