@hjmds/react-native 1.34.0 → 1.35.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 (174) hide show
  1. package/dist/actions.d.ts +12 -72
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +1 -1
  4. package/dist/actions.js.map +1 -1
  5. package/dist/auth-screen.d.ts.map +1 -1
  6. package/dist/auth-screen.js +3 -2
  7. package/dist/auth-screen.js.map +1 -1
  8. package/dist/calendar.d.ts.map +1 -1
  9. package/dist/calendar.js.map +1 -1
  10. package/dist/carousel.d.ts +10 -2
  11. package/dist/carousel.d.ts.map +1 -1
  12. package/dist/carousel.js +12 -6
  13. package/dist/carousel.js.map +1 -1
  14. package/dist/collapsible.d.ts +7 -1
  15. package/dist/collapsible.d.ts.map +1 -1
  16. package/dist/collapsible.js +14 -12
  17. package/dist/collapsible.js.map +1 -1
  18. package/dist/collection-editing.d.ts.map +1 -1
  19. package/dist/collection-editing.js.map +1 -1
  20. package/dist/collection-rail.d.ts +4 -4
  21. package/dist/collection-rail.d.ts.map +1 -1
  22. package/dist/collection-rail.js.map +1 -1
  23. package/dist/communication.d.ts.map +1 -1
  24. package/dist/communication.js.map +1 -1
  25. package/dist/conversation-row.d.ts.map +1 -1
  26. package/dist/conversation-row.js +4 -1
  27. package/dist/conversation-row.js.map +1 -1
  28. package/dist/data-display.d.ts +62 -8
  29. package/dist/data-display.d.ts.map +1 -1
  30. package/dist/data-display.js +135 -90
  31. package/dist/data-display.js.map +1 -1
  32. package/dist/data-table.d.ts +3 -1
  33. package/dist/data-table.d.ts.map +1 -1
  34. package/dist/data-table.js +15 -3
  35. package/dist/data-table.js.map +1 -1
  36. package/dist/date-picker.d.ts.map +1 -1
  37. package/dist/date-picker.js +1 -1
  38. package/dist/date-picker.js.map +1 -1
  39. package/dist/effect-surface.js.map +1 -1
  40. package/dist/evidence.d.ts +1 -1
  41. package/dist/evidence.d.ts.map +1 -1
  42. package/dist/evidence.js +2 -1
  43. package/dist/evidence.js.map +1 -1
  44. package/dist/feedback.d.ts +6 -1
  45. package/dist/feedback.d.ts.map +1 -1
  46. package/dist/feedback.js +7 -5
  47. package/dist/feedback.js.map +1 -1
  48. package/dist/floating-action-button.d.ts +3 -12
  49. package/dist/floating-action-button.d.ts.map +1 -1
  50. package/dist/floating-action-button.js +1 -1
  51. package/dist/floating-action-button.js.map +1 -1
  52. package/dist/forms.d.ts +2 -1
  53. package/dist/forms.d.ts.map +1 -1
  54. package/dist/forms.js +36 -3
  55. package/dist/forms.js.map +1 -1
  56. package/dist/image-choice.d.ts.map +1 -1
  57. package/dist/image-choice.js +8 -6
  58. package/dist/image-choice.js.map +1 -1
  59. package/dist/image-viewer.d.ts.map +1 -1
  60. package/dist/image-viewer.js +7 -4
  61. package/dist/image-viewer.js.map +1 -1
  62. package/dist/index.d.ts +4 -3
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +3 -2
  65. package/dist/index.js.map +1 -1
  66. package/dist/inputs.d.ts +26 -8
  67. package/dist/inputs.d.ts.map +1 -1
  68. package/dist/inputs.js +102 -51
  69. package/dist/inputs.js.map +1 -1
  70. package/dist/internal/accessible-focus.d.ts +2 -1
  71. package/dist/internal/accessible-focus.d.ts.map +1 -1
  72. package/dist/internal/accessible-focus.js +1 -1
  73. package/dist/internal/accessible-focus.js.map +1 -1
  74. package/dist/internal/field-frame.d.ts +8 -4
  75. package/dist/internal/field-frame.d.ts.map +1 -1
  76. package/dist/internal/field-frame.js +6 -6
  77. package/dist/internal/field-frame.js.map +1 -1
  78. package/dist/internal/field-private.d.ts +1 -0
  79. package/dist/internal/field-private.d.ts.map +1 -1
  80. package/dist/internal/field-visibility.d.ts +6 -0
  81. package/dist/internal/field-visibility.d.ts.map +1 -0
  82. package/dist/internal/field-visibility.js +13 -0
  83. package/dist/internal/field-visibility.js.map +1 -0
  84. package/dist/internal/initial-focus.d.ts +8 -1
  85. package/dist/internal/initial-focus.d.ts.map +1 -1
  86. package/dist/internal/initial-focus.js +17 -8
  87. package/dist/internal/initial-focus.js.map +1 -1
  88. package/dist/internal/message-reactions.d.ts.map +1 -1
  89. package/dist/internal/message-reactions.js.map +1 -1
  90. package/dist/internal/modal-lifecycle.d.ts.map +1 -1
  91. package/dist/internal/modal-lifecycle.js +5 -2
  92. package/dist/internal/modal-lifecycle.js.map +1 -1
  93. package/dist/internal/pending-content.d.ts +11 -0
  94. package/dist/internal/pending-content.d.ts.map +1 -0
  95. package/dist/internal/pending-content.js +16 -0
  96. package/dist/internal/pending-content.js.map +1 -0
  97. package/dist/internal/recipe-button.d.ts +8 -31
  98. package/dist/internal/recipe-button.d.ts.map +1 -1
  99. package/dist/internal/recipe-button.js +49 -12
  100. package/dist/internal/recipe-button.js.map +1 -1
  101. package/dist/internal/screen-dismiss.d.ts +10 -0
  102. package/dist/internal/screen-dismiss.d.ts.map +1 -0
  103. package/dist/internal/screen-dismiss.js +62 -0
  104. package/dist/internal/screen-dismiss.js.map +1 -0
  105. package/dist/internal/screen-footer.d.ts +1 -1
  106. package/dist/internal/text-align.d.ts +2 -2
  107. package/dist/internal/text-align.d.ts.map +1 -1
  108. package/dist/keyboard.d.ts +3 -2
  109. package/dist/keyboard.d.ts.map +1 -1
  110. package/dist/keyboard.js +3 -1
  111. package/dist/keyboard.js.map +1 -1
  112. package/dist/mentions.js.map +1 -1
  113. package/dist/navigation.d.ts +3 -1
  114. package/dist/navigation.d.ts.map +1 -1
  115. package/dist/navigation.js +29 -8
  116. package/dist/navigation.js.map +1 -1
  117. package/dist/number-field.d.ts +3 -26
  118. package/dist/number-field.d.ts.map +1 -1
  119. package/dist/number-field.js +2 -1
  120. package/dist/number-field.js.map +1 -1
  121. package/dist/overlays.d.ts +11 -6
  122. package/dist/overlays.d.ts.map +1 -1
  123. package/dist/overlays.js +19 -14
  124. package/dist/overlays.js.map +1 -1
  125. package/dist/primitives.d.ts +5 -31
  126. package/dist/primitives.d.ts.map +1 -1
  127. package/dist/primitives.js.map +1 -1
  128. package/dist/provider.d.ts.map +1 -1
  129. package/dist/provider.js +9 -2
  130. package/dist/provider.js.map +1 -1
  131. package/dist/public-exports.generated.d.ts +6 -0
  132. package/dist/public-exports.generated.d.ts.map +1 -1
  133. package/dist/public-exports.generated.js +18 -0
  134. package/dist/public-exports.generated.js.map +1 -1
  135. package/dist/screen-flows.d.ts +12 -5
  136. package/dist/screen-flows.d.ts.map +1 -1
  137. package/dist/screen-flows.js +25 -14
  138. package/dist/screen-flows.js.map +1 -1
  139. package/dist/screens.d.ts +33 -8
  140. package/dist/screens.d.ts.map +1 -1
  141. package/dist/screens.js +37 -15
  142. package/dist/screens.js.map +1 -1
  143. package/dist/sheet-gesture.d.ts +5 -2
  144. package/dist/sheet-gesture.d.ts.map +1 -1
  145. package/dist/sheet-gesture.js.map +1 -1
  146. package/dist/sheet-panel.d.ts +24 -0
  147. package/dist/sheet-panel.d.ts.map +1 -0
  148. package/dist/sheet-panel.js +111 -0
  149. package/dist/sheet-panel.js.map +1 -0
  150. package/dist/shortcut-rail.d.ts +28 -0
  151. package/dist/shortcut-rail.d.ts.map +1 -0
  152. package/dist/shortcut-rail.js +50 -0
  153. package/dist/shortcut-rail.js.map +1 -0
  154. package/dist/slider.d.ts +3 -20
  155. package/dist/slider.d.ts.map +1 -1
  156. package/dist/slider.js.map +1 -1
  157. package/dist/status-text.d.ts +3 -6
  158. package/dist/status-text.d.ts.map +1 -1
  159. package/dist/status-text.js.map +1 -1
  160. package/dist/steps.d.ts +2 -1
  161. package/dist/steps.d.ts.map +1 -1
  162. package/dist/steps.js +8 -5
  163. package/dist/steps.js.map +1 -1
  164. package/dist/table-family.d.ts.map +1 -1
  165. package/dist/table-family.js.map +1 -1
  166. package/dist/task-list.d.ts +3 -1
  167. package/dist/task-list.d.ts.map +1 -1
  168. package/dist/task-list.js +2 -2
  169. package/dist/task-list.js.map +1 -1
  170. package/dist/time-picker.d.ts +9 -0
  171. package/dist/time-picker.d.ts.map +1 -0
  172. package/dist/time-picker.js +59 -0
  173. package/dist/time-picker.js.map +1 -0
  174. package/package.json +44 -25
