@jameskabz/nextcraft-ui 0.6.16 → 0.7.1

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 (73) hide show
  1. package/README.md +37 -3
  2. package/dist/components/craft-button.cjs +64 -4
  3. package/dist/components/craft-button.cjs.map +1 -1
  4. package/dist/components/craft-button.d.cts +5 -2
  5. package/dist/components/craft-button.d.ts +5 -2
  6. package/dist/components/craft-button.js +65 -5
  7. package/dist/components/craft-button.js.map +1 -1
  8. package/dist/components/craft-checkbox.cjs +2 -2
  9. package/dist/components/craft-checkbox.cjs.map +1 -1
  10. package/dist/components/craft-checkbox.js +2 -2
  11. package/dist/components/craft-checkbox.js.map +1 -1
  12. package/dist/components/craft-create-edit-drawer.cjs +5 -3
  13. package/dist/components/craft-create-edit-drawer.cjs.map +1 -1
  14. package/dist/components/craft-create-edit-drawer.js +5 -3
  15. package/dist/components/craft-create-edit-drawer.js.map +1 -1
  16. package/dist/components/craft-data-table-header.cjs +74 -0
  17. package/dist/components/craft-data-table-header.cjs.map +1 -0
  18. package/dist/components/craft-data-table-header.d.cts +17 -0
  19. package/dist/components/craft-data-table-header.d.ts +17 -0
  20. package/dist/components/craft-data-table-header.js +50 -0
  21. package/dist/components/craft-data-table-header.js.map +1 -0
  22. package/dist/components/craft-data-table-pagination.cjs +107 -0
  23. package/dist/components/craft-data-table-pagination.cjs.map +1 -0
  24. package/dist/components/craft-data-table-pagination.d.cts +25 -0
  25. package/dist/components/craft-data-table-pagination.d.ts +25 -0
  26. package/dist/components/craft-data-table-pagination.js +83 -0
  27. package/dist/components/craft-data-table-pagination.js.map +1 -0
  28. package/dist/components/craft-data-table.cjs +424 -192
  29. package/dist/components/craft-data-table.cjs.map +1 -1
  30. package/dist/components/craft-data-table.d.cts +61 -9
  31. package/dist/components/craft-data-table.d.ts +61 -9
  32. package/dist/components/craft-data-table.js +424 -192
  33. package/dist/components/craft-data-table.js.map +1 -1
  34. package/dist/components/craft-form-field.cjs +67 -11
  35. package/dist/components/craft-form-field.cjs.map +1 -1
  36. package/dist/components/craft-form-field.d.cts +4 -1
  37. package/dist/components/craft-form-field.d.ts +4 -1
  38. package/dist/components/craft-form-field.js +57 -11
  39. package/dist/components/craft-form-field.js.map +1 -1
  40. package/dist/components/craft-form-modal.cjs +29 -28
  41. package/dist/components/craft-form-modal.cjs.map +1 -1
  42. package/dist/components/craft-form-modal.js +29 -28
  43. package/dist/components/craft-form-modal.js.map +1 -1
  44. package/dist/components/craft-form.cjs +5 -3
  45. package/dist/components/craft-form.cjs.map +1 -1
  46. package/dist/components/craft-form.js +5 -3
  47. package/dist/components/craft-form.js.map +1 -1
  48. package/dist/components/craft-icon.cjs +26 -32
  49. package/dist/components/craft-icon.cjs.map +1 -1
  50. package/dist/components/craft-icon.d.cts +1 -2
  51. package/dist/components/craft-icon.d.ts +1 -2
  52. package/dist/components/craft-icon.js +36 -32
  53. package/dist/components/craft-icon.js.map +1 -1
  54. package/dist/components/craft-loader.cjs +195 -0
  55. package/dist/components/craft-loader.cjs.map +1 -0
  56. package/dist/components/craft-loader.d.cts +21 -0
  57. package/dist/components/craft-loader.d.ts +21 -0
  58. package/dist/components/craft-loader.js +171 -0
  59. package/dist/components/craft-loader.js.map +1 -0
  60. package/dist/components/layout/app-template.cjs +2 -3
  61. package/dist/components/layout/app-template.cjs.map +1 -1
  62. package/dist/components/layout/app-template.d.cts +1 -2
  63. package/dist/components/layout/app-template.d.ts +1 -2
  64. package/dist/components/layout/app-template.js +2 -3
  65. package/dist/components/layout/app-template.js.map +1 -1
  66. package/dist/index.cjs +9 -3
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.cts +4 -2
  69. package/dist/index.d.ts +4 -2
  70. package/dist/index.js +6 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/styles.css +164 -12
  73. package/package.json +11 -10
