@eztrak/shared 0.4.0 → 0.4.5

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/README.md CHANGED
@@ -128,7 +128,9 @@ const activeTab = searchParams.get("view") ?? "dashboard";
128
128
  }
129
129
  ```
130
130
 
131
- #### Theming — Tailwind `classNames`
131
+ #### Theming — Tailwind `classNames` (style in JSX, no extra CSS file)
132
+
133
+ Pass classes per slot — they merge with the default `eztrak-tabs-*` styles via `tailwind-merge`:
132
134
 
133
135
  ```tsx
134
136
  <EztrakTabs
@@ -136,14 +138,27 @@ const activeTab = searchParams.get("view") ?? "dashboard";
136
138
  activeTab={id}
137
139
  onTabChange={setId}
138
140
  classNames={{
139
- list: "gap-3",
141
+ list: "mb-4",
142
+ content: "bg-white rounded-xl border border-gray-100 shadow-sm",
143
+ panel: "px-6 py-4",
140
144
  button: "rounded-full px-6",
141
145
  underline: "h-1 bg-blue-600",
142
- panel: "mt-6",
143
146
  }}
144
147
  />
145
148
  ```
146
149
 
150
+ | `classNames` key | Element |
151
+ | --- | --- |
152
+ | `wrapper` | Outermost wrapper (or use top-level `className`) |
153
+ | `container` | Inner layout container |
154
+ | `nav` | Tab navigation region |
155
+ | `list` | Tab list (`<ul role="tablist">`) |
156
+ | `item` / `itemActive` | Tab `<li>` |
157
+ | `button` / `buttonActive` | Tab button |
158
+ | `label` / `icon` / `underline` | Label, icon, active underline |
159
+ | `content` | Panels wrapper |
160
+ | `panel` | Each tab panel |
161
+
147
162
  ### Root import
148
163
 
149
164
  ```js
