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

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/src/constants.ts CHANGED
@@ -3,60 +3,78 @@
3
3
  * These can be overridden via props
4
4
  */
5
5
 
6
- export const DEFAULT_FONTS = ["Impact", "Comic", "Serif", "Retro"] as const;
6
+ import type { ImageFilters } from "./types";
7
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
- "💫",
8
+ export const DEFAULT_TEXT_COLORS = [
9
+ "#FFFFFF", "#000000", "#888888", "#CCCCCC",
10
+ "#FF3B30", "#FF9500", "#FFCC00", "#FF2D55",
11
+ "#34C759", "#30B0C7", "#007AFF", "#5AC8FA",
12
+ "#5856D6", "#AF52DE", "#FF6B6B", "#FFD93D",
13
+ "#6BCB77", "#4D96FF", "#C77DFF", "#F72585",
14
+ ] as const;
15
+
16
+ export const DEFAULT_FONTS = [
17
+ "System",
18
+ "Impact",
19
+ "Comic",
20
+ "Serif",
21
+ "Retro",
41
22
  ] as const;
42
23
 
43
- export type FilterType = "none" | "sepia" | "grayscale" | "vintage" | "warm" | "cool";
24
+ export const DEFAULT_STICKERS = [
25
+ "😀", "😂", "🤣", "😍", "🥰", "😎", "🤯", "🥳", "😤", "💀",
26
+ "🔥", "❤️", "💯", "✨", "🎉", "🤡", "👀", "🙌", "👏", "💪",
27
+ "🤝", "🙈", "🐶", "🐱", "🦊", "🐸", "🌟", "⭐", "🌈", "☀️",
28
+ "🌙", "💫",
29
+ ] as const;
44
30
 
45
31
  export interface FilterOption {
46
- id: FilterType;
32
+ id: string;
47
33
  name: string;
34
+ /** Valid AtomicIcon name */
48
35
  icon: string;
49
- value: number;
36
+ /** Partial ImageFilters applied when this filter is selected */
37
+ filters: Partial<ImageFilters>;
50
38
  }
51
39
 
52
- export const DEFAULT_FILTERS = [
53
- { id: "none" as FilterType, name: "None", icon: "close-circle", value: 0 },
54
- { id: "sepia" as FilterType, name: "Sepia", icon: "color-palette", value: 0.5 },
55
- { id: "grayscale" as FilterType, name: "B&W", icon: "contrast", value: 1 },
56
- { id: "vintage" as FilterType, name: "Vintage", icon: "time", value: 0.7 },
57
- { id: "warm" as FilterType, name: "Warm", icon: "sunny", value: 0.3 },
58
- { id: "cool" as FilterType, name: "Cool", icon: "snow", value: 0.3 },
59
- ] as const;
40
+ export const DEFAULT_FILTERS: FilterOption[] = [
41
+ {
42
+ id: "none",
43
+ name: "None",
44
+ icon: "close",
45
+ filters: { brightness: 1, contrast: 1, saturation: 1, sepia: 0, grayscale: 0 },
46
+ },
47
+ {
48
+ id: "sepia",
49
+ name: "Sepia",
50
+ icon: "brush",
51
+ filters: { sepia: 0.7, saturation: 0.8 },
52
+ },
53
+ {
54
+ id: "grayscale",
55
+ name: "B&W",
56
+ icon: "swap-horizontal",
57
+ filters: { grayscale: 1, saturation: 0 },
58
+ },
59
+ {
60
+ id: "vintage",
61
+ name: "Vintage",
62
+ icon: "flash",
63
+ filters: { sepia: 0.3, contrast: 1.1, brightness: 0.9 },
64
+ },
65
+ {
66
+ id: "warm",
67
+ name: "Warm",
68
+ icon: "sparkles",
69
+ filters: { brightness: 1.05, saturation: 1.2 },
70
+ },
71
+ {
72
+ id: "cool",
73
+ name: "Cool",
74
+ icon: "image",
75
+ filters: { contrast: 1.05, brightness: 1.02, saturation: 0.85 },
76
+ },
77
+ ];
60
78
 
