@gridcore/react-smart-table 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.
package/dist/index.mjs ADDED
@@ -0,0 +1,822 @@
1
+ // src/components/SmartTable.tsx
2
+ import { useState as useState3, useMemo, useCallback } from "react";
3
+
4
+ // src/components/SearchBar.tsx
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ var SearchBar = ({ value, onChange, placeholder = "Search...", primaryColor = "purple" }) => {
7
+ const focusRingColor = primaryColor === "purple" ? "focus:ring-purple-600 focus:border-purple-600" : `focus:ring-${primaryColor}-600 focus:border-${primaryColor}-600`;
8
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
9
+ /* @__PURE__ */ jsx(
10
+ "input",
11
+ {
12
+ type: "text",
13
+ placeholder,
14
+ value,
15
+ onChange: (e) => onChange(e.target.value),
16
+ className: `w-full h-[41px] pl-10 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-1 ${focusRingColor}`
17
+ }
18
+ ),
19
+ /* @__PURE__ */ jsx("svg", { className: "absolute left-3 top-3 w-5 h-5 text-gray-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
20
+ value && /* @__PURE__ */ jsx(
21
+ "button",
22
+ {
23
+ onClick: () => onChange(""),
24
+ className: "absolute right-3 top-3 text-gray-400 hover:text-gray-600",
25
+ children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
26
+ }
27
+ )
28
+ ] });
29
+ };
30
+
31
+ // src/components/BulkActionsDropdown.tsx
32
+ import { useEffect, useRef } from "react";
33
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
34
+ var colorClasses = {
35
+ red: "text-red-600",
36
+ green: "text-green-600",
37
+ yellow: "text-yellow-600",
38
+ blue: "text-blue-600"
39
+ };
40
+ var BulkActionsDropdown = ({
41
+ selectedCount,
42
+ isOpen,
43
+ onToggle,
44
+ actions,
45
+ onClose
46
+ }) => {
47
+ const dropdownRef = useRef(null);
48
+ useEffect(() => {
49
+ const handleClickOutside = (event) => {
50
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
51
+ onClose();
52
+ }
53
+ };
54
+ if (isOpen) {
55
+ document.addEventListener("mousedown", handleClickOutside);
56
+ }
57
+ return () => {
58
+ document.removeEventListener("mousedown", handleClickOutside);
59
+ };
60
+ }, [isOpen, onClose]);
61
+ if (selectedCount === 0) return null;
62
+ return /* @__PURE__ */ jsxs2("div", { className: "relative", ref: dropdownRef, children: [
63
+ /* @__PURE__ */ jsxs2(
64
+ "button",
65
+ {
66
+ onClick: onToggle,
67
+ className: "h-[41px] px-3 border border-purple-600 rounded-md hover:bg-gray-50 transition-colors flex items-center gap-2 text-purple-600",
68
+ children: [
69
+ /* @__PURE__ */ jsxs2("span", { className: "text-sm font-medium", children: [
70
+ "Actions (",
71
+ selectedCount,
72
+ ")"
73
+ ] }),
74
+ /* @__PURE__ */ jsx2("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })
75
+ ]
76
+ }
77
+ ),
78
+ isOpen && /* @__PURE__ */ jsx2("div", { className: "absolute top-full mt-2 right-0 w-40 bg-white border border-gray-200 rounded-lg shadow-xl z-50 py-2", children: actions.map((action, idx) => /* @__PURE__ */ jsxs2(
79
+ "button",
80
+ {
81
+ onClick: () => {
82
+ action.onClick([]);
83
+ onClose();
84
+ },
85
+ className: `w-full px-4 py-2 text-left text-sm hover:bg-gray-50 flex items-center gap-3 ${colorClasses[action.color || "blue"]}`,
86
+ children: [
87
+ action.icon,
88
+ action.label
89
+ ]
90
+ },
91
+ idx
92
+ )) })
93
+ ] });
94
+ };
95
+
96
+ // src/components/Tooltip.tsx
97
+ import { useState, useRef as useRef2, useEffect as useEffect2 } from "react";
98
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
99
+ var Tooltip = ({ children, text, position = "left" }) => {
100
+ const [isVisible, setIsVisible] = useState(false);
101
+ const [isTruncated, setIsTruncated] = useState(false);
102
+ const contentRef = useRef2(null);
103
+ useEffect2(() => {
104
+ const checkTruncation = () => {
105
+ if (contentRef.current) {
106
+ const element = contentRef.current.querySelector("[data-truncate]");
107
+ if (element) {
108
+ setIsTruncated(element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight);
109
+ }
110
+ }
111
+ };
112
+ checkTruncation();
113
+ window.addEventListener("resize", checkTruncation);
114
+ return () => window.removeEventListener("resize", checkTruncation);
115
+ }, [children]);
116
+ const positionClasses = {
117
+ left: "right-full mr-2 top-1/2 -translate-y-1/2",
118
+ right: "left-full ml-2 top-1/2 -translate-y-1/2",
119
+ top: "bottom-full mb-2 left-1/2 -translate-x-1/2",
120
+ bottom: "top-full mt-2 left-1/2 -translate-x-1/2"
121
+ };
122
+ const arrowClasses = {
123
+ left: "absolute left-full top-1/2 -translate-y-1/2 border-l-gray-800 border-l-4 border-y-transparent border-y-4 border-r-0",
124
+ right: "absolute right-full top-1/2 -translate-y-1/2 border-r-gray-800 border-r-4 border-y-transparent border-y-4 border-l-0",
125
+ top: "absolute top-full left-1/2 -translate-x-1/2 border-t-gray-800 border-t-4 border-x-transparent border-x-4 border-b-0",
126
+ bottom: "absolute bottom-full left-1/2 -translate-x-1/2 border-b-gray-800 border-b-4 border-x-transparent border-x-4 border-t-0"
127
+ };
128
+ return /* @__PURE__ */ jsxs3(
129
+ "div",
130
+ {
131
+ ref: contentRef,
132
+ className: "relative inline-block",
133
+ onMouseEnter: () => isTruncated && setIsVisible(true),
134
+ onMouseLeave: () => setIsVisible(false),
135
+ children: [
136
+ children,
137
+ isVisible && isTruncated && /* @__PURE__ */ jsx3("div", { className: `absolute z-50 ${positionClasses[position]}`, children: /* @__PURE__ */ jsxs3("div", { className: "bg-gray-800 text-white text-xs px-2 py-1 rounded whitespace-nowrap", children: [
138
+ text,
139
+ /* @__PURE__ */ jsx3("div", { className: arrowClasses[position] })
140
+ ] }) })
141
+ ]
142
+ }
143
+ );
144
+ };
145
+
146
+ // src/utils/icons.tsx
147
+ import * as LucideIcons from "lucide-react";
148
+ import { jsx as jsx4 } from "react/jsx-runtime";
149
+ var Icon = ({ name, className, size, strokeWidth, style }) => {
150
+ const LucideIcon = LucideIcons[name];
151
+ if (!LucideIcon) {
152
+ console.warn(`Icon "${name}" not found in lucide-react`);
153
+ return null;
154
+ }
155
+ return /* @__PURE__ */ jsx4(LucideIcon, { className, size, strokeWidth, style });
156
+ };
157
+ var getIcon = (name, props) => {
158
+ return /* @__PURE__ */ jsx4(Icon, { name, ...props });
159
+ };
160
+
161
+ // src/components/TableHeader.tsx
162
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
163
+ function TableHeader({
164
+ columns,
165
+ showCheckbox,
166
+ showActions,
167
+ selectAll,
168
+ onSelectAll,
169
+ sortConfig,
170
+ onSort,
171
+ styles = {},
172
+ primaryColor = "purple"
173
+ }) {
174
+ const checkboxBg = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
175
+ const checkboxBorder = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
176
+ return /* @__PURE__ */ jsxs4("tr", { className: styles.headerRow || "border-b border-gray-200", children: [
177
+ showCheckbox && /* @__PURE__ */ jsx5("th", { className: styles.headerCell || "w-15 pl-4 py-3 text-left", children: /* @__PURE__ */ jsxs4("div", { className: "relative group", children: [
178
+ /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: selectAll, onChange: onSelectAll, className: "sr-only" }),
179
+ /* @__PURE__ */ jsx5(
180
+ "div",
181
+ {
182
+ className: styles.checkbox || `w-5 h-5 border-2 rounded-lg cursor-pointer transition-all duration-300 flex items-center justify-center transform hover:scale-105 active:scale-95`,
183
+ style: {
184
+ borderColor: selectAll ? checkboxBorder : "rgb(209 213 219)",
185
+ backgroundColor: selectAll ? checkboxBg : "white"
186
+ },
187
+ onClick: onSelectAll,
188
+ children: selectAll && /* @__PURE__ */ jsx5(Icon, { name: "Check", className: "w-3 h-3 text-white", strokeWidth: 2.5 })
189
+ }
190
+ )
191
+ ] }) }),
192
+ columns.map((col) => /* @__PURE__ */ jsx5(
193
+ "th",
194
+ {
195
+ className: styles.headerCell || `px-4 py-3 text-xs font-medium uppercase tracking-wider text-left ${col.sortable ? "cursor-pointer hover:bg-gray-50" : ""}`,
196
+ onClick: () => col.sortable && onSort(col.key),
197
+ children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center space-x-2", children: [
198
+ /* @__PURE__ */ jsx5(Tooltip, { text: col.title, position: "top", children: /* @__PURE__ */ jsx5("span", { className: "truncate", "data-truncate": true, children: col.title }) }),
199
+ col.sortable && (sortConfig == null ? void 0 : sortConfig.key) === col.key && /* @__PURE__ */ jsx5("span", { children: sortConfig.direction === "asc" ? "\u2191" : "\u2193" })
200
+ ] })
201
+ },
202
+ String(col.key)
203
+ )),
204
+ showActions && /* @__PURE__ */ jsx5("th", { className: styles.headerCell || "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", children: "Action" })
205
+ ] });
206
+ }
207
+
208
+ // src/components/TableRow.tsx
209
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
210
+ function TableRow({
211
+ row,
212
+ columns,
213
+ showCheckbox,
214
+ showActions,
215
+ isSelected,
216
+ onSelect,
217
+ onRowClick,
218
+ onEdit,
219
+ onDelete,
220
+ styles = {},
221
+ primaryColor = "purple"
222
+ }) {
223
+ const checkboxBg = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
224
+ const checkboxBorder = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
225
+ const actionColor = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
226
+ return /* @__PURE__ */ jsxs5("tr", { className: styles.row || "hover:bg-gray-50 cursor-pointer border-b border-gray-100", onClick: onRowClick, children: [
227
+ showCheckbox && /* @__PURE__ */ jsx6("td", { className: "pl-4 py-4", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs5("div", { className: "relative group", children: [
228
+ /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: isSelected, onChange: onSelect, className: "sr-only" }),
229
+ /* @__PURE__ */ jsx6(
230
+ "div",
231
+ {
232
+ className: styles.checkbox || `w-5 h-5 border-2 rounded-lg cursor-pointer transition-all duration-300 flex items-center justify-center transform hover:scale-105 active:scale-95`,
233
+ style: {
234
+ borderColor: isSelected ? checkboxBorder : "rgb(209 213 219)",
235
+ backgroundColor: isSelected ? checkboxBg : "white"
236
+ },
237
+ onClick: onSelect,
238
+ children: isSelected && /* @__PURE__ */ jsx6(Icon, { name: "Check", className: "w-3 h-3 text-white", strokeWidth: 2.5 })
239
+ }
240
+ )
241
+ ] }) }),
242
+ columns.map((col) => /* @__PURE__ */ jsx6("td", { className: styles.cell || "px-4 py-4 text-sm text-gray-900", children: /* @__PURE__ */ jsx6(Tooltip, { text: String(col.render ? col.render(row[col.key], row) : row[col.key] || "-"), position: "top", children: /* @__PURE__ */ jsx6("span", { className: "block truncate", "data-truncate": true, children: col.render ? col.render(row[col.key], row) : row[col.key] || "-" }) }) }, String(col.key))),
243
+ showActions && /* @__PURE__ */ jsx6("td", { className: styles.cell || "px-2 py-4 text-sm", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs5("div", { className: "flex space-x-2", children: [
244
+ onEdit && /* @__PURE__ */ jsx6("button", { onClick: onEdit, className: "p-1 rounded hover:bg-gray-100", children: /* @__PURE__ */ jsx6(Icon, { name: "Edit2", className: "w-4 h-4", style: { color: actionColor } }) }),
245
+ onDelete && /* @__PURE__ */ jsx6("button", { onClick: onDelete, className: "p-1 rounded hover:bg-gray-100", children: /* @__PURE__ */ jsx6(Icon, { name: "Trash2", className: "w-4 h-4", style: { color: actionColor } }) })
246
+ ] }) })
247
+ ] });
248
+ }
249
+
250
+ // src/components/TableSkeleton.tsx
251
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
252
+ function TableSkeleton({ columns, showCheckbox, showActions, rows = 5 }) {
253
+ return /* @__PURE__ */ jsx7(Fragment, { children: Array.from({ length: rows }).map((_, idx) => /* @__PURE__ */ jsxs6("tr", { className: "animate-pulse", children: [
254
+ showCheckbox && /* @__PURE__ */ jsx7("td", { className: "pl-4 py-4", children: /* @__PURE__ */ jsx7("div", { className: "w-5 h-5 bg-gray-200 rounded" }) }),
255
+ columns.map((col) => /* @__PURE__ */ jsx7("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsx7("div", { className: "h-4 bg-gray-200 rounded w-3/4" }) }, String(col.key))),
256
+ showActions && /* @__PURE__ */ jsx7("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsxs6("div", { className: "flex space-x-2", children: [
257
+ /* @__PURE__ */ jsx7("div", { className: "w-4 h-4 bg-gray-200 rounded" }),
258
+ /* @__PURE__ */ jsx7("div", { className: "w-4 h-4 bg-gray-200 rounded" })
259
+ ] }) })
260
+ ] }, idx)) });
261
+ }
262
+
263
+ // src/components/Pagination.tsx
264
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
265
+ var Pagination = ({
266
+ currentPage,
267
+ totalPages,
268
+ onPageChange,
269
+ pageSize,
270
+ totalItems,
271
+ onPageSizeChange,
272
+ pageSizeOptions = [10, 25, 50, 100],
273
+ primaryColor = "purple"
274
+ }) => {
275
+ const startItem = (currentPage - 1) * pageSize + 1;
276
+ const endItem = Math.min(currentPage * pageSize, totalItems);
277
+ const activeColor = primaryColor === "purple" ? "bg-purple-600 text-white" : `bg-${primaryColor}-600 text-white`;
278
+ const hoverColor = primaryColor === "purple" ? "hover:bg-purple-50" : `hover:bg-${primaryColor}-50`;
279
+ const hoverBorderColor = primaryColor === "purple" ? "hover:border-purple-500" : `hover:border-${primaryColor}-500`;
280
+ const getPageNumbers = () => {
281
+ const pages = [];
282
+ const maxVisible = 5;
283
+ if (totalPages <= maxVisible) {
284
+ for (let i = 1; i <= totalPages; i++) {
285
+ pages.push(i);
286
+ }
287
+ } else {
288
+ if (currentPage <= 3) {
289
+ for (let i = 1; i <= 4; i++) pages.push(i);
290
+ pages.push("...");
291
+ pages.push(totalPages);
292
+ } else if (currentPage >= totalPages - 2) {
293
+ pages.push(1);
294
+ pages.push("...");
295
+ for (let i = totalPages - 3; i <= totalPages; i++) pages.push(i);
296
+ } else {
297
+ pages.push(1);
298
+ pages.push("...");
299
+ for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i);
300
+ pages.push("...");
301
+ pages.push(totalPages);
302
+ }
303
+ }
304
+ return pages;
305
+ };
306
+ return /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between px-4 py-3 border-t border-gray-200 bg-white", children: [
307
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-4", children: [
308
+ /* @__PURE__ */ jsxs7("span", { className: "text-xs text-gray-700", children: [
309
+ "Showing ",
310
+ startItem,
311
+ " to ",
312
+ endItem,
313
+ " of ",
314
+ totalItems,
315
+ " results"
316
+ ] }),
317
+ onPageSizeChange && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 relative", children: [
318
+ /* @__PURE__ */ jsx8("label", { className: "text-xs text-gray-700", children: "Rows per page:" }),
319
+ /* @__PURE__ */ jsxs7("div", { className: "relative", children: [
320
+ /* @__PURE__ */ jsx8(
321
+ "select",
322
+ {
323
+ value: pageSize,
324
+ onChange: (e) => onPageSizeChange(Number(e.target.value)),
325
+ className: `appearance-none bg-white border border-gray-300 rounded-md pl-2 pr-6 py-1 text-xs text-gray-700 cursor-pointer ${hoverBorderColor} focus:outline-none transition-all duration-200`,
326
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx8("option", { value: size, children: size }, size))
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsx8("div", { className: "absolute inset-y-0 right-0 flex items-center pr-1.5 pointer-events-none", children: /* @__PURE__ */ jsx8(Icon, { name: "ChevronDown", className: "w-3 h-3 text-gray-500" }) })
330
+ ] })
331
+ ] })
332
+ ] }),
333
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
334
+ /* @__PURE__ */ jsx8(
335
+ "button",
336
+ {
337
+ onClick: () => onPageChange(currentPage - 1),
338
+ disabled: currentPage === 1,
339
+ className: `p-2 rounded ${currentPage === 1 ? "text-gray-400 cursor-not-allowed" : `text-gray-700 ${hoverColor}`}`,
340
+ children: /* @__PURE__ */ jsx8(Icon, { name: "ChevronLeft", className: "w-4 h-4" })
341
+ }
342
+ ),
343
+ getPageNumbers().map((page, idx) => typeof page === "number" ? /* @__PURE__ */ jsx8(
344
+ "button",
345
+ {
346
+ onClick: () => onPageChange(page),
347
+ className: `px-3 py-1 rounded text-sm ${currentPage === page ? activeColor : `text-gray-700 ${hoverColor}`}`,
348
+ children: page
349
+ },
350
+ idx
351
+ ) : /* @__PURE__ */ jsx8("span", { className: "px-2 text-gray-500", children: page }, idx)),
352
+ /* @__PURE__ */ jsx8(
353
+ "button",
354
+ {
355
+ onClick: () => onPageChange(currentPage + 1),
356
+ disabled: currentPage === totalPages,
357
+ className: `p-2 rounded ${currentPage === totalPages ? "text-gray-400 cursor-not-allowed" : `text-gray-700 ${hoverColor}`}`,
358
+ children: /* @__PURE__ */ jsx8(Icon, { name: "ChevronRight", className: "w-4 h-4" })
359
+ }
360
+ )
361
+ ] })
362
+ ] });
363
+ };
364
+
365
+ // src/components/ColumnSelector.tsx
366
+ import { useState as useState2, useRef as useRef3, useEffect as useEffect3 } from "react";
367
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
368
+ var ColumnSelector = ({
369
+ columns,
370
+ visibleColumns,
371
+ onColumnToggle,
372
+ onReset,
373
+ primaryColor = "purple",
374
+ minColumns = 1,
375
+ maxColumns = 5,
376
+ label = "Show Columns",
377
+ position = "right"
378
+ }) => {
379
+ const [isOpen, setIsOpen] = useState2(false);
380
+ const dropdownRef = useRef3(null);
381
+ useEffect3(() => {
382
+ const handleClickOutside = (event) => {
383
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
384
+ setIsOpen(false);
385
+ }
386
+ };
387
+ if (isOpen) {
388
+ document.addEventListener("mousedown", handleClickOutside);
389
+ }
390
+ return () => {
391
+ document.removeEventListener("mousedown", handleClickOutside);
392
+ };
393
+ }, [isOpen]);
394
+ const checkboxBg = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
395
+ const checkboxBorder = primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`;
396
+ return /* @__PURE__ */ jsxs8("div", { className: "relative group", ref: dropdownRef, children: [
397
+ /* @__PURE__ */ jsx9(
398
+ "button",
399
+ {
400
+ onClick: () => setIsOpen(!isOpen),
401
+ className: "p-2 rounded cursor-pointer text-gray-600 hover:bg-gray-100 transition-colors",
402
+ children: /* @__PURE__ */ jsx9(Icon, { name: "Columns", className: "w-5 h-5" })
403
+ }
404
+ ),
405
+ /* @__PURE__ */ jsx9("div", { className: `absolute z-50 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none ${position === "left" ? "left-full ml-2" : "right-full mr-2"} top-1/2 -translate-y-1/2`, children: /* @__PURE__ */ jsxs8("div", { className: "bg-gray-800 text-white text-xs px-2 py-1 rounded whitespace-nowrap relative", children: [
406
+ "Column Visibility",
407
+ /* @__PURE__ */ jsx9("div", { className: `absolute ${position === "left" ? "right-full border-r-gray-800 border-r-4 border-y-transparent border-y-4 border-l-0" : "left-full border-l-gray-800 border-l-4 border-y-transparent border-y-4 border-r-0"} top-1/2 -translate-y-1/2` })
408
+ ] }) }),
409
+ isOpen && /* @__PURE__ */ jsxs8("div", { className: "absolute right-0 mt-2 w-56 bg-white border border-gray-200 rounded-lg shadow-xl z-50 py-2", children: [
410
+ /* @__PURE__ */ jsxs8("div", { className: "px-3 py-2 border-b border-gray-200", children: [
411
+ /* @__PURE__ */ jsx9("p", { className: "text-xs font-medium text-gray-700 uppercase", children: label }),
412
+ /* @__PURE__ */ jsxs8("p", { className: "text-xs text-gray-500 mt-1", children: [
413
+ visibleColumns.length,
414
+ " of ",
415
+ columns.length,
416
+ " selected"
417
+ ] })
418
+ ] }),
419
+ /* @__PURE__ */ jsx9("div", { className: "max-h-64 overflow-y-auto", children: columns.map((column) => {
420
+ const isVisible = visibleColumns.includes(column.key);
421
+ const canUncheck = visibleColumns.length > minColumns;
422
+ const canCheck = visibleColumns.length < maxColumns;
423
+ const isDisabled = isVisible && !canUncheck || !isVisible && !canCheck;
424
+ return /* @__PURE__ */ jsxs8(
425
+ "label",
426
+ {
427
+ className: `flex items-center px-3 py-2 ${isDisabled ? "opacity-50 cursor-not-allowed" : "hover:bg-gray-50 cursor-pointer"}`,
428
+ title: isDisabled ? isVisible ? `At least ${minColumns} column(s) must be visible` : `Maximum ${maxColumns} columns can be visible` : "",
429
+ children: [
430
+ /* @__PURE__ */ jsxs8("div", { className: "relative group mr-3", children: [
431
+ /* @__PURE__ */ jsx9(
432
+ "input",
433
+ {
434
+ type: "checkbox",
435
+ checked: isVisible,
436
+ onChange: () => !isDisabled && onColumnToggle(column.key),
437
+ disabled: isDisabled,
438
+ className: "sr-only"
439
+ }
440
+ ),
441
+ /* @__PURE__ */ jsx9(
442
+ "div",
443
+ {
444
+ className: `w-5 h-5 border-2 rounded-lg cursor-pointer transition-all duration-300 flex items-center justify-center transform hover:scale-105 active:scale-95`,
445
+ style: {
446
+ borderColor: isVisible ? checkboxBorder : "rgb(209 213 219)",
447
+ backgroundColor: isVisible ? checkboxBg : "white"
448
+ },
449
+ children: isVisible && /* @__PURE__ */ jsx9(Icon, { name: "Check", className: "w-3 h-3 text-white", strokeWidth: 2.5 })
450
+ }
451
+ )
452
+ ] }),
453
+ /* @__PURE__ */ jsx9("span", { className: "text-sm text-gray-700", children: column.title })
454
+ ]
455
+ },
456
+ column.key
457
+ );
458
+ }) }),
459
+ onReset && /* @__PURE__ */ jsx9("div", { className: "px-3 py-2 border-t border-gray-200", children: /* @__PURE__ */ jsx9(
460
+ "button",
461
+ {
462
+ onClick: onReset,
463
+ className: "w-full text-xs text-gray-600 hover:text-gray-900 py-1 hover:bg-gray-50 rounded",
464
+ children: "Reset to Default"
465
+ }
466
+ ) })
467
+ ] })
468
+ ] });
469
+ };
470
+
471
+ // src/components/SmartTable.tsx
472
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
473
+ function SmartTable({
474
+ data,
475
+ columns,
476
+ className = "",
477
+ showCheckbox = false,
478
+ showActions = false,
479
+ onEdit,
480
+ onDelete,
481
+ onRowClick,
482
+ loading = false,
483
+ emptyMessage = "No data available",
484
+ selectedRows = [],
485
+ onSelectionChange,
486
+ rowKey = "id",
487
+ bulkActions = [],
488
+ onSearch,
489
+ searchPlaceholder = "Search...",
490
+ sidebarActions = [],
491
+ sidebarPosition = "right",
492
+ onAdd,
493
+ onRefresh,
494
+ onExport,
495
+ onPrint,
496
+ onSettings,
497
+ showAdd = false,
498
+ showRefresh = false,
499
+ showExport = false,
500
+ showPrint = false,
501
+ showSettings = false,
502
+ tableHeight = "65vh",
503
+ styles = {},
504
+ primaryColor = "purple",
505
+ pagination,
506
+ showColumnSelector = true,
507
+ columnSelectorLabel = "Show Columns",
508
+ visibleColumns,
509
+ onColumnVisibilityChange,
510
+ minVisibleColumns = 1,
511
+ maxVisibleColumns = 5,
512
+ maxHeight = "65vh",
513
+ skeletonRows = 10
514
+ }) {
515
+ const [sortConfig, setSortConfig] = useState3(null);
516
+ const [showBulkActions, setShowBulkActions] = useState3(false);
517
+ const [searchValue, setSearchValue] = useState3("");
518
+ const [isRefreshing, setIsRefreshing] = useState3(false);
519
+ const [isColumnSelectorOpen, setIsColumnSelectorOpen] = useState3(false);
520
+ const [internalVisibleColumns, setInternalVisibleColumns] = useState3(
521
+ visibleColumns || columns.map((col) => String(col.key))
522
+ );
523
+ const activeVisibleColumns = visibleColumns || internalVisibleColumns;
524
+ const displayColumns = useMemo(
525
+ () => columns.filter((col) => activeVisibleColumns.includes(String(col.key))),
526
+ [columns, activeVisibleColumns]
527
+ );
528
+ const handleColumnToggle = (columnKey) => {
529
+ const newVisibleColumns = activeVisibleColumns.includes(columnKey) ? activeVisibleColumns.filter((key) => key !== columnKey) : [...activeVisibleColumns, columnKey];
530
+ if (onColumnVisibilityChange) {
531
+ onColumnVisibilityChange(newVisibleColumns);
532
+ } else {
533
+ setInternalVisibleColumns(newVisibleColumns);
534
+ }
535
+ };
536
+ const handleColumnReset = () => {
537
+ const firstFiveColumns = columns.slice(0, 5).map((col) => String(col.key));
538
+ if (onColumnVisibilityChange) {
539
+ onColumnVisibilityChange(firstFiveColumns);
540
+ } else {
541
+ setInternalVisibleColumns(firstFiveColumns);
542
+ }
543
+ };
544
+ const handleSort = useCallback((key) => {
545
+ setSortConfig((prev) => ({
546
+ key,
547
+ direction: (prev == null ? void 0 : prev.key) === key && prev.direction === "asc" ? "desc" : "asc"
548
+ }));
549
+ }, []);
550
+ const sortedData = useMemo(() => {
551
+ if (!sortConfig) return data;
552
+ return [...data].sort((a, b) => {
553
+ const aVal = a[sortConfig.key];
554
+ const bVal = b[sortConfig.key];
555
+ if (aVal < bVal) return sortConfig.direction === "asc" ? -1 : 1;
556
+ if (aVal > bVal) return sortConfig.direction === "asc" ? 1 : -1;
557
+ return 0;
558
+ });
559
+ }, [data, sortConfig]);
560
+ const handleSelectAll = useCallback(() => {
561
+ const allKeys = data.map((row) => String(row[rowKey]));
562
+ onSelectionChange == null ? void 0 : onSelectionChange(selectedRows.length === data.length ? [] : allKeys);
563
+ }, [data, selectedRows, onSelectionChange, rowKey]);
564
+ const handleRowSelect = useCallback((row) => {
565
+ const key = String(row[rowKey]);
566
+ const newSelection = selectedRows.includes(key) ? selectedRows.filter((k) => k !== key) : [...selectedRows, key];
567
+ onSelectionChange == null ? void 0 : onSelectionChange(newSelection);
568
+ }, [selectedRows, onSelectionChange, rowKey]);
569
+ const handleSearch = useCallback((value) => {
570
+ setSearchValue(value);
571
+ onSearch == null ? void 0 : onSearch(value);
572
+ }, [onSearch]);
573
+ const selectAll = selectedRows.length === data.length && data.length > 0;
574
+ const defaultBulkActions = [
575
+ {
576
+ label: "Delete",
577
+ color: "red",
578
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Trash2", className: "w-4 h-4" }),
579
+ onClick: (selected) => alert(`Delete ${selected.length} items`)
580
+ }
581
+ ];
582
+ const actionsToShow = bulkActions.length > 0 ? bulkActions : defaultBulkActions;
583
+ const handleRefresh = () => {
584
+ setIsRefreshing(true);
585
+ if (onRefresh) {
586
+ onRefresh();
587
+ }
588
+ setTimeout(() => setIsRefreshing(false), 1e3);
589
+ };
590
+ const defaultAdd = () => {
591
+ console.log("Add new item");
592
+ };
593
+ const defaultExport = () => {
594
+ const csv = [columns.map((col) => col.title).join(",")];
595
+ data.forEach((row) => {
596
+ csv.push(columns.map((col) => row[col.key]).join(","));
597
+ });
598
+ const blob = new Blob([csv.join("\n")], { type: "text/csv" });
599
+ const url = URL.createObjectURL(blob);
600
+ const a = document.createElement("a");
601
+ a.href = url;
602
+ a.download = "export.csv";
603
+ a.click();
604
+ };
605
+ const defaultPrint = () => {
606
+ window.print();
607
+ };
608
+ const defaultSettings = () => {
609
+ console.log("Open settings");
610
+ };
611
+ const allSidebarActions = [
612
+ ...showColumnSelector ? [{
613
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Columns", className: "w-5 h-5" }),
614
+ label: columnSelectorLabel,
615
+ onClick: () => setIsColumnSelectorOpen(true)
616
+ }] : [],
617
+ ...showAdd || onAdd ? [{
618
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Plus", className: "w-5 h-5" }),
619
+ label: "Add New",
620
+ onClick: onAdd || defaultAdd
621
+ }] : [],
622
+ ...showRefresh || onRefresh ? [{
623
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "RefreshCw", className: `w-5 h-5 ${isRefreshing ? "animate-spin" : ""}`, style: { color: isRefreshing ? primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)` : void 0 } }),
624
+ label: "Refresh",
625
+ onClick: handleRefresh
626
+ }] : [],
627
+ ...showExport || onExport ? [{
628
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Download", className: "w-5 h-5" }),
629
+ label: "Export CSV",
630
+ onClick: onExport || defaultExport
631
+ }] : [],
632
+ ...showPrint || onPrint ? [{
633
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Printer", className: "w-5 h-5" }),
634
+ label: "Print",
635
+ onClick: onPrint || defaultPrint
636
+ }] : [],
637
+ ...showSettings || onSettings ? [{
638
+ icon: /* @__PURE__ */ jsx10(Icon, { name: "Settings", className: "w-5 h-5" }),
639
+ label: "Settings",
640
+ onClick: onSettings || defaultSettings
641
+ }] : [],
642
+ ...sidebarActions
643
+ ];
644
+ const positionClasses = {
645
+ right: "right-0 top-0 bottom-0 flex-col border-l",
646
+ left: "left-0 top-0 bottom-0 flex-col border-r",
647
+ top: "top-0 left-0 right-0 flex-row w-full border-b",
648
+ bottom: "bottom-0 left-0 right-0 flex-row w-full border-t"
649
+ };
650
+ const tooltipPosition = sidebarPosition === "right" ? "left" : sidebarPosition === "left" ? "right" : sidebarPosition === "top" ? "bottom" : "top";
651
+ const containerClasses = sidebarPosition === "top" || sidebarPosition === "bottom" ? "flex-row" : "flex-col";
652
+ return /* @__PURE__ */ jsxs9("div", { className, children: [
653
+ onSearch && /* @__PURE__ */ jsxs9("div", { className: "mb-4 flex gap-4", children: [
654
+ /* @__PURE__ */ jsx10(
655
+ SearchBar,
656
+ {
657
+ value: searchValue,
658
+ onChange: handleSearch,
659
+ placeholder: searchPlaceholder,
660
+ primaryColor
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsx10(
664
+ BulkActionsDropdown,
665
+ {
666
+ selectedCount: selectedRows.length,
667
+ isOpen: showBulkActions,
668
+ onToggle: () => setShowBulkActions(!showBulkActions),
669
+ actions: actionsToShow.map((action) => ({
670
+ ...action,
671
+ onClick: () => action.onClick(selectedRows)
672
+ })),
673
+ onClose: () => setShowBulkActions(false)
674
+ }
675
+ )
676
+ ] }),
677
+ /* @__PURE__ */ jsxs9("div", { className: "flex", children: [
678
+ allSidebarActions.length > 0 && sidebarPosition === "left" && /* @__PURE__ */ jsx10("div", { className: styles.sidebar || `bg-white border border-gray-200 rounded-l-lg p-2`, children: /* @__PURE__ */ jsx10("div", { className: `flex flex-col items-center gap-4`, children: allSidebarActions.map((action, idx) => /* @__PURE__ */ jsx10("div", { className: "relative group", children: idx === 0 && showColumnSelector && isColumnSelectorOpen ? /* @__PURE__ */ jsx10(
679
+ ColumnSelector,
680
+ {
681
+ columns: columns.map((col) => ({ key: String(col.key), title: col.title })),
682
+ visibleColumns: activeVisibleColumns,
683
+ onColumnToggle: handleColumnToggle,
684
+ onReset: handleColumnReset,
685
+ primaryColor,
686
+ minColumns: minVisibleColumns,
687
+ maxColumns: maxVisibleColumns,
688
+ label: columnSelectorLabel,
689
+ position: "right"
690
+ }
691
+ ) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
692
+ /* @__PURE__ */ jsx10(
693
+ "button",
694
+ {
695
+ onClick: action.onClick,
696
+ className: styles.sidebarButton || `p-2 rounded cursor-pointer text-gray-600 transition-colors`,
697
+ onMouseEnter: (e) => {
698
+ if (!styles.sidebarButtonHover) {
699
+ e.currentTarget.style.backgroundColor = `${primaryColor === "purple" ? "rgb(250 245 255)" : `var(--${primaryColor}-50)`}`;
700
+ e.currentTarget.style.color = `${primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`}`;
701
+ }
702
+ },
703
+ onMouseLeave: (e) => {
704
+ if (!styles.sidebarButtonHover) {
705
+ e.currentTarget.style.backgroundColor = "";
706
+ e.currentTarget.style.color = "";
707
+ }
708
+ },
709
+ children: action.icon
710
+ }
711
+ ),
712
+ /* @__PURE__ */ jsx10("div", { className: `absolute z-50 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none left-full ml-2 top-1/2 -translate-y-1/2`, children: /* @__PURE__ */ jsxs9("div", { className: "bg-gray-800 text-white text-xs px-2 py-1 rounded whitespace-nowrap relative", children: [
713
+ action.label,
714
+ /* @__PURE__ */ jsx10("div", { className: `absolute right-full top-1/2 -translate-y-1/2 border-r-gray-800 border-r-4 border-y-transparent border-y-4 border-l-0` })
715
+ ] }) })
716
+ ] }) }, idx)) }) }),
717
+ /* @__PURE__ */ jsxs9("div", { className: styles.container || `bg-white ${allSidebarActions.length > 0 ? sidebarPosition === "left" ? "rounded-r-lg" : "rounded-l-lg" : "rounded-lg"} border border-gray-200 flex-1 overflow-hidden`, children: [
718
+ /* @__PURE__ */ jsx10("div", { className: styles.body || `${sortedData.length === 0 ? "flex items-center justify-center" : "overflow-auto"} [&::-webkit-scrollbar-track]:mt-11 [&::-webkit-scrollbar]:w-0.5 [&::-webkit-scrollbar]:h-0.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full dark:[&::-webkit-scrollbar-thumb]:bg-${primaryColor}-600 [&::-webkit-scrollbar-thumb]:bg-gray-300`, style: { height: maxHeight, maxHeight }, children: sortedData.length === 0 && !loading ? /* @__PURE__ */ jsx10("div", { className: "text-center text-gray-500", children: emptyMessage }) : /* @__PURE__ */ jsxs9("table", { className: styles.table || "min-w-full table-fixed", children: [
719
+ /* @__PURE__ */ jsx10("thead", { className: styles.header || "sticky top-0 z-10 bg-white shadow-sm", children: /* @__PURE__ */ jsx10(
720
+ TableHeader,
721
+ {
722
+ columns: displayColumns,
723
+ showCheckbox,
724
+ showActions,
725
+ selectAll,
726
+ onSelectAll: handleSelectAll,
727
+ sortConfig,
728
+ onSort: handleSort,
729
+ styles,
730
+ primaryColor
731
+ }
732
+ ) }),
733
+ /* @__PURE__ */ jsx10("tbody", { children: loading ? /* @__PURE__ */ jsx10(
734
+ TableSkeleton,
735
+ {
736
+ columns: displayColumns,
737
+ showCheckbox,
738
+ showActions,
739
+ rows: skeletonRows
740
+ }
741
+ ) : sortedData.map((row, idx) => /* @__PURE__ */ jsx10(
742
+ TableRow,
743
+ {
744
+ row,
745
+ columns: displayColumns,
746
+ showCheckbox,
747
+ showActions,
748
+ isSelected: selectedRows.includes(String(row[rowKey])),
749
+ onSelect: () => handleRowSelect(row),
750
+ onRowClick: () => onRowClick == null ? void 0 : onRowClick(row),
751
+ onEdit: onEdit ? () => onEdit(row) : void 0,
752
+ onDelete: onDelete ? () => onDelete(row) : void 0,
753
+ styles,
754
+ primaryColor
755
+ },
756
+ idx
757
+ )) })
758
+ ] }) }),
759
+ pagination && data.length > 0 && /* @__PURE__ */ jsx10("div", { children: /* @__PURE__ */ jsx10(
760
+ Pagination,
761
+ {
762
+ currentPage: pagination.currentPage,
763
+ totalPages: pagination.totalPages,
764
+ pageSize: pagination.pageSize,
765
+ totalItems: pagination.totalItems,
766
+ onPageChange: pagination.onPageChange,
767
+ onPageSizeChange: pagination.onPageSizeChange,
768
+ pageSizeOptions: pagination.pageSizeOptions,
769
+ primaryColor
770
+ }
771
+ ) })
772
+ ] }),
773
+ allSidebarActions.length > 0 && sidebarPosition === "right" && /* @__PURE__ */ jsx10("div", { className: styles.sidebar || `bg-white border border-gray-200 rounded-r-lg p-2`, children: /* @__PURE__ */ jsx10("div", { className: `flex flex-col items-center gap-4`, children: allSidebarActions.map((action, idx) => /* @__PURE__ */ jsx10("div", { className: "relative group", children: idx === 0 && showColumnSelector && isColumnSelectorOpen ? /* @__PURE__ */ jsx10(
774
+ ColumnSelector,
775
+ {
776
+ columns: columns.map((col) => ({ key: String(col.key), title: col.title })),
777
+ visibleColumns: activeVisibleColumns,
778
+ onColumnToggle: handleColumnToggle,
779
+ onReset: handleColumnReset,
780
+ primaryColor,
781
+ minColumns: minVisibleColumns,
782
+ maxColumns: maxVisibleColumns,
783
+ label: columnSelectorLabel,
784
+ position: "left"
785
+ }
786
+ ) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
787
+ /* @__PURE__ */ jsx10(
788
+ "button",
789
+ {
790
+ onClick: action.onClick,
791
+ className: styles.sidebarButton || `p-2 rounded cursor-pointer text-gray-600 transition-colors`,
792
+ onMouseEnter: (e) => {
793
+ if (!styles.sidebarButtonHover) {
794
+ e.currentTarget.style.backgroundColor = `${primaryColor === "purple" ? "rgb(250 245 255)" : `var(--${primaryColor}-50)`}`;
795
+ e.currentTarget.style.color = `${primaryColor === "purple" ? "rgb(147 51 234)" : `var(--${primaryColor}-600)`}`;
796
+ }
797
+ },
798
+ onMouseLeave: (e) => {
799
+ if (!styles.sidebarButtonHover) {
800
+ e.currentTarget.style.backgroundColor = "";
801
+ e.currentTarget.style.color = "";
802
+ }
803
+ },
804
+ children: action.icon
805
+ }
806
+ ),
807
+ /* @__PURE__ */ jsx10("div", { className: `absolute z-50 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none right-full mr-2 top-1/2 -translate-y-1/2`, children: /* @__PURE__ */ jsxs9("div", { className: "bg-gray-800 text-white text-xs px-2 py-1 rounded whitespace-nowrap relative", children: [
808
+ action.label,
809
+ /* @__PURE__ */ jsx10("div", { className: `absolute left-full top-1/2 -translate-y-1/2 border-l-gray-800 border-l-4 border-y-transparent border-y-4 border-r-0` })
810
+ ] }) })
811
+ ] }) }, idx)) }) })
812
+ ] })
813
+ ] });
814
+ }
815
+ export {
816
+ ColumnSelector,
817
+ Icon,
818
+ Pagination,
819
+ SmartTable,
820
+ Tooltip,
821
+ getIcon
822
+ };