@geomak/ui 1.7.2 → 1.7.4

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.cjs CHANGED
@@ -1723,9 +1723,7 @@ var DEFAULT_PAGINATION = {
1723
1723
  pickerOptions: DEFAULT_PICKER
1724
1724
  };
1725
1725
  var DEFAULT_EXPAND = {
1726
- enabled: false,
1727
- expandIcon: /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {}),
1728
- expandComponent: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {})
1726
+ enabled: false
1729
1727
  };
1730
1728
  function createDatasets(rows, perPage) {
1731
1729
  if (!perPage) return [rows.slice()];
@@ -1735,78 +1733,95 @@ function createDatasets(rows, perPage) {
1735
1733
  }
1736
1734
  return all;
1737
1735
  }
1738
- function TableHeader({ columns }) {
1739
- return /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-surface-raised min-h-[50px] border-b border-b-border flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "flex w-full items-center justify-center", children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
1740
- "th",
1741
- {
1742
- className: "text-center w-full text-[13px] text-foreground",
1743
- children: col.label
1744
- },
1745
- col.key
1746
- )) }) });
1736
+ var defaultGetRowKey = (_row, index) => index;
1737
+ var cellAlign = (align) => align === "left" ? "text-left" : align === "right" ? "text-right" : "text-center";
1738
+ function TableHeader({
1739
+ columns,
1740
+ hasExpand
1741
+ }) {
1742
+ return /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-surface-raised border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1743
+ hasExpand && /* @__PURE__ */ jsxRuntime.jsx("th", { "aria-hidden": "true", className: "w-9" }),
1744
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
1745
+ "th",
1746
+ {
1747
+ scope: "col",
1748
+ className: `${cellAlign(col.align)} text-sm font-semibold text-foreground py-3 px-3`,
1749
+ style: col.width != null ? { width: col.width } : void 0,
1750
+ children: col.label
1751
+ },
1752
+ col.key
1753
+ ))
1754
+ ] }) });
1747
1755
  }
1756
+ var DefaultExpandIcon = /* @__PURE__ */ jsxRuntime.jsx(
1757
+ "svg",
1758
+ {
1759
+ xmlns: "http://www.w3.org/2000/svg",
1760
+ viewBox: "0 0 24 24",
1761
+ fill: "currentColor",
1762
+ className: "w-5 h-5 text-foreground-muted",
1763
+ "aria-hidden": "true",
1764
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1765
+ "path",
1766
+ {
1767
+ fillRule: "evenodd",
1768
+ 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",
1769
+ clipRule: "evenodd"
1770
+ }
1771
+ )
1772
+ }
1773
+ );
1748
1774
  function TableBody({
1749
1775
  columns,
1750
1776
  rows,
1751
- expandRow
1777
+ expandRow,
1778
+ getRowKey
1752
1779
  }) {
1753
- const [visibleRows, setVisibleRows] = React9.useState({});
1780
+ const [expanded, setExpanded] = React9.useState(() => /* @__PURE__ */ new Set());
1754
1781
  const toggleRow = (rowKey) => {
1755
- setVisibleRows((prev) => ({
1756
- ...prev,
1757
- [rowKey]: { visible: !prev[rowKey]?.visible }
1758
- }));
1782
+ setExpanded((prev) => {
1783
+ const next = new Set(prev);
1784
+ if (next.has(rowKey)) next.delete(rowKey);
1785
+ else next.add(rowKey);
1786
+ return next;
1787
+ });
1759
1788
  };
1760
- React9.useEffect(() => {
1761
- if (rows.length && Object.keys(visibleRows).length === 0) {
1762
- const initial = {};
1763
- rows.forEach((row) => {
1764
- initial[row.key] = { visible: false };
1765
- });
1766
- setVisibleRows(initial);
1767
- }
1768
- }, [rows]);
1769
- return /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "w-full", children: rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsxs(React9__default.default.Fragment, { children: [
1770
- /* @__PURE__ */ jsxRuntime.jsxs(
1771
- "tr",
1772
- {
1773
- 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"}`,
1774
- children: [
1775
- expandRow.enabled && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1776
- "span",
1777
- {
1778
- onClick: () => toggleRow(row.key),
1779
- className: `p-2 cursor-pointer origin-center transition-all duration-200 ${visibleRows[row.key]?.visible ? "rotate-180" : "rotate-0"}`,
1780
- children: expandRow.expandIcon ?? /* PlusCircle */
1781
- /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) })
1782
- }
1783
- ) }),
1784
- columns.map((col, index) => /* @__PURE__ */ jsxRuntime.jsx(
1785
- "td",
1786
- {
1787
- className: `text-center min-h-[40px] w-full flex items-center justify-center p-1 border-border ${index !== columns.length - 1 ? "border-r-2" : ""}`,
1788
- children: "component" in col && col.component ? col.component(row[col.keyBind], row) : row[col.keyBind]
1789
- },
1790
- index
1791
- ))
1792
- ]
1793
- }
1794
- ),
1795
- expandRow.enabled && /* @__PURE__ */ jsxRuntime.jsx(
1796
- "tr",
1797
- {
1798
- className: `overflow-hidden w-full transition-all duration-300 ${visibleRows[row.key]?.visible ? "max-h-[2000px]" : "max-h-0"}`,
1799
- children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: columns.length, className: "p-0 pb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1800
- "div",
1801
- {
1802
- className: `overflow-hidden w-full transition-[max-height] duration-300 ${visibleRows[row.key]?.visible ? "max-h-[2000px]" : "max-h-0"}`,
1803
- children: expandRow.expandComponent?.(row)
1804
- }
1805
- ) })
1806
- },
1807
- `extra-${i}`
1808
- )
1809
- ] }, row.key)) });
1789
+ const hasExpand = !!expandRow.enabled;
1790
+ const expandColCount = columns.length + (hasExpand ? 1 : 0);
1791
+ return /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((row, i) => {
1792
+ const rowKey = getRowKey(row, i);
1793
+ const isExpanded = expanded.has(rowKey);
1794
+ return /* @__PURE__ */ jsxRuntime.jsxs(React9__default.default.Fragment, { children: [
1795
+ /* @__PURE__ */ jsxRuntime.jsxs(
1796
+ "tr",
1797
+ {
1798
+ className: `border-b border-border hover:bg-surface-raised transition-colors duration-150 ${i % 2 === 0 ? "bg-surface" : "bg-surface-raised"}`,
1799
+ children: [
1800
+ hasExpand && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-0 align-middle w-9", children: /* @__PURE__ */ jsxRuntime.jsx(
1801
+ "button",
1802
+ {
1803
+ type: "button",
1804
+ onClick: () => toggleRow(rowKey),
1805
+ "aria-expanded": isExpanded,
1806
+ "aria-label": isExpanded ? "Collapse row" : "Expand row",
1807
+ 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" : ""}`,
1808
+ children: expandRow.expandIcon ?? DefaultExpandIcon
1809
+ }
1810
+ ) }),
1811
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
1812
+ "td",
1813
+ {
1814
+ className: `${cellAlign(col.align)} text-sm text-foreground py-2 px-3 align-middle`,
1815
+ children: col.component ? col.component(row[col.keyBind], row) : row[col.keyBind]
1816
+ },
1817
+ col.key
1818
+ ))
1819
+ ]
1820
+ }
1821
+ ),
1822
+ hasExpand && isExpanded && /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "bg-surface", children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: expandColCount, className: "p-0 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: expandRow.expandComponent?.(row) }) }) })
1823
+ ] }, rowKey);
1824
+ }) });
1810
1825
  }
