kelt-ui-kit-react 2.0.1 → 2.0.3

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 (115) hide show
  1. package/dist/_core/type/generiqueType.d.ts +4 -0
  2. package/dist/badge/Badge.d.ts +3 -1
  3. package/dist/button/Button.d.ts +1 -1
  4. package/dist/button/button.interface.d.ts +1 -2
  5. package/dist/dataTable/DataTable.d.ts +17 -14
  6. package/dist/dataTable/dataTable.interface.d.ts +24 -19
  7. package/dist/dataTable/dataTableActions/DataTableAction.d.ts +3 -5
  8. package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +13 -7
  9. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.d.ts +10 -0
  10. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.d.ts +9 -0
  11. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.d.ts +9 -0
  12. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.d.ts +13 -0
  13. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.d.ts +9 -0
  14. package/dist/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.d.ts +11 -0
  15. package/dist/dataTable/dataTableFilters/DataTableFilters.d.ts +7 -0
  16. package/dist/dataTable/dataTableLoading/DataTableLoading.d.ts +1 -3
  17. package/dist/dataTable/dataTableRow/DataTableRow.d.ts +5 -6
  18. package/dist/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.d.ts +12 -0
  19. package/dist/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.d.ts +11 -0
  20. package/dist/expands/expand/expand.d.ts +1 -1
  21. package/dist/form/Form.d.ts +1 -2
  22. package/dist/form/form.enum.d.ts +2 -2
  23. package/dist/form/form.interface.d.ts +2 -4
  24. package/dist/form/input/Input.d.ts +3 -0
  25. package/dist/form/inputNumber/InputNumber.d.ts +0 -2
  26. package/dist/grid/col/Col.d.ts +6 -3
  27. package/dist/grid/col/colStyled/ColStyled.d.ts +5 -7
  28. package/dist/grid/grid.interface.d.ts +5 -9
  29. package/dist/header/Header.d.ts +1 -1
  30. package/dist/header/header.interface.d.ts +1 -2
  31. package/dist/icon/Icon.d.ts +1 -1
  32. package/dist/icon/icon.interface.d.ts +1 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +3740 -3443
  35. package/dist/menus/menu/Menu.d.ts +0 -1
  36. package/dist/overlayPanel/OverlayPanel.d.ts +4 -2
  37. package/dist/overlayPanel/overlayPanelStyled/OverlayPanelStyled.d.ts +2 -0
  38. package/dist/select/Select.d.ts +6 -2
  39. package/dist/select/selectOption.interface.d.ts +1 -0
  40. package/dist/style.css +1 -1
  41. package/dist/tabs/Tabs.d.ts +2 -1
  42. package/dist/toaster/store/useToasterStore.d.ts +0 -1
  43. package/index.html +2 -1
  44. package/package/package.json +56 -0
  45. package/package/src/sidebar/Sidebar.tsx +208 -0
  46. package/package/src/sidebar/sidebar.css +104 -0
  47. package/package.json +1 -1
  48. package/src/_core/type/generiqueType.tsx +4 -0
  49. package/src/badge/Badge.tsx +9 -1
  50. package/src/badge/badge.css +4 -0
  51. package/src/button/Button.tsx +1 -3
  52. package/src/button/button.css +2 -2
  53. package/src/button/button.interface.tsx +1 -2
  54. package/src/card/card.css +25 -5
  55. package/src/card/cardAction/CardAction.tsx +4 -4
  56. package/src/damier/Damier.tsx +7 -16
  57. package/src/damier/damier.css +4 -11
  58. package/src/dataTable/DataTable.tsx +685 -542
  59. package/src/dataTable/DataTable.view.tsx +48 -24
  60. package/src/dataTable/dataTable.css +134 -159
  61. package/src/dataTable/dataTable.interface.ts +25 -19
  62. package/src/dataTable/dataTableActions/DataTableAction.tsx +14 -25
  63. package/src/dataTable/dataTableColumn/DataTableColumn.tsx +64 -14
  64. package/src/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.tsx +36 -0
  65. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilter.css +0 -0
  66. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.tsx +39 -0
  67. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.tsx +80 -0
  68. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.tsx +89 -0
  69. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.tsx +37 -0
  70. package/src/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.tsx +42 -0
  71. package/src/dataTable/dataTableFilters/DataTableFilters.tsx +91 -0
  72. package/src/dataTable/dataTableFilters/dataTableFilters.css +16 -0
  73. package/src/dataTable/dataTableLoading/DataTableLoading.tsx +4 -15
  74. package/src/dataTable/dataTableRow/DataTableRow.tsx +64 -180
  75. package/src/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.tsx +80 -0
  76. package/src/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.tsx +123 -0
  77. package/src/expands/Expands.tsx +2 -16
  78. package/src/expands/expand/expand.tsx +5 -122
  79. package/src/expands/expands.css +3 -8
  80. package/src/form/Form.tsx +25 -125
  81. package/src/form/Form.view.tsx +1 -9
  82. package/src/form/form.css +1 -38
  83. package/src/form/form.enum.ts +1 -1
  84. package/src/form/form.interface.tsx +2 -7
  85. package/src/form/input/Input.tsx +61 -7
  86. package/src/form/input/input.css +23 -1
  87. package/src/form/inputNumber/InputNumber.tsx +6 -11
  88. package/src/form/inputPrice/InputPrice.tsx +2 -4
  89. package/src/grid/col/Col.tsx +7 -5
  90. package/src/grid/col/colStyled/ColStyled.tsx +7 -21
  91. package/src/grid/grid.interface.tsx +5 -11
  92. package/src/header/Header.tsx +2 -4
  93. package/src/header/header.css +0 -17
  94. package/src/header/header.interface.tsx +1 -2
  95. package/src/icon/Icon.tsx +6 -2
  96. package/src/icon/icon.interface.tsx +1 -0
  97. package/src/index.css +12 -58
  98. package/src/index.ts +2 -1
  99. package/src/menus/Menus.tsx +0 -1
  100. package/src/menus/menu/Menu.tsx +1 -2
  101. package/src/overlayPanel/OverlayPanel.tsx +71 -18
  102. package/src/overlayPanel/overlay.context.tsx +6 -4
  103. package/src/overlayPanel/overlayPanel.css +10 -1
  104. package/src/overlayPanel/overlayPanelStyled/OverlayPanelStyled.tsx +7 -2
  105. package/src/select/Select.tsx +178 -41
  106. package/src/select/select.css +94 -13
  107. package/src/select/selectOption.interface.ts +1 -0
  108. package/src/sidebar/Sidebar.tsx +5 -107
  109. package/src/sidebar/sidebar.css +1 -18
  110. package/src/tabs/Tabs.tsx +20 -1
  111. package/src/tabs/tabs.css +3 -9
  112. package/src/toaster/Toaster.tsx +2 -7
  113. package/src/toaster/Toaster.view.tsx +0 -1
  114. package/src/toaster/store/useToasterStore.tsx +0 -2
  115. package/src/toaster/toaster.css +2 -7
