@ptengine/design-components 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -0
- package/components.json +21 -0
- package/dist/components/ui/accordion.d.ts +8 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +21 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert.d.ts +9 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +4 -0
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/components/ui/attachment.d.ts +26 -0
- package/dist/components/ui/attachment.d.ts.map +1 -0
- package/dist/components/ui/avatar.d.ts +10 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +10 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +22 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/bubble.d.ts +19 -0
- package/dist/components/ui/bubble.d.ts.map +1 -0
- package/dist/components/ui/button-group.d.ts +13 -0
- package/dist/components/ui/button-group.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +13 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/calendar.d.ts +9 -0
- package/dist/components/ui/calendar.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/carousel.d.ts +23 -0
- package/dist/components/ui/carousel.d.ts.map +1 -0
- package/dist/components/ui/chart.d.ts +63 -0
- package/dist/components/ui/chart.d.ts.map +1 -0
- package/dist/components/ui/checkbox.d.ts +5 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/collapsible.d.ts +6 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/command.d.ts +81 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/context-menu.d.ts +28 -0
- package/dist/components/ui/context-menu.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +22 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/direction.d.ts +8 -0
- package/dist/components/ui/direction.d.ts.map +1 -0
- package/dist/components/ui/drawer.d.ts +23 -0
- package/dist/components/ui/drawer.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/empty.d.ts +13 -0
- package/dist/components/ui/empty.d.ts.map +1 -0
- package/dist/components/ui/field.d.ts +25 -0
- package/dist/components/ui/field.d.ts.map +1 -0
- package/dist/components/ui/hover-card.d.ts +7 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/input-group.d.ts +13 -0
- package/dist/components/ui/input-group.d.ts.map +1 -0
- package/dist/components/ui/input-otp.d.ts +35 -0
- package/dist/components/ui/input-otp.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/item.d.ts +24 -0
- package/dist/components/ui/item.d.ts.map +1 -0
- package/dist/components/ui/kbd.d.ts +5 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/label.d.ts +6 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/marker.d.ts +13 -0
- package/dist/components/ui/marker.d.ts.map +1 -0
- package/dist/components/ui/menubar.d.ts +29 -0
- package/dist/components/ui/menubar.d.ts.map +1 -0
- package/dist/components/ui/message-scroller.d.ts +26 -0
- package/dist/components/ui/message-scroller.d.ts.map +1 -0
- package/dist/components/ui/message.d.ts +11 -0
- package/dist/components/ui/message.d.ts.map +1 -0
- package/dist/components/ui/native-select.d.ts +4 -0
- package/dist/components/ui/native-select.d.ts.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +13 -0
- package/dist/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/components/ui/pagination.d.ts +35 -0
- package/dist/components/ui/pagination.d.ts.map +1 -0
- package/dist/components/ui/popover.d.ts +8 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/progress.d.ts +5 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/radio-group.d.ts +6 -0
- package/dist/components/ui/radio-group.d.ts.map +1 -0
- package/dist/components/ui/resizable.d.ts +24 -0
- package/dist/components/ui/resizable.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +6 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/search-input.d.ts +23 -0
- package/dist/components/ui/search-input.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts +14 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts +28 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sidebar.d.ts +70 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/skeleton.d.ts +4 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/slider.d.ts +5 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/sonner.d.ts +6 -0
- package/dist/components/ui/sonner.d.ts.map +1 -0
- package/dist/components/ui/spinner.d.ts +7 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/switch.d.ts +5 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/table.d.ts +11 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +13 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +13 -0
- package/dist/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/components/ui/toggle.d.ts +13 -0
- package/dist/components/ui/toggle.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/index.cjs +82 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20969 -0
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/llms.txt +173 -0
- package/package.json +125 -0
- package/src/components/ui/accordion.tsx +51 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +48 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/attachment.tsx +186 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.tsx +40 -0
- package/src/components/ui/breadcrumb.tsx +101 -0
- package/src/components/ui/bubble.tsx +123 -0
- package/src/components/ui/button-group.tsx +74 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/calendar.tsx +63 -0
- package/src/components/ui/card.tsx +55 -0
- package/src/components/ui/carousel.tsx +252 -0
- package/src/components/ui/chart.tsx +323 -0
- package/src/components/ui/checkbox.tsx +26 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.tsx +136 -0
- package/src/components/ui/context-menu.tsx +187 -0
- package/src/components/ui/dialog.tsx +103 -0
- package/src/components/ui/direction.tsx +15 -0
- package/src/components/ui/drawer.tsx +98 -0
- package/src/components/ui/dropdown-menu.tsx +186 -0
- package/src/components/ui/empty.tsx +75 -0
- package/src/components/ui/field.tsx +175 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-group.tsx +80 -0
- package/src/components/ui/input-otp.tsx +67 -0
- package/src/components/ui/input.tsx +24 -0
- package/src/components/ui/item.tsx +118 -0
- package/src/components/ui/kbd.tsx +21 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/marker.tsx +67 -0
- package/src/components/ui/menubar.tsx +218 -0
- package/src/components/ui/message-scroller.tsx +206 -0
- package/src/components/ui/message.tsx +85 -0
- package/src/components/ui/native-select.tsx +29 -0
- package/src/components/ui/navigation-menu.tsx +120 -0
- package/src/components/ui/pagination.tsx +108 -0
- package/src/components/ui/popover.tsx +31 -0
- package/src/components/ui/progress.tsx +23 -0
- package/src/components/ui/radio-group.tsx +36 -0
- package/src/components/ui/resizable.tsx +40 -0
- package/src/components/ui/scroll-area.tsx +44 -0
- package/src/components/ui/search-input.tsx +97 -0
- package/src/components/ui/select.tsx +151 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/sheet.tsx +124 -0
- package/src/components/ui/sidebar.tsx +704 -0
- package/src/components/ui/skeleton.tsx +9 -0
- package/src/components/ui/slider.tsx +41 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/spinner.tsx +21 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +95 -0
- package/src/components/ui/tabs.tsx +119 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/toggle-group.tsx +55 -0
- package/src/components/ui/toggle.tsx +40 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +414 -0
- package/src/lib/utils.ts +7 -0
- package/src/styles/tokens.css +360 -0
- package/tailwind.preset.js +235 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
6
|
+
return <div className={cn('animate-pulse rounded-[var(--pt-radius-md)] bg-muted', className)} {...props} />;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { Skeleton };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
const Slider = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
|
9
|
+
>(({ className, defaultValue, value, min = 0, ...props }, ref) => {
|
|
10
|
+
const values = React.useMemo(
|
|
11
|
+
() => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min]),
|
|
12
|
+
[value, defaultValue, min]
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<SliderPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
defaultValue={defaultValue}
|
|
19
|
+
value={value}
|
|
20
|
+
min={min}
|
|
21
|
+
className={cn(
|
|
22
|
+
'relative flex w-full touch-none select-none items-center data-[disabled]:cursor-not-allowed',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
<SliderPrimitive.Track className="relative h-[6px] w-full grow overflow-hidden rounded-full bg-secondary">
|
|
28
|
+
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
|
29
|
+
</SliderPrimitive.Track>
|
|
30
|
+
{values.map((_, index) => (
|
|
31
|
+
<SliderPrimitive.Thumb
|
|
32
|
+
key={index}
|
|
33
|
+
className="block h-[16px] w-[16px] shrink-0 rounded-full border border-neutral bg-background transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring data-[disabled]:opacity-50"
|
|
34
|
+
/>
|
|
35
|
+
))}
|
|
36
|
+
</SliderPrimitive.Root>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
40
|
+
|
|
41
|
+
export { Slider };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Toaster as Sonner, toast } from 'sonner';
|
|
3
|
+
|
|
4
|
+
type ToasterProps = React.ComponentProps<typeof Sonner>;
|
|
5
|
+
|
|
6
|
+
/** Reads the current theme from the `dark` class on <html> (no next-themes dependency). */
|
|
7
|
+
function useDocumentTheme(): 'light' | 'dark' {
|
|
8
|
+
const [theme, setTheme] = React.useState<'light' | 'dark'>(() =>
|
|
9
|
+
typeof document !== 'undefined' && document.documentElement.classList.contains('dark')
|
|
10
|
+
? 'dark'
|
|
11
|
+
: 'light'
|
|
12
|
+
);
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
const observer = new MutationObserver(() =>
|
|
15
|
+
setTheme(document.documentElement.classList.contains('dark') ? 'dark' : 'light')
|
|
16
|
+
);
|
|
17
|
+
observer.observe(document.documentElement, {
|
|
18
|
+
attributes: true,
|
|
19
|
+
attributeFilter: ['class']
|
|
20
|
+
});
|
|
21
|
+
return () => observer.disconnect();
|
|
22
|
+
}, []);
|
|
23
|
+
return theme;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
27
|
+
const theme = useDocumentTheme();
|
|
28
|
+
return (
|
|
29
|
+
<Sonner
|
|
30
|
+
theme={theme}
|
|
31
|
+
className="toaster group"
|
|
32
|
+
toastOptions={{
|
|
33
|
+
classNames: {
|
|
34
|
+
toast: 'group toast group-[.toaster]:bg-popover group-[.toaster]:text-popover-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
|
35
|
+
description: 'group-[.toast]:text-muted-foreground',
|
|
36
|
+
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
|
37
|
+
cancelButton: 'group-[.toast]:bg-secondary group-[.toast]:text-secondary-foreground'
|
|
38
|
+
}
|
|
39
|
+
}}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { Toaster, toast };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Loader2 } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
function Spinner({
|
|
7
|
+
className,
|
|
8
|
+
label = 'Loading',
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof Loader2> & { label?: string }) {
|
|
11
|
+
return (
|
|
12
|
+
<Loader2
|
|
13
|
+
role="status"
|
|
14
|
+
aria-label={label}
|
|
15
|
+
className={cn('size-[16px] animate-spin text-foreground', className)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Spinner };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
const Switch = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
|
9
|
+
>(({ className, ...props }, ref) => (
|
|
10
|
+
<SwitchPrimitives.Root
|
|
11
|
+
className={cn(
|
|
12
|
+
'peer state-layer inline-flex h-[20px] w-[36px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input [--pt-state-layer-border:2px]',
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
ref={ref}
|
|
17
|
+
>
|
|
18
|
+
<SwitchPrimitives.Thumb
|
|
19
|
+
className={cn(
|
|
20
|
+
'pointer-events-none block h-[16px] w-[16px] rounded-full border border-neutral bg-background ring-0 transition-transform data-[state=checked]:translate-x-[16px] data-[state=unchecked]:translate-x-0'
|
|
21
|
+
)}
|
|
22
|
+
/>
|
|
23
|
+
</SwitchPrimitives.Root>
|
|
24
|
+
));
|
|
25
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
26
|
+
|
|
27
|
+
export { Switch };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
|
|
6
|
+
({ className, ...props }, ref) => (
|
|
7
|
+
<div className="relative w-full overflow-auto">
|
|
8
|
+
<table
|
|
9
|
+
ref={ref}
|
|
10
|
+
className={cn('w-full caption-bottom text-[14px] leading-[20px] text-foreground', className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
);
|
|
16
|
+
Table.displayName = 'Table';
|
|
17
|
+
|
|
18
|
+
const TableHeader = React.forwardRef<
|
|
19
|
+
HTMLTableSectionElement,
|
|
20
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
21
|
+
>(({ className, ...props }, ref) => (
|
|
22
|
+
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
|
23
|
+
));
|
|
24
|
+
TableHeader.displayName = 'TableHeader';
|
|
25
|
+
|
|
26
|
+
const TableBody = React.forwardRef<
|
|
27
|
+
HTMLTableSectionElement,
|
|
28
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
29
|
+
>(({ className, ...props }, ref) => (
|
|
30
|
+
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
|
|
31
|
+
));
|
|
32
|
+
TableBody.displayName = 'TableBody';
|
|
33
|
+
|
|
34
|
+
const TableFooter = React.forwardRef<
|
|
35
|
+
HTMLTableSectionElement,
|
|
36
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
37
|
+
>(({ className, ...props }, ref) => (
|
|
38
|
+
<tfoot
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn('border-t bg-secondary font-medium [&>tr]:last:border-b-0', className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
TableFooter.displayName = 'TableFooter';
|
|
45
|
+
|
|
46
|
+
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
|
|
47
|
+
({ className, ...props }, ref) => (
|
|
48
|
+
<tr
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn(
|
|
51
|
+
'state-layer border-b transition-colors data-[state=selected]:bg-selected',
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
TableRow.displayName = 'TableRow';
|
|
59
|
+
|
|
60
|
+
const TableHead = React.forwardRef<
|
|
61
|
+
HTMLTableCellElement,
|
|
62
|
+
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
63
|
+
>(({ className, ...props }, ref) => (
|
|
64
|
+
<th
|
|
65
|
+
ref={ref}
|
|
66
|
+
className={cn(
|
|
67
|
+
'h-[40px] px-[12px] text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
));
|
|
73
|
+
TableHead.displayName = 'TableHead';
|
|
74
|
+
|
|
75
|
+
const TableCell = React.forwardRef<
|
|
76
|
+
HTMLTableCellElement,
|
|
77
|
+
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
78
|
+
>(({ className, ...props }, ref) => (
|
|
79
|
+
<td
|
|
80
|
+
ref={ref}
|
|
81
|
+
className={cn('p-[12px] align-middle [&:has([role=checkbox])]:pr-0', className)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
));
|
|
85
|
+
TableCell.displayName = 'TableCell';
|
|
86
|
+
|
|
87
|
+
const TableCaption = React.forwardRef<
|
|
88
|
+
HTMLTableCaptionElement,
|
|
89
|
+
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
90
|
+
>(({ className, ...props }, ref) => (
|
|
91
|
+
<caption ref={ref} className={cn('mt-[16px] text-[14px] leading-[20px] text-muted-foreground', className)} {...props} />
|
|
92
|
+
));
|
|
93
|
+
TableCaption.displayName = 'TableCaption';
|
|
94
|
+
|
|
95
|
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
/* Tabs 两种形态,用来表达层级:
|
|
7
|
+
* line —— 下划线,分量重,给「页面级」主标签(概览 / 报表 / 设置)
|
|
8
|
+
* segmented —— 灰底胶囊,分量轻,给「区块内」切换视图(日 / 周 / 月)
|
|
9
|
+
* 嵌套时外层用 line、内层用 segmented,层级一眼可辨。
|
|
10
|
+
*
|
|
11
|
+
* 尺寸接三档刻度:sm 24(字12)· default 32(字13)· lg 40(字14)。
|
|
12
|
+
* variant / size 写在 <Tabs> 上一次,List 与 Trigger 通过 context 取。 */
|
|
13
|
+
type TabsVariant = 'segmented' | 'line';
|
|
14
|
+
type TabsSize = 'sm' | 'default' | 'lg';
|
|
15
|
+
|
|
16
|
+
const TabsContext = React.createContext<{ variant: TabsVariant; size: TabsSize }>({
|
|
17
|
+
variant: 'segmented',
|
|
18
|
+
size: 'default'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const LIST_SIZE: Record<TabsVariant, Record<TabsSize, string>> = {
|
|
22
|
+
segmented: {
|
|
23
|
+
sm: 'h-[24px] p-[2px]',
|
|
24
|
+
default: 'h-[32px] p-[4px]',
|
|
25
|
+
lg: 'h-[40px] p-[4px]'
|
|
26
|
+
},
|
|
27
|
+
line: {
|
|
28
|
+
sm: 'h-[24px] gap-[12px]',
|
|
29
|
+
default: 'h-[32px] gap-[16px]',
|
|
30
|
+
lg: 'h-[40px] gap-[20px]'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const TRIGGER_TEXT: Record<TabsSize, string> = {
|
|
35
|
+
sm: 'text-[12px] leading-[16px]',
|
|
36
|
+
default: 'text-[13px] leading-[20px]',
|
|
37
|
+
lg: 'text-[14px] leading-[20px]'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const Tabs = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof TabsPrimitive.Root>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root> & {
|
|
43
|
+
variant?: TabsVariant;
|
|
44
|
+
size?: TabsSize;
|
|
45
|
+
}
|
|
46
|
+
>(({ variant = 'segmented', size = 'default', ...props }, ref) => {
|
|
47
|
+
const ctx = React.useMemo(() => ({ variant, size }), [variant, size]);
|
|
48
|
+
return (
|
|
49
|
+
<TabsContext.Provider value={ctx}>
|
|
50
|
+
<TabsPrimitive.Root ref={ref} {...props} />
|
|
51
|
+
</TabsContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
Tabs.displayName = TabsPrimitive.Root.displayName;
|
|
55
|
+
|
|
56
|
+
const TabsList = React.forwardRef<
|
|
57
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
58
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
59
|
+
>(({ className, ...props }, ref) => {
|
|
60
|
+
const { variant, size } = React.useContext(TabsContext);
|
|
61
|
+
return (
|
|
62
|
+
<TabsPrimitive.List
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cn(
|
|
65
|
+
'inline-flex items-center text-muted-foreground',
|
|
66
|
+
variant === 'segmented' && 'justify-center rounded-[var(--pt-radius)] bg-muted',
|
|
67
|
+
/* line:整宽 + 底部分隔线,标签左对齐 */
|
|
68
|
+
variant === 'line' && 'w-full justify-start border-b border-border',
|
|
69
|
+
LIST_SIZE[variant][size],
|
|
70
|
+
className
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
77
|
+
|
|
78
|
+
const TabsTrigger = React.forwardRef<
|
|
79
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
80
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
81
|
+
>(({ className, ...props }, ref) => {
|
|
82
|
+
const { variant, size } = React.useContext(TabsContext);
|
|
83
|
+
return (
|
|
84
|
+
<TabsPrimitive.Trigger
|
|
85
|
+
ref={ref}
|
|
86
|
+
className={cn(
|
|
87
|
+
'inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
88
|
+
TRIGGER_TEXT[size],
|
|
89
|
+
/* segmented:选中项白底浮起。按设计铁律不加阴影——白底与灰底容器的
|
|
90
|
+
明度差已足够表达选中,阴影在暗色模式下还会失效。 */
|
|
91
|
+
variant === 'segmented' &&
|
|
92
|
+
'h-full rounded-[var(--pt-radius-sm)] px-[12px] data-[state=active]:bg-background data-[state=active]:text-foreground',
|
|
93
|
+
/* line:选中项底部 2px 实线,-mb-px 让它压住 List 的分隔线 */
|
|
94
|
+
variant === 'line' &&
|
|
95
|
+
'-mb-px h-full border-b-2 border-transparent px-[2px] data-[state=active]:border-foreground data-[state=active]:text-foreground',
|
|
96
|
+
className
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
103
|
+
|
|
104
|
+
const TabsContent = React.forwardRef<
|
|
105
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
106
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
107
|
+
>(({ className, ...props }, ref) => (
|
|
108
|
+
<TabsPrimitive.Content
|
|
109
|
+
ref={ref}
|
|
110
|
+
className={cn(
|
|
111
|
+
'mt-[8px] ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
));
|
|
117
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
118
|
+
|
|
119
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
|
6
|
+
|
|
7
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
8
|
+
({ className, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<textarea
|
|
11
|
+
className={cn(
|
|
12
|
+
'flex min-h-[60px] w-full rounded-[var(--pt-radius-md)] border border-input bg-transparent px-[12px] py-[8px] text-[14px] leading-[20px] placeholder:text-muted-foreground transition-colors enabled:hover:border-neutral focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:enabled:hover:border-destructive aria-[invalid=true]:focus-visible:ring-destructive/30',
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
ref={ref}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
Textarea.displayName = 'Textarea';
|
|
22
|
+
|
|
23
|
+
export { Textarea };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
|
|
3
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
import { toggleVariants } from './toggle';
|
|
7
|
+
|
|
8
|
+
const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
|
|
9
|
+
size: 'default',
|
|
10
|
+
variant: 'default'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const ToggleGroup = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
|
16
|
+
VariantProps<typeof toggleVariants>
|
|
17
|
+
>(({ className, variant, size, children, ...props }, ref) => (
|
|
18
|
+
<ToggleGroupPrimitive.Root
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn('flex items-center justify-center gap-[4px]', className)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
24
|
+
{children}
|
|
25
|
+
</ToggleGroupContext.Provider>
|
|
26
|
+
</ToggleGroupPrimitive.Root>
|
|
27
|
+
));
|
|
28
|
+
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
|
|
29
|
+
|
|
30
|
+
const ToggleGroupItem = React.forwardRef<
|
|
31
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
|
32
|
+
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
|
33
|
+
VariantProps<typeof toggleVariants>
|
|
34
|
+
>(({ className, children, variant, size, ...props }, ref) => {
|
|
35
|
+
const context = React.useContext(ToggleGroupContext);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<ToggleGroupPrimitive.Item
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(
|
|
41
|
+
toggleVariants({
|
|
42
|
+
variant: context.variant || variant,
|
|
43
|
+
size: context.size || size
|
|
44
|
+
}),
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
>
|
|
49
|
+
{children}
|
|
50
|
+
</ToggleGroupPrimitive.Item>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
|
|
54
|
+
|
|
55
|
+
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TogglePrimitive from '@radix-ui/react-toggle';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
'state-layer inline-flex items-center justify-center gap-[8px] rounded-[var(--pt-radius-md)] text-[14px] leading-[20px] font-medium text-foreground transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=on]:bg-selected data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-[16px] [&_svg]:shrink-0',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-transparent',
|
|
13
|
+
outline: 'border border-input bg-transparent [--pt-state-layer-border:1px]'
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: 'h-[32px] min-w-[36px] px-[8px] text-[13px] leading-[20px]',
|
|
17
|
+
sm: 'h-[24px] min-w-[32px] rounded-[var(--pt-radius-sm)] px-[6px] text-[12px] leading-[16px]',
|
|
18
|
+
lg: 'h-[40px] min-w-[40px] px-[10px] text-[14px] leading-[20px] [&_svg]:size-[20px]'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: 'default',
|
|
23
|
+
size: 'default'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const Toggle = React.forwardRef<
|
|
29
|
+
React.ElementRef<typeof TogglePrimitive.Root>,
|
|
30
|
+
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>
|
|
31
|
+
>(({ className, variant, size, ...props }, ref) => (
|
|
32
|
+
<TogglePrimitive.Root
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
));
|
|
38
|
+
Toggle.displayName = TogglePrimitive.Root.displayName;
|
|
39
|
+
|
|
40
|
+
export { Toggle, toggleVariants };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
7
|
+
|
|
8
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
11
|
+
|
|
12
|
+
const TooltipContent = React.forwardRef<
|
|
13
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
15
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
16
|
+
<TooltipPrimitive.Portal>
|
|
17
|
+
<TooltipPrimitive.Content
|
|
18
|
+
ref={ref}
|
|
19
|
+
sideOffset={sideOffset}
|
|
20
|
+
className={cn(
|
|
21
|
+
'z-[var(--pt-z-tooltip,50)] overflow-hidden rounded-[var(--pt-radius-md)] bg-primary px-[12px] py-[6px] text-[13px] leading-[20px] text-primary-foreground animate-in fade-in-0 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',
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
</TooltipPrimitive.Portal>
|
|
27
|
+
));
|
|
28
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
29
|
+
|
|
30
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
12
|
+
};
|
|
13
|
+
mql.addEventListener('change', onChange);
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
15
|
+
return () => mql.removeEventListener('change', onChange);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
return !!isMobile;
|
|
19
|
+
}
|