UNPKG

expo-dynamic-image-crop

Version:

Dynamic image cropping component for Expo/React Native with free-form and fixed aspect ratio support

100 lines (99 loc) 4.32 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import * as ImageManipulator from "expo-image-manipulator"; import { useCallback, useEffect } from "react"; import { Modal, StatusBar, StyleSheet, View } from "react-native"; import { EditorContext } from "./context/editor"; import { ControlBar } from "./ControlBar"; import { EditingWindow } from "./EditingWindow"; import { Processing } from "./Processing"; import { useEditorStore } from "./store"; function ImageEditorCore(props) { const { minimumCropDimensions = { width: 100, height: 100 }, fixedAspectRatio = 0.66666666666, dynamicCrop = false, onEditingCancel, onEditingComplete, imageUri = null, processingComponent, editorOptions, } = props; const options = useEditorStore((s) => s.editorOptions); const setOptions = useEditorStore((s) => s.setEditorOptions); const imageData = useEditorStore((s) => s.imageData); const setImageData = useEditorStore((s) => s.setImageData); const setReady = useEditorStore((s) => s.setReady); const setEditingMode = useEditorStore((s) => s.setEditingMode); const setProcessing = useEditorStore((s) => s.setProcessing); const isEdit = useEditorStore((s) => s.isEdit); const resetEditorStore = useEditorStore((s) => s.resetEditorStore); const initialize = useCallback(async () => { setProcessing(true); if (imageUri) { const { width: pickerWidth, height: pickerHeight } = await ImageManipulator.manipulateAsync(imageUri, []); setImageData({ uri: imageUri, width: pickerWidth, height: pickerHeight, }); setReady(true); setProcessing(false); } }, [imageUri, setImageData, setProcessing, setReady]); const onBackPress = () => { if (!isEdit) { onEditingCancel(); } else { setProcessing(true); initialize().then(() => { setEditingMode("crop"); setProcessing(false); }); } }; const onSave = () => { onEditingComplete(imageData); resetEditorStore(); }; useEffect(() => { initialize().then(setCustomStyles).catch(console.error); // eslint-disable-next-line react-hooks/exhaustive-deps }, [imageUri]); function setCustomStyles() { if (editorOptions) { const custom = Object.assign({}, options); Object.entries(editorOptions).forEach(([key, value]) => { if (key) { // @ts-ignore if (typeof custom[key] === "object" && custom[key] !== null) { // @ts-ignore custom[key] = { ...custom[key], ...value }; } else { // @ts-ignore custom[key] = value; } } }); setOptions(custom); } } return (_jsxs(EditorContext.Provider, { value: { minimumCropDimensions, fixedAspectRatio, dynamicCrop, onBackPress, onSave, imageUri, }, children: [_jsx(StatusBar, { hidden: true }), _jsx(ImageEditorView, { processingComponent: processingComponent })] })); } export function ImageEditorView({ processingComponent }) { const ready = useEditorStore((s) => s.ready); const processing = useEditorStore((s) => s.processing); const { backgroundColor, controlBar } = useEditorStore((s) => s.editorOptions); return (_jsxs(_Fragment, { children: [ready && (_jsxs(View, { style: [styles.container, { backgroundColor }], children: [controlBar?.position === "top" && _jsx(ControlBar, {}), _jsx(EditingWindow, {}), controlBar?.position === "bottom" && _jsx(ControlBar, {})] })), processing && _jsx(Processing, { customComponent: processingComponent })] })); } export function ImageEditor({ isVisible, ...props }) { return (_jsx(Modal, { visible: isVisible, style: styles.modalContainer, children: _jsx(ImageEditorCore, { ...props }) })); } const styles = StyleSheet.create({ container: { flex: 1, }, modalContainer: { flex: 1, zIndex: 1, }, });