@yoamigo.com/core 0.1.14 → 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 +1025 -103
- package/dist/lib.js +18 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,6 +168,7 @@ var BuilderSelectionManager = class {
|
|
|
168
168
|
this.setupScrollResizeListeners();
|
|
169
169
|
this.setupKeyboardListener();
|
|
170
170
|
this.setupWheelForwarding();
|
|
171
|
+
this.setupHistoryInterception();
|
|
171
172
|
this.notifyPageReady();
|
|
172
173
|
window.addEventListener("popstate", () => this.notifyPageReady());
|
|
173
174
|
}
|
|
@@ -486,6 +487,23 @@ var BuilderSelectionManager = class {
|
|
|
486
487
|
}
|
|
487
488
|
}, { passive: false });
|
|
488
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Intercept History API to detect SPA navigation
|
|
492
|
+
* Catches programmatic navigation (wouter, react-router, etc.)
|
|
493
|
+
* Standard pattern used by analytics tools like Google Analytics
|
|
494
|
+
*/
|
|
495
|
+
setupHistoryInterception() {
|
|
496
|
+
const originalPushState = history.pushState.bind(history);
|
|
497
|
+
history.pushState = (...args) => {
|
|
498
|
+
originalPushState(...args);
|
|
499
|
+
this.notifyPageReady();
|
|
500
|
+
};
|
|
501
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
502
|
+
history.replaceState = (...args) => {
|
|
503
|
+
originalReplaceState(...args);
|
|
504
|
+
this.notifyPageReady();
|
|
505
|
+
};
|
|
506
|
+
}
|
|
489
507
|
showHoverOverlay(element) {
|
|
490
508
|
if (!this.hoverOverlay) return;
|
|
491
509
|
const rect = element.getBoundingClientRect();
|
|
@@ -1201,7 +1219,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1201
1219
|
processingRef.current = true;
|
|
1202
1220
|
const fieldId = queueRef.current.shift();
|
|
1203
1221
|
const state = animationsRef.current.get(fieldId);
|
|
1204
|
-
if (state
|
|
1222
|
+
if (state?.status === "pending") {
|
|
1205
1223
|
state.status = "animating";
|
|
1206
1224
|
state.startTime = performance.now();
|
|
1207
1225
|
notifyListeners(fieldId);
|
|
@@ -1214,7 +1232,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1214
1232
|
const queueAnimation = useCallback3(
|
|
1215
1233
|
(fieldId, config) => {
|
|
1216
1234
|
const existing = animationsRef.current.get(fieldId);
|
|
1217
|
-
if (existing
|
|
1235
|
+
if (existing?.status === "animating") {
|
|
1218
1236
|
animationsRef.current.delete(fieldId);
|
|
1219
1237
|
}
|
|
1220
1238
|
const state = {
|
|
@@ -2490,7 +2508,7 @@ function YaTooltip({
|
|
|
2490
2508
|
}
|
|
2491
2509
|
|
|
2492
2510
|
// src/components/ya-image.css
|
|
2493
|
-
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');
|
|
2494
2512
|
|
|
2495
2513
|
// src/components/YaImage.tsx
|
|
2496
2514
|
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -2543,6 +2561,8 @@ function YaImage({
|
|
|
2543
2561
|
const [isSelected, setIsSelected] = useState6(false);
|
|
2544
2562
|
const [isHovered, setIsHovered] = useState6(false);
|
|
2545
2563
|
const [isSmallImage, setIsSmallImage] = useState6(false);
|
|
2564
|
+
const [isDropMode, setIsDropMode] = useState6(false);
|
|
2565
|
+
const [isDropHover, setIsDropHover] = useState6(false);
|
|
2546
2566
|
const rawValue = getValue(fieldId);
|
|
2547
2567
|
const imageData = parseImageValue(rawValue);
|
|
2548
2568
|
const src = imageData.src || fallbackSrc || PLACEHOLDER_SVG;
|
|
@@ -2592,6 +2612,84 @@ function YaImage({
|
|
|
2592
2612
|
window.addEventListener("message", handleMessage2);
|
|
2593
2613
|
return () => window.removeEventListener("message", handleMessage2);
|
|
2594
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
|
+
);
|
|
2595
2693
|
useEffect6(() => {
|
|
2596
2694
|
if (mode !== "inline-edit") return;
|
|
2597
2695
|
const checkSize = () => {
|
|
@@ -2683,14 +2781,25 @@ function YaImage({
|
|
|
2683
2781
|
]
|
|
2684
2782
|
}
|
|
2685
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(" ");
|
|
2686
2791
|
return /* @__PURE__ */ jsxs3(
|
|
2687
2792
|
"div",
|
|
2688
2793
|
{
|
|
2689
2794
|
ref: containerRef,
|
|
2690
|
-
className:
|
|
2795
|
+
className: containerClasses,
|
|
2691
2796
|
onClick: handleClick,
|
|
2692
2797
|
onMouseEnter: () => setIsHovered(true),
|
|
2693
2798
|
onMouseLeave: () => setIsHovered(false),
|
|
2799
|
+
onDragEnter: handleDragEnter,
|
|
2800
|
+
onDragOver: handleDragOver,
|
|
2801
|
+
onDragLeave: handleDragLeave,
|
|
2802
|
+
onDrop: handleDrop,
|
|
2694
2803
|
"data-ya-restricted": "true",
|
|
2695
2804
|
"data-field-id": fieldId,
|
|
2696
2805
|
"data-ya-image": "true",
|
|
@@ -2733,8 +2842,393 @@ function YaImage({
|
|
|
2733
2842
|
);
|
|
2734
2843
|
}
|
|
2735
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
|
+
|
|
2736
3230
|
// src/components/YaLink.tsx
|
|
2737
|
-
import { useEffect as
|
|
3231
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState8, useCallback as useCallback9 } from "react";
|
|
2738
3232
|
import { createPortal as createPortal4 } from "react-dom";
|
|
2739
3233
|
import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
|
|
2740
3234
|
import { BubbleMenu as BubbleMenu2 } from "@tiptap/react/menus";
|
|
@@ -2747,7 +3241,7 @@ import { Link as WouterLink, useLocation } from "wouter";
|
|
|
2747
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');
|
|
2748
3242
|
|
|
2749
3243
|
// src/components/YaLink.tsx
|
|
2750
|
-
import { Fragment as Fragment3, jsx as
|
|
3244
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2751
3245
|
function isInternalPath(path) {
|
|
2752
3246
|
if (!path) return false;
|
|
2753
3247
|
if (path.startsWith("#")) return false;
|
|
@@ -2848,8 +3342,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2848
3342
|
const { getValue, setValue, mode, saveToWorker, getPages } = useContentStore();
|
|
2849
3343
|
const [, navigate] = useLocation();
|
|
2850
3344
|
const pages = availablePages ?? getPages();
|
|
2851
|
-
const [sections, setSections] =
|
|
2852
|
-
const [sectionsExpanded, setSectionsExpanded] =
|
|
3345
|
+
const [sections, setSections] = useState8([]);
|
|
3346
|
+
const [sectionsExpanded, setSectionsExpanded] = useState8(false);
|
|
2853
3347
|
const textFieldId = `${fieldId}.text`;
|
|
2854
3348
|
const hrefFieldId = `${fieldId}.href`;
|
|
2855
3349
|
const storeText = getValue(textFieldId);
|
|
@@ -2860,16 +3354,16 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2860
3354
|
const isExternal = isExternalHref(href);
|
|
2861
3355
|
const effectiveTarget = target ?? (isExternal ? "_blank" : void 0);
|
|
2862
3356
|
const effectiveRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
|
|
2863
|
-
const [editingMode, setEditingMode] =
|
|
2864
|
-
const [showEditPopover, setShowEditPopover] =
|
|
2865
|
-
const [originalText, setOriginalText] =
|
|
2866
|
-
const [originalHref, setOriginalHref] =
|
|
2867
|
-
const [currentHref, setCurrentHref] =
|
|
2868
|
-
const [isExternalUrl, setIsExternalUrl] =
|
|
2869
|
-
const [externalUrl, setExternalUrl] =
|
|
2870
|
-
const containerRef =
|
|
2871
|
-
const hrefPopoverRef =
|
|
2872
|
-
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);
|
|
2873
3367
|
const editor = useEditor2({
|
|
2874
3368
|
extensions: [
|
|
2875
3369
|
StarterKit2.configure({
|
|
@@ -2892,26 +3386,26 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2892
3386
|
}
|
|
2893
3387
|
}
|
|
2894
3388
|
});
|
|
2895
|
-
|
|
3389
|
+
useEffect8(() => {
|
|
2896
3390
|
if (editor && editingMode !== "text") {
|
|
2897
3391
|
if (editor.getHTML() !== text) {
|
|
2898
3392
|
editor.commands.setContent(text);
|
|
2899
3393
|
}
|
|
2900
3394
|
}
|
|
2901
3395
|
}, [text, editor, editingMode]);
|
|
2902
|
-
|
|
3396
|
+
useEffect8(() => {
|
|
2903
3397
|
if (editingMode !== "link") {
|
|
2904
3398
|
setCurrentHref(href);
|
|
2905
3399
|
}
|
|
2906
3400
|
}, [href, editingMode]);
|
|
2907
|
-
|
|
3401
|
+
useEffect8(() => {
|
|
2908
3402
|
return () => {
|
|
2909
3403
|
if (hidePopoverTimeoutRef.current) {
|
|
2910
3404
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
2911
3405
|
}
|
|
2912
3406
|
};
|
|
2913
3407
|
}, []);
|
|
2914
|
-
|
|
3408
|
+
useEffect8(() => {
|
|
2915
3409
|
if (editingMode !== "link") return;
|
|
2916
3410
|
const handleClickOutside = (event) => {
|
|
2917
3411
|
const target2 = event.target;
|
|
@@ -2925,7 +3419,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2925
3419
|
document.addEventListener("mousedown", handleClickOutside);
|
|
2926
3420
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
2927
3421
|
}, [editingMode, originalHref]);
|
|
2928
|
-
const handleSaveText =
|
|
3422
|
+
const handleSaveText = useCallback9(() => {
|
|
2929
3423
|
if (!editor) return;
|
|
2930
3424
|
let html = editor.getHTML();
|
|
2931
3425
|
html = html.replace(/<\/p><p>/g, "<br><br>").replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
@@ -2933,26 +3427,26 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2933
3427
|
saveToWorker?.(textFieldId, html);
|
|
2934
3428
|
setEditingMode(null);
|
|
2935
3429
|
}, [editor, textFieldId, setValue, saveToWorker]);
|
|
2936
|
-
const handleSaveLink =
|
|
3430
|
+
const handleSaveLink = useCallback9(() => {
|
|
2937
3431
|
setValue(hrefFieldId, currentHref);
|
|
2938
3432
|
saveToWorker?.(hrefFieldId, currentHref);
|
|
2939
3433
|
setEditingMode(null);
|
|
2940
3434
|
setIsExternalUrl(false);
|
|
2941
3435
|
setExternalUrl("");
|
|
2942
3436
|
}, [hrefFieldId, currentHref, setValue, saveToWorker]);
|
|
2943
|
-
const handleCancelText =
|
|
3437
|
+
const handleCancelText = useCallback9(() => {
|
|
2944
3438
|
if (editor) {
|
|
2945
3439
|
editor.commands.setContent(originalText);
|
|
2946
3440
|
}
|
|
2947
3441
|
setEditingMode(null);
|
|
2948
3442
|
}, [editor, originalText]);
|
|
2949
|
-
const handleCancelLink =
|
|
3443
|
+
const handleCancelLink = useCallback9(() => {
|
|
2950
3444
|
setCurrentHref(originalHref);
|
|
2951
3445
|
setEditingMode(null);
|
|
2952
3446
|
setIsExternalUrl(false);
|
|
2953
3447
|
setExternalUrl("");
|
|
2954
3448
|
}, [originalHref]);
|
|
2955
|
-
const handleClick =
|
|
3449
|
+
const handleClick = useCallback9(
|
|
2956
3450
|
(e) => {
|
|
2957
3451
|
const selectModeEnabled = window.__builderSelectModeEnabled;
|
|
2958
3452
|
if (selectModeEnabled) {
|
|
@@ -2984,7 +3478,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2984
3478
|
},
|
|
2985
3479
|
[href, navigate, onClick]
|
|
2986
3480
|
);
|
|
2987
|
-
const handleMouseEnter =
|
|
3481
|
+
const handleMouseEnter = useCallback9(() => {
|
|
2988
3482
|
if (hidePopoverTimeoutRef.current) {
|
|
2989
3483
|
clearTimeout(hidePopoverTimeoutRef.current);
|
|
2990
3484
|
hidePopoverTimeoutRef.current = null;
|
|
@@ -2993,19 +3487,19 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
2993
3487
|
setShowEditPopover(true);
|
|
2994
3488
|
}
|
|
2995
3489
|
}, [mode, editingMode]);
|
|
2996
|
-
const handleMouseLeave =
|
|
3490
|
+
const handleMouseLeave = useCallback9(() => {
|
|
2997
3491
|
hidePopoverTimeoutRef.current = window.setTimeout(() => {
|
|
2998
3492
|
if (!editingMode) {
|
|
2999
3493
|
setShowEditPopover(false);
|
|
3000
3494
|
}
|
|
3001
3495
|
}, 150);
|
|
3002
3496
|
}, [editingMode]);
|
|
3003
|
-
const handleFocus =
|
|
3497
|
+
const handleFocus = useCallback9(() => {
|
|
3004
3498
|
if (mode === "inline-edit" && !editingMode) {
|
|
3005
3499
|
setShowEditPopover(true);
|
|
3006
3500
|
}
|
|
3007
3501
|
}, [mode, editingMode]);
|
|
3008
|
-
const startEditText =
|
|
3502
|
+
const startEditText = useCallback9(() => {
|
|
3009
3503
|
setShowEditPopover(false);
|
|
3010
3504
|
setEditingMode("text");
|
|
3011
3505
|
setOriginalText(text);
|
|
@@ -3013,14 +3507,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3013
3507
|
editor?.chain().focus().selectAll().run();
|
|
3014
3508
|
}, 20);
|
|
3015
3509
|
}, [text, editor]);
|
|
3016
|
-
const startEditLink =
|
|
3510
|
+
const startEditLink = useCallback9(() => {
|
|
3017
3511
|
setShowEditPopover(false);
|
|
3018
3512
|
setEditingMode("link");
|
|
3019
3513
|
setOriginalHref(href);
|
|
3020
3514
|
setCurrentHref(href);
|
|
3021
3515
|
setSections(discoverSectionsFromDOM());
|
|
3022
3516
|
}, [href]);
|
|
3023
|
-
const handleKeyDown =
|
|
3517
|
+
const handleKeyDown = useCallback9(
|
|
3024
3518
|
(event) => {
|
|
3025
3519
|
if (editingMode !== "text") return;
|
|
3026
3520
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -3041,7 +3535,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3041
3535
|
},
|
|
3042
3536
|
[editingMode, handleSaveText, handleCancelText]
|
|
3043
3537
|
);
|
|
3044
|
-
const handleFontSizeChange =
|
|
3538
|
+
const handleFontSizeChange = useCallback9(
|
|
3045
3539
|
(e) => {
|
|
3046
3540
|
if (!editor) return;
|
|
3047
3541
|
const size = e.target.value;
|
|
@@ -3053,7 +3547,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3053
3547
|
},
|
|
3054
3548
|
[editor]
|
|
3055
3549
|
);
|
|
3056
|
-
const handleFontWeightChange =
|
|
3550
|
+
const handleFontWeightChange = useCallback9(
|
|
3057
3551
|
(e) => {
|
|
3058
3552
|
if (!editor) return;
|
|
3059
3553
|
const weight = e.target.value;
|
|
@@ -3065,11 +3559,11 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3065
3559
|
},
|
|
3066
3560
|
[editor]
|
|
3067
3561
|
);
|
|
3068
|
-
const handlePageSelect =
|
|
3562
|
+
const handlePageSelect = useCallback9((path) => {
|
|
3069
3563
|
setCurrentHref(path);
|
|
3070
3564
|
setIsExternalUrl(false);
|
|
3071
3565
|
}, []);
|
|
3072
|
-
const handleExternalUrlApply =
|
|
3566
|
+
const handleExternalUrlApply = useCallback9(() => {
|
|
3073
3567
|
if (externalUrl) {
|
|
3074
3568
|
setCurrentHref(externalUrl);
|
|
3075
3569
|
}
|
|
@@ -3085,9 +3579,9 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3085
3579
|
return attrs.fontWeight || "";
|
|
3086
3580
|
};
|
|
3087
3581
|
if (mode === "read-only") {
|
|
3088
|
-
const content = isIconMode ? children : /* @__PURE__ */
|
|
3582
|
+
const content = isIconMode ? children : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode });
|
|
3089
3583
|
if (isInternalPath(href)) {
|
|
3090
|
-
return /* @__PURE__ */
|
|
3584
|
+
return /* @__PURE__ */ jsx9(
|
|
3091
3585
|
WouterLink,
|
|
3092
3586
|
{
|
|
3093
3587
|
href,
|
|
@@ -3099,7 +3593,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3099
3593
|
}
|
|
3100
3594
|
);
|
|
3101
3595
|
}
|
|
3102
|
-
return /* @__PURE__ */
|
|
3596
|
+
return /* @__PURE__ */ jsx9(
|
|
3103
3597
|
Component,
|
|
3104
3598
|
{
|
|
3105
3599
|
ref: containerRef,
|
|
@@ -3114,8 +3608,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3114
3608
|
}
|
|
3115
3609
|
);
|
|
3116
3610
|
}
|
|
3117
|
-
return /* @__PURE__ */
|
|
3118
|
-
/* @__PURE__ */
|
|
3611
|
+
return /* @__PURE__ */ jsxs5("span", { className: "ya-link-wrapper", children: [
|
|
3612
|
+
/* @__PURE__ */ jsx9(
|
|
3119
3613
|
Component,
|
|
3120
3614
|
{
|
|
3121
3615
|
ref: containerRef,
|
|
@@ -3134,9 +3628,9 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3134
3628
|
children: isIconMode ? (
|
|
3135
3629
|
// Icon mode: render children directly, no text editing
|
|
3136
3630
|
children
|
|
3137
|
-
) : editor ? /* @__PURE__ */
|
|
3631
|
+
) : editor ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3138
3632
|
createPortal4(
|
|
3139
|
-
/* @__PURE__ */
|
|
3633
|
+
/* @__PURE__ */ jsxs5(
|
|
3140
3634
|
BubbleMenu2,
|
|
3141
3635
|
{
|
|
3142
3636
|
editor,
|
|
@@ -3144,28 +3638,28 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3144
3638
|
options: { offset: 6, placement: "top" },
|
|
3145
3639
|
className: "ya-bubble-menu",
|
|
3146
3640
|
children: [
|
|
3147
|
-
/* @__PURE__ */
|
|
3641
|
+
/* @__PURE__ */ jsx9(
|
|
3148
3642
|
"button",
|
|
3149
3643
|
{
|
|
3150
3644
|
type: "button",
|
|
3151
3645
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
3152
3646
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
3153
3647
|
title: "Bold",
|
|
3154
|
-
children: /* @__PURE__ */
|
|
3648
|
+
children: /* @__PURE__ */ jsx9("strong", { children: "B" })
|
|
3155
3649
|
}
|
|
3156
3650
|
),
|
|
3157
|
-
/* @__PURE__ */
|
|
3651
|
+
/* @__PURE__ */ jsx9(
|
|
3158
3652
|
"button",
|
|
3159
3653
|
{
|
|
3160
3654
|
type: "button",
|
|
3161
3655
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
3162
3656
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
3163
3657
|
title: "Italic",
|
|
3164
|
-
children: /* @__PURE__ */
|
|
3658
|
+
children: /* @__PURE__ */ jsx9("em", { children: "I" })
|
|
3165
3659
|
}
|
|
3166
3660
|
),
|
|
3167
|
-
/* @__PURE__ */
|
|
3168
|
-
/* @__PURE__ */
|
|
3661
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-bubble-divider" }),
|
|
3662
|
+
/* @__PURE__ */ jsxs5(
|
|
3169
3663
|
"select",
|
|
3170
3664
|
{
|
|
3171
3665
|
value: getCurrentFontSize(),
|
|
@@ -3173,12 +3667,12 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3173
3667
|
className: "ya-bubble-select",
|
|
3174
3668
|
title: "Font Size",
|
|
3175
3669
|
children: [
|
|
3176
|
-
/* @__PURE__ */
|
|
3177
|
-
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))
|
|
3178
3672
|
]
|
|
3179
3673
|
}
|
|
3180
3674
|
),
|
|
3181
|
-
/* @__PURE__ */
|
|
3675
|
+
/* @__PURE__ */ jsxs5(
|
|
3182
3676
|
"select",
|
|
3183
3677
|
{
|
|
3184
3678
|
value: getCurrentFontWeight(),
|
|
@@ -3186,8 +3680,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3186
3680
|
className: "ya-bubble-select",
|
|
3187
3681
|
title: "Font Weight",
|
|
3188
3682
|
children: [
|
|
3189
|
-
/* @__PURE__ */
|
|
3190
|
-
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))
|
|
3191
3685
|
]
|
|
3192
3686
|
}
|
|
3193
3687
|
)
|
|
@@ -3196,39 +3690,39 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3196
3690
|
),
|
|
3197
3691
|
document.body
|
|
3198
3692
|
),
|
|
3199
|
-
editingMode === "text" ? /* @__PURE__ */
|
|
3200
|
-
/* @__PURE__ */
|
|
3201
|
-
/* @__PURE__ */
|
|
3202
|
-
/* @__PURE__ */
|
|
3203
|
-
/* @__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" })
|
|
3204
3698
|
] })
|
|
3205
|
-
] }) : /* @__PURE__ */
|
|
3206
|
-
] }) : /* @__PURE__ */
|
|
3699
|
+
] }) : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode })
|
|
3700
|
+
] }) : /* @__PURE__ */ jsx9(SafeHtml, { content: text, mode })
|
|
3207
3701
|
}
|
|
3208
3702
|
),
|
|
3209
|
-
showEditPopover && !editingMode && mode === "inline-edit" && /* @__PURE__ */
|
|
3210
|
-
!isIconMode && /* @__PURE__ */
|
|
3211
|
-
/* @__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" })
|
|
3212
3706
|
] }),
|
|
3213
|
-
editingMode === "link" && /* @__PURE__ */
|
|
3214
|
-
/* @__PURE__ */
|
|
3215
|
-
!isExternalUrl ? /* @__PURE__ */
|
|
3216
|
-
sections.length > 0 && /* @__PURE__ */
|
|
3217
|
-
/* @__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(
|
|
3218
3712
|
"button",
|
|
3219
3713
|
{
|
|
3220
3714
|
type: "button",
|
|
3221
3715
|
className: "ya-href-popover-label ya-href-collapsible-header",
|
|
3222
3716
|
onClick: () => setSectionsExpanded(!sectionsExpanded),
|
|
3223
3717
|
children: [
|
|
3224
|
-
/* @__PURE__ */
|
|
3718
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-chevron", children: sectionsExpanded ? "\u25BC" : "\u25B6" }),
|
|
3225
3719
|
"Scroll to section (",
|
|
3226
3720
|
sections.length,
|
|
3227
3721
|
")"
|
|
3228
3722
|
]
|
|
3229
3723
|
}
|
|
3230
3724
|
),
|
|
3231
|
-
sectionsExpanded && /* @__PURE__ */
|
|
3725
|
+
sectionsExpanded && /* @__PURE__ */ jsx9("div", { className: "ya-href-popover-pages", children: sections.map((section) => /* @__PURE__ */ jsxs5(
|
|
3232
3726
|
"button",
|
|
3233
3727
|
{
|
|
3234
3728
|
type: "button",
|
|
@@ -3236,15 +3730,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3236
3730
|
onClick: () => handlePageSelect(section.path),
|
|
3237
3731
|
children: [
|
|
3238
3732
|
section.label,
|
|
3239
|
-
/* @__PURE__ */
|
|
3733
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-page-path", children: section.path })
|
|
3240
3734
|
]
|
|
3241
3735
|
},
|
|
3242
3736
|
section.path
|
|
3243
3737
|
)) })
|
|
3244
3738
|
] }),
|
|
3245
|
-
pages.length > 0 && /* @__PURE__ */
|
|
3246
|
-
/* @__PURE__ */
|
|
3247
|
-
/* @__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(
|
|
3248
3742
|
"button",
|
|
3249
3743
|
{
|
|
3250
3744
|
type: "button",
|
|
@@ -3252,13 +3746,13 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3252
3746
|
onClick: () => handlePageSelect(page.path),
|
|
3253
3747
|
children: [
|
|
3254
3748
|
page.label,
|
|
3255
|
-
/* @__PURE__ */
|
|
3749
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-href-page-path", children: page.path })
|
|
3256
3750
|
]
|
|
3257
3751
|
},
|
|
3258
3752
|
page.path
|
|
3259
3753
|
)) })
|
|
3260
3754
|
] }),
|
|
3261
|
-
/* @__PURE__ */
|
|
3755
|
+
/* @__PURE__ */ jsx9(
|
|
3262
3756
|
"button",
|
|
3263
3757
|
{
|
|
3264
3758
|
type: "button",
|
|
@@ -3270,10 +3764,10 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3270
3764
|
children: "Use external URL instead"
|
|
3271
3765
|
}
|
|
3272
3766
|
)
|
|
3273
|
-
] }) : /* @__PURE__ */
|
|
3274
|
-
/* @__PURE__ */
|
|
3275
|
-
/* @__PURE__ */
|
|
3276
|
-
/* @__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(
|
|
3277
3771
|
"input",
|
|
3278
3772
|
{
|
|
3279
3773
|
type: "url",
|
|
@@ -3285,21 +3779,444 @@ function YaLink({ fieldId, href: defaultHref = "#", className, style, as: Compon
|
|
|
3285
3779
|
}
|
|
3286
3780
|
)
|
|
3287
3781
|
] }),
|
|
3288
|
-
/* @__PURE__ */
|
|
3782
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: "ya-href-external-toggle", onClick: () => setIsExternalUrl(false), children: "\u2190 Back to pages" })
|
|
3289
3783
|
] }),
|
|
3290
|
-
/* @__PURE__ */
|
|
3291
|
-
/* @__PURE__ */
|
|
3292
|
-
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" })
|
|
3293
3787
|
] })
|
|
3294
3788
|
] })
|
|
3295
3789
|
] });
|
|
3296
3790
|
}
|
|
3297
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
|
+
|
|
3298
4215
|
// src/components/StaticText.tsx
|
|
3299
|
-
import { jsx as
|
|
4216
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
3300
4217
|
function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
3301
4218
|
const content = getContent(fieldId) || (typeof children === "string" ? children : "");
|
|
3302
|
-
return /* @__PURE__ */
|
|
4219
|
+
return /* @__PURE__ */ jsx11(
|
|
3303
4220
|
Component,
|
|
3304
4221
|
{
|
|
3305
4222
|
className,
|
|
@@ -3310,7 +4227,7 @@ function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3310
4227
|
}
|
|
3311
4228
|
|
|
3312
4229
|
// src/components/StaticImage.tsx
|
|
3313
|
-
import { jsx as
|
|
4230
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
3314
4231
|
function parseImageValue2(value) {
|
|
3315
4232
|
if (!value) {
|
|
3316
4233
|
return { src: "" };
|
|
@@ -3324,7 +4241,7 @@ function parseImageValue2(value) {
|
|
|
3324
4241
|
}
|
|
3325
4242
|
return { src: value };
|
|
3326
4243
|
}
|
|
3327
|
-
function
|
|
4244
|
+
function getObjectPosition3(imageData) {
|
|
3328
4245
|
if (imageData.focalPoint) {
|
|
3329
4246
|
return `${imageData.focalPoint.x}% ${imageData.focalPoint.y}%`;
|
|
3330
4247
|
}
|
|
@@ -3345,8 +4262,8 @@ function MpImage({
|
|
|
3345
4262
|
const src = imageData.src || fallbackSrc || "";
|
|
3346
4263
|
const altText = imageData.alt || alt || fallbackAlt || "";
|
|
3347
4264
|
const objectFit = imageData.objectFit || propObjectFit || "cover";
|
|
3348
|
-
const objectPosition =
|
|
3349
|
-
return /* @__PURE__ */
|
|
4265
|
+
const objectPosition = getObjectPosition3(imageData) || propObjectPosition || "50% 50%";
|
|
4266
|
+
return /* @__PURE__ */ jsx12(
|
|
3350
4267
|
"img",
|
|
3351
4268
|
{
|
|
3352
4269
|
src: resolveAssetUrl(src),
|
|
@@ -3364,7 +4281,7 @@ function MpImage({
|
|
|
3364
4281
|
|
|
3365
4282
|
// src/components/MarkdownText.tsx
|
|
3366
4283
|
import { Fragment as Fragment4 } from "react";
|
|
3367
|
-
import { jsx as
|
|
4284
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3368
4285
|
function tokenize(text) {
|
|
3369
4286
|
const tokens = [];
|
|
3370
4287
|
let remaining = text;
|
|
@@ -3426,13 +4343,13 @@ function tokensToElements(tokens) {
|
|
|
3426
4343
|
return tokens.map((token, index) => {
|
|
3427
4344
|
switch (token.type) {
|
|
3428
4345
|
case "text":
|
|
3429
|
-
return /* @__PURE__ */
|
|
4346
|
+
return /* @__PURE__ */ jsx13(Fragment4, { children: token.content }, index);
|
|
3430
4347
|
case "bold":
|
|
3431
|
-
return /* @__PURE__ */
|
|
4348
|
+
return /* @__PURE__ */ jsx13("strong", { children: token.content }, index);
|
|
3432
4349
|
case "italic":
|
|
3433
|
-
return /* @__PURE__ */
|
|
4350
|
+
return /* @__PURE__ */ jsx13("em", { children: token.content }, index);
|
|
3434
4351
|
case "link":
|
|
3435
|
-
return /* @__PURE__ */
|
|
4352
|
+
return /* @__PURE__ */ jsx13(
|
|
3436
4353
|
"a",
|
|
3437
4354
|
{
|
|
3438
4355
|
href: token.url,
|
|
@@ -3444,7 +4361,7 @@ function tokensToElements(tokens) {
|
|
|
3444
4361
|
index
|
|
3445
4362
|
);
|
|
3446
4363
|
case "newline":
|
|
3447
|
-
return /* @__PURE__ */
|
|
4364
|
+
return /* @__PURE__ */ jsx13("br", {}, index);
|
|
3448
4365
|
default:
|
|
3449
4366
|
return null;
|
|
3450
4367
|
}
|
|
@@ -3456,15 +4373,15 @@ function parseMarkdownToElements(content) {
|
|
|
3456
4373
|
}
|
|
3457
4374
|
function MarkdownText({ content, className }) {
|
|
3458
4375
|
const elements = parseMarkdownToElements(content);
|
|
3459
|
-
return /* @__PURE__ */
|
|
4376
|
+
return /* @__PURE__ */ jsx13("span", { className, children: elements });
|
|
3460
4377
|
}
|
|
3461
4378
|
|
|
3462
4379
|
// src/router/Link.tsx
|
|
3463
4380
|
import { Link as WouterLink2 } from "wouter";
|
|
3464
|
-
import { jsx as
|
|
4381
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3465
4382
|
function Link2({ to, href, children, className, onClick, replace, ...props }) {
|
|
3466
4383
|
const target = href ?? to ?? "/";
|
|
3467
|
-
return /* @__PURE__ */
|
|
4384
|
+
return /* @__PURE__ */ jsx14(WouterLink2, { href: target, className, onClick, replace, ...props, children });
|
|
3468
4385
|
}
|
|
3469
4386
|
|
|
3470
4387
|
// src/router/useNavigate.ts
|
|
@@ -3483,7 +4400,7 @@ function useNavigate() {
|
|
|
3483
4400
|
|
|
3484
4401
|
// src/router/Router.tsx
|
|
3485
4402
|
import { Router as WouterRouter } from "wouter";
|
|
3486
|
-
import { jsx as
|
|
4403
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
3487
4404
|
function detectBasename() {
|
|
3488
4405
|
if (typeof window === "undefined") return "";
|
|
3489
4406
|
const sessionMatch = window.location.pathname.match(/^\/session\/[^/]+/);
|
|
@@ -3498,7 +4415,7 @@ function detectBasename() {
|
|
|
3498
4415
|
}
|
|
3499
4416
|
function Router({ children, base }) {
|
|
3500
4417
|
const basename = base ?? detectBasename();
|
|
3501
|
-
return /* @__PURE__ */
|
|
4418
|
+
return /* @__PURE__ */ jsx15(WouterRouter, { base: basename, children });
|
|
3502
4419
|
}
|
|
3503
4420
|
|
|
3504
4421
|
// src/router/index.ts
|
|
@@ -3515,9 +4432,11 @@ export {
|
|
|
3515
4432
|
MpImage as StaticImage,
|
|
3516
4433
|
MpText as StaticText,
|
|
3517
4434
|
Switch,
|
|
4435
|
+
YaContainer,
|
|
3518
4436
|
YaImage,
|
|
3519
4437
|
YaLink,
|
|
3520
4438
|
YaText,
|
|
4439
|
+
YaVideo,
|
|
3521
4440
|
buildIntermediateText,
|
|
3522
4441
|
calculateAnimationTiming,
|
|
3523
4442
|
computeTextDiff,
|
|
@@ -3530,9 +4449,12 @@ export {
|
|
|
3530
4449
|
imageCrossfadeStrategy,
|
|
3531
4450
|
initBuilderSelection,
|
|
3532
4451
|
linkTransitionStrategy,
|
|
4452
|
+
parseBackgroundConfig,
|
|
3533
4453
|
registerContent,
|
|
3534
4454
|
resolveAssetUrl,
|
|
4455
|
+
serializeBackgroundConfig,
|
|
3535
4456
|
serializeImageValue,
|
|
4457
|
+
serializeVideoValue,
|
|
3536
4458
|
setAssetResolver,
|
|
3537
4459
|
stripHtml,
|
|
3538
4460
|
textTypingStrategy,
|