kelt-ui-kit-react 1.4.2 → 1.4.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kelt-ui-kit-react",
3
3
  "type": "module",
4
- "version": "1.4.2",
4
+ "version": "1.4.4",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -1,13 +1,16 @@
1
- import { useEffect, useRef } from "react";
2
- import { Button } from "../button/Button";
3
- import { Icon } from "../icon/Icon";
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { TypeInputEnum } from "../form/form.enum";
3
+ import { Input } from "../form/input/Input";
4
4
  import { Loader } from "../loader/Loader";
5
- import { Select } from "../select/Select";
6
5
  import "./dataTable.css";
7
6
  import {
8
7
  dataTableActionsInterface,
9
8
  dataTableColumnsInterface,
10
9
  } from "./dataTable.interface";
10
+ import { DataTableAction } from "./dataTableActions/DataTableAction";
11
+ import { DataTableColumn } from "./dataTableColumn/DataTableColumn";
12
+ import { DataTableLoading } from "./dataTableLoading/DataTableLoading";
13
+ import { DataTableRow } from "./dataTableRow/DataTableRow";
11
14
  export type DataTableProps<T> = {
12
15
  id: string;
13
16
  name?: string;
@@ -23,6 +26,7 @@ export type DataTableProps<T> = {
23
26
  onColumnClick?: (column: any) => void;
24
27
  onLoadMore?: () => void;
25
28
  hasMore?: boolean; // pour savoir s'il reste des données à charger
29
+ onSelectionChange?: (selectedRows: T[]) => void;
26
30
  };
27
31
  export function DataTable<T>(props: DataTableProps<T>) {
28
32
  const {
@@ -39,10 +43,31 @@ export function DataTable<T>(props: DataTableProps<T>) {
39
43
  onColumnClick,
40
44
  onLoadMore,
41
45
  hasMore,
46
+ onSelectionChange,
42
47
  } = props;
43
48
 
44
49
  const loaderRef = useRef<HTMLDivElement | null>(null);
45
-
50
+ const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
51
+ const toggleSelectAll = () => {
52
+ if (selectedRows.size === data.length) {
53
+ setSelectedRows(new Set());
54
+ onSelectionChange?.([]);
55
+ } else {
56
+ const allIndexes = new Set(data.map((_, i) => i));
57
+ setSelectedRows(allIndexes);
58
+ onSelectionChange?.(data);
59
+ }
60
+ };
61
+ const toggleRow = (rowIndex: number) => {
62
+ const newSelection = new Set(selectedRows);
63
+ if (newSelection.has(rowIndex)) {
64
+ newSelection.delete(rowIndex);
65
+ } else {
66
+ newSelection.add(rowIndex);
67
+ }
68
+ setSelectedRows(newSelection);
69
+ onSelectionChange?.(Array.from(newSelection).map((i) => data[i]));
70
+ };
46
71
  useEffect(() => {
47
72
  if (!onLoadMore || !hasMore) return;
48
73
 
@@ -76,21 +101,27 @@ export function DataTable<T>(props: DataTableProps<T>) {
76
101
  <table className={`data-table ${className ? className : ""}`} id={id}>
77
102
  <thead>
78
103
  <tr>
104
+ {onSelectionChange && (
105
+ <th
106
+ className="select-cell"
107
+ style={{ width: "20px", textAlign: "center" }}
108
+ >
109
+ <Input
110
+ id={`select-th-${id}`}
111
+ name={`select-th-${id}`}
112
+ type={TypeInputEnum.CHECKBOX}
113
+ checked={data.length > 0 && selectedRows.size === data.length}
114
+ onChange={toggleSelectAll}
115
+ />
116
+ </th>
117
+ )}
79
118
  {columns.map((column, idx) => (
80
- <>
81
- <th
82
- key={`colonne-${column.id}-${idx}`}
83
- style={{
84
- minWidth: column.minWidth,
85
- maxWidth: column.maxWidth,
86
- width: column.width,
87
- textAlign: column.align || "left",
88
- }}
89
- onClick={() => onColumnClick?.(column)}
90
- >
91
- {column.label}
92
- </th>
93
- </>
119
+ <DataTableColumn
120
+ key={idx}
121
+ column={column}
122
+ idx={idx}
123
+ onColumnClick={onColumnClick}
124
+ />
94
125
  ))}
95
126
  {actions.length > 0 && (
96
127
  <th
@@ -121,112 +152,42 @@ export function DataTable<T>(props: DataTableProps<T>) {
121
152
  {!loading &&
122
153
  data.map((row, rowIndex) => (
123
154
  <tr key={rowIndex} onClick={() => onRowClick?.(row)}>
124
- {columns.map((column, idx) => {
125
- const value = (row as any)[column.id]; // On utilise [column.id] pour accéder aux bonnes propriétés
126
- const style = column.onRowStyle ? column.onRowStyle(row) : {};
127
- return (
128
- <>
129
- <td
130
- style={{
131
- minWidth: column.minWidth,
132
- textAlign: column.align || "left",
133
- maxWidth: column.maxWidth,
134
- width: column.width,
135
- ...style,
136
- }}
137
- key={`row-${column.id}-${idx}`}
138
- >
139
- {column.editable ? (
140
- column.type === "select" ? (
141
- <Select
142
- options={column.options ?? []}
143
- value={value}
144
- onChange={(e) => {
145
- if (column.onEdit) {
146
- column.onEdit(e, row);
147
- }
148
- }}
149
- />
150
- ) : (
151
- <input
152
- type={column.type}
153
- value={value}
154
- step="0.01"
155
- style={{ textAlign: column.align || "left" }}
156
- onChange={(e) => {
157
- if (column.onEdit) {
158
- column.onEdit(e.target.value, row);
159
- }
160
- }}
161
- />
162
- )
163
- ) : column.format ? (
164
- column.format(value)
165
- ) : (
166
- value
167
- )}
168
- </td>
169
- </>
170
- );
171
- })}
155
+ {onSelectionChange && (
156
+ <td className="select-cell" style={{ textAlign: "center" }}>
157
+ <Input
158
+ id={`select-row-${rowIndex}`}
159
+ name={`select-row-${rowIndex}`}
160
+ type={TypeInputEnum.CHECKBOX}
161
+ checked={selectedRows.has(rowIndex)}
162
+ onChange={() => toggleRow(rowIndex)} // empêche le clic de déclencher onRowClick
163
+ />
164
+ </td>
165
+ )}
166
+ {columns.map((column, idx) => (
167
+ <DataTableRow column={column} idx={idx} row={row} key={idx} />
168
+ ))}
172
169
  {actions.length > 0 && (
173
170
  <td align="right">
174
171
  <div className="d-flex align-items-center justify-content-end">
175
- {actions.map((action, index) => {
176
- let title = action.label;
177
- const display = action.shouldDisplay
178
- ? action.shouldDisplay(row)
179
- : true;
180
- if (!display) return null;
181
- if (action.labelButton) {
182
- title = action.labelButton
183
- ? action.labelButton(row)
184
- : action.label;
185
- }
186
- return (
187
- <>
188
- {action.type === "button" && (
189
- <Button
190
- title={title}
191
- key={`button-action-${action.id}-${index}`}
192
- onClick={() => action.onClick(row)}
193
- disabled={action.disabled}
194
- className={`data-table-action button--tertiary mr-2 w-100 ${
195
- action.classIcon ? action.classIcon : ""
196
- }`}
197
- />
198
- )}
199
- {action.type === "icon" && (
200
- <div
201
- key={`icon-action-${action.id}-${index}`}
202
- className={`mr-2 `}
203
- onClick={() => action.onClick(row)}
204
- >
205
- <Icon
206
- classIcon={action.classIcon ?? ""}
207
- key={action.id}
208
- />
209
- </div>
210
- )}
211
- </>
212
- );
213
- })}
172
+ {actions.map((action, index) => (
173
+ <DataTableAction
174
+ key={index}
175
+ action={action}
176
+ row={row}
177
+ index={index}
178
+ />
179
+ ))}
214
180
  </div>
215
181
  </td>
216
182
  )}
217
183
  </tr>
218
184
  ))}
219
185
  {!loading && data.length === 0 && (
220
- <tr>
221
- <td
222
- align="center"
223
- colSpan={columns.length + (actions.length > 0 ? 1 : 0)}
224
- >
225
- <div className="mt-3">
226
- {props.emptyMessage || "Aucune donnée disponible"}
227
- </div>
228
- </td>
229
- </tr>
186
+ <DataTableLoading
187
+ columns={columns}
188
+ actions={actions}
189
+ emptyMessage={props.emptyMessage}
190
+ />
230
191
  )}
231
192
  </tbody>
232
193
  </table>
@@ -54,6 +54,9 @@ export const DataTableView = () => {
54
54
  columns={dataTableColumns}
55
55
  id="datatable-example"
56
56
  name={"DataTable"}
57
+ onSelectionChange={(selectedRows) => {
58
+ console.log("Selected Rows: ", selectedRows);
59
+ }}
57
60
  />
58
61
  );
59
62
  };
@@ -5,7 +5,16 @@
5
5
  border-spacing: 5px;
6
6
  font-size: 14px;
7
7
  color: #333;
8
-
8
+ .select-cell {
9
+ min-width: 40px !important;
10
+ .input-container {
11
+ min-width: 20px;
12
+ input {
13
+ min-width: 20px;
14
+ width: 20px;
15
+ }
16
+ }
17
+ }
9
18
  thead {
10
19
  margin-bottom: 1rem;
11
20
  position: sticky;
@@ -0,0 +1,44 @@
1
+ import { Button } from "../../button/Button";
2
+ import { Icon } from "../../icon/Icon";
3
+ import { dataTableActionsInterface } from "../dataTable.interface";
4
+ type DataTableActionProps = {
5
+ action: dataTableActionsInterface<any>;
6
+ row: any;
7
+ index?: number;
8
+ };
9
+ export const DataTableAction = ({
10
+ action,
11
+ row,
12
+ index,
13
+ }: DataTableActionProps) => {
14
+ let title = action.label;
15
+ const display = action.shouldDisplay ? action.shouldDisplay(row) : true;
16
+ if (!display) return null;
17
+ if (action.labelButton) {
18
+ title = action.labelButton ? action.labelButton(row) : action.label;
19
+ }
20
+ return (
21
+ <>
22
+ {action.type === "button" && (
23
+ <Button
24
+ title={title}
25
+ key={`button-action-${action.id}-${index}`}
26
+ onClick={() => action.onClick(row)}
27
+ disabled={action.disabled}
28
+ className={`data-table-action button--tertiary mr-2 w-100 ${
29
+ action.classIcon ? action.classIcon : ""
30
+ }`}
31
+ />
32
+ )}
33
+ {action.type === "icon" && (
34
+ <div
35
+ key={`icon-action-${action.id}-${index}`}
36
+ className={`mr-2 `}
37
+ onClick={() => action.onClick(row)}
38
+ >
39
+ <Icon classIcon={action.classIcon ?? ""} key={action.id} />
40
+ </div>
41
+ )}
42
+ </>
43
+ );
44
+ };
@@ -0,0 +1,27 @@
1
+ import { dataTableColumnsInterface } from "../dataTable.interface";
2
+
3
+ type DataTableColumnProps = {
4
+ column: dataTableColumnsInterface<any>;
5
+ idx: number;
6
+ onColumnClick?: (column: dataTableColumnsInterface<any>) => void;
7
+ };
8
+ export const DataTableColumn = ({
9
+ column,
10
+ idx,
11
+ onColumnClick,
12
+ }: DataTableColumnProps) => {
13
+ return (
14
+ <th
15
+ key={`colonne-${column.id}-${idx}`}
16
+ style={{
17
+ minWidth: column.minWidth,
18
+ maxWidth: column.maxWidth,
19
+ width: column.width,
20
+ textAlign: column.align || "left",
21
+ }}
22
+ onClick={() => onColumnClick?.(column)}
23
+ >
24
+ {column.label}
25
+ </th>
26
+ );
27
+ };
@@ -0,0 +1,21 @@
1
+ type DataTableLoadingProps = {
2
+ columns: any[];
3
+ actions: any[];
4
+ emptyMessage?: string;
5
+ };
6
+ export const DataTableLoading = ({
7
+ columns,
8
+ actions,
9
+ emptyMessage,
10
+ }: DataTableLoadingProps) => {
11
+ return (
12
+ <tr>
13
+ <td
14
+ align="center"
15
+ colSpan={columns.length + (actions.length > 0 ? 1 : 0)}
16
+ >
17
+ <div className="mt-3">{emptyMessage || "Aucune donnée disponible"}</div>
18
+ </td>
19
+ </tr>
20
+ );
21
+ };
@@ -0,0 +1,54 @@
1
+ import { Select } from "../../select/Select";
2
+ import { dataTableColumnsInterface } from "../dataTable.interface";
3
+
4
+ type DataTableRowProps = {
5
+ column: dataTableColumnsInterface<any>;
6
+ idx: number;
7
+ row: any;
8
+ };
9
+ export const DataTableRow = ({ column, idx, row }: DataTableRowProps) => {
10
+ const value = (row as any)[column.id]; // On utilise [column.id] pour accéder aux bonnes propriétés
11
+ const style = column.onRowStyle ? column.onRowStyle(row) : {};
12
+ return (
13
+ <td
14
+ style={{
15
+ minWidth: column.minWidth,
16
+ textAlign: column.align || "left",
17
+ maxWidth: column.maxWidth,
18
+ width: column.width,
19
+ ...style,
20
+ }}
21
+ key={`row-${column.id}-${idx}`}
22
+ >
23
+ {column.editable ? (
24
+ column.type === "select" ? (
25
+ <Select
26
+ options={column.options ?? []}
27
+ value={value}
28
+ onChange={(e) => {
29
+ if (column.onEdit) {
30
+ column.onEdit(e, row);
31
+ }
32
+ }}
33
+ />
34
+ ) : (
35
+ <input
36
+ type={column.type}
37
+ value={value}
38
+ step="0.01"
39
+ style={{ textAlign: column.align || "left" }}
40
+ onChange={(e) => {
41
+ if (column.onEdit) {
42
+ column.onEdit(e.target.value, row);
43
+ }
44
+ }}
45
+ />
46
+ )
47
+ ) : column.format ? (
48
+ column.format(value)
49
+ ) : (
50
+ value
51
+ )}
52
+ </td>
53
+ );
54
+ };
package/src/form/form.css CHANGED
@@ -8,6 +8,14 @@ input {
8
8
  outline: 1px solid #9696a7;
9
9
  }
10
10
  }
11
+ input[type="checkbox"],
12
+ input[type="radio"] {
13
+ min-width: auto;
14
+ padding: 0;
15
+ margin: 0;
16
+ height: 18px;
17
+ width: 18px;
18
+ }
11
19
  form {
12
20
  &.form-row {
13
21
  display: flex;
@@ -54,7 +54,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
54
54
  const [showPassword, setShowPassword] = useState(false);
55
55
 
56
56
  return (
57
- <div className="input-container">
57
+ <div className={`input-container ${`input-container-${type}`} `}>
58
58
  <input name={`hide-${name}`} type="text" style={{ display: "none" }} />
59
59
 
60
60
  <input
@@ -2,6 +2,15 @@
2
2
  position: relative;
3
3
  min-width: 250px;
4
4
  width: 100%;
5
+ &.input-container-radio,
6
+ &.input-container-checkbox {
7
+ min-width: 10px;
8
+ width: 10px;
9
+ input {
10
+ width: 10px;
11
+ min-width: 10px;
12
+ }
13
+ }
5
14
  .input-password-icon {
6
15
  position: absolute;
7
16
  right: 10px;