import { useRef, useImperativeHandle, forwardRef } from 'react' import { requireNativeViewManager, NativeModulesProxy } from 'expo-modules-core' import { ViewProps, StyleSheet, ViewStyle } from 'react-native' // MARK: - Types /** * Aspect ratio for crop */ export type CropAspectRatio = { label: string ratio: number | null // null = free form } /** * UI Configuration for the Image Editor * Provides extensive control over the editor's appearance and behavior */ export interface ImageEditorUIConfig { // ========== Feature Toggles ========== /** Enable adjustment tools (brightness, contrast, etc.) */ enableAdjustments?: boolean /** Enable filter gallery */ enableFilters?: boolean /** Enable transform tools (crop, rotate, flip) */ enableTransforms?: boolean /** Enable layer management */ enableLayers?: boolean /** Enable drawing tools */ enableDrawing?: boolean /** Enable text tool */ enableText?: boolean /** Enable stickers */ enableStickers?: boolean /** Enable LUT (color grading) support */ enableLUT?: boolean // ========== UI Toggles ========== /** Show top toolbar with controls */ showTopToolbar?: boolean /** Show bottom toolbar with tools */ showBottomToolbar?: boolean /** Enable undo button */ enableUndo?: boolean /** Enable redo button */ enableRedo?: boolean /** Enable reset button */ enableReset?: boolean /** Enable export button */ enableExport?: boolean /** Show close button */ showCloseButton?: boolean /** Show load new image button */ showLoadButton?: boolean /** Show share button */ showShareButton?: boolean /** Show info/help button */ showInfoButton?: boolean // ========== Export Options ========== /** Allowed export formats */ allowedExportFormats?: ('jpeg' | 'png' | 'heic')[] /** Default export format */ defaultExportFormat?: 'jpeg' | 'png' | 'heic' /** Default export quality (0.0 - 1.0) */ defaultExportQuality?: number /** Maximum dimension for export (width/height) */ maxExportDimension?: number /** Show export progress indicator */ showExportProgress?: boolean /** Export progress style */ exportProgressStyle?: 'bar' | 'circular' | 'minimal' /** Embed metadata in exported image */ embedMetadata?: boolean /** Preserve EXIF data */ preserveExif?: boolean /** Compression method */ compressionMethod?: 'fast' | 'balanced' | 'best' /** Automatically save to Photos */ exportToPhotos?: boolean /** Show share sheet after export */ exportShareSheet?: boolean // ========== Filter Configuration ========== /** Available filters (null = all) */ availableFilters?: string[] /** Default filter intensity (0.0 - 1.0) */ defaultFilterIntensity?: number /** Show intensity slider for filters */ showFilterIntensitySlider?: boolean /** Filter intensity range */ filterIntensityRange?: { min: number; max: number } /** Show filter preview thumbnails */ showFilterPreviews?: boolean /** Filter preview thumbnail size */ filterPreviewSize?: number /** Enable before/after comparison */ enableFilterComparison?: boolean /** Allow applying multiple filters */ allowMultipleFilters?: boolean // ========== Adjustment Configuration ========== /** Available adjustments (null = all) */ availableAdjustments?: string[] /** Custom adjustment ranges */ adjustmentRanges?: Record /** Slider step increments per adjustment */ adjustmentSteps?: Record /** Default values per adjustment */ adjustmentDefaults?: Record /** Show adjustment values */ showAdjustmentValues?: boolean /** Show reset button per adjustment */ showAdjustmentReset?: boolean /** Live preview while adjusting */ adjustmentLivePreview?: boolean // ========== Transform Configuration ========== /** Enable crop tool */ enableCrop?: boolean /** Enable rotate tool */ enableRotate?: boolean /** Enable flip tools */ enableFlip?: boolean /** Enable resize tool */ enableResize?: boolean /** Rotation step in degrees */ rotationStep?: number /** Crop aspect ratio presets */ cropAspectRatios?: CropAspectRatio[] /** Enable freeform crop */ enableFreeformCrop?: boolean /** Show crop grid overlay */ showCropGrid?: boolean /** Number of grid lines */ cropGridLines?: number /** Allow rotation beyond step increments */ allowRotationBeyondStep?: boolean /** Enable rotation gesture */ rotationGestureEnabled?: boolean // ========== Drawing Configuration ========== /** Default brush size */ defaultBrushSize?: number /** Default brush opacity (0.0 - 1.0) */ defaultBrushOpacity?: number /** Enable eraser tool */ enableEraser?: boolean /** Minimum brush size */ minBrushSize?: number /** Maximum brush size */ maxBrushSize?: number /** Brush size step increment */ brushSizeStep?: number /** Available brush colors (null = color picker) */ availableBrushColors?: string[] /** Enable brush opacity control */ enableBrushOpacity?: boolean /** Enable brush blending modes */ enableBrushBlending?: boolean /** Show brush preview */ showBrushPreview?: boolean /** Enable pressure sensitivity (Apple Pencil) */ enablePressureSensitivity?: boolean /** Drawing smoothing (0.0 - 1.0) */ drawingSmoothing?: number // ========== Layer Configuration ========== /** Maximum number of layers */ maxLayers?: number /** Default layer opacity */ defaultLayerOpacity?: number /** Available blend modes (null = all) */ availableBlendModes?: string[] // ========== Text Configuration ========== /** Default text size */ defaultTextSize?: number /** Available fonts (null = system fonts) */ availableFonts?: string[] /** Enable text shadow */ enableTextShadow?: boolean /** Enable text stroke */ enableTextStroke?: boolean // ========== Performance Configuration ========== /** Maximum undo/redo states */ maxHistoryStates?: number /** Enable automatic memory monitoring */ enableMemoryMonitoring?: boolean /** Maximum preview dimension (downsampling) */ maxPreviewDimension?: number /** Enable GPU acceleration */ enableGPUAcceleration?: boolean /** Enable auto-save */ autoSaveEnabled?: boolean /** Auto-save interval in seconds */ autoSaveInterval?: number /** Preload filters for faster application */ preloadFilters?: boolean /** Cache processed images */ cacheProcessedImages?: boolean /** Enable low memory mode (reduces quality) */ lowMemoryMode?: boolean // ========== UI Behavior ========== /** Enable haptic feedback */ enableHapticFeedback?: boolean /** Enable animations */ enableAnimations?: boolean /** Animation duration in seconds */ animationDuration?: number /** Animate toolbar transitions */ toolbarAnimated?: boolean /** Animate panel transitions */ panelAnimated?: boolean /** Enable double tap to zoom */ enableDoubleTapToZoom?: boolean /** Double tap zoom scale */ doubleTapZoomScale?: number /** Maximum zoom scale */ maxZoomScale?: number /** Minimum zoom scale */ minZoomScale?: number // ========== Gesture Configuration ========== /** Enable pinch to zoom */ enablePinchToZoom?: boolean /** Enable pan gesture */ enablePanGesture?: boolean /** Enable rotation gesture */ enableRotationGesture?: boolean /** Enable swipe gestures */ enableSwipeGestures?: boolean /** Swipe to undo/redo */ swipeToUndoEnabled?: boolean /** Long press for additional options */ longPressForOptions?: boolean // ========== Validation & Constraints ========== /** Minimum image dimension (width or height) */ minImageDimension?: number /** Maximum image dimension (width or height) */ maxImageDimension?: number /** Allowed image file formats */ allowedImageFormats?: string[] /** Maximum file size in bytes (null = no limit) */ maxFileSize?: number | null /** Warn user about large images */ warnOnLargeImages?: boolean /** Threshold for large image warning (pixels) */ largeImageThreshold?: number // ========== Toolbar Layout ========== /** Toolbar position */ toolbarPosition?: 'top' | 'bottom' | 'floating' /** Toolbar height */ toolbarHeight?: number /** Toolbar padding */ toolbarPadding?: number /** Toolbar item spacing */ toolbarSpacing?: number /** Bottom toolbar layout style */ bottomToolbarLayout?: 'scrollable' | 'grid' | 'grouped' /** Make toolbar collapsible */ toolbarCollapsible?: boolean // ========== Panel Configuration ========== /** Panel position */ panelPosition?: 'bottom' | 'side' | 'overlay' /** Panel height */ panelHeight?: number /** Allow panel dismissal */ panelDismissable?: boolean /** Show panel drag handle */ panelDragHandle?: boolean /** Apply blur effect to panel background */ panelBlurEffect?: boolean // ========== Localization ========== /** Localized strings map */ localizedStrings?: Record /** Date format string */ dateFormat?: string /** Use system language */ useSystemLanguage?: boolean } /** * Image source for loading */ export interface ImageSource { uri: string } /** * Export result */ export interface ExportResult { uri: string path: string } /** * Error event */ export interface ErrorEvent { error: string } /** * Props for ImageEditorUI component */ export interface ImageEditorUIProps extends ViewProps { /** * Image source to load */ source?: ImageSource /** * UI configuration */ config?: ImageEditorUIConfig /** * Called when the editor is ready */ onReady?: () => void /** * Called when an image is loaded */ onImageLoaded?: () => void /** * Called when the image is changed */ onImageChanged?: () => void /** * Called when an image is exported */ onExport?: (result: ExportResult) => void /** * Called when an error occurs */ onError?: (error: ErrorEvent) => void /** * Called when the editor is closed */ onClose?: () => void /** * Style for the view */ style?: ViewStyle } /** * Ref methods for imperative API */ export interface ImageEditorUIRef { /** * Load an image from a source */ loadImage: (source: ImageSource) => Promise /** * Update the configuration */ updateConfig: (config: ImageEditorUIConfig) => Promise /** * Get the current image */ getCurrentImage: () => Promise } // MARK: - Native View Manager const NativeView = requireNativeViewManager('ImageEditorUI') // MARK: - Component /** * Image Editor UI Component * * A complete native image editing interface with all tools built-in. * This component provides a full-featured UI that can be configured via props. * * @example * ```tsx * import { ImageEditorUI } from 'react-native-image-editor-sdk'; * * function MyEditor() { * const editorRef = useRef(null); * * return ( * { * console.log('Exported:', result.uri); * }} * onError={(error) => { * console.error('Error:', error.error); * }} * style={{ flex: 1 }} * /> * ); * } * ``` */ export const ImageEditorUI = forwardRef( ({ source, config, onReady, onImageLoaded, onImageChanged, onExport, onError, onClose, style, ...props }, ref) => { const nativeRef = useRef(null) // Expose imperative methods useImperativeHandle(ref, () => ({ loadImage: async (source: ImageSource) => { if (nativeRef.current) { await NativeModulesProxy.ImageEditorUI.loadImage(nativeRef.current, source) } }, updateConfig: async (config: ImageEditorUIConfig) => { if (nativeRef.current) { await NativeModulesProxy.ImageEditorUI.updateConfig(nativeRef.current, config) } }, getCurrentImage: async (): Promise => { if (nativeRef.current) { return await NativeModulesProxy.ImageEditorUI.getCurrentImage(nativeRef.current) } throw new Error('Editor not initialized') }, })) return ( ) } ) ImageEditorUI.displayName = 'ImageEditorUI' // MARK: - Preset Configurations /** * Preset configurations for common use cases */ export const ImageEditorUIPresets = { /** * Minimal configuration with only essential features */ minimal: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: false, enableDrawing: false, enableText: false, enableStickers: false, enableLUT: false, showFilterPreviews: false, enableAnimations: true, enableHapticFeedback: true, }), /** * Photo editor configuration */ photoEditor: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: true, enableDrawing: true, enableText: false, enableStickers: false, enableLUT: true, showFilterPreviews: true, showFilterIntensitySlider: true, adjustmentLivePreview: true, enableFilterComparison: true, showCropGrid: true, enableHapticFeedback: true, }), /** * Social media configuration with popular filters */ socialMedia: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: false, enableDrawing: false, enableText: true, enableStickers: true, enableLUT: false, availableFilters: ['vintage', 'dramatic', 'noir', 'fade', 'instant'], maxExportDimension: 2048, cropAspectRatios: [ { label: 'Square', ratio: 1.0 }, { label: 'Instagram', ratio: 4.0 / 5.0 }, { label: 'Story', ratio: 9.0 / 16.0 }, { label: 'Free', ratio: null }, ], showShareButton: true, exportShareSheet: true, enableHapticFeedback: true, enableAnimations: true, }), /** * Professional configuration with all features */ professional: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: true, enableDrawing: true, enableText: true, enableStickers: true, enableLUT: true, maxHistoryStates: 100, allowedExportFormats: ['jpeg', 'png', 'heic'], showFilterPreviews: true, showFilterIntensitySlider: true, adjustmentLivePreview: true, showAdjustmentValues: true, showAdjustmentReset: true, enableFilterComparison: true, showCropGrid: true, enableFreeformCrop: true, allowRotationBeyondStep: true, enablePressureSensitivity: true, showBrushPreview: true, enableBrushOpacity: true, enableBrushBlending: true, showExportProgress: true, preserveExif: true, embedMetadata: true, autoSaveEnabled: true, autoSaveInterval: 60, preloadFilters: true, cacheProcessedImages: true, enableHapticFeedback: true, enableAnimations: true, enableDoubleTapToZoom: true, longPressForOptions: true, }), /** * Drawing-focused configuration */ drawing: (): ImageEditorUIConfig => ({ enableAdjustments: false, enableFilters: false, enableTransforms: true, enableLayers: true, enableDrawing: true, enableText: true, enableStickers: false, enableLUT: false, minBrushSize: 1, maxBrushSize: 100, brushSizeStep: 0.5, defaultBrushSize: 10, showBrushPreview: true, enableBrushOpacity: true, enablePressureSensitivity: true, drawingSmoothing: 0.7, enableHapticFeedback: true, maxHistoryStates: 200, }), /** * Quick edit - fast and simple adjustments */ quickEdit: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: false, enableDrawing: false, enableText: false, enableStickers: false, availableAdjustments: ['brightness', 'contrast', 'saturation'], availableFilters: ['vintage', 'mono', 'warm', 'cool'], cropAspectRatios: [ { label: 'Free', ratio: null }, { label: 'Square', ratio: 1.0 }, ], showFilterPreviews: false, showAdjustmentReset: true, enableAnimations: true, animationDuration: 0.2, maxHistoryStates: 20, }), /** * Performance optimized for large images */ performanceOptimized: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: false, enableDrawing: false, enableText: false, enableStickers: false, maxPreviewDimension: 1536, maxHistoryStates: 30, enableGPUAcceleration: true, lowMemoryMode: false, preloadFilters: true, cacheProcessedImages: true, showFilterPreviews: false, adjustmentLivePreview: true, enableAnimations: false, compressionMethod: 'fast', warnOnLargeImages: true, largeImageThreshold: 4096, }), /** * High quality export focused */ highQuality: (): ImageEditorUIConfig => ({ enableAdjustments: true, enableFilters: true, enableTransforms: true, enableLayers: true, enableDrawing: true, enableText: true, allowedExportFormats: ['png', 'heic'], defaultExportFormat: 'heic', defaultExportQuality: 1.0, maxExportDimension: undefined, compressionMethod: 'best', preserveExif: true, embedMetadata: true, showExportProgress: true, exportProgressStyle: 'bar', maxHistoryStates: 100, enableGPUAcceleration: true, }), } // MARK: - Styles const styles = StyleSheet.create({ default: { flex: 1, }, }) // MARK: - Exports export default ImageEditorUI