PE08▸⌨️ Input · 進階輸入▸選擇
Segmented Control
分段控制
難度 ★★★★★標籤 iOS · segment · apple2 min read
模式簡述
Segmented Control:iOS 風格的 2-5 選一控制。比 radio 視覺更緊湊,比 dropdown 一步可達。
顯示「日」視圖
Claude Code Prompt
參考:PE08 segmented-control 組件:SegmentedControl.tsx Segmented Control:iOS 風格的 2-5 選一控制。比 radio 視覺更緊湊,比 dropdown 一步可達。 限制: - indicator 不滑動 - 第一個選項預設淡 - 鍵盤導航沒做
完整程式碼
"use client";
import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
type State = "first" | "middle" | "last";
const OPTIONS = ["日", "週", "月", "年"];
export function SegmentedControl({ state = "first" }: { state?: State }) {
const init = state === "middle" ? 1 : state === "last" ? 3 : 0;
const [active, setActive] = useState(init);
useEffect(() => setActive(init), [state]);
return (
<div className="w-full max-w-md">
<div className="relative flex rounded-lg bg-muted p-0.5">
{OPTIONS.map((o, i) => (
<button
key={o}
onClick={() => setActive(i)}
className={cn(
"relative z-10 flex-1 rounded-md py-1.5 text-xs transition-colors",
i === active ? "text-foreground" : "text-muted-foreground hover:text-foreground",
)}
>
{i === active && (
<motion.span
layoutId="seg-thumb"
className="absolute inset-0 -z-10 rounded-md bg-background shadow-sm"
transition={{ type: "spring", stiffness: 380, damping: 32 }}
/>
)}
{o}
</button>
))}
</div>
<p className="mt-3 font-mono text-[11px] text-muted-foreground">
顯示「{OPTIONS[active]}」視圖
</p>
</div>
);
}何時用
- ✅ 時間範圍切換(日/週/月/年)
- ✅ 視角切換(卡片/列表)
- ✅ Tab 的精簡版
何時別用
- ❌ 選項超過 5 個(用 select 或 tabs)
- ❌ 選項長度差很多(會擠)
- ❌ 需要多選(用 checkbox group)
我踩過的坑
-
indicator 不滑動:用 Framer layoutId 滑過去,瞬間 iOS。
-
第一個選項預設淡:當前選項應該明顯,未選的灰。
-
鍵盤導航沒做:ArrowLeft/Right 切換 focus,跟 tab 一樣。
相關模式
- PC01
- PE07