uibee 3.3.0 → 3.3.2

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 (162) hide show
  1. package/dist/components/alert/alert.d.ts +15 -0
  2. package/dist/components/alert/alert.js +29 -0
  3. package/dist/components/badge/badge.d.ts +16 -0
  4. package/dist/components/badge/badge.js +26 -0
  5. package/dist/components/buttons/button.d.ts +25 -0
  6. package/dist/components/buttons/button.js +44 -0
  7. package/dist/components/code/code.d.ts +23 -0
  8. package/dist/components/code/code.js +39 -0
  9. package/dist/components/confirm/confirmPopup.d.ts +23 -0
  10. package/dist/components/confirm/confirmPopup.js +52 -0
  11. package/dist/components/container/accordion.d.ts +25 -0
  12. package/dist/components/container/accordion.js +41 -0
  13. package/dist/components/container/card.d.ts +13 -0
  14. package/dist/components/container/card.js +10 -0
  15. package/dist/components/container/expandableCard.d.ts +32 -0
  16. package/dist/components/container/expandableCard.js +70 -0
  17. package/dist/components/container/glassCard.d.ts +13 -0
  18. package/dist/components/container/glassCard.js +10 -0
  19. package/dist/components/container/highlight.d.ts +10 -0
  20. package/dist/components/container/highlight.js +10 -0
  21. package/dist/components/container/iconBubble.d.ts +25 -0
  22. package/dist/components/container/iconBubble.js +21 -0
  23. package/dist/components/container/leftBarPanel.d.ts +15 -0
  24. package/dist/components/container/leftBarPanel.js +10 -0
  25. package/dist/components/container/page.d.ts +15 -0
  26. package/dist/components/container/page.js +16 -0
  27. package/dist/components/container/pulseDot.d.ts +12 -0
  28. package/dist/components/container/pulseDot.js +23 -0
  29. package/dist/components/container/statCard.d.ts +18 -0
  30. package/dist/components/container/statCard.js +25 -0
  31. package/dist/components/container/tabs.d.ts +35 -0
  32. package/dist/components/container/tabs.js +36 -0
  33. package/dist/components/empty/emptyState.d.ts +19 -0
  34. package/dist/components/empty/emptyState.js +27 -0
  35. package/dist/components/footer/footer.d.ts +49 -0
  36. package/dist/components/footer/footer.js +109 -0
  37. package/dist/components/footer/loginContent.d.ts +18 -0
  38. package/dist/components/footer/loginContent.js +205 -0
  39. package/dist/components/inputs/checkbox.d.ts +20 -0
  40. package/dist/components/inputs/checkbox.js +76 -0
  41. package/dist/components/inputs/fileInput.d.ts +19 -0
  42. package/dist/components/inputs/fileInput.js +108 -0
  43. package/dist/components/inputs/input.d.ts +16 -0
  44. package/dist/components/inputs/input.js +157 -0
  45. package/dist/components/inputs/multiSelect.d.ts +37 -0
  46. package/dist/components/inputs/multiSelect.js +99 -0
  47. package/dist/components/inputs/radio.d.ts +20 -0
  48. package/dist/components/inputs/radio.js +70 -0
  49. package/dist/components/inputs/range.d.ts +14 -0
  50. package/dist/components/inputs/range.js +60 -0
  51. package/dist/components/inputs/searchInput.d.ts +11 -0
  52. package/dist/components/inputs/searchInput.js +56 -0
  53. package/dist/components/inputs/select.d.ts +42 -0
  54. package/dist/components/inputs/select.js +164 -0
  55. package/dist/components/inputs/shared/colorPickerPopup.js +293 -0
  56. package/dist/components/inputs/shared/dateTimePickerPopup.js +225 -0
  57. package/dist/components/inputs/shared/fieldWrapper.js +33 -0
  58. package/dist/components/inputs/shared/index.js +6 -0
  59. package/dist/components/inputs/shared/inputError.js +17 -0
  60. package/dist/components/inputs/shared/inputInfo.js +13 -0
  61. package/dist/components/inputs/shared/inputLabel.js +19 -0
  62. package/dist/components/inputs/switch.d.ts +14 -0
  63. package/dist/components/inputs/switch.js +37 -0
  64. package/dist/components/inputs/tagInput.d.ts +31 -0
  65. package/dist/components/inputs/tagInput.js +72 -0
  66. package/dist/components/inputs/textarea.d.ts +14 -0
  67. package/dist/components/inputs/textarea.js +76 -0
  68. package/dist/components/inputs/toggle.d.ts +21 -0
  69. package/dist/components/inputs/toggle.js +33 -0
  70. package/dist/components/login/loginPage.d.ts +15 -0
  71. package/dist/components/login/loginPage.js +80 -0
  72. package/dist/components/logo/logo.d.ts +9 -0
  73. package/dist/components/logo/logo.js +138 -0
  74. package/dist/components/logo/logoSmall.d.ts +4 -0
  75. package/dist/components/logo/logoSmall.js +56 -0
  76. package/dist/components/markdownrender/markdownRender.d.ts +16 -0
  77. package/dist/components/markdownrender/markdownRender.js +59 -0
  78. package/dist/components/modal/modal.d.ts +22 -0
  79. package/dist/components/modal/modal.js +59 -0
  80. package/dist/components/navbar/bubble.js +36 -0
  81. package/dist/components/navbar/navbar.d.ts +50 -0
  82. package/dist/components/navbar/navbar.js +99 -0
  83. package/dist/components/navbar/navbarDropdown.d.ts +15 -0
  84. package/dist/components/navbar/navbarDropdown.js +63 -0
  85. package/dist/components/navbar/navbarItem.d.ts +23 -0
  86. package/dist/components/navbar/navbarItem.js +38 -0
  87. package/dist/components/sidebar/sidebar.d.ts +34 -0
  88. package/dist/components/sidebar/sidebar.js +135 -0
  89. package/dist/components/spinner/spinner.d.ts +11 -0
  90. package/dist/components/spinner/spinner.js +16 -0
  91. package/dist/components/table/body.js +253 -0
  92. package/dist/components/table/constants.js +58 -0
  93. package/dist/components/table/empty.js +24 -0
  94. package/dist/components/table/format.js +39 -0
  95. package/dist/components/table/header.js +109 -0
  96. package/dist/components/table/menu.d.ts +18 -0
  97. package/dist/components/table/menu.js +76 -0
  98. package/dist/components/table/pagination.d.ts +14 -0
  99. package/dist/components/table/pagination.js +130 -0
  100. package/dist/components/table/skeleton.js +39 -0
  101. package/dist/components/table/table.d.ts +7 -0
  102. package/dist/components/table/table.js +213 -0
  103. package/dist/components/table/types.d.ts +52 -0
  104. package/dist/components/table/utils.js +9 -0
  105. package/dist/components/toast/toastItem.js +61 -0
  106. package/dist/components/toast/toaster.d.ts +13 -0
  107. package/dist/components/toast/toaster.js +117 -0
  108. package/dist/components/toggle/language.d.ts +10 -0
  109. package/dist/components/toggle/language.js +34 -0
  110. package/dist/components/toggle/theme.d.ts +8 -0
  111. package/dist/components/toggle/theme.js +135 -0
  112. package/dist/components/version/version.d.ts +13 -0
  113. package/dist/components/version/version.js +19 -0
  114. package/dist/components/vulnerability/constants.d.ts +4 -0
  115. package/dist/components/vulnerability/constants.js +17 -0
  116. package/dist/components/vulnerability/severityPill.d.ts +15 -0
  117. package/dist/components/vulnerability/severityPill.js +20 -0
  118. package/dist/{style.css → globals.css} +210 -264
  119. package/dist/hooks/useClickOutside.d.ts +6 -0
  120. package/dist/{useClickOutside-Cmp-RsP3.js → hooks/useClickOutside.js} +1 -1
  121. package/dist/hooks/useDarkMode.d.ts +4 -0
  122. package/dist/hooks/useDarkMode.js +19 -0
  123. package/dist/hooks/useVisibility.d.ts +7 -0
  124. package/dist/hooks/useVisibility.js +25 -0
  125. package/dist/icons/icons.d.ts +420 -0
  126. package/dist/{icons-lZYQ6Vlr.js → icons/icons.js} +1 -1
  127. package/dist/src/components/index.d.ts +51 -905
  128. package/dist/src/components/index.js +49 -4179
  129. package/dist/src/hooks/index.d.ts +3 -14
  130. package/dist/src/hooks/index.js +3 -42
  131. package/dist/src/icons/index.d.ts +1 -419
  132. package/dist/src/icons/index.js +1 -1
  133. package/dist/src/styles/index.js +1 -1
  134. package/dist/src/utils/index.d.ts +5 -37
  135. package/dist/src/utils/index.js +5 -124
  136. package/dist/utils/auth/callback.d.ts +14 -0
  137. package/dist/utils/auth/callback.js +53 -0
  138. package/dist/utils/auth/getDomain.js +6 -0
  139. package/dist/utils/auth/login.d.ts +12 -0
  140. package/dist/utils/auth/login.js +17 -0
  141. package/dist/utils/auth/logout.d.ts +10 -0
  142. package/dist/utils/auth/logout.js +20 -0
  143. package/dist/utils/auth/token.d.ts +10 -0
  144. package/dist/utils/auth/token.js +30 -0
  145. package/dist/utils/logoConsoleOutput/logoConsoleOutput.d.ts +4 -0
  146. package/dist/utils/logoConsoleOutput/logoConsoleOutput.js +11 -0
  147. package/package.json +1 -1
  148. package/src/components/buttons/button.tsx +2 -0
  149. package/src/components/footer/footer.tsx +60 -42
  150. package/src/components/footer/loginContent.tsx +38 -2
  151. package/src/components/index.ts +0 -1
  152. package/src/components/inputs/checkbox.tsx +2 -0
  153. package/src/components/inputs/input.tsx +2 -0
  154. package/src/components/inputs/radio.tsx +2 -0
  155. package/src/components/inputs/range.tsx +2 -0
  156. package/src/components/inputs/shared/colorPickerPopup.tsx +2 -0
  157. package/src/components/inputs/shared/dateTimePickerPopup.tsx +2 -0
  158. package/src/components/inputs/textarea.tsx +2 -0
  159. package/src/components/inputs/toggle.tsx +2 -0
  160. package/src/components/toast/toastItem.tsx +2 -0
  161. package/src/globals.css +5 -0
  162. package/tsdown.config.ts +1 -0
