@12-apps/ui 6.82.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 (241) hide show
  1. package/components-catalog.md +7 -0
  2. package/dist/_internal/components/data-display/Avatar/Avatar.js +3 -3
  3. package/dist/_internal/components/data-display/Avatar/Avatar.view.js +2 -2
  4. package/dist/_internal/components/data-display/Carousel/Carousel.chrome.js +3 -3
  5. package/dist/_internal/components/data-display/Carousel/Carousel.js +5 -5
  6. package/dist/_internal/components/data-display/Carousel/Carousel.parts.js +2 -2
  7. package/dist/_internal/components/data-display/Chart/Chart.js +3 -3
  8. package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
  9. package/dist/_internal/components/data-display/Chart/chart-renderers.js +2 -2
  10. package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
  11. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
  12. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +20 -19
  13. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +21 -20
  14. package/dist/_internal/components/data-display/DataViews/base-list-card.js +2 -2
  15. package/dist/_internal/components/data-display/DataViews/data-views-day-input.js +7 -3
  16. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +2 -2
  17. package/dist/_internal/components/data-display/DataViews/data-views-empty.js +1 -1
  18. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -4
  19. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +3 -3
  20. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +17 -16
  21. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +7 -6
  22. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +5 -4
  23. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +6 -5
  24. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +4 -3
  25. package/dist/_internal/components/data-display/DataViews/data-views-range-reset.js +9 -0
  26. package/dist/_internal/components/data-display/DataViews/data-views-range-reset.js.map +1 -0
  27. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +4 -4
  28. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +3 -3
  29. package/dist/_internal/components/data-display/DataViews/data-views-writers.js +3 -1
  30. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
  31. package/dist/_internal/components/data-display/FleetMap/FleetBody.js +8 -8
  32. package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +7 -7
  33. package/dist/_internal/components/data-display/FleetMap/FleetMap.fixtures.js +1 -1
  34. package/dist/_internal/components/data-display/FleetMap/FleetMap.js +9 -9
  35. package/dist/_internal/components/data-display/MapPreview/MapControlBar.js +3 -3
  36. package/dist/_internal/components/data-display/MapPreview/MapLayers.js +4 -4
  37. package/dist/_internal/components/data-display/MapPreview/MapMarkers.js +3 -3
  38. package/dist/_internal/components/data-display/MapPreview/MapPreview.helpers.js +2 -2
  39. package/dist/_internal/components/data-display/MapPreview/MapPreview.js +6 -6
  40. package/dist/_internal/components/data-display/index.js +29 -29
  41. package/dist/_internal/components/form/CepField/CepField.js +1 -1
  42. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.hooks.js +3 -3
  43. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +5 -5
  44. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.range.js +2 -2
  45. package/dist/_internal/components/form/DateRangePicker/DateRangePickerFields.js +3 -3
  46. package/dist/_internal/components/form/InputOTP/InputOTP.hooks.js +1 -1
  47. package/dist/_internal/components/form/InputOTP/InputOTP.js +2 -2
  48. package/dist/_internal/components/form/index.js +8 -8
  49. package/dist/_internal/components/form/total-form/Fields/CepField.js +2 -2
  50. package/dist/_internal/components/form/total-form/Fields/TextField.js +1 -1
  51. package/dist/_internal/components/form/total-form/Fields/index.js +4 -4
  52. package/dist/_internal/components/index.js +37 -37
  53. package/dist/_internal/en-US.data-display.js +1 -1
  54. package/dist/_internal/pt-BR.data-display.js +1 -1
  55. package/dist/_internal/user-avatar/user-avatar.js +4 -4
  56. package/dist/charts.js +11 -11
  57. package/dist/{chunk-HF2WLLGH.js → chunk-2XI4R6UO.js} +2 -2
  58. package/dist/{chunk-HF2WLLGH.js.map → chunk-2XI4R6UO.js.map} +1 -1
  59. package/dist/{chunk-GYFKIPR2.js → chunk-4PUCT3RX.js} +2 -2
  60. package/dist/{chunk-JQSF3CUR.js → chunk-6HMDUUVJ.js} +2 -1
  61. package/dist/{chunk-JQSF3CUR.js.map → chunk-6HMDUUVJ.js.map} +1 -1
  62. package/dist/{chunk-THD3N4BJ.js → chunk-6ONQTXFM.js} +24 -5
  63. package/dist/chunk-6ONQTXFM.js.map +1 -0
  64. package/dist/{chunk-C2I7NQC4.js → chunk-74KCGLOU.js} +2 -2
  65. package/dist/{chunk-4J62V5OE.js → chunk-7YJYIMWP.js} +2 -2
  66. package/dist/{chunk-MT74NRLY.js → chunk-C2OX3C7L.js} +4 -4
  67. package/dist/{chunk-62XVHRTX.js → chunk-D7SOXI2K.js} +70 -21
  68. package/dist/chunk-D7SOXI2K.js.map +1 -0
  69. package/dist/{chunk-BO6IMK5P.js → chunk-DPHSJLMN.js} +2 -2
  70. package/dist/{chunk-7XSCEHGA.js → chunk-EOQ6EKTS.js} +7 -7
  71. package/dist/{chunk-4OTJ66ID.js → chunk-EXTES7LP.js} +15 -4
  72. package/dist/chunk-EXTES7LP.js.map +1 -0
  73. package/dist/{chunk-4IVEBBF6.js → chunk-FYJDI5BW.js} +4 -3
  74. package/dist/{chunk-4IVEBBF6.js.map → chunk-FYJDI5BW.js.map} +1 -1
  75. package/dist/{chunk-A5U7HEBI.js → chunk-GNDIVKWF.js} +4 -2
  76. package/dist/chunk-GNDIVKWF.js.map +1 -0
  77. package/dist/{chunk-XKPV52CG.js → chunk-GPTNZ57P.js} +2 -2
  78. package/dist/{chunk-ZGAOA7LN.js → chunk-H56CRMCM.js} +6 -6
  79. package/dist/{chunk-AMLHEKSV.js → chunk-HI7JPDD6.js} +5 -5
  80. package/dist/{chunk-JK3QB7MO.js → chunk-I54LHHZS.js} +2 -2
  81. package/dist/{chunk-GLAAX5JJ.js → chunk-I5NTADBO.js} +2 -1
  82. package/dist/{chunk-GLAAX5JJ.js.map → chunk-I5NTADBO.js.map} +1 -1
  83. package/dist/{chunk-LCGK22QQ.js → chunk-IKXCAYFH.js} +5 -5
  84. package/dist/{chunk-MLGAGZJ3.js → chunk-ILNAPS44.js} +8 -7
  85. package/dist/chunk-ILNAPS44.js.map +1 -0
  86. package/dist/{chunk-D22FGMGU.js → chunk-JIAIPOEV.js} +2 -2
  87. package/dist/{chunk-HQCZT6TO.js → chunk-JVOEVTCZ.js} +5 -5
  88. package/dist/{chunk-76IHYINM.js → chunk-KEBYZ2MO.js} +16 -9
  89. package/dist/chunk-KEBYZ2MO.js.map +1 -0
  90. package/dist/{chunk-JJNY5MZL.js → chunk-KG7OTJ26.js} +2 -2
  91. package/dist/{chunk-2ZMR3TZU.js → chunk-L6UTYEUS.js} +3 -3
  92. package/dist/{chunk-WWB43LWU.js → chunk-LOGSJH2Q.js} +2 -2
  93. package/dist/{chunk-YJPBNIEM.js → chunk-MGFTHOPG.js} +2 -2
  94. package/dist/{chunk-RB3R6BJL.js → chunk-O4G7QKDU.js} +2 -2
  95. package/dist/{chunk-DYHOBKPU.js → chunk-OA6T2ME3.js} +5 -5
  96. package/dist/{chunk-U5FOJBLG.js → chunk-OOLES6RR.js} +14 -2
  97. package/dist/chunk-OOLES6RR.js.map +1 -0
  98. package/dist/{chunk-YHKHHSZH.js → chunk-OQ554425.js} +3 -3
  99. package/dist/{chunk-54OJAOXC.js → chunk-PDSZN26U.js} +2 -2
  100. package/dist/{chunk-MNNIOQTQ.js → chunk-PGVGCDPD.js} +3 -3
  101. package/dist/{chunk-RDRXTH47.js → chunk-PWXDMP7G.js} +10 -6
  102. package/dist/chunk-PWXDMP7G.js.map +1 -0
  103. package/dist/{chunk-6ATXOJVQ.js → chunk-QADTI63U.js} +4 -4
  104. package/dist/{chunk-UETHX57J.js → chunk-R6L376KR.js} +21 -7
  105. package/dist/chunk-R6L376KR.js.map +1 -0
  106. package/dist/{chunk-SEUJNFOD.js → chunk-RLDTV5I2.js} +2 -2
  107. package/dist/{chunk-4UCXJYOO.js → chunk-RUQNKOSP.js} +4 -4
  108. package/dist/chunk-RUQNKOSP.js.map +1 -0
  109. package/dist/{chunk-7RG37FVT.js → chunk-RXKFPXVH.js} +29 -2
  110. package/dist/chunk-RXKFPXVH.js.map +1 -0
  111. package/dist/{chunk-KSJBFT4G.js → chunk-SGAHME6W.js} +3 -3
  112. package/dist/{chunk-SB2SEGR6.js → chunk-TIM4PXWN.js} +3 -3
  113. package/dist/{chunk-D4LMRSY7.js → chunk-UIWVSZ6I.js} +2 -2
  114. package/dist/{chunk-SMANO5OD.js → chunk-V2UJEICC.js} +3 -3
  115. package/dist/chunk-V2UJEICC.js.map +1 -0
  116. package/dist/{chunk-XATRYJ4Q.js → chunk-VQBY73QH.js} +4 -4
  117. package/dist/{chunk-SYERREN5.js → chunk-W2JYXZC2.js} +4 -4
  118. package/dist/{chunk-QCNUMOTL.js → chunk-YC2QYRNE.js} +9 -3
  119. package/dist/chunk-YC2QYRNE.js.map +1 -0
  120. package/dist/chunk-YOPMXGHY.js +23 -0
  121. package/dist/chunk-YOPMXGHY.js.map +1 -0
  122. package/dist/{chunk-QK3XXPBA.js → chunk-YWJCMGTE.js} +2 -2
  123. package/dist/{chunk-UQ6SAIX7.js → chunk-Z47SYKRX.js} +2 -2
  124. package/dist/data-display/Avatar.js +3 -3
  125. package/dist/data-display/CardKit.js +3 -3
  126. package/dist/data-display/Carousel.js +5 -5
  127. package/dist/data-display/Chart.js +11 -11
  128. package/dist/data-display/DataGrid.js +2 -2
  129. package/dist/data-display/DataViews.js +25 -24
  130. package/dist/data-display/FleetMap.js +9 -9
  131. package/dist/data-display/MapPreview.js +6 -6
  132. package/dist/en-US.js +1 -1
  133. package/dist/form/CepField.js +1 -1
  134. package/dist/form/DateRangePicker.js +5 -5
  135. package/dist/form/InputOTP.js +2 -2
  136. package/dist/form/total-form.js +4 -4
  137. package/dist/locales.js +2 -2
  138. package/dist/pt-BR.js +1 -1
  139. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +6 -0
  140. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  141. package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts +32 -5
  142. package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts.map +1 -1
  143. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts +6 -0
  144. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts.map +1 -1
  145. package/dist/types/components/data-display/DataViews/data-views-grid-parts.d.ts.map +1 -1
  146. package/dist/types/components/data-display/DataViews/data-views-inline-bar.d.ts +3 -1
  147. package/dist/types/components/data-display/DataViews/data-views-inline-bar.d.ts.map +1 -1
  148. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts +3 -1
  149. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  150. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  151. package/dist/types/components/data-display/DataViews/data-views-range-reset.d.ts +16 -0
  152. package/dist/types/components/data-display/DataViews/data-views-range-reset.d.ts.map +1 -0
  153. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts +13 -0
  154. package/dist/types/components/data-display/DataViews/data-views-writers.d.ts.map +1 -1
  155. package/dist/types/components/data-display/DataViews/use-data-views-state.d.ts +4 -0
  156. package/dist/types/components/data-display/DataViews/use-data-views-state.d.ts.map +1 -1
  157. package/dist/types/components/form/CepField/CepField.d.ts +2 -1
  158. package/dist/types/components/form/CepField/CepField.d.ts.map +1 -1
  159. package/dist/types/components/form/CepField/CepField.types.d.ts +24 -0
  160. package/dist/types/components/form/CepField/CepField.types.d.ts.map +1 -1
  161. package/dist/types/components/form/InputOTP/InputOTP.d.ts.map +1 -1
  162. package/dist/types/components/form/InputOTP/InputOTP.hooks.d.ts.map +1 -1
  163. package/dist/types/components/form/total-form/Fields/CepField.d.ts +11 -1
  164. package/dist/types/components/form/total-form/Fields/CepField.d.ts.map +1 -1
  165. package/dist/types/components/form/total-form/Fields/TextField.d.ts +22 -1
  166. package/dist/types/components/form/total-form/Fields/TextField.d.ts.map +1 -1
  167. package/dist/types/components/form/total-form/Fields/index.d.ts +1 -1
  168. package/dist/types/components/form/total-form/Fields/index.d.ts.map +1 -1
  169. package/dist/types/components/form/total-form/index.d.ts +1 -1
  170. package/dist/types/components/form/total-form/index.d.ts.map +1 -1
  171. package/dist/types/en-US.data-display.d.ts.map +1 -1
  172. package/dist/types/pt-BR.data-display.d.ts.map +1 -1
  173. package/dist/user-avatar.js +4 -4
  174. package/package.json +1 -1
  175. package/src/components/data-display/DataViews/data-views-copy.ts +6 -0
  176. package/src/components/data-display/DataViews/data-views-day-input.tsx +134 -38
  177. package/src/components/data-display/DataViews/data-views-empty.tsx +1 -1
  178. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +8 -2
  179. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +3 -1
  180. package/src/components/data-display/DataViews/data-views-inline-bar.tsx +7 -0
  181. package/src/components/data-display/DataViews/data-views-more-filters.tsx +4 -1
  182. package/src/components/data-display/DataViews/data-views-range-pill.tsx +11 -3
  183. package/src/components/data-display/DataViews/data-views-range-reset.ts +34 -0
  184. package/src/components/data-display/DataViews/data-views-writers.ts +24 -1
  185. package/src/components/data-display/DataViews/use-data-views-state.ts +6 -1
  186. package/src/components/form/CepField/CepField.md +29 -2
  187. package/src/components/form/CepField/CepField.tsx +44 -7
  188. package/src/components/form/CepField/CepField.types.ts +24 -0
  189. package/src/components/form/InputOTP/InputOTP.hooks.ts +48 -9
  190. package/src/components/form/InputOTP/InputOTP.md +6 -1
  191. package/src/components/form/InputOTP/InputOTP.tsx +40 -1
  192. package/src/components/form/total-form/Fields/CepField.tsx +16 -0
  193. package/src/components/form/total-form/Fields/TextField.tsx +64 -1
  194. package/src/components/form/total-form/Fields/index.ts +1 -1
  195. package/src/components/form/total-form/index.ts +1 -1
  196. package/src/en-US.data-display.ts +1 -0
  197. package/src/pt-BR.data-display.ts +1 -0
  198. package/dist/chunk-4OTJ66ID.js.map +0 -1
  199. package/dist/chunk-4UCXJYOO.js.map +0 -1
  200. package/dist/chunk-62XVHRTX.js.map +0 -1
  201. package/dist/chunk-76IHYINM.js.map +0 -1
  202. package/dist/chunk-7RG37FVT.js.map +0 -1
  203. package/dist/chunk-A5U7HEBI.js.map +0 -1
  204. package/dist/chunk-MLGAGZJ3.js.map +0 -1
  205. package/dist/chunk-QCNUMOTL.js.map +0 -1
  206. package/dist/chunk-RDRXTH47.js.map +0 -1
  207. package/dist/chunk-SMANO5OD.js.map +0 -1
  208. package/dist/chunk-THD3N4BJ.js.map +0 -1
  209. package/dist/chunk-U5FOJBLG.js.map +0 -1
  210. package/dist/chunk-UETHX57J.js.map +0 -1
  211. /package/dist/{chunk-GYFKIPR2.js.map → chunk-4PUCT3RX.js.map} +0 -0
  212. /package/dist/{chunk-C2I7NQC4.js.map → chunk-74KCGLOU.js.map} +0 -0
  213. /package/dist/{chunk-4J62V5OE.js.map → chunk-7YJYIMWP.js.map} +0 -0
  214. /package/dist/{chunk-MT74NRLY.js.map → chunk-C2OX3C7L.js.map} +0 -0
  215. /package/dist/{chunk-BO6IMK5P.js.map → chunk-DPHSJLMN.js.map} +0 -0
  216. /package/dist/{chunk-7XSCEHGA.js.map → chunk-EOQ6EKTS.js.map} +0 -0
  217. /package/dist/{chunk-XKPV52CG.js.map → chunk-GPTNZ57P.js.map} +0 -0
  218. /package/dist/{chunk-ZGAOA7LN.js.map → chunk-H56CRMCM.js.map} +0 -0
  219. /package/dist/{chunk-AMLHEKSV.js.map → chunk-HI7JPDD6.js.map} +0 -0
  220. /package/dist/{chunk-JK3QB7MO.js.map → chunk-I54LHHZS.js.map} +0 -0
  221. /package/dist/{chunk-LCGK22QQ.js.map → chunk-IKXCAYFH.js.map} +0 -0
  222. /package/dist/{chunk-D22FGMGU.js.map → chunk-JIAIPOEV.js.map} +0 -0
  223. /package/dist/{chunk-HQCZT6TO.js.map → chunk-JVOEVTCZ.js.map} +0 -0
  224. /package/dist/{chunk-JJNY5MZL.js.map → chunk-KG7OTJ26.js.map} +0 -0
  225. /package/dist/{chunk-2ZMR3TZU.js.map → chunk-L6UTYEUS.js.map} +0 -0
  226. /package/dist/{chunk-WWB43LWU.js.map → chunk-LOGSJH2Q.js.map} +0 -0
  227. /package/dist/{chunk-YJPBNIEM.js.map → chunk-MGFTHOPG.js.map} +0 -0
  228. /package/dist/{chunk-RB3R6BJL.js.map → chunk-O4G7QKDU.js.map} +0 -0
  229. /package/dist/{chunk-DYHOBKPU.js.map → chunk-OA6T2ME3.js.map} +0 -0
  230. /package/dist/{chunk-YHKHHSZH.js.map → chunk-OQ554425.js.map} +0 -0
  231. /package/dist/{chunk-54OJAOXC.js.map → chunk-PDSZN26U.js.map} +0 -0
  232. /package/dist/{chunk-MNNIOQTQ.js.map → chunk-PGVGCDPD.js.map} +0 -0
  233. /package/dist/{chunk-6ATXOJVQ.js.map → chunk-QADTI63U.js.map} +0 -0
  234. /package/dist/{chunk-SEUJNFOD.js.map → chunk-RLDTV5I2.js.map} +0 -0
  235. /package/dist/{chunk-KSJBFT4G.js.map → chunk-SGAHME6W.js.map} +0 -0
  236. /package/dist/{chunk-SB2SEGR6.js.map → chunk-TIM4PXWN.js.map} +0 -0
  237. /package/dist/{chunk-D4LMRSY7.js.map → chunk-UIWVSZ6I.js.map} +0 -0
  238. /package/dist/{chunk-XATRYJ4Q.js.map → chunk-VQBY73QH.js.map} +0 -0
  239. /package/dist/{chunk-SYERREN5.js.map → chunk-W2JYXZC2.js.map} +0 -0
  240. /package/dist/{chunk-QK3XXPBA.js.map → chunk-YWJCMGTE.js.map} +0 -0
  241. /package/dist/{chunk-UQ6SAIX7.js.map → chunk-Z47SYKRX.js.map} +0 -0
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+
5
+ import type { RangeValue } from './data-views-range-types';
6
+
7
+ /** One string per range CONTENT: `{}` is rebuilt on every render upstream. */
8
+ const rangeKeyOf = (range: RangeValue): string => `${range.min ?? ''}|${range.max ?? ''}`;
9
+
10
+ /**
11
+ * Remounts the two bound fields when the range is moved from OUTSIDE them — a
12
+ * "Limpar", a preset, a saved view — and never for the fields' own writes.
13
+ *
14
+ * A field keeps a day it refuses (`31/02/2026`) on screen until the APPLIED
15
+ * bound moves. When a clear empties the other bound, or a bound that was open
16
+ * already, this field's bound does not move, so without a remount the refused
17
+ * day would sit red under a filter that had just been cleared (FUT-4144).
18
+ * Remounting only for outside writes keeps the caret where it is while typing.
19
+ */
20
+ export function useOutsideResetKey(value: RangeValue): { key: number; emitted: (next: RangeValue) => RangeValue } {
21
+ const [key, setKey] = useState(0);
22
+ const lastWritten = useRef(rangeKeyOf(value));
23
+ const current = rangeKeyOf(value);
24
+ useEffect(() => {
25
+ if (current === lastWritten.current) return;
26
+ lastWritten.current = current;
27
+ setKey((n) => n + 1);
28
+ }, [current]);
29
+ const emitted = (next: RangeValue) => {
30
+ lastWritten.current = rangeKeyOf(next);
31
+ return next;
32
+ };
33
+ return { key, emitted };
34
+ }
@@ -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,
@@ -20,6 +20,7 @@ serves a supplier form, a store-location form or a delivery checkout.
20
20
  - Race-safe: a slow response for a since-edited CEP can never overwrite a newer one
