import type { CSSProperties } from 'react'; import React from 'react'; import { CircleStencil, Cropper, type CropperProps, type CropperRef, RectangleStencil } from 'react-advanced-cropper'; import 'react-advanced-cropper/dist/style.css'; import classNames from 'classnames'; type CropShape = 'rect' | 'round'; type StencilProps = Record; export interface ImageCropProps extends Omit< CropperProps, 'className' | 'onUpdate' | 'src' | 'stencilComponent' | 'stencilProps' > { /** * Source image URL to crop. */ image: string; /** * Aspect ratio for the rectangle stencil. */ aspect?: number; /** * Additional class name for the wrapper. */ className?: string; /** * Stencil shape. * @default 'rect' */ cropShape?: CropShape; /** * Height of the cropper wrapper. * @default 320 */ height?: number | string; /** * Maximum aspect ratio for the rectangle stencil. */ maxAspect?: number; /** * Minimum aspect ratio for the rectangle stencil. */ minAspect?: number; /** * Called whenever cropper state changes. */ onChange?: (cropper: CropperRef) => void; /** * Whether the stencil grid is visible. * @default true */ showGrid?: boolean; /** * Additional stencil props passed to the underlying stencil component. */ stencilProps?: StencilProps; /** * Width of the cropper wrapper. * @default '100%' */ width?: number | string; /** * Custom styles for the wrapper element. */ wrapperStyle?: CSSProperties; } function buildStencilProps({ aspect, cropShape, maxAspect, minAspect, showGrid, stencilProps, }: Pick) { const nextStencilProps: StencilProps = { ...stencilProps, grid: showGrid, }; if (cropShape === 'round') { return nextStencilProps; } if (aspect !== undefined) { nextStencilProps.aspectRatio = aspect; } if (minAspect !== undefined) { nextStencilProps.minAspectRatio = minAspect; } if (maxAspect !== undefined) { nextStencilProps.maxAspectRatio = maxAspect; } return nextStencilProps; } export const ImageCrop = React.forwardRef(function ImageCrop( { aspect, className, cropShape = 'rect', height = 320, image, maxAspect, minAspect, onChange, showGrid = true, stencilProps, width = '100%', wrapperStyle, ...cropperProps }, ref ) { const resolvedStencilProps = React.useMemo( () => buildStencilProps({ aspect, cropShape, maxAspect, minAspect, showGrid, stencilProps, }), [aspect, cropShape, maxAspect, minAspect, showGrid, stencilProps] ); return (
{ onChange?.(cropper); }} stencilComponent={cropShape === 'round' ? CircleStencil : RectangleStencil} stencilProps={resolvedStencilProps} />
); });