shadcn-zod-formkit 3.2.0 → 3.3.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
@@ -112,6 +112,8 @@ export default function BasicFormExample() {
112
112
 
113
113
  ## 📚 Available Input Types
114
114
 
115
+ > New in v1.36.0: `Date Range`, `Country Select`, `Range`, plus existing `Location Picker` and `Search` inputs.
116
+
115
117
  | Input Type | Constant | Description |
116
118
  |-------------------------------------|-------------------------------------------|---------------------------------------|
117
119
  | **Text Input** | `InputTypes.TEXT_GROUP` | Text input with icon support |
@@ -126,6 +128,9 @@ export default function BasicFormExample() {
126
128
  | **Date Time Picker** | `InputTypes.DATE_TIME` | Date and time selection |
127
129
  | **Time Picker** | `InputTypes.TIME` | Time selection |
128
130
  | **Location Picker** ✨ | `InputTypes.LOCATION_PICKER` | Interactive map with GPS & geocoding |
131
+ | **Date Range Picker** ✨ | `InputTypes.DATE_RANGE` | Range selection with dual calendar |
132
+ | **Country Select** ✨ | `InputTypes.COUNTRY_SELECT` | Searchable country dropdown with flags|
133
+ | **Range Input** ✨ | `InputTypes.RANGE` | Min/max slider range input |
129
134
  | **Select** | `InputTypes.SELECT` | Dropdown select |
130
135
  | **Multi Select** | `InputTypes.MULTI_SELECT` | Multiple selection dropdown |
131
136
  | **Combobox** | `InputTypes.COMBOBOX` | Searchable select |
package/dist/index.cjs CHANGED
@@ -10002,6 +10002,10 @@ var InputTypes = /* @__PURE__ */ ((InputTypes2) => {
10002
10002
  InputTypes2["EMAIL"] = "email";
10003
10003
  InputTypes2["SEARCH"] = "search";
10004
10004
  InputTypes2["LOCATION_PICKER"] = "location_picker";
10005
+ InputTypes2["DATE_RANGE"] = "date_range";
10006
+ InputTypes2["COUNTRY_SELECT"] = "country_select";
10007
+ InputTypes2["RANGE"] = "range";
10008
+ InputTypes2["FILE_UPLOAD"] = "file_upload";
10005
10009
  return InputTypes2;
10006
10010
  })(InputTypes || {});
10007
10011
  var inputFieldComp = [
@@ -15100,6 +15104,606 @@ function LocationPickerComponent({
15100
15104
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: "\u{1F4A1} Haz click en el mapa para marcar una ubicaci\xF3n o busca una direcci\xF3n." })
15101
15105
  ] });
15102
15106
  }
