rn-vs-lb 1.0.66 → 1.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,5 +78,6 @@ yarn build-storybook
78
78
  6. **Опубликуйте релиз**. Убедившись, что версия не занята, выполните:
79
79
  ```sh
80
80
  npm publish --access public
81
+ npm publish --access public --otp=RECOVERY_CODE
81
82
  ```
82
83
  7. **Проверьте публикацию**. После успешной публикации обновите теги в git (`git tag vX.Y.Z && git push --tags`) и убедитесь, что пакет появился на npmjs.com.
@@ -265,11 +265,12 @@ export const getStyles = (theme: ThemeType) =>
265
265
  paddingHorizontal: 10,
266
266
  paddingVertical: 6,
267
267
  borderRadius: 4,
268
+ top:4,
268
269
  borderColor: theme.white,
269
270
  },
270
271
  sendButton: {
271
272
  position: 'absolute',
272
- bottom: 9,
273
+ bottom: 8,
273
274
  right: 8,
274
275
  borderRadius: 4,
275
276
  justifyContent: 'center',
@@ -3,6 +3,7 @@ import React, { FC, useMemo } from 'react';
3
3
  import { Text } from 'react-native';
4
4
  import { ThemeType } from '../../../theme';
5
5
  import { getStyles } from './styles';
6
+ import { formatBoldText } from '../../../utils';
6
7
 
7
8
  interface LinkyTextProps {
8
9
  text: string;
@@ -21,6 +22,13 @@ export const LinkyText: FC<LinkyTextProps> = ({ text, theme, onLinkPress, onLong
21
22
  onLinkPress?.(url);
22
23
  };
23
24
 
25
+ const renderBoldParts = (segment: string, keyPrefix: string) =>
26
+ formatBoldText(segment).map((part, index) => (
27
+ <Text key={`${keyPrefix}-${index}`} style={part.bold ? styles.messageTextBold : undefined}>
28
+ {part.text}
29
+ </Text>
30
+ ));
31
+
24
32
  return (
25
33
  <Text style={styles.messageText}>
26
34
  {parts.map((part, i) =>
@@ -35,7 +43,7 @@ export const LinkyText: FC<LinkyTextProps> = ({ text, theme, onLinkPress, onLong
35
43
  {part}
36
44
  </Text>
37
45
  ) : (
38
- <Text key={i}>{part}</Text>
46
+ renderBoldParts(part, `text-${i}`)
39
47
  )
40
48
  )}
41
49
  </Text>
@@ -68,6 +68,9 @@ export const getStyles = ({ theme }: { theme: ThemeType }) =>
68
68
  fontSize: 15,
69
69
  color: theme.black,
70
70
  },
71
+ messageTextBold: {
72
+ fontWeight: 'bold',
73
+ },
71
74
  messageFooter: {
72
75
  flexDirection: 'row',
73
76
  alignItems: 'center',
@@ -62,7 +62,7 @@ const ReportModal: React.FC<Props> = ({
62
62
  <View style={styles.container}>
63
63
  <Text style={styles.title}>{title}</Text>
64
64
  <ScrollView style={styles.reasonsContainer}>
65
- {reasons.map((reason) => (
65
+ {reasons?.map((reason) => (
66
66
  <TouchableOpacity
67
67
  key={reason}
68
68
  style={[
@@ -0,0 +1,299 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import { Meta, StoryFn } from '@storybook/react';
3
+ import { FlatList, ScrollView, StyleSheet, Text, View } from 'react-native';
4
+ import { Feather, MaterialCommunityIcons } from '@expo/vector-icons';
5
+
6
+ import { ThemeProvider, createAppTheme, useTheme } from '../../theme';
7
+ import HeaderDefault from '../Header/HeaderDefault';
8
+ import TabBar from '../UI/TabBar/TabBarAi';
9
+ import { darkTheme, SIZES } from '../../theme/theme';
10
+ import { HeaderHome } from '../Header';
11
+
12
+ const darkAppTheme = createAppTheme({
13
+ light: darkTheme,
14
+ dark: darkTheme,
15
+ });
16
+
17
+ const previewStyles = StyleSheet.create({
18
+ previewBackground: {
19
+ flex: 1,
20
+ alignItems: 'center',
21
+ justifyContent: 'center',
22
+ paddingVertical: 48,
23
+ backgroundColor: '#050A1B',
24
+ },
25
+ });
26
+
27
+ const meta: Meta = {
28
+ title: 'Screens/Horoscope/DailyOverview',
29
+ component: () => null,
30
+ decorators: [
31
+ (Story) => (
32
+ <ThemeProvider theme={darkAppTheme}>
33
+ <View style={previewStyles.previewBackground}>
34
+ <Story />
35
+ </View>
36
+ </ThemeProvider>
37
+ ),
38
+ ],
39
+ };
40
+
41
+ export default meta;
42
+
43
+ type Story = StoryFn;
44
+
45
+ type HoroscopeCardData = {
46
+ key: string;
47
+ title: string;
48
+ description: string;
49
+ icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];
50
+ accent: string;
51
+ };
52
+
53
+ const cards: HoroscopeCardData[] = [
54
+ {
55
+ key: 'career',
56
+ title: 'Карьера',
57
+ description:
58
+ 'Луна во Льве помогает сфокусироваться на долгосрочных целях и заметить новые возможности роста.',
59
+ icon: 'briefcase-variant-outline',
60
+ accent: '#63B3FF',
61
+ },
62
+ {
63
+ key: 'love',
64
+ title: 'Любовь',
65
+ description:
66
+ 'В отношениях сегодня больше тепла. Откровенный разговор сделает связь сильнее.',
67
+ icon: 'heart-outline',
68
+ accent: '#FF7AB8',
69
+ },
70
+ {
71
+ key: 'health',
72
+ title: 'Здоровье',
73
+ description:
74
+ 'Добавьте к привычному распорядку короткую разминку — организм отблагодарит энергией.',
75
+ icon: 'heart-pulse',
76
+ accent: '#7DE2AC',
77
+ },
78
+ {
79
+ key: 'family',
80
+ title: 'Семья',
81
+ description:
82
+ 'Совместный вечер укрепит доверие. Запланируйте семейный ритуал, чтобы повторить его позже.',
83
+ icon: 'account-group-outline',
84
+ accent: '#F7C977',
85
+ },
86
+ ];
87
+
88
+ const tabs = [
89
+ { key: 'yesterday', label: 'Вчера' },
90
+ { key: 'today', label: 'Сегодня' },
91
+ { key: 'tomorrow', label: 'Завтра' },
92
+ { key: 'week', label: 'На неделю' },
93
+ { key: 'month', label: 'На месяц' },
94
+ ];
95
+
96
+ const ScreenSurface: React.FC<React.PropsWithChildren> = ({ children }) => {
97
+ const { theme, sizes } = useTheme();
98
+ const surfaceStyles = useMemo(
99
+ () =>
100
+ StyleSheet.create({
101
+ surface: {
102
+ backgroundColor: theme.background,
103
+ borderRadius: sizes.radius_lg as number,
104
+ paddingHorizontal: sizes.xs as number,
105
+ paddingVertical: sizes.xs as number,
106
+ width: 360,
107
+ minHeight: 640,
108
+ gap: sizes.lg as number,
109
+ },
110
+ }),
111
+ [theme, sizes],
112
+ );
113
+
114
+ return <View style={surfaceStyles.surface}>{children}</View>;
115
+ };
116
+
117
+ const HoroscopeDescription: React.FC = () => {
118
+ const { theme, sizes, typography } = useTheme();
119
+ const styles = useMemo(
120
+ () =>
121
+ StyleSheet.create({
122
+ container: {
123
+ backgroundColor: theme.card,
124
+ borderRadius: sizes.radius_lg as number,
125
+ paddingHorizontal: sizes.lg as number,
126
+ paddingVertical: sizes.lg as number,
127
+ gap: sizes.sm as number,
128
+ },
129
+ title: {
130
+ ...typography.titleH5,
131
+ },
132
+ text: {
133
+ ...typography.body,
134
+ lineHeight: 20,
135
+ },
136
+ }),
137
+ [theme, sizes, typography],
138
+ );
139
+
140
+ return (
141
+ <View style={styles.container}>
142
+ <Text style={styles.title}>Понедельник, 10 ноября</Text>
143
+ <Text style={styles.text}>
144
+ Сегодня, Козерог, энергия Луны во Льве помогает смело заявить о себе. Используйте этот заряд, чтобы показать
145
+ свои идеи и таланты, а также поддержать тех, кто рядом с вами.
146
+ </Text>
147
+ <Text style={styles.text}>
148
+ День отлично подходит для проектов, что зажигают вас изнутри. Делитесь вдохновением и не бойтесь инициативы —
149
+ это поможет получить заслуженное внимание.
150
+ </Text>
151
+ </View>
152
+ );
153
+ };
154
+
155
+ const CARD_WIDTH = 280;
156
+
157
+ const HoroscopeCard: React.FC<{ item: HoroscopeCardData }> = ({ item }) => {
158
+ const { theme, sizes, typography } = useTheme();
159
+ const styles = useMemo(
160
+ () =>
161
+ StyleSheet.create({
162
+ card: {
163
+ backgroundColor: theme.card,
164
+ borderRadius: sizes.radius_lg as number,
165
+ padding: sizes.lg as number,
166
+ width: CARD_WIDTH,
167
+ minHeight: 156,
168
+ justifyContent: 'space-between',
169
+ },
170
+ header: {
171
+ flexDirection: 'row',
172
+ justifyContent: 'space-between',
173
+ alignItems: 'flex-start',
174
+ },
175
+ iconWrapper: {
176
+ backgroundColor: item.accent,
177
+ borderRadius: sizes.radius as number,
178
+ padding: sizes.sm as number,
179
+ },
180
+ title: {
181
+ marginTop: sizes.sm as number,
182
+ ...typography.titleH6,
183
+ },
184
+ description: {
185
+ marginTop: sizes.xs as number,
186
+ ...typography.body,
187
+ },
188
+ }),
189
+ [theme, sizes, typography, item.accent],
190
+ );
191
+
192
+ return (
193
+ <View style={styles.card}>
194
+ <View style={styles.header}>
195
+ <View style={styles.iconWrapper}>
196
+ <MaterialCommunityIcons name={item.icon} size={24} color={theme.background} />
197
+ </View>
198
+ <Feather name="lock" size={18} color={theme.greyText} />
199
+ </View>
200
+ <Text style={styles.title}>{item.title}</Text>
201
+ <Text numberOfLines={3} style={styles.description}>
202
+ {item.description}
203
+ </Text>
204
+ </View>
205
+ );
206
+ };
207
+
208
+ const HoroscopeCardsCarousel: React.FC = () => {
209
+ const { sizes } = useTheme();
210
+ const GAP = 16;
211
+ const contentPad = 0;
212
+
213
+ const getItemLayout = (_: unknown, index: number) => ({
214
+ length: CARD_WIDTH + GAP,
215
+ offset: (CARD_WIDTH + GAP) * index + contentPad,
216
+ index,
217
+ });
218
+
219
+ return (
220
+ <FlatList
221
+ horizontal
222
+ data={cards}
223
+ keyExtractor={(it) => it.key}
224
+ showsHorizontalScrollIndicator={false}
225
+ contentContainerStyle={{ paddingHorizontal: contentPad }}
226
+ ItemSeparatorComponent={() => <View style={{ width: GAP }} />}
227
+ renderItem={({ item }) => <HoroscopeCard item={item} />}
228
+ getItemLayout={getItemLayout}
229
+ snapToAlignment="start"
230
+ decelerationRate="fast"
231
+ snapToInterval={CARD_WIDTH + GAP}
232
+ />
233
+ );
234
+ };
235
+
236
+ const HoroscopeTabBar: React.FC<{
237
+ activeIndex: number;
238
+ onChange: (index: number) => void;
239
+ }> = ({ activeIndex, onChange }) => {
240
+ const { theme } = useTheme();
241
+ const styles = useMemo(
242
+ () =>
243
+ StyleSheet.create({
244
+ wrapper: {
245
+ backgroundColor: 'transparent',
246
+ paddingVertical: 4,
247
+ },
248
+ divider: {
249
+ height: 1,
250
+ backgroundColor: theme.border,
251
+ marginTop: 8,
252
+ },
253
+ }),
254
+ [theme],
255
+ );
256
+
257
+ return (
258
+ <View>
259
+ <View style={styles.wrapper}>
260
+ <TabBar
261
+ tabs={tabs}
262
+ activeIndex={activeIndex}
263
+ onChange={onChange}
264
+ activeColor={theme.title}
265
+ inactiveColor="rgba(255,255,255,0.45)"
266
+ indicatorColor="#F7C977"
267
+ indicatorHeight={3}
268
+ fontSize={16}
269
+ fontWeightActive="700"
270
+ fontWeightInactive="500"
271
+ tabHorizontalPadding={8}
272
+ gap={20}
273
+ />
274
+ </View>
275
+ <View style={styles.divider} />
276
+ </View>
277
+ );
278
+ };
279
+
280
+ export const HoroscopeDailyOverview: Story = () => {
281
+ const [activeTab, setActiveTab] = useState(1);
282
+
283
+ return (
284
+ <ScreenSurface>
285
+ <HeaderHome
286
+ logo={<Text style={{ paddingHorizontal: SIZES.xs, fontSize: 24, fontWeight: '700', letterSpacing: 1, color: 'white' }}>CityLife</Text>}
287
+ onPress={() => console.log('filters')}
288
+ />
289
+ <HoroscopeTabBar activeIndex={activeTab} onChange={setActiveTab} />
290
+ <ScrollView
291
+ showsVerticalScrollIndicator={false}
292
+ contentContainerStyle={{ gap: 24, paddingBottom: 32 }}
293
+ >
294
+ <HoroscopeDescription />
295
+ <HoroscopeCardsCarousel />
296
+ </ScrollView>
297
+ </ScreenSurface>
298
+ );
299
+ };
package/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './components';
2
+ export * from './utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-vs-lb",
3
- "version": "1.0.66",
3
+ "version": "1.0.67",
4
4
  "description": "Expo Router + Storybook template ready for npm distribution.",
5
5
  "keywords": [
6
6
  "expo",
@@ -79,6 +79,7 @@
79
79
  "@types/react": "~19.1.10",
80
80
  "babel-plugin-react-docgen-typescript": "^1.5.1",
81
81
  "cross-env": "^10.0.0",
82
+ "react-icons": "^5.5.0",
82
83
  "storybook": "^9.1.8",
83
84
  "typescript": "~5.9.2",
84
85
  "vite": "^7.1.7"
package/theme/index.ts CHANGED
@@ -4,6 +4,8 @@ export {
4
4
  FONTS,
5
5
  SIZES,
6
6
  createAppTheme,
7
+ lightTheme,
8
+ darkTheme,
7
9
  default as appTheme,
8
10
  } from "./theme";
9
11
  export type { AppThemeConfig, ThemeColors, ThemeOverrides, ThemeShape } from "./theme";