Checkbox
Checkboxは、ユーザーが複数の選択肢から1つ以上を選択できるコンポーネントです。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer">
<Checkbox bind:checked /> 選択肢
</Label>
プロパティ
Checkboxは、以下のプロパティをサポートしています。
| 名前 | 型 | デフォルト値 | 説明 |
|---|---|---|---|
checked |
boolean |
チェック状態を制御します。 | |
indeterminate |
boolean |
false |
一部のみ選択された状態や、不確定な状態を表します。 |
isError |
boolean |
false |
エラーのスタイルを適用します。 |
disabled |
boolean |
false |
チェックボックスを無効化します。 |
インストールの手順
以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。
atoms/Checkbox.svelte
<!--
@component
## 概要
- ユーザーが複数の選択肢から1つ以上を選択できるコンポーネントです
## 機能
- checkboxタグと同様に bind:group が使用できます
- 見た目を変更するためのいくつかのスタイル用Propsが追加されています(詳細はPropsセクションを参照)
## Props
- isError: true の場合エラー時のスタイルを適用します
- disabled: 指定するとグレーアウトされ、クリック不可になります
- indeterminate: 一部のみ選択された状態や、不確定な状態です
## Usage
```svelte
<Checkbox bind:checked />
```
-->
<script module lang="ts">
import type { ClassValue, HTMLInputAttributes } from 'svelte/elements';
import { cva, type VariantProps } from 'class-variance-authority';
export const checkboxBorderVariants = cva('relative size-4 border border-base-stroke-default rounded-xs outline-primary/20 transition-shadow peer-focus-visible:hover:outline-[0.125rem] peer-focus-visible:outline-[0.125rem] peer-focus-visible:outline-offset-[0.125rem] peer-focus-visible:hover:outline-primary peer-focus-visible:outline-primary peer-focus-visible:hover:ring-[0.125rem] peer-focus-visible:hover:ring-primary/20', {
variants: {
/** 操作できるかどうか */
disabled: {
true: ['bg-base-container-muted opacity-50 pointer-events-none'],
false: ['cursor-pointer group-hover:ring-[0.25rem] hover:ring-[0.25rem] group-hover:ring-primary/20 hover:ring-primary/20'],
},
/** エラーかどうか */
isError: {
true: ['border-destructive'],
false: [],
},
},
});
export const checkboxIconVariants = cva('absolute top-0 size-4 rounded-xs text-base-foreground-on-fill-bright pointer-events-none scale-0 transition-shadow peer-checked:scale-100', {
variants: {
/** 操作できるかどうか */
disabled: {
true: ['bg-base-container-muted opacity-50'],
false: [],
},
/** エラーかどうか */
isError: {
true: ['bg-destructive'],
false: ['bg-primary'],
},
},
});
export type CheckboxVariants = VariantProps<typeof checkboxBorderVariants>;
export interface CheckboxProps extends CheckboxVariants, HTMLInputAttributes {
/** クラス */
class?: ClassValue;
/** チェックしているかどうか */
checked?: boolean;
/** グループ */
group?: any[];
/** 値 */
value?: any;
/** 不確定な要素かどうか */
indeterminate?: boolean;
}
// for bind:group
const groupSetMap = new WeakMap<any[], Set<any>>();
function getGroupSet(group: any[]): Set<any> {
let set = groupSetMap.get(group);
if (!set) {
set = new Set(group);
groupSetMap.set(group, set);
}
return set;
}
</script>
<script lang="ts">
import { Check, Minus } from '@lucide/svelte';
import { untrack } from 'svelte';
let { class: className, checked = $bindable(false), group = $bindable(), indeterminate = false, isError = false, disabled = false, value, ...inputAttributes }: CheckboxProps = $props();
let checkboxBorderVariantClass = $derived(checkboxBorderVariants({ disabled, isError }));
let checkboxIconVariantClass = $derived(checkboxIconVariants({ disabled, isError }));
$effect(() => {
if (group) {
const set = getGroupSet(group);
checked = set.has(value);
}
});
$effect(() => {
// watch `checked`
checked;
untrack(() => {
if (group) {
const set = getGroupSet(group);
// skip
if (set.has(value) === checked) return;
if (checked) {
set.add(value);
}
else {
set.delete(value);
}
// update group
group = [...set.values()];
}
});
});
</script>
<label class={[className, 'relative block size-fit']}>
<input class="sr-only peer" type="checkbox" {disabled} {...inputAttributes} bind:checked />
<div class={checkboxBorderVariantClass}></div>
{#if indeterminate}
<Minus class={checkboxIconVariantClass} />
{:else}
<Check class={checkboxIconVariantClass} />
{/if}
</label>
使い方
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer">
<Checkbox bind:checked /> 選択肢
</Label>
サンプル
Default
特に操作が行われていない、デフォルトの状態です。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer">
<Checkbox bind:checked /> 選択肢
</Label>
Error
入力内容に問題があり、エラーが表示されている状態です。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer">
<Checkbox bind:checked isError /> 選択肢
</Label>
Disabled
利用不可の状態です。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer" disabled>
<Checkbox bind:checked disabled /> 選択肢
</Label>
Indeterminate
一部のみ選択された状態や、不確定な状態を示します。
ツリービューで親が一部の子だけ選ばれている場合などに使用されます。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let checked = $state(false);
</script>
<Label class="gap-2 group cursor-pointer">
<Checkbox bind:checked indeterminate /> 選択肢
</Label>
Group
bind:groupを使うことで、複数のチェックボックスをグループ化し、同じ値を共有できます。
これにより、複数のチェックボックスの状態を一括で管理できます。
<script lang="ts">
import Checkbox from '$lib/components/ui/atoms/Checkbox.svelte';
import DebugConsole from '$lib/components/ui/atoms/DebugConsole.svelte';
import Label from '$lib/components/ui/atoms/Label.svelte';
let group = $state(['item1', 'item3']);
</script>
<div class="size-full">
<div class="grid gap-2">
<Label class="gap-2 group cursor-pointer">
<Checkbox value="item1" bind:group />選択肢1
</Label>
<Label class="gap-2 group cursor-pointer">
<Checkbox value="item2" bind:group />選択肢2
</Label>
<Label class="gap-2 group cursor-pointer">
<Checkbox value="item3" bind:group />選択肢3
</Label>
</div>
<DebugConsole class="mt-4" data={{ group }} />
</div>