Поставьте зависимости:
npm install expo-blur expo-haptics react-native-reanimatedpnpm add expo-blur expo-haptics react-native-reanimatedyarn add expo-blur expo-haptics react-native-reanimatedbun add expo-blur expo-haptics react-native-reanimatedСкопируйте эти файлы в src/shared/components/micro-interactions/stacked-chips/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import { useRef, useState } from "react";
import { View, Text, StyleSheet, useWindowDimensions } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useFonts } from "expo-font";
import { SymbolView } from "expo-symbols";
import { StackedChips } from "@/shared/components/micro-interactions/stacked-chips";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";
import sfProRounded from "@/assets/fonts/sf-pro-rounded.ttf";
import helveticaNowDisplayMedium from "@/assets/fonts/HelveticaNowDisplayMedium.ttf";
const CUSTOMIZE = [
{ key: "create", label: "Чип 1", type: "text", default: "Create", maxLength: 20 },
{ key: "document", label: "Чип 2", type: "text", default: "Document", maxLength: 20 },
{ key: "folder", label: "Чип 3", type: "text", default: "Folder", maxLength: 20 },
{ key: "photo", label: "Чип 4", type: "text", default: "Photo", maxLength: 20 },
{ key: "primary", label: "Цвет главного", type: "color", default: "#ffffff" },
{ key: "primaryText", label: "Текст главного", type: "color", default: "#000000" },
{ key: "secondary", label: "Цвет чипа 2", type: "color", default: "#333333" },
{ key: "tertiary", label: "Цвет чипа 3", type: "color", default: "#222222" },
{ key: "quaternary", label: "Цвет чипа 4", type: "color", default: "#1a1a1a" },
{ key: "textColor", label: "Цвет текста", type: "color", default: "#ffffff" },
{ key: "step", label: "Шаг автопоказа", type: "slider", min: 200, max: 3000, step: 50, unit: "ms", default: 900 },
{ key: "hold", label: "Пауза раскрытия", type: "slider", min: 300, max: 5000, step: 50, unit: "ms", default: 1700 },
] as const;
const SAME = (value: string, original: string) => (value === original ? undefined : value);
export default function App() {
const [fontLoaded] = useFonts({
SfProRounded: sfProRounded,
HelveticaNowDisplay: helveticaNowDisplayMedium,
});
const [createOpen, setCreateOpen] = useState(false);
const [documentOpen, setDocumentOpen] = useState(false);
const [folderOpen, setFolderOpen] = useState(false);
const custom = useCustomize(CUSTOMIZE);
const compact = useWindowDimensions().width < 440;
const timingRef = useRef({ step: custom.step, hold: custom.hold });
timingRef.current = { step: custom.step, hold: custom.hold };
const textTint = SAME(custom.textColor, "#ffffff");
const labelColor = textTint ? { color: textTint } : null;
const iconTint = textTint ?? "#fff";
useAutoplay(async ({ wait }) => {
await wait(500);
setCreateOpen(true);
await wait(timingRef.current.step);
setDocumentOpen(true);
await wait(timingRef.current.step);
setFolderOpen(true);
await wait(timingRef.current.hold);
setFolderOpen(false);
await wait(300);
setDocumentOpen(false);
await wait(350);
setCreateOpen(false);
await wait(1200);
});
return (
<Showcase>
<GestureHandlerRootView style={styles.container}>
<StatusBar style="light" />
<StackedChips open={createOpen}>
<StackedChips.Trigger onPress={() => setCreateOpen((v) => !v)}>
<View
style={[
styles.chip,
styles.chipPrimary,
{ backgroundColor: SAME(custom.primary, "#ffffff") ?? "#fff" },
]}
>
<SymbolView
name="plus"
size={16}
tintColor={SAME(custom.primaryText, "#000000") ?? "#000"}
/>
<Text
style={[
styles.chipText,
fontLoaded && { fontFamily: "SfProRounded" },
{
color: SAME(custom.primaryText, "#000000") ?? "#000",
},
]}
>
{custom.create}
</Text>
</View>
</StackedChips.Trigger>
<StackedChips.Content>
<StackedChips open={documentOpen}>
<StackedChips.Trigger onPress={() => setDocumentOpen((v) => !v)}>
<View
style={[
styles.chip,
styles.chipSecondary,
compact && styles.chipCompact,
{
marginLeft: 20,
},
{ backgroundColor: SAME(custom.secondary, "#333333") ?? "#333" },
]}
>
<SymbolView name="doc.fill" size={16} tintColor={iconTint} />
<Text
style={[
styles.chipText,
fontLoaded && { fontFamily: "SfProRounded" },
labelColor,
]}
>
{custom.document}
</Text>
</View>
</StackedChips.Trigger>
<StackedChips.Content>
<StackedChips open={folderOpen}>
<StackedChips.Trigger onPress={() => setFolderOpen((v) => !v)}>
<View
style={[
styles.chip,
styles.chipTertiary,
compact && styles.chipCompact,
{ backgroundColor: SAME(custom.tertiary, "#222222") ?? "#222" },
]}
>
<SymbolView
name="folder.fill"
size={16}
tintColor={iconTint}
/>
<Text
style={[
styles.chipText,
fontLoaded && { fontFamily: "SfProRounded" },
labelColor,
]}
>
{custom.folder}
</Text>
</View>
</StackedChips.Trigger>
<StackedChips.Content>
<View
style={[
styles.chip,
styles.chipQuaternary,
compact && styles.chipCompactLast,
{ backgroundColor: custom.quaternary },
]}
>
<SymbolView
name="photo.fill"
size={16}
tintColor={iconTint}
/>
<Text
style={[
styles.chipText,
fontLoaded && { fontFamily: "SfProRounded" },
labelColor,
]}
>
{custom.photo}
</Text>
</View>
</StackedChips.Content>
</StackedChips>
</StackedChips.Content>
</StackedChips>
</StackedChips.Content>
</StackedChips>
</GestureHandlerRootView>
</Showcase>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0a0a0a",
justifyContent: "center",
paddingHorizontal: 12,
},
chip: {
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 50,
},
chipPrimary: {
backgroundColor: "#fff",
},
chipSecondary: {
backgroundColor: "#333",
marginLeft: 20,
paddingHorizontal: 34,
},
chipTertiary: {
backgroundColor: "#222",
marginLeft: 20,
paddingHorizontal: 34,
},
chipQuaternary: {
backgroundColor: "#1a1a1a",
marginLeft: 20,
},
chipCompact: {
marginLeft: 12,
paddingHorizontal: 20,
},
chipCompactLast: {
marginLeft: 12,
},
chipText: {
fontSize: 15,
fontWeight: "600",
color: "#fff",
},
});childrenТип ReactNode
open?Тип boolean
childrenТип ReactNode
onPress?Тип () => void