@hjmds/react-native 1.34.0 → 1.35.1
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.
- package/dist/actions.d.ts +12 -72
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +1 -1
- package/dist/actions.js.map +1 -1
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +3 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js.map +1 -1
- package/dist/carousel.d.ts +10 -2
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +12 -6
- package/dist/carousel.js.map +1 -1
- package/dist/collapsible.d.ts +7 -1
- package/dist/collapsible.d.ts.map +1 -1
- package/dist/collapsible.js +14 -12
- package/dist/collapsible.js.map +1 -1
- package/dist/collection-editing.d.ts +3 -1
- package/dist/collection-editing.d.ts.map +1 -1
- package/dist/collection-editing.js +6 -6
- package/dist/collection-editing.js.map +1 -1
- package/dist/collection-rail.d.ts +4 -4
- package/dist/collection-rail.d.ts.map +1 -1
- package/dist/collection-rail.js.map +1 -1
- package/dist/communication.d.ts.map +1 -1
- package/dist/communication.js.map +1 -1
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +4 -1
- package/dist/conversation-row.js.map +1 -1
- package/dist/data-display.d.ts +62 -8
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +135 -90
- package/dist/data-display.js.map +1 -1
- package/dist/data-table.d.ts +3 -1
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +15 -3
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +1 -1
- package/dist/date-picker.js.map +1 -1
- package/dist/effect-surface.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +2 -1
- package/dist/evidence.js.map +1 -1
- package/dist/feedback.d.ts +6 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +7 -5
- package/dist/feedback.js.map +1 -1
- package/dist/floating-action-button.d.ts +3 -12
- package/dist/floating-action-button.d.ts.map +1 -1
- package/dist/floating-action-button.js +1 -1
- package/dist/floating-action-button.js.map +1 -1
- package/dist/forms.d.ts +2 -1
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +36 -3
- package/dist/forms.js.map +1 -1
- package/dist/image-choice.d.ts.map +1 -1
- package/dist/image-choice.js +8 -6
- package/dist/image-choice.js.map +1 -1
- package/dist/image-viewer.d.ts.map +1 -1
- package/dist/image-viewer.js +7 -4
- package/dist/image-viewer.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/inputs.d.ts +26 -8
- package/dist/inputs.d.ts.map +1 -1
- package/dist/inputs.js +102 -51
- package/dist/inputs.js.map +1 -1
- package/dist/internal/accessible-focus.d.ts +2 -1
- package/dist/internal/accessible-focus.d.ts.map +1 -1
- package/dist/internal/accessible-focus.js +1 -1
- package/dist/internal/accessible-focus.js.map +1 -1
- package/dist/internal/field-frame.d.ts +8 -4
- package/dist/internal/field-frame.d.ts.map +1 -1
- package/dist/internal/field-frame.js +6 -6
- package/dist/internal/field-frame.js.map +1 -1
- package/dist/internal/field-private.d.ts +1 -0
- package/dist/internal/field-private.d.ts.map +1 -1
- package/dist/internal/field-visibility.d.ts +6 -0
- package/dist/internal/field-visibility.d.ts.map +1 -0
- package/dist/internal/field-visibility.js +13 -0
- package/dist/internal/field-visibility.js.map +1 -0
- package/dist/internal/initial-focus.d.ts +8 -1
- package/dist/internal/initial-focus.d.ts.map +1 -1
- package/dist/internal/initial-focus.js +17 -8
- package/dist/internal/initial-focus.js.map +1 -1
- package/dist/internal/message-reactions.d.ts.map +1 -1
- package/dist/internal/message-reactions.js.map +1 -1
- package/dist/internal/modal-lifecycle.d.ts.map +1 -1
- package/dist/internal/modal-lifecycle.js +5 -2
- package/dist/internal/modal-lifecycle.js.map +1 -1
- package/dist/internal/pending-content.d.ts +11 -0
- package/dist/internal/pending-content.d.ts.map +1 -0
- package/dist/internal/pending-content.js +16 -0
- package/dist/internal/pending-content.js.map +1 -0
- package/dist/internal/recipe-button.d.ts +8 -31
- package/dist/internal/recipe-button.d.ts.map +1 -1
- package/dist/internal/recipe-button.js +49 -12
- package/dist/internal/recipe-button.js.map +1 -1
- package/dist/internal/screen-dismiss.d.ts +10 -0
- package/dist/internal/screen-dismiss.d.ts.map +1 -0
- package/dist/internal/screen-dismiss.js +62 -0
- package/dist/internal/screen-dismiss.js.map +1 -0
- package/dist/internal/screen-footer.d.ts +1 -1
- package/dist/internal/text-align.d.ts +2 -2
- package/dist/internal/text-align.d.ts.map +1 -1
- package/dist/keyboard.d.ts +3 -2
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +3 -1
- package/dist/keyboard.js.map +1 -1
- package/dist/mentions.js.map +1 -1
- package/dist/navigation.d.ts +3 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +29 -8
- package/dist/navigation.js.map +1 -1
- package/dist/number-field.d.ts +3 -26
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +2 -1
- package/dist/number-field.js.map +1 -1
- package/dist/overlays.d.ts +11 -6
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +19 -14
- package/dist/overlays.js.map +1 -1
- package/dist/primitives.d.ts +5 -31
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +9 -2
- package/dist/provider.js.map +1 -1
- package/dist/public-exports.generated.d.ts +6 -0
- package/dist/public-exports.generated.d.ts.map +1 -1
- package/dist/public-exports.generated.js +18 -0
- package/dist/public-exports.generated.js.map +1 -1
- package/dist/screen-flows.d.ts +13 -5
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +25 -14
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts +33 -8
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +37 -15
- package/dist/screens.js.map +1 -1
- package/dist/sheet-gesture.d.ts +5 -2
- package/dist/sheet-gesture.d.ts.map +1 -1
- package/dist/sheet-gesture.js.map +1 -1
- package/dist/sheet-panel.d.ts +24 -0
- package/dist/sheet-panel.d.ts.map +1 -0
- package/dist/sheet-panel.js +111 -0
- package/dist/sheet-panel.js.map +1 -0
- package/dist/shortcut-rail.d.ts +28 -0
- package/dist/shortcut-rail.d.ts.map +1 -0
- package/dist/shortcut-rail.js +50 -0
- package/dist/shortcut-rail.js.map +1 -0
- package/dist/slider.d.ts +3 -20
- package/dist/slider.d.ts.map +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/status-text.d.ts +3 -6
- package/dist/status-text.d.ts.map +1 -1
- package/dist/status-text.js.map +1 -1
- package/dist/steps.d.ts +2 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +8 -5
- package/dist/steps.js.map +1 -1
- package/dist/table-family.d.ts.map +1 -1
- package/dist/table-family.js.map +1 -1
- package/dist/task-list.d.ts +3 -1
- package/dist/task-list.d.ts.map +1 -1
- package/dist/task-list.js +2 -2
- package/dist/task-list.js.map +1 -1
- package/dist/time-picker.d.ts +9 -0
- package/dist/time-picker.d.ts.map +1 -0
- package/dist/time-picker.js +59 -0
- package/dist/time-picker.js.map +1 -0
- package/package.json +44 -25
package/dist/data-display.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 [
|
|
641
|
-
return (_jsxs(View, { children: [_jsx(ListItemLeadingContext.Provider, { value:
|
|
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: [
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
752
|
-
|
|
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 -
|
|
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 -
|
|
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
|
|
819
|
+
gap,
|
|
777
820
|
},
|
|
778
821
|
layoutStyle,
|
|
779
|
-
], children: descriptor.items.map((item, index) =>
|
|
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
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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
|