@dimaan/ui 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -756,7 +756,6 @@ function SidebarNavItem({
756
756
  }
757
757
  );
758
758
  }
759
- console.log("AppShell");
760
759
  function isGroup(entry) {
761
760
  return "items" in entry && Array.isArray(entry.items);
762
761
  }
@@ -1724,12 +1723,26 @@ var formPageBodyClass = "flex-1";
1724
1723
  var formPageActionsBarClass = "sticky bottom-0 -mx-6 -mb-6 mt-6 flex items-center justify-end gap-2 border-t border-border bg-background/95 px-6 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80";
1725
1724
  var formPageSkeletonRowClass = "h-10 w-full animate-pulse rounded-md bg-muted";
1726
1725
  var DEFAULT_SKELETON_ROW_COUNT2 = 6;
1726
+ var DEFAULT_LABELS_LTR = {
1727
+ back: "Back",
1728
+ cancel: "Cancel",
1729
+ save: "Save",
1730
+ saving: "Saving\u2026"
1731
+ };
1732
+ var DEFAULT_LABELS_RTL = {
1733
+ back: "\u0631\u062C\u0648\u0639",
1734
+ cancel: "\u0625\u0644\u063A\u0627\u0621",
1735
+ save: "\u062D\u0641\u0638",
1736
+ saving: "\u062C\u0627\u0631\u064D \u0627\u0644\u062D\u0641\u0638\u2026"
1737
+ };
1727
1738
  function FormPage({
1728
1739
  title,
1729
1740
  description,
1730
- back,
1731
1741
  bordered = true,
1732
1742
  onSubmit,
1743
+ onCancel,
1744
+ isSubmitting,
1745
+ labels: labelsProp,
1733
1746
  isLoading = false,
1734
1747
  loadingRowCount = DEFAULT_SKELETON_ROW_COUNT2,
1735
1748
  actions,
@@ -1739,8 +1752,23 @@ function FormPage({
1739
1752
  bodyClassName,
1740
1753
  actionsClassName
1741
1754
  }) {
1755
+ const navigate = reactRouterDom.useNavigate();
1756
+ const dir = useDirection();
1757
+ const formContext = reactHookForm.useFormContext();
1758
+ const submitting = isSubmitting ?? formContext?.formState?.isSubmitting ?? false;
1759
+ const defaults = dir === "rtl" ? DEFAULT_LABELS_RTL : DEFAULT_LABELS_LTR;
1760
+ const labels = { ...defaults, ...labelsProp };
1761
+ const goBack = () => navigate(-1);
1742
1762
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "form-page", className: cn(formPageBaseClass, className), children: [
1743
- /* @__PURE__ */ jsxRuntime.jsx(PageHeader, { title, description, back, bordered }),
1763
+ /* @__PURE__ */ jsxRuntime.jsx(
1764
+ PageHeader,
1765
+ {
1766
+ title,
1767
+ description,
1768
+ back: { label: labels.back, onClick: goBack },
1769
+ bordered
1770
+ }
1771
+ ),
1744
1772
  /* @__PURE__ */ jsxRuntime.jsxs(
1745
1773
  "form",
1746
1774
  {
@@ -1751,14 +1779,27 @@ function FormPage({
1751
1779
  className: cn("flex flex-1 flex-col gap-6", formClassName),
1752
1780
  children: [
1753
1781
  /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "form-page-body", className: cn(formPageBodyClass, bodyClassName), children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(FormPageSkeleton, { rowCount: loadingRowCount }) : children }),
1754
- actions !== null && actions !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1782
+ /* @__PURE__ */ jsxRuntime.jsxs(
1755
1783
  "div",
1756
1784
  {
1757
1785
  "data-slot": "form-page-actions",
1758
1786
  className: cn(formPageActionsBarClass, actionsClassName),
1759
- children: actions
1787
+ children: [
1788
+ actions,
1789
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "outline", onClick: onCancel ?? goBack, children: labels.cancel }),
1790
+ /* @__PURE__ */ jsxRuntime.jsx(
1791
+ Button,
1792
+ {
1793
+ type: "submit",
1794
+ loading: submitting,
1795
+ loadingText: labels.saving,
1796
+ disabled: isLoading,
1797
+ children: labels.save
1798
+ }
1799
+ )
1800
+ ]
1760
1801
  }
1761
- ) : null
1802
+ )
1762
1803
  ]
1763
1804
  }
1764
1805
  )
@@ -2009,15 +2050,35 @@ var SelectItem = react.forwardRef(function SelectItem2({ className, children, ..
2009
2050
  /* @__PURE__ */ jsxRuntime.jsx(RadixSelect__namespace.ItemText, { children })
2010
2051
  ] });
2011
2052
  });
