From 2c995b41d79c9ea013f543ad04cfcecd9c78f9d3 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Fri, 7 Aug 2026 16:11:33 +0700 Subject: [PATCH] feat: add new UI components including RadioGroup, Resizable, Sidebar, Spinner, Table, Toast, and ToggleGroup - Implemented RadioGroup and RadioGroupItem for radio button functionality. - Created ResizablePanelGroup, ResizablePanel, and ResizableHandle for resizable panels. - Developed Sidebar component with context for state management and various subcomponents (SidebarTrigger, SidebarMenu, etc.). - Added Spinner component for loading indicators. - Introduced Table component with TableHeader, TableBody, TableFooter, and related subcomponents for structured data display. - Built Toast component for notifications with customizable actions and icons. - Implemented ToggleGroup and ToggleGroupItem for toggle button functionality with context support. --- .../frontend/src/components/ui/accordion.tsx | 72 ++ .../src/components/ui/alert-dialog.tsx | 187 +++++ services/frontend/src/components/ui/alert.tsx | 76 ++ .../src/components/ui/aspect-ratio.tsx | 22 + .../frontend/src/components/ui/attachment.tsx | 207 +++++ .../frontend/src/components/ui/avatar.tsx | 42 +- services/frontend/src/components/ui/badge.tsx | 18 +- .../frontend/src/components/ui/breadcrumb.tsx | 125 +++ .../frontend/src/components/ui/bubble.tsx | 128 ++++ .../src/components/ui/button-group.tsx | 87 +++ .../frontend/src/components/ui/button.tsx | 14 +- .../frontend/src/components/ui/calendar.tsx | 221 ++++++ services/frontend/src/components/ui/card.tsx | 40 +- .../frontend/src/components/ui/carousel.tsx | 242 ++++++ services/frontend/src/components/ui/chart.tsx | 373 +++++++++ .../frontend/src/components/ui/checkbox.tsx | 29 + .../src/components/ui/collapsible.tsx | 21 + .../frontend/src/components/ui/combobox.tsx | 297 +++++++ .../frontend/src/components/ui/command.tsx | 196 +++++ .../src/components/ui/context-menu.tsx | 271 +++++++ .../frontend/src/components/ui/dialog.tsx | 54 +- .../frontend/src/components/ui/direction.tsx | 6 + .../frontend/src/components/ui/drawer.tsx | 228 ++++++ .../src/components/ui/dropdown-menu.tsx | 93 ++- services/frontend/src/components/ui/empty.tsx | 104 +++ services/frontend/src/components/ui/field.tsx | 238 ++++++ .../frontend/src/components/ui/hover-card.tsx | 51 ++ .../src/components/ui/input-group.tsx | 68 +- .../frontend/src/components/ui/input-otp.tsx | 87 +++ services/frontend/src/components/ui/input.tsx | 12 +- services/frontend/src/components/ui/item.tsx | 201 +++++ services/frontend/src/components/ui/kbd.tsx | 26 + services/frontend/src/components/ui/label.tsx | 20 +- .../frontend/src/components/ui/marker.tsx | 71 ++ .../frontend/src/components/ui/menubar.tsx | 280 +++++++ .../src/components/ui/message-scroller.tsx | 131 ++++ .../frontend/src/components/ui/message.tsx | 92 +++ .../src/components/ui/native-select.tsx | 61 ++ .../src/components/ui/navigation-menu.tsx | 168 ++++ .../frontend/src/components/ui/pagination.tsx | 130 ++++ .../frontend/src/components/ui/popover.tsx | 90 +++ .../frontend/src/components/ui/progress.tsx | 24 +- .../src/components/ui/questionnaire.tsx | 324 ++++++++ .../src/components/ui/radio-group.tsx | 38 + .../frontend/src/components/ui/resizable.tsx | 50 ++ .../src/components/ui/scroll-area.tsx | 15 +- .../frontend/src/components/ui/select.tsx | 56 +- .../frontend/src/components/ui/separator.tsx | 12 +- services/frontend/src/components/ui/sheet.tsx | 56 +- .../frontend/src/components/ui/sidebar.tsx | 723 ++++++++++++++++++ .../frontend/src/components/ui/skeleton.tsx | 6 +- .../frontend/src/components/ui/slider.tsx | 14 +- .../frontend/src/components/ui/spinner.tsx | 10 + .../frontend/src/components/ui/switch.tsx | 14 +- services/frontend/src/components/ui/table.tsx | 116 +++ services/frontend/src/components/ui/tabs.tsx | 26 +- .../frontend/src/components/ui/textarea.tsx | 10 +- services/frontend/src/components/ui/toast.tsx | 234 ++++++ .../src/components/ui/toggle-group.tsx | 89 +++ .../frontend/src/components/ui/toggle.tsx | 16 +- .../frontend/src/components/ui/tooltip.tsx | 18 +- services/frontend/src/hooks/use-mobile.ts | 24 +- 62 files changed, 6422 insertions(+), 332 deletions(-) create mode 100644 services/frontend/src/components/ui/accordion.tsx create mode 100644 services/frontend/src/components/ui/alert-dialog.tsx create mode 100644 services/frontend/src/components/ui/alert.tsx create mode 100644 services/frontend/src/components/ui/aspect-ratio.tsx create mode 100644 services/frontend/src/components/ui/attachment.tsx create mode 100644 services/frontend/src/components/ui/breadcrumb.tsx create mode 100644 services/frontend/src/components/ui/bubble.tsx create mode 100644 services/frontend/src/components/ui/button-group.tsx create mode 100644 services/frontend/src/components/ui/calendar.tsx create mode 100644 services/frontend/src/components/ui/carousel.tsx create mode 100644 services/frontend/src/components/ui/chart.tsx create mode 100644 services/frontend/src/components/ui/checkbox.tsx create mode 100644 services/frontend/src/components/ui/collapsible.tsx create mode 100644 services/frontend/src/components/ui/combobox.tsx create mode 100644 services/frontend/src/components/ui/command.tsx create mode 100644 services/frontend/src/components/ui/context-menu.tsx create mode 100644 services/frontend/src/components/ui/direction.tsx create mode 100644 services/frontend/src/components/ui/drawer.tsx create mode 100644 services/frontend/src/components/ui/empty.tsx create mode 100644 services/frontend/src/components/ui/field.tsx create mode 100644 services/frontend/src/components/ui/hover-card.tsx create mode 100644 services/frontend/src/components/ui/input-otp.tsx create mode 100644 services/frontend/src/components/ui/item.tsx create mode 100644 services/frontend/src/components/ui/kbd.tsx create mode 100644 services/frontend/src/components/ui/marker.tsx create mode 100644 services/frontend/src/components/ui/menubar.tsx create mode 100644 services/frontend/src/components/ui/message-scroller.tsx create mode 100644 services/frontend/src/components/ui/message.tsx create mode 100644 services/frontend/src/components/ui/native-select.tsx create mode 100644 services/frontend/src/components/ui/navigation-menu.tsx create mode 100644 services/frontend/src/components/ui/pagination.tsx create mode 100644 services/frontend/src/components/ui/popover.tsx create mode 100644 services/frontend/src/components/ui/questionnaire.tsx create mode 100644 services/frontend/src/components/ui/radio-group.tsx create mode 100644 services/frontend/src/components/ui/resizable.tsx create mode 100644 services/frontend/src/components/ui/sidebar.tsx create mode 100644 services/frontend/src/components/ui/spinner.tsx create mode 100644 services/frontend/src/components/ui/table.tsx create mode 100644 services/frontend/src/components/ui/toast.tsx create mode 100644 services/frontend/src/components/ui/toggle-group.tsx diff --git a/services/frontend/src/components/ui/accordion.tsx b/services/frontend/src/components/ui/accordion.tsx new file mode 100644 index 0000000..597ef3c --- /dev/null +++ b/services/frontend/src/components/ui/accordion.tsx @@ -0,0 +1,72 @@ +import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion" + +import { cn } from "@/lib/utils" +import { ChevronDownIcon, ChevronUpIcon } from "lucide-react" + +function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { + return ( + + ) +} + +function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: AccordionPrimitive.Trigger.Props) { + return ( + + + {children} + + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: AccordionPrimitive.Panel.Props) { + return ( + +
+ {children} +
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/services/frontend/src/components/ui/alert-dialog.tsx b/services/frontend/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..05dce24 --- /dev/null +++ b/services/frontend/src/components/ui/alert-dialog.tsx @@ -0,0 +1,187 @@ +"use client" + +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { + return +} + +function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { + return ( + + ) +} + +function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: AlertDialogPrimitive.Backdrop.Props) { + return ( + + ) +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: AlertDialogPrimitive.Popup.Props & { + size?: "default" | "sm" +}) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CarouselNext({ + className, + variant = "outline", + size = "icon-sm", + ...props +}: React.ComponentProps) { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +} + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, + useCarousel, +} diff --git a/services/frontend/src/components/ui/chart.tsx b/services/frontend/src/components/ui/chart.tsx new file mode 100644 index 0000000..7c2dc84 --- /dev/null +++ b/services/frontend/src/components/ui/chart.tsx @@ -0,0 +1,373 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" +import type { TooltipValueType } from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +const INITIAL_DIMENSION = { width: 320, height: 200 } as const +type TooltipNameType = number | string + +export type ChartConfig = Record< + string, + { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +> + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +function ChartContainer({ + id, + className, + children, + config, + initialDimension = INITIAL_DIMENSION, + ...props +}: React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + initialDimension?: { + width: number + height: number + } +}) { + const uniqueId = React.useId() + const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +} + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme ?? config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +