|
1 | 1 | import * as imageCropper from "@zag-js/image-cropper" |
2 | 2 | import { normalizeProps, useMachine } from "@zag-js/react" |
3 | | -import { handlePositions } from "@zag-js/shared" |
4 | | -import { useEffect, useId, useState } from "react" |
| 3 | +import { useId, useState } from "react" |
5 | 4 |
|
6 | 5 | interface ImageCropperProps extends Omit<imageCropper.Props, "id"> {} |
7 | 6 |
|
8 | 7 | export function ImageCropper(props: ImageCropperProps) { |
| 8 | + const [previewUrl, setPreviewUrl] = useState<string | null>(null) |
| 9 | + |
9 | 10 | const service = useMachine(imageCropper.machine, { |
10 | 11 | id: useId(), |
| 12 | + initialCrop: { x: 120, y: 40, width: 120, height: 120 }, |
11 | 13 | ...props, |
12 | 14 | }) |
13 | 15 |
|
14 | 16 | const api = imageCropper.connect(service, normalizeProps) |
15 | 17 |
|
| 18 | + const handleShowPreview = async () => { |
| 19 | + const result = await api.getCroppedImage() |
| 20 | + let url: string | null = null |
| 21 | + if (result instanceof Blob) { |
| 22 | + // Revoke previous URL if it exists |
| 23 | + if (previewUrl) { |
| 24 | + URL.revokeObjectURL(previewUrl) |
| 25 | + } |
| 26 | + url = URL.createObjectURL(result) |
| 27 | + } else if (typeof result === "string") { |
| 28 | + url = result |
| 29 | + } |
| 30 | + setPreviewUrl(url) |
| 31 | + } |
| 32 | + |
| 33 | + const revokePreview = () => { |
| 34 | + // Revoke URL after image loads for performance |
| 35 | + if (previewUrl) { |
| 36 | + URL.revokeObjectURL(previewUrl) |
| 37 | + } |
| 38 | + } |
| 39 | + |
16 | 40 | return ( |
17 | | - <div {...api.getRootProps()}> |
18 | | - <div {...api.getViewportProps()}> |
19 | | - <img |
20 | | - src="https://placedog.net/500/280?id=2" |
21 | | - crossOrigin="anonymous" |
22 | | - {...api.getImageProps()} |
23 | | - /> |
24 | | - <div {...api.getSelectionProps()}> |
25 | | - {handlePositions.map((position) => ( |
26 | | - <div key={position} {...api.getHandleProps({ position })}> |
27 | | - <div /> |
28 | | - </div> |
29 | | - ))} |
| 41 | + <div className="image-cropper-container"> |
| 42 | + <div {...api.getRootProps()}> |
| 43 | + <div {...api.getViewportProps()}> |
| 44 | + <img |
| 45 | + src="https://placedog.net/500/280?id=2" |
| 46 | + crossOrigin="anonymous" |
| 47 | + {...api.getImageProps()} |
| 48 | + /> |
| 49 | + <div {...api.getSelectionProps()}> |
| 50 | + {imageCropper.handles.map((position) => ( |
| 51 | + <div key={position} {...api.getHandleProps({ position })}> |
| 52 | + <div /> |
| 53 | + </div> |
| 54 | + ))} |
| 55 | + </div> |
30 | 56 | </div> |
31 | 57 | </div> |
| 58 | + |
| 59 | + <button className="preview-button" onClick={handleShowPreview}> |
| 60 | + Show Preview |
| 61 | + </button> |
| 62 | + |
| 63 | + {previewUrl && ( |
| 64 | + <div> |
| 65 | + <h3>Cropped Image Preview</h3> |
| 66 | + <img |
| 67 | + className="preview-image" |
| 68 | + src={previewUrl} |
| 69 | + alt="Cropped preview" |
| 70 | + onLoad={revokePreview} |
| 71 | + /> |
| 72 | + </div> |
| 73 | + )} |
32 | 74 | </div> |
33 | 75 | ) |
34 | 76 | } |
0 commit comments