@janovix/blocks 1.0.0-rc.2 → 1.0.0-rc.4
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.cjs +1566 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +160 -1
- package/dist/index.d.ts +160 -1
- package/dist/index.js +1532 -3
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/dist/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { useTheme } from 'next-themes';
|
|
2
|
-
import { CheckIcon, CircleIcon, ChevronRightIcon, Monitor, Sun, Moon, Languages } from 'lucide-react';
|
|
2
|
+
import { CheckIcon, CircleIcon, ChevronRightIcon, Monitor, Sun, Moon, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, X, XIcon, User, Pencil, Check, Loader2, Bell, CheckCheck, XCircle, AlertTriangle, CheckCircle, Info, Trash2 } from 'lucide-react';
|
|
3
3
|
import { AnimatePresence, motion } from 'motion/react';
|
|
4
4
|
import * as React32 from 'react';
|
|
5
|
-
import React32__default, { useLayoutEffect, useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import React32__default, { useLayoutEffect, useState, useCallback, useEffect, useRef } from 'react';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import * as ReactDOM from 'react-dom';
|
|
8
8
|
import ReactDOM__default from 'react-dom';
|
|
9
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
10
|
+
import * as TogglePrimitive from '@radix-ui/react-toggle';
|
|
11
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
12
|
+
import { Drawer as Drawer$1 } from 'vaul';
|
|
13
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
14
|
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
9
15
|
|
|
10
16
|
function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
|
|
11
17
|
return function handleEvent(event) {
|
|
@@ -9507,7 +9513,1530 @@ function LanguageSwitcher({
|
|
|
9507
9513
|
}
|
|
9508
9514
|
);
|
|
9509
9515
|
}
|
|
9516
|
+
function Slider({
|
|
9517
|
+
className,
|
|
9518
|
+
defaultValue,
|
|
9519
|
+
value,
|
|
9520
|
+
min: min2 = 0,
|
|
9521
|
+
max: max2 = 100,
|
|
9522
|
+
...props
|
|
9523
|
+
}) {
|
|
9524
|
+
const _values = React32.useMemo(
|
|
9525
|
+
() => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min2, max2],
|
|
9526
|
+
[value, defaultValue, min2, max2]
|
|
9527
|
+
);
|
|
9528
|
+
return /* @__PURE__ */ jsxs(
|
|
9529
|
+
SliderPrimitive.Root,
|
|
9530
|
+
{
|
|
9531
|
+
"data-slot": "slider",
|
|
9532
|
+
defaultValue,
|
|
9533
|
+
value,
|
|
9534
|
+
min: min2,
|
|
9535
|
+
max: max2,
|
|
9536
|
+
className: cn(
|
|
9537
|
+
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
9538
|
+
className
|
|
9539
|
+
),
|
|
9540
|
+
...props,
|
|
9541
|
+
children: [
|
|
9542
|
+
/* @__PURE__ */ jsx(
|
|
9543
|
+
SliderPrimitive.Track,
|
|
9544
|
+
{
|
|
9545
|
+
"data-slot": "slider-track",
|
|
9546
|
+
className: cn(
|
|
9547
|
+
"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
|
9548
|
+
),
|
|
9549
|
+
children: /* @__PURE__ */ jsx(
|
|
9550
|
+
SliderPrimitive.Range,
|
|
9551
|
+
{
|
|
9552
|
+
"data-slot": "slider-range",
|
|
9553
|
+
className: cn(
|
|
9554
|
+
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
9555
|
+
)
|
|
9556
|
+
}
|
|
9557
|
+
)
|
|
9558
|
+
}
|
|
9559
|
+
),
|
|
9560
|
+
Array.from({ length: _values.length }, (_, index2) => /* @__PURE__ */ jsx(
|
|
9561
|
+
SliderPrimitive.Thumb,
|
|
9562
|
+
{
|
|
9563
|
+
"data-slot": "slider-thumb",
|
|
9564
|
+
className: "border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
|
9565
|
+
},
|
|
9566
|
+
index2
|
|
9567
|
+
))
|
|
9568
|
+
]
|
|
9569
|
+
}
|
|
9570
|
+
);
|
|
9571
|
+
}
|
|
9572
|
+
var toggleVariants = cva(
|
|
9573
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
|
9574
|
+
{
|
|
9575
|
+
variants: {
|
|
9576
|
+
variant: {
|
|
9577
|
+
default: "bg-transparent",
|
|
9578
|
+
outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
|
|
9579
|
+
},
|
|
9580
|
+
size: {
|
|
9581
|
+
default: "h-9 px-2 min-w-9",
|
|
9582
|
+
sm: "h-8 px-1.5 min-w-8",
|
|
9583
|
+
lg: "h-10 px-2.5 min-w-10"
|
|
9584
|
+
}
|
|
9585
|
+
},
|
|
9586
|
+
defaultVariants: {
|
|
9587
|
+
variant: "default",
|
|
9588
|
+
size: "default"
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
);
|
|
9592
|
+
function Toggle({
|
|
9593
|
+
className,
|
|
9594
|
+
variant,
|
|
9595
|
+
size: size4,
|
|
9596
|
+
...props
|
|
9597
|
+
}) {
|
|
9598
|
+
return /* @__PURE__ */ jsx(
|
|
9599
|
+
TogglePrimitive.Root,
|
|
9600
|
+
{
|
|
9601
|
+
"data-slot": "toggle",
|
|
9602
|
+
className: cn(toggleVariants({ variant, size: size4, className })),
|
|
9603
|
+
...props
|
|
9604
|
+
}
|
|
9605
|
+
);
|
|
9606
|
+
}
|
|
9607
|
+
function AvatarEditor({
|
|
9608
|
+
value,
|
|
9609
|
+
onChange,
|
|
9610
|
+
size: size4 = 280,
|
|
9611
|
+
showGrid: initialShowGrid = false,
|
|
9612
|
+
outputSize = 256,
|
|
9613
|
+
outputFormat = "png",
|
|
9614
|
+
outputQuality = 0.92,
|
|
9615
|
+
className,
|
|
9616
|
+
controlSize = "default",
|
|
9617
|
+
defaultImage,
|
|
9618
|
+
initials: _initials
|
|
9619
|
+
}) {
|
|
9620
|
+
const canvasRef = useRef(null);
|
|
9621
|
+
const fileInputRef = useRef(null);
|
|
9622
|
+
const containerRef = useRef(null);
|
|
9623
|
+
const imageSource = value ?? defaultImage;
|
|
9624
|
+
const [image, setImage] = useState(null);
|
|
9625
|
+
const [imageLoaded, setImageLoaded] = useState(false);
|
|
9626
|
+
const [zoom, setZoom] = useState(1);
|
|
9627
|
+
const [rotation, setRotation] = useState(0);
|
|
9628
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
9629
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
9630
|
+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
|
9631
|
+
const [showGrid, setShowGrid] = useState(initialShowGrid);
|
|
9632
|
+
useEffect(() => {
|
|
9633
|
+
if (imageSource && !imageSource.startsWith("data:")) {
|
|
9634
|
+
const img = new Image();
|
|
9635
|
+
img.crossOrigin = "anonymous";
|
|
9636
|
+
img.onload = () => {
|
|
9637
|
+
setImage(img);
|
|
9638
|
+
setImageLoaded(true);
|
|
9639
|
+
setZoom(1);
|
|
9640
|
+
setRotation(0);
|
|
9641
|
+
setPosition({ x: 0, y: 0 });
|
|
9642
|
+
};
|
|
9643
|
+
img.src = imageSource;
|
|
9644
|
+
}
|
|
9645
|
+
}, [imageSource]);
|
|
9646
|
+
const generateOutput = useCallback(() => {
|
|
9647
|
+
if (!image) return null;
|
|
9648
|
+
const outputCanvas = document.createElement("canvas");
|
|
9649
|
+
outputCanvas.width = outputSize;
|
|
9650
|
+
outputCanvas.height = outputSize;
|
|
9651
|
+
const ctx = outputCanvas.getContext("2d");
|
|
9652
|
+
if (!ctx) return null;
|
|
9653
|
+
ctx.imageSmoothingEnabled = true;
|
|
9654
|
+
ctx.imageSmoothingQuality = "high";
|
|
9655
|
+
if (outputFormat === "jpeg") {
|
|
9656
|
+
ctx.fillStyle = "#ffffff";
|
|
9657
|
+
ctx.fillRect(0, 0, outputSize, outputSize);
|
|
9658
|
+
}
|
|
9659
|
+
ctx.save();
|
|
9660
|
+
ctx.translate(outputSize / 2, outputSize / 2);
|
|
9661
|
+
ctx.rotate(rotation * Math.PI / 180);
|
|
9662
|
+
const scale = zoom;
|
|
9663
|
+
const imgAspect = image.width / image.height;
|
|
9664
|
+
let drawWidth, drawHeight;
|
|
9665
|
+
if (imgAspect > 1) {
|
|
9666
|
+
drawHeight = outputSize * scale;
|
|
9667
|
+
drawWidth = drawHeight * imgAspect;
|
|
9668
|
+
} else {
|
|
9669
|
+
drawWidth = outputSize * scale;
|
|
9670
|
+
drawHeight = drawWidth / imgAspect;
|
|
9671
|
+
}
|
|
9672
|
+
const positionScale = outputSize / size4;
|
|
9673
|
+
const scaledPosX = position.x * positionScale;
|
|
9674
|
+
const scaledPosY = position.y * positionScale;
|
|
9675
|
+
ctx.drawImage(
|
|
9676
|
+
image,
|
|
9677
|
+
-drawWidth / 2 + scaledPosX,
|
|
9678
|
+
-drawHeight / 2 + scaledPosY,
|
|
9679
|
+
drawWidth,
|
|
9680
|
+
drawHeight
|
|
9681
|
+
);
|
|
9682
|
+
ctx.restore();
|
|
9683
|
+
const mimeType = `image/${outputFormat}`;
|
|
9684
|
+
return outputCanvas.toDataURL(mimeType, outputQuality);
|
|
9685
|
+
}, [
|
|
9686
|
+
image,
|
|
9687
|
+
outputSize,
|
|
9688
|
+
outputFormat,
|
|
9689
|
+
outputQuality,
|
|
9690
|
+
zoom,
|
|
9691
|
+
rotation,
|
|
9692
|
+
position,
|
|
9693
|
+
size4
|
|
9694
|
+
]);
|
|
9695
|
+
useEffect(() => {
|
|
9696
|
+
if (!canvasRef.current || !image || !imageLoaded) return;
|
|
9697
|
+
const canvas = canvasRef.current;
|
|
9698
|
+
const ctx = canvas.getContext("2d");
|
|
9699
|
+
if (!ctx) return;
|
|
9700
|
+
const displaySize = size4;
|
|
9701
|
+
canvas.width = displaySize;
|
|
9702
|
+
canvas.height = displaySize;
|
|
9703
|
+
ctx.clearRect(0, 0, displaySize, displaySize);
|
|
9704
|
+
ctx.fillStyle = "#1a1a2e";
|
|
9705
|
+
ctx.fillRect(0, 0, displaySize, displaySize);
|
|
9706
|
+
ctx.save();
|
|
9707
|
+
ctx.translate(displaySize / 2, displaySize / 2);
|
|
9708
|
+
ctx.rotate(rotation * Math.PI / 180);
|
|
9709
|
+
const scale = zoom;
|
|
9710
|
+
const imgAspect = image.width / image.height;
|
|
9711
|
+
let drawWidth, drawHeight;
|
|
9712
|
+
if (imgAspect > 1) {
|
|
9713
|
+
drawHeight = displaySize * scale;
|
|
9714
|
+
drawWidth = drawHeight * imgAspect;
|
|
9715
|
+
} else {
|
|
9716
|
+
drawWidth = displaySize * scale;
|
|
9717
|
+
drawHeight = drawWidth / imgAspect;
|
|
9718
|
+
}
|
|
9719
|
+
ctx.drawImage(
|
|
9720
|
+
image,
|
|
9721
|
+
-drawWidth / 2 + position.x,
|
|
9722
|
+
-drawHeight / 2 + position.y,
|
|
9723
|
+
drawWidth,
|
|
9724
|
+
drawHeight
|
|
9725
|
+
);
|
|
9726
|
+
ctx.restore();
|
|
9727
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
9728
|
+
ctx.beginPath();
|
|
9729
|
+
ctx.arc(
|
|
9730
|
+
displaySize / 2,
|
|
9731
|
+
displaySize / 2,
|
|
9732
|
+
displaySize / 2 - 4,
|
|
9733
|
+
0,
|
|
9734
|
+
Math.PI * 2
|
|
9735
|
+
);
|
|
9736
|
+
ctx.fill();
|
|
9737
|
+
ctx.globalCompositeOperation = "source-over";
|
|
9738
|
+
ctx.strokeStyle = "hsl(var(--primary))";
|
|
9739
|
+
ctx.lineWidth = 3;
|
|
9740
|
+
ctx.beginPath();
|
|
9741
|
+
ctx.arc(
|
|
9742
|
+
displaySize / 2,
|
|
9743
|
+
displaySize / 2,
|
|
9744
|
+
displaySize / 2 - 2,
|
|
9745
|
+
0,
|
|
9746
|
+
Math.PI * 2
|
|
9747
|
+
);
|
|
9748
|
+
ctx.stroke();
|
|
9749
|
+
const dataUrl = generateOutput();
|
|
9750
|
+
if (dataUrl) {
|
|
9751
|
+
onChange?.(dataUrl);
|
|
9752
|
+
}
|
|
9753
|
+
}, [
|
|
9754
|
+
image,
|
|
9755
|
+
imageLoaded,
|
|
9756
|
+
zoom,
|
|
9757
|
+
rotation,
|
|
9758
|
+
position,
|
|
9759
|
+
size4,
|
|
9760
|
+
generateOutput,
|
|
9761
|
+
onChange
|
|
9762
|
+
]);
|
|
9763
|
+
const resetTransforms = useCallback(() => {
|
|
9764
|
+
setZoom(1);
|
|
9765
|
+
setRotation(0);
|
|
9766
|
+
setPosition({ x: 0, y: 0 });
|
|
9767
|
+
}, []);
|
|
9768
|
+
const handleFileSelect = useCallback(
|
|
9769
|
+
(e) => {
|
|
9770
|
+
const file = e.target.files?.[0];
|
|
9771
|
+
if (!file) return;
|
|
9772
|
+
const reader = new FileReader();
|
|
9773
|
+
reader.onload = (event) => {
|
|
9774
|
+
const img = new Image();
|
|
9775
|
+
img.crossOrigin = "anonymous";
|
|
9776
|
+
img.onload = () => {
|
|
9777
|
+
setImage(img);
|
|
9778
|
+
setImageLoaded(true);
|
|
9779
|
+
resetTransforms();
|
|
9780
|
+
};
|
|
9781
|
+
img.src = event.target?.result;
|
|
9782
|
+
};
|
|
9783
|
+
reader.readAsDataURL(file);
|
|
9784
|
+
},
|
|
9785
|
+
[resetTransforms]
|
|
9786
|
+
);
|
|
9787
|
+
const handleMouseDown = useCallback(
|
|
9788
|
+
(e) => {
|
|
9789
|
+
if (!imageLoaded) return;
|
|
9790
|
+
setIsDragging(true);
|
|
9791
|
+
setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });
|
|
9792
|
+
},
|
|
9793
|
+
[imageLoaded, position]
|
|
9794
|
+
);
|
|
9795
|
+
const handleMouseMove = useCallback(
|
|
9796
|
+
(e) => {
|
|
9797
|
+
if (!isDragging) return;
|
|
9798
|
+
setPosition({
|
|
9799
|
+
x: e.clientX - dragStart.x,
|
|
9800
|
+
y: e.clientY - dragStart.y
|
|
9801
|
+
});
|
|
9802
|
+
},
|
|
9803
|
+
[isDragging, dragStart]
|
|
9804
|
+
);
|
|
9805
|
+
const handleMouseUp = useCallback(() => {
|
|
9806
|
+
setIsDragging(false);
|
|
9807
|
+
}, []);
|
|
9808
|
+
const handleTouchStart = useCallback(
|
|
9809
|
+
(e) => {
|
|
9810
|
+
if (!imageLoaded) return;
|
|
9811
|
+
const touch = e.touches[0];
|
|
9812
|
+
setIsDragging(true);
|
|
9813
|
+
setDragStart({
|
|
9814
|
+
x: touch.clientX - position.x,
|
|
9815
|
+
y: touch.clientY - position.y
|
|
9816
|
+
});
|
|
9817
|
+
},
|
|
9818
|
+
[imageLoaded, position]
|
|
9819
|
+
);
|
|
9820
|
+
const handleTouchMove = useCallback(
|
|
9821
|
+
(e) => {
|
|
9822
|
+
if (!isDragging) return;
|
|
9823
|
+
const touch = e.touches[0];
|
|
9824
|
+
setPosition({
|
|
9825
|
+
x: touch.clientX - dragStart.x,
|
|
9826
|
+
y: touch.clientY - dragStart.y
|
|
9827
|
+
});
|
|
9828
|
+
},
|
|
9829
|
+
[isDragging, dragStart]
|
|
9830
|
+
);
|
|
9831
|
+
const handleDiscard = useCallback(() => {
|
|
9832
|
+
setImage(null);
|
|
9833
|
+
setImageLoaded(false);
|
|
9834
|
+
resetTransforms();
|
|
9835
|
+
if (fileInputRef.current) {
|
|
9836
|
+
fileInputRef.current.value = "";
|
|
9837
|
+
}
|
|
9838
|
+
onChange?.(null);
|
|
9839
|
+
}, [resetTransforms, onChange]);
|
|
9840
|
+
const handleZoomIn = () => setZoom((z) => Math.min(z + 0.1, 3));
|
|
9841
|
+
const handleZoomOut = () => setZoom((z) => Math.max(z - 0.1, 0.5));
|
|
9842
|
+
const handleRotateLeft = () => setRotation((r2) => r2 - 15);
|
|
9843
|
+
const handleRotateRight = () => setRotation((r2) => r2 + 15);
|
|
9844
|
+
const isLarge = controlSize === "large";
|
|
9845
|
+
const buttonSize = isLarge ? "h-12 w-12" : "h-8 w-8";
|
|
9846
|
+
const iconSize = isLarge ? "w-6 h-6" : "w-4 h-4";
|
|
9847
|
+
const smallIconSize = isLarge ? "w-5 h-5" : "w-3 h-3";
|
|
9848
|
+
const textSize = isLarge ? "text-sm" : "text-xs";
|
|
9849
|
+
const uploadIconSize = isLarge ? "w-12 h-12" : "w-8 h-8";
|
|
9850
|
+
const uploadContainerSize = isLarge ? "w-24 h-24" : "w-16 h-16";
|
|
9851
|
+
const gapSize = isLarge ? "gap-6" : "gap-4";
|
|
9852
|
+
const controlGap = isLarge ? "gap-3" : "gap-2";
|
|
9853
|
+
const sliderHeight = isLarge ? "[&_[role=slider]]:h-6 [&_[role=slider]]:w-6" : "";
|
|
9854
|
+
return /* @__PURE__ */ jsxs(
|
|
9855
|
+
"div",
|
|
9856
|
+
{
|
|
9857
|
+
className: cn("flex flex-col", gapSize, className),
|
|
9858
|
+
style: { width: size4 },
|
|
9859
|
+
children: [
|
|
9860
|
+
/* @__PURE__ */ jsxs(
|
|
9861
|
+
"div",
|
|
9862
|
+
{
|
|
9863
|
+
ref: containerRef,
|
|
9864
|
+
className: "relative bg-muted rounded-2xl overflow-hidden",
|
|
9865
|
+
style: { width: size4, height: size4 },
|
|
9866
|
+
children: [
|
|
9867
|
+
!imageLoaded && /* @__PURE__ */ jsxs(
|
|
9868
|
+
"button",
|
|
9869
|
+
{
|
|
9870
|
+
onClick: () => fileInputRef.current?.click(),
|
|
9871
|
+
className: cn(
|
|
9872
|
+
"absolute inset-0 z-10 flex flex-col items-center justify-center text-muted-foreground hover:text-foreground hover:bg-muted/80 transition-colors cursor-pointer",
|
|
9873
|
+
isLarge ? "gap-4" : "gap-3"
|
|
9874
|
+
),
|
|
9875
|
+
"aria-label": "Upload image",
|
|
9876
|
+
type: "button",
|
|
9877
|
+
children: [
|
|
9878
|
+
/* @__PURE__ */ jsx(
|
|
9879
|
+
"div",
|
|
9880
|
+
{
|
|
9881
|
+
className: cn(
|
|
9882
|
+
"rounded-full bg-primary/10 flex items-center justify-center",
|
|
9883
|
+
uploadContainerSize
|
|
9884
|
+
),
|
|
9885
|
+
children: /* @__PURE__ */ jsx(Upload, { className: cn("text-primary", uploadIconSize) })
|
|
9886
|
+
}
|
|
9887
|
+
),
|
|
9888
|
+
/* @__PURE__ */ jsx(
|
|
9889
|
+
"span",
|
|
9890
|
+
{
|
|
9891
|
+
className: cn("font-medium", isLarge ? "text-base" : "text-sm"),
|
|
9892
|
+
children: "Click to upload"
|
|
9893
|
+
}
|
|
9894
|
+
),
|
|
9895
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-muted-foreground", textSize), children: "PNG, JPG up to 10MB" })
|
|
9896
|
+
]
|
|
9897
|
+
}
|
|
9898
|
+
),
|
|
9899
|
+
/* @__PURE__ */ jsx(
|
|
9900
|
+
"canvas",
|
|
9901
|
+
{
|
|
9902
|
+
ref: canvasRef,
|
|
9903
|
+
className: cn(
|
|
9904
|
+
"absolute inset-0",
|
|
9905
|
+
imageLoaded ? "cursor-move" : "pointer-events-none opacity-0"
|
|
9906
|
+
),
|
|
9907
|
+
style: { width: size4, height: size4 },
|
|
9908
|
+
onMouseDown: handleMouseDown,
|
|
9909
|
+
onMouseMove: handleMouseMove,
|
|
9910
|
+
onMouseUp: handleMouseUp,
|
|
9911
|
+
onMouseLeave: handleMouseUp,
|
|
9912
|
+
onTouchStart: handleTouchStart,
|
|
9913
|
+
onTouchMove: handleTouchMove,
|
|
9914
|
+
onTouchEnd: handleMouseUp
|
|
9915
|
+
}
|
|
9916
|
+
),
|
|
9917
|
+
showGrid && imageLoaded && /* @__PURE__ */ jsx(
|
|
9918
|
+
"div",
|
|
9919
|
+
{
|
|
9920
|
+
className: "absolute inset-0 pointer-events-none",
|
|
9921
|
+
style: { width: size4, height: size4 },
|
|
9922
|
+
children: /* @__PURE__ */ jsxs("svg", { width: size4, height: size4, className: "opacity-30", children: [
|
|
9923
|
+
/* @__PURE__ */ jsx(
|
|
9924
|
+
"line",
|
|
9925
|
+
{
|
|
9926
|
+
x1: size4 / 3,
|
|
9927
|
+
y1: 0,
|
|
9928
|
+
x2: size4 / 3,
|
|
9929
|
+
y2: size4,
|
|
9930
|
+
stroke: "white",
|
|
9931
|
+
strokeWidth: "1"
|
|
9932
|
+
}
|
|
9933
|
+
),
|
|
9934
|
+
/* @__PURE__ */ jsx(
|
|
9935
|
+
"line",
|
|
9936
|
+
{
|
|
9937
|
+
x1: size4 * 2 / 3,
|
|
9938
|
+
y1: 0,
|
|
9939
|
+
x2: size4 * 2 / 3,
|
|
9940
|
+
y2: size4,
|
|
9941
|
+
stroke: "white",
|
|
9942
|
+
strokeWidth: "1"
|
|
9943
|
+
}
|
|
9944
|
+
),
|
|
9945
|
+
/* @__PURE__ */ jsx(
|
|
9946
|
+
"line",
|
|
9947
|
+
{
|
|
9948
|
+
x1: 0,
|
|
9949
|
+
y1: size4 / 3,
|
|
9950
|
+
x2: size4,
|
|
9951
|
+
y2: size4 / 3,
|
|
9952
|
+
stroke: "white",
|
|
9953
|
+
strokeWidth: "1"
|
|
9954
|
+
}
|
|
9955
|
+
),
|
|
9956
|
+
/* @__PURE__ */ jsx(
|
|
9957
|
+
"line",
|
|
9958
|
+
{
|
|
9959
|
+
x1: 0,
|
|
9960
|
+
y1: size4 * 2 / 3,
|
|
9961
|
+
x2: size4,
|
|
9962
|
+
y2: size4 * 2 / 3,
|
|
9963
|
+
stroke: "white",
|
|
9964
|
+
strokeWidth: "1"
|
|
9965
|
+
}
|
|
9966
|
+
),
|
|
9967
|
+
/* @__PURE__ */ jsx(
|
|
9968
|
+
"line",
|
|
9969
|
+
{
|
|
9970
|
+
x1: size4 / 2 - 10,
|
|
9971
|
+
y1: size4 / 2,
|
|
9972
|
+
x2: size4 / 2 + 10,
|
|
9973
|
+
y2: size4 / 2,
|
|
9974
|
+
stroke: "white",
|
|
9975
|
+
strokeWidth: "1"
|
|
9976
|
+
}
|
|
9977
|
+
),
|
|
9978
|
+
/* @__PURE__ */ jsx(
|
|
9979
|
+
"line",
|
|
9980
|
+
{
|
|
9981
|
+
x1: size4 / 2,
|
|
9982
|
+
y1: size4 / 2 - 10,
|
|
9983
|
+
x2: size4 / 2,
|
|
9984
|
+
y2: size4 / 2 + 10,
|
|
9985
|
+
stroke: "white",
|
|
9986
|
+
strokeWidth: "1"
|
|
9987
|
+
}
|
|
9988
|
+
)
|
|
9989
|
+
] })
|
|
9990
|
+
}
|
|
9991
|
+
),
|
|
9992
|
+
imageLoaded && isDragging && /* @__PURE__ */ jsxs(
|
|
9993
|
+
"div",
|
|
9994
|
+
{
|
|
9995
|
+
className: cn(
|
|
9996
|
+
"absolute top-2 left-1/2 -translate-x-1/2 bg-black/60 rounded text-white flex items-center",
|
|
9997
|
+
isLarge ? "px-3 py-2 text-sm gap-2" : "px-2 py-1 text-xs gap-1"
|
|
9998
|
+
),
|
|
9999
|
+
children: [
|
|
10000
|
+
/* @__PURE__ */ jsx(Move, { className: smallIconSize }),
|
|
10001
|
+
"Dragging"
|
|
10002
|
+
]
|
|
10003
|
+
}
|
|
10004
|
+
)
|
|
10005
|
+
]
|
|
10006
|
+
}
|
|
10007
|
+
),
|
|
10008
|
+
imageLoaded && /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", isLarge && "space-y-5"), children: [
|
|
10009
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center", controlGap), children: [
|
|
10010
|
+
/* @__PURE__ */ jsx(
|
|
10011
|
+
Button,
|
|
10012
|
+
{
|
|
10013
|
+
variant: "ghost",
|
|
10014
|
+
size: "icon",
|
|
10015
|
+
className: cn(buttonSize, "shrink-0"),
|
|
10016
|
+
onClick: handleZoomOut,
|
|
10017
|
+
"aria-label": "Zoom out",
|
|
10018
|
+
type: "button",
|
|
10019
|
+
children: /* @__PURE__ */ jsx(ZoomOut, { className: iconSize })
|
|
10020
|
+
}
|
|
10021
|
+
),
|
|
10022
|
+
/* @__PURE__ */ jsx(
|
|
10023
|
+
Slider,
|
|
10024
|
+
{
|
|
10025
|
+
value: [zoom],
|
|
10026
|
+
onValueChange: ([v]) => setZoom(v),
|
|
10027
|
+
min: 0.5,
|
|
10028
|
+
max: 3,
|
|
10029
|
+
step: 0.01,
|
|
10030
|
+
className: cn("flex-1", sliderHeight),
|
|
10031
|
+
"aria-label": "Zoom level"
|
|
10032
|
+
}
|
|
10033
|
+
),
|
|
10034
|
+
/* @__PURE__ */ jsx(
|
|
10035
|
+
Button,
|
|
10036
|
+
{
|
|
10037
|
+
variant: "ghost",
|
|
10038
|
+
size: "icon",
|
|
10039
|
+
className: cn(buttonSize, "shrink-0"),
|
|
10040
|
+
onClick: handleZoomIn,
|
|
10041
|
+
"aria-label": "Zoom in",
|
|
10042
|
+
type: "button",
|
|
10043
|
+
children: /* @__PURE__ */ jsx(ZoomIn, { className: iconSize })
|
|
10044
|
+
}
|
|
10045
|
+
)
|
|
10046
|
+
] }),
|
|
10047
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between", controlGap), children: [
|
|
10048
|
+
/* @__PURE__ */ jsxs(
|
|
10049
|
+
"div",
|
|
10050
|
+
{
|
|
10051
|
+
className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
|
|
10052
|
+
children: [
|
|
10053
|
+
/* @__PURE__ */ jsx(
|
|
10054
|
+
Button,
|
|
10055
|
+
{
|
|
10056
|
+
variant: "ghost",
|
|
10057
|
+
size: "icon",
|
|
10058
|
+
className: buttonSize,
|
|
10059
|
+
onClick: handleRotateLeft,
|
|
10060
|
+
"aria-label": "Rotate left 15 degrees",
|
|
10061
|
+
type: "button",
|
|
10062
|
+
children: /* @__PURE__ */ jsx(RotateCcw, { className: iconSize })
|
|
10063
|
+
}
|
|
10064
|
+
),
|
|
10065
|
+
/* @__PURE__ */ jsxs(
|
|
10066
|
+
"span",
|
|
10067
|
+
{
|
|
10068
|
+
className: cn(
|
|
10069
|
+
"text-muted-foreground text-center tabular-nums",
|
|
10070
|
+
textSize,
|
|
10071
|
+
isLarge ? "w-16" : "w-12"
|
|
10072
|
+
),
|
|
10073
|
+
children: [
|
|
10074
|
+
rotation,
|
|
10075
|
+
"\xB0"
|
|
10076
|
+
]
|
|
10077
|
+
}
|
|
10078
|
+
),
|
|
10079
|
+
/* @__PURE__ */ jsx(
|
|
10080
|
+
Button,
|
|
10081
|
+
{
|
|
10082
|
+
variant: "ghost",
|
|
10083
|
+
size: "icon",
|
|
10084
|
+
className: buttonSize,
|
|
10085
|
+
onClick: handleRotateRight,
|
|
10086
|
+
"aria-label": "Rotate right 15 degrees",
|
|
10087
|
+
type: "button",
|
|
10088
|
+
children: /* @__PURE__ */ jsx(RotateCw, { className: iconSize })
|
|
10089
|
+
}
|
|
10090
|
+
)
|
|
10091
|
+
]
|
|
10092
|
+
}
|
|
10093
|
+
),
|
|
10094
|
+
/* @__PURE__ */ jsxs(
|
|
10095
|
+
"div",
|
|
10096
|
+
{
|
|
10097
|
+
className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
|
|
10098
|
+
children: [
|
|
10099
|
+
/* @__PURE__ */ jsx(
|
|
10100
|
+
Toggle,
|
|
10101
|
+
{
|
|
10102
|
+
pressed: showGrid,
|
|
10103
|
+
onPressedChange: setShowGrid,
|
|
10104
|
+
size: "sm",
|
|
10105
|
+
className: cn(buttonSize, "p-0"),
|
|
10106
|
+
"aria-label": "Toggle grid overlay",
|
|
10107
|
+
children: /* @__PURE__ */ jsx(Grid3X3, { className: iconSize })
|
|
10108
|
+
}
|
|
10109
|
+
),
|
|
10110
|
+
/* @__PURE__ */ jsx(
|
|
10111
|
+
Button,
|
|
10112
|
+
{
|
|
10113
|
+
variant: "ghost",
|
|
10114
|
+
size: "icon",
|
|
10115
|
+
className: buttonSize,
|
|
10116
|
+
onClick: resetTransforms,
|
|
10117
|
+
"aria-label": "Reset all transforms",
|
|
10118
|
+
type: "button",
|
|
10119
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { className: iconSize })
|
|
10120
|
+
}
|
|
10121
|
+
),
|
|
10122
|
+
/* @__PURE__ */ jsx(
|
|
10123
|
+
Button,
|
|
10124
|
+
{
|
|
10125
|
+
variant: "ghost",
|
|
10126
|
+
size: "icon",
|
|
10127
|
+
className: buttonSize,
|
|
10128
|
+
onClick: () => fileInputRef.current?.click(),
|
|
10129
|
+
"aria-label": "Upload new image",
|
|
10130
|
+
type: "button",
|
|
10131
|
+
children: /* @__PURE__ */ jsx(Upload, { className: iconSize })
|
|
10132
|
+
}
|
|
10133
|
+
),
|
|
10134
|
+
/* @__PURE__ */ jsx(
|
|
10135
|
+
Button,
|
|
10136
|
+
{
|
|
10137
|
+
variant: "ghost",
|
|
10138
|
+
size: "icon",
|
|
10139
|
+
className: cn(
|
|
10140
|
+
buttonSize,
|
|
10141
|
+
"text-destructive hover:text-destructive hover:bg-destructive/10"
|
|
10142
|
+
),
|
|
10143
|
+
onClick: handleDiscard,
|
|
10144
|
+
"aria-label": "Discard image",
|
|
10145
|
+
type: "button",
|
|
10146
|
+
children: /* @__PURE__ */ jsx(X, { className: iconSize })
|
|
10147
|
+
}
|
|
10148
|
+
)
|
|
10149
|
+
]
|
|
10150
|
+
}
|
|
10151
|
+
)
|
|
10152
|
+
] })
|
|
10153
|
+
] }),
|
|
10154
|
+
/* @__PURE__ */ jsx(
|
|
10155
|
+
"input",
|
|
10156
|
+
{
|
|
10157
|
+
ref: fileInputRef,
|
|
10158
|
+
type: "file",
|
|
10159
|
+
accept: "image/*",
|
|
10160
|
+
onChange: handleFileSelect,
|
|
10161
|
+
className: "hidden",
|
|
10162
|
+
"aria-hidden": "true"
|
|
10163
|
+
}
|
|
10164
|
+
)
|
|
10165
|
+
]
|
|
10166
|
+
}
|
|
10167
|
+
);
|
|
10168
|
+
}
|
|
10169
|
+
function Dialog({
|
|
10170
|
+
...props
|
|
10171
|
+
}) {
|
|
10172
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
10173
|
+
}
|
|
10174
|
+
function DialogTrigger({
|
|
10175
|
+
...props
|
|
10176
|
+
}) {
|
|
10177
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
10178
|
+
}
|
|
10179
|
+
function DialogPortal({
|
|
10180
|
+
...props
|
|
10181
|
+
}) {
|
|
10182
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
10183
|
+
}
|
|
10184
|
+
function DialogClose({
|
|
10185
|
+
...props
|
|
10186
|
+
}) {
|
|
10187
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
|
|
10188
|
+
}
|
|
10189
|
+
function DialogOverlay({
|
|
10190
|
+
className,
|
|
10191
|
+
...props
|
|
10192
|
+
}) {
|
|
10193
|
+
return /* @__PURE__ */ jsx(
|
|
10194
|
+
DialogPrimitive.Overlay,
|
|
10195
|
+
{
|
|
10196
|
+
"data-slot": "dialog-overlay",
|
|
10197
|
+
className: cn(
|
|
10198
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
10199
|
+
className
|
|
10200
|
+
),
|
|
10201
|
+
...props
|
|
10202
|
+
}
|
|
10203
|
+
);
|
|
10204
|
+
}
|
|
10205
|
+
function DialogContent({
|
|
10206
|
+
className,
|
|
10207
|
+
children,
|
|
10208
|
+
showCloseButton = true,
|
|
10209
|
+
...props
|
|
10210
|
+
}) {
|
|
10211
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
10212
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
10213
|
+
/* @__PURE__ */ jsxs(
|
|
10214
|
+
DialogPrimitive.Content,
|
|
10215
|
+
{
|
|
10216
|
+
"data-slot": "dialog-content",
|
|
10217
|
+
className: cn(
|
|
10218
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
10219
|
+
className
|
|
10220
|
+
),
|
|
10221
|
+
...props,
|
|
10222
|
+
children: [
|
|
10223
|
+
children,
|
|
10224
|
+
showCloseButton && /* @__PURE__ */ jsxs(
|
|
10225
|
+
DialogPrimitive.Close,
|
|
10226
|
+
{
|
|
10227
|
+
"data-slot": "dialog-close",
|
|
10228
|
+
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
10229
|
+
children: [
|
|
10230
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
10231
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
10232
|
+
]
|
|
10233
|
+
}
|
|
10234
|
+
)
|
|
10235
|
+
]
|
|
10236
|
+
}
|
|
10237
|
+
)
|
|
10238
|
+
] });
|
|
10239
|
+
}
|
|
10240
|
+
function DialogHeader({ className, ...props }) {
|
|
10241
|
+
return /* @__PURE__ */ jsx(
|
|
10242
|
+
"div",
|
|
10243
|
+
{
|
|
10244
|
+
"data-slot": "dialog-header",
|
|
10245
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
10246
|
+
...props
|
|
10247
|
+
}
|
|
10248
|
+
);
|
|
10249
|
+
}
|
|
10250
|
+
function DialogFooter({ className, ...props }) {
|
|
10251
|
+
return /* @__PURE__ */ jsx(
|
|
10252
|
+
"div",
|
|
10253
|
+
{
|
|
10254
|
+
"data-slot": "dialog-footer",
|
|
10255
|
+
className: cn(
|
|
10256
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
10257
|
+
className
|
|
10258
|
+
),
|
|
10259
|
+
...props
|
|
10260
|
+
}
|
|
10261
|
+
);
|
|
10262
|
+
}
|
|
10263
|
+
function DialogTitle({
|
|
10264
|
+
className,
|
|
10265
|
+
...props
|
|
10266
|
+
}) {
|
|
10267
|
+
return /* @__PURE__ */ jsx(
|
|
10268
|
+
DialogPrimitive.Title,
|
|
10269
|
+
{
|
|
10270
|
+
"data-slot": "dialog-title",
|
|
10271
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
10272
|
+
...props
|
|
10273
|
+
}
|
|
10274
|
+
);
|
|
10275
|
+
}
|
|
10276
|
+
function DialogDescription({
|
|
10277
|
+
className,
|
|
10278
|
+
...props
|
|
10279
|
+
}) {
|
|
10280
|
+
return /* @__PURE__ */ jsx(
|
|
10281
|
+
DialogPrimitive.Description,
|
|
10282
|
+
{
|
|
10283
|
+
"data-slot": "dialog-description",
|
|
10284
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
10285
|
+
...props
|
|
10286
|
+
}
|
|
10287
|
+
);
|
|
10288
|
+
}
|
|
10289
|
+
function Drawer({
|
|
10290
|
+
...props
|
|
10291
|
+
}) {
|
|
10292
|
+
return /* @__PURE__ */ jsx(Drawer$1.Root, { "data-slot": "drawer", ...props });
|
|
10293
|
+
}
|
|
10294
|
+
function DrawerTrigger({
|
|
10295
|
+
...props
|
|
10296
|
+
}) {
|
|
10297
|
+
return /* @__PURE__ */ jsx(Drawer$1.Trigger, { "data-slot": "drawer-trigger", ...props });
|
|
10298
|
+
}
|
|
10299
|
+
function DrawerPortal({
|
|
10300
|
+
...props
|
|
10301
|
+
}) {
|
|
10302
|
+
return /* @__PURE__ */ jsx(Drawer$1.Portal, { "data-slot": "drawer-portal", ...props });
|
|
10303
|
+
}
|
|
10304
|
+
function DrawerClose({
|
|
10305
|
+
...props
|
|
10306
|
+
}) {
|
|
10307
|
+
return /* @__PURE__ */ jsx(Drawer$1.Close, { "data-slot": "drawer-close", ...props });
|
|
10308
|
+
}
|
|
10309
|
+
function DrawerOverlay({
|
|
10310
|
+
className,
|
|
10311
|
+
...props
|
|
10312
|
+
}) {
|
|
10313
|
+
return /* @__PURE__ */ jsx(
|
|
10314
|
+
Drawer$1.Overlay,
|
|
10315
|
+
{
|
|
10316
|
+
"data-slot": "drawer-overlay",
|
|
10317
|
+
className: cn(
|
|
10318
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
10319
|
+
className
|
|
10320
|
+
),
|
|
10321
|
+
...props
|
|
10322
|
+
}
|
|
10323
|
+
);
|
|
10324
|
+
}
|
|
10325
|
+
function DrawerContent({
|
|
10326
|
+
className,
|
|
10327
|
+
children,
|
|
10328
|
+
...props
|
|
10329
|
+
}) {
|
|
10330
|
+
return /* @__PURE__ */ jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
|
|
10331
|
+
/* @__PURE__ */ jsx(DrawerOverlay, {}),
|
|
10332
|
+
/* @__PURE__ */ jsxs(
|
|
10333
|
+
Drawer$1.Content,
|
|
10334
|
+
{
|
|
10335
|
+
"data-slot": "drawer-content",
|
|
10336
|
+
className: cn(
|
|
10337
|
+
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
|
|
10338
|
+
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
|
|
10339
|
+
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
|
|
10340
|
+
"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
10341
|
+
"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
|
|
10342
|
+
className
|
|
10343
|
+
),
|
|
10344
|
+
...props,
|
|
10345
|
+
children: [
|
|
10346
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
|
|
10347
|
+
children
|
|
10348
|
+
]
|
|
10349
|
+
}
|
|
10350
|
+
)
|
|
10351
|
+
] });
|
|
10352
|
+
}
|
|
10353
|
+
function DrawerHeader({ className, ...props }) {
|
|
10354
|
+
return /* @__PURE__ */ jsx(
|
|
10355
|
+
"div",
|
|
10356
|
+
{
|
|
10357
|
+
"data-slot": "drawer-header",
|
|
10358
|
+
className: cn(
|
|
10359
|
+
"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
|
|
10360
|
+
className
|
|
10361
|
+
),
|
|
10362
|
+
...props
|
|
10363
|
+
}
|
|
10364
|
+
);
|
|
10365
|
+
}
|
|
10366
|
+
function DrawerFooter({ className, ...props }) {
|
|
10367
|
+
return /* @__PURE__ */ jsx(
|
|
10368
|
+
"div",
|
|
10369
|
+
{
|
|
10370
|
+
"data-slot": "drawer-footer",
|
|
10371
|
+
className: cn("mt-auto flex flex-col gap-2 p-4", className),
|
|
10372
|
+
...props
|
|
10373
|
+
}
|
|
10374
|
+
);
|
|
10375
|
+
}
|
|
10376
|
+
function DrawerTitle({
|
|
10377
|
+
className,
|
|
10378
|
+
...props
|
|
10379
|
+
}) {
|
|
10380
|
+
return /* @__PURE__ */ jsx(
|
|
10381
|
+
Drawer$1.Title,
|
|
10382
|
+
{
|
|
10383
|
+
"data-slot": "drawer-title",
|
|
10384
|
+
className: cn("text-foreground font-semibold", className),
|
|
10385
|
+
...props
|
|
10386
|
+
}
|
|
10387
|
+
);
|
|
10388
|
+
}
|
|
10389
|
+
function DrawerDescription({
|
|
10390
|
+
className,
|
|
10391
|
+
...props
|
|
10392
|
+
}) {
|
|
10393
|
+
return /* @__PURE__ */ jsx(
|
|
10394
|
+
Drawer$1.Description,
|
|
10395
|
+
{
|
|
10396
|
+
"data-slot": "drawer-description",
|
|
10397
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
10398
|
+
...props
|
|
10399
|
+
}
|
|
10400
|
+
);
|
|
10401
|
+
}
|
|
10402
|
+
function useMediaQuery(query) {
|
|
10403
|
+
const [matches, setMatches] = useState(false);
|
|
10404
|
+
useEffect(() => {
|
|
10405
|
+
const media = window.matchMedia(query);
|
|
10406
|
+
if (media.matches !== matches) {
|
|
10407
|
+
setMatches(media.matches);
|
|
10408
|
+
}
|
|
10409
|
+
const listener = () => setMatches(media.matches);
|
|
10410
|
+
media.addEventListener("change", listener);
|
|
10411
|
+
return () => media.removeEventListener("change", listener);
|
|
10412
|
+
}, [matches, query]);
|
|
10413
|
+
return matches;
|
|
10414
|
+
}
|
|
10415
|
+
function AvatarEditorDialog({
|
|
10416
|
+
value,
|
|
10417
|
+
onChange,
|
|
10418
|
+
onSave,
|
|
10419
|
+
displaySize = 120,
|
|
10420
|
+
editorSize = 280,
|
|
10421
|
+
outputSize = 256,
|
|
10422
|
+
placeholder: _placeholder = "Add Photo",
|
|
10423
|
+
editLabel = "Edit avatar",
|
|
10424
|
+
dialogTitle = "Edit Avatar",
|
|
10425
|
+
acceptText = "Accept",
|
|
10426
|
+
cancelText = "Cancel",
|
|
10427
|
+
successMessage = "Avatar saved successfully!",
|
|
10428
|
+
errorMessage = "Failed to save avatar. Please try again.",
|
|
10429
|
+
className
|
|
10430
|
+
}) {
|
|
10431
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
10432
|
+
const [editedValue, setEditedValue] = useState(value ?? null);
|
|
10433
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
10434
|
+
const [feedback, setFeedback] = useState(null);
|
|
10435
|
+
const isMobile = useMediaQuery("(max-width: 640px)");
|
|
10436
|
+
const handleOpenChange = useCallback(
|
|
10437
|
+
(open) => {
|
|
10438
|
+
if (open) {
|
|
10439
|
+
setEditedValue(value ?? null);
|
|
10440
|
+
setFeedback(null);
|
|
10441
|
+
}
|
|
10442
|
+
setIsOpen(open);
|
|
10443
|
+
},
|
|
10444
|
+
[value]
|
|
10445
|
+
);
|
|
10446
|
+
const handleAccept = useCallback(async () => {
|
|
10447
|
+
if (!editedValue) return;
|
|
10448
|
+
setIsSaving(true);
|
|
10449
|
+
setFeedback(null);
|
|
10450
|
+
try {
|
|
10451
|
+
if (onSave) {
|
|
10452
|
+
const result = await onSave(editedValue);
|
|
10453
|
+
if (result) {
|
|
10454
|
+
setFeedback({ type: "success", message: successMessage });
|
|
10455
|
+
setTimeout(() => {
|
|
10456
|
+
setIsOpen(false);
|
|
10457
|
+
setFeedback(null);
|
|
10458
|
+
}, 1500);
|
|
10459
|
+
} else {
|
|
10460
|
+
setFeedback({ type: "error", message: errorMessage });
|
|
10461
|
+
}
|
|
10462
|
+
} else {
|
|
10463
|
+
onChange?.(editedValue);
|
|
10464
|
+
setFeedback({ type: "success", message: successMessage });
|
|
10465
|
+
setTimeout(() => {
|
|
10466
|
+
setIsOpen(false);
|
|
10467
|
+
setFeedback(null);
|
|
10468
|
+
}, 1500);
|
|
10469
|
+
}
|
|
10470
|
+
} catch {
|
|
10471
|
+
setFeedback({ type: "error", message: errorMessage });
|
|
10472
|
+
} finally {
|
|
10473
|
+
setIsSaving(false);
|
|
10474
|
+
}
|
|
10475
|
+
}, [editedValue, onChange, onSave, successMessage, errorMessage]);
|
|
10476
|
+
const AvatarDisplay = /* @__PURE__ */ jsxs(
|
|
10477
|
+
"div",
|
|
10478
|
+
{
|
|
10479
|
+
className: cn("relative group", className),
|
|
10480
|
+
style: { width: displaySize, height: displaySize },
|
|
10481
|
+
children: [
|
|
10482
|
+
/* @__PURE__ */ jsx(
|
|
10483
|
+
"div",
|
|
10484
|
+
{
|
|
10485
|
+
className: "w-full h-full rounded-full overflow-hidden bg-muted border-2 border-border flex items-center justify-center",
|
|
10486
|
+
style: { width: displaySize, height: displaySize },
|
|
10487
|
+
children: value ? /* @__PURE__ */ jsx(
|
|
10488
|
+
"img",
|
|
10489
|
+
{
|
|
10490
|
+
src: value || "/placeholder.svg",
|
|
10491
|
+
alt: "Avatar",
|
|
10492
|
+
className: "w-full h-full object-cover"
|
|
10493
|
+
}
|
|
10494
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx(User, { className: "w-10 h-10" }) })
|
|
10495
|
+
}
|
|
10496
|
+
),
|
|
10497
|
+
/* @__PURE__ */ jsx(
|
|
10498
|
+
"button",
|
|
10499
|
+
{
|
|
10500
|
+
onClick: () => handleOpenChange(true),
|
|
10501
|
+
className: "absolute bottom-0 right-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center shadow-lg hover:bg-primary/90 transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2",
|
|
10502
|
+
"aria-label": editLabel,
|
|
10503
|
+
type: "button",
|
|
10504
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" })
|
|
10505
|
+
}
|
|
10506
|
+
)
|
|
10507
|
+
]
|
|
10508
|
+
}
|
|
10509
|
+
);
|
|
10510
|
+
const mobileEditorSize = isMobile ? Math.min(editorSize + 40, window.innerWidth - 48) : editorSize;
|
|
10511
|
+
const EditorContent = /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
10512
|
+
/* @__PURE__ */ jsx(
|
|
10513
|
+
AvatarEditor,
|
|
10514
|
+
{
|
|
10515
|
+
value: editedValue,
|
|
10516
|
+
onChange: setEditedValue,
|
|
10517
|
+
size: mobileEditorSize,
|
|
10518
|
+
outputSize,
|
|
10519
|
+
controlSize: isMobile ? "large" : "default"
|
|
10520
|
+
}
|
|
10521
|
+
),
|
|
10522
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: feedback && /* @__PURE__ */ jsxs(
|
|
10523
|
+
motion.div,
|
|
10524
|
+
{
|
|
10525
|
+
initial: { opacity: 0, y: -10 },
|
|
10526
|
+
animate: { opacity: 1, y: 0 },
|
|
10527
|
+
exit: { opacity: 0, y: -10 },
|
|
10528
|
+
className: cn(
|
|
10529
|
+
"flex items-center gap-2 rounded-lg font-medium",
|
|
10530
|
+
isMobile ? "px-5 py-3 text-base" : "px-4 py-2 text-sm",
|
|
10531
|
+
feedback.type === "success" ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400"
|
|
10532
|
+
),
|
|
10533
|
+
children: [
|
|
10534
|
+
feedback.type === "success" ? /* @__PURE__ */ jsx(Check, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }) : /* @__PURE__ */ jsx(X, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }),
|
|
10535
|
+
feedback.message
|
|
10536
|
+
]
|
|
10537
|
+
}
|
|
10538
|
+
) })
|
|
10539
|
+
] });
|
|
10540
|
+
const FooterButtons = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10541
|
+
isMobile ? /* @__PURE__ */ jsx(DrawerClose, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
10542
|
+
Button,
|
|
10543
|
+
{
|
|
10544
|
+
variant: "outline",
|
|
10545
|
+
disabled: isSaving,
|
|
10546
|
+
size: "lg",
|
|
10547
|
+
className: "flex-1 bg-transparent",
|
|
10548
|
+
children: cancelText
|
|
10549
|
+
}
|
|
10550
|
+
) }) : /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", disabled: isSaving, children: cancelText }) }),
|
|
10551
|
+
/* @__PURE__ */ jsx(
|
|
10552
|
+
Button,
|
|
10553
|
+
{
|
|
10554
|
+
onClick: handleAccept,
|
|
10555
|
+
disabled: !editedValue || isSaving || feedback?.type === "success",
|
|
10556
|
+
size: isMobile ? "lg" : "default",
|
|
10557
|
+
className: isMobile ? "flex-1" : "",
|
|
10558
|
+
children: isSaving ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10559
|
+
/* @__PURE__ */ jsx(
|
|
10560
|
+
Loader2,
|
|
10561
|
+
{
|
|
10562
|
+
className: cn(
|
|
10563
|
+
"mr-2 animate-spin",
|
|
10564
|
+
isMobile ? "w-5 h-5" : "w-4 h-4"
|
|
10565
|
+
)
|
|
10566
|
+
}
|
|
10567
|
+
),
|
|
10568
|
+
"Saving..."
|
|
10569
|
+
] }) : feedback?.type === "success" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10570
|
+
/* @__PURE__ */ jsx(Check, { className: cn("mr-2", isMobile ? "w-5 h-5" : "w-4 h-4") }),
|
|
10571
|
+
"Saved!"
|
|
10572
|
+
] }) : acceptText
|
|
10573
|
+
}
|
|
10574
|
+
)
|
|
10575
|
+
] });
|
|
10576
|
+
if (isMobile) {
|
|
10577
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10578
|
+
AvatarDisplay,
|
|
10579
|
+
/* @__PURE__ */ jsx(Drawer, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(DrawerContent, { className: "max-h-[96vh]", children: [
|
|
10580
|
+
/* @__PURE__ */ jsx(DrawerHeader, { className: "text-center", children: /* @__PURE__ */ jsx(DrawerTitle, { className: "text-xl", children: dialogTitle }) }),
|
|
10581
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 pb-6 overflow-y-auto", children: EditorContent }),
|
|
10582
|
+
/* @__PURE__ */ jsx(DrawerFooter, { className: "flex-row gap-3 px-6 pb-8", children: FooterButtons })
|
|
10583
|
+
] }) })
|
|
10584
|
+
] });
|
|
10585
|
+
}
|
|
10586
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10587
|
+
AvatarDisplay,
|
|
10588
|
+
/* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
10589
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: dialogTitle }) }),
|
|
10590
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center py-4", children: EditorContent }),
|
|
10591
|
+
/* @__PURE__ */ jsx(DialogFooter, { className: "gap-2 sm:gap-0", children: FooterButtons })
|
|
10592
|
+
] }) })
|
|
10593
|
+
] });
|
|
10594
|
+
}
|
|
10595
|
+
function Popover({
|
|
10596
|
+
...props
|
|
10597
|
+
}) {
|
|
10598
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
10599
|
+
}
|
|
10600
|
+
function PopoverTrigger({
|
|
10601
|
+
...props
|
|
10602
|
+
}) {
|
|
10603
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
10604
|
+
}
|
|
10605
|
+
function PopoverContent({
|
|
10606
|
+
className,
|
|
10607
|
+
align = "center",
|
|
10608
|
+
sideOffset = 4,
|
|
10609
|
+
...props
|
|
10610
|
+
}) {
|
|
10611
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
10612
|
+
PopoverPrimitive.Content,
|
|
10613
|
+
{
|
|
10614
|
+
"data-slot": "popover-content",
|
|
10615
|
+
align,
|
|
10616
|
+
sideOffset,
|
|
10617
|
+
className: cn(
|
|
10618
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
10619
|
+
className
|
|
10620
|
+
),
|
|
10621
|
+
...props
|
|
10622
|
+
}
|
|
10623
|
+
) });
|
|
10624
|
+
}
|
|
10625
|
+
function PopoverAnchor({
|
|
10626
|
+
...props
|
|
10627
|
+
}) {
|
|
10628
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
10629
|
+
}
|
|
10630
|
+
function ScrollArea({
|
|
10631
|
+
className,
|
|
10632
|
+
children,
|
|
10633
|
+
...props
|
|
10634
|
+
}) {
|
|
10635
|
+
return /* @__PURE__ */ jsxs(
|
|
10636
|
+
ScrollAreaPrimitive.Root,
|
|
10637
|
+
{
|
|
10638
|
+
"data-slot": "scroll-area",
|
|
10639
|
+
className: cn("relative overflow-hidden", className),
|
|
10640
|
+
...props,
|
|
10641
|
+
children: [
|
|
10642
|
+
/* @__PURE__ */ jsx(
|
|
10643
|
+
ScrollAreaPrimitive.Viewport,
|
|
10644
|
+
{
|
|
10645
|
+
"data-slot": "scroll-area-viewport",
|
|
10646
|
+
className: "h-full w-full rounded-[inherit]",
|
|
10647
|
+
children
|
|
10648
|
+
}
|
|
10649
|
+
),
|
|
10650
|
+
/* @__PURE__ */ jsx(ScrollBar, {}),
|
|
10651
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
|
|
10652
|
+
]
|
|
10653
|
+
}
|
|
10654
|
+
);
|
|
10655
|
+
}
|
|
10656
|
+
function ScrollBar({
|
|
10657
|
+
className,
|
|
10658
|
+
orientation = "vertical",
|
|
10659
|
+
...props
|
|
10660
|
+
}) {
|
|
10661
|
+
return /* @__PURE__ */ jsx(
|
|
10662
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
10663
|
+
{
|
|
10664
|
+
"data-slot": "scroll-bar",
|
|
10665
|
+
orientation,
|
|
10666
|
+
className: cn(
|
|
10667
|
+
"flex touch-none select-none transition-colors",
|
|
10668
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
10669
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
10670
|
+
className
|
|
10671
|
+
),
|
|
10672
|
+
...props,
|
|
10673
|
+
children: /* @__PURE__ */ jsx(
|
|
10674
|
+
ScrollAreaPrimitive.ScrollAreaThumb,
|
|
10675
|
+
{
|
|
10676
|
+
"data-slot": "scroll-thumb",
|
|
10677
|
+
className: "relative flex-1 rounded-full bg-border"
|
|
10678
|
+
}
|
|
10679
|
+
)
|
|
10680
|
+
}
|
|
10681
|
+
);
|
|
10682
|
+
}
|
|
10683
|
+
var sizeConfig = {
|
|
10684
|
+
sm: {
|
|
10685
|
+
button: "h-8 w-8",
|
|
10686
|
+
icon: "w-4 h-4",
|
|
10687
|
+
badge: "min-w-[16px] h-4 text-[10px] -top-1 -right-1",
|
|
10688
|
+
dot: "w-2.5 h-2.5 -top-0.5 -right-0.5"
|
|
10689
|
+
},
|
|
10690
|
+
md: {
|
|
10691
|
+
button: "h-9 w-9",
|
|
10692
|
+
icon: "w-5 h-5",
|
|
10693
|
+
badge: "min-w-[18px] h-[18px] text-[11px] -top-1 -right-1",
|
|
10694
|
+
dot: "w-3 h-3 -top-0.5 -right-0.5"
|
|
10695
|
+
},
|
|
10696
|
+
lg: {
|
|
10697
|
+
button: "h-10 w-10",
|
|
10698
|
+
icon: "w-6 h-6",
|
|
10699
|
+
badge: "min-w-[20px] h-5 text-xs -top-1.5 -right-1.5",
|
|
10700
|
+
dot: "w-3.5 h-3.5 -top-0.5 -right-0.5"
|
|
10701
|
+
}
|
|
10702
|
+
};
|
|
10703
|
+
var typeConfig = {
|
|
10704
|
+
info: {
|
|
10705
|
+
icon: Info,
|
|
10706
|
+
color: "text-blue-500",
|
|
10707
|
+
bg: "bg-blue-500/10"
|
|
10708
|
+
},
|
|
10709
|
+
success: {
|
|
10710
|
+
icon: CheckCircle,
|
|
10711
|
+
color: "text-green-500",
|
|
10712
|
+
bg: "bg-green-500/10"
|
|
10713
|
+
},
|
|
10714
|
+
warning: {
|
|
10715
|
+
icon: AlertTriangle,
|
|
10716
|
+
color: "text-amber-500",
|
|
10717
|
+
bg: "bg-amber-500/10"
|
|
10718
|
+
},
|
|
10719
|
+
error: {
|
|
10720
|
+
icon: XCircle,
|
|
10721
|
+
color: "text-red-500",
|
|
10722
|
+
bg: "bg-red-500/10"
|
|
10723
|
+
}
|
|
10724
|
+
};
|
|
10725
|
+
var dotColorConfig = {
|
|
10726
|
+
red: "bg-red-500",
|
|
10727
|
+
blue: "bg-blue-500",
|
|
10728
|
+
green: "bg-green-500",
|
|
10729
|
+
amber: "bg-amber-500",
|
|
10730
|
+
purple: "bg-purple-500",
|
|
10731
|
+
primary: "bg-primary"
|
|
10732
|
+
};
|
|
10733
|
+
var soundConfig = {
|
|
10734
|
+
chime: {
|
|
10735
|
+
frequencies: [880, 1100],
|
|
10736
|
+
durations: [0.1, 0.2],
|
|
10737
|
+
gain: 0.3
|
|
10738
|
+
},
|
|
10739
|
+
bell: {
|
|
10740
|
+
frequencies: [523, 659, 784],
|
|
10741
|
+
durations: [0.15, 0.15, 0.2],
|
|
10742
|
+
gain: 0.25
|
|
10743
|
+
},
|
|
10744
|
+
pop: {
|
|
10745
|
+
frequencies: [400, 600],
|
|
10746
|
+
durations: [0.05, 0.08],
|
|
10747
|
+
gain: 0.4
|
|
10748
|
+
},
|
|
10749
|
+
ding: {
|
|
10750
|
+
frequencies: [1200],
|
|
10751
|
+
durations: [0.15],
|
|
10752
|
+
gain: 0.2
|
|
10753
|
+
}
|
|
10754
|
+
};
|
|
10755
|
+
var pulseVariants = {
|
|
10756
|
+
ring: {
|
|
10757
|
+
animate: { scale: [1, 1.8], opacity: [0.6, 0] },
|
|
10758
|
+
transition: {
|
|
10759
|
+
duration: 1.2,
|
|
10760
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
10761
|
+
ease: "easeOut"
|
|
10762
|
+
}
|
|
10763
|
+
},
|
|
10764
|
+
glow: {
|
|
10765
|
+
animate: { scale: [1, 1.3, 1], opacity: [0.8, 0.4, 0.8] },
|
|
10766
|
+
transition: {
|
|
10767
|
+
duration: 1.5,
|
|
10768
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
10769
|
+
ease: "easeInOut"
|
|
10770
|
+
}
|
|
10771
|
+
},
|
|
10772
|
+
bounce: {
|
|
10773
|
+
animate: { scale: [1, 1.2, 1], y: [0, -2, 0] },
|
|
10774
|
+
transition: {
|
|
10775
|
+
duration: 0.6,
|
|
10776
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
10777
|
+
ease: "easeInOut"
|
|
10778
|
+
}
|
|
10779
|
+
}
|
|
10780
|
+
};
|
|
10781
|
+
function formatTimeAgo(date) {
|
|
10782
|
+
const now = /* @__PURE__ */ new Date();
|
|
10783
|
+
const diffMs = now.getTime() - date.getTime();
|
|
10784
|
+
const diffSecs = Math.floor(diffMs / 1e3);
|
|
10785
|
+
const diffMins = Math.floor(diffSecs / 60);
|
|
10786
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
10787
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
10788
|
+
if (diffSecs < 60) return "just now";
|
|
10789
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
10790
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
10791
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
10792
|
+
return date.toLocaleDateString();
|
|
10793
|
+
}
|
|
10794
|
+
function playNotificationSound(soundType = "chime", soundUrl) {
|
|
10795
|
+
if (typeof window === "undefined" || soundType === "none") return;
|
|
10796
|
+
if (soundUrl) {
|
|
10797
|
+
const audio = new Audio(soundUrl);
|
|
10798
|
+
audio.volume = 0.5;
|
|
10799
|
+
audio.play().catch(() => {
|
|
10800
|
+
});
|
|
10801
|
+
return;
|
|
10802
|
+
}
|
|
10803
|
+
const config = soundConfig[soundType];
|
|
10804
|
+
if (!config) return;
|
|
10805
|
+
try {
|
|
10806
|
+
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
10807
|
+
let currentTime = audioContext.currentTime;
|
|
10808
|
+
config.frequencies.forEach((freq, i) => {
|
|
10809
|
+
const oscillator = audioContext.createOscillator();
|
|
10810
|
+
const gainNode = audioContext.createGain();
|
|
10811
|
+
oscillator.connect(gainNode);
|
|
10812
|
+
gainNode.connect(audioContext.destination);
|
|
10813
|
+
oscillator.frequency.setValueAtTime(freq, currentTime);
|
|
10814
|
+
gainNode.gain.setValueAtTime(config.gain, currentTime);
|
|
10815
|
+
gainNode.gain.exponentialRampToValueAtTime(
|
|
10816
|
+
0.01,
|
|
10817
|
+
currentTime + config.durations[i]
|
|
10818
|
+
);
|
|
10819
|
+
oscillator.start(currentTime);
|
|
10820
|
+
oscillator.stop(currentTime + config.durations[i]);
|
|
10821
|
+
currentTime += config.durations[i] * 0.7;
|
|
10822
|
+
});
|
|
10823
|
+
} catch {
|
|
10824
|
+
}
|
|
10825
|
+
}
|
|
10826
|
+
function NotificationsWidget({
|
|
10827
|
+
notifications,
|
|
10828
|
+
onMarkAsRead,
|
|
10829
|
+
onMarkAllAsRead,
|
|
10830
|
+
onDismiss,
|
|
10831
|
+
onClearAll,
|
|
10832
|
+
onNotificationClick,
|
|
10833
|
+
size: size4 = "md",
|
|
10834
|
+
maxVisible = 5,
|
|
10835
|
+
playSound: _playSound = true,
|
|
10836
|
+
soundUrl,
|
|
10837
|
+
className,
|
|
10838
|
+
emptyMessage = "No notifications",
|
|
10839
|
+
title = "Notifications",
|
|
10840
|
+
dotColor = "red",
|
|
10841
|
+
showPulse = true,
|
|
10842
|
+
soundType = "chime",
|
|
10843
|
+
pulseStyle = "ring",
|
|
10844
|
+
soundCooldown = 2e3
|
|
10845
|
+
}) {
|
|
10846
|
+
const [isOpen, setIsOpen] = React32.useState(false);
|
|
10847
|
+
const [prevCount, setPrevCount] = React32.useState(0);
|
|
10848
|
+
const lastSoundPlayedRef = React32.useRef(0);
|
|
10849
|
+
const styles = sizeConfig[size4];
|
|
10850
|
+
const dotBgColor = dotColorConfig[dotColor];
|
|
10851
|
+
const unreadCount = notifications.filter((n) => !n.read).length;
|
|
10852
|
+
const visibleNotifications = notifications.slice(0, maxVisible);
|
|
10853
|
+
const hasMore = notifications.length > maxVisible;
|
|
10854
|
+
React32.useEffect(() => {
|
|
10855
|
+
if (soundType !== "none" && unreadCount > prevCount && prevCount > 0) {
|
|
10856
|
+
const now = Date.now();
|
|
10857
|
+
if (now - lastSoundPlayedRef.current >= soundCooldown) {
|
|
10858
|
+
playNotificationSound(soundType, soundUrl);
|
|
10859
|
+
lastSoundPlayedRef.current = now;
|
|
10860
|
+
}
|
|
10861
|
+
}
|
|
10862
|
+
setPrevCount(unreadCount);
|
|
10863
|
+
}, [unreadCount, prevCount, soundType, soundUrl, soundCooldown]);
|
|
10864
|
+
React32.useEffect(() => {
|
|
10865
|
+
if (isOpen && onMarkAsRead) {
|
|
10866
|
+
visibleNotifications.forEach((notification) => {
|
|
10867
|
+
if (!notification.read) {
|
|
10868
|
+
onMarkAsRead(notification.id);
|
|
10869
|
+
}
|
|
10870
|
+
});
|
|
10871
|
+
}
|
|
10872
|
+
}, [isOpen, onMarkAsRead, visibleNotifications]);
|
|
10873
|
+
const handleNotificationClick = (notification) => {
|
|
10874
|
+
if (notification.href) {
|
|
10875
|
+
onNotificationClick?.(notification);
|
|
10876
|
+
}
|
|
10877
|
+
};
|
|
10878
|
+
return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
10879
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
10880
|
+
motion.button,
|
|
10881
|
+
{
|
|
10882
|
+
className: cn(
|
|
10883
|
+
"relative inline-flex items-center justify-center rounded-lg",
|
|
10884
|
+
"bg-muted/50 border border-border/50 hover:bg-muted/70 transition-colors",
|
|
10885
|
+
styles.button,
|
|
10886
|
+
className
|
|
10887
|
+
),
|
|
10888
|
+
whileTap: { scale: 0.95 },
|
|
10889
|
+
"aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
|
|
10890
|
+
children: [
|
|
10891
|
+
/* @__PURE__ */ jsx(Bell, { className: cn(styles.icon, "text-muted-foreground") }),
|
|
10892
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: unreadCount > 0 && /* @__PURE__ */ jsx(
|
|
10893
|
+
motion.div,
|
|
10894
|
+
{
|
|
10895
|
+
initial: { scale: 0, opacity: 0 },
|
|
10896
|
+
animate: { scale: 1, opacity: 1 },
|
|
10897
|
+
exit: { scale: 0, opacity: 0 },
|
|
10898
|
+
className: cn(
|
|
10899
|
+
"absolute flex items-center justify-center rounded-full",
|
|
10900
|
+
dotBgColor,
|
|
10901
|
+
"text-white font-medium",
|
|
10902
|
+
unreadCount > 9 ? styles.badge : styles.dot,
|
|
10903
|
+
unreadCount > 9 && "px-1"
|
|
10904
|
+
),
|
|
10905
|
+
children: unreadCount > 9 ? /* @__PURE__ */ jsx("span", { children: unreadCount > 99 ? "99+" : unreadCount }) : null
|
|
10906
|
+
}
|
|
10907
|
+
) }),
|
|
10908
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: unreadCount > 0 && showPulse && pulseStyle !== "none" && /* @__PURE__ */ jsx(
|
|
10909
|
+
motion.div,
|
|
10910
|
+
{
|
|
10911
|
+
initial: { scale: 1, opacity: 0.5 },
|
|
10912
|
+
animate: pulseVariants[pulseStyle].animate,
|
|
10913
|
+
transition: pulseVariants[pulseStyle].transition,
|
|
10914
|
+
className: cn("absolute rounded-full", dotBgColor, styles.dot)
|
|
10915
|
+
}
|
|
10916
|
+
) })
|
|
10917
|
+
]
|
|
10918
|
+
}
|
|
10919
|
+
) }),
|
|
10920
|
+
/* @__PURE__ */ jsxs(
|
|
10921
|
+
PopoverContent,
|
|
10922
|
+
{
|
|
10923
|
+
side: "bottom",
|
|
10924
|
+
align: "end",
|
|
10925
|
+
className: "w-80 p-0 overflow-hidden",
|
|
10926
|
+
children: [
|
|
10927
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border", children: [
|
|
10928
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10929
|
+
/* @__PURE__ */ jsx("h4", { className: "font-semibold text-sm", children: title }),
|
|
10930
|
+
unreadCount > 0 && /* @__PURE__ */ jsxs("span", { className: "px-2 py-0.5 text-xs font-medium rounded-full bg-primary/10 text-primary", children: [
|
|
10931
|
+
unreadCount,
|
|
10932
|
+
" new"
|
|
10933
|
+
] })
|
|
10934
|
+
] }),
|
|
10935
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: unreadCount > 0 && onMarkAllAsRead && /* @__PURE__ */ jsxs(
|
|
10936
|
+
Button,
|
|
10937
|
+
{
|
|
10938
|
+
variant: "ghost",
|
|
10939
|
+
size: "sm",
|
|
10940
|
+
className: "h-7 px-2 text-xs",
|
|
10941
|
+
onClick: () => onMarkAllAsRead(),
|
|
10942
|
+
children: [
|
|
10943
|
+
/* @__PURE__ */ jsx(CheckCheck, { className: "w-3.5 h-3.5 mr-1" }),
|
|
10944
|
+
"Mark all read"
|
|
10945
|
+
]
|
|
10946
|
+
}
|
|
10947
|
+
) })
|
|
10948
|
+
] }),
|
|
10949
|
+
notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-4 text-center", children: [
|
|
10950
|
+
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-full bg-muted flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx(Bell, { className: "w-6 h-6 text-muted-foreground" }) }),
|
|
10951
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
|
|
10952
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10953
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "h-[320px]", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-border", children: visibleNotifications.map((notification) => {
|
|
10954
|
+
const config = typeConfig[notification.type || "info"];
|
|
10955
|
+
const Icon = config.icon;
|
|
10956
|
+
const hasLink = !!notification.href;
|
|
10957
|
+
return /* @__PURE__ */ jsxs(
|
|
10958
|
+
motion.div,
|
|
10959
|
+
{
|
|
10960
|
+
initial: { opacity: 0, y: -10 },
|
|
10961
|
+
animate: { opacity: 1, y: 0 },
|
|
10962
|
+
className: cn(
|
|
10963
|
+
"relative flex gap-3 px-4 py-3 transition-colors group",
|
|
10964
|
+
hasLink && "cursor-pointer hover:bg-muted/50",
|
|
10965
|
+
!hasLink && "cursor-default",
|
|
10966
|
+
!notification.read && "bg-primary/5"
|
|
10967
|
+
),
|
|
10968
|
+
onClick: () => handleNotificationClick(notification),
|
|
10969
|
+
children: [
|
|
10970
|
+
/* @__PURE__ */ jsx(
|
|
10971
|
+
"div",
|
|
10972
|
+
{
|
|
10973
|
+
className: cn(
|
|
10974
|
+
"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
|
|
10975
|
+
config.bg
|
|
10976
|
+
),
|
|
10977
|
+
children: /* @__PURE__ */ jsx(Icon, { className: cn("w-4 h-4", config.color) })
|
|
10978
|
+
}
|
|
10979
|
+
),
|
|
10980
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
10981
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-start justify-between gap-2", children: /* @__PURE__ */ jsx(
|
|
10982
|
+
"p",
|
|
10983
|
+
{
|
|
10984
|
+
className: cn(
|
|
10985
|
+
"text-sm line-clamp-1",
|
|
10986
|
+
!notification.read ? "font-semibold" : "font-medium",
|
|
10987
|
+
hasLink && "group-hover:underline"
|
|
10988
|
+
),
|
|
10989
|
+
children: notification.title
|
|
10990
|
+
}
|
|
10991
|
+
) }),
|
|
10992
|
+
notification.message && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground line-clamp-2 mt-0.5", children: notification.message }),
|
|
10993
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground/70 mt-1", children: formatTimeAgo(notification.timestamp) })
|
|
10994
|
+
] }),
|
|
10995
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 flex items-start gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: onDismiss && /* @__PURE__ */ jsx(
|
|
10996
|
+
Button,
|
|
10997
|
+
{
|
|
10998
|
+
variant: "ghost",
|
|
10999
|
+
size: "icon",
|
|
11000
|
+
className: "h-6 w-6 text-muted-foreground hover:text-destructive",
|
|
11001
|
+
onClick: (e) => {
|
|
11002
|
+
e.stopPropagation();
|
|
11003
|
+
onDismiss(notification.id);
|
|
11004
|
+
},
|
|
11005
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3 h-3" })
|
|
11006
|
+
}
|
|
11007
|
+
) })
|
|
11008
|
+
]
|
|
11009
|
+
},
|
|
11010
|
+
notification.id
|
|
11011
|
+
);
|
|
11012
|
+
}) }) }),
|
|
11013
|
+
(hasMore || onClearAll) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-t border-border bg-muted/30", children: [
|
|
11014
|
+
hasMore && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
11015
|
+
"+",
|
|
11016
|
+
notifications.length - maxVisible,
|
|
11017
|
+
" more"
|
|
11018
|
+
] }),
|
|
11019
|
+
onClearAll && notifications.length > 0 && /* @__PURE__ */ jsxs(
|
|
11020
|
+
Button,
|
|
11021
|
+
{
|
|
11022
|
+
variant: "ghost",
|
|
11023
|
+
size: "sm",
|
|
11024
|
+
className: "h-7 px-2 text-xs text-muted-foreground hover:text-destructive",
|
|
11025
|
+
onClick: () => onClearAll(),
|
|
11026
|
+
children: [
|
|
11027
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3 mr-1" }),
|
|
11028
|
+
"Clear all"
|
|
11029
|
+
]
|
|
11030
|
+
}
|
|
11031
|
+
)
|
|
11032
|
+
] })
|
|
11033
|
+
] })
|
|
11034
|
+
]
|
|
11035
|
+
}
|
|
11036
|
+
)
|
|
11037
|
+
] });
|
|
11038
|
+
}
|
|
9510
11039
|
|
|
9511
|
-
export { Button, DropdownMenu2 as DropdownMenu, DropdownMenuCheckboxItem2 as DropdownMenuCheckboxItem, DropdownMenuContent2 as DropdownMenuContent, DropdownMenuGroup2 as DropdownMenuGroup, DropdownMenuItem2 as DropdownMenuItem, DropdownMenuLabel2 as DropdownMenuLabel, DropdownMenuPortal2 as DropdownMenuPortal, DropdownMenuRadioGroup2 as DropdownMenuRadioGroup, DropdownMenuRadioItem2 as DropdownMenuRadioItem, DropdownMenuSeparator2 as DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub2 as DropdownMenuSub, DropdownMenuSubContent2 as DropdownMenuSubContent, DropdownMenuSubTrigger2 as DropdownMenuSubTrigger, DropdownMenuTrigger2 as DropdownMenuTrigger, LanguageSwitcher, ThemeSwitcher, Tooltip2 as Tooltip, TooltipContent2 as TooltipContent, TooltipProvider2 as TooltipProvider, TooltipTrigger2 as TooltipTrigger, buttonVariants, cn };
|
|
11040
|
+
export { AvatarEditor, AvatarEditorDialog, Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu2 as DropdownMenu, DropdownMenuCheckboxItem2 as DropdownMenuCheckboxItem, DropdownMenuContent2 as DropdownMenuContent, DropdownMenuGroup2 as DropdownMenuGroup, DropdownMenuItem2 as DropdownMenuItem, DropdownMenuLabel2 as DropdownMenuLabel, DropdownMenuPortal2 as DropdownMenuPortal, DropdownMenuRadioGroup2 as DropdownMenuRadioGroup, DropdownMenuRadioItem2 as DropdownMenuRadioItem, DropdownMenuSeparator2 as DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub2 as DropdownMenuSub, DropdownMenuSubContent2 as DropdownMenuSubContent, DropdownMenuSubTrigger2 as DropdownMenuSubTrigger, DropdownMenuTrigger2 as DropdownMenuTrigger, LanguageSwitcher, NotificationsWidget, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ScrollArea, ScrollBar, Slider, ThemeSwitcher, Toggle, Tooltip2 as Tooltip, TooltipContent2 as TooltipContent, TooltipProvider2 as TooltipProvider, TooltipTrigger2 as TooltipTrigger, buttonVariants, cn, toggleVariants };
|
|
9512
11041
|
//# sourceMappingURL=index.js.map
|
|
9513
11042
|
//# sourceMappingURL=index.js.map
|