@12-apps/ui 6.39.9 → 6.39.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
- package/dist/_internal/components/data-display/CardKit/CardKebab.js +3 -3
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +4 -4
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +4 -4
- package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
- package/dist/_internal/components/data-display/Chart/SpecChart.js +5 -5
- 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 +30 -30
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +39 -39
- package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +3 -3
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +5 -5
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +3 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-columns.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-empty.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-export.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-grid-helpers.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +27 -27
- 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 +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-select-all-strip.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +10 -10
- 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 +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-view-nav.js +3 -3
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +5 -5
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +10 -10
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +7 -7
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +11 -11
- package/dist/_internal/components/data-display/FleetMap/FleetRoster.js +2 -2
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapLayers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +6 -6
- package/dist/_internal/components/data-display/Sheet/Sheet.hooks.js +2 -2
- package/dist/_internal/components/data-display/Sheet/Sheet.js +3 -3
- package/dist/_internal/components/data-display/Table/Table.js +3 -3
- package/dist/_internal/components/data-display/index.js +28 -28
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +3 -3
- package/dist/_internal/components/feedback/Modal/Modal.js +2 -2
- package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
- package/dist/_internal/components/feedback/index.js +9 -9
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.hooks.js +2 -2
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.js +6 -6
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocompleteField.js +3 -3
- package/dist/_internal/components/form/Button/Button.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/CategorySelect.js +2 -2
- package/dist/_internal/components/form/CodeEditor/CodeEditor.js +2 -2
- package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +4 -4
- package/dist/_internal/components/form/InputOTP/InputOTP.js +2 -2
- package/dist/_internal/components/form/Menubar/Menubar.js +2 -2
- package/dist/_internal/components/form/UploadButton/UploadButton.js +2 -2
- package/dist/_internal/components/form/index.js +20 -20
- package/dist/_internal/components/form/total-form/Fields/CategoryField.js +3 -3
- package/dist/_internal/components/form/total-form/Fields/index.js +7 -7
- package/dist/_internal/components/form/total-form/SubmitButton.js +3 -3
- package/dist/_internal/components/index.js +61 -61
- package/dist/_internal/components/layout/ScrollArea/ScrollArea.js +2 -2
- package/dist/_internal/components/layout/SettingsLayout/SettingsLayout.js +5 -4
- package/dist/_internal/components/layout/SettingsLayout/SettingsPanel.js +3 -2
- package/dist/_internal/components/layout/SettingsLayout/SettingsSectionChips.js +2 -1
- package/dist/_internal/components/layout/SettingsLayout/SettingsSectionChips.scroll.js +8 -0
- package/dist/_internal/components/layout/SettingsLayout/SettingsSectionChips.scroll.js.map +1 -0
- package/dist/_internal/components/layout/index.js +5 -5
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +6 -6
- package/dist/_internal/components/navigation/index.js +6 -6
- package/dist/_internal/social-login-button/social-login-button.js +3 -3
- package/dist/_internal/user-avatar/user-avatar.js +3 -3
- package/dist/charts.js +5 -5
- package/dist/{chunk-Y2HPRRVP.js → chunk-3333KCAW.js} +2 -2
- package/dist/{chunk-3RHZBO7L.js → chunk-36JKV7CA.js} +2 -2
- package/dist/{chunk-QGFBIUOK.js → chunk-3CTQZDSD.js} +2 -2
- package/dist/{chunk-R2WMSXQR.js → chunk-4BNV5AAI.js} +7 -7
- package/dist/{chunk-OHC3F7GJ.js → chunk-4NPPO63U.js} +6 -6
- package/dist/{chunk-ZXIUIIUE.js → chunk-4WF6ZLIW.js} +3 -3
- package/dist/{chunk-6WLHEZXI.js → chunk-4Y6W7BT4.js} +4 -4
- package/dist/{chunk-5KGABAQB.js → chunk-5752357K.js} +4 -4
- package/dist/{chunk-TYNK662L.js → chunk-5CQNBMLK.js} +2 -2
- package/dist/{chunk-3PQ5MZMJ.js → chunk-5FAI25JQ.js} +2 -2
- package/dist/{chunk-YMG4CXGI.js → chunk-5KWHINO5.js} +2 -2
- package/dist/{chunk-TT3HZRU6.js → chunk-5QD6YGZ2.js} +9 -9
- package/dist/{chunk-JQ3D5QP6.js → chunk-5WT4YWXC.js} +3 -3
- package/dist/{chunk-JGJFL3PF.js → chunk-6MFMLQ72.js} +4 -4
- package/dist/{chunk-VIUDWOV6.js → chunk-7D2C4LK2.js} +5 -5
- package/dist/{chunk-6QXFBZ7Q.js → chunk-7ICK5MXV.js} +4 -4
- package/dist/{chunk-QS2DCLG6.js → chunk-A5B7B4HT.js} +3 -3
- package/dist/{chunk-WYUBTRRV.js → chunk-AXKLCWFR.js} +15 -15
- package/dist/{chunk-34BZXJGT.js → chunk-BKASAYQL.js} +4 -4
- package/dist/{chunk-UAVSBSV5.js → chunk-BUPDBEBA.js} +2 -2
- package/dist/{chunk-DIM6SUGY.js → chunk-DAHYLPK7.js} +7 -7
- package/dist/{chunk-KBZFBHLO.js → chunk-DXZOSP6P.js} +3 -3
- package/dist/{chunk-4GWHYLEB.js → chunk-DYGWKZ7T.js} +2 -2
- package/dist/{chunk-PK6TRXV4.js → chunk-ECIHOZPK.js} +3 -3
- package/dist/{chunk-QU3JX53V.js → chunk-ENRBXB6L.js} +6 -6
- package/dist/{chunk-FWSMIUWA.js → chunk-ESCF5VMQ.js} +2 -2
- package/dist/{chunk-QI5CRSOW.js → chunk-F4KYBBTY.js} +4 -4
- package/dist/{chunk-NCEQWEXT.js → chunk-FNO5TWBJ.js} +4 -4
- package/dist/{chunk-S6IWKDWT.js → chunk-GC56IWIZ.js} +2 -2
- package/dist/{chunk-2EAAWOZ2.js → chunk-HM2JJNUE.js} +4 -4
- package/dist/{chunk-OOCREE37.js → chunk-HMAPQV4C.js} +2 -2
- package/dist/{chunk-2KWYNFVE.js → chunk-HUPR5JXY.js} +4 -4
- package/dist/{chunk-F4WFUQYR.js → chunk-IFS3BRRL.js} +5 -5
- package/dist/{chunk-3GTMGPNQ.js → chunk-IQJCLFF4.js} +5 -5
- package/dist/{chunk-2ZRZTGWP.js → chunk-ISW5K72V.js} +5 -5
- package/dist/{chunk-EDKLXA5W.js → chunk-J3XFUCCR.js} +3 -3
- package/dist/{chunk-TTZPO345.js → chunk-JGRBHNK6.js} +6 -6
- package/dist/{chunk-GS3RP2QE.js → chunk-KKN4LLVE.js} +2 -2
- package/dist/{chunk-QFTBXHOY.js → chunk-LH46NYGR.js} +2 -2
- package/dist/{chunk-56JF5PZK.js → chunk-LYVTJLCF.js} +4 -4
- package/dist/{chunk-PZ7PU4AP.js → chunk-M2Q5CLPU.js} +4 -4
- package/dist/{chunk-5L7N5HER.js → chunk-M4KVKQRN.js} +2 -2
- package/dist/{chunk-ERY6D2OV.js → chunk-MWPXKZ7O.js} +4 -4
- package/dist/{chunk-XWZJLXJT.js → chunk-N7LPVR7P.js} +36 -10
- package/dist/chunk-N7LPVR7P.js.map +1 -0
- package/dist/{chunk-3AOQBR7N.js → chunk-OAW6GCS3.js} +2 -2
- package/dist/{chunk-6N2EDWMG.js → chunk-OGB25P3N.js} +4 -4
- package/dist/{chunk-R53ZOW4B.js → chunk-PARTZ7XI.js} +2 -2
- package/dist/{chunk-7GHYCQCJ.js → chunk-PCLKFR27.js} +4 -4
- package/dist/{chunk-K6VKL62Z.js → chunk-PG5C2AA4.js} +5 -5
- package/dist/{chunk-R3JYC63P.js → chunk-QXSV7Z57.js} +2 -2
- package/dist/{chunk-SNYJSACF.js → chunk-S6JHTEV4.js} +2 -2
- package/dist/{chunk-5JCGBCFJ.js → chunk-SAOCDKNK.js} +4 -4
- package/dist/chunk-SLJAQA2X.js +82 -0
- package/dist/chunk-SLJAQA2X.js.map +1 -0
- package/dist/{chunk-XG7NTGUH.js → chunk-T3DNYPEA.js} +4 -4
- package/dist/{chunk-JU4MSYYH.js → chunk-T6EKPFKZ.js} +2 -2
- package/dist/{chunk-Q2YNXJEL.js → chunk-TZBOWSAM.js} +5 -5
- package/dist/{chunk-D4FZ3ANG.js → chunk-UKX3MPB7.js} +2 -2
- package/dist/{chunk-TXGB2WSW.js → chunk-VJ2BNJ5M.js} +2 -2
- package/dist/{chunk-7XGHJD75.js → chunk-VJG7NMGL.js} +2 -2
- package/dist/{chunk-BWHOCNGW.js → chunk-VPPIODKX.js} +2 -2
- package/dist/{chunk-4B5XUQQS.js → chunk-WB6ZU3B3.js} +3 -3
- package/dist/{chunk-W7F2QIAT.js → chunk-WCRGSMLV.js} +3 -3
- package/dist/{chunk-G62KCU3D.js → chunk-WL6N33AB.js} +2 -2
- package/dist/{chunk-TKKQ7RGT.js → chunk-WZNTZX5N.js} +2 -2
- package/dist/{chunk-KE473LK6.js → chunk-YMTZN54Q.js} +2 -2
- package/dist/{chunk-JSD5TFDK.js → chunk-YS67XMOP.js} +5 -5
- package/dist/{chunk-WC3W36AM.js → chunk-ZOUWHGV3.js} +4 -4
- package/dist/data-display/Badge.js +2 -2
- package/dist/data-display/CardKit.js +8 -8
- package/dist/data-display/Chart.js +5 -5
- package/dist/data-display/DataViews.js +51 -51
- package/dist/data-display/FleetMap.js +11 -11
- package/dist/data-display/HoverCard.js +2 -2
- package/dist/data-display/Lightbox.js +2 -2
- package/dist/data-display/MapPreview.js +6 -6
- package/dist/data-display/Sheet.js +3 -3
- package/dist/data-display/Table.js +3 -3
- package/dist/feedback/ConfirmAction.js +6 -6
- package/dist/feedback/Modal.js +2 -2
- package/dist/feedback/NotificationStatusNotice.js +3 -3
- package/dist/form/AddressAutocomplete.js +6 -6
- package/dist/form/Button.js +2 -2
- package/dist/form/Calendar.js +3 -3
- package/dist/form/CategorySelect.js +2 -2
- package/dist/form/CodeEditor.js +2 -2
- package/dist/form/DateRangePicker.js +4 -4
- package/dist/form/InputOTP.js +2 -2
- package/dist/form/Menubar.js +2 -2
- package/dist/form/UploadButton.js +2 -2
- package/dist/form/total-form.js +8 -8
- package/dist/layout/ContentToolbar.js +3 -3
- package/dist/layout/ScrollArea.js +2 -2
- package/dist/layout/SettingsLayout.js +5 -4
- package/dist/navigation/AppHeader.js +6 -6
- package/dist/social-login-button.js +3 -3
- package/dist/types/components/layout/SettingsLayout/SettingsSectionChips.d.ts.map +1 -1
- package/dist/types/components/layout/SettingsLayout/SettingsSectionChips.scroll.d.ts +64 -0
- package/dist/types/components/layout/SettingsLayout/SettingsSectionChips.scroll.d.ts.map +1 -0
- package/dist/user-avatar.js +3 -3
- package/package.json +1 -1
- package/src/components/layout/SettingsLayout/SettingsLayout.md +3 -1
- package/src/components/layout/SettingsLayout/SettingsSectionChips.scroll.ts +180 -0
- package/src/components/layout/SettingsLayout/SettingsSectionChips.tsx +73 -15
- package/dist/chunk-XWZJLXJT.js.map +0 -1
- /package/dist/{chunk-Y2HPRRVP.js.map → chunk-3333KCAW.js.map} +0 -0
- /package/dist/{chunk-3RHZBO7L.js.map → chunk-36JKV7CA.js.map} +0 -0
- /package/dist/{chunk-QGFBIUOK.js.map → chunk-3CTQZDSD.js.map} +0 -0
- /package/dist/{chunk-R2WMSXQR.js.map → chunk-4BNV5AAI.js.map} +0 -0
- /package/dist/{chunk-OHC3F7GJ.js.map → chunk-4NPPO63U.js.map} +0 -0
- /package/dist/{chunk-ZXIUIIUE.js.map → chunk-4WF6ZLIW.js.map} +0 -0
- /package/dist/{chunk-6WLHEZXI.js.map → chunk-4Y6W7BT4.js.map} +0 -0
- /package/dist/{chunk-5KGABAQB.js.map → chunk-5752357K.js.map} +0 -0
- /package/dist/{chunk-TYNK662L.js.map → chunk-5CQNBMLK.js.map} +0 -0
- /package/dist/{chunk-3PQ5MZMJ.js.map → chunk-5FAI25JQ.js.map} +0 -0
- /package/dist/{chunk-YMG4CXGI.js.map → chunk-5KWHINO5.js.map} +0 -0
- /package/dist/{chunk-TT3HZRU6.js.map → chunk-5QD6YGZ2.js.map} +0 -0
- /package/dist/{chunk-JQ3D5QP6.js.map → chunk-5WT4YWXC.js.map} +0 -0
- /package/dist/{chunk-JGJFL3PF.js.map → chunk-6MFMLQ72.js.map} +0 -0
- /package/dist/{chunk-VIUDWOV6.js.map → chunk-7D2C4LK2.js.map} +0 -0
- /package/dist/{chunk-6QXFBZ7Q.js.map → chunk-7ICK5MXV.js.map} +0 -0
- /package/dist/{chunk-QS2DCLG6.js.map → chunk-A5B7B4HT.js.map} +0 -0
- /package/dist/{chunk-WYUBTRRV.js.map → chunk-AXKLCWFR.js.map} +0 -0
- /package/dist/{chunk-34BZXJGT.js.map → chunk-BKASAYQL.js.map} +0 -0
- /package/dist/{chunk-UAVSBSV5.js.map → chunk-BUPDBEBA.js.map} +0 -0
- /package/dist/{chunk-DIM6SUGY.js.map → chunk-DAHYLPK7.js.map} +0 -0
- /package/dist/{chunk-KBZFBHLO.js.map → chunk-DXZOSP6P.js.map} +0 -0
- /package/dist/{chunk-4GWHYLEB.js.map → chunk-DYGWKZ7T.js.map} +0 -0
- /package/dist/{chunk-PK6TRXV4.js.map → chunk-ECIHOZPK.js.map} +0 -0
- /package/dist/{chunk-QU3JX53V.js.map → chunk-ENRBXB6L.js.map} +0 -0
- /package/dist/{chunk-FWSMIUWA.js.map → chunk-ESCF5VMQ.js.map} +0 -0
- /package/dist/{chunk-QI5CRSOW.js.map → chunk-F4KYBBTY.js.map} +0 -0
- /package/dist/{chunk-NCEQWEXT.js.map → chunk-FNO5TWBJ.js.map} +0 -0
- /package/dist/{chunk-S6IWKDWT.js.map → chunk-GC56IWIZ.js.map} +0 -0
- /package/dist/{chunk-2EAAWOZ2.js.map → chunk-HM2JJNUE.js.map} +0 -0
- /package/dist/{chunk-OOCREE37.js.map → chunk-HMAPQV4C.js.map} +0 -0
- /package/dist/{chunk-2KWYNFVE.js.map → chunk-HUPR5JXY.js.map} +0 -0
- /package/dist/{chunk-F4WFUQYR.js.map → chunk-IFS3BRRL.js.map} +0 -0
- /package/dist/{chunk-3GTMGPNQ.js.map → chunk-IQJCLFF4.js.map} +0 -0
- /package/dist/{chunk-2ZRZTGWP.js.map → chunk-ISW5K72V.js.map} +0 -0
- /package/dist/{chunk-EDKLXA5W.js.map → chunk-J3XFUCCR.js.map} +0 -0
- /package/dist/{chunk-TTZPO345.js.map → chunk-JGRBHNK6.js.map} +0 -0
- /package/dist/{chunk-GS3RP2QE.js.map → chunk-KKN4LLVE.js.map} +0 -0
- /package/dist/{chunk-QFTBXHOY.js.map → chunk-LH46NYGR.js.map} +0 -0
- /package/dist/{chunk-56JF5PZK.js.map → chunk-LYVTJLCF.js.map} +0 -0
- /package/dist/{chunk-PZ7PU4AP.js.map → chunk-M2Q5CLPU.js.map} +0 -0
- /package/dist/{chunk-5L7N5HER.js.map → chunk-M4KVKQRN.js.map} +0 -0
- /package/dist/{chunk-ERY6D2OV.js.map → chunk-MWPXKZ7O.js.map} +0 -0
- /package/dist/{chunk-3AOQBR7N.js.map → chunk-OAW6GCS3.js.map} +0 -0
- /package/dist/{chunk-6N2EDWMG.js.map → chunk-OGB25P3N.js.map} +0 -0
- /package/dist/{chunk-R53ZOW4B.js.map → chunk-PARTZ7XI.js.map} +0 -0
- /package/dist/{chunk-7GHYCQCJ.js.map → chunk-PCLKFR27.js.map} +0 -0
- /package/dist/{chunk-K6VKL62Z.js.map → chunk-PG5C2AA4.js.map} +0 -0
- /package/dist/{chunk-R3JYC63P.js.map → chunk-QXSV7Z57.js.map} +0 -0
- /package/dist/{chunk-SNYJSACF.js.map → chunk-S6JHTEV4.js.map} +0 -0
- /package/dist/{chunk-5JCGBCFJ.js.map → chunk-SAOCDKNK.js.map} +0 -0
- /package/dist/{chunk-XG7NTGUH.js.map → chunk-T3DNYPEA.js.map} +0 -0
- /package/dist/{chunk-JU4MSYYH.js.map → chunk-T6EKPFKZ.js.map} +0 -0
- /package/dist/{chunk-Q2YNXJEL.js.map → chunk-TZBOWSAM.js.map} +0 -0
- /package/dist/{chunk-D4FZ3ANG.js.map → chunk-UKX3MPB7.js.map} +0 -0
- /package/dist/{chunk-TXGB2WSW.js.map → chunk-VJ2BNJ5M.js.map} +0 -0
- /package/dist/{chunk-7XGHJD75.js.map → chunk-VJG7NMGL.js.map} +0 -0
- /package/dist/{chunk-BWHOCNGW.js.map → chunk-VPPIODKX.js.map} +0 -0
- /package/dist/{chunk-4B5XUQQS.js.map → chunk-WB6ZU3B3.js.map} +0 -0
- /package/dist/{chunk-W7F2QIAT.js.map → chunk-WCRGSMLV.js.map} +0 -0
- /package/dist/{chunk-G62KCU3D.js.map → chunk-WL6N33AB.js.map} +0 -0
- /package/dist/{chunk-TKKQ7RGT.js.map → chunk-WZNTZX5N.js.map} +0 -0
- /package/dist/{chunk-KE473LK6.js.map → chunk-YMTZN54Q.js.map} +0 -0
- /package/dist/{chunk-JSD5TFDK.js.map → chunk-YS67XMOP.js.map} +0 -0
- /package/dist/{chunk-WC3W36AM.js.map → chunk-ZOUWHGV3.js.map} +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telling the strip's own scrolls from the visitor's (FUT-2606).
|
|
3
|
+
*
|
|
4
|
+
* Split from `SettingsSectionChips.tsx` so the chips stay a component file and
|
|
5
|
+
* the rule for "who moved the strip" can be read in one place.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* How near to where the strip aimed itself it has to come to rest for the scroll
|
|
10
|
+
* to count as the strip's own. A smooth scroll lands on a device pixel, and the
|
|
11
|
+
* target it was given is fractional.
|
|
12
|
+
*/
|
|
13
|
+
const LANDED_SLACK = 1;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Where `scrollend` is missing: how long the strip has to go without a `scroll`
|
|
17
|
+
* before it counts as settled. Generous on purpose — a busy main thread can go
|
|
18
|
+
* several frames between ticks of a smooth scroll, and judging one mid-flight
|
|
19
|
+
* would read the strip's own animation as the visitor's. Too long only delays a
|
|
20
|
+
* re-centre; too short can latch the strip for good.
|
|
21
|
+
*/
|
|
22
|
+
const QUIET_BEFORE_SETTLED_MS = 250;
|
|
23
|
+
|
|
24
|
+
interface VisitorScrollWatch {
|
|
25
|
+
/** True once the visitor has moved the strip themselves. */
|
|
26
|
+
hasScrolled: () => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* True while a scroll the visitor MAY be making has not come to rest yet. The
|
|
29
|
+
* strip must not scroll itself under it: that would yank a drag in progress.
|
|
30
|
+
*/
|
|
31
|
+
isUndecided: () => boolean;
|
|
32
|
+
/** Where the strip last aimed itself, in its own pixels; `NaN` before it has. */
|
|
33
|
+
aimedAt: () => number;
|
|
34
|
+
/**
|
|
35
|
+
* The strip is about to scroll itself to `left`. Records the aim and NOTHING
|
|
36
|
+
* else: it used to disarm too, and a re-centre landing between a visitor's key
|
|
37
|
+
* press (or wheel, or pan) and the first tick of the scroll it causes wiped
|
|
38
|
+
* the arming — their scroll went unclaimed, read as the strip's own. Left
|
|
39
|
+
* armed, the worst a stale arming does is put the strip's OWN next scroll
|
|
40
|
+
* under judgement, which rests on its aim, is not latched, and disarms.
|
|
41
|
+
*/
|
|
42
|
+
beforeOwnScroll: (left: number) => void;
|
|
43
|
+
/** Called once an undecided scroll has come to rest and been judged. */
|
|
44
|
+
onSettled: (listener: () => void) => void;
|
|
45
|
+
detach: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type Listeners = Array<[string, (event: Event) => void]>;
|
|
49
|
+
|
|
50
|
+
/** A wheel's sideways travel: `deltaX`, or `deltaY` with Shift held, as browsers read it. */
|
|
51
|
+
function horizontalDelta(event: Event): number {
|
|
52
|
+
if (!(event instanceof WheelEvent)) return 0;
|
|
53
|
+
return event.deltaX !== 0 ? event.deltaX : event.shiftKey ? event.deltaY : 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface WatchState {
|
|
57
|
+
pointer: boolean;
|
|
58
|
+
touch: boolean;
|
|
59
|
+
nudged: boolean;
|
|
60
|
+
undecided: boolean;
|
|
61
|
+
scrolled: boolean;
|
|
62
|
+
aimed: number;
|
|
63
|
+
quiet: ReturnType<typeof setTimeout> | undefined;
|
|
64
|
+
settled: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Whether the strip came to rest where it last aimed itself — clamped as the browser would. */
|
|
68
|
+
function restsOnAim(strip: HTMLElement, aimed: number): boolean {
|
|
69
|
+
const reachable = Math.min(aimed, strip.scrollWidth - strip.clientWidth);
|
|
70
|
+
// Written so that `NaN` — the strip never aimed — reads as "not on aim".
|
|
71
|
+
return Math.abs(strip.scrollLeft - reachable) <= LANDED_SLACK;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Tell a scroll the VISITOR made from every other one.
|
|
76
|
+
*
|
|
77
|
+
* The `scroll` event cannot say: the strip's own `scrollTo` fires it, and so does
|
|
78
|
+
* the browser clamping a strip whose content shrank. So a scroll is only a
|
|
79
|
+
* CANDIDATE when it arrives during something only a visitor does:
|
|
80
|
+
*
|
|
81
|
+
* - a finger held on the strip (`touchstart` until `touchend`) — a drag;
|
|
82
|
+
* - a pointer held on it (`pointerdown` until `pointerup`);
|
|
83
|
+
* - a key press, a SIDEWAYS wheel, or the browser taking a pointer over to pan
|
|
84
|
+
* it (`pointercancel`), each armed until the next scroll under judgement has
|
|
85
|
+
* been judged — whoever's it turns out to be.
|
|
86
|
+
*
|
|
87
|
+
* and the verdict waits until the strip comes to REST (`scrollend`, or a quiet
|
|
88
|
+
* spell where the browser has no `scrollend`): it is the visitor's only if the
|
|
89
|
+
* strip rests somewhere other than where it last aimed itself.
|
|
90
|
+
*
|
|
91
|
+
* Judged at rest rather than per `scroll` because the strip's own smooth scroll
|
|
92
|
+
* fires `scroll` for a third of a second, and a visitor who presses Tab or puts
|
|
93
|
+
* a finger on a chip to scroll the PAGE in that window is not scrolling the
|
|
94
|
+
* strip. Every tick of that animation used to count as theirs, the strip stopped
|
|
95
|
+
* re-centring for good, and the open chip could be left clipped (FUT-2606
|
|
96
|
+
* again, by another door). A visitor who really drags or wheels the strip
|
|
97
|
+
* mid-animation cancels it, so the strip rests where THEY left it — and that
|
|
98
|
+
* still counts. The one loss: a visitor who happens to leave it within a pixel
|
|
99
|
+
* of the strip's own aim is not told apart from it, and the strip there is
|
|
100
|
+
* already where it would have put itself.
|
|
101
|
+
*
|
|
102
|
+
* A tap is a pointer held with no scroll under it, so it never counts. A
|
|
103
|
+
* vertical wheel scrolls the PAGE — the strip is `overflowY: hidden` — and is
|
|
104
|
+
* not armed at all.
|
|
105
|
+
*/
|
|
106
|
+
export function watchVisitorScroll(strip: HTMLElement): VisitorScrollWatch {
|
|
107
|
+
const view = strip.ownerDocument.defaultView;
|
|
108
|
+
const hasScrollEnd = 'onscrollend' in strip;
|
|
109
|
+
const state: WatchState = {
|
|
110
|
+
pointer: false,
|
|
111
|
+
touch: false,
|
|
112
|
+
nudged: false,
|
|
113
|
+
undecided: false,
|
|
114
|
+
scrolled: false,
|
|
115
|
+
aimed: Number.NaN,
|
|
116
|
+
quiet: undefined,
|
|
117
|
+
settled: () => undefined,
|
|
118
|
+
};
|
|
119
|
+
const set =
|
|
120
|
+
(key: 'pointer' | 'touch' | 'nudged', value: boolean) =>
|
|
121
|
+
(): void => {
|
|
122
|
+
state[key] = value;
|
|
123
|
+
};
|
|
124
|
+
const settle = (): void => {
|
|
125
|
+
// Only a scroll that was UNDER JUDGEMENT disarms. Chromium can end the
|
|
126
|
+
// strip's own smooth scroll with a `scrollend` when another scroll cuts it
|
|
127
|
+
// off, fired BEFORE that scroll's first `scroll` (seen with an instant one);
|
|
128
|
+
// disarming there would hand the visitor's scroll to nobody.
|
|
129
|
+
if (!state.undecided) return;
|
|
130
|
+
state.undecided = false;
|
|
131
|
+
state.nudged = false;
|
|
132
|
+
if (!restsOnAim(strip, state.aimed)) state.scrolled = true;
|
|
133
|
+
state.settled();
|
|
134
|
+
};
|
|
135
|
+
const onWheel = (event: Event): void => {
|
|
136
|
+
if (horizontalDelta(event) !== 0) state.nudged = true;
|
|
137
|
+
};
|
|
138
|
+
const onScroll = (): void => {
|
|
139
|
+
if (state.pointer || state.touch || state.nudged) state.undecided = true;
|
|
140
|
+
if (!state.undecided || hasScrollEnd) return;
|
|
141
|
+
// Every tick while undecided, armed or not: a quiet spell that began while
|
|
142
|
+
// the strip was still moving would judge it mid-flight, away from its aim.
|
|
143
|
+
clearTimeout(state.quiet);
|
|
144
|
+
state.quiet = setTimeout(settle, QUIET_BEFORE_SETTLED_MS);
|
|
145
|
+
};
|
|
146
|
+
const onStrip: Listeners = [
|
|
147
|
+
['pointerdown', set('pointer', true)],
|
|
148
|
+
['touchstart', set('touch', true)],
|
|
149
|
+
['pointercancel', set('nudged', true)],
|
|
150
|
+
['keydown', set('nudged', true)],
|
|
151
|
+
['wheel', onWheel],
|
|
152
|
+
['scroll', onScroll],
|
|
153
|
+
['scrollend', settle],
|
|
154
|
+
];
|
|
155
|
+
// A gesture can END off the strip, so its end is heard on the window.
|
|
156
|
+
const onView: Listeners = [
|
|
157
|
+
['pointerup', set('pointer', false)],
|
|
158
|
+
['pointercancel', set('pointer', false)],
|
|
159
|
+
['touchend', set('touch', false)],
|
|
160
|
+
['touchcancel', set('touch', false)],
|
|
161
|
+
];
|
|
162
|
+
for (const [type, listener] of onStrip) strip.addEventListener(type, listener, { passive: true });
|
|
163
|
+
for (const [type, listener] of onView) view?.addEventListener(type, listener, { passive: true });
|
|
164
|
+
return {
|
|
165
|
+
hasScrolled: () => state.scrolled,
|
|
166
|
+
isUndecided: () => state.undecided,
|
|
167
|
+
aimedAt: () => state.aimed,
|
|
168
|
+
beforeOwnScroll: (left) => {
|
|
169
|
+
state.aimed = left;
|
|
170
|
+
},
|
|
171
|
+
onSettled: (listener) => {
|
|
172
|
+
state.settled = listener;
|
|
173
|
+
},
|
|
174
|
+
detach: () => {
|
|
175
|
+
clearTimeout(state.quiet);
|
|
176
|
+
for (const [type, listener] of onStrip) strip.removeEventListener(type, listener);
|
|
177
|
+
for (const [type, listener] of onView) view?.removeEventListener(type, listener);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -4,6 +4,7 @@ import Box from '@mui/material/Box/index.js';
|
|
|
4
4
|
import { alpha, useTheme, type Theme } from '@mui/material/styles/index.js';
|
|
5
5
|
import React, { useEffect, useRef, type RefObject } from 'react';
|
|
6
6
|
|
|
7
|
+
import { watchVisitorScroll } from './SettingsSectionChips.scroll';
|
|
7
8
|
import { SettingsStatusMarker } from './SettingsStatusMarker';
|
|
8
9
|
import { TOUCH_TARGET } from './SettingsLayout.styles';
|
|
9
10
|
import { rem, sxRem } from '../../../tokens/relative';
|
|
@@ -37,7 +38,38 @@ function prefersReducedMotion(): boolean {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
|
-
*
|
|
41
|
+
* Where the strip has to scroll for `chip`'s centre to sit on its own, clamped
|
|
42
|
+
* to what the strip can scroll.
|
|
43
|
+
*
|
|
44
|
+
* Measured from the two boxes rather than from `chip.offsetLeft`. `offsetLeft`
|
|
45
|
+
* counts from the nearest POSITIONED ancestor, and the strip is not one — nor
|
|
46
|
+
* are the panel and layout around it — so it counted from whatever box the host
|
|
47
|
+
* happened to position, and every target overshot by the strip's offset inside
|
|
48
|
+
* it. The clamp hid that for the last chip; a middle one was simply off-centre.
|
|
49
|
+
* The boxes are both in viewport coordinates, so their difference is the strip's
|
|
50
|
+
* own, whatever the host wraps it in.
|
|
51
|
+
*/
|
|
52
|
+
function centredScrollLeft(strip: HTMLElement, chip: HTMLElement): number {
|
|
53
|
+
const chipBox = chip.getBoundingClientRect();
|
|
54
|
+
const visibleStart = strip.getBoundingClientRect().left + strip.clientLeft;
|
|
55
|
+
const chipCentre = chipBox.left - visibleStart + strip.scrollLeft + chipBox.width / 2;
|
|
56
|
+
const target = chipCentre - strip.clientWidth / 2;
|
|
57
|
+
return Math.max(0, Math.min(target, strip.scrollWidth - strip.clientWidth));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The chip for `id`, walked rather than selected. */
|
|
61
|
+
function findChip(strip: HTMLElement, id: string): HTMLElement | undefined {
|
|
62
|
+
// An id is host data, and a section id carrying a quote would turn a selector
|
|
63
|
+
// string into a thrown SyntaxError — which here would take the whole strip
|
|
64
|
+
// down to centre one chip.
|
|
65
|
+
return Array.from(strip.children).find(
|
|
66
|
+
(node): node is HTMLElement => node instanceof HTMLElement && node.dataset.chipId === id,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Keep the open section's chip in view, however the visitor got here — and
|
|
72
|
+
* however the strip's content settles after it mounted.
|
|
41
73
|
*
|
|
42
74
|
* Keyed on `activeItemId` in an effect rather than done in a click handler:
|
|
43
75
|
* arriving by the desktop rail, by a card, by a pasted link and by the browser's
|
|
@@ -46,9 +78,22 @@ function prefersReducedMotion(): boolean {
|
|
|
46
78
|
* the active chip off the right edge on any list longer than a screen — the
|
|
47
79
|
* visitor is told where they are by an element they cannot see.
|
|
48
80
|
*
|
|
81
|
+
* Centring ONCE is not enough (FUT-2606). A host can widen a chip after mount
|
|
82
|
+
* with the ids unchanged — a status marker whose read resolves late, a web font
|
|
83
|
+
* arriving — and a strip that fitted at mount then overflows with its last chip
|
|
84
|
+
* clipped, because the one centring saw nothing to scroll. So the strip and
|
|
85
|
+
* every chip are watched with a `ResizeObserver`, and a resize re-aims at the
|
|
86
|
+
* active chip when its target has moved.
|
|
87
|
+
*
|
|
88
|
+
* Never against the visitor, though: once they have scrolled the strip
|
|
89
|
+
* themselves, it is theirs until the open section changes
|
|
90
|
+
* ({@link watchVisitorScroll}, which also says why the strip's OWN smooth scroll
|
|
91
|
+
* never counts as theirs). A strip that yanks back mid-drag because a
|
|
92
|
+
* marker loaded is worse than one that does not centre.
|
|
93
|
+
*
|
|
49
94
|
* `itemsKey` rather than `items`: a host almost always rebuilds that array per
|
|
50
95
|
* render (`groups.flatMap(...)`), and depending on it would re-run this on every
|
|
51
|
-
* render and
|
|
96
|
+
* render and reset the visitor's latch with it.
|
|
52
97
|
*
|
|
53
98
|
* The key joins on NUL — as the ESCAPE `'\u0000'`, which it must stay. A
|
|
54
99
|
* literal NUL byte works identically at runtime and makes git classify this file
|
|
@@ -63,25 +108,38 @@ function useCentreActiveChip(
|
|
|
63
108
|
): void {
|
|
64
109
|
useEffect(() => {
|
|
65
110
|
const strip = stripRef.current;
|
|
66
|
-
if (!strip || !activeItemId) return;
|
|
67
|
-
// Walked rather than selected: an id is host data, and a section id carrying
|
|
68
|
-
// a quote would turn a selector string into a thrown SyntaxError — which
|
|
69
|
-
// here would take the whole strip down to centre one chip.
|
|
70
|
-
const chip = Array.from(strip.children).find(
|
|
71
|
-
(node): node is HTMLElement =>
|
|
72
|
-
node instanceof HTMLElement && node.dataset.chipId === activeItemId,
|
|
73
|
-
);
|
|
74
|
-
if (!chip) return;
|
|
111
|
+
if (!strip || !activeItemId) return undefined;
|
|
75
112
|
// Feature-checked, not assumed. `Element.scrollTo` is absent in jsdom and in
|
|
76
113
|
// a few embedded webviews, and an unguarded call there THROWS out of an
|
|
77
114
|
// effect — which React escalates to the nearest error boundary. The whole
|
|
78
115
|
// settings area would go down to centre one chip, and the host's own tests
|
|
79
116
|
// could not render the shell at all. Same reasoning as walking the children
|
|
80
117
|
// instead of building a selector: nothing in here is worth a crash.
|
|
81
|
-
if (typeof strip.scrollTo !== 'function') return;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
118
|
+
if (typeof strip.scrollTo !== 'function') return undefined;
|
|
119
|
+
const visitor = watchVisitorScroll(strip);
|
|
120
|
+
const centre = (): void => {
|
|
121
|
+
const chip = findChip(strip, activeItemId);
|
|
122
|
+
// Not while a scroll that may be the visitor's is still moving: a resize
|
|
123
|
+
// mid-drag must not yank the strip. It is re-tried once that scroll rests.
|
|
124
|
+
if (!chip || visitor.hasScrolled() || visitor.isUndecided()) return;
|
|
125
|
+
const left = centredScrollLeft(strip, chip);
|
|
126
|
+
// Only when the target MOVED: a resize that leaves it where it was must not
|
|
127
|
+
// restart a smooth scroll that is already on its way there. The aim is a
|
|
128
|
+
// runtime-computed scroll offset, like the exempt `left` below — not a size.
|
|
129
|
+
if (Math.abs(left - visitor.aimedAt()) < 0.5) return;
|
|
130
|
+
visitor.beforeOwnScroll(left);
|
|
131
|
+
strip.scrollTo({ left, behavior: prefersReducedMotion() ? 'auto' : 'smooth' });
|
|
132
|
+
};
|
|
133
|
+
visitor.onSettled(centre);
|
|
134
|
+
centre();
|
|
135
|
+
// Also feature-checked: without it the strip still centres once, as before.
|
|
136
|
+
const resizes = typeof ResizeObserver === 'function' ? new ResizeObserver(centre) : undefined;
|
|
137
|
+
resizes?.observe(strip);
|
|
138
|
+
for (const child of Array.from(strip.children)) resizes?.observe(child);
|
|
139
|
+
return () => {
|
|
140
|
+
resizes?.disconnect();
|
|
141
|
+
visitor.detach();
|
|
142
|
+
};
|
|
85
143
|
}, [stripRef, activeItemId, itemsKey]);
|
|
86
144
|
}
|
|
87
145
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/layout/SettingsLayout/SettingsSectionChips.tsx"],"sourcesContent":["'use client';\n\nimport Box from '@mui/material/Box/index.js';\nimport { alpha, useTheme, type Theme } from '@mui/material/styles/index.js';\nimport React, { useEffect, useRef, type RefObject } from 'react';\n\nimport { SettingsStatusMarker } from './SettingsStatusMarker';\nimport { TOUCH_TARGET } from './SettingsLayout.styles';\nimport { rem, sxRem } from '../../../tokens/relative';\n\n/**\n * The chip's drawn height, against `TOUCH_TARGET`'s 44 for the tappable one.\n *\n * The strip sits between a section's header and its first field on the width\n * with the least room for either, so the pill is sized to its text rather than\n * to the thumb — and the thumb is served by the hit area instead.\n */\nconst CHIP_HEIGHT = sxRem(34);\nimport type { SettingsLayoutProps, SettingsNavItem } from './SettingsLayout.types';\n\nexport interface SettingsSectionChipsProps {\n /** The sibling sections to offer — usually the open item's own group. */\n items: SettingsNavItem[];\n /** Which one is open. */\n activeItemId?: string;\n /** Accessible name of the strip, in the host's language. */\n ariaLabel: string;\n linkComponent?: SettingsLayoutProps['linkComponent'];\n onSelectItem?: (id: string) => void;\n testIdPrefix: string;\n}\n\n/** True when the visitor asked the OS for less movement. Re-read per scroll. */\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/**\n * Keep the open section's chip in view, however the visitor got here.\n *\n * Keyed on `activeItemId` in an effect rather than done in a click handler:\n * arriving by the desktop rail, by a card, by a pasted link and by the browser's\n * back button all have to land the same way, and only one of those is a click on\n * a chip. Every re-render otherwise starts the strip at scroll 0, which parks\n * the active chip off the right edge on any list longer than a screen — the\n * visitor is told where they are by an element they cannot see.\n *\n * `itemsKey` rather than `items`: a host almost always rebuilds that array per\n * render (`groups.flatMap(...)`), and depending on it would re-run this on every\n * render and yank the strip back mid-drag.\n *\n * The key joins on NUL — as the ESCAPE `'\\u0000'`, which it must stay. A\n * literal NUL byte works identically at runtime and makes git classify this file\n * as BINARY: `git diff` prints \"Binary file not shown\" and the GitHub review UI\n * shows nothing at all. This file shipped that way once, and the cost was not\n * the byte — it was a whole change nobody could read.\n */\nfunction useCentreActiveChip(\n stripRef: RefObject<HTMLDivElement | null>,\n activeItemId: string | undefined,\n itemsKey: string,\n): void {\n useEffect(() => {\n const strip = stripRef.current;\n if (!strip || !activeItemId) return;\n // Walked rather than selected: an id is host data, and a section id carrying\n // a quote would turn a selector string into a thrown SyntaxError — which\n // here would take the whole strip down to centre one chip.\n const chip = Array.from(strip.children).find(\n (node): node is HTMLElement =>\n node instanceof HTMLElement && node.dataset.chipId === activeItemId,\n );\n if (!chip) return;\n // Feature-checked, not assumed. `Element.scrollTo` is absent in jsdom and in\n // a few embedded webviews, and an unguarded call there THROWS out of an\n // effect — which React escalates to the nearest error boundary. The whole\n // settings area would go down to centre one chip, and the host's own tests\n // could not render the shell at all. Same reasoning as walking the children\n // instead of building a selector: nothing in here is worth a crash.\n if (typeof strip.scrollTo !== 'function') return;\n const target = chip.offsetLeft + chip.offsetWidth / 2 - strip.clientWidth / 2;\n const left = Math.max(0, Math.min(target, strip.scrollWidth - strip.clientWidth));\n strip.scrollTo({ left, behavior: prefersReducedMotion() ? 'auto' : 'smooth' });\n }, [stripRef, activeItemId, itemsKey]);\n}\n\n/** One chip: a link when the host gave it a target, else a button. */\nfunction SectionChip({\n item,\n active,\n theme,\n linkComponent,\n onSelectItem,\n testIdPrefix,\n}: {\n item: SettingsNavItem;\n active: boolean;\n theme: Theme;\n linkComponent?: SettingsLayoutProps['linkComponent'];\n onSelectItem?: (id: string) => void;\n testIdPrefix: string;\n}): React.JSX.Element {\n const asLink = item.href !== undefined && linkComponent !== undefined;\n const behaviour = asLink\n ? { component: linkComponent, href: item.href }\n : { component: 'button' as const, type: 'button' as const, onClick: () => onSelectItem?.(item.id) };\n\n return (\n <Box\n {...behaviour}\n data-chip-id={item.id}\n data-testid={`${testIdPrefix}-chip-${item.id}`}\n aria-current={active ? 'page' : undefined}\n sx={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 0.75,\n flex: '0 0 auto',\n position: 'relative',\n // The chip LOOKS this tall; it is still tapped at `TOUCH_TARGET`.\n //\n // Shrinking `minHeight` alone would have bought a slimmer strip with a\n // smaller hit area — on the one width where the strip IS the navigation\n // and the input is a thumb. The `::after` below keeps the tappable\n // region at the full target while the pill itself takes less room, so\n // this is a visual change and not an accessibility trade.\n minHeight: CHIP_HEIGHT,\n px: 1.5,\n borderRadius: 999,\n cursor: 'pointer',\n font: 'inherit',\n fontSize: rem(theme, 13),\n fontWeight: active ? 700 : 500,\n textDecoration: 'none',\n whiteSpace: 'nowrap',\n color: active ? 'primary.main' : 'text.secondary',\n bgcolor: active ? alpha(theme.palette.primary.main, 0.12) : 'transparent',\n border: `1px solid ${active ? alpha(theme.palette.primary.main, 0.4) : theme.palette.divider}`,\n // Spans the chip's width only, so neighbours never overlap: the strip\n // is a row, and the extra reach is vertical.\n '&::after': {\n content: '\"\"',\n position: 'absolute',\n insetInlineStart: 0,\n insetInlineEnd: 0,\n top: '50%',\n transform: 'translateY(-50%)',\n height: rem(theme, TOUCH_TARGET),\n },\n }}\n >\n {item.status ? (\n <SettingsStatusMarker\n status={item.status}\n label={item.statusLabel}\n testId={`${testIdPrefix}-chip-status-${item.id}`}\n />\n ) : null}\n {item.label}\n </Box>\n );\n}\n\n/**\n * The narrow-width section strip: horizontally scrollable chips for the sections\n * beside the open one.\n *\n * It clips its own overflow — `overflow-x: auto` on the strip, never on an\n * ancestor. A strip that pushes the page wide makes every OTHER screen at that\n * width scroll sideways too, and the cause is nowhere near the symptom.\n */\nexport function SettingsSectionChips({\n items,\n activeItemId,\n ariaLabel,\n linkComponent,\n onSelectItem,\n testIdPrefix,\n}: SettingsSectionChipsProps): React.JSX.Element {\n const theme = useTheme();\n const stripRef = useRef<HTMLDivElement | null>(null);\n useCentreActiveChip(stripRef, activeItemId, items.map((item) => item.id).join('\\u0000'));\n\n return (\n <Box\n ref={stripRef}\n component=\"nav\"\n aria-label={ariaLabel}\n data-testid={`${testIdPrefix}-chips`}\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 0.75,\n px: 0.25,\n py: 0.25,\n overflowX: 'auto',\n overflowY: 'hidden',\n scrollbarWidth: 'none',\n '&::-webkit-scrollbar': { display: 'none' },\n }}\n >\n {items.map((item) => (\n <SectionChip\n key={item.id}\n item={item}\n active={item.id === activeItemId}\n theme={theme}\n linkComponent={linkComponent}\n onSelectItem={onSelectItem}\n testIdPrefix={testIdPrefix}\n />\n ))}\n </Box>\n );\n}\n\nSettingsSectionChips.displayName = 'SettingsSectionChips';\n"],"mappings":"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","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|