panelui-native 0.65.0 → 0.68.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 (209) hide show
  1. package/README.md +21 -1
  2. package/lib/module/components/area-chart/index.js +18 -1
  3. package/lib/module/components/area-chart/index.js.map +1 -1
  4. package/lib/module/components/bar-chart/index.js +18 -1
  5. package/lib/module/components/bar-chart/index.js.map +1 -1
  6. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  7. package/lib/module/components/candlestick-chart/index.js +18 -1
  8. package/lib/module/components/candlestick-chart/index.js.map +1 -1
  9. package/lib/module/components/carousel/carousel-lifecycle.js +51 -0
  10. package/lib/module/components/carousel/carousel-lifecycle.js.map +1 -0
  11. package/lib/module/components/carousel/index.js +61 -24
  12. package/lib/module/components/carousel/index.js.map +1 -1
  13. package/lib/module/components/context-menu/context-menu-invocation.js +23 -0
  14. package/lib/module/components/context-menu/context-menu-invocation.js.map +1 -0
  15. package/lib/module/components/context-menu/index.js +78 -9
  16. package/lib/module/components/context-menu/index.js.map +1 -1
  17. package/lib/module/components/dialog/index.js.map +1 -1
  18. package/lib/module/components/drawer/index.js.map +1 -1
  19. package/lib/module/components/flow/flow-accessibility.js +18 -2
  20. package/lib/module/components/flow/flow-accessibility.js.map +1 -1
  21. package/lib/module/components/flow/flow-identifiers.js +81 -0
  22. package/lib/module/components/flow/flow-identifiers.js.map +1 -0
  23. package/lib/module/components/flow/index.js +12 -18
  24. package/lib/module/components/flow/index.js.map +1 -1
  25. package/lib/module/components/form/index.js +7 -5
  26. package/lib/module/components/form/index.js.map +1 -1
  27. package/lib/module/components/form/typed-form.js +11 -0
  28. package/lib/module/components/form/typed-form.js.map +1 -0
  29. package/lib/module/components/form/use-form.js +4 -0
  30. package/lib/module/components/form/use-form.js.map +1 -1
  31. package/lib/module/components/heatmap-chart/index.js +27 -1
  32. package/lib/module/components/heatmap-chart/index.js.map +1 -1
  33. package/lib/module/components/line-chart/index.js +18 -1
  34. package/lib/module/components/line-chart/index.js.map +1 -1
  35. package/lib/module/components/message-scroller/index.js +187 -17
  36. package/lib/module/components/message-scroller/index.js.map +1 -1
  37. package/lib/module/components/message-scroller/message-scroller-math.js +43 -0
  38. package/lib/module/components/message-scroller/message-scroller-math.js.map +1 -0
  39. package/lib/module/components/meter/index.js +250 -0
  40. package/lib/module/components/meter/index.js.map +1 -0
  41. package/lib/module/components/meter/meter-scale.js +88 -0
  42. package/lib/module/components/meter/meter-scale.js.map +1 -0
  43. package/lib/module/components/planner/index.js +714 -0
  44. package/lib/module/components/planner/index.js.map +1 -0
  45. package/lib/module/components/planner/planner-entries.js +124 -0
  46. package/lib/module/components/planner/planner-entries.js.map +1 -0
  47. package/lib/module/components/popover/index.js.map +1 -1
  48. package/lib/module/components/radar-chart/index.js +18 -1
  49. package/lib/module/components/radar-chart/index.js.map +1 -1
  50. package/lib/module/components/scatter-chart/index.js +18 -1
  51. package/lib/module/components/scatter-chart/index.js.map +1 -1
  52. package/lib/module/components/selection-mode/index.js +26 -31
  53. package/lib/module/components/selection-mode/index.js.map +1 -1
  54. package/lib/module/components/selection-mode/selection-mode-contracts.js +48 -0
  55. package/lib/module/components/selection-mode/selection-mode-contracts.js.map +1 -0
  56. package/lib/module/components/signature/index.js +62 -20
  57. package/lib/module/components/signature/index.js.map +1 -1
  58. package/lib/module/components/signature/signature-accessibility.js +58 -0
  59. package/lib/module/components/signature/signature-accessibility.js.map +1 -0
  60. package/lib/module/components/skeleton/index.js +45 -7
  61. package/lib/module/components/skeleton/index.js.map +1 -1
  62. package/lib/module/components/slider/index.js +22 -18
  63. package/lib/module/components/slider/index.js.map +1 -1
  64. package/lib/module/components/spinner/index.js +63 -14
  65. package/lib/module/components/spinner/index.js.map +1 -1
  66. package/lib/module/components/time-picker/index.js +46 -21
  67. package/lib/module/components/time-picker/index.js.map +1 -1
  68. package/lib/module/components/time-picker/ruler-window.js +25 -0
  69. package/lib/module/components/time-picker/ruler-window.js.map +1 -0
  70. package/lib/module/hooks/breakpoint-contract.js +29 -0
  71. package/lib/module/hooks/breakpoint-contract.js.map +1 -0
  72. package/lib/module/hooks/index.js +1 -1
  73. package/lib/module/hooks/index.js.map +1 -1
  74. package/lib/module/hooks/use-breakpoint.js +46 -1
  75. package/lib/module/hooks/use-breakpoint.js.map +1 -1
  76. package/lib/module/index.js +3 -1
  77. package/lib/module/index.js.map +1 -1
  78. package/lib/module/primitives/chart-accessibility.js +68 -0
  79. package/lib/module/primitives/chart-accessibility.js.map +1 -0
  80. package/lib/module/primitives/scrim.js +94 -3
  81. package/lib/module/primitives/scrim.js.map +1 -1
  82. package/lib/typescript/src/components/area-chart/index.d.ts +2 -1
  83. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  84. package/lib/typescript/src/components/bar-chart/index.d.ts +2 -1
  85. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
  86. package/lib/typescript/src/components/bottom-sheet/index.d.ts +3 -0
  87. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  88. package/lib/typescript/src/components/candlestick-chart/index.d.ts +2 -1
  89. package/lib/typescript/src/components/candlestick-chart/index.d.ts.map +1 -1
  90. package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts +23 -0
  91. package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts.map +1 -0
  92. package/lib/typescript/src/components/carousel/index.d.ts +5 -2
  93. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
  94. package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts +20 -0
  95. package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts.map +1 -0
  96. package/lib/typescript/src/components/context-menu/index.d.ts +18 -8
  97. package/lib/typescript/src/components/context-menu/index.d.ts.map +1 -1
  98. package/lib/typescript/src/components/dialog/index.d.ts +3 -0
  99. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  100. package/lib/typescript/src/components/drawer/index.d.ts +3 -0
  101. package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
  102. package/lib/typescript/src/components/flow/flow-accessibility.d.ts +8 -0
  103. package/lib/typescript/src/components/flow/flow-accessibility.d.ts.map +1 -1
  104. package/lib/typescript/src/components/flow/flow-identifiers.d.ts +24 -0
  105. package/lib/typescript/src/components/flow/flow-identifiers.d.ts.map +1 -0
  106. package/lib/typescript/src/components/flow/index.d.ts +6 -5
  107. package/lib/typescript/src/components/flow/index.d.ts.map +1 -1
  108. package/lib/typescript/src/components/form/index.d.ts +18 -5
  109. package/lib/typescript/src/components/form/index.d.ts.map +1 -1
  110. package/lib/typescript/src/components/form/typed-form.d.ts +7 -0
  111. package/lib/typescript/src/components/form/typed-form.d.ts.map +1 -0
  112. package/lib/typescript/src/components/form/use-form.d.ts +4 -0
  113. package/lib/typescript/src/components/form/use-form.d.ts.map +1 -1
  114. package/lib/typescript/src/components/heatmap-chart/index.d.ts +2 -1
  115. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
  116. package/lib/typescript/src/components/line-chart/index.d.ts +2 -1
  117. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  118. package/lib/typescript/src/components/message-scroller/index.d.ts +30 -2
  119. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts +18 -0
  121. package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts.map +1 -0
  122. package/lib/typescript/src/components/meter/index.d.ts +270 -0
  123. package/lib/typescript/src/components/meter/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/meter/meter-scale.d.ts +57 -0
  125. package/lib/typescript/src/components/meter/meter-scale.d.ts.map +1 -0
  126. package/lib/typescript/src/components/planner/index.d.ts +274 -0
  127. package/lib/typescript/src/components/planner/index.d.ts.map +1 -0
  128. package/lib/typescript/src/components/planner/planner-entries.d.ts +75 -0
  129. package/lib/typescript/src/components/planner/planner-entries.d.ts.map +1 -0
  130. package/lib/typescript/src/components/popover/index.d.ts +3 -0
  131. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  132. package/lib/typescript/src/components/radar-chart/index.d.ts +4 -0
  133. package/lib/typescript/src/components/radar-chart/index.d.ts.map +1 -1
  134. package/lib/typescript/src/components/scatter-chart/index.d.ts +2 -1
  135. package/lib/typescript/src/components/scatter-chart/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/selection-mode/index.d.ts +6 -4
  137. package/lib/typescript/src/components/selection-mode/index.d.ts.map +1 -1
  138. package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts +18 -0
  139. package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts.map +1 -0
  140. package/lib/typescript/src/components/signature/index.d.ts +6 -0
  141. package/lib/typescript/src/components/signature/index.d.ts.map +1 -1
  142. package/lib/typescript/src/components/signature/signature-accessibility.d.ts +27 -0
  143. package/lib/typescript/src/components/signature/signature-accessibility.d.ts.map +1 -0
  144. package/lib/typescript/src/components/skeleton/index.d.ts +25 -2
  145. package/lib/typescript/src/components/skeleton/index.d.ts.map +1 -1
  146. package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
  147. package/lib/typescript/src/components/spinner/index.d.ts +25 -2
  148. package/lib/typescript/src/components/spinner/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/time-picker/index.d.ts +5 -6
  150. package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
  151. package/lib/typescript/src/components/time-picker/ruler-window.d.ts +12 -0
  152. package/lib/typescript/src/components/time-picker/ruler-window.d.ts.map +1 -0
  153. package/lib/typescript/src/hooks/breakpoint-contract.d.ts +6 -0
  154. package/lib/typescript/src/hooks/breakpoint-contract.d.ts.map +1 -0
  155. package/lib/typescript/src/hooks/index.d.ts +1 -1
  156. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  157. package/lib/typescript/src/hooks/use-breakpoint.d.ts +22 -0
  158. package/lib/typescript/src/hooks/use-breakpoint.d.ts.map +1 -1
  159. package/lib/typescript/src/index.d.ts +4 -2
  160. package/lib/typescript/src/index.d.ts.map +1 -1
  161. package/lib/typescript/src/primitives/chart-accessibility.d.ts +33 -0
  162. package/lib/typescript/src/primitives/chart-accessibility.d.ts.map +1 -0
  163. package/lib/typescript/src/primitives/scrim.d.ts +5 -3
  164. package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
  165. package/lib/typescript/types/form-api.type-test.d.ts +2 -0
  166. package/lib/typescript/types/form-api.type-test.d.ts.map +1 -0
  167. package/package.json +34 -5
  168. package/src/components/area-chart/index.tsx +27 -2
  169. package/src/components/bar-chart/index.tsx +27 -2
  170. package/src/components/bottom-sheet/index.tsx +3 -0
  171. package/src/components/candlestick-chart/index.tsx +30 -2
  172. package/src/components/carousel/carousel-lifecycle.ts +74 -0
  173. package/src/components/carousel/index.tsx +80 -25
  174. package/src/components/context-menu/context-menu-invocation.ts +44 -0
  175. package/src/components/context-menu/index.tsx +113 -13
  176. package/src/components/dialog/index.tsx +3 -0
  177. package/src/components/drawer/index.tsx +3 -0
  178. package/src/components/flow/flow-accessibility.ts +16 -2
  179. package/src/components/flow/flow-identifiers.ts +98 -0
  180. package/src/components/flow/index.tsx +29 -19
  181. package/src/components/form/index.tsx +32 -5
  182. package/src/components/form/typed-form.ts +13 -0
  183. package/src/components/form/use-form.ts +9 -0
  184. package/src/components/heatmap-chart/index.tsx +49 -3
  185. package/src/components/line-chart/index.tsx +27 -2
  186. package/src/components/message-scroller/index.tsx +273 -13
  187. package/src/components/message-scroller/message-scroller-math.ts +62 -0
  188. package/src/components/meter/index.tsx +318 -0
  189. package/src/components/meter/meter-scale.ts +109 -0
  190. package/src/components/planner/index.tsx +886 -0
  191. package/src/components/planner/planner-entries.ts +146 -0
  192. package/src/components/popover/index.tsx +3 -0
  193. package/src/components/radar-chart/index.tsx +24 -0
  194. package/src/components/scatter-chart/index.tsx +27 -2
  195. package/src/components/selection-mode/index.tsx +37 -30
  196. package/src/components/selection-mode/selection-mode-contracts.ts +53 -0
  197. package/src/components/signature/index.tsx +107 -20
  198. package/src/components/signature/signature-accessibility.ts +67 -0
  199. package/src/components/skeleton/index.tsx +59 -5
  200. package/src/components/slider/index.tsx +28 -18
  201. package/src/components/spinner/index.tsx +80 -13
  202. package/src/components/time-picker/index.tsx +57 -29
  203. package/src/components/time-picker/ruler-window.ts +24 -0
  204. package/src/hooks/breakpoint-contract.ts +31 -0
  205. package/src/hooks/index.ts +4 -0
  206. package/src/hooks/use-breakpoint.ts +61 -10
  207. package/src/index.ts +32 -0
  208. package/src/primitives/chart-accessibility.ts +102 -0
  209. package/src/primitives/scrim.tsx +127 -7
