Yong Tang 14ddef5c05
feat(gallery): add Style Detail Modal with phone UI controls preview (#155)
Click any StyleCard to open a full-screen modal showing the style applied
to 9 interactive phone UI controls (buttons, inputs, toggles, checkboxes,
radios, cards, badges, slider, nav bar) inside an iPhone mockup frame.

Co-authored-by: yongtang <jlinux@msn.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-01 08:29:30 +07:00

112 lines
2.9 KiB
TypeScript

"use client";
import { Filters } from "@/lib/filterStyles";
interface FilterBarProps {
filters: Filters;
onChange: (filters: Filters) => void;
types: string[];
complexities: string[];
eras: string[];
resultCount: number;
totalCount: number;
}
function FilterGroup({
label,
options,
value,
onChange,
}: {
label: string;
options: string[];
value: string;
onChange: (v: string) => void;
}) {
return (
<div className="flex flex-wrap items-center gap-1.5">
<span className="text-xs font-medium text-gray-500 dark:text-gray-400 mr-1">
{label}:
</span>
<button
onClick={() => onChange("")}
className={`px-2.5 py-1 text-xs rounded-full transition-colors ${
value === ""
? "bg-blue-500 text-white"
: "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
}`}
>
All
</button>
{options.map((opt) => (
<button
key={opt}
onClick={() => onChange(value === opt ? "" : opt)}
className={`px-2.5 py-1 text-xs rounded-full transition-colors ${
value === opt
? "bg-blue-500 text-white"
: "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
}`}
>
{opt}
</button>
))}
</div>
);
}
export function FilterBar({
filters,
onChange,
types,
complexities,
eras,
resultCount,
totalCount,
}: FilterBarProps) {
const hasFilters =
filters.search || filters.type || filters.complexity || filters.era;
return (
<div className="space-y-3">
<div className="flex flex-wrap gap-4">
<FilterGroup
label="Type"
options={types}
value={filters.type}
onChange={(type) => onChange({ ...filters, type })}
/>
<FilterGroup
label="Complexity"
options={complexities}
value={filters.complexity}
onChange={(complexity) => onChange({ ...filters, complexity })}
/>
</div>
<div className="flex flex-wrap gap-4">
<FilterGroup
label="Era"
options={eras}
value={filters.era}
onChange={(era) => onChange({ ...filters, era })}
/>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-gray-500 dark:text-gray-400">
Showing <span className="font-semibold text-gray-700 dark:text-gray-200">{resultCount}</span> of {totalCount} styles
</p>
{hasFilters && (
<button
onClick={() =>
onChange({ search: "", type: "", complexity: "", era: "" })
}
className="text-xs text-blue-500 hover:text-blue-600 transition-colors"
>
Clear all filters
</button>
)}
</div>
</div>
);
}