shadcn-zod-formkit 1.7.0 → 1.9.0

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/README.md CHANGED
@@ -26,7 +26,7 @@ yarn add shadcn-zod-formkit
26
26
  You need installa shadcn basic components
27
27
  ```typescript
28
28
  # Add Shadcn Basics
29
- npx shadcn@latest add accordion alert badge button calendar card checkbox dialog popover form input label select sonner tooltip switch textarea input-otp collapsible input-group radio-group slider button-group
29
+ npx shadcn@latest add accordion alert badge button calendar card checkbox dialog popover form input label select sonner tooltip switch textarea input-otp collapsible input-group radio-group slider button-group command
30
30
  ```
31
31
 
32
32
 
@@ -118,6 +118,7 @@ const mockFields: Array<FieldProps |FieldProps[]> = [
118
118
  | **Input Currency** | `InputTypes.CURRENCY` |
119
119
  | **Input Key Value** | `InputTypes.KEY_VALUE` |
120
120
  | **Input Repeater** | `InputTypes.REPEATER` |
121
+ | **Input Multi Select** | `InputTypes.MULTI_SELECT` |
121
122
 
122
123
 
123
124
 
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ var nextThemes = require('next-themes');
26
26
  var sonner = require('sonner');
27
27
  var SwitchPrimitive = require('@radix-ui/react-switch');
28
28
  var TooltipPrimitive = require('@radix-ui/react-tooltip');
29
+ var cmdk = require('cmdk');
29
30
  var SliderPrimitive = require('@radix-ui/react-slider');
30
31
  var z2 = require('zod');
31
32
  var zod = require('@hookform/resolvers/zod');
@@ -266,9 +267,11 @@ var InputTypes = /* @__PURE__ */ ((InputTypes2) => {
266
267
  InputTypes2["CURRENCY"] = "currency";
267
268
  InputTypes2["KEY_VALUE"] = "key_value";
268
269
  InputTypes2["REPEATER"] = "repeater";
270
+ InputTypes2["MULTI_SELECT"] = "multi_select";
269
271
  return InputTypes2;
270
272
  })(InputTypes || {});
271
273
  var inputFieldComp = [
274
+ "multi_select" /* MULTI_SELECT */,
272
275
  "repeater" /* REPEATER */,
273
276
  "key_value" /* KEY_VALUE */,
274
277
  "currency" /* CURRENCY */,
@@ -4044,6 +4047,201 @@ var FieldKeyValueList = ({ form, input, isSubmitting }) => {
4044
4047
  }
4045
4048
  );
4046
4049
  };