15107
+ var DateRangeInput = class extends BaseInput {
15108
+ render() {
15109
+ const { input, form, isSubmitting } = this;
15110
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldDateRangeInput, { input, form, isSubmitting });
15111
+ }
15112
+ };
15113
+ var FieldDateRangeInput = ({ form, input, isSubmitting }) => {
15114
+ const [isValid, setIsValid] = React3.useState(isValidField(input, form));
15115
+ const infoTooltip = input?.infoTooltip;
15116
+ const groupConfig = input.inputGroupConfig;
15117
+ const autoValidate = groupConfig?.autoValidIcons ?? input.zodType ? true : false;
15118
+ const iconValidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { style: { color: "#00bf3e" } });
15119
+ const iconInvalidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleX, { style: { color: "#ff8080" } });
15120
+ const iconLoadingState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin", style: { color: "#1e90ff" } });
15121
+ const iconsRight = groupConfig?.iconsRight ?? [];
15122
+ groupConfig?.iconsLeft ?? [];
15123
+ groupConfig?.textLeft;
15124
+ const textRight = groupConfig?.textRight;
15125
+ const formField = /* @__PURE__ */ jsxRuntime.jsx(
15126
+ FormField,
15127
+ {
15128
+ control: form.control,
15129
+ name: input.name,
15130
+ render: ({ field }) => {
15131
+ setIsValid(isValidField(input, form));
15132
+ const [dateRange, setDateRange] = React3__namespace.useState(
15133
+ field.value ? {
15134
+ from: field.value.from ? new Date(field.value.from) : void 0,
15135
+ to: field.value.to ? new Date(field.value.to) : void 0
15136
+ } : void 0
15137
+ );
15138
+ React3__namespace.useEffect(() => {
15139
+ if (field.value && !dateRange) {
15140
+ setDateRange({
15141
+ from: field.value.from ? new Date(field.value.from) : void 0,
15142
+ to: field.value.to ? new Date(field.value.to) : void 0
15143
+ });
15144
+ setIsValid(isValidField(input, form));
15145
+ }
15146
+ }, [field.value]);
15147
+ const formatDateRange = (range) => {
15148
+ if (!range || !range.from) return input.placeHolder ?? "Seleccionar rango de fechas";
15149
+ if (range.to) {
15150
+ return `${dateFns.format(range.from, "LLL dd, y")} - ${dateFns.format(range.to, "LLL dd, y")}`;
15151
+ }
15152
+ return dateFns.format(range.from, "LLL dd, y");
15153
+ };
15154
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { children: [
15155
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
15156
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
15157
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { className: "flex flex-row gap-1", children: [
15158
+ infoTooltip && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
15159
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { size: 20 }) }),
15160
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: infoTooltip }) })
15161
+ ] }),
15162
+ /* @__PURE__ */ jsxRuntime.jsxs(
15163
+ Button,
15164
+ {
15165
+ variant: "outline",
15166
+ type: "button",
15167
+ className: cn(
15168
+ "w-full justify-start text-left py-0.5 ",
15169
+ !dateRange && "text-muted-foreground"
15170
+ ),
15171
+ children: [
15172
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center gap-1 justify-start text-left ", children: [
15173
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarIcon, {}),
15174
+ formatDateRange(dateRange)
15175
+ ] }),
15176
+ (iconsRight.length > 0 || textRight || autoValidate) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15177
+ textRight && /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: textRight }),
15178
+ iconsRight.map((IconComponent, index) => /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 24, className: "w-6! h-6!" }, index)),
15179
+ autoValidate && /* @__PURE__ */ jsxRuntime.jsx("div", { children: isSubmitting ? iconLoadingState : isValid ? iconValidState : iconInvalidState })
15180
+ ] })
15181
+ ]
15182
+ }
15183
+ )
15184
+ ] }) }) }),
15185
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-auto p-0", align: "start" })
15186
+ ] }),
15187
+ /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description }),
15188
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
15189
+ ] });
15190
+ }
15191
+ },
15192
+ input.name
15193
+ );
15194
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: formField });
15195
+ };
15196
+ var countries = [
15197
+ { name: "Afghanistan", code: "AF", flag: "\u{1F1E6}\u{1F1EB}" },
15198
+ { name: "Albania", code: "AL", flag: "\u{1F1E6}\u{1F1F1}" },
15199
+ { name: "Algeria", code: "DZ", flag: "\u{1F1E9}\u{1F1FF}" },
15200
+ { name: "Argentina", code: "AR", flag: "\u{1F1E6}\u{1F1F7}" },
15201
+ { name: "Australia", code: "AU", flag: "\u{1F1E6}\u{1F1FA}" },
15202
+ { name: "Austria", code: "AT", flag: "\u{1F1E6}\u{1F1F9}" },
15203
+ { name: "Bangladesh", code: "BD", flag: "\u{1F1E7}\u{1F1E9}" },
15204
+ { name: "Belgium", code: "BE", flag: "\u{1F1E7}\u{1F1EA}" },
15205
+ { name: "Brazil", code: "BR", flag: "\u{1F1E7}\u{1F1F7}" },
15206
+ { name: "Canada", code: "CA", flag: "\u{1F1E8}\u{1F1E6}" },
15207
+ { name: "Chile", code: "CL", flag: "\u{1F1E8}\u{1F1F1}" },
15208
+ { name: "China", code: "CN", flag: "\u{1F1E8}\u{1F1F3}" },
15209
+ { name: "Colombia", code: "CO", flag: "\u{1F1E8}\u{1F1F4}" },
15210
+ { name: "Denmark", code: "DK", flag: "\u{1F1E9}\u{1F1F0}" },
15211
+ { name: "Egypt", code: "EG", flag: "\u{1F1EA}\u{1F1EC}" },
15212
+ { name: "Finland", code: "FI", flag: "\u{1F1EB}\u{1F1EE}" },
15213
+ { name: "France", code: "FR", flag: "\u{1F1EB}\u{1F1F7}" },
15214
+ { name: "Germany", code: "DE", flag: "\u{1F1E9}\u{1F1EA}" },
15215
+ { name: "Greece", code: "GR", flag: "\u{1F1EC}\u{1F1F7}" },
15216
+ { name: "India", code: "IN", flag: "\u{1F1EE}\u{1F1F3}" },
15217
+ { name: "Indonesia", code: "ID", flag: "\u{1F1EE}\u{1F1E9}" },
15218
+ { name: "Ireland", code: "IE", flag: "\u{1F1EE}\u{1F1EA}" },
15219
+ { name: "Italy", code: "IT", flag: "\u{1F1EE}\u{1F1F9}" },
15220
+ { name: "Japan", code: "JP", flag: "\u{1F1EF}\u{1F1F5}" },
15221
+ { name: "Jordan", code: "JO", flag: "\u{1F1EF}\u{1F1F4}" },
15222
+ { name: "Kenya", code: "KE", flag: "\u{1F1F0}\u{1F1EA}" },
15223
+ { name: "South Korea", code: "KR", flag: "\u{1F1F0}\u{1F1F7}" },
15224
+ { name: "Lebanon", code: "LB", flag: "\u{1F1F1}\u{1F1E7}" },
15225
+ { name: "Malaysia", code: "MY", flag: "\u{1F1F2}\u{1F1FE}" },
15226
+ { name: "Mexico", code: "MX", flag: "\u{1F1F2}\u{1F1FD}" },
15227
+ { name: "Morocco", code: "MA", flag: "\u{1F1F2}\u{1F1E6}" },
15228
+ { name: "Netherlands", code: "NL", flag: "\u{1F1F3}\u{1F1F1}" },
15229
+ { name: "New Zealand", code: "NZ", flag: "\u{1F1F3}\u{1F1FF}" },
15230
+ { name: "Norway", code: "NO", flag: "\u{1F1F3}\u{1F1F4}" },
15231
+ { name: "Pakistan", code: "PK", flag: "\u{1F1F5}\u{1F1F0}" },
15232
+ { name: "Peru", code: "PE", flag: "\u{1F1F5}\u{1F1EA}" },
15233
+ { name: "Philippines", code: "PH", flag: "\u{1F1F5}\u{1F1ED}" },
15234
+ { name: "Poland", code: "PL", flag: "\u{1F1F5}\u{1F1F1}" },
15235
+ { name: "Portugal", code: "PT", flag: "\u{1F1F5}\u{1F1F9}" },
15236
+ { name: "Romania", code: "RO", flag: "\u{1F1F7}\u{1F1F4}" },
15237
+ { name: "Russia", code: "RU", flag: "\u{1F1F7}\u{1F1FA}" },
15238
+ { name: "Saudi Arabia", code: "SA", flag: "\u{1F1F8}\u{1F1E6}" },
15239
+ { name: "Singapore", code: "SG", flag: "\u{1F1F8}\u{1F1EC}" },
15240
+ { name: "South Africa", code: "ZA", flag: "\u{1F1FF}\u{1F1E6}" },
15241
+ { name: "Spain", code: "ES", flag: "\u{1F1EA}\u{1F1F8}" },
15242
+ { name: "Sweden", code: "SE", flag: "\u{1F1F8}\u{1F1EA}" },
15243
+ { name: "Switzerland", code: "CH", flag: "\u{1F1E8}\u{1F1ED}" },
15244
+ { name: "Thailand", code: "TH", flag: "\u{1F1F9}\u{1F1ED}" },
15245
+ { name: "Turkey", code: "TR", flag: "\u{1F1F9}\u{1F1F7}" },
15246
+ { name: "Ukraine", code: "UA", flag: "\u{1F1FA}\u{1F1E6}" },
15247
+ { name: "United Arab Emirates", code: "AE", flag: "\u{1F1E6}\u{1F1EA}" },
15248
+ { name: "United Kingdom", code: "GB", flag: "\u{1F1EC}\u{1F1E7}" },
15249
+ { name: "United States", code: "US", flag: "\u{1F1FA}\u{1F1F8}" },
15250
+ { name: "Vietnam", code: "VN", flag: "\u{1F1FB}\u{1F1F3}" }
15251
+ ];
15252
+ var CountrySelectInput = class extends BaseInput {
15253
+ render() {
15254
+ const { input, form, isSubmitting } = this;
15255
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldCountrySelectInput, { input, form, isSubmitting });
15256
+ }
15257
+ };
15258
+ var FieldCountrySelectInput = ({ form, input, isSubmitting }) => {
15259
+ const [isValid, setIsValid] = React3.useState(isValidField(input, form));
15260
+ const infoTooltip = input?.infoTooltip;
15261
+ const groupConfig = input.inputGroupConfig;
15262
+ const autoValidate = groupConfig?.autoValidIcons ?? input.zodType ? true : false;
15263
+ const iconValidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { color: "#00bf3e" } });
15264
+ const iconInvalidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { color: "#ff8080" } });
15265
+ const iconsRight = groupConfig?.iconsRight ?? [];
15266
+ groupConfig?.iconsLeft ?? [];
15267
+ groupConfig?.textLeft;
15268
+ const textRight = groupConfig?.textRight;
15269
+ const formField = /* @__PURE__ */ jsxRuntime.jsx(
15270
+ FormField,
15271
+ {
15272
+ control: form.control,
15273
+ name: input.name,
15274
+ render: ({ field }) => {
15275
+ setIsValid(isValidField(input, form));
15276
+ const [open, setOpen] = React3__namespace.useState(false);
15277
+ const [value, setValue] = React3__namespace.useState(field.value || "");
15278
+ const selectedCountry = countries.find((country) => country.code === value);
15279
+ const handleSelect = (countryCode) => {
15280
+ setValue(countryCode);
15281
+ setOpen(false);
15282
+ handleOnChage(countryCode, input, field);
15283
+ };
15284
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { children: [
15285
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
15286
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
15287
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { className: "flex flex-row gap-1", children: [
15288
+ infoTooltip && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
15289
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { size: 20 }) }),
15290
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: infoTooltip }) })
15291
+ ] }),
15292
+ /* @__PURE__ */ jsxRuntime.jsxs(
15293
+ Button,
15294
+ {
15295
+ type: "button",
15296
+ variant: "outline",
15297
+ role: "combobox",
15298
+ "aria-expanded": open,
15299
+ className: "w-full justify-between",
15300
+ children: [
15301
+ selectedCountry ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
15302
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: selectedCountry.flag }),
15303
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: selectedCountry.name }),
15304
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
15305
+ "(",
15306
+ selectedCountry.code,
15307
+ ")"
15308
+ ] })
15309
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: input.placeHolder ?? "Select country..." }),
15310
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
15311
+ ]
15312
+ }
15313
+ ),
15314
+ (iconsRight.length > 0 || textRight || autoValidate) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15315
+ textRight && /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: textRight }),
15316
+ iconsRight.map((IconComponent, index) => /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 24, className: "w-6! h-6!" }, index)),
15317
+ autoValidate && /* @__PURE__ */ jsxRuntime.jsx("div", { children: isValid ? iconValidState : iconInvalidState })
15318
+ ] })
15319
+ ] }) }) }),
15320
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[400px] p-0", children: /* @__PURE__ */ jsxRuntime.jsxs(Command, { children: [
15321
+ /* @__PURE__ */ jsxRuntime.jsx(CommandInput, { placeholder: "Search countries..." }),
15322
+ /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { children: [
15323
+ /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { children: "No country found." }),
15324
+ /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { children: countries.map((country) => /* @__PURE__ */ jsxRuntime.jsxs(
15325
+ CommandItem,
15326
+ {
15327
+ value: `${country.name} ${country.code}`,
15328
+ onSelect: () => handleSelect(country.code),
15329
+ children: [
15330
+ /* @__PURE__ */ jsxRuntime.jsx(
15331
+ lucideReact.Check,
15332
+ {
15333
+ className: cn(
15334
+ "mr-2 h-4 w-4",
15335
+ value === country.code ? "opacity-100" : "opacity-0"
15336
+ )
15337
+ }
15338
+ ),
15339
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-2", children: country.flag }),
15340
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: country.name }),
15341
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto text-muted-foreground", children: [
15342
+ "(",
15343
+ country.code,
15344
+ ")"
15345
+ ] })
15346
+ ]
15347
+ },
15348
+ country.code
15349
+ )) })
15350
+ ] })
15351
+ ] }) })
15352
+ ] }),
15353
+ /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description }),
15354
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
15355
+ ] });
15356
+ }
15357
+ },
15358
+ input.name
15359
+ );
15360
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: formField });
15361
+ };
15362
+ var RangeInput = class extends BaseInput {
15363
+ render() {
15364
+ const { input, form, isSubmitting } = this;
15365
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldRange, { input, form, isSubmitting });
15366
+ }
15367
+ };
15368
+ var FieldRange = ({ input, form, isSubmitting }) => {
15369
+ const [isValid, setIsValid] = React3.useState(isValidField(input, form));
15370
+ const groupConfig = input.inputGroupConfig;
15371
+ const autoValidate = groupConfig?.autoValidIcons ?? input.zodType ? true : false;
15372
+ const iconValidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { style: { color: "#00bf3e" } });
15373
+ const iconInvalidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleX, { style: { color: "#ff8080" } });
15374
+ const iconLoadingState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin", style: { color: "#1e90ff" } });
15375
+ const iconsRight = groupConfig?.iconsRight ?? [];
15376
+ const textRight = groupConfig?.textRight;
15377
+ const minValue = input.min ?? 0;
15378
+ const maxValue = input.max ?? 100;
15379
+ const step = input.step ?? 1;
15380
+ const initialValue = form.getValues(input.name) ?? input.value ?? [minValue, maxValue];
15381
+ const [value, setValue] = React3.useState(Array.isArray(initialValue) ? initialValue : [minValue, maxValue]);
15382
+ const handleChange = (val) => {
15383
+ setValue(val);
15384
+ ({ min: val[0], max: val[1] });
15385
+ };
15386
+ return /* @__PURE__ */ jsxRuntime.jsx(
15387
+ FormField,
15388
+ {
15389
+ control: form.control,
15390
+ name: input.name,
15391
+ render: ({ field, fieldState }) => {
15392
+ setIsValid(isValidField(input, form));
15393
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { className: input.className, children: [
15394
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
15395
+ /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
15396
+ /* @__PURE__ */ jsxRuntime.jsx(
15397
+ Slider,
15398
+ {
15399
+ value,
15400
+ onValueChange: handleChange,
15401
+ max: maxValue,
15402
+ min: minValue,
15403
+ step,
15404
+ className: "w-full"
15405
+ }
15406
+ ),
15407
+ /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { className: "flex flex-row gap-1 justify-center", children: [
15408
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
15409
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Min:" }),
15410
+ /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { className: "min-w-[60px] text-center", children: value[0] })
15411
+ ] }),
15412
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
15413
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Max:" }),
15414
+ /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { className: "min-w-[60px] text-center", children: value[1] })
15415
+ ] }),
15416
+ (iconsRight.length > 0 || textRight || autoValidate) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15417
+ textRight && /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: textRight }),
15418
+ iconsRight.map((IconComponent, index) => /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 24, className: "w-6! h-6!" }, index)),
15419
+ autoValidate && /* @__PURE__ */ jsxRuntime.jsx("div", { children: isSubmitting ? iconLoadingState : isValid ? iconValidState : iconInvalidState })
15420
+ ] })
15421
+ ] })
15422
+ ] }) }),
15423
+ /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description }),
15424
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
15425
+ ] });
15426
+ }
15427
+ },
15428
+ input.name
15429
+ );
15430
+ };
15431
+ var FileUploadInput = class extends BaseInput {
15432
+ render() {
15433
+ const { input, form, isSubmitting } = this;
15434
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldFileUpload, { input, form, isSubmitting });
15435
+ }
15436
+ };
15437
+ var FieldFileUpload = ({ input, form, isSubmitting }) => {
15438
+ const inputRef = React3.useRef(null);
15439
+ const [file, setFile] = React3.useState(form.getValues(input.name) || null);
15440
+ const [dragOver, setDragOver] = React3.useState(false);
15441
+ const [uploading, setUploading] = React3.useState(false);
15442
+ const [uploadProgress, setUploadProgress] = React3.useState(0);
15443
+ const [previewUrl, setPreviewUrl] = React3.useState(null);
15444
+ const config = input.fileConfig;
15445
+ const {
15446
+ dragAndDrop = true,
15447
+ progressBar = true,
15448
+ uploadUrl,
15449
+ onUploadProgress,
15450
+ onUploadComplete,
15451
+ previewFormats = { image: true, video: false, audio: false, pdf: false },
15452
+ maxSize = 10 * 1024 * 1024,
15453
+ // 10MB default
15454
+ acceptedFormats = ["*"]
15455
+ } = config || {};
15456
+ const autoValidate = input.inputGroupConfig?.autoValidIcons;
15457
+ const iconValidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { style: { color: "#00bf3e" } });
15458
+ const iconInvalidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleX, { style: { color: "#ff8080" } });
15459
+ const generatePreview = (fileData) => {
15460
+ const type = fileData.type;
15461
+ if (previewFormats.image && type.startsWith("image/")) {
15462
+ const reader = new FileReader();
15463
+ reader.onload = (e) => {
15464
+ setPreviewUrl(e.target?.result);
15465
+ };
15466
+ if (fileData.file) reader.readAsDataURL(fileData.file);
15467
+ } else if (previewFormats.video && type.startsWith("video/")) {
15468
+ const reader = new FileReader();
15469
+ reader.onload = (e) => {
15470
+ setPreviewUrl(e.target?.result);
15471
+ };
15472
+ if (fileData.file) reader.readAsDataURL(fileData.file);
15473
+ } else if (previewFormats.audio && type.startsWith("audio/")) {
15474
+ setPreviewUrl("audio");
15475
+ } else if (previewFormats.pdf && type === "application/pdf") {
15476
+ setPreviewUrl("pdf");
15477
+ }
15478
+ };
15479
+ const handleFileSelect = async (selectedFile) => {
15480
+ if (selectedFile.size > maxSize) {
15481
+ form.setError(input.name, {
15482
+ type: "manual",
15483
+ message: `File size exceeds ${(maxSize / 1024 / 1024).toFixed(0)}MB limit`
15484
+ });
15485
+ return;
15486
+ }
15487
+ const fileData = {
15488
+ name: selectedFile.name,
15489
+ size: selectedFile.size,
15490
+ type: selectedFile.type,
15491
+ lastModified: selectedFile.lastModified,
15492
+ file: selectedFile,
15493
+ uploadProgress: 0
15494
+ };
15495
+ setFile(fileData);
15496
+ form.setValue(input.name, fileData);
15497
+ generatePreview(fileData);
15498
+ if (uploadUrl) {
15499
+ await handleUpload(fileData);
15500
+ }
15501
+ };
15502
+ const handleUpload = async (fileData) => {
15503
+ if (!uploadUrl || !fileData.file) return;
15504
+ setUploading(true);
15505
+ setUploadProgress(0);
15506
+ try {
15507
+ const formData = new FormData();
15508
+ formData.append("file", fileData.file);
15509
+ const xhr = new XMLHttpRequest();
15510
+ xhr.upload.addEventListener("progress", (e) => {
15511
+ if (e.lengthComputable) {
15512
+ const percentComplete = e.loaded / e.total * 100;
15513
+ setUploadProgress(percentComplete);
15514
+ onUploadProgress?.(percentComplete);
15515
+ }
15516
+ });
15517
+ xhr.addEventListener("load", () => {
15518
+ if (xhr.status === 200) {
15519
+ try {
15520
+ const response = JSON.parse(xhr.responseText);
15521
+ fileData.uploadedUrl = response.url || response.data?.url;
15522
+ fileData.uploadProgress = 100;
15523
+ setUploadProgress(100);
15524
+ setFile({ ...fileData });
15525
+ form.setValue(input.name, fileData);
15526
+ onUploadComplete?.(response);
15527
+ } catch (e) {
15528
+ console.error("Failed to parse upload response", e);
15529
+ }
15530
+ }
15531
+ });
15532
+ xhr.addEventListener("error", () => {
15533
+ form.setError(input.name, {
15534
+ type: "manual",
15535
+ message: "Upload failed. Please try again."
15536
+ });
15537
+ setUploading(false);
15538
+ });
15539
+ xhr.open("POST", uploadUrl);
15540
+ xhr.send(formData);
15541
+ } catch (error) {
15542
+ form.setError(input.name, {
15543
+ type: "manual",
15544
+ message: "Upload error. Please try again."
15545
+ });
15546
+ setUploading(false);
15547
+ }
15548
+ };
15549
+ const handleInputChange = (event) => {
15550
+ const selectedFile = event.target.files?.[0];
15551
+ if (selectedFile) {
15552
+ handleFileSelect(selectedFile);
15553
+ }
15554
+ };
15555
+ const handleDrop = (event) => {
15556
+ if (!dragAndDrop) return;
15557
+ event.preventDefault();
15558
+ setDragOver(false);
15559
+ const droppedFile = event.dataTransfer.files?.[0];
15560
+ if (droppedFile) {
15561
+ handleFileSelect(droppedFile);
15562
+ }
15563
+ };
15564
+ const handleDragOver = (event) => {
15565
+ if (!dragAndDrop) return;
15566
+ event.preventDefault();
15567
+ setDragOver(true);
15568
+ };
15569
+ const handleDragLeave = () => {
15570
+ setDragOver(false);
15571
+ };
15572
+ const handleRemove = () => {
15573
+ setFile(null);
15574
+ setPreviewUrl(null);
15575
+ setUploadProgress(0);
15576
+ form.setValue(input.name, null);
15577
+ if (inputRef.current) {
15578
+ inputRef.current.value = "";
15579
+ }
15580
+ };
15581
+ const getFileIcon = () => {
15582
+ if (!file) return null;
15583
+ const type = file.type;
15584
+ if (type.startsWith("image/")) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { className: "w-8 h-8" });
15585
+ if (type.startsWith("video/")) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { className: "w-8 h-8" });
15586
+ if (type.startsWith("audio/")) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Music, { className: "w-8 h-8" });
15587
+ if (type === "application/pdf") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "w-8 h-8" });
15588
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { className: "w-8 h-8" });
15589
+ };
15590
+ const formatFileSize = (bytes) => {
15591
+ if (bytes === 0) return "0 Bytes";
15592
+ const k = 1024;
15593
+ const sizes = ["Bytes", "KB", "MB", "GB"];
15594
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
15595
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
15596
+ };
15597
+ return /* @__PURE__ */ jsxRuntime.jsx(
15598
+ FormField,
15599
+ {
15600
+ control: form.control,
15601
+ name: input.name,
15602
+ render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { children: [
15603
+ /* @__PURE__ */ jsxRuntime.jsxs(FormLabel, { htmlFor: input.name, children: [
15604
+ input.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 mr-1", children: "*" }),
15605
+ input.label
15606
+ ] }),
15607
+ /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
15608
+ !file || !file.uploadedUrl ? /* @__PURE__ */ jsxRuntime.jsxs(
15609
+ "div",
15610
+ {
15611
+ onDrop: handleDrop,
15612
+ onDragOver: handleDragOver,
15613
+ onDragLeave: handleDragLeave,
15614
+ className: cn(
15615
+ "relative border-2 border-dashed rounded-lg p-8 transition-colors",
15616
+ dragAndDrop ? "cursor-pointer" : "",
15617
+ dragOver ? "border-blue-500 bg-blue-50" : "border-gray-300 hover:border-gray-400",
15618
+ uploading ? "opacity-50 pointer-events-none" : ""
15619
+ ),
15620
+ children: [
15621
+ /* @__PURE__ */ jsxRuntime.jsx(
15622
+ "input",
15623
+ {
15624
+ ref: inputRef,
15625
+ type: "file",
15626
+ onChange: handleInputChange,
15627
+ className: "hidden",
15628
+ id: input.name,
15629
+ disabled: uploading || isSubmitting,
15630
+ accept: acceptedFormats.join(",")
15631
+ }
15632
+ ),
15633
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center justify-center gap-3", children: uploading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15634
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-8 h-8 animate-spin text-blue-500" }),
15635
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: "Uploading..." })
15636
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15637
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-8 h-8 text-gray-400" }),
15638
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
15639
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900", children: dragAndDrop ? "Drag and drop your file here, or click to select" : "Click to select a file" }),
15640
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1", children: [
15641
+ "Max size: ",
15642
+ formatFileSize(maxSize)
15643
+ ] })
15644
+ ] }),
15645
+ /* @__PURE__ */ jsxRuntime.jsx(
15646
+ "button",
15647
+ {
15648
+ type: "button",
15649
+ onClick: () => inputRef.current?.click(),
15650
+ className: "mt-2 px-4 py-2 bg-blue-500 text-white text-sm rounded-md hover:bg-blue-600 transition-colors",
15651
+ children: "Select File"
15652
+ }
15653
+ )
15654
+ ] }) })
15655
+ ]
15656
+ }
15657
+ ) : null,
15658
+ file && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border rounded-lg p-4 bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
15659
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: previewUrl && previewUrl !== "audio" && previewUrl !== "pdf" ? /* @__PURE__ */ jsxRuntime.jsx(
15660
+ "img",
15661
+ {
15662
+ src: previewUrl,
15663
+ alt: "preview",
15664
+ className: "w-16 h-16 object-cover rounded-md"
15665
+ }
15666
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 bg-gray-200 rounded-md flex items-center justify-center text-gray-400", children: getFileIcon() }) }),
15667
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
15668
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900 truncate", children: file.name }),
15669
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: formatFileSize(file.size) }),
15670
+ progressBar && file.uploadProgress !== void 0 && file.uploadProgress < 100 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3", children: [
15671
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-2", children: /* @__PURE__ */ jsxRuntime.jsx(
15672
+ "div",
15673
+ {
15674
+ className: "bg-blue-500 h-2 rounded-full transition-all duration-300",
15675
+ style: { width: `${file.uploadProgress}%` }
15676
+ }
15677
+ ) }),
15678
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1", children: [
15679
+ Math.round(file.uploadProgress),
15680
+ "% uploaded"
15681
+ ] })
15682
+ ] }),
15683
+ file.uploadedUrl && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
15684
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { className: "w-4 h-4 text-green-500" }),
15685
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-green-600", children: "Upload complete" })
15686
+ ] })
15687
+ ] }),
15688
+ !uploading && /* @__PURE__ */ jsxRuntime.jsx(
15689
+ "button",
15690
+ {
15691
+ type: "button",
15692
+ onClick: handleRemove,
15693
+ className: "shrink-0 p-1 text-gray-400 hover:text-gray-600 rounded-md hover:bg-gray-200 transition-colors",
15694
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
15695
+ }
15696
+ )
15697
+ ] }) }),
15698
+ autoValidate && form.formState.errors[input.name] && iconInvalidState,
15699
+ autoValidate && !form.formState.errors[input.name] && file && iconValidState
15700
+ ] }) }),
15701
+ input.description && /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description }),
15702
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
15703
+ ] })
15704
+ }
15705
+ );
15706
+ };
15103
15707
  var inputMap = {
15104
15708
  ["text_group" /* TEXT_GROUP */]: TextInputGroup,
15105
15709
  ["text" /* TEXT */]: TextInput,
@@ -15138,8 +15742,13 @@ var inputMap = {
15138
15742
  // ✨ New input types (v1.36.0)
15139
15743
  ["email" /* EMAIL */]: EmailInput,
15140
15744
  ["search" /* SEARCH */]: TextInput,
15141
- // TODO: Implement SearchInput
15745
+ // SearchInput component is currently a function component
15142
15746
  ["location_picker" /* LOCATION_PICKER */]: LocationPickerInput,
15747
+ ["date_range" /* DATE_RANGE */]: DateRangeInput,
15748
+ ["country_select" /* COUNTRY_SELECT */]: CountrySelectInput,
15749
+ ["range" /* RANGE */]: RangeInput,
15750
+ // ✨ New input types (v1.37.0)
15751
+ ["file_upload" /* FILE_UPLOAD */]: FileUploadInput,
15143
15752
  //ToDos: ============================================================
15144
15753
  ["slider" /* SLIDER */]: SliderInput,
15145
15754
  //ToDo: // PENDIENTE ... VISUALMENTE NO SE VE BIEN.!!!