@12-apps/ui 6.92.0 → 6.92.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +10 -10
  2. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +11 -11
  3. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -5
  4. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +9 -9
  5. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +7 -7
  6. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +13 -7
  7. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +6 -6
  8. package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +2 -2
  9. package/dist/_internal/components/data-display/DataViews/data-views-range-calendar.js +1 -1
  10. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +4 -4
  11. package/dist/_internal/components/data-display/DataViews/data-views-range-summary.js +1 -1
  12. package/dist/{chunk-W2KOP27S.js → chunk-3I5PHWKQ.js} +2 -2
  13. package/dist/{chunk-3OHZAZEE.js → chunk-3WYEENUH.js} +9 -4
  14. package/dist/chunk-3WYEENUH.js.map +1 -0
  15. package/dist/{chunk-R4LCPPKA.js → chunk-CNUO2BRV.js} +2 -2
  16. package/dist/{chunk-MZAYIG2I.js → chunk-D2JQF2WC.js} +66 -16
  17. package/dist/chunk-D2JQF2WC.js.map +1 -0
  18. package/dist/{chunk-HS662RLW.js → chunk-DWH4TQXK.js} +4 -3
  19. package/dist/chunk-DWH4TQXK.js.map +1 -0
  20. package/dist/{chunk-FORAVDJF.js → chunk-FC7UCFFA.js} +35 -7
  21. package/dist/chunk-FC7UCFFA.js.map +1 -0
  22. package/dist/{chunk-CKQOYPH3.js → chunk-FYUNROLW.js} +2 -2
  23. package/dist/{chunk-MICX23P4.js → chunk-HRES2S3Q.js} +85 -8
  24. package/dist/chunk-HRES2S3Q.js.map +1 -0
  25. package/dist/{chunk-X3GD3TFI.js → chunk-KQDBOZQE.js} +3 -3
  26. package/dist/{chunk-AI7XWBNG.js → chunk-QE4KYG5I.js} +3 -3
  27. package/dist/chunk-WLDP7C4K.js +44 -0
  28. package/dist/chunk-WLDP7C4K.js.map +1 -0
  29. package/dist/data-display/DataViews.js +11 -11
  30. package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts +80 -0
  31. package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
  32. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  33. package/dist/types/components/data-display/DataViews/data-views-range-bounds.d.ts +6 -1
  34. package/dist/types/components/data-display/DataViews/data-views-range-bounds.d.ts.map +1 -1
  35. package/dist/types/components/data-display/DataViews/data-views-range-calendar.d.ts +13 -1
  36. package/dist/types/components/data-display/DataViews/data-views-range-calendar.d.ts.map +1 -1
  37. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  38. package/dist/types/components/data-display/DataViews/data-views-range-summary.d.ts +4 -5
  39. package/dist/types/components/data-display/DataViews/data-views-range-summary.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/components/data-display/DataViews/RESPONSIVE-BEHAVIOUR.md +8 -4
  42. package/src/components/data-display/DataViews/data-views-more-fields.tsx +90 -14
  43. package/src/components/data-display/DataViews/data-views-more-filters.tsx +2 -2
  44. package/src/components/data-display/DataViews/data-views-range-bounds.tsx +57 -3
  45. package/src/components/data-display/DataViews/data-views-range-calendar.tsx +122 -8
  46. package/src/components/data-display/DataViews/data-views-range-pill.tsx +5 -0
  47. package/src/components/data-display/DataViews/data-views-range-summary.tsx +9 -22
  48. package/dist/chunk-3OHZAZEE.js.map +0 -1
  49. package/dist/chunk-FORAVDJF.js.map +0 -1
  50. package/dist/chunk-HS662RLW.js.map +0 -1
  51. package/dist/chunk-J2AVWIMM.js +0 -60
  52. package/dist/chunk-J2AVWIMM.js.map +0 -1
  53. package/dist/chunk-MICX23P4.js.map +0 -1
  54. package/dist/chunk-MZAYIG2I.js.map +0 -1
  55. /package/dist/{chunk-W2KOP27S.js.map → chunk-3I5PHWKQ.js.map} +0 -0
  56. /package/dist/{chunk-R4LCPPKA.js.map → chunk-CNUO2BRV.js.map} +0 -0
  57. /package/dist/{chunk-CKQOYPH3.js.map → chunk-FYUNROLW.js.map} +0 -0
  58. /package/dist/{chunk-X3GD3TFI.js.map → chunk-KQDBOZQE.js.map} +0 -0
  59. /package/dist/{chunk-AI7XWBNG.js.map → chunk-QE4KYG5I.js.map} +0 -0
@@ -30,6 +30,18 @@ export interface DayRangeCalendarProps {
30
30
  */
31
31
  viewKey: string;
32
32
  testId: string;
33
+ /**
34
+ * The applied window in words ("Data: 01/10–15/10"), said on the hint's own
35
+ * line with "Limpar" at its end. Set by the pill, whose popover has no other
36
+ * place for them; a panel says them in its own header and leaves this unset.
37
+ */
38
+ summary?: string;
39
+ /**
40
+ * Empties the range: the line's "Limpar", shown while `summary` is set. Set
41
+ * only where the line may hold it; the line reserves its 40px only then.
42
+ * `fromKeyboard` is true when it was pressed from the keyboard.
43
+ */
44
+ onClear?: (fromKeyboard: boolean) => void;
33
45
  }
