Prechádzať zdrojové kódy

[stdf]Update Radio compoent, demo and documents.

dufu1991 1 rok pred
rodič
commit
e211ca8091

+ 173 - 134
demo/src/routes/en_US/radio/+page.svelte

@@ -1,162 +1,201 @@
 <!-- Radio Demo -->
 <script>
-	import { RadioGroup, Radio, Divider, Button } from '../../../../../packages/stdf/components';
+	import { Radio, 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 value = dota[0].name;
-	const dotaFun = e => {
-		value = e.detail;
-	};
-	const dotaImgs = ['Lina', 'ES', 'Mor', 'Sni', 'SV'];
-	const dotaForImgs = ['火女', '小牛', '水人', '火枪', '斯温'];
-	let imgValue = dotaImgs[3];
-	const dotaImgsFun = e => {
-		imgValue = e.detail;
-	};
+	let value = $state(dota[0].name);
+	let checkedsCustom = $state('');
+
+	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 checkInline = $state('Morphling');
+
+	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 imgChecked = $state('Lina');
+
+	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 voiceValue = dotaVoices[0].name;
-	const dotaVoicesFun = e => {
-		voiceValue = e.detail;
-	};
+	let voiceChecked = $state('');
 </script>
 
 <div class="px-4">
-	<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>
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name}>{item.label}</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">The text is in different places</div>
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="l">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+	<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>
+	<Radio layout="h" data={dota} />
 	<Divider />
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="b">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+
+	<div class="mt-6 mb-4 font-bold text-lg">Get Selected Value</div>
+	<Radio layout="h" data={dota} bind:value />
+	<div class="mt-4 text-sm">Selected: {value}</div>
 	<Divider />
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="t">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">Custom icon</div>
-	<RadioGroup value="Juggernaut" horizontal>
-		<Radio icon={{ name: 'ri-checkbox-line' }} iconChecked={{ name: 'ri-checkbox-fill' }} name="Juggernaut">Jugg</Radio>
-		<Radio icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} name="Spirit Breaker">SB</Radio>
-		<Radio icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} name="Keeper of the Light"
-			>Kotl</Radio
-		>
-		<Radio icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} name="Magnus">Mag</Radio>
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">Picture option</div>
-	<RadioGroup value={imgValue} horizontal on:change={dotaImgsFun}>
-		{#each dotaImgs as dota, i}
-			<Radio name={dota} textPosition="t" icon={{ name: 'ri-arrow-up-s-line' }} iconChecked={{ name: 'ri-arrow-up-s-fill', size: 30 }}>
-				<div class={`w-12 h-12 mb-1 rounded overflow-hidden ${imgValue === 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>
-			</Radio>
-		{/each}
-	</RadioGroup>
-	<div class="text-sm">What, are you choosing again <span class="text-[red] font-bold text-lg">{imgValue}!!</span></div>
-
-	<div class="mt-8 mb-4 font-bold text-lg">No icon && use Button</div>
-	<RadioGroup {value} horizontal on:change={dotaFun}>
-		{#each dota as item}
-			<Radio name={item.name} icon="none" iconChecked="none">
-				<Button fill={value === item.name ? 'base' : 'lineLight'} heightIn="0">{item.label}</Button>
-			</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">No icon && Customize the selection effect</div>
-	<RadioGroup {value} horizontal on:change={dotaFun}>
-		{#each dota as item}
-			<Radio name={item.name} icon="none" iconChecked="none">
+
+	<div class="mt-8 mb-4 font-bold text-lg">Text Position</div>
+	<Radio layout="h" data={dota} textPosition="l" />
+	<Divider />
+	<Radio layout="h" data={dota} textPosition="b" />
+	<Divider />
+	<Radio layout="h" data={dota} textPosition="t" />
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Custom Icon</div>
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} />
+	<Divider />
+	<Radio 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">Snippet as Button Component</div>
+	<Radio layout="h" data={dota}>
+		{#snippet radioChild({ item })}
+			<Button fill={value === item.name ? 'base' : 'lineLight'} injClass="px-2 !py-1" onclick={() => (value = item.name)}>
+				{item.label}
+			</Button>
+		{/snippet}
+	</Radio>
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet as Custom Element</div>
+	<Radio layout="h" data={dota}>
+		{#snippet radioChild({ item })}
+			<button
+				class="border py-0.5 px-5 text-sm rounded {checkedsCustom === 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={() => (checkedsCustom = item.name)}
+			>
+				{item.label}
+			</button>
+		{/snippet}
+	</Radio>
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Image Options</div>
+	<Radio layout="h" data={dotaImgs}>
+		{#snippet radioChild({ 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 ${
-						value === 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 {imgChecked === item.name ? 'ring-2 ring-primary dark:ring-dark' : ''}"
+					onclick={() => (imgChecked = item.name)}
 				>
-					{item.label}
+					<img class="w-full h-full object-cover" src={`/assets/images/dota_${item.imgName}.png`} alt="" />
+				</div>
+				<div>
+					<Icon
+						name={imgChecked === item.name ? 'ri-arrow-up-s-fill' : 'ri-arrow-up-s-line'}
+						theme={imgChecked === item.name}
+						alpha={imgChecked === item.name ? 1 : 0.2}
+					/>
 				</div>
-			</Radio>
-		{/each}
-	</RadioGroup>
+			</div>
+		{/snippet}
+	</Radio>
+	<div class="text-sm">
+		Selected <span class="text-[red]">{imgChecked}</span>
+	</div>
+	<Divider />
+</div>
+
+<div class="px-4">
+	<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>
+	<Radio layout="inline" data={dotaInlines}>
+		{#snippet radioChild({ 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 {checkInline === 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={() => (checkInline = item.name)}
+			>
+				{item.label}
+			</div>
+		{/snippet}
+	</Radio>
+	<div class="text-xs mt-2">Selected: {checkInline}</div>
 </div>
 <Divider />
+
 <div class="px-4 pb-8">
-	<div class="mb-4 font-bold text-2xl">Longitudinal arrangement</div>
-	<div class="mt-8 mb-4 font-bold text-lg">Basic usage</div>
-	<RadioGroup {value}>
-		{#each dota as item}
-			<Radio name={item.name}>{item.label}</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">Long text option</div>
-	<RadioGroup {value}>
-		{#each dota as item}
-			<Radio name={item.name}>{item.desc}</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">Text to the left</div>
-	<RadioGroup {value}>
-		{#each dota as item, i}
-			<Radio name={item.name} textPosition="l">
-				{item.label}
-				{#if i !== dota.length - 1}
-					<div class="h-px mt-1 bg-gray-100 dark:bg-gray-800" />
-				{/if}
-			</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">Complex option && Customize the selection effect</div>
-	<RadioGroup value={voiceValue} on:change={dotaVoicesFun}>
-		{#each dotaVoices as item, i}
-			<Radio name={item.name}>
-				<div class={`ml-2 ${voiceValue === item.name ? 'text-primary dark:text-dark' : ''}`}>
+	<div class="mb-4 font-bold text-2xl">Vertical Layout</div>
+	<div class="mt-8 mb-4 font-bold text-lg">Basic Usage</div>
+	<Radio data={dota} />
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Long Text Options</div>
+	<Radio data={dotaLong} />
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Text Position</div>
+	<Radio data={dota} textPosition="l" />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Complex Options && Custom Selected Effect</div>
+	<Radio data={dotaVoices}>
+		{#snippet radioChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div class="flex items-center cursor-pointer" onclick={() => (voiceChecked = item.name)}>
+				<div>
+					<Icon
+						name={voiceChecked === item.name ? 'ri-radio-button-line' : 'ri-checkbox-blank-circle-line'}
+						theme={voiceChecked === item.name}
+						alpha={voiceChecked === item.name ? 1 : 0.2}
+					/>
+				</div>
+				<div class="ml-2 grow {voiceChecked === 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-100 dark:bg-gray-800" />
-					{/if}
+					<div class="h-px mt-1 bg-black/10 dark:bg-white/10"></div>
 				</div>
-			</Radio>
-		{/each}
-	</RadioGroup>
+			</div>
+		{/snippet}
+	</Radio>
 </div>

+ 153 - 111
demo/src/routes/zh_CN/radio/+page.svelte

@@ -1,155 +1,197 @@
 <!-- Radio Demo -->
 <script>
-	import { RadioGroup, Radio, Divider, Button } from '../../../../../packages/stdf/components';
+	import { Radio, Divider, Button, Icon } from '../../../../../packages/stdf/components';
 
 	const dota = [
-		{ label: '主宰', name: '奶棒人', desc: '主宰是一个近战敏捷英雄,他能够迅速切入战斗。' },
-		{ label: '白牛', name: '令狐冲', desc: '巴拉森,裂魂人是一个强大的 Gank 型的力量型近战英雄。' },
-		{ label: '光法', name: '光之守卫', desc: '伊扎洛,光之守卫,他是一个著名的辅助性智力英雄。' },
-		{ label: '猛犸', name: '马格纳斯', desc: '猛犸?为什么不 BAN 猛犸?为什么不 BAN 猛犸?' },
+		{ label: '主宰', name: '奶棒人' },
+		{ label: '白牛', name: '令狐冲' },
+		{ label: '光法', name: '光之守卫' },
+		{ label: '猛犸', name: '马格纳斯' },
 	];
-	let value = dota[0].name;
-	const dotaFun = e => {
-		value = e.detail;
-	};
-	const dotaImgs = ['火女', '小牛', '水人', '火枪', '斯温'];
-	let imgValue = dotaImgs[3];
-	const dotaImgsFun = e => {
-		imgValue = e.detail;
-	};
+	let value = $state(dota[0].name);
+	let checkedsCustom = $state('');
+
+	const dotaInlines = [
+		{ label: '火女', name: '火女' },
+		{ label: '小牛', name: '小牛' },
+		{ label: '水人', name: '水人' },
+		{ label: '火枪', name: '火枪' },
+		{ label: '斯温', name: '斯温' },
+		{ label: '祈求者', name: '祈求者' },
+		{ label: '潮汐', name: '潮汐' },
+		{ label: '蝙蝠', name: '蝙蝠' },
+		{ label: '猛犸', name: '猛犸' },
+	];
+	let checkInline = $state('小牛');
+
+	const dotaImgs = [
+		{ label: '火女', name: '火女' },
+		{ label: '小牛', name: '小牛' },
+		{ label: '水人', name: '水人' },
+		{ label: '火枪', name: '火枪' },
+		{ label: '斯温', name: '斯温' },
+	];
+	let imgChecked = $state('火女');
+
+	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 voiceValue = dotaVoices[0].name;
-	const dotaVoicesFun = e => {
-		voiceValue = e.detail;
-	};
+	let voiceChecked = $state('');
 </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>
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name}>{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio layout="h" data={dota} />
+	<Divider />
+
+	<div class="mt-6 mb-4 font-bold text-lg">获取选中值</div>
+	<Radio layout="h" data={dota} bind:value />
+	<div class="mt-4 text-sm">已选:{value}</div>
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">文字在不同位置</div>
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="l">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio layout="h" data={dota} textPosition="l" />
+	<Divider />
+	<Radio layout="h" data={dota} textPosition="b" />
 	<Divider />
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="b">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio layout="h" data={dota} textPosition="t" />
 	<Divider />
-	<RadioGroup {value} horizontal>
-		{#each dota as item}
-			<Radio name={item.name} textPosition="t">{item.label}</Radio>
-		{/each}
-	</RadioGroup>
 
 	<div class="mt-8 mb-4 font-bold text-lg">自定义图标</div>
-	<RadioGroup value="奶棒人" horizontal>
-		<Radio icon={{ name: 'ri-checkbox-line' }} iconChecked={{ name: 'ri-checkbox-fill' }} name="奶棒人">主宰</Radio>
-		<Radio icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} name="令狐冲">白牛</Radio>
-		<Radio icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} name="光之守卫">光法</Radio>
-		<Radio icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} name="马格纳斯">猛犸</Radio>
-	</RadioGroup>
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-blank-circle-line' }} iconChecked={{ name: 'ri-radio-button-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-circle-line' }} iconChecked={{ name: 'ri-checkbox-circle-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-checkbox-multiple-line' }} iconChecked={{ name: 'ri-checkbox-multiple-fill' }} />
+	<Divider />
+	<Radio layout="h" data={dota} icon={{ name: 'ri-check-line' }} iconChecked={{ name: 'ri-check-fill' }} />
+	<Divider />
+	<Radio 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">Snippet 为 Button 组件</div>
+	<Radio layout="h" data={dota}>
+		{#snippet radioChild({ item })}
+			<Button fill={value === item.name ? 'base' : 'lineLight'} injClass="px-2 !py-1" onclick={() => (value = item.name)}>
+				{item.label}
+			</Button>
+		{/snippet}
+	</Radio>
+	<Divider />
+
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet 为自定义元素</div>
+	<Radio layout="h" data={dota}>
+		{#snippet radioChild({ item })}
+			<button
+				class="border py-0.5 px-5 text-sm rounded {checkedsCustom === 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={() => (checkedsCustom = item.name)}
+			>
+				{item.label}
+			</button>
+		{/snippet}
+	</Radio>
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">图片选项</div>
-	<RadioGroup value={imgValue} horizontal on:change={dotaImgsFun}>
-		{#each dotaImgs as dota}
-			<Radio name={dota} textPosition="t" icon={{ name: 'ri-arrow-up-s-line' }} iconChecked={{ name: 'ri-arrow-up-s-fill', size: 30 }}>
-				<div class={`w-12 h-12 mb-1 rounded overflow-hidden ${imgValue === dota && 'ring-2 ring-primary dark:ring-dark'}`}>
-					<img class="w-full h-full object-cover" src={`/assets/images/dota_${dota}.png`} alt="" />
-				</div>
-			</Radio>
-		{/each}
-	</RadioGroup>
-	<div class="text-sm">什么,你又要选 <span class="text-[red] font-bold text-lg">{imgValue}!!</span></div>
-
-	<div class="mt-8 mb-4 font-bold text-lg">不用图标 && 使用 Button</div>
-	<RadioGroup {value} horizontal on:change={dotaFun}>
-		{#each dota as item}
-			<Radio name={item.name} icon="none" iconChecked="none">
-				<Button fill={value === item.name ? 'base' : 'lineLight'} heightIn="0">{item.label}</Button>
-			</Radio>
-		{/each}
-	</RadioGroup>
-
-	<div class="mt-8 mb-4 font-bold text-lg">不用图标 && 自定义选中效果</div>
-	<RadioGroup {value} horizontal on:change={dotaFun}>
-		{#each dota as item}
-			<Radio name={item.name} icon="none" iconChecked="none">
+	<Radio layout="h" data={dotaImgs}>
+		{#snippet radioChild({ 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 ${
-						value === 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 {imgChecked === item.name ? 'ring-2 ring-primary dark:ring-dark' : ''}"
+					onclick={() => (imgChecked = item.name)}
 				>
-					{item.label}
+					<img class="w-full h-full object-cover" src={`/assets/images/dota_${item.name}.png`} alt="" />
 				</div>
-			</Radio>
-		{/each}
-	</RadioGroup>
+				<div>
+					<Icon
+						name={imgChecked === item.name ? 'ri-arrow-up-s-fill' : 'ri-arrow-up-s-line'}
+						theme={imgChecked === item.name}
+						alpha={imgChecked === item.name ? 1 : 0.2}
+					/>
+				</div>
+			</div>
+		{/snippet}
+	</Radio>
+	<div class="text-sm">
+		已选 <span class="text-[red]">{imgChecked}</span>
+	</div>
+	<Divider />
+</div>
+
+<div class="px-4">
+	<div class="mb-4 font-bold text-2xl">行内元素排列</div>
+	<div class="mt-8 mb-4 font-bold text-lg">Snippet 行内元素排列</div>
+	<Radio layout="inline" data={dotaInlines}>
+		{#snippet radioChild({ 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 {checkInline === 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={() => (checkInline = item.name)}
+			>
+				{item.label}
+			</div>
+		{/snippet}
+	</Radio>
+	<div class="text-xs mt-2">已选:{checkInline}</div>
 </div>
 <Divider />
+
 <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>
-	<RadioGroup {value}>
-		{#each dota as item}
-			<Radio name={item.name}>{item.label}</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio data={dota} />
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">长文字选项</div>
-	<RadioGroup {value}>
-		{#each dota as item}
-			<Radio name={item.name}>{item.desc}</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio data={dotaLong} />
+	<Divider />
 
 	<div class="mt-8 mb-4 font-bold text-lg">文字靠左</div>
-	<RadioGroup {value}>
-		{#each dota as item, i}
-			<Radio name={item.name} textPosition="l">
-				{item.label}
-				{#if i !== dota.length - 1}
-					<div class="h-px mt-1 bg-gray-100 dark:bg-gray-800" />
-				{/if}
-			</Radio>
-		{/each}
-	</RadioGroup>
+	<Radio data={dota} textPosition="l" />
 
 	<div class="mt-8 mb-4 font-bold text-lg">复杂选项 && 自定义选中效果</div>
-	<RadioGroup value={voiceValue} on:change={dotaVoicesFun}>
-		{#each dotaVoices as item, i}
-			<Radio name={item.name}>
-				<div class={`ml-2 ${voiceValue === item.name ? 'text-primary dark:text-dark' : ''}`}>
+	<Radio data={dotaVoices}>
+		{#snippet radioChild({ item })}
+			<!-- svelte-ignore a11y_click_events_have_key_events -->
+			<!-- svelte-ignore a11y_no_static_element_interactions -->
+			<div class="flex items-center cursor-pointer" onclick={() => (voiceChecked = item.name)}>
+				<div>
+					<Icon
+						name={voiceChecked === item.name ? 'ri-radio-button-line' : 'ri-checkbox-blank-circle-line'}
+						theme={voiceChecked === item.name}
+						alpha={voiceChecked === item.name ? 1 : 0.2}
+					/>
+				</div>
+				<div class="ml-2 grow {voiceChecked === 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-100 dark:bg-gray-800" />
-					{/if}
+					<div class="h-px mt-1 bg-black/10 dark:bg-white/10"></div>
 				</div>
-			</Radio>
-		{/each}
-	</RadioGroup>
+			</div>
+		{/snippet}
+	</Radio>
 </div>

+ 37 - 24
doc/components/radio/api.md

@@ -1,33 +1,46 @@
-## RadioGroup Props
+## Radio Props
 
-| 属性       | 类型    | 默认值 | 可选值     | 必传 | 说明                                     |
-| ---------- | ------- | ------ | ---------- | ---- | ---------------------------------------- |
-| value      | String  | ''     | -          | Y    | 选定的 value,对应 Radio Props 的 name。 |
-| horizontal | Boolean | false  | true/false | N    | 是否水平排列。                           |
+| 名称         | 类型                                                                         | 默认值      | 必传 | 说明                     |
+| ------------ | ---------------------------------------------------------------------------- | ----------- | ---- | ------------------------ |
+| data         | `RadioItem[]`                                                                | `[]`        | Y    | 选项数据。               |
+| layout       | `'v'\|'h'\|'inline'`                                                         | `'v'`       | N    | 排列方式。               |
+| value        | `string`                                                                     | `''`        | N    | 选中的 value。           |
+| textPosition | `'l'\|'r'\|'t'\|'b'`                                                         | `'r'`       | N    | 选项内容区域位置。       |
+| icon         | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N    | 未选中选项图标区域内容。 |
+| iconChecked  | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N    | 选中选项图标区域内容。   |
 
-## RadioGroup Events
+## Radio Events
 
-| 名称   | 参数                                                   | 描述                                                     |
-| ------ | ------------------------------------------------------ | -------------------------------------------------------- |
-| change | event:事件对象,其中 detail 表示选中的 Radio 标识符。 | 点击 Radio(即 RadioGroup Props 的 value 变化) 时触发。 |
+| 名称     | 类型                      | 参数                        | 描述                                             |
+| -------- | ------------------------- | --------------------------- | ------------------------------------------------ |
+| onchange | `(value: string) => void` | value - 选中的 Radio 标识符 | 点击 RadioItem(即 Radio 的 value 变化)时触发。 |
 
-## RadioGroup Slots
+## Radio Snippets
 
-| 名称 | 说明                                  |
-| ---- | ------------------------------------- |
-| -    | RadioGroup 内部插槽一般为多条 Radio。 |
+| 名称       | 类型                                                                                       | 参数            | 说明                     |
+| ---------- | ------------------------------------------------------------------------------------------ | --------------- | ------------------------ |
+| radioChild | [`Snippet<[{ item: RadioItem }]>`](https://svelte.dev/docs/svelte/snippet#Typing-snippets) | item - 选项数据 | 当选项区域自定义时使用。 |
 
-## Radio Props
+## RadioItem Props
+
+| 名称         | 类型                                                                         | 默认值      | 必传 | 说明                                |
+| ------------ | ---------------------------------------------------------------------------- | ----------- | ---- | ----------------------------------- |
+| name         | `string`                                                                     | `''`        | Y    | 标识符,对应 Radio Props 的 value。 |
+| label        | `string`                                                                     | `''`        | N    | 选项内容。                          |
+| layout       | `'v'\|'h'\|'inline'`                                                         | `'v'`       | N    | 排列方式。                          |
+| textPosition | `'l'\|'r'\|'t'\|'b'`                                                         | `'r'`       | N    | 选项内容区域位置。                  |
+| icon         | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N    | 未选中选项图标区域内容。            |
+| iconChecked  | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N    | 选中选项图标区域内容。              |
+| checked      | `boolean`                                                                    | `false`     | N    | 是否选中。                          |
+
+## RadioItem Events
 
-| 属性         | 类型          | 默认值    | 可选值                      | 必传 | 说明                                     |
-| ------------ | ------------- | --------- | --------------------------- | ---- | ---------------------------------------- |
-| name         | String        | ''        | -                           | Y    | 标识符,对应 RadioGroup Props 的 value。 |
-| 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    | 选中选项图标区域内容。                   |
+| 名称    | 类型                     | 参数                | 说明         |
+| ------- | ------------------------ | ------------------- | ------------ |
+| onclick | `(name: string) => void` | name - 点击项标识符 | 点击时触发。 |
 
-## Radio Slots
+## RadioItem Snippets
 
-| 名称 | 说明           |
-| ---- | -------------- |
-| -    | 选项区域内容。 |
+| 名称     | 类型                                                                | 参数 | 说明           |
+| -------- | ------------------------------------------------------------------- | ---- | -------------- |
+| children | [`Snippet`](https://svelte.dev/docs/svelte/snippet#Typing-snippets) | -    | 选项区域内容。 |

+ 37 - 24
doc/components/radio/api_en.md

@@ -1,33 +1,46 @@
-## RadioGroup Props
+## Radio Props
 
-| Property   | Type    | Default | Options    | Required | Description                                                            |
-| ---------- | ------- | ------- | ---------- | -------- | ---------------------------------------------------------------------- |
-| value      | String  | ''      | -          | Y        | The selected value, corresponding to the name property of Radio Props. |
-| horizontal | Boolean | false   | true/false | N        | Whether to display the radio buttons horizontally.                     |
+| Name         | Type                                                                         | Default     | Required | Description                            |
+| ------------ | ---------------------------------------------------------------------------- | ----------- | -------- | -------------------------------------- |
+| data         | `RadioItem[]`                                                                | `[]`        | Y        | Option data.                           |
+| layout       | `'v'\|'h'\|'inline'`                                                         | `'v'`       | N        | Layout mode.                           |
+| value        | `string`                                                                     | `''`        | N        | Selected value.                        |
+| textPosition | `'l'\|'r'\|'t'\|'b'`                                                         | `'r'`       | N        | Position of option content area.       |
+| icon         | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N        | Content of unchecked option icon area. |
+| iconChecked  | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N        | Content of checked option icon area.   |
 
-## RadioGroup Events
+## Radio Events
 
-| Event Name | Event Parameter                                                                     | Description                                                                                     |
-| ---------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
-| change     | event: event object, where detail represents the name of the selected radio button. | Triggered when a radio button is clicked (when the value property of RadioGroup Props changes). |
+| Name     | Type                      | Parameters                        | Description                                                |
+| -------- | ------------------------- | --------------------------------- | ---------------------------------------------------------- |
+| onchange | `(value: string) => void` | value - Selected Radio identifier | Triggered when RadioItem is clicked (Radio value changes). |
 
-## RadioGroup Slots
+## Radio Snippets
 
-| Name | Description                                                             |
-| ---- | ----------------------------------------------------------------------- |
-| -    | The RadioGroup internal slot generally includes multiple radio buttons. |
+| Name       | Type                                                                                       | Parameters         | Description                            |
+| ---------- | ------------------------------------------------------------------------------------------ | ------------------ | -------------------------------------- |
+| radioChild | [`Snippet<[{ item: RadioItem }]>`](https://svelte.dev/docs/svelte/snippet#Typing-snippets) | item - Option data | Used when customizing the option area. |
 
-## Radio Props
+## RadioItem Props
+
+| Name         | Type                                                                         | Default     | Required | Description                                   |
+| ------------ | ---------------------------------------------------------------------------- | ----------- | -------- | --------------------------------------------- |
+| name         | `string`                                                                     | `''`        | Y        | Identifier, corresponds to Radio Props value. |
+| label        | `string`                                                                     | `''`        | N        | Option content.                               |
+| layout       | `'v'\|'h'\|'inline'`                                                         | `'v'`       | N        | Layout mode.                                  |
+| textPosition | `'l'\|'r'\|'t'\|'b'`                                                         | `'r'`       | N        | Position of option content area.              |
+| icon         | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N        | Content of unchecked option icon area.        |
+| iconChecked  | `'default'\|null`\|[`Icon`](https://stdf.design/#/components?nav=icon&tab=1) | `'default'` | N        | Content of checked option icon area.          |
+| checked      | `boolean`                                                                    | `false`     | N        | Whether checked.                              |
+
+## RadioItem Events
 
-| Property     | Type          | Default   | Options                     | Required | Description                                                              |
-| ------------ | ------------- | --------- | --------------------------- | -------- | ------------------------------------------------------------------------ |
-| name         | String        | ''        | -                           | Y        | The identifier, corresponding to the value property of RadioGroup Props. |
-| 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 unchecked option icon area.                           |
-| iconChecked  | String/Object | 'default' | 'default'/'none'/Icon Props | N        | The content of the checked option icon area.                             |
+| Name    | Type                     | Parameters                     | Description             |
+| ------- | ------------------------ | ------------------------------ | ----------------------- |
+| onclick | `(name: string) => void` | name - Clicked item identifier | Triggered when clicked. |
 
-## Radio Slots
+## RadioItem Snippets
 
-| Name | Description     |
-| ---- | --------------- |
-| -    | Option content. |
+| Name     | Type                                                                | Parameters | Description          |
+| -------- | ------------------------------------------------------------------- | ---------- | -------------------- |
+| children | [`Snippet`](https://svelte.dev/docs/svelte/snippet#Typing-snippets) | -          | Option area content. |

+ 6 - 6
doc/components/radio/guide.md

@@ -2,25 +2,25 @@
 
 ## 说明
 
-由于移动端横向显示内容有限,RadioGroup 默认为纵向布局,横向布局建议只用作简单使用。当选项较多、内容较长或显示内容较多时建议使用纵向布局,
+由于移动端横向显示内容有限,Radio 默认为纵向布局,横向布局建议只用作简单使用。当选项较多、内容较长或显示内容较多时建议使用纵向布局,
 
-RadioGroup 通过 value 传入一个值作为内部 Radio 的初始标识符;而当 Radio 被点击时,RadioGroup 通过 change 事件,可以获取到当前 value,即当前选定 Radio 的 name。
+Radio 通过 value 传入一个值作为内部 Radio 的初始标识符;而当 RadioItem 被点击时,Radio 通过 onchange 事件,可以获取到当前 value,即当前选定 RadioItem 的 name。
 
 ## icon && iconChecked
 
-Radio Props 的 icon 默认值 'default' 会按照 Remix Icon 的 ri-checkbox-blank-circle-line 以 Icon 组件形式渲染;传入字符 'none',表示未选中选项图标区域不显示内容;也可以传入 Icon Props,未选中选项图标区域将会以传入的参数以 Icon 组件形式渲染。
+Radio Props 的 icon 默认值 `'default'` 会按照 Remix Icon 的 ri-checkbox-blank-circle-line 以 Icon 组件形式渲染;传入 `null` 表示未选中选项图标区域不显示内容;也可以传入 Icon Props,未选中选项图标区域将会以传入的参数以 Icon 组件形式渲染。
 
 iconChecked 与 icon 逻辑一样,对应的是选中选项的图标区域内容。
 
-## Radio Slots
+## Radio Snippets
 
-Radio 的插槽将会渲染在每条 Radio 的内容区域,可结合 RadioGroup change 事件获取到的 value,自由设计选中与未选中的效果。
+当传入 radioChild 时,Radio 的每条 RadioItem 的内容区域将会使用 radioChild 的内容区域,可结合 Radio 的 onchange 事件获取到的 value,自由设计选中与未选中的效果。
 
 注:虽然有 Tailwind 写样式比较方便,但过多的自定义将会失去使用组件库的意义。
 
 ## 点击区域优化
 
-为了优化体验,RadioGroup 整个区域都可以点击,将会激活与之对应的 Radio,不仅仅只可以点击 Radio 的文字或图标区域。
+为了优化体验,RadioItem 整个区域都可以点击,将会激活与之对应的 value,不仅仅只可以点击文字或图标区域。
 
 ## ActionSheet、Picker、Radio
 

+ 16 - 16
doc/components/radio/guide_en.md

@@ -1,31 +1,31 @@
-> Radio uses the icons ri-radio-button-line and ri-checkbox-blank-circle-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).
+> Radio uses icons ri-radio-button-line and ri-checkbox-blank-circle-line. Please ensure these icons are included in your project's symbol.svg file. For detailed information, please refer to [STDF-Guide-Icon](https://stdf.design/#/guide/icon).
 
-## Explanation
+## Description
 
-Due to limited horizontal space on mobile devices, RadioGroup defaults to vertical layout. Horizontal layout is recommended only for simple use cases. When there are many options, long content, or a lot of information to display, a vertical layout is recommended.
+Due to limited horizontal display space on mobile devices, Radio defaults to vertical layout. Horizontal layout is recommended only for simple use cases. When there are multiple options, longer content, or more display content, vertical layout is recommended.
 
-RadioGroup passes a value as the initial identifier for internal Radio components with the value property. When a Radio button is clicked, RadioGroup triggers a change event, which can be used to obtain the current value, corresponding to the name of the currently selected Radio.
+Radio accepts a value prop as the initial identifier for internal Radio items. When a RadioItem is clicked, Radio can get the current value through the onchange event, which is the name of the currently selected RadioItem.
 
 ## icon && iconChecked
 
-The default value of the icon property of Radio Props is 'default', which will render as an Icon component in the shape of ri-checkbox-blank-circle-line in Remix Icon. Passing the character 'none' indicates that the unchecked option icon area should not display any content. You can also pass in Icon Props, and the unchecked option icon area will be rendered as an Icon component based on the parameters passed in.
+The default value `'default'` for Radio Props' icon will render as an Icon component using Remix Icon's ri-checkbox-blank-circle-line. 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 render as an Icon component with the passed parameters.
 
-The logic for iconChecked is the same as that for icon, and corresponds to the content of the selected option's icon area.
+iconChecked follows the same logic as icon, corresponding to the icon area content of the selected option.
 
-## Radio Slots
+## Radio Snippets
 
-The slot for Radio will be rendered in the content area of each Radio button. By combining the value obtained from the RadioGroup change event, you can freely design the selection and unselection effects.
+When radioChild is passed, the content area of each RadioItem in Radio will use the content area of radioChild. You can freely design the selected and unselected effects by combining with the value obtained from Radio's onchange event.
 
-Note: Although it is convenient to write styles with Tailwind, too much customization will lose the meaning of using a component library.
+Note: Although Tailwind makes styling convenient, too much customization will defeat the purpose of using a component library.
 
-## Optimization for Clicking Areas
+## Click Area Optimization
 
-To optimize the user experience, the entire area of RadioGroup can be clicked, which will activate the corresponding Radio, not just the text or icon area of the Radio button.
+To optimize the user experience, the entire area of RadioItem is clickable and will activate its corresponding value, not just the text or icon areas.
 
-## ActionSheet, Picker, and Radio
+## ActionSheet, Picker, Radio
 
-ActionSheet, Picker, and Radio are all used to give users options for data selection or to perform some actions. But when and how to use which component?
+ActionSheet, Picker, and Radio all provide data for users to select or perform operations, but when should each component be used?
 
-- Radio is generally used for single-choice data with a fixed and small number of options, when you need to display the options directly in the UI for users to preview multiple data items during interaction, such as selecting gender.
-- ActionSheet is generally used when you need to immediately perform some operations after selecting, with a fixed and small number of options, but when you do not need to directly display the options in the UI, such as deleting or modifying items.
-- Picker is generally used when you need to select single or multiple items, with a large or undetermined number of dynamic data or when you need to select some linked data, such as selecting cities and districts.
+- Radio is generally used for single-selection data with a fixed and small number of options that need to be directly displayed on the UI interface, allowing users to have an overview of multiple data points during interface interaction, such as gender selection.
+- ActionSheet is generally used when actions need to be executed immediately after selection, with fixed and fewer options that don't need to be directly displayed on the UI interface, such as delete or modify operations.
+- Picker is generally used for selecting single or multiple data points with a large amount of data or dynamic data of uncertain length, or for selecting linked data, such as province-city-district selection.

+ 24 - 43
packages/stdf/components/radio/Radio.svelte

@@ -1,52 +1,33 @@
 <script>
-	import { getContext } from 'svelte';
-	import Icon from '../icon/Icon.svelte';
+	import RadioItem from './RadioItem.svelte';
 
-	/** @typedef {import('../../index.d').Radio} Radio */
-	/** @type {Radio} */
-	let { name = '', textPosition = 'r', icon = 'default', iconChecked = 'default', children } = $props();
+	/** @typedef {import('../..').Radio} RadioProps */
+	/** @type {RadioProps} */
+	let {
+		data = [],
+		value = $bindable(''),
+		layout = 'v',
+		textPosition = 'r',
+		icon = 'default',
+		iconChecked = 'default',
+		radioChild,
+		onchange,
+	} = $props();
 
-	const STDF_radioValueStore = getContext('STDF_radioValueContext');
-	const STDF_radioHorizontalStore = getContext('STDF_radioHorizontalContext');
-	const clickRadioFun = () => {
-		STDF_radioValueStore.set(name);
+	const clickItemFn = name => {
+		value = name;
+		onchange && onchange(value);
 	};
 </script>
 
-<!-- svelte-ignore a11y_click_events_have_key_events -->
-<!-- svelte-ignore a11y_no_static_element_interactions -->
-<div
-	class={`flex grow active:opacity-80 ${textPosition === 'l' && !$STDF_radioHorizontalStore ? 'justify-between' : ''} ${
-		textPosition === 'b' || textPosition === 't' ? 'flex-col items-center' : $STDF_radioHorizontalStore ? 'justify-center' : 'items-center'
-	}`}
-	onclick={clickRadioFun}
->
-	{#if textPosition === 'l' || textPosition === 't'}
-		<div class={`${textPosition === 'l' && 'mr-0.5'} ${!$STDF_radioHorizontalStore ? 'grow' : ''}`}>
-			{@render children?.()}
-		</div>
-	{/if}
-	<div class:hidden={$STDF_radioValueStore !== name}>
-		{#if iconChecked === 'none'}
-			<!-- none -->
-		{:else if iconChecked === 'default'}
-			<Icon name="ri-radio-button-line" theme 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 radioChild}
+			{@render radioChild({ item })}
 		{:else}
-			<Icon {...iconChecked} theme />
+			<RadioItem {layout} {...item} {textPosition} {icon} {iconChecked} checked={value === item.name} onclick={clickItemFn}>
+				{item.label}
+			</RadioItem>
 		{/if}
-	</div>
-	<div class:hidden={$STDF_radioValueStore === name}>
-		{#if icon === 'none'}
-			<!-- none -->
-		{:else if icon === 'default'}
-			<Icon name="ri-checkbox-blank-circle-line" alpha={0.1} top={textPosition === 't' || textPosition === 'b' ? 0 : -1} />
-		{:else}
-			<Icon {...icon} alpha={0.1} />
-		{/if}
-	</div>
-	{#if textPosition === 'r' || textPosition === 'b'}
-		<div class={`${textPosition === 'r' && 'ml-0.5'} ${!$STDF_radioHorizontalStore ? 'grow' : ''}`}>
-			{@render children?.()}
-		</div>
-	{/if}
+	{/each}
 </div>

+ 0 - 20
packages/stdf/components/radio/RadioGroup.svelte

@@ -1,20 +0,0 @@
-<script>
-	import { setContext } from 'svelte';
-	import { writable } from 'svelte/store';
-
-	/** @typedef {import('../../index.d').RadioGroup} RadioGroup */
-	/** @type {RadioGroup} */
-	let { value = $bindable(''), horizontal = false, children, onchange } = $props();
-
-	const STDF_radioValueStore = writable(value);
-	const STDF_radioHorizontalStore = writable(horizontal);
-	setContext('STDF_radioValueContext', STDF_radioValueStore);
-	setContext('STDF_radioHorizontalContext', STDF_radioHorizontalStore);
-
-	STDF_radioValueStore.subscribe(v => {
-		value = v;
-		onchange && onchange(value);
-	});
-</script>
-
-<div class={`flex ${horizontal ? 'justify-between' : 'flex-col space-y-2'}`}>{@render children?.()}</div>

+ 57 - 0
packages/stdf/components/radio/RadioItem.svelte

@@ -0,0 +1,57 @@
+<script>
+	import Icon from '../icon/Icon.svelte';
+
+	/** @typedef {import('../..').RadioItem} RadioItemProps */
+	/** @type {RadioItemProps} */
+	let {
+		name = '',
+		textPosition = 'r',
+		layout = 'v',
+		checked = false,
+		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-radio-button-line" 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-blank-circle-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>