@12-apps/ui 6.13.1 → 6.13.2

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.
Files changed (74) hide show
  1. package/dist/_internal/components/data-display/Carousel/Carousel.js +2 -2
  2. package/dist/_internal/components/data-display/Chart/Chart.js +3 -3
  3. package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
  4. package/dist/_internal/components/data-display/Chart/chart-renderers.js +2 -2
  5. package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
  6. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +1 -1
  7. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +13 -12
  8. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +18 -17
  9. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +3 -3
  10. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +1 -1
  11. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +9 -9
  12. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +5 -5
  13. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +3 -3
  14. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +4 -4
  15. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +2 -2
  16. package/dist/_internal/components/data-display/DataViews/data-views-selection.js +1 -1
  17. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +2 -2
  18. package/dist/_internal/components/data-display/DataViews/data-views-writers.js +9 -0
  19. package/dist/_internal/components/data-display/DataViews/data-views-writers.js.map +1 -0
  20. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +3 -2
  21. package/dist/_internal/components/data-display/index.js +14 -14
  22. package/dist/_internal/components/index.js +14 -14
  23. package/dist/charts.js +11 -11
  24. package/dist/{chunk-BV5XTGKB.js → chunk-2UCJJACD.js} +7 -9
  25. package/dist/chunk-2UCJJACD.js.map +1 -0
  26. package/dist/{chunk-IN642YHK.js → chunk-4VNUOSTQ.js} +2 -2
  27. package/dist/{chunk-XPS7FWZS.js → chunk-4ZEU22L2.js} +4 -4
  28. package/dist/{chunk-NC2AATCC.js → chunk-6PFBYCBU.js} +2 -2
  29. package/dist/{chunk-CXA6UNFF.js → chunk-AP3PVR3T.js} +5 -5
  30. package/dist/{chunk-XULTDO56.js → chunk-BVGR7OYA.js} +4 -4
  31. package/dist/{chunk-JKLWHENB.js → chunk-HNDUCGM3.js} +3 -3
  32. package/dist/{chunk-NPEKJXVV.js → chunk-HRJ27X5B.js} +4 -4
  33. package/dist/chunk-LLZWB32X.js +36 -0
  34. package/dist/chunk-LLZWB32X.js.map +1 -0
  35. package/dist/{chunk-ZHPKFV7Y.js → chunk-N6JGYVRY.js} +2 -2
  36. package/dist/{chunk-JPDKRGYI.js → chunk-PDGAFQ6Z.js} +3 -3
  37. package/dist/{chunk-T4BFDZGU.js → chunk-Q4ITS2EP.js} +6 -6
  38. package/dist/{chunk-GOWBYHYW.js → chunk-R6SBP5M5.js} +4 -4
  39. package/dist/{chunk-DEOC5OI7.js → chunk-RRYQG5Y6.js} +3 -3
  40. package/dist/{chunk-WLCVOQCJ.js → chunk-RUO4D4N6.js} +3 -3
  41. package/dist/chunk-U5FOJBLG.js +19 -0
  42. package/dist/chunk-U5FOJBLG.js.map +1 -0
  43. package/dist/{chunk-BZTFPSWT.js → chunk-XEE7PLDR.js} +4 -4
  44. package/dist/data-display/CardKit.js +3 -3
  45. package/dist/data-display/Carousel.js +2 -2
  46. package/dist/data-display/Chart.js +11 -11
  47. package/dist/data-display/DataGrid.js +1 -1
  48. package/dist/data-display/DataViews.js +19 -18
  49. package/dist/types/components/data-display/DataViews/data-views-selection.d.ts +31 -2
  50. package/dist/types/components/data-display/DataViews/data-views-selection.d.ts.map +1 -1
  51. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts +20 -0
  52. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts.map +1 -0
  53. package/dist/types/components/data-display/DataViews/use-data-views-state.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/src/components/data-display/DataViews/data-views-selection.ts +61 -6
  56. package/src/components/data-display/DataViews/data-views-writers.ts +45 -0
  57. package/src/components/data-display/DataViews/use-data-views-state.ts +4 -11
  58. package/dist/chunk-BV5XTGKB.js.map +0 -1
  59. package/dist/chunk-WTOTWP6A.js +0 -27
  60. package/dist/chunk-WTOTWP6A.js.map +0 -1
  61. /package/dist/{chunk-IN642YHK.js.map → chunk-4VNUOSTQ.js.map} +0 -0
  62. /package/dist/{chunk-XPS7FWZS.js.map → chunk-4ZEU22L2.js.map} +0 -0
  63. /package/dist/{chunk-NC2AATCC.js.map → chunk-6PFBYCBU.js.map} +0 -0
  64. /package/dist/{chunk-CXA6UNFF.js.map → chunk-AP3PVR3T.js.map} +0 -0
  65. /package/dist/{chunk-XULTDO56.js.map → chunk-BVGR7OYA.js.map} +0 -0
  66. /package/dist/{chunk-JKLWHENB.js.map → chunk-HNDUCGM3.js.map} +0 -0
  67. /package/dist/{chunk-NPEKJXVV.js.map → chunk-HRJ27X5B.js.map} +0 -0
  68. /package/dist/{chunk-ZHPKFV7Y.js.map → chunk-N6JGYVRY.js.map} +0 -0
  69. /package/dist/{chunk-JPDKRGYI.js.map → chunk-PDGAFQ6Z.js.map} +0 -0
  70. /package/dist/{chunk-T4BFDZGU.js.map → chunk-Q4ITS2EP.js.map} +0 -0
  71. /package/dist/{chunk-GOWBYHYW.js.map → chunk-R6SBP5M5.js.map} +0 -0
  72. /package/dist/{chunk-DEOC5OI7.js.map → chunk-RRYQG5Y6.js.map} +0 -0
  73. /package/dist/{chunk-WLCVOQCJ.js.map → chunk-RUO4D4N6.js.map} +0 -0
  74. /package/dist/{chunk-BZTFPSWT.js.map → chunk-XEE7PLDR.js.map} +0 -0
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  DataViewsTableBase
3
- } from "../chunk-DEOC5OI7.js";
3
+ } from "../chunk-RRYQG5Y6.js";
4
4
  import "../chunk-LS42VJAI.js";
