@umituz/react-native-photo-editor 2.0.1 → 2.0.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-photo-editor",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "A powerful, generic photo editor for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -1,4 +1,4 @@
1
- import React, { useState } from "react";
1
+ import React, { useState, useMemo } from "react";
2
2
  import { View, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText, AtomicIcon, AtomicButton } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -21,7 +21,7 @@ export const AIMagicSheet: React.FC<AIMagicSheetProps> = ({
21
21
  const [selected, setSelected] = useState<string | null>(null);
22
22
  const [loading, setLoading] = useState(false);
23
23
 
24
- const styles = StyleSheet.create({
24
+ const styles = useMemo(() => StyleSheet.create({
25
25
  container: { padding: tokens.spacing.md, gap: tokens.spacing.md },
26
26
  header: { flexDirection: "row", alignItems: "center", gap: tokens.spacing.sm },
27
27
  grid: { gap: tokens.spacing.sm },
@@ -39,7 +39,7 @@ export const AIMagicSheet: React.FC<AIMagicSheetProps> = ({
39
39
  backgroundColor: tokens.colors.primary + "10",
40
40
  },
41
41
  info: { flex: 1, marginLeft: tokens.spacing.sm },
42
- });
42
+ }), [tokens]);
43
43
 
44
44
  const handleGenerate = async () => {
45
45
  if (!selected || !onGenerateCaption) return;
@@ -3,7 +3,7 @@ import { View, StyleSheet } from "react-native";
3
3
  import { Image } from "expo-image";
4
4
  import { DraggableText, LayerTransform } from "./DraggableText";
5
5
  import { DraggableSticker } from "./DraggableSticker";
6
- import { Layer, TextLayer, StickerLayer, ImageFilters } from "../types";
6
+ import { Layer, ImageFilters } from "../types";
7
7
 
8
8
  interface EditorCanvasProps {
9
9
  imageUrl: string;
@@ -60,39 +60,37 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
60
60
 
61
61
  {layers.map((layer) => {
62
62
  if (layer.type === "text") {
63
- const textLayer = layer as TextLayer;
64
63
  return (
65
64
  <DraggableText
66
65
  key={layer.id}
67
- text={textLayer.text || "Tap to edit"}
68
- color={textLayer.color}
69
- fontSize={textLayer.fontSize}
70
- fontFamily={textLayer.fontFamily}
71
- textAlign={textLayer.textAlign}
72
- rotation={textLayer.rotation}
73
- scale={textLayer.scale}
74
- opacity={textLayer.opacity}
75
- backgroundColor={textLayer.backgroundColor}
76
- isBold={textLayer.isBold}
77
- isItalic={textLayer.isItalic}
78
- initialX={textLayer.x}
79
- initialY={textLayer.y}
66
+ text={layer.text || "Tap to edit"}
67
+ color={layer.color}
68
+ fontSize={layer.fontSize}
69
+ fontFamily={layer.fontFamily}
70
+ textAlign={layer.textAlign}
71
+ rotation={layer.rotation}
72
+ scale={layer.scale}
73
+ opacity={layer.opacity}
74
+ backgroundColor={layer.backgroundColor}
75
+ isBold={layer.isBold}
76
+ isItalic={layer.isItalic}
77
+ initialX={layer.x}
78
+ initialY={layer.y}
80
79
  onTransformEnd={(t) => onLayerTransform(layer.id, t)}
81
80
  onPress={() => onLayerTap(layer.id)}
82
81
  isSelected={activeLayerId === layer.id}
83
82
  />
84
83
  );
85
84
  } else if (layer.type === "sticker") {
86
- const stickerLayer = layer as StickerLayer;
87
85
  return (
88
86
  <DraggableSticker
89
87
  key={layer.id}
90
- uri={stickerLayer.uri}
91
- initialX={stickerLayer.x}
92
- initialY={stickerLayer.y}
93
- rotation={stickerLayer.rotation}
94
- scale={stickerLayer.scale}
95
- opacity={stickerLayer.opacity}
88
+ uri={layer.uri}
89
+ initialX={layer.x}
90
+ initialY={layer.y}
91
+ rotation={layer.rotation}
92
+ scale={layer.scale}
93
+ opacity={layer.opacity}
96
94
  onTransformEnd={(t) => onLayerTransform(layer.id, t)}
97
95
  onPress={() => onLayerTap(layer.id)}
98
96
  isSelected={activeLayerId === layer.id}
@@ -30,7 +30,6 @@ const ToolButton = ({
30
30
  onPress,
31
31
  isActive,
32
32
  disabled,
33
- tokens,
34
33
  parentStyles,
35
34
  }: {
36
35
  icon: string;
@@ -38,7 +37,6 @@ const ToolButton = ({
38
37
  onPress: () => void;
39
38
  isActive?: boolean;
40
39
  disabled?: boolean;
41
- tokens: ReturnType<typeof useAppDesignTokens>;
42
40
  parentStyles: EditorToolbarProps["styles"];
43
41
  }) => (
44
42
  <TouchableOpacity
@@ -87,7 +85,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
87
85
  label={t("editor.undo") || "Undo"}
88
86
  onPress={onUndo}
89
87
  disabled={!canUndo}
90
- tokens={tokens}
91
88
  parentStyles={parentStyles}
92
89
  />
93
90
  )}
@@ -96,7 +93,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
96
93
  icon="edit"
97
94
  label={t("editor.text") || "Text"}
98
95
  onPress={onAddText}
99
- tokens={tokens}
100
96
  parentStyles={parentStyles}
101
97
  />
102
98
 
@@ -105,7 +101,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
105
101
  icon="sparkles"
106
102
  label={t("editor.sticker") || "Sticker"}
107
103
  onPress={onAddSticker}
108
- tokens={tokens}
109
104
  parentStyles={parentStyles}
110
105
  />
111
106
  )}
@@ -126,7 +121,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
126
121
  icon="flash"
127
122
  label={t("editor.adjust") || "Adjust"}
128
123
  onPress={onOpenAdjustments}
129
- tokens={tokens}
130
124
  parentStyles={parentStyles}
131
125
  />
132
126
  )}
@@ -136,7 +130,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
136
130
  icon="brush"
137
131
  label={t("editor.filters") || "Filters"}
138
132
  onPress={onOpenFilters}
139
- tokens={tokens}
140
133
  parentStyles={parentStyles}
141
134
  />
142
135
  )}
@@ -145,7 +138,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
145
138
  icon="copy"
146
139
  label={t("editor.layers") || "Layers"}
147
140
  onPress={onOpenLayers}
148
- tokens={tokens}
149
141
  parentStyles={parentStyles}
150
142
  />
151
143
 
@@ -155,7 +147,6 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({
155
147
  label={t("editor.redo") || "Redo"}
156
148
  onPress={onRedo}
157
149
  disabled={!canRedo}
158
- tokens={tokens}
159
150
  parentStyles={parentStyles}
160
151
  />
161
152
  )}
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -17,7 +17,7 @@ export const FilterPicker: React.FC<FilterPickerProps> = ({
17
17
  }) => {
18
18
  const tokens = useAppDesignTokens();
19
19
 
20
- const styles = StyleSheet.create({
20
+ const styles = useMemo(() => StyleSheet.create({
21
21
  container: { padding: tokens.spacing.md, gap: tokens.spacing.md },
22
22
  grid: { flexDirection: "row", flexWrap: "wrap", gap: tokens.spacing.sm },
23
23
  filter: {
@@ -34,7 +34,7 @@ export const FilterPicker: React.FC<FilterPickerProps> = ({
34
34
  borderColor: tokens.colors.primary,
35
35
  backgroundColor: tokens.colors.primary + "10",
36
36
  },
37
- });
37
+ }), [tokens]);
38
38
 
39
39
  return (
40
40
  <View style={styles.container}>
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -32,7 +32,7 @@ export const FontControls: React.FC<FontControlsProps> = ({
32
32
  }) => {
33
33
  const tokens = useAppDesignTokens();
34
34
 
35
- const styles = StyleSheet.create({
35
+ const styles = useMemo(() => StyleSheet.create({
36
36
  stepRow: {
37
37
  flexDirection: "row",
38
38
  gap: tokens.spacing.sm,
@@ -58,7 +58,7 @@ export const FontControls: React.FC<FontControlsProps> = ({
58
58
  alignItems: "center",
59
59
  gap: tokens.spacing.xs,
60
60
  },
61
- });
61
+ }), [tokens]);
62
62
 
63
63
  return (
64
64
  <View style={externalStyles.controlsPanel}>
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -27,7 +27,7 @@ export const LayerManager: React.FC<LayerManagerProps> = ({
27
27
  }) => {
28
28
  const tokens = useAppDesignTokens();
29
29
 
30
- const styles = StyleSheet.create({
30
+ const styles = useMemo(() => StyleSheet.create({
31
31
  container: { padding: tokens.spacing.md, gap: tokens.spacing.md },
32
32
  item: {
33
33
  flexDirection: "row",
@@ -53,7 +53,7 @@ export const LayerManager: React.FC<LayerManagerProps> = ({
53
53
  padding: tokens.spacing.xs,
54
54
  borderRadius: tokens.borders.radius.sm,
55
55
  },
56
- });
56
+ }), [tokens]);
57
57
 
58
58
  const sortedLayers = [...layers].reverse(); // top layer first in list
59
59
 
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -15,7 +15,7 @@ export const StickerPicker: React.FC<StickerPickerProps> = ({
15
15
  }) => {
16
16
  const tokens = useAppDesignTokens();
17
17
 
18
- const styles = StyleSheet.create({
18
+ const styles = useMemo(() => StyleSheet.create({
19
19
  container: { padding: tokens.spacing.md, gap: tokens.spacing.md },
20
20
  grid: { flexDirection: "row", flexWrap: "wrap", gap: tokens.spacing.sm },
21
21
  sticker: {
@@ -26,7 +26,7 @@ export const StickerPicker: React.FC<StickerPickerProps> = ({
26
26
  alignItems: "center",
27
27
  justifyContent: "center",
28
28
  },
29
- });
29
+ }), [tokens]);
30
30
 
31
31
  return (
32
32
  <View style={styles.container}>
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, TextInput, TouchableOpacity, StyleSheet } from "react-native";
3
3
  import { AtomicText, AtomicButton } from "@umituz/react-native-design-system/atoms";
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
@@ -42,7 +42,7 @@ export const TextEditorSheet: React.FC<TextEditorSheetProps> = ({
42
42
  }) => {
43
43
  const tokens = useAppDesignTokens();
44
44
 
45
- const styles = StyleSheet.create({
45
+ const styles = useMemo(() => StyleSheet.create({
46
46
  container: { padding: tokens.spacing.md, gap: tokens.spacing.md },
47
47
  input: {
48
48
  backgroundColor: tokens.colors.surfaceVariant,
@@ -72,7 +72,7 @@ export const TextEditorSheet: React.FC<TextEditorSheetProps> = ({
72
72
  borderColor: tokens.colors.primary,
73
73
  backgroundColor: tokens.colors.primary + "20",
74
74
  },
75
- });
75
+ }), [tokens]);
76
76
 
77
77
  return (
78
78
  <View style={styles.container}>
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import { useState, useCallback } from "react";
2
2
  import * as ImagePicker from "expo-image-picker";
3
3
 
4
4
  export interface ImagePickerResult {
@@ -16,7 +16,7 @@ export interface UseImagePickerReturn {
16
16
  export const useImagePicker = (): UseImagePickerReturn => {
17
17
  const [loading, setLoading] = useState(false);
18
18
 
19
- const pickFromGallery = async (
19
+ const pickFromGallery = useCallback(async (
20
20
  options?: ImagePicker.ImagePickerOptions,
21
21
  ): Promise<ImagePickerResult | null> => {
22
22
  setLoading(true);
@@ -39,9 +39,9 @@ export const useImagePicker = (): UseImagePickerReturn => {
39
39
  } finally {
40
40
  setLoading(false);
41
41
  }
42
- };
42
+ }, []);
43
43
 
44
- const takePhoto = async (
44
+ const takePhoto = useCallback(async (
45
45
  options?: ImagePicker.ImagePickerOptions,
46
46
  ): Promise<ImagePickerResult | null> => {
47
47
  setLoading(true);
@@ -63,7 +63,7 @@ export const useImagePicker = (): UseImagePickerReturn => {
63
63
  } finally {
64
64
  setLoading(false);
65
65
  }
66
- };
66
+ }, []);
67
67
 
68
68
  return { pickFromGallery, takePhoto, loading };
69
69
  };
@@ -40,7 +40,6 @@ export const usePhotoEditorUI = (initialCaption?: string) => {
40
40
  initialCaptionApplied.current = true;
41
41
  editor.addTextLayer(tokens.colors.textPrimary, { text: initialCaption });
42
42
  }
43
- // eslint-disable-next-line react-hooks/exhaustive-deps
44
43
  }, []);
45
44
 
46
45
  const handleTextLayerTap = useCallback(
@@ -152,12 +151,10 @@ export const usePhotoEditorUI = (initialCaption?: string) => {
152
151
  fontFamily: selectedFont,
153
152
  });
154
153
  textEditorSheetRef.current?.present();
155
- // eslint-disable-next-line react-hooks/exhaustive-deps
156
154
  }, [editor, fontSize, selectedFont, tokens.colors.textPrimary]),
157
155
  handleSelectSticker: useCallback((uri: string) => {
158
156
  editor.addStickerLayer(uri);
159
157
  stickerSheetRef.current?.dismiss();
160
- // eslint-disable-next-line react-hooks/exhaustive-deps
161
158
  }, [editor]),
162
159
  };
163
160
  };