@universe-platform/ui 0.1.0-beta.10 → 0.1.0-beta.12

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.mjs CHANGED
@@ -159,7 +159,7 @@ var COLUMN_DEFAULT_INPUT_CONFIG = {
159
159
  NUMBER: { inputType: "number", placeholder: "\u05D4\u05D6\u05DF \u05DE\u05E1\u05E4\u05E8" },
160
160
  BOOLEAN: { inputType: "select" },
161
161
  DATE: { inputType: "date" },
162
- RELATION: { inputType: "text", placeholder: "\u05D4\u05D6\u05DF \u05E2\u05E8\u05DA \u05EA\u05E6\u05D5\u05D2\u05D4" },
162
+ RELATION: { inputType: "select", placeholder: "\u05D1\u05D7\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D4 \u05DB\u05D1\u05E8\u05D9\u05E8\u05EA \u05DE\u05D7\u05D3\u05DC" },
163
163
  GOOGLE_DRIVE_FILE: { inputType: "text" },
164
164
  CURRENCY: { inputType: "number", placeholder: "\u05D4\u05D6\u05DF \u05DE\u05E1\u05E4\u05E8" },
165
165
  TIME: { inputType: "time" },
@@ -170,7 +170,7 @@ var COLUMN_DEFAULT_INPUT_CONFIG = {
170
170
  };
171
171
 
172
172
  // src/components/TableBlock/TableBlockClient.tsx
173
- import React9, { useState as useState10, useMemo as useMemo5, useEffect as useEffect8, useCallback as useCallback9, useRef as useRef7 } from "react";
173
+ import React11, { useState as useState12, useMemo as useMemo6, useEffect as useEffect10, useCallback as useCallback10, useRef as useRef8 } from "react";
174
174
 
175
175
  // src/hooks/useToast.ts
176
176
  import { useContext as useContext2 } from "react";
@@ -1460,9 +1460,10 @@ function LinkPickerCell({ value, onChange, readOnly = false }) {
1460
1460
  }
1461
1461
 
1462
1462
  // src/components/TableBlock/fields/RelationPickerCell.tsx
1463
- import { useState as useState6, useRef as useRef5, useEffect as useEffect5, useCallback as useCallback5, useMemo as useMemo3 } from "react";
1464
- import { X as X6, Search, Check as Check3, ChevronDown } from "lucide-react";
1465
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1463
+ import React6, { useState as useState6, useRef as useRef5, useEffect as useEffect5, useCallback as useCallback5, useMemo as useMemo3 } from "react";
1464
+ import { createPortal } from "react-dom";
1465
+ import { X as X6, Search, Check as Check3, ChevronDown, Loader2 } from "lucide-react";
1466
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1466
1467
  function isRawObjectId(str) {
1467
1468
  if (typeof str !== "string") return false;
1468
1469
  const trimmed = str.trim();
@@ -1519,6 +1520,30 @@ function resolveRelationLabel(opt, column) {
1519
1520
  }
1520
1521
  return "";
1521
1522
  }
1523
+ var ITEM_HEIGHT = 35;
1524
+ var CONTAINER_HEIGHT = 200;
1525
+ var OVERSCAN = 5;
1526
+ var BATCH_SIZE = 50;
1527
+ var RelationPickerRow = React6.memo(function RelationPickerRow2({
1528
+ id,
1529
+ label,
1530
+ isSelected,
1531
+ onSelect,
1532
+ height
1533
+ }) {
1534
+ return /* @__PURE__ */ jsxs6(
1535
+ "div",
1536
+ {
1537
+ onClick: () => onSelect(id),
1538
+ style: { height: `${height}px` },
1539
+ className: `flex items-center justify-between px-2.5 rounded cursor-pointer text-xs transition-colors ${isSelected ? "bg-blue-50 text-blue-900 font-medium" : "hover:bg-slate-50 text-slate-700"}`,
1540
+ children: [
1541
+ /* @__PURE__ */ jsx6("span", { className: "truncate flex-1", children: label }),
1542
+ isSelected && /* @__PURE__ */ jsx6(Check3, { className: "w-3.5 h-3.5 text-blue-600 shrink-0 mr-1" })
1543
+ ]
1544
+ }
1545
+ );
1546
+ });
1522
1547
  function RelationPickerCell({
1523
1548
  value,
1524
1549
  onChange,
@@ -1527,42 +1552,147 @@ function RelationPickerCell({
1527
1552
  rowId,
1528
1553
  readOnly = false
1529
1554
  }) {
1530
- var _a;
1555
+ var _a, _b, _c;
1531
1556
  const [isOpen, setIsOpen] = useState6(false);
1532
1557
  const [searchQuery, setSearchQuery] = useState6("");
1558
+ const [isRemoteSearching, setIsRemoteSearching] = useState6(false);
1559
+ const [isLoadingMore, setIsLoadingMore] = useState6(false);
1560
+ const [hasMore, setHasMore] = useState6(true);
1561
+ const [items, setItems] = useState6([]);
1562
+ const [skip, setSkip] = useState6(0);
1563
+ const [scrollTop, setScrollTop] = useState6(0);
1533
1564
  const containerRef = useRef5(null);
1565
+ const dropdownRef = useRef5(null);
1534
1566
  const searchInputRef = useRef5(null);
1567
+ const listContainerRef = useRef5(null);
1568
+ const sentinelRef = useRef5(null);
1569
+ const [dropdownCoords, setDropdownCoords] = useState6(null);
1570
+ const updateDropdownCoords = useCallback5(() => {
1571
+ if (!containerRef.current) return;
1572
+ const rect = containerRef.current.getBoundingClientRect();
1573
+ const dropdownHeight = 280;
1574
+ const dropdownWidth = Math.max(220, Math.min(320, rect.width || 220));
1575
+ let right = window.innerWidth - rect.right;
1576
+ if (right + dropdownWidth > window.innerWidth - 10) {
1577
+ right = window.innerWidth - dropdownWidth - 10;
1578
+ }
1579
+ if (right < 10) {
1580
+ right = 10;
1581
+ }
1582
+ const spaceBelow = window.innerHeight - rect.bottom;
1583
+ let top = rect.bottom + 4;
1584
+ if (spaceBelow < dropdownHeight && rect.top > dropdownHeight) {
1585
+ top = Math.max(10, rect.top - dropdownHeight - 4);
1586
+ } else {
1587
+ top = Math.min(window.innerHeight - dropdownHeight - 10, top);
1588
+ }
1589
+ setDropdownCoords({ top, right, width: dropdownWidth });
1590
+ }, []);
1591
+ useEffect5(() => {
1592
+ if (!isOpen) return;
1593
+ updateDropdownCoords();
1594
+ const handleScrollOrResize = () => updateDropdownCoords();
1595
+ window.addEventListener("scroll", handleScrollOrResize, true);
1596
+ window.addEventListener("resize", handleScrollOrResize);
1597
+ return () => {
1598
+ window.removeEventListener("scroll", handleScrollOrResize, true);
1599
+ window.removeEventListener("resize", handleScrollOrResize);
1600
+ };
1601
+ }, [isOpen, updateDropdownCoords]);
1602
+ const stableLabelCacheRef = useRef5(/* @__PURE__ */ new Map());
1603
+ const skipRef = useRef5(0);
1604
+ const isLoadingMoreRef = useRef5(false);
1535
1605
  const allowMultiple = Boolean((_a = column.config) == null ? void 0 : _a.allowMultiple);
1606
+ const referencedTableId = (_b = column.config) == null ? void 0 : _b.referencedTableId;
1607
+ const displayField = (_c = column.config) == null ? void 0 : _c.displayField;
1536
1608
  const selectedIds = useMemo3(() => normalizeRelationValue(value), [value]);
1537
1609
  const isStandalone = !context || context.relationOptions === void 0;
1538
1610
  const optionsLoaded = isStandalone || context.relationOptions[column.name] !== void 0;
1539
- const rawOptions = useMemo3(() => {
1611
+ const baseOptions = useMemo3(() => {
1540
1612
  var _a2;
1541
1613
  return ((_a2 = context == null ? void 0 : context.relationOptions) == null ? void 0 : _a2[column.name]) || [];
1542
1614
  }, [context == null ? void 0 : context.relationOptions, column.name]);
1543
1615
  const processedOptions = useMemo3(() => {
1544
- return rawOptions.map((opt) => {
1545
- const label = resolveRelationLabel(opt, column);
1546
- return {
1547
- id: String(opt.id),
1548
- _displayLabel: label
1549
- };
1550
- }).filter((opt) => opt._displayLabel && opt._displayLabel.trim() !== "" && !isRawObjectId(opt._displayLabel));
1551
- }, [rawOptions, column]);
1616
+ return items.map((opt) => ({
1617
+ id: String(opt.id),
1618
+ _displayLabel: resolveRelationLabel(opt, column) || opt.label || ""
1619
+ })).filter((opt) => opt._displayLabel && opt._displayLabel.trim() !== "" && !isRawObjectId(opt._displayLabel));
1620
+ }, [items, column]);
1552
1621
  const getDisplayForId = useCallback5((id) => {
1553
- const found = processedOptions.find((o) => String(o.id) === String(id));
1554
- if (found && found._displayLabel && !isRawObjectId(found._displayLabel)) {
1555
- return found._displayLabel;
1622
+ var _a2, _b2, _c2;
1623
+ if (!id) return "-";
1624
+ const strId = String(id).trim();
1625
+ if (!strId) return "-";
1626
+ if (stableLabelCacheRef.current.has(strId)) {
1627
+ const cached = stableLabelCacheRef.current.get(strId);
1628
+ if (cached && cached !== "-" && !isRawObjectId(cached)) {
1629
+ return cached;
1630
+ }
1631
+ }
1632
+ const dictOpt = (_c2 = (_b2 = (_a2 = context == null ? void 0 : context.relationDictionaryRef) == null ? void 0 : _a2.current) == null ? void 0 : _b2[column.name]) == null ? void 0 : _c2.get(strId);
1633
+ if (dictOpt) {
1634
+ const lbl = resolveRelationLabel(dictOpt, column) || dictOpt.label || dictOpt.name;
1635
+ if (lbl && !isRawObjectId(String(lbl))) {
1636
+ stableLabelCacheRef.current.set(strId, String(lbl));
1637
+ return String(lbl);
1638
+ }
1639
+ }
1640
+ const foundInItems = items.find((o) => String(o.id) === strId);
1641
+ if (foundInItems) {
1642
+ const lbl = resolveRelationLabel(foundInItems, column) || foundInItems.label || foundInItems.name;
1643
+ if (lbl && !isRawObjectId(String(lbl))) {
1644
+ stableLabelCacheRef.current.set(strId, String(lbl));
1645
+ return String(lbl);
1646
+ }
1647
+ }
1648
+ const foundInBase = (baseOptions || []).find((o) => String(o.id) === strId);
1649
+ if (foundInBase) {
1650
+ const lbl = resolveRelationLabel(foundInBase, column) || foundInBase.label || foundInBase.name;
1651
+ if (lbl && !isRawObjectId(String(lbl))) {
1652
+ stableLabelCacheRef.current.set(strId, String(lbl));
1653
+ return String(lbl);
1654
+ }
1556
1655
  }
1557
1656
  return "-";
1558
- }, [processedOptions]);
1657
+ }, [items, baseOptions, column, context]);
1559
1658
  const handleOpen = (e) => {
1560
1659
  if (readOnly) return;
1561
1660
  e == null ? void 0 : e.stopPropagation();
1562
- setIsOpen((prev) => !prev);
1661
+ const nextOpen = !isOpen;
1662
+ if (nextOpen) {
1663
+ updateDropdownCoords();
1664
+ }
1665
+ setIsOpen(nextOpen);
1666
+ if (!nextOpen) return;
1563
1667
  setSearchQuery("");
1564
- if (context == null ? void 0 : context.refetchRelation) {
1565
- context.refetchRelation(column.name);
1668
+ setScrollTop(0);
1669
+ setSkip(0);
1670
+ skipRef.current = 0;
1671
+ setHasMore(true);
1672
+ const fetcher = (context == null ? void 0 : context.executeAction) ? (p) => context.executeAction("FETCH_RELATION", p) : (context == null ? void 0 : context.refetchRelation) ? (p) => context.refetchRelation(column.name, p) : null;
1673
+ if (fetcher && referencedTableId) {
1674
+ setIsLoadingMore(true);
1675
+ isLoadingMoreRef.current = true;
1676
+ fetcher({
1677
+ referencedTableId,
1678
+ displayField,
1679
+ take: BATCH_SIZE,
1680
+ limit: BATCH_SIZE,
1681
+ skip: 0
1682
+ }).then((res) => {
1683
+ const newData = Array.isArray(res) ? res : (res == null ? void 0 : res.data) && Array.isArray(res.data) ? res.data : [];
1684
+ setItems(newData);
1685
+ setSkip(BATCH_SIZE);
1686
+ skipRef.current = BATCH_SIZE;
1687
+ if (newData.length < BATCH_SIZE) {
1688
+ setHasMore(false);
1689
+ }
1690
+ }).catch((err) => {
1691
+ console.warn("[RelationPickerCell] Initial fetch failed:", err);
1692
+ }).finally(() => {
1693
+ setIsLoadingMore(false);
1694
+ isLoadingMoreRef.current = false;
1695
+ });
1566
1696
  }
1567
1697
  };
1568
1698
  useEffect5(() => {
@@ -1573,7 +1703,105 @@ function RelationPickerCell({
1573
1703
  }, 50);
1574
1704
  }
1575
1705
  }, [isOpen]);
1576
- const handleSelectOption = (optId) => {
1706
+ useEffect5(() => {
1707
+ if (!isOpen) return;
1708
+ const query = searchQuery.trim();
1709
+ const fetcher = (context == null ? void 0 : context.executeAction) ? (p) => context.executeAction("FETCH_RELATION", p) : (context == null ? void 0 : context.refetchRelation) ? (p) => context.refetchRelation(column.name, p) : null;
1710
+ if (!fetcher || !referencedTableId) return;
1711
+ setSkip(0);
1712
+ skipRef.current = 0;
1713
+ setHasMore(true);
1714
+ setScrollTop(0);
1715
+ if (listContainerRef.current) {
1716
+ listContainerRef.current.scrollTop = 0;
1717
+ }
1718
+ setIsRemoteSearching(true);
1719
+ const timer = setTimeout(() => {
1720
+ fetcher({
1721
+ referencedTableId,
1722
+ displayField,
1723
+ search: query,
1724
+ take: BATCH_SIZE,
1725
+ limit: BATCH_SIZE,
1726
+ skip: 0
1727
+ }).then((res) => {
1728
+ const newData = Array.isArray(res) ? res : (res == null ? void 0 : res.data) && Array.isArray(res.data) ? res.data : [];
1729
+ setItems(newData);
1730
+ setSkip(BATCH_SIZE);
1731
+ skipRef.current = BATCH_SIZE;
1732
+ if (newData.length < BATCH_SIZE) {
1733
+ setHasMore(false);
1734
+ }
1735
+ }).catch((err) => {
1736
+ console.warn("[RelationPickerCell] Remote search failed:", err);
1737
+ }).finally(() => {
1738
+ setIsRemoteSearching(false);
1739
+ });
1740
+ }, 250);
1741
+ return () => clearTimeout(timer);
1742
+ }, [searchQuery, isOpen, referencedTableId, displayField, context, column.name]);
1743
+ const handleLoadMore = useCallback5(async () => {
1744
+ if (isLoadingMoreRef.current || !hasMore || !referencedTableId) return;
1745
+ const fetcher = (context == null ? void 0 : context.executeAction) ? (p) => context.executeAction("FETCH_RELATION", p) : (context == null ? void 0 : context.refetchRelation) ? (p) => context.refetchRelation(column.name, p) : null;
1746
+ if (!fetcher) return;
1747
+ isLoadingMoreRef.current = true;
1748
+ setIsLoadingMore(true);
1749
+ const currentQuery = searchQuery.trim();
1750
+ const currentSkip = skipRef.current;
1751
+ try {
1752
+ const res = await fetcher({
1753
+ referencedTableId,
1754
+ displayField,
1755
+ search: currentQuery,
1756
+ take: BATCH_SIZE,
1757
+ limit: BATCH_SIZE,
1758
+ skip: currentSkip
1759
+ });
1760
+ const newData = Array.isArray(res) ? res : (res == null ? void 0 : res.data) && Array.isArray(res.data) ? res.data : [];
1761
+ if (newData.length < BATCH_SIZE) {
1762
+ setHasMore(false);
1763
+ }
1764
+ if (newData.length > 0) {
1765
+ setItems((prev) => {
1766
+ const seen = new Set(prev.map((i) => String(i.id)));
1767
+ const unique = newData.filter((i) => !seen.has(String(i.id)));
1768
+ return [...prev, ...unique];
1769
+ });
1770
+ setSkip((prev) => prev + BATCH_SIZE);
1771
+ skipRef.current += BATCH_SIZE;
1772
+ }
1773
+ } catch (err) {
1774
+ console.warn("[RelationPickerCell] Load more failed:", err);
1775
+ } finally {
1776
+ isLoadingMoreRef.current = false;
1777
+ setIsLoadingMore(false);
1778
+ }
1779
+ }, [hasMore, referencedTableId, searchQuery, context, column.name, displayField]);
1780
+ useEffect5(() => {
1781
+ if (!isOpen || !hasMore) return;
1782
+ const sentinel = sentinelRef.current;
1783
+ if (!sentinel) return;
1784
+ const observer = new IntersectionObserver(
1785
+ (entries) => {
1786
+ var _a2;
1787
+ if (((_a2 = entries[0]) == null ? void 0 : _a2.isIntersecting) && !isLoadingMoreRef.current) {
1788
+ handleLoadMore();
1789
+ }
1790
+ },
1791
+ { root: listContainerRef.current, threshold: 0.1 }
1792
+ );
1793
+ observer.observe(sentinel);
1794
+ return () => observer.disconnect();
1795
+ }, [isOpen, hasMore, handleLoadMore]);
1796
+ const handleListScroll = (e) => {
1797
+ const currentScrollTop = e.currentTarget.scrollTop;
1798
+ setScrollTop(currentScrollTop);
1799
+ const { clientHeight, scrollHeight } = e.currentTarget;
1800
+ if (scrollHeight - (currentScrollTop + clientHeight) < 60 && hasMore && !isLoadingMoreRef.current) {
1801
+ handleLoadMore();
1802
+ }
1803
+ };
1804
+ const handleSelectOption = useCallback5((optId) => {
1577
1805
  if (!allowMultiple) {
1578
1806
  onChange([optId]);
1579
1807
  setIsOpen(false);
@@ -1584,7 +1812,7 @@ function RelationPickerCell({
1584
1812
  onChange([...selectedIds, optId]);
1585
1813
  }
1586
1814
  }
1587
- };
1815
+ }, [allowMultiple, onChange, selectedIds]);
1588
1816
  const handleRemove = (idToRemove) => {
1589
1817
  onChange(selectedIds.filter((id) => id !== idToRemove));
1590
1818
  };
@@ -1595,7 +1823,8 @@ function RelationPickerCell({
1595
1823
  useEffect5(() => {
1596
1824
  if (!isOpen) return;
1597
1825
  function handleClickOutside(event) {
1598
- if (containerRef.current && !containerRef.current.contains(event.target)) {
1826
+ const target = event.target;
1827
+ if (containerRef.current && !containerRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target)) {
1599
1828
  setIsOpen(false);
1600
1829
  }
1601
1830
  }
@@ -1611,13 +1840,19 @@ function RelationPickerCell({
1611
1840
  document.removeEventListener("keydown", handleKeyDown);
1612
1841
  };
1613
1842
  }, [isOpen]);
1614
- const filteredOptions = useMemo3(() => {
1843
+ const displayedOptions = useMemo3(() => {
1615
1844
  const query = searchQuery.trim().toLowerCase();
1616
1845
  if (!query) return processedOptions;
1617
1846
  return processedOptions.filter((opt) => {
1618
1847
  return (opt._displayLabel || "").toLowerCase().includes(query);
1619
1848
  });
1620
1849
  }, [processedOptions, searchQuery]);
1850
+ const totalItems = displayedOptions.length;
1851
+ const totalHeight = totalItems * ITEM_HEIGHT;
1852
+ const startIndex = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - OVERSCAN);
1853
+ const endIndex = Math.min(totalItems, Math.ceil((scrollTop + CONTAINER_HEIGHT) / ITEM_HEIGHT) + OVERSCAN);
1854
+ const visibleOptions = displayedOptions.slice(startIndex, endIndex);
1855
+ const topPadding = startIndex * ITEM_HEIGHT;
1621
1856
  if (!optionsLoaded && selectedIds.length > 0) {
1622
1857
  return /* @__PURE__ */ jsx6("div", { className: "px-2 py-1 flex items-center h-full w-full", children: /* @__PURE__ */ jsx6("span", { className: "inline-block w-16 h-4 bg-slate-100 animate-pulse rounded" }) });
1623
1858
  }
@@ -1739,51 +1974,101 @@ function RelationPickerCell({
1739
1974
  )
1740
1975
  }
1741
1976
  ),
1742
- isOpen && /* @__PURE__ */ jsxs6(
1743
- "div",
1744
- {
1745
- id: `relation-dropdown-${column.name}-${rowId || "cell"}`,
1746
- dir: "rtl",
1747
- className: "absolute top-full right-0 mt-1 z-[100] bg-white rounded-md shadow-lg border border-slate-200 p-1 min-w-[200px] w-full max-w-[300px] animate-in fade-in duration-100 flex flex-col",
1748
- onClick: (e) => e.stopPropagation(),
1749
- children: [
1750
- /* @__PURE__ */ jsxs6("div", { className: "relative mb-1", children: [
1751
- /* @__PURE__ */ jsx6(Search, { className: "w-3.5 h-3.5 text-slate-400 absolute right-2 top-2 pointer-events-none" }),
1977
+ isOpen && dropdownCoords && typeof document !== "undefined" && createPortal(
1978
+ /* @__PURE__ */ jsxs6(
1979
+ "div",
1980
+ {
1981
+ ref: dropdownRef,
1982
+ id: `relation-dropdown-${column.name}-${rowId || "cell"}`,
1983
+ dir: "rtl",
1984
+ style: {
1985
+ position: "fixed",
1986
+ top: `${dropdownCoords.top}px`,
1987
+ right: `${dropdownCoords.right}px`,
1988
+ width: `${dropdownCoords.width}px`,
1989
+ zIndex: 99999
1990
+ },
1991
+ className: "bg-white rounded-md shadow-2xl border border-slate-200 p-1 animate-in fade-in duration-100 flex flex-col",
1992
+ onClick: (e) => e.stopPropagation(),
1993
+ onMouseDown: (e) => e.stopPropagation(),
1994
+ children: [
1995
+ /* @__PURE__ */ jsxs6("div", { className: "relative mb-1", children: [
1996
+ /* @__PURE__ */ jsx6(Search, { className: "w-3.5 h-3.5 text-slate-400 absolute right-2 top-2 pointer-events-none" }),
1997
+ /* @__PURE__ */ jsx6(
1998
+ "input",
1999
+ {
2000
+ ref: searchInputRef,
2001
+ type: "text",
2002
+ placeholder: "\u05D7\u05E4\u05E9...",
2003
+ value: searchQuery,
2004
+ onChange: (e) => setSearchQuery(e.target.value),
2005
+ onKeyDown: (e) => {
2006
+ if (e.key === "Escape") {
2007
+ e.stopPropagation();
2008
+ setIsOpen(false);
2009
+ }
2010
+ },
2011
+ className: "w-full h-7 pr-7 pl-7 bg-slate-50 border border-slate-200 rounded text-xs text-slate-800 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors"
2012
+ }
2013
+ ),
2014
+ isRemoteSearching && /* @__PURE__ */ jsx6(Loader2, { className: "w-3.5 h-3.5 text-blue-500 animate-spin absolute left-2 top-2 pointer-events-none" })
2015
+ ] }),
1752
2016
  /* @__PURE__ */ jsx6(
1753
- "input",
2017
+ "div",
1754
2018
  {
1755
- ref: searchInputRef,
1756
- type: "text",
1757
- placeholder: "\u05D7\u05E4\u05E9...",
1758
- value: searchQuery,
1759
- onChange: (e) => setSearchQuery(e.target.value),
1760
- onKeyDown: (e) => {
1761
- if (e.key === "Escape") {
1762
- e.stopPropagation();
1763
- setIsOpen(false);
1764
- }
1765
- },
1766
- className: "w-full h-7 pr-7 pl-2 bg-slate-50 border border-slate-200 rounded text-xs text-slate-800 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors"
2019
+ ref: listContainerRef,
2020
+ onScroll: handleListScroll,
2021
+ style: { maxHeight: `${CONTAINER_HEIGHT}px` },
2022
+ className: "overflow-y-auto relative",
2023
+ children: displayedOptions.length > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
2024
+ /* @__PURE__ */ jsx6("div", { style: { height: `${totalHeight}px`, position: "relative", width: "100%" }, children: /* @__PURE__ */ jsx6(
2025
+ "div",
2026
+ {
2027
+ style: {
2028
+ position: "absolute",
2029
+ top: 0,
2030
+ left: 0,
2031
+ right: 0,
2032
+ transform: `translateY(${topPadding}px)`
2033
+ },
2034
+ children: visibleOptions.map((opt) => /* @__PURE__ */ jsx6(
2035
+ RelationPickerRow,
2036
+ {
2037
+ id: opt.id,
2038
+ label: opt._displayLabel,
2039
+ isSelected: selectedIds.includes(opt.id),
2040
+ onSelect: handleSelectOption,
2041
+ height: ITEM_HEIGHT
2042
+ },
2043
+ opt.id
2044
+ ))
2045
+ }
2046
+ ) }),
2047
+ hasMore && /* @__PURE__ */ jsx6("div", { ref: sentinelRef, className: "pt-1 pb-0.5 px-2 text-center", children: isLoadingMore ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-center gap-1.5 text-xs text-slate-400 py-1", children: [
2048
+ /* @__PURE__ */ jsx6(Loader2, { className: "w-3 h-3 text-blue-500 animate-spin shrink-0" }),
2049
+ /* @__PURE__ */ jsx6("span", { children: "\u05D8\u05D5\u05E2\u05DF \u05E2\u05D5\u05D3 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA..." })
2050
+ ] }) : /* @__PURE__ */ jsx6(
2051
+ "button",
2052
+ {
2053
+ type: "button",
2054
+ onClick: (e) => {
2055
+ e.stopPropagation();
2056
+ handleLoadMore();
2057
+ },
2058
+ className: "text-[11px] text-blue-600 hover:text-blue-700 hover:underline py-1 px-2 font-medium",
2059
+ children: "\u05D8\u05E2\u05DF \u05E2\u05D5\u05D3 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA..."
2060
+ }
2061
+ ) })
2062
+ ] }) : /* @__PURE__ */ jsx6("div", { className: "py-2.5 px-2 text-center text-xs text-slate-400 select-none", children: isRemoteSearching ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-center gap-1.5 py-1", children: [
2063
+ /* @__PURE__ */ jsx6(Loader2, { className: "w-3 h-3 text-blue-500 animate-spin shrink-0" }),
2064
+ /* @__PURE__ */ jsx6("span", { children: "\u05DE\u05D7\u05E4\u05E9 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA..." })
2065
+ ] }) : items.length === 0 ? "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D6\u05DE\u05D9\u05E0\u05D5\u05EA" : "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA" })
1767
2066
  }
1768
2067
  )
1769
- ] }),
1770
- /* @__PURE__ */ jsx6("div", { className: "overflow-y-auto max-h-[180px] space-y-0.5", children: filteredOptions.length > 0 ? filteredOptions.map((opt) => {
1771
- const isSelected = selectedIds.includes(opt.id);
1772
- return /* @__PURE__ */ jsxs6(
1773
- "div",
1774
- {
1775
- onClick: () => handleSelectOption(opt.id),
1776
- className: `flex items-center justify-between px-2.5 py-1.5 rounded cursor-pointer text-xs transition-colors ${isSelected ? "bg-blue-50 text-blue-900 font-medium" : "hover:bg-slate-50 text-slate-700"}`,
1777
- children: [
1778
- /* @__PURE__ */ jsx6("span", { className: "truncate flex-1", children: opt._displayLabel }),
1779
- isSelected && /* @__PURE__ */ jsx6(Check3, { className: "w-3.5 h-3.5 text-blue-600 shrink-0 mr-1" })
1780
- ]
1781
- },
1782
- opt.id
1783
- );
1784
- }) : /* @__PURE__ */ jsx6("div", { className: "py-2.5 px-2 text-center text-xs text-slate-400 select-none", children: processedOptions.length === 0 ? "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D6\u05DE\u05D9\u05E0\u05D5\u05EA" : "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA" }) })
1785
- ]
1786
- }
2068
+ ]
2069
+ }
2070
+ ),
2071
+ document.body
1787
2072
  )
