@lmb-it/kitsconcerto-native 0.1.253 → 0.1.255

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.
@@ -83,6 +83,7 @@ require('../../apps/mobile/src/Core/RadioButton/index.cjs');
83
83
  require('../../apps/mobile/src/Core/Input/index.cjs');
84
84
  require('../../apps/mobile/src/Core/Input/Field.cjs');
85
85
  require('../../apps/mobile/src/Core/Input/Slot.cjs');
86
+ var useRtlLineHeight = require('../../hooks/useRtlLineHeight.cjs');
86
87
  var index$1 = require('../../primitives/Box/index.cjs');
87
88
  var index_native = require('../../primitives/Icon/index.cjs');
88
89
  var index = require('../../layout/Flex/index.cjs');
@@ -148,6 +149,7 @@ function BottomTabBar(rawProps) {
148
149
  showLabels = true,
149
150
  testID
150
151
  } = props;
152
+ const rtlLineHeight = useRtlLineHeight.default(void 0, 11);
151
153
  const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
152
154
  const activeC = style.resolveThemeTokenForNative(activeColor);
153
155
  const inactiveC = style.resolveThemeTokenForNative(inactiveColor);
@@ -231,7 +233,7 @@ function BottomTabBar(rawProps) {
231
233
  /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: iconName, size: 24, color }),
232
234
  item.badge != null && item.badge !== false && /* @__PURE__ */ jsxRuntime.jsx(Badge, { badge: item.badge, barBg: bg, dangerColor: danger })
233
235
  ] }),
234
- showLabels && item.label != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: { fontSize: 11, fontWeight: "500", color }, children: item.label })
236
+ showLabels && item.label != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: { fontSize: 11, fontWeight: "500", color, ...rtlLineHeight != null ? { lineHeight: rtlLineHeight } : {} }, children: item.label })
235
237
  ]
236
238
  },
237
239
  item.key
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/components/BottomTabBar/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {Pressable, Text, View} from \"react-native\";\nimport {useSafeAreaInsets} from \"react-native-safe-area-context\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport type {IBottomTabBarProps, ITabBarItem} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {Box} from \"../../primitives\";\nimport Flex from \"../../layout/Flex\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n// Mirrors the app's AMTabBar geometry (App/src/components/shared/layout).\nconst BAR_MIN_HEIGHT = 60;\nconst FAB_SIZE = 56;\n\nfunction Badge({badge, barBg, dangerColor}: {badge: number | boolean; barBg: string; dangerColor: string}) {\n const isDot = badge === true || badge === 0;\n if (isDot) {\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -2,\n right: -2,\n width: 10,\n height: 10,\n borderRadius: 5,\n backgroundColor: dangerColor,\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n />\n );\n }\n const count = typeof badge === \"number\" ? (badge > 99 ? \"99+\" : String(badge)) : \"\";\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -6,\n right: -10,\n minWidth: 16,\n height: 16,\n borderRadius: 8,\n paddingHorizontal: 4,\n backgroundColor: dangerColor,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n >\n <Text style={{color: \"#FFFFFF\", fontSize: 9, fontWeight: \"700\", lineHeight: 11}}>{count}</Text>\n </Box>\n );\n}\n\n/**\n * BottomTabBar — iOS/Android bottom navigation with an elevated center FAB.\n *\n * Layout mirrors the app's working `AMTabBar`: a `row` / `space-between` bar\n * with four `flex:1` **column** slots and a content-width centre slot holding\n * the FAB. The FAB is lifted with `marginTop:-26` and ringed with a 3px\n * border so it sits above the bar plane — never absolutely positioned.\n * Presentational + controlled (router-agnostic).\n */\nexport function BottomTabBar<K extends string = string>(rawProps: IBottomTabBarProps<K>) {\n const props = useComponentDefaults<IBottomTabBarProps<K>>(\"BottomTabBar\", rawProps).mergedProps;\n const {\n items,\n value,\n onChange,\n activeColor = \"brand\",\n inactiveColor = \"text-secondary\",\n background = \"surface-card\",\n fabColor,\n showLabels = true,\n testID,\n } = props;\n\n const insets = useSafeAreaInsets();\n\n // Resolve tokens → hex with the standalone resolver (the one `Icon` uses),\n // so colors apply on raw RN elements in both app and Storybook contexts.\n const activeC = resolveThemeTokenForNative(activeColor);\n const inactiveC = resolveThemeTokenForNative(inactiveColor);\n const bg = resolveThemeTokenForNative(background);\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fabBg = resolveThemeTokenForNative(fabColor ?? activeColor);\n const danger = resolveThemeTokenForNative(\"danger\");\n\n return (\n <View\n testID={testID}\n style={{\n width: \"100%\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n minHeight: BAR_MIN_HEIGHT,\n paddingHorizontal: 20,\n paddingTop: 10,\n paddingBottom: Math.max(insets.bottom, 12),\n backgroundColor: bg,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {items.map((item: ITabBarItem<K>) => {\n const active = item.key === value;\n\n // Centre FAB — a content-width slot (NOT flex:1) so the four\n // side slots distribute evenly; the FAB is lifted in-flow.\n if (item.fab) {\n return (\n <Flex key={item.key} style={{alignItems: \"center\", justifyContent: \"center\"}}>\n <Pressable\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n hitSlop={8}\n style={({pressed}) => [\n {\n width: FAB_SIZE,\n height: FAB_SIZE,\n borderRadius: FAB_SIZE / 2,\n marginTop: -26,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: fabBg,\n borderWidth: 3,\n borderColor: bg,\n shadowColor: fabBg,\n shadowOffset: {width: 0, height: 6},\n shadowOpacity: 0.35,\n shadowRadius: 12,\n elevation: 8,\n },\n pressed && {opacity: 0.9},\n ]}\n >\n <Icon name={item.icon as never} size={26} color=\"#FFFFFF\" />\n </Pressable>\n </Flex>\n );\n }\n\n const color = active ? activeC : inactiveC;\n const iconName = active && item.activeIcon != null ? item.activeIcon : item.icon;\n\n return (\n <Pressable\n key={item.key}\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n style={({pressed}) => [\n {\n flex: 1,\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 4,\n paddingVertical: 6,\n paddingHorizontal: 4,\n minHeight: 44,\n },\n pressed && {opacity: 0.7},\n ]}\n >\n <View style={{alignItems: \"center\", justifyContent: \"center\", position: \"relative\"}}>\n <Icon name={iconName as never} size={24} color={color} />\n {item.badge != null && item.badge !== false && (\n <Badge badge={item.badge} barBg={bg} dangerColor={danger} />\n )}\n </View>\n {showLabels && item.label != null && (\n <Text numberOfLines={1} style={{fontSize: 11, fontWeight: \"500\", color}}>\n {item.label}\n </Text>\n )}\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nexport default BottomTabBar;\n"],"names":["jsx","Box","Text","useComponentDefaults","useSafeAreaInsets","resolveThemeTokenForNative","View","Flex","Pressable","Icon","jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/BottomTabBar/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {Pressable, Text, View} from \"react-native\";\nimport {useSafeAreaInsets} from \"react-native-safe-area-context\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\nimport type {IBottomTabBarProps, ITabBarItem} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {Box} from \"../../primitives\";\nimport Flex from \"../../layout/Flex\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n// Mirrors the app's AMTabBar geometry (App/src/components/shared/layout).\nconst BAR_MIN_HEIGHT = 60;\nconst FAB_SIZE = 56;\n\nfunction Badge({badge, barBg, dangerColor}: {badge: number | boolean; barBg: string; dangerColor: string}) {\n const isDot = badge === true || badge === 0;\n if (isDot) {\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -2,\n right: -2,\n width: 10,\n height: 10,\n borderRadius: 5,\n backgroundColor: dangerColor,\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n />\n );\n }\n const count = typeof badge === \"number\" ? (badge > 99 ? \"99+\" : String(badge)) : \"\";\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -6,\n right: -10,\n minWidth: 16,\n height: 16,\n borderRadius: 8,\n paddingHorizontal: 4,\n backgroundColor: dangerColor,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n >\n <Text style={{color: \"#FFFFFF\", fontSize: 9, fontWeight: \"700\", lineHeight: 11}}>{count}</Text>\n </Box>\n );\n}\n\n/**\n * BottomTabBar — iOS/Android bottom navigation with an elevated center FAB.\n *\n * Layout mirrors the app's working `AMTabBar`: a `row` / `space-between` bar\n * with four `flex:1` **column** slots and a content-width centre slot holding\n * the FAB. The FAB is lifted with `marginTop:-26` and ringed with a 3px\n * border so it sits above the bar plane — never absolutely positioned.\n * Presentational + controlled (router-agnostic).\n */\nexport function BottomTabBar<K extends string = string>(rawProps: IBottomTabBarProps<K>) {\n const props = useComponentDefaults<IBottomTabBarProps<K>>(\"BottomTabBar\", rawProps).mergedProps;\n const {\n items,\n value,\n onChange,\n activeColor = \"brand\",\n inactiveColor = \"text-secondary\",\n background = \"surface-card\",\n fabColor,\n showLabels = true,\n testID,\n } = props;\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX — a raw react-native <Text>\n * with a fontSize and no lineHeight gets the FONT'S natural box, and\n * NotoKufiArabic's is too short for its own ascenders. In an RTL script\n * that reads as a BAD TRANSLATION rather than as clipping: ش/س, غ/ع and\n * ة/ه differ only by marks above the baseline.\n * ⚠️ undefined in LTR, so no English layout moves.\n */\n const rtlLineHeight = useRtlLineHeight(undefined, 11);\n\n const insets = useSafeAreaInsets();\n\n // Resolve tokens → hex with the standalone resolver (the one `Icon` uses),\n // so colors apply on raw RN elements in both app and Storybook contexts.\n const activeC = resolveThemeTokenForNative(activeColor);\n const inactiveC = resolveThemeTokenForNative(inactiveColor);\n const bg = resolveThemeTokenForNative(background);\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fabBg = resolveThemeTokenForNative(fabColor ?? activeColor);\n const danger = resolveThemeTokenForNative(\"danger\");\n\n return (\n <View\n testID={testID}\n style={{\n width: \"100%\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n minHeight: BAR_MIN_HEIGHT,\n paddingHorizontal: 20,\n paddingTop: 10,\n paddingBottom: Math.max(insets.bottom, 12),\n backgroundColor: bg,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {items.map((item: ITabBarItem<K>) => {\n const active = item.key === value;\n\n // Centre FAB — a content-width slot (NOT flex:1) so the four\n // side slots distribute evenly; the FAB is lifted in-flow.\n if (item.fab) {\n return (\n <Flex key={item.key} style={{alignItems: \"center\", justifyContent: \"center\"}}>\n <Pressable\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n hitSlop={8}\n style={({pressed}) => [\n {\n width: FAB_SIZE,\n height: FAB_SIZE,\n borderRadius: FAB_SIZE / 2,\n marginTop: -26,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: fabBg,\n borderWidth: 3,\n borderColor: bg,\n shadowColor: fabBg,\n shadowOffset: {width: 0, height: 6},\n shadowOpacity: 0.35,\n shadowRadius: 12,\n elevation: 8,\n },\n pressed && {opacity: 0.9},\n ]}\n >\n <Icon name={item.icon as never} size={26} color=\"#FFFFFF\" />\n </Pressable>\n </Flex>\n );\n }\n\n const color = active ? activeC : inactiveC;\n const iconName = active && item.activeIcon != null ? item.activeIcon : item.icon;\n\n return (\n <Pressable\n key={item.key}\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n style={({pressed}) => [\n {\n flex: 1,\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 4,\n paddingVertical: 6,\n paddingHorizontal: 4,\n minHeight: 44,\n },\n pressed && {opacity: 0.7},\n ]}\n >\n <View style={{alignItems: \"center\", justifyContent: \"center\", position: \"relative\"}}>\n <Icon name={iconName as never} size={24} color={color} />\n {item.badge != null && item.badge !== false && (\n <Badge badge={item.badge} barBg={bg} dangerColor={danger} />\n )}\n </View>\n {showLabels && item.label != null && (\n <Text numberOfLines={1} style={{fontSize: 11, fontWeight: \"500\", color, ...(rtlLineHeight != null ? {lineHeight: rtlLineHeight} : {})}}>\n {item.label}\n </Text>\n )}\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nexport default BottomTabBar;\n"],"names":["jsx","Box","Text","useComponentDefaults","useRtlLineHeight","useSafeAreaInsets","resolveThemeTokenForNative","View","Flex","Pressable","Icon","jsxs"],"mappings":"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"}
@@ -81,6 +81,7 @@ import '../../apps/mobile/src/Core/RadioButton/index.js';
81
81
  import '../../apps/mobile/src/Core/Input/index.js';
