@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/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 [menuOpen, setMenuOpen] = useState(false);
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 (!menuOpen) {
536
+ if (!accountOpen) {
531
537
  return;
532
538
  }
533
539
  function onPointerDown(event) {
534
540
  if (menuRef.current && !menuRef.current.contains(event.target)) {
535
- setMenuOpen(false);
541
+ setAccountOpen(false);
536
542
  }
537
543
  }
538
544
  document.addEventListener("mousedown", onPointerDown);
539
545
  return () => document.removeEventListener("mousedown", onPointerDown);
540
- }, [menuOpen]);
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": menuOpen,
566
- onClick: () => setMenuOpen((open) => !open),
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
- menuOpen ? /* @__PURE__ */ jsxs("div", { className: "avatar-menu", role: "menu", children: [
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
- setMenuOpen(false);
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
- showBottomNav = true,
716
+ mobileNav = "drawer",
717
+ showBottomNav,
718
+ navDrawerOpen,
719
+ onNavDrawerOpenChange,
699
720
  topbarActions,
700
721
  children,
701
722
  screenClassName = ""
702
723
  }) {
703
- return /* @__PURE__ */ jsxs("div", { className: "aksui-root app-shell", children: [
704
- /* @__PURE__ */ jsx(Topbar, { title, logoSrc, logoAlt, username, onLogout, children: topbarActions }),
705
- /* @__PURE__ */ jsxs("div", { className: "app-body", children: [
706
- /* @__PURE__ */ jsx(SideNav, { items: navItems, activeId: activeNavId, onSelect: onNavSelect, label: navLabel }),
707
- /* @__PURE__ */ jsx("div", { className: ["screen", screenClassName].filter(Boolean).join(" "), children })
708
- ] }),
709
- showBottomNav ? /* @__PURE__ */ jsx("nav", { className: "bottom-nav", "aria-label": navLabel, children: navItems.map((item) => /* @__PURE__ */ jsx(
710
- "button",
711
- {
712
- type: "button",
713
- className: activeNavId === item.id ? "active" : "",
714
- onClick: () => onNavSelect(item.id),
715
- children: item.short ?? item.label
716
- },
717
- item.id
718
- )) }) : null
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 filterableColumns = columns.filter((column) => column.filterable !== false);
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) => activeFilters.every((column) => {
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, columns]);
1057
+ }, [items, filters, filterableColumns]);
837
1058
  const sorted = useMemo(() => {
838
1059
  if (!sort) {
839
1060
  return filtered;
840
1061
  }
841
- const column = columns.find((entry) => entry.key === sort.key);
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, columns]);
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 columns.map((column) => ({
1113
+ return visibleColumns.map((column) => ({
875
1114
  label: column.header,
876
1115
  value: cellText(column, item)
877
1116
  }));
878
1117
  }
879
- const previewTitle = previewItem && columns[0] ? cellText(columns[0], previewItem) : null;
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
- columns.map((column) => {
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: columns.length + 1, children: /* @__PURE__ */ jsx("p", { className: "hint table-empty", children: items.length === 0 ? empty : "No records match the current filters." }) }) }) : pageItems.map((item) => {
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
- columns.map((column) => /* @__PURE__ */ jsx("td", { children: cellText(column, item) }, column.key)),
982
- /* @__PURE__ */ jsx("td", { className: "actions-col", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsxs("div", { className: "row-actions", 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
  {