@@ -26,6 +26,7 @@ export function Badge({ label, tone = badgeRecipe.defaults.tone, size = badgeRec
26
26
  const metrics = badgeRecipe.sizes[size];
27
27
  const variantPresentation = badgeRecipe.variants[variant];
28
28
  const outlined = !variantPresentation.usesToneBackground;
29
+ const iconContext = { color: resolveColorReference(outlined ? presentation.outlineContent : presentation.content, theme.palette), size: metrics.iconSize };
29
30
  const borderReference = outlined ? presentation.border ?? variantPresentation.borderFallback : null;
30
31
  const borderColor = borderReference === null ? "transparent" : resolveColorReference(borderReference, theme.palette);
31
32
  return (_jsxs(View, { ...props, accessibilityLabel: accessibilityLabel ?? String(label), accessible: true, style: [
@@ -46,12 +47,12 @@ export function Badge({ label, tone = badgeRecipe.defaults.tone, size = badgeRec
46
47
  paddingHorizontal: metrics.paddingHorizontal,
47
48
  },
48
49
  layoutStyle,
49
- ], children: [leading === undefined ? null : (_jsx(View, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", children: leading })), _jsx(Text, { accessible: false, align: "center", emphasis: "strong", style: [
50
+ ], children: [leading === undefined ? null : (_jsx(View, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", children: typeof leading === "function" ? leading(iconContext) : leading })), _jsx(Text, { accessible: false, align: "center", emphasis: "strong", style: [
50
51
  {
51
52
  fontWeight: badgeRecipe.fontWeight,
52
53
  color: resolveColorReference(outlined ? presentation.outlineContent : presentation.content, theme.palette),
53
54
  },
54
- ], variant: metrics.textVariant, children: label }), trailing === undefined ? null : (_jsx(View, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", children: trailing }))] }));
55
+ ], variant: metrics.textVariant, children: label }), trailing === undefined ? null : (_jsx(View, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", children: typeof trailing === "function" ? trailing(iconContext) : trailing }))] }));
55
56
  }
56
57
  export function Tag({ children, tone, accessibilityLabel, layoutStyle, ...props }) {
57
58
  assertNoRemovedStyleProps("Tag", props, ["style", "labelStyle"]);
@@ -82,15 +83,24 @@ export function Tag({ children, tone, accessibilityLabel, layoutStyle, ...props
82
83
  layoutStyle,
83
84
  ], children: _jsx(Text, { align: "center", emphasis: "medium", style: [{ color: presentation.content }], variant: tagRecipe.size.textVariant, children: descriptor.label }) }));
84
85
  }
85
- export function Card({ children, title, description, leading, media, actions, selected = cardRecipe.defaults.selected, onActivate, disabled = false, accessibilityLabel, accessibilityHint, tone = cardRecipe.defaults.tone, bordered = cardRecipe.defaults.bordered, padding = cardRecipe.defaults.padding, layoutStyle, radius: cornerRadius = surfaceDefaults.radius, ...props }) {
86
- const { environment, tokens } = useHjmNativeTheme();
86
+ export function Card({ children, title, description, leading, media, backgroundMedia, backgroundInteraction, mediaBadges, badges, mediaAction, actions, headerActions, selected = cardRecipe.defaults.selected, onActivate, disabled = false, accessibilityLabel, accessibilityHint, tone = cardRecipe.defaults.tone, bordered = cardRecipe.defaults.bordered, padding = cardRecipe.defaults.padding, layoutStyle, radius: cornerRadius = surfaceDefaults.radius, ...props }) {
87
+ const { environment, tokens, colors } = useHjmNativeTheme();
87
88
  if (onActivate && !accessibilityLabel?.trim()) {
88
89
  throw new TypeError("Interactive Card requires accessibilityLabel");
89
90
  }
91
+ if (mediaAction && (onActivate || !media || !mediaAction.label.trim()))
92
+ throw new TypeError("Card mediaAction requires passive media, a label, and no whole-card onActivate");
93
+ if (backgroundMedia && (media || mediaAction || onActivate))
94
+ throw new TypeError("Card backgroundMedia is decorative and cannot combine with media, mediaAction or onActivate");
95
+ if ((backgroundInteraction !== undefined || mediaBadges !== undefined) && !backgroundMedia)
96
+ throw new TypeError("Card backgroundInteraction/mediaBadges require backgroundMedia");
97
+ const [headerActionSize, setHeaderActionSize] = useState({ width: 0, height: 0 });
98
+ const [headerBodyTop, setHeaderBodyTop] = useState(0);
99
+ const [headerRow, setHeaderRow] = useState(null);
90
100
  const bodyPadding = surfaceGeometry.paddings[padding];
91
- const hasHeader = leading !== undefined || title !== undefined || description !== undefined;
101
+ const hasHeader = leading !== undefined || title !== undefined || description !== undefined || headerActions !== undefined;
92
102
  // Whole-card open plus separate commands (2026-10-18 unairplane journey card: open + delete); see `onActivate`.
93
- const split = onActivate !== undefined && actions !== undefined && actions !== null && actions !== false;
103
+ const split = onActivate !== undefined && Boolean(actions || headerActions);
94
104
  const activate = () => { if (!disabled)
95
105
  onActivate?.(); };
96
106
  const activationA11y = {
@@ -100,15 +110,22 @@ export function Card({ children, title, description, leading, media, actions, se
100
110
  accessibilityState: { selected, disabled },
101
111
  };
102
112
  const feedbackOpacity = (pressed) => disabled ? cardRecipe.interaction.disabledOpacity : pressed ? cardRecipe.interaction.pressedOpacity : 1;
103
- const frame = (_jsx(Surface, { ...props, bordered: bordered, padding: "none", radius: cornerRadius, ...(onActivate ? {} : { accessibilityLabel, accessibilityHint }), ...(layoutStyle === undefined || (onActivate && !split) ? {} : { layoutStyle }), tone: selected ? cardRecipe.selectedTone : tone, children: _jsxs(View, { style: {
113
+ // The activation host owns placement; its inner frame/body fill a stretched collection slot.
114
+ const frame = (_jsx(Surface, { ...props, bordered: bordered, padding: "none", radius: cornerRadius, ...(onActivate ? {} : { accessibilityLabel, accessibilityHint }), ...(onActivate && !split ? { layoutStyle: { flexGrow: 1 } } : layoutStyle === undefined ? {} : { layoutStyle }), tone: selected ? cardRecipe.selectedTone : tone, children: _jsxs(View, { pointerEvents: backgroundMedia ? "box-none" : "auto", style: {
104
115
  overflow: "hidden",
116
+ flexGrow: 1,
105
117
  borderRadius: tokens.radius[cornerRadius],
106
- }, children: [_jsxs(CardActivationRegion, { split: split, activation: activationA11y, disabled: disabled, onPress: activate, feedbackOpacity: feedbackOpacity, children: [media === undefined ? null : _jsx(View, { children: media }), _jsxs(View, { style: { gap: cardRecipe.body.gap, padding: bodyPadding }, children: [hasHeader ? (_jsxs(View, { style: {
107
- alignItems: "flex-start",
118
+ ...(backgroundMedia ? { flex: 1, justifyContent: "flex-end" } : {}),
119
+ }, children: [backgroundMedia ? _jsxs(View, { pointerEvents: "none", accessible: false, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: StyleSheet.absoluteFill, children: [backgroundMedia, _jsx(View, { style: [StyleSheet.absoluteFill, { backgroundColor: cardRecipe.backgroundMedia.scrim }] })] }) : null, backgroundInteraction === undefined ? null : _jsx(View, { pointerEvents: "box-none", style: StyleSheet.absoluteFill, children: backgroundInteraction }), mediaBadges === undefined ? null : _jsx(View, { pointerEvents: "box-none", style: { position: "absolute", top: cardRecipe.mediaAction.inset, end: cardRecipe.mediaAction.inset, flexDirection: "row", flexWrap: "wrap", gap: cardRecipe.body.gap, zIndex: 1 }, children: mediaBadges }), _jsxs(CardActivationRegion, { split: split, activation: activationA11y, disabled: disabled, onPress: activate, feedbackOpacity: feedbackOpacity, children: [media === undefined ? null : mediaAction ? _jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: mediaAction.label, accessibilityState: { disabled }, disabled: disabled, onPress: () => { if (!disabled)
120
+ mediaAction.onActivate(); }, style: ({ pressed }) => [minimumTargetStyle, { opacity: feedbackOpacity(pressed) }], children: [media, mediaAction.icon ? _jsx(View, { pointerEvents: "none", accessible: false, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { position: "absolute", end: cardRecipe.mediaAction.inset, bottom: cardRecipe.mediaAction.inset, width: cardRecipe.mediaAction.diameter, height: cardRecipe.mediaAction.diameter, alignItems: "center", justifyContent: "center", borderRadius: tokens.radius.full, backgroundColor: colors.bg, borderWidth: 1, borderColor: colors.border }, children: mediaAction.icon({ color: colors.text, size: cardRecipe.mediaAction.iconSize }) }) : null] }) : _jsx(View, { children: media }), _jsxs(View, { onLayout: ({ nativeEvent: { layout } }) => setHeaderBodyTop(layout.y), style: { gap: cardRecipe.body.gap, padding: bodyPadding, flexGrow: backgroundMedia ? 0 : 1 }, children: [badges === undefined ? null : _jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: cardRecipe.body.gap }, children: badges }), hasHeader ? (_jsxs(View, { onLayout: ({ nativeEvent: { layout } }) => setHeaderRow(current => current?.top === layout.y && current.height === layout.height ? current : { top: layout.y, height: layout.height }), style: {
121
+ alignItems: cardRecipe.header.alignItems,
108
122
  direction: environment.direction,
109
123
  flexDirection: "row",
110
124
  gap: cardRecipe.header.gap,
111
- }, children: [leading === undefined ? null : (_jsx(View, { style: { flexShrink: 0 }, children: leading })), _jsxs(View, { style: { flex: 1, gap: cardRecipe.body.gap, minWidth: 0 }, children: [title === undefined ? null : (_jsx(Text, { accessibilityRole: "header", emphasis: "strong", tone: "primary", variant: "title", children: title })), description === undefined ? null : (_jsx(Text, { emphasis: "regular", tone: "muted", variant: "body", children: description }))] })] })) : null, children === undefined ? null : _jsx(View, { children: children })] })] }), actions === undefined ? null : (_jsx(View, { style: {
125
+ ...(headerActionSize.width > 0 ? { marginEnd: headerActionSize.width + cardRecipe.header.gap, minHeight: headerActionSize.height } : {}),
126
+ }, children: [leading === undefined ? null : (_jsx(View, { style: { flexShrink: 0 }, children: leading })), _jsxs(View, { style: { flex: 1, gap: cardRecipe.body.gap, minWidth: 0 }, children: [title === undefined ? null : (_jsx(Text, { accessibilityRole: "header", emphasis: "strong", tone: "primary", ...(backgroundMedia ? { color: cardRecipe.backgroundMedia.foreground } : {}), variant: "title", children: title })), description === undefined ? null : (_jsx(Text, { emphasis: "regular", tone: "muted", ...(backgroundMedia ? { color: cardRecipe.backgroundMedia.foreground } : {}), variant: "body", children: description }))] })] })) : null, children === undefined ? null : _jsx(View, { style: { flexGrow: 1 }, children: children })] })] }), headerActions === undefined ? null : _jsx(View, { onLayout: ({ nativeEvent: { layout } }) => setHeaderActionSize(current => current.width === layout.width && current.height === layout.height ? current : { width: layout.width, height: layout.height }),
127
+ // Commands stay outside the accessible card target; measured row height also centers wrapped copy.
128
+ style: { position: "absolute", top: headerBodyTop + (headerRow?.top ?? bodyPadding) + Math.max(0, ((headerRow?.height ?? headerActionSize.height) - headerActionSize.height) / 2), end: bodyPadding, flexDirection: "row", alignItems: cardRecipe.header.alignItems, gap: cardRecipe.body.gap, zIndex: 1 }, children: headerActions }), actions === undefined ? null : (_jsx(View, { style: {
112
129
  direction: environment.direction,
113
130
  flexDirection: "row",
114
131
  flexWrap: "wrap",
@@ -126,11 +143,16 @@ export function Card({ children, title, description, leading, media, actions, se
126
143
  function CardActivationRegion({ split, activation, disabled, onPress, feedbackOpacity, children }) {
127
144
  if (!split)
128
145
  return _jsx(_Fragment, { children: children });
129
- return _jsx(Pressable, { ...activation, disabled: disabled, onPress: onPress, style: ({ pressed }) => [minimumTargetStyle, { opacity: feedbackOpacity(pressed) }], children: children });
146
+ return _jsx(Pressable, { ...activation, disabled: disabled, onPress: onPress, style: ({ pressed }) => [minimumTargetStyle, { flexGrow: 1, opacity: feedbackOpacity(pressed) }], children: children });
147
+ }
148
+ /** The quotation and caption stay one visual unit; nested disclosures remain independently accessible. */
149
+ export function QuoteCard({ children, attribution, renderMark, ...props }) {
150
+ const { colors } = useHjmNativeTheme();
151
+ return _jsx(Card, { ...props, leading: _jsx(View, { accessible: false, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: renderMark?.({ color: colors.contentBrand, size: cardRecipe.quotation.markSize }) ?? _jsx(Text, { variant: "heading", tone: "brand", children: "\u201C" }) }), children: _jsxs(View, { style: { gap: cardRecipe.quotation.gap }, children: [children, attribution === undefined ? null : _jsx(Text, { variant: "caption", tone: "muted", children: attribution })] }) });
130
152
  }
131
153
  /**
132
154
  * A List item hands its rows this reporter so the indented separator drawn after the item follows the row's text
133
- * column: leading 40 + gap 12 when the row has a leading frame, the content edge when it has none (2026-10-17 user
155
+ * column: measured leading width + gap when the row has a leading frame, the content edge when it has none (2026-10-17 user
134
156
  * report: a fixed 52 cut the line off midway under rows without leading). Web reads the same fact from the ListRow
135
157
  * root's `data-leading` attribute in CSS.
136
158
  */
@@ -143,12 +165,21 @@ export function ListRow({ title, tone = listRowRecipe.defaults.tone, description
143
165
  // this row's text column (listRecipe.separators). Runs before the loading early return to keep hook order.
144
166
  const reportLeading = useContext(ListItemLeadingContext);
145
167
  const hasLeading = Boolean(leading);
168
+ const [leadingWidth, setLeadingWidth] = useState(listRowRecipe.leadingSize);
169
+ const measureLeading = (event) => {
170
+ if (!reportLeading)
171
+ return;
172
+ const width = event.nativeEvent.layout.width;
173
+ if (Number.isFinite(width) && width >= 0)
174
+ setLeadingWidth(current => current === width ? current : width);
175
+ };
146
176
  useLayoutEffect(() => {
147
177
  if (!reportLeading)
148
178
  return undefined;
149
- reportLeading(hasLeading);
150
- return () => reportLeading(false);
151
- }, [reportLeading, hasLeading]);
179
+ // Ranking rows may compose rank + portrait in a wider public leading slot (2026-10-19).
180
+ reportLeading(hasLeading ? leadingWidth + listRowRecipe.gap : 0);
181
+ return () => reportLeading(0);
182
+ }, [reportLeading, hasLeading, leadingWidth]);
152
183
  const metrics = listRowRecipe.density[density];
153
184
  const interactive = onPress !== undefined;
154
185
  const selected = selectedProp ?? listRowRecipe.defaults.selected;
@@ -193,9 +224,9 @@ export function ListRow({ title, tone = listRowRecipe.defaults.tone, description
193
224
  return _jsxs(View, { testID: props.testID, onLayout: props.onLayout, accessible: Boolean(loadingLabel), accessibilityRole: "progressbar", accessibilityLabel: loadingLabel, accessibilityState: { busy: true }, importantForAccessibility: loadingLabel ? "yes" : "no-hide-descendants", style: [{ alignItems: "center", flexDirection: "row", direction: theme.environment.direction,
194
225
  gap: listRowRecipe.gap, minHeight: visualState.minHeight, marginHorizontal: -metrics.paddingHorizontal,
195
226
  paddingHorizontal: metrics.paddingHorizontal - rowBorder, paddingVertical: metrics.paddingVertical - rowBorder,
196
- borderWidth: rowBorder, borderColor: "transparent" }, layoutStyle], children: [leading ? _jsx(View, { style: [placeholder, leadingFrameStyle] }) : null, _jsxs(View, { style: { flex: 1, minWidth: 0, gap: spacing.xxs }, children: [_jsx(View, { style: [placeholder, { width: `${listRowRecipe.loading.titleWidthPercent}%`, height: theme.tokens.typography[listRowRecipe.title.textVariant].lineHeight }] }), description ? _jsx(View, { style: [placeholder, { width: `${listRowRecipe.loading.descriptionWidthPercent}%`, height: theme.tokens.typography[listRowRecipe.description.textVariant].lineHeight }] }) : null] }), trailing || trailingText ? _jsx(View, { style: [placeholder, { width: listRowRecipe.loading.trailingWidth, height: theme.tokens.typography[listRowRecipe.trailing.textVariant].lineHeight }] }) : null] });
227
+ borderWidth: rowBorder, borderColor: "transparent" }, layoutStyle], children: [leading ? _jsx(View, { onLayout: measureLeading, style: [placeholder, leadingFrameStyle, leadingStyle] }) : null, _jsxs(View, { style: { flex: 1, minWidth: 0, gap: spacing.xxs }, children: [_jsx(View, { style: [placeholder, { width: `${listRowRecipe.loading.titleWidthPercent}%`, height: theme.tokens.typography[listRowRecipe.title.textVariant].lineHeight }] }), description ? _jsx(View, { style: [placeholder, { width: `${listRowRecipe.loading.descriptionWidthPercent}%`, height: theme.tokens.typography[listRowRecipe.description.textVariant].lineHeight }] }) : null] }), trailing || trailingText ? _jsx(View, { style: [placeholder, { width: listRowRecipe.loading.trailingWidth, height: theme.tokens.typography[listRowRecipe.trailing.textVariant].lineHeight }] }) : null] });
197
228
  }
198
- const rowContent = (_jsxs(_Fragment, { children: [leading ? (_jsx(View, { accessible: interactive ? false : undefined, importantForAccessibility: interactive ? "no-hide-descendants" : "auto", style: [leadingFrameStyle, leadingStyle], children: leading })) : null, _jsxs(View, { style: [{ flex: 1, gap: spacing.xxs, minWidth: 0 }, contentStyle], children: [_jsxs(View, { style: [
229
+ const rowContent = (_jsxs(_Fragment, { children: [leading ? (_jsx(View, { onLayout: measureLeading, accessible: interactive ? false : undefined, importantForAccessibility: interactive ? "no-hide-descendants" : "auto", style: [leadingFrameStyle, leadingStyle], children: leading })) : null, _jsxs(View, { style: [{ flex: 1, gap: spacing.xxs, minWidth: 0 }, contentStyle], children: [_jsxs(View, { style: [
199
230
  {
200
231
  alignItems: "center",
201
232
  direction: theme.environment.direction,
@@ -635,21 +666,28 @@ export function List({ label, children, separator = listRecipe.defaults.separato
635
666
  layoutStyle,
636
667
  ], children: items.map((item, index) => (_jsx(ListItem, { borderColor: colors.border, separator: index < items.length - 1 ? separatorContract : null, children: item }, isValidElement(item) && item.key !== null ? item.key : `hjm-list-${index}`))) }));
637
668
  }
669
+ /** Decorative separator for a product-owned virtual collection; never replaces its accessible row. */
670
+ export function ListSeparator({ separator = "content", leading = false, layoutStyle }) {
671
+ const { colors } = useHjmNativeTheme();
672
+ const insets = listRecipe.separators[separator];
673
+ return insets ? _jsx(ListSeparatorFrame, { color: colors.border, start: leading ? insets.leadingInsetStart : insets.insetStart, end: insets.insetEnd, layoutStyle: layoutStyle }) : null;
674
+ }
675
+ // List and virtual hosts share the actual line. List keeps its measured leading width for custom row slots.
676
+ function ListSeparatorFrame({ color, start, end, layoutStyle }) {
677
+ // Keep host placement outside the line: Yoga's explicit logical marginStart/End would otherwise override
678
+ // a caller's marginHorizontal even when the content-edge inset is zero.
679
+ return _jsx(View, { accessible: false, importantForAccessibility: "no", style: layoutStyle, children: _jsx(View, { accessible: false, importantForAccessibility: "no", style: { backgroundColor: color, height: 1, marginStart: start, marginEnd: end } }) });
680
+ }
638
681
  /** One list item and the separator that belongs to it (drawn after it, as an iOS cell separator). */
639
682
  function ListItem({ borderColor, children, separator, }) {
640
- const [hasLeading, setHasLeading] = useState(false);
641
- return (_jsxs(View, { children: [_jsx(ListItemLeadingContext.Provider, { value: setHasLeading, children: children }), separator ? (_jsx(View, { accessible: false, style: {
642
- backgroundColor: borderColor,
643
- height: 1,
644
- marginEnd: separator.insetEnd,
645
- marginStart: hasLeading ? separator.leadingInsetStart : separator.insetStart,
646
- } })) : null] }));
683
+ const [contentInset, setContentInset] = useState(0);
684
+ return (_jsxs(View, { children: [_jsx(ListItemLeadingContext.Provider, { value: setContentInset, children: children }), separator ? _jsx(ListSeparatorFrame, { color: borderColor, start: separator.leadingInsetStart === separator.insetStart ? separator.insetStart : contentInset, end: separator.insetEnd }) : null] }));
647
685
  }
648
686
  export function Statistic(props) {
649
687
  return renderStatistic(props);
650
688
  }
651
689
  // StatisticGroup reuses this renderer with its calculated layout width.
652
- function renderStatistic({ descriptor, density = "comfortable", presentation = "plain", contextLabel, accessibilityLabel, composeAccessibilityLabel, renderTrendMark, layoutStyle, }) {
690
+ function renderStatistic({ descriptor, density = "comfortable", presentation = "plain", contextLabel, accessibilityLabel, composeAccessibilityLabel, leading, valueColor, renderTrendMark, layoutStyle, }, bar = false, barDivider = false) {
653
691
  const resolved = resolveStatisticDescriptor(descriptor);
654
692
  const theme = useHjmNativeTheme();
655
693
  const densityContract = statisticRecipe.density[density];
@@ -674,6 +712,13 @@ function renderStatistic({ descriptor, density = "comfortable", presentation = "
674
712
  const trendColor = resolved.trend
675
713
  ? resolveColorReference(statisticRecipe.trend.tones[resolved.trend.tone], theme.palette)
676
714
  : undefined;
715
+ const labelNode = (_jsx(Text, { accessible: false, style: [
716
+ {
717
+ color: resolveColorReference(statisticRecipe.label.color, theme.palette),
718
+ fontWeight: statisticRecipe.label.fontWeight,
719
+ ...(bar ? { textAlign: "center" } : {}),
720
+ },
721
+ ], variant: densityContract.labelVariant, children: resolved.label }));
677
722
  return (_jsxs(View, { accessibilityLabel: announcement, accessible: true, style: [
678
723
  {
679
724
  backgroundColor: presentationContract.background
@@ -682,34 +727,31 @@ function renderStatistic({ descriptor, density = "comfortable", presentation = "
682
727
  borderColor: presentationContract.border
683
728
  ? resolveColorReference(presentationContract.border, theme.palette)
684
729
  : "transparent",
685
- borderRadius: theme.tokens.radius[presentationContract.radius],
730
+ borderRadius: bar ? 0 : theme.tokens.radius[presentationContract.radius],
686
731
  borderWidth: presentationContract.borderWidth,
687
- gap: densityContract.gap,
732
+ gap: bar ? statisticRecipe.group.bar.valueGap : densityContract.gap,
733
+ ...(bar ? { alignItems: "center", paddingHorizontal: statisticRecipe.group.bar.cellPadding, borderStartWidth: barDivider ? 1 : 0, borderStartColor: theme.colors.border } : {}),
688
734
  minWidth: 0,
689
735
  // Plain statistics align with surrounding text; only the surface plate is inset (Web keys on data-presentation).
690
736
  padding: presentation === "surface" ? densityContract.padding : 0,
691
737
  },
692
738
  layoutStyle,
693
- ], children: [_jsx(Text, { accessible: false, style: [
694
- {
695
- color: resolveColorReference(statisticRecipe.label.color, theme.palette),
696
- fontWeight: statisticRecipe.label.fontWeight,
697
- },
698
- ], variant: densityContract.labelVariant, children: resolved.label }), _jsxs(View, { accessible: false, style: {
699
- alignItems: "baseline",
739
+ ], children: [!bar && labelNode, _jsxs(View, { accessible: false, style: {
740
+ alignItems: leading ? "center" : "baseline",
741
+ ...(bar ? { justifyContent: "center" } : {}),
700
742
  direction: theme.environment.direction,
701
743
  flexDirection: "row",
702
744
  flexWrap: "wrap",
703
745
  gap: spacing.xxs,
704
746
  minWidth: 0,
705
- }, children: [resolved.prefix ? (_jsx(Text, { accessible: false, style: [
747
+ }, children: [leading === undefined ? null : _jsx(View, { accessible: false, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: leading }), resolved.prefix ? (_jsx(Text, { accessible: false, style: [
706
748
  {
707
749
  color: resolveColorReference(statisticRecipe.affix.color, theme.palette),
708
750
  fontWeight: statisticRecipe.affix.fontWeight,
709
751
  },
710
752
  ], variant: statisticRecipe.affix.textVariant, children: resolved.prefix })) : null, _jsx(Text, { accessible: false, style: [
711
753
  {
712
- color: resolveColorReference(statisticRecipe.value.color, theme.palette),
754
+ color: valueColor ?? resolveColorReference(statisticRecipe.value.color, theme.palette),
713
755
  flexShrink: 1,
714
756
  fontVariant: ["tabular-nums"],
715
757
  fontWeight: statisticRecipe.value.fontWeight,
@@ -719,7 +761,7 @@ function renderStatistic({ descriptor, density = "comfortable", presentation = "
719
761
  color: resolveColorReference(statisticRecipe.affix.color, theme.palette),
720
762
  fontWeight: statisticRecipe.affix.fontWeight,
721
763
  },
722
- ], variant: statisticRecipe.affix.textVariant, children: resolved.suffix })) : null] }), resolved.trend ? (_jsxs(View, { accessible: false, style: {
764
+ ], variant: statisticRecipe.affix.textVariant, children: resolved.suffix })) : null] }), bar && labelNode, resolved.trend ? (_jsxs(View, { accessible: false, style: {
723
765
  alignItems: "center",
724
766
  flexDirection: "row",
725
767
  flexWrap: "wrap",
@@ -735,11 +777,11 @@ function renderStatistic({ descriptor, density = "comfortable", presentation = "
735
777
  { color: resolveColorReference(statisticRecipe.hint.color, theme.palette) },
736
778
  ], variant: statisticRecipe.hint.textVariant, children: resolved.hint })) : null] }));
737
779
  }
738
- export function StatisticGroup({ label, descriptor, availableWidth, density, presentation, composeAccessibilityLabel, renderTrendMark, layoutStyle, onLayout, ...props }) {
780
+ export function StatisticGroup({ label, descriptor, availableWidth, density, presentation, composeAccessibilityLabel, arrangement = "grid", renderLeading, getValueColor, renderTrendMark, layoutStyle, onLayout, ...props }) {
739
781
  assertNoRemovedStyleProps("StatisticGroup", props, ["style", "itemStyle"]);
740
782
  validateStatisticGroup(descriptor);
741
783
  const { width: windowWidth } = useWindowDimensions();
742
- const { environment } = useHjmNativeTheme();
784
+ const { environment, colors } = useHjmNativeTheme();
743
785
  const [measuredWidth, setMeasuredWidth] = useState(null);
744
786
  const innerWidth = availableWidth ?? measuredWidth ?? windowWidth;
745
787
  if (!Number.isFinite(innerWidth) || innerWidth <= 0) {
@@ -747,18 +789,19 @@ export function StatisticGroup({ label, descriptor, availableWidth, density, pre
747
789
  }
748
790
  const requested = descriptor.columns ?? statisticRecipe.defaults.columns;
749
791
  const minItemWidth = statisticRecipe.group.minItemWidth * Math.max(1, environment.textScale);
750
- let columns = requested;
751
- while (columns > 1 &&
752
- (innerWidth - statisticRecipe.group.gap * (columns - 1)) / columns < minItemWidth) {
792
+ let columns = arrangement === "bar" ? descriptor.items.length : requested;
793
+ const gap = arrangement === "bar" ? 0 : statisticRecipe.group.gap;
794
+ while (arrangement !== "bar" && columns > 1 &&
795
+ (innerWidth - gap * (columns - 1)) / columns < minItemWidth) {
753
796
  columns -= 1;
754
797
  }
755
- const itemWidth = (innerWidth - statisticRecipe.group.gap * (columns - 1)) / columns;
798
+ const itemWidth = (innerWidth - gap * (columns - 1)) / columns;
756
799
  const remainder = descriptor.items.length % columns;
757
800
  const finalRowCount = remainder === 0 ? columns : remainder;
758
801
  const finalRowStart = descriptor.items.length - finalRowCount;
759
802
  const finalRowItemWidth = finalRowCount === columns
760
803
  ? itemWidth
761
- : (innerWidth - statisticRecipe.group.gap * (finalRowCount - 1)) / finalRowCount;
804
+ : (innerWidth - gap * (finalRowCount - 1)) / finalRowCount;
762
805
  const handleLayout = (event) => {
763
806
  onLayout?.(event);
764
807
  if (availableWidth !== undefined)
@@ -773,63 +816,65 @@ export function StatisticGroup({ label, descriptor, availableWidth, density, pre
773
816
  direction: environment.direction,
774
817
  flexDirection: "row",
775
818
  flexWrap: "wrap",
776
- gap: statisticRecipe.group.gap,
819
+ gap,
777
820
  },
778
821
  layoutStyle,
779
- ], children: descriptor.items.map((item, index) => (_jsx(StatisticGroupItem, { contextLabel: label, descriptor: item, ...(composeAccessibilityLabel === undefined ? {} : { composeAccessibilityLabel }), ...(density === undefined ? {} : { density }), ...(presentation === undefined ? {} : { presentation }), ...(renderTrendMark === undefined ? {} : { renderTrendMark }), layoutStyle: { width: index >= finalRowStart ? finalRowItemWidth : itemWidth } }, item.id))) }));
822
+ ], children: descriptor.items.map((item, index) => {
823
+ const valueColor = getValueColor?.(item);
824
+ return (_jsx(StatisticGroupItem, { bar: arrangement === "bar", barDivider: index > 0, ...(valueColor ? { valueColor } : {}), ...(renderLeading ? { leading: renderLeading(item, { color: valueColor ?? colors.text, size: statisticRecipe.group.bar.iconSize }) } : {}), contextLabel: label, descriptor: item, ...(composeAccessibilityLabel === undefined ? {} : { composeAccessibilityLabel }), ...(density === undefined ? {} : { density }), ...(presentation === undefined ? {} : { presentation }), ...(renderTrendMark === undefined ? {} : { renderTrendMark }), layoutStyle: { width: index >= finalRowStart ? finalRowItemWidth : itemWidth } }, item.id));
825
+ }) }));
780
826
  }
781
827
  /** Group cell: same render as Statistic without attributing the group's styles to the caller. */
782
- function StatisticGroupItem(props) {
783
- return renderStatistic(props);
828
+ function StatisticGroupItem({ bar, barDivider, ...props }) {
829
+ return renderStatistic(props, bar, barDivider);
784
830
  }
785
831
  /** Ordered record of completed events; unlike Steps it has no current cursor. */
786
- export function Timeline({ items, composeAccessibleName, layoutStyle, ...props }) {
832
+ export function Timeline({ items, composeAccessibleName, renderMetadata, renderActions, renderList, layoutStyle, ...props }) {
787
833
  assertNoRemovedStyleProps("Timeline", props, ["style"]);
788
834
  const theme = useHjmNativeTheme();
789
835
  const resolved = resolveTimelineDescriptor({ items }, { composeAccessibleName });
790
836
  // Dot, label first line and timestamp share the label's rendered first line box (token × text scale), matching
791
837
  // Web's 1lh anchor (2026-10-17 measured audit: the dot sat at the top and the caption rode the baseline).
792
838
  const labelLineHeight = theme.tokens.typography[timelineRecipe.label.textVariant].lineHeight * theme.textScaling.scale;
793
- const timestampLineHeight = theme.tokens.typography[timelineRecipe.timestamp.textVariant].lineHeight * theme.textScaling.scale;
794
- return (_jsx(View, { ...props, style: [{ gap: timelineRecipe.gap }, layoutStyle], children: resolved.map((item, index) => {
795
- const tone = timelineRecipe.dot.tones[item.tone];
796
- const accessibilityLabel = [
797
- item.accessibleName,
798
- item.timestamp,
799
- item.description,
800
- ]
801
- .filter(Boolean)
802
- .join(", ");
803
- return (_jsxs(View, { accessible: true, accessibilityLabel: accessibilityLabel, style: { flexDirection: "row", gap: spacing.sm }, children: [_jsxs(View, { importantForAccessibility: "no-hide-descendants", style: { alignItems: "center", width: 16 }, children: [_jsx(View, { style: {
804
- backgroundColor: resolveColorReference(tone.fill, theme.palette),
805
- borderColor: tone.border
806
- ? resolveColorReference(tone.border, theme.palette)
807
- : "transparent",
808
- borderRadius: radius.full,
809
- borderWidth: tone.border
810
- ? timelineRecipe.dot.borderWidth
811
- : 0,
812
- height: timelineRecipe.dot.diameter,
813
- marginTop: (labelLineHeight - timelineRecipe.dot.diameter) / 2,
814
- width: timelineRecipe.dot.diameter,
815
- } }), index < resolved.length - 1 ? (_jsx(View, { style: {
816
- backgroundColor: resolveColorReference(timelineRecipe.connector.tone, theme.palette),
817
- flex: 1,
818
- width: timelineRecipe.connector.width,
819
- } })) : null] }), _jsxs(View, { importantForAccessibility: "no-hide-descendants", style: { flex: 1, gap: spacing.xxs, minWidth: 0, paddingBottom: spacing.xxs }, children: [_jsxs(View, { style: {
820
- alignItems: "flex-start",
821
- flexDirection: "row",
822
- flexWrap: "wrap",
823
- gap: spacing.xs,
824
- justifyContent: "space-between",
825
- }, children: [_jsx(Text, { emphasis: "medium", style: {
826
- color: resolveColorReference(timelineRecipe.label.color, theme.palette),
827
- }, variant: timelineRecipe.label.textVariant, children: item.label }), item.timestamp ? (_jsx(Text, { style: {
828
- color: resolveColorReference(timelineRecipe.timestamp.color, theme.palette),
829
- marginTop: (labelLineHeight - timestampLineHeight) / 2,
830
- }, variant: timelineRecipe.timestamp.textVariant, children: item.timestamp })) : null] }), item.description ? (_jsx(Text, { style: {
831
- color: resolveColorReference(timelineRecipe.description.color, theme.palette),
832
- }, variant: timelineRecipe.description.textVariant, children: item.description })) : null] })] }, item.id));
833
- }) }));
839
+ const renderRecord = (item, index) => {
840
+ const actions = renderActions?.(item);
841
+ const tone = timelineRecipe.dot.tones[item.tone];
842
+ const accessibilityLabel = [
843
+ item.accessibleName,
844
+ item.description,
845
+ item.timestamp,
846
+ ]
847
+ .filter(Boolean)
848
+ .join(", ");
849
+ return (_jsxs(View, { accessible: !actions, accessibilityLabel: actions ? undefined : accessibilityLabel, style: { flexDirection: "row", gap: spacing.sm, padding: timelineRecipe.item.padding, borderRadius: radius.md, backgroundColor: item.emphasized ? resolveColorReference(timelineRecipe.item.emphasisBackground, theme.palette) : undefined }, children: [_jsxs(View, { importantForAccessibility: "no-hide-descendants", style: { alignItems: "center", width: 16 }, children: [_jsx(View, { style: {
850
+ backgroundColor: resolveColorReference(tone.fill, theme.palette),
851
+ borderColor: tone.border
852
+ ? resolveColorReference(tone.border, theme.palette)
853
+ : "transparent",
854
+ borderRadius: radius.full,
855
+ borderWidth: tone.border
856
+ ? timelineRecipe.dot.borderWidth
857
+ : 0,
858
+ height: timelineRecipe.dot.diameter,
859
+ marginTop: (labelLineHeight - timelineRecipe.dot.diameter) / 2,
860
+ width: timelineRecipe.dot.diameter,
861
+ } }), index < resolved.length - 1 ? (_jsx(View, { style: {
862
+ backgroundColor: resolveColorReference(timelineRecipe.connector.tone, theme.palette),
863
+ position: "absolute",
864
+ top: (labelLineHeight + timelineRecipe.dot.diameter) / 2,
865
+ bottom: -timelineRecipe.gap - 2 * timelineRecipe.item.padding - labelLineHeight / 2,
866
+ width: timelineRecipe.connector.width,
867
+ } })) : null] }), _jsxs(View, { accessible: !!actions, accessibilityLabel: actions ? accessibilityLabel : undefined, importantForAccessibility: actions ? "auto" : "no-hide-descendants", style: { flex: 1, gap: spacing.xxs, minWidth: 0, paddingBottom: spacing.xxs }, children: [_jsxs(View, { style: {
868
+ alignItems: "flex-start",
869
+ flexDirection: "row",
870
+ flexWrap: "wrap",
871
+ gap: spacing.xs,
872
+ }, children: [_jsx(Text, { emphasis: "medium", style: {
873
+ color: resolveColorReference(timelineRecipe.label.color, theme.palette),
874
+ }, variant: timelineRecipe.label.textVariant, children: item.label }), renderMetadata?.(item)] }), item.description ? (_jsx(Text, { style: {
875
+ color: resolveColorReference(timelineRecipe.description.color, theme.palette),
876
+ }, variant: timelineRecipe.description.textVariant, children: item.description })) : null, item.timestamp ? _jsx(Text, { tone: "muted", variant: timelineRecipe.timestamp.textVariant, children: item.timestamp }) : null] }), actions ? _jsx(View, { style: { flexDirection: "row", alignSelf: "center", alignItems: "center", gap: spacing.xs }, children: actions }) : null] }, item.id));
877
+ };
878
+ return _jsx(View, { ...props, style: [renderList ? {} : { gap: timelineRecipe.gap }, layoutStyle], children: renderList ? renderList({ items: resolved, renderItem: renderRecord, separator: _jsx(View, { style: { height: timelineRecipe.gap } }) }) : resolved.map(renderRecord) });
834
879
  }
835
880
  //# sourceMappingURL=data-display.js.map