@multiplatform.one/components 7.10.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 +100 -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.cjs +22 -3
- package/dist/cjs/SheetModal/index.native.js +115 -27
- package/dist/cjs/SheetModal/index.native.js.map +1 -1
- package/dist/cjs/Tabs/index.cjs +35 -18
- package/dist/cjs/Tabs/index.native.js +39 -18
- 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/layouts/tags.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/TreeView.cjs +175 -43
- package/dist/cjs/views/TreeView.native.js +181 -46
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- 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.mjs +22 -3
- package/dist/esm/SheetModal/index.mjs.map +1 -1
- package/dist/esm/SheetModal/index.native.js +117 -29
- 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 +36 -19
- package/dist/esm/Tabs/index.mjs.map +1 -1
- package/dist/esm/Tabs/index.native.js +40 -19
- 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.mjs.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.mjs +175 -43
- package/dist/esm/views/TreeView.mjs.map +1 -1
- package/dist/esm/views/TreeView.native.js +181 -46
- 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.mjs +22 -3
- package/dist/jsx/SheetModal/index.mjs.map +1 -1
- package/dist/jsx/SheetModal/index.native.js +115 -27
- package/dist/jsx/SheetModal/index.native.js.map +1 -1
- package/dist/jsx/Tabs/index.mjs +36 -19
- package/dist/jsx/Tabs/index.mjs.map +1 -1
- package/dist/jsx/Tabs/index.native.js +39 -18
- 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/layouts/tags.mjs.map +1 -1
- package/dist/jsx/layouts/tags.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/TreeView.mjs +175 -43
- package/dist/jsx/views/TreeView.mjs.map +1 -1
- package/dist/jsx/views/TreeView.native.js +181 -46
- package/dist/jsx/views/TreeView.native.js.map +1 -1
- 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 +12 -12
- package/src/Accordion/Accordion.spec.tsx +20 -0
- package/src/Accordion/index.tsx +8 -4
- 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.fill.spec.tsx +147 -0
- package/src/SheetModal/SheetModal.ownedBody.spec.tsx +200 -0
- package/src/SheetModal/SheetModal.stories.spec.tsx +23 -2
- package/src/SheetModal/SheetModal.stories.tsx +275 -2
- package/src/SheetModal/SheetModal.touch.spec.tsx +63 -0
- package/src/SheetModal/index.native.tsx +143 -21
- package/src/SheetModal/index.tsx +40 -3
- package/src/Tabs/Tabs.hydration.spec.tsx +123 -0
- package/src/Tabs/Tabs.lifecycle.spec.tsx +123 -0
- package/src/Tabs/Tabs.measurement.spec.tsx +186 -0
- package/src/Tabs/Tabs.stories.tsx +43 -0
- package/src/Tabs/index.tsx +45 -15
- 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/layouts/tags.tsx +2 -1
- 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/TreeView.reveal.spec.tsx +7 -0
- package/src/views/TreeView.reveal.stories.tsx +134 -0
- package/src/views/TreeView.scroll-index.spec.tsx +246 -0
- package/src/views/TreeView.scroll.spec.tsx +711 -50
- package/src/views/TreeView.tsx +230 -49
- 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.d.ts +8 -1
- package/types/SheetModal/index.d.ts.map +1 -1
- package/types/SheetModal/index.native.d.ts +1 -1
- 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/layouts/tags.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/TreeView.d.ts +10 -1
- package/types/views/TreeView.d.ts.map +1 -1
- 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,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `00` L1 test 1, the adjacency check (MPO-23, gap ledger GL-41). A Button,
|
|
3
|
+
* an Input, a Select trigger and a ListItem in one `XStack` at the same size
|
|
4
|
+
* token, with only a `gap`, must agree on height and on radius. They differ
|
|
5
|
+
* only in interior padding (`00` L9f). `constraintAudit` checks that each
|
|
6
|
+
* value lands on a token step; nothing checked that siblings agree.
|
|
7
|
+
*
|
|
8
|
+
* The DOM here cannot lay out and does not resolve Tamagui's CSS variables,
|
|
9
|
+
* so each frame's box is read from the atoms it paints (`_h-44px`,
|
|
10
|
+
* `_btlr-t-radius-4`, `_mih-t-size-4`) and resolved through the live token
|
|
11
|
+
* table. That is the same box a browser computes for these frames: none of
|
|
12
|
+
* them grows past its height or min-height with one line of content.
|
|
13
|
+
*
|
|
14
|
+
* The law holds for height at every size stop and for the controls' radius at
|
|
15
|
+
* the default stop. ListItem joins the height check only: it is a flush row
|
|
16
|
+
* (SP-EDGE, radius 0) and never takes the controls' radius (Clay 2026-09-26,
|
|
17
|
+
* MPO-23 ruling 1A). Two disagreements are open and pinned below as expected
|
|
18
|
+
* failures, each naming its culprit. Fixing one makes its `it.fails` go red,
|
|
19
|
+
* which is the signal to turn it into a plain assertion.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { Input, Select } from "@multiplatform.one/forms";
|
|
23
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
24
|
+
import { Preset, type Size } from "@multiplatform.one/theme";
|
|
25
|
+
import { cleanup } from "@testing-library/react";
|
|
26
|
+
import type { ReactElement } from "react";
|
|
27
|
+
import { getTokens } from "tamagui";
|
|
28
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
29
|
+
import { Button } from "./Button";
|
|
30
|
+
import { ListItem, XStack } from "./tamagui";
|
|
31
|
+
|
|
32
|
+
afterEach(cleanup);
|
|
33
|
+
|
|
34
|
+
type Box = { height: number; radius: number };
|
|
35
|
+
type Row = Record<"Button" | "Input" | "Select trigger" | "ListItem", Box>;
|
|
36
|
+
|
|
37
|
+
const stops = [
|
|
38
|
+
["small", "$3"],
|
|
39
|
+
["medium", "$4"],
|
|
40
|
+
["large", "$5"],
|
|
41
|
+
] as const satisfies readonly (readonly [Size, string])[];
|
|
42
|
+
|
|
43
|
+
function atom(el: HTMLElement, prop: string) {
|
|
44
|
+
for (const cls of String(el.className || "").split(/\s+/)) {
|
|
45
|
+
if (!cls.startsWith(`_${prop}-`)) continue;
|
|
46
|
+
const value = cls.slice(prop.length + 2);
|
|
47
|
+
if (/^-?\d+(?:\.\d+)?px$|^t-(?:size|radius)-\d+$/.test(value)) return value;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function px(value: string | undefined): number | undefined {
|
|
53
|
+
if (value === undefined) return undefined;
|
|
54
|
+
const literal = value.match(/^(-?\d+(?:\.\d+)?)px$/);
|
|
55
|
+
if (literal) return Number(literal[1]);
|
|
56
|
+
const [, scale, step] = value.match(/^t-(size|radius)-(\d+)$/) ?? [];
|
|
57
|
+
const tokens = getTokens() as unknown as Record<string, Record<string, { val: unknown }>>;
|
|
58
|
+
return Number.parseFloat(String(tokens[scale ?? ""]?.[`$${step}`]?.val));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function box(el: HTMLElement | null, name: string): Box {
|
|
62
|
+
if (!el) throw new Error(`${name} frame not rendered`);
|
|
63
|
+
const height = px(atom(el, "h") ?? atom(el, "mih"));
|
|
64
|
+
if (height === undefined) throw new Error(`${name} paints no height`);
|
|
65
|
+
return { height, radius: px(atom(el, "btlr")) ?? 0 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function measure(ui: ReactElement): Row {
|
|
69
|
+
const { container } = renderWithProviders(ui);
|
|
70
|
+
const frame = (selector: string) => container.querySelector<HTMLElement>(selector);
|
|
71
|
+
return {
|
|
72
|
+
Button: box(frame("[data-mp-button-height]"), "Button"),
|
|
73
|
+
Input: box(frame("[data-mp-input-box]"), "Input"),
|
|
74
|
+
"Select trigger": box(frame('[data-testid="select-trigger"]'), "Select trigger"),
|
|
75
|
+
ListItem: box(frame('[data-testid="l1-list-item"]'), "ListItem"),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The mpo size channel: the size knob, which every house control reads. */
|
|
80
|
+
function atKnob(knob: Size, token: string) {
|
|
81
|
+
return measure(
|
|
82
|
+
<Preset overrides={{ size: knob }}>
|
|
83
|
+
<XStack gap="$2">
|
|
84
|
+
<Button>Save</Button>
|
|
85
|
+
<Input placeholder="Name" />
|
|
86
|
+
<Select options={[{ value: "a", label: "A" }]} placeholder="Pick" />
|
|
87
|
+
<ListItem size={token as "$4"} title="Row" data-testid="l1-list-item" />
|
|
88
|
+
</XStack>
|
|
89
|
+
</Preset>,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The same token handed to each component directly. */
|
|
94
|
+
function atSizeProp(token: "$3" | "$4" | "$5") {
|
|
95
|
+
return measure(
|
|
96
|
+
<XStack gap="$2">
|
|
97
|
+
<Button size={token}>Save</Button>
|
|
98
|
+
<Input size={token} placeholder="Name" />
|
|
99
|
+
<Select size={token} options={[{ value: "a", label: "A" }]} placeholder="Pick" />
|
|
100
|
+
<ListItem size={token} title="Row" data-testid="l1-list-item" />
|
|
101
|
+
</XStack>,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function heights(row: Row) {
|
|
106
|
+
return Object.fromEntries(Object.entries(row).map(([name, b]) => [name, b.height]));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function radii(row: Row, names: (keyof Row)[]) {
|
|
110
|
+
return Object.fromEntries(names.map((name) => [name, row[name].radius]));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function agreed(values: Record<string, number>) {
|
|
114
|
+
const first = Object.values(values)[0];
|
|
115
|
+
return Object.fromEntries(Object.keys(values).map((name) => [name, first]));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const controls: (keyof Row)[] = ["Button", "Input", "Select trigger"];
|
|
119
|
+
|
|
120
|
+
describe("00 L1 adjacency: Button, Input, Select trigger and ListItem at one size token", () => {
|
|
121
|
+
for (const [knob, token] of stops) {
|
|
122
|
+
it(`all four paint one height at size ${knob} (${token})`, () => {
|
|
123
|
+
const row = heights(atKnob(knob, token));
|
|
124
|
+
expect(row).toEqual(agreed(row));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
it("catches a deliberate violation: a Button ejected to its own height", () => {
|
|
129
|
+
const row = heights(
|
|
130
|
+
measure(
|
|
131
|
+
<XStack gap="$2">
|
|
132
|
+
<Button height={30} sizeRecipeEscape="L1 adjacency fixture">
|
|
133
|
+
Save
|
|
134
|
+
</Button>
|
|
135
|
+
<Input placeholder="Name" />
|
|
136
|
+
<Select options={[{ value: "a", label: "A" }]} placeholder="Pick" />
|
|
137
|
+
<ListItem size="$4" title="Row" data-testid="l1-list-item" />
|
|
138
|
+
</XStack>,
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
expect(row.Button).toBe(30);
|
|
142
|
+
expect(row).not.toEqual(agreed(row));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("Button, Input and Select trigger paint one radius at the default size", () => {
|
|
146
|
+
const row = radii(atKnob("medium", "$4"), controls);
|
|
147
|
+
expect(row).toEqual(agreed(row));
|
|
148
|
+
expect(row.Button).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("ListItem stays a flush row beside them: radius 0, never the controls' radius", () => {
|
|
152
|
+
const row = radii(atKnob("medium", "$4"), [...controls, "ListItem"]);
|
|
153
|
+
expect(row.ListItem).toBe(0);
|
|
154
|
+
expect(row.Button).toBeGreaterThan(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
for (const [knob, token] of stops.filter(([k]) => k !== "medium")) {
|
|
158
|
+
it.fails(`OPEN: Button and Input paint one radius at size ${knob} (Button's radius follows size, Input's only the radius knob)`, () => {
|
|
159
|
+
const row = radii(atKnob(knob, token), controls);
|
|
160
|
+
expect(row).toEqual(agreed(row));
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
it("an explicit size prop agrees with itself at the default token", () => {
|
|
165
|
+
const row = heights(atSizeProp("$4"));
|
|
166
|
+
expect(row).toEqual(agreed(row));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
for (const token of ["$3", "$5"] as const) {
|
|
170
|
+
it.fails(`OPEN: an explicit size=${token} moves Input and Select trigger with Button (Input paints the knob height, Select ignores size)`, () => {
|
|
171
|
+
const row = heights(atSizeProp(token));
|
|
172
|
+
expect(row).toEqual(agreed(row));
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
-
import { describe, expect, it
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { Code, CodeInline } from "./index";
|
|
4
4
|
|
|
5
5
|
describe("Code", () => {
|
|
@@ -27,7 +27,7 @@ describe("Code", () => {
|
|
|
27
27
|
const codeElement = container.querySelector("code");
|
|
28
28
|
expect(codeElement).toBeTruthy();
|
|
29
29
|
expect(codeElement!.getAttribute("data-code-font")).toBe("mono");
|
|
30
|
-
//
|
|
30
|
+
// An RN prop map on this host would land as dataset="[object Object]".
|
|
31
31
|
expect(codeElement!.hasAttribute("dataset")).toBe(false);
|
|
32
32
|
});
|
|
33
33
|
});
|
|
@@ -51,21 +51,4 @@ describe("CodeInline", () => {
|
|
|
51
51
|
const codeElement = container.querySelector("code");
|
|
52
52
|
expect(codeElement).toBeDefined();
|
|
53
53
|
});
|
|
54
|
-
|
|
55
|
-
it("emits data-code-font on web without a dataSet host-prop leak", () => {
|
|
56
|
-
const leak = /does not recognize the `dataSet` prop on a DOM element/;
|
|
57
|
-
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
58
|
-
try {
|
|
59
|
-
const { container } = renderWithProviders(<CodeInline>npm install</CodeInline>);
|
|
60
|
-
const codeElement = container.querySelector("code");
|
|
61
|
-
expect(codeElement).toBeDefined();
|
|
62
|
-
expect(codeElement?.getAttribute("data-code-font")).toBe("mono");
|
|
63
|
-
const leaks = errorSpy.mock.calls
|
|
64
|
-
.map((args) => args.map(String).join(" "))
|
|
65
|
-
.filter((message) => leak.test(message));
|
|
66
|
-
expect(leaks).toEqual([]);
|
|
67
|
-
} finally {
|
|
68
|
-
errorSpy.mockRestore();
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
54
|
});
|
package/src/code/Code/index.tsx
CHANGED
|
@@ -81,12 +81,6 @@ export const CodeInline = CodeInlineFrame.styleable((props, ref) => {
|
|
|
81
81
|
fontFamily="$mono"
|
|
82
82
|
letterSpacing={0}
|
|
83
83
|
data-code-font="mono"
|
|
84
|
-
// react-native-web reads dataSet; React-DOM warns on the camel host
|
|
85
|
-
// prop. Keep the kebab attribute on every target; spread dataSet
|
|
86
|
-
// only when TAMAGUI_TARGET is native.
|
|
87
|
-
{...(process.env.TAMAGUI_TARGET === "native"
|
|
88
|
-
? ({ dataSet: { codeFont: "mono" } } as object)
|
|
89
|
-
: {})}
|
|
90
84
|
/>
|
|
91
85
|
);
|
|
92
86
|
});
|
|
@@ -10,10 +10,11 @@ import { LinearGradient } from "@tamagui/linear-gradient";
|
|
|
10
10
|
import { ClipboardIcon } from "@phosphor-icons/react";
|
|
11
11
|
import {
|
|
12
12
|
MIN_PRESS_TARGET,
|
|
13
|
-
|
|
13
|
+
useTouchSurface,
|
|
14
14
|
pressTargetHitSlop,
|
|
15
15
|
sizeRecipeForToken,
|
|
16
16
|
useResolvedKnobs,
|
|
17
|
+
transitionProps,
|
|
17
18
|
} from "@multiplatform.one/theme";
|
|
18
19
|
import { useEffect, useRef, useState } from "react";
|
|
19
20
|
import { useTranslation } from "../../shared/i18n";
|
|
@@ -75,6 +76,7 @@ export function CodeBlock({
|
|
|
75
76
|
const { region, copy } = useCopyFeedback();
|
|
76
77
|
const { t } = useTranslation();
|
|
77
78
|
const { knobProps } = useResolvedKnobs();
|
|
79
|
+
const touch = useTouchSurface();
|
|
78
80
|
// Content type scale follows the size knob (T-VALUE via sizeToken, LC-18);
|
|
79
81
|
// an explicit size prop ejects.
|
|
80
82
|
const codeSize = (size ?? knobProps.sizeToken) as any;
|
|
@@ -126,7 +128,7 @@ export function CodeBlock({
|
|
|
126
128
|
const activateCopy = () => {
|
|
127
129
|
void copy(codeText);
|
|
128
130
|
};
|
|
129
|
-
const capSquare = sizeRecipeForToken(String(codeSize), { touch
|
|
131
|
+
const capSquare = sizeRecipeForToken(String(codeSize), { touch }).height;
|
|
130
132
|
|
|
131
133
|
return (
|
|
132
134
|
<YStack position="relative" marginBottom="$4">
|
|
@@ -134,11 +136,11 @@ export function CodeBlock({
|
|
|
134
136
|
<YStack
|
|
135
137
|
ref={clampRef}
|
|
136
138
|
position="relative"
|
|
139
|
+
{...transitionProps(transition)}
|
|
137
140
|
{...(clamped && {
|
|
138
141
|
maxHeight: expandTarget ?? clampHeight,
|
|
139
142
|
overflow: "hidden" as const,
|
|
140
143
|
...knobProps.borderRadius,
|
|
141
|
-
...(transition ? { transition } : {}),
|
|
142
144
|
})}
|
|
143
145
|
>
|
|
144
146
|
{isCutoff && expandTarget == null && (
|
|
@@ -125,8 +125,8 @@ describe("HighlightedCode", () => {
|
|
|
125
125
|
// The actual LC-07 pin: bodyFont serif / fontWeight bold must not reach
|
|
126
126
|
// code text, so the line keeps the mono family class.
|
|
127
127
|
expect(line!.className).toContain("font_mono");
|
|
128
|
-
// React does not recognise
|
|
129
|
-
// unknown-prop error and wrote dataset="[object Object]" on every node.
|
|
128
|
+
// React does not recognise the RN prop map; leaving it on the line logged
|
|
129
|
+
// an unknown-prop error and wrote dataset="[object Object]" on every node.
|
|
130
130
|
expect(container.querySelectorAll("[dataset]")).toHaveLength(0);
|
|
131
131
|
});
|
|
132
132
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { Code, CodeInline } from "./Code";
|
|
5
|
+
import type { HighlightLine } from "./Highlighter";
|
|
6
|
+
import { HighlightedCode } from "./HighlightedCode";
|
|
7
|
+
|
|
8
|
+
const tokens: HighlightLine[] = [[{ content: "const x = 1;" }]];
|
|
9
|
+
|
|
10
|
+
const cases: [string, ReactElement][] = [
|
|
11
|
+
["Code", <Code>const x = 1;</Code>],
|
|
12
|
+
["CodeInline", <CodeInline>npm install</CodeInline>],
|
|
13
|
+
["HighlightedCode", <HighlightedCode tokens={tokens} />],
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function hostProps(el: Element): Record<string, unknown> {
|
|
17
|
+
const key = Object.keys(el).find((k) => k.startsWith("__reactProps$"));
|
|
18
|
+
return key ? ((el as unknown as Record<string, Record<string, unknown>>)[key] ?? {}) : {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatConsoleCall(args: unknown[]): string {
|
|
22
|
+
const [format = "", ...substitutions] = args.map(String);
|
|
23
|
+
let next = 0;
|
|
24
|
+
return format.replace(/%s/g, () => substitutions[next++] ?? "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.unstubAllEnvs();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe.each(["web", "native"])("code family host props, TAMAGUI_TARGET=%s", (target) => {
|
|
32
|
+
it.each(cases)("%s hands the code-font node no dataset prop map", (_name, node) => {
|
|
33
|
+
vi.stubEnv("TAMAGUI_TARGET", target);
|
|
34
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
35
|
+
try {
|
|
36
|
+
const { container } = renderWithProviders(node);
|
|
37
|
+
const hosts = Array.from(container.querySelectorAll("[data-code-font]"));
|
|
38
|
+
expect(hosts.length).toBeGreaterThan(0);
|
|
39
|
+
for (const host of hosts) {
|
|
40
|
+
const props = hostProps(host);
|
|
41
|
+
expect(props["data-code-font"]).toBe("mono");
|
|
42
|
+
expect(Object.keys(props).filter((key) => key.toLowerCase() === "dataset")).toEqual([]);
|
|
43
|
+
expect(host.hasAttribute("dataset")).toBe(false);
|
|
44
|
+
}
|
|
45
|
+
// React warns once per prop name per process, so the console alone
|
|
46
|
+
// cannot prove a later case clean; the props check above can.
|
|
47
|
+
const unknownProps = errorSpy.mock.calls
|
|
48
|
+
.map(formatConsoleCall)
|
|
49
|
+
.filter((message) => /does not recognize the `\w+` prop on a DOM element/.test(message));
|
|
50
|
+
expect(unknownProps).toEqual([]);
|
|
51
|
+
} finally {
|
|
52
|
+
errorSpy.mockRestore();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act } from "react";
|
|
3
|
+
import { renderToString } from "react-dom/server";
|
|
4
|
+
import { hydrateRoot } from "react-dom/client";
|
|
5
|
+
import { TestProviders } from "@multiplatform.one/test-utils";
|
|
6
|
+
import { sizeRecipeForToken } from "@multiplatform.one/theme";
|
|
7
|
+
import { describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { themed } from "./themed";
|
|
9
|
+
|
|
10
|
+
const host = vi.hoisted(() => ({ touch: false }));
|
|
11
|
+
vi.mock("@multiplatform.one/platform", async (importOriginal) => ({
|
|
12
|
+
...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
|
|
13
|
+
isWeb: true,
|
|
14
|
+
isTouchable: false,
|
|
15
|
+
get isWebTouchable() {
|
|
16
|
+
return host.touch;
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const Icon = themed(({ size }: { size?: string | number }) => <svg width={size} height={size} />);
|
|
21
|
+
function Fixture() {
|
|
22
|
+
return (
|
|
23
|
+
<TestProviders>
|
|
24
|
+
<Icon size="$4" />
|
|
25
|
+
</TestProviders>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("token icon hydration", () => {
|
|
30
|
+
it("preserves the server SVG before adopting touch sizing", async () => {
|
|
31
|
+
host.touch = false;
|
|
32
|
+
const container = document.createElement("div");
|
|
33
|
+
container.innerHTML = renderToString(<Fixture />);
|
|
34
|
+
document.body.append(container);
|
|
35
|
+
const icon = container.querySelector("svg");
|
|
36
|
+
const errors: unknown[] = [];
|
|
37
|
+
const spy = vi.spyOn(console, "error").mockImplementation((...args) => errors.push(args));
|
|
38
|
+
host.touch = true;
|
|
39
|
+
let root: ReturnType<typeof hydrateRoot> | undefined;
|
|
40
|
+
try {
|
|
41
|
+
await act(async () => {
|
|
42
|
+
root = hydrateRoot(container, <Fixture />, {
|
|
43
|
+
onRecoverableError: (error) => errors.push(error),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
expect(container.querySelector("svg")).toBe(icon);
|
|
47
|
+
expect(icon?.getAttribute("width")).toBe(
|
|
48
|
+
String(sizeRecipeForToken("$4", { touch: true }).iconSize),
|
|
49
|
+
);
|
|
50
|
+
expect(
|
|
51
|
+
errors.filter((error) => /hydration|hydrated|didn't match/i.test(String(error))),
|
|
52
|
+
).toEqual([]);
|
|
53
|
+
} finally {
|
|
54
|
+
await act(async () => root?.unmount());
|
|
55
|
+
spy.mockRestore();
|
|
56
|
+
container.remove();
|
|
57
|
+
host.touch = false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
package/src/icons/themed.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useTouchSurface, sizeRecipeForToken } from "@multiplatform.one/theme";
|
|
2
2
|
import type { FC } from "react";
|
|
3
3
|
import { getVariable, useProps, useTheme } from "tamagui";
|
|
4
4
|
|
|
@@ -9,6 +9,7 @@ import { getVariable, useProps, useTheme } from "tamagui";
|
|
|
9
9
|
*/
|
|
10
10
|
export function themed<A extends FC>(Component: A) {
|
|
11
11
|
return ((props: any) => {
|
|
12
|
+
const touch = useTouchSurface();
|
|
12
13
|
const { color, disableTheme, size } = useProps(props);
|
|
13
14
|
const theme = useTheme();
|
|
14
15
|
const fromTheme =
|
|
@@ -19,11 +20,7 @@ export function themed<A extends FC>(Component: A) {
|
|
|
19
20
|
<Component
|
|
20
21
|
{...props}
|
|
21
22
|
color={colorValue}
|
|
22
|
-
size={
|
|
23
|
-
typeof size === "string"
|
|
24
|
-
? sizeRecipeForToken(size, { touch: isTouchSurface() }).iconSize
|
|
25
|
-
: size
|
|
26
|
-
}
|
|
23
|
+
size={typeof size === "string" ? sizeRecipeForToken(size, { touch }).iconSize : size}
|
|
27
24
|
/>
|
|
28
25
|
);
|
|
29
26
|
}) as A;
|
package/src/knobImmuneExports.ts
CHANGED
|
@@ -35,9 +35,9 @@ export const knobImmuneExports: Readonly<Record<string, string>> = {
|
|
|
35
35
|
ScrollView: "scroll container lego; no opinionated chrome, fragments ride the content",
|
|
36
36
|
// Separator left the registry 2026-08-28 (MPO-47): bare `Separator` is now
|
|
37
37
|
// a house implementation (./Separator — painted 1px line), so it no longer
|
|
38
|
-
// qualifies for a row (house implementations ARE the knob story).
|
|
39
|
-
// weight
|
|
40
|
-
//
|
|
38
|
+
// qualifies for a row (house implementations ARE the knob story). Its
|
|
39
|
+
// weight has no knob by ruling (MPO-21 G10-A, 2026-09-26), and Circle and
|
|
40
|
+
// Square stay knob-immune lego with no per-role radius (MPO-21 R-B).
|
|
41
41
|
// MPO-21 G1 — Fold's substrate. Logic shell only; Accordion (and Fold)
|
|
42
42
|
// own the painted chrome. Restored with the three below after MPO-21 R9 G8
|
|
43
43
|
// (the Adapt drop) rewrote this file from a pre-MPO-21 copy and took them
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { cleanup, screen } from "@testing-library/react";
|
|
1
|
+
import { cleanup, fireEvent, screen } from "@testing-library/react";
|
|
2
2
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
3
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import { Paragraph, YStack } from "tamagui";
|
|
5
5
|
import {
|
|
6
6
|
FeedLayout,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PaneScaffold,
|
|
9
9
|
ReadingWidth,
|
|
10
10
|
SupportingPaneLayout,
|
|
11
|
+
type PaneScaffoldProps,
|
|
11
12
|
} from "./PaneScaffold";
|
|
12
13
|
import * as Layouts from "./index";
|
|
13
14
|
|
|
@@ -277,3 +278,148 @@ describe("ReadingWidth", () => {
|
|
|
277
278
|
expect(el).toHaveStyle({ maxWidth: "70ch" });
|
|
278
279
|
});
|
|
279
280
|
});
|
|
281
|
+
|
|
282
|
+
// MPO-253: the seam runs on one axis. A vertical scaffold docks secondary
|
|
283
|
+
// under primary with the sash, clamps and fraction contract the side-by-side
|
|
284
|
+
// split already has, turned on its side.
|
|
285
|
+
describe("PaneScaffold orientation (MPO-253)", () => {
|
|
286
|
+
function renderSplit(props: Partial<PaneScaffoldProps> = {}) {
|
|
287
|
+
renderWithProviders(
|
|
288
|
+
<PaneScaffold
|
|
289
|
+
sizeClass="expanded"
|
|
290
|
+
primary={<Paragraph>Body</Paragraph>}
|
|
291
|
+
secondary={<Paragraph>Editor</Paragraph>}
|
|
292
|
+
{...props}
|
|
293
|
+
/>,
|
|
294
|
+
);
|
|
295
|
+
return screen.getByTestId("pane-sash");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function boundRow(width: number, height: number) {
|
|
299
|
+
const row = screen.getByTestId("pane-primary").parentElement as HTMLElement;
|
|
300
|
+
row.getBoundingClientRect = () =>
|
|
301
|
+
({
|
|
302
|
+
x: 0,
|
|
303
|
+
y: 0,
|
|
304
|
+
top: 0,
|
|
305
|
+
left: 0,
|
|
306
|
+
right: width,
|
|
307
|
+
bottom: height,
|
|
308
|
+
width,
|
|
309
|
+
height,
|
|
310
|
+
toJSON: () => ({}),
|
|
311
|
+
}) as DOMRect;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
it("docks secondary under primary behind a horizontal sash", () => {
|
|
315
|
+
const sash = renderSplit({ orientation: "vertical" });
|
|
316
|
+
const root = screen.getByTestId("pane-scaffold");
|
|
317
|
+
expect(root).toHaveAttribute("data-orientation", "vertical");
|
|
318
|
+
expect(root).toHaveAttribute("data-pane-layout", "split");
|
|
319
|
+
expect(root).toHaveAttribute("data-sash", "true");
|
|
320
|
+
expect(sash).toHaveAttribute("aria-orientation", "horizontal");
|
|
321
|
+
const primary = screen.getByTestId("pane-primary");
|
|
322
|
+
const secondary = screen.getByTestId("pane-secondary");
|
|
323
|
+
expect(
|
|
324
|
+
primary.compareDocumentPosition(secondary) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
325
|
+
).toBeTruthy();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("keeps the side-by-side sash vertical by default", () => {
|
|
329
|
+
const sash = renderSplit();
|
|
330
|
+
expect(screen.getByTestId("pane-scaffold")).toHaveAttribute("data-orientation", "horizontal");
|
|
331
|
+
expect(sash).toHaveAttribute("aria-orientation", "vertical");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it.each([
|
|
335
|
+
["horizontal", "ArrowRight", "ArrowLeft"],
|
|
336
|
+
["vertical", "ArrowDown", "ArrowUp"],
|
|
337
|
+
] as const)("%s: arrows step the fraction along its own axis", (orientation, forward, back) => {
|
|
338
|
+
const sash = renderSplit({ orientation });
|
|
339
|
+
expect(sash).toHaveAttribute("aria-valuenow", "50");
|
|
340
|
+
fireEvent.keyDown(sash, { key: forward });
|
|
341
|
+
expect(sash).toHaveAttribute("aria-valuenow", "52");
|
|
342
|
+
fireEvent.keyDown(sash, { key: forward, shiftKey: true });
|
|
343
|
+
expect(sash).toHaveAttribute("aria-valuenow", "62");
|
|
344
|
+
fireEvent.keyDown(sash, { key: back });
|
|
345
|
+
expect(sash).toHaveAttribute("aria-valuenow", "60");
|
|
346
|
+
fireEvent.keyDown(sash, { key: "Enter" });
|
|
347
|
+
expect(sash).toHaveAttribute("aria-valuenow", "50");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("vertical ignores the horizontal arrows", () => {
|
|
351
|
+
const sash = renderSplit({ orientation: "vertical" });
|
|
352
|
+
fireEvent.keyDown(sash, { key: "ArrowRight" });
|
|
353
|
+
fireEvent.keyDown(sash, { key: "ArrowLeft" });
|
|
354
|
+
expect(sash).toHaveAttribute("aria-valuenow", "50");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("vertical honours the clamps: the default is clamped, Home / End land on min / max", () => {
|
|
358
|
+
const sash = renderSplit({
|
|
359
|
+
orientation: "vertical",
|
|
360
|
+
paneFlex: [9, 1],
|
|
361
|
+
minPaneFraction: 0.3,
|
|
362
|
+
maxPaneFraction: 0.7,
|
|
363
|
+
});
|
|
364
|
+
expect(sash).toHaveAttribute("aria-valuemin", "30");
|
|
365
|
+
expect(sash).toHaveAttribute("aria-valuemax", "70");
|
|
366
|
+
expect(sash).toHaveAttribute("aria-valuenow", "70");
|
|
367
|
+
fireEvent.keyDown(sash, { key: "Home" });
|
|
368
|
+
expect(sash).toHaveAttribute("aria-valuenow", "30");
|
|
369
|
+
fireEvent.keyDown(sash, { key: "End" });
|
|
370
|
+
expect(sash).toHaveAttribute("aria-valuenow", "70");
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("vertical is controlled through primaryFraction / onPrimaryFractionChange", () => {
|
|
374
|
+
const onChange = vi.fn();
|
|
375
|
+
const sash = renderSplit({
|
|
376
|
+
orientation: "vertical",
|
|
377
|
+
primaryFraction: 0.4,
|
|
378
|
+
onPrimaryFractionChange: onChange,
|
|
379
|
+
});
|
|
380
|
+
expect(sash).toHaveAttribute("aria-valuenow", "40");
|
|
381
|
+
fireEvent.keyDown(sash, { key: "ArrowDown" });
|
|
382
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
383
|
+
expect(onChange.mock.calls[0][0]).toBeCloseTo(0.42);
|
|
384
|
+
expect(sash).toHaveAttribute("aria-valuenow", "40");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it.each([
|
|
388
|
+
["horizontal", { clientX: 200, clientY: 0 }, { clientX: 280, clientY: 0 }],
|
|
389
|
+
["vertical", { clientX: 0, clientY: 100 }, { clientX: 0, clientY: 140 }],
|
|
390
|
+
] as const)(
|
|
391
|
+
"%s: dragging the seam moves the fraction by the distance over the row",
|
|
392
|
+
(orientation, down, move) => {
|
|
393
|
+
const sash = renderSplit({ orientation });
|
|
394
|
+
boundRow(800, 400);
|
|
395
|
+
fireEvent.pointerDown(sash, { button: 0, ...down });
|
|
396
|
+
fireEvent.pointerMove(window, move);
|
|
397
|
+
expect(sash).toHaveAttribute("aria-valuenow", "60");
|
|
398
|
+
fireEvent.pointerUp(window, move);
|
|
399
|
+
fireEvent.pointerMove(window, { clientX: 0, clientY: 0 });
|
|
400
|
+
expect(sash).toHaveAttribute("aria-valuenow", "60");
|
|
401
|
+
},
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
it("vertical stacks like horizontal below the two-pane budget", () => {
|
|
405
|
+
renderWithProviders(
|
|
406
|
+
<PaneScaffold
|
|
407
|
+
sizeClass="compact"
|
|
408
|
+
orientation="vertical"
|
|
409
|
+
primary={<Paragraph>Body</Paragraph>}
|
|
410
|
+
secondary={<Paragraph>Editor</Paragraph>}
|
|
411
|
+
/>,
|
|
412
|
+
);
|
|
413
|
+
const root = screen.getByTestId("pane-scaffold");
|
|
414
|
+
expect(root).toHaveAttribute("data-pane-layout", "stack");
|
|
415
|
+
expect(root).toHaveAttribute("data-orientation", "vertical");
|
|
416
|
+
expect(screen.queryByTestId("pane-sash")).not.toBeInTheDocument();
|
|
417
|
+
expect(screen.getByText("Editor")).toBeInTheDocument();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("vertical keeps the seam on a narrow window when maxPanes ejects", () => {
|
|
421
|
+
const sash = renderSplit({ orientation: "vertical", sizeClass: "compact", maxPanes: 2 });
|
|
422
|
+
expect(screen.getByTestId("pane-scaffold")).toHaveAttribute("data-pane-layout", "split");
|
|
423
|
+
expect(sash).toHaveAttribute("aria-orientation", "horizontal");
|
|
424
|
+
});
|
|
425
|
+
});
|
|
@@ -110,7 +110,7 @@ const meta: Meta<typeof PaneScaffold> = {
|
|
|
110
110
|
docs: {
|
|
111
111
|
description: {
|
|
112
112
|
component:
|
|
113
|
-
'Adaptive TWO-pane shell (DG-LAY-01/02) — `primary` and `secondary`, no more. Size classes: compact <640, medium ≥640, expanded ≥860 (FloatingPanel), large ≥1024, xl ≥1280. Two panes from expanded up unless `maxPanes` ejects. Side-by-side uses a hairline sash (Linear / VS Code / Polaris Frame); pass `paneGap` to eject to spaced columns. Below expanded the panes stack and the sash goes away, so nothing is hidden on a phone; `narrowLayout="swap"` (what `activePane` opts into) is the list-detail exception. Three regions come from nesting a scaffold inside a pane — see the Nested three-region story; the size class rides down, so the inner scaffold stacks with the outer.',
|
|
113
|
+
'Adaptive TWO-pane shell (DG-LAY-01/02) — `primary` and `secondary`, no more. Size classes: compact <640, medium ≥640, expanded ≥860 (FloatingPanel), large ≥1024, xl ≥1280. Two panes from expanded up unless `maxPanes` ejects. Side-by-side uses a hairline sash (Linear / VS Code / Polaris Frame); pass `paneGap` to eject to spaced columns. Below expanded the panes stack and the sash goes away, so nothing is hidden on a phone; `narrowLayout="swap"` (what `activePane` opts into) is the list-detail exception. Three regions come from nesting a scaffold inside a pane — see the Nested three-region story; the size class rides down, so the inner scaffold stacks with the outer. `orientation="vertical"` docks `secondary` under `primary` with the same sash turned on its side (↑/↓, Home/End, double-press resets) — see the Docked editor story.',
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
},
|
|
@@ -119,6 +119,7 @@ const meta: Meta<typeof PaneScaffold> = {
|
|
|
119
119
|
control: "select",
|
|
120
120
|
options: ["compact", "medium", "expanded", "large", "xl"],
|
|
121
121
|
},
|
|
122
|
+
orientation: { control: "radio", options: ["horizontal", "vertical"] },
|
|
122
123
|
activePane: { control: "radio", options: ["primary", "secondary"] },
|
|
123
124
|
narrowLayout: { control: "radio", options: ["stack", "swap"] },
|
|
124
125
|
maxPanes: { control: "radio", options: [1, 2, undefined] },
|
|
@@ -202,6 +203,37 @@ export const NestedThreeRegion: Story = {
|
|
|
202
203
|
),
|
|
203
204
|
};
|
|
204
205
|
|
|
206
|
+
export const DockedEditor: Story = {
|
|
207
|
+
name: "Docked editor (vertical)",
|
|
208
|
+
args: { sizeClass: "xl", orientation: "vertical" },
|
|
209
|
+
parameters: {
|
|
210
|
+
docs: {
|
|
211
|
+
description: {
|
|
212
|
+
story:
|
|
213
|
+
'`orientation="vertical"` runs the seam across the page: the body above, an editor docked below, resized by dragging the seam (↑/↓ from the keyboard, Home/End for the clamps, double-press resets). The body here is itself a horizontal scaffold, which is how a strip editor sits under a multi-pane page. A vertical scaffold splits the height its parent gives it, so the frame is bounded.',
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
render: (args) => (
|
|
218
|
+
<YStack height={520} padding="$4">
|
|
219
|
+
<SplitFrame height={480}>
|
|
220
|
+
<PaneScaffold
|
|
221
|
+
{...args}
|
|
222
|
+
paneFlex={[3, 1]}
|
|
223
|
+
primary={
|
|
224
|
+
<PaneScaffold
|
|
225
|
+
paneFlex={[2, 1]}
|
|
226
|
+
primary={<PaneFill label="Page body" hint="Drag the seam below · ↑/↓ resize" />}
|
|
227
|
+
secondary={<PaneFill label="Inspector" muted />}
|
|
228
|
+
/>
|
|
229
|
+
}
|
|
230
|
+
secondary={<PaneFill label="Editor" hint="Docked under the page" muted />}
|
|
231
|
+
/>
|
|
232
|
+
</SplitFrame>
|
|
233
|
+
</YStack>
|
|
234
|
+
),
|
|
235
|
+
};
|
|
236
|
+
|
|
205
237
|
export const CompactActiveSecondary: Story = {
|
|
206
238
|
name: "Compact (swap)",
|
|
207
239
|
args: { sizeClass: "compact", activePane: "secondary" },
|