Files
nats-console/apps/web/components/ui/select.tsx
T
Fizer Khan b4f28bda41 add alert dialog
add confirmation dialog
fix clickhouse metrics table.
2025-12-05 10:37:02 +05:30

125 lines
3.6 KiB
TypeScript

'use client';
import * as React from 'react';
import { ChevronDown } from 'lucide-react';
import { cn } from '@/lib/utils';
interface SelectContextValue {
value: string;
onValueChange: (value: string) => void;
open: boolean;
setOpen: (open: boolean) => void;
}
const SelectContext = React.createContext<SelectContextValue | null>(null);
interface SelectProps {
value: string;
onValueChange: (value: string) => void;
children: React.ReactNode;
}
export function Select({ value, onValueChange, children }: SelectProps) {
const [open, setOpen] = React.useState(false);
return (
<SelectContext.Provider value={{ value, onValueChange, open, setOpen }}>
<div className="relative">{children}</div>
</SelectContext.Provider>
);
}
interface SelectTriggerProps extends React.HTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
}
export function SelectTrigger({ className, children, ...props }: SelectTriggerProps) {
const context = React.useContext(SelectContext);
if (!context) throw new Error('SelectTrigger must be used within Select');
return (
<button
type="button"
className={cn(
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className
)}
onClick={() => context.setOpen(!context.open)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 opacity-50" />
</button>
);
}
interface SelectValueProps {
placeholder?: string;
}
export function SelectValue({ placeholder }: SelectValueProps) {
const context = React.useContext(SelectContext);
if (!context) throw new Error('SelectValue must be used within Select');
return (
<span className={!context.value ? 'text-muted-foreground' : ''}>
{context.value || placeholder}
</span>
);
}
interface SelectContentProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
export function SelectContent({ className, children, ...props }: SelectContentProps) {
const context = React.useContext(SelectContext);
if (!context) throw new Error('SelectContent must be used within Select');
if (!context.open) return null;
return (
<>
<div className="fixed inset-0 z-40" onClick={() => context.setOpen(false)} />
<div
className={cn(
'absolute z-50 min-w-[8rem] w-full mt-1 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95',
className
)}
{...props}
>
<div className="p-1">{children}</div>
</div>
</>
);
}
interface SelectItemProps extends React.HTMLAttributes<HTMLDivElement> {
value: string;
children: React.ReactNode;
}
export function SelectItem({ className, value, children, ...props }: SelectItemProps) {
const context = React.useContext(SelectContext);
if (!context) throw new Error('SelectItem must be used within Select');
const isSelected = context.value === value;
return (
<div
className={cn(
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 px-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground',
isSelected && 'bg-accent text-accent-foreground',
className
)}
onClick={() => {
context.onValueChange(value);
context.setOpen(false);
}}
{...props}
>
{children}
</div>
);
}