@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,118 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
import { Separator } from './separator';
|
|
7
|
+
|
|
8
|
+
function ItemGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return <div role="list" className={cn('group/item-group flex flex-col', className)} {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function ItemSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
|
|
13
|
+
return <Separator orientation="horizontal" className={cn('my-0', className)} {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const itemVariants = cva(
|
|
17
|
+
'group/item flex flex-wrap items-center rounded-[var(--pt-radius-md)] border text-[14px] leading-[20px] text-foreground outline-none transition-colors',
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: 'border-transparent',
|
|
22
|
+
outline: 'border-border',
|
|
23
|
+
muted: 'border-transparent bg-muted'
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
default: 'gap-[16px] p-[16px]',
|
|
27
|
+
sm: 'gap-[10px] px-[16px] py-[12px]'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: 'default',
|
|
32
|
+
size: 'default'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function Item({
|
|
38
|
+
className,
|
|
39
|
+
variant,
|
|
40
|
+
size,
|
|
41
|
+
asChild = false,
|
|
42
|
+
...props
|
|
43
|
+
}: React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof itemVariants> & { asChild?: boolean }) {
|
|
44
|
+
const Comp = asChild ? Slot : 'div';
|
|
45
|
+
return <Comp className={cn(itemVariants({ variant, size }), className)} {...props} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const itemMediaVariants = cva('flex shrink-0 items-center justify-center gap-[8px] [&_svg]:pointer-events-none', {
|
|
49
|
+
variants: {
|
|
50
|
+
variant: {
|
|
51
|
+
default: 'bg-transparent [&_svg]:size-[20px] [&_svg]:text-muted-foreground',
|
|
52
|
+
icon: 'size-[32px] rounded-[var(--pt-radius-sm)] border bg-secondary [&_svg]:size-[16px] [&_svg]:text-foreground',
|
|
53
|
+
image: 'size-[40px] overflow-hidden rounded-[var(--pt-radius-sm)] [&_img]:size-full [&_img]:object-cover'
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
defaultVariants: {
|
|
57
|
+
variant: 'default'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function ItemMedia({
|
|
62
|
+
className,
|
|
63
|
+
variant,
|
|
64
|
+
...props
|
|
65
|
+
}: React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof itemMediaVariants>) {
|
|
66
|
+
return <div className={cn(itemMediaVariants({ variant }), className)} {...props} />;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function ItemContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
70
|
+
return <div className={cn('flex flex-1 flex-col gap-[4px]', className)} {...props} />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ItemTitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
className={cn('flex w-fit items-center gap-[8px] text-[14px] leading-[20px] font-medium leading-snug', className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ItemDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
83
|
+
return (
|
|
84
|
+
<p
|
|
85
|
+
className={cn('line-clamp-2 text-[14px] leading-[20px] leading-normal text-muted-foreground', className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function ItemActions({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
92
|
+
return <div className={cn('flex items-center gap-[8px]', className)} {...props} />;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ItemHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
96
|
+
return (
|
|
97
|
+
<div className={cn('flex basis-full items-center justify-between gap-[8px]', className)} {...props} />
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ItemFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
102
|
+
return (
|
|
103
|
+
<div className={cn('flex basis-full items-center justify-between gap-[8px]', className)} {...props} />
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
Item,
|
|
109
|
+
ItemActions,
|
|
110
|
+
ItemContent,
|
|
111
|
+
ItemDescription,
|
|
112
|
+
ItemFooter,
|
|
113
|
+
ItemGroup,
|
|
114
|
+
ItemHeader,
|
|
115
|
+
ItemMedia,
|
|
116
|
+
ItemSeparator,
|
|
117
|
+
ItemTitle
|
|
118
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
function Kbd({ className, ...props }: React.HTMLAttributes<HTMLElement>) {
|
|
6
|
+
return (
|
|
7
|
+
<kbd
|
|
8
|
+
className={cn(
|
|
9
|
+
'pointer-events-none inline-flex h-[20px] w-fit min-w-[20px] select-none items-center justify-center gap-[4px] rounded-[var(--pt-radius-sm)] bg-muted px-[4px] font-sans text-[12px] leading-[16px] font-medium text-muted-foreground [&_svg]:size-[12px]',
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function KbdGroup({ className, ...props }: React.HTMLAttributes<HTMLElement>) {
|
|
18
|
+
return <span className={cn('inline-flex items-center gap-[4px]', className)} {...props} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Kbd, KbdGroup };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
'text-[14px] leading-[20px] font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
16
|
+
));
|
|
17
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
export { Label };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
|
|
7
|
+
const markerVariants = cva(
|
|
8
|
+
"group/marker relative flex min-h-[16px] w-full items-center gap-[8px] text-left text-[14px] leading-[20px] text-muted-foreground [&_svg:not([class*='size-'])]:size-[16px] [&:is(a)]:underline [&:is(a)]:underline-offset-4 [&:is(a):hover]:text-foreground",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: '',
|
|
13
|
+
separator:
|
|
14
|
+
'before:mr-[4px] before:h-px before:min-w-0 before:flex-1 before:bg-border after:ml-[4px] after:h-px after:min-w-0 after:flex-1 after:bg-border',
|
|
15
|
+
border: 'border-b border-border pb-[8px]'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
function Marker({
|
|
22
|
+
className,
|
|
23
|
+
variant = 'default',
|
|
24
|
+
asChild = false,
|
|
25
|
+
...props
|
|
26
|
+
}: React.HTMLAttributes<HTMLDivElement> &
|
|
27
|
+
VariantProps<typeof markerVariants> & {
|
|
28
|
+
/** Render the child element as the root (e.g. an <a>), keeping Marker styles. */
|
|
29
|
+
asChild?: boolean;
|
|
30
|
+
}) {
|
|
31
|
+
const Comp = asChild ? Slot : 'div';
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Comp
|
|
35
|
+
data-slot="marker"
|
|
36
|
+
data-variant={variant}
|
|
37
|
+
className={cn(markerVariants({ variant, className }))}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function MarkerIcon({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
44
|
+
return (
|
|
45
|
+
<span
|
|
46
|
+
data-slot="marker-icon"
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
className={cn("size-[16px] shrink-0 [&_svg:not([class*='size-'])]:size-[16px]", className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function MarkerContent({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
55
|
+
return (
|
|
56
|
+
<span
|
|
57
|
+
data-slot="marker-content"
|
|
58
|
+
className={cn(
|
|
59
|
+
'min-w-0 break-words group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center [&_a]:underline [&_a]:underline-offset-4 [&_a:hover]:text-foreground',
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { Marker, MarkerIcon, MarkerContent, markerVariants };
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as MenubarPrimitive from '@radix-ui/react-menubar';
|
|
3
|
+
import { Check, ChevronRight, Circle } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
|
|
7
|
+
function MenubarMenu(props: React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Menu>) {
|
|
8
|
+
return <MenubarPrimitive.Menu {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const MenubarGroup = MenubarPrimitive.Group;
|
|
12
|
+
|
|
13
|
+
const MenubarPortal = MenubarPrimitive.Portal;
|
|
14
|
+
|
|
15
|
+
const MenubarSub = MenubarPrimitive.Sub;
|
|
16
|
+
|
|
17
|
+
const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
|
|
18
|
+
|
|
19
|
+
const Menubar = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof MenubarPrimitive.Root>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<MenubarPrimitive.Root
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn('flex h-[32px] items-center gap-[4px] rounded-[var(--pt-radius-md)] border bg-background p-[4px]', className)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
));
|
|
29
|
+
Menubar.displayName = MenubarPrimitive.Root.displayName;
|
|
30
|
+
|
|
31
|
+
const MenubarTrigger = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof MenubarPrimitive.Trigger>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<MenubarPrimitive.Trigger
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(
|
|
38
|
+
'flex cursor-default select-none items-center rounded-[var(--pt-radius-sm)] px-[12px] py-[4px] text-[14px] leading-[20px] font-medium text-foreground outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
|
|
45
|
+
|
|
46
|
+
const MenubarSubTrigger = React.forwardRef<
|
|
47
|
+
React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
|
|
48
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
|
|
49
|
+
inset?: boolean;
|
|
50
|
+
}
|
|
51
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
52
|
+
<MenubarPrimitive.SubTrigger
|
|
53
|
+
ref={ref}
|
|
54
|
+
className={cn(
|
|
55
|
+
'flex cursor-default select-none items-center rounded-[var(--pt-radius-sm)] px-[8px] py-[6px] text-[14px] leading-[20px] outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
|
|
56
|
+
inset && 'pl-[32px]',
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
<ChevronRight className="ml-auto h-[16px] w-[16px]" />
|
|
63
|
+
</MenubarPrimitive.SubTrigger>
|
|
64
|
+
));
|
|
65
|
+
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
|
|
66
|
+
|
|
67
|
+
const MenubarSubContent = React.forwardRef<
|
|
68
|
+
React.ElementRef<typeof MenubarPrimitive.SubContent>,
|
|
69
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>
|
|
70
|
+
>(({ className, ...props }, ref) => (
|
|
71
|
+
<MenubarPrimitive.SubContent
|
|
72
|
+
ref={ref}
|
|
73
|
+
className={cn(
|
|
74
|
+
'z-[var(--pt-z-popup,50)] min-w-[8rem] overflow-hidden rounded-[var(--pt-radius-md)] border bg-popover p-[4px] text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
));
|
|
80
|
+
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
|
|
81
|
+
|
|
82
|
+
const MenubarContent = React.forwardRef<
|
|
83
|
+
React.ElementRef<typeof MenubarPrimitive.Content>,
|
|
84
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>
|
|
85
|
+
>(({ className, align = 'start', alignOffset = -4, sideOffset = 8, ...props }, ref) => (
|
|
86
|
+
<MenubarPrimitive.Portal>
|
|
87
|
+
<MenubarPrimitive.Content
|
|
88
|
+
ref={ref}
|
|
89
|
+
align={align}
|
|
90
|
+
alignOffset={alignOffset}
|
|
91
|
+
sideOffset={sideOffset}
|
|
92
|
+
className={cn(
|
|
93
|
+
'z-[var(--pt-z-popup,50)] min-w-[12rem] overflow-hidden rounded-[var(--pt-radius-md)] border bg-popover p-[4px] text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
</MenubarPrimitive.Portal>
|
|
99
|
+
));
|
|
100
|
+
MenubarContent.displayName = MenubarPrimitive.Content.displayName;
|
|
101
|
+
|
|
102
|
+
const MenubarItem = React.forwardRef<
|
|
103
|
+
React.ElementRef<typeof MenubarPrimitive.Item>,
|
|
104
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
|
|
105
|
+
inset?: boolean;
|
|
106
|
+
}
|
|
107
|
+
>(({ className, inset, ...props }, ref) => (
|
|
108
|
+
<MenubarPrimitive.Item
|
|
109
|
+
ref={ref}
|
|
110
|
+
className={cn(
|
|
111
|
+
'relative flex cursor-default select-none items-center rounded-[var(--pt-radius-sm)] px-[8px] py-[6px] text-[14px] leading-[20px] outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
112
|
+
inset && 'pl-[32px]',
|
|
113
|
+
className
|
|
114
|
+
)}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
));
|
|
118
|
+
MenubarItem.displayName = MenubarPrimitive.Item.displayName;
|
|
119
|
+
|
|
120
|
+
const MenubarCheckboxItem = React.forwardRef<
|
|
121
|
+
React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
|
|
122
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>
|
|
123
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
124
|
+
<MenubarPrimitive.CheckboxItem
|
|
125
|
+
ref={ref}
|
|
126
|
+
className={cn(
|
|
127
|
+
'relative flex cursor-default select-none items-center rounded-[var(--pt-radius-sm)] py-[6px] pl-[32px] pr-[8px] text-[14px] leading-[20px] outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
128
|
+
className
|
|
129
|
+
)}
|
|
130
|
+
checked={checked}
|
|
131
|
+
{...props}
|
|
132
|
+
>
|
|
133
|
+
<span className="absolute left-[8px] flex h-[14px] w-[14px] items-center justify-center">
|
|
134
|
+
<MenubarPrimitive.ItemIndicator>
|
|
135
|
+
<Check className="h-[16px] w-[16px]" />
|
|
136
|
+
</MenubarPrimitive.ItemIndicator>
|
|
137
|
+
</span>
|
|
138
|
+
{children}
|
|
139
|
+
</MenubarPrimitive.CheckboxItem>
|
|
140
|
+
));
|
|
141
|
+
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
|
|
142
|
+
|
|
143
|
+
const MenubarRadioItem = React.forwardRef<
|
|
144
|
+
React.ElementRef<typeof MenubarPrimitive.RadioItem>,
|
|
145
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>
|
|
146
|
+
>(({ className, children, ...props }, ref) => (
|
|
147
|
+
<MenubarPrimitive.RadioItem
|
|
148
|
+
ref={ref}
|
|
149
|
+
className={cn(
|
|
150
|
+
'relative flex cursor-default select-none items-center rounded-[var(--pt-radius-sm)] py-[6px] pl-[32px] pr-[8px] text-[14px] leading-[20px] outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
151
|
+
className
|
|
152
|
+
)}
|
|
153
|
+
{...props}
|
|
154
|
+
>
|
|
155
|
+
<span className="absolute left-[8px] flex h-[14px] w-[14px] items-center justify-center">
|
|
156
|
+
<MenubarPrimitive.ItemIndicator>
|
|
157
|
+
<Circle className="h-[8px] w-[8px] fill-current" />
|
|
158
|
+
</MenubarPrimitive.ItemIndicator>
|
|
159
|
+
</span>
|
|
160
|
+
{children}
|
|
161
|
+
</MenubarPrimitive.RadioItem>
|
|
162
|
+
));
|
|
163
|
+
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
|
|
164
|
+
|
|
165
|
+
const MenubarLabel = React.forwardRef<
|
|
166
|
+
React.ElementRef<typeof MenubarPrimitive.Label>,
|
|
167
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
|
|
168
|
+
inset?: boolean;
|
|
169
|
+
}
|
|
170
|
+
>(({ className, inset, ...props }, ref) => (
|
|
171
|
+
<MenubarPrimitive.Label
|
|
172
|
+
ref={ref}
|
|
173
|
+
className={cn('px-[8px] py-[6px] text-[14px] leading-[20px] font-semibold text-foreground', inset && 'pl-[32px]', className)}
|
|
174
|
+
{...props}
|
|
175
|
+
/>
|
|
176
|
+
));
|
|
177
|
+
MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
|
|
178
|
+
|
|
179
|
+
const MenubarSeparator = React.forwardRef<
|
|
180
|
+
React.ElementRef<typeof MenubarPrimitive.Separator>,
|
|
181
|
+
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>
|
|
182
|
+
>(({ className, ...props }, ref) => (
|
|
183
|
+
<MenubarPrimitive.Separator
|
|
184
|
+
ref={ref}
|
|
185
|
+
className={cn('-mx-[4px] my-[4px] h-px bg-muted', className)}
|
|
186
|
+
{...props}
|
|
187
|
+
/>
|
|
188
|
+
));
|
|
189
|
+
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
|
|
190
|
+
|
|
191
|
+
const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
192
|
+
return (
|
|
193
|
+
<span
|
|
194
|
+
className={cn('ml-auto text-[13px] leading-[20px] tracking-widest text-muted-foreground', className)}
|
|
195
|
+
{...props}
|
|
196
|
+
/>
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
MenubarShortcut.displayName = 'MenubarShortcut';
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
Menubar,
|
|
203
|
+
MenubarMenu,
|
|
204
|
+
MenubarTrigger,
|
|
205
|
+
MenubarContent,
|
|
206
|
+
MenubarItem,
|
|
207
|
+
MenubarSeparator,
|
|
208
|
+
MenubarLabel,
|
|
209
|
+
MenubarCheckboxItem,
|
|
210
|
+
MenubarRadioGroup,
|
|
211
|
+
MenubarRadioItem,
|
|
212
|
+
MenubarPortal,
|
|
213
|
+
MenubarSubContent,
|
|
214
|
+
MenubarSubTrigger,
|
|
215
|
+
MenubarGroup,
|
|
216
|
+
MenubarSub,
|
|
217
|
+
MenubarShortcut
|
|
218
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ArrowDown } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
import { Button } from './button';
|
|
6
|
+
|
|
7
|
+
const EDGE_THRESHOLD = 32;
|
|
8
|
+
|
|
9
|
+
type MessageScrollerContextValue = {
|
|
10
|
+
viewportRef: React.RefObject<HTMLDivElement | null>;
|
|
11
|
+
atStart: boolean;
|
|
12
|
+
atEnd: boolean;
|
|
13
|
+
scrollToEdge: (edge: 'start' | 'end', behavior?: ScrollBehavior) => void;
|
|
14
|
+
measure: () => void;
|
|
15
|
+
stickToEndRef: React.MutableRefObject<boolean>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const MessageScrollerContext = React.createContext<MessageScrollerContextValue | null>(null);
|
|
19
|
+
|
|
20
|
+
function useMessageScroller() {
|
|
21
|
+
const ctx = React.useContext(MessageScrollerContext);
|
|
22
|
+
if (!ctx) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'useMessageScroller must be used within <MessageScrollerProvider /> or <MessageScroller />'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return ctx;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function useMessageScrollerState(): MessageScrollerContextValue {
|
|
31
|
+
const viewportRef = React.useRef<HTMLDivElement | null>(null);
|
|
32
|
+
const stickToEndRef = React.useRef(true);
|
|
33
|
+
const [atStart, setAtStart] = React.useState(true);
|
|
34
|
+
const [atEnd, setAtEnd] = React.useState(true);
|
|
35
|
+
|
|
36
|
+
const measure = React.useCallback(() => {
|
|
37
|
+
const el = viewportRef.current;
|
|
38
|
+
if (!el) return;
|
|
39
|
+
const end = el.scrollHeight - el.scrollTop - el.clientHeight <= EDGE_THRESHOLD;
|
|
40
|
+
stickToEndRef.current = end;
|
|
41
|
+
setAtEnd(end);
|
|
42
|
+
setAtStart(el.scrollTop <= EDGE_THRESHOLD);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const scrollToEdge = React.useCallback(
|
|
46
|
+
(edge: 'start' | 'end', behavior: ScrollBehavior = 'smooth') => {
|
|
47
|
+
const el = viewportRef.current;
|
|
48
|
+
if (!el) return;
|
|
49
|
+
el.scrollTo({ top: edge === 'end' ? el.scrollHeight : 0, behavior });
|
|
50
|
+
},
|
|
51
|
+
[]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return React.useMemo(
|
|
55
|
+
() => ({ viewportRef, atStart, atEnd, scrollToEdge, measure, stickToEndRef }),
|
|
56
|
+
[atStart, atEnd, scrollToEdge, measure]
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 可选:把滚动状态提升到 MessageScroller 之外(如让按钮/输入框读取 atEnd)。 */
|
|
61
|
+
function MessageScrollerProvider({ children }: { children: React.ReactNode }) {
|
|
62
|
+
const value = useMessageScrollerState();
|
|
63
|
+
return (
|
|
64
|
+
<MessageScrollerContext.Provider value={value}>{children}</MessageScrollerContext.Provider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function MessageScroller({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
69
|
+
const outer = React.useContext(MessageScrollerContext);
|
|
70
|
+
const own = useMessageScrollerState();
|
|
71
|
+
return (
|
|
72
|
+
<MessageScrollerContext.Provider value={outer ?? own}>
|
|
73
|
+
<div
|
|
74
|
+
data-slot="message-scroller"
|
|
75
|
+
className={cn(
|
|
76
|
+
'group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden',
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
</MessageScrollerContext.Provider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function MessageScrollerViewport({
|
|
86
|
+
className,
|
|
87
|
+
onScroll,
|
|
88
|
+
children,
|
|
89
|
+
...props
|
|
90
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
91
|
+
const { viewportRef, measure, stickToEndRef, scrollToEdge } = useMessageScroller();
|
|
92
|
+
|
|
93
|
+
React.useLayoutEffect(() => {
|
|
94
|
+
scrollToEdge('end', 'auto');
|
|
95
|
+
measure();
|
|
96
|
+
}, [scrollToEdge, measure]);
|
|
97
|
+
|
|
98
|
+
React.useEffect(() => {
|
|
99
|
+
const el = viewportRef.current;
|
|
100
|
+
const content = el?.firstElementChild;
|
|
101
|
+
if (!el || !content) return;
|
|
102
|
+
const ro = new ResizeObserver(() => {
|
|
103
|
+
if (stickToEndRef.current) el.scrollTop = el.scrollHeight;
|
|
104
|
+
measure();
|
|
105
|
+
});
|
|
106
|
+
ro.observe(content);
|
|
107
|
+
return () => ro.disconnect();
|
|
108
|
+
}, [viewportRef, stickToEndRef, measure]);
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div
|
|
112
|
+
ref={viewportRef}
|
|
113
|
+
data-slot="message-scroller-viewport"
|
|
114
|
+
className={cn(
|
|
115
|
+
'size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain [contain:content]',
|
|
116
|
+
className
|
|
117
|
+
)}
|
|
118
|
+
onScroll={e => {
|
|
119
|
+
measure();
|
|
120
|
+
onScroll?.(e);
|
|
121
|
+
}}
|
|
122
|
+
{...props}
|
|
123
|
+
>
|
|
124
|
+
{children}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function MessageScrollerContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
130
|
+
return (
|
|
131
|
+
<div
|
|
132
|
+
data-slot="message-scroller-content"
|
|
133
|
+
className={cn('flex h-max min-h-full flex-col gap-[32px]', className)}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function MessageScrollerItem({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
140
|
+
return (
|
|
141
|
+
<div
|
|
142
|
+
data-slot="message-scroller-item"
|
|
143
|
+
className={cn(
|
|
144
|
+
'min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]',
|
|
145
|
+
className
|
|
146
|
+
)}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function MessageScrollerButton({
|
|
153
|
+
direction = 'end',
|
|
154
|
+
className,
|
|
155
|
+
children,
|
|
156
|
+
label,
|
|
157
|
+
variant = 'secondary',
|
|
158
|
+
size = 'icon',
|
|
159
|
+
onClick,
|
|
160
|
+
...props
|
|
161
|
+
}: React.ComponentProps<typeof Button> & {
|
|
162
|
+
direction?: 'start' | 'end';
|
|
163
|
+
/** 读屏文案,库不做 i18n,消费方传译文 */
|
|
164
|
+
label?: string;
|
|
165
|
+
}) {
|
|
166
|
+
const { atStart, atEnd, scrollToEdge } = useMessageScroller();
|
|
167
|
+
const active = direction === 'end' ? !atEnd : !atStart;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<Button
|
|
171
|
+
data-slot="message-scroller-button"
|
|
172
|
+
data-direction={direction}
|
|
173
|
+
data-active={active}
|
|
174
|
+
variant={variant}
|
|
175
|
+
size={size}
|
|
176
|
+
className={cn(
|
|
177
|
+
'absolute left-1/2 z-10 h-[32px] w-[32px] -translate-x-1/2 rounded-full transition-[opacity,transform] duration-200 data-[direction=end]:bottom-[16px] data-[direction=start]:top-[16px] data-[active=false]:pointer-events-none data-[active=false]:opacity-0 data-[active=true]:opacity-100 data-[direction=start]:[&_svg]:rotate-180',
|
|
178
|
+
className
|
|
179
|
+
)}
|
|
180
|
+
onClick={e => {
|
|
181
|
+
onClick?.(e);
|
|
182
|
+
if (!e.defaultPrevented) scrollToEdge(direction);
|
|
183
|
+
}}
|
|
184
|
+
{...props}
|
|
185
|
+
>
|
|
186
|
+
{children ?? (
|
|
187
|
+
<>
|
|
188
|
+
<ArrowDown />
|
|
189
|
+
<span className="sr-only">
|
|
190
|
+
{label ?? (direction === 'end' ? 'Scroll to end' : 'Scroll to start')}
|
|
191
|
+
</span>
|
|
192
|
+
</>
|
|
193
|
+
)}
|
|
194
|
+
</Button>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export {
|
|
199
|
+
MessageScrollerProvider,
|
|
200
|
+
MessageScroller,
|
|
201
|
+
MessageScrollerViewport,
|
|
202
|
+
MessageScrollerContent,
|
|
203
|
+
MessageScrollerItem,
|
|
204
|
+
MessageScrollerButton,
|
|
205
|
+
useMessageScroller
|
|
206
|
+
};
|