rn-vs-lb 1.0.65 → 1.0.66

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.
Files changed (86) hide show
  1. package/components/Button/Button.stories.tsx +1 -1
  2. package/components/Button/DeleteAccountButton.stories.tsx +1 -1
  3. package/components/Button/PostButton.stories.tsx +1 -1
  4. package/components/Button/TelegramFeedbackLink.stories.tsx +1 -1
  5. package/components/Cards/BusinessIdeaCard.stories.tsx +73 -0
  6. package/components/Cards/BusinessIdeaCard.tsx +251 -0
  7. package/components/Cards/EventCard.stories.tsx +1 -1
  8. package/components/Cards/PlaceCard.stories.tsx +1 -1
  9. package/components/Cards/index.tsx +1 -0
  10. package/components/Chat/ChatItem.stories.tsx +1 -1
  11. package/components/Chat/ChatTab/ChatTabs.stories.tsx +1 -1
  12. package/components/Chat/ChatTab/SubTabButton.stories.tsx +1 -1
  13. package/components/Chat/InputMessage.stories.tsx +1 -1
  14. package/components/Chat/MessageItem/MessageItem.stories.tsx +1 -1
  15. package/components/Chat/PinnedMessagesBar/PinnedMessagesBar.stories.tsx +1 -1
  16. package/components/Gallery/AiAgentGallery.stories.tsx +1 -1
  17. package/components/Gallery/ProfileMediaGallery.stories.tsx +66 -0
  18. package/components/Gallery/ProfileMediaGallery.tsx +239 -0
  19. package/components/Gallery/ProfileMediaNavigationItem.tsx +73 -0
  20. package/components/Gallery/ProfileSelfiesGallery.pure.tsx +97 -0
  21. package/components/Gallery/ProfileSelfiesGallery.stories.tsx +86 -0
  22. package/components/Gallery/index.ts +4 -1
  23. package/components/Header/HeaderDefault.stories.tsx +1 -1
  24. package/components/Header/HeaderEdit.stories.tsx +1 -1
  25. package/components/Header/HeaderHome.stories.tsx +1 -1
  26. package/components/Header/HeaderSwitcher.stories.tsx +1 -1
  27. package/components/Header/HeaderWithImg.stories.tsx +1 -1
  28. package/components/Modals/GalleryModal.stories.tsx +1 -1
  29. package/components/Modals/GuestAiChatModal.stories.tsx +1 -1
  30. package/components/Modals/ReportModal.stories.tsx +1 -1
  31. package/components/Poll/CommentItem.stories.tsx +1 -1
  32. package/components/Poll/PollCardList.stories.tsx +1 -1
  33. package/components/Posts/EventCardList.stories.tsx +1 -1
  34. package/components/Posts/PlaceCardList.stories.tsx +1 -1
  35. package/components/Prank/HeroPrankCard.stories.tsx +47 -0
  36. package/components/Prank/HeroPrankCard.tsx +114 -0
  37. package/components/Prank/HomelessPrankPage.stories.tsx +58 -0
  38. package/components/Prank/UploadPromptCard.stories.tsx +55 -0
  39. package/components/Prank/UploadPromptCard.tsx +130 -0
  40. package/components/Prank/index.ts +5 -0
  41. package/components/Profile/ModalProfilePhoto.stories.tsx +1 -1
  42. package/components/Profile/ProfileCard/ProfileCard.stories.tsx +1 -1
  43. package/components/Profile/ProfilePhotoBanner.stories.tsx +1 -1
  44. package/components/Profile/ProfilePhotoUpload/ProfilePhotoUpload.stories.tsx +1 -1
  45. package/components/Profile/ProfileSummary/ProfileSummary.stories.tsx +52 -0
  46. package/components/Profile/ProfileSummary/ProfileSummary.tsx +188 -0
  47. package/components/Profile/ProfileTabs/UserProfileTabs.stories.tsx +1 -1
  48. package/components/Profile/index.ts +2 -0
  49. package/components/Screens/AppScreens.stories.tsx +572 -0
  50. package/components/Screens/HomeScreen.stories.tsx +159 -0
  51. package/components/Screens/LibraryScreen.stories.tsx +97 -0
  52. package/components/Settings/AppSettingsScreen.stories.tsx +134 -0
  53. package/components/Settings/AppSettingsScreen.tsx +215 -0
  54. package/components/Settings/SettingsHeader.tsx +37 -0
  55. package/components/Settings/SettingsListItem.tsx +142 -0
  56. package/components/Settings/SettingsManageAccountButton.tsx +58 -0
  57. package/components/Settings/SettingsOptionList.tsx +82 -0
  58. package/components/Settings/SettingsProfileCard.tsx +58 -0
  59. package/components/Settings/SettingsScreen.stories.tsx +66 -0
  60. package/components/Settings/SettingsScreen.tsx +129 -0
  61. package/components/Settings/SettingsSection.tsx +56 -0
  62. package/components/Settings/SettingsToggleItem.tsx +70 -0
  63. package/components/Settings/index.ts +23 -0
  64. package/components/Specialist/Hero.stories.tsx +1 -1
  65. package/components/Specialist/PortfolioCarousel.stories.tsx +1 -1
  66. package/components/Specialist/ServicesList.stories.tsx +1 -1
  67. package/components/Tooltip/DangerTooltip.stories.tsx +1 -1
  68. package/components/Tooltip/InfoTooltip.stories.tsx +1 -1
  69. package/components/Tooltip/InfoTooltipBase.stories.tsx +1 -1
  70. package/components/Tooltip/SucceedTooltip.stories.tsx +1 -1
  71. package/components/Tooltip/WarningTooltip.stories.tsx +1 -1
  72. package/components/UI/HorizontalCardSection.stories.tsx +85 -0
  73. package/components/UI/HorizontalCardSection.tsx +199 -0
  74. package/components/UI/TabBar/BottomTabBar.stories.tsx +87 -0
  75. package/components/UI/TabBar/BottomTabBar.tsx +128 -0
  76. package/components/UI/index.ts +4 -0
  77. package/components/UserCards/AiAgentHeroCard.stories.tsx +66 -0
  78. package/components/UserCards/AiAgentHeroCard.tsx +242 -0
  79. package/components/UserCards/Organizer.stories.tsx +1 -1
  80. package/components/UserCards/SpecialistCard.stories.tsx +1 -1
  81. package/components/UserCards/StoryCard.stories.tsx +1 -1
  82. package/components/UserCards/UserProfileCard.stories.tsx +1 -1
  83. package/components/UserCards/UserRow.stories.tsx +1 -1
  84. package/components/UserCards/index.ts +2 -0
  85. package/components/index.ts +2 -0
  86. package/package.json +2 -1