61
79
  export const DEFAULT_AI_STYLES = [
62
80
  { id: "viral", label: "✨ Viral", desc: "Catchy & shareable" },
@@ -9,57 +9,37 @@ export interface HistoryState<T> {
9
9
  }
10
10
 
11
11
  export class HistoryManager<T> {
12
- private maxHistory = 20;
12
+ private readonly maxHistory = 20;
13
13
 
14
14
  createInitialState(initialValue: T): HistoryState<T> {
15
- return {
16
- past: [],
17
- present: initialValue,
18
- future: [],
19
- };
15
+ return { past: [], present: initialValue, future: [] };
20
16
  }
21
17
 
22
18
  push(history: HistoryState<T>, newValue: T): HistoryState<T> {
23
- const { past, present } = history;
24
-
25
19
  return {
26
- past: [...past.slice(-this.maxHistory + 1), present],
20
+ past: [...history.past.slice(-this.maxHistory + 1), history.present],
27
21
  present: newValue,
28
22
  future: [],
29
23
  };
30
24
  }
31
25
 
32
26
  undo(history: HistoryState<T>): HistoryState<T> {
33
- const { past, present, future } = history;
34
-
35
- if (past.length === 0) {
36
- return history;
37
- }
38
-
39
- const previous = past[past.length - 1];
40
- const newPast = past.slice(0, past.length - 1);
41
-
27
+ if (history.past.length === 0) return history;
28
+ const previous = history.past[history.past.length - 1];
42
29
  return {
43
- past: newPast,
30
+ past: history.past.slice(0, -1),
44
31
  present: previous,
45
- future: [present, ...future],
32
+ future: [history.present, ...history.future],
46
33
  };
47
34
  }
48
35
 
49
36
  redo(history: HistoryState<T>): HistoryState<T> {
50
- const { past, present, future } = history;
51
-
52
- if (future.length === 0) {
53
- return history;
54
- }
55
-
56
- const next = future[0];
57
- const newFuture = future.slice(1);
58
-
37
+ if (history.future.length === 0) return history;
38
+ const next = history.future[0];
59
39
  return {
60
- past: [...past, present],
40
+ past: [...history.past, history.present],
61
41
  present: next,
62
- future: newFuture,
42
+ future: history.future.slice(1),
63
43
  };
64
44
  }
65
45
 
@@ -71,5 +51,3 @@ export class HistoryManager<T> {
71
51
  return history.future.length > 0;
72
52
  }
73
53
  }
74
-
75
-
@@ -0,0 +1,69 @@
1
+ import { useState } from "react";
2
+ import * as ImagePicker from "expo-image-picker";
3
+
4
+ export interface ImagePickerResult {
5
+ uri: string;
6
+ width: number;
7
+ height: number;
8
+ }
9
+
10
+ export interface UseImagePickerReturn {
11
+ pickFromGallery: (options?: ImagePicker.ImagePickerOptions) => Promise<ImagePickerResult | null>;
12
+ takePhoto: (options?: ImagePicker.ImagePickerOptions) => Promise<ImagePickerResult | null>;
13
+ loading: boolean;
14
+ }
15
+
16
+ export const useImagePicker = (): UseImagePickerReturn => {
17
+ const [loading, setLoading] = useState(false);
18
+
19
+ const pickFromGallery = async (
20
+ options?: ImagePicker.ImagePickerOptions,
21
+ ): Promise<ImagePickerResult | null> => {
22
+ setLoading(true);
23
+ try {
24
+ const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
25
+ if (status !== "granted") return null;
26
+
27
+ const result = await ImagePicker.launchImageLibraryAsync({
28
+ mediaTypes: ["images"],
29
+ allowsEditing: false,
30
+ quality: 1,
31
+ ...options,
32
+ });
33
+
34
+ if (result.canceled || result.assets.length === 0) return null;
35
+ const asset = result.assets[0];
36
+ return { uri: asset.uri, width: asset.width, height: asset.height };
37
+ } catch {
38
+ return null;
39
+ } finally {
40
+ setLoading(false);
41
+ }
42
+ };
43
+
44
+ const takePhoto = async (
45
+ options?: ImagePicker.ImagePickerOptions,
46
+ ): Promise<ImagePickerResult | null> => {
47
+ setLoading(true);
48
+ try {
49
+ const { status } = await ImagePicker.requestCameraPermissionsAsync();
50
+ if (status !== "granted") return null;
51
+
52
+ const result = await ImagePicker.launchCameraAsync({
53
+ mediaTypes: ["images"],
54
+ quality: 1,
55
+ ...options,
56
+ });
57
+
58
+ if (result.canceled || result.assets.length === 0) return null;
59
+ const asset = result.assets[0];
60
+ return { uri: asset.uri, width: asset.width, height: asset.height };
61
+ } catch {
62
+ return null;
63
+ } finally {
64
+ setLoading(false);
65
+ }
66
+ };
67
+
68
+ return { pickFromGallery, takePhoto, loading };
69
+ };
@@ -1,34 +1,31 @@
1
1
  import { useState, useCallback, useMemo } from "react";
2
- import { DesignTokens } from "@umituz/react-native-design-system";
3
- import { Layer, TextLayer, StickerLayer, ImageFilters } from "../types";
2
+ import { Layer, TextLayer, StickerLayer, ImageFilters, DEFAULT_IMAGE_FILTERS } from "../types";
4
3
  import { HistoryManager, HistoryState } from "../core/HistoryManager";
5
4
 
6
- const DEFAULT_FILTERS: ImageFilters = {
7
- brightness: 1,
8
- contrast: 1,
9
- saturation: 1,
10
- sepia: 0,
11
- grayscale: 0,
12
- };
13
-
14
5
  export const usePhotoEditor = (initialLayers: Layer[] = []) => {
15
6
  const historyManager = useMemo(() => new HistoryManager<Layer[]>(), []);
16
7
  const [history, setHistory] = useState<HistoryState<Layer[]>>(() =>
17
8
  historyManager.createInitialState(initialLayers),
18
9
  );
19
10
  const [activeLayerId, setActiveLayerId] = useState<string | null>(
20
- initialLayers[0]?.id || null,
11
+ initialLayers[0]?.id ?? null,
21
12
  );
22
- const [filters, setFilters] = useState<ImageFilters>(DEFAULT_FILTERS);
13
+ const [filters, setFilters] = useState<ImageFilters>(DEFAULT_IMAGE_FILTERS);
23
14
 
24
15
  const layers = history.present;
25
16
 
26
- const pushState = useCallback((newLayers: Layer[]) => {
27
- setHistory((prev) => historyManager.push(prev, newLayers));
28
- }, [historyManager]);
17
+ const pushState = useCallback(
18
+ (newLayers: Layer[]) => {
19
+ setHistory((prev) => historyManager.push(prev, newLayers));
20
+ },
21
+ [historyManager],
22
+ );
29
23
 
30
24
  const addTextLayer = useCallback(
31
- (tokens: DesignTokens) => {
25
+ (
26
+ defaultColor = "#FFFFFF",
27
+ overrides: Partial<Omit<TextLayer, "id" | "type">> = {},
28
+ ) => {
32
29
  const id = `text-${Date.now()}`;
33
30
  const newLayer: TextLayer = {
34
31
  id,
@@ -42,9 +39,10 @@ export const usePhotoEditor = (initialLayers: Layer[] = []) => {
42
39
  zIndex: layers.length,
43
40
  fontSize: 32,
44
41
  fontFamily: "System",
45
- color: tokens.colors.textPrimary,
42
+ color: defaultColor,
46
43
  backgroundColor: "transparent",
47
44
  textAlign: "center",
45
+ ...overrides,
48
46
  };
49
47
  pushState([...layers, newLayer]);
50
48
  setActiveLayerId(id);
@@ -76,7 +74,9 @@ export const usePhotoEditor = (initialLayers: Layer[] = []) => {
76
74
 
77
75
  const updateLayer = useCallback(
78
76
  (id: string, updates: Partial<Layer>) => {
79
- const newLayers = layers.map((l) => (l.id === id ? { ...l, ...updates } : l) as Layer);
77
+ const newLayers = layers.map(
78
+ (l) => (l.id === id ? ({ ...l, ...updates } as Layer) : l),
79
+ );
80
80
  pushState(newLayers);
81
81
  },
82
82
  [layers, pushState],
@@ -86,11 +86,60 @@ export const usePhotoEditor = (initialLayers: Layer[] = []) => {
86
86
  (id: string) => {
87
87
  const newLayers = layers.filter((l) => l.id !== id);
88
88
  pushState(newLayers);
89
- if (activeLayerId === id) setActiveLayerId(newLayers[0]?.id || null);
89
+ if (activeLayerId === id) {
90
+ setActiveLayerId(newLayers[0]?.id ?? null);
91
+ }
90
92
  },
91
93
  [layers, activeLayerId, pushState],
92
94
  );
93
95
 
96
+ const duplicateLayer = useCallback(
97
+ (id: string) => {
98
+ const layer = layers.find((l) => l.id === id);
99
+ if (!layer) return null;
100
+ const newId = `${layer.type}-${Date.now()}`;
101
+ const newLayer = { ...layer, id: newId, x: layer.x + 20, y: layer.y + 20, zIndex: layers.length };
102
+ pushState([...layers, newLayer]);
103
+ setActiveLayerId(newId);
104
+ return newId;
105
+ },
106
+ [layers, pushState],
107
+ );
108
+
109
+ const moveLayerUp = useCallback(
110
+ (id: string) => {
111
+ const sorted = [...layers].sort((a, b) => a.zIndex - b.zIndex);
112
+ const idx = sorted.findIndex((l) => l.id === id);
113
+ if (idx >= sorted.length - 1) return;
114
+ const reordered = [...sorted];
115
+ [reordered[idx], reordered[idx + 1]] = [reordered[idx + 1], reordered[idx]];
116
+ pushState(reordered.map((l, i) => ({ ...l, zIndex: i })));
117
+ },
118
+ [layers, pushState],
119
+ );
120
+
121
+ const moveLayerDown = useCallback(
122
+ (id: string) => {
123
+ const sorted = [...layers].sort((a, b) => a.zIndex - b.zIndex);
124
+ const idx = sorted.findIndex((l) => l.id === id);
125
+ if (idx <= 0) return;
126
+ const reordered = [...sorted];
127
+ [reordered[idx], reordered[idx - 1]] = [reordered[idx - 1], reordered[idx]];
128
+ pushState(reordered.map((l, i) => ({ ...l, zIndex: i })));
129
+ },
130
+ [layers, pushState],
131
+ );
132
+
133
+ const undo = useCallback(
134
+ () => setHistory((prev) => historyManager.undo(prev)),
135
+ [historyManager],
136
+ );
137
+
138
+ const redo = useCallback(
139
+ () => setHistory((prev) => historyManager.redo(prev)),
140
+ [historyManager],
141
+ );
142
+
94
143
  return {
95
144
  layers: [...layers].sort((a, b) => a.zIndex - b.zIndex),
96
145
  activeLayerId,
@@ -99,9 +148,12 @@ export const usePhotoEditor = (initialLayers: Layer[] = []) => {
99
148
  addStickerLayer,
100
149
  updateLayer,
101
150
  deleteLayer,
151
+ duplicateLayer,
152
+ moveLayerUp,
153
+ moveLayerDown,
102
154
  selectLayer: setActiveLayerId,
103
- undo: useCallback(() => setHistory(historyManager.undo), [historyManager]),
104
- redo: useCallback(() => setHistory(historyManager.redo), [historyManager]),
155
+ undo,
156
+ redo,
105
157
  canUndo: historyManager.canUndo(history),
106
158
  canRedo: historyManager.canRedo(history),
107
159
  filters,
@@ -1,31 +1,47 @@
1
1
  import { useRef, useState, useCallback, useEffect } from "react";
2
- import { BottomSheetModalRef, DesignTokens } from "@umituz/react-native-design-system";
2
+ import { BottomSheetModalRef } from "@umituz/react-native-design-system/molecules";
3
+ import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
3
4
  import { usePhotoEditor } from "./usePhotoEditor";
4
- import { TextLayer } from "../types";
5
+ import { TextLayer, DEFAULT_IMAGE_FILTERS, ImageFilters, TextAlign } from "../types";
6
+ import type { FilterOption } from "../constants";
7
+ import type { LayerTransform } from "../components/DraggableText";
5
8
 
6
- export const usePhotoEditorUI = (
7
- initialCaption: string | undefined,
8
- tokens: DesignTokens,
9
- ) => {
9
+ export const usePhotoEditorUI = (initialCaption?: string) => {
10
+ const tokens = useAppDesignTokens();
11
+
12
+ // Bottom sheet refs
10
13
  const textEditorSheetRef = useRef<BottomSheetModalRef>(null);
11
14
  const stickerSheetRef = useRef<BottomSheetModalRef>(null);
12
15
  const filterSheetRef = useRef<BottomSheetModalRef>(null);
16
+ const adjustmentsSheetRef = useRef<BottomSheetModalRef>(null);
13
17
  const layerSheetRef = useRef<BottomSheetModalRef>(null);
14
18
  const aiSheetRef = useRef<BottomSheetModalRef>(null);
15
19
 
20
+ // Global text/font state
16
21
  const [selectedFont, setSelectedFont] = useState<string>("System");
17
22
  const [fontSize, setFontSize] = useState(48);
23
+
24
+ // Per-layer text editing state (populated when sheet opens)
18
25
  const [editingText, setEditingText] = useState("");
26
+ const [editingColor, setEditingColor] = useState<string>(tokens.colors.textPrimary);
27
+ const [editingAlign, setEditingAlign] = useState<TextAlign>("center");
28
+ const [editingBold, setEditingBold] = useState(false);
29
+ const [editingItalic, setEditingItalic] = useState(false);
30
+
31
+ // Filter state
19
32
  const [selectedFilter, setSelectedFilter] = useState("none");
20
33
 
21
34
  const editor = usePhotoEditor([]);
22
35
 
36
+ // Apply initial caption once on mount — single history entry
37
+ const initialCaptionApplied = useRef(false);
23
38
  useEffect(() => {
24
- if (initialCaption) {
25
- const id = editor.addTextLayer(tokens);
26
- editor.updateLayer(id, { text: initialCaption });
39
+ if (initialCaption && !initialCaptionApplied.current) {
40
+ initialCaptionApplied.current = true;
41
+ editor.addTextLayer(tokens.colors.textPrimary, { text: initialCaption });
27
42
  }
28
- }, [initialCaption]);
43
+ // eslint-disable-next-line react-hooks/exhaustive-deps
44
+ }, []);
29
45
 
30
46
  const handleTextLayerTap = useCallback(
31
47
  (layerId: string) => {
@@ -33,12 +49,16 @@ export const usePhotoEditorUI = (
33
49
  const layer = editor.layers.find((l) => l.id === layerId);
34
50
  if (layer?.type === "text") {
35
51
  const textLayer = layer as TextLayer;
36
- setEditingText(textLayer.text || "");
37
- setFontSize(textLayer.fontSize || 48);
52
+ setEditingText(textLayer.text ?? "");
53
+ setFontSize(textLayer.fontSize ?? 48);
54
+ setEditingColor(textLayer.color ?? tokens.colors.textPrimary);
55
+ setEditingAlign(textLayer.textAlign ?? "center");
56
+ setEditingBold(textLayer.isBold ?? false);
57
+ setEditingItalic(textLayer.isItalic ?? false);
38
58
  textEditorSheetRef.current?.present();
39
59
  }
40
60
  },
41
- [editor],
61
+ [editor, tokens.colors.textPrimary],
42
62
  );
43
63
 
44
64
  const handleSaveText = useCallback(() => {
@@ -47,39 +67,97 @@ export const usePhotoEditorUI = (
47
67
  text: editingText,
48
68
  fontSize,
49
69
  fontFamily: selectedFont,
70
+ color: editingColor,
71
+ textAlign: editingAlign,
72
+ isBold: editingBold,
73
+ isItalic: editingItalic,
50
74
  });
51
75
  }
52
76
  textEditorSheetRef.current?.dismiss();
53
- }, [editor.activeLayerId, editingText, fontSize, selectedFont, editor.updateLayer]);
77
+ }, [
78
+ editor,
79
+ editingText,
80
+ fontSize,
81
+ selectedFont,
82
+ editingColor,
83
+ editingAlign,
84
+ editingBold,
85
+ editingItalic,
86
+ ]);
87
+
88
+ const handleSelectFilter = useCallback(
89
+ (option: FilterOption) => {
90
+ setSelectedFilter(option.id);
91
+ const newFilters: ImageFilters = { ...DEFAULT_IMAGE_FILTERS, ...option.filters };
92
+ editor.updateFilters(newFilters);
93
+ filterSheetRef.current?.dismiss();
94
+ },
95
+ [editor],
96
+ );
97
+
98
+ const handleLayerTransform = useCallback(
99
+ (layerId: string, transform: LayerTransform) => {
100
+ editor.updateLayer(layerId, {
101
+ x: transform.x,
102
+ y: transform.y,
103
+ scale: transform.scale,
104
+ rotation: transform.rotation,
105
+ });
106
+ },
107
+ [editor],
108
+ );
54
109
 
55
110
  return {
56
111
  ...editor,
112
+ // Sheet refs
57
113
  textEditorSheetRef,
58
114
  stickerSheetRef,
59
115
  filterSheetRef,
116
+ adjustmentsSheetRef,
60
117
  layerSheetRef,
61
118
  aiSheetRef,
119
+ // Font/size
62
120
  selectedFont,
63
121
  setSelectedFont,
64
122
  fontSize,
65
123
  setFontSize,
124
+ // Text editing
66
125
  editingText,
67
126
  setEditingText,
127
+ editingColor,
128
+ setEditingColor,
129
+ editingAlign,
130
+ setEditingAlign,
131
+ editingBold,
132
+ setEditingBold,
133
+ editingItalic,
134
+ setEditingItalic,
135
+ // Filter
68
136
  selectedFilter,
137
+ // Handlers
69
138
  handleTextLayerTap,
70
139
  handleSaveText,
71
- handleAddText: () => {
72
- editor.addTextLayer(tokens);
140
+ handleSelectFilter,
141
+ handleLayerTransform,
142
+ handleAddText: useCallback(() => {
143
+ const color = tokens.colors.textPrimary;
144
+ setEditingText("");
145
+ setEditingColor(color);
146
+ setEditingAlign("center");
147
+ setEditingBold(false);
148
+ setEditingItalic(false);
149
+ // Create layer with the currently active font settings so canvas preview matches sheet
150
+ editor.addTextLayer(color, {
151
+ fontSize,
152
+ fontFamily: selectedFont,
153
+ });
73
154
  textEditorSheetRef.current?.present();
74
- },
75
- handleSelectSticker: (s: string) => {
76
- editor.addStickerLayer(s);
155
+ // eslint-disable-next-line react-hooks/exhaustive-deps
156
+ }, [editor, fontSize, selectedFont, tokens.colors.textPrimary]),
157
+ handleSelectSticker: useCallback((uri: string) => {
158
+ editor.addStickerLayer(uri);
77
159
  stickerSheetRef.current?.dismiss();
78
- },
79
- handleSelectFilter: (id: string, val: number) => {
80
- setSelectedFilter(id);
81
- editor.updateFilters({ ...editor.filters, [id]: val });
82
- filterSheetRef.current?.dismiss();
83
- }
160
+ // eslint-disable-next-line react-hooks/exhaustive-deps
161
+ }, [editor]),
84
162
  };
85
163
  };
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  export * from "./types";
2
2
  export * from "./constants";
3
3
  export * from "./hooks/usePhotoEditor";
4
+ export * from "./hooks/usePhotoEditorUI";
5
+ export * from "./hooks/useImagePicker";
6
+ export * from "./utils/mediaUtils";
4
7
  export * from "./core/HistoryManager";
5
8
  export * from "./components/EditorCanvas";
6
9
  export * from "./components/LayerManager";
@@ -9,4 +12,9 @@ export * from "./components/FilterPicker";
9
12
  export * from "./components/DraggableText";
10
13
  export * from "./components/DraggableSticker";
11
14
  export * from "./components/AIMagicSheet";
15
+ export * from "./components/TextEditorSheet";
16
+ export * from "./components/StickerPicker";
17
+ export * from "./components/EditorToolbar";
18
+ export * from "./components/ColorPicker";
19
+ export * from "./components/AdjustmentsSheet";
12
20
  export * from "./PhotoEditor";
package/src/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StyleSheet } from "react-native";
2
- import { DesignTokens } from "@umituz/react-native-design-system";
2
+ import { DesignTokens } from "@umituz/react-native-design-system/theme";
3
3
  import { EdgeInsets } from "react-native-safe-area-context";
4
4
 
5
5
  export const createEditorStyles = (tokens: DesignTokens, insets: EdgeInsets) =>
@@ -18,8 +18,8 @@ export const createEditorStyles = (tokens: DesignTokens, insets: EdgeInsets) =>
18
18
  flex: 1,
19
19
  textAlign: "center",
20
20
  },
21
- scrollContent: {
22
- paddingHorizontal: tokens.spacing.md,
21
+ scrollContent: {
22
+ paddingHorizontal: tokens.spacing.md,
23
23
  paddingBottom: 120,
24
24
  gap: tokens.spacing.lg,
25
25
  },
@@ -37,8 +37,18 @@ export const createEditorStyles = (tokens: DesignTokens, insets: EdgeInsets) =>
37
37
  borderRadius: tokens.borders.radius.md,
38
38
  gap: tokens.spacing.md,
39
39
  },
40
- fontRow: {
41
- flexDirection: "row",
40
+ sliderRow: {
41
+ flexDirection: "row",
42
+ alignItems: "center",
43
+ justifyContent: "space-between",
44
+ },
45
+ sliderLabel: {
46
+ flexDirection: "row",
47
+ alignItems: "center",
48
+ gap: tokens.spacing.xs,
49
+ },
50
+ fontRow: {
51
+ flexDirection: "row",
42
52
  gap: tokens.spacing.sm,
43
53
  flexWrap: "wrap",
44
54
  },
@@ -74,7 +84,15 @@ export const createEditorStyles = (tokens: DesignTokens, insets: EdgeInsets) =>
74
84
  padding: tokens.spacing.sm,
75
85
  borderRadius: 999,
76
86
  },
77
- toolButtonActive: {
87
+ toolButtonActive: {
78
88
  backgroundColor: tokens.colors.primary + "20",
79
89
  },
90
+ aiMagicButton: {
91
+ width: 48,
92
+ height: 48,
93
+ borderRadius: 24,
94
+ backgroundColor: tokens.colors.primary,
95
+ alignItems: "center",
96
+ justifyContent: "center",
97
+ },
80
98
  });