@12-apps/ui 6.100.0 → 6.102.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/NATIVE.md +2 -1
- package/components-catalog.md +3 -2
- package/dist/_internal/components/data-display/Alert/Alert.content.js +2 -2
- package/dist/_internal/components/data-display/Alert/Alert.js +4 -4
- package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
- package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
- package/dist/_internal/components/data-display/CardKit/card-actions-context.js +5 -5
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +6 -6
- package/dist/_internal/components/data-display/Carousel/Carousel.js +3 -3
- package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
- package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
- 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/DataGrid/DataGrid.js +3 -3
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +45 -45
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +60 -60
- package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +4 -4
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +4 -4
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +4 -4
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +4 -4
- package/dist/_internal/components/data-display/DataViews/base-list-card-layout.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 +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +8 -8
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +20 -20
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +42 -42
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +24 -24
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +21 -21
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +22 -22
- package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-range-calendar.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +11 -11
- package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +10 -10
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +3 -3
- package/dist/_internal/components/data-display/DataViews/list-card-rails.js +3 -3
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
- package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.js +3 -3
- package/dist/_internal/components/data-display/DayRuler/DayRuler.js +10 -10
- package/dist/_internal/components/data-display/DayRuler/DayRuler.keyboard.js +2 -2
- package/dist/_internal/components/data-display/DayRuler/DayRuler.labels.js +2 -2
- package/dist/_internal/components/data-display/DayRuler/DayRuler.parts.js +3 -3
- package/dist/_internal/components/data-display/DayRuler/useRulerDrag.js +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +10 -10
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +9 -9
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +12 -12
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +7 -7
- package/dist/_internal/components/data-display/Lightbox/LightboxControls.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/LightboxOverlay.js +3 -3
- package/dist/_internal/components/data-display/Lightbox/LightboxThumbnails.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapLayers.js +3 -3
- package/dist/_internal/components/data-display/MapPreview/MapMarkers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +8 -8
- package/dist/_internal/components/data-display/Progress/Progress.js +2 -2
- package/dist/_internal/components/data-display/RegionMap/RegionMap.js +1 -1
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +8 -8
- package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.parts.js +3 -3
- package/dist/_internal/components/data-display/Sheet/Sheet.js +2 -2
- package/dist/_internal/components/data-display/Table/Table.js +3 -3
- package/dist/_internal/components/data-display/index.js +72 -72
- package/dist/_internal/components/feedback/Dialog/Dialog.js +3 -3
- package/dist/_internal/components/feedback/Modal/Modal.js +2 -2
- package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +5 -5
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.frame.js +8 -8
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.header.js +4 -4
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +16 -16
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.overview.js +6 -6
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +5 -5
- package/dist/_internal/components/feedback/TutorialOverlay/TutorialOverlay.js +2 -2
- package/dist/_internal/components/feedback/UsableModal/UsableModal.js +1 -1
- package/dist/_internal/components/feedback/UsableModal/UsableModal.sheet.js +2 -2
- package/dist/_internal/components/feedback/index.js +21 -21
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.hooks.js +2 -2
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.js +7 -7
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocompleteField.js +2 -2
- package/dist/_internal/components/form/Autocomplete/Autocomplete.js +1 -1
- package/dist/_internal/components/form/Calendar/Calendar.js +5 -5
- package/dist/_internal/components/form/Calendar/CalendarSelectorsMonth.js +3 -3
- package/dist/_internal/components/form/CategorySelect/CategoryPanel.js +4 -4
- package/dist/_internal/components/form/CategorySelect/CategorySelect.js +7 -7
- package/dist/_internal/components/form/CodeEditor/CodeEditor.js +2 -2
- package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +8 -8
- package/dist/_internal/components/form/InputOTP/InputOTP.base.js +1 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.base.js.map +1 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.core.js +16 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.core.js.map +1 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.hooks.js +2 -1
- package/dist/_internal/components/form/InputOTP/InputOTP.js +5 -3
- package/dist/_internal/components/form/InputOTP/InputOTP.metrics.js +20 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.metrics.js.map +1 -0
- package/dist/_internal/components/form/InputOTP/InputOTP.styles.js +2 -1
- package/dist/_internal/components/form/index.js +25 -23
- package/dist/_internal/components/form/total-form/Fields/CategoryField.js +8 -8
- package/dist/_internal/components/form/total-form/Fields/index.js +9 -9
- package/dist/_internal/components/index.js +120 -118
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +6 -6
- package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +6 -6
- package/dist/_internal/components/navigation/SectionNav/SectionNav.js +9 -9
- package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +3 -3
- package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +4 -4
- package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +5 -5
- package/dist/_internal/components/navigation/SectionNav/SectionNav.railCreate.js +4 -4
- package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +4 -4
- package/dist/_internal/components/navigation/index.js +15 -15
- package/dist/_internal/user-avatar/user-avatar.js +3 -3
- package/dist/charts.js +6 -6
- package/dist/{chunk-37MJZLQU.js → chunk-24OFA44S.js} +4 -4
- package/dist/{chunk-R775ISK6.js → chunk-2L6TANRY.js} +5 -5
- package/dist/{chunk-B4OZU7J4.js → chunk-423A7NUP.js} +7 -7
- package/dist/{chunk-LFECE4R7.js → chunk-4EDCF7TC.js} +2 -2
- package/dist/{chunk-HI7JPDD6.js → chunk-5357QUVE.js} +2 -2
- package/dist/{chunk-F7JRQ2EI.js → chunk-5YX2VQFP.js} +7 -7
- package/dist/{chunk-N5PVD4J5.js → chunk-6DO672AG.js} +7 -7
- package/dist/{chunk-PGJIWCQR.js → chunk-6EZQLNPK.js} +4 -4
- package/dist/{chunk-YQMJJ7AL.js → chunk-6G2RAZTS.js} +5 -5
- package/dist/{chunk-KNW63H63.js → chunk-6PQZIZMN.js} +5 -5
- package/dist/{chunk-FZZMNKVF.js → chunk-6TAPAIRR.js} +8 -8
- package/dist/{chunk-ERAMSYPR.js → chunk-7N4O632I.js} +2 -2
- package/dist/{chunk-XFOKLWJZ.js → chunk-7S5PXEO7.js} +2 -2
- package/dist/{chunk-UIJLDAV7.js → chunk-AA6YMOEK.js} +2 -2
- package/dist/{chunk-CRD2WCU7.js → chunk-AFWUQJ4P.js} +8 -8
- package/dist/{chunk-SNWCT6JN.js → chunk-AMIUZRDQ.js} +3 -3
- package/dist/{chunk-3I7KE2N6.js → chunk-BNV7P52K.js} +2 -2
- package/dist/{chunk-64UNVW6D.js → chunk-BP3ZKIPE.js} +4 -4
- package/dist/{chunk-3V7OTBUZ.js → chunk-BQ2SFA27.js} +2 -2
- package/dist/{chunk-Q5SCBQLY.js → chunk-BXLC3TBQ.js} +8 -8
- package/dist/{chunk-Z5V5E3JW.js → chunk-COKPPU42.js} +11 -11
- package/dist/{chunk-QADTI63U.js → chunk-CZVAVX5L.js} +4 -4
- package/dist/{chunk-WDEVKND3.js → chunk-D2YJPZMU.js} +9 -9
- package/dist/{chunk-OA6T2ME3.js → chunk-DYHOBKPU.js} +5 -5
- package/dist/{chunk-6WAP7BJS.js → chunk-E6SPWZRN.js} +2 -2
- package/dist/{chunk-UTIYB6VE.js → chunk-E73PAU4U.js} +19 -19
- package/dist/{chunk-XR474YYX.js → chunk-EDVAFY76.js} +2 -2
- package/dist/{chunk-MEMVZW64.js → chunk-EPNZQWS4.js} +3 -3
- package/dist/{chunk-GZITNT7O.js → chunk-FGZIEZEP.js} +11 -11
- package/dist/{chunk-4X2TOMK6.js → chunk-FKUVFPQZ.js} +4 -4
- package/dist/{chunk-UN5WU2OX.js → chunk-GDWU7WF4.js} +4 -4
- package/dist/{chunk-643YFWNK.js → chunk-GPWDP5FF.js} +3 -3
- package/dist/{chunk-XA5ES25V.js → chunk-HKQWFICM.js} +2 -2
- package/dist/chunk-IH6DZXTG.js +22 -0
- package/dist/chunk-IH6DZXTG.js.map +1 -0
- package/dist/{chunk-255WTBZ5.js → chunk-IWSTMVCA.js} +4 -4
- package/dist/{chunk-QZOQVJ5L.js → chunk-J3WJEUD2.js} +19 -5
- package/dist/chunk-J3WJEUD2.js.map +1 -0
- package/dist/{chunk-TZC6KIK2.js → chunk-JG733RMP.js} +5 -5
- package/dist/{chunk-7MLT66DE.js → chunk-K33X3PDT.js} +2 -2
- package/dist/{chunk-K3PMMW23.js → chunk-K6VEN5VN.js} +4 -4
- package/dist/{chunk-HOWAMSUY.js → chunk-KAWVH3AA.js} +3 -3
- package/dist/{chunk-IJVBLU25.js → chunk-KJZ4PINO.js} +3 -3
- package/dist/{chunk-REHGWN5M.js → chunk-KXDLLS62.js} +4 -4
- package/dist/{chunk-YQ5GQ32T.js → chunk-KZP5YTE7.js} +2 -2
- package/dist/{chunk-3V3Z4EML.js → chunk-L4FWEI2P.js} +5 -5
- package/dist/{chunk-UKUGSU3Y.js → chunk-L4V7FZJI.js} +2 -2
- package/dist/{chunk-W56KN53D.js → chunk-L6IDPTNX.js} +6 -6
- package/dist/{chunk-24HHUCWB.js → chunk-LDCZPZPD.js} +2 -2
- package/dist/{chunk-KV7LTOWC.js → chunk-M225SD4H.js} +13 -13
- package/dist/{chunk-R6L376KR.js → chunk-M6R32IBO.js} +8 -13
- package/dist/chunk-M6R32IBO.js.map +1 -0
- package/dist/{chunk-FQMLTCXT.js → chunk-MAHY7PK3.js} +4 -4
- package/dist/{chunk-Z2J4MRTF.js → chunk-MKNW4FRR.js} +4 -4
- package/dist/{chunk-ILJ3FXIC.js → chunk-MM3QESLI.js} +2 -2
- package/dist/{chunk-FYJL432J.js → chunk-MQPUS773.js} +6 -6
- package/dist/{chunk-VRSW7PIO.js → chunk-NKBSWAP3.js} +4 -4
- package/dist/{chunk-ULC4SCFC.js → chunk-NLJ7LL6H.js} +4 -4
- package/dist/{chunk-RAQD5QCM.js → chunk-NQ6JP5CC.js} +6 -6
- package/dist/{chunk-IFOHCY4O.js → chunk-OF3KG5EV.js} +2 -2
- package/dist/{chunk-W676GY42.js → chunk-P772HCHI.js} +4 -4
- package/dist/{chunk-CWRQGA2T.js → chunk-POIENBXE.js} +7 -7
- package/dist/{chunk-FW5GTAUY.js → chunk-Q4AX4FOK.js} +11 -11
- package/dist/{chunk-XQAFSC66.js → chunk-Q4FNCT4C.js} +2 -2
- package/dist/{chunk-PIELY4BJ.js → chunk-Q5Z66LKI.js} +1 -1
- package/dist/{chunk-4Y3QLISA.js → chunk-Q7AONB4G.js} +4 -4
- package/dist/{chunk-EZBBLLEU.js → chunk-QQBYZ3B3.js} +4 -4
- package/dist/{chunk-FT3LJZWZ.js → chunk-RWUXMBSO.js} +4 -4
- package/dist/{chunk-KWBMM6MJ.js → chunk-RZKKWIGQ.js} +9 -9
- package/dist/{chunk-RVBLOR32.js → chunk-S7CXVIEF.js} +2 -2
- package/dist/{chunk-WLLUARTC.js → chunk-T2G4LDU5.js} +2 -2
- package/dist/{chunk-OFRQMFWD.js → chunk-T5D3DYVN.js} +2 -2
- package/dist/{chunk-O4UXH7BM.js → chunk-TP6KZ2YR.js} +7 -7
- package/dist/{chunk-HAWOS4SF.js → chunk-TPDN3B5T.js} +2 -2
- package/dist/{chunk-54DOLCJ3.js → chunk-TXIIJW6C.js} +5 -5
- package/dist/{chunk-HN36UVOE.js → chunk-U4Z6GZDW.js} +4 -4
- package/dist/{chunk-J2IQ6FJ5.js → chunk-UKLNLWTP.js} +3 -3
- package/dist/{chunk-NQYC7V5N.js → chunk-VJ6ZLMQ3.js} +18 -13
- package/dist/chunk-VJ6ZLMQ3.js.map +1 -0
- package/dist/{chunk-MKHNLRVX.js → chunk-VPPD2AJN.js} +7 -7
- package/dist/{chunk-TIM4PXWN.js → chunk-VWRXSBT2.js} +2 -2
- package/dist/{chunk-KVP5A3YK.js → chunk-WDWZKTGA.js} +2 -2
- package/dist/{chunk-FYSS2PYA.js → chunk-WH5ZEBKS.js} +7 -7
- package/dist/{chunk-KAGXV3ID.js → chunk-WMMXOSZG.js} +7 -7
- package/dist/{chunk-4RSFYS2V.js → chunk-X6D5JEUV.js} +6 -6
- package/dist/{chunk-GFYDOH7D.js → chunk-XEPNAU3T.js} +2 -2
- package/dist/{chunk-VMACQT3T.js → chunk-XH7UDC5Y.js} +3 -3
- package/dist/{chunk-PDWQJFMK.js → chunk-XHMHHP7B.js} +10 -10
- package/dist/chunk-XHPL5VI2.js +31 -0
- package/dist/chunk-XHPL5VI2.js.map +1 -0
- package/dist/{chunk-SA3KLZDZ.js → chunk-XNAI7QGU.js} +5 -5
- package/dist/{chunk-7TENMQNQ.js → chunk-XZMSRHFD.js} +4 -4
- package/dist/{chunk-HBHBRRSW.js → chunk-YBAK2WN5.js} +7 -7
- package/dist/{chunk-QLKUWDMQ.js → chunk-YDJ5GAGL.js} +13 -13
- package/dist/{chunk-LOLYBFYY.js → chunk-ZCNR4DN5.js} +5 -5
- package/dist/data-display/Alert.js +4 -4
- package/dist/data-display/Avatar.js +2 -2
- package/dist/data-display/Badge.js +2 -2
- package/dist/data-display/CardKit.js +14 -14
- package/dist/data-display/Carousel.js +3 -3
- package/dist/data-display/Chart.js +6 -6
- package/dist/data-display/DataGrid.js +3 -3
- package/dist/data-display/DataViews.js +74 -74
- package/dist/data-display/DayRuler.js +12 -12
- package/dist/data-display/FleetMap.js +12 -12
- package/dist/data-display/HoverCard.js +2 -2
- package/dist/data-display/Lightbox.js +7 -7
- package/dist/data-display/MapPreview.js +8 -8
- package/dist/data-display/Progress.js +2 -2
- package/dist/data-display/RegionMap.js +4 -4
- package/dist/data-display/ScheduleGrid.js +10 -10
- package/dist/data-display/Sheet.js +2 -2
- package/dist/data-display/Table.js +3 -3
- package/dist/feedback/Dialog.js +3 -3
- package/dist/feedback/Modal.js +2 -2
- package/dist/feedback/NotificationStatusNotice.js +5 -5
- package/dist/feedback/SequenceResolver.js +16 -16
- package/dist/feedback/TutorialOverlay.js +2 -2
- package/dist/feedback/UsableModal.js +1 -1
- package/dist/form/AddressAutocomplete.js +7 -7
- package/dist/form/Autocomplete.js +1 -1
- package/dist/form/Calendar.js +5 -5
- package/dist/form/CategorySelect.js +7 -7
- package/dist/form/CodeEditor.js +2 -2
- package/dist/form/DateRangePicker.js +10 -10
- package/dist/form/InputOTP.js +5 -3
- package/dist/form/total-form.js +9 -9
- package/dist/native/form/InputOTP.js +241 -0
- package/dist/native/form/InputOTP.js.map +1 -0
- package/dist/navigation/AppHeader.js +6 -6
- package/dist/navigation/SectionNav.js +9 -9
- package/dist/types/components/form/InputOTP/InputOTP.base.d.ts +77 -0
- package/dist/types/components/form/InputOTP/InputOTP.base.d.ts.map +1 -0
- package/dist/types/components/form/InputOTP/InputOTP.core.d.ts +38 -0
- package/dist/types/components/form/InputOTP/InputOTP.core.d.ts.map +1 -0
- package/dist/types/components/form/InputOTP/InputOTP.d.ts +1 -2
- package/dist/types/components/form/InputOTP/InputOTP.d.ts.map +1 -1
- package/dist/types/components/form/InputOTP/InputOTP.hooks.d.ts.map +1 -1
- package/dist/types/components/form/InputOTP/InputOTP.metrics.d.ts +40 -0
- package/dist/types/components/form/InputOTP/InputOTP.metrics.d.ts.map +1 -0
- package/dist/types/components/form/InputOTP/InputOTP.styles.d.ts +2 -0
- package/dist/types/components/form/InputOTP/InputOTP.styles.d.ts.map +1 -1
- package/dist/types/components/form/InputOTP/InputOTP.types.d.ts +3 -56
- package/dist/types/components/form/InputOTP/InputOTP.types.d.ts.map +1 -1
- package/dist/types-native/components/form/InputOTP/InputOTP.base.d.ts +77 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.base.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.core.d.ts +38 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.core.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.hooks.native.d.ts +28 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.hooks.native.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.metrics.d.ts +40 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.metrics.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.native.d.ts +14 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.native.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.types.native.d.ts +14 -0
- package/dist/types-native/components/form/InputOTP/InputOTP.types.native.d.ts.map +1 -0
- package/dist/types-native/components/form/InputOTP/index.native.d.ts +3 -0
- package/dist/types-native/components/form/InputOTP/index.native.d.ts.map +1 -0
- package/dist/user-avatar.js +3 -3
- package/package.json +5 -1
- package/src/components/form/InputOTP/InputOTP.base.ts +91 -0
- package/src/components/form/InputOTP/InputOTP.core.ts +51 -0
- package/src/components/form/InputOTP/InputOTP.hooks.native.ts +84 -0
- package/src/components/form/InputOTP/InputOTP.hooks.ts +1 -46
- package/src/components/form/InputOTP/InputOTP.md +35 -0
- package/src/components/form/InputOTP/InputOTP.metrics.ts +51 -0
- package/src/components/form/InputOTP/InputOTP.native.tsx +217 -0
- package/src/components/form/InputOTP/InputOTP.styles.ts +35 -16
- package/src/components/form/InputOTP/InputOTP.tsx +17 -3
- package/src/components/form/InputOTP/InputOTP.types.native.ts +15 -0
- package/src/components/form/InputOTP/InputOTP.types.ts +3 -68
- package/src/components/form/InputOTP/NATIVE-NOTES.md +8 -0
- package/src/components/form/InputOTP/index.native.ts +2 -0
- package/dist/chunk-NQYC7V5N.js.map +0 -1
- package/dist/chunk-QZOQVJ5L.js.map +0 -1
- package/dist/chunk-R6L376KR.js.map +0 -1
- /package/dist/{chunk-37MJZLQU.js.map → chunk-24OFA44S.js.map} +0 -0
- /package/dist/{chunk-R775ISK6.js.map → chunk-2L6TANRY.js.map} +0 -0
- /package/dist/{chunk-B4OZU7J4.js.map → chunk-423A7NUP.js.map} +0 -0
- /package/dist/{chunk-LFECE4R7.js.map → chunk-4EDCF7TC.js.map} +0 -0
- /package/dist/{chunk-HI7JPDD6.js.map → chunk-5357QUVE.js.map} +0 -0
- /package/dist/{chunk-F7JRQ2EI.js.map → chunk-5YX2VQFP.js.map} +0 -0
- /package/dist/{chunk-N5PVD4J5.js.map → chunk-6DO672AG.js.map} +0 -0
- /package/dist/{chunk-PGJIWCQR.js.map → chunk-6EZQLNPK.js.map} +0 -0
- /package/dist/{chunk-YQMJJ7AL.js.map → chunk-6G2RAZTS.js.map} +0 -0
- /package/dist/{chunk-KNW63H63.js.map → chunk-6PQZIZMN.js.map} +0 -0
- /package/dist/{chunk-FZZMNKVF.js.map → chunk-6TAPAIRR.js.map} +0 -0
- /package/dist/{chunk-ERAMSYPR.js.map → chunk-7N4O632I.js.map} +0 -0
- /package/dist/{chunk-XFOKLWJZ.js.map → chunk-7S5PXEO7.js.map} +0 -0
- /package/dist/{chunk-UIJLDAV7.js.map → chunk-AA6YMOEK.js.map} +0 -0
- /package/dist/{chunk-CRD2WCU7.js.map → chunk-AFWUQJ4P.js.map} +0 -0
- /package/dist/{chunk-SNWCT6JN.js.map → chunk-AMIUZRDQ.js.map} +0 -0
- /package/dist/{chunk-3I7KE2N6.js.map → chunk-BNV7P52K.js.map} +0 -0
- /package/dist/{chunk-64UNVW6D.js.map → chunk-BP3ZKIPE.js.map} +0 -0
- /package/dist/{chunk-3V7OTBUZ.js.map → chunk-BQ2SFA27.js.map} +0 -0
- /package/dist/{chunk-Q5SCBQLY.js.map → chunk-BXLC3TBQ.js.map} +0 -0
- /package/dist/{chunk-Z5V5E3JW.js.map → chunk-COKPPU42.js.map} +0 -0
- /package/dist/{chunk-QADTI63U.js.map → chunk-CZVAVX5L.js.map} +0 -0
- /package/dist/{chunk-WDEVKND3.js.map → chunk-D2YJPZMU.js.map} +0 -0
- /package/dist/{chunk-OA6T2ME3.js.map → chunk-DYHOBKPU.js.map} +0 -0
- /package/dist/{chunk-6WAP7BJS.js.map → chunk-E6SPWZRN.js.map} +0 -0
- /package/dist/{chunk-UTIYB6VE.js.map → chunk-E73PAU4U.js.map} +0 -0
- /package/dist/{chunk-XR474YYX.js.map → chunk-EDVAFY76.js.map} +0 -0
- /package/dist/{chunk-MEMVZW64.js.map → chunk-EPNZQWS4.js.map} +0 -0
- /package/dist/{chunk-GZITNT7O.js.map → chunk-FGZIEZEP.js.map} +0 -0
- /package/dist/{chunk-4X2TOMK6.js.map → chunk-FKUVFPQZ.js.map} +0 -0
- /package/dist/{chunk-UN5WU2OX.js.map → chunk-GDWU7WF4.js.map} +0 -0
- /package/dist/{chunk-643YFWNK.js.map → chunk-GPWDP5FF.js.map} +0 -0
- /package/dist/{chunk-XA5ES25V.js.map → chunk-HKQWFICM.js.map} +0 -0
- /package/dist/{chunk-255WTBZ5.js.map → chunk-IWSTMVCA.js.map} +0 -0
- /package/dist/{chunk-TZC6KIK2.js.map → chunk-JG733RMP.js.map} +0 -0
- /package/dist/{chunk-7MLT66DE.js.map → chunk-K33X3PDT.js.map} +0 -0
- /package/dist/{chunk-K3PMMW23.js.map → chunk-K6VEN5VN.js.map} +0 -0
- /package/dist/{chunk-HOWAMSUY.js.map → chunk-KAWVH3AA.js.map} +0 -0
- /package/dist/{chunk-IJVBLU25.js.map → chunk-KJZ4PINO.js.map} +0 -0
- /package/dist/{chunk-REHGWN5M.js.map → chunk-KXDLLS62.js.map} +0 -0
- /package/dist/{chunk-YQ5GQ32T.js.map → chunk-KZP5YTE7.js.map} +0 -0
- /package/dist/{chunk-3V3Z4EML.js.map → chunk-L4FWEI2P.js.map} +0 -0
- /package/dist/{chunk-UKUGSU3Y.js.map → chunk-L4V7FZJI.js.map} +0 -0
- /package/dist/{chunk-W56KN53D.js.map → chunk-L6IDPTNX.js.map} +0 -0
- /package/dist/{chunk-24HHUCWB.js.map → chunk-LDCZPZPD.js.map} +0 -0
- /package/dist/{chunk-KV7LTOWC.js.map → chunk-M225SD4H.js.map} +0 -0
- /package/dist/{chunk-FQMLTCXT.js.map → chunk-MAHY7PK3.js.map} +0 -0
- /package/dist/{chunk-Z2J4MRTF.js.map → chunk-MKNW4FRR.js.map} +0 -0
- /package/dist/{chunk-ILJ3FXIC.js.map → chunk-MM3QESLI.js.map} +0 -0
- /package/dist/{chunk-FYJL432J.js.map → chunk-MQPUS773.js.map} +0 -0
- /package/dist/{chunk-VRSW7PIO.js.map → chunk-NKBSWAP3.js.map} +0 -0
- /package/dist/{chunk-ULC4SCFC.js.map → chunk-NLJ7LL6H.js.map} +0 -0
- /package/dist/{chunk-RAQD5QCM.js.map → chunk-NQ6JP5CC.js.map} +0 -0
- /package/dist/{chunk-IFOHCY4O.js.map → chunk-OF3KG5EV.js.map} +0 -0
- /package/dist/{chunk-W676GY42.js.map → chunk-P772HCHI.js.map} +0 -0
- /package/dist/{chunk-CWRQGA2T.js.map → chunk-POIENBXE.js.map} +0 -0
- /package/dist/{chunk-FW5GTAUY.js.map → chunk-Q4AX4FOK.js.map} +0 -0
- /package/dist/{chunk-XQAFSC66.js.map → chunk-Q4FNCT4C.js.map} +0 -0
- /package/dist/{chunk-PIELY4BJ.js.map → chunk-Q5Z66LKI.js.map} +0 -0
- /package/dist/{chunk-4Y3QLISA.js.map → chunk-Q7AONB4G.js.map} +0 -0
- /package/dist/{chunk-EZBBLLEU.js.map → chunk-QQBYZ3B3.js.map} +0 -0
- /package/dist/{chunk-FT3LJZWZ.js.map → chunk-RWUXMBSO.js.map} +0 -0
- /package/dist/{chunk-KWBMM6MJ.js.map → chunk-RZKKWIGQ.js.map} +0 -0
- /package/dist/{chunk-RVBLOR32.js.map → chunk-S7CXVIEF.js.map} +0 -0
- /package/dist/{chunk-WLLUARTC.js.map → chunk-T2G4LDU5.js.map} +0 -0
- /package/dist/{chunk-OFRQMFWD.js.map → chunk-T5D3DYVN.js.map} +0 -0
- /package/dist/{chunk-O4UXH7BM.js.map → chunk-TP6KZ2YR.js.map} +0 -0
- /package/dist/{chunk-HAWOS4SF.js.map → chunk-TPDN3B5T.js.map} +0 -0
- /package/dist/{chunk-54DOLCJ3.js.map → chunk-TXIIJW6C.js.map} +0 -0
- /package/dist/{chunk-HN36UVOE.js.map → chunk-U4Z6GZDW.js.map} +0 -0
- /package/dist/{chunk-J2IQ6FJ5.js.map → chunk-UKLNLWTP.js.map} +0 -0
- /package/dist/{chunk-MKHNLRVX.js.map → chunk-VPPD2AJN.js.map} +0 -0
- /package/dist/{chunk-TIM4PXWN.js.map → chunk-VWRXSBT2.js.map} +0 -0
- /package/dist/{chunk-KVP5A3YK.js.map → chunk-WDWZKTGA.js.map} +0 -0
- /package/dist/{chunk-FYSS2PYA.js.map → chunk-WH5ZEBKS.js.map} +0 -0
- /package/dist/{chunk-KAGXV3ID.js.map → chunk-WMMXOSZG.js.map} +0 -0
- /package/dist/{chunk-4RSFYS2V.js.map → chunk-X6D5JEUV.js.map} +0 -0
- /package/dist/{chunk-GFYDOH7D.js.map → chunk-XEPNAU3T.js.map} +0 -0
- /package/dist/{chunk-VMACQT3T.js.map → chunk-XH7UDC5Y.js.map} +0 -0
- /package/dist/{chunk-PDWQJFMK.js.map → chunk-XHMHHP7B.js.map} +0 -0
- /package/dist/{chunk-SA3KLZDZ.js.map → chunk-XNAI7QGU.js.map} +0 -0
- /package/dist/{chunk-7TENMQNQ.js.map → chunk-XZMSRHFD.js.map} +0 -0
- /package/dist/{chunk-HBHBRRSW.js.map → chunk-YBAK2WN5.js.map} +0 -0
- /package/dist/{chunk-QLKUWDMQ.js.map → chunk-YDJ5GAGL.js.map} +0 -0
- /package/dist/{chunk-LOLYBFYY.js.map → chunk-ZCNR4DN5.js.map} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ColorValue, SizeValue } from '../../../tokens/vocabulary';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* THE CONTRACT BOTH RENDERERS HONOUR (see `Button.base.ts`).
|
|
5
|
+
*
|
|
6
|
+
* No MUI and no react-native here: this file ships in both declaration
|
|
7
|
+
* outputs, and a native consumer has no `@mui/material` to resolve against.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface InputOTPBaseProps {
|
|
11
|
+
/**
|
|
12
|
+
* The variant of the OTP input
|
|
13
|
+
*/
|
|
14
|
+
variant?: 'numeric' | 'alphanumeric' | 'masked';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The color theme of the OTP input
|
|
18
|
+
*/
|
|
19
|
+
color?: ColorValue;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The size of the OTP input
|
|
23
|
+
*/
|
|
24
|
+
size?: SizeValue;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How the boxes take the row's width (FUT-4585).
|
|
28
|
+
*
|
|
29
|
+
* Left out, each box is a square of the size's field height, and the row is
|
|
30
|
+
* as wide as its boxes — what every caller drew before this option.
|
|
31
|
+
*
|
|
32
|
+
* `'width'`: the row takes its parent's whole width and the boxes share it
|
|
33
|
+
* with a 6px gap. Each keeps the size's field height (never under 40) and
|
|
34
|
+
* its type, and narrows to fit: never wider than it is tall, never under
|
|
35
|
+
* 32. On a narrow phone that draws PORTRAIT boxes instead of a smaller size.
|
|
36
|
+
* The boxes share the row's outer width, so pad the parent, not the row: a
|
|
37
|
+
* `style` (or `sx`) setting the row's padding or gap is not accounted for.
|
|
38
|
+
*/
|
|
39
|
+
fit?: 'width';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Number of OTP digits
|
|
43
|
+
*/
|
|
44
|
+
length?: number;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Current value
|
|
48
|
+
*/
|
|
49
|
+
value?: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Called when value changes
|
|
53
|
+
*/
|
|
54
|
+
onChange?: (value: string) => void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Called when OTP is complete
|
|
58
|
+
*/
|
|
59
|
+
onComplete?: (value: string) => void;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether the input should have glass morphism effect
|
|
63
|
+
*/
|
|
64
|
+
glass?: boolean;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Whether the input should have gradient effects
|
|
68
|
+
*/
|
|
69
|
+
gradient?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether to auto-focus first input
|
|
73
|
+
*/
|
|
74
|
+
autoFocus?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Error state
|
|
78
|
+
*/
|
|
79
|
+
error?: boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Disabled state
|
|
83
|
+
*/
|
|
84
|
+
disabled?: boolean;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Test ID for automated testing
|
|
88
|
+
* Container will have this ID, and individual slots will have {dataTestId}-slot-{index}
|
|
89
|
+
*/
|
|
90
|
+
dataTestId?: string;
|
|
91
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The digits' arithmetic, shared by both renderers' hooks (`InputOTP.hooks.ts`
|
|
3
|
+
* on the web, `InputOTP.hooks.native.ts` on React Native). Pure: no React, no
|
|
4
|
+
* DOM, no react-native.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What each variant accepts. `masked` constrains nothing — it only changes what
|
|
9
|
+
* is drawn — so it has no pattern here.
|
|
10
|
+
*/
|
|
11
|
+
export const PATTERNS: Record<string, RegExp> = {
|
|
12
|
+
numeric: /^\d*$/,
|
|
13
|
+
alphanumeric: /^[a-zA-Z0-9]*$/,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Whether `text` is something this variant will take. */
|
|
17
|
+
export const isAllowed = (variant: string, text: string) => (PATTERNS[variant] ?? /.*/).test(text);
|
|
18
|
+
|
|
19
|
+
/** The digit array is always `length` long, blank-padded on the right. */
|
|
20
|
+
export const padTo = (digits: string[], length: number) =>
|
|
21
|
+
Array.from({ length }, (_, i) => digits[i] ?? '');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* What a change to the numeric FIRST slot is, now that its `maxLength` is the
|
|
25
|
+
* code's length rather than 1 (FUT-4144).
|
|
26
|
+
*
|
|
27
|
+
* That slot carries `one-time-code`, and the phone's SMS fill (or a password
|
|
28
|
+
* manager) writes the WHOLE code into it as one change, not a paste — kept to
|
|
29
|
+
* one character, the code lost every digit but the last. So:
|
|
30
|
+
*
|
|
31
|
+
* - `overtype`: a keystroke into the already-filled slot (its old character
|
|
32
|
+
* plus the new one). Ignored, exactly as `maxLength={1}` refused it before.
|
|
33
|
+
* - `code`: anything else longer than one character, written in one go.
|
|
34
|
+
* Spread across the slots like a paste.
|
|
35
|
+
*
|
|
36
|
+
* Every other slot and variant keeps `maxLength={1}` and never gets here.
|
|
37
|
+
*/
|
|
38
|
+
export function firstSlotFill(variant: string, index: number, inputValue: string, current: string) {
|
|
39
|
+
if (variant !== 'numeric' || index !== 0 || inputValue.length < 2) return null;
|
|
40
|
+
const overtype = inputValue.length === 2 && current !== '' && inputValue.includes(current);
|
|
41
|
+
return overtype ? 'overtype' : 'code';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The code inside a first-slot fill. A phone that INSERTS the SMS code at the
|
|
46
|
+
* caret rather than replacing the slot leaves the digit already there in
|
|
47
|
+
* front: `4` + `482913` arrives as `4482913`, which spread as-is would give
|
|
48
|
+
* `448291`. Longer than the code and led by that digit: the digit is dropped.
|
|
49
|
+
*/
|
|
50
|
+
export const codeOfFill = (inputValue: string, current: string, length: number): string =>
|
|
51
|
+
inputValue.length > length && current !== '' && inputValue.startsWith(current) ? inputValue.slice(1) : inputValue;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { codeOfFill, firstSlotFill, isAllowed, padTo } from './InputOTP.core';
|
|
4
|
+
|
|
5
|
+
/** What a box needs from the element behind it: to take the focus. */
|
|
6
|
+
interface Focusable {
|
|
7
|
+
focus: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface NativeOtpInput {
|
|
11
|
+
variant: string;
|
|
12
|
+
length: number;
|
|
13
|
+
value: string;
|
|
14
|
+
onChange?: (value: string) => void;
|
|
15
|
+
onComplete?: (value: string) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The native boxes' digits — the web hook's rules (`InputOTP.hooks.ts`) over
|
|
20
|
+
* React Native's events: `onChangeText` carries the box's whole new text and
|
|
21
|
+
* `onKeyPress` carries only the key's name, and there is no paste event at
|
|
22
|
+
* all. A pasted or SMS-filled code arrives as a change of the FIRST box, whose
|
|
23
|
+
* `maxLength` is the code's length plus one for exactly that reason, and is
|
|
24
|
+
* spread across the boxes by the same `firstSlotFill` / `codeOfFill` the web
|
|
25
|
+
* uses.
|
|
26
|
+
*/
|
|
27
|
+
export const useNativeOtpDigits = ({ variant, length, value, onChange, onComplete }: NativeOtpInput) => {
|
|
28
|
+
const [digits, setDigits] = useState<string[]>(() => padTo([], length));
|
|
29
|
+
const boxRefs = useRef<(Focusable | null)[]>([]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
setDigits(padTo(value.split('').slice(0, length), length));
|
|
33
|
+
}, [value, length]);
|
|
34
|
+
|
|
35
|
+
const focusBox = useCallback((index: number) => boxRefs.current[index]?.focus(), []);
|
|
36
|
+
|
|
37
|
+
const publish = useCallback(
|
|
38
|
+
(next: string) => {
|
|
39
|
+
onChange?.(next);
|
|
40
|
+
if (next.length === length) onComplete?.(next);
|
|
41
|
+
},
|
|
42
|
+
[length, onChange, onComplete],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/** A whole code laid across the boxes from the first one. */
|
|
46
|
+
const spread = useCallback(
|
|
47
|
+
(text: string) => {
|
|
48
|
+
const code = text.slice(0, length);
|
|
49
|
+
if (!code || !isAllowed(variant, code)) return;
|
|
50
|
+
setDigits(padTo(code.split(''), length));
|
|
51
|
+
publish(code);
|
|
52
|
+
focusBox(Math.min(code.length, length - 1));
|
|
53
|
+
},
|
|
54
|
+
[variant, length, publish, focusBox],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const handleChangeText = useCallback(
|
|
58
|
+
(index: number, text: string) => {
|
|
59
|
+
// The web's paste spreads a code for EVERY variant; React Native has no
|
|
60
|
+
// paste event, so a code arrives as a change of the first box, read by
|
|
61
|
+
// the numeric rule for `alphanumeric` too (`masked` shows a bullet, whose
|
|
62
|
+
// text no longer holds the digit it hides: it keeps one character a box).
|
|
63
|
+
const fill = firstSlotFill(variant === 'alphanumeric' ? 'numeric' : variant, index, text, digits[0] ?? '');
|
|
64
|
+
if (fill) return fill === 'code' ? spread(codeOfFill(text, digits[0] ?? '', length)) : undefined;
|
|
65
|
+
if (!isAllowed(variant, text)) return;
|
|
66
|
+
const next = [...digits];
|
|
67
|
+
next[index] = text.slice(-1);
|
|
68
|
+
setDigits(next);
|
|
69
|
+
publish(next.join(''));
|
|
70
|
+
if (text && index < length - 1) focusBox(index + 1);
|
|
71
|
+
},
|
|
72
|
+
[variant, digits, length, publish, focusBox, spread],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/** Backspace in a box that is already empty steps back to the one before. */
|
|
76
|
+
const handleKey = useCallback(
|
|
77
|
+
(index: number, key: string) => {
|
|
78
|
+
if (key === 'Backspace' && !digits[index] && index > 0) focusBox(index - 1);
|
|
79
|
+
},
|
|
80
|
+
[digits, focusBox],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return { digits, boxRefs, handleChangeText, handleKey };
|
|
84
|
+
};
|
|
@@ -1,51 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* What each variant accepts. `masked` constrains nothing — it only changes what
|
|
6
|
-
* is drawn — so it has no pattern here.
|
|
7
|
-
*/
|
|
8
|
-
const PATTERNS: Record<string, RegExp> = {
|
|
9
|
-
numeric: /^\d*$/,
|
|
10
|
-
alphanumeric: /^[a-zA-Z0-9]*$/,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** Whether `text` is something this variant will take. */
|
|
14
|
-
const isAllowed = (variant: string, text: string) => (PATTERNS[variant] ?? /.*/).test(text);
|
|
15
|
-
|
|
16
|
-
/** The digit array is always `length` long, blank-padded on the right. */
|
|
17
|
-
const padTo = (digits: string[], length: number) =>
|
|
18
|
-
Array.from({ length }, (_, i) => digits[i] ?? '');
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* What a change to the numeric FIRST slot is, now that its `maxLength` is the
|
|
22
|
-
* code's length rather than 1 (FUT-4144).
|
|
23
|
-
*
|
|
24
|
-
* That slot carries `one-time-code`, and the phone's SMS fill (or a password
|
|
25
|
-
* manager) writes the WHOLE code into it as one change, not a paste — kept to
|
|
26
|
-
* one character, the code lost every digit but the last. So:
|
|
27
|
-
*
|
|
28
|
-
* - `overtype`: a keystroke into the already-filled slot (its old character
|
|
29
|
-
* plus the new one). Ignored, exactly as `maxLength={1}` refused it before.
|
|
30
|
-
* - `code`: anything else longer than one character, written in one go.
|
|
31
|
-
* Spread across the slots like a paste.
|
|
32
|
-
*
|
|
33
|
-
* Every other slot and variant keeps `maxLength={1}` and never gets here.
|
|
34
|
-
*/
|
|
35
|
-
function firstSlotFill(variant: string, index: number, inputValue: string, current: string) {
|
|
36
|
-
if (variant !== 'numeric' || index !== 0 || inputValue.length < 2) return null;
|
|
37
|
-
const overtype = inputValue.length === 2 && current !== '' && inputValue.includes(current);
|
|
38
|
-
return overtype ? 'overtype' : 'code';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The code inside a first-slot fill. A phone that INSERTS the SMS code at the
|
|
43
|
-
* caret rather than replacing the slot leaves the digit already there in
|
|
44
|
-
* front: `4` + `482913` arrives as `4482913`, which spread as-is would give
|
|
45
|
-
* `448291`. Longer than the code and led by that digit: the digit is dropped.
|
|
46
|
-
*/
|
|
47
|
-
const codeOfFill = (inputValue: string, current: string, length: number): string =>
|
|
48
|
-
inputValue.length > length && current !== '' && inputValue.startsWith(current) ? inputValue.slice(1) : inputValue;
|
|
3
|
+
import { codeOfFill, firstSlotFill, isAllowed, padTo } from './InputOTP.core';
|
|
49
4
|
|
|
50
5
|
interface OtpInput {
|
|
51
6
|
variant: string;
|
|
@@ -27,6 +27,7 @@ The InputOTP component provides a specialized input field for one-time passwords
|
|
|
27
27
|
| `onComplete` | `(value: string) => void` | - | Callback when all digits are filled |
|
|
28
28
|
| `color` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'neutral'` | `'primary'` | Theme color |
|
|
29
29
|
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Input size |
|
|
30
|
+
| `fit` | `'width'` | - | Share the row's width: each box keeps the size's height (never under 40) and type, and narrows to fit, never wider than tall nor under 32. Left out, each box is a square of the size's field height |
|
|
30
31
|
| `autoFocus` | `boolean` | `false` | Auto-focus first input on mount |
|
|
31
32
|
| `disabled` | `boolean` | `false` | Disable all inputs |
|
|
32
33
|
| `error` | `boolean` | `false` | Show error state |
|
|
@@ -191,6 +192,40 @@ The component supports various visual customizations:
|
|
|
191
192
|
- `lg`: Large (prominent forms)
|
|
192
193
|
- `xl`: Extra large (accessibility focus)
|
|
193
194
|
|
|
195
|
+
### Fitting a narrow row: `fit="width"`
|
|
196
|
+
|
|
197
|
+
A square box is as wide as its field is tall, so six `lg` boxes need 328px. On
|
|
198
|
+
a narrow phone the only way to fit them used to be a smaller size, whose boxes
|
|
199
|
+
drop under the 40px tap minimum and whose type shrinks with them. `fit="width"`
|
|
200
|
+
keeps the size and narrows the boxes instead (FUT-4585):
|
|
201
|
+
|
|
202
|
+
- the row takes its parent's whole width, with a 6px gap between boxes;
|
|
203
|
+
- each box keeps the size's field height, lifted to 40 if it is shorter, and
|
|
204
|
+
the size's own type;
|
|
205
|
+
- each box's width is the row less the gaps, divided by `length`, floored to a
|
|
206
|
+
whole pixel, never wider than the box is tall (a wide row keeps squares) and
|
|
207
|
+
never under 32.
|
|
208
|
+
|
|
209
|
+
At `size="lg"` (48 tall), a sheet with 24px of padding on each side draws:
|
|
210
|
+
|
|
211
|
+
| phone | row | box |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| 320 | 272 | 40 × 48 |
|
|
214
|
+
| 360 | 312 | 47 × 48 |
|
|
215
|
+
| 390 | 342 | 48 × 48 |
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
<InputOTP fit="width" size="lg" length={6} value={code} onChange={setCode} />
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The boxes share the row's **outer** width, and the row spans its parent in a
|
|
222
|
+
column or a row parent alike. Pad the parent, not the row: a `style` (or `sx`)
|
|
223
|
+
that sets the row's own padding or gap is not taken off the share.
|
|
224
|
+
|
|
225
|
+
The arithmetic is `otpFitBox` in `InputOTP.metrics.ts`, shared by both
|
|
226
|
+
renderers: React Native measures the row with `onLayout`, the web lets CSS flex
|
|
227
|
+
share it under the same bounds.
|
|
228
|
+
|
|
194
229
|
### Color Themes
|
|
195
230
|
- `primary`: Main brand color
|
|
196
231
|
- `secondary`: Alternative brand color
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The numbers both renderers read (see `NATIVE.md`, "Numbers").
|
|
3
|
+
*
|
|
4
|
+
* Each box's type size, in design px, per size step; its side is the theme's
|
|
5
|
+
* field height for that step (a square), its weight 600.
|
|
6
|
+
*/
|
|
7
|
+
export const OTP_FONT_PX: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl', number> = {
|
|
8
|
+
xs: 12,
|
|
9
|
+
sm: 14,
|
|
10
|
+
md: 16,
|
|
11
|
+
lg: 18,
|
|
12
|
+
xl: 20,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** The gap between two boxes: MUI's `gap: 1`, one 8px spacing unit. */
|
|
16
|
+
export const OTP_GAP_PX = 8;
|
|
17
|
+
|
|
18
|
+
/** The border: 1px at rest, 2px focused (MUI's focused outline). */
|
|
19
|
+
export const OTP_BORDER = { rest: 1, focused: 2 } as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `fit="width"` (FUT-4585): the gap between two boxes when they share the row's
|
|
23
|
+
* width — the approved prototype's 6px, a little tighter than the default 8px
|
|
24
|
+
* so six portrait boxes stay at least 40 wide on a 320 phone.
|
|
25
|
+
*/
|
|
26
|
+
export const OTP_FIT_GAP_PX = 6;
|
|
27
|
+
|
|
28
|
+
/** `fit="width"`: no box is narrower than this, however narrow the row. */
|
|
29
|
+
export const OTP_FIT_MIN_WIDTH_PX = 32;
|
|
30
|
+
|
|
31
|
+
/** `fit="width"`: no box is shorter than the 40px touch floor, whatever its size. */
|
|
32
|
+
export const OTP_FIT_MIN_HEIGHT_PX = 40;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* One box's size under `fit="width"`, for a row `rowWidth` wide holding
|
|
36
|
+
* `length` boxes whose size step draws a field `side` tall.
|
|
37
|
+
*
|
|
38
|
+
* The box keeps the field's height (never under {@link OTP_FIT_MIN_HEIGHT_PX})
|
|
39
|
+
* and takes an equal share of the row, less the gaps, for its width: floored
|
|
40
|
+
* to a whole pixel so six of them never overflow the row, never wider than it
|
|
41
|
+
* is tall (a wide row keeps squares) and never under
|
|
42
|
+
* {@link OTP_FIT_MIN_WIDTH_PX}. A narrow row therefore draws PORTRAIT boxes
|
|
43
|
+
* rather than dropping to a smaller size and its smaller type.
|
|
44
|
+
*/
|
|
45
|
+
export function otpFitBox(rowWidth: number, side: number, length: number): { width: number; height: number } {
|
|
46
|
+
const height = Math.max(side, OTP_FIT_MIN_HEIGHT_PX);
|
|
47
|
+
const count = Math.max(1, Math.floor(length));
|
|
48
|
+
const share = Math.floor((rowWidth - (count - 1) * OTP_FIT_GAP_PX) / count);
|
|
49
|
+
const width = Math.min(height, Math.max(OTP_FIT_MIN_WIDTH_PX, Number.isFinite(share) ? share : height));
|
|
50
|
+
return { width, height };
|
|
51
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Platform,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TextInput,
|
|
6
|
+
View,
|
|
7
|
+
type LayoutChangeEvent,
|
|
8
|
+
type TextInputProps,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
|
|
13
|
+
import { useNativeOtpDigits } from './InputOTP.hooks.native';
|
|
14
|
+
import {
|
|
15
|
+
OTP_BORDER,
|
|
16
|
+
OTP_FIT_GAP_PX,
|
|
17
|
+
OTP_FIT_MIN_HEIGHT_PX,
|
|
18
|
+
OTP_FIT_MIN_WIDTH_PX,
|
|
19
|
+
OTP_FONT_PX,
|
|
20
|
+
OTP_GAP_PX,
|
|
21
|
+
otpFitBox,
|
|
22
|
+
} from './InputOTP.metrics';
|
|
23
|
+
import type { InputOTPProps } from './InputOTP.types.native';
|
|
24
|
+
import { webAria, webDisabled } from '../../../platform/aria';
|
|
25
|
+
import { resolveFieldEdge } from '../../../tokens/field-edge.core';
|
|
26
|
+
import { fieldHeightPx } from '../../../tokens/field-height.core';
|
|
27
|
+
import type { UiPaletteKey, UiTheme } from '../../../tokens/theme';
|
|
28
|
+
import type { SizeValue } from '../../../tokens/vocabulary';
|
|
29
|
+
import { useUiTheme } from '../../../provider/use-ui-theme.native';
|
|
30
|
+
|
|
31
|
+
const PALETTE_KEYS: readonly UiPaletteKey[] = ['primary', 'secondary', 'success', 'warning', 'info', 'danger', 'neutral'];
|
|
32
|
+
|
|
33
|
+
/** The palette slot a `color` names; anything else is `primary`, as on the web. */
|
|
34
|
+
function accentOf(theme: UiTheme, color: string): string {
|
|
35
|
+
const key = PALETTE_KEYS.find((candidate) => candidate === color) ?? 'primary';
|
|
36
|
+
return theme.palette[key].main;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The keypad and the one-time-code fill, the web's `keypadProps` in React
|
|
41
|
+
* Native's words: digits only for `numeric`, and on the FIRST box the hint that
|
|
42
|
+
* lets the phone offer the code from an SMS — `oneTimeCode` on iOS,
|
|
43
|
+
* `sms-otp` on Android.
|
|
44
|
+
*/
|
|
45
|
+
function keypadProps(variant: string, index: number): Partial<TextInputProps> {
|
|
46
|
+
// A code is not prose: no capital on the first letter, no autocorrect.
|
|
47
|
+
if (variant !== 'numeric') return { autoCapitalize: 'none', autoCorrect: false, spellCheck: false };
|
|
48
|
+
const fill: Partial<TextInputProps> =
|
|
49
|
+
index === 0
|
|
50
|
+
? Platform.select<Partial<TextInputProps>>({
|
|
51
|
+
ios: { textContentType: 'oneTimeCode', autoComplete: 'one-time-code' },
|
|
52
|
+
android: { autoComplete: 'sms-otp' },
|
|
53
|
+
default: { autoComplete: 'one-time-code' },
|
|
54
|
+
})
|
|
55
|
+
: {};
|
|
56
|
+
return { keyboardType: 'number-pad', inputMode: 'numeric', ...fill };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The first box takes a whole code in one change (see the web's `slotMaxLength`), except `masked`'s. */
|
|
60
|
+
const boxMaxLength = (variant: string, index: number, length: number): number =>
|
|
61
|
+
variant !== 'masked' && index === 0 ? length + 1 : 1;
|
|
62
|
+
|
|
63
|
+
interface BoxState {
|
|
64
|
+
focused: boolean;
|
|
65
|
+
error: boolean;
|
|
66
|
+
disabled: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A box's footprint under `fit="width"`: the measured share of the row once
|
|
71
|
+
* the row has been laid out, and until then a flex share bounded by the same
|
|
72
|
+
* limits, so the first frame already fits the row.
|
|
73
|
+
*/
|
|
74
|
+
function fitFootprint(side: number, length: number, rowWidth: number | null): TextStyle {
|
|
75
|
+
if (rowWidth !== null) return otpFitBox(rowWidth, side, length);
|
|
76
|
+
const height = Math.max(side, OTP_FIT_MIN_HEIGHT_PX);
|
|
77
|
+
return { height, flexGrow: 1, flexShrink: 1, flexBasis: 0, minWidth: OTP_FIT_MIN_WIDTH_PX, maxWidth: height };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* `fit="width"`'s state: the row's width once laid out (the row takes its
|
|
82
|
+
* parent's whole width), the boxes' footprint from it, and what the row adds —
|
|
83
|
+
* its `onLayout` and the fit style. Without the option, nothing: the boxes keep
|
|
84
|
+
* their square and the row's props are exactly the default's.
|
|
85
|
+
*
|
|
86
|
+
* The share is of the row's OUTER width, so under `fit` the row's `style`
|
|
87
|
+
* should set no horizontal padding and no gap: pad the parent instead.
|
|
88
|
+
*/
|
|
89
|
+
function useFitWidth(fit: InputOTPProps['fit'], side: number, length: number) {
|
|
90
|
+
const [rowWidth, setRowWidth] = React.useState<number | null>(null);
|
|
91
|
+
if (fit !== 'width') return { footprint: undefined, rowLayout: {}, rowStyles: [] };
|
|
92
|
+
return {
|
|
93
|
+
footprint: fitFootprint(side, length, rowWidth),
|
|
94
|
+
rowLayout: { onLayout: (event: LayoutChangeEvent) => setRowWidth(event.nativeEvent.layout.width) },
|
|
95
|
+
rowStyles: [styles.fitRow],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** One box's paint: the web slot's square, border and type, from the same numbers. */
|
|
100
|
+
function boxStyle(theme: UiTheme, size: SizeValue, accent: string, state: BoxState, footprint?: TextStyle): TextStyle {
|
|
101
|
+
const side = fieldHeightPx(theme.fieldHeight, size);
|
|
102
|
+
const edge = resolveFieldEdge(theme.palette.divider, theme.palette.background.paper);
|
|
103
|
+
// Error and disabled outrank focus, as in MUI's outlined input (and the native `Input`).
|
|
104
|
+
const borderColor = state.error
|
|
105
|
+
? theme.palette.danger.main
|
|
106
|
+
: state.disabled
|
|
107
|
+
? theme.palette.action.disabled
|
|
108
|
+
: state.focused
|
|
109
|
+
? accent
|
|
110
|
+
: edge;
|
|
111
|
+
return {
|
|
112
|
+
...(footprint ?? { width: side, height: side }),
|
|
113
|
+
borderRadius: theme.radius.field,
|
|
114
|
+
borderWidth: state.focused ? OTP_BORDER.focused : OTP_BORDER.rest,
|
|
115
|
+
borderColor,
|
|
116
|
+
backgroundColor: theme.palette.background.paper,
|
|
117
|
+
color: state.disabled ? theme.palette.text.disabled : theme.palette.text.primary,
|
|
118
|
+
fontFamily: theme.typography.fontFamily,
|
|
119
|
+
fontSize: OTP_FONT_PX[size],
|
|
120
|
+
fontWeight: '600',
|
|
121
|
+
textAlign: 'center',
|
|
122
|
+
padding: 0,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The native `InputOTP`: one row of boxes, one character each, that moves
|
|
128
|
+
* forward as it is typed, steps back on Backspace in an empty box, and spreads
|
|
129
|
+
* a code the phone fills (or a paste) across every box from the first.
|
|
130
|
+
*
|
|
131
|
+
* The same props as the web one; the row is a `group` named by `aria-label`,
|
|
132
|
+
* each box is `${dataTestId}-slot-${index}`. Gaps against the web:
|
|
133
|
+
* `NATIVE-NOTES.md`.
|
|
134
|
+
*/
|
|
135
|
+
export const InputOTP = React.forwardRef<View, InputOTPProps>((props, ref) => {
|
|
136
|
+
const {
|
|
137
|
+
variant = 'numeric',
|
|
138
|
+
color = 'primary',
|
|
139
|
+
size = 'md',
|
|
140
|
+
length = 6,
|
|
141
|
+
value = '',
|
|
142
|
+
onChange,
|
|
143
|
+
onComplete,
|
|
144
|
+
autoFocus = false,
|
|
145
|
+
error = false,
|
|
146
|
+
disabled = false,
|
|
147
|
+
dataTestId,
|
|
148
|
+
fit,
|
|
149
|
+
style,
|
|
150
|
+
'aria-label': ariaLabel,
|
|
151
|
+
} = props;
|
|
152
|
+
|
|
153
|
+
const theme = useUiTheme();
|
|
154
|
+
const accent = accentOf(theme, color);
|
|
155
|
+
const [focusedIndex, setFocusedIndex] = React.useState<number | null>(null);
|
|
156
|
+
const { footprint, rowLayout, rowStyles } = useFitWidth(fit, fieldHeightPx(theme.fieldHeight, size), length);
|
|
157
|
+
const { digits, boxRefs, handleChangeText, handleKey } = useNativeOtpDigits({
|
|
158
|
+
variant,
|
|
159
|
+
length,
|
|
160
|
+
value,
|
|
161
|
+
onChange,
|
|
162
|
+
onComplete,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<View
|
|
167
|
+
ref={ref}
|
|
168
|
+
testID={dataTestId}
|
|
169
|
+
role="group"
|
|
170
|
+
aria-label={ariaLabel}
|
|
171
|
+
{...rowLayout}
|
|
172
|
+
style={[styles.row, ...rowStyles, style]}
|
|
173
|
+
>
|
|
174
|
+
{digits.map((digit, index) => (
|
|
175
|
+
<TextInput
|
|
176
|
+
key={index}
|
|
177
|
+
ref={(element) => {
|
|
178
|
+
boxRefs.current[index] = element;
|
|
179
|
+
}}
|
|
180
|
+
testID={dataTestId ? `${dataTestId}-slot-${index}` : undefined}
|
|
181
|
+
value={variant === 'masked' ? (digit ? '•' : '') : digit}
|
|
182
|
+
onChangeText={(text) => handleChangeText(index, text)}
|
|
183
|
+
onKeyPress={(event) => handleKey(index, event.nativeEvent.key)}
|
|
184
|
+
onFocus={() => setFocusedIndex(index)}
|
|
185
|
+
onBlur={() => setFocusedIndex((current) => (current === index ? null : current))}
|
|
186
|
+
autoFocus={autoFocus && index === 0}
|
|
187
|
+
editable={!disabled}
|
|
188
|
+
aria-disabled={disabled}
|
|
189
|
+
{...webAria({ 'aria-invalid': error })}
|
|
190
|
+
maxLength={boxMaxLength(variant, index, length)}
|
|
191
|
+
style={boxStyle(theme, size, accent, { focused: focusedIndex === index && !disabled, error, disabled }, footprint)}
|
|
192
|
+
{...webDisabled(disabled)}
|
|
193
|
+
{...keypadProps(variant, index)}
|
|
194
|
+
/>
|
|
195
|
+
))}
|
|
196
|
+
</View>
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
InputOTP.displayName = 'InputOTP';
|
|
201
|
+
|
|
202
|
+
const styles = StyleSheet.create({
|
|
203
|
+
row: {
|
|
204
|
+
flexDirection: 'row',
|
|
205
|
+
justifyContent: 'center',
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
gap: OTP_GAP_PX,
|
|
208
|
+
},
|
|
209
|
+
/** `fit="width"`: the parent's whole width, in a column or a row parent, shared at the prototype's gap. */
|
|
210
|
+
fitRow: {
|
|
211
|
+
// Both: `stretch` alone spans a column parent but not a row parent, where
|
|
212
|
+
// the row would shrink to its boxes and collapse them to their minimum.
|
|
213
|
+
alignSelf: 'stretch',
|
|
214
|
+
width: '100%',
|
|
215
|
+
gap: OTP_FIT_GAP_PX,
|
|
216
|
+
} satisfies ViewStyle,
|
|
217
|
+
});
|
|
@@ -7,6 +7,8 @@ import { asFieldSize, fieldHeight } from '../../../tokens/field-height';
|
|
|
7
7
|
import { absoluteInk, controlNeutral } from '../../../tokens/ink';
|
|
8
8
|
import { rem } from '../../../tokens/relative';
|
|
9
9
|
|
|
10
|
+
import { OTP_FIT_MIN_HEIGHT_PX, OTP_FIT_MIN_WIDTH_PX, OTP_FONT_PX } from './InputOTP.metrics';
|
|
11
|
+
|
|
10
12
|
interface ColorPalette {
|
|
11
13
|
main: string;
|
|
12
14
|
dark?: string;
|
|
@@ -47,37 +49,54 @@ const getColorFromTheme = (theme: Theme, color: string): ColorPalette => {
|
|
|
47
49
|
};
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
type SizeKey =
|
|
51
|
-
|
|
52
|
-
/** Each slot's type, in design px. Its side is the theme's field height for the size (a square). */
|
|
53
|
-
const SIZES: Record<SizeKey, { fontPx: number }> = {
|
|
54
|
-
xs: { fontPx: 12 },
|
|
55
|
-
sm: { fontPx: 14 },
|
|
56
|
-
md: { fontPx: 16 },
|
|
57
|
-
lg: { fontPx: 18 },
|
|
58
|
-
xl: { fontPx: 20 },
|
|
59
|
-
};
|
|
52
|
+
type SizeKey = keyof typeof OTP_FONT_PX;
|
|
60
53
|
|
|
61
54
|
export interface OtpSlotFlags {
|
|
62
55
|
customColor?: string;
|
|
63
56
|
customSize?: string;
|
|
64
57
|
glass?: boolean;
|
|
65
58
|
gradient?: boolean;
|
|
59
|
+
/** `fit="width"`: share the row's width instead of drawing a square (FUT-4585). */
|
|
60
|
+
fitWidth?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface SlotBox {
|
|
64
|
+
/** The slot's own footprint in the row. */
|
|
65
|
+
outer: CSSObject;
|
|
66
|
+
/** The outlined input inside it. */
|
|
67
|
+
inner: CSSObject;
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
/**
|
|
71
|
+
* A slot's box. By default a square of the field height, inside and out.
|
|
72
|
+
*
|
|
73
|
+
* Under `fit="width"` (FUT-4585), the native `otpFitBox` in CSS: the slot keeps
|
|
74
|
+
* the field's height (never under the 40px touch floor) and takes an equal
|
|
75
|
+
* share of the row, never wider than it is tall, never under 32px; the input
|
|
76
|
+
* fills it.
|
|
77
|
+
*/
|
|
78
|
+
const slotBox = (theme: Theme, side: string, fitWidth: boolean | undefined): SlotBox => {
|
|
79
|
+
if (!fitWidth) return { outer: { width: side, height: side }, inner: { width: side, height: side } };
|
|
80
|
+
const height = `max(${side}, ${rem(theme, OTP_FIT_MIN_HEIGHT_PX)})`;
|
|
81
|
+
return {
|
|
82
|
+
outer: { flex: '1 1 0', minWidth: rem(theme, OTP_FIT_MIN_WIDTH_PX), maxWidth: height, height },
|
|
83
|
+
inner: { width: '100%', height },
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
68
87
|
export const otpSlotSx = (theme: Theme, flags: OtpSlotFlags): CSSObject => {
|
|
69
|
-
const { customColor = 'primary', customSize = 'md', glass, gradient } = flags;
|
|
88
|
+
const { customColor = 'primary', customSize = 'md', glass, gradient, fitWidth } = flags;
|
|
70
89
|
const palette = getColorFromTheme(theme, customColor);
|
|
71
|
-
const
|
|
90
|
+
const fontPx = OTP_FONT_PX[customSize as SizeKey] ?? OTP_FONT_PX.md;
|
|
72
91
|
// A slot is a field: a square of the theme's field height for its size.
|
|
73
92
|
const side = fieldHeight(theme, asFieldSize(customSize));
|
|
74
93
|
|
|
94
|
+
const box = slotBox(theme, side, fitWidth);
|
|
95
|
+
|
|
75
96
|
return {
|
|
76
|
-
|
|
77
|
-
height: side,
|
|
97
|
+
...box.outer,
|
|
78
98
|
'& .MuiOutlinedInput-root': {
|
|
79
|
-
|
|
80
|
-
height: side,
|
|
99
|
+
...box.inner,
|
|
81
100
|
fontSize: rem(theme, fontPx),
|
|
82
101
|
fontWeight: 600,
|
|
83
102
|
textAlign: 'center',
|