From 0092947b1775fbfa17860eb0c4893e7733179ecd Mon Sep 17 00:00:00 2001 From: paulclindo Date: Wed, 22 Nov 2023 13:37:02 -0500 Subject: [PATCH 01/29] setup ui kit lib --- README.md | 1 + apps/shinkai-tray/src/pages/generate-code.tsx | 2 +- libs/shinkai-ui/.babelrc | 12 + libs/shinkai-ui/.eslintrc.json | 18 + libs/shinkai-ui/README.md | 7 + libs/shinkai-ui/package.json | 4 + libs/shinkai-ui/postcss.config.js | 7 + libs/shinkai-ui/project.json | 36 + libs/shinkai-ui/src/assets/app-icon.png | Bin 0 -> 3536 bytes libs/shinkai-ui/src/components/avatar.tsx | 48 + libs/shinkai-ui/src/components/button.tsx | 70 + libs/shinkai-ui/src/components/checkbox.tsx | 28 + libs/shinkai-ui/src/components/command.tsx | 153 ++ .../src/components/copy-to-clipboard-icon.tsx | 48 + libs/shinkai-ui/src/components/dialog.tsx | 118 + .../shinkai-ui/src/components/dots-loader.tsx | 13 + .../src/components/error-message.tsx | 10 + libs/shinkai-ui/src/components/form.tsx | 181 ++ libs/shinkai-ui/src/components/hover-card.tsx | 27 + libs/shinkai-ui/src/components/input.tsx | 25 + libs/shinkai-ui/src/components/label.tsx | 24 + libs/shinkai-ui/src/components/popover.tsx | 29 + libs/shinkai-ui/src/components/qr-code.tsx | 32 + .../shinkai-ui/src/components/scroll-area.tsx | 53 + libs/shinkai-ui/src/components/select.tsx | 118 + libs/shinkai-ui/src/components/separator.tsx | 29 + libs/shinkai-ui/src/components/skeleton.tsx | 18 + libs/shinkai-ui/src/components/switch.tsx | 27 + libs/shinkai-ui/src/components/textarea.tsx | 48 + libs/shinkai-ui/src/components/tooltip.tsx | 28 + libs/shinkai-ui/src/index.ts | 24 + libs/shinkai-ui/src/styles/globals.css | 60 + libs/shinkai-ui/src/styles/tailwind.css | 3 + libs/shinkai-ui/src/utils.ts | 6 + libs/shinkai-ui/tailwind.config.js | 53 + libs/shinkai-ui/tsconfig.json | 17 + libs/shinkai-ui/tsconfig.lib.json | 24 + nx.json | 35 +- package-lock.json | 1907 ++++++++++++++++- package.json | 6 +- tsconfig.base.json | 11 +- 41 files changed, 3249 insertions(+), 111 deletions(-) create mode 100644 libs/shinkai-ui/.babelrc create mode 100644 libs/shinkai-ui/.eslintrc.json create mode 100644 libs/shinkai-ui/README.md create mode 100644 libs/shinkai-ui/package.json create mode 100644 libs/shinkai-ui/postcss.config.js create mode 100644 libs/shinkai-ui/project.json create mode 100644 libs/shinkai-ui/src/assets/app-icon.png create mode 100644 libs/shinkai-ui/src/components/avatar.tsx create mode 100644 libs/shinkai-ui/src/components/button.tsx create mode 100644 libs/shinkai-ui/src/components/checkbox.tsx create mode 100644 libs/shinkai-ui/src/components/command.tsx create mode 100644 libs/shinkai-ui/src/components/copy-to-clipboard-icon.tsx create mode 100644 libs/shinkai-ui/src/components/dialog.tsx create mode 100644 libs/shinkai-ui/src/components/dots-loader.tsx create mode 100644 libs/shinkai-ui/src/components/error-message.tsx create mode 100644 libs/shinkai-ui/src/components/form.tsx create mode 100644 libs/shinkai-ui/src/components/hover-card.tsx create mode 100644 libs/shinkai-ui/src/components/input.tsx create mode 100644 libs/shinkai-ui/src/components/label.tsx create mode 100644 libs/shinkai-ui/src/components/popover.tsx create mode 100644 libs/shinkai-ui/src/components/qr-code.tsx create mode 100644 libs/shinkai-ui/src/components/scroll-area.tsx create mode 100644 libs/shinkai-ui/src/components/select.tsx create mode 100644 libs/shinkai-ui/src/components/separator.tsx create mode 100644 libs/shinkai-ui/src/components/skeleton.tsx create mode 100644 libs/shinkai-ui/src/components/switch.tsx create mode 100644 libs/shinkai-ui/src/components/textarea.tsx create mode 100644 libs/shinkai-ui/src/components/tooltip.tsx create mode 100644 libs/shinkai-ui/src/index.ts create mode 100644 libs/shinkai-ui/src/styles/globals.css create mode 100644 libs/shinkai-ui/src/styles/tailwind.css create mode 100644 libs/shinkai-ui/src/utils.ts create mode 100644 libs/shinkai-ui/tailwind.config.js create mode 100644 libs/shinkai-ui/tsconfig.json create mode 100644 libs/shinkai-ui/tsconfig.lib.json diff --git a/README.md b/README.md index ae41f71a9..bac9cecf4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ * shinkai-message-ts: Typescript library that implements the features and networking layer to enable systems to interact with shinkai-nodes. * shinkai-node-state: Typescript library which using @tanstack/react-query enables apps to interact with shinkai-node managing the state, caching and evictions. +* shinkai-ui: React UI library to build shinkai apps. ## Getting started diff --git a/apps/shinkai-tray/src/pages/generate-code.tsx b/apps/shinkai-tray/src/pages/generate-code.tsx index d38768e5e..5ca9adf79 100644 --- a/apps/shinkai-tray/src/pages/generate-code.tsx +++ b/apps/shinkai-tray/src/pages/generate-code.tsx @@ -128,7 +128,7 @@ const GenerateCodePage = () => { profile: auth?.profile ?? '', shinkai_identity: auth?.shinkai_identity ?? '', node_address: auth?.node_address ?? '', - //TODO: remove from network lib these unused params + //TODO: remove from network components these unused params registration_code: '', identity_type: '', }, diff --git a/libs/shinkai-ui/.babelrc b/libs/shinkai-ui/.babelrc new file mode 100644 index 000000000..1ea870ead --- /dev/null +++ b/libs/shinkai-ui/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/shinkai-ui/.eslintrc.json b/libs/shinkai-ui/.eslintrc.json new file mode 100644 index 000000000..a39ac5d05 --- /dev/null +++ b/libs/shinkai-ui/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shinkai-ui/README.md b/libs/shinkai-ui/README.md new file mode 100644 index 000000000..8748babcc --- /dev/null +++ b/libs/shinkai-ui/README.md @@ -0,0 +1,7 @@ +# shinkai-ui + +Shinkai UI is a library of reusable components for Shinkai applications. + +## Running unit tests + +Run `nx test shinkai-ui` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shinkai-ui/package.json b/libs/shinkai-ui/package.json new file mode 100644 index 000000000..12111dba6 --- /dev/null +++ b/libs/shinkai-ui/package.json @@ -0,0 +1,4 @@ +{ + "name": "@shinkai/shinkai-ui", + "version": "0.0.1" +} diff --git a/libs/shinkai-ui/postcss.config.js b/libs/shinkai-ui/postcss.config.js new file mode 100644 index 000000000..d4a7a64b4 --- /dev/null +++ b/libs/shinkai-ui/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + 'tailwindcss/nesting': {}, + }, +}; diff --git a/libs/shinkai-ui/project.json b/libs/shinkai-ui/project.json new file mode 100644 index 000000000..78e863e15 --- /dev/null +++ b/libs/shinkai-ui/project.json @@ -0,0 +1,36 @@ +{ + "name": "shinkai-ui", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shinkai-ui/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/shinkai-ui/**/*.{ts,tsx,js,jsx}"] + } + }, + "build": { + "executor": "@nx/rollup:rollup", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shinkai-ui", + "tsConfig": "libs/shinkai-ui/tsconfig.lib.json", + "project": "libs/shinkai-ui/package.json", + "entryFile": "libs/shinkai-ui/src/index.ts", + "external": ["react", "react-dom", "react/jsx-runtime"], + "rollupConfig": "@nx/react/plugins/bundle-rollup", + "compiler": "babel", + "assets": [ + { + "glob": "libs/shinkai-ui/README.md", + "input": ".", + "output": "." + } + ] + } + } + } +} diff --git a/libs/shinkai-ui/src/assets/app-icon.png b/libs/shinkai-ui/src/assets/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a72a423dfbfd6b0a9b76762c4d1e0025a967f439 GIT binary patch literal 3536 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�?H!8pcY5cks|Ce3^F?Jn%Qf9v0=grI9L*1>kci)d6emu`YYf5sd z^S1b}s(jsh34U=s^#{NB(~IcAP4pp?lqq&Mc6>_g-e*%ehgsz!D}NnM+PkU9VpvV; zBE6`OQf!4on}S>3l|ib)IJ(U$q)RC_(QO3idG}ABk6`g0oGsSz96E19>5IYcF-41X z>o#yJ7P_IkB@)>gLn!;rCLN9-_m5pZUn{>$(Wc?SS+$I6w6b3*IE9Y9OdUhd%a%A2 zXah2)Y2$vkrAw3M9eA}e5J+lvJ%5I(uGmZI7=IG6q{UF>G(?@1-4VuwRtA22*0#;0 zf+#PIhf4FWQ)gkWKg0C3E1U2Ub}9T%RvKkB6Vn<`%=FK{+?!La(35#pGvTlr#oW&e zeLO4{MF}=~#JzR$I4zWV*Lri-#05RWgjUbG=#M-*?_h!hwIcd8>ND7e+$nNh*Ydd7 z#jKWOX^hA4fUoBacQk>J=dC6}Q{S9RIi>xJ!5t*0{`zCJ5^Nx9ZL_^EGM6r>B}t#7 zC9~huPkJqr!h=ZjB(dTsN0Cw**9gG z*|C_}Wn;eRC4JVL)uz4Mh(|nK5}`|Zd+vn+pOXkQ->Ebo40;o%$JHI4REQF|XU%h^ zF|yP?ZfXKq0h3ILA>6J-{-=Jofs55ZGpe=u{OrSiyur~5t!imrq z4n8n9i{18|F?<`N%CE2YvVm($8McC>9O&1+0F7DoE7BXvM-7~FPei1vG2+ClDu76W zN|^F+1OvkA2yVq`ZBliSp_GIrW3$z?Lpqy|_wjlsFcry(8SYO2ULvZA!3U({G6uJz zwDA?#8KQ$H%ji#aOkk-4c2#9*=TELww@(6SjhIT>H;`Yp4<-fm?{C5b)xD_%$~v#dUO2P4N3>~ zAK_x108oxVqx*NBL6(WcDGJA^*Ly;+bE>G;u7nEF2}$SB+%MHIgx-RHWmP+TCw_=C z00&DTsx30`^@T$n!^O}U_Q*G{e;dLK(IqYD}?#@^b?e7|(@Ije?oN>c(inoQm>$fWP{*^kA3s&aH-AI&lJ w@E|@SLN+uU*DKfLk8=U5|KE#4FNq@Qs%`Nlx2?SY?|>1hDZv!W6)Z#k0~@@x;{X5v literal 0 HcmV?d00001 diff --git a/libs/shinkai-ui/src/components/avatar.tsx b/libs/shinkai-ui/src/components/avatar.tsx new file mode 100644 index 000000000..3a3f99205 --- /dev/null +++ b/libs/shinkai-ui/src/components/avatar.tsx @@ -0,0 +1,48 @@ +import * as AvatarPrimitive from '@radix-ui/react-avatar'; +import * as React from 'react'; + +import { cn } from '../utils'; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/libs/shinkai-ui/src/components/button.tsx b/libs/shinkai-ui/src/components/button.tsx new file mode 100644 index 000000000..dcdc438b3 --- /dev/null +++ b/libs/shinkai-ui/src/components/button.tsx @@ -0,0 +1,70 @@ +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { Loader } from 'lucide-react'; +import * as React from 'react'; + +import { cn } from '../utils'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-gray-300', + { + variants: { + variant: { + default: + 'bg-primary-600 hover:bg-primary-700 text-white shadow dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90', + destructive: + 'bg-red-500 text-gray-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90', + outline: + 'border border-gray-200 bg-transparent shadow-sm hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:hover:bg-gray-800 dark:hover:text-gray-50', + secondary: + 'bg-gray-100 text-gray-900 shadow-sm hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80', + ghost: + 'hover:bg-gray-800 hover:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-50', + link: 'text-gray-900 underline-offset-4 hover:underline dark:text-gray-50', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; + isLoading?: boolean; +} + +const Button = React.forwardRef( + ( + { className, variant, size, asChild = false, isLoading = false, ...props }, + ref, + ) => { + const Comp = asChild ? Slot : 'button'; + return ( + + {isLoading ? ( + + ) : null} + {isLoading && size === 'icon' ? null : props.children} + + ); + }, +); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/libs/shinkai-ui/src/components/checkbox.tsx b/libs/shinkai-ui/src/components/checkbox.tsx new file mode 100644 index 000000000..1e5fd8154 --- /dev/null +++ b/libs/shinkai-ui/src/components/checkbox.tsx @@ -0,0 +1,28 @@ +import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; +import { CheckIcon } from '@radix-ui/react-icons'; +import * as React from 'react'; + +import { cn } from '../utils'; + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox }; diff --git a/libs/shinkai-ui/src/components/command.tsx b/libs/shinkai-ui/src/components/command.tsx new file mode 100644 index 000000000..7b50f5019 --- /dev/null +++ b/libs/shinkai-ui/src/components/command.tsx @@ -0,0 +1,153 @@ +import { DialogProps } from '@radix-ui/react-dialog'; +import { MagnifyingGlassIcon } from '@radix-ui/react-icons'; +import { Command as CommandPrimitive } from 'cmdk'; +import * as React from 'react'; + +import { cn } from '../utils'; +import { Dialog, DialogContent } from './dialog'; + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Command.displayName = CommandPrimitive.displayName; + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ); +}; + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)); + +CommandInput.displayName = CommandPrimitive.Input.displayName; + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandList.displayName = CommandPrimitive.List.displayName; + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)); + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandGroup.displayName = CommandPrimitive.Group.displayName; + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandItem.displayName = CommandPrimitive.Item.displayName; + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +CommandShortcut.displayName = 'CommandShortcut'; + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}; diff --git a/libs/shinkai-ui/src/components/copy-to-clipboard-icon.tsx b/libs/shinkai-ui/src/components/copy-to-clipboard-icon.tsx new file mode 100644 index 000000000..69dc37972 --- /dev/null +++ b/libs/shinkai-ui/src/components/copy-to-clipboard-icon.tsx @@ -0,0 +1,48 @@ +import { CheckCircle2, CopyIcon } from 'lucide-react'; +import { useState } from 'react'; + +import { cn } from '../utils'; +import { Button } from './button'; + +type CopyToClipboardIconProps = { + string?: string; + children?: React.ReactNode; + className?: string; +}; + +const CopyToClipboardIcon = ({ + string, + children, + className, +}: CopyToClipboardIconProps) => { + const [clipboard, setClipboard] = useState(false); + + let timeout: ReturnType; + const onCopy = () => { + if (!string) return; + const string_ = string.trim(); + navigator.clipboard.writeText(string_); + setClipboard(true); + clearTimeout(timeout); + timeout = setTimeout(() => setClipboard(false), 1000); + }; + + const ClipboardIcon = clipboard ? CheckCircle2 : CopyIcon; + + return ( + + ); +}; + +export default CopyToClipboardIcon; diff --git a/libs/shinkai-ui/src/components/dialog.tsx b/libs/shinkai-ui/src/components/dialog.tsx new file mode 100644 index 000000000..afc094c2a --- /dev/null +++ b/libs/shinkai-ui/src/components/dialog.tsx @@ -0,0 +1,118 @@ +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { Cross2Icon } from '@radix-ui/react-icons'; +import * as React from 'react'; + +import { cn } from '../utils'; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = ({ ...props }: DialogPrimitive.DialogPortalProps) => ( + +); +DialogPortal.displayName = DialogPrimitive.Portal.displayName; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/libs/shinkai-ui/src/components/dots-loader.tsx b/libs/shinkai-ui/src/components/dots-loader.tsx new file mode 100644 index 000000000..18d01ceb4 --- /dev/null +++ b/libs/shinkai-ui/src/components/dots-loader.tsx @@ -0,0 +1,13 @@ +const DotsLoader = ({ className }: { className?: string }) => { + return ( +
+
+
+
+
+
+
+ ); +}; + +export default DotsLoader; diff --git a/libs/shinkai-ui/src/components/error-message.tsx b/libs/shinkai-ui/src/components/error-message.tsx new file mode 100644 index 000000000..f075ad541 --- /dev/null +++ b/libs/shinkai-ui/src/components/error-message.tsx @@ -0,0 +1,10 @@ +const ErrorMessage = ({ message }: { message: string }) => { + return ( +
+ Error: + {message} +
+ ); +}; + +export default ErrorMessage; diff --git a/libs/shinkai-ui/src/components/form.tsx b/libs/shinkai-ui/src/components/form.tsx new file mode 100644 index 000000000..5486ce661 --- /dev/null +++ b/libs/shinkai-ui/src/components/form.tsx @@ -0,0 +1,181 @@ +import * as LabelPrimitive from '@radix-ui/react-label'; +import { Slot } from '@radix-ui/react-slot'; +import * as React from 'react'; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from 'react-hook-form'; + +import { cn } from '../utils'; +import { Label } from './label'; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error('useFormField should be used within '); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = 'FormItem'; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +