@12-apps/ui 6.83.0 → 6.84.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 (240) hide show
  1. package/dist/_internal/components/data-display/Avatar/Avatar.js +3 -3
  2. package/dist/_internal/components/data-display/Avatar/Avatar.view.js +2 -2
  3. package/dist/_internal/components/data-display/Carousel/Carousel.chrome.js +3 -3
  4. package/dist/_internal/components/data-display/Carousel/Carousel.js +5 -5
  5. package/dist/_internal/components/data-display/Carousel/Carousel.parts.js +2 -2
  6. package/dist/_internal/components/data-display/Chart/Chart.js +3 -3
  7. package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
  8. package/dist/_internal/components/data-display/Chart/chart-renderers.js +2 -2
  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 +20 -19
  12. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +21 -20
  13. package/dist/_internal/components/data-display/DataViews/base-list-card.js +2 -2
  14. package/dist/_internal/components/data-display/DataViews/data-views-day-input.js +7 -3
  15. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +2 -2
  16. package/dist/_internal/components/data-display/DataViews/data-views-empty.js +1 -1
  17. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -4
  18. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +3 -3
  19. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +17 -16
  20. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +7 -6
  21. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +5 -4
  22. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +6 -5
  23. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +4 -3
  24. package/dist/_internal/components/data-display/DataViews/data-views-range-reset.js +9 -0
  25. package/dist/_internal/components/data-display/DataViews/data-views-range-reset.js.map +1 -0
  26. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +4 -4
  27. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +3 -3
  28. package/dist/_internal/components/data-display/DataViews/data-views-writers.js +3 -1
  29. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
  30. package/dist/_internal/components/data-display/FleetMap/FleetBody.js +8 -8
  31. package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +7 -7
  32. package/dist/_internal/components/data-display/FleetMap/FleetMap.fixtures.js +1 -1
  33. package/dist/_internal/components/data-display/FleetMap/FleetMap.js +9 -9
  34. package/dist/_internal/components/data-display/MapPreview/MapControlBar.js +3 -3
  35. package/dist/_internal/components/data-display/MapPreview/MapLayers.js +4 -4
  36. package/dist/_internal/components/data-display/MapPreview/MapMarkers.js +3 -3
  37. package/dist/_internal/components/data-display/MapPreview/MapPreview.helpers.js +2 -2
  38. package/dist/_internal/components/data-display/MapPreview/MapPreview.js +6 -6
  39. package/dist/_internal/components/data-display/index.js +29 -29
  40. package/dist/_internal/components/form/CepField/CepField.js +1 -1
  41. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.hooks.js +3 -3
  42. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +5 -5
  43. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.range.js +2 -2
  44. package/dist/_internal/components/form/DateRangePicker/DateRangePickerFields.js +3 -3
  45. package/dist/_internal/components/form/InputOTP/InputOTP.hooks.js +1 -1
  46. package/dist/_internal/components/form/InputOTP/InputOTP.js +2 -2
  47. package/dist/_internal/components/form/index.js +8 -8
  48. package/dist/_internal/components/form/total-form/Fields/CepField.js +2 -2
  49. package/dist/_internal/components/form/total-form/Fields/TextField.js +1 -1
  50. package/dist/_internal/components/form/total-form/Fields/index.js +4 -4
  51. package/dist/_internal/components/index.js +37 -37
  52. package/dist/_internal/en-US.data-display.js +1 -1
  53. package/dist/_internal/pt-BR.data-display.js +1 -1
  54. package/dist/_internal/user-avatar/user-avatar.js +4 -4
  55. package/dist/charts.js +11 -11
  56. package/dist/{chunk-HF2WLLGH.js → chunk-2XI4R6UO.js} +2 -2
  57. package/dist/{chunk-HF2WLLGH.js.map → chunk-2XI4R6UO.js.map} +1 -1
  58. package/dist/{chunk-GYFKIPR2.js → chunk-4PUCT3RX.js} +2 -2
  59. package/dist/{chunk-JQSF3CUR.js → chunk-6HMDUUVJ.js} +2 -1
  60. package/dist/{chunk-JQSF3CUR.js.map → chunk-6HMDUUVJ.js.map} +1 -1
  61. package/dist/{chunk-EBEMNKTK.js → chunk-6ONQTXFM.js} +7 -4
  62. package/dist/chunk-6ONQTXFM.js.map +1 -0
  63. package/dist/{chunk-C2I7NQC4.js → chunk-74KCGLOU.js} +2 -2
  64. package/dist/{chunk-4J62V5OE.js → chunk-7YJYIMWP.js} +2 -2
  65. package/dist/{chunk-MT74NRLY.js → chunk-C2OX3C7L.js} +4 -4
  66. package/dist/{chunk-62XVHRTX.js → chunk-D7SOXI2K.js} +70 -21
  67. package/dist/chunk-D7SOXI2K.js.map +1 -0
  68. package/dist/{chunk-BO6IMK5P.js → chunk-DPHSJLMN.js} +2 -2
  69. package/dist/{chunk-7XSCEHGA.js → chunk-EOQ6EKTS.js} +7 -7
  70. package/dist/{chunk-4OTJ66ID.js → chunk-EXTES7LP.js} +15 -4
  71. package/dist/chunk-EXTES7LP.js.map +1 -0
  72. package/dist/{chunk-4IVEBBF6.js → chunk-FYJDI5BW.js} +4 -3
  73. package/dist/{chunk-4IVEBBF6.js.map → chunk-FYJDI5BW.js.map} +1 -1
  74. package/dist/{chunk-A5U7HEBI.js → chunk-GNDIVKWF.js} +4 -2
  75. package/dist/chunk-GNDIVKWF.js.map +1 -0
  76. package/dist/{chunk-XKPV52CG.js → chunk-GPTNZ57P.js} +2 -2
  77. package/dist/{chunk-ZGAOA7LN.js → chunk-H56CRMCM.js} +6 -6
  78. package/dist/{chunk-AMLHEKSV.js → chunk-HI7JPDD6.js} +5 -5
  79. package/dist/{chunk-JK3QB7MO.js → chunk-I54LHHZS.js} +2 -2
  80. package/dist/{chunk-GLAAX5JJ.js → chunk-I5NTADBO.js} +2 -1
  81. package/dist/{chunk-GLAAX5JJ.js.map → chunk-I5NTADBO.js.map} +1 -1
  82. package/dist/{chunk-LCGK22QQ.js → chunk-IKXCAYFH.js} +5 -5
  83. package/dist/{chunk-MLGAGZJ3.js → chunk-ILNAPS44.js} +8 -7
  84. package/dist/chunk-ILNAPS44.js.map +1 -0
  85. package/dist/{chunk-D22FGMGU.js → chunk-JIAIPOEV.js} +2 -2
  86. package/dist/{chunk-HQCZT6TO.js → chunk-JVOEVTCZ.js} +5 -5
  87. package/dist/{chunk-76IHYINM.js → chunk-KEBYZ2MO.js} +16 -9
  88. package/dist/chunk-KEBYZ2MO.js.map +1 -0
  89. package/dist/{chunk-JJNY5MZL.js → chunk-KG7OTJ26.js} +2 -2
  90. package/dist/{chunk-2ZMR3TZU.js → chunk-L6UTYEUS.js} +3 -3
  91. package/dist/{chunk-WWB43LWU.js → chunk-LOGSJH2Q.js} +2 -2
  92. package/dist/{chunk-YJPBNIEM.js → chunk-MGFTHOPG.js} +2 -2
  93. package/dist/{chunk-RB3R6BJL.js → chunk-O4G7QKDU.js} +2 -2
  94. package/dist/{chunk-DYHOBKPU.js → chunk-OA6T2ME3.js} +5 -5
  95. package/dist/{chunk-U5FOJBLG.js → chunk-OOLES6RR.js} +14 -2
  96. package/dist/chunk-OOLES6RR.js.map +1 -0
  97. package/dist/{chunk-YHKHHSZH.js → chunk-OQ554425.js} +3 -3
  98. package/dist/{chunk-54OJAOXC.js → chunk-PDSZN26U.js} +2 -2
  99. package/dist/{chunk-MNNIOQTQ.js → chunk-PGVGCDPD.js} +3 -3
  100. package/dist/{chunk-RDRXTH47.js → chunk-PWXDMP7G.js} +10 -6
  101. package/dist/chunk-PWXDMP7G.js.map +1 -0
  102. package/dist/{chunk-6ATXOJVQ.js → chunk-QADTI63U.js} +4 -4
  103. package/dist/{chunk-UETHX57J.js → chunk-R6L376KR.js} +21 -7
  104. package/dist/chunk-R6L376KR.js.map +1 -0
  105. package/dist/{chunk-SEUJNFOD.js → chunk-RLDTV5I2.js} +2 -2
  106. package/dist/{chunk-4UCXJYOO.js → chunk-RUQNKOSP.js} +4 -4
  107. package/dist/chunk-RUQNKOSP.js.map +1 -0
  108. package/dist/{chunk-7RG37FVT.js → chunk-RXKFPXVH.js} +29 -2
  109. package/dist/chunk-RXKFPXVH.js.map +1 -0
  110. package/dist/{chunk-KSJBFT4G.js → chunk-SGAHME6W.js} +3 -3
  111. package/dist/{chunk-SB2SEGR6.js → chunk-TIM4PXWN.js} +3 -3
  112. package/dist/{chunk-D4LMRSY7.js → chunk-UIWVSZ6I.js} +2 -2
  113. package/dist/{chunk-GJOU26UW.js → chunk-V2UJEICC.js} +3 -3
  114. package/dist/chunk-V2UJEICC.js.map +1 -0
  115. package/dist/{chunk-XATRYJ4Q.js → chunk-VQBY73QH.js} +4 -4
  116. package/dist/{chunk-SYERREN5.js → chunk-W2JYXZC2.js} +4 -4
  117. package/dist/{chunk-XNLJ2Z7T.js → chunk-YC2QYRNE.js} +5 -3
  118. package/dist/chunk-YC2QYRNE.js.map +1 -0
  119. package/dist/chunk-YOPMXGHY.js +23 -0
  120. package/dist/chunk-YOPMXGHY.js.map +1 -0
  121. package/dist/{chunk-QK3XXPBA.js → chunk-YWJCMGTE.js} +2 -2
  122. package/dist/{chunk-UQ6SAIX7.js → chunk-Z47SYKRX.js} +2 -2
  123. package/dist/data-display/Avatar.js +3 -3
  124. package/dist/data-display/CardKit.js +3 -3
  125. package/dist/data-display/Carousel.js +5 -5
  126. package/dist/data-display/Chart.js +11 -11
  127. package/dist/data-display/DataGrid.js +2 -2
  128. package/dist/data-display/DataViews.js +25 -24
  129. package/dist/data-display/FleetMap.js +9 -9
  130. package/dist/data-display/MapPreview.js +6 -6
  131. package/dist/en-US.js +1 -1
  132. package/dist/form/CepField.js +1 -1
  133. package/dist/form/DateRangePicker.js +5 -5
  134. package/dist/form/InputOTP.js +2 -2
  135. package/dist/form/total-form.js +4 -4
  136. package/dist/locales.js +2 -2
  137. package/dist/pt-BR.js +1 -1
  138. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +6 -0
  139. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  140. package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts +32 -5
  141. package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts.map +1 -1
  142. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts +6 -0
  143. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts.map +1 -1
  144. package/dist/types/components/data-display/DataViews/data-views-grid-parts.d.ts.map +1 -1
  145. package/dist/types/components/data-display/DataViews/data-views-inline-bar.d.ts +3 -1
  146. package/dist/types/components/data-display/DataViews/data-views-inline-bar.d.ts.map +1 -1
  147. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts +3 -1
  148. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  149. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  150. package/dist/types/components/data-display/DataViews/data-views-range-reset.d.ts +16 -0
  151. package/dist/types/components/data-display/DataViews/data-views-range-reset.d.ts.map +1 -0
  152. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts +13 -0
  153. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts.map +1 -1
  154. package/dist/types/components/data-display/DataViews/use-data-views-state.d.ts +4 -0
  155. package/dist/types/components/data-display/DataViews/use-data-views-state.d.ts.map +1 -1
  156. package/dist/types/components/form/CepField/CepField.d.ts +1 -1
  157. package/dist/types/components/form/CepField/CepField.d.ts.map +1 -1
  158. package/dist/types/components/form/CepField/CepField.types.d.ts +8 -0
  159. package/dist/types/components/form/CepField/CepField.types.d.ts.map +1 -1
  160. package/dist/types/components/form/InputOTP/InputOTP.d.ts.map +1 -1
  161. package/dist/types/components/form/InputOTP/InputOTP.hooks.d.ts.map +1 -1
  162. package/dist/types/components/form/total-form/Fields/CepField.d.ts +6 -1
  163. package/dist/types/components/form/total-form/Fields/CepField.d.ts.map +1 -1
  164. package/dist/types/components/form/total-form/Fields/TextField.d.ts +22 -1
  165. package/dist/types/components/form/total-form/Fields/TextField.d.ts.map +1 -1
  166. package/dist/types/components/form/total-form/Fields/index.d.ts +1 -1
  167. package/dist/types/components/form/total-form/Fields/index.d.ts.map +1 -1
  168. package/dist/types/components/form/total-form/index.d.ts +1 -1
  169. package/dist/types/components/form/total-form/index.d.ts.map +1 -1
  170. package/dist/types/en-US.data-display.d.ts.map +1 -1
  171. package/dist/types/pt-BR.data-display.d.ts.map +1 -1
  172. package/dist/user-avatar.js +4 -4
  173. package/package.json +1 -1
  174. package/src/components/data-display/DataViews/data-views-copy.ts +6 -0
  175. package/src/components/data-display/DataViews/data-views-day-input.tsx +134 -38
  176. package/src/components/data-display/DataViews/data-views-empty.tsx +1 -1
  177. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +8 -2
  178. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +3 -1
  179. package/src/components/data-display/DataViews/data-views-inline-bar.tsx +7 -0
  180. package/src/components/data-display/DataViews/data-views-more-filters.tsx +4 -1
  181. package/src/components/data-display/DataViews/data-views-range-pill.tsx +11 -3
  182. package/src/components/data-display/DataViews/data-views-range-reset.ts +34 -0
  183. package/src/components/data-display/DataViews/data-views-writers.ts +24 -1
  184. package/src/components/data-display/DataViews/use-data-views-state.ts +6 -1
  185. package/src/components/form/CepField/CepField.md +7 -2
  186. package/src/components/form/CepField/CepField.tsx +13 -2
  187. package/src/components/form/CepField/CepField.types.ts +8 -0
  188. package/src/components/form/InputOTP/InputOTP.hooks.ts +48 -9
  189. package/src/components/form/InputOTP/InputOTP.md +6 -1
  190. package/src/components/form/InputOTP/InputOTP.tsx +40 -1
  191. package/src/components/form/total-form/Fields/CepField.tsx +7 -0
  192. package/src/components/form/total-form/Fields/TextField.tsx +64 -1
  193. package/src/components/form/total-form/Fields/index.ts +1 -1
  194. package/src/components/form/total-form/index.ts +1 -1
  195. package/src/en-US.data-display.ts +1 -0
  196. package/src/pt-BR.data-display.ts +1 -0
  197. package/dist/chunk-4OTJ66ID.js.map +0 -1
  198. package/dist/chunk-4UCXJYOO.js.map +0 -1
  199. package/dist/chunk-62XVHRTX.js.map +0 -1
  200. package/dist/chunk-76IHYINM.js.map +0 -1
  201. package/dist/chunk-7RG37FVT.js.map +0 -1
  202. package/dist/chunk-A5U7HEBI.js.map +0 -1
  203. package/dist/chunk-EBEMNKTK.js.map +0 -1
  204. package/dist/chunk-GJOU26UW.js.map +0 -1
  205. package/dist/chunk-MLGAGZJ3.js.map +0 -1
  206. package/dist/chunk-RDRXTH47.js.map +0 -1
  207. package/dist/chunk-U5FOJBLG.js.map +0 -1
  208. package/dist/chunk-UETHX57J.js.map +0 -1
  209. package/dist/chunk-XNLJ2Z7T.js.map +0 -1
  210. /package/dist/{chunk-GYFKIPR2.js.map → chunk-4PUCT3RX.js.map} +0 -0
  211. /package/dist/{chunk-C2I7NQC4.js.map → chunk-74KCGLOU.js.map} +0 -0
  212. /package/dist/{chunk-4J62V5OE.js.map → chunk-7YJYIMWP.js.map} +0 -0
  213. /package/dist/{chunk-MT74NRLY.js.map → chunk-C2OX3C7L.js.map} +0 -0
  214. /package/dist/{chunk-BO6IMK5P.js.map → chunk-DPHSJLMN.js.map} +0 -0
  215. /package/dist/{chunk-7XSCEHGA.js.map → chunk-EOQ6EKTS.js.map} +0 -0
  216. /package/dist/{chunk-XKPV52CG.js.map → chunk-GPTNZ57P.js.map} +0 -0
  217. /package/dist/{chunk-ZGAOA7LN.js.map → chunk-H56CRMCM.js.map} +0 -0
  218. /package/dist/{chunk-AMLHEKSV.js.map → chunk-HI7JPDD6.js.map} +0 -0
  219. /package/dist/{chunk-JK3QB7MO.js.map → chunk-I54LHHZS.js.map} +0 -0
  220. /package/dist/{chunk-LCGK22QQ.js.map → chunk-IKXCAYFH.js.map} +0 -0
  221. /package/dist/{chunk-D22FGMGU.js.map → chunk-JIAIPOEV.js.map} +0 -0
  222. /package/dist/{chunk-HQCZT6TO.js.map → chunk-JVOEVTCZ.js.map} +0 -0
  223. /package/dist/{chunk-JJNY5MZL.js.map → chunk-KG7OTJ26.js.map} +0 -0
  224. /package/dist/{chunk-2ZMR3TZU.js.map → chunk-L6UTYEUS.js.map} +0 -0
  225. /package/dist/{chunk-WWB43LWU.js.map → chunk-LOGSJH2Q.js.map} +0 -0
  226. /package/dist/{chunk-YJPBNIEM.js.map → chunk-MGFTHOPG.js.map} +0 -0
  227. /package/dist/{chunk-RB3R6BJL.js.map → chunk-O4G7QKDU.js.map} +0 -0
  228. /package/dist/{chunk-DYHOBKPU.js.map → chunk-OA6T2ME3.js.map} +0 -0
  229. /package/dist/{chunk-YHKHHSZH.js.map → chunk-OQ554425.js.map} +0 -0
  230. /package/dist/{chunk-54OJAOXC.js.map → chunk-PDSZN26U.js.map} +0 -0
  231. /package/dist/{chunk-MNNIOQTQ.js.map → chunk-PGVGCDPD.js.map} +0 -0
  232. /package/dist/{chunk-6ATXOJVQ.js.map → chunk-QADTI63U.js.map} +0 -0
  233. /package/dist/{chunk-SEUJNFOD.js.map → chunk-RLDTV5I2.js.map} +0 -0
  234. /package/dist/{chunk-KSJBFT4G.js.map → chunk-SGAHME6W.js.map} +0 -0
  235. /package/dist/{chunk-SB2SEGR6.js.map → chunk-TIM4PXWN.js.map} +0 -0
  236. /package/dist/{chunk-D4LMRSY7.js.map → chunk-UIWVSZ6I.js.map} +0 -0
  237. /package/dist/{chunk-XATRYJ4Q.js.map → chunk-VQBY73QH.js.map} +0 -0
  238. /package/dist/{chunk-SYERREN5.js.map → chunk-W2JYXZC2.js.map} +0 -0
  239. /package/dist/{chunk-QK3XXPBA.js.map → chunk-YWJCMGTE.js.map} +0 -0
  240. /package/dist/{chunk-UQ6SAIX7.js.map → chunk-Z47SYKRX.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { type Dispatch, type SetStateAction } from "react";
3
+ import { type Dispatch, type SetStateAction, useState } from "react";
4
4
 
5
5
  import { type DataViewState, type DataViewStatePatch } from "./data-views-types";
6
6
 
@@ -43,3 +43,26 @@ export function useStateWriters(setState: Dispatch<SetStateAction<DataViewState>
43
43
  })),
