@umituz/react-native-onboarding 3.6.26 → 3.6.27
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/package.json +10 -10
- package/src/infrastructure/utils/layouts/gridLayouts.ts +21 -11
- package/src/infrastructure/utils/layouts/layoutTypes.ts +8 -0
- package/src/infrastructure/utils/layouts/screenDimensions.ts +3 -3
- package/src/presentation/components/BackgroundImageCollage.tsx +11 -3
- package/src/presentation/components/BaseSlide.tsx +21 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.27",
|
|
4
4
|
"description": "Advanced onboarding flow for React Native apps with personalization questions, theme-aware colors, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@expo/vector-icons": ">=15.0.0",
|
|
32
|
-
"@umituz/react-native-design-system": "
|
|
33
|
-
"@umituz/react-native-localization": "
|
|
34
|
-
"@umituz/react-native-storage": "
|
|
35
|
-
"expo-image": ">=
|
|
36
|
-
"expo-video": ">=
|
|
37
|
-
"react": ">=
|
|
38
|
-
"react-native": ">=0.
|
|
39
|
-
"react-native-safe-area-context": ">=
|
|
40
|
-
"zustand": ">=
|
|
32
|
+
"@umituz/react-native-design-system": "latest",
|
|
33
|
+
"@umituz/react-native-localization": "latest",
|
|
34
|
+
"@umituz/react-native-storage": "latest",
|
|
35
|
+
"expo-image": ">=3.0.0",
|
|
36
|
+
"expo-video": ">=3.0.0",
|
|
37
|
+
"react": ">=19.0.0",
|
|
38
|
+
"react-native": ">=0.81.0",
|
|
39
|
+
"react-native-safe-area-context": ">=5.0.0",
|
|
40
|
+
"zustand": ">=5.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@expo/vector-icons": "^15.0.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Grid Layout Generators
|
|
3
|
-
* Standard and dense grid layouts
|
|
3
|
+
* Standard and dense grid layouts with safe area support
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { SCREEN_WIDTH, SCREEN_HEIGHT } from "./screenDimensions";
|
|
@@ -10,14 +10,19 @@ export const generateGridLayout = (
|
|
|
10
10
|
images: ImageSourceType[],
|
|
11
11
|
config: LayoutConfig = {},
|
|
12
12
|
): ImageLayoutItem[] => {
|
|
13
|
-
const { columns, gap = 0, borderRadius = 0 } = config;
|
|
13
|
+
const { columns, gap = 0, borderRadius = 0, safeAreaInsets } = config;
|
|
14
14
|
const count = images.length;
|
|
15
15
|
const cols = columns ?? Math.ceil(Math.sqrt(count));
|
|
16
16
|
const rows = Math.ceil(count / cols);
|
|
17
|
+
|
|
18
|
+
const insets = safeAreaInsets ?? { top: 0, bottom: 0, left: 0, right: 0 };
|
|
19
|
+
const availableWidth = SCREEN_WIDTH - insets.left - insets.right;
|
|
20
|
+
const availableHeight = SCREEN_HEIGHT - insets.top - insets.bottom;
|
|
21
|
+
|
|
17
22
|
const totalGapX = gap * (cols - 1);
|
|
18
23
|
const totalGapY = gap * (rows - 1);
|
|
19
|
-
const cellWidth = (
|
|
20
|
-
const cellHeight = (
|
|
24
|
+
const cellWidth = (availableWidth - totalGapX) / cols;
|
|
25
|
+
const cellHeight = (availableHeight - totalGapY) / rows;
|
|
21
26
|
|
|
22
27
|
return images.map((source, index) => {
|
|
23
28
|
const col = index % cols;
|
|
@@ -27,8 +32,8 @@ export const generateGridLayout = (
|
|
|
27
32
|
source,
|
|
28
33
|
style: {
|
|
29
34
|
position: "absolute" as const,
|
|
30
|
-
left: col * (cellWidth + gap),
|
|
31
|
-
top: row * (cellHeight + gap),
|
|
35
|
+
left: insets.left + col * (cellWidth + gap),
|
|
36
|
+
top: insets.top + row * (cellHeight + gap),
|
|
32
37
|
width: cellWidth,
|
|
33
38
|
height: cellHeight,
|
|
34
39
|
borderRadius,
|
|
@@ -41,13 +46,18 @@ export const generateDenseGridLayout = (
|
|
|
41
46
|
images: ImageSourceType[],
|
|
42
47
|
config: LayoutConfig = {},
|
|
43
48
|
): ImageLayoutItem[] => {
|
|
44
|
-
const { columns = 6, gap = 2, borderRadius = 4 } = config;
|
|
49
|
+
const { columns = 6, gap = 2, borderRadius = 4, safeAreaInsets } = config;
|
|
45
50
|
const count = images.length;
|
|
46
51
|
const rows = Math.ceil(count / columns);
|
|
52
|
+
|
|
53
|
+
const insets = safeAreaInsets ?? { top: 0, bottom: 0, left: 0, right: 0 };
|
|
54
|
+
const availableWidth = SCREEN_WIDTH - insets.left - insets.right;
|
|
55
|
+
const availableHeight = SCREEN_HEIGHT - insets.top - insets.bottom;
|
|
56
|
+
|
|
47
57
|
const totalGapX = gap * (columns - 1);
|
|
48
58
|
const totalGapY = gap * (rows - 1);
|
|
49
|
-
const cellWidth = (
|
|
50
|
-
const cellHeight = (
|
|
59
|
+
const cellWidth = (availableWidth - totalGapX) / columns;
|
|
60
|
+
const cellHeight = (availableHeight - totalGapY) / rows;
|
|
51
61
|
|
|
52
62
|
return images.map((source, index) => {
|
|
53
63
|
const col = index % columns;
|
|
@@ -57,8 +67,8 @@ export const generateDenseGridLayout = (
|
|
|
57
67
|
source,
|
|
58
68
|
style: {
|
|
59
69
|
position: "absolute" as const,
|
|
60
|
-
left: col * (cellWidth + gap),
|
|
61
|
-
top: row * (cellHeight + gap),
|
|
70
|
+
left: insets.left + col * (cellWidth + gap),
|
|
71
|
+
top: insets.top + row * (cellHeight + gap),
|
|
62
72
|
width: cellWidth,
|
|
63
73
|
height: cellHeight,
|
|
64
74
|
borderRadius,
|
|
@@ -7,6 +7,13 @@ import type { ImageSource } from "expo-image";
|
|
|
7
7
|
|
|
8
8
|
export type ImageSourceType = ImageSource | number | string;
|
|
9
9
|
|
|
10
|
+
export interface SafeAreaInsets {
|
|
11
|
+
top: number;
|
|
12
|
+
bottom: number;
|
|
13
|
+
left: number;
|
|
14
|
+
right: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export interface ImageLayoutStyle {
|
|
11
18
|
position: "absolute";
|
|
12
19
|
top: number;
|
|
@@ -26,4 +33,5 @@ export interface LayoutConfig {
|
|
|
26
33
|
gap?: number;
|
|
27
34
|
borderRadius?: number;
|
|
28
35
|
randomizeSize?: boolean;
|
|
36
|
+
safeAreaInsets?: SafeAreaInsets;
|
|
29
37
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Screen Dimensions
|
|
3
|
-
* Centralized screen dimension values
|
|
3
|
+
* Centralized screen dimension values using design system utilities
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { getScreenDimensions } from "@umituz/react-native-design-system";
|
|
7
7
|
|
|
8
|
-
const dimensions =
|
|
8
|
+
const dimensions = getScreenDimensions();
|
|
9
9
|
|
|
10
10
|
export const SCREEN_WIDTH = dimensions.width;
|
|
11
11
|
export const SCREEN_HEIGHT = dimensions.height;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Background Image Collage Component
|
|
3
|
-
* Displays multiple images in various layout patterns
|
|
3
|
+
* Displays multiple images in various layout patterns with safe area support
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useMemo } from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { Image } from "expo-image";
|
|
9
|
+
import { useSafeAreaInsets } from "@umituz/react-native-design-system";
|
|
9
10
|
import {
|
|
10
11
|
generateGridLayout,
|
|
11
12
|
generateDenseGridLayout,
|
|
@@ -57,12 +58,19 @@ export const BackgroundImageCollage: React.FC<BackgroundImageCollageProps> = ({
|
|
|
57
58
|
borderRadius,
|
|
58
59
|
opacity = 1,
|
|
59
60
|
}) => {
|
|
61
|
+
const insets = useSafeAreaInsets();
|
|
62
|
+
|
|
60
63
|
const imageLayouts = useMemo(() => {
|
|
61
64
|
if (!images || images.length === 0) return [];
|
|
62
65
|
|
|
63
66
|
const generator = LAYOUT_GENERATORS[layout] ?? generateGridLayout;
|
|
64
|
-
return generator(images, {
|
|
65
|
-
|
|
67
|
+
return generator(images, {
|
|
68
|
+
columns,
|
|
69
|
+
gap,
|
|
70
|
+
borderRadius,
|
|
71
|
+
safeAreaInsets: insets
|
|
72
|
+
});
|
|
73
|
+
}, [images, layout, columns, gap, borderRadius, insets]);
|
|
66
74
|
|
|
67
75
|
if (imageLayouts.length === 0) return null;
|
|
68
76
|
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Single Responsibility: Provide a base layout for all onboarding slides
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View,
|
|
6
|
+
import React, { useMemo } from "react";
|
|
7
|
+
import { View, ScrollView } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, useResponsive } from "@umituz/react-native-design-system";
|
|
8
9
|
import type { ContentPosition } from "../../domain/entities/OnboardingSlide";
|
|
9
10
|
|
|
10
11
|
export interface BaseSlideProps {
|
|
@@ -16,41 +17,31 @@ export const BaseSlide = ({
|
|
|
16
17
|
children,
|
|
17
18
|
contentPosition = "center",
|
|
18
19
|
}: BaseSlideProps) => {
|
|
20
|
+
const tokens = useAppDesignTokens();
|
|
21
|
+
const { verticalPadding, horizontalPadding } = useResponsive();
|
|
19
22
|
const isBottom = contentPosition === "bottom";
|
|
20
23
|
|
|
24
|
+
const contentContainerStyle = useMemo(() => ({
|
|
25
|
+
flexGrow: 1,
|
|
26
|
+
paddingVertical: verticalPadding,
|
|
27
|
+
justifyContent: isBottom ? "flex-end" as const : "center" as const,
|
|
28
|
+
paddingBottom: isBottom ? verticalPadding : undefined,
|
|
29
|
+
}), [verticalPadding, isBottom]);
|
|
30
|
+
|
|
31
|
+
const slideContainerStyle = useMemo(() => ({
|
|
32
|
+
width: "100%" as const,
|
|
33
|
+
paddingHorizontal: horizontalPadding,
|
|
34
|
+
alignItems: "center" as const,
|
|
35
|
+
}), [horizontalPadding]);
|
|
36
|
+
|
|
21
37
|
return (
|
|
22
38
|
<ScrollView
|
|
23
|
-
style={
|
|
24
|
-
contentContainerStyle={
|
|
25
|
-
styles.content,
|
|
26
|
-
isBottom ? styles.contentBottom : styles.contentCenter,
|
|
27
|
-
]}
|
|
39
|
+
style={{ flex: 1 }}
|
|
40
|
+
contentContainerStyle={contentContainerStyle}
|
|
28
41
|
showsVerticalScrollIndicator={false}
|
|
29
42
|
bounces={false}
|
|
30
43
|
>
|
|
31
|
-
<View style={
|
|
44
|
+
<View style={slideContainerStyle}>{children}</View>
|
|
32
45
|
</ScrollView>
|
|
33
46
|
);
|
|
34
47
|
};
|
|
35
|
-
|
|
36
|
-
const styles = StyleSheet.create({
|
|
37
|
-
container: {
|
|
38
|
-
flex: 1,
|
|
39
|
-
},
|
|
40
|
-
content: {
|
|
41
|
-
flexGrow: 1,
|
|
42
|
-
paddingVertical: 40,
|
|
43
|
-
},
|
|
44
|
-
contentCenter: {
|
|
45
|
-
justifyContent: "center",
|
|
46
|
-
},
|
|
47
|
-
contentBottom: {
|
|
48
|
-
justifyContent: "flex-end",
|
|
49
|
-
paddingBottom: 40, // Reduced from 140 as footer handles its own padding
|
|
50
|
-
},
|
|
51
|
-
slideContainer: {
|
|
52
|
-
width: "100%",
|
|
53
|
-
paddingHorizontal: 24,
|
|
54
|
-
alignItems: "center",
|
|
55
|
-
},
|
|
56
|
-
});
|