@@ -0,0 +1,540 @@
1
+ import {
2
+ cn,
3
+ confirmationAlert
4
+ } from "./chunk-5OG22DIX.mjs";
5
+
6
+ // src/components/tabs/EztrakTabs.tsx
7
+ import {
8
+ useCallback,
9
+ useEffect,
10
+ useId,
11
+ useRef,
12
+ useState
13
+ } from "react";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ function getInitialTabId(tabs, defaultTabId) {
16
+ if (defaultTabId && tabs.some((t) => t.id === defaultTabId && !t.disabled)) {
17
+ return defaultTabId;
18
+ }
19
+ return tabs.find((t) => !t.disabled)?.id;
20
+ }
21
+ function getEnabledTabIds(tabs) {
22
+ return tabs.filter((t) => !t.disabled).map((t) => t.id);
23
+ }
24
+ function EztrakTabs({
25
+ tabs,
26
+ activeTab,
27
+ defaultTabId,
28
+ onTabChange,
29
+ showPanels = true,
30
+ keepMounted = false,
31
+ classNames,
32
+ className
33
+ }) {
34
+ const baseId = useId().replace(/:/g, "");
35
+ const isControlled = activeTab !== void 0 && activeTab !== null;
36
+ const [uncontrolledActive, setUncontrolledActive] = useState(
37
+ () => getInitialTabId(tabs, defaultTabId)
38
+ );
39
+ const tabRefs = useRef(/* @__PURE__ */ new Map());
40
+ const displayActive = isControlled ? activeTab : uncontrolledActive;
41
+ useEffect(() => {
42
+ if (isControlled) return;
43
+ const enabledIds = getEnabledTabIds(tabs);
44
+ if (!displayActive || !enabledIds.includes(displayActive)) {
45
+ const next = getInitialTabId(tabs, defaultTabId);
46
+ if (next !== displayActive) {
47
+ setUncontrolledActive(next);
48
+ }
49
+ }
50
+ }, [tabs, defaultTabId, isControlled, displayActive]);
51
+ const handleTabClick = useCallback(
52
+ (tabId, disabled) => {
53
+ if (disabled) return;
54
+ if (!isControlled) {
55
+ setUncontrolledActive(tabId);
56
+ }
57
+ onTabChange?.(tabId);
58
+ },
59
+ [isControlled, onTabChange]
60
+ );
61
+ const focusTab = useCallback((tabId) => {
62
+ tabRefs.current.get(tabId)?.focus();
63
+ }, []);
64
+ const handleListKeyDown = useCallback(
65
+ (event) => {
66
+ const enabledIds = getEnabledTabIds(tabs);
67
+ if (enabledIds.length === 0) return;
68
+ const currentIndex = displayActive ? enabledIds.indexOf(displayActive) : -1;
69
+ let nextIndex = currentIndex;
70
+ switch (event.key) {
71
+ case "ArrowRight":
72
+ case "ArrowDown":
73
+ event.preventDefault();
74
+ nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % enabledIds.length;
75
+ break;
76
+ case "ArrowLeft":
77
+ case "ArrowUp":
78
+ event.preventDefault();
79
+ nextIndex = currentIndex < 0 ? enabledIds.length - 1 : (currentIndex - 1 + enabledIds.length) % enabledIds.length;
80
+ break;
81
+ case "Home":
82
+ event.preventDefault();
83
+ nextIndex = 0;
84
+ break;
85
+ case "End":
86
+ event.preventDefault();
87
+ nextIndex = enabledIds.length - 1;
88
+ break;
89
+ default:
90
+ return;
91
+ }
92
+ const nextId = enabledIds[nextIndex];
93
+ if (nextId) {
94
+ handleTabClick(nextId);
95
+ focusTab(nextId);
96
+ }
97
+ },
98
+ [tabs, displayActive, handleTabClick, focusTab]
99
+ );
100
+ if (!tabs.length) {
101
+ return null;
102
+ }
103
+ const wrapperClass = cn(
104
+ "eztrak-tabs-wrapper",
105
+ classNames?.wrapper,
106
+ className
107
+ );
108
+ return /* @__PURE__ */ jsx("div", { className: wrapperClass, children: /* @__PURE__ */ jsxs("div", { className: cn("eztrak-tabs-container", classNames?.container), children: [
109
+ /* @__PURE__ */ jsx(
110
+ "nav",
111
+ {
112
+ className: cn("eztrak-tabs-nav", classNames?.nav),
113
+ "aria-label": "Tabs",
114
+ children: /* @__PURE__ */ jsx(
115
+ "ul",
116
+ {
117
+ role: "tablist",
118
+ className: cn("eztrak-tabs-list", classNames?.list),
119
+ onKeyDown: handleListKeyDown,
120
+ children: tabs.map((tab) => {
121
+ const isActive = displayActive === tab.id;
122
+ const tabId = `${baseId}-tab-${tab.id}`;
123
+ const panelId = `${baseId}-panel-${tab.id}`;
124
+ return /* @__PURE__ */ jsx(
125
+ "li",
126
+ {
127
+ role: "presentation",
128
+ className: cn(
129
+ "eztrak-tabs-item",
130
+ isActive && "eztrak-tabs-item-active",
131
+ classNames?.item,
132
+ isActive && classNames?.itemActive
133
+ ),
134
+ children: /* @__PURE__ */ jsxs(
135
+ "button",
136
+ {
137
+ ref: (el) => {
138
+ if (el) {
139
+ tabRefs.current.set(tab.id, el);
140
+ } else {
141
+ tabRefs.current.delete(tab.id);
142
+ }
143
+ },
144
+ type: "button",
145
+ role: "tab",
146
+ id: tabId,
147
+ "aria-selected": isActive,
148
+ "aria-controls": showPanels ? panelId : void 0,
149
+ tabIndex: isActive ? 0 : -1,
150
+ disabled: tab.disabled,
151
+ className: cn(
152
+ "eztrak-tabs-button",
153
+ isActive && "eztrak-tabs-button-active",
154
+ classNames?.button,
155
+ isActive && classNames?.buttonActive
156
+ ),
157
+ onClick: () => handleTabClick(tab.id, tab.disabled),
158
+ children: [
159
+ tab.icon ? /* @__PURE__ */ jsx(
160
+ "span",
161
+ {
162
+ className: cn("eztrak-tabs-icon", classNames?.icon),
163
+ "aria-hidden": true,
164
+ children: tab.icon
165
+ }
166
+ ) : null,
167
+ /* @__PURE__ */ jsx(
168
+ "span",
169
+ {
170
+ className: cn("eztrak-tabs-label", classNames?.label),
171
+ children: tab.label
172
+ }
173
+ ),
174
+ isActive ? /* @__PURE__ */ jsx(
175
+ "span",
176
+ {
177
+ className: cn(
178
+ "eztrak-tabs-underline",
179
+ classNames?.underline
180
+ ),
181
+ "aria-hidden": true
182
+ }
183
+ ) : null
184
+ ]
185
+ }
186
+ )
187
+ },
188
+ tab.id
189
+ );
190
+ })
191
+ }
192
+ )
193
+ }
194
+ ),
195
+ showPanels ? /* @__PURE__ */ jsx("div", { className: cn("eztrak-tabs-content", classNames?.content), children: tabs.map((tab) => {
196
+ const isActive = displayActive === tab.id;
197
+ const panelId = `${baseId}-panel-${tab.id}`;
198
+ const tabId = `${baseId}-tab-${tab.id}`;
199
+ if (!keepMounted && !isActive) {
200
+ return null;
201
+ }
202
+ return /* @__PURE__ */ jsx(
203
+ "div",
204
+ {
205
+ id: panelId,
206
+ role: "tabpanel",
207
+ "aria-labelledby": tabId,
208
+ hidden: !isActive,
209
+ className: cn("eztrak-tabs-panel", classNames?.panel),
210
+ children: tab.content
211
+ },
212
+ tab.id
213
+ );
214
+ }) }) : null
215
+ ] }) });
216
+ }
217
+
218
+ // src/components/pagination/CustomPagination.tsx
219
+ import { useSearchParams } from "react-router-dom";
220
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
221
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 30, 50, 100];
222
+ function padCount(num) {
223
+ return num.toString().padStart(2, "0");
224
+ }
225
+ function RefreshIcon({
226
+ size = 14,
227
+ className
228
+ }) {
229
+ return /* @__PURE__ */ jsxs2(
230
+ "svg",
231
+ {
232
+ width: size,
233
+ height: size,
234
+ viewBox: "0 0 24 24",
235
+ fill: "none",
236
+ stroke: "currentColor",
237
+ strokeWidth: "2",
238
+ strokeLinecap: "round",
239
+ strokeLinejoin: "round",
240
+ "aria-hidden": "true",
241
+ className,
242
+ children: [
243
+ /* @__PURE__ */ jsx2("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
244
+ /* @__PURE__ */ jsx2("path", { d: "M21 3v5h-5" }),
245
+ /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
246
+ /* @__PURE__ */ jsx2("path", { d: "M8 16H3v5" })
247
+ ]
248
+ }
249
+ );
250
+ }
251
+ function ResetColumnsButton({
252
+ onReset,
253
+ disabled = false,
254
+ isLoading = false
255
+ }) {
256
+ const handleClick = () => {
257
+ confirmationAlert(onReset, {
258
+ text: "Reset column order, widths, and visibility to defaults?",
259
+ icon: "warning",
260
+ title: "Reset column layout to default"
261
+ });
262
+ };
263
+ return /* @__PURE__ */ jsxs2(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ onClick: handleClick,
268
+ disabled: disabled || isLoading,
269
+ title: "Reset column layout to default",
270
+ className: "inline-flex items-center gap-1.5 rounded-xl border border-primary-150 bg-white px-3 py-1.5 text-sm font-medium text-primary-150 hover:bg-gray-50 focus:outline-none disabled:hover:bg-white",
271
+ children: [
272
+ /* @__PURE__ */ jsx2(RefreshIcon, { size: 14, className: cn(isLoading ? "animate-spin" : "") }),
273
+ "Reset columns"
274
+ ]
275
+ }
276
+ );
277
+ }
278
+ function TableLayoutToolbarControls({
279
+ layoutStatus = {},
280
+ onResetLayout,
281
+ renderResetControl,
282
+ isLoading = false
283
+ }) {
284
+ const { isLayoutLoading, isLayoutSaving, isLayoutResetting } = layoutStatus;
285
+ const isLayoutBusy = isLayoutLoading || isLayoutSaving || isLayoutResetting;
286
+ return /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-3", children: onResetLayout && (renderResetControl ? renderResetControl(onResetLayout) : /* @__PURE__ */ jsx2(
287
+ ResetColumnsButton,
288
+ {
289
+ onReset: onResetLayout,
290
+ disabled: isLayoutBusy,
291
+ isLoading
292
+ }
293
+ )) });
294
+ }
295
+ function CustomPagination({
296
+ paginationData,
297
+ paginationPageSize,
298
+ pageSizeOptions: pageSizeOptionsProp,
299
+ isLoading = false,
300
+ paramNames,
301
+ onPageChange,
302
+ onPageSizeChange,
303
+ onResetLayout,
304
+ renderResetControl,
305
+ layoutStatus,
306
+ classNames,
307
+ className
308
+ }) {
309
+ const [searchParams, setSearchParams] = useSearchParams();
310
+ const pageKey = paramNames?.page ?? "page";
311
+ const perPageKey = paramNames?.perPage ?? "perPage";
312
+ const pageSizeOptions = pageSizeOptionsProp ?? paginationPageSize ?? DEFAULT_PAGE_SIZE_OPTIONS;
313
+ const currentPage = paginationData ? Math.max(0, paginationData.currentPage - 1) : 0;
314
+ const handlePageChange = (pageNumber) => {
315
+ if (!paginationData || pageNumber < 0 || pageNumber >= paginationData.pageCount) {
316
+ return;
317
+ }
318
+ const safePage = Math.max(
319
+ 1,
320
+ Math.min(pageNumber + 1, paginationData.pageCount)
321
+ );
322
+ const newParams = new URLSearchParams(searchParams);
323
+ newParams.set(pageKey, String(safePage));
324
+ setSearchParams(newParams, { replace: true });
325
+ onPageChange?.(safePage);
326
+ };
327
+ const handlePageSizeChange = (event) => {
328
+ const parsed = parseInt(event.target.value, 10);
329
+ if (!Number.isFinite(parsed) || parsed < 1) {
330
+ return;
331
+ }
332
+ const newParams = new URLSearchParams(searchParams);
333
+ newParams.set(pageKey, "1");
334
+ newParams.set(perPageKey, String(parsed));
335
+ setSearchParams(newParams, { replace: true });
336
+ onPageSizeChange?.(parsed);
337
+ };
338
+ const createPageButtons = () => {
339
+ const buttons = [];
340
+ const maxButtons = 5;
341
+ const totalPages = Math.max(1, paginationData?.pageCount ?? 1);
342
+ let startPage = Math.max(0, currentPage - Math.floor(maxButtons / 2));
343
+ let endPage = Math.min(totalPages - 1, startPage + maxButtons - 1);
344
+ if (endPage - startPage + 1 < maxButtons) {
345
+ startPage = Math.max(0, endPage - maxButtons + 1);
346
+ }
347
+ if (startPage > 0) {
348
+ buttons.push(
349
+ /* @__PURE__ */ jsx2(
350
+ "button",
351
+ {
352
+ type: "button",
353
+ onClick: () => handlePageChange(0),
354
+ "aria-label": "Page 1",
355
+ className: cn(
356
+ "px-3 py-2 text-sm text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors",
357
+ classNames?.pageButton
358
+ ),
359
+ children: "1"
360
+ },
361
+ "1"
362
+ )
363
+ );
364
+ if (startPage > 1) {
365
+ buttons.push(
366
+ /* @__PURE__ */ jsx2(
367
+ "span",
368
+ {
369
+ "aria-hidden": "true",
370
+ className: "px-2 py-2 text-gray-400",
371
+ children: "..."
372
+ },
373
+ "ellipsis1"
374
+ )
375
+ );
376
+ }
377
+ }
378
+ for (let i = startPage; i <= endPage; i++) {
379
+ const isActive = currentPage === i;
380
+ buttons.push(
381
+ /* @__PURE__ */ jsx2(
382
+ "button",
383
+ {
384
+ type: "button",
385
+ onClick: () => handlePageChange(i),
386
+ "aria-label": `Page ${i + 1}`,
387
+ "aria-current": isActive ? "page" : void 0,
388
+ className: cn(
389
+ "px-3 py-2 text-sm rounded-md transition-colors",
390
+ isActive ? cn("bg-gray-400 text-white", classNames?.activePageButton) : cn(
391
+ "text-gray-600 hover:text-gray-600 hover:bg-gray-50",
392
+ classNames?.pageButton
393
+ )
394
+ ),
395
+ children: i + 1
396
+ },
397
+ i + 1
398
+ )
399
+ );
400
+ }
401
+ if (endPage < totalPages - 1) {
402
+ if (endPage < totalPages - 2) {
403
+ buttons.push(
404
+ /* @__PURE__ */ jsx2(
405
+ "span",
406
+ {
407
+ "aria-hidden": "true",
408
+ className: "px-2 py-2 text-gray-400",
409
+ children: "..."
410
+ },
411
+ "ellipsis2"
412
+ )
413
+ );
414
+ }
415
+ buttons.push(
416
+ /* @__PURE__ */ jsx2(
417
+ "button",
418
+ {
419
+ type: "button",
420
+ onClick: () => handlePageChange(totalPages - 1),
421
+ "aria-label": `Page ${totalPages}`,
422
+ className: cn(
423
+ "px-3 py-2 text-sm text-gray-600 hover:text-gray-600 hover:bg-gray-50 rounded-md transition-colors",
424
+ classNames?.pageButton
425
+ ),
426
+ children: totalPages
427
+ },
428
+ totalPages
429
+ )
430
+ );
431
+ }
432
+ return buttons;
433
+ };
434
+ if (isLoading || !paginationData) {
435
+ return /* @__PURE__ */ jsx2("div", { className: "flex items-center justify-center p-6 bg-gray-50", children: /* @__PURE__ */ jsx2("span", { className: "text-gray-500", children: isLoading ? "Loading..." : "No pagination data" }) });
436
+ }
437
+ const effectiveSizeOptions = pageSizeOptions.includes(paginationData.perPage) ? pageSizeOptions : [...pageSizeOptions, paginationData.perPage].sort((a, b) => a - b);
438
+ const startItem = paginationData.totalCount === 0 ? 0 : (paginationData.currentPage - 1) * paginationData.perPage + 1;
439
+ const endItem = Math.min(
440
+ startItem + paginationData.perPage - 1,
441
+ paginationData.totalCount
442
+ );
443
+ const { isLayoutLoading, isLayoutSaving, isLayoutResetting } = layoutStatus ?? {};
444
+ const isLayoutBusy = isLayoutLoading || isLayoutSaving || isLayoutResetting;
445
+ return /* @__PURE__ */ jsxs2(
446
+ "div",
447
+ {
448
+ className: cn(
449
+ "flex items-center justify-between pb-4",
450
+ className,
451
+ classNames?.root
452
+ ),
453
+ children: [
454
+ /* @__PURE__ */ jsx2("div", { className: cn("flex-1", classNames?.info), children: /* @__PURE__ */ jsxs2("span", { className: "text-xs font-medium text-neutral-400", children: [
455
+ "Showing ",
456
+ padCount(startItem),
457
+ " - ",
458
+ padCount(endItem),
459
+ " of",
460
+ " ",
461
+ paginationData.totalCount ?? 0,
462
+ " results"
463
+ ] }) }),
464
+ /* @__PURE__ */ jsxs2(
465
+ "nav",
466
+ {
467
+ "aria-label": "Pagination",
468
+ className: cn("flex items-center space-x-1", classNames?.nav),
469
+ children: [
470
+ /* @__PURE__ */ jsx2(
471
+ "button",
472
+ {
473
+ type: "button",
474
+ onClick: () => handlePageChange(currentPage - 1),
475
+ disabled: currentPage === 0,
476
+ className: "p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
477
+ "aria-label": "Previous page",
478
+ children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M10.7 2.3a1 1 0 0 1 0 1.4L6.4 8l4.3 4.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 0z" }) })
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center space-x-1", children: createPageButtons() }),
482
+ /* @__PURE__ */ jsx2(
483
+ "button",
484
+ {
485
+ type: "button",
486
+ onClick: () => handlePageChange(currentPage + 1),
487
+ disabled: currentPage >= paginationData.pageCount - 1,
488
+ className: "p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
489
+ "aria-label": "Next page",
490
+ children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M5.3 13.7a1 1 0 0 1 0-1.4L9.6 8 5.3 3.7a1 1 0 0 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4 0z" }) })
491
+ }
492
+ )
493
+ ]
494
+ }
495
+ ),
496
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 flex justify-end", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
497
+ onResetLayout && (renderResetControl ? renderResetControl(onResetLayout) : /* @__PURE__ */ jsx2(
498
+ ResetColumnsButton,
499
+ {
500
+ onReset: onResetLayout,
501
+ disabled: isLayoutBusy,
502
+ isLoading
503
+ }
504
+ )),
505
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center space-x-2", children: [
506
+ /* @__PURE__ */ jsx2(
507
+ "label",
508
+ {
509
+ htmlFor: "pagination-per-page",
510
+ className: "text-sm text-gray-600",
511
+ children: "Items per page"
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx2(
515
+ "select",
516
+ {
517
+ id: "pagination-per-page",
518
+ value: paginationData.perPage || 10,
519
+ onChange: handlePageSizeChange,
520
+ className: cn(
521
+ "px-3 py-1.5 text-sm border border-gray-300 rounded-md bg-white text-gray-700 hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",
522
+ classNames?.select
523
+ ),
524
+ children: effectiveSizeOptions.map((option) => /* @__PURE__ */ jsx2("option", { value: option, children: option }, option))
525
+ }
526
+ )
527
+ ] })
528
+ ] }) })
529
+ ]
530
+ }
531
+ );
532
+ }
533
+
534
+ export {
535
+ EztrakTabs,
536
+ ResetColumnsButton,
537
+ TableLayoutToolbarControls,
538
+ CustomPagination
539
+ };
540
+ //# sourceMappingURL=chunk-2VPBTT7J.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/tabs/EztrakTabs.tsx","../src/components/pagination/CustomPagination.tsx"],"sourcesContent":["import {\r\n useCallback,\r\n useEffect,\r\n useId,\r\n useRef,\r\n useState,\r\n type KeyboardEvent,\r\n} from \"react\";\r\nimport { cn } from \"../../utils/cn\";\r\nimport type { EztrakTab, EztrakTabsProps } from \"./types\";\r\n\r\nfunction getInitialTabId(\r\n tabs: EztrakTab[],\r\n defaultTabId?: string\r\n): string | undefined {\r\n if (defaultTabId && tabs.some((t) => t.id === defaultTabId && !t.disabled)) {\r\n return defaultTabId;\r\n }\r\n return tabs.find((t) => !t.disabled)?.id;\r\n}\r\n\r\nfunction getEnabledTabIds(tabs: EztrakTab[]): string[] {\r\n return tabs.filter((t) => !t.disabled).map((t) => t.id);\r\n}\r\n\r\nexport function EztrakTabs({\r\n tabs,\r\n activeTab,\r\n defaultTabId,\r\n onTabChange,\r\n showPanels = true,\r\n keepMounted = false,\r\n classNames,\r\n className,\r\n}: EztrakTabsProps) {\r\n const baseId = useId().replace(/:/g, \"\");\r\n const isControlled = activeTab !== undefined && activeTab !== null;\r\n const [uncontrolledActive, setUncontrolledActive] = useState(() =>\r\n getInitialTabId(tabs, defaultTabId)\r\n );\r\n const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\r\n\r\n const displayActive = isControlled ? activeTab : uncontrolledActive;\r\n\r\n useEffect(() => {\r\n if (isControlled) return;\r\n const enabledIds = getEnabledTabIds(tabs);\r\n if (!displayActive || !enabledIds.includes(displayActive)) {\r\n const next = getInitialTabId(tabs, defaultTabId);\r\n if (next !== displayActive) {\r\n setUncontrolledActive(next);\r\n }\r\n }\r\n }, [tabs, defaultTabId, isControlled, displayActive]);\r\n\r\n const handleTabClick = useCallback(\r\n (tabId: string, disabled?: boolean) => {\r\n if (disabled) return;\r\n if (!isControlled) {\r\n setUncontrolledActive(tabId);\r\n }\r\n onTabChange?.(tabId);\r\n },\r\n [isControlled, onTabChange]\r\n );\r\n\r\n const focusTab = useCallback((tabId: string) => {\r\n tabRefs.current.get(tabId)?.focus();\r\n }, []);\r\n\r\n const handleListKeyDown = useCallback(\r\n (event: KeyboardEvent<HTMLUListElement>) => {\r\n const enabledIds = getEnabledTabIds(tabs);\r\n if (enabledIds.length === 0) return;\r\n\r\n const currentIndex = displayActive\r\n ? enabledIds.indexOf(displayActive)\r\n : -1;\r\n let nextIndex = currentIndex;\r\n\r\n switch (event.key) {\r\n case \"ArrowRight\":\r\n case \"ArrowDown\":\r\n event.preventDefault();\r\n nextIndex =\r\n currentIndex < 0\r\n ? 0\r\n : (currentIndex + 1) % enabledIds.length;\r\n break;\r\n case \"ArrowLeft\":\r\n case \"ArrowUp\":\r\n event.preventDefault();\r\n nextIndex =\r\n currentIndex < 0\r\n ? enabledIds.length - 1\r\n : (currentIndex - 1 + enabledIds.length) % enabledIds.length;\r\n break;\r\n case \"Home\":\r\n event.preventDefault();\r\n nextIndex = 0;\r\n break;\r\n case \"End\":\r\n event.preventDefault();\r\n nextIndex = enabledIds.length - 1;\r\n break;\r\n default:\r\n return;\r\n }\r\n\r\n const nextId = enabledIds[nextIndex];\r\n if (nextId) {\r\n handleTabClick(nextId);\r\n focusTab(nextId);\r\n }\r\n },\r\n [tabs, displayActive, handleTabClick, focusTab]\r\n );\r\n\r\n if (!tabs.length) {\r\n return null;\r\n }\r\n\r\n const wrapperClass = cn(\r\n \"eztrak-tabs-wrapper\",\r\n classNames?.wrapper,\r\n className\r\n );\r\n\r\n return (\r\n <div className={wrapperClass}>\r\n <div className={cn(\"eztrak-tabs-container\", classNames?.container)}>\r\n <nav\r\n className={cn(\"eztrak-tabs-nav\", classNames?.nav)}\r\n aria-label=\"Tabs\"\r\n >\r\n <ul\r\n role=\"tablist\"\r\n className={cn(\"eztrak-tabs-list\", classNames?.list)}\r\n onKeyDown={handleListKeyDown}\r\n >\r\n {tabs.map((tab) => {\r\n const isActive = displayActive === tab.id;\r\n const tabId = `${baseId}-tab-${tab.id}`;\r\n const panelId = `${baseId}-panel-${tab.id}`;\r\n\r\n return (\r\n <li\r\n key={tab.id}\r\n role=\"presentation\"\r\n className={cn(\r\n \"eztrak-tabs-item\",\r\n isActive && \"eztrak-tabs-item-active\",\r\n classNames?.item,\r\n isActive && classNames?.itemActive\r\n )}\r\n >\r\n <button\r\n ref={(el) => {\r\n if (el) {\r\n tabRefs.current.set(tab.id, el);\r\n } else {\r\n tabRefs.current.delete(tab.id);\r\n }\r\n }}\r\n type=\"button\"\r\n role=\"tab\"\r\n id={tabId}\r\n aria-selected={isActive}\r\n aria-controls={showPanels ? panelId : undefined}\r\n tabIndex={isActive ? 0 : -1}\r\n disabled={tab.disabled}\r\n className={cn(\r\n \"eztrak-tabs-button\",\r\n isActive && \"eztrak-tabs-button-active\",\r\n classNames?.button,\r\n isActive && classNames?.buttonActive\r\n )}\r\n onClick={() => handleTabClick(tab.id, tab.disabled)}\r\n >\r\n {tab.icon ? (\r\n <span\r\n className={cn(\"eztrak-tabs-icon\", classNames?.icon)}\r\n aria-hidden\r\n >\r\n {tab.icon}\r\n </span>\r\n ) : null}\r\n <span\r\n className={cn(\"eztrak-tabs-label\", classNames?.label)}\r\n >\r\n {tab.label}\r\n </span>\r\n {isActive ? (\r\n <span\r\n className={cn(\r\n \"eztrak-tabs-underline\",\r\n classNames?.underline\r\n )}\r\n aria-hidden\r\n />\r\n ) : null}\r\n </button>\r\n </li>\r\n );\r\n })}\r\n </ul>\r\n </nav>\r\n\r\n {showPanels ? (\r\n <div className={cn(\"eztrak-tabs-content\", classNames?.content)}>\r\n {tabs.map((tab) => {\r\n const isActive = displayActive === tab.id;\r\n const panelId = `${baseId}-panel-${tab.id}`;\r\n const tabId = `${baseId}-tab-${tab.id}`;\r\n\r\n if (!keepMounted && !isActive) {\r\n return null;\r\n }\r\n\r\n return (\r\n <div\r\n key={tab.id}\r\n id={panelId}\r\n role=\"tabpanel\"\r\n aria-labelledby={tabId}\r\n hidden={!isActive}\r\n className={cn(\"eztrak-tabs-panel\", classNames?.panel)}\r\n >\r\n {tab.content}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n ) : null}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { type ChangeEvent, type ReactNode } from \"react\";\r\nimport { useSearchParams } from \"react-router-dom\";\r\nimport { cn } from \"../../utils/cn\";\r\nimport { confirmationAlert } from \"../../utils/handleApiError\";\r\nimport type {\r\n CustomPaginationProps,\r\n ResetColumnsButtonProps,\r\n TableLayoutToolbarControlsProps,\r\n} from \"./types\";\r\n\r\nconst DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 30, 50, 100];\r\n\r\nfunction padCount(num: number) {\r\n return num.toString().padStart(2, \"0\");\r\n}\r\n\r\nfunction RefreshIcon({\r\n size = 14,\r\n className,\r\n}: {\r\n size?: number;\r\n className?: string;\r\n}) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n className={className}\r\n >\r\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\r\n <path d=\"M21 3v5h-5\" />\r\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\r\n <path d=\"M8 16H3v5\" />\r\n </svg>\r\n );\r\n}\r\n\r\nexport function ResetColumnsButton({\r\n onReset,\r\n disabled = false,\r\n isLoading = false,\r\n}: ResetColumnsButtonProps) {\r\n const handleClick = () => {\r\n confirmationAlert(onReset, {\r\n text: \"Reset column order, widths, and visibility to defaults?\",\r\n icon: \"warning\",\r\n title: \"Reset column layout to default\",\r\n });\r\n };\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={handleClick}\r\n disabled={disabled || isLoading}\r\n title=\"Reset column layout to default\"\r\n className=\"inline-flex items-center gap-1.5 rounded-xl border border-primary-150 bg-white px-3 py-1.5 text-sm font-medium text-primary-150 hover:bg-gray-50 focus:outline-none disabled:hover:bg-white\"\r\n >\r\n <RefreshIcon size={14} className={cn(isLoading ? \"animate-spin\" : \"\")} />\r\n Reset columns\r\n </button>\r\n );\r\n}\r\n\r\nexport function TableLayoutToolbarControls({\r\n layoutStatus = {},\r\n onResetLayout,\r\n renderResetControl,\r\n isLoading = false,\r\n}: TableLayoutToolbarControlsProps) {\r\n const { isLayoutLoading, isLayoutSaving, isLayoutResetting } = layoutStatus;\r\n const isLayoutBusy = isLayoutLoading || isLayoutSaving || isLayoutResetting;\r\n\r\n return (\r\n <div className=\"flex items-center gap-3\">\r\n {onResetLayout &&\r\n (renderResetControl ? (\r\n renderResetControl(onResetLayout)\r\n ) : (\r\n <ResetColumnsButton\r\n onReset={onResetLayout}\r\n disabled={isLayoutBusy}\r\n isLoading={isLoading}\r\n />\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\nexport function CustomPagination({\r\n paginationData,\r\n paginationPageSize,\r\n pageSizeOptions: pageSizeOptionsProp,\r\n isLoading = false,\r\n paramNames,\r\n onPageChange,\r\n onPageSizeChange,\r\n onResetLayout,\r\n renderResetControl,\r\n layoutStatus,\r\n classNames,\r\n className,\r\n}: CustomPaginationProps) {\r\n const [searchParams, setSearchParams] = useSearchParams();\r\n const pageKey = paramNames?.page ?? \"page\";\r\n const perPageKey = paramNames?.perPage ?? \"perPage\";\r\n const pageSizeOptions =\r\n pageSizeOptionsProp ?? paginationPageSize ?? DEFAULT_PAGE_SIZE_OPTIONS;\r\n\r\n const currentPage = paginationData\r\n ? Math.max(0, paginationData.currentPage - 1)\r\n : 0;\r\n\r\n const handlePageChange = (pageNumber: number) => {\r\n if (\r\n !paginationData ||\r\n pageNumber < 0 ||\r\n pageNumber >= paginationData.pageCount\r\n ) {\r\n return;\r\n }\r\n\r\n const safePage = Math.max(\r\n 1,\r\n Math.min(pageNumber + 1, paginationData.pageCount)\r\n );\r\n const newParams = new URLSearchParams(searchParams);\r\n newParams.set(pageKey, String(safePage));\r\n setSearchParams(newParams, { replace: true });\r\n onPageChange?.(safePage);\r\n };\r\n\r\n const handlePageSizeChange = (event: ChangeEvent<HTMLSelectElement>) => {\r\n const parsed = parseInt(event.target.value, 10);\r\n if (!Number.isFinite(parsed) || parsed < 1) {\r\n return;\r\n }\r\n\r\n const newParams = new URLSearchParams(searchParams);\r\n newParams.set(pageKey, \"1\");\r\n newParams.set(perPageKey, String(parsed));\r\n setSearchParams(newParams, { replace: true });\r\n onPageSizeChange?.(parsed);\r\n };\r\n\r\n const createPageButtons = () => {\r\n const buttons: ReactNode[] = [];\r\n const maxButtons = 5;\r\n const totalPages = Math.max(1, paginationData?.pageCount ?? 1);\r\n\r\n let startPage = Math.max(0, currentPage - Math.floor(maxButtons / 2));\r\n let endPage = Math.min(totalPages - 1, startPage + maxButtons - 1);\r\n\r\n if (endPage - startPage + 1 < maxButtons) {\r\n startPage = Math.max(0, endPage - maxButtons + 1);\r\n }\r\n\r\n if (startPage > 0) {\r\n buttons.push(\r\n <button\r\n key=\"1\"\r\n type=\"button\"\r\n onClick={() => handlePageChange(0)}\r\n aria-label=\"Page 1\"\r\n className={cn(\r\n \"px-3 py-2 text-sm text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors\",\r\n classNames?.pageButton\r\n )}\r\n >\r\n 1\r\n </button>\r\n );\r\n\r\n if (startPage > 1) {\r\n buttons.push(\r\n <span\r\n key=\"ellipsis1\"\r\n aria-hidden=\"true\"\r\n className=\"px-2 py-2 text-gray-400\"\r\n >\r\n ...\r\n </span>\r\n );\r\n }\r\n }\r\n\r\n for (let i = startPage; i <= endPage; i++) {\r\n const isActive = currentPage === i;\r\n buttons.push(\r\n <button\r\n key={i + 1}\r\n type=\"button\"\r\n onClick={() => handlePageChange(i)}\r\n aria-label={`Page ${i + 1}`}\r\n aria-current={isActive ? \"page\" : undefined}\r\n className={cn(\r\n \"px-3 py-2 text-sm rounded-md transition-colors\",\r\n isActive\r\n ? cn(\"bg-gray-400 text-white\", classNames?.activePageButton)\r\n : cn(\r\n \"text-gray-600 hover:text-gray-600 hover:bg-gray-50\",\r\n classNames?.pageButton\r\n )\r\n )}\r\n >\r\n {i + 1}\r\n </button>\r\n );\r\n }\r\n\r\n if (endPage < totalPages - 1) {\r\n if (endPage < totalPages - 2) {\r\n buttons.push(\r\n <span\r\n key=\"ellipsis2\"\r\n aria-hidden=\"true\"\r\n className=\"px-2 py-2 text-gray-400\"\r\n >\r\n ...\r\n </span>\r\n );\r\n }\r\n\r\n buttons.push(\r\n <button\r\n key={totalPages}\r\n type=\"button\"\r\n onClick={() => handlePageChange(totalPages - 1)}\r\n aria-label={`Page ${totalPages}`}\r\n className={cn(\r\n \"px-3 py-2 text-sm text-gray-600 hover:text-gray-600 hover:bg-gray-50 rounded-md transition-colors\",\r\n classNames?.pageButton\r\n )}\r\n >\r\n {totalPages}\r\n </button>\r\n );\r\n }\r\n\r\n return buttons;\r\n };\r\n\r\n if (isLoading || !paginationData) {\r\n return (\r\n <div className=\"flex items-center justify-center p-6 bg-gray-50\">\r\n <span className=\"text-gray-500\">\r\n {isLoading ? \"Loading...\" : \"No pagination data\"}\r\n </span>\r\n </div>\r\n );\r\n }\r\n\r\n const effectiveSizeOptions = pageSizeOptions.includes(paginationData.perPage)\r\n ? pageSizeOptions\r\n : [...pageSizeOptions, paginationData.perPage].sort((a, b) => a - b);\r\n\r\n const startItem =\r\n paginationData.totalCount === 0\r\n ? 0\r\n : (paginationData.currentPage - 1) * paginationData.perPage + 1;\r\n const endItem = Math.min(\r\n startItem + paginationData.perPage - 1,\r\n paginationData.totalCount\r\n );\r\n\r\n const { isLayoutLoading, isLayoutSaving, isLayoutResetting } =\r\n layoutStatus ?? {};\r\n const isLayoutBusy =\r\n isLayoutLoading || isLayoutSaving || isLayoutResetting;\r\n\r\n return (\r\n <div\r\n className={cn(\r\n \"flex items-center justify-between pb-4\",\r\n className,\r\n classNames?.root\r\n )}\r\n >\r\n <div className={cn(\"flex-1\", classNames?.info)}>\r\n <span className=\"text-xs font-medium text-neutral-400\">\r\n Showing {padCount(startItem)} - {padCount(endItem)} of{\" \"}\r\n {paginationData.totalCount ?? 0} results\r\n </span>\r\n </div>\r\n\r\n <nav\r\n aria-label=\"Pagination\"\r\n className={cn(\"flex items-center space-x-1\", classNames?.nav)}\r\n >\r\n <button\r\n type=\"button\"\r\n onClick={() => handlePageChange(currentPage - 1)}\r\n disabled={currentPage === 0}\r\n className=\"p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n aria-label=\"Previous page\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M10.7 2.3a1 1 0 0 1 0 1.4L6.4 8l4.3 4.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 0z\" />\r\n </svg>\r\n </button>\r\n\r\n <div className=\"flex items-center space-x-1\">{createPageButtons()}</div>\r\n\r\n <button\r\n type=\"button\"\r\n onClick={() => handlePageChange(currentPage + 1)}\r\n disabled={currentPage >= paginationData.pageCount - 1}\r\n className=\"p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n aria-label=\"Next page\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M5.3 13.7a1 1 0 0 1 0-1.4L9.6 8 5.3 3.7a1 1 0 0 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4 0z\" />\r\n </svg>\r\n </button>\r\n </nav>\r\n\r\n <div className=\"flex-1 flex justify-end\">\r\n <div className=\"flex items-center gap-3\">\r\n {onResetLayout &&\r\n (renderResetControl ? (\r\n renderResetControl(onResetLayout)\r\n ) : (\r\n <ResetColumnsButton\r\n onReset={onResetLayout}\r\n disabled={isLayoutBusy}\r\n isLoading={isLoading}\r\n />\r\n ))}\r\n <div className=\"flex items-center space-x-2\">\r\n <label\r\n htmlFor=\"pagination-per-page\"\r\n className=\"text-sm text-gray-600\"\r\n >\r\n Items per page\r\n </label>\r\n <select\r\n id=\"pagination-per-page\"\r\n value={paginationData.perPage || 10}\r\n onChange={handlePageSizeChange}\r\n className={cn(\r\n \"px-3 py-1.5 text-sm border border-gray-300 rounded-md bg-white text-gray-700 hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors\",\r\n classNames?.select\r\n )}\r\n >\r\n {effectiveSizeOptions.map((option) => (\r\n <option key={option} value={option}>\r\n {option}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nexport default CustomPagination;\r\n"],"mappings":"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","names":["jsx","jsxs"]}