Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/content/docs/charts/heatmap-chart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ Rows that are hours and columns that are weekdays. “When is this busy” is a
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` | — | |
| `width` | `number` | `26` | Width reserved for the labels. The grid is sized around it. |
| `width` | `number` | — | Width reserved for the labels. The grid is sized around it. Defaults to 26, and widens with the system text size so a larger weekday does not wrap. |
| `tickFilter` | `'all' \| 'odd' \| 'even'` | `'odd'` | Which rows get a label. Every other row is the usual choice. |
| `labelFormat` | `'initial' \| 'full'` | `'full'` | `initial` is the single letter; `full` is the abbreviated name. |
| `labels` | `string[]` | — | Row labels, top to bottom. Overrides the weekday names — for a grid whose rows are not days. |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/public/r/area-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/bar-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/bubble-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/bump-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/candlestick-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/chart-accessibility.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
"path": "ui/chart-accessibility.ts",
"type": "registry:ui",
"content": "import { createElement } from 'react';\n\nexport interface ChartAccessibilityProps<Datum> {\n /** Spoken summary. Defaults to the chart family and number of data entries. */\n accessibilityLabel?: string;\n /** Additional guidance spoken after the chart summary. */\n accessibilityHint?: string;\n /** Overrides the spoken label for one data entry. */\n accessibilityLabelForDatum?: (datum: Datum, index: number) => string;\n /** Makes each data entry actionable without making the SVG geometry focusable. */\n onAccessibilityDatumPress?: (datum: Datum, index: number) => void;\n}\n\nexport interface ChartAccessibilityItem<Datum> {\n datum: Datum;\n index: number;\n label: string;\n}\n\nfunction spoken(value: unknown): string | null {\n if (value instanceof Date) return value.toLocaleDateString();\n if (typeof value === 'string' || typeof value === 'number') return String(value);\n return null;\n}\n\n/** Pure semantic projection, shared by tests and the native accessibility tree. */\nexport function chartAccessibilityModel<Datum>(\n chart: string,\n data: Datum[],\n valueOf: (datum: Datum) => [string, unknown][],\n labelForDatum?: (datum: Datum, index: number) => string\n): { summary: string; items: ChartAccessibilityItem<Datum>[] } {\n return {\n summary: `${chart}, ${data.length} ${data.length === 1 ? 'data entry' : 'data entries'}`,\n items: data.map((datum, index) => ({\n datum,\n index,\n label:\n labelForDatum?.(datum, index) ??\n valueOf(datum)\n .map(([label, value]) => {\n const text = spoken(value);\n return text === null ? null : `${label}, ${text}`;\n })\n .filter((part): part is string => part !== null)\n .join('. '),\n })),\n };\n}\n\nexport interface ChartAccessibilityDataProps<Datum>\n extends ChartAccessibilityProps<Datum> {\n chart: string;\n data: Datum[];\n valueOf: (datum: Datum) => [string, unknown][];\n disabled?: boolean;\n}\n\n/**\n * A compact semantic sibling to the visual plot. The SVG remains one\n * decorative subtree while screen readers receive one summary and one entry\n * per datum, rather than every path, grid line and marker.\n */\nexport function ChartAccessibilityData<Datum>({\n chart,\n data,\n valueOf,\n disabled = false,\n accessibilityLabel,\n accessibilityHint,\n accessibilityLabelForDatum,\n onAccessibilityDatumPress,\n}: ChartAccessibilityDataProps<Datum>) {\n if (disabled) return null;\n // Keep the pure semantic projection importable by Node tests without asking\n // Node to parse React Native's Flow entry point.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { Pressable, View } = require('react-native') as typeof import('react-native');\n const model = chartAccessibilityModel(chart, data, valueOf, accessibilityLabelForDatum);\n\n return createElement(\n View,\n { style: { position: 'absolute', left: -10_000, width: 1, height: 1 } },\n createElement(View, {\n accessible: true,\n accessibilityRole: 'image',\n accessibilityLabel: accessibilityLabel ?? model.summary,\n accessibilityHint,\n }),\n ...model.items.map((item) =>\n createElement(Pressable, {\n key: item.index,\n accessible: true,\n accessibilityRole: onAccessibilityDatumPress ? 'button' : 'text',\n accessibilityLabel: item.label,\n onPress: onAccessibilityDatumPress\n ? () => onAccessibilityDatumPress(item.datum, item.index)\n : undefined,\n })\n )\n );\n}\n"
"content": "import { createElement } from 'react';\n\n/**\n * The largest text scale a chart's axis labels follow: iOS's biggest size before\n * the accessibility range. Axis labels sit in fixed-width gutters at fixed\n * steps, so past this they overlap each other or clip. Screen readers still get\n * every value through the chart's accessibility data.\n */\nexport const CHART_AXIS_MAX_FONT_SCALE = 1.353;\n\nexport interface ChartAccessibilityProps<Datum> {\n /** Spoken summary. Defaults to the chart family and number of data entries. */\n accessibilityLabel?: string;\n /** Additional guidance spoken after the chart summary. */\n accessibilityHint?: string;\n /** Overrides the spoken label for one data entry. */\n accessibilityLabelForDatum?: (datum: Datum, index: number) => string;\n /** Makes each data entry actionable without making the SVG geometry focusable. */\n onAccessibilityDatumPress?: (datum: Datum, index: number) => void;\n}\n\nexport interface ChartAccessibilityItem<Datum> {\n datum: Datum;\n index: number;\n label: string;\n}\n\nfunction spoken(value: unknown): string | null {\n if (value instanceof Date) return value.toLocaleDateString();\n if (typeof value === 'string' || typeof value === 'number') return String(value);\n return null;\n}\n\n/** Pure semantic projection, shared by tests and the native accessibility tree. */\nexport function chartAccessibilityModel<Datum>(\n chart: string,\n data: Datum[],\n valueOf: (datum: Datum) => [string, unknown][],\n labelForDatum?: (datum: Datum, index: number) => string\n): { summary: string; items: ChartAccessibilityItem<Datum>[] } {\n return {\n summary: `${chart}, ${data.length} ${data.length === 1 ? 'data entry' : 'data entries'}`,\n items: data.map((datum, index) => ({\n datum,\n index,\n label:\n labelForDatum?.(datum, index) ??\n valueOf(datum)\n .map(([label, value]) => {\n const text = spoken(value);\n return text === null ? null : `${label}, ${text}`;\n })\n .filter((part): part is string => part !== null)\n .join('. '),\n })),\n };\n}\n\nexport interface ChartAccessibilityDataProps<Datum>\n extends ChartAccessibilityProps<Datum> {\n chart: string;\n data: Datum[];\n valueOf: (datum: Datum) => [string, unknown][];\n disabled?: boolean;\n}\n\n/**\n * A compact semantic sibling to the visual plot. The SVG remains one\n * decorative subtree while screen readers receive one summary and one entry\n * per datum, rather than every path, grid line and marker.\n */\nexport function ChartAccessibilityData<Datum>({\n chart,\n data,\n valueOf,\n disabled = false,\n accessibilityLabel,\n accessibilityHint,\n accessibilityLabelForDatum,\n onAccessibilityDatumPress,\n}: ChartAccessibilityDataProps<Datum>) {\n if (disabled) return null;\n // Keep the pure semantic projection importable by Node tests without asking\n // Node to parse React Native's Flow entry point.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { Pressable, View } = require('react-native') as typeof import('react-native');\n const model = chartAccessibilityModel(chart, data, valueOf, accessibilityLabelForDatum);\n\n return createElement(\n View,\n { style: { position: 'absolute', left: -10_000, width: 1, height: 1 } },\n createElement(View, {\n accessible: true,\n accessibilityRole: 'image',\n accessibilityLabel: accessibilityLabel ?? model.summary,\n accessibilityHint,\n }),\n ...model.items.map((item) =>\n createElement(Pressable, {\n key: item.index,\n accessible: true,\n accessibilityRole: onAccessibilityDatumPress ? 'button' : 'text',\n accessibilityLabel: item.label,\n onPress: onAccessibilityDatumPress\n ? () => onAccessibilityDatumPress(item.datum, item.index)\n : undefined,\n })\n )\n );\n}\n"
}
]
}
2 changes: 1 addition & 1 deletion apps/docs/public/r/heatmap-chart.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/docs/public/r/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@
"description": "A reading that keeps arriving, against a window that keeps moving.",
"registryDependencies": [
"chart",
"chart-accessibility",
"cn",
"text"
],
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/public/r/line-chart.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion apps/docs/public/r/live-line-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/mirror-area-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/plot.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/pyramid-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/scatter-chart.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/public/r/waterfall-chart.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Extends `ViewProps, ChartAccessibilityProps<HeatmapCell>`.
| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `className` | `string` | — | — |
| `width` | `number` | `26` | Width reserved for the labels. The grid is sized around it. |
| `width` | `number` | — | Width reserved for the labels. The grid is sized around it. Defaults to 26, and widens with the system text size so a larger weekday does not wrap. |
| `tickFilter` | `'all' \| 'odd' \| 'even'` | `odd` | Which rows get a label. Every other row is the usual choice. |
| `labelFormat` | `'initial' \| 'full'` | `full` | `initial` is the single letter; `full` is the abbreviated name. |
| `labels` | `string[]` | — | Row labels, top to bottom. Overrides the weekday names — for a grid whose rows are not days. |
Expand Down
4 changes: 2 additions & 2 deletions packages/panelui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ import { Scrim } from 'panelui-native/primitives/scrim';
```

