@12-apps/ui 6.57.0 → 6.59.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.
Files changed (210) hide show
  1. package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
  2. package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
  3. package/dist/_internal/components/data-display/Carousel/Carousel.js +2 -2
  4. package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
  5. package/dist/_internal/components/data-display/Chart/SpecChart.js +7 -7
  6. package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
  7. package/dist/_internal/components/data-display/Chart/chart-format.js +5 -1
  8. package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
  9. package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
  10. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
  11. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +18 -18
  12. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +20 -19
  13. package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +5 -3
  14. package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +45 -0
  15. package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js.map +1 -0
  16. package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +2 -2
  17. package/dist/_internal/components/data-display/DataViews/base-list-card.js +5 -4
  18. package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +1 -1
  19. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +4 -4
  20. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +4 -4
  21. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +17 -17
  22. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +6 -6
  23. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +4 -4
  24. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +5 -5
  25. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +2 -2
  26. package/dist/_internal/components/data-display/DataViews/data-views-selection-extra.js +3 -1
  27. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +3 -3
  28. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +1 -1
  29. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +2 -2
  30. package/dist/_internal/components/data-display/DataViews/data-views-url-echo.js +13 -0
  31. package/dist/_internal/components/data-display/DataViews/data-views-url-echo.js.map +1 -0
  32. package/dist/_internal/components/data-display/DataViews/list-card-rails.js +1 -1
  33. package/dist/_internal/components/data-display/FleetMap/FleetMap.fixtures.js +1 -1
  34. package/dist/_internal/components/data-display/index.js +16 -16
  35. package/dist/_internal/components/index.js +24 -24
  36. package/dist/_internal/components/layout/ContentToolbar/ContentToolbar.js +1 -1
  37. package/dist/_internal/components/layout/index.js +1 -1
  38. package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +6 -6
  39. package/dist/_internal/components/navigation/SectionNav/SectionNav.js +9 -9
  40. package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +3 -3
  41. package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +4 -4
  42. package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +5 -5
  43. package/dist/_internal/components/navigation/SectionNav/SectionNav.railCreate.js +4 -4
  44. package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +4 -4
  45. package/dist/_internal/components/navigation/index.js +9 -9
  46. package/dist/_internal/en-US.data-display.js +1 -1
  47. package/dist/_internal/pt-BR.data-display.js +1 -1
  48. package/dist/_internal/user-avatar/user-avatar.js +3 -3
  49. package/dist/charts.js +8 -8
  50. package/dist/{chunk-RBHBJDOZ.js → chunk-3FMWQQW6.js} +5 -5
  51. package/dist/chunk-3FMWQQW6.js.map +1 -0
  52. package/dist/{chunk-MKAA3YIJ.js → chunk-3K7WN2PO.js} +3 -3
  53. package/dist/{chunk-GI24X7XA.js → chunk-4PPNCZWY.js} +3 -3
  54. package/dist/{chunk-573XC4HK.js → chunk-4QKS5Y35.js} +2 -2
  55. package/dist/{chunk-KX75HM5U.js → chunk-5YDS6HO3.js} +7 -2
  56. package/dist/{chunk-KX75HM5U.js.map → chunk-5YDS6HO3.js.map} +1 -1
  57. package/dist/{chunk-N4MR4EMC.js → chunk-6D2PFC5C.js} +2 -2
  58. package/dist/{chunk-QVWNK4JO.js → chunk-6GPNZSAE.js} +19 -2
  59. package/dist/chunk-6GPNZSAE.js.map +1 -0
  60. package/dist/{chunk-LJKWWXZD.js → chunk-6OSUK2C7.js} +28 -22
  61. package/dist/chunk-6OSUK2C7.js.map +1 -0
  62. package/dist/{chunk-ICSXPZXT.js → chunk-6QBT5TPS.js} +2 -2
  63. package/dist/{chunk-A6EUQDRK.js → chunk-ARJIKW56.js} +4 -2
  64. package/dist/chunk-ARJIKW56.js.map +1 -0
  65. package/dist/{chunk-53ACGKBW.js → chunk-EDBP62WG.js} +3 -3
  66. package/dist/{chunk-YXIVQFNW.js → chunk-EWIKSTJV.js} +4 -4
  67. package/dist/{chunk-LWZ2K225.js → chunk-FXZUVZON.js} +6 -6
  68. package/dist/chunk-G6EJLEVV.js +75 -0
  69. package/dist/chunk-G6EJLEVV.js.map +1 -0
  70. package/dist/{chunk-IK2ZVV3M.js → chunk-GK7JNSMK.js} +2 -2
  71. package/dist/{chunk-HFBQCJNP.js → chunk-HCYRCU4W.js} +2 -2
  72. package/dist/{chunk-BJMNAPKH.js → chunk-HH7PM5HT.js} +10 -22
  73. package/dist/chunk-HH7PM5HT.js.map +1 -0
  74. package/dist/{chunk-7AAJYWC3.js → chunk-HPO7PB4F.js} +2 -2
  75. package/dist/{chunk-WF3P7GE5.js → chunk-I3CQBEKS.js} +2 -2
  76. package/dist/{chunk-RREWYOKS.js → chunk-IL4FCC54.js} +2 -2
  77. package/dist/{chunk-7M3DRP7Q.js → chunk-ILNO4ZJ6.js} +3 -3
  78. package/dist/{chunk-R6SBP5M5.js → chunk-JVOEVTCZ.js} +4 -2
  79. package/dist/chunk-JVOEVTCZ.js.map +1 -0
  80. package/dist/{chunk-2CJZLL5Y.js → chunk-NHVL2QPG.js} +2 -1
  81. package/dist/{chunk-2CJZLL5Y.js.map → chunk-NHVL2QPG.js.map} +1 -1
  82. package/dist/{chunk-45PGSVOH.js → chunk-NTJUAPJM.js} +10 -8
  83. package/dist/chunk-NTJUAPJM.js.map +1 -0
  84. package/dist/{chunk-EUPUEMFX.js → chunk-PC4H5LVB.js} +7 -7
  85. package/dist/{chunk-KEKOPD43.js → chunk-PQ7RWH7L.js} +4 -2
  86. package/dist/chunk-PQ7RWH7L.js.map +1 -0
  87. package/dist/{chunk-HRMB72E4.js → chunk-QHP5PZNC.js} +2 -1
  88. package/dist/{chunk-HRMB72E4.js.map → chunk-QHP5PZNC.js.map} +1 -1
  89. package/dist/{chunk-GNBCSTXF.js → chunk-QKAYXAQE.js} +6 -6
  90. package/dist/{chunk-GNBCSTXF.js.map → chunk-QKAYXAQE.js.map} +1 -1
  91. package/dist/{chunk-TZFBJ47T.js → chunk-SFLNHCA2.js} +39 -15
  92. package/dist/chunk-SFLNHCA2.js.map +1 -0
  93. package/dist/{chunk-OBMPE6PW.js → chunk-SSDYEOU4.js} +5 -5
  94. package/dist/{chunk-I6ESCBY3.js → chunk-TF27Z27K.js} +2 -2
  95. package/dist/{chunk-JF5LQOGB.js → chunk-U47KRLEW.js} +57 -45
  96. package/dist/chunk-U47KRLEW.js.map +1 -0
  97. package/dist/chunk-V4V2N6YJ.js +55 -0
  98. package/dist/chunk-V4V2N6YJ.js.map +1 -0
  99. package/dist/{chunk-C67FDYG7.js → chunk-W7N4ORCV.js} +16 -6
  100. package/dist/chunk-W7N4ORCV.js.map +1 -0
  101. package/dist/{chunk-JL77BSOL.js → chunk-WBDQSBLV.js} +2 -2
  102. package/dist/{chunk-G76BNXWE.js → chunk-XW5SEW3H.js} +6 -6
  103. package/dist/{chunk-46ZATLQV.js → chunk-YIKFYWKD.js} +3 -3
  104. package/dist/{chunk-4JBS6NOT.js → chunk-ZWBGQYVO.js} +35 -6
  105. package/dist/chunk-ZWBGQYVO.js.map +1 -0
  106. package/dist/{chunk-VWW2EXH7.js → chunk-ZYJZHXFU.js} +4 -4
  107. package/dist/data-display/Avatar.js +2 -2
  108. package/dist/data-display/Badge.js +2 -2
  109. package/dist/data-display/CardKit.js +10 -10
  110. package/dist/data-display/Carousel.js +2 -2
  111. package/dist/data-display/Chart.js +8 -8
  112. package/dist/data-display/DataGrid.js +2 -2
  113. package/dist/data-display/DataViews.js +25 -23
  114. package/dist/en-US.js +1 -1
  115. package/dist/layout/ContentToolbar.js +1 -1
  116. package/dist/locales.js +2 -2
  117. package/dist/navigation/SectionNav.js +9 -9
  118. package/dist/pt-BR.js +1 -1
  119. package/dist/types/components/data-display/Chart/Chart.types.d.ts +5 -0
  120. package/dist/types/components/data-display/Chart/Chart.types.d.ts.map +1 -1
  121. package/dist/types/components/data-display/Chart/chart-context.d.ts.map +1 -1
  122. package/dist/types/components/data-display/Chart/chart-format.d.ts +14 -0
  123. package/dist/types/components/data-display/Chart/chart-format.d.ts.map +1 -1
  124. package/dist/types/components/data-display/Chart/chart-spec.d.ts.map +1 -1
  125. package/dist/types/components/data-display/DataViews/DataViewsTableBase.d.ts.map +1 -1
  126. package/dist/types/components/data-display/DataViews/base-list-card-geometry.d.ts +29 -1
  127. package/dist/types/components/data-display/DataViews/base-list-card-geometry.d.ts.map +1 -1
  128. package/dist/types/components/data-display/DataViews/base-list-card-layout.d.ts +46 -0
  129. package/dist/types/components/data-display/DataViews/base-list-card-layout.d.ts.map +1 -0
  130. package/dist/types/components/data-display/DataViews/base-list-card-slots.d.ts +5 -2
  131. package/dist/types/components/data-display/DataViews/base-list-card-slots.d.ts.map +1 -1
  132. package/dist/types/components/data-display/DataViews/base-list-card.d.ts +10 -1
  133. package/dist/types/components/data-display/DataViews/base-list-card.d.ts.map +1 -1
  134. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +7 -0
  135. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  136. package/dist/types/components/data-display/DataViews/data-views-grid-parts.d.ts.map +1 -1
  137. package/dist/types/components/data-display/DataViews/data-views-selection-extra.d.ts +2 -0
  138. package/dist/types/components/data-display/DataViews/data-views-selection-extra.d.ts.map +1 -1
  139. package/dist/types/components/data-display/DataViews/data-views-shell-toolbar.d.ts +3 -1
  140. package/dist/types/components/data-display/DataViews/data-views-shell-toolbar.d.ts.map +1 -1
  141. package/dist/types/components/data-display/DataViews/data-views-toolbar.d.ts +2 -0
  142. package/dist/types/components/data-display/DataViews/data-views-toolbar.d.ts.map +1 -1
  143. package/dist/types/components/data-display/DataViews/data-views-url-echo.d.ts +47 -0
  144. package/dist/types/components/data-display/DataViews/data-views-url-echo.d.ts.map +1 -0
  145. package/dist/types/components/data-display/DataViews/list-card-rails.d.ts +17 -2
  146. package/dist/types/components/data-display/DataViews/list-card-rails.d.ts.map +1 -1
  147. package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts +3 -2
  148. package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts.map +1 -1
  149. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +14 -0
  150. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
  151. package/dist/types/en-US.data-display.d.ts.map +1 -1
  152. package/dist/types/pt-BR.data-display.d.ts.map +1 -1
  153. package/dist/user-avatar.js +3 -3
  154. package/package.json +1 -1
  155. package/src/components/data-display/Chart/Chart.md +5 -1
  156. package/src/components/data-display/Chart/Chart.types.ts +5 -0
  157. package/src/components/data-display/Chart/chart-context.ts +21 -1
  158. package/src/components/data-display/Chart/chart-format.ts +31 -0
  159. package/src/components/data-display/Chart/chart-renderers.tsx +1 -1
  160. package/src/components/data-display/Chart/chart-spec.ts +2 -1
  161. package/src/components/data-display/DataViews/DataViewsTableBase.tsx +7 -36
  162. package/src/components/data-display/DataViews/base-list-card-geometry.tsx +114 -40
  163. package/src/components/data-display/DataViews/base-list-card-layout.tsx +104 -0
  164. package/src/components/data-display/DataViews/base-list-card-slots.tsx +47 -3
  165. package/src/components/data-display/DataViews/base-list-card.tsx +65 -68
  166. package/src/components/data-display/DataViews/data-views-copy.ts +7 -0
  167. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +2 -1
  168. package/src/components/data-display/DataViews/data-views-selection-extra.ts +10 -1
  169. package/src/components/data-display/DataViews/data-views-shell-toolbar.tsx +4 -0
  170. package/src/components/data-display/DataViews/data-views-toolbar.tsx +4 -0
  171. package/src/components/data-display/DataViews/data-views-url-echo.ts +134 -0
  172. package/src/components/data-display/DataViews/list-card-rails.tsx +22 -6
  173. package/src/components/layout/ContentToolbar/ContentToolbar.md +5 -2
  174. package/src/components/layout/ContentToolbar/ContentToolbar.tsx +51 -10
  175. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +14 -0
  176. package/src/en-US.data-display.ts +1 -0
  177. package/src/pt-BR.data-display.ts +1 -0
  178. package/dist/chunk-45PGSVOH.js.map +0 -1
  179. package/dist/chunk-4JBS6NOT.js.map +0 -1
  180. package/dist/chunk-A6EUQDRK.js.map +0 -1
  181. package/dist/chunk-BJMNAPKH.js.map +0 -1
  182. package/dist/chunk-C67FDYG7.js.map +0 -1
  183. package/dist/chunk-JF5LQOGB.js.map +0 -1
  184. package/dist/chunk-KEKOPD43.js.map +0 -1
  185. package/dist/chunk-LJKWWXZD.js.map +0 -1
  186. package/dist/chunk-QVWNK4JO.js.map +0 -1
  187. package/dist/chunk-R6SBP5M5.js.map +0 -1
  188. package/dist/chunk-RBHBJDOZ.js.map +0 -1
  189. package/dist/chunk-TZFBJ47T.js.map +0 -1
  190. /package/dist/{chunk-MKAA3YIJ.js.map → chunk-3K7WN2PO.js.map} +0 -0
  191. /package/dist/{chunk-GI24X7XA.js.map → chunk-4PPNCZWY.js.map} +0 -0
  192. /package/dist/{chunk-573XC4HK.js.map → chunk-4QKS5Y35.js.map} +0 -0
  193. /package/dist/{chunk-N4MR4EMC.js.map → chunk-6D2PFC5C.js.map} +0 -0
  194. /package/dist/{chunk-ICSXPZXT.js.map → chunk-6QBT5TPS.js.map} +0 -0
  195. /package/dist/{chunk-53ACGKBW.js.map → chunk-EDBP62WG.js.map} +0 -0
  196. /package/dist/{chunk-YXIVQFNW.js.map → chunk-EWIKSTJV.js.map} +0 -0
  197. /package/dist/{chunk-LWZ2K225.js.map → chunk-FXZUVZON.js.map} +0 -0
  198. /package/dist/{chunk-IK2ZVV3M.js.map → chunk-GK7JNSMK.js.map} +0 -0
  199. /package/dist/{chunk-HFBQCJNP.js.map → chunk-HCYRCU4W.js.map} +0 -0
  200. /package/dist/{chunk-7AAJYWC3.js.map → chunk-HPO7PB4F.js.map} +0 -0
  201. /package/dist/{chunk-WF3P7GE5.js.map → chunk-I3CQBEKS.js.map} +0 -0
  202. /package/dist/{chunk-RREWYOKS.js.map → chunk-IL4FCC54.js.map} +0 -0
  203. /package/dist/{chunk-7M3DRP7Q.js.map → chunk-ILNO4ZJ6.js.map} +0 -0
  204. /package/dist/{chunk-EUPUEMFX.js.map → chunk-PC4H5LVB.js.map} +0 -0
  205. /package/dist/{chunk-OBMPE6PW.js.map → chunk-SSDYEOU4.js.map} +0 -0
  206. /package/dist/{chunk-I6ESCBY3.js.map → chunk-TF27Z27K.js.map} +0 -0
  207. /package/dist/{chunk-JL77BSOL.js.map → chunk-WBDQSBLV.js.map} +0 -0
  208. /package/dist/{chunk-G76BNXWE.js.map → chunk-XW5SEW3H.js.map} +0 -0
  209. /package/dist/{chunk-46ZATLQV.js.map → chunk-YIKFYWKD.js.map} +0 -0
  210. /package/dist/{chunk-VWW2EXH7.js.map → chunk-ZYJZHXFU.js.map} +0 -0
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import { type ReactNode } from "react";
4
+
5
+ import { Box } from "../../../mui/Box";
6
+ import type { BaseListCardProps } from "./base-list-card";
7
+ import { rowGutters, type StackPlacement, type useRowShell } from "./base-list-card-geometry";
8
+ import { cellRailsTemplate } from "./list-card-rails";
9
+ import { cellTracks, useCellConfig, type ListCardCellConfig } from "./list-card-cells";
10
+
11
+ /**
12
+ * WHAT SHAPE A ROW TAKES, resolved once: its cells and their template, whether
13
+ * it ends in the "this opens" chevron, and what its two-line form keeps.
14
+ *
15
+ * Split from the card at the file-size gate. Everything here decides; nothing
16
+ * here renders but the standalone container.
17
+ */
18
+
19
+ /**
20
+ * Which cell config applies, and whether this row resolves its own tracks.
21
+ *
22
+ * The list's config wins over the card's: one declaration of the list's shape is
23
+ * the point, so a row inside a group cannot introduce a column of its own. And
24
+ * the template is standalone-only — inside a group the GROUP owns it and the row
25
+ * is subgrid over it, because a row resolving its own tracks is exactly what
26
+ * stops a list from lining up.
27
+ */
28
+ function useResolvedCells(
29
+ props: BaseListCardProps,
30
+ inGroup: boolean,
31
+ gutters: { disclose: boolean; drag: boolean; select: boolean },
32
+ ): {
33
+ cells: readonly ListCardCellConfig<never>[] | null;
34
+ configured: boolean;
35
+ cellTemplate: string | null;
36
+ } {
37
+ const groupCells = useCellConfig();
38
+ const cells = groupCells ?? props.cells ?? null;
39
+ const configured = cells != null && cells.length > 0 && props.row != null;
40
+ const cellTemplate = configured && !inGroup ? cellRailsTemplate(cellTracks(cells), gutters) : null;
41
+ return { cells, configured, cellTemplate };
42
+ }
43
+
44
+ /**
45
+ * Whether the row ends in the "this opens" chevron: it is live and leads
46
+ * somewhere it can actually go — a click handler, or a link the row renders.
47
+ * Only a named-slot row with a title renders an anchor for `href` (the caption
48
+ * wraps the title), so an `href` alone does not count elsewhere: a chevron on
49
+ * a row that cannot open is the promise the disclosure chevron's docblock
50
+ * warns about. Not when the row expands either:
51
+ * the disclosure chevron already speaks, and a second arrow meaning something
52
+ * else beside it would contradict it.
53
+ */
54
+ function rowOpens(
55
+ props: BaseListCardProps,
56
+ shell: ReturnType<typeof useRowShell>,
57
+ configured: boolean,
58
+ expandable: boolean,
59
+ ): boolean {
60
+ if (expandable) return false;
61
+ // The named-slot caption renders its anchor only around a title.
62
+ return shell.acts || (shell.actionable && props.href != null && props.title != null && !configured);
63
+ }
64
+
65
+ /**
66
+ * The cell that takes the VALUE's place when the row stacks: the one the config
67
+ * marks `strong` ("the figure the row is really about"), else the last — as
68
+ * long as it is not the first cell, which already holds the title's place.
69
+ */
70
+ function valueCellId(cells: readonly ListCardCellConfig<never>[]): string | null {
71
+ const first = cells[0];
72
+ const candidate = cells.find((cell) => cell.strong === true && cell !== first) ?? cells[cells.length - 1];
73
+ return candidate != null && candidate !== first ? candidate.id : null;
74
+ }
75
+
76
+ /** Everything the row's shape depends on, resolved once. */
77
+ export function useRowLayout(
78
+ props: BaseListCardProps,
79
+ shell: ReturnType<typeof useRowShell>,
80
+ expandable: boolean,
81
+ ): ReturnType<typeof useResolvedCells> & { opens: boolean; stack: StackPlacement } {
82
+ const resolved = useResolvedCells(props, shell.group != null, rowGutters(props, shell));
83
+ const cells = resolved.configured ? resolved.cells : null;
84
+ return {
85
+ ...resolved,
86
+ opens: rowOpens(props, shell, resolved.configured, expandable),
87
+ stack: {
88
+ firstCell: cells?.[0]?.id ?? null,
89
+ valueCell: cells == null ? null : valueCellId(cells),
90
+ expandable,
91
+ },
92
+ };
93
+ }
94
+
95
+ /**
96
+ * A standalone row's QUERY CONTAINER. A container query matches an ancestor,
97
+ * never the element itself, so the row cannot be the container its own
98
+ * two-line template asks about. In a group the group is the container, and the
99
+ * row must stay its direct child for subgrid, so no wrapper there.
100
+ */
101
+ export function StandaloneContainer({ inGroup, children }: { inGroup: boolean; children: ReactNode }): React.JSX.Element {
102
+ if (inGroup) return <>{children}</>;
103
+ return <Box sx={{ containerType: "inline-size", width: "100%", minWidth: 0 }}>{children}</Box>;
104
+ }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
3
4
  import { Fragment, type ReactNode } from "react";
