@umituz/react-native-photo-editor 2.0.22 → 2.0.23

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.
Files changed (30) hide show
  1. package/ARCHITECTURE.md +104 -0
  2. package/MIGRATION.md +100 -0
  3. package/package.json +1 -1
  4. package/src/PhotoEditor.tsx +56 -44
  5. package/src/application/hooks/useEditor.ts +67 -0
  6. package/src/application/hooks/useEditorUI.ts +145 -0
  7. package/src/application/stores/EditorStore.ts +137 -0
  8. package/src/constants.ts +5 -52
  9. package/src/domain/entities/Filters.ts +72 -0
  10. package/src/domain/entities/Layer.ts +126 -0
  11. package/src/domain/entities/Transform.ts +55 -0
  12. package/src/domain/services/HistoryService.ts +60 -0
  13. package/src/domain/services/LayerService.ts +105 -0
  14. package/src/index.ts +25 -5
  15. package/src/infrastructure/gesture/types.ts +27 -0
  16. package/src/infrastructure/gesture/useTransformGesture.ts +136 -0
  17. package/src/infrastructure/history/HistoryManager.ts +38 -0
  18. package/src/presentation/components/DraggableLayer.tsx +114 -0
  19. package/src/presentation/components/EditorCanvas.tsx +90 -0
  20. package/src/presentation/components/EditorToolbar.tsx +192 -0
  21. package/src/presentation/components/FontControls.tsx +99 -0
  22. package/src/presentation/components/sheets/AIMagicSheet.tsx +99 -0
  23. package/src/presentation/components/sheets/AdjustmentsSheet.tsx +113 -0
  24. package/src/presentation/components/sheets/FilterSheet.tsx +128 -0
  25. package/src/presentation/components/sheets/LayerManager.tsx +151 -0
  26. package/src/presentation/components/sheets/StickerPicker.tsx +67 -0
  27. package/src/presentation/components/sheets/TextEditorSheet.tsx +159 -0
  28. package/src/presentation/components/ui/ColorPicker.tsx +78 -0
  29. package/src/presentation/components/ui/Slider.tsx +116 -0
  30. package/src/types.ts +13 -58
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Editor Store
3
+ * Central state management for the editor (React hooks based)
4
+ */
5
+
6
+ import { useState, useCallback, useMemo } from "react";
7
+ import { Layer, TextLayer, StickerLayer } from "../../domain/entities/Layer";
8
+ import { FiltersVO, FilterValues, DEFAULT_FILTERS } from "../../domain/entities/Filters";
9
+ import { HistoryService, HistoryState } from "../../domain/services/HistoryService";
10
+ import { LayerService } from "../../domain/services/LayerService";
11
+ import type { Transform } from "../../domain/entities/Transform";
12
+
13
+ const historyService = new HistoryService<Layer[]>(20);
14
+ const layerService = new LayerService();
15
+
16
+ export function useEditorStore() {
17
+ // History state
18
+ const [history, setHistory] = useState<HistoryState<Layer[]>>(() =>
19
+ historyService.createInitialState([])
20
+ );
21
+ const [activeLayerId, setActiveLayerId] = useState<string | null>(null);
22
+ const [filters, setFilters] = useState<FiltersVO>(FiltersVO.default());
23
+
24
+ // History actions
25
+ const pushLayers = useCallback((layers: Layer[]) => {
26
+ setHistory((prev) => historyService.push(prev, layers));
27
+ }, []);
28
+
29
+ const undo = useCallback(() => {
30
+ setHistory((prev) => historyService.undo(prev));
31
+ }, []);
32
+
33
+ const redo = useCallback(() => {
34
+ setHistory((prev) => historyService.redo(prev));
35
+ }, []);
36
+
37
+ // Layer actions
38
+ const addTextLayer = useCallback((overrides?: Partial<Omit<TextLayerData, "id" | "type">>) => {
39
+ const layer = layerService.createTextLayer({
40
+ ...overrides,
41
+ zIndex: history.present.length,
42
+ });
43
+ pushLayers([...history.present, layer]);
44
+ setActiveLayerId(layer.id);
45
+ return layer.id;
46
+ }, [history.present, pushLayers]);
47
+
48
+ const addStickerLayer = useCallback((uri: string) => {
49
+ const layer = layerService.createStickerLayer(uri, {
50
+ zIndex: history.present.length,
51
+ });
52
+ pushLayers([...history.present, layer]);
53
+ setActiveLayerId(layer.id);
54
+ return layer.id;
55
+ }, [history.present, pushLayers]);
56
+
57
+ const updateLayer = useCallback((id: string, updates: Partial<Transform>) => {
58
+ const layers = layerService.updateLayer(history.present, id, updates);
59
+ pushLayers(layers);
60
+ }, [history.present, pushLayers]);
61
+
62
+ const deleteLayer = useCallback((id: string) => {
63
+ const layers = layerService.deleteLayer(history.present, id);
64
+ pushLayers(layers);
65
+ if (activeLayerId === id) {
66
+ setActiveLayerId(layers[0]?.id ?? null);
67
+ }
68
+ }, [history.present, activeLayerId, pushLayers]);
69
+
70
+ const duplicateLayer = useCallback((id: string) => {
71
+ const layers = layerService.duplicateLayer(history.present, id);
72
+ pushLayers(layers);
73
+ const newLayer = layers[layers.length - 1];
74
+ setActiveLayerId(newLayer.id);
75
+ }, [history.present, pushLayers]);
76
+
77
+ const moveLayerUp = useCallback((id: string) => {
78
+ const layers = layerService.moveLayerUp(history.present, id);
79
+ pushLayers(layers);
80
+ }, [history.present, pushLayers]);
81
+
82
+ const moveLayerDown = useCallback((id: string) => {
83
+ const layers = layerService.moveLayerDown(history.present, id);
84
+ pushLayers(layers);
85
+ }, [history.present, pushLayers]);
86
+
87
+ const selectLayer = useCallback((id: string | null) => {
88
+ setActiveLayerId(id);
89
+ }, []);
90
+
91
+ // Filter actions
92
+ const updateFilters = useCallback((updates: Partial<FilterValues>) => {
93
+ const current = filters.toJSON();
94
+ setFilters(FiltersVO.from({ ...current, ...updates }));
95
+ }, [filters]);
96
+
97
+ const resetFilters = useCallback(() => {
98
+ setFilters(FiltersVO.default());
99
+ }, []);
100
+
101
+ // Getters
102
+ const layers = useMemo(() => layerService.sortByZIndex(history.present), [history.present]);
103
+ const activeLayer = useMemo(() =>
104
+ history.present.find(l => l.id === activeLayerId) ?? null,
105
+ [history.present, activeLayerId]
106
+ );
107
+ const canUndo = useMemo(() => historyService.canUndo(history), [history]);
108
+ const canRedo = useMemo(() => historyService.canRedo(history), [history]);
109
+
110
+ return {
111
+ // State
112
+ layers,
113
+ activeLayerId,
114
+ activeLayer,
115
+ filters: filters.toJSON(),
116
+ canUndo,
117
+ canRedo,
118
+
119
+ // History actions
120
+ undo,
121
+ redo,
122
+
123
+ // Layer actions
124
+ addTextLayer,
125
+ addStickerLayer,
126
+ updateLayer,
127
+ deleteLayer,
128
+ duplicateLayer,
129
+ moveLayerUp,
130
+ moveLayerDown,
131
+ selectLayer,
132
+
133
+ // Filter actions
134
+ updateFilters,
135
+ resetFilters,
136
+ };
137
+ }
package/src/constants.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  /**
2
- * Default constants for Photo Editor
3
- * These can be overridden via props
2
+ * Default Constants for Photo Editor
4
3
  */
