@geomak/ui 1.7.1 → 1.7.3

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
@@ -1,7 +1,7 @@
1
1
  import { colors_default } from './chunk-GKXP6OJJ.js';
2
2
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-GKXP6OJJ.js';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import React10, { createContext, useState, useEffect, useMemo, useContext, useRef, useCallback, useId } from 'react';
4
+ import React9, { createContext, useState, useEffect, useMemo, useContext, useRef, useCallback, useId } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import * as Dialog from '@radix-ui/react-dialog';
7
7
  import { useReducedMotion, AnimatePresence, motion } from 'framer-motion';
@@ -940,39 +940,61 @@ function useNotification() {
940
940
  danger: (props) => open({ type: "danger", ...props })
941
941
  };
942
942
  }
943
- function LoadingSpinner({ prompt }) {
944
- const letterRefs = useMemo(() => [], []);
945
- const letters = prompt.split("");
946
- useEffect(() => {
947
- const timeouts = [];
948
- if (letterRefs.length === letters.length) {
949
- letterRefs.forEach((ref, index) => {
950
- const t = setTimeout(() => {
951
- ref?.classList.add("slowly-appear");
952
- }, index * 100);
953
- timeouts.push(t);
954
- });
943
+ var containerVariants = {
944
+ hidden: {},
945
+ visible: { transition: { staggerChildren: 0.06 } }
946
+ };
947
+ var letterVariants = {
948
+ hidden: { opacity: 0, y: 6 },
949
+ visible: { opacity: 1, y: 0, transition: { duration: 0.25, ease: "easeOut" } }
950
+ };
951
+ function LoadingSpinner({
952
+ prompt,
953
+ spinnerColor,
954
+ textColor,
955
+ backdropOpacity = 0.92
956
+ }) {
957
+ const reduced = useReducedMotion();
958
+ const letters = Array.from(prompt);
959
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
960
+ "div",
961
+ {
962
+ role: "status",
963
+ "aria-live": "polite",
964
+ "aria-label": prompt,
965
+ className: "fixed inset-0 z-[8000000] flex flex-col items-center justify-center gap-6 bg-background",
966
+ style: { opacity: backdropOpacity },
967
+ children: [
968
+ /* @__PURE__ */ jsx(
969
+ "div",
970
+ {
971
+ className: "w-20 h-20 rounded-2xl border-[6px] border-transparent border-t-current border-r-current animate-spin",
972
+ style: { color: spinnerColor ?? "var(--color-accent)" },
973
+ "aria-hidden": "true"
974
+ }
975
+ ),
976
+ /* @__PURE__ */ jsx(
977
+ motion.div,
978
+ {
979
+ className: "text-3xl font-bold tracking-tight select-none",
980
+ style: { color: textColor ?? "var(--color-foreground)" },
981
+ variants: containerVariants,
982
+ initial: reduced ? "visible" : "hidden",
983
+ animate: "visible",
984
+ children: letters.map((letter, index) => /* @__PURE__ */ jsx(
985
+ motion.span,
986
+ {
987
+ className: "inline-block whitespace-pre",
988
+ variants: letterVariants,
989
+ children: letter
990
+ },
991
+ index
992
+ ))
993
+ }
994
+ )
995
+ ]
955
996
  }
956
- return () => timeouts.forEach(clearTimeout);
957
- }, [letterRefs, letters.length]);
958
- return (
959
- // Portaled so the full-screen overlay always covers the real viewport,
960
- // not whatever container the consumer renders LoadingSpinner inside.
961
- /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs("div", { className: "fixed top-0 bottom-0 right-0 left-0 bg-oxford-blue-700-opaque z-[8000000] flex flex-col gap-5 items-center justify-start pt-80", children: [
962
- /* @__PURE__ */ jsx("div", { className: "border-r-prussian-blue border-l-prussian-blue border-t-white border-b-white border-[10px] w-[80px] h-[80px] rounded-xl shapeshift" }),
963
- /* @__PURE__ */ jsx("div", { className: "text-prussian-blue dark:text-white text-3xl font-bold", children: letters.map((letter, index) => /* @__PURE__ */ jsx(
964
- "span",
965
- {
966
- className: "select-none",
967
- ref: (ref) => {
968
- letterRefs[index] = ref;
969
- },
970
- children: letter
971
- },
972
- index
973
- )) })
974
- ] }) })
975
- );
997
+ ) });
976
998
  }