@@ -2,7 +2,14 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../utils/cn";
5
- import { CraftPagination } from "../components/craft-pagination";
5
+ import { CraftButton } from "../components/craft-button";
6
+ import { CraftCheckbox } from "../components/craft-checkbox";
7
+ import { CraftIcon } from "../components/craft-icon";
8
+ import { CraftModal } from "../components/craft-modal";
9
+ import { CraftTooltip } from "../components/craft-tooltip";
10
+ import { CraftLoader } from "../components/craft-loader";
11
+ import { CraftDataTableHeader } from "../components/craft-data-table-header";
12
+ import { CraftDataTablePagination } from "../components/craft-data-table-pagination";
6
13
  function getColumnValue(column, row) {
7
14
  if (typeof column.accessor === "function") return column.accessor(row);
8
15
  const record = row;
@@ -16,18 +23,58 @@ function normalizeValue(value) {
16
23
  if (value instanceof Date) return value.getTime();
17
24
  return String(value).toLowerCase();
18
25
  }
26
+ function truncateText(text, maxWords) {
27
+ const words = text.split(" ");
28
+ if (words.length <= maxWords) return text;
29
+ return `${words.slice(0, maxWords).join(" ")}...`;
30
+ }
31
+ function resolveActionVariant(variant) {
32
+ switch (variant) {
33
+ case "solid":
34
+ case "default":
35
+ case "primary":
36
+ return "solid";
37
+ case "gradient":
38
+ return "gradient";
39
+ case "outline":
40
+ case "primaryOutline":
41
+ case "dangerOutline":
42
+ case "successOutline":
43
+ return "outline";
44
+ case "ghost":
45
+ case "link":
46
+ case "secondary":
47
+ case "danger":
48
+ case "success":
49
+ case "warning":
50
+ case "info":
51
+ case "subtle":
52
+ case "dark":
53
+ case "light":
54
+ default:
55
+ return "ghost";
56
+ }
57
+ }
19
58
  function CraftDataTable({
20
59
  data,
21
60
  columns,
61
+ title,
62
+ description,
22
63
  tone,
23
64
  className,
24
- loading = false,
25
- emptyState,
26
65
  toolbar,
66
+ emptyState,
67
+ actions = [],
68
+ showActionsColumn = true,
69
+ selectable,
70
+ enableRowSelection = true,
71
+ selectedRowIds,
72
+ onRowSelectionChange,
73
+ getRowId,
74
+ rowKey,
27
75
  enableSorting = true,
28
76
  enableFiltering = true,
29
77
  enableColumnVisibility = true,
30
- enableRowSelection = true,
31
78
  enablePagination = true,
32
79
  showGlobalFilter,
33
80
  manualSorting = false,
@@ -41,14 +88,39 @@ function CraftDataTable({
41
88
  onGlobalFilterChange,
42
89
  columnVisibility,
43
90
  onColumnVisibilityChange,
44
- selectedRowIds,
45
- onRowSelectionChange,
46
- getRowId,
47
91
  pageIndex,
48
92
  pageSize = 10,
49
93
  pageCount,
50
94
  onPageChange,
51
- onPageSizeChange
95
+ onPageSizeChange,
96
+ striped = false,
97
+ hoverable = true,
98
+ clickableRows = false,
99
+ emptyText = "No data available",
100
+ emptySubtitle = "Try adjusting your search or filter criteria",
101
+ variant = "default",
102
+ density = "normal",
103
+ headerVariant = "default",
104
+ headerPadding = "normal",
105
+ paginationVariant = "default",
106
+ paginationPadding = "normal",
107
+ loading = false,
108
+ dataLoading = false,
109
+ sortLoading = false,
110
+ paginationLoading = false,
111
+ bulkLoading = false,
112
+ rowLoading = {},
113
+ loadingType = "dots",
114
+ loadingSize = "medium",
115
+ loadingColor = "rgb(var(--nc-accent-1))",
116
+ loadingText = "Loading...",
117
+ loadingTextPosition = "bottom",
118
+ loadingBackground = "rgba(0, 0, 0, 0.35)",
119
+ showSkeleton = true,
120
+ skeletonRows = 5,
121
+ truncateWords = 10,
122
+ enableTextModal = true,
123
+ onRowClick
52
124
  }) {
53
125
  const [internalSort, setInternalSort] = React.useState(null);
54
126
  const [internalFilters, setInternalFilters] = React.useState({});
@@ -62,12 +134,15 @@ function CraftDataTable({
62
134
  const [internalSelection, setInternalSelection] = React.useState({});
63
135
  const [internalPageIndex, setInternalPageIndex] = React.useState(0);
64
136
  const [showColumns, setShowColumns] = React.useState(false);
137
+ const [showModal, setShowModal] = React.useState(false);
138
+ const [modalContent, setModalContent] = React.useState("");
65
139
  const resolvedSort = sortBy != null ? sortBy : internalSort;
66
140
  const resolvedFilters = filters != null ? filters : internalFilters;
67
141
  const resolvedGlobalFilter = globalFilter != null ? globalFilter : internalGlobalFilter;
68
142
  const resolvedVisibility = columnVisibility != null ? columnVisibility : internalVisibility;
69
143
  const resolvedSelection = selectedRowIds != null ? selectedRowIds : internalSelection;
70
144
  const resolvedPageIndex = pageIndex != null ? pageIndex : internalPageIndex;
145
+ const resolvedSelectable = selectable != null ? selectable : enableRowSelection;
71
146
  const setSort = (next) => {
72
147
  if (sortBy === void 0) setInternalSort(next);
73
148
  onSortChange == null ? void 0 : onSortChange(next);
@@ -142,9 +217,11 @@ function CraftDataTable({
142
217
  const rowIdFor = React.useCallback(
143
218
  (row, index) => {
144
219
  var _a;
145
- return (_a = getRowId == null ? void 0 : getRowId(row, index)) != null ? _a : String(index);
220
+ if (getRowId) return getRowId(row, index);
221
+ if (rowKey) return String((_a = row[rowKey]) != null ? _a : index);
222
+ return String(index);
146
223
  },
147
- [getRowId]
224
+ [getRowId, rowKey]
148
225
  );
149
226
  const pageStartIndex = enablePagination && !manualPagination ? resolvedPageIndex * pageSize : 0;
150
227
  const pageRowIds = pagedData.map(
@@ -171,200 +248,355 @@ function CraftDataTable({
171
248
  }
172
249
  setSort(null);
173
250
  };
174
- const emptyContent = emptyState != null ? emptyState : /* @__PURE__ */ jsx("div", { className: "text-center text-sm text-[rgb(var(--nc-fg-muted))]", children: "No results found." });
175
251
  const resolvedShowGlobalFilter = showGlobalFilter != null ? showGlobalFilter : enableFiltering && !toolbar;
176
252
  const setGlobalFilter = (next) => {
177
253
  if (globalFilter === void 0) setInternalGlobalFilter(next);
178
254
  onGlobalFilterChange == null ? void 0 : onGlobalFilterChange(next);
179
255
  };
256
+ const shouldShowActionsColumn = React.useMemo(() => {
257
+ if (!showActionsColumn) return false;
258
+ if (!actions || actions.length === 0) return false;
259
+ return actions.some((action) => {
260
+ if (action.permission === void 0) return true;
261
+ if (typeof action.permission === "boolean") return action.permission;
262
+ return true;
263
+ });
264
+ }, [actions, showActionsColumn]);
265
+ const getVisibleActions = (item) => actions.filter((action) => {
266
+ if (action.permission !== void 0) {
267
+ if (typeof action.permission === "boolean" && !action.permission) return false;
268
+ if (typeof action.permission === "function" && !action.permission(item)) return false;
269
+ }
270
+ if (action.visible && typeof action.visible === "function") {
271
+ return action.visible(item);
272
+ }
273
+ if (typeof action.visible === "boolean") return action.visible;
274
+ return true;
275
+ });
276
+ const isActionDisabled = (action, item) => {
277
+ if (action.disabled && typeof action.disabled === "function") return action.disabled(item);
278
+ if (typeof action.disabled === "boolean") return action.disabled;
279
+ return false;
280
+ };
281
+ const handleActionClick = (action, item) => {
282
+ var _a;
283
+ if (isActionDisabled(action, item)) return;
284
+ (_a = action.onClick) == null ? void 0 : _a.call(action, item);
285
+ };
286
+ const totalColumns = visibleColumns.length + (resolvedSelectable ? 1 : 0) + (shouldShowActionsColumn ? 1 : 0);
287
+ const densityPadding = {
288
+ compact: "px-4 py-2",
289
+ normal: "px-6 py-4",
290
+ comfortable: "px-8 py-6"
291
+ };
292
+ const headerPaddingClasses = {
293
+ compact: "px-4 py-2",
294
+ normal: "px-6 py-3",
295
+ comfortable: "px-8 py-4"
296
+ };
297
+ const rowClassNames = (isSelected, rowId, index) => cn(
298
+ "transition-colors",
299
+ variant === "bordered" && "border-b border-[rgb(var(--nc-border)/0.2)]",
300
+ variant === "minimal" && "border-b border-[rgb(var(--nc-border)/0.15)]",
301
+ striped && index % 2 === 1 && "bg-[rgb(var(--nc-surface)/0.04)]",
302
+ hoverable && !clickableRows && "hover:bg-[rgb(var(--nc-surface)/0.12)]",
303
+ clickableRows && "cursor-pointer hover:bg-[rgb(var(--nc-surface)/0.16)]",
304
+ isSelected && "bg-[rgb(var(--nc-accent-1)/0.12)]",
305
+ rowLoading[rowId] && "opacity-60"
306
+ );
307
+ const containerClasses = cn(
308
+ "overflow-hidden rounded-3xl border border-[rgb(var(--nc-border)/0.3)]",
309
+ "bg-[rgb(var(--nc-surface)/0.08)] shadow-[0_18px_50px_rgba(0,0,0,0.35)] backdrop-blur-2xl",
310
+ variant === "minimal" && "border-transparent bg-transparent shadow-none"
311
+ );
312
+ const openModal = (content) => {
313
+ if (!enableTextModal) return;
314
+ setModalContent(content);
315
+ setShowModal(true);
316
+ };
317
+ const closeModal = () => {
318
+ setShowModal(false);
319
+ setModalContent("");
320
+ };
180
321
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-nc-theme": tone, children: [
181
322
  toolbar,
182
- resolvedShowGlobalFilter && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-sm text-[rgb(var(--nc-fg))]", children: [
183
- /* @__PURE__ */ jsx("span", { className: "text-xs text-[rgb(var(--nc-fg-muted))]", children: "Global filter" }),
184
- /* @__PURE__ */ jsx(
185
- "input",
186
- {
187
- type: "search",
188
- value: resolvedGlobalFilter,
189
- onChange: (event) => setGlobalFilter(event.target.value),
190
- placeholder: "Search all columns...",
191
- className: "w-full max-w-xs rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.18)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))]"
192
- }
193
- )
194
- ] }),
195
- enableColumnVisibility && /* @__PURE__ */ jsxs("div", { className: "relative flex justify-end", children: [
196
- /* @__PURE__ */ jsx(
197
- "button",
198
- {
199
- type: "button",
200
- className: "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))] transition hover:bg-[rgb(var(--nc-surface)/0.2)]",
201
- onClick: () => setShowColumns((prev) => !prev),
202
- children: "Columns"
203
- }
204
- ),
205
- showColumns && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-10 z-20 w-48 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.2)] p-3 shadow-[0_12px_30px_rgba(0,0,0,0.35)] backdrop-blur-2xl", children: /* @__PURE__ */ jsx("div", { className: "grid gap-2", children: columns.map((column) => /* @__PURE__ */ jsxs(
206
- "label",
207
- {
208
- className: "flex items-center gap-2 text-xs text-[rgb(var(--nc-fg))]",
209
- children: [
210
- /* @__PURE__ */ jsx(
211
- "input",
212
- {
213
- type: "checkbox",
214
- className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
215
- checked: resolvedVisibility[column.id] !== false,
216
- onChange: (event) => setVisibility({
217
- ...resolvedVisibility,
218
- [column.id]: event.target.checked
219
- })
220
- }
221
- ),
222
- column.header
223
- ]
224
- },
225
- column.id
226
- )) }) })
227
- ] }),
228
- /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] shadow-[0_18px_50px_rgba(0,0,0,0.35)] backdrop-blur-2xl", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-sm", children: [
229
- /* @__PURE__ */ jsx("thead", { className: "bg-[rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg-muted))]", children: /* @__PURE__ */ jsxs("tr", { children: [
230
- enableRowSelection && /* @__PURE__ */ jsx("th", { className: "w-12 px-4 py-3", children: /* @__PURE__ */ jsx(
231
- "input",
232
- {
233
- ref: headerCheckboxRef,
234
- type: "checkbox",
235
- className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
236
- checked: allSelected,
237
- onChange: (event) => {
238
- const next = { ...resolvedSelection };
239
- pageRowIds.forEach((id) => {
240
- next[id] = event.target.checked;
241
- });
242
- setSelection(next);
323
+ /* @__PURE__ */ jsx(
324
+ CraftDataTableHeader,
325
+ {
326
+ title,
327
+ description,
328
+ variant: headerVariant,
329
+ padding: headerPadding,
330
+ tone,
331
+ filters: resolvedShowGlobalFilter ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))]", children: [
332
+ /* @__PURE__ */ jsx(CraftIcon, { name: "search", className: "h-4 w-4 text-[rgb(var(--nc-fg-muted))]" }),
333
+ /* @__PURE__ */ jsx(
334
+ "input",
335
+ {
336
+ type: "search",
337
+ value: resolvedGlobalFilter,
338
+ onChange: (event) => setGlobalFilter(event.target.value),
339
+ placeholder: "Search all columns...",
340
+ className: "w-full max-w-xs bg-transparent text-xs text-[rgb(var(--nc-fg))] placeholder:text-[rgb(var(--nc-fg-muted))] focus:outline-none"
243
341
  }
244
- }
245
- ) }),
246
- visibleColumns.map((column) => {
247
- var _a;
248
- return /* @__PURE__ */ jsxs(
249
- "th",
342
+ )
343
+ ] }) : null,
344
+ actions: enableColumnVisibility ? /* @__PURE__ */ jsxs("div", { className: "relative", children: [
345
+ /* @__PURE__ */ jsx(
346
+ CraftButton,
250
347
  {
251
- className: cn(
252
- "px-4 py-3 text-xs font-semibold uppercase tracking-[0.2em]",
253
- column.headerClassName
254
- ),
255
- style: { width: column.width },
256
- children: [
257
- /* @__PURE__ */ jsxs(
258
- "button",
259
- {
260
- type: "button",
261
- className: cn(
262
- "flex items-center gap-2",
263
- enableSorting && column.sortable !== false ? "cursor-pointer" : "cursor-default"
264
- ),
265
- onClick: () => toggleSort(column),
266
- children: [
267
- /* @__PURE__ */ jsx("span", { children: column.header }),
268
- (resolvedSort == null ? void 0 : resolvedSort.id) === column.id && /* @__PURE__ */ jsx("span", { className: "text-[rgb(var(--nc-accent-1))]", children: resolvedSort.desc ? "\u2193" : "\u2191" })
269
- ]
270
- }
271
- ),
272
- enableFiltering && column.filterable !== false && /* @__PURE__ */ jsx(
273
- "input",
274
- {
275
- type: "text",
276
- value: (_a = resolvedFilters[column.id]) != null ? _a : "",
277
- onChange: (event) => setFilters({
278
- ...resolvedFilters,
279
- [column.id]: event.target.value
280
- }),
281
- placeholder: "Filter",
282
- className: "mt-2 w-full rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.18)] px-2 py-1 text-xs text-[rgb(var(--nc-fg))]"
283
- }
284
- )
285
- ]
286
- },
287
- column.id
288
- );
289
- })
290
- ] }) }),
291
- /* @__PURE__ */ jsxs("tbody", { className: "text-[rgb(var(--nc-fg))]", children: [
292
- loading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
293
- "td",
294
- {
295
- colSpan: visibleColumns.length + (enableRowSelection ? 1 : 0),
296
- className: "px-4 py-10 text-center text-sm text-[rgb(var(--nc-fg-muted))]",
297
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
298
- /* @__PURE__ */ jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-[rgb(var(--nc-fg-muted))] border-t-transparent" }),
299
- "Loading data..."
300
- ] })
301
- }
302
- ) }),
303
- !loading && pagedData.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
304
- "td",
348
+ type: "button",
349
+ variant: "ghost",
350
+ size: "sm",
351
+ onClick: () => setShowColumns((prev) => !prev),
352
+ children: "Columns"
353
+ }
354
+ ),
355
+ showColumns && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-12 z-20 w-56 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.2)] p-3 shadow-[0_12px_30px_rgba(0,0,0,0.35)] backdrop-blur-2xl", children: /* @__PURE__ */ jsx("div", { className: "grid gap-2", children: columns.map((column) => {
356
+ var _a, _b;
357
+ return /* @__PURE__ */ jsxs(
358
+ "label",
359
+ {
360
+ className: "flex items-center gap-2 text-xs text-[rgb(var(--nc-fg))]",
361
+ children: [
362
+ /* @__PURE__ */ jsx(
363
+ "input",
364
+ {
365
+ type: "checkbox",
366
+ className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
367
+ checked: resolvedVisibility[column.id] !== false,
368
+ onChange: (event) => setVisibility({
369
+ ...resolvedVisibility,
370
+ [column.id]: event.target.checked
371
+ })
372
+ }
373
+ ),
374
+ (_b = (_a = column.header) != null ? _a : column.label) != null ? _b : column.id
375
+ ]
376
+ },
377
+ column.id
378
+ );
379
+ }) }) })
380
+ ] }) : null
381
+ }
382
+ ),
383
+ /* @__PURE__ */ jsxs("div", { className: containerClasses, children: [
384
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
385
+ /* @__PURE__ */ jsx(
386
+ CraftLoader,
305
387
  {
306
- colSpan: visibleColumns.length + (enableRowSelection ? 1 : 0),
307
- className: "px-4 py-10",
308
- children: emptyContent
388
+ loading: loading || dataLoading,
389
+ type: loadingType,
390
+ size: loadingSize,
391
+ color: loadingColor,
392
+ overlay: true,
393
+ text: loadingText,
394
+ textPosition: loadingTextPosition,
395
+ backgroundColor: loadingBackground
309
396
  }
310
- ) }),
311
- !loading && pagedData.map((row, rowIndex) => {
312
- const rowId = rowIdFor(row, pageStartIndex + rowIndex);
313
- const isSelected = resolvedSelection[rowId];
314
- return /* @__PURE__ */ jsxs(
315
- "tr",
316
- {
317
- className: cn(
318
- "border-t border-[rgb(var(--nc-border)/0.15)]",
319
- isSelected && "bg-[rgb(var(--nc-accent-1)/0.08)]"
320
- ),
321
- children: [
322
- enableRowSelection && /* @__PURE__ */ jsx("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsx(
323
- "input",
324
- {
325
- type: "checkbox",
326
- className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
327
- checked: isSelected,
328
- onChange: (event) => setSelection({
329
- ...resolvedSelection,
330
- [rowId]: event.target.checked
331
- })
332
- }
333
- ) }),
334
- visibleColumns.map((column) => {
335
- var _a;
336
- return /* @__PURE__ */ jsx(
337
- "td",
397
+ ),
398
+ /* @__PURE__ */ jsx("div", { className: cn("overflow-x-auto", (loading || dataLoading) && "opacity-60"), children: /* @__PURE__ */ jsxs("table", { className: "min-w-full border-collapse text-left text-sm", children: [
399
+ /* @__PURE__ */ jsx("thead", { className: "bg-[rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg-muted))]", children: /* @__PURE__ */ jsxs("tr", { children: [
400
+ resolvedSelectable && /* @__PURE__ */ jsx("th", { className: cn("w-12", headerPaddingClasses[density]), children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: bulkLoading ? /* @__PURE__ */ jsx(CraftLoader, { loading: true, type: "spin", size: "small", color: loadingColor }) : /* @__PURE__ */ jsx(
401
+ CraftCheckbox,
402
+ {
403
+ ref: headerCheckboxRef,
404
+ checked: allSelected,
405
+ onChange: (event) => {
406
+ const next = { ...resolvedSelection };
407
+ pageRowIds.forEach((id) => {
408
+ next[id] = event.target.checked;
409
+ });
410
+ setSelection(next);
411
+ }
412
+ }
413
+ ) }) }),
414
+ visibleColumns.map((column) => {
415
+ var _a, _b, _c;
416
+ const headerLabel = (_b = (_a = column.header) != null ? _a : column.label) != null ? _b : column.id;
417
+ return /* @__PURE__ */ jsxs(
418
+ "th",
419
+ {
420
+ className: cn(
421
+ headerPaddingClasses[density],
422
+ "text-xs font-semibold uppercase tracking-[0.2em]",
423
+ column.headerClassName
424
+ ),
425
+ style: { width: column.width },
426
+ children: [
427
+ /* @__PURE__ */ jsxs(
428
+ "button",
429
+ {
430
+ type: "button",
431
+ className: cn(
432
+ "flex w-full items-center gap-2",
433
+ enableSorting && column.sortable !== false ? "cursor-pointer" : "cursor-default"
434
+ ),
435
+ onClick: () => toggleSort(column),
436
+ children: [
437
+ /* @__PURE__ */ jsx("span", { children: headerLabel }),
438
+ sortLoading && (resolvedSort == null ? void 0 : resolvedSort.id) === column.id ? /* @__PURE__ */ jsx(CraftLoader, { loading: true, type: "spin", size: "small", color: loadingColor }) : null,
439
+ (resolvedSort == null ? void 0 : resolvedSort.id) === column.id && !sortLoading ? /* @__PURE__ */ jsx("span", { className: "text-[rgb(var(--nc-accent-1))]", children: resolvedSort.desc ? "\u2193" : "\u2191" }) : null
440
+ ]
441
+ }
442
+ ),
443
+ enableFiltering && column.filterable !== false && /* @__PURE__ */ jsx(
444
+ "input",
445
+ {
446
+ type: "text",
447
+ value: (_c = resolvedFilters[column.id]) != null ? _c : "",
448
+ onChange: (event) => setFilters({
449
+ ...resolvedFilters,
450
+ [column.id]: event.target.value
451
+ }),
452
+ placeholder: "Filter",
453
+ className: "mt-2 w-full rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.18)] px-2 py-1 text-xs text-[rgb(var(--nc-fg))]"
454
+ }
455
+ )
456
+ ]
457
+ },
458
+ column.id
459
+ );
460
+ }),
461
+ shouldShowActionsColumn && /* @__PURE__ */ jsx("th", { className: cn(headerPaddingClasses[density], "text-xs uppercase"), children: "Actions" })
462
+ ] }) }),
463
+ /* @__PURE__ */ jsx("tbody", { className: "text-[rgb(var(--nc-fg))]", children: showSkeleton && (loading || dataLoading) ? Array.from({ length: skeletonRows }).map((_, rowIndex) => /* @__PURE__ */ jsxs("tr", { className: "animate-pulse", children: [
464
+ resolvedSelectable && /* @__PURE__ */ jsx("td", { className: cn(headerPaddingClasses[density], "w-12"), children: /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded bg-[rgb(var(--nc-border)/0.3)]" }) }),
465
+ visibleColumns.map((column) => /* @__PURE__ */ jsx("td", { className: cn(densityPadding[density]), children: /* @__PURE__ */ jsx("div", { className: "h-4 w-3/4 rounded bg-[rgb(var(--nc-border)/0.3)]" }) }, column.id)),
466
+ shouldShowActionsColumn && /* @__PURE__ */ jsx("td", { className: cn(densityPadding[density]), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
467
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-6 rounded bg-[rgb(var(--nc-border)/0.3)]" }),
468
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-6 rounded bg-[rgb(var(--nc-border)/0.3)]" })
469
+ ] }) })
470
+ ] }, `skeleton-${rowIndex}`)) : pagedData.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: totalColumns, className: cn(densityPadding[density], "py-12"), children: emptyState != null ? emptyState : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-center", children: [
471
+ /* @__PURE__ */ jsx(
472
+ CraftIcon,
473
+ {
474
+ name: "search",
475
+ className: "mb-4 h-12 w-12 text-[rgb(var(--nc-fg-muted))]"
476
+ }
477
+ ),
478
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-[rgb(var(--nc-fg))]", children: emptyText }),
479
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: emptySubtitle })
480
+ ] }) }) }) : pagedData.map((row, rowIndex) => {
481
+ const rowId = rowIdFor(row, pageStartIndex + rowIndex);
482
+ const isSelected = Boolean(resolvedSelection[rowId]);
483
+ return /* @__PURE__ */ jsxs(
484
+ "tr",
485
+ {
486
+ className: rowClassNames(isSelected, rowId, rowIndex),
487
+ onClick: () => {
488
+ if (!clickableRows) return;
489
+ onRowClick == null ? void 0 : onRowClick({ item: row, index: rowIndex });
490
+ },
491
+ children: [
492
+ resolvedSelectable && /* @__PURE__ */ jsx("td", { className: cn(densityPadding[density], "w-12"), children: /* @__PURE__ */ jsx(
493
+ CraftCheckbox,
338
494
  {
339
- className: cn(
340
- "px-4 py-4",
341
- column.align === "center" && "text-center",
342
- column.align === "right" && "text-right",
343
- column.cellClassName
344
- ),
345
- children: column.cell ? column.cell(row) : String((_a = getColumnValue(column, row)) != null ? _a : "")
346
- },
347
- column.id
348
- );
349
- })
350
- ]
351
- },
352
- rowId
353
- );
354
- })
355
- ] })
356
- ] }) }),
357
- enablePagination && /* @__PURE__ */ jsx(
358
- CraftPagination,
359
- {
360
- pageIndex: resolvedPageIndex,
361
- pageCount: resolvedPageCount,
362
- onPageChange: setPageIndex,
363
- pageSize,
364
- onPageSizeChange,
365
- tone
366
- }
367
- )
495
+ checked: isSelected,
496
+ onChange: (event) => setSelection({
497
+ ...resolvedSelection,
498
+ [rowId]: event.target.checked
499
+ })
500
+ }
501
+ ) }),
502
+ visibleColumns.map((column) => {
503
+ var _a, _b, _c;
504
+ const rawValue = getColumnValue(column, row);
505
+ const formatted = column.formatter ? column.formatter(rawValue, row) : rawValue;
506
+ const content = column.cell ? column.cell(row) : formatted != null ? formatted : "";
507
+ if (enableTextModal && !column.cell && typeof formatted === "string" && ((_a = column.truncate) != null ? _a : true) && formatted.split(" ").length > ((_b = column.maxWords) != null ? _b : truncateWords)) {
508
+ const maxWords = (_c = column.maxWords) != null ? _c : truncateWords;
509
+ return /* @__PURE__ */ jsx(
510
+ "td",
511
+ {
512
+ className: cn(
513
+ densityPadding[density],
514
+ column.align === "center" && "text-center",
515
+ column.align === "right" && "text-right",
516
+ column.cellClassName
517
+ ),
518
+ children: /* @__PURE__ */ jsx(
519
+ "button",
520
+ {
521
+ type: "button",
522
+ className: "text-[rgb(var(--nc-accent-1))] hover:text-[rgb(var(--nc-accent-2))]",
523
+ onClick: () => openModal(formatted),
524
+ children: truncateText(formatted, maxWords)
525
+ }
526
+ )
527
+ },
528
+ column.id
529
+ );
530
+ }
531
+ return /* @__PURE__ */ jsx(
532
+ "td",
533
+ {
534
+ className: cn(
535
+ densityPadding[density],
536
+ column.align === "center" && "text-center",
537
+ column.align === "right" && "text-right",
538
+ column.cellClassName
539
+ ),
540
+ children: content
541
+ },
542
+ column.id
543
+ );
544
+ }),
545
+ shouldShowActionsColumn && /* @__PURE__ */ jsx("td", { className: cn(densityPadding[density]), children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-2", children: getVisibleActions(row).map((action) => {
546
+ const tooltip = typeof action.tooltip === "function" ? action.tooltip(row) : action.tooltip;
547
+ const className2 = typeof action.className === "function" ? action.className(row) : action.className;
548
+ const button = /* @__PURE__ */ jsx(
549
+ CraftButton,
550
+ {
551
+ type: "button",
552
+ size: "sm",
553
+ variant: resolveActionVariant(action.variant),
554
+ disabled: isActionDisabled(action, row),
555
+ className: cn("h-8 w-8 p-0", className2),
556
+ onClick: (event) => {
557
+ event.stopPropagation();
558
+ handleActionClick(action, row);
559
+ },
560
+ children: action.icon ? typeof action.icon === "string" ? /* @__PURE__ */ jsx(CraftIcon, { name: action.icon, className: "h-4 w-4" }) : action.icon : action.label ? /* @__PURE__ */ jsx("span", { className: "text-[11px]", children: action.label }) : null
561
+ }
562
+ );
563
+ if (tooltip) {
564
+ return /* @__PURE__ */ jsx(CraftTooltip, { content: tooltip, children: button }, action.key);
565
+ }
566
+ return /* @__PURE__ */ jsx(React.Fragment, { children: button }, action.key);
567
+ }) }) })
568
+ ]
569
+ },
570
+ rowId
571
+ );
572
+ }) })
573
+ ] }) })
574
+ ] }),
575
+ /* @__PURE__ */ jsx(
576
+ CraftDataTablePagination,
577
+ {
578
+ currentPage: resolvedPageIndex,
579
+ totalPages: resolvedPageCount,
580
+ total: filteredData.length,
581
+ pageSize,
582
+ selectable: Boolean(resolvedSelectable),
583
+ selectedCount: Object.values(resolvedSelection).filter(Boolean).length,
584
+ showPagination: enablePagination,
585
+ loading: paginationLoading,
586
+ disabled: loading || dataLoading,
587
+ onPageChange: setPageIndex,
588
+ onPageSizeChange,
589
+ variant: paginationVariant,
590
+ padding: paginationPadding,
591
+ tone
592
+ }
593
+ )
594
+ ] }),
595
+ /* @__PURE__ */ jsx(CraftModal, { open: showModal, onOpenChange: setShowModal, title: "Full Text", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
596
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: "Full content" }),
597
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-[rgb(var(--nc-fg))] whitespace-pre-wrap wrap-break-words", children: modalContent }),
598
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(CraftButton, { type: "button", variant: "ghost", onClick: closeModal, children: "Close" }) })
599
+ ] }) })
368
600
  ] });
369
601
  }
370
602
  export {