@multiplatform.one/components 7.11.0 → 7.15.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/README.md +2 -2
- package/dist/cjs/Accordion/index.cjs +2 -6
- package/dist/cjs/Accordion/index.native.js +2 -6
- package/dist/cjs/Accordion/index.native.js.map +1 -1
- package/dist/cjs/AnimateHeight/index.cjs +1 -3
- package/dist/cjs/AnimateHeight/index.native.js +1 -3
- package/dist/cjs/AnimateHeight/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.cjs +8 -1
- package/dist/cjs/Chip/index.native.js +8 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/SheetModal/index.native.js +70 -22
- package/dist/cjs/SheetModal/index.native.js.map +1 -1
- package/dist/cjs/Tabs/index.cjs +2 -6
- package/dist/cjs/Tabs/index.native.js +2 -6
- package/dist/cjs/Tabs/index.native.js.map +1 -1
- package/dist/cjs/Text.cjs +2 -0
- package/dist/cjs/Text.native.js +2 -0
- package/dist/cjs/Text.native.js.map +1 -1
- package/dist/cjs/TreeSelect/index.cjs +55 -52
- package/dist/cjs/TreeSelect/index.native.js +55 -52
- package/dist/cjs/TreeSelect/index.native.js.map +1 -1
- package/dist/cjs/Video/PlayerChrome.cjs +3 -9
- package/dist/cjs/Video/PlayerChrome.native.js +3 -9
- package/dist/cjs/Video/PlayerChrome.native.js.map +1 -1
- package/dist/cjs/code/Code/index.cjs +6 -0
- package/dist/cjs/code/Code/index.native.js +7 -4
- package/dist/cjs/code/Code/index.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.cjs +4 -5
- package/dist/cjs/code/CodeBlock/index.native.js +4 -5
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/icons/themed.cjs +2 -1
- package/dist/cjs/icons/themed.native.js +2 -1
- package/dist/cjs/icons/themed.native.js.map +1 -1
- package/dist/cjs/knobImmuneExports.cjs +3 -3
- package/dist/cjs/knobImmuneExports.native.js +3 -3
- package/dist/cjs/layouts/PaneScaffold.cjs +70 -42
- package/dist/cjs/layouts/PaneScaffold.native.js +75 -48
- package/dist/cjs/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/cjs/layouts/page.cjs +2 -1
- package/dist/cjs/layouts/page.native.js +2 -1
- package/dist/cjs/layouts/page.native.js.map +1 -1
- package/dist/cjs/layouts/sidebar.cjs +1 -3
- package/dist/cjs/layouts/sidebar.native.js +1 -3
- package/dist/cjs/layouts/sidebar.native.js.map +1 -1
- package/dist/cjs/surfaces.cjs +0 -32
- package/dist/cjs/surfaces.native.js +0 -32
- package/dist/cjs/surfaces.native.js.map +1 -1
- package/dist/cjs/views/List.cjs +1 -3
- package/dist/cjs/views/List.native.js +1 -3
- package/dist/cjs/views/List.native.js.map +1 -1
- package/dist/cjs/views/Matrix.cjs +568 -0
- package/dist/cjs/views/Matrix.native.js +612 -0
- package/dist/cjs/views/Matrix.native.js.map +1 -0
- package/dist/cjs/views/ganttMath.cjs +2 -0
- package/dist/cjs/views/ganttMath.native.js +2 -0
- package/dist/cjs/views/ganttMath.native.js.map +1 -1
- package/dist/cjs/views/index.cjs +16 -0
- package/dist/cjs/views/index.native.js +16 -0
- package/dist/cjs/views/index.native.js.map +1 -1
- package/dist/cjs/views/matrixMath.cjs +157 -0
- package/dist/cjs/views/matrixMath.native.js +229 -0
- package/dist/cjs/views/matrixMath.native.js.map +1 -0
- package/dist/esm/Accordion/index.mjs +3 -7
- package/dist/esm/Accordion/index.mjs.map +1 -1
- package/dist/esm/Accordion/index.native.js +3 -7
- package/dist/esm/Accordion/index.native.js.map +1 -1
- package/dist/esm/AnimateHeight/index.mjs +2 -4
- package/dist/esm/AnimateHeight/index.mjs.map +1 -1
- package/dist/esm/AnimateHeight/index.native.js +2 -4
- package/dist/esm/AnimateHeight/index.native.js.map +1 -1
- package/dist/esm/Avatar/getInitials.native.js.map +1 -1
- package/dist/esm/Avatar/index.native.js.map +1 -1
- package/dist/esm/Badge/index.native.js.map +1 -1
- package/dist/esm/Button.native.js.map +1 -1
- package/dist/esm/Carousel/index.native.js.map +1 -1
- package/dist/esm/Chip/index.mjs +9 -2
- package/dist/esm/Chip/index.mjs.map +1 -1
- package/dist/esm/Chip/index.native.js +9 -2
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/Collapsible.native.js.map +1 -1
- package/dist/esm/ContextMenu/index.native.js.map +1 -1
- package/dist/esm/CopyField/index.native.js.map +1 -1
- package/dist/esm/DotIndicator/index.native.js.map +1 -1
- package/dist/esm/DropdownMenu/index.native.js.map +1 -1
- package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
- package/dist/esm/Heading.native.js.map +1 -1
- package/dist/esm/Meter/index.native.js.map +1 -1
- package/dist/esm/Meter/meter.native.js.map +1 -1
- package/dist/esm/Pagination.native.js.map +1 -1
- package/dist/esm/ProgressSteps/index.native.js.map +1 -1
- package/dist/esm/Quote/index.native.js.map +1 -1
- package/dist/esm/Separator/index.native.js.map +1 -1
- package/dist/esm/SheetModal/detents.native.js.map +1 -1
- package/dist/esm/SheetModal/index.native.js +71 -23
- package/dist/esm/SheetModal/index.native.js.map +1 -1
- package/dist/esm/Skeleton/index.native.js.map +1 -1
- package/dist/esm/SkipLink/index.native.js.map +1 -1
- package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
- package/dist/esm/SortableList/index.native.js.map +1 -1
- package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
- package/dist/esm/Tabs/index.mjs +3 -7
- package/dist/esm/Tabs/index.mjs.map +1 -1
- package/dist/esm/Tabs/index.native.js +3 -7
- package/dist/esm/Tabs/index.native.js.map +1 -1
- package/dist/esm/Text.mjs +3 -1
- package/dist/esm/Text.mjs.map +1 -1
- package/dist/esm/Text.native.js +3 -1
- package/dist/esm/Text.native.js.map +1 -1
- package/dist/esm/Toast/index.native.js.map +1 -1
- package/dist/esm/Toast/store.native.js.map +1 -1
- package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
- package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
- package/dist/esm/TreeSelect/index.mjs +56 -53
- package/dist/esm/TreeSelect/index.mjs.map +1 -1
- package/dist/esm/TreeSelect/index.native.js +56 -53
- package/dist/esm/TreeSelect/index.native.js.map +1 -1
- package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
- package/dist/esm/Video/PlayerChrome.mjs +4 -10
- package/dist/esm/Video/PlayerChrome.mjs.map +1 -1
- package/dist/esm/Video/PlayerChrome.native.js +4 -10
- package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
- package/dist/esm/Video/VideoFrame.native.js.map +1 -1
- package/dist/esm/Video/types.native.js.map +1 -1
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/Wizard/index.native.js.map +1 -1
- package/dist/esm/actions/ActionBar.native.js.map +1 -1
- package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
- package/dist/esm/actions/devWarn.native.js.map +1 -1
- package/dist/esm/actions/index.native.js.map +1 -1
- package/dist/esm/actions/severity.native.js.map +1 -1
- package/dist/esm/charts/Axis.native.js.map +1 -1
- package/dist/esm/charts/BarChart.native.js.map +1 -1
- package/dist/esm/charts/ChartDataView.native.js.map +1 -1
- package/dist/esm/charts/ChartSurface.native.js.map +1 -1
- package/dist/esm/charts/LineChart.native.js.map +1 -1
- package/dist/esm/charts/PieChart.native.js.map +1 -1
- package/dist/esm/charts/Sparkline.native.js.map +1 -1
- package/dist/esm/charts/accessibility.native.js.map +1 -1
- package/dist/esm/charts/composeChart.native.js.map +1 -1
- package/dist/esm/charts/index.native.js.map +1 -1
- package/dist/esm/charts/math.native.js.map +1 -1
- package/dist/esm/charts/series.native.js.map +1 -1
- package/dist/esm/charts/svg.native.js.map +1 -1
- package/dist/esm/charts/types.native.js.map +1 -1
- package/dist/esm/charts/useChartTheme.native.js.map +1 -1
- package/dist/esm/code/Code/index.mjs +6 -0
- package/dist/esm/code/Code/index.mjs.map +1 -1
- package/dist/esm/code/Code/index.native.js +10 -7
- package/dist/esm/code/Code/index.native.js.map +1 -1
- package/dist/esm/code/CodeBlock/index.mjs +5 -6
- package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
- package/dist/esm/code/CodeBlock/index.native.js +5 -6
- package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
- package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/esm/code/Highlighter/index.native.js.map +1 -1
- package/dist/esm/code/Pre/index.native.js.map +1 -1
- package/dist/esm/code/index.native.js.map +1 -1
- package/dist/esm/componentColors.native.js.map +1 -1
- package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
- package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
- package/dist/esm/feedback/copy.native.js.map +1 -1
- package/dist/esm/feedback/index.native.js.map +1 -1
- package/dist/esm/feedback/notify.native.js.map +1 -1
- package/dist/esm/feedback/route.native.js.map +1 -1
- package/dist/esm/feedback/store.native.js.map +1 -1
- package/dist/esm/hooks/index.native.js.map +1 -1
- package/dist/esm/hooks/useClipboard.native.js.map +1 -1
- package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
- package/dist/esm/icons/CheckRegular.native.js.map +1 -1
- package/dist/esm/icons/MinusRegular.native.js.map +1 -1
- package/dist/esm/icons/index.native.js.map +1 -1
- package/dist/esm/icons/lucide.native.js.map +1 -1
- package/dist/esm/icons/themed.mjs +3 -2
- package/dist/esm/icons/themed.mjs.map +1 -1
- package/dist/esm/icons/themed.native.js +3 -2
- package/dist/esm/icons/themed.native.js.map +1 -1
- package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
- package/dist/esm/images/Image/shared.native.js.map +1 -1
- package/dist/esm/images/Image/types.native.js.map +1 -1
- package/dist/esm/images/index.native.js.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/knobImmuneExports.mjs +3 -3
- package/dist/esm/knobImmuneExports.native.js +3 -3
- package/dist/esm/knobImmuneExports.native.js.map +1 -1
- package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
- package/dist/esm/layouts/Debug/index.native.js.map +1 -1
- package/dist/esm/layouts/EmptyState.native.js.map +1 -1
- package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
- package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
- package/dist/esm/layouts/PaneScaffold.mjs +70 -42
- package/dist/esm/layouts/PaneScaffold.mjs.map +1 -1
- package/dist/esm/layouts/PaneScaffold.native.js +75 -48
- package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
- package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/esm/layouts/attachments.native.js.map +1 -1
- package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/esm/layouts/index.native.js.map +1 -1
- package/dist/esm/layouts/page.mjs +3 -2
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +3 -2
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
- package/dist/esm/layouts/sidebar.mjs +2 -4
- package/dist/esm/layouts/sidebar.mjs.map +1 -1
- package/dist/esm/layouts/sidebar.native.js +2 -4
- package/dist/esm/layouts/sidebar.native.js.map +1 -1
- package/dist/esm/layouts/tags.native.js.map +1 -1
- package/dist/esm/layouts/timeline.native.js.map +1 -1
- package/dist/esm/layouts/toolbar.native.js.map +1 -1
- package/dist/esm/shared/actionScan.native.js.map +1 -1
- package/dist/esm/shared/i18n.native.js.map +1 -1
- package/dist/esm/shared/t.native.js.map +1 -1
- package/dist/esm/shared/textRidesText.native.js.map +1 -1
- package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/esm/surfaces.mjs +1 -33
- package/dist/esm/surfaces.mjs.map +1 -1
- package/dist/esm/surfaces.native.js +1 -33
- package/dist/esm/surfaces.native.js.map +1 -1
- package/dist/esm/tamagui.native.js.map +1 -1
- package/dist/esm/testSetup.native.js.map +1 -1
- package/dist/esm/utils/setTinted.native.js.map +1 -1
- package/dist/esm/utils/unwrapText.native.js.map +1 -1
- package/dist/esm/views/Calendar.native.js.map +1 -1
- package/dist/esm/views/Dashboard.native.js.map +1 -1
- package/dist/esm/views/Gantt.native.js.map +1 -1
- package/dist/esm/views/ImageGrid.native.js.map +1 -1
- package/dist/esm/views/List.mjs +2 -4
- package/dist/esm/views/List.mjs.map +1 -1
- package/dist/esm/views/List.native.js +2 -4
- package/dist/esm/views/List.native.js.map +1 -1
- package/dist/esm/views/Matrix.mjs +543 -0
- package/dist/esm/views/Matrix.mjs.map +1 -0
- package/dist/esm/views/Matrix.native.js +573 -0
- package/dist/esm/views/Matrix.native.js.map +1 -0
- package/dist/esm/views/ReportBuilder.native.js.map +1 -1
- package/dist/esm/views/TreeView.native.js.map +1 -1
- package/dist/esm/views/calendarMath.native.js.map +1 -1
- package/dist/esm/views/ganttMath.mjs +1 -1
- package/dist/esm/views/ganttMath.mjs.map +1 -1
- package/dist/esm/views/ganttMath.native.js +1 -1
- package/dist/esm/views/ganttMath.native.js.map +1 -1
- package/dist/esm/views/index.mjs +4 -2
- package/dist/esm/views/index.mjs.map +1 -1
- package/dist/esm/views/index.native.js +4 -2
- package/dist/esm/views/index.native.js.map +1 -1
- package/dist/esm/views/kanbanDnd.native.js.map +1 -1
- package/dist/esm/views/matrixMath.mjs +122 -0
- package/dist/esm/views/matrixMath.mjs.map +1 -0
- package/dist/esm/views/matrixMath.native.js +191 -0
- package/dist/esm/views/matrixMath.native.js.map +1 -0
- package/dist/esm/views/treeViewDnd.native.js.map +1 -1
- package/dist/esm/views/treeViewLayout.native.js.map +1 -1
- package/dist/esm/views/treeViewMotion.native.js.map +1 -1
- package/dist/esm/views/treeViewReveal.native.js.map +1 -1
- package/dist/jsx/Accordion/index.mjs +3 -7
- package/dist/jsx/Accordion/index.mjs.map +1 -1
- package/dist/jsx/Accordion/index.native.js +2 -6
- package/dist/jsx/Accordion/index.native.js.map +1 -1
- package/dist/jsx/AnimateHeight/index.mjs +2 -4
- package/dist/jsx/AnimateHeight/index.mjs.map +1 -1
- package/dist/jsx/AnimateHeight/index.native.js +1 -3
- package/dist/jsx/AnimateHeight/index.native.js.map +1 -1
- package/dist/jsx/Chip/index.mjs +9 -2
- package/dist/jsx/Chip/index.mjs.map +1 -1
- package/dist/jsx/Chip/index.native.js +8 -1
- package/dist/jsx/Chip/index.native.js.map +1 -1
- package/dist/jsx/SheetModal/index.native.js +70 -22
- package/dist/jsx/SheetModal/index.native.js.map +1 -1
- package/dist/jsx/Tabs/index.mjs +3 -7
- package/dist/jsx/Tabs/index.mjs.map +1 -1
- package/dist/jsx/Tabs/index.native.js +2 -6
- package/dist/jsx/Tabs/index.native.js.map +1 -1
- package/dist/jsx/Text.mjs +3 -1
- package/dist/jsx/Text.mjs.map +1 -1
- package/dist/jsx/Text.native.js +2 -0
- package/dist/jsx/Text.native.js.map +1 -1
- package/dist/jsx/TreeSelect/index.mjs +56 -53
- package/dist/jsx/TreeSelect/index.mjs.map +1 -1
- package/dist/jsx/TreeSelect/index.native.js +55 -52
- package/dist/jsx/TreeSelect/index.native.js.map +1 -1
- package/dist/jsx/Video/PlayerChrome.mjs +4 -10
- package/dist/jsx/Video/PlayerChrome.mjs.map +1 -1
- package/dist/jsx/Video/PlayerChrome.native.js +3 -9
- package/dist/jsx/Video/PlayerChrome.native.js.map +1 -1
- package/dist/jsx/code/Code/index.mjs +6 -0
- package/dist/jsx/code/Code/index.mjs.map +1 -1
- package/dist/jsx/code/Code/index.native.js +7 -4
- package/dist/jsx/code/Code/index.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.mjs +5 -6
- package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +4 -5
- package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
- package/dist/jsx/icons/themed.mjs +3 -2
- package/dist/jsx/icons/themed.mjs.map +1 -1
- package/dist/jsx/icons/themed.native.js +2 -1
- package/dist/jsx/icons/themed.native.js.map +1 -1
- package/dist/jsx/knobImmuneExports.mjs +3 -3
- package/dist/jsx/knobImmuneExports.native.js +3 -3
- package/dist/jsx/layouts/PaneScaffold.mjs +70 -42
- package/dist/jsx/layouts/PaneScaffold.mjs.map +1 -1
- package/dist/jsx/layouts/PaneScaffold.native.js +75 -48
- package/dist/jsx/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/jsx/layouts/page.mjs +3 -2
- package/dist/jsx/layouts/page.mjs.map +1 -1
- package/dist/jsx/layouts/page.native.js +2 -1
- package/dist/jsx/layouts/page.native.js.map +1 -1
- package/dist/jsx/layouts/sidebar.mjs +2 -4
- package/dist/jsx/layouts/sidebar.mjs.map +1 -1
- package/dist/jsx/layouts/sidebar.native.js +1 -3
- package/dist/jsx/layouts/sidebar.native.js.map +1 -1
- package/dist/jsx/surfaces.mjs +1 -33
- package/dist/jsx/surfaces.mjs.map +1 -1
- package/dist/jsx/surfaces.native.js +0 -32
- package/dist/jsx/surfaces.native.js.map +1 -1
- package/dist/jsx/views/List.mjs +2 -4
- package/dist/jsx/views/List.mjs.map +1 -1
- package/dist/jsx/views/List.native.js +1 -3
- package/dist/jsx/views/List.native.js.map +1 -1
- package/dist/jsx/views/Matrix.mjs +543 -0
- package/dist/jsx/views/Matrix.mjs.map +1 -0
- package/dist/jsx/views/Matrix.native.js +612 -0
- package/dist/jsx/views/Matrix.native.js.map +1 -0
- package/dist/jsx/views/ganttMath.mjs +1 -1
- package/dist/jsx/views/ganttMath.mjs.map +1 -1
- package/dist/jsx/views/ganttMath.native.js +2 -0
- package/dist/jsx/views/ganttMath.native.js.map +1 -1
- package/dist/jsx/views/index.mjs +4 -2
- package/dist/jsx/views/index.mjs.map +1 -1
- package/dist/jsx/views/index.native.js +16 -0
- package/dist/jsx/views/index.native.js.map +1 -1
- package/dist/jsx/views/matrixMath.mjs +122 -0
- package/dist/jsx/views/matrixMath.mjs.map +1 -0
- package/dist/jsx/views/matrixMath.native.js +229 -0
- package/dist/jsx/views/matrixMath.native.js.map +1 -0
- package/package.json +10 -10
- package/src/Accordion/Accordion.spec.tsx +20 -0
- package/src/Accordion/index.tsx +7 -3
- package/src/AnimateHeight/index.tsx +143 -142
- package/src/Chip/Chip.borderLadder.spec.tsx +54 -0
- package/src/Chip/Chip.pressSlop.spec.tsx +57 -0
- package/src/Chip/index.tsx +15 -1
- package/src/Heading.native.spec.tsx +1 -1
- package/src/Separator/index.tsx +3 -3
- package/src/SheetModal/SheetModal.detentLayout.spec.tsx +173 -0
- package/src/SheetModal/SheetModal.touch.spec.tsx +63 -0
- package/src/SheetModal/index.native.tsx +95 -17
- package/src/Tabs/Tabs.hydration.spec.tsx +123 -0
- package/src/Tabs/index.tsx +3 -2
- package/src/Text.spec.tsx +32 -2
- package/src/Text.tsx +7 -6
- package/src/Toast/Toast.spec.tsx +50 -0
- package/src/Tooltip/Tooltip.spec.tsx +55 -2
- package/src/TreeSelect/TreeSelect.spec.tsx +15 -15
- package/src/TreeSelect/index.tsx +53 -50
- package/src/Video/PlayerChrome.tsx +4 -3
- package/src/Wheel/Wheel.spec.tsx +4 -4
- package/src/adjacency.l1.spec.tsx +175 -0
- package/src/code/Code/code.spec.tsx +2 -19
- package/src/code/Code/index.tsx +0 -6
- package/src/code/CodeBlock/index.tsx +5 -3
- package/src/code/highlightedCode.spec.tsx +2 -2
- package/src/code/hostProps.spec.tsx +55 -0
- package/src/icons/themed.hydration.spec.tsx +60 -0
- package/src/icons/themed.tsx +3 -6
- package/src/knobImmuneExports.ts +3 -3
- package/src/layouts/PaneScaffold.spec.tsx +148 -2
- package/src/layouts/PaneScaffold.stories.tsx +33 -1
- package/src/layouts/PaneScaffold.tsx +101 -48
- package/src/layouts/page.designLaw.spec.tsx +20 -0
- package/src/layouts/page.tsx +2 -0
- package/src/layouts/sidebar.tsx +2 -3
- package/src/overlayElevation.spec.tsx +39 -51
- package/src/surfaces.spec.tsx +13 -10
- package/src/surfaces.tsx +4 -33
- package/src/tamagui.spec.ts +4 -3
- package/src/textAccentSecondary.spec.tsx +100 -2
- package/src/views/List.tsx +2 -1
- package/src/views/Matrix.spec.tsx +339 -0
- package/src/views/Matrix.stories.spec.tsx +68 -0
- package/src/views/Matrix.stories.tsx +153 -0
- package/src/views/Matrix.tsx +738 -0
- package/src/views/ganttMath.ts +4 -2
- package/src/views/index.ts +30 -0
- package/src/views/matrixMath.spec.ts +237 -0
- package/src/views/matrixMath.ts +251 -0
- package/src/views/views.spec.tsx +3 -1
- package/types/Accordion/index.d.ts.map +1 -1
- package/types/AnimateHeight/index.d.ts.map +1 -1
- package/types/Chip/index.d.ts.map +1 -1
- package/types/Separator/index.d.ts +3 -3
- package/types/SheetModal/index.native.d.ts.map +1 -1
- package/types/Tabs/index.d.ts.map +1 -1
- package/types/Text.d.ts.map +1 -1
- package/types/TreeSelect/index.d.ts.map +1 -1
- package/types/Video/PlayerChrome.d.ts.map +1 -1
- package/types/code/Code/index.d.ts.map +1 -1
- package/types/code/CodeBlock/index.d.ts.map +1 -1
- package/types/icons/themed.d.ts.map +1 -1
- package/types/layouts/PaneScaffold.d.ts +14 -1
- package/types/layouts/PaneScaffold.d.ts.map +1 -1
- package/types/layouts/page.d.ts.map +1 -1
- package/types/layouts/sidebar.d.ts.map +1 -1
- package/types/surfaces.d.ts.map +1 -1
- package/types/views/List.d.ts.map +1 -1
- package/types/views/Matrix.d.ts +102 -0
- package/types/views/Matrix.d.ts.map +1 -0
- package/types/views/ganttMath.d.ts +4 -0
- package/types/views/ganttMath.d.ts.map +1 -1
- package/types/views/index.d.ts +3 -1
- package/types/views/index.d.ts.map +1 -1
- package/types/views/matrixMath.d.ts +114 -0
- package/types/views/matrixMath.d.ts.map +1 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-68 / MPO-299: what the native sheet lays out at a settled detent.
|
|
3
|
+
*
|
|
4
|
+
* The iOS simulator run on 965825866 dragged a sheet to a lower detent: it
|
|
5
|
+
* kept its full height and slid it below the screen, or under the keyboard,
|
|
6
|
+
* so the tail of a list inside it was unreachable.
|
|
7
|
+
*/
|
|
8
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
|
+
import { act, cleanup } from "@testing-library/react";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import { Animated, PanResponder, type PanResponderCallbacks } from "react-native";
|
|
12
|
+
import { Text } from "tamagui";
|
|
13
|
+
import { resolveSheetDetents } from "./detents";
|
|
14
|
+
import type { SheetModalProps } from "./index";
|
|
15
|
+
import { SheetModal } from "./index.native";
|
|
16
|
+
|
|
17
|
+
const keyboard = vi.hoisted(() => ({ inset: 0 }));
|
|
18
|
+
|
|
19
|
+
vi.mock("react-native-web", async (importOriginal) => ({
|
|
20
|
+
...(await importOriginal<typeof import("react-native")>()),
|
|
21
|
+
ActionSheetIOS: undefined,
|
|
22
|
+
Modal: ({ children }: { children: import("react").ReactNode }) => children,
|
|
23
|
+
useWindowDimensions: () => ({ width: 390, height: 844, scale: 3, fontScale: 1 }),
|
|
24
|
+
}));
|
|
25
|
+
vi.mock("../shared/useKeyboardInset", () => ({ useKeyboardInset: () => keyboard.inset }));
|
|
26
|
+
|
|
27
|
+
let pan: PanResponderCallbacks;
|
|
28
|
+
let springs: Array<{ toValue: number; done?: (result: { finished: boolean }) => void }>;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
keyboard.inset = 0;
|
|
32
|
+
springs = [];
|
|
33
|
+
vi.spyOn(PanResponder, "create").mockImplementation((config) => {
|
|
34
|
+
pan = config;
|
|
35
|
+
return { panHandlers: {} } as never;
|
|
36
|
+
});
|
|
37
|
+
vi.spyOn(Animated, "spring").mockImplementation((_value, config) => ({
|
|
38
|
+
start: (done) => {
|
|
39
|
+
springs.push({ toValue: config.toValue as number, done });
|
|
40
|
+
},
|
|
41
|
+
stop: vi.fn(),
|
|
42
|
+
reset: vi.fn(),
|
|
43
|
+
}));
|
|
44
|
+
vi.spyOn(Animated, "timing").mockImplementation(() => ({
|
|
45
|
+
start: (done) => done?.({ finished: true }),
|
|
46
|
+
stop: vi.fn(),
|
|
47
|
+
reset: vi.fn(),
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
cleanup();
|
|
52
|
+
vi.restoreAllMocks();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function px(value: string) {
|
|
56
|
+
return Number.parseFloat(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function open(props: Partial<SheetModalProps> = {}) {
|
|
60
|
+
const view = renderWithProviders(
|
|
61
|
+
<SheetModal open onOpenChange={() => {}} scrollable {...props}>
|
|
62
|
+
<Text>row</Text>
|
|
63
|
+
</SheetModal>,
|
|
64
|
+
);
|
|
65
|
+
const frame = view.getByTestId("sheet-modal-frame");
|
|
66
|
+
const full = px(frame.style.maxHeight);
|
|
67
|
+
return { view, frame, full };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** react-native-web keeps the onLayout handler on the node; call it the way a device layout would. */
|
|
71
|
+
function layout(element: HTMLElement, height: number) {
|
|
72
|
+
const handler = (element as unknown as Record<string, (event: unknown) => void>)
|
|
73
|
+
.__reactLayoutHandler;
|
|
74
|
+
act(() => handler({ nativeEvent: { layout: { x: 0, y: 0, width: 390, height } } }));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function drag(dy: number) {
|
|
78
|
+
act(() => {
|
|
79
|
+
pan.onPanResponderGrant?.({} as never, {} as never);
|
|
80
|
+
pan.onPanResponderMove?.({} as never, { dy } as never);
|
|
81
|
+
pan.onPanResponderRelease?.({} as never, { dy, vy: 0 } as never);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function settle() {
|
|
86
|
+
const last = springs[springs.length - 1];
|
|
87
|
+
act(() => last.done?.({ finished: true }));
|
|
88
|
+
return last;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe("a native sheet at rest on a lower detent", () => {
|
|
92
|
+
it("is laid out at the detent height, not slid down at full height", () => {
|
|
93
|
+
const { frame, full } = open();
|
|
94
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
95
|
+
expect(lower).toBeLessThan(full);
|
|
96
|
+
drag(full - lower);
|
|
97
|
+
expect(settle().toValue).toBe(full - lower);
|
|
98
|
+
expect(px(frame.style.maxHeight)).toBe(lower);
|
|
99
|
+
expect(frame.style.transform).toContain("translateY(0px)");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("gives a filled body the detent as its definite height", () => {
|
|
103
|
+
const { frame, full } = open({ fill: true });
|
|
104
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
105
|
+
drag(full - lower);
|
|
106
|
+
settle();
|
|
107
|
+
expect(px(frame.style.height)).toBe(lower);
|
|
108
|
+
expect(px(frame.style.maxHeight)).toBe(lower);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("ends above the keyboard when the keyboard is up", () => {
|
|
112
|
+
keyboard.inset = 300;
|
|
113
|
+
const { frame, full } = open();
|
|
114
|
+
expect(frame.parentElement?.style.paddingBottom).toBe("300px");
|
|
115
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
116
|
+
drag(full - lower);
|
|
117
|
+
settle();
|
|
118
|
+
expect(px(frame.style.maxHeight)).toBe(lower);
|
|
119
|
+
expect(frame.style.transform).toContain("translateY(0px)");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("grows back to full height under a still top edge when a drag picks it up", () => {
|
|
123
|
+
const { frame, full } = open();
|
|
124
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
125
|
+
drag(full - lower);
|
|
126
|
+
settle();
|
|
127
|
+
act(() => pan.onPanResponderGrant?.({} as never, {} as never));
|
|
128
|
+
expect(px(frame.style.maxHeight)).toBe(full);
|
|
129
|
+
expect(frame.style.transform).toContain(`translateY(${full - lower}px)`);
|
|
130
|
+
act(() => pan.onPanResponderRelease?.({} as never, { dy: -(full - lower), vy: 0 } as never));
|
|
131
|
+
expect(settle().toValue).toBe(0);
|
|
132
|
+
expect(px(frame.style.maxHeight)).toBe(full);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("keeps its top edge when the keyboard moved the max height under it", () => {
|
|
136
|
+
const { view, frame, full } = open();
|
|
137
|
+
layout(frame, full);
|
|
138
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
139
|
+
drag(full - lower);
|
|
140
|
+
settle();
|
|
141
|
+
keyboard.inset = 300;
|
|
142
|
+
view.rerender(
|
|
143
|
+
<SheetModal open onOpenChange={() => {}} scrollable>
|
|
144
|
+
<Text>row</Text>
|
|
145
|
+
</SheetModal>,
|
|
146
|
+
);
|
|
147
|
+
const lifted = 844 - 300 - 48;
|
|
148
|
+
expect(px(frame.style.maxHeight)).toBe(Math.min(lifted, lower));
|
|
149
|
+
act(() => pan.onPanResponderGrant?.({} as never, {} as never));
|
|
150
|
+
expect(px(frame.style.maxHeight)).toBe(lifted);
|
|
151
|
+
expect(frame.style.transform).toContain(`translateY(${lifted - lower}px)`);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("keeps the drag when a scroll inside the sheet asks for the touch", () => {
|
|
155
|
+
open();
|
|
156
|
+
expect(pan.onPanResponderTerminationRequest?.({} as never, {} as never)).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("forgets the rest height when the sheet closes and opens again", () => {
|
|
160
|
+
const { view, frame, full } = open();
|
|
161
|
+
const [, lower] = resolveSheetDetents({ travel: full, scrollable: true });
|
|
162
|
+
drag(full - lower);
|
|
163
|
+
settle();
|
|
164
|
+
const sheet = (next: boolean) => (
|
|
165
|
+
<SheetModal open={next} onOpenChange={() => {}} scrollable>
|
|
166
|
+
<Text>row</Text>
|
|
167
|
+
</SheetModal>
|
|
168
|
+
);
|
|
169
|
+
view.rerender(sheet(false));
|
|
170
|
+
view.rerender(sheet(true));
|
|
171
|
+
expect(px(frame.style.maxHeight)).toBe(full);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-299: an owned ScrollView inside the native sheet never scrolled on
|
|
3
|
+
* the iOS simulator, while the same swipe scrolled the Combobox sheet.
|
|
4
|
+
*
|
|
5
|
+
* On native, tamagui turns a press or focus-visible pseudo into a JS
|
|
6
|
+
* responder claim (`onStartShouldSetResponder` returning true). A surface
|
|
7
|
+
* holding that claim takes every touch that starts on plain content inside
|
|
8
|
+
* it, and a responder ancestor stops the ScrollView's own pan.
|
|
9
|
+
*/
|
|
10
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
11
|
+
import { cleanup } from "@testing-library/react";
|
|
12
|
+
import { afterEach, expect, it, vi } from "vitest";
|
|
13
|
+
import { Text } from "tamagui";
|
|
14
|
+
import { SheetModal } from "./index.native";
|
|
15
|
+
|
|
16
|
+
const stacks = vi.hoisted(() => new Map<string, Record<string, unknown>>());
|
|
17
|
+
|
|
18
|
+
vi.mock("react-native-web", async (importOriginal) => ({
|
|
19
|
+
...(await importOriginal<typeof import("react-native")>()),
|
|
20
|
+
ActionSheetIOS: undefined,
|
|
21
|
+
Modal: ({ children }: { children: import("react").ReactNode }) => children,
|
|
22
|
+
}));
|
|
23
|
+
vi.mock("tamagui", async (importOriginal) => {
|
|
24
|
+
const actual = await importOriginal<typeof import("tamagui")>();
|
|
25
|
+
const React = await import("react");
|
|
26
|
+
const YStack = React.forwardRef((props: Record<string, unknown>, ref) => {
|
|
27
|
+
if (typeof props.testID === "string") stacks.set(props.testID, props);
|
|
28
|
+
return React.createElement(actual.YStack as never, { ...props, ref });
|
|
29
|
+
});
|
|
30
|
+
return { ...actual, YStack };
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
cleanup();
|
|
35
|
+
stacks.clear();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const claimsResponder = [
|
|
39
|
+
"pressStyle",
|
|
40
|
+
"focusStyle",
|
|
41
|
+
"focusVisibleStyle",
|
|
42
|
+
"onPress",
|
|
43
|
+
"onPressIn",
|
|
44
|
+
"onPressOut",
|
|
45
|
+
"onLongPress",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
it("keeps every native sheet stack out of the touch responder", () => {
|
|
49
|
+
for (const fill of [false, true]) {
|
|
50
|
+
renderWithProviders(
|
|
51
|
+
<SheetModal open onOpenChange={() => {}} fill={fill} scrollable>
|
|
52
|
+
<Text>row</Text>
|
|
53
|
+
</SheetModal>,
|
|
54
|
+
);
|
|
55
|
+
for (const id of ["sheet-modal-surface", "sheet-modal-content"]) {
|
|
56
|
+
const stack = stacks.get(id);
|
|
57
|
+
expect(stack, id).toBeDefined();
|
|
58
|
+
for (const key of claimsResponder) expect(stack, `${id} ${key}`).not.toHaveProperty(key);
|
|
59
|
+
}
|
|
60
|
+
cleanup();
|
|
61
|
+
stacks.clear();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
* height with a large-detent cap. LC-71: the frame is not a control.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
useCallback,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useLayoutEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
useRef,
|
|
20
|
+
useState,
|
|
21
|
+
} from "react";
|
|
14
22
|
import {
|
|
15
23
|
Animated,
|
|
16
24
|
Easing,
|
|
@@ -23,6 +31,7 @@ import {
|
|
|
23
31
|
View as RNView,
|
|
24
32
|
useWindowDimensions,
|
|
25
33
|
} from "react-native";
|
|
34
|
+
import { SafeAreaInsetsContext, initialWindowMetrics } from "react-native-safe-area-context";
|
|
26
35
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
27
36
|
import { minSheetHeight, resolveSheetDetents } from "./detents";
|
|
28
37
|
import { useTranslation } from "../shared/i18n";
|
|
@@ -62,6 +71,7 @@ export function SheetModal({
|
|
|
62
71
|
const { knobProps } = useResolvedKnobs();
|
|
63
72
|
const { height: windowHeight } = useWindowDimensions();
|
|
64
73
|
const keyboardInset = useKeyboardInset();
|
|
74
|
+
const topInset = useContext(SafeAreaInsetsContext)?.top ?? initialWindowMetrics?.insets.top ?? 0;
|
|
65
75
|
const [backdropArmed, setBackdropArmed] = useState(false);
|
|
66
76
|
const armTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
67
77
|
|
|
@@ -71,11 +81,12 @@ export function SheetModal({
|
|
|
71
81
|
if (backdropArmed) close();
|
|
72
82
|
}, [backdropArmed, close]);
|
|
73
83
|
|
|
74
|
-
// MPO-64: the sheet ends above the keyboard rather than running under it
|
|
84
|
+
// MPO-64: the sheet ends above the keyboard rather than running under it,
|
|
85
|
+
// and below the status bar with a strip of backdrop still showing.
|
|
75
86
|
const available = Math.max(0, windowHeight - keyboardInset);
|
|
76
87
|
const maxHeight = Math.max(
|
|
77
88
|
minSheetHeight,
|
|
78
|
-
Math.round(Math.min(windowHeight * sheetMaxHeightRatio, available - backdropStrip)),
|
|
89
|
+
Math.round(Math.min(windowHeight * sheetMaxHeightRatio, available - topInset - backdropStrip)),
|
|
79
90
|
);
|
|
80
91
|
|
|
81
92
|
/*
|
|
@@ -127,8 +138,59 @@ export function SheetModal({
|
|
|
127
138
|
const presentationRef = useRef(0);
|
|
128
139
|
const activeRef = useRef(false);
|
|
129
140
|
|
|
141
|
+
/*
|
|
142
|
+
* MPO-68 / MPO-299: a sheet at rest below full height is LAID OUT at that
|
|
143
|
+
* height. Translating a full-height frame down pushed its bottom off the
|
|
144
|
+
* screen, or under the keyboard, and the scroll owner inside still measured
|
|
145
|
+
* the full height, so the tail could never be scrolled into view.
|
|
146
|
+
*
|
|
147
|
+
* `frameCap` rides the same Animated.View as `translateY`, so the swap
|
|
148
|
+
* between "full height, translated" and "detent height, at 0" lands in one
|
|
149
|
+
* commit and the top edge does not move. `restHeightRef` is null at full
|
|
150
|
+
* height; a gesture always runs uncapped, in `travel` coordinates.
|
|
151
|
+
*/
|
|
152
|
+
const frameCap = useRef(new Animated.Value(maxHeight)).current;
|
|
153
|
+
const maxHeightRef = useRef(maxHeight);
|
|
154
|
+
const restHeightRef = useRef<number | null>(null);
|
|
155
|
+
// The last uncapped layout, and whether it filled the max height. A capped
|
|
156
|
+
// frame measures only its cap, so when the keyboard moves the max height
|
|
157
|
+
// under a resting sheet, its full height is re-derived from these.
|
|
158
|
+
const naturalRef = useRef(0);
|
|
159
|
+
const filledRef = useRef(false);
|
|
160
|
+
|
|
161
|
+
useLayoutEffect(() => {
|
|
162
|
+
maxHeightRef.current = maxHeight;
|
|
163
|
+
const rest = restHeightRef.current;
|
|
164
|
+
frameCap.setValue(rest === null ? maxHeight : Math.min(maxHeight, rest));
|
|
165
|
+
if (rest !== null && naturalRef.current > 0) {
|
|
166
|
+
setMeasuredHeight(filledRef.current ? maxHeight : Math.min(naturalRef.current, maxHeight));
|
|
167
|
+
}
|
|
168
|
+
}, [frameCap, maxHeight]);
|
|
169
|
+
|
|
170
|
+
const restAt = useCallback(
|
|
171
|
+
(height: number) => {
|
|
172
|
+
restHeightRef.current = height;
|
|
173
|
+
frameCap.setValue(Math.min(maxHeightRef.current, height));
|
|
174
|
+
restRef.current = 0;
|
|
175
|
+
translateY.setValue(0);
|
|
176
|
+
},
|
|
177
|
+
[frameCap, translateY],
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const releaseRest = useCallback(() => {
|
|
181
|
+
const rest = restHeightRef.current;
|
|
182
|
+
if (rest === null) return;
|
|
183
|
+
restHeightRef.current = null;
|
|
184
|
+
const offset = Math.max(0, travelRef.current - Math.min(maxHeightRef.current, rest));
|
|
185
|
+
frameCap.setValue(maxHeightRef.current);
|
|
186
|
+
restRef.current = offset;
|
|
187
|
+
translateY.setValue(offset);
|
|
188
|
+
}, [frameCap, translateY]);
|
|
189
|
+
|
|
130
190
|
const settleTo = useCallback(
|
|
131
|
-
(
|
|
191
|
+
(height: number, velocity = 0) => {
|
|
192
|
+
const presentation = presentationRef.current;
|
|
193
|
+
const toValue = Math.max(0, travelRef.current - height);
|
|
132
194
|
restRef.current = toValue;
|
|
133
195
|
Animated.spring(translateY, {
|
|
134
196
|
toValue,
|
|
@@ -141,9 +203,12 @@ export function SheetModal({
|
|
|
141
203
|
// Modal does not advance on the New Architecture, and the drag path
|
|
142
204
|
// already writes this value from JS.
|
|
143
205
|
useNativeDriver: false,
|
|
144
|
-
}).start()
|
|
206
|
+
}).start(({ finished }) => {
|
|
207
|
+
if (!finished || toValue === 0) return;
|
|
208
|
+
if (activeRef.current && presentation === presentationRef.current) restAt(height);
|
|
209
|
+
});
|
|
145
210
|
},
|
|
146
|
-
[translateY],
|
|
211
|
+
[restAt, translateY],
|
|
147
212
|
);
|
|
148
213
|
|
|
149
214
|
const dismiss = useCallback(
|
|
@@ -171,10 +236,15 @@ export function SheetModal({
|
|
|
171
236
|
PanResponder.create({
|
|
172
237
|
onStartShouldSetPanResponder: () => true,
|
|
173
238
|
onMoveShouldSetPanResponder: (_evt, g) => Math.abs(g.dy) > 2,
|
|
239
|
+
// Lifting a rest cap can scroll the list inside, and a scroll lets
|
|
240
|
+
// any ancestor ScrollView the finger started under claim the touch,
|
|
241
|
+
// ending the drag and blurring a focused input on release.
|
|
242
|
+
onPanResponderTerminationRequest: () => false,
|
|
174
243
|
onPanResponderGrant: () => {
|
|
175
244
|
translateY.stopAnimation((value) => {
|
|
176
245
|
if (typeof value === "number" && Number.isFinite(value)) restRef.current = value;
|
|
177
246
|
});
|
|
247
|
+
releaseRest();
|
|
178
248
|
},
|
|
179
249
|
onPanResponderMove: (_evt, g) => {
|
|
180
250
|
const next = restRef.current + g.dy;
|
|
@@ -199,11 +269,11 @@ export function SheetModal({
|
|
|
199
269
|
best = height;
|
|
200
270
|
}
|
|
201
271
|
}
|
|
202
|
-
settleTo(
|
|
272
|
+
settleTo(best, g.vy);
|
|
203
273
|
},
|
|
204
|
-
onPanResponderTerminate: () => settleTo(restRef.current),
|
|
274
|
+
onPanResponderTerminate: () => settleTo(travelRef.current - restRef.current),
|
|
205
275
|
}),
|
|
206
|
-
[dismiss, settleTo, translateY],
|
|
276
|
+
[dismiss, releaseRest, settleTo, translateY],
|
|
207
277
|
);
|
|
208
278
|
|
|
209
279
|
/*
|
|
@@ -246,6 +316,8 @@ export function SheetModal({
|
|
|
246
316
|
useEffect(() => {
|
|
247
317
|
presentationRef.current += 1;
|
|
248
318
|
activeRef.current = open;
|
|
319
|
+
restHeightRef.current = null;
|
|
320
|
+
frameCap.setValue(maxHeightRef.current);
|
|
249
321
|
if (open) {
|
|
250
322
|
enter();
|
|
251
323
|
} else {
|
|
@@ -265,7 +337,7 @@ export function SheetModal({
|
|
|
265
337
|
}
|
|
266
338
|
translateY.stopAnimation();
|
|
267
339
|
};
|
|
268
|
-
}, [open, enter, translateY]);
|
|
340
|
+
}, [open, enter, frameCap, translateY]);
|
|
269
341
|
|
|
270
342
|
return (
|
|
271
343
|
<Modal
|
|
@@ -287,8 +359,12 @@ export function SheetModal({
|
|
|
287
359
|
</Pressable>
|
|
288
360
|
<Animated.View
|
|
289
361
|
onLayout={(e) => {
|
|
362
|
+
if (restHeightRef.current !== null) return;
|
|
290
363
|
const h = Math.round(e.nativeEvent.layout.height);
|
|
291
|
-
if (h
|
|
364
|
+
if (h <= 0) return;
|
|
365
|
+
naturalRef.current = h;
|
|
366
|
+
filledRef.current = h >= maxHeightRef.current - 1;
|
|
367
|
+
if (h !== measuredHeight) setMeasuredHeight(h);
|
|
292
368
|
}}
|
|
293
369
|
testID="sheet-modal-frame"
|
|
294
370
|
style={{
|
|
@@ -296,10 +372,10 @@ export function SheetModal({
|
|
|
296
372
|
// the sheet cannot give way to the keyboard.
|
|
297
373
|
flexShrink: 1,
|
|
298
374
|
minHeight: 0,
|
|
299
|
-
maxHeight,
|
|
375
|
+
maxHeight: frameCap,
|
|
300
376
|
// The cap is already keyboard- and window-aware; fill only asks
|
|
301
377
|
// for it as a definite height rather than an upper bound.
|
|
302
|
-
...(fill ? { height:
|
|
378
|
+
...(fill ? { height: frameCap } : null),
|
|
303
379
|
transform: [{ translateY }],
|
|
304
380
|
}}
|
|
305
381
|
>
|
|
@@ -311,13 +387,15 @@ export function SheetModal({
|
|
|
311
387
|
borderBottomRightRadius={0}
|
|
312
388
|
elevation={knobProps.elevation}
|
|
313
389
|
maxHeight={maxHeight}
|
|
314
|
-
{
|
|
390
|
+
flexShrink={1}
|
|
391
|
+
minHeight={0}
|
|
392
|
+
{...(fill ? fillSlot : null)}
|
|
315
393
|
paddingTop="$2"
|
|
316
394
|
paddingBottom={keyboardInset > 0 ? 8 : bottomInset}
|
|
317
395
|
overflow="hidden"
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
396
|
+
// No focus or press pseudo on this surface: on native tamagui
|
|
397
|
+
// turns one into a touch-responder claim, and a responder here
|
|
398
|
+
// takes every drag from the ScrollView inside (MPO-299).
|
|
321
399
|
accessibilityViewIsModal
|
|
322
400
|
onAccessibilityEscape={close}
|
|
323
401
|
>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* MPO-279: on touch mobile web the server cannot know the device, so it renders
|
|
4
|
+
* pointer-sized tabs. Hydration has to reproduce that markup, then adopt the
|
|
5
|
+
* touch floor; a first client render at touch size is an attribute mismatch
|
|
6
|
+
* React never patches.
|
|
7
|
+
*/
|
|
8
|
+
import { TestProviders } from "@multiplatform.one/test-utils";
|
|
9
|
+
import { act } from "react";
|
|
10
|
+
import { hydrateRoot, type Root } from "react-dom/client";
|
|
11
|
+
import { renderToString } from "react-dom/server";
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
13
|
+
import { Tabs } from "./index";
|
|
14
|
+
|
|
15
|
+
const host = vi.hoisted(() => {
|
|
16
|
+
vi.stubGlobal("matchMedia", () => ({
|
|
17
|
+
matches: false,
|
|
18
|
+
addEventListener() {},
|
|
19
|
+
removeEventListener() {},
|
|
20
|
+
addListener() {},
|
|
21
|
+
removeListener() {},
|
|
22
|
+
}));
|
|
23
|
+
return { web: true, touch: false };
|
|
24
|
+
});
|
|
25
|
+
vi.mock("@multiplatform.one/platform", async (importOriginal) => ({
|
|
26
|
+
...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
|
|
27
|
+
get isWeb() {
|
|
28
|
+
return host.web;
|
|
29
|
+
},
|
|
30
|
+
get isTouchable() {
|
|
31
|
+
return !host.web && host.touch;
|
|
32
|
+
},
|
|
33
|
+
get isWebTouchable() {
|
|
34
|
+
return host.web && host.touch;
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const items = [
|
|
39
|
+
{ value: "address", label: "Address & Contact", content: <>address panel</> },
|
|
40
|
+
{ value: "settings", label: "Settings", content: <>settings panel</> },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function Fixture() {
|
|
44
|
+
return (
|
|
45
|
+
<TestProviders>
|
|
46
|
+
<Tabs items={items} defaultValue="address" />
|
|
47
|
+
</TestProviders>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Every `_h-` height atom painted inside each tab, in document order. */
|
|
52
|
+
function tabHeights(root: ParentNode): string[][] {
|
|
53
|
+
return [...root.querySelectorAll('[role="tab"]')].map((tab) =>
|
|
54
|
+
[tab, ...tab.querySelectorAll("*")].flatMap((node) =>
|
|
55
|
+
[...node.classList].filter((name) => /^_h-\d/.test(name)),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let container: HTMLDivElement;
|
|
61
|
+
let root: Root | undefined;
|
|
62
|
+
let errors: unknown[];
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
host.web = true;
|
|
66
|
+
host.touch = false;
|
|
67
|
+
errors = [];
|
|
68
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
69
|
+
vi.spyOn(console, "error").mockImplementation((...args) => errors.push(args));
|
|
70
|
+
container = document.createElement("div");
|
|
71
|
+
document.body.append(container);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(async () => {
|
|
75
|
+
if (root) await act(async () => root?.unmount());
|
|
76
|
+
root = undefined;
|
|
77
|
+
container.remove();
|
|
78
|
+
vi.restoreAllMocks();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
async function hydrate() {
|
|
82
|
+
await act(async () => {
|
|
83
|
+
root = hydrateRoot(container, <Fixture />, {
|
|
84
|
+
onRecoverableError: (error) => errors.push(error),
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const hydrationErrors = () =>
|
|
90
|
+
errors.filter((error) => /hydration|hydrated|didn't match/i.test(String(error)));
|
|
91
|
+
|
|
92
|
+
describe("Tabs sizing through hydration (MPO-279)", () => {
|
|
93
|
+
it("keeps the server's pointer-sized tabs through hydration, then adopts the touch floor", async () => {
|
|
94
|
+
container.innerHTML = renderToString(<Fixture />);
|
|
95
|
+
const serverTabs = [...container.querySelectorAll('[role="tab"]')];
|
|
96
|
+
const serverHeights = tabHeights(container);
|
|
97
|
+
expect(serverHeights.flat()).not.toHaveLength(0);
|
|
98
|
+
|
|
99
|
+
host.touch = true;
|
|
100
|
+
await hydrate();
|
|
101
|
+
|
|
102
|
+
expect(hydrationErrors()).toEqual([]);
|
|
103
|
+
expect([...container.querySelectorAll('[role="tab"]')]).toEqual(serverTabs);
|
|
104
|
+
const clientHeights = tabHeights(container);
|
|
105
|
+
expect(clientHeights).not.toEqual(serverHeights);
|
|
106
|
+
for (const heights of clientHeights) expect(heights).toContain("_h-48px");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("leaves pointer sizing alone on a pointer device", async () => {
|
|
110
|
+
container.innerHTML = renderToString(<Fixture />);
|
|
111
|
+
const serverHeights = tabHeights(container);
|
|
112
|
+
await hydrate();
|
|
113
|
+
expect(hydrationErrors()).toEqual([]);
|
|
114
|
+
expect(tabHeights(container)).toEqual(serverHeights);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("sizes native tabs to the touch floor on the first render", () => {
|
|
118
|
+
host.web = false;
|
|
119
|
+
host.touch = true;
|
|
120
|
+
container.innerHTML = renderToString(<Fixture />);
|
|
121
|
+
for (const heights of tabHeights(container)) expect(heights).toContain("_h-48px");
|
|
122
|
+
});
|
|
123
|
+
});
|
package/src/Tabs/index.tsx
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
useResolvedKnobs,
|
|
38
38
|
wasKeyboardFocus,
|
|
39
39
|
type GroupPosition,
|
|
40
|
+
transitionProps,
|
|
40
41
|
} from "@multiplatform.one/theme";
|
|
41
42
|
import type { ReactNode } from "react";
|
|
42
43
|
import React from "react";
|
|
@@ -492,7 +493,7 @@ function TabsRoot({
|
|
|
492
493
|
x={activeLayout.x}
|
|
493
494
|
width={activeLayout.width}
|
|
494
495
|
data-tabs-indicator="underline"
|
|
495
|
-
{...(transition
|
|
496
|
+
{...transitionProps(transition)}
|
|
496
497
|
/>
|
|
497
498
|
</Theme>
|
|
498
499
|
) : (
|
|
@@ -508,7 +509,7 @@ function TabsRoot({
|
|
|
508
509
|
{...(knobProps.elevation !== undefined
|
|
509
510
|
? { elevation: knobProps.elevation }
|
|
510
511
|
: undefined)}
|
|
511
|
-
{...(transition
|
|
512
|
+
{...transitionProps(transition)}
|
|
512
513
|
/>
|
|
513
514
|
</Theme>
|
|
514
515
|
)
|
package/src/Text.spec.tsx
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { cleanup, screen } from "@testing-library/react";
|
|
23
23
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
24
|
-
import { Preset } from "@multiplatform.one/theme";
|
|
24
|
+
import { Preset, useAccentOnSurface } from "@multiplatform.one/theme";
|
|
25
25
|
import { XStack } from "tamagui";
|
|
26
26
|
import { afterEach, describe, expect, it } from "vitest";
|
|
27
|
-
import { Text } from "./Text";
|
|
27
|
+
import { Anchor, Text } from "./Text";
|
|
28
28
|
|
|
29
29
|
afterEach(cleanup);
|
|
30
30
|
|
|
@@ -181,6 +181,36 @@ describe("house Text — fontWeight and bodyFont reach the text node", () => {
|
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
describe("house Anchor — link chrome is house Text's, in the accent (MPO-21 G11-A)", () => {
|
|
185
|
+
function AccentProbe({ onInk }: { onInk: (ink: string) => void }) {
|
|
186
|
+
onInk(useAccentOnSurface());
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
it("paints the AA-safe accent ink, not tamagui's link colour", () => {
|
|
191
|
+
let ink = "";
|
|
192
|
+
const anchor = renderTextClasses(
|
|
193
|
+
<>
|
|
194
|
+
<AccentProbe onInk={(value) => (ink = value)} />
|
|
195
|
+
<Anchor href="#">{LABEL}</Anchor>
|
|
196
|
+
</>,
|
|
197
|
+
["_col-"],
|
|
198
|
+
);
|
|
199
|
+
expect(ink).not.toBe("");
|
|
200
|
+
const accented = renderTextClasses(<Anchor color={ink as never}>{LABEL}</Anchor>, ["_col-"]);
|
|
201
|
+
const body = renderTextClasses(<Text>{LABEL}</Text>, ["_col-"]);
|
|
202
|
+
expect(anchor).not.toHaveLength(0);
|
|
203
|
+
expect(anchor).toEqual(accented);
|
|
204
|
+
expect(anchor).not.toEqual(body);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("a consumer colour still ejects (STD-EJECT-LAST)", () => {
|
|
208
|
+
const ejected = renderTextClasses(<Anchor color="$color9">{LABEL}</Anchor>, ["_col-"]);
|
|
209
|
+
const explicit = renderTextClasses(<Text color="$color9">{LABEL}</Text>, ["_col-"]);
|
|
210
|
+
expect(ejected).toEqual(explicit);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
184
214
|
// The NATIVE arm lives in ./Text.native.spec.tsx. It used to sit here as a
|
|
185
215
|
// `vi.resetModules()` + dynamic-import block, but that returned a SPLIT module
|
|
186
216
|
// graph — a fresh `renderWithProviders` over a stale `useResolvedKnobs` — and
|
package/src/Text.tsx
CHANGED
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
* 500/600 are not label weights; see tamagui-rendered-reference.md §9)
|
|
25
25
|
* - textAccent → knobProps.textAccentColor (high $color / medium+low
|
|
26
26
|
* $color11, the AA legibility floor) on Text/SizableText/Paragraph.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* - link ink → Anchor paints the accent, as the most chromatic step of the
|
|
28
|
+
* theme's accent ramp that still clears AA on both page surfaces
|
|
29
|
+
* (`useAccentOnSurface`). House Text owns link chrome (Clay 2026-09-26,
|
|
30
|
+
* MPO-21 G11-A); a consumer `color` still ejects.
|
|
31
31
|
*
|
|
32
32
|
* Default body/label ramp (tamagui-rendered-reference.md §9, the $4 / $true
|
|
33
33
|
* stop): 14px / 25px / 400. createInterFont's size+10 table yields 14/24;
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* Raw primitives stay reachable as TamaguiText / TamaguiSizableText /
|
|
39
39
|
* TamaguiParagraph / TamaguiAnchor (./tamagui.ts escape hatches, LC-45).
|
|
40
40
|
*/
|
|
41
|
-
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
41
|
+
import { useAccentOnSurface, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
42
42
|
import {
|
|
43
43
|
Anchor as RawAnchor,
|
|
44
44
|
type AnchorProps,
|
|
@@ -81,5 +81,6 @@ export function Paragraph(props: ParagraphProps) {
|
|
|
81
81
|
|
|
82
82
|
export function Anchor(props: AnchorProps) {
|
|
83
83
|
const { knobProps } = useResolvedKnobs();
|
|
84
|
-
|
|
84
|
+
const linkInk = useAccentOnSurface();
|
|
85
|
+
return <RawAnchor {...knobProps.body} color={linkInk} {...props} />;
|
|
85
86
|
}
|