kelt-ui-kit-react 2.0.3 → 2.0.4
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 +26 -0
- package/dist/addressSearch/AddressSearch.d.ts +20 -0
- package/dist/addressSearch/AddressSearch.view.d.ts +1 -0
- package/dist/badge/Badge.d.ts +1 -3
- package/dist/button/Button.d.ts +1 -1
- package/dist/button/button.interface.d.ts +2 -1
- package/dist/card/Card.d.ts +4 -1
- package/dist/dataTable/DataTable.d.ts +14 -17
- package/dist/dataTable/dataTable.interface.d.ts +19 -24
- package/dist/dataTable/dataTableActions/DataTableAction.d.ts +5 -3
- package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +7 -13
- package/dist/dataTable/dataTableLoading/DataTableLoading.d.ts +3 -1
- package/dist/dataTable/dataTableRow/DataTableRow.d.ts +6 -5
- package/dist/expands/Expands.d.ts +1 -0
- package/dist/expands/expand/expand.d.ts +3 -2
- package/dist/form/Form.d.ts +10 -2
- package/dist/form/form.enum.d.ts +3 -2
- package/dist/form/form.interface.d.ts +22 -3
- package/dist/form/input/Input.d.ts +0 -3
- package/dist/form/inputNumber/InputNumber.d.ts +2 -0
- package/dist/geocoding/GeocodingService.d.ts +13 -0
- package/dist/geocoding/geocoding.interface.d.ts +20 -0
- package/dist/grid/Grid.d.ts +5 -2
- package/dist/grid/col/Col.d.ts +3 -6
- package/dist/grid/col/colStyled/ColStyled.d.ts +8 -5
- package/dist/grid/grid.interface.d.ts +20 -5
- package/dist/grid/row/Row.d.ts +7 -0
- package/dist/header/Header.d.ts +1 -1
- package/dist/header/header.interface.d.ts +2 -1
- package/dist/icon/Icon.d.ts +1 -1
- package/dist/icon/icon.interface.d.ts +0 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3724 -3628
- package/dist/menus/menu/Menu.d.ts +1 -0
- package/dist/select/Select.d.ts +2 -1
- package/dist/style.css +1 -1
- package/dist/tabs/Tabs.d.ts +1 -2
- package/dist/toaster/store/useToasterStore.d.ts +1 -0
- package/index.html +1 -2
- package/package.json +1 -1
- package/src/App.menu.tsx +8 -0
- package/src/addressSearch/AddressSearch.tsx +246 -0
- package/src/addressSearch/AddressSearch.view.tsx +52 -0
- package/src/addressSearch/addressSearch.css +133 -0
- package/src/badge/Badge.tsx +1 -9
- package/src/badge/badge.css +0 -4
- package/src/button/Button.tsx +3 -1
- package/src/button/button.css +2 -2
- package/src/button/button.interface.tsx +2 -1
- package/src/card/Card.tsx +37 -11
- package/src/card/card.css +7 -28
- package/src/card/cardAction/CardAction.tsx +4 -4
- package/src/damier/Damier.tsx +16 -7
- package/src/damier/damier.css +11 -4
- package/src/dataTable/DataTable.tsx +542 -685
- package/src/dataTable/DataTable.view.tsx +24 -48
- package/src/dataTable/dataTable.css +159 -134
- package/src/dataTable/dataTable.interface.ts +19 -25
- package/src/dataTable/dataTableActions/DataTableAction.tsx +25 -14
- package/src/dataTable/dataTableColumn/DataTableColumn.tsx +14 -64
- package/src/dataTable/dataTableLoading/DataTableLoading.tsx +15 -4
- package/src/dataTable/dataTableRow/DataTableRow.tsx +180 -64
- package/src/expands/Expands.tsx +18 -2
- package/src/expands/expand/expand.tsx +128 -5
- package/src/expands/expands.css +8 -3
- package/src/form/Form.tsx +294 -99
- package/src/form/Form.view.tsx +9 -1
- package/src/form/form.css +64 -1
- package/src/form/form.enum.ts +2 -1
- package/src/form/form.interface.tsx +31 -3
- package/src/form/input/Input.tsx +7 -61
- package/src/form/input/input.css +1 -19
- package/src/form/inputNumber/InputNumber.tsx +11 -6
- package/src/form/inputPrice/InputPrice.tsx +4 -2
- package/src/geocoding/GeocodingService.ts +89 -0
- package/src/geocoding/geocoding.interface.ts +25 -0
- package/src/grid/Grid.tsx +34 -21
- package/src/grid/col/Col.tsx +24 -15
- package/src/grid/col/col.css +3 -0
- package/src/grid/col/colStyled/ColStyled.tsx +59 -41
- package/src/grid/container/container.css +2 -2
- package/src/grid/grid.interface.tsx +25 -5
- package/src/grid/row/Row.tsx +49 -12
- package/src/grid/row/row.css +11 -3
- package/src/header/Header.tsx +4 -2
- package/src/header/header.css +17 -0
- package/src/header/header.interface.tsx +2 -1
- package/src/icon/Icon.tsx +2 -6
- package/src/icon/icon.interface.tsx +0 -1
- package/src/index.css +58 -12
- package/src/index.ts +12 -2
- package/src/menus/Menus.tsx +1 -0
- package/src/menus/menu/Menu.tsx +2 -1
- package/src/select/Select.tsx +5 -3
- package/src/sidebar/Sidebar.tsx +107 -5
- package/src/sidebar/sidebar.css +18 -1
- package/src/tabs/Tabs.tsx +1 -20
- package/src/tabs/tabs.css +9 -3
- package/src/toaster/Toaster.tsx +7 -2
- package/src/toaster/Toaster.view.tsx +1 -0
- package/src/toaster/store/useToasterStore.tsx +2 -0
- package/src/toaster/toaster.css +7 -2
- package/dist/_core/type/generiqueType.d.ts +0 -4
- package/dist/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.d.ts +0 -10
- package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.d.ts +0 -9
- package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.d.ts +0 -9
- package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.d.ts +0 -13
- package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.d.ts +0 -9
- package/dist/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.d.ts +0 -11
- package/dist/dataTable/dataTableFilters/DataTableFilters.d.ts +0 -7
- package/dist/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.d.ts +0 -12
- package/dist/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.d.ts +0 -11
- package/package/package.json +0 -56
- package/package/src/sidebar/Sidebar.tsx +0 -208
- package/package/src/sidebar/sidebar.css +0 -104
- package/src/_core/type/generiqueType.tsx +0 -4
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.tsx +0 -36
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilter.css +0 -0
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.tsx +0 -39
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.tsx +0 -80
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.tsx +0 -89
- package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.tsx +0 -37
- package/src/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.tsx +0 -42
- package/src/dataTable/dataTableFilters/DataTableFilters.tsx +0 -91
- package/src/dataTable/dataTableFilters/dataTableFilters.css +0 -16
- package/src/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.tsx +0 -80
- package/src/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.tsx +0 -123
package/src/form/Form.tsx
CHANGED
|
@@ -9,12 +9,41 @@ import { Button } from "../button/Button";
|
|
|
9
9
|
import { Icon } from "../icon/Icon";
|
|
10
10
|
import { Select } from "../select/Select"; // <--- ton composant
|
|
11
11
|
import "./form.css";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
FormInterface,
|
|
14
|
+
FormMode,
|
|
15
|
+
FormSection,
|
|
16
|
+
FormValuesInterface,
|
|
17
|
+
} from "./form.interface";
|
|
13
18
|
import { Input } from "./input/Input";
|
|
19
|
+
import { InputPrice } from "./inputPrice/InputPrice";
|
|
14
20
|
import { TextArea } from "./textArea/TextArea";
|
|
21
|
+
import { Col } from "../grid/col/Col";
|
|
22
|
+
import { Row } from "../grid/row/Row";
|
|
23
|
+
import { GridBreakpoints, GridResponsive } from "../grid/grid.interface";
|
|
24
|
+
import { TypeInputEnum } from "./form.enum";
|
|
25
|
+
import { AddressSearch } from "../addressSearch/AddressSearch";
|
|
26
|
+
|
|
27
|
+
const euroFormatter = new Intl.NumberFormat("fr-FR", {
|
|
28
|
+
style: "currency",
|
|
29
|
+
currency: "EUR",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
type FormFieldContainerProps = {
|
|
33
|
+
field: FormInterface;
|
|
34
|
+
gridEnabled: boolean;
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const FormFieldContainer = ({
|
|
39
|
+
field,
|
|
40
|
+
gridEnabled,
|
|
41
|
+
children,
|
|
42
|
+
}: FormFieldContainerProps) =>
|
|
43
|
+
gridEnabled ? <Col {...field.col}>{children}</Col> : <>{children}</>;
|
|
15
44
|
|
|
16
45
|
export type FormProps<
|
|
17
|
-
T extends { [key: string]: string | number | boolean | Date | string[] }
|
|
46
|
+
T extends { [key: string]: string | number | boolean | Date | string[] }
|
|
18
47
|
> = {
|
|
19
48
|
id?: string;
|
|
20
49
|
className?: string;
|
|
@@ -22,8 +51,15 @@ export type FormProps<
|
|
|
22
51
|
hideSubmit?: boolean;
|
|
23
52
|
autoComplete?: string;
|
|
24
53
|
children?: React.ReactNode;
|
|
25
|
-
initialForm
|
|
54
|
+
initialForm?: FormInterface[];
|
|
55
|
+
sections?: FormSection[];
|
|
26
56
|
disabledSubmit?: boolean;
|
|
57
|
+
mode?: FormMode;
|
|
58
|
+
/** Largeur sur laquelle se basent les breakpoints des champs (col), défaut : "app" */
|
|
59
|
+
responsive?: GridResponsive;
|
|
60
|
+
/** Place le bouton submit dans la grid (dernière ligne) avec ces tailles de col.
|
|
61
|
+
* Ignoré si les champs n'utilisent pas la grid : le bouton reste dans le footer. */
|
|
62
|
+
submitCol?: GridBreakpoints;
|
|
27
63
|
onSubmit?: (values: FormValuesInterface<T>) => void;
|
|
28
64
|
onChange?: (values: FormValuesInterface<T>) => void;
|
|
29
65
|
};
|
|
@@ -32,6 +68,7 @@ export const DynamicForm = forwardRef(
|
|
|
32
68
|
<T extends { [key: string]: string | number | boolean | Date | string[] }>(
|
|
33
69
|
{
|
|
34
70
|
initialForm,
|
|
71
|
+
sections,
|
|
35
72
|
title,
|
|
36
73
|
onSubmit,
|
|
37
74
|
id,
|
|
@@ -40,7 +77,10 @@ export const DynamicForm = forwardRef(
|
|
|
40
77
|
onChange,
|
|
41
78
|
hideSubmit,
|
|
42
79
|
disabledSubmit = false,
|
|
80
|
+
mode = "edit",
|
|
43
81
|
className,
|
|
82
|
+
responsive,
|
|
83
|
+
submitCol,
|
|
44
84
|
}: FormProps<T>,
|
|
45
85
|
ref: React.Ref<{
|
|
46
86
|
clearInput?: (name: string) => void;
|
|
@@ -49,13 +89,19 @@ export const DynamicForm = forwardRef(
|
|
|
49
89
|
getValues?: () => FormValuesInterface<T>;
|
|
50
90
|
updateFormValue?: (values: FormValuesInterface<T>) => void;
|
|
51
91
|
setBtnDisabledSubmit?: (disabled: boolean) => void;
|
|
52
|
-
}
|
|
92
|
+
}>
|
|
53
93
|
) => {
|
|
94
|
+
const formFields = sections
|
|
95
|
+
? sections.reduce<FormInterface[]>(
|
|
96
|
+
(fields, section) => fields.concat(section.fields),
|
|
97
|
+
[],
|
|
98
|
+
)
|
|
99
|
+
: (initialForm ?? []);
|
|
54
100
|
const [btnDisabledSubmit, setBtnDisabledSubmit] = useState(disabledSubmit);
|
|
55
101
|
const [values, setValues] = useState<FormValuesInterface<T>>(() => {
|
|
56
102
|
const initialValues: FormValuesInterface<T> =
|
|
57
103
|
{} as FormValuesInterface<T>;
|
|
58
|
-
|
|
104
|
+
formFields.forEach((field) => {
|
|
59
105
|
(initialValues as any)[field.name] = field.value ?? "";
|
|
60
106
|
});
|
|
61
107
|
return initialValues;
|
|
@@ -67,7 +113,7 @@ export const DynamicForm = forwardRef(
|
|
|
67
113
|
const handleChangeInput = useCallback(
|
|
68
114
|
(
|
|
69
115
|
event: React.ChangeEvent<HTMLInputElement>,
|
|
70
|
-
forminterface: FormInterface
|
|
116
|
+
forminterface: FormInterface
|
|
71
117
|
) => {
|
|
72
118
|
const { name, type, checked, value } = event.target;
|
|
73
119
|
const newValue = type === "checkbox" ? checked : value;
|
|
@@ -84,7 +130,7 @@ export const DynamicForm = forwardRef(
|
|
|
84
130
|
return updatedValues;
|
|
85
131
|
});
|
|
86
132
|
},
|
|
87
|
-
[onChange]
|
|
133
|
+
[onChange]
|
|
88
134
|
);
|
|
89
135
|
const handleChangeTextArea = useCallback(
|
|
90
136
|
(name: string, value: string, forminterface: FormInterface) => {
|
|
@@ -100,14 +146,14 @@ export const DynamicForm = forwardRef(
|
|
|
100
146
|
return updatedValues;
|
|
101
147
|
});
|
|
102
148
|
},
|
|
103
|
-
[onChange]
|
|
149
|
+
[onChange]
|
|
104
150
|
);
|
|
105
151
|
/** Gestion des <select> */
|
|
106
152
|
const handleChangeSelect = useCallback(
|
|
107
153
|
(
|
|
108
154
|
newValue: string | string[],
|
|
109
155
|
name: string,
|
|
110
|
-
forminterface: FormInterface
|
|
156
|
+
forminterface: FormInterface
|
|
111
157
|
) => {
|
|
112
158
|
if (forminterface.onChange) {
|
|
113
159
|
forminterface.onChange(newValue, name);
|
|
@@ -119,7 +165,7 @@ export const DynamicForm = forwardRef(
|
|
|
119
165
|
return updatedValues;
|
|
120
166
|
});
|
|
121
167
|
},
|
|
122
|
-
[onChange]
|
|
168
|
+
[onChange]
|
|
123
169
|
);
|
|
124
170
|
|
|
125
171
|
const handleSubmit = useCallback(
|
|
@@ -129,14 +175,14 @@ export const DynamicForm = forwardRef(
|
|
|
129
175
|
onSubmit(values);
|
|
130
176
|
}
|
|
131
177
|
},
|
|
132
|
-
[onSubmit, values]
|
|
178
|
+
[onSubmit, values]
|
|
133
179
|
);
|
|
134
180
|
|
|
135
181
|
const resetForm = () => {
|
|
136
182
|
setValues(() => {
|
|
137
183
|
const resetValues: FormValuesInterface<T> =
|
|
138
184
|
{} as FormValuesInterface<T>;
|
|
139
|
-
|
|
185
|
+
formFields.forEach((field) => {
|
|
140
186
|
(resetValues as any)[field.name] = field.value ?? "";
|
|
141
187
|
});
|
|
142
188
|
return resetValues;
|
|
@@ -151,7 +197,7 @@ export const DynamicForm = forwardRef(
|
|
|
151
197
|
}, []);
|
|
152
198
|
|
|
153
199
|
const focusInitialElement = () => {
|
|
154
|
-
const focusedField =
|
|
200
|
+
const focusedField = formFields.find((field) => field.focus);
|
|
155
201
|
if (focusedField && inputRefs.current[focusedField.name]) {
|
|
156
202
|
setTimeout(() => {
|
|
157
203
|
inputRefs.current[focusedField.name]?.select();
|
|
@@ -162,7 +208,7 @@ export const DynamicForm = forwardRef(
|
|
|
162
208
|
const getValues = () => values;
|
|
163
209
|
|
|
164
210
|
const updateFormValue: (values: FormValuesInterface<T>) => void = (
|
|
165
|
-
values
|
|
211
|
+
values
|
|
166
212
|
) => {
|
|
167
213
|
setValues((prevValues) => ({
|
|
168
214
|
...prevValues,
|
|
@@ -170,6 +216,17 @@ export const DynamicForm = forwardRef(
|
|
|
170
216
|
}));
|
|
171
217
|
};
|
|
172
218
|
|
|
219
|
+
const updateValues = useCallback(
|
|
220
|
+
(updatedValues: FormValuesInterface<T>) => {
|
|
221
|
+
setValues((previousValues) => {
|
|
222
|
+
const nextValues = { ...previousValues, ...updatedValues };
|
|
223
|
+
onChange?.(nextValues);
|
|
224
|
+
return nextValues;
|
|
225
|
+
});
|
|
226
|
+
},
|
|
227
|
+
[onChange],
|
|
228
|
+
);
|
|
229
|
+
|
|
173
230
|
useImperativeHandle(ref, () => ({
|
|
174
231
|
resetForm,
|
|
175
232
|
clearInput,
|
|
@@ -179,104 +236,242 @@ export const DynamicForm = forwardRef(
|
|
|
179
236
|
setBtnDisabledSubmit,
|
|
180
237
|
}));
|
|
181
238
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
239
|
+
const getReadValue = (field: FormInterface): React.ReactNode => {
|
|
240
|
+
const value = values[field.name];
|
|
241
|
+
|
|
242
|
+
if (field.formatReadValue) {
|
|
243
|
+
return field.formatReadValue(value ?? "");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (field.type === TypeInputEnum.SELECT) {
|
|
247
|
+
const selectedValues = Array.isArray(value) ? value : [value];
|
|
248
|
+
const labels = selectedValues
|
|
249
|
+
.map(
|
|
250
|
+
(selectedValue) =>
|
|
251
|
+
field.options?.find((option) => option.value === selectedValue)
|
|
252
|
+
?.label ?? selectedValue,
|
|
253
|
+
)
|
|
254
|
+
.filter((selectedValue) => selectedValue !== undefined);
|
|
255
|
+
|
|
256
|
+
return labels.join(", ") || "-";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (field.type === TypeInputEnum.PRICE) {
|
|
260
|
+
const amount = Number(value);
|
|
261
|
+
return value === "" || value === undefined || Number.isNaN(amount)
|
|
262
|
+
? "-"
|
|
263
|
+
: euroFormatter.format(amount);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (typeof value === "boolean") {
|
|
267
|
+
return value ? "Oui" : "Non";
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (value instanceof Date) {
|
|
271
|
+
return value.toLocaleDateString();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (Array.isArray(value)) {
|
|
275
|
+
return value.join(", ") || "-";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return value === undefined || value === null || value === ""
|
|
279
|
+
? "-"
|
|
280
|
+
: String(value);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const submitButton = mode === "edit" && !hideSubmit && (
|
|
284
|
+
<Button
|
|
285
|
+
id={`button-${id}`}
|
|
286
|
+
disabled={btnDisabledSubmit}
|
|
287
|
+
title={title || "submit"}
|
|
288
|
+
/>
|
|
289
|
+
);
|
|
290
|
+
// Le bouton va dans la grid de la dernière liste de champs, si elle en a une
|
|
291
|
+
const lastFields = sections
|
|
292
|
+
? sections[sections.length - 1]?.fields ?? []
|
|
293
|
+
: initialForm ?? [];
|
|
294
|
+
const submitInGrid =
|
|
295
|
+
!!submitCol && lastFields.some((field) => field.col !== undefined);
|
|
296
|
+
|
|
297
|
+
const renderFields = (fields: FormInterface[], withSubmit = false) => {
|
|
298
|
+
const gridEnabled = fields.some((field) => field.col !== undefined);
|
|
299
|
+
const FieldsContainer: React.ElementType = gridEnabled
|
|
300
|
+
? Row
|
|
301
|
+
: React.Fragment;
|
|
302
|
+
// Un Fragment n'accepte pas de props : on ne les passe qu'à la Row
|
|
303
|
+
const containerProps = gridEnabled ? { responsive } : {};
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<FieldsContainer {...containerProps}>
|
|
307
|
+
{fields.map((v, key) => {
|
|
190
308
|
const required = v.onRequired
|
|
191
309
|
? v.onRequired(v.value)
|
|
192
|
-
:
|
|
310
|
+
: v.required ?? false;
|
|
193
311
|
const disabled = v.onDisabled
|
|
194
312
|
? v.onDisabled(v.value)
|
|
195
|
-
:
|
|
313
|
+
: v.disabled ?? false;
|
|
196
314
|
|
|
197
315
|
return (
|
|
198
|
-
<
|
|
199
|
-
key={key}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
} form-group form-group-${v.type}`}
|
|
316
|
+
<FormFieldContainer
|
|
317
|
+
key={v.name || key}
|
|
318
|
+
field={v}
|
|
319
|
+
gridEnabled={gridEnabled}
|
|
203
320
|
>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
321
|
+
<div
|
|
322
|
+
className={`d-flex flex-column ${v.className ?? ""
|
|
323
|
+
} form-group form-group-${v.type}`}
|
|
324
|
+
>
|
|
325
|
+
{v.label && <label>{v.label}</label>}
|
|
326
|
+
{v.icon && <Icon classIcon={v.icon} />}
|
|
327
|
+
|
|
328
|
+
{mode === "read" ? (
|
|
329
|
+
<div
|
|
330
|
+
className={`form-read-value ${v.type === TypeInputEnum.TEXTAREA
|
|
331
|
+
? "form-read-value--textarea"
|
|
332
|
+
: ""
|
|
333
|
+
}`}
|
|
334
|
+
>
|
|
335
|
+
{getReadValue(v)}
|
|
336
|
+
</div>
|
|
337
|
+
) : v.type === TypeInputEnum.ADDRESS ? (
|
|
338
|
+
<AddressSearch
|
|
339
|
+
id={v.id ?? v.name}
|
|
340
|
+
label=""
|
|
341
|
+
placeholder={v.placeholder}
|
|
342
|
+
value={String(values[v.name] ?? "")}
|
|
343
|
+
disabled={disabled}
|
|
344
|
+
minimumQueryLength={v.addressSearch?.minimumQueryLength}
|
|
345
|
+
debounceTime={v.addressSearch?.debounceTime}
|
|
346
|
+
limit={v.addressSearch?.limit}
|
|
347
|
+
provider={v.addressSearch?.provider}
|
|
348
|
+
allowManualEntry
|
|
349
|
+
manualEntryLabel={v.addressSearch?.manualEntryLabel}
|
|
350
|
+
onChange={(value) =>
|
|
351
|
+
updateValues({ [v.name]: value } as FormValuesInterface<T>)
|
|
352
|
+
}
|
|
353
|
+
onSelect={(address) => {
|
|
354
|
+
const mappedValues =
|
|
355
|
+
v.addressSearch?.mapAddress?.(address) ?? {};
|
|
356
|
+
updateValues(mappedValues as FormValuesInterface<T>);
|
|
357
|
+
v.addressSearch?.onSelect?.(address);
|
|
358
|
+
}}
|
|
359
|
+
/>
|
|
360
|
+
) : v.type === "select" ? (
|
|
361
|
+
<Select
|
|
362
|
+
id={v.id ?? v.name}
|
|
363
|
+
label={undefined}
|
|
364
|
+
name={v.name}
|
|
365
|
+
options={v.options ?? []}
|
|
366
|
+
disabled={disabled}
|
|
367
|
+
multiple={v.multiple}
|
|
368
|
+
usePortal={v.usePortal}
|
|
369
|
+
searchable={v.searchable}
|
|
370
|
+
value={values[v.name] as any}
|
|
371
|
+
required={required}
|
|
372
|
+
defaultValue={v.value as string | string[]}
|
|
373
|
+
placeholder={v.placeholder}
|
|
374
|
+
onChangeMultiple={(newValue) =>
|
|
375
|
+
handleChangeSelect(newValue, v.name, v)
|
|
376
|
+
}
|
|
377
|
+
onChange={(newValue) =>
|
|
378
|
+
handleChangeSelect(newValue, v.name, v)
|
|
379
|
+
}
|
|
380
|
+
/>
|
|
381
|
+
) : v.type === "textarea" ? (
|
|
382
|
+
<TextArea
|
|
383
|
+
id={v.id ?? v.name}
|
|
384
|
+
name={v.name}
|
|
385
|
+
className="textarea-input"
|
|
386
|
+
placeholder={v.placeholder ?? ""}
|
|
387
|
+
disabled={disabled}
|
|
388
|
+
required={required}
|
|
389
|
+
value={values[v.name] as string}
|
|
390
|
+
onChange={(value, name) =>
|
|
391
|
+
handleChangeTextArea(name, value, v)
|
|
392
|
+
}
|
|
393
|
+
/>
|
|
394
|
+
) : v.type === TypeInputEnum.PRICE ? (
|
|
395
|
+
<InputPrice
|
|
396
|
+
id={v.id ?? v.name}
|
|
397
|
+
ref={(el) => (inputRefs.current[v.name] = el)}
|
|
398
|
+
name={v.name}
|
|
399
|
+
placeholder={v.placeholder ?? ""}
|
|
400
|
+
autoComplete={v.autoComplete}
|
|
401
|
+
tabIndex={0}
|
|
402
|
+
disabled={disabled}
|
|
403
|
+
autoFocus={v.focus ?? false}
|
|
404
|
+
required={required}
|
|
405
|
+
value={String(values[v.name] ?? "")}
|
|
406
|
+
onChange={(value) =>
|
|
407
|
+
handleChangeTextArea(v.name, value, v)
|
|
408
|
+
}
|
|
409
|
+
/>
|
|
410
|
+
) : (
|
|
411
|
+
<Input
|
|
412
|
+
id={v.id ?? v.name}
|
|
413
|
+
ref={(el) => (inputRefs.current[v.name] = el)}
|
|
414
|
+
name={v.name}
|
|
415
|
+
type={v.type}
|
|
416
|
+
placeholder={v.placeholder ?? ""}
|
|
417
|
+
autoComplete={v.autoComplete}
|
|
418
|
+
tabIndex={0}
|
|
419
|
+
disabled={disabled}
|
|
420
|
+
autoFocus={v.focus ?? false}
|
|
421
|
+
required={required}
|
|
422
|
+
step={v.step ?? "0.01"}
|
|
423
|
+
value={values[v.name] as string | number}
|
|
424
|
+
checked={
|
|
425
|
+
v.type === "checkbox" ? !!values[v.name] : undefined
|
|
426
|
+
}
|
|
427
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
428
|
+
handleChangeInput(e, v)
|
|
429
|
+
}
|
|
430
|
+
onInvalid={v.onInvalid ? v.onInvalid : undefined}
|
|
431
|
+
/>
|
|
432
|
+
)}
|
|
433
|
+
</div>
|
|
434
|
+
</FormFieldContainer>
|
|
266
435
|
);
|
|
267
436
|
})}
|
|
437
|
+
{withSubmit && submitButton && (
|
|
438
|
+
<Col {...submitCol}>
|
|
439
|
+
<div className="form-submit-col">{submitButton}</div>
|
|
440
|
+
</Col>
|
|
441
|
+
)}
|
|
442
|
+
</FieldsContainer>
|
|
443
|
+
);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
return (
|
|
447
|
+
<form
|
|
448
|
+
autoComplete={autoComplete ?? "off"}
|
|
449
|
+
onSubmit={handleSubmit}
|
|
450
|
+
className={className ?? ""}
|
|
451
|
+
>
|
|
452
|
+
<div className="form-content">
|
|
453
|
+
{sections
|
|
454
|
+
? sections.map((section, index) => (
|
|
455
|
+
<section
|
|
456
|
+
className={`form-section ${section.className ?? ""}`}
|
|
457
|
+
key={index}
|
|
458
|
+
>
|
|
459
|
+
{section.title && (
|
|
460
|
+
<h3 className="form-section-title">{section.title}</h3>
|
|
461
|
+
)}
|
|
462
|
+
{renderFields(
|
|
463
|
+
section.fields,
|
|
464
|
+
submitInGrid && index === sections.length - 1
|
|
465
|
+
)}
|
|
466
|
+
</section>
|
|
467
|
+
))
|
|
468
|
+
: renderFields(initialForm ?? [], submitInGrid)}
|
|
268
469
|
{children}
|
|
269
470
|
</div>
|
|
270
471
|
<div className="d-flex justify-content-end form-footer">
|
|
271
|
-
{!
|
|
272
|
-
<Button
|
|
273
|
-
id={`button-${id}`}
|
|
274
|
-
disabled={btnDisabledSubmit}
|
|
275
|
-
title={title || "submit"}
|
|
276
|
-
/>
|
|
277
|
-
)}
|
|
472
|
+
{!submitInGrid && submitButton}
|
|
278
473
|
</div>
|
|
279
474
|
</form>
|
|
280
475
|
);
|
|
281
|
-
}
|
|
476
|
+
}
|
|
282
477
|
);
|
package/src/form/Form.view.tsx
CHANGED
|
@@ -18,12 +18,14 @@ export const FormView = (): JSX.Element => {
|
|
|
18
18
|
name: "nom",
|
|
19
19
|
type: TypeInputEnum.PASSWORD,
|
|
20
20
|
required: true,
|
|
21
|
+
col: { xs: 12, md: 6 },
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
value: 30,
|
|
24
25
|
label: "Age",
|
|
25
26
|
name: "age",
|
|
26
27
|
type: TypeInputEnum.NUMBER,
|
|
28
|
+
col: { xs: 12, md: 6 },
|
|
27
29
|
},
|
|
28
30
|
{
|
|
29
31
|
value: false,
|
|
@@ -55,5 +57,11 @@ export const FormView = (): JSX.Element => {
|
|
|
55
57
|
const handleSubmit = (values: FormValuesInterface<FormData>) => {
|
|
56
58
|
console.log("Form Submitted: ", values);
|
|
57
59
|
};
|
|
58
|
-
return
|
|
60
|
+
return (
|
|
61
|
+
<DynamicForm
|
|
62
|
+
mode="edit"
|
|
63
|
+
onSubmit={handleSubmit}
|
|
64
|
+
initialForm={initialValues}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
59
67
|
};
|
package/src/form/form.css
CHANGED
|
@@ -6,7 +6,7 @@ input {
|
|
|
6
6
|
min-width: 250px;
|
|
7
7
|
|
|
8
8
|
&:focus-visible {
|
|
9
|
-
outline:
|
|
9
|
+
outline: 1px solid #9696a7;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -20,6 +20,19 @@ input[type="radio"] {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
form {
|
|
23
|
+
.form-section+.form-section {
|
|
24
|
+
margin-top: 1.25rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.form-section-title {
|
|
28
|
+
margin: 0 0 0.5rem;
|
|
29
|
+
padding-bottom: 0.5rem;
|
|
30
|
+
border-bottom: 1px solid #dedede;
|
|
31
|
+
color: var(--primary-color);
|
|
32
|
+
font-size: 1rem;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
&.form-row {
|
|
24
37
|
display: flex;
|
|
25
38
|
flex-direction: row;
|
|
@@ -70,4 +83,54 @@ form {
|
|
|
70
83
|
button {
|
|
71
84
|
margin-top: 1rem;
|
|
72
85
|
}
|
|
86
|
+
|
|
87
|
+
.form-read-value {
|
|
88
|
+
width: 100%;
|
|
89
|
+
min-width: 250px;
|
|
90
|
+
min-height: 0px;
|
|
91
|
+
box-sizing: border-box;
|
|
92
|
+
border: 1px solid transparent;
|
|
93
|
+
border-radius: 4px;
|
|
94
|
+
margin: 0.25rem 0;
|
|
95
|
+
padding: 0;
|
|
96
|
+
font-size: 0.8rem;
|
|
97
|
+
line-height: normal;
|
|
98
|
+
overflow-wrap: anywhere;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.form-read-value--textarea {
|
|
102
|
+
min-height: 3.25rem;
|
|
103
|
+
white-space: pre-wrap;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.grid-row {
|
|
107
|
+
|
|
108
|
+
/* Bouton submit placé dans la grid (submitCol) : aligné en bas, sur les inputs */
|
|
109
|
+
.form-submit-col {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: flex-end;
|
|
112
|
+
justify-content: flex-end;
|
|
113
|
+
height: 100%;
|
|
114
|
+
|
|
115
|
+
button {
|
|
116
|
+
margin-top: 0;
|
|
117
|
+
margin-bottom: 0.25rem;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.form-group,
|
|
122
|
+
.input-container,
|
|
123
|
+
.select-container,
|
|
124
|
+
.form-read-value {
|
|
125
|
+
min-width: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
input:not([type="checkbox"]):not([type="radio"]),
|
|
129
|
+
.form-control-textarea,
|
|
130
|
+
.form-read-value {
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
min-width: 0;
|
|
133
|
+
width: 100%;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
73
136
|
}
|
package/src/form/form.enum.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export enum TypeInputEnum {
|
|
2
|
+
ADDRESS = "address",
|
|
2
3
|
TEXT = "text",
|
|
3
4
|
BUTTON = "button",
|
|
4
5
|
EMAIL = "email",
|
|
@@ -8,7 +9,7 @@ export enum TypeInputEnum {
|
|
|
8
9
|
CHECKBOX = "checkbox",
|
|
9
10
|
RADIO = "radio",
|
|
10
11
|
DATE = "date",
|
|
11
|
-
DATERANGE = "daterange",
|
|
12
12
|
SELECT = "select",
|
|
13
13
|
TEXTAREA = "textarea",
|
|
14
|
+
PRICE = "price",
|
|
14
15
|
}
|