5
5
  import {
6
6
  DataViewsGrid
7
- } from "../chunk-JKLWHENB.js";
7
+ } from "../chunk-HNDUCGM3.js";
8
8
  import {
9
9
  BaseListCard
10
10
  } from "../chunk-RDYBS45D.js";
@@ -16,31 +16,32 @@ import {
16
16
  BaseCard
17
17
  } from "../chunk-6U7NA74A.js";
18
18
  import "../chunk-MQUU2XZB.js";
19
+ import "../chunk-ZOLAYP2D.js";
19
20
  import {
20
21
  DragContainerProvider,
21
22
  DropIndicator,
22
23
  useDragItem
23
24
  } from "../chunk-JCJGJX7V.js";
24
- import "../chunk-ZOLAYP2D.js";
25
- import "../chunk-T4BFDZGU.js";
26
- import "../chunk-IN642YHK.js";
25
+ import "../chunk-Q4ITS2EP.js";
26
+ import "../chunk-4VNUOSTQ.js";
27
27
  import "../chunk-S7CAXRRP.js";
28
- import "../chunk-WLCVOQCJ.js";
28
+ import "../chunk-RUO4D4N6.js";
29
29
  import "../chunk-5WNQJ5KS.js";
30
30
  import "../chunk-ECIGNYSI.js";
31
- import "../chunk-ZHPKFV7Y.js";
32
- import "../chunk-NC2AATCC.js";
31
+ import "../chunk-N6JGYVRY.js";
32
+ import "../chunk-6PFBYCBU.js";
33
33
  import "../chunk-QQE2PVLO.js";
34
34
  import {
35
35
  DataViewsBoard
36
36
  } from "../chunk-3DQP6OPC.js";
37
- import "../chunk-BZTFPSWT.js";
37
+ import "../chunk-XEE7PLDR.js";
38
+ import "../chunk-FV36LFPX.js";
38
39
  import "../chunk-F4RR4666.js";
39
40
  import "../chunk-CBOB5AV6.js";
40
41
  import "../chunk-VR5HDUBK.js";
41
42
  import "../chunk-TQ6FPOPD.js";
42
43
  import "../chunk-IAGOUTI6.js";
43
- import "../chunk-FV36LFPX.js";
44
+ import "../chunk-AWTERXCW.js";
44
45
  import "../chunk-2I5B5NNT.js";
45
46
  import "../chunk-A7Q5AQVD.js";
46
47
  import "../chunk-OGGHD64H.js";