44
44
  };
45
45
  }
46
+
47
+ /**
48
+ * "Limpar filtros", from ANY surface — the bar, the slide-in panel, the modal,
49
+ * the empty state — plus a key that changes with every click of it.
50
+ *
51
+ * The filter controls are keyed on `clearKey`, so a clear remounts them. That
52
+ * is what empties a field holding text the filter never applied: a refused day
53
+ * (`31/02/2026`) under a range that was open already. Clearing `{}` to `{}`
54
+ * changes no state, so nothing else would reach that field (FUT-4144).
55
+ */
56
+ export function useClearFilters(patch: (next: DataViewStatePatch) => void): {
57
+ clearKey: number;
58
+ clearFilters: () => void;
59
+ } {
60
+ const [clearKey, setClearKey] = useState(0);
61
+ return {
62
+ clearKey,
63
+ clearFilters: () => {
64
+ patch({ search: "", pills: {}, ranges: {} });
65
+ setClearKey((n) => n + 1);
66
+ },
67
+ };
68
+ }
@@ -16,7 +16,7 @@ import { computeActiveFilterCount, defaultSortFields } from "./data-views-grid-h
16
16
  import { resolveScope, useScopeConfigChecks, type ScopeConfig } from "./data-views-scopes";
17
17
  import { serverDerived, useServerQuery } from "./data-views-server-query";
18
18
  import { useSelection } from "./data-views-selection";
