@mshuja288/aksuicore 0.3.0 → 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 +13 -0
- package/dist/aksui.css +327 -6
- package/dist/index.cjs +430 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -9
- package/dist/index.d.ts +49 -9
- package/dist/index.js +430 -67
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/patterns/AppShell.tsx +140 -17
- package/src/patterns/RecordWorkspace.tsx +347 -46
- package/src/patterns/index.ts +2 -1
- package/src/primitives/SideNav.tsx +3 -1
- package/src/primitives/Topbar.tsx +38 -8
- package/src/theme/patterns.css +164 -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(
|
|
@@ -731,6 +852,37 @@ var EXPORT_FORMAT_OPTIONS = [
|
|
|
731
852
|
{ format: "xlsx", label: "Excel" },
|
|
732
853
|
{ format: "json", label: "JSON" }
|
|
733
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
|
+
}
|
|
734
886
|
function compareColumnValues(column, left, right) {
|
|
735
887
|
const leftRaw = column.display ? column.display(left) : column.value(left);
|
|
736
888
|
const rightRaw = column.display ? column.display(right) : column.value(right);
|
|
@@ -764,6 +916,9 @@ function ResetIcon() {
|
|
|
764
916
|
/* @__PURE__ */ jsx("path", { d: "M4.5 4.2v4h4", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
765
917
|
] });
|
|
766
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
|
+
}
|
|
767
922
|
function ChevronIcon({ open }) {
|
|
768
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" }) });
|
|
769
924
|
}
|
|
@@ -780,6 +935,13 @@ function DownloadIcon() {
|
|
|
780
935
|
/* @__PURE__ */ jsx("path", { d: "M4 15.5h12", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })
|
|
781
936
|
] });
|
|
782
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
|
+
}
|
|
783
945
|
function DetailList({ lines }) {
|
|
784
946
|
return /* @__PURE__ */ jsx("dl", { className: "record-detail-list", children: lines.map((line) => /* @__PURE__ */ jsxs("div", { className: "record-detail-row", children: [
|
|
785
947
|
/* @__PURE__ */ jsx("dt", { children: line.label }),
|
|
@@ -803,24 +965,83 @@ function RecordWorkspace({
|
|
|
803
965
|
onDownload,
|
|
804
966
|
canDownload = true,
|
|
805
967
|
downloadBusy = false,
|
|
968
|
+
showColumnPicker = true,
|
|
969
|
+
columnPrefsKey,
|
|
970
|
+
visibleColumnKeys,
|
|
971
|
+
defaultVisibleColumnKeys,
|
|
972
|
+
onVisibleColumnKeysChange,
|
|
806
973
|
toolbar,
|
|
807
974
|
form
|
|
808
975
|
}) {
|
|
809
|
-
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
|
+
);
|
|
810
985
|
const [filters, setFilters] = useState({});
|
|
811
986
|
const [sort, setSort] = useState(null);
|
|
812
987
|
const [sideOpen, setSideOpen] = useState(false);
|
|
813
988
|
const [downloadOpen, setDownloadOpen] = useState(false);
|
|
989
|
+
const [columnsOpen, setColumnsOpen] = useState(false);
|
|
814
990
|
const [previewEnabled, setPreviewEnabled] = useState(false);
|
|
815
991
|
const [previewKey, setPreviewKey] = useState(null);
|
|
816
992
|
const [tooltip, setTooltip] = useState(null);
|
|
817
993
|
const [pageSize, setPageSize] = useState("15");
|
|
818
994
|
const [page, setPage] = useState(1);
|
|
995
|
+
const [actionsMenuKey, setActionsMenuKey] = useState(null);
|
|
996
|
+
const actionsMenuRef = useRef(null);
|
|
819
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);
|
|
820
1042
|
const filtered = useMemo(() => {
|
|
821
|
-
const activeFilters = columns.filter((column) => column.filterable !== false);
|
|
822
1043
|
return items.filter(
|
|
823
|
-
(item) =>
|
|
1044
|
+
(item) => filterableColumns.every((column) => {
|
|
824
1045
|
const query = (filters[column.key] ?? "").trim();
|
|
825
1046
|
if (!query) {
|
|
826
1047
|
return true;
|
|
@@ -833,22 +1054,23 @@ function RecordWorkspace({
|
|
|
833
1054
|
return text.toLowerCase().includes(query.toLowerCase());
|
|
834
1055
|
})
|
|
835
1056
|
);
|
|
836
|
-
}, [items, filters,
|
|
1057
|
+
}, [items, filters, filterableColumns]);
|
|
837
1058
|
const sorted = useMemo(() => {
|
|
838
1059
|
if (!sort) {
|
|
839
1060
|
return filtered;
|
|
840
1061
|
}
|
|
841
|
-
const column =
|
|
1062
|
+
const column = visibleColumns.find((entry) => entry.key === sort.key);
|
|
842
1063
|
if (!column || column.sortable === false) {
|
|
843
1064
|
return filtered;
|
|
844
1065
|
}
|
|
845
1066
|
const direction = sort.direction === "asc" ? 1 : -1;
|
|
846
1067
|
return [...filtered].sort((left, right) => compareColumnValues(column, left, right) * direction);
|
|
847
|
-
}, [filtered, sort,
|
|
1068
|
+
}, [filtered, sort, visibleColumns]);
|
|
848
1069
|
const activeFilterCount = useMemo(
|
|
849
1070
|
() => filterableColumns.reduce((count, column) => count + ((filters[column.key] ?? "").trim() ? 1 : 0), 0),
|
|
850
1071
|
[filterableColumns, filters]
|
|
851
1072
|
);
|
|
1073
|
+
const hiddenColumnCount = columns.length - visibleColumns.length;
|
|
852
1074
|
const showAll = pageSize === "all";
|
|
853
1075
|
const numericPageSize = showAll ? Math.max(sorted.length, 1) : Number(pageSize);
|
|
854
1076
|
const pageCount = showAll ? 1 : Math.max(1, Math.ceil(sorted.length / numericPageSize));
|
|
@@ -858,12 +1080,29 @@ function RecordWorkspace({
|
|
|
858
1080
|
const showAdd = canAdd && Boolean(onAdd);
|
|
859
1081
|
const showDownload = Boolean(onDownload) && canDownload;
|
|
860
1082
|
const showFilters = filterableColumns.length > 0;
|
|
1083
|
+
const showColumns = showColumnPicker && columns.length > 0;
|
|
861
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]);
|
|
862
1101
|
function requestDownload(format) {
|
|
863
1102
|
if (!onDownload || downloadBusy) {
|
|
864
1103
|
return;
|
|
865
1104
|
}
|
|
866
|
-
onDownload(format);
|
|
1105
|
+
onDownload(format, exportColumns);
|
|
867
1106
|
setDownloadOpen(false);
|
|
868
1107
|
}
|
|
869
1108
|
function cellText(column, item) {
|
|
@@ -871,12 +1110,12 @@ function RecordWorkspace({
|
|
|
871
1110
|
return raw === null || raw === void 0 || raw === "" ? "\u2014" : String(raw);
|
|
872
1111
|
}
|
|
873
1112
|
function detailLines(item) {
|
|
874
|
-
return
|
|
1113
|
+
return visibleColumns.map((column) => ({
|
|
875
1114
|
label: column.header,
|
|
876
1115
|
value: cellText(column, item)
|
|
877
1116
|
}));
|
|
878
1117
|
}
|
|
879
|
-
const previewTitle = previewItem &&
|
|
1118
|
+
const previewTitle = previewItem && visibleColumns[0] ? cellText(visibleColumns[0], previewItem) : null;
|
|
880
1119
|
function changeFilter(key, value) {
|
|
881
1120
|
setFilters((current) => ({ ...current, [key]: value }));
|
|
882
1121
|
setPage(1);
|
|
@@ -920,6 +1159,99 @@ function RecordWorkspace({
|
|
|
920
1159
|
setTooltip({ x, y, lines: detailLines(item) });
|
|
921
1160
|
}
|
|
922
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
|
+
}
|
|
923
1255
|
return /* @__PURE__ */ jsxs("div", { className: "list-workspace", "aria-label": title, children: [
|
|
924
1256
|
/* @__PURE__ */ jsxs(
|
|
925
1257
|
"div",
|
|
@@ -930,7 +1262,7 @@ function RecordWorkspace({
|
|
|
930
1262
|
toolbar ? /* @__PURE__ */ jsx("div", { className: "table-card-bar", children: /* @__PURE__ */ jsx("div", { className: "table-card-context", children: toolbar }) }) : null,
|
|
931
1263
|
/* @__PURE__ */ jsx("div", { className: "table-wrap", children: /* @__PURE__ */ jsxs("table", { className: "data-table", children: [
|
|
932
1264
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
933
|
-
|
|
1265
|
+
visibleColumns.map((column) => {
|
|
934
1266
|
const sortable = column.sortable !== false;
|
|
935
1267
|
const active = sort?.key === column.key ? sort.direction : null;
|
|
936
1268
|
return /* @__PURE__ */ jsx(
|
|
@@ -955,10 +1287,8 @@ function RecordWorkspace({
|
|
|
955
1287
|
}),
|
|
956
1288
|
/* @__PURE__ */ jsx("th", { className: "actions-col", children: "Actions" })
|
|
957
1289
|
] }) }),
|
|
958
|
-
/* @__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) => {
|
|
959
1291
|
const key = rowKey(item);
|
|
960
|
-
const updateAllowed = canUpdate ? canUpdate(item) : Boolean(onUpdate);
|
|
961
|
-
const deleteAllowed = canDelete ? canDelete(item) : Boolean(onDelete);
|
|
962
1292
|
const selected = previewEnabled && previewKey === key;
|
|
963
1293
|
return /* @__PURE__ */ jsxs(
|
|
964
1294
|
"tr",
|
|
@@ -978,32 +1308,8 @@ function RecordWorkspace({
|
|
|
978
1308
|
onMouseLeave: () => setTooltip(null),
|
|
979
1309
|
onClick: () => focusRecord(item),
|
|
980
1310
|
children: [
|
|
981
|
-
|
|
982
|
-
/* @__PURE__ */ jsx("td", { className: "actions-col", onClick: (event) => event.stopPropagation(), children:
|
|
983
|
-
onView ? /* @__PURE__ */ jsx("button", { type: "button", className: "ghost row-action", onClick: () => onView(item), children: "View" }) : null,
|
|
984
|
-
onUpdate ? /* @__PURE__ */ jsx(
|
|
985
|
-
"button",
|
|
986
|
-
{
|
|
987
|
-
type: "button",
|
|
988
|
-
className: "ghost row-action",
|
|
989
|
-
disabled: !updateAllowed,
|
|
990
|
-
title: updateAllowed ? "Update" : "Update is not available",
|
|
991
|
-
onClick: () => onUpdate(item),
|
|
992
|
-
children: "Update"
|
|
993
|
-
}
|
|
994
|
-
) : null,
|
|
995
|
-
onDelete ? /* @__PURE__ */ jsx(
|
|
996
|
-
"button",
|
|
997
|
-
{
|
|
998
|
-
type: "button",
|
|
999
|
-
className: "ghost row-action danger",
|
|
1000
|
-
disabled: !deleteAllowed,
|
|
1001
|
-
title: deleteAllowed ? "Delete" : "Delete is not available",
|
|
1002
|
-
onClick: () => onDelete(item),
|
|
1003
|
-
children: "Delete"
|
|
1004
|
-
}
|
|
1005
|
-
) : null
|
|
1006
|
-
] }) })
|
|
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) })
|
|
1007
1313
|
]
|
|
1008
1314
|
},
|
|
1009
1315
|
key
|
|
@@ -1065,6 +1371,46 @@ function RecordWorkspace({
|
|
|
1065
1371
|
/* @__PURE__ */ jsx(PlusIcon, {}),
|
|
1066
1372
|
/* @__PURE__ */ jsx("span", { children: addLabel })
|
|
1067
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,
|
|
1068
1414
|
showDownload ? /* @__PURE__ */ jsxs("div", { className: "side-rail-download", children: [
|
|
1069
1415
|
/* @__PURE__ */ jsxs(
|
|
1070
1416
|
"button",
|
|
@@ -1145,6 +1491,23 @@ function RecordWorkspace({
|
|
|
1145
1491
|
/* @__PURE__ */ jsx(PreviewIcon, {})
|
|
1146
1492
|
] }),
|
|
1147
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,
|
|
1148
1511
|
showDownload ? /* @__PURE__ */ jsx(
|
|
1149
1512
|
"button",
|
|
1150
1513
|
{
|