@mshuja288/aksuicore 0.2.1 → 0.4.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/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/dist/aksui.css +386 -6
- package/dist/index.cjs +496 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -7
- package/dist/index.d.ts +53 -7
- package/dist/index.js +496 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/patterns/AppShell.tsx +140 -17
- package/src/patterns/RecordWorkspace.tsx +426 -43
- package/src/patterns/index.ts +8 -2
- package/src/primitives/SideNav.tsx +3 -1
- package/src/primitives/Topbar.tsx +38 -8
- package/src/theme/patterns.css +223 -0
- package/src/theme/primitives.css +9 -1
- package/src/theme/shell.css +154 -5
package/dist/index.js
CHANGED
|
@@ -515,31 +515,48 @@ function Snackbar({
|
|
|
515
515
|
);
|
|
516
516
|
}
|
|
517
517
|
var StatusMessage = Snackbar;
|
|
518
|
+
function MenuIcon({ open }) {
|
|
519
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", children: open ? /* @__PURE__ */ jsx("path", { d: "M5 5l10 10M15 5 5 15", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }) : /* @__PURE__ */ jsx("path", { d: "M3.5 5.5h13M3.5 10h13M3.5 14.5h13", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }) });
|
|
520
|
+
}
|
|
518
521
|
function Topbar({
|
|
519
522
|
title,
|
|
520
523
|
logoSrc,
|
|
521
524
|
logoAlt = "Logo",
|
|
522
525
|
username = "",
|
|
523
526
|
onLogout,
|
|
527
|
+
showMenuButton = false,
|
|
528
|
+
menuOpen = false,
|
|
529
|
+
onMenuClick,
|
|
524
530
|
children
|
|
525
531
|
}) {
|
|
526
|
-
const [
|
|
532
|
+
const [accountOpen, setAccountOpen] = useState(false);
|
|
527
533
|
const menuRef = useRef(null);
|
|
528
534
|
const initial = (username || "U").slice(0, 1).toUpperCase();
|
|
529
535
|
useEffect(() => {
|
|
530
|
-
if (!
|
|
536
|
+
if (!accountOpen) {
|
|
531
537
|
return;
|
|
532
538
|
}
|
|
533
539
|
function onPointerDown(event) {
|
|
534
540
|
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
535
|
-
|
|
541
|
+
setAccountOpen(false);
|
|
536
542
|
}
|
|
537
543
|
}
|
|
538
544
|
document.addEventListener("mousedown", onPointerDown);
|
|
539
545
|
return () => document.removeEventListener("mousedown", onPointerDown);
|
|
540
|
-
}, [
|
|
546
|
+
}, [accountOpen]);
|
|
541
547
|
return /* @__PURE__ */ jsxs("header", { className: "topbar", children: [
|
|
542
548
|
/* @__PURE__ */ jsxs("div", { className: "topbar-brand", children: [
|
|
549
|
+
showMenuButton && onMenuClick ? /* @__PURE__ */ jsx(
|
|
550
|
+
"button",
|
|
551
|
+
{
|
|
552
|
+
type: "button",
|
|
553
|
+
className: "ghost topbar-menu-button",
|
|
554
|
+
"aria-label": menuOpen ? "Close navigation menu" : "Open navigation menu",
|
|
555
|
+
"aria-expanded": menuOpen,
|
|
556
|
+
onClick: onMenuClick,
|
|
557
|
+
children: /* @__PURE__ */ jsx(MenuIcon, { open: menuOpen })
|
|
558
|
+
}
|
|
559
|
+
) : null,
|
|
543
560
|
logoSrc ? /* @__PURE__ */ jsx(
|
|
544
561
|
"img",
|
|
545
562
|
{
|
|
@@ -562,13 +579,13 @@ function Topbar({
|
|
|
562
579
|
type: "button",
|
|
563
580
|
className: "avatar-button",
|
|
564
581
|
"aria-haspopup": "menu",
|
|
565
|
-
"aria-expanded":
|
|
566
|
-
onClick: () =>
|
|
582
|
+
"aria-expanded": accountOpen,
|
|
583
|
+
onClick: () => setAccountOpen((open) => !open),
|
|
567
584
|
title: username || "Account",
|
|
568
585
|
children: /* @__PURE__ */ jsx("span", { className: "avatar", children: initial })
|
|
569
586
|
}
|
|
570
587
|
),
|
|
571
|
-
|
|
588
|
+
accountOpen ? /* @__PURE__ */ jsxs("div", { className: "avatar-menu", role: "menu", children: [
|
|
572
589
|
username ? /* @__PURE__ */ jsx("p", { className: "avatar-name", children: username }) : null,
|
|
573
590
|
/* @__PURE__ */ jsx(
|
|
574
591
|
Button,
|
|
@@ -576,7 +593,7 @@ function Topbar({
|
|
|
576
593
|
variant: "ghost",
|
|
577
594
|
role: "menuitem",
|
|
578
595
|
onClick: () => {
|
|
579
|
-
|
|
596
|
+
setAccountOpen(false);
|
|
580
597
|
onLogout();
|
|
581
598
|
},
|
|
582
599
|
children: "Logout"
|
|
@@ -591,9 +608,10 @@ function SideNav({
|
|
|
591
608
|
items,
|
|
592
609
|
activeId,
|
|
593
610
|
onSelect,
|
|
594
|
-
label = "Sections"
|
|
611
|
+
label = "Sections",
|
|
612
|
+
className = ""
|
|
595
613
|
}) {
|
|
596
|
-
return /* @__PURE__ */ jsx("nav", { className: "side-nav", "aria-label": label, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
614
|
+
return /* @__PURE__ */ jsx("nav", { className: ["side-nav", className].filter(Boolean).join(" "), "aria-label": label, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
597
615
|
"button",
|
|
598
616
|
{
|
|
599
617
|
type: "button",
|
|
@@ -695,28 +713,131 @@ function AppShell({
|
|
|
695
713
|
activeNavId,
|
|
696
714
|
onNavSelect,
|
|
697
715
|
navLabel = "Sections",
|
|
698
|
-
|
|
716
|
+
mobileNav = "drawer",
|
|
717
|
+
showBottomNav,
|
|
718
|
+
navDrawerOpen,
|
|
719
|
+
onNavDrawerOpenChange,
|
|
699
720
|
topbarActions,
|
|
700
721
|
children,
|
|
701
722
|
screenClassName = ""
|
|
702
723
|
}) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
724
|
+
const [internalDrawerOpen, setInternalDrawerOpen] = useState(false);
|
|
725
|
+
const drawerControlled = navDrawerOpen !== void 0;
|
|
726
|
+
const drawerOpen = drawerControlled ? navDrawerOpen : internalDrawerOpen;
|
|
727
|
+
const useBottomNav = showBottomNav ?? (mobileNav === "bottom" || mobileNav === "hybrid");
|
|
728
|
+
const useDrawer = mobileNav === "drawer" || mobileNav === "hybrid";
|
|
729
|
+
const activeItem = navItems.find((item) => item.id === activeNavId);
|
|
730
|
+
function setDrawerOpen(open) {
|
|
731
|
+
if (!drawerControlled) {
|
|
732
|
+
setInternalDrawerOpen(open);
|
|
733
|
+
}
|
|
734
|
+
onNavDrawerOpenChange?.(open);
|
|
735
|
+
}
|
|
736
|
+
useEffect(() => {
|
|
737
|
+
if (!drawerOpen) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
function onKeyDown(event) {
|
|
741
|
+
if (event.key === "Escape") {
|
|
742
|
+
if (!drawerControlled) {
|
|
743
|
+
setInternalDrawerOpen(false);
|
|
744
|
+
}
|
|
745
|
+
onNavDrawerOpenChange?.(false);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
document.addEventListener("keydown", onKeyDown);
|
|
749
|
+
const previousOverflow = document.body.style.overflow;
|
|
750
|
+
document.body.style.overflow = "hidden";
|
|
751
|
+
return () => {
|
|
752
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
753
|
+
document.body.style.overflow = previousOverflow;
|
|
754
|
+
};
|
|
755
|
+
}, [drawerControlled, drawerOpen, onNavDrawerOpenChange]);
|
|
756
|
+
function selectNav(id) {
|
|
757
|
+
onNavSelect(id);
|
|
758
|
+
setDrawerOpen(false);
|
|
759
|
+
}
|
|
760
|
+
return /* @__PURE__ */ jsxs(
|
|
761
|
+
"div",
|
|
762
|
+
{
|
|
763
|
+
className: [
|
|
764
|
+
"aksui-root",
|
|
765
|
+
"app-shell",
|
|
766
|
+
`mobile-nav-${mobileNav}`,
|
|
767
|
+
useBottomNav ? "has-bottom-nav" : "",
|
|
768
|
+
drawerOpen ? "nav-drawer-open" : ""
|
|
769
|
+
].filter(Boolean).join(" "),
|
|
770
|
+
children: [
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
Topbar,
|
|
773
|
+
{
|
|
774
|
+
title,
|
|
775
|
+
logoSrc,
|
|
776
|
+
logoAlt,
|
|
777
|
+
username,
|
|
778
|
+
onLogout,
|
|
779
|
+
showMenuButton: useDrawer,
|
|
780
|
+
menuOpen: drawerOpen,
|
|
781
|
+
onMenuClick: () => setDrawerOpen(!drawerOpen),
|
|
782
|
+
children: topbarActions
|
|
783
|
+
}
|
|
784
|
+
),
|
|
785
|
+
/* @__PURE__ */ jsxs("div", { className: "app-body", children: [
|
|
786
|
+
/* @__PURE__ */ jsx(SideNav, { items: navItems, activeId: activeNavId, onSelect: onNavSelect, label: navLabel }),
|
|
787
|
+
/* @__PURE__ */ jsx("div", { className: ["screen", screenClassName].filter(Boolean).join(" "), children })
|
|
788
|
+
] }),
|
|
789
|
+
useDrawer ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
790
|
+
/* @__PURE__ */ jsx(
|
|
791
|
+
"button",
|
|
792
|
+
{
|
|
793
|
+
type: "button",
|
|
794
|
+
className: drawerOpen ? "nav-drawer-backdrop open" : "nav-drawer-backdrop",
|
|
795
|
+
"aria-label": "Close navigation menu",
|
|
796
|
+
tabIndex: drawerOpen ? 0 : -1,
|
|
797
|
+
onClick: () => setDrawerOpen(false)
|
|
798
|
+
}
|
|
799
|
+
),
|
|
800
|
+
/* @__PURE__ */ jsxs(
|
|
801
|
+
"aside",
|
|
802
|
+
{
|
|
803
|
+
className: drawerOpen ? "nav-drawer open" : "nav-drawer",
|
|
804
|
+
"aria-label": navLabel,
|
|
805
|
+
"aria-hidden": !drawerOpen,
|
|
806
|
+
children: [
|
|
807
|
+
/* @__PURE__ */ jsxs("div", { className: "nav-drawer-head", children: [
|
|
808
|
+
/* @__PURE__ */ jsx("p", { className: "nav-drawer-title", children: navLabel }),
|
|
809
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ghost nav-drawer-close", onClick: () => setDrawerOpen(false), children: "Close" })
|
|
810
|
+
] }),
|
|
811
|
+
/* @__PURE__ */ jsx(
|
|
812
|
+
SideNav,
|
|
813
|
+
{
|
|
814
|
+
className: "nav-drawer-nav",
|
|
815
|
+
items: navItems,
|
|
816
|
+
activeId: activeNavId,
|
|
817
|
+
onSelect: selectNav,
|
|
818
|
+
label: navLabel
|
|
819
|
+
}
|
|
820
|
+
)
|
|
821
|
+
]
|
|
822
|
+
}
|
|
823
|
+
)
|
|
824
|
+
] }) : null,
|
|
825
|
+
useBottomNav ? /* @__PURE__ */ jsx("nav", { className: "bottom-nav", "aria-label": navLabel, children: mobileNav === "hybrid" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
826
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "active bottom-nav-current", disabled: true, children: activeItem?.short ?? activeItem?.label ?? "Section" }),
|
|
827
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "bottom-nav-menu", onClick: () => setDrawerOpen(true), children: "Menu" })
|
|
828
|
+
] }) : navItems.map((item) => /* @__PURE__ */ jsx(
|
|
829
|
+
"button",
|
|
830
|
+
{
|
|
831
|
+
type: "button",
|
|
832
|
+
className: activeNavId === item.id ? "active" : "",
|
|
833
|
+
onClick: () => onNavSelect(item.id),
|
|
834
|
+
children: item.short ?? item.label
|
|
835
|
+
},
|
|
836
|
+
item.id
|
|
837
|
+
)) }) : null
|
|
838
|
+
]
|
|
839
|
+
}
|
|
840
|
+
);
|
|
720
841
|
}
|
|
721
842
|
function useIsClient2() {
|
|
722
843
|
return useSyncExternalStore(
|
|
@@ -726,6 +847,42 @@ function useIsClient2() {
|
|
|
726
847
|
() => false
|
|
727
848
|
);
|
|
728
849
|
}
|
|
850
|
+
var EXPORT_FORMAT_OPTIONS = [
|
|
851
|
+
{ format: "csv", label: "CSV" },
|
|
852
|
+
{ format: "xlsx", label: "Excel" },
|
|
853
|
+
{ format: "json", label: "JSON" }
|
|
854
|
+
];
|
|
855
|
+
function defaultVisibleKeys(columns) {
|
|
856
|
+
return columns.filter((column) => column.defaultVisible !== false).map((column) => column.key);
|
|
857
|
+
}
|
|
858
|
+
function readStoredColumnKeys(prefsKey, fallback, allowed) {
|
|
859
|
+
if (!prefsKey || typeof window === "undefined") {
|
|
860
|
+
return fallback;
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
const raw = window.localStorage.getItem(prefsKey);
|
|
864
|
+
if (!raw) {
|
|
865
|
+
return fallback;
|
|
866
|
+
}
|
|
867
|
+
const parsed = JSON.parse(raw);
|
|
868
|
+
if (!Array.isArray(parsed)) {
|
|
869
|
+
return fallback;
|
|
870
|
+
}
|
|
871
|
+
const keys = parsed.filter((entry) => typeof entry === "string" && allowed.has(entry));
|
|
872
|
+
return keys.length > 0 ? keys : fallback;
|
|
873
|
+
} catch {
|
|
874
|
+
return fallback;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
function writeStoredColumnKeys(prefsKey, keys) {
|
|
878
|
+
if (!prefsKey || typeof window === "undefined") {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
window.localStorage.setItem(prefsKey, JSON.stringify(keys));
|
|
883
|
+
} catch {
|
|
884
|
+
}
|
|
885
|
+
}
|
|
729
886
|
function compareColumnValues(column, left, right) {
|
|
730
887
|
const leftRaw = column.display ? column.display(left) : column.value(left);
|
|
731
888
|
const rightRaw = column.display ? column.display(right) : column.value(right);
|
|
@@ -759,6 +916,9 @@ function ResetIcon() {
|
|
|
759
916
|
/* @__PURE__ */ jsx("path", { d: "M4.5 4.2v4h4", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
760
917
|
] });
|
|
761
918
|
}
|
|
919
|
+
function ColumnsIcon() {
|
|
920
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 4.5h3.5v11H4zM8.25 4.5h3.5v11h-3.5zM12.5 4.5H16v11h-3.5z", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) });
|
|
921
|
+
}
|
|
762
922
|
function ChevronIcon({ open }) {
|
|
763
923
|
return /* @__PURE__ */ jsx("svg", { className: open ? "side-rail-chevron open" : "side-rail-chevron", viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M8 4.5 13.5 10 8 15.5", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
764
924
|
}
|
|
@@ -768,6 +928,20 @@ function PreviewIcon() {
|
|
|
768
928
|
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.6" })
|
|
769
929
|
] });
|
|
770
930
|
}
|
|
931
|
+
function DownloadIcon() {
|
|
932
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", children: [
|
|
933
|
+
/* @__PURE__ */ jsx("path", { d: "M10 3.5v8.2", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }),
|
|
934
|
+
/* @__PURE__ */ jsx("path", { d: "M6.5 9.2 10 12.8l3.5-3.6", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
935
|
+
/* @__PURE__ */ jsx("path", { d: "M4 15.5h12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })
|
|
936
|
+
] });
|
|
937
|
+
}
|
|
938
|
+
function MoreIcon() {
|
|
939
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", children: [
|
|
940
|
+
/* @__PURE__ */ jsx("circle", { cx: "4.5", cy: "10", r: "1.5", fill: "currentColor" }),
|
|
941
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "1.5", fill: "currentColor" }),
|
|
942
|
+
/* @__PURE__ */ jsx("circle", { cx: "15.5", cy: "10", r: "1.5", fill: "currentColor" })
|
|
943
|
+
] });
|
|
944
|
+
}
|
|
771
945
|
function DetailList({ lines }) {
|
|
772
946
|
return /* @__PURE__ */ jsx("dl", { className: "record-detail-list", children: lines.map((line) => /* @__PURE__ */ jsxs("div", { className: "record-detail-row", children: [
|
|
773
947
|
/* @__PURE__ */ jsx("dt", { children: line.label }),
|
|
@@ -788,23 +962,86 @@ function RecordWorkspace({
|
|
|
788
962
|
onDelete,
|
|
789
963
|
canUpdate,
|
|
790
964
|
canDelete,
|
|
965
|
+
onDownload,
|
|
966
|
+
canDownload = true,
|
|
967
|
+
downloadBusy = false,
|
|
968
|
+
showColumnPicker = true,
|
|
969
|
+
columnPrefsKey,
|
|
970
|
+
visibleColumnKeys,
|
|
971
|
+
defaultVisibleColumnKeys,
|
|
972
|
+
onVisibleColumnKeysChange,
|
|
791
973
|
toolbar,
|
|
792
974
|
form
|
|
793
975
|
}) {
|
|
794
|
-
const
|
|
976
|
+
const allowedKeys = useMemo(() => new Set(columns.map((column) => column.key)), [columns]);
|
|
977
|
+
const fallbackKeys = useMemo(
|
|
978
|
+
() => defaultVisibleColumnKeys ?? defaultVisibleKeys(columns),
|
|
979
|
+
[columns, defaultVisibleColumnKeys]
|
|
980
|
+
);
|
|
981
|
+
const isControlled = visibleColumnKeys !== void 0;
|
|
982
|
+
const [internalVisibleKeys, setInternalVisibleKeys] = useState(
|
|
983
|
+
() => readStoredColumnKeys(columnPrefsKey, fallbackKeys, allowedKeys)
|
|
984
|
+
);
|
|
795
985
|
const [filters, setFilters] = useState({});
|
|
796
986
|
const [sort, setSort] = useState(null);
|
|
797
987
|
const [sideOpen, setSideOpen] = useState(false);
|
|
988
|
+
const [downloadOpen, setDownloadOpen] = useState(false);
|
|
989
|
+
const [columnsOpen, setColumnsOpen] = useState(false);
|
|
798
990
|
const [previewEnabled, setPreviewEnabled] = useState(false);
|
|
799
991
|
const [previewKey, setPreviewKey] = useState(null);
|
|
800
992
|
const [tooltip, setTooltip] = useState(null);
|
|
801
993
|
const [pageSize, setPageSize] = useState("15");
|
|
802
994
|
const [page, setPage] = useState(1);
|
|
995
|
+
const [actionsMenuKey, setActionsMenuKey] = useState(null);
|
|
996
|
+
const actionsMenuRef = useRef(null);
|
|
803
997
|
const isClient = useIsClient2();
|
|
998
|
+
useEffect(() => {
|
|
999
|
+
if (isControlled) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
setInternalVisibleKeys(readStoredColumnKeys(columnPrefsKey, fallbackKeys, allowedKeys));
|
|
1003
|
+
}, [allowedKeys, columnPrefsKey, fallbackKeys, isControlled]);
|
|
1004
|
+
const resolvedVisibleKeys = useMemo(() => {
|
|
1005
|
+
const source = isControlled ? visibleColumnKeys : internalVisibleKeys;
|
|
1006
|
+
const filtered2 = source.filter((key) => allowedKeys.has(key));
|
|
1007
|
+
return filtered2.length > 0 ? filtered2 : fallbackKeys.filter((key) => allowedKeys.has(key));
|
|
1008
|
+
}, [allowedKeys, fallbackKeys, internalVisibleKeys, isControlled, visibleColumnKeys]);
|
|
1009
|
+
const visibleColumns = useMemo(() => {
|
|
1010
|
+
const byKey = new Map(columns.map((column) => [column.key, column]));
|
|
1011
|
+
return resolvedVisibleKeys.map((key) => byKey.get(key)).filter((column) => Boolean(column));
|
|
1012
|
+
}, [columns, resolvedVisibleKeys]);
|
|
1013
|
+
function setVisibleKeys(next) {
|
|
1014
|
+
const unique = Array.from(new Set(next.filter((key) => allowedKeys.has(key))));
|
|
1015
|
+
const safe = unique.length > 0 ? unique : fallbackKeys.filter((key) => allowedKeys.has(key));
|
|
1016
|
+
if (!isControlled) {
|
|
1017
|
+
setInternalVisibleKeys(safe);
|
|
1018
|
+
writeStoredColumnKeys(columnPrefsKey, safe);
|
|
1019
|
+
}
|
|
1020
|
+
onVisibleColumnKeysChange?.(safe);
|
|
1021
|
+
}
|
|
1022
|
+
function toggleColumnVisible(key, checked) {
|
|
1023
|
+
if (checked) {
|
|
1024
|
+
if (resolvedVisibleKeys.includes(key)) {
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
setVisibleKeys([...resolvedVisibleKeys, key]);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (resolvedVisibleKeys.length <= 1) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
setVisibleKeys(resolvedVisibleKeys.filter((entry) => entry !== key));
|
|
1034
|
+
}
|
|
1035
|
+
function selectAllColumns() {
|
|
1036
|
+
setVisibleKeys(columns.map((column) => column.key));
|
|
1037
|
+
}
|
|
1038
|
+
function resetColumns() {
|
|
1039
|
+
setVisibleKeys(fallbackKeys);
|
|
1040
|
+
}
|
|
1041
|
+
const filterableColumns = visibleColumns.filter((column) => column.filterable !== false);
|
|
804
1042
|
const filtered = useMemo(() => {
|
|
805
|
-
const activeFilters = columns.filter((column) => column.filterable !== false);
|
|
806
1043
|
return items.filter(
|
|
807
|
-
(item) =>
|
|
1044
|
+
(item) => filterableColumns.every((column) => {
|
|
808
1045
|
const query = (filters[column.key] ?? "").trim();
|
|
809
1046
|
if (!query) {
|
|
810
1047
|
return true;
|
|
@@ -817,22 +1054,23 @@ function RecordWorkspace({
|
|
|
817
1054
|
return text.toLowerCase().includes(query.toLowerCase());
|
|
818
1055
|
})
|
|
819
1056
|
);
|
|
820
|
-
}, [items, filters,
|
|
1057
|
+
}, [items, filters, filterableColumns]);
|
|
821
1058
|
const sorted = useMemo(() => {
|
|
822
1059
|
if (!sort) {
|
|
823
1060
|
return filtered;
|
|
824
1061
|
}
|
|
825
|
-
const column =
|
|
1062
|
+
const column = visibleColumns.find((entry) => entry.key === sort.key);
|
|
826
1063
|
if (!column || column.sortable === false) {
|
|
827
1064
|
return filtered;
|
|
828
1065
|
}
|
|
829
1066
|
const direction = sort.direction === "asc" ? 1 : -1;
|
|
830
1067
|
return [...filtered].sort((left, right) => compareColumnValues(column, left, right) * direction);
|
|
831
|
-
}, [filtered, sort,
|
|
1068
|
+
}, [filtered, sort, visibleColumns]);
|
|
832
1069
|
const activeFilterCount = useMemo(
|
|
833
1070
|
() => filterableColumns.reduce((count, column) => count + ((filters[column.key] ?? "").trim() ? 1 : 0), 0),
|
|
834
1071
|
[filterableColumns, filters]
|
|
835
1072
|
);
|
|
1073
|
+
const hiddenColumnCount = columns.length - visibleColumns.length;
|
|
836
1074
|
const showAll = pageSize === "all";
|
|
837
1075
|
const numericPageSize = showAll ? Math.max(sorted.length, 1) : Number(pageSize);
|
|
838
1076
|
const pageCount = showAll ? 1 : Math.max(1, Math.ceil(sorted.length / numericPageSize));
|
|
@@ -840,19 +1078,44 @@ function RecordWorkspace({
|
|
|
840
1078
|
const start = showAll ? 0 : (currentPage - 1) * numericPageSize;
|
|
841
1079
|
const pageItems = showAll ? sorted : sorted.slice(start, start + numericPageSize);
|
|
842
1080
|
const showAdd = canAdd && Boolean(onAdd);
|
|
1081
|
+
const showDownload = Boolean(onDownload) && canDownload;
|
|
843
1082
|
const showFilters = filterableColumns.length > 0;
|
|
1083
|
+
const showColumns = showColumnPicker && columns.length > 0;
|
|
844
1084
|
const previewItem = previewKey === null ? null : sorted.find((item) => rowKey(item) === previewKey) ?? null;
|
|
1085
|
+
const exportColumns = visibleColumns.map((column) => ({
|
|
1086
|
+
key: column.exportKey ?? column.key,
|
|
1087
|
+
header: column.header
|
|
1088
|
+
}));
|
|
1089
|
+
useEffect(() => {
|
|
1090
|
+
if (actionsMenuKey === null) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
function onPointerDown(event) {
|
|
1094
|
+
if (actionsMenuRef.current && !actionsMenuRef.current.contains(event.target)) {
|
|
1095
|
+
setActionsMenuKey(null);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
document.addEventListener("mousedown", onPointerDown);
|
|
1099
|
+
return () => document.removeEventListener("mousedown", onPointerDown);
|
|
1100
|
+
}, [actionsMenuKey]);
|
|
1101
|
+
function requestDownload(format) {
|
|
1102
|
+
if (!onDownload || downloadBusy) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
onDownload(format, exportColumns);
|
|
1106
|
+
setDownloadOpen(false);
|
|
1107
|
+
}
|
|
845
1108
|
function cellText(column, item) {
|
|
846
1109
|
const raw = column.display ? column.display(item) : column.value(item);
|
|
847
1110
|
return raw === null || raw === void 0 || raw === "" ? "\u2014" : String(raw);
|
|
848
1111
|
}
|
|
849
1112
|
function detailLines(item) {
|
|
850
|
-
return
|
|
1113
|
+
return visibleColumns.map((column) => ({
|
|
851
1114
|
label: column.header,
|
|
852
1115
|
value: cellText(column, item)
|
|
853
1116
|
}));
|
|
854
1117
|
}
|
|
855
|
-
const previewTitle = previewItem &&
|
|
1118
|
+
const previewTitle = previewItem && visibleColumns[0] ? cellText(visibleColumns[0], previewItem) : null;
|
|
856
1119
|
function changeFilter(key, value) {
|
|
857
1120
|
setFilters((current) => ({ ...current, [key]: value }));
|
|
858
1121
|
setPage(1);
|
|
@@ -896,6 +1159,99 @@ function RecordWorkspace({
|
|
|
896
1159
|
setTooltip({ x, y, lines: detailLines(item) });
|
|
897
1160
|
}
|
|
898
1161
|
const summary = sorted.length === 0 ? "0 records" : showAll ? `1\u2013${sorted.length} of ${sorted.length}` : `${start + 1}\u2013${Math.min(start + numericPageSize, sorted.length)} of ${sorted.length}`;
|
|
1162
|
+
function renderRowActions(item) {
|
|
1163
|
+
const key = rowKey(item);
|
|
1164
|
+
const updateAllowed = canUpdate ? canUpdate(item) : Boolean(onUpdate);
|
|
1165
|
+
const deleteAllowed = canDelete ? canDelete(item) : Boolean(onDelete);
|
|
1166
|
+
const hasActions = Boolean(onView || onUpdate || onDelete);
|
|
1167
|
+
if (!hasActions) {
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1171
|
+
/* @__PURE__ */ jsxs("div", { className: "row-actions row-actions-desktop", children: [
|
|
1172
|
+
onView ? /* @__PURE__ */ jsx("button", { type: "button", className: "ghost row-action", onClick: () => onView(item), children: "View" }) : null,
|
|
1173
|
+
onUpdate ? /* @__PURE__ */ jsx(
|
|
1174
|
+
"button",
|
|
1175
|
+
{
|
|
1176
|
+
type: "button",
|
|
1177
|
+
className: "ghost row-action",
|
|
1178
|
+
disabled: !updateAllowed,
|
|
1179
|
+
title: updateAllowed ? "Update" : "Update is not available",
|
|
1180
|
+
onClick: () => onUpdate(item),
|
|
1181
|
+
children: "Update"
|
|
1182
|
+
}
|
|
1183
|
+
) : null,
|
|
1184
|
+
onDelete ? /* @__PURE__ */ jsx(
|
|
1185
|
+
"button",
|
|
1186
|
+
{
|
|
1187
|
+
type: "button",
|
|
1188
|
+
className: "ghost row-action danger",
|
|
1189
|
+
disabled: !deleteAllowed,
|
|
1190
|
+
title: deleteAllowed ? "Delete" : "Delete is not available",
|
|
1191
|
+
onClick: () => onDelete(item),
|
|
1192
|
+
children: "Delete"
|
|
1193
|
+
}
|
|
1194
|
+
) : null
|
|
1195
|
+
] }),
|
|
1196
|
+
/* @__PURE__ */ jsxs("div", { className: "row-actions row-actions-mobile", ref: actionsMenuKey === key ? actionsMenuRef : void 0, children: [
|
|
1197
|
+
/* @__PURE__ */ jsx(
|
|
1198
|
+
"button",
|
|
1199
|
+
{
|
|
1200
|
+
type: "button",
|
|
1201
|
+
className: "ghost row-action-menu-trigger",
|
|
1202
|
+
"aria-haspopup": "menu",
|
|
1203
|
+
"aria-expanded": actionsMenuKey === key,
|
|
1204
|
+
"aria-label": "Row actions",
|
|
1205
|
+
onClick: () => setActionsMenuKey((current) => current === key ? null : key),
|
|
1206
|
+
children: /* @__PURE__ */ jsx(MoreIcon, {})
|
|
1207
|
+
}
|
|
1208
|
+
),
|
|
1209
|
+
actionsMenuKey === key ? /* @__PURE__ */ jsxs("div", { className: "row-action-menu", role: "menu", children: [
|
|
1210
|
+
onView ? /* @__PURE__ */ jsx(
|
|
1211
|
+
"button",
|
|
1212
|
+
{
|
|
1213
|
+
type: "button",
|
|
1214
|
+
role: "menuitem",
|
|
1215
|
+
className: "ghost",
|
|
1216
|
+
onClick: () => {
|
|
1217
|
+
setActionsMenuKey(null);
|
|
1218
|
+
onView(item);
|
|
1219
|
+
},
|
|
1220
|
+
children: "View"
|
|
1221
|
+
}
|
|
1222
|
+
) : null,
|
|
1223
|
+
onUpdate ? /* @__PURE__ */ jsx(
|
|
1224
|
+
"button",
|
|
1225
|
+
{
|
|
1226
|
+
type: "button",
|
|
1227
|
+
role: "menuitem",
|
|
1228
|
+
className: "ghost",
|
|
1229
|
+
disabled: !updateAllowed,
|
|
1230
|
+
onClick: () => {
|
|
1231
|
+
setActionsMenuKey(null);
|
|
1232
|
+
onUpdate(item);
|
|
1233
|
+
},
|
|
1234
|
+
children: "Update"
|
|
1235
|
+
}
|
|
1236
|
+
) : null,
|
|
1237
|
+
onDelete ? /* @__PURE__ */ jsx(
|
|
1238
|
+
"button",
|
|
1239
|
+
{
|
|
1240
|
+
type: "button",
|
|
1241
|
+
role: "menuitem",
|
|
1242
|
+
className: "ghost danger",
|
|
1243
|
+
disabled: !deleteAllowed,
|
|
1244
|
+
onClick: () => {
|
|
1245
|
+
setActionsMenuKey(null);
|
|
1246
|
+
onDelete(item);
|
|
1247
|
+
},
|
|
1248
|
+
children: "Delete"
|
|
1249
|
+
}
|
|
1250
|
+
) : null
|
|
1251
|
+
] }) : null
|
|
1252
|
+
] })
|
|
1253
|
+
] });
|
|
1254
|
+
}
|
|
899
1255
|
return /* @__PURE__ */ jsxs("div", { className: "list-workspace", "aria-label": title, children: [
|
|
900
1256
|
/* @__PURE__ */ jsxs(
|
|
901
1257
|
"div",
|
|
@@ -906,7 +1262,7 @@ function RecordWorkspace({
|
|
|
906
1262
|
toolbar ? /* @__PURE__ */ jsx("div", { className: "table-card-bar", children: /* @__PURE__ */ jsx("div", { className: "table-card-context", children: toolbar }) }) : null,
|
|
907
1263
|
/* @__PURE__ */ jsx("div", { className: "table-wrap", children: /* @__PURE__ */ jsxs("table", { className: "data-table", children: [
|
|
908
1264
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
909
|
-
|
|
1265
|
+
visibleColumns.map((column) => {
|
|
910
1266
|
const sortable = column.sortable !== false;
|
|
911
1267
|
const active = sort?.key === column.key ? sort.direction : null;
|
|
912
1268
|
return /* @__PURE__ */ jsx(
|
|
@@ -931,10 +1287,8 @@ function RecordWorkspace({
|
|
|
931
1287
|
}),
|
|
932
1288
|
/* @__PURE__ */ jsx("th", { className: "actions-col", children: "Actions" })
|
|
933
1289
|
] }) }),
|
|
934
|
-
/* @__PURE__ */ jsx("tbody", { children: pageItems.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan:
|
|
1290
|
+
/* @__PURE__ */ jsx("tbody", { children: pageItems.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: visibleColumns.length + 1, children: /* @__PURE__ */ jsx("p", { className: "hint table-empty", children: items.length === 0 ? empty : "No records match the current filters." }) }) }) : pageItems.map((item) => {
|
|
935
1291
|
const key = rowKey(item);
|
|
936
|
-
const updateAllowed = canUpdate ? canUpdate(item) : Boolean(onUpdate);
|
|
937
|
-
const deleteAllowed = canDelete ? canDelete(item) : Boolean(onDelete);
|
|
938
1292
|
const selected = previewEnabled && previewKey === key;
|
|
939
1293
|
return /* @__PURE__ */ jsxs(
|
|
940
1294
|
"tr",
|
|
@@ -954,32 +1308,8 @@ function RecordWorkspace({
|
|
|
954
1308
|
onMouseLeave: () => setTooltip(null),
|
|
955
1309
|
onClick: () => focusRecord(item),
|
|
956
1310
|
children: [
|
|
957
|
-
|
|
958
|
-
/* @__PURE__ */ jsx("td", { className: "actions-col", onClick: (event) => event.stopPropagation(), children:
|
|
959
|
-
onView ? /* @__PURE__ */ jsx("button", { type: "button", className: "ghost row-action", onClick: () => onView(item), children: "View" }) : null,
|
|
960
|
-
onUpdate ? /* @__PURE__ */ jsx(
|
|
961
|
-
"button",
|
|
962
|
-
{
|
|
963
|
-
type: "button",
|
|
964
|
-
className: "ghost row-action",
|
|
965
|
-
disabled: !updateAllowed,
|
|
966
|
-
title: updateAllowed ? "Update" : "Update is not available",
|
|
967
|
-
onClick: () => onUpdate(item),
|
|
968
|
-
children: "Update"
|
|
969
|
-
}
|
|
970
|
-
) : null,
|
|
971
|
-
onDelete ? /* @__PURE__ */ jsx(
|
|
972
|
-
"button",
|
|
973
|
-
{
|
|
974
|
-
type: "button",
|
|
975
|
-
className: "ghost row-action danger",
|
|
976
|
-
disabled: !deleteAllowed,
|
|
977
|
-
title: deleteAllowed ? "Delete" : "Delete is not available",
|
|
978
|
-
onClick: () => onDelete(item),
|
|
979
|
-
children: "Delete"
|
|
980
|
-
}
|
|
981
|
-
) : null
|
|
982
|
-
] }) })
|
|
1311
|
+
visibleColumns.map((column) => /* @__PURE__ */ jsx("td", { children: cellText(column, item) }, column.key)),
|
|
1312
|
+
/* @__PURE__ */ jsx("td", { className: "actions-col", onClick: (event) => event.stopPropagation(), children: renderRowActions(item) })
|
|
983
1313
|
]
|
|
984
1314
|
},
|
|
985
1315
|
key
|
|
@@ -1041,6 +1371,73 @@ function RecordWorkspace({
|
|
|
1041
1371
|
/* @__PURE__ */ jsx(PlusIcon, {}),
|
|
1042
1372
|
/* @__PURE__ */ jsx("span", { children: addLabel })
|
|
1043
1373
|
] }) : null,
|
|
1374
|
+
showColumns ? /* @__PURE__ */ jsxs("div", { className: "side-rail-columns", children: [
|
|
1375
|
+
/* @__PURE__ */ jsxs(
|
|
1376
|
+
"button",
|
|
1377
|
+
{
|
|
1378
|
+
type: "button",
|
|
1379
|
+
className: "ghost side-rail-columns-toggle",
|
|
1380
|
+
"aria-expanded": columnsOpen,
|
|
1381
|
+
onClick: () => setColumnsOpen((open) => !open),
|
|
1382
|
+
children: [
|
|
1383
|
+
/* @__PURE__ */ jsx(ColumnsIcon, {}),
|
|
1384
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1385
|
+
"Columns",
|
|
1386
|
+
hiddenColumnCount > 0 ? ` (${visibleColumns.length}/${columns.length})` : ""
|
|
1387
|
+
] })
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
),
|
|
1391
|
+
columnsOpen ? /* @__PURE__ */ jsxs("div", { className: "side-rail-columns-panel", role: "group", "aria-label": "Visible columns", children: [
|
|
1392
|
+
/* @__PURE__ */ jsxs("div", { className: "side-rail-columns-actions", children: [
|
|
1393
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ghost", onClick: selectAllColumns, children: "Select all" }),
|
|
1394
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ghost", onClick: resetColumns, children: "Reset" })
|
|
1395
|
+
] }),
|
|
1396
|
+
/* @__PURE__ */ jsx("div", { className: "side-rail-columns-list", children: columns.map((column) => {
|
|
1397
|
+
const checked = resolvedVisibleKeys.includes(column.key);
|
|
1398
|
+
const disableUncheck = checked && resolvedVisibleKeys.length <= 1;
|
|
1399
|
+
return /* @__PURE__ */ jsxs("label", { className: "side-rail-column-option", children: [
|
|
1400
|
+
/* @__PURE__ */ jsx(
|
|
1401
|
+
"input",
|
|
1402
|
+
{
|
|
1403
|
+
type: "checkbox",
|
|
1404
|
+
checked,
|
|
1405
|
+
disabled: disableUncheck,
|
|
1406
|
+
onChange: (event) => toggleColumnVisible(column.key, event.target.checked)
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ jsx("span", { children: column.header })
|
|
1410
|
+
] }, column.key);
|
|
1411
|
+
}) })
|
|
1412
|
+
] }) : null
|
|
1413
|
+
] }) : null,
|
|
1414
|
+
showDownload ? /* @__PURE__ */ jsxs("div", { className: "side-rail-download", children: [
|
|
1415
|
+
/* @__PURE__ */ jsxs(
|
|
1416
|
+
"button",
|
|
1417
|
+
{
|
|
1418
|
+
type: "button",
|
|
1419
|
+
className: "ghost side-rail-download-toggle",
|
|
1420
|
+
"aria-expanded": downloadOpen,
|
|
1421
|
+
disabled: downloadBusy,
|
|
1422
|
+
onClick: () => setDownloadOpen((open) => !open),
|
|
1423
|
+
children: [
|
|
1424
|
+
/* @__PURE__ */ jsx(DownloadIcon, {}),
|
|
1425
|
+
/* @__PURE__ */ jsx("span", { children: downloadBusy ? "Downloading\u2026" : "Download" })
|
|
1426
|
+
]
|
|
1427
|
+
}
|
|
1428
|
+
),
|
|
1429
|
+
downloadOpen ? /* @__PURE__ */ jsx("div", { className: "side-rail-download-formats", role: "group", "aria-label": "Download format", children: EXPORT_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx(
|
|
1430
|
+
"button",
|
|
1431
|
+
{
|
|
1432
|
+
type: "button",
|
|
1433
|
+
className: "ghost side-rail-download-format",
|
|
1434
|
+
disabled: downloadBusy,
|
|
1435
|
+
onClick: () => requestDownload(option.format),
|
|
1436
|
+
children: option.label
|
|
1437
|
+
},
|
|
1438
|
+
option.format
|
|
1439
|
+
)) }) : null
|
|
1440
|
+
] }) : null,
|
|
1044
1441
|
showFilters ? /* @__PURE__ */ jsxs("div", { className: "side-rail-filters", children: [
|
|
1045
1442
|
/* @__PURE__ */ jsxs("div", { className: "filter-panel-head", children: [
|
|
1046
1443
|
/* @__PURE__ */ jsx("h3", { children: "Filters" }),
|
|
@@ -1094,6 +1491,39 @@ function RecordWorkspace({
|
|
|
1094
1491
|
/* @__PURE__ */ jsx(PreviewIcon, {})
|
|
1095
1492
|
] }),
|
|
1096
1493
|
showAdd ? /* @__PURE__ */ jsx("button", { type: "button", className: "ghost side-rail-icon", title: addLabel, "aria-label": addLabel, onClick: onAdd, children: /* @__PURE__ */ jsx(PlusIcon, {}) }) : null,
|
|
1494
|
+
showColumns ? /* @__PURE__ */ jsxs(
|
|
1495
|
+
"button",
|
|
1496
|
+
{
|
|
1497
|
+
type: "button",
|
|
1498
|
+
className: "ghost side-rail-icon",
|
|
1499
|
+
title: "Columns",
|
|
1500
|
+
"aria-label": "Columns",
|
|
1501
|
+
onClick: () => {
|
|
1502
|
+
setSideOpen(true);
|
|
1503
|
+
setColumnsOpen(true);
|
|
1504
|
+
},
|
|
1505
|
+
children: [
|
|
1506
|
+
/* @__PURE__ */ jsx(ColumnsIcon, {}),
|
|
1507
|
+
hiddenColumnCount > 0 ? /* @__PURE__ */ jsx("span", { className: "filter-badge", children: hiddenColumnCount }) : null
|
|
1508
|
+
]
|
|
1509
|
+
}
|
|
1510
|
+
) : null,
|
|
1511
|
+
showDownload ? /* @__PURE__ */ jsx(
|
|
1512
|
+
"button",
|
|
1513
|
+
{
|
|
1514
|
+
type: "button",
|
|
1515
|
+
className: "ghost side-rail-icon",
|
|
1516
|
+
title: "Download",
|
|
1517
|
+
"aria-label": "Download",
|
|
1518
|
+
"aria-expanded": downloadOpen,
|
|
1519
|
+
disabled: downloadBusy,
|
|
1520
|
+
onClick: () => {
|
|
1521
|
+
setSideOpen(true);
|
|
1522
|
+
setDownloadOpen(true);
|
|
1523
|
+
},
|
|
1524
|
+
children: /* @__PURE__ */ jsx(DownloadIcon, {})
|
|
1525
|
+
}
|
|
1526
|
+
) : null,
|
|
1097
1527
|
showFilters ? /* @__PURE__ */ jsxs(
|
|
1098
1528
|
"button",
|
|
1099
1529
|
{
|