Răsfoiți Sursa

[stdf]Update Checkbox compoent, demo and documents.

dufu1991 1 an în urmă
părinte
comite
fac670c0fd

+ 1 - 0
demo/src/lib/symbol/ri-check-double-line.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11.602 13.7599L13.014 15.1719L21.4795 6.7063L22.8938 8.12051L13.014 18.0003L6.65 11.6363L8.06421 10.2221L10.189 12.3469L11.6025 13.7594L11.602 13.7599ZM11.6037 10.9322L16.5563 5.97949L17.9666 7.38977L13.014 12.3424L11.6037 10.9322ZM8.77698 16.5873L7.36396 18.0003L1 11.6363L2.41421 10.2221L3.82723 11.6352L3.82604 11.6363L8.77698 16.5873Z"></path></svg>

+ 230 - 229
demo/src/routes/en_US/checkbox/+page.svelte

@@ -1,87 +1,113 @@
 <!-- Checkbox Demo -->
 <script>
-	import { CheckboxGroup, Checkbox, Divider, Button } from '../../../../../packages/stdf/components';
-	import { onMount } from 'svelte';
 	import { quintOut } from 'svelte/easing';
 	import { crossfade } from 'svelte/transition';
 	import { flip } from 'svelte/animate';
+	import { Checkbox, CheckboxItem, Divider, Button, Icon } from '../../../../../packages/stdf/components';
 
 	const dota = [
-		{ label: 'Jugg', name: 'Juggernaut', desc: 'The master is a melee agile hero, and he can quickly cut into the battle.' },
-		{ label: 'SB', name: 'Spirit Breaker', desc: 'Barlason, the soul -cracked man is a powerful Gank -type melee hero.' },
-		{ label: 'Kotl', name: 'Keeper of the Light', desc: 'Izalo, Guardian of Light, he is a famous auxiliary intellectual hero.' },
-		{ label: 'Mag', name: 'Magnus', desc: 'Mighty? Why not BAN mighty? Why not BAN mighty?' },
+		{ label: 'Jugg', name: 'Juggernaut', desc: 'Juggernaut is a melee agility hero who can quickly engage in combat.' },
+		{ label: 'SB', name: 'Spirit Breaker', desc: 'Barathrum, Spirit Breaker is a powerful ganking strength melee hero.' },
+		{ label: 'KOTL', name: 'Keeper of the Light', desc: 'Ezalor, Keeper of the Light, is a famous support intelligence hero.' },
+		{ label: 'Mag', name: 'Magnus', desc: 'Magnus? Why not ban Magnus? Why not ban Magnus?' },
 	];