@@ -0,0 +1,39 @@
1
+ //#region src/components/table/format.ts
2
+ const nullableTimeKeys = /* @__PURE__ */ new Set([
3
+ "date",
4
+ "last_sent",
5
+ "sent_at",
6
+ "time"
7
+ ]);
8
+ const RX_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
9
+ const RX_DATE = /^\d{4}-\d{2}-\d{2}$/;
10
+ const RX_TIME = /^\d{2}:\d{2}(:\d{2})?$/;
11
+ function formatValue(key, value) {
12
+ if (value === null || value === void 0) return nullableTimeKeys.has(key) ? "Never" : "-";
13
+ if (typeof value === "boolean") return value ? "Yes" : "No";
14
+ if (typeof value === "number") {
15
+ if (key.includes("capacity") && value === 0) return "Unlimited";
16
+ return value;
17
+ }
18
+ if (typeof value === "string") {
19
+ if (RX_DATETIME.test(value)) return new Date(value).toLocaleString("nb-NO", {
20
+ timeZone: "Europe/Oslo",
21
+ year: "numeric",
22
+ month: "2-digit",
23
+ day: "2-digit",
24
+ hour: "2-digit",
25
+ minute: "2-digit"
26
+ });
27
+ if (RX_DATE.test(value)) return new Date(value).toLocaleDateString("nb-NO", { timeZone: "Europe/Oslo" });
28
+ if (RX_TIME.test(value)) return (/* @__PURE__ */ new Date(`1970-01-01T${value}`)).toLocaleTimeString("nb-NO", {
29
+ timeZone: "Europe/Oslo",
30
+ hour: "2-digit",
31
+ minute: "2-digit"
32
+ });
33
+ return value;
34
+ }
35
+ if (Array.isArray(value)) return value.join(", ");
36
+ return String(value);
37
+ }
38
+ //#endregion
39
+ export { formatValue };
@@ -0,0 +1,109 @@
1
+ "use client";
2
+ import { DENSITY_TH, VARIANT_HEAD_BG, VARIANT_HEAD_BORDER, VARIANT_THEAD, VARIANT_THEAD_TH } from "./constants.js";
3
+ import { ChevronDown, ChevronUp, ChevronsUpDown } from "lucide-react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/table/header.tsx
6
+ function formatLabel(key, label) {
7
+ if (label) return label;
8
+ if (key.length <= 2) return key.toUpperCase();
9
+ return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ").replaceAll("-", " ").split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
10
+ }
11
+ function Header({ columns, sort, onSort, hasMenu, hasSelect, hasExpand, allSelected, someSelected, onSelectAll, variant, density }) {
12
+ function handleSort(key) {
13
+ onSort({
14
+ column: key,
15
+ order: sort?.column === key && sort?.order === "asc" ? "desc" : "asc"
16
+ });
17
+ }
18
+ return /* @__PURE__ */ jsx("thead", {
19
+ className: VARIANT_THEAD[variant],
20
+ children: /* @__PURE__ */ jsxs("tr", { children: [
21
+ hasSelect && /* @__PURE__ */ jsx("th", {
22
+ className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
23
+ style: {
24
+ width: "3rem",
25
+ minWidth: "3rem"
26
+ },
27
+ children: /* @__PURE__ */ jsx("div", {
28
+ className: "flex items-center justify-center",
29
+ children: /* @__PURE__ */ jsxs("button", {
30
+ type: "button",
31
+ "aria-label": allSelected ? "Deselect all" : "Select all",
32
+ onClick: onSelectAll,
33
+ className: `
34
+ h-4 w-4 rounded border flex items-center justify-center transition-colors cursor-pointer
35
+ ${allSelected || someSelected ? "bg-login border-login text-white" : "border-login-400 bg-transparent hover:border-login-200"}
36
+ `,
37
+ children: [someSelected && !allSelected && /* @__PURE__ */ jsx("span", { className: "block h-0.5 w-2 bg-white rounded-full" }), allSelected && /* @__PURE__ */ jsx("svg", {
38
+ className: "h-3 w-3",
39
+ viewBox: "0 0 12 12",
40
+ fill: "none",
41
+ children: /* @__PURE__ */ jsx("path", {
42
+ d: "M2 6l3 3 5-5",
43
+ stroke: "currentColor",
44
+ strokeWidth: "1.5",
45
+ strokeLinecap: "round",
46
+ strokeLinejoin: "round"
47
+ })
48
+ })]
49
+ })
50
+ })
51
+ }),
52
+ columns.map((col) => {
53
+ const sortable = col.sortable !== false;
54
+ const isActive = sort?.column === col.key;
55
+ const ariaSort = isActive ? sort.order === "asc" ? "ascending" : "descending" : "none";
56
+ const alignClass = col.align === "right" ? "justify-end" : col.align === "center" ? "justify-center" : "";
57
+ return /* @__PURE__ */ jsx("th", {
58
+ "aria-sort": sortable ? ariaSort : void 0,
59
+ style: col.width ? { width: col.width } : void 0,
60
+ className: `
61
+ sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}
62
+ text-xs font-medium uppercase tracking-wider
63
+ ${DENSITY_TH[density]} ${VARIANT_THEAD_TH[variant]}
64
+ `,
65
+ children: sortable ? /* @__PURE__ */ jsxs("button", {
66
+ type: "button",
67
+ className: `group inline-flex items-center gap-1.5 w-full cursor-pointer ${alignClass}`,
68
+ onClick: () => handleSort(col.key),
69
+ children: [/* @__PURE__ */ jsx("span", {
70
+ className: "whitespace-nowrap",
71
+ children: formatLabel(col.key, col.label)
72
+ }), /* @__PURE__ */ jsx("span", {
73
+ className: "shrink-0 text-current",
74
+ children: isActive ? sort.order === "asc" ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "h-3.5 w-3.5 opacity-0 group-hover:opacity-35 transition-opacity" })
75
+ })]
76
+ }) : /* @__PURE__ */ jsx("span", {
77
+ className: `flex w-full whitespace-nowrap ${alignClass}`,
78
+ children: formatLabel(col.key, col.label)
79
+ })
80
+ }, col.key);
81
+ }),
82
+ hasExpand && /* @__PURE__ */ jsx("th", {
83
+ className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
84
+ style: {
85
+ width: "2.5rem",
86
+ minWidth: "2.5rem"
87
+ },
88
+ children: /* @__PURE__ */ jsx("span", {
89
+ className: `
90
+ flex items-center justify-center
91
+ text-xs font-medium tracking-wider uppercase opacity-50
92
+ ${VARIANT_THEAD_TH[variant]}
93
+ `,
94
+ children: "+"
95
+ })
96
+ }),
97
+ hasMenu && /* @__PURE__ */ jsx("th", {
98
+ "aria-hidden": "true",
99
+ className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
100
+ style: {
101
+ width: "3.5rem",
102
+ minWidth: "3.5rem"
103
+ }
104
+ })
105
+ ] })
106
+ });
107
+ }
108
+ //#endregion
109
+ export { Header as default };
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+
3
+ //#region src/components/table/menu.d.ts
4
+ declare function MenuButton({
5
+ icon,
6
+ text,
7
+ hotKey,
8
+ onClick,
9
+ className
10
+ }: {
11
+ icon: React.ReactNode;
12
+ text: string;
13
+ hotKey?: string;
14
+ onClick: () => void;
15
+ className?: string;
16
+ }): React.JSX.Element;
17
+ //#endregion
18
+ export { MenuButton };
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { createContext, useContext, useEffect } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { createPortal } from "react-dom";
5
+ //#region src/components/table/menu.tsx
6
+ const MenuCtx = createContext({});
7
+ function Menu({ ref, children, anchor, onClose }) {
8
+ useEffect(() => {
9
+ function handleKey(e) {
10
+ if (e.key === "Escape") onClose?.();
11
+ }
12
+ window.addEventListener("keydown", handleKey);
13
+ return () => window.removeEventListener("keydown", handleKey);
14
+ }, [onClose]);
15
+ return createPortal(/* @__PURE__ */ jsx("div", {
16
+ ref,
17
+ role: "menu",
18
+ "aria-orientation": "vertical",
19
+ style: {
20
+ top: anchor.top,
21
+ right: anchor.right
22
+ },
23
+ className: "fixed z-[9999] w-44 overflow-hidden rounded-lg border border-login-500/60 bg-login-600 shadow-xl shadow-black/40",
24
+ children: /* @__PURE__ */ jsx(MenuCtx.Provider, {
25
+ value: { onClose },
26
+ children
27
+ })
28
+ }), document.body);
29
+ }
30
+ function MenuButton({ icon, text, hotKey, onClick, className = "" }) {
31
+ const { onClose } = useContext(MenuCtx);
32
+ useEffect(() => {
33
+ if (!hotKey) return;
34
+ function handleKey(e) {
35
+ const tag = e.target.tagName;
36
+ if (tag === "INPUT" || tag === "TEXTAREA") return;
37
+ if (e.key.toLowerCase() === hotKey.toLowerCase()) {
38
+ e.preventDefault();
39
+ onClick();
40
+ onClose?.();
41
+ }
42
+ }
43
+ window.addEventListener("keydown", handleKey);
44
+ return () => window.removeEventListener("keydown", handleKey);
45
+ }, [
46
+ hotKey,
47
+ onClick,
48
+ onClose
49
+ ]);
50
+ return /* @__PURE__ */ jsxs("button", {
51
+ role: "menuitem",
52
+ onClick: () => {
53
+ onClick();
54
+ onClose?.();
55
+ },
56
+ className: `
57
+ flex w-full items-center justify-between px-3 py-2 text-sm
58
+ text-login-75 transition-colors duration-100
59
+ hover:bg-login-500 focus:bg-login-500 focus:outline-none
60
+ first:rounded-t-lg last:rounded-b-lg
61
+ ${className}
62
+ `,
63
+ children: [/* @__PURE__ */ jsxs("span", {
64
+ className: "flex items-center gap-2",
65
+ children: [/* @__PURE__ */ jsx("span", {
66
+ className: "h-4 w-4 shrink-0 flex items-center justify-center",
67
+ children: icon
68
+ }), text]
69
+ }), hotKey && /* @__PURE__ */ jsx("kbd", {
70
+ className: "font-mono text-xs opacity-40",
71
+ children: hotKey
72
+ })]
73
+ });
74
+ }
75
+ //#endregion
76
+ export { MenuButton, Menu as default };
@@ -0,0 +1,14 @@
1
+ import { TableVariant } from "./types.js";
2
+
3
+ //#region src/components/table/pagination.d.ts
4
+ type PaginationProps = {
5
+ totalRows: number;
6
+ pageSize: number;
7
+ variant?: TableVariant;
8
+ urlState?: boolean;
9
+ page?: number;
10
+ onPageChange?: (page: number) => void;
11
+ };
12
+ declare function Pagination(props: PaginationProps): import("react").JSX.Element;
13
+ //#endregion
14
+ export { PaginationProps, Pagination as default };
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import { Suspense, useState } from "react";
3
+ import { ChevronLeft, ChevronRight } from "lucide-react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { usePathname, useRouter, useSearchParams } from "next/navigation.js";
6
+ //#region src/components/table/pagination.tsx
7
+ function computeTotalPages(totalRows, pageSize) {
8
+ return Math.max(1, pageSize > 0 ? Math.ceil(totalRows / pageSize) : 1);
9
+ }
10
+ function pageRange(current, total) {
11
+ const delta = 2;
12
+ const left = Math.max(1, current - delta);
13
+ const right = Math.min(total, current + delta);
14
+ const pages = [];
15
+ if (left > 1) {
16
+ pages.push(1);
17
+ if (left > 2) pages.push("…");
18
+ }
19
+ for (let i = left; i <= right; i++) pages.push(i);
20
+ if (right < total) {
21
+ if (right < total - 1) pages.push("…");
22
+ pages.push(total);
23
+ }
24
+ return pages;
25
+ }
26
+ function PaginationShell({ page, totalPages, totalRows, pageSize, onPageChange, variant }) {
27
+ const start = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
28
+ const end = Math.min(page * pageSize, totalRows);
29
+ const pages = pageRange(page, totalPages);
30
+ const btnBase = `
31
+ flex items-center justify-center rounded-md border text-sm transition-colors duration-100
32
+ disabled:opacity-35 disabled:cursor-not-allowed
33
+ `;
34
+ const btnStyle = variant === "original" ? "border-login-500/50 bg-login-700 hover:bg-login-600 text-login-100" : "border-login-500/40 bg-transparent hover:bg-login-700/60 text-login-100";
35
+ const pageActive = variant === "original" ? "border-login bg-login/10 text-login" : "border-login text-login bg-transparent";
36
+ const pageInactive = variant === "original" ? "border-login-500/50 bg-login-700 text-login-100 hover:bg-login-600" : "border-transparent bg-transparent text-login-200 hover:text-login-75 hover:bg-login-700/60";
37
+ return /* @__PURE__ */ jsxs("div", {
38
+ className: "flex items-center justify-between w-full gap-4 flex-wrap",
39
+ children: [/* @__PURE__ */ jsx("span", {
40
+ className: "text-xs text-login-300 tabular-nums",
41
+ children: totalRows === 0 ? "No results" : `${start}-${end} of ${totalRows}`
42
+ }), /* @__PURE__ */ jsxs("nav", {
43
+ className: "flex items-center gap-1.5",
44
+ "aria-label": "Pagination",
45
+ children: [
46
+ /* @__PURE__ */ jsx("button", {
47
+ type: "button",
48
+ "aria-label": "Previous page",
49
+ disabled: page <= 1,
50
+ onClick: () => onPageChange(page - 1),
51
+ className: `${btnBase} ${btnStyle} h-8 w-8`,
52
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
53
+ }),
54
+ pages.map((p, i) => p === "…" ? /* @__PURE__ */ jsx("span", {
55
+ className: "w-8 text-center text-sm text-login-400 select-none",
56
+ children: "…"
57
+ }, `ellipsis-${i}`) : /* @__PURE__ */ jsx("button", {
58
+ type: "button",
59
+ "aria-current": p === page ? "page" : void 0,
60
+ onClick: () => onPageChange(p),
61
+ className: `${btnBase} h-8 min-w-8 px-2 tabular-nums ${p === page ? pageActive : pageInactive}`,
62
+ children: p
63
+ }, p)),
64
+ /* @__PURE__ */ jsx("button", {
65
+ type: "button",
66
+ "aria-label": "Next page",
67
+ disabled: page >= totalPages,
68
+ onClick: () => onPageChange(page + 1),
69
+ className: `${btnBase} ${btnStyle} h-8 w-8`,
70
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
71
+ })
72
+ ]
73
+ })]
74
+ });
75
+ }
76
+ function PaginationLocalState({ totalRows, pageSize, variant = "original" }) {
77
+ const [page, setPage] = useState(1);
78
+ return /* @__PURE__ */ jsx(PaginationShell, {
79
+ page,
80
+ totalPages: computeTotalPages(totalRows, pageSize),
81
+ totalRows,
82
+ pageSize,
83
+ onPageChange: setPage,
84
+ variant
85
+ });
86
+ }
87
+ function PaginationURLState({ totalRows, pageSize, variant = "original" }) {
88
+ const router = useRouter();
89
+ const pathname = usePathname();
90
+ const searchParams = useSearchParams();
91
+ const page = Math.max(1, parseInt(searchParams.get("page") ?? "1", 10) || 1);
92
+ function onPageChange(p) {
93
+ const params = new URLSearchParams(searchParams.toString());
94
+ params.set("page", String(p));
95
+ router.replace(`${pathname}?${params.toString()}`);
96
+ }
97
+ return /* @__PURE__ */ jsx(PaginationShell, {
98
+ page,
99
+ totalPages: computeTotalPages(totalRows, pageSize),
100
+ totalRows,
101
+ pageSize,
102
+ onPageChange,
103
+ variant
104
+ });
105
+ }
106
+ function Pagination(props) {
107
+ const { totalRows, pageSize, variant = "original", urlState, page, onPageChange } = props;
108
+ if (urlState) return /* @__PURE__ */ jsx(Suspense, {
109
+ fallback: /* @__PURE__ */ jsx(PaginationShell, {
110
+ page: 1,
111
+ totalPages: computeTotalPages(totalRows, pageSize),
112
+ totalRows,
113
+ pageSize,
114
+ onPageChange: () => {},
115
+ variant
116
+ }),
117
+ children: /* @__PURE__ */ jsx(PaginationURLState, { ...props })
118
+ });
119
+ if (page !== void 0 && onPageChange !== void 0) return /* @__PURE__ */ jsx(PaginationShell, {
120
+ page,
121
+ totalPages: computeTotalPages(totalRows, pageSize),
122
+ totalRows,
123
+ pageSize,
124
+ onPageChange,
125
+ variant
126
+ });
127
+ return /* @__PURE__ */ jsx(PaginationLocalState, { ...props });
128
+ }
129
+ //#endregion
130
+ export { Pagination as default };
@@ -0,0 +1,39 @@
1
+ import { DENSITY_TD, VARIANT_ROW_BORDER, VARIANT_TBODY } from "./constants.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/components/table/skeleton.tsx
4
+ function Skeleton({ columns, rows, variant, density, hasMenu, hasSelect }) {
5
+ return /* @__PURE__ */ jsx("tbody", {
6
+ className: VARIANT_TBODY[variant],
7
+ children: Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxs("tr", { children: [
8
+ hasSelect && /* @__PURE__ */ jsx("td", {
9
+ className: `align-middle ${VARIANT_ROW_BORDER[variant]}`,
10
+ style: {
11
+ width: "3rem",
12
+ minWidth: "3rem"
13
+ },
14
+ children: /* @__PURE__ */ jsx("span", { className: "block h-4 w-4 mx-auto rounded animate-shimmer" })
15
+ }),
16
+ columns.map((col, colIdx) => /* @__PURE__ */ jsx("td", {
17
+ style: col.width ? { width: col.width } : void 0,
18
+ className: `align-middle ${DENSITY_TD[density]} ${VARIANT_ROW_BORDER[variant]}`,
19
+ children: /* @__PURE__ */ jsx("span", {
20
+ className: "block h-4 rounded animate-shimmer",
21
+ style: {
22
+ width: colIdx === 0 ? `${8 + (rowIdx * 17 + colIdx * 31) % 5}rem` : `${5 + (rowIdx * 13 + colIdx * 23) % 5}rem`,
23
+ animationDelay: `${(rowIdx * columns.length + colIdx) * 40}ms`
24
+ }
25
+ })
26
+ }, col.key)),
27
+ hasMenu && /* @__PURE__ */ jsx("td", {
28
+ className: `align-middle pr-3 ${VARIANT_ROW_BORDER[variant]}`,
29
+ style: {
30
+ width: "3.5rem",
31
+ minWidth: "3.5rem"
32
+ },
33
+ children: /* @__PURE__ */ jsx("span", { className: "block h-5 w-5 ml-auto rounded animate-shimmer" })
34
+ })
35
+ ] }, rowIdx))
36
+ });
37
+ }
38
+ //#endregion
39
+ export { Skeleton as default };
@@ -0,0 +1,7 @@
1
+ import { Column, Density, SortState, TableColor, TableProps, TableVariant } from "./types.js";
2
+ import { MenuButton } from "./menu.js";
3
+
4
+ //#region src/components/table/table.d.ts
5
+ declare function Table<T extends Record<string, unknown> = Record<string, unknown>>(props: TableProps<T>): import("react").JSX.Element;
6
+ //#endregion
7
+ export { MenuButton, Table };
@@ -0,0 +1,213 @@
1
+ "use client";
2
+ import { VARIANT_CONTAINER } from "./constants.js";
3
+ import Header from "./header.js";
4
+ import { resolveId } from "./utils.js";
5
+ import { MenuButton } from "./menu.js";
6
+ import Body from "./body.js";
7
+ import Skeleton from "./skeleton.js";
8
+ import Empty from "./empty.js";
9
+ import Pagination from "./pagination.js";
10
+ import { Suspense, useMemo, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { usePathname, useRouter, useSearchParams } from "next/navigation.js";
13
+ //#region src/components/table/table.tsx
14
+ function TableShell({ data, columns, idKey, variant, density, striped, loading, loadingRows, emptyState, redirectPath, onRowClick, renderExpandedRow, selectable, selectedIds, onSelectionChange, sort, onSort, page, onPageChange, totalRows, pageSize, hidePagination, menuItems, className }) {
15
+ const allIds = data.map((row) => resolveId(row, idKey, columns));
16
+ const allIdsSet = useMemo(() => new Set(allIds), [allIds]);
17
+ const allSelected = allIds.length > 0 && allIds.every((id) => selectedIds.includes(id));
18
+ const someSelected = !allSelected && allIds.some((id) => selectedIds.includes(id));
19
+ function handleSelectAll() {
20
+ if (allSelected) onSelectionChange(selectedIds.filter((id) => !allIdsSet.has(id)));
21
+ else onSelectionChange([.../* @__PURE__ */ new Set([...selectedIds, ...allIds])]);
22
+ }
23
+ const isEmpty = !loading && data.length === 0;
24
+ const showPagination = !hidePagination && pageSize > 0 && totalRows > 0;
25
+ const hasMenu = Boolean(menuItems);
26
+ const hasSelect = Boolean(selectable);
27
+ const hasExpand = Boolean(renderExpandedRow);
28
+ const paginationPad = variant === "original" ? "px-4 pb-4 pt-3" : "pt-4";
29
+ return /* @__PURE__ */ jsxs("div", {
30
+ className: [
31
+ "flex flex-col min-h-0 w-full",
32
+ VARIANT_CONTAINER[variant],
33
+ className
34
+ ].filter(Boolean).join(" "),
35
+ children: [/* @__PURE__ */ jsx("div", {
36
+ className: "flex-1 min-h-0 overflow-auto",
37
+ children: /* @__PURE__ */ jsxs("table", {
38
+ className: "min-w-full table-auto border-separate border-spacing-0",
39
+ children: [/* @__PURE__ */ jsx(Header, {
40
+ columns,
41
+ sort,
42
+ onSort,
43
+ hasMenu,
44
+ hasSelect,
45
+ hasExpand,
46
+ allSelected,
47
+ someSelected,
48
+ onSelectAll: handleSelectAll,
49
+ variant,
50
+ density
51
+ }), loading ? /* @__PURE__ */ jsx(Skeleton, {
52
+ columns,
53
+ rows: loadingRows,
54
+ variant,
55
+ density,
56
+ hasMenu,
57
+ hasSelect
58
+ }) : isEmpty ? /* @__PURE__ */ jsx(Empty, { emptyState }) : /* @__PURE__ */ jsx(Body, {
59
+ data,
60
+ columns,
61
+ idKey,
62
+ variant,
63
+ density,
64
+ striped,
65
+ redirectPath,
66
+ onRowClick,
67
+ renderExpandedRow,
68
+ selectable,
69
+ selectedIds,
70
+ onSelectionChange,
71
+ menuItems
72
+ })]
73
+ })
74
+ }), showPagination && /* @__PURE__ */ jsx("div", {
75
+ className: paginationPad,
76
+ children: /* @__PURE__ */ jsx(Pagination, {
77
+ page,
78
+ totalRows,
79
+ pageSize,
80
+ onPageChange,
81
+ variant
82
+ })
83
+ })]
84
+ });
85
+ }
86
+ function useInternalSelection(props) {
87
+ const [internal, setInternal] = useState([]);
88
+ return {
89
+ selectedIds: props.selectedIds ?? internal,
90
+ onSelectionChange: props.onSelectionChange ?? setInternal
91
+ };
92
+ }
93
+ function applyDefaults(props) {
94
+ return {
95
+ variant: props.variant ?? "original",
96
+ density: props.density ?? "comfortable",
97
+ selectable: props.selectable ?? false,
98
+ loading: props.loading ?? false,
99
+ loadingRows: props.loadingRows ?? props.pageSize ?? 5,
100
+ striped: props.striped ?? false,
101
+ hidePagination: props.hidePagination ?? false,
102
+ pageSize: props.pageSize ?? 0
103
+ };
104
+ }
105
+ function TableLocalState(props) {
106
+ const defaults = applyDefaults(props);
107
+ const selection = useInternalSelection(props);
108
+ const [sort, setSort] = useState(void 0);
109
+ const [page, setPage] = useState(1);
110
+ const ps = defaults.pageSize;
111
+ const allRows = props.data.length;
112
+ const sorted = useMemo(() => {
113
+ if (!sort) return props.data;
114
+ return [...props.data].sort((a, b) => {
115
+ const av = a[sort.column];
116
+ const bv = b[sort.column];
117
+ const cmp = String(av ?? "").localeCompare(String(bv ?? ""), void 0, {
118
+ numeric: true,
119
+ sensitivity: "base"
120
+ });
121
+ return sort.order === "asc" ? cmp : -cmp;
122
+ });
123
+ }, [props.data, sort]);
124
+ const displayData = ps > 0 ? sorted.slice((page - 1) * ps, page * ps) : sorted;
125
+ function handleSort(newSort) {
126
+ setSort(newSort);
127
+ setPage(1);
128
+ }
129
+ return /* @__PURE__ */ jsx(TableShell, {
130
+ ...props,
131
+ ...defaults,
132
+ ...selection,
133
+ data: displayData,
134
+ sort,
135
+ onSort: handleSort,
136
+ page,
137
+ onPageChange: setPage,
138
+ totalRows: ps > 0 ? allRows : props.totalRows ?? allRows
139
+ });
140
+ }
141
+ function TableURLState(props) {
142
+ const defaults = applyDefaults(props);
143
+ const selection = useInternalSelection(props);
144
+ const router = useRouter();
145
+ const pathname = usePathname();
146
+ const searchParams = useSearchParams();
147
+ const urlColumn = searchParams.get("column") ?? "";
148
+ const urlOrder = searchParams.get("order") ?? "asc";
149
+ const urlPage = Math.max(1, parseInt(searchParams.get("page") ?? "1", 10) || 1);
150
+ const sort = urlColumn ? {
151
+ column: urlColumn,
152
+ order: urlOrder
153
+ } : void 0;
154
+ function handleSort(newSort) {
155
+ const p = new URLSearchParams(searchParams.toString());
156
+ p.set("column", newSort.column);
157
+ p.set("order", newSort.order);
158
+ p.set("page", "1");
159
+ router.replace(`${pathname}?${p.toString()}`);
160
+ }
161
+ function handlePageChange(newPage) {
162
+ const p = new URLSearchParams(searchParams.toString());
163
+ p.set("page", String(newPage));
164
+ router.replace(`${pathname}?${p.toString()}`);
165
+ }
166
+ return /* @__PURE__ */ jsx(TableShell, {
167
+ ...props,
168
+ ...defaults,
169
+ ...selection,
170
+ sort,
171
+ onSort: handleSort,
172
+ page: urlPage,
173
+ onPageChange: handlePageChange,
174
+ totalRows: props.totalRows ?? props.data.length
175
+ });
176
+ }
177
+ function TableControlled(props) {
178
+ const defaults = applyDefaults(props);
179
+ const selection = useInternalSelection(props);
180
+ return /* @__PURE__ */ jsx(TableShell, {
181
+ ...props,
182
+ ...defaults,
183
+ ...selection,
184
+ sort: props.sort,
185
+ onSort: props.onSort,
186
+ page: props.page ?? 1,
187
+ onPageChange: props.onPageChange ?? (() => {}),
188
+ totalRows: props.totalRows ?? props.data.length
189
+ });
190
+ }
191
+ function Table(props) {
192
+ if (props.sort !== void 0 || props.onSort !== void 0) return /* @__PURE__ */ jsx(TableControlled, { ...props });
193
+ if (props.urlState) {
194
+ const defaults = applyDefaults(props);
195
+ return /* @__PURE__ */ jsx(Suspense, {
196
+ fallback: /* @__PURE__ */ jsx(TableShell, {
197
+ ...props,
198
+ ...defaults,
199
+ selectedIds: props.selectedIds ?? [],
200
+ onSelectionChange: props.onSelectionChange ?? (() => {}),
201
+ sort: void 0,
202
+ onSort: () => {},
203
+ page: 1,
204
+ onPageChange: () => {},
205
+ totalRows: props.totalRows ?? props.data.length
206
+ }),
207
+ children: /* @__PURE__ */ jsx(TableURLState, { ...props })
208
+ });
209
+ }
210
+ return /* @__PURE__ */ jsx(TableLocalState, { ...props });
211
+ }
212
+ //#endregion
213
+ export { MenuButton, Table };