2053
+ var EN_LABELS = {
2054
+ rowsPerPage: "Rows per page",
2055
+ pageRangeOf: "of",
2056
+ previousPage: "Previous page",
2057
+ nextPage: "Next page"
2058
+ };
2059
+ var AR_LABELS = {
2060
+ rowsPerPage: "\u0635\u0641\u0648\u0641 \u0644\u0643\u0644 \u0635\u0641\u062D\u0629",
2061
+ pageRangeOf: "\u0645\u0646",
2062
+ previousPage: "\u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u0633\u0627\u0628\u0642\u0629",
2063
+ nextPage: "\u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u062A\u0627\u0644\u064A\u0629"
2064
+ };
2012
2065
  function Pagination({
2013
2066
  pageIndex,
2014
2067
  pageSize,
2015
2068
  pageCount,
2016
2069
  totalRowCount,
2017
2070
  pageSizeOptions,
2018
- onChange
2071
+ onChange,
2072
+ labels: labelsProp
2019
2073
  }) {
2020
2074
  const dir = useDirection();
2075
+ const localeDefaults = dir === "rtl" ? AR_LABELS : EN_LABELS;
2076
+ const labels = {
2077
+ rowsPerPage: labelsProp?.rowsPerPage ?? localeDefaults.rowsPerPage,
2078
+ pageRangeOf: labelsProp?.pageRangeOf ?? localeDefaults.pageRangeOf,
2079
+ previousPage: labelsProp?.previousPage ?? localeDefaults.previousPage,
2080
+ nextPage: labelsProp?.nextPage ?? localeDefaults.nextPage
2081
+ };
2021
2082
  const isRtl = dir === "rtl";
2022
2083
  const isFirst = pageIndex <= 0;
2023
2084
  const isLast = pageIndex >= pageCount - 1;
@@ -2031,7 +2092,7 @@ function Pagination({
2031
2092
  const end = Math.min(totalRowCount, (pageIndex + 1) * pageSize);
2032
2093
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground", children: [
2033
2094
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
2034
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rows per page" }),
2095
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.rowsPerPage }),
2035
2096
  /* @__PURE__ */ jsxRuntime.jsx(
2036
2097
  "select",
2037
2098
  {
@@ -2050,7 +2111,9 @@ function Pagination({
2050
2111
  start,
2051
2112
  "\u2013",
2052
2113
  end,
2053
- " of ",
2114
+ " ",
2115
+ labels.pageRangeOf,
2116
+ " ",
2054
2117
  totalRowCount
2055
2118
  ] }),
2056
2119
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
@@ -2062,7 +2125,7 @@ function Pagination({
2062
2125
  size: "sm",
2063
2126
  disabled: isFirst,
2064
2127
  onClick: goPrev,
2065
- "aria-label": "Previous page",
2128
+ "aria-label": labels.previousPage,
2066
2129
  children: isRtl ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
2067
2130
  }
2068
2131
  ),
@@ -2079,7 +2142,7 @@ function Pagination({
2079
2142
  size: "sm",
2080
2143
  disabled: isLast,
2081
2144
  onClick: goNext,
2082
- "aria-label": "Next page",
2145
+ "aria-label": labels.nextPage,
2083
2146
  children: isRtl ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
2084
2147
  }
2085
2148
  )
@@ -2132,66 +2195,28 @@ var alignClass = {
2132
2195
  end: "text-end"
2133
2196
  };
2134
2197
  var EMPTY_SELECTION = /* @__PURE__ */ new Set();
2135
- var NO_SORT = { columnId: null, direction: "asc" };
2136
2198
  function useTableState(props) {
2137
2199
  const {
2138
- defaultSort,
2139
- sort: sortProp,
2140
- onSortChange,
2141
- defaultPagination,
2142
- pagination: paginationProp,
2143
- onPaginationChange,
2144
- pageSizeOptions,
2145
2200
  defaultSelectedRowIds,
2146
2201
  selectedRowIds: selectedRowIdsProp,
2147
- onSelectedRowIdsChange,
2148
- totalCount
2202
+ onSelectedRowIdsChange
2149
2203
  } = props;
2150
- const [internalSort, setInternalSort] = react.useState(defaultSort ?? NO_SORT);
2151
- const isSortControlled = sortProp !== void 0;
2152
- const sort = isSortControlled ? sortProp : internalSort;
2153
- const setSort = react.useCallback(
2154
- (next) => {
2155
- if (!isSortControlled) setInternalSort(next);
2156
- onSortChange?.(next);
2157
- },
2158
- [isSortControlled, onSortChange]
2159
- );
2160
- const [internalPagination, setInternalPagination] = react.useState(
2161
- defaultPagination ?? { pageIndex: 0, pageSize: pageSizeOptions?.[0] ?? 10 }
2162
- );
2163
- const isPaginationControlled = paginationProp !== void 0;
2164
- const pagination = isPaginationControlled ? paginationProp : internalPagination;
2165
- const setPagination = react.useCallback(
2166
- (next) => {
2167
- if (!isPaginationControlled) setInternalPagination(next);
2168
- onPaginationChange?.(next);
2169
- },
2170
- [isPaginationControlled, onPaginationChange]
2171
- );
2172
2204
  const [internalSelected, setInternalSelected] = react.useState(
2173
2205
  defaultSelectedRowIds ?? EMPTY_SELECTION
2174
2206
  );
2175
- const isSelectionControlled = selectedRowIdsProp !== void 0;
2176
- const selected = isSelectionControlled ? selectedRowIdsProp : internalSelected;
2207
+ const isControlled = selectedRowIdsProp !== void 0;
2208
+ const selected = isControlled ? selectedRowIdsProp : internalSelected;
2177
2209
  const setSelected = react.useCallback(
2178
2210
  (next) => {
2179
- if (!isSelectionControlled) setInternalSelected(next);
2211
+ if (!isControlled) setInternalSelected(next);
2180
2212
  onSelectedRowIdsChange?.(next);
2181
2213
  },
2182
- [isSelectionControlled, onSelectedRowIdsChange]
2214
+ [isControlled, onSelectedRowIdsChange]
2183
2215
  );
2184
- return {
2185
- sort,
2186
- setSort,
2187
- pagination,
2188
- setPagination,
2189
- selected,
2190
- setSelected,
2191
- isServerSide: totalCount !== void 0
2192
- };
2216
+ return { selected, setSelected };
2193
2217
  }
2194
2218
  var DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
2219
+ var DEFAULT_PAGE_SIZE = 10;
2195
2220
  function defaultGetRowId(row, index) {
2196
2221
  const rowId = row.id;
2197
2222
  return rowId === void 0 || rowId === null ? String(index) : String(rowId);
@@ -2218,36 +2243,26 @@ function Table(props) {
2218
2243
  tableRef,
2219
2244
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
2220
2245
  showPagination,
2221
- caption
2246
+ caption,
2247
+ sort,
2248
+ onSortChange,
2249
+ pagination,
2250
+ onPaginationChange,
2251
+ totalCount,
2252
+ labels
2222
2253
  } = props;
2223
2254
  const ariaLabel = props["aria-label"];
2224
2255
  const ariaLabelledBy = props["aria-labelledby"];
2225
- const { sort, setSort, pagination, setPagination, selected, setSelected, isServerSide } = useTableState(props);
2226
- const sortedRows = react.useMemo(() => {
2227
- if (isServerSide || sort.columnId === null) return data;
2228
- const col = columns.find((c) => c.id === sort.columnId);
2229
- if (!col) return data;
2230
- const get = resolveSortGetter(col);
2231
- if (!get) return data;
2232
- const tagged = data.map((row, index) => ({ row, index }));
2233
- tagged.sort((a, b) => {
2234
- const cmp = compareValues(get(a.row), get(b.row));
2235
- if (cmp !== 0) return sort.direction === "asc" ? cmp : -cmp;
2236
- return a.index - b.index;
2237
- });
2238
- return tagged.map((entry) => entry.row);
2239
- }, [data, columns, sort, isServerSide]);
2240
- const pagedRows = react.useMemo(() => {
2241
- if (isServerSide) return sortedRows;
2242
- const start = pagination.pageIndex * pagination.pageSize;
2243
- return sortedRows.slice(start, start + pagination.pageSize);
2244
- }, [sortedRows, pagination, isServerSide]);
2245
- const totalRowCount = isServerSide ? props.totalCount ?? 0 : data.length;
2246
- const pageCount = Math.max(1, Math.ceil(totalRowCount / pagination.pageSize));
2256
+ const { selected, setSelected } = useTableState(props);
2257
+ const effectiveSort = sort ?? { columnId: null, direction: "asc" };
2258
+ const pageSize = pagination?.pageSize ?? pageSizeOptions[0] ?? DEFAULT_PAGE_SIZE;
2259
+ const pageIndex = pagination?.pageIndex ?? 0;
2260
+ const totalRowCount = totalCount ?? data.length;
2261
+ const pageCount = Math.max(1, Math.ceil(totalRowCount / pageSize));
2247
2262
  const selectableRowIds = react.useMemo(() => {
2248
2263
  if (!enableRowSelection) return [];
2249
- return pagedRows.map((row, index) => ({ row, index })).filter(({ row }) => isRowSelectable ? isRowSelectable(row) : true).map(({ row, index }) => getRowId(row, index));
2250
- }, [pagedRows, enableRowSelection, isRowSelectable, getRowId]);
2264
+ return data.map((row, index) => ({ row, index })).filter(({ row }) => isRowSelectable ? isRowSelectable(row) : true).map(({ row, index }) => getRowId(row, index));
2265
+ }, [data, enableRowSelection, isRowSelectable, getRowId]);
2251
2266
  const selectedOnPageCount = selectableRowIds.reduce(
2252
2267
  (acc, id) => selected.has(id) ? acc + 1 : acc,
2253
2268
  0
@@ -2275,13 +2290,16 @@ function Table(props) {
2275
2290
  };
2276
2291
  const clearSelection = () => setSelected(/* @__PURE__ */ new Set());
2277
2292
  const handleSortClick = (columnId) => {
2278
- setSort(nextSort(sort, columnId));
2293
+ if (!onSortChange) return;
2294
+ onSortChange(nextSort(effectiveSort, columnId));
2279
2295
  };
2296
+ const handlePaginationChange = onPaginationChange ?? (() => {
2297
+ });
2280
2298
  const totalColumnCount = columns.length + (enableRowSelection ? 1 : 0);
2281
- const paginationVisible = showPagination ?? totalRowCount > pagination.pageSize;
2299
+ const paginationVisible = Boolean(pagination) && (showPagination ?? totalRowCount > pageSize);
2282
2300
  const sizeClasses = tableSizeClass[size];
2283
2301
  const showToolbar = enableRowSelection && bulkActions !== void 0 && selected.size > 0;
2284
- const skeletonCount = loadingRowCount ?? pagination.pageSize;
2302
+ const skeletonCount = loadingRowCount ?? pageSize;
2285
2303
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex w-full flex-col gap-3", className), children: [
2286
2304
  showToolbar && /* @__PURE__ */ jsxRuntime.jsx(
2287
2305
  Toolbar,
@@ -2330,8 +2348,8 @@ function Table(props) {
2330
2348
  }
2331
2349
  ) }) : null,
