shadcn-zod-formkit 1.3.0 → 1.4.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
@@ -115,6 +115,8 @@ const mockFields: Array<FieldProps |FieldProps[]> = [
115
115
  | **Input Time** | `InputTypes.TIME` |
116
116
  | **Upload Multi File** | `InputTypes.FILE_MULTI_UPLOAD` |
117
117
  | **Button Group** | `InputTypes.BUTTON_GROUP` |
118
+ | **Input Currency** | `InputTypes.CURRENCY` |
119
+ | **Input Key Value** | `InputTypes.KEY_VALUE` |
118
120
 
119
121
 
120
122
 
package/dist/index.cjs CHANGED
@@ -263,9 +263,13 @@ var InputTypes = /* @__PURE__ */ ((InputTypes2) => {
263
263
  InputTypes2["FILE_MULTI_UPLOAD"] = "file_multi_upload";
264
264
  InputTypes2["SLIDER"] = "slider";
265
265
  InputTypes2["BUTTON_GROUP"] = "button_group";
266
+ InputTypes2["CURRENCY"] = "currency";
267
+ InputTypes2["KEY_VALUE"] = "key_value";
266
268
  return InputTypes2;
267
269
  })(InputTypes || {});
268
270
  var inputFieldComp = [
271
+ "key_value" /* KEY_VALUE */,
272
+ "currency" /* CURRENCY */,
269
273
  "button_group" /* BUTTON_GROUP */,
270
274
  "slider" /* SLIDER */,
271
275
  "file_multi_upload" /* FILE_MULTI_UPLOAD */,
@@ -1977,6 +1981,124 @@ var ColorComp = React3__namespace.default.forwardRef(
1977
1981
  ] });
1978
1982
  }
1979
1983
  );
