@hanzo/ui 4.5.4 → 4.6.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-MCP.md +3 -3
- package/README.md +229 -0
- package/assets/ai-icons.tsx +207 -0
- package/assets/crypto.tsx +33 -0
- package/assets/file-type-icon.tsx +66 -0
- package/assets/file.tsx +45 -0
- package/assets/general.tsx +2318 -0
- package/assets/hanzo-logo.svg +9 -0
- package/assets/hanzo-logo.tsx +15 -0
- package/assets/index.ts +8 -0
- package/assets/index.tsx +4 -0
- package/assets/llm-provider.tsx +1094 -0
- package/bin/create-registry.js +1 -1
- package/bin/test-mcp.sh +1 -1
- package/bin/update-registry.js +2 -2
- package/blocks/components/content.tsx +1 -1
- package/blocks/components/grid-block/index.tsx +1 -1
- package/blocks/components/screenful-block/content.tsx +1 -1
- package/blocks/components/screenful-block/poster-background.tsx +1 -1
- package/components/index.ts +56 -0
- package/dist/button.d.ts +1 -0
- package/dist/button.js +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/use-click-away.d.ts +2 -0
- package/dist/hooks/use-click-away.js +23 -0
- package/dist/hooks/use-combined-refs.d.ts +3 -0
- package/dist/hooks/use-combined-refs.js +18 -0
- package/dist/hooks/use-copy-clipboard.d.ts +9 -0
- package/dist/hooks/use-copy-clipboard.js +21 -0
- package/dist/hooks/use-debounce.d.ts +1 -0
- package/dist/hooks/use-debounce.js +13 -0
- package/dist/hooks/use-fill-ids.d.ts +8 -0
- package/dist/hooks/use-fill-ids.js +20 -0
- package/dist/hooks/use-map.d.ts +1 -0
- package/dist/hooks/use-map.js +20 -0
- package/dist/hooks/use-measure.d.ts +8 -0
- package/dist/hooks/use-measure.js +25 -0
- package/dist/hooks/use-reverse-video-playback.d.ts +1 -0
- package/dist/hooks/use-reverse-video-playback.js +41 -0
- package/dist/hooks/use-scroll-restoration.d.ts +8 -0
- package/dist/hooks/use-scroll-restoration.js +36 -0
- package/dist/mcp/enhanced-server.js +2 -2
- package/dist/registry/api.d.ts +1 -1
- package/dist/registry/api.js +3 -3
- package/dist/registry/index.d.ts +48 -48
- package/dist/registry/index.js +3 -3
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/helpers/file.ts +33 -0
- package/helpers/memoization.ts +40 -0
- package/package.json +27 -6
- package/primitives/accordion.tsx +53 -45
- package/primitives/alert-dialog.tsx +185 -0
- package/primitives/alert.tsx +74 -0
- package/primitives/apply-typography.tsx +1 -1
- package/primitives/avatar.tsx +37 -29
- package/primitives/background-beams.tsx +142 -0
- package/primitives/badge.tsx +27 -19
- package/primitives/breadcrumb.tsx +77 -62
- package/primitives/button.tsx +69 -72
- package/primitives/card.tsx +73 -59
- package/primitives/chat/chat-input-area.tsx +87 -0
- package/primitives/chat/chat-input.tsx +71 -0
- package/primitives/chat/files-preview.tsx +330 -0
- package/primitives/chat/index.ts +6 -0
- package/primitives/chat/json-form.tsx +8 -0
- package/primitives/chat/message-list.tsx +307 -0
- package/primitives/chat/message.tsx +569 -0
- package/primitives/chat/sqlite-preview.tsx +215 -0
- package/primitives/checkbox.tsx +18 -19
- package/primitives/collapsible.tsx +9 -0
- package/primitives/command.tsx +75 -83
- package/primitives/context-menu.tsx +115 -109
- package/primitives/copy-to-clipboard-icon.tsx +60 -0
- package/primitives/dialog-video-controller.tsx +1 -1
- package/primitives/dialog.tsx +111 -145
- package/primitives/dot-pattern.tsx +57 -0
- package/primitives/dots-loader.tsx +13 -0
- package/primitives/drawer.tsx +59 -87
- package/primitives/dropdown-menu.tsx +199 -0
- package/primitives/error-message.tsx +19 -0
- package/primitives/file-uploader.tsx +200 -0
- package/primitives/form.tsx +92 -87
- package/primitives/hover-card.tsx +28 -0
- package/primitives/icons/github.tsx +1 -1
- package/primitives/icons/youtube-logo.tsx +1 -1
- package/primitives/index-common.ts +121 -42
- package/primitives/index-next.ts +3 -1
- package/primitives/input.tsx +115 -20
- package/primitives/label.tsx +15 -23
- package/primitives/loading-spinner.tsx +1 -1
- package/primitives/markdown-preview.tsx +609 -0
- package/primitives/mermaid.tsx +196 -0
- package/primitives/next/link-element.tsx +1 -1
- package/primitives/next/mdx-link.tsx +1 -1
- package/primitives/pagination.tsx +117 -0
- package/primitives/popover.tsx +20 -25
- package/primitives/pretty-json-print.tsx +28 -0
- package/primitives/progress.tsx +14 -15
- package/primitives/prompt-textarea.tsx +72 -0
- package/primitives/qr-code.tsx +112 -0
- package/primitives/radio-group.tsx +25 -39
- package/primitives/resizable.tsx +47 -0
- package/primitives/scroll-area.tsx +35 -25
- package/primitives/search-input.tsx +66 -0
- package/primitives/select.tsx +62 -109
- package/primitives/separator.tsx +22 -26
- package/primitives/sheet.tsx +78 -117
- package/primitives/skeleton.tsx +13 -16
- package/primitives/slider.tsx +50 -60
- package/primitives/stepper.tsx +272 -0
- package/primitives/switch.tsx +14 -23
- package/primitives/table.tsx +65 -77
- package/primitives/tabs.tsx +29 -39
- package/primitives/text-link.tsx +25 -0
- package/primitives/textarea.tsx +61 -0
- package/primitives/textfield.tsx +75 -0
- package/primitives/toast.tsx +30 -0
- package/primitives/toggle-group.tsx +33 -33
- package/primitives/toggle.tsx +22 -51
- package/primitives/tooltip.tsx +37 -38
- package/registry.json +1 -1
- package/src/button.ts +1 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/use-click-away.ts +31 -0
- package/src/hooks/use-combined-refs.ts +22 -0
- package/src/hooks/use-copy-clipboard.ts +30 -0
- package/src/hooks/use-debounce.ts +17 -0
- package/src/hooks/use-fill-ids.ts +25 -0
- package/src/hooks/use-map.ts +26 -0
- package/src/hooks/use-measure.ts +42 -0
- package/src/hooks/use-reverse-video-playback.ts +43 -0
- package/src/hooks/use-scroll-restoration.ts +50 -0
- package/src/mcp/README.md +1 -1
- package/src/mcp/enhanced-server.ts +2 -2
- package/src/registry/api.ts +3 -3
- package/src/registry/index.ts +3 -3
- package/src/utils.ts +1 -0
- package/style/theme-provider.tsx +1 -1
- package/test-imports.mjs +19 -0
- package/types/animation-def.ts +1 -1
- package/types/button-def.ts +1 -1
- package/types/index.ts +1 -1
- package/util/blob.ts +28 -0
- package/util/copy-to-clipboard.ts +17 -0
- package/util/create-shadow-root.ts +22 -0
- package/util/date.ts +83 -0
- package/util/debounce.ts +11 -0
- package/util/file.ts +15 -0
- package/util/format-and-abbreviate-as-currency.ts +1 -1
- package/util/format-text.ts +33 -0
- package/util/index.ts +9 -78
- package/util/timing.ts +3 -0
- package/util/toasts.tsx +17 -0
- package/utils.ts +9 -0
package/primitives/sheet.tsx
CHANGED
|
@@ -1,116 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
import { cn } from '../src/utils';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
import * as SheetPrimitive from '@radix-ui/react-dialog'
|
|
7
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
8
|
+
const Sheet = SheetPrimitive.Root;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const SheetTrigger = SheetPrimitive.Trigger;
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
+
const SheetClose = SheetPrimitive.Close;
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
+
const SheetPortal = SheetPrimitive.Portal;
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const SheetOverlay = React.forwardRef<
|
|
20
|
-
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
21
|
-
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
22
|
-
>(({ className, ...props }, ref) => (
|
|
16
|
+
const SheetOverlay = ({
|
|
17
|
+
className,
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) => (
|
|
23
20
|
<SheetPrimitive.Overlay
|
|
24
21
|
className={cn(
|
|
25
|
-
'fixed inset-0 z-
|
|
26
|
-
|
|
27
|
-
className
|
|
22
|
+
'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/70',
|
|
23
|
+
className,
|
|
28
24
|
)}
|
|
29
25
|
{...props}
|
|
30
|
-
ref={ref}
|
|
31
26
|
/>
|
|
32
|
-
)
|
|
33
|
-
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
|
27
|
+
);
|
|
28
|
+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
34
29
|
|
|
35
30
|
const sheetVariants = cva(
|
|
36
|
-
'fixed z-
|
|
31
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out bg-bg-dark border-divider fixed z-50 gap-4 p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
|
37
32
|
{
|
|
38
33
|
variants: {
|
|
39
34
|
side: {
|
|
40
|
-
top: '
|
|
41
|
-
bottom:
|
|
42
|
-
|
|
35
|
+
top: 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b',
|
|
36
|
+
bottom:
|
|
37
|
+
'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t',
|
|
38
|
+
left: 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 max-w-lg border-r',
|
|
43
39
|
right:
|
|
44
|
-
'
|
|
40
|
+
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 max-w-lg border-l',
|
|
45
41
|
},
|
|
46
42
|
},
|
|
47
43
|
defaultVariants: {
|
|
48
44
|
side: 'right',
|
|
49
45
|
},
|
|
50
|
-
}
|
|
51
|
-
)
|
|
46
|
+
},
|
|
47
|
+
);
|
|
52
48
|
|
|
53
|
-
interface SheetContentProps
|
|
49
|
+
interface SheetContentProps
|
|
54
50
|
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const SheetContent = React.forwardRef<
|
|
61
|
-
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
62
|
-
SheetContentProps &
|
|
63
|
-
{
|
|
64
|
-
closeButtonClass? : string
|
|
65
|
-
closeElement?: ReactNode
|
|
66
|
-
centerElement?: ReactNode
|
|
67
|
-
duplicateCloseOnBottom?: boolean
|
|
68
|
-
}
|
|
69
|
-
>(({
|
|
70
|
-
side = 'right',
|
|
71
|
-
className,
|
|
72
|
-
children,
|
|
73
|
-
closeElement,
|
|
74
|
-
centerElement,
|
|
75
|
-
duplicateCloseOnBottom=false,
|
|
76
|
-
closeButtonClass,
|
|
77
|
-
...props
|
|
78
|
-
}, ref ) => {
|
|
79
|
-
|
|
80
|
-
const xOfCloseUIClass = (side === 'right') ? 'left-2' : 'right-2'
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<SheetPortal>
|
|
84
|
-
<SheetOverlay />
|
|
85
|
-
<SheetPrimitive.Content
|
|
86
|
-
ref={ref}
|
|
87
|
-
className={cn(sheetVariants({ side }), className)}
|
|
88
|
-
{...props}
|
|
89
|
-
>
|
|
90
|
-
{children}
|
|
91
|
-
|
|
92
|
-
{closeElement && (
|
|
93
|
-
<SheetPrimitive.Close className={cn(closeUIclx, 'absolute z-above-content top-3', xOfCloseUIClass, closeButtonClass)}>
|
|
94
|
-
{closeElement}
|
|
95
|
-
</SheetPrimitive.Close>
|
|
96
|
-
)}
|
|
97
|
-
{closeElement && duplicateCloseOnBottom && (
|
|
98
|
-
<SheetPrimitive.Close className={cn(closeUIclx, 'absolute z-above-content bottom-3', xOfCloseUIClass, closeButtonClass)}>
|
|
99
|
-
{closeElement}
|
|
100
|
-
</SheetPrimitive.Close>
|
|
101
|
-
)}
|
|
102
|
-
{centerElement && (
|
|
103
|
-
<div className={'absolute z-content top-3 left-0 right-0 flex flex-row justify-center align-start'} >
|
|
104
|
-
{centerElement}
|
|
105
|
-
</div>
|
|
106
|
-
)}
|
|
107
|
-
</SheetPrimitive.Content>
|
|
108
|
-
</SheetPortal>
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
)
|
|
51
|
+
VariantProps<typeof sheetVariants> {
|
|
52
|
+
container?: HTMLElement;
|
|
53
|
+
overlayClassName?: string;
|
|
54
|
+
hideCloseButton?: boolean;
|
|
55
|
+
}
|
|
112
56
|
|
|
113
|
-
SheetContent
|
|
57
|
+
const SheetContent = ({
|
|
58
|
+
side = 'right',
|
|
59
|
+
className,
|
|
60
|
+
children,
|
|
61
|
+
container,
|
|
62
|
+
overlayClassName,
|
|
63
|
+
hideCloseButton,
|
|
64
|
+
...props
|
|
65
|
+
}: SheetContentProps) => (
|
|
66
|
+
<SheetPortal container={container}>
|
|
67
|
+
<SheetOverlay className={overlayClassName} />
|
|
68
|
+
<SheetPrimitive.Content
|
|
69
|
+
className={cn(sheetVariants({ side }), className)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
{!hideCloseButton && (
|
|
74
|
+
<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">
|
|
75
|
+
<X className="h-4 w-4" />
|
|
76
|
+
<span className="sr-only">Close</span>
|
|
77
|
+
</SheetPrimitive.Close>
|
|
78
|
+
)}
|
|
79
|
+
</SheetPrimitive.Content>
|
|
80
|
+
</SheetPortal>
|
|
81
|
+
);
|
|
82
|
+
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
114
83
|
|
|
115
84
|
const SheetHeader = ({
|
|
116
85
|
className,
|
|
@@ -119,50 +88,42 @@ const SheetHeader = ({
|
|
|
119
88
|
<div
|
|
120
89
|
className={cn(
|
|
121
90
|
'flex flex-col space-y-2 text-center sm:text-left',
|
|
122
|
-
className
|
|
91
|
+
className,
|
|
123
92
|
)}
|
|
124
93
|
{...props}
|
|
125
94
|
/>
|
|
126
|
-
)
|
|
127
|
-
SheetHeader.displayName = 'SheetHeader'
|
|
95
|
+
);
|
|
96
|
+
SheetHeader.displayName = 'SheetHeader';
|
|
128
97
|
|
|
129
98
|
const SheetFooter = ({
|
|
130
99
|
className,
|
|
131
100
|
...props
|
|
132
101
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
133
|
-
<div
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
className
|
|
137
|
-
)}
|
|
138
|
-
{...props}
|
|
139
|
-
/>
|
|
140
|
-
)
|
|
141
|
-
SheetFooter.displayName = 'SheetFooter'
|
|
102
|
+
<div className={cn('mt-3 flex flex-col gap-2', className)} {...props} />
|
|
103
|
+
);
|
|
104
|
+
SheetFooter.displayName = 'SheetFooter';
|
|
142
105
|
|
|
143
|
-
const SheetTitle =
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
106
|
+
const SheetTitle = ({
|
|
107
|
+
className,
|
|
108
|
+
...props
|
|
109
|
+
}: React.ComponentProps<typeof SheetPrimitive.Title>) => (
|
|
147
110
|
<SheetPrimitive.Title
|
|
148
|
-
|
|
149
|
-
className={cn('text-lg font-semibold text-foreground', className)}
|
|
111
|
+
className={cn('text-text-default text-lg font-semibold', className)}
|
|
150
112
|
{...props}
|
|
151
113
|
/>
|
|
152
|
-
)
|
|
153
|
-
SheetTitle.displayName = SheetPrimitive.Title.displayName
|
|
114
|
+
);
|
|
115
|
+
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
154
116
|
|
|
155
|
-
const SheetDescription =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
117
|
+
const SheetDescription = ({
|
|
118
|
+
className,
|
|
119
|
+
...props
|
|
120
|
+
}: React.ComponentProps<typeof SheetPrimitive.Description>) => (
|
|
159
121
|
<SheetPrimitive.Description
|
|
160
|
-
|
|
161
|
-
className={cn('text-sm text-muted-1', className)}
|
|
122
|
+
className={cn('text-text-secondary text-sm', className)}
|
|
162
123
|
{...props}
|
|
163
124
|
/>
|
|
164
|
-
)
|
|
165
|
-
SheetDescription.displayName = SheetPrimitive.Description.displayName
|
|
125
|
+
);
|
|
126
|
+
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
166
127
|
|
|
167
128
|
export {
|
|
168
129
|
Sheet,
|
|
@@ -175,4 +136,4 @@ export {
|
|
|
175
136
|
SheetFooter,
|
|
176
137
|
SheetTitle,
|
|
177
138
|
SheetDescription,
|
|
178
|
-
}
|
|
139
|
+
};
|
package/primitives/skeleton.tsx
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
reverse=false,
|
|
3
|
+
import { cn } from '../src/utils';
|
|
4
|
+
|
|
5
|
+
function Skeleton({
|
|
7
6
|
className,
|
|
8
7
|
...props
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/>
|
|
18
|
-
)
|
|
8
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
className={cn('bg-bg-secondary animate-pulse rounded-md', className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
19
16
|
|
|
20
|
-
export
|
|
17
|
+
export { Skeleton };
|
package/primitives/slider.tsx
CHANGED
|
@@ -1,72 +1,62 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import * as React from 'react'
|
|
3
|
-
import * as SliderPrimitive from '@radix-ui/react-slider'
|
|
1
|
+
'use client';
|
|
4
2
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
3
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../src/utils';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
10
|
-
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & {
|
|
11
|
-
trackBgClx?: string
|
|
12
|
-
rangeBgClx?: string
|
|
13
|
-
thumbClx?: string
|
|
14
|
-
thumbSlidingClx?: string
|
|
15
|
-
}
|
|
16
|
-
>(({
|
|
7
|
+
function Slider({
|
|
17
8
|
className,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (onValueChange) {
|
|
34
|
-
onValueChange(value)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const _onCommit = (value: number[]): void => {
|
|
39
|
-
setSliding(false)
|
|
40
|
-
if (onValueCommit) {
|
|
41
|
-
onValueCommit(value)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
9
|
+
defaultValue,
|
|
10
|
+
value,
|
|
11
|
+
min = 0,
|
|
12
|
+
max = 100,
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
|
|
15
|
+
const _values = React.useMemo(
|
|
16
|
+
() =>
|
|
17
|
+
Array.isArray(value)
|
|
18
|
+
? value
|
|
19
|
+
: Array.isArray(defaultValue)
|
|
20
|
+
? defaultValue
|
|
21
|
+
: [min, max],
|
|
22
|
+
[value, defaultValue, min, max],
|
|
23
|
+
);
|
|
44
24
|
|
|
45
25
|
return (
|
|
46
26
|
<SliderPrimitive.Root
|
|
47
|
-
|
|
27
|
+
data-slot="slider"
|
|
28
|
+
defaultValue={defaultValue}
|
|
29
|
+
value={value}
|
|
30
|
+
min={min}
|
|
31
|
+
max={max}
|
|
48
32
|
className={cn(
|
|
49
|
-
'relative flex w-full touch-none select-none
|
|
50
|
-
className
|
|
33
|
+
'relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col',
|
|
34
|
+
className,
|
|
51
35
|
)}
|
|
52
|
-
|
|
53
|
-
onValueCommit={_onCommit}
|
|
54
|
-
{...rest}
|
|
36
|
+
{...props}
|
|
55
37
|
>
|
|
56
|
-
<SliderPrimitive.Track
|
|
57
|
-
|
|
38
|
+
<SliderPrimitive.Track
|
|
39
|
+
data-slot="slider-track"
|
|
40
|
+
className={cn(
|
|
41
|
+
'bg-bg-quaternary relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5',
|
|
42
|
+
)}
|
|
43
|
+
>
|
|
44
|
+
<SliderPrimitive.Range
|
|
45
|
+
data-slot="slider-range"
|
|
46
|
+
className={cn(
|
|
47
|
+
'bg-brand absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full',
|
|
48
|
+
)}
|
|
49
|
+
/>
|
|
58
50
|
</SliderPrimitive.Track>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)}
|
|
51
|
+
{Array.from({ length: _values.length }, (_, index) => (
|
|
52
|
+
<SliderPrimitive.Thumb
|
|
53
|
+
data-slot="slider-thumb"
|
|
54
|
+
key={index}
|
|
55
|
+
className="border-brand-500 bg-brand ring-brand/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
|
56
|
+
/>
|
|
57
|
+
))}
|
|
66
58
|
</SliderPrimitive.Root>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
Slider.displayName = SliderPrimitive.Root.displayName
|
|
59
|
+
);
|
|
60
|
+
}
|
|
71
61
|
|
|
72
|
-
export
|
|
62
|
+
export { Slider };
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { CheckIcon } from '@radix-ui/react-icons';
|
|
2
|
+
import { LoaderCircle } from 'lucide-react';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { createContext, useContext } from 'react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '../src/utils';
|
|
7
|
+
|
|
8
|
+
type StepperContextValue = {
|
|
9
|
+
activeStep: number;
|
|
10
|
+
setActiveStep: (step: number) => void;
|
|
11
|
+
orientation: 'horizontal' | 'vertical';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type StepItemContextValue = {
|
|
15
|
+
step: number;
|
|
16
|
+
state: StepState;
|
|
17
|
+
isDisabled: boolean;
|
|
18
|
+
isLoading: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type StepState = 'active' | 'completed' | 'inactive' | 'loading';
|
|
22
|
+
|
|
23
|
+
const StepperContext = createContext<StepperContextValue | undefined>(
|
|
24
|
+
undefined,
|
|
25
|
+
);
|
|
26
|
+
const StepItemContext = createContext<StepItemContextValue | undefined>(
|
|
27
|
+
undefined,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const useStepper = () => {
|
|
31
|
+
const context = useContext(StepperContext);
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error('useStepper must be used within a Stepper');
|
|
34
|
+
}
|
|
35
|
+
return context;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const useStepItem = () => {
|
|
39
|
+
const context = useContext(StepItemContext);
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error('useStepItem must be used within a StepperItem');
|
|
42
|
+
}
|
|
43
|
+
return context;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
47
|
+
defaultValue?: number;
|
|
48
|
+
value?: number;
|
|
49
|
+
onValueChange?: (value: number) => void;
|
|
50
|
+
orientation?: 'horizontal' | 'vertical';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const Stepper = ({
|
|
54
|
+
defaultValue = 0,
|
|
55
|
+
value,
|
|
56
|
+
onValueChange,
|
|
57
|
+
orientation = 'horizontal',
|
|
58
|
+
className,
|
|
59
|
+
...props
|
|
60
|
+
}: StepperProps) => {
|
|
61
|
+
const [activeStep, setInternalStep] = React.useState(defaultValue);
|
|
62
|
+
|
|
63
|
+
const setActiveStep = React.useCallback(
|
|
64
|
+
(step: number) => {
|
|
65
|
+
if (value === undefined) {
|
|
66
|
+
setInternalStep(step);
|
|
67
|
+
}
|
|
68
|
+
onValueChange?.(step);
|
|
69
|
+
},
|
|
70
|
+
[value, onValueChange],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const currentStep = value ?? activeStep;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<StepperContext.Provider
|
|
77
|
+
value={{
|
|
78
|
+
activeStep: currentStep,
|
|
79
|
+
setActiveStep,
|
|
80
|
+
orientation,
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<div
|
|
84
|
+
className={cn(
|
|
85
|
+
'group/stepper inline-flex data-[orientation=horizontal]:w-full data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col',
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
data-orientation={orientation}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
</StepperContext.Provider>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
Stepper.displayName = 'Stepper';
|
|
95
|
+
|
|
96
|
+
// StepperItem
|
|
97
|
+
interface StepperItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
98
|
+
step: number;
|
|
99
|
+
completed?: boolean;
|
|
100
|
+
disabled?: boolean;
|
|
101
|
+
loading?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const StepperItem = ({
|
|
105
|
+
step,
|
|
106
|
+
completed = false,
|
|
107
|
+
disabled = false,
|
|
108
|
+
loading = false,
|
|
109
|
+
className,
|
|
110
|
+
children,
|
|
111
|
+
...props
|
|
112
|
+
}: StepperItemProps) => {
|
|
113
|
+
const { activeStep } = useStepper();
|
|
114
|
+
|
|
115
|
+
const state: StepState =
|
|
116
|
+
completed || step < activeStep
|
|
117
|
+
? 'completed'
|
|
118
|
+
: activeStep === step
|
|
119
|
+
? 'active'
|
|
120
|
+
: 'inactive';
|
|
121
|
+
|
|
122
|
+
const isLoading = loading && step === activeStep;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<StepItemContext.Provider
|
|
126
|
+
value={{ step, state, isDisabled: disabled, isLoading }}
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
className={cn(
|
|
130
|
+
'group/step flex items-center group-data-[orientation=horizontal]/stepper:flex-row group-data-[orientation=vertical]/stepper:flex-col',
|
|
131
|
+
className,
|
|
132
|
+
)}
|
|
133
|
+
data-state={state}
|
|
134
|
+
{...(isLoading ? { 'data-loading': true } : {})}
|
|
135
|
+
{...props}
|
|
136
|
+
>
|
|
137
|
+
{children}
|
|
138
|
+
</div>
|
|
139
|
+
</StepItemContext.Provider>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
StepperItem.displayName = 'StepperItem';
|
|
144
|
+
|
|
145
|
+
// StepperTrigger
|
|
146
|
+
interface StepperTriggerProps
|
|
147
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
148
|
+
asChild?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const StepperTrigger = ({
|
|
152
|
+
asChild = false,
|
|
153
|
+
className,
|
|
154
|
+
children,
|
|
155
|
+
...props
|
|
156
|
+
}: StepperTriggerProps) => {
|
|
157
|
+
const { setActiveStep } = useStepper();
|
|
158
|
+
const { step, isDisabled } = useStepItem();
|
|
159
|
+
|
|
160
|
+
if (asChild) {
|
|
161
|
+
return <div className={className}>{children}</div>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<button
|
|
166
|
+
className={cn(
|
|
167
|
+
'inline-flex items-center gap-3 disabled:pointer-events-none',
|
|
168
|
+
className,
|
|
169
|
+
)}
|
|
170
|
+
disabled={isDisabled}
|
|
171
|
+
onClick={() => setActiveStep(step)}
|
|
172
|
+
{...props}
|
|
173
|
+
>
|
|
174
|
+
{children}
|
|
175
|
+
</button>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
StepperTrigger.displayName = 'StepperTrigger';
|
|
179
|
+
|
|
180
|
+
interface StepperIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
181
|
+
asChild?: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const StepperIndicator = ({
|
|
185
|
+
asChild = false,
|
|
186
|
+
className,
|
|
187
|
+
children,
|
|
188
|
+
...props
|
|
189
|
+
}: StepperIndicatorProps) => {
|
|
190
|
+
const { state, step, isLoading } = useStepItem();
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div
|
|
194
|
+
className={cn(
|
|
195
|
+
'bg-bg-tertiary text-text-secondary data-[state=completed]:bg-brand data-[state=active]:text-text-default data-[state=completed]:text-primary-foreground data-[state=active]:bg-bg-secondary relative flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-medium',
|
|
196
|
+
className,
|
|
197
|
+
)}
|
|
198
|
+
data-state={state}
|
|
199
|
+
{...props}
|
|
200
|
+
>
|
|
201
|
+
{asChild ? (
|
|
202
|
+
children
|
|
203
|
+
) : (
|
|
204
|
+
<>
|
|
205
|
+
<span className="transition-all group-data-[loading=true]/step:scale-0 group-data-[loading=true]/step:opacity-0 group-data-[loading=true]/step:transition-none group-data-[state=completed]/step:scale-0 group-data-[state=completed]/step:opacity-0">
|
|
206
|
+
{step}
|
|
207
|
+
</span>
|
|
208
|
+
<CheckIcon
|
|
209
|
+
aria-hidden="true"
|
|
210
|
+
className="absolute scale-0 opacity-0 transition-all group-data-[state=completed]/step:scale-100 group-data-[state=completed]/step:opacity-100"
|
|
211
|
+
// size={16}
|
|
212
|
+
strokeWidth={2}
|
|
213
|
+
/>
|
|
214
|
+
{isLoading && (
|
|
215
|
+
<span className="absolute transition-all">
|
|
216
|
+
<LoaderCircle
|
|
217
|
+
aria-hidden="true"
|
|
218
|
+
className="animate-spin"
|
|
219
|
+
size={14}
|
|
220
|
+
strokeWidth={2}
|
|
221
|
+
/>
|
|
222
|
+
</span>
|
|
223
|
+
)}
|
|
224
|
+
</>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
};
|
|
229
|
+
StepperIndicator.displayName = 'StepperIndicator';
|
|
230
|
+
|
|
231
|
+
// StepperTitle
|
|
232
|
+
const StepperTitle = ({
|
|
233
|
+
className,
|
|
234
|
+
...props
|
|
235
|
+
}: React.HTMLAttributes<HTMLHeadingElement>) => (
|
|
236
|
+
<h3 className={cn('text-sm font-medium', className)} {...props} />
|
|
237
|
+
);
|
|
238
|
+
StepperTitle.displayName = 'StepperTitle';
|
|
239
|
+
|
|
240
|
+
const StepperDescription = ({
|
|
241
|
+
className,
|
|
242
|
+
...props
|
|
243
|
+
}: React.HTMLAttributes<HTMLParagraphElement>) => (
|
|
244
|
+
<p className={cn('text-text-secondary text-sm', className)} {...props} />
|
|
245
|
+
);
|
|
246
|
+
StepperDescription.displayName = 'StepperDescription';
|
|
247
|
+
|
|
248
|
+
const StepperSeparator = ({
|
|
249
|
+
className,
|
|
250
|
+
...props
|
|
251
|
+
}: React.HTMLAttributes<HTMLDivElement>) => {
|
|
252
|
+
return (
|
|
253
|
+
<div
|
|
254
|
+
className={cn(
|
|
255
|
+
'bg-muted group-data-[state=completed]/step:bg-primary m-0.5 group-data-[orientation=horizontal]/stepper:h-0.5 group-data-[orientation=horizontal]/stepper:w-full group-data-[orientation=horizontal]/stepper:flex-1 group-data-[orientation=vertical]/stepper:h-12 group-data-[orientation=vertical]/stepper:w-0.5',
|
|
256
|
+
className,
|
|
257
|
+
)}
|
|
258
|
+
{...props}
|
|
259
|
+
/>
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
StepperSeparator.displayName = 'StepperSeparator';
|
|
263
|
+
|
|
264
|
+
export {
|
|
265
|
+
Stepper,
|
|
266
|
+
StepperDescription,
|
|
267
|
+
StepperIndicator,
|
|
268
|
+
StepperItem,
|
|
269
|
+
StepperSeparator,
|
|
270
|
+
StepperTitle,
|
|
271
|
+
StepperTrigger,
|
|
272
|
+
};
|