@undevy-org/tablewright 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4271,90 +4271,6 @@ function FilterFormToggle({
4271
4271
  }
4272
4272
  );
4273
4273
  }
4274
- function computeColumnGaps(orderedColumnIds, columnVisibility) {
4275
- const gaps = [];
4276
- let lastVisibleId = null;
4277
- let pendingHidden = [];
4278
- for (const columnId of orderedColumnIds) {
4279
- if (columnVisibility[columnId] === false) {
4280
- pendingHidden.push(columnId);
4281
- } else {
4282
- if (pendingHidden.length > 0) {
4283
- gaps.push({ afterColumnId: lastVisibleId, hiddenIds: pendingHidden });
4284
- pendingHidden = [];
4285
- }
4286
- lastVisibleId = columnId;
4287
- }
4288
- }
4289
- if (pendingHidden.length > 0) {
4290
- gaps.push({ afterColumnId: lastVisibleId, hiddenIds: pendingHidden });
4291
- }
4292
- return gaps;
4293
- }
4294
- function getField(row, fieldName) {
4295
- return row[fieldName];
4296
- }
4297
- function computeColumnStats(rows, fieldName, config) {
4298
- switch (config.type) {
4299
- case "number-range": {
4300
- let min = Infinity;
4301
- let max = -Infinity;
4302
- for (const row of rows) {
4303
- const val = getField(row, fieldName);
4304
- if (typeof val === "number" && Number.isFinite(val)) {
4305
- if (val < min) min = val;
4306
- if (val > max) max = val;
4307
- }
4308
- }
4309
- if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
4310
- return { type: "number-range", min, max };
4311
- }
4312
- case "text": {
4313
- const seen = /* @__PURE__ */ new Set();
4314
- const samples = [];
4315
- for (const row of rows) {
4316
- const val = getField(row, fieldName);
4317
- if (typeof val === "string" && val.length > 0) {
4318
- if (!seen.has(val)) {
4319
- seen.add(val);
4320
- if (samples.length < 3) samples.push(val);
4321
- }
4322
- }
4323
- }
4324
- if (seen.size === 0) return null;
4325
- return { type: "text", samples, totalUnique: seen.size };
4326
- }
4327
- case "date": {
4328
- const valueField = `${fieldName}Value`;
4329
- let min = Infinity;
4330
- let max = -Infinity;
4331
- for (const row of rows) {
4332
- const val = getField(row, valueField);
4333
- if (typeof val === "number" && val > 0) {
4334
- if (val < min) min = val;
4335
- if (val > max) max = val;
4336
- }
4337
- }
4338
- if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
4339
- const toDateStr = (ts) => new Date(ts).toISOString().slice(0, 10);
4340
- return { type: "date", min: toDateStr(min), max: toDateStr(max) };
4341
- }
4342
- case "enum": {
4343
- const counts = {};
4344
- if (config.options) {
4345
- for (const opt of config.options) counts[opt.value] = 0;
4346
- }
4347
- for (const row of rows) {
4348
- const val = getField(row, fieldName);
4349
- const key = typeof val === "boolean" ? String(val) : typeof val === "string" ? val : null;
4350
- if (key !== null && key in counts) counts[key]++;
4351
- }
4352
- return { type: "enum", counts };
4353
- }
4354
- default:
4355
- return null;
4356
- }
4357
- }
4358
4274
  const CUSTOM_VIEW_KEY = "custom";
4359
4275
  const defaultValueForFilter = (cfg) => {
4360
4276
  if (cfg.type === "compound") return {};
@@ -4515,209 +4431,134 @@ function createTableOrchestrationStore({
4515
4431
  };
4516
4432
  });
4517
4433
  }