-	let checkeds = [];
-	const dotaFun = e => {
-		checkeds = e.detail;
+	let checkeds = $state([]);
+	const clickItemFn = name => {
+		checkeds = checkeds.includes(name) ? checkeds.filter(v => v !== name) : [name, ...checkeds];
+	};
+	let checkedsCustom = $state([]);
+	const clickCustomFn = name => {
+		checkedsCustom = checkedsCustom.includes(name) ? checkedsCustom.filter(v => v !== name) : [name, ...checkedsCustom];
 	};
 
-	const dotaImgs = ['Lina', 'ES', 'Mor', 'Sni', 'SV'];
-	const dotaForImgs = ['火女', '小牛', '水人', '火枪', '斯温'];
-	let imgCheckeds = ['Lina'];
-	const dotaImgsFun = e => {
-		imgCheckeds = e.detail;
+	const dotaInlines = [
+		{ label: 'Lina', name: 'Lina' },
+		{ label: 'SB', name: 'Spirit Breaker' },
+		{ label: 'Morph', name: 'Morphling' },
+		{ label: 'Sniper', name: 'Sniper' },
+		{ label: 'Sven', name: 'Sven' },
+		{ label: 'Invoker', name: 'Invoker' },
+		{ label: 'Tide', name: 'Tidehunter' },
+		{ label: 'Bat', name: 'Batrider' },
+		{ label: 'Mag', name: 'Magnus' },
+	];
+	let checkInlines = $state(['Spirit Breaker', 'Morphling']);
+	const dotaInlineFun = name => {
+		checkInlines = checkInlines.includes(name) ? checkInlines.filter(v => v !== name) : [name, ...checkInlines];
 	};
 
+	const dotaImgs = [
+		{ label: 'Lina', name: 'Lina', imgName: '火女' },
+		{ label: 'SB', name: 'Spirit Breaker', imgName: '小牛' },
+		{ label: 'Morph', name: 'Morphling', imgName: '水人' },
+		{ label: 'Sniper', name: 'Sniper', imgName: '火枪' },
+		{ label: 'Sven', name: 'Sven', imgName: '斯温' },
+	];
+	let imgCheckeds = $state(['Lina']);
+	const dotaImgsFun = name => {
+		imgCheckeds = imgCheckeds.includes(name) ? imgCheckeds.filter(v => v !== name) : [name, ...imgCheckeds];
+	};
+
+	const dotaLong = [
+		{ name: 'Jugg', label: 'Juggernaut is a melee agility hero who can quickly engage in combat.' },
+		{ name: 'SB', label: 'Barathrum, Spirit Breaker is a powerful ganking strength melee hero.' },
+		{ name: 'KOTL', label: 'Ezalor, Keeper of the Light, is a famous support intelligence hero.' },
+		{ name: 'Mag', label: 'Magnus? Why not ban Magnus? Why not ban Magnus?' },
+	];
+
 	const dotaVoices = [
-		{ label: 'karl', name: 'karl', voices: ['"From the great mystery."'] },
-		{ label: 'Storm', name: 'Storm', voices: ['"Hey ~ Quickly wake up, drink coffee, we are attacked on the tower."'] },
+		{ name: 'Invoker', label: 'Invoker', voices: ['"From the first point was begat a line."'] },
+		{ name: 'Storm', label: 'Storm', voices: ['"Hey, wake up! Get some coffee, our tower is under attack."'] },
 		{
-			label: 'Bat',
-			name: 'Bat',
+			name: 'Batrider',
+			label: 'Batrider',
 			voices: [
-				'"Oh, Lina, what about Mrs. My Village?"',
-				"'Oh, popular, you don't have to run when you see me.'",
-				'"Crystal room woman, you are like a tall cup full of ice water."',
+				'"Oh Lina, wanna be my queen?"',
+				'"Oh Windrunner, no need to run from me."',
+				'"Crystal Maiden, you\'re like a tall glass of water."',
 			],
 		},
-		{ label: 'Tide', name: 'Tide', voices: ['"I can still eat a seaweed."'] },
+		{ name: 'Tidehunter', label: 'Tidehunter', voices: ['"I could eat a sea cucumber."'] },
 	];
-	let voiceCheckeds = [];
-	const dotaVoicesFun = e => {
-		voiceCheckeds = e.detail;
+	let voiceCheckeds = $state([]);
+	const dotaVoicesFun = name => {
+		voiceCheckeds = voiceCheckeds.includes(name) ? voiceCheckeds.filter(v => v !== name) : [name, ...voiceCheckeds];
 	};
 
-	const dotaInlines = ['Lina', 'ES', 'Mor', 'Sni', 'SV', 'karl', 'Tide', 'Bat', 'Mag'];
-	let checkInlines = ['ES', 'Mor'];
-	const dotaInlineFun = e => {
-		checkInlines = e.detail;
-	};
-
-	const dotaAll = ['Lina', 'ES', 'Mor', 'Sni', 'SV', 'karl', 'Tide', 'Bat', 'Mag'];
-	let checkDotas = ['Sni', 'SV'];
-	$: noCheckDotas = dotaAll.filter(a => !checkDotas.some(b => b === a));
-	let outControl = true;
-	// Click the single item
-	const dotaAllFun = e => {
-		outControl = false;
-		checkDotas = e.detail;
-	};
-	// select all
+	const dotaAll = [
+		{ name: 'Lina', label: 'Lina' },
+		{ name: 'SB', label: 'Spirit Breaker' },
+		{ name: 'Morph', label: 'Morphling' },
+		{ name: 'Sniper', label: 'Sniper' },
+		{ name: 'Sven', label: 'Sven' },
+		{ name: 'Invoker', label: 'Invoker' },
+		{ name: 'Tide', label: 'Tidehunter' },
+		{ name: 'Bat', label: 'Batrider' },
+		{ name: 'Mag', label: 'Magnus' },
+	];
+	let checkDotas = $state(['Sniper', 'Sven']);
+	let noCheckDotas = $derived(dotaAll.filter(a => !checkDotas.some(b => b === a.name)).map(a => a.name));
+	// Select All
 	const checkAllFun = () => {
-		outControl = true;
-		checkDotas = dotaAll;
+		checkDotas = dotaAll.map(a => a.name);
 	};
-	// unselect all
+	// Select None
 	const checkNoneFun = () => {
-		outControl = true;
 		checkDotas = [];
 	};
-	// Counter -selection
+	// Reverse Selection
 	const checkReverseFun = () => {
-		outControl = true;
-		checkDotas = dotaAll.filter(a => !checkDotas.some(b => b === a));
+		checkDotas = dotaAll.filter(a => !checkDotas.some(b => b === a.name)).map(a => a.name);
 	};
 
-	const animates = ['Lina', 'ES', 'Mor', 'Sni', 'SV', 'karl', 'Tide', 'Bat', 'Mag'];
-	let animateNos = ['Lina', 'ES', 'Mor', 'Sni', 'SV', 'karl'];
-	$: animateYess = animates.filter(a => !animateNos.some(b => b === a));
-	const checkAnimateNoFun = e => {
-		animateNos = animateNos.filter(a => !e.detail.some(b => b === a));
+	const animates = ['Lina', 'SB', 'Morph', 'Sniper', 'Sven', 'Invoker', 'Tide', 'Bat', 'Mag'];
+	let animateNos = $state(['Lina', 'SB', 'Morph', 'Sniper', 'Sven', 'Invoker']);
+	let animateYess = $derived(animates.filter(a => !animateNos.some(b => b === a)));
+	const checkAnimateNoFun = name => {
+		animateNos = animateNos.filter(a => a !== name);
 	};
-	const checkAnimaYesFun = e => {
-		animateNos = animates.filter(a => !e.detail.some(b => b === a));
+	const checkAnimaYesFun = name => {
+		animateNos = [name, ...animateNos];
 	};
 	const [send, receive] = crossfade({
 		duration: d => Math.sqrt(d * 150),
@@ -98,221 +124,196 @@
 			};
 		},
 	});
-	// Set the height to avoid changes in page height and affect the click experience.
-	let heights = 0;
-	let animateHeight = 0;
-	onMount(() => {
-		animateHeight = (heights / 6) * 9 + 32;
-	});
+
+	// Set height to avoid page height changes affecting click experience
+	let heights = $state(0);
+	let animateHeight = $derived(heights * animates.length + 50);
 </script>
 
-<div class="px-4 text-left">
-	<div class="mt-8 mb-4 font-bold text-2xl">Horizontal arrangement</div>
-	<div class="mt-6 mb-4 font-bold text-lg">Simple usage</div>
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+<div class="px-4">
+	<div class="mt-8 mb-4 font-bold text-2xl">Horizontal Layout</div>
+	<div class="mt-6 mb-4 font-bold text-lg">Basic Usage</div>
+	<Checkbox layout="h" data={dota} />
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Text is in different positions</div>
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="l">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Text Position</div>
+	<Checkbox layout="h" data={dota} textPosition="l" />
 	<Divider />
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="b">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox layout="h" data={dota} textPosition="b" />
+	<Divider />
+	<Checkbox layout="h" data={dota} textPosition="t" />
 	<Divider />
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="t">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
 
-	<div class="mt-8 mb-4 font-bold text-lg">Custom icon</div>
-	<CheckboxGroup layout="h">
-		<Checkbox checkeds={[]} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} name="Daemon"
-			>Jugg</Checkbox
-		>
-		<Checkbox checkeds={[]} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} name="Linghuchong"
-			>Mor</Checkbox
-		>
-		<Checkbox
-			checkeds={[]}
-			icon={{ name: 'ri-checkbox-multiple-line' }}
-			iconChecked={{ name: 'ri-checkbox-multiple-fill' }}
-			name="Light Light methodard">Kotl</Checkbox
-		>
-		<Checkbox checkeds={[]} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} name="Magnas">Mag</Checkbox>
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Custom Icons</div>
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-check-double-line' }} iconChecked={{ name: 'ri-check-double-line' }} />
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Picture option</div>
-	<CheckboxGroup layout="h" on:change={dotaImgsFun}>
-		{#each dotaImgs as dota, i}
-			<Checkbox
-				name={dota}
-				textPosition="t"
-				icon={{ name: 'ri-arrow-up-s-line' }}
-				iconChecked={{ name: 'ri-arrow-up-s-fill' }}
-				outControl
-				checkeds={imgCheckeds}
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet as Button Component</div>
+	<Checkbox layout="h" data={dota}>
+		{#snippet checkboxChild({ item })}
+			<Button
+				fill={checkeds.includes(item.name) ? 'base' : 'lineLight'}
+				injClass="px-2 !py-1"
+				onclick={() => {
+					clickItemFn(item.name);
+				}}
 			>
-				<div class={`w-12 h-12 mb-1 rounded overflow-hidden ${imgCheckeds.includes(dota) ? 'ring-2 ring-primary dark:ring-dark' : ''}`}>
-					<img class="w-full h-full object-cover" src={`/assets/images/dota_${dotaForImgs[i]}.png`} alt="" />
-				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
-	<div class="text-sm">
-		{imgCheckeds.length > 0 ? 'selected' : 'No hero is selected.'}
-		<span class="text-[red]">{imgCheckeds.join(' + ')}</span>{imgCheckeds.length > 0 ? '。' : ''}
-	</div>
+				{item.label}
+			</Button>
+		{/snippet}
+	</Checkbox>
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">No icon && use Button</div>
-	<CheckboxGroup layout="h" on:change={dotaFun}>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} icon="none" iconChecked="none">
-				<Button fill={checkeds.includes(item.name) ? 'base' : 'lineLight'} size="auto" injClass="px-4">{item.label}</Button>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet as Custom Element</div>
+	<Checkbox layout="h" data={dota}>
+		{#snippet checkboxChild({ item })}
+			<button
+				class="border py-0.5 px-5 text-sm rounded {checkedsCustom.includes(item.name)
+					? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
+					: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'}"
+				onclick={() => {
+					clickCustomFn(item.name);
+				}}
+			>
+				{item.label}
+			</button>
+		{/snippet}
+	</Checkbox>
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">No icon && Customized selection effect</div>
-	<CheckboxGroup layout="h" on:change={dotaFun}>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} icon="none" iconChecked="none">
+	<div class="mt-8 mb-4 font-bold text-lg">Image Options</div>
+	<Checkbox layout="h" data={dotaImgs}>
+		{#snippet checkboxChild({ item })}
+			<div class="flex flex-col items-center">
+				<!-- svelte-ignore a11y_click_events_have_key_events -->
+				<!-- svelte-ignore a11y_no_static_element_interactions -->
 				<div
-					class={`border py-0.5 px-5 text-sm rounded ${
-						checkeds.includes(item.name)
-							? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
-							: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'
-					}`}
+					class="w-12 h-12 mb-1 rounded overflow-hidden {imgCheckeds.includes(item.name) ? 'ring-2 ring-primary dark:ring-dark' : ''}"
+					onclick={() => {
+						dotaImgsFun(item.name);
+					}}
 				>
-					{item.label}
+					<img class="w-full h-full object-cover" src={`/assets/images/dota_${item.imgName}.png`} alt="" />
 				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+				<div>
+					<Icon
+						name={imgCheckeds.includes(item.name) ? 'ri-arrow-up-s-fill' : 'ri-arrow-up-s-line'}
+						theme={imgCheckeds.includes(item.name)}
+						alpha={imgCheckeds.includes(item.name) ? 1 : 0.2}
+					/>
+				</div>
+			</div>
+		{/snippet}
+	</Checkbox>
+	<div class="text-sm">
+		{imgCheckeds.length > 0 ? 'Selected' : 'No heroes selected'}
+		<span class="text-[red]">{imgCheckeds.join(' + ')}</span>
+	</div>
+	<Divider />
 </div>
-<Divider />
 
 <div class="px-4">
-	<div class="mb-4 font-bold">Elemental element arrangementtal element arrangement</div>
-	<div class="mt-8 mb-4 font-bold">No icon && Elemental element arrangement</div>
-	<CheckboxGroup layout="inline" on:change={dotaInlineFun}>
-		{#each dotaInlines as item}
-			<Checkbox name={item} checkeds={checkInlines} icon="none" iconChecked="none">
-				<div
-					class={`border py-0.5 px-1 m-1 text-sm rounded ${
-						checkInlines.includes(item)
-							? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
-							: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'
-					}`}
-				>
-					{item}
-				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
-	<div class="text-xs mt-2">selected:{checkInlines.join('-')}</div>
+	<div class="mb-4 font-bold text-2xl">Inline Layout</div>
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet Inline Layout</div>
+	<Checkbox layout="inline" data={dotaInlines}>
+		{#snippet checkboxChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div
+				class="border py-0.5 px-2 m-1 text-sm rounded {checkInlines.includes(item.name)
+					? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
+					: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'}"
+				onclick={() => {
+					dotaInlineFun(item.name);
+				}}
+			>
+				{item.label}
+			</div>
+		{/snippet}
+	</Checkbox>
+	<div class="text-xs mt-2">Selected: {checkInlines.join('-')}</div>
 </div>
 <Divider />
 
 <div class="px-4 pb-8">
-	<div class="mb-4 font-bold text-2xl">Vertical arrangement</div>
-	<div class="mt-8 mb-4 font-bold text-lg">Basic usage</div>
-	<CheckboxGroup>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mb-4 font-bold text-2xl">Vertical Layout</div>
+	<div class="mt-8 mb-4 font-bold text-lg">Basic Usage</div>
+	<Checkbox data={dota} />
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Long text option</div>
-	<CheckboxGroup>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.desc}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Long Text Options</div>
+	<Checkbox data={dotaLong} />
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Text to left</div>
-	<CheckboxGroup>
-		{#each dota as item, i}
-			<Checkbox {checkeds} name={item.name} textPosition="l">
-				{item.label}
-				{#if i !== dota.length - 1}
-					<div class="h-px mt-1 bg-gray-200 dark:bg-gray-800" />
-				{/if}
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Left Aligned Text</div>
+	<Checkbox data={dota} textPosition="l" />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Complicated option && Customized selection effect</div>
-	<CheckboxGroup on:change={dotaVoicesFun}>
-		{#each dotaVoices as item, i}
-			<Checkbox checkeds={voiceCheckeds} name={item.name}>
-				<div class={`ml-2 ${voiceCheckeds.includes(item.name) ? 'text-primary dark:text-dark' : ''}`}>
+	<div class="mt-8 mb-4 font-bold text-lg">Complex Options && Custom Selection Effect</div>
+	<Checkbox data={dotaVoices}>
+		{#snippet checkboxChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div class="flex items-center cursor-pointer" onclick={() => dotaVoicesFun(item.name)}>
+				<div>
+					<Icon
+						name={voiceCheckeds.includes(item.name) ? 'ri-checkbox-fill' : 'ri-checkbox-line'}
+						theme={voiceCheckeds.includes(item.name)}
+						alpha={voiceCheckeds.includes(item.name) ? 1 : 0.2}
+					/>
+				</div>
+				<div class="ml-2 grow {voiceCheckeds.includes(item.name) ? 'text-primary dark:text-dark' : ''}">
 					{item.label}
 					{#each item.voices as voice}
 						<div class="text-xs">{voice}</div>
 					{/each}
-					{#if i !== dotaVoices.length - 1}
-						<div class="h-px mt-1 bg-gray-200 dark:bg-gray-800" />
-					{/if}
+					<div class="h-px mt-1 bg-black/10 dark:bg-white/10"></div>
 				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+			</div>
+		{/snippet}
+	</Checkbox>
 
-	<div class="mt-8 mb-4 font-bold text-lg">Batch operation</div>
-	<CheckboxGroup on:change={dotaAllFun}>
-		{#each dotaAll as item}
-			<Checkbox checkeds={checkDotas} name={item} {outControl}>{item}</Checkbox>
-		{/each}
-	</CheckboxGroup>
-	<div class="text-xs mt-2">selected:{checkDotas.join('-')}</div>
-	<div class="text-xs mt-2">Une selected:{noCheckDotas.join('-')}</div>
+	<div class="mt-8 mb-4 font-bold text-lg">Batch Operations</div>
+	<Checkbox data={dotaAll} bind:checkeds={checkDotas} />
+	<div class="text-xs mt-2">Selected: {checkDotas.map(a => dotaAll.find(b => b.name === a).label).join('-')}</div>
+	<div class="text-xs mt-2">Unselected: {noCheckDotas.map(a => dotaAll.find(b => b.name === a).label).join('-')}</div>
 	<div class="flex mt-2">
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkAllFun}>select all</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkAllFun}>All</Button>
 		</div>
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkNoneFun}>unselect all</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkNoneFun}>None</Button>
 		</div>
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkReverseFun}>Counter -selection</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkReverseFun}>Reverse</Button>
 		</div>
 	</div>
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">Animation</div>
-	<div class="flex" style="height: {animateHeight}px;">
+	<div class="mt-8 mb-4 font-bold text-lg">With Animation</div>
+	<div class="flex justify-center" style="height: {animateHeight}px;">
 		<div class="flex-1">
-			<div class="pb-2">Unsteady</div>
-			<div bind:clientHeight={heights}>
-				<CheckboxGroup on:change={checkAnimateNoFun}>
-					{#each animateNos as item (item)}
-						<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
-							<Checkbox name={item} outControl checkeds={[]}>{item}</Checkbox>
-						</div>
-					{/each}
-				</CheckboxGroup>
-			</div>
+			<div class="pb-2">Unselected</div>
+			{#each animateNos as item (item)}
+				<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }} bind:clientHeight={heights}>
+					<CheckboxItem name={item} checked={!animateNos.includes(item)} onclick={checkAnimateNoFun}>{item}</CheckboxItem>
+				</div>
+			{/each}
 		</div>
 		<div class="flex-1">
-			<div class="pb-2">selected</div>
-			<CheckboxGroup on:change={checkAnimaYesFun}>
-				{#each animateYess as item (item)}
-					<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
-						<Checkbox outControl checkeds={animateYess} name={item}>
-							{item}
-						</Checkbox>
-					</div>
-				{/each}
-			</CheckboxGroup>
+			<div class="pb-2">Selected</div>
+			{#each animateYess as item (item)}
+				<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
+					<CheckboxItem name={item} checked={animateYess.includes(item)} onclick={checkAnimaYesFun}>{item}</CheckboxItem>
+				</div>
+			{/each}
 		</div>
 	</div>
 </div>

+ 201 - 196
demo/src/routes/zh_CN/checkbox/+page.svelte

@@ -1,10 +1,9 @@
 <!-- Checkbox Demo -->
 <script>
-	import { CheckboxGroup, Checkbox, Divider, Button } from '../../../../../packages/stdf/components';
-	import { onMount } from 'svelte';
 	import { quintOut } from 'svelte/easing';
 	import { crossfade } from 'svelte/transition';
 	import { flip } from 'svelte/animate';
+	import { Checkbox, CheckboxItem, Divider, Button, Icon } from '../../../../../packages/stdf/components';
 
 	const dota = [
 		{ label: '主宰', name: '奶棒人', desc: '主宰是一个近战敏捷英雄,他能够迅速切入战斗。' },
@@ -12,71 +11,99 @@
 		{ label: '光法', name: '光之守卫', desc: '伊扎洛,光之守卫,他是一个著名的辅助性智力英雄。' },
 		{ label: '猛犸', name: '马格纳斯', desc: '猛犸?为什么不 BAN 猛犸?为什么不 BAN 猛犸?' },
 	];
-	let checkeds = [];
-	const dotaFun = e => {
-		checkeds = e.detail;
+	let checkeds = $state([]);
+	const clickItemFn = name => {
+		checkeds = checkeds.includes(name) ? checkeds.filter(v => v !== name) : [name, ...checkeds];
+	};
+	let checkedsCustom = $state([]);
+	const clickCustomFn = name => {
+		checkedsCustom = checkedsCustom.includes(name) ? checkedsCustom.filter(v => v !== name) : [name, ...checkedsCustom];
 	};
 
-	const dotaImgs = ['火女', '小牛', '水人', '火枪', '斯温'];
-	let imgCheckeds = ['火女'];
-	const dotaImgsFun = e => {
-		imgCheckeds = e.detail;
+	const dotaInlines = [
+		{ label: '火女', name: '火女' },
+		{ label: '小牛', name: '小牛' },
+		{ label: '水人', name: '水人' },
+		{ label: '火枪', name: '火枪' },
+		{ label: '斯温', name: '斯温' },
+		{ label: '祈求者', name: '祈求者' },
+		{ label: '潮汐', name: '潮汐' },
+		{ label: '蝙蝠', name: '蝙蝠' },
+		{ label: '猛犸', name: '猛犸' },
+	];
+	let checkInlines = $state(['小牛', '水人']);
+	const dotaInlineFun = name => {
+		checkInlines = checkInlines.includes(name) ? checkInlines.filter(v => v !== name) : [name, ...checkInlines];
 	};
 
+	const dotaImgs = [
+		{ label: '火女', name: '火女' },
+		{ label: '小牛', name: '小牛' },
+		{ label: '水人', name: '水人' },
+		{ label: '火枪', name: '火枪' },
+		{ label: '斯温', name: '斯温' },
+	];
+	let imgCheckeds = $state(['火女']);
+	const dotaImgsFun = name => {
+		imgCheckeds = imgCheckeds.includes(name) ? imgCheckeds.filter(v => v !== name) : [name, ...imgCheckeds];
+	};
+
+	const dotaLong = [
+		{ name: '主宰', label: '主宰是一个近战敏捷英雄,他能够迅速切入战斗。' },
+		{ name: '白牛', label: '巴拉森,裂魂人是一个强大的 Gank 型的力量型近战英雄。' },
+		{ name: '光法', label: '伊扎洛,光之守卫,他是一个著名的辅助性智力英雄。' },
+		{ name: '猛犸', label: '猛犸?为什么不 BAN 猛犸?为什么不 BAN 猛犸?' },
+	];
+
 	const dotaVoices = [
-		{ label: '祈求者', name: '祈求者', voices: ['“来自于伟大的奥秘。”'] },
-		{ label: '风暴之灵', name: '风暴之灵', voices: ['“喂~快醒醒,快喝点咖啡,我方上塔正遭受攻击。”'] },
+		{ name: '祈求者', label: '祈求者', voices: ['“来自于伟大的奥秘。”'] },
+		{ name: '风暴之灵', label: '风暴之灵', voices: ['“喂~快醒醒,快喝点咖啡,我方上塔正遭受攻击。”'] },
 		{
-			label: '蝙蝠骑士',
 			name: '蝙蝠骑士',
+			label: '蝙蝠骑士',
 			voices: ['“哦,莉娜,当我的压寨夫人怎么样?”', '“哦,风行者,见着我你不用跑。”', '“水晶室女,你就像一个装满冰水的高脚杯。”'],
 		},
-		{ label: '潮汐猎人', name: '潮汐猎人', voices: ['“我还能吃下一根海藻。”'] },
+		{ name: '潮汐猎人', label: '潮汐猎人', voices: ['“我还能吃下一根海藻。”'] },
 	];
-	let voiceCheckeds = [];
-	const dotaVoicesFun = e => {
-		voiceCheckeds = e.detail;
-	};
-
-	const dotaInlines = ['火女', '小牛', '水人', '火枪', '斯温', '祈求者', '潮汐', '蝙蝠', '猛犸'];
-	let checkInlines = ['小牛', '水人'];
-	const dotaInlineFun = e => {
-		checkInlines = e.detail;
+	let voiceCheckeds = $state([]);
+	const dotaVoicesFun = name => {
+		voiceCheckeds = voiceCheckeds.includes(name) ? voiceCheckeds.filter(v => v !== name) : [name, ...voiceCheckeds];
 	};
 
-	const dotaAll = ['火女', '小牛', '水人', '火枪', '斯温', '祈求者', '潮汐', '蝙蝠', '猛犸'];
-	let checkDotas = ['火枪', '斯温'];
-	$: noCheckDotas = dotaAll.filter(a => !checkDotas.some(b => b === a));
-	let outControl = true;
-	// 点击单项
-	const dotaAllFun = e => {
-		outControl = false;
-		checkDotas = e.detail;
-	};
+	const dotaAll = [
+		{ name: '火女', label: '火女' },
+		{ name: '小牛', label: '小牛' },
+		{ name: '水人', label: '水人' },
+		{ name: '火枪', label: '火枪' },
+		{ name: '斯温', label: '斯温' },
+		{ name: '祈求者', label: '祈求者' },
+		{ name: '潮汐', label: '潮汐' },
+		{ name: '蝙蝠', label: '蝙蝠' },
+		{ name: '猛犸', label: '猛犸' },
+	];
+	let checkDotas = $state(['火枪', '斯温']);
+	let noCheckDotas = $derived(dotaAll.filter(a => !checkDotas.some(b => b === a.name)).map(a => a.name));
 	// 全选
 	const checkAllFun = () => {
-		outControl = true;
-		checkDotas = dotaAll;
+		checkDotas = dotaAll.map(a => a.name);
 	};
 	// 全不选
 	const checkNoneFun = () => {
-		outControl = true;
 		checkDotas = [];
 	};
 	// 反选
 	const checkReverseFun = () => {
-		outControl = true;
-		checkDotas = dotaAll.filter(a => !checkDotas.some(b => b === a));
+		checkDotas = dotaAll.filter(a => !checkDotas.some(b => b === a.name)).map(a => a.name);
 	};
 
 	const animates = ['火女', '小牛', '水人', '火枪', '斯温', '祈求者', '潮汐', '蝙蝠', '猛犸'];
-	let animateNos = ['火女', '小牛', '水人', '火枪', '斯温', '祈求者'];
-	$: animateYess = animates.filter(a => !animateNos.some(b => b === a));
-	const checkAnimateNoFun = e => {
-		animateNos = animateNos.filter(a => !e.detail.some(b => b === a));
+	let animateNos = $state(['火女', '小牛', '水人', '火枪', '斯温', '祈求者']);
+	let animateYess = $derived(animates.filter(a => !animateNos.some(b => b === a)));
+	const checkAnimateNoFun = name => {
+		animateNos = animateNos.filter(a => a !== name);
 	};
-	const checkAnimaYesFun = e => {
-		animateNos = animates.filter(a => !e.detail.some(b => b === a));
+	const checkAnimaYesFun = name => {
+		animateNos = [name, ...animateNos];
 	};
 	const [send, receive] = crossfade({
 		duration: d => Math.sqrt(d * 150),
@@ -93,124 +120,121 @@
 			};
 		},
 	});
+
 	// 设定高度,避免页面高度变化影响点击体验。
-	let heights = 0;
-	let animateHeight = 0;
-	onMount(() => {
-		animateHeight = (heights / 6) * 9 + 32;
-	});
+	let heights = $state(0);
+	let animateHeight = $derived(heights * animates.length + 50);
 </script>
 
 <div class="px-4">
 	<div class="mt-8 mb-4 font-bold text-2xl">横向排列</div>
 	<div class="mt-6 mb-4 font-bold text-lg">简单用法</div>
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox layout="h" data={dota} />
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">文字在不同位置</div>
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="l">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox layout="h" data={dota} textPosition="l" />
 	<Divider />
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="b">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox layout="h" data={dota} textPosition="b" />
+	<Divider />
+	<Checkbox layout="h" data={dota} textPosition="t" />
 	<Divider />
-	<CheckboxGroup layout="h">
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} textPosition="t">{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
 
 	<div class="mt-8 mb-4 font-bold text-lg">自定义图标</div>
-	<CheckboxGroup layout="h">
-		<Checkbox checkeds={[]} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} name="奶棒人"
-			>主宰</Checkbox
-		>
-		<Checkbox checkeds={[]} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} name="令狐冲"
-			>白牛</Checkbox
-		>
-		<Checkbox checkeds={[]} icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} name="光之守卫"
-			>光法</Checkbox
-		>
-		<Checkbox checkeds={[]} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} name="马格纳斯">猛犸</Checkbox>
-	</CheckboxGroup>
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} />
+	<Divider />
+	<Checkbox layout="h" data={dota} icon={{ name: 'ri-check-double-line' }} iconChecked={{ name: 'ri-check-double-line' }} />
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">图片选项</div>
-	<CheckboxGroup layout="h" on:change={dotaImgsFun}>
-		{#each dotaImgs as dota}
-			<Checkbox
-				name={dota}
-				textPosition="t"
-				icon={{ name: 'ri-arrow-up-s-line' }}
-				iconChecked={{ name: 'ri-arrow-up-s-fill' }}
-				outControl
-				checkeds={imgCheckeds}
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet 为 Button 组件</div>
+	<Checkbox layout="h" data={dota}>
+		{#snippet checkboxChild({ item })}
+			<Button
+				fill={checkeds.includes(item.name) ? 'base' : 'lineLight'}
+				injClass="px-2 !py-1"
+				onclick={() => {
+					clickItemFn(item.name);
+				}}
 			>
-				<div class={`w-12 h-12 mb-1 rounded overflow-hidden ${imgCheckeds.includes(dota) ? 'ring-2 ring-primary dark:ring-dark' : ''}`}>
-					<img class="w-full h-full object-cover" src={`/assets/images/dota_${dota}.png`} alt="" />
-				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
-	<div class="text-sm">
-		{imgCheckeds.length > 0 ? '已选' : '未选择任何英雄。'}
-		<span class="text-[red]">{imgCheckeds.join(' + ')}</span>{imgCheckeds.length > 0 ? '。' : ''}
-	</div>
+				{item.label}
+			</Button>
+		{/snippet}
+	</Checkbox>
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">不用图标 && 使用 Button</div>
-	<CheckboxGroup layout="h" on:change={dotaFun}>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} icon="none" iconChecked="none">
-				<Button fill={checkeds.includes(item.name) ? 'base' : 'lineLight'} size="auto" injClass="px-4">{item.label}</Button>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet 为自定义元素</div>
+	<Checkbox layout="h" data={dota}>
+		{#snippet checkboxChild({ item })}
+			<button
+				class="border py-0.5 px-5 text-sm rounded {checkedsCustom.includes(item.name)
+					? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
+					: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'}"
+				onclick={() => {
+					clickCustomFn(item.name);
+				}}
+			>
+				{item.label}
+			</button>
+		{/snippet}
+	</Checkbox>
+	<Divider />
 
-	<div class="mt-8 mb-4 font-bold text-lg">不用图标 && 自定义选中效果</div>
-	<CheckboxGroup layout="h" on:change={dotaFun}>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name} icon="none" iconChecked="none">
+	<div class="mt-8 mb-4 font-bold text-lg">图片选项</div>
+	<Checkbox layout="h" data={dotaImgs}>
+		{#snippet checkboxChild({ item })}
+			<div class="flex flex-col items-center">
+				<!-- svelte-ignore a11y_click_events_have_key_events -->
+				<!-- svelte-ignore a11y_no_static_element_interactions -->
 				<div
-					class={`border py-0.5 px-5 text-sm rounded ${
-						checkeds.includes(item.name)
-							? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
-							: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'
-					}`}
+					class="w-12 h-12 mb-1 rounded overflow-hidden {imgCheckeds.includes(item.name) ? 'ring-2 ring-primary dark:ring-dark' : ''}"
+					onclick={() => {
+						dotaImgsFun(item.name);
+					}}
 				>
-					{item.label}
+					<img class="w-full h-full object-cover" src={`/assets/images/dota_${item.name}.png`} alt="" />
+				</div>
+				<div>
+					<Icon
+						name={imgCheckeds.includes(item.name) ? 'ri-arrow-up-s-fill' : 'ri-arrow-up-s-line'}
+						theme={imgCheckeds.includes(item.name)}
+						alpha={imgCheckeds.includes(item.name) ? 1 : 0.2}
+					/>
 				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+			</div>
+		{/snippet}
+	</Checkbox>
+	<div class="text-sm">
+		{imgCheckeds.length > 0 ? '已选' : '未选择任何英雄'}
+		<span class="text-[red]">{imgCheckeds.join(' + ')}</span>
+	</div>
+	<Divider />
 </div>
-<Divider />
 
 <div class="px-4">
 	<div class="mb-4 font-bold text-2xl">行内元素排列</div>
-	<div class="mt-8 mb-4 font-bold text-lg">不用图标 && 行内元素排列111</div>
-	<CheckboxGroup layout="inline" on:change={dotaInlineFun}>
-		{#each dotaInlines as item}
-			<Checkbox name={item} checkeds={checkInlines} icon="none" iconChecked="none">
-				<div
-					class={`border py-0.5 px-1 m-1 text-sm rounded ${
-						checkInlines.includes(item)
-							? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
-							: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'
-					}`}
-				>
-					{item}
-				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet 行内元素排列</div>
+	<Checkbox layout="inline" data={dotaInlines}>
+		{#snippet checkboxChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div
+				class="border py-0.5 px-2 m-1 text-sm rounded {checkInlines.includes(item.name)
+					? 'text-primary dark:text-dark bg-primary/10 dark:bg-dark/10'
+					: 'bg-gray-100 dark:bg-gray-600 border-gray-200 dark:border-gray-500'}"
+				onclick={() => {
+					dotaInlineFun(item.name);
+				}}
+			>
+				{item.label}
+			</div>
+		{/snippet}
+	</Checkbox>
 	<div class="text-xs mt-2">已选:{checkInlines.join('-')}</div>
 </div>
 <Divider />
@@ -218,93 +242,74 @@
 <div class="px-4 pb-8">
 	<div class="mb-4 font-bold text-2xl">纵向排列</div>
 	<div class="mt-8 mb-4 font-bold text-lg">基础用法</div>
-	<CheckboxGroup>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.label}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox data={dota} />
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">长文字选项</div>
-	<CheckboxGroup>
-		{#each dota as item}
-			<Checkbox {checkeds} name={item.name}>{item.desc}</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox data={dotaLong} />
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">文字靠左</div>
-	<CheckboxGroup>
-		{#each dota as item, i}
-			<Checkbox {checkeds} name={item.name} textPosition="l">
-				{item.label}
-				{#if i !== dota.length - 1}
-					<div class="h-px mt-1 bg-gray-200 dark:bg-gray-800" />
-				{/if}
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+	<Checkbox data={dota} textPosition="l" />
 
 	<div class="mt-8 mb-4 font-bold text-lg">复杂选项 && 自定义选中效果</div>
-	<CheckboxGroup on:change={dotaVoicesFun}>
-		{#each dotaVoices as item, i}
-			<Checkbox checkeds={voiceCheckeds} name={item.name}>
-				<div class={`ml-2 ${voiceCheckeds.includes(item.name) ? 'text-primary dark:text-dark' : ''}`}>
+	<Checkbox data={dotaVoices}>
+		{#snippet checkboxChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div class="flex items-center cursor-pointer" onclick={() => dotaVoicesFun(item.name)}>
+				<div>
+					<Icon
+						name={voiceCheckeds.includes(item.name) ? 'ri-checkbox-fill' : 'ri-checkbox-line'}
+						theme={voiceCheckeds.includes(item.name)}
+						alpha={voiceCheckeds.includes(item.name) ? 1 : 0.2}
+					/>
+				</div>
+				<div class="ml-2 grow {voiceCheckeds.includes(item.name) ? 'text-primary dark:text-dark' : ''}">
 					{item.label}
 					{#each item.voices as voice}
 						<div class="text-xs">{voice}</div>
 					{/each}
-					{#if i !== dotaVoices.length - 1}
-						<div class="h-px mt-1 bg-gray-200 dark:bg-gray-800" />
-					{/if}
+					<div class="h-px mt-1 bg-black/10 dark:bg-white/10"></div>
 				</div>
-			</Checkbox>
-		{/each}
-	</CheckboxGroup>
+			</div>
+		{/snippet}
+	</Checkbox>
 
 	<div class="mt-8 mb-4 font-bold text-lg">批量操作</div>
-	<CheckboxGroup on:change={dotaAllFun}>
-		{#each dotaAll as item}
-			<Checkbox checkeds={checkDotas} name={item} {outControl}>{item}</Checkbox>
-		{/each}
-	</CheckboxGroup>
-	<div class="text-xs mt-2">已选:{checkDotas.join('-')}</div>
-	<div class="text-xs mt-2">未选:{noCheckDotas.join('-')}</div>
+	<Checkbox data={dotaAll} bind:checkeds={checkDotas} />
+	<div class="text-xs mt-2">已选:{checkDotas.map(a => dotaAll.find(b => b.name === a).label).join('-')}</div>
+	<div class="text-xs mt-2">未选:{noCheckDotas.map(a => dotaAll.find(b => b.name === a).label).join('-')}</div>
 	<div class="flex mt-2">
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkAllFun}>全选</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkAllFun}>全选</Button>
 		</div>
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkNoneFun}>全不选</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkNoneFun}>全不选</Button>
 		</div>
 		<div class="flex-1">
-			<Button fill="lineTheme" heightIn="1" on:click={checkReverseFun}>反选</Button>
+			<Button fill="lineTheme" heightIn="1" onclick={checkReverseFun}>反选</Button>
 		</div>
 	</div>
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">带动画</div>
-	<div class="flex" style="height: {animateHeight}px;">
+	<div class="flex justify-center" style="height: {animateHeight}px;">
 		<div class="flex-1">
 			<div class="pb-2">未选</div>
-			<div bind:clientHeight={heights}>
-				<CheckboxGroup on:change={checkAnimateNoFun}>
-					{#each animateNos as item (item)}
-						<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
-							<Checkbox name={item} outControl checkeds={[]}>{item}</Checkbox>
-						</div>
-					{/each}
-				</CheckboxGroup>
-			</div>
+			{#each animateNos as item (item)}
+				<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }} bind:clientHeight={heights}>
+					<CheckboxItem name={item} checked={!animateNos.includes(item)} onclick={checkAnimateNoFun}>{item}</CheckboxItem>
+				</div>
+			{/each}
 		</div>
 		<div class="flex-1">
 			<div class="pb-2">已选</div>
-			<CheckboxGroup on:change={checkAnimaYesFun}>
-				{#each animateYess as item (item)}
-					<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
-						<Checkbox outControl checkeds={animateYess} name={item}>
-							{item}
-						</Checkbox>
-					</div>
-				{/each}
-			</CheckboxGroup>
+			{#each animateYess as item (item)}
+				<div in:receive={{ key: item }} out:send={{ key: item }} animate:flip={{ duration: 600 }}>
+					<CheckboxItem name={item} checked={animateYess.includes(item)} onclick={checkAnimaYesFun}>{item}</CheckboxItem>
+				</div>
+			{/each}
 		</div>
 	</div>
 </div>

+ 37 - 25
doc/components/checkbox/api.md

@@ -1,34 +1,46 @@
-## CheckboxGroup Props
+## Checkbox Props
 
-| 属性   | 类型   | 默认值 | 可选值           | 必传 | 说明                                    |
-| ------ | ------ | ------ | ---------------- | ---- | --------------------------------------- |
-| layout | String | 'v'    | 'v'/'h'/'inline' | N    | 排列方式,h-水平,v-垂直,inline-行内。 |
+| 名称         | 类型                    | 默认值      | 必传 | 说明                         |
+| ------------ | ----------------------- | ----------- | ---- | ---------------------------- |
+| data         | `CheckboxItem[]`        | `[]`        | Y    | 选项数据。                   |
+| layout       | `'h'\|'v'\|'inline'`    | `'v'`       | N    | 排列方式。                   |
+| checkeds     | `string[]`              | `[]`        | N    | 选中的选项标识符组成的数组。 |
+| textPosition | `'l'\|'r'\|'t'\|'b'`    | `'r'`       | N    | 选项文本区域位置。           |
+| icon         | `'default'\|null\|Icon` | `'default'` | N    | 未选中选项图标区域内容。     |
+| iconChecked  | `'default'\|null\|Icon` | `'default'` | N    | 选中选项图标区域内容。       |
 
-## CheckboxGroup Events
+## Checkbox Events
 
-| 名称   | 参数                                                      | 描述                   |
-| ------ | --------------------------------------------------------- | ---------------------- |
-| change | event:事件对象,其中 detail 表示选定项标识符组成的数组。 | 点击 Checkbox 时触发。 |
+| 名称     | 类型                           | 参数                                | 描述             |
+| -------- | ------------------------------ | ----------------------------------- | ---------------- |
+| onchange | `(checkeds: string[]) => void` | checkeds - 选定项标识符组成的数组。 | 点击选项时触发。 |
 
-## CheckboxGroup Slots
+## Checkbox Snippets
 
-| 名称 | 说明                                        |
-| ---- | ------------------------------------------- |
-| -    | CheckboxGroup 内部插槽一般为多条 Checkbox。 |
+| 名称          | 类型                                | 参数              | 说明                     |
+| ------------- | ----------------------------------- | ----------------- | ------------------------ |
+| checkboxChild | `Snippet<[{ item: CheckboxItem }]>` | item - 选项数据。 | 当选项区域自定义时使用。 |
 
-## Checkbox Props
+## CheckboxItem Props
+
+| 名称         | 类型                    | 默认值      | 必传 | 说明                     |
+| ------------ | ----------------------- | ----------- | ---- | ------------------------ |
+| name         | `string`                | `''`        | Y    | 标识符。                 |
+| label        | `string`                | `''`        | N    | 选项文本,主要用于显示。 |
+| layout       | `'h'\|'v'\|'inline'`    | `'v'`       | N    | 排列方式。               |
+| textPosition | `'l'\|'r'\|'t'\|'b'`    | `'r'`       | N    | 选项文本区域位置。       |
+| icon         | `'default'\|null\|Icon` | `'default'` | N    | 未选中选项图标区域内容。 |
+| iconChecked  | `'default'\|null\|Icon` | `'default'` | N    | 选中选项图标区域内容。   |
+| checked      | `boolean`               | `false`     | N    | 是否选中。               |
+
+## CheckboxItem Events
 
-| 属性         | 类型          | 默认值    | 可选值                      | 必传 | 说明                                                         |
-| ------------ | ------------- | --------- | --------------------------- | ---- | ------------------------------------------------------------ |
-| name         | String        | ''        | -                           | Y    | 标识符。                                                     |
-| checkeds     | Array         | []        | -                           | N    | Checkbox 所在的 CheckboxGroup 内所有选定项标识符组成的数组。 |
-| textPosition | String        | 'r'       | 't'/'b'/'l'/'r'             | N    | 选项内容区域位置。                                           |
-| icon         | String/Object | 'default' | 'default'/'none'/Icon Props | N    | 未选中选项图标区域内容。                                     |
-| iconChecked  | String/Object | 'default' | 'default'/'none'/Icon Props | N    | 选中选项图标区域内容。                                       |
-| outControl   | Boolean       | false     | true/false                  | N    | 选中状态是否由外部控制。                                     |
+| 名称    | 类型                     | 参数            | 描述             |
+| ------- | ------------------------ | --------------- | ---------------- |
+| onclick | `(name: string) => void` | name - 标识符。 | 点击选项时触发。 |
 
-## Checkbox Slots
+## CheckboxItem Snippets
 
-| 名称 | 说明           |
-| ---- | -------------- |
-| -    | 选项区域内容。 |
+| 名称     | 类型      | 参数 | 说明               |
+| -------- | --------- | ---- | ------------------ |
+| children | `Snippet` | -    | 选项文本区域内容。 |

+ 37 - 25
doc/components/checkbox/api_en.md

@@ -1,34 +1,46 @@
-## CheckboxGroup Props
+## Checkbox Props
 
-| Property | Type   | Default | Options          | Required | Description                                                                  |
-| -------- | ------ | ------- | ---------------- | -------- | ---------------------------------------------------------------------------- |
-| layout   | String | 'v'     | 'v'/'h'/'inline' | No       | The arrangement of the checkboxes, v-vertical, h-horizontal, inline-in line. |
+| Name         | Type                    | Default     | Required | Description                            |
+| ------------ | ----------------------- | ----------- | -------- | -------------------------------------- |
+| data         | `CheckboxItem[]`        | `[]`        | Y        | Option data.                           |
+| layout       | `'h'\|'v'\|'inline'`    | `'v'`       | N        | Layout mode.                           |
+| checkeds     | `string[]`              | `[]`        | N        | Array of selected option identifiers.  |
+| textPosition | `'l'\|'r'\|'t'\|'b'`    | `'r'`       | N        | Position of option text area.          |
+| icon         | `'default'\|null\|Icon` | `'default'` | N        | Content of unchecked option icon area. |
+| iconChecked  | `'default'\|null\|Icon` | `'default'` | N        | Content of checked option icon area.   |
 
-## CheckboxGroup Events
+## Checkbox Events
 
-| Event  | Parameters                                                                                                         | Description                           |
-| ------ | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------- |
-| change | event: the event object, including the detail property which represents an array of selected checkbox identifiers. | Triggered when a checkbox is clicked. |
+| Name     | Type                           | Parameters                                | Description                    |
+| -------- | ------------------------------ | ----------------------------------------- | ------------------------------ |
+| onchange | `(checkeds: string[]) => void` | checkeds - Array of selected identifiers. | Triggered when option clicked. |
 
-## CheckboxGroup Slots
+## Checkbox Snippets
 
-| Name | Description                                                                |
-| ---- | -------------------------------------------------------------------------- |
-| -    | The interior slot of CheckboxGroup generally contains multiple Checkboxes. |
+| Name          | Type                                | Parameters          | Description                        |
+| ------------- | ----------------------------------- | ------------------- | ---------------------------------- |
+| checkboxChild | `Snippet<[{ item: CheckboxItem }]>` | item - Option data. | Used when customizing option area. |
 
-## Checkbox Props
+## CheckboxItem Props
+
+| Name         | Type                    | Default     | Required | Description                            |
+| ------------ | ----------------------- | ----------- | -------- | -------------------------------------- |
+| name         | `string`                | `''`        | Y        | Identifier.                            |
+| label        | `string`                | `''`        | N        | Option text, mainly for display.       |
+| layout       | `'h'\|'v'\|'inline'`    | `'v'`       | N        | Layout mode.                           |
+| textPosition | `'l'\|'r'\|'t'\|'b'`    | `'r'`       | N        | Position of option text area.          |
+| icon         | `'default'\|null\|Icon` | `'default'` | N        | Content of unchecked option icon area. |
+| iconChecked  | `'default'\|null\|Icon` | `'default'` | N        | Content of checked option icon area.   |
+| checked      | `boolean`               | `false`     | N        | Whether checked.                       |
+
+## CheckboxItem Events
 
-| Property     | Type          | Default   | Options                     | Required | Description                                                         |
-| ------------ | ------------- | --------- | --------------------------- | -------- | ------------------------------------------------------------------- |
-| name         | String        | ''        | -                           | Y        | Identifier.                                                         |
-| checkeds     | Array         | []        | -                           | N        | An array of selected checkbox identifiers within the CheckboxGroup. |
-| textPosition | String        | 'r'       | 't'/'b'/'l'/'r'             | N        | The position of the option content area.                            |
-| icon         | String/Object | 'default' | 'default'/'none'/Icon Props | N        | The content of the unselected option icon area.                     |
-| iconChecked  | String/Object | 'default' | 'default'/'none'/Icon Props | N        | The content of the selected option icon area.                       |
-| outControl   | Boolean       | false     | true/false                  | N        | Whether the selection state is controlled externally.               |
+| Name    | Type                     | Parameters         | Description                    |
+| ------- | ------------------------ | ------------------ | ------------------------------ |
+| onclick | `(name: string) => void` | name - Identifier. | Triggered when option clicked. |
 
-## Checkbox Slots
+## CheckboxItem Snippets
 
-| Name | Description          |
-| ---- | -------------------- |
-| -    | Option content area. |
+| Name     | Type      | Parameters | Description                  |
+| -------- | --------- | ---------- | ---------------------------- |
+| children | `Snippet` | -          | Content of option text area. |

+ 6 - 12
doc/components/checkbox/guide.md

@@ -2,26 +2,20 @@
 
 ## 说明
 
-由于移动端横向显示内容有限,CheckboxGroup 默认为纵向布局,横向布局建议只用作简单使用。当选项较多、内容较长或显示内容较多时建议使用纵向布局,当 Checkbox 被点击时,CheckboxGroup 通过 change 事件,可以获取到当前所有选定 Checkbox 的 name 组成的数组。
+由于移动端横向显示内容有限,Checkbox 默认为纵向布局,横向布局建议只用作简单使用。当选项较多、内容较长或显示内容较多时建议使用纵向布局,当 Checkbox 内的选项被点击时,通过 onchange 事件,可以获取到当前所有选定项标识符 name 组成的数组。
 
 ## icon && iconChecked
 
-Checkbox Props 的 icon 默认值 'default' 会按照 Remix Icon 的 ri-checkbox-line 以 Icon 组件形式渲染;传入字符 'none',表示未选中选项图标区域不显示内容;也可以传入 Icon Props,未选中选项图标区域将会以传入的参数以 Icon 组件形式渲染。
+Checkbox Props 的 icon 默认值 `'default'` 会按照 Remix Icon 的 checkbox-line 以 Icon 组件形式渲染;传入字符 `null`,表示未选中选项图标区域不显示内容;也可以传入 Icon Props,未选中选项图标区域将会以传入的参数以 Icon 组件形式渲染。
 
 iconChecked 与 icon 逻辑一样,对应的是选中选项的图标区域内容。
 
-## Checkbox Slots
+## Checkbox Snippet
 
-Checkbox 的插槽将会渲染在每条 Checkbox 的内容区域,可结合 CheckboxGroup change 事件获取到的 checkeds,自由设计选中与未选中的效果
+当传入 checkboxChild 这个 Snippet 时,选项区域将会以传入的 Snippet 内容渲染,可自由设计选中与未选中的效果,获取数据逻辑等
 
-注:虽然有 Tailwind 写样式比较方便,但过多的自定义将会失去使用组件库的意义。
+> 虽然有 Tailwind 写样式比较方便,但过多的自定义将会失去使用组件库的意义。
 
 ## 点击区域优化
 
-为了优化体验,CheckboxGroup 整个区域都可以点击,将会激活与之对应的 Checkbox,不仅仅只可以点击 Checkbox 的文字或图标区域。
-
-## checkeds && outControl
-
-Checkbox 通过 checkeds 传入一组值初始控制 CheckboxGroup 内部所有选项是否已选定。
-
-outControl 常规使用时是不需要传入这个参数的,但是在做一些全选、全不选、反选等操作时,由于单项是否选中不是由点击单项本身来决定的,而是由外部事件决定的,就需要传入 outControl,结合 checkeds 用于控制单项内部的界面效果;另一方面,虽然 Checkbox 状态可以由外部控制,但单项 Checkbox 自身也是可以点击选定与取消的,所以就需要 outControl 这个动态值来通知 Checkbox,界面效果到底是由外部控制还是由自身点击控制。具体使用可以参考示例。
+为了优化体验,CheckboxItem 整个区域都可以点击,将会激活与之对应的选项,不仅仅只可以点击文字或图标区域。

+ 10 - 16
doc/components/checkbox/guide_en.md

@@ -1,27 +1,21 @@
-> Checkbox uses the icons ri-checkbox-fill and ri-checkbox-line, please ensure that the symbol.svg file in your project includes these icons. For details, please refer to [STDF-Guide-Icon](https://stdf.design/#/guide/icon).
+> Checkbox uses icons ri-checkbox-fill and ri-checkbox-line. Please ensure these icons are included in your project's symbol.svg file. For more details, please refer to [STDF-Guide-Icon](https://stdf.design/#/guide/icon).
 
 ## Description
 
-Due to limited space for horizontal display on mobile devices, CheckboxGroup is set to vertical layout by default. Horizontal layout should only be used for simple usage. When there are many options, long content, or much display content, vertical layout is recommended. When a checkbox is clicked, CheckboxGroup can obtain an array of names of all currently selected checkboxes through the change event.
+Due to limited horizontal display space on mobile devices, Checkbox defaults to vertical layout, with horizontal layout recommended only for simple use cases. When there are multiple options, long content, or rich display content, vertical layout is recommended. When an option within the Checkbox is clicked, through the onchange event, you can get an array of name identifiers for all currently selected items.
 
-## icon and iconChecked
+## icon && iconChecked
 
-The default value of the icon prop of Checkbox props is 'default', which will render as an Icon component according to ri-checkbox-line of Remix Icon. Passing the character 'none' means that the unselected option icon area does not display content. You can also pass the Icon props, and the unselected option icon area will be rendered as an Icon component according to the passed parameters.
+The default value `'default'` for Checkbox Props' icon will render as an Icon component following Remix Icon's checkbox-line style; passing `null` means no content will be displayed in the unchecked option's icon area; you can also pass Icon Props, and the unchecked option's icon area will be rendered as an Icon component with the passed parameters.
 
-The logic of iconChecked is the same as that of icon, which corresponds to the content of the selected option icon area.
+iconChecked follows the same logic as icon, corresponding to the icon area content for checked options.
 
-## Checkbox Slots
+## Checkbox Snippet
 
-The slot of Checkbox will be rendered in the content area of each Checkbox, and you can design the selected and unselected effects freely by combining with the checkeds obtained from the CheckboxGroup change event.
+When passing the checkboxChild Snippet, the option area will be rendered with the passed Snippet content, allowing free design of checked and unchecked effects, data logic handling, etc.
 
-Note: Although it is convenient to write styles with Tailwind, too much customization will lose the meaning of using the component library.
+> Although Tailwind makes styling convenient, too much customization will defeat the purpose of using a component library.
 
-## Click area optimization
+## Click Area Optimization
 
-To optimize the experience, the entire area of the CheckboxGroup can be clicked, which will activate the corresponding Checkbox, not just the text or icon area of the Checkbox.
-
-## checkeds and outControl
-
-Checkbox controls whether all options inside the CheckboxGroup are selected by passing in a set of values through checkeds.
-
-When performing operations such as select all, deselect all, or reverse selection, outControl needs to be passed in. Because whether an individual option is selected is not determined by clicking the individual option itself, but by external events, outControl is needed to notify Checkbox of whether the visual effect is controlled by external or self-clicking. Although the Checkbox state can be controlled externally, the individual Checkbox can also be selected and canceled by clicking. Therefore, outControl is needed as a dynamic value to notify Checkbox of whether the visual effect is controlled by external or self-clicking. Refer to the example for specific usage.
+To optimize the user experience, the entire CheckboxItem area is clickable and will activate its corresponding option, not just the text or icon areas.

+ 24 - 76
packages/stdf/components/checkbox/Checkbox.svelte

@@ -1,85 +1,33 @@
 <script>
-	import { getContext } from 'svelte';
-	import Icon from '../icon/Icon.svelte';
+	import CheckboxItem from './CheckboxItem.svelte';
 
-	/** @typedef {import('../../index.d').Checkbox} CheckboxProps */
+	/** @typedef {import('../../index.js').Checkbox} CheckboxProps */
 	/** @type {CheckboxProps} */
-	let { name = '', checkeds = [], outControl = false, textPosition = 'r', icon = 'default', iconChecked = 'default', children } = $props();
-
-	// 父组件传过来的选定值
-	// Selected value passed from parent component
-	const STDF_checkboxCheckedsStore = getContext('STDF_checkboxCheckedsContext');
-
-	// 设定当前选定值
-	// Set current selected value
-	STDF_checkboxCheckedsStore.set(checkeds);
-
-	// 当前选定值
-	// Current selected value
-	let currentChecked = $derived(outControl ? checkeds.includes(name) : $STDF_checkboxCheckedsStore.includes(name));
-
-	// 父组件传过来的排列方式
-	// Layout type passed from parent component
-
-	const STDF_checkboxLayoutStore = getContext('STDF_checkboxLayoutContext');
-
-	// 点击选项事件
-	// Click option event
-	const clickRadioFun = () => {
-		outControl && STDF_checkboxCheckedsStore.set([...checkeds]);
-		if ($STDF_checkboxCheckedsStore.includes(name)) {
-			const currentCheckeds = $STDF_checkboxCheckedsStore;
-			currentCheckeds.splice(
-				currentCheckeds.findIndex(item => item === name),
-				1,
-			);
-			STDF_checkboxCheckedsStore.set(currentCheckeds);
-		} else {
-			STDF_checkboxCheckedsStore.update(olds => [...olds, name]);
-		}
+	let {
+		data = [],
+		layout = 'v',
+		checkeds = $bindable([]),
+		textPosition = 'r',
+		icon = 'default',
+		iconChecked = 'default',
+		checkboxChild,
+		onchange,
+	} = $props();
+
+	const clickItemFn = name => {
+		checkeds = checkeds.includes(name) ? checkeds.filter(v => v !== name) : [name, ...checkeds];
+		onchange && onchange(checkeds);
 	};
 </script>
 
-<!-- svelte-ignore a11y_click_events_have_key_events -->
-<!-- svelte-ignore a11y_no_static_element_interactions -->
-<div
-	class={`${$STDF_checkboxLayoutStore === 'inline' ? 'inline-block' : 'flex'} grow active:opacity-80 ${
-		textPosition === 'l' && $STDF_checkboxLayoutStore != 'v' ? 'justify-between' : ''
-	} ${
-		textPosition === 'b' || textPosition === 't'
-			? 'flex-col items-center'
-			: $STDF_checkboxLayoutStore === 'h'
-				? 'justify-center'
-				: 'items-center'
-	}`}
-	onclick={clickRadioFun}
->
-	{#if textPosition === 'l' || textPosition === 't'}
-		<div class={`${textPosition === 'l' && icon !== 'none' ? 'mr-0.5' : ''} ${$STDF_checkboxLayoutStore === 'v' ? 'grow' : ''}`}>
-			{@render children?.()}
-		</div>
-	{/if}
-	<div class:hidden={!currentChecked}>
-		{#if iconChecked === 'none'}
-			<!-- none -->
-		{:else if iconChecked === 'default'}
-			<Icon name="ri-checkbox-fill" theme top={textPosition === 't' || textPosition === 'b' ? 0 : -1} />
-		{:else}
-			<Icon {...iconChecked} theme />
-		{/if}
-	</div>
-	<div class:hidden={currentChecked}>
-		{#if icon === 'none'}
-			<!-- none -->
-		{:else if icon === 'default'}
-			<Icon name="ri-checkbox-line" alpha={0.1} top={textPosition === 't' || textPosition === 'b' ? 0 : -1} />
+<div class={layout === 'inline' ? 'flex flex-wrap' : `flex ${layout === 'h' ? 'justify-between' : 'flex-col space-y-2 w-full'}`}>
+	{#each data as item}
+		{#if checkboxChild}
+			{@render checkboxChild({ item })}
 		{:else}
-			<Icon alpha={0.1} {...icon} />
+			<CheckboxItem {layout} {...item} {textPosition} {icon} {iconChecked} checked={checkeds.includes(item.name)} onclick={clickItemFn}>
+				{item.label}
+			</CheckboxItem>
 		{/if}
-	</div>
-	{#if textPosition === 'r' || textPosition === 'b'}
-		<div class={`${textPosition === 'r' && icon !== 'none' ? 'ml-0.5' : ''} ${$STDF_checkboxLayoutStore === 'v' ? 'grow' : ''}`}>
-			{@render children?.()}
-		</div>
-	{/if}
+	{/each}
 </div>

+ 0 - 31
packages/stdf/components/checkbox/CheckboxGroup.svelte

@@ -1,31 +0,0 @@
-<script>
-	import { setContext } from 'svelte';
-	import { writable } from 'svelte/store';
-
-	/** @typedef {import('../../index.d').CheckboxGroup} CheckboxGroupProps */
-	/** @type {CheckboxGroupProps} */
-	let { layout = 'v', children, onchange } = $props();
-
-	// 选中的值
-	// checked value
-	const STDF_checkboxCheckedsStore = writable([]);
-
-	// 排列方式
-	// layout type
-	const STDF_checkboxLayoutStore = writable(layout);
-
-	// 传递给子组件的选定值
-	// checked value passed to child component
-	setContext('STDF_checkboxCheckedsContext', STDF_checkboxCheckedsStore);
-	setContext('STDF_checkboxLayoutContext', STDF_checkboxLayoutStore);
-
-	// 监听排列方式变化
-	// Listen for layout type changes
-	STDF_checkboxCheckedsStore.subscribe(v => {
-		onchange && onchange(v);
-	});
-</script>
-
-<div class={`${layout === 'inline' ? '' : `flex ${layout === 'h' ? 'justify-between' : 'flex-col space-y-2'}`}`}>
-	{@render children?.()}
-</div>

+ 57 - 0
packages/stdf/components/checkbox/CheckboxItem.svelte

@@ -0,0 +1,57 @@
+<script>
+	import Icon from '../icon/Icon.svelte';
+
+	/** @typedef {import('../..').CheckboxItem} CheckboxItemProps */
+	/** @type {CheckboxItemProps} */
+	let {
+		name = '',
+		layout = 'v',
+		checked = false,
+		textPosition = 'r',
+		icon = 'default',
+		iconChecked = 'default',
+		children,
+		onclick,
+	} = $props();
+
+	// 点击选项事件
+	// Click option event
+	const clickRadioFun = () => {
+		onclick && onclick(name);
+	};
+</script>
+
+<button
+	onclick={() => clickRadioFun()}
+	class="{layout === 'inline' ? 'inline-block' : 'flex'} grow active:opacity-80 {textPosition === 'l' && layout === 'v'
+		? 'justify-between'
+		: ''} {textPosition === 'b' || textPosition === 't' ? 'flex-col items-center' : layout === 'h' ? 'justify-center' : 'items-center'}"
+>
+	{#if textPosition === 'l' || textPosition === 't'}
+		<div class="{textPosition === 'l' && icon !== null ? 'mr-0.5 text-left' : ''} {layout === 'v' ? 'grow' : ''}">
+			{@render children?.()}
+			{#if layout === 'v'}
+				<div class="h-px mt-1 bg-black/10 dark:bg-white/10"></div>
+			{/if}
+		</div>
+	{/if}
+	<div class:hidden={!checked}>
+		{#if iconChecked === null}{:else if iconChecked === 'default'}
+			<Icon name="ri-checkbox-fill" theme top={textPosition === 't' || textPosition === 'b' ? 0 : -1} />
+		{:else}
+			<Icon {...iconChecked} theme />
+		{/if}
+	</div>
+	<div class:hidden={checked}>
+		{#if icon === null}{:else if icon === 'default'}
+			<Icon name="ri-checkbox-line" alpha={0.2} top={textPosition === 't' || textPosition === 'b' ? 0 : -1} />
+		{:else}
+			<Icon alpha={0.2} {...icon} />
+		{/if}
+	</div>
+	{#if textPosition === 'r' || textPosition === 'b'}
+		<div class={textPosition === 'r' && icon !== null ? 'ml-0.5 text-left' : ''}>
+			{@render children?.()}
+		</div>
+	{/if}
+</button>