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