feat: add core UI components and baseline tests
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import {
|
||||
forwardRef,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ElementRef,
|
||||
type HTMLAttributes
|
||||
} from "react";
|
||||
|
||||
import {
|
||||
dropdownMenuContentVariants,
|
||||
dropdownMenuItemVariants,
|
||||
dropdownMenuLabelVariants,
|
||||
dropdownMenuSeparatorVariants
|
||||
} from "./dropdown-menu.variants";
|
||||
import { cn } from "../lib/cn";
|
||||
import type { VariantProps } from "../lib/cva";
|
||||
import { createDataAttributes, createSlot } from "../lib/contracts";
|
||||
|
||||
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
export type DropdownMenuContentProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> &
|
||||
VariantProps<typeof dropdownMenuContentVariants>;
|
||||
|
||||
export const DropdownMenuContent = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
DropdownMenuContentProps
|
||||
>(function DropdownMenuContent(
|
||||
{ className, sideOffset = 8, size, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
{...props}
|
||||
{...createSlot("content")}
|
||||
{...createDataAttributes({ size })}
|
||||
className={cn(dropdownMenuContentVariants({ size }), className)}
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
/>
|
||||
</DropdownMenuPortal>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuSubContentProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> &
|
||||
VariantProps<typeof dropdownMenuContentVariants>;
|
||||
|
||||
export const DropdownMenuSubContent = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
DropdownMenuSubContentProps
|
||||
>(function DropdownMenuSubContent(
|
||||
{ className, sideOffset = 10, size, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
{...props}
|
||||
{...createSlot("content")}
|
||||
{...createDataAttributes({ size })}
|
||||
className={cn(dropdownMenuContentVariants({ size }), className)}
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
/>
|
||||
</DropdownMenuPortal>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuItemProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> &
|
||||
VariantProps<typeof dropdownMenuItemVariants>;
|
||||
|
||||
export const DropdownMenuItem = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
DropdownMenuItemProps
|
||||
>(function DropdownMenuItem(
|
||||
{ className, inset, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
{...props}
|
||||
{...createSlot("item")}
|
||||
{...createDataAttributes({ inset, variant })}
|
||||
className={cn(dropdownMenuItemVariants({ inset, variant }), className)}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuCheckboxItemProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> &
|
||||
VariantProps<typeof dropdownMenuItemVariants>;
|
||||
|
||||
export const DropdownMenuCheckboxItem = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
DropdownMenuCheckboxItemProps
|
||||
>(function DropdownMenuCheckboxItem(
|
||||
{ checked, children, className, inset = true, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
{...props}
|
||||
checked={checked}
|
||||
{...createSlot("item")}
|
||||
{...createDataAttributes({ checked: checked === true, inset, variant })}
|
||||
className={cn(dropdownMenuItemVariants({ inset, variant }), className)}
|
||||
ref={ref}
|
||||
>
|
||||
<span
|
||||
{...createSlot("icon")}
|
||||
className="absolute left-2.5 inline-flex size-4 items-center justify-center text-xs"
|
||||
>
|
||||
<DropdownMenuPrimitive.ItemIndicator>✓</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuRadioItemProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> &
|
||||
VariantProps<typeof dropdownMenuItemVariants>;
|
||||
|
||||
export const DropdownMenuRadioItem = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
DropdownMenuRadioItemProps
|
||||
>(function DropdownMenuRadioItem(
|
||||
{ children, className, inset = true, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
{...props}
|
||||
{...createSlot("item")}
|
||||
{...createDataAttributes({ inset, variant })}
|
||||
className={cn(dropdownMenuItemVariants({ inset, variant }), className)}
|
||||
ref={ref}
|
||||
>
|
||||
<span
|
||||
{...createSlot("icon")}
|
||||
className="absolute left-2.5 inline-flex size-4 items-center justify-center text-xs"
|
||||
>
|
||||
<DropdownMenuPrimitive.ItemIndicator>•</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuLabelProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> &
|
||||
VariantProps<typeof dropdownMenuLabelVariants>;
|
||||
|
||||
export const DropdownMenuLabel = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
DropdownMenuLabelProps
|
||||
>(function DropdownMenuLabel({ className, inset, ...props }, ref) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Label
|
||||
{...props}
|
||||
{...createSlot("label")}
|
||||
{...createDataAttributes({ inset })}
|
||||
className={cn(dropdownMenuLabelVariants({ inset }), className)}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const DropdownMenuSeparator = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(function DropdownMenuSeparator({ className, ...props }, ref) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
{...props}
|
||||
{...createSlot("separator")}
|
||||
className={cn(dropdownMenuSeparatorVariants(), className)}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export type DropdownMenuSubTriggerProps =
|
||||
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> &
|
||||
VariantProps<typeof dropdownMenuItemVariants>;
|
||||
|
||||
export const DropdownMenuSubTrigger = forwardRef<
|
||||
ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
DropdownMenuSubTriggerProps
|
||||
>(function DropdownMenuSubTrigger(
|
||||
{ children, className, inset, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
{...props}
|
||||
{...createSlot("trigger")}
|
||||
{...createDataAttributes({ inset, variant })}
|
||||
className={cn(dropdownMenuItemVariants({ inset, variant }), className)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
<span className="ml-auto text-xs text-[var(--color-muted-foreground)]">›</span>
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
);
|
||||
});
|
||||
|
||||
export function DropdownMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLSpanElement>) {
|
||||
return (
|
||||
<span
|
||||
{...props}
|
||||
{...createSlot("shortcut")}
|
||||
className={cn(
|
||||
"ml-auto text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user