@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
package/src/index.ts CHANGED
@@ -1,176 +1,162 @@
1
- /**
2
- * @chipmobilesdk/rn-chart — domain-agnostic charting for ChipMobileSdk-family
3
- * React Native apps.
4
- *
5
- * This is the single public entrypoint (LIB-003). Modules under `geometry/`,
6
- * `render/`, `validation/`, and the internals of `react/` are private; import
7
- * them only through this file. Keeping them private is what lets the geometry,
8
- * the layout maths, and the rendering substrate change without a breaking
9
- * release.
10
- *
11
- * The package makes no network request, calls no AI service, reads no
12
- * database, persists nothing, and logs no data value or series label. It
13
- * renders numeric data and app-supplied labels — nothing else.
14
- *
15
- * There is deliberately **no insight, message, or narrative surface**: apps
16
- * render their own text around a chart, and a chart imposes no outer margin
17
- * that would fight that layout.
18
- */
19
-
20
- // ---------------------------------------------------------------------------
21
- // Value semantics — the foundation of the contract
22
- // ---------------------------------------------------------------------------
23
- export type {
24
- KnownValue,
25
- NotApplicableValue,
26
- TotalResult,
27
- UnknownValue,
28
- ValueKind,
29
- ValueState,
30
- WithheldValue,
31
- } from './contract/value';
32
- export {
33
- isKnown,
34
- isWithheld,
35
- known,
36
- notApplicable,
37
- participatesInTotals,
38
- sumKnown,
39
- unknown,
40
- withheld,
41
- } from './contract/value';
42
-
43
- // ---------------------------------------------------------------------------
44
- // Datasource contract
45
- // ---------------------------------------------------------------------------
46
- export type {
47
- CategoryPosition,
48
- ChartDatasource,
49
- ChartFamily,
50
- DataPoint,
51
- InstantPosition,
52
- MatrixPosition,
53
- Position,
54
- PositionKind,
55
- Series,
56
- SeriesPattern,
57
- } from './contract/datasource';
58
- export {
59
- FAMILY_POSITION_KINDS,
60
- familyAcceptsPositionKind,
61
- resolveFormat,
62
- resolveUnit,
63
- } from './contract/datasource';
64
-
65
- export type { MarkerKind, ProgressReading, ReferenceMarker } from './contract/marker';
66
- export { findTarget, isUsableTarget, readProgress } from './contract/marker';
67
-
68
- // ---------------------------------------------------------------------------
69
- // Formatting
70
- // ---------------------------------------------------------------------------
71
- export type {
72
- ChartFormatters,
73
- FormatContext,
74
- FormatHint,
75
- SeriesRef,
76
- } from './contract/format';
77
- export {
78
- NON_VALUE_TEXT,
79
- formatLegendDefault,
80
- formatPositionDefault,
81
- formatValueDefault,
82
- seriesRef,
83
- } from './contract/format';
84
-
85
- // ---------------------------------------------------------------------------
86
- // State and errors
87
- // ---------------------------------------------------------------------------
88
- export type {
89
- ChartStateContent,
90
- ChartStatus,
91
- DeclarableChartStatus,
92
- StatusInputs,
93
- } from './contract/state';
94
- export {
95
- STATUS_PRECEDENCE,
96
- drawsData,
97
- resolveStatus,
98
- suppressesInteraction,
99
- } from './contract/state';
100
-
101
- export type {
102
- ChartError,
103
- ChartErrorCategory,
104
- ChartErrorCode,
105
- ChartFailureHandler,
106
- ChartFailureReport,
107
- } from './contract/errors';
108
- export { isChartError } from './contract/errors';
109
-
110
- // ---------------------------------------------------------------------------
111
- // Interaction
112
- // ---------------------------------------------------------------------------
113
- export type {
114
- ChartInteractionEvent,
115
- InteractionProps,
116
- PointInteractionEvent,
117
- SelectionRef,
118
- SeriesInteractionEvent,
119
- } from './contract/interaction';
120
- export { sameSelection } from './contract/interaction';
121
-
122
- // ---------------------------------------------------------------------------
123
- // Theme
124
- // ---------------------------------------------------------------------------
125
- export type {
126
- ChartTheme,
127
- LegendEntry,
128
- NonValueTexture,
129
- TextStyleTokens,
130
- } from './contract/theme';
131
- export { defaultChartTheme, resolveSeriesColor } from './contract/theme';
132
-
133
- // ---------------------------------------------------------------------------
134
- // Props and accessibility
135
- // ---------------------------------------------------------------------------
136
- export type {
137
- BaseChartProps,
138
- ChartAccessibility,
139
- ValueFormatter,
140
- } from './contract/props';
141
-
142
- // ---------------------------------------------------------------------------
143
- // Dataset budgets
144
- // ---------------------------------------------------------------------------
145
- export type { BudgetCheck, DatasetBudget } from './validation/budgets';
146
- export { DATASET_BUDGETS, checkBudget } from './validation/budgets';
147
-
148
- // ---------------------------------------------------------------------------
149
- // Components
150
- // ---------------------------------------------------------------------------
151
- export type {
152
- BarChartProps,
153
- DistributionChartProps,
154
- HeatmapChartProps,
155
- ProgressChartProps,
156
- TimeSeriesChartProps,
157
- } from './react/charts';
158
- export {
159
- BarChart,
160
- DistributionChart,
161
- HeatmapChart,
162
- ProgressChart,
163
- TimeSeriesChart,
164
- } from './react/charts';
165
-
166
- export { ChartLegend, buildLegendEntries } from './react/ChartLegend';
167
- export { ChartThemeProvider, useChartTheme } from './react/ChartThemeProvider';
168
-
169
- // ---------------------------------------------------------------------------
170
- // Accessibility helpers
171
- // ---------------------------------------------------------------------------
172
- export {
173
- buildChartSummary,
174
- buildPointAnnouncement,
175
- buildTotalAnnouncement,
176
- } from './a11y/summary';
1
+ /**
2
+ * @chipmobilesdk/rn-chart — domain-agnostic charting for ChipMobileSdk-family
3
+ * React Native apps.
4
+ *
5
+ * This is the single public entrypoint (LIB-003). Modules under `geometry/`,
6
+ * `render/`, `validation/`, and the internals of `react/` are private; import
7
+ * them only through this file. Keeping them private is what lets the geometry,
8
+ * the layout maths, and the rendering substrate change without a breaking
9
+ * release.
10
+ *
11
+ * The package makes no network request, calls no AI service, reads no
12
+ * database, persists nothing, and logs no data value or series label. It
13
+ * renders numeric data and app-supplied labels — nothing else.
14
+ *
15
+ * There is deliberately **no insight, message, or narrative surface**: apps
16
+ * render their own text around a chart, and a chart imposes no outer margin
17
+ * that would fight that layout.
18
+ */
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Value semantics — the foundation of the contract
22
+ // ---------------------------------------------------------------------------
23
+ export type {
24
+ KnownValue,
25
+ NotApplicableValue,
26
+ TotalResult,
27
+ UnknownValue,
28
+ ValueKind,
29
+ ValueState,
30
+ WithheldValue,
31
+ } from './contract/value';
32
+ export {
33
+ isKnown,
34
+ isWithheld,
35
+ known,
36
+ notApplicable,
37
+ participatesInTotals,
38
+ sumKnown,
39
+ unknown,
40
+ withheld,
41
+ } from './contract/value';
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Datasource contract
45
+ // ---------------------------------------------------------------------------
46
+ export type {
47
+ CategoryPosition,
48
+ ChartDatasource,
49
+ ChartFamily,
50
+ DataPoint,
51
+ InstantPosition,
52
+ MatrixPosition,
53
+ Position,
54
+ PositionKind,
55
+ Series,
56
+ SeriesPattern,
57
+ } from './contract/datasource';
58
+ export {
59
+ FAMILY_POSITION_KINDS,
60
+ familyAcceptsPositionKind,
61
+ resolveFormat,
62
+ resolveUnit,
63
+ } from './contract/datasource';
64
+
65
+ export type { MarkerKind, ProgressReading, ReferenceMarker } from './contract/marker';
66
+ export { findTarget, isUsableTarget, readProgress } from './contract/marker';
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Formatting
70
+ // ---------------------------------------------------------------------------
71
+ export type { ChartFormatters, FormatContext, FormatHint, SeriesRef } from './contract/format';
72
+ export {
73
+ NON_VALUE_TEXT,
74
+ formatLegendDefault,
75
+ formatPositionDefault,
76
+ formatValueDefault,
77
+ seriesRef,
78
+ } from './contract/format';
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // State and errors
82
+ // ---------------------------------------------------------------------------
83
+ export type {
84
+ ChartStateContent,
85
+ ChartStatus,
86
+ DeclarableChartStatus,
87
+ StatusInputs,
88
+ } from './contract/state';
89
+ export {
90
+ STATUS_PRECEDENCE,
91
+ drawsData,
92
+ resolveStatus,
93
+ suppressesInteraction,
94
+ } from './contract/state';
95
+
96
+ export type {
97
+ ChartError,
98
+ ChartErrorCategory,
99
+ ChartErrorCode,
100
+ ChartFailureHandler,
101
+ ChartFailureReport,
102
+ } from './contract/errors';
103
+ export { isChartError } from './contract/errors';
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Interaction
107
+ // ---------------------------------------------------------------------------
108
+ export type {
109
+ ChartInteractionEvent,
110
+ InteractionProps,
111
+ PointInteractionEvent,
112
+ SelectionRef,
113
+ SeriesInteractionEvent,
114
+ } from './contract/interaction';
115
+ export { sameSelection } from './contract/interaction';
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Theme
119
+ // ---------------------------------------------------------------------------
120
+ export type { ChartTheme, LegendEntry, NonValueTexture, TextStyleTokens } from './contract/theme';
121
+ export { defaultChartTheme, resolveSeriesColor } from './contract/theme';
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Props and accessibility
125
+ // ---------------------------------------------------------------------------
126
+ export type { BaseChartProps, ChartAccessibility, ValueFormatter } from './contract/props';
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Dataset budgets
130
+ // ---------------------------------------------------------------------------
131
+ export type { BudgetCheck, DatasetBudget } from './validation/budgets';
132
+ export { DATASET_BUDGETS, checkBudget } from './validation/budgets';
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // Components
136
+ // ---------------------------------------------------------------------------
137
+ export type {
138
+ BarChartProps,
139
+ DistributionChartProps,
140
+ HeatmapChartProps,
141
+ ProgressChartProps,
142
+ TimeSeriesChartProps,
143
+ } from './react/charts';
144
+ export {
145
+ BarChart,
146
+ DistributionChart,
147
+ HeatmapChart,
148
+ ProgressChart,
149
+ TimeSeriesChart,
150
+ } from './react/charts';
151
+
152
+ export { ChartLegend, buildLegendEntries } from './react/ChartLegend';
153
+ export { ChartThemeProvider, useChartTheme } from './react/ChartThemeProvider';
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Accessibility helpers
157
+ // ---------------------------------------------------------------------------
158
+ export { buildChartSummary, buildPointAnnouncement, buildTotalAnnouncement } from './a11y/summary';
159
+
160
+ // The package's own texts, for translation (CHT-05).
161
+ export { chartStrings } from './contract/strings';
162
+ export type { ChartStringKey, ChartStrings } from './contract/strings';
@@ -1,96 +1,96 @@
1
- /**
2
- * Per-chart render-failure containment (FR-038a, FR-038b).
3
- *
4
- * React 19 still has no hook equivalent of an error boundary —
5
- * `getDerivedStateFromError` and `componentDidCatch` remain class-only — so
6
- * this is a class. Forty lines beats adding a dependency for it.
7
- *
8
- * Three behaviours are deliberate:
9
- *
10
- * - **Reset only on a real change.** The boundary clears its error when the
11
- * datasource identity or chart key changes, never on an unrelated parent
12
- * re-render. A deterministic geometry defect therefore cannot pin the CPU in
13
- * a render-fail-render loop.
14
- * - **Report once per distinct failure.** De-duplicated by signature, so a
15
- * chart re-rendered by a sibling's state change does not re-report.
16
- * - **Redact.** The report carries the chart identity and a catalogue message.
17
- * A third-party exception's own text is never copied in, because we cannot
18
- * promise it is free of data (POL-011).
19
- */
20
- import React from 'react';
21
-
22
- import type { ChartFamily } from '../contract/datasource';
23
- import {
24
- chartError,
25
- failureSignature,
26
- toFailureReport,
27
- type ChartError,
28
- type ChartFailureHandler,
29
- } from '../contract/errors';
30
-
31
- interface Props {
32
- readonly family: ChartFamily;
33
- readonly chartId?: string;
34
- readonly onFailure?: ChartFailureHandler;
35
- /** Changing this clears a contained failure. */
36
- readonly resetKey: unknown;
37
- readonly renderFallback: (error: ChartError) => React.ReactNode;
38
- readonly children: React.ReactNode;
39
- }
40
-
41
- interface State {
42
- readonly error?: ChartError;
43
- readonly resetKey: unknown;
44
- readonly reportedSignature?: string;
45
- }
46
-
47
- export class ChartBoundary extends React.Component<Props, State> {
48
- constructor(props: Props) {
49
- super(props);
50
- this.state = { resetKey: props.resetKey };
51
- }
52
-
53
- static getDerivedStateFromProps(props: Props, state: State): State | null {
54
- if (props.resetKey !== state.resetKey) {
55
- // New data: give the chart another chance.
56
- return { resetKey: props.resetKey, error: undefined };
57
- }
58
- return null;
59
- }
60
-
61
- static getDerivedStateFromError(): Partial<State> {
62
- // This must set the error, not just acknowledge it. Returning an empty
63
- // object leaves render() showing the children again, they throw again,
64
- // and React escalates past the boundary — which is exactly the
65
- // screen-blanking failure this class exists to prevent.
66
- //
67
- // componentDidCatch then refines this into a report carrying the chart
68
- // identity and family.
69
- return { error: chartError('render', 'renderException') };
70
- }
71
-
72
- componentDidCatch(error: unknown, info: { componentStack?: string }): void {
73
- const report = toFailureReport(
74
- this.props.family,
75
- error,
76
- this.props.chartId,
77
- info?.componentStack ?? undefined,
78
- );
79
-
80
- const signature = failureSignature(report);
81
- if (this.state.reportedSignature === signature) {
82
- this.setState({ error: report.error });
83
- return;
84
- }
85
-
86
- this.setState({ error: report.error, reportedSignature: signature });
87
- this.props.onFailure?.(report);
88
- }
89
-
90
- render(): React.ReactNode {
91
- if (this.state.error) {
92
- return this.props.renderFallback(this.state.error);
93
- }
94
- return this.props.children;
95
- }
96
- }
1
+ /**
2
+ * Per-chart render-failure containment (FR-038a, FR-038b).
3
+ *
4
+ * React 19 still has no hook equivalent of an error boundary —
5
+ * `getDerivedStateFromError` and `componentDidCatch` remain class-only — so
6
+ * this is a class. Forty lines beats adding a dependency for it.
7
+ *
8
+ * Three behaviours are deliberate:
9
+ *
10
+ * - **Reset only on a real change.** The boundary clears its error when the
11
+ * datasource identity or chart key changes, never on an unrelated parent
12
+ * re-render. A deterministic geometry defect therefore cannot pin the CPU in
13
+ * a render-fail-render loop.
14
+ * - **Report once per distinct failure.** De-duplicated by signature, so a
15
+ * chart re-rendered by a sibling's state change does not re-report.
16
+ * - **Redact.** The report carries the chart identity and a catalogue message.
17
+ * A third-party exception's own text is never copied in, because we cannot
18
+ * promise it is free of data (POL-011).
19
+ */
20
+ import React from 'react';
21
+
22
+ import type { ChartFamily } from '../contract/datasource';
23
+ import {
24
+ chartError,
25
+ failureSignature,
26
+ toFailureReport,
27
+ type ChartError,
28
+ type ChartFailureHandler,
29
+ } from '../contract/errors';
30
+
31
+ interface Props {
32
+ readonly family: ChartFamily;
33
+ readonly chartId?: string;
34
+ readonly onFailure?: ChartFailureHandler;
35
+ /** Changing this clears a contained failure. */
36
+ readonly resetKey: unknown;
37
+ readonly renderFallback: (error: ChartError) => React.ReactNode;
38
+ readonly children: React.ReactNode;
39
+ }
40
+
41
+ interface State {
42
+ readonly error?: ChartError;
43
+ readonly resetKey: unknown;
44
+ readonly reportedSignature?: string;
45
+ }
46
+
47
+ export class ChartBoundary extends React.Component<Props, State> {
48
+ constructor(props: Props) {
49
+ super(props);
50
+ this.state = { resetKey: props.resetKey };
51
+ }
52
+
53
+ static getDerivedStateFromProps(props: Props, state: State): State | null {
54
+ if (props.resetKey !== state.resetKey) {
55
+ // New data: give the chart another chance.
56
+ return { resetKey: props.resetKey, error: undefined };
57
+ }
58
+ return null;
59
+ }
60
+
61
+ static getDerivedStateFromError(): Partial<State> {
62
+ // This must set the error, not just acknowledge it. Returning an empty
63
+ // object leaves render() showing the children again, they throw again,
64
+ // and React escalates past the boundary — which is exactly the
65
+ // screen-blanking failure this class exists to prevent.
66
+ //
67
+ // componentDidCatch then refines this into a report carrying the chart
68
+ // identity and family.
69
+ return { error: chartError('render', 'renderException') };
70
+ }
71
+
72
+ override componentDidCatch(error: unknown, info: { componentStack?: string }): void {
73
+ const report = toFailureReport(
74
+ this.props.family,
75
+ error,
76
+ this.props.chartId,
77
+ info?.componentStack ?? undefined,
78
+ );
79
+
80
+ const signature = failureSignature(report);
81
+ if (this.state.reportedSignature === signature) {
82
+ this.setState({ error: report.error });
83
+ return;
84
+ }
85
+
86
+ this.setState({ error: report.error, reportedSignature: signature });
87
+ this.props.onFailure?.(report);
88
+ }
89
+
90
+ override render(): React.ReactNode {
91
+ if (this.state.error) {
92
+ return this.props.renderFallback(this.state.error);
93
+ }
94
+ return this.props.children;
95
+ }
96
+ }