@chipmobilesdk/rn-chart 0.2.0 → 0.3.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +164 -115
  2. package/README.md +391 -381
  3. package/package.json +49 -49
  4. package/skills/sdk-rn-chart/SKILL.md +68 -69
  5. package/src/a11y/summary.ts +190 -177
  6. package/src/contract/datasource.ts +126 -136
  7. package/src/contract/errors.ts +129 -133
  8. package/src/contract/format.ts +168 -168
  9. package/src/contract/interaction.ts +70 -71
  10. package/src/contract/marker.ts +75 -75
  11. package/src/contract/props.ts +64 -60
  12. package/src/contract/state.ts +88 -94
  13. package/src/contract/strings.ts +48 -0
  14. package/src/contract/theme.ts +169 -177
  15. package/src/contract/value.ts +121 -125
  16. package/src/geometry/bar.ts +271 -273
  17. package/src/geometry/distribution.ts +197 -203
  18. package/src/geometry/heatmap.ts +189 -195
  19. package/src/geometry/hitTest.ts +117 -120
  20. package/src/geometry/layout.ts +162 -173
  21. package/src/geometry/markers.ts +55 -57
  22. package/src/geometry/path.ts +193 -199
  23. package/src/geometry/progress.ts +223 -230
  24. package/src/geometry/scale.ts +140 -146
  25. package/src/geometry/ticks.ts +145 -153
  26. package/src/geometry/timeSeries.ts +168 -168
  27. package/src/geometry/types.ts +94 -94
  28. package/src/index.ts +162 -176
  29. package/src/react/ChartBoundary.tsx +96 -96
  30. package/src/react/ChartFrame.tsx +596 -622
  31. package/src/react/ChartLegend.tsx +216 -215
  32. package/src/react/ChartThemeProvider.tsx +44 -48
  33. package/src/react/charts.tsx +154 -161
  34. package/src/react/useChartLayout.ts +124 -124
  35. package/src/react/useChartSelection.ts +152 -170
  36. package/src/render/Axis.tsx +120 -122
  37. package/src/render/Marks.tsx +173 -205
  38. package/src/validation/budgets.ts +113 -110
  39. package/src/validation/validateDatasource.ts +174 -174