5
4
 
6
- import type { ImageFilters } from "./types";
7
-
8
5
  export const DEFAULT_TEXT_COLORS = [
9
6
  "#FFFFFF", "#000000", "#888888", "#CCCCCC",
10
7
  "#FF3B30", "#FF9500", "#FFCC00", "#FF2D55",
@@ -28,54 +25,6 @@ export const DEFAULT_STICKERS = [
28
25
  "🌙", "💫",
29
26
  ] as const;
30
27
 
31
- export interface FilterOption {
32
- id: string;
33
- name: string;
34
- /** Valid AtomicIcon name */
35
- icon: string;
36
- /** Partial ImageFilters applied when this filter is selected */
37
- filters: Partial<ImageFilters>;
38
- }
39
-
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
- ];
78
-
79
28
  export const DEFAULT_AI_STYLES = [
80
29
  { id: "viral", label: "✨ Viral", desc: "Catchy & shareable" },
81
30
  { id: "funny", label: "😂 Funny", desc: "Humor that connects" },
@@ -84,3 +33,7 @@ export const DEFAULT_AI_STYLES = [
84
33
  { id: "sarcastic", label: "😏 Sarcastic", desc: "Witty & ironic" },
85
34
  { id: "relatable", label: "🎯 Relatable", desc: "Everyone gets it" },
86
35
  ] as const;
36
+
37
+ // Filter presets will be in the presentation layer
38
+ export { DEFAULT_FILTERS } from "./presentation/components/sheets/FilterSheet";
39
+ export type { FilterOption } from "./presentation/components/sheets/FilterSheet";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Filters Value Object
3
+ * Represents image filter adjustments
4
+ */
5
+
6
+ export interface FilterValues {
7
+ brightness: number;
8
+ contrast: number;
9
+ saturation: number;
10
+ sepia: number;
11
+ grayscale: number;
12
+ hueRotate?: number;
13
+ }
14
+
15
+ export const DEFAULT_FILTERS: FilterValues = {
16
+ brightness: 1,
17
+ contrast: 1,
18
+ saturation: 1,
19
+ sepia: 0,
20
+ grayscale: 0,
21
+ };
22
+
23
+ export class FiltersVO {
24
+ constructor(private readonly value: FilterValues) {}
25
+
26
+ get brightness(): number { return this.value.brightness; }
27
+ get contrast(): number { return this.value.contrast; }
28
+ get saturation(): number { return this.value.saturation; }
29
+ get sepia(): number { return this.value.sepia; }
30
+ get grayscale(): number { return this.value.grayscale; }
31
+ get hueRotate(): number | undefined { return this.value.hueRotate; }
32
+
33
+ withBrightness(brightness: number): FiltersVO {
34
+ return new FiltersVO({ ...this.value, brightness });
35
+ }
36
+
37
+ withContrast(contrast: number): FiltersVO {
38
+ return new FiltersVO({ ...this.value, contrast });
39
+ }
40
+
41
+ withSaturation(saturation: number): FiltersVO {
42
+ return new FiltersVO({ ...this.value, saturation });
43
+ }
44
+
45
+ withSepia(sepia: number): FiltersVO {
46
+ return new FiltersVO({ ...this.value, sepia });
47
+ }
48
+
49
+ withGrayscale(grayscale: number): FiltersVO {
50
+ return new FiltersVO({ ...this.value, grayscale });
51
+ }
52
+
53
+ withHueRotate(hueRotate: number): FiltersVO {
54
+ return new FiltersVO({ ...this.value, hueRotate });
55
+ }
56
+
57
+ reset(): FiltersVO {
58
+ return new FiltersVO(DEFAULT_FILTERS);
59
+ }
60
+
61
+ toJSON(): FilterValues {
62
+ return { ...this.value };
63
+ }
64
+
65
+ static from(filters: FilterValues): FiltersVO {
66
+ return new FiltersVO(filters);
67
+ }
68
+
69
+ static default(): FiltersVO {
70
+ return new FiltersVO(DEFAULT_FILTERS);
71
+ }
72
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Layer Entities
3
+ * Base layer and specialized text/sticker layers
4
+ */
5
+
6
+ import type { Transform } from "./Transform";
7
+
8
+ export type LayerType = "text" | "sticker";
9
+ export type TextAlign = "left" | "center" | "right";
10
+
11
+ export interface BaseLayerData {
12
+ id: string;
13
+ type: LayerType;
14
+ x: number;
15
+ y: number;
16
+ rotation: number;
17
+ scale: number;
18
+ opacity: number;
19
+ zIndex: number;
20
+ }
21
+
22
+ export interface TextLayerData extends BaseLayerData {
23
+ type: "text";
24
+ text: string;
25
+ fontSize: number;
26
+ fontFamily: string;
27
+ color: string;
28
+ backgroundColor: string;
29
+ textAlign: TextAlign;
30
+ isBold?: boolean;
31
+ isItalic?: boolean;
32
+ strokeColor?: string;
33
+ strokeWidth?: number;
34
+ }
35
+
36
+ export interface StickerLayerData extends BaseLayerData {
37
+ type: "sticker";
38
+ uri: string;
39
+ }
40
+
41
+ export type LayerData = TextLayerData | StickerLayerData;
42
+
43
+ export class Layer {
44
+ constructor(private readonly data: LayerData) {}
45
+
46
+ get id(): string { return this.data.id; }
47
+ get type(): LayerType { return this.data.type; }
48
+ get x(): number { return this.data.x; }
49
+ get y(): number { return this.data.y; }
50
+ get rotation(): number { return this.data.rotation; }
51
+ get scale(): number { return this.data.scale; }
52
+ get opacity(): number { return this.data.opacity; }
53
+ get zIndex(): number { return this.data.zIndex; }
54
+
55
+ isText(): this is TextLayer {
56
+ return this.data.type === "text";
57
+ }
58
+
59
+ isSticker(): this is StickerLayer {
60
+ return this.data.type === "sticker";
61
+ }
62
+
63
+ withTransform(transform: Partial<Transform>): Layer {
64
+ const newData = { ...this.data, ...transform };
65
+ return this.recreate(newData);
66
+ }
67
+
68
+ withOpacity(opacity: number): Layer {
69
+ return this.recreate({ ...this.data, opacity });
70
+ }
71
+
72
+ withZIndex(zIndex: number): Layer {
73
+ return this.recreate({ ...this.data, zIndex });
74
+ }
75
+
76
+ protected recreate(data: LayerData): Layer {
77
+ return data.type === "text" ? new TextLayer(data) : new StickerLayer(data);
78
+ }
79
+
80
+ toJSON(): LayerData {
81
+ return { ...this.data };
82
+ }
83
+
84
+ static from(data: LayerData): Layer {
85
+ return data.type === "text" ? new TextLayer(data) : new StickerLayer(data);
86
+ }
87
+ }
88
+
89
+ export class TextLayer extends Layer {
90
+ declare readonly data: TextLayerData;
91
+
92
+ get text(): string { return this.data.text; }
93
+ get fontSize(): number { return this.data.fontSize; }
94
+ get fontFamily(): string { return this.data.fontFamily; }
95
+ get color(): string { return this.data.color; }
96
+ get backgroundColor(): string { return this.data.backgroundColor; }
97
+ get textAlign(): TextAlign { return this.data.textAlign; }
98
+ get isBold(): boolean { return this.data.isBold ?? false; }
99
+ get isItalic(): boolean { return this.data.isItalic ?? false; }
100
+
101
+ withText(text: string): TextLayer {
102
+ return new TextLayer({ ...this.data, text });
103
+ }
104
+
105
+ withStyle(styles: Partial<Omit<TextLayerData, "id" | "type">>): TextLayer {
106
+ return new TextLayer({ ...this.data, ...styles });
107
+ }
108
+ }
109
+
110
+ export class StickerLayer extends Layer {
111
+ declare readonly data: StickerLayerData;
112
+
113
+ get uri(): string { return this.data.uri; }
114
+
115
+ withUri(uri: string): StickerLayer {
116
+ return new StickerLayer({ ...this.data, uri });
117
+ }
118
+ }
119
+
120
+ export function isTextLayer(layer: Layer): layer is TextLayer {
121
+ return layer.isText();
122
+ }
123
+
124
+ export function isStickerLayer(layer: Layer): layer is StickerLayer {
125
+ return layer.isSticker();
126
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Transform Value Object
3
+ * Represents position, scale, and rotation state
4
+ */
5
+
6
+ export interface Transform {
7
+ x: number;
8
+ y: number;
9
+ scale: number;
10
+ rotation: number;
11
+ }
12
+
13
+ export const DEFAULT_TRANSFORM: Transform = {
14
+ x: 50,
15
+ y: 50,
16
+ scale: 1,
17
+ rotation: 0,
18
+ };
19
+
20
+ export class TransformVO {
21
+ constructor(private readonly value: Transform) {}
22
+
23
+ get x(): number { return this.value.x; }
24
+ get y(): number { return this.value.y; }
25
+ get scale(): number { return this.value.scale; }
26
+ get rotation(): number { return this.value.rotation; }
27
+
28
+ withX(x: number): TransformVO {
29
+ return new TransformVO({ ...this.value, x });
30
+ }
31
+
32
+ withY(y: number): TransformVO {
33
+ return new TransformVO({ ...this.value, y });
34
+ }
35
+
36
+ withScale(scale: number): TransformVO {
37
+ return new TransformVO({ ...this.value, scale });
38
+ }
39
+
40
+ withRotation(rotation: number): TransformVO {
41
+ return new TransformVO({ ...this.value, rotation });
42
+ }
43
+
44
+ toJSON(): Transform {
45
+ return { ...this.value };
46
+ }
47
+
48
+ static from(transform: Transform): TransformVO {
49
+ return new TransformVO(transform);
50
+ }
51
+
52
+ static default(): TransformVO {
53
+ return new TransformVO(DEFAULT_TRANSFORM);
54
+ }
55
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * History Service
3
+ * Manages undo/redo state with configurable max history
4
+ */
5
+
6
+ export interface HistoryState<T> {
7
+ past: T[];
8
+ present: T;
9
+ future: T[];
10
+ }
11
+
12
+ export class HistoryService<T> {
13
+ constructor(private readonly maxHistory: number = 20) {}
14
+
15
+ createInitialState(initialValue: T): HistoryState<T> {
16
+ return { past: [], present: initialValue, future: [] };
17
+ }
18
+
19
+ push(history: HistoryState<T>, newValue: T): HistoryState<T> {
20
+ return {
21
+ past: [...history.past.slice(-this.maxHistory + 1), history.present],
22
+ present: newValue,
23
+ future: [],
24
+ };
25
+ }
26
+
27
+ undo(history: HistoryState<T>): HistoryState<T> {
28
+ if (!this.canUndo(history)) return history;
29
+
30
+ const previous = history.past[history.past.length - 1];
31
+ return {
32
+ past: history.past.slice(0, -1),
33
+ present: previous,
34
+ future: [history.present, ...history.future],
35
+ };
36
+ }
37
+
38
+ redo(history: HistoryState<T>): HistoryState<T> {
39
+ if (!this.canRedo(history)) return history;
40
+
41
+ const next = history.future[0];
42
+ return {
43
+ past: [...history.past, history.present],
44
+ present: next,
45
+ future: history.future.slice(1),
46
+ };
47
+ }
48
+
49
+ canUndo(history: HistoryState<T>): boolean {
50
+ return history.past.length > 0;
51
+ }
52
+
53
+ canRedo(history: HistoryState<T>): boolean {
54
+ return history.future.length > 0;
55
+ }
56
+
57
+ clear(history: HistoryState<T>): HistoryState<T> {
58
+ return { past: [], present: history.present, future: [] };
59
+ }
60
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Layer Service
3
+ * Business logic for layer operations
4
+ */
5
+
6
+ import { Layer, TextLayer, StickerLayer, isTextLayer } from "../entities/Layer";
7
+ import type { Transform } from "../entities/Transform";
8
+
9
+ export class LayerService {
10
+ generateId(type: "text" | "sticker"): string {
11
+ return `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
12
+ }
13
+
14
+ createTextLayer(overrides: Partial<Omit<TextLayerData, "id" | "type">> = {}): TextLayer {
15
+ const id = this.generateId("text");
16
+ const defaults: TextLayerData = {
17
+ id,
18
+ type: "text",
19
+ text: "",
20
+ x: 50,
21
+ y: 50,
22
+ rotation: 0,
23
+ scale: 1,
24
+ opacity: 1,
25
+ zIndex: 0,
26
+ fontSize: 32,
27
+ fontFamily: "System",
28
+ color: "#FFFFFF",
29
+ backgroundColor: "transparent",
30
+ textAlign: "center",
31
+ isBold: false,
32
+ isItalic: false,
33
+ };
34
+
35
+ return new TextLayer({ ...defaults, ...overrides });
36
+ }
37
+
38
+ createStickerLayer(uri: string, overrides: Partial<Omit<StickerLayerData, "id" | "type" | "uri">> = {}): StickerLayer {
39
+ const id = this.generateId("sticker");
40
+ const defaults: StickerLayerData = {
41
+ id,
42
+ type: "sticker",
43
+ uri,
44
+ x: 100,
45
+ y: 100,
46
+ rotation: 0,
47
+ scale: 1,
48
+ opacity: 1,
49
+ zIndex: 0,
50
+ };
51
+
52
+ return new StickerLayer({ ...defaults, ...overrides });
53
+ }
54
+
55
+ updateLayer(layers: Layer[], layerId: string, updates: Partial<Transform>): Layer[] {
56
+ return layers.map(layer =>
57
+ layer.id === layerId ? layer.withTransform(updates) : layer
58
+ );
59
+ }
60
+
61
+ deleteLayer(layers: Layer[], layerId: string): Layer[] {
62
+ return layers.filter(layer => layer.id !== layerId);
63
+ }
64
+
65
+ duplicateLayer(layers: Layer[], layerId: string): Layer[] {
66
+ const layer = layers.find(l => l.id === layerId);
67
+ if (!layer) return layers;
68
+
69
+ const maxZIndex = layers.length > 0 ? Math.max(...layers.map(l => l.zIndex)) : -1;
70
+ const duplicateData = { ...layer.toJSON(), id: this.generateId(layer.type), x: layer.x + 20, y: layer.y + 20, zIndex: maxZIndex + 1 };
71
+ const duplicate = Layer.from(duplicateData);
72
+
73
+ return [...layers, duplicate];
74
+ }
75
+
76
+ moveLayerUp(layers: Layer[], layerId: string): Layer[] {
77
+ const sorted = this.sortByZIndex(layers);
78
+ const idx = sorted.findIndex(l => l.id === layerId);
79
+ if (idx >= sorted.length - 1) return layers;
80
+
81
+ const reordered = [...sorted];
82
+ [reordered[idx], reordered[idx + 1]] = [reordered[idx + 1], reordered[idx]];
83
+
84
+ return this.reassignZIndex(reordered);
85
+ }
86
+
87
+ moveLayerDown(layers: Layer[], layerId: string): Layer[] {
88
+ const sorted = this.sortByZIndex(layers);
89
+ const idx = sorted.findIndex(l => l.id === layerId);
90
+ if (idx <= 0) return layers;
91
+
92
+ const reordered = [...sorted];
93
+ [reordered[idx], reordered[idx - 1]] = [reordered[idx - 1], reordered[idx]];
94
+
95
+ return this.reassignZIndex(reordered);
96
+ }
97
+
98
+ sortByZIndex(layers: Layer[]): Layer[] {
99
+ return [...layers].sort((a, b) => a.zIndex - b.zIndex);
100
+ }
101
+
102
+ private reassignZIndex(layers: Layer[]): Layer[] {
103
+ return layers.map((layer, i) => layer.withZIndex(i));
104
+ }
105
+ }
package/src/index.ts CHANGED
@@ -1,9 +1,29 @@
1
- // Public API exports
1
+ /**
2
+ * @umituz/react-native-photo-editor
3
+ *
4
+ * A powerful, generic photo editor for React Native
5
+ * Built with DDD principles for maintainability
6
+ */
7
+
8
+ // Main component
2
9
  export { PhotoEditor } from "./PhotoEditor";
3
10
  export type { PhotoEditorProps } from "./PhotoEditor";
4
11
 
5
- // Type exports for consumer usage
6
- export * from "./types";
12
+ // Domain entities
13
+ export type { Layer, TextLayer, StickerLayer } from "./domain/entities/Layer";
14
+ export type { Transform } from "./domain/entities/Transform";
15
+ export type { FilterValues, FiltersVO } from "./domain/entities/Filters";
16
+ export { isTextLayer, isStickerLayer } from "./domain/entities/Layer";
17
+
18
+ // Application hooks
19
+ export { useEditor } from "./application/hooks/useEditor";
20
+ export { useEditorUI } from "./application/hooks/useEditorUI";
7
21
 
8
- // Constant exports for consumer customization
9
- export * from "./constants";
22
+ // Types & constants
23
+ export type { LayerTransform } from "./infrastructure/gesture/types";
24
+ export { DEFAULT_IMAGE_FILTERS } from "./domain/entities/Filters";
25
+ export { DEFAULT_FONTS, DEFAULT_TEXT_COLORS, DEFAULT_STICKERS, DEFAULT_AI_STYLES } from "./constants";
26
+ export type { FilterOption } from "./presentation/components/sheets/FilterSheet";
27
+
28
+ // Legacy types (for backward compatibility)
29
+ export * from "./types";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Gesture Types
3
+ * Shared types for transform gestures
4
+ */
5
+
6
+ import type { Transform } from "../../domain/entities/Transform";
7
+
8
+ export interface TransformGestureState {
9
+ position: { x: number; y: number };
10
+ scale: number;
11
+ rotation: number;
12
+ }
13
+
14
+ export interface TransformGestureConfig {
15
+ minScale?: number;
16
+ maxScale?: number;
17
+ onTransformEnd: (transform: Transform) => void;
18
+ onPress?: () => void;
19
+ }
20
+
21
+ export interface TransformGestureHandlers {
22
+ panGesture: ReturnType<typeof import("react-native-gesture-handler").Gesture.Pan>;
23
+ pinchGesture: ReturnType<typeof import("react-native-gesture-handler").Gesture.Pinch>;
24
+ rotationGesture: ReturnType<typeof import("react-native-gesture-handler").Gesture.Rotation>;
25
+ tapGesture: ReturnType<typeof import("react-native-gesture-handler").Gesture.Tap>;
26
+ composed: ReturnType<typeof import("react-native-gesture-handler").Gesture.Exclusive>;
27
+ }