1811
1826
  function Pagination({
1812
1827
  activePage,
@@ -1829,29 +1844,27 @@ function Pagination({
1829
1844
  }
1830
1845
  }, [serverSide, options.perPage, picker]);
1831
1846
  const navBtn = (icon, disabled, onClick) => /* @__PURE__ */ jsxRuntime.jsx(IconButton, { disabled, onClick, icon });
1832
- const chevronRight = (color) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
1833
- const doubleChevronRight = (color) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
1834
- const disabledColor = "var(--color-foreground-muted)";
1835
- const enabledColor = "var(--color-foreground)";
1847
+ const chevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
1848
+ const doubleChevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
1836
1849
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center justify-end pt-2", children: [
1837
1850
  navBtn(
1838
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: doubleChevronRight(activePage === 0 ? disabledColor : enabledColor) }),
1851
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: doubleChevronRight }),
1839
1852
  activePage === 0,
1840
1853
  () => onPageChange(0)
1841
1854
  ),
1842
1855
  navBtn(
1843
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: chevronRight(activePage === 0 ? disabledColor : enabledColor) }),
1856
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: chevronRight }),
1844
1857
  activePage === 0,
1845
1858
  () => activePage > 0 && onPageChange(activePage - 1)
1846
1859
  ),
1847
1860
  /* @__PURE__ */ jsxRuntime.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 }),
1848
1861
  navBtn(
1849
- chevronRight(activePage === maxPage ? disabledColor : enabledColor),
1862
+ chevronRight,
1850
1863
  activePage === maxPage,
1851
1864
  () => activePage < maxPage && onPageChange(activePage + 1)
1852
1865
  ),
1853
1866
  navBtn(
1854
- doubleChevronRight(activePage === maxPage ? disabledColor : enabledColor),
1867
+ doubleChevronRight,
1855
1868
  activePage === maxPage,
1856
1869
  () => onPageChange(maxPage)
1857
1870
  ),