4518
- const EMPTY_ID_SET = /* @__PURE__ */ new Set();
4519
- const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
4520
- function useTableOrchestration(config) {
4521
- const {
4522
- rows,
4523
- filterConfigs,
4524
- columnMeta,
4525
- toggleableColumnIds,
4526
- rowsPerPageOptions,
4527
- viewPresets,
4528
- defaultViewKey,
4529
- columnGroups,
4530
- initialColumnVisibility,
4531
- initialColumnWidths,
4532
- columnPriorities,
4533
- wideBreakpoint = 1280,
4534
- compactBreakpoint = 768
4535
- } = config;
4536
- const { getRowId, filterRow, getSortValue, getSearchText } = config;
4537
- const [store] = React.useState(
4538
- () => createTableOrchestrationStore({
4539
- defaultViewKey,
4540
- initialColumnVisibility,
4541
- initialRowsPerPage: rowsPerPageOptions[0]
4542
- })
4543
- );
4544
- const searchInput = zustand.useStore(store, (s) => s.searchInput);
4545
- const searchQuery = zustand.useStore(store, (s) => s.searchQuery);
4546
- const columnFilters = zustand.useStore(store, (s) => s.columnFilters);
4547
- const newestFilterColumnId = zustand.useStore(store, (s) => s.newestFilterColumnId);
4548
- const requestOpenFilterId = zustand.useStore(store, (s) => s.requestOpenFilterId);
4549
- const currentSort = zustand.useStore(store, (s) => s.currentSort);
4550
- const columnVisibility = zustand.useStore(store, (s) => s.columnVisibility);
4551
- const storedPage = zustand.useStore(store, (s) => s.page);
4552
- const rowsPerPage = zustand.useStore(store, (s) => s.rowsPerPage);
4553
- const density = zustand.useStore(store, (s) => s.density);
4554
- const isRefreshing = zustand.useStore(store, (s) => s.isRefreshing);
4555
- const storedSelectedIds = zustand.useStore(store, (s) => s.selectedIds);
4556
- const activeView = zustand.useStore(store, (s) => s.activeView);
4557
- const columnsMenuOpen = zustand.useStore(store, (s) => s.columnsMenuOpen);
4558
- const storedSelectedRowId = zustand.useStore(store, (s) => s.selectedRowId);
4559
- const hasPriorities = Boolean(columnPriorities && Object.keys(columnPriorities).length > 0);
4560
- const [isWide, setIsWide] = React.useState(true);
4561
- const [isCompact, setIsCompact] = React.useState(false);
4562
- React.useEffect(() => {
4563
- if (!hasPriorities || typeof window === "undefined") return;
4564
- const mql = window.matchMedia(`(min-width: ${wideBreakpoint}px)`);
4565
- const onChange = (e) => setIsWide(e.matches);
4566
- setIsWide(mql.matches);
4567
- mql.addEventListener("change", onChange);
4568
- return () => mql.removeEventListener("change", onChange);
4569
- }, [hasPriorities, wideBreakpoint]);
4570
- React.useEffect(() => {
4571
- if (typeof window === "undefined") return;
4572
- const mql = window.matchMedia(`(max-width: ${compactBreakpoint - 1}px)`);
4573
- const onChange = (e) => setIsCompact(e.matches);
4574
- setIsCompact(mql.matches);
4575
- mql.addEventListener("change", onChange);
4576
- return () => mql.removeEventListener("change", onChange);
4577
- }, [compactBreakpoint]);
4578
- const responsiveHiddenColumnIds = React.useMemo(() => {
4579
- if (isWide || !columnPriorities) return EMPTY_ID_SET;
4580
- const hidden = /* @__PURE__ */ new Set();
4581
- for (const [id, priority] of Object.entries(columnPriorities)) {
4582
- if (priority === 3) hidden.add(id);
4583
- }
4584
- return hidden;
4585
- }, [isWide, columnPriorities]);
4586
- const selectedIdsRef = React.useRef(storedSelectedIds);
4587
- const headerCheckStateRef = React.useRef(false);
4588
- const sortState = React.useMemo(() => {
4589
- if (!currentSort) return {};
4590
- return { [currentSort.columnId]: currentSort.direction };
4591
- }, [currentSort]);
4592
- const { widths, onPointerDown, setWidths } = useColumnResize(columnMeta, initialColumnWidths);
4593
- const activePreset = React.useMemo(() => {
4594
- if (activeView === "custom") {
4595
- return {
4596
- key: "custom",
4597
- label: "Custom",
4598
- filters: [],
4599
- sort: null,
4600
- columnVisibility: {}
4601
- };
4602
- }
4603
- return viewPresets.find((v) => v.key === activeView) ?? viewPresets[0];
4604
- }, [activeView, viewPresets]);
4605
- const allColumnFilterStats = React.useMemo(() => {
4606
- const result = {};
4607
- for (const [columnId, cfg] of Object.entries(filterConfigs)) {
4608
- if (cfg.type === "compound" && cfg.subFilters) {
4609
- for (const sub of cfg.subFilters) {
4610
- const s = computeColumnStats(rows, sub.field, {
4611
- type: sub.type,
4612
- label: sub.label,
4613
- options: sub.options
4614
- });
4615
- if (s) result[`${columnId}:${sub.key}`] = s;
4434
+ function getField(row, fieldName) {
4435
+ return row[fieldName];
4436
+ }
4437
+ function computeColumnStats(rows, fieldName, config) {
4438
+ switch (config.type) {
4439
+ case "number-range": {
4440
+ let min = Infinity;
4441
+ let max = -Infinity;
4442
+ for (const row of rows) {
4443
+ const val = getField(row, fieldName);
4444
+ if (typeof val === "number" && Number.isFinite(val)) {
4445
+ if (val < min) min = val;
4446
+ if (val > max) max = val;
4616
4447
  }
4617
- } else {
4618
- const s = computeColumnStats(rows, cfg.fieldName ?? columnId, cfg);
4619
- if (s) result[columnId] = s;
4620
4448
  }
4449
+ if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
4450
+ return { type: "number-range", min, max };
4621
4451
  }
4622
- return result;
4623
- }, [rows, filterConfigs]);
4624
- const filteredRows = React.useMemo(() => {
4625
- const search = searchQuery.trim().toLowerCase();
4626
- const filtered = rows.filter((row) => {
4627
- if (activePreset.customFilter && !activePreset.customFilter(row)) return false;
4628
- if (search.length > 0 && !getSearchText(row).includes(search)) return false;
4629
- return columnFilters.every((filter) => filterRow(row, filter));
4630
- });
4631
- if (!currentSort) return filtered;
4632
- const { columnId, direction } = currentSort;
4633
- return [...filtered].sort((a, b) => {
4634
- const aVal = getSortValue(a, columnId);
4635
- const bVal = getSortValue(b, columnId);
4636
- if (aVal < bVal) return direction === "asc" ? -1 : 1;
4637
- if (aVal > bVal) return direction === "asc" ? 1 : -1;
4638
- return 0;
4639
- });
4640
- }, [
4641
- rows,
4642
- searchQuery,
4643
- columnFilters,
4644
- currentSort,
4645
- activePreset,
4646
- getSearchText,
4647
- filterRow,
4648
- getSortValue
4649
- ]);
4650
- const filteredColumnFilterStats = React.useMemo(() => {
4651
- if (columnFilters.length === 0 && searchQuery.trim().length === 0) return null;
4652
- const result = {};
4653
- for (const [columnId, cfg] of Object.entries(filterConfigs)) {
4654
- if (cfg.type === "compound" && cfg.subFilters) {
4655
- for (const sub of cfg.subFilters) {
4656
- const s = computeColumnStats(filteredRows, sub.field, {
4657
- type: sub.type,
4658
- label: sub.label,
4659
- options: sub.options
4660
- });
4661
- if (s) result[`${columnId}:${sub.key}`] = s;
4452
+ case "text": {
4453
+ const seen = /* @__PURE__ */ new Set();
4454
+ const samples = [];
4455
+ for (const row of rows) {
4456
+ const val = getField(row, fieldName);
4457
+ if (typeof val === "string" && val.length > 0) {
4458
+ if (!seen.has(val)) {
4459
+ seen.add(val);
4460
+ if (samples.length < 3) samples.push(val);
4461
+ }
4662
4462
  }
4663
- } else {
4664
- const s = computeColumnStats(filteredRows, cfg.fieldName ?? columnId, cfg);
4665
- if (s) result[columnId] = s;
4666
4463
  }
4464
+ if (seen.size === 0) return null;
4465
+ return { type: "text", samples, totalUnique: seen.size };
4667
4466
  }
4668
- return result;
4669
- }, [filteredRows, filterConfigs, columnFilters, searchQuery]);
4670
- const columnFilterStats = React.useMemo(() => {
4671
- const result = {};
4672
- for (const [columnId, cfg] of Object.entries(filterConfigs)) {
4673
- if (cfg.type === "compound" && cfg.subFilters) {
4674
- for (const sub of cfg.subFilters) {
4675
- const key = `${columnId}:${sub.key}`;
4676
- const all = allColumnFilterStats[key];
4677
- if (!all) continue;
4678
- result[key] = { all, filtered: filteredColumnFilterStats?.[key] ?? null };
4467
+ case "date": {
4468
+ const valueField = `${fieldName}Value`;
4469
+ let min = Infinity;
4470
+ let max = -Infinity;
4471
+ for (const row of rows) {
4472
+ const val = getField(row, valueField);
4473
+ if (typeof val === "number" && val > 0) {
4474
+ if (val < min) min = val;
4475
+ if (val > max) max = val;
4679
4476
  }
4680
- } else {
4681
- const all = allColumnFilterStats[columnId];
4682
- if (!all) continue;
4683
- result[columnId] = { all, filtered: filteredColumnFilterStats?.[columnId] ?? null };
4684
4477
  }
4478
+ if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
4479
+ const toDateStr = (ts) => new Date(ts).toISOString().slice(0, 10);
4480
+ return { type: "date", min: toDateStr(min), max: toDateStr(max) };
4685
4481
  }
4686
- return result;
4687
- }, [allColumnFilterStats, filteredColumnFilterStats, filterConfigs]);
4688
- const totalPages = Math.max(1, Math.ceil(filteredRows.length / rowsPerPage));
4689
- const safePage = Math.min(storedPage, totalPages);
4690
- const page = safePage;
4691
- const pageNumbers = React.useMemo(
4692
- () => getVisiblePageNumbers(safePage, totalPages),
4693
- [safePage, totalPages]
4694
- );
4695
- const paginatedRows = React.useMemo(() => {
4696
- const start = (safePage - 1) * rowsPerPage;
4697
- return filteredRows.slice(start, start + rowsPerPage);
4698
- }, [filteredRows, rowsPerPage, safePage]);
4699
- const filteredIdSet = React.useMemo(
4700
- () => new Set(filteredRows.map(getRowId)),
4701
- [filteredRows, getRowId]
4702
- );
4703
- const selectedRowId = storedSelectedRowId && filteredIdSet.has(storedSelectedRowId) ? storedSelectedRowId : null;
4704
- const selectedIds = React.useMemo(() => {
4705
- const pruned = new Set([...storedSelectedIds].filter((id) => filteredIdSet.has(id)));
4706
- return pruned.size === storedSelectedIds.size ? storedSelectedIds : pruned;
4707
- }, [storedSelectedIds, filteredIdSet]);
4708
- useIsomorphicLayoutEffect(() => {
4709
- const state = store.getState();
4710
- if (state.page !== safePage) state.setPage(safePage);
4711
- if (state.selectedRowId !== selectedRowId) state.setSelectedRowId(selectedRowId);
4712
- if (state.selectedIds !== selectedIds) state.setSelectedIds(selectedIds);
4713
- }, [store, storedPage, safePage, storedSelectedRowId, selectedRowId, storedSelectedIds, selectedIds]);
4714
- const pageIds = React.useMemo(() => new Set(paginatedRows.map(getRowId)), [paginatedRows, getRowId]);
4715
- const selectedOnPage = React.useMemo(
4716
- () => [...selectedIds].filter((id) => pageIds.has(id)).length,
4717
- [selectedIds, pageIds]
4482
+ case "enum": {
4483
+ const counts = {};
4484
+ if (config.options) {
4485
+ for (const opt of config.options) counts[opt.value] = 0;
4486
+ }
4487
+ for (const row of rows) {
4488
+ const val = getField(row, fieldName);
4489
+ const key = typeof val === "boolean" ? String(val) : typeof val === "string" ? val : null;
4490
+ if (key !== null && key in counts) counts[key]++;
4491
+ }
4492
+ return { type: "enum", counts };
4493
+ }
4494
+ default:
4495
+ return null;
4496
+ }
4497
+ }
4498
+ function computeFilterStats(rows, filterConfigs) {
4499
+ const result = {};
4500
+ for (const [columnId, cfg] of Object.entries(filterConfigs)) {
4501
+ if (cfg.type === "compound" && cfg.subFilters) {
4502
+ for (const sub of cfg.subFilters) {
4503
+ const s = computeColumnStats(rows, sub.field, {
4504
+ type: sub.type,
4505
+ label: sub.label,
4506
+ options: sub.options
4507
+ });
4508
+ if (s) result[`${columnId}:${sub.key}`] = s;
4509
+ }
4510
+ } else {
4511
+ const s = computeColumnStats(rows, cfg.fieldName ?? columnId, cfg);
4512
+ if (s) result[columnId] = s;
4513
+ }
4514
+ }
4515
+ return result;
4516
+ }
4517
+ function useColumnFilterStats(rows, filteredRows, filterConfigs, isFiltered) {
4518
+ const allStats = React.useMemo(() => computeFilterStats(rows, filterConfigs), [rows, filterConfigs]);
4519
+ const filteredStats = React.useMemo(
4520
+ () => isFiltered ? computeFilterStats(filteredRows, filterConfigs) : null,
4521
+ [filteredRows, filterConfigs, isFiltered]
4718
4522
  );
4719
- const allOnPageSelected = pageIds.size > 0 && selectedOnPage === pageIds.size;
4720
- const headerCheckState = allOnPageSelected ? true : selectedOnPage > 0 ? "indeterminate" : false;
4523
+ return React.useMemo(() => {
4524
+ const result = {};
4525
+ for (const [key, all] of Object.entries(allStats)) {
4526
+ result[key] = { all, filtered: filteredStats?.[key] ?? null };
4527
+ }
4528
+ return result;
4529
+ }, [allStats, filteredStats]);
4530
+ }
4531
+ function gapColumnId(afterColumnId) {
4532
+ return `__gap_after_${afterColumnId ?? "start"}`;
4533
+ }
4534
+ function computeColumnGaps(orderedColumnIds, columnVisibility) {
4535
+ const gaps = [];
4536
+ let lastVisibleId = null;
4537
+ let pendingHidden = [];
4538
+ for (const columnId of orderedColumnIds) {
4539
+ if (columnVisibility[columnId] === false) {
4540
+ pendingHidden.push(columnId);
4541
+ } else {
4542
+ if (pendingHidden.length > 0) {
4543
+ gaps.push({ afterColumnId: lastVisibleId, hiddenIds: pendingHidden });
4544
+ pendingHidden = [];
4545
+ }
4546
+ lastVisibleId = columnId;
4547
+ }
4548
+ }
4549
+ if (pendingHidden.length > 0) {
4550
+ gaps.push({ afterColumnId: lastVisibleId, hiddenIds: pendingHidden });
4551
+ }
4552
+ return gaps;
4553
+ }
4554
+ const GAP_WIDTH = 32;
4555
+ function useColumnGaps({
4556
+ columnGroups,
4557
+ toggleableColumnIds,
4558
+ columnVisibility,
4559
+ columnMeta,
4560
+ widths
4561
+ }) {
4721
4562
  const columnGaps = React.useMemo(
4722
4563
  () => columnGroups ? computeColumnGaps(toggleableColumnIds, columnVisibility) : [],
4723
4564
  [columnGroups, toggleableColumnIds, columnVisibility]
@@ -4725,25 +4566,56 @@ function useTableOrchestration(config) {
4725
4566
  const extendedColumnMeta = React.useMemo(() => {
4726
4567
  if (columnGaps.length === 0) return columnMeta;
4727
4568
  const meta = { ...columnMeta };
4728
- for (const gap of columnGaps) {
4729
- meta[`__gap_after_${gap.afterColumnId ?? "start"}`] = { minW: 32 };
4730
- }
4569
+ for (const gap of columnGaps) meta[gapColumnId(gap.afterColumnId)] = { minW: GAP_WIDTH };
4731
4570
  return meta;
4732
4571
  }, [columnGaps, columnMeta]);
4733
4572
  const extendedWidths = React.useMemo(() => {
4734
4573
  if (columnGaps.length === 0) return widths;
4735
4574
  const merged = { ...widths };
4736
- for (const gap of columnGaps) {
4737
- merged[`__gap_after_${gap.afterColumnId ?? "start"}`] = 32;
4738
- }
4575
+ for (const gap of columnGaps) merged[gapColumnId(gap.afterColumnId)] = GAP_WIDTH;
4739
4576
  return merged;
4740
4577
  }, [widths, columnGaps]);
4741
- const hiddenColumnsCount = React.useMemo(() => {
4742
- return toggleableColumnIds.filter((id) => columnVisibility[id] === false).length;
4743
- }, [toggleableColumnIds, columnVisibility]);
4744
- const activeCustomFilterChip = activePreset.customFilterChip ?? null;
4745
- selectedIdsRef.current = selectedIds;
4746
- headerCheckStateRef.current = headerCheckState;
4578
+ return { columnGaps, extendedColumnMeta, extendedWidths };
4579
+ }
4580
+ function useFilteredRows({
4581
+ rows,
4582
+ activePreset,
4583
+ searchQuery,
4584
+ columnFilters,
4585
+ currentSort,
4586
+ filterRow,
4587
+ getSearchText,
4588
+ getSortValue
4589
+ }) {
4590
+ return React.useMemo(() => {
4591
+ const search = searchQuery.trim().toLowerCase();
4592
+ const filtered = rows.filter((row) => {
4593
+ if (activePreset.customFilter && !activePreset.customFilter(row)) return false;
4594
+ if (search.length > 0 && !getSearchText(row).includes(search)) return false;
4595
+ return columnFilters.every((filter) => filterRow(row, filter));
4596
+ });
4597
+ if (!currentSort) return filtered;
4598
+ const { columnId, direction } = currentSort;
4599
+ return [...filtered].sort((a, b) => {
4600
+ const aVal = getSortValue(a, columnId);
4601
+ const bVal = getSortValue(b, columnId);
4602
+ if (aVal < bVal) return direction === "asc" ? -1 : 1;
4603
+ if (aVal > bVal) return direction === "asc" ? 1 : -1;
4604
+ return 0;
4605
+ });
4606
+ }, [rows, searchQuery, columnFilters, currentSort, activePreset, getSearchText, filterRow, getSortValue]);
4607
+ }
4608
+ function useOrchestrationActions({
4609
+ store,
4610
+ viewPresets,
4611
+ filterConfigs,
4612
+ columnMeta,
4613
+ setWidths,
4614
+ defaultViewKey,
4615
+ paginatedRows,
4616
+ allOnPageSelected,
4617
+ getRowId
4618
+ }) {
4747
4619
  const handleSearchSubmit = React.useCallback(
4748
4620
  (e) => {
4749
4621
  e.preventDefault();
@@ -4879,6 +4751,234 @@ function useTableOrchestration(config) {
4879
4751
  (v) => store.getState().setSelectedRowId(v),
4880
4752
  [store]
4881
4753
  );
4754
+ return {
4755
+ handleSearchSubmit,
4756
+ handleHeaderSort,
4757
+ handleViewChange,
4758
+ handleAddColumnFilter,
4759
+ handleOpenExistingFilter,
4760
+ handleRequestOpenHandled,
4761
+ handleRemoveColumnFilter,
4762
+ handleChangeColumnFilter,
4763
+ handleSetColumnFilter,
4764
+ handleHeaderFilterClick,
4765
+ handleHideColumn,
4766
+ handleResetColumns,
4767
+ handleRemoveSort,
4768
+ handleRemoveCustomFilter,
4769
+ handleClearAll,
4770
+ handleRefresh,
4771
+ handleToggleRowSelection,
4772
+ handleToggleHeaderCheck,
4773
+ handleExpandRow,
4774
+ handleExpandGap,
4775
+ handlePageChange,
4776
+ handleRowsPerPageChange,
4777
+ setSearchInput,
4778
+ setColumnVisibility,
4779
+ setDensity,
4780
+ setSelectedIds,
4781
+ setColumnsMenuOpen,
4782
+ setSelectedRowId
4783
+ };
4784
+ }
4785
+ const EMPTY_ID_SET = /* @__PURE__ */ new Set();
4786
+ function useResponsiveColumns(columnPriorities, wideBreakpoint, compactBreakpoint) {
4787
+ const hasPriorities = Boolean(columnPriorities && Object.keys(columnPriorities).length > 0);
4788
+ const [isWide, setIsWide] = React.useState(true);
4789
+ const [isCompact, setIsCompact] = React.useState(false);
4790
+ React.useEffect(() => {
4791
+ if (!hasPriorities || typeof window === "undefined") return;
4792
+ const mql = window.matchMedia(`(min-width: ${wideBreakpoint}px)`);
4793
+ const onChange = (e) => setIsWide(e.matches);
4794
+ setIsWide(mql.matches);
4795
+ mql.addEventListener("change", onChange);
4796
+ return () => mql.removeEventListener("change", onChange);
4797
+ }, [hasPriorities, wideBreakpoint]);
4798
+ React.useEffect(() => {
4799
+ if (typeof window === "undefined") return;
4800
+ const mql = window.matchMedia(`(max-width: ${compactBreakpoint - 1}px)`);
4801
+ const onChange = (e) => setIsCompact(e.matches);
4802
+ setIsCompact(mql.matches);
4803
+ mql.addEventListener("change", onChange);
4804
+ return () => mql.removeEventListener("change", onChange);
4805
+ }, [compactBreakpoint]);
4806
+ const responsiveHiddenColumnIds = React.useMemo(() => {
4807
+ if (isWide || !columnPriorities) return EMPTY_ID_SET;
4808
+ const hidden = /* @__PURE__ */ new Set();
4809
+ for (const [id, priority] of Object.entries(columnPriorities)) {
4810
+ if (priority === 3) hidden.add(id);
4811
+ }
4812
+ return hidden;
4813
+ }, [isWide, columnPriorities]);
4814
+ return { responsiveHiddenColumnIds, isCompact };
4815
+ }
4816
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
4817
+ function useTableOrchestration(config) {
4818
+ const {
4819
+ rows,
4820
+ filterConfigs,
4821
+ columnMeta,
4822
+ toggleableColumnIds,
4823
+ rowsPerPageOptions,
4824
+ viewPresets,
4825
+ defaultViewKey,
4826
+ columnGroups,
4827
+ initialColumnVisibility,
4828
+ initialColumnWidths,
4829
+ columnPriorities,
4830
+ wideBreakpoint = 1280,
4831
+ compactBreakpoint = 768
4832
+ } = config;
4833
+ const { getRowId, filterRow, getSortValue, getSearchText } = config;
4834
+ const [store] = React.useState(
4835
+ () => createTableOrchestrationStore({
4836
+ defaultViewKey,
4837
+ initialColumnVisibility,
4838
+ initialRowsPerPage: rowsPerPageOptions[0]
4839
+ })
4840
+ );
4841
+ const searchInput = zustand.useStore(store, (s) => s.searchInput);
4842
+ const searchQuery = zustand.useStore(store, (s) => s.searchQuery);
4843
+ const columnFilters = zustand.useStore(store, (s) => s.columnFilters);
4844
+ const newestFilterColumnId = zustand.useStore(store, (s) => s.newestFilterColumnId);
4845
+ const requestOpenFilterId = zustand.useStore(store, (s) => s.requestOpenFilterId);
4846
+ const currentSort = zustand.useStore(store, (s) => s.currentSort);
4847
+ const columnVisibility = zustand.useStore(store, (s) => s.columnVisibility);
4848
+ const storedPage = zustand.useStore(store, (s) => s.page);
4849
+ const rowsPerPage = zustand.useStore(store, (s) => s.rowsPerPage);
4850
+ const density = zustand.useStore(store, (s) => s.density);
4851
+ const isRefreshing = zustand.useStore(store, (s) => s.isRefreshing);
4852
+ const storedSelectedIds = zustand.useStore(store, (s) => s.selectedIds);
4853
+ const activeView = zustand.useStore(store, (s) => s.activeView);
4854
+ const columnsMenuOpen = zustand.useStore(store, (s) => s.columnsMenuOpen);
4855
+ const storedSelectedRowId = zustand.useStore(store, (s) => s.selectedRowId);
4856
+ const { responsiveHiddenColumnIds, isCompact } = useResponsiveColumns(
4857
+ columnPriorities,
4858
+ wideBreakpoint,
4859
+ compactBreakpoint
4860
+ );
4861
+ const selectedIdsRef = React.useRef(storedSelectedIds);
4862
+ const headerCheckStateRef = React.useRef(false);
4863
+ const sortState = React.useMemo(() => {
4864
+ if (!currentSort) return {};
4865
+ return { [currentSort.columnId]: currentSort.direction };
4866
+ }, [currentSort]);
4867
+ const { widths, onPointerDown, setWidths } = useColumnResize(columnMeta, initialColumnWidths);
4868
+ const activePreset = React.useMemo(() => {
4869
+ if (activeView === "custom") {
4870
+ return {
4871
+ key: "custom",
4872
+ label: "Custom",
4873
+ filters: [],
4874
+ sort: null,
4875
+ columnVisibility: {}
4876
+ };
4877
+ }
4878
+ return viewPresets.find((v) => v.key === activeView) ?? viewPresets[0];
4879
+ }, [activeView, viewPresets]);
4880
+ const filteredRows = useFilteredRows({
4881
+ rows,
4882
+ activePreset,
4883
+ searchQuery,
4884
+ columnFilters,
4885
+ currentSort,
4886
+ filterRow,
4887
+ getSearchText,
4888
+ getSortValue
4889
+ });
4890
+ const columnFilterStats = useColumnFilterStats(
4891
+ rows,
4892
+ filteredRows,
4893
+ filterConfigs,
4894
+ columnFilters.length > 0 || searchQuery.trim().length > 0
4895
+ );
4896
+ const totalPages = Math.max(1, Math.ceil(filteredRows.length / rowsPerPage));
4897
+ const safePage = Math.min(storedPage, totalPages);
4898
+ const page = safePage;
4899
+ const pageNumbers = React.useMemo(
4900
+ () => getVisiblePageNumbers(safePage, totalPages),
4901
+ [safePage, totalPages]
4902
+ );
4903
+ const paginatedRows = React.useMemo(() => {
4904
+ const start = (safePage - 1) * rowsPerPage;
4905
+ return filteredRows.slice(start, start + rowsPerPage);
4906
+ }, [filteredRows, rowsPerPage, safePage]);
4907
+ const filteredIdSet = React.useMemo(
4908
+ () => new Set(filteredRows.map(getRowId)),
4909
+ [filteredRows, getRowId]
4910
+ );
4911
+ const selectedRowId = storedSelectedRowId && filteredIdSet.has(storedSelectedRowId) ? storedSelectedRowId : null;
4912
+ const selectedIds = React.useMemo(() => {
4913
+ const pruned = new Set([...storedSelectedIds].filter((id) => filteredIdSet.has(id)));
4914
+ return pruned.size === storedSelectedIds.size ? storedSelectedIds : pruned;
4915
+ }, [storedSelectedIds, filteredIdSet]);
4916
+ useIsomorphicLayoutEffect(() => {
4917
+ const state = store.getState();
4918
+ if (state.page !== safePage) state.setPage(safePage);
4919
+ if (state.selectedRowId !== selectedRowId) state.setSelectedRowId(selectedRowId);
4920
+ if (state.selectedIds !== selectedIds) state.setSelectedIds(selectedIds);
4921
+ }, [store, storedPage, safePage, storedSelectedRowId, selectedRowId, storedSelectedIds, selectedIds]);
4922
+ const pageIds = React.useMemo(() => new Set(paginatedRows.map(getRowId)), [paginatedRows, getRowId]);
4923
+ const selectedOnPage = React.useMemo(
4924
+ () => [...selectedIds].filter((id) => pageIds.has(id)).length,
4925
+ [selectedIds, pageIds]
4926
+ );
4927
+ const allOnPageSelected = pageIds.size > 0 && selectedOnPage === pageIds.size;
4928
+ const headerCheckState = allOnPageSelected ? true : selectedOnPage > 0 ? "indeterminate" : false;
4929
+ const { columnGaps, extendedColumnMeta, extendedWidths } = useColumnGaps({
4930
+ columnGroups,
4931
+ toggleableColumnIds,
4932
+ columnVisibility,
4933
+ columnMeta,
4934
+ widths
4935
+ });
4936
+ const hiddenColumnsCount = React.useMemo(() => {
4937
+ return toggleableColumnIds.filter((id) => columnVisibility[id] === false).length;
4938
+ }, [toggleableColumnIds, columnVisibility]);
4939
+ const activeCustomFilterChip = activePreset.customFilterChip ?? null;
4940
+ selectedIdsRef.current = selectedIds;
4941
+ headerCheckStateRef.current = headerCheckState;
4942
+ const {
4943
+ handleSearchSubmit,
4944
+ handleHeaderSort,
4945
+ handleViewChange,
4946
+ handleAddColumnFilter,
4947
+ handleOpenExistingFilter,
4948
+ handleRequestOpenHandled,
4949
+ handleRemoveColumnFilter,
4950
+ handleChangeColumnFilter,
4951
+ handleSetColumnFilter,
4952
+ handleHeaderFilterClick,
4953
+ handleHideColumn,
4954
+ handleResetColumns,
4955
+ handleRemoveSort,
4956
+ handleRemoveCustomFilter,
4957
+ handleClearAll,
4958
+ handleRefresh,
4959
+ handleToggleRowSelection,
4960
+ handleToggleHeaderCheck,
4961
+ handleExpandRow,
4962
+ handleExpandGap,
4963
+ handlePageChange,
4964
+ handleRowsPerPageChange,
4965
+ setSearchInput,
4966
+ setColumnVisibility,
4967
+ setDensity,
4968
+ setSelectedIds,
4969
+ setColumnsMenuOpen,
4970
+ setSelectedRowId
4971
+ } = useOrchestrationActions({
4972
+ store,
4973
+ viewPresets,
4974
+ filterConfigs,
4975
+ columnMeta,
4976
+ setWidths,
4977
+ defaultViewKey,
4978
+ paginatedRows,
4979
+ allOnPageSelected,
4980
+ getRowId
4981
+ });
4882
4982
  const getSelectedIds = React.useCallback(() => selectedIdsRef.current, []);
4883
4983
  const getHeaderCheckState = React.useCallback(() => headerCheckStateRef.current, []);
4884
4984
  return {
@@ -5444,6 +5544,7 @@ exports.displayTimeZone = displayTimeZone;
5444
5544
  exports.formatDateParts = formatDateParts;
5445
5545
  exports.formatEnumLabel = formatEnumLabel;
5446
5546
  exports.formatLiveTimestamp = formatLiveTimestamp;
5547
+ exports.gapColumnId = gapColumnId;
5447
5548
  exports.getCompoundValue = getCompoundValue;
5448
5549
  exports.getDateRange = getDateRange;
5449
5550
  exports.getNumberRange = getNumberRange;