@sentroy-co/client-sdk 2.13.0 → 2.13.1
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CropDialog.d.ts","sourceRoot":"","sources":["../../../src/react/crop/CropDialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CropDialog.d.ts","sourceRoot":"","sources":["../../../src/react/crop/CropDialog.tsx"],"names":[],"mappings":"AA8CA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb;mBACe;IACf,IAAI,EAAE,IAAI,CAAA;IACV,4EAA4E;IAC5E,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAA;IACtC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;yDACqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,aAAsB,EACtB,aAAoB,GACrB,EAAE,eAAe,2CAiYjB"}
|
|
@@ -17,14 +17,19 @@ const ASPECT_PRESETS = [
|
|
|
17
17
|
{ id: "9:16", label: "9:16", aspect: 9 / 16 },
|
|
18
18
|
];
|
|
19
19
|
const MAX_PIXEL_GUARD = 50_000_000; // ~24 MP — üstü tarayıcı memory peak'i riskli
|
|
20
|
+
const PREVIEW_MAX_DIM = 220; // sidebar thumbnail için max edge (px)
|
|
20
21
|
function CropDialog({ open, file, onClose, defaultAspect = "free", outputQuality = 0.92, }) {
|
|
21
22
|
const [imageUrl, setImageUrl] = (0, react_1.useState)(null);
|
|
22
23
|
const [aspectId, setAspectId] = (0, react_1.useState)(defaultAspect);
|
|
23
24
|
const [crop, setCrop] = (0, react_1.useState)({ x: 0, y: 0 });
|
|
24
25
|
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
26
|
+
const [rotation, setRotation] = (0, react_1.useState)(0);
|
|
25
27
|
const [croppedAreaPixels, setCroppedAreaPixels] = (0, react_1.useState)(null);
|
|
26
28
|
const [busy, setBusy] = (0, react_1.useState)(false);
|
|
27
29
|
const [tooLarge, setTooLarge] = (0, react_1.useState)(false);
|
|
30
|
+
const previewCanvasRef = (0, react_1.useRef)(null);
|
|
31
|
+
const previewRafRef = (0, react_1.useRef)(null);
|
|
32
|
+
const imageElRef = (0, react_1.useRef)(null);
|
|
28
33
|
// Object URL lifecycle
|
|
29
34
|
(0, react_1.useEffect)(() => {
|
|
30
35
|
if (!open)
|
|
@@ -33,29 +38,71 @@ function CropDialog({ open, file, onClose, defaultAspect = "free", outputQuality
|
|
|
33
38
|
setImageUrl(url);
|
|
34
39
|
setCrop({ x: 0, y: 0 });
|
|
35
40
|
setZoom(1);
|
|
41
|
+
setRotation(0);
|
|
36
42
|
setAspectId(defaultAspect);
|
|
37
43
|
setTooLarge(false);
|
|
38
|
-
// Pixel guard
|
|
44
|
+
// Pixel guard + cache HTMLImageElement (preview drawImage source).
|
|
39
45
|
const img = new Image();
|
|
40
46
|
img.onload = () => {
|
|
41
47
|
if (img.naturalWidth * img.naturalHeight > MAX_PIXEL_GUARD) {
|
|
42
48
|
setTooLarge(true);
|
|
43
49
|
}
|
|
50
|
+
imageElRef.current = img;
|
|
44
51
|
};
|
|
45
52
|
img.src = url;
|
|
46
|
-
return () =>
|
|
53
|
+
return () => {
|
|
54
|
+
URL.revokeObjectURL(url);
|
|
55
|
+
imageElRef.current = null;
|
|
56
|
+
};
|
|
47
57
|
}, [open, file, defaultAspect]);
|
|
48
58
|
const onCropComplete = (0, react_1.useCallback)((_area, areaPixels) => {
|
|
49
59
|
setCroppedAreaPixels(areaPixels);
|
|
50
60
|
}, []);
|
|
61
|
+
// Live preview render — crop / rotation değiştikçe sağ panel thumbnail'ı
|
|
62
|
+
// güncelle. requestAnimationFrame ile throttle (drag sırasında her event'te
|
|
63
|
+
// canvas çizmek pahalı).
|
|
64
|
+
(0, react_1.useEffect)(() => {
|
|
65
|
+
if (!croppedAreaPixels || !imageElRef.current || tooLarge)
|
|
66
|
+
return;
|
|
67
|
+
if (previewRafRef.current !== null) {
|
|
68
|
+
cancelAnimationFrame(previewRafRef.current);
|
|
69
|
+
}
|
|
70
|
+
previewRafRef.current = requestAnimationFrame(() => {
|
|
71
|
+
const canvas = previewCanvasRef.current;
|
|
72
|
+
const img = imageElRef.current;
|
|
73
|
+
if (!canvas || !img)
|
|
74
|
+
return;
|
|
75
|
+
const area = croppedAreaPixels;
|
|
76
|
+
// Preview boyutu — aspect korunarak max edge PREVIEW_MAX_DIM
|
|
77
|
+
const scale = area.width >= area.height
|
|
78
|
+
? PREVIEW_MAX_DIM / area.width
|
|
79
|
+
: PREVIEW_MAX_DIM / area.height;
|
|
80
|
+
const pw = Math.max(1, Math.round(area.width * scale));
|
|
81
|
+
const ph = Math.max(1, Math.round(area.height * scale));
|
|
82
|
+
canvas.width = pw;
|
|
83
|
+
canvas.height = ph;
|
|
84
|
+
const ctx = canvas.getContext("2d");
|
|
85
|
+
if (!ctx)
|
|
86
|
+
return;
|
|
87
|
+
ctx.imageSmoothingQuality = "high";
|
|
88
|
+
ctx.clearRect(0, 0, pw, ph);
|
|
89
|
+
drawRotatedCrop(ctx, img, area, rotation, pw, ph);
|
|
90
|
+
previewRafRef.current = null;
|
|
91
|
+
});
|
|
92
|
+
return () => {
|
|
93
|
+
if (previewRafRef.current !== null) {
|
|
94
|
+
cancelAnimationFrame(previewRafRef.current);
|
|
95
|
+
previewRafRef.current = null;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}, [croppedAreaPixels, rotation, tooLarge]);
|
|
51
99
|
const aspect = ASPECT_PRESETS.find((p) => p.id === aspectId)?.aspect ?? undefined;
|
|
52
100
|
const handleApply = (0, react_1.useCallback)(async () => {
|
|
53
101
|
if (!imageUrl || !croppedAreaPixels)
|
|
54
102
|
return;
|
|
55
103
|
setBusy(true);
|
|
56
104
|
try {
|
|
57
|
-
const blob = await getCroppedBlob(imageUrl, croppedAreaPixels, file.type, outputQuality);
|
|
58
|
-
// Cropped File — orijinal name'i koru ama uzantı output type'ına göre
|
|
105
|
+
const blob = await getCroppedBlob(imageUrl, croppedAreaPixels, rotation, file.type, outputQuality);
|
|
59
106
|
const ext = blob.type === "image/png" ? "png" : "jpg";
|
|
60
107
|
const baseName = file.name.replace(/\.[^.]+$/, "");
|
|
61
108
|
const cropped = new File([blob], `${baseName}.${ext}`, {
|
|
@@ -66,10 +113,16 @@ function CropDialog({ open, file, onClose, defaultAspect = "free", outputQuality
|
|
|
66
113
|
finally {
|
|
67
114
|
setBusy(false);
|
|
68
115
|
}
|
|
69
|
-
}, [imageUrl, croppedAreaPixels, file, onClose, outputQuality]);
|
|
116
|
+
}, [imageUrl, croppedAreaPixels, rotation, file, onClose, outputQuality]);
|
|
70
117
|
const handleUseOriginal = (0, react_1.useCallback)(() => onClose(file), [file, onClose]);
|
|
71
118
|
const handleCancel = (0, react_1.useCallback)(() => onClose(null), [onClose]);
|
|
72
|
-
|
|
119
|
+
const handleRotate = (0, react_1.useCallback)((delta) => {
|
|
120
|
+
setRotation((r) => {
|
|
121
|
+
const next = (r + delta + 360) % 360;
|
|
122
|
+
return next;
|
|
123
|
+
});
|
|
124
|
+
}, []);
|
|
125
|
+
// ESC kapatır, R döndürür
|
|
73
126
|
(0, react_1.useEffect)(() => {
|
|
74
127
|
if (!open)
|
|
75
128
|
return;
|
|
@@ -78,27 +131,76 @@ function CropDialog({ open, file, onClose, defaultAspect = "free", outputQuality
|
|
|
78
131
|
e.stopPropagation();
|
|
79
132
|
handleCancel();
|
|
80
133
|
}
|
|
134
|
+
else if (e.key === "r" || e.key === "R") {
|
|
135
|
+
if (e.target instanceof HTMLElement &&
|
|
136
|
+
(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
handleRotate(e.shiftKey ? -90 : 90);
|
|
141
|
+
}
|
|
81
142
|
};
|
|
82
143
|
window.addEventListener("keydown", onKey);
|
|
83
144
|
return () => window.removeEventListener("keydown", onKey);
|
|
84
|
-
}, [open, handleCancel]);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
}, [open, handleCancel, handleRotate]);
|
|
146
|
+
const outputWidth = croppedAreaPixels ? Math.round(croppedAreaPixels.width) : 0;
|
|
147
|
+
const outputHeight = croppedAreaPixels ? Math.round(croppedAreaPixels.height) : 0;
|
|
148
|
+
// Rotation 90° / 270° iken output dimensions swap edilir (canvas rotate
|
|
149
|
+
// sonrası kullanıcı görsel olarak swap görür).
|
|
150
|
+
const displayW = rotation % 180 === 0 ? outputWidth : outputHeight;
|
|
151
|
+
const displayH = rotation % 180 === 0 ? outputHeight : outputWidth;
|
|
152
|
+
return ((0, jsx_runtime_1.jsx)(react_2.AnimatePresence, { children: open && imageUrl && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/80 backdrop-blur-sm p-4", onClick: (e) => {
|
|
88
153
|
if (e.target === e.currentTarget)
|
|
89
154
|
handleCancel();
|
|
90
|
-
}, children: (0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, scale: 0.96, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.98 }, transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] }, className: "flex h-[min(
|
|
91
|
-
|
|
92
|
-
|
|
155
|
+
}, children: (0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, scale: 0.96, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.98 }, transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] }, className: "flex h-[min(92vh,760px)] w-full max-w-5xl flex-col overflow-hidden rounded-xl border bg-background shadow-2xl", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 border-b px-4 py-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col min-w-0", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm font-semibold", children: "Crop image" }), (0, jsx_runtime_1.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: file.name })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleCancel, className: "rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground", "aria-label": "Cancel", children: (0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "size-4", children: (0, jsx_runtime_1.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center gap-1 border-b bg-muted/20 px-3 py-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex flex-1 flex-wrap items-center gap-1", children: ASPECT_PRESETS.map((p) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setAspectId(p.id), className: cls("rounded-md px-2.5 py-1 text-xs transition-colors", aspectId === p.id
|
|
156
|
+
? "bg-foreground text-background"
|
|
157
|
+
: "text-muted-foreground hover:bg-muted hover:text-foreground"), children: p.label }, p.id))) }), (0, jsx_runtime_1.jsxs)("div", { className: "ms-auto flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => handleRotate(-90), title: "Rotate left (Shift+R)", "aria-label": "Rotate left", className: "rounded-md border px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 hover:text-foreground", children: (0, jsx_runtime_1.jsx)(RotateLeftIcon, {}) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => handleRotate(90), title: "Rotate right (R)", "aria-label": "Rotate right", className: "rounded-md border px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 hover:text-foreground", children: (0, jsx_runtime_1.jsx)(RotateRightIcon, {}) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-1 min-h-0 flex-col overflow-hidden md:flex-row", children: [(0, jsx_runtime_1.jsx)("div", { className: "relative flex-1 bg-black", children: tooLarge ? ((0, jsx_runtime_1.jsx)("div", { className: "flex h-full w-full items-center justify-center p-6 text-center text-sm text-white/70", children: "Image too large to crop in browser. Upload as-is or resize beforehand." })) : ((0, jsx_runtime_1.jsx)(react_easy_crop_1.default, { image: imageUrl, crop: crop, zoom: zoom, rotation: rotation, aspect: aspect, onCropChange: setCrop, onCropComplete: onCropComplete, onZoomChange: setZoom, onRotationChange: setRotation, showGrid: true, objectFit: "contain" })) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full shrink-0 flex-col gap-3 border-t bg-muted/10 p-4 md:w-64 md:border-l md:border-t-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: "Preview" }), displayW > 0 && displayH > 0 && ((0, jsx_runtime_1.jsxs)("span", { className: "font-mono text-[10px] text-muted-foreground", children: [displayW, "\u00D7", displayH] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "flex min-h-[140px] items-center justify-center rounded-lg border border-dashed bg-background/40 p-2", children: tooLarge ? ((0, jsx_runtime_1.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Preview unavailable" })) : croppedAreaPixels ? ((0, jsx_runtime_1.jsx)("canvas", { ref: previewCanvasRef, className: "max-h-[220px] max-w-full rounded-sm shadow-sm" })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Adjust crop\u2026" })) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1 text-[11px] text-muted-foreground", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { children: "Aspect" }), (0, jsx_runtime_1.jsx)("span", { className: "font-mono", children: ASPECT_PRESETS.find((p) => p.id === aspectId)?.label ??
|
|
158
|
+
"Free" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { children: "Rotation" }), (0, jsx_runtime_1.jsxs)("span", { className: "font-mono", children: [rotation, "\u00B0"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { children: "Zoom" }), (0, jsx_runtime_1.jsxs)("span", { className: "font-mono", children: [zoom.toFixed(2), "\u00D7"] })] })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-3 border-t bg-muted/20 px-4 py-3", children: [!tooLarge && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-muted-foreground", children: "Zoom" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setZoom((z) => Math.max(1, z - 0.1)), className: "rounded-md border px-2 py-0.5 text-xs hover:bg-muted/50", children: "\u2212" }), (0, jsx_runtime_1.jsx)("input", { type: "range", min: 1, max: 3, step: 0.05, value: zoom, onChange: (e) => setZoom(Number(e.target.value)), className: "flex-1 accent-foreground" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setZoom((z) => Math.min(3, z + 0.1)), className: "rounded-md border px-2 py-0.5 text-xs hover:bg-muted/50", children: "+" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => {
|
|
93
159
|
setZoom(1);
|
|
160
|
+
setRotation(0);
|
|
94
161
|
setCrop({ x: 0, y: 0 });
|
|
95
162
|
}, className: "rounded-md border px-2 py-0.5 text-xs hover:bg-muted/50", children: "Reset" })] })), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-2", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleCancel, disabled: busy, className: "rounded-md border px-3 py-1.5 text-xs hover:bg-muted/50", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleUseOriginal, disabled: busy, className: "rounded-md border px-3 py-1.5 text-xs hover:bg-muted/50", children: "Use original" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleApply, disabled: busy || tooLarge || !croppedAreaPixels, className: "rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background hover:opacity-90 disabled:opacity-50", children: busy ? "Cropping…" : "Apply crop" })] })] })] }) }, "backdrop")) }));
|
|
96
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Rotated crop'u canvas'a yaz. Rotation 0° fast-path; aksi halde önce
|
|
166
|
+
* "rotated bounding box" canvas'ı oluştur (orijinal image'in döndürülmüş
|
|
167
|
+
* versiyonu), sonra croppedAreaPixels koordinatlarında çıkar.
|
|
168
|
+
*
|
|
169
|
+
* react-easy-crop's `croppedAreaPixels` rotation-aware: koordinatlar
|
|
170
|
+
* orijinal image'i rotasyon merkezinden döndürdükten sonraki "visual"
|
|
171
|
+
* bounding box üzerinden hesaplanır — biz aynı transform'u canvas'a
|
|
172
|
+
* uygulayıp aynı koordinatlardan drawImage yapıyoruz.
|
|
173
|
+
*/
|
|
174
|
+
function drawRotatedCrop(ctx, image, area, rotation, dstW, dstH) {
|
|
175
|
+
if (rotation === 0) {
|
|
176
|
+
ctx.drawImage(image, area.x, area.y, area.width, area.height, 0, 0, dstW, dstH);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Build a rotated source canvas at the size of the rotated bounding box,
|
|
180
|
+
// then crop from it.
|
|
181
|
+
const rad = (rotation * Math.PI) / 180;
|
|
182
|
+
const sin = Math.abs(Math.sin(rad));
|
|
183
|
+
const cos = Math.abs(Math.cos(rad));
|
|
184
|
+
const iw = image.naturalWidth;
|
|
185
|
+
const ih = image.naturalHeight;
|
|
186
|
+
const bbW = iw * cos + ih * sin;
|
|
187
|
+
const bbH = iw * sin + ih * cos;
|
|
188
|
+
const tmp = document.createElement("canvas");
|
|
189
|
+
tmp.width = bbW;
|
|
190
|
+
tmp.height = bbH;
|
|
191
|
+
const tctx = tmp.getContext("2d");
|
|
192
|
+
if (!tctx)
|
|
193
|
+
return;
|
|
194
|
+
tctx.translate(bbW / 2, bbH / 2);
|
|
195
|
+
tctx.rotate(rad);
|
|
196
|
+
tctx.drawImage(image, -iw / 2, -ih / 2);
|
|
197
|
+
ctx.drawImage(tmp, area.x, area.y, area.width, area.height, 0, 0, dstW, dstH);
|
|
198
|
+
}
|
|
97
199
|
/**
|
|
98
200
|
* Canvas ile crop area'yı çıkar + Blob döndür.
|
|
99
201
|
* Output MIME: PNG ise PNG, diğerleri JPEG (transparency yoksa).
|
|
100
202
|
*/
|
|
101
|
-
async function getCroppedBlob(imageUrl, area, sourceMime, quality) {
|
|
203
|
+
async function getCroppedBlob(imageUrl, area, rotation, sourceMime, quality) {
|
|
102
204
|
const image = await loadImage(imageUrl);
|
|
103
205
|
const canvas = document.createElement("canvas");
|
|
104
206
|
canvas.width = area.width;
|
|
@@ -106,7 +208,8 @@ async function getCroppedBlob(imageUrl, area, sourceMime, quality) {
|
|
|
106
208
|
const ctx = canvas.getContext("2d");
|
|
107
209
|
if (!ctx)
|
|
108
210
|
throw new Error("Canvas 2D context unavailable");
|
|
109
|
-
ctx.
|
|
211
|
+
ctx.imageSmoothingQuality = "high";
|
|
212
|
+
drawRotatedCrop(ctx, image, area, rotation, area.width, area.height);
|
|
110
213
|
const outputMime = sourceMime === "image/png" ? "image/png" : "image/jpeg";
|
|
111
214
|
return new Promise((resolve, reject) => {
|
|
112
215
|
canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error("toBlob returned null"))), outputMime, outputMime === "image/jpeg" ? quality : undefined);
|
|
@@ -123,4 +226,10 @@ function loadImage(url) {
|
|
|
123
226
|
function cls(...arr) {
|
|
124
227
|
return arr.filter(Boolean).join(" ");
|
|
125
228
|
}
|
|
229
|
+
function RotateLeftIcon() {
|
|
230
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "size-3.5", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("path", { d: "M3 12a9 9 0 1 0 3-6.7" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 4v5h5" })] }));
|
|
231
|
+
}
|
|
232
|
+
function RotateRightIcon() {
|
|
233
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "size-3.5", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("path", { d: "M21 12a9 9 0 1 1-3-6.7" }), (0, jsx_runtime_1.jsx)("path", { d: "M21 4v5h-5" })] }));
|
|
234
|
+
}
|
|
126
235
|
//# sourceMappingURL=CropDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CropDialog.js","sourceRoot":"","sources":["../../../src/react/crop/CropDialog.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CropDialog.js","sourceRoot":"","sources":["../../../src/react/crop/CropDialog.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react"
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react"
|
|
2
2
|
import Cropper from "react-easy-crop"
|
|
3
3
|
import { motion, AnimatePresence } from "motion/react"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Image crop dialog — `react-easy-crop` üzerine
|
|
7
|
-
* upload akışında `preprocessFile` hook'unun içinde çağrılır:
|
|
6
|
+
* Image crop dialog — `react-easy-crop` üzerine professional crop UI:
|
|
8
7
|
* - Aspect preset toolbar (1:1, 4:3, 16:9, 3:2, 9:16, Free)
|
|
9
8
|
* - Zoom slider (+/− ile de)
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
9
|
+
* - Rotate 90° CW/CCW (`R` shortcut)
|
|
10
|
+
* - Sağ panelde **live preview thumbnail** — crop alanı her değiştiğinde
|
|
11
|
+
* küçük canvas'a aynı transformasyonu uygulayıp output sonucunu gösterir;
|
|
12
|
+
* kullanıcı Apply'a basmadan "ne çıkacak" göründüğü için
|
|
13
|
+
* Photoshop / Figma seviyesinde feedback.
|
|
14
|
+
* - Output pixel boyutu (örn. 1200×800) — RP'lerin export presetlerine
|
|
15
|
+
* hizalanmak için.
|
|
16
|
+
* - Apply → cropped Blob, Cancel → null, "Use original" → original File.
|
|
12
17
|
*
|
|
13
18
|
* Ayrı bir entry point (`@sentroy-co/client-sdk/react/crop`) — ana SDK
|
|
14
19
|
* import'u `react-easy-crop`'u bundle'a çekmesin (lazy subpath).
|
|
15
20
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
21
|
+
* `getCroppedBlob` rotation-aware: önce kaynak image rotate edilir,
|
|
22
|
+
* sonra `croppedAreaPixels`'in döndürülmüş koordinat sisteminden çıkarılır.
|
|
23
|
+
* react-easy-crop'un `croppedAreaPixels` çıktısı zaten rotation'a göre
|
|
24
|
+
* transform edilmiş — biz canvas'a aynı rotation'ı uygulayıp aynı koordinat
|
|
25
|
+
* sisteminde drawImage yapıyoruz.
|
|
19
26
|
*/
|
|
20
27
|
|
|
21
28
|
interface CropArea {
|
|
@@ -35,6 +42,7 @@ const ASPECT_PRESETS: Array<{ id: string; label: string; aspect: number | null }
|
|
|
35
42
|
]
|
|
36
43
|
|
|
37
44
|
const MAX_PIXEL_GUARD = 50_000_000 // ~24 MP — üstü tarayıcı memory peak'i riskli
|
|
45
|
+
const PREVIEW_MAX_DIM = 220 // sidebar thumbnail için max edge (px)
|
|
38
46
|
|
|
39
47
|
export interface CropDialogProps {
|
|
40
48
|
open: boolean
|
|
@@ -61,9 +69,13 @@ export function CropDialog({
|
|
|
61
69
|
const [aspectId, setAspectId] = useState(defaultAspect)
|
|
62
70
|
const [crop, setCrop] = useState({ x: 0, y: 0 })
|
|
63
71
|
const [zoom, setZoom] = useState(1)
|
|
72
|
+
const [rotation, setRotation] = useState(0)
|
|
64
73
|
const [croppedAreaPixels, setCroppedAreaPixels] = useState<CropArea | null>(null)
|
|
65
74
|
const [busy, setBusy] = useState(false)
|
|
66
75
|
const [tooLarge, setTooLarge] = useState(false)
|
|
76
|
+
const previewCanvasRef = useRef<HTMLCanvasElement | null>(null)
|
|
77
|
+
const previewRafRef = useRef<number | null>(null)
|
|
78
|
+
const imageElRef = useRef<HTMLImageElement | null>(null)
|
|
67
79
|
|
|
68
80
|
// Object URL lifecycle
|
|
69
81
|
useEffect(() => {
|
|
@@ -72,17 +84,22 @@ export function CropDialog({
|
|
|
72
84
|
setImageUrl(url)
|
|
73
85
|
setCrop({ x: 0, y: 0 })
|
|
74
86
|
setZoom(1)
|
|
87
|
+
setRotation(0)
|
|
75
88
|
setAspectId(defaultAspect)
|
|
76
89
|
setTooLarge(false)
|
|
77
|
-
// Pixel guard
|
|
90
|
+
// Pixel guard + cache HTMLImageElement (preview drawImage source).
|
|
78
91
|
const img = new Image()
|
|
79
92
|
img.onload = () => {
|
|
80
93
|
if (img.naturalWidth * img.naturalHeight > MAX_PIXEL_GUARD) {
|
|
81
94
|
setTooLarge(true)
|
|
82
95
|
}
|
|
96
|
+
imageElRef.current = img
|
|
83
97
|
}
|
|
84
98
|
img.src = url
|
|
85
|
-
return () =>
|
|
99
|
+
return () => {
|
|
100
|
+
URL.revokeObjectURL(url)
|
|
101
|
+
imageElRef.current = null
|
|
102
|
+
}
|
|
86
103
|
}, [open, file, defaultAspect])
|
|
87
104
|
|
|
88
105
|
const onCropComplete = useCallback(
|
|
@@ -92,6 +109,43 @@ export function CropDialog({
|
|
|
92
109
|
[],
|
|
93
110
|
)
|
|
94
111
|
|
|
112
|
+
// Live preview render — crop / rotation değiştikçe sağ panel thumbnail'ı
|
|
113
|
+
// güncelle. requestAnimationFrame ile throttle (drag sırasında her event'te
|
|
114
|
+
// canvas çizmek pahalı).
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!croppedAreaPixels || !imageElRef.current || tooLarge) return
|
|
117
|
+
if (previewRafRef.current !== null) {
|
|
118
|
+
cancelAnimationFrame(previewRafRef.current)
|
|
119
|
+
}
|
|
120
|
+
previewRafRef.current = requestAnimationFrame(() => {
|
|
121
|
+
const canvas = previewCanvasRef.current
|
|
122
|
+
const img = imageElRef.current
|
|
123
|
+
if (!canvas || !img) return
|
|
124
|
+
const area = croppedAreaPixels
|
|
125
|
+
// Preview boyutu — aspect korunarak max edge PREVIEW_MAX_DIM
|
|
126
|
+
const scale =
|
|
127
|
+
area.width >= area.height
|
|
128
|
+
? PREVIEW_MAX_DIM / area.width
|
|
129
|
+
: PREVIEW_MAX_DIM / area.height
|
|
130
|
+
const pw = Math.max(1, Math.round(area.width * scale))
|
|
131
|
+
const ph = Math.max(1, Math.round(area.height * scale))
|
|
132
|
+
canvas.width = pw
|
|
133
|
+
canvas.height = ph
|
|
134
|
+
const ctx = canvas.getContext("2d")
|
|
135
|
+
if (!ctx) return
|
|
136
|
+
ctx.imageSmoothingQuality = "high"
|
|
137
|
+
ctx.clearRect(0, 0, pw, ph)
|
|
138
|
+
drawRotatedCrop(ctx, img, area, rotation, pw, ph)
|
|
139
|
+
previewRafRef.current = null
|
|
140
|
+
})
|
|
141
|
+
return () => {
|
|
142
|
+
if (previewRafRef.current !== null) {
|
|
143
|
+
cancelAnimationFrame(previewRafRef.current)
|
|
144
|
+
previewRafRef.current = null
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}, [croppedAreaPixels, rotation, tooLarge])
|
|
148
|
+
|
|
95
149
|
const aspect =
|
|
96
150
|
ASPECT_PRESETS.find((p) => p.id === aspectId)?.aspect ?? undefined
|
|
97
151
|
|
|
@@ -99,8 +153,13 @@ export function CropDialog({
|
|
|
99
153
|
if (!imageUrl || !croppedAreaPixels) return
|
|
100
154
|
setBusy(true)
|
|
101
155
|
try {
|
|
102
|
-
const blob = await getCroppedBlob(
|
|
103
|
-
|
|
156
|
+
const blob = await getCroppedBlob(
|
|
157
|
+
imageUrl,
|
|
158
|
+
croppedAreaPixels,
|
|
159
|
+
rotation,
|
|
160
|
+
file.type,
|
|
161
|
+
outputQuality,
|
|
162
|
+
)
|
|
104
163
|
const ext = blob.type === "image/png" ? "png" : "jpg"
|
|
105
164
|
const baseName = file.name.replace(/\.[^.]+$/, "")
|
|
106
165
|
const cropped = new File([blob], `${baseName}.${ext}`, {
|
|
@@ -110,23 +169,48 @@ export function CropDialog({
|
|
|
110
169
|
} finally {
|
|
111
170
|
setBusy(false)
|
|
112
171
|
}
|
|
113
|
-
}, [imageUrl, croppedAreaPixels, file, onClose, outputQuality])
|
|
172
|
+
}, [imageUrl, croppedAreaPixels, rotation, file, onClose, outputQuality])
|
|
114
173
|
|
|
115
174
|
const handleUseOriginal = useCallback(() => onClose(file), [file, onClose])
|
|
116
175
|
const handleCancel = useCallback(() => onClose(null), [onClose])
|
|
176
|
+
const handleRotate = useCallback(
|
|
177
|
+
(delta: 90 | -90) => {
|
|
178
|
+
setRotation((r) => {
|
|
179
|
+
const next = (r + delta + 360) % 360
|
|
180
|
+
return next
|
|
181
|
+
})
|
|
182
|
+
},
|
|
183
|
+
[],
|
|
184
|
+
)
|
|
117
185
|
|
|
118
|
-
// ESC kapatır
|
|
186
|
+
// ESC kapatır, R döndürür
|
|
119
187
|
useEffect(() => {
|
|
120
188
|
if (!open) return
|
|
121
189
|
const onKey = (e: KeyboardEvent) => {
|
|
122
190
|
if (e.key === "Escape") {
|
|
123
191
|
e.stopPropagation()
|
|
124
192
|
handleCancel()
|
|
193
|
+
} else if (e.key === "r" || e.key === "R") {
|
|
194
|
+
if (
|
|
195
|
+
e.target instanceof HTMLElement &&
|
|
196
|
+
(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")
|
|
197
|
+
) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
e.preventDefault()
|
|
201
|
+
handleRotate(e.shiftKey ? -90 : 90)
|
|
125
202
|
}
|
|
126
203
|
}
|
|
127
204
|
window.addEventListener("keydown", onKey)
|
|
128
205
|
return () => window.removeEventListener("keydown", onKey)
|
|
129
|
-
}, [open, handleCancel])
|
|
206
|
+
}, [open, handleCancel, handleRotate])
|
|
207
|
+
|
|
208
|
+
const outputWidth = croppedAreaPixels ? Math.round(croppedAreaPixels.width) : 0
|
|
209
|
+
const outputHeight = croppedAreaPixels ? Math.round(croppedAreaPixels.height) : 0
|
|
210
|
+
// Rotation 90° / 270° iken output dimensions swap edilir (canvas rotate
|
|
211
|
+
// sonrası kullanıcı görsel olarak swap görür).
|
|
212
|
+
const displayW = rotation % 180 === 0 ? outputWidth : outputHeight
|
|
213
|
+
const displayH = rotation % 180 === 0 ? outputHeight : outputWidth
|
|
130
214
|
|
|
131
215
|
return (
|
|
132
216
|
<AnimatePresence>
|
|
@@ -137,7 +221,6 @@ export function CropDialog({
|
|
|
137
221
|
animate={{ opacity: 1 }}
|
|
138
222
|
exit={{ opacity: 0 }}
|
|
139
223
|
transition={{ duration: 0.2 }}
|
|
140
|
-
// z-index ana MediaManager modal'ından yüksek (nested)
|
|
141
224
|
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/80 backdrop-blur-sm p-4"
|
|
142
225
|
onClick={(e) => {
|
|
143
226
|
if (e.target === e.currentTarget) handleCancel()
|
|
@@ -148,11 +231,11 @@ export function CropDialog({
|
|
|
148
231
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
149
232
|
exit={{ opacity: 0, scale: 0.98 }}
|
|
150
233
|
transition={{ duration: 0.25, ease: [0.22, 1, 0.36, 1] }}
|
|
151
|
-
className="flex h-[min(
|
|
234
|
+
className="flex h-[min(92vh,760px)] w-full max-w-5xl flex-col overflow-hidden rounded-xl border bg-background shadow-2xl"
|
|
152
235
|
>
|
|
153
236
|
{/* Header */}
|
|
154
237
|
<div className="flex items-center justify-between gap-3 border-b px-4 py-3">
|
|
155
|
-
<div className="flex flex-col">
|
|
238
|
+
<div className="flex flex-col min-w-0">
|
|
156
239
|
<span className="text-sm font-semibold">Crop image</span>
|
|
157
240
|
<span className="truncate text-xs text-muted-foreground">
|
|
158
241
|
{file.name}
|
|
@@ -178,45 +261,119 @@ export function CropDialog({
|
|
|
178
261
|
</button>
|
|
179
262
|
</div>
|
|
180
263
|
|
|
181
|
-
{/* Aspect toolbar */}
|
|
264
|
+
{/* Aspect + rotate toolbar */}
|
|
182
265
|
<div className="flex flex-wrap items-center gap-1 border-b bg-muted/20 px-3 py-2">
|
|
183
|
-
|
|
266
|
+
<div className="flex flex-1 flex-wrap items-center gap-1">
|
|
267
|
+
{ASPECT_PRESETS.map((p) => (
|
|
268
|
+
<button
|
|
269
|
+
key={p.id}
|
|
270
|
+
type="button"
|
|
271
|
+
onClick={() => setAspectId(p.id)}
|
|
272
|
+
className={cls(
|
|
273
|
+
"rounded-md px-2.5 py-1 text-xs transition-colors",
|
|
274
|
+
aspectId === p.id
|
|
275
|
+
? "bg-foreground text-background"
|
|
276
|
+
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
277
|
+
)}
|
|
278
|
+
>
|
|
279
|
+
{p.label}
|
|
280
|
+
</button>
|
|
281
|
+
))}
|
|
282
|
+
</div>
|
|
283
|
+
<div className="ms-auto flex items-center gap-1">
|
|
184
284
|
<button
|
|
185
|
-
key={p.id}
|
|
186
285
|
type="button"
|
|
187
|
-
onClick={() =>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
286
|
+
onClick={() => handleRotate(-90)}
|
|
287
|
+
title="Rotate left (Shift+R)"
|
|
288
|
+
aria-label="Rotate left"
|
|
289
|
+
className="rounded-md border px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
290
|
+
>
|
|
291
|
+
<RotateLeftIcon />
|
|
292
|
+
</button>
|
|
293
|
+
<button
|
|
294
|
+
type="button"
|
|
295
|
+
onClick={() => handleRotate(90)}
|
|
296
|
+
title="Rotate right (R)"
|
|
297
|
+
aria-label="Rotate right"
|
|
298
|
+
className="rounded-md border px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
194
299
|
>
|
|
195
|
-
|
|
300
|
+
<RotateRightIcon />
|
|
196
301
|
</button>
|
|
197
|
-
|
|
302
|
+
</div>
|
|
198
303
|
</div>
|
|
199
304
|
|
|
200
|
-
{/*
|
|
201
|
-
<div className="
|
|
202
|
-
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
305
|
+
{/* Body: cropper + preview sidebar */}
|
|
306
|
+
<div className="flex flex-1 min-h-0 flex-col overflow-hidden md:flex-row">
|
|
307
|
+
{/* Cropper canvas */}
|
|
308
|
+
<div className="relative flex-1 bg-black">
|
|
309
|
+
{tooLarge ? (
|
|
310
|
+
<div className="flex h-full w-full items-center justify-center p-6 text-center text-sm text-white/70">
|
|
311
|
+
Image too large to crop in browser. Upload as-is or resize
|
|
312
|
+
beforehand.
|
|
313
|
+
</div>
|
|
314
|
+
) : (
|
|
315
|
+
<Cropper
|
|
316
|
+
image={imageUrl}
|
|
317
|
+
crop={crop}
|
|
318
|
+
zoom={zoom}
|
|
319
|
+
rotation={rotation}
|
|
320
|
+
aspect={aspect}
|
|
321
|
+
onCropChange={setCrop}
|
|
322
|
+
onCropComplete={onCropComplete}
|
|
323
|
+
onZoomChange={setZoom}
|
|
324
|
+
onRotationChange={setRotation}
|
|
325
|
+
showGrid
|
|
326
|
+
objectFit="contain"
|
|
327
|
+
/>
|
|
328
|
+
)}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
{/* Preview sidebar */}
|
|
332
|
+
<div className="flex w-full shrink-0 flex-col gap-3 border-t bg-muted/10 p-4 md:w-64 md:border-l md:border-t-0">
|
|
333
|
+
<div className="flex items-center justify-between">
|
|
334
|
+
<span className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
|
335
|
+
Preview
|
|
336
|
+
</span>
|
|
337
|
+
{displayW > 0 && displayH > 0 && (
|
|
338
|
+
<span className="font-mono text-[10px] text-muted-foreground">
|
|
339
|
+
{displayW}×{displayH}
|
|
340
|
+
</span>
|
|
341
|
+
)}
|
|
206
342
|
</div>
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
343
|
+
<div className="flex min-h-[140px] items-center justify-center rounded-lg border border-dashed bg-background/40 p-2">
|
|
344
|
+
{tooLarge ? (
|
|
345
|
+
<span className="text-[11px] text-muted-foreground">
|
|
346
|
+
Preview unavailable
|
|
347
|
+
</span>
|
|
348
|
+
) : croppedAreaPixels ? (
|
|
349
|
+
<canvas
|
|
350
|
+
ref={previewCanvasRef}
|
|
351
|
+
className="max-h-[220px] max-w-full rounded-sm shadow-sm"
|
|
352
|
+
/>
|
|
353
|
+
) : (
|
|
354
|
+
<span className="text-[11px] text-muted-foreground">
|
|
355
|
+
Adjust crop…
|
|
356
|
+
</span>
|
|
357
|
+
)}
|
|
358
|
+
</div>
|
|
359
|
+
<div className="flex flex-col gap-1 text-[11px] text-muted-foreground">
|
|
360
|
+
<div className="flex items-center justify-between">
|
|
361
|
+
<span>Aspect</span>
|
|
362
|
+
<span className="font-mono">
|
|
363
|
+
{ASPECT_PRESETS.find((p) => p.id === aspectId)?.label ??
|
|
364
|
+
"Free"}
|
|
365
|
+
</span>
|
|
366
|
+
</div>
|
|
367
|
+
<div className="flex items-center justify-between">
|
|
368
|
+
<span>Rotation</span>
|
|
369
|
+
<span className="font-mono">{rotation}°</span>
|
|
370
|
+
</div>
|
|
371
|
+
<div className="flex items-center justify-between">
|
|
372
|
+
<span>Zoom</span>
|
|
373
|
+
<span className="font-mono">{zoom.toFixed(2)}×</span>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
220
377
|
</div>
|
|
221
378
|
|
|
222
379
|
{/* Zoom + actions */}
|
|
@@ -251,6 +408,7 @@ export function CropDialog({
|
|
|
251
408
|
type="button"
|
|
252
409
|
onClick={() => {
|
|
253
410
|
setZoom(1)
|
|
411
|
+
setRotation(0)
|
|
254
412
|
setCrop({ x: 0, y: 0 })
|
|
255
413
|
}}
|
|
256
414
|
className="rounded-md border px-2 py-0.5 text-xs hover:bg-muted/50"
|
|
@@ -293,6 +451,68 @@ export function CropDialog({
|
|
|
293
451
|
)
|
|
294
452
|
}
|
|
295
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Rotated crop'u canvas'a yaz. Rotation 0° fast-path; aksi halde önce
|
|
456
|
+
* "rotated bounding box" canvas'ı oluştur (orijinal image'in döndürülmüş
|
|
457
|
+
* versiyonu), sonra croppedAreaPixels koordinatlarında çıkar.
|
|
458
|
+
*
|
|
459
|
+
* react-easy-crop's `croppedAreaPixels` rotation-aware: koordinatlar
|
|
460
|
+
* orijinal image'i rotasyon merkezinden döndürdükten sonraki "visual"
|
|
461
|
+
* bounding box üzerinden hesaplanır — biz aynı transform'u canvas'a
|
|
462
|
+
* uygulayıp aynı koordinatlardan drawImage yapıyoruz.
|
|
463
|
+
*/
|
|
464
|
+
function drawRotatedCrop(
|
|
465
|
+
ctx: CanvasRenderingContext2D,
|
|
466
|
+
image: HTMLImageElement,
|
|
467
|
+
area: CropArea,
|
|
468
|
+
rotation: number,
|
|
469
|
+
dstW: number,
|
|
470
|
+
dstH: number,
|
|
471
|
+
): void {
|
|
472
|
+
if (rotation === 0) {
|
|
473
|
+
ctx.drawImage(
|
|
474
|
+
image,
|
|
475
|
+
area.x,
|
|
476
|
+
area.y,
|
|
477
|
+
area.width,
|
|
478
|
+
area.height,
|
|
479
|
+
0,
|
|
480
|
+
0,
|
|
481
|
+
dstW,
|
|
482
|
+
dstH,
|
|
483
|
+
)
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
// Build a rotated source canvas at the size of the rotated bounding box,
|
|
487
|
+
// then crop from it.
|
|
488
|
+
const rad = (rotation * Math.PI) / 180
|
|
489
|
+
const sin = Math.abs(Math.sin(rad))
|
|
490
|
+
const cos = Math.abs(Math.cos(rad))
|
|
491
|
+
const iw = image.naturalWidth
|
|
492
|
+
const ih = image.naturalHeight
|
|
493
|
+
const bbW = iw * cos + ih * sin
|
|
494
|
+
const bbH = iw * sin + ih * cos
|
|
495
|
+
const tmp = document.createElement("canvas")
|
|
496
|
+
tmp.width = bbW
|
|
497
|
+
tmp.height = bbH
|
|
498
|
+
const tctx = tmp.getContext("2d")
|
|
499
|
+
if (!tctx) return
|
|
500
|
+
tctx.translate(bbW / 2, bbH / 2)
|
|
501
|
+
tctx.rotate(rad)
|
|
502
|
+
tctx.drawImage(image, -iw / 2, -ih / 2)
|
|
503
|
+
ctx.drawImage(
|
|
504
|
+
tmp,
|
|
505
|
+
area.x,
|
|
506
|
+
area.y,
|
|
507
|
+
area.width,
|
|
508
|
+
area.height,
|
|
509
|
+
0,
|
|
510
|
+
0,
|
|
511
|
+
dstW,
|
|
512
|
+
dstH,
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
|
|
296
516
|
/**
|
|
297
517
|
* Canvas ile crop area'yı çıkar + Blob döndür.
|
|
298
518
|
* Output MIME: PNG ise PNG, diğerleri JPEG (transparency yoksa).
|
|
@@ -300,6 +520,7 @@ export function CropDialog({
|
|
|
300
520
|
async function getCroppedBlob(
|
|
301
521
|
imageUrl: string,
|
|
302
522
|
area: CropArea,
|
|
523
|
+
rotation: number,
|
|
303
524
|
sourceMime: string,
|
|
304
525
|
quality: number,
|
|
305
526
|
): Promise<Blob> {
|
|
@@ -309,17 +530,8 @@ async function getCroppedBlob(
|
|
|
309
530
|
canvas.height = area.height
|
|
310
531
|
const ctx = canvas.getContext("2d")
|
|
311
532
|
if (!ctx) throw new Error("Canvas 2D context unavailable")
|
|
312
|
-
ctx.
|
|
313
|
-
|
|
314
|
-
area.x,
|
|
315
|
-
area.y,
|
|
316
|
-
area.width,
|
|
317
|
-
area.height,
|
|
318
|
-
0,
|
|
319
|
-
0,
|
|
320
|
-
area.width,
|
|
321
|
-
area.height,
|
|
322
|
-
)
|
|
533
|
+
ctx.imageSmoothingQuality = "high"
|
|
534
|
+
drawRotatedCrop(ctx, image, area, rotation, area.width, area.height)
|
|
323
535
|
const outputMime = sourceMime === "image/png" ? "image/png" : "image/jpeg"
|
|
324
536
|
return new Promise<Blob>((resolve, reject) => {
|
|
325
537
|
canvas.toBlob(
|
|
@@ -342,3 +554,39 @@ function loadImage(url: string): Promise<HTMLImageElement> {
|
|
|
342
554
|
function cls(...arr: Array<string | false | null | undefined>): string {
|
|
343
555
|
return arr.filter(Boolean).join(" ")
|
|
344
556
|
}
|
|
557
|
+
|
|
558
|
+
function RotateLeftIcon() {
|
|
559
|
+
return (
|
|
560
|
+
<svg
|
|
561
|
+
viewBox="0 0 24 24"
|
|
562
|
+
fill="none"
|
|
563
|
+
stroke="currentColor"
|
|
564
|
+
strokeWidth="2"
|
|
565
|
+
strokeLinecap="round"
|
|
566
|
+
strokeLinejoin="round"
|
|
567
|
+
className="size-3.5"
|
|
568
|
+
aria-hidden="true"
|
|
569
|
+
>
|
|
570
|
+
<path d="M3 12a9 9 0 1 0 3-6.7" />
|
|
571
|
+
<path d="M3 4v5h5" />
|
|
572
|
+
</svg>
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function RotateRightIcon() {
|
|
577
|
+
return (
|
|
578
|
+
<svg
|
|
579
|
+
viewBox="0 0 24 24"
|
|
580
|
+
fill="none"
|
|
581
|
+
stroke="currentColor"
|
|
582
|
+
strokeWidth="2"
|
|
583
|
+
strokeLinecap="round"
|
|
584
|
+
strokeLinejoin="round"
|
|
585
|
+
className="size-3.5"
|
|
586
|
+
aria-hidden="true"
|
|
587
|
+
>
|
|
588
|
+
<path d="M21 12a9 9 0 1 1-3-6.7" />
|
|
589
|
+
<path d="M21 4v5h-5" />
|
|
590
|
+
</svg>
|
|
591
|
+
)
|
|
592
|
+
}
|