rn-vs-lb 1.0.62 → 1.0.63
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/components/Gallery/AiAgentGallery.pure.tsx +138 -0
- package/components/Gallery/AiAgentGallery.stories.tsx +75 -0
- package/components/Gallery/index.ts +1 -0
- package/components/UI/StepProgress.stories.tsx +61 -0
- package/components/UI/StepProgress.tsx +153 -0
- package/components/UI/index.ts +1 -0
- package/components/index.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { memo, useMemo } from "react";
|
|
2
|
+
import { ActivityIndicator, Image, Pressable, StyleSheet, Text, View } from "react-native";
|
|
3
|
+
import { useTheme, ThemeType, SizesType, TypographytType } from "../../theme";
|
|
4
|
+
import { GalleryModal } from "../Modals";
|
|
5
|
+
|
|
6
|
+
export type AiAgentGalleryViewProps = {
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
photos: string[];
|
|
9
|
+
galleryColumns: number;
|
|
10
|
+
galleryItemSize: number;
|
|
11
|
+
|
|
12
|
+
visible: boolean;
|
|
13
|
+
initialIndex: number;
|
|
14
|
+
onOpenAt: (index: number) => void;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
|
|
17
|
+
emptyTest: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const AiAgentGalleryView = memo(
|
|
21
|
+
({
|
|
22
|
+
isLoading,
|
|
23
|
+
photos,
|
|
24
|
+
galleryColumns,
|
|
25
|
+
galleryItemSize,
|
|
26
|
+
visible,
|
|
27
|
+
initialIndex,
|
|
28
|
+
onOpenAt,
|
|
29
|
+
onClose,
|
|
30
|
+
emptyTest,
|
|
31
|
+
}: AiAgentGalleryViewProps) => {
|
|
32
|
+
const { theme, sizes, typography } = useTheme();
|
|
33
|
+
const isDark = (theme as any)?.isDark ?? false;
|
|
34
|
+
|
|
35
|
+
const styles = useMemo(() => getStyles(theme, sizes, typography, isDark), [
|
|
36
|
+
theme,
|
|
37
|
+
sizes,
|
|
38
|
+
typography,
|
|
39
|
+
isDark,
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
if (isLoading) {
|
|
43
|
+
return (
|
|
44
|
+
<View style={styles.galleryWrapper}>
|
|
45
|
+
<ActivityIndicator />
|
|
46
|
+
</View>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!photos.length) {
|
|
51
|
+
return (
|
|
52
|
+
<View style={styles.galleryWrapper}>
|
|
53
|
+
<View style={styles.emptyState}>
|
|
54
|
+
<Text style={styles.emptyText}>{emptyTest}</Text>
|
|
55
|
+
</View>
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View style={styles.galleryWrapper}>
|
|
62
|
+
<View style={styles.galleryGrid}>
|
|
63
|
+
{photos.map((photo, i) => {
|
|
64
|
+
const isLastInRow = (i + 1) % galleryColumns === 0;
|
|
65
|
+
return (
|
|
66
|
+
<Pressable
|
|
67
|
+
key={`${photo}-${i}`}
|
|
68
|
+
onPress={() => onOpenAt(i)}
|
|
69
|
+
android_ripple={{ color: "#00000022" }}
|
|
70
|
+
style={[
|
|
71
|
+
styles.galleryImage,
|
|
72
|
+
isLastInRow && styles.galleryImageLast,
|
|
73
|
+
{ width: galleryItemSize, height: galleryItemSize },
|
|
74
|
+
]}
|
|
75
|
+
>
|
|
76
|
+
<Image
|
|
77
|
+
source={{ uri: photo }}
|
|
78
|
+
style={{
|
|
79
|
+
width: "100%",
|
|
80
|
+
height: "100%",
|
|
81
|
+
borderRadius: styles.galleryImage.borderRadius,
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
</Pressable>
|
|
85
|
+
);
|
|
86
|
+
})}
|
|
87
|
+
</View>
|
|
88
|
+
|
|
89
|
+
<GalleryModal
|
|
90
|
+
visible={visible}
|
|
91
|
+
images={photos}
|
|
92
|
+
initialIndex={initialIndex}
|
|
93
|
+
onRequestClose={onClose}
|
|
94
|
+
/>
|
|
95
|
+
</View>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
AiAgentGalleryView.displayName = "AiAgentGalleryView";
|
|
101
|
+
|
|
102
|
+
const getStyles = (
|
|
103
|
+
theme: ThemeType,
|
|
104
|
+
sizes: SizesType,
|
|
105
|
+
typography: TypographytType,
|
|
106
|
+
isDark: boolean
|
|
107
|
+
) =>
|
|
108
|
+
StyleSheet.create({
|
|
109
|
+
galleryWrapper: {
|
|
110
|
+
paddingHorizontal: sizes.md as number,
|
|
111
|
+
paddingVertical: sizes.xl as number,
|
|
112
|
+
},
|
|
113
|
+
galleryGrid: {
|
|
114
|
+
flexDirection: "row",
|
|
115
|
+
flexWrap: "wrap",
|
|
116
|
+
},
|
|
117
|
+
galleryImage: {
|
|
118
|
+
borderRadius: 18,
|
|
119
|
+
backgroundColor: theme.backgroundSecond,
|
|
120
|
+
marginRight: sizes.sm as number,
|
|
121
|
+
marginBottom: sizes.sm as number,
|
|
122
|
+
},
|
|
123
|
+
galleryImageLast: {
|
|
124
|
+
marginRight: 0,
|
|
125
|
+
},
|
|
126
|
+
emptyState: {
|
|
127
|
+
alignItems: "center",
|
|
128
|
+
justifyContent: "center",
|
|
129
|
+
paddingVertical: sizes.lg as number,
|
|
130
|
+
},
|
|
131
|
+
emptyText: {
|
|
132
|
+
...(typography.bodySm as object),
|
|
133
|
+
color: theme.greyText,
|
|
134
|
+
textAlign: "center",
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export default AiAgentGalleryView;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
import { ThemeProvider } from '../../theme';
|
|
5
|
+
import { AiAgentGalleryView } from './AiAgentGallery.pure';
|
|
6
|
+
|
|
7
|
+
type Props = React.ComponentProps<typeof AiAgentGalleryView>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta<Props> = {
|
|
10
|
+
title: 'Gallery/AiAgentGallery',
|
|
11
|
+
component: AiAgentGalleryView,
|
|
12
|
+
decorators: [
|
|
13
|
+
(Story) => (
|
|
14
|
+
<ThemeProvider>
|
|
15
|
+
<View style={{ paddingVertical: 24 }}>
|
|
16
|
+
<Story />
|
|
17
|
+
</View>
|
|
18
|
+
</ThemeProvider>
|
|
19
|
+
),
|
|
20
|
+
],
|
|
21
|
+
args: {
|
|
22
|
+
galleryColumns: 3,
|
|
23
|
+
galleryItemSize: 108,
|
|
24
|
+
emptyTest: 'No generated images yet. Try creating one with your AI agent!',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default meta;
|
|
29
|
+
|
|
30
|
+
const Template: StoryFn<Props> = ({
|
|
31
|
+
onOpenAt: _onOpenAt,
|
|
32
|
+
onClose: _onClose,
|
|
33
|
+
visible: _visible,
|
|
34
|
+
initialIndex: _initialIndex,
|
|
35
|
+
...rest
|
|
36
|
+
}) => {
|
|
37
|
+
const [visible, setVisible] = useState(false);
|
|
38
|
+
const [initialIndex, setInitialIndex] = useState(0);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<AiAgentGalleryView
|
|
42
|
+
{...rest}
|
|
43
|
+
visible={visible}
|
|
44
|
+
initialIndex={initialIndex}
|
|
45
|
+
onOpenAt={(index) => {
|
|
46
|
+
setInitialIndex(index);
|
|
47
|
+
setVisible(true);
|
|
48
|
+
}}
|
|
49
|
+
onClose={() => setVisible(false)}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Default = Template.bind({});
|
|
55
|
+
Default.args = {
|
|
56
|
+
isLoading: false,
|
|
57
|
+
photos: [
|
|
58
|
+
'https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=600&q=60',
|
|
59
|
+
'https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&w=600&q=60',
|
|
60
|
+
'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=600&q=60',
|
|
61
|
+
'https://images.unsplash.com/photo-1545239351-1141bd82e8a6?auto=format&fit=crop&w=600&q=60',
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const Loading = Template.bind({});
|
|
66
|
+
Loading.args = {
|
|
67
|
+
isLoading: true,
|
|
68
|
+
photos: [],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const Empty = Template.bind({});
|
|
72
|
+
Empty.args = {
|
|
73
|
+
isLoading: false,
|
|
74
|
+
photos: [],
|
|
75
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./AiAgentGallery.pure"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { View, Alert } from 'react-native';
|
|
4
|
+
import { ThemeProvider } from '../../theme';
|
|
5
|
+
import { StepProgress } from './StepProgress';
|
|
6
|
+
|
|
7
|
+
type Props = React.ComponentProps<typeof StepProgress>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta<Props> = {
|
|
10
|
+
title: 'UI/StepProgress',
|
|
11
|
+
component: StepProgress,
|
|
12
|
+
decorators: [
|
|
13
|
+
(Story) => (
|
|
14
|
+
<ThemeProvider>
|
|
15
|
+
<View style={{ padding: 24 }}>
|
|
16
|
+
<Story />
|
|
17
|
+
</View>
|
|
18
|
+
</ThemeProvider>
|
|
19
|
+
),
|
|
20
|
+
],
|
|
21
|
+
args: {
|
|
22
|
+
steps: [
|
|
23
|
+
{ title: 'Briefing', description: 'Share context and goals for your agent.' },
|
|
24
|
+
{ title: 'Generation', description: 'The agent drafts a tailored solution.' },
|
|
25
|
+
{ title: 'Review', description: 'Assess the output and give feedback.' },
|
|
26
|
+
{ title: 'Launch', description: 'Publish or deploy your agent deliverable.' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
|
|
33
|
+
const Template: StoryFn<Props> = (args) => <StepProgress {...args} />;
|
|
34
|
+
|
|
35
|
+
export const Default = Template.bind({});
|
|
36
|
+
Default.args = {
|
|
37
|
+
activeStep: 1,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const FirstStep = Template.bind({});
|
|
41
|
+
FirstStep.args = {
|
|
42
|
+
activeStep: 0,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WithClickableSteps: StoryFn<Props> = (args) => {
|
|
46
|
+
const [activeStep, setActiveStep] = useState(args.activeStep ?? 2);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<StepProgress
|
|
50
|
+
{...args}
|
|
51
|
+
activeStep={activeStep}
|
|
52
|
+
onStepPress={(index) => {
|
|
53
|
+
Alert.alert('Step selected', args.steps?.[index]?.title ?? `Step ${index + 1}`);
|
|
54
|
+
setActiveStep(index);
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
WithClickableSteps.args = {
|
|
60
|
+
activeStep: 2,
|
|
61
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
3
|
+
import { ThemeType, TypographytType, SizesType, useTheme } from "rn-vs-lb/theme";
|
|
4
|
+
|
|
5
|
+
interface StepProgressProps {
|
|
6
|
+
steps: { title: string; description?: string }[];
|
|
7
|
+
activeStep: number;
|
|
8
|
+
onStepPress?: (index: number) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const StepProgress: React.FC<StepProgressProps> = ({ steps, activeStep, onStepPress }) => {
|
|
12
|
+
const { theme, typography, sizes } = useTheme();
|
|
13
|
+
|
|
14
|
+
const styles = useMemo(
|
|
15
|
+
() => createStyles({ theme, typography, sizes }),
|
|
16
|
+
[theme, typography, sizes],
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const currentDescription = steps[activeStep]?.description ?? "";
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={styles.container}>
|
|
23
|
+
<View style={styles.stepsRow}>
|
|
24
|
+
{steps.map((step, index) => {
|
|
25
|
+
const isActive = index === activeStep;
|
|
26
|
+
const isCompleted = index < activeStep;
|
|
27
|
+
const isPressable = typeof onStepPress === "function" && index < activeStep;
|
|
28
|
+
return (
|
|
29
|
+
<TouchableOpacity
|
|
30
|
+
key={step.title ?? index}
|
|
31
|
+
style={[styles.stepItem, isPressable && styles.stepItemPressable]}
|
|
32
|
+
activeOpacity={0.7}
|
|
33
|
+
onPress={isPressable ? () => onStepPress(index) : undefined}
|
|
34
|
+
disabled={!isPressable}
|
|
35
|
+
>
|
|
36
|
+
<View style={styles.stepHeader}>
|
|
37
|
+
<View
|
|
38
|
+
style={[
|
|
39
|
+
styles.circle,
|
|
40
|
+
(isActive || isCompleted) && styles.circleActive,
|
|
41
|
+
]}
|
|
42
|
+
>
|
|
43
|
+
<Text
|
|
44
|
+
style={[
|
|
45
|
+
styles.circleText,
|
|
46
|
+
(isActive || isCompleted) && styles.circleTextActive,
|
|
47
|
+
]}
|
|
48
|
+
>
|
|
49
|
+
{index + 1}
|
|
50
|
+
</Text>
|
|
51
|
+
</View>
|
|
52
|
+
{index < steps.length - 1 ? (
|
|
53
|
+
<View
|
|
54
|
+
style={[
|
|
55
|
+
styles.connector,
|
|
56
|
+
(isCompleted || (isActive && activeStep === index)) && styles.connectorActive,
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
) : null}
|
|
60
|
+
</View>
|
|
61
|
+
<Text
|
|
62
|
+
numberOfLines={2}
|
|
63
|
+
style={[styles.stepTitle, isActive && styles.stepTitleActive]}
|
|
64
|
+
>
|
|
65
|
+
{step.title}
|
|
66
|
+
</Text>
|
|
67
|
+
</TouchableOpacity>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</View>
|
|
71
|
+
{currentDescription ? (
|
|
72
|
+
<Text style={styles.description}>{currentDescription}</Text>
|
|
73
|
+
) : null}
|
|
74
|
+
</View>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const createStyles = ({
|
|
79
|
+
theme,
|
|
80
|
+
typography,
|
|
81
|
+
sizes,
|
|
82
|
+
}: {
|
|
83
|
+
theme: ThemeType;
|
|
84
|
+
typography: TypographytType;
|
|
85
|
+
sizes: SizesType;
|
|
86
|
+
}) =>
|
|
87
|
+
StyleSheet.create({
|
|
88
|
+
container: {
|
|
89
|
+
marginBottom: sizes.xl as number,
|
|
90
|
+
},
|
|
91
|
+
stepsRow: {
|
|
92
|
+
flexDirection: "row",
|
|
93
|
+
alignItems: "center",
|
|
94
|
+
justifyContent: "space-between",
|
|
95
|
+
},
|
|
96
|
+
stepItem: {
|
|
97
|
+
flex: 1,
|
|
98
|
+
marginRight: sizes.sm as number,
|
|
99
|
+
},
|
|
100
|
+
stepItemPressable: {
|
|
101
|
+
opacity: 0.9,
|
|
102
|
+
},
|
|
103
|
+
stepHeader: {
|
|
104
|
+
flexDirection: "row",
|
|
105
|
+
alignItems: "center",
|
|
106
|
+
marginBottom: sizes.xs as number,
|
|
107
|
+
},
|
|
108
|
+
circle: {
|
|
109
|
+
width: 36,
|
|
110
|
+
height: 36,
|
|
111
|
+
borderRadius: 18,
|
|
112
|
+
borderWidth: 2,
|
|
113
|
+
borderColor: theme.border,
|
|
114
|
+
alignItems: "center",
|
|
115
|
+
justifyContent: "center",
|
|
116
|
+
backgroundColor: theme.white,
|
|
117
|
+
},
|
|
118
|
+
circleActive: {
|
|
119
|
+
borderColor: theme.primary,
|
|
120
|
+
backgroundColor: theme.primary,
|
|
121
|
+
},
|
|
122
|
+
circleText: {
|
|
123
|
+
...typography.bodyXs,
|
|
124
|
+
color: theme.text,
|
|
125
|
+
fontWeight: "600",
|
|
126
|
+
},
|
|
127
|
+
circleTextActive: {
|
|
128
|
+
color: theme.white,
|
|
129
|
+
},
|
|
130
|
+
connector: {
|
|
131
|
+
flex: 1,
|
|
132
|
+
height: 2,
|
|
133
|
+
marginLeft: sizes.xs as number,
|
|
134
|
+
backgroundColor: theme.border,
|
|
135
|
+
},
|
|
136
|
+
connectorActive: {
|
|
137
|
+
backgroundColor: theme.primary,
|
|
138
|
+
},
|
|
139
|
+
stepTitle: {
|
|
140
|
+
...typography.bodyXs,
|
|
141
|
+
color: theme.greyText,
|
|
142
|
+
},
|
|
143
|
+
stepTitleActive: {
|
|
144
|
+
color: theme.text,
|
|
145
|
+
fontWeight: "600",
|
|
146
|
+
},
|
|
147
|
+
description: {
|
|
148
|
+
marginTop: sizes.sm as number,
|
|
149
|
+
...typography.bodySm,
|
|
150
|
+
color: theme.greyText,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
package/components/UI/index.ts
CHANGED
package/components/index.ts
CHANGED