my-animated-components 1.4.0 → 1.4.2
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/index.d.ts +10 -14
- package/dist/index.js +554 -98
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -458,24 +458,12 @@ interface OffcanvasHeaderProps extends BaseProps, WithChildren {
|
|
|
458
458
|
declare const OffcanvasHeader: React$1.FC<OffcanvasHeaderProps>;
|
|
459
459
|
|
|
460
460
|
type Color$5 = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
|
|
461
|
-
|
|
462
|
-
interface PaginationProps extends BaseProps, HTMLMotionProps<'nav'> {
|
|
461
|
+
interface PaginationProps extends BaseProps {
|
|
463
462
|
currentPage: number;
|
|
464
463
|
totalPages: number;
|
|
465
464
|
onPageChange: (page: number) => void;
|
|
466
465
|
color?: Color$5;
|
|
467
|
-
size?: PaginationSize;
|
|
468
466
|
motionVariant?: keyof typeof motionVariants;
|
|
469
|
-
showFirstLast?: boolean;
|
|
470
|
-
maxVisiblePages?: number;
|
|
471
|
-
showPageNumbers?: boolean;
|
|
472
|
-
previousLabel?: React$1.ReactNode;
|
|
473
|
-
nextLabel?: React$1.ReactNode;
|
|
474
|
-
firstLabel?: React$1.ReactNode;
|
|
475
|
-
lastLabel?: React$1.ReactNode;
|
|
476
|
-
buttonProps?: HTMLMotionProps<'button'>;
|
|
477
|
-
activeButtonProps?: HTMLMotionProps<'button'>;
|
|
478
|
-
disabledButtonProps?: HTMLMotionProps<'button'>;
|
|
479
467
|
}
|
|
480
468
|
declare const Pagination: React$1.FC<PaginationProps>;
|
|
481
469
|
|
|
@@ -607,4 +595,12 @@ interface TextProps extends BaseProps, WithChildren, SizeProps {
|
|
|
607
595
|
}
|
|
608
596
|
declare const Text: React$1.FC<TextProps>;
|
|
609
597
|
|
|
610
|
-
|
|
598
|
+
interface ImageEditorProps {
|
|
599
|
+
imageFile: File;
|
|
600
|
+
onSave: (file: File) => void;
|
|
601
|
+
onCancel: () => void;
|
|
602
|
+
className?: string;
|
|
603
|
+
}
|
|
604
|
+
declare const ImageEditor: React$1.FC<ImageEditorProps>;
|
|
605
|
+
|
|
606
|
+
export { Accordion, Alert, Avatar, Badge, Breadcrumb, Button, Card, CardBody, CardFooter, CardHeader, Checkbox, Container, Dropdown, DropdownItem, FileUpload, Flex, Grid, Heading, IconButton, ImageEditor, Input, List, ListItem, Modal, ModalBody, ModalFooter, ModalHeader, NavItem, Navbar, Offcanvas, OffcanvasBody, OffcanvasHeader, Pagination, ProgressBar, Radio, RangeSlider, Select, Skeleton, Slider, Stepper, Switch, Table, TableBody, TableCell, TableHead, TableRow, Tabs, Text, Textarea, Tooltip, motionVariants };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
var framerMotion = require('framer-motion');
|
|
5
|
+
var reactDom = require('react-dom');
|
|
5
6
|
|
|
6
7
|
function getVariantVisible(variantKey) {
|
|
7
8
|
const variant = motionVariants[variantKey]?.visible;
|
|
@@ -1132,24 +1133,12 @@ const OffcanvasHeader = ({ children, className = '', onClose }) => {
|
|
|
1132
1133
|
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" })))));
|
|
1133
1134
|
};
|
|
1134
1135
|
|
|
1135
|
-
const Pagination = ({ className = '', currentPage, totalPages, onPageChange, color = 'primary',
|
|
1136
|
-
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
// Calculate the range to show
|
|
1142
|
-
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
|
|
1143
|
-
let endPage = startPage + maxVisiblePages - 1;
|
|
1144
|
-
// Adjust if end page exceeds total pages
|
|
1145
|
-
if (endPage > totalPages) {
|
|
1146
|
-
endPage = totalPages;
|
|
1147
|
-
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
|
1148
|
-
}
|
|
1149
|
-
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
|
|
1150
|
-
};
|
|
1151
|
-
const pageNumbers = getPageRange();
|
|
1152
|
-
// Color classes for different states
|
|
1136
|
+
const Pagination = ({ className = '', currentPage, totalPages, onPageChange, color = 'primary', motionVariant = 'fadeIn', // Default motion variant
|
|
1137
|
+
}) => {
|
|
1138
|
+
const pageNumbers = [];
|
|
1139
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
1140
|
+
pageNumbers.push(i);
|
|
1141
|
+
}
|
|
1153
1142
|
const colorClasses = {
|
|
1154
1143
|
primary: 'text-blue-600 bg-blue-50 hover:bg-blue-100 hover:text-blue-700',
|
|
1155
1144
|
secondary: 'text-gray-600 bg-gray-50 hover:bg-gray-100 hover:text-gray-700',
|
|
@@ -1158,88 +1147,16 @@ const Pagination = ({ className = '', currentPage, totalPages, onPageChange, col
|
|
|
1158
1147
|
warning: 'text-yellow-600 bg-yellow-50 hover:bg-yellow-100 hover:text-yellow-700',
|
|
1159
1148
|
info: 'text-blue-400 bg-blue-50 hover:bg-blue-100 hover:text-blue-500',
|
|
1160
1149
|
};
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
sm: 'px-2 py-1 text-xs',
|
|
1164
|
-
md: 'px-3 py-2 text-sm',
|
|
1165
|
-
lg: 'px-4 py-3 text-base',
|
|
1166
|
-
};
|
|
1167
|
-
// Active page classes
|
|
1168
|
-
const activeClasses = {
|
|
1169
|
-
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
1170
|
-
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
|
|
1171
|
-
success: 'bg-green-600 text-white hover:bg-green-700',
|
|
1172
|
-
danger: 'bg-red-600 text-white hover:bg-red-700',
|
|
1173
|
-
warning: 'bg-yellow-600 text-white hover:bg-yellow-700',
|
|
1174
|
-
info: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
1175
|
-
};
|
|
1176
|
-
// Base button classes
|
|
1177
|
-
const baseButtonClasses = `
|
|
1178
|
-
border border-gray-300
|
|
1179
|
-
transition-colors duration-200 ease-in-out
|
|
1180
|
-
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
|
|
1181
|
-
${sizeClasses[size]}
|
|
1182
|
-
`;
|
|
1183
|
-
return (React.createElement(framerMotion.motion.nav, { className: `flex flex-wrap justify-center ${className}`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", transition: { duration: 0.5, staggerChildren: 0.05 }, ...restProps },
|
|
1184
|
-
React.createElement("ul", { className: "flex flex-wrap items-center space-x-1 md:space-x-2" },
|
|
1185
|
-
showFirstLast && (React.createElement("li", null,
|
|
1186
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(1), disabled: currentPage === 1, className: `
|
|
1187
|
-
${baseButtonClasses}
|
|
1188
|
-
${colorClasses[color]}
|
|
1189
|
-
rounded-l-lg md:rounded-lg
|
|
1190
|
-
${currentPage === 1 ? 'cursor-not-allowed opacity-50' : ''}
|
|
1191
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === 1 ? disabledButtonProps : {}), "aria-label": "Go to first page" }, firstLabel))),
|
|
1150
|
+
return (React.createElement(framerMotion.motion.nav, { className: `flex justify-center ${className}`, initial: { opacity: 0, y: -50 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.5 }, variants: motionVariants[motionVariant] },
|
|
1151
|
+
React.createElement("ul", { className: "flex items-center -space-x-px" },
|
|
1192
1152
|
React.createElement("li", null,
|
|
1193
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: `
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
hidden sm:block
|
|
1199
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === 1 ? disabledButtonProps : {}), "aria-label": "Go to previous page" }, previousLabel),
|
|
1200
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: `
|
|
1201
|
-
${baseButtonClasses}
|
|
1202
|
-
${colorClasses[color]}
|
|
1203
|
-
${!showFirstLast ? 'rounded-l-lg' : ''}
|
|
1204
|
-
${currentPage === 1 ? 'cursor-not-allowed opacity-50' : ''}
|
|
1205
|
-
sm:hidden
|
|
1206
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === 1 ? disabledButtonProps : {}), "aria-label": "Go to previous page" }, "<")),
|
|
1207
|
-
showPageNumbers && pageNumbers.map((number) => (React.createElement("li", { key: number, className: "hidden sm:block" },
|
|
1208
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(number), className: `
|
|
1209
|
-
${baseButtonClasses}
|
|
1210
|
-
${currentPage === number ? activeClasses[color] : colorClasses[color]}
|
|
1211
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === number ? activeButtonProps : {}), "aria-label": `Page ${number}`, "aria-current": currentPage === number ? 'page' : undefined }, number)))),
|
|
1212
|
-
React.createElement("li", { className: "block sm:hidden" },
|
|
1213
|
-
React.createElement(framerMotion.motion.span, { className: `
|
|
1214
|
-
${baseButtonClasses}
|
|
1215
|
-
${activeClasses[color]}
|
|
1216
|
-
flex items-center justify-center
|
|
1217
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible" },
|
|
1218
|
-
currentPage,
|
|
1219
|
-
" / ",
|
|
1220
|
-
totalPages)),
|
|
1153
|
+
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: `block px-3 py-2 ml-0 leading-tight border border-gray-300 rounded-l-lg ${colorClasses[color]} ${currentPage === 1 && 'cursor-not-allowed opacity-50'}`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible" }, "Previous")),
|
|
1154
|
+
pageNumbers.map((number) => (React.createElement("li", { key: number },
|
|
1155
|
+
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(number), className: `px-3 py-2 leading-tight border border-gray-300 ${colorClasses[color]} ${currentPage === number
|
|
1156
|
+
? 'bg-blue-100 text-blue-700 hover:bg-blue-200'
|
|
1157
|
+
: 'bg-white hover:bg-gray-100'}`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible" }, number)))),
|
|
1221
1158
|
React.createElement("li", null,
|
|
1222
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage + 1), disabled: currentPage === totalPages, className: `
|
|
1223
|
-
${baseButtonClasses}
|
|
1224
|
-
${colorClasses[color]}
|
|
1225
|
-
${!showFirstLast ? 'rounded-r-lg' : ''}
|
|
1226
|
-
${currentPage === totalPages ? 'cursor-not-allowed opacity-50' : ''}
|
|
1227
|
-
hidden sm:block
|
|
1228
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === totalPages ? disabledButtonProps : {}), "aria-label": "Go to next page" }, nextLabel),
|
|
1229
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage + 1), disabled: currentPage === totalPages, className: `
|
|
1230
|
-
${baseButtonClasses}
|
|
1231
|
-
${colorClasses[color]}
|
|
1232
|
-
${!showFirstLast ? 'rounded-r-lg' : ''}
|
|
1233
|
-
${currentPage === totalPages ? 'cursor-not-allowed opacity-50' : ''}
|
|
1234
|
-
sm:hidden
|
|
1235
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === totalPages ? disabledButtonProps : {}), "aria-label": "Go to next page" }, ">")),
|
|
1236
|
-
showFirstLast && (React.createElement("li", null,
|
|
1237
|
-
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(totalPages), disabled: currentPage === totalPages, className: `
|
|
1238
|
-
${baseButtonClasses}
|
|
1239
|
-
${colorClasses[color]}
|
|
1240
|
-
rounded-r-lg md:rounded-lg
|
|
1241
|
-
${currentPage === totalPages ? 'cursor-not-allowed opacity-50' : ''}
|
|
1242
|
-
`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible", ...buttonProps, ...(currentPage === totalPages ? disabledButtonProps : {}), "aria-label": "Go to last page" }, lastLabel))))));
|
|
1159
|
+
React.createElement(framerMotion.motion.button, { onClick: () => onPageChange(currentPage + 1), disabled: currentPage === totalPages, className: `block px-3 py-2 leading-tight border border-gray-300 rounded-r-lg ${colorClasses[color]} ${currentPage === totalPages && 'cursor-not-allowed opacity-50'}`, variants: motionVariants[motionVariant], initial: "hidden", animate: "visible" }, "Next")))));
|
|
1243
1160
|
};
|
|
1244
1161
|
|
|
1245
1162
|
const ProgressBar = ({ className = '', value, max = 100, color = 'primary', heightClass = 'h-2.5', containerBgClass = 'bg-gray-200', barBgClass, motionVariant = 'fadeIn', duration = 0.5, loop = false, showLabel = false, labelClassName = 'text-xs font-medium text-gray-700 ml-2', ...rest }) => {
|
|
@@ -1493,6 +1410,544 @@ const Text = ({ children, className = '', size = 'md', weight = 'normal', ...res
|
|
|
1493
1410
|
return (React.createElement("p", { ...rest, className: `${sizeClasses[size]} ${weightClasses[weight]} ${className}` }, children));
|
|
1494
1411
|
};
|
|
1495
1412
|
|
|
1413
|
+
// Custom SVG Icons
|
|
1414
|
+
const CropIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1415
|
+
React.createElement("path", { d: "M6 2V6H2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1416
|
+
React.createElement("path", { d: "M18 2V6H22", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1417
|
+
React.createElement("path", { d: "M18 22V18H22", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1418
|
+
React.createElement("path", { d: "M6 22V18H2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1419
|
+
React.createElement("rect", { x: "8", y: "8", width: "8", height: "8", stroke: "currentColor", strokeWidth: "2" })));
|
|
1420
|
+
const PaletteIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1421
|
+
React.createElement("path", { d: "M12 2C13.1 2 14 2.9 14 4C14 5.1 13.1 6 12 6C10.9 6 10 5.1 10 4C10 2.9 10.9 2 12 2Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1422
|
+
React.createElement("path", { d: "M20 12C21.1 12 22 12.9 22 14C22 15.1 21.1 16 20 16C18.9 16 18 15.1 18 14C18 12.9 18.9 12 20 12Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1423
|
+
React.createElement("path", { d: "M4 12C5.1 12 6 12.9 6 14C6 15.1 5.1 16 4 16C2.9 16 2 15.1 2 14C2 12.9 2.9 12 4 12Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1424
|
+
React.createElement("path", { d: "M12 20C13.1 20 14 20.9 14 22C14 23.1 13.1 24 12 24C10.9 24 10 23.1 10 22C10 20.9 10.9 20 12 20Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1425
|
+
React.createElement("path", { d: "M7 7C8.1 7 9 7.9 9 9C9 10.1 8.1 11 7 11C5.9 11 5 10.1 5 9C5 7.9 5.9 7 7 7Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1426
|
+
React.createElement("path", { d: "M17 7C18.1 7 19 7.9 19 9C19 10.1 18.1 11 17 11C15.9 11 15 10.1 15 9C15 7.9 15.9 7 17 7Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1427
|
+
React.createElement("path", { d: "M7 17C8.1 17 9 17.9 9 19C9 20.1 8.1 21 7 21C5.9 21 5 20.1 5 19C5 17.9 5.9 17 7 17Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1428
|
+
React.createElement("path", { d: "M17 17C18.1 17 19 17.9 19 19C19 20.1 18.1 21 17 21C15.9 21 15 20.1 15 19C15 17.9 15.9 17 17 17Z", stroke: "currentColor", strokeWidth: "2" })));
|
|
1429
|
+
const SparklesIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1430
|
+
React.createElement("path", { d: "M9.5 1L11 4.5L14.5 6L11 7.5L9.5 11L8 7.5L4.5 6L8 4.5L9.5 1Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1431
|
+
React.createElement("path", { d: "M17.5 8L18.5 10.5L21 11.5L18.5 12.5L17.5 15L16.5 12.5L14 11.5L16.5 10.5L17.5 8Z", stroke: "currentColor", strokeWidth: "2" }),
|
|
1432
|
+
React.createElement("path", { d: "M5.5 13L6.5 15.5L9 16.5L6.5 17.5L5.5 20L4.5 17.5L2 16.5L4.5 15.5L5.5 13Z", stroke: "currentColor", strokeWidth: "2" })));
|
|
1433
|
+
const RotateIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1434
|
+
React.createElement("path", { d: "M23 4V10H17", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1435
|
+
React.createElement("path", { d: "M1 20V14H7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1436
|
+
React.createElement("path", { d: "M3.51 9C4.01717 7.56678 4.87913 6.2854 6.01547 5.27542C7.1518 4.26543 8.52547 3.55976 10.0083 3.22426C11.4911 2.88875 13.0348 2.93434 14.4952 3.35677C15.9556 3.77921 17.2853 4.56471 18.36 5.64L23 10M1 14L5.64 18.36C6.71475 19.4353 8.04437 20.2208 9.50481 20.6432C10.9652 21.0657 12.5089 21.1113 13.9917 20.7757C15.4745 20.4402 16.8482 19.7346 17.9845 18.7246C19.1209 17.7146 19.9828 16.4332 20.49 15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1437
|
+
const FlipHorizontalIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1438
|
+
React.createElement("path", { d: "M3 12H21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1439
|
+
React.createElement("path", { d: "M12 3V21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1440
|
+
React.createElement("path", { d: "M7 7L3 12L7 17", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1441
|
+
React.createElement("path", { d: "M17 7L21 12L17 17", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1442
|
+
const FlipVerticalIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1443
|
+
React.createElement("path", { d: "M12 3V21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1444
|
+
React.createElement("path", { d: "M3 12H21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1445
|
+
React.createElement("path", { d: "M7 7L12 3L17 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1446
|
+
React.createElement("path", { d: "M7 17L12 21L17 17", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1447
|
+
const ZoomInIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1448
|
+
React.createElement("circle", { cx: "11", cy: "11", r: "8", stroke: "currentColor", strokeWidth: "2" }),
|
|
1449
|
+
React.createElement("path", { d: "M21 21L16.65 16.65", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1450
|
+
React.createElement("path", { d: "M11 8V14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1451
|
+
React.createElement("path", { d: "M8 11H14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })));
|
|
1452
|
+
const SunIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1453
|
+
React.createElement("circle", { cx: "12", cy: "12", r: "5", stroke: "currentColor", strokeWidth: "2" }),
|
|
1454
|
+
React.createElement("path", { d: "M12 1V3", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1455
|
+
React.createElement("path", { d: "M12 21V23", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1456
|
+
React.createElement("path", { d: "M4.22 4.22L5.64 5.64", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1457
|
+
React.createElement("path", { d: "M18.36 18.36L19.78 19.78", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1458
|
+
React.createElement("path", { d: "M1 12H3", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1459
|
+
React.createElement("path", { d: "M21 12H23", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1460
|
+
React.createElement("path", { d: "M4.22 19.78L5.64 18.36", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1461
|
+
React.createElement("path", { d: "M18.36 5.64L19.78 4.22", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })));
|
|
1462
|
+
const ContrastIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1463
|
+
React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
|
|
1464
|
+
React.createElement("path", { d: "M12 18C15.3137 18 18 15.3137 18 12C18 8.68629 15.3137 6 12 6C8.68629 6 6 8.68629 6 12C6 15.3137 8.68629 18 12 18Z", stroke: "currentColor", strokeWidth: "2" })));
|
|
1465
|
+
const DropletIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1466
|
+
React.createElement("path", { d: "M12 22C16.4183 22 20 18.4183 20 14C20 8 12 2 12 2C12 2 4 8 4 14C4 18.4183 7.58172 22 12 22Z", stroke: "currentColor", strokeWidth: "2" })));
|
|
1467
|
+
const ApertureIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1468
|
+
React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
|
|
1469
|
+
React.createElement("path", { d: "M14.31 8L20.05 17.94", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1470
|
+
React.createElement("path", { d: "M9.69 8L3.95 17.94", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1471
|
+
React.createElement("path", { d: "M18.62 15L5.38 15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
1472
|
+
React.createElement("path", { d: "M12 2V22", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })));
|
|
1473
|
+
const XIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1474
|
+
React.createElement("path", { d: "M18 6L6 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1475
|
+
React.createElement("path", { d: "M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1476
|
+
const CheckIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1477
|
+
React.createElement("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1478
|
+
const ChevronLeftIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1479
|
+
React.createElement("path", { d: "M15 18L9 12L15 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1480
|
+
const ChevronRightIcon = ({ size = 16, className = "" }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className: className },
|
|
1481
|
+
React.createElement("path", { d: "M9 18L15 12L9 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })));
|
|
1482
|
+
const ImageEditor = ({ imageFile, onSave, onCancel, className = "" }) => {
|
|
1483
|
+
const canvasRef = React.useRef(null);
|
|
1484
|
+
const [activeTab, setActiveTab] = React.useState('crop');
|
|
1485
|
+
const [originalImage, setOriginalImage] = React.useState(null);
|
|
1486
|
+
const [isPanelOpen, setIsPanelOpen] = React.useState(true);
|
|
1487
|
+
const [cropArea, setCropArea] = React.useState({ x: 0, y: 0, width: 100, height: 100 });
|
|
1488
|
+
const [aspectRatio, setAspectRatio] = React.useState('free');
|
|
1489
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
1490
|
+
const [dragStart, setDragStart] = React.useState({ x: 0, y: 0 });
|
|
1491
|
+
const [rotation, setRotation] = React.useState(0);
|
|
1492
|
+
const [flipH, setFlipH] = React.useState(false);
|
|
1493
|
+
const [flipV, setFlipV] = React.useState(false);
|
|
1494
|
+
const [zoom, setZoom] = React.useState(1);
|
|
1495
|
+
const [brightness, setBrightness] = React.useState(100);
|
|
1496
|
+
const [contrast, setContrast] = React.useState(100);
|
|
1497
|
+
const [saturation, setSaturation] = React.useState(100);
|
|
1498
|
+
const [blur, setBlur] = React.useState(0);
|
|
1499
|
+
const [hue, setHue] = React.useState(0);
|
|
1500
|
+
const [history, setHistory] = React.useState([]);
|
|
1501
|
+
const [historyIndex, setHistoryIndex] = React.useState(-1);
|
|
1502
|
+
const aspectRatios = [
|
|
1503
|
+
{ id: 'free', label: 'Free', ratio: null },
|
|
1504
|
+
{ id: '1:1', label: '1:1', ratio: 1 },
|
|
1505
|
+
{ id: '16:9', label: '16:9 H', ratio: 16 / 9 },
|
|
1506
|
+
{ id: '9:16', label: '9:16 V', ratio: 9 / 16 },
|
|
1507
|
+
{ id: '4:3', label: '4:3 H', ratio: 4 / 3 },
|
|
1508
|
+
{ id: '3:4', label: '3:4 V', ratio: 3 / 4 },
|
|
1509
|
+
{ id: '4:5', label: '4:5 V', ratio: 4 / 5 },
|
|
1510
|
+
{ id: '5:4', label: '5:4 H', ratio: 5 / 4 },
|
|
1511
|
+
];
|
|
1512
|
+
const filters = [
|
|
1513
|
+
{ id: 'none', name: 'Original', values: { brightness: 100, contrast: 100, saturation: 100, blur: 0, hue: 0 } },
|
|
1514
|
+
{ id: 'vivid', name: 'Vivid', values: { brightness: 110, contrast: 120, saturation: 140, blur: 0, hue: 0 } },
|
|
1515
|
+
{ id: 'warm', name: 'Warm', values: { brightness: 105, contrast: 105, saturation: 110, blur: 0, hue: 10 } },
|
|
1516
|
+
{ id: 'cool', name: 'Cool', values: { brightness: 100, contrast: 110, saturation: 90, blur: 0, hue: -10 } },
|
|
1517
|
+
{ id: 'bw', name: 'B&W', values: { brightness: 100, contrast: 120, saturation: 0, blur: 0, hue: 0 } },
|
|
1518
|
+
{ id: 'vintage', name: 'Vintage', values: { brightness: 95, contrast: 90, saturation: 80, blur: 0.5, hue: 15 } },
|
|
1519
|
+
];
|
|
1520
|
+
const getCurrentState = React.useCallback(() => ({
|
|
1521
|
+
rotation,
|
|
1522
|
+
flipH,
|
|
1523
|
+
flipV,
|
|
1524
|
+
zoom,
|
|
1525
|
+
brightness,
|
|
1526
|
+
contrast,
|
|
1527
|
+
saturation,
|
|
1528
|
+
blur,
|
|
1529
|
+
hue,
|
|
1530
|
+
cropArea
|
|
1531
|
+
}), [rotation, flipH, flipV, zoom, brightness, contrast, saturation, blur, hue, cropArea]);
|
|
1532
|
+
const saveToHistory = React.useCallback((state) => {
|
|
1533
|
+
const currentState = state || getCurrentState();
|
|
1534
|
+
const newHistory = history.slice(0, historyIndex + 1);
|
|
1535
|
+
newHistory.push(currentState);
|
|
1536
|
+
setHistory(newHistory);
|
|
1537
|
+
setHistoryIndex(newHistory.length - 1);
|
|
1538
|
+
}, [history, historyIndex, getCurrentState]);
|
|
1539
|
+
React.useCallback(() => {
|
|
1540
|
+
if (historyIndex > 0) {
|
|
1541
|
+
const prevState = history[historyIndex - 1];
|
|
1542
|
+
applyState(prevState);
|
|
1543
|
+
setHistoryIndex(historyIndex - 1);
|
|
1544
|
+
}
|
|
1545
|
+
}, [historyIndex, history]);
|
|
1546
|
+
React.useCallback(() => {
|
|
1547
|
+
if (historyIndex < history.length - 1) {
|
|
1548
|
+
const nextState = history[historyIndex + 1];
|
|
1549
|
+
applyState(nextState);
|
|
1550
|
+
setHistoryIndex(historyIndex + 1);
|
|
1551
|
+
}
|
|
1552
|
+
}, [historyIndex, history]);
|
|
1553
|
+
const applyState = React.useCallback((state) => {
|
|
1554
|
+
setRotation(state.rotation);
|
|
1555
|
+
setFlipH(state.flipH);
|
|
1556
|
+
setFlipV(state.flipV);
|
|
1557
|
+
setZoom(state.zoom);
|
|
1558
|
+
setBrightness(state.brightness);
|
|
1559
|
+
setContrast(state.contrast);
|
|
1560
|
+
setSaturation(state.saturation);
|
|
1561
|
+
setBlur(state.blur);
|
|
1562
|
+
setHue(state.hue);
|
|
1563
|
+
setCropArea(state.cropArea);
|
|
1564
|
+
}, []);
|
|
1565
|
+
// Initialize with first state
|
|
1566
|
+
React.useEffect(() => {
|
|
1567
|
+
if (imageFile && !originalImage) {
|
|
1568
|
+
const img = new Image();
|
|
1569
|
+
img.src = URL.createObjectURL(imageFile);
|
|
1570
|
+
img.onload = () => {
|
|
1571
|
+
setOriginalImage(img);
|
|
1572
|
+
const initialState = {
|
|
1573
|
+
rotation: 0,
|
|
1574
|
+
flipH: false,
|
|
1575
|
+
flipV: false,
|
|
1576
|
+
zoom: 1,
|
|
1577
|
+
brightness: 100,
|
|
1578
|
+
contrast: 100,
|
|
1579
|
+
saturation: 100,
|
|
1580
|
+
blur: 0,
|
|
1581
|
+
hue: 0,
|
|
1582
|
+
cropArea: { x: 0, y: 0, width: 100, height: 100 }
|
|
1583
|
+
};
|
|
1584
|
+
setHistory([initialState]);
|
|
1585
|
+
setHistoryIndex(0);
|
|
1586
|
+
drawCanvas(img);
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
}, [imageFile, originalImage]);
|
|
1590
|
+
// Draw canvas when state changes
|
|
1591
|
+
React.useEffect(() => {
|
|
1592
|
+
if (originalImage) {
|
|
1593
|
+
drawCanvas(originalImage, getCurrentState());
|
|
1594
|
+
}
|
|
1595
|
+
}, [rotation, flipH, flipV, zoom, brightness, contrast, saturation, blur, hue, originalImage, getCurrentState]);
|
|
1596
|
+
const drawCanvas = (img, state) => {
|
|
1597
|
+
const canvas = canvasRef.current;
|
|
1598
|
+
if (!canvas || !img)
|
|
1599
|
+
return;
|
|
1600
|
+
const ctx = canvas.getContext("2d");
|
|
1601
|
+
if (!ctx)
|
|
1602
|
+
return;
|
|
1603
|
+
const maxWidth = 1200;
|
|
1604
|
+
const maxHeight = 800;
|
|
1605
|
+
let width = img.width;
|
|
1606
|
+
let height = img.height;
|
|
1607
|
+
if (width > maxWidth || height > maxHeight) {
|
|
1608
|
+
const ratio = Math.min(maxWidth / width, maxHeight / height);
|
|
1609
|
+
width *= ratio;
|
|
1610
|
+
height *= ratio;
|
|
1611
|
+
}
|
|
1612
|
+
canvas.width = width;
|
|
1613
|
+
canvas.height = height;
|
|
1614
|
+
ctx.save();
|
|
1615
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1616
|
+
ctx.filter = `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%) blur(${blur}px) hue-rotate(${hue}deg)`;
|
|
1617
|
+
ctx.translate(canvas.width / 2, canvas.height / 2);
|
|
1618
|
+
ctx.rotate((rotation * Math.PI) / 180);
|
|
1619
|
+
ctx.scale(flipH ? -1 : 1, flipV ? -1 : 1);
|
|
1620
|
+
ctx.scale(zoom, zoom);
|
|
1621
|
+
ctx.drawImage(img, -width / 2, -height / 2, width, height);
|
|
1622
|
+
ctx.restore();
|
|
1623
|
+
};
|
|
1624
|
+
const handleCropMouseDown = (e) => {
|
|
1625
|
+
if (activeTab !== 'crop')
|
|
1626
|
+
return;
|
|
1627
|
+
const canvas = canvasRef.current;
|
|
1628
|
+
if (!canvas)
|
|
1629
|
+
return;
|
|
1630
|
+
const rect = canvas.getBoundingClientRect();
|
|
1631
|
+
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
|
1632
|
+
const y = ((e.clientY - rect.top) / rect.height) * 100;
|
|
1633
|
+
setIsDragging(true);
|
|
1634
|
+
setDragStart({ x, y });
|
|
1635
|
+
setCropArea({ x, y, width: 0, height: 0 });
|
|
1636
|
+
};
|
|
1637
|
+
const handleCropMouseMove = (e) => {
|
|
1638
|
+
if (!isDragging || activeTab !== 'crop')
|
|
1639
|
+
return;
|
|
1640
|
+
const canvas = canvasRef.current;
|
|
1641
|
+
if (!canvas)
|
|
1642
|
+
return;
|
|
1643
|
+
const rect = canvas.getBoundingClientRect();
|
|
1644
|
+
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
|
1645
|
+
const y = ((e.clientY - rect.top) / rect.height) * 100;
|
|
1646
|
+
let width = x - dragStart.x;
|
|
1647
|
+
let height = y - dragStart.y;
|
|
1648
|
+
if (aspectRatio !== 'free') {
|
|
1649
|
+
const ratio = aspectRatios.find(r => r.id === aspectRatio)?.ratio;
|
|
1650
|
+
if (ratio) {
|
|
1651
|
+
const absWidth = Math.abs(width);
|
|
1652
|
+
height = (width < 0 ? -1 : 1) * (absWidth / ratio);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
setCropArea({
|
|
1656
|
+
x: width < 0 ? x : dragStart.x,
|
|
1657
|
+
y: height < 0 ? y : dragStart.y,
|
|
1658
|
+
width: Math.abs(width),
|
|
1659
|
+
height: Math.abs(height)
|
|
1660
|
+
});
|
|
1661
|
+
};
|
|
1662
|
+
const handleCropMouseUp = () => {
|
|
1663
|
+
setIsDragging(false);
|
|
1664
|
+
};
|
|
1665
|
+
const applyCrop = () => {
|
|
1666
|
+
const canvas = canvasRef.current;
|
|
1667
|
+
if (!canvas)
|
|
1668
|
+
return;
|
|
1669
|
+
const ctx = canvas.getContext('2d');
|
|
1670
|
+
if (!ctx)
|
|
1671
|
+
return;
|
|
1672
|
+
const cropX = (cropArea.x / 100) * canvas.width;
|
|
1673
|
+
const cropY = (cropArea.y / 100) * canvas.height;
|
|
1674
|
+
const cropWidth = (cropArea.width / 100) * canvas.width;
|
|
1675
|
+
const cropHeight = (cropArea.height / 100) * canvas.height;
|
|
1676
|
+
const imageData = ctx.getImageData(cropX, cropY, cropWidth, cropHeight);
|
|
1677
|
+
canvas.width = cropWidth;
|
|
1678
|
+
canvas.height = cropHeight;
|
|
1679
|
+
ctx.putImageData(imageData, 0, 0);
|
|
1680
|
+
setCropArea({ x: 0, y: 0, width: 100, height: 100 });
|
|
1681
|
+
// Save new state after crop
|
|
1682
|
+
const newState = {
|
|
1683
|
+
...getCurrentState(),
|
|
1684
|
+
cropArea: { x: 0, y: 0, width: 100, height: 100 }
|
|
1685
|
+
};
|
|
1686
|
+
saveToHistory(newState);
|
|
1687
|
+
};
|
|
1688
|
+
const applyFilter = (filter) => {
|
|
1689
|
+
const newState = {
|
|
1690
|
+
...getCurrentState(),
|
|
1691
|
+
brightness: filter.values.brightness,
|
|
1692
|
+
contrast: filter.values.contrast,
|
|
1693
|
+
saturation: filter.values.saturation,
|
|
1694
|
+
blur: filter.values.blur,
|
|
1695
|
+
hue: filter.values.hue
|
|
1696
|
+
};
|
|
1697
|
+
applyState(newState);
|
|
1698
|
+
saveToHistory(newState);
|
|
1699
|
+
};
|
|
1700
|
+
const handleSave = () => {
|
|
1701
|
+
const canvas = canvasRef.current;
|
|
1702
|
+
if (!canvas)
|
|
1703
|
+
return;
|
|
1704
|
+
canvas.toBlob((blob) => {
|
|
1705
|
+
if (blob) {
|
|
1706
|
+
const file = new File([blob], imageFile.name, { type: 'image/png' });
|
|
1707
|
+
onSave(file);
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
};
|
|
1711
|
+
const handleSliderChange = (setter, value) => {
|
|
1712
|
+
setter(value);
|
|
1713
|
+
};
|
|
1714
|
+
const handleSliderChangeEnd = () => {
|
|
1715
|
+
saveToHistory();
|
|
1716
|
+
};
|
|
1717
|
+
const tabs = [
|
|
1718
|
+
{ id: 'crop', label: 'Crop', icon: CropIcon },
|
|
1719
|
+
{ id: 'adjust', label: 'Adjust', icon: PaletteIcon },
|
|
1720
|
+
{ id: 'filters', label: 'Filters', icon: SparklesIcon },
|
|
1721
|
+
{ id: 'transform', label: 'Transform', icon: RotateIcon }
|
|
1722
|
+
];
|
|
1723
|
+
return reactDom.createPortal(React.createElement("div", { style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', height: '100vh', width: '100vw', backgroundColor: '#0f0f0f' }, className: "dark" },
|
|
1724
|
+
React.createElement("div", { style: { flex: 1, display: 'flex', flexDirection: 'column' } },
|
|
1725
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px', borderBottom: '1px solid #2a2a2a', backgroundColor: '#1a1a1a' } },
|
|
1726
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' } },
|
|
1727
|
+
React.createElement(ApertureIcon, { className: "text-blue-500" }),
|
|
1728
|
+
React.createElement("h2", { style: { fontSize: '14px', fontWeight: 600, color: '#ffffff' } }, "Image Editor")),
|
|
1729
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '4px' } },
|
|
1730
|
+
React.createElement("div", { style: { width: '1px', height: '20px', backgroundColor: '#2a2a2a', margin: '0 4px' } }),
|
|
1731
|
+
React.createElement("button", { onClick: handleSave, style: {
|
|
1732
|
+
padding: '6px 12px',
|
|
1733
|
+
borderRadius: '8px',
|
|
1734
|
+
backgroundColor: '#3b82f6',
|
|
1735
|
+
color: '#ffffff',
|
|
1736
|
+
border: 'none',
|
|
1737
|
+
cursor: 'pointer',
|
|
1738
|
+
fontSize: '14px',
|
|
1739
|
+
fontWeight: 500,
|
|
1740
|
+
display: 'flex',
|
|
1741
|
+
alignItems: 'center',
|
|
1742
|
+
gap: '6px'
|
|
1743
|
+
}, onMouseEnter: (e) => e.currentTarget.style.opacity = '0.9', onMouseLeave: (e) => e.currentTarget.style.opacity = '1' },
|
|
1744
|
+
React.createElement(CheckIcon, null),
|
|
1745
|
+
"Save"),
|
|
1746
|
+
React.createElement("button", { onClick: onCancel, style: {
|
|
1747
|
+
padding: '8px',
|
|
1748
|
+
borderRadius: '8px',
|
|
1749
|
+
backgroundColor: 'transparent',
|
|
1750
|
+
border: 'none',
|
|
1751
|
+
cursor: 'pointer',
|
|
1752
|
+
color: '#e5e5e5'
|
|
1753
|
+
}, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'rgba(239, 68, 68, 0.1)'; e.currentTarget.style.color = '#ef4444'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'transparent'; e.currentTarget.style.color = '#e5e5e5'; }, title: "Cancel" },
|
|
1754
|
+
React.createElement(XIcon, null)))),
|
|
1755
|
+
React.createElement("div", { style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '16px', overflow: 'auto' } },
|
|
1756
|
+
React.createElement("div", { style: { position: 'relative' } },
|
|
1757
|
+
React.createElement("canvas", { ref: canvasRef, style: {
|
|
1758
|
+
maxWidth: '100%',
|
|
1759
|
+
maxHeight: 'calc(100vh - 12rem)',
|
|
1760
|
+
border: '1px solid #2a2a2a',
|
|
1761
|
+
borderRadius: '8px',
|
|
1762
|
+
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)',
|
|
1763
|
+
cursor: activeTab === 'crop' ? 'crosshair' : 'default'
|
|
1764
|
+
}, onMouseDown: handleCropMouseDown, onMouseMove: handleCropMouseMove, onMouseUp: handleCropMouseUp, onMouseLeave: handleCropMouseUp }),
|
|
1765
|
+
activeTab === 'crop' && cropArea.width > 0 && cropArea.height > 0 && (React.createElement("div", { style: {
|
|
1766
|
+
position: 'absolute',
|
|
1767
|
+
left: `${cropArea.x}%`,
|
|
1768
|
+
top: `${cropArea.y}%`,
|
|
1769
|
+
width: `${cropArea.width}%`,
|
|
1770
|
+
height: `${cropArea.height}%`,
|
|
1771
|
+
border: '2px solid #3b82f6',
|
|
1772
|
+
boxShadow: '0 0 0 9999px rgba(0,0,0,0.5)',
|
|
1773
|
+
pointerEvents: 'none'
|
|
1774
|
+
} },
|
|
1775
|
+
React.createElement("div", { style: { position: 'absolute', inset: 0, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gridTemplateRows: 'repeat(3, 1fr)' } }, [...Array(9)].map((_, i) => (React.createElement("div", { key: i, style: { border: '1px solid rgba(59, 130, 246, 0.3)' } })))))))),
|
|
1776
|
+
React.createElement("div", { style: { borderTop: '1px solid #2a2a2a', backgroundColor: '#1a1a1a', padding: '12px' } },
|
|
1777
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px', overflowX: 'auto' } }, tabs.map(tab => (React.createElement("button", { key: tab.id, onClick: () => setActiveTab(tab.id), style: {
|
|
1778
|
+
display: 'flex',
|
|
1779
|
+
alignItems: 'center',
|
|
1780
|
+
gap: '6px',
|
|
1781
|
+
padding: '6px 12px',
|
|
1782
|
+
borderRadius: '8px',
|
|
1783
|
+
backgroundColor: activeTab === tab.id ? '#3b82f6' : 'transparent',
|
|
1784
|
+
color: activeTab === tab.id ? '#ffffff' : '#e5e5e5',
|
|
1785
|
+
border: 'none',
|
|
1786
|
+
cursor: 'pointer',
|
|
1787
|
+
fontSize: '14px',
|
|
1788
|
+
fontWeight: 500,
|
|
1789
|
+
whiteSpace: 'nowrap'
|
|
1790
|
+
}, onMouseEnter: (e) => { if (activeTab !== tab.id)
|
|
1791
|
+
e.currentTarget.style.backgroundColor = '#2a2a2a'; }, onMouseLeave: (e) => { if (activeTab !== tab.id)
|
|
1792
|
+
e.currentTarget.style.backgroundColor = 'transparent'; } },
|
|
1793
|
+
React.createElement(tab.icon, null),
|
|
1794
|
+
tab.label)))))),
|
|
1795
|
+
React.createElement(framerMotion.AnimatePresence, null, isPanelOpen && (React.createElement(framerMotion.motion.div, { initial: { width: 0, opacity: 0 }, animate: { width: 280, opacity: 1 }, exit: { width: 0, opacity: 0 }, transition: { duration: 0.2 }, style: { borderLeft: '1px solid #2a2a2a', backgroundColor: '#1a1a1a', overflow: 'hidden' } },
|
|
1796
|
+
React.createElement("div", { style: { width: 280, height: '100%', overflowY: 'auto', padding: '16px' } },
|
|
1797
|
+
React.createElement(framerMotion.AnimatePresence, { mode: "wait" },
|
|
1798
|
+
activeTab === 'crop' && (React.createElement(framerMotion.motion.div, { key: "crop", initial: { opacity: 0, x: 10 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -10 }, style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
|
|
1799
|
+
React.createElement("div", null,
|
|
1800
|
+
React.createElement("label", { style: { fontSize: '12px', fontWeight: 500, marginBottom: '8px', display: 'block', color: '#e5e5e5' } }, "Aspect Ratio"),
|
|
1801
|
+
React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '6px' } }, aspectRatios.map(ratio => (React.createElement("button", { key: ratio.id, onClick: () => setAspectRatio(ratio.id), style: {
|
|
1802
|
+
padding: '6px 8px',
|
|
1803
|
+
borderRadius: '6px',
|
|
1804
|
+
fontSize: '12px',
|
|
1805
|
+
fontWeight: 500,
|
|
1806
|
+
backgroundColor: aspectRatio === ratio.id ? '#3b82f6' : '#2a2a2a',
|
|
1807
|
+
color: aspectRatio === ratio.id ? '#ffffff' : '#e5e5e5',
|
|
1808
|
+
border: 'none',
|
|
1809
|
+
cursor: 'pointer'
|
|
1810
|
+
}, onMouseEnter: (e) => { if (aspectRatio !== ratio.id)
|
|
1811
|
+
e.currentTarget.style.backgroundColor = '#3a3a3a'; }, onMouseLeave: (e) => { if (aspectRatio !== ratio.id)
|
|
1812
|
+
e.currentTarget.style.backgroundColor = '#2a2a2a'; } }, ratio.label))))),
|
|
1813
|
+
cropArea.width > 0 && (React.createElement("button", { onClick: applyCrop, style: {
|
|
1814
|
+
width: '100%',
|
|
1815
|
+
backgroundColor: '#3b82f6',
|
|
1816
|
+
color: '#ffffff',
|
|
1817
|
+
padding: '8px 12px',
|
|
1818
|
+
borderRadius: '8px',
|
|
1819
|
+
fontSize: '14px',
|
|
1820
|
+
fontWeight: 500,
|
|
1821
|
+
border: 'none',
|
|
1822
|
+
cursor: 'pointer',
|
|
1823
|
+
display: 'flex',
|
|
1824
|
+
alignItems: 'center',
|
|
1825
|
+
justifyContent: 'center',
|
|
1826
|
+
gap: '6px'
|
|
1827
|
+
}, onMouseEnter: (e) => e.currentTarget.style.opacity = '0.9', onMouseLeave: (e) => e.currentTarget.style.opacity = '1' },
|
|
1828
|
+
React.createElement(CropIcon, null),
|
|
1829
|
+
"Apply Crop")))),
|
|
1830
|
+
activeTab === 'adjust' && (React.createElement(framerMotion.motion.div, { key: "adjust", initial: { opacity: 0, x: 10 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -10 }, style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
|
|
1831
|
+
React.createElement(SliderControl, { icon: React.createElement(SunIcon, { className: "text-yellow-500" }), label: "Brightness", value: brightness, onChange: (value) => handleSliderChange(setBrightness, value), onChangeEnd: handleSliderChangeEnd, min: 0, max: 200, unit: "%" }),
|
|
1832
|
+
React.createElement(SliderControl, { icon: React.createElement(ContrastIcon, { className: "text-purple-500" }), label: "Contrast", value: contrast, onChange: (value) => handleSliderChange(setContrast, value), onChangeEnd: handleSliderChangeEnd, min: 0, max: 200, unit: "%" }),
|
|
1833
|
+
React.createElement(SliderControl, { icon: React.createElement(DropletIcon, { className: "text-blue-500" }), label: "Saturation", value: saturation, onChange: (value) => handleSliderChange(setSaturation, value), onChangeEnd: handleSliderChangeEnd, min: 0, max: 200, unit: "%" }),
|
|
1834
|
+
React.createElement(SliderControl, { icon: React.createElement(ApertureIcon, { className: "text-gray-500" }), label: "Blur", value: blur, onChange: (value) => handleSliderChange(setBlur, value), onChangeEnd: handleSliderChangeEnd, min: 0, max: 10, step: 0.1, unit: "px" }),
|
|
1835
|
+
React.createElement(SliderControl, { icon: React.createElement(PaletteIcon, { className: "text-pink-500" }), label: "Hue", value: hue, onChange: (value) => handleSliderChange(setHue, value), onChangeEnd: handleSliderChangeEnd, min: -180, max: 180, unit: "\u00B0" }))),
|
|
1836
|
+
activeTab === 'filters' && (React.createElement(framerMotion.motion.div, { key: "filters", initial: { opacity: 0, x: 10 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -10 }, style: { display: 'flex', flexDirection: 'column', gap: '6px' } }, filters.map(filter => (React.createElement("button", { key: filter.id, onClick: () => applyFilter(filter), style: {
|
|
1837
|
+
width: '100%',
|
|
1838
|
+
textAlign: 'left',
|
|
1839
|
+
padding: '8px 12px',
|
|
1840
|
+
borderRadius: '8px',
|
|
1841
|
+
backgroundColor: '#2a2a2a',
|
|
1842
|
+
color: '#e5e5e5',
|
|
1843
|
+
border: 'none',
|
|
1844
|
+
cursor: 'pointer',
|
|
1845
|
+
fontSize: '14px',
|
|
1846
|
+
fontWeight: 500
|
|
1847
|
+
}, onMouseEnter: (e) => e.currentTarget.style.backgroundColor = '#3a3a3a', onMouseLeave: (e) => e.currentTarget.style.backgroundColor = '#2a2a2a' }, filter.name))))),
|
|
1848
|
+
activeTab === 'transform' && (React.createElement(framerMotion.motion.div, { key: "transform", initial: { opacity: 0, x: 10 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -10 }, style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
|
|
1849
|
+
React.createElement(SliderControl, { icon: React.createElement(RotateIcon, null), label: "Rotation", value: rotation, onChange: (value) => handleSliderChange(setRotation, value), onChangeEnd: handleSliderChangeEnd, min: 0, max: 360, unit: "\u00B0" }),
|
|
1850
|
+
React.createElement("div", { style: { display: 'flex', gap: '8px' } },
|
|
1851
|
+
React.createElement("button", { onClick: () => {
|
|
1852
|
+
setFlipH(!flipH);
|
|
1853
|
+
handleSliderChangeEnd();
|
|
1854
|
+
}, style: {
|
|
1855
|
+
flex: 1,
|
|
1856
|
+
padding: '8px 12px',
|
|
1857
|
+
borderRadius: '8px',
|
|
1858
|
+
fontSize: '14px',
|
|
1859
|
+
fontWeight: 500,
|
|
1860
|
+
backgroundColor: flipH ? '#3b82f6' : '#2a2a2a',
|
|
1861
|
+
color: flipH ? '#ffffff' : '#e5e5e5',
|
|
1862
|
+
border: 'none',
|
|
1863
|
+
cursor: 'pointer',
|
|
1864
|
+
display: 'flex',
|
|
1865
|
+
alignItems: 'center',
|
|
1866
|
+
justifyContent: 'center',
|
|
1867
|
+
gap: '6px'
|
|
1868
|
+
}, onMouseEnter: (e) => { if (!flipH)
|
|
1869
|
+
e.currentTarget.style.backgroundColor = '#3a3a3a'; }, onMouseLeave: (e) => { if (!flipH)
|
|
1870
|
+
e.currentTarget.style.backgroundColor = '#2a2a2a'; } },
|
|
1871
|
+
React.createElement(FlipHorizontalIcon, null),
|
|
1872
|
+
"Flip H"),
|
|
1873
|
+
React.createElement("button", { onClick: () => {
|
|
1874
|
+
setFlipV(!flipV);
|
|
1875
|
+
handleSliderChangeEnd();
|
|
1876
|
+
}, style: {
|
|
1877
|
+
flex: 1,
|
|
1878
|
+
padding: '8px 12px',
|
|
1879
|
+
borderRadius: '8px',
|
|
1880
|
+
fontSize: '14px',
|
|
1881
|
+
fontWeight: 500,
|
|
1882
|
+
backgroundColor: flipV ? '#3b82f6' : '#2a2a2a',
|
|
1883
|
+
color: flipV ? '#ffffff' : '#e5e5e5',
|
|
1884
|
+
border: 'none',
|
|
1885
|
+
cursor: 'pointer',
|
|
1886
|
+
display: 'flex',
|
|
1887
|
+
alignItems: 'center',
|
|
1888
|
+
justifyContent: 'center',
|
|
1889
|
+
gap: '6px'
|
|
1890
|
+
}, onMouseEnter: (e) => { if (!flipV)
|
|
1891
|
+
e.currentTarget.style.backgroundColor = '#3a3a3a'; }, onMouseLeave: (e) => { if (!flipV)
|
|
1892
|
+
e.currentTarget.style.backgroundColor = '#2a2a2a'; } },
|
|
1893
|
+
React.createElement(FlipVerticalIcon, null),
|
|
1894
|
+
"Flip V")),
|
|
1895
|
+
React.createElement(SliderControl, { icon: React.createElement(ZoomInIcon, null), label: "Zoom", value: zoom, onChange: (value) => handleSliderChange(setZoom, value), onChangeEnd: handleSliderChangeEnd, min: 0.5, max: 3, step: 0.1, unit: "x" })))))))),
|
|
1896
|
+
React.createElement("button", { onClick: () => setIsPanelOpen(!isPanelOpen), style: {
|
|
1897
|
+
position: 'absolute',
|
|
1898
|
+
right: isPanelOpen ? '280px' : '0',
|
|
1899
|
+
top: '50%',
|
|
1900
|
+
transform: 'translateY(-50%)',
|
|
1901
|
+
backgroundColor: '#1a1a1a',
|
|
1902
|
+
border: '1px solid #2a2a2a',
|
|
1903
|
+
borderTopLeftRadius: '8px',
|
|
1904
|
+
borderBottomLeftRadius: '8px',
|
|
1905
|
+
padding: '4px',
|
|
1906
|
+
cursor: 'pointer',
|
|
1907
|
+
color: '#e5e5e5'
|
|
1908
|
+
}, onMouseEnter: (e) => e.currentTarget.style.backgroundColor = '#2a2a2a', onMouseLeave: (e) => e.currentTarget.style.backgroundColor = '#1a1a1a' }, isPanelOpen ? React.createElement(ChevronRightIcon, null) : React.createElement(ChevronLeftIcon, null))), document.body);
|
|
1909
|
+
};
|
|
1910
|
+
const SliderControl = ({ icon, label, value, onChange, onChangeEnd, min, max, step = 1, unit }) => {
|
|
1911
|
+
return (React.createElement("div", null,
|
|
1912
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '6px' } },
|
|
1913
|
+
React.createElement("label", { style: { fontSize: '12px', fontWeight: 500, display: 'flex', alignItems: 'center', gap: '6px', color: '#e5e5e5' } },
|
|
1914
|
+
icon,
|
|
1915
|
+
label),
|
|
1916
|
+
React.createElement("span", { style: { fontSize: '12px', color: '#a3a3a3' } },
|
|
1917
|
+
Math.round(value * 10) / 10,
|
|
1918
|
+
unit)),
|
|
1919
|
+
React.createElement("input", { type: "range", min: min, max: max, step: step, value: value, onChange: (e) => onChange(Number(e.target.value)), onMouseUp: onChangeEnd, onTouchEnd: onChangeEnd, style: {
|
|
1920
|
+
width: '100%',
|
|
1921
|
+
height: '6px',
|
|
1922
|
+
backgroundColor: '#2a2a2a',
|
|
1923
|
+
borderRadius: '8px',
|
|
1924
|
+
outline: 'none',
|
|
1925
|
+
cursor: 'pointer',
|
|
1926
|
+
WebkitAppearance: 'none',
|
|
1927
|
+
MozAppearance: 'none',
|
|
1928
|
+
appearance: 'none'
|
|
1929
|
+
} }),
|
|
1930
|
+
React.createElement("style", null, `
|
|
1931
|
+
input[type="range"]::-webkit-slider-thumb {
|
|
1932
|
+
-webkit-appearance: none;
|
|
1933
|
+
appearance: none;
|
|
1934
|
+
width: 14px;
|
|
1935
|
+
height: 14px;
|
|
1936
|
+
border-radius: 50%;
|
|
1937
|
+
background: #3b82f6;
|
|
1938
|
+
cursor: pointer;
|
|
1939
|
+
}
|
|
1940
|
+
input[type="range"]::-moz-range-thumb {
|
|
1941
|
+
width: 14px;
|
|
1942
|
+
height: 14px;
|
|
1943
|
+
border-radius: 50%;
|
|
1944
|
+
background: #3b82f6;
|
|
1945
|
+
cursor: pointer;
|
|
1946
|
+
border: none;
|
|
1947
|
+
}
|
|
1948
|
+
`)));
|
|
1949
|
+
};
|
|
1950
|
+
|
|
1496
1951
|
exports.Accordion = Accordion;
|
|
1497
1952
|
exports.Alert = Alert;
|
|
1498
1953
|
exports.Avatar = Avatar;
|
|
@@ -1512,6 +1967,7 @@ exports.Flex = Flex;
|
|
|
1512
1967
|
exports.Grid = Grid;
|
|
1513
1968
|
exports.Heading = Heading;
|
|
1514
1969
|
exports.IconButton = IconButton;
|
|
1970
|
+
exports.ImageEditor = ImageEditor;
|
|
1515
1971
|
exports.Input = Input;
|
|
1516
1972
|
exports.List = List;
|
|
1517
1973
|
exports.ListItem = ListItem;
|