From e73df42368d3c426abc1a4d4ed36575a8e590fc0 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:35:52 +0000 Subject: [PATCH 1/2] feat: add shadcn/ui registry for medusa-forms components - Add main registry.json with shadcn/ui schema - Create registry files for all UI components (input, select, checkbox, textarea, datepicker, currency-input) - Create registry files for all controlled components with react-hook-form integration - Add field-wrapper, field-error, and label base components - Include proper dependency management between components - Add comprehensive documentation in REGISTRY.md This allows developers to install medusa-forms components using: npx shadcn@latest add --registry --- packages/medusa-forms/REGISTRY.md | 118 ++++++++++++++++++ packages/medusa-forms/registry.json | 18 +++ .../registry/controlled-checkbox.json | 18 +++ .../registry/controlled-currency-input.json | 18 +++ .../registry/controlled-datepicker.json | 18 +++ .../registry/controlled-input.json | 18 +++ .../registry/controlled-select.json | 18 +++ .../registry/controlled-textarea.json | 18 +++ .../medusa-forms/registry/currency-input.json | 18 +++ .../medusa-forms/registry/datepicker.json | 18 +++ .../medusa-forms/registry/field-checkbox.json | 19 +++ .../medusa-forms/registry/field-error.json | 16 +++ .../medusa-forms/registry/field-wrapper.json | 23 ++++ packages/medusa-forms/registry/input.json | 18 +++ packages/medusa-forms/registry/label.json | 16 +++ packages/medusa-forms/registry/select.json | 18 +++ packages/medusa-forms/registry/textarea.json | 18 +++ 17 files changed, 408 insertions(+) create mode 100644 packages/medusa-forms/REGISTRY.md create mode 100644 packages/medusa-forms/registry.json create mode 100644 packages/medusa-forms/registry/controlled-checkbox.json create mode 100644 packages/medusa-forms/registry/controlled-currency-input.json create mode 100644 packages/medusa-forms/registry/controlled-datepicker.json create mode 100644 packages/medusa-forms/registry/controlled-input.json create mode 100644 packages/medusa-forms/registry/controlled-select.json create mode 100644 packages/medusa-forms/registry/controlled-textarea.json create mode 100644 packages/medusa-forms/registry/currency-input.json create mode 100644 packages/medusa-forms/registry/datepicker.json create mode 100644 packages/medusa-forms/registry/field-checkbox.json create mode 100644 packages/medusa-forms/registry/field-error.json create mode 100644 packages/medusa-forms/registry/field-wrapper.json create mode 100644 packages/medusa-forms/registry/input.json create mode 100644 packages/medusa-forms/registry/label.json create mode 100644 packages/medusa-forms/registry/select.json create mode 100644 packages/medusa-forms/registry/textarea.json diff --git a/packages/medusa-forms/REGISTRY.md b/packages/medusa-forms/REGISTRY.md new file mode 100644 index 0000000..e9540a8 --- /dev/null +++ b/packages/medusa-forms/REGISTRY.md @@ -0,0 +1,118 @@ +# Medusa Forms Registry + +This package provides a custom shadcn/ui registry that allows developers to install medusa-forms components using the native shadcn CLI. + +## Installation + +You can install components from this registry using the shadcn CLI: + +```bash +npx shadcn@latest add --registry https://raw.githubusercontent.com/lambda-curry/medusa-forms/main/packages/medusa-forms/registry.json input +``` + +## Available Components + +### Base UI Components + +- `field-wrapper` - Core wrapper component with error handling and labels +- `field-error` - Error display component +- `label` - Label component with tooltip support +- `input` - Base input component +- `select` - Base select component +- `field-checkbox` - Base checkbox component +- `textarea` - Base textarea component +- `datepicker` - Base datepicker component +- `currency-input` - Base currency input component + +### Controlled Components (React Hook Form) + +- `controlled-input` - Input with react-hook-form integration +- `controlled-select` - Select with react-hook-form integration +- `controlled-checkbox` - Checkbox with react-hook-form integration +- `controlled-textarea` - Textarea with react-hook-form integration +- `controlled-datepicker` - DatePicker with react-hook-form integration +- `controlled-currency-input` - CurrencyInput with react-hook-form integration + +## Usage Examples + +### Installing a single component + +```bash +npx shadcn@latest add --registry https://raw.githubusercontent.com/lambda-curry/medusa-forms/main/packages/medusa-forms/registry.json controlled-input +``` + +### Installing multiple components + +```bash +npx shadcn@latest add --registry https://raw.githubusercontent.com/lambda-curry/medusa-forms/main/packages/medusa-forms/registry.json controlled-input controlled-select controlled-checkbox +``` + +### Using the components + +```tsx +import { ControlledInput } from '@/components/ui/controlled-input' +import { ControlledSelect } from '@/components/ui/controlled-select' +import { useForm, FormProvider } from 'react-hook-form' + +function MyForm() { + const methods = useForm() + + return ( + +
+ + + + +
+ ) +} +``` + +## Component Dependencies + +The registry properly handles component dependencies: + +- Controlled components depend on their base UI components +- UI components depend on `field-wrapper` when needed +- `field-wrapper` depends on `field-error` and `label` +- All components use `@medusajs/ui` for base styling + +## Architecture + +### Field Wrapper Pattern + +All form components use a consistent wrapper pattern: + +- `FieldWrapper` provides consistent layout and error handling +- `Label` component handles labels and tooltips +- `FieldError` handles error message display +- UI components wrap `@medusajs/ui` components + +### Controlled Component Pattern + +Controlled components use react-hook-form: + +- Uses `Controller` from react-hook-form +- Integrates with form context +- Handles form validation and errors +- Preserves component props and types + +## Types and Interfaces + +- `BasicFieldProps` - Common field properties +- `FieldWrapperProps` - Wrapper component props +- Component-specific props (`InputProps`, `SelectProps`, etc.) +- React Hook Form integration types + diff --git a/packages/medusa-forms/registry.json b/packages/medusa-forms/registry.json new file mode 100644 index 0000000..a1dd062 --- /dev/null +++ b/packages/medusa-forms/registry.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "name": "medusa-forms", + "description": "Controlled form fields for Medusa Admin and Medusa UI", + "url": "https://raw.githubusercontent.com/lambda-curry/medusa-forms/main/packages/medusa-forms", + "style": "default", + "tailwind": { + "config": "tailwind.config.js", + "css": "src/styles/globals.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "src/components", + "utils": "src/lib/utils" + } +} + diff --git a/packages/medusa-forms/registry/controlled-checkbox.json b/packages/medusa-forms/registry/controlled-checkbox.json new file mode 100644 index 0000000..e90bb6b --- /dev/null +++ b/packages/medusa-forms/registry/controlled-checkbox.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-checkbox", + "type": "registry:ui", + "description": "A checkbox component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "field-checkbox" + ], + "files": [ + { + "name": "controlled-checkbox.tsx", + "content": "import {\n Controller,\n type ControllerProps,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { FieldCheckbox, type FieldCheckboxProps } from '../ui/FieldCheckbox';\n\nexport type ControlledCheckboxProps = Omit &\n Omit & {\n name: Path;\n rules?: Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>;\n };\n\nexport const ControlledCheckbox = ({\n name,\n rules,\n onChange,\n ...props\n}: ControlledCheckboxProps) => {\n const {\n control,\n formState: { errors },\n } = useFormContext();\n\n return (\n >, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}\n render={({ field }) => (\n {\n if (onChange) onChange(checked);\n field.onChange(checked);\n }}\n />\n )}\n />\n );\n};\n\n" + } + ] +} + diff --git a/packages/medusa-forms/registry/controlled-currency-input.json b/packages/medusa-forms/registry/controlled-currency-input.json new file mode 100644 index 0000000..a3dda62 --- /dev/null +++ b/packages/medusa-forms/registry/controlled-currency-input.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-currency-input", + "type": "registry:ui", + "description": "A currency-input component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "currency-input" + ], + "files": [ + { + "name": "controlled-currency-input.tsx", + "content": "import type * as React from 'react';\nimport {\n Controller,\n type ControllerProps,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { CurrencyInput, type CurrencyInputProps } from '../ui/CurrencyInput';\n\nexport type ControlledCurrencyInputProps = CurrencyInputProps &\n Omit & {\n name: Path;\n };\n\nexport const ControlledCurrencyInput = ({\n name,\n rules,\n ...props\n}: ControlledCurrencyInputProps) => {\n const { control } = useFormContext();\n\n return (\n \n control={control}\n name={name}\n rules={rules as Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}\n render={({ field }) => {\n return (\n ) => {\n field.onChange(e.target.value.replace(/[^0-9.-]+/g, ''));\n }}\n />\n );\n }}\n />\n );\n};\n\n" + } + ] +} + diff --git a/packages/medusa-forms/registry/controlled-datepicker.json b/packages/medusa-forms/registry/controlled-datepicker.json new file mode 100644 index 0000000..2ef3679 --- /dev/null +++ b/packages/medusa-forms/registry/controlled-datepicker.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-datepicker", + "type": "registry:ui", + "description": "A datepicker component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "datepicker" + ], + "files": [ + { + "name": "controlled-datepicker.tsx", + "content": "import {\n Controller,\n type ControllerProps,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { DatePickerInput, type DatePickerProps } from '../ui/DatePicker';\n\nexport type ControlledDatePickerProps = DatePickerProps &\n Omit & {\n name: Path;\n };\n\nexport const ControlledDatePicker = ({\n name,\n rules,\n ...props\n}: ControlledDatePickerProps) => {\n const { control } = useFormContext();\n return (\n \n control={control}\n name={name}\n rules={rules as Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}\n render={({ field }) => }\n />\n );\n};\n\n" + } + ] +} + diff --git a/packages/medusa-forms/registry/controlled-input.json b/packages/medusa-forms/registry/controlled-input.json new file mode 100644 index 0000000..37ad81d --- /dev/null +++ b/packages/medusa-forms/registry/controlled-input.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-input", + "type": "registry:ui", + "description": "An input component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "input" + ], + "files": [ + { + "name": "controlled-input.tsx", + "content": "import type { ComponentProps } from 'react';\nimport {\n Controller,\n type ControllerProps,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { Input, type InputProps } from '../ui/Input';\n\nexport type ControlledInputProps = InputProps &\n Omit & {\n name: Path;\n rules?: Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>;\n } & ComponentProps &\n Omit, 'render'>;\n\nexport const ControlledInput = ({\n name,\n rules,\n onChange,\n ...props\n}: ControlledInputProps) => {\n const {\n control,\n formState: { errors },\n } = useFormContext();\n\n return (\n >, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}\n render={({ field }) => (\n {\n if (onChange) {\n onChange(evt);\n }\n field.onChange(evt);\n }}\n />\n )}\n />\n );\n};\n\n" + } + ] +} + diff --git a/packages/medusa-forms/registry/controlled-select.json b/packages/medusa-forms/registry/controlled-select.json new file mode 100644 index 0000000..1ef6e7c --- /dev/null +++ b/packages/medusa-forms/registry/controlled-select.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-select", + "type": "registry:ui", + "description": "A select component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "select" + ], + "files": [ + { + "name": "controlled-select.tsx", + "content": "import type * as React from 'react';\nimport {\n Controller,\n type ControllerProps,\n type FieldPathValue,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { Select, type SelectProps } from '../ui/Select';\n\nexport type ControlledSelectProps = SelectProps &\n Omit & {\n name: Path;\n onBlur?: () => void;\n onChange?: (value: unknown) => void;\n } & (\n | {\n options: { label: React.ReactNode; value: FieldPathValue> }[];\n children?: never;\n }\n | {\n options?: never;\n children: React.ReactNode;\n }\n );\n\nexport const ControlledSelect = ({\n name,\n rules,\n children,\n options,\n onChange,\n onBlur,\n ...props\n}: ControlledSelectProps) => {\n const { control } = useFormContext();\n return (\n \n control={control}\n name={name}\n rules={rules as Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}\n render={({ field }) => {\n const handleChange = (value: unknown) => {\n if (typeof onChange === 'function') onChange(value);\n field.onChange(value);\n };\n\n if (options) {\n return (\n \n );\n }\n\n return ;\n }}\n />\n );\n};\n\n" + } + ] +} + diff --git a/packages/medusa-forms/registry/controlled-textarea.json b/packages/medusa-forms/registry/controlled-textarea.json new file mode 100644 index 0000000..7c0e763 --- /dev/null +++ b/packages/medusa-forms/registry/controlled-textarea.json @@ -0,0 +1,18 @@ +{ + "name": "controlled-textarea", + "type": "registry:ui", + "description": "A textarea component with react-hook-form integration", + "dependencies": [ + "react-hook-form" + ], + "registryDependencies": [ + "textarea" + ], + "files": [ + { + "name": "controlled-textarea.tsx", + "content": "import type * as React from 'react';\nimport {\n Controller,\n type ControllerProps,\n type FieldValues,\n type Path,\n type RegisterOptions,\n useFormContext,\n} from 'react-hook-form';\nimport { TextArea, type TextAreaProps } from '../ui/TextArea';\n\nexport type ControlledTextAreaProps = TextAreaProps &\n Omit & {\n name: Path;\n rules?: RegisterOptions>;\n } & React.ComponentProps &\n Omit, 'render'>;\n\nexport const ControlledTextArea = ({ name, rules, ...props }: ControlledTextAreaProps) => {\n const { control } = useFormContext();\n return (\n \n control={control}\n name={name}\n rules={rules}\n render={({ field }) =>