82
82
  import '../../apps/mobile/src/Core/Input/Field.js';
83
83
  import '../../apps/mobile/src/Core/Input/Slot.js';
84
+ import useRtlLineHeight from '../../hooks/useRtlLineHeight.js';
84
85
  import Box from '../../primitives/Box/index.js';
85
86
  import { Icon } from '../../primitives/Icon/index.js';
86
87
  import Flex from '../../layout/Flex/index.js';
@@ -146,6 +147,7 @@ function BottomTabBar(rawProps) {
146
147
  showLabels = true,
147
148
  testID
148
149
  } = props;
150
+ const rtlLineHeight = useRtlLineHeight(void 0, 11);
149
151
  const insets = useSafeAreaInsets();
150
152
  const activeC = resolveThemeTokenForNative(activeColor);
151
153
  const inactiveC = resolveThemeTokenForNative(inactiveColor);
@@ -229,7 +231,7 @@ function BottomTabBar(rawProps) {
229
231
  /* @__PURE__ */ jsx(Icon, { name: iconName, size: 24, color }),
230
232
  item.badge != null && item.badge !== false && /* @__PURE__ */ jsx(Badge, { badge: item.badge, barBg: bg, dangerColor: danger })
231
233
  ] }),
232
- showLabels && item.label != null && /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: { fontSize: 11, fontWeight: "500", color }, children: item.label })
234
+ showLabels && item.label != null && /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: { fontSize: 11, fontWeight: "500", color, ...rtlLineHeight != null ? { lineHeight: rtlLineHeight } : {} }, children: item.label })
233
235
  ]
234
236
  },
235
237
  item.key
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/BottomTabBar/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {Pressable, Text, View} from \"react-native\";\nimport {useSafeAreaInsets} from \"react-native-safe-area-context\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport type {IBottomTabBarProps, ITabBarItem} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {Box} from \"../../primitives\";\nimport Flex from \"../../layout/Flex\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n// Mirrors the app's AMTabBar geometry (App/src/components/shared/layout).\nconst BAR_MIN_HEIGHT = 60;\nconst FAB_SIZE = 56;\n\nfunction Badge({badge, barBg, dangerColor}: {badge: number | boolean; barBg: string; dangerColor: string}) {\n const isDot = badge === true || badge === 0;\n if (isDot) {\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -2,\n right: -2,\n width: 10,\n height: 10,\n borderRadius: 5,\n backgroundColor: dangerColor,\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n />\n );\n }\n const count = typeof badge === \"number\" ? (badge > 99 ? \"99+\" : String(badge)) : \"\";\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -6,\n right: -10,\n minWidth: 16,\n height: 16,\n borderRadius: 8,\n paddingHorizontal: 4,\n backgroundColor: dangerColor,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n >\n <Text style={{color: \"#FFFFFF\", fontSize: 9, fontWeight: \"700\", lineHeight: 11}}>{count}</Text>\n </Box>\n );\n}\n\n/**\n * BottomTabBar — iOS/Android bottom navigation with an elevated center FAB.\n *\n * Layout mirrors the app's working `AMTabBar`: a `row` / `space-between` bar\n * with four `flex:1` **column** slots and a content-width centre slot holding\n * the FAB. The FAB is lifted with `marginTop:-26` and ringed with a 3px\n * border so it sits above the bar plane — never absolutely positioned.\n * Presentational + controlled (router-agnostic).\n */\nexport function BottomTabBar<K extends string = string>(rawProps: IBottomTabBarProps<K>) {\n const props = useComponentDefaults<IBottomTabBarProps<K>>(\"BottomTabBar\", rawProps).mergedProps;\n const {\n items,\n value,\n onChange,\n activeColor = \"brand\",\n inactiveColor = \"text-secondary\",\n background = \"surface-card\",\n fabColor,\n showLabels = true,\n testID,\n } = props;\n\n const insets = useSafeAreaInsets();\n\n // Resolve tokens → hex with the standalone resolver (the one `Icon` uses),\n // so colors apply on raw RN elements in both app and Storybook contexts.\n const activeC = resolveThemeTokenForNative(activeColor);\n const inactiveC = resolveThemeTokenForNative(inactiveColor);\n const bg = resolveThemeTokenForNative(background);\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fabBg = resolveThemeTokenForNative(fabColor ?? activeColor);\n const danger = resolveThemeTokenForNative(\"danger\");\n\n return (\n <View\n testID={testID}\n style={{\n width: \"100%\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n minHeight: BAR_MIN_HEIGHT,\n paddingHorizontal: 20,\n paddingTop: 10,\n paddingBottom: Math.max(insets.bottom, 12),\n backgroundColor: bg,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {items.map((item: ITabBarItem<K>) => {\n const active = item.key === value;\n\n // Centre FAB — a content-width slot (NOT flex:1) so the four\n // side slots distribute evenly; the FAB is lifted in-flow.\n if (item.fab) {\n return (\n <Flex key={item.key} style={{alignItems: \"center\", justifyContent: \"center\"}}>\n <Pressable\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n hitSlop={8}\n style={({pressed}) => [\n {\n width: FAB_SIZE,\n height: FAB_SIZE,\n borderRadius: FAB_SIZE / 2,\n marginTop: -26,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: fabBg,\n borderWidth: 3,\n borderColor: bg,\n shadowColor: fabBg,\n shadowOffset: {width: 0, height: 6},\n shadowOpacity: 0.35,\n shadowRadius: 12,\n elevation: 8,\n },\n pressed && {opacity: 0.9},\n ]}\n >\n <Icon name={item.icon as never} size={26} color=\"#FFFFFF\" />\n </Pressable>\n </Flex>\n );\n }\n\n const color = active ? activeC : inactiveC;\n const iconName = active && item.activeIcon != null ? item.activeIcon : item.icon;\n\n return (\n <Pressable\n key={item.key}\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n style={({pressed}) => [\n {\n flex: 1,\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 4,\n paddingVertical: 6,\n paddingHorizontal: 4,\n minHeight: 44,\n },\n pressed && {opacity: 0.7},\n ]}\n >\n <View style={{alignItems: \"center\", justifyContent: \"center\", position: \"relative\"}}>\n <Icon name={iconName as never} size={24} color={color} />\n {item.badge != null && item.badge !== false && (\n <Badge badge={item.badge} barBg={bg} dangerColor={danger} />\n )}\n </View>\n {showLabels && item.label != null && (\n <Text numberOfLines={1} style={{fontSize: 11, fontWeight: \"500\", color}}>\n {item.label}\n </Text>\n )}\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nexport default BottomTabBar;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/BottomTabBar/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {Pressable, Text, View} from \"react-native\";\nimport {useSafeAreaInsets} from \"react-native-safe-area-context\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\nimport type {IBottomTabBarProps, ITabBarItem} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {Box} from \"../../primitives\";\nimport Flex from \"../../layout/Flex\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n// Mirrors the app's AMTabBar geometry (App/src/components/shared/layout).\nconst BAR_MIN_HEIGHT = 60;\nconst FAB_SIZE = 56;\n\nfunction Badge({badge, barBg, dangerColor}: {badge: number | boolean; barBg: string; dangerColor: string}) {\n const isDot = badge === true || badge === 0;\n if (isDot) {\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -2,\n right: -2,\n width: 10,\n height: 10,\n borderRadius: 5,\n backgroundColor: dangerColor,\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n />\n );\n }\n const count = typeof badge === \"number\" ? (badge > 99 ? \"99+\" : String(badge)) : \"\";\n return (\n <Box\n style={{\n position: \"absolute\",\n top: -6,\n right: -10,\n minWidth: 16,\n height: 16,\n borderRadius: 8,\n paddingHorizontal: 4,\n backgroundColor: dangerColor,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderWidth: 1.5,\n borderColor: barBg,\n zIndex: 10,\n }}\n >\n <Text style={{color: \"#FFFFFF\", fontSize: 9, fontWeight: \"700\", lineHeight: 11}}>{count}</Text>\n </Box>\n );\n}\n\n/**\n * BottomTabBar — iOS/Android bottom navigation with an elevated center FAB.\n *\n * Layout mirrors the app's working `AMTabBar`: a `row` / `space-between` bar\n * with four `flex:1` **column** slots and a content-width centre slot holding\n * the FAB. The FAB is lifted with `marginTop:-26` and ringed with a 3px\n * border so it sits above the bar plane — never absolutely positioned.\n * Presentational + controlled (router-agnostic).\n */\nexport function BottomTabBar<K extends string = string>(rawProps: IBottomTabBarProps<K>) {\n const props = useComponentDefaults<IBottomTabBarProps<K>>(\"BottomTabBar\", rawProps).mergedProps;\n const {\n items,\n value,\n onChange,\n activeColor = \"brand\",\n inactiveColor = \"text-secondary\",\n background = \"surface-card\",\n fabColor,\n showLabels = true,\n testID,\n } = props;\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX — a raw react-native <Text>\n * with a fontSize and no lineHeight gets the FONT'S natural box, and\n * NotoKufiArabic's is too short for its own ascenders. In an RTL script\n * that reads as a BAD TRANSLATION rather than as clipping: ش/س, غ/ع and\n * ة/ه differ only by marks above the baseline.\n * ⚠️ undefined in LTR, so no English layout moves.\n */\n const rtlLineHeight = useRtlLineHeight(undefined, 11);\n\n const insets = useSafeAreaInsets();\n\n // Resolve tokens → hex with the standalone resolver (the one `Icon` uses),\n // so colors apply on raw RN elements in both app and Storybook contexts.\n const activeC = resolveThemeTokenForNative(activeColor);\n const inactiveC = resolveThemeTokenForNative(inactiveColor);\n const bg = resolveThemeTokenForNative(background);\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fabBg = resolveThemeTokenForNative(fabColor ?? activeColor);\n const danger = resolveThemeTokenForNative(\"danger\");\n\n return (\n <View\n testID={testID}\n style={{\n width: \"100%\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n minHeight: BAR_MIN_HEIGHT,\n paddingHorizontal: 20,\n paddingTop: 10,\n paddingBottom: Math.max(insets.bottom, 12),\n backgroundColor: bg,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {items.map((item: ITabBarItem<K>) => {\n const active = item.key === value;\n\n // Centre FAB — a content-width slot (NOT flex:1) so the four\n // side slots distribute evenly; the FAB is lifted in-flow.\n if (item.fab) {\n return (\n <Flex key={item.key} style={{alignItems: \"center\", justifyContent: \"center\"}}>\n <Pressable\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n hitSlop={8}\n style={({pressed}) => [\n {\n width: FAB_SIZE,\n height: FAB_SIZE,\n borderRadius: FAB_SIZE / 2,\n marginTop: -26,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: fabBg,\n borderWidth: 3,\n borderColor: bg,\n shadowColor: fabBg,\n shadowOffset: {width: 0, height: 6},\n shadowOpacity: 0.35,\n shadowRadius: 12,\n elevation: 8,\n },\n pressed && {opacity: 0.9},\n ]}\n >\n <Icon name={item.icon as never} size={26} color=\"#FFFFFF\" />\n </Pressable>\n </Flex>\n );\n }\n\n const color = active ? activeC : inactiveC;\n const iconName = active && item.activeIcon != null ? item.activeIcon : item.icon;\n\n return (\n <Pressable\n key={item.key}\n onPress={() => onChange(item.key)}\n accessibilityRole=\"button\"\n accessibilityState={{selected: active}}\n style={({pressed}) => [\n {\n flex: 1,\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 4,\n paddingVertical: 6,\n paddingHorizontal: 4,\n minHeight: 44,\n },\n pressed && {opacity: 0.7},\n ]}\n >\n <View style={{alignItems: \"center\", justifyContent: \"center\", position: \"relative\"}}>\n <Icon name={iconName as never} size={24} color={color} />\n {item.badge != null && item.badge !== false && (\n <Badge badge={item.badge} barBg={bg} dangerColor={danger} />\n )}\n </View>\n {showLabels && item.label != null && (\n <Text numberOfLines={1} style={{fontSize: 11, fontWeight: \"500\", color, ...(rtlLineHeight != null ? {lineHeight: rtlLineHeight} : {})}}>\n {item.label}\n </Text>\n )}\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nexport default BottomTabBar;\n"],"names":[],"mappings":"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"}
@@ -83,6 +83,7 @@ require('../../apps/mobile/src/Core/RadioButton/index.cjs');
83
83
  require('../../apps/mobile/src/Core/Input/index.cjs');
84
84
  require('../../apps/mobile/src/Core/Input/Field.cjs');
85
85
  require('../../apps/mobile/src/Core/Input/Slot.cjs');
86
+ var useRtlLineHeight = require('../../hooks/useRtlLineHeight.cjs');
86
87
  var index_native = require('../../primitives/Icon/index.cjs');
87
88
  require('../../contexts/DialogContext.cjs');
88
89
  var useComponentDefaults = require('../../hooks/useComponentDefaults.cjs');
@@ -97,6 +98,8 @@ function MessageComposer(rawProps) {
97
98
  const textColor = style.resolveThemeTokenForNative("text");
98
99
  const muted = style.resolveThemeTokenForNative("text-secondary");
99
100
  const disabledBg = style.resolveThemeTokenForNative("gray.300");
101
+ const rtlLockedLine = useRtlLineHeight.default(void 0, 13);
102
+ const rtlHintLine = useRtlLineHeight.default(void 0, 11);
100
103
  if (locked) {
101
104
  return /* @__PURE__ */ jsxRuntime.jsx(
102
105
  reactNative.View,
@@ -128,7 +131,7 @@ function MessageComposer(rawProps) {
128
131
  },
129
132
  children: [
130
133
  /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "lock", size: 17, color: muted }),
131
- lockedLabel != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 13, color: muted }, children: lockedLabel })
134
+ lockedLabel != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 13, color: muted, ...rtlLockedLine != null ? { lineHeight: rtlLockedLine } : {} }, children: lockedLabel })
132
135
  ]
133
136
  }
134
137
  )
@@ -197,7 +200,7 @@ function MessageComposer(rawProps) {
197
200
  children: [
198
201
  attachHint != null && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center", gap: 6, paddingHorizontal: 12, paddingBottom: 8 }, children: [
199
202
  /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "info", size: 12, color: muted }),
