Поставьте зависимости:
npm install @expo/vector-icons react-native-reanimatedpnpm add @expo/vector-icons react-native-reanimatedyarn add @expo/vector-icons react-native-reanimatedbun add @expo/vector-icons react-native-reanimatedСкопируйте эти файлы в src/shared/components/molecules/bouncy-accordion/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import React, { useState } from "react";
import { ScrollView, StyleSheet, Text, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Feather } from "@expo/vector-icons";
import { BouncyAccordion } from "@/shared/components/molecules/bouncy-accordion";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";
import { SFSymbol, SymbolView } from "expo-symbols";
import { useFonts } from "expo-font";
const _DS_TOKENS = {
bg: "#121212",
label: "#ebebeb",
};
interface AccordionContent {
value: string;
label: string;
content: string;
icon: SFSymbol;
}
const _ACCORDION_CONTENT: AccordionContent[] = [
{
value: "what-is-Astra",
label: "What is Astra?",
content:
"Astra is a collection of beautifully crafted React Native components, animations, and utilities designed to help you build polished mobile apps faster.",
icon: "sparkles",
},
{
value: "is-Astra-free",
label: "Is Astra free?",
content:
"Most components, all logos and agent skills are free to use in personal and commercial apps. Components marked Pro come with an Astra Pro subscription.",
icon: "gift",
},
{
value: "expo-support",
label: "Does it support Expo?",
content:
"Absolutely. Astra is built with Expo and React Native in mind, while also supporting bare React Native projects whenever possible.",
icon: "iphone",
},
{
value: "customization",
label: "Can I customize the components?",
content:
"Every component is designed to be customizable. You can adjust colors, spacing, animations, and behavior to match your app's design system.",
icon: "paintbrush",
},
];
const CUSTOMIZE = [
{ key: "title", label: "Заголовок", type: "text", default: "FAQ", maxLength: 30 },
{ key: "subtitle", label: "Подзаголовок", type: "text", default: "Common questions", maxLength: 40 },
{ key: "itemColor", label: "Фон пунктов", type: "color", default: _DS_TOKENS.bg },
{ key: "labelColor", label: "Цвет вопросов", type: "color", default: _DS_TOKENS.label },
{ key: "titleColor", label: "Цвет заголовка", type: "color", default: "#ffffff" },
{ key: "gap", label: "Промежуток", type: "slider", min: 0, max: 32, step: 1, unit: "px", default: 12 },
{ key: "radius", label: "Скругление", type: "slider", min: 0, max: 40, step: 1, unit: "px", default: 28 },
{ key: "step", label: "Шаг автопоказа", type: "slider", min: 300, max: 2000, step: 10, unit: "ms", default: 720 },
] as const;
export default function BouncyAccordionScreen() {
const custom = useCustomize(CUSTOMIZE);
const [fontLoaded] = useFonts({
SfProRounded: require("@/assets/fonts/sf-pro-rounded.ttf"),
SfProRoundedBold: require("~/assets/fonts/SF-Pro-Rounded-Bold.otf"),
SfProRoundedMedium: require("~/assets/fonts/SF-Pro-Rounded-Medium.otf"),
});
const [openValue, setOpenValue] = useState<string | null>(null);
useAutoplay(async ({ wait }) => {
await wait(300);
for (const item of _ACCORDION_CONTENT) {
setOpenValue(item.value);
await wait(custom.step);
}
setOpenValue(_ACCORDION_CONTENT[0]!.value);
await wait(850);
setOpenValue(null);
await wait(500);
});
return (
<Showcase>
<View style={styles.content}>
<View style={styles.stage}>
<Text
style={[
styles.headingTitle,
{ color: custom.titleColor },
fontLoaded && { fontFamily: "SfProRoundedBold" },
]}
>
{custom.title}
</Text>
<Text
style={[
styles.headingSubtitle,
fontLoaded && { fontFamily: "SfProRounded" },
]}
>
{custom.subtitle}
</Text>
</View>
<BouncyAccordion.Root
value={openValue}
onValueChange={setOpenValue}
gap={custom.gap}
radius={custom.radius}
>
{_ACCORDION_CONTENT.map((item, index) => (
<BouncyAccordion.Item
value={item.value}
style={[styles.item, { backgroundColor: custom.itemColor }]}
key={index.toString()}
>
<BouncyAccordion.Trigger>
<BouncyAccordion.Trigger.Icon>
<SymbolView
name={item.icon}
size={18}
tintColor={custom.labelColor}
/>
</BouncyAccordion.Trigger.Icon>
<BouncyAccordion.Trigger.Label
style={[styles.title, { color: custom.labelColor }]}
>
{item.label}
</BouncyAccordion.Trigger.Label>
</BouncyAccordion.Trigger>
<BouncyAccordion.Content>{item.content}</BouncyAccordion.Content>
</BouncyAccordion.Item>
))}
</BouncyAccordion.Root>
</View>
</Showcase>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "#f2f2f7" },
content: { flex: 1, justifyContent: "center", padding: 20 },
stage: {
paddingHorizontal: 12,
},
title: {
color: _DS_TOKENS.label,
},
item: {
backgroundColor: _DS_TOKENS.bg,
},
headingTitle: {
fontSize: 32,
color: "#fff",
},
headingSubtitle: {
fontSize: 15,
color: "#555",
marginBottom: 32,
},
});childrenТип ReactNode
value?Controlled open value.
Тип TBouncyAccordionValue
defaultValue?Uncontrolled initial open value.
Тип TBouncyAccordionValue
onValueChange?Тип (value: TBouncyAccordionValue) => void
collapsible?Allow closing the open item by tapping it again.
Тип boolean
gap?Gap that opens up between connected groups.
Тип number
radius?Corner radius applied to the outer edges of each group.
Тип number
style?Тип StyleProp<ViewStyle>