my-animated-components 1.4.1 → 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 CHANGED
@@ -595,4 +595,12 @@ interface TextProps extends BaseProps, WithChildren, SizeProps {
595
595
  }
596
596
  declare const Text: React$1.FC<TextProps>;
597
597
 
598
- export { Accordion, Alert, Avatar, Badge, Breadcrumb, Button, Card, CardBody, CardFooter, CardHeader, Checkbox, Container, Dropdown, DropdownItem, FileUpload, Flex, Grid, Heading, IconButton, 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 };
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;
@@ -1409,6 +1410,544 @@ const Text = ({ children, className = '', size = 'md', weight = 'normal', ...res
1409
1410
  return (React.createElement("p", { ...rest, className: `${sizeClasses[size]} ${weightClasses[weight]} ${className}` }, children));
1410
1411
  };
1411
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
+
1412
1951
  exports.Accordion = Accordion;
1413
1952
  exports.Alert = Alert;
1414
1953
  exports.Avatar = Avatar;
@@ -1428,6 +1967,7 @@ exports.Flex = Flex;
1428
1967
  exports.Grid = Grid;
1429
1968
  exports.Heading = Heading;
1430
1969
  exports.IconButton = IconButton;
1970
+ exports.ImageEditor = ImageEditor;
1431
1971
  exports.Input = Input;
1432
1972
  exports.List = List;
1433
1973
  exports.ListItem = ListItem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-animated-components",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "main": "dist/index.js",