@@ -39,12 +39,10 @@
39
39
  height: 300px;
40
40
  flex: 1;
41
41
  min-height: 200px;
42
-
43
42
  .expand-header {
44
- background-color: color-mix(in srgb, var(--primary-color), white 5%);
43
+ background-color: #1d3758;
45
44
  color: #fff;
46
45
  }
47
-
48
46
  .expand-header-custom {
49
47
  .expand-header--children {
50
48
  display: flex;
@@ -67,7 +65,6 @@
67
65
  padding: 15px;
68
66
  color: #333;
69
67
  background-color: #fff;
70
-
71
68
  &.expand-header-custom {
72
69
  margin-bottom: 1rem;
73
70
  }
@@ -80,15 +77,13 @@
80
77
  .expand-header-title-left {
81
78
  margin-right: 0.5rem;
82
79
  text-transform: uppercase;
83
- font-size: 18px;
80
+ font-size: 14px;
84
81
  font-weight: 500;
85
82
  }
86
83
  }
87
-
88
84
  & .expand-footer {
89
85
  padding: 15px;
90
86
  }
91
-
92
87
  & .expand-body {
93
88
  display: none;
94
89
  flex: 1;
@@ -99,4 +94,4 @@
99
94
  flex-direction: column;
100
95
  }
101
96
  }