4
5
 
5
6
  import { DescriptionItem, type DescriptionItemProps } from "../DescriptionItem";
@@ -242,19 +243,34 @@ export function ListCardActions({
242
243
  actions,
243
244
  alwaysVisible,
244
245
  menu,
246
+ opens = false,
245
247
  testId,
246
248
  }: {
247
249
  actions?: ReactNode;
248
250
  alwaysVisible?: boolean;
249
251
  menu?: ReactNode;
252
+ /** End the row with the "this opens" chevron. */
253
+ opens?: boolean;
250
254
  testId: (slot: string) => string | undefined;
251
255
  }): React.JSX.Element | null {
252
- if (actions == null && menu == null) return null;
256
+ if (actions == null && menu == null && !opens) return null;
253
257
  return (
254
- // z-index 1: above the stretched link, so these stay clickable.
258
+ // z-index 1: above the stretched link, so these stay clickable. The box
259
+ // itself lets clicks through (`pointer-events: none`, re-enabled on its
260
+ // children): only the controls inside it are meant to catch one, and the
261
+ // chevron, which re-disables it, must hand its click to the row or link
262
+ // underneath rather than swallow it.
255
263
  <Box
256
264
  data-slot="actions"
257
- sx={{ display: "flex", alignItems: "center", gap: 0.5, position: "relative", zIndex: 1 }}
265
+ sx={{
266
+ display: "flex",
267
+ alignItems: "center",
268
+ gap: 0.5,
269
+ position: "relative",
270
+ zIndex: 1,
271
+ pointerEvents: "none",
272
+ "& > *": { pointerEvents: "auto" },
273
+ }}
258
274
  >
259
275
  {actions && (
260
276
  <Box
@@ -282,6 +298,31 @@ export function ListCardActions({
282
298
  {menu}
283
299
  </Box>
284
300
  )}
301
+ {opens && <OpenChevron testId={testId("open")} />}
302
+ </Box>
303
+ );
304
+ }
305
+
306
+ /**
307
+ * THE "THIS OPENS" CHEVRON, at the end of a row that leads somewhere.
308
+ *
309
+ * A row that navigates or opens a record looked exactly like a row that does
310
+ * nothing until a pointer hovered it, and a phone has no hover. Always visible —
311
+ * unlike `actions`, it is a statement about the row, not a control to reveal.
312
+ *
313
+ * Decorative to assistive tech (`aria-hidden`): the row itself is the button or
314
+ * the link, and already says so. It takes no click of its own for the same
315
+ * reason; the click falls through to the row.
316
+ */
317
+ function OpenChevron({ testId }: { testId?: string }): React.JSX.Element {
318
+ return (
319
+ <Box
320
+ component="span"
321
+ aria-hidden
322
+ data-testid={testId}
323
+ sx={{ display: "flex", alignItems: "center", color: "text.secondary", pointerEvents: "none !important" }}
324
+ >
325
+ <KeyboardArrowRightIcon fontSize="small" />
285
326
  </Box>
286
327
  );
287
328
  }
@@ -293,6 +334,7 @@ export function ListCardTail({
293
334
  actions,
294
335
  actionsAlwaysVisible,
295
336
  menu,
337
+ opens,
296
338
  testId,
297
339
  }: {
298
340
  value?: ReactNode;
@@ -301,6 +343,7 @@ export function ListCardTail({
301
343
  actions?: ReactNode;
302
344
  actionsAlwaysVisible?: boolean;
303
345
  menu?: ReactNode;
346
+ opens?: boolean;
304
347
  testId: (slot: string) => string | undefined;
305
348
  }): React.JSX.Element {
306
349
  return (
@@ -319,6 +362,7 @@ export function ListCardTail({
319
362
  actions={actions}
320
363
  alwaysVisible={actionsAlwaysVisible}
321
364
  menu={menu}
365
+ opens={opens}
322
366
  testId={testId}
323
367
  />
324
368
  </>
@@ -11,10 +11,10 @@ import { type CardSurfaceProps } from "./card-surface";
11
11
  import { DropIndicator } from "./data-views-drag";
12
12
  import { useDisclosure } from "./base-list-card-disclosure";
13
13
  import { actionProps, metaShape, rowStyles, useRowShell } from "./base-list-card-geometry";
14
+ import { StandaloneContainer, useRowLayout } from "./base-list-card-layout";
14
15
  import { DiscloseSlot, DragSlot, SelectSlot } from "./base-list-card-gutters";
15
16
  import { type DataViewsDensity } from "./data-views-layout-context";
16
- import { cellRailsTemplate } from "./list-card-rails";
17
- import { ListCardCells, cellTracks, useCellConfig, type ListCardCellConfig } from "./list-card-cells";
17
+ import { ListCardCells, type ListCardCellConfig } from "./list-card-cells";
18
18
  import {
19
19
  ListCardActions,
20
20
  ListCardCaption,
@@ -41,7 +41,16 @@ import {
41
41
  * same row renders full-bleed on a phone, in a 300px board column, and beside a
42
42
  * filter panel that opens and closes. Below `STACK_BREAK` it leaves the shared
43
43
  * rails entirely and goes two-line, which is the standard mobile transaction
44
- * row and beats truncating everything: title + value, then subtitle + status.
44
+ * row and beats truncating everything: the title (with its subtitle) on line
45
+ * 1, the value under it at the right edge.
46
+ * A CONFIGURED row keeps the same two facts — its first cell in the title's
47
+ * place, its `strong` (else last) cell in the value's — and drops the rest
48
+ * into the expandable body's job. Standalone, the card renders inside a
49
+ * wrapper that is the query container, since a row cannot query itself.
50
+ *
51
+ * A row that OPENS something (a click handler, or a link it renders) ends in a
52
+ * chevron saying so, at every width — unless it expands, where the disclosure
53
+ * chevron is the only arrow it carries.
45
54
  */
46
55
 
47
56
  export interface BaseListCardProps extends CardSurfaceProps {
@@ -145,33 +154,6 @@ export interface BaseListCardProps extends CardSurfaceProps {
145
154
  }
146
155
 
147
156
 
148
-
149
-
150
- /**
151
- * Which cell config applies, and whether this row resolves its own tracks.
152
- *
153
- * The list's config wins over the card's: one declaration of the list's shape is
154
- * the point, so a row inside a group cannot introduce a column of its own. And
155
- * the template is standalone-only — inside a group the GROUP owns it and the row
156
- * is subgrid over it, because a row resolving its own tracks is exactly what
157
- * stops a list from lining up.
158
- */
159
- function useResolvedCells(
160
- props: BaseListCardProps,
161
- inGroup: boolean,
162
- ): {
163
- cells: readonly ListCardCellConfig<never>[] | null;
164
- configured: boolean;
165
- cellTemplate: string | null;
166
- } {
167
- const groupCells = useCellConfig();
168
- const cells = groupCells ?? props.cells ?? null;
169
- const configured = cells != null && cells.length > 0 && props.row != null;
170
- const cellTemplate = configured && !inGroup ? cellRailsTemplate(cellTracks(cells)) : null;
171
- return { cells, configured, cellTemplate };
172
- }
173
-
174
-
175
157
  /**
176
158
  * The row's middle: either the list's configured cells, or the named slots.
177
159
  *
@@ -185,6 +167,7 @@ function RowContent({
185
167
  slot,
186
168
  actionable,
187
169
  meta,
170
+ opens,
188
171
  }: {
189
172
  card: BaseListCardProps;
190
173
  cells: readonly ListCardCellConfig<never>[] | null;
@@ -192,6 +175,8 @@ function RowContent({
192
175
  slot: (name: string) => string | undefined;
193
176
  actionable: boolean;
194
177
  meta: { columns: number; present: boolean };
178
+ /** Whether the row ends in the "this opens" chevron. */
179
+ opens: boolean;
195
180
  }): React.JSX.Element {
196
181
  if (configured && cells != null) {
197
182
  return (
@@ -206,6 +191,7 @@ function RowContent({
206
191
  actions={actionable ? card.actions : undefined}
207
192
  alwaysVisible={card.actionsAlwaysVisible}
208
193
  menu={card.menu}
194
+ opens={opens}
209
195
  testId={slot}
210
196
  />
211
197
  </>
@@ -227,6 +213,7 @@ function RowContent({
227
213
  actions={actionable ? card.actions : undefined}
228
214
  actionsAlwaysVisible={card.actionsAlwaysVisible}
229
215
  menu={card.menu}
216
+ opens={opens}
230
217
  testId={slot}
231
218
  />
232
219
  </>
@@ -294,46 +281,56 @@ export function BaseListCard(props: BaseListCardProps): React.JSX.Element {
294
281
  const { actionable, selectable, slot, drag, reserve, pad, padY, acts } = shell;
295
282
  const meta = metaShape(props);
296
283
  const disclosure = useDisclosure(props);
297
- const { cells, configured, cellTemplate } = useResolvedCells(props, shell.group != null);
284
+ const { cells, configured, cellTemplate, opens, stack } = useRowLayout(props, shell, disclosure.expandable);
298
285
 
299
286
  return (
300
- <Card
301
- variant="outlined"
302
- className={props.className}
303
- dataTestId={props.testId}
304
- aria-label={props["aria-label"]}
305
- aria-disabled={actionable ? undefined : true}
306
- onContextMenu={props.onContextMenu}
307
- {...(acts ? actionProps(onClick) : {})}
308
- {...drag.itemProps}
309
- // A PLAIN OBJECT: `Card` merges by spreading, so a function sx vanishes.
310
- sx={rowStyles(props, shell, cellTemplate)}
311
- >
312
- {drag.dropEdge != null && <DropIndicator edge={drag.dropEdge} />}
313
- <DiscloseSlot
314
- expandable={disclosure.expandable}
315
- expanded={disclosure.expanded}
316
- reserve={reserve}
317
- onToggle={disclosure.toggle}
318
- controls={disclosure.regionId}
319
- testId={slot("disclose")}
320
- />
321
- <DragSlot drag={drag} reserve={reserve} testId={slot("drag")} />
322
- <SelectSlot
323
- selectable={selectable}
324
- selected={selected}
325
- reserve={reserve}
326
- onToggleSelect={props.onToggleSelect}
327
- testId={slot("checkbox")}
328
- />
329
- <Box data-slot="leading" sx={{ display: "flex", alignItems: "center" }}>
330
- {props.leading}
331
- </Box>
332
- <RowContent card={props} cells={cells} configured={configured} slot={slot} actionable={actionable} meta={meta} />
333
- <ExpandableBody disclosure={disclosure} padY={padY} pad={pad}>
334
- {props.children}
335
- </ExpandableBody>
336
- </Card>
287
+ <StandaloneContainer inGroup={shell.group != null}>
288
+ <Card
289
+ variant="outlined"
290
+ className={props.className}
291
+ dataTestId={props.testId}
292
+ aria-label={props["aria-label"]}
293
+ aria-disabled={actionable ? undefined : true}
294
+ onContextMenu={props.onContextMenu}
295
+ {...(acts ? actionProps(onClick) : {})}
296
+ {...drag.itemProps}
297
+ // A PLAIN OBJECT: `Card` merges by spreading, so a function sx vanishes.
298
+ sx={rowStyles(props, shell, cellTemplate, stack)}
299
+ >
300
+ {drag.dropEdge != null && <DropIndicator edge={drag.dropEdge} />}
301
+ <DiscloseSlot
302
+ expandable={disclosure.expandable}
303
+ expanded={disclosure.expanded}
304
+ reserve={reserve}
305
+ onToggle={disclosure.toggle}
306
+ controls={disclosure.regionId}
307
+ testId={slot("disclose")}
308
+ />
309
+ <DragSlot drag={drag} reserve={reserve} testId={slot("drag")} />
310
+ <SelectSlot
311
+ selectable={selectable}
312
+ selected={selected}
313
+ reserve={reserve}
314
+ onToggleSelect={props.onToggleSelect}
315
+ testId={slot("checkbox")}
316
+ />
317
+ <Box data-slot="leading" sx={{ display: "flex", alignItems: "center" }}>
318
+ {props.leading}
319
+ </Box>
320
+ <RowContent
321
+ card={props}
322
+ cells={cells}
323
+ configured={configured}
324
+ slot={slot}
325
+ actionable={actionable}
326
+ meta={meta}
327
+ opens={opens}
328
+ />
329
+ <ExpandableBody disclosure={disclosure} padY={padY} pad={pad}>
330
+ {props.children}
331
+ </ExpandableBody>
332
+ </Card>
333
+ </StandaloneContainer>
337
334
  );
338
335
  }
339
336
 
@@ -225,6 +225,13 @@ export interface DataViewsSelectionCopy {
225
225
  clearSelection: string;
226
226
  selectAll: string;
227
227
  onThisPage(count: number): string;
228
+ /**
229
+ * The toolbar's count while rows are ticked: "1 selecionado". A plural, so a
230
+ * function. Keep it SHORT: at 320 it shares line 1 with the clear button and
231
+ * has about 118px — "20 itens selecionados" (135px) wrapped and stranded the
232
+ * divider before it; "20 selecionados" fits.
233
+ */
234
+ selectedCount(count: number): string;
228
235
  /** One row's checkbox, which carries no visible label of its own. */
229
236
  selectRow: string;
230
237
  /**
@@ -16,7 +16,7 @@ import type { ListGroupConfig } from "./list-card-rails";
16
16
  import { InlineFilterControls } from "./data-views-inline-bar";
17
17
  import { toOverflowFields, useFilterOverflow, type OverflowSplit } from "./data-views-overflow";
18
18
  import { GridHeaderRow } from "./data-views-header-row";
19
- import { resolveSelectionExtra, type SelectionExtraRender } from "./data-views-selection-extra";
19
+ import { allOnPageSelected, resolveSelectionExtra, type SelectionExtraRender } from "./data-views-selection-extra";
20
20
  import { ShellToolbar } from "./data-views-shell-toolbar";
21
21
  import type { DisplayPanelView } from "./data-views-display-panel";
22
22
  import type { DataViewExport } from "./data-views-export";
@@ -284,6 +284,7 @@ function ShellStack<T extends Record<string, unknown>>({
284
284
  rowActions={props.rowActions}
285
285
  bulkActions={props.bulkActions}
286
286
  selectionExtra={resolveSelectionExtra(props)}
287
+ allOnPageSelected={allOnPageSelected(c, props.getRowId)}
287
288
  toolbarRightSlot={props.toolbarRightSlot}
288
289
  compactControls={showInline && split.compactControls}
289
290
  counterHidden={showInline && split.counterHidden}
@@ -47,6 +47,15 @@ export type SelectionExtraRender<T extends Record<string, unknown>> = (
47
47
  * Returns `undefined` with nothing selected, so the toolbar renders no empty
48
48
  * slot: a widening with nothing to widen is not a control.
49
49
  */
50
+ /** Every row the page is rendering is selected — and the page is not empty. */
51
+ export function allOnPageSelected<T extends Record<string, unknown>>(
52
+ c: DataViewsController<T>,
53
+ getRowId: (row: T) => string | number,
54
+ ): boolean {
55
+ const page = c.matched;
56
+ return page.length > 0 && page.every((row) => c.selectedIds.has(getRowId(row)));
57
+ }
58
+
50
59
  export function resolveSelectionExtra<T extends Record<string, unknown>>({
51
60
  c,
52
61
  getRowId,
@@ -61,7 +70,7 @@ export function resolveSelectionExtra<T extends Record<string, unknown>>({
61
70
  return selectionExtra({
62
71
  selectedRows: c.selectedRows,
63
72
  clearSelection: c.clearSelection,
64
- allOnPageSelected: page.length > 0 && page.every((row) => c.selectedIds.has(getRowId(row))),
73
+ allOnPageSelected: allOnPageSelected(c, getRowId),
65
74
  pageRowCount: page.length,
66
75
  });
67
76
  }
@@ -23,6 +23,7 @@ export function ShellToolbar<T extends Record<string, unknown>>({
23
23
  rowActions,
24
24
  bulkActions,
25
25
  selectionExtra,
26
+ allOnPageSelected,
26
27
  toolbarRightSlot,
27
28
  showInline,
28
29
  compactControls,
@@ -43,6 +44,8 @@ export function ShellToolbar<T extends Record<string, unknown>>({
43
44
  bulkActions?: (selectedRows: T[], clearSelection: () => void) => React.ReactNode;
44
45
  /** Already resolved by the shell, which is the party that knows the page. */
45
46
  selectionExtra?: React.ReactNode;
47
+ /** Every row on the page is ticked: the select-all box reads checked, else partial. */
48
+ allOnPageSelected?: boolean;
46
49
  toolbarRightSlot?: React.ReactNode;
47
50
  /** Search + filters, rendered on the toolbar line (see `GridToolbar`). */
48
51
  filterControls?: React.ReactNode;
@@ -75,6 +78,7 @@ export function ShellToolbar<T extends Record<string, unknown>>({
75
78
  rowActions={rowActions}
76
79
  bulkActions={bulkActions}
77
80
  selectionExtra={selectionExtra}
81
+ allOnPageSelected={allOnPageSelected}
78
82
  sortFields={c.resolvedSortFields}
79
83
  activeSortField={c.activeSortField}
80
84
  activeSortOrder={c.activeSortOrder}
@@ -28,6 +28,8 @@ export interface GridToolbarProps<T extends Record<string, unknown>> {
28
28
  bulkActions?: (selectedRows: T[], clearSelection: () => void) => React.ReactNode;
29
29
  /** A control between the count and the actions menu — see `selectionExtra`. */
30
30
  selectionExtra?: React.ReactNode;
31
+ /** Every row on the page is ticked: the select-all box reads checked, else partial. */
32
+ allOnPageSelected?: boolean;
31
33
  sortFields: SortFieldDefinition[];
32
34
  activeSortField: string;
33
35
  activeSortOrder: "asc" | "desc";
@@ -160,11 +162,13 @@ export function GridToolbar<T extends Record<string, unknown>>(props: GridToolba
160
162
  clearAllText={copy.filters.clearAll}
161
163
  hasSelection={selectedRows.length > 0}
162
164
  selectedCount={selectedRows.length}
165
+ selectedCountLabel={copy.selection.selectedCount}
163
166
  selectAll={props.selectAll}
164
167
  clearSelection={props.clearSelection}
165
168
  selectAllTestId={`${testIdPrefix}-select-all`}
166
169
  clearAllTestId={`${testIdPrefix}-clear-all`}
167
170
  selectionExtra={props.selectionExtra}
171
+ allSelected={props.allOnPageSelected}
168
172
  actions={renderBulkActions({
169
173
  rowActions: props.rowActions,
170
174
  bulkActions: props.bulkActions,
@@ -0,0 +1,134 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+
5
+ import type { RangeValue } from "./data-views-range-types";
6
+ import type { DataViewServer, DataViewState, DataViewSyncState } from "./data-views-types";
7
+
8
+ /**
9
+ * A server-mode grid's URL round trip, and the echo it must not re-apply.
10
+ *
11
+ * A server-mode host mirrors the grid's query into its URL and hands it back as
12
+ * `initialState`, which the base turns into `syncState` and MERGES over the live
13
+ * state, so browser back/forward moves the controls. But the browser shows the
14
+ * new URL before React renders it. An edit made in that window, such as
15
+ * clearing a date bound right after typing it, is overwritten when the render
16
+ * carrying the PREVIOUS query lands. The grid then re-emits that stale query,
17
+ * and the cleared filter comes back and sticks in the URL (FUT-3182, the
18
+ * audit trail's "De" bound).
19
+ *
20
+ * So the base records every query it emits. An `initialState` matching one
21
+ * still pending is the host echoing it back, not a navigation, and it is not
22
+ * merged. Anything else (back/forward, a deep link, a host-side reset) still is.
23
+ * A host whose round trip changes the slice's shape never matches, and keeps
24
+ * the old behaviour rather than losing a navigation.
25
+ */
26
+
27
+ /** The URL-carried part of a grid state or query. */
28
+ type UrlSlice = Pick<DataViewState, "search" | "pills" | "ranges" | "sortBy" | "scope">;
29
+
30
+ /** Hosts drop unset bounds on the way through the URL, so the key drops them too. */
31
+ function bounds(range: RangeValue): RangeValue | null {
32
+ const set = (value: number | string | undefined): boolean => value !== undefined && value !== "";
33
+ const kept: RangeValue = {
34
+ ...(set(range.min) ? { min: range.min } : {}),
35
+ ...(set(range.max) ? { max: range.max } : {}),
36
+ };
37
+ return Object.keys(kept).length > 0 ? kept : null;
38
+ }
39
+
40
+ const byKey = ([a]: [string, unknown], [b]: [string, unknown]): number => a.localeCompare(b);
41
+
42
+ /** One comparable string for a URL slice: empty pills and unbounded ranges left out. */
43
+ export function urlSliceKey(slice: UrlSlice): string {
44
+ const pills = Object.entries(slice.pills ?? {})
45
+ .filter(([, values]) => values.length > 0)
46
+ .sort(byKey);
47
+ const ranges = Object.entries(slice.ranges ?? {})
48
+ .map(([id, range]): [string, RangeValue | null] => [id, bounds(range)])
49
+ .filter(([, range]) => range !== null)
50
+ .sort(byKey);
51
+ return JSON.stringify({
52
+ q: slice.search ?? "",
53
+ pills,
54
+ ranges,
55
+ sort: slice.sortBy ?? [],
56
+ scope: slice.scope ?? null,
57
+ });
58
+ }
59
+
60
+ /** Enough to cover a burst of edits; older entries can no longer echo. */
61
+ const MAX_PENDING = 20;
62
+
63
+ /**
64
+ * Record what the grid emits, and recognise it coming back.
65
+ *
66
+ * `consumeEcho` answers whether a URL slice is one of the queries still in
67
+ * flight. A match drops it and every older one, since the host has caught up to
68
+ * at least that far; a miss is a navigation and clears the list.
69
+ */
70
+ export function useUrlEchoGuard(server: DataViewServer | undefined): {
71
+ server: DataViewServer | undefined;
72
+ consumeEcho: (slice: UrlSlice) => boolean;
73
+ } {
74
+ const pending = useRef<string[]>([]);
75
+ const recorded: DataViewServer | undefined = server && {
76
+ ...server,
77
+ onQueryChange: (query) => {
78
+ pending.current = [...pending.current, urlSliceKey(query)].slice(-MAX_PENDING);
79
+ server.onQueryChange(query);
80
+ },
81
+ };
82
+ const consumeEcho = (slice: UrlSlice): boolean => {
83
+ const at = pending.current.lastIndexOf(urlSliceKey(slice));
84
+ pending.current = at === -1 ? [] : pending.current.slice(at + 1);
85
+ return at !== -1;
86
+ };
87
+ return { server: recorded, consumeEcho };
88
+ }
89
+
90
+ function syncFrom(serverMode: boolean, initialState: DataViewState | undefined): DataViewSyncState | undefined {
91
+ if (!serverMode || !initialState) return undefined;
92
+ return {
93
+ search: initialState.search,
94
+ pills: initialState.pills,
95
+ ranges: initialState.ranges,
96
+ sortBy: initialState.sortBy,
97
+ // The scope belongs in the URL-driven slice alongside them: a
98
+ // `?view=recusados`-style deep link and browser back/forward must move the
99
+ // tab strip, and they must do it WITHOUT resetting the user's hidden
100
+ // columns — which is the whole reason this is a merging `syncState` and
101
+ // not a replacing `appliedState`.
102
+ scope: initialState.scope,
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Derive the grid's reactive `syncState` from the URL-seeded `initialState` in
108
+ * server mode, so browser back/forward RE-APPLIES the search/pills/sort controls
109
+ * (merging over live state, preserving hidden columns — see
110
+ * {@link DataViewSyncState}). Client-mode tables (no `server`) get `undefined`.
111
+ *
112
+ * Seeded on the first render, so a deep link shows on mount. After that a new
113
+ * `initialState` reference is re-applied unless it is the grid's own query
114
+ * coming back (`consumeEcho`), which would overwrite an edit made since.
115
+ */
116
+ export function useUrlSyncState(
117
+ server: DataViewServer | undefined,
118
+ initialState: DataViewState | undefined,
119
+ consumeEcho: (slice: UrlSlice) => boolean,
120
+ ): DataViewSyncState | undefined {
121
+ const serverMode = server !== undefined;
122
+ const [syncState, setSyncState] = useState(() => syncFrom(serverMode, initialState));
123
+ const seen = useRef({ serverMode, initialState });
124
+ useEffect(() => {
125
+ const last = seen.current;
126
+ if (last.serverMode === serverMode && last.initialState === initialState) return;
127
+ seen.current = { serverMode, initialState };
128
+ const next = syncFrom(serverMode, initialState);
129
+ if (next && consumeEcho(next)) return;
130
+ setSyncState(next);
131
+ // `consumeEcho` reads a ref; only a new URL seed or a mode change re-runs this.
132
+ }, [serverMode, initialState]);
133
+ return syncState;
134
+ }