200
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 11, color: muted }, children: attachHint })
203
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 11, color: muted, ...rtlHintLine != null ? { lineHeight: rtlHintLine } : {} }, children: attachHint })
201
204
  ] }),
202
205
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8, paddingHorizontal: 12 }, children: [
203
206
  onAttach != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: 36, height: 36, alignItems: "center", justifyContent: "center", opacity: 0.5 }, children: /* @__PURE__ */ jsxRuntime.jsx(index_native.Icon, { name: "paperclip", size: 20, color: muted }) }),
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/components/MessageComposer/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {View, Text, TextInput, TouchableOpacity} from \"react-native\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport type {IMessageComposerProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * MessageComposer (native) — attach + multiline input + send, mirroring the\n * app's chat input bar (RN TextInput + TouchableOpacity, library Icon).\n */\nexport function MessageComposer(rawProps: IMessageComposerProps) {\n const props = useComponentDefaults<IMessageComposerProps>(\"MessageComposer\", rawProps).mergedProps;\n const {value, onChangeText, onSend, onAttach, placeholder = \"Message…\", sendDisabled, accentColor = \"brand\", locked, lockedLabel, attachDisabled, attachHint, onSelectionChange, selection, inputRef, testID} = props;\n\n const accent = resolveThemeTokenForNative(accentColor);\n const surface = resolveThemeTokenForNative(\"surface-card\");\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fieldBg = resolveThemeTokenForNative(\"gray.100\");\n const textColor = resolveThemeTokenForNative(\"text\");\n const muted = resolveThemeTokenForNative(\"text-secondary\");\n const disabledBg = resolveThemeTokenForNative(\"gray.300\");\n\n if (locked) {\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n <View\n style={{\n flexGrow: 1,\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n backgroundColor: fieldBg,\n borderWidth: 1,\n borderColor: border,\n borderRadius: 999,\n paddingHorizontal: 16,\n paddingVertical: 11,\n }}\n >\n <Icon name=\"lock\" size={17} color={muted} />\n {lockedLabel != null && <Text style={{fontSize: 13, color: muted}}>{lockedLabel}</Text>}\n </View>\n </View>\n );\n }\n\n const canSend = sendDisabled != null ? !sendDisabled : value.trim().length > 0;\n\n // Shared field + send (referenced by the default row AND the attach-disabled\n // branch — single source, so the input is never duplicated).\n //\n // onSelectionChange is adapted from RN's NativeSyntheticEvent shape to the\n // platform-agnostic {start, end} contract declared on IMessageComposerProps,\n // so consumers (e.g. @mention pickers) read the same shape on native and web.\n const field = (\n <TextInput\n ref={inputRef as React.Ref<TextInput>}\n style={{\n flex: 1,\n backgroundColor: fieldBg,\n borderRadius: 20,\n paddingHorizontal: 14,\n paddingVertical: 8,\n fontSize: 14,\n color: textColor,\n // Pin a single-line floor (= the 36px send button) so a\n // multiline RN TextInput doesn't measure a taller default on\n // frame 1 then settle on frame 2 — that re-measure re-flexes\n // the chat thread above it (a small shift-then-settle on open).\n // Still grows up to maxHeight when typing multiple lines.\n minHeight: 36,\n maxHeight: 120,\n }}\n placeholder={placeholder}\n placeholderTextColor={muted}\n value={value}\n onChangeText={onChangeText}\n selection={selection as {start: number; end?: number} | undefined}\n onSelectionChange={\n onSelectionChange\n ? e => onSelectionChange({start: e.nativeEvent.selection.start, end: e.nativeEvent.selection.end})\n : undefined\n }\n multiline\n />\n );\n\n const sendBtn = (\n <TouchableOpacity\n onPress={() => canSend && onSend?.()}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={{\n width: 36,\n height: 36,\n borderRadius: 18,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: canSend ? accent : disabledBg,\n }}\n >\n <Icon name=\"send\" size={18} color=\"#FFFFFF\" />\n </TouchableOpacity>\n );\n\n if (attachDisabled) {\n return (\n <View\n testID={testID}\n style={{\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {attachHint != null && (\n <View style={{flexDirection: \"row\", alignItems: \"center\", gap: 6, paddingHorizontal: 12, paddingBottom: 8}}>\n <Icon name=\"info\" size={12} color={muted} />\n <Text style={{fontSize: 11, color: muted}}>{attachHint}</Text>\n </View>\n )}\n <View style={{flexDirection: \"row\", alignItems: \"flex-end\", gap: 8, paddingHorizontal: 12}}>\n {onAttach != null && (\n <View style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\", opacity: 0.5}}>\n <Icon name=\"paperclip\" size={20} color={muted} />\n </View>\n )}\n {field}\n {sendBtn}\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"flex-end\",\n gap: 8,\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {onAttach != null && (\n <TouchableOpacity\n onPress={onAttach}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Attach\"\n style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\"}}\n >\n <Icon name=\"paperclip\" size={20} color={muted} />\n </TouchableOpacity>\n )}\n {field}\n {sendBtn}\n </View>\n );\n}\n\nexport default MessageComposer;\n"],"names":["useComponentDefaults","resolveThemeTokenForNative","jsx","View","jsxs","Icon","Text","TextInput","TouchableOpacity"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/MessageComposer/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {View, Text, TextInput, TouchableOpacity} from \"react-native\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\nimport type {IMessageComposerProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * MessageComposer (native) — attach + multiline input + send, mirroring the\n * app's chat input bar (RN TextInput + TouchableOpacity, library Icon).\n */\nexport function MessageComposer(rawProps: IMessageComposerProps) {\n const props = useComponentDefaults<IMessageComposerProps>(\"MessageComposer\", rawProps).mergedProps;\n const {value, onChangeText, onSend, onAttach, placeholder = \"Message…\", sendDisabled, accentColor = \"brand\", locked, lockedLabel, attachDisabled, attachHint, onSelectionChange, selection, inputRef, testID} = props;\n\n const accent = resolveThemeTokenForNative(accentColor);\n const surface = resolveThemeTokenForNative(\"surface-card\");\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fieldBg = resolveThemeTokenForNative(\"gray.100\");\n const textColor = resolveThemeTokenForNative(\"text\");\n const muted = resolveThemeTokenForNative(\"text-secondary\");\n const disabledBg = resolveThemeTokenForNative(\"gray.300\");\n\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX. These render RAW\n * react-native <Text> with a fontSize and no lineHeight, so React Native\n * uses the FONT'S natural box — and NotoKufiArabic's is too short for its\n * own ascenders. In an RTL script that does not read as clipping, it reads\n * as a BAD TRANSLATION: ش/س, غ/ع and ة/ه differ only by marks above the\n * baseline. Both are undefined in LTR, so no English layout moves.\n *\n * ⚠️ THE INPUT ITSELF IS DELIBERATELY NOT TOUCHED. The TextInput below is\n * fontSize 14 with no lineHeight for the same reason, but it also carries\n * a hand-tuned single-line height floor and a comment about a two-frame\n * re-measure. Giving it a taller line box without being able to TYPE\n * Arabic into it and watch the field settle would be guessing at the one\n * control a person uses most. Recorded, not changed.\n */\n const rtlLockedLine = useRtlLineHeight(undefined, 13);\n const rtlHintLine = useRtlLineHeight(undefined, 11);\n\n if (locked) {\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n <View\n style={{\n flexGrow: 1,\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n backgroundColor: fieldBg,\n borderWidth: 1,\n borderColor: border,\n borderRadius: 999,\n paddingHorizontal: 16,\n paddingVertical: 11,\n }}\n >\n <Icon name=\"lock\" size={17} color={muted} />\n {lockedLabel != null && <Text style={{fontSize: 13, color: muted, ...(rtlLockedLine != null ? {lineHeight: rtlLockedLine} : {})}}>{lockedLabel}</Text>}\n </View>\n </View>\n );\n }\n\n const canSend = sendDisabled != null ? !sendDisabled : value.trim().length > 0;\n\n // Shared field + send (referenced by the default row AND the attach-disabled\n // branch — single source, so the input is never duplicated).\n //\n // onSelectionChange is adapted from RN's NativeSyntheticEvent shape to the\n // platform-agnostic {start, end} contract declared on IMessageComposerProps,\n // so consumers (e.g. @mention pickers) read the same shape on native and web.\n const field = (\n <TextInput\n ref={inputRef as React.Ref<TextInput>}\n style={{\n flex: 1,\n backgroundColor: fieldBg,\n borderRadius: 20,\n paddingHorizontal: 14,\n paddingVertical: 8,\n fontSize: 14,\n color: textColor,\n // Pin a single-line floor (= the 36px send button) so a\n // multiline RN TextInput doesn't measure a taller default on\n // frame 1 then settle on frame 2 — that re-measure re-flexes\n // the chat thread above it (a small shift-then-settle on open).\n // Still grows up to maxHeight when typing multiple lines.\n minHeight: 36,\n maxHeight: 120,\n }}\n placeholder={placeholder}\n placeholderTextColor={muted}\n value={value}\n onChangeText={onChangeText}\n selection={selection as {start: number; end?: number} | undefined}\n onSelectionChange={\n onSelectionChange\n ? e => onSelectionChange({start: e.nativeEvent.selection.start, end: e.nativeEvent.selection.end})\n : undefined\n }\n multiline\n />\n );\n\n const sendBtn = (\n <TouchableOpacity\n onPress={() => canSend && onSend?.()}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={{\n width: 36,\n height: 36,\n borderRadius: 18,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: canSend ? accent : disabledBg,\n }}\n >\n <Icon name=\"send\" size={18} color=\"#FFFFFF\" />\n </TouchableOpacity>\n );\n\n if (attachDisabled) {\n return (\n <View\n testID={testID}\n style={{\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {attachHint != null && (\n <View style={{flexDirection: \"row\", alignItems: \"center\", gap: 6, paddingHorizontal: 12, paddingBottom: 8}}>\n <Icon name=\"info\" size={12} color={muted} />\n <Text style={{fontSize: 11, color: muted, ...(rtlHintLine != null ? {lineHeight: rtlHintLine} : {})}}>{attachHint}</Text>\n </View>\n )}\n <View style={{flexDirection: \"row\", alignItems: \"flex-end\", gap: 8, paddingHorizontal: 12}}>\n {onAttach != null && (\n <View style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\", opacity: 0.5}}>\n <Icon name=\"paperclip\" size={20} color={muted} />\n </View>\n )}\n {field}\n {sendBtn}\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"flex-end\",\n gap: 8,\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {onAttach != null && (\n <TouchableOpacity\n onPress={onAttach}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Attach\"\n style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\"}}\n >\n <Icon name=\"paperclip\" size={20} color={muted} />\n </TouchableOpacity>\n )}\n {field}\n {sendBtn}\n </View>\n );\n}\n\nexport default MessageComposer;\n"],"names":["useComponentDefaults","resolveThemeTokenForNative","useRtlLineHeight","jsx","View","jsxs","Icon","Text","TextInput","TouchableOpacity"],"mappings":"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"}
@@ -81,6 +81,7 @@ import '../../apps/mobile/src/Core/RadioButton/index.js';
81
81
  import '../../apps/mobile/src/Core/Input/index.js';