2332
2350
  columns.map((column) => {
2333
- const isSorted = sort.columnId === column.id;
2334
- const ariaSort = isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none";
2351
+ const isSorted = effectiveSort.columnId === column.id;
2352
+ const ariaSort = isSorted ? effectiveSort.direction === "asc" ? "ascending" : "descending" : "none";
2335
2353
  return /* @__PURE__ */ jsxRuntime.jsx(
2336
2354
  "th",
2337
2355
  {
@@ -2348,14 +2366,14 @@ function Table(props) {
2348
2366
  type: "button",
2349
2367
  onClick: () => handleSortClick(column.id),
2350
2368
  className: "inline-flex items-center gap-1.5 font-inherit uppercase tracking-inherit text-inherit hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
2351
- "aria-label": sortAriaLabel(column, sort),
2369
+ "aria-label": sortAriaLabel(column, effectiveSort),
2352
2370
  children: [
2353
2371
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: renderHeader(column.header) }),
2354
2372
  /* @__PURE__ */ jsxRuntime.jsx(
2355
2373
  SortIndicator,
2356
2374
  {
2357
2375
  active: isSorted,
2358
- direction: isSorted ? sort.direction : null
2376
+ direction: isSorted ? effectiveSort.direction : null
2359
2377
  }
2360
2378
  )
2361
2379
  ]
@@ -2375,14 +2393,14 @@ function Table(props) {
2375
2393
  columnCount: totalColumnCount,
2376
2394
  cellClassName: sizeClasses.cell
2377
2395
  }
2378
- ) : pagedRows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
2396
+ ) : data.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
2379
2397
  "td",
2380
2398
  {
2381
2399
  colSpan: totalColumnCount,
2382
2400
  className: cn(sizeClasses.cell, "py-10 text-center text-muted-foreground"),
2383
2401
  children: emptyState ?? "No data"
2384
2402
  }
2385
- ) }) : pagedRows.map((row, rowIndex) => {
2403
+ ) }) : data.map((row, rowIndex) => {
2386
2404
  const id = getRowId(row, rowIndex);
2387
2405
  const isSelected = selected.has(id);
2388
2406
  const rowSelectable = isRowSelectable ? isRowSelectable(row) : true;
@@ -2435,12 +2453,13 @@ function Table(props) {
2435
2453
  paginationVisible ? /* @__PURE__ */ jsxRuntime.jsx(
2436
2454
  Pagination,
2437
2455
  {
2438
- pageIndex: pagination.pageIndex,
2439
- pageSize: pagination.pageSize,
2456
+ pageIndex,
2457
+ pageSize,
2440
2458
  pageCount,
2441
2459
  totalRowCount,
2442
2460
  pageSizeOptions,
2443
- onChange: setPagination
2461
+ onChange: handlePaginationChange,
2462
+ labels
2444
2463
  }
2445
2464
  ) : null
2446
2465
  ] });