977
999
  function FadingBase({
978
1000
  className = "",
@@ -1447,7 +1469,7 @@ function Wizard({ children, steps, storageKey = "po_wizard" }) {
1447
1469
  children
1448
1470
  ] });
1449
1471
  }
1450
- var SearchInput = React10.forwardRef(function SearchInput2({
1472
+ var SearchInput = React9.forwardRef(function SearchInput2({
1451
1473
  value,
1452
1474
  onChange,
1453
1475
  disabled,
@@ -1669,9 +1691,7 @@ var DEFAULT_PAGINATION = {
1669
1691
  pickerOptions: DEFAULT_PICKER
1670
1692
  };
1671
1693
  var DEFAULT_EXPAND = {
1672
- enabled: false,
1673
- expandIcon: /* @__PURE__ */ jsx(Fragment, {}),
1674
- expandComponent: () => /* @__PURE__ */ jsx(Fragment, {})
1694
+ enabled: false
1675
1695
  };
1676
1696
  function createDatasets(rows, perPage) {
1677
1697
  if (!perPage) return [rows.slice()];
@@ -1681,78 +1701,95 @@ function createDatasets(rows, perPage) {
1681
1701
  }
1682
1702
  return all;
1683
1703
  }
1684
- function TableHeader({ columns }) {
1685
- return /* @__PURE__ */ jsx("thead", { className: "bg-surface-raised min-h-[50px] border-b border-b-border flex items-center", children: /* @__PURE__ */ jsx("tr", { className: "flex w-full items-center justify-center", children: columns.map((col) => /* @__PURE__ */ jsx(
1686
- "th",
1687
- {
1688
- className: "text-center w-full text-[13px] text-foreground",
1689
- children: col.label
1690
- },
1691
- col.key
1692
- )) }) });
1704
+ var defaultGetRowKey = (_row, index) => index;
1705
+ var cellAlign = (align) => align === "left" ? "text-left" : align === "right" ? "text-right" : "text-center";
1706
+ function TableHeader({
1707
+ columns,
1708
+ hasExpand
1709
+ }) {
1710
+ return /* @__PURE__ */ jsx("thead", { className: "bg-surface-raised border-b border-border", children: /* @__PURE__ */ jsxs("tr", { children: [
1711
+ hasExpand && /* @__PURE__ */ jsx("th", { "aria-hidden": "true", className: "w-9" }),
1712
+ columns.map((col) => /* @__PURE__ */ jsx(
1713
+ "th",
1714
+ {
1715
+ scope: "col",
1716
+ className: `${cellAlign(col.align)} text-sm font-semibold text-foreground py-3 px-3`,
1717
+ style: col.width != null ? { width: col.width } : void 0,
1718
+ children: col.label
1719
+ },
1720
+ col.key
1721
+ ))
1722
+ ] }) });
1693
1723
  }
1724
+ var DefaultExpandIcon = /* @__PURE__ */ jsx(
1725
+ "svg",
1726
+ {
1727
+ xmlns: "http://www.w3.org/2000/svg",
1728
+ viewBox: "0 0 24 24",
1729
+ fill: "currentColor",
1730
+ className: "w-5 h-5 text-foreground-muted",
1731
+ "aria-hidden": "true",
1732
+ children: /* @__PURE__ */ jsx(
1733
+ "path",
1734
+ {
1735
+ fillRule: "evenodd",
1736
+ d: "M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zM12.75 9a.75.75 0 00-1.5 0v2.25H9a.75.75 0 000 1.5h2.25V15a.75.75 0 001.5 0v-2.25H15a.75.75 0 000-1.5h-2.25V9z",
1737
+ clipRule: "evenodd"
1738
+ }
1739
+ )
1740
+ }
1741
+ );
1694
1742
  function TableBody({
1695
1743
  columns,
1696
1744
  rows,
1697
- expandRow
1745
+ expandRow,
1746
+ getRowKey
1698
1747
  }) {
1699
- const [visibleRows, setVisibleRows] = useState({});
1748
+ const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
1700
1749
  const toggleRow = (rowKey) => {
1701
- setVisibleRows((prev) => ({
1702
- ...prev,
1703
- [rowKey]: { visible: !prev[rowKey]?.visible }
1704
- }));
1750
+ setExpanded((prev) => {
1751
+ const next = new Set(prev);
1752
+ if (next.has(rowKey)) next.delete(rowKey);
1753
+ else next.add(rowKey);
1754
+ return next;
1755
+ });
1705
1756
  };
1706
- useEffect(() => {
1707
- if (rows.length && Object.keys(visibleRows).length === 0) {
1708
- const initial = {};
1709
- rows.forEach((row) => {
1710
- initial[row.key] = { visible: false };
1711
- });
1712
- setVisibleRows(initial);
1713
- }
1714
- }, [rows]);
1715
- return /* @__PURE__ */ jsx("tbody", { className: "w-full", children: rows.map((row, i) => /* @__PURE__ */ jsxs(React10.Fragment, { children: [
1716
- /* @__PURE__ */ jsxs(
1717
- "tr",
1718
- {
1719
- className: `border-b border-b-border flex min-w-max hover:bg-surface-raised transition-all duration-150 ${i % 2 === 0 ? "bg-surface" : "bg-surface-raised"}`,
1720
- children: [
1721
- expandRow.enabled && /* @__PURE__ */ jsx("td", { className: "flex items-center", children: /* @__PURE__ */ jsx(
1722
- "span",
1723
- {
1724
- onClick: () => toggleRow(row.key),
1725
- className: `p-2 cursor-pointer origin-center transition-all duration-200 ${visibleRows[row.key]?.visible ? "rotate-180" : "rotate-0"}`,
1726
- children: expandRow.expandIcon ?? /* PlusCircle */
1727
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 text-foreground-muted", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zM12.75 9a.75.75 0 00-1.5 0v2.25H9a.75.75 0 000 1.5h2.25V15a.75.75 0 001.5 0v-2.25H15a.75.75 0 000-1.5h-2.25V9z", clipRule: "evenodd" }) })
1728
- }
1729
- ) }),
1730
- columns.map((col, index) => /* @__PURE__ */ jsx(
1731
- "td",
1732
- {
1733
- className: `text-center min-h-[40px] w-full flex items-center justify-center p-1 border-border ${index !== columns.length - 1 ? "border-r-2" : ""}`,
1734
- children: "component" in col && col.component ? col.component(row[col.keyBind], row) : row[col.keyBind]
1735
- },
1736
- index
1737
- ))
1738
- ]
1739
- }
1740
- ),
1741
- expandRow.enabled && /* @__PURE__ */ jsx(
1742
- "tr",
1743
- {
1744
- className: `overflow-hidden w-full transition-all duration-300 ${visibleRows[row.key]?.visible ? "max-h-[2000px]" : "max-h-0"}`,
1745
- children: /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "p-0 pb-1", children: /* @__PURE__ */ jsx(
1746
- "div",
1747
- {
1748
- className: `overflow-hidden w-full transition-[max-height] duration-300 ${visibleRows[row.key]?.visible ? "max-h-[2000px]" : "max-h-0"}`,
1749
- children: expandRow.expandComponent?.(row)
1750
- }
1751
- ) })
1752
- },
1753
- `extra-${i}`
1754
- )
1755
- ] }, row.key)) });
1757
+ const hasExpand = !!expandRow.enabled;
1758
+ const expandColCount = columns.length + (hasExpand ? 1 : 0);
1759
+ return /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
1760
+ const rowKey = getRowKey(row, i);
1761
+ const isExpanded = expanded.has(rowKey);
1762
+ return /* @__PURE__ */ jsxs(React9.Fragment, { children: [
1763
+ /* @__PURE__ */ jsxs(
1764
+ "tr",
1765
+ {
1766
+ className: `border-b border-border hover:bg-surface-raised transition-colors duration-150 ${i % 2 === 0 ? "bg-surface" : "bg-surface-raised"}`,
1767
+ children: [
1768
+ hasExpand && /* @__PURE__ */ jsx("td", { className: "p-0 align-middle w-9", children: /* @__PURE__ */ jsx(
1769
+ "button",
1770
+ {
1771
+ type: "button",
1772
+ onClick: () => toggleRow(rowKey),
1773
+ "aria-expanded": isExpanded,
1774
+ "aria-label": isExpanded ? "Collapse row" : "Expand row",
1775
+ className: `w-9 h-9 inline-flex items-center justify-center rounded-md hover:bg-surface/80 transition-transform duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${isExpanded ? "rotate-180" : ""}`,
1776
+ children: expandRow.expandIcon ?? DefaultExpandIcon
1777
+ }
1778
+ ) }),
1779
+ columns.map((col) => /* @__PURE__ */ jsx(
1780
+ "td",
1781
+ {
1782
+ className: `${cellAlign(col.align)} text-sm text-foreground py-2 px-3 align-middle`,
1783
+ children: col.component ? col.component(row[col.keyBind], row) : row[col.keyBind]
1784
+ },
1785
+ col.key
1786
+ ))
1787
+ ]
1788
+ }
1789
+ ),
1790
+ hasExpand && isExpanded && /* @__PURE__ */ jsx("tr", { className: "bg-surface", children: /* @__PURE__ */ jsx("td", { colSpan: expandColCount, className: "p-0 border-b border-border", children: /* @__PURE__ */ jsx("div", { className: "p-3", children: expandRow.expandComponent?.(row) }) }) })
1791
+ ] }, rowKey);
1792
+ }) });
1756
1793
  }
