@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
|
@@ -1,108 +1,123 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import { cn } from '../
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
React.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
+
import { ChevronRight, MoreHorizontal } from 'lucide-react';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../src/utils';
|
|
6
|
+
|
|
7
|
+
type BreadcrumbProps = React.ComponentPropsWithoutRef<'nav'> & {
|
|
8
|
+
separator?: React.ReactNode;
|
|
9
|
+
ref?: React.RefObject<HTMLDivElement>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Breadcrumb = ({ ref, ...props }: BreadcrumbProps) => (
|
|
13
|
+
<nav aria-label="breadcrumb" ref={ref} {...props} />
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
Breadcrumb.displayName = 'Breadcrumb';
|
|
17
|
+
|
|
18
|
+
type BreadcrumbListProps = React.ComponentPropsWithoutRef<'ol'> & {
|
|
19
|
+
ref?: React.RefObject<HTMLOListElement>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const BreadcrumbList = ({ className, ref, ...props }: BreadcrumbListProps) => (
|
|
19
23
|
<ol
|
|
20
|
-
ref={ref}
|
|
21
24
|
className={cn(
|
|
22
|
-
|
|
23
|
-
className
|
|
25
|
+
'text-text-secondary flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5',
|
|
26
|
+
className,
|
|
24
27
|
)}
|
|
28
|
+
ref={ref}
|
|
25
29
|
{...props}
|
|
26
30
|
/>
|
|
27
|
-
)
|
|
28
|
-
BreadcrumbList.displayName =
|
|
31
|
+
);
|
|
32
|
+
BreadcrumbList.displayName = 'BreadcrumbList';
|
|
33
|
+
|
|
34
|
+
type BreadcrumbItemProps = React.ComponentPropsWithoutRef<'li'> & {
|
|
35
|
+
ref?: React.RefObject<HTMLLIElement>;
|
|
36
|
+
};
|
|
29
37
|
|
|
30
|
-
const BreadcrumbItem =
|
|
31
|
-
HTMLLIElement,
|
|
32
|
-
React.ComponentPropsWithoutRef<"li">
|
|
33
|
-
>(({ className, ...props }, ref) => (
|
|
38
|
+
const BreadcrumbItem = ({ className, ref, ...props }: BreadcrumbItemProps) => (
|
|
34
39
|
<li
|
|
40
|
+
className={cn('inline-flex items-center gap-1.5', className)}
|
|
35
41
|
ref={ref}
|
|
36
|
-
className={cn("inline-flex items-center gap-1.5", className)}
|
|
37
42
|
{...props}
|
|
38
43
|
/>
|
|
39
|
-
)
|
|
40
|
-
BreadcrumbItem.displayName =
|
|
44
|
+
);
|
|
45
|
+
BreadcrumbItem.displayName = 'BreadcrumbItem';
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
React.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
type BreadcrumbLinkProps = React.ComponentPropsWithoutRef<'a'> & {
|
|
48
|
+
asChild?: boolean;
|
|
49
|
+
ref?: React.RefObject<HTMLAnchorElement>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const BreadcrumbLink = ({
|
|
53
|
+
asChild,
|
|
54
|
+
className,
|
|
55
|
+
ref,
|
|
56
|
+
...props
|
|
57
|
+
}: BreadcrumbLinkProps) => {
|
|
58
|
+
const Comp = asChild ? Slot : 'a';
|
|
49
59
|
|
|
50
60
|
return (
|
|
51
61
|
<Comp
|
|
62
|
+
className={cn('hover:text-text-default transition-colors', className)}
|
|
52
63
|
ref={ref}
|
|
53
|
-
className={cn("transition-colors hover:text-foreground", className)}
|
|
54
64
|
{...props}
|
|
55
65
|
/>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
BreadcrumbLink.displayName =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
HTMLSpanElement
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
BreadcrumbLink.displayName = 'BreadcrumbLink';
|
|
69
|
+
|
|
70
|
+
type BreadcrumbPageProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
71
|
+
ref?: React.RefObject<HTMLSpanElement>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const BreadcrumbPage = ({ className, ref, ...props }: BreadcrumbPageProps) => (
|
|
64
75
|
<span
|
|
76
|
+
aria-current="page"
|
|
77
|
+
aria-disabled="true"
|
|
78
|
+
className={cn('text-text-default font-normal', className)}
|
|
65
79
|
ref={ref}
|
|
66
80
|
role="link"
|
|
67
|
-
aria-disabled="true"
|
|
68
|
-
aria-current="page"
|
|
69
|
-
className={cn("font-normal text-foreground", className)}
|
|
70
81
|
{...props}
|
|
71
82
|
/>
|
|
72
|
-
)
|
|
73
|
-
BreadcrumbPage.displayName =
|
|
83
|
+
);
|
|
84
|
+
BreadcrumbPage.displayName = 'BreadcrumbPage';
|
|
85
|
+
|
|
86
|
+
type BreadcrumbSeparatorProps = React.ComponentProps<'li'> & {
|
|
87
|
+
ref?: React.RefObject<HTMLLIElement>;
|
|
88
|
+
};
|
|
74
89
|
|
|
75
90
|
const BreadcrumbSeparator = ({
|
|
76
91
|
children,
|
|
77
92
|
className,
|
|
78
93
|
...props
|
|
79
|
-
}:
|
|
94
|
+
}: BreadcrumbSeparatorProps) => (
|
|
80
95
|
<li
|
|
81
|
-
role="presentation"
|
|
82
96
|
aria-hidden="true"
|
|
83
|
-
className={cn(
|
|
97
|
+
className={cn('[&>svg]:h-4 [&>svg]:w-4', className)}
|
|
98
|
+
role="presentation"
|
|
84
99
|
{...props}
|
|
85
100
|
>
|
|
86
101
|
{children ?? <ChevronRight />}
|
|
87
102
|
</li>
|
|
88
|
-
)
|
|
89
|
-
BreadcrumbSeparator.displayName =
|
|
103
|
+
);
|
|
104
|
+
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator';
|
|
90
105
|
|
|
91
106
|
const BreadcrumbEllipsis = ({
|
|
92
107
|
className,
|
|
93
108
|
...props
|
|
94
|
-
}: React.ComponentProps<
|
|
109
|
+
}: React.ComponentProps<'span'>) => (
|
|
95
110
|
<span
|
|
96
|
-
role="presentation"
|
|
97
111
|
aria-hidden="true"
|
|
98
|
-
className={cn(
|
|
112
|
+
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
|
113
|
+
role="presentation"
|
|
99
114
|
{...props}
|
|
100
115
|
>
|
|
101
116
|
<MoreHorizontal className="h-4 w-4" />
|
|
102
117
|
<span className="sr-only">More</span>
|
|
103
118
|
</span>
|
|
104
|
-
)
|
|
105
|
-
BreadcrumbEllipsis.displayName =
|
|
119
|
+
);
|
|
120
|
+
BreadcrumbEllipsis.displayName = 'BreadcrumbElipssis';
|
|
106
121
|
|
|
107
122
|
export {
|
|
108
123
|
Breadcrumb,
|
|
@@ -112,4 +127,4 @@ export {
|
|
|
112
127
|
BreadcrumbPage,
|
|
113
128
|
BreadcrumbSeparator,
|
|
114
129
|
BreadcrumbEllipsis,
|
|
115
|
-
}
|
|
130
|
+
};
|
package/primitives/button.tsx
CHANGED
|
@@ -1,85 +1,82 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
import { Loader2 } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
4
5
|
|
|
5
|
-
import { cn } from
|
|
6
|
+
import { cn } from '../src/utils';
|
|
6
7
|
|
|
7
|
-
// Define all variants
|
|
8
|
-
const variant = {
|
|
9
|
-
primary: "bg-primary text-primary-fg sm:hover:bg-primary-hover font-nav whitespace-nowrap not-typography",
|
|
10
|
-
secondary: "bg-secondary text-secondary-fg sm:hover:bg-secondary-hover font-nav whitespace-nowrap not-typography",
|
|
11
|
-
outline: "text-foreground bg-background border border-muted-4 sm:hover:bg-level-1 sm:hover:text-accent sm:hover:border-accent font-nav whitespace-nowrap not-typography",
|
|
12
|
-
destructive: "bg-destructive text-destructive-fg font-sans whitespace-nowrap sm:hover:bg-destructive-hover",
|
|
13
|
-
ghost: "text-foreground sm:hover:bg-level-1 sm:hover:text-accent whitespace-nowrap font-sans ",
|
|
14
|
-
link: "text-foreground sm:hover:text-muted-1 font-sans ",
|
|
15
|
-
linkFG: "text-foreground sm:hover:text-muted-1 font-sans ", // marker to style nav as regular link
|
|
16
|
-
linkMuted: "text-muted-1 sm:hover:text-foreground font-sans ",
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Define all sizes
|
|
20
|
-
const size = {
|
|
21
|
-
link: '',
|
|
22
|
-
xs: "h-8 px-2 text-xs",
|
|
23
|
-
sm: "h-9 px-3 text-xs",
|
|
24
|
-
square: 'h-9 py-2 px-2 text-sm aspect-square',
|
|
25
|
-
default: "h-9 py-2 px-4 text-sm md:text-base font-semibold min-w-0",
|
|
26
|
-
lg: "h-10 px-8 text-sm md:text-base font-semibold min-w-0 md:min-w-[260px] lg:min-w-[300px]",
|
|
27
|
-
icon: "h-10 w-10",
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Define rounded options
|
|
31
|
-
const rounded = {
|
|
32
|
-
full: 'rounded-full',
|
|
33
|
-
sm: 'rounded-sm',
|
|
34
|
-
md: 'rounded-md',
|
|
35
|
-
lg: 'rounded-lg',
|
|
36
|
-
xl: 'rounded-xl',
|
|
37
|
-
none: ''
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Updated buttonVariants with the latest Shadcn patterns
|
|
41
8
|
const buttonVariants = cva(
|
|
42
|
-
|
|
43
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 " +
|
|
44
|
-
"disabled:opacity-50 disabled:pointer-events-none",
|
|
9
|
+
'inline-flex items-center justify-center gap-2 border border-transparent text-sm font-semibold transition-colors focus-visible:ring-1 focus-visible:ring-gray-50 focus-visible:outline-hidden focus-visible:ring-inset',
|
|
45
10
|
{
|
|
46
11
|
variants: {
|
|
47
|
-
variant
|
|
48
|
-
|
|
49
|
-
|
|
12
|
+
variant: {
|
|
13
|
+
default:
|
|
14
|
+
'bg-brand hover:bg-brand-500 text-black disabled:bg-gray-800 disabled:text-gray-400 disabled:opacity-60',
|
|
15
|
+
destructive:
|
|
16
|
+
'disabled:text-text-secondary bg-red-500 text-white hover:bg-red-500/90 disabled:bg-gray-600',
|
|
17
|
+
outline:
|
|
18
|
+
'border border-gray-600 bg-transparent text-white hover:border-gray-400 hover:bg-gray-900 hover:text-white',
|
|
19
|
+
tertiary:
|
|
20
|
+
'bg-transparent text-gray-300 hover:bg-gray-900 hover:text-white',
|
|
21
|
+
link: 'text-white underline-offset-4 hover:underline',
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: 'h-[50px] px-8 text-sm',
|
|
25
|
+
xs: 'h-[34px] gap-1.5 px-3 text-xs',
|
|
26
|
+
sm: 'h-[36px] gap-1.5 px-3 text-xs',
|
|
27
|
+
md: 'h-[40px] gap-1.5 px-3 text-xs',
|
|
28
|
+
lg: 'h-[48px] gap-2 px-2 text-sm',
|
|
29
|
+
icon: 'h-9 w-9',
|
|
30
|
+
auto: 'h-auto p-4',
|
|
31
|
+
},
|
|
32
|
+
rounded: {
|
|
33
|
+
full: 'rounded-full',
|
|
34
|
+
lg: 'rounded-lg',
|
|
35
|
+
},
|
|
50
36
|
},
|
|
51
37
|
defaultVariants: {
|
|
52
|
-
variant:
|
|
53
|
-
size:
|
|
54
|
-
rounded: '
|
|
38
|
+
variant: 'default',
|
|
39
|
+
size: 'default',
|
|
40
|
+
rounded: 'full',
|
|
55
41
|
},
|
|
56
|
-
}
|
|
57
|
-
)
|
|
42
|
+
},
|
|
43
|
+
);
|
|
58
44
|
|
|
59
|
-
interface ButtonProps
|
|
60
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
export interface ButtonProps
|
|
46
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
47
|
+
VariantProps<typeof buttonVariants> {
|
|
48
|
+
asChild?: boolean;
|
|
49
|
+
isLoading?: boolean;
|
|
50
|
+
ref?: React.RefObject<HTMLButtonElement>;
|
|
64
51
|
}
|
|
65
52
|
|
|
66
|
-
const Button =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
const Button = ({
|
|
54
|
+
className,
|
|
55
|
+
variant,
|
|
56
|
+
size,
|
|
57
|
+
rounded,
|
|
58
|
+
asChild = false,
|
|
59
|
+
isLoading = false,
|
|
60
|
+
ref,
|
|
61
|
+
...props
|
|
62
|
+
}: ButtonProps) => {
|
|
63
|
+
const Comp = asChild ? Slot : 'button';
|
|
64
|
+
return (
|
|
65
|
+
<Comp
|
|
66
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
67
|
+
ref={ref}
|
|
68
|
+
{...props}
|
|
69
|
+
>
|
|
70
|
+
{isLoading ? (
|
|
71
|
+
<Loader2
|
|
72
|
+
className={cn('h-4 w-4 animate-spin', size !== 'icon' && 'mr-2')}
|
|
73
|
+
/>
|
|
74
|
+
) : null}
|
|
75
|
+
{isLoading && size === 'icon' ? null : props.children}
|
|
76
|
+
</Comp>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
78
79
|
|
|
79
|
-
Button.displayName =
|
|
80
|
+
Button.displayName = 'Button';
|
|
80
81
|
|
|
81
|
-
export {
|
|
82
|
-
Button as default,
|
|
83
|
-
type ButtonProps,
|
|
84
|
-
buttonVariants,
|
|
85
|
-
}
|
|
82
|
+
export { Button, buttonVariants };
|
package/primitives/card.tsx
CHANGED
|
@@ -1,83 +1,97 @@
|
|
|
1
|
-
|
|
2
|
-
import * as React from "react"
|
|
1
|
+
import * as React from 'react';
|
|
3
2
|
|
|
4
|
-
import { cn } from
|
|
3
|
+
import { cn } from '../src/utils';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
HTMLDivElement
|
|
8
|
-
|
|
9
|
-
>(({ className, ...props }, ref) => {
|
|
10
|
-
return(
|
|
11
|
-
<div
|
|
12
|
-
ref={ref}
|
|
13
|
-
className={cn(
|
|
14
|
-
"rounded-lg border overflow-hidden shadow-sm",
|
|
15
|
-
className
|
|
16
|
-
)}
|
|
17
|
-
{...props}
|
|
18
|
-
/>
|
|
19
|
-
)
|
|
20
|
-
})
|
|
5
|
+
type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
ref?: React.RefObject<HTMLDivElement>;
|
|
7
|
+
};
|
|
21
8
|
|
|
22
|
-
Card
|
|
23
|
-
|
|
24
|
-
const CardHeader = React.forwardRef<
|
|
25
|
-
HTMLDivElement,
|
|
26
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
27
|
-
>(({ className, ...props }, ref) => (
|
|
9
|
+
const Card = ({ className, ref, ...props }: CardProps) => (
|
|
28
10
|
<div
|
|
11
|
+
className={cn(
|
|
12
|
+
'bg-card text-card-foreground rounded-lg border shadow-xs',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
29
15
|
ref={ref}
|
|
30
|
-
className={cn("flex flex-col p-4 lg:p-5 xl:px-6 xl:py-5 border-b", className)}
|
|
31
16
|
{...props}
|
|
32
17
|
/>
|
|
33
|
-
)
|
|
34
|
-
|
|
18
|
+
);
|
|
19
|
+
Card.displayName = 'Card';
|
|
20
|
+
|
|
21
|
+
type CardHeaderProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
22
|
+
ref?: React.RefObject<HTMLDivElement>;
|
|
23
|
+
};
|
|
35
24
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
>(({ className, ...props }, ref) => (
|
|
40
|
-
<h5
|
|
25
|
+
const CardHeader = ({ className, ref, ...props }: CardHeaderProps) => (
|
|
26
|
+
<div
|
|
27
|
+
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
|
41
28
|
ref={ref}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CardHeader.displayName = 'CardHeader';
|
|
34
|
+
|
|
35
|
+
type CardTitleProps = React.HTMLAttributes<HTMLHeadingElement> & {
|
|
36
|
+
ref?: React.RefObject<HTMLHeadingElement>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const CardTitle = ({ className, ref, ...props }: CardTitleProps) => (
|
|
40
|
+
<h3
|
|
42
41
|
className={cn(
|
|
43
|
-
|
|
44
|
-
className
|
|
42
|
+
'text-2xl leading-none font-semibold tracking-tight',
|
|
43
|
+
className,
|
|
45
44
|
)}
|
|
45
|
+
ref={ref}
|
|
46
46
|
{...props}
|
|
47
47
|
/>
|
|
48
|
-
)
|
|
49
|
-
CardTitle.displayName =
|
|
48
|
+
);
|
|
49
|
+
CardTitle.displayName = 'CardTitle';
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
HTMLParagraphElement
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
type CardDescriptionProps = React.HTMLAttributes<HTMLParagraphElement> & {
|
|
52
|
+
ref?: React.RefObject<HTMLParagraphElement>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const CardDescription = ({
|
|
56
|
+
className,
|
|
57
|
+
ref,
|
|
58
|
+
...props
|
|
59
|
+
}: CardDescriptionProps) => (
|
|
60
|
+
<p
|
|
61
|
+
className={cn('text-text-secondary text-sm', className)}
|
|
56
62
|
ref={ref}
|
|
57
|
-
className={className}
|
|
58
63
|
{...props}
|
|
59
64
|
/>
|
|
60
|
-
)
|
|
61
|
-
CardDescription.displayName =
|
|
65
|
+
);
|
|
66
|
+
CardDescription.displayName = 'CardDescription';
|
|
67
|
+
|
|
68
|
+
type CardContentProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
69
|
+
ref?: React.RefObject<HTMLDivElement>;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const CardContent = ({ className, ref, ...props }: CardContentProps) => (
|
|
73
|
+
<div className={cn('p-6 pt-0', className)} ref={ref} {...props} />
|
|
74
|
+
);
|
|
75
|
+
CardContent.displayName = 'CardContent';
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
HTMLDivElement
|
|
65
|
-
|
|
66
|
-
>(({ className, ...props }, ref) => (
|
|
67
|
-
<div ref={ref} className={cn("p-6", className)} {...props} />
|
|
68
|
-
))
|
|
69
|
-
CardContent.displayName = "CardContent"
|
|
77
|
+
type CardFooterProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
78
|
+
ref?: React.RefObject<HTMLDivElement>;
|
|
79
|
+
};
|
|
70
80
|
|
|
71
|
-
const CardFooter =
|
|
72
|
-
HTMLDivElement,
|
|
73
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
74
|
-
>(({ className, ...props }, ref) => (
|
|
81
|
+
const CardFooter = ({ className, ref, ...props }: CardFooterProps) => (
|
|
75
82
|
<div
|
|
83
|
+
className={cn('flex items-center p-6 pt-0', className)}
|
|
76
84
|
ref={ref}
|
|
77
|
-
className={cn("flex items-center px-6 py-3 border-t", className)}
|
|
78
85
|
{...props}
|
|
79
86
|
/>
|
|
80
|
-
)
|
|
81
|
-
CardFooter.displayName =
|
|
87
|
+
);
|
|
88
|
+
CardFooter.displayName = 'CardFooter';
|
|
82
89
|
|
|
83
|
-
export {
|
|
90
|
+
export {
|
|
91
|
+
Card,
|
|
92
|
+
CardHeader,
|
|
93
|
+
CardFooter,
|
|
94
|
+
CardTitle,
|
|
95
|
+
CardDescription,
|
|
96
|
+
CardContent,
|
|
97
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useTranslation } from '@hanzo_network/hanzo-i18n';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { useCombinedRefs } from '../../hooks/use-combined-refs';
|
|
5
|
+
import { cn } from '../src/utils';
|
|
6
|
+
import { ChatInput } from './chat-input';
|
|
7
|
+
|
|
8
|
+
type ChatInputAreaProps = {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
12
|
+
onPaste?: (e: React.ClipboardEvent<HTMLTextAreaElement>) => void;
|
|
13
|
+
onSubmit: () => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
autoFocus?: boolean;
|
|
16
|
+
isLoading?: boolean;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
topAddons?: React.ReactNode;
|
|
19
|
+
bottomAddons?: React.ReactNode;
|
|
20
|
+
textareaClassName?: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
alternateElement?: React.ReactNode;
|
|
23
|
+
ref?: React.RefObject<HTMLTextAreaElement | null>;
|
|
24
|
+
};
|
|
25
|
+
export const ChatInputArea = ({
|
|
26
|
+
value,
|
|
27
|
+
onChange,
|
|
28
|
+
onPaste,
|
|
29
|
+
onKeyDown,
|
|
30
|
+
autoFocus,
|
|
31
|
+
onSubmit,
|
|
32
|
+
disabled,
|
|
33
|
+
isLoading,
|
|
34
|
+
placeholder,
|
|
35
|
+
topAddons,
|
|
36
|
+
bottomAddons,
|
|
37
|
+
textareaClassName,
|
|
38
|
+
alternateElement,
|
|
39
|
+
className,
|
|
40
|
+
ref,
|
|
41
|
+
}: ChatInputAreaProps) => {
|
|
42
|
+
const { t } = useTranslation();
|
|
43
|
+
const textareaRef = useCombinedRefs<HTMLTextAreaElement>(
|
|
44
|
+
ref as React.RefObject<HTMLTextAreaElement>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
className={cn(
|
|
50
|
+
'bg-bg-secondary flex w-full max-w-full flex-col rounded-xl text-sm aria-disabled:cursor-not-allowed aria-disabled:opacity-50',
|
|
51
|
+
'shadow-border-input focus-within:shadow-border-input-focus overflow-hidden shadow-[0_0_0_1px_currentColor] transition-shadow',
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
>
|
|
55
|
+
{topAddons}
|
|
56
|
+
<div
|
|
57
|
+
aria-disabled={disabled}
|
|
58
|
+
className="flex cursor-text flex-col aria-disabled:cursor-not-allowed"
|
|
59
|
+
onClick={(e) => {
|
|
60
|
+
if (e.target === e.currentTarget) {
|
|
61
|
+
textareaRef?.current?.focus();
|
|
62
|
+
}
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{alternateElement ? (
|
|
66
|
+
alternateElement
|
|
67
|
+
) : (
|
|
68
|
+
<ChatInput
|
|
69
|
+
autoFocus={autoFocus}
|
|
70
|
+
className={textareaClassName}
|
|
71
|
+
disabled={disabled || isLoading}
|
|
72
|
+
onChange={(e) => onChange(e.target.value)}
|
|
73
|
+
onKeyDown={onKeyDown}
|
|
74
|
+
onPaste={onPaste}
|
|
75
|
+
onSend={onSubmit}
|
|
76
|
+
placeholder={placeholder ?? t('chat.sendMessagePlaceholder')}
|
|
77
|
+
ref={textareaRef}
|
|
78
|
+
value={value}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
{bottomAddons}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
ChatInputArea.displayName = 'ChatInputArea';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../src/utils';
|
|
4
|
+
|
|
5
|
+
export interface ChatInputProps
|
|
6
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
7
|
+
onSend?: () => void;
|
|
8
|
+
ref: React.RefObject<HTMLTextAreaElement | null>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useAutoResizeTextarea = (
|
|
12
|
+
ref: React.RefObject<HTMLTextAreaElement | null>,
|
|
13
|
+
value: string | number | readonly string[] | undefined,
|
|
14
|
+
autoResize = true, //later to unify
|
|
15
|
+
) => {
|
|
16
|
+
const textAreaRef = React.useRef<HTMLTextAreaElement>(null);
|
|
17
|
+
|
|
18
|
+
React.useImperativeHandle(ref, () => textAreaRef.current!);
|
|
19
|
+
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
const ref = textAreaRef?.current;
|
|
22
|
+
|
|
23
|
+
const updateTextareaHeight = () => {
|
|
24
|
+
if (ref && autoResize) {
|
|
25
|
+
ref.style.height = 'auto';
|
|
26
|
+
ref.style.height = ref?.scrollHeight + 'px';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
updateTextareaHeight();
|
|
31
|
+
|
|
32
|
+
ref?.addEventListener('input', updateTextareaHeight);
|
|
33
|
+
return () => ref?.removeEventListener('input', updateTextareaHeight);
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
}, [value]);
|
|
36
|
+
|
|
37
|
+
return { textAreaRef };
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const ChatInputBase = ({
|
|
41
|
+
className,
|
|
42
|
+
onSend,
|
|
43
|
+
onKeyDown,
|
|
44
|
+
ref,
|
|
45
|
+
...props
|
|
46
|
+
}: ChatInputProps) => {
|
|
47
|
+
const { textAreaRef } = useAutoResizeTextarea(ref, props.value);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<textarea
|
|
51
|
+
className={cn(
|
|
52
|
+
'placeholder:!text-text-placeholder flex max-h-[40vh] min-h-[80px] w-full resize-none overflow-y-auto border-none bg-transparent px-3 py-2 text-base leading-normal break-words focus:outline-hidden focus-visible:ring-0 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
|
53
|
+
className,
|
|
54
|
+
)}
|
|
55
|
+
id="chat-input"
|
|
56
|
+
onKeyDown={(event) => {
|
|
57
|
+
onKeyDown?.(event);
|
|
58
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
onSend?.();
|
|
61
|
+
}
|
|
62
|
+
}}
|
|
63
|
+
ref={textAreaRef}
|
|
64
|
+
spellCheck={false}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
ChatInputBase.displayName = 'ChatInput';
|
|
71
|
+
export const ChatInput = React.memo(ChatInputBase);
|