turiy-form
Version:
This package is built on shadcn, zod
614 lines (595 loc) • 24.3 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Button: () => Button,
Formx: () => Formx,
FormxField: () => FormxField,
buttonVariants: () => buttonVariants,
equal: () => equal,
isObject: () => isObject,
regex: () => regex,
reset: () => reset
});
module.exports = __toCommonJS(src_exports);
// src/form/Formx.tsx
var import_react4 = __toESM(require("react"));
// src/ui/form.tsx
var React2 = __toESM(require("react"));
var import_react_slot = require("@radix-ui/react-slot");
var import_react_hook_form = require("react-hook-form");
// src/lib/utils.ts
var import_clsx = require("clsx");
var import_tailwind_merge = require("tailwind-merge");
function cn(...inputs) {
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
}
// src/ui/label.tsx
var React = __toESM(require("react"));
var LabelPrimitive = __toESM(require("@radix-ui/react-label"));
var import_class_variance_authority = require("class-variance-authority");
var labelVariants = (0, import_class_variance_authority.cva)(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
);
var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
LabelPrimitive.Root,
{
ref,
className: cn(labelVariants(), className),
...props
}
));
Label.displayName = LabelPrimitive.Root.displayName;
// src/ui/form.tsx
var Form = import_react_hook_form.FormProvider;
var FormFieldContext = React2.createContext(
{}
);
var FormField = ({
...props
}) => {
return /* @__PURE__ */ React2.createElement(FormFieldContext.Provider, { value: { name: props.name } }, /* @__PURE__ */ React2.createElement(import_react_hook_form.Controller, { ...props }));
};
var useFormField = () => {
const fieldContext = React2.useContext(FormFieldContext);
const itemContext = React2.useContext(FormItemContext);
const { getFieldState, formState } = (0, import_react_hook_form.useFormContext)();
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}
const { id } = itemContext;
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState
};
};
var FormItemContext = React2.createContext(
{}
);
var FormItem = React2.forwardRef(({ className, ...props }, ref) => {
const id = React2.useId();
return /* @__PURE__ */ React2.createElement(FormItemContext.Provider, { value: { id } }, /* @__PURE__ */ React2.createElement("div", { ref, className: cn("space-y-2", className), ...props }));
});
FormItem.displayName = "FormItem";
var FormLabel = React2.forwardRef(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();
return /* @__PURE__ */ React2.createElement(
Label,
{
ref,
className: cn(error && "text-destructive", className),
htmlFor: formItemId,
...props
}
);
});
FormLabel.displayName = "FormLabel";
var FormControl = React2.forwardRef(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
return /* @__PURE__ */ React2.createElement(
import_react_slot.Slot,
{
ref,
id: formItemId,
"aria-describedby": !error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`,
"aria-invalid": !!error,
...props
}
);
});
FormControl.displayName = "FormControl";
var FormDescription = React2.forwardRef(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();
return /* @__PURE__ */ React2.createElement(
"p",
{
ref,
id: formDescriptionId,
className: cn("text-sm text-muted-foreground", className),
...props
}
);
});
FormDescription.displayName = "FormDescription";
var FormMessage = React2.forwardRef(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
if (!body) {
return null;
}
return /* @__PURE__ */ React2.createElement(
"p",
{
ref,
id: formMessageId,
className: cn("text-sm font-medium text-destructive", className),
...props
},
body
);
});
FormMessage.displayName = "FormMessage";
// src/form/FomxField.tsx
var import_react = __toESM(require("react"));
var FormxField = import_react.default.forwardRef((props, ref) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null));
FormxField.displayName = "FormxField";
// src/form/Formx.tsx
var import_zod2 = require("zod");
var import_react_hook_form2 = require("react-hook-form");
var import_zod3 = require("@hookform/resolvers/zod");
// src/form/FormxSelect.tsx
var import_react2 = __toESM(require("react"));
// src/ui/select.tsx
var React4 = __toESM(require("react"));
var SelectPrimitive = __toESM(require("@radix-ui/react-select"));
var import_lucide_react = require("lucide-react");
var Select = SelectPrimitive.Root;
var SelectValue = SelectPrimitive.Value;
var SelectTrigger = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.Trigger,
{
ref,
className: cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
),
...props
},
children,
/* @__PURE__ */ React4.createElement(SelectPrimitive.Icon, { asChild: true }, /* @__PURE__ */ React4.createElement(import_lucide_react.ChevronDown, { className: "h-4 w-4 opacity-50" }))
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
var SelectScrollUpButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.ScrollUpButton,
{
ref,
className: cn(
"flex cursor-default items-center justify-center py-1",
className
),
...props
},
/* @__PURE__ */ React4.createElement(import_lucide_react.ChevronUp, { className: "h-4 w-4" })
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
var SelectScrollDownButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.ScrollDownButton,
{
ref,
className: cn(
"flex cursor-default items-center justify-center py-1",
className
),
...props
},
/* @__PURE__ */ React4.createElement(import_lucide_react.ChevronDown, { className: "h-4 w-4" })
));
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
var SelectContent = React4.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ React4.createElement(SelectPrimitive.Portal, null, /* @__PURE__ */ React4.createElement(
SelectPrimitive.Content,
{
ref,
className: cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
),
position,
...props
},
/* @__PURE__ */ React4.createElement(SelectScrollUpButton, null),
/* @__PURE__ */ React4.createElement(
SelectPrimitive.Viewport,
{
className: cn(
"p-1",
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)
},
children
),
/* @__PURE__ */ React4.createElement(SelectScrollDownButton, null)
)));
SelectContent.displayName = SelectPrimitive.Content.displayName;
var SelectLabel = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.Label,
{
ref,
className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
...props
}
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
var SelectItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.Item,
{
ref,
className: cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
),
...props
},
/* @__PURE__ */ React4.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React4.createElement(SelectPrimitive.ItemIndicator, null, /* @__PURE__ */ React4.createElement(import_lucide_react.Check, { className: "h-4 w-4" }))),
/* @__PURE__ */ React4.createElement(SelectPrimitive.ItemText, null, children)
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
var SelectSeparator = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
SelectPrimitive.Separator,
{
ref,
className: cn("-mx-1 my-1 h-px bg-muted", className),
...props
}
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
// src/ui/button.tsx
var React5 = __toESM(require("react"));
var import_react_slot2 = require("@radix-ui/react-slot");
var import_class_variance_authority2 = require("class-variance-authority");
var buttonVariants = (0, import_class_variance_authority2.cva)(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background 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",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
var Button = React5.forwardRef(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? import_react_slot2.Slot : "button";
return /* @__PURE__ */ React5.createElement(
Comp,
{
className: cn(buttonVariants({ variant, size, className })),
ref,
...props
}
);
}
);
Button.displayName = "Button";
// src/form/FormxSelect.tsx
var import_lucide_react2 = require("lucide-react");
var FormxSelect = ({
control,
name,
label,
placeholder,
options,
className,
onAdd,
description,
disabled,
required,
autoFocus,
onFocusChanged
}) => {
const ref = import_react2.default.useRef(null);
import_react2.default.useEffect(() => {
if (!!ref.current && !!autoFocus) ref.current.focus();
}, [autoFocus, ref]);
return /* @__PURE__ */ import_react2.default.createElement("div", { className: cn(className) }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex w-full gap-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-1" }, /* @__PURE__ */ import_react2.default.createElement(
FormField,
{
control,
name,
render: ({ field }) => /* @__PURE__ */ import_react2.default.createElement(FormItem, null, /* @__PURE__ */ import_react2.default.createElement(FormLabel, null, " ", !disabled && !!required ? /* @__PURE__ */ import_react2.default.createElement("span", { className: "text-red-700" }, "*") : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null), label), /* @__PURE__ */ import_react2.default.createElement(
Select,
{
onValueChange: field.onChange,
defaultValue: field.value,
disabled,
required: !disabled && !!required,
onOpenChange: (focused) => onFocusChanged(focused)
},
/* @__PURE__ */ import_react2.default.createElement(FormControl, null, /* @__PURE__ */ import_react2.default.createElement(SelectTrigger, { ref }, /* @__PURE__ */ import_react2.default.createElement(SelectValue, { placeholder }))),
/* @__PURE__ */ import_react2.default.createElement(SelectContent, null, options.map((op) => /* @__PURE__ */ import_react2.default.createElement(SelectItem, { key: op.value, value: op.value }, op.label)))
), !!description ? /* @__PURE__ */ import_react2.default.createElement(FormDescription, null, description) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null), /* @__PURE__ */ import_react2.default.createElement(FormMessage, null))
}
)), onAdd ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-full flex flex-col items-center justify-center" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-1" }), /* @__PURE__ */ import_react2.default.createElement(
Button,
{
type: "button",
variant: "secondary",
onClick: onAdd,
className: cn(!!onAdd ? "visible" : "hidden"),
disabled
},
/* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.PlusIcon, null)
)) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null)));
};
var FormxSelect_default = FormxSelect;
// src/form/FormxInput.tsx
var import_react3 = __toESM(require("react"));
// src/ui/input.tsx
var React7 = __toESM(require("react"));
var Input = React7.forwardRef(
({ className, type, ...props }, ref) => {
return /* @__PURE__ */ React7.createElement(
"input",
{
type,
className: cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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",
className
),
ref,
...props
}
);
}
);
Input.displayName = "Input";
// src/form/FormxInput.tsx
var FormxInput = ({
control,
name,
label,
placeholder,
type,
className,
description,
disabled,
required,
autoFocus,
onFocusChanged
}) => {
const ref = import_react3.default.useRef(null);
import_react3.default.useEffect(() => {
if (!!ref.current && !!autoFocus) ref.current.focus();
}, [autoFocus, ref]);
return /* @__PURE__ */ import_react3.default.createElement("div", { className: cn(className) }, /* @__PURE__ */ import_react3.default.createElement(
FormField,
{
control,
name,
render: ({ field }) => /* @__PURE__ */ import_react3.default.createElement(FormItem, null, /* @__PURE__ */ import_react3.default.createElement(FormLabel, null, !disabled && !!required ? /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-red-700" }, "*") : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null), label), /* @__PURE__ */ import_react3.default.createElement(FormControl, null, /* @__PURE__ */ import_react3.default.createElement(
Input,
{
...field,
ref,
placeholder,
type,
disabled,
required: !disabled && !!required,
onFocus: () => onFocusChanged(true),
onBlur: () => onFocusChanged(true)
}
)), !!description ? /* @__PURE__ */ import_react3.default.createElement(FormDescription, null, description) : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null), /* @__PURE__ */ import_react3.default.createElement(FormMessage, null))
}
));
};
var FormxInput_default = FormxInput;
// src/form/validation.ts
var import_zod = require("zod");
var zValid = (label, required, match) => import_zod.z.string(required ? { required_error: `${label} is required!` } : void 0).refine(
(x) => required || x.length > 0 ? !!match ? !!match.type ? (
//for string
match.type === "string" && (!match.eq || x.length === match.eq) && (!match.min || x.length >= (match.min ?? x.length)) && (!match.max || x.length <= (match.max ?? x.length)) || //for number
!isNaN(x) && (!match.min || +x >= (match.min ?? +x)) && (!match.max || +x <= (match.max ?? +x))
) : x.match(match) : true : true,
!!match ? !!match.type ? `${label} ${match.type === "string" ? "length" : ""} should be ${!!match.eq ? `=${match.eq}` : ""} ${!!match.min ? `>=${match.min} ${!!match.max ? `<=${match.max}` : ""}` : ""} !` : `Invalid ${label}!` : ""
);
var regex = {
email: /^([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})$/i,
mobile: /^\+?[1-9]\d{1,14}$/,
url: /^(https?:\/\/)?([\w-]+(\.[\w-]+)+)(\/[\w-]*)*\/?$/,
pan: /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/,
gstin: /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/,
aadhar: /^[2-9]{1}[0-9]{3}[0-9]{4}[0-9]{4}$/,
cin: /^[LU][0-9]{5}[A-Z]{2}[0-9]{4}[A-Z]{3}[0-9]{6}$/,
epic: /^[A-Z]{3}[0-9]{7}$/,
pin: /^[1-9][0-9]{2}\s?[0-9]{3}$/,
password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{6,}$/
};
// src/form/Formx.tsx
var Formx = ({
children,
className,
onSubmit,
onChange,
onInit,
refine,
submitButton,
resetButton
}) => {
const validators = [];
const defaultValues = [];
const childs = [];
import_react4.default.Children.forEach(children, (child) => {
if (import_react4.default.isValidElement(child)) {
childs.push(child);
if (child.type === FormxField) {
validators.push([
child.props.name,
zValid(child.props.label, !!child.props.required, child.props.match)
]);
if (child.props.cached) {
const dv = localStorage.getItem(`cached_${child.props.name}`);
defaultValues.push([
child.props.name,
dv || child.props.defaultValue
]);
} else {
defaultValues.push([child.props.name, child.props.defaultValue]);
}
}
}
});
const validateSchema = import_zod2.z.object(Object.fromEntries(validators));
const refinedSchema = !!refine ? validateSchema.refine((data) => refine.on(data), {
message: refine.message,
path: refine.path
}) : validateSchema;
const defaultSchemaValue = Object.fromEntries(defaultValues);
const form = (0, import_react_hook_form2.useForm)({
resolver: (0, import_zod3.zodResolver)(refinedSchema),
defaultValues: defaultSchemaValue
});
import_react4.default.useEffect(() => {
if (!!onInit) onInit(form);
}, [form]);
const _onSubmit = (data) => {
childs.forEach((c) => {
if (c.props.cached) {
localStorage.setItem(`cached_${c.props.name}`, data[c.props.name]);
}
});
onSubmit(data);
};
const [v, setV] = import_react4.default.useState({
selectedFieldName: ""
});
const values = form.watch();
import_react4.default.useEffect(() => {
if (!!onChange) {
const newV = { ...values, selectedFieldName: v.selectedFieldName };
if (JSON.stringify(v) !== JSON.stringify(newV)) {
setV(newV);
onChange(newV);
}
}
}, [values]);
return /* @__PURE__ */ import_react4.default.createElement(Form, { ...form }, /* @__PURE__ */ import_react4.default.createElement("form", { onSubmit: form.handleSubmit(_onSubmit), className: "space-y-8" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: cn(className) }, childs.map(
(child) => !!child.props.selectProps ? /* @__PURE__ */ import_react4.default.createElement(
FormxSelect_default,
{
key: child.props.name,
control: form.control,
name: child.props.name,
label: child.props.label,
placeholder: child.props.placeholder,
options: child.props.selectProps.options,
className: child.props.className,
onAdd: child.props.selectProps.onAdd,
description: child.props.description,
disabled: child.props.disabled,
required: child.props.required,
autoFocus: child.props.autoFocus,
onFocusChanged: (focused) => {
if (focused) {
setV({ ...v, selectedFieldName: child.props.name });
}
}
}
) : /* @__PURE__ */ import_react4.default.createElement(
FormxInput_default,
{
key: child.props.name,
control: form.control,
name: child.props.name,
label: child.props.label,
placeholder: child.props.placeholder,
className: child.props.className,
type: child.props.type,
description: child.props.description,
disabled: child.props.disabled,
required: child.props.required,
autoFocus: child.props.autoFocus,
onFocusChanged: (focused) => {
if (focused) {
setV({ ...v, selectedFieldName: child.props.name });
}
}
}
)
), submitButton, resetButton)));
};
Formx.displayName = "Formx";
// src/form/util.ts
var reset = (form, defaultValues) => Object.entries(defaultValues).forEach(
([key, value]) => form.setValue(key, `${value}`)
);
function equal(obj1, obj2) {
const areObjects = isObject(obj1) && isObject(obj2);
if (!areObjects) return obj1 === obj2;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) if (!equal(obj1[key], obj2[key])) return false;
return true;
}
function isObject(value) {
return value !== null && typeof value === "object";
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Button,
Formx,
FormxField,
buttonVariants,
equal,
isObject,
regex,
reset
});