@lmb-it/kitsconcerto-native 0.1.296 → 0.1.298

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.
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
4
5
  var index = require('../../primitives/Box/index.cjs');
5
- var index_native$2 = require('../../primitives/Text/index.cjs');
6
- var index_native$1 = require('../../primitives/Image/index.cjs');
6
+ var index_native$1 = require('../../primitives/Text/index.cjs');
7
+ var index_native$2 = require('../../primitives/Image/index.cjs');
7
8
  var index_native = require('../../primitives/Icon/index.cjs');
8
- require('react');
9
9
  require('../../contexts/DialogContext.cjs');
10
10
  require('../../contexts/PermissionsContext.cjs');
11
11
  require('../../contexts/BreadcrumbContext.cjs');
@@ -38,6 +38,9 @@ function deriveInitials(name, initials) {
38
38
  }
39
39
  function Avatar(props) {
40
40
  const { mergedProps } = useComponentDefaults.default("Avatar", props);
41
+ return /* @__PURE__ */ jsxRuntime.jsx(AvatarSurface, { ...mergedProps }, JSON.stringify([mergedProps.src ?? null, !!mergedProps.masked]));
42
+ }
43
+ function AvatarSurface(mergedProps) {
41
44
  const {
42
45
  src,
43
46
  name,
@@ -63,11 +66,13 @@ function Avatar(props) {
63
66
  const dot = Math.max(8, Math.round(px * 0.28));
64
67
  const ringW = px >= 56 ? 3 : 2;
65
68
  const resolvedInitials = deriveInitials(name, initials);
66
- const hasImage = !!src;
67
- const showInitials = !hasImage && resolvedInitials.length > 0;
68
- const showIcon = !hasImage && !showInitials && icon != null;
69
+ const [imageStatus, setImageStatus] = React.useState("pending");
70
+ const hasImage = !!src && imageStatus !== "failed";
71
+ const imageDelivered = hasImage && imageStatus === "loaded";
72
+ const showInitials = !imageDelivered && resolvedInitials.length > 0;
73
+ const showIcon = !imageDelivered && !showInitials && icon != null;
69
74
  const monogramBg = color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? "brand");
70
- const surfaceBg = masked ? maskFillColor : hasImage ? "gray.100" : monogramBg;
75
+ const surfaceBg = masked ? maskFillColor : imageDelivered || src && !resolvedInitials && icon == null ? "gray.100" : monogramBg;
71
76
  const initialsColor = "#FFFFFF";
72
77
  const statusColor = STATUS_TOKENS[status] ?? "gray.400";
73
78
  const ariaLabel = alt ?? name ?? "avatar";
@@ -91,71 +96,89 @@ function Avatar(props) {
91
96
  width: "100%",
92
97
  height: "100%",
93
98
  borderRadius: radius,
94
- overflow: hasImage && !masked ? "hidden" : "visible",
99
+ overflow: "visible",
95
100
  alignItems: "center",
96
101
  justifyContent: "center",
97
102
  bgColor: surfaceBg,
98
- children: masked ? /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : hasImage ? (
99
- /*
100
- * width/height via style: the Image primitive types dimensions
101
- * differently per platform (web string vs native number).
102
- *
103
- * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with
104
- * a `src` warned, on every render. gluestack's own image creator
105
- * does `if (typeof alt !== 'string') console.warn('Please pass
106
- * alt prop to Image component')`, so the check is on the TYPE,
107
- * not on emptiness: a missing prop warns and an empty string
108
- * does not. Measured in AlphaMatch on 5 October 2026 — 21
109
- * warnings in a single sign-in from `AMTabBar`'s Me slot alone,
110
- * which is one Avatar.
111
- *
112
- * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above
113
- * already carries `role="img"` and `aria-label={ariaLabel}`, so
114
- * this image is PRESENTATION inside an element that is already
115
- * named; repeating the label here would announce the person's
116
- * name twice. Empty is the standing answer for a decorative
117
- * image, and it is this library's own: `PhoneInput` passes
118
- * `alt={countryISO ?? ""}` for exactly this reason.
119
- *
120
- * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=""` ALONE WAS NOT
121
- * ENOUGH — found by Codex, 5 October 2026. gluestack does not
122
- * only read `alt`; it also forces
123
- * `role={props.role || 'img'}`, so an empty alt leaves a
124
- * SECOND, NAMELESS image role sitting under the labelled Box.
125
- * On the web an `<img alt="">` is decorative and harmless; on
126
- * React Native that role becomes `accessibilityRole="image"`
127
- * and a screen reader can land on an unnamed image. Hiding the
128
- * node is what makes it presentational on both platforms —
129
- * RN maps `aria-hidden` to `accessibilityElementsHidden` on
130
- * iOS and `importantForAccessibility="no-hide-descendants"` on
131
- * Android. `alt=""` stays because it is what silences the
132
- * warning: gluestack checks `typeof alt !== 'string'`.
133
- *
134
- * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is
135
- * NOT fixed here — it was outside what this change was opened
136
- * for. Recorded in the session report rather than swept in.
137
- */
138
- /* @__PURE__ */ jsxRuntime.jsx(
103
+ children: masked ? /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
104
+ showInitials ? /* @__PURE__ */ jsxRuntime.jsx(
139
105
  index_native$1.default,
140
106
  {
141
- src,
142
- alt: "",
143
- "aria-hidden": true,
144
- style: { width: px, height: px },
145
- objectFit: "cover"
107
+ color: initialsColor,
108
+ fontSize,
109
+ fontWeight: "700",
110
+ lineHeight: Math.round(fontSize * 1.2),
111
+ textAlign: "center",
112
+ children: resolvedInitials
146
113
  }
147
- )
148
- ) : showInitials ? /* @__PURE__ */ jsxRuntime.jsx(
149
- index_native$2.default,
150
- {
151
- color: initialsColor,
152
- fontSize,
153
- fontWeight: "700",
154
- lineHeight: Math.round(fontSize * 1.2),
155
- textAlign: "center",
156
- children: resolvedInitials
157
- }
158
- ) : showIcon ? /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: icon, size: iconSize, color: "gray.500" }) : null
114
+ ) : showIcon ? /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: icon, size: iconSize, color: "gray.500" }) : null,
115
+ hasImage ? (
116
+ /*
117
+ * width/height via style: the Image primitive types dimensions
118
+ * differently per platform (web string vs native number).
119
+ *
120
+ * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with
121
+ * a `src` warned, on every render. gluestack's own image creator
122
+ * does `if (typeof alt !== 'string') console.warn('Please pass
123
+ * alt prop to Image component')`, so the check is on the TYPE,
124
+ * not on emptiness: a missing prop warns and an empty string
125
+ * does not. Measured in AlphaMatch on 5 October 2026 — 21
126
+ * warnings in a single sign-in from `AMTabBar`'s Me slot alone,
127
+ * which is one Avatar.
128
+ *
129
+ * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above
130
+ * already carries `role="img"` and `aria-label={ariaLabel}`, so
131
+ * this image is PRESENTATION inside an element that is already
132
+ * named; repeating the label here would announce the person's
133
+ * name twice. Empty is the standing answer for a decorative
134
+ * image, and it is this library's own: `PhoneInput` passes
135
+ * `alt={countryISO ?? ""}` for exactly this reason.
136
+ *
137
+ * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=""` ALONE WAS NOT
138
+ * ENOUGH — found by Codex, 5 October 2026. gluestack does not
139
+ * only read `alt`; it also forces
140
+ * `role={props.role || 'img'}`, so an empty alt leaves a
141
+ * SECOND, NAMELESS image role sitting under the labelled Box.
142
+ * On the web an `<img alt="">` is decorative and harmless; on
143
+ * React Native that role becomes `accessibilityRole="image"`
144
+ * and a screen reader can land on an unnamed image. Hiding the
145
+ * node is what makes it presentational on both platforms —
146
+ * RN maps `aria-hidden` to `accessibilityElementsHidden` on
147
+ * iOS and `importantForAccessibility="no-hide-descendants"` on
148
+ * Android. `alt=""` stays because it is what silences the
149
+ * warning: gluestack checks `typeof alt !== 'string'`.
150
+ *
151
+ * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is
152
+ * NOT fixed here — it was outside what this change was opened
153
+ * for. Recorded in the session report rather than swept in.
154
+ */
155
+ /* @__PURE__ */ jsxRuntime.jsx(
156
+ index.default,
157
+ {
158
+ position: "absolute",
159
+ top: 0,
160
+ left: 0,
161
+ width: px,
162
+ height: px,
163
+ borderRadius: radius,
164
+ overflow: "hidden",
165
+ opacity: imageDelivered ? 1 : 0,
166
+ children: /* @__PURE__ */ jsxRuntime.jsx(
167
+ index_native$2.default,
168
+ {
169
+ src,
170
+ alt: "",
171
+ "aria-hidden": true,
172
+ style: { width: px, height: px },
173
+ objectFit: "cover",
174
+ onLoad: () => setImageStatus((current) => current === "failed" ? current : "loaded"),
175
+ onError: () => setImageStatus("failed")
176
+ }
177
+ )
178
+ }
179
+ )
180
+ ) : null
181
+ ] })
159
182
  }