21
21
  - Degrades to a plain masked input when the lookup fails, throws, or is absent
22
22
  - Accessible status via `role="status"` / `aria-live="polite"` and `aria-busy`
23
+ - Takes `Input`'s `size`, and can float its label inside the box like `Input`
23
24
  - Works uncontrolled-by-a-form (props) or bound to `total-form` (`Fields.CepField`)
24
25
 
25
26
  ## Props Documentation
@@ -42,6 +43,15 @@ serves a supplier form, a store-location form or a delivery checkout.
42
43
  - `placeholder`: Placeholder text (default `00000-000`)
43
44
  - `name`: Field name / input id (default `postalCode`)
44
45
  - `dataTestId`: Test id for the input (default `cep-field-<name>`)
46
+ - `size`: A step of the `Input` size scale (`xs` | `sm` | `md` | `lg` | `xl`, default
47
+ `md`). Same values and same box heights as `Input`, so a `CepField` at `size="sm"`
48
+ stands level with `sm` `Input`s
49
+ - `labelPlacement`: `above` (default) stacks the label over the box; `inside` draws it
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`
45
55
 
46
56
  ### State Props
47
57
 
@@ -83,6 +93,20 @@ const [cep, setCep] = useState('');
83
93
  />
84
94
  ```
85
95
 