1984
+ var CurrencyInput = class extends BaseInput {
1985
+ render() {
1986
+ const { input, form, isSubmitting } = this;
1987
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldCurrency, { input, form, isSubmitting });
1988
+ }
1989
+ };
1990
+ var FieldCurrency = ({ form, input, isSubmitting }) => {
1991
+ const groupConfig = input.inputGroupConfig;
1992
+ input?.infoTooltip;
1993
+ const autoValidate = groupConfig?.autoValidIcons;
1994
+ const iconValidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { style: { color: "#00bf3e" } });
1995
+ const iconInvalidState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleX, { style: { color: "#ff8080" } });
1996
+ const iconLoadingState = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin", style: { color: "#1e90ff" } });
1997
+ const [isValid2, setIsValid] = React3.useState(() => {
1998
+ const value = form.getValues(input.name);
1999
+ const fieldState = form.getFieldState(input.name);
2000
+ return !fieldState.error && value !== void 0 && value !== "";
2001
+ });
2002
+ const defaultCurrencyFormat = {
2003
+ style: "currency",
2004
+ currency: "USD",
2005
+ minimumFractionDigits: 2,
2006
+ maximumFractionDigits: 2
2007
+ };
2008
+ const mask = input?.mask;
2009
+ const currencyFormat = input?.currencyFormat ?? defaultCurrencyFormat;
2010
+ const [rawValue, setRawValue] = React3.useState(form.getValues(input.name) ?? "");
2011
+ const formatter = React3.useMemo(() => {
2012
+ return new Intl.NumberFormat("es-DO", currencyFormat);
2013
+ }, [currencyFormat]);
2014
+ const parseValue = (formatted) => {
2015
+ const numeric = parseFloat(formatted.replace(/[^0-9.-]/g, ""));
2016
+ return isNaN(numeric) ? null : numeric;
2017
+ };
2018
+ const formatValue = (value) => {
2019
+ if (!value) return "";
2020
+ const numeric = parseFloat(value.replace(/[^0-9.-]/g, ""));
2021
+ if (isNaN(numeric)) return "";
2022
+ if (typeof mask === "string") {
2023
+ return mask.replace(/0+(?:[.,]0+)?/, formatter.format(numeric).replace(/[^\d.,]/g, ""));
2024
+ }
2025
+ if (mask instanceof RegExp) {
2026
+ const valid = mask.test(value);
2027
+ return valid ? value : rawValue;
2028
+ }
2029
+ return formatter.format(numeric);
2030
+ };
2031
+ const handleKeyDown = (e) => {
2032
+ const allowedKeys = [
2033
+ "Backspace",
2034
+ "Delete",
2035
+ "Tab",
2036
+ "ArrowLeft",
2037
+ "ArrowRight",
2038
+ "Home",
2039
+ "End"
2040
+ ];
2041
+ if (allowedKeys.includes(e.key)) return;
2042
+ if (!/^[0-9.]$/.test(e.key)) {
2043
+ e.preventDefault();
2044
+ return;
2045
+ }
2046
+ if (e.key === "." && rawValue.includes(".")) {
2047
+ e.preventDefault();
2048
+ }
2049
+ };
2050
+ return /* @__PURE__ */ jsxRuntime.jsx(
2051
+ FormField,
2052
+ {
2053
+ control: form.control,
2054
+ name: input.name,
2055
+ render: ({ field, fieldState }) => {
2056
+ const validNow = !fieldState.error && field.value !== void 0 && field.value !== "";
2057
+ if (validNow !== isValid2) setIsValid(validNow);
2058
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { className: input.className, children: [
2059
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
2060
+ /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { children: [
2061
+ /* @__PURE__ */ jsxRuntime.jsxs(InputGroupAddon, { children: [
2062
+ /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: "$" }),
2063
+ input.inputGroupConfig?.textLeft && /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: input.inputGroupConfig.textLeft })
2064
+ ] }),
2065
+ /* @__PURE__ */ jsxRuntime.jsx(
2066
+ InputGroupInput,
2067
+ {
2068
+ ...field,
2069
+ disabled: input.disabled || isSubmitting,
2070
+ placeholder: input.placeHolder ?? "0.00",
2071
+ inputMode: "decimal",
2072
+ value: rawValue,
2073
+ onKeyDown: handleKeyDown,
2074
+ onChange: (e) => {
2075
+ const newVal = e.target.value;
2076
+ setRawValue(newVal);
2077
+ const parsed = parseValue(newVal);
2078
+ if (parsed !== null) field.onChange(parsed);
2079
+ },
2080
+ onBlur: (e) => {
2081
+ const formatted = formatValue(e.target.value);
2082
+ setRawValue(formatted);
2083
+ },
2084
+ onFocus: (e) => {
2085
+ const numeric = e.target.value.replace(/[^0-9.-]/g, "");
2086
+ setRawValue(numeric);
2087
+ }
2088
+ }
2089
+ ),
2090
+ /* @__PURE__ */ jsxRuntime.jsxs(InputGroupAddon, { align: "inline-end", children: [
2091
+ /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: currencyFormat.currency }),
2092
+ input.inputGroupConfig?.textRight && /* @__PURE__ */ jsxRuntime.jsx(InputGroupText, { children: input.inputGroupConfig.textRight }),
2093
+ autoValidate && /* @__PURE__ */ jsxRuntime.jsx("div", { children: isSubmitting ? iconLoadingState : isValid2 ? iconValidState : iconInvalidState })
2094
+ ] })
2095
+ ] }) }),
2096
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {})
2097
+ ] });
2098
+ }
2099
+ }
2100
+ );
2101
+ };
1980
2102
  var millisecondsInWeek = 6048e5;
1981
2103
  var millisecondsInDay = 864e5;
1982
2104
  var constructFromSymbol = Symbol.for("constructDateFrom");
@@ -3813,6 +3935,113 @@ var FieldFileMultiUpload = ({ input, form, isSubmitting }) => {
3813
3935
  );
3814
3936
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: formField });
3815
3937
  };
