@hjmds/react-native 0.8.2
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/README.md +310 -0
- package/dist/actions.d.ts +128 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +157 -0
- package/dist/actions.js.map +1 -0
- package/dist/data-display.d.ts +285 -0
- package/dist/data-display.d.ts.map +1 -0
- package/dist/data-display.js +775 -0
- package/dist/data-display.js.map +1 -0
- package/dist/date-picker.d.ts +25 -0
- package/dist/date-picker.d.ts.map +1 -0
- package/dist/date-picker.js +39 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/evidence.d.ts +954 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +98 -0
- package/dist/evidence.js.map +1 -0
- package/dist/feedback.d.ts +148 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +559 -0
- package/dist/feedback.js.map +1 -0
- package/dist/file-picker.d.ts +20 -0
- package/dist/file-picker.d.ts.map +1 -0
- package/dist/file-picker.js +37 -0
- package/dist/file-picker.js.map +1 -0
- package/dist/forms.d.ts +170 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +595 -0
- package/dist/forms.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs-public.d.ts +6 -0
- package/dist/inputs-public.d.ts.map +1 -0
- package/dist/inputs-public.js +6 -0
- package/dist/inputs-public.js.map +1 -0
- package/dist/inputs.d.ts +258 -0
- package/dist/inputs.d.ts.map +1 -0
- package/dist/inputs.js +681 -0
- package/dist/inputs.js.map +1 -0
- package/dist/internal/modal-lifecycle.d.ts +15 -0
- package/dist/internal/modal-lifecycle.d.ts.map +1 -0
- package/dist/internal/modal-lifecycle.js +17 -0
- package/dist/internal/modal-lifecycle.js.map +1 -0
- package/dist/internal/state.d.ts +8 -0
- package/dist/internal/state.d.ts.map +1 -0
- package/dist/internal/state.js +18 -0
- package/dist/internal/state.js.map +1 -0
- package/dist/internal/styles.d.ts +29 -0
- package/dist/internal/styles.d.ts.map +1 -0
- package/dist/internal/styles.js +51 -0
- package/dist/internal/styles.js.map +1 -0
- package/dist/navigation.d.ts +271 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +978 -0
- package/dist/navigation.js.map +1 -0
- package/dist/number-field.d.ts +58 -0
- package/dist/number-field.d.ts.map +1 -0
- package/dist/number-field.js +172 -0
- package/dist/number-field.js.map +1 -0
- package/dist/otp-field.d.ts +2 -0
- package/dist/otp-field.d.ts.map +1 -0
- package/dist/otp-field.js +2 -0
- package/dist/otp-field.js.map +1 -0
- package/dist/overlays.d.ts +68 -0
- package/dist/overlays.d.ts.map +1 -0
- package/dist/overlays.js +757 -0
- package/dist/overlays.js.map +1 -0
- package/dist/password-field.d.ts +2 -0
- package/dist/password-field.d.ts.map +1 -0
- package/dist/password-field.js +2 -0
- package/dist/password-field.js.map +1 -0
- package/dist/primitives.d.ts +132 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +238 -0
- package/dist/primitives.js.map +1 -0
- package/dist/provider.d.ts +41 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +78 -0
- package/dist/provider.js.map +1 -0
- package/dist/slider.d.ts +45 -0
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +200 -0
- package/dist/slider.js.map +1 -0
- package/dist/steps.d.ts +13 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +13 -0
- package/dist/steps.js.map +1 -0
- package/dist/upload-item.d.ts +13 -0
- package/dist/upload-item.d.ts.map +1 -0
- package/dist/upload-item.js +25 -0
- package/dist/upload-item.js.map +1 -0
- package/package.json +166 -0
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { resolveColorReference } from "@hjmds/design-contracts/color-references";
|
|
3
|
+
import { glyph, radius, spacing } from "@hjmds/design-contracts/foundations";
|
|
4
|
+
import { resolveDescriptionListColumnCount, resolveDescriptionListDescriptor, } from "@hjmds/design-contracts/components/description-list";
|
|
5
|
+
import { resolveStatisticDescriptor, validateStatisticGroup, } from "@hjmds/design-contracts/components/statistic";
|
|
6
|
+
import { resolveTagDescriptor, resolveTagPresentation, tagRecipe, } from "@hjmds/design-contracts/components/tag";
|
|
7
|
+
import { cardRecipe } from "@hjmds/design-contracts/components/card";
|
|
8
|
+
import { imageRecipe, nativeResizeModes, resolveImageAspectRatio, resolveImageDescriptor, resolveImageFallbackAccessibilityLabel, } from "@hjmds/design-contracts/components/image";
|
|
9
|
+
import { resolveTimelineDescriptor, timelineRecipe, } from "@hjmds/design-contracts/components/timeline";
|
|
10
|
+
import { surfaceGeometry } from "@hjmds/design-contracts/recipes/base";
|
|
11
|
+
import { accordionRecipe, counterBadgeRecipe, badgeRecipe, listRecipe, formatCounterBadgeCount, listRowRecipe, statisticRecipe, } from "@hjmds/design-contracts/recipes";
|
|
12
|
+
import { Children, isValidElement, useEffect, useMemo, useState, } from "react";
|
|
13
|
+
import { Image as NativeImage, LayoutAnimation, Pressable, StyleSheet, View, useWindowDimensions, } from "react-native";
|
|
14
|
+
import { useControllableState } from "./internal/state.js";
|
|
15
|
+
import { minimumTargetStyle } from "./internal/styles.js";
|
|
16
|
+
import { Surface, Text, } from "./primitives.js";
|
|
17
|
+
import { useHjmNativeTheme } from "./provider.js";
|
|
18
|
+
export function Badge({ label, tone = badgeRecipe.defaults.tone, size = badgeRecipe.defaults.size, variant = badgeRecipe.defaults.variant, leading, accessibilityLabel, style, labelStyle, ...props }) {
|
|
19
|
+
const theme = useHjmNativeTheme();
|
|
20
|
+
const presentation = badgeRecipe.tones[tone];
|
|
21
|
+
const metrics = badgeRecipe.sizes[size];
|
|
22
|
+
const variantPresentation = badgeRecipe.variants[variant];
|
|
23
|
+
const outlined = !variantPresentation.usesToneBackground;
|
|
24
|
+
const borderColor = presentation.border
|
|
25
|
+
? resolveColorReference(presentation.border, theme.palette)
|
|
26
|
+
: variantPresentation.borderFallback === null
|
|
27
|
+
? "transparent"
|
|
28
|
+
: resolveColorReference(variantPresentation.borderFallback, theme.palette);
|
|
29
|
+
return (_jsxs(View, { ...props, accessibilityLabel: accessibilityLabel ?? String(label), accessible: true, style: [
|
|
30
|
+
{
|
|
31
|
+
alignSelf: "flex-start",
|
|
32
|
+
alignItems: "center",
|
|
33
|
+
backgroundColor: outlined
|
|
34
|
+
? "transparent"
|
|
35
|
+
: resolveColorReference(presentation.background, theme.palette),
|
|
36
|
+
borderColor,
|
|
37
|
+
borderRadius: radius[badgeRecipe.radius],
|
|
38
|
+
borderWidth: presentation.border || variantPresentation.borderFallback
|
|
39
|
+
? badgeRecipe.borderWidth
|
|
40
|
+
: 0,
|
|
41
|
+
direction: theme.environment.direction,
|
|
42
|
+
flexDirection: "row",
|
|
43
|
+
gap: metrics.gap,
|
|
44
|
+
justifyContent: "center",
|
|
45
|
+
minHeight: metrics.minHeight,
|
|
46
|
+
paddingHorizontal: metrics.paddingHorizontal,
|
|
47
|
+
},
|
|
48
|
+
style,
|
|
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
|
+
{
|
|
51
|
+
color: resolveColorReference(outlined ? presentation.outlineContent : presentation.content, theme.palette),
|
|
52
|
+
},
|
|
53
|
+
labelStyle,
|
|
54
|
+
], variant: metrics.textVariant, children: label })] }));
|
|
55
|
+
}
|
|
56
|
+
export function Tag({ children, label, tone, accessibilityLabel, style, labelStyle, ...props }) {
|
|
57
|
+
const theme = useHjmNativeTheme();
|
|
58
|
+
const resolvedLabel = children ?? label;
|
|
59
|
+
if (resolvedLabel === undefined) {
|
|
60
|
+
throw new TypeError("Tag requires children (or the deprecated label prop)");
|
|
61
|
+
}
|
|
62
|
+
const descriptor = resolveTagDescriptor({
|
|
63
|
+
label: resolvedLabel,
|
|
64
|
+
...(tone === undefined ? {} : { tone }),
|
|
65
|
+
});
|
|
66
|
+
const presentation = resolveTagPresentation(descriptor.tone, theme.palette);
|
|
67
|
+
return (_jsx(View, { ...props, accessibilityLabel: accessibilityLabel ?? descriptor.label, accessible: true, style: [
|
|
68
|
+
{
|
|
69
|
+
alignItems: "center",
|
|
70
|
+
alignSelf: "flex-start",
|
|
71
|
+
backgroundColor: presentation.background,
|
|
72
|
+
borderColor: presentation.border ?? "transparent",
|
|
73
|
+
borderRadius: radius[tagRecipe.radius],
|
|
74
|
+
borderWidth: tagRecipe.borderWidth,
|
|
75
|
+
direction: theme.environment.direction,
|
|
76
|
+
flexDirection: "row",
|
|
77
|
+
gap: tagRecipe.size.gap,
|
|
78
|
+
minHeight: tagRecipe.size.minHeight,
|
|
79
|
+
paddingHorizontal: tagRecipe.size.paddingHorizontal,
|
|
80
|
+
},
|
|
81
|
+
style,
|
|
82
|
+
], children: _jsx(Text, { align: "center", emphasis: "medium", style: [{ color: presentation.content }, labelStyle], variant: tagRecipe.size.textVariant, children: descriptor.label }) }));
|
|
83
|
+
}
|
|
84
|
+
export function Card({ children, title, description, leading, media, actions, selected = cardRecipe.defaults.selected, tone = cardRecipe.defaults.tone, bordered = cardRecipe.defaults.bordered, padding = cardRecipe.defaults.padding, style, ...props }) {
|
|
85
|
+
const { environment } = useHjmNativeTheme();
|
|
86
|
+
const bodyPadding = typeof padding === "number" ? padding : surfaceGeometry.paddings[padding];
|
|
87
|
+
const hasHeader = leading !== undefined || title !== undefined || description !== undefined;
|
|
88
|
+
return (_jsxs(Surface, { ...props, bordered: bordered, padding: "none", style: [{ overflow: "hidden" }, style], tone: selected ? cardRecipe.selectedTone : tone, children: [media === undefined ? null : _jsx(View, { children: media }), _jsxs(View, { style: { gap: cardRecipe.body.gap, padding: bodyPadding }, children: [hasHeader ? (_jsxs(View, { style: {
|
|
89
|
+
alignItems: "flex-start",
|
|
90
|
+
direction: environment.direction,
|
|
91
|
+
flexDirection: "row",
|
|
92
|
+
gap: cardRecipe.header.gap,
|
|
93
|
+
}, 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: {
|
|
94
|
+
direction: environment.direction,
|
|
95
|
+
flexDirection: "row",
|
|
96
|
+
flexWrap: "wrap",
|
|
97
|
+
gap: cardRecipe.actions.gap,
|
|
98
|
+
paddingBottom: cardRecipe.actions.paddingBottom,
|
|
99
|
+
paddingHorizontal: cardRecipe.actions.paddingHorizontal,
|
|
100
|
+
}, children: actions }))] }));
|
|
101
|
+
}
|
|
102
|
+
export function ListRow({ title, description, leading, trailing, titleMetadata, badge, trailingAction, trailingText, metadataLabel, trailingLabel, onPress, accessibilityLabel, accessibilityHint, disabled = false, density = listRowRecipe.defaults.density, selected: selectedProp, style, leadingStyle, contentStyle, titleStyle, titleRowStyle, descriptionStyle, trailingStyle, trailingActionStyle, containerProps, accessibilityState, ...props }) {
|
|
103
|
+
const theme = useHjmNativeTheme();
|
|
104
|
+
const metrics = listRowRecipe.density[density];
|
|
105
|
+
const interactive = onPress !== undefined;
|
|
106
|
+
const selected = selectedProp ?? listRowRecipe.defaults.selected;
|
|
107
|
+
const resolvedMetadata = titleMetadata ?? badge;
|
|
108
|
+
const resolvedTrailingLabel = trailingLabel ?? trailingText;
|
|
109
|
+
const composedLabel = accessibilityLabel ?? [
|
|
110
|
+
title,
|
|
111
|
+
metadataLabel,
|
|
112
|
+
description,
|
|
113
|
+
resolvedTrailingLabel,
|
|
114
|
+
].filter(Boolean).join(", ");
|
|
115
|
+
const visualState = {
|
|
116
|
+
backgroundColor: selected
|
|
117
|
+
? resolveColorReference(listRowRecipe.states.selectedBackground, theme.palette)
|
|
118
|
+
: "transparent",
|
|
119
|
+
direction: theme.environment.direction,
|
|
120
|
+
minHeight: description ? metrics.twoLineMinHeight : metrics.oneLineMinHeight,
|
|
121
|
+
opacity: disabled ? listRowRecipe.states.disabledOpacity : 1,
|
|
122
|
+
};
|
|
123
|
+
const rowContent = (_jsxs(_Fragment, { children: [leading ? (_jsx(View, { accessible: interactive ? false : undefined, importantForAccessibility: interactive ? "no-hide-descendants" : "auto", style: leadingStyle, children: leading })) : null, _jsxs(View, { style: [{ flex: 1, gap: spacing.xxs, minWidth: 0 }, contentStyle], children: [_jsxs(View, { style: [
|
|
124
|
+
{
|
|
125
|
+
alignItems: "center",
|
|
126
|
+
direction: theme.environment.direction,
|
|
127
|
+
flexDirection: "row",
|
|
128
|
+
flexWrap: "wrap",
|
|
129
|
+
gap: spacing.xxs,
|
|
130
|
+
},
|
|
131
|
+
titleRowStyle,
|
|
132
|
+
], children: [_jsx(Text, { style: [
|
|
133
|
+
{
|
|
134
|
+
color: resolveColorReference(listRowRecipe.title.color, theme.palette),
|
|
135
|
+
flexShrink: 1,
|
|
136
|
+
fontWeight: listRowRecipe.title.fontWeight,
|
|
137
|
+
},
|
|
138
|
+
titleStyle,
|
|
139
|
+
], variant: listRowRecipe.title.textVariant, children: title }), resolvedMetadata === undefined ? null : (_jsx(View, { accessible: interactive ? false : undefined, importantForAccessibility: interactive ? "no-hide-descendants" : "auto", children: resolvedMetadata }))] }), description ? (_jsx(Text, { style: [
|
|
140
|
+
{ color: resolveColorReference(listRowRecipe.description.color, theme.palette) },
|
|
141
|
+
descriptionStyle,
|
|
142
|
+
], variant: listRowRecipe.description.textVariant, children: description })) : null] }), trailingText || trailing ? (_jsx(View, { accessible: interactive ? false : undefined, importantForAccessibility: interactive ? "no-hide-descendants" : "auto", style: [{ flexShrink: 0 }, trailingStyle], children: trailingText ? (_jsx(Text, { style: { color: resolveColorReference(listRowRecipe.trailing.textColor, theme.palette) }, variant: listRowRecipe.trailing.textVariant, children: trailingText })) : trailing })) : null] }));
|
|
143
|
+
const contentStyleFor = (pressed) => [
|
|
144
|
+
minimumTargetStyle,
|
|
145
|
+
{
|
|
146
|
+
alignItems: "center",
|
|
147
|
+
backgroundColor: pressed
|
|
148
|
+
? resolveColorReference(listRowRecipe.states.pressedBackground, theme.palette)
|
|
149
|
+
: trailingAction
|
|
150
|
+
? "transparent"
|
|
151
|
+
: visualState.backgroundColor,
|
|
152
|
+
direction: visualState.direction,
|
|
153
|
+
flex: trailingAction ? 1 : undefined,
|
|
154
|
+
flexDirection: "row",
|
|
155
|
+
gap: listRowRecipe.gap,
|
|
156
|
+
minHeight: visualState.minHeight,
|
|
157
|
+
opacity: trailingAction ? 1 : visualState.opacity,
|
|
158
|
+
...(trailingAction
|
|
159
|
+
? { paddingStart: metrics.paddingHorizontal }
|
|
160
|
+
: { paddingHorizontal: metrics.paddingHorizontal }),
|
|
161
|
+
paddingVertical: metrics.paddingVertical,
|
|
162
|
+
},
|
|
163
|
+
trailingAction ? undefined : style,
|
|
164
|
+
];
|
|
165
|
+
const main = interactive ? (_jsx(Pressable, { ...props, accessibilityHint: accessibilityHint, accessibilityLabel: composedLabel, accessibilityRole: "button", accessibilityState: {
|
|
166
|
+
...accessibilityState,
|
|
167
|
+
disabled,
|
|
168
|
+
...(selectedProp === undefined ? {} : { selected: selectedProp }),
|
|
169
|
+
}, disabled: disabled, onPress: onPress, style: ({ pressed }) => contentStyleFor(pressed), children: rowContent })) : (_jsx(View, { ...props, accessibilityHint: accessibilityHint, accessibilityLabel: accessibilityLabel, accessible: accessibilityLabel === undefined ? undefined : true, style: contentStyleFor(false), children: rowContent }));
|
|
170
|
+
if (trailingAction === undefined || trailingAction === null)
|
|
171
|
+
return main;
|
|
172
|
+
return (_jsxs(View, { ...containerProps, style: [
|
|
173
|
+
{
|
|
174
|
+
alignItems: "center",
|
|
175
|
+
backgroundColor: visualState.backgroundColor,
|
|
176
|
+
direction: visualState.direction,
|
|
177
|
+
flexDirection: "row",
|
|
178
|
+
minHeight: visualState.minHeight,
|
|
179
|
+
opacity: visualState.opacity,
|
|
180
|
+
},
|
|
181
|
+
style,
|
|
182
|
+
], children: [main, _jsx(View, { style: [
|
|
183
|
+
{ flexShrink: 0, paddingEnd: metrics.paddingHorizontal },
|
|
184
|
+
trailingActionStyle,
|
|
185
|
+
], children: trailingAction })] }));
|
|
186
|
+
}
|
|
187
|
+
function resolveInitials(name, provided) {
|
|
188
|
+
if (provided?.trim())
|
|
189
|
+
return provided.trim().slice(0, 3).toLocaleUpperCase();
|
|
190
|
+
const parts = name.trim().split(/\s+/u).filter(Boolean);
|
|
191
|
+
if (parts.length === 0)
|
|
192
|
+
throw new TypeError("Avatar name must not be empty");
|
|
193
|
+
return `${parts[0][0] ?? ""}${parts.length > 1 ? parts.at(-1)[0] ?? "" : ""}`
|
|
194
|
+
.toLocaleUpperCase();
|
|
195
|
+
}
|
|
196
|
+
export function Avatar({ source, name, initials, size = 44, decorative = false, accessibilityLabel, style, imageStyle, }) {
|
|
197
|
+
if (!Number.isFinite(size) || size < 24)
|
|
198
|
+
throw new RangeError("Avatar size must be at least 24");
|
|
199
|
+
const { colors } = useHjmNativeTheme();
|
|
200
|
+
const [failed, setFailed] = useState(false);
|
|
201
|
+
const fallback = resolveInitials(name, initials);
|
|
202
|
+
const mediaAccessibility = decorative
|
|
203
|
+
? { accessible: false }
|
|
204
|
+
: { accessible: true, accessibilityLabel, accessibilityRole: "image" };
|
|
205
|
+
return (_jsx(View, { ...mediaAccessibility, style: [
|
|
206
|
+
{
|
|
207
|
+
alignItems: "center",
|
|
208
|
+
backgroundColor: colors.surfaceAccent,
|
|
209
|
+
borderRadius: radius.full,
|
|
210
|
+
height: size,
|
|
211
|
+
justifyContent: "center",
|
|
212
|
+
overflow: "hidden",
|
|
213
|
+
width: size,
|
|
214
|
+
},
|
|
215
|
+
style,
|
|
216
|
+
], children: source !== undefined && !failed ? (_jsx(NativeImage, { accessible: false, onError: () => setFailed(true), source: source, style: [{ height: size, width: size }, imageStyle] })) : (_jsx(Text, { align: "center", style: { color: colors.contentBrand }, variant: "label", children: fallback })) }));
|
|
217
|
+
}
|
|
218
|
+
export function Divider({ orientation = "horizontal", inset = 0, style }) {
|
|
219
|
+
if (!Number.isFinite(inset) || inset < 0)
|
|
220
|
+
throw new RangeError("Divider inset must be non-negative");
|
|
221
|
+
const { colors } = useHjmNativeTheme();
|
|
222
|
+
return (_jsx(View, { accessible: false, style: [
|
|
223
|
+
orientation === "horizontal"
|
|
224
|
+
? { backgroundColor: colors.border, height: 1, marginHorizontal: inset, width: "auto" }
|
|
225
|
+
: { alignSelf: "stretch", backgroundColor: colors.border, marginVertical: inset, width: 1 },
|
|
226
|
+
style,
|
|
227
|
+
] }));
|
|
228
|
+
}
|
|
229
|
+
export function Accordion({ label, items, expandedValues, defaultExpandedValues = [], onExpandedValuesChange, multiple = accordionRecipe.defaults.allowsMultipleExpanded, density = accordionRecipe.defaults.density, renderIndicator, style, itemStyle, triggerStyle, titleStyle, indicatorStyle, panelStyle, }) {
|
|
230
|
+
if (items.length === 0)
|
|
231
|
+
throw new Error("Accordion requires at least one item");
|
|
232
|
+
const itemValues = new Set(items.map((item) => item.value));
|
|
233
|
+
if (itemValues.size !== items.length)
|
|
234
|
+
throw new TypeError("Accordion values must be unique");
|
|
235
|
+
const initial = expandedValues ?? defaultExpandedValues;
|
|
236
|
+
if (initial.some((value) => !itemValues.has(value))) {
|
|
237
|
+
throw new RangeError("Accordion expanded values must match items");
|
|
238
|
+
}
|
|
239
|
+
if (!multiple && initial.length > 1) {
|
|
240
|
+
throw new RangeError("Accordion only accepts one expanded value unless multiple is true");
|
|
241
|
+
}
|
|
242
|
+
const theme = useHjmNativeTheme();
|
|
243
|
+
const metrics = accordionRecipe.density[density];
|
|
244
|
+
const indicatorColor = resolveColorReference(accordionRecipe.indicator.color, theme.palette);
|
|
245
|
+
const [expanded, setExpanded] = useControllableState({
|
|
246
|
+
...(expandedValues === undefined ? {} : { value: expandedValues }),
|
|
247
|
+
defaultValue: defaultExpandedValues,
|
|
248
|
+
...(onExpandedValuesChange === undefined ? {} : { onChange: onExpandedValuesChange }),
|
|
249
|
+
});
|
|
250
|
+
return (_jsx(View, { accessibilityLabel: label, accessibilityRole: "list", style: style, children: items.map((item) => {
|
|
251
|
+
const isExpanded = expanded.includes(item.value);
|
|
252
|
+
return (_jsxs(View, { style: [
|
|
253
|
+
{
|
|
254
|
+
borderBottomColor: resolveColorReference(accordionRecipe.divider, theme.palette),
|
|
255
|
+
borderBottomWidth: 1,
|
|
256
|
+
},
|
|
257
|
+
itemStyle,
|
|
258
|
+
], children: [_jsxs(Pressable, { accessibilityHint: item.accessibilityHint, accessibilityLabel: item.accessibilityLabel ?? item.title, accessibilityRole: "button", accessibilityState: { disabled: item.disabled === true, expanded: isExpanded }, disabled: item.disabled, onPress: () => {
|
|
259
|
+
if (!theme.environment.reducedMotion) {
|
|
260
|
+
LayoutAnimation.configureNext({
|
|
261
|
+
duration: accordionRecipe.transition.duration,
|
|
262
|
+
update: { type: LayoutAnimation.Types.easeInEaseOut },
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (isExpanded) {
|
|
266
|
+
setExpanded(expanded.filter((value) => value !== item.value));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
setExpanded(multiple ? [...expanded, item.value] : [item.value]);
|
|
270
|
+
}
|
|
271
|
+
}, style: ({ pressed }) => [
|
|
272
|
+
minimumTargetStyle,
|
|
273
|
+
{
|
|
274
|
+
alignItems: "center",
|
|
275
|
+
backgroundColor: pressed
|
|
276
|
+
? resolveColorReference(accordionRecipe.states.pressedBackground, theme.palette)
|
|
277
|
+
: "transparent",
|
|
278
|
+
direction: theme.environment.direction,
|
|
279
|
+
flexDirection: "row",
|
|
280
|
+
gap: accordionRecipe.gap,
|
|
281
|
+
minHeight: metrics.triggerMinHeight,
|
|
282
|
+
opacity: item.disabled
|
|
283
|
+
? accordionRecipe.states.disabledOpacity
|
|
284
|
+
: 1,
|
|
285
|
+
paddingHorizontal: accordionRecipe.paddingHorizontal,
|
|
286
|
+
paddingVertical: metrics.paddingVertical,
|
|
287
|
+
},
|
|
288
|
+
triggerStyle,
|
|
289
|
+
], children: [_jsxs(View, { style: { flex: 1, gap: spacing.xxs }, children: [_jsx(Text, { style: [
|
|
290
|
+
{
|
|
291
|
+
color: resolveColorReference(accordionRecipe.title.color, theme.palette),
|
|
292
|
+
fontWeight: accordionRecipe.title.fontWeight,
|
|
293
|
+
},
|
|
294
|
+
titleStyle,
|
|
295
|
+
], variant: accordionRecipe.title.textVariant, children: item.title }), item.description ? _jsx(Text, { tone: "muted", variant: "caption", children: item.description }) : null] }), _jsx(View, { accessible: false, style: indicatorStyle, children: renderIndicator ? (renderIndicator({
|
|
296
|
+
value: item.value,
|
|
297
|
+
expanded: isExpanded,
|
|
298
|
+
disabled: item.disabled === true,
|
|
299
|
+
color: indicatorColor,
|
|
300
|
+
size: glyph[accordionRecipe.indicator.glyph],
|
|
301
|
+
})) : (_jsx(Text, { style: { color: indicatorColor }, children: isExpanded ? "−" : "+" })) })] }), isExpanded ? (_jsx(View, { accessibilityLabel: item.contentAccessibilityLabel, style: [
|
|
302
|
+
{
|
|
303
|
+
paddingBottom: accordionRecipe.panel.paddingBottom,
|
|
304
|
+
paddingStart: accordionRecipe.panel.paddingInlineStart,
|
|
305
|
+
},
|
|
306
|
+
panelStyle,
|
|
307
|
+
], children: item.content })) : null] }, item.value));
|
|
308
|
+
}) }));
|
|
309
|
+
}
|
|
310
|
+
export function DescriptionList({ label, descriptor, availableWidth, style, itemStyle, onLayout, ...props }) {
|
|
311
|
+
const resolved = resolveDescriptionListDescriptor(descriptor);
|
|
312
|
+
const { width: windowWidth } = useWindowDimensions();
|
|
313
|
+
const { environment } = useHjmNativeTheme();
|
|
314
|
+
const [measuredWidth, setMeasuredWidth] = useState(null);
|
|
315
|
+
const innerWidth = availableWidth ?? measuredWidth ?? windowWidth;
|
|
316
|
+
if (!Number.isFinite(innerWidth) || innerWidth <= 0) {
|
|
317
|
+
throw new RangeError("DescriptionList availableWidth must be positive");
|
|
318
|
+
}
|
|
319
|
+
const columns = resolveDescriptionListColumnCount(innerWidth, resolved.columns, environment.textScale);
|
|
320
|
+
const itemWidth = (innerWidth - spacing.sm * (columns - 1)) / columns;
|
|
321
|
+
const handleLayout = (event) => {
|
|
322
|
+
onLayout?.(event);
|
|
323
|
+
if (availableWidth !== undefined)
|
|
324
|
+
return;
|
|
325
|
+
const nextWidth = event.nativeEvent.layout.width;
|
|
326
|
+
if (Number.isFinite(nextWidth) && nextWidth > 0) {
|
|
327
|
+
setMeasuredWidth((current) => current === nextWidth ? current : nextWidth);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
return (_jsx(View, { ...props, accessibilityLabel: label, accessibilityRole: "list", onLayout: handleLayout, style: [
|
|
331
|
+
{
|
|
332
|
+
direction: environment.direction,
|
|
333
|
+
flexDirection: "row",
|
|
334
|
+
flexWrap: "wrap",
|
|
335
|
+
gap: spacing.sm,
|
|
336
|
+
},
|
|
337
|
+
style,
|
|
338
|
+
], children: resolved.items.map((item) => (_jsxs(View, { accessibilityLabel: `${item.label}, ${item.value}`, accessible: true, style: [{ gap: spacing.xxs, width: itemWidth }, itemStyle], children: [_jsx(Text, { accessible: false, tone: "muted", variant: "label", children: item.label }), _jsx(Text, { accessible: false, tone: "primary", children: item.value })] }, item.id))) }));
|
|
339
|
+
}
|
|
340
|
+
function resolveLegacyImageSourceKey(source) {
|
|
341
|
+
if (typeof source === "number")
|
|
342
|
+
return `asset:${source}`;
|
|
343
|
+
try {
|
|
344
|
+
return `source:${JSON.stringify(source)}`;
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return `source:${String(source)}`;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function resolveLegacyMedia(decorative, accessibilityLabel) {
|
|
351
|
+
const resolvedDecorative = decorative ?? accessibilityLabel === undefined;
|
|
352
|
+
if (resolvedDecorative) {
|
|
353
|
+
if (accessibilityLabel !== undefined) {
|
|
354
|
+
throw new TypeError("Decorative Image must not provide accessibilityLabel");
|
|
355
|
+
}
|
|
356
|
+
return { decorative: true };
|
|
357
|
+
}
|
|
358
|
+
if (accessibilityLabel === undefined || accessibilityLabel.trim().length === 0) {
|
|
359
|
+
throw new TypeError("Informative Image accessibilityLabel must not be empty");
|
|
360
|
+
}
|
|
361
|
+
return { decorative: false, accessibilityLabel };
|
|
362
|
+
}
|
|
363
|
+
/** Intrinsic-size Native image with canonical fit, accessibility, and fallback semantics. */
|
|
364
|
+
export function Image(imageProps) {
|
|
365
|
+
const { source: legacySource, src, width, height, fit, decorative, accessibilityLabel, sourceAdapter, fallback, onError, onLoad, onLoadStatusChange, renderImage, resizeMode, style, containerStyle, ...nativeProps } = imageProps;
|
|
366
|
+
const theme = useHjmNativeTheme();
|
|
367
|
+
if (src !== undefined && legacySource !== undefined) {
|
|
368
|
+
throw new TypeError("Image accepts either canonical src or legacy source, not both");
|
|
369
|
+
}
|
|
370
|
+
if (src === undefined && legacySource === undefined) {
|
|
371
|
+
throw new TypeError("Image requires src or legacy source");
|
|
372
|
+
}
|
|
373
|
+
const descriptor = src === undefined
|
|
374
|
+
? undefined
|
|
375
|
+
: resolveImageDescriptor({
|
|
376
|
+
src,
|
|
377
|
+
width: width,
|
|
378
|
+
height: height,
|
|
379
|
+
...(fit === undefined ? {} : { fit }),
|
|
380
|
+
...(decorative === undefined ? {} : { decorative }),
|
|
381
|
+
...(accessibilityLabel === undefined ? {} : { accessibilityLabel }),
|
|
382
|
+
});
|
|
383
|
+
const legacyMedia = descriptor === undefined
|
|
384
|
+
? resolveLegacyMedia(decorative, accessibilityLabel)
|
|
385
|
+
: undefined;
|
|
386
|
+
const resolvedDecorative = descriptor?.decorative ?? legacyMedia.decorative;
|
|
387
|
+
const resolvedAccessibilityLabel = descriptor?.decorative === false
|
|
388
|
+
? descriptor.accessibilityLabel
|
|
389
|
+
: legacyMedia?.accessibilityLabel;
|
|
390
|
+
const resolvedFit = descriptor?.fit ?? fit;
|
|
391
|
+
const resolvedResizeMode = descriptor === undefined
|
|
392
|
+
? resizeMode ?? (resolvedFit === undefined ? undefined : nativeResizeModes[resolvedFit])
|
|
393
|
+
: nativeResizeModes[descriptor.fit];
|
|
394
|
+
const sourceKey = descriptor === undefined
|
|
395
|
+
? resolveLegacyImageSourceKey(legacySource)
|
|
396
|
+
: `src:${descriptor.src}`;
|
|
397
|
+
const source = useMemo(() => descriptor === undefined
|
|
398
|
+
? legacySource
|
|
399
|
+
: sourceAdapter?.(descriptor) ?? { uri: descriptor.src }, [
|
|
400
|
+
descriptor?.accessibilityLabel,
|
|
401
|
+
descriptor?.decorative,
|
|
402
|
+
descriptor?.fit,
|
|
403
|
+
descriptor?.height,
|
|
404
|
+
descriptor?.src,
|
|
405
|
+
descriptor?.width,
|
|
406
|
+
legacySource,
|
|
407
|
+
sourceAdapter,
|
|
408
|
+
]);
|
|
409
|
+
const [state, setState] = useState({ sourceKey, status: "loading" });
|
|
410
|
+
const status = state.sourceKey === sourceKey ? state.status : "loading";
|
|
411
|
+
useEffect(() => {
|
|
412
|
+
setState((current) => current.sourceKey === sourceKey
|
|
413
|
+
? current
|
|
414
|
+
: { sourceKey, status: "loading" });
|
|
415
|
+
}, [sourceKey]);
|
|
416
|
+
const reportLoad = (event) => {
|
|
417
|
+
setState((current) => current.sourceKey === sourceKey
|
|
418
|
+
? { sourceKey, status: "loaded" }
|
|
419
|
+
: current);
|
|
420
|
+
onLoadStatusChange?.("loaded");
|
|
421
|
+
onLoad?.(event);
|
|
422
|
+
};
|
|
423
|
+
const reportError = (event) => {
|
|
424
|
+
setState((current) => current.sourceKey === sourceKey
|
|
425
|
+
? { sourceKey, status: "error" }
|
|
426
|
+
: current);
|
|
427
|
+
onLoadStatusChange?.("error");
|
|
428
|
+
onError?.(event);
|
|
429
|
+
};
|
|
430
|
+
const handleLoad = reportLoad;
|
|
431
|
+
const handleError = reportError;
|
|
432
|
+
const assetStyle = descriptor === undefined
|
|
433
|
+
? style
|
|
434
|
+
: [StyleSheet.absoluteFill, style];
|
|
435
|
+
const adapterBase = {
|
|
436
|
+
source,
|
|
437
|
+
accessible: !resolvedDecorative,
|
|
438
|
+
...(!resolvedDecorative && resolvedAccessibilityLabel !== undefined
|
|
439
|
+
? {
|
|
440
|
+
accessibilityRole: "image",
|
|
441
|
+
accessibilityLabel: resolvedAccessibilityLabel,
|
|
442
|
+
}
|
|
443
|
+
: {}),
|
|
444
|
+
onError: handleError,
|
|
445
|
+
onLoad: handleLoad,
|
|
446
|
+
reportError,
|
|
447
|
+
reportLoad,
|
|
448
|
+
...(resolvedResizeMode === undefined ? {} : { resizeMode: resolvedResizeMode }),
|
|
449
|
+
status: status === "loaded" ? "loaded" : "loading",
|
|
450
|
+
...(assetStyle === undefined ? {} : { style: assetStyle }),
|
|
451
|
+
nativeProps: {
|
|
452
|
+
...nativeProps,
|
|
453
|
+
...(descriptor === undefined && width !== undefined ? { width } : {}),
|
|
454
|
+
...(descriptor === undefined && height !== undefined ? { height } : {}),
|
|
455
|
+
...(resolvedResizeMode === undefined ? {} : { resizeMode: resolvedResizeMode }),
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
const placeholderBackground = resolveColorReference(imageRecipe.placeholder.background, theme.palette);
|
|
459
|
+
const fallbackLabel = descriptor === undefined
|
|
460
|
+
? resolvedAccessibilityLabel
|
|
461
|
+
: resolveImageFallbackAccessibilityLabel(descriptor);
|
|
462
|
+
let visual;
|
|
463
|
+
if (status === "error") {
|
|
464
|
+
visual = (_jsx(View, { ...(resolvedDecorative
|
|
465
|
+
? {
|
|
466
|
+
accessibilityElementsHidden: true,
|
|
467
|
+
accessible: false,
|
|
468
|
+
importantForAccessibility: "no-hide-descendants",
|
|
469
|
+
}
|
|
470
|
+
: {
|
|
471
|
+
accessible: true,
|
|
472
|
+
accessibilityLabel: fallbackLabel,
|
|
473
|
+
accessibilityRole: "image",
|
|
474
|
+
}), style: StyleSheet.absoluteFill, children: _jsx(View, { accessibilityElementsHidden: true, accessible: false, importantForAccessibility: "no-hide-descendants", style: styles.imageFallbackContent, children: fallback ?? (_jsx(View, { accessible: false, style: [
|
|
475
|
+
styles.imageFallbackIcon,
|
|
476
|
+
{ borderColor: theme.colors.textMuted },
|
|
477
|
+
], children: _jsx(Text, { accessible: false, emphasis: "strong", style: { color: theme.colors.textMuted }, variant: "label", children: "!" }) })) }) }));
|
|
478
|
+
}
|
|
479
|
+
else if (renderImage === undefined) {
|
|
480
|
+
visual = (_jsx(NativeImage, { ...adapterBase.nativeProps, ...(resolvedDecorative
|
|
481
|
+
? { accessible: false }
|
|
482
|
+
: {
|
|
483
|
+
accessible: true,
|
|
484
|
+
accessibilityLabel: resolvedAccessibilityLabel,
|
|
485
|
+
accessibilityRole: "image",
|
|
486
|
+
}), onError: handleError, onLoad: handleLoad, resizeMode: resolvedResizeMode, source: source, style: assetStyle }));
|
|
487
|
+
}
|
|
488
|
+
else if (descriptor === undefined) {
|
|
489
|
+
visual = renderImage({
|
|
490
|
+
...adapterBase,
|
|
491
|
+
...(resolvedFit === undefined ? {} : { fit: resolvedFit }),
|
|
492
|
+
...(width === undefined ? {} : { width }),
|
|
493
|
+
...(height === undefined ? {} : { height }),
|
|
494
|
+
legacySource: true,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
visual = renderImage({
|
|
499
|
+
...adapterBase,
|
|
500
|
+
descriptor,
|
|
501
|
+
src: descriptor.src,
|
|
502
|
+
width: descriptor.width,
|
|
503
|
+
height: descriptor.height,
|
|
504
|
+
fit: descriptor.fit,
|
|
505
|
+
legacySource: false,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return (_jsx(View, { style: [
|
|
509
|
+
{
|
|
510
|
+
alignItems: "center",
|
|
511
|
+
backgroundColor: placeholderBackground,
|
|
512
|
+
borderRadius: radius[imageRecipe.radius],
|
|
513
|
+
justifyContent: "center",
|
|
514
|
+
overflow: "hidden",
|
|
515
|
+
...(descriptor === undefined
|
|
516
|
+
? {}
|
|
517
|
+
: {
|
|
518
|
+
aspectRatio: resolveImageAspectRatio(descriptor.width, descriptor.height),
|
|
519
|
+
width: descriptor.width,
|
|
520
|
+
}),
|
|
521
|
+
},
|
|
522
|
+
containerStyle,
|
|
523
|
+
], children: visual }));
|
|
524
|
+
}
|
|
525
|
+
const styles = StyleSheet.create({
|
|
526
|
+
imageFallbackContent: {
|
|
527
|
+
alignItems: "center",
|
|
528
|
+
flex: 1,
|
|
529
|
+
justifyContent: "center",
|
|
530
|
+
},
|
|
531
|
+
imageFallbackIcon: {
|
|
532
|
+
alignItems: "center",
|
|
533
|
+
borderRadius: radius.full,
|
|
534
|
+
borderWidth: 2,
|
|
535
|
+
height: glyph.lg,
|
|
536
|
+
justifyContent: "center",
|
|
537
|
+
width: glyph.lg,
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
export function CounterBadge({ count, accessibilityLabel, max = counterBadgeRecipe.defaults.max, tone = counterBadgeRecipe.defaults.tone, size = counterBadgeRecipe.defaults.size, variant = counterBadgeRecipe.defaults.variant, style, }) {
|
|
541
|
+
if (accessibilityLabel !== undefined && !accessibilityLabel.trim()) {
|
|
542
|
+
throw new TypeError("CounterBadge accessibilityLabel must not be empty");
|
|
543
|
+
}
|
|
544
|
+
const visibleLabel = formatCounterBadgeCount(count, max);
|
|
545
|
+
if (visibleLabel === null)
|
|
546
|
+
return null;
|
|
547
|
+
const { colors } = useHjmNativeTheme();
|
|
548
|
+
const presentation = {
|
|
549
|
+
danger: { background: colors.dangerFill, content: colors.onDanger },
|
|
550
|
+
brand: { background: colors.primary, content: colors.onPrimary },
|
|
551
|
+
neutral: { background: colors.textBody, content: colors.bg },
|
|
552
|
+
};
|
|
553
|
+
const metrics = counterBadgeRecipe.sizes[size];
|
|
554
|
+
return (_jsx(View, { accessibilityLabel: accessibilityLabel, accessibilityRole: accessibilityLabel === undefined ? undefined : "text", accessible: accessibilityLabel !== undefined, importantForAccessibility: accessibilityLabel === undefined ? "no-hide-descendants" : "yes", style: [
|
|
555
|
+
{
|
|
556
|
+
alignItems: "center",
|
|
557
|
+
alignSelf: "flex-start",
|
|
558
|
+
backgroundColor: presentation[tone].background,
|
|
559
|
+
borderColor: variant === "floating" ? colors.bg : "transparent",
|
|
560
|
+
borderRadius: radius.full,
|
|
561
|
+
borderWidth: variant === "floating" ? 2 : 0,
|
|
562
|
+
justifyContent: "center",
|
|
563
|
+
minHeight: metrics.height,
|
|
564
|
+
minWidth: metrics.minWidth,
|
|
565
|
+
paddingHorizontal: metrics.paddingHorizontal,
|
|
566
|
+
},
|
|
567
|
+
style,
|
|
568
|
+
], children: _jsx(Text, { accessible: false, align: "center", style: { color: presentation[tone].content, fontWeight: "700" }, variant: "caption", children: visibleLabel }) }));
|
|
569
|
+
}
|
|
570
|
+
/** Semantic list container that owns separator rhythm around composed rows. */
|
|
571
|
+
export function List({ label, children, separator = listRecipe.defaults.separator, appearance = "plain", style, ...props }) {
|
|
572
|
+
const { colors, environment } = useHjmNativeTheme();
|
|
573
|
+
const items = Children.toArray(children);
|
|
574
|
+
const separatorContract = listRecipe.separators[separator];
|
|
575
|
+
if (!label.trim())
|
|
576
|
+
throw new TypeError("List label must not be empty");
|
|
577
|
+
return (_jsx(View, { ...props, accessibilityLabel: label, accessibilityRole: "list", style: [
|
|
578
|
+
{
|
|
579
|
+
direction: environment.direction,
|
|
580
|
+
...(appearance === "grouped"
|
|
581
|
+
? {
|
|
582
|
+
backgroundColor: colors.surface,
|
|
583
|
+
borderRadius: radius.lg,
|
|
584
|
+
overflow: "hidden",
|
|
585
|
+
}
|
|
586
|
+
: {}),
|
|
587
|
+
},
|
|
588
|
+
style,
|
|
589
|
+
], children: items.map((item, index) => (_jsxs(View, { children: [item, separatorContract && index < items.length - 1 ? (_jsx(View, { accessible: false, style: {
|
|
590
|
+
backgroundColor: colors.border,
|
|
591
|
+
height: 1,
|
|
592
|
+
marginEnd: separatorContract.insetEnd,
|
|
593
|
+
marginStart: separatorContract.insetStart,
|
|
594
|
+
} })) : null] }, isValidElement(item) && item.key !== null ? item.key : `hjm-list-${index}`))) }));
|
|
595
|
+
}
|
|
596
|
+
export function Statistic({ descriptor, density = "comfortable", presentation = "plain", contextLabel, accessibilityLabel, composeAccessibilityLabel, renderTrendMark, style, labelStyle, valueStyle, affixStyle, trendStyle, hintStyle, }) {
|
|
597
|
+
const resolved = resolveStatisticDescriptor(descriptor);
|
|
598
|
+
const theme = useHjmNativeTheme();
|
|
599
|
+
const densityContract = statisticRecipe.density[density];
|
|
600
|
+
const presentationContract = statisticRecipe.presentations[presentation];
|
|
601
|
+
const valueCopy = `${resolved.prefix ?? ""}${resolved.value}${resolved.suffix ?? ""}`;
|
|
602
|
+
if (contextLabel !== undefined && !contextLabel.trim()) {
|
|
603
|
+
throw new TypeError("Statistic contextLabel must not be empty");
|
|
604
|
+
}
|
|
605
|
+
const announcement = accessibilityLabel ?? composeAccessibilityLabel?.({
|
|
606
|
+
...(contextLabel === undefined ? {} : { contextLabel }),
|
|
607
|
+
descriptor: resolved,
|
|
608
|
+
valueText: valueCopy,
|
|
609
|
+
}) ?? [contextLabel, resolved.label, valueCopy, resolved.trend?.label, resolved.hint]
|
|
610
|
+
.filter(Boolean)
|
|
611
|
+
.join(", ");
|
|
612
|
+
if (!announcement.trim()) {
|
|
613
|
+
throw new TypeError("Statistic accessibility label must not be empty");
|
|
614
|
+
}
|
|
615
|
+
const trendMark = resolved.trend
|
|
616
|
+
? statisticRecipe.trend.marks[resolved.trend.direction]
|
|
617
|
+
: undefined;
|
|
618
|
+
const trendColor = resolved.trend
|
|
619
|
+
? resolveColorReference(statisticRecipe.trend.tones[resolved.trend.tone], theme.palette)
|
|
620
|
+
: undefined;
|
|
621
|
+
return (_jsxs(View, { accessibilityLabel: announcement, accessible: true, style: [
|
|
622
|
+
{
|
|
623
|
+
backgroundColor: presentationContract.background
|
|
624
|
+
? resolveColorReference(presentationContract.background, theme.palette)
|
|
625
|
+
: "transparent",
|
|
626
|
+
borderColor: presentationContract.border
|
|
627
|
+
? resolveColorReference(presentationContract.border, theme.palette)
|
|
628
|
+
: "transparent",
|
|
629
|
+
borderRadius: radius[presentationContract.radius],
|
|
630
|
+
borderWidth: presentationContract.borderWidth,
|
|
631
|
+
gap: densityContract.gap,
|
|
632
|
+
minWidth: 0,
|
|
633
|
+
padding: densityContract.padding,
|
|
634
|
+
},
|
|
635
|
+
style,
|
|
636
|
+
], children: [_jsx(Text, { accessible: false, style: [
|
|
637
|
+
{
|
|
638
|
+
color: resolveColorReference(statisticRecipe.label.color, theme.palette),
|
|
639
|
+
fontWeight: statisticRecipe.label.fontWeight,
|
|
640
|
+
},
|
|
641
|
+
labelStyle,
|
|
642
|
+
], variant: densityContract.labelVariant, children: resolved.label }), _jsxs(View, { accessible: false, style: {
|
|
643
|
+
alignItems: "baseline",
|
|
644
|
+
direction: theme.environment.direction,
|
|
645
|
+
flexDirection: "row",
|
|
646
|
+
flexWrap: "wrap",
|
|
647
|
+
gap: spacing.xxs,
|
|
648
|
+
minWidth: 0,
|
|
649
|
+
}, children: [resolved.prefix ? (_jsx(Text, { accessible: false, style: [
|
|
650
|
+
{
|
|
651
|
+
color: resolveColorReference(statisticRecipe.affix.color, theme.palette),
|
|
652
|
+
fontWeight: statisticRecipe.affix.fontWeight,
|
|
653
|
+
},
|
|
654
|
+
affixStyle,
|
|
655
|
+
], variant: statisticRecipe.affix.textVariant, children: resolved.prefix })) : null, _jsx(Text, { accessible: false, style: [
|
|
656
|
+
{
|
|
657
|
+
color: resolveColorReference(statisticRecipe.value.color, theme.palette),
|
|
658
|
+
flexShrink: 1,
|
|
659
|
+
fontVariant: ["tabular-nums"],
|
|
660
|
+
fontWeight: statisticRecipe.value.fontWeight,
|
|
661
|
+
},
|
|
662
|
+
valueStyle,
|
|
663
|
+
], variant: densityContract.valueVariant, children: resolved.value }), resolved.suffix ? (_jsx(Text, { accessible: false, style: [
|
|
664
|
+
{
|
|
665
|
+
color: resolveColorReference(statisticRecipe.affix.color, theme.palette),
|
|
666
|
+
fontWeight: statisticRecipe.affix.fontWeight,
|
|
667
|
+
},
|
|
668
|
+
affixStyle,
|
|
669
|
+
], variant: statisticRecipe.affix.textVariant, children: resolved.suffix })) : null] }), resolved.trend ? (_jsxs(View, { accessible: false, style: {
|
|
670
|
+
alignItems: "center",
|
|
671
|
+
flexDirection: "row",
|
|
672
|
+
flexWrap: "wrap",
|
|
673
|
+
gap: statisticRecipe.trend.gap,
|
|
674
|
+
minWidth: 0,
|
|
675
|
+
}, children: [renderTrendMark && trendMark && trendColor ? (_jsx(View, { accessible: false, children: renderTrendMark({ name: trendMark, color: trendColor, size: glyph.sm }) })) : (_jsx(Text, { accessible: false, style: { color: trendColor }, variant: "caption", children: resolved.trend.direction === "up" ? "↑" : resolved.trend.direction === "down" ? "↓" : "—" })), _jsx(Text, { accessible: false, style: [
|
|
676
|
+
{
|
|
677
|
+
color: trendColor,
|
|
678
|
+
flexShrink: 1,
|
|
679
|
+
fontWeight: statisticRecipe.trend.fontWeight,
|
|
680
|
+
},
|
|
681
|
+
trendStyle,
|
|
682
|
+
], variant: statisticRecipe.trend.textVariant, children: resolved.trend.label })] })) : null, resolved.hint ? (_jsx(Text, { accessible: false, style: [
|
|
683
|
+
{ color: resolveColorReference(statisticRecipe.hint.color, theme.palette) },
|
|
684
|
+
hintStyle,
|
|
685
|
+
], variant: statisticRecipe.hint.textVariant, children: resolved.hint })) : null] }));
|
|
686
|
+
}
|
|
687
|
+
export function StatisticGroup({ label, descriptor, availableWidth, density, presentation, composeAccessibilityLabel, renderTrendMark, style, itemStyle, onLayout, ...props }) {
|
|
688
|
+
validateStatisticGroup(descriptor);
|
|
689
|
+
const { width: windowWidth } = useWindowDimensions();
|
|
690
|
+
const { environment } = useHjmNativeTheme();
|
|
691
|
+
const [measuredWidth, setMeasuredWidth] = useState(null);
|
|
692
|
+
const innerWidth = availableWidth ?? measuredWidth ?? windowWidth;
|
|
693
|
+
if (!Number.isFinite(innerWidth) || innerWidth <= 0) {
|
|
694
|
+
throw new RangeError("StatisticGroup availableWidth must be positive");
|
|
695
|
+
}
|
|
696
|
+
const requested = descriptor.columns ?? statisticRecipe.defaults.columns;
|
|
697
|
+
const minItemWidth = statisticRecipe.group.minItemWidth * Math.max(1, environment.textScale);
|
|
698
|
+
let columns = requested;
|
|
699
|
+
while (columns > 1 &&
|
|
700
|
+
(innerWidth - statisticRecipe.group.gap * (columns - 1)) / columns < minItemWidth) {
|
|
701
|
+
columns -= 1;
|
|
702
|
+
}
|
|
703
|
+
const itemWidth = (innerWidth - statisticRecipe.group.gap * (columns - 1)) / columns;
|
|
704
|
+
const remainder = descriptor.items.length % columns;
|
|
705
|
+
const finalRowCount = remainder === 0 ? columns : remainder;
|
|
706
|
+
const finalRowStart = descriptor.items.length - finalRowCount;
|
|
707
|
+
const finalRowItemWidth = finalRowCount === columns
|
|
708
|
+
? itemWidth
|
|
709
|
+
: (innerWidth - statisticRecipe.group.gap * (finalRowCount - 1)) / finalRowCount;
|
|
710
|
+
const handleLayout = (event) => {
|
|
711
|
+
onLayout?.(event);
|
|
712
|
+
if (availableWidth !== undefined)
|
|
713
|
+
return;
|
|
714
|
+
const nextWidth = event.nativeEvent.layout.width;
|
|
715
|
+
if (Number.isFinite(nextWidth) && nextWidth > 0) {
|
|
716
|
+
setMeasuredWidth((current) => current === nextWidth ? current : nextWidth);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
return (_jsx(View, { ...props, accessibilityLabel: label, accessibilityRole: "list", onLayout: handleLayout, style: [
|
|
720
|
+
{
|
|
721
|
+
direction: environment.direction,
|
|
722
|
+
flexDirection: "row",
|
|
723
|
+
flexWrap: "wrap",
|
|
724
|
+
gap: statisticRecipe.group.gap,
|
|
725
|
+
},
|
|
726
|
+
style,
|
|
727
|
+
], children: descriptor.items.map((item, index) => (_jsx(Statistic, { contextLabel: label, descriptor: item, ...(composeAccessibilityLabel === undefined ? {} : { composeAccessibilityLabel }), ...(density === undefined ? {} : { density }), ...(presentation === undefined ? {} : { presentation }), ...(renderTrendMark === undefined ? {} : { renderTrendMark }), style: [
|
|
728
|
+
{ width: index >= finalRowStart ? finalRowItemWidth : itemWidth },
|
|
729
|
+
itemStyle,
|
|
730
|
+
] }, item.id))) }));
|
|
731
|
+
}
|
|
732
|
+
/** Ordered record of completed events; unlike Steps it has no current cursor. */
|
|
733
|
+
export function Timeline({ items, composeAccessibleName, style, ...props }) {
|
|
734
|
+
const theme = useHjmNativeTheme();
|
|
735
|
+
const resolved = resolveTimelineDescriptor({ items }, { composeAccessibleName });
|
|
736
|
+
return (_jsx(View, { ...props, style: [{ gap: timelineRecipe.gap }, style], children: resolved.map((item, index) => {
|
|
737
|
+
const tone = timelineRecipe.dot.tones[item.tone];
|
|
738
|
+
const accessibilityLabel = [
|
|
739
|
+
item.accessibleName,
|
|
740
|
+
item.timestamp,
|
|
741
|
+
item.description,
|
|
742
|
+
]
|
|
743
|
+
.filter(Boolean)
|
|
744
|
+
.join(", ");
|
|
745
|
+
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: {
|
|
746
|
+
backgroundColor: resolveColorReference(tone.fill, theme.palette),
|
|
747
|
+
borderColor: tone.border
|
|
748
|
+
? resolveColorReference(tone.border, theme.palette)
|
|
749
|
+
: "transparent",
|
|
750
|
+
borderRadius: radius.full,
|
|
751
|
+
borderWidth: tone.border
|
|
752
|
+
? timelineRecipe.dot.borderWidth
|
|
753
|
+
: 0,
|
|
754
|
+
height: timelineRecipe.dot.diameter,
|
|
755
|
+
width: timelineRecipe.dot.diameter,
|
|
756
|
+
} }), index < resolved.length - 1 ? (_jsx(View, { style: {
|
|
757
|
+
backgroundColor: resolveColorReference(timelineRecipe.connector.tone, theme.palette),
|
|
758
|
+
flex: 1,
|
|
759
|
+
width: timelineRecipe.connector.width,
|
|
760
|
+
} })) : null] }), _jsxs(View, { importantForAccessibility: "no-hide-descendants", style: { flex: 1, gap: spacing.xxs, minWidth: 0, paddingBottom: spacing.xxs }, children: [_jsxs(View, { style: {
|
|
761
|
+
alignItems: "baseline",
|
|
762
|
+
flexDirection: "row",
|
|
763
|
+
flexWrap: "wrap",
|
|
764
|
+
gap: spacing.xs,
|
|
765
|
+
justifyContent: "space-between",
|
|
766
|
+
}, children: [_jsx(Text, { emphasis: "medium", style: {
|
|
767
|
+
color: resolveColorReference(timelineRecipe.label.color, theme.palette),
|
|
768
|
+
}, variant: timelineRecipe.label.textVariant, children: item.label }), item.timestamp ? (_jsx(Text, { style: {
|
|
769
|
+
color: resolveColorReference(timelineRecipe.timestamp.color, theme.palette),
|
|
770
|
+
}, variant: timelineRecipe.timestamp.textVariant, children: item.timestamp })) : null] }), item.description ? (_jsx(Text, { style: {
|
|
771
|
+
color: resolveColorReference(timelineRecipe.description.color, theme.palette),
|
|
772
|
+
}, variant: timelineRecipe.description.textVariant, children: item.description })) : null] })] }, item.id));
|
|
773
|
+
}) }));
|
|
774
|
+
}
|
|
775
|
+
//# sourceMappingURL=data-display.js.map
|