@ohhwells/bridge 0.1.14 → 0.1.16-next.8
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/README.md +198 -70
- package/dist/index.cjs +242 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +238 -61
- package/dist/index.js.map +1 -1
- package/dist/styles.css +127 -72
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -3435,7 +3435,9 @@ function ToggleGroupItem({
|
|
|
3435
3435
|
Toggle,
|
|
3436
3436
|
{
|
|
3437
3437
|
pressed: groupValue === value,
|
|
3438
|
-
onPressedChange: () =>
|
|
3438
|
+
onPressedChange: (pressed) => {
|
|
3439
|
+
if (pressed) onValueChange(value);
|
|
3440
|
+
},
|
|
3439
3441
|
className: cn(
|
|
3440
3442
|
"text-foreground cursor-pointer hover:bg-transparent hover:text-foreground",
|
|
3441
3443
|
"data-[state=on]:bg-background data-[state=on]:shadow-sm",
|
|
@@ -3470,8 +3472,30 @@ function isEditSessionActive() {
|
|
|
3470
3472
|
return new URLSearchParams(q).get("mode") === "edit";
|
|
3471
3473
|
}
|
|
3472
3474
|
|
|
3473
|
-
// src/
|
|
3475
|
+
// src/ui/badge.tsx
|
|
3474
3476
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3477
|
+
var badgeVariants = cva(
|
|
3478
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
3479
|
+
{
|
|
3480
|
+
variants: {
|
|
3481
|
+
variant: {
|
|
3482
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
3483
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
3484
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
3485
|
+
outline: "text-foreground"
|
|
3486
|
+
}
|
|
3487
|
+
},
|
|
3488
|
+
defaultVariants: {
|
|
3489
|
+
variant: "default"
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
);
|
|
3493
|
+
function Badge({ className, variant, ...props }) {
|
|
3494
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
// src/OhhwellsBridge.tsx
|
|
3498
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3475
3499
|
var PRIMARY = "#0885FE";
|
|
3476
3500
|
var IMAGE_FADE_MS = 300;
|
|
3477
3501
|
function runOpacityFade(el, onDone) {
|
|
@@ -3679,7 +3703,7 @@ var TOOLBAR_GROUPS = [
|
|
|
3679
3703
|
];
|
|
3680
3704
|
function GlowFrame({ rect, elRef }) {
|
|
3681
3705
|
const GAP = 6;
|
|
3682
|
-
return /* @__PURE__ */ (0,
|
|
3706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3683
3707
|
"div",
|
|
3684
3708
|
{
|
|
3685
3709
|
ref: elRef,
|
|
@@ -3730,7 +3754,7 @@ function FloatingToolbar({
|
|
|
3730
3754
|
activeCommands
|
|
3731
3755
|
}) {
|
|
3732
3756
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
3733
|
-
return /* @__PURE__ */ (0,
|
|
3757
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3734
3758
|
"div",
|
|
3735
3759
|
{
|
|
3736
3760
|
ref: elRef,
|
|
@@ -3753,11 +3777,11 @@ function FloatingToolbar({
|
|
|
3753
3777
|
pointerEvents: "auto",
|
|
3754
3778
|
whiteSpace: "nowrap"
|
|
3755
3779
|
},
|
|
3756
|
-
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
3757
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
3780
|
+
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react.default.Fragment, { children: [
|
|
3781
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
3758
3782
|
btns.map((btn) => {
|
|
3759
3783
|
const isActive = activeCommands.has(btn.cmd);
|
|
3760
|
-
return /* @__PURE__ */ (0,
|
|
3784
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3761
3785
|
"button",
|
|
3762
3786
|
{
|
|
3763
3787
|
title: btn.title,
|
|
@@ -3783,7 +3807,7 @@ function FloatingToolbar({
|
|
|
3783
3807
|
flexShrink: 0,
|
|
3784
3808
|
padding: 6
|
|
3785
3809
|
},
|
|
3786
|
-
children: /* @__PURE__ */ (0,
|
|
3810
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3787
3811
|
"svg",
|
|
3788
3812
|
{
|
|
3789
3813
|
width: "16",
|
|
@@ -3816,7 +3840,7 @@ function StateToggle({
|
|
|
3816
3840
|
states,
|
|
3817
3841
|
onStateChange
|
|
3818
3842
|
}) {
|
|
3819
|
-
return /* @__PURE__ */ (0,
|
|
3843
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3820
3844
|
ToggleGroup,
|
|
3821
3845
|
{
|
|
3822
3846
|
"data-ohw-state-toggle": "",
|
|
@@ -3830,7 +3854,7 @@ function StateToggle({
|
|
|
3830
3854
|
left: rect.right - 8,
|
|
3831
3855
|
transform: "translateX(-100%)"
|
|
3832
3856
|
},
|
|
3833
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
3857
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
3834
3858
|
}
|
|
3835
3859
|
);
|
|
3836
3860
|
}
|
|
@@ -3840,20 +3864,19 @@ function OhhwellsBridge() {
|
|
|
3840
3864
|
const router = (0, import_navigation.useRouter)();
|
|
3841
3865
|
const searchParams = (0, import_navigation.useSearchParams)();
|
|
3842
3866
|
const isEditMode = isEditSessionActive();
|
|
3843
|
-
(0, import_react.useEffect)(() => {
|
|
3844
|
-
if (!isEditMode) return;
|
|
3845
|
-
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
3846
|
-
const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
|
|
3847
|
-
const stylesheet = Object.assign(document.createElement("link"), { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap" });
|
|
3848
|
-
document.head.append(preconnect1, preconnect2, stylesheet);
|
|
3849
|
-
return () => {
|
|
3850
|
-
preconnect1.remove();
|
|
3851
|
-
preconnect2.remove();
|
|
3852
|
-
stylesheet.remove();
|
|
3853
|
-
};
|
|
3854
|
-
}, [isEditMode]);
|
|
3855
3867
|
const [bridgeRoot, setBridgeRoot] = (0, import_react.useState)(null);
|
|
3856
3868
|
(0, import_react.useEffect)(() => {
|
|
3869
|
+
const figtreeFontId = "ohw-figtree-font";
|
|
3870
|
+
if (!document.getElementById(figtreeFontId)) {
|
|
3871
|
+
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
3872
|
+
const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
|
|
3873
|
+
const stylesheet = Object.assign(document.createElement("link"), {
|
|
3874
|
+
id: figtreeFontId,
|
|
3875
|
+
rel: "stylesheet",
|
|
3876
|
+
href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
|
|
3877
|
+
});
|
|
3878
|
+
document.head.append(preconnect1, preconnect2, stylesheet);
|
|
3879
|
+
}
|
|
3857
3880
|
const el = document.createElement("div");
|
|
3858
3881
|
el.setAttribute("data-ohw-bridge-root", "");
|
|
3859
3882
|
el.setAttribute("data-ohw-bridge", "");
|
|
@@ -3884,6 +3907,10 @@ function OhhwellsBridge() {
|
|
|
3884
3907
|
const toolbarElRef = (0, import_react.useRef)(null);
|
|
3885
3908
|
const glowElRef = (0, import_react.useRef)(null);
|
|
3886
3909
|
const hoveredImageRef = (0, import_react.useRef)(null);
|
|
3910
|
+
const hoveredImageHasTextOverlapRef = (0, import_react.useRef)(false);
|
|
3911
|
+
const hoveredGapRef = (0, import_react.useRef)(null);
|
|
3912
|
+
const imageUnhoverTimerRef = (0, import_react.useRef)(null);
|
|
3913
|
+
const imageShowTimerRef = (0, import_react.useRef)(null);
|
|
3887
3914
|
const editStylesRef = (0, import_react.useRef)(null);
|
|
3888
3915
|
const activateRef = (0, import_react.useRef)(() => {
|
|
3889
3916
|
});
|
|
@@ -3897,6 +3924,7 @@ function OhhwellsBridge() {
|
|
|
3897
3924
|
const [toggleState, setToggleState] = (0, import_react.useState)(null);
|
|
3898
3925
|
const [maxBadge, setMaxBadge] = (0, import_react.useState)(null);
|
|
3899
3926
|
const [activeCommands, setActiveCommands] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
3927
|
+
const [sectionGap, setSectionGap] = (0, import_react.useState)(null);
|
|
3900
3928
|
(0, import_react.useEffect)(() => {
|
|
3901
3929
|
const update = () => {
|
|
3902
3930
|
const el = activeElRef.current;
|
|
@@ -3905,6 +3933,12 @@ function OhhwellsBridge() {
|
|
|
3905
3933
|
if (!prev || !activeStateElRef.current) return prev;
|
|
3906
3934
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
3907
3935
|
});
|
|
3936
|
+
setSectionGap((prev) => {
|
|
3937
|
+
if (!prev) return prev;
|
|
3938
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
3939
|
+
if (!anchor) return prev;
|
|
3940
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
3941
|
+
});
|
|
3908
3942
|
};
|
|
3909
3943
|
const vvp = window.visualViewport;
|
|
3910
3944
|
if (!vvp) return;
|
|
@@ -3946,6 +3980,10 @@ function OhhwellsBridge() {
|
|
|
3946
3980
|
const activate = (0, import_react.useCallback)((el) => {
|
|
3947
3981
|
if (activeElRef.current === el) return;
|
|
3948
3982
|
deactivate();
|
|
3983
|
+
if (hoveredImageRef.current) {
|
|
3984
|
+
hoveredImageRef.current = null;
|
|
3985
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
3986
|
+
}
|
|
3949
3987
|
el.setAttribute("contenteditable", "true");
|
|
3950
3988
|
el.removeAttribute("data-ohw-hovered");
|
|
3951
3989
|
activeElRef.current = el;
|
|
@@ -3993,7 +4031,7 @@ function OhhwellsBridge() {
|
|
|
3993
4031
|
}
|
|
3994
4032
|
let cancelled = false;
|
|
3995
4033
|
setFetchState("loading");
|
|
3996
|
-
const apiUrl =
|
|
4034
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
3997
4035
|
fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
3998
4036
|
if (cancelled) return;
|
|
3999
4037
|
const content = data?.content ?? {};
|
|
@@ -4120,6 +4158,7 @@ function OhhwellsBridge() {
|
|
|
4120
4158
|
[data-ohw-editable][contenteditable] *::selection { background: ${PRIMARY}59 !important; }
|
|
4121
4159
|
[data-ohw-editable-state], [data-ohw-editable-state] * { pointer-events: none !important; }
|
|
4122
4160
|
[data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
|
|
4161
|
+
[data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
|
|
4123
4162
|
`;
|
|
4124
4163
|
const forceHover = document.createElement("style");
|
|
4125
4164
|
forceHover.setAttribute("data-ohw-active-state-style", "");
|
|
@@ -4146,10 +4185,12 @@ function OhhwellsBridge() {
|
|
|
4146
4185
|
if (editable) {
|
|
4147
4186
|
if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
|
|
4148
4187
|
e.preventDefault();
|
|
4188
|
+
e.stopPropagation();
|
|
4149
4189
|
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
|
|
4150
4190
|
return;
|
|
4151
4191
|
}
|
|
4152
4192
|
e.preventDefault();
|
|
4193
|
+
e.stopPropagation();
|
|
4153
4194
|
activateRef.current(editable);
|
|
4154
4195
|
return;
|
|
4155
4196
|
}
|
|
@@ -4212,12 +4253,15 @@ function OhhwellsBridge() {
|
|
|
4212
4253
|
}
|
|
4213
4254
|
return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
|
|
4214
4255
|
};
|
|
4215
|
-
const postImageHover = (imgEl, isDragOver = false) => {
|
|
4256
|
+
const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
|
|
4216
4257
|
const r2 = getVisibleRect(imgEl);
|
|
4258
|
+
const hasTextOverlap = forceTextOverlap ?? false;
|
|
4259
|
+
hoveredImageHasTextOverlapRef.current = hasTextOverlap;
|
|
4217
4260
|
postToParentRef.current({
|
|
4218
4261
|
type: "ow:image-hover",
|
|
4219
4262
|
key: imgEl.dataset.ohwKey ?? "",
|
|
4220
4263
|
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
4264
|
+
hasTextOverlap,
|
|
4221
4265
|
...isDragOver ? { isDragOver: true } : {}
|
|
4222
4266
|
});
|
|
4223
4267
|
const track = imgEl.closest("[data-ohw-hover-pause]");
|
|
@@ -4264,7 +4308,7 @@ function OhhwellsBridge() {
|
|
|
4264
4308
|
}
|
|
4265
4309
|
return smallest;
|
|
4266
4310
|
};
|
|
4267
|
-
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false
|
|
4311
|
+
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
4268
4312
|
const toggleEl = document.querySelector("[data-ohw-state-toggle]");
|
|
4269
4313
|
if (toggleEl) {
|
|
4270
4314
|
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
@@ -4290,33 +4334,52 @@ function OhhwellsBridge() {
|
|
|
4290
4334
|
}
|
|
4291
4335
|
return;
|
|
4292
4336
|
}
|
|
4293
|
-
const
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4337
|
+
const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
|
|
4338
|
+
const textEditable = Array.from(
|
|
4339
|
+
document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
|
|
4340
|
+
).find((el) => {
|
|
4341
|
+
const er = el.getBoundingClientRect();
|
|
4342
|
+
return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
|
|
4343
|
+
});
|
|
4344
|
+
if (imageUnhoverTimerRef.current) {
|
|
4345
|
+
clearTimeout(imageUnhoverTimerRef.current);
|
|
4346
|
+
imageUnhoverTimerRef.current = null;
|
|
4347
|
+
}
|
|
4348
|
+
if (imageShowTimerRef.current) {
|
|
4349
|
+
clearTimeout(imageShowTimerRef.current);
|
|
4350
|
+
imageShowTimerRef.current = null;
|
|
4304
4351
|
}
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
const outlinePad = 8;
|
|
4308
|
-
const hr = activeHovered.getBoundingClientRect();
|
|
4309
|
-
if (x >= hr.left - outlinePad && x <= hr.right + outlinePad && y >= hr.top - outlinePad && y <= hr.bottom + outlinePad) {
|
|
4352
|
+
if (textEditable) {
|
|
4353
|
+
if (textEditable.hasAttribute("contenteditable")) {
|
|
4310
4354
|
if (hoveredImageRef.current) {
|
|
4311
4355
|
hoveredImageRef.current = null;
|
|
4356
|
+
hoveredImageHasTextOverlapRef.current = false;
|
|
4312
4357
|
resumeAnimTracks();
|
|
4313
4358
|
postToParentRef.current({ type: "ow:image-unhover" });
|
|
4314
4359
|
}
|
|
4315
4360
|
return;
|
|
4316
4361
|
}
|
|
4362
|
+
if (isStateCardImage) {
|
|
4363
|
+
if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
|
|
4364
|
+
hoveredImageRef.current = imgEl;
|
|
4365
|
+
postImageHover(imgEl, isDragOver, true);
|
|
4366
|
+
}
|
|
4367
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4368
|
+
textEditable.setAttribute("data-ohw-hovered", "");
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4371
|
+
if (hoveredImageRef.current) {
|
|
4372
|
+
hoveredImageRef.current = null;
|
|
4373
|
+
hoveredImageHasTextOverlapRef.current = false;
|
|
4374
|
+
resumeAnimTracks();
|
|
4375
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
4376
|
+
}
|
|
4377
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4378
|
+
textEditable.setAttribute("data-ohw-hovered", "");
|
|
4379
|
+
return;
|
|
4317
4380
|
}
|
|
4318
4381
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4319
|
-
if (imgEl !== hoveredImageRef.current) {
|
|
4382
|
+
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
4320
4383
|
hoveredImageRef.current = imgEl;
|
|
4321
4384
|
postImageHover(imgEl, isDragOver);
|
|
4322
4385
|
} else if (isDragOver) {
|
|
@@ -4325,9 +4388,29 @@ function OhhwellsBridge() {
|
|
|
4325
4388
|
} else {
|
|
4326
4389
|
const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
|
|
4327
4390
|
if (!inIframeView) return;
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4391
|
+
if (imageUnhoverTimerRef.current) {
|
|
4392
|
+
clearTimeout(imageUnhoverTimerRef.current);
|
|
4393
|
+
imageUnhoverTimerRef.current = null;
|
|
4394
|
+
}
|
|
4395
|
+
if (imageShowTimerRef.current) {
|
|
4396
|
+
clearTimeout(imageShowTimerRef.current);
|
|
4397
|
+
imageShowTimerRef.current = null;
|
|
4398
|
+
}
|
|
4399
|
+
if (hoveredImageRef.current) {
|
|
4400
|
+
hoveredImageRef.current = null;
|
|
4401
|
+
hoveredImageHasTextOverlapRef.current = false;
|
|
4402
|
+
resumeAnimTracks();
|
|
4403
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
4404
|
+
}
|
|
4405
|
+
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
4406
|
+
const textEl = Array.from(
|
|
4407
|
+
document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
|
|
4408
|
+
).find((el) => {
|
|
4409
|
+
const er = el.getBoundingClientRect();
|
|
4410
|
+
return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
|
|
4411
|
+
});
|
|
4412
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4413
|
+
if (textEl && !textEl.hasAttribute("contenteditable")) textEl.setAttribute("data-ohw-hovered", "");
|
|
4331
4414
|
}
|
|
4332
4415
|
};
|
|
4333
4416
|
const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
|
|
@@ -4346,28 +4429,54 @@ function OhhwellsBridge() {
|
|
|
4346
4429
|
}) ?? null;
|
|
4347
4430
|
const currentLocked = activeStateElRef.current?.hasAttribute("data-ohw-active-state") ? activeStateElRef.current : null;
|
|
4348
4431
|
const active = found ?? currentLocked;
|
|
4432
|
+
const prev = activeStateElRef.current;
|
|
4349
4433
|
activeStateElRef.current = active;
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
if (active
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4434
|
+
if (active !== prev) {
|
|
4435
|
+
if (prev) prev.removeAttribute("data-ohw-state-hovered");
|
|
4436
|
+
if (active) {
|
|
4437
|
+
if (active.querySelector("[data-ohw-state-view]")) active.setAttribute("data-ohw-state-hovered", "");
|
|
4438
|
+
const states = deriveStates(active.dataset.ohwEditableState ?? "");
|
|
4439
|
+
const activeState = active.hasAttribute("data-ohw-active-state") ? (active.getAttribute("data-ohw-active-state") ?? "Default").charAt(0).toUpperCase() + (active.getAttribute("data-ohw-active-state") ?? "").slice(1) : "Default";
|
|
4440
|
+
setToggleState({ rect: active.getBoundingClientRect(), activeState, states });
|
|
4441
|
+
} else {
|
|
4442
|
+
setToggleState(null);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
};
|
|
4446
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
4447
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
4448
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
4449
|
+
const ZONE = 20;
|
|
4450
|
+
for (let i = 0; i < sections.length; i++) {
|
|
4451
|
+
const a = sections[i];
|
|
4452
|
+
const b = sections[i + 1] ?? null;
|
|
4453
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
4454
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
4455
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
4456
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
4457
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
4458
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
4459
|
+
return;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
if (hoveredGapRef.current) {
|
|
4463
|
+
hoveredGapRef.current = null;
|
|
4464
|
+
setSectionGap(null);
|
|
4358
4465
|
}
|
|
4359
4466
|
};
|
|
4360
4467
|
const handleMouseMove = (e) => {
|
|
4361
4468
|
const { clientX, clientY } = e;
|
|
4362
4469
|
probeImageAt(clientX, clientY);
|
|
4363
4470
|
probeHoverCardsAt(clientX, clientY);
|
|
4471
|
+
probeSectionGapAt(clientX, clientY);
|
|
4364
4472
|
};
|
|
4365
4473
|
const handlePointerSync = (e) => {
|
|
4366
4474
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
4367
|
-
const { clientX, clientY
|
|
4475
|
+
const { clientX, clientY } = e.data;
|
|
4368
4476
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
4369
|
-
probeImageAt(clientX, clientY
|
|
4477
|
+
probeImageAt(clientX, clientY);
|
|
4370
4478
|
probeHoverCardsAt(clientX, clientY);
|
|
4479
|
+
probeSectionGapAt(clientX, clientY);
|
|
4371
4480
|
};
|
|
4372
4481
|
const handleDragOver = (e) => {
|
|
4373
4482
|
e.preventDefault();
|
|
@@ -4379,7 +4488,8 @@ function OhhwellsBridge() {
|
|
|
4379
4488
|
e.dataTransfer.dropEffect = "copy";
|
|
4380
4489
|
if (hoveredImageRef.current !== el) {
|
|
4381
4490
|
hoveredImageRef.current = el;
|
|
4382
|
-
|
|
4491
|
+
const isStateCard = !!el.closest("[data-ohw-editable-state]");
|
|
4492
|
+
postImageHover(el, true, isStateCard ? true : void 0);
|
|
4383
4493
|
}
|
|
4384
4494
|
};
|
|
4385
4495
|
const handleDragLeave = (e) => {
|
|
@@ -4565,11 +4675,13 @@ function OhhwellsBridge() {
|
|
|
4565
4675
|
setToggleState((prev) => prev ? { ...prev, rect } : null);
|
|
4566
4676
|
}
|
|
4567
4677
|
if (hoveredImageRef.current) {
|
|
4568
|
-
const
|
|
4678
|
+
const el = hoveredImageRef.current;
|
|
4679
|
+
const r2 = el.getBoundingClientRect();
|
|
4569
4680
|
postToParentRef.current({
|
|
4570
4681
|
type: "ow:image-hover",
|
|
4571
|
-
key:
|
|
4572
|
-
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
|
|
4682
|
+
key: el.dataset.ohwKey ?? "",
|
|
4683
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
4684
|
+
hasTextOverlap: hoveredImageHasTextOverlapRef.current
|
|
4573
4685
|
});
|
|
4574
4686
|
}
|
|
4575
4687
|
};
|
|
@@ -4643,12 +4755,49 @@ function OhhwellsBridge() {
|
|
|
4643
4755
|
parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
|
|
4644
4756
|
if (activeElRef.current) applyToolbarPos(activeElRef.current.getBoundingClientRect());
|
|
4645
4757
|
};
|
|
4758
|
+
const handleClickAt = (e) => {
|
|
4759
|
+
if (e.data?.type !== "ow:click-at") return;
|
|
4760
|
+
const { clientX, clientY } = e.data;
|
|
4761
|
+
const allImages = Array.from(
|
|
4762
|
+
document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
|
|
4763
|
+
).filter((el) => !!el.closest("[data-ohw-editable-state]"));
|
|
4764
|
+
const stateCardImage = allImages.find((el) => {
|
|
4765
|
+
const ownerCard = el.closest("[data-ohw-editable-state]");
|
|
4766
|
+
if (ownerCard) {
|
|
4767
|
+
const inHoverState = ownerCard.getAttribute("data-ohw-active-state") === "hover";
|
|
4768
|
+
const elState = el.dataset.ohwState;
|
|
4769
|
+
if (el === ownerCard && inHoverState) return false;
|
|
4770
|
+
if (elState === "default" && inHoverState) return false;
|
|
4771
|
+
if (elState === "hover" && !inHoverState) return false;
|
|
4772
|
+
}
|
|
4773
|
+
const r2 = el.getBoundingClientRect();
|
|
4774
|
+
return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
|
|
4775
|
+
});
|
|
4776
|
+
if (stateCardImage) {
|
|
4777
|
+
postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const textEditable = Array.from(
|
|
4781
|
+
document.querySelectorAll(
|
|
4782
|
+
'[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
|
|
4783
|
+
)
|
|
4784
|
+
).find((el) => {
|
|
4785
|
+
const r2 = el.getBoundingClientRect();
|
|
4786
|
+
return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
|
|
4787
|
+
});
|
|
4788
|
+
if (textEditable) {
|
|
4789
|
+
activateRef.current(textEditable);
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4792
|
+
deactivateRef.current();
|
|
4793
|
+
};
|
|
4646
4794
|
window.addEventListener("message", handleSave);
|
|
4647
4795
|
window.addEventListener("message", handleImageUrl);
|
|
4648
4796
|
window.addEventListener("message", handleAnimLock);
|
|
4649
4797
|
window.addEventListener("message", handleCanvasHeight);
|
|
4650
4798
|
window.addEventListener("message", handleParentScroll);
|
|
4651
4799
|
window.addEventListener("message", handlePointerSync);
|
|
4800
|
+
window.addEventListener("message", handleClickAt);
|
|
4652
4801
|
document.addEventListener("click", handleClick, true);
|
|
4653
4802
|
document.addEventListener("paste", handlePaste, true);
|
|
4654
4803
|
document.addEventListener("input", handleInput, true);
|
|
@@ -4682,10 +4831,13 @@ function OhhwellsBridge() {
|
|
|
4682
4831
|
window.removeEventListener("message", handleCanvasHeight);
|
|
4683
4832
|
window.removeEventListener("message", handleParentScroll);
|
|
4684
4833
|
window.removeEventListener("message", handlePointerSync);
|
|
4834
|
+
window.removeEventListener("message", handleClickAt);
|
|
4685
4835
|
window.removeEventListener("message", handleHydrate);
|
|
4686
4836
|
window.removeEventListener("message", handleDeactivate);
|
|
4687
4837
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
4688
4838
|
autoSaveTimers.current.clear();
|
|
4839
|
+
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
4840
|
+
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
4689
4841
|
};
|
|
4690
4842
|
}, [isEditMode, refreshStateRules]);
|
|
4691
4843
|
(0, import_react.useEffect)(() => {
|
|
@@ -4730,12 +4882,12 @@ function OhhwellsBridge() {
|
|
|
4730
4882
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
4731
4883
|
}, [deactivate]);
|
|
4732
4884
|
return bridgeRoot ? (0, import_react_dom.createPortal)(
|
|
4733
|
-
/* @__PURE__ */ (0,
|
|
4734
|
-
toolbarRect && /* @__PURE__ */ (0,
|
|
4735
|
-
/* @__PURE__ */ (0,
|
|
4736
|
-
/* @__PURE__ */ (0,
|
|
4885
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
4886
|
+
toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
4887
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
4888
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
|
|
4737
4889
|
] }),
|
|
4738
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
4890
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
4739
4891
|
"div",
|
|
4740
4892
|
{
|
|
4741
4893
|
"data-ohw-max-badge": "",
|
|
@@ -4761,7 +4913,7 @@ function OhhwellsBridge() {
|
|
|
4761
4913
|
]
|
|
4762
4914
|
}
|
|
4763
4915
|
),
|
|
4764
|
-
toggleState && /* @__PURE__ */ (0,
|
|
4916
|
+
toggleState && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
4765
4917
|
StateToggle,
|
|
4766
4918
|
{
|
|
4767
4919
|
rect: toggleState.rect,
|
|
@@ -4769,6 +4921,31 @@ function OhhwellsBridge() {
|
|
|
4769
4921
|
states: toggleState.states,
|
|
4770
4922
|
onStateChange: handleStateChange
|
|
4771
4923
|
}
|
|
4924
|
+
),
|
|
4925
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
4926
|
+
"div",
|
|
4927
|
+
{
|
|
4928
|
+
"data-ohw-section-insert-line": "",
|
|
4929
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
4930
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
4931
|
+
children: [
|
|
4932
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
4933
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
4934
|
+
Badge,
|
|
4935
|
+
{
|
|
4936
|
+
className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
|
|
4937
|
+
onClick: () => {
|
|
4938
|
+
window.parent.postMessage(
|
|
4939
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
4940
|
+
"*"
|
|
4941
|
+
);
|
|
4942
|
+
},
|
|
4943
|
+
children: "Add Section"
|
|
4944
|
+
}
|
|
4945
|
+
),
|
|
4946
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
4947
|
+
]
|
|
4948
|
+
}
|
|
4772
4949
|
)
|
|
4773
4950
|
] }),
|
|
4774
4951
|
bridgeRoot
|