82
82
  import '../../apps/mobile/src/Core/Input/Field.js';
83
83
  import '../../apps/mobile/src/Core/Input/Slot.js';
84
+ import useRtlLineHeight from '../../hooks/useRtlLineHeight.js';
84
85
  import { Icon } from '../../primitives/Icon/index.js';
85
86
  import '../../contexts/DialogContext.js';
86
87
  import useComponentDefaults from '../../hooks/useComponentDefaults.js';
@@ -95,6 +96,8 @@ function MessageComposer(rawProps) {
95
96
  const textColor = resolveThemeTokenForNative("text");
96
97
  const muted = resolveThemeTokenForNative("text-secondary");
97
98
  const disabledBg = resolveThemeTokenForNative("gray.300");
99
+ const rtlLockedLine = useRtlLineHeight(void 0, 13);
100
+ const rtlHintLine = useRtlLineHeight(void 0, 11);
98
101
  if (locked) {
99
102
  return /* @__PURE__ */ jsx(
100
103
  View,
@@ -126,7 +129,7 @@ function MessageComposer(rawProps) {
126
129
  },
127
130
  children: [
128
131
  /* @__PURE__ */ jsx(Icon, { name: "lock", size: 17, color: muted }),
129
- lockedLabel != null && /* @__PURE__ */ jsx(Text, { style: { fontSize: 13, color: muted }, children: lockedLabel })
132
+ lockedLabel != null && /* @__PURE__ */ jsx(Text, { style: { fontSize: 13, color: muted, ...rtlLockedLine != null ? { lineHeight: rtlLockedLine } : {} }, children: lockedLabel })
130
133
  ]
131
134
  }
132
135
  )
@@ -195,7 +198,7 @@ function MessageComposer(rawProps) {
195
198
  children: [
196
199
  attachHint != null && /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6, paddingHorizontal: 12, paddingBottom: 8 }, children: [
197
200
  /* @__PURE__ */ jsx(Icon, { name: "info", size: 12, color: muted }),
198
- /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, color: muted }, children: attachHint })
201
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, color: muted, ...rtlHintLine != null ? { lineHeight: rtlHintLine } : {} }, children: attachHint })
199
202
  ] }),