96
+ ### Beside compact `Input`s
97
+
98
+ An address form made of `Input`s draws every label inside its box. Opt the CEP in to
99
+ the same treatment, and give it the form's `size`, so the CEP block is no taller than
100
+ its neighbours:
101
+
102
+ ```tsx
103
+ <CepField size="sm" labelPlacement="inside" value={cep} onChange={setCep} copy={copy} />
104
+ <Input size="sm" label="Rua" />
105
+ ```
106
+
107
+ `labelPlacement` defaults to `above` so a form that never asked keeps the stacked label
108
+ it has always had. `Fields.CepField` takes the same two props.
109
+
86
110
  ### Bound to `total-form`
87
111
 
88
112
  `Fields.CepField` wires the field to the surrounding `FormContainer` and writes
@@ -134,5 +158,8 @@ the lookup. The pure helpers (`formatCep`, `isValidCep`) live in
134
158
  "Buscando endereço…" / "Endereço preenchido pelo CEP" without stealing focus
135
159
  - `aria-busy` reflects the in-flight lookup
136
160
  - `aria-describedby` links the input to its status region
137
- - `inputMode="numeric"` brings up the numeric keypad on mobile
138
- - `autoComplete="postal-code"` lets the browser's own autofill participate
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
163
+ - With `labelPlacement="inside"` the label is the `Input`'s own, associated to the input
164
+ by `for`/`id`, so `getByLabelText` and screen readers name the field the same way
165
+ - `autoComplete="postal-code"` (the default) lets the browser's own autofill participate
@@ -118,6 +118,35 @@ 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
+
127
+ /** The stacked label over the box (`labelPlacement="above"`), or nothing when there is no label. */
128
+ function AboveLabel({
129
+ name,
130
+ label,
131
+ error,
132
+ }: {
133
+ name: string;
134
+ label?: string;
135
+ error?: string;
136
+ }): React.ReactElement | null {
137
+ if (!label) return null;
138
+ return (
139
+ <FormLabel htmlFor={name} error={Boolean(error)}>
140
+ {label}
141
+ </FormLabel>
142
+ );
143
+ }
144
+
145
+ /** The label handed to the `Input`, which draws it in the box: only for `inside`. */
146
+ function insideLabel(label: string | undefined, placement: 'above' | 'inside'): string | undefined {
147
+ return placement === 'inside' ? label : undefined;
148
+ }
149
+
121
150
  /**
122
151
  * A CEP input that fills the rest of the address in.
123
152
  *
@@ -156,6 +185,9 @@ export function CepField({
156
185
  name = 'postalCode',
157
186
  dataTestId,
158
187
  copy,
188
+ size, // undefined reaches `Input`, whose own default is `md`
189
+ labelPlacement = 'above',
190
+ autoComplete,
159
191
  }: CepFieldProps): React.ReactElement {
160
192
  const testId = dataTestId ?? `cep-field-${name}`;
161
193
 
@@ -176,17 +208,22 @@ export function CepField({
176
208
 
177
209
  return (
178
210
  <FormControl fullWidth>
179
- {label && (
180
- <FormLabel htmlFor={name} error={Boolean(error)}>
181
- {label}
182
- </FormLabel>
183
- )}
211
+ {labelPlacement === 'above' && <AboveLabel name={name} label={label} error={error} />}
184
212
  <Input
185
213
  id={name}
186
214
  name={name}
187
215
  type="text"
188
- inputMode="numeric"
189
- 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)}
222
+ size={size}
223
+ // `inside`: the label is the Input's own, drawn in the box and floating
224
+ // up on focus or a value — what every other `Input` in the form does.
225
+ // `above` keeps the stacked `FormLabel`, for callers that never opted in.
226
+ label={insideLabel(label, labelPlacement)}
190
227
  placeholder={placeholder}
191
228
  value={value}
192
229
  error={Boolean(error)}
@@ -1,4 +1,5 @@
1
1
  import type { CepFieldCopy } from "../../../copy";
2
+ import type { InputSize } from "../Input/Input.base";
2
3
  /**
3
4
  * Types for {@link CepField} — the reusable CEP auto-completer.
4
5
  */