1788
2073
  ]
1789
2074
  }
@@ -2204,7 +2489,7 @@ var FORM_INPUTS = {
2204
2489
  };
2205
2490
 
2206
2491
  // src/components/TableBlock/fields/SubTableViewer.tsx
2207
- import { useState as useState9, useEffect as useEffect7, useRef as useRef6, useCallback as useCallback8, useMemo as useMemo4 } from "react";
2492
+ import { useState as useState10, useEffect as useEffect8, useRef as useRef6, useCallback as useCallback8, useMemo as useMemo4 } from "react";
2208
2493
 
2209
2494
  // src/utils/phone.ts
2210
2495
  function sanitizePhoneNumber(phone) {
@@ -2362,6 +2647,7 @@ function useColumnReorder({
2362
2647
  }
2363
2648
 
2364
2649
  // src/components/TableBlock/columnTitleUtils.tsx
2650
+ import { useState as useState9, useEffect as useEffect7 } from "react";
2365
2651
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2366
2652
  var canvas = null;
2367
2653
  function measureTextWidth(text, font = '600 14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif') {
@@ -2402,10 +2688,10 @@ function splitHeaderTitle(name) {
2402
2688
  }
2403
2689
  function calculateMinColumnWidth(name, isSubTable = false, colType) {
2404
2690
  const font = isSubTable ? '600 12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' : '600 14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2405
- const chromeWidth = isSubTable ? 44 : 52;
2406
- let baseMin = isSubTable ? 50 : 60;
2691
+ const chromeWidth = isSubTable ? 24 : 26;
2692
+ let baseMin = isSubTable ? 30 : 36;
2407
2693
  if ((colType == null ? void 0 : colType.toUpperCase()) === "GOOGLE_CONTACT") {
2408
- baseMin = isSubTable ? 110 : 120;
2694
+ baseMin = isSubTable ? 100 : 110;
2409
2695
  }
2410
2696
  if (!name) return baseMin;
2411
2697
  const { isSingleWord, line1, line2 } = splitHeaderTitle(name);
@@ -2419,12 +2705,16 @@ function calculateMinColumnWidth(name, isSubTable = false, colType) {
2419
2705
  return Math.max(baseMin, Math.ceil(maxLine + chromeWidth));
2420
2706
  }
2421
2707
  function ColumnHeaderTitle({ name, width, isSubTable = false }) {
2708
+ const [isMounted, setIsMounted] = useState9(false);
2709
+ useEffect7(() => {
2710
+ setIsMounted(true);
2711
+ }, []);
2422
2712
  const { isSingleWord, line1, line2 } = splitHeaderTitle(name);
2423
- if (isSingleWord || !line2) {
2424
- return /* @__PURE__ */ jsx8("span", { className: "whitespace-nowrap select-none overflow-hidden text-right flex-1", title: name, children: line1 });
2713
+ if (!isMounted || isSingleWord || !line2) {
2714
+ return /* @__PURE__ */ jsx8("span", { className: "whitespace-nowrap select-none overflow-hidden text-right flex-1", title: name, children: name });
2425
2715
  }
2426
2716
  const font = isSubTable ? '600 12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' : '600 14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2427
- const chromeWidth = isSubTable ? 44 : 52;
2717
+ const chromeWidth = isSubTable ? 24 : 26;
2428
2718
  const fullTitleWidth = measureTextWidth(name, font) + chromeWidth;
2429
2719
  if (width !== void 0 && width >= fullTitleWidth) {
2430
2720
  return /* @__PURE__ */ jsx8("span", { className: "whitespace-nowrap select-none overflow-hidden text-right flex-1", title: name, children: name });
@@ -2558,9 +2848,9 @@ function SubTableCell({
2558
2848
  const isContact = colType === "GOOGLE_CONTACT";
2559
2849
  const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
2560
2850
  const serverValue = (_b = record.data[column.name]) != null ? _b : colType === "BOOLEAN" ? false : "";
2561
- const [draftValue, setDraftValue] = useState9(serverValue);
2562
- const [isDirty, setIsDirty] = useState9(false);
2563
- const [validationError, setValidationError] = useState9(null);
2851
+ const [draftValue, setDraftValue] = useState10(serverValue);
2852
+ const [isDirty, setIsDirty] = useState10(false);
2853
+ const [validationError, setValidationError] = useState10(null);
2564
2854
  const cellRef = useRef6(null);
2565
2855
  const draftRef = useRef6(draftValue);
2566
2856
  const isDirtyRef = useRef6(isDirty);
@@ -2577,7 +2867,7 @@ function SubTableCell({
2577
2867
  recordIdRef.current = record.id;
2578
2868
  columnRef.current = column;
2579
2869
  onCommitRef.current = onCommit;
2580
- useEffect7(() => {
2870
+ useEffect8(() => {
2581
2871
  if (!isDirtyRef.current) {
2582
2872
  setDraftValue(serverValue);
2583
2873
  draftRef.current = serverValue;
@@ -2634,7 +2924,7 @@ function SubTableCell({
2634
2924
  setValidationError(null);
2635
2925
  lastFailedValRef.current = void 0;
2636
2926
  }, []);
2637
- useEffect7(() => {
2927
+ useEffect8(() => {
2638
2928
  return () => {
2639
2929
  if (isDirtyRef.current && !isCommittingRef.current) {
2640
2930
  const val = draftRef.current;
@@ -2736,7 +3026,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2736
3026
  var _a, _b, _c, _d, _e, _f;
2737
3027
  const { addToast } = useToast();
2738
3028
  const entries = Array.isArray(value) ? value : [];
2739
- const [linkedRecords, setLinkedRecords] = useState9(() => {
3029
+ const [linkedRecords, setLinkedRecords] = useState10(() => {
2740
3030
  const initial = [];
2741
3031
  for (const entry of entries) {
2742
3032
  if (entry.recordId && entry._resolvedData) {
@@ -2745,15 +3035,15 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2745
3035
  }
2746
3036
  return initial;
2747
3037
  });
2748
- const [targetSchema, setTargetSchema] = useState9(null);
2749
- const [isLoading, setIsLoading] = useState9(true);
2750
- const [isSearchOpen, setIsSearchOpen] = useState9(false);
2751
- const [searchQuery, setSearchQuery] = useState9("");
2752
- const [searchResults, setSearchResults] = useState9([]);
2753
- const [isSearching, setIsSearching] = useState9(false);
2754
- const [isCreating, setIsCreating] = useState9(false);
2755
- const [newRecordData, setNewRecordData] = useState9({});
2756
- const [conflictModalState, setConflictModalState] = useState9({
3038
+ const [targetSchema, setTargetSchema] = useState10(null);
3039
+ const [isLoading, setIsLoading] = useState10(true);
3040
+ const [isSearchOpen, setIsSearchOpen] = useState10(false);
3041
+ const [searchQuery, setSearchQuery] = useState10("");
3042
+ const [searchResults, setSearchResults] = useState10([]);
3043
+ const [isSearching, setIsSearching] = useState10(false);
3044
+ const [isCreating, setIsCreating] = useState10(false);
3045
+ const [newRecordData, setNewRecordData] = useState10({});
3046
+ const [conflictModalState, setConflictModalState] = useState10({
2757
3047
  isOpen: false,
2758
3048
  fieldName: "",
2759
3049
  matchingRecords: []
@@ -2763,7 +3053,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2763
3053
  const executeAction = context == null ? void 0 : context.executeAction;
2764
3054
  const referencedTableId = ((_a = column.config) == null ? void 0 : _a.referencedTableId) || column.referencedTableId;
2765
3055
  const fetchedIdsRef = useRef6(/* @__PURE__ */ new Set());
2766
- useEffect7(() => {
3056
+ useEffect8(() => {
2767
3057
  if (entries.length > 0) {
2768
3058
  setLinkedRecords((prev) => {
2769
3059
  const existingIds = new Set(prev.map((r) => r.id));
@@ -2778,7 +3068,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2778
3068
  });
2779
3069
  }
2780
3070
  }, [entries]);
2781
- useEffect7(() => {
3071
+ useEffect8(() => {
2782
3072
  if (!referencedTableId || !executeAction) {
2783
3073
  setIsLoading(false);
2784
3074
  return;
@@ -2805,7 +3095,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2805
3095
  recordIdsToFetch.forEach((id) => fetchedIdsRef.current.delete(id));
2806
3096
  }).finally(() => setIsLoading(false));
2807
3097
  }, [referencedTableId, executeAction, entries, targetSchema]);
2808
- useEffect7(() => {
3098
+ useEffect8(() => {
2809
3099
  const handleClick = (e) => {
2810
3100
  if (searchRef.current && !searchRef.current.contains(e.target)) {
2811
3101
  setIsSearchOpen(false);
@@ -2840,8 +3130,8 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2840
3130
  }
2841
3131
  }, 300);
2842
3132
  }, [executeAction, referencedTableId, entries]);
2843
- const [localColumnOrder, setLocalColumnOrder] = useState9(null);
2844
- useEffect7(() => {
3133
+ const [localColumnOrder, setLocalColumnOrder] = useState10(null);
3134
+ useEffect8(() => {
2845
3135
  setLocalColumnOrder(null);
2846
3136
  }, [column.name, (_b = column.config) == null ? void 0 : _b.subTableColumnOrder]);
2847
3137
  const rawTargetColumns = useMemo4(() => {
@@ -2900,8 +3190,8 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2900
3190
  return calculateMinColumnWidth(colName, true, col == null ? void 0 : col.type);
2901
3191
  }
2902
3192
  });
2903
- const [relationOptions, setRelationOptions] = useState9({});
2904
- useEffect7(() => {
3193
+ const [relationOptions, setRelationOptions] = useState10({});
3194
+ useEffect8(() => {
2905
3195
  if (!executeAction || !(targetSchema == null ? void 0 : targetSchema.columns) || targetSchema.columns.length === 0) return;
2906
3196
  const relationCols = targetSchema.columns.filter((c) => {
2907
3197
  var _a2, _b2;
@@ -2912,7 +3202,8 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2912
3202
  try {
2913
3203
  const options = await executeAction("FETCH_RELATION", {
2914
3204
  referencedTableId: (_a2 = col.config) == null ? void 0 : _a2.referencedTableId,
2915
- displayField: (_b2 = col.config) == null ? void 0 : _b2.displayField
3205
+ displayField: (_b2 = col.config) == null ? void 0 : _b2.displayField,
3206
+ limit: 1e3
2916
3207
  });
2917
3208
  if (options && Array.isArray(options)) {
2918
3209
  setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
@@ -2922,18 +3213,35 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
2922
3213
  }
2923
3214
  });
2924
3215
  }, [executeAction, targetColumns]);
2925
- const refetchRelation = useCallback8(async (colName) => {
2926
- var _a2, _b2, _c2;
3216
+ const refetchRelation = useCallback8(async (colName, params) => {
3217
+ var _a2, _b2, _c2, _d2, _e2, _f2;
2927
3218
  const col = targetColumns.find((c) => c.name === colName);
2928
- if (!col || ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) !== "RELATION" || !((_b2 = col.config) == null ? void 0 : _b2.referencedTableId) || !executeAction) return;
3219
+ if (!col || ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) !== "RELATION" || !((_b2 = col.config) == null ? void 0 : _b2.referencedTableId) || !executeAction) return [];
2929
3220
  try {
2930
- const options = await executeAction("FETCH_RELATION", { referencedTableId: col.config.referencedTableId, displayField: (_c2 = col.config) == null ? void 0 : _c2.displayField });
3221
+ const options = await executeAction("FETCH_RELATION", {
3222
+ referencedTableId: col.config.referencedTableId,
3223
+ displayField: (_c2 = col.config) == null ? void 0 : _c2.displayField,
3224
+ limit: (_d2 = params == null ? void 0 : params.limit) != null ? _d2 : 1e3,
3225
+ skip: (_e2 = params == null ? void 0 : params.skip) != null ? _e2 : 0,
3226
+ search: (_f2 = params == null ? void 0 : params.search) != null ? _f2 : ""
3227
+ });
2931
3228
  if (options && Array.isArray(options)) {
2932
- setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
3229
+ if (params == null ? void 0 : params.append) {
3230
+ setRelationOptions((prev) => {
3231
+ const existing = prev[col.name] || [];
3232
+ const existingIds = new Set(existing.map((o) => String(o.id)));
3233
+ const uniqueNew = options.filter((o) => !existingIds.has(String(o.id)));
3234
+ return __spreadProps(__spreadValues({}, prev), { [col.name]: [...existing, ...uniqueNew] });
3235
+ });
3236
+ } else if (!(params == null ? void 0 : params.search)) {
3237
+ setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
3238
+ }
3239
+ return options;
2933
3240
  }
2934
3241
  } catch (err) {
2935
3242
  console.warn(`[SubTableViewer] Could not refetch relation options for column ${col.name}:`, err);
2936
3243
  }
3244
+ return [];
2937
3245
  }, [targetColumns, executeAction]);
2938
3246
  const updateParentLinkage = async (updated) => {
2939
3247
  const isNewRow = rowId.startsWith("temp_");
@@ -3067,7 +3375,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3067
3375
  updateParentLinkage(updated);
3068
3376
  };
3069
3377
  const isMountedRef = useRef6(true);
3070
- useEffect7(() => {
3378
+ useEffect8(() => {
3071
3379
  isMountedRef.current = true;
3072
3380
  return () => {
3073
3381
  isMountedRef.current = false;
@@ -3602,12 +3910,352 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3602
3910
  }
3603
3911
 
3604
3912
  // src/components/TableBlock/TableBlockClient.tsx
3605
- import { X as X7, Copy as Copy2, Search as Search2, RotateCcw } from "lucide-react";
3913
+ import { X as X8, Copy as Copy2, RotateCcw as RotateCcw2, Download, Filter, Loader2 as Loader22 } from "lucide-react";
3914
+
3915
+ // src/components/TableBlock/RelationFilterPopover.tsx
3916
+ import { useState as useState11, useEffect as useEffect9, useRef as useRef7, useMemo as useMemo5, useCallback as useCallback9 } from "react";
3917
+ import { Search as Search2, X as X7, RotateCcw } from "lucide-react";
3606
3918
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3919
+ function calculatePopoverCoords(rect) {
3920
+ if (!rect || typeof window === "undefined") return null;
3921
+ const popoverWidth = 260;
3922
+ const popoverHeight = 360;
3923
+ let right = window.innerWidth - rect.right;
3924
+ if (right + popoverWidth > window.innerWidth - 10) {
3925
+ right = window.innerWidth - popoverWidth - 10;
3926
+ }
3927
+ if (right < 10) {
3928
+ right = 10;
3929
+ }
3930
+ const spaceBelow = window.innerHeight - rect.bottom;
3931
+ let top = rect.bottom + 6;
3932
+ if (spaceBelow < popoverHeight && rect.top > popoverHeight) {
3933
+ top = Math.max(10, rect.top - popoverHeight - 6);
3934
+ } else {
3935
+ top = Math.min(window.innerHeight - popoverHeight - 10, top);
3936
+ }
3937
+ return { top, right };
3938
+ }
3939
+ function RelationFilterPopover({
3940
+ isOpen,
3941
+ onClose,
3942
+ column,
3943
+ triggerRect,
3944
+ options,
3945
+ filterValue,
3946
+ selectedValues,
3947
+ onSelectionChange,
3948
+ isLoading = false
3949
+ }) {
3950
+ const popoverRef = useRef7(null);
3951
+ const searchInputRef = useRef7(null);
3952
+ const selectAllRef = useRef7(null);
3953
+ const [searchQuery, setSearchQuery] = useState11("");
3954
+ const [coords, setCoords] = useState11(
3955
+ () => calculatePopoverCoords(triggerRect)
3956
+ );
3957
+ const [mode, setMode] = useState11("include");
3958
+ const [checkedSet, setCheckedSet] = useState11(/* @__PURE__ */ new Set());
3959
+ const [excludedSet, setExcludedSet] = useState11(/* @__PURE__ */ new Set());
3960
+ const prevIsOpenRef = useRef7(isOpen);
3961
+ useEffect9(() => {
3962
+ const justOpened = isOpen && !prevIsOpenRef.current;
3963
+ prevIsOpenRef.current = isOpen;
3964
+ if (justOpened) {
3965
+ const activeVal = filterValue != null ? filterValue : selectedValues;
3966
+ if (activeVal && typeof activeVal === "object" && !Array.isArray(activeVal) && "operator" in activeVal && activeVal.operator === "notIn") {
3967
+ setMode("exclude");
3968
+ setExcludedSet(new Set(activeVal.values || []));
3969
+ setCheckedSet(/* @__PURE__ */ new Set());
3970
+ } else if (Array.isArray(activeVal) && activeVal.length > 0) {
3971
+ setMode("include");
3972
+ setCheckedSet(new Set(activeVal));
3973
+ setExcludedSet(/* @__PURE__ */ new Set());
3974
+ } else {
3975
+ setMode("include");
3976
+ setCheckedSet(/* @__PURE__ */ new Set());
3977
+ setExcludedSet(/* @__PURE__ */ new Set());
3978
+ }
3979
+ }
3980
+ }, [isOpen, filterValue, selectedValues]);
3981
+ const updatePosition = useCallback9(() => {
3982
+ const next = calculatePopoverCoords(triggerRect);
3983
+ if (next) setCoords(next);
3984
+ }, [triggerRect]);
3985
+ useEffect9(() => {
3986
+ if (isOpen) {
3987
+ updatePosition();
3988
+ setTimeout(() => {
3989
+ var _a;
3990
+ return (_a = searchInputRef.current) == null ? void 0 : _a.focus();
3991
+ }, 50);
3992
+ } else {
3993
+ setSearchQuery("");
3994
+ }
3995
+ }, [isOpen, updatePosition]);
3996
+ useEffect9(() => {
3997
+ if (!isOpen) return;
3998
+ const handleScrollOrResize = () => updatePosition();
3999
+ window.addEventListener("scroll", handleScrollOrResize, true);
4000
+ window.addEventListener("resize", handleScrollOrResize);
4001
+ return () => {
4002
+ window.removeEventListener("scroll", handleScrollOrResize, true);
4003
+ window.removeEventListener("resize", handleScrollOrResize);
4004
+ };
4005
+ }, [isOpen, updatePosition]);
4006
+ useEffect9(() => {
4007
+ if (!isOpen) return;
4008
+ const handleMouseDown = (e) => {
4009
+ if (popoverRef.current && !popoverRef.current.contains(e.target)) {
4010
+ onClose();
4011
+ }
4012
+ };
4013
+ document.addEventListener("mousedown", handleMouseDown);
4014
+ return () => {
4015
+ document.removeEventListener("mousedown", handleMouseDown);
4016
+ };
4017
+ }, [isOpen, onClose]);
4018
+ useEffect9(() => {
4019
+ if (!isOpen) return;
4020
+ const handleKeyDown = (e) => {
4021
+ if (e.key === "Escape") {
4022
+ onClose();
4023
+ }
4024
+ };
4025
+ window.addEventListener("keydown", handleKeyDown);
4026
+ return () => {
4027
+ window.removeEventListener("keydown", handleKeyDown);
4028
+ };
4029
+ }, [isOpen, onClose]);
4030
+ const filteredOptions = useMemo5(() => {
4031
+ if (!searchQuery.trim()) return options;
4032
+ const q = searchQuery.trim().toLowerCase();
4033
+ return options.filter((opt) => opt.label.toLowerCase().includes(q));
4034
+ }, [options, searchQuery]);
4035
+ const isOptionChecked = useCallback9(
4036
+ (label) => {
4037
+ if (mode === "exclude") {
4038
+ return !excludedSet.has(label);
4039
+ }
4040
+ return checkedSet.has(label);
4041
+ },
4042
+ [mode, excludedSet, checkedSet]
4043
+ );
4044
+ const allFilteredSelected = useMemo5(() => {
4045
+ if (filteredOptions.length === 0) return false;
4046
+ return filteredOptions.every((opt) => isOptionChecked(opt.label));
4047
+ }, [filteredOptions, isOptionChecked]);
4048
+ const someFilteredSelected = useMemo5(() => {
4049
+ return filteredOptions.some((opt) => isOptionChecked(opt.label));
4050
+ }, [filteredOptions, isOptionChecked]);
4051
+ useEffect9(() => {
4052
+ if (selectAllRef.current) {
4053
+ selectAllRef.current.indeterminate = !allFilteredSelected && someFilteredSelected;
4054
+ }
4055
+ }, [allFilteredSelected, someFilteredSelected]);
4056
+ const handleToggleOption = (label) => {
4057
+ if (mode === "exclude") {
4058
+ const nextExcluded = new Set(excludedSet);
4059
+ if (nextExcluded.has(label)) {
4060
+ nextExcluded.delete(label);
4061
+ } else {
4062
+ nextExcluded.add(label);
4063
+ }
4064
+ setExcludedSet(nextExcluded);
4065
+ if (nextExcluded.size === 0) {
4066
+ onSelectionChange(void 0);
4067
+ } else {
4068
+ onSelectionChange({ operator: "notIn", values: Array.from(nextExcluded) });
4069
+ }
4070
+ } else {
4071
+ const nextChecked = new Set(checkedSet);
4072
+ if (nextChecked.has(label)) {
4073
+ nextChecked.delete(label);
4074
+ } else {
4075
+ nextChecked.add(label);
4076
+ }
4077
+ setCheckedSet(nextChecked);
4078
+ if (nextChecked.size === 0) {
4079
+ onSelectionChange(void 0);
4080
+ } else if (options.length > 0 && nextChecked.size >= options.length) {
4081
+ setMode("exclude");
4082
+ setExcludedSet(/* @__PURE__ */ new Set());
4083
+ onSelectionChange(void 0);
4084
+ } else {
4085
+ onSelectionChange(Array.from(nextChecked));
4086
+ }
4087
+ }
4088
+ };
4089
+ const handleToggleSelectAll = (checked) => {
4090
+ if (checked) {
4091
+ setMode("exclude");
4092
+ setExcludedSet(/* @__PURE__ */ new Set());
4093
+ setCheckedSet(/* @__PURE__ */ new Set());
4094
+ onSelectionChange(void 0);
4095
+ } else {
4096
+ setMode("include");
4097
+ setCheckedSet(/* @__PURE__ */ new Set());
4098
+ setExcludedSet(/* @__PURE__ */ new Set());
4099
+ onSelectionChange(void 0);
4100
+ }
4101
+ };
4102
+ const handleResetFilter = () => {
4103
+ setMode("include");
4104
+ setCheckedSet(/* @__PURE__ */ new Set());
4105
+ setExcludedSet(/* @__PURE__ */ new Set());
4106
+ setSearchQuery("");
4107
+ onSelectionChange(void 0);
4108
+ };
4109
+ if (!isOpen || !coords) return null;
4110
+ const selectedCount = useMemo5(() => {
4111
+ if (mode === "exclude") {
4112
+ return Math.max(0, options.length - excludedSet.size);
4113
+ }
4114
+ return checkedSet.size;
4115
+ }, [mode, options.length, excludedSet.size, checkedSet.size]);
4116
+ return /* @__PURE__ */ jsxs10(
4117
+ "div",
4118
+ {
4119
+ ref: popoverRef,
4120
+ dir: "rtl",
4121
+ style: {
4122
+ top: `${coords.top}px`,
4123
+ right: `${coords.right}px`
4124
+ },
4125
+ className: "fixed z-[99999] w-[260px] bg-white rounded-xl shadow-2xl border border-slate-200 p-2.5 text-right select-none animate-in fade-in duration-100",
4126
+ onClick: (e) => e.stopPropagation(),
4127
+ onMouseDown: (e) => e.stopPropagation(),
4128
+ children: [
4129
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between border-b border-slate-100 pb-2 mb-2", children: [
4130
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0", children: [
4131
+ /* @__PURE__ */ jsxs10("span", { className: "text-xs font-bold text-slate-700 truncate", children: [
4132
+ "\u05E1\u05D9\u05E0\u05D5\u05DF \u05DC\u05E4\u05D9 ",
4133
+ column.name
4134
+ ] }),
4135
+ mode === "exclude" && excludedSet.size > 0 ? /* @__PURE__ */ jsxs10("span", { className: "text-[10px] text-amber-600 font-semibold", children: [
4136
+ "(\u05D4\u05DB\u05DC \u05DC\u05DE\u05E2\u05D8 ",
4137
+ excludedSet.size,
4138
+ ")"
4139
+ ] }) : mode === "exclude" && excludedSet.size === 0 ? /* @__PURE__ */ jsx10("span", { className: "text-[10px] text-brand font-semibold", children: "(\u05D4\u05DB\u05DC \u05E0\u05D1\u05D7\u05E8)" }) : selectedCount > 0 ? /* @__PURE__ */ jsxs10("span", { className: "text-[10px] text-brand font-semibold", children: [
4140
+ "(",
4141
+ selectedCount,
4142
+ " \u05E0\u05D1\u05D7\u05E8\u05D5)"
4143
+ ] }) : null
4144
+ ] }),
4145
+ /* @__PURE__ */ jsx10(
4146
+ "button",
4147
+ {
4148
+ type: "button",
4149
+ onClick: onClose,
4150
+ className: "text-slate-400 hover:text-slate-600 p-0.5 rounded hover:bg-slate-100 transition-colors cursor-pointer",
4151
+ title: "\u05E1\u05D2\u05D5\u05E8",
4152
+ children: /* @__PURE__ */ jsx10(X7, { className: "w-3.5 h-3.5" })
4153
+ }
4154
+ )
4155
+ ] }),
4156
+ /* @__PURE__ */ jsxs10("div", { className: "relative mb-2", children: [
4157
+ /* @__PURE__ */ jsx10(Search2, { className: "w-3.5 h-3.5 text-slate-400 absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none" }),
4158
+ /* @__PURE__ */ jsx10(
4159
+ "input",
4160
+ {
4161
+ ref: searchInputRef,
4162
+ type: "text",
4163
+ placeholder: "\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA...",
4164
+ value: searchQuery,
4165
+ onChange: (e) => setSearchQuery(e.target.value),
4166
+ className: "h-7 text-xs pr-7 pl-6 w-full rounded-md border border-slate-200 bg-slate-50 focus:bg-white focus:border-brand focus:ring-1 focus:ring-brand text-slate-800 transition-all placeholder:text-slate-400"
4167
+ }
4168
+ ),
4169
+ searchQuery && /* @__PURE__ */ jsx10(
4170
+ "button",
4171
+ {
4172
+ type: "button",
4173
+ onClick: () => setSearchQuery(""),
4174
+ className: "absolute left-1.5 top-1/2 -translate-y-1/2 p-0.5 text-slate-400 hover:text-slate-600 rounded",
4175
+ children: /* @__PURE__ */ jsx10(X7, { className: "w-3 h-3" })
4176
+ }
4177
+ )
4178
+ ] }),
4179
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between px-1.5 py-1 mb-1 rounded hover:bg-slate-50 transition-colors border-b border-slate-100 pb-1.5", children: [
4180
+ /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-2 cursor-pointer text-xs font-medium text-slate-700 flex-1", children: [
4181
+ /* @__PURE__ */ jsx10(
4182
+ "input",
4183
+ {
4184
+ ref: selectAllRef,
4185
+ type: "checkbox",
4186
+ checked: allFilteredSelected,
4187
+ onChange: (e) => handleToggleSelectAll(e.target.checked),
4188
+ className: "w-3.5 h-3.5 rounded border-slate-300 text-brand focus:ring-brand cursor-pointer accent-[#0d9488]"
4189
+ }
4190
+ ),
4191
+ /* @__PURE__ */ jsx10("span", { children: "\u05D1\u05D7\u05E8 \u05D4\u05DB\u05DC" })
4192
+ ] }),
4193
+ (mode === "exclude" || checkedSet.size > 0) && /* @__PURE__ */ jsxs10(
4194
+ "button",
4195
+ {
4196
+ type: "button",
4197
+ onClick: handleResetFilter,
4198
+ className: "text-[11px] text-blue-600 hover:text-blue-700 hover:underline flex items-center gap-1 cursor-pointer",
4199
+ title: "\u05E0\u05E7\u05D4 \u05E1\u05D9\u05E0\u05D5\u05DF \u05D5\u05D4\u05E6\u05D2 \u05D4\u05DB\u05DC",
4200
+ children: [
4201
+ /* @__PURE__ */ jsx10(RotateCcw, { className: "w-2.5 h-2.5" }),
4202
+ /* @__PURE__ */ jsx10("span", { children: "\u05E0\u05E7\u05D4" })
4203
+ ]
4204
+ }
4205
+ )
4206
+ ] }),
4207
+ /* @__PURE__ */ jsx10("div", { className: "max-h-[250px] overflow-y-auto space-y-0.5 custom-scrollbar pr-0.5", children: isLoading ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-center py-6 text-slate-400 text-xs", children: [
4208
+ /* @__PURE__ */ jsx10("div", { className: "w-4 h-4 border-2 border-brand border-t-transparent rounded-full animate-spin ml-2" }),
4209
+ /* @__PURE__ */ jsx10("span", { children: "\u05D8\u05D5\u05E2\u05DF \u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA..." })
4210
+ ] }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx10("div", { className: "py-6 text-center text-xs text-slate-400", children: "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA" }) : filteredOptions.map((opt) => {
4211
+ const isChecked = isOptionChecked(opt.label);
4212
+ return /* @__PURE__ */ jsxs10(
4213
+ "label",
4214
+ {
4215
+ className: `flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors text-xs ${isChecked ? "text-slate-800 font-medium bg-slate-50/60" : "text-slate-500 hover:bg-slate-50"}`,
4216
+ children: [
4217
+ /* @__PURE__ */ jsx10(
4218
+ "input",
4219
+ {
4220
+ type: "checkbox",
4221
+ checked: isChecked,
4222
+ onChange: () => handleToggleOption(opt.label),
4223
+ className: "w-3.5 h-3.5 rounded border-slate-300 text-brand focus:ring-brand cursor-pointer accent-[#0d9488]"
4224
+ }
4225
+ ),
4226
+ /* @__PURE__ */ jsx10("span", { className: "truncate flex-1", title: opt.label, children: opt.label })
4227
+ ]
4228
+ },
4229
+ opt.id
4230
+ );
4231
+ }) }),
4232
+ /* @__PURE__ */ jsxs10("div", { className: "border-t border-slate-100 pt-1.5 mt-1 flex items-center justify-between text-[11px] text-slate-400 px-1", children: [
4233
+ /* @__PURE__ */ jsxs10("span", { children: [
4234
+ filteredOptions.length,
4235
+ " \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA"
4236
+ ] }),
4237
+ /* @__PURE__ */ jsx10(
4238
+ "button",
4239
+ {
4240
+ type: "button",
4241
+ onClick: onClose,
4242
+ className: "text-slate-600 hover:text-slate-900 font-medium px-2 py-0.5 rounded hover:bg-slate-100 transition-colors cursor-pointer",
4243
+ children: "\u05E1\u05D2\u05D5\u05E8"
4244
+ }
4245
+ )
4246
+ ] })
4247
+ ]
4248
+ }
4249
+ );
4250
+ }
4251
+
4252
+ // src/components/TableBlock/TableBlockClient.tsx
4253
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3607
4254
  function TableBlockClient({
3608
4255
  data: initialRecords,
3609
4256
  settings: schema,
3610
4257
  executeAction,
4258
+ exportAction,
3611
4259
  emitEvent,
3612
4260
  pageState,
3613
4261
  isLoading,
@@ -3620,6 +4268,7 @@ function TableBlockClient({
3620
4268
  "data-testid": dataTestId
3621
4269
  }) {
3622
4270
  const { showError, addToast } = useToast();
4271
+ const [isExporting, setIsExporting] = useState12(false);
3623
4272
  const tableKey = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id) || (schema == null ? void 0 : schema.name) || "default";
3624
4273
  const { columnWidths, startResize } = useColumnResize({
3625
4274
  storageKey: `table_widths_${tableKey}`,
@@ -3630,14 +4279,18 @@ function TableBlockClient({
3630
4279
  return calculateMinColumnWidth(colName, false, col == null ? void 0 : col.type);
3631
4280
  }
3632
4281
  });
3633
- const [isMounted, setIsMounted] = useState10(false);
3634
- const [isInitialLoading, setIsInitialLoading] = useState10(true);
3635
- const [page, setPage] = useState10(1);
3636
- const [limit, setLimit] = useState10(50);
3637
- const [totalRecords, setTotalRecords] = useState10(0);
3638
- const [totalPages, setTotalPages] = useState10(1);
3639
- const [showSummary, setShowSummary] = useState10(false);
3640
- useEffect8(() => {
4282
+ const executeActionRef = useRef8(executeAction);
4283
+ useEffect10(() => {
4284
+ executeActionRef.current = executeAction;
4285
+ }, [executeAction]);
4286
+ const [isMounted, setIsMounted] = useState12(false);
4287
+ const [isInitialLoading, setIsInitialLoading] = useState12(true);
4288
+ const [page, setPage] = useState12(1);
4289
+ const [limit, setLimit] = useState12(50);
4290
+ const [totalRecords, setTotalRecords] = useState12(0);
4291
+ const [totalPages, setTotalPages] = useState12(1);
4292
+ const [showSummary, setShowSummary] = useState12(false);
4293
+ useEffect10(() => {
3641
4294
  if (typeof window !== "undefined") {
3642
4295
  const savedSummary = localStorage.getItem(`table_summary_${tableKey}`);
3643
4296
  if (savedSummary !== null) {
@@ -3686,7 +4339,7 @@ function TableBlockClient({
3686
4339
  return __spreadProps(__spreadValues({}, row), { id: String(row.id || ""), data: rowData });
3687
4340
  });
3688
4341
  };
3689
- const mergeColumnDefs = useCallback9((existingCols, incomingCols) => {
4342
+ const mergeColumnDefs = useCallback10((existingCols, incomingCols) => {
3690
4343
  if (!incomingCols || incomingCols.length === 0) return existingCols || [];
3691
4344
  if (!existingCols || existingCols.length === 0) return incomingCols;
3692
4345
  const existingMap = new Map(existingCols.map((c) => [c.name, c]));
@@ -3706,18 +4359,18 @@ function TableBlockClient({
3706
4359
  return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
3707
4360
  });
3708
4361
  }, []);
3709
- const [localColumns, setLocalColumns] = useState10(() => (schema == null ? void 0 : schema.columns) || []);
3710
- useEffect8(() => {
4362
+ const [localColumns, setLocalColumns] = useState12(() => (schema == null ? void 0 : schema.columns) || []);
4363
+ useEffect10(() => {
3711
4364
  if ((schema == null ? void 0 : schema.columns) && schema.columns.length > 0) {
3712
4365
  setLocalColumns((prev) => mergeColumnDefs(prev, schema.columns));
3713
4366
  }
3714
4367
  }, [schema == null ? void 0 : schema.columns, mergeColumnDefs]);
3715
- const [dirty, setDirty] = useState10({ added: [], modified: {}, deleted: [] });
3716
- const dirtyRef = useRef7(dirty);
3717
- useEffect8(() => {
4368
+ const [dirty, setDirty] = useState12({ added: [], modified: {}, deleted: [] });
4369
+ const dirtyRef = useRef8(dirty);
4370
+ useEffect10(() => {
3718
4371
  dirtyRef.current = dirty;
3719
4372
  }, [dirty]);
3720
- const mergeWithDirty = useCallback9((baseRecords) => {
4373
+ const mergeWithDirty = useCallback10((baseRecords) => {
3721
4374
  const { added, modified, deleted } = dirtyRef.current;
3722
4375
  const nonDeleted = (baseRecords || []).filter((r) => !deleted.includes(r.id));
3723
4376
  const modifiedRecords = nonDeleted.map((r) => {
@@ -3730,9 +4383,10 @@ function TableBlockClient({
3730
4383
  const uniqueAdded = added.filter((r) => !baseIds.has(r.id));
3731
4384
  return [...uniqueAdded, ...modifiedRecords];
3732
4385
  }, []);
3733
- const [data, setData] = useState10(() => normalizeRecords(initialRecords));
3734
- const [serverRecords, setServerRecords] = useState10(() => normalizeRecords(initialRecords));
3735
- useEffect8(() => {
4386
+ const [data, setData] = useState12(() => normalizeRecords(initialRecords));
4387
+ const [serverRecords, setServerRecords] = useState12(() => normalizeRecords(initialRecords));
4388
+ const filteredData = data;
4389
+ useEffect10(() => {
3736
4390
  if (initialRecords) {
3737
4391
  const norm = normalizeRecords(initialRecords);
3738
4392
  setServerRecords(norm);
@@ -3749,31 +4403,118 @@ function TableBlockClient({
3749
4403
  }
3750
4404
  return false;
3751
4405
  };
3752
- const [baseRecordsMap, setBaseRecordsMap] = useState10(() => {
4406
+ const [baseRecordsMap, setBaseRecordsMap] = useState12(() => {
3753
4407
  const map = /* @__PURE__ */ new Map();
3754
4408
  (normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
3755
4409
  return map;
3756
4410
  });
3757
- useEffect8(() => {
4411
+ useEffect10(() => {
3758
4412
  if (initialRecords && initialRecords.length > 0) {
3759
4413
  const map = /* @__PURE__ */ new Map();
3760
4414
  (normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
3761
4415
  setBaseRecordsMap(map);
3762
4416
  }
3763
4417
  }, [initialRecords]);
3764
- const refetchTableData = useCallback9((targetPage) => {
3765
- const tableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
3766
- if (!tableId || !executeAction) return Promise.resolve();
3767
- const activePage = targetPage !== void 0 ? targetPage : page;
3768
- const skip = (activePage - 1) * limit;
3769
- return executeAction("FETCH_TABLE_DATA", { tableId, page: activePage, limit, skip }).then((res) => {
3770
- var _a;
3771
- if (res) {
3772
- if (((_a = res.schema) == null ? void 0 : _a.columns) && res.schema.columns.length > 0) {
3773
- setLocalColumns((prev) => mergeColumnDefs(prev, res.schema.columns));
3774
- }
3775
- if (res.records && Array.isArray(res.records)) {
3776
- const norm = normalizeRecords(res.records);
4418
+ const [searchQuery, setSearchQuery] = useState12("");
4419
+ const [columnFilters, setColumnFilters] = useState12({});
4420
+ const [activeSearchCol, setActiveSearchCol] = useState12(null);
4421
+ const activeSearchRef = useRef8(null);
4422
+ const [activeRelationCol, setActiveRelationCol] = useState12(null);
4423
+ const [relationTriggerRect, setRelationTriggerRect] = useState12(null);
4424
+ const filterButtonRefs = useRef8({});
4425
+ const [relationOptions, setRelationOptions] = useState12({});
4426
+ const [relationFilterOptions, setRelationFilterOptions] = useState12({});
4427
+ const [loadingFilterCol, setLoadingFilterCol] = useState12(null);
4428
+ const relationDictionaryRef = useRef8({});
4429
+ const mergeRelationOptions = useCallback10((colName, newOptions) => {
4430
+ if (!Array.isArray(newOptions) || newOptions.length === 0) return;
4431
+ if (!relationDictionaryRef.current[colName]) {
4432
+ relationDictionaryRef.current[colName] = /* @__PURE__ */ new Map();
4433
+ }
4434
+ const dict = relationDictionaryRef.current[colName];
4435
+ let hasNew = false;
4436
+ for (const opt of newOptions) {
4437
+ if (!opt) continue;
4438
+ const id = String(opt.id || opt._id || "");
4439
+ if (!id) continue;
4440
+ const existing = dict.get(id);
4441
+ if (!existing || opt.label && !existing.label) {
4442
+ dict.set(id, opt);
4443
+ hasNew = true;
4444
+ }
4445
+ }
4446
+ if (hasNew) {
4447
+ setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), {
4448
+ [colName]: Array.from(dict.values())
4449
+ }));
4450
+ }
4451
+ }, []);
4452
+ useEffect10(() => {
4453
+ if (!activeSearchCol) return;
4454
+ const handleMouseDown = (e) => {
4455
+ if (activeSearchRef.current && !activeSearchRef.current.contains(e.target)) {
4456
+ setActiveSearchCol(null);
4457
+ }
4458
+ };
4459
+ document.addEventListener("mousedown", handleMouseDown);
4460
+ return () => {
4461
+ document.removeEventListener("mousedown", handleMouseDown);
4462
+ };
4463
+ }, [activeSearchCol]);
4464
+ const filtersKey = useMemo6(() => {
4465
+ const active = {};
4466
+ Object.entries(columnFilters).forEach(([k, v]) => {
4467
+ if (Array.isArray(v) && v.length > 0) active[k] = v;
4468
+ else if (v && typeof v === "object" && "operator" in v && Array.isArray(v.values) && v.values.length > 0) active[k] = v;
4469
+ else if (typeof v === "string" && v.trim()) active[k] = v.trim();
4470
+ });
4471
+ return JSON.stringify(active);
4472
+ }, [columnFilters]);
4473
+ const [debouncedSearch, setDebouncedSearch] = useState12(searchQuery);
4474
+ useEffect10(() => {
4475
+ const timer = setTimeout(() => {
4476
+ setDebouncedSearch(searchQuery.trim());
4477
+ }, 300);
4478
+ return () => clearTimeout(timer);
4479
+ }, [searchQuery]);
4480
+ const isSearchMountedRef = useRef8(false);
4481
+ useEffect10(() => {
4482
+ if (!isSearchMountedRef.current) {
4483
+ isSearchMountedRef.current = true;
4484
+ return;
4485
+ }
4486
+ setPage(1);
4487
+ }, [debouncedSearch]);
4488
+ const [isFetching, setIsFetching] = useState12(false);
4489
+ const fetchRequestIdRef = useRef8(0);
4490
+ const fetchTableData = useCallback10((targetPage, targetLimit, targetSearch, targetFiltersKey) => {
4491
+ const tableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
4492
+ const action = executeActionRef.current;
4493
+ if (!tableId || !action) return Promise.resolve();
4494
+ const currentRequestId = ++fetchRequestIdRef.current;
4495
+ setIsFetching(true);
4496
+ let activeFilters = {};
4497
+ try {
4498
+ activeFilters = JSON.parse(targetFiltersKey);
4499
+ } catch (e) {
4500
+ }
4501
+ const skip = (targetPage - 1) * targetLimit;
4502
+ return action("FETCH_TABLE_DATA", {
4503
+ tableId,
4504
+ page: targetPage,
4505
+ limit: targetLimit,
4506
+ skip,
4507
+ search: targetSearch || void 0,
4508
+ columnFilters: Object.keys(activeFilters).length > 0 ? activeFilters : void 0
4509
+ }).then((res) => {
4510
+ var _a;
4511
+ if (currentRequestId !== fetchRequestIdRef.current) return;
4512
+ if (res) {
4513
+ if (((_a = res.schema) == null ? void 0 : _a.columns) && res.schema.columns.length > 0) {
4514
+ setLocalColumns((prev) => mergeColumnDefs(prev, res.schema.columns));
4515
+ }
4516
+ if (res.records && Array.isArray(res.records)) {
4517
+ const norm = normalizeRecords(res.records);
3777
4518
  const map = /* @__PURE__ */ new Map();
3778
4519
  norm.forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
3779
4520
  setBaseRecordsMap(map);
@@ -3782,37 +4523,50 @@ function TableBlockClient({
3782
4523
  }
3783
4524
  if (res.meta) {
3784
4525
  const total = typeof res.meta.totalRecords === "number" ? res.meta.totalRecords : 0;
3785
- const pages = typeof res.meta.totalPages === "number" ? res.meta.totalPages : Math.ceil(total / limit) || 1;
4526
+ const pages = typeof res.meta.totalPages === "number" ? res.meta.totalPages : Math.ceil(total / targetLimit) || 1;
3786
4527
  setTotalRecords(total);
3787
4528
  setTotalPages(pages);
3788
- if (activePage > pages && pages >= 1) {
3789
- setPage(pages);
3790
- }
3791
4529
  } else if (res.records && Array.isArray(res.records)) {
3792
4530
  setTotalRecords(res.records.length);
3793
- setTotalPages(Math.ceil(res.records.length / limit) || 1);
4531
+ setTotalPages(Math.ceil(res.records.length / targetLimit) || 1);
4532
+ }
4533
+ if (res.relationOptions && typeof res.relationOptions === "object") {
4534
+ for (const [colName, opts] of Object.entries(res.relationOptions)) {
4535
+ if (Array.isArray(opts)) {
4536
+ mergeRelationOptions(colName, opts);
4537
+ }
4538
+ }
3794
4539
  }
3795
4540
  }
3796
4541
  }).catch((err) => {
3797
- console.error("[TableBlock] Failed to auto-fetch table data:", err);
4542
+ if (currentRequestId === fetchRequestIdRef.current) {
4543
+ console.error("[TableBlock] Failed to fetch table data:", err);
4544
+ }
4545
+ }).finally(() => {
4546
+ if (currentRequestId === fetchRequestIdRef.current) {
4547
+ setIsFetching(false);
4548
+ setIsInitialLoading(false);
4549
+ }
3798
4550
  });
3799
- }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, page, limit, executeAction, mergeColumnDefs, mergeWithDirty]);
3800
- useEffect8(() => {
3801
- let isMounted2 = true;
3802
- const fetchPromise = refetchTableData();
3803
- if (fetchPromise && typeof fetchPromise.finally === "function") {
3804
- fetchPromise.finally(() => {
3805
- if (isMounted2) {
3806
- setIsInitialLoading(false);
3807
- }
3808
- });
3809
- } else {
3810
- setIsInitialLoading(false);
3811
- }
3812
- return () => {
3813
- isMounted2 = false;
3814
- };
3815
- }, [refetchTableData]);
4551
+ }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, mergeColumnDefs, mergeWithDirty, mergeRelationOptions]);
4552
+ useEffect10(() => {
4553
+ fetchTableData(page, limit, debouncedSearch, filtersKey);
4554
+ }, [page, limit, debouncedSearch, filtersKey, fetchTableData]);
4555
+ const refetchTableData = useCallback10(() => {
4556
+ return fetchTableData(page, limit, debouncedSearch, filtersKey);
4557
+ }, [page, limit, debouncedSearch, filtersKey, fetchTableData]);
4558
+ const handleApplyColumnFilter = useCallback10((colName, nextVal) => {
4559
+ setColumnFilters((prev) => {
4560
+ const next = __spreadValues({}, prev);
4561
+ if (nextVal === void 0 || Array.isArray(nextVal) && nextVal.length === 0 || typeof nextVal === "string" && !nextVal.trim() || typeof nextVal === "object" && "values" in nextVal && Array.isArray(nextVal.values) && nextVal.values.length === 0) {
4562
+ delete next[colName];
4563
+ } else {
4564
+ next[colName] = nextVal;
4565
+ }
4566
+ return next;
4567
+ });
4568
+ setPage(1);
4569
+ }, []);
3816
4570
  const handleLimitChange = (newLimit) => {
3817
4571
  setLimit(newLimit);
3818
4572
  setPage(1);
@@ -3826,37 +4580,15 @@ function TableBlockClient({
3826
4580
  const handleNextPage = () => {
3827
4581
  setPage((prev) => Math.min(totalPages, prev + 1));
3828
4582
  };
3829
- useEffect8(() => {
3830
- if (error) {
3831
- showError(error, "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E8\u05DB\u05D9\u05D1");
3832
- }
3833
- }, [error, showError]);
3834
- const [searchQuery, setSearchQuery] = useState10("");
3835
- const [columnFilters, setColumnFilters] = useState10({});
3836
- const [activeSearchCol, setActiveSearchCol] = useState10(null);
3837
- const activeSearchRef = useRef7(null);
3838
- useEffect8(() => {
3839
- if (!activeSearchCol) return;
3840
- const handleMouseDown = (e) => {
3841
- if (activeSearchRef.current && !activeSearchRef.current.contains(e.target)) {
3842
- setActiveSearchCol(null);
3843
- }
3844
- };
3845
- document.addEventListener("mousedown", handleMouseDown);
3846
- return () => {
3847
- document.removeEventListener("mousedown", handleMouseDown);
3848
- };
3849
- }, [activeSearchCol]);
3850
- const [selectedIds, setSelectedIds] = useState10(/* @__PURE__ */ new Set());
3851
- const [pendingDeletions, setPendingDeletions] = useState10([]);
3852
- const [isSaving, setIsSaving] = useState10(false);
4583
+ const [selectedIds, setSelectedIds] = useState12(/* @__PURE__ */ new Set());
4584
+ const [pendingDeletions, setPendingDeletions] = useState12([]);
4585
+ const [isSaving, setIsSaving] = useState12(false);
3853
4586
  const isUpdatingActive = Boolean(isUpdating || isSaving);
3854
- const [isSaveSuccess, setIsSaveSuccess] = useState10(false);
3855
- const [relationOptions, setRelationOptions] = useState10({});
3856
- const [expandedRowIds, setExpandedRowIds] = useState10(/* @__PURE__ */ new Set());
3857
- const schemaColsString = useMemo5(() => JSON.stringify(localColumns || []), [localColumns]);
3858
- const refetchRelation = useCallback9(async (colName) => {
3859
- var _a, _b, _c;
4587
+ const [isSaveSuccess, setIsSaveSuccess] = useState12(false);
4588
+ const [expandedRowIds, setExpandedRowIds] = useState12(/* @__PURE__ */ new Set());
4589
+ const schemaColsString = useMemo6(() => JSON.stringify(localColumns || []), [localColumns]);
4590
+ const refetchRelation = useCallback10(async (colName, params) => {
4591
+ var _a, _b, _c, _d, _e, _f, _g;
3860
4592
  let parsedCols = [];
3861
4593
  try {
3862
4594
  parsedCols = JSON.parse(schemaColsString);
@@ -3866,19 +4598,125 @@ function TableBlockClient({
3866
4598
  var _a2;
3867
4599
  return c.name === colName && ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION";
3868
4600
  });
3869
- if (!col || !((_a = col.config) == null ? void 0 : _a.referencedTableId)) return;
4601
+ if (!col || !((_a = col.config) == null ? void 0 : _a.referencedTableId)) return [];
3870
4602
  try {
3871
4603
  if (executeAction) {
3872
- const options = await executeAction("FETCH_RELATION", { referencedTableId: (_b = col.config) == null ? void 0 : _b.referencedTableId, displayField: (_c = col.config) == null ? void 0 : _c.displayField });
3873
- if (options && Array.isArray(options)) {
3874
- setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [colName]: options }));
4604
+ const take = (_c = (_b = params == null ? void 0 : params.take) != null ? _b : params == null ? void 0 : params.limit) != null ? _c : 1e3;
4605
+ const payload = {
4606
+ referencedTableId: (_d = col.config) == null ? void 0 : _d.referencedTableId,
4607
+ displayField: (_e = col.config) == null ? void 0 : _e.displayField
4608
+ };
4609
+ if (params == null ? void 0 : params.recordIds) {
4610
+ payload.recordIds = params.recordIds;
4611
+ } else {
4612
+ payload.take = take;
4613
+ payload.limit = take;
4614
+ payload.skip = (_f = params == null ? void 0 : params.skip) != null ? _f : 0;
4615
+ payload.search = (_g = params == null ? void 0 : params.search) != null ? _g : "";
4616
+ }
4617
+ const options = await executeAction("FETCH_RELATION", payload);
4618
+ const list = Array.isArray(options) ? options : (options == null ? void 0 : options.data) && Array.isArray(options.data) ? options.data : [];
4619
+ if (list && Array.isArray(list)) {
4620
+ mergeRelationOptions(colName, list);
4621
+ return list;
3875
4622
  }
3876
4623
  }
3877
4624
  } catch (err) {
3878
4625
  console.warn(`[TableBlock] Could not fetch relation options for column ${colName}:`, err);
3879
4626
  }
3880
- }, [schemaColsString, executeAction]);
3881
- useEffect8(() => {
4627
+ return [];
4628
+ }, [schemaColsString, executeAction, mergeRelationOptions]);
4629
+ const fetchRelationFilterOptions = useCallback10(async (colName) => {
4630
+ var _a;
4631
+ let parsedCols = [];
4632
+ try {
4633
+ parsedCols = JSON.parse(schemaColsString);
4634
+ } catch (e) {
4635
+ }
4636
+ const col = parsedCols.find((c) => {
4637
+ var _a2;
4638
+ return c.name === colName && ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION";
4639
+ });
4640
+ if (!col || !((_a = col.config) == null ? void 0 : _a.referencedTableId) || !executeAction) return;
4641
+ const currentTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
4642
+ setLoadingFilterCol(colName);
4643
+ try {
4644
+ const res = await executeAction("FETCH_RELATION", {
4645
+ referencedTableId: col.config.referencedTableId,
4646
+ currentTableId,
4647
+ columnName: colName,
4648
+ isFilter: true,
4649
+ displayField: col.config.displayField,
4650
+ take: 1e3
4651
+ });
4652
+ const list = Array.isArray(res) ? res : (res == null ? void 0 : res.data) && Array.isArray(res.data) ? res.data : [];
4653
+ setRelationFilterOptions((prev) => __spreadProps(__spreadValues({}, prev), {
4654
+ [colName]: list
4655
+ }));
4656
+ if (list.length > 0) {
4657
+ mergeRelationOptions(colName, list);
4658
+ }
4659
+ } catch (err) {
4660
+ console.warn(`[TableBlock] Failed to fetch distinct relation filter options for ${colName}:`, err);
4661
+ } finally {
4662
+ setLoadingFilterCol(null);
4663
+ }
4664
+ }, [schemaColsString, schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, executeAction, mergeRelationOptions]);
4665
+ const requestedRelationIdsRef = useRef8({});
4666
+ useEffect10(() => {
4667
+ if (!executeAction || !data || data.length === 0) return;
4668
+ let parsedCols = [];
4669
+ try {
4670
+ parsedCols = JSON.parse(schemaColsString);
4671
+ } catch (e) {
4672
+ }
4673
+ const relationCols = parsedCols.filter(
4674
+ (c) => {
4675
+ var _a;
4676
+ return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "RELATION";
4677
+ }
4678
+ );
4679
+ if (relationCols.length === 0) return;
4680
+ relationCols.forEach(async (col) => {
4681
+ var _a, _b;
4682
+ const referencedTableId = (_a = col.config) == null ? void 0 : _a.referencedTableId;
4683
+ if (!referencedTableId) return;
4684
+ const extractedIds = /* @__PURE__ */ new Set();
4685
+ data.forEach((row) => {
4686
+ if (!(row == null ? void 0 : row.data)) return;
4687
+ const ids = normalizeRelationValue(row.data[col.name]);
4688
+ ids.forEach((id) => {
4689
+ if (/^[0-9a-fA-F]{24}$/.test(id)) {
4690
+ extractedIds.add(id);
4691
+ }
4692
+ });
4693
+ });
4694
+ if (extractedIds.size === 0) return;
4695
+ const existingOptions = relationOptions[col.name] || [];
4696
+ const existingIds = new Set(existingOptions.map((o) => String(o.id)));
4697
+ if (!requestedRelationIdsRef.current[col.name]) {
4698
+ requestedRelationIdsRef.current[col.name] = /* @__PURE__ */ new Set();
4699
+ }
4700
+ const inFlight = requestedRelationIdsRef.current[col.name];
4701
+ const missingIds = Array.from(extractedIds).filter((id) => !existingIds.has(id) && !inFlight.has(id));
4702
+ if (missingIds.length === 0) return;
4703
+ missingIds.forEach((id) => inFlight.add(id));
4704
+ try {
4705
+ const resolved = await executeAction("FETCH_RELATION", {
4706
+ referencedTableId,
4707
+ displayField: (_b = col.config) == null ? void 0 : _b.displayField,
4708
+ recordIds: missingIds
4709
+ });
4710
+ const items = Array.isArray(resolved) ? resolved : (resolved == null ? void 0 : resolved.data) && Array.isArray(resolved.data) ? resolved.data : [];
4711
+ if (items.length > 0) {
4712
+ mergeRelationOptions(col.name, items);
4713
+ }
4714
+ } catch (err) {
4715
+ console.warn(`[TableBlock] Failed to populate relations for ${col.name}:`, err);
4716
+ }
4717
+ });
4718
+ }, [data, schemaColsString, executeAction, relationOptions, mergeRelationOptions]);
4719
+ useEffect10(() => {
3882
4720
  let parsedCols = [];
3883
4721
  try {
3884
4722
  parsedCols = JSON.parse(schemaColsString);
@@ -3892,7 +4730,7 @@ function TableBlockClient({
3892
4730
  refetchRelation(col.name);
3893
4731
  });
3894
4732
  }, [schemaColsString, refetchRelation]);
3895
- const normalizedColumns = useMemo5(() => {
4733
+ const normalizedColumns = useMemo6(() => {
3896
4734
  return (localColumns || []).map((col, idx) => {
3897
4735
  const isRequired = col.isRequired === true || String(col.isRequired) === "true";
3898
4736
  const isUnique = col.isUnique === true || String(col.isUnique) === "true";
@@ -3906,17 +4744,17 @@ function TableBlockClient({
3906
4744
  return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
3907
4745
  });
3908
4746
  }, [localColumns]);
3909
- const filteredColumns = useMemo5(() => {
4747
+ const filteredColumns = useMemo6(() => {
3910
4748
  const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
3911
4749
  return normalizedColumns.filter((col) => !systemFields.includes(col.name) && !col.isHidden);
3912
4750
  }, [normalizedColumns]);
3913
- const customizableColumns = useMemo5(() => {
4751
+ const customizableColumns = useMemo6(() => {
3914
4752
  const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
3915
4753
  return normalizedColumns.filter((col) => !systemFields.includes(col.name));
3916
4754
  }, [normalizedColumns]);
3917
- const [isColumnsDropdownOpen, setIsColumnsDropdownOpen] = useState10(false);
3918
- const dropdownRef = useRef7(null);
3919
- useEffect8(() => {
4755
+ const [isColumnsDropdownOpen, setIsColumnsDropdownOpen] = useState12(false);
4756
+ const dropdownRef = useRef8(null);
4757
+ useEffect10(() => {
3920
4758
  const handleClickOutside = (e) => {
3921
4759
  if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
3922
4760
  setIsColumnsDropdownOpen(false);
@@ -4126,46 +4964,45 @@ function TableBlockClient({
4126
4964
  setData((prev) => [newRow, ...prev]);
4127
4965
  setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
4128
4966
  };
4129
- const handleCellChange = (rowId, colName, value) => {
4967
+ const handleCellChange = useCallback10((rowId, colName, value) => {
4130
4968
  setData((prev) => prev.map(
4131
4969
  (row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
4132
4970
  ));
4133
- const isAdded = dirty.added.some((r) => r.id === rowId);
4134
- if (isAdded) {
4135
- setDirty((prev) => __spreadProps(__spreadValues({}, prev), {
4136
- added: prev.added.map(
4137
- (r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
4138
- )
4139
- }));
4140
- } else {
4141
- const origData = baseRecordsMap.get(rowId) || {};
4142
- const origValue = origData[colName];
4143
- setDirty((prev) => {
4144
- const currentMods = __spreadValues({}, prev.modified[rowId] || {});
4145
- if (isEqualValues(value, origValue)) {
4146
- delete currentMods[colName];
4147
- } else {
4148
- currentMods[colName] = value;
4149
- }
4150
- const nextModified = __spreadValues({}, prev.modified);
4151
- if (Object.keys(currentMods).length === 0) {
4152
- delete nextModified[rowId];
4153
- } else {
4154
- nextModified[rowId] = currentMods;
4155
- }
4971
+ setDirty((prev) => {
4972
+ const isAdded = prev.added.some((r) => r.id === rowId);
4973
+ if (isAdded) {
4156
4974
  return __spreadProps(__spreadValues({}, prev), {
4157
- modified: nextModified
4975
+ added: prev.added.map(
4976
+ (r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
4977
+ )
4158
4978
  });
4979
+ }
4980
+ const origData = baseRecordsMap.get(rowId) || {};
4981
+ const origValue = origData[colName];
4982
+ const currentMods = __spreadValues({}, prev.modified[rowId] || {});
4983
+ if (isEqualValues(value, origValue)) {
4984
+ delete currentMods[colName];
4985
+ } else {
4986
+ currentMods[colName] = value;
4987
+ }
4988
+ const nextModified = __spreadValues({}, prev.modified);
4989
+ if (Object.keys(currentMods).length === 0) {
4990
+ delete nextModified[rowId];
4991
+ } else {
4992
+ nextModified[rowId] = currentMods;
4993
+ }
4994
+ return __spreadProps(__spreadValues({}, prev), {
4995
+ modified: nextModified
4159
4996
  });
4160
- }
4161
- };
4162
- const triggerSaveSuccess = useCallback9(() => {
4997
+ });
4998
+ }, [baseRecordsMap]);
4999
+ const triggerSaveSuccess = useCallback10(() => {
4163
5000
  setIsSaveSuccess(true);
4164
5001
  setTimeout(() => {
4165
5002
  setIsSaveSuccess(false);
4166
5003
  }, 2500);
4167
5004
  }, []);
4168
- const handleQuietCellChange = useCallback9((rowId, colName, value) => {
5005
+ const handleQuietCellChange = useCallback10((rowId, colName, value) => {
4169
5006
  setData((prev) => prev.map(
4170
5007
  (row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
4171
5008
  ));
@@ -4189,22 +5026,28 @@ function TableBlockClient({
4189
5026
  });
4190
5027
  }
4191
5028
  }, []);
4192
- const toggleSelectAll = (checked) => {
4193
- const nextSelected = checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set();
4194
- setSelectedIds(nextSelected);
4195
- if (emitEvent) {
4196
- emitEvent("SELECTION_CHANGE", { selectedIds: Array.from(nextSelected), componentId });
4197
- }
4198
- };
4199
- const toggleSelectRow = (rowId, checked) => {
4200
- const newSelected = new Set(selectedIds);
4201
- if (checked) newSelected.add(rowId);
4202
- else newSelected.delete(rowId);
4203
- setSelectedIds(newSelected);
4204
- if (emitEvent) {
4205
- emitEvent("SELECTION_CHANGE", { selectedIds: Array.from(newSelected), componentId });
4206
- }
4207
- };
5029
+ const toggleSelectAll = useCallback10((checked) => {
5030
+ setSelectedIds(checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set());
5031
+ }, [filteredData]);
5032
+ const toggleSelectRow = useCallback10((rowId, checked) => {
5033
+ setSelectedIds((prev) => {
5034
+ const next = new Set(prev);
5035
+ if (checked) next.add(rowId);
5036
+ else next.delete(rowId);
5037
+ return next;
5038
+ });
5039
+ }, []);
5040
+ const handleToggleExpand = useCallback10((rowId) => {
5041
+ setExpandedRowIds((prev) => {
5042
+ const next = new Set(prev);
5043
+ if (next.has(rowId)) next.delete(rowId);
5044
+ else next.add(rowId);
5045
+ return next;
5046
+ });
5047
+ }, []);
5048
+ const handleMarkForDeletion = useCallback10((fileId) => {
5049
+ setPendingDeletions((prev) => [...prev, fileId]);
5050
+ }, []);
4208
5051
  const handleDeleteSelected = () => {
4209
5052
  const idsToDelete = Array.from(selectedIds);
4210
5053
  if (idsToDelete.length === 0) return;
@@ -4283,9 +5126,6 @@ function TableBlockClient({
4283
5126
  setData(serverRecords.map((r) => __spreadProps(__spreadValues({}, r), { data: __spreadValues({}, r.data) })));
4284
5127
  setSelectedIds(/* @__PURE__ */ new Set());
4285
5128
  setPendingDeletions([]);
4286
- if (emitEvent) {
4287
- emitEvent("SELECTION_CHANGE", { selectedIds: [], componentId });
4288
- }
4289
5129
  };
4290
5130
  const handleSave = async () => {
4291
5131
  var _a, _b;
@@ -4433,7 +5273,7 @@ function TableBlockClient({
4433
5273
  setIsSaving(false);
4434
5274
  }
4435
5275
  };
4436
- const getRowRelationLabels = useCallback9((rowVal, col) => {
5276
+ const getRowRelationLabels = useCallback10((rowVal, col) => {
4437
5277
  const ids = normalizeRelationValue(rowVal);
4438
5278
  if (ids.length === 0) return [];
4439
5279
  const options = relationOptions[col.name] || [];
@@ -4441,19 +5281,23 @@ function TableBlockClient({
4441
5281
  const opt = options.find((o) => o.id === id || o._id === id);
4442
5282
  if (opt) {
4443
5283
  const resolved = resolveRelationLabel(opt, col);
4444
- if (resolved) return resolved;
4445
- if (opt.label) return String(opt.label);
4446
- if (opt.name) return String(opt.name);
5284
+ if (resolved && !isRawObjectId(resolved)) return resolved;
5285
+ if (opt.label && !isRawObjectId(String(opt.label))) return String(opt.label);
5286
+ if (opt.name && !isRawObjectId(String(opt.name))) return String(opt.name);
4447
5287
  }
4448
5288
  if (typeof rowVal === "object" && rowVal !== null) {
4449
- if (rowVal._displayLabel) return String(rowVal._displayLabel);
4450
- if (rowVal.label) return String(rowVal.label);
5289
+ if (rowVal._displayLabel && !isRawObjectId(String(rowVal._displayLabel))) return String(rowVal._displayLabel);
5290
+ if (rowVal.label && !isRawObjectId(String(rowVal.label))) return String(rowVal.label);
4451
5291
  }
4452
- return id;
4453
- });
5292
+ return "";
5293
+ }).filter(Boolean);
4454
5294
  }, [relationOptions]);
4455
- const getUniqueRelationOptions = useCallback9((col) => {
5295
+ const getUniqueRelationOptions = useCallback10((col) => {
5296
+ if (relationFilterOptions[col.name]) {
5297
+ return relationFilterOptions[col.name];
5298
+ }
4456
5299
  const seenIds = /* @__PURE__ */ new Set();
5300
+ const seenLabels = /* @__PURE__ */ new Set();
4457
5301
  const results = [];
4458
5302
  const options = relationOptions[col.name] || [];
4459
5303
  data.forEach((row) => {
@@ -4464,58 +5308,23 @@ function TableBlockClient({
4464
5308
  seenIds.add(id);
4465
5309
  const opt = options.find((o) => o.id === id || o._id === id);
4466
5310
  let label = opt ? resolveRelationLabel(opt, col) : "";
4467
- if (!label && opt) label = opt.label || opt.name || opt.title || id;
4468
- if (!label) label = id;
4469
- results.push({ id, label });
4470
- }
4471
- });
4472
- });
4473
- return results.sort((a, b) => a.label.localeCompare(b.label));
4474
- }, [data, relationOptions]);
4475
- const filteredData = useMemo5(() => {
4476
- const hasGlobalSearch = Boolean(searchQuery && searchQuery.trim() !== "");
4477
- const activeFilters = Object.entries(columnFilters).filter(([, val]) => Boolean(val && val.trim() !== ""));
4478
- const hasColumnFilters = activeFilters.length > 0;
4479
- if (!hasGlobalSearch && !hasColumnFilters) return data;
4480
- const lowerGlobal = hasGlobalSearch ? searchQuery.trim().toLowerCase() : "";
4481
- return data.filter((row) => {
4482
- var _a;
4483
- if (!row || !row.data) return false;
4484
- if (hasGlobalSearch) {
4485
- const matchesGlobal = (localColumns || []).some((col) => {
4486
- var _a2;
4487
- const rawVal = row.data[col.name];
4488
- if (rawVal === void 0 || rawVal === null) return false;
4489
- if (((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION") {
4490
- const labels = getRowRelationLabels(rawVal, col);
4491
- return labels.some((l) => l.toLowerCase().includes(lowerGlobal));
5311
+ if (!label && opt) {
5312
+ const fallback = opt.label || opt.name || opt.title;
5313
+ if (fallback && typeof fallback === "string" && !isRawObjectId(fallback)) {
5314
+ label = fallback.trim();
5315
+ }
4492
5316
  }
4493
- if (typeof rawVal === "object") {
4494
- return JSON.stringify(rawVal).toLowerCase().includes(lowerGlobal);
5317
+ if (label && label.trim() && label !== id && !isRawObjectId(label) && !seenLabels.has(label.trim())) {
5318
+ seenLabels.add(label.trim());
5319
+ results.push({ id, label: label.trim() });
4495
5320
  }
4496
- return String(rawVal).toLowerCase().includes(lowerGlobal);
4497
- });
4498
- if (!matchesGlobal) return false;
4499
- }
4500
- for (const [colName, filterVal] of activeFilters) {
4501
- const lowerFilter = filterVal.trim().toLowerCase();
4502
- const col = (localColumns || []).find((c) => c.name === colName);
4503
- const rawVal = row.data[colName];
4504
- if (((_a = col == null ? void 0 : col.type) == null ? void 0 : _a.toUpperCase()) === "RELATION") {
4505
- const labels = getRowRelationLabels(rawVal, col);
4506
- const matches = labels.some((l) => l.toLowerCase().includes(lowerFilter));
4507
- if (!matches) return false;
4508
- } else {
4509
- if (rawVal === void 0 || rawVal === null) return false;
4510
- const strVal = typeof rawVal === "object" ? JSON.stringify(rawVal).toLowerCase() : String(rawVal).toLowerCase();
4511
- if (!strVal.includes(lowerFilter)) return false;
4512
5321
  }
4513
- }
4514
- return true;
5322
+ });
4515
5323
  });
4516
- }, [data, searchQuery, columnFilters, localColumns, getRowRelationLabels]);
5324
+ return results.sort((a, b) => a.label.localeCompare(b.label));
5325
+ }, [data, relationOptions, relationFilterOptions]);
4517
5326
  const errorMessage = error ? typeof error === "string" ? error : error.message || String(error) : null;
4518
- const columnTotals = useMemo5(() => {
5327
+ const columnTotals = useMemo6(() => {
4519
5328
  const totals = {};
4520
5329
  if (!showSummary) return totals;
4521
5330
  filteredColumns.forEach((col) => {
@@ -4533,42 +5342,120 @@ function TableBlockClient({
4533
5342
  });
4534
5343
  return totals;
4535
5344
  }, [filteredData, filteredColumns, showSummary]);
4536
- return /* @__PURE__ */ jsxs10(
5345
+ const hasSelectedRows = selectedIds.size > 0;
5346
+ const hasActiveSearchOrFilter = Boolean(searchQuery && searchQuery.trim() !== "") || Object.values(columnFilters).some(
5347
+ (v) => Array.isArray(v) ? v.length > 0 : v && typeof v === "object" && "values" in v ? v.values.length > 0 : typeof v === "string" && Boolean(v.trim())
5348
+ );
5349
+ let exportButtonText = "\u05D9\u05D9\u05E6\u05D0 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD";
5350
+ let ExportButtonIcon = Download;
5351
+ if (hasSelectedRows) {
5352
+ exportButtonText = `\u05D9\u05D9\u05E6\u05D0 ${selectedIds.size} \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA`;
5353
+ ExportButtonIcon = Download;
5354
+ } else if (hasActiveSearchOrFilter) {
5355
+ exportButtonText = "\u05D9\u05D9\u05E6\u05D0 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E1\u05D9\u05E0\u05D5\u05DF";
5356
+ ExportButtonIcon = Filter;
5357
+ }
5358
+ const handleExportCsv = async () => {
5359
+ if (isExporting) return;
5360
+ const systemFields = ["_id", "id", "createdAt", "updatedAt", "createdBy", "updatedBy", "orgId", "tableId"];
5361
+ const userHiddenColumns = normalizedColumns.filter((c) => c.isHidden && !systemFields.includes(c.name)).map((c) => c.name);
5362
+ if (userHiddenColumns.length > 0) {
5363
+ addToast({
5364
+ message: "\u05DE\u05D9\u05D9\u05E6\u05D0 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD... \u05E9\u05D9\u05DD \u05DC\u05D1: \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05DE\u05D5\u05E1\u05EA\u05E8\u05D5\u05EA \u05DC\u05D0 \u05D9\u05D9\u05DB\u05DC\u05DC\u05D5 \u05D1\u05E7\u05D5\u05D1\u05E5",
5365
+ type: "info",
5366
+ duration: 3e3
5367
+ });
5368
+ } else {
5369
+ addToast({
5370
+ message: "\u05DE\u05D9\u05D9\u05E6\u05D0 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD...",
5371
+ type: "info",
5372
+ duration: 3e3
5373
+ });
5374
+ }
5375
+ setIsExporting(true);
5376
+ try {
5377
+ const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
5378
+ if (!targetTableId) {
5379
+ showError("\u05DE\u05D6\u05D4\u05D4 \u05D4\u05D8\u05D1\u05DC\u05D4 \u05D7\u05E1\u05E8 (tableId)", "\u05E9\u05D2\u05D9\u05D0\u05EA \u05D9\u05D9\u05E6\u05D5\u05D0");
5380
+ return;
5381
+ }
5382
+ const payload = {
5383
+ tableId: targetTableId,
5384
+ orgId,
5385
+ search: searchQuery.trim() || void 0,
5386
+ columnFilters: Object.keys(columnFilters).length > 0 ? columnFilters : void 0,
5387
+ selectedRowIds: hasSelectedRows ? Array.from(selectedIds) : [],
5388
+ hiddenColumnIds: userHiddenColumns
5389
+ };
5390
+ let res;
5391
+ if (exportAction) {
5392
+ res = await exportAction(payload);
5393
+ } else if (executeAction) {
5394
+ res = await executeAction("EXPORT_TABLE_TO_CSV", payload);
5395
+ } else {
5396
+ throw new Error("\u05E8\u05DB\u05D9\u05D1 \u05D4\u05EA\u05E7\u05E9\u05D5\u05E8\u05EA \u05DC\u05D0 \u05D0\u05D5\u05EA\u05D7\u05DC");
5397
+ }
5398
+ const result = (res == null ? void 0 : res.data) || res;
5399
+ if ((result == null ? void 0 : result.success) === false) {
5400
+ throw new Error(result.error || "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
5401
+ }
5402
+ const csvString = (result == null ? void 0 : result.csvString) || (result == null ? void 0 : result.csv);
5403
+ if (!csvString) {
5404
+ throw new Error("\u05DC\u05D0 \u05D4\u05EA\u05E7\u05D1\u05DC\u05D5 \u05E0\u05EA\u05D5\u05E0\u05D9 CSV \u05DE\u05D4\u05E9\u05E8\u05EA");
5405
+ }
5406
+ const fileName = (result == null ? void 0 : result.fileName) || `${((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name) || "table").trim()}_export.csv`;
5407
+ const blob = new Blob([csvString], { type: "text/csv;charset=utf-8;" });
5408
+ const url = URL.createObjectURL(blob);
5409
+ const link = document.createElement("a");
5410
+ link.href = url;
5411
+ link.setAttribute("download", fileName);
5412
+ document.body.appendChild(link);
5413
+ link.click();
5414
+ document.body.removeChild(link);
5415
+ URL.revokeObjectURL(url);
5416
+ } catch (err) {
5417
+ console.error("[TableBlock] Export to CSV failed:", err);
5418
+ showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
5419
+ } finally {
5420
+ setIsExporting(false);
5421
+ }
5422
+ };
5423
+ return /* @__PURE__ */ jsxs11(
4537
5424
  "div",
4538
5425
  {
4539
5426
  id: componentId,
4540
5427
  "data-testid": dataTestId || (componentId ? `table-block-${componentId}` : void 0),
4541
- className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs min-h-[350px] overflow-hidden ${className}`,
5428
+ className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs flex-1 min-h-0 overflow-hidden ${className}`,
4542
5429
  dir: "rtl",
4543
5430
  children: [
4544
- errorMessage && /* @__PURE__ */ jsx10("div", { className: "p-3 bg-rose-50 border-b border-rose-200 text-rose-700 text-sm flex items-center justify-between", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
4545
- /* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 text-rose-500 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
4546
- /* @__PURE__ */ jsx10("span", { children: errorMessage })
5431
+ errorMessage && /* @__PURE__ */ jsx11("div", { className: "p-3 bg-rose-50 border-b border-rose-200 text-rose-700 text-sm flex items-center justify-between shrink-0", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
5432
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 text-rose-500 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
5433
+ /* @__PURE__ */ jsx11("span", { children: errorMessage })
4547
5434
  ] }) }),
4548
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center justify-between gap-3 p-3.5 sm:p-4 bg-slate-50 border-b border-slate-200", children: [
4549
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2.5", children: [
4550
- ((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ jsx10("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ jsx10("span", { children: schema.title || schema.name }) }),
4551
- /* @__PURE__ */ jsx10(
5435
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center justify-between gap-3 p-3.5 sm:p-4 bg-slate-50 border-b border-slate-200 shrink-0", children: [
5436
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2.5", children: [
5437
+ ((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ jsx11("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ jsx11("span", { children: schema.title || schema.name }) }),
5438
+ /* @__PURE__ */ jsx11(
4552
5439
  "button",
4553
5440
  {
4554
5441
  onClick: handleAddRow,
4555
5442
  disabled: isInitialLoading || isLoading || isUpdatingActive,
4556
5443
  className: `w-9 h-9 text-white rounded-lg shadow-xs flex items-center justify-center transition-all ${isInitialLoading || isLoading || isUpdatingActive ? "bg-emerald-950/80 text-emerald-400/60 pointer-events-none cursor-not-allowed" : "bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 cursor-pointer"}`,
4557
5444
  title: isInitialLoading ? "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." : "\u05D4\u05D5\u05E1\u05E3 \u05E9\u05D5\u05E8\u05D4 \u05D7\u05D3\u05E9\u05D4",
4558
- children: /* @__PURE__ */ jsx10("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
5445
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
4559
5446
  }
4560
5447
  ),
4561
- /* @__PURE__ */ jsx10(
5448
+ /* @__PURE__ */ jsx11(
4562
5449
  "button",
4563
5450
  {
4564
5451
  onClick: handleDuplicateRow,
4565
5452
  disabled: isInitialLoading || isLoading || isUpdatingActive || selectedIds.size !== 1,
4566
5453
  className: "w-9 h-9 bg-blue-50 hover:bg-blue-100 border border-blue-200 text-blue-700 rounded-lg shadow-2xs flex items-center justify-center transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
4567
5454
  title: "\u05E9\u05DB\u05E4\u05DC \u05E9\u05D5\u05E8\u05D4 \u05E0\u05D1\u05D7\u05E8\u05EA",
4568
- children: /* @__PURE__ */ jsx10(Copy2, { className: "w-4 h-4 text-blue-600 shrink-0" })
5455
+ children: /* @__PURE__ */ jsx11(Copy2, { className: "w-4 h-4 text-blue-600 shrink-0" })
4569
5456
  }
4570
5457
  ),
4571
- /* @__PURE__ */ jsxs10(
5458
+ /* @__PURE__ */ jsxs11(
4572
5459
  "button",
4573
5460
  {
4574
5461
  onClick: handleDeleteSelected,
@@ -4576,12 +5463,12 @@ function TableBlockClient({
4576
5463
  className: "w-9 h-9 bg-rose-50 hover:bg-rose-100 border border-rose-200 text-rose-700 rounded-lg shadow-2xs flex items-center justify-center relative transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
4577
5464
  title: "\u05DE\u05D7\u05E7 \u05E9\u05D5\u05E8\u05D5\u05EA \u05E0\u05D1\u05D7\u05E8\u05D5\u05EA",
4578
5465
  children: [
4579
- /* @__PURE__ */ jsx10("svg", { className: "w-4.5 h-4.5 text-rose-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
4580
- selectedIds.size > 0 && /* @__PURE__ */ jsx10("span", { className: "absolute -top-1.5 -right-1.5 bg-rose-600 text-white min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold flex items-center justify-center shadow-xs", children: selectedIds.size })
5466
+ /* @__PURE__ */ jsx11("svg", { className: "w-4.5 h-4.5 text-rose-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
5467
+ selectedIds.size > 0 && /* @__PURE__ */ jsx11("span", { className: "absolute -top-1.5 -right-1.5 bg-rose-600 text-white min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold flex items-center justify-center shadow-xs", children: selectedIds.size })
4581
5468
  ]
4582
5469
  }
4583
5470
  ),
4584
- /* @__PURE__ */ jsxs10(
5471
+ /* @__PURE__ */ jsxs11(
4585
5472
  "button",
4586
5473
  {
4587
5474
  onClick: toggleSummary,
@@ -4589,13 +5476,13 @@ function TableBlockClient({
4589
5476
  className: `h-9 px-3 border font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer ${showSummary ? "bg-indigo-50 border-indigo-200 text-indigo-700 hover:bg-indigo-100" : "bg-white border-slate-300 text-slate-700 hover:bg-slate-100"}`,
4590
5477
  title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E9\u05D5\u05E8\u05EA \u05E1\u05D9\u05DB\u05D5\u05DD",
4591
5478
  children: [
4592
- /* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) }),
4593
- /* @__PURE__ */ jsx10("span", { className: "hidden sm:inline", children: "\u05E1\u05DB\u05D9\u05DE\u05D4" })
5479
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) }),
5480
+ /* @__PURE__ */ jsx11("span", { className: "hidden sm:inline", children: "\u05E1\u05DB\u05D9\u05DE\u05D4" })
4594
5481
  ]
4595
5482
  }
4596
5483
  ),
4597
- /* @__PURE__ */ jsxs10("div", { className: "relative", ref: dropdownRef, children: [
4598
- /* @__PURE__ */ jsxs10(
5484
+ /* @__PURE__ */ jsxs11("div", { className: "relative", ref: dropdownRef, children: [
5485
+ /* @__PURE__ */ jsxs11(
4599
5486
  "button",
4600
5487
  {
4601
5488
  onClick: () => setIsColumnsDropdownOpen((prev) => !prev),
@@ -4603,16 +5490,16 @@ function TableBlockClient({
4603
5490
  className: "h-9 px-3 bg-white hover:bg-slate-100 border border-slate-300 text-slate-700 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
4604
5491
  title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA",
4605
5492
  children: [
4606
- /* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 text-slate-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" }) }),
4607
- /* @__PURE__ */ jsx10("svg", { className: `w-3.5 h-3.5 text-slate-500 transition-transform ${isColumnsDropdownOpen ? "rotate-180" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 9l-7 7-7-7" }) })
5493
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 text-slate-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" }) }),
5494
+ /* @__PURE__ */ jsx11("svg", { className: `w-3.5 h-3.5 text-slate-500 transition-transform ${isColumnsDropdownOpen ? "rotate-180" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 9l-7 7-7-7" }) })
4608
5495
  ]
4609
5496
  }
4610
5497
  ),
4611
- isColumnsDropdownOpen && /* @__PURE__ */ jsxs10("div", { className: "absolute right-0 top-full mt-1.5 w-56 bg-white border border-slate-200 rounded-xl shadow-xl z-50 p-1.5 dir-rtl animate-in fade-in slide-in-from-top-2 duration-150", children: [
4612
- /* @__PURE__ */ jsxs10("div", { className: "px-2 py-1.5 text-xs font-bold text-slate-500 border-b border-slate-100 mb-1 flex items-center justify-between", children: [
4613
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5", children: [
4614
- /* @__PURE__ */ jsx10("span", { children: "\u05D4\u05E6\u05D2\u05EA \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA" }),
4615
- /* @__PURE__ */ jsxs10("span", { className: "text-[10px] font-normal text-slate-400", children: [
5498
+ isColumnsDropdownOpen && /* @__PURE__ */ jsxs11("div", { className: "absolute right-0 top-full mt-1.5 w-56 bg-white border border-slate-200 rounded-xl shadow-xl z-50 p-1.5 dir-rtl animate-in fade-in slide-in-from-top-2 duration-150", children: [
5499
+ /* @__PURE__ */ jsxs11("div", { className: "px-2 py-1.5 text-xs font-bold text-slate-500 border-b border-slate-100 mb-1 flex items-center justify-between", children: [
5500
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5", children: [
5501
+ /* @__PURE__ */ jsx11("span", { children: "\u05D4\u05E6\u05D2\u05EA \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA" }),
5502
+ /* @__PURE__ */ jsxs11("span", { className: "text-[10px] font-normal text-slate-400", children: [
4616
5503
  "(",
4617
5504
  customizableColumns.filter((c) => !c.isHidden).length,
4618
5505
  "/",
@@ -4620,7 +5507,7 @@ function TableBlockClient({
4620
5507
  ")"
4621
5508
  ] })
4622
5509
  ] }),
4623
- /* @__PURE__ */ jsx10(
5510
+ /* @__PURE__ */ jsx11(
4624
5511
  "button",
4625
5512
  {
4626
5513
  type: "button",
@@ -4630,7 +5517,7 @@ function TableBlockClient({
4630
5517
  }
4631
5518
  )
4632
5519
  ] }),
4633
- /* @__PURE__ */ jsx10("div", { className: "max-h-60 overflow-y-auto space-y-0.5", children: customizableColumns.map((col) => {
5520
+ /* @__PURE__ */ jsx11("div", { className: "max-h-60 overflow-y-auto space-y-0.5", children: customizableColumns.map((col) => {
4634
5521
  const isRequired = col.isRequired === true || String(col.isRequired) === "true";
4635
5522
  const isUnique = col.isUnique === true || String(col.isUnique) === "true";
4636
5523
  const isProtected = isRequired || isUnique;
@@ -4644,7 +5531,7 @@ function TableBlockClient({
4644
5531
  protectedExplanation = "\u05E9\u05D3\u05D4 \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9 - \u05DC\u05D0 \u05E0\u05D9\u05EA\u05DF \u05DC\u05D4\u05D5\u05E8\u05D9\u05D3 \u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5 \u05DE\u05D4\u05EA\u05E6\u05D5\u05D2\u05D4";
4645
5532
  }
4646
5533
  const badgeLabel = isRequired && isUnique ? "\u05D7\u05D5\u05D1\u05D4 + \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9" : isRequired ? "\u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4" : "\u05E9\u05D3\u05D4 \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9";
4647
- return /* @__PURE__ */ jsxs10(
5534
+ return /* @__PURE__ */ jsxs11(
4648
5535
  "div",
4649
5536
  {
4650
5537
  onClick: () => {
@@ -4655,8 +5542,8 @@ function TableBlockClient({
4655
5542
  title: isProtected ? protectedExplanation : void 0,
4656
5543
  className: `group flex items-center justify-between px-2.5 py-1.5 rounded-lg text-xs transition-colors select-none ${isProtected ? "opacity-75 bg-slate-50 border border-slate-100/70 cursor-not-allowed" : "hover:bg-slate-100 active:bg-slate-200 cursor-pointer"}`,
4657
5544
  children: [
4658
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 min-w-0", children: [
4659
- /* @__PURE__ */ jsx10(
5545
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 min-w-0", children: [
5546
+ /* @__PURE__ */ jsx11(
4660
5547
  "input",
4661
5548
  {
4662
5549
  type: "checkbox",
@@ -4667,16 +5554,16 @@ function TableBlockClient({
4667
5554
  className: "w-3.5 h-3.5 rounded border-slate-300 text-brand focus:ring-brand/20 cursor-pointer disabled:cursor-not-allowed disabled:opacity-60"
4668
5555
  }
4669
5556
  ),
4670
- /* @__PURE__ */ jsx10("span", { className: `font-medium truncate ${isProtected ? "text-slate-500" : "text-slate-700"}`, children: col.name })
5557
+ /* @__PURE__ */ jsx11("span", { className: `font-medium truncate ${isProtected ? "text-slate-500" : "text-slate-700"}`, children: col.name })
4671
5558
  ] }),
4672
- isProtected && /* @__PURE__ */ jsxs10(
5559
+ isProtected && /* @__PURE__ */ jsxs11(
4673
5560
  "div",
4674
5561
  {
4675
5562
  className: "flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-semibold bg-amber-50 text-amber-800 border border-amber-200/80 cursor-help",
4676
5563
  title: protectedExplanation,
4677
5564
  children: [
4678
- /* @__PURE__ */ jsx10("svg", { className: "w-3 h-3 text-amber-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: "2.2", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" }) }),
4679
- /* @__PURE__ */ jsx10("span", { children: badgeLabel })
5565
+ /* @__PURE__ */ jsx11("svg", { className: "w-3 h-3 text-amber-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: "2.2", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" }) }),
5566
+ /* @__PURE__ */ jsx11("span", { children: badgeLabel })
4680
5567
  ]
4681
5568
  }
4682
5569
  )
@@ -4687,9 +5574,23 @@ function TableBlockClient({
4687
5574
  }) })
4688
5575
  ] })
4689
5576
  ] }),
4690
- /* @__PURE__ */ jsx10("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
4691
- /* @__PURE__ */ jsxs10("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
4692
- /* @__PURE__ */ jsx10(
5577
+ /* @__PURE__ */ jsxs11(
5578
+ "button",
5579
+ {
5580
+ onClick: handleExportCsv,
5581
+ disabled: isLoading || isUpdatingActive || isExporting,
5582
+ className: `h-9 px-3 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer border ${hasSelectedRows ? "bg-blue-50 hover:bg-blue-100 border-blue-200 text-blue-700" : hasActiveSearchOrFilter ? "bg-amber-50 hover:bg-amber-100 border-amber-200 text-amber-800" : "bg-white hover:bg-slate-100 border-slate-300 text-slate-700"}`,
5583
+ title: exportButtonText,
5584
+ "data-testid": "export-csv-button",
5585
+ children: [
5586
+ isExporting ? /* @__PURE__ */ jsx11(Loader22, { className: "w-4 h-4 shrink-0 animate-spin text-slate-500" }) : /* @__PURE__ */ jsx11(ExportButtonIcon, { className: "w-4 h-4 shrink-0" }),
5587
+ /* @__PURE__ */ jsx11("span", { children: isExporting ? "\u05DE\u05D9\u05D9\u05E6\u05D0..." : exportButtonText })
5588
+ ]
5589
+ }
5590
+ ),
5591
+ /* @__PURE__ */ jsx11("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
5592
+ /* @__PURE__ */ jsxs11("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
5593
+ /* @__PURE__ */ jsx11(
4693
5594
  "input",
4694
5595
  {
4695
5596
  placeholder: "\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA...",
@@ -4699,28 +5600,28 @@ function TableBlockClient({
4699
5600
  className: "w-full h-9 px-3 pr-9 text-xs border border-slate-300 rounded-lg bg-white shadow-2xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 text-slate-800 disabled:opacity-50"
4700
5601
  }
4701
5602
  ),
4702
- searchQuery ? /* @__PURE__ */ jsx10(
5603
+ searchQuery ? /* @__PURE__ */ jsx11(
4703
5604
  "button",
4704
5605
  {
4705
5606
  type: "button",
4706
5607
  onClick: () => setSearchQuery(""),
4707
5608
  className: "absolute right-2.5 top-2.5 text-slate-400 hover:text-slate-600 transition-colors p-0.5 rounded-full hover:bg-slate-100 cursor-pointer",
4708
5609
  title: "Clear search",
4709
- children: /* @__PURE__ */ jsx10(X7, { className: "w-3.5 h-3.5" })
5610
+ children: /* @__PURE__ */ jsx11(X8, { className: "w-3.5 h-3.5" })
4710
5611
  }
4711
- ) : /* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 text-slate-400 absolute right-2.5 top-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) })
5612
+ ) : /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 text-slate-400 absolute right-2.5 top-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) })
4712
5613
  ] })
4713
5614
  ] }),
4714
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-3", children: [
4715
- isUpdatingActive ? /* @__PURE__ */ jsxs10("span", { className: "text-xs text-blue-600 font-semibold flex items-center gap-1.5 bg-blue-50 border border-blue-200 px-3 py-1.5 rounded-lg animate-pulse", children: [
4716
- /* @__PURE__ */ jsx10("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
5615
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3", children: [
5616
+ isUpdatingActive ? /* @__PURE__ */ jsxs11("span", { className: "text-xs text-blue-600 font-semibold flex items-center gap-1.5 bg-blue-50 border border-blue-200 px-3 py-1.5 rounded-lg animate-pulse", children: [
5617
+ /* @__PURE__ */ jsx11("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
4717
5618
  "\u05DE\u05E2\u05D3\u05DB\u05DF..."
4718
- ] }) : isSaveSuccess ? /* @__PURE__ */ jsxs10("span", { className: "text-xs text-emerald-700 font-semibold flex items-center gap-1.5 bg-emerald-50 border border-emerald-200 px-3 py-1.5 rounded-lg animate-in fade-in duration-200", children: [
4719
- /* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
5619
+ ] }) : isSaveSuccess ? /* @__PURE__ */ jsxs11("span", { className: "text-xs text-emerald-700 font-semibold flex items-center gap-1.5 bg-emerald-50 border border-emerald-200 px-3 py-1.5 rounded-lg animate-in fade-in duration-200", children: [
5620
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
4720
5621
  "\u05E2\u05D5\u05D3\u05DB\u05DF \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4"
4721
- ] }) : hasChanges ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 animate-in fade-in", children: [
4722
- /* @__PURE__ */ jsx10("span", { className: "text-xs font-semibold text-amber-700", children: "\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5" }),
4723
- /* @__PURE__ */ jsx10(
5622
+ ] }) : hasChanges ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 animate-in fade-in", children: [
5623
+ /* @__PURE__ */ jsx11("span", { className: "text-xs font-semibold text-amber-700", children: "\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5" }),
5624
+ /* @__PURE__ */ jsx11(
4724
5625
  "button",
4725
5626
  {
4726
5627
  type: "button",
@@ -4729,30 +5630,31 @@ function TableBlockClient({
4729
5630
  className: "p-1 text-amber-700 hover:text-amber-900 hover:bg-amber-100/80 active:bg-amber-200/80 rounded-md transition-colors cursor-pointer disabled:opacity-50 disabled:pointer-events-none",
4730
5631
  title: "\u05D1\u05D8\u05DC \u05D0\u05EA \u05DB\u05DC \u05D4\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5",
4731
5632
  "aria-label": "\u05D1\u05D8\u05DC \u05D0\u05EA \u05DB\u05DC \u05D4\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5",
4732
- children: /* @__PURE__ */ jsx10(RotateCcw, { className: "w-3.5 h-3.5" })
5633
+ children: /* @__PURE__ */ jsx11(RotateCcw2, { className: "w-3.5 h-3.5" })
4733
5634
  }
4734
5635
  )
4735
5636
  ] }) : null,
4736
- /* @__PURE__ */ jsx10(
5637
+ /* @__PURE__ */ jsx11(
4737
5638
  "button",
4738
5639
  {
4739
5640
  onClick: handleSave,
4740
5641
  disabled: isUpdatingActive || isLoading || !hasChanges,
4741
5642
  className: `h-9 px-4 text-xs font-bold rounded-lg shadow-2xs transition-all flex items-center justify-center cursor-pointer ${hasChanges && !isUpdatingActive && !isLoading ? "bg-brand hover:bg-brand-hover active:bg-[#00897b] text-white shadow-teal-500/20" : "opacity-50 grayscale bg-slate-200 text-slate-400 cursor-not-allowed"}`,
4742
- children: /* @__PURE__ */ jsx10("span", { children: isUpdatingActive ? "\u05E9\u05D5\u05DE\u05E8..." : "\u05E9\u05DE\u05D9\u05E8\u05EA \u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD" })
5643
+ children: /* @__PURE__ */ jsx11("span", { children: isUpdatingActive ? "\u05E9\u05D5\u05DE\u05E8..." : "\u05E9\u05DE\u05D9\u05E8\u05EA \u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD" })
4743
5644
  }
4744
5645
  )
4745
5646
  ] })
4746
5647
  ] }),
4747
- /* @__PURE__ */ jsxs10("div", { className: `overflow-x-auto relative min-h-[260px] flex-1 transition-opacity duration-200 ${isMounted ? "opacity-100" : "opacity-0"}`, children: [
4748
- isLoading && /* @__PURE__ */ jsxs10("div", { className: "absolute inset-0 bg-white/80 backdrop-blur-xs z-20 flex flex-col items-center justify-center gap-2", children: [
4749
- /* @__PURE__ */ jsx10("div", { className: "w-6 h-6 border-2 border-brand border-t-transparent rounded-full animate-spin" }),
4750
- /* @__PURE__ */ jsx10("span", { className: "text-sm font-medium text-slate-600", children: "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." })
5648
+ /* @__PURE__ */ jsxs11("div", { className: `overflow-x-auto overflow-y-auto relative max-h-[calc(100vh-240px)] min-h-[260px] flex-1 transition-opacity duration-200 ${isMounted ? "opacity-100" : "opacity-0"}`, children: [
5649
+ isFetching && /* @__PURE__ */ jsx11("div", { className: "absolute top-0 left-0 right-0 h-0.5 bg-blue-500/20 overflow-hidden z-30", children: /* @__PURE__ */ jsx11("div", { className: "w-full h-full bg-brand animate-pulse" }) }),
5650
+ isLoading && /* @__PURE__ */ jsxs11("div", { className: "absolute inset-0 bg-white/80 backdrop-blur-xs z-20 flex flex-col items-center justify-center gap-2", children: [
5651
+ /* @__PURE__ */ jsx11("div", { className: "w-6 h-6 border-2 border-brand border-t-transparent rounded-full animate-spin" }),
5652
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-slate-600", children: "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." })
4751
5653
  ] }),
4752
- /* @__PURE__ */ jsxs10("table", { className: "w-full text-sm text-right border-collapse table-auto", children: [
4753
- /* @__PURE__ */ jsx10("thead", { className: "bg-slate-100/80 text-slate-700 sticky top-0 z-10", children: /* @__PURE__ */ jsxs10("tr", { className: "h-12 min-h-[48px]", children: [
4754
- /* @__PURE__ */ jsx10("th", { className: "border-b border-l border-slate-200 p-1.5 text-center shrink-0 h-12 min-h-[48px] align-middle", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-center gap-1.5", children: [
4755
- /* @__PURE__ */ jsx10(
5654
+ /* @__PURE__ */ jsxs11("table", { className: "w-full text-sm text-right border-collapse table-auto", children: [
5655
+ /* @__PURE__ */ jsx11("thead", { className: "bg-white text-slate-700 sticky top-0 z-10 shadow-xs", children: /* @__PURE__ */ jsxs11("tr", { className: "h-12 min-h-[48px]", children: [
5656
+ /* @__PURE__ */ jsx11("th", { className: "border-b border-l border-slate-200 p-1.5 text-center shrink-0 h-12 min-h-[48px] align-middle bg-white", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-center gap-1.5", children: [
5657
+ /* @__PURE__ */ jsx11(
4756
5658
  "input",
4757
5659
  {
4758
5660
  type: "checkbox",
@@ -4765,7 +5667,7 @@ function TableBlockClient({
4765
5667
  filteredColumns.some((c) => {
4766
5668
  var _a;
4767
5669
  return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
4768
- }) && /* @__PURE__ */ jsx10("div", { className: "w-5 h-5 shrink-0" })
5670
+ }) && /* @__PURE__ */ jsx11("div", { className: "w-5 h-5 shrink-0" })
4769
5671
  ] }) }),
4770
5672
  filteredColumns.map((col) => {
4771
5673
  var _a, _b;
@@ -4776,16 +5678,25 @@ function TableBlockClient({
4776
5678
  const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
4777
5679
  const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
4778
5680
  const isRelation = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) === "RELATION";
4779
- const filterVal = columnFilters[col.name] || "";
4780
- return /* @__PURE__ */ jsxs10(
5681
+ const filterVal = columnFilters[col.name];
5682
+ const hasActiveFilter = Array.isArray(filterVal) ? filterVal.length > 0 : filterVal && typeof filterVal === "object" && "values" in filterVal ? filterVal.values.length > 0 : Boolean(filterVal && filterVal.trim() !== "");
5683
+ let filterTooltip = "\u05D7\u05E4\u05E9 \u05D5\u05E1\u05E0\u05DF \u05D1\u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5";
5684
+ if (Array.isArray(filterVal)) {
5685
+ filterTooltip = `\u05DE\u05E1\u05D5\u05E0\u05DF \u05DC\u05E4\u05D9: ${filterVal.length} \u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05E0\u05D1\u05D7\u05E8\u05D5\u05EA`;
5686
+ } else if (filterVal && typeof filterVal === "object" && "operator" in filterVal && filterVal.operator === "notIn") {
5687
+ filterTooltip = `\u05DE\u05D5\u05D7\u05E8\u05D2\u05D9\u05DD: ${filterVal.values.length} \u05E4\u05E8\u05D9\u05D8\u05D9\u05DD (\u05D4\u05DB\u05DC \u05DC\u05DE\u05E2\u05D8)`;
5688
+ } else if (typeof filterVal === "string" && filterVal.trim()) {
5689
+ filterTooltip = `\u05DE\u05E1\u05D5\u05E0\u05DF \u05DC\u05E4\u05D9: "${filterVal}" (\u05DC\u05D7\u05E5 \u05DC\u05E2\u05E8\u05D9\u05DB\u05D4)`;
5690
+ }
5691
+ return /* @__PURE__ */ jsxs11(
4781
5692
  "th",
4782
5693
  __spreadProps(__spreadValues({
4783
5694
  style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
4784
5695
  title: isSearching ? void 0 : col.name
4785
5696
  }, dragProps), {
4786
- className: `relative border-b border-l border-slate-200 px-2.5 py-1.5 font-semibold text-slate-700 select-none transition-colors h-12 min-h-[48px] align-middle ${isSearching ? "cursor-default bg-blue-50/40" : "cursor-grab active:cursor-grabbing hover:bg-slate-200/60"} ${isDragging ? "opacity-40 bg-slate-200 border-dashed border-2 border-brand" : ""} ${isDragOver ? "bg-blue-50 border-r-4 border-r-brand text-brand font-bold" : ""}`,
5697
+ className: `group relative border-b border-l border-slate-200 px-2 py-1 font-semibold text-slate-700 select-none transition-colors h-12 min-h-[48px] align-middle bg-white ${isSearching ? "cursor-default bg-blue-50/40" : "cursor-grab active:cursor-grabbing hover:bg-slate-100"} ${isDragging ? "opacity-40 bg-slate-200 border-dashed border-2 border-brand" : ""} ${isDragOver ? "bg-blue-50 border-r-4 border-r-brand text-brand font-bold" : ""}`,
4787
5698
  children: [
4788
- /* @__PURE__ */ jsx10(
5699
+ /* @__PURE__ */ jsx11(
4789
5700
  "div",
4790
5701
  {
4791
5702
  onMouseDown: (e) => {
@@ -4797,7 +5708,7 @@ function TableBlockClient({
4797
5708
  title: "\u05E9\u05E0\u05D4 \u05D2\u05D5\u05D3\u05DC \u05E2\u05DE\u05D5\u05D3\u05D4"
4798
5709
  }
4799
5710
  ),
4800
- isSearching ? /* @__PURE__ */ jsxs10(
5711
+ isSearching && !isRelation ? /* @__PURE__ */ jsxs11(
4801
5712
  "div",
4802
5713
  {
4803
5714
  ref: activeSearchRef,
@@ -4805,46 +5716,15 @@ function TableBlockClient({
4805
5716
  onClick: (e) => e.stopPropagation(),
4806
5717
  onMouseDown: (e) => e.stopPropagation(),
4807
5718
  children: [
4808
- isRelation ? /* @__PURE__ */ jsx10("div", { className: "relative flex items-center w-full min-w-0", children: /* @__PURE__ */ jsxs10(
4809
- "select",
4810
- {
4811
- autoFocus: true,
4812
- value: filterVal,
4813
- onChange: (e) => {
4814
- const val = e.target.value;
4815
- setColumnFilters((prev) => {
4816
- const next = __spreadValues({}, prev);
4817
- if (val) next[col.name] = val;
4818
- else delete next[col.name];
4819
- return next;
4820
- });
4821
- },
4822
- onKeyDown: (e) => {
4823
- if (e.key === "Escape" || e.key === "Enter") {
4824
- setActiveSearchCol(null);
4825
- }
4826
- },
4827
- className: "h-7 text-xs px-1.5 w-full rounded border border-blue-400 bg-white text-slate-800 focus:outline-none focus:ring-1 focus:ring-blue-400 cursor-pointer truncate",
4828
- children: [
4829
- /* @__PURE__ */ jsx10("option", { value: "", children: "\u05D4\u05DB\u05DC" }),
4830
- getUniqueRelationOptions(col).map((opt) => /* @__PURE__ */ jsx10("option", { value: opt.label, children: opt.label }, opt.id))
4831
- ]
4832
- }
4833
- ) }) : /* @__PURE__ */ jsx10("div", { className: "relative flex items-center w-full min-w-0", children: /* @__PURE__ */ jsx10(
5719
+ /* @__PURE__ */ jsx11("div", { className: "relative flex items-center w-full min-w-0", children: /* @__PURE__ */ jsx11(
4834
5720
  "input",
4835
5721
  {
4836
5722
  autoFocus: true,
4837
5723
  type: "text",
4838
5724
  placeholder: `\u05D7\u05E4\u05E9 \u05D1-${col.name}...`,
4839
- value: filterVal,
5725
+ value: typeof filterVal === "string" ? filterVal : "",
4840
5726
  onChange: (e) => {
4841
- const val = e.target.value;
4842
- setColumnFilters((prev) => {
4843
- const next = __spreadValues({}, prev);
4844
- if (val) next[col.name] = val;
4845
- else delete next[col.name];
4846
- return next;
4847
- });
5727
+ handleApplyColumnFilter(col.name, e.target.value);
4848
5728
  },
4849
5729
  onKeyDown: (e) => {
4850
5730
  if (e.key === "Escape" || e.key === "Enter") {
@@ -4854,7 +5734,7 @@ function TableBlockClient({
4854
5734
  className: "h-7 text-xs px-2 w-full rounded border border-blue-400 bg-white text-slate-800 focus:outline-none focus:ring-1 focus:ring-blue-400"
4855
5735
  }
4856
5736
  ) }),
4857
- /* @__PURE__ */ jsx10(
5737
+ /* @__PURE__ */ jsx11(
4858
5738
  "button",
4859
5739
  {
4860
5740
  type: "button",
@@ -4864,59 +5744,60 @@ function TableBlockClient({
4864
5744
  },
4865
5745
  onClick: (e) => {
4866
5746
  e.stopPropagation();
4867
- setColumnFilters((prev) => {
4868
- const next = __spreadValues({}, prev);
4869
- delete next[col.name];
4870
- return next;
4871
- });
5747
+ handleApplyColumnFilter(col.name, void 0);
4872
5748
  setActiveSearchCol(null);
4873
5749
  },
4874
5750
  className: "p-1 text-slate-400 hover:text-slate-700 hover:bg-slate-200/70 rounded transition-colors cursor-pointer shrink-0",
4875
5751
  title: "\u05E0\u05E7\u05D4 \u05D5\u05E1\u05D2\u05D5\u05E8 \u05D7\u05D9\u05E4\u05D5\u05E9",
4876
- children: /* @__PURE__ */ jsx10(X7, { className: "w-3.5 h-3.5" })
5752
+ children: /* @__PURE__ */ jsx11(X8, { className: "w-3.5 h-3.5" })
4877
5753
  }
4878
5754
  )
4879
5755
  ]
4880
5756
  }
4881
- ) : /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-1.5 group overflow-hidden w-full", title: col.name, children: [
4882
- /* @__PURE__ */ jsx10("div", { className: "flex items-center gap-1.5 min-w-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx10(ColumnHeaderTitle, { name: col.name, width: currentWidth, isSubTable: false }) }),
4883
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 shrink-0", children: [
4884
- ((_b = col.type) == null ? void 0 : _b.toUpperCase()) !== "SUB_TABLE" && /* @__PURE__ */ jsx10(
4885
- "button",
4886
- {
4887
- type: "button",
4888
- onClick: (e) => {
4889
- e.stopPropagation();
4890
- e.preventDefault();
5757
+ ) : /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center justify-center w-full h-full min-w-0 select-none py-0.5", title: col.name, children: [
5758
+ ((_b = col.type) == null ? void 0 : _b.toUpperCase()) !== "SUB_TABLE" && /* @__PURE__ */ jsx11(
5759
+ "button",
5760
+ {
5761
+ ref: (el) => {
5762
+ filterButtonRefs.current[col.name] = el;
5763
+ },
5764
+ type: "button",
5765
+ onClick: (e) => {
5766
+ e.stopPropagation();
5767
+ e.preventDefault();
5768
+ if (isRelation) {
5769
+ if (activeRelationCol === col.name) {
5770
+ setActiveRelationCol(null);
5771
+ setRelationTriggerRect(null);
5772
+ } else {
5773
+ const btn = filterButtonRefs.current[col.name];
5774
+ if (btn) {
5775
+ setRelationTriggerRect(btn.getBoundingClientRect());
5776
+ }
5777
+ setActiveRelationCol(col.name);
5778
+ fetchRelationFilterOptions(col.name);
5779
+ }
5780
+ } else {
4891
5781
  setActiveSearchCol(col.name);
4892
- },
4893
- className: `p-1 rounded transition-all cursor-pointer ${filterVal ? "opacity-100 text-blue-600 bg-blue-100 hover:bg-blue-200" : "opacity-0 group-hover:opacity-100 text-slate-400 hover:text-slate-600 hover:bg-slate-200/60"}`,
4894
- title: filterVal ? `\u05DE\u05E1\u05D5\u05E0\u05DF \u05DC\u05E4\u05D9: "${filterVal}" (\u05DC\u05D7\u05E5 \u05DC\u05E2\u05E8\u05D9\u05DB\u05D4)` : "\u05D7\u05E4\u05E9 \u05D5\u05E1\u05E0\u05DF \u05D1\u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5",
4895
- children: /* @__PURE__ */ jsx10(Search2, { className: "w-3.5 h-3.5" })
4896
- }
4897
- ),
4898
- /* @__PURE__ */ jsx10(
4899
- "svg",
4900
- {
4901
- className: "w-3.5 h-3.5 text-slate-400 opacity-40 group-hover:opacity-100 transition-opacity shrink-0 cursor-grab",
4902
- fill: "none",
4903
- viewBox: "0 0 24 24",
4904
- stroke: "currentColor",
4905
- children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M8 9h.01M8 15h.01M16 9h.01M16 15h.01" })
4906
- }
4907
- )
4908
- ] })
5782
+ }
5783
+ },
5784
+ className: `p-0.5 rounded transition-colors cursor-pointer mb-0.5 shrink-0 ${hasActiveFilter ? "opacity-100 text-blue-600 bg-blue-100 hover:bg-blue-200" : activeRelationCol === col.name ? "opacity-100 text-blue-600 bg-blue-100" : "opacity-0 group-hover:opacity-100 text-slate-400 hover:text-slate-600 hover:bg-slate-200/60"}`,
5785
+ title: filterTooltip,
5786
+ children: /* @__PURE__ */ jsx11(Filter, { className: "w-3 h-3" })
5787
+ }
5788
+ ),
5789
+ /* @__PURE__ */ jsx11(ColumnHeaderTitle, { name: col.name, width: currentWidth, isSubTable: false })
4909
5790
  ] })
4910
5791
  ]
4911
5792
  }),
4912
5793
  col.name
4913
5794
  );
4914
5795
  }),
4915
- /* @__PURE__ */ jsx10("th", { className: "w-full min-w-[50px] border-b border-slate-200 pointer-events-none h-12 min-h-[48px]" })
5796
+ /* @__PURE__ */ jsx11("th", { className: "w-full min-w-[50px] border-b border-slate-200 pointer-events-none h-12 min-h-[48px] bg-white" })
4916
5797
  ] }) }),
4917
- /* @__PURE__ */ jsx10("tbody", { className: "bg-white", children: (isStateEmpty || filteredData.length === 0) && !isLoading ? /* @__PURE__ */ jsx10("tr", { children: /* @__PURE__ */ jsx10("td", { colSpan: filteredColumns.length + 2, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center justify-center gap-2", children: [
4918
- /* @__PURE__ */ jsx10("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", d: "M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" }) }),
4919
- /* @__PURE__ */ jsx10("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
5798
+ /* @__PURE__ */ jsx11("tbody", { className: `bg-white transition-opacity duration-150 ${isFetching ? "opacity-50 pointer-events-none" : "opacity-100"}`, children: (isStateEmpty || filteredData.length === 0) && !isLoading && !isFetching ? /* @__PURE__ */ jsx11("tr", { children: /* @__PURE__ */ jsx11("td", { colSpan: filteredColumns.length + 2, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center justify-center gap-2", children: [
5799
+ /* @__PURE__ */ jsx11("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", d: "M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" }) }),
5800
+ /* @__PURE__ */ jsx11("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
4920
5801
  ] }) }) }) : filteredData.map((row) => {
4921
5802
  const hasSubTableCols = filteredColumns.some((c) => {
4922
5803
  var _a;
@@ -4927,108 +5808,40 @@ function TableBlockClient({
4927
5808
  var _a;
4928
5809
  return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
4929
5810
  });
4930
- return /* @__PURE__ */ jsxs10(React9.Fragment, { children: [
4931
- /* @__PURE__ */ jsxs10("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
4932
- /* @__PURE__ */ jsx10("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-center gap-1.5", children: [
4933
- /* @__PURE__ */ jsx10(
4934
- "input",
4935
- {
4936
- type: "checkbox",
4937
- checked: selectedIds.has(row.id),
4938
- onChange: (e) => toggleSelectRow(row.id, e.target.checked),
4939
- disabled: isLoading || isUpdatingActive,
4940
- className: "w-4 h-4 rounded border-slate-300 text-brand"
4941
- }
4942
- ),
4943
- hasSubTableCols && /* @__PURE__ */ jsx10(
4944
- "button",
4945
- {
4946
- type: "button",
4947
- onClick: () => setExpandedRowIds((prev) => {
4948
- const next = new Set(prev);
4949
- if (next.has(row.id)) next.delete(row.id);
4950
- else next.add(row.id);
4951
- return next;
4952
- }),
4953
- className: "w-5 h-5 rounded-full bg-slate-500 hover:bg-slate-600 text-white flex items-center justify-center transition-colors cursor-pointer shrink-0 shadow-sm",
4954
- title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
4955
- children: /* @__PURE__ */ jsx10("svg", { className: `w-3 h-3 transition-transform duration-150 ${isExpanded ? "rotate-90" : "rotate-180"}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "3", d: "M9 5l7 7-7 7" }) })
4956
- }
4957
- )
4958
- ] }) }),
4959
- filteredColumns.map((col) => {
4960
- var _a;
4961
- const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
4962
- const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
4963
- const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
4964
- const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
4965
- let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
4966
- if (value === void 0 && col.defaultValue !== void 0) {
4967
- value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
4968
- if (colType === "CURRENCY" || colType === "NUMBER") {
4969
- if (isNaN(value)) value = null;
4970
- }
4971
- }
4972
- if (value === void 0) value = null;
4973
- return /* @__PURE__ */ jsx10(
4974
- "td",
4975
- {
4976
- className: `border-b border-l border-slate-200 p-0 relative ${colType === "RELATION" ? "overflow-visible" : "overflow-hidden"}`,
4977
- style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
4978
- children: /* @__PURE__ */ jsx10(
4979
- InputComponent,
4980
- {
4981
- value,
4982
- column: col,
4983
- rowId: row.id,
4984
- onChange: (v) => handleCellChange(row.id, col.name, v),
4985
- context: {
4986
- tableName: (schema == null ? void 0 : schema.name) || (schema == null ? void 0 : schema.tableName) || (schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.id) || "Table",
4987
- tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
4988
- onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
4989
- relationOptions,
4990
- refetchRelation,
4991
- executeAction,
4992
- emitEvent,
4993
- pageState,
4994
- componentId,
4995
- orgId,
4996
- updateQuietly: handleQuietCellChange,
4997
- triggerSaveSuccess,
4998
- rowData: row.data
4999
- }
5000
- }
5001
- )
5002
- },
5003
- col.name
5004
- );
5005
- }),
5006
- /* @__PURE__ */ jsx10("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
5007
- ] }),
5008
- isExpanded && /* @__PURE__ */ jsx10(
5009
- ExpandedSubTables,
5010
- {
5011
- rowId: row.id,
5012
- rowData: row.data,
5013
- subTableColumns,
5014
- filteredColumnsLength: filteredColumns.length,
5015
- handleCellChange,
5016
- context: {
5017
- executeAction,
5018
- orgId,
5019
- parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
5020
- tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
5021
- rowData: row.data,
5022
- updateQuietly: handleQuietCellChange,
5023
- triggerSaveSuccess,
5024
- onUpdateColumnConfig: handleUpdateColumnConfig
5025
- }
5026
- }
5027
- )
5028
- ] }, row.id);
5811
+ return /* @__PURE__ */ jsx11(
5812
+ TableRow,
5813
+ {
5814
+ row,
5815
+ isSelected: selectedIds.has(row.id),
5816
+ isExpanded,
5817
+ filteredColumns,
5818
+ columnWidths,
5819
+ hasSubTableCols,
5820
+ subTableColumns,
5821
+ isLoading: isLoading || isFetching,
5822
+ isUpdatingActive,
5823
+ onToggleSelect: toggleSelectRow,
5824
+ onToggleExpand: handleToggleExpand,
5825
+ handleCellChange,
5826
+ handleQuietCellChange,
5827
+ handleUpdateColumnConfig,
5828
+ onMarkForDeletion: handleMarkForDeletion,
5829
+ triggerSaveSuccess,
5830
+ schema,
5831
+ orgId,
5832
+ componentId,
5833
+ pageState,
5834
+ relationOptions,
5835
+ relationDictionaryRef,
5836
+ refetchRelation,
5837
+ executeAction,
5838
+ emitEvent
5839
+ },
5840
+ row.id
5841
+ );
5029
5842
  }) }),
5030
- showSummary && filteredData.length > 0 && /* @__PURE__ */ jsx10("tfoot", { className: "bg-slate-50 border-t-2 border-slate-300 sticky bottom-0 z-10 shadow-[0_-2px_4px_rgba(0,0,0,0.02)]", children: /* @__PURE__ */ jsxs10("tr", { children: [
5031
- /* @__PURE__ */ jsx10("td", { className: "border-b border-l border-slate-200 p-1.5 text-center font-bold text-slate-600 text-xs", children: '\u05E1\u05D4"\u05DB' }),
5843
+ showSummary && filteredData.length > 0 && /* @__PURE__ */ jsx11("tfoot", { className: "bg-slate-50 border-t-2 border-slate-300 sticky bottom-0 z-10 shadow-[0_-2px_4px_rgba(0,0,0,0.02)]", children: /* @__PURE__ */ jsxs11("tr", { children: [
5844
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-l border-slate-200 p-1.5 text-center font-bold text-slate-600 text-xs", children: '\u05E1\u05D4"\u05DB' }),
5032
5845
  filteredColumns.map((col) => {
5033
5846
  var _a, _b;
5034
5847
  const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
@@ -5042,16 +5855,16 @@ function TableBlockClient({
5042
5855
  displayVal = symbol ? `${symbol} ${displayVal}` : displayVal;
5043
5856
  }
5044
5857
  }
5045
- return /* @__PURE__ */ jsx10("td", { className: "border-b border-l border-slate-200 p-2 text-sm font-semibold text-slate-700 bg-slate-50 relative", children: isNumeric && /* @__PURE__ */ jsx10("div", { className: "flex items-center text-right justify-start", children: /* @__PURE__ */ jsx10("span", { className: "truncate", children: displayVal }) }) }, col.name);
5858
+ return /* @__PURE__ */ jsx11("td", { className: "border-b border-l border-slate-200 p-2 text-sm font-semibold text-slate-700 bg-slate-50 relative", children: isNumeric && /* @__PURE__ */ jsx11("div", { className: "flex items-center text-right justify-start", children: /* @__PURE__ */ jsx11("span", { className: "truncate", children: displayVal }) }) }, col.name);
5046
5859
  }),
5047
- /* @__PURE__ */ jsx10("td", { className: "border-b border-slate-200 bg-slate-50 pointer-events-none" })
5860
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-slate-200 bg-slate-50 pointer-events-none" })
5048
5861
  ] }) })
5049
5862
  ] })
5050
5863
  ] }),
5051
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-col sm:flex-row items-center justify-between gap-3 px-4 py-3 bg-slate-50 border-t border-slate-200 text-xs text-slate-600 rounded-b-xl", children: [
5052
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
5053
- /* @__PURE__ */ jsx10("span", { className: "font-medium text-slate-700", children: "\u05E9\u05D5\u05E8\u05D5\u05EA \u05DC\u05E2\u05DE\u05D5\u05D3:" }),
5054
- /* @__PURE__ */ jsxs10(
5864
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col sm:flex-row items-center justify-between gap-3 px-4 py-3 bg-slate-50 border-t border-slate-200 text-xs text-slate-600 rounded-b-xl shrink-0", children: [
5865
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
5866
+ /* @__PURE__ */ jsx11("span", { className: "font-medium text-slate-700", children: "\u05E9\u05D5\u05E8\u05D5\u05EA \u05DC\u05E2\u05DE\u05D5\u05D3:" }),
5867
+ /* @__PURE__ */ jsxs11(
5055
5868
  "select",
5056
5869
  {
5057
5870
  value: limit,
@@ -5059,27 +5872,27 @@ function TableBlockClient({
5059
5872
  disabled: isLoading || isUpdatingActive,
5060
5873
  className: "bg-white border border-slate-300 rounded-lg px-2.5 py-1 text-slate-700 font-semibold shadow-2xs cursor-pointer focus:outline-none focus:ring-2 focus:ring-brand disabled:opacity-50",
5061
5874
  children: [
5062
- /* @__PURE__ */ jsx10("option", { value: 20, children: "20" }),
5063
- /* @__PURE__ */ jsx10("option", { value: 50, children: "50" }),
5064
- /* @__PURE__ */ jsx10("option", { value: 100, children: "100" })
5875
+ /* @__PURE__ */ jsx11("option", { value: 20, children: "20" }),
5876
+ /* @__PURE__ */ jsx11("option", { value: 50, children: "50" }),
5877
+ /* @__PURE__ */ jsx11("option", { value: 100, children: "100" })
5065
5878
  ]
5066
5879
  }
5067
5880
  )
5068
5881
  ] }),
5069
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-4", children: [
5070
- /* @__PURE__ */ jsxs10("span", { className: "font-medium text-slate-600", children: [
5882
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-4", children: [
5883
+ /* @__PURE__ */ jsxs11("span", { className: "font-medium text-slate-600", children: [
5071
5884
  "\u05E2\u05DE\u05D5\u05D3 ",
5072
- /* @__PURE__ */ jsx10("strong", { className: "text-slate-900", children: page }),
5885
+ /* @__PURE__ */ jsx11("strong", { className: "text-slate-900", children: page }),
5073
5886
  " \u05DE\u05EA\u05D5\u05DA ",
5074
- /* @__PURE__ */ jsx10("strong", { className: "text-slate-900", children: totalPages || 1 }),
5075
- totalRecords > 0 && /* @__PURE__ */ jsxs10("span", { className: "text-slate-500 font-normal", children: [
5887
+ /* @__PURE__ */ jsx11("strong", { className: "text-slate-900", children: totalPages || 1 }),
5888
+ totalRecords > 0 && /* @__PURE__ */ jsxs11("span", { className: "text-slate-500 font-normal", children: [
5076
5889
  " (",
5077
5890
  totalRecords,
5078
5891
  ' \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D1\u05E1\u05D4"\u05DB)'
5079
5892
  ] })
5080
5893
  ] }),
5081
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5", children: [
5082
- /* @__PURE__ */ jsx10(
5894
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5", children: [
5895
+ /* @__PURE__ */ jsx11(
5083
5896
  "button",
5084
5897
  {
5085
5898
  onClick: handlePrevPage,
@@ -5089,7 +5902,7 @@ function TableBlockClient({
5089
5902
  children: "\u05D4\u05E7\u05D5\u05D3\u05DD"
5090
5903
  }
5091
5904
  ),
5092
- /* @__PURE__ */ jsx10(
5905
+ /* @__PURE__ */ jsx11(
5093
5906
  "button",
5094
5907
  {
5095
5908
  onClick: handleNextPage,
@@ -5101,7 +5914,31 @@ function TableBlockClient({
5101
5914
  )
5102
5915
  ] })
5103
5916
  ] })
5104
- ] })
5917
+ ] }),
5918
+ activeRelationCol && (() => {
5919
+ const activeColDef = filteredColumns.find((c) => c.name === activeRelationCol);
5920
+ if (!activeColDef) return null;
5921
+ const rawSelected = columnFilters[activeRelationCol];
5922
+ const currentSelected = typeof rawSelected === "object" && rawSelected !== null ? rawSelected : void 0;
5923
+ return /* @__PURE__ */ jsx11(
5924
+ RelationFilterPopover,
5925
+ {
5926
+ isOpen: Boolean(activeRelationCol),
5927
+ onClose: () => {
5928
+ setActiveRelationCol(null);
5929
+ setRelationTriggerRect(null);
5930
+ },
5931
+ column: activeColDef,
5932
+ triggerRect: relationTriggerRect,
5933
+ options: getUniqueRelationOptions(activeColDef),
5934
+ filterValue: currentSelected,
5935
+ onSelectionChange: (newSelected) => {
5936
+ handleApplyColumnFilter(activeRelationCol, newSelected);
5937
+ },
5938
+ isLoading: loadingFilterCol === activeRelationCol
5939
+ }
5940
+ );
5941
+ })()
5105
5942
  ]
5106
5943
  }
5107
5944
  );
@@ -5115,11 +5952,11 @@ function ExpandedSubTables({
5115
5952
  context
5116
5953
  }) {
5117
5954
  var _a;
5118
- const [activeTab, setActiveTab] = useState10((_a = subTableColumns[0]) == null ? void 0 : _a.name);
5955
+ const [activeTab, setActiveTab] = useState12((_a = subTableColumns[0]) == null ? void 0 : _a.name);
5119
5956
  if (subTableColumns.length === 0) return null;
5120
5957
  if (subTableColumns.length === 1) {
5121
5958
  const stCol = subTableColumns[0];
5122
- return /* @__PURE__ */ jsx10("tr", { children: /* @__PURE__ */ jsx10("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50", children: /* @__PURE__ */ jsx10(
5959
+ return /* @__PURE__ */ jsx11("tr", { children: /* @__PURE__ */ jsx11("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50", children: /* @__PURE__ */ jsx11(
5123
5960
  SubTableViewer,
5124
5961
  {
5125
5962
  rowId,
@@ -5131,8 +5968,8 @@ function ExpandedSubTables({
5131
5968
  ) }) });
5132
5969
  }
5133
5970
  const activeCol = subTableColumns.find((c) => c.name === activeTab) || subTableColumns[0];
5134
- return /* @__PURE__ */ jsx10("tr", { children: /* @__PURE__ */ jsxs10("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50 overflow-hidden", children: [
5135
- /* @__PURE__ */ jsx10("div", { className: "flex border-b border-slate-200 bg-slate-100/50 px-2 pt-2 gap-1 overflow-hidden", children: subTableColumns.map((col) => /* @__PURE__ */ jsx10(
5971
+ return /* @__PURE__ */ jsx11("tr", { children: /* @__PURE__ */ jsxs11("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50 overflow-hidden", children: [
5972
+ /* @__PURE__ */ jsx11("div", { className: "flex border-b border-slate-200 bg-slate-100/50 px-2 pt-2 gap-1 overflow-hidden", children: subTableColumns.map((col) => /* @__PURE__ */ jsx11(
5136
5973
  "button",
5137
5974
  {
5138
5975
  type: "button",
@@ -5142,7 +5979,7 @@ function ExpandedSubTables({
5142
5979
  },
5143
5980
  col.name
5144
5981
  )) }),
5145
- /* @__PURE__ */ jsx10("div", { className: "bg-white", children: /* @__PURE__ */ jsx10(
5982
+ /* @__PURE__ */ jsx11("div", { className: "bg-white", children: /* @__PURE__ */ jsx11(
5146
5983
  SubTableViewer,
5147
5984
  {
5148
5985
  rowId,
@@ -5155,11 +5992,134 @@ function ExpandedSubTables({
5155
5992
  ) })
5156
5993
  ] }) });
5157
5994
  }
5995
+ var TableRow = React11.memo(function TableRow2({
5996
+ row,
5997
+ isSelected,
5998
+ isExpanded,
5999
+ filteredColumns,
6000
+ columnWidths,
6001
+ hasSubTableCols,
6002
+ subTableColumns,
6003
+ isLoading,
6004
+ isUpdatingActive,
6005
+ onToggleSelect,
6006
+ onToggleExpand,
6007
+ handleCellChange,
6008
+ handleQuietCellChange,
6009
+ handleUpdateColumnConfig,
6010
+ onMarkForDeletion,
6011
+ triggerSaveSuccess,
6012
+ schema,
6013
+ orgId,
6014
+ componentId,
6015
+ pageState,
6016
+ relationOptions,
6017
+ relationDictionaryRef,
6018
+ refetchRelation,
6019
+ executeAction,
6020
+ emitEvent
6021
+ }) {
6022
+ return /* @__PURE__ */ jsxs11(React11.Fragment, { children: [
6023
+ /* @__PURE__ */ jsxs11("tr", { className: `hover:bg-blue-50/30 transition-colors group ${isSelected ? "bg-blue-50/40" : ""}`, children: [
6024
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-center gap-1.5", children: [
6025
+ /* @__PURE__ */ jsx11(
6026
+ "input",
6027
+ {
6028
+ type: "checkbox",
6029
+ checked: isSelected,
6030
+ onChange: (e) => onToggleSelect(row.id, e.target.checked),
6031
+ disabled: isLoading || isUpdatingActive,
6032
+ className: "w-4 h-4 rounded border-slate-300 text-brand"
6033
+ }
6034
+ ),
6035
+ hasSubTableCols && /* @__PURE__ */ jsx11(
6036
+ "button",
6037
+ {
6038
+ type: "button",
6039
+ onClick: () => onToggleExpand(row.id),
6040
+ className: "w-5 h-5 rounded-full bg-slate-500 hover:bg-slate-600 text-white flex items-center justify-center transition-colors cursor-pointer shrink-0 shadow-sm",
6041
+ title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
6042
+ children: /* @__PURE__ */ jsx11("svg", { className: `w-3 h-3 transition-transform duration-150 ${isExpanded ? "rotate-90" : "rotate-180"}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "3", d: "M9 5l7 7-7 7" }) })
6043
+ }
6044
+ )
6045
+ ] }) }),
6046
+ filteredColumns.map((col) => {
6047
+ var _a;
6048
+ const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
6049
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
6050
+ const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
6051
+ const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
6052
+ let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
6053
+ if (value === void 0 && col.defaultValue !== void 0) {
6054
+ value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
6055
+ if (colType === "CURRENCY" || colType === "NUMBER") {
6056
+ if (isNaN(value)) value = null;
6057
+ }
6058
+ }
6059
+ if (value === void 0) value = null;
6060
+ return /* @__PURE__ */ jsx11(
6061
+ "td",
6062
+ {
6063
+ className: `border-b border-l border-slate-200 p-0 relative ${colType === "RELATION" ? "overflow-visible" : "overflow-hidden"}`,
6064
+ style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
6065
+ children: /* @__PURE__ */ jsx11(
6066
+ InputComponent,
6067
+ {
6068
+ value,
6069
+ column: col,
6070
+ rowId: row.id,
6071
+ onChange: (v) => handleCellChange(row.id, col.name, v),
6072
+ context: {
6073
+ tableName: (schema == null ? void 0 : schema.name) || (schema == null ? void 0 : schema.tableName) || (schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.id) || "Table",
6074
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6075
+ onMarkForDeletion,
6076
+ relationOptions,
6077
+ relationDictionaryRef,
6078
+ refetchRelation,
6079
+ executeAction,
6080
+ emitEvent,
6081
+ pageState,
6082
+ componentId,
6083
+ orgId,
6084
+ updateQuietly: handleQuietCellChange,
6085
+ triggerSaveSuccess,
6086
+ rowData: row.data
6087
+ }
6088
+ }
6089
+ )
6090
+ },
6091
+ col.name
6092
+ );
6093
+ }),
6094
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
6095
+ ] }),
6096
+ isExpanded && /* @__PURE__ */ jsx11(
6097
+ ExpandedSubTables,
6098
+ {
6099
+ rowId: row.id,
6100
+ rowData: row.data,
6101
+ subTableColumns,
6102
+ filteredColumnsLength: filteredColumns.length,
6103
+ handleCellChange,
6104
+ context: {
6105
+ executeAction,
6106
+ orgId,
6107
+ parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6108
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6109
+ rowData: row.data,
6110
+ updateQuietly: handleQuietCellChange,
6111
+ triggerSaveSuccess,
6112
+ onUpdateColumnConfig: handleUpdateColumnConfig
6113
+ }
6114
+ }
6115
+ )
6116
+ ] });
6117
+ });
5158
6118
 
5159
6119
  // src/components/TableBlock/TableBlock.tsx
5160
- import { jsx as jsx11 } from "react/jsx-runtime";
6120
+ import { jsx as jsx12 } from "react/jsx-runtime";
5161
6121
  function TableBlock(props) {
5162
- return /* @__PURE__ */ jsx11(TableBlockClient, __spreadValues({}, props));
6122
+ return /* @__PURE__ */ jsx12(TableBlockClient, __spreadValues({}, props));
5163
6123
  }
5164
6124
 
5165
6125
  // src/registry.ts
@@ -5175,6 +6135,7 @@ export {
5175
6135
  DatePickerCell,
5176
6136
  LinkPickerCell,
5177
6137
  PASTEL_PALETTES,
6138
+ RelationFilterPopover,
5178
6139
  RelationPickerCell,
5179
6140
  TEXT_REPRESENTABLE_TYPES,
5180
6141
  TableBlock,