200
203
  /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8, paddingHorizontal: 12 }, children: [
201
204
  onAttach != null && /* @__PURE__ */ jsx(View, { style: { width: 36, height: 36, alignItems: "center", justifyContent: "center", opacity: 0.5 }, children: /* @__PURE__ */ jsx(Icon, { name: "paperclip", size: 20, color: muted }) }),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/MessageComposer/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {View, Text, TextInput, TouchableOpacity} from \"react-native\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport type {IMessageComposerProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * MessageComposer (native) — attach + multiline input + send, mirroring the\n * app's chat input bar (RN TextInput + TouchableOpacity, library Icon).\n */\nexport function MessageComposer(rawProps: IMessageComposerProps) {\n const props = useComponentDefaults<IMessageComposerProps>(\"MessageComposer\", rawProps).mergedProps;\n const {value, onChangeText, onSend, onAttach, placeholder = \"Message…\", sendDisabled, accentColor = \"brand\", locked, lockedLabel, attachDisabled, attachHint, onSelectionChange, selection, inputRef, testID} = props;\n\n const accent = resolveThemeTokenForNative(accentColor);\n const surface = resolveThemeTokenForNative(\"surface-card\");\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fieldBg = resolveThemeTokenForNative(\"gray.100\");\n const textColor = resolveThemeTokenForNative(\"text\");\n const muted = resolveThemeTokenForNative(\"text-secondary\");\n const disabledBg = resolveThemeTokenForNative(\"gray.300\");\n\n if (locked) {\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n <View\n style={{\n flexGrow: 1,\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n backgroundColor: fieldBg,\n borderWidth: 1,\n borderColor: border,\n borderRadius: 999,\n paddingHorizontal: 16,\n paddingVertical: 11,\n }}\n >\n <Icon name=\"lock\" size={17} color={muted} />\n {lockedLabel != null && <Text style={{fontSize: 13, color: muted}}>{lockedLabel}</Text>}\n </View>\n </View>\n );\n }\n\n const canSend = sendDisabled != null ? !sendDisabled : value.trim().length > 0;\n\n // Shared field + send (referenced by the default row AND the attach-disabled\n // branch — single source, so the input is never duplicated).\n //\n // onSelectionChange is adapted from RN's NativeSyntheticEvent shape to the\n // platform-agnostic {start, end} contract declared on IMessageComposerProps,\n // so consumers (e.g. @mention pickers) read the same shape on native and web.\n const field = (\n <TextInput\n ref={inputRef as React.Ref<TextInput>}\n style={{\n flex: 1,\n backgroundColor: fieldBg,\n borderRadius: 20,\n paddingHorizontal: 14,\n paddingVertical: 8,\n fontSize: 14,\n color: textColor,\n // Pin a single-line floor (= the 36px send button) so a\n // multiline RN TextInput doesn't measure a taller default on\n // frame 1 then settle on frame 2 — that re-measure re-flexes\n // the chat thread above it (a small shift-then-settle on open).\n // Still grows up to maxHeight when typing multiple lines.\n minHeight: 36,\n maxHeight: 120,\n }}\n placeholder={placeholder}\n placeholderTextColor={muted}\n value={value}\n onChangeText={onChangeText}\n selection={selection as {start: number; end?: number} | undefined}\n onSelectionChange={\n onSelectionChange\n ? e => onSelectionChange({start: e.nativeEvent.selection.start, end: e.nativeEvent.selection.end})\n : undefined\n }\n multiline\n />\n );\n\n const sendBtn = (\n <TouchableOpacity\n onPress={() => canSend && onSend?.()}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={{\n width: 36,\n height: 36,\n borderRadius: 18,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: canSend ? accent : disabledBg,\n }}\n >\n <Icon name=\"send\" size={18} color=\"#FFFFFF\" />\n </TouchableOpacity>\n );\n\n if (attachDisabled) {\n return (\n <View\n testID={testID}\n style={{\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {attachHint != null && (\n <View style={{flexDirection: \"row\", alignItems: \"center\", gap: 6, paddingHorizontal: 12, paddingBottom: 8}}>\n <Icon name=\"info\" size={12} color={muted} />\n <Text style={{fontSize: 11, color: muted}}>{attachHint}</Text>\n </View>\n )}\n <View style={{flexDirection: \"row\", alignItems: \"flex-end\", gap: 8, paddingHorizontal: 12}}>\n {onAttach != null && (\n <View style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\", opacity: 0.5}}>\n <Icon name=\"paperclip\" size={20} color={muted} />\n </View>\n )}\n {field}\n {sendBtn}\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"flex-end\",\n gap: 8,\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {onAttach != null && (\n <TouchableOpacity\n onPress={onAttach}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Attach\"\n style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\"}}\n >\n <Icon name=\"paperclip\" size={20} color={muted} />\n </TouchableOpacity>\n )}\n {field}\n {sendBtn}\n </View>\n );\n}\n\nexport default MessageComposer;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/MessageComposer/index.native.tsx"],"sourcesContent":["import React from \"react\";\nimport {View, Text, TextInput, TouchableOpacity} from \"react-native\";\nimport {resolveThemeTokenForNative} from \"@lmb-it/kitsconcertomobile\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\nimport type {IMessageComposerProps} from \"@lmb-it/kitsconcerto-types\";\nimport {Icon} from \"../../primitives\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * MessageComposer (native) — attach + multiline input + send, mirroring the\n * app's chat input bar (RN TextInput + TouchableOpacity, library Icon).\n */\nexport function MessageComposer(rawProps: IMessageComposerProps) {\n const props = useComponentDefaults<IMessageComposerProps>(\"MessageComposer\", rawProps).mergedProps;\n const {value, onChangeText, onSend, onAttach, placeholder = \"Message…\", sendDisabled, accentColor = \"brand\", locked, lockedLabel, attachDisabled, attachHint, onSelectionChange, selection, inputRef, testID} = props;\n\n const accent = resolveThemeTokenForNative(accentColor);\n const surface = resolveThemeTokenForNative(\"surface-card\");\n const border = resolveThemeTokenForNative(\"surface-border\");\n const fieldBg = resolveThemeTokenForNative(\"gray.100\");\n const textColor = resolveThemeTokenForNative(\"text\");\n const muted = resolveThemeTokenForNative(\"text-secondary\");\n const disabledBg = resolveThemeTokenForNative(\"gray.300\");\n\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX. These render RAW\n * react-native <Text> with a fontSize and no lineHeight, so React Native\n * uses the FONT'S natural box — and NotoKufiArabic's is too short for its\n * own ascenders. In an RTL script that does not read as clipping, it reads\n * as a BAD TRANSLATION: ش/س, غ/ع and ة/ه differ only by marks above the\n * baseline. Both are undefined in LTR, so no English layout moves.\n *\n * ⚠️ THE INPUT ITSELF IS DELIBERATELY NOT TOUCHED. The TextInput below is\n * fontSize 14 with no lineHeight for the same reason, but it also carries\n * a hand-tuned single-line height floor and a comment about a two-frame\n * re-measure. Giving it a taller line box without being able to TYPE\n * Arabic into it and watch the field settle would be guessing at the one\n * control a person uses most. Recorded, not changed.\n */\n const rtlLockedLine = useRtlLineHeight(undefined, 13);\n const rtlHintLine = useRtlLineHeight(undefined, 11);\n\n if (locked) {\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n <View\n style={{\n flexGrow: 1,\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n backgroundColor: fieldBg,\n borderWidth: 1,\n borderColor: border,\n borderRadius: 999,\n paddingHorizontal: 16,\n paddingVertical: 11,\n }}\n >\n <Icon name=\"lock\" size={17} color={muted} />\n {lockedLabel != null && <Text style={{fontSize: 13, color: muted, ...(rtlLockedLine != null ? {lineHeight: rtlLockedLine} : {})}}>{lockedLabel}</Text>}\n </View>\n </View>\n );\n }\n\n const canSend = sendDisabled != null ? !sendDisabled : value.trim().length > 0;\n\n // Shared field + send (referenced by the default row AND the attach-disabled\n // branch — single source, so the input is never duplicated).\n //\n // onSelectionChange is adapted from RN's NativeSyntheticEvent shape to the\n // platform-agnostic {start, end} contract declared on IMessageComposerProps,\n // so consumers (e.g. @mention pickers) read the same shape on native and web.\n const field = (\n <TextInput\n ref={inputRef as React.Ref<TextInput>}\n style={{\n flex: 1,\n backgroundColor: fieldBg,\n borderRadius: 20,\n paddingHorizontal: 14,\n paddingVertical: 8,\n fontSize: 14,\n color: textColor,\n // Pin a single-line floor (= the 36px send button) so a\n // multiline RN TextInput doesn't measure a taller default on\n // frame 1 then settle on frame 2 — that re-measure re-flexes\n // the chat thread above it (a small shift-then-settle on open).\n // Still grows up to maxHeight when typing multiple lines.\n minHeight: 36,\n maxHeight: 120,\n }}\n placeholder={placeholder}\n placeholderTextColor={muted}\n value={value}\n onChangeText={onChangeText}\n selection={selection as {start: number; end?: number} | undefined}\n onSelectionChange={\n onSelectionChange\n ? e => onSelectionChange({start: e.nativeEvent.selection.start, end: e.nativeEvent.selection.end})\n : undefined\n }\n multiline\n />\n );\n\n const sendBtn = (\n <TouchableOpacity\n onPress={() => canSend && onSend?.()}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={{\n width: 36,\n height: 36,\n borderRadius: 18,\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: canSend ? accent : disabledBg,\n }}\n >\n <Icon name=\"send\" size={18} color=\"#FFFFFF\" />\n </TouchableOpacity>\n );\n\n if (attachDisabled) {\n return (\n <View\n testID={testID}\n style={{\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {attachHint != null && (\n <View style={{flexDirection: \"row\", alignItems: \"center\", gap: 6, paddingHorizontal: 12, paddingBottom: 8}}>\n <Icon name=\"info\" size={12} color={muted} />\n <Text style={{fontSize: 11, color: muted, ...(rtlHintLine != null ? {lineHeight: rtlHintLine} : {})}}>{attachHint}</Text>\n </View>\n )}\n <View style={{flexDirection: \"row\", alignItems: \"flex-end\", gap: 8, paddingHorizontal: 12}}>\n {onAttach != null && (\n <View style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\", opacity: 0.5}}>\n <Icon name=\"paperclip\" size={20} color={muted} />\n </View>\n )}\n {field}\n {sendBtn}\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n style={{\n flexDirection: \"row\",\n alignItems: \"flex-end\",\n gap: 8,\n paddingHorizontal: 12,\n paddingVertical: 10,\n backgroundColor: surface,\n borderTopWidth: 1,\n borderTopColor: border,\n }}\n >\n {onAttach != null && (\n <TouchableOpacity\n onPress={onAttach}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Attach\"\n style={{width: 36, height: 36, alignItems: \"center\", justifyContent: \"center\"}}\n >\n <Icon name=\"paperclip\" size={20} color={muted} />\n </TouchableOpacity>\n )}\n {field}\n {sendBtn}\n </View>\n );\n}\n\nexport default MessageComposer;\n"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ var React = require('react');
5
5
  var reactNative = require('react-native');
6
6
  require('../../contexts/DialogContext.cjs');
7
7
  var useComponentDefaults = require('../../hooks/useComponentDefaults.cjs');
8
+ var useRtlLineHeight = require('../../hooks/useRtlLineHeight.cjs');
8
9
  var KitsThemeProvider_native = require('../../contexts/Theme/KitsThemeProvider.cjs');
9
10
 
10
11
  function TypingIndicator(rawProps) {
@@ -14,6 +15,7 @@ function TypingIndicator(rawProps) {
14
15
  const dot = resolveToken(dotColor);
15
16
  const bg = resolveToken(bubbleColor);
16
17
  const labelColor = resolveToken("text-secondary");
18
+ const rtlLineHeight = useRtlLineHeight.default(void 0, 11);
17
19
  const d0 = React.useRef(new reactNative.Animated.Value(0.3)).current;
18
20
  const d1 = React.useRef(new reactNative.Animated.Value(0.3)).current;
19
21
  const d2 = React.useRef(new reactNative.Animated.Value(0.3)).current;
@@ -33,7 +35,7 @@ function TypingIndicator(rawProps) {
33
35
  }, [d0, d1, d2]);
34
36
  const dots = /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", gap: 5, alignItems: "center" }, children: [d0, d1, d2].map((v, i) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: { width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v } }, i)) });
35
37
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { testID, style: { alignItems: "flex-start", gap: 4 }, children: [
36
- label != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 11, fontWeight: "600", color: labelColor }, children: label }),
38
+ label != null && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 11, fontWeight: "600", color: labelColor, ...rtlLineHeight != null ? { lineHeight: rtlLineHeight } : {} }, children: label }),
37
39
  bubble ? /* @__PURE__ */ jsxRuntime.jsx(
38
40
  reactNative.View,
39
41
  {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/components/TypingIndicator/index.native.tsx"],"sourcesContent":["import React, {useEffect, useRef} from \"react\";\nimport {View, Text, Animated, Easing} from \"react-native\";\nimport type {ITypingIndicatorProps} from \"@lmb-it/kitsconcerto-types\";\nimport {useKitsTheme} from \"../../contexts/Theme\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * TypingIndicator (native) — three dots pulsing via RN `Animated` (opacity only,\n * which is native-driver-safe), optionally inside an incoming-style bubble, with\n * an optional resolved caption. Mirrors CircularProgress.native (Animated.loop +\n * useNativeDriver). Each loop is stopped on unmount. Colours resolve from tokens.\n */\nexport function TypingIndicator(rawProps: ITypingIndicatorProps) {\n const props = useComponentDefaults<ITypingIndicatorProps>(\"TypingIndicator\", rawProps).mergedProps;\n const {label, dotColor = \"text-secondary\", bubble = true, bubbleColor = \"surface-muted\", testID} = props;\n const {resolveToken} = useKitsTheme();\n const dot = resolveToken(dotColor);\n const bg = resolveToken(bubbleColor);\n const labelColor = resolveToken(\"text-secondary\");\n\n const d0 = useRef(new Animated.Value(0.3)).current;\n const d1 = useRef(new Animated.Value(0.3)).current;\n const d2 = useRef(new Animated.Value(0.3)).current;\n\n useEffect(() => {\n const values = [d0, d1, d2];\n const loops = values.map((v, i) =>\n Animated.loop(\n Animated.sequence([\n Animated.delay(i * 180),\n Animated.timing(v, {toValue: 1, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n Animated.timing(v, {toValue: 0.3, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n ]),\n ),\n );\n loops.forEach(l => l.start());\n return () => loops.forEach(l => l.stop());\n }, [d0, d1, d2]);\n\n const dots = (\n <View style={{flexDirection: \"row\", gap: 5, alignItems: \"center\"}}>\n {[d0, d1, d2].map((v, i) => (\n <Animated.View key={i} style={{width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v}} />\n ))}\n </View>\n );\n\n return (\n <View testID={testID} style={{alignItems: \"flex-start\", gap: 4}}>\n {label != null && <Text style={{fontSize: 11, fontWeight: \"600\", color: labelColor}}>{label}</Text>}\n {bubble ? (\n <View\n style={{\n backgroundColor: bg,\n borderRadius: 16,\n borderBottomLeftRadius: 4,\n paddingVertical: 10,\n paddingHorizontal: 14,\n alignSelf: \"flex-start\",\n }}\n >\n {dots}\n </View>\n ) : (\n dots\n )}\n </View>\n );\n}\n\nexport default TypingIndicator;\n"],"names":["useComponentDefaults","useKitsTheme","useRef","Animated","useEffect","Easing","jsx","View","jsxs","Text"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/TypingIndicator/index.native.tsx"],"sourcesContent":["import React, {useEffect, useRef} from \"react\";\nimport {View, Text, Animated, Easing} from \"react-native\";\nimport type {ITypingIndicatorProps} from \"@lmb-it/kitsconcerto-types\";\nimport {useKitsTheme} from \"../../contexts/Theme\";\nimport {useComponentDefaults} from \"../../hooks\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\n\n/**\n * TypingIndicator (native) — three dots pulsing via RN `Animated` (opacity only,\n * which is native-driver-safe), optionally inside an incoming-style bubble, with\n * an optional resolved caption. Mirrors CircularProgress.native (Animated.loop +\n * useNativeDriver). Each loop is stopped on unmount. Colours resolve from tokens.\n */\nexport function TypingIndicator(rawProps: ITypingIndicatorProps) {\n const props = useComponentDefaults<ITypingIndicatorProps>(\"TypingIndicator\", rawProps).mergedProps;\n const {label, dotColor = \"text-secondary\", bubble = true, bubbleColor = \"surface-muted\", testID} = props;\n const {resolveToken} = useKitsTheme();\n const dot = resolveToken(dotColor);\n const bg = resolveToken(bubbleColor);\n const labelColor = resolveToken(\"text-secondary\");\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX. This renders a RAW\n * react-native <Text> with a fontSize and no lineHeight, so React Native\n * uses the FONT'S natural line box — and NotoKufiArabic's is too short for\n * its own ascenders, cutting the glyph along its top edge. In an RTL\n * script that does not read as clipping, it reads as a BAD TRANSLATION:\n * ش/س, غ/ع and ة/ه differ only by marks above the baseline.\n * ⚠️ undefined in LTR, so no English layout moves.\n */\n const rtlLineHeight = useRtlLineHeight(undefined, 11);\n\n const d0 = useRef(new Animated.Value(0.3)).current;\n const d1 = useRef(new Animated.Value(0.3)).current;\n const d2 = useRef(new Animated.Value(0.3)).current;\n\n useEffect(() => {\n const values = [d0, d1, d2];\n const loops = values.map((v, i) =>\n Animated.loop(\n Animated.sequence([\n Animated.delay(i * 180),\n Animated.timing(v, {toValue: 1, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n Animated.timing(v, {toValue: 0.3, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n ]),\n ),\n );\n loops.forEach(l => l.start());\n return () => loops.forEach(l => l.stop());\n }, [d0, d1, d2]);\n\n const dots = (\n <View style={{flexDirection: \"row\", gap: 5, alignItems: \"center\"}}>\n {[d0, d1, d2].map((v, i) => (\n <Animated.View key={i} style={{width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v}} />\n ))}\n </View>\n );\n\n return (\n <View testID={testID} style={{alignItems: \"flex-start\", gap: 4}}>\n {label != null && <Text style={{fontSize: 11, fontWeight: \"600\", color: labelColor, ...(rtlLineHeight != null ? {lineHeight: rtlLineHeight} : {})}}>{label}</Text>}\n {bubble ? (\n <View\n style={{\n backgroundColor: bg,\n borderRadius: 16,\n borderBottomLeftRadius: 4,\n paddingVertical: 10,\n paddingHorizontal: 14,\n alignSelf: \"flex-start\",\n }}\n >\n {dots}\n </View>\n ) : (\n dots\n )}\n </View>\n );\n}\n\nexport default TypingIndicator;\n"],"names":["useComponentDefaults","useKitsTheme","useRtlLineHeight","useRef","Animated","useEffect","Easing","jsx","View","jsxs","Text"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { useRef, useEffect } from 'react';
3
3
  import { Animated, Easing, View, Text } from 'react-native';
4
4
  import '../../contexts/DialogContext.js';
5
5
  import useComponentDefaults from '../../hooks/useComponentDefaults.js';
6
+ import useRtlLineHeight from '../../hooks/useRtlLineHeight.js';
6
7
  import { useKitsTheme } from '../../contexts/Theme/KitsThemeProvider.js';
7
8
 
8
9
  function TypingIndicator(rawProps) {
@@ -12,6 +13,7 @@ function TypingIndicator(rawProps) {
12
13
  const dot = resolveToken(dotColor);
13
14
  const bg = resolveToken(bubbleColor);
14
15
  const labelColor = resolveToken("text-secondary");
16
+ const rtlLineHeight = useRtlLineHeight(void 0, 11);
15
17
  const d0 = useRef(new Animated.Value(0.3)).current;
16
18
  const d1 = useRef(new Animated.Value(0.3)).current;
17
19
  const d2 = useRef(new Animated.Value(0.3)).current;
@@ -31,7 +33,7 @@ function TypingIndicator(rawProps) {
31
33
  }, [d0, d1, d2]);
32
34
  const dots = /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", gap: 5, alignItems: "center" }, children: [d0, d1, d2].map((v, i) => /* @__PURE__ */ jsx(Animated.View, { style: { width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v } }, i)) });
33
35
  return /* @__PURE__ */ jsxs(View, { testID, style: { alignItems: "flex-start", gap: 4 }, children: [
34
- label != null && /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, fontWeight: "600", color: labelColor }, children: label }),
36
+ label != null && /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, fontWeight: "600", color: labelColor, ...rtlLineHeight != null ? { lineHeight: rtlLineHeight } : {} }, children: label }),
35
37
  bubble ? /* @__PURE__ */ jsx(
36
38
  View,
37
39
  {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/TypingIndicator/index.native.tsx"],"sourcesContent":["import React, {useEffect, useRef} from \"react\";\nimport {View, Text, Animated, Easing} from \"react-native\";\nimport type {ITypingIndicatorProps} from \"@lmb-it/kitsconcerto-types\";\nimport {useKitsTheme} from \"../../contexts/Theme\";\nimport {useComponentDefaults} from \"../../hooks\";\n\n/**\n * TypingIndicator (native) — three dots pulsing via RN `Animated` (opacity only,\n * which is native-driver-safe), optionally inside an incoming-style bubble, with\n * an optional resolved caption. Mirrors CircularProgress.native (Animated.loop +\n * useNativeDriver). Each loop is stopped on unmount. Colours resolve from tokens.\n */\nexport function TypingIndicator(rawProps: ITypingIndicatorProps) {\n const props = useComponentDefaults<ITypingIndicatorProps>(\"TypingIndicator\", rawProps).mergedProps;\n const {label, dotColor = \"text-secondary\", bubble = true, bubbleColor = \"surface-muted\", testID} = props;\n const {resolveToken} = useKitsTheme();\n const dot = resolveToken(dotColor);\n const bg = resolveToken(bubbleColor);\n const labelColor = resolveToken(\"text-secondary\");\n\n const d0 = useRef(new Animated.Value(0.3)).current;\n const d1 = useRef(new Animated.Value(0.3)).current;\n const d2 = useRef(new Animated.Value(0.3)).current;\n\n useEffect(() => {\n const values = [d0, d1, d2];\n const loops = values.map((v, i) =>\n Animated.loop(\n Animated.sequence([\n Animated.delay(i * 180),\n Animated.timing(v, {toValue: 1, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n Animated.timing(v, {toValue: 0.3, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n ]),\n ),\n );\n loops.forEach(l => l.start());\n return () => loops.forEach(l => l.stop());\n }, [d0, d1, d2]);\n\n const dots = (\n <View style={{flexDirection: \"row\", gap: 5, alignItems: \"center\"}}>\n {[d0, d1, d2].map((v, i) => (\n <Animated.View key={i} style={{width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v}} />\n ))}\n </View>\n );\n\n return (\n <View testID={testID} style={{alignItems: \"flex-start\", gap: 4}}>\n {label != null && <Text style={{fontSize: 11, fontWeight: \"600\", color: labelColor}}>{label}</Text>}\n {bubble ? (\n <View\n style={{\n backgroundColor: bg,\n borderRadius: 16,\n borderBottomLeftRadius: 4,\n paddingVertical: 10,\n paddingHorizontal: 14,\n alignSelf: \"flex-start\",\n }}\n >\n {dots}\n </View>\n ) : (\n dots\n )}\n </View>\n );\n}\n\nexport default TypingIndicator;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/TypingIndicator/index.native.tsx"],"sourcesContent":["import React, {useEffect, useRef} from \"react\";\nimport {View, Text, Animated, Easing} from \"react-native\";\nimport type {ITypingIndicatorProps} from \"@lmb-it/kitsconcerto-types\";\nimport {useKitsTheme} from \"../../contexts/Theme\";\nimport {useComponentDefaults} from \"../../hooks\";\nimport useRtlLineHeight from \"../../hooks/useRtlLineHeight\";\n\n/**\n * TypingIndicator (native) — three dots pulsing via RN `Animated` (opacity only,\n * which is native-driver-safe), optionally inside an incoming-style bubble, with\n * an optional resolved caption. Mirrors CircularProgress.native (Animated.loop +\n * useNativeDriver). Each loop is stopped on unmount. Colours resolve from tokens.\n */\nexport function TypingIndicator(rawProps: ITypingIndicatorProps) {\n const props = useComponentDefaults<ITypingIndicatorProps>(\"TypingIndicator\", rawProps).mergedProps;\n const {label, dotColor = \"text-secondary\", bubble = true, bubbleColor = \"surface-muted\", testID} = props;\n const {resolveToken} = useKitsTheme();\n const dot = resolveToken(dotColor);\n const bg = resolveToken(bubbleColor);\n const labelColor = resolveToken(\"text-secondary\");\n /*\n * ⛔ ARABIC CLIPS WITHOUT AN EXPLICIT LINE BOX. This renders a RAW\n * react-native <Text> with a fontSize and no lineHeight, so React Native\n * uses the FONT'S natural line box — and NotoKufiArabic's is too short for\n * its own ascenders, cutting the glyph along its top edge. In an RTL\n * script that does not read as clipping, it reads as a BAD TRANSLATION:\n * ش/س, غ/ع and ة/ه differ only by marks above the baseline.\n * ⚠️ undefined in LTR, so no English layout moves.\n */\n const rtlLineHeight = useRtlLineHeight(undefined, 11);\n\n const d0 = useRef(new Animated.Value(0.3)).current;\n const d1 = useRef(new Animated.Value(0.3)).current;\n const d2 = useRef(new Animated.Value(0.3)).current;\n\n useEffect(() => {\n const values = [d0, d1, d2];\n const loops = values.map((v, i) =>\n Animated.loop(\n Animated.sequence([\n Animated.delay(i * 180),\n Animated.timing(v, {toValue: 1, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n Animated.timing(v, {toValue: 0.3, duration: 400, easing: Easing.inOut(Easing.ease), useNativeDriver: true}),\n ]),\n ),\n );\n loops.forEach(l => l.start());\n return () => loops.forEach(l => l.stop());\n }, [d0, d1, d2]);\n\n const dots = (\n <View style={{flexDirection: \"row\", gap: 5, alignItems: \"center\"}}>\n {[d0, d1, d2].map((v, i) => (\n <Animated.View key={i} style={{width: 7, height: 7, borderRadius: 9999, backgroundColor: dot, opacity: v}} />\n ))}\n </View>\n );\n\n return (\n <View testID={testID} style={{alignItems: \"flex-start\", gap: 4}}>\n {label != null && <Text style={{fontSize: 11, fontWeight: \"600\", color: labelColor, ...(rtlLineHeight != null ? {lineHeight: rtlLineHeight} : {})}}>{label}</Text>}\n {bubble ? (\n <View\n style={{\n backgroundColor: bg,\n borderRadius: 16,\n borderBottomLeftRadius: 4,\n paddingVertical: 10,\n paddingHorizontal: 14,\n alignSelf: \"flex-start\",\n }}\n >\n {dots}\n </View>\n ) : (\n dots\n )}\n </View>\n );\n}\n\nexport default TypingIndicator;\n"],"names":[],"mappings":"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"}
