@umituz/react-native-photo-editor 1.0.5 → 1.0.7
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 +4 -2
- package/src/PhotoEditor.tsx +10 -4
- package/src/components/EditorCanvas.tsx +2 -0
- package/src/components/FilterPicker.tsx +13 -10
- package/src/components/FontControls.tsx +2 -0
- package/src/components/StickerPicker.tsx +7 -37
- package/src/components/TextEditorSheet.tsx +2 -0
- package/src/constants.ts +61 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-photo-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "A powerful, generic photo editor for React Native",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"react-native": "*",
|
|
24
24
|
"@umituz/react-native-design-system": "*",
|
|
25
25
|
"react-native-gesture-handler": "*",
|
|
26
|
-
"react-native-reanimated": "*"
|
|
26
|
+
"react-native-reanimated": "*",
|
|
27
|
+
"react-native-safe-area-context": "*"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"expo-image-picker": "*",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"typescript": "*",
|
|
41
42
|
"react-native-gesture-handler": "*",
|
|
42
43
|
"react-native-reanimated": "*",
|
|
44
|
+
"react-native-safe-area-context": "*",
|
|
43
45
|
"expo-application": "*",
|
|
44
46
|
"expo-device": "*",
|
|
45
47
|
"expo-linear-gradient": "*"
|
package/src/PhotoEditor.tsx
CHANGED
|
@@ -20,6 +20,7 @@ import { TextEditorSheet } from "./components/TextEditorSheet";
|
|
|
20
20
|
import { createEditorStyles } from "./styles";
|
|
21
21
|
import { usePhotoEditorUI } from "./hooks/usePhotoEditorUI";
|
|
22
22
|
import { Layer } from "./types";
|
|
23
|
+
import { DEFAULT_FONTS } from "./constants";
|
|
23
24
|
|
|
24
25
|
export interface EditorActions {
|
|
25
26
|
addTextLayer: (tokens: DesignTokens) => string;
|
|
@@ -36,10 +37,11 @@ export interface PhotoEditorProps {
|
|
|
36
37
|
customTools?: React.ReactNode | ((actions: EditorActions) => React.ReactNode);
|
|
37
38
|
initialCaption?: string;
|
|
38
39
|
t: (key: string) => string;
|
|
40
|
+
fonts?: readonly string[];
|
|
41
|
+
stickers?: readonly string[];
|
|
42
|
+
showAI?: boolean;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
const FONTS = ["Impact", "Comic", "Serif", "Retro"] as const;
|
|
42
|
-
|
|
43
45
|
export const PhotoEditor: React.FC<PhotoEditorProps> = ({
|
|
44
46
|
imageUri,
|
|
45
47
|
onSave,
|
|
@@ -48,6 +50,9 @@ export const PhotoEditor: React.FC<PhotoEditorProps> = ({
|
|
|
48
50
|
customTools,
|
|
49
51
|
initialCaption,
|
|
50
52
|
t,
|
|
53
|
+
fonts = DEFAULT_FONTS,
|
|
54
|
+
stickers,
|
|
55
|
+
showAI = false,
|
|
51
56
|
}) => {
|
|
52
57
|
const tokens = useAppDesignTokens();
|
|
53
58
|
const insets = useSafeAreaInsets();
|
|
@@ -118,7 +123,7 @@ export const PhotoEditor: React.FC<PhotoEditorProps> = ({
|
|
|
118
123
|
<FontControls
|
|
119
124
|
fontSize={fontSize}
|
|
120
125
|
selectedFont={selectedFont}
|
|
121
|
-
fonts={
|
|
126
|
+
fonts={fonts}
|
|
122
127
|
onFontSizeChange={(s) => setFontSize(Math.max(12, Math.min(96, s)))}
|
|
123
128
|
onFontSelect={setSelectedFont}
|
|
124
129
|
styles={styles}
|
|
@@ -130,6 +135,7 @@ export const PhotoEditor: React.FC<PhotoEditorProps> = ({
|
|
|
130
135
|
onAddSticker={() => stickerSheetRef.current?.present()}
|
|
131
136
|
onOpenFilters={() => filterSheetRef.current?.present()}
|
|
132
137
|
onOpenLayers={() => layerSheetRef.current?.present()}
|
|
138
|
+
onAIMagic={showAI ? undefined : undefined}
|
|
133
139
|
styles={styles}
|
|
134
140
|
t={t}
|
|
135
141
|
/>
|
|
@@ -144,7 +150,7 @@ export const PhotoEditor: React.FC<PhotoEditorProps> = ({
|
|
|
144
150
|
</BottomSheetModal>
|
|
145
151
|
|
|
146
152
|
<BottomSheetModal ref={stickerSheetRef} snapPoints={["50%"]}>
|
|
147
|
-
<StickerPicker onSelectSticker={handleSelectSticker} />
|
|
153
|
+
<StickerPicker stickers={stickers} onSelectSticker={handleSelectSticker} />
|
|
148
154
|
</BottomSheetModal>
|
|
149
155
|
|
|
150
156
|
<BottomSheetModal ref={filterSheetRef} snapPoints={["40%"]}>
|
|
@@ -5,24 +5,25 @@ import {
|
|
|
5
5
|
AtomicIcon,
|
|
6
6
|
useAppDesignTokens,
|
|
7
7
|
} from "@umituz/react-native-design-system";
|
|
8
|
+
import { DEFAULT_FILTERS, type FilterType } from "../constants";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{ id: "cool", name: "Cool", icon: "snow" as const, value: 0.3 },
|
|
16
|
-
] as const;
|
|
10
|
+
interface FilterOption {
|
|
11
|
+
id: FilterType;
|
|
12
|
+
name: string;
|
|
13
|
+
icon: string;
|
|
14
|
+
value: number;
|
|
15
|
+
}
|
|
17
16
|
|
|
18
17
|
interface FilterPickerProps {
|
|
19
18
|
selectedFilter: string;
|
|
20
19
|
onSelectFilter: (filterId: string, value: number) => void;
|
|
20
|
+
filters?: readonly FilterOption[];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const FilterPicker: React.FC<FilterPickerProps> = ({
|
|
24
24
|
selectedFilter,
|
|
25
25
|
onSelectFilter,
|
|
26
|
+
filters = DEFAULT_FILTERS,
|
|
26
27
|
}) => {
|
|
27
28
|
const tokens = useAppDesignTokens();
|
|
28
29
|
|
|
@@ -65,7 +66,7 @@ export const FilterPicker: React.FC<FilterPickerProps> = ({
|
|
|
65
66
|
<View style={styles.container}>
|
|
66
67
|
<AtomicText style={styles.title}>Filters</AtomicText>
|
|
67
68
|
<View style={styles.grid}>
|
|
68
|
-
{
|
|
69
|
+
{filters.map((filter) => (
|
|
69
70
|
<TouchableOpacity
|
|
70
71
|
key={filter.id}
|
|
71
72
|
style={[
|
|
@@ -75,7 +76,7 @@ export const FilterPicker: React.FC<FilterPickerProps> = ({
|
|
|
75
76
|
onPress={() => onSelectFilter(filter.id, filter.value)}
|
|
76
77
|
>
|
|
77
78
|
<AtomicIcon
|
|
78
|
-
name={filter.icon}
|
|
79
|
+
name={filter.icon as "close-circle" | "color-palette" | "contrast" | "time" | "sunny" | "snow"}
|
|
79
80
|
size="lg"
|
|
80
81
|
color={selectedFilter === filter.id ? "primary" : "textSecondary"}
|
|
81
82
|
/>
|
|
@@ -93,3 +94,5 @@ export const FilterPicker: React.FC<FilterPickerProps> = ({
|
|
|
93
94
|
</View>
|
|
94
95
|
);
|
|
95
96
|
};
|
|
97
|
+
|
|
98
|
+
export default React.memo(FilterPicker);
|
|
@@ -4,48 +4,16 @@ import {
|
|
|
4
4
|
AtomicText,
|
|
5
5
|
useAppDesignTokens,
|
|
6
6
|
} from "@umituz/react-native-design-system";
|
|
7
|
-
|
|
8
|
-
const STICKERS = [
|
|
9
|
-
"😀",
|
|
10
|
-
"😂",
|
|
11
|
-
"🤣",
|
|
12
|
-
"😍",
|
|
13
|
-
"🥰",
|
|
14
|
-
"😎",
|
|
15
|
-
"🤯",
|
|
16
|
-
"🥳",
|
|
17
|
-
"😤",
|
|
18
|
-
"💀",
|
|
19
|
-
"🔥",
|
|
20
|
-
"❤️",
|
|
21
|
-
"💯",
|
|
22
|
-
"✨",
|
|
23
|
-
"🎉",
|
|
24
|
-
"🤡",
|
|
25
|
-
"👀",
|
|
26
|
-
"🙌",
|
|
27
|
-
"👏",
|
|
28
|
-
"💪",
|
|
29
|
-
"🤝",
|
|
30
|
-
"🙈",
|
|
31
|
-
"🐶",
|
|
32
|
-
"🐱",
|
|
33
|
-
"🦊",
|
|
34
|
-
"🐸",
|
|
35
|
-
"🌟",
|
|
36
|
-
"⭐",
|
|
37
|
-
"🌈",
|
|
38
|
-
"☀️",
|
|
39
|
-
"🌙",
|
|
40
|
-
"💫",
|
|
41
|
-
];
|
|
7
|
+
import { DEFAULT_STICKERS } from "../constants";
|
|
42
8
|
|
|
43
9
|
interface StickerPickerProps {
|
|
44
10
|
onSelectSticker: (sticker: string) => void;
|
|
11
|
+
stickers?: readonly string[];
|
|
45
12
|
}
|
|
46
13
|
|
|
47
14
|
export const StickerPicker: React.FC<StickerPickerProps> = ({
|
|
48
15
|
onSelectSticker,
|
|
16
|
+
stickers = DEFAULT_STICKERS,
|
|
49
17
|
}) => {
|
|
50
18
|
const tokens = useAppDesignTokens();
|
|
51
19
|
|
|
@@ -78,9 +46,9 @@ export const StickerPicker: React.FC<StickerPickerProps> = ({
|
|
|
78
46
|
<AtomicText style={styles.title}>Emoji</AtomicText>
|
|
79
47
|
<ScrollView showsVerticalScrollIndicator={false}>
|
|
80
48
|
<View style={styles.grid}>
|
|
81
|
-
{
|
|
49
|
+
{stickers.map((sticker, index) => (
|
|
82
50
|
<TouchableOpacity
|
|
83
|
-
key={index}
|
|
51
|
+
key={sticker || index}
|
|
84
52
|
style={styles.sticker}
|
|
85
53
|
onPress={() => onSelectSticker(sticker)}
|
|
86
54
|
>
|
|
@@ -92,3 +60,5 @@ export const StickerPicker: React.FC<StickerPickerProps> = ({
|
|
|
92
60
|
</View>
|
|
93
61
|
);
|
|
94
62
|
};
|
|
63
|
+
|
|
64
|
+
export default React.memo(StickerPicker);
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default constants for Photo Editor
|
|
3
|
+
* These can be overridden via props
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_FONTS = ["Impact", "Comic", "Serif", "Retro"] as const;
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_STICKERS = [
|
|
9
|
+
"😀",
|
|
10
|
+
"😂",
|
|
11
|
+
"🤣",
|
|
12
|
+
"😍",
|
|
13
|
+
"🥰",
|
|
14
|
+
"😎",
|
|
15
|
+
"🤯",
|
|
16
|
+
"🥳",
|
|
17
|
+
"😤",
|
|
18
|
+
"💀",
|
|
19
|
+
"🔥",
|
|
20
|
+
"❤️",
|
|
21
|
+
"💯",
|
|
22
|
+
"✨",
|
|
23
|
+
"🎉",
|
|
24
|
+
"🤡",
|
|
25
|
+
"👀",
|
|
26
|
+
"🙌",
|
|
27
|
+
"👏",
|
|
28
|
+
"💪",
|
|
29
|
+
"🤝",
|
|
30
|
+
"🙈",
|
|
31
|
+
"🐶",
|
|
32
|
+
"🐱",
|
|
33
|
+
"🦊",
|
|
34
|
+
"🐸",
|
|
35
|
+
"🌟",
|
|
36
|
+
"⭐",
|
|
37
|
+
"🌈",
|
|
38
|
+
"☀️",
|
|
39
|
+
"🌙",
|
|
40
|
+
"💫",
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
export type FilterType = "none" | "sepia" | "grayscale" | "vintage" | "warm" | "cool";
|
|
44
|
+
|
|
45
|
+
export const DEFAULT_FILTERS = [
|
|
46
|
+
{ id: "none" as FilterType, name: "None", icon: "close-circle", value: 0 },
|
|
47
|
+
{ id: "sepia" as FilterType, name: "Sepia", icon: "color-palette", value: 0.5 },
|
|
48
|
+
{ id: "grayscale" as FilterType, name: "B&W", icon: "contrast", value: 1 },
|
|
49
|
+
{ id: "vintage" as FilterType, name: "Vintage", icon: "time", value: 0.7 },
|
|
50
|
+
{ id: "warm" as FilterType, name: "Warm", icon: "sunny", value: 0.3 },
|
|
51
|
+
{ id: "cool" as FilterType, name: "Cool", icon: "snow", value: 0.3 },
|
|
52
|
+
] as const;
|
|
53
|
+
|
|
54
|
+
export const DEFAULT_AI_STYLES = [
|
|
55
|
+
{ id: "viral", label: "✨ Viral", desc: "Catchy & shareable" },
|
|
56
|
+
{ id: "funny", label: "😂 Funny", desc: "Humor that connects" },
|
|
57
|
+
{ id: "savage", label: "🔥 Savage", desc: "Bold & edgy" },
|
|
58
|
+
{ id: "wholesome", label: "💕 Wholesome", desc: "Warm & positive" },
|
|
59
|
+
{ id: "sarcastic", label: "😏 Sarcastic", desc: "Witty & ironic" },
|
|
60
|
+
{ id: "relatable", label: "🎯 Relatable", desc: "Everyone gets it" },
|
|
61
|
+
] as const;
|