diff --git a/packages/coss-ui/src/components/accordion.tsx b/packages/coss-ui/src/components/accordion.tsx index fdba1a25fb..f0ec59102a 100644 --- a/packages/coss-ui/src/components/accordion.tsx +++ b/packages/coss-ui/src/components/accordion.tsx @@ -28,14 +28,17 @@ function AccordionTrigger({ svg]:rotate-180", + "flex flex-1 cursor-pointer items-start justify-between gap-4 rounded-md py-4 text-left font-medium text-sm outline-none transition-all focus-visible:ring-[3px] focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 data-panel-open:*:data-[slot=accordion-indicator]:rotate-180", className, )} data-slot="accordion-trigger" {...props} > {children} - + ); diff --git a/packages/coss-ui/src/components/alert-dialog.tsx b/packages/coss-ui/src/components/alert-dialog.tsx index f9b3abe31a..e62ee6fa95 100644 --- a/packages/coss-ui/src/components/alert-dialog.tsx +++ b/packages/coss-ui/src/components/alert-dialog.tsx @@ -4,6 +4,8 @@ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog import { cn } from "@coss/ui/lib/utils"; +const AlertDialogCreateHandle = AlertDialogPrimitive.createHandle; + const AlertDialog = AlertDialogPrimitive.Root; const AlertDialogPortal = AlertDialogPrimitive.Portal; @@ -37,7 +39,7 @@ function AlertDialogViewport({ return ( - + @@ -137,6 +152,7 @@ function AlertDialogClose(props: AlertDialogPrimitive.Close.Props) { } export { + AlertDialogCreateHandle, AlertDialog, AlertDialogPortal, AlertDialogBackdrop, diff --git a/packages/coss-ui/src/components/autocomplete.tsx b/packages/coss-ui/src/components/autocomplete.tsx index 0894c3afa7..c63a7110fa 100644 --- a/packages/coss-ui/src/components/autocomplete.tsx +++ b/packages/coss-ui/src/components/autocomplete.tsx @@ -13,26 +13,40 @@ function AutocompleteInput({ className, showTrigger = false, showClear = false, + startAddon, size, ...props }: Omit & { showTrigger?: boolean; showClear?: boolean; + startAddon?: React.ReactNode; size?: "sm" | "default" | "lg" | number; + ref?: React.Ref; }) { const sizeValue = (size ?? "default") as "sm" | "default" | "lg" | number; return ( -
+
+ {startAddon && ( + + )} } + render={} {...props} /> {showTrigger && ( @@ -76,7 +90,7 @@ function AutocompletePopup({ > @@ -131,7 +145,7 @@ function AutocompleteGroup({ }: AutocompletePrimitive.Group.Props) { return ( @@ -197,7 +211,7 @@ function AutocompleteList({ ) {
    ) { return ( + // biome-ignore lint(a11y/useFocusableInteractive): known ) { - return ( -
    - ); +function Card({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn( + "relative flex flex-col gap-6 rounded-2xl border bg-card not-dark:bg-clip-padding py-6 text-card-foreground shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", + className, + ), + "data-slot": "card", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardHeader({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn( + "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", + className, + ), + "data-slot": "card-header", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardTitle({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardTitle({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn("font-semibold text-lg leading-none", className), + "data-slot": "card-title", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardDescription({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn("text-muted-foreground text-sm", className), + "data-slot": "card-description", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardAction({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardAction({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn( + "col-start-2 row-span-2 row-start-1 self-start justify-self-end", + className, + ), + "data-slot": "card-action", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardPanel({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardPanel({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn("px-6", className), + "data-slot": "card-content", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } -function CardFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ); +function CardFooter({ + className, + render, + ...props +}: useRender.ComponentProps<"div">) { + const defaultProps = { + className: cn("flex items-center px-6 [.border-t]:pt-6", className), + "data-slot": "card-footer", + }; + + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">(defaultProps, props), + render, + }); } export { Card, - CardHeader, - CardFooter, - CardTitle, CardAction, CardDescription, + CardFooter, + CardHeader, CardPanel, CardPanel as CardContent, + CardTitle, }; diff --git a/packages/coss-ui/src/components/checkbox.tsx b/packages/coss-ui/src/components/checkbox.tsx index cd0eb869dc..948bddc536 100644 --- a/packages/coss-ui/src/components/checkbox.tsx +++ b/packages/coss-ui/src/components/checkbox.tsx @@ -8,7 +8,7 @@ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) { return ( & { showTrigger?: boolean; showClear?: boolean; + startAddon?: React.ReactNode; size?: "sm" | "default" | "lg" | number; + ref?: React.Ref; }) { const { multiple } = React.useContext(ComboboxContext); const sizeValue = (size ?? "default") as "sm" | "default" | "lg" | number; @@ -64,18 +67,36 @@ function ComboboxInput({ /> ); } + // single mode return ( -
    +
    + {startAddon && ( + + )} } + render={ + + } {...props} /> {showTrigger && ( @@ -135,7 +156,7 @@ function ComboboxPopup({ > @@ -202,7 +223,7 @@ function ComboboxSeparator({ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( @@ -299,19 +320,48 @@ function ComboboxCollection(props: ComboboxPrimitive.Collection.Props) { ); } -function ComboboxChips({ className, ...props }: ComboboxPrimitive.Chips.Props) { +function ComboboxChips({ + className, + children, + startAddon, + ...props +}: ComboboxPrimitive.Chips.Props & { + startAddon?: React.ReactNode; +}) { const { chipsRef } = React.useContext(ComboboxContext); return ( { + const target = e.target as HTMLElement; + const isChip = target.closest('[data-slot="combobox-chip"]'); + if (isChip || !chipsRef?.current) return; + e.preventDefault(); + const input: HTMLInputElement | null = + chipsRef.current.querySelector("input"); + if (input && !chipsRef.current.querySelector("input:focus")) { + input.focus(); + } + }} ref={chipsRef as React.Ref | null} {...props} - /> + > + {startAddon && ( + + )} + {children} + ); } @@ -341,6 +391,8 @@ function ComboboxChipRemove(props: ComboboxPrimitive.ChipRemove.Props) { ); } +const useComboboxFilter = ComboboxPrimitive.useFilter; + export { Combobox, ComboboxInput, @@ -359,4 +411,5 @@ export { ComboboxCollection, ComboboxChips, ComboboxChip, + useComboboxFilter, }; diff --git a/packages/coss-ui/src/components/command.tsx b/packages/coss-ui/src/components/command.tsx new file mode 100644 index 0000000000..8792d841d6 --- /dev/null +++ b/packages/coss-ui/src/components/command.tsx @@ -0,0 +1,264 @@ +"use client"; + +import { Dialog as CommandDialogPrimitive } from "@base-ui/react/dialog"; +import { SearchIcon } from "lucide-react"; +import type * as React from "react"; +import { cn } from "@coss/ui/lib/utils"; +import { + Autocomplete, + AutocompleteCollection, + AutocompleteEmpty, + AutocompleteGroup, + AutocompleteGroupLabel, + AutocompleteInput, + AutocompleteItem, + AutocompleteList, + AutocompleteSeparator, +} from "@coss/ui/components/autocomplete"; + +const CommandDialog = CommandDialogPrimitive.Root; + +const CommandDialogPortal = CommandDialogPrimitive.Portal; + +const CommandCreateHandle = CommandDialogPrimitive.createHandle; + +function CommandDialogTrigger(props: CommandDialogPrimitive.Trigger.Props) { + return ( + + ); +} + +function CommandDialogBackdrop({ + className, + ...props +}: CommandDialogPrimitive.Backdrop.Props) { + return ( + + ); +} + +function CommandDialogViewport({ + className, + ...props +}: CommandDialogPrimitive.Viewport.Props) { + return ( + + ); +} + +function CommandDialogPopup({ + className, + children, + ...props +}: CommandDialogPrimitive.Popup.Props) { + return ( + + + + + {children} + + + + ); +} + +function Command({ + autoHighlight = "always", + keepHighlight = true, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandInput({ + className, + placeholder = undefined, + ...props +}: React.ComponentProps) { + return ( +
    + } + {...props} + /> +
    + ); +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandEmpty({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandPanel({ className, ...props }: React.ComponentProps<"div">) { + return ( +
    + ); +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandGroupLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandCollection({ + ...props +}: React.ComponentProps) { + return ; +} + +function CommandItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandShortcut({ className, ...props }: React.ComponentProps<"kbd">) { + return ( + + ); +} + +function CommandFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
    + ); +} + +export { + CommandCreateHandle, + Command, + CommandCollection, + CommandDialog, + CommandDialogPopup, + CommandDialogTrigger, + CommandEmpty, + CommandFooter, + CommandGroup, + CommandGroupLabel, + CommandInput, + CommandItem, + CommandList, + CommandPanel, + CommandSeparator, + CommandShortcut, +}; diff --git a/packages/coss-ui/src/components/dialog.tsx b/packages/coss-ui/src/components/dialog.tsx index bd45c7d09c..1c402693d4 100644 --- a/packages/coss-ui/src/components/dialog.tsx +++ b/packages/coss-ui/src/components/dialog.tsx @@ -6,6 +6,8 @@ import { cn } from "@coss/ui/lib/utils"; import { Button } from "@coss/ui/components/button"; import { ScrollArea } from "@coss/ui/components/scroll-area"; +const DialogCreateHandle = DialogPrimitive.createHandle; + const Dialog = DialogPrimitive.Root; const DialogPortal = DialogPrimitive.Portal; @@ -41,7 +43,7 @@ function DialogViewport({ return ( - + @@ -144,9 +158,13 @@ function DialogDescription({ ); } -function DialogPanel({ className, ...props }: React.ComponentProps<"div">) { +function DialogPanel({ + className, + scrollFade = true, + ...props +}: React.ComponentProps<"div"> & { scrollFade?: boolean }) { return ( - +
    ) { } export { + DialogCreateHandle, Dialog, DialogTrigger, DialogPortal, diff --git a/packages/coss-ui/src/components/empty.tsx b/packages/coss-ui/src/components/empty.tsx index 020eb45903..eb07c04417 100644 --- a/packages/coss-ui/src/components/empty.tsx +++ b/packages/coss-ui/src/components/empty.tsx @@ -6,7 +6,7 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) { return (
    ) { return (
    @@ -95,7 +95,7 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { return (
    a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 [[data-slot=empty-title]+&]:mt-1", + "text-muted-foreground text-sm [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 [[data-slot=empty-title]+&]:mt-1", className, )} data-slot="empty-description" diff --git a/packages/coss-ui/src/components/field.tsx b/packages/coss-ui/src/components/field.tsx index d1840808dd..cd865aeebf 100644 --- a/packages/coss-ui/src/components/field.tsx +++ b/packages/coss-ui/src/components/field.tsx @@ -27,6 +27,16 @@ function FieldLabel({ className, ...props }: FieldPrimitive.Label.Props) { ); } +function FieldItem({ className, ...props }: FieldPrimitive.Item.Props) { + return ( + + ); +} + function FieldDescription({ className, ...props @@ -59,5 +69,6 @@ export { FieldDescription, FieldError, FieldControl, + FieldItem, FieldValidity, }; diff --git a/packages/coss-ui/src/components/frame.tsx b/packages/coss-ui/src/components/frame.tsx index 64be129bc4..a01fea3ab4 100644 --- a/packages/coss-ui/src/components/frame.tsx +++ b/packages/coss-ui/src/components/frame.tsx @@ -2,18 +2,12 @@ import type * as React from "react"; import { cn } from "@coss/ui/lib/utils"; -function Frame({ - className, - stackedPanels = false, - ...props -}: React.ComponentProps<"div"> & { stackedPanels?: boolean }) { +function Frame({ className, ...props }: React.ComponentProps<"div">) { return (
    ) { return (
    [data-slot=group]]:gap-2 *:has-focus-visible:z-10", + "flex w-fit *:focus-visible:z-1 has-[>[data-slot=group]]:gap-2 *:has-focus-visible:z-1 dark:*:[[data-slot=button]:hover~[data-slot=separator]:not([data-slot]:hover~[data-slot=separator]~[data-slot=separator]),[data-slot][data-pressed]~[data-slot=separator]:not([data-slot][data-pressed]~[data-slot=separator]~[data-slot=separator])]:before:bg-input/64 dark:*:[[data-slot=separator]:has(~[data-slot=button]:hover):not(:has(~[data-slot=separator]~[data-slot]:hover)),[data-slot=separator]:has(~[data-slot][data-pressed]):not(:has(~[data-slot=separator]~[data-slot][data-pressed]))]:before:bg-input/64", { defaultVariants: { orientation: "horizontal", @@ -15,9 +17,9 @@ const groupVariants = cva( variants: { orientation: { horizontal: - "*:not-first:before:-start-[0.5px] *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:before:-end-[0.5px] *:not-first:rounded-s-none *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:rounded-e-none *:not-first:border-s-0 *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:border-e-0 *:not-first:before:rounded-s-none *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:before:rounded-e-none *:after:absolute *:after:start-full *:after:h-full *:after:w-px *:pointer-coarse:after:min-w-auto", + "*:[[data-slot]~[data-slot]:not([data-slot=separator])]:before:-start-[0.5px] *:data-slot:not-data-[slot=separator]:has-[~[data-slot]]:before:-end-[0.5px] *:pointer-coarse:after:min-w-auto *:data-slot:has-[~[data-slot]]:rounded-e-none *:data-slot:has-[~[data-slot]]:border-e-0 *:data-slot:has-[~[data-slot]]:before:rounded-e-none *:[[data-slot]~[data-slot]]:rounded-s-none *:[[data-slot]~[data-slot]]:border-s-0 *:[[data-slot]~[data-slot]]:before:rounded-s-none", vertical: - "*:not-first:before:-top-[0.5px] *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:before:-bottom-[0.5px] flex-col *:not-first:rounded-t-none *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:rounded-b-none *:not-first:border-t-0 *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:border-b-0 *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:before:hidden *:not-first:before:rounded-t-none *:not-nth-last-[1_of_:not(span[data-base-ui-focus-guard],span[data-base-ui-inert])]:before:rounded-b-none *:after:absolute *:after:top-full *:after:h-px *:pointer-coarse:after:min-h-auto *:after:w-full dark:*:last:before:hidden dark:*:first:before:block", + "*:[[data-slot]~[data-slot]:not([data-slot=separator])]:before:-top-[0.5px] *:data-slot:not-data-[slot=separator]:has-[~[data-slot]]:before:-bottom-[0.5px] flex-col *:pointer-coarse:after:min-h-auto *:data-slot:has-[~[data-slot]]:rounded-b-none *:data-slot:has-[~[data-slot]]:border-b-0 *:data-slot:not-data-[slot=separator]:has-[~[data-slot]]:before:hidden *:data-slot:has-[~[data-slot]]:before:rounded-b-none dark:*:last:before:hidden dark:*:first:before:block *:[[data-slot]~[data-slot]]:rounded-t-none *:[[data-slot]~[data-slot]]:border-t-0 *:[[data-slot]~[data-slot]]:before:rounded-t-none", }, }, }, @@ -53,7 +55,7 @@ function GroupText({ }: useRender.ComponentProps<"div">) { const defaultProps = { className: cn( - "relative inline-flex items-center whitespace-nowrap rounded-lg border border-border bg-muted bg-clip-padding px-[calc(--spacing(3)-1px)] text-muted-foreground text-base sm:text-sm shadow-xs outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/64 dark:before:shadow-[0_-1px_--theme(--color-white/8%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 [&_svg]:-mx-0.5", + "relative inline-flex items-center whitespace-nowrap rounded-lg border border-input bg-muted not-dark:bg-clip-padding px-[calc(--spacing(3)-1px)] text-muted-foreground text-base sm:text-sm shadow-xs/5 outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:bg-input/64 dark:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 [&_svg]:-mx-0.5", className, ), "data-slot": "group-text", @@ -75,7 +77,7 @@ function GroupSeparator({ return ( ) { return (
    ) { } const inputGroupAddonVariants = cva( - "[&_svg]:-mx-0.5 not-has-[button]:**:[svg]:not([class*='opacity-'])]:opacity-80 flex h-auto cursor-text select-none items-center justify-center gap-2 [&>kbd]:rounded-[calc(var(--radius)-5px)] in-[[data-slot=input-group]:has([data-slot=input-control],[data-slot=textarea-control])]:[&_svg:not([class*='size-'])]:size-4.5 sm:in-[[data-slot=input-group]:has([data-slot=input-control],[data-slot=textarea-control])]:[&_svg:not([class*='size-'])]:size-4", + "[&_svg]:-mx-0.5 flex h-auto cursor-text select-none items-center justify-center gap-2 leading-none [&>kbd]:rounded-[calc(var(--radius)-5px)] in-[[data-slot=input-group]:has([data-slot=input-control],[data-slot=textarea-control])]:[&_svg:not([class*='size-'])]:size-4.5 sm:in-[[data-slot=input-group]:has([data-slot=input-control],[data-slot=textarea-control])]:[&_svg:not([class*='size-'])]:size-4 not-has-[button]:**:[svg:not([class*='opacity-'])]:opacity-80", { defaultVariants: { align: "inline-start", @@ -54,7 +54,9 @@ function InputGroupAddon({ data-slot="input-group-addon" onMouseDown={(e) => { const target = e.target as HTMLElement; - const isInteractive = target.closest("button, a"); + const isInteractive = target.closest( + "button, a, input, select, textarea, [role='button'], [role='combobox'], [role='listbox'], [data-slot='select-trigger']", + ); if (isInteractive) return; e.preventDefault(); const parent = e.currentTarget.parentElement; @@ -74,7 +76,7 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) { return ( & { size?: "sm" | "default" | "lg" | number; unstyled?: boolean; + nativeInput?: boolean; }; function Input({ className, size = "default", unstyled = false, + nativeInput = false, ...props }: InputProps) { + const inputClassName = cn( + "h-8.5 w-full min-w-0 rounded-[inherit] px-[calc(--spacing(3)-1px)] leading-8.5 outline-none placeholder:text-muted-foreground/72 sm:h-7.5 sm:leading-7.5", + size === "sm" && + "h-7.5 px-[calc(--spacing(2.5)-1px)] leading-7.5 sm:h-6.5 sm:leading-6.5", + size === "lg" && "h-9.5 leading-9.5 sm:h-8.5 sm:leading-8.5", + props.type === "search" && + "[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none", + props.type === "file" && + "text-muted-foreground file:me-3 file:bg-transparent file:font-medium file:text-foreground file:text-sm", + ); + return ( - + {nativeInput ? ( + + ) : ( + + )} ); } diff --git a/packages/coss-ui/src/components/label.tsx b/packages/coss-ui/src/components/label.tsx index 7cd0aaf549..ce1c56ce6b 100644 --- a/packages/coss-ui/src/components/label.tsx +++ b/packages/coss-ui/src/components/label.tsx @@ -1,3 +1,5 @@ +"use client"; + import { mergeProps } from "@base-ui/react/merge-props"; import { useRender } from "@base-ui/react/use-render"; diff --git a/packages/coss-ui/src/components/menu.tsx b/packages/coss-ui/src/components/menu.tsx index 7d2c90a3fe..a285a50c53 100644 --- a/packages/coss-ui/src/components/menu.tsx +++ b/packages/coss-ui/src/components/menu.tsx @@ -5,6 +5,8 @@ import { ChevronRightIcon } from "lucide-react"; import type * as React from "react"; import { cn } from "@coss/ui/lib/utils"; +const MenuCreateHandle = MenuPrimitive.createHandle; + const Menu = MenuPrimitive.Root; const MenuPortal = MenuPrimitive.Portal; @@ -39,7 +41,7 @@ function MenuPopup({ > svg]:-mx-0.5 flex min-h-8 cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-inset:ps-8 data-[variant=destructive]:text-destructive-foreground data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:shrink-0", className, )} data-inset={inset} @@ -85,34 +87,54 @@ function MenuCheckboxItem({ className, children, checked, + variant = "default", ...props -}: MenuPrimitive.CheckboxItem.Props) { +}: MenuPrimitive.CheckboxItem.Props & { + variant?: "default" | "switch"; +}) { return ( - - - - - - {children} + {variant === "switch" ? ( + <> + {children} + + + + + ) : ( + <> + + + + + + {children} + + )} ); } @@ -185,11 +207,11 @@ function MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) { ); } -function MenuShortcut({ className, ...props }: React.ComponentProps<"span">) { +function MenuShortcut({ className, ...props }: React.ComponentProps<"kbd">) { return ( - @@ -81,7 +81,7 @@ function SelectPopup({ > - +
    {children} - +
    diff --git a/packages/coss-ui/src/components/sidebar.tsx b/packages/coss-ui/src/components/sidebar.tsx index 4cd526a7cb..6a40c7b609 100644 --- a/packages/coss-ui/src/components/sidebar.tsx +++ b/packages/coss-ui/src/components/sidebar.tsx @@ -10,6 +10,7 @@ import { useIsMobile } from "@coss/ui/hooks/use-mobile"; import { cn } from "@coss/ui/lib/utils"; import { Button } from "@coss/ui/components/button"; import { Input } from "@coss/ui/components/input"; +import { ScrollArea } from "@coss/ui/components/scroll-area"; import { Separator } from "@coss/ui/components/separator"; import { Sheet, @@ -245,7 +246,7 @@ function Sidebar({ {...props} >
    @@ -313,7 +314,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
    ) { return ( -
    + +
    + ); } diff --git a/packages/coss-ui/src/components/slider.tsx b/packages/coss-ui/src/components/slider.tsx index b0a993bc57..31e45d8101 100644 --- a/packages/coss-ui/src/components/slider.tsx +++ b/packages/coss-ui/src/components/slider.tsx @@ -52,7 +52,7 @@ function Slider({ /> {Array.from({ length: _values.length }, (_, index) => ( diff --git a/packages/coss-ui/src/components/spinner.tsx b/packages/coss-ui/src/components/spinner.tsx index 8b059c873c..be8d21a999 100644 --- a/packages/coss-ui/src/components/spinner.tsx +++ b/packages/coss-ui/src/components/spinner.tsx @@ -1,8 +1,10 @@ import { Loader2Icon } from "lucide-react"; - import { cn } from "@coss/ui/lib/utils"; -function Spinner({ className, ...props }: React.ComponentProps) { +function Spinner({ + className, + ...props +}: React.ComponentProps) { return ( diff --git a/packages/coss-ui/src/components/table.tsx b/packages/coss-ui/src/components/table.tsx index f44af1e5a8..13a5026ac7 100644 --- a/packages/coss-ui/src/components/table.tsx +++ b/packages/coss-ui/src/components/table.tsx @@ -37,7 +37,7 @@ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { return ( ) { return ( tr]:last:border-b-0 in-data-[slot=frame]:*:[tr]:hover:bg-transparent", + "border-t in-data-[slot=frame]:border-none bg-muted/72 in-data-[slot=frame]:bg-transparent font-medium [&>tr]:last:border-b-0 in-data-[slot=frame]:*:[tr]:hover:bg-transparent", className, )} data-slot="table-footer" @@ -63,7 +63,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) { return ( ) { return ( ) { return ( @@ -58,13 +58,10 @@ function TabsTab({ className, ...props }: TabsPrimitive.Tab.Props) { return ( ); diff --git a/packages/coss-ui/src/components/textarea.tsx b/packages/coss-ui/src/components/textarea.tsx index 26871f5a10..ac565d4cd1 100644 --- a/packages/coss-ui/src/components/textarea.tsx +++ b/packages/coss-ui/src/components/textarea.tsx @@ -22,7 +22,7 @@ function Textarea({ className={ cn( !unstyled && - "relative inline-flex w-full rounded-lg border border-input bg-background bg-clip-padding text-base shadow-xs ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] sm:text-sm dark:bg-input/32 dark:bg-clip-border dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/8%)]", + "relative inline-flex w-full rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/6%)] sm:text-sm dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]", className, ) || undefined } diff --git a/packages/coss-ui/src/components/toast.tsx b/packages/coss-ui/src/components/toast.tsx index f09a19a487..38af438c06 100644 --- a/packages/coss-ui/src/components/toast.tsx +++ b/packages/coss-ui/src/components/toast.tsx @@ -76,7 +76,7 @@ function Toasts({ position = "bottom-right" }: { position: ToastPosition }) { return ( ) { return ( - + ); } diff --git a/packages/coss-ui/src/components/toggle.tsx b/packages/coss-ui/src/components/toggle.tsx index 380885319f..7d432b6366 100644 --- a/packages/coss-ui/src/components/toggle.tsx +++ b/packages/coss-ui/src/components/toggle.tsx @@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@coss/ui/lib/utils"; const toggleVariants = cva( - "[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-pressed:bg-accent data-pressed:text-accent-foreground data-pressed:transition-none sm:text-sm dark:data-pressed:bg-input/80 dark:hover:bg-accent [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", + "[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-pressed:bg-input/64 data-pressed:text-accent-foreground sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", { defaultVariants: { size: "default", @@ -21,7 +21,7 @@ const toggleVariants = cva( variant: { default: "border-transparent", outline: - "border-border bg-clip-padding shadow-xs not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:hover:bg-input/64 dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/8%)] dark:not-disabled:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/4%)] [:disabled,:active,[data-pressed]]:shadow-none", + "border-input bg-background not-dark:bg-clip-padding shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/6%)] dark:bg-input/32 dark:data-pressed:bg-input dark:hover:bg-input/64 dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] dark:not-disabled:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/2%)] [:disabled,:active,[data-pressed]]:shadow-none", }, }, }, diff --git a/packages/coss-ui/src/components/toolbar.tsx b/packages/coss-ui/src/components/toolbar.tsx index 61b9ad6b2e..857b92ce52 100644 --- a/packages/coss-ui/src/components/toolbar.tsx +++ b/packages/coss-ui/src/components/toolbar.tsx @@ -8,7 +8,7 @@ function Toolbar({ className, ...props }: ToolbarPrimitive.Root.Props) { return (