3938
+ var KeyValueListInput = class extends BaseInput {
3939
+ render() {
3940
+ const { input, form, isSubmitting } = this;
3941
+ return /* @__PURE__ */ jsxRuntime.jsx(
3942
+ FieldKeyValueList,
3943
+ {
3944
+ input,
3945
+ form,
3946
+ isSubmitting
3947
+ }
3948
+ );
3949
+ }
3950
+ };
3951
+ var FieldKeyValueList = ({ form, input, isSubmitting }) => {
3952
+ const fieldName = input.name;
3953
+ React3.useEffect(() => {
3954
+ const current = form.getValues(fieldName);
3955
+ if (!Array.isArray(current)) {
3956
+ form.setValue(fieldName, []);
3957
+ }
3958
+ }, [form, fieldName]);
3959
+ const handleAddPair = () => {
3960
+ const current = form.getValues(fieldName) || [];
3961
+ form.setValue(fieldName, [...current, { key: "", value: "" }]);
3962
+ };
3963
+ const handleRemovePair = (index) => {
3964
+ const current = form.getValues(fieldName) || [];
3965
+ const updated = current.filter((_, i) => i !== index);
3966
+ form.setValue(fieldName, updated);
3967
+ };
3968
+ const handleChange = (index, fieldType, newValue) => {
3969
+ const current = form.getValues(fieldName) || [];
3970
+ const updated = current.map(
3971
+ (item, i) => i === index ? { ...item, [fieldType]: newValue } : item
3972
+ );
3973
+ form.setValue(fieldName, updated);
3974
+ };
3975
+ return /* @__PURE__ */ jsxRuntime.jsx(
3976
+ FormField,
3977
+ {
3978
+ control: form.control,
3979
+ name: fieldName,
3980
+ render: () => {
3981
+ const pairs = form.watch(fieldName) || [];
3982
+ return /* @__PURE__ */ jsxRuntime.jsxs(FormItem, { className: input.className, children: [
3983
+ /* @__PURE__ */ jsxRuntime.jsx(FormLabel, { children: /* @__PURE__ */ jsxRuntime.jsx("b", { children: input.label }) }),
3984
+ /* @__PURE__ */ jsxRuntime.jsx(FormMessage, {}),
3985
+ /* @__PURE__ */ jsxRuntime.jsx(FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 shadow-lg rounded-xl p-3 bg-white", children: [
3986
+ pairs.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "No pairs have been added yet." }),
3987
+ pairs.map((pair, index) => /* @__PURE__ */ jsxRuntime.jsxs(
3988
+ "div",
3989
+ {
3990
+ className: "flex gap-2 items-center",
3991
+ children: [
3992
+ /* @__PURE__ */ jsxRuntime.jsx(
3993
+ Input,
3994
+ {
3995
+ placeholder: "Key",
3996
+ value: pair.key,
3997
+ disabled: isSubmitting,
3998
+ onChange: (e) => handleChange(index, "key", e.target.value),
3999
+ className: "w-1/2"
4000
+ }
4001
+ ),
4002
+ /* @__PURE__ */ jsxRuntime.jsx(
4003
+ Input,
4004
+ {
4005
+ placeholder: "Value",
4006
+ value: pair.value,
4007
+ disabled: isSubmitting,
4008
+ onChange: (e) => handleChange(index, "value", e.target.value),
4009
+ className: "w-1/2"
4010
+ }
4011
+ ),
4012
+ /* @__PURE__ */ jsxRuntime.jsx(
4013
+ Button,
4014
+ {
4015
+ type: "button",
4016
+ variant: "destructive",
4017
+ size: "icon",
4018
+ onClick: () => handleRemovePair(index),
4019
+ disabled: isSubmitting,
4020
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 18 })
4021
+ }
4022
+ )
4023
+ ]
4024
+ },
4025
+ index
4026
+ )),
4027
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
4028
+ Button,
4029
+ {
4030
+ type: "button",
4031
+ variant: "outline",
4032
+ size: "sm",
4033
+ onClick: handleAddPair,
4034
+ disabled: isSubmitting,
4035
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 18, className: "mr-1" })
4036
+ }
4037
+ ) })
4038
+ ] }) }),
4039
+ input.description && /* @__PURE__ */ jsxRuntime.jsx(FormDescription, { children: input.description })
4040
+ ] });
4041
+ }
4042
+ }
4043
+ );
4044
+ };
3816
4045
  var TextInputGroup = class extends BaseInput {
3817
4046
  render() {
3818
4047
  const { input, form, isSubmitting } = this;
@@ -4513,6 +4742,8 @@ var inputMap = {
4513
4742
  ["time" /* TIME */]: TimeInput,
4514
4743
  ["file_multi_upload" /* FILE_MULTI_UPLOAD */]: FileMultiUploadInput,
4515
4744
  ["button_group" /* BUTTON_GROUP */]: ButtonGroupInput,
4745
+ ["currency" /* CURRENCY */]: CurrencyInput,
4746
+ ["key_value" /* KEY_VALUE */]: KeyValueListInput,
4516
4747
  //ToDos: ============================================================
4517
4748
  ["slider" /* SLIDER */]: SliderInput,
4518
4749
  //ToDo: // PENDIENTE ... VISUALMENTE NO SE VE BIEN.!!!
@@ -4522,7 +4753,6 @@ var inputMap = {
4522
4753
  // [InputTypes.SWITCH_LIST]: SwitchListInput,
4523
4754
  // [InputTypes.RANGE]: TextInput,
4524
4755
  // [InputTypes.MULTISELECT]: TextInput,
4525
- // [InputTypes.CURRENCY]: TextInput,
4526
4756
  // [InputTypes.IMAGE_UPLOAD]: TextInput,
4527
4757
  // [InputTypes.AUDIO_UPLOAD]: TextInput,
4528
4758
  // [InputTypes.VIDEO_UPLOAD]: TextInput,