@12-apps/ui 6.93.0 → 6.93.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components-catalog.md +9 -2
- package/components-guidelines.md +30 -0
- 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/AsyncStateContainer/AsyncStateContainer.js +3 -3
- package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
- package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +7 -7
- 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/DataViews/DataViewsGrid.js +31 -31
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +42 -42
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +7 -7
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +2 -2
- package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +2 -2
- 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 +5 -5
- 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 +26 -26
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +8 -8
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +7 -7
- 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 +8 -8
- 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 +3 -3
- 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 +14 -12
- 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/ErrorState/ErrorState.js +2 -2
- package/dist/_internal/components/data-display/ErrorState/ErrorState.metrics.js +3 -1
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +6 -6
- 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/index.js +41 -39
- 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/index.js +6 -6
- package/dist/_internal/components/index.js +52 -50
- 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/_internal/user-avatar/user-avatar.js +3 -3
- package/dist/charts.js +13 -13
- 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-F5H3WDG7.js → chunk-4E2RQOSQ.js} +2 -2
- package/dist/{chunk-JIEVKHOH.js → chunk-4J62V5OE.js} +6 -6
- 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-E2UOEI7N.js → chunk-6LGKJVOA.js} +2 -2
- package/dist/{chunk-XBWW3QSI.js → chunk-7WE432RQ.js} +9 -9
- package/dist/{chunk-5357QUVE.js → chunk-AMLHEKSV.js} +5 -5
- package/dist/{chunk-HSVSU7I2.js → chunk-ASEHJIIV.js} +3 -3
- package/dist/{chunk-FYUNROLW.js → chunk-BZYLTGOE.js} +2 -2
- package/dist/{chunk-TZ44F6W7.js → chunk-CGS2LQE5.js} +2 -2
- package/dist/{chunk-KPUUHAK6.js → chunk-CWXX5JR3.js} +11 -9
- package/dist/chunk-CWXX5JR3.js.map +1 -0
- package/dist/{chunk-6HXNFNOB.js → chunk-EE6DJPB7.js} +4 -4
- package/dist/{chunk-OM6ZT64U.js → chunk-F2UP5ZF4.js} +3 -3
- package/dist/{chunk-P53ANKZV.js → chunk-F7LBTMCA.js} +4 -4
- package/dist/{chunk-6HPVCDVU.js → chunk-FGZIEZEP.js} +69 -112
- 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-WJMSND5Q.js → chunk-GXGHEOWC.js} +2 -2
- package/dist/{chunk-U2SAW6DH.js → chunk-GY53SDVL.js} +4 -4
- package/dist/{chunk-MFX4ASX5.js → chunk-HWZETMO4.js} +3 -3
- package/dist/{chunk-HBH7V3IP.js → chunk-I6IFUFLW.js} +5 -3
- package/dist/chunk-I6IFUFLW.js.map +1 -0
- package/dist/{chunk-MM3QESLI.js → chunk-ILJ3FXIC.js} +2 -2
- 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-MAYAJXO6.js → chunk-KXDLLS62.js} +5 -5
- 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-MPVH2GM5.js +83 -0
- package/dist/chunk-MPVH2GM5.js.map +1 -0
- package/dist/{chunk-6DO672AG.js → chunk-N5PVD4J5.js} +7 -7
- package/dist/{chunk-ZYZPSBNQ.js → chunk-OGSTMHT6.js} +22 -7
- package/dist/chunk-OGSTMHT6.js.map +1 -0
- package/dist/{chunk-TLGGVE56.js → chunk-OTVOQHZB.js} +3 -1
- package/dist/{chunk-TLGGVE56.js.map → chunk-OTVOQHZB.js.map} +1 -1
- package/dist/{chunk-7F6D73NL.js → chunk-RLTGD5WJ.js} +2 -2
- package/dist/{chunk-R52N5UCS.js → chunk-SB2SEGR6.js} +2 -2
- 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-BNBWUXT3.js → chunk-TE6HAWDM.js} +2 -2
- package/dist/{chunk-JG733RMP.js → chunk-TZC6KIK2.js} +5 -5
- package/dist/{chunk-4QGKR65P.js → chunk-U4Z6GZDW.js} +5 -5
- package/dist/{chunk-XFZDO2KB.js → chunk-UIIHUWOV.js} +13 -13
- package/dist/{chunk-L4V7FZJI.js → chunk-UKUGSU3Y.js} +2 -2
- package/dist/{chunk-VJIJCLUX.js → chunk-UOKXYV3U.js} +2 -2
- 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-3YFDGFEK.js → chunk-Y7BOP7VZ.js} +2 -2
- package/dist/{chunk-XNAI7QGU.js → chunk-ZM6FJCWS.js} +4 -4
- package/dist/{chunk-FC7UCFFA.js → chunk-ZPWPA6FR.js} +8 -8
- package/dist/data-display/AlertDialog.js +2 -2
- package/dist/data-display/AsyncStateContainer.js +3 -3
- package/dist/data-display/Avatar.js +2 -2
- package/dist/data-display/Badge.js +2 -2
- package/dist/data-display/CardKit.js +10 -10
- package/dist/data-display/Carousel.js +3 -3
- package/dist/data-display/Chart.js +13 -13
- package/dist/data-display/DataViews.js +56 -56
- package/dist/data-display/DayRuler.js +14 -12
- package/dist/data-display/ErrorState.js +2 -2
- package/dist/data-display/ScheduleGrid.js +6 -6
- package/dist/feedback/ConfirmAction.js +6 -6
- package/dist/native/data-display/ErrorState.js.map +1 -1
- 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/ErrorState/ErrorState.d.ts.map +1 -1
- package/dist/types/components/data-display/ErrorState/ErrorState.metrics.d.ts +5 -0
- package/dist/types/components/data-display/ErrorState/ErrorState.metrics.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-native/components/data-display/ErrorState/ErrorState.metrics.d.ts +5 -0
- package/dist/types-native/components/data-display/ErrorState/ErrorState.metrics.d.ts.map +1 -1
- package/dist/user-avatar.js +3 -3
- 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/ErrorState/ErrorState.metrics.ts +5 -0
- package/src/components/data-display/ErrorState/ErrorState.tsx +2 -0
- package/src/components/data-display/ScheduleGrid/schedule-tones.ts +13 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.md +10 -0
- package/dist/chunk-6HPVCDVU.js.map +0 -1
- package/dist/chunk-7ZQ3KQ7J.js.map +0 -1
- package/dist/chunk-COK65GX2.js.map +0 -1
- package/dist/chunk-HBH7V3IP.js.map +0 -1
- package/dist/chunk-KPUUHAK6.js.map +0 -1
- package/dist/chunk-LE5JYNR7.js.map +0 -1
- package/dist/chunk-OJZLFFG4.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-F5H3WDG7.js.map → chunk-4E2RQOSQ.js.map} +0 -0
- /package/dist/{chunk-JIEVKHOH.js.map → chunk-4J62V5OE.js.map} +0 -0
- /package/dist/{chunk-CZVAVX5L.js.map → chunk-6ATXOJVQ.js.map} +0 -0
- /package/dist/{chunk-E2UOEI7N.js.map → chunk-6LGKJVOA.js.map} +0 -0
- /package/dist/{chunk-XBWW3QSI.js.map → chunk-7WE432RQ.js.map} +0 -0
- /package/dist/{chunk-5357QUVE.js.map → chunk-AMLHEKSV.js.map} +0 -0
- /package/dist/{chunk-HSVSU7I2.js.map → chunk-ASEHJIIV.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-OM6ZT64U.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-WJMSND5Q.js.map → chunk-GXGHEOWC.js.map} +0 -0
- /package/dist/{chunk-U2SAW6DH.js.map → chunk-GY53SDVL.js.map} +0 -0
- /package/dist/{chunk-MFX4ASX5.js.map → chunk-HWZETMO4.js.map} +0 -0
- /package/dist/{chunk-MM3QESLI.js.map → chunk-ILJ3FXIC.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-MAYAJXO6.js.map → chunk-KXDLLS62.js.map} +0 -0
- /package/dist/{chunk-DBI6AMNY.js.map → chunk-LKWXU6NH.js.map} +0 -0
- /package/dist/{chunk-6DO672AG.js.map → chunk-N5PVD4J5.js.map} +0 -0
- /package/dist/{chunk-7F6D73NL.js.map → chunk-RLTGD5WJ.js.map} +0 -0
- /package/dist/{chunk-R52N5UCS.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-BNBWUXT3.js.map → chunk-TE6HAWDM.js.map} +0 -0
- /package/dist/{chunk-JG733RMP.js.map → chunk-TZC6KIK2.js.map} +0 -0
- /package/dist/{chunk-4QGKR65P.js.map → chunk-U4Z6GZDW.js.map} +0 -0
- /package/dist/{chunk-XFZDO2KB.js.map → chunk-UIIHUWOV.js.map} +0 -0
- /package/dist/{chunk-L4V7FZJI.js.map → chunk-UKUGSU3Y.js.map} +0 -0
- /package/dist/{chunk-VJIJCLUX.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-3YFDGFEK.js.map → chunk-Y7BOP7VZ.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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import Box from '@mui/material/Box/index.js';
|
|
2
|
+
import type * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';
|
|
5
|
+
import type { RulerScale } from './DayRuler.keyboard';
|
|
6
|
+
import { coverageLabelSx, coverageSx, lineSx } from './DayRuler.styles';
|
|
7
|
+
import { footerCellSx, footerNoteSx, trendSx } from './DayRuler.footer.styles';
|
|
8
|
+
import type { DayRulerFooterCell, DayRulerFooterRow, RulerHour } from './DayRuler.types';
|
|
9
|
+
import { formatHour } from './day-ruler-model';
|
|
10
|
+
|
|
11
|
+
/** 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). */
|
|
12
|
+
export function trendPoints(values: readonly (number | null)[], hours: number): string {
|
|
13
|
+
const present = values.slice(0, hours).filter((value): value is number => value !== null);
|
|
14
|
+
const max = Math.max(0, ...present);
|
|
15
|
+
return values
|
|
16
|
+
.slice(0, hours)
|
|
17
|
+
.flatMap((value, index) => {
|
|
18
|
+
if (value === null) return [];
|
|
19
|
+
const x = ((index + 0.5) * 100) / hours;
|
|
20
|
+
const y = max > 0 ? 38 - (value / max) * 34 : 38;
|
|
21
|
+
return [`${x.toFixed(2)},${y.toFixed(2)}`];
|
|
22
|
+
})
|
|
23
|
+
.join(' ');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** One hour's default words: "20:00 · 4"; just the hour when it says nothing. */
|
|
27
|
+
const hourWords = (hour: RulerHour, text: string): string => (text ? `${formatHour(hour)} · ${text}` : formatHour(hour));
|
|
28
|
+
|
|
29
|
+
/** What a reader hears for a marked hour: the host's `title`, else the hour, its count and that it is short. */
|
|
30
|
+
function markedWords(cell: DayRulerFooterCell, hour: RulerHour, marked: string): string {
|
|
31
|
+
return cell.title ?? `${hourWords(hour, cell.text)} · ${marked}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface FooterContext {
|
|
35
|
+
hours: number;
|
|
36
|
+
from: RulerHour;
|
|
37
|
+
/** `labels.footerMarked`. */
|
|
38
|
+
marked: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function FooterCells({ cells, ctx }: { cells: readonly DayRulerFooterCell[]; ctx: FooterContext }): React.JSX.Element {
|
|
42
|
+
return (
|
|
43
|
+
<Box sx={coverageSx(ctx.hours)}>
|
|
44
|
+
{Array.from({ length: ctx.hours }, (_, index) => {
|
|
45
|
+
const cell = cells[index] ?? { text: '' };
|
|
46
|
+
const hour = ctx.from + index;
|
|
47
|
+
return (
|
|
48
|
+
<Box
|
|
49
|
+
key={index}
|
|
50
|
+
component="span"
|
|
51
|
+
sx={footerCellSx(Boolean(cell.marked))}
|
|
52
|
+
title={cell.marked ? markedWords(cell, hour, ctx.marked) : (cell.title ?? hourWords(hour, cell.text))}
|
|
53
|
+
data-marked={cell.marked ? '' : undefined}
|
|
54
|
+
>
|
|
55
|
+
{/* A marked hour is said in words too, not only by its outline and a tooltip touch never shows. */}
|
|
56
|
+
{cell.marked ? (
|
|
57
|
+
<>
|
|
58
|
+
<span aria-hidden>{cell.text}</span>
|
|
59
|
+
<Box component="span" sx={SR_ONLY_SX}>
|
|
60
|
+
{markedWords(cell, hour, ctx.marked)}
|
|
61
|
+
</Box>
|
|
62
|
+
</>
|
|
63
|
+
) : (
|
|
64
|
+
cell.text
|
|
65
|
+
)}
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
68
|
+
})}
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A trend's figures in words, for the reader the drawn line does not reach: "11:00 4 · 12:00 6". */
|
|
74
|
+
function trendWords(values: readonly (number | null)[], ctx: FooterContext): string {
|
|
75
|
+
return values
|
|
76
|
+
.slice(0, ctx.hours)
|
|
77
|
+
.flatMap((value, index) => (value === null ? [] : [`${formatHour(ctx.from + index)} ${Math.round(value * 10) / 10}`]))
|
|
78
|
+
.join(' · ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function FooterFigures({ row, ctx }: { row: DayRulerFooterRow; ctx: FooterContext }): React.JSX.Element {
|
|
82
|
+
if (row.trend) {
|
|
83
|
+
return (
|
|
84
|
+
<Box component="span" sx={{ display: 'block', minWidth: 0 }}>
|
|
85
|
+
<Box component="svg" viewBox="0 0 100 40" preserveAspectRatio="none" sx={trendSx} aria-hidden>
|
|
86
|
+
<polyline fill="none" stroke="currentColor" strokeWidth={1.5} vectorEffect="non-scaling-stroke" points={trendPoints(row.trend, ctx.hours)} />
|
|
87
|
+
</Box>
|
|
88
|
+
<Box component="span" sx={SR_ONLY_SX}>
|
|
89
|
+
{trendWords(row.trend, ctx)}
|
|
90
|
+
</Box>
|
|
91
|
+
</Box>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (row.note !== undefined) {
|
|
95
|
+
return (
|
|
96
|
+
<Box component="span" sx={footerNoteSx(Boolean(row.marked))} data-marked={row.marked ? '' : undefined}>
|
|
97
|
+
{row.note}
|
|
98
|
+
</Box>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return <FooterCells cells={row.cells ?? []} ctx={ctx} />;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A group's lines under its rows (FUT-3882): counts per hour, a trend, or a
|
|
106
|
+
* sentence, on the tracks' columns. `marked` is `labels.footerMarked`.
|
|
107
|
+
*/
|
|
108
|
+
export function GroupFooter(props: { rows: readonly DayRulerFooterRow[]; scale: RulerScale; testId: string; marked: string }): React.JSX.Element {
|
|
109
|
+
const { rows, scale, testId } = props;
|
|
110
|
+
const ctx: FooterContext = { hours: scale.to - scale.from, from: scale.from, marked: props.marked };
|
|
111
|
+
return (
|
|
112
|
+
<>
|
|
113
|
+
{rows.map((row) => (
|
|
114
|
+
<Box key={row.id} sx={lineSx} data-testid={`${testId}-footer-${row.id}`}>
|
|
115
|
+
<Box component="span" sx={coverageLabelSx}>
|
|
116
|
+
{row.label}
|
|
117
|
+
</Box>
|
|
118
|
+
<FooterFigures row={row} ctx={ctx} />
|
|
119
|
+
</Box>
|
|
120
|
+
))}
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -9,10 +9,11 @@ gestures; hours, limits and refusals are the host's.
|
|
|
9
9
|
Na escala, por hora [ ][ ][5][5][5] … [1][1][1][ ][ ]
|
|
10
10
|
06:00 10:00 … 22:00 02:00
|
|
11
11
|
ENTREGAS
|
|
12
|
-
Luiz entrega |··········[↻ 20:39–00:00]···|
|
|
13
|
-
atrasado · era 20:39
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
Luiz entrega |··········[↻ 20:39–00:00]···|
|
|
13
|
+
atrasado · era 20:39 | ┃ agora|
|
|
14
|
+
[Iniciar agora]
|
|
15
|
+
Anderson Paiva | sem escala · desde 08:26 |
|
|
16
|
+
[Encerrar turno] | ━━━━━━━━━━━━━━━━━━━━━━━━━┃|
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Times are **hours from the day's midnight**, fractions included: `20.75` is 20:45 and a value past
|
|
@@ -42,7 +43,7 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
|
|
|
42
43
|
|
|
43
44
|
| Prop | Type | Description |
|
|
44
45
|
|---|---|---|
|
|
45
|
-
| `rows` | `DayRulerRow[]` | `id`, `label`, `meta?`, `metaTone?`, `nameAdornment?` (node beside the name — a "Freela" tag; it moves under the name when there is no room for both), `chips?` (node — one line, a chip that does not fit shrinks and cuts its label), `actions?` (node — "Encerrar turno", "Iniciar agora"), `blocks` (`id`, `start`, `end`, `label
|
|
46
|
+
| `rows` | `DayRulerRow[]` | `id`, `label`, `meta?`, `metaTone?`, `nameAdornment?` (node beside the name — a "Freela" tag; it moves under the name when there is no room for both), `chips?` (node — one line, a chip that does not fit shrinks and cuts its label), `actions?` (node — "Encerrar turno", "Iniciar agora"; under the name on a computer, beside it when stacked — never a column of their own), `blocks` (`id`, `start`, `end`, `label` (drawn whole or not at all — see Layout), `shortLabel?`, `tone`, `movable?`, `resizable?`, `selected?`, `title?`, `breakSpan?` — `{ start, end }` in ruler hours, a meal break drawn as a hatched gap inside the block: it travels with a moved block and is cut to its ends while one is dragged; a span that falls outside the block, is empty or is reversed (`end <= start`) draws nothing; its words are the host's, in `label` / `title`), `actual?` (`start`, `end`), `actualLabel?`, `group?` (a new value starts a new heading), `onLabelClick?`, `labelAccessible?`. |
|
|
46
47
|
| `from` / `to` | `number` (default 6 / 26) | The ruler's span. |
|
|
47
48
|
| `now` | `number?` | The "agora" line, when on the ruler. |
|
|
48
49
|
| `coverage` | `number[]?` | People per hour, index 0 = `from…from+1`. |
|
|
@@ -57,27 +58,50 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
|
|
|
57
58
|
| `loading` / `error` / `empty` | `boolean` / node / node | Skeleton rows with `aria-busy`; the host's error; the host's empty sentence. |
|
|
58
59
|
| `aria-label` | `string?` | The region's name. |
|
|
59
60
|
| `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`. |
|
|
61
|
+
| `dataTestId` | `string?` (default `day-ruler`) | Parts: `-coverage`, `-axis`, `-footer-${footerRowId}`, `-row-${rowId}`, `-actions-${rowId}` (only on a row with actions), `-track-${rowId}`, `-block-${blockId}`, `-handle-l/-r-${blockId}`, `-break-${blockId}`, `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`. |
|
|
61
62
|
|
|
62
63
|
Tones are `ScheduleGrid`'s; on the ruler a `plan` block is a dashed outline over a faint ground.
|
|
63
64
|
|
|
64
65
|
## Layout
|
|
65
66
|
|
|
66
|
-
- **Computer:** name 220px · track (`minmax(0, 1fr)`)
|
|
67
|
-
`actions`
|
|
68
|
-
|
|
67
|
+
- **Computer:** name 220px · track (`minmax(0, 1fr)`), 16px gap; a hairline between rows. A
|
|
68
|
+
row's `actions` sit in the name's column, under the name, meta and chips (8px above them) —
|
|
69
|
+
**never a column of their own** (FUT-3831). A column reserved for the one row with an "Iniciar
|
|
70
|
+
agora" used to leave every other row a blank 236px band; a column per row (or per group) would
|
|
71
|
+
have given those rows a wider track and put their hours out of line with the axis. So every
|
|
72
|
+
track, with actions or without, is the same width and the hours line up down the whole ruler.
|
|
73
|
+
Track 40px tall with a line every hour; blocks 24px at +4px; the actual bar 6px at +31px; the
|
|
74
|
+
now line the track's full height.
|
|
75
|
+
- **The now line is drawn UNDER the blocks** (FUT-3831): it shows above and below each bar, where
|
|
76
|
+
it overhangs the track, and never through a block's label — it used to run through a late
|
|
77
|
+
block's "07:30–1|3:00". On a block whose fill is not opaque (the dashed `plan`, the `away` /
|
|
78
|
+
`uncovered` outline) the label takes an opaque ground of its own (the fill flattened over the
|
|
79
|
+
cell's ground), so the line may show through the bar but not through its text.
|
|
69
80
|
- **Stacked (a container under 800px: a phone, a tablet in portrait)** — a container query, not a
|
|
70
81
|
media query: name, meta, chips and the actions on top, the track under them at full width, 30px
|
|
71
82
|
tall with a line every `tickHours`; the coverage row (its label above the counts) and the hour
|
|
72
83
|
axis span the same full width. **Under 600px** (a phone-sized pane) there is no coverage row and
|
|
73
|
-
no axis. Every bound is "from that width up".
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
no axis. Every bound is "from that width up". The queries read the ruler's own named container
|
|
85
|
+
(`day-ruler`).
|
|
86
|
+
- **A block's text is drawn whole or not at all, at every width** (FUT-3831). By pixels: the
|
|
87
|
+
`label` when it fits the bar; else the `shortLabel` (its start, "20:39"); else **nothing** — the
|
|
88
|
+
bar stays empty rather than showing a cut "20:" or "20:39–00:". 12px is the type's floor, so a
|
|
89
|
+
text is never drawn smaller to squeeze in. Whatever the bar shows, its tooltip (`title`, default
|
|
90
|
+
`label`) and its accessible name still carry the whole `label`.
|
|
91
|
+
- **Hit band (stacked):** the 20px bars and the 24px name buttons answer a 40px finger, the look
|
|
92
|
+
unchanged. A bar's invisible band is 40px tall and **at least 40px wide, centred on the bar** —
|
|
93
|
+
a 1-hour turno at 320 is a 16px bar (FUT-3831). Where two bars are closer than that, each band
|
|
94
|
+
stops at the **midpoint of the gap** between them, so neither takes a tap meant for the other;
|
|
95
|
+
bars that touch or overlap give each other nothing past their own edges, and no band reaches
|
|
96
|
+
past the track's ends. A press in the band is a press on the block: a tap opens it, a mouse drag
|
|
97
|
+
moves it (a drag-create starts only on track the bands leave free). The end handles sit above
|
|
98
|
+
the band, so a press on a bar's end still resizes it.
|
|
76
99
|
|
|
77
100
|
## Accessibility
|
|
78
101
|
|
|
79
102
|
Rows are a `list` per group. Blocks are native buttons with `aria-keyshortcuts`; the `selected`
|
|
80
|
-
block is `aria-current="true"`.
|
|
103
|
+
block is `aria-current="true"`. A block that shows less than its `label` (its start, or nothing)
|
|
104
|
+
is named by the whole `label` (`aria-label`). An interactive
|
|
81
105
|
track is a focusable `group` named `labels.createTrack(row)`; Enter or Space creates.
|
|
82
106
|
|
|
83
107
|
## Related
|
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
* 1280 and the phone rows), in px. The web reads them through `rem`/`sxRem`.
|
|
4
4
|
*/
|
|
5
5
|
export const DAY_RULER_METRICS = {
|
|
6
|
-
/** The name
|
|
6
|
+
/** The name column on a computer; a row's actions sit in it, under the name (FUT-3831). */
|
|
7
7
|
sideColumn: 220,
|
|
8
8
|
gap: 16,
|
|
9
|
+
/** Between the name (with its meta and chips) and the row's actions under it, on a computer. */
|
|
10
|
+
actionsTop: 8,
|
|
9
11
|
/**
|
|
10
12
|
* Below this container width a row stacks: name and actions on top, the
|
|
11
|
-
* track under them.
|
|
12
|
-
*
|
|
13
|
-
* rounded up
|
|
13
|
+
* track under them. Measured when the actions had a 220px column of their
|
|
14
|
+
* own (two 220px side columns, their gaps and a track of at least 320px,
|
|
15
|
+
* where the hour labels every 4 hours do not collide: 792, rounded up); kept
|
|
16
|
+
* when they moved under the name (FUT-3831) so the bound every host was
|
|
17
|
+
* checked at did not move.
|
|
14
18
|
*/
|
|
15
19
|
compactBelow: 800,
|
|
16
20
|
/**
|
|
@@ -21,6 +25,12 @@ export const DAY_RULER_METRICS = {
|
|
|
21
25
|
phoneBelow: 600,
|
|
22
26
|
rowPaddingY: 8,
|
|
23
27
|
track: { height: 40, compactHeight: 30, radius: 8, compactRadius: 6 },
|
|
28
|
+
/**
|
|
29
|
+
* `hitArea`: on a narrow ruler the invisible hit band round a bar is this
|
|
30
|
+
* tall and at least this wide, centred on the bar and cut at the midpoint to
|
|
31
|
+
* a neighbour (FUT-3831). `font` is the floor: a text that does not fit at
|
|
32
|
+
* it is not drawn, never drawn smaller.
|
|
33
|
+
*/
|
|
24
34
|
block: { top: 4, height: 24, compactTop: 3, compactHeight: 20, radius: 6, paddingX: 6, font: 12, hitArea: 40 },
|
|
25
35
|
/** The end handles' hit area. */
|
|
26
36
|
handle: 10,
|
|
@@ -37,7 +47,7 @@ export const DAY_RULER_METRICS = {
|
|
|
37
47
|
ringOffset: 1,
|
|
38
48
|
/** The loading row's name bar. */
|
|
39
49
|
skeletonNameRadius: 4,
|
|
40
|
-
/** Where a block's text cannot be measured, one shorter than this share of the ruler says only its start
|
|
50
|
+
/** Where a block's text cannot be measured, one shorter than this share of the ruler says only its start. */
|
|
41
51
|
narrowShare: 0.3,
|
|
42
52
|
/** The shortest block a resize leaves, in hours. */
|
|
43
53
|
minHours: 0.5,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import Box from '@mui/material/Box/index.js';
|
|
2
2
|
import type * as React from 'react';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { Fragment, isValidElement, useRef } from 'react';
|
|
4
4
|
|
|
5
5
|
import { ScheduleNameCell } from '../ScheduleGrid/ScheduleNameCell';
|
|
6
6
|
import type { RulerAbilities, RulerScale } from './DayRuler.keyboard';
|
|
7
7
|
import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
|
|
8
|
-
import {
|
|
9
|
-
import { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';
|
|
10
|
-
import { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';
|
|
8
|
+
import { useTextFit } from './useTextFits';
|
|
9
|
+
import { formatHour, formatRange, hitBoundsOf, percentOf, ticksOf, type BlockTextFit } from './day-ruler-model';
|
|
11
10
|
import {
|
|
12
11
|
actionsSx,
|
|
13
12
|
actualLabelSx,
|
|
@@ -18,18 +17,16 @@ import {
|
|
|
18
17
|
coverageCellSx,
|
|
19
18
|
coverageLabelSx,
|
|
20
19
|
coverageSx,
|
|
21
|
-
footerCellSx,
|
|
22
|
-
footerNoteSx,
|
|
23
20
|
handleSx,
|
|
24
21
|
lineSx,
|
|
25
22
|
nowSx,
|
|
26
23
|
rowSx,
|
|
27
24
|
trackSx,
|
|
28
|
-
trendSx,
|
|
29
25
|
whoSx,
|
|
26
|
+
type HitBounds,
|
|
30
27
|
type Span,
|
|
31
28
|
} from './DayRuler.styles';
|
|
32
|
-
import type { DayRulerBlock,
|
|
29
|
+
import type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';
|
|
33
30
|
import type { RulerDrag, RulerDragApi } from './useRulerDrag';
|
|
34
31
|
|
|
35
32
|
export const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {
|
|
@@ -71,6 +68,8 @@ interface BlockProps {
|
|
|
71
68
|
span: Span;
|
|
72
69
|
/** The hours it is drawn at (its own, or a drag's). */
|
|
73
70
|
at: { start: RulerHour; end: RulerHour };
|
|
71
|
+
/** How far its hit band may reach on a narrow ruler, in % of the track (the midpoints to its neighbours). */
|
|
72
|
+
hit: HitBounds;
|
|
74
73
|
text: string;
|
|
75
74
|
ctx: RowContext;
|
|
76
75
|
testId: string;
|
|
@@ -98,25 +97,39 @@ function BreakBand({ block, at, testId }: { block: DayRulerBlock; at: BlockProps
|
|
|
98
97
|
return <Box component="span" aria-hidden sx={breakSx(gap)} data-dr-break="" data-testid={testId} />;
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
/** Where nothing can be measured, a block shorter than the prototype's 30% of the ruler says its start. */
|
|
101
|
+
function fallbackFit(block: DayRulerBlock, scale: RulerScale): BlockTextFit {
|
|
102
|
+
return (block.end - block.start) / (scale.to - scale.from) >= M.narrowShare ? 'full' : 'short';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* What the bar's fit puts on it: `data-fit` for the styles, and — while the bar
|
|
107
|
+
* shows less than its full text — that full text as its name, so the name still
|
|
108
|
+
* says all of it.
|
|
109
|
+
*/
|
|
110
|
+
function fitProps(fit: BlockTextFit, text: string): { 'aria-label'?: string; 'data-fit'?: BlockTextFit } {
|
|
111
|
+
return fit === 'full' ? {} : { 'aria-label': text, 'data-fit': fit };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function RulerBlock({ row, block, span, at, hit, text, ctx, testId }: BlockProps): React.JSX.Element {
|
|
102
115
|
const { can, dragApi, scale } = ctx;
|
|
103
116
|
const movable = can.move && Boolean(block.movable);
|
|
104
117
|
const resizable = can.resize && Boolean(block.resizable);
|
|
105
118
|
const ref = useRef<HTMLButtonElement | null>(null);
|
|
106
|
-
|
|
107
|
-
const
|
|
119
|
+
const short = block.shortLabel ?? text;
|
|
120
|
+
const fit = useTextFit(ref, text, short, fallbackFit(block, scale));
|
|
108
121
|
const press = (mode: 'move' | 'start' | 'end') => (event: React.PointerEvent<HTMLElement>) =>
|
|
109
122
|
dragApi.startBlock(event, row.id, block, mode);
|
|
110
123
|
return (
|
|
111
124
|
<Box
|
|
112
125
|
component="button"
|
|
113
126
|
type="button"
|
|
114
|
-
sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable })}
|
|
127
|
+
sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable, hit })}
|
|
115
128
|
title={block.title ?? block.label}
|
|
129
|
+
{...fitProps(fit, text)}
|
|
116
130
|
aria-keyshortcuts={shortcutsOf(movable, resizable)}
|
|
117
131
|
aria-current={block.selected ? 'true' : undefined}
|
|
118
132
|
ref={ref}
|
|
119
|
-
data-narrow={fits ? undefined : ''}
|
|
120
133
|
data-tone={block.tone}
|
|
121
134
|
data-testid={testId}
|
|
122
135
|
onPointerDown={movable ? press('move') : undefined}
|
|
@@ -131,7 +144,7 @@ function RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): Re
|
|
|
131
144
|
{text}
|
|
132
145
|
</span>
|
|
133
146
|
<span data-dr-text="short" title={text}>
|
|
134
|
-
{
|
|
147
|
+
{short}
|
|
135
148
|
</span>
|
|
136
149
|
{resizable ? <Handle side="r" onPointerDown={press('end')} testId={`${ctx.testId}-handle-r-${block.id}`} /> : null}
|
|
137
150
|
</Box>
|
|
@@ -141,39 +154,51 @@ function RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): Re
|
|
|
141
154
|
/** A row's blocks, each where a drag has it now (with its live hours) or where it is. */
|
|
142
155
|
function Blocks({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
|
|
143
156
|
const mine = ctx.drag && ctx.drag.rowId === row.id ? ctx.drag : null;
|
|
157
|
+
const drawn = row.blocks.map((block) => {
|
|
158
|
+
const live = mine?.blockId === block.id ? mine : null;
|
|
159
|
+
const start = live ? live.start : block.start;
|
|
160
|
+
const end = live ? live.end : block.end;
|
|
161
|
+
return { block, live, start, end, span: spanOf(start, end, ctx.scale) };
|
|
162
|
+
});
|
|
163
|
+
const preview = mine && mine.mode === 'create' ? previewSpan(mine, ctx.scale) : null;
|
|
164
|
+
// Every bar drawn on the row, the create's preview included, bounds its neighbours' hit bands.
|
|
165
|
+
const hits = hitBoundsOf([...drawn.map((d) => d.span), ...(preview ? [preview] : [])]);
|
|
144
166
|
return (
|
|
145
167
|
<>
|
|
146
|
-
{
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
/>
|
|
161
|
-
);
|
|
162
|
-
})}
|
|
163
|
-
{mine && mine.mode === 'create' ? <CreatePreview row={row} drag={mine} ctx={ctx} /> : null}
|
|
168
|
+
{drawn.map(({ block, live, start, end, span }, index) => (
|
|
169
|
+
<RulerBlock
|
|
170
|
+
key={block.id}
|
|
171
|
+
row={row}
|
|
172
|
+
block={block}
|
|
173
|
+
span={span}
|
|
174
|
+
at={{ start, end }}
|
|
175
|
+
hit={hits[index]!}
|
|
176
|
+
text={live?.moved ? formatRange(start, end) : block.label}
|
|
177
|
+
ctx={ctx}
|
|
178
|
+
testId={`${ctx.testId}-block-${block.id}`}
|
|
179
|
+
/>
|
|
180
|
+
))}
|
|
181
|
+
{mine && preview ? <CreatePreview row={row} drag={mine} span={preview} hit={hits[drawn.length]!} ctx={ctx} /> : null}
|
|
164
182
|
</>
|
|
165
183
|
);
|
|
166
184
|
}
|
|
167
185
|
|
|
186
|
+
/** Where a create drag's preview is drawn: at least one snap long. */
|
|
187
|
+
function previewSpan(drag: RulerDrag, scale: RulerScale): Span {
|
|
188
|
+
return spanOf(drag.start, Math.max(drag.end, drag.start + scale.snap), scale);
|
|
189
|
+
}
|
|
190
|
+
|
|
168
191
|
/** The block a create drag is drawing, with its live hours. */
|
|
169
|
-
function CreatePreview(
|
|
192
|
+
function CreatePreview(props: { row: DayRulerRow; drag: RulerDrag; span: Span; hit: HitBounds; ctx: RowContext }): React.JSX.Element {
|
|
193
|
+
const { row, drag, span, hit, ctx } = props;
|
|
170
194
|
const range = formatRange(drag.start, drag.end);
|
|
171
195
|
return (
|
|
172
196
|
<RulerBlock
|
|
173
197
|
row={row}
|
|
174
198
|
block={{ id: '\u0000new', start: drag.start, end: drag.end, label: range, tone: 'plan' }}
|
|
175
|
-
span={
|
|
199
|
+
span={span}
|
|
176
200
|
at={{ start: drag.start, end: drag.end }}
|
|
201
|
+
hit={hit}
|
|
177
202
|
text={range}
|
|
178
203
|
ctx={ctx}
|
|
179
204
|
testId={`${ctx.testId}-preview`}
|
|
@@ -197,7 +222,7 @@ function Actual({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.
|
|
|
197
222
|
);
|
|
198
223
|
}
|
|
199
224
|
|
|
200
|
-
/** A row's track: the actual bar, its blocks (or the one being dragged, live)
|
|
225
|
+
/** A row's track: the actual bar, the now line under its blocks (or the one being dragged, live). */
|
|
201
226
|
function Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
|
|
202
227
|
const { scale, now, can, dragApi } = ctx;
|
|
203
228
|
const showNow = now !== undefined && now >= scale.from && now <= scale.to;
|
|
@@ -217,19 +242,42 @@ function Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.E
|
|
|
217
242
|
}}
|
|
218
243
|
>
|
|
219
244
|
<Actual row={row} ctx={ctx} />
|
|
220
|
-
|
|
245
|
+
{/* Before the blocks, so it paints under them and never through a label. */}
|
|
221
246
|
{showNow ? <Box sx={nowSx(percentOf(now, scale.from, scale.to))} data-testid={`${ctx.testId}-now`} /> : null}
|
|
247
|
+
<Blocks row={row} ctx={ctx} />
|
|
222
248
|
</Box>
|
|
223
249
|
);
|
|
224
250
|
}
|
|
225
251
|
|
|
226
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Whether a node draws anything: nothing, a boolean, an empty string, an empty
|
|
254
|
+
* array and an empty fragment do not — the actions slot (and its 8px above)
|
|
255
|
+
* is rendered only for content.
|
|
256
|
+
*/
|
|
257
|
+
export function hasContent(node: React.ReactNode): boolean {
|
|
258
|
+
if (node === null || node === undefined || typeof node === 'boolean' || node === '') return false;
|
|
259
|
+
if (Array.isArray(node)) return node.some((child) => hasContent(child as React.ReactNode));
|
|
260
|
+
if (isValidElement<{ children?: React.ReactNode }>(node) && node.type === Fragment) return hasContent(node.props.children);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* One person: name, meta and chips, the row's actions under them · the track.
|
|
266
|
+
* The actions live in the name's column, never a column of their own: a
|
|
267
|
+
* column reserved for one row's "Iniciar agora" left every other row a blank
|
|
268
|
+
* band, and a per-row one would have cut the hours out of line (FUT-3831).
|
|
269
|
+
*/
|
|
227
270
|
export function RulerRow({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
|
|
271
|
+
const hasActions = hasContent(row.actions);
|
|
228
272
|
return (
|
|
229
273
|
<Box sx={rowSx} data-testid={`${ctx.testId}-row-${row.id}`} role="listitem">
|
|
230
274
|
<ScheduleNameCell row={row} sx={whoSx} sizes={{ name: M.name, meta: M.meta, chipsTop: 0.75 }} />
|
|
231
275
|
<Track row={row} ctx={ctx} />
|
|
232
|
-
|
|
276
|
+
{hasActions ? (
|
|
277
|
+
<Box sx={actionsSx} data-testid={`${ctx.testId}-actions-${row.id}`}>
|
|
278
|
+
{row.actions}
|
|
279
|
+
</Box>
|
|
280
|
+
) : null}
|
|
233
281
|
</Box>
|
|
234
282
|
);
|
|
235
283
|
}
|
|
@@ -253,126 +301,10 @@ export function Coverage(props: { coverage: readonly number[]; label?: string; s
|
|
|
253
301
|
);
|
|
254
302
|
})}
|
|
255
303
|
</Box>
|
|
256
|
-
<span />
|
|
257
304
|
</Box>
|
|
258
305
|
);
|
|
259
306
|
}
|
|
260
307
|
|
|
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
|
-
|
|
376
308
|
/** The hour labels under the coverage row. */
|
|
377
309
|
export function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {
|
|
378
310
|
return (
|
|
@@ -385,7 +317,6 @@ export function Axis({ scale, testId }: { scale: RulerScale; testId: string }):
|
|
|
385
317
|
</span>
|
|
386
318
|
))}
|
|
387
319
|
</Box>
|
|
388
|
-
<span />
|
|
389
320
|
</Box>
|
|
390
321
|
);
|
|
391
322
|
}
|