@lmb-it/kitsconcerto-native 0.1.271 → 0.1.273

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.
@@ -94,9 +94,55 @@ function Avatar(props) {
94
94
  justifyContent: "center",
95
95
  bgColor: surfaceBg,
96
96
  children: masked ? /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : hasImage ? (
97
- // width/height via style: the Image primitive types dimensions
98
- // differently per platform (web string vs native number).
99
- /* @__PURE__ */ jsxRuntime.jsx(index_native$1.default, { src, style: { width: px, height: px }, objectFit: "cover" })
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__ */ jsxRuntime.jsx(
137
+ index_native$1.default,
138
+ {
139
+ src,
140
+ alt: "",
141
+ "aria-hidden": true,
142
+ style: { width: px, height: px },
143
+ objectFit: "cover"
144
+ }
145
+ )
100
146
  ) : showInitials ? /* @__PURE__ */ jsxRuntime.jsx(
101
147
  index_native$2.default,
102
148
  {
@@ -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 // width/height via style: the Image primitive types dimensions\n // differently per platform (web string vs native number).\n <Image src={src} style={{width: px, height: px} as any} objectFit=\"cover\" />\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 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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"}
@@ -92,9 +92,55 @@ function Avatar(props) {
92
92
  justifyContent: "center",
93
93
  bgColor: surfaceBg,
94
94
  children: masked ? /* @__PURE__ */ jsx(Icon, { name: "user", size: iconSize, color: "#FFFFFF" }) : hasImage ? (
95
- // width/height via style: the Image primitive types dimensions
96
- // differently per platform (web string vs native number).
97
- /* @__PURE__ */ jsx(Image, { src, style: { width: px, height: px }, objectFit: "cover" })
95
+ /*
96
+ * width/height via style: the Image primitive types dimensions
97
+ * differently per platform (web string vs native number).
98
+ *
99
+ * ⛔ `alt` IS REQUIRED HERE AND WAS MISSING — every Avatar with
100
+ * a `src` warned, on every render. gluestack's own image creator
101
+ * does `if (typeof alt !== 'string') console.warn('Please pass
102
+ * alt prop to Image component')`, so the check is on the TYPE,
103
+ * not on emptiness: a missing prop warns and an empty string
104
+ * does not. Measured in AlphaMatch on 5 October 2026 — 21
105
+ * warnings in a single sign-in from `AMTabBar`'s Me slot alone,
106
+ * which is one Avatar.
107
+ *
108
+ * ⛔ AND IT IS EMPTY ON PURPOSE, NOT `ariaLabel`. The Box above
109
+ * already carries `role="img"` and `aria-label={ariaLabel}`, so
110
+ * this image is PRESENTATION inside an element that is already
111
+ * named; repeating the label here would announce the person's
112
+ * name twice. Empty is the standing answer for a decorative
113
+ * image, and it is this library's own: `PhoneInput` passes
114
+ * `alt={countryISO ?? ""}` for exactly this reason.
115
+ *
116
+ * ⛔ `aria-hidden` IS THE OTHER HALF, AND `alt=""` ALONE WAS NOT
117
+ * ENOUGH — found by Codex, 5 October 2026. gluestack does not
118
+ * only read `alt`; it also forces
119
+ * `role={props.role || 'img'}`, so an empty alt leaves a
120
+ * SECOND, NAMELESS image role sitting under the labelled Box.
121
+ * On the web an `<img alt="">` is decorative and harmless; on
122
+ * React Native that role becomes `accessibilityRole="image"`
123
+ * and a screen reader can land on an unnamed image. Hiding the
124
+ * node is what makes it presentational on both platforms —
125
+ * RN maps `aria-hidden` to `accessibilityElementsHidden` on
126
+ * iOS and `importantForAccessibility="no-hide-descendants"` on
127
+ * Android. `alt=""` stays because it is what silences the
128
+ * warning: gluestack checks `typeof alt !== 'string'`.
129
+ *
130
+ * ⚠️ `Gallery` HAS THE SAME MISSING `alt`, one file away, and is
131
+ * NOT fixed here — it was outside what this change was opened
132
+ * for. Recorded in the session report rather than swept in.
133
+ */
134
+ /* @__PURE__ */ jsx(
135
+ Image,
136
+ {
137
+ src,
138
+ alt: "",
139
+ "aria-hidden": true,
140
+ style: { width: px, height: px },
141
+ objectFit: "cover"
142
+ }
143
+ )
98
144
  ) : showInitials ? /* @__PURE__ */ jsx(
99
145
  Text,
100
146
  {
@@ -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 // width/height via style: the Image primitive types dimensions\n // differently per platform (web string vs native number).\n <Image src={src} style={{width: px, height: px} as any} objectFit=\"cover\" />\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 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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"}
@@ -37,7 +37,63 @@ function Gallery(rawProps) {
37
37
  borderWidth: item.selected ? 2 : 0,
38
38
  borderColor: item.selected ? "brand" : "transparent",
39
39
  children: [
40
- item.src != null && /* @__PURE__ */ jsxRuntime.jsx(index_native.default, { src: item.src, style: { width: "100%", height: "100%" }, objectFit: "cover" }),
40
+ item.src != null && /*
41
+ * ⛔ `alt` WAS MISSING, SO EVERY TILE WITH AN IMAGE
42
+ * WARNED — the same omission the Avatar carried, found
43
+ * beside it on 5 October 2026. gluestack's image creator
44
+ * does `if (typeof alt !== 'string') console.warn('Please
45
+ * pass alt prop to Image component')`, so the check is on
46
+ * the TYPE: a missing prop warns, an empty string does
47
+ * not.
48
+ *
49
+ * ⛔ AND `IGalleryItem.alt` HAS EXISTED ALL ALONG,
50
+ * published and documented as "Accessibility label", and
51
+ * this component read it NOWHERE. A caller could set it,
52
+ * tsc accepted it, and it reached nothing — so the fix is
53
+ * to honour the contract that was already written down,
54
+ * not to invent one.
55
+ *
56
+ * ⛔ `aria-hidden` IS CONDITIONAL HERE, WHERE THE
57
+ * AVATAR'S IS UNCONDITIONAL — the two structures differ.
58
+ * The Avatar's image sits inside a Box that already
59
+ * carries `role="img"` and `aria-label`, so it is ALWAYS
60
+ * redundant. This tile's Box carries no role and no label
61
+ * at all, so when a caller HAS described the image it is
62
+ * the only thing that can say so and it must stay in the
63
+ * tree.
64
+ *
65
+ * ⛔ AND WITH NO DESCRIPTION IT IS HIDDEN, NOT EXPOSED
66
+ * EMPTY — found by Codex, 5 October 2026, and the first
67
+ * draft of this fix had it backwards. gluestack forces
68
+ * `role={props.role || 'img'}` whatever `alt` says, so
69
+ * `alt=""` alone leaves an EXPOSED, NAMELESS image node on
70
+ * React Native. "No labelled ancestor" was the wrong
71
+ * reason to leave it exposed: an image with no name cannot
72
+ * speak for the tile either way.
73
+ *
74
+ * ⛔ AND IT DOES NOT FALL BACK TO `title`. The draft
75
+ * did, and `title` is ALREADY rendered as a Text sibling a
76
+ * few lines below — a screen reader would have read it
77
+ * twice: "image, Rewire" then "Rewire". The caption
78
+ * announces itself; the image either adds something the
79
+ * caller wrote in `alt`, or it adds nothing and gets out
80
+ * of the way.
81
+ *
82
+ * ⚠️ `alt=""` STAYS IN BOTH BRANCHES because it is what
83
+ * silences the warning: gluestack tests
84
+ * `typeof alt !== 'string'`, so a missing prop warns and
85
+ * an empty string does not.
86
+ */
87
+ /* @__PURE__ */ jsxRuntime.jsx(
88
+ index_native.default,
89
+ {
90
+ src: item.src,
91
+ alt: item.alt ?? "",
92
+ "aria-hidden": !item.alt,
93
+ style: { width: "100%", height: "100%" },
94
+ objectFit: "cover"
95
+ }
96
+ ),
41
97
  item.topLeft != null && /* @__PURE__ */ jsxRuntime.jsx(index.default, { position: "absolute", top: 6, left: 6, children: item.topLeft }),
42
98
  item.bottomRight != null && /* @__PURE__ */ jsxRuntime.jsx(index.default, { position: "absolute", bottom: 6, right: 6, children: item.bottomRight }),
43
99
  item.selected && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/components/Gallery/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IGalleryProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * Gallery — responsive image grid with per-tile overlay slots, caption, and a\n * `selected` state (ring + check) for multiselect grids.\n *\n * Cross-platform and presentational (Box + Image + Text + Icon). Tappability is\n * the caller's concern — wrap tiles, or drive `selected` from your own handler.\n *\n * <Gallery columns={2} items={[\n * {src, title:'Rewire', meta:'12 May · Newtown', topLeft:<Chip label=\"VERIFIED\" size=\"sm\" />},\n * ]} />\n */\nexport function Gallery(rawProps: IGalleryProps) {\n const props = useComponentDefaults<IGalleryProps>(\"Gallery\", rawProps).mergedProps;\n const {items, columns = 2, gap = 8, aspectRatio = 1, radius = 12, testID, ...rest} = props;\n\n const cols = Math.max(1, Math.floor(columns));\n const widthPct = `${100 / cols}%`;\n const half = gap / 2;\n\n return (\n <Box\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n marginLeft={-half}\n marginRight={-half}\n {...(rest as any)}\n {...(testID ? ({testID, \"data-testid\": testID} as any) : {})}\n >\n {items.map((item, i) => (\n <Box key={i} width={widthPct} paddingLeft={half} paddingRight={half} paddingBottom={gap}>\n {/* image card */}\n <Box\n position=\"relative\"\n width=\"100%\"\n aspectRatio={aspectRatio}\n borderRadius={radius}\n overflow=\"hidden\"\n bgColor=\"gray.100\"\n borderWidth={item.selected ? 2 : 0}\n borderColor={item.selected ? \"brand\" : \"transparent\"}\n >\n {item.src != null && (\n <Image src={item.src} style={{width: \"100%\", height: \"100%\"} as any} objectFit=\"cover\" />\n )}\n\n {item.topLeft != null && (\n <Box position=\"absolute\" top={6} left={6}>\n {item.topLeft}\n </Box>\n )}\n {item.bottomRight != null && (\n <Box position=\"absolute\" bottom={6} right={6}>\n {item.bottomRight}\n </Box>\n )}\n {item.selected && (\n <Box\n position=\"absolute\"\n top={6}\n right={6}\n width={22}\n height={22}\n borderRadius={999}\n bgColor=\"brand\"\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={2}\n borderColor=\"#FFFFFF\"\n >\n <Icon name=\"check\" size={12} color=\"#FFFFFF\" />\n </Box>\n )}\n </Box>\n\n {(item.title != null || item.meta != null) && (\n <Box paddingTop={6} gap={2}>\n {item.title != null && (\n <Text fontSize={13} fontWeight=\"700\" color=\"text\">\n {item.title}\n </Text>\n )}\n {item.meta != null && (\n <Text fontSize={11} color=\"text-secondary\">\n {item.meta}\n </Text>\n )}\n </Box>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n\nexport default Gallery;\n"],"names":["useComponentDefaults","jsx","Box","jsxs","Image","Icon","Text"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Gallery/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IGalleryProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * Gallery — responsive image grid with per-tile overlay slots, caption, and a\n * `selected` state (ring + check) for multiselect grids.\n *\n * Cross-platform and presentational (Box + Image + Text + Icon). Tappability is\n * the caller's concern — wrap tiles, or drive `selected` from your own handler.\n *\n * <Gallery columns={2} items={[\n * {src, title:'Rewire', meta:'12 May · Newtown', topLeft:<Chip label=\"VERIFIED\" size=\"sm\" />},\n * ]} />\n */\nexport function Gallery(rawProps: IGalleryProps) {\n const props = useComponentDefaults<IGalleryProps>(\"Gallery\", rawProps).mergedProps;\n const {items, columns = 2, gap = 8, aspectRatio = 1, radius = 12, testID, ...rest} = props;\n\n const cols = Math.max(1, Math.floor(columns));\n const widthPct = `${100 / cols}%`;\n const half = gap / 2;\n\n return (\n <Box\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n marginLeft={-half}\n marginRight={-half}\n {...(rest as any)}\n {...(testID ? ({testID, \"data-testid\": testID} as any) : {})}\n >\n {items.map((item, i) => (\n <Box key={i} width={widthPct} paddingLeft={half} paddingRight={half} paddingBottom={gap}>\n {/* image card */}\n <Box\n position=\"relative\"\n width=\"100%\"\n aspectRatio={aspectRatio}\n borderRadius={radius}\n overflow=\"hidden\"\n bgColor=\"gray.100\"\n borderWidth={item.selected ? 2 : 0}\n borderColor={item.selected ? \"brand\" : \"transparent\"}\n >\n {item.src != null && (\n /*\n * ⛔ `alt` WAS MISSING, SO EVERY TILE WITH AN IMAGE\n * WARNED — the same omission the Avatar carried, found\n * beside it on 5 October 2026. gluestack's image creator\n * does `if (typeof alt !== 'string') console.warn('Please\n * pass alt prop to Image component')`, so the check is on\n * the TYPE: a missing prop warns, an empty string does\n * not.\n *\n * ⛔ AND `IGalleryItem.alt` HAS EXISTED ALL ALONG,\n * published and documented as \"Accessibility label\", and\n * this component read it NOWHERE. A caller could set it,\n * tsc accepted it, and it reached nothing — so the fix is\n * to honour the contract that was already written down,\n * not to invent one.\n *\n * ⛔ `aria-hidden` IS CONDITIONAL HERE, WHERE THE\n * AVATAR'S IS UNCONDITIONAL — the two structures differ.\n * The Avatar's image sits inside a Box that already\n * carries `role=\"img\"` and `aria-label`, so it is ALWAYS\n * redundant. This tile's Box carries no role and no label\n * at all, so when a caller HAS described the image it is\n * the only thing that can say so and it must stay in the\n * tree.\n *\n * ⛔ AND WITH NO DESCRIPTION IT IS HIDDEN, NOT EXPOSED\n * EMPTY — found by Codex, 5 October 2026, and the first\n * draft of this fix had it backwards. gluestack forces\n * `role={props.role || 'img'}` whatever `alt` says, so\n * `alt=\"\"` alone leaves an EXPOSED, NAMELESS image node on\n * React Native. \"No labelled ancestor\" was the wrong\n * reason to leave it exposed: an image with no name cannot\n * speak for the tile either way.\n *\n * ⛔ AND IT DOES NOT FALL BACK TO `title`. The draft\n * did, and `title` is ALREADY rendered as a Text sibling a\n * few lines below — a screen reader would have read it\n * twice: \"image, Rewire\" then \"Rewire\". The caption\n * announces itself; the image either adds something the\n * caller wrote in `alt`, or it adds nothing and gets out\n * of the way.\n *\n * ⚠️ `alt=\"\"` STAYS IN BOTH BRANCHES because it is what\n * silences the warning: gluestack tests\n * `typeof alt !== 'string'`, so a missing prop warns and\n * an empty string does not.\n */\n <Image\n src={item.src}\n alt={item.alt ?? \"\"}\n aria-hidden={!item.alt}\n style={{width: \"100%\", height: \"100%\"} as any}\n objectFit=\"cover\"\n />\n )}\n\n {item.topLeft != null && (\n <Box position=\"absolute\" top={6} left={6}>\n {item.topLeft}\n </Box>\n )}\n {item.bottomRight != null && (\n <Box position=\"absolute\" bottom={6} right={6}>\n {item.bottomRight}\n </Box>\n )}\n {item.selected && (\n <Box\n position=\"absolute\"\n top={6}\n right={6}\n width={22}\n height={22}\n borderRadius={999}\n bgColor=\"brand\"\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={2}\n borderColor=\"#FFFFFF\"\n >\n <Icon name=\"check\" size={12} color=\"#FFFFFF\" />\n </Box>\n )}\n </Box>\n\n {(item.title != null || item.meta != null) && (\n <Box paddingTop={6} gap={2}>\n {item.title != null && (\n <Text fontSize={13} fontWeight=\"700\" color=\"text\">\n {item.title}\n </Text>\n )}\n {item.meta != null && (\n <Text fontSize={11} color=\"text-secondary\">\n {item.meta}\n </Text>\n )}\n </Box>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n\nexport default Gallery;\n"],"names":["useComponentDefaults","jsx","Box","jsxs","Image","Icon","Text"],"mappings":";;;;;;;;;;;AAgBO,SAAS,QAAQ,QAAA,EAAyB;AAC7C,EAAA,MAAM,KAAA,GAAQA,4BAAA,CAAoC,SAAA,EAAW,QAAQ,CAAA,CAAE,WAAA;AACvE,EAAA,MAAM,EAAC,KAAA,EAAO,OAAA,GAAU,CAAA,EAAG,GAAA,GAAM,CAAA,EAAG,WAAA,GAAc,CAAA,EAAG,MAAA,GAAS,EAAA,EAAI,MAAA,EAAQ,GAAG,MAAI,GAAI,KAAA;AAErF,EAAA,MAAM,OAAO,IAAA,CAAK,GAAA,CAAI,GAAG,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAC5C,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,GAAA,GAAM,IAAI,CAAA,CAAA,CAAA;AAC9B,EAAA,MAAM,OAAO,GAAA,GAAM,CAAA;AAEnB,EAAA,uBACIC,cAAA;AAAA,IAACC,aAAA;AAAA,IAAA;AAAA,MACG,aAAA,EAAc,KAAA;AAAA,MACd,QAAA,EAAS,MAAA;AAAA,MACT,YAAY,CAAC,IAAA;AAAA,MACb,aAAa,CAAC,IAAA;AAAA,MACb,GAAI,IAAA;AAAA,MACJ,GAAI,MAAA,GAAU,EAAC,QAAQ,aAAA,EAAe,MAAA,KAAkB,EAAC;AAAA,MAEzD,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,sBACdC,eAAA,CAACD,aAAA,EAAA,EAAY,KAAA,EAAO,QAAA,EAAU,WAAA,EAAa,IAAA,EAAM,YAAA,EAAc,IAAA,EAAM,eAAe,GAAA,EAEhF,QAAA,EAAA;AAAA,wBAAAC,eAAA;AAAA,UAACD,aAAA;AAAA,UAAA;AAAA,YACG,QAAA,EAAS,UAAA;AAAA,YACT,KAAA,EAAM,MAAA;AAAA,YACN,WAAA;AAAA,YACA,YAAA,EAAc,MAAA;AAAA,YACd,QAAA,EAAS,QAAA;AAAA,YACT,OAAA,EAAQ,UAAA;AAAA,YACR,WAAA,EAAa,IAAA,CAAK,QAAA,GAAW,CAAA,GAAI,CAAA;AAAA,YACjC,WAAA,EAAa,IAAA,CAAK,QAAA,GAAW,OAAA,GAAU,aAAA;AAAA,YAEtC,QAAA,EAAA;AAAA,cAAA,IAAA,CAAK,GAAA,IAAO,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAgDTD,cAAA;AAAA,gBAACG,oBAAA;AAAA,gBAAA;AAAA,kBACG,KAAK,IAAA,CAAK,GAAA;AAAA,kBACV,GAAA,EAAK,KAAK,GAAA,IAAO,EAAA;AAAA,kBACjB,aAAA,EAAa,CAAC,IAAA,CAAK,GAAA;AAAA,kBACnB,KAAA,EAAO,EAAC,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAM;AAAA,kBACrC,SAAA,EAAU;AAAA;AAAA,eACd;AAAA,cAGH,IAAA,CAAK,OAAA,IAAW,IAAA,oBACbH,cAAA,CAACC,aAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EAAW,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,CAAA,EAClC,QAAA,EAAA,IAAA,CAAK,OAAA,EACV,CAAA;AAAA,cAEH,IAAA,CAAK,WAAA,IAAe,IAAA,oBACjBD,cAAA,CAACC,aAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EAAW,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAO,CAAA,EACtC,QAAA,EAAA,IAAA,CAAK,WAAA,EACV,CAAA;AAAA,cAEH,KAAK,QAAA,oBACFD,cAAA;AAAA,gBAACC,aAAA;AAAA,gBAAA;AAAA,kBACG,QAAA,EAAS,UAAA;AAAA,kBACT,GAAA,EAAK,CAAA;AAAA,kBACL,KAAA,EAAO,CAAA;AAAA,kBACP,KAAA,EAAO,EAAA;AAAA,kBACP,MAAA,EAAQ,EAAA;AAAA,kBACR,YAAA,EAAc,GAAA;AAAA,kBACd,OAAA,EAAQ,OAAA;AAAA,kBACR,UAAA,EAAW,QAAA;AAAA,kBACX,cAAA,EAAe,QAAA;AAAA,kBACf,WAAA,EAAa,CAAA;AAAA,kBACb,WAAA,EAAY,SAAA;AAAA,kBAEZ,yCAACG,mBAAA,EAAA,EAAK,IAAA,EAAK,SAAQ,IAAA,EAAM,EAAA,EAAI,OAAM,SAAA,EAAU;AAAA;AAAA;AACjD;AAAA;AAAA,SAER;AAAA,QAAA,CAEE,IAAA,CAAK,KAAA,IAAS,IAAA,IAAQ,IAAA,CAAK,IAAA,IAAQ,IAAA,qBACjCF,eAAA,CAACD,aAAA,EAAA,EAAI,UAAA,EAAY,CAAA,EAAG,GAAA,EAAK,CAAA,EACpB,QAAA,EAAA;AAAA,UAAA,IAAA,CAAK,KAAA,IAAS,IAAA,oBACXD,cAAA,CAACK,sBAAA,EAAA,EAAK,QAAA,EAAU,EAAA,EAAI,UAAA,EAAW,KAAA,EAAM,KAAA,EAAM,MAAA,EACtC,QAAA,EAAA,IAAA,CAAK,KAAA,EACV,CAAA;AAAA,UAEH,IAAA,CAAK,IAAA,IAAQ,IAAA,oBACVL,cAAA,CAACK,sBAAA,EAAA,EAAK,UAAU,EAAA,EAAI,KAAA,EAAM,gBAAA,EACrB,QAAA,EAAA,IAAA,CAAK,IAAA,EACV;AAAA,SAAA,EAER;AAAA,OAAA,EAAA,EA9GE,CAgHV,CACH;AAAA;AAAA,GACL;AAER;;;;"}
@@ -35,7 +35,63 @@ function Gallery(rawProps) {
35
35
  borderWidth: item.selected ? 2 : 0,
36
36
  borderColor: item.selected ? "brand" : "transparent",
37
37
  children: [
38
- item.src != null && /* @__PURE__ */ jsx(Image, { src: item.src, style: { width: "100%", height: "100%" }, objectFit: "cover" }),
38
+ item.src != null && /*
39
+ * ⛔ `alt` WAS MISSING, SO EVERY TILE WITH AN IMAGE
40
+ * WARNED — the same omission the Avatar carried, found
41
+ * beside it on 5 October 2026. gluestack's image creator
42
+ * does `if (typeof alt !== 'string') console.warn('Please
43
+ * pass alt prop to Image component')`, so the check is on
44
+ * the TYPE: a missing prop warns, an empty string does
45
+ * not.
46
+ *
47
+ * ⛔ AND `IGalleryItem.alt` HAS EXISTED ALL ALONG,
48
+ * published and documented as "Accessibility label", and
49
+ * this component read it NOWHERE. A caller could set it,
50
+ * tsc accepted it, and it reached nothing — so the fix is
51
+ * to honour the contract that was already written down,
52
+ * not to invent one.
53
+ *
54
+ * ⛔ `aria-hidden` IS CONDITIONAL HERE, WHERE THE
55
+ * AVATAR'S IS UNCONDITIONAL — the two structures differ.
56
+ * The Avatar's image sits inside a Box that already
57
+ * carries `role="img"` and `aria-label`, so it is ALWAYS
58
+ * redundant. This tile's Box carries no role and no label
59
+ * at all, so when a caller HAS described the image it is
60
+ * the only thing that can say so and it must stay in the
61
+ * tree.
62
+ *
63
+ * ⛔ AND WITH NO DESCRIPTION IT IS HIDDEN, NOT EXPOSED
64
+ * EMPTY — found by Codex, 5 October 2026, and the first
65
+ * draft of this fix had it backwards. gluestack forces
66
+ * `role={props.role || 'img'}` whatever `alt` says, so
67
+ * `alt=""` alone leaves an EXPOSED, NAMELESS image node on
68
+ * React Native. "No labelled ancestor" was the wrong
69
+ * reason to leave it exposed: an image with no name cannot
70
+ * speak for the tile either way.
71
+ *
72
+ * ⛔ AND IT DOES NOT FALL BACK TO `title`. The draft
73
+ * did, and `title` is ALREADY rendered as a Text sibling a
74
+ * few lines below — a screen reader would have read it
75
+ * twice: "image, Rewire" then "Rewire". The caption
76
+ * announces itself; the image either adds something the
77
+ * caller wrote in `alt`, or it adds nothing and gets out
78
+ * of the way.
79
+ *
80
+ * ⚠️ `alt=""` STAYS IN BOTH BRANCHES because it is what
81
+ * silences the warning: gluestack tests
82
+ * `typeof alt !== 'string'`, so a missing prop warns and
83
+ * an empty string does not.
84
+ */
85
+ /* @__PURE__ */ jsx(
86
+ Image,
87
+ {
88
+ src: item.src,
89
+ alt: item.alt ?? "",
90
+ "aria-hidden": !item.alt,
91
+ style: { width: "100%", height: "100%" },
92
+ objectFit: "cover"
93
+ }
94
+ ),
39
95
  item.topLeft != null && /* @__PURE__ */ jsx(Box, { position: "absolute", top: 6, left: 6, children: item.topLeft }),
40
96
  item.bottomRight != null && /* @__PURE__ */ jsx(Box, { position: "absolute", bottom: 6, right: 6, children: item.bottomRight }),
41
97
  item.selected && /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/Gallery/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IGalleryProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * Gallery — responsive image grid with per-tile overlay slots, caption, and a\n * `selected` state (ring + check) for multiselect grids.\n *\n * Cross-platform and presentational (Box + Image + Text + Icon). Tappability is\n * the caller's concern — wrap tiles, or drive `selected` from your own handler.\n *\n * <Gallery columns={2} items={[\n * {src, title:'Rewire', meta:'12 May · Newtown', topLeft:<Chip label=\"VERIFIED\" size=\"sm\" />},\n * ]} />\n */\nexport function Gallery(rawProps: IGalleryProps) {\n const props = useComponentDefaults<IGalleryProps>(\"Gallery\", rawProps).mergedProps;\n const {items, columns = 2, gap = 8, aspectRatio = 1, radius = 12, testID, ...rest} = props;\n\n const cols = Math.max(1, Math.floor(columns));\n const widthPct = `${100 / cols}%`;\n const half = gap / 2;\n\n return (\n <Box\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n marginLeft={-half}\n marginRight={-half}\n {...(rest as any)}\n {...(testID ? ({testID, \"data-testid\": testID} as any) : {})}\n >\n {items.map((item, i) => (\n <Box key={i} width={widthPct} paddingLeft={half} paddingRight={half} paddingBottom={gap}>\n {/* image card */}\n <Box\n position=\"relative\"\n width=\"100%\"\n aspectRatio={aspectRatio}\n borderRadius={radius}\n overflow=\"hidden\"\n bgColor=\"gray.100\"\n borderWidth={item.selected ? 2 : 0}\n borderColor={item.selected ? \"brand\" : \"transparent\"}\n >\n {item.src != null && (\n <Image src={item.src} style={{width: \"100%\", height: \"100%\"} as any} objectFit=\"cover\" />\n )}\n\n {item.topLeft != null && (\n <Box position=\"absolute\" top={6} left={6}>\n {item.topLeft}\n </Box>\n )}\n {item.bottomRight != null && (\n <Box position=\"absolute\" bottom={6} right={6}>\n {item.bottomRight}\n </Box>\n )}\n {item.selected && (\n <Box\n position=\"absolute\"\n top={6}\n right={6}\n width={22}\n height={22}\n borderRadius={999}\n bgColor=\"brand\"\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={2}\n borderColor=\"#FFFFFF\"\n >\n <Icon name=\"check\" size={12} color=\"#FFFFFF\" />\n </Box>\n )}\n </Box>\n\n {(item.title != null || item.meta != null) && (\n <Box paddingTop={6} gap={2}>\n {item.title != null && (\n <Text fontSize={13} fontWeight=\"700\" color=\"text\">\n {item.title}\n </Text>\n )}\n {item.meta != null && (\n <Text fontSize={11} color=\"text-secondary\">\n {item.meta}\n </Text>\n )}\n </Box>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n\nexport default Gallery;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/Gallery/index.tsx"],"sourcesContent":["import React from \"react\";\nimport type {IGalleryProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Box, Text, Image, Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * Gallery — responsive image grid with per-tile overlay slots, caption, and a\n * `selected` state (ring + check) for multiselect grids.\n *\n * Cross-platform and presentational (Box + Image + Text + Icon). Tappability is\n * the caller's concern — wrap tiles, or drive `selected` from your own handler.\n *\n * <Gallery columns={2} items={[\n * {src, title:'Rewire', meta:'12 May · Newtown', topLeft:<Chip label=\"VERIFIED\" size=\"sm\" />},\n * ]} />\n */\nexport function Gallery(rawProps: IGalleryProps) {\n const props = useComponentDefaults<IGalleryProps>(\"Gallery\", rawProps).mergedProps;\n const {items, columns = 2, gap = 8, aspectRatio = 1, radius = 12, testID, ...rest} = props;\n\n const cols = Math.max(1, Math.floor(columns));\n const widthPct = `${100 / cols}%`;\n const half = gap / 2;\n\n return (\n <Box\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n marginLeft={-half}\n marginRight={-half}\n {...(rest as any)}\n {...(testID ? ({testID, \"data-testid\": testID} as any) : {})}\n >\n {items.map((item, i) => (\n <Box key={i} width={widthPct} paddingLeft={half} paddingRight={half} paddingBottom={gap}>\n {/* image card */}\n <Box\n position=\"relative\"\n width=\"100%\"\n aspectRatio={aspectRatio}\n borderRadius={radius}\n overflow=\"hidden\"\n bgColor=\"gray.100\"\n borderWidth={item.selected ? 2 : 0}\n borderColor={item.selected ? \"brand\" : \"transparent\"}\n >\n {item.src != null && (\n /*\n * ⛔ `alt` WAS MISSING, SO EVERY TILE WITH AN IMAGE\n * WARNED — the same omission the Avatar carried, found\n * beside it on 5 October 2026. gluestack's image creator\n * does `if (typeof alt !== 'string') console.warn('Please\n * pass alt prop to Image component')`, so the check is on\n * the TYPE: a missing prop warns, an empty string does\n * not.\n *\n * ⛔ AND `IGalleryItem.alt` HAS EXISTED ALL ALONG,\n * published and documented as \"Accessibility label\", and\n * this component read it NOWHERE. A caller could set it,\n * tsc accepted it, and it reached nothing — so the fix is\n * to honour the contract that was already written down,\n * not to invent one.\n *\n * ⛔ `aria-hidden` IS CONDITIONAL HERE, WHERE THE\n * AVATAR'S IS UNCONDITIONAL — the two structures differ.\n * The Avatar's image sits inside a Box that already\n * carries `role=\"img\"` and `aria-label`, so it is ALWAYS\n * redundant. This tile's Box carries no role and no label\n * at all, so when a caller HAS described the image it is\n * the only thing that can say so and it must stay in the\n * tree.\n *\n * ⛔ AND WITH NO DESCRIPTION IT IS HIDDEN, NOT EXPOSED\n * EMPTY — found by Codex, 5 October 2026, and the first\n * draft of this fix had it backwards. gluestack forces\n * `role={props.role || 'img'}` whatever `alt` says, so\n * `alt=\"\"` alone leaves an EXPOSED, NAMELESS image node on\n * React Native. \"No labelled ancestor\" was the wrong\n * reason to leave it exposed: an image with no name cannot\n * speak for the tile either way.\n *\n * ⛔ AND IT DOES NOT FALL BACK TO `title`. The draft\n * did, and `title` is ALREADY rendered as a Text sibling a\n * few lines below — a screen reader would have read it\n * twice: \"image, Rewire\" then \"Rewire\". The caption\n * announces itself; the image either adds something the\n * caller wrote in `alt`, or it adds nothing and gets out\n * of the way.\n *\n * ⚠️ `alt=\"\"` STAYS IN BOTH BRANCHES because it is what\n * silences the warning: gluestack tests\n * `typeof alt !== 'string'`, so a missing prop warns and\n * an empty string does not.\n */\n <Image\n src={item.src}\n alt={item.alt ?? \"\"}\n aria-hidden={!item.alt}\n style={{width: \"100%\", height: \"100%\"} as any}\n objectFit=\"cover\"\n />\n )}\n\n {item.topLeft != null && (\n <Box position=\"absolute\" top={6} left={6}>\n {item.topLeft}\n </Box>\n )}\n {item.bottomRight != null && (\n <Box position=\"absolute\" bottom={6} right={6}>\n {item.bottomRight}\n </Box>\n )}\n {item.selected && (\n <Box\n position=\"absolute\"\n top={6}\n right={6}\n width={22}\n height={22}\n borderRadius={999}\n bgColor=\"brand\"\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={2}\n borderColor=\"#FFFFFF\"\n >\n <Icon name=\"check\" size={12} color=\"#FFFFFF\" />\n </Box>\n )}\n </Box>\n\n {(item.title != null || item.meta != null) && (\n <Box paddingTop={6} gap={2}>\n {item.title != null && (\n <Text fontSize={13} fontWeight=\"700\" color=\"text\">\n {item.title}\n </Text>\n )}\n {item.meta != null && (\n <Text fontSize={11} color=\"text-secondary\">\n {item.meta}\n </Text>\n )}\n </Box>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n\nexport default Gallery;\n"],"names":[],"mappings":"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"}
@@ -144,7 +144,8 @@ const FormRenderer = ({
144
144
  getValues,
145
145
  focusedField,
146
146
  setFocusedField,
147
- fieldLogic: props
147
+ fieldLogic: props,
148
+ rowGap
148
149
  }
149
150
  )
150
151
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../../src/form-renderer/components/FormRenderer/index.native.tsx"],"sourcesContent":["// components/FormRenderer.tsx\n\nimport React, {useCallback, useEffect, useMemo, useState} from 'react';\nimport type {Control, FieldValues, UseFormGetValues} from 'react-hook-form';\nimport type {GroupFieldConfigs, IFormElement} from '@lmb-it/kitsconcerto-types';\nimport {FieldWrapper} from '../layout/FieldWrapper';\nimport {KeyboardNavContext, useKeyboardNavProvider} from '../../../hooks/useKeyboardNavigation';\n\n// Import all your \"dumb\" field components\nimport {\n Checkboxes,\n ColorPicker,\n CombinedElement,\n ContainerElement,\n DateField,\n FileUploader,\n Group,\n InputNumber,\n InputText,\n ListBox,\n Location,\n NormalSelect,\n ObjectElement,\n Password,\n Phone,\n RadiusPicker,\n Radios,\n Select,\n Switch,\n TagsSelect,\n Textarea,\n TreeSelect\n} from '../fields';\nimport {Grid, GridItem} from \"../../../_compat\";\nimport type {IStyleClasses} from \"@lmb-it/kitsconcerto-types\";\n\n// ... import all other field components\n\n/**\n * ⛔ THE NUMBER THIS RENDERER HAS ALWAYS USED. It is named rather than written\n * twice so the default and the documentation cannot drift apart; a caller that\n * passes no `rowGap` gets exactly what it got before this prop existed.\n */\nconst DEFAULT_ROW_GAP = 6;\n\ninterface FormRendererProps<T extends FieldValues> extends GroupFieldConfigs<T> {\n elements: IFormElement<T>[];\n control: Control<T>;\n getValues: UseFormGetValues<T>;\n parentPath?: string; // ✨ Pass down the parent path for nesting\n grid?: IStyleClasses;\n /**\n * Vertical space between rows, in points. Omitted, the 6 this renderer has\n * always used — see `IFormComponent.rowGap` for why it is a named prop and\n * not the existing `grid` bag.\n */\n rowGap?: number;\n focusedField?: string;\n setFocusedField: React.Dispatch<React.SetStateAction<string>>;\n}\n\nexport const FormRenderer = <T extends FieldValues>({\n elements,\n control,\n getValues,\n groupField,\n grid = {},\n rowGap = DEFAULT_ROW_GAP,\n parentPath = '',\n focusedField, setFocusedField\n }: FormRendererProps<T>) => {\n\n const [gridItemProps, setGridItemProps] = useState<Record<string, string>>({})\n\n // --- Keyboard navigation ---\n const TEXT_TYPES = ['Text', 'Email', 'Number', 'Password', 'Phone', 'Textarea'];\n const textFieldIds = useMemo(() => {\n return elements\n .filter((el) => TEXT_TYPES.includes(el.type))\n .map((el) => `${parentPath}${el.id as string}`);\n }, [elements, parentPath]);\n\n const keyboardNavCtx = useKeyboardNavProvider();\n\n useEffect(() => {\n keyboardNavCtx.setElementsOrder(textFieldIds);\n }, [textFieldIds]);\n\n const getElement = useCallback((elementType: string) => {\n\n switch (elementType) {\n\n case 'Email':\n case 'Text':\n return InputText;\n case 'Number':\n return InputNumber;\n case 'Password':\n return Password;\n case 'Location':\n return Location;\n case 'Phone':\n return Phone\n case 'Date':\n return DateField\n case 'ColorPicker':\n return ColorPicker\n case 'RadiusPicker':\n return RadiusPicker\n case 'Radios':\n return Radios\n case 'Textarea':\n return Textarea\n case 'Image':\n case 'File':\n return FileUploader\n case 'Switch':\n return Switch\n case 'Checkbox':\n return Checkboxes\n case 'Select':\n return NormalSelect\n case 'TreeSelect':\n return TreeSelect\n case 'Tags':\n return TagsSelect\n case 'ListBox':\n return ListBox\n case 'Multiselect':\n return Select\n case 'Group':\n return Group\n case 'Object':\n return ObjectElement\n case 'Container':\n return ContainerElement\n case 'Combined':\n return CombinedElement\n // default:\n // return <Flex/>;\n }\n }, []);\n\n\n return (\n <KeyboardNavContext.Provider value={keyboardNavCtx}>\n <Grid\n columns={12}\n rowGap={rowGap}\n columnGap={8}\n >\n {elements.map((element) => {\n\n const FieldComponent = getElement(element.type);\n\n const elementId = `${parentPath}${element.id as string}`;\n const resolvedElement = elementId !== element.id ? { ...element, id: elementId } : element;\n\n\n if (!FieldComponent) {\n console.warn(`No component found for element type: \"${resolvedElement.type}\"`);\n return null;\n }\n\n const colSpan = parseInt(gridItemProps[elementId] ?? '12', 10);\n\n return (\n <GridItem\n key={elementId}\n id={elementId}\n colSpan={colSpan}\n /* The cell's own style — the stacking level an\n element config cannot otherwise reach. Sizing keys\n are forbidden by the type. */\n style={(resolvedElement as any).cellStyle}\n >\n <FieldWrapper\n key={elementId}\n element={resolvedElement}\n control={control}\n grid={grid}\n groupField={groupField}\n getValues={getValues}\n focusedField={focusedField}\n setFocusedField={setFocusedField}\n onGridChange={(value) => {\n if (value != gridItemProps[elementId]) {\n setGridItemProps(prevState => ({\n ...prevState,\n [elementId]: value\n }));\n }\n }}\n\n\n >\n {(props) => <FieldComponent\n groupField={groupField}\n element={resolvedElement}\n control={control}\n getValues={getValues}\n focusedField={focusedField}\n setFocusedField={setFocusedField}\n fieldLogic={props}\n\n />}\n </FieldWrapper>\n </GridItem>\n );\n })}\n </Grid>\n </KeyboardNavContext.Provider>\n );\n};\n"],"names":["useState","useMemo","useKeyboardNavProvider","useEffect","useCallback","InputText","InputNumber","Password","Location","Phone","DateField","ColorPicker","RadiusPicker","Radios","Textarea","FileUploader","Switch","Checkboxes","NormalSelect","TreeSelect","TagsSelect","ListBox","Select","Group","ObjectElement","ContainerElement","CombinedElement","jsx","KeyboardNavContext","Grid","GridItem","FieldWrapper"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../../src/form-renderer/components/FormRenderer/index.native.tsx"],"sourcesContent":["// components/FormRenderer.tsx\n\nimport React, {useCallback, useEffect, useMemo, useState} from 'react';\nimport type {Control, FieldValues, UseFormGetValues} from 'react-hook-form';\nimport type {GroupFieldConfigs, IFormElement} from '@lmb-it/kitsconcerto-types';\nimport {FieldWrapper} from '../layout/FieldWrapper';\nimport {KeyboardNavContext, useKeyboardNavProvider} from '../../../hooks/useKeyboardNavigation';\n\n// Import all your \"dumb\" field components\nimport {\n Checkboxes,\n ColorPicker,\n CombinedElement,\n ContainerElement,\n DateField,\n FileUploader,\n Group,\n InputNumber,\n InputText,\n ListBox,\n Location,\n NormalSelect,\n ObjectElement,\n Password,\n Phone,\n RadiusPicker,\n Radios,\n Select,\n Switch,\n TagsSelect,\n Textarea,\n TreeSelect\n} from '../fields';\nimport {Grid, GridItem} from \"../../../_compat\";\nimport type {IStyleClasses} from \"@lmb-it/kitsconcerto-types\";\n\n// ... import all other field components\n\n/**\n * ⛔ THE NUMBER THIS RENDERER HAS ALWAYS USED. It is named rather than written\n * twice so the default and the documentation cannot drift apart; a caller that\n * passes no `rowGap` gets exactly what it got before this prop existed.\n */\nconst DEFAULT_ROW_GAP = 6;\n\ninterface FormRendererProps<T extends FieldValues> extends GroupFieldConfigs<T> {\n elements: IFormElement<T>[];\n control: Control<T>;\n getValues: UseFormGetValues<T>;\n parentPath?: string; // ✨ Pass down the parent path for nesting\n grid?: IStyleClasses;\n /**\n * Vertical space between rows, in points. Omitted, the 6 this renderer has\n * always used — see `IFormComponent.rowGap` for why it is a named prop and\n * not the existing `grid` bag.\n */\n rowGap?: number;\n focusedField?: string;\n setFocusedField: React.Dispatch<React.SetStateAction<string>>;\n}\n\nexport const FormRenderer = <T extends FieldValues>({\n elements,\n control,\n getValues,\n groupField,\n grid = {},\n rowGap = DEFAULT_ROW_GAP,\n parentPath = '',\n focusedField, setFocusedField\n }: FormRendererProps<T>) => {\n\n const [gridItemProps, setGridItemProps] = useState<Record<string, string>>({})\n\n // --- Keyboard navigation ---\n const TEXT_TYPES = ['Text', 'Email', 'Number', 'Password', 'Phone', 'Textarea'];\n const textFieldIds = useMemo(() => {\n return elements\n .filter((el) => TEXT_TYPES.includes(el.type))\n .map((el) => `${parentPath}${el.id as string}`);\n }, [elements, parentPath]);\n\n const keyboardNavCtx = useKeyboardNavProvider();\n\n useEffect(() => {\n keyboardNavCtx.setElementsOrder(textFieldIds);\n }, [textFieldIds]);\n\n const getElement = useCallback((elementType: string) => {\n\n switch (elementType) {\n\n case 'Email':\n case 'Text':\n return InputText;\n case 'Number':\n return InputNumber;\n case 'Password':\n return Password;\n case 'Location':\n return Location;\n case 'Phone':\n return Phone\n case 'Date':\n return DateField\n case 'ColorPicker':\n return ColorPicker\n case 'RadiusPicker':\n return RadiusPicker\n case 'Radios':\n return Radios\n case 'Textarea':\n return Textarea\n case 'Image':\n case 'File':\n return FileUploader\n case 'Switch':\n return Switch\n case 'Checkbox':\n return Checkboxes\n case 'Select':\n return NormalSelect\n case 'TreeSelect':\n return TreeSelect\n case 'Tags':\n return TagsSelect\n case 'ListBox':\n return ListBox\n case 'Multiselect':\n return Select\n case 'Group':\n return Group\n case 'Object':\n return ObjectElement\n case 'Container':\n return ContainerElement\n case 'Combined':\n return CombinedElement\n // default:\n // return <Flex/>;\n }\n }, []);\n\n\n return (\n <KeyboardNavContext.Provider value={keyboardNavCtx}>\n <Grid\n columns={12}\n rowGap={rowGap}\n columnGap={8}\n >\n {elements.map((element) => {\n\n const FieldComponent = getElement(element.type);\n\n const elementId = `${parentPath}${element.id as string}`;\n const resolvedElement = elementId !== element.id ? { ...element, id: elementId } : element;\n\n\n if (!FieldComponent) {\n console.warn(`No component found for element type: \"${resolvedElement.type}\"`);\n return null;\n }\n\n const colSpan = parseInt(gridItemProps[elementId] ?? '12', 10);\n\n return (\n <GridItem\n key={elementId}\n id={elementId}\n colSpan={colSpan}\n /* The cell's own style — the stacking level an\n element config cannot otherwise reach. Sizing keys\n are forbidden by the type. */\n style={(resolvedElement as any).cellStyle}\n >\n <FieldWrapper\n key={elementId}\n element={resolvedElement}\n control={control}\n grid={grid}\n groupField={groupField}\n getValues={getValues}\n focusedField={focusedField}\n setFocusedField={setFocusedField}\n onGridChange={(value) => {\n if (value != gridItemProps[elementId]) {\n setGridItemProps(prevState => ({\n ...prevState,\n [elementId]: value\n }));\n }\n }}\n\n\n >\n {(props) => <FieldComponent\n groupField={groupField}\n element={resolvedElement}\n control={control}\n getValues={getValues}\n focusedField={focusedField}\n setFocusedField={setFocusedField}\n fieldLogic={props}\n /* ⛔ SO THE NESTING CAN CARRY IT DOWN. `Object`\n and `Group` each render their children through\n a NESTED FormRenderer; a renderer handed\n nothing falls back to its own default, which\n stopped the form's `rowGap` at the top level.\n Every other field type ignores this prop. */\n rowGap={rowGap}\n\n />}\n </FieldWrapper>\n </GridItem>\n );\n })}\n </Grid>\n </KeyboardNavContext.Provider>\n );\n};\n"],"names":["useState","useMemo","useKeyboardNavProvider","useEffect","useCallback","InputText","InputNumber","Password","Location","Phone","DateField","ColorPicker","RadiusPicker","Radios","Textarea","FileUploader","Switch","Checkboxes","NormalSelect","TreeSelect","TagsSelect","ListBox","Select","Group","ObjectElement","ContainerElement","CombinedElement","jsx","KeyboardNavContext","Grid","GridItem","FieldWrapper"],"mappings":"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"}
@@ -142,7 +142,8 @@ const FormRenderer = ({
142
142
  getValues,
143
143
  focusedField,
144
144
  setFocusedField,
145
- fieldLogic: props
145
+ fieldLogic: props,
146
+ rowGap
146
147
  }
147
148
  )
148
149
  },