@appcorp/shadcn 1.1.24 → 1.1.25
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.
|
@@ -33,6 +33,8 @@ export type EnhancedDropzoneProps = {
|
|
|
33
33
|
onChange?: (files: File[]) => void;
|
|
34
34
|
/** Called when a remote URL preview is removed by the user. */
|
|
35
35
|
onRemoveRemote?: (url: string) => void;
|
|
36
|
+
/** Enable image resize/crop functionality. When true, opens a dialog to crop images to square aspect ratio. */
|
|
37
|
+
resize?: boolean;
|
|
36
38
|
/** `data-testid` for the root dropzone container. */
|
|
37
39
|
testIdDropzone?: string;
|
|
38
40
|
/** `data-testid` for the hidden file input element. */
|
|
@@ -60,13 +60,18 @@ var react_dropzone_1 = require("react-dropzone");
|
|
|
60
60
|
var utils_1 = require("../lib/utils");
|
|
61
61
|
var carousel_1 = require("./ui/carousel");
|
|
62
62
|
var button_1 = require("./ui/button");
|
|
63
|
+
var image_resize_dialog_1 = require("./image-resize-dialog");
|
|
63
64
|
var lucide_react_1 = require("lucide-react");
|
|
64
65
|
var EnhancedDropzone = function (_a) {
|
|
65
|
-
var id = _a.id, label = _a.label, info = _a.info, error = _a.error, accept = _a.accept, _b = _a.maxFiles, maxFiles = _b === void 0 ? 10 : _b, maxSize = _a.maxSize, minSize = _a.minSize, disabled = _a.disabled, _c = _a.value, value = _c === void 0 ? [] : _c, onChange = _a.onChange, onRemoveRemote = _a.onRemoveRemote, className = _a.className, testIdDropzone = _a.testIdDropzone, testIdInput = _a.testIdInput, testIdPrev = _a.testIdPrev, testIdNext = _a.testIdNext, testIdPreviewPrefix = _a.testIdPreviewPrefix, testIdImagePrefix = _a.testIdImagePrefix, testIdRemovePrefix = _a.testIdRemovePrefix, testIdCount = _a.testIdCount, testIdEmptyIcon = _a.testIdEmptyIcon, testIdEmptyTitle = _a.testIdEmptyTitle, testIdEmptySubtitle = _a.testIdEmptySubtitle, testIdEmptyNote = _a.testIdEmptyNote, testIdMessage = _a.testIdMessage;
|
|
66
|
+
var id = _a.id, label = _a.label, info = _a.info, error = _a.error, accept = _a.accept, _b = _a.maxFiles, maxFiles = _b === void 0 ? 10 : _b, maxSize = _a.maxSize, minSize = _a.minSize, disabled = _a.disabled, _c = _a.value, value = _c === void 0 ? [] : _c, onChange = _a.onChange, onRemoveRemote = _a.onRemoveRemote, _d = _a.resize, resize = _d === void 0 ? false : _d, className = _a.className, testIdDropzone = _a.testIdDropzone, testIdInput = _a.testIdInput, testIdPrev = _a.testIdPrev, testIdNext = _a.testIdNext, testIdPreviewPrefix = _a.testIdPreviewPrefix, testIdImagePrefix = _a.testIdImagePrefix, testIdRemovePrefix = _a.testIdRemovePrefix, testIdCount = _a.testIdCount, testIdEmptyIcon = _a.testIdEmptyIcon, testIdEmptyTitle = _a.testIdEmptyTitle, testIdEmptySubtitle = _a.testIdEmptySubtitle, testIdEmptyNote = _a.testIdEmptyNote, testIdMessage = _a.testIdMessage;
|
|
66
67
|
// Local files selected by user
|
|
67
|
-
var
|
|
68
|
+
var _e = (0, react_1.useState)([]), localFiles = _e[0], setLocalFiles = _e[1];
|
|
68
69
|
// Track object URLs created for local files
|
|
69
70
|
var localPreviewsRef = (0, react_1.useRef)(new Map());
|
|
71
|
+
// State for resize dialog
|
|
72
|
+
var _f = (0, react_1.useState)(false), resizeDialogOpen = _f[0], setResizeDialogOpen = _f[1];
|
|
73
|
+
var _g = (0, react_1.useState)(null), pendingFile = _g[0], setPendingFile = _g[1];
|
|
74
|
+
var _h = (0, react_1.useState)(""), pendingFileUrl = _h[0], setPendingFileUrl = _h[1];
|
|
70
75
|
// Create object URLs for local files synchronously to avoid loading state
|
|
71
76
|
var createObjectURLs = (0, react_1.useCallback)(function () {
|
|
72
77
|
var map = localPreviewsRef.current;
|
|
@@ -104,6 +109,14 @@ var EnhancedDropzone = function (_a) {
|
|
|
104
109
|
map.clear();
|
|
105
110
|
};
|
|
106
111
|
}, []);
|
|
112
|
+
// Cleanup pending file URL when dialog closes
|
|
113
|
+
(0, react_1.useEffect)(function () {
|
|
114
|
+
if (!resizeDialogOpen && pendingFileUrl) {
|
|
115
|
+
URL.revokeObjectURL(pendingFileUrl);
|
|
116
|
+
setPendingFileUrl("");
|
|
117
|
+
setPendingFile(null);
|
|
118
|
+
}
|
|
119
|
+
}, [resizeDialogOpen, pendingFileUrl]);
|
|
107
120
|
var dropzoneOptions = {
|
|
108
121
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
122
|
accept: (Array.isArray(accept) ? undefined : accept) || undefined,
|
|
@@ -112,15 +125,26 @@ var EnhancedDropzone = function (_a) {
|
|
|
112
125
|
minSize: minSize,
|
|
113
126
|
disabled: disabled,
|
|
114
127
|
onDrop: (0, react_1.useCallback)(function (acceptedFiles) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
var
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if (resize && acceptedFiles.length > 0) {
|
|
129
|
+
// If resize is enabled, open the resize dialog with the first file
|
|
130
|
+
var file = acceptedFiles[0];
|
|
131
|
+
var url = URL.createObjectURL(file);
|
|
132
|
+
setPendingFile(file);
|
|
133
|
+
setPendingFileUrl(url);
|
|
134
|
+
setResizeDialogOpen(true);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// If resize is disabled, add files directly
|
|
138
|
+
setLocalFiles(function (prev) {
|
|
139
|
+
var combined = __spreadArray(__spreadArray([], prev, true), acceptedFiles, true);
|
|
140
|
+
var limited = maxFiles ? combined.slice(0, maxFiles) : combined;
|
|
141
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(limited);
|
|
142
|
+
return limited;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}, [maxFiles, onChange, resize]),
|
|
122
146
|
};
|
|
123
|
-
var
|
|
147
|
+
var _j = (0, react_dropzone_1.useDropzone)(dropzoneOptions), getRootProps = _j.getRootProps, getInputProps = _j.getInputProps, isDragActive = _j.isDragActive;
|
|
124
148
|
// Remove remote URL
|
|
125
149
|
var handleRemoveRemote = (0, react_1.useCallback)(function (url) {
|
|
126
150
|
onRemoveRemote === null || onRemoveRemote === void 0 ? void 0 : onRemoveRemote(url);
|
|
@@ -133,6 +157,19 @@ var EnhancedDropzone = function (_a) {
|
|
|
133
157
|
return updated;
|
|
134
158
|
});
|
|
135
159
|
}, [onChange]);
|
|
160
|
+
// Handle crop completion from resize dialog
|
|
161
|
+
var handleCropComplete = (0, react_1.useCallback)(function (croppedImage) {
|
|
162
|
+
setLocalFiles(function (prev) {
|
|
163
|
+
var combined = __spreadArray(__spreadArray([], prev, true), [croppedImage], false);
|
|
164
|
+
var limited = maxFiles ? combined.slice(0, maxFiles) : combined;
|
|
165
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(limited);
|
|
166
|
+
return limited;
|
|
167
|
+
});
|
|
168
|
+
}, [maxFiles, onChange]);
|
|
169
|
+
// Handle resize dialog close
|
|
170
|
+
var handleResizeDialogClose = (0, react_1.useCallback)(function () {
|
|
171
|
+
setResizeDialogOpen(false);
|
|
172
|
+
}, []);
|
|
136
173
|
// Get all preview URLs (remote + local)
|
|
137
174
|
var allPreviews = __spreadArray(__spreadArray([], value.map(function (url) { return ({ type: "remote", url: url, index: 0 }); }), true), localFiles.map(function (file, index) { return ({
|
|
138
175
|
type: "local",
|
|
@@ -182,6 +219,7 @@ var EnhancedDropzone = function (_a) {
|
|
|
182
219
|
"Up to ",
|
|
183
220
|
maxFiles,
|
|
184
221
|
" files")))))),
|
|
185
|
-
(error || info) && (react_1.default.createElement("div", { className: "mt-2", "data-testid": testIdMessage }, error ? (react_1.default.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (react_1.default.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null))
|
|
222
|
+
(error || info) && (react_1.default.createElement("div", { className: "mt-2", "data-testid": testIdMessage }, error ? (react_1.default.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (react_1.default.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null)),
|
|
223
|
+
resize && pendingFile && (react_1.default.createElement(image_resize_dialog_1.ImageResizeDialog, { open: resizeDialogOpen, onClose: handleResizeDialogClose, imageUrl: pendingFileUrl, onCropComplete: handleCropComplete, fileName: pendingFile.name }))));
|
|
186
224
|
};
|
|
187
225
|
exports.EnhancedDropzone = EnhancedDropzone;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Props for `ImageResizeDialog` component.
|
|
4
|
+
*/
|
|
5
|
+
export type ImageResizeDialogProps = {
|
|
6
|
+
/** Whether the dialog is open. */
|
|
7
|
+
open: boolean;
|
|
8
|
+
/** Called when the dialog should close (user cancels or saves). */
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
/** The image URL to be resized/cropped. */
|
|
11
|
+
imageUrl: string;
|
|
12
|
+
/** Called when the user confirms the crop. Receives the cropped image as a File. */
|
|
13
|
+
onCropComplete: (croppedImage: File) => void;
|
|
14
|
+
/** Original file name for the cropped output. */
|
|
15
|
+
fileName?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* A dialog component that allows users to crop/resize an image to a square aspect ratio.
|
|
19
|
+
* Uses react-easy-crop for the cropping functionality.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ImageResizeDialog: React.FC<ImageResizeDialogProps>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
46
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
47
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
48
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
49
|
+
function step(op) {
|
|
50
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
51
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
52
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
53
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
54
|
+
switch (op[0]) {
|
|
55
|
+
case 0: case 1: t = op; break;
|
|
56
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
57
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
58
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
59
|
+
default:
|
|
60
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
61
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
62
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
63
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
64
|
+
if (t[2]) _.ops.pop();
|
|
65
|
+
_.trys.pop(); continue;
|
|
66
|
+
}
|
|
67
|
+
op = body.call(thisArg, _);
|
|
68
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
69
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
73
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
74
|
+
};
|
|
75
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
76
|
+
exports.ImageResizeDialog = void 0;
|
|
77
|
+
var react_1 = __importStar(require("react"));
|
|
78
|
+
var react_easy_crop_1 = __importDefault(require("react-easy-crop"));
|
|
79
|
+
var dialog_1 = require("./ui/dialog");
|
|
80
|
+
var button_1 = require("./ui/button");
|
|
81
|
+
var slider_1 = require("./ui/slider");
|
|
82
|
+
/**
|
|
83
|
+
* Create a cropped image from the provided source and crop area.
|
|
84
|
+
*/
|
|
85
|
+
function getCroppedImg(imageSrc_1, pixelCrop_1) {
|
|
86
|
+
return __awaiter(this, arguments, void 0, function (imageSrc, pixelCrop, fileName) {
|
|
87
|
+
var image, canvas, ctx;
|
|
88
|
+
if (fileName === void 0) { fileName = "cropped-image.jpg"; }
|
|
89
|
+
return __generator(this, function (_a) {
|
|
90
|
+
switch (_a.label) {
|
|
91
|
+
case 0: return [4 /*yield*/, createImage(imageSrc)];
|
|
92
|
+
case 1:
|
|
93
|
+
image = _a.sent();
|
|
94
|
+
canvas = document.createElement("canvas");
|
|
95
|
+
ctx = canvas.getContext("2d");
|
|
96
|
+
if (!ctx) {
|
|
97
|
+
throw new Error("No 2d context");
|
|
98
|
+
}
|
|
99
|
+
// Set canvas size to match the crop area
|
|
100
|
+
canvas.width = pixelCrop.width;
|
|
101
|
+
canvas.height = pixelCrop.height;
|
|
102
|
+
// Draw the cropped image
|
|
103
|
+
ctx.drawImage(image, pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, 0, 0, pixelCrop.width, pixelCrop.height);
|
|
104
|
+
// Convert canvas to blob and then to File
|
|
105
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
106
|
+
canvas.toBlob(function (blob) {
|
|
107
|
+
if (!blob) {
|
|
108
|
+
reject(new Error("Canvas is empty"));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
var file = new File([blob], fileName, { type: "image/jpeg" });
|
|
112
|
+
resolve(file);
|
|
113
|
+
}, "image/jpeg");
|
|
114
|
+
})];
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create an HTMLImageElement from a URL.
|
|
121
|
+
*/
|
|
122
|
+
function createImage(url) {
|
|
123
|
+
return new Promise(function (resolve, reject) {
|
|
124
|
+
var image = new Image();
|
|
125
|
+
image.addEventListener("load", function () { return resolve(image); });
|
|
126
|
+
image.addEventListener("error", function (error) { return reject(error); });
|
|
127
|
+
image.src = url;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A dialog component that allows users to crop/resize an image to a square aspect ratio.
|
|
132
|
+
* Uses react-easy-crop for the cropping functionality.
|
|
133
|
+
*/
|
|
134
|
+
var ImageResizeDialog = function (_a) {
|
|
135
|
+
var open = _a.open, onClose = _a.onClose, imageUrl = _a.imageUrl, onCropComplete = _a.onCropComplete, _b = _a.fileName, fileName = _b === void 0 ? "cropped-image.jpg" : _b;
|
|
136
|
+
var _c = (0, react_1.useState)({ x: 0, y: 0 }), crop = _c[0], setCrop = _c[1];
|
|
137
|
+
var _d = (0, react_1.useState)(1), zoom = _d[0], setZoom = _d[1];
|
|
138
|
+
var _e = (0, react_1.useState)(null), croppedAreaPixels = _e[0], setCroppedAreaPixels = _e[1];
|
|
139
|
+
var handleCropComplete = (0, react_1.useCallback)(function (_croppedArea, croppedAreaPixels) {
|
|
140
|
+
setCroppedAreaPixels(croppedAreaPixels);
|
|
141
|
+
}, []);
|
|
142
|
+
var handleSave = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
143
|
+
var croppedImage, error_1;
|
|
144
|
+
return __generator(this, function (_a) {
|
|
145
|
+
switch (_a.label) {
|
|
146
|
+
case 0:
|
|
147
|
+
if (!croppedAreaPixels)
|
|
148
|
+
return [2 /*return*/];
|
|
149
|
+
_a.label = 1;
|
|
150
|
+
case 1:
|
|
151
|
+
_a.trys.push([1, 3, , 4]);
|
|
152
|
+
return [4 /*yield*/, getCroppedImg(imageUrl, croppedAreaPixels, fileName)];
|
|
153
|
+
case 2:
|
|
154
|
+
croppedImage = _a.sent();
|
|
155
|
+
onCropComplete(croppedImage);
|
|
156
|
+
onClose();
|
|
157
|
+
return [3 /*break*/, 4];
|
|
158
|
+
case 3:
|
|
159
|
+
error_1 = _a.sent();
|
|
160
|
+
console.error("Failed to crop image:", error_1);
|
|
161
|
+
return [3 /*break*/, 4];
|
|
162
|
+
case 4: return [2 /*return*/];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}); }, [croppedAreaPixels, imageUrl, fileName, onCropComplete, onClose]);
|
|
166
|
+
return (react_1.default.createElement(dialog_1.Dialog, { open: open, onOpenChange: onClose },
|
|
167
|
+
react_1.default.createElement(dialog_1.DialogContent, { className: "max-w-3xl", "data-slot": "image-resize-dialog", "data-testid": "image-resize-dialog" },
|
|
168
|
+
react_1.default.createElement(dialog_1.DialogHeader, null,
|
|
169
|
+
react_1.default.createElement(dialog_1.DialogTitle, null, "Resize Image"),
|
|
170
|
+
react_1.default.createElement(dialog_1.DialogDescription, null, "Adjust the crop area to create a square image. Use the slider to zoom in or out.")),
|
|
171
|
+
react_1.default.createElement("div", { className: "relative h-[400px] w-full bg-muted rounded-md overflow-hidden" },
|
|
172
|
+
react_1.default.createElement(react_easy_crop_1.default, { image: imageUrl, crop: crop, zoom: zoom, aspect: 1, onCropChange: setCrop, onCropComplete: handleCropComplete, onZoomChange: setZoom })),
|
|
173
|
+
react_1.default.createElement("div", { className: "space-y-2" },
|
|
174
|
+
react_1.default.createElement("label", { className: "text-sm font-medium" }, "Zoom"),
|
|
175
|
+
react_1.default.createElement(slider_1.Slider, { value: [zoom], onValueChange: function (values) { return setZoom(values[0] || 1); }, min: 1, max: 3, step: 0.1, className: "w-full", "data-testid": "zoom-slider" })),
|
|
176
|
+
react_1.default.createElement(dialog_1.DialogFooter, null,
|
|
177
|
+
react_1.default.createElement(button_1.Button, { variant: "outline", onClick: onClose, "data-testid": "cancel-button" }, "Cancel"),
|
|
178
|
+
react_1.default.createElement(button_1.Button, { onClick: handleSave, "data-testid": "save-button" }, "Save")))));
|
|
179
|
+
};
|
|
180
|
+
exports.ImageResizeDialog = ImageResizeDialog;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appcorp/shadcn",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.25",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build:next": "next build",
|
|
6
6
|
"build:storybook": "storybook build -c .storybook -o .out",
|
|
@@ -123,6 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
"packageManager": "yarn@4.12.0",
|
|
125
125
|
"dependencies": {
|
|
126
|
-
"@tabler/icons-react": "^3.36.1"
|
|
126
|
+
"@tabler/icons-react": "^3.36.1",
|
|
127
|
+
"react-easy-crop": "^5.5.6"
|
|
127
128
|
}
|
|
128
129
|
}
|