@@ -11,7 +11,7 @@ const IMAGES = [
11
11
  ];
12
12
 
13
13
  const meta = {
14
- title: 'Specialist/PortfolioCarousel',
14
+ title: 'Features/Specialist/PortfolioCarousel',
15
15
  component: PortfolioCarousel,
16
16
  decorators: [(Story) => <View style={{ paddingVertical: 12 }}><Story /></View>],
17
17
  argTypes: { onPressImage: { action: 'onPressImage' } },
@@ -30,7 +30,7 @@ const SERVICES: ServiceItem[] = [
30
30
  ];
31
31
 
32
32
  const meta = {
33
- title: 'Specialist/ServicesList',
33
+ title: 'Features/Specialist/ServicesList',
34
34
  component: ServicesList,
35
35
  decorators: [(Story) => <View style={{ paddingVertical: 12 }}><Story /></View>],
36
36
  argTypes: {
@@ -39,7 +39,7 @@ const renderTooltip = ({ contentKey, ...args }: DangerTooltipStoryProps) => (
39
39
  );
40
40
 
41
41
  const meta: Meta<DangerTooltipStoryProps> = {
42
- title: 'Tooltip/DangerTooltip',
42
+ title: 'UI/Tooltip/DangerTooltip',
43
43
  component: DangerTooltip,
44
44
  argTypes: {
45
45
  contentKey: {
@@ -42,7 +42,7 @@ const renderTooltip = ({ contentKey, ...args }: InfoTooltipStoryProps) => (
42
42
  );
43
43
 
44
44
  const meta: Meta<InfoTooltipStoryProps> = {
45
- title: 'Tooltip/InfoTooltip',
45
+ title: 'UI/Tooltip/InfoTooltip',
46
46
  component: InfoTooltip,
47
47
  argTypes: {
48
48
  contentKey: {
@@ -34,7 +34,7 @@ const renderTooltip = ({ contentKey, ...args }: InfoTooltipBaseStoryProps) => (
34
34
  );
35
35
 
36
36
  const meta: Meta<InfoTooltipBaseStoryProps> = {
37
- title: 'Tooltip/InfoTooltipBase',
37
+ title: 'UI/Tooltip/InfoTooltipBase',
38
38
  component: InfoTooltipBase,
39
39
  argTypes: {
40
40
  contentKey: {
@@ -39,7 +39,7 @@ const renderTooltip = ({ contentKey, ...args }: SucceedTooltipStoryProps) => (
39
39
  );
40
40
 
41
41
  const meta: Meta<SucceedTooltipStoryProps> = {
42
- title: 'Tooltip/SucceedTooltip',
42
+ title: 'UI/Tooltip/SucceedTooltip',
43
43
  component: SucceedTooltip,
44
44
  argTypes: {
45
45
  contentKey: {
@@ -41,7 +41,7 @@ const renderTooltip = ({ contentKey = 'pendingReview', ...args }: WarningTooltip
41
41
  );
42
42
 
43
43
  const meta: Meta<WarningTooltipStoryProps> = {
44
- title: 'Tooltip/WarningTooltip',
44
+ title: 'UI/Tooltip/WarningTooltip',
45
45
  component: WarningTooltip,
46
46
  argTypes: {
47
47
  contentKey: {
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { ThemeProvider } from "../../theme";
5
+ import HorizontalCardSection, { HorizontalCard } from "./HorizontalCardSection";
6
+
7
+ const cards: HorizontalCard[] = [
8
+ {
9
+ id: "1",
10
+ title: "AI Agents Meetup",
11
+ image: {
12
+ uri: "https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=600&q=80",
13
+ },
14
+ },
15
+ {
16
+ id: "2",
17
+ title: "Design Sprint Workshop",
18
+ image: {
19
+ uri: "https://images.unsplash.com/photo-1521737604893-0f34a1f438b8?auto=format&fit=crop&w=600&q=80",
20
+ },
21
+ },
22
+ {
23
+ id: "3",
24
+ title: "Community Hack Night",
25
+ image: {
26
+ uri: "https://images.unsplash.com/photo-1521737604893-9d53e79e4d4a?auto=format&fit=crop&w=600&q=80",
27
+ },
28
+ },
29
+ ];
30
+
31
+ const meta: Meta<typeof HorizontalCardSection> = {
32
+ title: "UI/Cards/HorizontalCardSection",
33
+ component: HorizontalCardSection,
34
+ decorators: [
35
+ (Story) => (
36
+ <ThemeProvider>
37
+ <Story />
38
+ </ThemeProvider>
39
+ ),
40
+ ],
41
+ argTypes: {
42
+ onPressSeeAll: { action: "see all press" },
43
+ onPressCard: { action: "card press" },
44
+ isDark: { control: "boolean" },
45
+ },
46
+ parameters: {
47
+ backgrounds: {
48
+ default: "light",
49
+ values: [
50
+ { name: "light", value: "#f4f4f6" },
51
+ { name: "dark", value: "#070C1F" },
52
+ ],
53
+ },
54
+ },
55
+ };
56
+
57
+ export default meta;
58
+
59
+ type Story = StoryObj<typeof HorizontalCardSection>;
60
+
61
+ export const Default: Story = {
62
+ args: {
63
+ title: "Popular this week",
64
+ cards,
65
+ seeAllLabel: "See all",
66
+ },
67
+ };
68
+
69
+ export const WithCustomLabel: Story = {
70
+ args: {
71
+ ...Default.args,
72
+ title: "Recommended spaces",
73
+ seeAllLabel: "View more",
74
+ },
75
+ };
76
+
77
+ export const DarkModePreview: Story = {
78
+ args: {
79
+ ...Default.args,
80
+ isDark: true,
81
+ },
82
+ parameters: {
83
+ backgrounds: { default: "dark" },
84
+ },
85
+ };
@@ -0,0 +1,199 @@
1
+ import React, { memo, useCallback, useMemo } from "react";
2
+ import {
3
+ FlatList,
4
+ ImageBackground,
5
+ ImageSourcePropType,
6
+ ListRenderItemInfo,
7
+ StyleProp,
8
+ StyleSheet,
9
+ Text,
10
+ TouchableOpacity,
11
+ View,
12
+ ViewStyle,
13
+ } from "react-native";
14
+ import { LinearGradient } from "expo-linear-gradient";
15
+
16
+ import { SizesType, ThemeType, useTheme } from "../../theme";
17
+
18
+ export type HorizontalCard = {
19
+ id?: string | number;
20
+ title: string;
21
+ image: ImageSourcePropType;
22
+ };
23
+
24
+ export interface HorizontalCardSectionProps {
25
+ title: string;
26
+ cards: HorizontalCard[];
27
+ onPressSeeAll?: () => void;
28
+ onPressCard?: (card: HorizontalCard, index: number) => void;
29
+ seeAllLabel?: string;
30
+ /**
31
+ * Force dark styles regardless of the theme. Useful for previewing in storybook.
32
+ */
33
+ isDark?: boolean;
34
+ style?: StyleProp<ViewStyle>;
35
+ contentContainerStyle?: StyleProp<ViewStyle>;
36
+ }
37
+
38
+ const CARD_WIDTH = 152;
39
+ const CARD_HEIGHT = 230;
40
+
41
+ const HorizontalCardSectionComponent: React.FC<HorizontalCardSectionProps> = ({
42
+ title,
43
+ cards,
44
+ onPressSeeAll,
45
+ onPressCard,
46
+ seeAllLabel = "See All",
47
+ isDark,
48
+ style,
49
+ contentContainerStyle,
50
+ }) => {
51
+ const { theme, typography, sizes, isDark: themeIsDark } = useTheme();
52
+ const resolvedIsDark = isDark ?? themeIsDark;
53
+
54
+ const styles = useMemo(
55
+ () => getStyles({ theme, sizes, isDark: resolvedIsDark }),
56
+ [resolvedIsDark, sizes, theme],
57
+ );
58
+
59
+ const gradientColors = useMemo(
60
+ () => ["rgba(0,0,0,0)", resolvedIsDark ? "rgba(7,12,31,0.85)" : "rgba(0,0,0,0.75)"],
61
+ [resolvedIsDark],
62
+ );
63
+
64
+ const handleCardPress = useCallback(
65
+ (card: HorizontalCard, index: number) => () => {
66
+ if (onPressCard) {
67
+ onPressCard(card, index);
68
+ }
69
+ },
70
+ [onPressCard],
71
+ );
72
+
73
+ const renderCard = useCallback(
74
+ ({ item, index }: ListRenderItemInfo<HorizontalCard>) => (
75
+ <TouchableOpacity
76
+ accessibilityLabel={item.title}
77
+ accessibilityRole={onPressCard ? "button" : undefined}
78
+ activeOpacity={0.85}
79
+ disabled={!onPressCard}
80
+ onPress={handleCardPress(item, index)}
81
+ style={styles.cardWrapper}
82
+ >
83
+ <ImageBackground source={item.image} style={styles.card} imageStyle={styles.cardImage}>
84
+ <LinearGradient colors={gradientColors} style={styles.gradient} />
85
+ <Text style={[typography.titleH6, styles.cardTitle]} numberOfLines={2}>
86
+ {item.title}
87
+ </Text>
88
+ </ImageBackground>
89
+ </TouchableOpacity>
90
+ ),
91
+ [gradientColors, handleCardPress, onPressCard, styles, typography.titleH6],
92
+ );
93
+
94
+ const keyExtractor = useCallback(
95
+ (item: HorizontalCard, index: number) => `${item.id ?? index}`,
96
+ [],
97
+ );
98
+
99
+ const listFooter = useMemo(
100
+ () => (cards.length > 0 ? <View style={styles.footerSpacer} /> : null),
101
+ [cards.length, styles.footerSpacer],
102
+ );
103
+
104
+ return (
105
+ <View style={[styles.container, style]}>
106
+ <View style={styles.header}>
107
+ <Text style={[typography.titleH4, styles.title]} numberOfLines={1}>
108
+ {title}
109
+ </Text>
110
+
111
+ {onPressSeeAll ? (
112
+ <TouchableOpacity
113
+ accessibilityRole="button"
114
+ activeOpacity={0.7}
115
+ onPress={onPressSeeAll}
116
+ style={styles.seeAllButton}
117
+ >
118
+ <Text style={[typography.bodySm, styles.seeAllText]}>{seeAllLabel}</Text>
119
+ </TouchableOpacity>
120
+ ) : null}
121
+ </View>
122
+
123
+ <FlatList
124
+ horizontal
125
+ data={cards}
126
+ keyExtractor={keyExtractor}
127
+ renderItem={renderCard}
128
+ showsHorizontalScrollIndicator={false}
129
+ contentContainerStyle={[styles.listContent, contentContainerStyle]}
130
+ ListFooterComponent={listFooter}
131
+ />
132
+ </View>
133
+ );
134
+ };
135
+
136
+ const getStyles = ({
137
+ theme,
138
+ sizes,
139
+ isDark,
140
+ }: {
141
+ theme: ThemeType;
142
+ sizes: SizesType;
143
+ isDark: boolean;
144
+ }) =>
145
+ StyleSheet.create({
146
+ container: {
147
+ gap: sizes.md,
148
+ },
149
+ header: {
150
+ flexDirection: "row",
151
+ alignItems: "center",
152
+ justifyContent: "space-between",
153
+ paddingHorizontal: sizes.xs,
154
+ },
155
+ title: {
156
+ color: theme.title,
157
+ },
158
+ seeAllButton: {
159
+ paddingVertical: sizes.xs,
160
+ paddingHorizontal: sizes.sm,
161
+ borderRadius: sizes.radius_sm,
162
+ },
163
+ seeAllText: {
164
+ color: isDark ? theme.white : theme.primary,
165
+ fontWeight: "500",
166
+ },
167
+ listContent: {
168
+ paddingHorizontal: sizes.xs,
169
+ },
170
+ cardWrapper: {
171
+ marginRight: sizes.md,
172
+ },
173
+ card: {
174
+ width: CARD_WIDTH,
175
+ height: CARD_HEIGHT,
176
+ borderRadius: sizes.radius_lg,
177
+ overflow: "hidden",
178
+ justifyContent: "flex-end",
179
+ backgroundColor: theme.background,
180
+ },
181
+ cardImage: {
182
+ borderRadius: sizes.radius_lg,
183
+ },
184
+ gradient: {
185
+ ...StyleSheet.absoluteFillObject,
186
+ },
187
+ cardTitle: {
188
+ color: theme.white,
189
+ paddingHorizontal: sizes.sm,
190
+ paddingBottom: sizes.sm,
191
+ },
192
+ footerSpacer: {
193
+ width: sizes.md,
194
+ },
195
+ });
196
+
197
+ export const HorizontalCardSection = memo(HorizontalCardSectionComponent);
198
+
199
+ export default HorizontalCardSection;
@@ -0,0 +1,87 @@
1
+ import React, { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { View } from 'react-native';
4
+ import { MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons';
5
+ import BottomTabBar, { BottomTabBarItem } from './BottomTabBar';
6
+
7
+ const ITEMS: BottomTabBarItem[] = [
8
+ {
9
+ key: 'overview',
10
+ renderIcon: ({ color, size }) => (
11
+ <MaterialCommunityIcons name="view-grid-outline" size={size} color={color} />
12
+ ),
13
+ },
14
+ {
15
+ key: 'private',
16
+ renderIcon: ({ color, size }) => <MaterialIcons name="lock-outline" size={size} color={color} />,
17
+ },
18
+ {
19
+ key: 'bookmarks',
20
+ renderIcon: ({ color, size }) => (
21
+ <MaterialCommunityIcons name="bookmark-off-outline" size={size} color={color} />
22
+ ),
23
+ },
24
+ {
25
+ key: 'favorites',
26
+ renderIcon: ({ color, size }) => (
27
+ <MaterialCommunityIcons name="heart-off-outline" size={size} color={color} />
28
+ ),
29
+ },
30
+ ];
31
+
32
+ const meta = {
33
+ title: 'UI/BottomTabBar',
34
+ component: BottomTabBar,
35
+ decorators: [(Story) => <View style={{ paddingTop: 24 }}><Story /></View>],
36
+ argTypes: {
37
+ onChange: { action: 'onChange' },
38
+ },
39
+ args: {
40
+ items: ITEMS,
41
+ },
42
+ } satisfies Meta<typeof BottomTabBar>;
43
+
44
+ export default meta;
45
+
46
+ type Story = StoryObj<typeof BottomTabBar>;
47
+
48
+ const Controlled = (props: React.ComponentProps<typeof BottomTabBar>) => {
49
+ const [active, setActive] = useState(0);
50
+
51
+ return (
52
+ <BottomTabBar
53
+ {...props}
54
+ activeIndex={active}
55
+ onChange={(index) => {
56
+ setActive(index);
57
+ props.onChange?.(index);
58
+ }}
59
+ />
60
+ );
61
+ };
62
+
63
+ export const Default: Story = {
64
+ render: (args) => <Controlled {...args} />,
65
+ };
66
+
67
+ export const CustomColors: Story = {
68
+ render: (args) => <Controlled {...args} />,
69
+ args: {
70
+ activeColor: '#111',
71
+ inactiveColor: '#B0B0B0',
72
+ indicatorColor: '#111',
73
+ indicatorWidth: 36,
74
+ },
75
+ };
76
+
77
+ export const WithDisabled: Story = {
78
+ render: (args) => <Controlled {...args} />,
79
+ args: {
80
+ items: [
81
+ ITEMS[0],
82
+ ITEMS[1],
83
+ { ...ITEMS[2], disabled: true },
84
+ ITEMS[3],
85
+ ],
86
+ },
87
+ };
@@ -0,0 +1,128 @@
1
+ import React, { FC, memo, useMemo } from 'react';
2
+ import { Pressable, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
3
+ import { useTheme, ThemeType } from '../../../theme';
4
+
5
+ export type BottomTabBarIconProps = {
6
+ color: string;
7
+ size: number;
8
+ };
9
+
10
+ export type BottomTabBarItem = {
11
+ key: string;
12
+ renderIcon: (props: BottomTabBarIconProps) => React.ReactNode;
13
+ disabled?: boolean;
14
+ };
15
+
16
+ type BottomTabBarProps = {
17
+ items: BottomTabBarItem[];
18
+ activeIndex: number;
19
+ onChange: (index: number) => void;
20
+ style?: StyleProp<ViewStyle>;
21
+ activeColor?: string;
22
+ inactiveColor?: string;
23
+ indicatorColor?: string;
24
+ indicatorWidth?: number;
25
+ indicatorHeight?: number;
26
+ iconSize?: number;
27
+ pressableStyle?: StyleProp<ViewStyle>;
28
+ };
29
+
30
+ const BottomTabBar: FC<BottomTabBarProps> = memo(
31
+ ({
32
+ items,
33
+ activeIndex,
34
+ onChange,
35
+ style,
36
+ pressableStyle,
37
+ activeColor,
38
+ inactiveColor,
39
+ indicatorColor,
40
+ indicatorWidth = 32,
41
+ indicatorHeight = 3,
42
+ iconSize = 26,
43
+ }) => {
44
+ const { theme } = useTheme();
45
+ const styles = useMemo(() => getStyles({ theme, indicatorHeight }), [theme, indicatorHeight]);
46
+
47
+ const resolvedActiveColor = activeColor ?? theme.black;
48
+ const resolvedInactiveColor = inactiveColor ?? theme.greyText;
49
+ const resolvedIndicatorColor = indicatorColor ?? theme.black;
50
+
51
+ return (
52
+ <View style={[styles.container, style]} accessibilityRole="tablist">
53
+ {items.map((item, index) => {
54
+ const isActive = index === activeIndex;
55
+ const color = isActive ? resolvedActiveColor : resolvedInactiveColor;
56
+
57
+ return (
58
+ <Pressable
59
+ key={item.key}
60
+ onPress={() => onChange(index)}
61
+ disabled={item.disabled}
62
+ style={({ pressed }) => [
63
+ styles.item,
64
+ pressableStyle,
65
+ item.disabled && styles.disabled,
66
+ pressed && !item.disabled ? styles.pressed : null,
67
+ ]}
68
+ accessibilityRole="tab"
69
+ accessibilityState={{ selected: isActive, disabled: !!item.disabled }}
70
+ >
71
+ <View style={styles.iconWrapper}>{item.renderIcon({ color, size: iconSize })}</View>
72
+ <View
73
+ style={[
74
+ styles.indicator,
75
+ {
76
+ backgroundColor: isActive ? resolvedIndicatorColor : 'transparent',
77
+ width: indicatorWidth,
78
+ },
79
+ ]}
80
+ />
81
+ </Pressable>
82
+ );
83
+ })}
84
+ </View>
85
+ );
86
+ },
87
+ );
88
+
89
+ BottomTabBar.displayName = 'BottomTabBar';
90
+
91
+ const getStyles = ({ theme, indicatorHeight }: { theme: ThemeType; indicatorHeight: number }) =>
92
+ StyleSheet.create({
93
+ container: {
94
+ flexDirection: 'row',
95
+ justifyContent: 'space-around',
96
+ alignItems: 'center',
97
+ paddingTop: 12,
98
+ paddingBottom: 6,
99
+ backgroundColor: theme.white,
100
+ borderTopWidth: 1,
101
+ borderTopColor: theme.border,
102
+ },
103
+ item: {
104
+ flex: 1,
105
+ alignItems: 'center',
106
+ justifyContent: 'center',
107
+ paddingHorizontal: 4,
108
+ },
109
+ iconWrapper: {
110
+ height: 28,
111
+ justifyContent: 'center',
112
+ alignItems: 'center',
113
+ marginBottom: 8,
114
+ },
115
+ indicator: {
116
+ height: indicatorHeight,
117
+ borderRadius: indicatorHeight / 2,
118
+ marginTop: 2,
119
+ },
120
+ pressed: {
121
+ opacity: 0.7,
122
+ },
123
+ disabled: {
124
+ opacity: 0.4,
125
+ },
126
+ });
127
+
128
+ export default BottomTabBar;
@@ -10,6 +10,8 @@ export { LinkPreview } from './LinkPreview';
10
10
  export { default as ListBlockItem } from './ListBlockItem';
11
11
  export { default as ListItem } from './ListItem';
12
12
  export { default as LoadingScreen } from './LoadingScreen';
13
+ export { default as HorizontalCardSection } from './HorizontalCardSection';
14
+ export type { HorizontalCardSectionProps, HorizontalCard } from './HorizontalCardSection';
13
15
  export { NoAuth } from './NoAuth';
14
16
  export { ParticipantItem } from './ParticipantItem';
15
17
  export * from './Social';
@@ -20,6 +22,8 @@ export { default as TabBar } from './TabBar/TabBar';
20
22
  export type { TabItem } from './TabBar/TabBar';
21
23
  export { default as TabBarAi } from './TabBar/TabBarAi';
22
24
  export type { TabItem as TabItemAi } from './TabBar/TabBarAi';
25
+ export { default as BottomTabBar } from './TabBar/BottomTabBar';
26
+ export type { BottomTabBarItem, BottomTabBarIconProps } from './TabBar/BottomTabBar';
23
27
  export { default as TabButton } from './TabButton/TabButton';
24
28
  export type { TabButtonProps } from './TabButton/TabButton';
25
29
  export { default as TextWithLinks } from './TextWithLinks';
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+ import { View } from 'react-native';
4
+
5
+ import AiAgentHeroCard from './AiAgentHeroCard';
6
+
7
+ const meta = {
8
+ title: 'Features/UserCards/AiAgentHeroCard',
9
+ component: AiAgentHeroCard,
10
+ decorators: [
11
+ (Story) => (
12
+ <View style={{ padding: 16 }}>
13
+ <Story />
14
+ </View>
15
+ ),
16
+ ],
17
+ args: {
18
+ avatarUri: 'https://i.pravatar.cc/150?img=47',
19
+ displayName: 'Алина Лебедева',
20
+ profession: 'HR-аналитик и карьерный консультант',
21
+ categories: ['HR', 'Карьера', 'AI-инструменты'],
22
+ followButtonTitle: 'Подписаться',
23
+ isFollowUpdating: false,
24
+ disableFollowAction: false,
25
+ isChatLoading: false,
26
+ aiBotId: 'ai-agent-42',
27
+ isFollowing: false,
28
+ },
29
+ argTypes: {
30
+ onToggleFollow: { action: 'toggle-follow' },
31
+ onStartChat: { action: 'start-chat' },
32
+ onAvatarPress: { action: 'avatar-press' },
33
+ },
34
+ } satisfies Meta<typeof AiAgentHeroCard>;
35
+
36
+ export default meta;
37
+
38
+ type Story = StoryObj<typeof AiAgentHeroCard>;
39
+
40
+ export const Default: Story = {};
41
+
42
+ export const Following: Story = {
43
+ args: {
44
+ isFollowing: true,
45
+ followButtonTitle: 'Отписаться',
46
+ },
47
+ };
48
+
49
+ export const LoadingStates: Story = {
50
+ args: {
51
+ isFollowUpdating: true,
52
+ isChatLoading: true,
53
+ },
54
+ };
55
+
56
+ export const WithoutCategories: Story = {
57
+ args: {
58
+ categories: [],
59
+ },
60
+ };
61
+
62
+ export const ChatUnavailable: Story = {
63
+ args: {
64
+ aiBotId: undefined,
65
+ },
66
+ };