@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.
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +10 -10
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +11 -11
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +13 -7
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-range-calendar.js +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-range-summary.js +1 -1
- package/dist/{chunk-W2KOP27S.js → chunk-3I5PHWKQ.js} +2 -2
- package/dist/{chunk-3OHZAZEE.js → chunk-3WYEENUH.js} +9 -4
- package/dist/chunk-3WYEENUH.js.map +1 -0
- package/dist/{chunk-R4LCPPKA.js → chunk-CNUO2BRV.js} +2 -2
- package/dist/{chunk-MZAYIG2I.js → chunk-D2JQF2WC.js} +66 -16
- package/dist/chunk-D2JQF2WC.js.map +1 -0
- package/dist/{chunk-HS662RLW.js → chunk-DWH4TQXK.js} +4 -3
- package/dist/chunk-DWH4TQXK.js.map +1 -0
- package/dist/{chunk-FORAVDJF.js → chunk-FC7UCFFA.js} +35 -7
- package/dist/chunk-FC7UCFFA.js.map +1 -0
- package/dist/{chunk-CKQOYPH3.js → chunk-FYUNROLW.js} +2 -2
- package/dist/{chunk-MICX23P4.js → chunk-HRES2S3Q.js} +85 -8
- package/dist/chunk-HRES2S3Q.js.map +1 -0
- package/dist/{chunk-X3GD3TFI.js → chunk-KQDBOZQE.js} +3 -3
- package/dist/{chunk-AI7XWBNG.js → chunk-QE4KYG5I.js} +3 -3
- package/dist/chunk-WLDP7C4K.js +44 -0
- package/dist/chunk-WLDP7C4K.js.map +1 -0
- package/dist/data-display/DataViews.js +11 -11
- package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts +80 -0
- package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-bounds.d.ts +6 -1
- package/dist/types/components/data-display/DataViews/data-views-range-bounds.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-calendar.d.ts +13 -1
- package/dist/types/components/data-display/DataViews/data-views-range-calendar.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-summary.d.ts +4 -5
- package/dist/types/components/data-display/DataViews/data-views-range-summary.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/DataViews/RESPONSIVE-BEHAVIOUR.md +8 -4
- package/src/components/data-display/DataViews/data-views-more-fields.tsx +90 -14
- package/src/components/data-display/DataViews/data-views-more-filters.tsx +2 -2
- package/src/components/data-display/DataViews/data-views-range-bounds.tsx +57 -3
- package/src/components/data-display/DataViews/data-views-range-calendar.tsx +122 -8
- package/src/components/data-display/DataViews/data-views-range-pill.tsx +5 -0
- package/src/components/data-display/DataViews/data-views-range-summary.tsx +9 -22
- package/dist/chunk-3OHZAZEE.js.map +0 -1
- package/dist/chunk-FORAVDJF.js.map +0 -1
- package/dist/chunk-HS662RLW.js.map +0 -1
- package/dist/chunk-J2AVWIMM.js +0 -60
- package/dist/chunk-J2AVWIMM.js.map +0 -1
- package/dist/chunk-MICX23P4.js.map +0 -1
- package/dist/chunk-MZAYIG2I.js.map +0 -1
- /package/dist/{chunk-W2KOP27S.js.map → chunk-3I5PHWKQ.js.map} +0 -0
- /package/dist/{chunk-R4LCPPKA.js.map → chunk-CNUO2BRV.js.map} +0 -0
- /package/dist/{chunk-CKQOYPH3.js.map → chunk-FYUNROLW.js.map} +0 -0
- /package/dist/{chunk-X3GD3TFI.js.map → chunk-KQDBOZQE.js.map} +0 -0
- /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;
|
|
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;
|
|
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
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
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
|
@@ -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
|
|
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."),
|
|
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
|
|
324
|
-
|
|
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={
|
|
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:
|
|
269
|
-
<Box sx={
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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",
|
|
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
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
-
|
|
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
|
-
{
|
|
40
|
+
{rangeChipLabel(field, value)}
|
|
53
41
|
</Typography>
|
|
54
42
|
<Button
|
|
55
43
|
variant="text"
|
|
56
44
|
size="small"
|
|
57
|
-
onClick={
|
|
58
|
-
|
|
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":[]}
|