102
- }
97
+ }
package/src/form/Form.tsx CHANGED
@@ -9,38 +9,12 @@ 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 {
13
- FormInterface,
14
- FormMode,
15
- FormValuesInterface,
16
- } from "./form.interface";
12
+ import { FormInterface, FormValuesInterface } from "./form.interface";
17
13
  import { Input } from "./input/Input";
18
- import { InputPrice } from "./inputPrice/InputPrice";
19
14
  import { TextArea } from "./textArea/TextArea";
20
- import { Col } from "../grid/col/Col";
21
- import { Row } from "../grid/row/Row";
22
- import { TypeInputEnum } from "./form.enum";
23
-
24
- const euroFormatter = new Intl.NumberFormat("fr-FR", {
25
- style: "currency",
26
- currency: "EUR",
27
- });
28
-
29
- type FormFieldContainerProps = {
30
- field: FormInterface;
31
- gridEnabled: boolean;
32
- children: React.ReactNode;
33
- };
34
-
35
- const FormFieldContainer = ({
36
- field,
37
- gridEnabled,
38
- children,
39
- }: FormFieldContainerProps) =>
40
- gridEnabled ? <Col {...field.col}>{children}</Col> : <>{children}</>;
41
15
 
42
16
  export type FormProps<
43
- T extends { [key: string]: string | number | boolean | Date | string[] }
17
+ T extends { [key: string]: string | number | boolean | Date | string[] },
44
18
  > = {
45
19
  id?: string;
46
20
  className?: string;
@@ -50,7 +24,6 @@ export type FormProps<
50
24
  children?: React.ReactNode;
51
25
  initialForm: FormInterface[];
52
26
  disabledSubmit?: boolean;
53
- mode?: FormMode;
54
27
  onSubmit?: (values: FormValuesInterface<T>) => void;
55
28
  onChange?: (values: FormValuesInterface<T>) => void;
56
29
  };
@@ -67,7 +40,6 @@ export const DynamicForm = forwardRef(
67
40
  onChange,
68
41
  hideSubmit,
69
42
  disabledSubmit = false,
70
- mode = "edit",
71
43
  className,
72
44
  }: FormProps<T>,