@@ -16,14 +16,14 @@ function useSeverityColors(severity) {
16
16
  if (severity === "brand") {
17
17
  const primary = resolveToken("primary");
18
18
  if (colorMode === "dark") {
19
- const swapShade = (token, target) => resolveToken(token.replace(/\.\d+$/, `.${target}`));
19
+ const swapShade = (token, target, fallbackSlot) => /\.\d+$/.test(token) ? resolveToken(token.replace(/\.\d+$/, `.${target}`)) : resolveToken(fallbackSlot);
20
20
  return {
21
21
  solid: primary,
22
22
  solidText: resolveSolidText(),
23
- bgTint: swapShade(slots.solid, "900"),
23
+ bgTint: swapShade(slots.solid, "900", slots.bgTint),
24
24
  iconFg: primary,
25
- iconBg: swapShade(slots.solid, "800"),
26
- text: swapShade(slots.solid, "200"),
25
+ iconBg: swapShade(slots.solid, "800", slots.iconBg),
26
+ text: swapShade(slots.solid, "200", slots.text),
27
27
  border: primary
28
28
  };
29
29
  }
@@ -38,16 +38,14 @@ function useSeverityColors(severity) {
38
38
  };
39
39
  }
40
40
  if (colorMode === "dark") {
41
- const swapShade = (token, target) => {
42
- return resolveToken(token.replace(/\.\d+$/, `.${target}`));
43
- };
41
+ const swapShade = (token, target, fallbackSlot) => /\.\d+$/.test(token) ? resolveToken(token.replace(/\.\d+$/, `.${target}`)) : resolveToken(fallbackSlot);
44
42
  return {
45
43
  solid: resolveToken(slots.solid),
46
44
  solidText: resolveSolidText(),
47
- bgTint: swapShade(slots.solid, "900"),
48
- iconFg: swapShade(slots.solid, "300"),
49
- iconBg: swapShade(slots.solid, "800"),
50
- text: swapShade(slots.solid, "200"),
45
+ bgTint: swapShade(slots.solid, "900", slots.bgTint),
46
+ iconFg: swapShade(slots.solid, "300", slots.iconFg),
47
+ iconBg: swapShade(slots.solid, "800", slots.iconBg),
48
+ text: swapShade(slots.solid, "200", slots.text),
51
49
  border: resolveToken(slots.border)
52
50
  };
53
51
  }
@@ -74,16 +72,14 @@ function useAllSeverityColors() {
74
72
  const isBrand = key === "brand";
75
73
  const solidText = slots.solidText ? resolveToken(slots.solidText) : WHITE;
76
74
  if (colorMode === "dark") {
77
- const swapShade = (token, target) => {
78
- return resolveToken(token.replace(/\.\d+$/, `.${target}`));
79
- };
75
+ const swapShade = (token, target, fallbackSlot) => /\.\d+$/.test(token) ? resolveToken(token.replace(/\.\d+$/, `.${target}`)) : resolveToken(fallbackSlot);
80
76
  result[key] = {
81
77
  solid: isBrand ? primary : resolveToken(slots.solid),
82
78
  solidText,
83
- bgTint: swapShade(slots.solid, "900"),
84
- iconFg: isBrand ? primary : swapShade(slots.solid, "300"),
85
- iconBg: swapShade(slots.solid, "800"),
86
- text: swapShade(slots.solid, "200"),
79
+ bgTint: swapShade(slots.solid, "900", slots.bgTint),
80
+ iconFg: isBrand ? primary : swapShade(slots.solid, "300", slots.iconFg),
81
+ iconBg: swapShade(slots.solid, "800", slots.iconBg),
82
+ text: swapShade(slots.solid, "200", slots.text),
87
83
  border: isBrand ? primary : resolveToken(slots.border)
88
84
  };
89
85
  } else {