@alpaca-editor/core 1.0.3978 → 1.0.3980
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.
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/config/config.js +18 -2
- package/dist/config/config.js.map +1 -1
- package/dist/editor/AspectRatioSelector.d.ts +13 -0
- package/dist/editor/AspectRatioSelector.js +71 -0
- package/dist/editor/AspectRatioSelector.js.map +1 -0
- package/dist/editor/ConfirmationDialog.js +4 -5
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/PictureCropper.d.ts +1 -1
- package/dist/editor/PictureCropper.js +466 -113
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/Terminal.js +5 -4
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AiTerminal.js +20 -2
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/EditorClient.js +14 -3
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +3 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +13 -5
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/media-selector/Preview.js +1 -1
- package/dist/editor/media-selector/Preview.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +48 -24
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +3 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +6 -6
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +26 -4
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
- package/dist/editor/page-viewer/EditorForm.js +16 -2
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/EditorFormPopup.d.ts +11 -0
- package/dist/editor/page-viewer/EditorFormPopup.js +41 -0
- package/dist/editor/page-viewer/EditorFormPopup.js.map +1 -0
- package/dist/editor/page-viewer/PageViewer.js +4 -6
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/services/contextService.d.ts +26 -0
- package/dist/editor/services/contextService.js +102 -0
- package/dist/editor/services/contextService.js.map +1 -0
- package/dist/editor/sidebar/Completions.d.ts +1 -0
- package/dist/editor/sidebar/Completions.js +54 -0
- package/dist/editor/sidebar/Completions.js.map +1 -0
- package/dist/editor/sidebar/Validation.js +3 -3
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +17 -3
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +2 -1
- package/dist/editor/ui/SimpleTabs.js +2 -2
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +134 -30
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ui/switch.tsx +1 -1
- package/src/config/config.tsx +18 -1
- package/src/editor/AspectRatioSelector.tsx +146 -0
- package/src/editor/ConfirmationDialog.tsx +36 -45
- package/src/editor/PictureCropper.tsx +724 -233
- package/src/editor/Terminal.tsx +9 -8
- package/src/editor/ai/AiTerminal.tsx +58 -15
- package/src/editor/client/EditorClient.tsx +26 -1
- package/src/editor/client/editContext.ts +7 -0
- package/src/editor/commands/componentCommands.tsx +14 -9
- package/src/editor/media-selector/Preview.tsx +7 -5
- package/src/editor/page-editor-chrome/FrameMenu.tsx +70 -15
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +9 -3
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +31 -5
- package/src/editor/page-viewer/EditorForm.tsx +21 -1
- package/src/editor/page-viewer/EditorFormPopup.tsx +104 -0
- package/src/editor/page-viewer/PageViewer.tsx +3 -11
- package/src/editor/services/contextService.ts +146 -0
- package/src/editor/sidebar/Completions.tsx +160 -0
- package/src/editor/sidebar/Validation.tsx +9 -10
- package/src/editor/ui/PerfectTree.tsx +19 -3
- package/src/editor/ui/SimpleTabs.tsx +4 -1
- package/src/revision.ts +2 -2
- package/src/types.ts +1 -0
- package/dist/editor/menubar/BrowseHistory.d.ts +0 -6
- package/dist/editor/menubar/BrowseHistory.js +0 -11
- package/dist/editor/menubar/BrowseHistory.js.map +0 -1
- package/src/editor/menubar/BrowseHistory.tsx +0 -28
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Dialog } from "
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "../components/ui/dialog";
|
|
3
3
|
import { useEditContext } from "./client/editContext";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
|
-
import { Button } from "
|
|
5
|
+
import { Button } from "../components/ui/button";
|
|
6
6
|
import DialogButtons from "./ui/DialogButtons";
|
|
7
7
|
import { classNames } from "primereact/utils";
|
|
8
|
-
|
|
8
|
+
import { RotateCcw } from "lucide-react";
|
|
9
|
+
import { SimpleTabs } from "./ui/SimpleTabs";
|
|
10
|
+
import { AspectRatioSelector } from "./AspectRatioSelector";
|
|
11
|
+
export function PictureCropper({ field, onClose, variantName: initialVariantName, }) {
|
|
9
12
|
const [pictureValue, setPictureValue] = useState();
|
|
10
13
|
const [isValid, setIsValid] = useState(true);
|
|
11
14
|
const [rawValue, setRawValue] = useState();
|
|
12
15
|
const [actualImageDimensions, setActualImageDimensions] = useState(null);
|
|
16
|
+
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
|
17
|
+
// Track selected aspect ratio for each variant
|
|
18
|
+
const [selectedAspectRatio, setSelectedAspectRatio] = useState(null);
|
|
19
|
+
// Track crop rectangles for each variant separately
|
|
20
|
+
const [variantRects, setVariantRects] = useState(new Map());
|
|
21
|
+
// Cache image dimensions by URL to avoid showing loading indicator for already loaded images
|
|
22
|
+
const imageDimensionsCache = useRef(new Map());
|
|
13
23
|
const imageRef = useRef(null);
|
|
14
24
|
const [rect, setRect] = useState();
|
|
15
25
|
const rectRef = useRef(rect);
|
|
@@ -23,6 +33,29 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
23
33
|
const EDGE_THRESHOLD = 10; // pixels from edge to detect resize
|
|
24
34
|
const [resizeEdge, setResizeEdge] = useState(null);
|
|
25
35
|
const resizeEdgeRef = useRef(null);
|
|
36
|
+
// Helper function to create default region for aspect ratio locked variants
|
|
37
|
+
const createDefaultRegion = () => {
|
|
38
|
+
if (!selectedAspectRatio || !actualImageDimensions) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
// Always use the actual loaded image dimensions for aspect ratio calculations
|
|
42
|
+
const sourceWidth = actualImageDimensions.width;
|
|
43
|
+
const sourceHeight = actualImageDimensions.height;
|
|
44
|
+
const sourceAspectRatio = sourceHeight > 0 ? sourceWidth / sourceHeight : 1;
|
|
45
|
+
const targetAspectRatio = selectedAspectRatio;
|
|
46
|
+
// Calculate maximum size that fits the target aspect ratio within the source image
|
|
47
|
+
let width = 1.0; // Start with full width
|
|
48
|
+
let height = (width / targetAspectRatio) * sourceAspectRatio;
|
|
49
|
+
// If height exceeds bounds, start with full height instead
|
|
50
|
+
if (height > 1.0) {
|
|
51
|
+
height = 1.0;
|
|
52
|
+
width = (height * targetAspectRatio) / sourceAspectRatio;
|
|
53
|
+
}
|
|
54
|
+
// Center the rectangle
|
|
55
|
+
const x = (1.0 - width) / 2;
|
|
56
|
+
const y = (1.0 - height) / 2;
|
|
57
|
+
return { x, y, width, height };
|
|
58
|
+
};
|
|
26
59
|
const getResizeEdge = (pos, rect, bounds) => {
|
|
27
60
|
if (!rect || !bounds)
|
|
28
61
|
return null;
|
|
@@ -62,28 +95,106 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
62
95
|
: { Variants: [] };
|
|
63
96
|
setRawValue(raw);
|
|
64
97
|
}, [field]);
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
// Set initial tab based on the provided variant name
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (pictureValue?.variants && initialVariantName) {
|
|
101
|
+
const initialIndex = pictureValue.variants.findIndex((variant) => variant.name === initialVariantName);
|
|
102
|
+
if (initialIndex >= 0) {
|
|
103
|
+
setActiveTabIndex(initialIndex);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}, [pictureValue, initialVariantName]);
|
|
107
|
+
const selectedVariant = pictureValue &&
|
|
108
|
+
pictureValue.variants &&
|
|
109
|
+
pictureValue.variants[activeTabIndex]
|
|
110
|
+
? pictureValue.variants[activeTabIndex]
|
|
67
111
|
: null;
|
|
112
|
+
const selectedVariantName = selectedVariant?.name || "";
|
|
113
|
+
// Update selected aspect ratio when switching variants
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (selectedVariant) {
|
|
116
|
+
// If variant has locked aspect ratio, use that
|
|
117
|
+
if (selectedVariant.aspectRatioLock) {
|
|
118
|
+
setSelectedAspectRatio(selectedVariant.aspectRatioLock);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Default to free form if no lock
|
|
122
|
+
setSelectedAspectRatio(null);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}, [selectedVariant]);
|
|
126
|
+
// Manage image dimensions when the image source changes between variants
|
|
127
|
+
const currentImageSrc = selectedVariant?.originalSrc ?? selectedVariant?.src;
|
|
128
|
+
const previousImageSrcRef = useRef(undefined);
|
|
68
129
|
useEffect(() => {
|
|
130
|
+
if (currentImageSrc && currentImageSrc !== previousImageSrcRef.current) {
|
|
131
|
+
// Check if we have cached dimensions for this image
|
|
132
|
+
const cachedDimensions = imageDimensionsCache.current.get(currentImageSrc);
|
|
133
|
+
if (cachedDimensions) {
|
|
134
|
+
setActualImageDimensions(cachedDimensions);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Only reset to null if we don't have cached dimensions
|
|
138
|
+
setActualImageDimensions(null);
|
|
139
|
+
}
|
|
140
|
+
previousImageSrcRef.current = currentImageSrc;
|
|
141
|
+
}
|
|
142
|
+
}, [currentImageSrc]);
|
|
143
|
+
// Load crop rectangle when switching variants
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!selectedVariantName)
|
|
146
|
+
return;
|
|
147
|
+
// Check if we have a current working rect for this variant
|
|
148
|
+
const existingRect = variantRects.get(selectedVariantName);
|
|
149
|
+
// If we have a valid rect, use it
|
|
150
|
+
if (existingRect !== undefined) {
|
|
151
|
+
setRect(existingRect);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Initialize rect for this variant if not already done
|
|
155
|
+
let initialRect;
|
|
156
|
+
// Try to load from saved region if this variant hasn't been touched yet
|
|
69
157
|
if (selectedVariant?.region &&
|
|
70
|
-
(!
|
|
71
|
-
Math.abs(
|
|
158
|
+
(!selectedAspectRatio ||
|
|
159
|
+
Math.abs(selectedAspectRatio -
|
|
72
160
|
(selectedVariant.region.width * selectedVariant.width) /
|
|
73
161
|
(selectedVariant.region.height * selectedVariant.height)) < 0.1) &&
|
|
74
162
|
selectedVariant.region.width > 0 &&
|
|
75
163
|
selectedVariant.region.height > 0) {
|
|
76
|
-
|
|
164
|
+
initialRect = {
|
|
77
165
|
width: selectedVariant.region.width,
|
|
78
166
|
height: selectedVariant.region.height,
|
|
79
167
|
y: selectedVariant.region.y,
|
|
80
168
|
x: selectedVariant.region.x,
|
|
81
|
-
}
|
|
169
|
+
};
|
|
82
170
|
}
|
|
83
171
|
else {
|
|
84
|
-
|
|
172
|
+
// Try to create a default region if aspect ratio is locked
|
|
173
|
+
initialRect = createDefaultRegion();
|
|
85
174
|
}
|
|
86
|
-
|
|
175
|
+
// Store in variantRects and set as current rect
|
|
176
|
+
setVariantRects((prev) => new Map(prev).set(selectedVariantName, initialRect));
|
|
177
|
+
setRect(initialRect);
|
|
178
|
+
}, [selectedVariant, selectedVariantName]);
|
|
179
|
+
// Create default region when image dimensions become available
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (selectedVariant &&
|
|
182
|
+
actualImageDimensions &&
|
|
183
|
+
!selectedVariant?.region &&
|
|
184
|
+
selectedAspectRatio) {
|
|
185
|
+
const defaultRect = createDefaultRegion();
|
|
186
|
+
setVariantRects((prev) => new Map(prev).set(selectedVariant.name, defaultRect));
|
|
187
|
+
setRect(defaultRect);
|
|
188
|
+
}
|
|
189
|
+
}, [actualImageDimensions, selectedVariant, selectedAspectRatio]);
|
|
190
|
+
// Update working state when rect changes (but only if user is actively editing)
|
|
191
|
+
const isUserEditingRef = useRef(false);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (selectedVariantName && isUserEditingRef.current) {
|
|
194
|
+
setVariantRects((prev) => new Map(prev).set(selectedVariantName, rect));
|
|
195
|
+
isUserEditingRef.current = false;
|
|
196
|
+
}
|
|
197
|
+
}, [rect, selectedVariantName]);
|
|
87
198
|
useEffect(() => {
|
|
88
199
|
if (!rect)
|
|
89
200
|
return;
|
|
@@ -101,13 +212,18 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
101
212
|
if (!selectedVariantName)
|
|
102
213
|
return;
|
|
103
214
|
if (selectedVariant) {
|
|
104
|
-
|
|
215
|
+
// Create a deep copy of rawValue to avoid mutations
|
|
216
|
+
const newRawValue = JSON.parse(JSON.stringify(rawValue));
|
|
217
|
+
let selected = newRawValue.Variants?.find((x) => x.Name == selectedVariantName);
|
|
105
218
|
if (!selected) {
|
|
106
219
|
selected = {
|
|
107
220
|
Name: selectedVariantName,
|
|
108
221
|
MediaId: selectedVariant.mediaId,
|
|
109
222
|
};
|
|
110
|
-
|
|
223
|
+
if (!newRawValue.Variants) {
|
|
224
|
+
newRawValue.Variants = [];
|
|
225
|
+
}
|
|
226
|
+
newRawValue.Variants.push(selected);
|
|
111
227
|
}
|
|
112
228
|
if (selected) {
|
|
113
229
|
if (rect)
|
|
@@ -119,7 +235,7 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
119
235
|
};
|
|
120
236
|
else
|
|
121
237
|
selected.Region = undefined;
|
|
122
|
-
setRawValue(
|
|
238
|
+
setRawValue(newRawValue);
|
|
123
239
|
}
|
|
124
240
|
}
|
|
125
241
|
}, [rect]);
|
|
@@ -152,68 +268,256 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
152
268
|
y: (ev.clientY - bounds.top) / bounds.height,
|
|
153
269
|
};
|
|
154
270
|
let newRect = { ...rect };
|
|
155
|
-
const aspectRatio =
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
271
|
+
const aspectRatio = selectedAspectRatio;
|
|
272
|
+
const sourceWidth = actualImageDimensions?.width || 0;
|
|
273
|
+
const sourceHeight = actualImageDimensions?.height || 0;
|
|
274
|
+
const sourceAspectRatio = sourceHeight > 0 ? sourceWidth / sourceHeight : 1;
|
|
275
|
+
// Handle resize based on edge - structure to avoid conflicts between corner handles
|
|
276
|
+
if (resizeEdgeRef.current === "w") {
|
|
277
|
+
// Pure west edge - resize from left, keep right edge fixed
|
|
278
|
+
const rightEdge = rect.x + rect.width;
|
|
279
|
+
const newWidth = Math.max(0.01, rightEdge - pos.x);
|
|
280
|
+
newRect.width = newWidth;
|
|
281
|
+
newRect.x = rightEdge - newWidth;
|
|
282
|
+
if (aspectRatio) {
|
|
283
|
+
const newHeight = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
284
|
+
const centerY = rect.y + rect.height / 2;
|
|
285
|
+
newRect.y = centerY - newHeight / 2;
|
|
286
|
+
newRect.height = newHeight;
|
|
172
287
|
}
|
|
173
288
|
}
|
|
174
|
-
if (resizeEdgeRef.current
|
|
289
|
+
else if (resizeEdgeRef.current === "e") {
|
|
290
|
+
// Pure east edge - resize from right, keep left edge fixed
|
|
175
291
|
const newWidth = pos.x - rect.x;
|
|
176
292
|
if (newWidth > 0) {
|
|
177
293
|
newRect.width = newWidth;
|
|
178
294
|
if (aspectRatio) {
|
|
179
|
-
|
|
180
|
-
|
|
295
|
+
const newHeight = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
296
|
+
const centerY = rect.y + rect.height / 2;
|
|
297
|
+
newRect.y = centerY - newHeight / 2;
|
|
298
|
+
newRect.height = newHeight;
|
|
181
299
|
}
|
|
182
300
|
}
|
|
183
301
|
}
|
|
184
|
-
if (resizeEdgeRef.current
|
|
185
|
-
|
|
302
|
+
else if (resizeEdgeRef.current === "n") {
|
|
303
|
+
// Pure north edge - resize from top, keep bottom edge fixed
|
|
304
|
+
const bottomEdge = rect.y + rect.height;
|
|
305
|
+
const newHeight = bottomEdge - pos.y;
|
|
186
306
|
if (newHeight > 0) {
|
|
187
307
|
newRect.height = newHeight;
|
|
188
308
|
newRect.y = pos.y;
|
|
189
309
|
if (aspectRatio) {
|
|
190
|
-
|
|
191
|
-
|
|
310
|
+
const newWidth = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
311
|
+
const centerX = rect.x + rect.width / 2;
|
|
312
|
+
newRect.x = centerX - newWidth / 2;
|
|
313
|
+
newRect.width = newWidth;
|
|
192
314
|
}
|
|
193
315
|
}
|
|
194
316
|
}
|
|
195
|
-
if (resizeEdgeRef.current
|
|
317
|
+
else if (resizeEdgeRef.current === "s") {
|
|
318
|
+
// Pure south edge - resize from bottom, keep top edge fixed
|
|
196
319
|
const newHeight = pos.y - rect.y;
|
|
197
320
|
if (newHeight > 0) {
|
|
198
321
|
newRect.height = newHeight;
|
|
199
322
|
if (aspectRatio) {
|
|
200
|
-
|
|
201
|
-
|
|
323
|
+
const newWidth = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
324
|
+
const centerX = rect.x + rect.width / 2;
|
|
325
|
+
newRect.x = centerX - newWidth / 2;
|
|
326
|
+
newRect.width = newWidth;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else if (resizeEdgeRef.current.includes("w") &&
|
|
331
|
+
resizeEdgeRef.current.includes("n")) {
|
|
332
|
+
// Northwest corner - keep southeast corner fixed
|
|
333
|
+
const rightEdge = rect.x + rect.width;
|
|
334
|
+
const bottomEdge = rect.y + rect.height;
|
|
335
|
+
const newWidth = Math.max(0.01, rightEdge - pos.x);
|
|
336
|
+
const newHeight = Math.max(0.01, bottomEdge - pos.y);
|
|
337
|
+
newRect.width = newWidth;
|
|
338
|
+
newRect.height = newHeight;
|
|
339
|
+
newRect.x = rightEdge - newWidth;
|
|
340
|
+
newRect.y = bottomEdge - newHeight;
|
|
341
|
+
if (aspectRatio) {
|
|
342
|
+
// Use mouse movement direction to determine primary dimension
|
|
343
|
+
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
344
|
+
newRect.height = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
345
|
+
newRect.y = bottomEdge - newRect.height;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
newRect.width = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
349
|
+
newRect.x = rightEdge - newRect.width;
|
|
202
350
|
}
|
|
203
351
|
}
|
|
204
352
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
newRect.width =
|
|
212
|
-
|
|
213
|
-
newRect.
|
|
353
|
+
else if (resizeEdgeRef.current.includes("w") &&
|
|
354
|
+
resizeEdgeRef.current.includes("s")) {
|
|
355
|
+
// Southwest corner - keep northeast corner fixed
|
|
356
|
+
const rightEdge = rect.x + rect.width;
|
|
357
|
+
const newWidth = Math.max(0.01, rightEdge - pos.x);
|
|
358
|
+
const newHeight = Math.max(0.01, pos.y - rect.y);
|
|
359
|
+
newRect.width = newWidth;
|
|
360
|
+
newRect.height = newHeight;
|
|
361
|
+
newRect.x = rightEdge - newWidth;
|
|
362
|
+
if (aspectRatio) {
|
|
363
|
+
// Use mouse movement direction to determine primary dimension
|
|
364
|
+
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
365
|
+
newRect.height = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
newRect.width = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
369
|
+
newRect.x = rightEdge - newRect.width;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else if (resizeEdgeRef.current.includes("e") &&
|
|
374
|
+
resizeEdgeRef.current.includes("n")) {
|
|
375
|
+
// Northeast corner - keep southwest corner fixed
|
|
376
|
+
const bottomEdge = rect.y + rect.height;
|
|
377
|
+
const newWidth = Math.max(0.01, pos.x - rect.x);
|
|
378
|
+
const newHeight = Math.max(0.01, bottomEdge - pos.y);
|
|
379
|
+
newRect.width = newWidth;
|
|
380
|
+
newRect.height = newHeight;
|
|
381
|
+
newRect.y = bottomEdge - newHeight;
|
|
382
|
+
if (aspectRatio) {
|
|
383
|
+
// Use mouse movement direction to determine primary dimension
|
|
384
|
+
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
385
|
+
newRect.height = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
386
|
+
newRect.y = bottomEdge - newRect.height;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
newRect.width = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (resizeEdgeRef.current.includes("e") &&
|
|
394
|
+
resizeEdgeRef.current.includes("s")) {
|
|
395
|
+
// Southeast corner - keep northwest corner fixed
|
|
396
|
+
const newWidth = Math.max(0.01, pos.x - rect.x);
|
|
397
|
+
const newHeight = Math.max(0.01, pos.y - rect.y);
|
|
398
|
+
newRect.width = newWidth;
|
|
399
|
+
newRect.height = newHeight;
|
|
400
|
+
if (aspectRatio) {
|
|
401
|
+
// Use mouse movement direction to determine primary dimension
|
|
402
|
+
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
403
|
+
newRect.height = (newWidth / aspectRatio) * sourceAspectRatio;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
newRect.width = (newHeight * aspectRatio) / sourceAspectRatio;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Constrain to bounds while maintaining aspect ratio
|
|
411
|
+
// For west edge resizes, maintain the right edge position when hitting left bound
|
|
412
|
+
if (newRect.x < 0) {
|
|
413
|
+
if (resizeEdgeRef.current?.includes("w")) {
|
|
414
|
+
const rightEdge = rect.x + rect.width; // Original right edge position
|
|
415
|
+
newRect.x = 0;
|
|
416
|
+
newRect.width = rightEdge; // Width = distance from left bound to original right edge
|
|
417
|
+
// Recalculate height if aspect ratio is locked
|
|
418
|
+
if (aspectRatio) {
|
|
419
|
+
const newHeight = (rightEdge / aspectRatio) * sourceAspectRatio;
|
|
420
|
+
if (resizeEdgeRef.current === "w") {
|
|
421
|
+
const centerY = rect.y + rect.height / 2;
|
|
422
|
+
newRect.y = centerY - newHeight / 2;
|
|
423
|
+
}
|
|
424
|
+
newRect.height = newHeight;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
newRect.x = 0;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// For north edge resizes, maintain the bottom edge position when hitting top bound
|
|
432
|
+
if (newRect.y < 0) {
|
|
433
|
+
if (resizeEdgeRef.current?.includes("n")) {
|
|
434
|
+
const bottomEdge = rect.y + rect.height;
|
|
435
|
+
newRect.y = 0;
|
|
436
|
+
newRect.height = bottomEdge; // Keep bottom edge fixed
|
|
437
|
+
// Recalculate width if aspect ratio is locked
|
|
438
|
+
if (aspectRatio) {
|
|
439
|
+
const newWidth = (newRect.height * aspectRatio) / sourceAspectRatio;
|
|
440
|
+
if (resizeEdgeRef.current === "n") {
|
|
441
|
+
const centerX = rect.x + rect.width / 2;
|
|
442
|
+
newRect.x = centerX - newWidth / 2;
|
|
443
|
+
}
|
|
444
|
+
newRect.width = newWidth;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
newRect.y = 0;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Apply bounds constraints and maintain aspect ratio
|
|
452
|
+
let boundedRect = { ...newRect };
|
|
453
|
+
if (boundedRect.x + boundedRect.width > 1) {
|
|
454
|
+
boundedRect.width = 1 - boundedRect.x;
|
|
455
|
+
// Re-apply aspect ratio if it was locked
|
|
456
|
+
if (aspectRatio) {
|
|
457
|
+
const newHeight = (boundedRect.width / aspectRatio) * sourceAspectRatio;
|
|
458
|
+
// Maintain anchoring behavior based on resize edge
|
|
459
|
+
if (resizeEdgeRef.current === "w" ||
|
|
460
|
+
resizeEdgeRef.current === "e") {
|
|
461
|
+
// For horizontal resizes, keep centered vertically
|
|
462
|
+
const centerY = rect.y + rect.height / 2;
|
|
463
|
+
boundedRect.y = centerY - newHeight / 2;
|
|
464
|
+
}
|
|
465
|
+
boundedRect.height = newHeight;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (boundedRect.y + boundedRect.height > 1) {
|
|
469
|
+
boundedRect.height = 1 - boundedRect.y;
|
|
470
|
+
// Re-apply aspect ratio if it was locked
|
|
471
|
+
if (aspectRatio) {
|
|
472
|
+
const newWidth = (boundedRect.height * aspectRatio) / sourceAspectRatio;
|
|
473
|
+
// Maintain anchoring behavior based on resize edge
|
|
474
|
+
if (resizeEdgeRef.current === "n" ||
|
|
475
|
+
resizeEdgeRef.current === "s") {
|
|
476
|
+
// For vertical resizes, keep centered horizontally
|
|
477
|
+
const centerX = rect.x + rect.width / 2;
|
|
478
|
+
boundedRect.x = centerX - newWidth / 2;
|
|
479
|
+
}
|
|
480
|
+
boundedRect.width = newWidth;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// Final check - if aspect ratio re-calculation pushes us out of bounds again,
|
|
484
|
+
// we need to find the maximum size that fits both constraints
|
|
485
|
+
if (aspectRatio &&
|
|
486
|
+
(boundedRect.x + boundedRect.width > 1 ||
|
|
487
|
+
boundedRect.y + boundedRect.height > 1)) {
|
|
488
|
+
const maxWidth = 1 - boundedRect.x;
|
|
489
|
+
const maxHeight = 1 - boundedRect.y;
|
|
490
|
+
const maxHeightFromWidth = (maxWidth / aspectRatio) * sourceAspectRatio;
|
|
491
|
+
const maxWidthFromHeight = (maxHeight * aspectRatio) / sourceAspectRatio;
|
|
492
|
+
if (maxHeightFromWidth <= maxHeight) {
|
|
493
|
+
// Width is the limiting factor
|
|
494
|
+
boundedRect.width = maxWidth;
|
|
495
|
+
boundedRect.height = maxHeightFromWidth;
|
|
496
|
+
// Maintain anchoring for horizontal resizes
|
|
497
|
+
if (resizeEdgeRef.current === "w" ||
|
|
498
|
+
resizeEdgeRef.current === "e") {
|
|
499
|
+
const centerY = rect.y + rect.height / 2;
|
|
500
|
+
boundedRect.y = Math.max(0, Math.min(1 - boundedRect.height, centerY - boundedRect.height / 2));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
// Height is the limiting factor
|
|
505
|
+
boundedRect.height = maxHeight;
|
|
506
|
+
boundedRect.width = maxWidthFromHeight;
|
|
507
|
+
// Maintain anchoring for vertical resizes
|
|
508
|
+
if (resizeEdgeRef.current === "n" ||
|
|
509
|
+
resizeEdgeRef.current === "s") {
|
|
510
|
+
const centerX = rect.x + rect.width / 2;
|
|
511
|
+
boundedRect.x = Math.max(0, Math.min(1 - boundedRect.width, centerX - boundedRect.width / 2));
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
newRect = boundedRect;
|
|
516
|
+
isUserEditingRef.current = true;
|
|
214
517
|
setRect(newRect);
|
|
215
518
|
}
|
|
216
519
|
else if (movingRef.current) {
|
|
520
|
+
isUserEditingRef.current = true;
|
|
217
521
|
setRect({
|
|
218
522
|
...rect,
|
|
219
523
|
x: Math.max(0, Math.min(1 - rectRef.current.width, (ev.clientX - bounds.left) / bounds.width - offset.current.x)),
|
|
@@ -221,14 +525,10 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
221
525
|
});
|
|
222
526
|
}
|
|
223
527
|
else {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
? selectedVariant.height
|
|
229
|
-
: actualImageDimensions?.height || 0;
|
|
230
|
-
const originalAspectRatio = actualHeight > 0 ? actualWidth / actualHeight : 1;
|
|
231
|
-
const aspectRatio = selectedVariant.aspectRatioLock;
|
|
528
|
+
const sourceWidth = actualImageDimensions?.width || 0;
|
|
529
|
+
const sourceHeight = actualImageDimensions?.height || 0;
|
|
530
|
+
const sourceAspectRatio = sourceHeight > 0 ? sourceWidth / sourceHeight : 1;
|
|
531
|
+
const aspectRatio = selectedAspectRatio;
|
|
232
532
|
const currentX = (ev.clientX - bounds.left) / bounds.width;
|
|
233
533
|
const currentY = (ev.clientY - bounds.top) / bounds.height;
|
|
234
534
|
const minX = Math.min(rect.x, currentX);
|
|
@@ -239,26 +539,47 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
239
539
|
let height = maxY - minY;
|
|
240
540
|
if (aspectRatio) {
|
|
241
541
|
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
242
|
-
height = (width / aspectRatio) *
|
|
542
|
+
height = (width / aspectRatio) * sourceAspectRatio;
|
|
243
543
|
}
|
|
244
544
|
else {
|
|
245
|
-
width = (height * aspectRatio) /
|
|
545
|
+
width = (height * aspectRatio) / sourceAspectRatio;
|
|
246
546
|
}
|
|
247
547
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
548
|
+
// Apply bounds constraints and maintain aspect ratio
|
|
549
|
+
if (aspectRatio) {
|
|
550
|
+
const maxWidth = 1 - minX;
|
|
551
|
+
const maxHeight = 1 - minY;
|
|
552
|
+
const maxHeightFromWidth = (maxWidth / aspectRatio) * sourceAspectRatio;
|
|
553
|
+
const maxWidthFromHeight = (maxHeight * aspectRatio) / sourceAspectRatio;
|
|
554
|
+
if (maxHeightFromWidth <= maxHeight) {
|
|
555
|
+
// Width is the limiting factor
|
|
556
|
+
if (width > maxWidth) {
|
|
557
|
+
width = maxWidth;
|
|
558
|
+
height = maxHeightFromWidth;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
// Height is the limiting factor
|
|
563
|
+
if (height > maxHeight) {
|
|
564
|
+
height = maxHeight;
|
|
565
|
+
width = maxWidthFromHeight;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
252
568
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (
|
|
256
|
-
width =
|
|
569
|
+
else {
|
|
570
|
+
// No aspect ratio lock, just apply simple bounds
|
|
571
|
+
if (width + minX > 1) {
|
|
572
|
+
width = 1 - minX;
|
|
573
|
+
}
|
|
574
|
+
if (height + minY > 1) {
|
|
575
|
+
height = 1 - minY;
|
|
576
|
+
}
|
|
257
577
|
}
|
|
258
578
|
// Ensure final dimensions are non-negative
|
|
259
579
|
width = Math.max(0, width);
|
|
260
580
|
height = Math.max(0, height);
|
|
261
581
|
const newRect = { x: minX, y: minY, width, height };
|
|
582
|
+
isUserEditingRef.current = true;
|
|
262
583
|
setRect(newRect);
|
|
263
584
|
}
|
|
264
585
|
ev.preventDefault();
|
|
@@ -267,6 +588,7 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
267
588
|
};
|
|
268
589
|
const handleMouseUp = () => {
|
|
269
590
|
if (!rectRef.current || !rectRef.current.width || !rectRef.current.height) {
|
|
591
|
+
isUserEditingRef.current = true;
|
|
270
592
|
setRect(undefined);
|
|
271
593
|
}
|
|
272
594
|
movingRef.current = false;
|
|
@@ -304,6 +626,7 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
304
626
|
};
|
|
305
627
|
}
|
|
306
628
|
else {
|
|
629
|
+
isUserEditingRef.current = true;
|
|
307
630
|
setRect({
|
|
308
631
|
x: (ev.clientX - bounds.left) / bounds.width,
|
|
309
632
|
y: (ev.clientY - bounds.top) / bounds.height,
|
|
@@ -314,56 +637,86 @@ export function PictureCropper({ field, onClose, variantName: selectedVariantNam
|
|
|
314
637
|
setStartPos({ x: ev.clientX, y: ev.clientY });
|
|
315
638
|
window.addEventListener("mouseup", handleMouseUp);
|
|
316
639
|
};
|
|
317
|
-
if (!
|
|
640
|
+
if (!pictureValue?.variants || pictureValue.variants.length === 0) {
|
|
318
641
|
return null;
|
|
642
|
+
}
|
|
643
|
+
// Create tabs for each variant
|
|
644
|
+
const variantTabs = pictureValue.variants.map((variant, index) => ({
|
|
645
|
+
id: `variant-${index}`,
|
|
646
|
+
label: variant.name,
|
|
647
|
+
content: null, // Content will be rendered separately
|
|
648
|
+
}));
|
|
319
649
|
const imageBounds = imageRef.current?.getBoundingClientRect();
|
|
320
|
-
const
|
|
650
|
+
const sourceWidth = actualImageDimensions?.width || 0;
|
|
651
|
+
const sourceHeight = actualImageDimensions?.height || 0;
|
|
652
|
+
// For output dimensions, prefer variant dimensions if specified
|
|
653
|
+
const outputWidth = selectedVariant?.width && selectedVariant.width > 0
|
|
321
654
|
? selectedVariant.width
|
|
322
|
-
:
|
|
323
|
-
const
|
|
655
|
+
: sourceWidth;
|
|
656
|
+
const outputHeight = selectedVariant?.height && selectedVariant.height > 0
|
|
324
657
|
? selectedVariant.height
|
|
325
|
-
:
|
|
326
|
-
const scale = imageBounds &&
|
|
327
|
-
const widthPx = rect ? Math.round(rect.width *
|
|
328
|
-
const heightPx = rect ? Math.round(rect.height *
|
|
329
|
-
return (_jsx(_Fragment, { children: _jsx(Dialog, {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
658
|
+
: sourceHeight;
|
|
659
|
+
const scale = imageBounds && outputWidth > 0 ? outputWidth / imageBounds.width : 1;
|
|
660
|
+
const widthPx = rect ? Math.round(rect.width * outputWidth) : 0;
|
|
661
|
+
const heightPx = rect ? Math.round(rect.height * outputHeight) : 0;
|
|
662
|
+
return (_jsx(_Fragment, { children: _jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: "h-[75vh] max-h-none w-[75vw] max-w-none", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { children: ["Crop ", field.name] }) }), _jsxs("div", { className: "justify flex h-full flex-col", children: [_jsxs("div", { className: "flex flex-1 gap-2", children: [_jsxs("div", { className: "flex w-72 flex-col gap-3 p-6 text-sm", children: [_jsx("div", { className: "mb-4", children: _jsx(SimpleTabs, { tabs: variantTabs, activeTab: activeTabIndex, setActiveTab: setActiveTabIndex, className: "border-gray-2 border-b text-xs", tabClassName: "flex-1 text-center" }) }), selectedVariant && (_jsxs(_Fragment, { children: [_jsx("div", { className: "mb-4", children: _jsx(AspectRatioSelector, { selectedRatio: selectedAspectRatio, onRatioChange: (ratio) => {
|
|
663
|
+
setSelectedAspectRatio(ratio);
|
|
664
|
+
// Reset the crop rect to default when aspect ratio changes
|
|
665
|
+
const defaultRect = createDefaultRegion();
|
|
666
|
+
isUserEditingRef.current = true;
|
|
667
|
+
setRect(defaultRect);
|
|
668
|
+
}, lockedRatio: selectedVariant.aspectRatioLock }) }), _jsx(LabelAndValue, { label: "Image Dimensions", value: selectedVariant.width > 0 &&
|
|
669
|
+
selectedVariant.height > 0 ? (_jsxs(_Fragment, { children: [selectedVariant.width, " x ", selectedVariant.height] })) : actualImageDimensions ? (_jsxs(_Fragment, { children: [actualImageDimensions.width, " x", " ", actualImageDimensions.height, " (actual)"] })) : (_jsx("span", { className: "text-gray-500", children: "Loading..." })) }), selectedVariant.minWidth && (_jsx(LabelAndValue, { label: "Minimum Width:", value: selectedVariant.minWidth })), selectedVariant.minHeight && (_jsx(LabelAndValue, { label: "Minimum Height:", value: selectedVariant.minHeight })), rect && (_jsxs(_Fragment, { children: [_jsx(LabelAndValue, { label: "Selection", value: _jsxs(_Fragment, { children: [widthPx, " x ", heightPx] }) }), selectedVariant.minWidth &&
|
|
670
|
+
widthPx < selectedVariant.minWidth && (_jsx("div", { className: "text-red-500", children: "Minimum width not met!" })), selectedVariant.minHeight &&
|
|
671
|
+
heightPx < selectedVariant.minHeight && (_jsx("div", { className: "text-red-500", children: "Minimum height not met!" }))] }))] }))] }), _jsx("div", { className: "bg-gray-4 relative flex-1", children: selectedVariant && (_jsxs("div", { className: "absolute inset-3 flex items-center justify-center select-none", children: [!actualImageDimensions && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "text-gray-500", children: "Loading image..." }) })), !actualImageDimensions && (_jsx("img", { className: "pointer-events-none absolute opacity-0", src: selectedVariant.originalSrc ?? selectedVariant.src, onLoad: (e) => {
|
|
672
|
+
const img = e.target;
|
|
673
|
+
const dimensions = {
|
|
674
|
+
width: img.naturalWidth,
|
|
675
|
+
height: img.naturalHeight,
|
|
676
|
+
};
|
|
677
|
+
// Cache the dimensions for this image URL
|
|
678
|
+
const imgSrc = selectedVariant.originalSrc ?? selectedVariant.src;
|
|
679
|
+
if (imgSrc) {
|
|
680
|
+
imageDimensionsCache.current.set(imgSrc, dimensions);
|
|
681
|
+
}
|
|
682
|
+
setActualImageDimensions(dimensions);
|
|
683
|
+
} })), actualImageDimensions && (_jsxs("div", { ref: imageRef, className: "relative max-h-full cursor-crosshair", style: {
|
|
684
|
+
aspectRatio: `${sourceWidth}/${sourceHeight}`,
|
|
685
|
+
}, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, children: [_jsx("img", { className: "max-h-full max-w-full object-scale-down", src: selectedVariant.originalSrc ?? selectedVariant.src }), rect && (_jsxs(_Fragment, { children: [_jsx("div", { className: "pointer-events-none absolute inset-0 bg-black/40", style: {
|
|
686
|
+
clipPath: `polygon(0% 0%, 0% 100%, ${rect.x * 100}% 100%, ${rect.x * 100}% ${rect.y * 100}%, ${(rect.x + rect.width) * 100}% ${rect.y * 100}%, ${(rect.x + rect.width) * 100}% ${(rect.y + rect.height) * 100}%, ${rect.x * 100}% ${(rect.y + rect.height) * 100}%, ${rect.x * 100}% 100%, 100% 100%, 100% 0%)`,
|
|
687
|
+
} }), _jsxs("div", { className: classNames("absolute cursor-move border-2 border-dashed", isValid
|
|
688
|
+
? "border-theme-secondary"
|
|
689
|
+
: "border-red-400"), style: {
|
|
690
|
+
left: rect.x * 100 + "%",
|
|
691
|
+
top: rect.y * 100 + "%",
|
|
692
|
+
width: rect.width * 100 + "%",
|
|
693
|
+
height: rect.height * 100 + "%",
|
|
694
|
+
cursor: resizeEdge
|
|
695
|
+
? resizeEdge.length === 1
|
|
696
|
+
? `${resizeEdge}-resize`
|
|
697
|
+
: `${resizeEdge}-resize`
|
|
698
|
+
: "move",
|
|
699
|
+
}, children: [_jsx("div", { className: "absolute -top-1.5 -left-1.5 h-3 w-3 cursor-nw-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute -top-1.5 -right-1.5 h-3 w-3 cursor-ne-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute -bottom-1.5 -left-1.5 h-3 w-3 cursor-sw-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute -right-1.5 -bottom-1.5 h-3 w-3 cursor-se-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute -top-1.5 left-1/2 h-3 w-3 -translate-x-1/2 transform cursor-n-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute -bottom-1.5 left-1/2 h-3 w-3 -translate-x-1/2 transform cursor-s-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute top-1/2 -left-1.5 h-3 w-3 -translate-y-1/2 transform cursor-w-resize rounded-full bg-white/90 shadow-lg" }), _jsx("div", { className: "absolute top-1/2 -right-1.5 h-3 w-3 -translate-y-1/2 transform cursor-e-resize rounded-full bg-white/90 shadow-lg" }), rect.width > 0.05 && (_jsxs("div", { className: classNames("absolute right-1 bottom-1 rounded p-2 text-xs text-nowrap", isValid
|
|
700
|
+
? "bg-black/40 text-white"
|
|
701
|
+
: "bg-red-400/40 text-white"), children: [widthPx, " x ", heightPx] }))] })] }))] }))] })) })] }), _jsxs(DialogButtons, { children: [_jsx(Button, { onClick: onClose, variant: "ghost", children: "Cancel" }), _jsxs(Button, { onClick: () => {
|
|
702
|
+
// If there's an aspect ratio lock, restore the default centered, maximum-sized region
|
|
703
|
+
const defaultRect = createDefaultRegion();
|
|
704
|
+
isUserEditingRef.current = true;
|
|
705
|
+
setRect(defaultRect);
|
|
706
|
+
}, variant: "outline", children: [_jsx(RotateCcw, { strokeWidth: 1 }), "Reset"] }), _jsx(Button, { disabled: !isValid, onClick: () => {
|
|
707
|
+
if (pictureValue) {
|
|
708
|
+
if (field) {
|
|
709
|
+
editContext?.operations.editField({
|
|
710
|
+
field: field.descriptor,
|
|
711
|
+
rawValue: JSON.stringify(rawValue),
|
|
712
|
+
refresh: "immediate",
|
|
340
713
|
});
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
top: rect.y * 100 + "%",
|
|
346
|
-
width: widthPx / scale + "px",
|
|
347
|
-
height: heightPx / scale + "px",
|
|
348
|
-
cursor: resizeEdge
|
|
349
|
-
? resizeEdge.length === 1
|
|
350
|
-
? `${resizeEdge}-resize`
|
|
351
|
-
: `${resizeEdge}-resize`
|
|
352
|
-
: "move",
|
|
353
|
-
}, children: widthPx / scale > 50 && (_jsxs("div", { className: "absolute right-2 bottom-1 text-nowrap", style: { textShadow: "white 1px 1px" }, children: [widthPx, " x ", heightPx] })) }))] }) }) })] }), _jsxs(DialogButtons, { children: [_jsx(Button, { onClick: () => setRect(undefined), children: "Reset" }), _jsx(Button, { size: "small", disabled: !isValid, onClick: () => {
|
|
354
|
-
if (pictureValue) {
|
|
355
|
-
if (field) {
|
|
356
|
-
editContext?.operations.editField({
|
|
357
|
-
field: field.descriptor,
|
|
358
|
-
rawValue: JSON.stringify(rawValue),
|
|
359
|
-
refresh: "immediate",
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
onClose();
|
|
364
|
-
}, children: "Ok" }), _jsx(Button, { onClick: onClose, size: "small", children: "Cancel" })] })] }) }) }));
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
onClose();
|
|
717
|
+
}, children: "Save" })] })] })] }) }) }));
|
|
365
718
|
}
|
|
366
719
|
function LabelAndValue({ label, value, }) {
|
|
367
|
-
return (_jsxs("div", { children: [_jsx("div", { className: "font-
|
|
720
|
+
return (_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: label }), _jsx("div", { className: "text-xs font-light", children: value })] }));
|
|
368
721
|
}
|
|
369
722
|
//# sourceMappingURL=PictureCropper.js.map
|