@12-apps/ui 6.52.0 → 6.53.0
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 +1 -1
- 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/CardKit/use-remove-confirm.js +1 -1
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +1 -1
- package/dist/_internal/components/data-display/Chart/SpecChart.js +3 -3
- package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +24 -24
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +26 -26
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +1 -1
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +4 -4
- package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +3 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +21 -21
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +11 -11
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +10 -10
- package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +5 -5
- 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 +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +4 -4
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +3 -3
- package/dist/_internal/components/data-display/FleetMap/FleetMap.fixtures.js +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +5 -5
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +2 -2
- package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.js +3 -1
- package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +3 -3
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +2 -2
- package/dist/_internal/components/data-display/Progress/Progress.js +2 -2
- package/dist/_internal/components/data-display/Sheet/Sheet.hooks.js +2 -2
- package/dist/_internal/components/data-display/Sheet/Sheet.js +4 -4
- package/dist/_internal/components/data-display/index.js +27 -27
- package/dist/_internal/components/feedback/index.js +3 -3
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.hooks.js +2 -2
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.js +5 -5
- package/dist/_internal/components/form/Autocomplete/Autocomplete.js +2 -2
- package/dist/_internal/components/form/Calendar/Calendar.hooks.js +2 -2
- package/dist/_internal/components/form/Calendar/Calendar.js +3 -3
- package/dist/_internal/components/form/CategorySelect/CategoryPanel.js +1 -1
- package/dist/_internal/components/form/CategorySelect/CategorySelect.js +4 -4
- package/dist/_internal/components/form/Command/Command.js +3 -3
- package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +5 -5
- package/dist/_internal/components/form/UploadButton/UploadButton.js +2 -2
- package/dist/_internal/components/form/index.js +22 -22
- package/dist/_internal/components/form/total-form/Fields/CategoryField.js +5 -5
- package/dist/_internal/components/form/total-form/Fields/index.js +9 -9
- package/dist/_internal/components/index.js +67 -66
- package/dist/_internal/components/layout/ScrollArea/ScrollArea.js +2 -2
- package/dist/_internal/components/layout/SettingCard/SettingCard.js +9 -4
- package/dist/_internal/components/layout/SettingCard/SettingCard.metrics.js +1 -1
- package/dist/_internal/components/layout/SettingCard/SettingCard.parts.js +3 -3
- package/dist/_internal/components/layout/SettingCard/SettingCard.styles.js +6 -2
- package/dist/_internal/components/layout/SettingCard/SettingCard.summary.js +63 -0
- package/dist/_internal/components/layout/SettingCard/SettingCard.summary.js.map +1 -0
- package/dist/_internal/components/layout/SettingCard/SettingGrid.js +4 -4
- package/dist/_internal/components/layout/SettingCard/SettingGroup.js +10 -5
- package/dist/_internal/components/layout/SettingCard/SettingSwitch.js +9 -4
- package/dist/_internal/components/layout/SettingCard/SettingToggle.js +10 -5
- package/dist/_internal/components/layout/index.js +18 -13
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +5 -5
- package/dist/_internal/components/navigation/index.js +5 -5
- package/dist/_internal/en-US.data-display.js +2 -2
- package/dist/_internal/en-US.layout.js +1 -1
- package/dist/_internal/pt-BR.data-display.js +2 -2
- package/dist/_internal/pt-BR.layout.js +1 -1
- package/dist/_internal/user-avatar/user-avatar.js +4 -4
- package/dist/charts.js +6 -6
- package/dist/{chunk-GXQDWHPN.js → chunk-2CJZLL5Y.js} +2 -2
- package/dist/{chunk-57VL5UX7.js → chunk-2JTZWQ25.js} +6 -4
- package/dist/chunk-2JTZWQ25.js.map +1 -0
- package/dist/{chunk-STTULW2K.js → chunk-2KVWOTEQ.js} +5 -5
- package/dist/{chunk-ISW5K72V.js → chunk-2ZRZTGWP.js} +5 -5
- package/dist/{chunk-33L26P4J.js → chunk-3ZKGNEP2.js} +5 -3
- package/dist/{chunk-33L26P4J.js.map → chunk-3ZKGNEP2.js.map} +1 -1
- package/dist/{chunk-2OTBMU5Z.js → chunk-44LYXLW7.js} +7 -7
- package/dist/{chunk-LYVTJLCF.js → chunk-56JF5PZK.js} +4 -4
- package/dist/{chunk-5KGABAQB.js → chunk-5752357K.js} +4 -4
- package/dist/{chunk-SPZAEMGW.js → chunk-5WX6U34F.js} +4 -4
- package/dist/{chunk-QHXMNSOZ.js → chunk-6L6VXRLJ.js} +2 -2
- package/dist/{chunk-VVFASSFY.js → chunk-6VGB4OX2.js} +3 -3
- package/dist/{chunk-FRP7NM6X.js → chunk-6WZUKV55.js} +2 -2
- package/dist/{chunk-QEINKMMX.js → chunk-7C763T3K.js} +2 -2
- package/dist/{chunk-PARTZ7XI.js → chunk-7JKDB5J5.js} +4 -4
- package/dist/{chunk-3U7WKBMH.js → chunk-AA2X5GOT.js} +5 -3
- package/dist/{chunk-3U7WKBMH.js.map → chunk-AA2X5GOT.js.map} +1 -1
- package/dist/{chunk-QYV7KLEN.js → chunk-ASGSC5UA.js} +5 -5
- package/dist/{chunk-7GQ2ZZBQ.js → chunk-BCTRC6AR.js} +2 -2
- package/dist/{chunk-XSHWEUDB.js → chunk-BE5Z36XZ.js} +4 -4
- package/dist/{chunk-IC7GHPMK.js → chunk-BFRVMCQM.js} +7 -2
- package/dist/chunk-BFRVMCQM.js.map +1 -0
- package/dist/{chunk-PM7SI4YT.js → chunk-BJOKL5PQ.js} +5 -5
- package/dist/{chunk-PGPPN63I.js → chunk-EMEXXAQI.js} +2 -2
- package/dist/{chunk-R6SBP5M5.js → chunk-GOWBYHYW.js} +4 -4
- package/dist/{chunk-LZQQ4JXY.js → chunk-HGVPPJKE.js} +8 -8
- package/dist/{chunk-XNBX3M35.js → chunk-HLCEKWFS.js} +4 -4
- package/dist/{chunk-4MGFUPQT.js → chunk-HOJL2GKW.js} +4 -4
- package/dist/{chunk-V5ZLUJV7.js → chunk-HRMB72E4.js} +2 -2
- package/dist/{chunk-CSDVRHUX.js → chunk-HWBKMUMV.js} +4 -4
- package/dist/{chunk-KN3WYEH2.js → chunk-IA6F7DU6.js} +4 -4
- package/dist/{chunk-BE5FGUJS.js → chunk-IM5SR7G2.js} +2 -2
- package/dist/{chunk-PJVDIUT2.js → chunk-IRNKHHQM.js} +3 -3
- package/dist/{chunk-4XET5MWR.js → chunk-KKE4SHRS.js} +2 -2
- package/dist/{chunk-ZTTFLIPH.js → chunk-L624DFJV.js} +2 -2
- package/dist/{chunk-OYT7YXQL.js → chunk-MGPN5TZE.js} +5 -5
- package/dist/{chunk-ERY6D2OV.js → chunk-MWPXKZ7O.js} +4 -4
- package/dist/{chunk-SCCE6VWY.js → chunk-N2Z7NHD5.js} +2 -2
- package/dist/chunk-N4R6T5K7.js +95 -0
- package/dist/chunk-N4R6T5K7.js.map +1 -0
- package/dist/{chunk-GNHXFJRR.js → chunk-N6EEY7V3.js} +2 -2
- package/dist/{chunk-OEBHWQSX.js → chunk-OESAYTNG.js} +9 -4
- package/dist/chunk-OESAYTNG.js.map +1 -0
- package/dist/{chunk-G5XHAN56.js → chunk-OT3KFZRE.js} +2 -2
- package/dist/{chunk-XAUKQZJK.js → chunk-QX4LMVL2.js} +2 -2
- package/dist/{chunk-S5UY5U73.js → chunk-QYJ25FWM.js} +20 -2
- package/dist/{chunk-S5UY5U73.js.map → chunk-QYJ25FWM.js.map} +1 -1
- package/dist/{chunk-6PYX6V3R.js → chunk-RJXVVLBO.js} +4 -4
- package/dist/{chunk-FRCBJLV5.js → chunk-RKKYSSAV.js} +5 -5
- package/dist/{chunk-WF7BZT6V.js → chunk-RR6BIHFX.js} +2 -2
- package/dist/{chunk-NGAU6PNB.js → chunk-RRVQVQF7.js} +5 -5
- package/dist/{chunk-F33YKDO2.js → chunk-TEST2NBS.js} +5 -5
- package/dist/{chunk-NKJSYZEU.js → chunk-V32626FV.js} +4 -4
- package/dist/{chunk-MLDA44NW.js → chunk-VGXSPUTD.js} +3 -3
- package/dist/{chunk-CTXF22BU.js → chunk-VODKYDC4.js} +5 -5
- package/dist/{chunk-DWJH3KXN.js → chunk-WAKGVAOB.js} +4 -4
- package/dist/{chunk-3DO5AJCG.js → chunk-WE2LNTR6.js} +12 -5
- package/dist/chunk-WE2LNTR6.js.map +1 -0
- package/dist/{chunk-SZA44EFA.js → chunk-WOEVBEGJ.js} +4 -4
- package/dist/{chunk-EOTV5BLM.js → chunk-WOP2DRMC.js} +2 -2
- package/dist/{chunk-E6JRPGMN.js → chunk-X3DVZNZH.js} +2 -2
- package/dist/{chunk-MLQYIJTO.js → chunk-XM7QCNZQ.js} +9 -3
- package/dist/chunk-XM7QCNZQ.js.map +1 -0
- package/dist/{chunk-P35CTP6L.js → chunk-Y3WB66XT.js} +7 -7
- package/dist/{chunk-DI46ZEWP.js → chunk-Y5QDXG5N.js} +20 -4
- package/dist/chunk-Y5QDXG5N.js.map +1 -0
- package/dist/{chunk-TNQDSTIA.js → chunk-Y6C4MYZE.js} +7 -7
- package/dist/{chunk-TBZKUZNW.js → chunk-ZPWU7Z6O.js} +3 -3
- package/dist/{chunk-QQSRZUT4.js → chunk-ZXEDKRRT.js} +4 -4
- package/dist/data-display/Avatar.js +3 -3
- package/dist/data-display/CardKit.js +8 -8
- package/dist/data-display/Chart.js +6 -6
- package/dist/data-display/DataViews.js +35 -35
- package/dist/data-display/FleetMap.js +5 -5
- package/dist/data-display/HoverCard.js +2 -2
- package/dist/data-display/InteractiveTooltip.js +2 -2
- package/dist/data-display/Lightbox.js +3 -3
- package/dist/data-display/MapPreview.js +2 -2
- package/dist/data-display/Progress.js +2 -2
- package/dist/data-display/Sheet.js +4 -4
- package/dist/en-US.js +2 -2
- package/dist/feedback/ConfirmAction.js +3 -3
- package/dist/form/AddressAutocomplete.js +5 -5
- package/dist/form/Autocomplete.js +2 -2
- package/dist/form/Calendar.js +3 -3
- package/dist/form/CategorySelect.js +4 -4
- package/dist/form/Command.js +3 -3
- package/dist/form/DateRangePicker.js +10 -10
- package/dist/form/UploadButton.js +2 -2
- package/dist/form/total-form.js +7 -7
- package/dist/layout/ContentToolbar.js +3 -3
- package/dist/layout/ScrollArea.js +2 -2
- package/dist/layout/SettingCard.js +13 -8
- package/dist/locales.js +4 -4
- package/dist/native/feedback/Dialog.js +86 -40
- package/dist/native/feedback/Dialog.js.map +1 -1
- package/dist/navigation/AppHeader.js +5 -5
- package/dist/pt-BR.js +2 -2
- package/dist/types/components/data-display/InteractiveTooltip/InteractiveTooltip.d.ts.map +1 -1
- package/dist/types/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.d.ts +18 -0
- package/dist/types/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.d.ts.map +1 -1
- package/dist/types/components/layout/SettingCard/SettingCard.d.ts.map +1 -1
- package/dist/types/components/layout/SettingCard/SettingCard.metrics.d.ts +5 -0
- package/dist/types/components/layout/SettingCard/SettingCard.metrics.d.ts.map +1 -1
- package/dist/types/components/layout/SettingCard/SettingCard.parts.d.ts +2 -0
- package/dist/types/components/layout/SettingCard/SettingCard.parts.d.ts.map +1 -1
- package/dist/types/components/layout/SettingCard/SettingCard.styles.d.ts +9 -2
- package/dist/types/components/layout/SettingCard/SettingCard.styles.d.ts.map +1 -1
- package/dist/types/components/layout/SettingCard/SettingCard.summary.d.ts +33 -0
- package/dist/types/components/layout/SettingCard/SettingCard.summary.d.ts.map +1 -0
- package/dist/types/components/layout/SettingCard/SettingSwitch.d.ts.map +1 -1
- package/dist/types/copy/layout.d.ts +8 -0
- package/dist/types/copy/layout.d.ts.map +1 -1
- package/dist/types/en-US.layout.d.ts.map +1 -1
- package/dist/types/pt-BR.layout.d.ts.map +1 -1
- package/dist/types-native/components/feedback/Dialog/Dialog.look.native.d.ts +12 -0
- package/dist/types-native/components/feedback/Dialog/Dialog.look.native.d.ts.map +1 -1
- package/dist/types-native/components/feedback/Dialog/Dialog.native.d.ts.map +1 -1
- package/dist/types-native/platform/keyboard-overlap.native.d.ts +36 -0
- package/dist/types-native/platform/keyboard-overlap.native.d.ts.map +1 -0
- package/dist/user-avatar.js +4 -4
- package/package.json +1 -1
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.ts +36 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +7 -2
- package/src/components/feedback/Dialog/Dialog.look.native.ts +17 -0
- package/src/components/feedback/Dialog/Dialog.native.tsx +12 -2
- package/src/components/layout/SettingCard/SettingCard.md +1 -1
- package/src/components/layout/SettingCard/SettingCard.metrics.ts +5 -0
- package/src/components/layout/SettingCard/SettingCard.parts.tsx +4 -0
- package/src/components/layout/SettingCard/SettingCard.styles.ts +17 -5
- package/src/components/layout/SettingCard/SettingCard.summary.tsx +100 -0
- package/src/components/layout/SettingCard/SettingCard.tsx +6 -2
- package/src/components/layout/SettingCard/SettingSwitch.tsx +11 -2
- package/src/components/layout/SettingCard/SettingToggle.md +1 -1
- package/src/copy/layout.ts +8 -0
- package/src/en-US.layout.ts +2 -0
- package/src/platform/keyboard-overlap.native.ts +77 -0
- package/src/pt-BR.layout.ts +2 -0
- package/dist/chunk-3DO5AJCG.js.map +0 -1
- package/dist/chunk-57VL5UX7.js.map +0 -1
- package/dist/chunk-DI46ZEWP.js.map +0 -1
- package/dist/chunk-IC7GHPMK.js.map +0 -1
- package/dist/chunk-MLQYIJTO.js.map +0 -1
- package/dist/chunk-OEBHWQSX.js.map +0 -1
- /package/dist/{chunk-GXQDWHPN.js.map → chunk-2CJZLL5Y.js.map} +0 -0
- /package/dist/{chunk-STTULW2K.js.map → chunk-2KVWOTEQ.js.map} +0 -0
- /package/dist/{chunk-ISW5K72V.js.map → chunk-2ZRZTGWP.js.map} +0 -0
- /package/dist/{chunk-2OTBMU5Z.js.map → chunk-44LYXLW7.js.map} +0 -0
- /package/dist/{chunk-LYVTJLCF.js.map → chunk-56JF5PZK.js.map} +0 -0
- /package/dist/{chunk-5KGABAQB.js.map → chunk-5752357K.js.map} +0 -0
- /package/dist/{chunk-SPZAEMGW.js.map → chunk-5WX6U34F.js.map} +0 -0
- /package/dist/{chunk-QHXMNSOZ.js.map → chunk-6L6VXRLJ.js.map} +0 -0
- /package/dist/{chunk-VVFASSFY.js.map → chunk-6VGB4OX2.js.map} +0 -0
- /package/dist/{chunk-FRP7NM6X.js.map → chunk-6WZUKV55.js.map} +0 -0
- /package/dist/{chunk-QEINKMMX.js.map → chunk-7C763T3K.js.map} +0 -0
- /package/dist/{chunk-PARTZ7XI.js.map → chunk-7JKDB5J5.js.map} +0 -0
- /package/dist/{chunk-QYV7KLEN.js.map → chunk-ASGSC5UA.js.map} +0 -0
- /package/dist/{chunk-7GQ2ZZBQ.js.map → chunk-BCTRC6AR.js.map} +0 -0
- /package/dist/{chunk-XSHWEUDB.js.map → chunk-BE5Z36XZ.js.map} +0 -0
- /package/dist/{chunk-PM7SI4YT.js.map → chunk-BJOKL5PQ.js.map} +0 -0
- /package/dist/{chunk-PGPPN63I.js.map → chunk-EMEXXAQI.js.map} +0 -0
- /package/dist/{chunk-R6SBP5M5.js.map → chunk-GOWBYHYW.js.map} +0 -0
- /package/dist/{chunk-LZQQ4JXY.js.map → chunk-HGVPPJKE.js.map} +0 -0
- /package/dist/{chunk-XNBX3M35.js.map → chunk-HLCEKWFS.js.map} +0 -0
- /package/dist/{chunk-4MGFUPQT.js.map → chunk-HOJL2GKW.js.map} +0 -0
- /package/dist/{chunk-V5ZLUJV7.js.map → chunk-HRMB72E4.js.map} +0 -0
- /package/dist/{chunk-CSDVRHUX.js.map → chunk-HWBKMUMV.js.map} +0 -0
- /package/dist/{chunk-KN3WYEH2.js.map → chunk-IA6F7DU6.js.map} +0 -0
- /package/dist/{chunk-BE5FGUJS.js.map → chunk-IM5SR7G2.js.map} +0 -0
- /package/dist/{chunk-PJVDIUT2.js.map → chunk-IRNKHHQM.js.map} +0 -0
- /package/dist/{chunk-4XET5MWR.js.map → chunk-KKE4SHRS.js.map} +0 -0
- /package/dist/{chunk-ZTTFLIPH.js.map → chunk-L624DFJV.js.map} +0 -0
- /package/dist/{chunk-OYT7YXQL.js.map → chunk-MGPN5TZE.js.map} +0 -0
- /package/dist/{chunk-ERY6D2OV.js.map → chunk-MWPXKZ7O.js.map} +0 -0
- /package/dist/{chunk-SCCE6VWY.js.map → chunk-N2Z7NHD5.js.map} +0 -0
- /package/dist/{chunk-GNHXFJRR.js.map → chunk-N6EEY7V3.js.map} +0 -0
- /package/dist/{chunk-G5XHAN56.js.map → chunk-OT3KFZRE.js.map} +0 -0
- /package/dist/{chunk-XAUKQZJK.js.map → chunk-QX4LMVL2.js.map} +0 -0
- /package/dist/{chunk-6PYX6V3R.js.map → chunk-RJXVVLBO.js.map} +0 -0
- /package/dist/{chunk-FRCBJLV5.js.map → chunk-RKKYSSAV.js.map} +0 -0
- /package/dist/{chunk-WF7BZT6V.js.map → chunk-RR6BIHFX.js.map} +0 -0
- /package/dist/{chunk-NGAU6PNB.js.map → chunk-RRVQVQF7.js.map} +0 -0
- /package/dist/{chunk-F33YKDO2.js.map → chunk-TEST2NBS.js.map} +0 -0
- /package/dist/{chunk-NKJSYZEU.js.map → chunk-V32626FV.js.map} +0 -0
- /package/dist/{chunk-MLDA44NW.js.map → chunk-VGXSPUTD.js.map} +0 -0
- /package/dist/{chunk-CTXF22BU.js.map → chunk-VODKYDC4.js.map} +0 -0
- /package/dist/{chunk-DWJH3KXN.js.map → chunk-WAKGVAOB.js.map} +0 -0
- /package/dist/{chunk-SZA44EFA.js.map → chunk-WOEVBEGJ.js.map} +0 -0
- /package/dist/{chunk-EOTV5BLM.js.map → chunk-WOP2DRMC.js.map} +0 -0
- /package/dist/{chunk-E6JRPGMN.js.map → chunk-X3DVZNZH.js.map} +0 -0
- /package/dist/{chunk-P35CTP6L.js.map → chunk-Y3WB66XT.js.map} +0 -0
- /package/dist/{chunk-TNQDSTIA.js.map → chunk-Y6C4MYZE.js.map} +0 -0
- /package/dist/{chunk-TBZKUZNW.js.map → chunk-ZPWU7Z6O.js.map} +0 -0
- /package/dist/{chunk-QQSRZUT4.js.map → chunk-ZXEDKRRT.js.map} +0 -0
|
@@ -29,6 +29,18 @@ export interface DialogLook {
|
|
|
29
29
|
/** The paper's corner radius, for the pulse ring to follow. */
|
|
30
30
|
radius: number;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* The overlay's bottom inset while the soft keyboard covers `overlap` dp of it
|
|
34
|
+
* (FUT-3021), or nothing while it covers none.
|
|
35
|
+
*
|
|
36
|
+
* The inset grows by exactly the overlap, on top of the margin the variant
|
|
37
|
+
* already keeps, so the space the paper may fill — its `maxHeight: '100%'`, or
|
|
38
|
+
* the `height: '100%'` of `fullscreen` and `drawer` — shrinks by the same
|
|
39
|
+
* amount and ends where the keyboard starts. A centred paper re-centres in what
|
|
40
|
+
* is left, and a body taller than that scrolls inside `DialogContent` with the
|
|
41
|
+
* actions still on screen.
|
|
42
|
+
*/
|
|
43
|
+
export declare function dialogKeyboardLift(theme: UiTheme, variant: DialogVariant, overlap: number): ViewStyle | null;
|
|
32
44
|
/**
|
|
33
45
|
* Everything the dialog paints, decided once per render.
|
|
34
46
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.look.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/feedback/Dialog/Dialog.look.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAanF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;;;;GAQG;AAEH,oCAAoC;AACpC,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAE/E;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAIxD;AAYD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,OAAO,EAAE,SAAS,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AA0DD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,GAAG,UAAU,CAyBxE"}
|
|
1
|
+
{"version":3,"file":"Dialog.look.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/feedback/Dialog/Dialog.look.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAanF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;;;;GAQG;AAEH,oCAAoC;AACpC,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAE/E;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAIxD;AAYD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,OAAO,EAAE,SAAS,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AA0DD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAI5G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,GAAG,UAAU,CAyBxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/feedback/Dialog/Dialog.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"Dialog.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/feedback/Dialog/Dialog.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAkBzD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAwDlF,wBAAgB,MAAM,CAAC,QAAQ,EAAE,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA4E/D;yBA5Ee,MAAM"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type LayoutChangeEvent } from 'react-native';
|
|
2
|
+
/**
|
|
3
|
+
* HOW FAR THE SOFT KEYBOARD COVERS A FULL-WINDOW VIEW, AND NOTHING MORE.
|
|
4
|
+
*
|
|
5
|
+
* React Native's `Modal` asks Android for `SOFT_INPUT_ADJUST_RESIZE`, and under
|
|
6
|
+
* edge-to-edge that asks for nothing: the window keeps its full height and the
|
|
7
|
+
* keyboard is drawn over it. iOS never resized a modal at all. So an overlay
|
|
8
|
+
* that fills the modal has its bottom under the keyboard, and whatever sits
|
|
9
|
+
* there — a dialog's actions — is hidden until the keyboard goes away.
|
|
10
|
+
*
|
|
11
|
+
* This reads the keyboard's own top edge from the `Keyboard` events and the
|
|
12
|
+
* view's own bottom edge from its layout, and answers the difference. Reading
|
|
13
|
+
* the OVERLAP rather than the keyboard's height is what keeps it right when the
|
|
14
|
+
* window DID resize (a host that is not edge-to-edge): the view then already
|
|
15
|
+
* ends at the keyboard, the overlap is zero, and nothing is lifted twice. No
|
|
16
|
+
* soft-input mode is set here, deliberately — a second one would fight the one
|
|
17
|
+
* `Modal` already asks for.
|
|
18
|
+
*
|
|
19
|
+
* The view's layout `y` is relative to its parent, so the view is expected to
|
|
20
|
+
* be the root of a window that starts at the top of the screen — which a
|
|
21
|
+
* `transparent` modal is, on iOS and under Android edge-to-edge alike.
|
|
22
|
+
*/
|
|
23
|
+
export declare function keyboardOverlap(viewBottom: number | null, keyboardTop: number | null): number;
|
|
24
|
+
export interface KeyboardOverlap {
|
|
25
|
+
/** How many dp of the view the keyboard covers; `0` while it is down. */
|
|
26
|
+
overlap: number;
|
|
27
|
+
/** Goes on the view being measured. */
|
|
28
|
+
onLayout: (event: LayoutChangeEvent) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The keyboard's overlap with the view `onLayout` is put on. Android re-sends
|
|
32
|
+
* `keyboardDidShow` when the keyboard changes height while it stays up (an
|
|
33
|
+
* emoji panel, a suggestion strip), so the overlap follows those too.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useKeyboardOverlap(): KeyboardOverlap;
|
|
36
|
+
//# sourceMappingURL=keyboard-overlap.native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-overlap.native.d.ts","sourceRoot":"","sources":["../../../src/platform/keyboard-overlap.native.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAG7F;AAQD,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAuBpD"}
|
package/dist/user-avatar.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
UserAvatar,
|
|
3
3
|
UserMenu
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WOP2DRMC.js";
|
|
5
5
|
import "./chunk-UDQSIRAP.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-EMEXXAQI.js";
|
|
7
|
+
import "./chunk-Y6C4MYZE.js";
|
|
8
|
+
import "./chunk-MTEQEPNY.js";
|
|
8
9
|
import "./chunk-6ZZGK5DT.js";
|
|
9
10
|
import "./chunk-3ZVUNSFF.js";
|
|
10
|
-
import "./chunk-MTEQEPNY.js";
|
|
11
11
|
import "./chunk-VUHLNYAE.js";
|
|
12
12
|
import "./chunk-2GSIEDGN.js";
|
|
13
13
|
import "./chunk-WN5PNP6F.js";
|
package/package.json
CHANGED
|
@@ -97,3 +97,39 @@ export const usePinnedTooltip = ({ clickable, onPin, onUnpin }: PinOptions) => {
|
|
|
97
97
|
|
|
98
98
|
return { isPinned, isControlledOpen, wrapperRef, handleClick };
|
|
99
99
|
};
|
|
100
|
+
|
|
101
|
+
type OnOpen = NonNullable<InteractiveTooltipProps['onOpen']>;
|
|
102
|
+
type OnClose = NonNullable<InteractiveTooltipProps['onClose']>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The hover half of the tooltip's open state, so the MUI Tooltip is controlled
|
|
106
|
+
* from its first render (FUT-3013). It used to receive `open={undefined}` while
|
|
107
|
+
* hovering and a boolean once pinned, which MUI reports as a component switching
|
|
108
|
+
* from uncontrolled to controlled on the first click. The caller's own
|
|
109
|
+
* onOpen/onClose still run.
|
|
110
|
+
*
|
|
111
|
+
* Unpinning also closes it: the hover listeners are off while pinned, so a
|
|
112
|
+
* hover state left `true` from before the pin would otherwise keep the tooltip
|
|
113
|
+
* open after the click outside that unpinned it.
|
|
114
|
+
*/
|
|
115
|
+
export const useHoverOpen = (
|
|
116
|
+
isPinned: boolean,
|
|
117
|
+
onOpen?: OnOpen,
|
|
118
|
+
onClose?: OnClose,
|
|
119
|
+
): { open: boolean; onOpen: OnOpen; onClose: OnClose } => {
|
|
120
|
+
const [open, setOpen] = useState(false);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (!isPinned) setOpen(false);
|
|
123
|
+
}, [isPinned]);
|
|
124
|
+
return {
|
|
125
|
+
open,
|
|
126
|
+
onOpen: (event) => {
|
|
127
|
+
setOpen(true);
|
|
128
|
+
onOpen?.(event);
|
|
129
|
+
},
|
|
130
|
+
onClose: (event) => {
|
|
131
|
+
setOpen(false);
|
|
132
|
+
onClose?.(event);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
};
|
|
@@ -2,7 +2,7 @@ import MuiTooltip from '@mui/material/Tooltip/index.js';
|
|
|
2
2
|
import { styled } from '@mui/material/styles/index.js';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
|
|
5
|
-
import { resolveTooltipProps, usePinnedTooltip } from './InteractiveTooltip.hooks';
|
|
5
|
+
import { resolveTooltipProps, useHoverOpen, usePinnedTooltip } from './InteractiveTooltip.hooks';
|
|
6
6
|
import {
|
|
7
7
|
arrowColor,
|
|
8
8
|
emphasisStyles,
|
|
@@ -58,6 +58,8 @@ export const InteractiveTooltip = React.forwardRef<HTMLDivElement, InteractiveTo
|
|
|
58
58
|
onUnpin,
|
|
59
59
|
className,
|
|
60
60
|
children,
|
|
61
|
+
onOpen,
|
|
62
|
+
onClose,
|
|
61
63
|
...props
|
|
62
64
|
} = resolveTooltipProps(rawProps);
|
|
63
65
|
|
|
@@ -66,6 +68,7 @@ export const InteractiveTooltip = React.forwardRef<HTMLDivElement, InteractiveTo
|
|
|
66
68
|
onPin,
|
|
67
69
|
onUnpin,
|
|
68
70
|
});
|
|
71
|
+
const hover = useHoverOpen(isPinned, onOpen, onClose);
|
|
69
72
|
|
|
70
73
|
// Determine which content to show
|
|
71
74
|
const tooltipContent = isPinned ? pinnedContent : hoverContent;
|
|
@@ -94,7 +97,9 @@ export const InteractiveTooltip = React.forwardRef<HTMLDivElement, InteractiveTo
|
|
|
94
97
|
glow={glow}
|
|
95
98
|
pulse={pulse}
|
|
96
99
|
title={tooltipContent}
|
|
97
|
-
open={isPinned ? isControlledOpen :
|
|
100
|
+
open={isPinned ? isControlledOpen : hover.open}
|
|
101
|
+
onOpen={hover.onOpen}
|
|
102
|
+
onClose={hover.onClose}
|
|
98
103
|
enterDelay={isPinned ? 0 : 100}
|
|
99
104
|
leaveDelay={0}
|
|
100
105
|
disableHoverListener={isPinned}
|
|
@@ -120,6 +120,23 @@ function overlayFor(theme: UiTheme, variant: DialogVariant): ViewStyle {
|
|
|
120
120
|
return centredOverlay(theme);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* The overlay's bottom inset while the soft keyboard covers `overlap` dp of it
|
|
125
|
+
* (FUT-3021), or nothing while it covers none.
|
|
126
|
+
*
|
|
127
|
+
* The inset grows by exactly the overlap, on top of the margin the variant
|
|
128
|
+
* already keeps, so the space the paper may fill — its `maxHeight: '100%'`, or
|
|
129
|
+
* the `height: '100%'` of `fullscreen` and `drawer` — shrinks by the same
|
|
130
|
+
* amount and ends where the keyboard starts. A centred paper re-centres in what
|
|
131
|
+
* is left, and a body taller than that scrolls inside `DialogContent` with the
|
|
132
|
+
* actions still on screen.
|
|
133
|
+
*/
|
|
134
|
+
export function dialogKeyboardLift(theme: UiTheme, variant: DialogVariant, overlap: number): ViewStyle | null {
|
|
135
|
+
if (overlap <= 0) return null;
|
|
136
|
+
const margin = variant === 'fullscreen' || variant === 'drawer' ? 0 : theme.spacing(DIALOG_MARGIN_UNITS);
|
|
137
|
+
return { paddingBottom: margin + overlap };
|
|
138
|
+
}
|
|
139
|
+
|
|
123
140
|
/**
|
|
124
141
|
* Everything the dialog paints, decided once per render.
|
|
125
142
|
*
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { Modal, Pressable, ScrollView, StyleSheet, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { hasSpacingSlot, withDialogDefaults } from './Dialog.helpers';
|
|
5
|
-
import { dialogBackdrop, dialogLook } from './Dialog.look.native';
|
|
5
|
+
import { dialogBackdrop, dialogKeyboardLift, dialogLook } from './Dialog.look.native';
|
|
6
6
|
import { DIALOG_BODY_PADDING_UNITS, DIALOG_PULSE } from './Dialog.metrics';
|
|
7
7
|
import type { DialogProps } from './Dialog.types.native';
|
|
8
8
|
import {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
DialogTitledContext,
|
|
13
13
|
dialogBodyTextStyle,
|
|
14
14
|
} from './DialogParts.native';
|
|
15
|
+
import { useKeyboardOverlap } from '../../../platform/keyboard-overlap.native';
|
|
15
16
|
import { PulseRing } from '../../../platform/pulse-ring.native';
|
|
16
17
|
import { resolveTestId, withoutTestIdProps } from '../../../platform/test-id';
|
|
17
18
|
import { renderTextChildren } from '../../../platform/text-children';
|
|
@@ -106,9 +107,18 @@ export function Dialog(rawProps: DialogProps): React.JSX.Element {
|
|
|
106
107
|
// backdrop and Escape (which `Modal` routes through `onRequestClose`).
|
|
107
108
|
const dismiss = persistent ? undefined : onClose;
|
|
108
109
|
|
|
110
|
+
// Edge-to-edge defeats the `SOFT_INPUT_ADJUST_RESIZE` that `Modal` asks for,
|
|
111
|
+
// so the window never shrinks for the keyboard and nothing else moves the
|
|
112
|
+
// paper: the overlay measures how much of it the keyboard covers and takes
|
|
113
|
+
// that off the paper's room itself (FUT-3021).
|
|
114
|
+
const keyboard = useKeyboardOverlap();
|
|
115
|
+
|
|
109
116
|
return (
|
|
110
117
|
<Modal visible={open} transparent onRequestClose={dismiss} {...modalProps}>
|
|
111
|
-
<View
|
|
118
|
+
<View
|
|
119
|
+
style={[look.overlay, dialogKeyboardLift(theme, variant, keyboard.overlap)]}
|
|
120
|
+
onLayout={keyboard.onLayout}
|
|
121
|
+
>
|
|
112
122
|
<Pressable
|
|
113
123
|
aria-hidden
|
|
114
124
|
tabIndex={-1}
|
|
@@ -51,7 +51,7 @@ const [draft, setDraft] = useState<number | null>(saved);
|
|
|
51
51
|
| `title` | `string` | **required** | The setting's name. A heading, and the accessible name of the card's region. |
|
|
52
52
|
| `onSave` | `() => void \| Promise<unknown>` | **required** | Persist the draft. See *Saving* below. |
|
|
53
53
|
| `children` | `ReactNode` | **required** | The form. Rendered only while open. |
|
|
54
|
-
| `summary` | `ReactNode` | — |
|
|
54
|
+
| `summary` | `ReactNode` | — | What a closed card shows under its title: two lines at most, then an ellipsis. While it is cut, an [i] beside the title shows the whole text (hover, or a click/tap that pins it). |
|
|
55
55
|
| `status` | `{ label: string; color?: ColorValue }` | — | The pill beside the title. `label` is always shown, so colour never carries the meaning alone. |
|
|
56
56
|
| `learnMore` | `ReactNode` | — | Longer explanation behind a disclosure in the open card. Omit for no disclosure. |
|
|
57
57
|
| `onCancel` | `() => void` | — | The card closed without saving. Reset the host's draft here. |
|
|
@@ -13,6 +13,11 @@ export const SETTING_CARD = {
|
|
|
13
13
|
groupRuleWidth: 2,
|
|
14
14
|
/** The narrowest a grid column gets by default. */
|
|
15
15
|
gridMinColumnWidth: 320,
|
|
16
|
+
/** The widest the [i]'s full-summary tooltip gets. */
|
|
17
|
+
infoMaxWidth: 360,
|
|
18
|
+
/** The [i]'s keyboard focus ring: its width, and its gap from the glyph. */
|
|
19
|
+
infoFocusRingWidth: 2,
|
|
20
|
+
infoFocusRingOffset: 2,
|
|
16
21
|
} as const;
|
|
17
22
|
|
|
18
23
|
/** The grid's default gap, in theme SPACING units (not px): what `sx`'s `gap: 2` means. */
|
|
@@ -28,6 +28,8 @@ interface HeaderProps {
|
|
|
28
28
|
trailing?: React.ReactNode;
|
|
29
29
|
/** Renders the title as a `<label>` for this control instead of a heading. */
|
|
30
30
|
labelFor?: string;
|
|
31
|
+
/** Beside the title, after the status pill — the [i] while the summary is cut. */
|
|
32
|
+
info?: React.ReactNode;
|
|
31
33
|
dataTestId?: string;
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -46,6 +48,7 @@ export const SettingHeader: React.FC<HeaderProps> = ({
|
|
|
46
48
|
status,
|
|
47
49
|
trailing,
|
|
48
50
|
labelFor,
|
|
51
|
+
info,
|
|
49
52
|
dataTestId,
|
|
50
53
|
}) => {
|
|
51
54
|
const theme = useTheme();
|
|
@@ -75,6 +78,7 @@ export const SettingHeader: React.FC<HeaderProps> = ({
|
|
|
75
78
|
dataTestId={partTestId(dataTestId, 'status')}
|
|
76
79
|
/>
|
|
77
80
|
)}
|
|
81
|
+
{info}
|
|
78
82
|
</Box>
|
|
79
83
|
{trailing}
|
|
80
84
|
</Box>
|
|
@@ -78,17 +78,29 @@ export const titleStyles = (theme: Theme): CSSObject => ({
|
|
|
78
78
|
overflowWrap: 'anywhere',
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
/** The
|
|
81
|
+
/** The most lines a card's summary shows before the rest goes behind the [i]. */
|
|
82
|
+
export const SUMMARY_MAX_LINES = 2;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Two lines at most, cut with an ellipsis. Visual only: the whole sentence stays
|
|
86
|
+
* in the DOM, so a switch that is `aria-describedby` it still reads all of it.
|
|
87
|
+
*/
|
|
88
|
+
export const clampStyles: CSSObject = {
|
|
89
|
+
display: '-webkit-box',
|
|
90
|
+
WebkitLineClamp: SUMMARY_MAX_LINES,
|
|
91
|
+
WebkitBoxOrient: 'vertical',
|
|
92
|
+
overflow: 'hidden',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** A closed card's value: two lines at most, the rest behind the [i]. */
|
|
82
96
|
export const summaryStyles = (theme: Theme): CSSObject => ({
|
|
83
97
|
...theme.typography.body2,
|
|
84
98
|
color: theme.palette.text.secondary,
|
|
85
99
|
margin: 0,
|
|
86
|
-
|
|
87
|
-
textOverflow: 'ellipsis',
|
|
88
|
-
whiteSpace: 'nowrap',
|
|
100
|
+
...clampStyles,
|
|
89
101
|
});
|
|
90
102
|
|
|
91
|
-
/** A switch card's explanation
|
|
103
|
+
/** A switch card's explanation; `SettingSwitchBlock` clamps it (see `clampStyles`). */
|
|
92
104
|
export const descriptionStyles = (theme: Theme): CSSObject => ({
|
|
93
105
|
...theme.typography.body2,
|
|
94
106
|
color: theme.palette.text.secondary,
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Box from '@mui/material/Box/index.js';
|
|
2
|
+
import { useTheme } from '@mui/material/styles/index.js';
|
|
3
|
+
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { Icon } from '../../../icons';
|
|
6
|
+
import { rem } from '../../../tokens/relative';
|
|
7
|
+
import { InteractiveTooltip } from '../../data-display/InteractiveTooltip';
|
|
8
|
+
|
|
9
|
+
import { SETTING_CARD } from './SettingCard.metrics';
|
|
10
|
+
import { partTestId } from './SettingCard.parts';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether a clamped element is hiding text — measured, never guessed from the
|
|
14
|
+
* string's length, because what fits depends on the width the card is GIVEN.
|
|
15
|
+
* Re-measured whenever the element resizes, so a card that widens drops its [i].
|
|
16
|
+
*/
|
|
17
|
+
export const useClamped = (content?: unknown): { ref: (node: HTMLElement | null) => void; clamped: boolean } => {
|
|
18
|
+
const [node, setNode] = useState<HTMLElement | null>(null);
|
|
19
|
+
const [clamped, setClamped] = useState(false);
|
|
20
|
+
const ref = useCallback((next: HTMLElement | null) => setNode(next), []);
|
|
21
|
+
|
|
22
|
+
useLayoutEffect(() => {
|
|
23
|
+
if (!node) return undefined;
|
|
24
|
+
const measure = (): void => setClamped(node.scrollHeight > node.clientHeight + 1);
|
|
25
|
+
measure();
|
|
26
|
+
if (typeof ResizeObserver === 'undefined') return undefined;
|
|
27
|
+
const observer = new ResizeObserver(measure);
|
|
28
|
+
observer.observe(node);
|
|
29
|
+
return () => observer.disconnect();
|
|
30
|
+
// `content` too: a summary that grows from two lines to three inside the
|
|
31
|
+
// same clamped height fires no resize, so the text changing re-measures.
|
|
32
|
+
}, [node, content]);
|
|
33
|
+
|
|
34
|
+
return { ref, clamped };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The [i] beside a card's title while its summary is cut: hover shows the whole
|
|
39
|
+
* text, a click or tap pins it — so it works on a phone, where there is no hover.
|
|
40
|
+
*/
|
|
41
|
+
export const SummaryInfo: React.FC<{ label: string; summary: React.ReactNode; dataTestId?: string }> = ({
|
|
42
|
+
label,
|
|
43
|
+
summary,
|
|
44
|
+
dataTestId,
|
|
45
|
+
}) => {
|
|
46
|
+
const theme = useTheme();
|
|
47
|
+
return (
|
|
48
|
+
<InteractiveTooltip
|
|
49
|
+
variant="light"
|
|
50
|
+
size="sm"
|
|
51
|
+
maxWidth={SETTING_CARD.infoMaxWidth}
|
|
52
|
+
hoverContent={summary}
|
|
53
|
+
pinnedContent={summary}
|
|
54
|
+
dataTestId={partTestId(dataTestId, 'info')}
|
|
55
|
+
>
|
|
56
|
+
<Box
|
|
57
|
+
component="button"
|
|
58
|
+
type="button"
|
|
59
|
+
aria-label={label}
|
|
60
|
+
sx={{
|
|
61
|
+
display: 'inline-flex',
|
|
62
|
+
alignItems: 'center',
|
|
63
|
+
justifyContent: 'center',
|
|
64
|
+
padding: 0,
|
|
65
|
+
border: 0,
|
|
66
|
+
background: 'none',
|
|
67
|
+
color: theme.palette.text.secondary,
|
|
68
|
+
cursor: 'pointer',
|
|
69
|
+
borderRadius: '50%',
|
|
70
|
+
'&:focus-visible': {
|
|
71
|
+
outline: `${rem(theme, SETTING_CARD.infoFocusRingWidth)} solid ${theme.palette.primary.main}`,
|
|
72
|
+
outlineOffset: rem(theme, SETTING_CARD.infoFocusRingOffset),
|
|
73
|
+
},
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
<Icon name="InfoOutlined" size="xs" />
|
|
77
|
+
</Box>
|
|
78
|
+
</InteractiveTooltip>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The summary's measuring ref, and the [i] for the header while it is cut —
|
|
84
|
+
* `null` when there is no summary, it fits, or `show` is false (an open card).
|
|
85
|
+
*/
|
|
86
|
+
export const useSummaryInfo = ({
|
|
87
|
+
summary,
|
|
88
|
+
show = true,
|
|
89
|
+
label,
|
|
90
|
+
dataTestId,
|
|
91
|
+
}: {
|
|
92
|
+
summary: React.ReactNode;
|
|
93
|
+
show?: boolean;
|
|
94
|
+
label: string;
|
|
95
|
+
dataTestId?: string;
|
|
96
|
+
}): { ref: (node: HTMLElement | null) => void; info: React.ReactNode } => {
|
|
97
|
+
const { ref, clamped } = useClamped(summary);
|
|
98
|
+
const visible = show && clamped && summary != null;
|
|
99
|
+
return { ref, info: visible ? <SummaryInfo label={label} summary={summary} dataTestId={dataTestId} /> : null };
|
|
100
|
+
};
|
|
@@ -7,6 +7,7 @@ import { Button } from '../../form/Button';
|
|
|
7
7
|
import { useSettingCard } from './SettingCard.hooks';
|
|
8
8
|
import { LearnMore, PendingSpinner, SaveError, SettingHeader, partTestId } from './SettingCard.parts';
|
|
9
9
|
import { SETTING_CARD_OPEN_ATTR, actionsStyles, summaryStyles, surfaceStyles, withCallerSx } from './SettingCard.styles';
|
|
10
|
+
import { useSummaryInfo } from './SettingCard.summary';
|
|
10
11
|
import type { SettingCardCopy, SettingCardProps } from './SettingCard.types';
|
|
11
12
|
|
|
12
13
|
interface OpenBodyProps {
|
|
@@ -82,7 +83,8 @@ const OpenBody: React.FC<OpenBodyProps> = ({
|
|
|
82
83
|
/**
|
|
83
84
|
* A setting, summarised while closed and edited in place.
|
|
84
85
|
*
|
|
85
|
-
* Closed: title, status pill, a
|
|
86
|
+
* Closed: title, status pill, a summary of at most two lines (an [i] beside the
|
|
87
|
+
* title holds the rest) and Edit. Edit opens the card
|
|
86
88
|
* where it is — no dialog, no route — around the host's form, a "learn more"
|
|
87
89
|
* disclosure and Cancel / Save. Save may be async: the card holds its saving
|
|
88
90
|
* state while the promise is pending, closes when it resolves, and stays open
|
|
@@ -134,6 +136,7 @@ export const SettingCard = forwardRef<HTMLElement, SettingCardProps>(function Se
|
|
|
134
136
|
const titleId = `${baseId}-title`;
|
|
135
137
|
const card = useSettingCard(props);
|
|
136
138
|
const { open } = card;
|
|
139
|
+
const clamp = useSummaryInfo({ summary, show: !open, label: copy.moreInfo ?? title, dataTestId });
|
|
137
140
|
|
|
138
141
|
return (
|
|
139
142
|
<Box
|
|
@@ -153,6 +156,7 @@ export const SettingCard = forwardRef<HTMLElement, SettingCardProps>(function Se
|
|
|
153
156
|
headingLevel={headingLevel}
|
|
154
157
|
icon={icon}
|
|
155
158
|
status={status}
|
|
159
|
+
info={clamp.info}
|
|
156
160
|
trailing={
|
|
157
161
|
open ? null : (
|
|
158
162
|
<EditTrigger
|
|
@@ -169,7 +173,7 @@ export const SettingCard = forwardRef<HTMLElement, SettingCardProps>(function Se
|
|
|
169
173
|
dataTestId={dataTestId}
|
|
170
174
|
/>
|
|
171
175
|
{!open && summary != null && (
|
|
172
|
-
<Box component="p" sx={summaryStyles} data-testid={partTestId(dataTestId, 'summary')}>
|
|
176
|
+
<Box component="p" ref={clamp.ref} sx={summaryStyles} data-testid={partTestId(dataTestId, 'summary')}>
|
|
173
177
|
{summary}
|
|
174
178
|
</Box>
|
|
175
179
|
)}
|
|
@@ -7,7 +7,8 @@ import { SR_ONLY_SX } from '../../form/Label/Label.styles';
|
|
|
7
7
|
|
|
8
8
|
import { useAsyncSwitch } from './SettingCard.hooks';
|
|
9
9
|
import { PendingSpinner, SaveError, SettingHeader, partTestId } from './SettingCard.parts';
|
|
10
|
-
import { descriptionStyles } from './SettingCard.styles';
|
|
10
|
+
import { clampStyles, descriptionStyles } from './SettingCard.styles';
|
|
11
|
+
import { useSummaryInfo } from './SettingCard.summary';
|
|
11
12
|
import type { SettingToggleProps } from './SettingCard.types';
|
|
12
13
|
|
|
13
14
|
/** Everything the switch-driven cards share, minus the surface each draws around it. */
|
|
@@ -46,6 +47,7 @@ export const SettingSwitchBlock: React.FC<
|
|
|
46
47
|
SettingSwitchBlockProps & { ids: SettingSwitchIds; state: ReturnType<typeof useAsyncSwitch> }
|
|
47
48
|
> = ({ title, headingLevel = 'h3', icon, summary, copy, disabled, dataTestId, ids, state }) => {
|
|
48
49
|
const theme = useTheme();
|
|
50
|
+
const clamp = useSummaryInfo({ summary, label: copy.moreInfo ?? title, dataTestId });
|
|
49
51
|
const { shown, saving, error, flip } = state;
|
|
50
52
|
const describedBy = [summary != null ? ids.summaryId : null, error ? ids.errorId : null]
|
|
51
53
|
.filter(Boolean)
|
|
@@ -76,10 +78,17 @@ export const SettingSwitchBlock: React.FC<
|
|
|
76
78
|
icon={icon}
|
|
77
79
|
labelFor={ids.inputId}
|
|
78
80
|
trailing={control}
|
|
81
|
+
info={clamp.info}
|
|
79
82
|
dataTestId={dataTestId}
|
|
80
83
|
/>
|
|
81
84
|
{summary != null && (
|
|
82
|
-
<Box
|
|
85
|
+
<Box
|
|
86
|
+
component="p"
|
|
87
|
+
ref={clamp.ref}
|
|
88
|
+
id={ids.summaryId}
|
|
89
|
+
sx={{ ...descriptionStyles(theme), ...clampStyles }}
|
|
90
|
+
data-testid={partTestId(dataTestId, 'summary')}
|
|
91
|
+
>
|
|
83
92
|
{summary}
|
|
84
93
|
</Box>
|
|
85
94
|
)}
|
|
@@ -28,7 +28,7 @@ import { SettingToggle } from '@12-apps/ui/layout/SettingCard';
|
|
|
28
28
|
| `title` | `string` | **required** | The switch's `<label>` — its accessible name, and a tap target far larger than the track. |
|
|
29
29
|
| `checked` | `boolean` | **required** | The persisted value. |
|
|
30
30
|
| `onChange` | `(checked: boolean) => void \| Promise<unknown>` | **required** | Persist a flip. See below. |
|
|
31
|
-
| `summary` | `ReactNode` | — | Explanation under the title; the switch is `aria-describedby` it. |
|
|
31
|
+
| `summary` | `ReactNode` | — | Explanation under the title; the switch is `aria-describedby` it. Two lines at most — while it is cut, an [i] beside the title shows the rest. The whole text stays in the DOM, so the description a screen reader hears is complete. |
|
|
32
32
|
| `variant` | `'card' \| 'row'` | `'card'` | `row` drops the surface — for a dependent row inside a [`SettingGroup`](./SettingGroup.md). |
|
|
33
33
|
| `disabled` | `boolean` | `false` | Disables the switch. Also disabled inside a `SettingGroup` whose main switch is off. |
|
|
34
34
|
| `formatError` | `(error: unknown) => string \| undefined` | — | The sentence for a rejected save; `undefined` falls back to `copy.saveFailed`. |
|
package/src/copy/layout.ts
CHANGED
|
@@ -33,6 +33,12 @@ export interface SettingCardCopy {
|
|
|
33
33
|
learnMore: string;
|
|
34
34
|
/** Shown when the save rejected and the host's `formatError` gave no sentence of its own. */
|
|
35
35
|
saveFailed: string;
|
|
36
|
+
/**
|
|
37
|
+
* The accessible name of the [i] beside the title while the summary is cut to
|
|
38
|
+
* two lines. Optional so a host's own copy object keeps compiling; without it
|
|
39
|
+
* the [i] is named after the card's title.
|
|
40
|
+
*/
|
|
41
|
+
moreInfo?: string;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
/**
|
|
@@ -44,4 +50,6 @@ export interface SettingSwitchCopy {
|
|
|
44
50
|
saving: string;
|
|
45
51
|
/** Shown when the save rejected and the host's `formatError` gave no sentence of its own. */
|
|
46
52
|
saveFailed: string;
|
|
53
|
+
/** The [i]'s accessible name — see `SettingCardCopy.moreInfo`. */
|
|
54
|
+
moreInfo?: string;
|
|
47
55
|
}
|
package/src/en-US.layout.ts
CHANGED
|
@@ -23,9 +23,11 @@ export const EN_US_SETTING_CARD_COPY: SettingCardCopy = {
|
|
|
23
23
|
saving: "Saving…",
|
|
24
24
|
learnMore: "Learn more",
|
|
25
25
|
saveFailed: "Could not save. Please try again.",
|
|
26
|
+
moreInfo: "Show the full text",
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export const EN_US_SETTING_SWITCH_COPY: SettingSwitchCopy = {
|
|
29
30
|
saving: "Saving…",
|
|
30
31
|
saveFailed: "Could not save. Please try again.",
|
|
32
|
+
moreInfo: "Show the full text",
|
|
31
33
|
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Keyboard,
|
|
4
|
+
Platform,
|
|
5
|
+
type KeyboardEvent,
|
|
6
|
+
type KeyboardEventName,
|
|
7
|
+
type LayoutChangeEvent,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HOW FAR THE SOFT KEYBOARD COVERS A FULL-WINDOW VIEW, AND NOTHING MORE.
|
|
12
|
+
*
|
|
13
|
+
* React Native's `Modal` asks Android for `SOFT_INPUT_ADJUST_RESIZE`, and under
|
|
14
|
+
* edge-to-edge that asks for nothing: the window keeps its full height and the
|
|
15
|
+
* keyboard is drawn over it. iOS never resized a modal at all. So an overlay
|
|
16
|
+
* that fills the modal has its bottom under the keyboard, and whatever sits
|
|
17
|
+
* there — a dialog's actions — is hidden until the keyboard goes away.
|
|
18
|
+
*
|
|
19
|
+
* This reads the keyboard's own top edge from the `Keyboard` events and the
|
|
20
|
+
* view's own bottom edge from its layout, and answers the difference. Reading
|
|
21
|
+
* the OVERLAP rather than the keyboard's height is what keeps it right when the
|
|
22
|
+
* window DID resize (a host that is not edge-to-edge): the view then already
|
|
23
|
+
* ends at the keyboard, the overlap is zero, and nothing is lifted twice. No
|
|
24
|
+
* soft-input mode is set here, deliberately — a second one would fight the one
|
|
25
|
+
* `Modal` already asks for.
|
|
26
|
+
*
|
|
27
|
+
* The view's layout `y` is relative to its parent, so the view is expected to
|
|
28
|
+
* be the root of a window that starts at the top of the screen — which a
|
|
29
|
+
* `transparent` modal is, on iOS and under Android edge-to-edge alike.
|
|
30
|
+
*/
|
|
31
|
+
export function keyboardOverlap(viewBottom: number | null, keyboardTop: number | null): number {
|
|
32
|
+
if (viewBottom === null || keyboardTop === null) return 0;
|
|
33
|
+
return Math.max(0, viewBottom - keyboardTop);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** iOS announces the keyboard before it moves; Android only once it has. */
|
|
37
|
+
const KEYBOARD_EVENTS: { show: KeyboardEventName; hide: KeyboardEventName } =
|
|
38
|
+
Platform.OS === 'ios'
|
|
39
|
+
? { show: 'keyboardWillShow', hide: 'keyboardWillHide' }
|
|
40
|
+
: { show: 'keyboardDidShow', hide: 'keyboardDidHide' };
|
|
41
|
+
|
|
42
|
+
export interface KeyboardOverlap {
|
|
43
|
+
/** How many dp of the view the keyboard covers; `0` while it is down. */
|
|
44
|
+
overlap: number;
|
|
45
|
+
/** Goes on the view being measured. */
|
|
46
|
+
onLayout: (event: LayoutChangeEvent) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The keyboard's overlap with the view `onLayout` is put on. Android re-sends
|
|
51
|
+
* `keyboardDidShow` when the keyboard changes height while it stays up (an
|
|
52
|
+
* emoji panel, a suggestion strip), so the overlap follows those too.
|
|
53
|
+
*/
|
|
54
|
+
export function useKeyboardOverlap(): KeyboardOverlap {
|
|
55
|
+
const [viewBottom, setViewBottom] = React.useState<number | null>(null);
|
|
56
|
+
const [keyboardTop, setKeyboardTop] = React.useState<number | null>(null);
|
|
57
|
+
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
const shown = Keyboard.addListener(KEYBOARD_EVENTS.show, (event: KeyboardEvent) => {
|
|
60
|
+
setKeyboardTop(event.endCoordinates.screenY);
|
|
61
|
+
});
|
|
62
|
+
const hidden = Keyboard.addListener(KEYBOARD_EVENTS.hide, () => {
|
|
63
|
+
setKeyboardTop(null);
|
|
64
|
+
});
|
|
65
|
+
return () => {
|
|
66
|
+
shown.remove();
|
|
67
|
+
hidden.remove();
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
const onLayout = React.useCallback((event: LayoutChangeEvent) => {
|
|
72
|
+
const { y, height } = event.nativeEvent.layout;
|
|
73
|
+
setViewBottom(y + height);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
return { overlap: keyboardOverlap(viewBottom, keyboardTop), onLayout };
|
|
77
|
+
}
|
package/src/pt-BR.layout.ts
CHANGED
|
@@ -23,9 +23,11 @@ export const PT_BR_SETTING_CARD_COPY: SettingCardCopy = {
|
|
|
23
23
|
saving: "Salvando…",
|
|
24
24
|
learnMore: "Saiba mais",
|
|
25
25
|
saveFailed: "Não foi possível salvar. Tente novamente.",
|
|
26
|
+
moreInfo: "Ver o texto inteiro",
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export const PT_BR_SETTING_SWITCH_COPY: SettingSwitchCopy = {
|
|
29
30
|
saving: "Salvando…",
|
|
30
31
|
saveFailed: "Não foi possível salvar. Tente novamente.",
|
|
32
|
+
moreInfo: "Ver o texto inteiro",
|
|
31
33
|
};
|