@multiplatform.one/components 7.81.1 → 7.82.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/dist/cjs/charts/BarChart.cjs +1 -0
- package/dist/cjs/charts/BarChart.native.js +1 -0
- package/dist/cjs/charts/BarChart.native.js.map +1 -1
- package/dist/cjs/charts/ChartDataView.cjs +89 -9
- package/dist/cjs/charts/ChartDataView.native.js +94 -9
- package/dist/cjs/charts/ChartDataView.native.js.map +1 -1
- package/dist/cjs/charts/ChartSurface.cjs +26 -8
- package/dist/cjs/charts/ChartSurface.native.js +27 -9
- package/dist/cjs/charts/ChartSurface.native.js.map +1 -1
- package/dist/cjs/charts/chartHeight.cjs +1 -1
- package/dist/cjs/charts/chartHeight.native.js +1 -1
- package/dist/cjs/charts/chartHeight.native.js.map +1 -1
- package/dist/cjs/charts/composeChart.cjs +1 -1
- package/dist/cjs/charts/composeChart.native.js +1 -1
- package/dist/cjs/charts/composeChart.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.cjs +38 -31
- package/dist/cjs/code/CodeBlock/index.native.js +38 -31
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/code/LogView.cjs +142 -0
- package/dist/cjs/code/LogView.native.js +151 -0
- package/dist/cjs/code/LogView.native.js.map +1 -0
- package/dist/cjs/code/index.cjs +2 -1
- package/dist/cjs/code/index.native.js +1 -0
- package/dist/cjs/code/index.native.js.map +1 -1
- package/dist/cjs/layouts/EmptyState.cjs +9 -43
- package/dist/cjs/layouts/EmptyState.native.js +9 -45
- package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
- package/dist/cjs/layouts/PageTemplates.cjs +61 -0
- package/dist/cjs/layouts/PageTemplates.native.js +62 -0
- package/dist/cjs/layouts/PageTemplates.native.js.map +1 -1
- package/dist/cjs/layouts/barTitleSpan.cjs +54 -0
- package/dist/cjs/layouts/barTitleSpan.native.js +78 -0
- package/dist/cjs/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/cjs/layouts/page.cjs +130 -41
- package/dist/cjs/layouts/page.native.js +143 -45
- package/dist/cjs/layouts/page.native.js.map +1 -1
- package/dist/cjs/views/ImageGrid.cjs +9 -0
- package/dist/cjs/views/ImageGrid.native.js +12 -1
- package/dist/cjs/views/ImageGrid.native.js.map +1 -1
- package/dist/esm/charts/BarChart.mjs +1 -0
- package/dist/esm/charts/BarChart.mjs.map +1 -1
- package/dist/esm/charts/BarChart.native.js +1 -0
- package/dist/esm/charts/BarChart.native.js.map +1 -1
- package/dist/esm/charts/ChartDataView.mjs +83 -3
- package/dist/esm/charts/ChartDataView.mjs.map +1 -1
- package/dist/esm/charts/ChartDataView.native.js +88 -3
- package/dist/esm/charts/ChartDataView.native.js.map +1 -1
- package/dist/esm/charts/ChartSurface.mjs +26 -8
- package/dist/esm/charts/ChartSurface.mjs.map +1 -1
- package/dist/esm/charts/ChartSurface.native.js +27 -9
- package/dist/esm/charts/ChartSurface.native.js.map +1 -1
- package/dist/esm/charts/chartHeight.mjs +1 -1
- package/dist/esm/charts/chartHeight.mjs.map +1 -1
- package/dist/esm/charts/chartHeight.native.js +1 -1
- package/dist/esm/charts/chartHeight.native.js.map +1 -1
- package/dist/esm/charts/composeChart.mjs +1 -1
- package/dist/esm/charts/composeChart.mjs.map +1 -1
- package/dist/esm/charts/composeChart.native.js +1 -1
- package/dist/esm/charts/composeChart.native.js.map +1 -1
- package/dist/esm/code/CodeBlock/index.mjs +39 -32
- package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
- package/dist/esm/code/CodeBlock/index.native.js +39 -32
- package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
- package/dist/esm/code/LogView.mjs +117 -0
- package/dist/esm/code/LogView.mjs.map +1 -0
- package/dist/esm/code/LogView.native.js +123 -0
- package/dist/esm/code/LogView.native.js.map +1 -0
- package/dist/esm/code/index.mjs +1 -0
- package/dist/esm/code/index.mjs.map +1 -1
- package/dist/esm/code/index.native.js +1 -0
- package/dist/esm/code/index.native.js.map +1 -1
- package/dist/esm/layouts/EmptyState.mjs +9 -43
- package/dist/esm/layouts/EmptyState.mjs.map +1 -1
- package/dist/esm/layouts/EmptyState.native.js +9 -45
- package/dist/esm/layouts/EmptyState.native.js.map +1 -1
- package/dist/esm/layouts/PageTemplates.mjs +61 -1
- package/dist/esm/layouts/PageTemplates.mjs.map +1 -1
- package/dist/esm/layouts/PageTemplates.native.js +62 -1
- package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
- package/dist/esm/layouts/barTitleSpan.mjs +27 -0
- package/dist/esm/layouts/barTitleSpan.mjs.map +1 -0
- package/dist/esm/layouts/barTitleSpan.native.js +48 -0
- package/dist/esm/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/esm/layouts/page.mjs +132 -43
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +145 -47
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/esm/views/ImageGrid.mjs +10 -1
- package/dist/esm/views/ImageGrid.mjs.map +1 -1
- package/dist/esm/views/ImageGrid.native.js +13 -2
- package/dist/esm/views/ImageGrid.native.js.map +1 -1
- package/dist/jsx/charts/BarChart.mjs +1 -0
- package/dist/jsx/charts/BarChart.mjs.map +1 -1
- package/dist/jsx/charts/BarChart.native.js +1 -0
- package/dist/jsx/charts/BarChart.native.js.map +1 -1
- package/dist/jsx/charts/ChartDataView.mjs +83 -3
- package/dist/jsx/charts/ChartDataView.mjs.map +1 -1
- package/dist/jsx/charts/ChartDataView.native.js +94 -9
- package/dist/jsx/charts/ChartDataView.native.js.map +1 -1
- package/dist/jsx/charts/ChartSurface.mjs +26 -8
- package/dist/jsx/charts/ChartSurface.mjs.map +1 -1
- package/dist/jsx/charts/ChartSurface.native.js +27 -9
- package/dist/jsx/charts/ChartSurface.native.js.map +1 -1
- package/dist/jsx/charts/chartHeight.mjs +1 -1
- package/dist/jsx/charts/chartHeight.mjs.map +1 -1
- package/dist/jsx/charts/chartHeight.native.js +1 -1
- package/dist/jsx/charts/chartHeight.native.js.map +1 -1
- package/dist/jsx/charts/composeChart.mjs +1 -1
- package/dist/jsx/charts/composeChart.mjs.map +1 -1
- package/dist/jsx/charts/composeChart.native.js +1 -1
- package/dist/jsx/charts/composeChart.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.mjs +39 -32
- package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +38 -31
- package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
- package/dist/jsx/code/LogView.mjs +117 -0
- package/dist/jsx/code/LogView.mjs.map +1 -0
- package/dist/jsx/code/LogView.native.js +151 -0
- package/dist/jsx/code/LogView.native.js.map +1 -0
- package/dist/jsx/code/index.mjs +1 -0
- package/dist/jsx/code/index.mjs.map +1 -1
- package/dist/jsx/code/index.native.js +1 -0
- package/dist/jsx/code/index.native.js.map +1 -1
- package/dist/jsx/layouts/EmptyState.mjs +9 -43
- package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
- package/dist/jsx/layouts/EmptyState.native.js +9 -45
- package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
- package/dist/jsx/layouts/PageTemplates.mjs +61 -1
- package/dist/jsx/layouts/PageTemplates.mjs.map +1 -1
- package/dist/jsx/layouts/PageTemplates.native.js +62 -0
- package/dist/jsx/layouts/PageTemplates.native.js.map +1 -1
- package/dist/jsx/layouts/barTitleSpan.mjs +27 -0
- package/dist/jsx/layouts/barTitleSpan.mjs.map +1 -0
- package/dist/jsx/layouts/barTitleSpan.native.js +78 -0
- package/dist/jsx/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/jsx/layouts/page.mjs +132 -43
- package/dist/jsx/layouts/page.mjs.map +1 -1
- package/dist/jsx/layouts/page.native.js +143 -45
- package/dist/jsx/layouts/page.native.js.map +1 -1
- package/dist/jsx/views/ImageGrid.mjs +10 -1
- package/dist/jsx/views/ImageGrid.mjs.map +1 -1
- package/dist/jsx/views/ImageGrid.native.js +12 -1
- package/dist/jsx/views/ImageGrid.native.js.map +1 -1
- package/package.json +9 -9
- package/src/charts/BarChart.tsx +8 -2
- package/src/charts/ChartDataView.spec.tsx +23 -2
- package/src/charts/ChartDataView.tsx +87 -6
- package/src/charts/ChartSurface.tsx +17 -8
- package/src/charts/chartHeight.ts +8 -2
- package/src/charts/composeChart.ts +3 -2
- package/src/code/CodeBlock/CodeBlock.copy.spec.tsx +13 -0
- package/src/code/CodeBlock/index.tsx +46 -30
- package/src/code/LogView.tsx +127 -0
- package/src/code/index.ts +1 -0
- package/src/layouts/DenseContent.stories.tsx +93 -0
- package/src/layouts/EmptyState.spec.tsx +2 -0
- package/src/layouts/EmptyState.tsx +6 -39
- package/src/layouts/PageHeader.stories.tsx +238 -38
- package/src/layouts/PageTemplates.tsx +90 -0
- package/src/layouts/barTitleSpan.spec.ts +35 -0
- package/src/layouts/barTitleSpan.ts +44 -0
- package/src/layouts/glyphCentre.spec.tsx +218 -0
- package/src/layouts/navBar.spec.tsx +58 -2
- package/src/layouts/page.tsx +126 -34
- package/src/views/ImageGrid.tsx +19 -2
- package/types/charts/BarChart.d.ts +1 -1
- package/types/charts/BarChart.d.ts.map +1 -1
- package/types/charts/ChartDataView.d.ts +3 -1
- package/types/charts/ChartDataView.d.ts.map +1 -1
- package/types/charts/ChartSurface.d.ts.map +1 -1
- package/types/charts/chartHeight.d.ts +2 -1
- package/types/charts/chartHeight.d.ts.map +1 -1
- package/types/charts/composeChart.d.ts +2 -1
- package/types/charts/composeChart.d.ts.map +1 -1
- package/types/code/CodeBlock/index.d.ts.map +1 -1
- package/types/code/LogView.d.ts +31 -0
- package/types/code/LogView.d.ts.map +1 -0
- package/types/code/index.d.ts +1 -0
- package/types/code/index.d.ts.map +1 -1
- package/types/layouts/EmptyState.d.ts.map +1 -1
- package/types/layouts/PageTemplates.d.ts +26 -0
- package/types/layouts/PageTemplates.d.ts.map +1 -1
- package/types/layouts/barTitleSpan.d.ts +29 -0
- package/types/layouts/barTitleSpan.d.ts.map +1 -0
- package/types/layouts/page.d.ts +6 -1
- package/types/layouts/page.d.ts.map +1 -1
- package/types/views/ImageGrid.d.ts.map +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LC-176 (FZ-8442): a glyph on a glass circle sits at the circle's centre.
|
|
3
|
+
* Clay on the Kitchen Sink SHC Nodes story: "the icons inside of these are
|
|
4
|
+
* aligned wrong". The back caret was pulled toward the bar's edge and the "+"
|
|
5
|
+
* pushed past its circle's centre.
|
|
6
|
+
*
|
|
7
|
+
* jsdom has no layout, so the ink centre is computed: the offset each box
|
|
8
|
+
* between the glyph and its circle adds (a margin moves a centred child by
|
|
9
|
+
* half of it, a translate by all of it), plus where the glyph's ink sits in
|
|
10
|
+
* its 256-unit Phosphor grid, read from the rendered path.
|
|
11
|
+
*/
|
|
12
|
+
import { IconButton } from "@multiplatform.one/forms";
|
|
13
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
14
|
+
import { Surface } from "@multiplatform.one/theme";
|
|
15
|
+
import { PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
16
|
+
import { cleanup, screen } from "@testing-library/react";
|
|
17
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
import { PageBackButton, ScreenToolbar } from "./page";
|
|
19
|
+
|
|
20
|
+
const IPHONE =
|
|
21
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1";
|
|
22
|
+
const GRID = 256;
|
|
23
|
+
const TOLERANCE_PT = 0.5;
|
|
24
|
+
|
|
25
|
+
type Box = { minX: number; maxX: number; minY: number; maxY: number };
|
|
26
|
+
|
|
27
|
+
function arcExtremes(
|
|
28
|
+
box: Box,
|
|
29
|
+
from: [number, number],
|
|
30
|
+
r: number,
|
|
31
|
+
large: number,
|
|
32
|
+
sweep: number,
|
|
33
|
+
to: [number, number],
|
|
34
|
+
) {
|
|
35
|
+
const [x1, y1] = from;
|
|
36
|
+
const [x2, y2] = to;
|
|
37
|
+
const mx = (x1 - x2) / 2;
|
|
38
|
+
const my = (y1 - y2) / 2;
|
|
39
|
+
const d2 = mx * mx + my * my;
|
|
40
|
+
const radius = Math.max(r, Math.sqrt(d2));
|
|
41
|
+
const k = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, radius ** 2 / d2 - 1));
|
|
42
|
+
const cx = k * my + (x1 + x2) / 2;
|
|
43
|
+
const cy = -k * mx + (y1 + y2) / 2;
|
|
44
|
+
const start = Math.atan2(y1 - cy, x1 - cx);
|
|
45
|
+
let delta = Math.atan2(y2 - cy, x2 - cx) - start;
|
|
46
|
+
if (sweep && delta < 0) delta += 2 * Math.PI;
|
|
47
|
+
if (!sweep && delta > 0) delta -= 2 * Math.PI;
|
|
48
|
+
for (const angle of [0, Math.PI / 2, Math.PI, -Math.PI / 2]) {
|
|
49
|
+
let t = angle - start;
|
|
50
|
+
while (t < 0) t += 2 * Math.PI;
|
|
51
|
+
while (t >= 2 * Math.PI) t -= 2 * Math.PI;
|
|
52
|
+
const within = delta >= 0 ? t <= delta : t >= 2 * Math.PI + delta || t === 0;
|
|
53
|
+
if (within) include(box, cx + radius * Math.cos(angle), cy + radius * Math.sin(angle));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function include(box: Box, x: number, y: number) {
|
|
58
|
+
box.minX = Math.min(box.minX, x);
|
|
59
|
+
box.maxX = Math.max(box.maxX, x);
|
|
60
|
+
box.minY = Math.min(box.minY, y);
|
|
61
|
+
box.maxY = Math.max(box.maxY, y);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The ink box of an SVG path made of the commands Phosphor uses. */
|
|
65
|
+
function inkBox(d: string): Box {
|
|
66
|
+
const box: Box = { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity };
|
|
67
|
+
const tokens = d.match(/[a-zA-Z]|-?(?:\d*\.\d+|\d+)(?:e-?\d+)?/g) ?? [];
|
|
68
|
+
let i = 0;
|
|
69
|
+
let command = "";
|
|
70
|
+
let x = 0;
|
|
71
|
+
let y = 0;
|
|
72
|
+
let startX = 0;
|
|
73
|
+
let startY = 0;
|
|
74
|
+
const num = () => Number(tokens[i++]);
|
|
75
|
+
while (i < tokens.length) {
|
|
76
|
+
if (/[a-zA-Z]/.test(tokens[i]!)) command = tokens[i++]!;
|
|
77
|
+
const rel = command === command.toLowerCase();
|
|
78
|
+
const ox = rel ? x : 0;
|
|
79
|
+
const oy = rel ? y : 0;
|
|
80
|
+
switch (command.toUpperCase()) {
|
|
81
|
+
case "M":
|
|
82
|
+
x = ox + num();
|
|
83
|
+
y = oy + num();
|
|
84
|
+
startX = x;
|
|
85
|
+
startY = y;
|
|
86
|
+
include(box, x, y);
|
|
87
|
+
command = rel ? "l" : "L";
|
|
88
|
+
break;
|
|
89
|
+
case "L":
|
|
90
|
+
x = ox + num();
|
|
91
|
+
y = oy + num();
|
|
92
|
+
include(box, x, y);
|
|
93
|
+
break;
|
|
94
|
+
case "H":
|
|
95
|
+
x = ox + num();
|
|
96
|
+
include(box, x, y);
|
|
97
|
+
break;
|
|
98
|
+
case "V":
|
|
99
|
+
y = oy + num();
|
|
100
|
+
include(box, x, y);
|
|
101
|
+
break;
|
|
102
|
+
case "C": {
|
|
103
|
+
for (let n = 0; n < 2; n++) include(box, ox + num(), oy + num());
|
|
104
|
+
x = ox + num();
|
|
105
|
+
y = oy + num();
|
|
106
|
+
include(box, x, y);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "A": {
|
|
110
|
+
const r = num();
|
|
111
|
+
num();
|
|
112
|
+
num();
|
|
113
|
+
const large = num();
|
|
114
|
+
const sweep = num();
|
|
115
|
+
const to: [number, number] = [ox + num(), oy + num()];
|
|
116
|
+
arcExtremes(box, [x, y], r, large, sweep, to);
|
|
117
|
+
[x, y] = to;
|
|
118
|
+
include(box, x, y);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "Z":
|
|
122
|
+
x = startX;
|
|
123
|
+
y = startY;
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
throw new Error(`inkBox: unhandled path command ${command}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return box;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const px = (value: string | undefined) => Number.parseFloat(value || "0") || 0;
|
|
133
|
+
|
|
134
|
+
function translate(transform: string, axis: "X" | "Y") {
|
|
135
|
+
const one = transform.match(new RegExp(`translate${axis}\\((-?[\\d.]+)px\\)`));
|
|
136
|
+
if (one) return Number(one[1]);
|
|
137
|
+
const both = transform.match(/translate\((-?[\d.]+)px(?:,\s*(-?[\d.]+)px)?\)/);
|
|
138
|
+
if (!both) return 0;
|
|
139
|
+
return axis === "X" ? Number(both[1]) : Number(both[2] ?? 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** How far the glyph's ink centre sits from its circle's centre, in pt. */
|
|
143
|
+
function inkOffset(button: HTMLElement) {
|
|
144
|
+
const svg = button.querySelector("svg");
|
|
145
|
+
if (!svg) throw new Error("no glyph");
|
|
146
|
+
const size = Number(svg.getAttribute("width"));
|
|
147
|
+
const scale = size / GRID;
|
|
148
|
+
const box = inkBox(
|
|
149
|
+
Array.from(svg.querySelectorAll("path"))
|
|
150
|
+
.map((path) => path.getAttribute("d") ?? "")
|
|
151
|
+
.join(" "),
|
|
152
|
+
);
|
|
153
|
+
let dx = ((box.minX + box.maxX) / 2 - GRID / 2) * scale;
|
|
154
|
+
let dy = ((box.minY + box.maxY) / 2 - GRID / 2) * scale;
|
|
155
|
+
const frame = getComputedStyle(button);
|
|
156
|
+
dx += (px(frame.paddingLeft) - px(frame.paddingRight)) / 2;
|
|
157
|
+
dy += (px(frame.paddingTop) - px(frame.paddingBottom)) / 2;
|
|
158
|
+
for (let el: Element | null = svg; el && el !== button; el = el.parentElement) {
|
|
159
|
+
const style = getComputedStyle(el);
|
|
160
|
+
dx +=
|
|
161
|
+
(px(style.marginLeft) +
|
|
162
|
+
px(style.marginInlineStart) -
|
|
163
|
+
px(style.marginRight) -
|
|
164
|
+
px(style.marginInlineEnd)) /
|
|
165
|
+
2 +
|
|
166
|
+
translate(style.transform, "X");
|
|
167
|
+
dy += (px(style.marginTop) - px(style.marginBottom)) / 2 + translate(style.transform, "Y");
|
|
168
|
+
}
|
|
169
|
+
return { dx, dy, size };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
afterEach(() => {
|
|
173
|
+
vi.unstubAllGlobals();
|
|
174
|
+
cleanup();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("glyph centre == glass circle centre (LC-176, FZ-8442)", () => {
|
|
178
|
+
it("the ink box reads Phosphor's bold glyphs where they draw", () => {
|
|
179
|
+
const plus = inkBox(
|
|
180
|
+
"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z",
|
|
181
|
+
);
|
|
182
|
+
expect([plus.minX, plus.maxX, plus.minY, plus.maxY].map(Math.round)).toEqual([
|
|
183
|
+
28, 228, 28, 228,
|
|
184
|
+
]);
|
|
185
|
+
const caret = inkBox(
|
|
186
|
+
"M168.49,199.51a12,12,0,0,1-17,17l-80-80a12,12,0,0,1,0-17l80-80a12,12,0,0,1,17,17L97,128Z",
|
|
187
|
+
);
|
|
188
|
+
expect(Math.round((caret.minX + caret.maxX) / 2)).toBe(120);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("off glass the back caret keeps its optical pull, so the measure sees margins", () => {
|
|
192
|
+
renderWithProviders(<ScreenToolbar leading={<PageBackButton />} />);
|
|
193
|
+
const back = screen.getByLabelText("Back");
|
|
194
|
+
expect(back.getAttribute("data-mp-icon-button")).not.toBe("glass");
|
|
195
|
+
const { dx, size } = inkOffset(back);
|
|
196
|
+
expect(dx).toBeLessThan(-size * 0.05);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("back caret, + and ✕ sit at their circle's centre within 0.5pt on iPhone", () => {
|
|
200
|
+
vi.stubGlobal("navigator", { userAgent: IPHONE, platform: "iPhone", maxTouchPoints: 5 });
|
|
201
|
+
renderWithProviders(
|
|
202
|
+
<Surface bar>
|
|
203
|
+
<ScreenToolbar
|
|
204
|
+
leading={<PageBackButton />}
|
|
205
|
+
trailing={<IconButton icon={PlusIcon} aria-label="Add node" edge="end" />}
|
|
206
|
+
/>
|
|
207
|
+
<IconButton icon={XIcon} aria-label="Close" edge="start" />
|
|
208
|
+
</Surface>,
|
|
209
|
+
);
|
|
210
|
+
for (const name of ["Back", "Add node", "Close"]) {
|
|
211
|
+
const button = screen.getByLabelText(name);
|
|
212
|
+
expect(button.getAttribute("data-mp-icon-button"), name).toBe("glass");
|
|
213
|
+
const { dx, dy } = inkOffset(button);
|
|
214
|
+
expect(Math.abs(dx), `${name} dx`).toBeLessThanOrEqual(TOLERANCE_PT);
|
|
215
|
+
expect(Math.abs(dy), `${name} dy`).toBeLessThanOrEqual(TOLERANCE_PT);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* jsdom has no layout: the large title's geometry is stubbed through
|
|
9
9
|
* getBoundingClientRect, and scrolling sets scrollTop on the scroll element.
|
|
10
10
|
*/
|
|
11
|
-
import { ICON_BUTTON_GLYPH_PX } from "@multiplatform.one/forms";
|
|
11
|
+
import { ICON_BUTTON_GLYPH_PX, IconButton } from "@multiplatform.one/forms";
|
|
12
12
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
13
13
|
import { Preset, useGlyphTintOnSurface } from "@multiplatform.one/theme";
|
|
14
|
-
import { SlidersHorizontalIcon } from "@phosphor-icons/react";
|
|
14
|
+
import { PlusIcon, SlidersHorizontalIcon } from "@phosphor-icons/react";
|
|
15
15
|
import { act, cleanup, fireEvent, screen } from "@testing-library/react";
|
|
16
16
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
17
17
|
import {
|
|
@@ -269,3 +269,59 @@ describe("bar glyphs carry their weight (LC-176)", () => {
|
|
|
269
269
|
expect(screen.getByLabelText("Back").closest("[data-mp-icon-button-group]")).toBeNull();
|
|
270
270
|
});
|
|
271
271
|
});
|
|
272
|
+
|
|
273
|
+
const IPHONE =
|
|
274
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1";
|
|
275
|
+
|
|
276
|
+
describe("inline bar title and glyphs on glass (FZ-8442)", () => {
|
|
277
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
278
|
+
|
|
279
|
+
it("a title is centred in the flow toolbar and shown at rest, clear of the back control", () => {
|
|
280
|
+
renderWithProviders(
|
|
281
|
+
<ScreenToolbar
|
|
282
|
+
leading={<PageBackButton />}
|
|
283
|
+
title="Inbox"
|
|
284
|
+
trailing={<PageBarButton icon={SlidersHorizontalIcon} aria-label="Filter" />}
|
|
285
|
+
/>,
|
|
286
|
+
);
|
|
287
|
+
const title = document.querySelector("[data-bar-title]") as HTMLElement;
|
|
288
|
+
expect(title.textContent).toBe("Inbox");
|
|
289
|
+
expect(title.getAttribute("aria-hidden")).not.toBe("true");
|
|
290
|
+
const back = screen.getByLabelText("Back");
|
|
291
|
+
expect(title.contains(back)).toBe(false);
|
|
292
|
+
expect(back.parentElement?.contains(title)).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("the pinned bar shows a given title at rest", () => {
|
|
296
|
+
renderWithProviders(
|
|
297
|
+
<Screen topBar={<ScreenToolbar leading={<PageBackButton />} title="Inbox" />}>
|
|
298
|
+
<div style={{ height: 2000 }} />
|
|
299
|
+
</Screen>,
|
|
300
|
+
);
|
|
301
|
+
const title = document.querySelector("[data-top-bar] [data-bar-title]") as HTMLElement;
|
|
302
|
+
expect(title.textContent).toBe("Inbox");
|
|
303
|
+
expect(title.getAttribute("aria-hidden")).not.toBe("true");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("on glass the back caret drops its pull and a lone + keeps its own circle", () => {
|
|
307
|
+
vi.stubGlobal("navigator", { userAgent: IPHONE, platform: "iPhone", maxTouchPoints: 5 });
|
|
308
|
+
renderWithProviders(
|
|
309
|
+
<Screen topBar={<ScreenToolbar leading={<PageBackButton />} />}>
|
|
310
|
+
<PageHeader
|
|
311
|
+
title="Nodes"
|
|
312
|
+
actions={<IconButton icon={PlusIcon} aria-label="Add node" edge="end" />}
|
|
313
|
+
/>
|
|
314
|
+
</Screen>,
|
|
315
|
+
);
|
|
316
|
+
const back = screen.getByLabelText("Back");
|
|
317
|
+
expect(back.getAttribute("data-mp-icon-button")).toBe("glass");
|
|
318
|
+
const caret = back.querySelector("svg")!;
|
|
319
|
+
const pull = Math.round(Number(caret.getAttribute("width")) * (72 / 256));
|
|
320
|
+
expect(Array.from(caret.parentElement!.classList).some((c) => c.includes(`-${pull}px`))).toBe(
|
|
321
|
+
false,
|
|
322
|
+
);
|
|
323
|
+
const add = screen.getByLabelText("Add node");
|
|
324
|
+
expect(add.getAttribute("data-mp-icon-button")).toBe("glass");
|
|
325
|
+
expect(add.closest("[data-mp-glass-capsule]")).toBeNull();
|
|
326
|
+
});
|
|
327
|
+
});
|
package/src/layouts/page.tsx
CHANGED
|
@@ -33,9 +33,15 @@ import {
|
|
|
33
33
|
type PageTitleScale,
|
|
34
34
|
type ParentScreen,
|
|
35
35
|
} from "@multiplatform.one/theme";
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
Button,
|
|
38
|
+
IconButtonGroup,
|
|
39
|
+
useGlyphOnPlate,
|
|
40
|
+
type ButtonProps,
|
|
41
|
+
} from "@multiplatform.one/forms";
|
|
37
42
|
import { CaretLeftIcon, CaretRightIcon, DotsThreeIcon } from "@phosphor-icons/react";
|
|
38
43
|
import { useState, type ReactNode } from "react";
|
|
44
|
+
import { barTitleSpan, sideExtents } from "./barTitleSpan";
|
|
39
45
|
import type { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent } from "react-native";
|
|
40
46
|
import type { ScrollViewProps as TamaguiScrollViewProps, XStackProps, YStackProps } from "tamagui";
|
|
41
47
|
import {
|
|
@@ -655,11 +661,94 @@ export { EmptyState, type EmptyStateProps } from "./EmptyState";
|
|
|
655
661
|
// ScreenToolbar — minimal back/edit-style screen toolbar
|
|
656
662
|
// ---------------------------------------------------------------------------
|
|
657
663
|
|
|
658
|
-
export interface ScreenToolbarProps extends XStackProps {
|
|
664
|
+
export interface ScreenToolbarProps extends Omit<XStackProps, "title"> {
|
|
659
665
|
/** Leading content (typically a back button). */
|
|
660
666
|
leading?: ReactNode;
|
|
661
667
|
/** Trailing content (typically primary/secondary actions). */
|
|
662
668
|
trailing?: ReactNode;
|
|
669
|
+
/**
|
|
670
|
+
* The bar's inline title (the iOS inline navigation title): centred in the
|
|
671
|
+
* bar, clear of the wider side group, never set against the back control.
|
|
672
|
+
*/
|
|
673
|
+
title?: string;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function useBarTitleSpan() {
|
|
677
|
+
const none = { x: 0, width: 0 };
|
|
678
|
+
const [box, setBox] = useState({ row: 0, leading: none, trailing: none, title: 0 });
|
|
679
|
+
const set = <K extends keyof typeof box>(key: K, value: (typeof box)[K]) =>
|
|
680
|
+
setBox((prev) =>
|
|
681
|
+
JSON.stringify(prev[key]) === JSON.stringify(value) ? prev : { ...prev, [key]: value },
|
|
682
|
+
);
|
|
683
|
+
const extent = (event: LayoutChangeEvent) => {
|
|
684
|
+
const { x, width } = event.nativeEvent.layout;
|
|
685
|
+
return { x, width };
|
|
686
|
+
};
|
|
687
|
+
return {
|
|
688
|
+
span: barTitleSpan({
|
|
689
|
+
row: box.row,
|
|
690
|
+
title: box.title,
|
|
691
|
+
...sideExtents(box.row, box.leading, box.trailing),
|
|
692
|
+
}),
|
|
693
|
+
measureRow: (event: LayoutChangeEvent) => set("row", Math.floor(extent(event).width)),
|
|
694
|
+
measureLeading: (event: LayoutChangeEvent) => set("leading", extent(event)),
|
|
695
|
+
measureTrailing: (event: LayoutChangeEvent) => set("trailing", extent(event)),
|
|
696
|
+
measureTitle: (event: LayoutChangeEvent) => set("title", Math.ceil(extent(event).width)),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function BarTitle({
|
|
701
|
+
title,
|
|
702
|
+
span,
|
|
703
|
+
onTitleWidth,
|
|
704
|
+
shown = true,
|
|
705
|
+
...rest
|
|
706
|
+
}: {
|
|
707
|
+
title: string;
|
|
708
|
+
span: { left: number; right: number };
|
|
709
|
+
onTitleWidth: (event: LayoutChangeEvent) => void;
|
|
710
|
+
shown?: boolean;
|
|
711
|
+
} & XStackProps) {
|
|
712
|
+
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
713
|
+
const text = {
|
|
714
|
+
...knobProps.body,
|
|
715
|
+
fontWeight: "600",
|
|
716
|
+
color: "$color12",
|
|
717
|
+
numberOfLines: 1,
|
|
718
|
+
} as const;
|
|
719
|
+
return (
|
|
720
|
+
<>
|
|
721
|
+
{shown ? (
|
|
722
|
+
<XStack
|
|
723
|
+
position="absolute"
|
|
724
|
+
top={0}
|
|
725
|
+
left={0}
|
|
726
|
+
opacity={0}
|
|
727
|
+
pointerEvents="none"
|
|
728
|
+
aria-hidden
|
|
729
|
+
onLayout={onTitleWidth}
|
|
730
|
+
>
|
|
731
|
+
<SizableText {...text}>{title}</SizableText>
|
|
732
|
+
</XStack>
|
|
733
|
+
) : null}
|
|
734
|
+
<XStack
|
|
735
|
+
position="absolute"
|
|
736
|
+
top={0}
|
|
737
|
+
bottom={0}
|
|
738
|
+
left={span.left}
|
|
739
|
+
right={span.right}
|
|
740
|
+
alignItems="center"
|
|
741
|
+
justifyContent="center"
|
|
742
|
+
pointerEvents="none"
|
|
743
|
+
aria-hidden={!shown}
|
|
744
|
+
data-bar-title=""
|
|
745
|
+
opacity={shown ? 1 : 0}
|
|
746
|
+
{...rest}
|
|
747
|
+
>
|
|
748
|
+
<SizableText {...text}>{title}</SizableText>
|
|
749
|
+
</XStack>
|
|
750
|
+
</>
|
|
751
|
+
);
|
|
663
752
|
}
|
|
664
753
|
|
|
665
754
|
/**
|
|
@@ -679,8 +768,9 @@ export function ScreenToolbar(props: ScreenToolbarProps) {
|
|
|
679
768
|
return slot ? <PinnedTopBar {...props} slot={slot} /> : <InlineToolbar {...props} />;
|
|
680
769
|
}
|
|
681
770
|
|
|
682
|
-
function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarProps) {
|
|
771
|
+
function InlineToolbar({ leading, trailing, title, children, ...rest }: ScreenToolbarProps) {
|
|
683
772
|
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
773
|
+
const titleSpan = useBarTitleSpan();
|
|
684
774
|
const insetToken = usePageChromeInset();
|
|
685
775
|
const bleed = insetToken ? { marginHorizontal: negateSpaceToken(insetToken) as "$4" } : null;
|
|
686
776
|
// LC-145: a phone top bar is one row; the leading group shrinks first.
|
|
@@ -692,8 +782,9 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
692
782
|
data-testid="screen-toolbar"
|
|
693
783
|
justifyContent="space-between"
|
|
694
784
|
alignItems="center"
|
|
695
|
-
flexWrap={oneRow ? "nowrap" : "wrap"}
|
|
785
|
+
flexWrap={oneRow || title ? "nowrap" : "wrap"}
|
|
696
786
|
minHeight={MIN_PRESS_TARGET}
|
|
787
|
+
{...(title ? { position: "relative" as const, onLayout: titleSpan.measureRow } : null)}
|
|
697
788
|
{...bleed}
|
|
698
789
|
{...(insetToken ? { paddingHorizontal: insetToken as "$4" } : knobProps.panelPadding)}
|
|
699
790
|
{...knobProps.gap}
|
|
@@ -706,6 +797,7 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
706
797
|
// Shrink weight, not a size: the back label gives way before the actions wrap.
|
|
707
798
|
flexShrink={oneRow ? 1000 : 1}
|
|
708
799
|
minWidth={0}
|
|
800
|
+
{...(title ? { onLayout: titleSpan.measureLeading } : null)}
|
|
709
801
|
>
|
|
710
802
|
{leading}
|
|
711
803
|
</XStack>
|
|
@@ -716,9 +808,13 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
716
808
|
flexShrink={1}
|
|
717
809
|
minWidth={0}
|
|
718
810
|
justifyContent="flex-end"
|
|
811
|
+
{...(title ? { onLayout: titleSpan.measureTrailing } : null)}
|
|
719
812
|
>
|
|
720
813
|
{trailing}
|
|
721
814
|
</IconButtonGroup>
|
|
815
|
+
{title ? (
|
|
816
|
+
<BarTitle title={title} span={titleSpan.span} onTitleWidth={titleSpan.measureTitle} />
|
|
817
|
+
) : null}
|
|
722
818
|
</XStack>
|
|
723
819
|
</Surface>
|
|
724
820
|
);
|
|
@@ -737,19 +833,16 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
737
833
|
function PinnedTopBar({
|
|
738
834
|
leading,
|
|
739
835
|
trailing,
|
|
836
|
+
title: inlineTitle,
|
|
740
837
|
children,
|
|
741
838
|
slot,
|
|
742
839
|
...rest
|
|
743
840
|
}: ScreenToolbarProps & { slot: { inset: string | null; maxWidth: number | null } }) {
|
|
744
841
|
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
745
|
-
const { scrolled, collapsed, title } = useScreenScroll();
|
|
746
|
-
const
|
|
747
|
-
const measureSide = (side: "leading" | "trailing") => (event: LayoutChangeEvent) => {
|
|
748
|
-
const width = Math.ceil(event.nativeEvent.layout.width);
|
|
749
|
-
setSides((prev) => (prev[side] === width ? prev : { ...prev, [side]: width }));
|
|
750
|
-
};
|
|
842
|
+
const { scrolled, collapsed, title: largeTitle } = useScreenScroll();
|
|
843
|
+
const titleSpan = useBarTitleSpan();
|
|
751
844
|
const motion = transitionProps(knobProps.motion.control);
|
|
752
|
-
const
|
|
845
|
+
const title = inlineTitle ?? largeTitle;
|
|
753
846
|
// LC-153: the pinned bar is a bar, so its neutral buttons paint plain.
|
|
754
847
|
return (
|
|
755
848
|
<Surface bar>
|
|
@@ -772,6 +865,7 @@ function PinnedTopBar({
|
|
|
772
865
|
flexWrap="nowrap"
|
|
773
866
|
// The collapsed title is placed against this row, not the page.
|
|
774
867
|
position="relative"
|
|
868
|
+
onLayout={titleSpan.measureRow}
|
|
775
869
|
paddingHorizontal={(slot.inset ?? knobProps.panelPadding.padding) as "$4"}
|
|
776
870
|
{...knobProps.gap}
|
|
777
871
|
{...rest}
|
|
@@ -781,7 +875,7 @@ function PinnedTopBar({
|
|
|
781
875
|
alignItems="center"
|
|
782
876
|
flexShrink={1000}
|
|
783
877
|
minWidth={0}
|
|
784
|
-
onLayout={
|
|
878
|
+
onLayout={titleSpan.measureLeading}
|
|
785
879
|
>
|
|
786
880
|
{leading}
|
|
787
881
|
</XStack>
|
|
@@ -794,29 +888,18 @@ function PinnedTopBar({
|
|
|
794
888
|
flexShrink={1}
|
|
795
889
|
minWidth={0}
|
|
796
890
|
justifyContent="flex-end"
|
|
797
|
-
onLayout={
|
|
891
|
+
onLayout={titleSpan.measureTrailing}
|
|
798
892
|
>
|
|
799
893
|
{trailing}
|
|
800
894
|
</IconButtonGroup>
|
|
801
895
|
{title ? (
|
|
802
|
-
<
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
right={clearance}
|
|
808
|
-
alignItems="center"
|
|
809
|
-
justifyContent="center"
|
|
810
|
-
pointerEvents="none"
|
|
811
|
-
aria-hidden={!collapsed}
|
|
812
|
-
data-bar-title=""
|
|
813
|
-
opacity={collapsed ? 1 : 0}
|
|
896
|
+
<BarTitle
|
|
897
|
+
title={title}
|
|
898
|
+
span={titleSpan.span}
|
|
899
|
+
onTitleWidth={titleSpan.measureTitle}
|
|
900
|
+
shown={Boolean(inlineTitle) || collapsed}
|
|
814
901
|
{...motion}
|
|
815
|
-
|
|
816
|
-
<SizableText {...knobProps.body} fontWeight="600" color="$color12" numberOfLines={1}>
|
|
817
|
-
{title}
|
|
818
|
-
</SizableText>
|
|
819
|
-
</XStack>
|
|
902
|
+
/>
|
|
820
903
|
) : null}
|
|
821
904
|
</XStack>
|
|
822
905
|
</YStack>
|
|
@@ -841,8 +924,11 @@ function useTopBarControl() {
|
|
|
841
924
|
// Phosphor's caret path starts 72 units into its 256 grid; back pulls by that
|
|
842
925
|
// so the caret's point, not its box, lands on the title's leading edge.
|
|
843
926
|
const CARET_OPTICAL_INSET = 72 / 256;
|
|
927
|
+
// Its ink is centred 8 units toward the point, so on a glass circle it moves
|
|
928
|
+
// back by that to sit at the circle's centre (FZ-8442).
|
|
929
|
+
const CARET_INK_OFFSET = 8 / 256;
|
|
844
930
|
|
|
845
|
-
function backCaret(Caret: typeof CaretLeftIcon) {
|
|
931
|
+
function backCaret(Caret: typeof CaretLeftIcon, inkSign: 1 | -1) {
|
|
846
932
|
return function BackCaret({
|
|
847
933
|
size = 22,
|
|
848
934
|
color,
|
|
@@ -856,16 +942,22 @@ function backCaret(Caret: typeof CaretLeftIcon) {
|
|
|
856
942
|
strokeWidth?: number;
|
|
857
943
|
strokeLinejoin?: "round";
|
|
858
944
|
}) {
|
|
945
|
+
const plated = useGlyphOnPlate();
|
|
859
946
|
return (
|
|
860
|
-
<XStack
|
|
947
|
+
<XStack
|
|
948
|
+
data-back-caret=""
|
|
949
|
+
{...(plated
|
|
950
|
+
? { x: inkSign * size * CARET_INK_OFFSET }
|
|
951
|
+
: { marginInlineStart: -Math.round(size * CARET_OPTICAL_INSET) })}
|
|
952
|
+
>
|
|
861
953
|
<Caret size={size} color={color} weight={weight} {...stroke} />
|
|
862
954
|
</XStack>
|
|
863
955
|
);
|
|
864
956
|
};
|
|
865
957
|
}
|
|
866
958
|
|
|
867
|
-
const BackCaretLeft = backCaret(CaretLeftIcon);
|
|
868
|
-
const BackCaretRight = backCaret(CaretRightIcon);
|
|
959
|
+
const BackCaretLeft = backCaret(CaretLeftIcon, 1);
|
|
960
|
+
const BackCaretRight = backCaret(CaretRightIcon, -1);
|
|
869
961
|
|
|
870
962
|
export interface PageBackButtonProps extends Omit<ButtonProps, "children"> {
|
|
871
963
|
/** Where back goes, shown beside the caret (the list's name). */
|
package/src/views/ImageGrid.tsx
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
XStack,
|
|
31
31
|
YStack,
|
|
32
32
|
getTokenValue,
|
|
33
|
+
isWeb,
|
|
33
34
|
useWindowDimensions,
|
|
34
35
|
type YStackProps,
|
|
35
36
|
} from "tamagui";
|
|
@@ -251,6 +252,7 @@ function ImageGridTile({
|
|
|
251
252
|
data-focused={isFocused || undefined}
|
|
252
253
|
data-media-tile="image-grid"
|
|
253
254
|
position="relative"
|
|
255
|
+
flexGrow={1}
|
|
254
256
|
{...radiusFragment}
|
|
255
257
|
borderWidth={0}
|
|
256
258
|
cursor={clickable ? "pointer" : undefined}
|
|
@@ -264,8 +266,23 @@ function ImageGridTile({
|
|
|
264
266
|
onPress={onPress}
|
|
265
267
|
>
|
|
266
268
|
{/* Clip media to the radius; keep overflow off the ring node so
|
|
267
|
-
LC-71 outline is not severed (same split as Input.Box).
|
|
268
|
-
|
|
269
|
+
LC-71 outline is not severed (same split as Input.Box).
|
|
270
|
+
LC-179: every tile takes its row's height. On web the clip is a
|
|
271
|
+
one-cell grid so a card drawn inside stretches to that height. */}
|
|
272
|
+
<View
|
|
273
|
+
borderRadius={radius}
|
|
274
|
+
overflow="hidden"
|
|
275
|
+
flexGrow={1}
|
|
276
|
+
{...(isWeb
|
|
277
|
+
? {
|
|
278
|
+
style: {
|
|
279
|
+
display: "grid",
|
|
280
|
+
gridTemplateColumns: "minmax(0, 1fr)",
|
|
281
|
+
justifyItems: "stretch",
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
: undefined)}
|
|
285
|
+
>
|
|
269
286
|
<MediaRadiusContext.Provider value={radius}>{children}</MediaRadiusContext.Provider>
|
|
270
287
|
</View>
|
|
271
288
|
{hovered && !showRing ? (
|
|
@@ -3,7 +3,7 @@ import type { YStackProps } from "tamagui";
|
|
|
3
3
|
import type { ChartDatum, ChartDatumEvents } from "./types";
|
|
4
4
|
export interface BarChartProps extends ChartDatumEvents, Omit<YStackProps, "children" | "width" | "height"> {
|
|
5
5
|
data: ChartDatum[];
|
|
6
|
-
/** Drawing height as a theme size token or
|
|
6
|
+
/** Drawing height as a theme size token, pixels, or "fill" (default 200). */
|
|
7
7
|
height?: ChartHeight;
|
|
8
8
|
/** Explicit width bypasses responsive measurement. */
|
|
9
9
|
width?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.d.ts","sourceRoot":"","sources":["../../src/charts/BarChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAc3C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG5D,MAAM,WAAW,aACf,SAAQ,gBAAgB,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5E,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,
|
|
1
|
+
{"version":3,"file":"BarChart.d.ts","sourceRoot":"","sources":["../../src/charts/BarChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAc3C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG5D,MAAM,WAAW,aACf,SAAQ,gBAAgB,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5E,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,MAAY,EACZ,KAAK,EACL,WAAmB,EACnB,KAAK,EACL,MAAM,EACN,SAAgB,EAChB,SAAgB,EAChB,QAAe,EACf,MAAU,EACV,cAAc,EACd,KAAK,EACL,KAAK,EACL,MAAM,EAAE,UAAU,EAClB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,GAAG,UAAU,EACd,EAAE,aAAa,2CAmGf"}
|
|
@@ -14,9 +14,11 @@ export interface ChartDataViewProps {
|
|
|
14
14
|
/** One complete, reachable data view. The ordinary chart keeps its short name. */
|
|
15
15
|
export declare function ChartDataView({ data, title, valueFormatter, children }: ChartDataViewProps): import("react/jsx-runtime").JSX.Element;
|
|
16
16
|
/** Shared chart implementations join a composed header instead of adding a second action. */
|
|
17
|
-
export declare function ChartDataFrame({ data, title, valueFormatter, width, children, }: Omit<ChartDataViewProps, "children"> & {
|
|
17
|
+
export declare function ChartDataFrame({ data, title, valueFormatter, width, fill, children, }: Omit<ChartDataViewProps, "children"> & {
|
|
18
18
|
children: ReactNode;
|
|
19
19
|
width?: number;
|
|
20
|
+
/** The chart flexes to its region's height (LC-215). */
|
|
21
|
+
fill?: boolean;
|
|
20
22
|
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
21
23
|
export declare function useChartDataViewProps(): Record<string, unknown>;
|
|
22
24
|
//# sourceMappingURL=ChartDataView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartDataView.d.ts","sourceRoot":"","sources":["../../src/charts/ChartDataView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChartDataView.d.ts","sourceRoot":"","sources":["../../src/charts/ChartDataView.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAanF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,4EAA4E;IAC5E,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,MAAM,EAAE,SAAS,CAAC;QAClB,IAAI,EAAE,UAAU,EAAE,CAAC;QACnB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;KAC3C,KAAK,SAAS,CAAC;CACjB;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,EAAE,kBAAkB,2CA2F1F;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,KAAK,EACL,cAAc,EACd,KAAK,EACL,IAAI,EACJ,QAAQ,GACT,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,GAAG;IACxC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,2UAkCA;AAsDD,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartSurface.d.ts","sourceRoot":"","sources":["../../src/charts/ChartSurface.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC3F;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,SAAS,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,KAAK,EACL,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,SAAS,EAChB,QAAQ,EACR,GAAG,UAAU,EACd,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ChartSurface.d.ts","sourceRoot":"","sources":["../../src/charts/ChartSurface.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC3F;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,SAAS,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,KAAK,EACL,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,SAAS,EAChB,QAAQ,EACR,GAAG,UAAU,EACd,EAAE,iBAAiB,2CAsCnB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type SizeKey } from "@multiplatform.one/theme";
|
|
2
|
-
|
|
2
|
+
/** `"fill"` flexes the chart to the height its region hands it (LC-215). */
|
|
3
|
+
export type ChartHeight = number | SizeKey | "fill";
|
|
3
4
|
export declare function useChartHeight(height: ChartHeight | undefined, fallback: number): number;
|
|
4
5
|
//# sourceMappingURL=chartHeight.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chartHeight.d.ts","sourceRoot":"","sources":["../../src/charts/chartHeight.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEhG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"chartHeight.d.ts","sourceRoot":"","sources":["../../src/charts/chartHeight.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEhG,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpD,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CASxF"}
|