@@ -73,4 +74,27 @@ export interface CepFieldProps {
73
74
  name?: string;
74
75
  /** Test id for the input. Defaults to `cep-field-<name>`. */
75
76
  dataTestId?: string;
77
+ /**
78
+ * A step of the house size scale — the same values, and the same box
79
+ * heights, as {@link Input}'s `size`. Defaults to `md`.
80
+ */
81
+ size?: InputSize;
82
+ /**
83
+ * Where the label is drawn.
84
+ *
85
+ * - `above` (default): a stacked label over the box, as the field has always
86
+ * rendered. Every existing caller keeps it.
87
+ * - `inside`: the label sits in the box and floats up on focus or a value,
88
+ * exactly as `Input`'s label does. Use it beside `Input`s so the CEP block
89
+ * is the same height as its neighbours.
90
+ */
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;
76
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
  ))}
@@ -3,6 +3,7 @@
3
3
  import type { CepFieldCopy } from "../../../../copy";
4
4
  import React, { useCallback, useMemo, useRef } from 'react';
5
5
 
6
+ import type { InputSize } from '../../Input/Input.base';
6
7
  import { CepField as BaseCepField, type CepAddress } from '../../CepField';
7
8
  import { useFormContext } from '../FormContext';
8
9
 
@@ -29,6 +30,15 @@ export interface CepFieldProps {
29
30
  fills?: CepFills;
30
31
  /** Milliseconds to wait after the last keystroke before looking up. */
31
32
  debounceMs?: number;
33
+ /** The input's size — the same scale, and box heights, as `Input`. Defaults to `md`. */
34
+ size?: InputSize;
35
+ /** `above` (default) stacks the label over the box; `inside` floats it in the box like `Input`. */
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;
32
42
  }
33
43
 
34
44
  const PARTS: readonly CepPart[] = ['street', 'neighborhood', 'city', 'state'];
@@ -107,6 +117,9 @@ export function CepField({
107
117
  fills,
108
118
  debounceMs,
109
119
  copy,
120
+ size,
121
+ labelPlacement,
122
+ autoComplete,
110
123
  }: CepFieldProps): React.ReactElement {
111
124
  const { values, errors, setFieldValue } = useFormContext();
112
125
  const { write, clearOwned } = useOwnedAutofill();
@@ -142,6 +155,9 @@ export function CepField({
142
155
  onResolved={handleResolved}
143
156
  onNotFound={clearOwned}
144
157
  debounceMs={debounceMs}
158
+ size={size}
159
+ labelPlacement={labelPlacement}
160
+ autoComplete={autoComplete}
145
161
  dataTestId={`total-form-field-${name}`}
146
162
  />
147
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":[]}