@sio-group/ui-datatable 0.1.0

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 (45) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +429 -0
  3. package/dist/index.cjs +647 -0
  4. package/dist/index.d.cts +83 -0
  5. package/dist/index.d.ts +83 -0
  6. package/dist/index.js +620 -0
  7. package/dist/styles/index.css +154 -0
  8. package/dist/styles/index.css.map +1 -0
  9. package/package.json +44 -0
  10. package/src/assets/scss/index.scss +170 -0
  11. package/src/assets/scss/tokens/_color.scss +19 -0
  12. package/src/assets/scss/tokens/_datatable.scss +10 -0
  13. package/src/components/ActionCell.tsx +88 -0
  14. package/src/components/DataTable.tsx +85 -0
  15. package/src/components/DataTableBody.tsx +34 -0
  16. package/src/components/DataTableControls.tsx +35 -0
  17. package/src/components/DataTableHeader.tsx +59 -0
  18. package/src/components/DefaultSortIcon.tsx +13 -0
  19. package/src/components/TableCell.tsx +17 -0
  20. package/src/components/cell-types/BooleanCell.tsx +29 -0
  21. package/src/components/cell-types/DateCell.tsx +28 -0
  22. package/src/components/cell-types/EmptyCell.tsx +3 -0
  23. package/src/components/cell-types/InlineInputCell.tsx +129 -0
  24. package/src/hooks/useDataTable.ts +113 -0
  25. package/src/index.ts +14 -0
  26. package/src/types/action-cell-props.d.ts +9 -0
  27. package/src/types/action-menu.d.ts +15 -0
  28. package/src/types/column.d.ts +10 -0
  29. package/src/types/data-table-body-props.d.ts +16 -0
  30. package/src/types/data-table-header-props.d.ts +11 -0
  31. package/src/types/data-table-props.d.ts +32 -0
  32. package/src/types/entity.d.ts +4 -0
  33. package/src/types/form-field.d.ts +8 -0
  34. package/src/types/index.ts +11 -0
  35. package/src/types/pagination-meta.d.ts +7 -0
  36. package/src/types/sort-state.d.ts +6 -0
  37. package/src/types/table-cell-props.d.ts +9 -0
  38. package/src/types/use-data-table-props.d.ts +14 -0
  39. package/src/types/use-data-table-return.d.ts +14 -0
  40. package/src/utils/is-pill-value.ts +7 -0
  41. package/src/utils/render-object.tsx +18 -0
  42. package/src/utils/render-value.tsx +89 -0
  43. package/tsconfig.json +17 -0
  44. package/tsup.config.ts +8 -0
  45. package/vitest.config.ts +9 -0