34
- export declare function DayRangeCalendar({ fieldLabel, value, onChange, months, viewKey, testId, }: DayRangeCalendarProps): React.JSX.Element;
46
+ export declare function DayRangeCalendar({ fieldLabel, value, onChange, months, viewKey, testId, summary, onClear, }: DayRangeCalendarProps): React.JSX.Element;
35
47
  //# sourceMappingURL=data-views-range-calendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-views-range-calendar.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-calendar.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAI/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAgDrD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,GAAG,UAAU,CAIrF;AAwBD,yCAAyC;AACzC,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC;;;OAGG;IACH,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;IACd;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,KAAK,EACL,QAAQ,EACR,MAAM,EACN,OAAO,EACP,MAAM,GACP,EAAE,qBAAqB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAwC3C"}
1
+ {"version":3,"file":"data-views-range-calendar.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-calendar.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAI/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAgDrD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,GAAG,UAAU,CAIrF;AA6HD,yCAAyC;AACzC,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC;;;OAGG;IACH,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;IACd;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,KAAK,EACL,QAAQ,EACR,MAAM,EACN,OAAO,EACP,MAAM,EACN,OAAO,EACP,OAAO,GACR,EAAE,qBAAqB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAuC3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"data-views-range-pill.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-pill.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAM1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAUxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EAAE,WAAW,EAAE,CAAC;AA8IvB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,KAAK,EACL,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAwCpB;AAGD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACjE,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAoCpB"}
1
+ {"version":3,"file":"data-views-range-pill.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-pill.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAM1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAUxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EAAE,WAAW,EAAE,CAAC;AAkJvB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,KAAK,EACL,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAyCpB;AAGD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACjE,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAoCpB"}
@@ -1,16 +1,15 @@
1
1
  import React from 'react';
2
2
  import type { RangeFieldConfig, RangeValue } from './data-views-types';
3
3
  /**
4
- * The applied window in words, and "Limpar".
4
+ * The applied window in words, and "Limpar", under a NUMBER range's boxes.
5
5
  *
6
6
  * An inverted window is not an error the merchant must fix before the list
7
7
  * responds — it simply matches nothing, and saying so beats an empty grid with
8
8
  * no explanation (FUT-668).
9
9
  *
10
- * A DAY filter keeps the row's height while nothing is applied: its first tap
11
- * on the calendar applies a bound, and a row appearing then grew the popover
12
- * 26px and moved it 32px up at 320×568, the day under the finger with it
13
- * (FUT-4144 UI review round 2). The held row is invisible and inert.
10
+ * A DAY range says these on its calendar's hint line instead (FUT-4144): a row
11
+ * of its own, held empty until the first tap so the popover would not grow,
12
+ * left 59px of white under the hint at 1280.
14
13
  */
15
14
  export declare function RangeSummary<T extends Record<string, unknown>>({ field, value, onClear, testId, }: {
16
15
  field: RangeFieldConfig<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"data-views-range-summary.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-summary.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGvE;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC9D,KAAK,EACL,KAAK,EACL,OAAO,EACP,MAAM,GACP,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CA+B3B"}
1
+ {"version":3,"file":"data-views-range-summary.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-range-summary.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGvE;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC9D,KAAK,EACL,KAAK,EACL,OAAO,EACP,MAAM,GACP,EAAE;IACD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAmB3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.92.0",
3
+ "version": "6.92.2",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -292,9 +292,11 @@ the keystrokes it did.
292
292
  | months | one, `<Month> ▾ <Year> ▾ ‹ ›` caption | one, same caption | two side by side, `‹ setembro` · `outubro ›`, the current month on the right |
293
293
 
294
294
  - Order in the popover: preset chips, the `De`/`Até` boxes (sharing the row
295
- equally), the inclusive/inverted note, the calendar, then the line saying
295
+ equally), the inclusive/inverted note, the calendar, then ONE line under it:
296
296
  which tap comes next ("Toque no primeiro e no último dia." / "Agora toque no
297
- último dia."), then the pill's summary and "Limpar".
297
+ último dia."), and once a period is applied the window itself ("Data:
298
+ 01/10–15/10"), with "Limpar" at the line's end from the first tap on. In the
299
+ panels the line says only the step; their header carries "Limpar".
298
300
  - One view of ONE range: a tap writes the boxes; a typed whole day writes the
299
301
  bound and moves the calendar to it; an impossible typed day (`31/02/2026`)
300
302
  is refused in its box and the calendar does not move; "Limpar", a preset or a
@@ -320,8 +322,10 @@ the keystrokes it did.
320
322
  where their trigger was when they opened, so a "Limpar" or a badge appearing
321
323
  on the bar after the first tap does not drag them sideways. They are
322
324
  re-placed only when their own height changes (a refused day's message), so
323
- they never leave the screen. A day popover keeps its summary/"Limpar" row's
324
- height while nothing is applied, so the first tap does not grow it.
325
+ they never leave the screen. A day popover has no summary row of its own:
326
+ the window and "Limpar" are on the hint's line, whose height does not change
327
+ as its words do, so no row is held empty and the first tap does not grow it
328
+ (an empty held row left 59px of white under the hint at 1280).
325
329
  - While a panel is open the bar is frozen; if the "Limpar" that a first filter
326
330
  brings in does not fit the row it shows as its icon until the panel closes.
327
331
  - A second tap before the first swaps them — the boxes never show a period
@@ -13,6 +13,7 @@
13
13
  * a search — it is the bar's own {@link PillControl}; see `sharesBarControl`.
14
14
  */
15
15
  import Checkbox from "@mui/material/Checkbox/index.js";
16
+ import type { Theme } from "@mui/material/styles/index.js";
16
17
 
17
18
  import { Box } from "../../../mui/Box";
18
19
  import { Text } from "../../typography/Text";
@@ -227,6 +228,17 @@ export function fieldClearing<T extends Record<string, unknown>>({
227
228
  }
228
229
 
229
230
 
231
+ /**
232
+ * Under a shed quick chip: 12px below `sm`, the gap a following group's
233
+ * "Limpar" target reaches into (its 8px let the target cover the chip's
234
+ * label); 8px from `sm` up, as before.
235
+ */
236
+ const quickSx = (theme: Theme) => ({
237
+ mb: MORE_SPACING.underQuick,
238
+ [theme.breakpoints.up("sm")]: { mb: 1 },
239
+ "&:last-of-type": { mb: 0 },
240
+ });
241
+
230
242
  /**
231
243
  * A quick chip shed into the panel: its own line, because label and count are
232
244
  * already the whole control — no heading above it, no "Limpar" beside it.
@@ -240,7 +252,7 @@ export function MoreQuick({
240
252
  quick: QuickFilterConfig;
241
253
  } & Pick<MoreFieldProps, "pills" | "onToggleQuick" | "testIdPrefix">): React.JSX.Element {
242
254
  return (
243
- <Box sx={{ mb: 1, "&:last-of-type": { mb: 0 } }}>
255
+ <Box sx={quickSx}>
244
256
  <QuickRow
245
257
  quick={quick}
246
258
  active={isQuickActive(quick, pills)}
@@ -251,6 +263,67 @@ export function MoreQuick({
251
263
  );
252
264
  }
253
265
 
266
+ /**
267
+ * The "Mais" panel's vertical rhythm, in the theme's spacing units: the unit
268
+ * the header "Limpar"'s phone target reaches into (FUT-4144). The target is
269
+ * the button's 18px of text plus `reachUp` and `reachDown`, 40px at the
270
+ * default type scale, and it may never reach further than the gap that is
271
+ * there: under the group or quick chip above it (or the scroller's own top
272
+ * inset, for the first group) and under its own header. Sideways it reaches
273
+ * into the header row's empty gap and the scroller's padding, so a 37px
274
+ * "Limpar" is 41px wide.
275
+ */
276
+ export const MORE_SPACING = {
277
+ /** Under a group. */
278
+ underGroup: 1.5,
279
+ /** Under a shed quick chip, below `sm` (8px from `sm` up, as before). */
280
+ underQuick: 1.5,
281
+ /**
282
+ * The scroller's top inset (`data-views-more-filters.tsx`), at every width:
283
+ * the first group's overlay reaches into it. Not part of a responsive `p`,
284
+ * whose `xs` value is emitted as a media query and would win over it.
285
+ */
286
+ aboveFirst: 1.5,
287
+ /** Under a group's header, below `sm`, applied or not, so nothing moves on apply. */
288
+ underHeader: 1.25,
289
+ /** Under a group's header from `sm` up, as it always was. */
290
+ underHeaderFromSm: 0.5,
291
+ reachUp: 1.5,
292
+ reachDown: 1.25,
293
+ reachSide: 0.25,
294
+ } as const;
295
+
296
+ /** A group's header row: the field's name, and "Limpar" while it is applied. */
297
+ export const moreHeaderSx = (theme: Theme) => ({
298
+ display: "flex",
299
+ alignItems: "center",
300
+ gap: 1,
301
+ mb: MORE_SPACING.underHeader,
302
+ [theme.breakpoints.up("sm")]: { mb: MORE_SPACING.underHeaderFromSm },
303
+ });
304
+
305
+ /**
306
+ * "Limpar" is 12px text, a 37×18 button: on a phone that was the whole target
307
+ * (FUT-4144, measured in the admin at 320–900). Below `sm` an overlay makes it
308
+ * at least 40×40 without moving the text, reaching only into gaps
309
+ * {@link MORE_SPACING} guarantees, in the same unit, so it grows and shrinks
310
+ * with them and covers nothing else. From `sm` up there is no overlay and the
311
+ * button is exactly as it was.
312
+ */
313
+ export const clearTargetSx = (theme: Theme) => ({
314
+ position: "relative" as const,
315
+ [theme.breakpoints.up("sm")]: { position: "static" as const },
316
+ "&::before": {
317
+ content: '""',
318
+ position: "absolute" as const,
319
+ top: `-${theme.spacing(MORE_SPACING.reachUp)}`,
320
+ bottom: `-${theme.spacing(MORE_SPACING.reachDown)}`,
321
+ left: `-${theme.spacing(MORE_SPACING.reachSide)}`,
322
+ right: `-${theme.spacing(MORE_SPACING.reachSide)}`,
323
+ [theme.breakpoints.up("sm")]: { display: "none" },
324
+ },
325
+ });
326
+
254
327
  /** One labelled group in the panel: the field's name, then its control. */
255
328
  export function MoreGroup<T extends Record<string, unknown>>({
256
329
  field,
@@ -265,8 +338,8 @@ export function MoreGroup<T extends Record<string, unknown>>({
265
338
  const copy = useDataViewsCopy();
266
339
  const { applied, clear } = fieldClearing({ field, pills, ranges, onTogglePill, onChangeRange });
267
340
  return (
268
- <Box sx={{ mb: 1.5, "&:last-of-type": { mb: 0 } }}>
269
- <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}>
341
+ <Box sx={{ mb: MORE_SPACING.underGroup, "&:last-of-type": { mb: 0 } }}>
342
+ <Box sx={moreHeaderSx}>
270
343
  <Text variant="caption" as="span">
271
344
  <Box component="span" sx={{ color: "text.secondary" }}>
272
345
  {field.label}
@@ -283,17 +356,20 @@ export function MoreGroup<T extends Record<string, unknown>>({
283
356
  type="button"
284
357
  onClick={clear}
285
358
  data-testid={`${testIdPrefix}-more-${field.id}-clear`}
286
- sx={{
287
- ml: "auto",
288
- border: 0,
289
- p: 0,
290
- bgcolor: "transparent",
291
- cursor: "pointer",
292
- font: "inherit",
293
- fontSize: sxRem(12),
294
- color: "primary.main",
295
- "&:hover": { textDecoration: "underline" },
296
- }}
359
+ sx={[
360
+ {
361
+ ml: "auto",
362
+ border: 0,
363
+ p: 0,
364
+ bgcolor: "transparent",
365
+ cursor: "pointer",
366
+ font: "inherit",
367
+ fontSize: sxRem(12),
368
+ color: "primary.main",
369
+ "&:hover": { textDecoration: "underline" },
370
+ },
371
+ clearTargetSx,
372
+ ]}
297
373
  >
298
374
  {copy.filters.clear}
299
375
  </Box>
@@ -17,7 +17,7 @@ import { Button } from "../../form/Button";
17
17
  import { Box } from "../../../mui/Box";
18
18
  import { Text } from "../../typography/Text";
19
19
 
20
- import { fieldClearing, MoreGroup, MoreQuick, type MoreFieldProps } from "./data-views-more-fields";
20
+ import { fieldClearing, MORE_SPACING, MoreGroup, MoreQuick, type MoreFieldProps } from "./data-views-more-fields";
21
21
  import { useStillAnchor } from "./data-views-still-anchor";
22
22
  import type { OverflowField } from "./data-views-overflow";
23
23
  import { rem, remPx, sxRem } from "../../../tokens/relative";
@@ -322,7 +322,7 @@ export function MoreFilters<T extends Record<string, unknown>>({
322
322
  contents are told to shrink into it. A horizontal scrollbar here
323
323
  means something refused to, which is a bug to fix rather than a
324
324
  scrollbar to live with. */}
325
- <Box sx={{ flex: "1 1 auto", minHeight: 0, overflowY: "auto", overflowX: "hidden", p: { xs: 1, sm: 1.5 }, minWidth: 0 }}>
325
+ <Box sx={{ flex: "1 1 auto", minHeight: 0, overflowY: "auto", overflowX: "hidden", px: { xs: 1, sm: 1.5 }, pb: { xs: 1, sm: 1.5 }, pt: MORE_SPACING.aboveFirst, minWidth: 0 }}>
326
326
  <MoreFields key={clearKey} fields={fields} {...rest} />
327
327
  </Box>
328
328
  {(anyApplied || holdsDayRange) && (
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import Box from '@mui/material/Box/index.js';
10
10
  import Typography from '@mui/material/Typography/index.js';
11
- import React, { useState } from 'react';
11
+ import React, { useEffect, useRef, useState } from 'react';
12
12
 
13
13
  import { useDataViewsCopy } from './data-views-copy-context';
14
14
  import { DayBoundInput } from './data-views-day-input';
@@ -17,7 +17,7 @@ import { RangePresets } from './data-views-preset-chips';
17
17
  import { DayRangeCalendar } from './data-views-range-calendar';
18
18
  import { presetsFor } from './data-views-range-presets';
19
19
  import { useOutsideResetKey } from './data-views-range-reset';
20
- import { isRangeInverted } from './data-views-range-values';
20
+ import { isRangeInverted, isRangeSet, rangeChipLabel } from './data-views-range-values';
21
21
  import type { RangeFieldConfig, RangeValue } from './data-views-types';
22
22
  import { sxRem } from '../../../tokens/relative';
23
23
 
@@ -71,6 +71,51 @@ function BoundField<T extends Record<string, unknown>>({
71
71
  }
72
72
 
73
73
 
74
+ /**
75
+ * After the line's "Limpar" pressed from the keyboard, focus goes to the
76
+ * cleared "De" — the next thing to type — instead of falling to the page with
77
+ * the button that held it. A tap leaves focus alone: focusing "De" there would
78
+ * open the phone's keyboard over the calendar the next tap is for.
79
+ * The boxes remount on a clear, so focus waits for the reset key to move.
80
+ */
81
+ function useFocusDeAfterClear(
82
+ key: number,
83
+ testId: string,
84
+ ): { rootRef: React.RefObject<HTMLDivElement | null>; clearToDe: (clear: () => void, fromKeyboard: boolean) => void } {
85
+ const rootRef = useRef<HTMLDivElement>(null);
86
+ const pending = useRef(false);
87
+ useEffect(() => {
88
+ if (!pending.current) return;
89
+ pending.current = false;
90
+ rootRef.current?.querySelector<HTMLInputElement>(`[data-testid="${testId}-min"]`)?.focus();
91
+ }, [key, testId]);
92
+ return {
93
+ rootRef,
94
+ clearToDe: (clear, fromKeyboard) => {
95
+ pending.current = fromKeyboard;
96
+ clear();
97
+ },
98
+ };
99
+ }
100
+
101
+ /**
102
+ * The window and "Limpar" for the hint line, where the host asks for them.
103
+ * `clear` goes straight to the host, not through `emitted`: it is an outside
104
+ * write, so the boxes and the calendar re-open on the empty range.
105
+ */
106
+ function hintLineProps<T extends Record<string, unknown>>(
107
+ field: RangeFieldConfig<T>,
108
+ value: RangeValue,
109
+ clear: (fromKeyboard: boolean) => void,
110
+ ): { summary: string | undefined; onClear: (fromKeyboard: boolean) => void } {
111
+ return { summary: appliedWindow(field, value), onClear: clear };
112
+ }
113
+
114
+ /** The applied window in words ("Data: 01/10–15/10"), or nothing while none is. */
115
+ function appliedWindow<T extends Record<string, unknown>>(field: RangeFieldConfig<T>, value: RangeValue): string | undefined {
116
+ return isRangeSet(value) ? rangeChipLabel(field, value) : undefined;
117
+ }
118
+
74
119
  /** One box of the pair: an equal share of the row for a day, as it was for a number. */
75
120
  function BoundCell({ grow, children }: { grow: boolean; children: React.ReactNode }): React.JSX.Element {
76
121
  if (!grow) return <>{children}</>;
@@ -142,6 +187,11 @@ interface RangeBoundsProps<T extends Record<string, unknown>> {
142
187
  * its content and keeps the boxes' own width.
143
188
  */
144
189
  fillRow?: boolean;
190
+ /**
191
+ * Say the applied window, and its "Limpar", on the day calendar's hint line.
192
+ * The pill's popover sets it; a panel has them in its own header.
193
+ */
194
+ summaryOnHint?: boolean;
145
195
  }
146
196
 
147
197
  /**
@@ -157,9 +207,12 @@ export function RangeBounds<T extends Record<string, unknown>>({
157
207
  calendarMonths = 1,
158
208
  showCalendar = true,
159
209
  fillRow = false,
210
+ summaryOnHint = false,
160
211
  }: RangeBoundsProps<T>): React.JSX.Element {
161
212
  const copy = useDataViewsCopy();
162
213
  const { key, emitted } = useOutsideResetKey(value);
214
+ const { rootRef, clearToDe } = useFocusDeAfterClear(key, testId);
215
+ const hintLine = summaryOnHint ? hintLineProps(field, value, (fromKeyboard) => clearToDe(() => onChange({}), fromKeyboard)) : {};
163
216
  // Moves the calendar to a TYPED day. A typed write is the fields' own, so
164
217
  // the outside key above does not see it; a tap on the calendar moves
165
218
  // neither, which is what keeps it still under the finger.
@@ -170,7 +223,7 @@ export function RangeBounds<T extends Record<string, unknown>>({
170
223
  };
171
224
  const day = field.kind === 'day';
172
225
  return (
173
- <Box data-testid={`${testId}-panel`}>
226
+ <Box ref={rootRef} data-testid={`${testId}-panel`}>
174
227
  {/* Above the inputs, not below: the presets are the answer for most
175
228
  merchants, and the two date pickers are the escape hatch. */}
176
229
  <Box sx={{ '&:not(:empty)': { mb: { xs: 1, sm: 1.5 } } }}>
@@ -216,6 +269,7 @@ export function RangeBounds<T extends Record<string, unknown>>({
216
269
  months={calendarMonths}
217
270
  viewKey={`${key}-${typed}`}
218
271
  testId={testId}
272
+ {...hintLine}
219
273
  />
220
274
  )}
221
275
  </Box>
@@ -114,6 +114,107 @@ function openingMonth(value: RangeValue, months: number): Date | undefined {
114
114
  return new Date(anchor.getFullYear(), anchor.getMonth() - (months > 1 ? 1 : 0), 1);
115
115
  }
116
116
 
117
+ /**
118
+ * The line under the calendar: the next step ("Toque no primeiro e no último
119
+ * dia." / "Agora toque no último dia.") or, once a period is applied, the
120
+ * window, with "Limpar" at its end.
121
+ *
122
+ * ONE line whose words change, never a row held empty for later (FUT-4144
123
+ * follow-up): #932 kept an invisible summary row under the hint so the first
124
+ * tap would not grow the popover, and at 1280 that was 59px of white.
125
+ *
126
+ * Where the line can hold "Limpar" (the pill's popover) it is always as tall as
127
+ * its 40px target, so it keeps one height with the button and without it, and
128
+ * the popover does not move. Where it cannot (a panel, whose header has
129
+ * "Limpar") it stays a plain line of text: no row reserved for a button that
130
+ * never comes. The text keeps to one line, cut with an ellipsis and whole in
131
+ * its title, so a long label never grows the line on the closing tap. The target
132
+ * is its own box, not padding pushed out with a negative margin: that version
133
+ * ran under the calendar's last week, which took its top 10px at 320 (FUT-4144
134
+ * UI review). The window is said in the primary ink, the steps in the
135
+ * secondary one, so the result does not read as one more instruction.
136
+ */
137
+ function HintLine({
138
+ text,
139
+ isWindow,
140
+ canClear,
141
+ onClear,
142
+ testId,
143
+ }: {
144
+ text: string;
145
+ /** `text` is the applied window, not a step. */
146
+ isWindow: boolean;
147
+ /** The line can hold "Limpar" at all — fixed per surface, not per state. */
148
+ canClear: boolean;
149
+ /** Shown now: "Limpar" empties the range; `fromKeyboard` is a click with `detail` 0 (Enter or Space). */
150
+ onClear: ((fromKeyboard: boolean) => void) | undefined;
151
+ testId: string;
152
+ }): React.JSX.Element {
153
+ const clear = useDataViewsCopy().filters.clear;
154
+ return (
155
+ <Box
156
+ data-testid={`${testId}-calendar-line`}
157
+ sx={{
158
+ display: 'flex',
159
+ alignItems: 'center',
160
+ justifyContent: 'space-between',
161
+ gap: 2,
162
+ mt: { xs: 0.25, sm: 1 },
163
+ minHeight: canClear ? sxRem(40) : undefined,
164
+ fontSize: sxRem(12),
165
+ lineHeight: 1.5,
166
+ }}
167
+ >
168
+ <Typography
169
+ component="p"
170
+ aria-live="polite"
171
+ data-testid={`${testId}-calendar-hint`}
172
+ title={text}
173
+ noWrap
174
+ sx={{ m: 0, minWidth: 0, font: 'inherit', color: isWindow ? 'text.primary' : 'text.secondary' }}
175
+ >
176
+ {text}
177
+ </Typography>
178
+ {onClear && (
179
+ <Box
180
+ component="button"
181
+ type="button"
182
+ onClick={(event) => onClear(event.detail === 0)}
183
+ data-testid={`${testId}-clear`}
184
+ sx={{
185
+ flexShrink: 0,
186
+ display: 'inline-flex',
187
+ alignItems: 'center',
188
+ minHeight: sxRem(40),
189
+ border: 0,
190
+ bgcolor: 'transparent',
191
+ cursor: 'pointer',
192
+ font: 'inherit',
193
+ fontWeight: 600,
194
+ color: 'primary.main',
195
+ px: 1,
196
+ py: 0,
197
+ // Its text lines up with the boxes' right edge; the side padding
198
+ // reaches into the popover's own.
199
+ mr: -1,
200
+ borderRadius: 1,
201
+ '&:hover': { bgcolor: 'action.hover' },
202
+ // The theme's focus ring for a keyboard, none for a tap.
203
+ '&:focus': { outline: 'none' },
204
+ '&:focus-visible': {
205
+ outline: (theme: Theme) => `${rem(theme, 2)} solid`,
206
+ outlineColor: 'primary.main',
207
+ outlineOffset: sxRem(1),
208
+ },
209
+ }}
210
+ >
211
+ {clear}
212
+ </Box>
213
+ )}
214
+ </Box>
215
+ );
216
+ }
217
+
117
218
  /** Props of {@link DayRangeCalendar}. */
118
219
  export interface DayRangeCalendarProps {
119
220
  /** The filter's own label ("Data"), which names the calendar. */
@@ -133,6 +234,18 @@ export interface DayRangeCalendarProps {
133
234
  */
134
235
  viewKey: string;
135
236
  testId: string;
237
+ /**
238
+ * The applied window in words ("Data: 01/10–15/10"), said on the hint's own
239
+ * line with "Limpar" at its end. Set by the pill, whose popover has no other
240
+ * place for them; a panel says them in its own header and leaves this unset.
241
+ */
242
+ summary?: string;
243
+ /**
244
+ * Empties the range: the line's "Limpar", shown while `summary` is set. Set
245
+ * only where the line may hold it; the line reserves its 40px only then.
246
+ * `fromKeyboard` is true when it was pressed from the keyboard.
247
+ */
248
+ onClear?: (fromKeyboard: boolean) => void;
136
249
  }
137
250
 
138
251
  export function DayRangeCalendar({
@@ -142,6 +255,8 @@ export function DayRangeCalendar({
142
255
  months,
143
256
  viewKey,
144
257
  testId,
258
+ summary,
259
+ onClear,
145
260
  }: DayRangeCalendarProps): React.JSX.Element {
146
261
  const copy: DataViewsDayCalendarCopy =
147
262
  useDataViewsCopy().filters.dayCalendar ?? (EN_US_DATA_VIEWS_COPY.filters.dayCalendar as DataViewsDayCalendarCopy);
@@ -172,14 +287,13 @@ export function DayRangeCalendar({
172
287
  // tap that CLOSES a period, so the first tap would write nothing.
173
288
  onIntermediateRangeChange={pick}
174
289
  />
175
- <Typography
176
- component="p"
177
- aria-live="polite"
178
- data-testid={`${testId}-calendar-hint`}
179
- sx={{ mt: { xs: 0.25, sm: 1 }, fontSize: sxRem(12), color: 'text.secondary' }}
180
- >
181
- {waitingForEnd ? copy.pickEnd : copy.pickStart}
182
- </Typography>
290
+ <HintLine
291
+ text={waitingForEnd ? copy.pickEnd : (summary ?? copy.pickStart)}
292
+ isWindow={!waitingForEnd && summary !== undefined}
293
+ canClear={onClear !== undefined}
294
+ onClear={summary === undefined ? undefined : onClear}
295
+ testId={testId}
296
+ />
183
297
  </Box>
184
298
  );
185
299
  }
@@ -180,6 +180,10 @@ const DAY_PAPER_SX = (theme: Theme) => ({
180
180
  // 280px seven 40px day columns need: at 320 it had 256 and every day was a
181
181
  // 33px target (FUT-4144 UI review).
182
182
  maxWidth: { xs: `calc(100% - ${rem(theme, 16)})`, sm: `calc(100% - ${rem(theme, 32)})` },
183
+ // And as tall as those same margins allow: MUI's default keeps 16px off the
184
+ // top and bottom, so a refused day's message at 320×568 made the popover
185
+ // 1px taller than its cap and it scrolled (FUT-4144 code review).
186
+ maxHeight: { xs: `calc(100% - ${rem(theme, 16)})`, sm: `calc(100% - ${rem(theme, 32)})` },
183
187
  p: { xs: 1, sm: 2 },
184
188
  });
185
189
 
@@ -232,6 +236,7 @@ export function RangePill<T extends Record<string, unknown>>({
232
236
  onChange={onChange}
233
237
  testId={testId}
234
238
  calendarMonths={calendarMonths}
239
+ summaryOnHint
235
240
  />
236
241
  <RangeSummary field={field} value={value} onClear={() => onChange({})} testId={testId} />
237
242
  </Popover>
@@ -11,16 +11,15 @@ import type { RangeFieldConfig, RangeValue } from './data-views-types';
11
11
  import { sxRem } from '../../../tokens/relative';
12
12
 
13
13
  /**
14
- * The applied window in words, and "Limpar".
14
+ * The applied window in words, and "Limpar", under a NUMBER range's boxes.
15
15
  *
16
16
  * An inverted window is not an error the merchant must fix before the list
17
17
  * responds — it simply matches nothing, and saying so beats an empty grid with
18
18
  * no explanation (FUT-668).
19
19
  *
20
- * A DAY filter keeps the row's height while nothing is applied: its first tap
21
- * on the calendar applies a bound, and a row appearing then grew the popover
22
- * 26px and moved it 32px up at 320×568, the day under the finger with it
23
- * (FUT-4144 UI review round 2). The held row is invisible and inert.
20
+ * A DAY range says these on its calendar's hint line instead (FUT-4144): a row
21
+ * of its own, held empty until the first tap so the popover would not grow,
22
+ * left 59px of white under the hint at 1280.
24
23
  */
25
24
  export function RangeSummary<T extends Record<string, unknown>>({
26
25
  field,
@@ -34,29 +33,17 @@ export function RangeSummary<T extends Record<string, unknown>>({
34
33
  testId: string;
35
34
  }): React.JSX.Element | null {
36
35
  const copy = useDataViewsCopy();
37
- const active = isRangeSet(value);
38
- if (!active && field.kind !== 'day') return null;
36
+ if (field.kind === 'day' || !isRangeSet(value)) return null;
39
37
  return (
40
- <Box
41
- aria-hidden={active ? undefined : true}
42
- sx={{
43
- display: 'flex',
44
- alignItems: 'center',
45
- justifyContent: 'space-between',
46
- gap: 2,
47
- mt: { xs: 0.5, sm: 1.5 },
48
- visibility: active ? undefined : 'hidden',
49
- }}
50
- >
38
+ <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 2, mt: { xs: 0.5, sm: 1.5 } }}>
51
39
  <Typography component="span" sx={{ fontSize: sxRem(12), color: 'text.secondary' }}>
52
- {active ? rangeChipLabel(field, value) : field.label}
40
+ {rangeChipLabel(field, value)}
53
41
  </Typography>
54
42
  <Button
55
43
  variant="text"
56
44
  size="small"
57
- onClick={active ? onClear : undefined}
58
- tabIndex={active ? undefined : -1}
59
- data-testid={active ? `${testId}-clear` : undefined}
45
+ onClick={onClear}
46
+ data-testid={`${testId}-clear`}
60
47
  sx={{ textTransform: 'none' }}
61
48
  >
62
49
  {copy.filters.clear}
@@ -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 useMediaQuery from '@mui/material/useMediaQuery/index.js';\nimport { useTheme, type Theme } from '@mui/material/styles/index.js';\nimport React, { useContext } from 'react';\n\nimport { useDataViewsCopy } from \"./data-views-copy-context\";\nimport { TableFilter } from '../../layout/TableFilter';\nimport { TableFilterContext } from '../../layout/TableFilter/TableFilterContext';\nimport { RangePresets } from './data-views-preset-chips';\nimport { RangeBounds } from './data-views-range-bounds';\nimport { RangeSummary } from './data-views-range-summary';\nimport { useStillAnchor } from './data-views-still-anchor';\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 { rem, remPx, sxRem } from '../../../tokens/relative';\n\n// Re-exported: the pair moved to its own file, and its importers keep their path.\nexport { RangeBounds };\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\n/**\n * How many months the pill's calendar shows: two from the theme's `md` up,\n * where the popover has the ~670px they take; one below it.\n *\n * A media query, not an `sx` breakpoint, because it decides what to RENDER —\n * CSS cannot un-render a second month. `up('md')` so that where nothing can be\n * measured (a test, the server) the answer is the one month that always fits.\n */\nfunction useCalendarMonths(): 1 | 2 {\n const theme = useTheme();\n return useMediaQuery(theme.breakpoints.up('md')) ? 2 : 1;\n}\n\n/**\n * The day popover's width. Below `sm` it takes the screen's (less the\n * popover's own 16px margins, which its `maxWidth` already keeps), so the boxes\n * and the month share it instead of leaving a strip of grid beside a narrow\n * card. From `sm` to `md` it holds one month, and sized to its content the\n * preset row alone stretched it to ~515px with the month hugging the left and\n * a band of nothing beside it; 400 keeps the month and the boxes one width.\n * From `md` its content — two months — sets it.\n */\nconst DAY_PAPER_SX = (theme: Theme) => ({\n width: { xs: '100%', sm: rem(theme, 400), md: 'auto' },\n // Below `sm` 8px from each edge and 8px of padding, so the month gets the\n // 280px seven 40px day columns need: at 320 it had 256 and every day was a\n // 33px target (FUT-4144 UI review).\n maxWidth: { xs: `calc(100% - ${rem(theme, 16)})`, sm: `calc(100% - ${rem(theme, 32)})` },\n p: { xs: 1, sm: 2 },\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 still = useStillAnchor();\n const testId = `${testIdPrefix}-range-${field.id}`;\n const calendarMonths = useCalendarMonths();\n const theme = useTheme();\n const phone = useMediaQuery(theme.breakpoints.down('sm'));\n return (\n <>\n <RangeTrigger\n field={field}\n value={value}\n onChange={onChange}\n onOpen={(element) => {\n still.open(element);\n onOpenChange?.(true);\n }}\n open={Boolean(still.anchor)}\n testId={testId}\n />\n <Popover\n {...still.popoverProps}\n onClose={() => {\n still.close();\n onOpenChange?.(false);\n }}\n // Below `sm` a day popover keeps 8px from the screen's edges instead of 16.\n marginThreshold={field.kind === 'day' && phone ? remPx(theme, 8) : undefined}\n slotProps={{ paper: { ref: still.paperRef, sx: [{ p: 2, mt: 0.5 }, field.kind === 'day' && DAY_PAPER_SX] } }}\n >\n <RangeBounds\n field={field}\n value={value}\n onChange={onChange}\n testId={testId}\n calendarMonths={calendarMonths}\n />\n <RangeSummary field={field} value={value} onClear={() => onChange({})} testId={testId} />\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 // The slide-in panel stays mounted while closed; its calendar does not.\n // Outside a `TableFilter` (the \"Mais\" sheet, a test) the pair is on screen.\n const panel = useContext(TableFilterContext);\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\n field={field}\n value={value}\n onChange={onChange}\n testId={testId}\n showCalendar={panel ? panel.open : true}\n />\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":"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","names":[]}