@@ -2456,24 +2475,6 @@ function renderCell(column, row, rowIndex) {
2456
2475
  }
2457
2476
  return null;
2458
2477
  }
2459
- function resolveSortGetter(column) {
2460
- if (column.sortAccessor) return column.sortAccessor;
2461
- if (column.accessor !== void 0) {
2462
- const key = column.accessor;
2463
- return (row) => row[key];
2464
- }
2465
- return null;
2466
- }
2467
- function compareValues(a, b) {
2468
- if (a === b) return 0;
2469
- if (a === null || a === void 0) return 1;
2470
- if (b === null || b === void 0) return -1;
2471
- if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
2472
- if (typeof a === "number" && typeof b === "number") return a - b;
2473
- if (typeof a === "bigint" && typeof b === "bigint") return a < b ? -1 : 1;
2474
- if (typeof a === "boolean" && typeof b === "boolean") return a === b ? 0 : a ? 1 : -1;
2475
- return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
2476
- }
2477
2478
  function nextSort(current, columnId) {
2478
2479
  if (current.columnId !== columnId) return { columnId, direction: "asc" };
2479
2480
  if (current.direction === "asc") return { columnId, direction: "desc" };
@@ -2524,16 +2525,14 @@ function ListPage({
2524
2525
  getRowId,
2525
2526
  isLoading = false,
2526
2527
  loadingRowCount,
2527
- searchKeys,
2528
- searchValue: searchValueProp,
2528
+ searchValue,
2529
2529
  onSearchChange,
2530
2530
  filters,
2531
- filterValues: filterValuesProp,
2531
+ filterValues,
2532
2532
  onFilterChange,
2533
2533
  enableRowSelection,
2534
2534
  bulkActions,
2535
2535
  pagination,
2536
- defaultPagination,
2537
2536
  onPaginationChange,
2538
2537
  totalCount,
2539
2538
  pageSizeOptions,
@@ -2543,82 +2542,36 @@ function ListPage({
2543
2542
  className
2544
2543
  }) {
2545
2544
  const labels = { ...DEFAULT_LABELS2, ...labelsProp };
2546
- const initialFilterValues = react.useMemo(() => {
2547
- const init = {};
2545
+ const showSearch = onSearchChange !== void 0;
2546
+ const showFilterBar = showSearch || Boolean(filters?.length);
2547
+ const hasActiveQuery = react.useMemo(() => {
2548
+ if ((searchValue ?? "").trim() !== "") return true;
2548
2549
  for (const f of filters ?? []) {
2549
- init[f.key] = f.defaultValue ?? f.options[0]?.value ?? "";
2550
- }
2551
- return init;
2552
- }, [filters]);
2553
- const isSearchControlled = searchValueProp !== void 0;
2554
- const isFiltersControlled = filterValuesProp !== void 0;
2555
- const [internalSearch, setInternalSearch] = react.useState("");
2556
- const [internalFilterValues, setInternalFilterValues] = react.useState(initialFilterValues);
2557
- const search = isSearchControlled ? searchValueProp : internalSearch;
2558
- const filterValues = isFiltersControlled ? filterValuesProp : internalFilterValues;
2559
- const setSearch = (next) => {
2560
- if (!isSearchControlled) setInternalSearch(next);
2561
- onSearchChange?.(next);
2562
- };
2563
- const setFilter = (key, value) => {
2564
- if (!isFiltersControlled) {
2565
- setInternalFilterValues((prev) => ({ ...prev, [key]: value }));
2550
+ const current = filterValues?.[f.key];
2551
+ const def = f.options[0]?.value ?? "";
2552
+ if (current !== void 0 && current !== def) return true;
2566
2553
  }
2567
- onFilterChange?.(key, value);
2568
- };
2554
+ return false;
2555
+ }, [searchValue, filters, filterValues]);
2569
2556
  const reset = () => {
2570
- if (!isSearchControlled) setInternalSearch("");
2571
2557
  onSearchChange?.("");
2572
- if (!isFiltersControlled) setInternalFilterValues(initialFilterValues);
2573
2558
  for (const f of filters ?? []) {
2574
- const def = f.defaultValue ?? f.options[0]?.value ?? "";
2559
+ const def = f.options[0]?.value ?? "";
2575
2560
  onFilterChange?.(f.key, def);
2576
2561
  }
2577
2562
  };
2578
- const hasActiveFilters = react.useMemo(() => {
2579
- if (search.trim() !== "") return true;
2580
- for (const f of filters ?? []) {
2581
- const current = filterValues[f.key];
2582
- const def = f.defaultValue ?? f.options[0]?.value ?? "";
2583
- if (current !== def) return true;
2584
- }
2585
- return false;
2586
- }, [search, filters, filterValues]);
2587
- const filtered = react.useMemo(() => {
2588
- return data.filter((row) => {
2589
- if (!isSearchControlled && search.trim() && searchKeys && searchKeys.length > 0) {
2590
- const q = search.trim().toLowerCase();
2591
- const matches = searchKeys.some((key) => {
2592
- const val = row[key];
2593
- return val != null && String(val).toLowerCase().includes(q);
2594
- });
2595
- if (!matches) return false;
2596
- }
2597
- if (!isFiltersControlled) {
2598
- for (const f of filters ?? []) {
2599
- const value = filterValues[f.key];
2600
- const def = f.defaultValue ?? f.options[0]?.value ?? "";
2601
- if (value !== void 0 && value !== def) {
2602
- if (f.accessor(row) !== value) return false;
2603
- }
2604
- }
2605
- }
2606
- return true;
2607
- });
2608
- }, [data, search, searchKeys, filters, filterValues, isSearchControlled, isFiltersControlled]);
2609
- const showFilterBar = Boolean(searchKeys?.length) || Boolean(filters?.length);
2610
- const tableMode = isLoading ? "loading" : data.length === 0 && !hasActiveFilters ? "no-data" : filtered.length === 0 ? "no-results" : "rows";
2563
+ const tableMode = isLoading ? "loading" : data.length === 0 && !hasActiveQuery ? "no-data" : data.length === 0 && hasActiveQuery ? "no-results" : "rows";
2611
2564
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "list-page", className: cn("space-y-6", className), children: [
2612
2565
  /* @__PURE__ */ jsxRuntime.jsx(PageHeader, { title, description, bordered, actions }),
2613
2566
  showFilterBar ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "list-page-filter-bar", className: "flex flex-wrap items-center gap-3", children: [
2614
- searchKeys?.length ? /* @__PURE__ */ jsxRuntime.jsx(
2567
+ showSearch ? /* @__PURE__ */ jsxRuntime.jsx(
2615
2568
  Input,
2616
2569
  {
2617
2570
  type: "search",
2618
2571
  placeholder: labels.searchPlaceholder,
2619
2572
  "aria-label": labels.searchAriaLabel || labels.searchPlaceholder,
2620
- value: search,
2621
- onChange: (e) => setSearch(e.target.value),
2573
+ value: searchValue ?? "",
2574
+ onChange: (e) => onSearchChange?.(e.target.value),
2622
2575
  leadingIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, {}),
2623
2576
  wrapperClassName: "sm:max-w-xs",
2624
2577
  disabled: isLoading
@@ -2627,16 +2580,16 @@ function ListPage({
2627
2580
  filters?.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
2628
2581
  Select,
2629
2582
  {
2630
- "aria-label": f.ariaLabel,
2631
- value: filterValues[f.key],
2632
- onValueChange: (v) => setFilter(f.key, v),
2583
+ "aria-label": typeof f.label === "string" ? f.label : f.key,
2584
+ value: filterValues?.[f.key] ?? f.options[0]?.value ?? "",
2585
+ onValueChange: (v) => onFilterChange?.(f.key, v),
2633
2586
  options: f.options,
2634
2587
  className: FILTER_WIDTH_CLASS[f.width ?? "default"],
2635
2588
  disabled: isLoading
2636
2589
  },
2637
2590
  f.key
2638
2591
  )),
2639
- hasActiveFilters && !isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", onClick: reset, children: [
2592
+ hasActiveQuery && !isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", onClick: reset, children: [
2640
2593
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, {}),
2641
2594
  labels.reset
2642
2595
  ] }) : null
@@ -2645,7 +2598,7 @@ function ListPage({
2645
2598
  Table,
2646
2599
  {
2647
2600
  "aria-label": typeof title === "string" ? title : void 0,
2648
- data: filtered,
2601
+ data,
2649
2602
  columns,
2650
2603
  getRowId,
2651
2604
  enableRowSelection,
@@ -2653,10 +2606,10 @@ function ListPage({
2653
2606
  loading: isLoading,
2654
2607
  loadingRowCount,
2655
2608
  pagination,
2656
- defaultPagination,
2657
2609
  onPaginationChange,
2658
2610
  totalCount,
2659
- pageSizeOptions
2611
+ pageSizeOptions,
2612
+ labels
2660
2613
  }
2661
2614
  ) : tableMode === "no-data" ? /* @__PURE__ */ jsxRuntime.jsx(
2662
2615
  "div",