@hexclave/ui 1.0.3 → 1.0.6
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/dist/components/copy-button.d.ts +1 -1
- package/dist/components/simple-tooltip.js +2 -2
- package/dist/components/simple-tooltip.js.map +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/form.d.ts +1 -1
- package/dist/components/ui/form.d.ts.map +1 -1
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/typography.d.ts +1 -1
- package/dist/esm/components/copy-button.d.ts +1 -1
- package/dist/esm/components/simple-tooltip.js +3 -3
- package/dist/esm/components/simple-tooltip.js.map +1 -1
- package/dist/esm/components/ui/button.d.ts +2 -2
- package/dist/esm/components/ui/form.d.ts +1 -1
- package/dist/esm/components/ui/form.d.ts.map +1 -1
- package/dist/esm/components/ui/resizable.d.ts +1 -1
- package/dist/esm/components/ui/resizable.d.ts.map +1 -1
- package/dist/esm/components/ui/typography.d.ts +1 -1
- package/package.json +3 -2
- package/src/components/action-dialog.tsx +135 -0
- package/src/components/brand-icons.tsx +276 -0
- package/src/components/browser-frame/LICENSE +3 -0
- package/src/components/browser-frame/index.tsx +51 -0
- package/src/components/copy-button.tsx +36 -0
- package/src/components/copy-field.tsx +52 -0
- package/src/components/data-table/cells.tsx +133 -0
- package/src/components/data-table/column-header.tsx +52 -0
- package/src/components/data-table/data-table.tsx +318 -0
- package/src/components/data-table/faceted-filter.tsx +138 -0
- package/src/components/data-table/index.tsx +9 -0
- package/src/components/data-table/pagination.tsx +76 -0
- package/src/components/data-table/toolbar-items.tsx +13 -0
- package/src/components/data-table/toolbar.tsx +100 -0
- package/src/components/data-table/utils.tsx +7 -0
- package/src/components/data-table/view-options.tsx +64 -0
- package/src/components/simple-tooltip.tsx +48 -0
- package/src/components/ui/accordion.tsx +58 -0
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/aspect-ratio.tsx +7 -0
- package/src/components/ui/avatar.tsx +51 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/breadcrumb.tsx +116 -0
- package/src/components/ui/button.tsx +95 -0
- package/src/components/ui/calendar.tsx +77 -0
- package/src/components/ui/card.tsx +88 -0
- package/src/components/ui/checkbox.tsx +31 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/command.tsx +159 -0
- package/src/components/ui/context-menu.tsx +205 -0
- package/src/components/ui/dialog.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +245 -0
- package/src/components/ui/form.tsx +173 -0
- package/src/components/ui/hover-card.tsx +30 -0
- package/src/components/ui/inline-code.tsx +40 -0
- package/src/components/ui/input-otp.tsx +73 -0
- package/src/components/ui/input.tsx +68 -0
- package/src/components/ui/label.tsx +40 -0
- package/src/components/ui/menubar.tsx +241 -0
- package/src/components/ui/navigation-menu.tsx +131 -0
- package/src/components/ui/password-input.tsx +50 -0
- package/src/components/ui/popover.tsx +34 -0
- package/src/components/ui/progress.tsx +29 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/resizable.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +49 -0
- package/src/components/ui/select.tsx +162 -0
- package/src/components/ui/separator.tsx +32 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/skeleton.tsx +23 -0
- package/src/components/ui/slider.tsx +29 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/switch.tsx +75 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +56 -0
- package/src/components/ui/textarea.tsx +24 -0
- package/src/components/ui/toast.tsx +135 -0
- package/src/components/ui/toaster.tsx +35 -0
- package/src/components/ui/toggle-group.tsx +62 -0
- package/src/components/ui/toggle.tsx +46 -0
- package/src/components/ui/tooltip.tsx +40 -0
- package/src/components/ui/typography.tsx +47 -0
- package/src/components/ui/use-toast.tsx +195 -0
- package/src/index.ts +54 -0
- package/src/lib/utils.tsx +6 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
4
|
+
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "@radix-ui/react-icons";
|
|
5
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
6
|
+
import React from "react";
|
|
7
|
+
|
|
8
|
+
import { useAsyncCallback } from "@hexclave/shared/dist/hooks/use-async-callback";
|
|
9
|
+
import { throwErr } from "@hexclave/shared/dist/utils/errors";
|
|
10
|
+
import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
|
|
11
|
+
import { cn } from "../../lib/utils";
|
|
12
|
+
import { Spinner } from "./spinner";
|
|
13
|
+
|
|
14
|
+
const DropdownMenuContext = React.createContext<{
|
|
15
|
+
open: boolean,
|
|
16
|
+
setOpen: (open: boolean) => void,
|
|
17
|
+
} | undefined>(undefined);
|
|
18
|
+
|
|
19
|
+
const DropdownMenu = forwardRefIfNeeded<
|
|
20
|
+
never,
|
|
21
|
+
DropdownMenuPrimitive.DropdownMenuProps
|
|
22
|
+
>(({ ...props }, ref) => {
|
|
23
|
+
const [open, setOpen] = React.useState(!!props.open);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<DropdownMenuContext.Provider value={{
|
|
27
|
+
open,
|
|
28
|
+
setOpen: (o) => {
|
|
29
|
+
props.onOpenChange?.(o);
|
|
30
|
+
setOpen(o);
|
|
31
|
+
}
|
|
32
|
+
}}>
|
|
33
|
+
<DropdownMenuPrimitive.Root
|
|
34
|
+
{...props}
|
|
35
|
+
open={props.open ?? open}
|
|
36
|
+
onOpenChange={o => {
|
|
37
|
+
props.onOpenChange?.(o);
|
|
38
|
+
setOpen(o);
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
</DropdownMenuContext.Provider>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
DropdownMenu.displayName = DropdownMenuPrimitive.Root.displayName;
|
|
45
|
+
|
|
46
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
47
|
+
|
|
48
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
49
|
+
|
|
50
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
51
|
+
|
|
52
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
53
|
+
|
|
54
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
55
|
+
|
|
56
|
+
const DropdownMenuSubTrigger = forwardRefIfNeeded<
|
|
57
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
58
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
59
|
+
inset?: boolean,
|
|
60
|
+
}
|
|
61
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
62
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cn(
|
|
65
|
+
"stack-scope flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
|
66
|
+
inset && "pl-8",
|
|
67
|
+
className
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
<ChevronRightIcon className="ml-auto h-4 w-4" />
|
|
73
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
74
|
+
));
|
|
75
|
+
DropdownMenuSubTrigger.displayName =
|
|
76
|
+
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
77
|
+
|
|
78
|
+
const DropdownMenuSubContent = forwardRefIfNeeded<
|
|
79
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
80
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
81
|
+
>(({ className, ...props }, ref) => (
|
|
82
|
+
<DropdownMenuPrimitive.SubContent
|
|
83
|
+
ref={ref}
|
|
84
|
+
className={cn(
|
|
85
|
+
"stack-scope z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 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",
|
|
86
|
+
className
|
|
87
|
+
)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
));
|
|
91
|
+
DropdownMenuSubContent.displayName =
|
|
92
|
+
DropdownMenuPrimitive.SubContent.displayName;
|
|
93
|
+
|
|
94
|
+
const DropdownMenuContent = forwardRefIfNeeded<
|
|
95
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
96
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
97
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
98
|
+
<DropdownMenuPrimitive.Portal>
|
|
99
|
+
<DropdownMenuPrimitive.Content
|
|
100
|
+
ref={ref}
|
|
101
|
+
sideOffset={sideOffset}
|
|
102
|
+
className={cn(
|
|
103
|
+
"stack-scope z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
104
|
+
"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",
|
|
105
|
+
className
|
|
106
|
+
)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
</DropdownMenuPrimitive.Portal>
|
|
110
|
+
));
|
|
111
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
112
|
+
|
|
113
|
+
const DropdownMenuItem = forwardRefIfNeeded<
|
|
114
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
115
|
+
Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>, "onClick"> & {
|
|
116
|
+
inset?: boolean,
|
|
117
|
+
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void | Promise<void>,
|
|
118
|
+
}
|
|
119
|
+
>(({ className, inset, ...props }, ref) => {
|
|
120
|
+
const { setOpen } = React.useContext(DropdownMenuContext) ?? throwErr("No DropdownMenuContext found");
|
|
121
|
+
const [handleClick, isLoading] = useAsyncCallback(async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
122
|
+
await props.onClick?.(e);
|
|
123
|
+
setOpen(false);
|
|
124
|
+
}, [props.onClick]);
|
|
125
|
+
|
|
126
|
+
return <DropdownMenuPrimitive.Item
|
|
127
|
+
ref={ref}
|
|
128
|
+
className={cn(
|
|
129
|
+
"stack-scope relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
130
|
+
inset && "pl-8",
|
|
131
|
+
className
|
|
132
|
+
)}
|
|
133
|
+
{...props}
|
|
134
|
+
disabled={isLoading || props.disabled}
|
|
135
|
+
onClick={props.onClick ? (e) => {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
runAsynchronouslyWithAlert(handleClick(e));
|
|
139
|
+
} : undefined}
|
|
140
|
+
>
|
|
141
|
+
<div style={{ visibility: isLoading ? "visible" : "hidden", position: "absolute", inset: 0, display: "flex", justifyContent: "center", alignItems: "center" }}>
|
|
142
|
+
<Spinner />
|
|
143
|
+
</div>
|
|
144
|
+
<div style={{ visibility: isLoading ? "hidden" : "visible" }}>
|
|
145
|
+
{props.children}
|
|
146
|
+
</div>
|
|
147
|
+
</DropdownMenuPrimitive.Item>;
|
|
148
|
+
});
|
|
149
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
150
|
+
|
|
151
|
+
const DropdownMenuCheckboxItem = forwardRefIfNeeded<
|
|
152
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
153
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
154
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
155
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
156
|
+
ref={ref}
|
|
157
|
+
className={cn(
|
|
158
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
159
|
+
className
|
|
160
|
+
)}
|
|
161
|
+
checked={checked}
|
|
162
|
+
{...props}
|
|
163
|
+
>
|
|
164
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
165
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
166
|
+
<CheckIcon className="h-4 w-4" />
|
|
167
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
168
|
+
</span>
|
|
169
|
+
{children}
|
|
170
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
171
|
+
));
|
|
172
|
+
DropdownMenuCheckboxItem.displayName =
|
|
173
|
+
DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
174
|
+
|
|
175
|
+
const DropdownMenuRadioItem = forwardRefIfNeeded<
|
|
176
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
177
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
|
178
|
+
>(({ className, children, ...props }, ref) => (
|
|
179
|
+
<DropdownMenuPrimitive.RadioItem
|
|
180
|
+
ref={ref}
|
|
181
|
+
className={cn(
|
|
182
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
183
|
+
className
|
|
184
|
+
)}
|
|
185
|
+
{...props}
|
|
186
|
+
>
|
|
187
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
188
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
189
|
+
<DotFilledIcon className="h-4 w-4 fill-current" />
|
|
190
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
191
|
+
</span>
|
|
192
|
+
{children}
|
|
193
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
194
|
+
));
|
|
195
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
196
|
+
|
|
197
|
+
const DropdownMenuLabel = forwardRefIfNeeded<
|
|
198
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
199
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
200
|
+
inset?: boolean,
|
|
201
|
+
}
|
|
202
|
+
>(({ className, inset, ...props }, ref) => (
|
|
203
|
+
<DropdownMenuPrimitive.Label
|
|
204
|
+
ref={ref}
|
|
205
|
+
className={cn(
|
|
206
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
207
|
+
inset && "pl-8",
|
|
208
|
+
className
|
|
209
|
+
)}
|
|
210
|
+
{...props}
|
|
211
|
+
/>
|
|
212
|
+
));
|
|
213
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
214
|
+
|
|
215
|
+
const DropdownMenuSeparator = forwardRefIfNeeded<
|
|
216
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
217
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
218
|
+
>(({ className, ...props }, ref) => (
|
|
219
|
+
<DropdownMenuPrimitive.Separator
|
|
220
|
+
ref={ref}
|
|
221
|
+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
222
|
+
{...props}
|
|
223
|
+
/>
|
|
224
|
+
));
|
|
225
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
226
|
+
|
|
227
|
+
const DropdownMenuShortcut = ({
|
|
228
|
+
className,
|
|
229
|
+
...props
|
|
230
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
231
|
+
return (
|
|
232
|
+
<span
|
|
233
|
+
className={cn("stack-scope ml-auto text-xs tracking-widest opacity-60", className)}
|
|
234
|
+
{...props}
|
|
235
|
+
/>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
239
|
+
|
|
240
|
+
export {
|
|
241
|
+
DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator,
|
|
242
|
+
DropdownMenuShortcut, DropdownMenuSub,
|
|
243
|
+
DropdownMenuSubContent,
|
|
244
|
+
DropdownMenuSubTrigger, DropdownMenuTrigger
|
|
245
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
7
|
+
import {
|
|
8
|
+
Controller,
|
|
9
|
+
ControllerProps,
|
|
10
|
+
FieldPath,
|
|
11
|
+
FieldValues,
|
|
12
|
+
FormProvider,
|
|
13
|
+
useFormContext,
|
|
14
|
+
} from "react-hook-form";
|
|
15
|
+
|
|
16
|
+
import { HexclaveAssertionError } from "@hexclave/shared/dist/utils/errors";
|
|
17
|
+
import { cn } from "../../lib/utils";
|
|
18
|
+
import { SpanLabel } from "./label";
|
|
19
|
+
|
|
20
|
+
const Form = FormProvider;
|
|
21
|
+
|
|
22
|
+
type FormFieldContextValue<
|
|
23
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
24
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
|
25
|
+
> = {
|
|
26
|
+
name: TName,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const FormFieldContext = React.createContext<FormFieldContextValue | null>(null);
|
|
30
|
+
|
|
31
|
+
const FormField = <
|
|
32
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
33
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
|
34
|
+
>({
|
|
35
|
+
...props
|
|
36
|
+
}: ControllerProps<TFieldValues, TName>) => {
|
|
37
|
+
return (
|
|
38
|
+
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
39
|
+
<Controller {...props} />
|
|
40
|
+
</FormFieldContext.Provider>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const useFormField = () => {
|
|
45
|
+
const fieldContext = React.useContext(FormFieldContext);
|
|
46
|
+
const itemContext = React.useContext(FormItemContext);
|
|
47
|
+
const { getFieldState, formState } = useFormContext();
|
|
48
|
+
|
|
49
|
+
if (!fieldContext) {
|
|
50
|
+
throw new HexclaveAssertionError("useFormField should be used within <FormField>");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
54
|
+
|
|
55
|
+
const { id } = itemContext;
|
|
56
|
+
const formItemId = React.useId();
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
id,
|
|
60
|
+
name: fieldContext.name,
|
|
61
|
+
formItemId: `${formItemId}-form-item`,
|
|
62
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
63
|
+
formMessageId: `${id}-form-item-message`,
|
|
64
|
+
...fieldState,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type FormItemContextValue = {
|
|
69
|
+
id: string,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const FormItemContext = React.createContext<FormItemContextValue>(
|
|
73
|
+
{} as FormItemContextValue
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const FormItem = forwardRefIfNeeded<
|
|
77
|
+
HTMLDivElement,
|
|
78
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
79
|
+
>(({ className, ...props }, ref) => {
|
|
80
|
+
const id = React.useId();
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<FormItemContext.Provider value={{ id }}>
|
|
84
|
+
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
|
85
|
+
</FormItemContext.Provider>
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
FormItem.displayName = "FormItem";
|
|
89
|
+
|
|
90
|
+
const FormLabel = forwardRefIfNeeded<
|
|
91
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
92
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
93
|
+
>(({ className, ...props }, ref) => {
|
|
94
|
+
const { error } = useFormField();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<SpanLabel
|
|
98
|
+
ref={ref}
|
|
99
|
+
className={cn(error && "text-destructive", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
FormLabel.displayName = "FormLabel";
|
|
105
|
+
|
|
106
|
+
const FormControl = forwardRefIfNeeded<
|
|
107
|
+
React.ElementRef<typeof Slot>,
|
|
108
|
+
React.ComponentPropsWithoutRef<typeof Slot>
|
|
109
|
+
>(({ ...props }, ref) => {
|
|
110
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Slot
|
|
114
|
+
ref={ref}
|
|
115
|
+
id={formItemId}
|
|
116
|
+
aria-describedby={
|
|
117
|
+
!error
|
|
118
|
+
? `${formDescriptionId}`
|
|
119
|
+
: `${formDescriptionId} ${formMessageId}`
|
|
120
|
+
}
|
|
121
|
+
aria-invalid={!!error}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
FormControl.displayName = "FormControl";
|
|
127
|
+
|
|
128
|
+
const FormDescription = forwardRefIfNeeded<
|
|
129
|
+
HTMLParagraphElement,
|
|
130
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
131
|
+
>(({ className, ...props }, ref) => {
|
|
132
|
+
const { formDescriptionId } = useFormField();
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<p
|
|
136
|
+
ref={ref}
|
|
137
|
+
id={formDescriptionId}
|
|
138
|
+
className={cn("text-[0.8rem] text-muted-foreground", className)}
|
|
139
|
+
{...props}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
FormDescription.displayName = "FormDescription";
|
|
144
|
+
|
|
145
|
+
const FormMessage = forwardRefIfNeeded<
|
|
146
|
+
HTMLParagraphElement,
|
|
147
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
148
|
+
>(({ className, children, ...props }, ref) => {
|
|
149
|
+
const { error, formMessageId } = useFormField();
|
|
150
|
+
const body = error ? String(error.message) : children;
|
|
151
|
+
|
|
152
|
+
if (!body) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<p
|
|
158
|
+
ref={ref}
|
|
159
|
+
id={formMessageId}
|
|
160
|
+
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
|
161
|
+
{...props}
|
|
162
|
+
>
|
|
163
|
+
{body}
|
|
164
|
+
</p>
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
FormMessage.displayName = "FormMessage";
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
Form, FormControl,
|
|
171
|
+
FormDescription, FormField, FormItem,
|
|
172
|
+
FormLabel, FormMessage, useFormField
|
|
173
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
5
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils";
|
|
8
|
+
|
|
9
|
+
const HoverCard = HoverCardPrimitive.Root;
|
|
10
|
+
|
|
11
|
+
const HoverCardTrigger = HoverCardPrimitive.Trigger;
|
|
12
|
+
|
|
13
|
+
const HoverCardContent = forwardRefIfNeeded<
|
|
14
|
+
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
|
16
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
17
|
+
<HoverCardPrimitive.Content
|
|
18
|
+
ref={ref}
|
|
19
|
+
align={align}
|
|
20
|
+
sideOffset={sideOffset}
|
|
21
|
+
className={cn(
|
|
22
|
+
"stack-scope z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
));
|
|
28
|
+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
|
29
|
+
|
|
30
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
6
|
+
import { hasClickableParent } from "@hexclave/shared/dist/utils/dom";
|
|
7
|
+
import { getNodeText } from "@hexclave/shared/dist/utils/react";
|
|
8
|
+
import { runAsynchronously } from "@hexclave/shared/dist/utils/promises";
|
|
9
|
+
import { useToast } from "./use-toast";
|
|
10
|
+
|
|
11
|
+
const InlineCode = forwardRefIfNeeded<
|
|
12
|
+
React.ElementRef<"code">,
|
|
13
|
+
React.ComponentPropsWithoutRef<"code">
|
|
14
|
+
>((props, ref) => {
|
|
15
|
+
const { toast } = useToast();
|
|
16
|
+
|
|
17
|
+
return <code
|
|
18
|
+
ref={ref}
|
|
19
|
+
{...props}
|
|
20
|
+
className={cn("stack-scope bg-zinc-200 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300 rounded-sm px-1 cursor-pointer", props.className)}
|
|
21
|
+
onClick={(e: React.MouseEvent<HTMLElement>) => {
|
|
22
|
+
props.onClick?.(e);
|
|
23
|
+
if (!hasClickableParent(e.currentTarget)) {
|
|
24
|
+
e.stopPropagation();
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
runAsynchronously(async () => {
|
|
27
|
+
try {
|
|
28
|
+
await navigator.clipboard.writeText(getNodeText(props.children));
|
|
29
|
+
toast({ description: 'Copied to clipboard!', variant: 'success' });
|
|
30
|
+
} catch (e) {
|
|
31
|
+
toast({ description: 'Failed to copy to clipboard', variant: 'destructive' });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}}
|
|
36
|
+
/>;
|
|
37
|
+
});
|
|
38
|
+
InlineCode.displayName = "Code";
|
|
39
|
+
|
|
40
|
+
export { InlineCode };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
5
|
+
import { DashIcon } from "@radix-ui/react-icons";
|
|
6
|
+
import { OTPInput, OTPInputContext } from "input-otp";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
9
|
+
|
|
10
|
+
const InputOTP: React.FC<React.ComponentPropsWithoutRef<typeof OTPInput>> = forwardRefIfNeeded<
|
|
11
|
+
React.ElementRef<typeof OTPInput>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof OTPInput>
|
|
13
|
+
>(({ className, containerClassName, ...props }, ref) => (
|
|
14
|
+
<OTPInput
|
|
15
|
+
ref={ref}
|
|
16
|
+
containerClassName={cn(
|
|
17
|
+
"stack-scope flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
18
|
+
containerClassName
|
|
19
|
+
)}
|
|
20
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
));
|
|
24
|
+
InputOTP.displayName = "InputOTP";
|
|
25
|
+
|
|
26
|
+
const InputOTPGroup = forwardRefIfNeeded<
|
|
27
|
+
React.ElementRef<"div">,
|
|
28
|
+
React.ComponentPropsWithoutRef<"div">
|
|
29
|
+
>(({ className, ...props }, ref) => (
|
|
30
|
+
<div ref={ref} className={cn("flex items-center gap-1", className)} {...props} />
|
|
31
|
+
));
|
|
32
|
+
InputOTPGroup.displayName = "InputOTPGroup";
|
|
33
|
+
|
|
34
|
+
const InputOTPSlot = forwardRefIfNeeded<
|
|
35
|
+
React.ElementRef<"div">,
|
|
36
|
+
React.ComponentPropsWithoutRef<"div"> & { index: number, size?: 'default' | 'lg' }
|
|
37
|
+
>(({ index, className, size = 'default', ...props }, ref) => {
|
|
38
|
+
const inputOTPContext = React.useContext(OTPInputContext);
|
|
39
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn(
|
|
45
|
+
"relative flex h-9 w-9 items-center justify-center border border-input text-sm rounded-md",
|
|
46
|
+
size === 'lg' ? 'h-10 w-10 text-lg font-medium' : '',
|
|
47
|
+
isActive && "z-10 ring-1 ring-ring",
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{char}
|
|
53
|
+
{hasFakeCaret && (
|
|
54
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
55
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
InputOTPSlot.displayName = "InputOTPSlot";
|
|
62
|
+
|
|
63
|
+
const InputOTPSeparator = forwardRefIfNeeded<
|
|
64
|
+
React.ElementRef<"div">,
|
|
65
|
+
React.ComponentPropsWithoutRef<"div">
|
|
66
|
+
>(({ ...props }, ref) => (
|
|
67
|
+
<div ref={ref} role="separator" {...props}>
|
|
68
|
+
<DashIcon />
|
|
69
|
+
</div>
|
|
70
|
+
));
|
|
71
|
+
InputOTPSeparator.displayName = "InputOTPSeparator";
|
|
72
|
+
|
|
73
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
|
|
6
|
+
export type InputProps = {
|
|
7
|
+
prefixItem?: React.ReactNode,
|
|
8
|
+
} & React.InputHTMLAttributes<HTMLInputElement>
|
|
9
|
+
|
|
10
|
+
export const Input = forwardRefIfNeeded<HTMLInputElement, InputProps>(
|
|
11
|
+
({ className, type, prefixItem, ...props }, ref) => {
|
|
12
|
+
const baseClasses = "stack-scope flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
|
|
13
|
+
|
|
14
|
+
if (prefixItem) {
|
|
15
|
+
return (
|
|
16
|
+
<div className="flex flex-row items-center backdrop-blur-md bg-white/20 dark:bg-black/20 flex-1">
|
|
17
|
+
<div className={'flex self-stretch justify-center items-center text-muted-foreground pl-3 select-none bg-muted/70 pr-3 border-r border-input rounded-l-md'}>
|
|
18
|
+
{prefixItem}
|
|
19
|
+
</div>
|
|
20
|
+
<input
|
|
21
|
+
type={type}
|
|
22
|
+
className={cn(baseClasses, 'rounded-l-none', className)}
|
|
23
|
+
ref={ref}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
} else {
|
|
29
|
+
return (
|
|
30
|
+
<div className="flex flex-row items-center backdrop-blur-md bg-white/20 dark:bg-black/20 flex-1">
|
|
31
|
+
<input
|
|
32
|
+
type={type}
|
|
33
|
+
className={cn(baseClasses, className)}
|
|
34
|
+
ref={ref}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
Input.displayName = "Input";
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export type DelayedInputProps = {
|
|
46
|
+
delay?: number,
|
|
47
|
+
} & InputProps
|
|
48
|
+
|
|
49
|
+
export const DelayedInput = forwardRefIfNeeded<HTMLInputElement, DelayedInputProps>(
|
|
50
|
+
({ delay = 500, defaultValue, ...props }, ref) => {
|
|
51
|
+
const [value, setValue] = React.useState(defaultValue ?? "");
|
|
52
|
+
|
|
53
|
+
const timeout = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
54
|
+
|
|
55
|
+
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
56
|
+
setValue(e.target.value);
|
|
57
|
+
if (timeout.current) {
|
|
58
|
+
clearTimeout(timeout.current);
|
|
59
|
+
}
|
|
60
|
+
timeout.current = setTimeout(() => {
|
|
61
|
+
props.onChange?.(e);
|
|
62
|
+
}, delay);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return <Input ref={ref} {...props} value={value} onChange={onChange} />;
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
DelayedInput.displayName = "DelayedInput";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
|
|
5
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
6
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
9
|
+
|
|
10
|
+
const labelVariants = cva(
|
|
11
|
+
"stack-scope text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-gray-600 dark:text-gray-400",
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const Label = forwardRefIfNeeded<
|
|
15
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
17
|
+
VariantProps<typeof labelVariants>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<label
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn(labelVariants(), className)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
));
|
|
25
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
26
|
+
|
|
27
|
+
const SpanLabel = forwardRefIfNeeded<
|
|
28
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
29
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
30
|
+
VariantProps<typeof labelVariants>
|
|
31
|
+
>(({ className, ...props }, ref) => (
|
|
32
|
+
<span
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={cn(labelVariants(), className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
));
|
|
38
|
+
SpanLabel.displayName = LabelPrimitive.Root.displayName;
|
|
39
|
+
|
|
40
|
+
export { Label, SpanLabel };
|