@zauru-sdk/components 1.0.12 → 1.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/Alerts/index.d.ts +0 -1
  3. package/dist/Alerts/index.js +0 -1
  4. package/dist/Chat/ChatLayout.d.ts +2 -2
  5. package/dist/Chat/ChatLayout.js +4 -3
  6. package/dist/Chat/ChatMessageHistory.js +1 -1
  7. package/dist/ConnectionState/ConnectionState.d.ts +2 -0
  8. package/dist/ConnectionState/ConnectionState.js +22 -0
  9. package/dist/ConnectionState/index.d.ts +1 -0
  10. package/dist/ConnectionState/index.js +1 -0
  11. package/dist/DynamicTable/BasicPrintDynamicTable.d.ts +10 -0
  12. package/dist/DynamicTable/BasicPrintDynamicTable.js +27 -0
  13. package/dist/DynamicTable/DynamicPrintTable.d.ts +23 -0
  14. package/dist/DynamicTable/DynamicPrintTable.js +132 -0
  15. package/dist/DynamicTable/GenericDynamicTable.d.ts +21 -0
  16. package/dist/DynamicTable/GenericDynamicTable.js +195 -0
  17. package/dist/DynamicTable/index.d.ts +24 -0
  18. package/dist/DynamicTable/index.js +193 -0
  19. package/dist/Footer/Footer.js +2 -2
  20. package/dist/Form/Checkbox/index.d.ts +17 -0
  21. package/dist/Form/Checkbox/index.js +34 -0
  22. package/dist/Form/Checklist/index.d.ts +14 -0
  23. package/dist/Form/Checklist/index.js +10 -0
  24. package/dist/Form/DatePicker/index.d.ts +18 -0
  25. package/dist/Form/DatePicker/index.js +31 -0
  26. package/dist/Form/DynamicBaculoForm/index.d.ts +18 -0
  27. package/dist/Form/DynamicBaculoForm/index.js +138 -0
  28. package/dist/Form/FieldContainer/DoubleFieldContainer.d.ts +8 -0
  29. package/dist/Form/FieldContainer/DoubleFieldContainer.js +14 -0
  30. package/dist/Form/FieldContainer/QuadrupleFieldContainer.d.ts +7 -0
  31. package/dist/Form/FieldContainer/QuadrupleFieldContainer.js +14 -0
  32. package/dist/Form/FieldContainer/TripleFieldContainer.d.ts +7 -0
  33. package/dist/Form/FieldContainer/TripleFieldContainer.js +14 -0
  34. package/dist/Form/FieldContainer/index.d.ts +3 -0
  35. package/dist/Form/FieldContainer/index.js +3 -0
  36. package/dist/Form/FileUpload/index.d.ts +21 -0
  37. package/dist/Form/FileUpload/index.js +54 -0
  38. package/dist/Form/FormButtons/index.d.ts +16 -0
  39. package/dist/Form/FormButtons/index.js +5 -0
  40. package/dist/Form/FormLayout/index.d.ts +11 -0
  41. package/dist/Form/FormLayout/index.js +7 -0
  42. package/dist/Form/SelectField/index.d.ts +27 -0
  43. package/dist/Form/SelectField/index.js +74 -0
  44. package/dist/Form/TextArea/index.d.ts +23 -0
  45. package/dist/Form/TextArea/index.js +36 -0
  46. package/dist/Form/TextField/index.d.ts +25 -0
  47. package/dist/Form/TextField/index.js +70 -0
  48. package/dist/Form/TimePicker/index.d.ts +16 -0
  49. package/dist/Form/TimePicker/index.js +31 -0
  50. package/dist/Form/YesNo/index.d.ts +12 -0
  51. package/dist/Form/YesNo/index.js +19 -0
  52. package/dist/Form/index.d.ts +13 -0
  53. package/dist/Form/index.js +13 -0
  54. package/dist/NavBar/NavBar.js +2 -2
  55. package/dist/Table/ZauruTable.js +1 -1
  56. package/dist/Zendesk/Chat.d.ts +2 -2
  57. package/dist/Zendesk/Chat.js +2 -1
  58. package/dist/Zendesk/zendesk.config.d.ts +1 -1
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +4 -2
  61. package/package.json +9 -8
  62. package/src/Alerts/index.ts +0 -1
  63. package/src/Chat/ChatLayout.tsx +133 -0
  64. package/src/Chat/ChatMessageHistory.tsx +142 -0
  65. package/src/Chat/index.ts +2 -0
  66. package/src/ConnectionState/ConnectionState.tsx +29 -0
  67. package/src/ConnectionState/index.tsx +1 -0
  68. package/src/DynamicTable/BasicPrintDynamicTable.tsx +73 -0
  69. package/src/DynamicTable/DynamicPrintTable.tsx +290 -0
  70. package/src/DynamicTable/GenericDynamicTable.tsx +455 -0
  71. package/src/DynamicTable/index.tsx +407 -0
  72. package/src/Footer/Footer.tsx +3 -3
  73. package/src/Form/Checkbox/index.tsx +96 -0
  74. package/src/Form/Checklist/index.tsx +35 -0
  75. package/src/Form/DatePicker/index.tsx +132 -0
  76. package/src/Form/DynamicBaculoForm/index.tsx +359 -0
  77. package/src/Form/FieldContainer/DoubleFieldContainer.tsx +35 -0
  78. package/src/Form/FieldContainer/QuadrupleFieldContainer.tsx +36 -0
  79. package/src/Form/FieldContainer/TripleFieldContainer.tsx +35 -0
  80. package/src/Form/FieldContainer/index.ts +3 -0
  81. package/src/Form/FileUpload/index.tsx +184 -0
  82. package/src/Form/FormButtons/index.tsx +78 -0
  83. package/src/Form/FormLayout/index.tsx +37 -0
  84. package/src/Form/SelectField/index.tsx +237 -0
  85. package/src/Form/TextArea/index.tsx +125 -0
  86. package/src/Form/TextField/index.tsx +194 -0
  87. package/src/Form/TimePicker/index.tsx +127 -0
  88. package/src/Form/YesNo/index.tsx +79 -0
  89. package/src/Form/index.ts +13 -0
  90. package/src/NavBar/NavBar.tsx +2 -2
  91. package/src/Table/ZauruTable.tsx +1 -1
  92. package/src/Zendesk/Chat.tsx +85 -0
  93. package/src/Zendesk/index.ts +2 -0
  94. package/src/Zendesk/zendesk.config.ts +40 -0
  95. package/src/index.ts +4 -2
  96. package/dist/Alerts/Alert.d.ts +0 -9
  97. package/dist/Alerts/Alert.js +0 -97
  98. package/dist/Icons/Icons.d.ts +0 -47
  99. package/dist/Icons/Icons.js +0 -110
  100. package/dist/Icons/StylesConstants.d.ts +0 -26
  101. package/dist/Icons/StylesConstants.js +0 -34
  102. package/src/Alerts/Alert.tsx +0 -149
  103. package/src/Icons/Icons.tsx +0 -782
  104. package/src/Icons/StylesConstants.tsx +0 -66
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+
3
+ type Props = {
4
+ //Guardar
5
+ saveTitle?: string;
6
+ saveName?: string;
7
+ onClickSave?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
8
+ //Cancelar
9
+ showCancel?: boolean;
10
+ cancelTitle?: string;
11
+ cancelName?: string;
12
+ //Limpiar
13
+ showClear?: boolean;
14
+ clearTitle?: string;
15
+ clearName?: string;
16
+ //Cargando...
17
+ loading?: boolean;
18
+ loadingSaveText?: string;
19
+ };
20
+
21
+ export const FormButtons = (props: Props) => {
22
+ const {
23
+ saveTitle = "Guardar",
24
+ saveName = "save",
25
+ cancelTitle = "Cancelar",
26
+ cancelName = "cancel",
27
+ showCancel = true,
28
+ showClear = false,
29
+ clearName = "clear",
30
+ clearTitle = "Limpiar",
31
+ loading = false,
32
+ onClickSave,
33
+ loadingSaveText = "Guardando...",
34
+ } = props;
35
+
36
+ return (
37
+ <>
38
+ {showClear && (
39
+ <button
40
+ type="reset"
41
+ name="action"
42
+ disabled={loading}
43
+ value={clearName}
44
+ className={`ml-5 ${
45
+ loading ? " bg-opacity-25 cursor-progress" : ""
46
+ } rounded-md border border-gray-300 bg-white py-2 px-3 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2`}
47
+ >
48
+ {clearTitle}
49
+ </button>
50
+ )}
51
+ {showCancel && (
52
+ <button
53
+ type="button"
54
+ name="action"
55
+ disabled={loading}
56
+ value={cancelName}
57
+ className={`${showClear ? "ml-2" : "ml-5"} ${
58
+ loading ? " bg-opacity-25 cursor-progress" : ""
59
+ } rounded-md border border-gray-300 bg-white py-2 px-3 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2`}
60
+ >
61
+ {cancelTitle}
62
+ </button>
63
+ )}
64
+ <button
65
+ type="submit"
66
+ name="action"
67
+ disabled={loading}
68
+ value={saveName}
69
+ onClick={onClickSave}
70
+ className={`ml-2 ${
71
+ loading ? " bg-opacity-25 cursor-progress" : "hover:bg-indigo-700"
72
+ } inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2`}
73
+ >
74
+ {loading ? loadingSaveText : saveTitle}
75
+ </button>
76
+ </>
77
+ );
78
+ };
@@ -0,0 +1,37 @@
1
+ import { Form, type FormMethod } from "@remix-run/react";
2
+ import { type ReactNode } from "react";
3
+ import { ButtonSectionContainer } from "src";
4
+
5
+ type Props = {
6
+ formId: string;
7
+ title?: string;
8
+ children: ReactNode;
9
+ buttons?: ReactNode;
10
+ method?: FormMethod;
11
+ };
12
+
13
+ export const FormLayout = (props: Props) => {
14
+ const { title, children, buttons, method, formId } = props;
15
+
16
+ return (
17
+ <Form id={formId} name={formId} method={method ?? "post"} key={formId}>
18
+ {title && (
19
+ <label className="block text-sm font-medium text-gray-700 mb-3">
20
+ {title}
21
+ </label>
22
+ )}
23
+ <div className="shadow sm:overflow-hidden sm:rounded-md">
24
+ <div className="space-y-6 bg-white px-4 py-5 sm:p-6">
25
+ {/* AQUÍ VAN EL CONTENIDO DEL FORM */}
26
+ {children}
27
+ </div>
28
+ {buttons && (
29
+ <ButtonSectionContainer>
30
+ {/* AQUÍ VAN LOS BOTONES */}
31
+ {buttons}
32
+ </ButtonSectionContainer>
33
+ )}
34
+ </div>
35
+ </Form>
36
+ );
37
+ };
@@ -0,0 +1,237 @@
1
+ import { IdeaIconSVG } from "@zauru-sdk/icons";
2
+ import { useAppSelector } from "@zauru-sdk/redux";
3
+ import { SelectFieldOption } from "@zauru-sdk/types";
4
+ import { useEffect, useState } from "react";
5
+ import type { SingleValue, InputActionMeta } from "react-select";
6
+ import Select, { components } from "react-select";
7
+ import { LoadingInputSkeleton } from "src";
8
+
9
+ type Props = {
10
+ id?: string;
11
+ formName?: string;
12
+ name?: string;
13
+ title?: string;
14
+ defaultValue?: SingleValue<SelectFieldOption>;
15
+ defaultValueMulti?: SingleValue<SelectFieldOption>[];
16
+ helpText?: string;
17
+ options: Array<SelectFieldOption>;
18
+ onChange?: (value: SingleValue<SelectFieldOption>) => void;
19
+ onChangeMulti?: (value: Array<SingleValue<SelectFieldOption>>) => void;
20
+ onInputChange?: (newValue: string, actionMeta: InputActionMeta) => void;
21
+ isClearable?: boolean;
22
+ error?: string | undefined;
23
+ disabled?: boolean;
24
+ menuIsOpen?: boolean;
25
+ readOnly?: boolean;
26
+ isMulti?: boolean;
27
+ loading?: boolean;
28
+ hint?: string;
29
+ className?: string;
30
+ };
31
+
32
+ const Input = (props: any) => (
33
+ <components.Input {...props} readOnly={props.selectProps.isReadOnly} />
34
+ );
35
+
36
+ export const SelectFieldWithoutValidation = (props: Props) => {
37
+ const {
38
+ id,
39
+ name,
40
+ title,
41
+ defaultValue,
42
+ defaultValueMulti = [],
43
+ helpText,
44
+ hint,
45
+ options,
46
+ onChange,
47
+ onChangeMulti,
48
+ isClearable = false,
49
+ error = false,
50
+ disabled = false,
51
+ readOnly = false,
52
+ isMulti = false,
53
+ loading = false,
54
+ className = "",
55
+ onInputChange,
56
+ } = props;
57
+
58
+ const [value, setValue] = useState<SingleValue<SelectFieldOption>>(
59
+ defaultValue || null
60
+ );
61
+ const [valueMulti, setValueMulti] =
62
+ useState<SingleValue<SelectFieldOption>[]>(defaultValueMulti);
63
+ const [inputValue, setInputValue] = useState("");
64
+ const [showTooltip, setShowTooltip] = useState<boolean>(false);
65
+ const [isClient, setIsClient] = useState(typeof window !== "undefined");
66
+
67
+ const menuIsOpen = readOnly ? false : props?.menuIsOpen;
68
+ const color = error ? "red" : "gray";
69
+ let documentRef = null;
70
+
71
+ const isReadOnly = disabled || readOnly;
72
+ const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
73
+ const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
74
+ const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-500`;
75
+
76
+ if (typeof window !== "undefined") {
77
+ documentRef = document;
78
+ }
79
+
80
+ useEffect(() => {
81
+ setValue(defaultValue || null);
82
+ }, [defaultValue]);
83
+
84
+ useEffect(() => {
85
+ setIsClient(true);
86
+ }, []);
87
+
88
+ if (!isClient || loading || !documentRef) {
89
+ return (
90
+ <>
91
+ {title && (
92
+ <label
93
+ htmlFor={error ? `${name}-error` : `${name}-success`}
94
+ className={`block text-sm font-medium text-${color}-700 dark:text-${color}-500`}
95
+ >
96
+ {title}
97
+ </label>
98
+ )}
99
+ <LoadingInputSkeleton />
100
+ {helpText && (
101
+ <p
102
+ className={`mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`}
103
+ >
104
+ {helpText}
105
+ </p>
106
+ )}
107
+ </>
108
+ );
109
+ }
110
+
111
+ const handleOnChange = (
112
+ selection: SingleValue<SelectFieldOption> | unknown
113
+ ) => {
114
+ // Verificar si el valor de selección es un objeto con propiedades 'value' y 'label'
115
+ if (
116
+ typeof selection === "object" &&
117
+ selection !== null &&
118
+ "value" in selection &&
119
+ "label" in selection
120
+ ) {
121
+ setValue(selection as SingleValue<SelectFieldOption>);
122
+ onChange && onChange(selection as SingleValue<SelectFieldOption>);
123
+ } else {
124
+ setValue(null);
125
+ onChange && onChange(null);
126
+ }
127
+ };
128
+
129
+ const handleOnChangeMulti = (
130
+ selection: SingleValue<SelectFieldOption>[] | unknown
131
+ ) => {
132
+ if (Array.isArray(selection)) {
133
+ setValueMulti(selection as SingleValue<SelectFieldOption>[]);
134
+ onChangeMulti &&
135
+ onChangeMulti(selection as SingleValue<SelectFieldOption>[]);
136
+ } else {
137
+ setValueMulti([]);
138
+ onChangeMulti && onChangeMulti([]);
139
+ }
140
+ };
141
+
142
+ const selectComponent = (
143
+ <>
144
+ <Select
145
+ className={`block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm`}
146
+ id={isMulti ? undefined : id}
147
+ instanceId={isMulti ? undefined : id}
148
+ isDisabled={disabled}
149
+ name={isMulti ? undefined : name}
150
+ options={options}
151
+ onChange={isMulti ? handleOnChangeMulti : handleOnChange}
152
+ defaultValue={isMulti ? valueMulti : value}
153
+ onInputChange={(newValue: string, actionMeta: InputActionMeta) => {
154
+ setInputValue(newValue);
155
+ onInputChange && onInputChange(newValue, actionMeta);
156
+ }}
157
+ inputValue={inputValue}
158
+ onMenuOpen={() => {}}
159
+ onMenuClose={() => {}}
160
+ menuPortalTarget={documentRef?.body}
161
+ styles={{ menuPortal: (base) => ({ ...base, zIndex: 9999 }) }}
162
+ isClearable={isClearable}
163
+ isSearchable={true}
164
+ components={{ Input }}
165
+ menuIsOpen={menuIsOpen}
166
+ //windowThreshold={50}
167
+ isMulti={isMulti}
168
+ />
169
+ {isMulti && (
170
+ <input
171
+ hidden
172
+ readOnly
173
+ name={name}
174
+ value={valueMulti.map((x) => x?.value).join(",")}
175
+ id={id}
176
+ />
177
+ )}
178
+ </>
179
+ );
180
+
181
+ return (
182
+ <div className={`col-span-6 sm:col-span-3 ${className}`}>
183
+ {title && (
184
+ <label
185
+ htmlFor={error ? `${name}-error` : `${name}-success`}
186
+ className={`block text-sm font-medium ${
187
+ color === "red"
188
+ ? "text-red-700 dark:text-red-500"
189
+ : "text-gray-700 dark:text-gray-500"
190
+ }`}
191
+ >
192
+ {title}
193
+ </label>
194
+ )}
195
+ <div className="flex relative items-center">
196
+ {selectComponent}
197
+ {helpText && (
198
+ <div className="flex items-center relative ml-3">
199
+ <div
200
+ className="relative cursor-pointer"
201
+ onMouseEnter={() => setShowTooltip(true)}
202
+ onMouseLeave={() => setShowTooltip(false)}
203
+ >
204
+ <IdeaIconSVG />
205
+ {showTooltip && (
206
+ <div className="absolute -left-48 top-0 mt-8 p-2 bg-white border rounded shadow text-black z-50">
207
+ {helpText}
208
+ </div>
209
+ )}
210
+ </div>
211
+ </div>
212
+ )}
213
+ </div>
214
+ {error && (
215
+ <p className={`mt-2 text-sm text-${color}-600 dark:text-${color}-500`}>
216
+ <span className="font-medium">Oops!</span> {error}
217
+ </p>
218
+ )}
219
+ {!error && hint && (
220
+ <p
221
+ className={`mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`}
222
+ >
223
+ {hint}
224
+ </p>
225
+ )}
226
+ </div>
227
+ );
228
+ };
229
+
230
+ export const SelectField = (props: Props) => {
231
+ const { formValidations } = useAppSelector((state) => state.formValidation);
232
+ const error = formValidations[props.formName ?? "-1"]?.[props.name ?? "-1"];
233
+
234
+ props = { ...props, error };
235
+
236
+ return <SelectFieldWithoutValidation {...props} />;
237
+ };
@@ -0,0 +1,125 @@
1
+ import { useAppSelector } from "@zauru-sdk/redux";
2
+ import React, { useEffect, useState } from "react";
3
+
4
+ type Props = {
5
+ id?: string;
6
+ name: string;
7
+ formName?: string;
8
+ title?: string;
9
+ defaultValue?: string | number;
10
+ hidden?: boolean;
11
+ hint?: string;
12
+ helpText?: string;
13
+ onChange?: (value: string) => void;
14
+ onKeyDown?: (event: React.KeyboardEvent) => void;
15
+ disabled?: boolean;
16
+ readOnly?: boolean;
17
+ error?: string | undefined;
18
+ rows?: number;
19
+ cols?: number;
20
+ stopChangeEvents?: boolean;
21
+ className?: string;
22
+ };
23
+
24
+ export const TextAreaWithoutValidation = (props: Props) => {
25
+ const {
26
+ id,
27
+ name,
28
+ title,
29
+ defaultValue = "",
30
+ hidden,
31
+ hint,
32
+ onChange,
33
+ onKeyDown,
34
+ disabled = false,
35
+ error = false,
36
+ readOnly = false,
37
+ rows,
38
+ cols,
39
+ stopChangeEvents,
40
+ className = "",
41
+ } = props;
42
+
43
+ const [value, setValue] = useState(defaultValue);
44
+
45
+ const color = error ? "red" : "gray";
46
+ const isReadOnly = disabled || readOnly;
47
+ const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
48
+ const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
49
+ const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-500`;
50
+
51
+ useEffect(() => {
52
+ setValue(defaultValue);
53
+ }, [defaultValue]);
54
+
55
+ if (hidden) {
56
+ return (
57
+ <textarea
58
+ id={id ?? name}
59
+ name={name}
60
+ value={defaultValue}
61
+ readOnly={true}
62
+ hidden={true}
63
+ />
64
+ );
65
+ }
66
+
67
+ const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
68
+ if (stopChangeEvents) {
69
+ event.stopPropagation();
70
+ event.preventDefault();
71
+ }
72
+ setValue(event.target.value);
73
+ onChange && onChange(event.target.value);
74
+ };
75
+
76
+ return (
77
+ <div className={`col-span-6 sm:col-span-3 ${className}`}>
78
+ {title && (
79
+ <label
80
+ htmlFor={error ? `${name}-error` : `${name}-success`}
81
+ className={`block text-sm font-medium text-${color}-700 dark:text-${color}-500`}
82
+ >
83
+ {title}
84
+ </label>
85
+ )}
86
+ <textarea
87
+ name={name}
88
+ readOnly={readOnly}
89
+ disabled={disabled}
90
+ id={id ?? name}
91
+ autoComplete="given-name"
92
+ value={value}
93
+ rows={rows}
94
+ cols={cols}
95
+ onChange={handleInputChange}
96
+ onKeyDown={(event: React.KeyboardEvent) => {
97
+ onKeyDown && onKeyDown(event);
98
+ }}
99
+ className={`mt-1 block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm`}
100
+ />
101
+ {error && (
102
+ <p className={`mt-2 text-sm text-${color}-600 dark:text-${color}-500`}>
103
+ <span className="font-medium">Oops!</span> {error}
104
+ </p>
105
+ )}
106
+ {!error && hint && (
107
+ <p
108
+ className={`mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`}
109
+ >
110
+ {hint}
111
+ </p>
112
+ )}
113
+ </div>
114
+ );
115
+ };
116
+
117
+ //<reference> https://tailwindui.com/components/application-ui/forms/form-layouts
118
+ export const TextArea = (props: Props) => {
119
+ const { formValidations } = useAppSelector((state) => state.formValidation);
120
+ const error = formValidations[props.formName ?? "-1"]?.[props.name ?? "-1"];
121
+
122
+ props = { ...props, error };
123
+
124
+ return <TextAreaWithoutValidation {...props} />;
125
+ };
@@ -0,0 +1,194 @@
1
+ import { IdeaIconSVG } from "@zauru-sdk/icons";
2
+ import { useAppSelector } from "@zauru-sdk/redux";
3
+ import React, { useEffect, useState } from "react";
4
+
5
+ type Props = {
6
+ id?: string;
7
+ name?: string;
8
+ formName?: string;
9
+ title?: string;
10
+ defaultValue?: string | number;
11
+ hidden?: boolean;
12
+ type?: React.HTMLInputTypeAttribute;
13
+ helpText?: string;
14
+ hint?: string;
15
+ onChange?: (
16
+ value: string,
17
+ event: React.ChangeEvent<HTMLInputElement>
18
+ ) => void;
19
+ onKeyDown?: (event: React.KeyboardEvent) => void;
20
+ disabled?: boolean;
21
+ readOnly?: boolean;
22
+ error?: string;
23
+ min?: string | number;
24
+ integer?: boolean;
25
+ stopChangeEvents?: boolean;
26
+ style?: React.CSSProperties;
27
+ className?: string;
28
+ };
29
+
30
+ export const TextFieldWithoutValidation = (props: Props) => {
31
+ const {
32
+ id,
33
+ name,
34
+ defaultValue = "",
35
+ hidden,
36
+ type = "text",
37
+ onChange,
38
+ onKeyDown,
39
+ disabled = false,
40
+ readOnly = false,
41
+ min,
42
+ integer = false,
43
+ stopChangeEvents,
44
+ style,
45
+ error,
46
+ title,
47
+ helpText,
48
+ className,
49
+ hint,
50
+ } = props;
51
+
52
+ const [showTooltip, setShowTooltip] = useState<boolean>(false);
53
+ const [value, setValue] = useState(defaultValue);
54
+
55
+ const color = error ? "red" : "gray";
56
+
57
+ const isReadOnly = disabled || readOnly;
58
+ const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
59
+ const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
60
+ const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-500`;
61
+
62
+ useEffect(() => {
63
+ setValue(defaultValue);
64
+ }, [defaultValue]);
65
+
66
+ if (hidden) {
67
+ return (
68
+ <input
69
+ type={type}
70
+ id={id ?? name}
71
+ name={name}
72
+ value={defaultValue}
73
+ readOnly={true}
74
+ hidden={true}
75
+ />
76
+ );
77
+ }
78
+
79
+ const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
80
+ if (stopChangeEvents) {
81
+ event.stopPropagation();
82
+ event.preventDefault();
83
+ }
84
+ if (integer && type === "number") {
85
+ const value = event.target.value;
86
+ const isInteger = /^[0-9]*$/.test(value);
87
+ if (isInteger || value === "") {
88
+ setValue(value);
89
+ onChange && onChange(value, event);
90
+ }
91
+ } else {
92
+ setValue(event.target.value);
93
+ onChange && onChange(event.target.value, event);
94
+ }
95
+ };
96
+
97
+ const handleKeyDown = (event: React.KeyboardEvent) => {
98
+ if (integer && type === "number") {
99
+ // Permitir solo números y teclas de control
100
+ const allowedKeys = [
101
+ "Backspace",
102
+ "ArrowLeft",
103
+ "ArrowRight",
104
+ "Delete",
105
+ "Enter",
106
+ "Tab",
107
+ ];
108
+ if (!allowedKeys.includes(event.key) && !/^[0-9]$/.test(event.key)) {
109
+ event.preventDefault();
110
+ }
111
+ }
112
+ };
113
+
114
+ const inputComponent = (
115
+ <input
116
+ type={type}
117
+ name={name}
118
+ readOnly={readOnly}
119
+ disabled={disabled}
120
+ id={id ?? name}
121
+ autoComplete="given-name"
122
+ value={value}
123
+ onWheel={(e: React.WheelEvent<HTMLInputElement>) => {
124
+ e.currentTarget.blur();
125
+ }}
126
+ step={type === "number" ? 0.01 : undefined} //para aceptar decimales
127
+ onChange={handleInputChange}
128
+ onKeyDown={(event: React.KeyboardEvent) => {
129
+ handleKeyDown(event);
130
+ onKeyDown && onKeyDown(event);
131
+ }}
132
+ min={min}
133
+ style={style}
134
+ className={`block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm`}
135
+ />
136
+ );
137
+
138
+ if (!error && !title) {
139
+ return <div className={`${className}`}>{inputComponent}</div>;
140
+ }
141
+
142
+ return (
143
+ <div className={`col-span-6 sm:col-span-3 ${className}`}>
144
+ {title && (
145
+ <label
146
+ htmlFor={error ? `${name}-error` : `${name}-success`}
147
+ className={`block mb-1 text-sm font-medium text-${color}-700 dark:text-${color}-500`}
148
+ >
149
+ {title}
150
+ </label>
151
+ )}
152
+ <div className="flex relative items-center">
153
+ {inputComponent}
154
+ {helpText && (
155
+ <div className="flex items-center relative ml-3">
156
+ <div
157
+ className="relative cursor-pointer"
158
+ onMouseEnter={() => setShowTooltip(true)}
159
+ onMouseLeave={() => setShowTooltip(false)}
160
+ >
161
+ <IdeaIconSVG />
162
+ {showTooltip && (
163
+ <div className="absolute -left-48 top-0 mt-8 p-2 bg-white border rounded shadow text-black z-50">
164
+ {helpText}
165
+ </div>
166
+ )}
167
+ </div>
168
+ </div>
169
+ )}
170
+ </div>
171
+ {error && (
172
+ <p className={`mt-2 text-sm text-${color}-600 dark:text-${color}-500`}>
173
+ <span className="font-medium">Oops!</span> {error}
174
+ </p>
175
+ )}
176
+ {!error && hint && (
177
+ <p
178
+ className={`mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`}
179
+ >
180
+ {hint}
181
+ </p>
182
+ )}
183
+ </div>
184
+ );
185
+ };
186
+
187
+ export const TextField = (props: Props) => {
188
+ const { formValidations } = useAppSelector((state) => state.formValidation);
189
+ const error = formValidations[props.formName ?? "-1"]?.[props.name ?? "-1"];
190
+
191
+ props = { ...props, error };
192
+
193
+ return <TextFieldWithoutValidation {...props} />;
194
+ };