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.
Files changed (127) hide show
  1. package/README.md +26 -0
  2. package/dist/addressSearch/AddressSearch.d.ts +20 -0
  3. package/dist/addressSearch/AddressSearch.view.d.ts +1 -0
  4. package/dist/badge/Badge.d.ts +1 -3
  5. package/dist/button/Button.d.ts +1 -1
  6. package/dist/button/button.interface.d.ts +2 -1
  7. package/dist/card/Card.d.ts +4 -1
  8. package/dist/dataTable/DataTable.d.ts +14 -17
  9. package/dist/dataTable/dataTable.interface.d.ts +19 -24
  10. package/dist/dataTable/dataTableActions/DataTableAction.d.ts +5 -3
  11. package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +7 -13
  12. package/dist/dataTable/dataTableLoading/DataTableLoading.d.ts +3 -1
  13. package/dist/dataTable/dataTableRow/DataTableRow.d.ts +6 -5
  14. package/dist/expands/Expands.d.ts +1 -0
  15. package/dist/expands/expand/expand.d.ts +3 -2
  16. package/dist/form/Form.d.ts +10 -2
  17. package/dist/form/form.enum.d.ts +3 -2
  18. package/dist/form/form.interface.d.ts +22 -3
  19. package/dist/form/input/Input.d.ts +0 -3
  20. package/dist/form/inputNumber/InputNumber.d.ts +2 -0
  21. package/dist/geocoding/GeocodingService.d.ts +13 -0
  22. package/dist/geocoding/geocoding.interface.d.ts +20 -0
  23. package/dist/grid/Grid.d.ts +5 -2
  24. package/dist/grid/col/Col.d.ts +3 -6
  25. package/dist/grid/col/colStyled/ColStyled.d.ts +8 -5
  26. package/dist/grid/grid.interface.d.ts +20 -5
  27. package/dist/grid/row/Row.d.ts +7 -0
  28. package/dist/header/Header.d.ts +1 -1
  29. package/dist/header/header.interface.d.ts +2 -1
  30. package/dist/icon/Icon.d.ts +1 -1
  31. package/dist/icon/icon.interface.d.ts +0 -1
  32. package/dist/index.d.ts +5 -1
  33. package/dist/index.js +3724 -3628
  34. package/dist/menus/menu/Menu.d.ts +1 -0
  35. package/dist/select/Select.d.ts +2 -1
  36. package/dist/style.css +1 -1
  37. package/dist/tabs/Tabs.d.ts +1 -2
  38. package/dist/toaster/store/useToasterStore.d.ts +1 -0
  39. package/index.html +1 -2
  40. package/package.json +1 -1
  41. package/src/App.menu.tsx +8 -0
  42. package/src/addressSearch/AddressSearch.tsx +246 -0
  43. package/src/addressSearch/AddressSearch.view.tsx +52 -0
  44. package/src/addressSearch/addressSearch.css +133 -0
  45. package/src/badge/Badge.tsx +1 -9
  46. package/src/badge/badge.css +0 -4
  47. package/src/button/Button.tsx +3 -1
  48. package/src/button/button.css +2 -2
  49. package/src/button/button.interface.tsx +2 -1
  50. package/src/card/Card.tsx +37 -11
  51. package/src/card/card.css +7 -28
  52. package/src/card/cardAction/CardAction.tsx +4 -4
  53. package/src/damier/Damier.tsx +16 -7
  54. package/src/damier/damier.css +11 -4
  55. package/src/dataTable/DataTable.tsx +542 -685
  56. package/src/dataTable/DataTable.view.tsx +24 -48
  57. package/src/dataTable/dataTable.css +159 -134
  58. package/src/dataTable/dataTable.interface.ts +19 -25
  59. package/src/dataTable/dataTableActions/DataTableAction.tsx +25 -14
  60. package/src/dataTable/dataTableColumn/DataTableColumn.tsx +14 -64
  61. package/src/dataTable/dataTableLoading/DataTableLoading.tsx +15 -4
  62. package/src/dataTable/dataTableRow/DataTableRow.tsx +180 -64
  63. package/src/expands/Expands.tsx +18 -2
  64. package/src/expands/expand/expand.tsx +128 -5
  65. package/src/expands/expands.css +8 -3
  66. package/src/form/Form.tsx +294 -99
  67. package/src/form/Form.view.tsx +9 -1
  68. package/src/form/form.css +64 -1
  69. package/src/form/form.enum.ts +2 -1
  70. package/src/form/form.interface.tsx +31 -3
  71. package/src/form/input/Input.tsx +7 -61
  72. package/src/form/input/input.css +1 -19
  73. package/src/form/inputNumber/InputNumber.tsx +11 -6
  74. package/src/form/inputPrice/InputPrice.tsx +4 -2
  75. package/src/geocoding/GeocodingService.ts +89 -0
  76. package/src/geocoding/geocoding.interface.ts +25 -0
  77. package/src/grid/Grid.tsx +34 -21
  78. package/src/grid/col/Col.tsx +24 -15
  79. package/src/grid/col/col.css +3 -0
  80. package/src/grid/col/colStyled/ColStyled.tsx +59 -41
  81. package/src/grid/container/container.css +2 -2
  82. package/src/grid/grid.interface.tsx +25 -5
  83. package/src/grid/row/Row.tsx +49 -12
  84. package/src/grid/row/row.css +11 -3
  85. package/src/header/Header.tsx +4 -2
  86. package/src/header/header.css +17 -0
  87. package/src/header/header.interface.tsx +2 -1
  88. package/src/icon/Icon.tsx +2 -6
  89. package/src/icon/icon.interface.tsx +0 -1
  90. package/src/index.css +58 -12
  91. package/src/index.ts +12 -2
  92. package/src/menus/Menus.tsx +1 -0
  93. package/src/menus/menu/Menu.tsx +2 -1
  94. package/src/select/Select.tsx +5 -3
  95. package/src/sidebar/Sidebar.tsx +107 -5
  96. package/src/sidebar/sidebar.css +18 -1
  97. package/src/tabs/Tabs.tsx +1 -20
  98. package/src/tabs/tabs.css +9 -3
  99. package/src/toaster/Toaster.tsx +7 -2
  100. package/src/toaster/Toaster.view.tsx +1 -0
  101. package/src/toaster/store/useToasterStore.tsx +2 -0
  102. package/src/toaster/toaster.css +7 -2
  103. package/dist/_core/type/generiqueType.d.ts +0 -4
  104. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.d.ts +0 -10
  105. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.d.ts +0 -9
  106. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.d.ts +0 -9
  107. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.d.ts +0 -13
  108. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.d.ts +0 -9
  109. package/dist/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.d.ts +0 -11
  110. package/dist/dataTable/dataTableFilters/DataTableFilters.d.ts +0 -7
  111. package/dist/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.d.ts +0 -12
  112. package/dist/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.d.ts +0 -11
  113. package/package/package.json +0 -56
  114. package/package/src/sidebar/Sidebar.tsx +0 -208
  115. package/package/src/sidebar/sidebar.css +0 -104
  116. package/src/_core/type/generiqueType.tsx +0 -4
  117. package/src/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.tsx +0 -36
  118. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilter.css +0 -0
  119. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.tsx +0 -39
  120. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.tsx +0 -80
  121. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.tsx +0 -89
  122. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.tsx +0 -37
  123. package/src/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.tsx +0 -42
  124. package/src/dataTable/dataTableFilters/DataTableFilters.tsx +0 -91
  125. package/src/dataTable/dataTableFilters/dataTableFilters.css +0 -16
  126. package/src/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.tsx +0 -80
  127. 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 { FormInterface, FormValuesInterface } from "./form.interface";
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: FormInterface[];
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
- initialForm.forEach((field) => {
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
- initialForm.forEach((field) => {
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 = initialForm.find((field) => field.focus);
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
- return (
183
- <form
184
- autoComplete={autoComplete ?? "off"}
185
- onSubmit={handleSubmit}
186
- className={className ?? ""}
187
- >
188
- <div className="form-content">
189
- {initialForm.map((v, key) => {
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
- : (v.required ?? false);
310
+ : v.required ?? false;
193
311
  const disabled = v.onDisabled
194
312
  ? v.onDisabled(v.value)
195
- : (v.disabled ?? false);
313
+ : v.disabled ?? false;
196
314
 
197
315
  return (
198
- <div
199
- key={key}
200
- className={`d-flex flex-column ${
201
- v.className ?? ""
202
- } form-group form-group-${v.type}`}
316
+ <FormFieldContainer
317
+ key={v.name || key}
318
+ field={v}
319
+ gridEnabled={gridEnabled}
203
320
  >
204
- {v.label && <label>{v.label}</label>}
205
- {v.icon && <Icon classIcon={v.icon} />}
206
-
207
- {v.type === "select" ? (
208
- <Select
209
- id={v.id ?? v.name}
210
- label={undefined}
211
- name={v.name}
212
- options={v.options ?? []}
213
- disabled={disabled}
214
- multiple={v.multiple}
215
- usePortal={v.usePortal}
216
- searchable={v.searchable}
217
- value={values[v.name] as any}
218
- required={required}
219
- defaultValue={v.value as string | string[]}
220
- placeholder={v.placeholder}
221
- onChangeMultiple={(newValue) =>
222
- handleChangeSelect(newValue, v.name, v)
223
- }
224
- onChange={(newValue) =>
225
- handleChangeSelect(newValue, v.name, v)
226
- }
227
- />
228
- ) : v.type === "textarea" ? (
229
- <TextArea
230
- id={v.id ?? v.name}
231
- name={v.name}
232
- className="textarea-input"
233
- placeholder={v.placeholder ?? ""}
234
- disabled={disabled}
235
- required={required}
236
- value={values[v.name] as string}
237
- onChange={(value, name) =>
238
- handleChangeTextArea(name, value, v)
239
- }
240
- />
241
- ) : (
242
- <Input
243
- id={v.id ?? v.name}
244
- ref={(el) => (inputRefs.current[v.name] = el)}
245
- name={v.name}
246
- type={v.type}
247
- placeholder={v.placeholder ?? ""}
248
- autoComplete={v.autoComplete}
249
- tabIndex={0}
250
- disabled={disabled}
251
- autoFocus={v.focus ?? false}
252
- required={required}
253
- step={v.step ?? "0.01"}
254
- disabledDebounce={true}
255
- value={values[v.name] as string | number}
256
- checked={
257
- v.type === "checkbox" ? !!values[v.name] : undefined
258
- }
259
- onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
260
- handleChangeInput(e, v)
261
- }
262
- onInvalid={v.onInvalid ? v.onInvalid : undefined}
263
- />
264
- )}
265
- </div>
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
- {!hideSubmit && (
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
  );
@@ -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 <DynamicForm onSubmit={handleSubmit} initialForm={initialValues} />;
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: 0px;
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
  }
@@ -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
  }