160
183
  ),
161
184
  ring && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IAvatarProps, IAvatarStackProps, AvatarSize} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/* ─── Size resolution ─────────────────────────────────────────── */\nconst SIZE_PX: Record<string, number> = {xs: 24, sm: 32, md: 40, lg: 56, xl: 72};\n\nfunction resolveSize(size: AvatarSize | undefined): number {\n if (typeof size === \"number\") return size;\n return SIZE_PX[size ?? \"md\"] ?? 40;\n}\n\n/* ─── Status dot → semantic token ─────────────────────────────── */\nconst STATUS_TOKENS: Record<string, string> = {\n online: \"success\",\n offline: \"gray.400\",\n away: \"warning\",\n busy: \"danger\",\n};\n\n/* ─── Deterministic monogram palette (all confirmed theme tokens) ─ */\nconst MONOGRAM_PALETTE = [\"brand\", \"primary.800\", \"success\", \"danger\", \"ink.700\"];\n\nfunction hashIndex(input: string, mod: number): number {\n let h = 0;\n for (let i = 0; i < input.length; i++) h = (h * 31 + input.charCodeAt(i)) >>> 0;\n return mod > 0 ? h % mod : 0;\n}\n\nfunction deriveInitials(name?: string, initials?: string): string {\n if (initials) return initials.slice(0, 3).toUpperCase();\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"\";\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]!.charAt(0) + parts[parts.length - 1]!.charAt(0)).toUpperCase();\n}\n\n/**\n * Avatar — image / monogram / icon identity tile.\n *\n * Pure presentational and cross-platform: composed from the `Box` + `Text` +\n * `Image` + `Icon` primitives, so it renders identically on web and native\n * with no platform split. Tappability is the caller's concern (wrap in a\n * Pressable / Button).\n *\n * <Avatar src=\"https://…/me.jpg\" size=\"lg\" />\n * <Avatar name=\"Fouad K\" status=\"online\" />\n * <Avatar initials=\"C\" masked /> // identity-masked (Flex)\n * <Avatar name=\"Acme\" shape=\"rounded\" badge={<Icon name=\"briefcase\" />} />\n */\nexport function Avatar(props: IAvatarProps) {\n const {mergedProps} = useComponentDefaults<IAvatarProps>(\"Avatar\", props);\n const {\n src,\n name,\n initials,\n icon,\n size = \"md\",\n shape = \"circle\",\n color,\n status = \"none\",\n ring = false,\n ringColor = \"brand\",\n masked = false,\n maskFillColor = \"indigo.600\",\n badge,\n alt,\n testID,\n ...rest\n } = mergedProps;\n\n const px = resolveSize(size);\n const radius = shape === \"circle\" ? px / 2 : Math.round(px * 0.28);\n const fontSize = Math.max(10, Math.round(px * 0.4));\n const iconSize = Math.round(px * 0.5);\n const dot = Math.max(8, Math.round(px * 0.28));\n const ringW = px >= 56 ? 3 : 2;\n\n const resolvedInitials = deriveInitials(name, initials);\n const hasImage = !!src;\n const showInitials = !hasImage && resolvedInitials.length > 0;\n const showIcon = !hasImage && !showInitials && icon != null;\n\n const monogramBg =\n color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? \"brand\");\n const surfaceBg = masked ? maskFillColor : hasImage ? \"gray.100\" : monogramBg;\n const initialsColor = \"#FFFFFF\";\n\n const statusColor = STATUS_TOKENS[status] ?? \"gray.400\";\n const ariaLabel = alt ?? name ?? \"avatar\";\n\n const a11yProps = {\n role: \"img\",\n \"aria-label\": ariaLabel,\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box\n position=\"relative\"\n width={px}\n height={px}\n {...rest}\n {...(a11yProps as any)}\n >\n {/* Surface (clips the image / centres the monogram).\n overflow:hidden only when an image is present — clipping the\n monogram <Text> cuts off the glyph tops on native. */}\n <Box\n width=\"100%\"\n height=\"100%\"\n borderRadius={radius}\n overflow={hasImage && !masked ? \"hidden\" : \"visible\"}\n alignItems=\"center\"\n justifyContent=\"center\"\n bgColor={surfaceBg}\n >\n {masked ? (\n <Icon name=\"user\" size={iconSize} color=\"#FFFFFF\" />\n ) : hasImage ? (\n /*\n * width/height via style: the Image primitive types dimensions\n * differently per platform (web string vs native number).\n *\n * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with\n * a `src` warned, on every render. gluestack's own image creator\n * does `if (typeof alt !== 'string') console.warn('Please pass\n * alt prop to Image component')`, so the check is on the TYPE,\n * not on emptiness: a missing prop warns and an empty string\n * does not. Measured in AlphaMatch on 5 October 2026 — 21\n * warnings in a single sign-in from `AMTabBar`'s Me slot alone,\n * which is one Avatar.\n *\n * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above\n * already carries `role=\"img\"` and `aria-label={ariaLabel}`, so\n * this image is PRESENTATION inside an element that is already\n * named; repeating the label here would announce the person's\n * name twice. Empty is the standing answer for a decorative\n * image, and it is this library's own: `PhoneInput` passes\n * `alt={countryISO ?? \"\"}` for exactly this reason.\n *\n * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=\"\"` ALONE WAS NOT\n * ENOUGH — found by Codex, 5 October 2026. gluestack does not\n * only read `alt`; it also forces\n * `role={props.role || 'img'}`, so an empty alt leaves a\n * SECOND, NAMELESS image role sitting under the labelled Box.\n * On the web an `<img alt=\"\">` is decorative and harmless; on\n * React Native that role becomes `accessibilityRole=\"image\"`\n * and a screen reader can land on an unnamed image. Hiding the\n * node is what makes it presentational on both platforms —\n * RN maps `aria-hidden` to `accessibilityElementsHidden` on\n * iOS and `importantForAccessibility=\"no-hide-descendants\"` on\n * Android. `alt=\"\"` stays because it is what silences the\n * warning: gluestack checks `typeof alt !== 'string'`.\n *\n * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is\n * NOT fixed here — it was outside what this change was opened\n * for. Recorded in the session report rather than swept in.\n */\n <Image\n src={src}\n alt=\"\"\n aria-hidden\n style={{width: px, height: px} as any}\n objectFit=\"cover\"\n />\n ) : showInitials ? (\n <Text\n color={initialsColor}\n fontSize={fontSize}\n fontWeight=\"700\"\n lineHeight={Math.round(fontSize * 1.2)}\n textAlign=\"center\"\n >\n {resolvedInitials}\n </Text>\n ) : showIcon ? (\n <Icon name={icon as never} size={iconSize} color=\"gray.500\" />\n ) : null}\n </Box>\n\n {/* Ring overlay (solid active ring) */}\n {ring && (\n <Box\n position=\"absolute\"\n top={0}\n right={0}\n bottom={0}\n left={0}\n borderRadius={radius}\n borderWidth={ringW}\n borderStyle=\"solid\"\n borderColor={ringColor}\n />\n )}\n\n {/* Badge OR presence dot (badge wins the corner) */}\n {badge != null ? (\n <Box position=\"absolute\" bottom={-2} right={-2} zIndex={2}>\n {badge}\n </Box>\n ) : status !== \"none\" ? (\n <Box\n position=\"absolute\"\n bottom={0}\n right={0}\n zIndex={2}\n width={dot}\n height={dot}\n borderRadius={dot}\n bgColor={statusColor}\n borderWidth={2}\n borderColor=\"surface-card\"\n />\n ) : null}\n </Box>\n );\n}\n\n/**\n * AvatarStack — overlapping row of avatars with a \"+N\" overflow tile.\n * Each avatar gets a surface-coloured cut-out ring so it reads cleanly\n * against the one behind it.\n *\n * <AvatarStack items={members} max={4} activeIndex={0} />\n */\nexport function AvatarStack(props: IAvatarStackProps) {\n const {mergedProps} = useComponentDefaults<IAvatarStackProps>(\"AvatarStack\", props);\n const {items, max = 4, size = \"md\", overlap = 0.35, activeIndex, ringColor = \"brand\", testID, ...rest} = mergedProps;\n\n const px = resolveSize(size);\n const clampedOverlap = Math.min(Math.max(overlap, 0), 0.9);\n const overlapPx = Math.round(px * clampedOverlap);\n const strokeW = px >= 56 ? 3 : 2;\n\n const visible = items.slice(0, max);\n const overflow = items.length - visible.length;\n\n const cell = (key: string, node: React.ReactNode, i: number) => (\n <Box\n key={key}\n marginLeft={i === 0 ? 0 : -overlapPx}\n zIndex={items.length - i}\n borderRadius={9999}\n borderWidth={strokeW}\n borderColor=\"surface-card\"\n bgColor=\"surface-card\"\n >\n {node}\n </Box>\n );\n\n const a11yProps = {\n role: \"group\",\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box flexDirection=\"row\" alignItems=\"center\" {...rest} {...(a11yProps as any)}>\n {visible.map((item, i) =>\n cell(\n `avatar-${i}`,\n <Avatar\n {...item}\n size={size}\n ring={i === activeIndex || item.ring}\n ringColor={item.ringColor ?? ringColor}\n />,\n i,\n ),\n )}\n {overflow > 0 && cell(\"avatar-overflow\", <Avatar initials={`+${overflow}`} size={size} color=\"ink.400\" />, visible.length)}\n </Box>\n );\n}\n\nexport default Avatar;\n"],"names":["useComponentDefaults","jsxs","Box","jsx","Icon","Image","Text"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IAvatarProps, IAvatarStackProps, AvatarSize} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/* ─── Size resolution ─────────────────────────────────────────── */\nconst SIZE_PX: Record<string, number> = {xs: 24, sm: 32, md: 40, lg: 56, xl: 72};\n\nfunction resolveSize(size: AvatarSize | undefined): number {\n if (typeof size === \"number\") return size;\n return SIZE_PX[size ?? \"md\"] ?? 40;\n}\n\n/* ─── Status dot → semantic token ─────────────────────────────── */\nconst STATUS_TOKENS: Record<string, string> = {\n online: \"success\",\n offline: \"gray.400\",\n away: \"warning\",\n busy: \"danger\",\n};\n\n/* ─── Deterministic monogram palette (all confirmed theme tokens) ─ */\nconst MONOGRAM_PALETTE = [\"brand\", \"primary.800\", \"success\", \"danger\", \"ink.700\"];\n\nfunction hashIndex(input: string, mod: number): number {\n let h = 0;\n for (let i = 0; i < input.length; i++) h = (h * 31 + input.charCodeAt(i)) >>> 0;\n return mod > 0 ? h % mod : 0;\n}\n\nfunction deriveInitials(name?: string, initials?: string): string {\n if (initials) return initials.slice(0, 3).toUpperCase();\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"\";\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]!.charAt(0) + parts[parts.length - 1]!.charAt(0)).toUpperCase();\n}\n\n/**\n * Avatar — image / monogram / icon identity tile.\n *\n * Pure presentational and cross-platform: composed from the `Box` + `Text` +\n * `Image` + `Icon` primitives, so it renders identically on web and native\n * with no platform split. Tappability is the caller's concern (wrap in a\n * Pressable / Button).\n *\n * <Avatar src=\"https://…/me.jpg\" size=\"lg\" />\n * <Avatar name=\"Fouad K\" status=\"online\" />\n * <Avatar initials=\"C\" masked /> // identity-masked (Flex)\n * <Avatar name=\"Acme\" shape=\"rounded\" badge={<Icon name=\"briefcase\" />} />\n */\nexport function Avatar(props: IAvatarProps) {\n const {mergedProps} = useComponentDefaults<IAvatarProps>(\"Avatar\", props);\n // Retire image events with their source. A late load/error from an old\n // photo must not replace the next identity's pending fallback.\n return <AvatarSurface key={JSON.stringify([mergedProps.src ?? null, !!mergedProps.masked])} {...mergedProps} />;\n}\n\nfunction AvatarSurface(mergedProps: IAvatarProps) {\n const {\n src,\n name,\n initials,\n icon,\n size = \"md\",\n shape = \"circle\",\n color,\n status = \"none\",\n ring = false,\n ringColor = \"brand\",\n masked = false,\n maskFillColor = \"indigo.600\",\n badge,\n alt,\n testID,\n ...rest\n } = mergedProps;\n\n const px = resolveSize(size);\n const radius = shape === \"circle\" ? px / 2 : Math.round(px * 0.28);\n const fontSize = Math.max(10, Math.round(px * 0.4));\n const iconSize = Math.round(px * 0.5);\n const dot = Math.max(8, Math.round(px * 0.28));\n const ringW = px >= 56 ? 3 : 2;\n\n const resolvedInitials = deriveInitials(name, initials);\n const [imageStatus, setImageStatus] = React.useState<\"pending\" | \"loaded\" | \"failed\">(\"pending\");\n const hasImage = !!src && imageStatus !== \"failed\";\n const imageDelivered = hasImage && imageStatus === \"loaded\";\n const showInitials = !imageDelivered && resolvedInitials.length > 0;\n const showIcon = !imageDelivered && !showInitials && icon != null;\n\n const monogramBg =\n color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? \"brand\");\n const surfaceBg = masked ? maskFillColor : imageDelivered || (src && !resolvedInitials && icon == null) ? \"gray.100\" : monogramBg;\n const initialsColor = \"#FFFFFF\";\n\n const statusColor = STATUS_TOKENS[status] ?? \"gray.400\";\n const ariaLabel = alt ?? name ?? \"avatar\";\n\n const a11yProps = {\n role: \"img\",\n \"aria-label\": ariaLabel,\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box\n position=\"relative\"\n width={px}\n height={px}\n {...rest}\n {...(a11yProps as any)}\n >\n {/* Clip only the photo frame: pending/failed monogram glyphs must\n remain unclipped on native. */}\n <Box\n width=\"100%\"\n height=\"100%\"\n borderRadius={radius}\n overflow=\"visible\"\n alignItems=\"center\"\n justifyContent=\"center\"\n bgColor={surfaceBg}\n >\n {masked ? (\n <Icon name=\"user\" size={iconSize} color=\"#FFFFFF\" />\n ) : (\n <>\n {showInitials ? (\n <Text\n color={initialsColor}\n fontSize={fontSize}\n fontWeight=\"700\"\n lineHeight={Math.round(fontSize * 1.2)}\n textAlign=\"center\"\n >\n {resolvedInitials}\n </Text>\n ) : showIcon ? (\n <Icon name={icon as never} size={iconSize} color=\"gray.500\" />\n ) : null}\n {hasImage ? (\n /*\n * width/height via style: the Image primitive types dimensions\n * differently per platform (web string vs native number).\n *\n * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with\n * a `src` warned, on every render. gluestack's own image creator\n * does `if (typeof alt !== 'string') console.warn('Please pass\n * alt prop to Image component')`, so the check is on the TYPE,\n * not on emptiness: a missing prop warns and an empty string\n * does not. Measured in AlphaMatch on 5 October 2026 — 21\n * warnings in a single sign-in from `AMTabBar`'s Me slot alone,\n * which is one Avatar.\n *\n * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above\n * already carries `role=\"img\"` and `aria-label={ariaLabel}`, so\n * this image is PRESENTATION inside an element that is already\n * named; repeating the label here would announce the person's\n * name twice. Empty is the standing answer for a decorative\n * image, and it is this library's own: `PhoneInput` passes\n * `alt={countryISO ?? \"\"}` for exactly this reason.\n *\n * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=\"\"` ALONE WAS NOT\n * ENOUGH — found by Codex, 5 October 2026. gluestack does not\n * only read `alt`; it also forces\n * `role={props.role || 'img'}`, so an empty alt leaves a\n * SECOND, NAMELESS image role sitting under the labelled Box.\n * On the web an `<img alt=\"\">` is decorative and harmless; on\n * React Native that role becomes `accessibilityRole=\"image\"`\n * and a screen reader can land on an unnamed image. Hiding the\n * node is what makes it presentational on both platforms —\n * RN maps `aria-hidden` to `accessibilityElementsHidden` on\n * iOS and `importantForAccessibility=\"no-hide-descendants\"` on\n * Android. `alt=\"\"` stays because it is what silences the\n * warning: gluestack checks `typeof alt !== 'string'`.\n *\n * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is\n * NOT fixed here — it was outside what this change was opened\n * for. Recorded in the session report rather than swept in.\n */\n <Box position=\"absolute\" top={0} left={0} width={px} height={px}\n borderRadius={radius} overflow=\"hidden\" opacity={imageDelivered ? 1 : 0}>\n <Image\n src={src}\n alt=\"\"\n aria-hidden\n style={{width: px, height: px} as any}\n objectFit=\"cover\"\n onLoad={() => setImageStatus(current => current === \"failed\" ? current : \"loaded\")}\n onError={() => setImageStatus(\"failed\")}\n />\n </Box>\n ) : null}\n </>\n )}\n </Box>\n\n {/* Ring overlay (solid active ring) */}\n {ring && (\n <Box\n position=\"absolute\"\n top={0}\n right={0}\n bottom={0}\n left={0}\n borderRadius={radius}\n borderWidth={ringW}\n borderStyle=\"solid\"\n borderColor={ringColor}\n />\n )}\n\n {/* Badge OR presence dot (badge wins the corner) */}\n {badge != null ? (\n <Box position=\"absolute\" bottom={-2} right={-2} zIndex={2}>\n {badge}\n </Box>\n ) : status !== \"none\" ? (\n <Box\n position=\"absolute\"\n bottom={0}\n right={0}\n zIndex={2}\n width={dot}\n height={dot}\n borderRadius={dot}\n bgColor={statusColor}\n borderWidth={2}\n borderColor=\"surface-card\"\n />\n ) : null}\n </Box>\n );\n}\n\n/**\n * AvatarStack — overlapping row of avatars with a \"+N\" overflow tile.\n * Each avatar gets a surface-coloured cut-out ring so it reads cleanly\n * against the one behind it.\n *\n * <AvatarStack items={members} max={4} activeIndex={0} />\n */\nexport function AvatarStack(props: IAvatarStackProps) {\n const {mergedProps} = useComponentDefaults<IAvatarStackProps>(\"AvatarStack\", props);\n const {items, max = 4, size = \"md\", overlap = 0.35, activeIndex, ringColor = \"brand\", testID, ...rest} = mergedProps;\n\n const px = resolveSize(size);\n const clampedOverlap = Math.min(Math.max(overlap, 0), 0.9);\n const overlapPx = Math.round(px * clampedOverlap);\n const strokeW = px >= 56 ? 3 : 2;\n\n const visible = items.slice(0, max);\n const overflow = items.length - visible.length;\n\n const cell = (key: string, node: React.ReactNode, i: number) => (\n <Box\n key={key}\n marginLeft={i === 0 ? 0 : -overlapPx}\n zIndex={items.length - i}\n borderRadius={9999}\n borderWidth={strokeW}\n borderColor=\"surface-card\"\n bgColor=\"surface-card\"\n >\n {node}\n </Box>\n );\n\n const a11yProps = {\n role: \"group\",\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box flexDirection=\"row\" alignItems=\"center\" {...rest} {...(a11yProps as any)}>\n {visible.map((item, i) =>\n cell(\n `avatar-${i}`,\n <Avatar\n {...item}\n size={size}\n ring={i === activeIndex || item.ring}\n ringColor={item.ringColor ?? ringColor}\n />,\n i,\n ),\n )}\n {overflow > 0 && cell(\"avatar-overflow\", <Avatar initials={`+${overflow}`} size={size} color=\"ink.400\" />, visible.length)}\n </Box>\n );\n}\n\nexport default Avatar;\n"],"names":["useComponentDefaults","jsx","jsxs","Box","Icon","Fragment","Text","Image"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import React from 'react';
2
3
  import Box from '../../primitives/Box/index.js';
3
4
  import Text from '../../primitives/Text/index.js';
4
5
  import Image from '../../primitives/Image/index.js';
5
6
  import { Icon } from '../../primitives/Icon/index.js';
6
- import 'react';
7
7
  import '../../contexts/DialogContext.js';
8
8
  import '../../contexts/PermissionsContext.js';
9
9
  import '../../contexts/BreadcrumbContext.js';
@@ -36,6 +36,9 @@ function deriveInitials(name, initials) {
36
36
  }
37
37
  function Avatar(props) {
38
38
  const { mergedProps } = useComponentDefaults("Avatar", props);
39
+ return /* @__PURE__ */ jsx(AvatarSurface, { ...mergedProps }, JSON.stringify([mergedProps.src ?? null, !!mergedProps.masked]));
40
+ }
41
+ function AvatarSurface(mergedProps) {
39
42
  const {
40
43
  src,
41
44
  name,
@@ -61,11 +64,13 @@ function Avatar(props) {
61
64
  const dot = Math.max(8, Math.round(px * 0.28));
62
65
  const ringW = px >= 56 ? 3 : 2;
63
66
  const resolvedInitials = deriveInitials(name, initials);
64
- const hasImage = !!src;
65
- const showInitials = !hasImage && resolvedInitials.length > 0;
66
- const showIcon = !hasImage && !showInitials && icon != null;
67
+ const [imageStatus, setImageStatus] = React.useState("pending");
68
+ const hasImage = !!src && imageStatus !== "failed";
69
+ const imageDelivered = hasImage && imageStatus === "loaded";
70
+ const showInitials = !imageDelivered && resolvedInitials.length > 0;
71
+ const showIcon = !imageDelivered && !showInitials && icon != null;
67
72
  const monogramBg = color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? "brand");
68
- const surfaceBg = masked ? maskFillColor : hasImage ? "gray.100" : monogramBg;
73
+ const surfaceBg = masked ? maskFillColor : imageDelivered || src && !resolvedInitials && icon == null ? "gray.100" : monogramBg;
69
74
  const initialsColor = "#FFFFFF";
70
75
  const statusColor = STATUS_TOKENS[status] ?? "gray.400";
71
76
  const ariaLabel = alt ?? name ?? "avatar";
@@ -89,71 +94,89 @@ function Avatar(props) {
89
94
  width: "100%",
90
95
  height: "100%",
91
96
  borderRadius: radius,
92
- overflow: hasImage && !masked ? "hidden" : "visible",
97
+ overflow: "visible",
93
98
  alignItems: "center",
94
99
  justifyContent: "center",
95
100
  bgColor: surfaceBg,
96
- children: masked ? /* @__PURE__ */ jsx(Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : hasImage ? (
97
- /*
98
- * width/height via style: the Image primitive types dimensions
99
- * differently per platform (web string vs native number).
100
- *
101
- * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with
102
- * a `src` warned, on every render. gluestack's own image creator
103
- * does `if (typeof alt !== 'string') console.warn('Please pass
104
- * alt prop to Image component')`, so the check is on the TYPE,
105
- * not on emptiness: a missing prop warns and an empty string
106
- * does not. Measured in AlphaMatch on 5 October 2026 — 21
107
- * warnings in a single sign-in from `AMTabBar`'s Me slot alone,
108
- * which is one Avatar.
109
- *
110
- * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above
111
- * already carries `role="img"` and `aria-label={ariaLabel}`, so
112
- * this image is PRESENTATION inside an element that is already
113
- * named; repeating the label here would announce the person's
114
- * name twice. Empty is the standing answer for a decorative
115
- * image, and it is this library's own: `PhoneInput` passes
116
- * `alt={countryISO ?? ""}` for exactly this reason.
117
- *
118
- * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=""` ALONE WAS NOT
119
- * ENOUGH — found by Codex, 5 October 2026. gluestack does not
120
- * only read `alt`; it also forces
121
- * `role={props.role || 'img'}`, so an empty alt leaves a
122
- * SECOND, NAMELESS image role sitting under the labelled Box.
123
- * On the web an `<img alt="">` is decorative and harmless; on
124
- * React Native that role becomes `accessibilityRole="image"`
125
- * and a screen reader can land on an unnamed image. Hiding the
126
- * node is what makes it presentational on both platforms —
127
- * RN maps `aria-hidden` to `accessibilityElementsHidden` on
128
- * iOS and `importantForAccessibility="no-hide-descendants"` on
129
- * Android. `alt=""` stays because it is what silences the
130
- * warning: gluestack checks `typeof alt !== 'string'`.
131
- *
132
- * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is
133
- * NOT fixed here — it was outside what this change was opened
134
- * for. Recorded in the session report rather than swept in.
135
- */
136
- /* @__PURE__ */ jsx(
137
- Image,
101
+ children: masked ? /* @__PURE__ */ jsx(Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
102
+ showInitials ? /* @__PURE__ */ jsx(
103
+ Text,
138
104
  {
139
- src,
140
- alt: "",
141
- "aria-hidden": true,
142
- style: { width: px, height: px },
143
- objectFit: "cover"
105
+ color: initialsColor,
106
+ fontSize,
107
+ fontWeight: "700",
108
+ lineHeight: Math.round(fontSize * 1.2),
109
+ textAlign: "center",
110
+ children: resolvedInitials
144
111
  }
145
- )
146
- ) : showInitials ? /* @__PURE__ */ jsx(
147
- Text,
148
- {
149
- color: initialsColor,
150
- fontSize,
151
- fontWeight: "700",
152
- lineHeight: Math.round(fontSize * 1.2),
153
- textAlign: "center",
154
- children: resolvedInitials
155
- }
156
- ) : showIcon ? /* @__PURE__ */ jsx(Icon, { name: icon, size: iconSize, color: "gray.500" }) : null
112
+ ) : showIcon ? /* @__PURE__ */ jsx(Icon, { name: icon, size: iconSize, color: "gray.500" }) : null,
113
+ hasImage ? (
114
+ /*
115
+ * width/height via style: the Image primitive types dimensions
116
+ * differently per platform (web string vs native number).
117
+ *
118
+ * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with
119
+ * a `src` warned, on every render. gluestack's own image creator
120
+ * does `if (typeof alt !== 'string') console.warn('Please pass
121
+ * alt prop to Image component')`, so the check is on the TYPE,
122
+ * not on emptiness: a missing prop warns and an empty string
123
+ * does not. Measured in AlphaMatch on 5 October 2026 — 21
124
+ * warnings in a single sign-in from `AMTabBar`'s Me slot alone,
125
+ * which is one Avatar.
126
+ *
127
+ * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above
128
+ * already carries `role="img"` and `aria-label={ariaLabel}`, so
129
+ * this image is PRESENTATION inside an element that is already
130
+ * named; repeating the label here would announce the person's
131
+ * name twice. Empty is the standing answer for a decorative
132
+ * image, and it is this library's own: `PhoneInput` passes
133
+ * `alt={countryISO ?? ""}` for exactly this reason.
134
+ *
135
+ * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=""` ALONE WAS NOT
136
+ * ENOUGH — found by Codex, 5 October 2026. gluestack does not
137
+ * only read `alt`; it also forces
138
+ * `role={props.role || 'img'}`, so an empty alt leaves a
139
+ * SECOND, NAMELESS image role sitting under the labelled Box.
140
+ * On the web an `<img alt="">` is decorative and harmless; on
141
+ * React Native that role becomes `accessibilityRole="image"`
142
+ * and a screen reader can land on an unnamed image. Hiding the
143
+ * node is what makes it presentational on both platforms —
144
+ * RN maps `aria-hidden` to `accessibilityElementsHidden` on
145
+ * iOS and `importantForAccessibility="no-hide-descendants"` on
146
+ * Android. `alt=""` stays because it is what silences the
147
+ * warning: gluestack checks `typeof alt !== 'string'`.
148
+ *
149
+ * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is
150
+ * NOT fixed here — it was outside what this change was opened
151
+ * for. Recorded in the session report rather than swept in.
152
+ */
153
+ /* @__PURE__ */ jsx(
154
+ Box,
155
+ {
156
+ position: "absolute",
157
+ top: 0,
158
+ left: 0,
159
+ width: px,
160
+ height: px,
161
+ borderRadius: radius,
162
+ overflow: "hidden",
163
+ opacity: imageDelivered ? 1 : 0,
164
+ children: /* @__PURE__ */ jsx(
165
+ Image,
166
+ {
167
+ src,
168
+ alt: "",
169
+ "aria-hidden": true,
170
+ style: { width: px, height: px },
171
+ objectFit: "cover",
172
+ onLoad: () => setImageStatus((current) => current === "failed" ? current : "loaded"),
173
+ onError: () => setImageStatus("failed")
174
+ }
175
+ )
176
+ }
177
+ )
178
+ ) : null
179
+ ] })
157
180
  }
158
181
  ),
159
182
  ring && /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IAvatarProps, IAvatarStackProps, AvatarSize} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/* ─── Size resolution ─────────────────────────────────────────── */\nconst SIZE_PX: Record<string, number> = {xs: 24, sm: 32, md: 40, lg: 56, xl: 72};\n\nfunction resolveSize(size: AvatarSize | undefined): number {\n if (typeof size === \"number\") return size;\n return SIZE_PX[size ?? \"md\"] ?? 40;\n}\n\n/* ─── Status dot → semantic token ─────────────────────────────── */\nconst STATUS_TOKENS: Record<string, string> = {\n online: \"success\",\n offline: \"gray.400\",\n away: \"warning\",\n busy: \"danger\",\n};\n\n/* ─── Deterministic monogram palette (all confirmed theme tokens) ─ */\nconst MONOGRAM_PALETTE = [\"brand\", \"primary.800\", \"success\", \"danger\", \"ink.700\"];\n\nfunction hashIndex(input: string, mod: number): number {\n let h = 0;\n for (let i = 0; i < input.length; i++) h = (h * 31 + input.charCodeAt(i)) >>> 0;\n return mod > 0 ? h % mod : 0;\n}\n\nfunction deriveInitials(name?: string, initials?: string): string {\n if (initials) return initials.slice(0, 3).toUpperCase();\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"\";\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]!.charAt(0) + parts[parts.length - 1]!.charAt(0)).toUpperCase();\n}\n\n/**\n * Avatar — image / monogram / icon identity tile.\n *\n * Pure presentational and cross-platform: composed from the `Box` + `Text` +\n * `Image` + `Icon` primitives, so it renders identically on web and native\n * with no platform split. Tappability is the caller's concern (wrap in a\n * Pressable / Button).\n *\n * <Avatar src=\"https://…/me.jpg\" size=\"lg\" />\n * <Avatar name=\"Fouad K\" status=\"online\" />\n * <Avatar initials=\"C\" masked /> // identity-masked (Flex)\n * <Avatar name=\"Acme\" shape=\"rounded\" badge={<Icon name=\"briefcase\" />} />\n */\nexport function Avatar(props: IAvatarProps) {\n const {mergedProps} = useComponentDefaults<IAvatarProps>(\"Avatar\", props);\n const {\n src,\n name,\n initials,\n icon,\n size = \"md\",\n shape = \"circle\",\n color,\n status = \"none\",\n ring = false,\n ringColor = \"brand\",\n masked = false,\n maskFillColor = \"indigo.600\",\n badge,\n alt,\n testID,\n ...rest\n } = mergedProps;\n\n const px = resolveSize(size);\n const radius = shape === \"circle\" ? px / 2 : Math.round(px * 0.28);\n const fontSize = Math.max(10, Math.round(px * 0.4));\n const iconSize = Math.round(px * 0.5);\n const dot = Math.max(8, Math.round(px * 0.28));\n const ringW = px >= 56 ? 3 : 2;\n\n const resolvedInitials = deriveInitials(name, initials);\n const hasImage = !!src;\n const showInitials = !hasImage && resolvedInitials.length > 0;\n const showIcon = !hasImage && !showInitials && icon != null;\n\n const monogramBg =\n color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? \"brand\");\n const surfaceBg = masked ? maskFillColor : hasImage ? \"gray.100\" : monogramBg;\n const initialsColor = \"#FFFFFF\";\n\n const statusColor = STATUS_TOKENS[status] ?? \"gray.400\";\n const ariaLabel = alt ?? name ?? \"avatar\";\n\n const a11yProps = {\n role: \"img\",\n \"aria-label\": ariaLabel,\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box\n position=\"relative\"\n width={px}\n height={px}\n {...rest}\n {...(a11yProps as any)}\n >\n {/* Surface (clips the image / centres the monogram).\n overflow:hidden only when an image is present — clipping the\n monogram <Text> cuts off the glyph tops on native. */}\n <Box\n width=\"100%\"\n height=\"100%\"\n borderRadius={radius}\n overflow={hasImage && !masked ? \"hidden\" : \"visible\"}\n alignItems=\"center\"\n justifyContent=\"center\"\n bgColor={surfaceBg}\n >\n {masked ? (\n <Icon name=\"user\" size={iconSize} color=\"#FFFFFF\" />\n ) : hasImage ? (\n /*\n * width/height via style: the Image primitive types dimensions\n * differently per platform (web string vs native number).\n *\n * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with\n * a `src` warned, on every render. gluestack's own image creator\n * does `if (typeof alt !== 'string') console.warn('Please pass\n * alt prop to Image component')`, so the check is on the TYPE,\n * not on emptiness: a missing prop warns and an empty string\n * does not. Measured in AlphaMatch on 5 October 2026 — 21\n * warnings in a single sign-in from `AMTabBar`'s Me slot alone,\n * which is one Avatar.\n *\n * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above\n * already carries `role=\"img\"` and `aria-label={ariaLabel}`, so\n * this image is PRESENTATION inside an element that is already\n * named; repeating the label here would announce the person's\n * name twice. Empty is the standing answer for a decorative\n * image, and it is this library's own: `PhoneInput` passes\n * `alt={countryISO ?? \"\"}` for exactly this reason.\n *\n * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=\"\"` ALONE WAS NOT\n * ENOUGH — found by Codex, 5 October 2026. gluestack does not\n * only read `alt`; it also forces\n * `role={props.role || 'img'}`, so an empty alt leaves a\n * SECOND, NAMELESS image role sitting under the labelled Box.\n * On the web an `<img alt=\"\">` is decorative and harmless; on\n * React Native that role becomes `accessibilityRole=\"image\"`\n * and a screen reader can land on an unnamed image. Hiding the\n * node is what makes it presentational on both platforms —\n * RN maps `aria-hidden` to `accessibilityElementsHidden` on\n * iOS and `importantForAccessibility=\"no-hide-descendants\"` on\n * Android. `alt=\"\"` stays because it is what silences the\n * warning: gluestack checks `typeof alt !== 'string'`.\n *\n * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is\n * NOT fixed here — it was outside what this change was opened\n * for. Recorded in the session report rather than swept in.\n */\n <Image\n src={src}\n alt=\"\"\n aria-hidden\n style={{width: px, height: px} as any}\n objectFit=\"cover\"\n />\n ) : showInitials ? (\n <Text\n color={initialsColor}\n fontSize={fontSize}\n fontWeight=\"700\"\n lineHeight={Math.round(fontSize * 1.2)}\n textAlign=\"center\"\n >\n {resolvedInitials}\n </Text>\n ) : showIcon ? (\n <Icon name={icon as never} size={iconSize} color=\"gray.500\" />\n ) : null}\n </Box>\n\n {/* Ring overlay (solid active ring) */}\n {ring && (\n <Box\n position=\"absolute\"\n top={0}\n right={0}\n bottom={0}\n left={0}\n borderRadius={radius}\n borderWidth={ringW}\n borderStyle=\"solid\"\n borderColor={ringColor}\n />\n )}\n\n {/* Badge OR presence dot (badge wins the corner) */}\n {badge != null ? (\n <Box position=\"absolute\" bottom={-2} right={-2} zIndex={2}>\n {badge}\n </Box>\n ) : status !== \"none\" ? (\n <Box\n position=\"absolute\"\n bottom={0}\n right={0}\n zIndex={2}\n width={dot}\n height={dot}\n borderRadius={dot}\n bgColor={statusColor}\n borderWidth={2}\n borderColor=\"surface-card\"\n />\n ) : null}\n </Box>\n );\n}\n\n/**\n * AvatarStack — overlapping row of avatars with a \"+N\" overflow tile.\n * Each avatar gets a surface-coloured cut-out ring so it reads cleanly\n * against the one behind it.\n *\n * <AvatarStack items={members} max={4} activeIndex={0} />\n */\nexport function AvatarStack(props: IAvatarStackProps) {\n const {mergedProps} = useComponentDefaults<IAvatarStackProps>(\"AvatarStack\", props);\n const {items, max = 4, size = \"md\", overlap = 0.35, activeIndex, ringColor = \"brand\", testID, ...rest} = mergedProps;\n\n const px = resolveSize(size);\n const clampedOverlap = Math.min(Math.max(overlap, 0), 0.9);\n const overlapPx = Math.round(px * clampedOverlap);\n const strokeW = px >= 56 ? 3 : 2;\n\n const visible = items.slice(0, max);\n const overflow = items.length - visible.length;\n\n const cell = (key: string, node: React.ReactNode, i: number) => (\n <Box\n key={key}\n marginLeft={i === 0 ? 0 : -overlapPx}\n zIndex={items.length - i}\n borderRadius={9999}\n borderWidth={strokeW}\n borderColor=\"surface-card\"\n bgColor=\"surface-card\"\n >\n {node}\n </Box>\n );\n\n const a11yProps = {\n role: \"group\",\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box flexDirection=\"row\" alignItems=\"center\" {...rest} {...(a11yProps as any)}>\n {visible.map((item, i) =>\n cell(\n `avatar-${i}`,\n <Avatar\n {...item}\n size={size}\n ring={i === activeIndex || item.ring}\n ringColor={item.ringColor ?? ringColor}\n />,\n i,\n ),\n )}\n {overflow > 0 && cell(\"avatar-overflow\", <Avatar initials={`+${overflow}`} size={size} color=\"ink.400\" />, visible.length)}\n </Box>\n );\n}\n\nexport default Avatar;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IAvatarProps, IAvatarStackProps, AvatarSize} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/* ─── Size resolution ─────────────────────────────────────────── */\nconst SIZE_PX: Record<string, number> = {xs: 24, sm: 32, md: 40, lg: 56, xl: 72};\n\nfunction resolveSize(size: AvatarSize | undefined): number {\n if (typeof size === \"number\") return size;\n return SIZE_PX[size ?? \"md\"] ?? 40;\n}\n\n/* ─── Status dot → semantic token ─────────────────────────────── */\nconst STATUS_TOKENS: Record<string, string> = {\n online: \"success\",\n offline: \"gray.400\",\n away: \"warning\",\n busy: \"danger\",\n};\n\n/* ─── Deterministic monogram palette (all confirmed theme tokens) ─ */\nconst MONOGRAM_PALETTE = [\"brand\", \"primary.800\", \"success\", \"danger\", \"ink.700\"];\n\nfunction hashIndex(input: string, mod: number): number {\n let h = 0;\n for (let i = 0; i < input.length; i++) h = (h * 31 + input.charCodeAt(i)) >>> 0;\n return mod > 0 ? h % mod : 0;\n}\n\nfunction deriveInitials(name?: string, initials?: string): string {\n if (initials) return initials.slice(0, 3).toUpperCase();\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"\";\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]!.charAt(0) + parts[parts.length - 1]!.charAt(0)).toUpperCase();\n}\n\n/**\n * Avatar — image / monogram / icon identity tile.\n *\n * Pure presentational and cross-platform: composed from the `Box` + `Text` +\n * `Image` + `Icon` primitives, so it renders identically on web and native\n * with no platform split. Tappability is the caller's concern (wrap in a\n * Pressable / Button).\n *\n * <Avatar src=\"https://…/me.jpg\" size=\"lg\" />\n * <Avatar name=\"Fouad K\" status=\"online\" />\n * <Avatar initials=\"C\" masked /> // identity-masked (Flex)\n * <Avatar name=\"Acme\" shape=\"rounded\" badge={<Icon name=\"briefcase\" />} />\n */\nexport function Avatar(props: IAvatarProps) {\n const {mergedProps} = useComponentDefaults<IAvatarProps>(\"Avatar\", props);\n // Retire image events with their source. A late load/error from an old\n // photo must not replace the next identity's pending fallback.\n return <AvatarSurface key={JSON.stringify([mergedProps.src ?? null, !!mergedProps.masked])} {...mergedProps} />;\n}\n\nfunction AvatarSurface(mergedProps: IAvatarProps) {\n const {\n src,\n name,\n initials,\n icon,\n size = \"md\",\n shape = \"circle\",\n color,\n status = \"none\",\n ring = false,\n ringColor = \"brand\",\n masked = false,\n maskFillColor = \"indigo.600\",\n badge,\n alt,\n testID,\n ...rest\n } = mergedProps;\n\n const px = resolveSize(size);\n const radius = shape === \"circle\" ? px / 2 : Math.round(px * 0.28);\n const fontSize = Math.max(10, Math.round(px * 0.4));\n const iconSize = Math.round(px * 0.5);\n const dot = Math.max(8, Math.round(px * 0.28));\n const ringW = px >= 56 ? 3 : 2;\n\n const resolvedInitials = deriveInitials(name, initials);\n const [imageStatus, setImageStatus] = React.useState<\"pending\" | \"loaded\" | \"failed\">(\"pending\");\n const hasImage = !!src && imageStatus !== \"failed\";\n const imageDelivered = hasImage && imageStatus === \"loaded\";\n const showInitials = !imageDelivered && resolvedInitials.length > 0;\n const showIcon = !imageDelivered && !showInitials && icon != null;\n\n const monogramBg =\n color ?? (MONOGRAM_PALETTE[hashIndex(name ?? resolvedInitials, MONOGRAM_PALETTE.length)] ?? \"brand\");\n const surfaceBg = masked ? maskFillColor : imageDelivered || (src && !resolvedInitials && icon == null) ? \"gray.100\" : monogramBg;\n const initialsColor = \"#FFFFFF\";\n\n const statusColor = STATUS_TOKENS[status] ?? \"gray.400\";\n const ariaLabel = alt ?? name ?? \"avatar\";\n\n const a11yProps = {\n role: \"img\",\n \"aria-label\": ariaLabel,\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box\n position=\"relative\"\n width={px}\n height={px}\n {...rest}\n {...(a11yProps as any)}\n >\n {/* Clip only the photo frame: pending/failed monogram glyphs must\n remain unclipped on native. */}\n <Box\n width=\"100%\"\n height=\"100%\"\n borderRadius={radius}\n overflow=\"visible\"\n alignItems=\"center\"\n justifyContent=\"center\"\n bgColor={surfaceBg}\n >\n {masked ? (\n <Icon name=\"user\" size={iconSize} color=\"#FFFFFF\" />\n ) : (\n <>\n {showInitials ? (\n <Text\n color={initialsColor}\n fontSize={fontSize}\n fontWeight=\"700\"\n lineHeight={Math.round(fontSize * 1.2)}\n textAlign=\"center\"\n >\n {resolvedInitials}\n </Text>\n ) : showIcon ? (\n <Icon name={icon as never} size={iconSize} color=\"gray.500\" />\n ) : null}\n {hasImage ? (\n /*\n * width/height via style: the Image primitive types dimensions\n * differently per platform (web string vs native number).\n *\n * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with\n * a `src` warned, on every render. gluestack's own image creator\n * does `if (typeof alt !== 'string') console.warn('Please pass\n * alt prop to Image component')`, so the check is on the TYPE,\n * not on emptiness: a missing prop warns and an empty string\n * does not. Measured in AlphaMatch on 5 October 2026 — 21\n * warnings in a single sign-in from `AMTabBar`'s Me slot alone,\n * which is one Avatar.\n *\n * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above\n * already carries `role=\"img\"` and `aria-label={ariaLabel}`, so\n * this image is PRESENTATION inside an element that is already\n * named; repeating the label here would announce the person's\n * name twice. Empty is the standing answer for a decorative\n * image, and it is this library's own: `PhoneInput` passes\n * `alt={countryISO ?? \"\"}` for exactly this reason.\n *\n * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=\"\"` ALONE WAS NOT\n * ENOUGH — found by Codex, 5 October 2026. gluestack does not\n * only read `alt`; it also forces\n * `role={props.role || 'img'}`, so an empty alt leaves a\n * SECOND, NAMELESS image role sitting under the labelled Box.\n * On the web an `<img alt=\"\">` is decorative and harmless; on\n * React Native that role becomes `accessibilityRole=\"image\"`\n * and a screen reader can land on an unnamed image. Hiding the\n * node is what makes it presentational on both platforms —\n * RN maps `aria-hidden` to `accessibilityElementsHidden` on\n * iOS and `importantForAccessibility=\"no-hide-descendants\"` on\n * Android. `alt=\"\"` stays because it is what silences the\n * warning: gluestack checks `typeof alt !== 'string'`.\n *\n * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is\n * NOT fixed here — it was outside what this change was opened\n * for. Recorded in the session report rather than swept in.\n */\n <Box position=\"absolute\" top={0} left={0} width={px} height={px}\n borderRadius={radius} overflow=\"hidden\" opacity={imageDelivered ? 1 : 0}>\n <Image\n src={src}\n alt=\"\"\n aria-hidden\n style={{width: px, height: px} as any}\n objectFit=\"cover\"\n onLoad={() => setImageStatus(current => current === \"failed\" ? current : \"loaded\")}\n onError={() => setImageStatus(\"failed\")}\n />\n </Box>\n ) : null}\n </>\n )}\n </Box>\n\n {/* Ring overlay (solid active ring) */}\n {ring && (\n <Box\n position=\"absolute\"\n top={0}\n right={0}\n bottom={0}\n left={0}\n borderRadius={radius}\n borderWidth={ringW}\n borderStyle=\"solid\"\n borderColor={ringColor}\n />\n )}\n\n {/* Badge OR presence dot (badge wins the corner) */}\n {badge != null ? (\n <Box position=\"absolute\" bottom={-2} right={-2} zIndex={2}>\n {badge}\n </Box>\n ) : status !== \"none\" ? (\n <Box\n position=\"absolute\"\n bottom={0}\n right={0}\n zIndex={2}\n width={dot}\n height={dot}\n borderRadius={dot}\n bgColor={statusColor}\n borderWidth={2}\n borderColor=\"surface-card\"\n />\n ) : null}\n </Box>\n );\n}\n\n/**\n * AvatarStack — overlapping row of avatars with a \"+N\" overflow tile.\n * Each avatar gets a surface-coloured cut-out ring so it reads cleanly\n * against the one behind it.\n *\n * <AvatarStack items={members} max={4} activeIndex={0} />\n */\nexport function AvatarStack(props: IAvatarStackProps) {\n const {mergedProps} = useComponentDefaults<IAvatarStackProps>(\"AvatarStack\", props);\n const {items, max = 4, size = \"md\", overlap = 0.35, activeIndex, ringColor = \"brand\", testID, ...rest} = mergedProps;\n\n const px = resolveSize(size);\n const clampedOverlap = Math.min(Math.max(overlap, 0), 0.9);\n const overlapPx = Math.round(px * clampedOverlap);\n const strokeW = px >= 56 ? 3 : 2;\n\n const visible = items.slice(0, max);\n const overflow = items.length - visible.length;\n\n const cell = (key: string, node: React.ReactNode, i: number) => (\n <Box\n key={key}\n marginLeft={i === 0 ? 0 : -overlapPx}\n zIndex={items.length - i}\n borderRadius={9999}\n borderWidth={strokeW}\n borderColor=\"surface-card\"\n bgColor=\"surface-card\"\n >\n {node}\n </Box>\n );\n\n const a11yProps = {\n role: \"group\",\n ...(testID ? {testID, \"data-testid\": testID} : {}),\n } as Record<string, unknown>;\n\n return (\n <Box flexDirection=\"row\" alignItems=\"center\" {...rest} {...(a11yProps as any)}>\n {visible.map((item, i) =>\n cell(\n `avatar-${i}`,\n <Avatar\n {...item}\n size={size}\n ring={i === activeIndex || item.ring}\n ringColor={item.ringColor ?? ringColor}\n />,\n i,\n ),\n )}\n {overflow > 0 && cell(\"avatar-overflow\", <Avatar initials={`+${overflow}`} size={size} color=\"ink.400\" />, visible.length)}\n </Box>\n );\n}\n\nexport default Avatar;\n"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@ function KitsConfirm(props) {
45
45
  defaultUI,
46
46
  async: asyncMode,
47
47
  onHide,
48
+ closeAriaLabel = "Close",
48
49
  content,
49
50
  children,
50
51
  position = "center",
@@ -243,7 +244,17 @@ function KitsConfirm(props) {
243
244
  resolvedContentStyle
244
245
  ],
245
246
  children: [
246
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles.closeBtn, onPress: hide, hitSlop: 12, children: /* @__PURE__ */ jsxRuntime.jsx(index_native.default, { style: { fontSize: 18, color: subtextColor }, children: "\u2715" }) }),
247
+ /* @__PURE__ */ jsxRuntime.jsx(
248
+ reactNative.Pressable,
249
+ {
250
+ style: styles.closeBtn,
251
+ onPress: hide,
252
+ hitSlop: 12,
253
+ accessibilityRole: "button",
254
+ accessibilityLabel: closeAriaLabel,
255
+ children: /* @__PURE__ */ jsxRuntime.jsx(index_native.default, { style: { fontSize: 18, color: subtextColor }, children: "\u2715" })
256
+ }
257
+ ),
247
258
  header != null && /* @__PURE__ */ jsxRuntime.jsx(index_native.default, { style: [styles.header, { color: textColor }, resolvedHeaderStyle], children: header }),
248
259
  message != null && /* @__PURE__ */ jsxRuntime.jsx(index_native.default, { style: [styles.message, { color: subtextColor }], children: message }),
249
260
  children,