@12-apps/ui 6.92.2 → 6.93.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 +10 -3
- package/components-guidelines.md +30 -0
- package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
- package/dist/_internal/components/data-display/AlertDialog/AlertDialog.js +2 -2
- package/dist/_internal/components/data-display/AlertDialog/AlertDialog.parts.js +9 -3
- package/dist/_internal/components/data-display/Avatar/Avatar.js +3 -3
- package/dist/_internal/components/data-display/Avatar/Avatar.view.js +2 -2
- 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 +10 -10
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +7 -7
- package/dist/_internal/components/data-display/Carousel/Carousel.js +3 -3
- package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
- package/dist/_internal/components/data-display/Chart/SpecChart.js +7 -7
- package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
- package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
- package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +34 -34
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +48 -48
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +7 -7
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +3 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +3 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +2 -2
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +28 -28
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +10 -10
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +8 -8
- package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +5 -5
- 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 +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +10 -10
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +6 -6
- package/dist/_internal/components/data-display/DataViews/list-card-rails.js +2 -2
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +4 -4
- package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.js +25 -0
- package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.js.map +1 -0
- package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.styles.js +23 -0
- package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.styles.js.map +1 -0
- package/dist/_internal/components/data-display/DayRuler/DayRuler.js +15 -13
- package/dist/_internal/components/data-display/DayRuler/DayRuler.keyboard.js +3 -3
- package/dist/_internal/components/data-display/DayRuler/DayRuler.metrics.js +1 -1
- package/dist/_internal/components/data-display/DayRuler/DayRuler.parts.js +12 -15
- package/dist/_internal/components/data-display/DayRuler/DayRuler.styles.js +3 -11
- package/dist/_internal/components/data-display/DayRuler/day-ruler-model.js +5 -1
- package/dist/_internal/components/data-display/DayRuler/useRulerDrag.js +3 -3
- package/dist/_internal/components/data-display/DayRuler/useTextFits.js +4 -3
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +1 -1
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +5 -5
- package/dist/_internal/components/data-display/Lightbox/LightboxControls.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/LightboxOverlay.js +3 -3
- package/dist/_internal/components/data-display/Lightbox/LightboxThumbnails.js +2 -2
- package/dist/_internal/components/data-display/RegionMap/RegionMap.js +2 -2
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +7 -7
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.parts.js +5 -5
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.styles.js +2 -2
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleNameCell.js +3 -3
- package/dist/_internal/components/data-display/ScheduleGrid/schedule-name.styles.js +2 -2
- package/dist/_internal/components/data-display/ScheduleGrid/schedule-tones.js +3 -1
- package/dist/_internal/components/data-display/Sheet/Sheet.hooks.js +2 -2
- package/dist/_internal/components/data-display/Sheet/Sheet.js +3 -3
- package/dist/_internal/components/data-display/Table/Table.js +3 -3
- package/dist/_internal/components/data-display/index.js +61 -59
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.js +4 -4
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.parts.js +3 -3
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +5 -5
- package/dist/_internal/components/feedback/ConfirmAction/with-confirmation.js +4 -4
- package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.frame.js +48 -0
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.frame.js.map +1 -0
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +6 -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 +15 -14
- package/dist/_internal/components/index.js +80 -77
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +4 -4
- 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 +13 -13
- package/dist/_internal/user-avatar/user-avatar.js +4 -4
- package/dist/charts.js +16 -16
- package/dist/{chunk-LDCZPZPD.js → chunk-24HHUCWB.js} +2 -2
- package/dist/{chunk-7ZQ3KQ7J.js → chunk-273OLFJD.js} +16 -6
- package/dist/chunk-273OLFJD.js.map +1 -0
- package/dist/{chunk-FAUNQ3FS.js → chunk-2M3W6ACW.js} +3 -3
- package/dist/{chunk-ALOEOGQV.js → chunk-3TZUZXY5.js} +2 -2
- package/dist/{chunk-4HK4FVMV.js → chunk-3UAHDLKH.js} +7 -1
- package/dist/{chunk-4HK4FVMV.js.map → chunk-3UAHDLKH.js.map} +1 -1
- package/dist/{chunk-DWH4TQXK.js → chunk-4E2RQOSQ.js} +2 -2
- package/dist/{chunk-4QR7LM5F.js → chunk-4J62V5OE.js} +7 -7
- package/dist/{chunk-OJZLFFG4.js → chunk-5AZEDQXX.js} +59 -70
- package/dist/chunk-5AZEDQXX.js.map +1 -0
- package/dist/{chunk-CZVAVX5L.js → chunk-6ATXOJVQ.js} +2 -2
- package/dist/{chunk-YE7M7URT.js → chunk-6D5AMANO.js} +2 -2
- package/dist/{chunk-Q3SILZTQ.js → chunk-6LGKJVOA.js} +2 -2
- package/dist/{chunk-ZCNR4DN5.js → chunk-7WE432RQ.js} +12 -12
- package/dist/{chunk-EOQ6EKTS.js → chunk-7XSCEHGA.js} +7 -7
- package/dist/{chunk-B5STMM3R.js → chunk-ALMGYCHY.js} +2 -2
- package/dist/{chunk-5357QUVE.js → chunk-AMLHEKSV.js} +5 -5
- package/dist/{chunk-3I5PHWKQ.js → chunk-ASEHJIIV.js} +6 -6
- package/dist/{chunk-64UNVW6D.js → chunk-BP3ZKIPE.js} +4 -4
- package/dist/{chunk-OCT5VCLP.js → chunk-BQBEN44R.js} +2 -2
- package/dist/{chunk-FYUNROLW.js → chunk-BZYLTGOE.js} +2 -2
- package/dist/{chunk-TZ44F6W7.js → chunk-CGS2LQE5.js} +2 -2
- package/dist/chunk-CPQJFHNZ.js +63 -0
- package/dist/chunk-CPQJFHNZ.js.map +1 -0
- package/dist/{chunk-PCQ5JWLF.js → chunk-CWXX5JR3.js} +14 -12
- package/dist/chunk-CWXX5JR3.js.map +1 -0
- package/dist/{chunk-6HXNFNOB.js → chunk-EE6DJPB7.js} +4 -4
- package/dist/{chunk-QE4KYG5I.js → chunk-F2UP5ZF4.js} +6 -6
- package/dist/{chunk-P53ANKZV.js → chunk-F7LBTMCA.js} +4 -4
- package/dist/{chunk-UDBOGSFO.js → chunk-FGZIEZEP.js} +67 -110
- package/dist/chunk-FGZIEZEP.js.map +1 -0
- package/dist/{chunk-IWSTMVCA.js → chunk-FKS2PNJF.js} +8 -8
- package/dist/{chunk-3WYEENUH.js → chunk-FPNVOB52.js} +7 -7
- package/dist/{chunk-E73C73UD.js → chunk-FYSS2PYA.js} +3 -3
- package/dist/{chunk-N557OEZR.js → chunk-G6NMSBSQ.js} +2 -2
- package/dist/{chunk-A5OERY43.js → chunk-GARM5SX6.js} +2 -2
- package/dist/{chunk-66VKX6JE.js → chunk-GXGHEOWC.js} +3 -3
- package/dist/{chunk-U2SAW6DH.js → chunk-GY53SDVL.js} +4 -4
- package/dist/{chunk-JVOEVTCZ.js → chunk-HQCZT6TO.js} +5 -5
- package/dist/{chunk-CNUO2BRV.js → chunk-HWZETMO4.js} +3 -3
- package/dist/{chunk-MM3QESLI.js → chunk-ILJ3FXIC.js} +2 -2
- package/dist/{chunk-FWTVIRZZ.js → chunk-JVQ56E3U.js} +6 -6
- package/dist/{chunk-YOLLISEY.js → chunk-JXK53XJW.js} +9 -9
- package/dist/{chunk-K6VEN5VN.js → chunk-K3PMMW23.js} +4 -4
- package/dist/{chunk-T5RFH2T5.js → chunk-KOGJBAFI.js} +2 -2
- package/dist/{chunk-COK65GX2.js → chunk-KURE5R7Z.js} +12 -2
- package/dist/chunk-KURE5R7Z.js.map +1 -0
- package/dist/{chunk-EKSJES5E.js → chunk-KXDLLS62.js} +3 -3
- package/dist/{chunk-LE5JYNR7.js → chunk-L5CAHQIN.js} +23 -2
- package/dist/chunk-L5CAHQIN.js.map +1 -0
- package/dist/{chunk-DBI6AMNY.js → chunk-LKWXU6NH.js} +4 -4
- package/dist/{chunk-56JF5PZK.js → chunk-LYVTJLCF.js} +4 -4
- package/dist/{chunk-NXQ6PXLJ.js → chunk-LZ5PGB7U.js} +2 -2
- package/dist/chunk-MPVH2GM5.js +83 -0
- package/dist/chunk-MPVH2GM5.js.map +1 -0
- package/dist/{chunk-5I75WRXT.js → chunk-N3GLX64O.js} +4 -4
- package/dist/{chunk-6DO672AG.js → chunk-N5PVD4J5.js} +7 -7
- package/dist/{chunk-PEAUGQKU.js → chunk-NQCTANF3.js} +7 -7
- package/dist/{chunk-ZYZPSBNQ.js → chunk-OGSTMHT6.js} +22 -7
- package/dist/chunk-OGSTMHT6.js.map +1 -0
- package/dist/{chunk-W676GY42.js → chunk-P772HCHI.js} +4 -4
- package/dist/{chunk-FQNDJITD.js → chunk-RCTHCXS6.js} +2 -2
- package/dist/{chunk-VWRXSBT2.js → chunk-SB2SEGR6.js} +3 -3
- package/dist/{chunk-Z2WZDVGG.js → chunk-SI5SKX52.js} +2 -2
- package/dist/{chunk-RYJ6XGFA.js → chunk-SJAOQGQI.js} +2 -2
- package/dist/{chunk-D7R6SRJB.js → chunk-SKQPUAMK.js} +3 -3
- package/dist/{chunk-AMIUZRDQ.js → chunk-SNWCT6JN.js} +3 -3
- package/dist/{chunk-7W5X3KQ2.js → chunk-SW7JCF4Z.js} +2 -2
- package/dist/{chunk-WLLUARTC.js → chunk-T2G4LDU5.js} +2 -2
- package/dist/{chunk-BNBWUXT3.js → chunk-TE6HAWDM.js} +2 -2
- package/dist/{chunk-JG733RMP.js → chunk-TZC6KIK2.js} +5 -5
- package/dist/{chunk-Y3F2F36I.js → chunk-U4Z6GZDW.js} +3 -3
- package/dist/{chunk-KQDBOZQE.js → chunk-UIIHUWOV.js} +13 -13
- package/dist/{chunk-L4V7FZJI.js → chunk-UKUGSU3Y.js} +2 -2
- package/dist/{chunk-D2JQF2WC.js → chunk-UOKXYV3U.js} +5 -5
- package/dist/{chunk-K7IDOTSU.js → chunk-VNMD6BA4.js} +2 -2
- package/dist/chunk-WG7CAT5D.js +65 -0
- package/dist/chunk-WG7CAT5D.js.map +1 -0
- package/dist/chunk-WSSD7H5E.js +52 -0
- package/dist/chunk-WSSD7H5E.js.map +1 -0
- package/dist/{chunk-DTZOP4ZR.js → chunk-XCUHGPPZ.js} +2 -2
- package/dist/{chunk-DBPFKGXX.js → chunk-Y7BOP7VZ.js} +3 -3
- package/dist/{chunk-BOADHYTA.js → chunk-YNZUYZ2O.js} +2 -2
- package/dist/{chunk-XRV4QFEQ.js → chunk-ZBUMGMWP.js} +83 -65
- package/dist/chunk-ZBUMGMWP.js.map +1 -0
- package/dist/{chunk-XNAI7QGU.js → chunk-ZM6FJCWS.js} +4 -4
- package/dist/{chunk-FC7UCFFA.js → chunk-ZPWPA6FR.js} +8 -8
- package/dist/data-display/Alert.js +2 -2
- package/dist/data-display/AlertDialog.js +2 -2
- package/dist/data-display/Avatar.js +3 -3
- package/dist/data-display/Badge.js +2 -2
- package/dist/data-display/CardKit.js +18 -18
- package/dist/data-display/Carousel.js +3 -3
- package/dist/data-display/Chart.js +16 -16
- package/dist/data-display/DataViews.js +66 -66
- package/dist/data-display/DayRuler.js +15 -13
- package/dist/data-display/HoverCard.js +1 -1
- package/dist/data-display/Lightbox.js +5 -5
- package/dist/data-display/RegionMap.js +2 -2
- package/dist/data-display/ScheduleGrid.js +9 -9
- package/dist/data-display/Sheet.js +3 -3
- package/dist/data-display/Table.js +3 -3
- package/dist/feedback/ConfirmAction.js +8 -8
- package/dist/feedback/NotificationStatusNotice.js +3 -3
- package/dist/feedback/SequenceResolver.js +6 -5
- package/dist/navigation/AppHeader.js +4 -4
- package/dist/navigation/SectionNav.js +9 -9
- package/dist/types/components/data-display/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.d.ts +11 -0
- package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.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.footer.d.ts +16 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.footer.d.ts.map +1 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.footer.styles.d.ts +18 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.footer.styles.d.ts.map +1 -0
- package/dist/types/components/data-display/DayRuler/DayRuler.metrics.d.ts +15 -5
- package/dist/types/components/data-display/DayRuler/DayRuler.metrics.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts +13 -14
- package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts +20 -23
- 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 +13 -3
- package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/day-ruler-model.d.ts +27 -0
- package/dist/types/components/data-display/DayRuler/day-ruler-model.d.ts.map +1 -1
- package/dist/types/components/data-display/DayRuler/useTextFits.d.ts +11 -9
- package/dist/types/components/data-display/DayRuler/useTextFits.d.ts.map +1 -1
- package/dist/types/components/data-display/ScheduleGrid/schedule-tones.d.ts +8 -0
- package/dist/types/components/data-display/ScheduleGrid/schedule-tones.d.ts.map +1 -1
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.d.ts +55 -2
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.d.ts.map +1 -1
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.frame.d.ts +16 -0
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.frame.d.ts.map +1 -0
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.types.d.ts +10 -4
- package/dist/types/components/feedback/SequenceResolver/SequenceResolver.types.d.ts.map +1 -1
- package/dist/user-avatar.js +4 -4
- package/package.json +1 -1
- package/src/components/data-display/AlertDialog/AlertDialog.md +25 -1
- package/src/components/data-display/AlertDialog/AlertDialog.parts.tsx +28 -0
- package/src/components/data-display/AlertDialog/AlertDialog.tsx +34 -5
- package/src/components/data-display/DayRuler/DayRuler.footer.styles.ts +54 -0
- package/src/components/data-display/DayRuler/DayRuler.footer.tsx +123 -0
- package/src/components/data-display/DayRuler/DayRuler.md +37 -13
- package/src/components/data-display/DayRuler/DayRuler.metrics.ts +15 -5
- package/src/components/data-display/DayRuler/DayRuler.parts.tsx +86 -155
- package/src/components/data-display/DayRuler/DayRuler.styles.ts +89 -83
- package/src/components/data-display/DayRuler/DayRuler.tsx +3 -2
- package/src/components/data-display/DayRuler/DayRuler.types.ts +13 -3
- package/src/components/data-display/DayRuler/day-ruler-model.ts +45 -0
- package/src/components/data-display/DayRuler/useTextFits.ts +68 -25
- package/src/components/data-display/ScheduleGrid/schedule-tones.ts +13 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.md +10 -0
- package/src/components/feedback/SequenceResolver/SequenceResolver.frame.tsx +71 -0
- package/src/components/feedback/SequenceResolver/SequenceResolver.md +2 -2
- package/src/components/feedback/SequenceResolver/SequenceResolver.tsx +122 -61
- package/src/components/feedback/SequenceResolver/SequenceResolver.types.ts +10 -4
- package/dist/chunk-7ZQ3KQ7J.js.map +0 -1
- package/dist/chunk-COK65GX2.js.map +0 -1
- package/dist/chunk-LE5JYNR7.js.map +0 -1
- package/dist/chunk-OJZLFFG4.js.map +0 -1
- package/dist/chunk-PCQ5JWLF.js.map +0 -1
- package/dist/chunk-UDBOGSFO.js.map +0 -1
- package/dist/chunk-XRV4QFEQ.js.map +0 -1
- package/dist/chunk-YMYJO2CZ.js +0 -32
- package/dist/chunk-YMYJO2CZ.js.map +0 -1
- package/dist/chunk-ZYZPSBNQ.js.map +0 -1
- /package/dist/{chunk-LDCZPZPD.js.map → chunk-24HHUCWB.js.map} +0 -0
- /package/dist/{chunk-FAUNQ3FS.js.map → chunk-2M3W6ACW.js.map} +0 -0
- /package/dist/{chunk-ALOEOGQV.js.map → chunk-3TZUZXY5.js.map} +0 -0
- /package/dist/{chunk-DWH4TQXK.js.map → chunk-4E2RQOSQ.js.map} +0 -0
- /package/dist/{chunk-4QR7LM5F.js.map → chunk-4J62V5OE.js.map} +0 -0
- /package/dist/{chunk-CZVAVX5L.js.map → chunk-6ATXOJVQ.js.map} +0 -0
- /package/dist/{chunk-YE7M7URT.js.map → chunk-6D5AMANO.js.map} +0 -0
- /package/dist/{chunk-Q3SILZTQ.js.map → chunk-6LGKJVOA.js.map} +0 -0
- /package/dist/{chunk-ZCNR4DN5.js.map → chunk-7WE432RQ.js.map} +0 -0
- /package/dist/{chunk-EOQ6EKTS.js.map → chunk-7XSCEHGA.js.map} +0 -0
- /package/dist/{chunk-B5STMM3R.js.map → chunk-ALMGYCHY.js.map} +0 -0
- /package/dist/{chunk-5357QUVE.js.map → chunk-AMLHEKSV.js.map} +0 -0
- /package/dist/{chunk-3I5PHWKQ.js.map → chunk-ASEHJIIV.js.map} +0 -0
- /package/dist/{chunk-64UNVW6D.js.map → chunk-BP3ZKIPE.js.map} +0 -0
- /package/dist/{chunk-OCT5VCLP.js.map → chunk-BQBEN44R.js.map} +0 -0
- /package/dist/{chunk-FYUNROLW.js.map → chunk-BZYLTGOE.js.map} +0 -0
- /package/dist/{chunk-TZ44F6W7.js.map → chunk-CGS2LQE5.js.map} +0 -0
- /package/dist/{chunk-6HXNFNOB.js.map → chunk-EE6DJPB7.js.map} +0 -0
- /package/dist/{chunk-QE4KYG5I.js.map → chunk-F2UP5ZF4.js.map} +0 -0
- /package/dist/{chunk-P53ANKZV.js.map → chunk-F7LBTMCA.js.map} +0 -0
- /package/dist/{chunk-IWSTMVCA.js.map → chunk-FKS2PNJF.js.map} +0 -0
- /package/dist/{chunk-3WYEENUH.js.map → chunk-FPNVOB52.js.map} +0 -0
- /package/dist/{chunk-E73C73UD.js.map → chunk-FYSS2PYA.js.map} +0 -0
- /package/dist/{chunk-N557OEZR.js.map → chunk-G6NMSBSQ.js.map} +0 -0
- /package/dist/{chunk-A5OERY43.js.map → chunk-GARM5SX6.js.map} +0 -0
- /package/dist/{chunk-66VKX6JE.js.map → chunk-GXGHEOWC.js.map} +0 -0
- /package/dist/{chunk-U2SAW6DH.js.map → chunk-GY53SDVL.js.map} +0 -0
- /package/dist/{chunk-JVOEVTCZ.js.map → chunk-HQCZT6TO.js.map} +0 -0
- /package/dist/{chunk-CNUO2BRV.js.map → chunk-HWZETMO4.js.map} +0 -0
- /package/dist/{chunk-MM3QESLI.js.map → chunk-ILJ3FXIC.js.map} +0 -0
- /package/dist/{chunk-FWTVIRZZ.js.map → chunk-JVQ56E3U.js.map} +0 -0
- /package/dist/{chunk-YOLLISEY.js.map → chunk-JXK53XJW.js.map} +0 -0
- /package/dist/{chunk-K6VEN5VN.js.map → chunk-K3PMMW23.js.map} +0 -0
- /package/dist/{chunk-T5RFH2T5.js.map → chunk-KOGJBAFI.js.map} +0 -0
- /package/dist/{chunk-EKSJES5E.js.map → chunk-KXDLLS62.js.map} +0 -0
- /package/dist/{chunk-DBI6AMNY.js.map → chunk-LKWXU6NH.js.map} +0 -0
- /package/dist/{chunk-56JF5PZK.js.map → chunk-LYVTJLCF.js.map} +0 -0
- /package/dist/{chunk-NXQ6PXLJ.js.map → chunk-LZ5PGB7U.js.map} +0 -0
- /package/dist/{chunk-5I75WRXT.js.map → chunk-N3GLX64O.js.map} +0 -0
- /package/dist/{chunk-6DO672AG.js.map → chunk-N5PVD4J5.js.map} +0 -0
- /package/dist/{chunk-PEAUGQKU.js.map → chunk-NQCTANF3.js.map} +0 -0
- /package/dist/{chunk-W676GY42.js.map → chunk-P772HCHI.js.map} +0 -0
- /package/dist/{chunk-FQNDJITD.js.map → chunk-RCTHCXS6.js.map} +0 -0
- /package/dist/{chunk-VWRXSBT2.js.map → chunk-SB2SEGR6.js.map} +0 -0
- /package/dist/{chunk-Z2WZDVGG.js.map → chunk-SI5SKX52.js.map} +0 -0
- /package/dist/{chunk-RYJ6XGFA.js.map → chunk-SJAOQGQI.js.map} +0 -0
- /package/dist/{chunk-D7R6SRJB.js.map → chunk-SKQPUAMK.js.map} +0 -0
- /package/dist/{chunk-AMIUZRDQ.js.map → chunk-SNWCT6JN.js.map} +0 -0
- /package/dist/{chunk-7W5X3KQ2.js.map → chunk-SW7JCF4Z.js.map} +0 -0
- /package/dist/{chunk-WLLUARTC.js.map → chunk-T2G4LDU5.js.map} +0 -0
- /package/dist/{chunk-BNBWUXT3.js.map → chunk-TE6HAWDM.js.map} +0 -0
- /package/dist/{chunk-JG733RMP.js.map → chunk-TZC6KIK2.js.map} +0 -0
- /package/dist/{chunk-Y3F2F36I.js.map → chunk-U4Z6GZDW.js.map} +0 -0
- /package/dist/{chunk-KQDBOZQE.js.map → chunk-UIIHUWOV.js.map} +0 -0
- /package/dist/{chunk-L4V7FZJI.js.map → chunk-UKUGSU3Y.js.map} +0 -0
- /package/dist/{chunk-D2JQF2WC.js.map → chunk-UOKXYV3U.js.map} +0 -0
- /package/dist/{chunk-K7IDOTSU.js.map → chunk-VNMD6BA4.js.map} +0 -0
- /package/dist/{chunk-DTZOP4ZR.js.map → chunk-XCUHGPPZ.js.map} +0 -0
- /package/dist/{chunk-DBPFKGXX.js.map → chunk-Y7BOP7VZ.js.map} +0 -0
- /package/dist/{chunk-BOADHYTA.js.map → chunk-YNZUYZ2O.js.map} +0 -0
- /package/dist/{chunk-XNAI7QGU.js.map → chunk-ZM6FJCWS.js.map} +0 -0
- /package/dist/{chunk-FC7UCFFA.js.map → chunk-ZPWPA6FR.js.map} +0 -0
|
@@ -14,9 +14,14 @@ export interface DayRulerBlock {
|
|
|
14
14
|
id: string;
|
|
15
15
|
start: RulerHour;
|
|
16
16
|
end: RulerHour;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* What the block says ("↻ 20:39–00:00", "Ausente"). One line, drawn whole or
|
|
19
|
+
* not at all: a bar too short for it says `shortLabel`, and one too short
|
|
20
|
+
* even for that says nothing — never a cut text (FUT-3831). Either way it
|
|
21
|
+
* stays the block's tooltip (unless `title`) and its accessible name.
|
|
22
|
+
*/
|
|
18
23
|
label: string;
|
|
19
|
-
/** What a
|
|
24
|
+
/** What a block too short for its `label` says instead, at any width — its start ("20:39"). */
|
|
20
25
|
shortLabel?: string;
|
|
21
26
|
tone: ScheduleBlockTone;
|
|
22
27
|
/** Its middle can be dragged along the row (and Alt+←/→ moves it). */
|
|
@@ -53,7 +58,12 @@ export interface DayRulerRow {
|
|
|
53
58
|
nameAdornment?: React.ReactNode;
|
|
54
59
|
/** Under the meta: the host's chips (praça, "Em pausa desde …"). One line; a chip that does not fit shrinks, its label cut. */
|
|
55
60
|
chips?: React.ReactNode;
|
|
56
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* The row's own buttons ("Encerrar turno", "Iniciar agora"): under the name
|
|
63
|
+
* on a computer, beside it when the row stacks. They take no column of their
|
|
64
|
+
* own, so every track keeps the same width and the hours stay aligned
|
|
65
|
+
* (FUT-3831).
|
|
66
|
+
*/
|
|
57
67
|
actions?: React.ReactNode;
|
|
58
68
|
blocks: readonly DayRulerBlock[];
|
|
59
69
|
actual?: DayRulerActual;
|
|
@@ -81,3 +81,48 @@ export function ticksOf(from: RulerHour, to: RulerHour, step: number): RulerHour
|
|
|
81
81
|
for (let h = from; h <= to + 1e-9; h += step) out.push(h);
|
|
82
82
|
return out;
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/** A bar along the track: where it starts and how long it is, both in % of the track. */
|
|
86
|
+
export interface TrackSpan {
|
|
87
|
+
at: number;
|
|
88
|
+
share: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* How far each bar's hit band may reach, in % of the track (FUT-3831): from
|
|
93
|
+
* `lo` to `hi`, never past the track's ends and never past the MIDPOINT of
|
|
94
|
+
* the gap to the bar beside it, so a band widened round a short bar cannot
|
|
95
|
+
* take a tap meant for its neighbour. Two bars that touch or overlap give each
|
|
96
|
+
* other nothing: the band stays on the bar's own edge on that side.
|
|
97
|
+
*
|
|
98
|
+
* The band itself is drawn in CSS — at least the hit area wide, centred on the
|
|
99
|
+
* bar, then cut to `lo…hi` — because the bar's width in px is the layout's.
|
|
100
|
+
*/
|
|
101
|
+
export function hitBoundsOf(spans: readonly TrackSpan[]): { lo: number; hi: number }[] {
|
|
102
|
+
const order = spans.map((_, index) => index).sort((a, b) => spans[a]!.at - spans[b]!.at);
|
|
103
|
+
const out = spans.map(() => ({ lo: 0, hi: 100 }));
|
|
104
|
+
for (let k = 1; k < order.length; k += 1) {
|
|
105
|
+
const left = spans[order[k - 1]!]!;
|
|
106
|
+
const right = spans[order[k]!]!;
|
|
107
|
+
const leftEnd = left.at + left.share;
|
|
108
|
+
const mid = leftEnd <= right.at ? (leftEnd + right.at) / 2 : null;
|
|
109
|
+
out[order[k - 1]!]!.hi = Math.min(out[order[k - 1]!]!.hi, mid ?? leftEnd);
|
|
110
|
+
out[order[k]!]!.lo = Math.max(out[order[k]!]!.lo, mid ?? right.at);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** What a block can say in its bar: its full text, its start, or nothing. */
|
|
116
|
+
export type BlockTextFit = 'full' | 'short' | 'none';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Which of a block's texts fits the room it leaves inside the bar, by pixels
|
|
120
|
+
* (FUT-3831): the full one, else the short one, else NOTHING — a cut "12:"
|
|
121
|
+
* says less than an empty bar whose title and accessible name carry the text.
|
|
122
|
+
*/
|
|
123
|
+
export function textFitOf(fullWidth: number, shortWidth: number, room: number): BlockTextFit {
|
|
124
|
+
const slack = 0.5;
|
|
125
|
+
if (fullWidth <= room + slack) return 'full';
|
|
126
|
+
if (shortWidth <= room + slack) return 'short';
|
|
127
|
+
return 'none';
|
|
128
|
+
}
|
|
@@ -1,41 +1,84 @@
|
|
|
1
1
|
import type * as React from 'react';
|
|
2
2
|
import { useLayoutEffect, useState } from 'react';
|
|
3
3
|
|
|
4
|
+
import { textFitOf, type BlockTextFit } from './day-ruler-model';
|
|
5
|
+
|
|
6
|
+
/** A text span's own width (its laid-out text, whatever the span's own box). */
|
|
7
|
+
function textWidth(span: HTMLElement | null): number {
|
|
8
|
+
if (!span) return Number.POSITIVE_INFINITY;
|
|
9
|
+
const range = span.ownerDocument.createRange();
|
|
10
|
+
range.selectNodeContents(span);
|
|
11
|
+
return range.getBoundingClientRect().width;
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
15
|
+
* The block's fit, measured in ONE layout: every write first (both texts
|
|
16
|
+
* shown for the measurement alone), then every read, then the writes undone —
|
|
17
|
+
* so a ruler of many blocks forces one layout per block, not one per text.
|
|
18
|
+
* `null` when there is nothing to measure yet.
|
|
19
|
+
*/
|
|
20
|
+
function measureFit(el: HTMLElement): BlockTextFit | null {
|
|
21
|
+
const full = el.querySelector<HTMLElement>('[data-dr-text="full"]');
|
|
22
|
+
if (!full) return null;
|
|
23
|
+
const short = el.querySelector<HTMLElement>('[data-dr-text="short"]');
|
|
24
|
+
const spans = [full, short].filter((span): span is HTMLElement => span !== null);
|
|
25
|
+
const shown = spans.map((span) => span.style.display);
|
|
26
|
+
for (const span of spans) span.style.display = 'inline';
|
|
27
|
+
const fullWidth = textWidth(full);
|
|
28
|
+
const shortWidth = textWidth(short);
|
|
29
|
+
const style = getComputedStyle(el);
|
|
30
|
+
const outer = el.clientWidth;
|
|
31
|
+
const room = outer - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);
|
|
32
|
+
spans.forEach((span, index) => {
|
|
33
|
+
span.style.display = shown[index] ?? '';
|
|
34
|
+
});
|
|
35
|
+
if (room > 0) return textFitOf(fullWidth, shortWidth, room);
|
|
36
|
+
// A bar with no room at all (narrower than its own padding) says nothing.
|
|
37
|
+
return outer > 0 ? 'none' : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Which of a block's texts fits it, by pixels (FUT-3825, FUT-3831): the full
|
|
42
|
+
* text's and the short text's own widths against the room the block leaves
|
|
43
|
+
* them. A wide block keeps "18:00–23:00"; one too short for it says its start
|
|
44
|
+
* ("18:00"); one too short even for that says nothing — never a cut "18:".
|
|
9
45
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
46
|
+
* Measured at once (synchronously, so nothing is painted), then again on the
|
|
47
|
+
* next frame whenever the block is resized, and once more when the page's
|
|
48
|
+
* fonts have loaded: a webfont arriving late changes every text's width, and
|
|
49
|
+
* a fit read in the fallback font would leave a label cut. Where nothing can
|
|
50
|
+
* be measured (no `ResizeObserver`) it answers `fallback`.
|
|
14
51
|
*/
|
|
15
|
-
export function
|
|
16
|
-
const [
|
|
52
|
+
export function useTextFit(block: React.RefObject<HTMLElement | null>, text: string, short: string, fallback: BlockTextFit): BlockTextFit {
|
|
53
|
+
const [fit, setFit] = useState<BlockTextFit>(fallback);
|
|
17
54
|
|
|
18
55
|
useLayoutEffect(() => {
|
|
19
56
|
const el = block.current;
|
|
20
57
|
if (!el || typeof ResizeObserver === 'undefined') return undefined;
|
|
58
|
+
let live = true;
|
|
59
|
+
let frame = 0;
|
|
21
60
|
const measure = (): void => {
|
|
22
|
-
|
|
23
|
-
if (!
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (room > 0) setFits(width <= room + 0.5);
|
|
61
|
+
frame = 0;
|
|
62
|
+
if (!live) return;
|
|
63
|
+
const next = measureFit(el);
|
|
64
|
+
if (next) setFit(next);
|
|
65
|
+
};
|
|
66
|
+
// Resizes and the fonts come in bursts: one measurement per frame.
|
|
67
|
+
const schedule = (): void => {
|
|
68
|
+
if (frame || !live) return;
|
|
69
|
+
frame = typeof requestAnimationFrame === 'function' ? requestAnimationFrame(measure) : 0;
|
|
70
|
+
if (!frame) measure();
|
|
33
71
|
};
|
|
34
72
|
measure();
|
|
35
|
-
const observer = new ResizeObserver(
|
|
73
|
+
const observer = new ResizeObserver(schedule);
|
|
36
74
|
observer.observe(el);
|
|
37
|
-
|
|
38
|
-
|
|
75
|
+
el.ownerDocument.fonts?.ready.then(schedule, () => undefined);
|
|
76
|
+
return () => {
|
|
77
|
+
live = false;
|
|
78
|
+
observer.disconnect();
|
|
79
|
+
if (frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(frame);
|
|
80
|
+
};
|
|
81
|
+
}, [block, text, short]);
|
|
39
82
|
|
|
40
|
-
return
|
|
83
|
+
return fit;
|
|
41
84
|
}
|
|
@@ -108,6 +108,19 @@ export function blockToneStyles(theme: Theme, tone: ScheduleBlockTone, dashedPla
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* An OPAQUE ground for a ruler block's label where the block's own fill is
|
|
113
|
+
* not opaque (the dashed plan's faint wash, the away / uncovered outline): the
|
|
114
|
+
* fill flattened over the cell's ground, so the label reads the same and
|
|
115
|
+
* whatever the track draws under the block — the "agora" line — never shows
|
|
116
|
+
* through the text (FUT-3831). An opaque fill needs none: `undefined`.
|
|
117
|
+
*/
|
|
118
|
+
export function dashedLabelGround(theme: Theme, tone: ScheduleBlockTone): string | undefined {
|
|
119
|
+
if (tone === 'away' || tone === 'uncovered') return cellGround(theme);
|
|
120
|
+
if (tone === 'plan') return flatten(cellGround(theme), theme.palette.action.hover);
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
111
124
|
/**
|
|
112
125
|
* A break inside a block (a meal break): the page's own ground with diagonal
|
|
113
126
|
* stripes of the disabled action colour, opaque, so the block reads as CUT there in any
|
|
@@ -29,6 +29,16 @@ Whichever of the three entry points you use, you get the same contract:
|
|
|
29
29
|
- **The popup names the thing.** `entityName` is echoed into the body, so it
|
|
30
30
|
reads "Bebidas", not "este item".
|
|
31
31
|
|
|
32
|
+
## On a phone
|
|
33
|
+
|
|
34
|
+
The popup is an `AlertDialog`, so it takes that dialog's narrow layout
|
|
35
|
+
(FUT-3831): given less than 480px — a container query, never a media query —
|
|
36
|
+
the icon moves above the title so the question has the paper's whole width, and
|
|
37
|
+
the buttons stack each across the row, **the verb on top and Cancel at the
|
|
38
|
+
bottom**, the library's stacked order (`components-guidelines.md`, "Stacked
|
|
39
|
+
buttons"). The keyboard contract is unchanged: a destructive popup still opens
|
|
40
|
+
with focus on Cancel, and Cancel is still the first button to tab to.
|
|
41
|
+
|
|
32
42
|
## Three entry points
|
|
33
43
|
|
|
34
44
|
### `withConfirmation(Component, defaults?)` — the HOC
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import Box from '@mui/material/Box/index.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Dialog } from '../Dialog';
|
|
5
|
+
import { SequenceHeader } from './SequenceResolver.header';
|
|
6
|
+
import { chromeSx, columnSx, paperSx, scrollBodySx } from './SequenceResolver.styles';
|
|
7
|
+
import type { SequenceResolverLabels } from './SequenceResolver.types';
|
|
8
|
+
import type { Screen } from './SequenceResolver';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Esc closes. `persistent` turns MUI's own Esc off together with the veil's
|
|
12
|
+
* click (the prototype's veil does nothing), so Esc is put back here; a popup
|
|
13
|
+
* inside a step that handles its own Esc stops it before it gets here.
|
|
14
|
+
*/
|
|
15
|
+
function escapeCloses(close: () => void) {
|
|
16
|
+
return (event: React.KeyboardEvent): void => {
|
|
17
|
+
if (event.key !== 'Escape' || event.defaultPrevented) return;
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
close();
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ResolverFrameProps {
|
|
24
|
+
shown: boolean;
|
|
25
|
+
/** The kept last step is playing its exit: a picture, nothing on it acts. */
|
|
26
|
+
exiting: boolean;
|
|
27
|
+
touch: boolean;
|
|
28
|
+
titleId: string;
|
|
29
|
+
testId: string;
|
|
30
|
+
labels: SequenceResolverLabels;
|
|
31
|
+
screen: Screen;
|
|
32
|
+
close: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function swallowClick(event: React.SyntheticEvent): void {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
event.stopPropagation();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function ResolverFrame(props: ResolverFrameProps): React.JSX.Element {
|
|
41
|
+
const { shown, exiting, touch, titleId, testId, labels, screen, close } = props;
|
|
42
|
+
return (
|
|
43
|
+
<Dialog
|
|
44
|
+
open={shown}
|
|
45
|
+
onClose={close}
|
|
46
|
+
persistent
|
|
47
|
+
variant={touch ? 'bottom-sheet' : 'default'}
|
|
48
|
+
size="sm"
|
|
49
|
+
aria-labelledby={titleId}
|
|
50
|
+
onKeyDown={escapeCloses(close)}
|
|
51
|
+
slotProps={{ paper: { sx: paperSx(touch) } }}
|
|
52
|
+
dataTestId={testId}
|
|
53
|
+
>
|
|
54
|
+
<Box sx={columnSx} inert={exiting} aria-hidden={exiting || undefined} onClickCapture={exiting ? swallowClick : undefined}>
|
|
55
|
+
<Box sx={chromeSx}>
|
|
56
|
+
<SequenceHeader
|
|
57
|
+
touch={touch}
|
|
58
|
+
{...screen.header}
|
|
59
|
+
closeLabel={labels.close}
|
|
60
|
+
onClose={close}
|
|
61
|
+
progressLabel={labels.progress}
|
|
62
|
+
testId={testId}
|
|
63
|
+
/>
|
|
64
|
+
</Box>
|
|
65
|
+
<Box sx={scrollBodySx} data-testid={`${testId}-body`}>
|
|
66
|
+
{screen.body}
|
|
67
|
+
</Box>
|
|
68
|
+
</Box>
|
|
69
|
+
</Dialog>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -47,7 +47,7 @@ data-sized and shrinks while you go.
|
|
|
47
47
|
| Prop | Type | Description |
|
|
48
48
|
|---|---|---|
|
|
49
49
|
| `open` | `boolean` | Shows the resolver. Every opening — with an intro or without — starts a fresh run. |
|
|
50
|
-
| `onClose` | `(summary) => void` | Close button, Esc and the end screen's button. Gets `{ resolved, skipped, total, alsoResolved }`; a close toast counts `resolved + alsoResolved` (and shows only when that is above 0). Clicking the veil does nothing (as approved). |
|
|
50
|
+
| `onClose` | `(summary) => void` | Close button, Esc and the end screen's button — or, with `done: null`, the queue running out. Gets `{ resolved, skipped, total, alsoResolved }`; a close toast counts `resolved + alsoResolved` (and shows only when that is above 0). Clicking the veil does nothing (as approved). |
|
|
51
51
|
| `intro` | `SequenceIntro?` | While given, the intro shows instead of the run: `title`, `description`, `categories` (`label`, `description`, `count`, `onPick`), `startAllLabel`, `onStartAll`. The host drops it and sets `queue` in the same update as a pick. |
|
|
52
52
|
| `queue` | `readonly I[]` (`I extends { key; kindLabel }`) | The problems, in order. Live data: it may shrink (siblings answered together) or grow mid-run. |
|
|
53
53
|
| `renderStep` | `(item, api) => SequenceStep` | The current step: `content`, `primary?`, `secondary?` (default: skip with `labels.skip`; `false` removes it), `tertiary?`, `footer?` (`'end'` default — right-aligned over a hairline; `'split'` — two equal columns, no hairline; on a phone the actions always share the row without a hairline), `canGoBack?` (`false`: this answer has no inverse — once it is answered, Back is not offered from the next step). |
|
|
@@ -55,7 +55,7 @@ data-sized and shrinks while you go.
|
|
|
55
55
|
| `canGoBack` | `boolean` (default `true`) | Offer Back at all. It shows only once a previous step exists — on steps and on the end screen, never on the intro. |
|
|
56
56
|
| `overview` | `SequenceOverview?` | "Ver todos (N)" inside the run (FUT-3825): `label(total)`, `title`, `categories` (`label`, `description?`, `count?` — the host's bookkeeping, not drawn: the `label` says the number, "Atrasado · 1" — `keys`, `onPick?`), `pickLabel`, `continueLabel`. See "Ver todos". |
|
|
57
57
|
| `initialKey` | `string?` | Open the run at this problem (a click on its block). The ones before it follow it; nothing is passed over. |
|
|
58
|
-
| `done` | `SequenceDone \| (summary) => SequenceDone` | The end screen: `title`, `description?`, `confirmLabel`. |
|
|
58
|
+
| `done` | `SequenceDone \| ((summary) => SequenceDone) \| null` | The end screen: `title`, `description?`, `confirmLabel`. **`null` drops it** (FUT-3833): when the queue runs out — the last step answered or passed over, or the host's queue emptied under the run — the resolver calls `onClose(summary)` once (StrictMode included) and starts closing, its exit playing over the last step as it was; the host's toast is the run's last word. A pending last write keeps the step until it returns; a refused one keeps it with its message and does not close. Never fires from the intro. |
|
|
59
59
|
| `labels` | `SequenceResolverLabels` | Every chrome word: `back`, `backAccessible?`, `close`, `skip`, `step(position, total, item)`, `introStep`, `doneStep`, `progress`, `failed`. **Required — the package ships no copy.** |
|
|
60
60
|
| `dataTestId` | `string` (default `sequence-resolver`) | Parts: `-back`, `-close`, `-step-label`, `-progress`, `-handle`, `-category-<n>`, `-start-all`, `-primary`, `-secondary`, `-tertiary`, `-footer`, `-error`, `-done`, `-done-confirm`, `-see-all`, `-overview`, `-overview-pick-<n>`, `-continue`. |
|
|
61
61
|
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import Box from '@mui/material/Box/index.js';
|
|
4
3
|
import { useTheme } from '@mui/material/styles/index.js';
|
|
5
4
|
import useMediaQuery from '@mui/material/useMediaQuery/index.js';
|
|
6
|
-
import React, { useCallback, useId, useRef, useState } from 'react';
|
|
5
|
+
import React, { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
7
6
|
|
|
8
|
-
import { Dialog } from '../Dialog';
|
|
9
7
|
import { canReturn, currentOf, jumpTargetOf, positionOf, progressOf, summaryOf, type RunState } from './sequence-model';
|
|
10
8
|
import {
|
|
11
9
|
SequenceDoneBody,
|
|
12
10
|
SequenceIntroBody,
|
|
13
11
|
SequenceStepBody,
|
|
14
12
|
} from './SequenceResolver.parts';
|
|
15
|
-
import { SequenceHeader } from './SequenceResolver.header';
|
|
16
13
|
import { SequenceOverviewBody } from './SequenceResolver.overview';
|
|
14
|
+
import { ResolverFrame } from './SequenceResolver.frame';
|
|
17
15
|
import { SEQUENCE_METRICS } from './SequenceResolver.metrics';
|
|
18
|
-
import { chromeSx, columnSx, paperSx, scrollBodySx } from './SequenceResolver.styles';
|
|
19
16
|
import type {
|
|
20
17
|
SequenceItem,
|
|
21
18
|
SequenceOverviewCategory,
|
|
@@ -124,19 +121,6 @@ function headerOf<I extends SequenceItem, R>(inputs: HeaderInputs<I, R>) {
|
|
|
124
121
|
return { ...base, back: backOf(props, state), seeAll: overview.button };
|
|
125
122
|
}
|
|
126
123
|
|
|
127
|
-
/**
|
|
128
|
-
* Esc closes. `persistent` turns MUI's own Esc off together with the veil's
|
|
129
|
-
* click (the prototype's veil does nothing), so Esc is put back here; a popup
|
|
130
|
-
* inside a step that handles its own Esc stops it before it gets here.
|
|
131
|
-
*/
|
|
132
|
-
function escapeCloses(close: () => void) {
|
|
133
|
-
return (event: React.KeyboardEvent): void => {
|
|
134
|
-
if (event.key !== 'Escape' || event.defaultPrevented) return;
|
|
135
|
-
event.stopPropagation();
|
|
136
|
-
close();
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
124
|
interface BodyProps<I extends SequenceItem, R> {
|
|
141
125
|
props: SequenceResolverProps<I, R>;
|
|
142
126
|
state: SequenceRun<I, R>;
|
|
@@ -193,6 +177,8 @@ function SequenceBody<I extends SequenceItem, R>(body: BodyProps<I, R>): React.J
|
|
|
193
177
|
);
|
|
194
178
|
}
|
|
195
179
|
if (stepBody) return <>{stepBody}</>;
|
|
180
|
+
// `done: null`: no end screen — the resolver is closing over its last step (see `useExitScreen`).
|
|
181
|
+
if (props.done === null) return <></>;
|
|
196
182
|
const summary = summaryOf(state.run);
|
|
197
183
|
return (
|
|
198
184
|
<SequenceDoneBody
|
|
@@ -207,10 +193,94 @@ function SequenceBody<I extends SequenceItem, R>(body: BodyProps<I, R>): React.J
|
|
|
207
193
|
);
|
|
208
194
|
}
|
|
209
195
|
|
|
196
|
+
/**
|
|
197
|
+
* With `done: null`, the run's end closes the resolver: `onClose(summary)`
|
|
198
|
+
* once, when the queue runs out while it is open and past the intro. Returns
|
|
199
|
+
* whether the run has ended that way, so the dialog starts closing in the same
|
|
200
|
+
* render (and keeps its last screen for the exit, see `useExitScreen`).
|
|
201
|
+
*
|
|
202
|
+
* `fired` makes it once per ending: StrictMode replays a mount's effects, and
|
|
203
|
+
* a resolver mounted open over an empty queue would otherwise close twice. It
|
|
204
|
+
* resets when the run is live again (a reopening, a queue that grew back).
|
|
205
|
+
*/
|
|
206
|
+
function useCloseAtEnd(ended: boolean, close: () => void): boolean {
|
|
207
|
+
const closeRef = useRef(close);
|
|
208
|
+
const fired = useRef(false);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
closeRef.current = close;
|
|
211
|
+
});
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (!ended) {
|
|
214
|
+
fired.current = false;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (fired.current) return;
|
|
218
|
+
fired.current = true;
|
|
219
|
+
closeRef.current();
|
|
220
|
+
}, [ended]);
|
|
221
|
+
return ended;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface Screen {
|
|
225
|
+
header: ReturnType<typeof headerOf>;
|
|
226
|
+
body: React.JSX.Element;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* What the dialog shows. While a step is on screen, that screen — and it is
|
|
231
|
+
* kept: when the run ends with `done: null` the dialog closes in that same
|
|
232
|
+
* render, and its exit transition (a fade, the sheet sliding down) plays over
|
|
233
|
+
* the LAST step as it was — its label, Back, progress and body — rather than
|
|
234
|
+
* an empty, collapsed "done" paper — also once the host turns `open` off from
|
|
235
|
+
* `onClose`. Back is inert during the exit.
|
|
236
|
+
*/
|
|
237
|
+
function useExitScreen(live: Screen, input: { open: boolean; hasStep: boolean; closesAtEnd: boolean }): Screen {
|
|
238
|
+
const { open, hasStep, closesAtEnd } = input;
|
|
239
|
+
const last = useRef<Screen | null>(null);
|
|
240
|
+
const wasOpen = useRef(open);
|
|
241
|
+
// A new opening is a new run: the last one's screen is not kept into it.
|
|
242
|
+
if (open && !wasOpen.current) last.current = null;
|
|
243
|
+
wasOpen.current = open;
|
|
244
|
+
if (!closesAtEnd) return live;
|
|
245
|
+
if (open && hasStep) {
|
|
246
|
+
last.current = live;
|
|
247
|
+
return live;
|
|
248
|
+
}
|
|
249
|
+
// Ended (or closed by the host after `onClose`): the exit plays over the last step.
|
|
250
|
+
const kept = last.current;
|
|
251
|
+
if (!kept) return live;
|
|
252
|
+
const { header } = kept;
|
|
253
|
+
if (!('back' in header) || !header.back) return kept;
|
|
254
|
+
return { ...kept, header: { ...header, back: { ...header.back, onClick: () => undefined } } };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
interface RunEnding<I extends SequenceItem> {
|
|
258
|
+
current: I | undefined;
|
|
259
|
+
close: () => void;
|
|
260
|
+
/** The run ended with `done: null`: the dialog is closing. */
|
|
261
|
+
ended: boolean;
|
|
262
|
+
/** The kept last step is playing its exit. */
|
|
263
|
+
exiting: boolean;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function useRunEnding<I extends SequenceItem, R>(
|
|
267
|
+
props: SequenceResolverProps<I, R>,
|
|
268
|
+
state: SequenceRun<I, R>,
|
|
269
|
+
): RunEnding<I> {
|
|
270
|
+
const { open, onClose, intro } = props;
|
|
271
|
+
const { run } = state;
|
|
272
|
+
const current = intro ? undefined : currentOf(run);
|
|
273
|
+
const close = (): void => onClose(summaryOf(run));
|
|
274
|
+
const closesAtEnd = props.done === null && !intro;
|
|
275
|
+
const ended = useCloseAtEnd(open && closesAtEnd && !current, close);
|
|
276
|
+
return { current, close, ended, exiting: ended || (!open && closesAtEnd) };
|
|
277
|
+
}
|
|
278
|
+
|
|
210
279
|
/**
|
|
211
280
|
* Resolve a queue of problems one at a time, in ONE dialog whose body is
|
|
212
281
|
* replaced step by step: a header with "‹ Back", the step's label and close,
|
|
213
|
-
* a progress bar, the host's step, and an end screen (FUT-3671)
|
|
282
|
+
* a progress bar, the host's step, and an end screen (FUT-3671) — or none,
|
|
283
|
+
* with `done: null`: the run's end closes it (FUT-3833).
|
|
214
284
|
*
|
|
215
285
|
* A centred `Dialog` from the `sm` breakpoint up; the `Dialog` inset
|
|
216
286
|
* bottom-sheet variant below it, never taller than 95% of the viewport. Either
|
|
@@ -223,7 +293,7 @@ function SequenceBody<I extends SequenceItem, R>(body: BodyProps<I, R>): React.J
|
|
|
223
293
|
export function SequenceResolver<I extends SequenceItem, R = unknown>(
|
|
224
294
|
props: SequenceResolverProps<I, R>,
|
|
225
295
|
): React.JSX.Element {
|
|
226
|
-
const { open,
|
|
296
|
+
const { open, intro, labels } = props;
|
|
227
297
|
const testId = props.dataTestId ?? 'sequence-resolver';
|
|
228
298
|
const theme = useTheme();
|
|
229
299
|
const touch = useMediaQuery(theme.breakpoints.down(SEQUENCE_METRICS.sheetBreakpoint));
|
|
@@ -244,53 +314,44 @@ export function SequenceResolver<I extends SequenceItem, R = unknown>(
|
|
|
244
314
|
failed: labels.failed,
|
|
245
315
|
stepAllowsBack: () => stepAllowsBack.current,
|
|
246
316
|
});
|
|
247
|
-
const
|
|
248
|
-
const current
|
|
249
|
-
const close = (): void => onClose(summaryOf(run));
|
|
317
|
+
const ending = useRunEnding(props, state);
|
|
318
|
+
const { current, close } = ending;
|
|
250
319
|
const overview = useOverview(props, state, current, open);
|
|
251
320
|
// Under the overview the step is kept but hidden, and the overview's title names the dialog.
|
|
252
321
|
const titleProps = { id: overview.shown ? `${titleId}-kept` : titleId, tabIndex: -1, ref: titleRef } as const;
|
|
253
322
|
const step = current ? props.renderStep(current, stepApiOf(state, titleProps)) : undefined;
|
|
254
323
|
stepAllowsBack.current = step?.canGoBack !== false;
|
|
324
|
+
const screen = useExitScreen(
|
|
325
|
+
{
|
|
326
|
+
header: headerOf({ props, state, current, overview, titleId }),
|
|
327
|
+
body: (
|
|
328
|
+
<SequenceBody
|
|
329
|
+
props={props}
|
|
330
|
+
state={state}
|
|
331
|
+
current={current}
|
|
332
|
+
step={step}
|
|
333
|
+
overview={overview}
|
|
334
|
+
titleId={titleId}
|
|
335
|
+
stepTitleRef={stepTitleRef}
|
|
336
|
+
touch={touch}
|
|
337
|
+
testId={testId}
|
|
338
|
+
close={close}
|
|
339
|
+
/>
|
|
340
|
+
),
|
|
341
|
+
},
|
|
342
|
+
{ open, hasStep: current !== undefined && step !== undefined, closesAtEnd: props.done === null && !intro },
|
|
343
|
+
);
|
|
255
344
|
|
|
256
345
|
return (
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
>
|
|
268
|
-
<Box sx={columnSx}>
|
|
269
|
-
<Box sx={chromeSx}>
|
|
270
|
-
<SequenceHeader
|
|
271
|
-
touch={touch}
|
|
272
|
-
{...headerOf({ props, state, current, overview, titleId })}
|
|
273
|
-
closeLabel={labels.close}
|
|
274
|
-
onClose={close}
|
|
275
|
-
progressLabel={labels.progress}
|
|
276
|
-
testId={testId}
|
|
277
|
-
/>
|
|
278
|
-
</Box>
|
|
279
|
-
<Box sx={scrollBodySx} data-testid={`${testId}-body`}>
|
|
280
|
-
<SequenceBody
|
|
281
|
-
props={props}
|
|
282
|
-
state={state}
|
|
283
|
-
current={current}
|
|
284
|
-
step={step}
|
|
285
|
-
overview={overview}
|
|
286
|
-
titleId={titleId}
|
|
287
|
-
stepTitleRef={stepTitleRef}
|
|
288
|
-
touch={touch}
|
|
289
|
-
testId={testId}
|
|
290
|
-
close={close}
|
|
291
|
-
/>
|
|
292
|
-
</Box>
|
|
293
|
-
</Box>
|
|
294
|
-
</Dialog>
|
|
346
|
+
<ResolverFrame
|
|
347
|
+
shown={open && !ending.ended}
|
|
348
|
+
exiting={ending.exiting}
|
|
349
|
+
touch={touch}
|
|
350
|
+
titleId={titleId}
|
|
351
|
+
testId={testId}
|
|
352
|
+
labels={labels}
|
|
353
|
+
screen={screen}
|
|
354
|
+
close={close}
|
|
355
|
+
/>
|
|
295
356
|
);
|
|
296
357
|
}
|
|
@@ -196,8 +196,9 @@ export interface SequenceResolverLabels {
|
|
|
196
196
|
export interface SequenceResolverProps<I extends SequenceItem = SequenceItem, R = unknown> {
|
|
197
197
|
open: boolean;
|
|
198
198
|
/**
|
|
199
|
-
* The close button, Esc, and the end screen's button
|
|
200
|
-
* stood, for the host's
|
|
199
|
+
* The close button, Esc, and the end screen's button — or, with `done:
|
|
200
|
+
* null`, the queue running out. Gets where the run stood, for the host's
|
|
201
|
+
* "N resolved · Undo" toast.
|
|
201
202
|
*/
|
|
202
203
|
onClose: (summary: SequenceSummary) => void;
|
|
203
204
|
/**
|
|
@@ -228,8 +229,13 @@ export interface SequenceResolverProps<I extends SequenceItem = SequenceItem, R
|
|
|
228
229
|
overview?: SequenceOverview;
|
|
229
230
|
/** Open the run at this problem (a click on its block); the ones before it follow it. */
|
|
230
231
|
initialKey?: string;
|
|
231
|
-
/**
|
|
232
|
-
|
|
232
|
+
/**
|
|
233
|
+
* The end screen's words, from the run's summary. `null` drops the end
|
|
234
|
+
* screen: when the queue runs out — the last step answered or passed over —
|
|
235
|
+
* the resolver calls `onClose(summary)` at once, and the host's toast is the
|
|
236
|
+
* run's last word (FUT-3833).
|
|
237
|
+
*/
|
|
238
|
+
done: SequenceDone | ((summary: SequenceSummary) => SequenceDone) | null;
|
|
233
239
|
labels: SequenceResolverLabels;
|
|
234
240
|
/**
|
|
235
241
|
* Base test id (default `sequence-resolver`). Parts get `${id}-back`,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/DayRuler/DayRuler.metrics.ts"],"sourcesContent":["/**\n * THE DAY RULER'S NUMBERS — the approved prototype's (FUT-3823, frames d02 at\n * 1280 and the phone rows), in px. The web reads them through `rem`/`sxRem`.\n */\nexport const DAY_RULER_METRICS = {\n /** The name and the actions columns on a computer. */\n sideColumn: 220,\n gap: 16,\n /**\n * Below this container width a row stacks: name and actions on top, the\n * track under them. Two 220px side columns, their gaps and a track of at\n * least 320px (where the hour labels every 4 hours do not collide): 792,\n * rounded up.\n */\n compactBelow: 800,\n /**\n * Below this container width the pane is phone-sized: no coverage row and\n * no hour axis. Between it and `compactBelow` the rows stack and the two\n * lines are drawn across the full width, like the tracks.\n */\n phoneBelow: 600,\n rowPaddingY: 8,\n track: { height: 40, compactHeight: 30, radius: 8, compactRadius: 6 },\n block: { top: 4, height: 24, compactTop: 3, compactHeight: 20, radius: 6, paddingX: 6, font: 12, hitArea: 40 },\n /** The end handles' hit area. */\n handle: 10,\n actual: { top: 31, height: 6, compactTop: 24, compactHeight: 4, radius: 3 },\n actualLabel: { top: 8, offset: 6, font: 12 },\n now: { width: 2, overhang: 2 },\n coverage: { height: 26, gap: 2, radius: 4, font: 12 },\n axis: { height: 18, font: 12 },\n name: 15,\n meta: 13,\n groupTitle: 12,\n ring: 2,\n /** How far a block's focus ring stands off it. */\n ringOffset: 1,\n /** The loading row's name bar. */\n skeletonNameRadius: 4,\n /** Where a block's text cannot be measured, one shorter than this share of the ruler says only its start on a narrow ruler. */\n narrowShare: 0.3,\n /** The shortest block a resize leaves, in hours. */\n minHours: 0.5,\n skeletonRows: 4,\n /** The hatch of a break inside a block: stripe width. */\n breakStripe: { ink: 2, gap: 3 },\n} as const;\n"],"mappings":";AAIO,IAAM,oBAAoB;AAAA;AAAA,EAE/B,YAAY;AAAA,EACZ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOL,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,OAAO,EAAE,QAAQ,IAAI,eAAe,IAAI,QAAQ,GAAG,eAAe,EAAE;AAAA,EACpE,OAAO,EAAE,KAAK,GAAG,QAAQ,IAAI,YAAY,GAAG,eAAe,IAAI,QAAQ,GAAG,UAAU,GAAG,MAAM,IAAI,SAAS,GAAG;AAAA;AAAA,EAE7G,QAAQ;AAAA,EACR,QAAQ,EAAE,KAAK,IAAI,QAAQ,GAAG,YAAY,IAAI,eAAe,GAAG,QAAQ,EAAE;AAAA,EAC1E,aAAa,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG;AAAA,EAC3C,KAAK,EAAE,OAAO,GAAG,UAAU,EAAE;AAAA,EAC7B,UAAU,EAAE,QAAQ,IAAI,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG;AAAA,EACpD,MAAM,EAAE,QAAQ,IAAI,MAAM,GAAG;AAAA,EAC7B,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,MAAM;AAAA;AAAA,EAEN,YAAY;AAAA;AAAA,EAEZ,oBAAoB;AAAA;AAAA,EAEpB,aAAa;AAAA;AAAA,EAEb,UAAU;AAAA,EACV,cAAc;AAAA;AAAA,EAEd,aAAa,EAAE,KAAK,GAAG,KAAK,EAAE;AAChC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/AlertDialog/AlertDialog.parts.tsx"],"sourcesContent":["/**\n * The AlertDialog's action row, kept apart from the dialog shell so each stays\n * inside the complexity budget: which button is filled, where each one sits,\n * and how the row wraps.\n */\nimport type { ButtonProps } from '@mui/material/Button/index.js';\nimport Button from '@mui/material/Button/index.js';\nimport CircularProgress from '@mui/material/CircularProgress/index.js';\nimport DialogActions from '@mui/material/DialogActions/index.js';\nimport { styled, useTheme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { rem } from '../../../tokens/relative';\nimport { EN_US_UI_LABELS } from '../../../en-US.labels';\n\nimport type { AlertDialogProps } from './AlertDialog.types';\n\n/**\n * The action row WRAPS WHOLE BUTTONS; it never breaks a label inside one. A pair\n * that did not fit side by side used to squeeze the longer label onto two\n * lines, so one button stood twice as tall as its neighbour. Now the row breaks\n * instead. `wrap-reverse` puts the LAST button — the primary, whichever\n * `emphasis` picks — on top when it stacks, the same slot it holds on the right\n * when the row fits. A pair that fits is laid out exactly as before: natural\n * widths, pushed to the right.\n *\n * `fill` — set only by `emphasis=\"cancel\"` — lets the buttons grow to share the\n * row, and so to take the full width of their line once it stacks. It is not\n * the default because growing also stretches a pair that fits, which would\n * redraw every existing dialog's footer.\n *\n * Rendered with `disableSpacing`: MUI's own spacing is a left margin on every\n * button after the first, which would indent a button that wrapped onto a line\n * of its own. The gaps below replace it, at the same 16px across.\n */\nconst StyledDialogActions = styled(DialogActions, {\n shouldForwardProp: (prop) => prop !== 'fill',\n})<{ fill: boolean }>(({ theme, fill }) => ({\n padding: theme.spacing(2, 3, 3, 3),\n flexWrap: 'wrap-reverse',\n columnGap: theme.spacing(2),\n rowGap: theme.spacing(1),\n // A 40px tap target, the floor the kit's own Button keeps (FUT-3269): MUI's\n // medium button is 37px, short of it on a phone.\n '& > .MuiButton-root': { minHeight: rem(theme, 40) },\n ...(fill && { '& > .MuiButton-root': { flexGrow: 1, minHeight: rem(theme, 40) } }),\n}));\n\nconst confirmButtonColor = (variant: AlertDialogProps['variant']): 'error' | 'primary' =>\n variant === 'destructive' ? 'error' : 'primary';\n\ninterface ButtonLook {\n variant: ButtonProps['variant'];\n color: ButtonProps['color'];\n}\n\n/**\n * How each button is drawn, by `emphasis`: the emphasised one is filled, the\n * other a NEUTRAL outline. Primary / neutral both ways round — a confirm that\n * steps down drops the variant's colour too, so the one filled button is the\n * only coloured thing in the row and the eye has a single place to land.\n */\nconst buttonLooks = (\n emphasis: AlertDialogProps['emphasis'],\n variant: AlertDialogProps['variant'],\n): { cancel: ButtonLook; confirm: ButtonLook } =>\n emphasis === 'cancel'\n ? {\n cancel: { variant: 'contained', color: 'primary' },\n confirm: { variant: 'outlined', color: 'inherit' },\n }\n : {\n cancel: { variant: 'outlined', color: 'inherit' },\n confirm: { variant: 'contained', color: confirmButtonColor(variant) },\n };\n\n/**\n * The emphasis that applies: `cancel` only while the cancel is shown. A hidden\n * cancel with the confirm stepped down would leave a dialog with no primary.\n */\nconst effectiveEmphasis = (\n emphasis: AlertDialogProps['emphasis'],\n showCancel: boolean,\n): NonNullable<AlertDialogProps['emphasis']> =>\n emphasis === 'cancel' && showCancel ? 'cancel' : 'confirm';\n\n/**\n * Whether the cancel owns focus on open: what `initialFocus` says, and when it\n * says nothing, the emphasised button — so Enter on a steered question keeps\n * the work instead of completing the loss.\n */\nconst focusesCancel = (\n initialFocus: AlertDialogProps['initialFocus'],\n emphasis: NonNullable<AlertDialogProps['emphasis']>,\n): boolean => (initialFocus ?? emphasis) === 'cancel';\n\n/** The two buttons in slot order: the emphasised one LAST, the primary slot. */\nconst inSlotOrder = (\n emphasis: AlertDialogProps['emphasis'],\n cancel: React.ReactNode,\n confirm: React.ReactNode,\n): React.ReactNode[] => (emphasis === 'cancel' ? [confirm, cancel] : [cancel, confirm]);\n\n/**\n * Action row: the optional cancel button and the (variant-coloured) confirm,\n * with the emphasised one LAST — the primary slot, on the right or, once the\n * row stacks, on top. DOM order is the tab order, so it follows the same rule.\n */\nexport function AlertDialogFooter({\n variant,\n cancelText = EN_US_UI_LABELS.alertDialog.cancel,\n confirmText = EN_US_UI_LABELS.alertDialog.confirm,\n showCancel = true,\n loading = false,\n confirmDisabled = false,\n initialFocus,\n emphasis: asked,\n onCancel,\n onConfirm,\n dataTestId,\n}: Pick<\n AlertDialogProps,\n | 'variant'\n | 'cancelText'\n | 'confirmText'\n | 'showCancel'\n | 'loading'\n | 'confirmDisabled'\n | 'initialFocus'\n | 'emphasis'\n> & {\n onCancel: () => void;\n onConfirm: () => void;\n dataTestId: string;\n}): React.ReactElement {\n const theme = useTheme();\n const emphasis = effectiveEmphasis(asked, showCancel);\n const look = buttonLooks(emphasis, variant);\n const cancelFocused = focusesCancel(initialFocus, emphasis);\n const cancel = showCancel && (\n <Button\n key=\"cancel\"\n onClick={onCancel}\n variant={look.cancel.variant}\n color={look.cancel.color}\n disabled={loading}\n autoFocus={cancelFocused}\n data-testid={`${dataTestId}-cancel-button`}\n >\n {cancelText}\n </Button>\n );\n const confirm = (\n <Button\n key=\"confirm\"\n onClick={onConfirm}\n variant={look.confirm.variant}\n color={look.confirm.color}\n disabled={confirmDisabled || loading}\n startIcon={\n loading ? (\n <CircularProgress\n size={rem(theme, 16)}\n color=\"inherit\"\n data-testid={`${dataTestId}-loading-spinner`}\n />\n ) : undefined\n }\n autoFocus={!cancelFocused}\n data-testid={`${dataTestId}-confirm-button`}\n >\n {confirmText}\n </Button>\n );\n return (\n <StyledDialogActions\n disableSpacing\n fill={emphasis === 'cancel'}\n data-testid={`${dataTestId}-actions`}\n >\n {inSlotOrder(emphasis, cancel, confirm)}\n </StyledDialogActions>\n );\n}\n"],"mappings":";;;;;;;;AAMA,OAAO,YAAY;AACnB,OAAO,sBAAsB;AAC7B,OAAO,mBAAmB;AAC1B,SAAS,QAAQ,gBAAgB;AAmI7B;AAzGJ,IAAM,sBAAsB,OAAO,eAAe;AAAA,EAChD,mBAAmB,CAAC,SAAS,SAAS;AACxC,CAAC,EAAqB,CAAC,EAAE,OAAO,KAAK,OAAO;AAAA,EAC1C,SAAS,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC;AAAA,EACjC,UAAU;AAAA,EACV,WAAW,MAAM,QAAQ,CAAC;AAAA,EAC1B,QAAQ,MAAM,QAAQ,CAAC;AAAA;AAAA;AAAA,EAGvB,uBAAuB,EAAE,WAAW,IAAI,OAAO,EAAE,EAAE;AAAA,EACnD,GAAI,QAAQ,EAAE,uBAAuB,EAAE,UAAU,GAAG,WAAW,IAAI,OAAO,EAAE,EAAE,EAAE;AAClF,EAAE;AAEF,IAAM,qBAAqB,CAAC,YAC1B,YAAY,gBAAgB,UAAU;AAaxC,IAAM,cAAc,CAClB,UACA,YAEA,aAAa,WACT;AAAA,EACE,QAAQ,EAAE,SAAS,aAAa,OAAO,UAAU;AAAA,EACjD,SAAS,EAAE,SAAS,YAAY,OAAO,UAAU;AACnD,IACA;AAAA,EACE,QAAQ,EAAE,SAAS,YAAY,OAAO,UAAU;AAAA,EAChD,SAAS,EAAE,SAAS,aAAa,OAAO,mBAAmB,OAAO,EAAE;AACtE;AAMN,IAAM,oBAAoB,CACxB,UACA,eAEA,aAAa,YAAY,aAAa,WAAW;AAOnD,IAAM,gBAAgB,CACpB,cACA,cACa,gBAAgB,cAAc;AAG7C,IAAM,cAAc,CAClB,UACA,QACA,YACuB,aAAa,WAAW,CAAC,SAAS,MAAM,IAAI,CAAC,QAAQ,OAAO;AAO9E,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,aAAa,gBAAgB,YAAY;AAAA,EACzC,cAAc,gBAAgB,YAAY;AAAA,EAC1C,aAAa;AAAA,EACb,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AACF,GAcuB;AACrB,QAAM,QAAQ,SAAS;AACvB,QAAM,WAAW,kBAAkB,OAAO,UAAU;AACpD,QAAM,OAAO,YAAY,UAAU,OAAO;AAC1C,QAAM,gBAAgB,cAAc,cAAc,QAAQ;AAC1D,QAAM,SAAS,cACb;AAAA,IAAC;AAAA;AAAA,MAEC,SAAS;AAAA,MACT,SAAS,KAAK,OAAO;AAAA,MACrB,OAAO,KAAK,OAAO;AAAA,MACnB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,eAAa,GAAG,UAAU;AAAA,MAEzB;AAAA;AAAA,IARG;AAAA,EASN;AAEF,QAAM,UACJ;AAAA,IAAC;AAAA;AAAA,MAEC,SAAS;AAAA,MACT,SAAS,KAAK,QAAQ;AAAA,MACtB,OAAO,KAAK,QAAQ;AAAA,MACpB,UAAU,mBAAmB;AAAA,MAC7B,WACE,UACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,IAAI,OAAO,EAAE;AAAA,UACnB,OAAM;AAAA,UACN,eAAa,GAAG,UAAU;AAAA;AAAA,MAC5B,IACE;AAAA,MAEN,WAAW,CAAC;AAAA,MACZ,eAAa,GAAG,UAAU;AAAA,MAEzB;AAAA;AAAA,IAjBG;AAAA,EAkBN;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,gBAAc;AAAA,MACd,MAAM,aAAa;AAAA,MACnB,eAAa,GAAG,UAAU;AAAA,MAEzB,sBAAY,UAAU,QAAQ,OAAO;AAAA;AAAA,EACxC;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/DayRuler/day-ruler-model.ts"],"sourcesContent":["import type { RulerHour } from './DayRuler.types';\n\n/**\n * THE RULER'S ARITHMETIC, as pure functions (FUT-3825): snapping, clamping,\n * positions and the HH:MM the ruler writes. Hours are from the day's\n * midnight; past 24 is the next morning.\n */\n\n/** `h` on the `snap`-hour grid (0.25 for 15 minutes). */\nexport function snapTo(h: RulerHour, snap: number): RulerHour {\n return snap > 0 ? Math.round(h / snap) * snap : h;\n}\n\nexport function clamp(h: RulerHour, from: RulerHour, to: RulerHour): RulerHour {\n return Math.min(to, Math.max(from, h));\n}\n\n/** The hour under `x` on a track whose box starts at `left` and is `width` wide, snapped and clamped. */\nexport function hourAt(x: number, left: number, width: number, from: RulerHour, to: RulerHour, snap: number): RulerHour {\n const share = width > 0 ? (x - left) / width : 0;\n return clamp(snapTo(from + share * (to - from), snap), from, to);\n}\n\n/** Where `h` sits along the ruler, 0..100 (%), clamped to it. */\nexport function percentOf(h: RulerHour, from: RulerHour, to: RulerHour): number {\n return ((clamp(h, from, to) - from) / (to - from)) * 100;\n}\n\n/** \"20:45\"; 26 → \"02:00\", 24 → \"00:00\". */\nexport function formatHour(h: RulerHour): string {\n const minutes = Math.round(h * 60);\n const hh = Math.floor(minutes / 60) % 24;\n const mm = minutes % 60;\n return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;\n}\n\n/** \"20:45–00:45\". */\nexport function formatRange(start: RulerHour, end: RulerHour): string {\n return `${formatHour(start)}–${formatHour(end)}`;\n}\n\n/** A block slid by `delta`, keeping its length and staying on the ruler. */\nexport function moved(start: RulerHour, end: RulerHour, delta: number, from: RulerHour, to: RulerHour): [RulerHour, RulerHour] {\n const length = end - start;\n const next = clamp(start + delta, from, to - length);\n return [next, next + length];\n}\n\n/** A block whose `side` was dragged to `h`, never shorter than `minHours`, never off the ruler. */\nexport function resized(\n start: RulerHour,\n end: RulerHour,\n side: 'start' | 'end',\n h: RulerHour,\n minHours: number,\n from: RulerHour,\n to: RulerHour,\n): [RulerHour, RulerHour] {\n if (side === 'start') return [clamp(Math.min(h, end - minHours), from, to), end];\n return [start, clamp(Math.max(h, start + minHours), from, to)];\n}\n\n/** A click alone (shorter than `minHours`) becomes `createHours` from where it was pressed. */\nexport function createdSpan(\n a: RulerHour,\n b: RulerHour,\n minHours: number,\n createHours: number,\n to: RulerHour,\n): [RulerHour, RulerHour] {\n const start = Math.min(a, b);\n const end = Math.max(a, b);\n if (end - start >= minHours) return [start, end];\n return [start, Math.min(to, start + createHours)];\n}\n\n/** The axis labels: every `step` hours from `from` to `to`, both ends included. */\nexport function ticksOf(from: RulerHour, to: RulerHour, step: number): RulerHour[] {\n const out: RulerHour[] = [];\n if (step <= 0) return [from, to];\n for (let h = from; h <= to + 1e-9; h += step) out.push(h);\n return out;\n}\n"],"mappings":";AASO,SAAS,OAAO,GAAc,MAAyB;AAC5D,SAAO,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,IAAI,OAAO;AAClD;AAEO,SAAS,MAAM,GAAc,MAAiB,IAA0B;AAC7E,SAAO,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC;AACvC;AAGO,SAAS,OAAO,GAAW,MAAc,OAAe,MAAiB,IAAe,MAAyB;AACtH,QAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ;AAC/C,SAAO,MAAM,OAAO,OAAO,SAAS,KAAK,OAAO,IAAI,GAAG,MAAM,EAAE;AACjE;AAGO,SAAS,UAAU,GAAc,MAAiB,IAAuB;AAC9E,UAAS,MAAM,GAAG,MAAM,EAAE,IAAI,SAAS,KAAK,QAAS;AACvD;AAGO,SAAS,WAAW,GAAsB;AAC/C,QAAM,UAAU,KAAK,MAAM,IAAI,EAAE;AACjC,QAAM,KAAK,KAAK,MAAM,UAAU,EAAE,IAAI;AACtC,QAAM,KAAK,UAAU;AACrB,SAAO,GAAG,OAAO,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AACtE;AAGO,SAAS,YAAY,OAAkB,KAAwB;AACpE,SAAO,GAAG,WAAW,KAAK,CAAC,SAAI,WAAW,GAAG,CAAC;AAChD;AAGO,SAAS,MAAM,OAAkB,KAAgB,OAAe,MAAiB,IAAuC;AAC7H,QAAM,SAAS,MAAM;AACrB,QAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,MAAM;AACnD,SAAO,CAAC,MAAM,OAAO,MAAM;AAC7B;AAGO,SAAS,QACd,OACA,KACA,MACA,GACA,UACA,MACA,IACwB;AACxB,MAAI,SAAS,QAAS,QAAO,CAAC,MAAM,KAAK,IAAI,GAAG,MAAM,QAAQ,GAAG,MAAM,EAAE,GAAG,GAAG;AAC/E,SAAO,CAAC,OAAO,MAAM,KAAK,IAAI,GAAG,QAAQ,QAAQ,GAAG,MAAM,EAAE,CAAC;AAC/D;AAGO,SAAS,YACd,GACA,GACA,UACA,aACA,IACwB;AACxB,QAAM,QAAQ,KAAK,IAAI,GAAG,CAAC;AAC3B,QAAM,MAAM,KAAK,IAAI,GAAG,CAAC;AACzB,MAAI,MAAM,SAAS,SAAU,QAAO,CAAC,OAAO,GAAG;AAC/C,SAAO,CAAC,OAAO,KAAK,IAAI,IAAI,QAAQ,WAAW,CAAC;AAClD;AAGO,SAAS,QAAQ,MAAiB,IAAe,MAA2B;AACjF,QAAM,MAAmB,CAAC;AAC1B,MAAI,QAAQ,EAAG,QAAO,CAAC,MAAM,EAAE;AAC/B,WAAS,IAAI,MAAM,KAAK,KAAK,MAAM,KAAK,KAAM,KAAI,KAAK,CAAC;AACxD,SAAO;AACT;","names":[]}
|