@@ -1,60 +1,64 @@
1
- /**
2
- * Props shared by every chart family.
3
- *
4
- * See specs/007-rn-chart-package/contracts/public-api.md §2 for the contract
5
- * behind each field.
6
- */
7
- import type { ReactNode } from 'react';
8
- import type { StyleProp, ViewStyle } from 'react-native';
9
-
10
- import type { ChartDatasource } from './datasource';
11
- import type { ChartFailureHandler } from './errors';
12
- import type { ChartFormatters, FormatContext } from './format';
13
- import type { InteractionProps } from './interaction';
14
- import type { ChartStateContent, DeclarableChartStatus } from './state';
15
- import type { ChartTheme } from './theme';
16
- import type { ValueState } from './value';
17
-
18
- export interface ChartAccessibility<M = unknown> {
19
- /** Replaces the composed summary entirely. */
20
- readonly label?: string;
21
- readonly summaryFormatter?: (
22
- datasource: ChartDatasource<M>,
23
- ctx: FormatContext,
24
- ) => string;
25
- /** Defaults to true when `onSelectionChange` is supplied. */
26
- readonly interactive?: boolean;
27
- }
28
-
29
- export interface BaseChartProps<M = unknown> extends InteractionProps<M> {
30
- /**
31
- * Must be referentially stable across renders, or memoization is defeated.
32
- * Hold it in state or a memo rather than building it inline.
33
- */
34
- readonly datasource: ChartDatasource<M>;
35
-
36
- /**
37
- * `empty` and `partial` are derived from the data and are not accepted here
38
- * (FR-036).
39
- */
40
- readonly status?: DeclarableChartStatus;
41
- readonly stateContent?: ChartStateContent;
42
-
43
- readonly chartId?: string;
44
- readonly onFailure?: ChartFailureHandler;
45
-
46
- readonly theme?: ChartTheme;
47
- readonly formatters?: ChartFormatters;
48
- readonly title?: string;
49
- readonly legend?: boolean | { readonly position: 'top' | 'bottom' };
50
-
51
- readonly accessibility?: ChartAccessibility<M>;
52
-
53
- /** Width always comes from the container. */
54
- readonly height?: number;
55
- readonly style?: StyleProp<ViewStyle>;
56
- /** Rendered below the plot, inside the chart's own bounds. */
57
- readonly footer?: ReactNode;
58
- }
59
-
60
- export type ValueFormatter = (value: ValueState) => string;
1
+ /**
2
+ * Props shared by every chart family.
3
+ *
4
+ * See specs/007-rn-chart-package/contracts/public-api.md §2 for the contract
5
+ * behind each field.
6
+ */
7
+ import type { ChartStrings } from './strings';
8
+ import type { ReactNode } from 'react';
9
+ import type { StyleProp, ViewStyle } from 'react-native';
10
+
11
+ import type { ChartDatasource } from './datasource';
12
+ import type { ChartFailureHandler } from './errors';
13
+ import type { ChartFormatters, FormatContext } from './format';
14
+ import type { InteractionProps } from './interaction';
15
+ import type { ChartStateContent, DeclarableChartStatus } from './state';
16
+ import type { ChartTheme } from './theme';
17
+ import type { ValueState } from './value';
18
+
19
+ export interface ChartAccessibility<M = unknown> {
20
+ /** Replaces the composed summary entirely. */
21
+ readonly label?: string;
22
+ readonly summaryFormatter?: (datasource: ChartDatasource<M>, ctx: FormatContext) => string;
23
+ /** Defaults to true when `onSelectionChange` is supplied. */
24
+ readonly interactive?: boolean;
25
+ }
26
+
27
+ export interface BaseChartProps<M = unknown> extends InteractionProps<M> {
28
+ /**
29
+ * Must be referentially stable across renders, or memoization is defeated.
30
+ * Hold it in state or a memo rather than building it inline.
31
+ */
32
+ readonly datasource: ChartDatasource<M>;
33
+
34
+ /**
35
+ * `empty` and `partial` are derived from the data and are not accepted here
36
+ * (FR-036).
37
+ */
38
+ readonly status?: DeclarableChartStatus;
39
+ readonly stateContent?: ChartStateContent;
40
+
41
+ readonly chartId?: string;
42
+ readonly onFailure?: ChartFailureHandler;
43
+
44
+ readonly theme?: ChartTheme;
45
+ readonly formatters?: ChartFormatters;
46
+ readonly title?: string;
47
+ readonly legend?: boolean | { readonly position: 'top' | 'bottom' };
48
+
49
+ readonly accessibility?: ChartAccessibility<M>;
50
+ /**
51
+ * The package's own texts in the app's language: summaries, announcements,
52
+ * action labels, built-in state text, legend hints (`chartStrings` holds the
53
+ * keys and English defaults).
54
+ */
55
+ readonly strings?: Partial<ChartStrings>;
56
+
57
+ /** Width always comes from the container. */
58
+ readonly height?: number;
59
+ readonly style?: StyleProp<ViewStyle>;
60
+ /** Rendered below the plot, inside the chart's own bounds. */
61
+ readonly footer?: ReactNode;
62
+ }
63
+
64
+ export type ValueFormatter = (value: ValueState) => string;
@@ -1,94 +1,88 @@
1
- /**
2
- * Chart status contract (FR-029 to FR-038).
3
- *
4
- * Ownership is split on a simple principle: the package derives what only the
5
- * data can determine, and the app declares what only the app can know.
6
- *
7
- * empty, partial -> derived from the datasource
8
- * loading, stale, -> declared by the app: it alone knows about its own
9
- * noPermission, fetches, cache age, access rules, and failures
10
- * error
11
- */
12
- import type { ReactNode } from 'react';
13
-
14
- import type { ChartError } from './errors';
15
-
16
- export type ChartStatus =
17
- | 'loading'
18
- | 'empty'
19
- | 'ready'
20
- | 'error'
21
- | 'stale'
22
- | 'noPermission'
23
- | 'partial';
24
-
25
- /** The statuses an app may declare. `empty` and `partial` are derived. */
26
- export type DeclarableChartStatus = Exclude<ChartStatus, 'empty' | 'partial'>;
27
-
28
- /**
29
- * Resolution precedence, highest first.
30
- *
31
- * `noPermission` outranks everything so restricted values can never slip
32
- * through some other state. `stale` and `partial` sit below the blocking
33
- * states because they are modifiers on a chart that still draws its data.
34
- */
35
- export const STATUS_PRECEDENCE: readonly ChartStatus[] = [
36
- 'noPermission',
37
- 'error',
38
- 'loading',
39
- 'empty',
40
- 'stale',
41
- 'partial',
42
- 'ready',
43
- ];
44
-
45
- export interface StatusInputs {
46
- /** What the app declared, if anything. */
47
- readonly declared?: DeclarableChartStatus;
48
- /** True when the datasource carries no points at all. */
49
- readonly isEmpty: boolean;
50
- /** True when some points are valid and others are unknown/n-a/withheld. */
51
- readonly isPartial: boolean;
52
- /** A validation failure or a contained render exception. */
53
- readonly error?: ChartError;
54
- }
55
-
56
- export function resolveStatus(inputs: StatusInputs): ChartStatus {
57
- const candidates = new Set<ChartStatus>(['ready']);
58
-
59
- if (inputs.declared) {
60
- candidates.add(inputs.declared);
61
- }
62
- if (inputs.error) {
63
- candidates.add('error');
64
- }
65
- if (inputs.isEmpty) {
66
- candidates.add('empty');
67
- }
68
- if (inputs.isPartial) {
69
- candidates.add('partial');
70
- }
71
-
72
- for (const status of STATUS_PRECEDENCE) {
73
- if (candidates.has(status)) {
74
- return status;
75
- }
76
- }
77
- return 'ready';
78
- }
79
-
80
- /** Statuses in which the chart draws its data (possibly with an indicator). */
81
- export function drawsData(status: ChartStatus): boolean {
82
- return status === 'ready' || status === 'stale' || status === 'partial';
83
- }
84
-
85
- /**
86
- * Statuses in which no data-selection callback may fire, because the chart is
87
- * not displaying the values a callback would carry (FR-053).
88
- */
89
- export function suppressesInteraction(status: ChartStatus): boolean {
90
- return !drawsData(status);
91
- }
92
-
93
- /** Per-status content the app supplies, replacing any package wording (FR-035). */
94
- export type ChartStateContent = Partial<Record<ChartStatus, ReactNode>>;
1
+ /**
2
+ * Chart status contract (FR-029 to FR-038).
3
+ *
4
+ * Ownership is split on a simple principle: the package derives what only the
5
+ * data can determine, and the app declares what only the app can know.
6
+ *
7
+ * empty, partial -> derived from the datasource
8
+ * loading, stale, -> declared by the app: it alone knows about its own
9
+ * noPermission, fetches, cache age, access rules, and failures
10
+ * error
11
+ */
12
+ import type { ReactNode } from 'react';
13
+
14
+ import type { ChartError } from './errors';
15
+
16
+ export type ChartStatus =
17
+ 'loading' | 'empty' | 'ready' | 'error' | 'stale' | 'noPermission' | 'partial';
18
+
19
+ /** The statuses an app may declare. `empty` and `partial` are derived. */
20
+ export type DeclarableChartStatus = Exclude<ChartStatus, 'empty' | 'partial'>;
21
+
22
+ /**
23
+ * Resolution precedence, highest first.
24
+ *
25
+ * `noPermission` outranks everything so restricted values can never slip
26
+ * through some other state. `stale` and `partial` sit below the blocking
27
+ * states because they are modifiers on a chart that still draws its data.
28
+ */
29
+ export const STATUS_PRECEDENCE: readonly ChartStatus[] = [
30
+ 'noPermission',
31
+ 'error',
32
+ 'loading',
33
+ 'empty',
34
+ 'stale',
35
+ 'partial',
36
+ 'ready',
37
+ ];
38
+
39
+ export interface StatusInputs {
40
+ /** What the app declared, if anything. */
41
+ readonly declared?: DeclarableChartStatus;
42
+ /** True when the datasource carries no points at all. */
43
+ readonly isEmpty: boolean;
44
+ /** True when some points are valid and others are unknown/n-a/withheld. */
45
+ readonly isPartial: boolean;
46
+ /** A validation failure or a contained render exception. */
47
+ readonly error?: ChartError;
48
+ }
49
+
50
+ export function resolveStatus(inputs: StatusInputs): ChartStatus {
51
+ const candidates = new Set<ChartStatus>(['ready']);
52
+
53
+ if (inputs.declared) {
54
+ candidates.add(inputs.declared);
55
+ }
56
+ if (inputs.error) {
57
+ candidates.add('error');
58
+ }
59
+ if (inputs.isEmpty) {
60
+ candidates.add('empty');
61
+ }
62
+ if (inputs.isPartial) {
63
+ candidates.add('partial');
64
+ }
65
+
66
+ for (const status of STATUS_PRECEDENCE) {
67
+ if (candidates.has(status)) {
68
+ return status;
69
+ }
70
+ }
71
+ return 'ready';
72
+ }
73
+
74
+ /** Statuses in which the chart draws its data (possibly with an indicator). */
75
+ export function drawsData(status: ChartStatus): boolean {
76
+ return status === 'ready' || status === 'stale' || status === 'partial';
77
+ }
78
+
79
+ /**
80
+ * Statuses in which no data-selection callback may fire, because the chart is
81
+ * not displaying the values a callback would carry (FR-053).
82
+ */
83
+ export function suppressesInteraction(status: ChartStatus): boolean {
84
+ return !drawsData(status);
85
+ }
86
+
87
+ /** Per-status content the app supplies, replacing any package wording (FR-035). */
88
+ export type ChartStateContent = Partial<Record<ChartStatus, ReactNode>>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Every text the package itself speaks or shows (CHT-05): screen-reader
3
+ * summaries and announcements, adjustable-action labels, built-in state text and
4
+ * legend hints. English defaults; an app translates them by passing `strings`
5
+ * (or by registering `chartStrings.user` with rn-i18n and passing the translated
6
+ * texts). `{name}` marks a variable.
7
+ */
8
+ import { defineMessages } from '@chipmobilesdk/rn-core';
9
+
10
+ export const chartStrings = defineMessages({
11
+ user: {
12
+ 'chart.family.progress': 'progress chart',
13
+ 'chart.family.distribution': 'distribution chart',
14
+ 'chart.family.timeSeries': 'time series chart',
15
+ 'chart.family.bar': 'bar chart',
16
+ 'chart.family.heatmap': 'heatmap',
17
+ 'chart.status.loading': 'Loading data.',
18
+ 'chart.status.empty': 'No data yet.',
19
+ 'chart.status.error': 'Data could not be shown.',
20
+ 'chart.status.stale': 'Showing data that may be out of date.',
21
+ 'chart.status.noPermission': 'This data is not available to you.',
22
+ 'chart.status.partial': 'Some data is missing.',
23
+ 'chart.summary.heading': '{title}, {noun}.',
24
+ 'chart.summary.headingUntitled': '{noun}.',
25
+ 'chart.summary.scale': '{series} series, {points} points.',
26
+ 'chart.summary.scaleSingle': '{points} points.',
27
+ 'chart.summary.single': 'Value {value}.',
28
+ 'chart.summary.extremes': 'Highest {maxLabel} at {max}. Lowest {minLabel} at {min}.',
29
+ 'chart.summary.marker': '{name} {value}',
30
+ 'chart.summary.incomplete': 'Some values are missing, so totals are incomplete.',
31
+ 'chart.point.withheld': '{position}, {series}, not available.',
32
+ 'chart.point.noData': '{position}, {series}, no data.',
33
+ 'chart.point.value': '{position}, {series}, {value}.',
34
+ 'chart.total': 'Total {value}.',
35
+ 'chart.total.incomplete': 'Total {value}, excluding {count} values that are not available.',
36
+ 'chart.action.next': 'Next data point',
37
+ 'chart.action.previous': 'Previous data point',
38
+ 'chart.state.loading': 'Loading…',
39
+ 'chart.state.empty': 'No data yet',
40
+ 'chart.state.error': 'Unable to show this chart',
41
+ 'chart.state.noPermission': 'Not available',
42
+ 'chart.legend.hide': 'Hides this series',
43
+ 'chart.legend.show': 'Shows this series',
44
+ },
45
+ });
46
+
47
+ export type ChartStrings = typeof chartStrings.defaults;
48
+ export type ChartStringKey = keyof ChartStrings;