@skybin-tech/nebula-ui 0.0.11 → 0.0.14
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/cjs/components/Button/Button.cjs +32 -38
- package/dist/cjs/components/Button/Button.cjs.map +1 -1
- package/dist/cjs/components/Form/TextBox.cjs +10 -2
- package/dist/cjs/components/Form/TextBox.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/primitives/button.cjs +66 -0
- package/dist/cjs/primitives/button.cjs.map +1 -0
- package/dist/cjs/primitives/{input.cjs → textbox.cjs} +4 -4
- package/dist/cjs/primitives/textbox.cjs.map +1 -0
- package/dist/components/Button/Button.js +33 -39
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Form/TextBox.js +10 -2
- package/dist/components/Form/TextBox.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/primitives/button.js +49 -0
- package/dist/primitives/button.js.map +1 -0
- package/dist/primitives/{input.js → textbox.js} +4 -4
- package/dist/primitives/textbox.js.map +1 -0
- package/package.json +1 -1
- package/dist/cjs/primitives/input.cjs.map +0 -1
- package/dist/primitives/input.js.map +0 -1
|
@@ -1,37 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
-
const
|
|
4
|
+
const React = require("react");
|
|
5
|
+
require("class-variance-authority");
|
|
5
6
|
const cn = require("../../utils/cn.cjs");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
lg: "h-12 px-6 text-base"
|
|
21
|
-
},
|
|
22
|
-
fullWidth: {
|
|
23
|
-
true: "w-full flex",
|
|
24
|
-
false: ""
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
defaultVariants: {
|
|
28
|
-
variant: "primary",
|
|
29
|
-
size: "md",
|
|
30
|
-
fullWidth: false
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
function Button({
|
|
7
|
+
const button = require("../../primitives/button.cjs");
|
|
8
|
+
const variantMap = {
|
|
9
|
+
primary: "default",
|
|
10
|
+
secondary: "secondary",
|
|
11
|
+
outline: "outline",
|
|
12
|
+
ghost: "ghost",
|
|
13
|
+
danger: "destructive"
|
|
14
|
+
};
|
|
15
|
+
const sizeMap = {
|
|
16
|
+
sm: "sm",
|
|
17
|
+
md: "default",
|
|
18
|
+
lg: "lg"
|
|
19
|
+
};
|
|
20
|
+
const Button = React.forwardRef(function Button2({
|
|
35
21
|
children,
|
|
36
22
|
variant = "primary",
|
|
37
23
|
size = "md",
|
|
@@ -39,20 +25,28 @@ function Button({
|
|
|
39
25
|
loading = false,
|
|
40
26
|
disabled,
|
|
41
27
|
className,
|
|
28
|
+
asChild = false,
|
|
29
|
+
style,
|
|
42
30
|
...props
|
|
43
|
-
}) {
|
|
44
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
45
|
-
|
|
31
|
+
}, ref) {
|
|
32
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
33
|
+
button.ButtonPrimitive,
|
|
46
34
|
{
|
|
47
|
-
|
|
35
|
+
ref,
|
|
36
|
+
variant: variantMap[variant],
|
|
37
|
+
size: sizeMap[size],
|
|
38
|
+
asChild,
|
|
48
39
|
disabled: disabled || loading,
|
|
40
|
+
className: cn.cn(fullWidth && "w-full", "relative cursor-pointer", className),
|
|
41
|
+
style,
|
|
49
42
|
...props,
|
|
50
|
-
children: [
|
|
51
|
-
loading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-4 border-2 border-current border-r-transparent rounded-full animate-spin" }),
|
|
43
|
+
children: asChild ? children : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
44
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-4 border-2 border-current border-r-transparent rounded-full animate-spin" }) }),
|
|
52
45
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn.cn(loading && "invisible"), children })
|
|
53
|
-
]
|
|
46
|
+
] })
|
|
54
47
|
}
|
|
55
48
|
);
|
|
56
|
-
}
|
|
49
|
+
});
|
|
50
|
+
Button.displayName = "Button";
|
|
57
51
|
exports.Button = Button;
|
|
58
52
|
//# sourceMappingURL=Button.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import type
|
|
1
|
+
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { ButtonPrimitive, buttonVariants } from \"../../primitives/button\";\n\ntype PrimitiveVariant = NonNullable<VariantProps<typeof buttonVariants>[\"variant\"]>;\ntype PrimitiveSize = NonNullable<VariantProps<typeof buttonVariants>[\"size\"]>;\n\nconst variantMap: Record<string, PrimitiveVariant> = {\n primary: \"default\",\n secondary: \"secondary\",\n outline: \"outline\",\n ghost: \"ghost\",\n danger: \"destructive\",\n};\n\nconst sizeMap: Record<string, PrimitiveSize> = {\n sm: \"sm\",\n md: \"default\",\n lg: \"lg\",\n};\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant */\n variant?: keyof typeof variantMap;\n /** Size of the button */\n size?: keyof typeof sizeMap;\n /** Stretch button to full container width */\n fullWidth?: boolean;\n /** Show a loading spinner and disable interaction */\n loading?: boolean;\n /** Render as child element via Slot (e.g. wrap a Link) */\n asChild?: boolean;\n children: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n disabled,\n className,\n asChild = false,\n style,\n ...props\n },\n ref\n) {\n return (\n <ButtonPrimitive\n ref={ref}\n variant={variantMap[variant]}\n size={sizeMap[size]}\n asChild={asChild}\n disabled={disabled || loading}\n className={cn(fullWidth && \"w-full\", \"relative cursor-pointer\", className)}\n style={style}\n {...props}\n >\n {asChild ? (\n children\n ) : (\n <>\n {loading && (\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"size-4 border-2 border-current border-r-transparent rounded-full animate-spin\" />\n </span>\n )}\n <span className={cn(loading && \"invisible\")}>{children}</span>\n </>\n )}\n </ButtonPrimitive>\n );\n});\n\nButton.displayName = \"Button\";\n"],"names":["forwardRef","Button","jsx","ButtonPrimitive","cn","jsxs","Fragment"],"mappings":";;;;;;;AAQA,MAAM,aAA+C;AAAA,EACnD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,MAAM,UAAyC;AAAA,EAC7C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAgBO,MAAM,SAASA,MAAAA,WAA2C,SAASC,QACxE;AAAA,EACE;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,GAAG;AACL,GACA,KACA;AACA,SACEC,2BAAAA;AAAAA,IAACC,OAAAA;AAAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAS,WAAW,OAAO;AAAA,MAC3B,MAAM,QAAQ,IAAI;AAAA,MAClB;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,WAAWC,GAAAA,GAAG,aAAa,UAAU,2BAA2B,SAAS;AAAA,MACzE;AAAA,MACC,GAAG;AAAA,MAEH,UAAA,UACC,WAEAC,2BAAAA,KAAAC,WAAAA,UAAA,EACG,UAAA;AAAA,QAAA,WACCJ,2BAAAA,IAAC,UAAK,WAAU,qDACd,yCAAC,QAAA,EAAK,WAAU,iFAAgF,EAAA,CAClG;AAAA,uCAED,QAAA,EAAK,WAAWE,GAAAA,GAAG,WAAW,WAAW,GAAI,SAAA,CAAS;AAAA,MAAA,EAAA,CACzD;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAED,OAAO,cAAc;;"}
|
|
@@ -7,7 +7,7 @@ const classVarianceAuthority = require("class-variance-authority");
|
|
|
7
7
|
const cn = require("../../utils/cn.cjs");
|
|
8
8
|
const context = require("./context.cjs");
|
|
9
9
|
const variants = require("./variants.cjs");
|
|
10
|
-
const
|
|
10
|
+
const textbox = require("../../primitives/textbox.cjs");
|
|
11
11
|
const label = require("../../primitives/label.cjs");
|
|
12
12
|
const lucideReact = require("lucide-react");
|
|
13
13
|
const textBoxVariants = classVarianceAuthority.cva(
|
|
@@ -84,6 +84,14 @@ function TextBoxInner({
|
|
|
84
84
|
let fieldType = "string";
|
|
85
85
|
if (inputType === "number") {
|
|
86
86
|
fieldType = "number";
|
|
87
|
+
} else if (inputType === "date") {
|
|
88
|
+
fieldType = "date";
|
|
89
|
+
} else if (inputType === "email" && rules.email === void 0) {
|
|
90
|
+
rules.email = true;
|
|
91
|
+
} else if (inputType === "url" && rules.url === void 0) {
|
|
92
|
+
rules.url = true;
|
|
93
|
+
} else if (inputType === "tel" && rules.pattern === void 0) {
|
|
94
|
+
rules.pattern = { value: /^\+?[\d\s\-().]{7,}$/, message: "Invalid phone number" };
|
|
87
95
|
}
|
|
88
96
|
registerFieldValidation({
|
|
89
97
|
name,
|
|
@@ -138,7 +146,7 @@ function TextBoxInner({
|
|
|
138
146
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
139
147
|
prefix && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: prefix }),
|
|
140
148
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
141
|
-
|
|
149
|
+
textbox.TextBoxPrimitive,
|
|
142
150
|
{
|
|
143
151
|
...props,
|
|
144
152
|
...field,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextBox.cjs","sources":["../../../../src/components/Form/TextBox.tsx"],"sourcesContent":["import { forwardRef, useId, useContext, useEffect } from \"react\";\nimport type { InputHTMLAttributes, ReactNode } from \"react\";\nimport { useController, useFormContext as useRHFFormContext, type FieldValues, type FieldPath, type Control } from \"react-hook-form\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { FormConfigContext, type FormConfig, type FieldValidationRules } from \"../Form/context\";\nimport { labelVariants } from \"./variants\";\nimport { Input } from \"../../primitives/input\";\nimport { Label } from \"../../primitives/label\";\nimport { X } from \"lucide-react\";\n\nconst textBoxVariants = cva(\n \"\",\n {\n variants: {\n size: {\n sm: \"h-8 text-xs px-2\",\n md: \"h-10 text-sm px-3\",\n lg: \"h-12 text-base px-4\",\n },\n variant: {\n default: \"border-input focus-visible:ring-ring\",\n error: \"border-destructive focus-visible:ring-destructive\",\n success: \"border-green-500 focus-visible:ring-green-500\",\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"default\",\n },\n }\n);\n\n/**\n * Validation rule with optional custom message\n */\ntype ValidationRule<T> = T | { value: T; message: string };\n\nexport interface TextBoxProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> extends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"name\" | \"prefix\" | \"pattern\" | \"required\" | \"minLength\" | \"maxLength\">,\n VariantProps<typeof textBoxVariants> {\n /** Field name - required for form integration */\n name: TName;\n /** Label text for the input */\n label?: string;\n /** Helper text displayed below the input */\n helperText?: string;\n /** Whether to show the error message */\n showError?: boolean;\n /** Custom error message (overrides form error) */\n error?: string;\n /** Whether the input should take full width */\n fullWidth?: boolean;\n /** Prefix element */\n prefix?: ReactNode;\n /** Suffix element */\n suffix?: ReactNode;\n /** Allow clear button */\n allowClear?: boolean;\n /** Callback when clear is clicked */\n onClear?: () => void;\n /** External control (for use outside Form) */\n control?: Control<TFieldValues>;\n \n // Validation props\n /** Field is required */\n required?: boolean | string;\n /** Minimum length for strings */\n minLength?: ValidationRule<number>;\n /** Maximum length for strings */\n maxLength?: ValidationRule<number>;\n /** Minimum value for numbers */\n minValue?: ValidationRule<number>;\n /** Maximum value for numbers */\n maxValue?: ValidationRule<number>;\n /** Regex pattern for validation */\n pattern?: ValidationRule<RegExp>;\n /** Email validation */\n email?: boolean | string;\n /** URL validation */\n url?: boolean | string;\n /** Custom validation function */\n validate?: (value: unknown) => boolean | string | Promise<boolean | string>;\n}\n\n/**\n * TextBox component with form integration and automatic validation registration\n * \n * This is a wrapper around the shadcn/ui Input primitive that adds:\n * - Form integration with react-hook-form\n * - Automatic validation registration\n * - Label, helper text, and error message support\n * - Prefix/suffix elements\n * - Clear button functionality\n * \n * @example\n * ```tsx\n * // Inside a Form component - validation is automatically registered\n * <Form onSubmit={handleSubmit} defaultValues={{ username: \"\", email: \"\" }}>\n * <TextBox name=\"username\" label=\"Username\" required minLength={3} maxLength={50} />\n * <TextBox name=\"email\" label=\"Email\" type=\"email\" required email />\n * <TextBox name=\"website\" label=\"Website\" url />\n * <Button type=\"submit\">Submit</Button>\n * </Form>\n * ```\n */\nfunction TextBoxInner<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n {\n name,\n label,\n helperText,\n showError = true,\n error: customError,\n size,\n variant,\n fullWidth = true,\n className,\n disabled,\n prefix,\n suffix,\n allowClear,\n onClear,\n id: providedId,\n control: externalControl,\n // Validation props\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n ...props\n }: TextBoxProps<TFieldValues, TName>,\n ref: React.ForwardedRef<HTMLInputElement>\n) {\n const generatedId = useId();\n const inputId = providedId ?? generatedId;\n \n // Try to get form context\n const formConfigContext = useContext(FormConfigContext);\n const formConfig: FormConfig = formConfigContext ?? {};\n\n // Get form context from react-hook-form\n const rhfContext = useRHFFormContext<TFieldValues>();\n const control = externalControl ?? rhfContext?.control;\n\n // Destructure stable function references so the effect doesn't depend on the\n // context object identity (which changes every render due to inline construction).\n const registerFieldValidation = formConfigContext?.registerFieldValidation;\n const unregisterFieldValidation = formConfigContext?.unregisterFieldValidation;\n\n // Register validation rules with the form\n useEffect(() => {\n if (registerFieldValidation) {\n const rules: FieldValidationRules = {};\n\n if (required !== undefined) rules.required = required;\n if (minLength !== undefined) rules.minLength = minLength;\n if (maxLength !== undefined) rules.maxLength = maxLength;\n if (minValue !== undefined) rules.min = minValue;\n if (maxValue !== undefined) rules.max = maxValue;\n if (pattern !== undefined) rules.pattern = pattern;\n if (email !== undefined) rules.email = email;\n if (url !== undefined) rules.url = url;\n if (validate !== undefined) rules.validate = validate;\n\n // Determine field type based on input type\n const inputType = props.type ?? \"text\";\n let fieldType: \"string\" | \"number\" = \"string\";\n if (inputType === \"number\") {\n fieldType = \"number\";\n }\n\n registerFieldValidation({\n name: name as string,\n type: fieldType,\n rules,\n });\n\n return () => {\n unregisterFieldValidation?.(name as string);\n };\n }\n }, [\n registerFieldValidation,\n unregisterFieldValidation,\n name,\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n props.type,\n ]);\n\n // Use controller for form integration\n const { field, fieldState } = useController<TFieldValues, TName>({\n name,\n control,\n });\n\n const fieldError = fieldState.error?.message;\n const errorMessage = customError ?? fieldError;\n const hasError = !!errorMessage;\n \n // Merge sizes - prop takes precedence over form config\n const effectiveSize = size ?? formConfig.size ?? \"md\";\n const effectiveDisabled = disabled ?? formConfig.disabled;\n \n // Determine variant based on error state\n const effectiveVariant = hasError ? \"error\" : variant;\n\n const handleClear = () => {\n field.onChange(\"\");\n onClear?.();\n };\n\n return (\n <div className={cn(\"space-y-1.5\", fullWidth && \"w-full\")}>\n {label && (\n <Label\n htmlFor={inputId}\n className={labelVariants({ required: !!required })}\n >\n {label}\n {formConfig.colon && \":\"}\n </Label>\n )}\n \n <div className=\"relative\">\n {prefix && (\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {prefix}\n </div>\n )}\n \n <Input\n {...props}\n {...field}\n ref={(node) => {\n // Handle both refs\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n field.ref(node);\n }}\n id={inputId}\n disabled={effectiveDisabled}\n aria-invalid={hasError}\n aria-describedby={\n hasError\n ? `${inputId}-error`\n : helperText\n ? `${inputId}-helper`\n : undefined\n }\n className={cn(\n textBoxVariants({ size: effectiveSize, variant: effectiveVariant }),\n prefix && \"pl-10\",\n (suffix || allowClear) && \"pr-10\",\n className\n )}\n />\n \n {(suffix || (allowClear && field.value)) && (\n <div className=\"absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1\">\n {allowClear && field.value && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n tabIndex={-1}\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n {suffix && (\n <span className=\"text-muted-foreground\">{suffix}</span>\n )}\n </div>\n )}\n </div>\n\n {showError && hasError && (\n <p\n id={`${inputId}-error`}\n className=\"text-sm text-destructive\"\n role=\"alert\"\n >\n {errorMessage}\n </p>\n )}\n \n {helperText && !hasError && (\n <p\n id={`${inputId}-helper`}\n className=\"text-sm text-muted-foreground\"\n >\n {helperText}\n </p>\n )}\n </div>\n );\n}\n\n// Use forwardRef with generic support\nexport const TextBox = forwardRef(TextBoxInner) as <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n props: TextBoxProps<TFieldValues, TName> & { ref?: React.ForwardedRef<HTMLInputElement> }\n) => React.ReactElement;\n\n(TextBox as React.FC).displayName = \"TextBox\";\n"],"names":["cva","label","useId","useContext","FormConfigContext","useRHFFormContext","useEffect","useController","cn","jsxs","Label","labelVariants","jsx","Input","X","forwardRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TextBox.cjs","sources":["../../../../src/components/Form/TextBox.tsx"],"sourcesContent":["import { forwardRef, useId, useContext, useEffect } from \"react\";\nimport type { InputHTMLAttributes, ReactNode } from \"react\";\nimport { useController, useFormContext as useRHFFormContext, type FieldValues, type FieldPath, type Control } from \"react-hook-form\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { FormConfigContext, type FormConfig, type FieldValidationRules } from \"../Form/context\";\nimport { labelVariants } from \"./variants\";\nimport { TextBoxPrimitive } from \"../../primitives/textbox\";\nimport { Label } from \"../../primitives/label\";\nimport { X } from \"lucide-react\";\n\nconst textBoxVariants = cva(\n \"\",\n {\n variants: {\n size: {\n sm: \"h-8 text-xs px-2\",\n md: \"h-10 text-sm px-3\",\n lg: \"h-12 text-base px-4\",\n },\n variant: {\n default: \"border-input focus-visible:ring-ring\",\n error: \"border-destructive focus-visible:ring-destructive\",\n success: \"border-green-500 focus-visible:ring-green-500\",\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"default\",\n },\n }\n);\n\n/**\n * Validation rule with optional custom message\n */\ntype ValidationRule<T> = T | { value: T; message: string };\n\nexport interface TextBoxProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> extends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"name\" | \"prefix\" | \"pattern\" | \"required\" | \"minLength\" | \"maxLength\">,\n VariantProps<typeof textBoxVariants> {\n /** Field name - required for form integration */\n name: TName;\n /** Label text for the input */\n label?: string;\n /** Helper text displayed below the input */\n helperText?: string;\n /** Whether to show the error message */\n showError?: boolean;\n /** Custom error message (overrides form error) */\n error?: string;\n /** Whether the input should take full width */\n fullWidth?: boolean;\n /** Prefix element */\n prefix?: ReactNode;\n /** Suffix element */\n suffix?: ReactNode;\n /** Allow clear button */\n allowClear?: boolean;\n /** Callback when clear is clicked */\n onClear?: () => void;\n /** External control (for use outside Form) */\n control?: Control<TFieldValues>;\n \n // Validation props\n /** Field is required */\n required?: boolean | string;\n /** Minimum length for strings */\n minLength?: ValidationRule<number>;\n /** Maximum length for strings */\n maxLength?: ValidationRule<number>;\n /** Minimum value for numbers */\n minValue?: ValidationRule<number>;\n /** Maximum value for numbers */\n maxValue?: ValidationRule<number>;\n /** Regex pattern for validation */\n pattern?: ValidationRule<RegExp>;\n /** Email validation */\n email?: boolean | string;\n /** URL validation */\n url?: boolean | string;\n /** Custom validation function */\n validate?: (value: unknown) => boolean | string | Promise<boolean | string>;\n}\n\n/**\n * TextBox component with form integration and automatic validation registration\n * \n * This is a wrapper around the shadcn/ui Input primitive that adds:\n * - Form integration with react-hook-form\n * - Automatic validation registration\n * - Label, helper text, and error message support\n * - Prefix/suffix elements\n * - Clear button functionality\n * \n * @example\n * ```tsx\n * // Inside a Form component - validation is automatically registered\n * <Form onSubmit={handleSubmit} defaultValues={{ username: \"\", email: \"\" }}>\n * <TextBox name=\"username\" label=\"Username\" required minLength={3} maxLength={50} />\n * <TextBox name=\"email\" label=\"Email\" type=\"email\" required email />\n * <TextBox name=\"website\" label=\"Website\" url />\n * <Button type=\"submit\">Submit</Button>\n * </Form>\n * ```\n */\nfunction TextBoxInner<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n {\n name,\n label,\n helperText,\n showError = true,\n error: customError,\n size,\n variant,\n fullWidth = true,\n className,\n disabled,\n prefix,\n suffix,\n allowClear,\n onClear,\n id: providedId,\n control: externalControl,\n // Validation props\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n ...props\n }: TextBoxProps<TFieldValues, TName>,\n ref: React.ForwardedRef<HTMLInputElement>\n) {\n const generatedId = useId();\n const inputId = providedId ?? generatedId;\n \n // Try to get form context\n const formConfigContext = useContext(FormConfigContext);\n const formConfig: FormConfig = formConfigContext ?? {};\n\n // Get form context from react-hook-form\n const rhfContext = useRHFFormContext<TFieldValues>();\n const control = externalControl ?? rhfContext?.control;\n\n // Destructure stable function references so the effect doesn't depend on the\n // context object identity (which changes every render due to inline construction).\n const registerFieldValidation = formConfigContext?.registerFieldValidation;\n const unregisterFieldValidation = formConfigContext?.unregisterFieldValidation;\n\n // Register validation rules with the form\n useEffect(() => {\n if (registerFieldValidation) {\n const rules: FieldValidationRules = {};\n\n if (required !== undefined) rules.required = required;\n if (minLength !== undefined) rules.minLength = minLength;\n if (maxLength !== undefined) rules.maxLength = maxLength;\n if (minValue !== undefined) rules.min = minValue;\n if (maxValue !== undefined) rules.max = maxValue;\n if (pattern !== undefined) rules.pattern = pattern;\n if (email !== undefined) rules.email = email;\n if (url !== undefined) rules.url = url;\n if (validate !== undefined) rules.validate = validate;\n\n // Determine field type and auto-infer rules from input type\n const inputType = props.type ?? \"text\";\n let fieldType: \"string\" | \"number\" | \"date\" = \"string\";\n\n if (inputType === \"number\") {\n fieldType = \"number\";\n } else if (inputType === \"date\") {\n fieldType = \"date\";\n } else if (inputType === \"email\" && rules.email === undefined) {\n rules.email = true;\n } else if (inputType === \"url\" && rules.url === undefined) {\n rules.url = true;\n } else if (inputType === \"tel\" && rules.pattern === undefined) {\n rules.pattern = { value: /^\\+?[\\d\\s\\-().]{7,}$/, message: \"Invalid phone number\" };\n }\n\n registerFieldValidation({\n name: name as string,\n type: fieldType,\n rules,\n });\n\n return () => {\n unregisterFieldValidation?.(name as string);\n };\n }\n }, [\n registerFieldValidation,\n unregisterFieldValidation,\n name,\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n props.type,\n ]);\n\n // Use controller for form integration\n const { field, fieldState } = useController<TFieldValues, TName>({\n name,\n control,\n });\n\n const fieldError = fieldState.error?.message;\n const errorMessage = customError ?? fieldError;\n const hasError = !!errorMessage;\n \n // Merge sizes - prop takes precedence over form config\n const effectiveSize = size ?? formConfig.size ?? \"md\";\n const effectiveDisabled = disabled ?? formConfig.disabled;\n \n // Determine variant based on error state\n const effectiveVariant = hasError ? \"error\" : variant;\n\n const handleClear = () => {\n field.onChange(\"\");\n onClear?.();\n };\n\n return (\n <div className={cn(\"space-y-1.5\", fullWidth && \"w-full\")}>\n {label && (\n <Label\n htmlFor={inputId}\n className={labelVariants({ required: !!required })}\n >\n {label}\n {formConfig.colon && \":\"}\n </Label>\n )}\n \n <div className=\"relative\">\n {prefix && (\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {prefix}\n </div>\n )}\n \n <TextBoxPrimitive\n {...props}\n {...field}\n ref={(node) => {\n // Handle both refs\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n field.ref(node);\n }}\n id={inputId}\n disabled={effectiveDisabled}\n aria-invalid={hasError}\n aria-describedby={\n hasError\n ? `${inputId}-error`\n : helperText\n ? `${inputId}-helper`\n : undefined\n }\n className={cn(\n textBoxVariants({ size: effectiveSize, variant: effectiveVariant }),\n prefix && \"pl-10\",\n (suffix || allowClear) && \"pr-10\",\n className\n )}\n />\n\n {(suffix || (allowClear && field.value)) && (\n <div className=\"absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1\">\n {allowClear && field.value && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n tabIndex={-1}\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n {suffix && (\n <span className=\"text-muted-foreground\">{suffix}</span>\n )}\n </div>\n )}\n </div>\n\n {showError && hasError && (\n <p\n id={`${inputId}-error`}\n className=\"text-sm text-destructive\"\n role=\"alert\"\n >\n {errorMessage}\n </p>\n )}\n \n {helperText && !hasError && (\n <p\n id={`${inputId}-helper`}\n className=\"text-sm text-muted-foreground\"\n >\n {helperText}\n </p>\n )}\n </div>\n );\n}\n\n// Use forwardRef with generic support\nexport const TextBox = forwardRef(TextBoxInner) as <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n props: TextBoxProps<TFieldValues, TName> & { ref?: React.ForwardedRef<HTMLInputElement> }\n) => React.ReactElement;\n\n(TextBox as React.FC).displayName = \"TextBox\";\n"],"names":["cva","label","useId","useContext","FormConfigContext","useRHFFormContext","useEffect","useController","cn","jsxs","Label","labelVariants","jsx","TextBoxPrimitive","X","forwardRef"],"mappings":"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"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -17,6 +17,7 @@ const separator = require("./primitives/separator.cjs");
|
|
|
17
17
|
const Switch = require("./components/Form/Switch.cjs");
|
|
18
18
|
const TextArea = require("./components/Form/TextArea.cjs");
|
|
19
19
|
const TextBox = require("./components/Form/TextBox.cjs");
|
|
20
|
+
const button = require("./primitives/button.cjs");
|
|
20
21
|
const hooks = require("./components/Form/hooks.cjs");
|
|
21
22
|
exports.useToggle = useToggle.useToggle;
|
|
22
23
|
exports.useDebounce = useDebounce.useDebounce;
|
|
@@ -61,6 +62,7 @@ exports.Separator = separator.Separator;
|
|
|
61
62
|
exports.Switch = Switch.Switch;
|
|
62
63
|
exports.TextArea = TextArea.TextArea;
|
|
63
64
|
exports.TextBox = TextBox.TextBox;
|
|
65
|
+
exports.buttonVariants = button.buttonVariants;
|
|
64
66
|
exports.useFieldError = hooks.useFieldError;
|
|
65
67
|
exports.useForm = hooks.useForm;
|
|
66
68
|
exports.useFormConfig = hooks.useFormConfig;
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const reactSlot = require("@radix-ui/react-slot");
|
|
6
|
+
const classVarianceAuthority = require("class-variance-authority");
|
|
7
|
+
const cn = require("../utils/cn.cjs");
|
|
8
|
+
function _interopNamespaceDefault(e) {
|
|
9
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
10
|
+
if (e) {
|
|
11
|
+
for (const k in e) {
|
|
12
|
+
if (k !== "default") {
|
|
13
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: () => e[k]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
25
|
+
const buttonVariants = classVarianceAuthority.cva(
|
|
26
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
variant: {
|
|
30
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
31
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
32
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
33
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
34
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
35
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
36
|
+
},
|
|
37
|
+
size: {
|
|
38
|
+
default: "h-10 px-4 py-2",
|
|
39
|
+
sm: "h-9 px-3",
|
|
40
|
+
lg: "h-11 px-8",
|
|
41
|
+
icon: "h-10 w-10"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
variant: "default",
|
|
46
|
+
size: "default"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
const ButtonPrimitive = React__namespace.forwardRef(
|
|
51
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
52
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
54
|
+
Comp,
|
|
55
|
+
{
|
|
56
|
+
ref,
|
|
57
|
+
className: cn.cn(buttonVariants({ variant, size, className })),
|
|
58
|
+
...props
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
ButtonPrimitive.displayName = "ButtonPrimitive";
|
|
64
|
+
exports.ButtonPrimitive = ButtonPrimitive;
|
|
65
|
+
exports.buttonVariants = buttonVariants;
|
|
66
|
+
//# sourceMappingURL=button.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.cjs","sources":["../../../src/primitives/button.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 px-3\",\n lg: \"h-11 px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonPrimitiveProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst ButtonPrimitive = React.forwardRef<HTMLButtonElement, ButtonPrimitiveProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n <Comp\n ref={ref}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n }\n)\nButtonPrimitive.displayName = \"ButtonPrimitive\"\n\nexport { ButtonPrimitive, buttonVariants }\n"],"names":["cva","React","Slot","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAM,iBAAiBA,uBAAAA;AAAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,QACT,WAAW;AAAA,QACX,OAAO;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,MAER,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAQA,MAAM,kBAAkBC,iBAAM;AAAA,EAC5B,CAAC,EAAE,WAAW,SAAS,MAAM,UAAU,OAAO,GAAG,MAAA,GAAS,QAAQ;AAChE,UAAM,OAAO,UAAUC,UAAAA,OAAO;AAC9B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA,GAAG,eAAe,EAAE,SAAS,MAAM,UAAA,CAAW,CAAC;AAAA,QACzD,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,gBAAgB,cAAc;;;"}
|
|
@@ -20,7 +20,7 @@ function _interopNamespaceDefault(e) {
|
|
|
20
20
|
return Object.freeze(n);
|
|
21
21
|
}
|
|
22
22
|
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
23
|
-
const
|
|
23
|
+
const TextBoxPrimitive = React__namespace.forwardRef(
|
|
24
24
|
({ className, type, ...props }, ref) => {
|
|
25
25
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26
26
|
"input",
|
|
@@ -36,6 +36,6 @@ const Input = React__namespace.forwardRef(
|
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
);
|
|
39
|
-
|
|
40
|
-
exports.
|
|
41
|
-
//# sourceMappingURL=
|
|
39
|
+
TextBoxPrimitive.displayName = "TextBoxPrimitive";
|
|
40
|
+
exports.TextBoxPrimitive = TextBoxPrimitive;
|
|
41
|
+
//# sourceMappingURL=textbox.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textbox.cjs","sources":["../../../src/primitives/textbox.tsx"],"sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/utils\"\n\nconst TextBoxPrimitive = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n }\n)\nTextBoxPrimitive.displayName = \"TextBoxPrimitive\"\n\nexport { TextBoxPrimitive }\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,mBAAmBA,iBAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,MAAM,GAAG,MAAA,GAAS,QAAQ;AACtC,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,iBAAiB,cAAc;;"}
|
|
@@ -1,35 +1,21 @@
|
|
|
1
|
-
import { jsxs,
|
|
2
|
-
import {
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import "class-variance-authority";
|
|
3
4
|
import { cn } from "../../utils/cn.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
lg: "h-12 px-6 text-base"
|
|
19
|
-
},
|
|
20
|
-
fullWidth: {
|
|
21
|
-
true: "w-full flex",
|
|
22
|
-
false: ""
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
defaultVariants: {
|
|
26
|
-
variant: "primary",
|
|
27
|
-
size: "md",
|
|
28
|
-
fullWidth: false
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
function Button({
|
|
5
|
+
import { ButtonPrimitive } from "../../primitives/button.js";
|
|
6
|
+
const variantMap = {
|
|
7
|
+
primary: "default",
|
|
8
|
+
secondary: "secondary",
|
|
9
|
+
outline: "outline",
|
|
10
|
+
ghost: "ghost",
|
|
11
|
+
danger: "destructive"
|
|
12
|
+
};
|
|
13
|
+
const sizeMap = {
|
|
14
|
+
sm: "sm",
|
|
15
|
+
md: "default",
|
|
16
|
+
lg: "lg"
|
|
17
|
+
};
|
|
18
|
+
const Button = forwardRef(function Button2({
|
|
33
19
|
children,
|
|
34
20
|
variant = "primary",
|
|
35
21
|
size = "md",
|
|
@@ -37,21 +23,29 @@ function Button({
|
|
|
37
23
|
loading = false,
|
|
38
24
|
disabled,
|
|
39
25
|
className,
|
|
26
|
+
asChild = false,
|
|
27
|
+
style,
|
|
40
28
|
...props
|
|
41
|
-
}) {
|
|
42
|
-
return /* @__PURE__ */
|
|
43
|
-
|
|
29
|
+
}, ref) {
|
|
30
|
+
return /* @__PURE__ */ jsx(
|
|
31
|
+
ButtonPrimitive,
|
|
44
32
|
{
|
|
45
|
-
|
|
33
|
+
ref,
|
|
34
|
+
variant: variantMap[variant],
|
|
35
|
+
size: sizeMap[size],
|
|
36
|
+
asChild,
|
|
46
37
|
disabled: disabled || loading,
|
|
38
|
+
className: cn(fullWidth && "w-full", "relative cursor-pointer", className),
|
|
39
|
+
style,
|
|
47
40
|
...props,
|
|
48
|
-
children: [
|
|
49
|
-
loading && /* @__PURE__ */ jsx("span", { className: "size-4 border-2 border-current border-r-transparent rounded-full animate-spin" }),
|
|
41
|
+
children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
+
loading && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "size-4 border-2 border-current border-r-transparent rounded-full animate-spin" }) }),
|
|
50
43
|
/* @__PURE__ */ jsx("span", { className: cn(loading && "invisible"), children })
|
|
51
|
-
]
|
|
44
|
+
] })
|
|
52
45
|
}
|
|
53
46
|
);
|
|
54
|
-
}
|
|
47
|
+
});
|
|
48
|
+
Button.displayName = "Button";
|
|
55
49
|
export {
|
|
56
50
|
Button
|
|
57
51
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["import type
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { ButtonPrimitive, buttonVariants } from \"../../primitives/button\";\n\ntype PrimitiveVariant = NonNullable<VariantProps<typeof buttonVariants>[\"variant\"]>;\ntype PrimitiveSize = NonNullable<VariantProps<typeof buttonVariants>[\"size\"]>;\n\nconst variantMap: Record<string, PrimitiveVariant> = {\n primary: \"default\",\n secondary: \"secondary\",\n outline: \"outline\",\n ghost: \"ghost\",\n danger: \"destructive\",\n};\n\nconst sizeMap: Record<string, PrimitiveSize> = {\n sm: \"sm\",\n md: \"default\",\n lg: \"lg\",\n};\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant */\n variant?: keyof typeof variantMap;\n /** Size of the button */\n size?: keyof typeof sizeMap;\n /** Stretch button to full container width */\n fullWidth?: boolean;\n /** Show a loading spinner and disable interaction */\n loading?: boolean;\n /** Render as child element via Slot (e.g. wrap a Link) */\n asChild?: boolean;\n children: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n disabled,\n className,\n asChild = false,\n style,\n ...props\n },\n ref\n) {\n return (\n <ButtonPrimitive\n ref={ref}\n variant={variantMap[variant]}\n size={sizeMap[size]}\n asChild={asChild}\n disabled={disabled || loading}\n className={cn(fullWidth && \"w-full\", \"relative cursor-pointer\", className)}\n style={style}\n {...props}\n >\n {asChild ? (\n children\n ) : (\n <>\n {loading && (\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"size-4 border-2 border-current border-r-transparent rounded-full animate-spin\" />\n </span>\n )}\n <span className={cn(loading && \"invisible\")}>{children}</span>\n </>\n )}\n </ButtonPrimitive>\n );\n});\n\nButton.displayName = \"Button\";\n"],"names":["Button"],"mappings":";;;;;AAQA,MAAM,aAA+C;AAAA,EACnD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,MAAM,UAAyC;AAAA,EAC7C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAgBO,MAAM,SAAS,WAA2C,SAASA,QACxE;AAAA,EACE;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,GAAG;AACL,GACA,KACA;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAS,WAAW,OAAO;AAAA,MAC3B,MAAM,QAAQ,IAAI;AAAA,MAClB;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,WAAW,GAAG,aAAa,UAAU,2BAA2B,SAAS;AAAA,MACzE;AAAA,MACC,GAAG;AAAA,MAEH,UAAA,UACC,WAEA,qBAAA,UAAA,EACG,UAAA;AAAA,QAAA,WACC,oBAAC,UAAK,WAAU,qDACd,8BAAC,QAAA,EAAK,WAAU,iFAAgF,EAAA,CAClG;AAAA,4BAED,QAAA,EAAK,WAAW,GAAG,WAAW,WAAW,GAAI,SAAA,CAAS;AAAA,MAAA,EAAA,CACzD;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAED,OAAO,cAAc;"}
|
|
@@ -5,7 +5,7 @@ import { cva } from "class-variance-authority";
|
|
|
5
5
|
import { cn } from "../../utils/cn.js";
|
|
6
6
|
import { FormConfigContext } from "./context.js";
|
|
7
7
|
import { labelVariants } from "./variants.js";
|
|
8
|
-
import {
|
|
8
|
+
import { TextBoxPrimitive } from "../../primitives/textbox.js";
|
|
9
9
|
import { Label } from "../../primitives/label.js";
|
|
10
10
|
import { X } from "lucide-react";
|
|
11
11
|
const textBoxVariants = cva(
|
|
@@ -82,6 +82,14 @@ function TextBoxInner({
|
|
|
82
82
|
let fieldType = "string";
|
|
83
83
|
if (inputType === "number") {
|
|
84
84
|
fieldType = "number";
|
|
85
|
+
} else if (inputType === "date") {
|
|
86
|
+
fieldType = "date";
|
|
87
|
+
} else if (inputType === "email" && rules.email === void 0) {
|
|
88
|
+
rules.email = true;
|
|
89
|
+
} else if (inputType === "url" && rules.url === void 0) {
|
|
90
|
+
rules.url = true;
|
|
91
|
+
} else if (inputType === "tel" && rules.pattern === void 0) {
|
|
92
|
+
rules.pattern = { value: /^\+?[\d\s\-().]{7,}$/, message: "Invalid phone number" };
|
|
85
93
|
}
|
|
86
94
|
registerFieldValidation({
|
|
87
95
|
name,
|
|
@@ -136,7 +144,7 @@ function TextBoxInner({
|
|
|
136
144
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
137
145
|
prefix && /* @__PURE__ */ jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: prefix }),
|
|
138
146
|
/* @__PURE__ */ jsx(
|
|
139
|
-
|
|
147
|
+
TextBoxPrimitive,
|
|
140
148
|
{
|
|
141
149
|
...props,
|
|
142
150
|
...field,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextBox.js","sources":["../../../src/components/Form/TextBox.tsx"],"sourcesContent":["import { forwardRef, useId, useContext, useEffect } from \"react\";\nimport type { InputHTMLAttributes, ReactNode } from \"react\";\nimport { useController, useFormContext as useRHFFormContext, type FieldValues, type FieldPath, type Control } from \"react-hook-form\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { FormConfigContext, type FormConfig, type FieldValidationRules } from \"../Form/context\";\nimport { labelVariants } from \"./variants\";\nimport { Input } from \"../../primitives/input\";\nimport { Label } from \"../../primitives/label\";\nimport { X } from \"lucide-react\";\n\nconst textBoxVariants = cva(\n \"\",\n {\n variants: {\n size: {\n sm: \"h-8 text-xs px-2\",\n md: \"h-10 text-sm px-3\",\n lg: \"h-12 text-base px-4\",\n },\n variant: {\n default: \"border-input focus-visible:ring-ring\",\n error: \"border-destructive focus-visible:ring-destructive\",\n success: \"border-green-500 focus-visible:ring-green-500\",\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"default\",\n },\n }\n);\n\n/**\n * Validation rule with optional custom message\n */\ntype ValidationRule<T> = T | { value: T; message: string };\n\nexport interface TextBoxProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> extends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"name\" | \"prefix\" | \"pattern\" | \"required\" | \"minLength\" | \"maxLength\">,\n VariantProps<typeof textBoxVariants> {\n /** Field name - required for form integration */\n name: TName;\n /** Label text for the input */\n label?: string;\n /** Helper text displayed below the input */\n helperText?: string;\n /** Whether to show the error message */\n showError?: boolean;\n /** Custom error message (overrides form error) */\n error?: string;\n /** Whether the input should take full width */\n fullWidth?: boolean;\n /** Prefix element */\n prefix?: ReactNode;\n /** Suffix element */\n suffix?: ReactNode;\n /** Allow clear button */\n allowClear?: boolean;\n /** Callback when clear is clicked */\n onClear?: () => void;\n /** External control (for use outside Form) */\n control?: Control<TFieldValues>;\n \n // Validation props\n /** Field is required */\n required?: boolean | string;\n /** Minimum length for strings */\n minLength?: ValidationRule<number>;\n /** Maximum length for strings */\n maxLength?: ValidationRule<number>;\n /** Minimum value for numbers */\n minValue?: ValidationRule<number>;\n /** Maximum value for numbers */\n maxValue?: ValidationRule<number>;\n /** Regex pattern for validation */\n pattern?: ValidationRule<RegExp>;\n /** Email validation */\n email?: boolean | string;\n /** URL validation */\n url?: boolean | string;\n /** Custom validation function */\n validate?: (value: unknown) => boolean | string | Promise<boolean | string>;\n}\n\n/**\n * TextBox component with form integration and automatic validation registration\n * \n * This is a wrapper around the shadcn/ui Input primitive that adds:\n * - Form integration with react-hook-form\n * - Automatic validation registration\n * - Label, helper text, and error message support\n * - Prefix/suffix elements\n * - Clear button functionality\n * \n * @example\n * ```tsx\n * // Inside a Form component - validation is automatically registered\n * <Form onSubmit={handleSubmit} defaultValues={{ username: \"\", email: \"\" }}>\n * <TextBox name=\"username\" label=\"Username\" required minLength={3} maxLength={50} />\n * <TextBox name=\"email\" label=\"Email\" type=\"email\" required email />\n * <TextBox name=\"website\" label=\"Website\" url />\n * <Button type=\"submit\">Submit</Button>\n * </Form>\n * ```\n */\nfunction TextBoxInner<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n {\n name,\n label,\n helperText,\n showError = true,\n error: customError,\n size,\n variant,\n fullWidth = true,\n className,\n disabled,\n prefix,\n suffix,\n allowClear,\n onClear,\n id: providedId,\n control: externalControl,\n // Validation props\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n ...props\n }: TextBoxProps<TFieldValues, TName>,\n ref: React.ForwardedRef<HTMLInputElement>\n) {\n const generatedId = useId();\n const inputId = providedId ?? generatedId;\n \n // Try to get form context\n const formConfigContext = useContext(FormConfigContext);\n const formConfig: FormConfig = formConfigContext ?? {};\n\n // Get form context from react-hook-form\n const rhfContext = useRHFFormContext<TFieldValues>();\n const control = externalControl ?? rhfContext?.control;\n\n // Destructure stable function references so the effect doesn't depend on the\n // context object identity (which changes every render due to inline construction).\n const registerFieldValidation = formConfigContext?.registerFieldValidation;\n const unregisterFieldValidation = formConfigContext?.unregisterFieldValidation;\n\n // Register validation rules with the form\n useEffect(() => {\n if (registerFieldValidation) {\n const rules: FieldValidationRules = {};\n\n if (required !== undefined) rules.required = required;\n if (minLength !== undefined) rules.minLength = minLength;\n if (maxLength !== undefined) rules.maxLength = maxLength;\n if (minValue !== undefined) rules.min = minValue;\n if (maxValue !== undefined) rules.max = maxValue;\n if (pattern !== undefined) rules.pattern = pattern;\n if (email !== undefined) rules.email = email;\n if (url !== undefined) rules.url = url;\n if (validate !== undefined) rules.validate = validate;\n\n // Determine field type based on input type\n const inputType = props.type ?? \"text\";\n let fieldType: \"string\" | \"number\" = \"string\";\n if (inputType === \"number\") {\n fieldType = \"number\";\n }\n\n registerFieldValidation({\n name: name as string,\n type: fieldType,\n rules,\n });\n\n return () => {\n unregisterFieldValidation?.(name as string);\n };\n }\n }, [\n registerFieldValidation,\n unregisterFieldValidation,\n name,\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n props.type,\n ]);\n\n // Use controller for form integration\n const { field, fieldState } = useController<TFieldValues, TName>({\n name,\n control,\n });\n\n const fieldError = fieldState.error?.message;\n const errorMessage = customError ?? fieldError;\n const hasError = !!errorMessage;\n \n // Merge sizes - prop takes precedence over form config\n const effectiveSize = size ?? formConfig.size ?? \"md\";\n const effectiveDisabled = disabled ?? formConfig.disabled;\n \n // Determine variant based on error state\n const effectiveVariant = hasError ? \"error\" : variant;\n\n const handleClear = () => {\n field.onChange(\"\");\n onClear?.();\n };\n\n return (\n <div className={cn(\"space-y-1.5\", fullWidth && \"w-full\")}>\n {label && (\n <Label\n htmlFor={inputId}\n className={labelVariants({ required: !!required })}\n >\n {label}\n {formConfig.colon && \":\"}\n </Label>\n )}\n \n <div className=\"relative\">\n {prefix && (\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {prefix}\n </div>\n )}\n \n <Input\n {...props}\n {...field}\n ref={(node) => {\n // Handle both refs\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n field.ref(node);\n }}\n id={inputId}\n disabled={effectiveDisabled}\n aria-invalid={hasError}\n aria-describedby={\n hasError\n ? `${inputId}-error`\n : helperText\n ? `${inputId}-helper`\n : undefined\n }\n className={cn(\n textBoxVariants({ size: effectiveSize, variant: effectiveVariant }),\n prefix && \"pl-10\",\n (suffix || allowClear) && \"pr-10\",\n className\n )}\n />\n \n {(suffix || (allowClear && field.value)) && (\n <div className=\"absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1\">\n {allowClear && field.value && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n tabIndex={-1}\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n {suffix && (\n <span className=\"text-muted-foreground\">{suffix}</span>\n )}\n </div>\n )}\n </div>\n\n {showError && hasError && (\n <p\n id={`${inputId}-error`}\n className=\"text-sm text-destructive\"\n role=\"alert\"\n >\n {errorMessage}\n </p>\n )}\n \n {helperText && !hasError && (\n <p\n id={`${inputId}-helper`}\n className=\"text-sm text-muted-foreground\"\n >\n {helperText}\n </p>\n )}\n </div>\n );\n}\n\n// Use forwardRef with generic support\nexport const TextBox = forwardRef(TextBoxInner) as <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n props: TextBoxProps<TFieldValues, TName> & { ref?: React.ForwardedRef<HTMLInputElement> }\n) => React.ReactElement;\n\n(TextBox as React.FC).displayName = \"TextBox\";\n"],"names":["useRHFFormContext"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TextBox.js","sources":["../../../src/components/Form/TextBox.tsx"],"sourcesContent":["import { forwardRef, useId, useContext, useEffect } from \"react\";\nimport type { InputHTMLAttributes, ReactNode } from \"react\";\nimport { useController, useFormContext as useRHFFormContext, type FieldValues, type FieldPath, type Control } from \"react-hook-form\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\nimport { FormConfigContext, type FormConfig, type FieldValidationRules } from \"../Form/context\";\nimport { labelVariants } from \"./variants\";\nimport { TextBoxPrimitive } from \"../../primitives/textbox\";\nimport { Label } from \"../../primitives/label\";\nimport { X } from \"lucide-react\";\n\nconst textBoxVariants = cva(\n \"\",\n {\n variants: {\n size: {\n sm: \"h-8 text-xs px-2\",\n md: \"h-10 text-sm px-3\",\n lg: \"h-12 text-base px-4\",\n },\n variant: {\n default: \"border-input focus-visible:ring-ring\",\n error: \"border-destructive focus-visible:ring-destructive\",\n success: \"border-green-500 focus-visible:ring-green-500\",\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"default\",\n },\n }\n);\n\n/**\n * Validation rule with optional custom message\n */\ntype ValidationRule<T> = T | { value: T; message: string };\n\nexport interface TextBoxProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> extends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"name\" | \"prefix\" | \"pattern\" | \"required\" | \"minLength\" | \"maxLength\">,\n VariantProps<typeof textBoxVariants> {\n /** Field name - required for form integration */\n name: TName;\n /** Label text for the input */\n label?: string;\n /** Helper text displayed below the input */\n helperText?: string;\n /** Whether to show the error message */\n showError?: boolean;\n /** Custom error message (overrides form error) */\n error?: string;\n /** Whether the input should take full width */\n fullWidth?: boolean;\n /** Prefix element */\n prefix?: ReactNode;\n /** Suffix element */\n suffix?: ReactNode;\n /** Allow clear button */\n allowClear?: boolean;\n /** Callback when clear is clicked */\n onClear?: () => void;\n /** External control (for use outside Form) */\n control?: Control<TFieldValues>;\n \n // Validation props\n /** Field is required */\n required?: boolean | string;\n /** Minimum length for strings */\n minLength?: ValidationRule<number>;\n /** Maximum length for strings */\n maxLength?: ValidationRule<number>;\n /** Minimum value for numbers */\n minValue?: ValidationRule<number>;\n /** Maximum value for numbers */\n maxValue?: ValidationRule<number>;\n /** Regex pattern for validation */\n pattern?: ValidationRule<RegExp>;\n /** Email validation */\n email?: boolean | string;\n /** URL validation */\n url?: boolean | string;\n /** Custom validation function */\n validate?: (value: unknown) => boolean | string | Promise<boolean | string>;\n}\n\n/**\n * TextBox component with form integration and automatic validation registration\n * \n * This is a wrapper around the shadcn/ui Input primitive that adds:\n * - Form integration with react-hook-form\n * - Automatic validation registration\n * - Label, helper text, and error message support\n * - Prefix/suffix elements\n * - Clear button functionality\n * \n * @example\n * ```tsx\n * // Inside a Form component - validation is automatically registered\n * <Form onSubmit={handleSubmit} defaultValues={{ username: \"\", email: \"\" }}>\n * <TextBox name=\"username\" label=\"Username\" required minLength={3} maxLength={50} />\n * <TextBox name=\"email\" label=\"Email\" type=\"email\" required email />\n * <TextBox name=\"website\" label=\"Website\" url />\n * <Button type=\"submit\">Submit</Button>\n * </Form>\n * ```\n */\nfunction TextBoxInner<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n {\n name,\n label,\n helperText,\n showError = true,\n error: customError,\n size,\n variant,\n fullWidth = true,\n className,\n disabled,\n prefix,\n suffix,\n allowClear,\n onClear,\n id: providedId,\n control: externalControl,\n // Validation props\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n ...props\n }: TextBoxProps<TFieldValues, TName>,\n ref: React.ForwardedRef<HTMLInputElement>\n) {\n const generatedId = useId();\n const inputId = providedId ?? generatedId;\n \n // Try to get form context\n const formConfigContext = useContext(FormConfigContext);\n const formConfig: FormConfig = formConfigContext ?? {};\n\n // Get form context from react-hook-form\n const rhfContext = useRHFFormContext<TFieldValues>();\n const control = externalControl ?? rhfContext?.control;\n\n // Destructure stable function references so the effect doesn't depend on the\n // context object identity (which changes every render due to inline construction).\n const registerFieldValidation = formConfigContext?.registerFieldValidation;\n const unregisterFieldValidation = formConfigContext?.unregisterFieldValidation;\n\n // Register validation rules with the form\n useEffect(() => {\n if (registerFieldValidation) {\n const rules: FieldValidationRules = {};\n\n if (required !== undefined) rules.required = required;\n if (minLength !== undefined) rules.minLength = minLength;\n if (maxLength !== undefined) rules.maxLength = maxLength;\n if (minValue !== undefined) rules.min = minValue;\n if (maxValue !== undefined) rules.max = maxValue;\n if (pattern !== undefined) rules.pattern = pattern;\n if (email !== undefined) rules.email = email;\n if (url !== undefined) rules.url = url;\n if (validate !== undefined) rules.validate = validate;\n\n // Determine field type and auto-infer rules from input type\n const inputType = props.type ?? \"text\";\n let fieldType: \"string\" | \"number\" | \"date\" = \"string\";\n\n if (inputType === \"number\") {\n fieldType = \"number\";\n } else if (inputType === \"date\") {\n fieldType = \"date\";\n } else if (inputType === \"email\" && rules.email === undefined) {\n rules.email = true;\n } else if (inputType === \"url\" && rules.url === undefined) {\n rules.url = true;\n } else if (inputType === \"tel\" && rules.pattern === undefined) {\n rules.pattern = { value: /^\\+?[\\d\\s\\-().]{7,}$/, message: \"Invalid phone number\" };\n }\n\n registerFieldValidation({\n name: name as string,\n type: fieldType,\n rules,\n });\n\n return () => {\n unregisterFieldValidation?.(name as string);\n };\n }\n }, [\n registerFieldValidation,\n unregisterFieldValidation,\n name,\n required,\n minLength,\n maxLength,\n minValue,\n maxValue,\n pattern,\n email,\n url,\n validate,\n props.type,\n ]);\n\n // Use controller for form integration\n const { field, fieldState } = useController<TFieldValues, TName>({\n name,\n control,\n });\n\n const fieldError = fieldState.error?.message;\n const errorMessage = customError ?? fieldError;\n const hasError = !!errorMessage;\n \n // Merge sizes - prop takes precedence over form config\n const effectiveSize = size ?? formConfig.size ?? \"md\";\n const effectiveDisabled = disabled ?? formConfig.disabled;\n \n // Determine variant based on error state\n const effectiveVariant = hasError ? \"error\" : variant;\n\n const handleClear = () => {\n field.onChange(\"\");\n onClear?.();\n };\n\n return (\n <div className={cn(\"space-y-1.5\", fullWidth && \"w-full\")}>\n {label && (\n <Label\n htmlFor={inputId}\n className={labelVariants({ required: !!required })}\n >\n {label}\n {formConfig.colon && \":\"}\n </Label>\n )}\n \n <div className=\"relative\">\n {prefix && (\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {prefix}\n </div>\n )}\n \n <TextBoxPrimitive\n {...props}\n {...field}\n ref={(node) => {\n // Handle both refs\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n field.ref(node);\n }}\n id={inputId}\n disabled={effectiveDisabled}\n aria-invalid={hasError}\n aria-describedby={\n hasError\n ? `${inputId}-error`\n : helperText\n ? `${inputId}-helper`\n : undefined\n }\n className={cn(\n textBoxVariants({ size: effectiveSize, variant: effectiveVariant }),\n prefix && \"pl-10\",\n (suffix || allowClear) && \"pr-10\",\n className\n )}\n />\n\n {(suffix || (allowClear && field.value)) && (\n <div className=\"absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1\">\n {allowClear && field.value && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n tabIndex={-1}\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n {suffix && (\n <span className=\"text-muted-foreground\">{suffix}</span>\n )}\n </div>\n )}\n </div>\n\n {showError && hasError && (\n <p\n id={`${inputId}-error`}\n className=\"text-sm text-destructive\"\n role=\"alert\"\n >\n {errorMessage}\n </p>\n )}\n \n {helperText && !hasError && (\n <p\n id={`${inputId}-helper`}\n className=\"text-sm text-muted-foreground\"\n >\n {helperText}\n </p>\n )}\n </div>\n );\n}\n\n// Use forwardRef with generic support\nexport const TextBox = forwardRef(TextBoxInner) as <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>(\n props: TextBoxProps<TFieldValues, TName> & { ref?: React.ForwardedRef<HTMLInputElement> }\n) => React.ReactElement;\n\n(TextBox as React.FC).displayName = \"TextBox\";\n"],"names":["useRHFFormContext"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { Separator } from "./primitives/separator.js";
|
|
|
15
15
|
import { Switch } from "./components/Form/Switch.js";
|
|
16
16
|
import { TextArea } from "./components/Form/TextArea.js";
|
|
17
17
|
import { TextBox } from "./components/Form/TextBox.js";
|
|
18
|
+
import { buttonVariants } from "./primitives/button.js";
|
|
18
19
|
import { useFieldError, useForm, useFormConfig, useFormField } from "./components/Form/hooks.js";
|
|
19
20
|
export {
|
|
20
21
|
Avatar,
|
|
@@ -55,6 +56,7 @@ export {
|
|
|
55
56
|
TextBox,
|
|
56
57
|
badgeVariants,
|
|
57
58
|
buildZodSchemaFromRules,
|
|
59
|
+
buttonVariants,
|
|
58
60
|
cn,
|
|
59
61
|
defaultFormConfig,
|
|
60
62
|
useDebounce,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { cn } from "../utils/cn.js";
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
12
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
13
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
14
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
15
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
16
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
default: "h-10 px-4 py-2",
|
|
20
|
+
sm: "h-9 px-3",
|
|
21
|
+
lg: "h-11 px-8",
|
|
22
|
+
icon: "h-10 w-10"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
variant: "default",
|
|
27
|
+
size: "default"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
const ButtonPrimitive = React.forwardRef(
|
|
32
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
33
|
+
const Comp = asChild ? Slot : "button";
|
|
34
|
+
return /* @__PURE__ */ jsx(
|
|
35
|
+
Comp,
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
39
|
+
...props
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
ButtonPrimitive.displayName = "ButtonPrimitive";
|
|
45
|
+
export {
|
|
46
|
+
ButtonPrimitive,
|
|
47
|
+
buttonVariants
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.js","sources":["../../src/primitives/button.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 px-3\",\n lg: \"h-11 px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonPrimitiveProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst ButtonPrimitive = React.forwardRef<HTMLButtonElement, ButtonPrimitiveProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n <Comp\n ref={ref}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n }\n)\nButtonPrimitive.displayName = \"ButtonPrimitive\"\n\nexport { ButtonPrimitive, buttonVariants }\n"],"names":[],"mappings":";;;;;AAMA,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,QACT,WAAW;AAAA,QACX,OAAO;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,MAER,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAQA,MAAM,kBAAkB,MAAM;AAAA,EAC5B,CAAC,EAAE,WAAW,SAAS,MAAM,UAAU,OAAO,GAAG,MAAA,GAAS,QAAQ;AAChE,UAAM,OAAO,UAAU,OAAO;AAC9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAA,CAAW,CAAC;AAAA,QACzD,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,gBAAgB,cAAc;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "../utils/cn.js";
|
|
4
|
-
const
|
|
4
|
+
const TextBoxPrimitive = React.forwardRef(
|
|
5
5
|
({ className, type, ...props }, ref) => {
|
|
6
6
|
return /* @__PURE__ */ jsx(
|
|
7
7
|
"input",
|
|
@@ -17,8 +17,8 @@ const Input = React.forwardRef(
|
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
);
|
|
20
|
-
|
|
20
|
+
TextBoxPrimitive.displayName = "TextBoxPrimitive";
|
|
21
21
|
export {
|
|
22
|
-
|
|
22
|
+
TextBoxPrimitive
|
|
23
23
|
};
|
|
24
|
-
//# sourceMappingURL=
|
|
24
|
+
//# sourceMappingURL=textbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textbox.js","sources":["../../src/primitives/textbox.tsx"],"sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/utils\"\n\nconst TextBoxPrimitive = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n }\n)\nTextBoxPrimitive.displayName = \"TextBoxPrimitive\"\n\nexport { TextBoxPrimitive }\n"],"names":[],"mappings":";;;AAIA,MAAM,mBAAmB,MAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,MAAM,GAAG,MAAA,GAAS,QAAQ;AACtC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,iBAAiB,cAAc;"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"input.cjs","sources":["../../../src/primitives/input.tsx"],"sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/utils\"\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,QAAQA,iBAAM;AAAA,EAClB,CAAC,EAAE,WAAW,MAAM,GAAG,MAAA,GAAS,QAAQ;AACtC,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,MAAM,cAAc;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","sources":["../../src/primitives/input.tsx"],"sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/utils\"\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"],"names":[],"mappings":";;;AAIA,MAAM,QAAQ,MAAM;AAAA,EAClB,CAAC,EAAE,WAAW,MAAM,GAAG,MAAA,GAAS,QAAQ;AACtC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,MAAM,cAAc;"}
|