mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
feat: add Accordion component with customizable behavior and animations, update Checkbox and Slider components for improved functionality, and enhance package dependencies
This commit is contained in:
@@ -40,7 +40,6 @@
|
|||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@lezer/highlight": "^1.2.3",
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@radix-ui/react-accordion": "^1.2.12",
|
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
|
|||||||
@@ -2522,29 +2522,33 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accordion animations */
|
/* Accordion animations - CSS-only approach */
|
||||||
@keyframes accordion-down {
|
@keyframes accordion-down {
|
||||||
from {
|
from {
|
||||||
height: 0;
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
height: var(--radix-accordion-content-height);
|
height: var(--accordion-content-height, auto);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes accordion-up {
|
@keyframes accordion-up {
|
||||||
from {
|
from {
|
||||||
height: var(--radix-accordion-content-height);
|
height: var(--accordion-content-height, auto);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
height: 0;
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-accordion-down {
|
.animate-accordion-down {
|
||||||
animation: accordion-down 0.2s ease-out;
|
animation: accordion-down 0.2s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-accordion-up {
|
.animate-accordion-up {
|
||||||
animation: accordion-up 0.2s ease-out;
|
animation: accordion-up 0.2s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,243 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Accordion = AccordionPrimitive.Root;
|
type AccordionType = "single" | "multiple";
|
||||||
|
|
||||||
const AccordionItem = React.forwardRef<
|
interface AccordionContextValue {
|
||||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
type: AccordionType;
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
value: string | string[];
|
||||||
>(({ className, ...props }, ref) => (
|
onValueChange: (value: string) => void;
|
||||||
<AccordionPrimitive.Item
|
collapsible?: boolean;
|
||||||
ref={ref}
|
}
|
||||||
className={cn("border-b border-border", className)}
|
|
||||||
{...props}
|
const AccordionContext = React.createContext<AccordionContextValue | null>(
|
||||||
/>
|
null
|
||||||
));
|
);
|
||||||
|
|
||||||
|
interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
type?: "single" | "multiple";
|
||||||
|
value?: string | string[];
|
||||||
|
defaultValue?: string | string[];
|
||||||
|
onValueChange?: (value: string | string[]) => void;
|
||||||
|
collapsible?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
type = "single",
|
||||||
|
value,
|
||||||
|
defaultValue,
|
||||||
|
onValueChange,
|
||||||
|
collapsible = false,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const [internalValue, setInternalValue] = React.useState<string | string[]>(
|
||||||
|
() => {
|
||||||
|
if (value !== undefined) return value;
|
||||||
|
if (defaultValue !== undefined) return defaultValue;
|
||||||
|
return type === "single" ? "" : [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentValue = value !== undefined ? value : internalValue;
|
||||||
|
|
||||||
|
const handleValueChange = React.useCallback(
|
||||||
|
(itemValue: string) => {
|
||||||
|
let newValue: string | string[];
|
||||||
|
|
||||||
|
if (type === "single") {
|
||||||
|
if (currentValue === itemValue && collapsible) {
|
||||||
|
newValue = "";
|
||||||
|
} else if (currentValue === itemValue && !collapsible) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
newValue = itemValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentArray = Array.isArray(currentValue)
|
||||||
|
? currentValue
|
||||||
|
: [currentValue].filter(Boolean);
|
||||||
|
if (currentArray.includes(itemValue)) {
|
||||||
|
newValue = currentArray.filter((v) => v !== itemValue);
|
||||||
|
} else {
|
||||||
|
newValue = [...currentArray, itemValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
onValueChange?.(newValue);
|
||||||
|
},
|
||||||
|
[type, currentValue, collapsible, value, onValueChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const contextValue = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
type,
|
||||||
|
value: currentValue,
|
||||||
|
onValueChange: handleValueChange,
|
||||||
|
collapsible,
|
||||||
|
}),
|
||||||
|
[type, currentValue, handleValueChange, collapsible]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccordionContext.Provider value={contextValue}>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
data-slot="accordion"
|
||||||
|
className={cn("w-full", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</AccordionContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Accordion.displayName = "Accordion";
|
||||||
|
|
||||||
|
interface AccordionItemContextValue {
|
||||||
|
value: string;
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccordionItemContext =
|
||||||
|
React.createContext<AccordionItemContextValue | null>(null);
|
||||||
|
|
||||||
|
interface AccordionItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
||||||
|
({ className, value, children, ...props }, ref) => {
|
||||||
|
const accordionContext = React.useContext(AccordionContext);
|
||||||
|
|
||||||
|
if (!accordionContext) {
|
||||||
|
throw new Error("AccordionItem must be used within an Accordion");
|
||||||
|
}
|
||||||
|
|
||||||
|
const isOpen = Array.isArray(accordionContext.value)
|
||||||
|
? accordionContext.value.includes(value)
|
||||||
|
: accordionContext.value === value;
|
||||||
|
|
||||||
|
const contextValue = React.useMemo(
|
||||||
|
() => ({ value, isOpen }),
|
||||||
|
[value, isOpen]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccordionItemContext.Provider value={contextValue}>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
data-slot="accordion-item"
|
||||||
|
data-state={isOpen ? "open" : "closed"}
|
||||||
|
className={cn("border-b border-border", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</AccordionItemContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
AccordionItem.displayName = "AccordionItem";
|
AccordionItem.displayName = "AccordionItem";
|
||||||
|
|
||||||
const AccordionTrigger = React.forwardRef<
|
interface AccordionTriggerProps
|
||||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
||||||
>(({ className, children, ...props }, ref) => (
|
|
||||||
<AccordionPrimitive.Header className="flex">
|
|
||||||
<AccordionPrimitive.Trigger
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
||||||
</AccordionPrimitive.Trigger>
|
|
||||||
</AccordionPrimitive.Header>
|
|
||||||
));
|
|
||||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
||||||
|
|
||||||
const AccordionContent = React.forwardRef<
|
const AccordionTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
HTMLButtonElement,
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
AccordionTriggerProps
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => {
|
||||||
<AccordionPrimitive.Content
|
const accordionContext = React.useContext(AccordionContext);
|
||||||
ref={ref}
|
const itemContext = React.useContext(AccordionItemContext);
|
||||||
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
||||||
{...props}
|
if (!accordionContext || !itemContext) {
|
||||||
>
|
throw new Error("AccordionTrigger must be used within an AccordionItem");
|
||||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
}
|
||||||
</AccordionPrimitive.Content>
|
|
||||||
));
|
const { onValueChange } = accordionContext;
|
||||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
const { value, isOpen } = itemContext;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-slot="accordion-header" className="flex">
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
type="button"
|
||||||
|
data-slot="accordion-trigger"
|
||||||
|
data-state={isOpen ? "open" : "closed"}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
onClick={() => onValueChange(value)}
|
||||||
|
className={cn(
|
||||||
|
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
AccordionTrigger.displayName = "AccordionTrigger";
|
||||||
|
|
||||||
|
interface AccordionContentProps extends React.HTMLAttributes<HTMLDivElement> {}
|
||||||
|
|
||||||
|
const AccordionContent = React.forwardRef<HTMLDivElement, AccordionContentProps>(
|
||||||
|
({ className, children, ...props }, ref) => {
|
||||||
|
const itemContext = React.useContext(AccordionItemContext);
|
||||||
|
const contentRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
const [height, setHeight] = React.useState<number | undefined>(undefined);
|
||||||
|
|
||||||
|
if (!itemContext) {
|
||||||
|
throw new Error("AccordionContent must be used within an AccordionItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { isOpen } = itemContext;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (contentRef.current) {
|
||||||
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
setHeight(entry.contentRect.height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver.observe(contentRef.current);
|
||||||
|
return () => resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="accordion-content"
|
||||||
|
data-state={isOpen ? "open" : "closed"}
|
||||||
|
className="overflow-hidden text-sm transition-all duration-200 ease-out"
|
||||||
|
style={{
|
||||||
|
height: isOpen ? (height !== undefined ? `${height}px` : "auto") : 0,
|
||||||
|
opacity: isOpen ? 1 : 0,
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div ref={contentRef}>
|
||||||
|
<div ref={ref} className={cn("pb-4 pt-0", className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
AccordionContent.displayName = "AccordionContent";
|
||||||
|
|
||||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||||
|
|
||||||
|
|||||||
@@ -6,25 +6,37 @@ import { Check } from "lucide-react";
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Checkbox = React.forwardRef<
|
interface CheckboxProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "checked" | "defaultChecked"> {
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
checked?: boolean | "indeterminate";
|
||||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
defaultChecked?: boolean | "indeterminate";
|
||||||
>(({ className, ...props }, ref) => (
|
onCheckedChange?: (checked: boolean) => void;
|
||||||
<CheckboxPrimitive.Root
|
required?: boolean;
|
||||||
ref={ref}
|
}
|
||||||
className={cn(
|
|
||||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground hover:border-primary/80",
|
const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
|
||||||
className
|
({ className, onCheckedChange, ...props }, ref) => (
|
||||||
)}
|
<CheckboxPrimitive.Root
|
||||||
{...props}
|
ref={ref}
|
||||||
>
|
className={cn(
|
||||||
<CheckboxPrimitive.Indicator
|
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground hover:border-primary/80",
|
||||||
className={cn("flex items-center justify-center text-current")}
|
className
|
||||||
|
)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
// Handle indeterminate state by treating it as false for consumers expecting boolean
|
||||||
|
if (onCheckedChange) {
|
||||||
|
onCheckedChange(checked === true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
<Check className="h-4 w-4" />
|
<CheckboxPrimitive.Indicator
|
||||||
</CheckboxPrimitive.Indicator>
|
className={cn("flex items-center justify-center text-current")}
|
||||||
</CheckboxPrimitive.Root>
|
>
|
||||||
));
|
<Check className="h-4 w-4" />
|
||||||
|
</CheckboxPrimitive.Indicator>
|
||||||
|
</CheckboxPrimitive.Root>
|
||||||
|
)
|
||||||
|
);
|
||||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||||
|
|
||||||
export { Checkbox };
|
export { Checkbox };
|
||||||
|
|||||||
@@ -1,39 +1,43 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||||
import { XIcon } from "lucide-react"
|
import { XIcon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTrigger({
|
function SheetTrigger({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetClose({
|
function SheetClose({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetPortal({
|
function SheetPortal({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetOverlay({
|
interface SheetOverlayProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
className,
|
forceMount?: true;
|
||||||
...props
|
}
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
|
||||||
|
const SheetOverlay = ({ className, ...props }: SheetOverlayProps) => {
|
||||||
|
const Overlay = SheetPrimitive.Overlay as React.ComponentType<
|
||||||
|
SheetOverlayProps & { "data-slot": string }
|
||||||
|
>;
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Overlay
|
<Overlay
|
||||||
data-slot="sheet-overlay"
|
data-slot="sheet-overlay"
|
||||||
className={cn(
|
className={cn(
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
@@ -41,23 +45,35 @@ function SheetOverlay({
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface SheetContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
|
forceMount?: true;
|
||||||
|
onEscapeKeyDown?: (event: KeyboardEvent) => void;
|
||||||
|
onPointerDownOutside?: (event: PointerEvent) => void;
|
||||||
|
onInteractOutside?: (event: Event) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetContent({
|
const SheetContent = ({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
side = "right",
|
side = "right",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
}: SheetContentProps) => {
|
||||||
side?: "top" | "right" | "bottom" | "left"
|
const Content = SheetPrimitive.Content as React.ComponentType<
|
||||||
children?: React.ReactNode
|
SheetContentProps & { "data-slot": string }
|
||||||
className?: string
|
>;
|
||||||
}) {
|
const Close = SheetPrimitive.Close as React.ComponentType<{
|
||||||
|
className: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SheetPortal>
|
<SheetPortal>
|
||||||
<SheetOverlay />
|
<SheetOverlay />
|
||||||
<SheetPrimitive.Content
|
<Content
|
||||||
data-slot="sheet-content"
|
data-slot="sheet-content"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||||
@@ -74,14 +90,14 @@ function SheetContent({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
<Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||||
<XIcon className="size-4" />
|
<XIcon className="size-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</SheetPrimitive.Close>
|
</Close>
|
||||||
</SheetPrimitive.Content>
|
</Content>
|
||||||
</SheetPortal>
|
</SheetPortal>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
@@ -90,7 +106,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
@@ -100,34 +116,39 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTitle({
|
interface SheetTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}
|
||||||
className,
|
|
||||||
...props
|
const SheetTitle = ({ className, ...props }: SheetTitleProps) => {
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
const Title = SheetPrimitive.Title as React.ComponentType<
|
||||||
|
SheetTitleProps & { "data-slot": string }
|
||||||
|
>;
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Title
|
<Title
|
||||||
data-slot="sheet-title"
|
data-slot="sheet-title"
|
||||||
className={cn("text-foreground font-semibold", className)}
|
className={cn("text-foreground font-semibold", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function SheetDescription({
|
interface SheetDescriptionProps
|
||||||
className,
|
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
const SheetDescription = ({ className, ...props }: SheetDescriptionProps) => {
|
||||||
|
const Description = SheetPrimitive.Description as React.ComponentType<
|
||||||
|
SheetDescriptionProps & { "data-slot": string }
|
||||||
|
>;
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Description
|
<Description
|
||||||
data-slot="sheet-description"
|
data-slot="sheet-description"
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Sheet,
|
Sheet,
|
||||||
@@ -138,4 +159,4 @@ export {
|
|||||||
SheetFooter,
|
SheetFooter,
|
||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetDescription,
|
SheetDescription,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -4,24 +4,38 @@ import * as React from "react";
|
|||||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Slider = React.forwardRef<
|
interface SliderProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "defaultValue" | "dir"> {
|
||||||
React.ComponentRef<typeof SliderPrimitive.Root>,
|
value?: number[];
|
||||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
defaultValue?: number[];
|
||||||
>(({ className, ...props }, ref) => (
|
onValueChange?: (value: number[]) => void;
|
||||||
<SliderPrimitive.Root
|
onValueCommit?: (value: number[]) => void;
|
||||||
ref={ref}
|
min?: number;
|
||||||
className={cn(
|
max?: number;
|
||||||
"relative flex w-full touch-none select-none items-center",
|
step?: number;
|
||||||
className
|
disabled?: boolean;
|
||||||
)}
|
orientation?: "horizontal" | "vertical";
|
||||||
{...props}
|
dir?: "ltr" | "rtl";
|
||||||
>
|
inverted?: boolean;
|
||||||
<SliderPrimitive.Track className="slider-track relative h-1.5 w-full grow overflow-hidden rounded-full bg-muted cursor-pointer">
|
minStepsBetweenThumbs?: number;
|
||||||
<SliderPrimitive.Range className="slider-range absolute h-full bg-primary" />
|
}
|
||||||
</SliderPrimitive.Track>
|
|
||||||
<SliderPrimitive.Thumb className="slider-thumb block h-4 w-4 rounded-full border border-border bg-card shadow transition-colors cursor-grab active:cursor-grabbing focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed hover:bg-accent" />
|
const Slider = React.forwardRef<HTMLSpanElement, SliderProps>(
|
||||||
</SliderPrimitive.Root>
|
({ className, ...props }, ref) => (
|
||||||
));
|
<SliderPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full touch-none select-none items-center",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SliderPrimitive.Track className="slider-track relative h-1.5 w-full grow overflow-hidden rounded-full bg-muted cursor-pointer">
|
||||||
|
<SliderPrimitive.Range className="slider-range absolute h-full bg-primary" />
|
||||||
|
</SliderPrimitive.Track>
|
||||||
|
<SliderPrimitive.Thumb className="slider-thumb block h-4 w-4 rounded-full border border-border bg-card shadow transition-colors cursor-grab active:cursor-grabbing focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed hover:bg-accent" />
|
||||||
|
</SliderPrimitive.Root>
|
||||||
|
)
|
||||||
|
);
|
||||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||||
|
|
||||||
export { Slider };
|
export { Slider };
|
||||||
|
|||||||
22194
package-lock.json
generated
22194
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,5 +29,8 @@
|
|||||||
"test:headed": "npm run test:headed --workspace=apps/app",
|
"test:headed": "npm run test:headed --workspace=apps/app",
|
||||||
"test:server": "npm run test --workspace=apps/server",
|
"test:server": "npm run test --workspace=apps/server",
|
||||||
"test:server:coverage": "npm run test:cov --workspace=apps/server"
|
"test:server:coverage": "npm run test:cov --workspace=apps/server"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "^7.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user