@yoamigo.com/core 0.1.15 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +89 -4
- package/dist/index.js +1007 -103
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1219,7 +1219,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1219
1219
|
processingRef.current = true;
|
|
1220
1220
|
const fieldId = queueRef.current.shift();
|
|
1221
1221
|
const state = animationsRef.current.get(fieldId);
|
|
1222
|
-
if (state
|
|
1222
|
+
if (state?.status === "pending") {
|
|
1223
1223
|
state.status = "animating";
|
|
1224
1224
|
state.startTime = performance.now();
|
|
1225
1225
|
notifyListeners(fieldId);
|
|
@@ -1232,7 +1232,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1232
1232
|
const queueAnimation = useCallback3(
|
|
1233
1233
|
(fieldId, config) => {
|
|
1234
1234
|
const existing = animationsRef.current.get(fieldId);
|
|
1235
|
-
if (existing
|
|
1235
|
+
if (existing?.status === "animating") {
|
|
1236
1236
|
animationsRef.current.delete(fieldId);
|
|
1237
1237
|
}
|
|
1238
1238
|
const state = {
|
|
@@ -2508,7 +2508,7 @@ function YaTooltip({
|
|
|
2508
2508
|
}
|
|
2509
2509
|
|
|
2510
2510
|
// src/components/ya-image.css
|
|
2511
|
-
styleInject('.ya-image-container {\n position: relative;\n display: inline-block;\n min-width: 45px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-image-container img {\n display: block;\n}\n.ya-image-editable {\n cursor: pointer;\n}\n.ya-image-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-image-editable:hover .ya-image-overlay {\n opacity: 1;\n}\n.ya-image-selected .ya-image-overlay {\n opacity: 0;\n}\n.ya-image-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-image-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-image-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n@keyframes ya-image-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-image-success {\n animation: ya-image-success 0.4s ease;\n}\n.ya-image-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-image-shimmer 1.5s infinite;\n}\n@keyframes ya-image-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-image-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-image-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-small .ya-image-overlay {\n display: none;\n}\n');
|
|
2511
|
+
styleInject('.ya-image-container {\n position: relative;\n display: inline-block;\n min-width: 45px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-image-container img {\n display: block;\n}\n.ya-image-editable {\n cursor: pointer;\n}\n.ya-image-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-image-editable:hover .ya-image-overlay {\n opacity: 1;\n}\n.ya-image-selected .ya-image-overlay {\n opacity: 0;\n}\n.ya-image-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-image-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-image-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n@keyframes ya-image-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-image-success {\n animation: ya-image-success 0.4s ease;\n}\n.ya-image-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-image-shimmer 1.5s infinite;\n}\n@keyframes ya-image-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-image-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-image-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-small .ya-image-overlay {\n display: none;\n}\n.ya-image-drop-target {\n outline: 2px dashed var(--ya-drop-color, #3b82f6) !important;\n outline-offset: 4px;\n}\n.ya-image-drop-target .ya-image-overlay {\n display: none !important;\n}\n.ya-image-drop-hover {\n outline: 3px solid var(--ya-drop-color, #3b82f6) !important;\n outline-offset: 4px;\n background-color: rgba(59, 130, 246, 0.1);\n}\n.ya-image-drop-hover::before {\n content: "";\n position: absolute;\n inset: -4px;\n border: 2px solid var(--ya-drop-color, #3b82f6);\n border-radius: inherit;\n animation: ya-drop-pulse 1s ease-in-out infinite;\n pointer-events: none;\n}\n@keyframes ya-drop-pulse {\n 0%, 100% {\n opacity: 0.4;\n transform: scale(1);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.02);\n }\n}\n');
|
|
2512
2512
|
|
|
2513
2513
|
// src/components/YaImage.tsx
|
|
2514
2514
|
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -2561,6 +2561,8 @@ function YaImage({
|
|
|
2561
2561
|
const [isSelected, setIsSelected] = useState6(false);
|
|
2562
2562
|
const [isHovered, setIsHovered] = useState6(false);
|
|
2563
2563
|
const [isSmallImage, setIsSmallImage] = useState6(false);
|
|
2564
|
+
const [isDropMode, setIsDropMode] = useState6(false);
|
|
2565
|
+
const [isDropHover, setIsDropHover] = useState6(false);
|
|
2564
2566
|
const rawValue = getValue(fieldId);
|
|
2565
2567
|
const imageData = parseImageValue(rawValue);
|
|
2566
2568
|
const src = imageData.src || fallbackSrc || PLACEHOLDER_SVG;
|
|
@@ -2610,6 +2612,84 @@ function YaImage({
|
|
|
2610
2612
|
window.addEventListener("message", handleMessage2);
|
|
2611
2613
|
return () => window.removeEventListener("message", handleMessage2);
|
|
2612
2614
|
}, [mode, fieldId]);
|
|
2615
|
+
useEffect6(() => {
|
|
2616
|
+
if (mode !== "inline-edit") return;
|
|
2617
|
+
const handleDropModeMessage = (event) => {
|
|
2618
|
+
if (event.data?.type === "DROP_MODE_START") {
|
|
2619
|
+
setIsDropMode(true);
|
|
2620
|
+
}
|
|
2621
|
+
if (event.data?.type === "DROP_MODE_END") {
|
|
2622
|
+
setIsDropMode(false);
|
|
2623
|
+
setIsDropHover(false);
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
window.addEventListener("message", handleDropModeMessage);
|
|
2627
|
+
return () => window.removeEventListener("message", handleDropModeMessage);
|
|
2628
|
+
}, [mode]);
|
|
2629
|
+
const handleDragEnter = useCallback7(
|
|
2630
|
+
(e) => {
|
|
2631
|
+
if (!isDropMode) return;
|
|
2632
|
+
e.preventDefault();
|
|
2633
|
+
e.stopPropagation();
|
|
2634
|
+
setIsDropHover(true);
|
|
2635
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
2636
|
+
window.parent.postMessage(
|
|
2637
|
+
{
|
|
2638
|
+
type: "DROP_ZONE_ENTER",
|
|
2639
|
+
fieldId,
|
|
2640
|
+
zoneType: "image",
|
|
2641
|
+
rect: rect ? {
|
|
2642
|
+
top: rect.top,
|
|
2643
|
+
left: rect.left,
|
|
2644
|
+
width: rect.width,
|
|
2645
|
+
height: rect.height
|
|
2646
|
+
} : { top: 0, left: 0, width: 0, height: 0 }
|
|
2647
|
+
},
|
|
2648
|
+
"*"
|
|
2649
|
+
);
|
|
2650
|
+
},
|
|
2651
|
+
[isDropMode, fieldId]
|
|
2652
|
+
);
|
|
2653
|
+
const handleDragOver = useCallback7(
|
|
2654
|
+
(e) => {
|
|
2655
|
+
if (!isDropMode) return;
|
|
2656
|
+
e.preventDefault();
|
|
2657
|
+
e.stopPropagation();
|
|
2658
|
+
},
|
|
2659
|
+
[isDropMode]
|
|
2660
|
+
);
|
|
2661
|
+
const handleDragLeave = useCallback7(
|
|
2662
|
+
(e) => {
|
|
2663
|
+
if (!isDropMode) return;
|
|
2664
|
+
e.preventDefault();
|
|
2665
|
+
e.stopPropagation();
|
|
2666
|
+
const target = e.currentTarget;
|
|
2667
|
+
const relatedTarget = e.relatedTarget;
|
|
2668
|
+
if (!relatedTarget || !target.contains(relatedTarget)) {
|
|
2669
|
+
setIsDropHover(false);
|
|
2670
|
+
window.parent.postMessage({ type: "DROP_ZONE_LEAVE" }, "*");
|
|
2671
|
+
}
|
|
2672
|
+
},
|
|
2673
|
+
[isDropMode]
|
|
2674
|
+
);
|
|
2675
|
+
const handleDrop = useCallback7(
|
|
2676
|
+
(e) => {
|
|
2677
|
+
if (!isDropMode) return;
|
|
2678
|
+
e.preventDefault();
|
|
2679
|
+
e.stopPropagation();
|
|
2680
|
+
setIsDropHover(false);
|
|
2681
|
+
setIsDropMode(false);
|
|
2682
|
+
window.parent.postMessage(
|
|
2683
|
+
{
|
|
2684
|
+
type: "DROP_ZONE_DROP",
|
|
2685
|
+
fieldId,
|
|
2686
|
+
zoneType: "image"
|
|
2687
|
+
},
|
|
2688
|
+
"*"
|
|
2689
|
+
);
|
|
2690
|
+
},
|
|
2691
|
+
[isDropMode, fieldId]
|
|
2692
|
+
);
|
|
2613
2693
|
useEffect6(() => {
|
|
2614
2694
|
if (mode !== "inline-edit") return;
|
|
2615
2695
|
const checkSize = () => {
|
|
@@ -2701,14 +2781,25 @@ function YaImage({
|
|
|
2701
2781
|
]
|
|
2702
2782
|
}
|
|
2703
2783
|
);
|
|
2784
|
+
const containerClasses = [
|
|
2785
|
+
"ya-image-container",
|
|
2786
|
+
isSelected ? "ya-image-selected" : "ya-image-editable",
|
|
2787
|
+
isSmallImage ? "ya-image-small" : "",
|
|
2788
|
+
isDropMode ? "ya-image-drop-target" : "",
|
|
2789
|
+
isDropHover ? "ya-image-drop-hover" : ""
|
|
2790
|
+
].filter(Boolean).join(" ");
|
|
2704
2791
|
return /* @__PURE__ */ jsxs3(
|
|
2705
2792
|
"div",
|
|
2706
2793
|
{
|
|
2707
2794
|
ref: containerRef,
|
|
2708
|
-
className:
|
|
2795
|
+
className: containerClasses,
|
|
2709
2796
|
onClick: handleClick,
|
|
2710
2797
|
onMouseEnter: () => setIsHovered(true),
|
|
2711
2798
|
onMouseLeave: () => setIsHovered(false),
|
|
2799
|
+
onDragEnter: handleDragEnter,
|
|
2800
|
+
onDragOver: handleDragOver,
|
|
2801
|
+
onDragLeave: handleDragLeave,
|
|
2802
|
+
onDrop: handleDrop,
|
|
2712
2803
|
"data-ya-restricted": "true",
|
|
2713
2804
|
"data-field-id": fieldId,
|
|
2714
2805
|
"data-ya-image": "true",
|
|
@@ -2751,8 +2842,393 @@ function YaImage({
|
|
|
2751
2842
|
);
|
|
2752
2843
|
}
|
|
2753
2844
|
|
|
2845
|
+
// src/components/YaVideo.tsx
|
|
2846
|
+
import { useCallback as useCallback8, useEffect as useEffect7, useRef as useRef8, useState as useState7 } from "react";
|
|
2847
|
+
|
|
2848
|
+
// src/components/ya-video.css
|
|
2849
|
+
styleInject('.ya-video-wrapper {\n position: relative;\n display: block;\n width: 100%;\n}\n.ya-video-wrapper video,\n.ya-video-wrapper iframe {\n display: block;\n width: 100%;\n height: 100%;\n}\n.ya-video-container {\n position: relative;\n display: block;\n width: 100%;\n min-width: 80px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-video-container video,\n.ya-video-container iframe {\n display: block;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.ya-video-editable {\n cursor: pointer;\n}\n.ya-video-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-video-editable:hover .ya-video-overlay {\n opacity: 1;\n}\n.ya-video-selected .ya-video-overlay {\n opacity: 0;\n}\n.ya-video-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-video-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-video-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n.ya-video-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n height: 100%;\n min-height: 120px;\n background: #f3f4f6;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n color: #6b7280;\n font-size: 14px;\n}\n.ya-video-placeholder img {\n width: 64px;\n height: auto;\n opacity: 0.5;\n}\n@keyframes ya-video-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-video-success {\n animation: ya-video-success 0.4s ease;\n}\n.ya-video-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-video-shimmer 1.5s infinite;\n}\n@keyframes ya-video-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-video-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-video-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-small .ya-video-overlay {\n display: none;\n}\n.ya-video-background {\n position: absolute;\n inset: 0;\n z-index: -1;\n}\n.ya-video-background video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n');
|
|
2850
|
+
|
|
2851
|
+
// src/components/YaVideo.tsx
|
|
2852
|
+
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2853
|
+
function parseVideoValue(value) {
|
|
2854
|
+
if (!value) {
|
|
2855
|
+
return { type: "upload", src: "" };
|
|
2856
|
+
}
|
|
2857
|
+
try {
|
|
2858
|
+
const parsed = JSON.parse(value);
|
|
2859
|
+
if (typeof parsed === "object" && parsed.src) {
|
|
2860
|
+
return {
|
|
2861
|
+
type: parsed.type || "upload",
|
|
2862
|
+
...parsed
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
} catch {
|
|
2866
|
+
}
|
|
2867
|
+
return { type: "upload", src: value };
|
|
2868
|
+
}
|
|
2869
|
+
function serializeVideoValue(value) {
|
|
2870
|
+
return JSON.stringify(value);
|
|
2871
|
+
}
|
|
2872
|
+
function buildYouTubeEmbedUrl(videoId, value) {
|
|
2873
|
+
const params = new URLSearchParams({
|
|
2874
|
+
rel: "0",
|
|
2875
|
+
modestbranding: "1"
|
|
2876
|
+
});
|
|
2877
|
+
if (value.autoplay) params.set("autoplay", "1");
|
|
2878
|
+
if (value.muted) params.set("mute", "1");
|
|
2879
|
+
if (value.loop) {
|
|
2880
|
+
params.set("loop", "1");
|
|
2881
|
+
params.set("playlist", videoId);
|
|
2882
|
+
}
|
|
2883
|
+
if (value.controls === false) params.set("controls", "0");
|
|
2884
|
+
if (value.startTime) params.set("start", String(Math.floor(value.startTime)));
|
|
2885
|
+
if (value.endTime) params.set("end", String(Math.floor(value.endTime)));
|
|
2886
|
+
return `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
|
|
2887
|
+
}
|
|
2888
|
+
function buildVimeoEmbedUrl(videoId, value) {
|
|
2889
|
+
const params = new URLSearchParams({
|
|
2890
|
+
title: "0",
|
|
2891
|
+
byline: "0",
|
|
2892
|
+
portrait: "0"
|
|
2893
|
+
});
|
|
2894
|
+
if (value.autoplay) params.set("autoplay", "1");
|
|
2895
|
+
if (value.muted) params.set("muted", "1");
|
|
2896
|
+
if (value.loop) params.set("loop", "1");
|
|
2897
|
+
if (value.controls === false) params.set("controls", "0");
|
|
2898
|
+
let url = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
|
|
2899
|
+
if (value.startTime) {
|
|
2900
|
+
url += `#t=${Math.floor(value.startTime)}s`;
|
|
2901
|
+
}
|
|
2902
|
+
return url;
|
|
2903
|
+
}
|
|
2904
|
+
var SMALL_VIDEO_THRESHOLD = 100;
|
|
2905
|
+
var PLACEHOLDER_SVG2 = `data:image/svg+xml,${encodeURIComponent(`
|
|
2906
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="225" viewBox="0 0 400 225">
|
|
2907
|
+
<rect fill="#e5e7eb" width="400" height="225"/>
|
|
2908
|
+
<g fill="#9ca3af" transform="translate(175, 87)">
|
|
2909
|
+
<rect x="0" y="5" width="35" height="25" rx="3" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
2910
|
+
<polygon points="40,17.5 50,10 50,25" fill="currentColor"/>
|
|
2911
|
+
</g>
|
|
2912
|
+
</svg>
|
|
2913
|
+
`)}`;
|
|
2914
|
+
function YaVideo({
|
|
2915
|
+
fieldId,
|
|
2916
|
+
className,
|
|
2917
|
+
aspectRatio: propAspectRatio,
|
|
2918
|
+
objectFit: propObjectFit,
|
|
2919
|
+
loading = "lazy",
|
|
2920
|
+
fallbackSrc,
|
|
2921
|
+
fallbackPoster
|
|
2922
|
+
}) {
|
|
2923
|
+
const { getValue, mode } = useContentStore();
|
|
2924
|
+
const containerRef = useRef8(null);
|
|
2925
|
+
const videoRef = useRef8(null);
|
|
2926
|
+
const [isSelected, setIsSelected] = useState7(false);
|
|
2927
|
+
const [isHovered, setIsHovered] = useState7(false);
|
|
2928
|
+
const [isSmallVideo, setIsSmallVideo] = useState7(false);
|
|
2929
|
+
const [isInView, setIsInView] = useState7(loading === "eager");
|
|
2930
|
+
const rawValue = getValue(fieldId);
|
|
2931
|
+
const videoData = parseVideoValue(rawValue);
|
|
2932
|
+
const src = videoData.src || fallbackSrc || "";
|
|
2933
|
+
const poster = videoData.poster || fallbackPoster || "";
|
|
2934
|
+
const objectFit = videoData.objectFit || propObjectFit || "cover";
|
|
2935
|
+
const aspectRatio = videoData.aspectRatio || propAspectRatio || "16/9";
|
|
2936
|
+
const autoplay = videoData.autoplay ?? false;
|
|
2937
|
+
const muted = videoData.muted ?? false;
|
|
2938
|
+
const loop = videoData.loop ?? false;
|
|
2939
|
+
const controls = videoData.controls ?? true;
|
|
2940
|
+
const playsinline = videoData.playsinline ?? true;
|
|
2941
|
+
const preload = videoData.preload ?? "metadata";
|
|
2942
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState7(false);
|
|
2943
|
+
useEffect7(() => {
|
|
2944
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
2945
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
2946
|
+
const handleChange = (e) => {
|
|
2947
|
+
setPrefersReducedMotion(e.matches);
|
|
2948
|
+
};
|
|
2949
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
2950
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
2951
|
+
}, []);
|
|
2952
|
+
const effectiveAutoplay = autoplay && !prefersReducedMotion;
|
|
2953
|
+
useEffect7(() => {
|
|
2954
|
+
if (loading === "eager" || isInView) return;
|
|
2955
|
+
const observer = new IntersectionObserver(
|
|
2956
|
+
(entries) => {
|
|
2957
|
+
if (entries[0]?.isIntersecting) {
|
|
2958
|
+
setIsInView(true);
|
|
2959
|
+
observer.disconnect();
|
|
2960
|
+
}
|
|
2961
|
+
},
|
|
2962
|
+
{ rootMargin: "200px" }
|
|
2963
|
+
// Start loading 200px before visible
|
|
2964
|
+
);
|
|
2965
|
+
if (containerRef.current) {
|
|
2966
|
+
observer.observe(containerRef.current);
|
|
2967
|
+
}
|
|
2968
|
+
return () => observer.disconnect();
|
|
2969
|
+
}, [loading, isInView]);
|
|
2970
|
+
const handleKeyDown = useCallback8(
|
|
2971
|
+
(e) => {
|
|
2972
|
+
if ((e.key === " " || e.key === "Enter") && videoData.type === "upload" && controls) {
|
|
2973
|
+
e.preventDefault();
|
|
2974
|
+
const video = videoRef.current;
|
|
2975
|
+
if (video) {
|
|
2976
|
+
if (video.paused) {
|
|
2977
|
+
video.play();
|
|
2978
|
+
} else {
|
|
2979
|
+
video.pause();
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
},
|
|
2984
|
+
[videoData.type, controls]
|
|
2985
|
+
);
|
|
2986
|
+
const handleClick = useCallback8(() => {
|
|
2987
|
+
if (mode !== "inline-edit") return;
|
|
2988
|
+
if (document.body.classList.contains("builder-selector-active")) return;
|
|
2989
|
+
setIsSelected(true);
|
|
2990
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
2991
|
+
window.parent.postMessage(
|
|
2992
|
+
{
|
|
2993
|
+
type: "YA_VIDEO_EDIT_REQUEST",
|
|
2994
|
+
fieldId,
|
|
2995
|
+
currentValue: videoData,
|
|
2996
|
+
elementRect: rect ? {
|
|
2997
|
+
top: rect.top,
|
|
2998
|
+
left: rect.left,
|
|
2999
|
+
width: rect.width,
|
|
3000
|
+
height: rect.height
|
|
3001
|
+
} : void 0
|
|
3002
|
+
},
|
|
3003
|
+
"*"
|
|
3004
|
+
);
|
|
3005
|
+
}, [mode, fieldId, videoData]);
|
|
3006
|
+
useEffect7(() => {
|
|
3007
|
+
if (mode !== "inline-edit") return;
|
|
3008
|
+
const handleMessage2 = (event) => {
|
|
3009
|
+
if (event.data?.type === "YA_VIDEO_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
3010
|
+
setIsSelected(false);
|
|
3011
|
+
}
|
|
3012
|
+
if (event.data?.type === "YA_VIDEO_EDIT_CANCEL" && event.data.fieldId === fieldId) {
|
|
3013
|
+
setIsSelected(false);
|
|
3014
|
+
}
|
|
3015
|
+
};
|
|
3016
|
+
window.addEventListener("message", handleMessage2);
|
|
3017
|
+
return () => window.removeEventListener("message", handleMessage2);
|
|
3018
|
+
}, [mode, fieldId]);
|
|
3019
|
+
useEffect7(() => {
|
|
3020
|
+
if (mode !== "inline-edit") return;
|
|
3021
|
+
const checkSize = () => {
|
|
3022
|
+
if (containerRef.current) {
|
|
3023
|
+
const { width, height } = containerRef.current.getBoundingClientRect();
|
|
3024
|
+
setIsSmallVideo(width < SMALL_VIDEO_THRESHOLD || height < SMALL_VIDEO_THRESHOLD);
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
checkSize();
|
|
3028
|
+
window.addEventListener("resize", checkSize);
|
|
3029
|
+
return () => window.removeEventListener("resize", checkSize);
|
|
3030
|
+
}, [mode]);
|
|
3031
|
+
useEffect7(() => {
|
|
3032
|
+
if (!isSelected || mode !== "inline-edit") return;
|
|
3033
|
+
let lastRectKey = "";
|
|
3034
|
+
let lastTime = 0;
|
|
3035
|
+
let rafId;
|
|
3036
|
+
const loop2 = (time) => {
|
|
3037
|
+
if (time - lastTime >= 50) {
|
|
3038
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
3039
|
+
if (rect) {
|
|
3040
|
+
const rectKey = `${Math.round(rect.top)},${Math.round(rect.left)},${Math.round(rect.width)},${Math.round(rect.height)}`;
|
|
3041
|
+
if (rectKey !== lastRectKey) {
|
|
3042
|
+
lastRectKey = rectKey;
|
|
3043
|
+
window.parent.postMessage(
|
|
3044
|
+
{
|
|
3045
|
+
type: "YA_VIDEO_POSITION_UPDATE",
|
|
3046
|
+
fieldId,
|
|
3047
|
+
elementRect: {
|
|
3048
|
+
top: rect.top,
|
|
3049
|
+
left: rect.left,
|
|
3050
|
+
width: rect.width,
|
|
3051
|
+
height: rect.height
|
|
3052
|
+
}
|
|
3053
|
+
},
|
|
3054
|
+
"*"
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
lastTime = time;
|
|
3059
|
+
}
|
|
3060
|
+
rafId = requestAnimationFrame(loop2);
|
|
3061
|
+
};
|
|
3062
|
+
rafId = requestAnimationFrame(loop2);
|
|
3063
|
+
return () => cancelAnimationFrame(rafId);
|
|
3064
|
+
}, [isSelected, fieldId, mode]);
|
|
3065
|
+
const renderVideo = (isReadOnly) => {
|
|
3066
|
+
if (!src && !isReadOnly) {
|
|
3067
|
+
return /* @__PURE__ */ jsxs4("div", { className: "ya-video-placeholder", children: [
|
|
3068
|
+
/* @__PURE__ */ jsx8("img", { src: PLACEHOLDER_SVG2, alt: "" }),
|
|
3069
|
+
/* @__PURE__ */ jsx8("span", { children: "No video selected" })
|
|
3070
|
+
] });
|
|
3071
|
+
}
|
|
3072
|
+
if (!isInView && loading === "lazy" && !isReadOnly) {
|
|
3073
|
+
return /* @__PURE__ */ jsx8(
|
|
3074
|
+
"div",
|
|
3075
|
+
{
|
|
3076
|
+
className: "ya-video-placeholder",
|
|
3077
|
+
style: { aspectRatio },
|
|
3078
|
+
children: /* @__PURE__ */ jsx8("img", { src: PLACEHOLDER_SVG2, alt: "" })
|
|
3079
|
+
}
|
|
3080
|
+
);
|
|
3081
|
+
}
|
|
3082
|
+
if (videoData.type === "youtube" && src) {
|
|
3083
|
+
const embedUrl = buildYouTubeEmbedUrl(src, videoData);
|
|
3084
|
+
return /* @__PURE__ */ jsx8(
|
|
3085
|
+
"iframe",
|
|
3086
|
+
{
|
|
3087
|
+
src: embedUrl,
|
|
3088
|
+
className,
|
|
3089
|
+
style: {
|
|
3090
|
+
width: "100%",
|
|
3091
|
+
height: "100%",
|
|
3092
|
+
border: "none",
|
|
3093
|
+
aspectRatio,
|
|
3094
|
+
objectFit
|
|
3095
|
+
},
|
|
3096
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
3097
|
+
allowFullScreen: true,
|
|
3098
|
+
loading
|
|
3099
|
+
}
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
if (videoData.type === "vimeo" && src) {
|
|
3103
|
+
const embedUrl = buildVimeoEmbedUrl(src, videoData);
|
|
3104
|
+
return /* @__PURE__ */ jsx8(
|
|
3105
|
+
"iframe",
|
|
3106
|
+
{
|
|
3107
|
+
src: embedUrl,
|
|
3108
|
+
className,
|
|
3109
|
+
style: {
|
|
3110
|
+
width: "100%",
|
|
3111
|
+
height: "100%",
|
|
3112
|
+
border: "none",
|
|
3113
|
+
aspectRatio,
|
|
3114
|
+
objectFit
|
|
3115
|
+
},
|
|
3116
|
+
allow: "autoplay; fullscreen; picture-in-picture",
|
|
3117
|
+
allowFullScreen: true,
|
|
3118
|
+
loading
|
|
3119
|
+
}
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
const resolvedSrc = resolveAssetUrl(src);
|
|
3123
|
+
const resolvedPoster = poster ? resolveAssetUrl(poster) : void 0;
|
|
3124
|
+
return /* @__PURE__ */ jsx8(
|
|
3125
|
+
"video",
|
|
3126
|
+
{
|
|
3127
|
+
ref: videoRef,
|
|
3128
|
+
src: resolvedSrc,
|
|
3129
|
+
poster: resolvedPoster,
|
|
3130
|
+
className,
|
|
3131
|
+
style: {
|
|
3132
|
+
objectFit,
|
|
3133
|
+
aspectRatio
|
|
3134
|
+
},
|
|
3135
|
+
autoPlay: effectiveAutoplay,
|
|
3136
|
+
muted,
|
|
3137
|
+
loop,
|
|
3138
|
+
controls: controls && isReadOnly,
|
|
3139
|
+
playsInline: playsinline,
|
|
3140
|
+
preload,
|
|
3141
|
+
onLoadedMetadata: (e) => {
|
|
3142
|
+
if (videoData.startTime) {
|
|
3143
|
+
e.currentTarget.currentTime = videoData.startTime;
|
|
3144
|
+
}
|
|
3145
|
+
},
|
|
3146
|
+
onTimeUpdate: (e) => {
|
|
3147
|
+
if (videoData.endTime && e.currentTarget.currentTime >= videoData.endTime) {
|
|
3148
|
+
if (loop) {
|
|
3149
|
+
e.currentTarget.currentTime = videoData.startTime || 0;
|
|
3150
|
+
} else {
|
|
3151
|
+
e.currentTarget.pause();
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
);
|
|
3157
|
+
};
|
|
3158
|
+
if (mode === "read-only") {
|
|
3159
|
+
return /* @__PURE__ */ jsx8(
|
|
3160
|
+
"div",
|
|
3161
|
+
{
|
|
3162
|
+
ref: containerRef,
|
|
3163
|
+
className: `ya-video-wrapper ${className || ""}`,
|
|
3164
|
+
style: { aspectRatio },
|
|
3165
|
+
"data-ya-restricted": "true",
|
|
3166
|
+
"data-field-id": fieldId,
|
|
3167
|
+
tabIndex: videoData.type === "upload" && controls ? 0 : void 0,
|
|
3168
|
+
role: videoData.type === "upload" && controls ? "application" : void 0,
|
|
3169
|
+
"aria-label": videoData.type === "upload" && controls ? "Video player. Press Space or Enter to play/pause." : void 0,
|
|
3170
|
+
onKeyDown: handleKeyDown,
|
|
3171
|
+
children: renderVideo(true)
|
|
3172
|
+
}
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3175
|
+
const videoIcon = /* @__PURE__ */ jsxs4(
|
|
3176
|
+
"svg",
|
|
3177
|
+
{
|
|
3178
|
+
width: "24",
|
|
3179
|
+
height: "24",
|
|
3180
|
+
viewBox: "0 0 24 24",
|
|
3181
|
+
fill: "none",
|
|
3182
|
+
stroke: "currentColor",
|
|
3183
|
+
strokeWidth: "2",
|
|
3184
|
+
strokeLinecap: "round",
|
|
3185
|
+
strokeLinejoin: "round",
|
|
3186
|
+
children: [
|
|
3187
|
+
/* @__PURE__ */ jsx8("rect", { x: "2", y: "4", width: "15", height: "13", rx: "2", ry: "2" }),
|
|
3188
|
+
/* @__PURE__ */ jsx8("polygon", { points: "22 7 15 12 22 17 22 7", fill: "currentColor" })
|
|
3189
|
+
]
|
|
3190
|
+
}
|
|
3191
|
+
);
|
|
3192
|
+
return /* @__PURE__ */ jsxs4(
|
|
3193
|
+
"div",
|
|
3194
|
+
{
|
|
3195
|
+
ref: containerRef,
|
|
3196
|
+
className: `ya-video-container ${isSelected ? "ya-video-selected" : "ya-video-editable"} ${isSmallVideo ? "ya-video-small" : ""}`,
|
|
3197
|
+
onClick: handleClick,
|
|
3198
|
+
onMouseEnter: () => setIsHovered(true),
|
|
3199
|
+
onMouseLeave: () => setIsHovered(false),
|
|
3200
|
+
"data-ya-restricted": "true",
|
|
3201
|
+
"data-field-id": fieldId,
|
|
3202
|
+
"data-ya-video": "true",
|
|
3203
|
+
role: "button",
|
|
3204
|
+
tabIndex: 0,
|
|
3205
|
+
"aria-label": `Edit video: ${fieldId}`,
|
|
3206
|
+
onKeyDown: (e) => {
|
|
3207
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3208
|
+
e.preventDefault();
|
|
3209
|
+
handleClick();
|
|
3210
|
+
}
|
|
3211
|
+
},
|
|
3212
|
+
style: { aspectRatio },
|
|
3213
|
+
children: [
|
|
3214
|
+
renderVideo(false),
|
|
3215
|
+
isSmallVideo ? /* @__PURE__ */ jsxs4(YaTooltip, { anchorRef: containerRef, show: isHovered && !isSelected, children: [
|
|
3216
|
+
videoIcon,
|
|
3217
|
+
/* @__PURE__ */ jsx8("span", { children: "Click to edit" })
|
|
3218
|
+
] }) : (
|
|
3219
|
+
/* For large videos: show overlay inside the container */
|
|
3220
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-video-overlay", children: [
|
|
3221
|
+
/* @__PURE__ */ jsx8("div", { className: "ya-video-edit-icon", children: videoIcon }),
|
|
3222
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-video-edit-label", children: "Click to edit" })
|
|
3223
|
+
] })
|
|
3224
|
+
)
|
|
3225
|
+
]
|
|
3226
|
+
}
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
|
|
2754
3230
|
// src/components/YaLink.tsx
|
|
2755
|
-
import { useEffect as
|
|
3231
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState8, useCallback as useCallback9 } from "react";
|
|
2756
3232
|
import { createPortal as createPortal4 } from "react-dom";
|
|
2757
3233
|
import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
|
|
2758
3234
|
import { BubbleMenu as BubbleMenu2 } from "@tiptap/react/menus";
|
|
@@ -2765,7 +3241,7 @@ import { Link as WouterLink, useLocation } from "wouter";
|
|
|
2765
3241
|
styleInject('.ya-link-wrapper {\n position: relative;\n display: inline;\n}\n.ya-link-editable {\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-link-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n}\nbody.builder-selector-active .ya-link-editable:hover {\n outline: none;\n cursor: inherit;\n}\n.ya-link-editing {\n outline: 2px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n position: relative;\n}\n.ya-link-actions {\n display: flex;\n gap: 8px;\n position: absolute;\n bottom: -60px;\n right: 0;\n z-index: 10;\n background: rgba(26, 26, 26, 0.95);\n padding: 8px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n.ya-link-btn {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n border: none;\n}\n.ya-link-btn-cancel {\n background: #333333;\n color: #ffffff;\n border: 1px solid #555555;\n}\n.ya-link-btn-cancel:hover {\n background: #444444;\n color: #ffffff;\n border-color: #666666;\n}\n.ya-link-btn-save {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-link-btn-save:hover {\n background: #c4956a;\n}\n.ya-href-popover {\n position: absolute;\n top: 100%;\n left: 50%;\n margin-top: 8px;\n z-index: 10;\n min-width: 280px;\n max-width: 320px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n transform: translateX(-50%);\n animation: ya-href-popover-fade-in 0.15s ease;\n overflow: hidden;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n@keyframes ya-href-popover-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n.ya-href-popover::before {\n content: "";\n position: absolute;\n top: -6px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #1a1a1a;\n}\n.ya-href-popover-header {\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: #ffffff;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-href-popover-section {\n padding: 12px 16px;\n}\n.ya-href-popover-label {\n display: block;\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n.ya-href-collapsible-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 0;\n background: transparent;\n border: none;\n cursor: pointer;\n transition: color 0.15s ease;\n}\n.ya-href-collapsible-header:hover {\n color: rgba(255, 255, 255, 0.8);\n}\n.ya-href-chevron {\n font-size: 8px;\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-popover-pages {\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 200px;\n overflow-y: auto;\n}\n.ya-href-page-btn {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid transparent;\n border-radius: 8px;\n color: #e0e0e0;\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.ya-href-page-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ya-href-page-btn.is-selected {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-href-page-btn.is-selected .ya-href-page-path {\n color: rgba(26, 26, 26, 0.6);\n}\n.ya-href-page-path {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.4);\n font-family: monospace;\n word-break: break-all;\n}\n.ya-href-external-toggle {\n display: block;\n width: 100%;\n padding: 10px 16px;\n background: transparent;\n border: none;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n color: #D4A574;\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n.ya-href-external-toggle:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.ya-href-url-input {\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n color: #ffffff;\n font-size: 13px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n.ya-href-url-input::placeholder {\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-url-input:focus {\n border-color: var(--color-primary, #D4A574);\n}\n.ya-href-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-link-edit-popover {\n position: absolute;\n top: 100%;\n left: 50%;\n margin-top: 8px;\n z-index: 10;\n background: #2a2a2a;\n border-radius: 6px;\n padding: 4px;\n display: flex;\n gap: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n transform: translateX(-50%);\n animation: ya-edit-popover-fade-in 0.1s ease;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n}\n@keyframes ya-edit-popover-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n.ya-link-edit-popover::before {\n content: "";\n position: absolute;\n top: -5px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #2a2a2a;\n}\n.ya-link-edit-popover button {\n background: #3a3a3a;\n border: none;\n color: #fff;\n padding: 6px 12px;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.15s ease;\n}\n.ya-link-edit-popover button:hover {\n background: #4a4a4a;\n}\n');
|
|
2766
3242
|
|
|
2767
3243
|
// src/components/YaLink.tsx
|
|
2768
|
-
import { Fragment as Fragment3, jsx as
|
|
3244
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2769
3245
|
function isInternalPath(path) {
|
|
2770
3246
|
if (!path) return false;
|
|
2771
3247
|
if (path.startsWith("#")) return false;
|
|
@@ -2866,8 +3342,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2866
3342
|
const { getValue, setValue, mode, saveToWorker, getPages } = useContentStore();
|
|
2867
3343
|
const [, navigate] = useLocation();
|
|
2868
3344
|
const pages = availablePages ?? getPages();
|
|
2869
|
-
const [sections, setSections] =
|
|
2870
|
-
const [sectionsExpanded, setSectionsExpanded] =
|
|
3345
|
+
const [sections, setSections] = useState8([]);
|
|
3346
|
+
const [sectionsExpanded, setSectionsExpanded] = useState8(false);
|
|
2871
3347
|
const textFieldId = `${fieldId}.text`;
|
|
2872
3348
|
const hrefFieldId = `${fieldId}.href`;
|
|
2873
3349
|
const storeText = getValue(textFieldId);
|
|
@@ -2878,16 +3354,16 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2878
3354
|
const isExternal = isExternalHref(href);
|
|
2879
3355
|
const effectiveTarget = target ?? (isExternal ? "_blank" : void 0);
|
|
2880
3356
|
const effectiveRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
|
|
2881
|
-
const [editingMode, setEditingMode] =
|
|
2882
|
-
const [showEditPopover, setShowEditPopover] =
|
|
2883
|
-
const [originalText, setOriginalText] =
|
|
2884
|
-
const [originalHref, setOriginalHref] =
|
|
2885
|
-
const [currentHref, setCurrentHref] =
|
|
2886
|
-
const [isExternalUrl, setIsExternalUrl] =
|
|
2887
|
-
const [externalUrl, setExternalUrl] =
|
|
2888
|
-
const containerRef =
|
|
2889
|
-
const hrefPopoverRef =
|
|
2890
|
-
const hidePopoverTimeoutRef =
|
|
3357
|
+
const [editingMode, setEditingMode] = useState8(null);
|
|
3358
|
+
const [showEditPopover, setShowEditPopover] = useState8(false);
|
|
3359
|
+
const [originalText, setOriginalText] = useState8(text);
|
|
3360
|
+
const [originalHref, setOriginalHref] = useState8(href);
|
|
3361
|
+
const [currentHref, setCurrentHref] = useState8(href);
|
|
3362
|
+
const [isExternalUrl, setIsExternalUrl] = useState8(false);
|
|
3363
|
+
const [externalUrl, setExternalUrl] = useState8("");
|
|
3364
|
+
const containerRef = useRef9(null);
|
|
3365
|
+
const hrefPopoverRef = useRef9(null);
|
|
3366
|
+
const hidePopoverTimeoutRef = useRef9(null);
|
|
2891
3367
|
const editor = useEditor2({
|
|
2892
3368
|
extensions: [
|
|
2893
3369
|
StarterKit2.configure({
|
|
@@ -2910,26 +3386,26 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2910
3386
|
}
|
|
2911
3387
|
}
|
|
2912
3388
|
});
|
|
2913
|
-
|
|
3389
|
+
useEffect8(() => {
|
|
2914
3390
|
if (editor && editingMode !== "text") {
|
|
2915
3391
|
if (editor.getHTML() !== text) {
|
|
2916
3392
|
editor.commands.setContent(text);
|
|
2917
3393
|
}
|
|
2918
3394
|
}
|
|
2919
3395
|
}, [text, editor, editingMode]);
|
|
2920
|
-
|
|
3396
|
+
useEffect8(() => {
|
|
2921
3397
|
if (editingMode !== "link") {
|
|
2922
3398
|
setCurrentHref(href);
|
|
2923
3399
|
}
|
|
2924
3400
|
}, [href, editingMode]);
|
|
2925
|
-
|
|
3401
|
+
useEffect8(() => {
|
|
2926
3402
|
return () => {
|
|
2927
3403
|
if (hidePopoverTimeoutRef.current) {
|
|
2928
3404
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
2929
3405
|
}
|
|
2930
3406
|
};
|
|
2931
3407
|
}, []);
|
|
2932
|
-
|
|
3408
|
+
useEffect8(() => {
|
|
2933
3409
|
if (editingMode !== "link") return;
|
|
2934
3410
|
const handleClickOutside = (event) => {
|
|
2935
3411
|
const target2 = event.target;
|
|
@@ -2943,7 +3419,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2943
3419
|
document.addEventListener("mousedown", handleClickOutside);
|
|
2944
3420
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
2945
3421
|
}, [editingMode, originalHref]);
|
|
2946
|
-
const handleSaveText =
|
|
3422
|
+
const handleSaveText = useCallback9(() => {
|
|
2947
3423
|
if (!editor) return;
|
|
2948
3424
|
let html = editor.getHTML();
|
|
2949
3425
|
html = html.replace(/<\/p><p>/g, "<br><br>").replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
@@ -2951,26 +3427,26 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2951
3427
|
saveToWorker?.(textFieldId, html);
|
|
2952
3428
|
setEditingMode(null);
|
|
2953
3429
|
}, [editor, textFieldId, setValue, saveToWorker]);
|
|
2954
|
-
const handleSaveLink =
|
|
3430
|
+
const handleSaveLink = useCallback9(() => {
|
|
2955
3431
|
setValue(hrefFieldId, currentHref);
|
|
2956
3432
|
saveToWorker?.(hrefFieldId, currentHref);
|
|
2957
3433
|
setEditingMode(null);
|
|
2958
3434
|
setIsExternalUrl(false);
|
|
2959
3435
|
setExternalUrl("");
|
|
2960
3436
|
}, [hrefFieldId, currentHref, setValue, saveToWorker]);
|
|
2961
|
-
const handleCancelText =
|
|
3437
|
+
const handleCancelText = useCallback9(() => {
|
|
2962
3438
|
if (editor) {
|
|
2963
3439
|
editor.commands.setContent(originalText);
|
|
2964
3440
|
}
|
|
2965
3441
|
setEditingMode(null);
|
|
2966
3442
|
}, [editor, originalText]);
|
|
2967
|
-
const handleCancelLink =
|
|
3443
|
+
const handleCancelLink = useCallback9(() => {
|
|
2968
3444
|
setCurrentHref(originalHref);
|
|
2969
3445
|
setEditingMode(null);
|
|
2970
3446
|
setIsExternalUrl(false);
|
|
2971
3447
|
setExternalUrl("");
|
|
2972
3448
|
}, [originalHref]);
|
|
2973
|
-
const handleClick =
|
|
3449
|
+
const handleClick = useCallback9(
|
|
2974
3450
|
(e) => {
|
|
2975
3451
|
const selectModeEnabled = window.__builderSelectModeEnabled;
|
|
2976
3452
|
if (selectModeEnabled) {
|
|
@@ -3002,7 +3478,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3002
3478
|
},
|
|
3003
3479
|
[href, navigate, onClick]
|
|
3004
3480
|
);
|
|
3005
|
-
const handleMouseEnter =
|
|
3481
|
+
const handleMouseEnter = useCallback9(() => {
|
|
3006
3482
|
if (hidePopoverTimeoutRef.current) {
|
|
3007
3483
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
3008
3484
|
hidePopoverTimeoutRef.current = null;
|
|
@@ -3011,19 +3487,19 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3011
3487
|
setShowEditPopover(true);
|
|
3012
3488
|
}
|
|
3013
3489
|
}, [mode, editingMode]);
|
|
3014
|
-
const handleMouseLeave =
|
|
3490
|
+
const handleMouseLeave = useCallback9(() => {
|
|
3015
3491
|
hidePopoverTimeoutRef.current = window.setTimeout(() => {
|
|
3016
3492
|
if (!editingMode) {
|
|
3017
3493
|
setShowEditPopover(false);
|
|
3018
3494
|
}
|
|
3019
3495
|
}, 150);
|
|
3020
3496
|
}, [editingMode]);
|
|
3021
|
-
const handleFocus =
|
|
3497
|
+
const handleFocus = useCallback9(() => {
|
|
3022
3498
|
if (mode === "inline-edit" && !editingMode) {
|
|
3023
3499
|
setShowEditPopover(true);
|
|
3024
3500
|
}
|
|
3025
3501
|
}, [mode, editingMode]);
|
|
3026
|
-
const startEditText =
|
|
3502
|
+
const startEditText = useCallback9(() => {
|
|
3027
3503
|
setShowEditPopover(false);
|
|
3028
3504
|
setEditingMode("text");
|
|
3029
3505
|
setOriginalText(text);
|
|
@@ -3031,14 +3507,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3031
3507
|
editor?.chain().focus().selectAll().run();
|
|
3032
3508
|
}, 20);
|
|
3033
3509
|
}, [text, editor]);
|
|
3034
|
-
const startEditLink =
|
|
3510
|
+
const startEditLink = useCallback9(() => {
|
|
3035
3511
|
setShowEditPopover(false);
|
|
3036
3512
|
setEditingMode("link");
|
|
3037
3513
|
setOriginalHref(href);
|
|
3038
3514
|
setCurrentHref(href);
|
|
3039
3515
|
setSections(discoverSectionsFromDOM());
|
|
3040
3516
|
}, [href]);
|
|
3041
|
-
const handleKeyDown =
|
|
3517
|
+
const handleKeyDown = useCallback9(
|
|
3042
3518
|
(event) => {
|
|
3043
3519
|
if (editingMode !== "text") return;
|
|
3044
3520
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -3059,7 +3535,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3059
3535
|
},
|
|
3060
3536
|
[editingMode, handleSaveText, handleCancelText]
|
|
3061
3537
|
);
|
|
3062
|
-
const handleFontSizeChange =
|
|
3538
|
+
const handleFontSizeChange = useCallback9(
|
|
3063
3539
|
(e) => {
|
|
3064
3540
|
if (!editor) return;
|
|
3065
3541
|
const size = e.target.value;
|
|
@@ -3071,7 +3547,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3071
3547
|
},
|
|
3072
3548
|
[editor]
|
|
3073
3549
|
);
|
|
3074
|
-
const handleFontWeightChange =
|
|
3550
|
+
const handleFontWeightChange = useCallback9(
|
|
3075
3551
|
(e) => {
|
|
3076
3552
|
if (!editor) return;
|
|
3077
3553
|
const weight = e.target.value;
|
|
@@ -3083,11 +3559,11 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3083
3559
|
},
|
|
3084
3560
|
[editor]
|
|
3085
3561
|
);
|
|
3086
|
-
const handlePageSelect =
|
|
3562
|
+
const handlePageSelect = useCallback9((path) => {
|
|
3087
3563
|
setCurrentHref(path);
|
|
3088
3564
|
setIsExternalUrl(false);
|
|
3089
3565
|
}, []);
|
|
3090
|
-
const handleExternalUrlApply =
|
|
3566
|
+
const handleExternalUrlApply = useCallback9(() => {
|
|
3091
3567
|
if (externalUrl) {
|
|
3092
3568
|
setCurrentHref(externalUrl);
|
|
3093
3569
|
}
|
|
@@ -3103,9 +3579,9 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3103
3579
|
return attrs.fontWeight || "";
|
|
3104
3580
|
};
|
|
3105
3581
|
if (mode === "read-only") {
|
|
3106
|
-
const content = isIconMode ? children : /* @__PURE__ */
|
|
3582
|
+
const content = isIconMode ? children : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode });
|
|
3107
3583
|
if (isInternalPath(href)) {
|
|
3108
|
-
return /* @__PURE__ */
|
|
3584
|
+
return /* @__PURE__ */ jsx9(
|
|
3109
3585
|
WouterLink,
|
|
3110
3586
|
{
|
|
3111
3587
|
href,
|
|
@@ -3117,7 +3593,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3117
3593
|
}
|
|
3118
3594
|
);
|
|
3119
3595
|
}
|
|
3120
|
-
return /* @__PURE__ */
|
|
3596
|
+
return /* @__PURE__ */ jsx9(
|
|
3121
3597
|
Component,
|
|
3122
3598
|
{
|
|
3123
3599
|
ref: containerRef,
|
|
@@ -3132,8 +3608,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3132
3608
|
}
|
|
3133
3609
|
);
|
|
3134
3610
|
}
|
|
3135
|
-
return /* @__PURE__ */
|
|
3136
|
-
/* @__PURE__ */
|
|
3611
|
+
return /* @__PURE__ */ jsxs5("span", { className: "ya-link-wrapper", children: [
|
|
3612
|
+
/* @__PURE__ */ jsx9(
|
|
3137
3613
|
Component,
|
|
3138
3614
|
{
|
|
3139
3615
|
ref: containerRef,
|
|
@@ -3152,9 +3628,9 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3152
3628
|
children: isIconMode ? (
|
|
3153
3629
|
// Icon mode: render children directly, no text editing
|
|
3154
3630
|
children
|
|
3155
|
-
) : editor ? /* @__PURE__ */
|
|
3631
|
+
) : editor ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3156
3632
|
createPortal4(
|
|
3157
|
-
/* @__PURE__ */
|
|
3633
|
+
/* @__PURE__ */ jsxs5(
|
|
3158
3634
|
BubbleMenu2,
|
|
3159
3635
|
{
|
|
3160
3636
|
editor,
|
|
@@ -3162,28 +3638,28 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3162
3638
|
options: { offset: 6, placement: "top" },
|
|
3163
3639
|
className: "ya-bubble-menu",
|
|
3164
3640
|
children: [
|
|
3165
|
-
/* @__PURE__ */
|
|
3641
|
+
/* @__PURE__ */ jsx9(
|
|
3166
3642
|
"button",
|
|
3167
3643
|
{
|
|
3168
3644
|
type: "button",
|
|
3169
3645
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
3170
3646
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
3171
3647
|
title: "Bold",
|
|
3172
|
-
children: /* @__PURE__ */
|
|
3648
|
+
children: /* @__PURE__ */ jsx9("strong", { children: "B" })
|
|
3173
3649
|
}
|
|
3174
3650
|
),
|
|
3175
|
-
/* @__PURE__ */
|
|
3651
|
+
/* @__PURE__ */ jsx9(
|
|
3176
3652
|
"button",
|
|
3177
3653
|
{
|
|
3178
3654
|
type: "button",
|
|
3179
3655
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
3180
3656
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
3181
3657
|
title: "Italic",
|
|
3182
|
-
children: /* @__PURE__ */
|
|
3658
|
+
children: /* @__PURE__ */ jsx9("em", { children: "I" })
|
|
3183
3659
|
}
|
|
3184
3660
|
),
|
|
3185
|
-
/* @__PURE__ */
|
|
3186
|
-
/* @__PURE__ */
|
|
3661
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-bubble-divider" }),
|
|
3662
|
+
/* @__PURE__ */ jsxs5(
|
|
3187
3663
|
"select",
|
|
3188
3664
|
{
|
|
3189
3665
|
value: getCurrentFontSize(),
|
|
@@ -3191,12 +3667,12 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3191
3667
|
className: "ya-bubble-select",
|
|
3192
3668
|
title: "Font Size",
|
|
3193
3669
|
children: [
|
|
3194
|
-
/* @__PURE__ */
|
|
3195
|
-
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */
|
|
3670
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: "Size" }),
|
|
3671
|
+
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */ jsx9("option", { value: size, children: name }, name))
|
|
3196
3672
|
]
|
|
3197
3673
|
}
|
|
3198
3674
|
),
|
|
3199
|
-
/* @__PURE__ */
|
|
3675
|
+
/* @__PURE__ */ jsxs5(
|
|
3200
3676
|
"select",
|
|
3201
3677
|
{
|
|
3202
3678
|
value: getCurrentFontWeight(),
|
|
@@ -3204,8 +3680,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3204
3680
|
className: "ya-bubble-select",
|
|
3205
3681
|
title: "Font Weight",
|
|
3206
3682
|
children: [
|
|
3207
|
-
/* @__PURE__ */
|
|
3208
|
-
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */
|
|
3683
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: "Weight" }),
|
|
3684
|
+
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */ jsx9("option", { value: weight, children: name }, name))
|
|
3209
3685
|
]
|
|
3210
3686
|
}
|
|
3211
3687
|
)
|
|
@@ -3214,39 +3690,39 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3214
3690
|
),
|
|
3215
3691
|
document.body
|
|
3216
3692
|
),
|
|
3217
|
-
editingMode === "text" ? /* @__PURE__ */
|
|
3218
|
-
/* @__PURE__ */
|
|
3219
|
-
/* @__PURE__ */
|
|
3220
|
-
/* @__PURE__ */
|
|
3221
|
-
/* @__PURE__ */
|
|
3693
|
+
editingMode === "text" ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3694
|
+
/* @__PURE__ */ jsx9(EditorContent2, { editor }),
|
|
3695
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-link-actions", children: [
|
|
3696
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: handleCancelText, className: "ya-link-btn ya-link-btn-cancel", children: "Cancel" }),
|
|
3697
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: handleSaveText, className: "ya-link-btn ya-link-btn-save", children: "Save" })
|
|
3222
3698
|
] })
|
|
3223
|
-
] }) : /* @__PURE__ */
|
|
3224
|
-
] }) : /* @__PURE__ */
|
|
3699
|
+
] }) : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode })
|
|
3700
|
+
] }) : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode })
|
|
3225
3701
|
}
|
|
3226
3702
|
),
|
|
3227
|
-
showEditPopover && !editingMode && mode === "inline-edit" && /* @__PURE__ */
|
|
3228
|
-
!isIconMode && /* @__PURE__ */
|
|
3229
|
-
/* @__PURE__ */
|
|
3703
|
+
showEditPopover && !editingMode && mode === "inline-edit" && /* @__PURE__ */ jsxs5("div", { className: "ya-link-edit-popover", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [
|
|
3704
|
+
!isIconMode && /* @__PURE__ */ jsx9("button", { type: "button", onClick: startEditText, children: "Edit text" }),
|
|
3705
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: startEditLink, children: "Edit link" })
|
|
3230
3706
|
] }),
|
|
3231
|
-
editingMode === "link" && /* @__PURE__ */
|
|
3232
|
-
/* @__PURE__ */
|
|
3233
|
-
!isExternalUrl ? /* @__PURE__ */
|
|
3234
|
-
sections.length > 0 && /* @__PURE__ */
|
|
3235
|
-
/* @__PURE__ */
|
|
3707
|
+
editingMode === "link" && /* @__PURE__ */ jsxs5("div", { ref: hrefPopoverRef, className: "ya-href-popover", children: [
|
|
3708
|
+
/* @__PURE__ */ jsx9("div", { className: "ya-href-popover-header", children: "Link destination" }),
|
|
3709
|
+
!isExternalUrl ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3710
|
+
sections.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "ya-href-popover-section", children: [
|
|
3711
|
+
/* @__PURE__ */ jsxs5(
|
|
3236
3712
|
"button",
|
|
3237
3713
|
{
|
|
3238
3714
|
type: "button",
|
|
3239
3715
|
className: "ya-href-popover-label ya-href-collapsible-header",
|
|
3240
3716
|
onClick: () => setSectionsExpanded(!sectionsExpanded),
|
|
3241
3717
|
children: [
|
|
3242
|
-
/* @__PURE__ */
|
|
3718
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-chevron", children: sectionsExpanded ? "\u25BC" : "\u25B6" }),
|
|
3243
3719
|
"Scroll to section (",
|
|
3244
3720
|
sections.length,
|
|
3245
3721
|
")"
|
|
3246
3722
|
]
|
|
3247
3723
|
}
|
|
3248
3724
|
),
|
|
3249
|
-
sectionsExpanded && /* @__PURE__ */
|
|
3725
|
+
sectionsExpanded && /* @__PURE__ */ jsx9("div", { className: "ya-href-popover-pages", children: sections.map((section) => /* @__PURE__ */ jsxs5(
|
|
3250
3726
|
"button",
|
|
3251
3727
|
{
|
|
3252
3728
|
type: "button",
|
|
@@ -3254,15 +3730,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3254
3730
|
onClick: () => handlePageSelect(section.path),
|
|
3255
3731
|
children: [
|
|
3256
3732
|
section.label,
|
|
3257
|
-
/* @__PURE__ */
|
|
3733
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-page-path", children: section.path })
|
|
3258
3734
|
]
|
|
3259
3735
|
},
|
|
3260
3736
|
section.path
|
|
3261
3737
|
)) })
|
|
3262
3738
|
] }),
|
|
3263
|
-
pages.length > 0 && /* @__PURE__ */
|
|
3264
|
-
/* @__PURE__ */
|
|
3265
|
-
/* @__PURE__ */
|
|
3739
|
+
pages.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "ya-href-popover-section", children: [
|
|
3740
|
+
/* @__PURE__ */ jsx9("label", { className: "ya-href-popover-label", children: "Navigate to page" }),
|
|
3741
|
+
/* @__PURE__ */ jsx9("div", { className: "ya-href-popover-pages", children: pages.map((page) => /* @__PURE__ */ jsxs5(
|
|
3266
3742
|
"button",
|
|
3267
3743
|
{
|
|
3268
3744
|
type: "button",
|
|
@@ -3270,13 +3746,13 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3270
3746
|
onClick: () => handlePageSelect(page.path),
|
|
3271
3747
|
children: [
|
|
3272
3748
|
page.label,
|
|
3273
|
-
/* @__PURE__ */
|
|
3749
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-page-path", children: page.path })
|
|
3274
3750
|
]
|
|
3275
3751
|
},
|
|
3276
3752
|
page.path
|
|
3277
3753
|
)) })
|
|
3278
3754
|
] }),
|
|
3279
|
-
/* @__PURE__ */
|
|
3755
|
+
/* @__PURE__ */ jsx9(
|
|
3280
3756
|
"button",
|
|
3281
3757
|
{
|
|
3282
3758
|
type: "button",
|
|
@@ -3288,10 +3764,10 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3288
3764
|
children: "Use external URL instead"
|
|
3289
3765
|
}
|
|
3290
3766
|
)
|
|
3291
|
-
] }) : /* @__PURE__ */
|
|
3292
|
-
/* @__PURE__ */
|
|
3293
|
-
/* @__PURE__ */
|
|
3294
|
-
/* @__PURE__ */
|
|
3767
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3768
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-href-popover-section", children: [
|
|
3769
|
+
/* @__PURE__ */ jsx9("label", { className: "ya-href-popover-label", children: "External URL" }),
|
|
3770
|
+
/* @__PURE__ */ jsx9(
|
|
3295
3771
|
"input",
|
|
3296
3772
|
{
|
|
3297
3773
|
type: "url",
|
|
@@ -3303,21 +3779,444 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3303
3779
|
}
|
|
3304
3780
|
)
|
|
3305
3781
|
] }),
|
|
3306
|
-
/* @__PURE__ */
|
|
3782
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: "ya-href-external-toggle", onClick: () => setIsExternalUrl(false), children: "\u2190 Back to pages" })
|
|
3307
3783
|
] }),
|
|
3308
|
-
/* @__PURE__ */
|
|
3309
|
-
/* @__PURE__ */
|
|
3310
|
-
isExternalUrl ? /* @__PURE__ */
|
|
3784
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-href-popover-actions", children: [
|
|
3785
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: "ya-link-btn ya-link-btn-cancel", onClick: handleCancelLink, children: "Cancel" }),
|
|
3786
|
+
isExternalUrl ? /* @__PURE__ */ jsx9("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleExternalUrlApply, children: "Apply" }) : /* @__PURE__ */ jsx9("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleSaveLink, children: "Save" })
|
|
3311
3787
|
] })
|
|
3312
3788
|
] })
|
|
3313
3789
|
] });
|
|
3314
3790
|
}
|
|
3315
3791
|
|
|
3792
|
+
// src/components/YaContainer.tsx
|
|
3793
|
+
import { useCallback as useCallback10, useEffect as useEffect9, useRef as useRef10, useState as useState9 } from "react";
|
|
3794
|
+
import { createPortal as createPortal5 } from "react-dom";
|
|
3795
|
+
|
|
3796
|
+
// src/components/ya-container.css
|
|
3797
|
+
styleInject('.ya-container {\n position: relative;\n}\n.ya-container-has-overlay::after {\n content: "";\n position: absolute;\n inset: 0;\n background: var(--ya-overlay-color, transparent);\n opacity: var(--ya-overlay-opacity, 0);\n pointer-events: none;\n z-index: 0;\n}\n.ya-container > *:not(.ya-container-toolbar) {\n position: relative;\n z-index: 1;\n}\n.ya-container-editable {\n transition: outline 0.15s ease;\n}\n.ya-container-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: -2px;\n}\nbody.builder-selector-active .ya-container-editable:hover {\n outline: none;\n}\n.ya-container-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: -3px;\n}\n.ya-container-toolbar {\n display: flex;\n gap: 4px;\n background: rgba(26, 26, 26, 0.95);\n padding: 6px 8px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 9999;\n animation: ya-container-toolbar-fade-in 0.15s ease;\n}\n@keyframes ya-container-toolbar-fade-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ya-container-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: #3a3a3a;\n border: none;\n border-radius: 6px;\n color: #ffffff;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease;\n}\n.ya-container-toolbar button:hover {\n background: #4a4a4a;\n transform: scale(1.05);\n}\n.ya-container-toolbar button:active {\n transform: scale(0.98);\n}\n.ya-container-toolbar button.active {\n background: var(--color-primary, #D4A574);\n color: #1a1a1a;\n}\n.ya-container-toolbar button svg {\n width: 16px;\n height: 16px;\n}\n.ya-container-toolbar button[aria-label="Clear background"] {\n background: #3a2a2a;\n}\n.ya-container-toolbar button[aria-label="Clear background"]:hover {\n background: #5a3a3a;\n}\n.ya-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: -3px;\n}\n.ya-container-toolbar button:focus-visible {\n outline: 2px solid var(--color-primary, #D4A574);\n outline-offset: 2px;\n}\n.ya-container-drop-target {\n outline: 2px dashed var(--ya-drop-color, #3b82f6) !important;\n outline-offset: -2px;\n}\n.ya-container-drop-target .ya-container-toolbar {\n display: none !important;\n}\n.ya-container-drop-hover {\n outline: 3px solid var(--ya-drop-color, #3b82f6) !important;\n outline-offset: -3px;\n}\n.ya-container-drop-hover::before {\n content: "";\n position: absolute;\n inset: 0;\n background: rgba(59, 130, 246, 0.08);\n pointer-events: none;\n z-index: 10;\n animation: ya-container-drop-pulse 1s ease-in-out infinite;\n}\n@keyframes ya-container-drop-pulse {\n 0%, 100% {\n background: rgba(59, 130, 246, 0.05);\n }\n 50% {\n background: rgba(59, 130, 246, 0.12);\n }\n}\n');
|
|
3798
|
+
|
|
3799
|
+
// src/components/YaContainer.tsx
|
|
3800
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3801
|
+
function parseBackgroundConfig(value) {
|
|
3802
|
+
if (!value) {
|
|
3803
|
+
return { type: "none" };
|
|
3804
|
+
}
|
|
3805
|
+
try {
|
|
3806
|
+
const parsed = JSON.parse(value);
|
|
3807
|
+
if (typeof parsed === "object" && parsed.type) {
|
|
3808
|
+
return parsed;
|
|
3809
|
+
}
|
|
3810
|
+
} catch {
|
|
3811
|
+
}
|
|
3812
|
+
return { type: "none" };
|
|
3813
|
+
}
|
|
3814
|
+
function serializeBackgroundConfig(config) {
|
|
3815
|
+
return JSON.stringify(config);
|
|
3816
|
+
}
|
|
3817
|
+
function getObjectPosition2(imageConfig) {
|
|
3818
|
+
if (imageConfig.focalPoint) {
|
|
3819
|
+
return `${imageConfig.focalPoint.x}% ${imageConfig.focalPoint.y}%`;
|
|
3820
|
+
}
|
|
3821
|
+
return imageConfig.objectPosition || "50% 50%";
|
|
3822
|
+
}
|
|
3823
|
+
function deriveContainerLabel(element) {
|
|
3824
|
+
if (!element) return "Section";
|
|
3825
|
+
const tagName = element.tagName.toLowerCase();
|
|
3826
|
+
const id = element.id;
|
|
3827
|
+
const ariaLabel = element.getAttribute("aria-label");
|
|
3828
|
+
if (ariaLabel) return ariaLabel;
|
|
3829
|
+
if (id) return id.replace(/-/g, " ").replace(/([A-Z])/g, " $1").trim();
|
|
3830
|
+
const tagLabels = {
|
|
3831
|
+
section: "Section",
|
|
3832
|
+
header: "Header",
|
|
3833
|
+
footer: "Footer",
|
|
3834
|
+
main: "Main Content",
|
|
3835
|
+
article: "Article",
|
|
3836
|
+
aside: "Sidebar",
|
|
3837
|
+
div: "Container"
|
|
3838
|
+
};
|
|
3839
|
+
return tagLabels[tagName] || "Section";
|
|
3840
|
+
}
|
|
3841
|
+
function Toolbar({ containerRef, onImageClick, onColorClick, onAIClick, onClearClick, hasBackground }) {
|
|
3842
|
+
const [position, setPosition] = useState9(null);
|
|
3843
|
+
useEffect9(() => {
|
|
3844
|
+
const updatePosition = () => {
|
|
3845
|
+
if (containerRef.current) {
|
|
3846
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
3847
|
+
setPosition({
|
|
3848
|
+
top: rect.top + 8,
|
|
3849
|
+
left: rect.left + 8
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
};
|
|
3853
|
+
updatePosition();
|
|
3854
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
3855
|
+
window.addEventListener("resize", updatePosition);
|
|
3856
|
+
return () => {
|
|
3857
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
3858
|
+
window.removeEventListener("resize", updatePosition);
|
|
3859
|
+
};
|
|
3860
|
+
}, [containerRef]);
|
|
3861
|
+
if (!position) return null;
|
|
3862
|
+
return createPortal5(
|
|
3863
|
+
/* @__PURE__ */ jsxs6(
|
|
3864
|
+
"div",
|
|
3865
|
+
{
|
|
3866
|
+
className: "ya-container-toolbar",
|
|
3867
|
+
style: {
|
|
3868
|
+
position: "fixed",
|
|
3869
|
+
top: position.top,
|
|
3870
|
+
left: position.left
|
|
3871
|
+
},
|
|
3872
|
+
onClick: (e) => e.stopPropagation(),
|
|
3873
|
+
children: [
|
|
3874
|
+
/* @__PURE__ */ jsx10(
|
|
3875
|
+
"button",
|
|
3876
|
+
{
|
|
3877
|
+
type: "button",
|
|
3878
|
+
onClick: onImageClick,
|
|
3879
|
+
"aria-label": "Edit background image",
|
|
3880
|
+
title: "Background Image",
|
|
3881
|
+
children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3882
|
+
/* @__PURE__ */ jsx10("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
3883
|
+
/* @__PURE__ */ jsx10("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
3884
|
+
/* @__PURE__ */ jsx10("polyline", { points: "21 15 16 10 5 21" })
|
|
3885
|
+
] })
|
|
3886
|
+
}
|
|
3887
|
+
),
|
|
3888
|
+
/* @__PURE__ */ jsx10(
|
|
3889
|
+
"button",
|
|
3890
|
+
{
|
|
3891
|
+
type: "button",
|
|
3892
|
+
onClick: onColorClick,
|
|
3893
|
+
"aria-label": "Edit background color",
|
|
3894
|
+
title: "Background Color",
|
|
3895
|
+
children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3896
|
+
/* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3897
|
+
/* @__PURE__ */ jsx10("path", { d: "M12 2a10 10 0 0 1 0 20", fill: "currentColor" })
|
|
3898
|
+
] })
|
|
3899
|
+
}
|
|
3900
|
+
),
|
|
3901
|
+
/* @__PURE__ */ jsx10(
|
|
3902
|
+
"button",
|
|
3903
|
+
{
|
|
3904
|
+
type: "button",
|
|
3905
|
+
onClick: onAIClick,
|
|
3906
|
+
"aria-label": "Ask AI for help",
|
|
3907
|
+
title: "AI Assist",
|
|
3908
|
+
children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3909
|
+
/* @__PURE__ */ jsx10("path", { d: "M12 2L2 7l10 5 10-5-10-5z" }),
|
|
3910
|
+
/* @__PURE__ */ jsx10("path", { d: "M2 17l10 5 10-5" }),
|
|
3911
|
+
/* @__PURE__ */ jsx10("path", { d: "M2 12l10 5 10-5" })
|
|
3912
|
+
] })
|
|
3913
|
+
}
|
|
3914
|
+
),
|
|
3915
|
+
hasBackground && /* @__PURE__ */ jsx10(
|
|
3916
|
+
"button",
|
|
3917
|
+
{
|
|
3918
|
+
type: "button",
|
|
3919
|
+
onClick: onClearClick,
|
|
3920
|
+
"aria-label": "Clear background",
|
|
3921
|
+
title: "Clear Background",
|
|
3922
|
+
children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3923
|
+
/* @__PURE__ */ jsx10("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
3924
|
+
/* @__PURE__ */ jsx10("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
3925
|
+
] })
|
|
3926
|
+
}
|
|
3927
|
+
)
|
|
3928
|
+
]
|
|
3929
|
+
}
|
|
3930
|
+
),
|
|
3931
|
+
document.body
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3934
|
+
function YaContainer({
|
|
3935
|
+
fieldId,
|
|
3936
|
+
className,
|
|
3937
|
+
style,
|
|
3938
|
+
as: Tag = "section",
|
|
3939
|
+
children,
|
|
3940
|
+
defaultBackground
|
|
3941
|
+
}) {
|
|
3942
|
+
const { getValue, setValue, saveToWorker, mode } = useContentStore();
|
|
3943
|
+
const containerRef = useRef10(null);
|
|
3944
|
+
const [isHovered, setIsHovered] = useState9(false);
|
|
3945
|
+
const [isSelected, setIsSelected] = useState9(false);
|
|
3946
|
+
const [isDropMode, setIsDropMode] = useState9(false);
|
|
3947
|
+
const [isDropHover, setIsDropHover] = useState9(false);
|
|
3948
|
+
const rawValue = getValue(fieldId);
|
|
3949
|
+
const backgroundConfig = rawValue ? parseBackgroundConfig(rawValue) : defaultBackground || { type: "none" };
|
|
3950
|
+
const hasBackground = backgroundConfig.type !== "none";
|
|
3951
|
+
const backgroundStyles = {};
|
|
3952
|
+
if (backgroundConfig.type === "color" && backgroundConfig.backgroundColor) {
|
|
3953
|
+
backgroundStyles.backgroundColor = backgroundConfig.backgroundColor;
|
|
3954
|
+
}
|
|
3955
|
+
if (backgroundConfig.type === "image" && backgroundConfig.backgroundImage) {
|
|
3956
|
+
const img = backgroundConfig.backgroundImage;
|
|
3957
|
+
const resolvedSrc = resolveAssetUrl(img.src);
|
|
3958
|
+
backgroundStyles.backgroundImage = `url(${resolvedSrc})`;
|
|
3959
|
+
backgroundStyles.backgroundSize = img.objectFit || "cover";
|
|
3960
|
+
backgroundStyles.backgroundPosition = getObjectPosition2(img);
|
|
3961
|
+
backgroundStyles.backgroundRepeat = "no-repeat";
|
|
3962
|
+
}
|
|
3963
|
+
const overlayCustomProps = {};
|
|
3964
|
+
if (backgroundConfig.overlay) {
|
|
3965
|
+
overlayCustomProps["--ya-overlay-color"] = backgroundConfig.overlay.color;
|
|
3966
|
+
overlayCustomProps["--ya-overlay-opacity"] = backgroundConfig.overlay.opacity;
|
|
3967
|
+
}
|
|
3968
|
+
const handleImageClick = useCallback10(() => {
|
|
3969
|
+
if (mode !== "inline-edit") return;
|
|
3970
|
+
setIsSelected(true);
|
|
3971
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
3972
|
+
window.parent.postMessage(
|
|
3973
|
+
{
|
|
3974
|
+
type: "YA_CONTAINER_EDIT_REQUEST",
|
|
3975
|
+
fieldId,
|
|
3976
|
+
currentValue: backgroundConfig,
|
|
3977
|
+
editType: "image",
|
|
3978
|
+
elementRect: rect ? {
|
|
3979
|
+
top: rect.top,
|
|
3980
|
+
left: rect.left,
|
|
3981
|
+
width: rect.width,
|
|
3982
|
+
height: rect.height
|
|
3983
|
+
} : void 0
|
|
3984
|
+
},
|
|
3985
|
+
"*"
|
|
3986
|
+
);
|
|
3987
|
+
}, [mode, fieldId, backgroundConfig]);
|
|
3988
|
+
const handleColorClick = useCallback10(() => {
|
|
3989
|
+
if (mode !== "inline-edit") return;
|
|
3990
|
+
setIsSelected(true);
|
|
3991
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
3992
|
+
window.parent.postMessage(
|
|
3993
|
+
{
|
|
3994
|
+
type: "YA_CONTAINER_EDIT_REQUEST",
|
|
3995
|
+
fieldId,
|
|
3996
|
+
currentValue: backgroundConfig,
|
|
3997
|
+
editType: "color",
|
|
3998
|
+
elementRect: rect ? {
|
|
3999
|
+
top: rect.top,
|
|
4000
|
+
left: rect.left,
|
|
4001
|
+
width: rect.width,
|
|
4002
|
+
height: rect.height
|
|
4003
|
+
} : void 0
|
|
4004
|
+
},
|
|
4005
|
+
"*"
|
|
4006
|
+
);
|
|
4007
|
+
}, [mode, fieldId, backgroundConfig]);
|
|
4008
|
+
const handleAIClick = useCallback10(() => {
|
|
4009
|
+
if (mode !== "inline-edit") return;
|
|
4010
|
+
const label = deriveContainerLabel(containerRef.current);
|
|
4011
|
+
window.parent.postMessage(
|
|
4012
|
+
{
|
|
4013
|
+
type: "YA_CONTAINER_SELECT_FOR_AI",
|
|
4014
|
+
fieldId,
|
|
4015
|
+
label: `Background: ${label}`,
|
|
4016
|
+
currentValue: backgroundConfig
|
|
4017
|
+
},
|
|
4018
|
+
"*"
|
|
4019
|
+
);
|
|
4020
|
+
}, [mode, fieldId, backgroundConfig]);
|
|
4021
|
+
const handleClearClick = useCallback10(() => {
|
|
4022
|
+
if (mode !== "inline-edit") return;
|
|
4023
|
+
const clearedConfig = { type: "none" };
|
|
4024
|
+
const serialized = serializeBackgroundConfig(clearedConfig);
|
|
4025
|
+
setValue(fieldId, serialized);
|
|
4026
|
+
saveToWorker?.(fieldId, serialized);
|
|
4027
|
+
}, [mode, fieldId, setValue, saveToWorker]);
|
|
4028
|
+
useEffect9(() => {
|
|
4029
|
+
if (mode !== "inline-edit") return;
|
|
4030
|
+
const handleMessage2 = (event) => {
|
|
4031
|
+
if (event.data?.type === "YA_CONTAINER_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
4032
|
+
setIsSelected(false);
|
|
4033
|
+
}
|
|
4034
|
+
if (event.data?.type === "YA_CONTAINER_EDIT_CANCEL" && event.data.fieldId === fieldId) {
|
|
4035
|
+
setIsSelected(false);
|
|
4036
|
+
}
|
|
4037
|
+
};
|
|
4038
|
+
window.addEventListener("message", handleMessage2);
|
|
4039
|
+
return () => window.removeEventListener("message", handleMessage2);
|
|
4040
|
+
}, [mode, fieldId]);
|
|
4041
|
+
useEffect9(() => {
|
|
4042
|
+
if (mode !== "inline-edit") return;
|
|
4043
|
+
const handleDropModeMessage = (event) => {
|
|
4044
|
+
if (event.data?.type === "DROP_MODE_START") {
|
|
4045
|
+
setIsDropMode(true);
|
|
4046
|
+
}
|
|
4047
|
+
if (event.data?.type === "DROP_MODE_END") {
|
|
4048
|
+
setIsDropMode(false);
|
|
4049
|
+
setIsDropHover(false);
|
|
4050
|
+
}
|
|
4051
|
+
};
|
|
4052
|
+
window.addEventListener("message", handleDropModeMessage);
|
|
4053
|
+
return () => window.removeEventListener("message", handleDropModeMessage);
|
|
4054
|
+
}, [mode]);
|
|
4055
|
+
const handleDragEnter = useCallback10(
|
|
4056
|
+
(e) => {
|
|
4057
|
+
if (!isDropMode) return;
|
|
4058
|
+
e.preventDefault();
|
|
4059
|
+
e.stopPropagation();
|
|
4060
|
+
setIsDropHover(true);
|
|
4061
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
4062
|
+
window.parent.postMessage(
|
|
4063
|
+
{
|
|
4064
|
+
type: "DROP_ZONE_ENTER",
|
|
4065
|
+
fieldId,
|
|
4066
|
+
zoneType: "background",
|
|
4067
|
+
rect: rect ? {
|
|
4068
|
+
top: rect.top,
|
|
4069
|
+
left: rect.left,
|
|
4070
|
+
width: rect.width,
|
|
4071
|
+
height: rect.height
|
|
4072
|
+
} : { top: 0, left: 0, width: 0, height: 0 }
|
|
4073
|
+
},
|
|
4074
|
+
"*"
|
|
4075
|
+
);
|
|
4076
|
+
},
|
|
4077
|
+
[isDropMode, fieldId]
|
|
4078
|
+
);
|
|
4079
|
+
const handleDragOver = useCallback10(
|
|
4080
|
+
(e) => {
|
|
4081
|
+
if (!isDropMode) return;
|
|
4082
|
+
e.preventDefault();
|
|
4083
|
+
e.stopPropagation();
|
|
4084
|
+
},
|
|
4085
|
+
[isDropMode]
|
|
4086
|
+
);
|
|
4087
|
+
const handleDragLeave = useCallback10(
|
|
4088
|
+
(e) => {
|
|
4089
|
+
if (!isDropMode) return;
|
|
4090
|
+
e.preventDefault();
|
|
4091
|
+
e.stopPropagation();
|
|
4092
|
+
const target = e.currentTarget;
|
|
4093
|
+
const relatedTarget = e.relatedTarget;
|
|
4094
|
+
if (!relatedTarget || !target.contains(relatedTarget)) {
|
|
4095
|
+
setIsDropHover(false);
|
|
4096
|
+
window.parent.postMessage({ type: "DROP_ZONE_LEAVE" }, "*");
|
|
4097
|
+
}
|
|
4098
|
+
},
|
|
4099
|
+
[isDropMode]
|
|
4100
|
+
);
|
|
4101
|
+
const handleDrop = useCallback10(
|
|
4102
|
+
(e) => {
|
|
4103
|
+
if (!isDropMode) return;
|
|
4104
|
+
e.preventDefault();
|
|
4105
|
+
e.stopPropagation();
|
|
4106
|
+
setIsDropHover(false);
|
|
4107
|
+
setIsDropMode(false);
|
|
4108
|
+
window.parent.postMessage(
|
|
4109
|
+
{
|
|
4110
|
+
type: "DROP_ZONE_DROP",
|
|
4111
|
+
fieldId,
|
|
4112
|
+
zoneType: "background"
|
|
4113
|
+
},
|
|
4114
|
+
"*"
|
|
4115
|
+
);
|
|
4116
|
+
},
|
|
4117
|
+
[isDropMode, fieldId]
|
|
4118
|
+
);
|
|
4119
|
+
useEffect9(() => {
|
|
4120
|
+
if (!isSelected || mode !== "inline-edit") return;
|
|
4121
|
+
let lastRectKey = "";
|
|
4122
|
+
let lastTime = 0;
|
|
4123
|
+
let rafId;
|
|
4124
|
+
const loop = (time) => {
|
|
4125
|
+
if (time - lastTime >= 50) {
|
|
4126
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
4127
|
+
if (rect) {
|
|
4128
|
+
const rectKey = `${Math.round(rect.top)},${Math.round(rect.left)},${Math.round(rect.width)},${Math.round(rect.height)}`;
|
|
4129
|
+
if (rectKey !== lastRectKey) {
|
|
4130
|
+
lastRectKey = rectKey;
|
|
4131
|
+
window.parent.postMessage(
|
|
4132
|
+
{
|
|
4133
|
+
type: "YA_CONTAINER_POSITION_UPDATE",
|
|
4134
|
+
fieldId,
|
|
4135
|
+
elementRect: {
|
|
4136
|
+
top: rect.top,
|
|
4137
|
+
left: rect.left,
|
|
4138
|
+
width: rect.width,
|
|
4139
|
+
height: rect.height
|
|
4140
|
+
}
|
|
4141
|
+
},
|
|
4142
|
+
"*"
|
|
4143
|
+
);
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
lastTime = time;
|
|
4147
|
+
}
|
|
4148
|
+
rafId = requestAnimationFrame(loop);
|
|
4149
|
+
};
|
|
4150
|
+
rafId = requestAnimationFrame(loop);
|
|
4151
|
+
return () => cancelAnimationFrame(rafId);
|
|
4152
|
+
}, [isSelected, fieldId, mode]);
|
|
4153
|
+
if (mode === "read-only") {
|
|
4154
|
+
return /* @__PURE__ */ jsx10(
|
|
4155
|
+
Tag,
|
|
4156
|
+
{
|
|
4157
|
+
className: `ya-container ${className || ""}`,
|
|
4158
|
+
style: {
|
|
4159
|
+
...backgroundStyles,
|
|
4160
|
+
...overlayCustomProps,
|
|
4161
|
+
...style
|
|
4162
|
+
},
|
|
4163
|
+
"data-ya-restricted": "true",
|
|
4164
|
+
"data-field-id": fieldId,
|
|
4165
|
+
children
|
|
4166
|
+
}
|
|
4167
|
+
);
|
|
4168
|
+
}
|
|
4169
|
+
const containerClasses = [
|
|
4170
|
+
"ya-container",
|
|
4171
|
+
mode === "inline-edit" ? "ya-container-editable" : "",
|
|
4172
|
+
isSelected ? "ya-container-selected" : "",
|
|
4173
|
+
hasBackground ? "ya-container-has-overlay" : "",
|
|
4174
|
+
isDropMode ? "ya-container-drop-target" : "",
|
|
4175
|
+
isDropHover ? "ya-container-drop-hover" : "",
|
|
4176
|
+
className || ""
|
|
4177
|
+
].filter(Boolean).join(" ");
|
|
4178
|
+
return /* @__PURE__ */ jsxs6(
|
|
4179
|
+
Tag,
|
|
4180
|
+
{
|
|
4181
|
+
ref: containerRef,
|
|
4182
|
+
className: containerClasses,
|
|
4183
|
+
style: {
|
|
4184
|
+
...backgroundStyles,
|
|
4185
|
+
...overlayCustomProps,
|
|
4186
|
+
...style
|
|
4187
|
+
},
|
|
4188
|
+
"data-ya-restricted": "true",
|
|
4189
|
+
"data-field-id": fieldId,
|
|
4190
|
+
"data-ya-container": "true",
|
|
4191
|
+
onMouseEnter: () => setIsHovered(true),
|
|
4192
|
+
onMouseLeave: () => setIsHovered(false),
|
|
4193
|
+
onDragEnter: handleDragEnter,
|
|
4194
|
+
onDragOver: handleDragOver,
|
|
4195
|
+
onDragLeave: handleDragLeave,
|
|
4196
|
+
onDrop: handleDrop,
|
|
4197
|
+
children: [
|
|
4198
|
+
children,
|
|
4199
|
+
mode === "inline-edit" && (isHovered || isSelected) && !document.body.classList.contains("builder-selector-active") && /* @__PURE__ */ jsx10(
|
|
4200
|
+
Toolbar,
|
|
4201
|
+
{
|
|
4202
|
+
containerRef,
|
|
4203
|
+
onImageClick: handleImageClick,
|
|
4204
|
+
onColorClick: handleColorClick,
|
|
4205
|
+
onAIClick: handleAIClick,
|
|
4206
|
+
onClearClick: handleClearClick,
|
|
4207
|
+
hasBackground
|
|
4208
|
+
}
|
|
4209
|
+
)
|
|
4210
|
+
]
|
|
4211
|
+
}
|
|
4212
|
+
);
|
|
4213
|
+
}
|
|
4214
|
+
|
|
3316
4215
|
// src/components/StaticText.tsx
|
|
3317
|
-
import { jsx as
|
|
4216
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
3318
4217
|
function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
3319
4218
|
const content = getContent(fieldId) || (typeof children === "string" ? children : "");
|
|
3320
|
-
return /* @__PURE__ */
|
|
4219
|
+
return /* @__PURE__ */ jsx11(
|
|
3321
4220
|
Component,
|
|
3322
4221
|
{
|
|
3323
4222
|
className,
|
|
@@ -3328,7 +4227,7 @@ function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3328
4227
|
}
|
|
3329
4228
|
|
|
3330
4229
|
// src/components/StaticImage.tsx
|
|
3331
|
-
import { jsx as
|
|
4230
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
3332
4231
|
function parseImageValue2(value) {
|
|
3333
4232
|
if (!value) {
|
|
3334
4233
|
return { src: "" };
|
|
@@ -3342,7 +4241,7 @@ function parseImageValue2(value) {
|
|
|
3342
4241
|
}
|
|
3343
4242
|
return { src: value };
|
|
3344
4243
|
}
|
|
3345
|
-
function
|
|
4244
|
+
function getObjectPosition3(imageData) {
|
|
3346
4245
|
if (imageData.focalPoint) {
|
|
3347
4246
|
return `${imageData.focalPoint.x}% ${imageData.focalPoint.y}%`;
|
|
3348
4247
|
}
|
|
@@ -3363,8 +4262,8 @@ function MpImage({
|
|
|
3363
4262
|
const src = imageData.src || fallbackSrc || "";
|
|
3364
4263
|
const altText = imageData.alt || alt || fallbackAlt || "";
|
|
3365
4264
|
const objectFit = imageData.objectFit || propObjectFit || "cover";
|
|
3366
|
-
const objectPosition =
|
|
3367
|
-
return /* @__PURE__ */
|
|
4265
|
+
const objectPosition = getObjectPosition3(imageData) || propObjectPosition || "50% 50%";
|
|
4266
|
+
return /* @__PURE__ */ jsx12(
|
|
3368
4267
|
"img",
|
|
3369
4268
|
{
|
|
3370
4269
|
src: resolveAssetUrl(src),
|
|
@@ -3382,7 +4281,7 @@ function MpImage({
|
|
|
3382
4281
|
|
|
3383
4282
|
// src/components/MarkdownText.tsx
|
|
3384
4283
|
import { Fragment as Fragment4 } from "react";
|
|
3385
|
-
import { jsx as
|
|
4284
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3386
4285
|
function tokenize(text) {
|
|
3387
4286
|
const tokens = [];
|
|
3388
4287
|
let remaining = text;
|
|
@@ -3444,13 +4343,13 @@ function tokensToElements(tokens) {
|
|
|
3444
4343
|
return tokens.map((token, index) => {
|
|
3445
4344
|
switch (token.type) {
|
|
3446
4345
|
case "text":
|
|
3447
|
-
return /* @__PURE__ */
|
|
4346
|
+
return /* @__PURE__ */ jsx13(Fragment4, { children: token.content }, index);
|
|
3448
4347
|
case "bold":
|
|
3449
|
-
return /* @__PURE__ */
|
|
4348
|
+
return /* @__PURE__ */ jsx13("strong", { children: token.content }, index);
|
|
3450
4349
|
case "italic":
|
|
3451
|
-
return /* @__PURE__ */
|
|
4350
|
+
return /* @__PURE__ */ jsx13("em", { children: token.content }, index);
|
|
3452
4351
|
case "link":
|
|
3453
|
-
return /* @__PURE__ */
|
|
4352
|
+
return /* @__PURE__ */ jsx13(
|
|
3454
4353
|
"a",
|
|
3455
4354
|
{
|
|
3456
4355
|
href: token.url,
|
|
@@ -3462,7 +4361,7 @@ function tokensToElements(tokens) {
|
|
|
3462
4361
|
index
|
|
3463
4362
|
);
|
|
3464
4363
|
case "newline":
|
|
3465
|
-
return /* @__PURE__ */
|
|
4364
|
+
return /* @__PURE__ */ jsx13("br", {}, index);
|
|
3466
4365
|
default:
|
|
3467
4366
|
return null;
|
|
3468
4367
|
}
|
|
@@ -3474,15 +4373,15 @@ function parseMarkdownToElements(content) {
|
|
|
3474
4373
|
}
|
|
3475
4374
|
function MarkdownText({ content, className }) {
|
|
3476
4375
|
const elements = parseMarkdownToElements(content);
|
|
3477
|
-
return /* @__PURE__ */
|
|
4376
|
+
return /* @__PURE__ */ jsx13("span", { className, children: elements });
|
|
3478
4377
|
}
|
|
3479
4378
|
|
|
3480
4379
|
// src/router/Link.tsx
|
|
3481
4380
|
import { Link as WouterLink2 } from "wouter";
|
|
3482
|
-
import { jsx as
|
|
4381
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3483
4382
|
function Link2({ to, href, children, className, onClick, replace, ...props }) {
|
|
3484
4383
|
const target = href ?? to ?? "/";
|
|
3485
|
-
return /* @__PURE__ */
|
|
4384
|
+
return /* @__PURE__ */ jsx14(WouterLink2, { href: target, className, onClick, replace, ...props, children });
|
|
3486
4385
|
}
|
|
3487
4386
|
|
|
3488
4387
|
// src/router/useNavigate.ts
|
|
@@ -3501,7 +4400,7 @@ function useNavigate() {
|
|
|
3501
4400
|
|
|
3502
4401
|
// src/router/Router.tsx
|
|
3503
4402
|
import { Router as WouterRouter } from "wouter";
|
|
3504
|
-
import { jsx as
|
|
4403
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
3505
4404
|
function detectBasename() {
|
|
3506
4405
|
if (typeof window === "undefined") return "";
|
|
3507
4406
|
const sessionMatch = window.location.pathname.match(/^\/session\/[^/]+/);
|
|
@@ -3516,7 +4415,7 @@ function detectBasename() {
|
|
|
3516
4415
|
}
|
|
3517
4416
|
function Router({ children, base }) {
|
|
3518
4417
|
const basename = base ?? detectBasename();
|
|
3519
|
-
return /* @__PURE__ */
|
|
4418
|
+
return /* @__PURE__ */ jsx15(WouterRouter, { base: basename, children });
|
|
3520
4419
|
}
|
|
3521
4420
|
|
|
3522
4421
|
// src/router/index.ts
|
|
@@ -3533,9 +4432,11 @@ export {
|
|
|
3533
4432
|
MpImage as StaticImage,
|
|
3534
4433
|
MpText as StaticText,
|
|
3535
4434
|
Switch,
|
|
4435
|
+
YaContainer,
|
|
3536
4436
|
YaImage,
|
|
3537
4437
|
YaLink,
|
|
3538
4438
|
YaText,
|
|
4439
|
+
YaVideo,
|
|
3539
4440
|
buildIntermediateText,
|
|
3540
4441
|
calculateAnimationTiming,
|
|
3541
4442
|
computeTextDiff,
|
|
@@ -3548,9 +4449,12 @@ export {
|
|
|
3548
4449
|
imageCrossfadeStrategy,
|
|
3549
4450
|
initBuilderSelection,
|
|
3550
4451
|
linkTransitionStrategy,
|
|
4452
|
+
parseBackgroundConfig,
|
|
3551
4453
|
registerContent,
|
|
3552
4454
|
resolveAssetUrl,
|
|
4455
|
+
serializeBackgroundConfig,
|
|
3553
4456
|
serializeImageValue,
|
|
4457
|
+
serializeVideoValue,
|
|
3554
4458
|
setAssetResolver,
|
|
3555
4459
|
stripHtml,
|
|
3556
4460
|
textTypingStrategy,
|