package/dist/index.js ADDED
@@ -0,0 +1,620 @@
1
+ // src/components/DefaultSortIcon.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ var DefaultSortIcon = ({
4
+ direction,
5
+ active
6
+ }) => /* @__PURE__ */ jsx(
7
+ "span",
8
+ {
9
+ style: { opacity: active ? 1 : 0.3, fontSize: "0.7em" },
10
+ "aria-hidden": "true",
11
+ children: direction === "asc" ? "\u25B2" : "\u25BC"
12
+ }
13
+ );
14
+
15
+ // src/components/DataTableHeader.tsx
16
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
17
+ var DataTableHeader = ({
18
+ columns,
19
+ onSort,
20
+ sortValue,
21
+ hasActionMenu,
22
+ renderSortIcon
23
+ }) => {
24
+ const handleSort = (column) => {
25
+ if (!onSort) return;
26
+ if (!sortValue || sortValue.name !== column.name) {
27
+ onSort({ name: column.name, direction: "asc" });
28
+ } else if (sortValue.direction === "asc") {
29
+ onSort({ ...sortValue, direction: "desc" });
30
+ } else {
31
+ onSort(null);
32
+ }
33
+ };
34
+ return /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
35
+ hasActionMenu && /* @__PURE__ */ jsx2("th", { "aria-label": "Actions" }),
36
+ columns.map((column) => /* @__PURE__ */ jsx2(
37
+ "th",
38
+ {
39
+ onClick: () => column.sort && handleSort(column),
40
+ className: [column.className, column.sort ? "sort" : null].filter(Boolean).join(" "),
41
+ style: column.style,
42
+ children: /* @__PURE__ */ jsxs("span", { children: [
43
+ /* @__PURE__ */ jsx2("span", { className: "label", children: column.label }),
44
+ column.sort && /* @__PURE__ */ jsx2("span", { className: "icons", children: renderSortIcon ? /* @__PURE__ */ jsxs(Fragment, { children: [
45
+ renderSortIcon("asc", sortValue?.name === column.name && sortValue?.direction === "asc"),
46
+ renderSortIcon("desc", sortValue?.name === column.name && sortValue?.direction === "desc")
47
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
48
+ /* @__PURE__ */ jsx2(
49
+ DefaultSortIcon,
50
+ {
51
+ direction: "asc",
52
+ active: sortValue?.name === column.name && sortValue?.direction === "asc"
53
+ }
54
+ ),
55
+ /* @__PURE__ */ jsx2(
56
+ DefaultSortIcon,
57
+ {
58
+ direction: "desc",
59
+ active: sortValue?.name === column.name && sortValue?.direction === "desc"
60
+ }
61
+ )
62
+ ] }) })
63
+ ] })
64
+ },
65
+ String(column.name)
66
+ ))
67
+ ] }) });
68
+ };
69
+
70
+ // src/hooks/useDataTable.ts
71
+ import { useMemo, useState } from "react";
72
+ var useDataTable = ({
73
+ data,
74
+ pagination,
75
+ onSearch,
76
+ onSort,
77
+ onPaginate,
78
+ searchValue,
79
+ sortValue,
80
+ clientPageSize,
81
+ clientSearchKeys
82
+ }) => {
83
+ const isControlled = pagination !== void 0;
84
+ const showPagination = isControlled ? !!onPaginate : !!clientPageSize;
85
+ const showSearch = isControlled ? !!onSearch : !!clientSearchKeys?.length;
86
+ const [clientSearch, setClientSearch] = useState("");
87
+ const [clientSort, setClientSort] = useState(null);
88
+ const [clientPage, setClientPage] = useState(1);
89
+ const handleSearch = (query) => {
90
+ if (isControlled) {
91
+ onSearch?.(query);
92
+ } else {
93
+ setClientSearch(query);
94
+ setClientPage(1);
95
+ }
96
+ };
97
+ const handleSort = (sort) => {
98
+ if (isControlled) {
99
+ onSort?.(sort);
100
+ } else {
101
+ setClientSort(sort);
102
+ }
103
+ };
104
+ const handlePaginate = (page) => {
105
+ if (isControlled) {
106
+ onPaginate?.(page);
107
+ } else {
108
+ setClientPage(page);
109
+ }
110
+ };
111
+ const processedData = useMemo(() => {
112
+ if (isControlled) return data;
113
+ let result = [...data];
114
+ if (clientSearch && clientSearchKeys?.length) {
115
+ result = result.filter(
116
+ (item) => clientSearchKeys.some(
117
+ (key) => String(item[key]).toLowerCase().includes(clientSearch.toLowerCase())
118
+ )
119
+ );
120
+ }
121
+ if (clientSort) {
122
+ result.sort((a, b) => {
123
+ const aVal = String(a[clientSort.name]);
124
+ const bVal = String(b[clientSort.name]);
125
+ return clientSort.direction === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
126
+ });
127
+ }
128
+ return result;
129
+ }, [data, clientSearch, clientSort, isControlled]);
130
+ const paginationMeta = useMemo(() => {
131
+ if (isControlled || !clientPageSize) return pagination;
132
+ const pageSize = clientPageSize;
133
+ const total = processedData.length;
134
+ const pageCount = Math.ceil(total / pageSize);
135
+ const from = (clientPage - 1) * pageSize;
136
+ return {
137
+ currentPage: clientPage,
138
+ pageCount,
139
+ total,
140
+ from: from + 1,
141
+ to: Math.min(from + pageSize, total)
142
+ };
143
+ }, [isControlled, pagination, processedData, clientPage, clientPageSize]);
144
+ const pagedData = useMemo(() => {
145
+ if (isControlled || !clientPageSize) return data;
146
+ const pageSize = clientPageSize;
147
+ const from = (clientPage - 1) * pageSize;
148
+ return processedData.slice(from, from + pageSize);
149
+ }, [isControlled, processedData, clientPage, clientPageSize]);
150
+ return {
151
+ pagedData,
152
+ paginationMeta,
153
+ showPagination,
154
+ showSearch,
155
+ handleSearch,
156
+ handleSort,
157
+ handlePaginate,
158
+ currentSort: isControlled ? sortValue : clientSort,
159
+ currentSearch: isControlled ? searchValue : clientSearch
160
+ };
161
+ };
162
+
163
+ // src/components/DataTable.tsx
164
+ import { Pagination } from "@sio-group/ui-pagination";
165
+
166
+ // src/components/ActionCell.tsx
167
+ import { Button } from "@sio-group/ui-core";
168
+ import { useEffect, useRef, useState as useState2 } from "react";
169
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
170
+ var ActionCell = ({
171
+ actionMenu,
172
+ item,
173
+ renderMenuIcon
174
+ }) => {
175
+ const dropdownRef = useRef(null);
176
+ const triggerRef = useRef(null);
177
+ const [isOpen, setIsOpen] = useState2(false);
178
+ const positionDropdown = () => {
179
+ if (dropdownRef.current && triggerRef.current) {
180
+ const rect = triggerRef.current.getBoundingClientRect();
181
+ dropdownRef.current.style.left = `${rect.left}px`;
182
+ dropdownRef.current.style.top = `${rect.top}px`;
183
+ }
184
+ };
185
+ useEffect(() => {
186
+ if (!isOpen) return;
187
+ const handleOutside = (e) => {
188
+ if (dropdownRef.current?.contains(e.target)) return;
189
+ setIsOpen(false);
190
+ };
191
+ document.addEventListener("mousedown", handleOutside, false);
192
+ document.addEventListener("wheel", handleOutside, false);
193
+ return () => {
194
+ document.removeEventListener("mousedown", handleOutside, false);
195
+ document.removeEventListener("wheel", handleOutside, false);
196
+ };
197
+ }, [isOpen]);
198
+ const renderButton = ({ label, name, icon, onClick }, showLabel) => /* @__PURE__ */ jsxs2(
199
+ Button,
200
+ {
201
+ onClick: () => onClick(item),
202
+ variant: "secondary",
203
+ className: "",
204
+ ariaLabel: label,
205
+ children: [
206
+ icon && /* @__PURE__ */ jsx3("span", { children: icon }),
207
+ showLabel && /* @__PURE__ */ jsx3("span", { children: label })
208
+ ]
209
+ },
210
+ name
211
+ );
212
+ return /* @__PURE__ */ jsx3("td", { className: "action-cel", children: actionMenu?.type === "inline" ? /* @__PURE__ */ jsx3("div", { className: "action-group", children: actionMenu.actions.map((action) => renderButton(action, false)) }) : /* @__PURE__ */ jsxs2(
213
+ "section",
214
+ {
215
+ className: "action-dropdown",
216
+ ref: triggerRef,
217
+ children: [
218
+ /* @__PURE__ */ jsx3(
219
+ "button",
220
+ {
221
+ className: "btn--link btn--default",
222
+ "aria-label": "Acties",
223
+ "aria-expanded": isOpen,
224
+ "aria-haspopup": "menu",
225
+ onClick: () => {
226
+ setIsOpen(!isOpen);
227
+ positionDropdown();
228
+ },
229
+ children: renderMenuIcon ? renderMenuIcon() : "\u22EE"
230
+ }
231
+ ),
232
+ isOpen && /* @__PURE__ */ jsx3(
233
+ "div",
234
+ {
235
+ className: "action-dropdown__menu",
236
+ ref: dropdownRef,
237
+ role: "menu",
238
+ children: actionMenu?.actions.map((action) => renderButton(action, true))
239
+ }
240
+ )
241
+ ]
242
+ }
243
+ ) });
244
+ };
245
+
246
+ // src/components/cell-types/EmptyCell.tsx
247
+ import { jsx as jsx4 } from "react/jsx-runtime";
248
+ var EmptyCell = () => /* @__PURE__ */ jsx4("span", { className: "empty", "aria-label": "empty" });
249
+
250
+ // src/components/cell-types/BooleanCell.tsx
251
+ import { Button as Button2 } from "@sio-group/ui-core";
252
+ import { jsx as jsx5 } from "react/jsx-runtime";
253
+ var BooleanCell = ({
254
+ column,
255
+ value,
256
+ item,
257
+ updateData
258
+ }) => /* @__PURE__ */ jsx5(
259
+ Button2,
260
+ {
261
+ color: value ? "success" : "error",
262
+ variant: column.format === "button" ? "primary" : "link",
263
+ onClick: () => updateData?.(item.id, {
264
+ [column.name]: !value
265
+ }),
266
+ ariaLabel: String(column.name),
267
+ children: value ? "\u2713" : "\u2717"
268
+ }
269
+ );
270
+
271
+ // src/utils/render-value.tsx
272
+ import { Link, Pill } from "@sio-group/ui-core";
273
+
274
+ // src/utils/render-object.tsx
275
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
276
+ var renderObject = (obj) => {
277
+ const entries = Object.entries(obj);
278
+ if (!entries.length) return /* @__PURE__ */ jsx6(EmptyCell, {});
279
+ return /* @__PURE__ */ jsx6(Fragment2, { children: entries.map(([key, val]) => /* @__PURE__ */ jsxs3("div", { children: [
280
+ /* @__PURE__ */ jsxs3("span", { children: [
281
+ key,
282
+ ": "
283
+ ] }),
284
+ /* @__PURE__ */ jsx6("span", { children: String(val) })
285
+ ] }, key)) });
286
+ };
287
+
288
+ // src/components/cell-types/DateCell.tsx
289
+ var DateCell = ({
290
+ column,
291
+ value
292
+ }) => new Date(value).toLocaleString("nl-BE", {
293
+ timeZone: "Europe/Brussels",
294
+ year: "numeric",
295
+ month: "2-digit",
296
+ day: "2-digit",
297
+ ...column.format === "datetime" ? {
298
+ hour: "2-digit",
299
+ minute: "2-digit",
300
+ second: "2-digit"
301
+ } : {},
302
+ hour12: false
303
+ }).replace(/\//g, "-");
304
+
305
+ // src/utils/is-pill-value.ts
306
+ var isPillValue = (val) => typeof val === "object" && val !== null && "status" in val && "label" in val;
307
+
308
+ // src/components/cell-types/InlineInputCell.tsx
309
+ import { useEffect as useEffect2, useState as useState3 } from "react";
310
+ import { Button as Button3 } from "@sio-group/ui-core";
311
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
312
+ var InlineInputCell = ({
313
+ column,
314
+ formField,
315
+ item,
316
+ value,
317
+ updateData
318
+ }) => {
319
+ const [showEdit, setShowEdit] = useState3(false);
320
+ const [fieldValue, setFieldValue] = useState3(String(value));
321
+ const [isValid, setIsValid] = useState3(true);
322
+ const handleCancel = () => {
323
+ setShowEdit(false);
324
+ setFieldValue(String(value));
325
+ };
326
+ const handleSave = async () => {
327
+ updateData?.(item.id, { [formField.name]: fieldValue });
328
+ setFieldValue(String(value));
329
+ setShowEdit(false);
330
+ };
331
+ useEffect2(() => {
332
+ let bool = true;
333
+ if (formField.required) {
334
+ bool = fieldValue !== null && fieldValue !== "";
335
+ }
336
+ setIsValid(bool);
337
+ }, [fieldValue]);
338
+ useEffect2(() => {
339
+ const handleEsc = (e) => {
340
+ if (e.key === "Escape") setShowEdit(false);
341
+ };
342
+ document.addEventListener("keydown", handleEsc);
343
+ return () => document.removeEventListener("keydown", handleEsc);
344
+ }, [setShowEdit]);
345
+ return /* @__PURE__ */ jsx7(Fragment3, { children: showEdit ? /* @__PURE__ */ jsxs4("form", { noValidate: true, children: [
346
+ formField.type === "select" || formField.type === "radio" ? /* @__PURE__ */ jsx7(
347
+ "select",
348
+ {
349
+ id: formField.name,
350
+ name: formField.name,
351
+ value: fieldValue,
352
+ onChange: (e) => setFieldValue(e.target.value),
353
+ autoFocus: true,
354
+ children: formField?.options?.map((option) => {
355
+ const val = typeof option === "string" ? option : option.value;
356
+ const label = typeof option === "string" ? option : option.label;
357
+ return /* @__PURE__ */ jsx7("option", { value: val, children: label }, val);
358
+ })
359
+ }
360
+ ) : /* @__PURE__ */ jsx7(
361
+ "input",
362
+ {
363
+ type: "text",
364
+ id: formField.name,
365
+ name: formField.name,
366
+ value: fieldValue,
367
+ onChange: (e) => setFieldValue(e.target.value),
368
+ autoFocus: true
369
+ }
370
+ ),
371
+ /* @__PURE__ */ jsxs4("div", { className: "btn-group", children: [
372
+ /* @__PURE__ */ jsx7(
373
+ Button3,
374
+ {
375
+ type: "submit",
376
+ variant: "link",
377
+ color: "success",
378
+ onClick: handleSave,
379
+ ariaLabel: "inline edit field",
380
+ disabled: !isValid,
381
+ label: "\u2713"
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx7(
385
+ Button3,
386
+ {
387
+ type: "button",
388
+ variant: "link",
389
+ color: "error",
390
+ onClick: handleCancel,
391
+ ariaLabel: "inline edit field",
392
+ label: "\u2717"
393
+ }
394
+ )
395
+ ] })
396
+ ] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
397
+ value,
398
+ /* @__PURE__ */ jsx7(
399
+ Button3,
400
+ {
401
+ variant: "link",
402
+ onClick: () => setShowEdit(true),
403
+ ariaLabel: "inline edit field",
404
+ children: /* @__PURE__ */ jsxs4(
405
+ "svg",
406
+ {
407
+ xmlns: "http://www.w3.org/2000/svg",
408
+ width: "16",
409
+ height: "16",
410
+ viewBox: "0 0 24 24",
411
+ fill: "none",
412
+ stroke: "currentColor",
413
+ strokeWidth: "2",
414
+ strokeLinecap: "round",
415
+ strokeLinejoin: "round",
416
+ children: [
417
+ /* @__PURE__ */ jsx7("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
418
+ /* @__PURE__ */ jsx7("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
419
+ ]
420
+ }
421
+ )
422
+ }
423
+ )
424
+ ] }) });
425
+ };
426
+
427
+ // src/utils/render-value.tsx
428
+ import { Fragment as Fragment4, jsx as jsx8 } from "react/jsx-runtime";
429
+ var renderValue = ({ value, column, item, formFields, updateData }) => {
430
+ const formatKey = typeof column.format === "object" ? column.format.key : void 0;
431
+ const formField = formFields?.find((f) => f.name === column.name);
432
+ if (formField) {
433
+ return /* @__PURE__ */ jsx8(
434
+ InlineInputCell,
435
+ {
436
+ column,
437
+ value,
438
+ item,
439
+ formField,
440
+ updateData
441
+ }
442
+ );
443
+ }
444
+ if (value === null || value === void 0) return /* @__PURE__ */ jsx8(EmptyCell, {});
445
+ if (Array.isArray(value) && !value.length) return /* @__PURE__ */ jsx8(EmptyCell, {});
446
+ if (column.format === "boolean" || column.format === "button") {
447
+ return /* @__PURE__ */ jsx8(
448
+ BooleanCell,
449
+ {
450
+ column,
451
+ value,
452
+ item,
453
+ updateData
454
+ }
455
+ );
456
+ }
457
+ if (column.format === "datetime" || column.format === "date") {
458
+ return /* @__PURE__ */ jsx8(
459
+ DateCell,
460
+ {
461
+ column,
462
+ value
463
+ }
464
+ );
465
+ }
466
+ if (column.format === "pill" && isPillValue(value)) {
467
+ return /* @__PURE__ */ jsx8(Pill, { status: value.status, label: value.label });
468
+ }
469
+ if (column.format === "email") {
470
+ return /* @__PURE__ */ jsx8(Link, { to: `mailto:${value}`, children: String(value) });
471
+ }
472
+ if (Array.isArray(value)) {
473
+ return /* @__PURE__ */ jsx8(Fragment4, { children: value.map((x, i) => /* @__PURE__ */ jsx8("div", { children: typeof x === "object" && x !== null ? formatKey ? String(x[formatKey] ?? "") : renderObject(x) : String(x) }, i)) });
474
+ }
475
+ if (typeof value === "object") {
476
+ return formatKey ? String(value[formatKey] ?? "") : renderObject(value);
477
+ }
478
+ return String(value);
479
+ };
480
+
481
+ // src/components/TableCell.tsx
482
+ import { jsx as jsx9 } from "react/jsx-runtime";
483
+ var TableCell = ({
484
+ column,
485
+ item,
486
+ formFields,
487
+ updateData
488
+ }) => {
489
+ const cellValue = item[column.name];
490
+ return /* @__PURE__ */ jsx9("td", { className: column.className ?? "", children: renderValue({ value: cellValue, column, item, formFields, updateData }) });
491
+ };
492
+
493
+ // src/components/DataTableBody.tsx
494
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
495
+ var DataTableBody = ({
496
+ item,
497
+ columns,
498
+ actionMenu,
499
+ formFields,
500
+ updateData,
501
+ renderMenuIcon
502
+ }) => {
503
+ return /* @__PURE__ */ jsxs5("tr", { children: [
504
+ actionMenu && /* @__PURE__ */ jsx10(
505
+ ActionCell,
506
+ {
507
+ actionMenu,
508
+ item,
509
+ renderMenuIcon
510
+ }
511
+ ),
512
+ columns.map((column) => /* @__PURE__ */ jsx10(
513
+ TableCell,
514
+ {
515
+ column,
516
+ item,
517
+ formFields,
518
+ updateData
519
+ },
520
+ `${String(column.name)}-${item.id}`
521
+ ))
522
+ ] });
523
+ };
524
+
525
+ // src/components/DataTableControls.tsx
526
+ import { useState as useState4 } from "react";
527
+ import { jsx as jsx11 } from "react/jsx-runtime";
528
+ var DataTableControls = ({
529
+ currentSearch,
530
+ handleSearch,
531
+ entity
532
+ }) => {
533
+ const [focused, setFocused] = useState4(false);
534
+ return /* @__PURE__ */ jsx11("div", { className: "datatable__controls", children: /* @__PURE__ */ jsx11("div", { className: `form-field${currentSearch ? " form-field--has-value" : ""}${focused ? " form-field--focused" : ""}`, children: /* @__PURE__ */ jsx11("div", { className: "form-field__control", children: /* @__PURE__ */ jsx11(
535
+ "input",
536
+ {
537
+ className: "search",
538
+ type: "search",
539
+ value: currentSearch ?? "",
540
+ onChange: (e) => handleSearch(e.target.value),
541
+ placeholder: `Zoeken in ${entity?.label.toLowerCase() ?? "tabel"}`,
542
+ "aria-label": `Zoeken in ${entity?.label.toLowerCase() ?? "tabel"}`,
543
+ onFocus: () => setFocused(true),
544
+ onBlur: () => setFocused(false)
545
+ }
546
+ ) }) }) });
547
+ };
548
+
549
+ // src/components/DataTable.tsx
550
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
551
+ var DataTable = ({
552
+ columns,
553
+ entity,
554
+ actionMenu,
555
+ renderMenuIcon,
556
+ onUpdate,
557
+ formFields,
558
+ renderSortIcon,
559
+ emptyMessage = "Nog geen data",
560
+ striped = false,
561
+ hover = false,
562
+ style,
563
+ ...props
564
+ }) => {
565
+ const {
566
+ pagedData,
567
+ paginationMeta,
568
+ showPagination,
569
+ showSearch,
570
+ handleSearch,
571
+ handleSort,
572
+ handlePaginate,
573
+ currentSort,
574
+ currentSearch
575
+ } = useDataTable(props);
576
+ return /* @__PURE__ */ jsxs6("div", { className: "datatable", style, children: [
577
+ showSearch && /* @__PURE__ */ jsx12(
578
+ DataTableControls,
579
+ {
580
+ currentSearch,
581
+ handleSearch,
582
+ entity
583
+ }
584
+ ),
585
+ /* @__PURE__ */ jsx12("div", { className: "datatable__wrapper", children: /* @__PURE__ */ jsxs6("table", { className: [striped && "striped", hover && "hover"].filter(Boolean).join(" "), children: [
586
+ /* @__PURE__ */ jsx12(
587
+ DataTableHeader,
588
+ {
589
+ columns,
590
+ onSort: handleSort,
591
+ sortValue: currentSort,
592
+ hasActionMenu: !!actionMenu?.actions?.length,
593
+ renderSortIcon
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsx12("tbody", { children: pagedData.length ? pagedData.map((item) => /* @__PURE__ */ jsx12(
597
+ DataTableBody,
598
+ {
599
+ item,
600
+ columns,
601
+ actionMenu,
602
+ formFields,
603
+ updateData: onUpdate,
604
+ renderMenuIcon
605
+ },
606
+ item.id
607
+ )) : /* @__PURE__ */ jsx12("tr", { children: /* @__PURE__ */ jsx12("td", { colSpan: columns.length + (actionMenu?.actions?.length ? 1 : 0), children: /* @__PURE__ */ jsx12("div", { className: "empty-state", children: emptyMessage }) }) }) })
608
+ ] }) }),
609
+ showPagination && paginationMeta && /* @__PURE__ */ jsx12(
610
+ Pagination,
611
+ {
612
+ ...paginationMeta,
613
+ onPaginate: handlePaginate
614
+ }
615
+ )
616
+ ] });
617
+ };
618
+ export {
619
+ DataTable
620
+ };