@janovix/blocks 1.0.0-rc.2 → 1.0.0-rc.3
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 +1111 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -1
- package/dist/index.d.ts +95 -1
- package/dist/index.js +1086 -3
- package/dist/index.js.map +1 -1
- package/package.json +10 -4
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,10 @@ var react = require('motion/react');
|
|
|
6
6
|
var React32 = require('react');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
var ReactDOM = require('react-dom');
|
|
9
|
+
var SliderPrimitive = require('@radix-ui/react-slider');
|
|
10
|
+
var TogglePrimitive = require('@radix-ui/react-toggle');
|
|
11
|
+
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
12
|
+
var vaul = require('vaul');
|
|
9
13
|
|
|
10
14
|
function _interopNamespace(e) {
|
|
11
15
|
if (e && e.__esModule) return e;
|
|
@@ -27,6 +31,9 @@ function _interopNamespace(e) {
|
|
|
27
31
|
|
|
28
32
|
var React32__namespace = /*#__PURE__*/_interopNamespace(React32);
|
|
29
33
|
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
34
|
+
var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
|
|
35
|
+
var TogglePrimitive__namespace = /*#__PURE__*/_interopNamespace(TogglePrimitive);
|
|
36
|
+
var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
|
|
30
37
|
|
|
31
38
|
function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
|
|
32
39
|
return function handleEvent(event) {
|
|
@@ -9528,8 +9535,1109 @@ function LanguageSwitcher({
|
|
|
9528
9535
|
}
|
|
9529
9536
|
);
|
|
9530
9537
|
}
|
|
9538
|
+
function Slider({
|
|
9539
|
+
className,
|
|
9540
|
+
defaultValue,
|
|
9541
|
+
value,
|
|
9542
|
+
min: min2 = 0,
|
|
9543
|
+
max: max2 = 100,
|
|
9544
|
+
...props
|
|
9545
|
+
}) {
|
|
9546
|
+
const _values = React32__namespace.useMemo(
|
|
9547
|
+
() => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min2, max2],
|
|
9548
|
+
[value, defaultValue, min2, max2]
|
|
9549
|
+
);
|
|
9550
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9551
|
+
SliderPrimitive__namespace.Root,
|
|
9552
|
+
{
|
|
9553
|
+
"data-slot": "slider",
|
|
9554
|
+
defaultValue,
|
|
9555
|
+
value,
|
|
9556
|
+
min: min2,
|
|
9557
|
+
max: max2,
|
|
9558
|
+
className: cn(
|
|
9559
|
+
"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",
|
|
9560
|
+
className
|
|
9561
|
+
),
|
|
9562
|
+
...props,
|
|
9563
|
+
children: [
|
|
9564
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9565
|
+
SliderPrimitive__namespace.Track,
|
|
9566
|
+
{
|
|
9567
|
+
"data-slot": "slider-track",
|
|
9568
|
+
className: cn(
|
|
9569
|
+
"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"
|
|
9570
|
+
),
|
|
9571
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9572
|
+
SliderPrimitive__namespace.Range,
|
|
9573
|
+
{
|
|
9574
|
+
"data-slot": "slider-range",
|
|
9575
|
+
className: cn(
|
|
9576
|
+
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
9577
|
+
)
|
|
9578
|
+
}
|
|
9579
|
+
)
|
|
9580
|
+
}
|
|
9581
|
+
),
|
|
9582
|
+
Array.from({ length: _values.length }, (_, index2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9583
|
+
SliderPrimitive__namespace.Thumb,
|
|
9584
|
+
{
|
|
9585
|
+
"data-slot": "slider-thumb",
|
|
9586
|
+
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"
|
|
9587
|
+
},
|
|
9588
|
+
index2
|
|
9589
|
+
))
|
|
9590
|
+
]
|
|
9591
|
+
}
|
|
9592
|
+
);
|
|
9593
|
+
}
|
|
9594
|
+
var toggleVariants = cva(
|
|
9595
|
+
"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",
|
|
9596
|
+
{
|
|
9597
|
+
variants: {
|
|
9598
|
+
variant: {
|
|
9599
|
+
default: "bg-transparent",
|
|
9600
|
+
outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
|
|
9601
|
+
},
|
|
9602
|
+
size: {
|
|
9603
|
+
default: "h-9 px-2 min-w-9",
|
|
9604
|
+
sm: "h-8 px-1.5 min-w-8",
|
|
9605
|
+
lg: "h-10 px-2.5 min-w-10"
|
|
9606
|
+
}
|
|
9607
|
+
},
|
|
9608
|
+
defaultVariants: {
|
|
9609
|
+
variant: "default",
|
|
9610
|
+
size: "default"
|
|
9611
|
+
}
|
|
9612
|
+
}
|
|
9613
|
+
);
|
|
9614
|
+
function Toggle({
|
|
9615
|
+
className,
|
|
9616
|
+
variant,
|
|
9617
|
+
size: size4,
|
|
9618
|
+
...props
|
|
9619
|
+
}) {
|
|
9620
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9621
|
+
TogglePrimitive__namespace.Root,
|
|
9622
|
+
{
|
|
9623
|
+
"data-slot": "toggle",
|
|
9624
|
+
className: cn(toggleVariants({ variant, size: size4, className })),
|
|
9625
|
+
...props
|
|
9626
|
+
}
|
|
9627
|
+
);
|
|
9628
|
+
}
|
|
9629
|
+
function AvatarEditor({
|
|
9630
|
+
value,
|
|
9631
|
+
onChange,
|
|
9632
|
+
size: size4 = 280,
|
|
9633
|
+
showGrid: initialShowGrid = false,
|
|
9634
|
+
outputSize = 256,
|
|
9635
|
+
outputFormat = "png",
|
|
9636
|
+
outputQuality = 0.92,
|
|
9637
|
+
className,
|
|
9638
|
+
controlSize = "default",
|
|
9639
|
+
defaultImage,
|
|
9640
|
+
initials: _initials
|
|
9641
|
+
}) {
|
|
9642
|
+
const canvasRef = React32.useRef(null);
|
|
9643
|
+
const fileInputRef = React32.useRef(null);
|
|
9644
|
+
const containerRef = React32.useRef(null);
|
|
9645
|
+
const imageSource = value ?? defaultImage;
|
|
9646
|
+
const [image, setImage] = React32.useState(null);
|
|
9647
|
+
const [imageLoaded, setImageLoaded] = React32.useState(false);
|
|
9648
|
+
const [zoom, setZoom] = React32.useState(1);
|
|
9649
|
+
const [rotation, setRotation] = React32.useState(0);
|
|
9650
|
+
const [position, setPosition] = React32.useState({ x: 0, y: 0 });
|
|
9651
|
+
const [isDragging, setIsDragging] = React32.useState(false);
|
|
9652
|
+
const [dragStart, setDragStart] = React32.useState({ x: 0, y: 0 });
|
|
9653
|
+
const [showGrid, setShowGrid] = React32.useState(initialShowGrid);
|
|
9654
|
+
React32.useEffect(() => {
|
|
9655
|
+
if (imageSource && !imageSource.startsWith("data:")) {
|
|
9656
|
+
const img = new Image();
|
|
9657
|
+
img.crossOrigin = "anonymous";
|
|
9658
|
+
img.onload = () => {
|
|
9659
|
+
setImage(img);
|
|
9660
|
+
setImageLoaded(true);
|
|
9661
|
+
setZoom(1);
|
|
9662
|
+
setRotation(0);
|
|
9663
|
+
setPosition({ x: 0, y: 0 });
|
|
9664
|
+
};
|
|
9665
|
+
img.src = imageSource;
|
|
9666
|
+
}
|
|
9667
|
+
}, [imageSource]);
|
|
9668
|
+
const generateOutput = React32.useCallback(() => {
|
|
9669
|
+
if (!image) return null;
|
|
9670
|
+
const outputCanvas = document.createElement("canvas");
|
|
9671
|
+
outputCanvas.width = outputSize;
|
|
9672
|
+
outputCanvas.height = outputSize;
|
|
9673
|
+
const ctx = outputCanvas.getContext("2d");
|
|
9674
|
+
if (!ctx) return null;
|
|
9675
|
+
ctx.imageSmoothingEnabled = true;
|
|
9676
|
+
ctx.imageSmoothingQuality = "high";
|
|
9677
|
+
if (outputFormat === "jpeg") {
|
|
9678
|
+
ctx.fillStyle = "#ffffff";
|
|
9679
|
+
ctx.fillRect(0, 0, outputSize, outputSize);
|
|
9680
|
+
}
|
|
9681
|
+
ctx.save();
|
|
9682
|
+
ctx.translate(outputSize / 2, outputSize / 2);
|
|
9683
|
+
ctx.rotate(rotation * Math.PI / 180);
|
|
9684
|
+
const scale = zoom;
|
|
9685
|
+
const imgAspect = image.width / image.height;
|
|
9686
|
+
let drawWidth, drawHeight;
|
|
9687
|
+
if (imgAspect > 1) {
|
|
9688
|
+
drawHeight = outputSize * scale;
|
|
9689
|
+
drawWidth = drawHeight * imgAspect;
|
|
9690
|
+
} else {
|
|
9691
|
+
drawWidth = outputSize * scale;
|
|
9692
|
+
drawHeight = drawWidth / imgAspect;
|
|
9693
|
+
}
|
|
9694
|
+
const positionScale = outputSize / size4;
|
|
9695
|
+
const scaledPosX = position.x * positionScale;
|
|
9696
|
+
const scaledPosY = position.y * positionScale;
|
|
9697
|
+
ctx.drawImage(
|
|
9698
|
+
image,
|
|
9699
|
+
-drawWidth / 2 + scaledPosX,
|
|
9700
|
+
-drawHeight / 2 + scaledPosY,
|
|
9701
|
+
drawWidth,
|
|
9702
|
+
drawHeight
|
|
9703
|
+
);
|
|
9704
|
+
ctx.restore();
|
|
9705
|
+
const mimeType = `image/${outputFormat}`;
|
|
9706
|
+
return outputCanvas.toDataURL(mimeType, outputQuality);
|
|
9707
|
+
}, [
|
|
9708
|
+
image,
|
|
9709
|
+
outputSize,
|
|
9710
|
+
outputFormat,
|
|
9711
|
+
outputQuality,
|
|
9712
|
+
zoom,
|
|
9713
|
+
rotation,
|
|
9714
|
+
position,
|
|
9715
|
+
size4
|
|
9716
|
+
]);
|
|
9717
|
+
React32.useEffect(() => {
|
|
9718
|
+
if (!canvasRef.current || !image || !imageLoaded) return;
|
|
9719
|
+
const canvas = canvasRef.current;
|
|
9720
|
+
const ctx = canvas.getContext("2d");
|
|
9721
|
+
if (!ctx) return;
|
|
9722
|
+
const displaySize = size4;
|
|
9723
|
+
canvas.width = displaySize;
|
|
9724
|
+
canvas.height = displaySize;
|
|
9725
|
+
ctx.clearRect(0, 0, displaySize, displaySize);
|
|
9726
|
+
ctx.fillStyle = "#1a1a2e";
|
|
9727
|
+
ctx.fillRect(0, 0, displaySize, displaySize);
|
|
9728
|
+
ctx.save();
|
|
9729
|
+
ctx.translate(displaySize / 2, displaySize / 2);
|
|
9730
|
+
ctx.rotate(rotation * Math.PI / 180);
|
|
9731
|
+
const scale = zoom;
|
|
9732
|
+
const imgAspect = image.width / image.height;
|
|
9733
|
+
let drawWidth, drawHeight;
|
|
9734
|
+
if (imgAspect > 1) {
|
|
9735
|
+
drawHeight = displaySize * scale;
|
|
9736
|
+
drawWidth = drawHeight * imgAspect;
|
|
9737
|
+
} else {
|
|
9738
|
+
drawWidth = displaySize * scale;
|
|
9739
|
+
drawHeight = drawWidth / imgAspect;
|
|
9740
|
+
}
|
|
9741
|
+
ctx.drawImage(
|
|
9742
|
+
image,
|
|
9743
|
+
-drawWidth / 2 + position.x,
|
|
9744
|
+
-drawHeight / 2 + position.y,
|
|
9745
|
+
drawWidth,
|
|
9746
|
+
drawHeight
|
|
9747
|
+
);
|
|
9748
|
+
ctx.restore();
|
|
9749
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
9750
|
+
ctx.beginPath();
|
|
9751
|
+
ctx.arc(
|
|
9752
|
+
displaySize / 2,
|
|
9753
|
+
displaySize / 2,
|
|
9754
|
+
displaySize / 2 - 4,
|
|
9755
|
+
0,
|
|
9756
|
+
Math.PI * 2
|
|
9757
|
+
);
|
|
9758
|
+
ctx.fill();
|
|
9759
|
+
ctx.globalCompositeOperation = "source-over";
|
|
9760
|
+
ctx.strokeStyle = "hsl(var(--primary))";
|
|
9761
|
+
ctx.lineWidth = 3;
|
|
9762
|
+
ctx.beginPath();
|
|
9763
|
+
ctx.arc(
|
|
9764
|
+
displaySize / 2,
|
|
9765
|
+
displaySize / 2,
|
|
9766
|
+
displaySize / 2 - 2,
|
|
9767
|
+
0,
|
|
9768
|
+
Math.PI * 2
|
|
9769
|
+
);
|
|
9770
|
+
ctx.stroke();
|
|
9771
|
+
const dataUrl = generateOutput();
|
|
9772
|
+
if (dataUrl) {
|
|
9773
|
+
onChange?.(dataUrl);
|
|
9774
|
+
}
|
|
9775
|
+
}, [
|
|
9776
|
+
image,
|
|
9777
|
+
imageLoaded,
|
|
9778
|
+
zoom,
|
|
9779
|
+
rotation,
|
|
9780
|
+
position,
|
|
9781
|
+
size4,
|
|
9782
|
+
generateOutput,
|
|
9783
|
+
onChange
|
|
9784
|
+
]);
|
|
9785
|
+
const resetTransforms = React32.useCallback(() => {
|
|
9786
|
+
setZoom(1);
|
|
9787
|
+
setRotation(0);
|
|
9788
|
+
setPosition({ x: 0, y: 0 });
|
|
9789
|
+
}, []);
|
|
9790
|
+
const handleFileSelect = React32.useCallback(
|
|
9791
|
+
(e) => {
|
|
9792
|
+
const file = e.target.files?.[0];
|
|
9793
|
+
if (!file) return;
|
|
9794
|
+
const reader = new FileReader();
|
|
9795
|
+
reader.onload = (event) => {
|
|
9796
|
+
const img = new Image();
|
|
9797
|
+
img.crossOrigin = "anonymous";
|
|
9798
|
+
img.onload = () => {
|
|
9799
|
+
setImage(img);
|
|
9800
|
+
setImageLoaded(true);
|
|
9801
|
+
resetTransforms();
|
|
9802
|
+
};
|
|
9803
|
+
img.src = event.target?.result;
|
|
9804
|
+
};
|
|
9805
|
+
reader.readAsDataURL(file);
|
|
9806
|
+
},
|
|
9807
|
+
[resetTransforms]
|
|
9808
|
+
);
|
|
9809
|
+
const handleMouseDown = React32.useCallback(
|
|
9810
|
+
(e) => {
|
|
9811
|
+
if (!imageLoaded) return;
|
|
9812
|
+
setIsDragging(true);
|
|
9813
|
+
setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });
|
|
9814
|
+
},
|
|
9815
|
+
[imageLoaded, position]
|
|
9816
|
+
);
|
|
9817
|
+
const handleMouseMove = React32.useCallback(
|
|
9818
|
+
(e) => {
|
|
9819
|
+
if (!isDragging) return;
|
|
9820
|
+
setPosition({
|
|
9821
|
+
x: e.clientX - dragStart.x,
|
|
9822
|
+
y: e.clientY - dragStart.y
|
|
9823
|
+
});
|
|
9824
|
+
},
|
|
9825
|
+
[isDragging, dragStart]
|
|
9826
|
+
);
|
|
9827
|
+
const handleMouseUp = React32.useCallback(() => {
|
|
9828
|
+
setIsDragging(false);
|
|
9829
|
+
}, []);
|
|
9830
|
+
const handleTouchStart = React32.useCallback(
|
|
9831
|
+
(e) => {
|
|
9832
|
+
if (!imageLoaded) return;
|
|
9833
|
+
const touch = e.touches[0];
|
|
9834
|
+
setIsDragging(true);
|
|
9835
|
+
setDragStart({
|
|
9836
|
+
x: touch.clientX - position.x,
|
|
9837
|
+
y: touch.clientY - position.y
|
|
9838
|
+
});
|
|
9839
|
+
},
|
|
9840
|
+
[imageLoaded, position]
|
|
9841
|
+
);
|
|
9842
|
+
const handleTouchMove = React32.useCallback(
|
|
9843
|
+
(e) => {
|
|
9844
|
+
if (!isDragging) return;
|
|
9845
|
+
const touch = e.touches[0];
|
|
9846
|
+
setPosition({
|
|
9847
|
+
x: touch.clientX - dragStart.x,
|
|
9848
|
+
y: touch.clientY - dragStart.y
|
|
9849
|
+
});
|
|
9850
|
+
},
|
|
9851
|
+
[isDragging, dragStart]
|
|
9852
|
+
);
|
|
9853
|
+
const handleDiscard = React32.useCallback(() => {
|
|
9854
|
+
setImage(null);
|
|
9855
|
+
setImageLoaded(false);
|
|
9856
|
+
resetTransforms();
|
|
9857
|
+
if (fileInputRef.current) {
|
|
9858
|
+
fileInputRef.current.value = "";
|
|
9859
|
+
}
|
|
9860
|
+
onChange?.(null);
|
|
9861
|
+
}, [resetTransforms, onChange]);
|
|
9862
|
+
const handleZoomIn = () => setZoom((z) => Math.min(z + 0.1, 3));
|
|
9863
|
+
const handleZoomOut = () => setZoom((z) => Math.max(z - 0.1, 0.5));
|
|
9864
|
+
const handleRotateLeft = () => setRotation((r2) => r2 - 15);
|
|
9865
|
+
const handleRotateRight = () => setRotation((r2) => r2 + 15);
|
|
9866
|
+
const isLarge = controlSize === "large";
|
|
9867
|
+
const buttonSize = isLarge ? "h-12 w-12" : "h-8 w-8";
|
|
9868
|
+
const iconSize = isLarge ? "w-6 h-6" : "w-4 h-4";
|
|
9869
|
+
const smallIconSize = isLarge ? "w-5 h-5" : "w-3 h-3";
|
|
9870
|
+
const textSize = isLarge ? "text-sm" : "text-xs";
|
|
9871
|
+
const uploadIconSize = isLarge ? "w-12 h-12" : "w-8 h-8";
|
|
9872
|
+
const uploadContainerSize = isLarge ? "w-24 h-24" : "w-16 h-16";
|
|
9873
|
+
const gapSize = isLarge ? "gap-6" : "gap-4";
|
|
9874
|
+
const controlGap = isLarge ? "gap-3" : "gap-2";
|
|
9875
|
+
const sliderHeight = isLarge ? "[&_[role=slider]]:h-6 [&_[role=slider]]:w-6" : "";
|
|
9876
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9877
|
+
"div",
|
|
9878
|
+
{
|
|
9879
|
+
className: cn("flex flex-col", gapSize, className),
|
|
9880
|
+
style: { width: size4 },
|
|
9881
|
+
children: [
|
|
9882
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9883
|
+
"div",
|
|
9884
|
+
{
|
|
9885
|
+
ref: containerRef,
|
|
9886
|
+
className: "relative bg-muted rounded-2xl overflow-hidden",
|
|
9887
|
+
style: { width: size4, height: size4 },
|
|
9888
|
+
children: [
|
|
9889
|
+
!imageLoaded && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9890
|
+
"button",
|
|
9891
|
+
{
|
|
9892
|
+
onClick: () => fileInputRef.current?.click(),
|
|
9893
|
+
className: cn(
|
|
9894
|
+
"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",
|
|
9895
|
+
isLarge ? "gap-4" : "gap-3"
|
|
9896
|
+
),
|
|
9897
|
+
"aria-label": "Upload image",
|
|
9898
|
+
type: "button",
|
|
9899
|
+
children: [
|
|
9900
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9901
|
+
"div",
|
|
9902
|
+
{
|
|
9903
|
+
className: cn(
|
|
9904
|
+
"rounded-full bg-primary/10 flex items-center justify-center",
|
|
9905
|
+
uploadContainerSize
|
|
9906
|
+
),
|
|
9907
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: cn("text-primary", uploadIconSize) })
|
|
9908
|
+
}
|
|
9909
|
+
),
|
|
9910
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9911
|
+
"span",
|
|
9912
|
+
{
|
|
9913
|
+
className: cn("font-medium", isLarge ? "text-base" : "text-sm"),
|
|
9914
|
+
children: "Click to upload"
|
|
9915
|
+
}
|
|
9916
|
+
),
|
|
9917
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-muted-foreground", textSize), children: "PNG, JPG up to 10MB" })
|
|
9918
|
+
]
|
|
9919
|
+
}
|
|
9920
|
+
),
|
|
9921
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9922
|
+
"canvas",
|
|
9923
|
+
{
|
|
9924
|
+
ref: canvasRef,
|
|
9925
|
+
className: cn(
|
|
9926
|
+
"absolute inset-0",
|
|
9927
|
+
imageLoaded ? "cursor-move" : "pointer-events-none opacity-0"
|
|
9928
|
+
),
|
|
9929
|
+
style: { width: size4, height: size4 },
|
|
9930
|
+
onMouseDown: handleMouseDown,
|
|
9931
|
+
onMouseMove: handleMouseMove,
|
|
9932
|
+
onMouseUp: handleMouseUp,
|
|
9933
|
+
onMouseLeave: handleMouseUp,
|
|
9934
|
+
onTouchStart: handleTouchStart,
|
|
9935
|
+
onTouchMove: handleTouchMove,
|
|
9936
|
+
onTouchEnd: handleMouseUp
|
|
9937
|
+
}
|
|
9938
|
+
),
|
|
9939
|
+
showGrid && imageLoaded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9940
|
+
"div",
|
|
9941
|
+
{
|
|
9942
|
+
className: "absolute inset-0 pointer-events-none",
|
|
9943
|
+
style: { width: size4, height: size4 },
|
|
9944
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size4, height: size4, className: "opacity-30", children: [
|
|
9945
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9946
|
+
"line",
|
|
9947
|
+
{
|
|
9948
|
+
x1: size4 / 3,
|
|
9949
|
+
y1: 0,
|
|
9950
|
+
x2: size4 / 3,
|
|
9951
|
+
y2: size4,
|
|
9952
|
+
stroke: "white",
|
|
9953
|
+
strokeWidth: "1"
|
|
9954
|
+
}
|
|
9955
|
+
),
|
|
9956
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9957
|
+
"line",
|
|
9958
|
+
{
|
|
9959
|
+
x1: size4 * 2 / 3,
|
|
9960
|
+
y1: 0,
|
|
9961
|
+
x2: size4 * 2 / 3,
|
|
9962
|
+
y2: size4,
|
|
9963
|
+
stroke: "white",
|
|
9964
|
+
strokeWidth: "1"
|
|
9965
|
+
}
|
|
9966
|
+
),
|
|
9967
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9968
|
+
"line",
|
|
9969
|
+
{
|
|
9970
|
+
x1: 0,
|
|
9971
|
+
y1: size4 / 3,
|
|
9972
|
+
x2: size4,
|
|
9973
|
+
y2: size4 / 3,
|
|
9974
|
+
stroke: "white",
|
|
9975
|
+
strokeWidth: "1"
|
|
9976
|
+
}
|
|
9977
|
+
),
|
|
9978
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9979
|
+
"line",
|
|
9980
|
+
{
|
|
9981
|
+
x1: 0,
|
|
9982
|
+
y1: size4 * 2 / 3,
|
|
9983
|
+
x2: size4,
|
|
9984
|
+
y2: size4 * 2 / 3,
|
|
9985
|
+
stroke: "white",
|
|
9986
|
+
strokeWidth: "1"
|
|
9987
|
+
}
|
|
9988
|
+
),
|
|
9989
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9990
|
+
"line",
|
|
9991
|
+
{
|
|
9992
|
+
x1: size4 / 2 - 10,
|
|
9993
|
+
y1: size4 / 2,
|
|
9994
|
+
x2: size4 / 2 + 10,
|
|
9995
|
+
y2: size4 / 2,
|
|
9996
|
+
stroke: "white",
|
|
9997
|
+
strokeWidth: "1"
|
|
9998
|
+
}
|
|
9999
|
+
),
|
|
10000
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10001
|
+
"line",
|
|
10002
|
+
{
|
|
10003
|
+
x1: size4 / 2,
|
|
10004
|
+
y1: size4 / 2 - 10,
|
|
10005
|
+
x2: size4 / 2,
|
|
10006
|
+
y2: size4 / 2 + 10,
|
|
10007
|
+
stroke: "white",
|
|
10008
|
+
strokeWidth: "1"
|
|
10009
|
+
}
|
|
10010
|
+
)
|
|
10011
|
+
] })
|
|
10012
|
+
}
|
|
10013
|
+
),
|
|
10014
|
+
imageLoaded && isDragging && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10015
|
+
"div",
|
|
10016
|
+
{
|
|
10017
|
+
className: cn(
|
|
10018
|
+
"absolute top-2 left-1/2 -translate-x-1/2 bg-black/60 rounded text-white flex items-center",
|
|
10019
|
+
isLarge ? "px-3 py-2 text-sm gap-2" : "px-2 py-1 text-xs gap-1"
|
|
10020
|
+
),
|
|
10021
|
+
children: [
|
|
10022
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Move, { className: smallIconSize }),
|
|
10023
|
+
"Dragging"
|
|
10024
|
+
]
|
|
10025
|
+
}
|
|
10026
|
+
)
|
|
10027
|
+
]
|
|
10028
|
+
}
|
|
10029
|
+
),
|
|
10030
|
+
imageLoaded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-4", isLarge && "space-y-5"), children: [
|
|
10031
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center", controlGap), children: [
|
|
10032
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10033
|
+
Button,
|
|
10034
|
+
{
|
|
10035
|
+
variant: "ghost",
|
|
10036
|
+
size: "icon",
|
|
10037
|
+
className: cn(buttonSize, "shrink-0"),
|
|
10038
|
+
onClick: handleZoomOut,
|
|
10039
|
+
"aria-label": "Zoom out",
|
|
10040
|
+
type: "button",
|
|
10041
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZoomOut, { className: iconSize })
|
|
10042
|
+
}
|
|
10043
|
+
),
|
|
10044
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10045
|
+
Slider,
|
|
10046
|
+
{
|
|
10047
|
+
value: [zoom],
|
|
10048
|
+
onValueChange: ([v]) => setZoom(v),
|
|
10049
|
+
min: 0.5,
|
|
10050
|
+
max: 3,
|
|
10051
|
+
step: 0.01,
|
|
10052
|
+
className: cn("flex-1", sliderHeight),
|
|
10053
|
+
"aria-label": "Zoom level"
|
|
10054
|
+
}
|
|
10055
|
+
),
|
|
10056
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10057
|
+
Button,
|
|
10058
|
+
{
|
|
10059
|
+
variant: "ghost",
|
|
10060
|
+
size: "icon",
|
|
10061
|
+
className: cn(buttonSize, "shrink-0"),
|
|
10062
|
+
onClick: handleZoomIn,
|
|
10063
|
+
"aria-label": "Zoom in",
|
|
10064
|
+
type: "button",
|
|
10065
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZoomIn, { className: iconSize })
|
|
10066
|
+
}
|
|
10067
|
+
)
|
|
10068
|
+
] }),
|
|
10069
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center justify-between", controlGap), children: [
|
|
10070
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10071
|
+
"div",
|
|
10072
|
+
{
|
|
10073
|
+
className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
|
|
10074
|
+
children: [
|
|
10075
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10076
|
+
Button,
|
|
10077
|
+
{
|
|
10078
|
+
variant: "ghost",
|
|
10079
|
+
size: "icon",
|
|
10080
|
+
className: buttonSize,
|
|
10081
|
+
onClick: handleRotateLeft,
|
|
10082
|
+
"aria-label": "Rotate left 15 degrees",
|
|
10083
|
+
type: "button",
|
|
10084
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: iconSize })
|
|
10085
|
+
}
|
|
10086
|
+
),
|
|
10087
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10088
|
+
"span",
|
|
10089
|
+
{
|
|
10090
|
+
className: cn(
|
|
10091
|
+
"text-muted-foreground text-center tabular-nums",
|
|
10092
|
+
textSize,
|
|
10093
|
+
isLarge ? "w-16" : "w-12"
|
|
10094
|
+
),
|
|
10095
|
+
children: [
|
|
10096
|
+
rotation,
|
|
10097
|
+
"\xB0"
|
|
10098
|
+
]
|
|
10099
|
+
}
|
|
10100
|
+
),
|
|
10101
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10102
|
+
Button,
|
|
10103
|
+
{
|
|
10104
|
+
variant: "ghost",
|
|
10105
|
+
size: "icon",
|
|
10106
|
+
className: buttonSize,
|
|
10107
|
+
onClick: handleRotateRight,
|
|
10108
|
+
"aria-label": "Rotate right 15 degrees",
|
|
10109
|
+
type: "button",
|
|
10110
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCw, { className: iconSize })
|
|
10111
|
+
}
|
|
10112
|
+
)
|
|
10113
|
+
]
|
|
10114
|
+
}
|
|
10115
|
+
),
|
|
10116
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10117
|
+
"div",
|
|
10118
|
+
{
|
|
10119
|
+
className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
|
|
10120
|
+
children: [
|
|
10121
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10122
|
+
Toggle,
|
|
10123
|
+
{
|
|
10124
|
+
pressed: showGrid,
|
|
10125
|
+
onPressedChange: setShowGrid,
|
|
10126
|
+
size: "sm",
|
|
10127
|
+
className: cn(buttonSize, "p-0"),
|
|
10128
|
+
"aria-label": "Toggle grid overlay",
|
|
10129
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Grid3X3, { className: iconSize })
|
|
10130
|
+
}
|
|
10131
|
+
),
|
|
10132
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10133
|
+
Button,
|
|
10134
|
+
{
|
|
10135
|
+
variant: "ghost",
|
|
10136
|
+
size: "icon",
|
|
10137
|
+
className: buttonSize,
|
|
10138
|
+
onClick: resetTransforms,
|
|
10139
|
+
"aria-label": "Reset all transforms",
|
|
10140
|
+
type: "button",
|
|
10141
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: iconSize })
|
|
10142
|
+
}
|
|
10143
|
+
),
|
|
10144
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10145
|
+
Button,
|
|
10146
|
+
{
|
|
10147
|
+
variant: "ghost",
|
|
10148
|
+
size: "icon",
|
|
10149
|
+
className: buttonSize,
|
|
10150
|
+
onClick: () => fileInputRef.current?.click(),
|
|
10151
|
+
"aria-label": "Upload new image",
|
|
10152
|
+
type: "button",
|
|
10153
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: iconSize })
|
|
10154
|
+
}
|
|
10155
|
+
),
|
|
10156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10157
|
+
Button,
|
|
10158
|
+
{
|
|
10159
|
+
variant: "ghost",
|
|
10160
|
+
size: "icon",
|
|
10161
|
+
className: cn(
|
|
10162
|
+
buttonSize,
|
|
10163
|
+
"text-destructive hover:text-destructive hover:bg-destructive/10"
|
|
10164
|
+
),
|
|
10165
|
+
onClick: handleDiscard,
|
|
10166
|
+
"aria-label": "Discard image",
|
|
10167
|
+
type: "button",
|
|
10168
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: iconSize })
|
|
10169
|
+
}
|
|
10170
|
+
)
|
|
10171
|
+
]
|
|
10172
|
+
}
|
|
10173
|
+
)
|
|
10174
|
+
] })
|
|
10175
|
+
] }),
|
|
10176
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10177
|
+
"input",
|
|
10178
|
+
{
|
|
10179
|
+
ref: fileInputRef,
|
|
10180
|
+
type: "file",
|
|
10181
|
+
accept: "image/*",
|
|
10182
|
+
onChange: handleFileSelect,
|
|
10183
|
+
className: "hidden",
|
|
10184
|
+
"aria-hidden": "true"
|
|
10185
|
+
}
|
|
10186
|
+
)
|
|
10187
|
+
]
|
|
10188
|
+
}
|
|
10189
|
+
);
|
|
10190
|
+
}
|
|
10191
|
+
function Dialog({
|
|
10192
|
+
...props
|
|
10193
|
+
}) {
|
|
10194
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Root, { "data-slot": "dialog", ...props });
|
|
10195
|
+
}
|
|
10196
|
+
function DialogTrigger({
|
|
10197
|
+
...props
|
|
10198
|
+
}) {
|
|
10199
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
10200
|
+
}
|
|
10201
|
+
function DialogPortal({
|
|
10202
|
+
...props
|
|
10203
|
+
}) {
|
|
10204
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Portal, { "data-slot": "dialog-portal", ...props });
|
|
10205
|
+
}
|
|
10206
|
+
function DialogClose({
|
|
10207
|
+
...props
|
|
10208
|
+
}) {
|
|
10209
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Close, { "data-slot": "dialog-close", ...props });
|
|
10210
|
+
}
|
|
10211
|
+
function DialogOverlay({
|
|
10212
|
+
className,
|
|
10213
|
+
...props
|
|
10214
|
+
}) {
|
|
10215
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10216
|
+
DialogPrimitive__namespace.Overlay,
|
|
10217
|
+
{
|
|
10218
|
+
"data-slot": "dialog-overlay",
|
|
10219
|
+
className: cn(
|
|
10220
|
+
"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",
|
|
10221
|
+
className
|
|
10222
|
+
),
|
|
10223
|
+
...props
|
|
10224
|
+
}
|
|
10225
|
+
);
|
|
10226
|
+
}
|
|
10227
|
+
function DialogContent({
|
|
10228
|
+
className,
|
|
10229
|
+
children,
|
|
10230
|
+
showCloseButton = true,
|
|
10231
|
+
...props
|
|
10232
|
+
}) {
|
|
10233
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
10234
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
10235
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10236
|
+
DialogPrimitive__namespace.Content,
|
|
10237
|
+
{
|
|
10238
|
+
"data-slot": "dialog-content",
|
|
10239
|
+
className: cn(
|
|
10240
|
+
"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",
|
|
10241
|
+
className
|
|
10242
|
+
),
|
|
10243
|
+
...props,
|
|
10244
|
+
children: [
|
|
10245
|
+
children,
|
|
10246
|
+
showCloseButton && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10247
|
+
DialogPrimitive__namespace.Close,
|
|
10248
|
+
{
|
|
10249
|
+
"data-slot": "dialog-close",
|
|
10250
|
+
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",
|
|
10251
|
+
children: [
|
|
10252
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {}),
|
|
10253
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
10254
|
+
]
|
|
10255
|
+
}
|
|
10256
|
+
)
|
|
10257
|
+
]
|
|
10258
|
+
}
|
|
10259
|
+
)
|
|
10260
|
+
] });
|
|
10261
|
+
}
|
|
10262
|
+
function DialogHeader({ className, ...props }) {
|
|
10263
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10264
|
+
"div",
|
|
10265
|
+
{
|
|
10266
|
+
"data-slot": "dialog-header",
|
|
10267
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
10268
|
+
...props
|
|
10269
|
+
}
|
|
10270
|
+
);
|
|
10271
|
+
}
|
|
10272
|
+
function DialogFooter({ className, ...props }) {
|
|
10273
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10274
|
+
"div",
|
|
10275
|
+
{
|
|
10276
|
+
"data-slot": "dialog-footer",
|
|
10277
|
+
className: cn(
|
|
10278
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
10279
|
+
className
|
|
10280
|
+
),
|
|
10281
|
+
...props
|
|
10282
|
+
}
|
|
10283
|
+
);
|
|
10284
|
+
}
|
|
10285
|
+
function DialogTitle({
|
|
10286
|
+
className,
|
|
10287
|
+
...props
|
|
10288
|
+
}) {
|
|
10289
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10290
|
+
DialogPrimitive__namespace.Title,
|
|
10291
|
+
{
|
|
10292
|
+
"data-slot": "dialog-title",
|
|
10293
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
10294
|
+
...props
|
|
10295
|
+
}
|
|
10296
|
+
);
|
|
10297
|
+
}
|
|
10298
|
+
function DialogDescription({
|
|
10299
|
+
className,
|
|
10300
|
+
...props
|
|
10301
|
+
}) {
|
|
10302
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10303
|
+
DialogPrimitive__namespace.Description,
|
|
10304
|
+
{
|
|
10305
|
+
"data-slot": "dialog-description",
|
|
10306
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
10307
|
+
...props
|
|
10308
|
+
}
|
|
10309
|
+
);
|
|
10310
|
+
}
|
|
10311
|
+
function Drawer({
|
|
10312
|
+
...props
|
|
10313
|
+
}) {
|
|
10314
|
+
return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Root, { "data-slot": "drawer", ...props });
|
|
10315
|
+
}
|
|
10316
|
+
function DrawerTrigger({
|
|
10317
|
+
...props
|
|
10318
|
+
}) {
|
|
10319
|
+
return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Trigger, { "data-slot": "drawer-trigger", ...props });
|
|
10320
|
+
}
|
|
10321
|
+
function DrawerPortal({
|
|
10322
|
+
...props
|
|
10323
|
+
}) {
|
|
10324
|
+
return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Portal, { "data-slot": "drawer-portal", ...props });
|
|
10325
|
+
}
|
|
10326
|
+
function DrawerClose({
|
|
10327
|
+
...props
|
|
10328
|
+
}) {
|
|
10329
|
+
return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Close, { "data-slot": "drawer-close", ...props });
|
|
10330
|
+
}
|
|
10331
|
+
function DrawerOverlay({
|
|
10332
|
+
className,
|
|
10333
|
+
...props
|
|
10334
|
+
}) {
|
|
10335
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10336
|
+
vaul.Drawer.Overlay,
|
|
10337
|
+
{
|
|
10338
|
+
"data-slot": "drawer-overlay",
|
|
10339
|
+
className: cn(
|
|
10340
|
+
"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",
|
|
10341
|
+
className
|
|
10342
|
+
),
|
|
10343
|
+
...props
|
|
10344
|
+
}
|
|
10345
|
+
);
|
|
10346
|
+
}
|
|
10347
|
+
function DrawerContent({
|
|
10348
|
+
className,
|
|
10349
|
+
children,
|
|
10350
|
+
...props
|
|
10351
|
+
}) {
|
|
10352
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
|
|
10353
|
+
/* @__PURE__ */ jsxRuntime.jsx(DrawerOverlay, {}),
|
|
10354
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10355
|
+
vaul.Drawer.Content,
|
|
10356
|
+
{
|
|
10357
|
+
"data-slot": "drawer-content",
|
|
10358
|
+
className: cn(
|
|
10359
|
+
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
|
|
10360
|
+
"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",
|
|
10361
|
+
"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",
|
|
10362
|
+
"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",
|
|
10363
|
+
"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",
|
|
10364
|
+
className
|
|
10365
|
+
),
|
|
10366
|
+
...props,
|
|
10367
|
+
children: [
|
|
10368
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
10369
|
+
children
|
|
10370
|
+
]
|
|
10371
|
+
}
|
|
10372
|
+
)
|
|
10373
|
+
] });
|
|
10374
|
+
}
|
|
10375
|
+
function DrawerHeader({ className, ...props }) {
|
|
10376
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10377
|
+
"div",
|
|
10378
|
+
{
|
|
10379
|
+
"data-slot": "drawer-header",
|
|
10380
|
+
className: cn(
|
|
10381
|
+
"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",
|
|
10382
|
+
className
|
|
10383
|
+
),
|
|
10384
|
+
...props
|
|
10385
|
+
}
|
|
10386
|
+
);
|
|
10387
|
+
}
|
|
10388
|
+
function DrawerFooter({ className, ...props }) {
|
|
10389
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10390
|
+
"div",
|
|
10391
|
+
{
|
|
10392
|
+
"data-slot": "drawer-footer",
|
|
10393
|
+
className: cn("mt-auto flex flex-col gap-2 p-4", className),
|
|
10394
|
+
...props
|
|
10395
|
+
}
|
|
10396
|
+
);
|
|
10397
|
+
}
|
|
10398
|
+
function DrawerTitle({
|
|
10399
|
+
className,
|
|
10400
|
+
...props
|
|
10401
|
+
}) {
|
|
10402
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10403
|
+
vaul.Drawer.Title,
|
|
10404
|
+
{
|
|
10405
|
+
"data-slot": "drawer-title",
|
|
10406
|
+
className: cn("text-foreground font-semibold", className),
|
|
10407
|
+
...props
|
|
10408
|
+
}
|
|
10409
|
+
);
|
|
10410
|
+
}
|
|
10411
|
+
function DrawerDescription({
|
|
10412
|
+
className,
|
|
10413
|
+
...props
|
|
10414
|
+
}) {
|
|
10415
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10416
|
+
vaul.Drawer.Description,
|
|
10417
|
+
{
|
|
10418
|
+
"data-slot": "drawer-description",
|
|
10419
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
10420
|
+
...props
|
|
10421
|
+
}
|
|
10422
|
+
);
|
|
10423
|
+
}
|
|
10424
|
+
function useMediaQuery(query) {
|
|
10425
|
+
const [matches, setMatches] = React32.useState(false);
|
|
10426
|
+
React32.useEffect(() => {
|
|
10427
|
+
const media = window.matchMedia(query);
|
|
10428
|
+
if (media.matches !== matches) {
|
|
10429
|
+
setMatches(media.matches);
|
|
10430
|
+
}
|
|
10431
|
+
const listener = () => setMatches(media.matches);
|
|
10432
|
+
media.addEventListener("change", listener);
|
|
10433
|
+
return () => media.removeEventListener("change", listener);
|
|
10434
|
+
}, [matches, query]);
|
|
10435
|
+
return matches;
|
|
10436
|
+
}
|
|
10437
|
+
function AvatarEditorDialog({
|
|
10438
|
+
value,
|
|
10439
|
+
onChange,
|
|
10440
|
+
onSave,
|
|
10441
|
+
displaySize = 120,
|
|
10442
|
+
editorSize = 280,
|
|
10443
|
+
outputSize = 256,
|
|
10444
|
+
placeholder: _placeholder = "Add Photo",
|
|
10445
|
+
editLabel = "Edit avatar",
|
|
10446
|
+
dialogTitle = "Edit Avatar",
|
|
10447
|
+
acceptText = "Accept",
|
|
10448
|
+
cancelText = "Cancel",
|
|
10449
|
+
successMessage = "Avatar saved successfully!",
|
|
10450
|
+
errorMessage = "Failed to save avatar. Please try again.",
|
|
10451
|
+
className
|
|
10452
|
+
}) {
|
|
10453
|
+
const [isOpen, setIsOpen] = React32.useState(false);
|
|
10454
|
+
const [editedValue, setEditedValue] = React32.useState(value ?? null);
|
|
10455
|
+
const [isSaving, setIsSaving] = React32.useState(false);
|
|
10456
|
+
const [feedback, setFeedback] = React32.useState(null);
|
|
10457
|
+
const isMobile = useMediaQuery("(max-width: 640px)");
|
|
10458
|
+
const handleOpenChange = React32.useCallback(
|
|
10459
|
+
(open) => {
|
|
10460
|
+
if (open) {
|
|
10461
|
+
setEditedValue(value ?? null);
|
|
10462
|
+
setFeedback(null);
|
|
10463
|
+
}
|
|
10464
|
+
setIsOpen(open);
|
|
10465
|
+
},
|
|
10466
|
+
[value]
|
|
10467
|
+
);
|
|
10468
|
+
const handleAccept = React32.useCallback(async () => {
|
|
10469
|
+
if (!editedValue) return;
|
|
10470
|
+
setIsSaving(true);
|
|
10471
|
+
setFeedback(null);
|
|
10472
|
+
try {
|
|
10473
|
+
if (onSave) {
|
|
10474
|
+
const result = await onSave(editedValue);
|
|
10475
|
+
if (result) {
|
|
10476
|
+
setFeedback({ type: "success", message: successMessage });
|
|
10477
|
+
setTimeout(() => {
|
|
10478
|
+
setIsOpen(false);
|
|
10479
|
+
setFeedback(null);
|
|
10480
|
+
}, 1500);
|
|
10481
|
+
} else {
|
|
10482
|
+
setFeedback({ type: "error", message: errorMessage });
|
|
10483
|
+
}
|
|
10484
|
+
} else {
|
|
10485
|
+
onChange?.(editedValue);
|
|
10486
|
+
setFeedback({ type: "success", message: successMessage });
|
|
10487
|
+
setTimeout(() => {
|
|
10488
|
+
setIsOpen(false);
|
|
10489
|
+
setFeedback(null);
|
|
10490
|
+
}, 1500);
|
|
10491
|
+
}
|
|
10492
|
+
} catch {
|
|
10493
|
+
setFeedback({ type: "error", message: errorMessage });
|
|
10494
|
+
} finally {
|
|
10495
|
+
setIsSaving(false);
|
|
10496
|
+
}
|
|
10497
|
+
}, [editedValue, onChange, onSave, successMessage, errorMessage]);
|
|
10498
|
+
const AvatarDisplay = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10499
|
+
"div",
|
|
10500
|
+
{
|
|
10501
|
+
className: cn("relative group", className),
|
|
10502
|
+
style: { width: displaySize, height: displaySize },
|
|
10503
|
+
children: [
|
|
10504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10505
|
+
"div",
|
|
10506
|
+
{
|
|
10507
|
+
className: "w-full h-full rounded-full overflow-hidden bg-muted border-2 border-border flex items-center justify-center",
|
|
10508
|
+
style: { width: displaySize, height: displaySize },
|
|
10509
|
+
children: value ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10510
|
+
"img",
|
|
10511
|
+
{
|
|
10512
|
+
src: value || "/placeholder.svg",
|
|
10513
|
+
alt: "Avatar",
|
|
10514
|
+
className: "w-full h-full object-cover"
|
|
10515
|
+
}
|
|
10516
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-10 h-10" }) })
|
|
10517
|
+
}
|
|
10518
|
+
),
|
|
10519
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10520
|
+
"button",
|
|
10521
|
+
{
|
|
10522
|
+
onClick: () => handleOpenChange(true),
|
|
10523
|
+
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",
|
|
10524
|
+
"aria-label": editLabel,
|
|
10525
|
+
type: "button",
|
|
10526
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "w-4 h-4" })
|
|
10527
|
+
}
|
|
10528
|
+
)
|
|
10529
|
+
]
|
|
10530
|
+
}
|
|
10531
|
+
);
|
|
10532
|
+
const mobileEditorSize = isMobile ? Math.min(editorSize + 40, window.innerWidth - 48) : editorSize;
|
|
10533
|
+
const EditorContent = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
10534
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10535
|
+
AvatarEditor,
|
|
10536
|
+
{
|
|
10537
|
+
value: editedValue,
|
|
10538
|
+
onChange: setEditedValue,
|
|
10539
|
+
size: mobileEditorSize,
|
|
10540
|
+
outputSize,
|
|
10541
|
+
controlSize: isMobile ? "large" : "default"
|
|
10542
|
+
}
|
|
10543
|
+
),
|
|
10544
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: feedback && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10545
|
+
react.motion.div,
|
|
10546
|
+
{
|
|
10547
|
+
initial: { opacity: 0, y: -10 },
|
|
10548
|
+
animate: { opacity: 1, y: 0 },
|
|
10549
|
+
exit: { opacity: 0, y: -10 },
|
|
10550
|
+
className: cn(
|
|
10551
|
+
"flex items-center gap-2 rounded-lg font-medium",
|
|
10552
|
+
isMobile ? "px-5 py-3 text-base" : "px-4 py-2 text-sm",
|
|
10553
|
+
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"
|
|
10554
|
+
),
|
|
10555
|
+
children: [
|
|
10556
|
+
feedback.type === "success" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }),
|
|
10557
|
+
feedback.message
|
|
10558
|
+
]
|
|
10559
|
+
}
|
|
10560
|
+
) })
|
|
10561
|
+
] });
|
|
10562
|
+
const FooterButtons = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10563
|
+
isMobile ? /* @__PURE__ */ jsxRuntime.jsx(DrawerClose, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10564
|
+
Button,
|
|
10565
|
+
{
|
|
10566
|
+
variant: "outline",
|
|
10567
|
+
disabled: isSaving,
|
|
10568
|
+
size: "lg",
|
|
10569
|
+
className: "flex-1 bg-transparent",
|
|
10570
|
+
children: cancelText
|
|
10571
|
+
}
|
|
10572
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", disabled: isSaving, children: cancelText }) }),
|
|
10573
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10574
|
+
Button,
|
|
10575
|
+
{
|
|
10576
|
+
onClick: handleAccept,
|
|
10577
|
+
disabled: !editedValue || isSaving || feedback?.type === "success",
|
|
10578
|
+
size: isMobile ? "lg" : "default",
|
|
10579
|
+
className: isMobile ? "flex-1" : "",
|
|
10580
|
+
children: isSaving ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10581
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10582
|
+
lucideReact.Loader2,
|
|
10583
|
+
{
|
|
10584
|
+
className: cn(
|
|
10585
|
+
"mr-2 animate-spin",
|
|
10586
|
+
isMobile ? "w-5 h-5" : "w-4 h-4"
|
|
10587
|
+
)
|
|
10588
|
+
}
|
|
10589
|
+
),
|
|
10590
|
+
"Saving..."
|
|
10591
|
+
] }) : feedback?.type === "success" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10592
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: cn("mr-2", isMobile ? "w-5 h-5" : "w-4 h-4") }),
|
|
10593
|
+
"Saved!"
|
|
10594
|
+
] }) : acceptText
|
|
10595
|
+
}
|
|
10596
|
+
)
|
|
10597
|
+
] });
|
|
10598
|
+
if (isMobile) {
|
|
10599
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10600
|
+
AvatarDisplay,
|
|
10601
|
+
/* @__PURE__ */ jsxRuntime.jsx(Drawer, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DrawerContent, { className: "max-h-[96vh]", children: [
|
|
10602
|
+
/* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "text-center", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { className: "text-xl", children: dialogTitle }) }),
|
|
10603
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 pb-6 overflow-y-auto", children: EditorContent }),
|
|
10604
|
+
/* @__PURE__ */ jsxRuntime.jsx(DrawerFooter, { className: "flex-row gap-3 px-6 pb-8", children: FooterButtons })
|
|
10605
|
+
] }) })
|
|
10606
|
+
] });
|
|
10607
|
+
}
|
|
10608
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10609
|
+
AvatarDisplay,
|
|
10610
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
10611
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: dialogTitle }) }),
|
|
10612
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-4", children: EditorContent }),
|
|
10613
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogFooter, { className: "gap-2 sm:gap-0", children: FooterButtons })
|
|
10614
|
+
] }) })
|
|
10615
|
+
] });
|
|
10616
|
+
}
|
|
9531
10617
|
|
|
10618
|
+
exports.AvatarEditor = AvatarEditor;
|
|
10619
|
+
exports.AvatarEditorDialog = AvatarEditorDialog;
|
|
9532
10620
|
exports.Button = Button;
|
|
10621
|
+
exports.Dialog = Dialog;
|
|
10622
|
+
exports.DialogClose = DialogClose;
|
|
10623
|
+
exports.DialogContent = DialogContent;
|
|
10624
|
+
exports.DialogDescription = DialogDescription;
|
|
10625
|
+
exports.DialogFooter = DialogFooter;
|
|
10626
|
+
exports.DialogHeader = DialogHeader;
|
|
10627
|
+
exports.DialogOverlay = DialogOverlay;
|
|
10628
|
+
exports.DialogPortal = DialogPortal;
|
|
10629
|
+
exports.DialogTitle = DialogTitle;
|
|
10630
|
+
exports.DialogTrigger = DialogTrigger;
|
|
10631
|
+
exports.Drawer = Drawer;
|
|
10632
|
+
exports.DrawerClose = DrawerClose;
|
|
10633
|
+
exports.DrawerContent = DrawerContent;
|
|
10634
|
+
exports.DrawerDescription = DrawerDescription;
|
|
10635
|
+
exports.DrawerFooter = DrawerFooter;
|
|
10636
|
+
exports.DrawerHeader = DrawerHeader;
|
|
10637
|
+
exports.DrawerOverlay = DrawerOverlay;
|
|
10638
|
+
exports.DrawerPortal = DrawerPortal;
|
|
10639
|
+
exports.DrawerTitle = DrawerTitle;
|
|
10640
|
+
exports.DrawerTrigger = DrawerTrigger;
|
|
9533
10641
|
exports.DropdownMenu = DropdownMenu2;
|
|
9534
10642
|
exports.DropdownMenuCheckboxItem = DropdownMenuCheckboxItem2;
|
|
9535
10643
|
exports.DropdownMenuContent = DropdownMenuContent2;
|
|
@@ -9546,12 +10654,15 @@ exports.DropdownMenuSubContent = DropdownMenuSubContent2;
|
|
|
9546
10654
|
exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger2;
|
|
9547
10655
|
exports.DropdownMenuTrigger = DropdownMenuTrigger2;
|
|
9548
10656
|
exports.LanguageSwitcher = LanguageSwitcher;
|
|
10657
|
+
exports.Slider = Slider;
|
|
9549
10658
|
exports.ThemeSwitcher = ThemeSwitcher;
|
|
10659
|
+
exports.Toggle = Toggle;
|
|
9550
10660
|
exports.Tooltip = Tooltip2;
|
|
9551
10661
|
exports.TooltipContent = TooltipContent2;
|
|
9552
10662
|
exports.TooltipProvider = TooltipProvider2;
|
|
9553
10663
|
exports.TooltipTrigger = TooltipTrigger2;
|
|
9554
10664
|
exports.buttonVariants = buttonVariants;
|
|
9555
10665
|
exports.cn = cn;
|
|
10666
|
+
exports.toggleVariants = toggleVariants;
|
|
9556
10667
|
//# sourceMappingURL=index.cjs.map
|
|
9557
10668
|
//# sourceMappingURL=index.cjs.map
|