@@ -0,0 +1,98 @@
1
+ const FLOW_TOKEN_PREFIX = 'panelui-flow:';
2
+
3
+ export interface FlowEndpoint {
4
+ node: string;
5
+ handle?: string;
6
+ }
7
+
8
+ export type FlowEndpointReference = string | FlowEndpoint;
9
+
10
+ type EncodedReference = ['legacy', string] | ['endpoint', string] | ['endpoint', string, string];
11
+
12
+ function encodedReference(reference: FlowEndpointReference): EncodedReference {
13
+ if (typeof reference === 'string') return ['legacy', reference];
14
+ return reference.handle === undefined
15
+ ? ['endpoint', reference.node]
16
+ : ['endpoint', reference.node, reference.handle];
17
+ }
18
+
19
+ function decodedReference(value: unknown): FlowEndpointReference | undefined {
20
+ if (!Array.isArray(value)) return undefined;
21
+ if (value.length === 2 && value[0] === 'legacy' && typeof value[1] === 'string') {
22
+ return value[1];
23
+ }
24
+ if (
25
+ (value.length === 2 || value.length === 3) &&
26
+ value[0] === 'endpoint' &&
27
+ typeof value[1] === 'string' &&
28
+ (value.length === 2 || typeof value[2] === 'string')
29
+ ) {
30
+ return value.length === 2 ? { node: value[1] } : { node: value[1], handle: value[2] };
31
+ }
32
+ return undefined;
33
+ }
34
+
35
+ function decodeToken(token: string): unknown[] | undefined {
36
+ if (!token.startsWith(FLOW_TOKEN_PREFIX)) return undefined;
37
+ try {
38
+ const value: unknown = JSON.parse(token.slice(FLOW_TOKEN_PREFIX.length));
39
+ return Array.isArray(value) ? value : undefined;
40
+ } catch {
41
+ return undefined;
42
+ }
43
+ }
44
+
45
+ /** Opaque identity for a registered handle. Node and handle text remain untouched. */
46
+ export function encodeFlowHandleKey(node: string, handle: string): string {
47
+ return FLOW_TOKEN_PREFIX + JSON.stringify(['handle', node, handle]);
48
+ }
49
+
50
+ export function decodeFlowHandleKey(token: string): FlowEndpoint | undefined {
51
+ const value = decodeToken(token);
52
+ if (
53
+ value?.length !== 3 ||
54
+ value[0] !== 'handle' ||
55
+ typeof value[1] !== 'string' ||
56
+ typeof value[2] !== 'string'
57
+ ) {
58
+ return undefined;
59
+ }
60
+ return { node: value[1], handle: value[2] };
61
+ }
62
+
63
+ /** Opaque identity for an edge registration or accessibility connection action. */
64
+ export function encodeFlowEdgeKey(
65
+ from: FlowEndpointReference,
66
+ to: FlowEndpointReference
67
+ ): string {
68
+ return FLOW_TOKEN_PREFIX + JSON.stringify(['edge', encodedReference(from), encodedReference(to)]);
69
+ }
70
+
71
+ export function decodeFlowEdgeKey(
72
+ token: string
73
+ ): { from: FlowEndpointReference; to: FlowEndpointReference } | undefined {
74
+ const value = decodeToken(token);
75
+ if (value?.length !== 3 || value[0] !== 'edge') return undefined;
76
+ const from = decodedReference(value[1]);
77
+ const to = decodedReference(value[2]);
78
+ return from === undefined || to === undefined ? undefined : { from, to };
79
+ }
80
+
81
+ /**
82
+ * Resolve a public endpoint without splitting its identifiers. Exact node ids
83
+ * win; legacy `node.handle` text is accepted only when it names one registered
84
+ * handle. Structured endpoints cover the otherwise ambiguous case.
85
+ */
86
+ export function resolveFlowEndpoint(
87
+ reference: FlowEndpointReference,
88
+ nodes: readonly string[],
89
+ handles: readonly { node: string; id: string }[]
90
+ ): FlowEndpoint {
91
+ if (typeof reference !== 'string') return reference;
92
+ if (nodes.includes(reference)) return { node: reference };
93
+
94
+ const matches = handles.filter((handle) => `${handle.node}.${handle.id}` === reference);
95
+ return matches.length === 1
96
+ ? { node: matches[0]!.node, handle: matches[0]!.id }
97
+ : { node: reference };
98
+ }
@@ -107,6 +107,13 @@ import {
107
107
  type FlowAccessibilityHandle,
108
108
  type FlowAccessibilityNode,
109
109
  } from './flow-accessibility';
