Seed: travel template (16 files)

This commit is contained in:
2026-07-23 22:14:14 +00:00
parent 1b4f7277b7
commit 306f7e8a91
+72
View File
@@ -0,0 +1,72 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const SelectContext = React.createContext<{
value: string; setValue: (v: string) => void;
open: boolean; setOpen: (o: boolean) => void;
labels: React.MutableRefObject<Record<string, React.ReactNode>>;
}>({ value: "", setValue: () => {}, open: false, setOpen: () => {}, labels: { current: {} } as any });
export function Select({ defaultValue = "", value: controlled, onValueChange, children }: { defaultValue?: string; value?: string; onValueChange?: (v: string) => void; children?: React.ReactNode }) {
const [internal, setInternal] = React.useState(defaultValue);
const [open, setOpen] = React.useState(false);
const labels = React.useRef<Record<string, React.ReactNode>>({});
const value = controlled ?? internal;
const setValue = (v: string) => { setInternal(v); onValueChange?.(v); setOpen(false); };
return (
<SelectContext.Provider value={{ value, setValue, open, setOpen, labels }}>
<div className="relative inline-block w-full">{children}</div>
</SelectContext.Provider>
);
}
export function SelectTrigger({ className, children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
const ctx = React.useContext(SelectContext);
return (
<button
type="button"
onClick={() => ctx.setOpen(!ctx.open)}
className={cn("flex h-10 w-full items-center justify-between rounded-md border border-slate-300 bg-white px-3 py-2 text-sm", className)}
{...props}
>
{children}
<span className="ml-2 opacity-50"></span>
</button>
);
}
export function SelectValue({ placeholder }: { placeholder?: string }) {
const ctx = React.useContext(SelectContext);
return <span>{ctx.value ? (ctx.labels.current[ctx.value] ?? ctx.value) : (placeholder || "Select...")}</span>;
}
export function SelectContent({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
const ctx = React.useContext(SelectContext);
if (!ctx.open) return null;
return (
<div className={cn("absolute z-50 mt-1 w-full rounded-md border border-slate-200 bg-white py-1 shadow-lg", className)} {...props}>
{children}
</div>
);
}
export function SelectItem({ value, className, children, ...props }: React.HTMLAttributes<HTMLDivElement> & { value: string }) {
const ctx = React.useContext(SelectContext);
ctx.labels.current[value] = children;
return (
<div
onClick={() => ctx.setValue(value)}
className={cn("cursor-pointer px-3 py-2 text-sm hover:bg-slate-100", ctx.value === value && "bg-slate-50 font-medium", className)}
{...props}
>
{children}
</div>
);
}
export function SelectGroup({ children }: { children?: React.ReactNode }) { return <div>{children}</div>; }
export function SelectLabel({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn("px-3 py-1.5 text-xs font-semibold text-slate-500", className)} {...props} />;
}
export default Select;