@@ -1876,6 +1889,7 @@ function Pagination({
1876
1889
  function Table({
1877
1890
  columns = [],
1878
1891
  rows = [],
1892
+ getRowKey = defaultGetRowKey,
1879
1893
  pagination = DEFAULT_PAGINATION,
1880
1894
  expandRow = DEFAULT_EXPAND,
1881
1895
  hasSearch = true,
@@ -1888,8 +1902,20 @@ function Table({
1888
1902
  typeof pagination.perPage === "number" ? pagination.perPage : 15
1889
1903
  );
1890
1904
  const [activePage, setActivePage] = React9.useState(0);
1891
- const [datasets, setDatasets] = React9.useState([]);
1892
1905
  const isServerSide = !!(pagination.enabled && pagination.serverSide);
1906
+ const filteredRows = React9.useMemo(() => {
1907
+ if (isServerSide || !searchTerm) return rows;
1908
+ const term = searchTerm.toLowerCase();
1909
+ return rows.filter(
1910
+ (row) => Object.values(row).some(
1911
+ (v) => v != null && String(v).toLowerCase().includes(term)
1912
+ )
1913
+ );
1914
+ }, [rows, searchTerm, isServerSide]);
1915
+ const datasets = React9.useMemo(() => {
1916
+ if (isServerSide) return [rows];
1917
+ return createDatasets(filteredRows, pagination.enabled ? perPage : null);
1918
+ }, [filteredRows, perPage, pagination.enabled, isServerSide, rows]);
1893
1919
  const MAX_PAGE = React9.useMemo(() => {
1894
1920
  if (isServerSide && typeof pagination.maxPage === "number") return Math.max(0, pagination.maxPage);
1895
1921
  if (isServerSide && typeof pagination.totalCount === "number")
@@ -1898,32 +1924,22 @@ function Table({
1898
1924
  }, [isServerSide, pagination.maxPage, pagination.totalCount, perPage, datasets.length]);
1899
1925
  const currentPageRows = React9.useMemo(() => {
1900
1926
  if (isServerSide) return rows;
1901
- return datasets.length ? datasets[activePage] ?? [] : [];
1927
+ return datasets[activePage] ?? [];
1902
1928
  }, [isServerSide, rows, datasets, activePage]);
1903
1929
  React9.useEffect(() => {
1904
- if (pagination.enabled && !isServerSide) setPerPage(pagination.perPage ?? 15);
1905
- }, [pagination, isServerSide]);
1930
+ if (pagination.enabled && !isServerSide && typeof pagination.perPage === "number") {
1931
+ setPerPage(pagination.perPage);
1932
+ }
1933
+ }, [pagination.enabled, pagination.perPage, isServerSide]);
1906
1934
  React9.useEffect(() => {
1907
1935
  if (isServerSide && typeof pagination.perPage === "number") setPerPage(pagination.perPage);
1908
1936
  }, [isServerSide, pagination.perPage]);
1909
- React9.useEffect(() => {
1910
- if (isServerSide) return;
1911
- setDatasets(createDatasets(rows, pagination.enabled ? perPage : null));
1912
- }, [rows, perPage, pagination, isServerSide]);
1913
1937
  React9.useEffect(() => {
1914
1938
  if (isServerSide && typeof pagination.page === "number" && pagination.page >= 1)
1915
1939
  setActivePage(pagination.page - 1);
1916
1940
  }, [isServerSide, pagination.page]);
1917
1941
  const onSearchChange = (e) => {
1918
- const term = e.target.value;
1919
- setSearchTerm(term);
1920
- if (isServerSide) return;
1921
- const filtered = rows.filter(
1922
- (row) => Object.values(row).some(
1923
- (v) => !!v && String(v).toLowerCase().includes(term.toLowerCase())
1924
- )
1925
- );
1926
- setDatasets(createDatasets(filtered, pagination.enabled ? perPage : null));
1942
+ setSearchTerm(e.target.value);
1927
1943
  setActivePage(0);
1928
1944
  };
1929
1945
  const onPaginationChange = (perPageValue) => {
@@ -1962,9 +1978,17 @@ function Table({
1962
1978
  )
1963
1979
  ] }),
1964
1980
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: header }),
1965
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
1966
- /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { columns }),
1967
- /* @__PURE__ */ jsxRuntime.jsx(TableBody, { columns, rows: currentPageRows, expandRow })
1981
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse", children: [
1982
+ /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { columns, hasExpand: !!expandRow.enabled }),
1983
+ /* @__PURE__ */ jsxRuntime.jsx(
1984
+ TableBody,
1985
+ {
1986
+ columns,
1987
+ rows: currentPageRows,
1988
+ expandRow,
1989
+ getRowKey
1990
+ }
1991
+ )
1968
1992
  ] }) }),
1969
1993
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: footer })
1970
1994
  ] });
@@ -2336,8 +2360,24 @@ function toCssVars(theme) {
2336
2360
  }
2337
2361
  return out;
2338
2362
  }
2363
+ var CSS_VALUE_REJECT_RE = /[;{}<>\\]|\*\/|\/\*/;
2364
+ function isSafeCssValue(v) {
2365
+ if (typeof v !== "string") return false;
2366
+ if (v.length > 500) return false;
2367
+ return !CSS_VALUE_REJECT_RE.test(v);
2368
+ }
2339
2369
  function varsToStyleString(vars2) {
2340
- return Object.entries(vars2).map(([k, v]) => `${k}: ${v};`).join(" ");
2370
+ const out = [];
2371
+ for (const [k, v] of Object.entries(vars2)) {
2372
+ if (!isSafeCssValue(v)) {
2373
+ console.warn(
2374
+ `[ThemeProvider] Dropping unsafe value for "${k}". Theme values may contain letters, digits, and CSS punctuation but must not include: ; { } < > \\ /* */`
2375
+ );
2376
+ continue;
2377
+ }
2378
+ out.push(`${k}: ${v};`);
2379
+ }
2380
+ return out.join(" ");
2341
2381
  }
2342
2382
  function ThemeProvider({
2343
2383
  theme,