The component subpath is the slug from its documentation URL. The public utilities are `cn`,
`color` and `time`; the other foundation leaves are `animated-pressable`, `keyboard-avoider` and
`scroll-progress` under `panelui-native/primitives/`.
`color` and `time`; the other foundation leaves are `animated-pressable`, `glass`,
`keyboard-avoider`, `portal` and `scroll-progress` under `panelui-native/primitives/`.

**Prefer the subpath for anything you ship.** The root entry re-exports all 122 components and
Metro does not tree-shake, so one name from it loads every component and everything they import —
Expand Down
10 changes: 10 additions & 0 deletions packages/panelui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,21 @@
"react-native": "./src/primitives/animated-pressable.tsx",
"default": "./lib/module/primitives/animated-pressable.js"
},
"./primitives/glass": {
"types": "./lib/typescript/src/primitives/glass.d.ts",
"react-native": "./src/primitives/glass.tsx",
"default": "./lib/module/primitives/glass.js"
},
"./primitives/keyboard-avoider": {
"types": "./lib/typescript/src/primitives/keyboard-avoider.d.ts",
"react-native": "./src/primitives/keyboard-avoider.tsx",
"default": "./lib/module/primitives/keyboard-avoider.js"
},
"./primitives/portal": {
"types": "./lib/typescript/src/primitives/portal.d.ts",
"react-native": "./src/primitives/portal.tsx",
"default": "./lib/module/primitives/portal.js"
},
"./primitives/scrim": {
"types": "./lib/typescript/src/primitives/scrim.d.ts",
"react-native": "./src/primitives/scrim.tsx",
Expand Down
2 changes: 2 additions & 0 deletions packages/panelui/scripts/generate-subpath-exports.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ const leaves = [
['./icons', 'icons/index.tsx'],
['./primitives/text', 'primitives/text.tsx'],
['./primitives/animated-pressable', 'primitives/animated-pressable.tsx'],
['./primitives/glass', 'primitives/glass.tsx'],
['./primitives/keyboard-avoider', 'primitives/keyboard-avoider.tsx'],
['./primitives/portal', 'primitives/portal.tsx'],
['./primitives/scrim', 'primitives/scrim.tsx'],
['./primitives/scroll-progress', 'primitives/scroll-progress.tsx'],
['./utils/cn', 'utils/cn.ts'],
Expand Down
15 changes: 13 additions & 2 deletions packages/panelui/src/components/area-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ import Svg, {
} from 'react-native-svg';
import { useCSSVariable } from 'uniwind';
import { Text } from '../../primitives/text';
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
import {
CHART_AXIS_MAX_FONT_SCALE,
ChartAccessibilityData,
type ChartAccessibilityProps,
} from '../../primitives/chart-accessibility';
import {
areaPath,
columnValues,
Expand Down Expand Up @@ -829,6 +833,7 @@ function AreaChartXAxis({ ticks = 4, format, className }: AreaChartXAxisProps) {
width: POINT_LABEL_WIDTH,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
Expand Down Expand Up @@ -877,7 +882,13 @@ function AreaChartYAxis({ ticks = 4, format, className }: AreaChartYAxisProps) {
className={cn('items-end justify-between', className)}
>
{labels.map((label) => (
<Text key={label.key} size="xs" muted numberOfLines={1}>
<Text
key={label.key}
size="xs"
muted
numberOfLines={1}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
))}
Expand Down
9 changes: 7 additions & 2 deletions packages/panelui/src/components/bar-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ import Animated, {
import Svg, { Defs, G, Line as SvgLine, LinearGradient, Path, Stop } from 'react-native-svg';
import { useCSSVariable } from 'uniwind';
import { Text } from '../../primitives/text';
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
import {
CHART_AXIS_MAX_FONT_SCALE,
ChartAccessibilityData,
type ChartAccessibilityProps,
} from '../../primitives/chart-accessibility';
import {
bandOf,
barPath,
Expand Down Expand Up @@ -980,6 +984,7 @@ function BarChartXAxis({ ticks, format, className }: BarChartXAxisProps) {
width: bandWidth,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
Expand Down Expand Up @@ -1053,7 +1058,7 @@ function BarChartYAxis({ ticks = 4, format, className }: BarChartYAxisProps) {
key={label.key}
className={horizontal ? 'flex-1 justify-center' : undefined}
>
<Text size="xs" muted numberOfLines={1}>
<Text size="xs" muted numberOfLines={1} maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}>
{label.text}
</Text>
</View>
Expand Down
19 changes: 17 additions & 2 deletions packages/panelui/src/components/bubble-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import Svg, { Circle, G, Line as SvgLine } from 'react-native-svg';
import { useCSSVariable } from 'uniwind';
import { useSkeletonHandoff } from '../../hooks/use-skeleton-handoff';
import {
CHART_AXIS_MAX_FONT_SCALE,
ChartAccessibilityData,
type ChartAccessibilityProps,
} from '../../primitives/chart-accessibility';
Expand Down Expand Up @@ -1649,6 +1650,7 @@ function BubbleChartXAxis({ ticks = 4, format, label, className }: BubbleChartXA
width: plot.width,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label}
</Text>
Expand Down Expand Up @@ -1676,6 +1678,7 @@ function BubbleChartXAxis({ ticks = 4, format, label, className }: BubbleChartXA
width: AXIS_LABEL_WIDTH,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{tick.text}
</Text>
Expand Down Expand Up @@ -1745,7 +1748,13 @@ function BubbleChartYAxis({ ticks = 4, format, label, className }: BubbleChartYA
transform: [{ rotate: '-90deg' }],
}}
>
<Text size="xs" muted numberOfLines={1} style={{ textAlign: 'center' }}>
<Text
size="xs"
muted
numberOfLines={1}
style={{ textAlign: 'center' }}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label}
</Text>
</View>
Expand All @@ -1765,7 +1774,13 @@ function BubbleChartYAxis({ ticks = 4, format, label, className }: BubbleChartYA
className={cn('items-end justify-between', className)}
>
{labels.map((tick) => (
<Text key={tick.key} size="xs" muted numberOfLines={1}>
<Text
key={tick.key}
size="xs"
muted
numberOfLines={1}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{tick.text}
</Text>
))}
Expand Down
15 changes: 13 additions & 2 deletions packages/panelui/src/components/bump-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ import Animated, {
import Svg, { Defs, G, Line as SvgLine, LinearGradient, Path, Rect, Stop } from 'react-native-svg';
import { useCSSVariable } from 'uniwind';
import { Text } from '../../primitives/text';
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
import {
CHART_AXIS_MAX_FONT_SCALE,
ChartAccessibilityData,
type ChartAccessibilityProps,
} from '../../primitives/chart-accessibility';
import { useControllableState } from '../../primitives/controllable-state';
import { finiteChartNumber } from '../../primitives/finite-chart';
import {
Expand Down Expand Up @@ -997,6 +1001,7 @@ function BumpChartXAxis({ ticks = 4, format, className }: BumpChartXAxisProps) {
width: POINT_LABEL_WIDTH,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
Expand Down Expand Up @@ -1030,7 +1035,13 @@ function BumpChartYAxis({ format, className }: BumpChartYAxisProps) {
className={cn(rows <= 1 ? 'items-end justify-center' : 'items-end justify-between', className)}
>
{Array.from({ length: rows }, (_unused, index) => (
<Text key={index} size="xs" muted numberOfLines={1}>
<Text
key={index}
size="xs"
muted
numberOfLines={1}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{format ? format(index + 1) : `#${index + 1}`}
</Text>
))}
Expand Down
16 changes: 14 additions & 2 deletions packages/panelui/src/components/candlestick-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ import Animated, {
import Svg, { G, Line as SvgLine, Path } from 'react-native-svg';
import { useCSSVariable } from 'uniwind';
import { Text } from '../../primitives/text';
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
import {
CHART_AXIS_MAX_FONT_SCALE,
ChartAccessibilityData,
type ChartAccessibilityProps,
} from '../../primitives/chart-accessibility';
import { bandOf, compactNumber, yOf, type Plot } from '../../utils/chart';
import { cn } from '../../utils/cn';

Expand Down Expand Up @@ -1009,6 +1013,7 @@ function CandlestickChartXAxis({
width: LABEL_BOX,
textAlign: 'center',
}}
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
Expand Down Expand Up @@ -1067,7 +1072,14 @@ function CandlestickChartYAxis({
className={cn(className)}
>
{labels.map((label) => (
<Text key={label.key} size="xs" muted numberOfLines={1} className="text-right">
<Text
key={label.key}
size="xs"
muted
numberOfLines={1}
className="text-right"
maxFontSizeMultiplier={CHART_AXIS_MAX_FONT_SCALE}
>
{label.text}
</Text>
))}
Expand Down
Loading
Loading