From 40b8b815823e206ba0d24290d4bbf5c4b0a38f45 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sun, 18 Oct 2026 16:31:25 -0700 Subject: [PATCH] Rebuild dialogs, menus, select, command and calendar on shadcn/ui Dialog, alert dialog, dropdown menu, select, command and calendar follow shadcn/ui 4.21.1 (MIT) as function components, over the same Radix, cmdk and DayPicker packages, keeping the console's look. Dialog and alert dialog share their overlay and panel styles (modalClasses.ts). Exports nothing uses are gone: the command dialog, shortcut and separator, the menu's checkbox item and shortcut, the select's group, label and scroll buttons as exports, and the portal and overlay parts. The calendar only picks single dates, which is all the date field asks of it, so range and week-number styling are dropped. --- src/components/ui/alert-dialog.tsx | 145 +++++++------- src/components/ui/calendar.tsx | 145 +++++--------- src/components/ui/command.tsx | 192 ++++++++---------- src/components/ui/dialog.tsx | 143 +++++++------- src/components/ui/dropdown-menu.tsx | 291 +++++++++++++--------------- src/components/ui/modalClasses.ts | 22 +++ src/components/ui/select.tsx | 217 +++++++++------------ 7 files changed, 515 insertions(+), 640 deletions(-) create mode 100644 src/components/ui/modalClasses.ts diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx index b253adf..fa90fdf 100644 --- a/src/components/ui/alert-dialog.tsx +++ b/src/components/ui/alert-dialog.tsx @@ -1,104 +1,91 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; import { cn } from '@/lib/utils'; import { buttonVariants } from '@/components/ui/button'; +import { + modalContentClasses, + modalFooterClasses, + modalHeaderClasses, + modalOverlayClasses, +} from '@/components/ui/modalClasses'; -const AlertDialog = AlertDialogPrimitive.Root; +function AlertDialog(props: React.ComponentProps) { + return ; +} -const AlertDialogTrigger = AlertDialogPrimitive.Trigger; +function AlertDialogTrigger(props: React.ComponentProps) { + return ; +} -const AlertDialogPortal = AlertDialogPrimitive.Portal; +function AlertDialogContent({ className, ...props }: React.ComponentProps) { + return ( + + + + + ); +} -const AlertDialogOverlay = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; +function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>) { + return
; +} -const AlertDialogContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - ) { + return
; +} + +function AlertDialogTitle({ className, ...props }: React.ComponentProps) { + return ( + - -)); -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + ); +} -const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -AlertDialogHeader.displayName = 'AlertDialogHeader'; +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} -const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -AlertDialogFooter.displayName = 'AlertDialogFooter'; +function AlertDialogAction({ className, ...props }: React.ComponentProps) { + return ; +} -const AlertDialogTitle = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; - -const AlertDialogDescription = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; - -const AlertDialogAction = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; - -const AlertDialogCancel = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; +function AlertDialogCancel({ className, ...props }: React.ComponentProps) { + return ( + + ); +} export { AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index ac254bb..3b4a957 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -1,121 +1,76 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; -import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; import { DayPicker, getDefaultClassNames, type DayButton } from '@daypicker/react'; import { cn } from '@/lib/utils'; import { Button, buttonVariants } from '@/components/ui/button'; +const navButton = cn( + buttonVariants({ variant: 'ghost' }), + 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50', +); + +/** + * A month grid for picking one day. The console only picks single dates (the + * date field), so only that is styled: no ranges, week numbers or month and + * year dropdowns. + */ function Calendar({ className, classNames, - showOutsideDays = true, - captionLayout = 'label', - buttonVariant = 'ghost', - formatters, components, + showOutsideDays = true, ...props -}: React.ComponentProps & { - buttonVariant?: React.ComponentProps['variant']; -}) { - const defaultClassNames = getDefaultClassNames(); - +}: React.ComponentProps) { + const base = getDefaultClassNames(); return ( svg]:rotate-180`, - String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className, )} - captionLayout={captionLayout} - formatters={{ - formatMonthDropdown: (date) => date.toLocaleString('default', { month: 'short' }), - ...formatters, - }} classNames={{ - root: cn('w-fit', defaultClassNames.root), - months: cn('relative flex flex-col gap-4 md:flex-row', defaultClassNames.months), - month: cn('flex w-full flex-col gap-4', defaultClassNames.month), - nav: cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', defaultClassNames.nav), - button_previous: cn( - buttonVariants({ variant: buttonVariant }), - 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50', - defaultClassNames.button_previous, - ), - button_next: cn( - buttonVariants({ variant: buttonVariant }), - 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50', - defaultClassNames.button_next, - ), + root: cn('w-fit', base.root), + months: cn('relative flex flex-col gap-4 md:flex-row', base.months), + month: cn('flex w-full flex-col gap-4', base.month), + nav: cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', base.nav), + button_previous: cn(navButton, base.button_previous), + button_next: cn(navButton, base.button_next), month_caption: cn( 'flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', - defaultClassNames.month_caption, + base.month_caption, ), - dropdowns: cn( - 'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium', - defaultClassNames.dropdowns, - ), - dropdown_root: cn( - 'relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50', - defaultClassNames.dropdown_root, - ), - dropdown: cn('absolute inset-0 bg-popover opacity-0', defaultClassNames.dropdown), - caption_label: cn( - 'font-medium select-none', - captionLayout === 'label' - ? 'text-sm' - : 'flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground', - defaultClassNames.caption_label, - ), - month_grid: cn('w-full border-collapse', defaultClassNames.month_grid), - weekdays: cn('flex', defaultClassNames.weekdays), - weekday: cn( - 'flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none', - defaultClassNames.weekday, - ), - week: cn('mt-2 flex w-full', defaultClassNames.week), - week_number_header: cn('w-(--cell-size) select-none', defaultClassNames.week_number_header), - week_number: cn('text-[0.8rem] text-muted-foreground select-none', defaultClassNames.week_number), - day: cn( - 'group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md', - props.showWeekNumber - ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-md' - : '[&:first-child[data-selected=true]_button]:rounded-l-md', - defaultClassNames.day, - ), - range_start: cn('rounded-l-md bg-accent', defaultClassNames.range_start), - range_middle: cn('rounded-none', defaultClassNames.range_middle), - range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end), - today: cn( - 'rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none', - defaultClassNames.today, - ), - outside: cn('text-muted-foreground aria-selected:text-muted-foreground', defaultClassNames.outside), - disabled: cn('text-muted-foreground opacity-50', defaultClassNames.disabled), - hidden: cn('invisible', defaultClassNames.hidden), + caption_label: cn('text-sm font-medium select-none', base.caption_label), + month_grid: cn('w-full border-collapse', base.month_grid), + weekdays: cn('flex', base.weekdays), + weekday: cn('flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none', base.weekday), + week: cn('mt-2 flex w-full', base.week), + day: cn('group/day relative aspect-square size-full p-0 text-center select-none', base.day), + today: cn('rounded-md bg-accent text-accent-foreground', base.today), + outside: cn('text-muted-foreground aria-selected:text-muted-foreground', base.outside), + disabled: cn('text-muted-foreground opacity-50', base.disabled), + hidden: cn('invisible', base.hidden), ...classNames, }} components={{ - Root: ({ className, rootRef, ...props }) => ( -
+ Root: ({ className, rootRef, ...rest }) => ( +
), - Chevron: ({ className, orientation, ...props }) => { - const Icon = orientation === 'left' ? ChevronLeft : orientation === 'right' ? ChevronRight : ChevronDown; - return ; + Chevron: ({ className, orientation, ...rest }) => { + const Icon = orientation === 'left' ? ChevronLeft : ChevronRight; + return ; }, DayButton: CalendarDayButton, - WeekNumber: ({ children, ...props }) => ( - -
{children}
- - ), ...components, }} {...props} @@ -124,9 +79,8 @@ function Calendar({ } function CalendarDayButton({ className, day, modifiers, ...props }: React.ComponentProps) { - const defaultClassNames = getDefaultClassNames(); - const ref = React.useRef(null); + // Keyboard moves focus between days by re-rendering; follow it. React.useEffect(() => { if (modifiers.focused) ref.current?.focus(); }, [modifiers.focused]); @@ -137,15 +91,12 @@ function CalendarDayButton({ className, day, modifiers, ...props }: React.Compon variant="ghost" size="icon" data-day={day.date.toLocaleDateString()} - data-selected-single={ - modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle - } - data-range-start={modifiers.range_start} - data-range-end={modifiers.range_end} - data-range-middle={modifiers.range_middle} + data-selected-single={modifiers.selected} className={cn( - 'flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70', - defaultClassNames.day, + 'flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal dark:hover:text-accent-foreground', + 'group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50', + 'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground', + getDefaultClassNames().day, className, )} {...props} @@ -153,4 +104,4 @@ function CalendarDayButton({ className, day, modifiers, ...props }: React.Compon ); } -export { Calendar, CalendarDayButton }; +export { Calendar }; diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx index 8e30b64..5d935f8 100644 --- a/src/components/ui/command.tsx +++ b/src/components/ui/command.tsx @@ -1,132 +1,100 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; -import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; import { Search } from 'lucide-react'; import { cn } from '@/lib/utils'; -import { Dialog, DialogContent } from '@/components/ui/dialog'; -const Command = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -Command.displayName = CommandPrimitive.displayName; - -const CommandDialog = ({ children, ...props }: DialogProps) => { +function Command({ className, ...props }: React.ComponentProps) { return ( - - - - {children} - - - + ); -}; +} -const CommandInput = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef & { trailing?: React.ReactNode } ->(({ className, trailing, ...props }, ref) => ( -
- - & { trailing?: React.ReactNode }) { + return ( +
+ + + {trailing} +
+ ); +} + +function CommandList({ className, ...props }: React.ComponentProps) { + return ( + + ); +} + +function CommandEmpty({ className, ...props }: React.ComponentProps) { + return ( + + ); +} + +function CommandGroup({ className, ...props }: React.ComponentProps) { + return ( + - {trailing} -
-)); -CommandInput.displayName = CommandPrimitive.Input.displayName; + ); +} -const CommandList = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -CommandList.displayName = CommandPrimitive.List.displayName; +function CommandItem({ className, ...props }: React.ComponentProps) { + return ( + + ); +} -const CommandEmpty = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->((props, ref) => ); -CommandEmpty.displayName = CommandPrimitive.Empty.displayName; - -const CommandGroup = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -CommandGroup.displayName = CommandPrimitive.Group.displayName; - -const CommandSeparator = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -CommandSeparator.displayName = CommandPrimitive.Separator.displayName; - -const CommandItem = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -CommandItem.displayName = CommandPrimitive.Item.displayName; - -const CommandShortcut = ({ className, ...props }: React.HTMLAttributes) => { - return ; -}; -CommandShortcut.displayName = 'CommandShortcut'; - -export { - Command, - CommandDialog, - CommandInput, - CommandList, - CommandEmpty, - CommandGroup, - CommandItem, - CommandShortcut, - CommandSeparator, -}; +export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem }; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 055ad11..0379ad7 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,98 +1,91 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { + modalContentClasses, + modalFooterClasses, + modalHeaderClasses, + modalOverlayClasses, +} from '@/components/ui/modalClasses'; -const Dialog = DialogPrimitive.Root; +function Dialog(props: React.ComponentProps) { + return ; +} -const DialogTrigger = DialogPrimitive.Trigger; +function DialogTrigger(props: React.ComponentProps) { + return ; +} -const DialogPortal = DialogPrimitive.Portal; +function DialogClose(props: React.ComponentProps) { + return ; +} -const DialogClose = DialogPrimitive.Close; +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { showCloseButton?: boolean }) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ); +} -const DialogOverlay = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; +function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { + return
; +} -const DialogContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef & { showCloseButton?: boolean } ->(({ className, children, showCloseButton = true, ...props }, ref) => ( - - - ) { + return
; +} + +function DialogTitle({ className, ...props }: React.ComponentProps) { + return ( + - {children} - {showCloseButton && ( - - - Close - - )} - - -)); -DialogContent.displayName = DialogPrimitive.Content.displayName; + /> + ); +} -const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -DialogHeader.displayName = 'DialogHeader'; - -const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -DialogFooter.displayName = 'DialogFooter'; - -const DialogTitle = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogTitle.displayName = DialogPrimitive.Title.displayName; - -const DialogDescription = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogDescription.displayName = DialogPrimitive.Description.displayName; +function DialogDescription({ className, ...props }: React.ComponentProps) { + return ( + + ); +} export { Dialog, - DialogPortal, - DialogOverlay, DialogTrigger, DialogClose, DialogContent, diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index 4790b35..bba58f8 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -1,186 +1,173 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; -import { Check, ChevronRight, Circle } from 'lucide-react'; +import { ChevronRight, Circle } from 'lucide-react'; import { cn } from '@/lib/utils'; -const DropdownMenu = DropdownMenuPrimitive.Root; +// The panel and the item rows, shared by the menu and its submenus. +const panel = [ + 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground', + 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', + 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', +]; +const row = + 'relative flex cursor-default select-none items-center rounded-sm text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'; -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; +function DropdownMenu(props: React.ComponentProps) { + return ; +} -const DropdownMenuGroup = DropdownMenuPrimitive.Group; +function DropdownMenuTrigger(props: React.ComponentProps) { + return ; +} -const DropdownMenuPortal = DropdownMenuPrimitive.Portal; +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} -const DropdownMenuSub = DropdownMenuPrimitive.Sub; +function DropdownMenuGroup(props: React.ComponentProps) { + return ; +} -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; +function DropdownMenuItem({ + className, + inset, + ...props +}: React.ComponentProps & { inset?: boolean }) { + return ( + svg]:size-4 [&>svg]:shrink-0', inset && 'pl-8', className)} + {...props} + /> + ); +} -const DropdownMenuSubTrigger = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)); -DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; +function DropdownMenuRadioGroup(props: React.ComponentProps) { + return ; +} -const DropdownMenuSubContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} -const DropdownMenuContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - & { inset?: boolean }) { + return ( + + ); +} + +function DropdownMenuSeparator({ className, ...props }: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuSub(props: React.ComponentProps) { + return ; +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { inset?: boolean }) { + return ( + + {children} + + + ); +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + - -)); -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; - -const DropdownMenuItem = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, ...props }, ref) => ( - svg]:size-4 [&>svg]:shrink-0', - inset && 'pl-8', - className, - )} - {...props} - /> -)); -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)); -DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; - -const DropdownMenuRadioItem = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)); -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; - -const DropdownMenuLabel = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, ...props }, ref) => ( - -)); -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; - -const DropdownMenuSeparator = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; - -const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { - return ; -}; -DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; + ); +} export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, + DropdownMenuGroup, DropdownMenuItem, - DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, DropdownMenuSub, - DropdownMenuSubContent, DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenuSubContent, }; diff --git a/src/components/ui/modalClasses.ts b/src/components/ui/modalClasses.ts new file mode 100644 index 0000000..57a29e2 --- /dev/null +++ b/src/components/ui/modalClasses.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** The look Dialog and AlertDialog share. */ +export const modalOverlayClasses = + 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0'; + +/** + * Centered with translate, so a caller can move it with its own `top-*`, + * `left-*` or `translate-*` classes (the help panel docks it to the right). + */ +export const modalContentClasses = [ + 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg', + 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]', + 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]', +]; + +export const modalHeaderClasses = 'flex flex-col text-center sm:text-left'; +export const modalFooterClasses = 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2'; diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index b68e55d..61cf1d6 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -1,148 +1,115 @@ /* - * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * SPDX-FileCopyrightText: 2023 shadcn + * SPDX-FileCopyrightText: 2026 Coffey Labs LLC * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * SPDX-License-Identifier: MIT */ +// Structure from shadcn/ui 4.21.1 (MIT), styled for inbuxa. + import * as React from 'react'; import * as SelectPrimitive from '@radix-ui/react-select'; import { Check, ChevronDown, ChevronUp } from 'lucide-react'; import { cn } from '@/lib/utils'; -const Select = SelectPrimitive.Root; +function Select(props: React.ComponentProps) { + return ; +} -const SelectGroup = SelectPrimitive.Group; +function SelectValue(props: React.ComponentProps) { + return ; +} -const SelectValue = SelectPrimitive.Value; - -const SelectTrigger = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - span]:line-clamp-1', - className, - )} - {...props} - > - {children} - - - - -)); -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; - -const SelectScrollUpButton = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; - -const SelectScrollDownButton = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; - -const SelectContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, children, position = 'popper', ...props }, ref) => ( - - ) { + return ( + span]:line-clamp-1', className, )} - position={position} {...props} > - - + + + + ); +} + +function SelectContent({ + className, + children, + position = 'popper', + ...props +}: React.ComponentProps) { + const popper = position === 'popper'; + return ( + + - {children} - - - - -)); -SelectContent.displayName = SelectPrimitive.Content.displayName; + + + + + {children} + + + + + + + ); +} -const SelectLabel = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectLabel.displayName = SelectPrimitive.Label.displayName; +function SelectItem({ className, children, ...props }: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} -const SelectItem = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)); -SelectItem.displayName = SelectPrimitive.Item.displayName; +function SelectSeparator({ className, ...props }: React.ComponentProps) { + return ( + + ); +} -const SelectSeparator = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectSeparator.displayName = SelectPrimitive.Separator.displayName; - -export { - Select, - SelectGroup, - SelectValue, - SelectTrigger, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, - SelectScrollUpButton, - SelectScrollDownButton, -}; +export { Select, SelectValue, SelectTrigger, SelectContent, SelectItem, SelectSeparator }; -- 2.54.0