19
- import { useStateWriters } from "./data-views-writers";
19
+ import { useClearFilters, useStateWriters } from "./data-views-writers";
20
20
  import {
21
21
  emptyViewState,
22
22
  type DataViewColumn,
@@ -85,6 +85,10 @@ export interface DataViewsController<T extends Record<string, unknown>> {
85
85
  * its patch from the same render-scoped snapshot and the last write would win.
86
86
  */
87
87
  patch: (next: DataViewStatePatch) => void;
88
+ /** Drop every filter — search, pills, ranges — from any surface that offers it. */
89
+ clearFilters: () => void;
90
+ /** Changes on every {@link clearFilters}; the filter controls are keyed on it. */
91
+ clearKey: number;
88
92
  matched: T[];
89
93
  gridColumns: GridColumn<T>[];
90
94
  /** Columns eligible for the show/hide menu (hideable, with a text header). */
@@ -355,6 +359,7 @@ export function useDataViewsState<T extends Record<string, unknown>>({
355
359
  state,
356
360
  ranges: state.ranges ?? {},
357
361
  patch,
362
+ ...useClearFilters(patch),
358
363
  matched,
359
364
  gridColumns,
360
365
  hideableColumns,
@@ -48,6 +48,10 @@ serves a supplier form, a store-location form or a delivery checkout.
48
48
  stands level with `sm` `Input`s
49
49
  - `labelPlacement`: `above` (default) stacks the label over the box; `inside` draws it
50
50
  in the box and floats it up on focus or a value, exactly as `Input`'s label does
51
+ - `autoComplete`: the `<input>`'s `autocomplete` (default `postal-code`). Pass `off` on a
52
+ form for somebody else's address (a supplier), or the browser offers the signed-in
53
+ person's own CEP and the lookup then fills their street (FUT-4140). Same prop on
54
+ `Fields.CepField`
51
55
 
52
56
  ### State Props
53
57
 
@@ -154,7 +158,8 @@ the lookup. The pure helpers (`formatCep`, `isValidCep`) live in
154
158
  "Buscando endereço…" / "Endereço preenchido pelo CEP" without stealing focus
155
159
  - `aria-busy` reflects the in-flight lookup
156
160
  - `aria-describedby` links the input to its status region
157
- - `inputMode="numeric"` brings up the numeric keypad on mobile
161
+ - `inputMode="numeric"` sits on the `<input>` (through `inputProps`), so it brings up the
162
+ numeric keypad on mobile; at the top level it landed on the wrapper `div` and did nothing
158
163
  - With `labelPlacement="inside"` the label is the `Input`'s own, associated to the input
159
164
  by `for`/`id`, so `getByLabelText` and screen readers name the field the same way
160
- - `autoComplete="postal-code"` lets the browser's own autofill participate
165
+ - `autoComplete="postal-code"` (the default) lets the browser's own autofill participate
@@ -118,6 +118,12 @@ function CepStatus({
118
118
  );
119
119
  }
120
120
 
121
+ /** What only the `<input>` can act on. Hoisted: one object for every render. */
122
+ const CEP_INPUT_PROPS = { inputMode: 'numeric' } as const;
123
+
124
+ /** `postal-code` unless the caller said otherwise (`off` for somebody else's address). */
125
+ const autoCompleteOf = (autoComplete: string | undefined): string => autoComplete ?? 'postal-code';
126
+
121
127
  /** The stacked label over the box (`labelPlacement="above"`), or nothing when there is no label. */
122
128
  function AboveLabel({
123
129
  name,
@@ -181,6 +187,7 @@ export function CepField({
181
187
  copy,
182
188
  size, // undefined reaches `Input`, whose own default is `md`
183
189
  labelPlacement = 'above',
190
+ autoComplete,
184
191
  }: CepFieldProps): React.ReactElement {
185
192
  const testId = dataTestId ?? `cep-field-${name}`;
186
193
 
@@ -206,8 +213,12 @@ export function CepField({
206
213
  id={name}
207
214
  name={name}
208
215
  type="text"
209
- inputMode="numeric"
210
- autoComplete="postal-code"
216
+ // On the `<input>`, through `inputProps`: a top-level `inputMode` is a
217
+ // prop `Input` does not recognise, so it landed on the wrapper `div`
218
+ // and the phone kept opening the letter keyboard for eight digits
219
+ // (FUT-1440's CepField row).
220
+ inputProps={CEP_INPUT_PROPS}
221
+ autoComplete={autoCompleteOf(autoComplete)}
211
222
  size={size}
212
223
  // `inside`: the label is the Input's own, drawn in the box and floating
213
224
  // up on focus or a value — what every other `Input` in the form does.
@@ -89,4 +89,12 @@ export interface CepFieldProps {
89
89
  * is the same height as its neighbours.
90
90
  */
91
91
  labelPlacement?: 'above' | 'inside';
92
+ /**
93
+ * The `<input>`'s `autocomplete`. Defaults to `postal-code`, which lets the
94
+ * browser offer the signed-in person's own CEP — right on their own address
95
+ * form, wrong on a form for somebody else's (a supplier): pass `off` there,
96
+ * or the owner's CEP goes in and the lookup fills the owner's street
97
+ * (FUT-4140).
98
+ */
99
+ autoComplete?: string;
92
100
  }
@@ -17,6 +17,36 @@ const isAllowed = (variant: string, text: string) => (PATTERNS[variant] ?? /.*/)
17
17
  const padTo = (digits: string[], length: number) =>
18
18
  Array.from({ length }, (_, i) => digits[i] ?? '');
19
19
 
20
+ /**
21
+ * What a change to the numeric FIRST slot is, now that its `maxLength` is the
22
+ * code's length rather than 1 (FUT-4144).
23
+ *
24
+ * That slot carries `one-time-code`, and the phone's SMS fill (or a password
25
+ * manager) writes the WHOLE code into it as one change, not a paste — kept to
26
+ * one character, the code lost every digit but the last. So:
27
+ *
28
+ * - `overtype`: a keystroke into the already-filled slot (its old character
29
+ * plus the new one). Ignored, exactly as `maxLength={1}` refused it before.
30
+ * - `code`: anything else longer than one character, written in one go.
31
+ * Spread across the slots like a paste.
32
+ *
33
+ * Every other slot and variant keeps `maxLength={1}` and never gets here.
34
+ */
35
+ function firstSlotFill(variant: string, index: number, inputValue: string, current: string) {
36
+ if (variant !== 'numeric' || index !== 0 || inputValue.length < 2) return null;
37
+ const overtype = inputValue.length === 2 && current !== '' && inputValue.includes(current);
38
+ return overtype ? 'overtype' : 'code';
39
+ }
40
+
41
+ /**
42
+ * The code inside a first-slot fill. A phone that INSERTS the SMS code at the
43
+ * caret rather than replacing the slot leaves the digit already there in
44
+ * front: `4` + `482913` arrives as `4482913`, which spread as-is would give
45
+ * `448291`. Longer than the code and led by that digit: the digit is dropped.
46
+ */
47
+ const codeOfFill = (inputValue: string, current: string, length: number): string =>
48
+ inputValue.length > length && current !== '' && inputValue.startsWith(current) ? inputValue.slice(1) : inputValue;
49
+
20
50
  interface OtpInput {
21
51
  variant: string;
22
52
  length: number;
@@ -55,8 +85,23 @@ export const useOtpDigits = ({ variant, length, value, onChange, onComplete }: O
55
85
  [length, onChange, onComplete],
56
86
  );
57
87
 
88
+ /** Lays a whole code across the slots from the first: a paste, or an OS/SMS fill. */
89
+ const spread = useCallback(
90
+ (text: string) => {
91
+ const code = text.slice(0, length);
92
+ // An empty code is not a rejection, but it is nothing to apply either.
93
+ if (!code || !isAllowed(variant, code)) return;
94
+
95
+ setDigits(padTo(code.split(''), length));
96
+ publish(code);
97
+ },
98
+ [variant, length, publish],
99
+ );
100
+
58
101
  const handleInputChange = useCallback(
59
102
  (index: number, inputValue: string) => {
103
+ const fill = firstSlotFill(variant, index, inputValue, digits[0] ?? '');
104
+ if (fill) return fill === 'code' ? spread(codeOfFill(inputValue, digits[0] ?? '', length)) : undefined;
60
105
  if (!isAllowed(variant, inputValue)) return;
61
106
 
62
107
  const next = [...digits];
@@ -68,7 +113,7 @@ export const useOtpDigits = ({ variant, length, value, onChange, onComplete }: O
68
113
  focusSlot(index + 1);
69
114
  }
70
115
  },
71
- [variant, digits, length, publish, focusSlot],
116
+ [variant, digits, length, publish, focusSlot, spread],
72
117
  );
73
118
 
74
119
  const handleKeyDown = useCallback(
@@ -89,15 +134,9 @@ export const useOtpDigits = ({ variant, length, value, onChange, onComplete }: O
89
134
  const handlePaste = useCallback(
90
135
  (e: React.ClipboardEvent) => {
91
136
  e.preventDefault();
92
- const pasted = e.clipboardData.getData('text').slice(0, length);
93
-
94
- // An empty paste is not a rejection, but it is nothing to apply either.
95
- if (!pasted || !isAllowed(variant, pasted)) return;
96
-
97
- setDigits(padTo(pasted.split(''), length));
98
- publish(pasted);
137
+ spread(e.clipboardData.getData('text'));
99
138
  },
100
- [variant, length, publish],
139
+ [spread],
101
140
  );
102
141
 
103
142
  return { digits, inputRefs, handleInputChange, handleKeyDown, handlePaste };
@@ -161,7 +161,12 @@ function CodeVerification() {
161
161
 
162
162
  The InputOTP component is designed with accessibility in mind:
163
163
 
164
- - **ARIA Labels**: Each input field has proper ARIA attributes
164
+ - **ARIA Labels**: The container is a `role="group"`, so the `aria-label` you pass names
165
+ the slots together
166
+ - **Phone keypad** (`variant="numeric"`, FUT-4144): every slot `<input>` carries
167
+ `inputmode="numeric"` and `pattern="[0-9]*"`, and the first slot
168
+ `autocomplete="one-time-code"` so the phone can offer a code from an SMS. The
169
+ `alphanumeric` and `masked` variants carry none of the three
165
170
  - **Keyboard Navigation**: Full keyboard support for all interactions
166
171
  - **Focus Management**: Clear focus indicators and logical tab order
167
172
  - **Screen Reader Support**: Announces field positions and values
@@ -15,6 +15,41 @@ const StyledOTPInput = styled(TextField, {
15
15
  !['customColor', 'customSize', 'glass', 'gradient'].includes(prop as string),
16
16
  })<OtpSlotFlags>(({ theme, ...flags }) => ({ ...otpSlotSx(theme, flags) }));
17
17
 
18
+ /**
19
+ * What tells a phone that a `numeric` code is digits (FUT-4144).
20
+ *
21
+ * The hook already refused letters, but nothing reached the `<input>` to say
22
+ * so, and the courier's six-digit join code opened the letter keyboard.
23
+ * `inputMode` picks the keypad; `pattern` is what iOS reads for the same; and
24
+ * `one-time-code` on the FIRST slot lets the phone offer a code from an SMS —
25
+ * the first slot, because it is the one that takes a paste and spreads it.
26
+ * `alphanumeric` and `masked` are left exactly as they were.
27
+ */
28
+ function keypadProps(variant: InputOTPProps['variant'], index: number): Record<string, string> {
29
+ if (variant !== 'numeric') return {};
30
+ return {
31
+ inputMode: 'numeric',
32
+ pattern: '[0-9]*',
33
+ ...(index === 0 ? { autoComplete: 'one-time-code' } : {}),
34
+ };
35
+ }
36
+
37
+ /**
38
+ * One character per slot — except the numeric FIRST slot, which carries
39
+ * `one-time-code`: the phone's SMS fill writes the whole code there in one
40
+ * change, and a `maxLength` of 1 would cut it to one digit before the hook
41
+ * could spread it.
42
+ *
43
+ * The code's length PLUS ONE: a phone that INSERTS the code at the caret keeps
44
+ * the digit already there in front (`4` + `482913`), and the browser caps that
45
+ * insertion at `maxlength` BEFORE the hook sees it. At the bare length it
46
+ * arrived as `448291` — a wrong code that looks whole. One more character lets
47
+ * the hook see the leading digit and drop it. A keystroke into the slot once
48
+ * filled is still refused, by the hook, as `maxLength={1}` refused it.
49
+ */
50
+ const slotMaxLength = (variant: InputOTPProps['variant'], index: number, length: number): number =>
51
+ variant === 'numeric' && index === 0 ? length + 1 : 1;
52
+
18
53
  const DEFAULTS = {
19
54
  variant: 'numeric',
20
55
  color: 'primary',
@@ -49,6 +84,9 @@ export const InputOTP = forwardRef<HTMLDivElement, InputOTPProps>((props, ref) =
49
84
  <Box
50
85
  ref={ref}
51
86
  data-testid={dataTestId}
87
+ // A group, so the `aria-label` a caller gives the code names the six
88
+ // slots together: on a plain `div` it named nothing anyone could reach.
89
+ role="group"
52
90
  sx={{ display: 'flex', gap: 1, justifyContent: 'center', alignItems: 'center' }}
53
91
  {...rest}
54
92
  >
@@ -72,9 +110,10 @@ export const InputOTP = forwardRef<HTMLDivElement, InputOTPProps>((props, ref) =
72
110
  error={error}
73
111
  disabled={disabled}
74
112
  inputProps={{
75
- maxLength: 1,
113
+ maxLength: slotMaxLength(variant, index, length),
76
114
  style: { textAlign: 'center' },
77
115
  'data-testid': dataTestId ? `${dataTestId}-slot-${index}` : undefined,
116
+ ...keypadProps(variant, index),
78
117
  }}
79
118
  />
80
119
  ))}
@@ -34,6 +34,11 @@ export interface CepFieldProps {
34
34
  size?: InputSize;
35
35
  /** `above` (default) stacks the label over the box; `inside` floats it in the box like `Input`. */
36
36
  labelPlacement?: 'above' | 'inside';
37
+ /**
38
+ * The `<input>`'s `autocomplete`. Defaults to `postal-code`; pass `off` when
39
+ * the form holds somebody else's address (FUT-4140).
40
+ */
41
+ autoComplete?: string;
37
42
  }
38
43
 
39
44
  const PARTS: readonly CepPart[] = ['street', 'neighborhood', 'city', 'state'];
@@ -114,6 +119,7 @@ export function CepField({
114
119
  copy,
115
120
  size,
116
121
  labelPlacement,
122
+ autoComplete,
117
123
  }: CepFieldProps): React.ReactElement {
118
124
  const { values, errors, setFieldValue } = useFormContext();
119
125
  const { write, clearOwned } = useOwnedAutofill();
@@ -151,6 +157,7 @@ export function CepField({
151
157
  debounceMs={debounceMs}
152
158
  size={size}
153
159
  labelPlacement={labelPlacement}
160
+ autoComplete={autoComplete}
154
161
  dataTestId={`total-form-field-${name}`}
155
162
  />
156
163
  );
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import type { InputHTMLAttributes } from 'react';
3
4
  import React from 'react';
4
5
 
5
6
  import { FormControl, FormLabel, FormMessage } from '../../Form';
@@ -16,6 +17,59 @@ export interface TextFieldProps {
16
17
  type?: string;
17
18
  /** Placeholder shown when the field is empty. */
18
19
  placeholder?: string;
20
+ /**
21
+ * The `autocomplete` attribute of the `<input>`. Omitted, the browser decides
22
+ * from the `name` — which is how a supplier's `name`/`phone`/`email` gets
23
+ * offered the signed-in owner's own (FUT-4140). Pass `off` for somebody
24
+ * else's data, `new-password` for a secret the password manager must not
25
+ * fill. An `inputProps.autoComplete` wins over this one.
26
+ */
27
+ autoComplete?: string;
28
+ /**
29
+ * Attributes for the `<input>` element itself — `inputMode`, `autoCapitalize`,
30
+ * `autoCorrect`, `spellCheck`, `maxLength`, `pattern`, `enterKeyHint`…
31
+ *
32
+ * They reach the `<input>`, never the wrapper `div`, where a keypad hint does
33
+ * nothing. What the field owns — `id`, `name`, `type`, `value`, `onChange`,
34
+ * `placeholder` and the `total-form-field-<name>` test id — is not
35
+ * overridable here.
36
+ */
37
+ inputProps?: TextFieldInputProps;
38
+ }
39
+
40
+ /** The `<input>` attributes {@link TextField} lets a caller set (see `inputProps`). */
41
+ export type TextFieldInputProps = Omit<
42
+ InputHTMLAttributes<HTMLInputElement>,
43
+ 'id' | 'name' | 'type' | 'value' | 'defaultValue' | 'onChange' | 'placeholder' | 'size' | 'color'
44
+ >;
45
+
46
+ /** The keys {@link TextFieldInputProps} leaves out, also dropped at runtime. */
47
+ const FIELD_OWNED = [
48
+ 'id',
49
+ 'name',
50
+ 'type',
51
+ 'value',
52
+ 'defaultValue',
53
+ 'onChange',
54
+ 'placeholder',
55
+ 'size',
56
+ 'color',
57
+ 'data-testid',
58
+ ];
59
+
60
+ /**
61
+ * The caller's `<input>` attributes, minus what the field owns.
62
+ *
63
+ * The type already refuses those keys; plain JS or a cast does not, and a
64
+ * renamed id, name or test id would silently unbind the label, the form value
65
+ * or every spec that finds the field by it. So they are dropped here too, and
66
+ * the field's own test id is written last.
67
+ */
68
+ function callerInputProps(inputProps: TextFieldInputProps | undefined, testId: string): Record<string, unknown> {
69
+ const kept = Object.fromEntries(
70
+ Object.entries(inputProps ?? {}).filter(([key]) => !FIELD_OWNED.includes(key)),
71
+ );
72
+ return { ...kept, 'data-testid': testId };
19
73
  }
20
74
 
21
75
  /**
@@ -26,7 +80,14 @@ export interface TextFieldProps {
26
80
  * Renders its error via `total-form-field-<name>-message` and the input via
27
81
  * `total-form-field-<name>`.
28
82
  */
29
- export function TextField({ name, label, type = 'text', placeholder }: TextFieldProps): React.ReactElement {
83
+ export function TextField({
84
+ name,
85
+ label,
86
+ type = 'text',
87
+ placeholder,
88
+ autoComplete,
89
+ inputProps,
90
+ }: TextFieldProps): React.ReactElement {
30
91
  const { values, errors, setFieldValue } = useFormContext();
31
92
  const error = errors[name];
32
93
  const testId = `total-form-field-${name}`;
@@ -45,6 +106,8 @@ export function TextField({ name, label, type = 'text', placeholder }: TextField
45
106
  placeholder={placeholder}
46
107
  value={values[name] ?? ''}
47
108
  error={Boolean(error)}
109
+ autoComplete={autoComplete}
110
+ inputProps={callerInputProps(inputProps, testId)}
48
111
  data-testid={testId}
49
112
  onChange={(event) => setFieldValue(name, event.target.value)}
50
113
  />
@@ -3,7 +3,7 @@ export type { CategoryFieldProps } from './CategoryField';
3
3
  export { CepField } from './CepField';
4
4
  export type { CepFieldProps, CepFills } from './CepField';
5
5
  export { TextField } from './TextField';
6
- export type { TextFieldProps } from './TextField';
6
+ export type { TextFieldInputProps, TextFieldProps } from './TextField';
7
7
  export { SelectField } from './SelectField';
8
8
  export type { SelectFieldProps } from './SelectField';
9
9
  export { ToggleField } from './ToggleField';
@@ -9,4 +9,4 @@ export type { SubmitButtonProps } from './SubmitButton';
9
9
 
10
10
  export * as Fields from './Fields';
11
11
  export { CategoryField, TextField, SelectField } from './Fields';
12
- export type { CategoryFieldProps, TextFieldProps, SelectFieldProps } from './Fields';
12
+ export type { CategoryFieldProps, TextFieldInputProps, TextFieldProps, SelectFieldProps } from './Fields';
@@ -219,6 +219,7 @@ export const EN_US_DATA_VIEWS_COPY: DataViewsCopy = {
219
219
  // rather than being carried over. A translated label above an unchanged
220
220
  // mask is the shape that gets a date entered backwards.
221
221
  dayMask: "mm/dd/yyyy",
222
+ dayInvalid: "That date does not exist.",
222
223
  clearAllFilters: "Clear all filters",
223
224
  // The KEYS are the package's own preset ids and stay as they are — the
224
225
  // surface matches on them. Only the labels beside them are words.
@@ -213,6 +213,7 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
213
213
  rangeInclusiveNote: "Ambas as datas entram no resultado.",
214
214
  rangeStart: "De",
215
215
  dayMask: "dd/mm/aaaa",
216
+ dayInvalid: "Essa data não existe.",
216
217
  clearAllFilters: "Limpar todos os filtros",
217
218
  rangePresets: {
218
219
  hoje: "Hoje",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/form/InputOTP/InputOTP.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport TextField from '@mui/material/TextField/index.js';\nimport { styled } from '@mui/material/styles/index.js';\nimport React, { forwardRef } from 'react';\n\nimport { withDefaults } from '../../../utils/withDefaults';\n\nimport { useOtpDigits } from './InputOTP.hooks';\nimport { otpSlotSx } from './InputOTP.styles';\nimport type { OtpSlotFlags } from './InputOTP.styles';\nimport type { InputOTPProps } from './InputOTP.types';\n\nconst StyledOTPInput = styled(TextField, {\n shouldForwardProp: (prop) =>\n !['customColor', 'customSize', 'glass', 'gradient'].includes(prop as string),\n})<OtpSlotFlags>(({ theme, ...flags }) => ({ ...otpSlotSx(theme, flags) }));\n\nconst DEFAULTS = {\n variant: 'numeric',\n color: 'primary',\n size: 'md',\n length: 6,\n value: '',\n glass: false,\n gradient: false,\n autoFocus: false,\n error: false,\n disabled: false,\n} satisfies Partial<InputOTPProps>;\n\ntype ResolvedProps = InputOTPProps & Required<Pick<InputOTPProps, keyof typeof DEFAULTS>>;\n\nexport const InputOTP = forwardRef<HTMLDivElement, InputOTPProps>((props, ref) => {\n const {\n variant, color, size, length, value, onChange, onComplete,\n glass, gradient, autoFocus, error, disabled, dataTestId,\n ...rest\n } = withDefaults(props, DEFAULTS) as ResolvedProps;\n\n const { digits, inputRefs, handleInputChange, handleKeyDown, handlePaste } = useOtpDigits({\n variant,\n length,\n value,\n onChange,\n onComplete,\n });\n\n return (\n <Box\n ref={ref}\n data-testid={dataTestId}\n sx={{ display: 'flex', gap: 1, justifyContent: 'center', alignItems: 'center' }}\n {...rest}\n >\n {digits.map((digit, index) => (\n <StyledOTPInput\n key={index}\n ref={(el: HTMLDivElement | null) => {\n inputRefs.current[index] = el?.querySelector('input') || null;\n }}\n customColor={color}\n customSize={size}\n glass={glass}\n gradient={gradient}\n // `masked` keeps the digit in state and shows a bullet in its place.\n value={variant === 'masked' ? (digit ? '•' : '') : digit}\n onChange={(e) => handleInputChange(index, e.target.value)}\n onKeyDown={(e) => handleKeyDown(index, e)}\n // Only the first slot takes a paste; it fills the rest itself.\n onPaste={index === 0 ? handlePaste : undefined}\n autoFocus={autoFocus && index === 0}\n error={error}\n disabled={disabled}\n inputProps={{\n maxLength: 1,\n style: { textAlign: 'center' },\n 'data-testid': dataTestId ? `${dataTestId}-slot-${index}` : undefined,\n }}\n />\n ))}\n </Box>\n );\n});\n\nInputOTP.displayName = 'InputOTP';\n"],"mappings":";;;;;;;;;;;AAAA,OAAO,SAAS;AAChB,OAAO,eAAe;AACtB,SAAS,cAAc;AACvB,SAAgB,kBAAkB;AAoD1B;AA3CR,IAAM,iBAAiB,OAAO,WAAW;AAAA,EACvC,mBAAmB,CAAC,SAClB,CAAC,CAAC,eAAe,cAAc,SAAS,UAAU,EAAE,SAAS,IAAc;AAC/E,CAAC,EAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,OAAO,EAAE,GAAG,UAAU,OAAO,KAAK,EAAE,EAAE;AAE1E,IAAM,WAAW;AAAA,EACf,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AACZ;AAIO,IAAM,WAAW,WAA0C,CAAC,OAAO,QAAQ;AAChF,QAAM;AAAA,IACJ;AAAA,IAAS;AAAA,IAAO;AAAA,IAAM;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAU;AAAA,IAC/C;AAAA,IAAO;AAAA,IAAU;AAAA,IAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAC7C,GAAG;AAAA,EACL,IAAI,aAAa,OAAO,QAAQ;AAEhC,QAAM,EAAE,QAAQ,WAAW,mBAAmB,eAAe,YAAY,IAAI,aAAa;AAAA,IACxF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,eAAa;AAAA,MACb,IAAI,EAAE,SAAS,QAAQ,KAAK,GAAG,gBAAgB,UAAU,YAAY,SAAS;AAAA,MAC7E,GAAG;AAAA,MAEH,iBAAO,IAAI,CAAC,OAAO,UAClB;AAAA,QAAC;AAAA;AAAA,UAEC,KAAK,CAAC,OAA8B;AAClC,sBAAU,QAAQ,KAAK,IAAI,IAAI,cAAc,OAAO,KAAK;AAAA,UAC3D;AAAA,UACA,aAAa;AAAA,UACb,YAAY;AAAA,UACZ;AAAA,UACA;AAAA,UAEA,OAAO,YAAY,WAAY,QAAQ,WAAM,KAAM;AAAA,UACnD,UAAU,CAAC,MAAM,kBAAkB,OAAO,EAAE,OAAO,KAAK;AAAA,UACxD,WAAW,CAAC,MAAM,cAAc,OAAO,CAAC;AAAA,UAExC,SAAS,UAAU,IAAI,cAAc;AAAA,UACrC,WAAW,aAAa,UAAU;AAAA,UAClC;AAAA,UACA;AAAA,UACA,YAAY;AAAA,YACV,WAAW;AAAA,YACX,OAAO,EAAE,WAAW,SAAS;AAAA,YAC7B,eAAe,aAAa,GAAG,UAAU,SAAS,KAAK,KAAK;AAAA,UAC9D;AAAA;AAAA,QArBK;AAAA,MAsBP,CACD;AAAA;AAAA,EACH;AAEJ,CAAC;AAED,SAAS,cAAc;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/data-views-filter-panel.tsx"],"sourcesContent":["\"use client\";\n\nimport Box from \"@mui/material/Box/index.js\";\nimport Link from \"@mui/material/Link/index.js\";\n\nimport { StackedModal, StackedModalContent } from \"../../feedback/StackedModal\";\nimport { TableFilter } from \"../../layout/TableFilter\";\n\nimport { PillControl } from \"./data-views-category-pill\";\nimport { PanelRangeField } from \"./data-views-range-pill\";\nimport { useDataViewsCopy } from \"./data-views-copy-context\";\nimport type {\n FilterFieldConfig,\n RangeFieldConfig,\n RangeValue,\n} from \"./data-views-types\";\nimport { sxRem } from \"../../../tokens/relative\";\n\n/* ── Filter panel ────────────────────────────────────────────────────────── */\n\nexport interface GridFilterPanelProps<T extends Record<string, unknown>> {\n testIdPrefix: string;\n search: string;\n fields: FilterFieldConfig<T>[];\n rangeFields: RangeFieldConfig<T>[];\n pills: Record<string, string[]>;\n ranges: Record<string, RangeValue>;\n onSearchChange: (value: string) => void;\n onTogglePill: (fieldId: string, value: string, checked: boolean) => void;\n onChangeRange: (fieldId: string, range: RangeValue) => void;\n onClearField: (fieldId: string) => void;\n onClearAll: () => void;\n}\n\n/** One pill field: a compact multi-select for long lists, else a checkbox list. */\nfunction PillField<T extends Record<string, unknown>>({\n field,\n selected,\n testIdPrefix,\n onTogglePill,\n onClearField,\n}: {\n field: FilterFieldConfig<T>;\n selected: string[];\n testIdPrefix: string;\n onTogglePill: (fieldId: string, value: string, checked: boolean) => void;\n onClearField: (fieldId: string) => void;\n}): React.JSX.Element {\n const testId = `${testIdPrefix}-filter-${field.id}`;\n // `searchEnabled` forces the searchable multi-select; a plain `multiselect`\n // keeps the auto \"search only when the list is long\" rule. `category` is the\n // hierarchical tree — see PillControl.\n if (field.searchEnabled || field.control === \"multiselect\" || field.control === \"category\") {\n return (\n <PillControl\n fieldId={field.id}\n pill={field}\n selected={selected}\n onTogglePill={onTogglePill}\n onClearField={onClearField}\n testIdPrefix={testIdPrefix}\n layout=\"stacked\"\n />\n );\n }\n return (\n <TableFilter.CheckboxField\n label={field.label}\n options={field.options}\n selected={new Set(selected)}\n onToggle={(value, checked) => onTogglePill(field.id, value, checked)}\n testId={testId}\n />\n );\n}\n\n/** The filter controls (keyword + pill facets + numeric ranges), surface-agnostic. */\nfunction FilterControls<T extends Record<string, unknown>>({\n testIdPrefix,\n search,\n fields,\n rangeFields,\n pills,\n ranges,\n onSearchChange,\n onTogglePill,\n onChangeRange,\n onClearField,\n}: Omit<GridFilterPanelProps<T>, \"onClearAll\">): React.JSX.Element {\n const copy = useDataViewsCopy();\n return (\n <>\n <TableFilter.Keyword\n value={search}\n onChange={onSearchChange}\n label={copy.search.allColumnsLabel}\n placeholder={copy.search.keywordPlaceholder}\n testId={`${testIdPrefix}-search-all`}\n />\n {(fields.length > 0 || rangeFields.length > 0) && (\n <TableFilter.Section title={copy.filters.panelTitle}>\n {fields.map((field) => (\n <PillField\n key={field.id}\n field={field}\n selected={pills[field.id] ?? []}\n testIdPrefix={testIdPrefix}\n onTogglePill={onTogglePill}\n onClearField={onClearField}\n />\n ))}\n {rangeFields.map((field) => (\n <PanelRangeField\n key={field.id}\n field={field}\n value={ranges[field.id] ?? {}}\n onChange={(range) => onChangeRange(field.id, range)}\n testId={`${testIdPrefix}-range-${field.id}`}\n />\n ))}\n </TableFilter.Section>\n )}\n </>\n );\n}\n\n/** The slide-in filter panel: keyword + checkbox facets + numeric ranges. */\nexport function GridFilterPanel<T extends Record<string, unknown>>(\n props: GridFilterPanelProps<T>,\n): React.JSX.Element {\n const copy = useDataViewsCopy();\n return (\n <TableFilter.Panel\n onClearAll={props.onClearAll}\n clearTestId={`${props.testIdPrefix}-clear-filters`}\n ariaLabel={copy.filters.panelTitle}\n >\n <FilterControls {...props} />\n </TableFilter.Panel>\n );\n}\n\n/** Props for the modal filter surface used below the inline breakpoint. */\ninterface FilterDialogProps<T extends Record<string, unknown>> extends GridFilterPanelProps<T> {\n open: boolean;\n onClose: () => void;\n}\n\n/**\n * The same filter controls in a centered modal — used on smaller screens where the\n * slide-in panel would overlap the table. The \"Filtros\" toolbar button opens it.\n */\nexport function FilterDialog<T extends Record<string, unknown>>({\n open,\n onClose,\n ...props\n}: FilterDialogProps<T>): React.JSX.Element {\n const copy = useDataViewsCopy();\n return (\n <StackedModal\n open={open}\n onClose={onClose}\n navigationTitle={copy.filters.panelTitle}\n backLabel={copy.nav.goBack}\n modalId={`${props.testIdPrefix}-filters`}\n maxWidth=\"xs\"\n dataTestId={`${props.testIdPrefix}-filter-dialog`}\n >\n <StackedModalContent>\n <Box sx={{ display: \"flex\", flexDirection: \"column\", gap: 2.5 }}>\n <Box sx={{ display: \"flex\", justifyContent: \"flex-end\" }}>\n <Link\n component=\"button\"\n type=\"button\"\n underline=\"hover\"\n onClick={props.onClearAll}\n data-testid={`${props.testIdPrefix}-clear-filters`}\n sx={{ fontSize: sxRem(12), color: \"text.secondary\" }}\n >\n {copy.filters.clearAllFilters}\n </Link>\n </Box>\n <FilterControls {...props} />\n </Box>\n </StackedModalContent>\n </StackedModal>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/data-views-day-input.tsx"],"sourcesContent":["'use client';\n\n/**\n * A day bound as a MASKED `dd/mm/aaaa` text field, replacing `<input type=\"date\">`.\n *\n * The native control is three separate segments wearing one box. Typing a date\n * means filling a segment, being jumped to the next, and starting over — and\n * which segment comes first is the BROWSER's locale, not ours, so the same\n * field reads `dd/mm/aaaa` for one merchant and `mm/dd/yyyy` for the next with\n * nothing in our code saying which. There is no way to type a date as one\n * continuous run of digits, which is how people actually type dates.\n *\n * A mask gives that back: eight digits straight through, separators inserted as\n * you go, backspace walking back through them.\n *\n * Measured, driving both controls in Chromium with the same eight keystrokes\n * (`01072026`, one `keydown` at a time):\n *\n * native → field ends EMPTY, no filter applied, 0 requests\n * masked → field reads `01/07/2026`, filter applied, 1 request\n *\n * The native result is the whole complaint: you type a date and nothing lands.\n *\n * The second half of the fix is WHEN the bound is written. A partially-typed\n * year is itself a valid date — `2026` passes through `0002`, `0020`, `0202` on\n * the way — so committing on every keystroke would send the grid off to fetch\n * years nobody meant. A bound is written only once the text is a WHOLE, real\n * date, which is what keeps the run above at one request rather than four.\n */\nimport TextField from '@mui/material/TextField/index.js';\nimport React, { useEffect, useState } from 'react';\nimport { fieldTextFieldStyles } from '../../../tokens/field-height';\nimport { sxRem } from '../../../tokens/relative';\n\n\n/**\n * THE MASK IS THE ORDER, and both halves of this file read it.\n *\n * The placeholder a host supplies (`copy.filters.dayMask` — `dd/mm/aaaa` for\n * pt-BR, `mm/dd/yyyy` for en-US) is not decoration: it tells the reader which\n * segment comes first. Everything below therefore derives the segment order\n * FROM that same string, so the shape on screen and the shape the parser\n * expects cannot disagree.\n *\n * They did disagree, and the failure was silent in the worst direction. The\n * mask/parse pair was hardcoded day-first (the `Br` in the old names was\n * accurate) while the placeholder followed the copy pack — so under `en-US` the\n * field ASKED for `mm/dd/yyyy`, and a reader who typed exactly that got a day\n * of 12 and a month of 31, which is not a date, which commits nothing. No\n * error, no filter, a full list that reads like an answer. The en-US pack's own\n * comment already said the mask \"is the shape that gets a date entered\n * backwards\"; nothing enforced it.\n */\n\n/** What one segment of a mask is, and how many digits it takes. */\ntype DaySegment = 'year' | 'month' | 'day';\nconst SEGMENT_WIDTH: Readonly<Record<DaySegment, number>> = { year: 4, month: 2, day: 2 };\n\n/**\n * The segments a mask asks for, in its own order.\n *\n * Keyed on each token's FIRST letter so one rule covers every spelling a host\n * might use — `aaaa`/`yyyy` for the year, `mm` for the month, `dd`/`tt` for the\n * day. An unreadable mask falls back to day-first rather than throwing: this\n * runs on every keystroke, and a field that renders is recoverable where one\n * that crashes the grid is not.\n */\nfunction segmentsOf(mask: string): readonly DaySegment[] {\n const tokens = mask.split(/[^A-Za-z]+/).filter(Boolean);\n if (tokens.length !== 3) return ['day', 'month', 'year'];\n const segments = tokens.map((token): DaySegment => {\n const head = token.slice(0, 1).toLowerCase();\n if (head === 'y' || head === 'a') return 'year';\n return head === 'm' ? 'month' : 'day';\n });\n // A mask naming the same segment twice describes no date. Fall back rather\n // than build a parser that can never succeed.\n return new Set(segments).size === 3 ? segments : ['day', 'month', 'year'];\n}\n\n/** The separator the mask uses, defaulting to `/`. */\nconst separatorOf = (mask: string): string => mask.replace(/[A-Za-z]/g, '').slice(0, 1) || '/';\n\n/** `AAAA-MM-DD` (the wire) → the mask's own order (what the reader sees). */\nexport function isoToMasked(iso: string, mask: string): string {\n const parts = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(iso);\n if (!parts) return '';\n const value: Record<DaySegment, string> = {\n year: parts[1] as string,\n month: parts[2] as string,\n day: parts[3] as string,\n };\n return segmentsOf(mask)\n .map((segment) => value[segment])\n .join(separatorOf(mask));\n}\n\n/**\n * The mask's order → `AAAA-MM-DD`, but ONLY for a day that exists.\n *\n * `31/02/2026` parses fine as three numbers and is not a date, and `Date` would\n * silently roll it into March rather than reject it — so the round trip through\n * UTC is the check: if the month or day comes back changed, the input was not a\n * day.\n */\nexport function maskedToIso(text: string, mask: string): string {\n const segments = segmentsOf(mask);\n const digits = text.replace(/\\D/g, '');\n if (digits.length !== 8) return '';\n const value = {} as Record<DaySegment, string>;\n let cursor = 0;\n for (const segment of segments) {\n value[segment] = digits.slice(cursor, cursor + SEGMENT_WIDTH[segment]);\n cursor += SEGMENT_WIDTH[segment];\n }\n const year = Number(value.year);\n const month = Number(value.month);\n const day = Number(value.day);\n if (year < 1000) return '';\n const probe = new Date(Date.UTC(year, month - 1, day));\n if (probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return '';\n return `${value.year}-${value.month}-${value.day}`;\n}\n\n/**\n * Digits only, capped at the mask's width, with the separators put back as you\n * type.\n *\n * Deriving the whole string from the digits — rather than editing the text in\n * place — is what makes backspace behave. Four digits of `dd/mm/aaaa` render as\n * `06/08`, not `06/08/`, so deleting the first digit of the year removes the\n * trailing separator with it instead of leaving a dead keystroke that changes\n * nothing.\n */\nexport function maskDate(raw: string, mask: string): string {\n const segments = segmentsOf(mask);\n const separator = separatorOf(mask);\n const digits = raw.replace(/\\D/g, '').slice(0, 8);\n const out: string[] = [];\n let cursor = 0;\n for (const segment of segments) {\n if (cursor >= digits.length) break;\n out.push(digits.slice(cursor, cursor + SEGMENT_WIDTH[segment]));\n cursor += SEGMENT_WIDTH[segment];\n }\n return out.join(separator);\n}\n\n/**\n * What has to land on the `<input>` itself rather than on the `TextField`.\n *\n * Anything `TextField` does not recognise is spread onto its root `FormControl`\n * div. `inputMode` went up there once: the attribute sat on a non-editable\n * wrapper, meant nothing, and the phone went on offering the letter keyboard\n * for a field that only takes digits (the `dd/mm/aaaa` hint under a QWERTY\n * layout). Only the `<input>` this reaches decides which keypad opens — and the\n * same is true of `aria-describedby`, which on the wrapper points at a\n * description no screen reader will read out for the field.\n *\n * `numeric` and not `tel`: both raise a keypad, but the phone one is a dialler\n * with `+ * #` on it — keys that cannot occur in a date. A paste carrying\n * separators still lands, because the MASK is what filters the text; the keypad\n * only narrows what a thumb can reach for.\n *\n * Autofill off for the same reason: the browser's saved-value strip covers the\n * keypad to suggest names and addresses, none of which are a date, and none of\n * which the mask would keep.\n */\nfunction dayInputProps(testId: string, describedBy: string | undefined) {\n return {\n 'data-testid': testId,\n maxLength: 10,\n inputMode: 'numeric' as const,\n autoComplete: 'off',\n ...(describedBy ? { 'aria-describedby': describedBy } : {}),\n };\n}\n\n/**\n * One end of a day window. The BOUND stays `AAAA-MM-DD` — the form the backend's\n * inclusive day comparison already speaks, and the form the chip label reads —\n * so only the typing surface changes.\n */\nexport function DayBoundInput({\n label,\n value,\n onChange,\n testId,\n error = false,\n describedBy,\n mask,\n}: {\n label: string;\n /** The applied bound, `AAAA-MM-DD`, or undefined when this end is open. */\n value: string | undefined;\n onChange: (bound: string | undefined) => void;\n testId: string;\n /**\n * Paint the field as rejected. It only ever writes a real day by itself, so\n * this is for a caller judging the PAIR: a window whose end precedes its\n * start is two valid days and one impossible period, and the field it was\n * typed into is the only place to say which half is being complained about.\n */\n error?: boolean;\n /** Id of the element carrying that complaint, so it is read WITH the field. */\n describedBy?: string;\n /**\n * The day mask, which IS the placeholder — so the expected ORDER is on\n * screen before the first keystroke rather than discovered by getting it\n * wrong. `dd/mm/aaaa` is one locale's order and one locale's letters.\n *\n * A PROP rather than a context read: this field has two callers, and only\n * one of them is inside DataViews. `DateRangePicker` mounts it standalone\n * with its own message table, so a context read here throws for every host\n * of that picker — which is exactly what it did.\n */\n mask: string;\n}): React.JSX.Element {\n const applied = value ?? '';\n const [text, setText] = useState(() => isoToMasked(applied, mask));\n\n // The bound is the APPLIED filter; `text` is what is being typed. Re-sync only\n // when the applied value moved somewhere the text does not already mean — a\n // \"Limpar\", a saved view, the other pill resetting the pair. Without that\n // guard the field's own write bounces straight back mid-edit and resets the\n // caret, which is the native control's worst habit reproduced by hand.\n useEffect(() => {\n setText((current) =>\n maskedToIso(current, mask) === applied ? current : isoToMasked(applied, mask),\n );\n }, [applied, mask]);\n\n return (\n <TextField\n size=\"small\"\n type=\"text\"\n label={label}\n error={error}\n placeholder={mask}\n value={text}\n onChange={(event) => {\n const next = maskDate(event.target.value, mask);\n setText(next);\n // Emptying clears the bound. A COMPLETE day applies it. A partial one\n // does NEITHER — it leaves whatever is applied alone, so typing is never\n // fought and a half-typed year never reaches the backend.\n if (next === '') return onChange(undefined);\n const iso = maskedToIso(next, mask);\n if (iso) onChange(iso);\n }}\n // Leaving a half-typed (or impossible) date on screen would show a filter\n // the list is not actually using. On blur the field snaps back to what IS\n // applied; a complete date already equals it, so this only ever tidies.\n onBlur={() => setText(isoToMasked(applied, mask))}\n // The placeholder occupies the space an un-shrunk label would render into.\n InputLabelProps={{ shrink: true }}\n inputProps={dayInputProps(testId, describedBy)}\n sx={[{ width: sxRem(165) }, fieldTextFieldStyles]}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/data-views-range-pill.tsx"],"sourcesContent":["'use client';\n\n/**\n * A range filter as a PILL in the inline filter bar — the same affordance the\n * multi-select facets use, so \"Data\" and \"Valor\" sit in the filter row beside\n * \"Pagamento\"/\"Situação\"/\"Método\" instead of floating above the grid.\n *\n * Before this the inline bar rendered pills only, and `rangeFields` had a\n * surface on the slide-in panel alone — which `inlineFilters` REPLACES on a wide\n * screen. So a range filter was invisible on exactly the screens that use it,\n * and a page wanting one had to hang its own inputs outside the grid, outside\n * `DataViewState`, and outside the grid's \"Limpar filtros\" (FUT-668).\n */\nimport CloseIcon from '@mui/icons-material/Close';\nimport ChevronDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport Box from '@mui/material/Box/index.js';\nimport Button from '@mui/material/Button/index.js';\nimport Popover from '@mui/material/Popover/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport React, { useState } from 'react';\n\nimport { useDataViewsCopy } from \"./data-views-copy-context\";\nimport { TableFilter } from '../../layout/TableFilter';\nimport { DayBoundInput } from './data-views-day-input';\nimport { NumberBoundInput } from './data-views-number-input';\nimport { RangePresets } from './data-views-preset-chips';\nimport { presetsFor } from './data-views-range-presets';\nimport {\n isRangeInverted,\n isRangeSet,\n numericRange,\n rangeChipLabel,\n} from './data-views-range-values';\nimport type { RangeFieldConfig, RangeValue } from './data-views-types';\nimport { fieldRadiusPx } from '../../../tokens/field-radius';\nimport { fieldHeight } from '../../../tokens/field-height';\nimport { fieldEdge } from '../../../tokens/field-edge';\nimport { sxRem } from '../../../tokens/relative';\n\n\n\n/**\n * One end of the range. A day keeps its `AAAA-MM-DD` string verbatim (the form\n * both the field and the backend speak) and is typed through the masked\n * `dd/mm/aaaa` field rather than a native date input — see\n * {@link DayBoundInput} for why. A number parses, and an unparseable/blank\n * entry CLEARS the bound rather than writing `NaN`.\n */\nfunction BoundField<T extends Record<string, unknown>>({\n field,\n label,\n value,\n onChange,\n testId,\n}: {\n field: RangeFieldConfig<T>;\n label: string;\n value: number | string | undefined;\n onChange: (bound: number | string | undefined) => void;\n testId: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n if (field.kind === 'day') {\n return (\n <DayBoundInput\n mask={copy.filters.dayMask}\n label={label}\n value={value == null ? undefined : String(value)}\n onChange={onChange}\n testId={testId}\n />\n );\n }\n return (\n <NumberBoundInput\n label={label}\n value={value}\n unit={field.unit}\n onChange={onChange}\n testId={testId}\n />\n );\n}\n\n\n/**\n * The `De`/`Até` pair for a range — shared by the pill's popover and the\n * slide-in panel's day fields, so the two surfaces cannot drift on what a bound\n * accepts or how it clears.\n */\nexport function RangeBounds<T extends Record<string, unknown>>({\n field,\n value,\n onChange,\n testId,\n}: {\n field: RangeFieldConfig<T>;\n value: RangeValue;\n onChange: (range: RangeValue) => void;\n testId: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n const inverted = isRangeInverted(value);\n return (\n <Box data-testid={`${testId}-panel`}>\n {/* Above the inputs, not below: the presets are the answer for most\n merchants, and the two date pickers are the escape hatch. */}\n <Box sx={{ '&:not(:empty)': { mb: 1.5 } }}>\n <RangePresets presets={presetsFor(field, copy)} value={value} onChange={onChange} testId={testId} />\n </Box>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>\n <BoundField\n field={field}\n label={copy.filters.rangeStart}\n value={value.min}\n onChange={(min) => onChange({ ...value, min })}\n testId={`${testId}-min`}\n />\n <BoundField\n field={field}\n label={copy.filters.rangeEnd}\n value={value.max}\n onChange={(max) => onChange({ ...value, max })}\n testId={`${testId}-max`}\n />\n </Box>\n {inverted && (\n <Typography\n component=\"p\"\n data-testid={`${testId}-inverted`}\n sx={{ mt: 0.75, fontSize: sxRem(12), color: 'error.main' }}\n >\n {copy.filters.rangeInvalid}\n </Typography>\n )}\n {/* BOTH days are in the result. Saying so is FUT-668's other half: the\n bounds compare as ISO strings precisely so `até` keeps its whole day,\n and nothing on screen would otherwise tell the merchant that. */}\n {field.kind === 'day' && !inverted && (\n <Typography\n component=\"p\"\n data-testid={`${testId}-inclusive`}\n sx={{ mt: 0.75, fontSize: sxRem(12), color: 'text.disabled' }}\n >\n {copy.filters.rangeInclusiveNote}\n </Typography>\n )}\n </Box>\n );\n}\n\n/**\n * The pill + its popover. Bounds commit on change (the list is server-driven, so\n * each edit is a query) and the popover stays open, which is what lets a\n * merchant set \"de\" and then \"até\" without re-opening it.\n */\n/** An applied range clears from the pill itself — re-opening to empty two fields is three gestures for one intent. */\nfunction ClearAffordance({\n label,\n onClear,\n testId,\n}: {\n label: string;\n onClear: () => void;\n testId: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n const clear = (event: React.SyntheticEvent): void => {\n event.stopPropagation();\n onClear();\n };\n return (\n <Box\n component=\"span\"\n role=\"button\"\n tabIndex={0}\n aria-label={copy.filters.clearRange(label)}\n data-testid={testId}\n onClick={clear}\n onKeyDown={(event: React.KeyboardEvent) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n clear(event);\n }}\n sx={{ display: 'inline-flex', borderRadius: 999, p: 0.125, '&:hover': { bgcolor: 'action.selected' } }}\n >\n <CloseIcon sx={{ fontSize: sxRem(14) }} />\n </Box>\n );\n}\n\n/** The pill button: its label carries the applied window, and its tone the state. */\nfunction RangeTrigger<T extends Record<string, unknown>>({\n field,\n value,\n onChange,\n onOpen,\n open,\n testId,\n}: {\n field: RangeFieldConfig<T>;\n value: RangeValue;\n onChange: (range: RangeValue) => void;\n onOpen: (anchor: HTMLElement) => void;\n open: boolean;\n testId: string;\n}): React.JSX.Element {\n const active = isRangeSet(value);\n const inverted = isRangeInverted(value);\n // An INVERTED window gets its own tone rather than the ordinary \"applied\"\n // one: it is applied, and it matches nothing, and those are different states.\n // At rest the pill draws the one field border (`fieldEdge`).\n const tone = inverted ? 'error.main' : active ? 'primary.main' : null;\n return (\n <Button\n variant=\"outlined\"\n color=\"inherit\"\n size=\"small\"\n data-testid={testId}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onClick={(event) => onOpen(event.currentTarget)}\n // ✕ FIRST, chevron LAST — the same shape as every other pill. It used to\n // replace the chevron at the end, so a range pill and a multi-select pill\n // put the clear in different places, and an applied range lost the\n // affordance that says it opens.\n startIcon={\n active ? (\n <ClearAffordance label={field.label} onClear={() => onChange({})} testId={`${testId}-clear-inline`} />\n ) : undefined\n }\n endIcon={<ChevronDownIcon sx={{ fontSize: sxRem(16) }} />}\n sx={{\n borderRadius: fieldRadiusPx,\n height: fieldHeight,\n px: 1.5,\n maxWidth: sxRem(280),\n color: inverted ? 'error.main' : 'text.primary',\n fontWeight: 600,\n fontSize: sxRem(13),\n textTransform: 'none',\n whiteSpace: 'nowrap',\n borderColor: (theme) => tone ?? fieldEdge(theme),\n bgcolor: active ? 'action.selected' : 'background.paper',\n '&:hover': {\n borderColor: active ? tone : 'text.primary',\n bgcolor: active ? 'action.selected' : 'action.hover',\n },\n }}\n >\n {/* The applied bounds, in the label — a pill reading only \"Valor\" hides\n the very thing that is narrowing the list. */}\n <Box component=\"span\" sx={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {active ? rangeChipLabel(field, value) : field.label}\n </Box>\n </Button>\n );\n}\n\nexport function RangePill<T extends Record<string, unknown>>({\n field,\n value,\n onChange,\n onOpenChange,\n testIdPrefix,\n}: {\n field: RangeFieldConfig<T>;\n value: RangeValue;\n onChange: (range: RangeValue) => void;\n /** So a measured toolbar can hold still while this is open. */\n onOpenChange?: (open: boolean) => void;\n testIdPrefix: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const active = isRangeSet(value);\n const testId = `${testIdPrefix}-range-${field.id}`;\n return (\n <>\n <RangeTrigger\n field={field}\n value={value}\n onChange={onChange}\n onOpen={(element) => {\n setAnchor(element);\n onOpenChange?.(true);\n }}\n open={Boolean(anchor)}\n testId={testId}\n />\n <Popover\n open={Boolean(anchor)}\n anchorEl={anchor}\n onClose={() => {\n setAnchor(null);\n onOpenChange?.(false);\n }}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\n slotProps={{ paper: { sx: { p: 2, mt: 0.5 } } }}\n >\n <RangeBounds field={field} value={value} onChange={onChange} testId={testId} />\n {/* An inverted window is not an error the merchant must fix before the\n list responds — it simply matches nothing, and saying so beats an\n empty grid with no explanation (FUT-668). */}\n {active && (\n <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 2, mt: 1.5 }}>\n <Typography component=\"span\" sx={{ fontSize: sxRem(12), color: 'text.secondary' }}>\n {rangeChipLabel(field, value)}\n </Typography>\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={() => onChange({})}\n data-testid={`${testId}-clear`}\n sx={{ textTransform: 'none' }}\n >\n {copy.filters.clear}\n </Button>\n </Box>\n )}\n </Popover>\n </>\n );\n}\n\n\n/**\n * One range on the SLIDE-IN panel (and the narrow-screen modal): the numeric\n * control the panel already had, or — for a day range, which that control cannot\n * express — the same `De`/`Até` pair the pill's popover uses.\n */\nexport function PanelRangeField<T extends Record<string, unknown>>({\n field,\n value,\n onChange,\n testId,\n}: {\n field: RangeFieldConfig<T>;\n value: RangeValue;\n onChange: (range: RangeValue) => void;\n testId: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n if (field.kind === \"day\") {\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", gap: 1 }}>\n <Typography component=\"span\" sx={{ fontSize: sxRem(12), fontWeight: 500, color: \"text.secondary\" }}>\n {field.label}\n </Typography>\n <RangeBounds field={field} value={value} onChange={onChange} testId={testId} />\n </Box>\n );\n }\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", gap: 1 }}>\n <TableFilter.RangeField\n label={field.label}\n unit={field.unit}\n value={numericRange(value)}\n onChange={(range) => onChange(range)}\n testId={testId}\n />\n {/* Below the numeric control here, unlike the pill's popover: this one\n renders its own label, and chips above it would separate the label\n from the field it names. */}\n <RangePresets presets={presetsFor(field, copy)} value={value} onChange={onChange} testId={testId} />\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,OAAO,eAAe;AACtB,OAAO,qBAAqB;AAC5B,OAAO,SAAS;AAChB,OAAO,YAAY;AACnB,OAAO,aAAa;AACpB,OAAO,gBAAgB;AACvB,SAAgB,gBAAgB;AA6C1B,SAsNF,UAtNE,KA8CA,YA9CA;AAhBN,SAAS,WAA8C;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,MAAI,MAAM,SAAS,OAAO;AACxB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,KAAK,QAAQ;AAAA,QACnB;AAAA,QACA,OAAO,SAAS,OAAO,SAAY,OAAO,KAAK;AAAA,QAC/C;AAAA,QACA;AAAA;AAAA,IACF;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,MAAM,MAAM;AAAA,MACZ;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AAQO,SAAS,YAA+C;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,QAAM,WAAW,gBAAgB,KAAK;AACtC,SACE,qBAAC,OAAI,eAAa,GAAG,MAAM,UAGzB;AAAA,wBAAC,OAAI,IAAI,EAAE,iBAAiB,EAAE,IAAI,IAAI,EAAE,GACtC,8BAAC,gBAAa,SAAS,WAAW,OAAO,IAAI,GAAG,OAAc,UAAoB,QAAgB,GACpG;AAAA,IACA,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,GACzD;AAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO,KAAK,QAAQ;AAAA,UACpB,OAAO,MAAM;AAAA,UACb,UAAU,CAAC,QAAQ,SAAS,EAAE,GAAG,OAAO,IAAI,CAAC;AAAA,UAC7C,QAAQ,GAAG,MAAM;AAAA;AAAA,MACnB;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO,KAAK,QAAQ;AAAA,UACpB,OAAO,MAAM;AAAA,UACb,UAAU,CAAC,QAAQ,SAAS,EAAE,GAAG,OAAO,IAAI,CAAC;AAAA,UAC7C,QAAQ,GAAG,MAAM;AAAA;AAAA,MACnB;AAAA,OACF;AAAA,IACC,YACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAa,GAAG,MAAM;AAAA,QACtB,IAAI,EAAE,IAAI,MAAM,UAAU,MAAM,EAAE,GAAG,OAAO,aAAa;AAAA,QAExD,eAAK,QAAQ;AAAA;AAAA,IAChB;AAAA,IAKD,MAAM,SAAS,SAAS,CAAC,YACxB;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAa,GAAG,MAAM;AAAA,QACtB,IAAI,EAAE,IAAI,MAAM,UAAU,MAAM,EAAE,GAAG,OAAO,gBAAgB;AAAA,QAE3D,eAAK,QAAQ;AAAA;AAAA,IAChB;AAAA,KAEJ;AAEJ;AAQA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF,GAIsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,QAAM,QAAQ,CAAC,UAAsC;AACnD,UAAM,gBAAgB;AACtB,YAAQ;AAAA,EACV;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,MAAK;AAAA,MACL,UAAU;AAAA,MACV,cAAY,KAAK,QAAQ,WAAW,KAAK;AAAA,MACzC,eAAa;AAAA,MACb,SAAS;AAAA,MACT,WAAW,CAAC,UAA+B;AACzC,YAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,IAAK;AAChD,cAAM,eAAe;AACrB,cAAM,KAAK;AAAA,MACb;AAAA,MACA,IAAI,EAAE,SAAS,eAAe,cAAc,KAAK,GAAG,OAAO,WAAW,EAAE,SAAS,kBAAkB,EAAE;AAAA,MAErG,8BAAC,aAAU,IAAI,EAAE,UAAU,MAAM,EAAE,EAAE,GAAG;AAAA;AAAA,EAC1C;AAEJ;AAGA,SAAS,aAAgD;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOsB;AACpB,QAAM,SAAS,WAAW,KAAK;AAC/B,QAAM,WAAW,gBAAgB,KAAK;AAItC,QAAM,OAAO,WAAW,eAAe,SAAS,iBAAiB;AACjE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,MAAK;AAAA,MACL,eAAa;AAAA,MACb,iBAAc;AAAA,MACd,iBAAe;AAAA,MACf,SAAS,CAAC,UAAU,OAAO,MAAM,aAAa;AAAA,MAK9C,WACE,SACE,oBAAC,mBAAgB,OAAO,MAAM,OAAO,SAAS,MAAM,SAAS,CAAC,CAAC,GAAG,QAAQ,GAAG,MAAM,iBAAiB,IAClG;AAAA,MAEN,SAAS,oBAAC,mBAAgB,IAAI,EAAE,UAAU,MAAM,EAAE,EAAE,GAAG;AAAA,MACvD,IAAI;AAAA,QACF,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,UAAU,MAAM,GAAG;AAAA,QACnB,OAAO,WAAW,eAAe;AAAA,QACjC,YAAY;AAAA,QACZ,UAAU,MAAM,EAAE;AAAA,QAClB,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,aAAa,CAAC,UAAU,QAAQ,UAAU,KAAK;AAAA,QAC/C,SAAS,SAAS,oBAAoB;AAAA,QACtC,WAAW;AAAA,UACT,aAAa,SAAS,OAAO;AAAA,UAC7B,SAAS,SAAS,oBAAoB;AAAA,QACxC;AAAA,MACF;AAAA,MAIA,8BAAC,OAAI,WAAU,QAAO,IAAI,EAAE,UAAU,UAAU,cAAc,WAAW,GACtE,mBAAS,eAAe,OAAO,KAAK,IAAI,MAAM,OACjD;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,UAA6C;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,IAAI;AAC7D,QAAM,SAAS,WAAW,KAAK;AAC/B,QAAM,SAAS,GAAG,YAAY,UAAU,MAAM,EAAE;AAChD,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,CAAC,YAAY;AACnB,oBAAU,OAAO;AACjB,yBAAe,IAAI;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,MAAM;AAAA,QACpB;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,QAAQ,MAAM;AAAA,QACpB,UAAU;AAAA,QACV,SAAS,MAAM;AACb,oBAAU,IAAI;AACd,yBAAe,KAAK;AAAA,QACtB;AAAA,QACA,cAAc,EAAE,UAAU,UAAU,YAAY,OAAO;AAAA,QACvD,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE;AAAA,QAE9C;AAAA,8BAAC,eAAY,OAAc,OAAc,UAAoB,QAAgB;AAAA,UAI5E,UACC,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,iBAAiB,KAAK,GAAG,IAAI,IAAI,GACjG;AAAA,gCAAC,cAAW,WAAU,QAAO,IAAI,EAAE,UAAU,MAAM,EAAE,GAAG,OAAO,iBAAiB,GAC7E,yBAAe,OAAO,KAAK,GAC9B;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,SAAS,MAAM,SAAS,CAAC,CAAC;AAAA,gBAC1B,eAAa,GAAG,MAAM;AAAA,gBACtB,IAAI,EAAE,eAAe,OAAO;AAAA,gBAE3B,eAAK,QAAQ;AAAA;AAAA,YAChB;AAAA,aACF;AAAA;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAQO,SAAS,gBAAmD;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,MAAI,MAAM,SAAS,OAAO;AACxB,WACE,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC1D;AAAA,0BAAC,cAAW,WAAU,QAAO,IAAI,EAAE,UAAU,MAAM,EAAE,GAAG,YAAY,KAAK,OAAO,iBAAiB,GAC9F,gBAAM,OACT;AAAA,MACA,oBAAC,eAAY,OAAc,OAAc,UAAoB,QAAgB;AAAA,OAC/E;AAAA,EAEJ;AACA,SACE,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC1D;AAAA;AAAA,MAAC,YAAY;AAAA,MAAZ;AAAA,QACC,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,QACZ,OAAO,aAAa,KAAK;AAAA,QACzB,UAAU,CAAC,UAAU,SAAS,KAAK;AAAA,QACnC;AAAA;AAAA,IACF;AAAA,IAIA,oBAAC,gBAAa,SAAS,WAAW,OAAO,IAAI,GAAG,OAAc,UAAoB,QAAgB;AAAA,KACpG;AAEJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/form/total-form/Fields/TextField.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { FormControl, FormLabel, FormMessage } from '../../Form';\nimport { Input } from '../../Input';\nimport { useFormContext } from '../FormContext';\n\n/** Props for the `total-form` {@link TextField}. */\nexport interface TextFieldProps {\n /** Field name; also the key into the form's values/errors. */\n name: string;\n /** Optional visible label rendered above the input. */\n label?: string;\n /** Native input type (e.g. `email`, `password`). Defaults to `text`. */\n type?: string;\n /** Placeholder shown when the field is empty. */\n placeholder?: string;\n}\n\n/**\n * Text input field bound to {@link useFormContext}. Composes the existing\n * `@12-apps/ui` `Form*` presentational primitives and the `Input` control,\n * reading its value/error from context and writing changes back through it.\n *\n * Renders its error via `total-form-field-<name>-message` and the input via\n * `total-form-field-<name>`.\n */\nexport function TextField({ name, label, type = 'text', placeholder }: TextFieldProps): React.ReactElement {\n const { values, errors, setFieldValue } = useFormContext();\n const error = errors[name];\n const testId = `total-form-field-${name}`;\n\n return (\n <FormControl fullWidth>\n {label && (\n <FormLabel htmlFor={name} error={Boolean(error)}>\n {label}\n </FormLabel>\n )}\n <Input\n id={name}\n name={name}\n type={type}\n placeholder={placeholder}\n value={values[name] ?? ''}\n error={Boolean(error)}\n data-testid={testId}\n onChange={(event) => setFieldValue(name, event.target.value)}\n />\n {error && (\n <FormMessage error dataTestId={`${testId}-message`}>\n {error}\n </FormMessage>\n )}\n </FormControl>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAkCI,SAEI,KAFJ;AANG,SAAS,UAAU,EAAE,MAAM,OAAO,OAAO,QAAQ,YAAY,GAAuC;AACzG,QAAM,EAAE,QAAQ,QAAQ,cAAc,IAAI,eAAe;AACzD,QAAM,QAAQ,OAAO,IAAI;AACzB,QAAM,SAAS,oBAAoB,IAAI;AAEvC,SACE,qBAAC,eAAY,WAAS,MACnB;AAAA,aACC,oBAAC,aAAU,SAAS,MAAM,OAAO,QAAQ,KAAK,GAC3C,iBACH;AAAA,IAEF;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,OAAO,IAAI,KAAK;AAAA,QACvB,OAAO,QAAQ,KAAK;AAAA,QACpB,eAAa;AAAA,QACb,UAAU,CAAC,UAAU,cAAc,MAAM,MAAM,OAAO,KAAK;AAAA;AAAA,IAC7D;AAAA,IACC,SACC,oBAAC,eAAY,OAAK,MAAC,YAAY,GAAG,MAAM,YACrC,iBACH;AAAA,KAEJ;AAEJ;","names":[]}
@@ -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 { useDataViewsCopy } from \"./data-views-copy-context\";\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 { useStateWriters } from \"./data-views-writers\";\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/**\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 copy = useDataViewsCopy();\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, toggleColumn } = useStateWriters(setState);\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 // Server mode is where `matched` is a PAGE — see `useSelection` (FUT-942).\n const selection = useSelection(matched, getRowId, { rememberOffPage: Boolean(server) });\n const resolvedSortFields = sortFields ?? defaultSortFields(columns, copy.grid);\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AAmJrD,SAAS,eACP,QACA,MACA,SACA,QACA,aACA,OACK;AACL,SAAO;AAAA,IACL,MACE,SACI,OACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,MAAM,QAAQ,OAAO,MAAM,OAAO,QAAQ,MAAM,OAAO;AAAA,MACjE;AAAA,IACF;AAAA,IACN,CAAC,QAAQ,MAAM,SAAS,QAAQ,aAAa,MAAM,QAAQ,MAAM,OAAO,MAAM,MAAM;AAAA,EACtF;AACF;AAWA,SAAS,oBACP,cACA,WACA,OACA,eACA,UACM;AACN,QAAM,eAAe,OAAO,IAAI;AAChC,YAAU,MAAM;AACd,QAAI,aAAa,SAAS;AACxB,mBAAa,UAAU;AACvB;AAAA,IACF;AACA,QAAI,aAAc,UAAS,YAAY;AAAA,EAGzC,GAAG,CAAC,cAAc,QAAQ,CAAC;AAC3B,QAAM,YAAY,OAAO,IAAI;AAC7B,YAAU,MAAM;AACd,QAAI,UAAU,SAAS;AACrB,gBAAU,UAAU;AACpB;AAAA,IACF;AAGA,QAAI,UAAW,UAAS,CAAC,UAAU,EAAE,GAAG,MAAM,GAAG,UAAU,EAAE;AAAA,EAC/D,GAAG,CAAC,WAAW,QAAQ,CAAC;AACxB,YAAU,MAAM;AACd,oBAAgB,KAAK;AAAA,EACvB,GAAG,CAAC,OAAO,aAAa,CAAC;AAC3B;AAQA,SAAS,UACP,cACA,WACA,cACe;AACf,QAAM,OAAO,gBAAgB,eAAe,YAAY;AACxD,SAAO,YAAY,EAAE,GAAG,MAAM,GAAG,UAAU,IAAI;AACjD;AASA,SAAS,aACP,OACA,gBACsB;AACtB,SAAO,CAAC,OAAO;AACb,UAAM,EAAE,OAAO,GAAG,CAAC;AACnB,mBAAe;AAAA,EACjB;AACF;AAMA,SAAS,iBAAiB,cAAwB,OAAuC;AACvF,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,QAAM,WAAW,IAAI,IAAI,YAAY;AACrC,QAAM,OAAO,MAAM,OAAO,CAAC,OAAO,SAAS,IAAI,EAAE,CAAC;AAClD,QAAM,OAAO,IAAI,IAAI,IAAI;AACzB,SAAO,CAAC,GAAG,MAAM,GAAG,aAAa,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AAChE;AAOA,SAAS,UACP,OACA,OACA,QACA,cACe;AACf,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB,QAAQ,MAAM;AAAA;AAAA,IAEd,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACvC,MAAM,QAAQ,QAAQ;AAAA;AAAA,IAEtB,UAAU,QAAQ,YAAY;AAAA,EAChC;AACF;AAGA,SAAS,aACP,OACA,oBAC8D;AAC9D,QAAM,SAAS,MAAM,SAAS,CAAC;AAC/B,SAAO;AAAA,IACL,iBAAiB,QAAQ,MAAM,mBAAmB,CAAC,GAAG,SAAS;AAAA,IAC/D,iBAAiB,QAAQ,QAAQ,SAAS,SAAS;AAAA,EACrD;AACF;AAQO,SAAS,kBAAqD;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AACF,GAAqD;AACnD,uBAAqB,QAAQ,OAAO,IAAI,CAAC,UAAU,MAAM,EAAE,GAAG,cAAc,WAAW,MAAS;AAChG,QAAM,OAAO,iBAAiB;AAC9B,QAAM,EAAE,oBAAoB,aAAa,IAAI,sBAAsB,SAAS;AAAA,IAC1E;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,CAAC,OAAO,QAAQ,IAAI;AAAA,IAAwB,MAChD,UAAU,cAAc,WAAW,YAAY;AAAA,EACjD;AACA,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,sBAAoB,cAAc,WAAW,OAAO,eAAe,QAAQ;AAE3E,QAAM,EAAE,OAAO,aAAa,IAAI,gBAAgB,QAAQ;AACxD,QAAM,UAAU,eAAe,QAAQ,MAAM,SAAS,QAAQ,aAAa,KAAK;AAIhF,QAAM,QAAQ,aAAa,QAAQ,MAAM,KAAK;AAE9C,QAAM,EAAE,WAAW,IAAI,eAAe,QAAQ,OAAO,KAAK;AAC1D,QAAM,EAAE,aAAa,gBAAgB,IAAI;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,YAAY,aAAa,SAAS,UAAU,EAAE,iBAAiB,QAAQ,MAAM,EAAE,CAAC;AACtF,QAAM,qBAAqB,cAAc,kBAAkB,SAAS,KAAK,IAAI;AAE7E,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,iBAAiB,cAAc,MAAM,KAAK;AAAA,IACvD;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,aAAa,OAAO,kBAAkB;AAAA,IACzC,mBAAmB,yBAAyB,OAAO,QAAQ,WAAW;AAAA,IACtE,GAAG,cAAc,QAAQ,QAAQ,MAAM;AAAA,IACvC;AAAA,IACA;AAAA,IACA,UAAU,aAAa,OAAO,UAAU,cAAc;AAAA,IACtD,cAAc,UAAU,OAAO,OAAO,QAAQ,QAAQ,MAAM;AAAA;AAAA;AAAA,IAG5D,aAAa,QAAQ;AAAA,EACvB;AACF;","names":[]}