110
+ import {
111
+ encodeFlowEdgeKey,
112
+ encodeFlowHandleKey,
113
+ resolveFlowEndpoint,
114
+ type FlowEndpoint,
115
+ type FlowEndpointReference,
116
+ } from './flow-identifiers';
110
117
 
111
118
  const AnimatedPath = Animated.createAnimatedComponent(Path);
112
119
  const AnimatedRect = Animated.createAnimatedComponent(Rect);
@@ -1314,7 +1321,7 @@ function FlowHandle({
1314
1321
  unregisterHandle,
1315
1322
  connectNodes,
1316
1323
  } = flow;
1317
- const key = `${node}.${id}`;
1324
+ const key = encodeFlowHandleKey(node, id);
1318
1325
 
1319
1326
  useEffect(() => {
1320
1327
  registerHandle({
@@ -1481,10 +1488,10 @@ FlowHandle.displayName = 'Flow.Handle';
1481
1488
  /* -------------------------------------------------------------------------- */
1482
1489
 
1483
1490
  export interface FlowEdgeProps {
1484
- /** Source, as `"nodeId"` or `"nodeId.handleId"`. */
1485
- from: string;
1486
- /** Target, same shape. */
1487
- to: string;
1491
+ /** Source node or handle. Strings retain the `"nodeId.handleId"` shorthand. */
1492
+ from: FlowEndpointReference;
1493
+ /** Target node or handle, in the same shape. */
1494
+ to: FlowEndpointReference;
1488
1495
  /** How the edge is routed. */
1489
1496
  variant?: FlowEdgeVariant;
1490
1497
  /**
@@ -1530,7 +1537,7 @@ export interface FlowEdgeProps {
1530
1537
  */
1531
1538
  function FlowEdge(props: FlowEdgeProps) {
1532
1539
  const { registerEdge, unregisterEdge } = useFlow('Flow.Edge');
1533
- const key = `${props.from}->${props.to}`;
1540
+ const key = encodeFlowEdgeKey(props.from, props.to);
1534
1541
 
1535
1542
  // Re-registered whenever any prop changes, so `animated` toggling or a
1536
1543
  // variant switch reaches the layer that draws it.
@@ -1571,8 +1578,9 @@ function FlowEdgePath({
1571
1578
  tint: string;
1572
1579
  origin: { x: number; y: number };
1573
1580
  }) {
1574
- const source = resolveEnd(from, fromSide, handles);
1575
- const target = resolveEnd(to, toSide, handles);
1581
+ const nodes = Object.keys(boxes);
1582
+ const source = resolveEnd(from, fromSide, nodes, handles);
1583
+ const target = resolveEnd(to, toSide, nodes, handles);
1576
1584
 
1577
1585
  const fromRect = boxes[source.node];
1578
1586
  const toRect = boxes[target.node];
@@ -1631,22 +1639,24 @@ function FlowEdgePath({
1631
1639
  }
1632
1640
 
1633
1641
  /**
1634
- * Splits `"node"` or `"node.handle"` into the node and, when a handle was
1635
- * named, the face and offset that handle registered.
1642
+ * Resolves a node or handle without parsing either identifier. Legacy
1643
+ * `"node.handle"` references are matched against the structured registry.
1636
1644
  */
1637
1645
  function resolveEnd(
1638
- reference: string,
1646
+ reference: FlowEndpointReference,
1639
1647
  explicit: FlowSide | undefined,
1648
+ nodes: string[],
1640
1649
  handles: HandleEntry[]
1641
1650
  ): { node: string; side: FlowSide | undefined; offset: number } {
1642
- const dot = reference.indexOf('.');
1643
- if (dot === -1) {
1644
- return { node: reference, side: explicit, offset: 0.5 };
1645
- }
1646
- const node = reference.slice(0, dot);
1647
- const entry = handles.find((handle) => handle.key === reference);
1651
+ const endpoint = resolveFlowEndpoint(reference, nodes, handles);
1652
+ const entry =
1653
+ endpoint.handle === undefined
1654
+ ? undefined
1655
+ : handles.find(
1656
+ (handle) => handle.node === endpoint.node && handle.id === endpoint.handle
1657
+ );
1648
1658
  return {
1649
- node,
1659
+ node: endpoint.node,
1650
1660
  side: explicit ?? entry?.side,
1651
1661
  offset: entry?.offset ?? 0.5,
1652
1662
  };
@@ -2199,4 +2209,4 @@ export const Flow = Object.assign(FlowRoot, {
2199
2209
  MiniMap: FlowMiniMap,
2200
2210
  });
2201
2211
 
2202
- export type { FlowSide, FlowRect, FlowPoint };
2212
+ export type { FlowEndpoint, FlowEndpointReference, FlowSide, FlowRect, FlowPoint };
@@ -7,11 +7,8 @@
7
7
  * take differently-shaped change props, and only the caller knows which one
8
8
  * a given field needs to wire up.
9
9
  *
10
- * `Form`/`Form.Field` trade some type precision for that JSX ergonomics — a
11
- * field's `name` isn't checked against `keyof YourValues` here, because a
12
- * value's shape can't flow through `useContext` generically. Reach for
13
- * `useField(form, name)` directly when a field's name should be checked
14
- * against the form's own value type.
10
+ * `createForm<Values>()` binds that value shape to the same runtime components
11
+ * when field names and render values should stay typed through JSX.
15
12
  *
16
13
  * ```tsx
17
14
  * const form = useForm({
@@ -37,6 +34,7 @@
37
34
  import { createContext, useContext, type ReactNode } from 'react';
38
35
  import { useField, type FormFieldRenderProps, type UseFieldOptions } from './use-field';
39
36
  import { useForm, type FormApi, type FieldErrors, type FieldTouched, type FieldState, type UseFormOptions, type Validator } from './use-form';
37
+ import { bindFormRuntime } from './typed-form';
40
38
 
41
39
  export { useForm, useField };
42
40
  export type {
@@ -57,6 +55,11 @@ export interface FormProps {
57
55
  children?: ReactNode;
58
56
  }
59
57
 
58
+ export interface TypedFormProps<T extends Record<string, any>> {
59
+ form: FormApi<T>;
60
+ children?: ReactNode;
61
+ }
62
+
60
63
  function FormRoot({ form, children }: FormProps) {
61
64
  return <FormContext.Provider value={form}>{children}</FormContext.Provider>;
62
65
  }
@@ -70,6 +73,14 @@ export interface FormFieldProps {
70
73
  children: (field: FormFieldRenderProps<any>) => ReactNode;
71
74
  }
72
75
 
76
+ export interface TypedFormFieldProps<
77
+ T extends Record<string, any>,
78
+ K extends keyof T,
79
+ > extends UseFieldOptions<T, K> {
80
+ name: K;
81
+ children: (field: FormFieldRenderProps<T[K]>) => ReactNode;
82
+ }
83
+
73
84
  function FormField({ name, validate, validateOn, children }: FormFieldProps) {
74
85
  const form = useContext(FormContext);
75
86
  if (!form) {
@@ -83,3 +94,19 @@ FormField.displayName = 'Form.Field';
83
94
  export const Form = Object.assign(FormRoot, {
84
95
  Field: FormField,
85
96
  });
97
+
98
+ export interface TypedForm<T extends Record<string, any>> {
99
+ (props: TypedFormProps<T>): ReactNode;
100
+ Field: <K extends keyof T>(props: TypedFormFieldProps<T, K>) => ReactNode;
101
+ useForm: (options: UseFormOptions<T>) => FormApi<T>;
102
+ useField: <K extends keyof T>(
103
+ form: FormApi<T>,
104
+ name: K,
105
+ options?: UseFieldOptions<T, K>
106
+ ) => FormFieldRenderProps<T[K]>;
107
+ }
108
+
109
+ /** Bind a value shape to Form's existing runtime without creating new state. */
110
+ export function createForm<T extends Record<string, any>>(): TypedForm<T> {
111
+ return bindFormRuntime(FormRoot, FormField, useForm, useField) as TypedForm<T>;
112
+ }
@@ -0,0 +1,13 @@
1
+ /** Attach bound helpers to the existing runtime components; no state is cloned. */
2
+ export function bindFormRuntime<
3
+ Root,
4
+ Field,
5
+ UseForm,
6
+ UseField,
7
+ >(root: Root, field: Field, useForm: UseForm, useField: UseField) {
8
+ return Object.assign(root as object, { Field: field, useForm, useField }) as Root & {
9
+ Field: Field;
10
+ useForm: UseForm;
11
+ useField: UseField;
12
+ };
13
+ }
@@ -67,6 +67,10 @@ export interface FormApi<T extends Record<string, any>> {
67
67
  setFieldValue: <K extends keyof T>(name: K, value: T[K]) => void;
68
68
  setFieldTouched: <K extends keyof T>(name: K, touched?: boolean) => void;
69
69
  setFieldError: <K extends keyof T>(name: K, error: string | undefined) => void;
70
+ /** Read the latest value, including a change made before React renders again. */
71
+ getValue: <K extends keyof T>(name: K) => T[K];
72
+ /** Read a field from this render so a component naturally follows its changes. */
73
+ watch: <K extends keyof T>(name: K) => T[K];
70
74
  getFieldState: <K extends keyof T>(name: K) => FieldState<T[K]>;
71
75
  /** Used by `useField`/`Form.Field` to attach a field's validation rule. */
72
76
  registerValidator: <K extends keyof T>(name: K, validator?: Validator<T, K>) => void;
@@ -249,6 +253,9 @@ export function useForm<T extends Record<string, any>>({
249
253
  [state.values, state.errors, state.touched]
250
254
  );
251
255
 
256
+ const getValue = useCallback(<K extends keyof T>(name: K): T[K] => valuesRef.current[name], []);
257
+ const watch = useCallback(<K extends keyof T>(name: K): T[K] => state.values[name], [state.values]);
258
+
252
259
  const validateField = useCallback(async <K extends keyof T>(name: K) => {
253
260
  const validator = validatorsRef.current[name];
254
261
  if (!validator) return true;
@@ -369,6 +376,8 @@ export function useForm<T extends Record<string, any>>({
369
376
  setFieldValue,
370
377
  setFieldTouched,
371
378
  setFieldError,
379
+ getValue,
380
+ watch,
372
381
  getFieldState,
373
382
  registerValidator,
374
383
  validateField,
@@ -61,6 +61,7 @@ import Animated, {
61
61
  import Svg, { ClipPath, Defs, G, Line as SvgLine, Rect } from 'react-native-svg';
62
62
  import { useCSSVariable } from 'uniwind';
63
63
  import { Text } from '../../primitives/text';
64
+ import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
64
65
  import { cn } from '../../utils/cn';
65
66
  import { normalizeWeekStart, startOfDay } from '../../utils/date';
66
67
 
@@ -309,7 +310,7 @@ function deriveLevels(data: HeatmapColumn[]): number[] {
309
310
  /* Root */
310
311
  /* -------------------------------------------------------------------------- */
311
312
 
312
- export interface HeatmapChartProps extends ViewProps {
313
+ export interface HeatmapChartProps extends ViewProps, ChartAccessibilityProps<HeatmapCell> {
313
314
  className?: string;
314
315
  /** One column per period, with its row bins inside. */
315
316
  data: HeatmapColumn[];
@@ -396,6 +397,11 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
396
397
  animationDuration = 900,
397
398
  inactiveOpacity = 1,
398
399
  onActiveCellChange,
400
+ accessible,
401
+ accessibilityLabel,
402
+ accessibilityHint,
403
+ accessibilityLabelForDatum,
404
+ onAccessibilityDatumPress,
399
405
  children,
400
406
  ...props
401
407
  },
@@ -491,6 +497,20 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
491
497
  [data, levelOf]
492
498
  );
493
499
 
500
+ const accessibilityCells = useMemo(
501
+ () =>
502
+ data.flatMap((column) =>
503
+ column.bins.map((bin) => ({
504
+ column: column.bin,
505
+ row: bin.bin,
506
+ count: bin.count,
507
+ level: levelOf(bin.count),
508
+ date: bin.date,
509
+ }))
510
+ ),
511
+ [data, levelOf]
512
+ );
513
+
494
514
  const setActiveCell = useMemo(
495
515
  () => (cell: HeatmapCell | null) => {
496
516
  setActiveCellState(cell);
@@ -570,11 +590,37 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
570
590
  whole chart, so it starts at the chart's edge, not the grid's. */}
571
591
  {parts.header}
572
592
 
593
+ <ChartAccessibilityData
594
+ chart="Heatmap chart"
595
+ data={accessibilityCells}
596
+ disabled={accessible === false}
597
+ accessibilityLabel={accessibilityLabel}
598
+ accessibilityHint={accessibilityHint}
599
+ accessibilityLabelForDatum={accessibilityLabelForDatum}
600
+ onAccessibilityDatumPress={onAccessibilityDatumPress}
601
+ valueOf={(cell) => [
602
+ ['date', cell.date],
603
+ ['column', cell.column],
604
+ ['row', cell.row],
605
+ ['count', cell.count],
606
+ ]}
607
+ />
608
+
573
609
  {parts.x ? (
574
- <View style={{ paddingLeft: axisWidth }}>{parts.x}</View>
610
+ <View
611
+ style={{ paddingLeft: axisWidth }}
612
+ accessibilityElementsHidden
613
+ importantForAccessibility="no-hide-descendants"
614
+ >
615
+ {parts.x}
616
+ </View>
575
617
  ) : null}
576
618
 
577
- <View className="flex-row">
619
+ <View
620
+ className="flex-row"
621
+ accessibilityElementsHidden
622
+ importantForAccessibility="no-hide-descendants"
623
+ >
578
624
  {parts.y}
579
625
  <View style={{ width: grid.width, height: grid.height }}>
580
626
  {grid.width > 0 ? (
@@ -82,6 +82,7 @@ import Svg, {
82
82
  } from 'react-native-svg';
83
83
  import { useCSSVariable } from 'uniwind';
84
84
  import { Text } from '../../primitives/text';
85
+ import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
85
86
  import {
86
87
  areaPath,
87
88
  columnValues,
@@ -172,7 +173,7 @@ export function useLineChart() {
172
173
  };
173
174
  }
174
175
 
175
- export interface LineChartProps extends ViewProps {
176
+ export interface LineChartProps extends ViewProps, ChartAccessibilityProps<LineChartDatum> {
176
177
  className?: string;
177
178
  /** The rows. Each one is a point along the x-axis. */
178
179
  data: LineChartDatum[];
@@ -227,6 +228,11 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
227
228
  yDomain,
228
229
  curve = 'monotone',
229
230
  onActiveIndexChange,
231
+ accessible,
232
+ accessibilityLabel,
233
+ accessibilityHint,
234
+ accessibilityLabelForDatum,
235
+ onAccessibilityDatumPress,
230
236
  compact = false,
231
237
  children,
232
238
  ...props
@@ -443,7 +449,26 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
443
449
  <LineChartContext.Provider value={context}>
444
450
  <View {...props} style={props.style} className={cn('w-full', className)}>
445
451
  {header}
446
- <View onLayout={onLayout} style={{ aspectRatio }} className="w-full">
452
+ <ChartAccessibilityData
453
+ chart="Line chart"
454
+ data={data}
455
+ disabled={accessible === false || loading}
456
+ accessibilityLabel={accessibilityLabel}
457
+ accessibilityHint={accessibilityHint}
458
+ accessibilityLabelForDatum={accessibilityLabelForDatum}
459
+ onAccessibilityDatumPress={onAccessibilityDatumPress}
460
+ valueOf={(datum) => [
461
+ [xDataKey, datum[xDataKey]],
462
+ ...series.map(([key]) => [key, datum[key]] as [string, unknown]),
463
+ ]}
464
+ />
465
+ <View
466
+ onLayout={onLayout}
467
+ style={{ aspectRatio }}
468
+ className="w-full"
469
+ accessibilityElementsHidden
470
+ importantForAccessibility="no-hide-descendants"
471
+ >
447
472
  {plot.width > 0 ? (
448
473
  <>
449
474
  <Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>