@12-apps/ui 6.91.0 → 6.92.1
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/components-catalog.md +2 -2
- package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
- package/dist/_internal/components/data-display/AsyncStateContainer/AsyncStateContainer.js +3 -3
- package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
- package/dist/_internal/components/data-display/CardKit/card-actions-context.js +3 -3
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +4 -4
- package/dist/_internal/components/data-display/Chart/Chart.js +3 -3
- package/dist/_internal/components/data-display/Chart/SpecChart.js +5 -5
- package/dist/_internal/components/data-display/Chart/chart-renderers.js +2 -2
- package/dist/_internal/components/data-display/Chip/Chip.js +3 -3
- package/dist/_internal/components/data-display/Chip/Chip.styles.js +2 -2
- package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +20 -20
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +26 -26
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +17 -17
- 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 +5 -5
- 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/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +1 -1
- package/dist/_internal/components/data-display/DayRuler/DayRuler.en-US.js +1 -1
- package/dist/_internal/components/data-display/DayRuler/DayRuler.js +5 -5
- package/dist/_internal/components/data-display/DayRuler/DayRuler.labels.js +3 -3
- package/dist/_internal/components/data-display/DayRuler/DayRuler.parts.js +9 -4
- package/dist/_internal/components/data-display/DayRuler/DayRuler.pt-BR.js +1 -1
- package/dist/_internal/components/data-display/DayRuler/DayRuler.styles.js +9 -1
- package/dist/_internal/components/data-display/EmptyState/EmptyState.js +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +5 -5
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +4 -4
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +9 -9
- package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapLayers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +3 -3
- package/dist/_internal/components/data-display/RegionMap/RegionMap.js +2 -2
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +4 -3
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.parts.js +3 -2
- package/dist/_internal/components/data-display/ScheduleGrid/schedule-header-host.styles.js +12 -0
- package/dist/_internal/components/data-display/ScheduleGrid/schedule-header-host.styles.js.map +1 -0
- package/dist/_internal/components/data-display/SectionOnboarding/SectionOnboarding.js +3 -3
- package/dist/_internal/components/data-display/index.js +42 -41
- package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +5 -5
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.overview.js +4 -4
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +3 -3
- package/dist/_internal/components/feedback/index.js +6 -6
- package/dist/_internal/components/index.js +60 -59
- package/dist/_internal/components/layout/SettingCard/SettingCard.js +8 -8
- package/dist/_internal/components/layout/SettingCard/SettingCard.parts.js +4 -4
- package/dist/_internal/components/layout/SettingCard/SettingCard.summary.js +7 -7
- package/dist/_internal/components/layout/SettingCard/SettingGrid.js +5 -5
- package/dist/_internal/components/layout/SettingCard/SettingGroup.js +9 -9
- package/dist/_internal/components/layout/SettingCard/SettingSwitch.js +8 -8
- package/dist/_internal/components/layout/SettingCard/SettingToggle.js +9 -9
- package/dist/_internal/components/layout/index.js +12 -12
- package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +6 -6
- package/dist/_internal/components/navigation/SectionNav/SectionNav.js +9 -9
- package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +3 -3
- package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +4 -4
- package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +5 -5
- package/dist/_internal/components/navigation/SectionNav/SectionNav.railCreate.js +4 -4
- package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +4 -4
- package/dist/_internal/components/navigation/index.js +9 -9
- package/dist/charts.js +6 -6
- package/dist/{chunk-HC6OZ7QK.js → chunk-3EIRRLCQ.js} +2 -2
- package/dist/{chunk-U2AEM3K4.js → chunk-3PQL2EN5.js} +2 -2
- package/dist/{chunk-7NP2JHGM.js → chunk-3WYEENUH.js} +11 -6
- package/dist/chunk-3WYEENUH.js.map +1 -0
- package/dist/{chunk-M3XJVD5G.js → chunk-5357QUVE.js} +2 -2
- package/dist/{chunk-Z7T4EFEC.js → chunk-5I75WRXT.js} +4 -4
- package/dist/chunk-5MXVKZKZ.js +30 -0
- package/dist/chunk-5MXVKZKZ.js.map +1 -0
- package/dist/{chunk-5E2CRRD4.js → chunk-66VKX6JE.js} +5 -5
- package/dist/{chunk-N5PVD4J5.js → chunk-6DO672AG.js} +7 -7
- package/dist/{chunk-76DX2IMH.js → chunk-7F6D73NL.js} +2 -2
- package/dist/{chunk-SNWCT6JN.js → chunk-AMIUZRDQ.js} +3 -3
- package/dist/{chunk-A3DDEKLV.js → chunk-BOADHYTA.js} +2 -2
- package/dist/{chunk-JKZPGCN7.js → chunk-BXZMV6QC.js} +12 -12
- package/dist/{chunk-JH2XLVWM.js → chunk-CW5Z67NA.js} +2 -2
- package/dist/{chunk-3JZ3KEQQ.js → chunk-CZVAVX5L.js} +4 -4
- package/dist/{chunk-O3OU3ED7.js → chunk-D7R6SRJB.js} +3 -3
- package/dist/{chunk-O3OU3ED7.js.map → chunk-D7R6SRJB.js.map} +1 -1
- package/dist/{chunk-DVX4HDNP.js → chunk-DBPFKGXX.js} +2 -2
- package/dist/{chunk-QN7FVZQD.js → chunk-DHAXUVMR.js} +4 -4
- package/dist/{chunk-XCUHGPPZ.js → chunk-DTZOP4ZR.js} +2 -2
- package/dist/{chunk-BLZDSCIZ.js → chunk-DYHOBKPU.js} +6 -6
- package/dist/{chunk-MX2RF34M.js → chunk-E73C73UD.js} +15 -7
- package/dist/chunk-E73C73UD.js.map +1 -0
- package/dist/{chunk-42D7KBDH.js → chunk-EOIRU62W.js} +2 -2
- package/dist/{chunk-HNW7IZPV.js → chunk-EZBBLLEU.js} +5 -5
- package/dist/{chunk-HNW7IZPV.js.map → chunk-EZBBLLEU.js.map} +1 -1
- package/dist/{chunk-K3IB7S7Q.js → chunk-F2ZUUEXC.js} +3 -2
- package/dist/{chunk-K3IB7S7Q.js.map → chunk-F2ZUUEXC.js.map} +1 -1
- package/dist/{chunk-2M3W6ACW.js → chunk-FAUNQ3FS.js} +3 -3
- package/dist/{chunk-FORAVDJF.js → chunk-FC7UCFFA.js} +35 -7
- package/dist/chunk-FC7UCFFA.js.map +1 -0
- package/dist/{chunk-PJNIJIE6.js → chunk-FQNDJITD.js} +2 -2
- package/dist/{chunk-ZIHSCBMN.js → chunk-FWTVIRZZ.js} +4 -4
- package/dist/{chunk-L3PDY7CJ.js → chunk-FYUNROLW.js} +2 -2
- package/dist/{chunk-KJKD6Y6V.js → chunk-GBB23QLL.js} +3 -3
- package/dist/{chunk-MICX23P4.js → chunk-HRES2S3Q.js} +85 -8
- package/dist/chunk-HRES2S3Q.js.map +1 -0
- package/dist/{chunk-XILMDEGZ.js → chunk-IEUVP6UV.js} +3 -3
- package/dist/{chunk-3IDL7NK7.js → chunk-K2VDRODL.js} +2 -2
- package/dist/{chunk-K3PMMW23.js → chunk-K6VEN5VN.js} +4 -4
- package/dist/{chunk-UKUGSU3Y.js → chunk-L4V7FZJI.js} +2 -2
- package/dist/{chunk-24HHUCWB.js → chunk-LDCZPZPD.js} +2 -2
- package/dist/{chunk-42TOCPXB.js → chunk-LXIPBHNJ.js} +5 -5
- package/dist/{chunk-ILJ3FXIC.js → chunk-MM3QESLI.js} +2 -2
- package/dist/{chunk-PGANINQD.js → chunk-NI67M453.js} +5 -5
- package/dist/{chunk-JN2IANXD.js → chunk-OCGPOACB.js} +2 -2
- package/dist/{chunk-RDCIST7P.js → chunk-OCT5VCLP.js} +2 -2
- package/dist/{chunk-G2PVUPE7.js → chunk-OJZLFFG4.js} +41 -3
- package/dist/chunk-OJZLFFG4.js.map +1 -0
- package/dist/{chunk-F7LBTMCA.js → chunk-P53ANKZV.js} +4 -4
- package/dist/{chunk-HMEGQKNQ.js → chunk-PCQ5JWLF.js} +15 -7
- package/dist/chunk-PCQ5JWLF.js.map +1 -0
- package/dist/{chunk-QVDEGBME.js → chunk-PEAUGQKU.js} +4 -4
- package/dist/{chunk-HS662RLW.js → chunk-RZWU2KYP.js} +2 -2
- package/dist/{chunk-U2QXQ7D4.js → chunk-S2VZUBM7.js} +4 -4
- package/dist/{chunk-HOPRZEBM.js → chunk-SKIAWOUC.js} +3 -3
- package/dist/{chunk-WCF56ZCD.js → chunk-TMINUTQQ.js} +2 -2
- package/dist/{chunk-ACM3HHZY.js → chunk-UDBOGSFO.js} +68 -2
- package/dist/chunk-UDBOGSFO.js.map +1 -0
- package/dist/{chunk-MZAYIG2I.js → chunk-VCJ72ODU.js} +2 -2
- package/dist/{chunk-YGQM6GFQ.js → chunk-VHE7UEG5.js} +2 -2
- package/dist/{chunk-QHIWV55K.js → chunk-VWRXSBT2.js} +2 -2
- package/dist/chunk-WLDP7C4K.js +44 -0
- package/dist/chunk-WLDP7C4K.js.map +1 -0
- package/dist/{chunk-TSEBMVYY.js → chunk-X2GWR75Q.js} +3 -3
- package/dist/{chunk-NKEFH3RS.js → chunk-XNAI7QGU.js} +2 -2
- package/dist/{chunk-L6JAALMX.js → chunk-XRV4QFEQ.js} +3 -3
- package/dist/{chunk-WH3LUOCQ.js → chunk-YE7M7URT.js} +2 -2
- package/dist/{chunk-DA3D5X5Z.js → chunk-YVDNCV2D.js} +6 -6
- package/dist/{chunk-G3JFESKD.js → chunk-Z5WUSI47.js} +6 -6
- package/dist/{chunk-5HADIIWN.js → chunk-ZDAAHCIS.js} +3 -3
- package/dist/{chunk-D3OXTS7Y.js → chunk-ZHCZOPKE.js} +3 -2
- package/dist/{chunk-D3OXTS7Y.js.map → chunk-ZHCZOPKE.js.map} +1 -1
- package/dist/{chunk-4KWDNGMA.js → chunk-ZVHHFLYY.js} +3 -3
- package/dist/data-display/Alert.js +2 -2
- package/dist/data-display/AsyncStateContainer.js +3 -3
- package/dist/data-display/Badge.js +2 -2
- package/dist/data-display/CardKit.js +7 -7
- package/dist/data-display/Chart.js +6 -6
- package/dist/data-display/Chip.js +3 -3
- package/dist/data-display/DataGrid.js +2 -2
- package/dist/data-display/DataViews.js +35 -35
- package/dist/data-display/DayRuler.js +7 -7
- package/dist/data-display/EmptyState.js +2 -2
- package/dist/data-display/FleetMap.js +9 -9
- package/dist/data-display/InteractiveTooltip.js +2 -2
- package/dist/data-display/Lightbox.js +2 -2
- package/dist/data-display/MapPreview.js +3 -3
- package/dist/data-display/RegionMap.js +2 -2
- package/dist/data-display/ScheduleGrid.js +4 -3
- package/dist/data-display/SectionOnboarding.js +3 -3
- package/dist/feedback/NotificationStatusNotice.js +3 -3
- package/dist/feedback/SequenceResolver.js +5 -5
- package/dist/layout/SettingCard.js +12 -12
- package/dist/navigation/SectionNav.js +9 -9
- 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/dist/types/components/data-display/DayRuler/DayRuler.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.en-US.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.labels.d.ts +2 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.labels.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts +13 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.pt-BR.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts +16 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts +44 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/index.d.ts +1 -1
- package/dist/types/components/data-display/DayRuler/index.d.ts.map +1 -1
- package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.parts.d.ts +3 -1
- package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.parts.d.ts.map +1 -1
- package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.types.d.ts +12 -0
- package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.types.d.ts.map +1 -1
- package/dist/types/components/data-display/ScheduleGrid/schedule-header-host.styles.d.ts +16 -0
- package/dist/types/components/data-display/ScheduleGrid/schedule-header-host.styles.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/data-display/DataViews/RESPONSIVE-BEHAVIOUR.md +8 -4
- 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/src/components/data-display/DayRuler/DayRuler.en-US.ts +1 -0
- package/src/components/data-display/DayRuler/DayRuler.labels.ts +2 -0
- package/src/components/data-display/DayRuler/DayRuler.md +3 -2
- package/src/components/data-display/DayRuler/DayRuler.parts.tsx +120 -1
- package/src/components/data-display/DayRuler/DayRuler.pt-BR.ts +1 -0
- package/src/components/data-display/DayRuler/DayRuler.styles.ts +50 -1
- package/src/components/data-display/DayRuler/DayRuler.tsx +10 -1
- package/src/components/data-display/DayRuler/DayRuler.types.ts +30 -1
- package/src/components/data-display/DayRuler/index.ts +2 -0
- package/src/components/data-display/ScheduleGrid/ScheduleGrid.md +3 -2
- package/src/components/data-display/ScheduleGrid/ScheduleGrid.parts.tsx +18 -3
- package/src/components/data-display/ScheduleGrid/ScheduleGrid.tsx +1 -1
- package/src/components/data-display/ScheduleGrid/ScheduleGrid.types.ts +12 -0
- package/src/components/data-display/ScheduleGrid/schedule-header-host.styles.ts +35 -0
- package/dist/chunk-7NP2JHGM.js.map +0 -1
- package/dist/chunk-ACM3HHZY.js.map +0 -1
- package/dist/chunk-FORAVDJF.js.map +0 -1
- package/dist/chunk-G2PVUPE7.js.map +0 -1
- package/dist/chunk-HMEGQKNQ.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-MX2RF34M.js.map +0 -1
- /package/dist/{chunk-HC6OZ7QK.js.map → chunk-3EIRRLCQ.js.map} +0 -0
- /package/dist/{chunk-U2AEM3K4.js.map → chunk-3PQL2EN5.js.map} +0 -0
- /package/dist/{chunk-M3XJVD5G.js.map → chunk-5357QUVE.js.map} +0 -0
- /package/dist/{chunk-Z7T4EFEC.js.map → chunk-5I75WRXT.js.map} +0 -0
- /package/dist/{chunk-5E2CRRD4.js.map → chunk-66VKX6JE.js.map} +0 -0
- /package/dist/{chunk-N5PVD4J5.js.map → chunk-6DO672AG.js.map} +0 -0
- /package/dist/{chunk-76DX2IMH.js.map → chunk-7F6D73NL.js.map} +0 -0
- /package/dist/{chunk-SNWCT6JN.js.map → chunk-AMIUZRDQ.js.map} +0 -0
- /package/dist/{chunk-A3DDEKLV.js.map → chunk-BOADHYTA.js.map} +0 -0
- /package/dist/{chunk-JKZPGCN7.js.map → chunk-BXZMV6QC.js.map} +0 -0
- /package/dist/{chunk-JH2XLVWM.js.map → chunk-CW5Z67NA.js.map} +0 -0
- /package/dist/{chunk-3JZ3KEQQ.js.map → chunk-CZVAVX5L.js.map} +0 -0
- /package/dist/{chunk-DVX4HDNP.js.map → chunk-DBPFKGXX.js.map} +0 -0
- /package/dist/{chunk-QN7FVZQD.js.map → chunk-DHAXUVMR.js.map} +0 -0
- /package/dist/{chunk-XCUHGPPZ.js.map → chunk-DTZOP4ZR.js.map} +0 -0
- /package/dist/{chunk-BLZDSCIZ.js.map → chunk-DYHOBKPU.js.map} +0 -0
- /package/dist/{chunk-42D7KBDH.js.map → chunk-EOIRU62W.js.map} +0 -0
- /package/dist/{chunk-2M3W6ACW.js.map → chunk-FAUNQ3FS.js.map} +0 -0
- /package/dist/{chunk-PJNIJIE6.js.map → chunk-FQNDJITD.js.map} +0 -0
- /package/dist/{chunk-ZIHSCBMN.js.map → chunk-FWTVIRZZ.js.map} +0 -0
- /package/dist/{chunk-L3PDY7CJ.js.map → chunk-FYUNROLW.js.map} +0 -0
- /package/dist/{chunk-KJKD6Y6V.js.map → chunk-GBB23QLL.js.map} +0 -0
- /package/dist/{chunk-XILMDEGZ.js.map → chunk-IEUVP6UV.js.map} +0 -0
- /package/dist/{chunk-3IDL7NK7.js.map → chunk-K2VDRODL.js.map} +0 -0
- /package/dist/{chunk-K3PMMW23.js.map → chunk-K6VEN5VN.js.map} +0 -0
- /package/dist/{chunk-UKUGSU3Y.js.map → chunk-L4V7FZJI.js.map} +0 -0
- /package/dist/{chunk-24HHUCWB.js.map → chunk-LDCZPZPD.js.map} +0 -0
- /package/dist/{chunk-42TOCPXB.js.map → chunk-LXIPBHNJ.js.map} +0 -0
- /package/dist/{chunk-ILJ3FXIC.js.map → chunk-MM3QESLI.js.map} +0 -0
- /package/dist/{chunk-PGANINQD.js.map → chunk-NI67M453.js.map} +0 -0
- /package/dist/{chunk-JN2IANXD.js.map → chunk-OCGPOACB.js.map} +0 -0
- /package/dist/{chunk-RDCIST7P.js.map → chunk-OCT5VCLP.js.map} +0 -0
- /package/dist/{chunk-F7LBTMCA.js.map → chunk-P53ANKZV.js.map} +0 -0
- /package/dist/{chunk-QVDEGBME.js.map → chunk-PEAUGQKU.js.map} +0 -0
- /package/dist/{chunk-HS662RLW.js.map → chunk-RZWU2KYP.js.map} +0 -0
- /package/dist/{chunk-U2QXQ7D4.js.map → chunk-S2VZUBM7.js.map} +0 -0
- /package/dist/{chunk-HOPRZEBM.js.map → chunk-SKIAWOUC.js.map} +0 -0
- /package/dist/{chunk-WCF56ZCD.js.map → chunk-TMINUTQQ.js.map} +0 -0
- /package/dist/{chunk-MZAYIG2I.js.map → chunk-VCJ72ODU.js.map} +0 -0
- /package/dist/{chunk-YGQM6GFQ.js.map → chunk-VHE7UEG5.js.map} +0 -0
- /package/dist/{chunk-QHIWV55K.js.map → chunk-VWRXSBT2.js.map} +0 -0
- /package/dist/{chunk-TSEBMVYY.js.map → chunk-X2GWR75Q.js.map} +0 -0
- /package/dist/{chunk-NKEFH3RS.js.map → chunk-XNAI7QGU.js.map} +0 -0
- /package/dist/{chunk-L6JAALMX.js.map → chunk-XRV4QFEQ.js.map} +0 -0
- /package/dist/{chunk-WH3LUOCQ.js.map → chunk-YE7M7URT.js.map} +0 -0
- /package/dist/{chunk-DA3D5X5Z.js.map → chunk-YVDNCV2D.js.map} +0 -0
- /package/dist/{chunk-G3JFESKD.js.map → chunk-Z5WUSI47.js.map} +0 -0
- /package/dist/{chunk-5HADIIWN.js.map → chunk-ZDAAHCIS.js.map} +0 -0
- /package/dist/{chunk-4KWDNGMA.js.map → chunk-ZVHHFLYY.js.map} +0 -0
|
@@ -47,6 +47,7 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
|
|
|
47
47
|
| `now` | `number?` | The "agora" line, when on the ruler. |
|
|
48
48
|
| `coverage` | `number[]?` | People per hour, index 0 = `from…from+1`. |
|
|
49
49
|
| `coverageLabel` | `string?` | Beside the counts ("Na escala, por hora"). |
|
|
50
|
+
| `groupFooter` | `(group) => DayRulerFooterRow[]?` | Lines under a group's rows, on the tracks' columns (FUT-3882), for the group titled `group` (`undefined` for rows with no group). A row is `{ id, label, cells }` — one `{ text, marked?, title? }` per hour from `from`, a `marked` hour drawn in the warning ink (deepened to read at 4.5:1 on the page) inside a `warning.main` outline ("O movimento pede", an hour short of people). A marked hour's `title` is also said as visually hidden text; without one, it reads "20:00 · 2 · " + `labels.footerMarked`. An unmarked hour's default title is "20:00 · 2", or just the hour when it says nothing — or `{ id, label, trend }`, one value (or `null`) per hour drawn as a line in the primary colour, the highest near the top ("Média de pedidos/hora"; the drawing is `aria-hidden` and its figures are said as visually hidden text, "11:00 4 · 12:00 6"). A row `{ id, label, note, marked? }` says a sentence across the track instead ("Pouco histórico: menos de 4 semanas com pedidos na cozinha."); `marked` draws it in the readable warning ink behind a warning bar (a shape, not colour alone) and flags it `data-marked` ("Faltam 2, 20h–22h"). Not drawn in a phone-sized pane, like the coverage row. Called for every group on each render: keep it cheap (derive the rows once, outside, and look them up). A host drawing these usually leaves out `coverage`: the same counts would show twice. |
|
|
50
51
|
| `tickHours` | `number` (default 4) | An axis label every this many hours (HH:MM). |
|
|
51
52
|
| `snapMinutes` | `number` (default 15) | |
|
|
52
53
|
| `createHours` | `number` (default 4) | What a click alone creates. |
|
|
@@ -55,8 +56,8 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
|
|
|
55
56
|
| `onCreate` / `onMove` / `onResize` / `onBlockClick` | see Gestures | Each is offered only when its handler is given. |
|
|
56
57
|
| `loading` / `error` / `empty` | `boolean` / node / node | Skeleton rows with `aria-busy`; the host's error; the host's empty sentence. |
|
|
57
58
|
| `aria-label` | `string?` | The region's name. |
|
|
58
|
-
| `labels` | `Partial<DayRulerLabels>` | `createTrack(row)`, `moved(block, range)`, `resized(block, range)`, `refused`, `loading`. Default: the English pack; pt-BR: `DAY_RULER_LABELS['pt-BR']` from `@12-apps/ui/data-display/DayRuler`. |
|
|
59
|
-
| `dataTestId` | `string?` (default `day-ruler`) | Parts: `-coverage`, `-axis`, `-row-${rowId}`, `-track-${rowId}`, `-block-${blockId}`, `-handle-l/-r-${blockId}`, `-break-${blockId}`, `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`. |
|
|
59
|
+
| `labels` | `Partial<DayRulerLabels>` | `createTrack(row)`, `moved(block, range)`, `resized(block, range)`, `refused`, `loading`, `footerMarked`. Default: the English pack; pt-BR: `DAY_RULER_LABELS['pt-BR']` from `@12-apps/ui/data-display/DayRuler`. |
|
|
60
|
+
| `dataTestId` | `string?` (default `day-ruler`) | Parts: `-coverage`, `-axis`, `-footer-${footerRowId}`, `-row-${rowId}`, `-track-${rowId}`, `-block-${blockId}`, `-handle-l/-r-${blockId}`, `-break-${blockId}`, `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`. |
|
|
60
61
|
|
|
61
62
|
Tones are `ScheduleGrid`'s; on the ruler a `plan` block is a dashed outline over a faint ground.
|
|
62
63
|
|
|
@@ -7,6 +7,7 @@ import type { RulerAbilities, RulerScale } from './DayRuler.keyboard';
|
|
|
7
7
|
import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
|
|
8
8
|
import { useTextFits } from './useTextFits';
|
|
9
9
|
import { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';
|
|
10
|
+
import { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';
|
|
10
11
|
import {
|
|
11
12
|
actionsSx,
|
|
12
13
|
actualLabelSx,
|
|
@@ -17,15 +18,18 @@ import {
|
|
|
17
18
|
coverageCellSx,
|
|
18
19
|
coverageLabelSx,
|
|
19
20
|
coverageSx,
|
|
21
|
+
footerCellSx,
|
|
22
|
+
footerNoteSx,
|
|
20
23
|
handleSx,
|
|
21
24
|
lineSx,
|
|
22
25
|
nowSx,
|
|
23
26
|
rowSx,
|
|
24
27
|
trackSx,
|
|
28
|
+
trendSx,
|
|
25
29
|
whoSx,
|
|
26
30
|
type Span,
|
|
27
31
|
} from './DayRuler.styles';
|
|
28
|
-
import type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';
|
|
32
|
+
import type { DayRulerBlock, DayRulerFooterCell, DayRulerFooterRow, DayRulerRow, RulerHour } from './DayRuler.types';
|
|
29
33
|
import type { RulerDrag, RulerDragApi } from './useRulerDrag';
|
|
30
34
|
|
|
31
35
|
export const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {
|
|
@@ -254,6 +258,121 @@ export function Coverage(props: { coverage: readonly number[]; label?: string; s
|
|
|
254
258
|
);
|
|
255
259
|
}
|
|
256
260
|
|
|
261
|
+
/** The points of a trend over the hours' centres, in a 100 × 40 box: the highest value near the top, `null` breaks nothing (it is skipped). */
|
|
262
|
+
export function trendPoints(values: readonly (number | null)[], hours: number): string {
|
|
263
|
+
const present = values.slice(0, hours).filter((value): value is number => value !== null);
|
|
264
|
+
const max = Math.max(0, ...present);
|
|
265
|
+
return values
|
|
266
|
+
.slice(0, hours)
|
|
267
|
+
.flatMap((value, index) => {
|
|
268
|
+
if (value === null) return [];
|
|
269
|
+
const x = ((index + 0.5) * 100) / hours;
|
|
270
|
+
const y = max > 0 ? 38 - (value / max) * 34 : 38;
|
|
271
|
+
return [`${x.toFixed(2)},${y.toFixed(2)}`];
|
|
272
|
+
})
|
|
273
|
+
.join(' ');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** One hour's default words: "20:00 · 4"; just the hour when it says nothing. */
|
|
277
|
+
const hourWords = (hour: RulerHour, text: string): string => (text ? `${formatHour(hour)} · ${text}` : formatHour(hour));
|
|
278
|
+
|
|
279
|
+
/** What a reader hears for a marked hour: the host's `title`, else the hour, its count and that it is short. */
|
|
280
|
+
function markedWords(cell: DayRulerFooterCell, hour: RulerHour, marked: string): string {
|
|
281
|
+
return cell.title ?? `${hourWords(hour, cell.text)} · ${marked}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
interface FooterContext {
|
|
285
|
+
hours: number;
|
|
286
|
+
from: RulerHour;
|
|
287
|
+
/** `labels.footerMarked`. */
|
|
288
|
+
marked: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function FooterCells({ cells, ctx }: { cells: readonly DayRulerFooterCell[]; ctx: FooterContext }): React.JSX.Element {
|
|
292
|
+
return (
|
|
293
|
+
<Box sx={coverageSx(ctx.hours)}>
|
|
294
|
+
{Array.from({ length: ctx.hours }, (_, index) => {
|
|
295
|
+
const cell = cells[index] ?? { text: '' };
|
|
296
|
+
const hour = ctx.from + index;
|
|
297
|
+
return (
|
|
298
|
+
<Box
|
|
299
|
+
key={index}
|
|
300
|
+
component="span"
|
|
301
|
+
sx={footerCellSx(Boolean(cell.marked))}
|
|
302
|
+
title={cell.marked ? markedWords(cell, hour, ctx.marked) : (cell.title ?? hourWords(hour, cell.text))}
|
|
303
|
+
data-marked={cell.marked ? '' : undefined}
|
|
304
|
+
>
|
|
305
|
+
{/* A marked hour is said in words too, not only by its outline and a tooltip touch never shows. */}
|
|
306
|
+
{cell.marked ? (
|
|
307
|
+
<>
|
|
308
|
+
<span aria-hidden>{cell.text}</span>
|
|
309
|
+
<Box component="span" sx={SR_ONLY_SX}>
|
|
310
|
+
{markedWords(cell, hour, ctx.marked)}
|
|
311
|
+
</Box>
|
|
312
|
+
</>
|
|
313
|
+
) : (
|
|
314
|
+
cell.text
|
|
315
|
+
)}
|
|
316
|
+
</Box>
|
|
317
|
+
);
|
|
318
|
+
})}
|
|
319
|
+
</Box>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** A trend's figures in words, for the reader the drawn line does not reach: "11:00 4 · 12:00 6". */
|
|
324
|
+
function trendWords(values: readonly (number | null)[], ctx: FooterContext): string {
|
|
325
|
+
return values
|
|
326
|
+
.slice(0, ctx.hours)
|
|
327
|
+
.flatMap((value, index) => (value === null ? [] : [`${formatHour(ctx.from + index)} ${Math.round(value * 10) / 10}`]))
|
|
328
|
+
.join(' · ');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function FooterFigures({ row, ctx }: { row: DayRulerFooterRow; ctx: FooterContext }): React.JSX.Element {
|
|
332
|
+
if (row.trend) {
|
|
333
|
+
return (
|
|
334
|
+
<Box component="span" sx={{ display: 'block', minWidth: 0 }}>
|
|
335
|
+
<Box component="svg" viewBox="0 0 100 40" preserveAspectRatio="none" sx={trendSx} aria-hidden>
|
|
336
|
+
<polyline fill="none" stroke="currentColor" strokeWidth={1.5} vectorEffect="non-scaling-stroke" points={trendPoints(row.trend, ctx.hours)} />
|
|
337
|
+
</Box>
|
|
338
|
+
<Box component="span" sx={SR_ONLY_SX}>
|
|
339
|
+
{trendWords(row.trend, ctx)}
|
|
340
|
+
</Box>
|
|
341
|
+
</Box>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if (row.note !== undefined) {
|
|
345
|
+
return (
|
|
346
|
+
<Box component="span" sx={footerNoteSx(Boolean(row.marked))} data-marked={row.marked ? '' : undefined}>
|
|
347
|
+
{row.note}
|
|
348
|
+
</Box>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return <FooterCells cells={row.cells ?? []} ctx={ctx} />;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* A group's lines under its rows (FUT-3882): counts per hour, a trend, or a
|
|
356
|
+
* sentence, on the tracks' columns. `marked` is `labels.footerMarked`.
|
|
357
|
+
*/
|
|
358
|
+
export function GroupFooter(props: { rows: readonly DayRulerFooterRow[]; scale: RulerScale; testId: string; marked: string }): React.JSX.Element {
|
|
359
|
+
const { rows, scale, testId } = props;
|
|
360
|
+
const ctx: FooterContext = { hours: scale.to - scale.from, from: scale.from, marked: props.marked };
|
|
361
|
+
return (
|
|
362
|
+
<>
|
|
363
|
+
{rows.map((row) => (
|
|
364
|
+
<Box key={row.id} sx={lineSx} data-testid={`${testId}-footer-${row.id}`}>
|
|
365
|
+
<Box component="span" sx={coverageLabelSx}>
|
|
366
|
+
{row.label}
|
|
367
|
+
</Box>
|
|
368
|
+
<FooterFigures row={row} ctx={ctx} />
|
|
369
|
+
<span />
|
|
370
|
+
</Box>
|
|
371
|
+
))}
|
|
372
|
+
</>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
257
376
|
/** The hour labels under the coverage row. */
|
|
258
377
|
export function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {
|
|
259
378
|
return (
|
|
@@ -2,7 +2,7 @@ import { alpha, type CSSObject, type SxProps, type Theme } from '@mui/material/s
|
|
|
2
2
|
|
|
3
3
|
import { FIELD_BORDER_WIDTH } from '../../../tokens/field-height.core';
|
|
4
4
|
import { rem, sxRem } from '../../../tokens/relative';
|
|
5
|
-
import { actualBarColor, blockToneStyles, breakHatch } from '../ScheduleGrid/schedule-tones';
|
|
5
|
+
import { actualBarColor, blockToneStyles, breakHatch, signalInk } from '../ScheduleGrid/schedule-tones';
|
|
6
6
|
import type { ScheduleBlockTone } from '../ScheduleGrid/ScheduleGrid.types';
|
|
7
7
|
import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
|
|
8
8
|
|
|
@@ -301,3 +301,52 @@ export const skeletonNameSx: SxProps<Theme> = (theme: Theme) => ({
|
|
|
301
301
|
borderRadius: rem(theme, M.skeletonNameRadius),
|
|
302
302
|
backgroundColor: theme.palette.action.hover,
|
|
303
303
|
});
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* A marked footer's words (FUT-3882): the warning ink deepened until it reads
|
|
307
|
+
* at 4.5:1 on the page — `warning.main` is 3.1:1 on white. The outline keeps
|
|
308
|
+
* `warning.main`: a non-text mark needs 3:1.
|
|
309
|
+
*/
|
|
310
|
+
export function footerInk(theme: Theme, marked: boolean, plain: string): string {
|
|
311
|
+
return marked ? signalInk(theme, 'warning', theme.palette.background.default) : plain;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** A group's count line (FUT-3882): plain counts on the hours' columns, a marked hour outlined in the warning ink. */
|
|
315
|
+
export function footerCellSx(marked: boolean): SxProps<Theme> {
|
|
316
|
+
return (theme: Theme) => ({
|
|
317
|
+
display: 'flex',
|
|
318
|
+
alignItems: 'center',
|
|
319
|
+
justifyContent: 'center',
|
|
320
|
+
fontSize: rem(theme, M.coverage.font),
|
|
321
|
+
borderRadius: rem(theme, M.coverage.radius),
|
|
322
|
+
color: footerInk(theme, marked, theme.palette.text.primary),
|
|
323
|
+
fontWeight: marked ? 500 : 400,
|
|
324
|
+
// The hairline is always there, transparent when unmarked, so a mark never shifts a count.
|
|
325
|
+
border: `1px solid ${marked ? theme.palette.warning.main : 'transparent'}`,
|
|
326
|
+
minWidth: 0,
|
|
327
|
+
overflow: 'hidden',
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* A footer line's sentence; when the host marks it ("Faltam 2, 20h–22h"), in
|
|
333
|
+
* the readable warning ink with a warning bar before it — a shape, so colour
|
|
334
|
+
* is never the only signal.
|
|
335
|
+
*/
|
|
336
|
+
export function footerNoteSx(marked: boolean): SxProps<Theme> {
|
|
337
|
+
return (theme: Theme) => ({
|
|
338
|
+
fontSize: rem(theme, M.meta),
|
|
339
|
+
color: footerInk(theme, marked, theme.palette.text.secondary),
|
|
340
|
+
fontWeight: marked ? 500 : 400,
|
|
341
|
+
...(marked ? { borderLeft: `${rem(theme, 3)} solid ${theme.palette.warning.main}`, paddingLeft: rem(theme, 6) } : {}),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** A group's trend line: as tall as the coverage row, drawn in the primary colour. */
|
|
346
|
+
export const trendSx: SxProps<Theme> = (theme: Theme) => ({
|
|
347
|
+
display: 'block',
|
|
348
|
+
width: '100%',
|
|
349
|
+
height: rem(theme, M.coverage.height + M.coverage.height / 2),
|
|
350
|
+
color: theme.palette.primary.main,
|
|
351
|
+
overflow: 'visible',
|
|
352
|
+
});
|
|
@@ -9,7 +9,7 @@ import { useAnnouncer } from '../ScheduleGrid/useAnnouncer';
|
|
|
9
9
|
import { handleBlockKey, keyCreateSpan, type RulerAbilities, type RulerScale } from './DayRuler.keyboard';
|
|
10
10
|
import { EN_US_DAY_RULER_LABELS } from './DayRuler.en-US';
|
|
11
11
|
import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
|
|
12
|
-
import { Axis, Coverage, RulerRow, type RowContext } from './DayRuler.parts';
|
|
12
|
+
import { Axis, Coverage, GroupFooter, RulerRow, type RowContext } from './DayRuler.parts';
|
|
13
13
|
import { groupTitleSx, rootSx, rowSx, skeletonNameSx, skeletonTrackSx } from './DayRuler.styles';
|
|
14
14
|
import type { DayRulerProps, DayRulerRow } from './DayRuler.types';
|
|
15
15
|
import { useRulerDrag } from './useRulerDrag';
|
|
@@ -40,6 +40,14 @@ function abilitiesOf(props: DayRulerProps): RulerAbilities {
|
|
|
40
40
|
return { create: open && Boolean(props.onCreate), move: open && Boolean(props.onMove), resize: open && Boolean(props.onResize) };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** A group's lines under its rows, when the host gives any (`groupFooter`). */
|
|
44
|
+
function FooterOf({ props, group, ctx }: { props: DayRulerProps; group: string | undefined; ctx: RowContext }): React.JSX.Element | null {
|
|
45
|
+
const rows = props.groupFooter?.(group);
|
|
46
|
+
if (!rows || rows.length === 0) return null;
|
|
47
|
+
const marked = props.labels?.footerMarked ?? EN_US_DAY_RULER_LABELS.footerMarked;
|
|
48
|
+
return <GroupFooter rows={rows} scale={ctx.scale} testId={ctx.testId} marked={marked} />;
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
/** The groups and their rows — or the skeleton, the host's error, or the host's empty state. */
|
|
44
52
|
function RulerBody({ props, ctx }: { props: DayRulerProps; ctx: RowContext }): React.JSX.Element {
|
|
45
53
|
if (props.error) return <>{props.error}</>;
|
|
@@ -68,6 +76,7 @@ function RulerBody({ props, ctx }: { props: DayRulerProps; ctx: RowContext }): R
|
|
|
68
76
|
{group.rows.map((row) => (
|
|
69
77
|
<RulerRow key={row.id} row={row} ctx={ctx} />
|
|
70
78
|
))}
|
|
79
|
+
<FooterOf props={props} group={group.title} ctx={ctx} />
|
|
71
80
|
</Box>
|
|
72
81
|
))}
|
|
73
82
|
</>
|
|
@@ -66,6 +66,29 @@ export interface DayRulerRow {
|
|
|
66
66
|
labelAccessible?: string;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/** One hour of a group's count line: what it says, and whether the host marks it (an hour short of people). */
|
|
70
|
+
export interface DayRulerFooterCell {
|
|
71
|
+
/** What the hour says ("4", "—"). Empty draws nothing. */
|
|
72
|
+
text: string;
|
|
73
|
+
/** Drawn in the warning ink inside a warning outline. Never the only signal: say it in `title` too. */
|
|
74
|
+
marked?: boolean;
|
|
75
|
+
/** The hour's tooltip and accessible words ("20h · o movimento pede 4, a escala tem 2"). Default: the hour and `text`. */
|
|
76
|
+
title?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A line under a group's rows, on the tracks' columns (FUT-3882): one count
|
|
81
|
+
* per hour (`cells`, index 0 = `from…from+1`) — "Na escala, por hora",
|
|
82
|
+
* "O movimento pede" —, a trend drawn as a line (`trend`, one value per hour,
|
|
83
|
+
* `null` for none) — "Média de pedidos/hora" —, or a sentence across the
|
|
84
|
+
* track where the figures would be (`note`) — "Pouco histórico: …", or,
|
|
85
|
+
* `marked`, in the warning ink: "Faltam 2, 20h–22h".
|
|
86
|
+
*/
|
|
87
|
+
export type DayRulerFooterRow =
|
|
88
|
+
| { id: string; label: string; cells: readonly DayRulerFooterCell[]; trend?: undefined; note?: undefined }
|
|
89
|
+
| { id: string; label: string; trend: readonly (number | null)[]; cells?: undefined; note?: undefined }
|
|
90
|
+
| { id: string; label: string; note: string; marked?: boolean; cells?: undefined; trend?: undefined };
|
|
91
|
+
|
|
69
92
|
export interface DayRulerProps {
|
|
70
93
|
rows: readonly DayRulerRow[];
|
|
71
94
|
/** The ruler's first hour (default 6, 06:00). */
|
|
@@ -78,6 +101,12 @@ export interface DayRulerProps {
|
|
|
78
101
|
coverage?: readonly number[];
|
|
79
102
|
/** Beside the coverage row ("Na escala, por hora"). */
|
|
80
103
|
coverageLabel?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Lines under a group's rows, on the tracks' columns, for the group titled
|
|
106
|
+
* `group` (`undefined` for rows with no group). Like the coverage row they
|
|
107
|
+
* are not drawn in a phone-sized pane, where the host says it its own way.
|
|
108
|
+
*/
|
|
109
|
+
groupFooter?: (group: string | undefined) => readonly DayRulerFooterRow[] | undefined;
|
|
81
110
|
/** An axis label every this many hours (default 4). */
|
|
82
111
|
tickHours?: number;
|
|
83
112
|
/** Every gesture snaps to this (default 15 minutes). */
|
|
@@ -99,7 +128,7 @@ export interface DayRulerProps {
|
|
|
99
128
|
/** The component's own words. Default: the English pack. */
|
|
100
129
|
labels?: Partial<DayRulerLabels>;
|
|
101
130
|
/**
|
|
102
|
-
* Base test id (default `day-ruler`). Parts: `-coverage`, `-axis`, `-row-${rowId}`,
|
|
131
|
+
* Base test id (default `day-ruler`). Parts: `-coverage`, `-axis`, `-footer-${footerRowId}`, `-row-${rowId}`,
|
|
103
132
|
* `-track-${rowId}`, `-block-${blockId}`, `-handle-l-${blockId}`, `-handle-r-${blockId}`, `-break-${blockId}`,
|
|
104
133
|
* `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`.
|
|
105
134
|
*/
|
|
@@ -48,10 +48,11 @@ lists events on one axis). For one day by the hour, or for a phone, use `DayRule
|
|
|
48
48
|
|
|
49
49
|
| Prop | Type | Description |
|
|
50
50
|
|---|---|---|
|
|
51
|
-
| `columns` | `ScheduleGridColumn[]` | `label` (wraps to a second line when its column is short, never cut, and never between a word and the number after it: "Seg 19/10" stays whole; also in `title`), `sublabel?`, `today?` (label in the primary colour, cells framed), `past?` (the day has gone: label in `text.secondary`, see [Closed days](#closed-days)). A day is its index here. |
|
|
51
|
+
| `columns` | `ScheduleGridColumn[]` | `label` (wraps to a second line when its column is short, never cut, and never between a word and the number after it: "Seg 19/10" stays whole; also in `title`), `sublabel?`, `adornment?` (a node under the label and sublabel, inside the pinned header and the column's width — a host's own figure for the day, such as an hour band and a "falta 2 · 20–22h" line; every header cell grows to the tallest, so keep it short; it sits inside the `columnheader`, so its words become part of the day's accessible name — draw a figure `aria-hidden` and give it words of its own, as `role="img"` with an `aria-label`), `today?` (label in the primary colour, cells framed), `past?` (the day has gone: label in `text.secondary`, see [Closed days](#closed-days)). A day is its index here. |
|
|
52
52
|
| `groups` | `ScheduleGridGroup[]` | `title?` (upper-case heading, names the row group), `rows`: `id`, `label`, `meta?`, `metaTone?` (`'warning'` for "acima de 40h"), `total?`, `onLabelClick?` (makes the name a button), `labelAccessible?`, `nameAdornment?` (node beside the name — a "Freela" tag; it moves under the name when the column has no room for both), `chips?` (node under the meta — "2 regras ›"; one line, a chip that does not fit shrinks and cuts its label). |
|
|
53
53
|
| `cell` | `(rowId, day) => ScheduleGridCell` | `blocks` (`id`, `label`, `note?`, `tone`, `movable?`, `copyable?`, `selected?`, `title?`, `breakSpan?` — `{ start, end }` as fractions 0…1 of the block, a meal break drawn as a hatched gap; its words go in `note`), `blocked?` (hatched, refuses create and drop), `blockedLabel?`, `closed?` (the day can take no new turno — it has gone: no fill, no "+", see [Closed days](#closed-days)), `closedLabel?` (a closed cell's visually hidden words, "Já passou"). Called for every cell on each render: keep it cheap. |
|
|
54
54
|
| `totalLabel` | `string?` | The last column's header ("horas"). |
|
|
55
|
+
| `corner` | `ReactNode?` | The header row's cell above the names, pinned with it ("Movimento, 11h–23h" — what the days' adornments show), on the days' label line. A string never breaks at a range's dash ("11h–23h" stays whole). The cell itself always spans the header row's full height: it is the opaque mask the days scroll under. Empty by default. |
|
|
55
56
|
| `readOnly` | `boolean?` | No "+", no grips, no drag; empty cells are not focusable. Block clicks still reach `onBlockClick`. |
|
|
56
57
|
| `onCreate` / `onCopy` / `onMove` / `onBlockClick` | see Gestures | Each gesture is offered only when its handler is given. |
|
|
57
58
|
| `createPreview` | `(rowId) => string` | The ghost text a create drag shows (the person's last hours). |
|
|
@@ -62,7 +63,7 @@ lists events on one axis). For one day by the hour, or for a phone, use `DayRule
|
|
|
62
63
|
| `scrollToDay` | `number?` (an index into `columns`) | While the days scroll sideways, the day they open on. Applied **once per value** (and per day count): as soon as the days are drawn, they scroll at once (`behavior: 'instant'`, never smoothly, so reduced motion needs nothing else) until that day is the first column after the pinned names — or to the end of their reach, when the day is that close to the end. While `loading`, `error` or `empty` it waits for the rows; while the grid has no box (`display: none`, a container that gets its width after mount) it waits for the first width. The header row follows; focus and the page's vertical scroll are untouched. A re-render or a refetch with the same value keeps the person's own scroll, and **a later resize does not re-anchor** — pass a new value for that. A value outside the columns, and a grid whose days fit, do nothing. **Assumes LTR**: the names are the left column. Pass today's index (`3` on a Thursday) so a week opened mid-week does not start on the gone days. |
|
|
63
64
|
| `aria-label` | `string?` | The grid's name. |
|
|
64
65
|
| `labels` | `Partial<ScheduleGridLabels>` | `createCell(row, column)`, `blockedCell(row, column)`, `copied(block, column)`, `moved(block, row, column)`, `refused`, `loading`. Default: the English pack; a pt-BR host passes `SCHEDULE_GRID_LABELS['pt-BR']` from `@12-apps/ui/data-display/ScheduleGrid`. |
|
|
65
|
-
| `dataTestId` | `string?` (default `schedule-grid`) | Parts: `-header`, `-row-${rowId}`, `-cell-${rowId}-${day}`, `-block-${blockId}`, `-grip-l-${blockId}`, `-grip-r-${blockId}`, `-break-${blockId}`, `-live`, `-loading`. |
|
|
66
|
+
| `dataTestId` | `string?` (default `schedule-grid`) | Parts: `-header`, `-corner`, `-adornment-${day}`, `-row-${rowId}`, `-cell-${rowId}-${day}`, `-block-${blockId}`, `-grip-l-${blockId}`, `-grip-r-${blockId}`, `-break-${blockId}`, `-live`, `-loading`. |
|
|
66
67
|
|
|
67
68
|
### Tones — never colour alone
|
|
68
69
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
totalCellSx,
|
|
26
26
|
totalHeaderSx,
|
|
27
27
|
} from './ScheduleGrid.styles';
|
|
28
|
+
import { cornerSx, headerAdornmentSx } from './schedule-header-host.styles';
|
|
28
29
|
import type { CellLook } from './ScheduleGrid.styles';
|
|
29
30
|
import type {
|
|
30
31
|
ScheduleGridBlock,
|
|
@@ -248,22 +249,31 @@ interface HeaderProps {
|
|
|
248
249
|
columns: readonly ScheduleGridColumn[];
|
|
249
250
|
/** Present when the grid has a last column. */
|
|
250
251
|
totalLabel?: string;
|
|
252
|
+
/** Above the names (`ScheduleGridProps.corner`). */
|
|
253
|
+
corner?: React.ReactNode;
|
|
251
254
|
rowSx: RowSx;
|
|
252
255
|
testId: string;
|
|
253
256
|
}
|
|
254
257
|
|
|
255
258
|
/** The days, their counts and the total column's header — pinned while the grid scrolls. */
|
|
256
259
|
|
|
260
|
+
/** A range ("11h–23h") breaks before or after it, never at its dash; a spaced dash ("11h – 23h") keeps its spaces. */
|
|
261
|
+
function keepRangesWhole(node: React.ReactNode): React.ReactNode {
|
|
262
|
+
return typeof node === 'string' ? node.replace(/(\S)–(\S)/g, '$1\u2060–\u2060$2') : node;
|
|
263
|
+
}
|
|
264
|
+
|
|
257
265
|
/** A label wraps between words, never inside "Seg 19/10": the space before a number holds. */
|
|
258
266
|
function keepDatesWhole(label: string): string {
|
|
259
267
|
return label.replace(/ (?=\d)/g, '\u00a0');
|
|
260
268
|
}
|
|
261
269
|
|
|
262
|
-
export function GridHeader({ columns, totalLabel, rowSx, testId }: HeaderProps): React.JSX.Element {
|
|
270
|
+
export function GridHeader({ columns, totalLabel, corner, rowSx, testId }: HeaderProps): React.JSX.Element {
|
|
263
271
|
return (
|
|
264
272
|
<Box role="row" sx={[rowSx, headerRowSx]} data-testid={`${testId}-header`}>
|
|
265
|
-
{/* Above the names: a cell of the header row, with nothing to head. */}
|
|
266
|
-
<span role="cell" data-sg-corner=""
|
|
273
|
+
{/* Above the names: a cell of the header row, with nothing to head but the host's corner words. */}
|
|
274
|
+
<Box component="span" role="cell" data-sg-corner="" sx={cornerSx} data-testid={`${testId}-corner`}>
|
|
275
|
+
{keepRangesWhole(corner)}
|
|
276
|
+
</Box>
|
|
267
277
|
{columns.map((column, day) => (
|
|
268
278
|
<Box
|
|
269
279
|
role="columnheader"
|
|
@@ -279,6 +289,11 @@ export function GridHeader({ columns, totalLabel, rowSx, testId }: HeaderProps):
|
|
|
279
289
|
{column.sublabel}
|
|
280
290
|
</Box>
|
|
281
291
|
) : null}
|
|
292
|
+
{column.adornment ? (
|
|
293
|
+
<Box component="span" sx={headerAdornmentSx} data-sg-col="adornment" data-testid={`${testId}-adornment-${day}`}>
|
|
294
|
+
{column.adornment}
|
|
295
|
+
</Box>
|
|
296
|
+
) : null}
|
|
282
297
|
</Box>
|
|
283
298
|
))}
|
|
284
299
|
{totalLabel !== undefined ? (
|
|
@@ -132,7 +132,7 @@ export function ScheduleGrid(props: ScheduleGridProps): React.JSX.Element {
|
|
|
132
132
|
>
|
|
133
133
|
{/* The header row sits outside the sideways scroller, so it sticks to the page; it follows the days sideways. */}
|
|
134
134
|
<Box ref={headRef} role="none" sx={headerSx(props.stickyTop ?? 0)} data-sg-head="" data-testid={`${testId}-head`} onWheel={sideways.forwardWheel}>
|
|
135
|
-
<GridHeader columns={columns} totalLabel={hasTotal ? (props.totalLabel ?? '') : undefined} rowSx={rowSx} testId={testId} />
|
|
135
|
+
<GridHeader columns={columns} totalLabel={hasTotal ? (props.totalLabel ?? '') : undefined} corner={props.corner} rowSx={rowSx} testId={testId} />
|
|
136
136
|
</Box>
|
|
137
137
|
<Box ref={bodyRef} role="none" sx={scrollSx} data-sg-scroll="" data-testid={`${testId}-scroll`} onScroll={sideways.follow}
|
|
138
138
|
onFocus={sideways.bringOut}
|
|
@@ -19,6 +19,13 @@ export interface ScheduleGridColumn {
|
|
|
19
19
|
label: string;
|
|
20
20
|
/** Under the label ("3 turnos", "ninguém"). */
|
|
21
21
|
sublabel?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Under the label and the sublabel, inside the pinned header row: the host's
|
|
24
|
+
* own figure for that day (an hour band of how busy it gets, a "falta 2 ·
|
|
25
|
+
* 20–22h" line). It takes the column's width and travels with the header,
|
|
26
|
+
* sideways too. Keep it short: every header cell grows to the tallest.
|
|
27
|
+
*/
|
|
28
|
+
adornment?: React.ReactNode;
|
|
22
29
|
/** Today's column: its label in the primary colour and its cells framed. */
|
|
23
30
|
today?: boolean;
|
|
24
31
|
/**
|
|
@@ -125,6 +132,11 @@ export interface ScheduleGridProps {
|
|
|
125
132
|
cell: (rowId: string, day: number) => ScheduleGridCell;
|
|
126
133
|
/** The last column's header ("horas"). Without it and without row totals, there is no last column. */
|
|
127
134
|
totalLabel?: string;
|
|
135
|
+
/**
|
|
136
|
+
* The header row's cell above the names, pinned with it ("Movimento,
|
|
137
|
+
* 11h–23h" — what the days' adornments show). Empty by default.
|
|
138
|
+
*/
|
|
139
|
+
corner?: React.ReactNode;
|
|
128
140
|
/**
|
|
129
141
|
* No create, copy or move: no "+", no grips, empty cells are not focusable.
|
|
130
142
|
* Block clicks still reach `onBlockClick` (a read-only window).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles/index.js';
|
|
2
|
+
|
|
3
|
+
import { sxRem } from '../../../tokens/relative';
|
|
4
|
+
import { SCHEDULE_GRID_METRICS as M } from './ScheduleGrid.metrics';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The header row's host figures (FUT-3882): a day's adornment under its label
|
|
8
|
+
* and the corner above the names. Their own module to keep the grid's styles
|
|
9
|
+
* under the file-size budget.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** A day's adornment: under its label, the column's full width, in the secondary ink unless the host says otherwise. */
|
|
13
|
+
export const headerAdornmentSx: SxProps<Theme> = {
|
|
14
|
+
mt: 0.5,
|
|
15
|
+
fontSize: sxRem(M.headerSublabel),
|
|
16
|
+
fontWeight: 400,
|
|
17
|
+
color: 'text.secondary',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The cell above the names. It is the pinned mask over the names column (the
|
|
22
|
+
* grid's sticky rule): it must stay a stretched grid item, the header row's
|
|
23
|
+
* full height, or a day scrolled sideways shows above the names. Its words sit
|
|
24
|
+
* inside it on the days' label line (the prototype's "Movimento, 11h–23h").
|
|
25
|
+
*/
|
|
26
|
+
export const cornerSx: SxProps<Theme> = {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
flexDirection: 'column',
|
|
29
|
+
justifyContent: 'flex-start',
|
|
30
|
+
fontSize: sxRem(M.headerSublabel),
|
|
31
|
+
color: 'text.secondary',
|
|
32
|
+
pt: 0.5,
|
|
33
|
+
pr: 0.5,
|
|
34
|
+
minWidth: 0,
|
|
35
|
+
};
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,OAAO,eAAe;AACtB,OAAO,qBAAqB;AAC5B,OAAO,SAAS;AAChB,OAAO,YAAY;AACnB,OAAO,aAAa;AACpB,OAAO,gBAAgB;AACvB,OAAO,mBAAmB;AAC1B,SAAS,gBAA4B;AACrC,SAAgB,kBAAkB;AA4D5B,SA6HF,UA7HE,KAyIA,YAzIA;AA7BN,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF,GAIsB;AACpB,QAAM,OAAO,iBAAiB;AAC9B,QAAM,QAAQ,CAAC,UAAsC;AACnD,UAAM,gBAAgB;AACtB,YAAQ;AAAA,EACV;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,MAAK;AAAA,MACL,UAAU;AAAA,MACV,cAAY,KAAK,QAAQ,WAAW,KAAK;AAAA,MACzC,eAAa;AAAA,MACb,SAAS;AAAA,MACT,WAAW,CAAC,UAA+B;AACzC,YAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,IAAK;AAChD,cAAM,eAAe;AACrB,cAAM,KAAK;AAAA,MACb;AAAA,MACA,IAAI,EAAE,SAAS,eAAe,cAAc,KAAK,GAAG,OAAO,WAAW,EAAE,SAAS,kBAAkB,EAAE;AAAA,MAErG,8BAAC,aAAU,IAAI,EAAE,UAAU,MAAM,EAAE,EAAE,GAAG;AAAA;AAAA,EAC1C;AAEJ;AAGA,SAAS,aAAgD;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOsB;AACpB,QAAM,SAAS,WAAW,KAAK;AAC/B,QAAM,WAAW,gBAAgB,KAAK;AAItC,QAAM,OAAO,WAAW,eAAe,SAAS,iBAAiB;AACjE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,MAAK;AAAA,MACL,eAAa;AAAA,MACb,iBAAc;AAAA,MACd,iBAAe;AAAA,MACf,SAAS,CAAC,UAAU,OAAO,MAAM,aAAa;AAAA,MAK9C,WACE,SACE,oBAAC,mBAAgB,OAAO,MAAM,OAAO,SAAS,MAAM,SAAS,CAAC,CAAC,GAAG,QAAQ,GAAG,MAAM,iBAAiB,IAClG;AAAA,MAEN,SAAS,oBAAC,mBAAgB,IAAI,EAAE,UAAU,MAAM,EAAE,EAAE,GAAG;AAAA,MACvD,IAAI;AAAA,QACF,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,UAAU,MAAM,GAAG;AAAA,QACnB,OAAO,WAAW,eAAe;AAAA,QACjC,YAAY;AAAA,QACZ,UAAU,MAAM,EAAE;AAAA,QAClB,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,aAAa,CAAC,UAAU,QAAQ,UAAU,KAAK;AAAA,QAC/C,SAAS,SAAS,oBAAoB;AAAA,QACtC,WAAW;AAAA,UACT,aAAa,SAAS,OAAO;AAAA,UAC7B,SAAS,SAAS,oBAAoB;AAAA,QACxC;AAAA,MACF;AAAA,MAIA,8BAAC,OAAI,WAAU,QAAO,IAAI,EAAE,UAAU,UAAU,cAAc,WAAW,GACtE,mBAAS,eAAe,OAAO,KAAK,IAAI,MAAM,OACjD;AAAA;AAAA,EACF;AAEJ;AAUA,SAAS,oBAA2B;AAClC,QAAM,QAAQ,SAAS;AACvB,SAAO,cAAc,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,IAAI;AACzD;AAWA,IAAM,eAAe,CAAC,WAAkB;AAAA,EACtC,OAAO,EAAE,IAAI,QAAQ,IAAI,IAAI,OAAO,GAAG,GAAG,IAAI,OAAO;AAAA;AAAA;AAAA;AAAA,EAIrD,UAAU,EAAE,IAAI,eAAe,IAAI,OAAO,EAAE,CAAC,KAAK,IAAI,eAAe,IAAI,OAAO,EAAE,CAAC,IAAI;AAAA,EACvF,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE;AACpB;AAGO,SAAS,UAA6C;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOsB;AACpB,QAAM,QAAQ,eAAe;AAC7B,QAAM,SAAS,GAAG,YAAY,UAAU,MAAM,EAAE;AAChD,QAAM,iBAAiB,kBAAkB;AACzC,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ,cAAc,MAAM,YAAY,KAAK,IAAI,CAAC;AACxD,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,CAAC,YAAY;AACnB,gBAAM,KAAK,OAAO;AAClB,yBAAe,IAAI;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,MAAM,MAAM;AAAA,QAC1B;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG,MAAM;AAAA,QACV,SAAS,MAAM;AACb,gBAAM,MAAM;AACZ,yBAAe,KAAK;AAAA,QACtB;AAAA,QAEA,iBAAiB,MAAM,SAAS,SAAS,QAAQ,MAAM,OAAO,CAAC,IAAI;AAAA,QACnE,WAAW,EAAE,OAAO,EAAE,KAAK,MAAM,UAAU,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,IAAI,GAAG,MAAM,SAAS,SAAS,YAAY,EAAE,EAAE;AAAA,QAE3G;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,UACF;AAAA,UACA,oBAAC,gBAAa,OAAc,OAAc,SAAS,MAAM,SAAS,CAAC,CAAC,GAAG,QAAgB;AAAA;AAAA;AAAA,IACzF;AAAA,KACF;AAEJ;AAQO,SAAS,gBAAmD;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKsB;AACpB,QAAM,OAAO,iBAAiB;AAG9B,QAAM,QAAQ,WAAW,kBAAkB;AAC3C,MAAI,MAAM,SAAS,OAAO;AACxB,WACE,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC1D;AAAA,0BAAC,cAAW,WAAU,QAAO,IAAI,EAAE,UAAU,MAAM,EAAE,GAAG,YAAY,KAAK,OAAO,iBAAiB,GAC9F,gBAAM,OACT;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc,QAAQ,MAAM,OAAO;AAAA;AAAA,MACrC;AAAA,OACF;AAAA,EAEJ;AACA,SACE,qBAAC,OAAI,IAAI,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC1D;AAAA;AAAA,MAAC,YAAY;AAAA,MAAZ;AAAA,QACC,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,QACZ,OAAO,aAAa,KAAK;AAAA,QACzB,UAAU,CAAC,UAAU,SAAS,KAAK;AAAA,QACnC;AAAA;AAAA,IACF;AAAA,IAIA,oBAAC,gBAAa,SAAS,WAAW,OAAO,IAAI,GAAG,OAAc,UAAoB,QAAgB;AAAA,KACpG;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/DayRuler/DayRuler.parts.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport type * as React from 'react';\nimport { useRef } from 'react';\n\nimport { ScheduleNameCell } from '../ScheduleGrid/ScheduleNameCell';\nimport type { RulerAbilities, RulerScale } from './DayRuler.keyboard';\nimport { DAY_RULER_METRICS as M } from './DayRuler.metrics';\nimport { useTextFits } from './useTextFits';\nimport { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';\nimport {\n actionsSx,\n actualLabelSx,\n actualSx,\n axisSx,\n blockSx,\n breakSx,\n coverageCellSx,\n coverageLabelSx,\n coverageSx,\n handleSx,\n lineSx,\n nowSx,\n rowSx,\n trackSx,\n whoSx,\n type Span,\n} from './DayRuler.styles';\nimport type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';\nimport type { RulerDrag, RulerDragApi } from './useRulerDrag';\n\nexport const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {\n const at = percentOf(start, scale.from, scale.to);\n return { at, share: Math.max(0, percentOf(end, scale.from, scale.to) - at) };\n};\n\n/** What every row of one ruler shares. */\nexport interface RowContext {\n scale: RulerScale;\n now?: RulerHour;\n can: RulerAbilities;\n drag: RulerDrag | null;\n dragApi: RulerDragApi;\n onBlockClick?: (blockId: string) => void;\n onKeyCreate: (row: DayRulerRow) => void;\n onBlockKey: (event: React.KeyboardEvent, block: DayRulerBlock) => void;\n createLabel: (row: string) => string;\n testId: string;\n}\n\n/** The keys a block answers to, for `aria-keyshortcuts`. */\nfunction shortcutsOf(movable: boolean, resizable: boolean): string | undefined {\n const keys = [\n movable ? 'Alt+ArrowLeft Alt+ArrowRight' : '',\n resizable ? 'Shift+ArrowLeft Shift+ArrowRight Shift+Alt+ArrowLeft Shift+Alt+ArrowRight' : '',\n ];\n return keys.filter(Boolean).join(' ') || undefined;\n}\n\nfunction Handle({ side, onPointerDown, testId }: { side: 'l' | 'r'; onPointerDown: (event: React.PointerEvent<HTMLElement>) => void; testId: string }) {\n return <Box component=\"span\" aria-hidden sx={handleSx(side)} data-testid={testId} onPointerDown={onPointerDown} />;\n}\n\ninterface BlockProps {\n row: DayRulerRow;\n block: DayRulerBlock;\n /** Where it is drawn: its own hours, or where a drag has it now. */\n span: Span;\n /** The hours it is drawn at (its own, or a drag's). */\n at: { start: RulerHour; end: RulerHour };\n text: string;\n ctx: RowContext;\n testId: string;\n}\n\n/**\n * The block's break, where it falls inside the block as drawn: it moves with a\n * dragged block (same offset from its start) and is cut to the block's ends.\n */\nfunction breakShare(block: DayRulerBlock, start: RulerHour, end: RulerHour): Span | null {\n const span = block.breakSpan;\n const length = end - start;\n if (!span || !(length > 0)) return null;\n const shift = Math.abs(length - (block.end - block.start)) < 1e-6 ? start - block.start : 0;\n const a = Math.max(start, span.start + shift);\n const b = Math.min(end, span.end + shift);\n if (!(b > a)) return null;\n return { at: ((a - start) / length) * 100, share: ((b - a) / length) * 100 };\n}\n\n/** The block's break as a hatched band, when it falls inside the block as drawn. */\nfunction BreakBand({ block, at, testId }: { block: DayRulerBlock; at: BlockProps['at']; testId: string }): React.JSX.Element | null {\n const gap = breakShare(block, at.start, at.end);\n if (!gap) return null;\n return <Box component=\"span\" aria-hidden sx={breakSx(gap)} data-dr-break=\"\" data-testid={testId} />;\n}\n\nfunction RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): React.JSX.Element {\n const { can, dragApi, scale } = ctx;\n const movable = can.move && Boolean(block.movable);\n const resizable = can.resize && Boolean(block.resizable);\n const ref = useRef<HTMLButtonElement | null>(null);\n // Short by pixels; where nothing can be measured, by the prototype's 30% rule.\n const fits = useTextFits(ref, text, (block.end - block.start) / (scale.to - scale.from) >= M.narrowShare);\n const press = (mode: 'move' | 'start' | 'end') => (event: React.PointerEvent<HTMLElement>) =>\n dragApi.startBlock(event, row.id, block, mode);\n return (\n <Box\n component=\"button\"\n type=\"button\"\n sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable })}\n title={block.title ?? block.label}\n aria-keyshortcuts={shortcutsOf(movable, resizable)}\n aria-current={block.selected ? 'true' : undefined}\n ref={ref}\n data-narrow={fits ? undefined : ''}\n data-tone={block.tone}\n data-testid={testId}\n onPointerDown={movable ? press('move') : undefined}\n onClick={() => {\n if (!dragApi.takeClick()) ctx.onBlockClick?.(block.id);\n }}\n onKeyDown={(event: React.KeyboardEvent) => ctx.onBlockKey(event, block)}\n >\n <BreakBand block={block} at={at} testId={`${ctx.testId}-break-${block.id}`} />\n {resizable ? <Handle side=\"l\" onPointerDown={press('start')} testId={`${ctx.testId}-handle-l-${block.id}`} /> : null}\n <span data-dr-text=\"full\" title={text}>\n {text}\n </span>\n <span data-dr-text=\"short\" title={text}>\n {block.shortLabel ?? text}\n </span>\n {resizable ? <Handle side=\"r\" onPointerDown={press('end')} testId={`${ctx.testId}-handle-r-${block.id}`} /> : null}\n </Box>\n );\n}\n\n/** A row's blocks, each where a drag has it now (with its live hours) or where it is. */\nfunction Blocks({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n const mine = ctx.drag && ctx.drag.rowId === row.id ? ctx.drag : null;\n return (\n <>\n {row.blocks.map((block) => {\n const live = mine?.blockId === block.id ? mine : null;\n const start = live ? live.start : block.start;\n const end = live ? live.end : block.end;\n return (\n <RulerBlock\n key={block.id}\n row={row}\n block={block}\n span={spanOf(start, end, ctx.scale)}\n at={{ start, end }}\n text={live?.moved ? formatRange(start, end) : block.label}\n ctx={ctx}\n testId={`${ctx.testId}-block-${block.id}`}\n />\n );\n })}\n {mine && mine.mode === 'create' ? <CreatePreview row={row} drag={mine} ctx={ctx} /> : null}\n </>\n );\n}\n\n/** The block a create drag is drawing, with its live hours. */\nfunction CreatePreview({ row, drag, ctx }: { row: DayRulerRow; drag: RulerDrag; ctx: RowContext }): React.JSX.Element {\n const range = formatRange(drag.start, drag.end);\n return (\n <RulerBlock\n row={row}\n block={{ id: '\\u0000new', start: drag.start, end: drag.end, label: range, tone: 'plan' }}\n span={spanOf(drag.start, Math.max(drag.end, drag.start + ctx.scale.snap), ctx.scale)}\n at={{ start: drag.start, end: drag.end }}\n text={range}\n ctx={ctx}\n testId={`${ctx.testId}-preview`}\n />\n );\n}\n\n/** The real shift under the blocks, and its label at its start. */\nfunction Actual({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element | null {\n const { actual } = row;\n if (!actual) return null;\n return (\n <>\n <Box sx={actualSx(spanOf(actual.start, actual.end, ctx.scale))} data-testid={`${ctx.testId}-actual-${row.id}`} />\n {row.actualLabel ? (\n <Box component=\"span\" sx={actualLabelSx(percentOf(actual.start, ctx.scale.from, ctx.scale.to))}>\n {row.actualLabel}\n </Box>\n ) : null}\n </>\n );\n}\n\n/** A row's track: the actual bar, its blocks (or the one being dragged, live) and the now line. */\nfunction Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n const { scale, now, can, dragApi } = ctx;\n const showNow = now !== undefined && now >= scale.from && now <= scale.to;\n return (\n <Box\n sx={trackSx({ hours: scale.to - scale.from, tickHours: scale.tickHours, interactive: can.create })}\n data-dr-track=\"\"\n data-testid={`${ctx.testId}-track-${row.id}`}\n role={can.create ? 'group' : undefined}\n tabIndex={can.create ? 0 : undefined}\n aria-label={can.create ? ctx.createLabel(row.label) : undefined}\n onPointerDown={can.create ? (event: React.PointerEvent<HTMLElement>) => dragApi.startCreate(event, row.id) : undefined}\n onKeyDown={(event: React.KeyboardEvent) => {\n if (!can.create || event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return;\n event.preventDefault();\n ctx.onKeyCreate(row);\n }}\n >\n <Actual row={row} ctx={ctx} />\n <Blocks row={row} ctx={ctx} />\n {showNow ? <Box sx={nowSx(percentOf(now, scale.from, scale.to))} data-testid={`${ctx.testId}-now`} /> : null}\n </Box>\n );\n}\n\n/** One person: name, meta and chips · the track · the row's actions. */\nexport function RulerRow({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n return (\n <Box sx={rowSx} data-testid={`${ctx.testId}-row-${row.id}`} role=\"listitem\">\n <ScheduleNameCell row={row} sx={whoSx} sizes={{ name: M.name, meta: M.meta, chipsTop: 0.75 }} />\n <Track row={row} ctx={ctx} />\n <Box sx={actionsSx}>{row.actions}</Box>\n </Box>\n );\n}\n\n/** \"Na escala, por hora\": a count per hour above the axis. */\nexport function Coverage(props: { coverage: readonly number[]; label?: string; scale: RulerScale; testId: string }): React.JSX.Element {\n const { coverage, label, scale, testId } = props;\n const hours = scale.to - scale.from;\n return (\n <Box sx={lineSx} data-testid={`${testId}-coverage`}>\n <Box component=\"span\" sx={coverageLabelSx}>\n {label}\n </Box>\n <Box sx={coverageSx(hours)}>\n {Array.from({ length: hours }, (_, index) => {\n const count = coverage[index] ?? 0;\n return (\n <Box key={index} component=\"span\" sx={coverageCellSx(count)} title={`${formatHour(scale.from + index)} · ${count}`}>\n {count > 0 ? count : ''}\n </Box>\n );\n })}\n </Box>\n <span />\n </Box>\n );\n}\n\n/** The hour labels under the coverage row. */\nexport function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {\n return (\n <Box sx={lineSx} data-testid={`${testId}-axis`} aria-hidden>\n <span />\n <Box sx={axisSx}>\n {ticksOf(scale.from, scale.to, scale.tickHours).map((h) => (\n <span key={h} style={{ left: `${percentOf(h, scale.from, scale.to)}%` }}>\n {formatHour(h)}\n </span>\n ))}\n </Box>\n <span />\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/DataViews/data-views-range-bounds.tsx"],"sourcesContent":["'use client';\n\n/**\n * The `De`/`Até` pair of a range filter, its preset chips, the line under it\n * and — for a day range — the calendar under that (FUT-4144). Shared by the\n * pill's popover, the slide-in panel and the \"Mais\" sheet, so the three cannot\n * drift on what a bound accepts or how it clears.\n */\nimport Box from '@mui/material/Box/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport React, { useState } from 'react';\n\nimport { useDataViewsCopy } from './data-views-copy-context';\nimport { DayBoundInput } from './data-views-day-input';\nimport { NumberBoundInput } from './data-views-number-input';\nimport { RangePresets } from './data-views-preset-chips';\nimport { DayRangeCalendar } from './data-views-range-calendar';\nimport { presetsFor } from './data-views-range-presets';\nimport { useOutsideResetKey } from './data-views-range-reset';\nimport { isRangeInverted } from './data-views-range-values';\nimport type { RangeFieldConfig, RangeValue } from './data-views-types';\nimport { sxRem } from '../../../tokens/relative';\n\n/**\n * One end of the range. A day keeps its `AAAA-MM-DD` string verbatim (the form\n * both the field and the backend speak) and is typed through the masked\n * `dd/mm/aaaa` field rather than a native date input — see\n * {@link DayBoundInput} for why. A number parses, and an unparseable/blank\n * entry CLEARS the bound rather than writing `NaN`.\n */\nfunction BoundField<T extends Record<string, unknown>>({\n field,\n label,\n value,\n onChange,\n testId,\n fill,\n}: {\n field: RangeFieldConfig<T>;\n label: string;\n value: number | string | undefined;\n onChange: (bound: number | string | undefined) => void;\n testId: string;\n /** A number box takes its cell's width; a day box always does. */\n fill: boolean;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n if (field.kind === 'day') {\n return (\n <DayBoundInput\n fill\n mask={copy.filters.dayMask}\n invalidMessage={copy.filters.dayInvalid}\n label={label}\n value={value == null ? undefined : String(value)}\n onChange={onChange}\n testId={testId}\n />\n );\n }\n return (\n <NumberBoundInput\n fill={fill}\n label={label}\n value={value}\n unit={field.unit}\n onChange={onChange}\n testId={testId}\n />\n );\n}\n\n\n/** One box of the pair: an equal share of the row for a day, as it was for a number. */\nfunction BoundCell({ grow, children }: { grow: boolean; children: React.ReactNode }): React.JSX.Element {\n if (!grow) return <>{children}</>;\n return <Box sx={{ flex: '1 1 0', minWidth: 0 }}>{children}</Box>;\n}\n\n/**\n * The line under the pair: why an inverted window matches nothing, or — for a\n * day range — that BOTH days are in the result. Saying so is FUT-668's other\n * half: the bounds compare as ISO strings precisely so `até` keeps its whole\n * day, and nothing on screen would otherwise tell the merchant that.\n */\nfunction RangeNote({\n day,\n inverted,\n testId,\n}: {\n day: boolean;\n inverted: boolean;\n testId: string;\n}): React.JSX.Element | null {\n const copy = useDataViewsCopy();\n if (inverted) {\n return (\n <Typography\n component=\"p\"\n data-testid={`${testId}-inverted`}\n sx={{ mt: 0.75, fontSize: sxRem(12), color: 'error.main' }}\n >\n {copy.filters.rangeInvalid}\n </Typography>\n );\n }\n if (!day) return null;\n return (\n <Typography\n component=\"p\"\n data-testid={`${testId}-inclusive`}\n // `text.secondary`, not `disabled`: the note is read, and disabled grey\n // at 12px is 2.68:1 (FUT-4144 UI review).\n sx={{ mt: { xs: 0.25, sm: 0.75 }, fontSize: sxRem(12), color: 'text.secondary' }}\n >\n {copy.filters.rangeInclusiveNote}\n </Typography>\n );\n}\n\n/** Props of {@link RangeBounds}. */\ninterface RangeBoundsProps<T extends Record<string, unknown>> {\n field: RangeFieldConfig<T>;\n value: RangeValue;\n onChange: (range: RangeValue) => void;\n testId: string;\n /**\n * Months the day calendar shows side by side (FUT-4144). Two where the\n * popover has the room; one, with the month/year selectors, on a phone and\n * in the panels.\n */\n calendarMonths?: 1 | 2;\n /**\n * Whether the day calendar is mounted. A host that keeps the pair mounted\n * while hidden (the slide-in panel) passes its open state, so a closed panel\n * holds no calendar of its own.\n */\n showCalendar?: boolean;\n /**\n * A NUMBER pair splits the row equally too, as a day pair always does. Set\n * by the panels, whose width is the row's; the pill's popover sizes itself to\n * its content and keeps the boxes' own width.\n */\n fillRow?: boolean;\n}\n\n/**\n * The `De`/`Até` pair for a range — shared by the pill's popover and the\n * slide-in panel's day fields, so the two surfaces cannot drift on what a bound\n * accepts or how it clears.\n */\nexport function RangeBounds<T extends Record<string, unknown>>({\n field,\n value,\n onChange,\n testId,\n calendarMonths = 1,\n showCalendar = true,\n fillRow = false,\n}: RangeBoundsProps<T>): React.JSX.Element {\n const copy = useDataViewsCopy();\n const { key, emitted } = useOutsideResetKey(value);\n // Moves the calendar to a TYPED day. A typed write is the fields' own, so\n // the outside key above does not see it; a tap on the calendar moves\n // neither, which is what keeps it still under the finger.\n const [typed, setTyped] = useState(0);\n const typedChange = (range: RangeValue) => {\n setTyped((n) => n + 1);\n onChange(emitted(range));\n };\n const day = field.kind === 'day';\n return (\n <Box data-testid={`${testId}-panel`}>\n {/* Above the inputs, not below: the presets are the answer for most\n merchants, and the two date pickers are the escape hatch. */}\n <Box sx={{ '&:not(:empty)': { mb: { xs: 1, sm: 1.5 } } }}>\n <RangePresets presets={presetsFor(field, copy)} value={value} onChange={onChange} testId={testId} />\n </Box>\n {/* Top-aligned: a day that does not exist grows ITS field by a message\n line, and centring would push the other box half a line down. The two\n boxes are one height, so with no message this is the same row. */}\n <Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.5 }}>\n {/* A day pair shares the row equally, so it spans the calendar under\n it; a number pair keeps its own widths. */}\n <BoundCell grow={day || fillRow}>\n <BoundField\n key={`min-${key}`}\n field={field}\n label={copy.filters.rangeStart}\n value={value.min}\n onChange={(min) => typedChange({ ...value, min })}\n testId={`${testId}-min`}\n fill={fillRow}\n />\n </BoundCell>\n <BoundCell grow={day || fillRow}>\n <BoundField\n key={`max-${key}`}\n field={field}\n label={copy.filters.rangeEnd}\n value={value.max}\n onChange={(max) => typedChange({ ...value, max })}\n testId={`${testId}-max`}\n fill={fillRow}\n />\n </BoundCell>\n </Box>\n <RangeNote day={day} inverted={isRangeInverted(value)} testId={testId} />\n {/* Under the boxes and the note, with no button to reach it: the typed\n path stays as short as it was, and a period is two taps (FUT-4144). */}\n {day && showCalendar && (\n <DayRangeCalendar\n fieldLabel={field.label}\n value={value}\n onChange={(range) => onChange(emitted(range))}\n months={calendarMonths}\n viewKey={`${key}-${typed}`}\n testId={testId}\n />\n )}\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|