1757
1794
  function Pagination({
1758
1795
  activePage,
@@ -1775,29 +1812,27 @@ function Pagination({
1775
1812
  }
1776
1813
  }, [serverSide, options.perPage, picker]);
1777
1814
  const navBtn = (icon, disabled, onClick) => /* @__PURE__ */ jsx(IconButton, { disabled, onClick, icon });
1778
- const chevronRight = (color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
1779
- const doubleChevronRight = (color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
1780
- const disabledColor = "var(--color-foreground-muted)";
1781
- const enabledColor = "var(--color-foreground)";
1815
+ const chevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
1816
+ const doubleChevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
1782
1817
  return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center justify-end pt-2", children: [
1783
1818
  navBtn(
1784
- /* @__PURE__ */ jsx("span", { className: "rotate-180 inline-flex", children: doubleChevronRight(activePage === 0 ? disabledColor : enabledColor) }),
1819
+ /* @__PURE__ */ jsx("span", { className: "rotate-180 inline-flex", children: doubleChevronRight }),
1785
1820
  activePage === 0,
1786
1821
  () => onPageChange(0)
1787
1822
  ),
1788
1823
  navBtn(
1789
- /* @__PURE__ */ jsx("span", { className: "rotate-180 inline-flex", children: chevronRight(activePage === 0 ? disabledColor : enabledColor) }),
1824
+ /* @__PURE__ */ jsx("span", { className: "rotate-180 inline-flex", children: chevronRight }),
1790
1825
  activePage === 0,
1791
1826
  () => activePage > 0 && onPageChange(activePage - 1)
1792
1827
  ),
1793
1828
  /* @__PURE__ */ jsx("span", { className: "bg-surface-raised rounded-lg ml-2 mr-2 shadow-sm p-2 w-10 text-center select-none text-foreground", children: activePage + 1 }),
1794
1829
  navBtn(
1795
- chevronRight(activePage === maxPage ? disabledColor : enabledColor),
1830
+ chevronRight,
1796
1831
  activePage === maxPage,
1797
1832
  () => activePage < maxPage && onPageChange(activePage + 1)
1798
1833
  ),
1799
1834
  navBtn(
1800
- doubleChevronRight(activePage === maxPage ? disabledColor : enabledColor),
1835
+ doubleChevronRight,
1801
1836
  activePage === maxPage,
1802
1837
  () => onPageChange(maxPage)
1803
1838
  ),
@@ -1822,6 +1857,7 @@ function Pagination({
1822
1857
  function Table({
1823
1858
  columns = [],
1824
1859
  rows = [],
1860
+ getRowKey = defaultGetRowKey,
1825
1861
  pagination = DEFAULT_PAGINATION,
1826
1862
  expandRow = DEFAULT_EXPAND,
1827
1863
  hasSearch = true,
@@ -1834,8 +1870,20 @@ function Table({
1834
1870
  typeof pagination.perPage === "number" ? pagination.perPage : 15
1835
1871
  );
1836
1872
  const [activePage, setActivePage] = useState(0);
1837
- const [datasets, setDatasets] = useState([]);
1838
1873
  const isServerSide = !!(pagination.enabled && pagination.serverSide);
1874
+ const filteredRows = useMemo(() => {
1875
+ if (isServerSide || !searchTerm) return rows;
1876
+ const term = searchTerm.toLowerCase();
1877
+ return rows.filter(
1878
+ (row) => Object.values(row).some(
1879
+ (v) => v != null && String(v).toLowerCase().includes(term)
1880
+ )
1881
+ );
1882
+ }, [rows, searchTerm, isServerSide]);
1883
+ const datasets = useMemo(() => {
1884
+ if (isServerSide) return [rows];
1885
+ return createDatasets(filteredRows, pagination.enabled ? perPage : null);
1886
+ }, [filteredRows, perPage, pagination.enabled, isServerSide, rows]);
1839
1887
  const MAX_PAGE = useMemo(() => {
1840
1888
  if (isServerSide && typeof pagination.maxPage === "number") return Math.max(0, pagination.maxPage);
1841
1889
  if (isServerSide && typeof pagination.totalCount === "number")
@@ -1844,32 +1892,22 @@ function Table({
1844
1892
  }, [isServerSide, pagination.maxPage, pagination.totalCount, perPage, datasets.length]);
1845
1893
  const currentPageRows = useMemo(() => {
1846
1894
  if (isServerSide) return rows;
1847
- return datasets.length ? datasets[activePage] ?? [] : [];
1895
+ return datasets[activePage] ?? [];
1848
1896
  }, [isServerSide, rows, datasets, activePage]);
1849
1897
  useEffect(() => {
1850
- if (pagination.enabled && !isServerSide) setPerPage(pagination.perPage ?? 15);
1851
- }, [pagination, isServerSide]);
1898
+ if (pagination.enabled && !isServerSide && typeof pagination.perPage === "number") {
1899
+ setPerPage(pagination.perPage);
1900
+ }
1901
+ }, [pagination.enabled, pagination.perPage, isServerSide]);
1852
1902
  useEffect(() => {
1853
1903
  if (isServerSide && typeof pagination.perPage === "number") setPerPage(pagination.perPage);
1854
1904
  }, [isServerSide, pagination.perPage]);
1855
- useEffect(() => {
1856
- if (isServerSide) return;
1857
- setDatasets(createDatasets(rows, pagination.enabled ? perPage : null));
1858
- }, [rows, perPage, pagination, isServerSide]);
1859
1905
  useEffect(() => {
1860
1906
  if (isServerSide && typeof pagination.page === "number" && pagination.page >= 1)
1861
1907
  setActivePage(pagination.page - 1);
1862
1908
  }, [isServerSide, pagination.page]);
1863
1909
  const onSearchChange = (e) => {
1864
- const term = e.target.value;
1865
- setSearchTerm(term);
1866
- if (isServerSide) return;
1867
- const filtered = rows.filter(
1868
- (row) => Object.values(row).some(
1869
- (v) => !!v && String(v).toLowerCase().includes(term.toLowerCase())
1870
- )
1871
- );
1872
- setDatasets(createDatasets(filtered, pagination.enabled ? perPage : null));
1910
+ setSearchTerm(e.target.value);
1873
1911
  setActivePage(0);
1874
1912
  };
1875
1913
  const onPaginationChange = (perPageValue) => {
@@ -1908,9 +1946,17 @@ function Table({
1908
1946
  )
1909
1947
  ] }),
1910
1948
  /* @__PURE__ */ jsx("div", { children: header }),
1911
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
1912
- /* @__PURE__ */ jsx(TableHeader, { columns }),
1913
- /* @__PURE__ */ jsx(TableBody, { columns, rows: currentPageRows, expandRow })
1949
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse", children: [
1950
+ /* @__PURE__ */ jsx(TableHeader, { columns, hasExpand: !!expandRow.enabled }),
1951
+ /* @__PURE__ */ jsx(
1952
+ TableBody,
1953
+ {
1954
+ columns,
1955
+ rows: currentPageRows,
1956
+ expandRow,
1957
+ getRowKey
1958
+ }
1959
+ )
1914
1960
  ] }) }),
1915
1961
  /* @__PURE__ */ jsx("div", { children: footer })
1916
1962
  ] });
@@ -2293,7 +2339,7 @@ function ThemeProvider({
2293
2339
  className = "",
2294
2340
  style
2295
2341
  }) {
2296
- const id = React10.useId().replace(/:/g, "");
2342
+ const id = React9.useId().replace(/:/g, "");
2297
2343
  const scopeClass = `geo-th-${id}`;
2298
2344
  const divRef = useRef(null);
2299
2345
  useEffect(() => {