4050
+ function Command({
4051
+ className,
4052
+ ...props
4053
+ }) {
4054
+ return /* @__PURE__ */ jsxRuntime.jsx(
4055
+ cmdk.Command,
4056
+ {
4057
+ "data-slot": "command",
4058
+ className: cn(
4059
+ "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
4060
+ className
4061
+ ),
4062
+ ...props
4063
+ }
4064
+ );
4065
+ }
4066
+ function CommandInput({
4067
+ className,
4068
+ ...props
4069
+ }) {
4070
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4071
+ "div",
4072
+ {
4073
+ "data-slot": "command-input-wrapper",
4074
+ className: "flex h-9 items-center gap-2 border-b px-3",
4075
+ children: [
4076
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
4077
+ /* @__PURE__ */ jsxRuntime.jsx(
4078
+ cmdk.Command.Input,
4079
+ {
4080
+ "data-slot": "command-input",
4081
+ className: cn(
4082
+ "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
4083
+ className
4084
+ ),
4085
+ ...props
4086
+ }
4087
+ )
4088
+ ]
4089
+ }
4090
+ );
4091
+ }
4092
+ function CommandList({
4093
+ className,
4094
+ ...props
4095
+ }) {
4096
+ return /* @__PURE__ */ jsxRuntime.jsx(
4097
+ cmdk.Command.List,
4098
+ {
4099
+ "data-slot": "command-list",
4100
+ className: cn(
4101
+ "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
4102
+ className
4103
+ ),
4104
+ ...props
4105
+ }
4106
+ );
4107
+ }
4108
+ function CommandEmpty({
4109
+ ...props
4110
+ }) {
4111
+ return /* @__PURE__ */ jsxRuntime.jsx(
4112
+ cmdk.Command.Empty,
4113
+ {
4114
+ "data-slot": "command-empty",
4115
+ className: "py-6 text-center text-sm",
4116
+ ...props
4117
+ }
4118
+ );
4119
+ }
4120
+ function CommandGroup({
4121
+ className,
4122
+ ...props
4123
+ }) {
4124
+ return /* @__PURE__ */ jsxRuntime.jsx(
4125
+ cmdk.Command.Group,
4126
+ {
4127
+ "data-slot": "command-group",
4128
+ className: cn(
4129
+ "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
4130
+ className
4131
+ ),
4132
+ ...props
4133
+ }
4134
+ );
4135
+ }
4136
+ function CommandItem({
4137
+ className,
4138
+ ...props
4139
+ }) {
4140
+ return /* @__PURE__ */ jsxRuntime.jsx(
4141
+ cmdk.Command.Item,
4142
+ {
4143
+ "data-slot": "command-item",
4144
+ className: cn(
4145
+ "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
4146
+ className
4147
+ ),
4148
+ ...props
4149
+ }
4150
+ );
4151
+ }
4152
+ var MultiSelectInput = class extends BaseInput {
4153
+ render() {
4154
+ const { input, form, isSubmitting } = this;
4155
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldMultiSelect, { input, form, isSubmitting });
4156
+ }
4157
+ };
4158
+ var FieldMultiSelect = ({ form, input, isSubmitting }) => {
4159
+ const mockInputOptions = [
4160
+ { id: 1, name: "PERMISO 1" },
4161
+ { id: 2, name: "PERMISO 2" },
4162
+ { id: 3, name: "PERMISO 3" },
4163
+ { id: 4, name: "PERMISO 4" }
4164
+ ];
4165
+ const lista = input?.listConfig?.list ?? mockInputOptions;
4166
+ const optionValue = input?.listConfig?.optionValue ?? input.optionValue ?? "id";
4167
+ const getValue = (item) => {
4168
+ if (optionValue === "name") return item[optionValue];
4169
+ return item.value ?? item.id;
4170
+ };
4171
+ const [open, setOpen] = React3.useState(false);
4172
+ return /* @__PURE__ */ jsxRuntime.jsx(
4173
+ FormField,
4174
+ {
4175
+ control: form.control,
4176
+ name: input.name,
4177
+ render: ({ field }) => {
4178
+ const selectedValues = Array.isArray(field.value) ? field.value : [];
4179
+ const toggleOption = (value) => {
4180
+ const newValues = selectedValues.includes(value) ? selectedValues.filter((v) => v !== value) : [...selectedValues, value];
4181
+ field.onChange(newValues);
4182
+ };
4183
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { className: "flex flex-col rounded-lg border p-3 shadow bg-blue-100/20", children: [
4184
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
4185
+ input.description && /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description }),
4186
+ /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
4187
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
4188
+ Button,
4189
+ {
4190
+ variant: "outline",
4191
+ role: "combobox",
4192
+ disabled: input.disabled || isSubmitting,
4193
+ className: cn(
4194
+ "justify-between w-full bg-black/10 dark:bg-white/10",
4195
+ !selectedValues.length && "text-muted-foreground"
4196
+ ),
4197
+ children: [
4198
+ selectedValues.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: selectedValues.map((val) => {
4199
+ const option = lista.find(
4200
+ (item) => getValue(item).toString() === val
4201
+ );
4202
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: option?.name ?? val }, val);
4203
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: input.placeHolder ?? "Selecciona..." }),
4204
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "ml-2 h-4 w-4 opacity-50" })
4205
+ ]
4206
+ }
4207
+ ) }),
4208
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "p-0 w-[var(--radix-popover-trigger-width)]", children: /* @__PURE__ */ jsxRuntime.jsxs(Command, { children: [
4209
+ /* @__PURE__ */ jsxRuntime.jsx(CommandInput, { placeholder: "Buscar..." }),
4210
+ /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { children: [
4211
+ /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { children: "No hay resultados." }),
4212
+ /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { children: lista.map((item) => {
4213
+ const value = getValue(item).toString();
4214
+ const selected = selectedValues.includes(value);
4215
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4216
+ CommandItem,
4217
+ {
4218
+ onSelect: () => toggleOption(value),
4219
+ children: [
4220
+ /* @__PURE__ */ jsxRuntime.jsx(
4221
+ lucideReact.Check,
4222
+ {
4223
+ className: cn(
4224
+ "mr-2 h-4 w-4",
4225
+ selected ? "opacity-100" : "opacity-0"
4226
+ )
4227
+ }
4228
+ ),
4229
+ item.name
4230
+ ]
4231
+ },
4232
+ value
4233
+ );
4234
+ }) })
4235
+ ] })
4236
+ ] }) })
4237
+ ] }) }),
4238
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
4239
+ ] });
4240
+ }
4241
+ },
4242
+ input.name
4243
+ );
4244
+ };
4047
4245
  var TextInputGroup = class extends BaseInput {
4048
4246
  render() {
4049
4247
  const { input, form, isSubmitting } = this;
@@ -4841,6 +5039,7 @@ var inputMap = {
4841
5039
  ["currency" /* CURRENCY */]: CurrencyInput,
4842
5040
  ["key_value" /* KEY_VALUE */]: KeyValueListInput,
4843
5041
  ["repeater" /* REPEATER */]: RepeaterInput,
5042
+ ["multi_select" /* MULTI_SELECT */]: MultiSelectInput,
4844
5043
  //ToDos: ============================================================
4845
5044
  ["slider" /* SLIDER */]: SliderInput,
4846
5045
  //ToDo: // PENDIENTE ... VISUALMENTE NO SE VE BIEN.!!!
@@ -4866,6 +5065,7 @@ var InputFactory = class {
4866
5065
  };
4867
5066
  function getDefaultValues(entity) {
4868
5067
  const defaults = {};
5068
+ if (!entity) return defaults;
4869
5069
  for (const key in entity) {
4870
5070
  const value = entity[key];
4871
5071
  if (value === null || value === void 0) {
@@ -4896,26 +5096,31 @@ function getDefaultValues(entity) {
4896
5096
  return defaults;
4897
5097
  }
4898
5098
  var getDynamicSchema = (fields, extraValidations) => {
4899
- const flatFields = fields.flatMap((f) => Array.isArray(f) ? f : [f]);
4900
- const shape = {};
4901
- flatFields.forEach((f) => {
4902
- shape[f.name] = f.zodType ?? z2__default.default.any();
4903
- });
5099
+ const flatFields = fields.flatMap(
5100
+ (f) => Array.isArray(f) ? f : [f]
5101
+ );
5102
+ const shape = flatFields.reduce((acc, f) => {
5103
+ acc[f.name] = f.zodType ?? z2__default.default.any();
5104
+ return acc;
5105
+ }, {});
4904
5106
  let schema = z2__default.default.object(shape);
4905
- if (extraValidations && extraValidations.length > 0) {
4906
- extraValidations.forEach((fn) => {
5107
+ if (extraValidations?.length) {
5108
+ for (const fn of extraValidations) {
4907
5109
  schema = fn(schema);
4908
- });
5110
+ }
4909
5111
  }
4910
5112
  return schema;
4911
5113
  };
4912
- var FormErrorsAlert = ({ formState, fields }) => {
5114
+ var FormErrorsAlert = ({
5115
+ formState,
5116
+ fields
5117
+ }) => {
4913
5118
  const flatFields = fields.flatMap((f) => Array.isArray(f) ? f : [f]);
4914
5119
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 4 }, children: Object.entries(formState.errors).length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
4915
5120
  CustomAlert,
4916
5121
  {
4917
5122
  title: "Revisar los siguientes criterios",
4918
- description: /* @__PURE__ */ jsxRuntime.jsx("ul", { children: Object.entries(formState?.errors).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
5123
+ description: /* @__PURE__ */ jsxRuntime.jsx("ul", { children: Object.entries(formState.errors).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
4919
5124
  /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
4920
5125
  getFieldLabel(key, flatFields),
4921
5126
  ":"
@@ -4929,15 +5134,36 @@ var FormErrorsAlert = ({ formState, fields }) => {
4929
5134
  ) });
4930
5135
  };
4931
5136
  var getFieldLabel = (fieldErrorKey, fields) => {
4932
- const findedField = fields.find((field) => field.name == fieldErrorKey);
4933
- return findedField?.label ?? fieldErrorKey;
5137
+ const foundField = fields.find((field) => field.name === fieldErrorKey);
5138
+ return foundField?.label ?? fieldErrorKey;
4934
5139
  };
4935
- var FormFieldsGrid = ({ fields, form, isPending, readOnly, className = "", gap = "gap-2" }) => {
4936
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fields.map(
4937
- (input, idx) => Array.isArray(input) ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-full flex flex-row justify-between py-3", children: input.map((field, subIdx) => {
4938
- if (readOnly) field.disabled = readOnly;
4939
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full px-2", children: InputFactory.create(field, form, isPending) }, subIdx);
4940
- }) }, `field-group-${idx}`) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex flex-col justify-between py-3 w-full px-2", children: InputFactory.create(input, form, isPending) }, `field-group-${idx}`)
5140
+ var FormFieldsGrid = ({
5141
+ fields,
5142
+ form,
5143
+ isPending,
5144
+ readOnly,
5145
+ className = "",
5146
+ gap = "gap-2"
5147
+ }) => {
5148
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full grid grid-cols-1 ${gap} ${className}`, children: fields.map(
5149
+ (input, idx) => Array.isArray(input) ? /* @__PURE__ */ jsxRuntime.jsx(
5150
+ "span",
5151
+ {
5152
+ className: "w-full flex flex-row justify-between py-3",
5153
+ children: input.map((field, subIdx) => {
5154
+ if (readOnly) field.disabled = true;
5155
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full px-2", children: InputFactory.create(field, form, isPending) }, subIdx);
5156
+ })
5157
+ },
5158
+ `field-group-${idx}`
5159
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
5160
+ "span",
5161
+ {
5162
+ className: "flex flex-col justify-between py-3 w-full px-2",
5163
+ children: InputFactory.create(input, form, isPending)
5164
+ },
5165
+ `field-group-${idx}`
5166
+ )
4941
5167
  ) });
4942
5168
  };
4943
5169
  var DynamicForm = ({
@@ -4956,14 +5182,15 @@ var DynamicForm = ({
4956
5182
  }) => {
4957
5183
  const [isPending, startTransition] = React3.useTransition();
4958
5184
  const schema = React3.useMemo(() => getDynamicSchema(fields, extraValidations), [fields, extraValidations]);
4959
- const defaultValues = React3.useMemo(() => getDefaultValues(record), [record]);
5185
+ const resolver = zod.zodResolver(schema);
5186
+ const initialValues = React3.useMemo(() => getDefaultValues(record), [record]);
4960
5187
  const form = reactHookForm.useForm({
4961
- resolver: zod.zodResolver(schema),
4962
- defaultValues
5188
+ resolver,
5189
+ defaultValues: initialValues
4963
5190
  });
4964
5191
  React3.useEffect(() => {
4965
- form.reset(defaultValues);
4966
- }, [defaultValues, form]);
5192
+ form.reset(initialValues);
5193
+ }, [initialValues, form]);
4967
5194
  const handleSubmit = (data) => {
4968
5195
  if (readOnly) return;
4969
5196
  try {
@@ -4972,7 +5199,7 @@ var DynamicForm = ({
4972
5199
  onSubmit?.(resp);
4973
5200
  });
4974
5201
  } catch (error) {
4975
- console.error("Ocurri\xF3 un error al enviar el formulario.");
5202
+ console.error("Ocurri\xF3 un error al enviar el formulario.", error);
4976
5203
  }
4977
5204
  };
4978
5205
  const formContent = /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -5000,8 +5227,7 @@ var DynamicForm = ({
5000
5227
  ] }) }) })
5001
5228
  ]
5002
5229
  }
5003
- ) }),
5004
- withErrorsAlert && errorAlertPosition === "down" && /* @__PURE__ */ jsxRuntime.jsx(FormErrorsAlert, { formState: form.formState, fields })
5230
+ ) })
5005
5231
  ] });
5006
5232
  if (!withCard) return formContent;
5007
5233
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: formContent }) });
@@ -5281,6 +5507,7 @@ exports.entityToGroupedOption = entityToGroupedOption;
5281
5507
  exports.entityToInputOption = entityToInputOption;
5282
5508
  exports.getDefaultValues = getDefaultValues;
5283
5509
  exports.getDynamicSchema = getDynamicSchema;
5510
+ exports.getFieldLabel = getFieldLabel;
5284
5511
  exports.inputFieldComp = inputFieldComp;
5285
5512
  exports.mockFields = mockFields;
5286
5513
  exports.useFormField = useFormField;