@@ -53,11 +54,6 @@ import {
53
54
  import {
54
55
  DEFAULT_CELL_WIDTH
55
56
  } from "../chunk-6VP2L3DJ.js";
56
- import "../chunk-XPS7FWZS.js";
57
- import "../chunk-U7SO3N42.js";
58
- import "../chunk-UHXNEXOC.js";
59
- import "../chunk-5M4TYFR3.js";
60
- import "../chunk-AWTERXCW.js";
61
57
  import "../chunk-WXBBWSNU.js";
62
58
  import "../chunk-NQCYVYP3.js";
63
59
  import "../chunk-HGBZZDP6.js";
@@ -69,18 +65,23 @@ import {
69
65
  cardScaleForZoom
70
66
  } from "../chunk-ZPIMPOHV.js";
71
67
  import "../chunk-QTPKACVO.js";
68
+ import "../chunk-4ZEU22L2.js";
69
+ import "../chunk-UHXNEXOC.js";
70
+ import "../chunk-U7SO3N42.js";
71
+ import "../chunk-5M4TYFR3.js";
72
72
  import "../chunk-HR7YTBR7.js";
73
73
  import "../chunk-DZEC6RFM.js";
74
- import "../chunk-BV5XTGKB.js";
74
+ import "../chunk-2UCJJACD.js";
75
75
  import "../chunk-4QQVIGRG.js";
76
76
  import "../chunk-YGQX7BNI.js";
77
77
  import "../chunk-IMLIIW77.js";
78
- import "../chunk-WTOTWP6A.js";
78
+ import "../chunk-LLZWB32X.js";
79
79
  import {
80
80
  DataViewsScopeTabs,
81
81
  assertNoScopePillOverlap,
82
82
  resolveScope
83
83
  } from "../chunk-LYKV3C7B.js";
84
+ import "../chunk-U5FOJBLG.js";
84
85
  import "../chunk-5VZPVDUF.js";
85
86
  import {
86
87
  CARD_ASPECT_RATIOS,
@@ -169,8 +170,8 @@ import "../chunk-X5MPHXG5.js";
169
170
  import "../chunk-DLJGBJZW.js";
170
171
  import "../chunk-KZLLTOB2.js";
171
172
  import "../chunk-E5LWJDI3.js";
172
- import "../chunk-5E3XZAED.js";
173
173
  import "../chunk-G3E66C6J.js";
174
+ import "../chunk-5E3XZAED.js";
174
175
  import "../chunk-SNCSGLJ6.js";
175
176
  import "../chunk-TCAB2ERD.js";
176
177
  import "../chunk-NUGT7LMR.js";
@@ -4,7 +4,7 @@ interface DataViewsSelection<T> {
4
4
  setSelectedIds: (ids: Set<string | number>) => void;
5
5
  /** Add/remove ONE id — what a card's or board card's checkbox calls. */
6
6
  toggleId: (id: string | number) => void;
7
- /** The selected rows, narrowed to what is currently rendered. */
7
+ /** The selected rows, INCLUDING ones the current page no longer renders. */
8
8
  selectedRows: T[];
9
9
  clearSelection: () => void;
10
10
  selectAll: () => void;
@@ -18,7 +18,36 @@ interface DataViewsSelection<T> {
18
18
  * because `toggleId` needs a functional update: two cards toggled in the same
19
19
  * tick would otherwise both read the pre-render set and the second would drop
20
20
  * the first's change.
21
+ *
22
+ * ## A selection has to survive PAGING
23
+ *
24
+ * `selectedIds` always did — nothing clears it when the page changes — but
25
+ * `selectedRows` was `matched.filter(...)`, and in SERVER mode `matched` is one
26
+ * page. So ticking three rows, paging, and ticking two more left the checkboxes
27
+ * claiming five and the bulk action receiving two, silently, with the other
28
+ * three still ticked on a page nobody was looking at.
29
+ *
30
+ * That is the worst shape a bug can have here: the UI and the write disagree,
31
+ * neither says so, and the rows that did not move are exactly the ones out of
32
+ * sight. So a row is REMEMBERED for as long as its id is selected, and
33
+ * `selectedRows` answers from that memory for anything the current page does
34
+ * not hold.
35
+ *
36
+ * **Server mode only** ({@link SelectionOptions.rememberOffPage}), and the
37
+ * distinction is not a hedge. In client mode `matched` is the whole filtered
38
+ * result, so nothing is ever off-page and there is nothing to remember — while
39
+ * narrowing the selection as the FILTER narrows is the behaviour a client-mode
40
+ * grid should keep. Remembering there would mean a bulk action reaching rows
41
+ * the operator has just filtered away.
21
42
  */
22
- export declare function useSelection<T>(matched: T[], getRowId: (row: T) => string | number): DataViewsSelection<T>;
43
+ export interface SelectionOptions {
44
+ /**
45
+ * Keep rows the current page no longer renders.
46
+ *
47
+ * True in server mode, where `matched` is a page rather than the result.
48
+ */
49
+ rememberOffPage?: boolean;
50
+ }
51
+ export declare function useSelection<T>(matched: T[], getRowId: (row: T) => string | number, options?: SelectionOptions): DataViewsSelection<T>;
23
52
  export {};
24
53
  //# sourceMappingURL=data-views-selection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-views-selection.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-selection.ts"],"names":[],"mappings":"AAIA,+EAA+E;AAC/E,UAAU,kBAAkB,CAAC,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACpD,wEAAwE;IACxE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACxC,iEAAiE;IACjE,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,OAAO,EAAE,CAAC,EAAE,EACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,GACpC,kBAAkB,CAAC,CAAC,CAAC,CAoBvB"}
1
+ {"version":3,"file":"data-views-selection.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-selection.ts"],"names":[],"mappings":"AAIA,+EAA+E;AAC/E,UAAU,kBAAkB,CAAC,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACpD,wEAAwE;IACxE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACxC,4EAA4E;IAC5E,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,OAAO,EAAE,CAAC,EAAE,EACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,EACrC,OAAO,GAAE,gBAAqB,GAC7B,kBAAkB,CAAC,CAAC,CAAC,CA4CvB"}
@@ -0,0 +1,20 @@
1
+ import { type Dispatch, type SetStateAction } from "react";
2
+ import { type DataViewState, type DataViewStatePatch } from "./data-views-types";
3
+ /**
4
+ * The two ways a DataViews state is WRITTEN, spelled once.
5
+ *
6
+ * Both are `setState` with a shape — a whole-state patch, and the one column
7
+ * toggle that has to merge into a list rather than replace a field. They sat
8
+ * inline in `useDataViewsState`, which is permanently at the size gate's
9
+ * ceiling; here they sit next to each other, where the fact that
10
+ * `toggleColumn` is just a `patch` with a merge rule is visible.
11
+ *
12
+ * Extracted for the same reason `useSelection` was: that hook is a COMPOSITION
13
+ * of the pieces below it, and every piece that can be named on its own leaves
14
+ * it reading as what it assembles rather than as how each part works.
15
+ */
16
+ export declare function useStateWriters(setState: Dispatch<SetStateAction<DataViewState>>): {
17
+ patch: (next: DataViewStatePatch) => void;
18
+ toggleColumn: (id: string, visible: boolean) => void;
19
+ };
20
+ //# sourceMappingURL=data-views-writers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-views-writers.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-writers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAcjF;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,GAAG;IAClF,KAAK,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACtD,CAUA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-data-views-state.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/use-data-views-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-data-views-state.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/use-data-views-state.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.13.1",
3
+ "version": "6.13.2",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useMemo, useState } from "react";
3
+ import { useMemo, useRef, useState } from "react";
4
4
 
5
5
  /** The multi-select model, identical in every layout (table, cards, board). */
6
6
  interface DataViewsSelection<T> {
@@ -8,7 +8,7 @@ interface DataViewsSelection<T> {
8
8
  setSelectedIds: (ids: Set<string | number>) => void;
9
9
  /** Add/remove ONE id — what a card's or board card's checkbox calls. */
10
10
  toggleId: (id: string | number) => void;
11
- /** The selected rows, narrowed to what is currently rendered. */
11
+ /** The selected rows, INCLUDING ones the current page no longer renders. */
12
12
  selectedRows: T[];
13
13
  clearSelection: () => void;
14
14
  selectAll: () => void;
@@ -23,16 +23,71 @@ interface DataViewsSelection<T> {
23
23
  * because `toggleId` needs a functional update: two cards toggled in the same
24
24
  * tick would otherwise both read the pre-render set and the second would drop
25
25
  * the first's change.
26
+ *
27
+ * ## A selection has to survive PAGING
28
+ *
29
+ * `selectedIds` always did — nothing clears it when the page changes — but
30
+ * `selectedRows` was `matched.filter(...)`, and in SERVER mode `matched` is one
31
+ * page. So ticking three rows, paging, and ticking two more left the checkboxes
32
+ * claiming five and the bulk action receiving two, silently, with the other
33
+ * three still ticked on a page nobody was looking at.
34
+ *
35
+ * That is the worst shape a bug can have here: the UI and the write disagree,
36
+ * neither says so, and the rows that did not move are exactly the ones out of
37
+ * sight. So a row is REMEMBERED for as long as its id is selected, and
38
+ * `selectedRows` answers from that memory for anything the current page does
39
+ * not hold.
40
+ *
41
+ * **Server mode only** ({@link SelectionOptions.rememberOffPage}), and the
42
+ * distinction is not a hedge. In client mode `matched` is the whole filtered
43
+ * result, so nothing is ever off-page and there is nothing to remember — while
44
+ * narrowing the selection as the FILTER narrows is the behaviour a client-mode
45
+ * grid should keep. Remembering there would mean a bulk action reaching rows
46
+ * the operator has just filtered away.
26
47
  */
48
+ export interface SelectionOptions {
49
+ /**
50
+ * Keep rows the current page no longer renders.
51
+ *
52
+ * True in server mode, where `matched` is a page rather than the result.
53
+ */
54
+ rememberOffPage?: boolean;
55
+ }
56
+
27
57
  export function useSelection<T>(
28
58
  matched: T[],
29
59
  getRowId: (row: T) => string | number,
60
+ options: SelectionOptions = {},
30
61
  ): DataViewsSelection<T> {
31
62
  const [selectedIds, setSelectedIds] = useState<Set<string | number>>(new Set());
32
- const selectedRows = useMemo(
33
- () => matched.filter((row) => selectedIds.has(getRowId(row))),
34
- [matched, selectedIds, getRowId],
35
- );
63
+ // Rows seen while selected, so a selection outlives the page that made it.
64
+ // A ref rather than state: it is a CACHE derived from what has been rendered,
65
+ // and holding it in state would re-render on every page load to store rows
66
+ // nothing is waiting for.
67
+ const remembered = useRef(new Map<string | number, T>());
68
+
69
+ const selectedRows = useMemo(() => {
70
+ const onPage = matched.filter((row) => selectedIds.has(getRowId(row)));
71
+ if (!options.rememberOffPage) return onPage;
72
+
73
+ const cache = remembered.current;
74
+ // Refresh from what is on screen NOW: a row edited elsewhere and re-fetched
75
+ // must not be acted on through a stale snapshot from three pages ago.
76
+ for (const row of onPage) cache.set(getRowId(row), row);
77
+ // …and forget anything no longer selected, so the map cannot outgrow the
78
+ // selection it exists to describe.
79
+ for (const id of cache.keys()) if (!selectedIds.has(id)) cache.delete(id);
80
+
81
+ // On-page rows first, in the page's own order — unchanged from before this
82
+ // — then the remembered tail. Appending rather than interleaving keeps the
83
+ // familiar case byte-identical and makes the addition exactly the addition.
84
+ const onPageIds = new Set(onPage.map(getRowId));
85
+ const offPage = [...cache.entries()].flatMap(([id, row]) =>
86
+ onPageIds.has(id) ? [] : [row],
87
+ );
88
+ return [...onPage, ...offPage];
89
+ }, [matched, selectedIds, getRowId, options.rememberOffPage]);
90
+
36
91
  return {
37
92
  selectedIds,
38
93
  setSelectedIds,
@@ -0,0 +1,45 @@
1
+ "use client";
2
+
3
+ import { type Dispatch, type SetStateAction } from "react";
4
+
5
+ import { type DataViewState, type DataViewStatePatch } from "./data-views-types";
6
+
7
+ /**
8
+ * Add or remove one column id from the visible-columns list.
9
+ *
10
+ * Moved here with its only caller. A visible column is APPENDED rather than
11
+ * restored to its original index, which is what makes re-showing a column put
12
+ * it last — the behaviour the Exibir panel has always had.
13
+ */
14
+ function nextVisibleColumns(current: string[], id: string, visible: boolean): string[] {
15
+ const without = current.filter((colId) => colId !== id);
16
+ return visible ? [...without, id] : without;
17
+ }
18
+
19
+ /**
20
+ * The two ways a DataViews state is WRITTEN, spelled once.
21
+ *
22
+ * Both are `setState` with a shape — a whole-state patch, and the one column
23
+ * toggle that has to merge into a list rather than replace a field. They sat
24
+ * inline in `useDataViewsState`, which is permanently at the size gate's
25
+ * ceiling; here they sit next to each other, where the fact that
26
+ * `toggleColumn` is just a `patch` with a merge rule is visible.
27
+ *
28
+ * Extracted for the same reason `useSelection` was: that hook is a COMPOSITION
29
+ * of the pieces below it, and every piece that can be named on its own leaves
30
+ * it reading as what it assembles rather than as how each part works.
31
+ */
32
+ export function useStateWriters(setState: Dispatch<SetStateAction<DataViewState>>): {
33
+ patch: (next: DataViewStatePatch) => void;
34
+ toggleColumn: (id: string, visible: boolean) => void;
35
+ } {
36
+ return {
37
+ patch: (next) =>
38
+ setState((prev) => ({ ...prev, ...(typeof next === "function" ? next(prev) : next) })),
39
+ toggleColumn: (id, visible) =>
40
+ setState((prev) => ({
41
+ ...prev,
42
+ visibleColumns: nextVisibleColumns(prev.visibleColumns, id, visible),
43
+ })),
44
+ };
45
+ }
@@ -15,6 +15,7 @@ import { computeActiveFilterCount, defaultSortFields } from "./data-views-grid-h
15
15
  import { resolveScope, useScopeConfigChecks, type ScopeConfig } from "./data-views-scopes";
16
16
  import { serverDerived, useServerQuery } from "./data-views-server-query";
17
17
  import { useSelection } from "./data-views-selection";
18
+ import { useStateWriters } from "./data-views-writers";
18
19
  import {
19
20
  emptyViewState,
20
21
  type DataViewColumn,
@@ -168,12 +169,6 @@ function useMatchedRows<T extends Record<string, unknown>>(
168
169
  );
169
170
  }
170
171
 
171
- /** Add or remove one column id from the visible-columns list. */
172
- function nextVisibleColumns(current: string[], id: string, visible: boolean): string[] {
173
- const without = current.filter((colId) => colId !== id);
174
- return visible ? [...without, id] : without;
175
- }
176
-
177
172
  /**
178
173
  * Reconcile external state changes and surface every change to the caller:
179
174
  * - `appliedState` (a saved view) fully REPLACES the state — it is columns-
@@ -336,8 +331,7 @@ export function useDataViewsState<T extends Record<string, unknown>>({
336
331
 
337
332
  useStateSyncEffects(appliedState, syncState, state, onStateChange, setState);
338
333
 
339
- const patch = (next: DataViewStatePatch): void =>
340
- setState((prev) => ({ ...prev, ...(typeof next === "function" ? next(prev) : next) }));
334
+ const { patch, toggleColumn } = useStateWriters(setState);
341
335
  const matched = useMatchedRows(server, rows, columns, fields, rangeFields, state);
342
336
  // Resolved at READ time, every render: a stale deep link or a saved view naming
343
337
  // a scope that has since been removed falls back to the first declared scope
@@ -351,9 +345,8 @@ export function useDataViewsState<T extends Record<string, unknown>>({
351
345
  onRowClick,
352
346
  state.order,
353
347
  );
354
- const toggleColumn = (id: string, visible: boolean): void =>
355
- setState((prev) => ({ ...prev, visibleColumns: nextVisibleColumns(prev.visibleColumns, id, visible) }));
356
- const selection = useSelection(matched, getRowId);
348
+ // Server mode is where `matched` is a PAGE — see `useSelection` (FUT-942).
349
+ const selection = useSelection(matched, getRowId, { rememberOffPage: Boolean(server) });
357
350
  const resolvedSortFields = sortFields ?? defaultSortFields(columns);
358
351
 
359
352
  return {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/use-data-views-state.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { type GridColumn } from \"../DataGrid\";\nimport { type SortFieldDefinition } from \"../../layout/ContentToolbar\";\n\nimport { filterRows } from \"./data-views-filter\";\nimport {\n useColumnsWithActions,\n useRenderedColumns,\n type HideableColumn,\n} from \"./data-views-columns\";\nimport { computeActiveFilterCount, defaultSortFields } from \"./data-views-grid-helpers\";\nimport { resolveScope, useScopeConfigChecks, type ScopeConfig } from \"./data-views-scopes\";\nimport { serverDerived, useServerQuery } from \"./data-views-server-query\";\nimport { useSelection } from \"./data-views-selection\";\nimport {\n emptyViewState,\n type DataViewColumn,\n type DataViewServer,\n type DataViewState,\n type DataViewStatePatch,\n type DataViewSyncState,\n type FilterFieldConfig,\n type RangeFieldConfig,\n type DataViewQuery,\n type RangeValue,\n type RowAction,\n} from \"./data-views-types\";\n\nexport type { HideableColumn };\n\n/** Inputs the hook needs to own the view state + derived data. */\ninterface UseDataViewsStateArgs<T extends Record<string, unknown>> {\n rows: T[];\n columns: DataViewColumn<T>[];\n fields: FilterFieldConfig<T>[];\n rangeFields: RangeFieldConfig<T>[];\n getRowId: (row: T) => string | number;\n onRowClick?: (row: T) => void;\n appliedState?: DataViewState;\n /**\n * URL-driven controls (search/pills/ranges/sort) that re-apply on reference\n * change while preserving column visibility — see {@link DataViewSyncState}.\n */\n syncState?: DataViewSyncState;\n onStateChange?: (state: DataViewState) => void;\n testIdPrefix: string;\n sortFields?: SortFieldDefinition[];\n rowActions?: RowAction<T>[];\n rowActionsLeading?: (row: T) => React.ReactNode;\n /** Bespoke per-row menu rendered in the actions column instead of the auto kebab. */\n renderRowMenu?: (row: T) => React.ReactNode;\n /**\n * When present, the table is BACKEND-driven: `rows` is already the current\n * page (no in-browser filter/sort/paginate), and query changes re-fetch via\n * `server.onQueryChange`. Omit for the legacy client-side behaviour.\n */\n server?: DataViewServer;\n /**\n * The page-level partition rendered as tabs under the toolbar. Requires server\n * mode: a scope is applied at the BACKEND, never in the browser.\n */\n scopes?: ScopeConfig[];\n /**\n * The row field the scopes partition by, when the host knows it — used only to\n * reject the pill/scope clash at setup. See `assertNoScopePillOverlap`.\n */\n scopeFieldId?: string;\n}\n\n/** Everything the DataViewsGrid render needs; keeps the component itself thin. */\nexport interface DataViewsController<T extends Record<string, unknown>> {\n state: DataViewState;\n ranges: Record<string, RangeValue>;\n /**\n * Merge a partial into the view state.\n *\n * The UPDATER form is the one to reach for when the patch is derived from\n * state you are also changing — successive calls within one tick each see the\n * previous call's result, where the object form would have every caller build\n * its patch from the same render-scoped snapshot and the last write would win.\n */\n patch: (next: DataViewStatePatch) => void;\n matched: T[];\n gridColumns: GridColumn<T>[];\n /** Columns eligible for the show/hide menu (hideable, with a text header). */\n hideableColumns: HideableColumn[];\n /** Every column id in reading order — what the Colunas tab reorders. */\n columnOrder: string[];\n /** Toggle one column's visibility in the current view state. */\n toggleColumn: (id: string, visible: boolean) => void;\n selectedIds: Set<string | number>;\n setSelectedIds: (ids: Set<string | number>) => void;\n /**\n * Add/remove ONE id from the selection. The card and board bodies both need\n * it, so it lives here rather than being written out twice at the call sites —\n * one selection model, one toggle, identical in every layout.\n */\n toggleId: (id: string | number) => void;\n selectedRows: T[];\n clearSelection: () => void;\n selectAll: () => void;\n filterOpen: boolean;\n setFilterOpen: (open: boolean) => void;\n resolvedSortFields: SortFieldDefinition[];\n activeSortField: string;\n activeSortOrder: \"asc\" | \"desc\";\n activeFilterCount: number;\n /** True when the table is backend-driven (server mode). */\n serverMode: boolean;\n /** Server-mode pagination: 1-based current page (1 when client mode). */\n serverPage: number;\n /** Server-mode pagination: total pages (1 when client mode). */\n serverPageCount: number;\n /** Server-mode: total matched rows across all pages (drives the counter). */\n serverTotalCount: number;\n /** Server-mode: request a specific 1-based page (re-fetches via onQueryChange). */\n changePage: (page: number) => void;\n /**\n * The active scope id, RESOLVED against the declared scopes (see\n * `resolveScope`). `undefined` when the table declares none — which is what\n * keeps `scope` out of the emitted query for every table that never opted in.\n */\n scope: string | undefined;\n /** Select a scope: re-fetches from page 1 and drops the selection. */\n setScope: (id: string) => void;\n /**\n * The query the grid is currently showing. Read by Export, which hands it\n * back to the host UNPAGINATED — so an export follows the filters rather\n * than the 25 rows that happen to be loaded.\n */\n currentQuery: DataViewQuery;\n /**\n * Server-supplied per-scope totals, passed through verbatim, or `undefined`\n * when the server omits them. NEVER falls back to counting `matched`: a count\n * off the loaded page is wrong under pagination, which is the bug scopes fix.\n */\n scopeCounts?: Record<string, number>;\n}\n\n/**\n * The rows to render: in server mode `rows` is already the current page (the\n * backend filtered/sorted/paginated), so it passes through untouched; otherwise\n * the client search/pill/range filter narrows it.\n */\nfunction useMatchedRows<T extends Record<string, unknown>>(\n server: DataViewServer | undefined,\n rows: T[],\n columns: DataViewColumn<T>[],\n fields: FilterFieldConfig<T>[],\n rangeFields: RangeFieldConfig<T>[],\n state: DataViewState,\n): T[] {\n return useMemo(\n () =>\n server\n ? rows\n : filterRows(\n rows,\n columns,\n fields,\n { search: state.search, pills: state.pills, ranges: state.ranges },\n rangeFields,\n ),\n [server, rows, columns, fields, rangeFields, state.search, state.pills, state.ranges],\n );\n}\n\n/** Add or remove one column id from the visible-columns list. */\nfunction nextVisibleColumns(current: string[], id: string, visible: boolean): string[] {\n const without = current.filter((colId) => colId !== id);\n return visible ? [...without, id] : without;\n}\n\n/**\n * Reconcile external state changes and surface every change to the caller:\n * - `appliedState` (a saved view) fully REPLACES the state — it is columns-\n * authoritative, so applying a view restores its filters, sort AND columns.\n * - `syncState` (URL-driven controls) MERGES over the current state, leaving\n * `visibleColumns` untouched — so browser back/forward or a same-route\n * deep-link re-applies search/pills/sort without wiping a user's hidden columns.\n * Each channel only fires when its own reference changes (after the first run).\n */\nfunction useStateSyncEffects(\n appliedState: DataViewState | undefined,\n syncState: DataViewSyncState | undefined,\n state: DataViewState,\n onStateChange: ((state: DataViewState) => void) | undefined,\n setState: React.Dispatch<React.SetStateAction<DataViewState>>,\n): void {\n const firstApplied = useRef(true);\n useEffect(() => {\n if (firstApplied.current) {\n firstApplied.current = false;\n return;\n }\n if (appliedState) setState(appliedState);\n // `setState` is a stable useState setter (passed in), included to satisfy\n // exhaustive-deps; the effect still only re-applies when `appliedState` changes.\n }, [appliedState, setState]);\n const firstSync = useRef(true);\n useEffect(() => {\n if (firstSync.current) {\n firstSync.current = false;\n return;\n }\n // Merge — keep the current `visibleColumns` (never URL-carried); only the\n // URL-driven controls change.\n if (syncState) setState((prev) => ({ ...prev, ...syncState }));\n }, [syncState, setState]);\n useEffect(() => {\n onStateChange?.(state);\n }, [state, onStateChange]);\n}\n\n/**\n * The seed view state: a saved view (`appliedState`) or a pristine all-columns\n * one, with the URL-driven controls (`syncState`) merged over it so a deep-linked\n * filter or scope is reflected on the FIRST render. Column visibility is never\n * URL-carried, so the merge cannot wipe it.\n */\nfunction seedState(\n appliedState: DataViewState | undefined,\n syncState: DataViewSyncState | undefined,\n allColumnIds: string[],\n): DataViewState {\n const seed = appliedState ?? emptyViewState(allColumnIds);\n return syncState ? { ...seed, ...syncState } : seed;\n}\n\n/**\n * Selecting a scope: record it, then DROP the selection.\n *\n * A scope change renders a different result set. A selection that survived it\n * would let a bulk action run against rows the user can no longer see — the\n * same reason changing any other part of the query clears it.\n */\nfunction makeSetScope(\n patch: (next: DataViewStatePatch) => void,\n clearSelection: () => void,\n): (id: string) => void {\n return (id) => {\n patch({ scope: id });\n clearSelection();\n };\n}\n\n/**\n * The declared column ids in the stored reading order — the same tolerance\n * `applyOrder` uses, so the Colunas tab lists exactly what the grid renders.\n */\nfunction orderedColumnIds(allColumnIds: string[], order: string[] | undefined): string[] {\n if (!order || order.length === 0) return allColumnIds;\n const declared = new Set(allColumnIds);\n const kept = order.filter((id) => declared.has(id));\n const seen = new Set(kept);\n return [...kept, ...allColumnIds.filter((id) => !seen.has(id))];\n}\n\n/**\n * The query the grid is currently SHOWING — the same shape `onQueryChange`\n * emits, kept as a value so a host control (Exportar) can re-run it against the\n * backend rather than reading the loaded rows.\n */\nfunction liveQuery(\n state: DataViewState,\n scope: string | undefined,\n server: DataViewServer | undefined,\n matchedCount: number,\n): DataViewQuery {\n return {\n search: state.search,\n pills: state.pills,\n ranges: state.ranges ?? {},\n sortBy: state.sortBy,\n // Absent, not undefined, when the table declares no scopes.\n ...(scope !== undefined ? { scope } : {}),\n page: server?.page ?? 1,\n // Client mode has one page, and it is everything that matched.\n pageSize: server?.pageSize ?? matchedCount,\n };\n}\n\n/** The toolbar's active sort, read off the state's FIRST sort entry. */\nfunction activeSortOf(\n state: DataViewState,\n resolvedSortFields: SortFieldDefinition[],\n): { activeSortField: string; activeSortOrder: \"asc\" | \"desc\" } {\n const active = state.sortBy?.[0];\n return {\n activeSortField: active?.id ?? resolvedSortFields[0]?.value ?? \"\",\n activeSortOrder: active?.dir === \"desc\" ? \"desc\" : \"asc\",\n };\n}\n\n/**\n * Owns the DataViews view state (search/pills/ranges/sort/visibleColumns),\n * multi-select, the filter-panel toggle, and every derived value (filtered rows,\n * grid columns, active sort/filter counts). Extracted from the component so the\n * render stays within the size/complexity budget.\n */\nexport function useDataViewsState<T extends Record<string, unknown>>({\n rows,\n columns,\n fields,\n rangeFields,\n getRowId,\n onRowClick,\n appliedState,\n syncState,\n onStateChange,\n testIdPrefix,\n sortFields,\n rowActions,\n rowActionsLeading,\n renderRowMenu,\n server,\n scopes = [],\n scopeFieldId,\n}: UseDataViewsStateArgs<T>): DataViewsController<T> {\n useScopeConfigChecks(scopes, fields.map((field) => field.id), scopeFieldId, server !== undefined);\n const { columnsWithActions, allColumnIds } = useColumnsWithActions(columns, {\n rowActions,\n rowActionsLeading,\n renderRowMenu,\n testIdPrefix,\n getRowId,\n });\n // Seed once — see {@link seedState}.\n const [state, setState] = useState<DataViewState>(() =>\n seedState(appliedState, syncState, allColumnIds),\n );\n const [filterOpen, setFilterOpen] = useState(false);\n\n useStateSyncEffects(appliedState, syncState, state, onStateChange, setState);\n\n const patch = (next: DataViewStatePatch): void =>\n setState((prev) => ({ ...prev, ...(typeof next === \"function\" ? next(prev) : next) }));\n const matched = useMatchedRows(server, rows, columns, fields, rangeFields, state);\n // Resolved at READ time, every render: a stale deep link or a saved view naming\n // a scope that has since been removed falls back to the first declared scope\n // instead of putting an id the backend rejects on the wire.\n const scope = resolveScope(scopes, state.scope);\n // Server mode: re-fetch on query change (page resets to 1) + drive pagination.\n const { changePage } = useServerQuery(server, state, scope);\n const { gridColumns, hideableColumns } = useRenderedColumns(\n columnsWithActions,\n state.visibleColumns,\n onRowClick,\n state.order,\n );\n const toggleColumn = (id: string, visible: boolean): void =>\n setState((prev) => ({ ...prev, visibleColumns: nextVisibleColumns(prev.visibleColumns, id, visible) }));\n const selection = useSelection(matched, getRowId);\n const resolvedSortFields = sortFields ?? defaultSortFields(columns);\n\n return {\n state,\n ranges: state.ranges ?? {},\n patch,\n matched,\n gridColumns,\n hideableColumns,\n columnOrder: orderedColumnIds(allColumnIds, state.order),\n toggleColumn,\n ...selection,\n filterOpen,\n setFilterOpen,\n resolvedSortFields,\n ...activeSortOf(state, resolvedSortFields),\n activeFilterCount: computeActiveFilterCount(state, fields, rangeFields),\n ...serverDerived(server, matched.length),\n changePage,\n scope,\n setScope: makeSetScope(patch, selection.clearSelection),\n currentQuery: liveQuery(state, scope, server, matched.length),\n // Verbatim, or absent. See DataViewServer.scopeCounts for why there is no\n // client-side fallback.\n scopeCounts: server?.scopeCounts,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,27 +0,0 @@
1
- // src/components/data-display/DataViews/data-views-selection.ts
2
- import { useMemo, useState } from "react";
3
- function useSelection(matched, getRowId) {
4
- const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
5
- const selectedRows = useMemo(
6
- () => matched.filter((row) => selectedIds.has(getRowId(row))),
7
- [matched, selectedIds, getRowId]
8
- );
9
- return {
10
- selectedIds,
11
- setSelectedIds,
12
- toggleId: (id) => setSelectedIds((prev) => {
13
- const next = new Set(prev);
14
- if (next.has(id)) next.delete(id);
15
- else next.add(id);
16
- return next;
17
- }),
18
- selectedRows,
19
- clearSelection: () => setSelectedIds(/* @__PURE__ */ new Set()),
20
- selectAll: () => setSelectedIds(new Set(matched.map((row) => getRowId(row))))
21
- };
22
- }
23
-
24
- export {
25
- useSelection
26
- };
27
- //# sourceMappingURL=chunk-WTOTWP6A.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/data-views-selection.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\n\n/** The multi-select model, identical in every layout (table, cards, board). */\ninterface DataViewsSelection<T> {\n selectedIds: Set<string | number>;\n setSelectedIds: (ids: Set<string | number>) => void;\n /** Add/remove ONE id — what a card's or board card's checkbox calls. */\n toggleId: (id: string | number) => void;\n /** The selected rows, narrowed to what is currently rendered. */\n selectedRows: T[];\n clearSelection: () => void;\n selectAll: () => void;\n}\n\n/**\n * Owns the DataViews selection. ONE set of ids across every layout, so switching\n * from the board to the table (or to the cards) keeps exactly the same rows\n * selected and offers exactly the same bulk actions.\n *\n * Extracted from {@link useDataViewsState} for that hook's size budget, and\n * because `toggleId` needs a functional update: two cards toggled in the same\n * tick would otherwise both read the pre-render set and the second would drop\n * the first's change.\n */\nexport function useSelection<T>(\n matched: T[],\n getRowId: (row: T) => string | number,\n): DataViewsSelection<T> {\n const [selectedIds, setSelectedIds] = useState<Set<string | number>>(new Set());\n const selectedRows = useMemo(\n () => matched.filter((row) => selectedIds.has(getRowId(row))),\n [matched, selectedIds, getRowId],\n );\n return {\n selectedIds,\n setSelectedIds,\n toggleId: (id) =>\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n }),\n selectedRows,\n clearSelection: () => setSelectedIds(new Set()),\n selectAll: () => setSelectedIds(new Set(matched.map((row) => getRowId(row)))),\n };\n}\n"],"mappings":";AAEA,SAAS,SAAS,gBAAgB;AAwB3B,SAAS,aACd,SACA,UACuB;AACvB,QAAM,CAAC,aAAa,cAAc,IAAI,SAA+B,oBAAI,IAAI,CAAC;AAC9E,QAAM,eAAe;AAAA,IACnB,MAAM,QAAQ,OAAO,CAAC,QAAQ,YAAY,IAAI,SAAS,GAAG,CAAC,CAAC;AAAA,IAC5D,CAAC,SAAS,aAAa,QAAQ;AAAA,EACjC;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,CAAC,OACT,eAAe,CAAC,SAAS;AACvB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO;AAAA,IACT,CAAC;AAAA,IACH;AAAA,IACA,gBAAgB,MAAM,eAAe,oBAAI,IAAI,CAAC;AAAA,IAC9C,WAAW,MAAM,eAAe,IAAI,IAAI,QAAQ,IAAI,CAAC,QAAQ,SAAS,GAAG,CAAC,CAAC,CAAC;AAAA,EAC9E;AACF;","names":[]}