@multiplatform.one/components 7.28.1 → 7.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Accordion/index.cjs +26 -17
- package/dist/cjs/Accordion/index.native.js +36 -27
- package/dist/cjs/Accordion/index.native.js.map +1 -1
- package/dist/cjs/Heading.cjs +74 -30
- package/dist/cjs/Heading.native.js +81 -30
- package/dist/cjs/Heading.native.js.map +1 -1
- package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
- package/dist/cjs/ScrollView/index.native.js +4 -0
- package/dist/cjs/ScrollView/index.native.js.map +1 -1
- package/dist/cjs/SortableList/SortableHandle.cjs +1 -1
- package/dist/cjs/SortableList/SortableHandle.native.js +1 -1
- package/dist/cjs/Text.cjs +58 -21
- package/dist/cjs/Text.native.js +62 -21
- package/dist/cjs/Text.native.js.map +1 -1
- package/dist/cjs/Toast/index.native.js.map +1 -1
- package/dist/cjs/charts/ChartDataView.cjs +2 -1
- package/dist/cjs/charts/ChartDataView.native.js +2 -1
- package/dist/cjs/charts/ChartDataView.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.cjs +8 -8
- package/dist/cjs/code/CodeBlock/index.native.js +8 -8
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/layouts/EmptyState.cjs +4 -3
- package/dist/cjs/layouts/EmptyState.native.js +14 -9
- package/dist/cjs/layouts/EmptyState.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 +2 -1
- package/dist/cjs/layouts/sidebar.native.js +2 -1
- package/dist/cjs/layouts/sidebar.native.js.map +1 -1
- package/dist/cjs/layouts/tags.cjs +2 -1
- package/dist/cjs/layouts/tags.native.js +2 -1
- package/dist/cjs/layouts/tags.native.js.map +1 -1
- package/dist/cjs/layouts/toolbar.cjs +2 -1
- package/dist/cjs/layouts/toolbar.native.js +2 -1
- package/dist/cjs/layouts/toolbar.native.js.map +1 -1
- package/dist/cjs/shared/useReturnFocusOnClose.cjs +1 -0
- package/dist/cjs/shared/useReturnFocusOnClose.native.js +1 -0
- package/dist/cjs/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/cjs/surfaces.cjs +61 -0
- package/dist/cjs/surfaces.native.js +73 -0
- package/dist/cjs/surfaces.native.js.map +1 -1
- package/dist/cjs/views/Calendar.cjs +13 -10
- package/dist/cjs/views/Calendar.native.js +13 -10
- package/dist/cjs/views/Calendar.native.js.map +1 -1
- package/dist/cjs/views/Dashboard.cjs +2 -1
- package/dist/cjs/views/Dashboard.native.js +2 -1
- package/dist/cjs/views/Dashboard.native.js.map +1 -1
- package/dist/cjs/views/Gantt.cjs +39 -27
- package/dist/cjs/views/Gantt.native.js +38 -23
- package/dist/cjs/views/Gantt.native.js.map +1 -1
- package/dist/cjs/views/Kanban.cjs +2 -1
- package/dist/cjs/views/Kanban.native.js +1 -0
- package/dist/cjs/views/Kanban.native.js.map +1 -1
- package/dist/cjs/views/List.cjs +1 -0
- package/dist/cjs/views/List.native.js +1 -0
- package/dist/cjs/views/List.native.js.map +1 -1
- package/dist/cjs/views/Matrix.cjs +5 -3
- package/dist/cjs/views/Matrix.native.js +5 -3
- package/dist/cjs/views/Matrix.native.js.map +1 -1
- package/dist/cjs/views/ReportBuilder.cjs +2 -1
- package/dist/cjs/views/ReportBuilder.native.js +2 -1
- package/dist/cjs/views/ReportBuilder.native.js.map +1 -1
- package/dist/cjs/views/TreeView.cjs +2 -1
- package/dist/cjs/views/TreeView.native.js +2 -1
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- package/dist/esm/Accordion/index.mjs +26 -18
- package/dist/esm/Accordion/index.mjs.map +1 -1
- package/dist/esm/Accordion/index.native.js +36 -28
- package/dist/esm/Accordion/index.native.js.map +1 -1
- package/dist/esm/Heading.mjs +74 -30
- package/dist/esm/Heading.mjs.map +1 -1
- package/dist/esm/Heading.native.js +81 -30
- package/dist/esm/Heading.native.js.map +1 -1
- package/dist/esm/ProgressSteps/index.mjs.map +1 -1
- package/dist/esm/ProgressSteps/index.native.js.map +1 -1
- package/dist/esm/ScrollView/index.native.js +4 -0
- package/dist/esm/ScrollView/index.native.js.map +1 -1
- package/dist/esm/SortableList/SortableHandle.mjs +1 -1
- package/dist/esm/SortableList/SortableHandle.native.js +1 -1
- package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
- package/dist/esm/Text.mjs +58 -21
- package/dist/esm/Text.mjs.map +1 -1
- package/dist/esm/Text.native.js +62 -21
- package/dist/esm/Text.native.js.map +1 -1
- package/dist/esm/Toast/index.mjs.map +1 -1
- package/dist/esm/Toast/index.native.js.map +1 -1
- package/dist/esm/charts/ChartDataView.mjs +2 -1
- package/dist/esm/charts/ChartDataView.mjs.map +1 -1
- package/dist/esm/charts/ChartDataView.native.js +2 -1
- package/dist/esm/charts/ChartDataView.native.js.map +1 -1
- package/dist/esm/code/CodeBlock/index.mjs +1 -1
- package/dist/esm/code/CodeBlock/index.native.js +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/layouts/EmptyState.mjs +4 -3
- package/dist/esm/layouts/EmptyState.mjs.map +1 -1
- package/dist/esm/layouts/EmptyState.native.js +14 -9
- package/dist/esm/layouts/EmptyState.native.js.map +1 -1
- package/dist/esm/layouts/page.mjs +2 -1
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +2 -1
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/esm/layouts/sidebar.mjs +2 -1
- package/dist/esm/layouts/sidebar.mjs.map +1 -1
- package/dist/esm/layouts/sidebar.native.js +2 -1
- package/dist/esm/layouts/sidebar.native.js.map +1 -1
- package/dist/esm/layouts/tags.mjs +2 -1
- package/dist/esm/layouts/tags.mjs.map +1 -1
- package/dist/esm/layouts/tags.native.js +2 -1
- package/dist/esm/layouts/tags.native.js.map +1 -1
- package/dist/esm/layouts/toolbar.mjs +2 -1
- package/dist/esm/layouts/toolbar.mjs.map +1 -1
- package/dist/esm/layouts/toolbar.native.js +2 -1
- package/dist/esm/layouts/toolbar.native.js.map +1 -1
- package/dist/esm/shared/useReturnFocusOnClose.mjs +1 -0
- package/dist/esm/shared/useReturnFocusOnClose.mjs.map +1 -1
- package/dist/esm/shared/useReturnFocusOnClose.native.js +1 -0
- package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/esm/surfaces.mjs +63 -3
- package/dist/esm/surfaces.mjs.map +1 -1
- package/dist/esm/surfaces.native.js +75 -3
- package/dist/esm/surfaces.native.js.map +1 -1
- package/dist/esm/views/Calendar.mjs +12 -9
- package/dist/esm/views/Calendar.mjs.map +1 -1
- package/dist/esm/views/Calendar.native.js +12 -9
- package/dist/esm/views/Calendar.native.js.map +1 -1
- package/dist/esm/views/Dashboard.mjs +2 -1
- package/dist/esm/views/Dashboard.mjs.map +1 -1
- package/dist/esm/views/Dashboard.native.js +2 -1
- package/dist/esm/views/Dashboard.native.js.map +1 -1
- package/dist/esm/views/Gantt.mjs +37 -25
- package/dist/esm/views/Gantt.mjs.map +1 -1
- package/dist/esm/views/Gantt.native.js +36 -21
- package/dist/esm/views/Gantt.native.js.map +1 -1
- package/dist/esm/views/Kanban.mjs +2 -1
- package/dist/esm/views/Kanban.mjs.map +1 -1
- package/dist/esm/views/Kanban.native.js +1 -0
- package/dist/esm/views/Kanban.native.js.map +1 -1
- package/dist/esm/views/List.mjs +1 -0
- package/dist/esm/views/List.mjs.map +1 -1
- package/dist/esm/views/List.native.js +1 -0
- package/dist/esm/views/List.native.js.map +1 -1
- package/dist/esm/views/Matrix.mjs +3 -1
- package/dist/esm/views/Matrix.mjs.map +1 -1
- package/dist/esm/views/Matrix.native.js +3 -1
- package/dist/esm/views/Matrix.native.js.map +1 -1
- package/dist/esm/views/ReportBuilder.mjs +2 -1
- package/dist/esm/views/ReportBuilder.mjs.map +1 -1
- package/dist/esm/views/ReportBuilder.native.js +2 -1
- package/dist/esm/views/ReportBuilder.native.js.map +1 -1
- package/dist/esm/views/TreeView.mjs +2 -1
- package/dist/esm/views/TreeView.mjs.map +1 -1
- package/dist/esm/views/TreeView.native.js +2 -1
- package/dist/esm/views/TreeView.native.js.map +1 -1
- package/dist/jsx/Accordion/index.mjs +26 -18
- package/dist/jsx/Accordion/index.mjs.map +1 -1
- package/dist/jsx/Accordion/index.native.js +36 -27
- package/dist/jsx/Accordion/index.native.js.map +1 -1
- package/dist/jsx/Heading.mjs +74 -30
- package/dist/jsx/Heading.mjs.map +1 -1
- package/dist/jsx/Heading.native.js +81 -30
- package/dist/jsx/Heading.native.js.map +1 -1
- package/dist/jsx/ProgressSteps/index.mjs.map +1 -1
- package/dist/jsx/ProgressSteps/index.native.js.map +1 -1
- package/dist/jsx/ScrollView/index.native.js +4 -0
- package/dist/jsx/ScrollView/index.native.js.map +1 -1
- package/dist/jsx/SortableList/SortableHandle.mjs +1 -1
- package/dist/jsx/SortableList/SortableHandle.native.js +1 -1
- package/dist/jsx/Text.mjs +58 -21
- package/dist/jsx/Text.mjs.map +1 -1
- package/dist/jsx/Text.native.js +62 -21
- package/dist/jsx/Text.native.js.map +1 -1
- package/dist/jsx/Toast/index.mjs.map +1 -1
- package/dist/jsx/Toast/index.native.js.map +1 -1
- package/dist/jsx/charts/ChartDataView.mjs +2 -1
- package/dist/jsx/charts/ChartDataView.mjs.map +1 -1
- package/dist/jsx/charts/ChartDataView.native.js +2 -1
- package/dist/jsx/charts/ChartDataView.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.mjs +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +8 -8
- package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
- package/dist/jsx/index.js +2 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +1 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/layouts/EmptyState.mjs +4 -3
- package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
- package/dist/jsx/layouts/EmptyState.native.js +14 -9
- package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
- package/dist/jsx/layouts/page.mjs +2 -1
- 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 -1
- package/dist/jsx/layouts/sidebar.mjs.map +1 -1
- package/dist/jsx/layouts/sidebar.native.js +2 -1
- package/dist/jsx/layouts/sidebar.native.js.map +1 -1
- package/dist/jsx/layouts/tags.mjs +2 -1
- package/dist/jsx/layouts/tags.mjs.map +1 -1
- package/dist/jsx/layouts/tags.native.js +2 -1
- package/dist/jsx/layouts/tags.native.js.map +1 -1
- package/dist/jsx/layouts/toolbar.mjs +2 -1
- package/dist/jsx/layouts/toolbar.mjs.map +1 -1
- package/dist/jsx/layouts/toolbar.native.js +2 -1
- package/dist/jsx/layouts/toolbar.native.js.map +1 -1
- package/dist/jsx/shared/useReturnFocusOnClose.mjs +1 -0
- package/dist/jsx/shared/useReturnFocusOnClose.mjs.map +1 -1
- package/dist/jsx/shared/useReturnFocusOnClose.native.js +1 -0
- package/dist/jsx/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/jsx/surfaces.mjs +63 -3
- package/dist/jsx/surfaces.mjs.map +1 -1
- package/dist/jsx/surfaces.native.js +73 -0
- package/dist/jsx/surfaces.native.js.map +1 -1
- package/dist/jsx/views/Calendar.mjs +12 -9
- package/dist/jsx/views/Calendar.mjs.map +1 -1
- package/dist/jsx/views/Calendar.native.js +13 -10
- package/dist/jsx/views/Calendar.native.js.map +1 -1
- package/dist/jsx/views/Dashboard.mjs +2 -1
- package/dist/jsx/views/Dashboard.mjs.map +1 -1
- package/dist/jsx/views/Dashboard.native.js +2 -1
- package/dist/jsx/views/Dashboard.native.js.map +1 -1
- package/dist/jsx/views/Gantt.mjs +37 -25
- package/dist/jsx/views/Gantt.mjs.map +1 -1
- package/dist/jsx/views/Gantt.native.js +38 -23
- package/dist/jsx/views/Gantt.native.js.map +1 -1
- package/dist/jsx/views/Kanban.mjs +2 -1
- package/dist/jsx/views/Kanban.mjs.map +1 -1
- package/dist/jsx/views/Kanban.native.js +1 -0
- package/dist/jsx/views/Kanban.native.js.map +1 -1
- package/dist/jsx/views/List.mjs +1 -0
- package/dist/jsx/views/List.mjs.map +1 -1
- package/dist/jsx/views/List.native.js +1 -0
- package/dist/jsx/views/List.native.js.map +1 -1
- package/dist/jsx/views/Matrix.mjs +3 -1
- package/dist/jsx/views/Matrix.mjs.map +1 -1
- package/dist/jsx/views/Matrix.native.js +5 -3
- package/dist/jsx/views/Matrix.native.js.map +1 -1
- package/dist/jsx/views/ReportBuilder.mjs +2 -1
- package/dist/jsx/views/ReportBuilder.mjs.map +1 -1
- package/dist/jsx/views/ReportBuilder.native.js +2 -1
- package/dist/jsx/views/ReportBuilder.native.js.map +1 -1
- package/dist/jsx/views/TreeView.mjs +2 -1
- package/dist/jsx/views/TreeView.mjs.map +1 -1
- package/dist/jsx/views/TreeView.native.js +2 -1
- package/dist/jsx/views/TreeView.native.js.map +1 -1
- package/package.json +9 -9
- package/src/Accordion/index.tsx +34 -15
- package/src/Heading.stories.tsx +13 -0
- package/src/Heading.tsx +62 -21
- package/src/ProgressSteps/ProgressSteps.spec.tsx +2 -3
- package/src/ProgressSteps/index.tsx +3 -4
- package/src/ScrollView/ScrollView.native.spec.tsx +97 -1
- package/src/ScrollView/index.native.tsx +4 -0
- package/src/SortableList/SortableHandle.tsx +2 -2
- package/src/Tabs/Tabs.lifecycle.spec.tsx +3 -1
- package/src/Text.tsx +32 -20
- package/src/Toast/index.tsx +4 -3
- package/src/charts/ChartDataView.tsx +2 -1
- package/src/code/CodeBlock/index.tsx +1 -1
- package/src/feedback/ConfirmDialog.spec.tsx +2 -2
- package/src/icons/phosphorTokenColor.spec.tsx +86 -0
- package/src/index.ts +1 -1
- package/src/layouts/EmptyState.spec.tsx +17 -0
- package/src/layouts/EmptyState.tsx +10 -3
- package/src/layouts/Screen.keyboardTaps.spec.tsx +57 -0
- package/src/layouts/Screen.stories.tsx +55 -2
- package/src/layouts/page.tsx +2 -14
- package/src/layouts/sidebar.tsx +2 -1
- package/src/layouts/tags.tsx +1 -1
- package/src/layouts/toolbar.tsx +2 -1
- package/src/shared/useReturnFocusOnClose.spec.tsx +39 -0
- package/src/shared/useReturnFocusOnClose.ts +4 -0
- package/src/surfaces.spec.tsx +272 -3
- package/src/surfaces.tsx +97 -4
- package/src/textWeight.houseFonts.spec.tsx +127 -0
- package/src/textWeight.native.spec.tsx +27 -0
- package/src/textWeight.spec.tsx +15 -0
- package/src/views/Calendar.tsx +37 -29
- package/src/views/Dashboard.tsx +2 -13
- package/src/views/Gantt.scroll.spec.tsx +4 -1
- package/src/views/Gantt.tsx +40 -25
- package/src/views/Kanban.native.tsx +1 -0
- package/src/views/Kanban.tsx +2 -10
- package/src/views/List.tsx +1 -0
- package/src/views/Matrix.tsx +3 -1
- package/src/views/ReportBuilder.tsx +2 -1
- package/src/views/TreeView.tsx +2 -1
- package/src/views/views.spec.tsx +14 -2
- package/types/Accordion/index.d.ts +8 -0
- package/types/Accordion/index.d.ts.map +1 -1
- package/types/Chip/index.d.ts +1 -1
- package/types/Heading.d.ts +7 -7
- package/types/Heading.d.ts.map +1 -1
- package/types/ProgressSteps/index.d.ts.map +1 -1
- package/types/ScrollView/index.native.d.ts.map +1 -1
- package/types/SortableList/SortableHandle.d.ts +1 -1
- package/types/Tabs/index.d.ts +1 -1
- package/types/Text.d.ts +4 -4
- package/types/Text.d.ts.map +1 -1
- package/types/Toast/index.d.ts.map +1 -1
- package/types/charts/ChartDataView.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/layouts/EmptyState.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/layouts/toolbar.d.ts.map +1 -1
- package/types/shared/useReturnFocusOnClose.d.ts.map +1 -1
- package/types/surfaces.d.ts +14 -0
- package/types/surfaces.d.ts.map +1 -1
- package/types/views/Calendar.d.ts +18 -1
- package/types/views/Calendar.d.ts.map +1 -1
- package/types/views/Dashboard.d.ts.map +1 -1
- package/types/views/Gantt.d.ts.map +1 -1
- package/types/views/Kanban.d.ts.map +1 -1
- package/types/views/List.d.ts.map +1 -1
- package/types/views/Matrix.d.ts.map +1 -1
- package/types/views/ReportBuilder.d.ts.map +1 -1
- package/types/views/TreeView.d.ts.map +1 -1
package/src/surfaces.tsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Preset, Surface, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import { createContext, forwardRef, useContext } from "react";
|
|
3
|
+
import { createContext, forwardRef, useContext, useEffect } from "react";
|
|
4
4
|
import {
|
|
5
5
|
AlertDialog,
|
|
6
6
|
Dialog,
|
|
7
|
+
isWeb,
|
|
7
8
|
Popover,
|
|
8
9
|
Sheet,
|
|
10
|
+
useDialogContext,
|
|
9
11
|
View,
|
|
10
12
|
type DialogContentProps as TamaguiDialogContentProps,
|
|
11
13
|
type DialogOverlayProps as TamaguiDialogOverlayProps,
|
|
@@ -56,6 +58,86 @@ const overlayFrameFocusStyle = {
|
|
|
56
58
|
outlineStyle: "none" as any,
|
|
57
59
|
} as const;
|
|
58
60
|
|
|
61
|
+
// ── Overlay open focus (MPO-452, MPO-466) ────────────────────────────────
|
|
62
|
+
// Tamagui 2.7's FocusScope decides "focus is not inside yet", waits for
|
|
63
|
+
// idle (up to 200 ms), then focuses the first tabbable that does not
|
|
64
|
+
// already hold focus: a click into a field during that wait had focus
|
|
65
|
+
// moved to another control, so the typing that followed went there. The
|
|
66
|
+
// house handler checks and focuses in one step. With no tabbable yet,
|
|
67
|
+
// tamagui's deferred path still runs. Dialog content sits in a native
|
|
68
|
+
// <dialog> whose show() puts focus inside before the check in browsers,
|
|
69
|
+
// so there FocusScope never calls the handler; useDialogShowFocus does
|
|
70
|
+
// when show() left focus on something that is not a tab stop (MPO-470).
|
|
71
|
+
|
|
72
|
+
function isTabbableWithin(node: HTMLElement, container: HTMLElement): boolean {
|
|
73
|
+
if (node.tabIndex < 0 || node.hidden || node.tagName === "A") return false;
|
|
74
|
+
if ((node as HTMLInputElement).disabled) return false;
|
|
75
|
+
if (node instanceof HTMLInputElement && node.type === "hidden") return false;
|
|
76
|
+
if (getComputedStyle(node).visibility === "hidden") return false;
|
|
77
|
+
for (let el: HTMLElement | null = node; el && el !== container; el = el.parentElement) {
|
|
78
|
+
if (getComputedStyle(el).display === "none") return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function focusFirstTabbableOnOpen(event: Event): void {
|
|
84
|
+
const container = event.currentTarget;
|
|
85
|
+
if (typeof HTMLElement === "undefined" || !(container instanceof HTMLElement)) return;
|
|
86
|
+
const first = Array.from(container.querySelectorAll<HTMLElement>("*")).find((node) =>
|
|
87
|
+
isTabbableWithin(node, container),
|
|
88
|
+
);
|
|
89
|
+
if (!first) return;
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
first.focus({ preventScroll: true });
|
|
92
|
+
if (first instanceof HTMLInputElement) first.select();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type OpenAutoFocus = (event: Event) => void;
|
|
96
|
+
|
|
97
|
+
/** The caller's handler runs first; its `preventDefault()` skips the house open focus. */
|
|
98
|
+
function withHouseOpenFocus(onOpenAutoFocus?: OpenAutoFocus): OpenAutoFocus {
|
|
99
|
+
return (event) => {
|
|
100
|
+
onOpenAutoFocus?.(event);
|
|
101
|
+
if (!event.defaultPrevented) focusFirstTabbableOnOpen(event);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const DIALOG_SHOW_FOCUS_EVENT = "mp.dialogShowFocus";
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Chromium and WebKit's `<dialog>.show()` focuses the first focusable
|
|
109
|
+
* descendant, so a tabIndex -1 frame (the house Input's group frame, a
|
|
110
|
+
* tabIndex -1 panel) takes focus; Firefox and the HTML spec take the first
|
|
111
|
+
* tab stop. When focus sits on something that is not a tab stop, run
|
|
112
|
+
* `onOpenAutoFocus` against the content the way FocusScope would. The
|
|
113
|
+
* content is found by its id because FocusScope clones it with its own ref,
|
|
114
|
+
* so a ref passed to `Dialog.Content` never attaches on web. Passive effects
|
|
115
|
+
* run after the portal's layout-effect `show()`. Mirror of the forms hook.
|
|
116
|
+
*/
|
|
117
|
+
function useDialogShowFocus(onOpenAutoFocus: OpenAutoFocus): void {
|
|
118
|
+
const { open, contentId } = useDialogContext();
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (!open || !contentId || !isWeb || typeof document === "undefined") return;
|
|
121
|
+
const content = document.getElementById(contentId);
|
|
122
|
+
const focused = document.activeElement;
|
|
123
|
+
if (!content || !(focused instanceof HTMLElement)) return;
|
|
124
|
+
if (!content.contains(focused) || focused.tabIndex >= 0) return;
|
|
125
|
+
content.addEventListener(DIALOG_SHOW_FOCUS_EVENT, onOpenAutoFocus, { once: true });
|
|
126
|
+
content.dispatchEvent(new CustomEvent(DIALOG_SHOW_FOCUS_EVENT, { cancelable: true }));
|
|
127
|
+
}, [open, contentId]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The house open focus for a raw `Dialog.Content`, which `DialogContent` and
|
|
132
|
+
* `AlertDialogContent` already run: render it inside the content. When
|
|
133
|
+
* `<dialog>.show()` left focus off a tab stop, the caller's `onOpenAutoFocus`
|
|
134
|
+
* runs, then the first tabbable takes focus (MPO-470).
|
|
135
|
+
*/
|
|
136
|
+
export function DialogShowFocus({ onOpenAutoFocus }: { onOpenAutoFocus?: OpenAutoFocus }): null {
|
|
137
|
+
useDialogShowFocus(withHouseOpenFocus(onOpenAutoFocus));
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
59
141
|
// Card frame lives in ./Card (NestedScale only; no Tint — R12); the parts
|
|
60
142
|
// live in ./CardHeader and ./CardFooter and are re-exported through ./Card.
|
|
61
143
|
|
|
@@ -105,8 +187,10 @@ export const DialogContent = forwardRef<
|
|
|
105
187
|
const DialogContentInner = forwardRef<
|
|
106
188
|
TamaguiElement,
|
|
107
189
|
TamaguiDialogContentProps & { children?: ReactNode }
|
|
108
|
-
>(function DialogContentInner({ children, ...props }, ref) {
|
|
190
|
+
>(function DialogContentInner({ children, onOpenAutoFocus, ...props }, ref) {
|
|
109
191
|
const { knobProps } = useResolvedKnobs({ component: "DialogContent" });
|
|
192
|
+
const openFocus = withHouseOpenFocus(onOpenAutoFocus);
|
|
193
|
+
useDialogShowFocus(openFocus);
|
|
110
194
|
// ThemeableStack unstyled:false defaults `elevate: true`, which paints
|
|
111
195
|
// getElevation(size) — `$true` is a control stop. `elevate={false}` leaves
|
|
112
196
|
// the overlay ladder in `elevatedSurface` as the only shadow (MPO-99).
|
|
@@ -126,6 +210,7 @@ const DialogContentInner = forwardRef<
|
|
|
126
210
|
{...overlayFrameFocus}
|
|
127
211
|
focusStyle={overlayFrameFocusStyle}
|
|
128
212
|
focusVisibleStyle={overlayFrameFocusStyle}
|
|
213
|
+
onOpenAutoFocus={openFocus}
|
|
129
214
|
{...props}
|
|
130
215
|
>
|
|
131
216
|
<DialogRegionContext.Provider value={IN_DIALOG_REGION}>
|
|
@@ -190,6 +275,7 @@ const AlertDialogContentInner = forwardRef<
|
|
|
190
275
|
TamaguiDialogContentProps & { children?: ReactNode }
|
|
191
276
|
>(function AlertDialogContentInner({ children, ...props }, ref) {
|
|
192
277
|
const { knobProps } = useResolvedKnobs({ component: "AlertDialogContent" });
|
|
278
|
+
useDialogShowFocus(withHouseOpenFocus(props.onOpenAutoFocus));
|
|
193
279
|
return (
|
|
194
280
|
<AlertDialog.Content
|
|
195
281
|
ref={ref}
|
|
@@ -242,8 +328,14 @@ export const AlertDialogOverlay = forwardRef<
|
|
|
242
328
|
// containerRadius cap spread over it; children must not draw their own
|
|
243
329
|
// outer border.
|
|
244
330
|
|
|
245
|
-
|
|
246
|
-
|
|
331
|
+
type PopoverContentProps = ViewProps & {
|
|
332
|
+
children?: ReactNode;
|
|
333
|
+
/** Runs first; `preventDefault()` skips the house open focus. */
|
|
334
|
+
onOpenAutoFocus?: OpenAutoFocus;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export const PopoverContent = forwardRef<TamaguiElement, PopoverContentProps>(
|
|
338
|
+
function PopoverContent({ children, onOpenAutoFocus, ...props }, ref) {
|
|
247
339
|
const { knobProps } = useResolvedKnobs({ component: "PopoverContent" });
|
|
248
340
|
return (
|
|
249
341
|
<Popover.Content
|
|
@@ -256,6 +348,7 @@ export const PopoverContent = forwardRef<TamaguiElement, ViewProps & { children?
|
|
|
256
348
|
{...overlayFrameFocus}
|
|
257
349
|
focusStyle={overlayFrameFocusStyle}
|
|
258
350
|
focusVisibleStyle={overlayFrameFocusStyle}
|
|
351
|
+
onOpenAutoFocus={withHouseOpenFocus(onOpenAutoFocus)}
|
|
259
352
|
{...props}
|
|
260
353
|
>
|
|
261
354
|
<NestedScale density={knobProps.density}>{children}</NestedScale>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-446 on the HOUSE font tables, read as computed weight on the text node.
|
|
3
|
+
*
|
|
4
|
+
* The class-identity matrix (../tests/textWeightMatrix.tsx) runs on the stock
|
|
5
|
+
* test config, which registers no `$serif`, so a family swap to serif there
|
|
6
|
+
* resolves no font at all and cannot show the defect the portal did: a Band
|
|
7
|
+
* H2 at Inter 600 falling to Georgia 400 under `headingFont: serif`. Here
|
|
8
|
+
* the provider carries `createDefaultThemeConfig()`, the tables the live
|
|
9
|
+
* apps ship (Inter heading ramp 600/700/800, category fonts 400 at every
|
|
10
|
+
* step), and every assertion is a `getComputedStyle` value.
|
|
11
|
+
*/
|
|
12
|
+
import { cleanup, render } from "@testing-library/react";
|
|
13
|
+
import { createDefaultThemeConfig, Preset } from "@multiplatform.one/theme";
|
|
14
|
+
import type { ReactElement } from "react";
|
|
15
|
+
import { TamaguiProvider } from "tamagui";
|
|
16
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
17
|
+
import { H1, H2, Heading } from "./Heading";
|
|
18
|
+
import { Paragraph, SizableText, Text } from "./Text";
|
|
19
|
+
|
|
20
|
+
const themeConfig = createDefaultThemeConfig();
|
|
21
|
+
const LABEL = "Sign in to see your clusters";
|
|
22
|
+
|
|
23
|
+
afterEach(cleanup);
|
|
24
|
+
|
|
25
|
+
function textNodeHost(root: HTMLElement): HTMLElement {
|
|
26
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
27
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
28
|
+
if (node.textContent?.trim() === LABEL && node.parentElement) return node.parentElement;
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`no text node "${LABEL}"`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function measure(node: ReactElement, scheme: "light" | "dark") {
|
|
34
|
+
const { container } = render(
|
|
35
|
+
<TamaguiProvider config={themeConfig.tamagui} defaultTheme={scheme}>
|
|
36
|
+
{node}
|
|
37
|
+
</TamaguiProvider>,
|
|
38
|
+
);
|
|
39
|
+
const cs = getComputedStyle(textNodeHost(container));
|
|
40
|
+
const weight = cs.fontWeight.trim();
|
|
41
|
+
const family = cs.fontFamily.split(",")[0]?.replace(/['"]/g, "").trim() ?? "";
|
|
42
|
+
cleanup();
|
|
43
|
+
if (!weight) throw new Error("no computed font-weight on the text node");
|
|
44
|
+
if (!family) throw new Error("no computed font-family on the text node");
|
|
45
|
+
return { weight, family };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const schemes = ["light", "dark"] as const;
|
|
49
|
+
const headingSizes = ["$1", "$4", "$6", "$9"] as const;
|
|
50
|
+
const knobWeights = [
|
|
51
|
+
["regular", "400"],
|
|
52
|
+
["bold", "700"],
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
describe("house headings keep their weight across headingFont (MPO-446, LC-11)", () => {
|
|
56
|
+
for (const scheme of schemes) {
|
|
57
|
+
for (const [name, Voice] of [
|
|
58
|
+
["Heading", Heading],
|
|
59
|
+
["H1", H1],
|
|
60
|
+
["H2", H2],
|
|
61
|
+
] as const) {
|
|
62
|
+
it(`${scheme}: ${name} weight is the knob's at every size, sans-serif and serif alike`, () => {
|
|
63
|
+
for (const [fontWeight, expected] of knobWeights) {
|
|
64
|
+
for (const size of headingSizes) {
|
|
65
|
+
const at = (headingFont: "sans-serif" | "serif") =>
|
|
66
|
+
measure(
|
|
67
|
+
<Preset overrides={{ fontWeight, headingFont }}>
|
|
68
|
+
<Voice size={size}>{LABEL}</Voice>
|
|
69
|
+
</Preset>,
|
|
70
|
+
scheme,
|
|
71
|
+
);
|
|
72
|
+
const sans = at("sans-serif");
|
|
73
|
+
const serif = at("serif");
|
|
74
|
+
expect(sans.family).toBe("Inter");
|
|
75
|
+
expect(serif.family).toBe("Georgia");
|
|
76
|
+
expect({ size, fontWeight, sans: sans.weight, serif: serif.weight }).toEqual({
|
|
77
|
+
size,
|
|
78
|
+
fontWeight,
|
|
79
|
+
sans: expected,
|
|
80
|
+
serif: expected,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
it(`${scheme}: the portal Band heading keeps its explicit 700 under serif`, () => {
|
|
88
|
+
for (const [fontWeight] of knobWeights) {
|
|
89
|
+
for (const headingFont of ["sans-serif", "serif"] as const) {
|
|
90
|
+
const { weight } = measure(
|
|
91
|
+
<Preset overrides={{ fontWeight, headingFont }}>
|
|
92
|
+
<H2 margin={0} size="$1" fontWeight="700" color="$color12">
|
|
93
|
+
{LABEL}
|
|
94
|
+
</H2>
|
|
95
|
+
</Preset>,
|
|
96
|
+
scheme,
|
|
97
|
+
);
|
|
98
|
+
expect({ fontWeight, headingFont, weight }).toEqual({
|
|
99
|
+
fontWeight,
|
|
100
|
+
headingFont,
|
|
101
|
+
weight: "700",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("sized body voices take fontWeight and bodyFont together (MPO-446)", () => {
|
|
110
|
+
for (const scheme of schemes) {
|
|
111
|
+
for (const [name, Voice] of [
|
|
112
|
+
["Text", Text],
|
|
113
|
+
["SizableText", SizableText],
|
|
114
|
+
["Paragraph", Paragraph],
|
|
115
|
+
] as const) {
|
|
116
|
+
it(`${scheme}: ${name} size $3 is 700 Georgia under bold + serif`, () => {
|
|
117
|
+
const m = measure(
|
|
118
|
+
<Preset overrides={{ fontWeight: "bold", bodyFont: "serif" }}>
|
|
119
|
+
<Voice size="$3">{LABEL}</Voice>
|
|
120
|
+
</Preset>,
|
|
121
|
+
scheme,
|
|
122
|
+
);
|
|
123
|
+
expect(m).toEqual({ weight: "700", family: "Georgia" });
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-446 native arm: the same matrix as ./textWeight.spec.tsx under the
|
|
3
|
+
* native platform flags. A file-scoped `vi.mock` is one module graph (see
|
|
4
|
+
* ./Heading.native.spec.tsx for why that matters); the props channel is the
|
|
5
|
+
* only channel this arm can carry the knob on, which is the one native has.
|
|
6
|
+
*/
|
|
7
|
+
import { cleanup } from "@testing-library/react";
|
|
8
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
9
|
+
import { registerWeightMatrix } from "../tests/textWeightMatrix";
|
|
10
|
+
|
|
11
|
+
vi.mock("@multiplatform.one/platform", async (importOriginal) => ({
|
|
12
|
+
...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
|
|
13
|
+
isWeb: false,
|
|
14
|
+
isNative: true,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
afterEach(cleanup);
|
|
18
|
+
|
|
19
|
+
describe("house typography — fontWeight x size x family (native, MPO-446)", () => {
|
|
20
|
+
it("really is running with the native platform flags", async () => {
|
|
21
|
+
const platform = await import("@multiplatform.one/platform");
|
|
22
|
+
expect(platform.isWeb).toBe(false);
|
|
23
|
+
expect(platform.isNative).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
registerWeightMatrix();
|
|
27
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPO-446 web arm: the fontWeight knob reaches sized house text. The matrix
|
|
3
|
+
* and the reason a size prop used to block the knob live in
|
|
4
|
+
* ../tests/textWeightMatrix.tsx; ./textWeight.native.spec.tsx runs the same table
|
|
5
|
+
* with the native platform flags.
|
|
6
|
+
*/
|
|
7
|
+
import { afterEach, describe } from "vitest";
|
|
8
|
+
import { cleanup } from "@testing-library/react";
|
|
9
|
+
import { registerWeightMatrix } from "../tests/textWeightMatrix";
|
|
10
|
+
|
|
11
|
+
afterEach(cleanup);
|
|
12
|
+
|
|
13
|
+
describe("house typography — fontWeight x size x family (web, MPO-446)", () => {
|
|
14
|
+
registerWeightMatrix();
|
|
15
|
+
});
|
package/src/views/Calendar.tsx
CHANGED
|
@@ -39,16 +39,8 @@ import {
|
|
|
39
39
|
import React, { useState, useMemo, useCallback, useEffect, useId, useRef } from "react";
|
|
40
40
|
import { useTranslation } from "../shared/i18n";
|
|
41
41
|
import { Button, FloatingPanel, Input, zIndex } from "@multiplatform.one/forms";
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
ScrollView,
|
|
45
|
-
Text,
|
|
46
|
-
XStack,
|
|
47
|
-
YStack,
|
|
48
|
-
isWeb,
|
|
49
|
-
styled,
|
|
50
|
-
type YStackProps,
|
|
51
|
-
} from "tamagui";
|
|
42
|
+
import { AnimatePresence, Text, XStack, YStack, isWeb, styled, type YStackProps } from "tamagui";
|
|
43
|
+
import { ScrollView } from "../ScrollView";
|
|
52
44
|
import { useDirection } from "../hooks/useDirection";
|
|
53
45
|
|
|
54
46
|
/**
|
|
@@ -89,18 +81,6 @@ export interface CalendarEventDraft {
|
|
|
89
81
|
end: Date;
|
|
90
82
|
}
|
|
91
83
|
|
|
92
|
-
/**
|
|
93
|
-
* Readable chip text for an arbitrary consumer-supplied event color.
|
|
94
|
-
* White text on mid-tone brand hexes (#10b981, #f59e0b) measures 2-3.7:1 —
|
|
95
|
-
* pick black/white by WCAG relative luminance instead. Theme tokens fall
|
|
96
|
-
* back to $accentColor, the guaranteed pair for $accentBackground.
|
|
97
|
-
*
|
|
98
|
-
* The dark candidate is pure black: the old #1a1a1a candidate was scored
|
|
99
|
-
* against ideal black (L=0) but rendered at L=0.0103, overstating its own
|
|
100
|
-
* contrast — measured 4.11:1 on #8B5CF6 while claiming 4.96 (Axiom 15:
|
|
101
|
-
* foreground on fills is luminance-COMPUTED, and the computation must match
|
|
102
|
-
* the rendered pixels). Black keeps every sanctioned step-9 solid ≥ 4.5:1.
|
|
103
|
-
*/
|
|
104
84
|
/**
|
|
105
85
|
* Inline-END hairline rule (P-16 RTL mirroring): grid column separators sit
|
|
106
86
|
* on the logical end edge so the grid mirrors with the writing direction.
|
|
@@ -132,8 +112,26 @@ function gridCellEdges(end: boolean, bottom: boolean) {
|
|
|
132
112
|
};
|
|
133
113
|
}
|
|
134
114
|
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Readable chip text for an event's fill.
|
|
117
|
+
* White text on mid-tone brand hexes (#10b981, #f59e0b) measures 2-3.7:1 —
|
|
118
|
+
* pick black/white by WCAG relative luminance instead.
|
|
119
|
+
*
|
|
120
|
+
* An event with no color paints `$accentBackground`, and its text is
|
|
121
|
+
* `onAccent`: the fill's readable anchor, which the caller resolves with
|
|
122
|
+
* `useReadableTextOn("$accentBackground")`. `$accentColor` is accent ink for
|
|
123
|
+
* the page, not for the fill; on it the fallback chip measured 1.76:1 in light
|
|
124
|
+
* and 3.84:1 in dark (MPO-468). A token color keeps `$accentColor`.
|
|
125
|
+
*
|
|
126
|
+
* The dark candidate is pure black: the old #1a1a1a candidate was scored
|
|
127
|
+
* against ideal black (L=0) but rendered at L=0.0103, overstating its own
|
|
128
|
+
* contrast — measured 4.11:1 on #8B5CF6 while claiming 4.96 (Axiom 15:
|
|
129
|
+
* foreground on fills is luminance-COMPUTED, and the computation must match
|
|
130
|
+
* the rendered pixels). Black keeps every sanctioned step-9 solid ≥ 4.5:1.
|
|
131
|
+
*/
|
|
132
|
+
export function eventChipTextColor(bg?: string, onAccent?: string): string {
|
|
133
|
+
if (!bg) return onAccent ?? "$accentColor";
|
|
134
|
+
if (!bg.startsWith("#")) return "$accentColor";
|
|
137
135
|
const hex = bg.slice(1);
|
|
138
136
|
const full =
|
|
139
137
|
hex.length === 3
|
|
@@ -652,6 +650,7 @@ function SettleGhost({
|
|
|
652
650
|
borderRadius: Record<string, unknown>;
|
|
653
651
|
onDone: () => void;
|
|
654
652
|
}) {
|
|
653
|
+
const onAccent = useReadableTextOn("$accentBackground");
|
|
655
654
|
const [moved, setMoved] = useState(false);
|
|
656
655
|
useEffect(() => {
|
|
657
656
|
// Double rAF so the ghost paints at `from` before the tween retargets it
|
|
@@ -690,7 +689,7 @@ function SettleGhost({
|
|
|
690
689
|
},
|
|
691
690
|
} as any)}
|
|
692
691
|
>
|
|
693
|
-
<Text fontSize="$2" numberOfLines={1} color={eventChipTextColor(settle.color)}>
|
|
692
|
+
<Text fontSize="$2" numberOfLines={1} color={eventChipTextColor(settle.color, onAccent)}>
|
|
694
693
|
{settle.title}
|
|
695
694
|
</Text>
|
|
696
695
|
</YStack>
|
|
@@ -1662,7 +1661,7 @@ export function Calendar({
|
|
|
1662
1661
|
{...knobProps.body}
|
|
1663
1662
|
fontSize={compact ? "$1" : "$2"}
|
|
1664
1663
|
numberOfLines={compact ? 1 : 2}
|
|
1665
|
-
color={eventChipTextColor(event.color)}
|
|
1664
|
+
color={eventChipTextColor(event.color, onAccent)}
|
|
1666
1665
|
>
|
|
1667
1666
|
{event.title}
|
|
1668
1667
|
</Text>
|
|
@@ -1679,6 +1678,7 @@ export function Calendar({
|
|
|
1679
1678
|
getEventDragProps,
|
|
1680
1679
|
eventA11yProps,
|
|
1681
1680
|
draggingEventId,
|
|
1681
|
+
onAccent,
|
|
1682
1682
|
],
|
|
1683
1683
|
);
|
|
1684
1684
|
|
|
@@ -1958,12 +1958,16 @@ export function Calendar({
|
|
|
1958
1958
|
fontSize="$1"
|
|
1959
1959
|
fontWeight="600"
|
|
1960
1960
|
numberOfLines={1}
|
|
1961
|
-
color={eventChipTextColor(event.color)}
|
|
1961
|
+
color={eventChipTextColor(event.color, onAccent)}
|
|
1962
1962
|
>
|
|
1963
1963
|
{event.title}
|
|
1964
1964
|
</Text>
|
|
1965
1965
|
{blockHeight >= 34 && (
|
|
1966
|
-
<Text
|
|
1966
|
+
<Text
|
|
1967
|
+
fontSize="$1"
|
|
1968
|
+
numberOfLines={1}
|
|
1969
|
+
color={eventChipTextColor(event.color, onAccent)}
|
|
1970
|
+
>
|
|
1967
1971
|
{formatTimeRangeLabel(start, end)}
|
|
1968
1972
|
</Text>
|
|
1969
1973
|
)}
|
|
@@ -2517,7 +2521,11 @@ export function Calendar({
|
|
|
2517
2521
|
}),
|
|
2518
2522
|
} as any)}
|
|
2519
2523
|
>
|
|
2520
|
-
<Text
|
|
2524
|
+
<Text
|
|
2525
|
+
fontSize="$2"
|
|
2526
|
+
numberOfLines={1}
|
|
2527
|
+
color={eventChipTextColor(dragGhost.event.color, onAccent)}
|
|
2528
|
+
>
|
|
2521
2529
|
{dragGhost.event.title}
|
|
2522
2530
|
</Text>
|
|
2523
2531
|
</YStack>
|
package/src/views/Dashboard.tsx
CHANGED
|
@@ -15,19 +15,8 @@ import {
|
|
|
15
15
|
} from "@multiplatform.one/theme";
|
|
16
16
|
import React, { useCallback, useMemo, useState, type ReactNode } from "react";
|
|
17
17
|
import { useTranslation } from "../shared/i18n";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
Text,
|
|
21
|
-
XStack,
|
|
22
|
-
YStack,
|
|
23
|
-
isWeb,
|
|
24
|
-
type YStackProps,
|
|
25
|
-
H2,
|
|
26
|
-
H3,
|
|
27
|
-
H4,
|
|
28
|
-
H5,
|
|
29
|
-
H6,
|
|
30
|
-
} from "tamagui";
|
|
18
|
+
import { Text, XStack, YStack, isWeb, type YStackProps, H2, H3, H4, H5, H6 } from "tamagui";
|
|
19
|
+
import { ScrollView } from "../ScrollView";
|
|
31
20
|
|
|
32
21
|
/**
|
|
33
22
|
* LC-17 SECTION-HEADING-SCALE + DG-A11Y-02: card sub-titles read at the ONE
|
|
@@ -11,7 +11,10 @@ const resizeCallbacks = new Map<Element, () => void>();
|
|
|
11
11
|
vi.stubGlobal(
|
|
12
12
|
"ResizeObserver",
|
|
13
13
|
class {
|
|
14
|
-
|
|
14
|
+
private callback: ResizeObserverCallback;
|
|
15
|
+
constructor(callback: ResizeObserverCallback) {
|
|
16
|
+
this.callback = callback;
|
|
17
|
+
}
|
|
15
18
|
observe(target: Element) {
|
|
16
19
|
const notify = () =>
|
|
17
20
|
this.callback([{ target } as ResizeObserverEntry], this as unknown as ResizeObserver);
|
package/src/views/Gantt.tsx
CHANGED
|
@@ -22,7 +22,8 @@ import { Button } from "@multiplatform.one/forms";
|
|
|
22
22
|
import React, { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
23
23
|
import { useTranslation } from "../shared/i18n";
|
|
24
24
|
import { ScrollView as RNScrollView } from "react-native";
|
|
25
|
-
import {
|
|
25
|
+
import { Text, XStack, YStack, isWeb, type YStackProps } from "tamagui";
|
|
26
|
+
import { ScrollView } from "../ScrollView";
|
|
26
27
|
import {
|
|
27
28
|
addDays,
|
|
28
29
|
barGeometry,
|
|
@@ -424,6 +425,36 @@ function GanttBarLabel({ title, fill }: { title: string; fill: string }) {
|
|
|
424
425
|
);
|
|
425
426
|
}
|
|
426
427
|
|
|
428
|
+
/** Open-end mark: the bar label's ink, so it reads on every fill. */
|
|
429
|
+
function GanttOpenEdgeMark({
|
|
430
|
+
fill,
|
|
431
|
+
glyph,
|
|
432
|
+
side,
|
|
433
|
+
edge,
|
|
434
|
+
}: {
|
|
435
|
+
fill: string;
|
|
436
|
+
glyph: string;
|
|
437
|
+
side: { left: number } | { right: number };
|
|
438
|
+
edge: "start" | "end";
|
|
439
|
+
}) {
|
|
440
|
+
const { knobProps } = useResolvedKnobs();
|
|
441
|
+
const ink = useReadableTextOn(fill);
|
|
442
|
+
return (
|
|
443
|
+
<Text
|
|
444
|
+
position="absolute"
|
|
445
|
+
{...side}
|
|
446
|
+
{...knobProps.label}
|
|
447
|
+
color={ink ?? "$color12"}
|
|
448
|
+
opacity={0.75}
|
|
449
|
+
pointerEvents="none"
|
|
450
|
+
aria-hidden
|
|
451
|
+
{...{ [`data-gantt-open-${edge}`]: "true" }}
|
|
452
|
+
>
|
|
453
|
+
{glyph}
|
|
454
|
+
</Text>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
427
458
|
/** Row-identity label: selected fill gets readable on-accent ink (LC-71). */
|
|
428
459
|
function GanttTaskName({
|
|
429
460
|
title,
|
|
@@ -1386,32 +1417,15 @@ export function GanttView({
|
|
|
1386
1417
|
|
|
1387
1418
|
{/* "This end is not an end" — a mark, never a cap. */}
|
|
1388
1419
|
{geom.openStart && (
|
|
1389
|
-
<
|
|
1390
|
-
|
|
1391
|
-
{
|
|
1392
|
-
{
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
pointerEvents="none"
|
|
1396
|
-
aria-hidden
|
|
1397
|
-
{...{ "data-gantt-open-start": "true" }}
|
|
1398
|
-
>
|
|
1399
|
-
{rtl ? "»" : "«"}
|
|
1400
|
-
</Text>
|
|
1420
|
+
<GanttOpenEdgeMark
|
|
1421
|
+
fill={fill}
|
|
1422
|
+
glyph={rtl ? "»" : "«"}
|
|
1423
|
+
side={openStartSide}
|
|
1424
|
+
edge="start"
|
|
1425
|
+
/>
|
|
1401
1426
|
)}
|
|
1402
1427
|
{geom.openEnd && (
|
|
1403
|
-
<
|
|
1404
|
-
position="absolute"
|
|
1405
|
-
{...openEndSide}
|
|
1406
|
-
{...knobProps.label}
|
|
1407
|
-
color="$color12"
|
|
1408
|
-
opacity={0.75}
|
|
1409
|
-
pointerEvents="none"
|
|
1410
|
-
aria-hidden
|
|
1411
|
-
{...{ "data-gantt-open-end": "true" }}
|
|
1412
|
-
>
|
|
1413
|
-
{rtl ? "«" : "»"}
|
|
1414
|
-
</Text>
|
|
1428
|
+
<GanttOpenEdgeMark fill={fill} glyph={rtl ? "«" : "»"} side={openEndSide} edge="end" />
|
|
1415
1429
|
)}
|
|
1416
1430
|
|
|
1417
1431
|
{/* Live date label while dragging (honest preview of the commit). */}
|
|
@@ -1780,6 +1794,7 @@ export function GanttView({
|
|
|
1780
1794
|
Tamagui's styled ScrollView does not deliver onScroll there, and
|
|
1781
1795
|
the pinned axis follower depends on it. */}
|
|
1782
1796
|
<CanvasScroller
|
|
1797
|
+
keyboardShouldPersistTaps="handled"
|
|
1783
1798
|
ref={hScrollRef}
|
|
1784
1799
|
horizontal
|
|
1785
1800
|
{...(isWeb ? { flex: 1 } : null)}
|
package/src/views/Kanban.tsx
CHANGED
|
@@ -20,16 +20,8 @@ import React, {
|
|
|
20
20
|
useState,
|
|
21
21
|
type ReactNode,
|
|
22
22
|
} from "react";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
ListItem,
|
|
26
|
-
ScrollView,
|
|
27
|
-
Text,
|
|
28
|
-
View,
|
|
29
|
-
XStack,
|
|
30
|
-
YStack,
|
|
31
|
-
type YStackProps,
|
|
32
|
-
} from "tamagui";
|
|
23
|
+
import { AnimatePresence, ListItem, Text, View, XStack, YStack, type YStackProps } from "tamagui";
|
|
24
|
+
import { ScrollView } from "../ScrollView";
|
|
33
25
|
import { componentColors } from "../componentColors";
|
|
34
26
|
import { AsyncBoundary, useAsyncCount } from "../layouts/AsyncBoundary";
|
|
35
27
|
import { EmptyState } from "../layouts/page";
|
package/src/views/List.tsx
CHANGED
package/src/views/Matrix.tsx
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
import React, { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
|
|
19
19
|
import { useTranslation } from "../shared/i18n";
|
|
20
20
|
import { ScrollView as RNScrollView } from "react-native";
|
|
21
|
-
import {
|
|
21
|
+
import { Text, XStack, YStack, isWeb, type YStackProps } from "tamagui";
|
|
22
|
+
import { ScrollView } from "../ScrollView";
|
|
22
23
|
import { formatRangeLabel } from "./ganttMath";
|
|
23
24
|
import {
|
|
24
25
|
alignMatrixRange,
|
|
@@ -634,6 +635,7 @@ export function MatrixView({
|
|
|
634
635
|
</YStack>
|
|
635
636
|
|
|
636
637
|
<CanvasScroller
|
|
638
|
+
keyboardShouldPersistTaps="handled"
|
|
637
639
|
horizontal
|
|
638
640
|
{...(isWeb ? { flex: 1 } : null)}
|
|
639
641
|
showsHorizontalScrollIndicator
|
|
@@ -30,7 +30,8 @@ import {
|
|
|
30
30
|
import { useHotkey } from "@tanstack/react-hotkeys";
|
|
31
31
|
import React, { useState, useMemo, useCallback, useId, useRef } from "react";
|
|
32
32
|
import { useTranslation } from "../shared/i18n";
|
|
33
|
-
import {
|
|
33
|
+
import { Text, XStack, YStack, type YStackProps } from "tamagui";
|
|
34
|
+
import { ScrollView } from "../ScrollView";
|
|
34
35
|
|
|
35
36
|
const AGGREGATIONS = ["count", "sum", "avg", "min", "max"] as const;
|
|
36
37
|
|
package/src/views/TreeView.tsx
CHANGED
|
@@ -1468,6 +1468,7 @@ export function TreeView<T = unknown>({
|
|
|
1468
1468
|
}}
|
|
1469
1469
|
>
|
|
1470
1470
|
<FlashList
|
|
1471
|
+
keyboardShouldPersistTaps="handled"
|
|
1471
1472
|
ref={attachList}
|
|
1472
1473
|
data={flatNodes}
|
|
1473
1474
|
drawDistance={overscan * estimateSize}
|
|
@@ -1702,7 +1703,7 @@ export function TreeView<T = unknown>({
|
|
|
1702
1703
|
marginVertical={-handleOutset}
|
|
1703
1704
|
hitSlop={pressTargetHitSlop(caretSize)}
|
|
1704
1705
|
cursor="grab"
|
|
1705
|
-
opacity={0.
|
|
1706
|
+
opacity={0.6}
|
|
1706
1707
|
transition={transition as any}
|
|
1707
1708
|
hoverStyle={{ opacity: 1 }}
|
|
1708
1709
|
onPress={(e: any) => e?.stopPropagation?.()}
|