73
45
  ref: React.Ref<{
@@ -77,7 +49,7 @@ export const DynamicForm = forwardRef(
77
49
  getValues?: () => FormValuesInterface<T>;
78
50
  updateFormValue?: (values: FormValuesInterface<T>) => void;
79
51
  setBtnDisabledSubmit?: (disabled: boolean) => void;
80
- }>
52
+ }>,
81
53
  ) => {
82
54
  const [btnDisabledSubmit, setBtnDisabledSubmit] = useState(disabledSubmit);
83
55
  const [values, setValues] = useState<FormValuesInterface<T>>(() => {
@@ -95,7 +67,7 @@ export const DynamicForm = forwardRef(
95
67
  const handleChangeInput = useCallback(
96
68
  (
97
69
  event: React.ChangeEvent<HTMLInputElement>,
98
- forminterface: FormInterface
70
+ forminterface: FormInterface,
99
71
  ) => {
100
72
  const { name, type, checked, value } = event.target;
101
73
  const newValue = type === "checkbox" ? checked : value;
@@ -112,7 +84,7 @@ export const DynamicForm = forwardRef(
112
84
  return updatedValues;
113
85
  });
114
86
  },
115
- [onChange]
87
+ [onChange],
116
88
  );
117
89
  const handleChangeTextArea = useCallback(
118
90
  (name: string, value: string, forminterface: FormInterface) => {
@@ -128,14 +100,14 @@ export const DynamicForm = forwardRef(
128
100
  return updatedValues;
129
101
  });
130
102
  },
131
- [onChange]
103
+ [onChange],
132
104
  );
133
105
  /** Gestion des <select> */
134
106
  const handleChangeSelect = useCallback(
135
107
  (
136
108
  newValue: string | string[],
137
109
  name: string,
138
- forminterface: FormInterface
110
+ forminterface: FormInterface,
139
111
  ) => {
140
112
  if (forminterface.onChange) {
141
113
  forminterface.onChange(newValue, name);
@@ -147,7 +119,7 @@ export const DynamicForm = forwardRef(
147
119
  return updatedValues;
148
120
  });
149
121
  },
150
- [onChange]
122
+ [onChange],
151
123
  );
152
124
 
153
125
  const handleSubmit = useCallback(
@@ -157,7 +129,7 @@ export const DynamicForm = forwardRef(
157
129
  onSubmit(values);
158
130
  }
159
131
  },
160
- [onSubmit, values]
132
+ [onSubmit, values],
161
133
  );
162
134
 
163
135
  const resetForm = () => {
@@ -190,7 +162,7 @@ export const DynamicForm = forwardRef(
190
162
  const getValues = () => values;
191
163
 
192
164
  const updateFormValue: (values: FormValuesInterface<T>) => void = (
193
- values
165
+ values,
194
166
  ) => {
195
167
  setValues((prevValues) => ({
196
168
  ...prevValues,
@@ -207,52 +179,6 @@ export const DynamicForm = forwardRef(
207
179
  setBtnDisabledSubmit,
208
180
  }));
209
181
 
210
- const gridEnabled = initialForm.some((field) => field.col !== undefined);
211
- const FieldsContainer = gridEnabled ? Row : React.Fragment;
212
- const getReadValue = (field: FormInterface): React.ReactNode => {
213
- const value = values[field.name];
214
-
215
- if (field.formatReadValue) {
216
- return field.formatReadValue(value ?? "");
217
- }
218
-
219
- if (field.type === TypeInputEnum.SELECT) {
220
- const selectedValues = Array.isArray(value) ? value : [value];
221
- const labels = selectedValues
222
- .map(
223
- (selectedValue) =>
224
- field.options?.find((option) => option.value === selectedValue)
225
- ?.label ?? selectedValue,
226
- )
227
- .filter((selectedValue) => selectedValue !== undefined);
228
-
229
- return labels.join(", ") || "-";
230
- }
231
-
232
- if (field.type === TypeInputEnum.PRICE) {
233
- const amount = Number(value);
234
- return value === "" || value === undefined || Number.isNaN(amount)
235
- ? "-"
236
- : euroFormatter.format(amount);
237
- }
238
-
239
- if (typeof value === "boolean") {
240
- return value ? "Oui" : "Non";
241
- }
242
-
243
- if (value instanceof Date) {
244
- return value.toLocaleDateString();
245
- }
246
-
247
- if (Array.isArray(value)) {
248
- return value.join(", ") || "-";
249
- }
250
-
251
- return value === undefined || value === null || value === ""
252
- ? "-"
253
- : String(value);
254
- };
255
-
256
182
  return (
257
183
  <form
258
184
  autoComplete={autoComplete ?? "off"}
@@ -260,36 +186,25 @@ export const DynamicForm = forwardRef(
260
186
  className={className ?? ""}
261
187
  >
262
188
  <div className="form-content">
263
- <FieldsContainer>
264
189
  {initialForm.map((v, key) => {
265
190
  const required = v.onRequired
266
191
  ? v.onRequired(v.value)
267
- : v.required ?? false;
192
+ : (v.required ?? false);
268
193
  const disabled = v.onDisabled
269
194
  ? v.onDisabled(v.value)
270
- : v.disabled ?? false;
195
+ : (v.disabled ?? false);
271
196
 
272
197
  return (
273
- <FormFieldContainer key={key} field={v} gridEnabled={gridEnabled}>
274
- <div
275
- className={`d-flex flex-column ${
276
- v.className ?? ""
277
- } form-group form-group-${v.type}`}
278
- >
198
+ <div
199
+ key={key}
200
+ className={`d-flex flex-column ${
201
+ v.className ?? ""
202
+ } form-group form-group-${v.type}`}
203
+ >
279
204
  {v.label && <label>{v.label}</label>}
280
205
  {v.icon && <Icon classIcon={v.icon} />}
281
206
 
282
- {mode === "read" ? (
283
- <div
284
- className={`form-read-value ${
285
- v.type === TypeInputEnum.TEXTAREA
286
- ? "form-read-value--textarea"
287
- : ""
288
- }`}
289
- >
290
- {getReadValue(v)}
291
- </div>
292
- ) : v.type === "select" ? (
207
+ {v.type === "select" ? (
293
208
  <Select
294
209
  id={v.id ?? v.name}
295
210
  label={undefined}
@@ -297,6 +212,8 @@ export const DynamicForm = forwardRef(
297
212
  options={v.options ?? []}
298
213
  disabled={disabled}
299
214
  multiple={v.multiple}
215
+ usePortal={v.usePortal}
216
+ searchable={v.searchable}
300
217
  value={values[v.name] as any}
301
218
  required={required}
302
219
  defaultValue={v.value as string | string[]}
@@ -321,22 +238,6 @@ export const DynamicForm = forwardRef(
321
238
  handleChangeTextArea(name, value, v)
322
239
  }
323
240
  />
324
- ) : v.type === TypeInputEnum.PRICE ? (
325
- <InputPrice
326
- id={v.id ?? v.name}
327
- ref={(el) => (inputRefs.current[v.name] = el)}
328
- name={v.name}
329
- placeholder={v.placeholder ?? ""}
330
- autoComplete={v.autoComplete}
331
- tabIndex={0}
332
- disabled={disabled}
333
- autoFocus={v.focus ?? false}
334
- required={required}
335
- value={String(values[v.name] ?? "")}
336
- onChange={(value) =>
337
- handleChangeTextArea(v.name, value, v)
338
- }
339
- />
340
241
  ) : (
341
242
  <Input
342
243
  id={v.id ?? v.name}
@@ -350,6 +251,7 @@ export const DynamicForm = forwardRef(
350
251
  autoFocus={v.focus ?? false}
351
252
  required={required}
352
253
  step={v.step ?? "0.01"}
254
+ disabledDebounce={true}
353
255
  value={values[v.name] as string | number}
354
256
  checked={
355
257
  v.type === "checkbox" ? !!values[v.name] : undefined
@@ -360,15 +262,13 @@ export const DynamicForm = forwardRef(
360
262
  onInvalid={v.onInvalid ? v.onInvalid : undefined}
361
263
  />
362
264
  )}
363
- </div>
364
- </FormFieldContainer>
265
+ </div>
365
266
  );
366
267
  })}
367
268
  {children}
368
- </FieldsContainer>
369
269
  </div>
370
270
  <div className="d-flex justify-content-end form-footer">
371
- {mode === "edit" && !hideSubmit && (
271
+ {!hideSubmit && (
372
272
  <Button
373
273
  id={`button-${id}`}
374
274
  disabled={btnDisabledSubmit}
@@ -378,5 +278,5 @@ export const DynamicForm = forwardRef(
378
278
  </div>
379
279
  </form>
380
280
  );
381
- }
281
+ },
382
282
  );
@@ -18,14 +18,12 @@ export const FormView = (): JSX.Element => {
18
18
  name: "nom",
19
19
  type: TypeInputEnum.PASSWORD,
20
20
  required: true,
21
- col: { xs: 12, md: 6 },
22
21
  },
23
22
  {
24
23
  value: 30,
25
24
  label: "Age",
26
25
  name: "age",
27
26
  type: TypeInputEnum.NUMBER,
28
- col: { xs: 12, md: 6 },
29
27
  },
30
28
  {
31
29
  value: false,
@@ -57,11 +55,5 @@ export const FormView = (): JSX.Element => {
57
55
  const handleSubmit = (values: FormValuesInterface<FormData>) => {
58
56
  console.log("Form Submitted: ", values);
59
57
  };
60
- return (
61
- <DynamicForm
62
- mode="edit"
63
- onSubmit={handleSubmit}
64
- initialForm={initialValues}
65
- />
66
- );
58
+ return <DynamicForm onSubmit={handleSubmit} initialForm={initialValues} />;
67
59
  };
package/src/form/form.css CHANGED
@@ -6,7 +6,7 @@ input {
6
6
  min-width: 250px;
7
7
 
8
8
  &:focus-visible {
9
- outline: 1px solid #9696a7;
9
+ outline: 0px;
10
10
  }
11
11
  }
12
12
 
@@ -70,41 +70,4 @@ form {
70
70
  button {
71
71
  margin-top: 1rem;
72
72
  }
73
-
74
- .form-read-value {
75
- width: 100%;
76
- min-width: 250px;
77
- min-height: calc(1em + 1rem + 2px);
78
- box-sizing: border-box;
79
- border: 1px solid transparent;
80
- border-radius: 4px;
81
- margin: 0.25rem 0;
82
- padding: 0.5rem 1rem;
83
- font-size: 0.8rem;
84
- line-height: normal;
85
- overflow-wrap: anywhere;
86
- }
87
-
88
- .form-read-value--textarea {
89
- min-height: 3.25rem;
90
- white-space: pre-wrap;
91
- }
92
-
93
- .grid-row {
94
-
95
- .form-group,
96
- .input-container,
97
- .select-container,
98
- .form-read-value {
99
- min-width: 0;
100
- }
101
-
102
- input:not([type="checkbox"]):not([type="radio"]),
103
- .form-control-textarea,
104
- .form-read-value {
105
- box-sizing: border-box;
106
- min-width: 0;
107
- width: 100%;
108
- }
109
- }
110
73
  }
@@ -8,7 +8,7 @@ export enum TypeInputEnum {
8
8
  CHECKBOX = "checkbox",
9
9
  RADIO = "radio",
10
10
  DATE = "date",
11
+ DATERANGE = "daterange",
11
12
  SELECT = "select",
12
13
  TEXTAREA = "textarea",
13
- PRICE = "price",
14
14
  }
@@ -1,7 +1,4 @@
1
1
  import { TypeInputEnum } from "./form.enum";
2
- import { GridBreakpoints } from "../grid/grid.interface";
3
-
4
- export type FormMode = "read" | "edit";
5
2
 
6
3
  export type FormValuesInterface<
7
4
  T extends { [key: string]: string | number | boolean | Date | string[] }
@@ -26,12 +23,10 @@ export interface FormInterface {
26
23
  condition?: FormCondition;
27
24
  icon?: string;
28
25
  multiple?: boolean;
26
+ usePortal?: boolean;
27
+ searchable?: boolean;
29
28
  options?: { value: string; label: string }[]; // <--- pour les <option>
30
29
  className?: string;
31
- col?: GridBreakpoints;
32
- formatReadValue?: (
33
- value: string | number | boolean | Date | string[]
34
- ) => React.ReactNode;
35
30
  autoComplete?: string;
36
31
  step?: string;
37
32
  onChange?: (
@@ -1,4 +1,4 @@
1
- import { forwardRef, useState } from "react";
1
+ import { forwardRef, useRef, useState } from "react";
2
2
  import { Icon } from "../../icon/Icon";
3
3
  import { TypeInputEnum } from "../form.enum";
4
4
  import "./input.css";
@@ -15,12 +15,15 @@ export interface InputProps {
15
15
  disabled?: boolean;
16
16
  autoFocus?: boolean;
17
17
  required?: boolean;
18
+ disabledDebounce?: boolean;
18
19
  minLength?: number;
19
20
  maxLength?: number;
21
+ width?: string;
20
22
  autoComplete?: string;
21
23
  step?: string;
22
24
  tabIndex?: number;
23
25
  checked?: boolean;
26
+ showClearButton?: boolean;
24
27
  handleKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
25
28
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
26
29
  onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
@@ -34,11 +37,13 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
34
37
  name,
35
38
  type = TypeInputEnum.TEXT,
36
39
  value,
40
+ width,
37
41
  onInvalid,
38
42
  placeholder,
39
43
  className,
40
44
  inputMode,
41
45
  minLength,
46
+ disabledDebounce = false,
42
47
  maxLength,
43
48
  autoFocus = false,
44
49
  disabled = false,
@@ -47,18 +52,61 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
47
52
  tabIndex = 0,
48
53
  step,
49
54
  checked,
55
+ showClearButton = false,
50
56
  onChange,
51
57
  onBlur,
52
58
  onClick,
53
59
  handleKeyDown,
54
60
  },
55
- ref
61
+ ref,
56
62
  ) => {
57
63
  // Si le type est password, on utilise l'input de type password
58
64
  const [showPassword, setShowPassword] = useState(false);
65
+ const [localValue, setLocalValue] = useState(value ?? "");
66
+ const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
67
+
68
+ const triggerOnChange = (nextValue: string) => {
69
+ if (!onChange) return;
70
+
71
+ const syntheticEvent = {
72
+ target: { value: nextValue, name, id },
73
+ currentTarget: { value: nextValue, name, id },
74
+ } as unknown as React.ChangeEvent<HTMLInputElement>;
75
+
76
+ onChange(syntheticEvent);
77
+ };
78
+
79
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
80
+ const value = e.target.value; // 👈 capturer avant le setTimeout
81
+ setLocalValue(value);
82
+ if (!onChange) return;
83
+ if (disabledDebounce) {
84
+ onChange(e);
85
+ return;
86
+ }
87
+ if (debounceRef.current) clearTimeout(debounceRef.current);
88
+ debounceRef.current = setTimeout(() => {
89
+ onChange({ ...e, target: { ...e.target, value } }); // 👈 passer la valeur capturée
90
+ }, 300);
91
+ };
92
+
93
+ const handleClear = (e: React.MouseEvent<HTMLDivElement>) => {
94
+ e.stopPropagation();
95
+ if (debounceRef.current) clearTimeout(debounceRef.current);
96
+ setLocalValue("");
97
+ triggerOnChange("");
98
+ };
99
+
100
+ const hasValue = String(localValue ?? "").length > 0;
101
+ const hasClearButtonSlot = showClearButton && type === TypeInputEnum.TEXT;
102
+ const canShowClearButton =
103
+ showClearButton && hasValue && !disabled && type === TypeInputEnum.TEXT;
59
104
 
60
105
  return (
61
- <div className={`input-container ${`input-container-${type}`} `}>
106
+ <div
107
+ className={`input-container ${`input-container-${type}`} `}
108
+ style={{ width }}
109
+ >
62
110
  <input name={`hide-${name}`} type="text" style={{ display: "none" }} />
63
111
 
64
112
  <input
@@ -68,7 +116,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
68
116
  inputMode={inputMode ?? (type === "number" ? "numeric" : "text")}
69
117
  type={showPassword ? TypeInputEnum.TEXT : type}
70
118
  spellCheck="false"
71
- value={value}
119
+ value={localValue}
72
120
  placeholder={placeholder}
73
121
  disabled={disabled}
74
122
  required={required}
@@ -78,7 +126,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
78
126
  autoFocus={autoFocus}
79
127
  minLength={minLength}
80
128
  maxLength={maxLength}
81
- onChange={onChange}
129
+ onChange={handleChange}
82
130
  checked={checked}
83
131
  onKeyDown={handleKeyDown}
84
132
  onBlur={onBlur}
@@ -86,10 +134,16 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
86
134
  e.stopPropagation();
87
135
  onClick && onClick(e);
88
136
  }}
89
- className={`input-field ${className ?? ""}`}
137
+ className={`input-field ${hasClearButtonSlot ? "with-clear-button" : ""} ${className ?? ""}`}
90
138
  onInvalid={onInvalid}
91
139
  />
92
140
 
141
+ {canShowClearButton && (
142
+ <div onClick={handleClear} className="input-clear-icon" role="button">
143
+ <Icon classIcon="bi-x-circle" />
144
+ </div>
145
+ )}
146
+
93
147
  {type === TypeInputEnum.PASSWORD && (
94
148
  <div
95
149
  onClick={() => setShowPassword(!showPassword)}
@@ -100,7 +154,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
100
154
  )}
101
155
  </div>
102
156
  );
103
- }
157
+ },
104
158
  );
105
159
 
106
160
  Input.displayName = "Input";
@@ -2,15 +2,18 @@
2
2
  position: relative;
3
3
  min-width: 250px;
4
4
  width: 100%;
5
+
5
6
  &.input-container-radio,
6
7
  &.input-container-checkbox {
7
8
  min-width: 10px;
8
9
  width: 10px;
10
+
9
11
  input {
10
12
  width: 10px;
11
13
  min-width: 10px;
12
14
  }
13
15
  }
16
+
14
17
  .input-password-icon {
15
18
  position: absolute;
16
19
  right: 10px;
@@ -19,8 +22,27 @@
19
22
  display: flex;
20
23
  align-items: center;
21
24
  }
25
+
26
+ .input-clear-icon {
27
+ position: absolute;
28
+ right: 8px;
29
+ width: 24px;
30
+ height: 100%;
31
+ top: 0;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ cursor: pointer;
36
+ z-index: 2;
37
+ }
38
+
39
+ .with-clear-button {
40
+ padding-right: 40px;
41
+ }
42
+
22
43
  input {
23
44
  width: 100%;
24
45
  font-size: 0.8rem;
46
+ box-sizing: border-box;
25
47
  }
26
- }
48
+ }