@ohhwells/bridge 0.1.37 → 0.1.38-next.57
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 +12 -0
- package/dist/index.cjs +2191 -615
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -4
- package/dist/index.d.ts +51 -4
- package/dist/index.js +2189 -613
- package/dist/index.js.map +1 -1
- package/dist/styles.css +160 -34
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -60,15 +60,18 @@ __export(index_exports, {
|
|
|
60
60
|
module.exports = __toCommonJS(index_exports);
|
|
61
61
|
|
|
62
62
|
// src/OhhwellsBridge.tsx
|
|
63
|
-
var
|
|
63
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
64
64
|
var import_client = require("react-dom/client");
|
|
65
|
-
var
|
|
65
|
+
var import_react_dom2 = require("react-dom");
|
|
66
66
|
|
|
67
67
|
// src/linkHrefStore.ts
|
|
68
68
|
var linkHrefStore = /* @__PURE__ */ new Map();
|
|
69
69
|
function setStoredLinkHref(key, href) {
|
|
70
70
|
linkHrefStore.set(key, href);
|
|
71
71
|
}
|
|
72
|
+
function getStoredLinkHref(key) {
|
|
73
|
+
return linkHrefStore.get(key);
|
|
74
|
+
}
|
|
72
75
|
function enforceLinkHrefs() {
|
|
73
76
|
document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
|
|
74
77
|
const key = el.getAttribute("data-ohw-href-key") ?? "";
|
|
@@ -110,6 +113,7 @@ var import_react3 = require("react");
|
|
|
110
113
|
var import_react2 = require("react");
|
|
111
114
|
var import_radix_ui = require("radix-ui");
|
|
112
115
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
116
|
+
var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
113
117
|
function Spinner() {
|
|
114
118
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
115
119
|
"svg",
|
|
@@ -143,12 +147,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
143
147
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
144
148
|
const isBook = title === "Confirm your spot";
|
|
145
149
|
const handleSubmit = async () => {
|
|
146
|
-
|
|
150
|
+
const trimmed = email.trim();
|
|
151
|
+
if (!trimmed) return;
|
|
152
|
+
if (!isValidEmail(trimmed)) {
|
|
153
|
+
setError("Please enter a valid email address.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
147
156
|
setLoading(true);
|
|
148
157
|
setError(null);
|
|
149
158
|
try {
|
|
150
|
-
await onSubmit(
|
|
151
|
-
setSubmittedEmail(
|
|
159
|
+
await onSubmit(trimmed);
|
|
160
|
+
setSubmittedEmail(trimmed);
|
|
152
161
|
setSuccess(true);
|
|
153
162
|
} catch (e) {
|
|
154
163
|
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
@@ -223,7 +232,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
223
232
|
{
|
|
224
233
|
type: "email",
|
|
225
234
|
value: email,
|
|
226
|
-
onChange: (e) =>
|
|
235
|
+
onChange: (e) => {
|
|
236
|
+
setEmail(e.target.value);
|
|
237
|
+
if (error) setError(null);
|
|
238
|
+
},
|
|
227
239
|
onKeyDown: handleKeyDown,
|
|
228
240
|
placeholder: "hello@gmail.com",
|
|
229
241
|
autoFocus: true,
|
|
@@ -280,12 +292,20 @@ function formatClassTime(cls) {
|
|
|
280
292
|
const em = endTotal % 60;
|
|
281
293
|
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
282
294
|
}
|
|
295
|
+
function formatBookingLabel(cls) {
|
|
296
|
+
const price = cls.price;
|
|
297
|
+
const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
|
|
298
|
+
return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
|
|
299
|
+
}
|
|
283
300
|
function getBookingsOnDate(cls, date) {
|
|
284
301
|
if (!cls.bookings?.length) return 0;
|
|
285
|
-
const
|
|
302
|
+
const target = new Date(date);
|
|
303
|
+
target.setHours(0, 0, 0, 0);
|
|
286
304
|
return cls.bookings.filter((b) => {
|
|
287
305
|
try {
|
|
288
|
-
|
|
306
|
+
const bookingDate = new Date(b.classDate);
|
|
307
|
+
bookingDate.setHours(0, 0, 0, 0);
|
|
308
|
+
return bookingDate.getTime() === target.getTime();
|
|
289
309
|
} catch {
|
|
290
310
|
return false;
|
|
291
311
|
}
|
|
@@ -589,7 +609,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
589
609
|
if (!cls.id) return;
|
|
590
610
|
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
591
611
|
},
|
|
592
|
-
children: isFull ? "Join Waitlist" :
|
|
612
|
+
children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
|
|
593
613
|
}
|
|
594
614
|
)
|
|
595
615
|
] })
|
|
@@ -631,6 +651,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
631
651
|
const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
|
|
632
652
|
const [modalState, setModalState] = (0, import_react3.useState)(null);
|
|
633
653
|
const switchScheduleIdRef = (0, import_react3.useRef)(null);
|
|
654
|
+
const liveScheduleIdRef = (0, import_react3.useRef)(null);
|
|
655
|
+
const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
|
|
656
|
+
const id = liveScheduleIdRef.current;
|
|
657
|
+
if (!id) return;
|
|
658
|
+
try {
|
|
659
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
|
|
660
|
+
const data = await res.json();
|
|
661
|
+
if (data?.id) setSchedule(data);
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
}, []);
|
|
634
665
|
const dates = (0, import_react3.useMemo)(() => {
|
|
635
666
|
const today = /* @__PURE__ */ new Date();
|
|
636
667
|
today.setHours(0, 0, 0, 0);
|
|
@@ -650,6 +681,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
650
681
|
}
|
|
651
682
|
}
|
|
652
683
|
}, [schedule, dates]);
|
|
684
|
+
(0, import_react3.useEffect)(() => {
|
|
685
|
+
if (typeof window === "undefined") return;
|
|
686
|
+
const onVisible = () => {
|
|
687
|
+
if (!document.hidden) void refetchLiveSchedule();
|
|
688
|
+
};
|
|
689
|
+
window.addEventListener("focus", refetchLiveSchedule);
|
|
690
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
691
|
+
return () => {
|
|
692
|
+
window.removeEventListener("focus", refetchLiveSchedule);
|
|
693
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
694
|
+
};
|
|
695
|
+
}, [refetchLiveSchedule]);
|
|
653
696
|
(0, import_react3.useEffect)(() => {
|
|
654
697
|
if (typeof window === "undefined") return;
|
|
655
698
|
const isInEditor = window.self !== window.top;
|
|
@@ -711,7 +754,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
711
754
|
setLoading(false);
|
|
712
755
|
return;
|
|
713
756
|
}
|
|
714
|
-
;
|
|
757
|
+
liveScheduleIdRef.current = effectiveId;
|
|
715
758
|
(async () => {
|
|
716
759
|
try {
|
|
717
760
|
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
@@ -768,18 +811,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
768
811
|
const data = await res.json().catch(() => ({}));
|
|
769
812
|
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
770
813
|
}
|
|
814
|
+
await refetchLiveSchedule();
|
|
771
815
|
};
|
|
772
816
|
const handleReplaceSchedule = () => {
|
|
773
817
|
setIsHovered(false);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
insertAfter
|
|
779
|
-
}, "*");
|
|
780
|
-
} else {
|
|
781
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
782
|
-
}
|
|
818
|
+
window.parent.postMessage(
|
|
819
|
+
{ type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
|
|
820
|
+
"*"
|
|
821
|
+
);
|
|
783
822
|
};
|
|
784
823
|
if (!inEditor && !loading && !schedule) return null;
|
|
785
824
|
const sectionId = `scheduling-${insertAfter}`;
|
|
@@ -822,7 +861,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
822
861
|
),
|
|
823
862
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
824
863
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
825
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
865
|
+
"h2",
|
|
866
|
+
{
|
|
867
|
+
className: "font-display text-center m-0 text-(--color-dark,#200C02)",
|
|
868
|
+
style: {
|
|
869
|
+
fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
|
|
870
|
+
fontWeight: "var(--font-weight-heading, 400)",
|
|
871
|
+
lineHeight: 1.05,
|
|
872
|
+
letterSpacing: "-0.02em"
|
|
873
|
+
},
|
|
874
|
+
children: schedule?.name ?? "Book an appointment"
|
|
875
|
+
}
|
|
876
|
+
),
|
|
826
877
|
schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
827
878
|
] }),
|
|
828
879
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
@@ -4408,6 +4459,7 @@ function ItemActionToolbar({
|
|
|
4408
4459
|
onEditLink,
|
|
4409
4460
|
onAddItem,
|
|
4410
4461
|
onMore,
|
|
4462
|
+
editLinkDisabled = false,
|
|
4411
4463
|
addItemDisabled = true,
|
|
4412
4464
|
moreDisabled = true,
|
|
4413
4465
|
tooltipSide = "bottom"
|
|
@@ -4416,10 +4468,12 @@ function ItemActionToolbar({
|
|
|
4416
4468
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
4417
4469
|
ToolbarActionTooltip,
|
|
4418
4470
|
{
|
|
4419
|
-
label: "
|
|
4471
|
+
label: "Edit link",
|
|
4420
4472
|
side: tooltipSide,
|
|
4473
|
+
disabled: editLinkDisabled,
|
|
4421
4474
|
buttonProps: {
|
|
4422
4475
|
onMouseDown: (e) => {
|
|
4476
|
+
if (editLinkDisabled) return;
|
|
4423
4477
|
e.preventDefault();
|
|
4424
4478
|
e.stopPropagation();
|
|
4425
4479
|
onEditLink?.();
|
|
@@ -4591,9 +4645,223 @@ function ItemInteractionLayer({
|
|
|
4591
4645
|
);
|
|
4592
4646
|
}
|
|
4593
4647
|
|
|
4648
|
+
// src/ui/MediaOverlay.tsx
|
|
4649
|
+
var React6 = __toESM(require("react"), 1);
|
|
4650
|
+
var import_lucide_react3 = require("lucide-react");
|
|
4651
|
+
|
|
4652
|
+
// src/ui/button.tsx
|
|
4653
|
+
var React5 = __toESM(require("react"), 1);
|
|
4654
|
+
var import_radix_ui4 = require("radix-ui");
|
|
4655
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4656
|
+
var buttonVariants = cva(
|
|
4657
|
+
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
|
|
4658
|
+
{
|
|
4659
|
+
variants: {
|
|
4660
|
+
variant: {
|
|
4661
|
+
default: "bg-primary text-primary-foreground hover:opacity-90",
|
|
4662
|
+
outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
|
|
4663
|
+
ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
|
|
4664
|
+
},
|
|
4665
|
+
size: {
|
|
4666
|
+
default: "h-9",
|
|
4667
|
+
sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
|
|
4668
|
+
}
|
|
4669
|
+
},
|
|
4670
|
+
defaultVariants: {
|
|
4671
|
+
variant: "default",
|
|
4672
|
+
size: "default"
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
);
|
|
4676
|
+
var Button = React5.forwardRef(
|
|
4677
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
4678
|
+
const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
|
|
4679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4680
|
+
Comp,
|
|
4681
|
+
{
|
|
4682
|
+
ref,
|
|
4683
|
+
"data-slot": "button",
|
|
4684
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
4685
|
+
...props
|
|
4686
|
+
}
|
|
4687
|
+
);
|
|
4688
|
+
}
|
|
4689
|
+
);
|
|
4690
|
+
Button.displayName = "Button";
|
|
4691
|
+
|
|
4692
|
+
// src/ui/MediaOverlay.tsx
|
|
4693
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4694
|
+
var MEDIA_UPLOAD_FADE_MS = 300;
|
|
4695
|
+
var VIDEO_SETTINGS_BAR_INSET = 8;
|
|
4696
|
+
var OVERLAY_BUTTON_STYLE = {
|
|
4697
|
+
pointerEvents: "auto",
|
|
4698
|
+
fontFamily: "Inter, sans-serif",
|
|
4699
|
+
fontSize: 12,
|
|
4700
|
+
color: "#000"
|
|
4701
|
+
};
|
|
4702
|
+
var SKELETON_CSS = `
|
|
4703
|
+
@keyframes ohw-media-shimmer {
|
|
4704
|
+
0% { transform: translateX(-100%); }
|
|
4705
|
+
100% { transform: translateX(100%); }
|
|
4706
|
+
}
|
|
4707
|
+
[data-ohw-media-skeleton] {
|
|
4708
|
+
overflow: hidden;
|
|
4709
|
+
background-color: #d1d5db; /* tailwind gray-300 */
|
|
4710
|
+
}
|
|
4711
|
+
[data-ohw-media-skeleton]::after {
|
|
4712
|
+
content: "";
|
|
4713
|
+
position: absolute;
|
|
4714
|
+
inset: 0;
|
|
4715
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
|
|
4716
|
+
animation: ohw-media-shimmer 1.4s infinite;
|
|
4717
|
+
}
|
|
4718
|
+
`;
|
|
4719
|
+
function MediaOverlay({
|
|
4720
|
+
hover,
|
|
4721
|
+
isUploading,
|
|
4722
|
+
fadingOut = false,
|
|
4723
|
+
onFadeOutComplete,
|
|
4724
|
+
onReplace,
|
|
4725
|
+
onVideoSettingsChange
|
|
4726
|
+
}) {
|
|
4727
|
+
const { rect } = hover;
|
|
4728
|
+
const skeletonRef = React6.useRef(null);
|
|
4729
|
+
const isVideo = hover.elementType === "video";
|
|
4730
|
+
const autoplay = hover.videoAutoplay ?? true;
|
|
4731
|
+
const muted = hover.videoMuted ?? true;
|
|
4732
|
+
const box = {
|
|
4733
|
+
position: "fixed",
|
|
4734
|
+
top: rect.top,
|
|
4735
|
+
left: rect.left,
|
|
4736
|
+
width: rect.width,
|
|
4737
|
+
height: rect.height,
|
|
4738
|
+
zIndex: 2147483646
|
|
4739
|
+
};
|
|
4740
|
+
React6.useEffect(() => {
|
|
4741
|
+
if (!isUploading || !fadingOut || !skeletonRef.current) return;
|
|
4742
|
+
const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
4743
|
+
duration: MEDIA_UPLOAD_FADE_MS,
|
|
4744
|
+
easing: "ease-out",
|
|
4745
|
+
fill: "forwards"
|
|
4746
|
+
});
|
|
4747
|
+
anim.finished.then(() => onFadeOutComplete?.(hover.key)).catch(() => onFadeOutComplete?.(hover.key));
|
|
4748
|
+
return () => anim.cancel();
|
|
4749
|
+
}, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
|
|
4750
|
+
if (isUploading) {
|
|
4751
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4752
|
+
"div",
|
|
4753
|
+
{
|
|
4754
|
+
ref: skeletonRef,
|
|
4755
|
+
"data-ohw-bridge": "",
|
|
4756
|
+
"data-ohw-media-overlay": "",
|
|
4757
|
+
"data-ohw-media-skeleton": "",
|
|
4758
|
+
"aria-hidden": true,
|
|
4759
|
+
style: { ...box, pointerEvents: "none" },
|
|
4760
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: SKELETON_CSS })
|
|
4761
|
+
}
|
|
4762
|
+
);
|
|
4763
|
+
}
|
|
4764
|
+
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4765
|
+
"div",
|
|
4766
|
+
{
|
|
4767
|
+
"data-ohw-bridge": "",
|
|
4768
|
+
"data-ohw-media-overlay": "",
|
|
4769
|
+
className: "flex items-center justify-center gap-1.5",
|
|
4770
|
+
style: {
|
|
4771
|
+
position: "fixed",
|
|
4772
|
+
top: rect.top + VIDEO_SETTINGS_BAR_INSET,
|
|
4773
|
+
left: rect.left,
|
|
4774
|
+
width: rect.width,
|
|
4775
|
+
zIndex: 2147483647,
|
|
4776
|
+
pointerEvents: "auto"
|
|
4777
|
+
},
|
|
4778
|
+
onClick: (e) => e.stopPropagation(),
|
|
4779
|
+
children: [
|
|
4780
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4781
|
+
Button,
|
|
4782
|
+
{
|
|
4783
|
+
"data-ohw-media-overlay": "",
|
|
4784
|
+
variant: "outline",
|
|
4785
|
+
size: "sm",
|
|
4786
|
+
className: "cursor-pointer hover:bg-background",
|
|
4787
|
+
style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
|
|
4788
|
+
"aria-label": autoplay ? "Disable autoplay" : "Enable autoplay",
|
|
4789
|
+
title: autoplay ? "Autoplay on \u2014 click to disable" : "Autoplay off \u2014 click to enable",
|
|
4790
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4791
|
+
onClick: (e) => {
|
|
4792
|
+
e.stopPropagation();
|
|
4793
|
+
onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
|
|
4794
|
+
},
|
|
4795
|
+
children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Play, { size: 14 })
|
|
4796
|
+
}
|
|
4797
|
+
),
|
|
4798
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4799
|
+
Button,
|
|
4800
|
+
{
|
|
4801
|
+
"data-ohw-media-overlay": "",
|
|
4802
|
+
variant: "outline",
|
|
4803
|
+
size: "sm",
|
|
4804
|
+
className: "cursor-pointer hover:bg-background",
|
|
4805
|
+
style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
|
|
4806
|
+
"aria-label": muted ? "Unmute video" : "Mute video",
|
|
4807
|
+
title: muted ? "Muted \u2014 click to unmute" : "Sound on \u2014 click to mute",
|
|
4808
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4809
|
+
onClick: (e) => {
|
|
4810
|
+
e.stopPropagation();
|
|
4811
|
+
onVideoSettingsChange?.(hover.key, { muted: !muted });
|
|
4812
|
+
},
|
|
4813
|
+
children: muted ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Volume2, { size: 14 })
|
|
4814
|
+
}
|
|
4815
|
+
)
|
|
4816
|
+
]
|
|
4817
|
+
}
|
|
4818
|
+
) : null;
|
|
4819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
4820
|
+
settingsBar,
|
|
4821
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4822
|
+
"div",
|
|
4823
|
+
{
|
|
4824
|
+
"data-ohw-bridge": "",
|
|
4825
|
+
"data-ohw-media-overlay": "",
|
|
4826
|
+
className: "flex items-center justify-center cursor-pointer",
|
|
4827
|
+
style: {
|
|
4828
|
+
...box,
|
|
4829
|
+
// When text overlays this media, let clicks fall THROUGH to the text underneath. In the
|
|
4830
|
+
// parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
|
|
4831
|
+
// in-document, pointer-events does it natively. The button below opts back in, so
|
|
4832
|
+
// Replace still works.
|
|
4833
|
+
pointerEvents: hover.hasTextOverlap ? "none" : "auto",
|
|
4834
|
+
boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
|
|
4835
|
+
background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
4836
|
+
},
|
|
4837
|
+
onClick: () => onReplace(hover.key),
|
|
4838
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4839
|
+
Button,
|
|
4840
|
+
{
|
|
4841
|
+
"data-ohw-media-overlay": "",
|
|
4842
|
+
variant: "outline",
|
|
4843
|
+
size: "sm",
|
|
4844
|
+
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
4845
|
+
style: OVERLAY_BUTTON_STYLE,
|
|
4846
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4847
|
+
onClick: (e) => {
|
|
4848
|
+
e.stopPropagation();
|
|
4849
|
+
onReplace(hover.key);
|
|
4850
|
+
},
|
|
4851
|
+
children: [
|
|
4852
|
+
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.ImageIcon, { size: 14 }),
|
|
4853
|
+
isVideo ? "Replace video" : "Replace image"
|
|
4854
|
+
]
|
|
4855
|
+
}
|
|
4856
|
+
)
|
|
4857
|
+
}
|
|
4858
|
+
)
|
|
4859
|
+
] });
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4594
4862
|
// src/OhhwellsBridge.tsx
|
|
4595
|
-
var
|
|
4596
|
-
var
|
|
4863
|
+
var import_react_dom3 = require("react-dom");
|
|
4864
|
+
var import_navigation2 = require("next/navigation");
|
|
4597
4865
|
|
|
4598
4866
|
// src/lib/session-search.ts
|
|
4599
4867
|
var STORAGE_KEY = "ohw-preserved-search";
|
|
@@ -4869,62 +5137,30 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
|
|
|
4869
5137
|
tick();
|
|
4870
5138
|
}
|
|
4871
5139
|
|
|
4872
|
-
// src/ui/
|
|
4873
|
-
var
|
|
4874
|
-
var import_radix_ui4 = require("radix-ui");
|
|
4875
|
-
|
|
4876
|
-
// src/ui/icons.tsx
|
|
4877
|
-
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4878
|
-
function IconX({ className, ...props }) {
|
|
4879
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4880
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M18 6 6 18" }),
|
|
4881
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m6 6 12 12" })
|
|
4882
|
-
] });
|
|
4883
|
-
}
|
|
4884
|
-
function IconChevronDown({ className, ...props }) {
|
|
4885
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m6 9 6 6 6-6" }) });
|
|
4886
|
-
}
|
|
4887
|
-
function IconFile({ className, ...props }) {
|
|
4888
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4889
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4890
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4891
|
-
] });
|
|
4892
|
-
}
|
|
4893
|
-
function IconInfo({ className, ...props }) {
|
|
4894
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4895
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4896
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 16v-4" }),
|
|
4897
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 8h.01" })
|
|
4898
|
-
] });
|
|
4899
|
-
}
|
|
4900
|
-
function IconArrowRight({ className, ...props }) {
|
|
4901
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4902
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M5 12h14" }),
|
|
4903
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m12 5 7 7-7 7" })
|
|
4904
|
-
] });
|
|
4905
|
-
}
|
|
4906
|
-
function IconSection({ className, ...props }) {
|
|
4907
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4908
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4909
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4910
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4911
|
-
] });
|
|
4912
|
-
}
|
|
5140
|
+
// src/ui/link-modal/LinkPopover.tsx
|
|
5141
|
+
var import_react7 = require("react");
|
|
4913
5142
|
|
|
4914
5143
|
// src/ui/dialog.tsx
|
|
4915
|
-
var
|
|
4916
|
-
|
|
4917
|
-
|
|
5144
|
+
var React7 = __toESM(require("react"), 1);
|
|
5145
|
+
var import_radix_ui5 = require("radix-ui");
|
|
5146
|
+
var import_lucide_react4 = require("lucide-react");
|
|
5147
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
5148
|
+
function Dialog2({
|
|
5149
|
+
...props
|
|
5150
|
+
}) {
|
|
5151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
4918
5152
|
}
|
|
4919
|
-
function DialogPortal({
|
|
4920
|
-
|
|
5153
|
+
function DialogPortal({
|
|
5154
|
+
...props
|
|
5155
|
+
}) {
|
|
5156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
|
|
4921
5157
|
}
|
|
4922
5158
|
function DialogOverlay({
|
|
4923
5159
|
className,
|
|
4924
5160
|
...props
|
|
4925
5161
|
}) {
|
|
4926
|
-
return /* @__PURE__ */ (0,
|
|
4927
|
-
|
|
5162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5163
|
+
import_radix_ui5.Dialog.Overlay,
|
|
4928
5164
|
{
|
|
4929
5165
|
"data-slot": "dialog-overlay",
|
|
4930
5166
|
"data-ohw-link-modal-root": "",
|
|
@@ -4933,120 +5169,118 @@ function DialogOverlay({
|
|
|
4933
5169
|
}
|
|
4934
5170
|
);
|
|
4935
5171
|
}
|
|
4936
|
-
var DialogContent =
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
/* @__PURE__ */ (0,
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
children
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
});
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
5172
|
+
var DialogContent = React7.forwardRef(
|
|
5173
|
+
({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
5174
|
+
const positionMode = container ? "absolute" : "fixed";
|
|
5175
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
5176
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
5177
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
5178
|
+
import_radix_ui5.Dialog.Content,
|
|
5179
|
+
{
|
|
5180
|
+
ref,
|
|
5181
|
+
"data-slot": "dialog-content",
|
|
5182
|
+
className: cn(
|
|
5183
|
+
positionMode,
|
|
5184
|
+
"left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
|
|
5185
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
5186
|
+
container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
|
|
5187
|
+
className
|
|
5188
|
+
),
|
|
5189
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5190
|
+
...props,
|
|
5191
|
+
children: [
|
|
5192
|
+
children,
|
|
5193
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5194
|
+
import_radix_ui5.Dialog.Close,
|
|
5195
|
+
{
|
|
5196
|
+
type: "button",
|
|
5197
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
5198
|
+
"aria-label": "Close",
|
|
5199
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
|
|
5200
|
+
}
|
|
5201
|
+
) : null
|
|
5202
|
+
]
|
|
5203
|
+
}
|
|
5204
|
+
)
|
|
5205
|
+
] });
|
|
5206
|
+
}
|
|
5207
|
+
);
|
|
5208
|
+
DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
|
|
5209
|
+
function DialogHeader({
|
|
5210
|
+
className,
|
|
5211
|
+
...props
|
|
5212
|
+
}) {
|
|
5213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
4973
5214
|
}
|
|
4974
|
-
function DialogFooter({
|
|
4975
|
-
|
|
5215
|
+
function DialogFooter({
|
|
5216
|
+
className,
|
|
5217
|
+
...props
|
|
5218
|
+
}) {
|
|
5219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5220
|
+
"div",
|
|
5221
|
+
{
|
|
5222
|
+
className: cn("flex items-center justify-end gap-2", className),
|
|
5223
|
+
...props
|
|
5224
|
+
}
|
|
5225
|
+
);
|
|
4976
5226
|
}
|
|
4977
|
-
var DialogTitle =
|
|
4978
|
-
|
|
5227
|
+
var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5228
|
+
import_radix_ui5.Dialog.Title,
|
|
4979
5229
|
{
|
|
4980
5230
|
ref,
|
|
4981
|
-
className: cn(
|
|
5231
|
+
className: cn(
|
|
5232
|
+
"text-lg font-semibold leading-none tracking-tight text-card-foreground",
|
|
5233
|
+
className
|
|
5234
|
+
),
|
|
4982
5235
|
...props
|
|
4983
5236
|
}
|
|
4984
5237
|
));
|
|
4985
|
-
DialogTitle.displayName =
|
|
4986
|
-
var DialogDescription =
|
|
4987
|
-
|
|
5238
|
+
DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
|
|
5239
|
+
var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5240
|
+
import_radix_ui5.Dialog.Description,
|
|
4988
5241
|
{
|
|
4989
5242
|
ref,
|
|
4990
5243
|
className: cn("text-sm text-muted-foreground", className),
|
|
4991
5244
|
...props
|
|
4992
5245
|
}
|
|
4993
5246
|
));
|
|
4994
|
-
DialogDescription.displayName =
|
|
4995
|
-
var DialogClose =
|
|
5247
|
+
DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
|
|
5248
|
+
var DialogClose = import_radix_ui5.Dialog.Close;
|
|
4996
5249
|
|
|
4997
|
-
// src/ui/
|
|
4998
|
-
var
|
|
4999
|
-
var import_radix_ui5 = require("radix-ui");
|
|
5000
|
-
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
5001
|
-
var buttonVariants = cva(
|
|
5002
|
-
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
|
|
5003
|
-
{
|
|
5004
|
-
variants: {
|
|
5005
|
-
variant: {
|
|
5006
|
-
default: "bg-primary text-primary-foreground hover:opacity-90",
|
|
5007
|
-
outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
|
|
5008
|
-
ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
|
|
5009
|
-
},
|
|
5010
|
-
size: {
|
|
5011
|
-
default: "h-9",
|
|
5012
|
-
sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
|
|
5013
|
-
}
|
|
5014
|
-
},
|
|
5015
|
-
defaultVariants: {
|
|
5016
|
-
variant: "default",
|
|
5017
|
-
size: "default"
|
|
5018
|
-
}
|
|
5019
|
-
}
|
|
5020
|
-
);
|
|
5021
|
-
var Button = React6.forwardRef(
|
|
5022
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
5023
|
-
const Comp = asChild ? import_radix_ui5.Slot.Root : "button";
|
|
5024
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5025
|
-
Comp,
|
|
5026
|
-
{
|
|
5027
|
-
ref,
|
|
5028
|
-
"data-slot": "button",
|
|
5029
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
5030
|
-
...props
|
|
5031
|
-
}
|
|
5032
|
-
);
|
|
5033
|
-
}
|
|
5034
|
-
);
|
|
5035
|
-
Button.displayName = "Button";
|
|
5250
|
+
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
5251
|
+
var import_lucide_react8 = require("lucide-react");
|
|
5036
5252
|
|
|
5037
5253
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
5254
|
+
var import_lucide_react5 = require("lucide-react");
|
|
5038
5255
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
5039
|
-
function DestinationBreadcrumb({
|
|
5256
|
+
function DestinationBreadcrumb({
|
|
5257
|
+
pageTitle,
|
|
5258
|
+
sectionLabel
|
|
5259
|
+
}) {
|
|
5040
5260
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
|
|
5041
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
5261
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
|
|
5042
5262
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
5043
5263
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5044
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5045
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
5264
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
5265
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
|
|
5046
5266
|
] }),
|
|
5047
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5267
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5268
|
+
import_lucide_react5.ArrowRight,
|
|
5269
|
+
{
|
|
5270
|
+
size: 16,
|
|
5271
|
+
className: "shrink-0 text-muted-foreground",
|
|
5272
|
+
"aria-hidden": true
|
|
5273
|
+
}
|
|
5274
|
+
),
|
|
5048
5275
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
5049
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5276
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5277
|
+
import_lucide_react5.GalleryVertical,
|
|
5278
|
+
{
|
|
5279
|
+
size: 16,
|
|
5280
|
+
className: "shrink-0 text-foreground",
|
|
5281
|
+
"aria-hidden": true
|
|
5282
|
+
}
|
|
5283
|
+
),
|
|
5050
5284
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
5051
5285
|
] })
|
|
5052
5286
|
] })
|
|
@@ -5054,8 +5288,13 @@ function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
|
5054
5288
|
}
|
|
5055
5289
|
|
|
5056
5290
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
5291
|
+
var import_lucide_react6 = require("lucide-react");
|
|
5057
5292
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
5058
|
-
function SectionTreeItem({
|
|
5293
|
+
function SectionTreeItem({
|
|
5294
|
+
section,
|
|
5295
|
+
onSelect,
|
|
5296
|
+
selected
|
|
5297
|
+
}) {
|
|
5059
5298
|
const interactive = Boolean(onSelect);
|
|
5060
5299
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
5061
5300
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
@@ -5083,30 +5322,28 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
5083
5322
|
interactive && selected && "border-primary"
|
|
5084
5323
|
),
|
|
5085
5324
|
children: [
|
|
5086
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5325
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5326
|
+
import_lucide_react6.GalleryVertical,
|
|
5327
|
+
{
|
|
5328
|
+
size: 16,
|
|
5329
|
+
className: "shrink-0 text-foreground",
|
|
5330
|
+
"aria-hidden": true
|
|
5331
|
+
}
|
|
5332
|
+
),
|
|
5087
5333
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
5088
5334
|
]
|
|
5089
5335
|
}
|
|
5090
5336
|
)
|
|
5091
5337
|
] });
|
|
5092
5338
|
}
|
|
5093
|
-
function SectionPickerList({ sections, onSelect }) {
|
|
5094
|
-
if (sections.length === 0) {
|
|
5095
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
5096
|
-
}
|
|
5097
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
5098
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
5099
|
-
sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionTreeItem, { section, onSelect }, section.id))
|
|
5100
|
-
] });
|
|
5101
|
-
}
|
|
5102
5339
|
|
|
5103
5340
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
5104
5341
|
var import_react4 = require("react");
|
|
5105
5342
|
|
|
5106
5343
|
// src/ui/input.tsx
|
|
5107
|
-
var
|
|
5344
|
+
var React8 = __toESM(require("react"), 1);
|
|
5108
5345
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
5109
|
-
var Input =
|
|
5346
|
+
var Input = React8.forwardRef(
|
|
5110
5347
|
({ className, type, ...props }, ref) => {
|
|
5111
5348
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5112
5349
|
"input",
|
|
@@ -5140,8 +5377,11 @@ function Label({ className, ...props }) {
|
|
|
5140
5377
|
}
|
|
5141
5378
|
|
|
5142
5379
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
5380
|
+
var import_lucide_react7 = require("lucide-react");
|
|
5143
5381
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
5144
|
-
function FieldChevron({
|
|
5382
|
+
function FieldChevron({
|
|
5383
|
+
onClick
|
|
5384
|
+
}) {
|
|
5145
5385
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5146
5386
|
"button",
|
|
5147
5387
|
{
|
|
@@ -5150,7 +5390,7 @@ function FieldChevron({ onClick }) {
|
|
|
5150
5390
|
onClick,
|
|
5151
5391
|
"aria-label": "Open page list",
|
|
5152
5392
|
tabIndex: -1,
|
|
5153
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5393
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
|
|
5154
5394
|
}
|
|
5155
5395
|
);
|
|
5156
5396
|
}
|
|
@@ -5168,7 +5408,29 @@ function UrlOrPageInput({
|
|
|
5168
5408
|
}) {
|
|
5169
5409
|
const inputId = (0, import_react4.useId)();
|
|
5170
5410
|
const inputRef = (0, import_react4.useRef)(null);
|
|
5411
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
5171
5412
|
const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
|
|
5413
|
+
(0, import_react4.useEffect)(() => {
|
|
5414
|
+
if (!dropdownOpen) return;
|
|
5415
|
+
const isOutside = (e) => {
|
|
5416
|
+
const root = rootRef.current;
|
|
5417
|
+
if (!root) return true;
|
|
5418
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
5419
|
+
if (path.includes(root)) return false;
|
|
5420
|
+
const target = e.target;
|
|
5421
|
+
return !(target instanceof Node && root.contains(target));
|
|
5422
|
+
};
|
|
5423
|
+
const closeIfOutside = (e) => {
|
|
5424
|
+
if (!isOutside(e)) return;
|
|
5425
|
+
onDropdownOpenChange(false);
|
|
5426
|
+
};
|
|
5427
|
+
document.addEventListener("pointerdown", closeIfOutside, true);
|
|
5428
|
+
document.addEventListener("mousedown", closeIfOutside, true);
|
|
5429
|
+
return () => {
|
|
5430
|
+
document.removeEventListener("pointerdown", closeIfOutside, true);
|
|
5431
|
+
document.removeEventListener("mousedown", closeIfOutside, true);
|
|
5432
|
+
};
|
|
5433
|
+
}, [dropdownOpen, onDropdownOpenChange]);
|
|
5172
5434
|
const openDropdown = () => {
|
|
5173
5435
|
if (readOnly || filteredPages.length === 0) return;
|
|
5174
5436
|
onDropdownOpenChange(true);
|
|
@@ -5186,14 +5448,21 @@ function UrlOrPageInput({
|
|
|
5186
5448
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
5187
5449
|
};
|
|
5188
5450
|
const fieldClassName = cn(
|
|
5189
|
-
"data-ohw-link-field flex h-
|
|
5451
|
+
"data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
|
|
5190
5452
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
5191
5453
|
);
|
|
5192
5454
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
5193
5455
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
5194
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative w-full", children: [
|
|
5456
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
|
|
5195
5457
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
5196
|
-
selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5458
|
+
selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5459
|
+
import_lucide_react7.File,
|
|
5460
|
+
{
|
|
5461
|
+
size: 16,
|
|
5462
|
+
className: "shrink-0 text-foreground",
|
|
5463
|
+
"aria-hidden": true
|
|
5464
|
+
}
|
|
5465
|
+
) }) : null,
|
|
5197
5466
|
readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5198
5467
|
Input,
|
|
5199
5468
|
{
|
|
@@ -5205,7 +5474,14 @@ function UrlOrPageInput({
|
|
|
5205
5474
|
setIsFocused(true);
|
|
5206
5475
|
if (!selectedPage) openDropdown();
|
|
5207
5476
|
},
|
|
5208
|
-
onBlur: () =>
|
|
5477
|
+
onBlur: () => {
|
|
5478
|
+
setIsFocused(false);
|
|
5479
|
+
requestAnimationFrame(() => {
|
|
5480
|
+
if (!rootRef.current?.contains(document.activeElement)) {
|
|
5481
|
+
onDropdownOpenChange(false);
|
|
5482
|
+
}
|
|
5483
|
+
});
|
|
5484
|
+
},
|
|
5209
5485
|
placeholder,
|
|
5210
5486
|
className: cn(
|
|
5211
5487
|
"min-w-0 flex-1 truncate border-0 bg-transparent p-0 text-sm font-normal leading-5 shadow-none outline-none ring-0 caret-foreground",
|
|
@@ -5221,7 +5497,7 @@ function UrlOrPageInput({
|
|
|
5221
5497
|
onMouseDown: clearSelection,
|
|
5222
5498
|
"aria-label": "Clear selected page",
|
|
5223
5499
|
tabIndex: -1,
|
|
5224
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5500
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
|
|
5225
5501
|
}
|
|
5226
5502
|
) : null,
|
|
5227
5503
|
!readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
|
|
@@ -5230,7 +5506,7 @@ function UrlOrPageInput({
|
|
|
5230
5506
|
"div",
|
|
5231
5507
|
{
|
|
5232
5508
|
"data-ohw-link-page-dropdown": "",
|
|
5233
|
-
className: "absolute left-0 right-0
|
|
5509
|
+
className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
|
|
5234
5510
|
onMouseDown: (e) => e.preventDefault(),
|
|
5235
5511
|
children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
5236
5512
|
"button",
|
|
@@ -5239,7 +5515,7 @@ function UrlOrPageInput({
|
|
|
5239
5515
|
className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
|
|
5240
5516
|
onClick: () => onPageSelect(page),
|
|
5241
5517
|
children: [
|
|
5242
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5518
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
|
|
5243
5519
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
|
|
5244
5520
|
]
|
|
5245
5521
|
},
|
|
@@ -5252,38 +5528,444 @@ function UrlOrPageInput({
|
|
|
5252
5528
|
] });
|
|
5253
5529
|
}
|
|
5254
5530
|
|
|
5255
|
-
// src/ui/link-modal/
|
|
5256
|
-
var
|
|
5257
|
-
function
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5531
|
+
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
5532
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
5533
|
+
function LinkEditorPanel({ state, onClose }) {
|
|
5534
|
+
const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
|
|
5535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
5536
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5537
|
+
"button",
|
|
5538
|
+
{
|
|
5539
|
+
type: "button",
|
|
5540
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
|
|
5541
|
+
"aria-label": "Close",
|
|
5542
|
+
onClick: onClose,
|
|
5543
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
|
|
5544
|
+
}
|
|
5545
|
+
) }),
|
|
5546
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
|
|
5547
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
5548
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5549
|
+
DestinationBreadcrumb,
|
|
5550
|
+
{
|
|
5551
|
+
pageTitle: state.selectedPage.title,
|
|
5552
|
+
sectionLabel: state.selectedSection.label
|
|
5553
|
+
}
|
|
5554
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5555
|
+
UrlOrPageInput,
|
|
5556
|
+
{
|
|
5557
|
+
value: state.searchValue,
|
|
5558
|
+
selectedPage: state.selectedPage,
|
|
5559
|
+
dropdownOpen: state.dropdownOpen,
|
|
5560
|
+
onDropdownOpenChange: state.setDropdownOpen,
|
|
5561
|
+
filteredPages: state.filteredPages,
|
|
5562
|
+
onInputChange: state.handleInputChange,
|
|
5563
|
+
onPageSelect: state.handlePageSelect,
|
|
5564
|
+
urlError: state.urlError
|
|
5565
|
+
}
|
|
5566
|
+
),
|
|
5567
|
+
state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
5568
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5569
|
+
Button,
|
|
5570
|
+
{
|
|
5571
|
+
type: "button",
|
|
5572
|
+
variant: "outline",
|
|
5573
|
+
className: "w-fit cursor-pointer",
|
|
5574
|
+
size: "sm",
|
|
5575
|
+
onClick: state.handleChooseSection,
|
|
5576
|
+
children: "Choose a section"
|
|
5577
|
+
}
|
|
5578
|
+
),
|
|
5579
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
5580
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
|
|
5581
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
|
|
5582
|
+
] })
|
|
5583
|
+
] }) : null,
|
|
5584
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
5585
|
+
] }),
|
|
5586
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
|
|
5587
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5588
|
+
Button,
|
|
5589
|
+
{
|
|
5590
|
+
type: "button",
|
|
5591
|
+
variant: isCancel ? "outline" : "ghost",
|
|
5592
|
+
className: "leading-6 shadow-none cursor-pointer",
|
|
5593
|
+
style: isCancel ? {
|
|
5594
|
+
backgroundColor: "var(--ohw-background)",
|
|
5595
|
+
borderColor: "var(--ohw-border)",
|
|
5596
|
+
color: "var(--ohw-foreground)"
|
|
5597
|
+
} : void 0,
|
|
5598
|
+
onClick: state.handleSecondary,
|
|
5599
|
+
children: state.secondaryLabel
|
|
5600
|
+
}
|
|
5601
|
+
),
|
|
5602
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5603
|
+
Button,
|
|
5604
|
+
{
|
|
5605
|
+
type: "button",
|
|
5606
|
+
className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
|
|
5607
|
+
style: {
|
|
5608
|
+
backgroundColor: "var(--ohw-primary)",
|
|
5609
|
+
color: "var(--ohw-primary-foreground)"
|
|
5610
|
+
},
|
|
5611
|
+
disabled: !state.isValid,
|
|
5612
|
+
onClick: state.handleSubmit,
|
|
5613
|
+
children: state.submitLabel
|
|
5614
|
+
}
|
|
5615
|
+
)
|
|
5616
|
+
] })
|
|
5617
|
+
] });
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5620
|
+
// src/ui/link-modal/SectionPickerOverlay.tsx
|
|
5621
|
+
var import_react5 = require("react");
|
|
5622
|
+
var import_react_dom = require("react-dom");
|
|
5623
|
+
var import_lucide_react9 = require("lucide-react");
|
|
5624
|
+
var import_navigation = require("next/navigation");
|
|
5625
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
5626
|
+
var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
|
|
5627
|
+
function rectsEqual(a, b) {
|
|
5628
|
+
if (a.size !== b.size) return false;
|
|
5629
|
+
for (const [id, rect] of a) {
|
|
5630
|
+
const other = b.get(id);
|
|
5631
|
+
if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
|
|
5632
|
+
return false;
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
return true;
|
|
5636
|
+
}
|
|
5637
|
+
function readSectionRects(sectionIds) {
|
|
5638
|
+
const next = /* @__PURE__ */ new Map();
|
|
5639
|
+
for (const id of sectionIds) {
|
|
5640
|
+
const el = document.querySelector(
|
|
5641
|
+
`[data-ohw-section="${CSS.escape(id)}"]`
|
|
5642
|
+
);
|
|
5643
|
+
if (el) next.set(id, el.getBoundingClientRect());
|
|
5644
|
+
}
|
|
5645
|
+
return next;
|
|
5646
|
+
}
|
|
5647
|
+
function hitTestSectionId(x, y, sectionIds, rects) {
|
|
5648
|
+
for (const id of sectionIds) {
|
|
5649
|
+
const rect = rects.get(id);
|
|
5650
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) continue;
|
|
5651
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
5652
|
+
return id;
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5655
|
+
return null;
|
|
5656
|
+
}
|
|
5657
|
+
function useSectionRects(sectionIdsKey, enabled) {
|
|
5658
|
+
const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
|
|
5659
|
+
const sectionIds = (0, import_react5.useMemo)(
|
|
5660
|
+
() => sectionIdsKey ? sectionIdsKey.split("\0") : [],
|
|
5661
|
+
[sectionIdsKey]
|
|
5662
|
+
);
|
|
5663
|
+
(0, import_react5.useEffect)(() => {
|
|
5664
|
+
if (!enabled || sectionIds.length === 0) {
|
|
5665
|
+
setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
|
|
5666
|
+
return;
|
|
5667
|
+
}
|
|
5668
|
+
const update = () => {
|
|
5669
|
+
const next = readSectionRects(sectionIds);
|
|
5670
|
+
setRects((prev) => rectsEqual(prev, next) ? prev : next);
|
|
5671
|
+
};
|
|
5672
|
+
update();
|
|
5673
|
+
const opts = { capture: true, passive: true };
|
|
5674
|
+
window.addEventListener("scroll", update, opts);
|
|
5675
|
+
window.addEventListener("resize", update);
|
|
5676
|
+
const vv = window.visualViewport;
|
|
5677
|
+
vv?.addEventListener("resize", update);
|
|
5678
|
+
vv?.addEventListener("scroll", update);
|
|
5679
|
+
const ro = new ResizeObserver(update);
|
|
5680
|
+
for (const id of sectionIds) {
|
|
5681
|
+
const el = document.querySelector(
|
|
5682
|
+
`[data-ohw-section="${CSS.escape(id)}"]`
|
|
5683
|
+
);
|
|
5684
|
+
if (el) ro.observe(el);
|
|
5685
|
+
}
|
|
5686
|
+
return () => {
|
|
5687
|
+
window.removeEventListener("scroll", update, opts);
|
|
5688
|
+
window.removeEventListener("resize", update);
|
|
5689
|
+
vv?.removeEventListener("resize", update);
|
|
5690
|
+
vv?.removeEventListener("scroll", update);
|
|
5691
|
+
ro.disconnect();
|
|
5692
|
+
};
|
|
5693
|
+
}, [sectionIds, enabled]);
|
|
5694
|
+
return rects;
|
|
5695
|
+
}
|
|
5696
|
+
function SectionPickerOverlay({
|
|
5697
|
+
pagePath,
|
|
5698
|
+
sections,
|
|
5699
|
+
onBack,
|
|
5700
|
+
onSelect
|
|
5701
|
+
}) {
|
|
5702
|
+
const router = (0, import_navigation.useRouter)();
|
|
5703
|
+
const pathname = (0, import_navigation.usePathname)();
|
|
5704
|
+
const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
|
|
5705
|
+
const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
|
|
5706
|
+
const pointerRef = (0, import_react5.useRef)(null);
|
|
5707
|
+
const pointerScreenRef = (0, import_react5.useRef)(null);
|
|
5708
|
+
const iframeOffsetTopRef = (0, import_react5.useRef)(null);
|
|
5709
|
+
const sectionIdsRef = (0, import_react5.useRef)([]);
|
|
5710
|
+
const [chromeClip, setChromeClip] = (0, import_react5.useState)(
|
|
5711
|
+
() => ({
|
|
5712
|
+
top: 0,
|
|
5713
|
+
bottom: typeof window !== "undefined" ? window.innerHeight : 0
|
|
5714
|
+
})
|
|
5715
|
+
);
|
|
5716
|
+
const normalizedTarget = normalizePath(pagePath);
|
|
5717
|
+
const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
|
|
5718
|
+
(0, import_react5.useEffect)(() => {
|
|
5719
|
+
if (isOnTargetPage) return;
|
|
5720
|
+
const search = readPreservedSearch();
|
|
5721
|
+
router.push(`${normalizedTarget}${search}`);
|
|
5722
|
+
}, [isOnTargetPage, normalizedTarget, router]);
|
|
5723
|
+
(0, import_react5.useEffect)(() => {
|
|
5724
|
+
document.documentElement.setAttribute("data-ohw-section-picking", "");
|
|
5725
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
|
|
5726
|
+
el.removeAttribute("data-ohw-hovered");
|
|
5727
|
+
});
|
|
5728
|
+
return () => {
|
|
5729
|
+
document.documentElement.removeAttribute("data-ohw-section-picking");
|
|
5730
|
+
};
|
|
5731
|
+
}, []);
|
|
5732
|
+
const liveSections = (0, import_react5.useMemo)(() => {
|
|
5733
|
+
if (!isOnTargetPage) return sections;
|
|
5734
|
+
const live = collectSectionsFromDom();
|
|
5735
|
+
if (live.length === 0) return sections;
|
|
5736
|
+
const labels = new Map(sections.map((s) => [s.id, s.label]));
|
|
5737
|
+
return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
|
|
5738
|
+
}, [isOnTargetPage, sections, pathname]);
|
|
5739
|
+
const sectionIdsKey = (0, import_react5.useMemo)(
|
|
5740
|
+
() => liveSections.map((s) => s.id).join("\0"),
|
|
5741
|
+
[liveSections]
|
|
5742
|
+
);
|
|
5743
|
+
const sectionIds = (0, import_react5.useMemo)(
|
|
5744
|
+
() => liveSections.map((s) => s.id),
|
|
5745
|
+
[liveSections]
|
|
5746
|
+
);
|
|
5747
|
+
sectionIdsRef.current = sectionIds;
|
|
5748
|
+
const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
|
|
5749
|
+
const applyHoverAt = (0, import_react5.useCallback)((x, y, liveRects) => {
|
|
5750
|
+
const ids = sectionIdsRef.current;
|
|
5751
|
+
const map = liveRects ?? readSectionRects(ids);
|
|
5752
|
+
const next = hitTestSectionId(x, y, ids, map);
|
|
5753
|
+
setHoveredId((prev) => prev === next ? prev : next);
|
|
5754
|
+
}, []);
|
|
5755
|
+
(0, import_react5.useEffect)(() => {
|
|
5756
|
+
const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
|
|
5757
|
+
const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
|
|
5758
|
+
const bottom = Math.min(
|
|
5759
|
+
window.innerHeight,
|
|
5760
|
+
visibleCanvasTop + canvasH - iframeOffsetTop
|
|
5761
|
+
);
|
|
5762
|
+
setChromeClip(
|
|
5763
|
+
(prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
|
|
5764
|
+
);
|
|
5765
|
+
};
|
|
5766
|
+
const onParentScroll = (e) => {
|
|
5767
|
+
if (e.data?.type !== "ow:parent-scroll") return;
|
|
5768
|
+
const { iframeOffsetTop, headerH, canvasH } = e.data;
|
|
5769
|
+
applyClip(iframeOffsetTop, headerH, canvasH);
|
|
5770
|
+
iframeOffsetTopRef.current = iframeOffsetTop;
|
|
5771
|
+
if (!pointerScreenRef.current && pointerRef.current) {
|
|
5772
|
+
pointerScreenRef.current = {
|
|
5773
|
+
x: pointerRef.current.x,
|
|
5774
|
+
y: pointerRef.current.y + iframeOffsetTop
|
|
5775
|
+
};
|
|
5776
|
+
}
|
|
5777
|
+
const screen = pointerScreenRef.current;
|
|
5778
|
+
if (screen) {
|
|
5779
|
+
const next = { x: screen.x, y: screen.y - iframeOffsetTop };
|
|
5780
|
+
pointerRef.current = next;
|
|
5781
|
+
applyHoverAt(next.x, next.y);
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
const onResize = () => {
|
|
5785
|
+
setChromeClip((prev) => {
|
|
5786
|
+
const bottom = Math.max(prev.top, window.innerHeight);
|
|
5787
|
+
return prev.bottom === bottom ? prev : { ...prev, bottom };
|
|
5788
|
+
});
|
|
5789
|
+
};
|
|
5790
|
+
window.addEventListener("message", onParentScroll);
|
|
5791
|
+
window.addEventListener("resize", onResize);
|
|
5792
|
+
return () => {
|
|
5793
|
+
window.removeEventListener("message", onParentScroll);
|
|
5794
|
+
window.removeEventListener("resize", onResize);
|
|
5795
|
+
};
|
|
5796
|
+
}, [applyHoverAt]);
|
|
5797
|
+
(0, import_react5.useEffect)(() => {
|
|
5798
|
+
const ptr = pointerRef.current;
|
|
5799
|
+
if (!ptr) return;
|
|
5800
|
+
applyHoverAt(ptr.x, ptr.y, rects);
|
|
5801
|
+
}, [rects, applyHoverAt]);
|
|
5802
|
+
(0, import_react5.useEffect)(() => {
|
|
5803
|
+
const rememberPointer = (clientX, clientY) => {
|
|
5804
|
+
pointerRef.current = { x: clientX, y: clientY };
|
|
5805
|
+
const top = iframeOffsetTopRef.current;
|
|
5806
|
+
if (top != null) {
|
|
5807
|
+
pointerScreenRef.current = { x: clientX, y: clientY + top };
|
|
5808
|
+
}
|
|
5809
|
+
applyHoverAt(clientX, clientY, rects);
|
|
5810
|
+
};
|
|
5811
|
+
const onPointerMove = (e) => {
|
|
5812
|
+
rememberPointer(e.clientX, e.clientY);
|
|
5813
|
+
};
|
|
5814
|
+
const onPointerSync = (e) => {
|
|
5815
|
+
if (e.data?.type !== "ow:pointer-sync") return;
|
|
5816
|
+
const { clientX, clientY } = e.data;
|
|
5817
|
+
rememberPointer(clientX, clientY);
|
|
5818
|
+
};
|
|
5819
|
+
window.addEventListener("pointermove", onPointerMove, { passive: true });
|
|
5820
|
+
window.addEventListener("message", onPointerSync);
|
|
5821
|
+
return () => {
|
|
5822
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
5823
|
+
window.removeEventListener("message", onPointerSync);
|
|
5824
|
+
};
|
|
5825
|
+
}, [rects, applyHoverAt]);
|
|
5826
|
+
const handleSelect = (0, import_react5.useCallback)(
|
|
5827
|
+
(section) => {
|
|
5828
|
+
if (selectedId) return;
|
|
5829
|
+
setSelectedId(section.id);
|
|
5830
|
+
onSelect(section);
|
|
5831
|
+
},
|
|
5832
|
+
[selectedId, onSelect]
|
|
5833
|
+
);
|
|
5834
|
+
(0, import_react5.useEffect)(() => {
|
|
5835
|
+
const onKeyDown = (e) => {
|
|
5836
|
+
if (e.key === "Escape") {
|
|
5837
|
+
e.preventDefault();
|
|
5838
|
+
e.stopPropagation();
|
|
5839
|
+
onBack();
|
|
5840
|
+
}
|
|
5841
|
+
};
|
|
5842
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
5843
|
+
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
5844
|
+
}, [onBack]);
|
|
5845
|
+
const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
|
|
5846
|
+
if (!portalRoot) return null;
|
|
5847
|
+
return (0, import_react_dom.createPortal)(
|
|
5848
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
5849
|
+
"div",
|
|
5850
|
+
{
|
|
5851
|
+
"data-ohw-section-picker": "",
|
|
5852
|
+
"data-ohw-bridge": "",
|
|
5853
|
+
className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
|
|
5854
|
+
"aria-modal": true,
|
|
5855
|
+
role: "dialog",
|
|
5856
|
+
"aria-label": "Choose a section",
|
|
5857
|
+
children: [
|
|
5858
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5859
|
+
"div",
|
|
5860
|
+
{
|
|
5861
|
+
className: "pointer-events-auto fixed left-5 z-[2]",
|
|
5862
|
+
style: { top: chromeClip.top + 20 },
|
|
5863
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
5864
|
+
Button,
|
|
5865
|
+
{
|
|
5866
|
+
type: "button",
|
|
5867
|
+
variant: "outline",
|
|
5868
|
+
size: "sm",
|
|
5869
|
+
className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
|
|
5870
|
+
onClick: onBack,
|
|
5871
|
+
children: [
|
|
5872
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
|
|
5873
|
+
"Back"
|
|
5874
|
+
]
|
|
5875
|
+
}
|
|
5876
|
+
)
|
|
5877
|
+
}
|
|
5878
|
+
),
|
|
5879
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5880
|
+
"div",
|
|
5881
|
+
{
|
|
5882
|
+
className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
|
|
5883
|
+
style: {
|
|
5884
|
+
top: chromeClip.bottom - 20,
|
|
5885
|
+
transform: "translate(-50%, -100%)",
|
|
5886
|
+
backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
|
|
5887
|
+
},
|
|
5888
|
+
"data-ohw-section-picker-hint": "",
|
|
5889
|
+
children: "Click on section to select"
|
|
5890
|
+
}
|
|
5891
|
+
),
|
|
5892
|
+
!isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5893
|
+
"div",
|
|
5894
|
+
{
|
|
5895
|
+
className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md bg-background/90 px-3 py-2 text-sm text-muted-foreground shadow-sm",
|
|
5896
|
+
style: { top: chromeClip.top + 64 },
|
|
5897
|
+
children: "Loading page preview\u2026"
|
|
5898
|
+
}
|
|
5899
|
+
) : null,
|
|
5900
|
+
isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
|
|
5901
|
+
isOnTargetPage ? liveSections.map((section) => {
|
|
5902
|
+
const rect = rects.get(section.id);
|
|
5903
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
5904
|
+
const isSelected = selectedId === section.id;
|
|
5905
|
+
const isLit = isSelected || hoveredId === section.id;
|
|
5906
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5907
|
+
"button",
|
|
5908
|
+
{
|
|
5909
|
+
type: "button",
|
|
5910
|
+
className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
|
|
5911
|
+
style: {
|
|
5912
|
+
top: rect.top,
|
|
5913
|
+
left: rect.left,
|
|
5914
|
+
width: rect.width,
|
|
5915
|
+
height: rect.height,
|
|
5916
|
+
backgroundColor: isLit ? "transparent" : DIM_OVERLAY
|
|
5917
|
+
},
|
|
5918
|
+
"aria-label": `Select section ${section.label}`,
|
|
5919
|
+
onClick: () => handleSelect(section),
|
|
5920
|
+
children: isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5921
|
+
"span",
|
|
5922
|
+
{
|
|
5923
|
+
className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
|
|
5924
|
+
style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
|
|
5925
|
+
"aria-hidden": true,
|
|
5926
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
|
|
5927
|
+
}
|
|
5928
|
+
) : null
|
|
5929
|
+
},
|
|
5930
|
+
section.id
|
|
5931
|
+
);
|
|
5932
|
+
}) : null
|
|
5933
|
+
]
|
|
5934
|
+
}
|
|
5935
|
+
),
|
|
5936
|
+
portalRoot
|
|
5937
|
+
);
|
|
5938
|
+
}
|
|
5939
|
+
|
|
5940
|
+
// src/ui/link-modal/useLinkModalState.ts
|
|
5941
|
+
var import_react6 = require("react");
|
|
5942
|
+
function useLinkModalState({
|
|
5943
|
+
open,
|
|
5944
|
+
mode,
|
|
5945
|
+
pages,
|
|
5946
|
+
sections: _sections,
|
|
5947
|
+
sectionsByPath,
|
|
5948
|
+
initialTarget,
|
|
5949
|
+
existingTargets: _existingTargets,
|
|
5950
|
+
onClose,
|
|
5951
|
+
onSubmit
|
|
5952
|
+
}) {
|
|
5953
|
+
const availablePages = (0, import_react6.useMemo)(() => pages, [pages]);
|
|
5954
|
+
const [searchValue, setSearchValue] = (0, import_react6.useState)("");
|
|
5955
|
+
const [selectedPage, setSelectedPage] = (0, import_react6.useState)(null);
|
|
5956
|
+
const [selectedSection, setSelectedSection] = (0, import_react6.useState)(null);
|
|
5957
|
+
const [step, setStep] = (0, import_react6.useState)("input");
|
|
5958
|
+
const [dropdownOpen, setDropdownOpen] = (0, import_react6.useState)(false);
|
|
5959
|
+
const [urlError, setUrlError] = (0, import_react6.useState)("");
|
|
5960
|
+
const reset = (0, import_react6.useCallback)(() => {
|
|
5961
|
+
setSearchValue("");
|
|
5962
|
+
setSelectedPage(null);
|
|
5281
5963
|
setSelectedSection(null);
|
|
5282
5964
|
setStep("input");
|
|
5283
5965
|
setDropdownOpen(false);
|
|
5284
5966
|
setUrlError("");
|
|
5285
5967
|
}, []);
|
|
5286
|
-
(0,
|
|
5968
|
+
(0, import_react6.useEffect)(() => {
|
|
5287
5969
|
if (!open) return;
|
|
5288
5970
|
if (mode === "edit" && initialTarget) {
|
|
5289
5971
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -5298,12 +5980,12 @@ function useLinkModalState({
|
|
|
5298
5980
|
}
|
|
5299
5981
|
setDropdownOpen(false);
|
|
5300
5982
|
setUrlError("");
|
|
5301
|
-
}, [open, mode, initialTarget,
|
|
5302
|
-
const filteredPages = (0,
|
|
5983
|
+
}, [open, mode, initialTarget, reset]);
|
|
5984
|
+
const filteredPages = (0, import_react6.useMemo)(() => {
|
|
5303
5985
|
if (!searchValue.trim()) return availablePages;
|
|
5304
5986
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
5305
5987
|
}, [availablePages, searchValue]);
|
|
5306
|
-
const activeSections = (0,
|
|
5988
|
+
const activeSections = (0, import_react6.useMemo)(() => {
|
|
5307
5989
|
if (!selectedPage) return [];
|
|
5308
5990
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
5309
5991
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -5345,11 +6027,14 @@ function useLinkModalState({
|
|
|
5345
6027
|
const handleBackToSections = () => {
|
|
5346
6028
|
setStep("sectionPicker");
|
|
5347
6029
|
};
|
|
6030
|
+
const handleSectionPickerBack = () => {
|
|
6031
|
+
setStep("input");
|
|
6032
|
+
};
|
|
5348
6033
|
const handleClose = () => {
|
|
5349
6034
|
reset();
|
|
5350
6035
|
onClose();
|
|
5351
6036
|
};
|
|
5352
|
-
const isValid = (0,
|
|
6037
|
+
const isValid = (0, import_react6.useMemo)(() => {
|
|
5353
6038
|
if (urlError) return false;
|
|
5354
6039
|
if (showBreadcrumb) return true;
|
|
5355
6040
|
if (selectedPage) return true;
|
|
@@ -5382,7 +6067,7 @@ function useLinkModalState({
|
|
|
5382
6067
|
onSubmit(normalizeUrl(searchValue));
|
|
5383
6068
|
handleClose();
|
|
5384
6069
|
};
|
|
5385
|
-
const submitLabel = mode === "edit" ? "Save" : "Create";
|
|
6070
|
+
const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
|
|
5386
6071
|
const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
|
|
5387
6072
|
const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
|
|
5388
6073
|
const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
|
|
@@ -5409,23 +6094,29 @@ function useLinkModalState({
|
|
|
5409
6094
|
handleChooseSection,
|
|
5410
6095
|
handleSectionSelect,
|
|
5411
6096
|
handleBackToSections,
|
|
6097
|
+
handleSectionPickerBack,
|
|
5412
6098
|
handleClose,
|
|
5413
6099
|
handleSecondary,
|
|
5414
6100
|
handleSubmit
|
|
5415
6101
|
};
|
|
5416
6102
|
}
|
|
5417
6103
|
|
|
5418
|
-
// src/ui/link-modal/
|
|
5419
|
-
var
|
|
5420
|
-
function
|
|
6104
|
+
// src/ui/link-modal/LinkPopover.tsx
|
|
6105
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
6106
|
+
function postToParent(data) {
|
|
6107
|
+
window.parent?.postMessage(data, "*");
|
|
6108
|
+
}
|
|
6109
|
+
function LinkPopover({
|
|
5421
6110
|
open = true,
|
|
6111
|
+
panelRef,
|
|
6112
|
+
portalContainer,
|
|
6113
|
+
onClose,
|
|
5422
6114
|
mode = "create",
|
|
5423
6115
|
pages,
|
|
5424
6116
|
sections = [],
|
|
5425
6117
|
sectionsByPath,
|
|
5426
6118
|
initialTarget,
|
|
5427
6119
|
existingTargets = [],
|
|
5428
|
-
onClose,
|
|
5429
6120
|
onSubmit
|
|
5430
6121
|
}) {
|
|
5431
6122
|
const state = useLinkModalState({
|
|
@@ -5439,115 +6130,109 @@ function LinkEditorPanel({
|
|
|
5439
6130
|
onClose,
|
|
5440
6131
|
onSubmit
|
|
5441
6132
|
});
|
|
5442
|
-
const
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
6133
|
+
const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
|
|
6134
|
+
(0, import_react7.useEffect)(() => {
|
|
6135
|
+
if (!open) return;
|
|
6136
|
+
if (sectionPickerActive) {
|
|
6137
|
+
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6138
|
+
postToParent({ type: "ow:section-picker", active: true });
|
|
6139
|
+
document.documentElement.style.overflow = "";
|
|
6140
|
+
document.body.style.overflow = "";
|
|
6141
|
+
return () => {
|
|
6142
|
+
postToParent({ type: "ow:section-picker", active: false });
|
|
6143
|
+
};
|
|
6144
|
+
}
|
|
6145
|
+
postToParent({ type: "ow:section-picker", active: false });
|
|
6146
|
+
postToParent({ type: "ow:link-modal-lock", locked: true });
|
|
6147
|
+
const html = document.documentElement;
|
|
6148
|
+
const body = document.body;
|
|
6149
|
+
const prevHtmlOverflow = html.style.overflow;
|
|
6150
|
+
const prevBodyOverflow = body.style.overflow;
|
|
6151
|
+
const prevHtmlOverscroll = html.style.overscrollBehavior;
|
|
6152
|
+
const prevBodyOverscroll = body.style.overscrollBehavior;
|
|
6153
|
+
html.style.overflow = "hidden";
|
|
6154
|
+
body.style.overflow = "hidden";
|
|
6155
|
+
html.style.overscrollBehavior = "none";
|
|
6156
|
+
body.style.overscrollBehavior = "none";
|
|
6157
|
+
const canScrollElement = (el, deltaY) => {
|
|
6158
|
+
const { overflowY } = getComputedStyle(el);
|
|
6159
|
+
if (overflowY !== "auto" && overflowY !== "scroll") return false;
|
|
6160
|
+
if (el.scrollHeight <= el.clientHeight + 1) return false;
|
|
6161
|
+
if (deltaY < 0) return el.scrollTop > 0;
|
|
6162
|
+
if (deltaY > 0)
|
|
6163
|
+
return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
|
|
6164
|
+
return true;
|
|
6165
|
+
};
|
|
6166
|
+
const allowsInternalScroll = (e) => {
|
|
6167
|
+
const target = e.target;
|
|
6168
|
+
if (!(target instanceof Element)) return false;
|
|
6169
|
+
const scrollRoot = target.closest(
|
|
6170
|
+
"[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
|
|
6171
|
+
);
|
|
6172
|
+
if (!scrollRoot) return false;
|
|
6173
|
+
const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
|
|
6174
|
+
return canScrollElement(scrollRoot, deltaY);
|
|
6175
|
+
};
|
|
6176
|
+
const preventBackgroundScroll = (e) => {
|
|
6177
|
+
if (allowsInternalScroll(e)) return;
|
|
6178
|
+
e.preventDefault();
|
|
6179
|
+
};
|
|
6180
|
+
const scrollOpts = { passive: false, capture: true };
|
|
6181
|
+
document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6182
|
+
document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6183
|
+
return () => {
|
|
6184
|
+
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6185
|
+
html.style.overflow = prevHtmlOverflow;
|
|
6186
|
+
body.style.overflow = prevBodyOverflow;
|
|
6187
|
+
html.style.overscrollBehavior = prevHtmlOverscroll;
|
|
6188
|
+
body.style.overscrollBehavior = prevBodyOverscroll;
|
|
6189
|
+
document.removeEventListener(
|
|
6190
|
+
"wheel",
|
|
6191
|
+
preventBackgroundScroll,
|
|
6192
|
+
scrollOpts
|
|
6193
|
+
);
|
|
6194
|
+
document.removeEventListener(
|
|
6195
|
+
"touchmove",
|
|
6196
|
+
preventBackgroundScroll,
|
|
6197
|
+
scrollOpts
|
|
6198
|
+
);
|
|
6199
|
+
};
|
|
6200
|
+
}, [open, sectionPickerActive]);
|
|
6201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
6202
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
6203
|
+
Dialog2,
|
|
5446
6204
|
{
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
6205
|
+
open: open && !sectionPickerActive,
|
|
6206
|
+
onOpenChange: (next) => {
|
|
6207
|
+
if (!next) onClose?.();
|
|
6208
|
+
},
|
|
6209
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
6210
|
+
DialogContent,
|
|
6211
|
+
{
|
|
6212
|
+
ref: panelRef,
|
|
6213
|
+
container: portalContainer,
|
|
6214
|
+
"data-ohw-link-popover-root": "",
|
|
6215
|
+
"data-ohw-link-modal-root": "",
|
|
6216
|
+
"data-ohw-bridge": "",
|
|
6217
|
+
showCloseButton: false,
|
|
6218
|
+
className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
|
|
6219
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
|
|
6220
|
+
}
|
|
6221
|
+
)
|
|
5451
6222
|
}
|
|
5452
|
-
)
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
UrlOrPageInput,
|
|
5463
|
-
{
|
|
5464
|
-
value: state.searchValue,
|
|
5465
|
-
selectedPage: state.selectedPage,
|
|
5466
|
-
dropdownOpen: state.dropdownOpen,
|
|
5467
|
-
onDropdownOpenChange: state.setDropdownOpen,
|
|
5468
|
-
filteredPages: state.filteredPages,
|
|
5469
|
-
onInputChange: state.handleInputChange,
|
|
5470
|
-
onPageSelect: state.handlePageSelect,
|
|
5471
|
-
urlError: state.urlError
|
|
5472
|
-
}
|
|
5473
|
-
),
|
|
5474
|
-
state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
5475
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
5476
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
5477
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
5478
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
|
|
5479
|
-
] })
|
|
5480
|
-
] }) : null,
|
|
5481
|
-
state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
5482
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
5483
|
-
] }),
|
|
5484
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
|
|
5485
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5486
|
-
Button,
|
|
5487
|
-
{
|
|
5488
|
-
type: "button",
|
|
5489
|
-
variant: isCancel ? "outline" : "ghost",
|
|
5490
|
-
className: "leading-6 shadow-none cursor-pointer",
|
|
5491
|
-
style: isCancel ? {
|
|
5492
|
-
backgroundColor: "var(--ohw-background)",
|
|
5493
|
-
borderColor: "var(--ohw-border)",
|
|
5494
|
-
color: "var(--ohw-foreground)"
|
|
5495
|
-
} : void 0,
|
|
5496
|
-
onClick: state.handleSecondary,
|
|
5497
|
-
children: state.secondaryLabel
|
|
5498
|
-
}
|
|
5499
|
-
),
|
|
5500
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5501
|
-
Button,
|
|
5502
|
-
{
|
|
5503
|
-
type: "button",
|
|
5504
|
-
className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
|
|
5505
|
-
style: {
|
|
5506
|
-
backgroundColor: "var(--ohw-primary)",
|
|
5507
|
-
color: "var(--ohw-primary-foreground)"
|
|
5508
|
-
},
|
|
5509
|
-
disabled: !state.isValid,
|
|
5510
|
-
onClick: state.handleSubmit,
|
|
5511
|
-
children: state.submitLabel
|
|
5512
|
-
}
|
|
5513
|
-
)
|
|
5514
|
-
] })
|
|
6223
|
+
),
|
|
6224
|
+
sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
6225
|
+
SectionPickerOverlay,
|
|
6226
|
+
{
|
|
6227
|
+
pagePath: state.selectedPage.path,
|
|
6228
|
+
sections: state.activeSections,
|
|
6229
|
+
onBack: state.handleSectionPickerBack,
|
|
6230
|
+
onSelect: state.handleSectionSelect
|
|
6231
|
+
}
|
|
6232
|
+
) : null
|
|
5515
6233
|
] });
|
|
5516
6234
|
}
|
|
5517
6235
|
|
|
5518
|
-
// src/ui/link-modal/LinkPopover.tsx
|
|
5519
|
-
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
5520
|
-
function LinkPopover({
|
|
5521
|
-
open = true,
|
|
5522
|
-
panelRef,
|
|
5523
|
-
portalContainer,
|
|
5524
|
-
onClose,
|
|
5525
|
-
...editorProps
|
|
5526
|
-
}) {
|
|
5527
|
-
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5528
|
-
Dialog2,
|
|
5529
|
-
{
|
|
5530
|
-
open,
|
|
5531
|
-
onOpenChange: (next) => {
|
|
5532
|
-
if (!next) onClose?.();
|
|
5533
|
-
},
|
|
5534
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
5535
|
-
DialogContent,
|
|
5536
|
-
{
|
|
5537
|
-
ref: panelRef,
|
|
5538
|
-
container: portalContainer,
|
|
5539
|
-
"data-ohw-link-popover-root": "",
|
|
5540
|
-
"data-ohw-link-modal-root": "",
|
|
5541
|
-
"data-ohw-bridge": "",
|
|
5542
|
-
showCloseButton: false,
|
|
5543
|
-
className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
|
|
5544
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
|
|
5545
|
-
}
|
|
5546
|
-
)
|
|
5547
|
-
}
|
|
5548
|
-
);
|
|
5549
|
-
}
|
|
5550
|
-
|
|
5551
6236
|
// src/ui/link-modal/devFixtures.ts
|
|
5552
6237
|
var DEV_SITE_PAGES = [
|
|
5553
6238
|
{ path: "/", title: "Home" },
|
|
@@ -5567,16 +6252,21 @@ var DEV_SECTIONS_BY_PATH = {
|
|
|
5567
6252
|
{ id: "founder-teaser", label: "Founder" },
|
|
5568
6253
|
{ id: "plan-form", label: "Plan Form" },
|
|
5569
6254
|
{ id: "testimonials", label: "Testimonials" },
|
|
5570
|
-
{ id: "wordmark", label: "Wordmark" }
|
|
6255
|
+
{ id: "wordmark", label: "Wordmark" },
|
|
6256
|
+
{ id: "footer", label: "Footer" }
|
|
5571
6257
|
],
|
|
5572
6258
|
"/about": [
|
|
5573
6259
|
{ id: "manifesto", label: "Manifesto" },
|
|
5574
6260
|
{ id: "story-letter", label: "Our Story" },
|
|
5575
6261
|
{ id: "lagree-explainer", label: "Lagree Explainer" },
|
|
5576
6262
|
{ id: "waitlist-cta", label: "Waitlist" },
|
|
5577
|
-
{ id: "personal-training", label: "Personal training" }
|
|
6263
|
+
{ id: "personal-training", label: "Personal training" },
|
|
6264
|
+
{ id: "footer", label: "Footer" }
|
|
6265
|
+
],
|
|
6266
|
+
"/classes": [
|
|
6267
|
+
{ id: "class-library", label: "Class Library" },
|
|
6268
|
+
{ id: "footer", label: "Footer" }
|
|
5578
6269
|
],
|
|
5579
|
-
"/classes": [{ id: "class-library", label: "Class Library" }],
|
|
5580
6270
|
"/pricing": [
|
|
5581
6271
|
{ id: "page-header", label: "Page Header" },
|
|
5582
6272
|
{ id: "free-class-cta", label: "Free Class" },
|
|
@@ -5584,19 +6274,25 @@ var DEV_SECTIONS_BY_PATH = {
|
|
|
5584
6274
|
{ id: "monthly-memberships", label: "Memberships" },
|
|
5585
6275
|
{ id: "specialty-programs", label: "Specialty Programs" },
|
|
5586
6276
|
{ id: "package-matcher", label: "Packages" },
|
|
5587
|
-
{ id: "wordmark", label: "Wordmark" }
|
|
6277
|
+
{ id: "wordmark", label: "Wordmark" },
|
|
6278
|
+
{ id: "footer", label: "Footer" }
|
|
5588
6279
|
],
|
|
5589
6280
|
"/studio": [
|
|
5590
6281
|
{ id: "studio-intro", label: "Studio Intro" },
|
|
5591
6282
|
{ id: "studio-features", label: "Studio Features" },
|
|
5592
6283
|
{ id: "studio-gallery", label: "Studio Gallery" },
|
|
5593
|
-
{ id: "studio-visit", label: "Visit Studio" }
|
|
6284
|
+
{ id: "studio-visit", label: "Visit Studio" },
|
|
6285
|
+
{ id: "footer", label: "Footer" }
|
|
6286
|
+
],
|
|
6287
|
+
"/instructors": [
|
|
6288
|
+
{ id: "instructor-grid", label: "Instructors" },
|
|
6289
|
+
{ id: "footer", label: "Footer" }
|
|
5594
6290
|
],
|
|
5595
|
-
"/instructors": [{ id: "instructor-grid", label: "Instructors" }],
|
|
5596
6291
|
"/contact": [
|
|
5597
6292
|
{ id: "hello-hero", label: "Hello" },
|
|
5598
6293
|
{ id: "contact-form", label: "Contact Form" },
|
|
5599
|
-
{ id: "faq", label: "FAQ" }
|
|
6294
|
+
{ id: "faq", label: "FAQ" },
|
|
6295
|
+
{ id: "footer", label: "Footer" }
|
|
5600
6296
|
]
|
|
5601
6297
|
};
|
|
5602
6298
|
function shouldUseDevFixtures() {
|
|
@@ -5606,8 +6302,277 @@ function shouldUseDevFixtures() {
|
|
|
5606
6302
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
5607
6303
|
}
|
|
5608
6304
|
|
|
6305
|
+
// src/lib/nav-items.ts
|
|
6306
|
+
var NAV_COUNT_KEY = "__ohw_nav_count";
|
|
6307
|
+
var NAV_ORDER_KEY = "__ohw_nav_order";
|
|
6308
|
+
function getLinkHref(el) {
|
|
6309
|
+
const key = el.getAttribute("data-ohw-href-key");
|
|
6310
|
+
if (key) {
|
|
6311
|
+
const stored = getStoredLinkHref(key);
|
|
6312
|
+
if (stored !== void 0) return stored;
|
|
6313
|
+
}
|
|
6314
|
+
return el.getAttribute("href") ?? "";
|
|
6315
|
+
}
|
|
6316
|
+
function isNavbarButton(el) {
|
|
6317
|
+
return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
|
|
6318
|
+
}
|
|
6319
|
+
function isNavbarLinkItem(el) {
|
|
6320
|
+
if (!el.matches("[data-ohw-href-key]")) return false;
|
|
6321
|
+
if (isNavbarButton(el)) return false;
|
|
6322
|
+
if (!el.querySelector('[data-ohw-editable="text"]')) return false;
|
|
6323
|
+
return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
|
|
6324
|
+
}
|
|
6325
|
+
function getNavbarDesktopContainer() {
|
|
6326
|
+
return document.querySelector("[data-ohw-nav-container]");
|
|
6327
|
+
}
|
|
6328
|
+
function getNavbarDrawerContainer() {
|
|
6329
|
+
return document.querySelector("[data-ohw-nav-drawer]");
|
|
6330
|
+
}
|
|
6331
|
+
function listNavbarItems() {
|
|
6332
|
+
const desktop = getNavbarDesktopContainer();
|
|
6333
|
+
if (desktop) {
|
|
6334
|
+
return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
|
|
6335
|
+
}
|
|
6336
|
+
const drawer = getNavbarDrawerContainer();
|
|
6337
|
+
if (drawer) {
|
|
6338
|
+
return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
|
|
6339
|
+
}
|
|
6340
|
+
return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
|
|
6341
|
+
}
|
|
6342
|
+
function parseNavIndexFromKey(key) {
|
|
6343
|
+
if (!key) return null;
|
|
6344
|
+
const match = key.match(/^nav-(\d+)-href$/);
|
|
6345
|
+
if (!match) return null;
|
|
6346
|
+
return parseInt(match[1], 10);
|
|
6347
|
+
}
|
|
6348
|
+
function getNavbarIndicesInDom() {
|
|
6349
|
+
const indices = /* @__PURE__ */ new Set();
|
|
6350
|
+
for (const item of listNavbarItems()) {
|
|
6351
|
+
const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
|
|
6352
|
+
if (idx !== null) indices.add(idx);
|
|
6353
|
+
}
|
|
6354
|
+
return [...indices].sort((a, b) => a - b);
|
|
6355
|
+
}
|
|
6356
|
+
function getNextNavbarIndex() {
|
|
6357
|
+
const indices = getNavbarIndicesInDom();
|
|
6358
|
+
if (indices.length === 0) return 0;
|
|
6359
|
+
return Math.max(...indices) + 1;
|
|
6360
|
+
}
|
|
6361
|
+
function getNavbarExistingTargets() {
|
|
6362
|
+
return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
|
|
6363
|
+
}
|
|
6364
|
+
function getNavOrderFromDom() {
|
|
6365
|
+
return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
|
|
6366
|
+
}
|
|
6367
|
+
function parseNavOrder(content) {
|
|
6368
|
+
const raw = content[NAV_ORDER_KEY];
|
|
6369
|
+
if (!raw) return null;
|
|
6370
|
+
try {
|
|
6371
|
+
const parsed = JSON.parse(raw);
|
|
6372
|
+
if (!Array.isArray(parsed)) return null;
|
|
6373
|
+
return parsed.filter((key) => typeof key === "string" && key.length > 0);
|
|
6374
|
+
} catch {
|
|
6375
|
+
return null;
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
function findCounterpartByHrefKey(hrefKey, root) {
|
|
6379
|
+
return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
|
|
6380
|
+
}
|
|
6381
|
+
function cloneNavLinkShell(template) {
|
|
6382
|
+
const anchor = document.createElement("a");
|
|
6383
|
+
if (template) {
|
|
6384
|
+
anchor.style.cssText = template.style.cssText;
|
|
6385
|
+
if (template.className) anchor.className = template.className;
|
|
6386
|
+
}
|
|
6387
|
+
anchor.style.cursor = "pointer";
|
|
6388
|
+
anchor.style.textDecoration = "none";
|
|
6389
|
+
return anchor;
|
|
6390
|
+
}
|
|
6391
|
+
function buildNavLink(index, href, label, template) {
|
|
6392
|
+
const anchor = cloneNavLinkShell(template);
|
|
6393
|
+
anchor.href = href;
|
|
6394
|
+
anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
|
|
6395
|
+
const span = document.createElement("span");
|
|
6396
|
+
span.setAttribute("data-ohw-editable", "text");
|
|
6397
|
+
span.setAttribute("data-ohw-key", `nav-${index}-label`);
|
|
6398
|
+
span.textContent = label;
|
|
6399
|
+
anchor.appendChild(span);
|
|
6400
|
+
return anchor;
|
|
6401
|
+
}
|
|
6402
|
+
function insertNavLinkInContainer(container, anchor, afterHrefKey) {
|
|
6403
|
+
if (!afterHrefKey) {
|
|
6404
|
+
container.appendChild(anchor);
|
|
6405
|
+
return;
|
|
6406
|
+
}
|
|
6407
|
+
const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
|
|
6408
|
+
if (afterEl?.parentElement === container) {
|
|
6409
|
+
afterEl.insertAdjacentElement("afterend", anchor);
|
|
6410
|
+
return;
|
|
6411
|
+
}
|
|
6412
|
+
container.appendChild(anchor);
|
|
6413
|
+
}
|
|
6414
|
+
function insertNavbarItemDom(index, href, label, afterAnchor) {
|
|
6415
|
+
const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
|
|
6416
|
+
const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
|
|
6417
|
+
const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
|
|
6418
|
+
const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
|
|
6419
|
+
const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
|
|
6420
|
+
let primary = null;
|
|
6421
|
+
const desktopContainer = getNavbarDesktopContainer();
|
|
6422
|
+
if (desktopContainer) {
|
|
6423
|
+
const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
|
|
6424
|
+
insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
|
|
6425
|
+
primary = desktopAnchor;
|
|
6426
|
+
}
|
|
6427
|
+
const drawerContainer = getNavbarDrawerContainer();
|
|
6428
|
+
if (drawerContainer) {
|
|
6429
|
+
const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
|
|
6430
|
+
insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
|
|
6431
|
+
if (!primary) primary = drawerAnchor;
|
|
6432
|
+
}
|
|
6433
|
+
if (!primary) {
|
|
6434
|
+
const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
|
|
6435
|
+
const nav = document.querySelector("nav");
|
|
6436
|
+
nav?.appendChild(fallback);
|
|
6437
|
+
primary = fallback;
|
|
6438
|
+
}
|
|
6439
|
+
return primary;
|
|
6440
|
+
}
|
|
6441
|
+
function applyNavOrderToContainer(container, order) {
|
|
6442
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6443
|
+
for (const hrefKey of order) {
|
|
6444
|
+
if (seen.has(hrefKey)) continue;
|
|
6445
|
+
seen.add(hrefKey);
|
|
6446
|
+
const el = findCounterpartByHrefKey(hrefKey, container);
|
|
6447
|
+
if (el) container.appendChild(el);
|
|
6448
|
+
}
|
|
6449
|
+
for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
|
|
6450
|
+
if (!isNavbarLinkItem(el)) continue;
|
|
6451
|
+
const key = el.getAttribute("data-ohw-href-key");
|
|
6452
|
+
if (!key || seen.has(key)) continue;
|
|
6453
|
+
container.appendChild(el);
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
function applyNavOrder(order) {
|
|
6457
|
+
const desktop = getNavbarDesktopContainer();
|
|
6458
|
+
if (desktop) applyNavOrderToContainer(desktop, order);
|
|
6459
|
+
const drawer = getNavbarDrawerContainer();
|
|
6460
|
+
if (drawer) applyNavOrderToContainer(drawer, order);
|
|
6461
|
+
}
|
|
6462
|
+
function collectNavbarIndicesFromContent(content) {
|
|
6463
|
+
const indices = /* @__PURE__ */ new Set();
|
|
6464
|
+
for (const key of Object.keys(content)) {
|
|
6465
|
+
const idx = parseNavIndexFromKey(key);
|
|
6466
|
+
if (idx !== null) indices.add(idx);
|
|
6467
|
+
}
|
|
6468
|
+
const countRaw = content[NAV_COUNT_KEY];
|
|
6469
|
+
if (countRaw) {
|
|
6470
|
+
const count = parseInt(countRaw, 10);
|
|
6471
|
+
if (Number.isFinite(count) && count > 0) {
|
|
6472
|
+
for (let i = 0; i < count; i++) indices.add(i);
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
return [...indices].sort((a, b) => a - b);
|
|
6476
|
+
}
|
|
6477
|
+
function navbarIndexExistsInDom(index) {
|
|
6478
|
+
return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
|
|
6479
|
+
}
|
|
6480
|
+
function reconcileNavbarItemsFromContent(content) {
|
|
6481
|
+
const indices = collectNavbarIndicesFromContent(content);
|
|
6482
|
+
for (const index of indices) {
|
|
6483
|
+
if (navbarIndexExistsInDom(index)) continue;
|
|
6484
|
+
const href = content[`nav-${index}-href`];
|
|
6485
|
+
const label = content[`nav-${index}-label`];
|
|
6486
|
+
if (!href && !label) continue;
|
|
6487
|
+
insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
|
|
6488
|
+
}
|
|
6489
|
+
const order = parseNavOrder(content);
|
|
6490
|
+
if (order?.length) applyNavOrder(order);
|
|
6491
|
+
}
|
|
6492
|
+
function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
|
|
6493
|
+
const order = getNavOrderFromDom();
|
|
6494
|
+
if (!afterAnchor) {
|
|
6495
|
+
if (!order.includes(newHrefKey)) order.push(newHrefKey);
|
|
6496
|
+
return order;
|
|
6497
|
+
}
|
|
6498
|
+
const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
|
|
6499
|
+
if (!afterKey) {
|
|
6500
|
+
if (!order.includes(newHrefKey)) order.push(newHrefKey);
|
|
6501
|
+
return order;
|
|
6502
|
+
}
|
|
6503
|
+
const withoutNew = order.filter((key) => key !== newHrefKey);
|
|
6504
|
+
const pos = withoutNew.indexOf(afterKey);
|
|
6505
|
+
if (pos >= 0) {
|
|
6506
|
+
withoutNew.splice(pos + 1, 0, newHrefKey);
|
|
6507
|
+
return withoutNew;
|
|
6508
|
+
}
|
|
6509
|
+
withoutNew.push(newHrefKey);
|
|
6510
|
+
return withoutNew;
|
|
6511
|
+
}
|
|
6512
|
+
function insertNavbarItem(href, label, afterAnchor = null) {
|
|
6513
|
+
const index = getNextNavbarIndex();
|
|
6514
|
+
const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
|
|
6515
|
+
if (!anchor) throw new Error("Failed to insert navbar item");
|
|
6516
|
+
const hrefKey = `nav-${index}-href`;
|
|
6517
|
+
const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
|
|
6518
|
+
applyNavOrder(order);
|
|
6519
|
+
return {
|
|
6520
|
+
anchor,
|
|
6521
|
+
index,
|
|
6522
|
+
hrefKey,
|
|
6523
|
+
labelKey: `nav-${index}-label`,
|
|
6524
|
+
href,
|
|
6525
|
+
label,
|
|
6526
|
+
order
|
|
6527
|
+
};
|
|
6528
|
+
}
|
|
6529
|
+
|
|
6530
|
+
// src/ui/navbar-container-chrome.tsx
|
|
6531
|
+
var import_lucide_react10 = require("lucide-react");
|
|
6532
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
6533
|
+
function NavbarContainerChrome({
|
|
6534
|
+
rect,
|
|
6535
|
+
onAdd
|
|
6536
|
+
}) {
|
|
6537
|
+
const chromeGap = 6;
|
|
6538
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
6539
|
+
"div",
|
|
6540
|
+
{
|
|
6541
|
+
"data-ohw-navbar-container-chrome": "",
|
|
6542
|
+
"data-ohw-bridge": "",
|
|
6543
|
+
className: "pointer-events-none fixed z-[2147483647]",
|
|
6544
|
+
style: {
|
|
6545
|
+
top: rect.top - chromeGap,
|
|
6546
|
+
left: rect.left - chromeGap,
|
|
6547
|
+
width: rect.width + chromeGap * 2,
|
|
6548
|
+
height: rect.height + chromeGap * 2
|
|
6549
|
+
},
|
|
6550
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
6551
|
+
"button",
|
|
6552
|
+
{
|
|
6553
|
+
type: "button",
|
|
6554
|
+
"data-ohw-navbar-add-button": "",
|
|
6555
|
+
className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
|
|
6556
|
+
style: { top: "70%" },
|
|
6557
|
+
"aria-label": "Add item",
|
|
6558
|
+
onMouseDown: (e) => {
|
|
6559
|
+
e.preventDefault();
|
|
6560
|
+
e.stopPropagation();
|
|
6561
|
+
},
|
|
6562
|
+
onClick: (e) => {
|
|
6563
|
+
e.preventDefault();
|
|
6564
|
+
e.stopPropagation();
|
|
6565
|
+
onAdd();
|
|
6566
|
+
},
|
|
6567
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
|
|
6568
|
+
}
|
|
6569
|
+
)
|
|
6570
|
+
}
|
|
6571
|
+
);
|
|
6572
|
+
}
|
|
6573
|
+
|
|
5609
6574
|
// src/ui/badge.tsx
|
|
5610
|
-
var
|
|
6575
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
5611
6576
|
var badgeVariants = cva(
|
|
5612
6577
|
"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",
|
|
5613
6578
|
{
|
|
@@ -5625,12 +6590,12 @@ var badgeVariants = cva(
|
|
|
5625
6590
|
}
|
|
5626
6591
|
);
|
|
5627
6592
|
function Badge({ className, variant, ...props }) {
|
|
5628
|
-
return /* @__PURE__ */ (0,
|
|
6593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5629
6594
|
}
|
|
5630
6595
|
|
|
5631
6596
|
// src/OhhwellsBridge.tsx
|
|
5632
|
-
var
|
|
5633
|
-
var
|
|
6597
|
+
var import_lucide_react11 = require("lucide-react");
|
|
6598
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
5634
6599
|
var PRIMARY2 = "#0885FE";
|
|
5635
6600
|
var IMAGE_FADE_MS = 300;
|
|
5636
6601
|
function runOpacityFade(el, onDone) {
|
|
@@ -5807,9 +6772,9 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
5807
6772
|
tail.insertAdjacentElement("afterend", container);
|
|
5808
6773
|
}
|
|
5809
6774
|
const root = (0, import_client.createRoot)(container);
|
|
5810
|
-
(0,
|
|
6775
|
+
(0, import_react_dom2.flushSync)(() => {
|
|
5811
6776
|
root.render(
|
|
5812
|
-
/* @__PURE__ */ (0,
|
|
6777
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5813
6778
|
SchedulingWidget,
|
|
5814
6779
|
{
|
|
5815
6780
|
notifyOnConnect,
|
|
@@ -5849,7 +6814,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
5849
6814
|
}
|
|
5850
6815
|
}
|
|
5851
6816
|
}
|
|
5852
|
-
function
|
|
6817
|
+
function getLinkHref2(el) {
|
|
5853
6818
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5854
6819
|
return anchor?.getAttribute("href") ?? "";
|
|
5855
6820
|
}
|
|
@@ -5882,8 +6847,11 @@ function collectEditableNodes() {
|
|
|
5882
6847
|
const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
|
|
5883
6848
|
return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
|
|
5884
6849
|
}
|
|
6850
|
+
if (el.dataset.ohwEditable === "video") {
|
|
6851
|
+
return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
|
|
6852
|
+
}
|
|
5885
6853
|
if (el.dataset.ohwEditable === "link") {
|
|
5886
|
-
return { key: el.dataset.ohwKey ?? "", type: "link", text:
|
|
6854
|
+
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
|
|
5887
6855
|
}
|
|
5888
6856
|
return {
|
|
5889
6857
|
key: el.dataset.ohwKey ?? "",
|
|
@@ -5894,9 +6862,79 @@ function collectEditableNodes() {
|
|
|
5894
6862
|
document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
|
|
5895
6863
|
const key = el.getAttribute("data-ohw-href-key") ?? "";
|
|
5896
6864
|
if (!key) return;
|
|
5897
|
-
nodes.push({ key, type: "link", text:
|
|
6865
|
+
nodes.push({ key, type: "link", text: getLinkHref2(el) });
|
|
6866
|
+
});
|
|
6867
|
+
document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
|
|
6868
|
+
const key = el.dataset.ohwKey ?? "";
|
|
6869
|
+
const video = getVideoEl(el);
|
|
6870
|
+
if (!key || !video) return;
|
|
6871
|
+
nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
|
|
6872
|
+
nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
|
|
6873
|
+
});
|
|
6874
|
+
return nodes;
|
|
6875
|
+
}
|
|
6876
|
+
function isMediaEditable(el) {
|
|
6877
|
+
const t = el.dataset.ohwEditable;
|
|
6878
|
+
return t === "image" || t === "bg-image" || t === "video";
|
|
6879
|
+
}
|
|
6880
|
+
var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
|
|
6881
|
+
var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
|
|
6882
|
+
function getVideoEl(el) {
|
|
6883
|
+
return el instanceof HTMLVideoElement ? el : el.querySelector("video");
|
|
6884
|
+
}
|
|
6885
|
+
var VIDEO_AUTOPLAY_SUFFIX = "__ohw_autoplay";
|
|
6886
|
+
var VIDEO_MUTED_SUFFIX = "__ohw_muted";
|
|
6887
|
+
function isBackgroundVideo(video) {
|
|
6888
|
+
return video.autoplay && video.muted;
|
|
6889
|
+
}
|
|
6890
|
+
function syncVideoPlayback(video) {
|
|
6891
|
+
const background = isBackgroundVideo(video);
|
|
6892
|
+
video.controls = !background;
|
|
6893
|
+
if (video.autoplay) void video.play().catch(() => {
|
|
6894
|
+
});
|
|
6895
|
+
else video.pause();
|
|
6896
|
+
}
|
|
6897
|
+
function lockVideoBox(video) {
|
|
6898
|
+
const { width, height } = video.getBoundingClientRect();
|
|
6899
|
+
if (width > 0 && height > 0 && !video.style.aspectRatio) {
|
|
6900
|
+
video.style.aspectRatio = `${width} / ${height}`;
|
|
6901
|
+
}
|
|
6902
|
+
if (getComputedStyle(video).objectFit === "fill") {
|
|
6903
|
+
video.style.objectFit = "contain";
|
|
6904
|
+
}
|
|
6905
|
+
const fit = video.style.objectFit || getComputedStyle(video).objectFit;
|
|
6906
|
+
const bg = getComputedStyle(video).backgroundColor;
|
|
6907
|
+
const isTransparent = bg === "rgba(0, 0, 0, 0)" || bg === "transparent" || !bg;
|
|
6908
|
+
if (fit === "contain" && isTransparent) {
|
|
6909
|
+
video.style.backgroundColor = "var(--color-dark, #000)";
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
function applyVideoSrc(video, url) {
|
|
6913
|
+
const { autoplay, muted } = video;
|
|
6914
|
+
lockVideoBox(video);
|
|
6915
|
+
video.src = url;
|
|
6916
|
+
video.loop = true;
|
|
6917
|
+
video.playsInline = true;
|
|
6918
|
+
video.autoplay = autoplay;
|
|
6919
|
+
video.muted = muted;
|
|
6920
|
+
video.load();
|
|
6921
|
+
syncVideoPlayback(video);
|
|
6922
|
+
}
|
|
6923
|
+
function applyVideoSettingNode(key, val) {
|
|
6924
|
+
const isAutoplay = key.endsWith(VIDEO_AUTOPLAY_SUFFIX);
|
|
6925
|
+
const isMuted = key.endsWith(VIDEO_MUTED_SUFFIX);
|
|
6926
|
+
if (!isAutoplay && !isMuted) return false;
|
|
6927
|
+
const suffixLen = (isAutoplay ? VIDEO_AUTOPLAY_SUFFIX : VIDEO_MUTED_SUFFIX).length;
|
|
6928
|
+
const baseKey = key.slice(0, key.length - suffixLen);
|
|
6929
|
+
const on = val === "true";
|
|
6930
|
+
document.querySelectorAll(`[data-ohw-key="${baseKey}"]`).forEach((el) => {
|
|
6931
|
+
const video = getVideoEl(el);
|
|
6932
|
+
if (!video) return;
|
|
6933
|
+
if (isAutoplay) video.autoplay = on;
|
|
6934
|
+
else video.muted = on;
|
|
6935
|
+
syncVideoPlayback(video);
|
|
5898
6936
|
});
|
|
5899
|
-
return
|
|
6937
|
+
return true;
|
|
5900
6938
|
}
|
|
5901
6939
|
function applyLinkByKey(key, val) {
|
|
5902
6940
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -5910,7 +6948,7 @@ function applyLinkByKey(key, val) {
|
|
|
5910
6948
|
}
|
|
5911
6949
|
function isInsideLinkEditor(target) {
|
|
5912
6950
|
return Boolean(
|
|
5913
|
-
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
6951
|
+
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
5914
6952
|
);
|
|
5915
6953
|
}
|
|
5916
6954
|
function getHrefKeyFromElement(el) {
|
|
@@ -5921,7 +6959,7 @@ function getHrefKeyFromElement(el) {
|
|
|
5921
6959
|
if (!key) return null;
|
|
5922
6960
|
return { anchor, key };
|
|
5923
6961
|
}
|
|
5924
|
-
function
|
|
6962
|
+
function isNavbarButton2(el) {
|
|
5925
6963
|
return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
|
|
5926
6964
|
}
|
|
5927
6965
|
function getNavigationItemAnchor(el) {
|
|
@@ -5933,19 +6971,8 @@ function getNavigationItemAnchor(el) {
|
|
|
5933
6971
|
function isNavigationItem(el) {
|
|
5934
6972
|
return getNavigationItemAnchor(el) !== null;
|
|
5935
6973
|
}
|
|
5936
|
-
function getNavigationItemAddHintTarget(anchor) {
|
|
5937
|
-
const items = listNavigationItems();
|
|
5938
|
-
const idx = items.indexOf(anchor);
|
|
5939
|
-
if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
|
|
5940
|
-
return anchor;
|
|
5941
|
-
}
|
|
5942
|
-
function listNavigationItems() {
|
|
5943
|
-
return Array.from(
|
|
5944
|
-
document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
|
|
5945
|
-
).filter((el) => isNavigationItem(el));
|
|
5946
|
-
}
|
|
5947
6974
|
function getNavigationItemReorderState(anchor) {
|
|
5948
|
-
if (
|
|
6975
|
+
if (isNavbarButton2(anchor)) return { key: null, disabled: false };
|
|
5949
6976
|
return getReorderHandleStateFromAnchor(anchor);
|
|
5950
6977
|
}
|
|
5951
6978
|
function getReorderHandleState(el) {
|
|
@@ -5967,6 +6994,120 @@ function clearHrefKeyHover(anchor) {
|
|
|
5967
6994
|
function isInsideNavigationItem(el) {
|
|
5968
6995
|
return getNavigationItemAnchor(el) !== null;
|
|
5969
6996
|
}
|
|
6997
|
+
function getNavigationRoot(el) {
|
|
6998
|
+
const explicit = el.closest("[data-ohw-nav-root]");
|
|
6999
|
+
if (explicit) return explicit;
|
|
7000
|
+
return el.closest("nav, footer, aside");
|
|
7001
|
+
}
|
|
7002
|
+
function findFooterItemGroup(item) {
|
|
7003
|
+
const footer = item.closest("footer");
|
|
7004
|
+
if (!footer) return null;
|
|
7005
|
+
let node = item.parentElement;
|
|
7006
|
+
while (node && node !== footer) {
|
|
7007
|
+
const hasDirectNavItems = Array.from(
|
|
7008
|
+
node.querySelectorAll(":scope > [data-ohw-href-key]")
|
|
7009
|
+
).some(isNavigationItem);
|
|
7010
|
+
if (hasDirectNavItems) return node;
|
|
7011
|
+
node = node.parentElement;
|
|
7012
|
+
}
|
|
7013
|
+
return null;
|
|
7014
|
+
}
|
|
7015
|
+
function isNavigationRoot(el) {
|
|
7016
|
+
return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
|
|
7017
|
+
}
|
|
7018
|
+
function isInferredFooterGroup(el) {
|
|
7019
|
+
const footer = el.closest("footer");
|
|
7020
|
+
if (!footer || el === footer) return false;
|
|
7021
|
+
return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
|
|
7022
|
+
}
|
|
7023
|
+
function isNavigationContainer(el) {
|
|
7024
|
+
return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
|
|
7025
|
+
}
|
|
7026
|
+
function isPointOverNavigation(x, y) {
|
|
7027
|
+
const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
|
|
7028
|
+
if (!navRoot) return false;
|
|
7029
|
+
const r2 = navRoot.getBoundingClientRect();
|
|
7030
|
+
return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
|
|
7031
|
+
}
|
|
7032
|
+
function isPointOverNavItem(container, x, y) {
|
|
7033
|
+
return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
|
|
7034
|
+
if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
|
|
7035
|
+
const itemRect = el.getBoundingClientRect();
|
|
7036
|
+
return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
7039
|
+
function resolveNavContainerSelectionTarget(target, clientX, clientY) {
|
|
7040
|
+
if (getNavigationItemAnchor(target)) return null;
|
|
7041
|
+
if (target.closest('[data-ohw-role="navbar-button"]')) return null;
|
|
7042
|
+
const plainLink = target.closest("a");
|
|
7043
|
+
if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
|
|
7044
|
+
return null;
|
|
7045
|
+
}
|
|
7046
|
+
const fromClosest = target.closest("[data-ohw-nav-container]");
|
|
7047
|
+
if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
|
|
7048
|
+
return fromClosest;
|
|
7049
|
+
}
|
|
7050
|
+
const navRoot = target.closest("[data-ohw-nav-root]");
|
|
7051
|
+
if (!navRoot) return null;
|
|
7052
|
+
const container = navRoot.querySelector("[data-ohw-nav-container]");
|
|
7053
|
+
if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
|
|
7054
|
+
if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
|
|
7055
|
+
return container;
|
|
7056
|
+
}
|
|
7057
|
+
return null;
|
|
7058
|
+
}
|
|
7059
|
+
function getNavigationSelectionParent(el) {
|
|
7060
|
+
if (isNavigationItem(el)) {
|
|
7061
|
+
const explicit = el.closest("[data-ohw-nav-container]");
|
|
7062
|
+
if (explicit) return explicit;
|
|
7063
|
+
const footerGroup = findFooterItemGroup(el);
|
|
7064
|
+
if (footerGroup) return footerGroup;
|
|
7065
|
+
return getNavigationRoot(el);
|
|
7066
|
+
}
|
|
7067
|
+
if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
|
|
7068
|
+
return getNavigationRoot(el);
|
|
7069
|
+
}
|
|
7070
|
+
return null;
|
|
7071
|
+
}
|
|
7072
|
+
function placeCaretAtPoint(el, x, y) {
|
|
7073
|
+
const selection = window.getSelection();
|
|
7074
|
+
if (!selection) return;
|
|
7075
|
+
if (typeof document.caretRangeFromPoint === "function") {
|
|
7076
|
+
const range = document.caretRangeFromPoint(x, y);
|
|
7077
|
+
if (range && el.contains(range.startContainer)) {
|
|
7078
|
+
selection.removeAllRanges();
|
|
7079
|
+
selection.addRange(range);
|
|
7080
|
+
return;
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
const caretPositionFromPoint = document.caretPositionFromPoint;
|
|
7084
|
+
if (typeof caretPositionFromPoint === "function") {
|
|
7085
|
+
const position = caretPositionFromPoint(x, y);
|
|
7086
|
+
if (position && el.contains(position.offsetNode)) {
|
|
7087
|
+
const range = document.createRange();
|
|
7088
|
+
range.setStart(position.offsetNode, position.offset);
|
|
7089
|
+
range.collapse(true);
|
|
7090
|
+
selection.removeAllRanges();
|
|
7091
|
+
selection.addRange(range);
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
function selectAllTextInEditable(el) {
|
|
7096
|
+
const selection = window.getSelection();
|
|
7097
|
+
if (!selection) return;
|
|
7098
|
+
const range = document.createRange();
|
|
7099
|
+
range.selectNodeContents(el);
|
|
7100
|
+
selection.removeAllRanges();
|
|
7101
|
+
selection.addRange(range);
|
|
7102
|
+
}
|
|
7103
|
+
function getNavigationLabelEditable(target) {
|
|
7104
|
+
const editable = target.closest('[data-ohw-editable="text"]');
|
|
7105
|
+
if (!editable) return null;
|
|
7106
|
+
const hrefCtx = getHrefKeyFromElement(editable);
|
|
7107
|
+
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
7108
|
+
if (!navAnchor) return null;
|
|
7109
|
+
return { editable, navAnchor };
|
|
7110
|
+
}
|
|
5970
7111
|
function collectSections() {
|
|
5971
7112
|
return collectSectionsFromDom();
|
|
5972
7113
|
}
|
|
@@ -6090,6 +7231,8 @@ var ICONS = {
|
|
|
6090
7231
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
6091
7232
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
6092
7233
|
};
|
|
7234
|
+
var TOOLBAR_PILL_PADDING = 2;
|
|
7235
|
+
var TOOLBAR_TOGGLE_GAP = 4;
|
|
6093
7236
|
var TOOLBAR_GROUPS = [
|
|
6094
7237
|
[
|
|
6095
7238
|
{ cmd: "bold", title: "Bold" },
|
|
@@ -6114,7 +7257,7 @@ function EditGlowChrome({
|
|
|
6114
7257
|
dragDisabled = false
|
|
6115
7258
|
}) {
|
|
6116
7259
|
const GAP = 6;
|
|
6117
|
-
return /* @__PURE__ */ (0,
|
|
7260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
6118
7261
|
"div",
|
|
6119
7262
|
{
|
|
6120
7263
|
ref: elRef,
|
|
@@ -6129,7 +7272,7 @@ function EditGlowChrome({
|
|
|
6129
7272
|
zIndex: 2147483646
|
|
6130
7273
|
},
|
|
6131
7274
|
children: [
|
|
6132
|
-
/* @__PURE__ */ (0,
|
|
7275
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6133
7276
|
"div",
|
|
6134
7277
|
{
|
|
6135
7278
|
style: {
|
|
@@ -6142,7 +7285,7 @@ function EditGlowChrome({
|
|
|
6142
7285
|
}
|
|
6143
7286
|
}
|
|
6144
7287
|
),
|
|
6145
|
-
reorderHrefKey && /* @__PURE__ */ (0,
|
|
7288
|
+
reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6146
7289
|
"div",
|
|
6147
7290
|
{
|
|
6148
7291
|
"data-ohw-drag-handle-container": "",
|
|
@@ -6154,7 +7297,7 @@ function EditGlowChrome({
|
|
|
6154
7297
|
transform: "translate(calc(-100% - 7px), -50%)",
|
|
6155
7298
|
pointerEvents: dragDisabled ? "none" : "auto"
|
|
6156
7299
|
},
|
|
6157
|
-
children: /* @__PURE__ */ (0,
|
|
7300
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6158
7301
|
DragHandle,
|
|
6159
7302
|
{
|
|
6160
7303
|
"aria-label": `Reorder ${reorderHrefKey}`,
|
|
@@ -6258,7 +7401,7 @@ function FloatingToolbar({
|
|
|
6258
7401
|
onEditLink
|
|
6259
7402
|
}) {
|
|
6260
7403
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
|
|
6261
|
-
return /* @__PURE__ */ (0,
|
|
7404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6262
7405
|
"div",
|
|
6263
7406
|
{
|
|
6264
7407
|
ref: elRef,
|
|
@@ -6268,14 +7411,23 @@ function FloatingToolbar({
|
|
|
6268
7411
|
left,
|
|
6269
7412
|
transform,
|
|
6270
7413
|
zIndex: 2147483647,
|
|
7414
|
+
background: "#fff",
|
|
7415
|
+
border: "1px solid #E7E5E4",
|
|
7416
|
+
borderRadius: 6,
|
|
7417
|
+
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
7418
|
+
display: "flex",
|
|
7419
|
+
alignItems: "center",
|
|
7420
|
+
padding: TOOLBAR_PILL_PADDING,
|
|
7421
|
+
gap: TOOLBAR_TOGGLE_GAP,
|
|
7422
|
+
fontFamily: "sans-serif",
|
|
6271
7423
|
pointerEvents: "auto"
|
|
6272
7424
|
},
|
|
6273
|
-
children: /* @__PURE__ */ (0,
|
|
6274
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
6275
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
7425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
|
|
7426
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
|
|
7427
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
|
|
6276
7428
|
btns.map((btn) => {
|
|
6277
7429
|
const isActive = activeCommands.has(btn.cmd);
|
|
6278
|
-
return /* @__PURE__ */ (0,
|
|
7430
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6279
7431
|
CustomToolbarButton,
|
|
6280
7432
|
{
|
|
6281
7433
|
title: btn.title,
|
|
@@ -6284,7 +7436,7 @@ function FloatingToolbar({
|
|
|
6284
7436
|
e.preventDefault();
|
|
6285
7437
|
onCommand(btn.cmd);
|
|
6286
7438
|
},
|
|
6287
|
-
children: /* @__PURE__ */ (0,
|
|
7439
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6288
7440
|
"svg",
|
|
6289
7441
|
{
|
|
6290
7442
|
width: "16",
|
|
@@ -6305,7 +7457,7 @@ function FloatingToolbar({
|
|
|
6305
7457
|
);
|
|
6306
7458
|
})
|
|
6307
7459
|
] }, gi)),
|
|
6308
|
-
showEditLink ? /* @__PURE__ */ (0,
|
|
7460
|
+
showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6309
7461
|
CustomToolbarButton,
|
|
6310
7462
|
{
|
|
6311
7463
|
type: "button",
|
|
@@ -6319,7 +7471,7 @@ function FloatingToolbar({
|
|
|
6319
7471
|
e.preventDefault();
|
|
6320
7472
|
e.stopPropagation();
|
|
6321
7473
|
},
|
|
6322
|
-
children: /* @__PURE__ */ (0,
|
|
7474
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
|
|
6323
7475
|
}
|
|
6324
7476
|
) : null
|
|
6325
7477
|
] })
|
|
@@ -6336,7 +7488,7 @@ function StateToggle({
|
|
|
6336
7488
|
states,
|
|
6337
7489
|
onStateChange
|
|
6338
7490
|
}) {
|
|
6339
|
-
return /* @__PURE__ */ (0,
|
|
7491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6340
7492
|
ToggleGroup,
|
|
6341
7493
|
{
|
|
6342
7494
|
"data-ohw-state-toggle": "",
|
|
@@ -6350,7 +7502,7 @@ function StateToggle({
|
|
|
6350
7502
|
left: rect.right - 8,
|
|
6351
7503
|
transform: "translateX(-100%)"
|
|
6352
7504
|
},
|
|
6353
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
7505
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
6354
7506
|
}
|
|
6355
7507
|
);
|
|
6356
7508
|
}
|
|
@@ -6373,12 +7525,12 @@ function resolveSubdomain(subdomainFromQuery) {
|
|
|
6373
7525
|
return "";
|
|
6374
7526
|
}
|
|
6375
7527
|
function OhhwellsBridge() {
|
|
6376
|
-
const pathname = (0,
|
|
6377
|
-
const router = (0,
|
|
6378
|
-
const searchParams = (0,
|
|
7528
|
+
const pathname = (0, import_navigation2.usePathname)();
|
|
7529
|
+
const router = (0, import_navigation2.useRouter)();
|
|
7530
|
+
const searchParams = (0, import_navigation2.useSearchParams)();
|
|
6379
7531
|
const isEditMode = isEditSessionActive();
|
|
6380
|
-
const [bridgeRoot, setBridgeRoot] = (0,
|
|
6381
|
-
(0,
|
|
7532
|
+
const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
|
|
7533
|
+
(0, import_react8.useEffect)(() => {
|
|
6382
7534
|
const figtreeFontId = "ohw-figtree-font";
|
|
6383
7535
|
if (!document.getElementById(figtreeFontId)) {
|
|
6384
7536
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -6386,7 +7538,10 @@ function OhhwellsBridge() {
|
|
|
6386
7538
|
const stylesheet = Object.assign(document.createElement("link"), {
|
|
6387
7539
|
id: figtreeFontId,
|
|
6388
7540
|
rel: "stylesheet",
|
|
6389
|
-
|
|
7541
|
+
// Inter is loaded alongside Figtree for the media overlay's Replace button. The bridge
|
|
7542
|
+
// renders inside the customer's document, so it cannot assume any font is present — an
|
|
7543
|
+
// unloaded family would silently fall back to whatever the template happens to use.
|
|
7544
|
+
href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=Inter:wght@400;500;600&display=swap"
|
|
6390
7545
|
});
|
|
6391
7546
|
document.head.append(preconnect1, preconnect2, stylesheet);
|
|
6392
7547
|
}
|
|
@@ -6403,80 +7558,91 @@ function OhhwellsBridge() {
|
|
|
6403
7558
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
6404
7559
|
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
6405
7560
|
useLinkHrefGuardian(pathname, subdomain, isEditMode);
|
|
6406
|
-
const
|
|
7561
|
+
const postToParent2 = (0, import_react8.useCallback)((data) => {
|
|
6407
7562
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
6408
7563
|
window.parent.postMessage(data, "*");
|
|
6409
7564
|
}
|
|
6410
7565
|
}, []);
|
|
6411
|
-
const [fetchState, setFetchState] = (0,
|
|
6412
|
-
const autoSaveTimers = (0,
|
|
6413
|
-
const activeElRef = (0,
|
|
6414
|
-
const selectedElRef = (0,
|
|
6415
|
-
const originalContentRef = (0,
|
|
6416
|
-
const activeStateElRef = (0,
|
|
6417
|
-
const parentScrollRef = (0,
|
|
6418
|
-
const visibleViewportRef = (0,
|
|
6419
|
-
const [dialogPortalContainer, setDialogPortalContainer] = (0,
|
|
6420
|
-
const attachVisibleViewport = (0,
|
|
7566
|
+
const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
|
|
7567
|
+
const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
7568
|
+
const activeElRef = (0, import_react8.useRef)(null);
|
|
7569
|
+
const selectedElRef = (0, import_react8.useRef)(null);
|
|
7570
|
+
const originalContentRef = (0, import_react8.useRef)(null);
|
|
7571
|
+
const activeStateElRef = (0, import_react8.useRef)(null);
|
|
7572
|
+
const parentScrollRef = (0, import_react8.useRef)(null);
|
|
7573
|
+
const visibleViewportRef = (0, import_react8.useRef)(null);
|
|
7574
|
+
const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
|
|
7575
|
+
const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
|
|
6421
7576
|
visibleViewportRef.current = node;
|
|
6422
7577
|
setDialogPortalContainer(node);
|
|
6423
7578
|
if (node) applyVisibleViewport(node, parentScrollRef.current);
|
|
6424
7579
|
}, []);
|
|
6425
|
-
const toolbarElRef = (0,
|
|
6426
|
-
const glowElRef = (0,
|
|
6427
|
-
const hoveredImageRef = (0,
|
|
6428
|
-
const hoveredImageHasTextOverlapRef = (0,
|
|
6429
|
-
const
|
|
6430
|
-
const
|
|
6431
|
-
const
|
|
6432
|
-
const
|
|
6433
|
-
const
|
|
7580
|
+
const toolbarElRef = (0, import_react8.useRef)(null);
|
|
7581
|
+
const glowElRef = (0, import_react8.useRef)(null);
|
|
7582
|
+
const hoveredImageRef = (0, import_react8.useRef)(null);
|
|
7583
|
+
const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
|
|
7584
|
+
const dragOverElRef = (0, import_react8.useRef)(null);
|
|
7585
|
+
const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
|
|
7586
|
+
const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
|
|
7587
|
+
const hoveredGapRef = (0, import_react8.useRef)(null);
|
|
7588
|
+
const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
|
|
7589
|
+
const imageShowTimerRef = (0, import_react8.useRef)(null);
|
|
7590
|
+
const editStylesRef = (0, import_react8.useRef)(null);
|
|
7591
|
+
const activateRef = (0, import_react8.useRef)(() => {
|
|
6434
7592
|
});
|
|
6435
|
-
const deactivateRef = (0,
|
|
7593
|
+
const deactivateRef = (0, import_react8.useRef)(() => {
|
|
6436
7594
|
});
|
|
6437
|
-
const selectRef = (0,
|
|
7595
|
+
const selectRef = (0, import_react8.useRef)(() => {
|
|
6438
7596
|
});
|
|
6439
|
-
const
|
|
7597
|
+
const selectFrameRef = (0, import_react8.useRef)(() => {
|
|
6440
7598
|
});
|
|
6441
|
-
const
|
|
7599
|
+
const deselectRef = (0, import_react8.useRef)(() => {
|
|
6442
7600
|
});
|
|
6443
|
-
const
|
|
7601
|
+
const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
|
|
6444
7602
|
});
|
|
6445
|
-
const
|
|
7603
|
+
const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
|
|
6446
7604
|
});
|
|
6447
|
-
const
|
|
6448
|
-
|
|
6449
|
-
const
|
|
6450
|
-
|
|
6451
|
-
const
|
|
6452
|
-
const
|
|
6453
|
-
const
|
|
7605
|
+
const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
|
|
7606
|
+
});
|
|
7607
|
+
const postToParentRef = (0, import_react8.useRef)(postToParent2);
|
|
7608
|
+
postToParentRef.current = postToParent2;
|
|
7609
|
+
const sectionsLoadedRef = (0, import_react8.useRef)(false);
|
|
7610
|
+
const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
|
|
7611
|
+
const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
|
|
7612
|
+
const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
|
|
7613
|
+
const toolbarVariantRef = (0, import_react8.useRef)("none");
|
|
6454
7614
|
toolbarVariantRef.current = toolbarVariant;
|
|
6455
|
-
const [reorderHrefKey, setReorderHrefKey] = (0,
|
|
6456
|
-
const [reorderDragDisabled, setReorderDragDisabled] = (0,
|
|
6457
|
-
const [toggleState, setToggleState] = (0,
|
|
6458
|
-
const [maxBadge, setMaxBadge] = (0,
|
|
6459
|
-
const [activeCommands, setActiveCommands] = (0,
|
|
6460
|
-
const [sectionGap, setSectionGap] = (0,
|
|
6461
|
-
const [toolbarShowEditLink, setToolbarShowEditLink] = (0,
|
|
6462
|
-
const
|
|
6463
|
-
const [
|
|
6464
|
-
const
|
|
6465
|
-
const [
|
|
6466
|
-
const
|
|
6467
|
-
const [
|
|
6468
|
-
const [
|
|
6469
|
-
const [
|
|
6470
|
-
const
|
|
6471
|
-
const
|
|
6472
|
-
const
|
|
6473
|
-
const
|
|
6474
|
-
const
|
|
7615
|
+
const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
|
|
7616
|
+
const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
|
|
7617
|
+
const [toggleState, setToggleState] = (0, import_react8.useState)(null);
|
|
7618
|
+
const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
|
|
7619
|
+
const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
7620
|
+
const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
|
|
7621
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
|
|
7622
|
+
const hoveredNavContainerRef = (0, import_react8.useRef)(null);
|
|
7623
|
+
const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
|
|
7624
|
+
const hoveredItemElRef = (0, import_react8.useRef)(null);
|
|
7625
|
+
const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
|
|
7626
|
+
const siblingHintElRef = (0, import_react8.useRef)(null);
|
|
7627
|
+
const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
|
|
7628
|
+
const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
|
|
7629
|
+
const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
|
|
7630
|
+
const linkPopoverSessionRef = (0, import_react8.useRef)(null);
|
|
7631
|
+
const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
|
|
7632
|
+
const editContentRef = (0, import_react8.useRef)({});
|
|
7633
|
+
const [sitePages, setSitePages] = (0, import_react8.useState)([]);
|
|
7634
|
+
const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
|
|
7635
|
+
const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
|
|
7636
|
+
const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
|
|
7637
|
+
const linkPopoverPanelRef = (0, import_react8.useRef)(null);
|
|
7638
|
+
const linkPopoverOpenRef = (0, import_react8.useRef)(false);
|
|
7639
|
+
const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
|
|
6475
7640
|
setLinkPopoverRef.current = setLinkPopover;
|
|
7641
|
+
linkPopoverSessionRef.current = linkPopover;
|
|
6476
7642
|
const bumpLinkPopoverGrace = () => {
|
|
6477
7643
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
6478
7644
|
};
|
|
6479
|
-
const runSectionsPrefetch = (0,
|
|
7645
|
+
const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
|
|
6480
7646
|
if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
|
|
6481
7647
|
const gen = ++sectionsPrefetchGenRef.current;
|
|
6482
7648
|
const paths = pages.map((p) => p.path);
|
|
@@ -6495,9 +7661,9 @@ function OhhwellsBridge() {
|
|
|
6495
7661
|
);
|
|
6496
7662
|
});
|
|
6497
7663
|
}, [isEditMode, pathname]);
|
|
6498
|
-
const runSectionsPrefetchRef = (0,
|
|
7664
|
+
const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
|
|
6499
7665
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
6500
|
-
(0,
|
|
7666
|
+
(0, import_react8.useEffect)(() => {
|
|
6501
7667
|
if (!linkPopover) return;
|
|
6502
7668
|
if (hoveredImageRef.current) {
|
|
6503
7669
|
hoveredImageRef.current = null;
|
|
@@ -6505,33 +7671,9 @@ function OhhwellsBridge() {
|
|
|
6505
7671
|
}
|
|
6506
7672
|
hoveredGapRef.current = null;
|
|
6507
7673
|
setSectionGap(null);
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
const body = document.body;
|
|
6512
|
-
const prevHtmlOverflow = html.style.overflow;
|
|
6513
|
-
const prevBodyOverflow = body.style.overflow;
|
|
6514
|
-
html.style.overflow = "hidden";
|
|
6515
|
-
body.style.overflow = "hidden";
|
|
6516
|
-
const preventBackgroundScroll = (e) => {
|
|
6517
|
-
const target = e.target;
|
|
6518
|
-
if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
|
|
6519
|
-
return;
|
|
6520
|
-
}
|
|
6521
|
-
e.preventDefault();
|
|
6522
|
-
};
|
|
6523
|
-
const scrollOpts = { passive: false, capture: true };
|
|
6524
|
-
document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6525
|
-
document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6526
|
-
return () => {
|
|
6527
|
-
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6528
|
-
html.style.overflow = prevHtmlOverflow;
|
|
6529
|
-
body.style.overflow = prevBodyOverflow;
|
|
6530
|
-
document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6531
|
-
document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6532
|
-
};
|
|
6533
|
-
}, [linkPopover, postToParent]);
|
|
6534
|
-
(0, import_react6.useEffect)(() => {
|
|
7674
|
+
postToParent2({ type: "ow:image-unhover" });
|
|
7675
|
+
}, [linkPopover, postToParent2]);
|
|
7676
|
+
(0, import_react8.useEffect)(() => {
|
|
6535
7677
|
if (!isEditMode) return;
|
|
6536
7678
|
const useFixtures = shouldUseDevFixtures();
|
|
6537
7679
|
if (useFixtures) {
|
|
@@ -6552,17 +7694,17 @@ function OhhwellsBridge() {
|
|
|
6552
7694
|
runSectionsPrefetchRef.current(mapped);
|
|
6553
7695
|
};
|
|
6554
7696
|
window.addEventListener("message", onSitePages);
|
|
6555
|
-
if (!useFixtures)
|
|
7697
|
+
if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
|
|
6556
7698
|
return () => window.removeEventListener("message", onSitePages);
|
|
6557
|
-
}, [isEditMode,
|
|
6558
|
-
(0,
|
|
7699
|
+
}, [isEditMode, postToParent2]);
|
|
7700
|
+
(0, import_react8.useEffect)(() => {
|
|
6559
7701
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
6560
7702
|
void loadAllSectionsManifest().then((manifest) => {
|
|
6561
7703
|
if (Object.keys(manifest).length === 0) return;
|
|
6562
7704
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
6563
7705
|
});
|
|
6564
7706
|
}, [isEditMode]);
|
|
6565
|
-
(0,
|
|
7707
|
+
(0, import_react8.useEffect)(() => {
|
|
6566
7708
|
const update = () => {
|
|
6567
7709
|
const el = activeElRef.current ?? selectedElRef.current;
|
|
6568
7710
|
if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
|
|
@@ -6586,10 +7728,10 @@ function OhhwellsBridge() {
|
|
|
6586
7728
|
vvp.removeEventListener("resize", update);
|
|
6587
7729
|
};
|
|
6588
7730
|
}, []);
|
|
6589
|
-
const refreshStateRules = (0,
|
|
7731
|
+
const refreshStateRules = (0, import_react8.useCallback)(() => {
|
|
6590
7732
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
6591
7733
|
}, []);
|
|
6592
|
-
const processConfigRequest = (0,
|
|
7734
|
+
const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
|
|
6593
7735
|
const tracker = getSectionsTracker();
|
|
6594
7736
|
let entries = [];
|
|
6595
7737
|
try {
|
|
@@ -6612,7 +7754,7 @@ function OhhwellsBridge() {
|
|
|
6612
7754
|
}
|
|
6613
7755
|
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
6614
7756
|
}, [isEditMode]);
|
|
6615
|
-
const deactivate = (0,
|
|
7757
|
+
const deactivate = (0, import_react8.useCallback)(() => {
|
|
6616
7758
|
const el = activeElRef.current;
|
|
6617
7759
|
if (!el) return;
|
|
6618
7760
|
const key = el.dataset.ohwKey;
|
|
@@ -6641,21 +7783,23 @@ function OhhwellsBridge() {
|
|
|
6641
7783
|
setMaxBadge(null);
|
|
6642
7784
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6643
7785
|
setToolbarShowEditLink(false);
|
|
6644
|
-
|
|
6645
|
-
}, [
|
|
6646
|
-
const deselect = (0,
|
|
7786
|
+
postToParent2({ type: "ow:exit-edit" });
|
|
7787
|
+
}, [postToParent2]);
|
|
7788
|
+
const deselect = (0, import_react8.useCallback)(() => {
|
|
6647
7789
|
selectedElRef.current = null;
|
|
6648
7790
|
setReorderHrefKey(null);
|
|
6649
7791
|
setReorderDragDisabled(false);
|
|
6650
7792
|
siblingHintElRef.current = null;
|
|
6651
7793
|
setSiblingHintRect(null);
|
|
6652
7794
|
setIsItemDragging(false);
|
|
7795
|
+
hoveredNavContainerRef.current = null;
|
|
7796
|
+
setHoveredNavContainerRect(null);
|
|
6653
7797
|
if (!activeElRef.current) {
|
|
6654
7798
|
setToolbarRect(null);
|
|
6655
7799
|
setToolbarVariant("none");
|
|
6656
7800
|
}
|
|
6657
7801
|
}, []);
|
|
6658
|
-
const reselectNavigationItem = (0,
|
|
7802
|
+
const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
|
|
6659
7803
|
selectedElRef.current = navAnchor;
|
|
6660
7804
|
const { key, disabled } = getNavigationItemReorderState(navAnchor);
|
|
6661
7805
|
setReorderHrefKey(key);
|
|
@@ -6665,7 +7809,7 @@ function OhhwellsBridge() {
|
|
|
6665
7809
|
setToolbarShowEditLink(false);
|
|
6666
7810
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6667
7811
|
}, []);
|
|
6668
|
-
const commitNavigationTextEdit = (0,
|
|
7812
|
+
const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
|
|
6669
7813
|
const el = activeElRef.current;
|
|
6670
7814
|
if (!el) return;
|
|
6671
7815
|
const key = el.dataset.ohwKey;
|
|
@@ -6678,9 +7822,9 @@ function OhhwellsBridge() {
|
|
|
6678
7822
|
const html = sanitizeHtml(el.innerHTML);
|
|
6679
7823
|
const original = originalContentRef.current ?? "";
|
|
6680
7824
|
if (html !== sanitizeHtml(original)) {
|
|
6681
|
-
|
|
7825
|
+
postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
|
|
6682
7826
|
const h = document.documentElement.scrollHeight;
|
|
6683
|
-
if (h > 50)
|
|
7827
|
+
if (h > 50) postToParent2({ type: "ow:height", height: h });
|
|
6684
7828
|
}
|
|
6685
7829
|
}
|
|
6686
7830
|
el.removeAttribute("contenteditable");
|
|
@@ -6688,31 +7832,38 @@ function OhhwellsBridge() {
|
|
|
6688
7832
|
setMaxBadge(null);
|
|
6689
7833
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6690
7834
|
setToolbarShowEditLink(false);
|
|
6691
|
-
|
|
7835
|
+
postToParent2({ type: "ow:exit-edit" });
|
|
6692
7836
|
reselectNavigationItem(navAnchor);
|
|
6693
|
-
}, [
|
|
6694
|
-
const
|
|
6695
|
-
const
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
7837
|
+
}, [postToParent2, reselectNavigationItem]);
|
|
7838
|
+
const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
|
|
7839
|
+
const items = listNavbarItems();
|
|
7840
|
+
addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
|
|
7841
|
+
deselectRef.current();
|
|
7842
|
+
deactivateRef.current();
|
|
7843
|
+
bumpLinkPopoverGrace();
|
|
7844
|
+
setLinkPopover({
|
|
7845
|
+
key: "__add-nav__",
|
|
7846
|
+
mode: "create",
|
|
7847
|
+
intent: "add-nav"
|
|
7848
|
+
});
|
|
6700
7849
|
}, []);
|
|
6701
|
-
const handleItemDragStart = (0,
|
|
7850
|
+
const handleItemDragStart = (0, import_react8.useCallback)(() => {
|
|
6702
7851
|
siblingHintElRef.current = null;
|
|
6703
7852
|
setSiblingHintRect(null);
|
|
6704
7853
|
setIsItemDragging(true);
|
|
6705
7854
|
}, []);
|
|
6706
|
-
const handleItemDragEnd = (0,
|
|
7855
|
+
const handleItemDragEnd = (0, import_react8.useCallback)(() => {
|
|
6707
7856
|
setIsItemDragging(false);
|
|
6708
7857
|
}, []);
|
|
6709
7858
|
reselectNavigationItemRef.current = reselectNavigationItem;
|
|
6710
7859
|
commitNavigationTextEditRef.current = commitNavigationTextEdit;
|
|
6711
|
-
const select = (0,
|
|
7860
|
+
const select = (0, import_react8.useCallback)((anchor) => {
|
|
6712
7861
|
if (!isNavigationItem(anchor)) return;
|
|
6713
7862
|
if (activeElRef.current) deactivate();
|
|
6714
7863
|
selectedElRef.current = anchor;
|
|
6715
7864
|
clearHrefKeyHover(anchor);
|
|
7865
|
+
hoveredNavContainerRef.current = null;
|
|
7866
|
+
setHoveredNavContainerRect(null);
|
|
6716
7867
|
setHoveredItemRect(null);
|
|
6717
7868
|
hoveredItemElRef.current = null;
|
|
6718
7869
|
siblingHintElRef.current = null;
|
|
@@ -6726,13 +7877,32 @@ function OhhwellsBridge() {
|
|
|
6726
7877
|
setToolbarShowEditLink(false);
|
|
6727
7878
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6728
7879
|
}, [deactivate]);
|
|
6729
|
-
const
|
|
7880
|
+
const selectFrame = (0, import_react8.useCallback)((el) => {
|
|
7881
|
+
if (!isNavigationContainer(el)) return;
|
|
7882
|
+
if (activeElRef.current) deactivate();
|
|
7883
|
+
selectedElRef.current = el;
|
|
7884
|
+
clearHrefKeyHover(el);
|
|
7885
|
+
hoveredNavContainerRef.current = null;
|
|
7886
|
+
setHoveredNavContainerRect(null);
|
|
7887
|
+
setHoveredItemRect(null);
|
|
7888
|
+
hoveredItemElRef.current = null;
|
|
7889
|
+
siblingHintElRef.current = null;
|
|
7890
|
+
setSiblingHintRect(null);
|
|
7891
|
+
setIsItemDragging(false);
|
|
7892
|
+
setReorderHrefKey(null);
|
|
7893
|
+
setReorderDragDisabled(false);
|
|
7894
|
+
setToolbarVariant("select-frame");
|
|
7895
|
+
setToolbarRect(el.getBoundingClientRect());
|
|
7896
|
+
setToolbarShowEditLink(false);
|
|
7897
|
+
setActiveCommands(/* @__PURE__ */ new Set());
|
|
7898
|
+
}, [deactivate]);
|
|
7899
|
+
const activate = (0, import_react8.useCallback)((el, options) => {
|
|
6730
7900
|
if (activeElRef.current === el) return;
|
|
6731
7901
|
selectedElRef.current = null;
|
|
6732
7902
|
deactivate();
|
|
6733
7903
|
if (hoveredImageRef.current) {
|
|
6734
7904
|
hoveredImageRef.current = null;
|
|
6735
|
-
|
|
7905
|
+
setMediaHover(null);
|
|
6736
7906
|
}
|
|
6737
7907
|
setToolbarVariant("rich-text");
|
|
6738
7908
|
siblingHintElRef.current = null;
|
|
@@ -6744,6 +7914,9 @@ function OhhwellsBridge() {
|
|
|
6744
7914
|
activeElRef.current = el;
|
|
6745
7915
|
originalContentRef.current = el.innerHTML;
|
|
6746
7916
|
el.focus();
|
|
7917
|
+
if (options?.caretX !== void 0 && options?.caretY !== void 0) {
|
|
7918
|
+
placeCaretAtPoint(el, options.caretX, options.caretY);
|
|
7919
|
+
}
|
|
6747
7920
|
setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
|
|
6748
7921
|
const navAnchor = getNavigationItemAnchor(el);
|
|
6749
7922
|
if (navAnchor) {
|
|
@@ -6755,14 +7928,15 @@ function OhhwellsBridge() {
|
|
|
6755
7928
|
setReorderDragDisabled(reorderDisabled);
|
|
6756
7929
|
}
|
|
6757
7930
|
setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
|
|
6758
|
-
|
|
7931
|
+
postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
|
|
6759
7932
|
requestAnimationFrame(() => refreshActiveCommandsRef.current());
|
|
6760
|
-
}, [deactivate,
|
|
7933
|
+
}, [deactivate, postToParent2]);
|
|
6761
7934
|
activateRef.current = activate;
|
|
6762
7935
|
deactivateRef.current = deactivate;
|
|
6763
7936
|
selectRef.current = select;
|
|
7937
|
+
selectFrameRef.current = selectFrame;
|
|
6764
7938
|
deselectRef.current = deselect;
|
|
6765
|
-
(0,
|
|
7939
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
6766
7940
|
if (!subdomain || isEditMode) {
|
|
6767
7941
|
setFetchState("done");
|
|
6768
7942
|
return;
|
|
@@ -6771,6 +7945,7 @@ function OhhwellsBridge() {
|
|
|
6771
7945
|
const imageLoads = [];
|
|
6772
7946
|
for (const [key, val] of Object.entries(content)) {
|
|
6773
7947
|
if (key === "__ohw_sections") continue;
|
|
7948
|
+
if (applyVideoSettingNode(key, val)) continue;
|
|
6774
7949
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6775
7950
|
if (el.dataset.ohwEditable === "image") {
|
|
6776
7951
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6784,6 +7959,15 @@ function OhhwellsBridge() {
|
|
|
6784
7959
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
6785
7960
|
const next = `url('${val}')`;
|
|
6786
7961
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
7962
|
+
} else if (el.dataset.ohwEditable === "video") {
|
|
7963
|
+
const video = getVideoEl(el);
|
|
7964
|
+
if (video && video.src !== val) {
|
|
7965
|
+
applyVideoSrc(video, val);
|
|
7966
|
+
imageLoads.push(new Promise((resolve) => {
|
|
7967
|
+
video.onloadeddata = () => resolve();
|
|
7968
|
+
video.onerror = () => resolve();
|
|
7969
|
+
}));
|
|
7970
|
+
}
|
|
6787
7971
|
} else if (el.dataset.ohwEditable === "link") {
|
|
6788
7972
|
applyLinkHref(el, val);
|
|
6789
7973
|
} else if (el.innerHTML !== val) {
|
|
@@ -6792,6 +7976,7 @@ function OhhwellsBridge() {
|
|
|
6792
7976
|
});
|
|
6793
7977
|
applyLinkByKey(key, val);
|
|
6794
7978
|
}
|
|
7979
|
+
reconcileNavbarItemsFromContent(content);
|
|
6795
7980
|
enforceLinkHrefs();
|
|
6796
7981
|
initSectionsFromContent(content, true);
|
|
6797
7982
|
sectionsLoadedRef.current = true;
|
|
@@ -6820,7 +8005,7 @@ function OhhwellsBridge() {
|
|
|
6820
8005
|
cancelled = true;
|
|
6821
8006
|
};
|
|
6822
8007
|
}, [subdomain, isEditMode]);
|
|
6823
|
-
(0,
|
|
8008
|
+
(0, import_react8.useEffect)(() => {
|
|
6824
8009
|
if (!subdomain || isEditMode) return;
|
|
6825
8010
|
let debounceTimer = null;
|
|
6826
8011
|
let observer = null;
|
|
@@ -6832,6 +8017,7 @@ function OhhwellsBridge() {
|
|
|
6832
8017
|
try {
|
|
6833
8018
|
for (const [key, val] of Object.entries(content)) {
|
|
6834
8019
|
if (key === "__ohw_sections") continue;
|
|
8020
|
+
if (applyVideoSettingNode(key, val)) continue;
|
|
6835
8021
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6836
8022
|
if (el.dataset.ohwEditable === "image") {
|
|
6837
8023
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6839,6 +8025,9 @@ function OhhwellsBridge() {
|
|
|
6839
8025
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
6840
8026
|
const next = `url('${val}')`;
|
|
6841
8027
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
8028
|
+
} else if (el.dataset.ohwEditable === "video") {
|
|
8029
|
+
const video = getVideoEl(el);
|
|
8030
|
+
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
6842
8031
|
} else if (el.dataset.ohwEditable === "link") {
|
|
6843
8032
|
applyLinkHref(el, val);
|
|
6844
8033
|
} else if (el.innerHTML !== val) {
|
|
@@ -6847,6 +8036,7 @@ function OhhwellsBridge() {
|
|
|
6847
8036
|
});
|
|
6848
8037
|
applyLinkByKey(key, val);
|
|
6849
8038
|
}
|
|
8039
|
+
reconcileNavbarItemsFromContent(content);
|
|
6850
8040
|
} finally {
|
|
6851
8041
|
observer?.observe(document.body, { childList: true, subtree: true });
|
|
6852
8042
|
}
|
|
@@ -6864,26 +8054,44 @@ function OhhwellsBridge() {
|
|
|
6864
8054
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6865
8055
|
};
|
|
6866
8056
|
}, [subdomain, isEditMode, pathname]);
|
|
6867
|
-
(0,
|
|
8057
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
6868
8058
|
const el = document.getElementById("ohw-loader");
|
|
6869
8059
|
if (!el) return;
|
|
6870
8060
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
6871
8061
|
el.style.display = visible ? "flex" : "none";
|
|
6872
8062
|
}, [subdomain, fetchState]);
|
|
6873
|
-
(0,
|
|
6874
|
-
|
|
6875
|
-
}, [pathname,
|
|
6876
|
-
(0,
|
|
8063
|
+
(0, import_react8.useEffect)(() => {
|
|
8064
|
+
postToParent2({ type: "ow:navigation", path: pathname });
|
|
8065
|
+
}, [pathname, postToParent2]);
|
|
8066
|
+
(0, import_react8.useEffect)(() => {
|
|
6877
8067
|
if (!isEditMode) return;
|
|
8068
|
+
if (linkPopoverSessionRef.current?.intent === "add-nav") return;
|
|
8069
|
+
if (document.querySelector("[data-ohw-section-picker]")) return;
|
|
6878
8070
|
setLinkPopover(null);
|
|
6879
8071
|
deselectRef.current();
|
|
6880
8072
|
deactivateRef.current();
|
|
6881
8073
|
}, [pathname, isEditMode]);
|
|
6882
|
-
(0,
|
|
8074
|
+
(0, import_react8.useEffect)(() => {
|
|
8075
|
+
const contentForNav = () => {
|
|
8076
|
+
if (isEditMode) return editContentRef.current;
|
|
8077
|
+
if (!subdomain) return {};
|
|
8078
|
+
return contentCache.get(subdomain) ?? {};
|
|
8079
|
+
};
|
|
8080
|
+
const run = () => reconcileNavbarItemsFromContent(contentForNav());
|
|
8081
|
+
run();
|
|
8082
|
+
const nav = document.querySelector("nav");
|
|
8083
|
+
if (!nav) return;
|
|
8084
|
+
const observer = new MutationObserver(() => {
|
|
8085
|
+
requestAnimationFrame(run);
|
|
8086
|
+
});
|
|
8087
|
+
observer.observe(nav, { childList: true, subtree: true });
|
|
8088
|
+
return () => observer.disconnect();
|
|
8089
|
+
}, [isEditMode, pathname, subdomain, fetchState]);
|
|
8090
|
+
(0, import_react8.useEffect)(() => {
|
|
6883
8091
|
if (!isEditMode) return;
|
|
6884
8092
|
const measure = () => {
|
|
6885
8093
|
const h = document.body.scrollHeight;
|
|
6886
|
-
if (h > 50)
|
|
8094
|
+
if (h > 50) postToParent2({ type: "ow:height", height: h });
|
|
6887
8095
|
};
|
|
6888
8096
|
const t1 = setTimeout(measure, 50);
|
|
6889
8097
|
const t2 = setTimeout(measure, 500);
|
|
@@ -6902,8 +8110,8 @@ function OhhwellsBridge() {
|
|
|
6902
8110
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
6903
8111
|
window.removeEventListener("resize", handleResize);
|
|
6904
8112
|
};
|
|
6905
|
-
}, [pathname, isEditMode,
|
|
6906
|
-
(0,
|
|
8113
|
+
}, [pathname, isEditMode, postToParent2]);
|
|
8114
|
+
(0, import_react8.useEffect)(() => {
|
|
6907
8115
|
if (!subdomainFromQuery || isEditMode) return;
|
|
6908
8116
|
const handleClick = (e) => {
|
|
6909
8117
|
const anchor = e.target.closest("a");
|
|
@@ -6919,7 +8127,7 @@ function OhhwellsBridge() {
|
|
|
6919
8127
|
document.addEventListener("click", handleClick, true);
|
|
6920
8128
|
return () => document.removeEventListener("click", handleClick, true);
|
|
6921
8129
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
6922
|
-
(0,
|
|
8130
|
+
(0, import_react8.useEffect)(() => {
|
|
6923
8131
|
if (!isEditMode) {
|
|
6924
8132
|
editStylesRef.current?.base.remove();
|
|
6925
8133
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -6942,8 +8150,9 @@ function OhhwellsBridge() {
|
|
|
6942
8150
|
[data-ohw-editable] {
|
|
6943
8151
|
display: block;
|
|
6944
8152
|
}
|
|
6945
|
-
[data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
|
|
8153
|
+
[data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
|
|
6946
8154
|
[data-ohw-editable="image"], [data-ohw-editable="image"] *,
|
|
8155
|
+
[data-ohw-editable="video"], [data-ohw-editable="video"] *,
|
|
6947
8156
|
[data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
|
|
6948
8157
|
[data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
|
|
6949
8158
|
[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
|
|
@@ -6992,6 +8201,13 @@ function OhhwellsBridge() {
|
|
|
6992
8201
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
6993
8202
|
if (isInsideLinkEditor(target)) return;
|
|
6994
8203
|
if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
|
|
8204
|
+
const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
|
|
8205
|
+
if (navFromChrome) {
|
|
8206
|
+
e.preventDefault();
|
|
8207
|
+
e.stopPropagation();
|
|
8208
|
+
selectFrameRef.current(navFromChrome);
|
|
8209
|
+
return;
|
|
8210
|
+
}
|
|
6995
8211
|
e.preventDefault();
|
|
6996
8212
|
e.stopPropagation();
|
|
6997
8213
|
return;
|
|
@@ -7006,14 +8222,15 @@ function OhhwellsBridge() {
|
|
|
7006
8222
|
bumpLinkPopoverGrace();
|
|
7007
8223
|
setLinkPopoverRef.current({
|
|
7008
8224
|
key: editable.dataset.ohwKey ?? "",
|
|
7009
|
-
|
|
8225
|
+
mode: "edit",
|
|
8226
|
+
target: getLinkHref2(editable)
|
|
7010
8227
|
});
|
|
7011
8228
|
return;
|
|
7012
8229
|
}
|
|
7013
|
-
if (editable
|
|
8230
|
+
if (isMediaEditable(editable)) {
|
|
7014
8231
|
e.preventDefault();
|
|
7015
8232
|
e.stopPropagation();
|
|
7016
|
-
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
|
|
8233
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
7017
8234
|
return;
|
|
7018
8235
|
}
|
|
7019
8236
|
const hrefCtx = getHrefKeyFromElement(editable);
|
|
@@ -7022,7 +8239,8 @@ function OhhwellsBridge() {
|
|
|
7022
8239
|
e.preventDefault();
|
|
7023
8240
|
e.stopPropagation();
|
|
7024
8241
|
if (selectedElRef.current === navAnchor) {
|
|
7025
|
-
|
|
8242
|
+
if (e.detail >= 2) return;
|
|
8243
|
+
activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
|
|
7026
8244
|
return;
|
|
7027
8245
|
}
|
|
7028
8246
|
selectRef.current(navAnchor);
|
|
@@ -7041,6 +8259,22 @@ function OhhwellsBridge() {
|
|
|
7041
8259
|
selectRef.current(hrefAnchor);
|
|
7042
8260
|
return;
|
|
7043
8261
|
}
|
|
8262
|
+
const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
|
|
8263
|
+
if (navContainerToSelect) {
|
|
8264
|
+
e.preventDefault();
|
|
8265
|
+
e.stopPropagation();
|
|
8266
|
+
selectFrameRef.current(navContainerToSelect);
|
|
8267
|
+
return;
|
|
8268
|
+
}
|
|
8269
|
+
const selectedContainer = selectedElRef.current;
|
|
8270
|
+
if (selectedContainer && isNavigationContainer(selectedContainer)) {
|
|
8271
|
+
const navItem = getNavigationItemAnchor(target);
|
|
8272
|
+
if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
|
|
8273
|
+
e.preventDefault();
|
|
8274
|
+
e.stopPropagation();
|
|
8275
|
+
return;
|
|
8276
|
+
}
|
|
8277
|
+
}
|
|
7044
8278
|
if (activeElRef.current) {
|
|
7045
8279
|
const sel = window.getSelection();
|
|
7046
8280
|
if (sel && !sel.isCollapsed) {
|
|
@@ -7066,10 +8300,48 @@ function OhhwellsBridge() {
|
|
|
7066
8300
|
deselectRef.current();
|
|
7067
8301
|
deactivateRef.current();
|
|
7068
8302
|
};
|
|
8303
|
+
const handleDblClick = (e) => {
|
|
8304
|
+
const target = e.target;
|
|
8305
|
+
if (target.closest("[data-ohw-toolbar]")) return;
|
|
8306
|
+
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
8307
|
+
if (target.closest("[data-ohw-max-badge]")) return;
|
|
8308
|
+
if (isInsideLinkEditor(target)) return;
|
|
8309
|
+
if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
|
|
8310
|
+
return;
|
|
8311
|
+
}
|
|
8312
|
+
const navLabel = getNavigationLabelEditable(target);
|
|
8313
|
+
if (!navLabel) return;
|
|
8314
|
+
const { editable } = navLabel;
|
|
8315
|
+
e.preventDefault();
|
|
8316
|
+
e.stopPropagation();
|
|
8317
|
+
if (activeElRef.current !== editable) {
|
|
8318
|
+
activateRef.current(editable);
|
|
8319
|
+
}
|
|
8320
|
+
requestAnimationFrame(() => {
|
|
8321
|
+
selectAllTextInEditable(editable);
|
|
8322
|
+
refreshActiveCommandsRef.current();
|
|
8323
|
+
});
|
|
8324
|
+
};
|
|
7069
8325
|
const handleMouseOver = (e) => {
|
|
7070
8326
|
const target = e.target;
|
|
8327
|
+
if (toolbarVariantRef.current !== "select-frame") {
|
|
8328
|
+
const navContainer = target.closest("[data-ohw-nav-container]");
|
|
8329
|
+
if (navContainer && !getNavigationItemAnchor(target)) {
|
|
8330
|
+
hoveredNavContainerRef.current = navContainer;
|
|
8331
|
+
setHoveredNavContainerRect(navContainer.getBoundingClientRect());
|
|
8332
|
+
hoveredItemElRef.current = null;
|
|
8333
|
+
setHoveredItemRect(null);
|
|
8334
|
+
return;
|
|
8335
|
+
}
|
|
8336
|
+
if (!target.closest("[data-ohw-nav-container]")) {
|
|
8337
|
+
hoveredNavContainerRef.current = null;
|
|
8338
|
+
setHoveredNavContainerRect(null);
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
7071
8341
|
const navAnchor = getNavigationItemAnchor(target);
|
|
7072
8342
|
if (navAnchor) {
|
|
8343
|
+
hoveredNavContainerRef.current = null;
|
|
8344
|
+
setHoveredNavContainerRect(null);
|
|
7073
8345
|
const selected2 = selectedElRef.current;
|
|
7074
8346
|
if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
|
|
7075
8347
|
clearHrefKeyHover(navAnchor);
|
|
@@ -7081,7 +8353,7 @@ function OhhwellsBridge() {
|
|
|
7081
8353
|
if (!editable) return;
|
|
7082
8354
|
const selected = selectedElRef.current;
|
|
7083
8355
|
if (selected && (selected === editable || selected.contains(editable))) return;
|
|
7084
|
-
if (editable
|
|
8356
|
+
if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
|
|
7085
8357
|
const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
|
|
7086
8358
|
if (hoverTarget.hasAttribute("data-ohw-href-key")) {
|
|
7087
8359
|
clearHrefKeyHover(hoverTarget);
|
|
@@ -7094,6 +8366,14 @@ function OhhwellsBridge() {
|
|
|
7094
8366
|
};
|
|
7095
8367
|
const handleMouseOut = (e) => {
|
|
7096
8368
|
const target = e.target;
|
|
8369
|
+
if (target.closest("[data-ohw-nav-container]")) {
|
|
8370
|
+
const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
|
|
8371
|
+
if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
|
|
8372
|
+
return;
|
|
8373
|
+
}
|
|
8374
|
+
hoveredNavContainerRef.current = null;
|
|
8375
|
+
setHoveredNavContainerRect(null);
|
|
8376
|
+
}
|
|
7097
8377
|
const navAnchor = getNavigationItemAnchor(target);
|
|
7098
8378
|
if (navAnchor) {
|
|
7099
8379
|
const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
|
|
@@ -7109,7 +8389,7 @@ function OhhwellsBridge() {
|
|
|
7109
8389
|
if (!editable) return;
|
|
7110
8390
|
const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
|
|
7111
8391
|
if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
|
|
7112
|
-
if (editable
|
|
8392
|
+
if (!isMediaEditable(editable)) {
|
|
7113
8393
|
const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
|
|
7114
8394
|
if (hoverTarget.hasAttribute("data-ohw-href-key")) {
|
|
7115
8395
|
if (!related?.closest("[data-ohw-href-key]")) {
|
|
@@ -7155,23 +8435,26 @@ function OhhwellsBridge() {
|
|
|
7155
8435
|
}
|
|
7156
8436
|
return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
|
|
7157
8437
|
};
|
|
7158
|
-
const
|
|
8438
|
+
const showImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
|
|
7159
8439
|
const r2 = getVisibleRect(imgEl);
|
|
7160
8440
|
const hasTextOverlap = forceTextOverlap ?? false;
|
|
7161
8441
|
hoveredImageHasTextOverlapRef.current = hasTextOverlap;
|
|
7162
|
-
|
|
7163
|
-
|
|
8442
|
+
const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
|
|
8443
|
+
setMediaHover({
|
|
7164
8444
|
key: imgEl.dataset.ohwKey ?? "",
|
|
7165
8445
|
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
8446
|
+
elementType: imgEl.dataset.ohwEditable ?? "image",
|
|
7166
8447
|
hasTextOverlap,
|
|
7167
|
-
|
|
8448
|
+
isDragOver,
|
|
8449
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
7168
8450
|
});
|
|
7169
8451
|
const track = imgEl.closest("[data-ohw-hover-pause]");
|
|
7170
8452
|
if (track) track.setAttribute("data-ohw-hover-paused", "");
|
|
7171
8453
|
};
|
|
8454
|
+
const clearImageHover = () => setMediaHover(null);
|
|
7172
8455
|
const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
|
|
7173
8456
|
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
7174
|
-
const images = Array.from(document.querySelectorAll(
|
|
8457
|
+
const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
|
|
7175
8458
|
const matches = [];
|
|
7176
8459
|
for (let i = images.length - 1; i >= 0; i--) {
|
|
7177
8460
|
const el = images[i];
|
|
@@ -7187,7 +8470,9 @@ function OhhwellsBridge() {
|
|
|
7187
8470
|
if (x >= r2.left && x <= r2.left + r2.width && y >= r2.top && y <= r2.top + r2.height) matches.push(el);
|
|
7188
8471
|
}
|
|
7189
8472
|
if (matches.length === 0) return null;
|
|
7190
|
-
const imageTypeMatches = matches.filter(
|
|
8473
|
+
const imageTypeMatches = matches.filter(
|
|
8474
|
+
(el) => el.dataset.ohwEditable === "image" || el.dataset.ohwEditable === "video"
|
|
8475
|
+
);
|
|
7191
8476
|
const candidatePool = imageTypeMatches.length > 0 ? imageTypeMatches : matches;
|
|
7192
8477
|
const smallest = candidatePool.reduce((best, el) => {
|
|
7193
8478
|
const br = getVisibleRect(best);
|
|
@@ -7210,47 +8495,91 @@ function OhhwellsBridge() {
|
|
|
7210
8495
|
}
|
|
7211
8496
|
return smallest;
|
|
7212
8497
|
};
|
|
8498
|
+
const dismissImageHover = () => {
|
|
8499
|
+
if (hoveredImageRef.current) {
|
|
8500
|
+
hoveredImageRef.current = null;
|
|
8501
|
+
hoveredImageHasTextOverlapRef.current = false;
|
|
8502
|
+
resumeAnimTracks();
|
|
8503
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
8504
|
+
}
|
|
8505
|
+
clearImageHover();
|
|
8506
|
+
};
|
|
8507
|
+
const probeNavigationHoverAt = (x, y) => {
|
|
8508
|
+
if (toolbarVariantRef.current === "select-frame") {
|
|
8509
|
+
hoveredNavContainerRef.current = null;
|
|
8510
|
+
setHoveredNavContainerRect(null);
|
|
8511
|
+
return;
|
|
8512
|
+
}
|
|
8513
|
+
const navContainer = document.querySelector("[data-ohw-nav-container]");
|
|
8514
|
+
if (!navContainer) {
|
|
8515
|
+
hoveredNavContainerRef.current = null;
|
|
8516
|
+
setHoveredNavContainerRect(null);
|
|
8517
|
+
return;
|
|
8518
|
+
}
|
|
8519
|
+
const containerRect = navContainer.getBoundingClientRect();
|
|
8520
|
+
const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
|
|
8521
|
+
if (!overContainer) {
|
|
8522
|
+
hoveredNavContainerRef.current = null;
|
|
8523
|
+
setHoveredNavContainerRect(null);
|
|
8524
|
+
return;
|
|
8525
|
+
}
|
|
8526
|
+
const navItemHit = Array.from(
|
|
8527
|
+
navContainer.querySelectorAll("[data-ohw-href-key]")
|
|
8528
|
+
).find((el) => {
|
|
8529
|
+
if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
|
|
8530
|
+
const itemRect = el.getBoundingClientRect();
|
|
8531
|
+
return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
|
|
8532
|
+
});
|
|
8533
|
+
if (navItemHit) {
|
|
8534
|
+
hoveredNavContainerRef.current = null;
|
|
8535
|
+
setHoveredNavContainerRect(null);
|
|
8536
|
+
const selected = selectedElRef.current;
|
|
8537
|
+
if (selected !== navItemHit && !selected?.contains(navItemHit)) {
|
|
8538
|
+
clearHrefKeyHover(navItemHit);
|
|
8539
|
+
hoveredItemElRef.current = navItemHit;
|
|
8540
|
+
setHoveredItemRect(navItemHit.getBoundingClientRect());
|
|
8541
|
+
}
|
|
8542
|
+
return;
|
|
8543
|
+
}
|
|
8544
|
+
hoveredNavContainerRef.current = navContainer;
|
|
8545
|
+
setHoveredNavContainerRect(containerRect);
|
|
8546
|
+
hoveredItemElRef.current = null;
|
|
8547
|
+
setHoveredItemRect(null);
|
|
8548
|
+
};
|
|
7213
8549
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
7214
8550
|
if (linkPopoverOpenRef.current) {
|
|
7215
|
-
|
|
7216
|
-
hoveredImageRef.current = null;
|
|
7217
|
-
hoveredImageHasTextOverlapRef.current = false;
|
|
7218
|
-
resumeAnimTracks();
|
|
7219
|
-
postToParentRef.current({ type: "ow:image-unhover" });
|
|
7220
|
-
}
|
|
8551
|
+
dismissImageHover();
|
|
7221
8552
|
return;
|
|
7222
8553
|
}
|
|
8554
|
+
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
8555
|
+
if (isPointOverNavigation(x, y)) {
|
|
8556
|
+
dismissImageHover();
|
|
8557
|
+
probeNavigationHoverAt(x, y);
|
|
8558
|
+
return;
|
|
8559
|
+
}
|
|
8560
|
+
hoveredNavContainerRef.current = null;
|
|
8561
|
+
setHoveredNavContainerRect(null);
|
|
7223
8562
|
const toggleEl = document.querySelector("[data-ohw-state-toggle]");
|
|
7224
8563
|
if (toggleEl) {
|
|
7225
|
-
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
7226
8564
|
const tr = toggleEl.getBoundingClientRect();
|
|
7227
8565
|
if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
|
|
7228
|
-
|
|
7229
|
-
hoveredImageRef.current = null;
|
|
7230
|
-
resumeAnimTracks();
|
|
7231
|
-
postToParentRef.current({ type: "ow:image-unhover" });
|
|
7232
|
-
}
|
|
8566
|
+
dismissImageHover();
|
|
7233
8567
|
return;
|
|
7234
8568
|
}
|
|
7235
8569
|
}
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
resumeAnimTracks();
|
|
7241
|
-
postToParentRef.current({ type: "ow:image-unhover" });
|
|
7242
|
-
}
|
|
8570
|
+
const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
|
|
8571
|
+
const activeEl = activeElRef.current;
|
|
8572
|
+
if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
|
|
8573
|
+
dismissImageHover();
|
|
7243
8574
|
return;
|
|
7244
8575
|
}
|
|
7245
|
-
const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
|
|
7246
8576
|
if (imgEl) {
|
|
7247
|
-
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
7248
8577
|
const topEl = document.elementFromPoint(x, y);
|
|
7249
8578
|
if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
|
|
7250
8579
|
if (hoveredImageRef.current) {
|
|
7251
8580
|
hoveredImageRef.current = null;
|
|
7252
8581
|
resumeAnimTracks();
|
|
7253
|
-
|
|
8582
|
+
clearImageHover();
|
|
7254
8583
|
}
|
|
7255
8584
|
return;
|
|
7256
8585
|
}
|
|
@@ -7259,13 +8588,13 @@ function OhhwellsBridge() {
|
|
|
7259
8588
|
hoveredImageRef.current = null;
|
|
7260
8589
|
hoveredImageHasTextOverlapRef.current = false;
|
|
7261
8590
|
resumeAnimTracks();
|
|
7262
|
-
|
|
8591
|
+
clearImageHover();
|
|
7263
8592
|
}
|
|
7264
8593
|
return;
|
|
7265
8594
|
}
|
|
7266
8595
|
const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
|
|
7267
8596
|
const textEditable = Array.from(
|
|
7268
|
-
document.querySelectorAll(
|
|
8597
|
+
document.querySelectorAll(NON_MEDIA_SELECTOR)
|
|
7269
8598
|
).find((el) => {
|
|
7270
8599
|
const er = el.getBoundingClientRect();
|
|
7271
8600
|
return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
|
|
@@ -7284,14 +8613,14 @@ function OhhwellsBridge() {
|
|
|
7284
8613
|
hoveredImageRef.current = null;
|
|
7285
8614
|
hoveredImageHasTextOverlapRef.current = false;
|
|
7286
8615
|
resumeAnimTracks();
|
|
7287
|
-
|
|
8616
|
+
clearImageHover();
|
|
7288
8617
|
}
|
|
7289
8618
|
return;
|
|
7290
8619
|
}
|
|
7291
8620
|
if (isStateCardImage) {
|
|
7292
8621
|
if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
|
|
7293
8622
|
hoveredImageRef.current = imgEl;
|
|
7294
|
-
|
|
8623
|
+
showImageHover(imgEl, isDragOver, true);
|
|
7295
8624
|
}
|
|
7296
8625
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7297
8626
|
if (textEditable && !isInsideNavigationItem(textEditable)) {
|
|
@@ -7303,7 +8632,7 @@ function OhhwellsBridge() {
|
|
|
7303
8632
|
hoveredImageRef.current = null;
|
|
7304
8633
|
hoveredImageHasTextOverlapRef.current = false;
|
|
7305
8634
|
resumeAnimTracks();
|
|
7306
|
-
|
|
8635
|
+
clearImageHover();
|
|
7307
8636
|
}
|
|
7308
8637
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7309
8638
|
if (textEditable && !isInsideNavigationItem(textEditable)) {
|
|
@@ -7315,7 +8644,7 @@ function OhhwellsBridge() {
|
|
|
7315
8644
|
if (hoveredImageRef.current) {
|
|
7316
8645
|
hoveredImageRef.current = null;
|
|
7317
8646
|
resumeAnimTracks();
|
|
7318
|
-
|
|
8647
|
+
clearImageHover();
|
|
7319
8648
|
}
|
|
7320
8649
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7321
8650
|
return;
|
|
@@ -7323,9 +8652,9 @@ function OhhwellsBridge() {
|
|
|
7323
8652
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7324
8653
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
7325
8654
|
hoveredImageRef.current = imgEl;
|
|
7326
|
-
|
|
8655
|
+
showImageHover(imgEl, isDragOver);
|
|
7327
8656
|
} else if (isDragOver) {
|
|
7328
|
-
|
|
8657
|
+
showImageHover(imgEl, true);
|
|
7329
8658
|
}
|
|
7330
8659
|
} else {
|
|
7331
8660
|
const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
|
|
@@ -7342,14 +8671,14 @@ function OhhwellsBridge() {
|
|
|
7342
8671
|
hoveredImageRef.current = null;
|
|
7343
8672
|
hoveredImageHasTextOverlapRef.current = false;
|
|
7344
8673
|
resumeAnimTracks();
|
|
7345
|
-
|
|
8674
|
+
clearImageHover();
|
|
7346
8675
|
}
|
|
7347
|
-
const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
8676
|
+
const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
7348
8677
|
const textEl = Array.from(
|
|
7349
|
-
document.querySelectorAll(
|
|
8678
|
+
document.querySelectorAll(NON_MEDIA_SELECTOR)
|
|
7350
8679
|
).find((el) => {
|
|
7351
8680
|
const er = el.getBoundingClientRect();
|
|
7352
|
-
return
|
|
8681
|
+
return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
|
|
7353
8682
|
});
|
|
7354
8683
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7355
8684
|
if (textEl && !textEl.hasAttribute("contenteditable")) {
|
|
@@ -7452,18 +8781,20 @@ function OhhwellsBridge() {
|
|
|
7452
8781
|
return;
|
|
7453
8782
|
}
|
|
7454
8783
|
e.dataTransfer.dropEffect = "copy";
|
|
7455
|
-
if (
|
|
8784
|
+
if (dragOverElRef.current !== el) {
|
|
8785
|
+
dragOverElRef.current = el;
|
|
7456
8786
|
hoveredImageRef.current = el;
|
|
7457
8787
|
const isStateCard = !!el.closest("[data-ohw-editable-state]");
|
|
7458
|
-
|
|
8788
|
+
showImageHover(el, true, isStateCard ? true : void 0);
|
|
7459
8789
|
}
|
|
7460
8790
|
};
|
|
7461
8791
|
const handleDragLeave = (e) => {
|
|
7462
8792
|
const imgEl = findImageAtPoint(e.clientX, e.clientY, false);
|
|
7463
8793
|
if (imgEl) return;
|
|
8794
|
+
dragOverElRef.current = null;
|
|
7464
8795
|
hoveredImageRef.current = null;
|
|
7465
8796
|
resumeAnimTracks();
|
|
7466
|
-
|
|
8797
|
+
clearImageHover();
|
|
7467
8798
|
};
|
|
7468
8799
|
const handleDrop = (e) => {
|
|
7469
8800
|
e.preventDefault();
|
|
@@ -7471,7 +8802,9 @@ function OhhwellsBridge() {
|
|
|
7471
8802
|
if (!el) return;
|
|
7472
8803
|
const file = e.dataTransfer?.files?.[0];
|
|
7473
8804
|
if (file) {
|
|
7474
|
-
|
|
8805
|
+
const wantsVideo = el.dataset.ohwEditable === "video";
|
|
8806
|
+
const accepted = wantsVideo ? file.type.startsWith("video/") : file.type.startsWith("image/");
|
|
8807
|
+
if (accepted) {
|
|
7475
8808
|
const key = el.dataset.ohwKey ?? "";
|
|
7476
8809
|
const { name, type: mimeType } = file;
|
|
7477
8810
|
const r2 = el.getBoundingClientRect();
|
|
@@ -7480,12 +8813,13 @@ function OhhwellsBridge() {
|
|
|
7480
8813
|
window.parent.postMessage({ type: "ow:image-drop", key, name, mimeType, buf, rect }, "*", [buf]);
|
|
7481
8814
|
});
|
|
7482
8815
|
} else {
|
|
7483
|
-
postToParentRef.current({ type: "ow:image-drop-invalid" });
|
|
8816
|
+
postToParentRef.current({ type: "ow:image-drop-invalid", expected: wantsVideo ? "video" : "image" });
|
|
7484
8817
|
}
|
|
7485
8818
|
}
|
|
8819
|
+
dragOverElRef.current = null;
|
|
7486
8820
|
hoveredImageRef.current = null;
|
|
7487
8821
|
resumeAnimTracks();
|
|
7488
|
-
|
|
8822
|
+
clearImageHover();
|
|
7489
8823
|
};
|
|
7490
8824
|
const handleImageUrl = (e) => {
|
|
7491
8825
|
if (e.data?.type !== "ow:image-url") return;
|
|
@@ -7503,7 +8837,11 @@ function OhhwellsBridge() {
|
|
|
7503
8837
|
}
|
|
7504
8838
|
});
|
|
7505
8839
|
hoveredImageRef.current = null;
|
|
7506
|
-
|
|
8840
|
+
setUploadingRects((prev) => {
|
|
8841
|
+
const entry = prev[key];
|
|
8842
|
+
if (!entry || entry.fadingOut) return prev;
|
|
8843
|
+
return { ...prev, [key]: { ...entry, fadingOut: true } };
|
|
8844
|
+
});
|
|
7507
8845
|
};
|
|
7508
8846
|
let found = false;
|
|
7509
8847
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -7538,6 +8876,19 @@ function OhhwellsBridge() {
|
|
|
7538
8876
|
requestAnimationFrame(() => onReady());
|
|
7539
8877
|
}
|
|
7540
8878
|
}
|
|
8879
|
+
} else if (el.dataset.ohwEditable === "video") {
|
|
8880
|
+
const video = getVideoEl(el);
|
|
8881
|
+
if (video) {
|
|
8882
|
+
found = true;
|
|
8883
|
+
const onReady = () => {
|
|
8884
|
+
video.onloadeddata = null;
|
|
8885
|
+
video.onerror = null;
|
|
8886
|
+
notify();
|
|
8887
|
+
};
|
|
8888
|
+
video.onloadeddata = onReady;
|
|
8889
|
+
video.onerror = onReady;
|
|
8890
|
+
applyVideoSrc(video, url);
|
|
8891
|
+
}
|
|
7541
8892
|
}
|
|
7542
8893
|
});
|
|
7543
8894
|
if (!found) notify();
|
|
@@ -7604,12 +8955,16 @@ function OhhwellsBridge() {
|
|
|
7604
8955
|
sectionsJson = val;
|
|
7605
8956
|
continue;
|
|
7606
8957
|
}
|
|
8958
|
+
if (applyVideoSettingNode(key, val)) continue;
|
|
7607
8959
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
7608
8960
|
if (el.dataset.ohwEditable === "image") {
|
|
7609
8961
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
7610
8962
|
if (img) applyEditableImageSrc(img, val);
|
|
7611
8963
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
7612
8964
|
el.style.backgroundImage = `url('${val}')`;
|
|
8965
|
+
} else if (el.dataset.ohwEditable === "video") {
|
|
8966
|
+
const video = getVideoEl(el);
|
|
8967
|
+
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
7613
8968
|
} else if (el.dataset.ohwEditable === "link") {
|
|
7614
8969
|
applyLinkHref(el, val);
|
|
7615
8970
|
} else {
|
|
@@ -7623,6 +8978,8 @@ function OhhwellsBridge() {
|
|
|
7623
8978
|
sectionsLoadedRef.current = true;
|
|
7624
8979
|
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
7625
8980
|
}
|
|
8981
|
+
editContentRef.current = { ...editContentRef.current, ...content };
|
|
8982
|
+
reconcileNavbarItemsFromContent(editContentRef.current);
|
|
7626
8983
|
enforceLinkHrefs();
|
|
7627
8984
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
7628
8985
|
};
|
|
@@ -7630,6 +8987,7 @@ function OhhwellsBridge() {
|
|
|
7630
8987
|
const handleDeactivate = (e) => {
|
|
7631
8988
|
if (e.data?.type !== "ow:deactivate") return;
|
|
7632
8989
|
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
8990
|
+
if (document.querySelector("[data-ohw-section-picker]")) return;
|
|
7633
8991
|
if (linkPopoverOpenRef.current) {
|
|
7634
8992
|
setLinkPopoverRef.current(null);
|
|
7635
8993
|
return;
|
|
@@ -7639,12 +8997,32 @@ function OhhwellsBridge() {
|
|
|
7639
8997
|
};
|
|
7640
8998
|
window.addEventListener("message", handleDeactivate);
|
|
7641
8999
|
const handleKeyDown = (e) => {
|
|
9000
|
+
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
9001
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
9002
|
+
const navAnchor = getNavigationItemAnchor(activeElRef.current);
|
|
9003
|
+
if (navAnchor) {
|
|
9004
|
+
e.preventDefault();
|
|
9005
|
+
selectAllTextInEditable(activeElRef.current);
|
|
9006
|
+
refreshActiveCommandsRef.current();
|
|
9007
|
+
return;
|
|
9008
|
+
}
|
|
9009
|
+
}
|
|
7642
9010
|
if (e.key === "Escape" && linkPopoverOpenRef.current) {
|
|
7643
9011
|
setLinkPopoverRef.current(null);
|
|
7644
9012
|
return;
|
|
7645
9013
|
}
|
|
7646
9014
|
if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
|
|
7647
|
-
|
|
9015
|
+
if (toolbarVariantRef.current === "select-frame") {
|
|
9016
|
+
deselectRef.current();
|
|
9017
|
+
return;
|
|
9018
|
+
}
|
|
9019
|
+
const parent = getNavigationSelectionParent(selectedElRef.current);
|
|
9020
|
+
if (parent) {
|
|
9021
|
+
e.preventDefault();
|
|
9022
|
+
selectFrameRef.current(parent);
|
|
9023
|
+
} else {
|
|
9024
|
+
deselectRef.current();
|
|
9025
|
+
}
|
|
7648
9026
|
return;
|
|
7649
9027
|
}
|
|
7650
9028
|
if (e.key === "Escape" && activeElRef.current) {
|
|
@@ -7702,18 +9080,18 @@ function OhhwellsBridge() {
|
|
|
7702
9080
|
if (hoveredItemElRef.current) {
|
|
7703
9081
|
setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
|
|
7704
9082
|
}
|
|
9083
|
+
if (hoveredNavContainerRef.current) {
|
|
9084
|
+
setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
|
|
9085
|
+
}
|
|
7705
9086
|
if (siblingHintElRef.current) {
|
|
7706
9087
|
setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
|
|
7707
9088
|
}
|
|
7708
9089
|
if (hoveredImageRef.current) {
|
|
7709
9090
|
const el = hoveredImageRef.current;
|
|
7710
9091
|
const r2 = el.getBoundingClientRect();
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
7715
|
-
hasTextOverlap: hoveredImageHasTextOverlapRef.current
|
|
7716
|
-
});
|
|
9092
|
+
setMediaHover(
|
|
9093
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
9094
|
+
);
|
|
7717
9095
|
}
|
|
7718
9096
|
};
|
|
7719
9097
|
const handleSave = (e) => {
|
|
@@ -7802,19 +9180,39 @@ function OhhwellsBridge() {
|
|
|
7802
9180
|
refreshActiveCommandsRef.current = handleSelectionChange;
|
|
7803
9181
|
const handleDocMouseLeave = () => {
|
|
7804
9182
|
hoveredImageRef.current = null;
|
|
9183
|
+
setMediaHover(null);
|
|
7805
9184
|
document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
|
|
7806
9185
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
7807
9186
|
activeStateElRef.current = null;
|
|
7808
9187
|
setToggleState(null);
|
|
7809
9188
|
};
|
|
7810
|
-
const
|
|
7811
|
-
if (e.data?.type !== "ow:
|
|
7812
|
-
const { key } = e.data;
|
|
9189
|
+
const handleImageUploading = (e) => {
|
|
9190
|
+
if (e.data?.type !== "ow:image-uploading") return;
|
|
9191
|
+
const { key, uploading } = e.data;
|
|
9192
|
+
setUploadingRects((prev) => {
|
|
9193
|
+
if (!uploading) {
|
|
9194
|
+
if (!(key in prev)) return prev;
|
|
9195
|
+
const next = { ...prev };
|
|
9196
|
+
delete next[key];
|
|
9197
|
+
return next;
|
|
9198
|
+
}
|
|
9199
|
+
const el = document.querySelector(`[data-ohw-key="${key}"]`);
|
|
9200
|
+
if (!el) return prev;
|
|
9201
|
+
const r2 = getVisibleRect(el);
|
|
9202
|
+
return {
|
|
9203
|
+
...prev,
|
|
9204
|
+
[key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
|
|
9205
|
+
};
|
|
9206
|
+
});
|
|
7813
9207
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
7814
9208
|
const track = el.closest("[data-ohw-hover-pause]");
|
|
7815
|
-
if (track)
|
|
9209
|
+
if (!track) return;
|
|
9210
|
+
if (uploading) {
|
|
7816
9211
|
uploadLockedTracks.add(track);
|
|
7817
9212
|
track.setAttribute("data-ohw-hover-paused", "");
|
|
9213
|
+
} else {
|
|
9214
|
+
uploadLockedTracks.delete(track);
|
|
9215
|
+
track.removeAttribute("data-ohw-hover-paused");
|
|
7818
9216
|
}
|
|
7819
9217
|
});
|
|
7820
9218
|
};
|
|
@@ -7856,7 +9254,7 @@ function OhhwellsBridge() {
|
|
|
7856
9254
|
if (e.data?.type !== "ow:click-at") return;
|
|
7857
9255
|
const { clientX, clientY } = e.data;
|
|
7858
9256
|
const allImages = Array.from(
|
|
7859
|
-
document.querySelectorAll(
|
|
9257
|
+
document.querySelectorAll(MEDIA_SELECTOR)
|
|
7860
9258
|
).filter((el) => !!el.closest("[data-ohw-editable-state]"));
|
|
7861
9259
|
const stateCardImage = allImages.find((el) => {
|
|
7862
9260
|
const ownerCard = el.closest("[data-ohw-editable-state]");
|
|
@@ -7871,21 +9269,55 @@ function OhhwellsBridge() {
|
|
|
7871
9269
|
return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
|
|
7872
9270
|
});
|
|
7873
9271
|
if (stateCardImage) {
|
|
7874
|
-
postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
|
|
9272
|
+
postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
|
|
7875
9273
|
return;
|
|
7876
9274
|
}
|
|
7877
9275
|
const textEditable = Array.from(
|
|
7878
|
-
document.querySelectorAll(
|
|
7879
|
-
'[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
|
|
7880
|
-
)
|
|
9276
|
+
document.querySelectorAll(NON_MEDIA_SELECTOR)
|
|
7881
9277
|
).find((el) => {
|
|
7882
9278
|
const r2 = el.getBoundingClientRect();
|
|
7883
9279
|
return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
|
|
7884
9280
|
});
|
|
7885
9281
|
if (textEditable) {
|
|
7886
|
-
|
|
9282
|
+
const hrefCtx = getHrefKeyFromElement(textEditable);
|
|
9283
|
+
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
9284
|
+
if (navAnchor) {
|
|
9285
|
+
if (selectedElRef.current === navAnchor) {
|
|
9286
|
+
activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
|
|
9287
|
+
} else {
|
|
9288
|
+
selectRef.current(navAnchor);
|
|
9289
|
+
}
|
|
9290
|
+
return;
|
|
9291
|
+
}
|
|
9292
|
+
activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
|
|
7887
9293
|
return;
|
|
7888
9294
|
}
|
|
9295
|
+
const navContainer = document.querySelector("[data-ohw-nav-container]");
|
|
9296
|
+
if (navContainer) {
|
|
9297
|
+
const r2 = navContainer.getBoundingClientRect();
|
|
9298
|
+
const pad = 8;
|
|
9299
|
+
const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
|
|
9300
|
+
if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
|
|
9301
|
+
selectFrameRef.current(navContainer);
|
|
9302
|
+
return;
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9305
|
+
const navRoot = document.querySelector("[data-ohw-nav-root]");
|
|
9306
|
+
if (navRoot && navContainer) {
|
|
9307
|
+
const rr = navRoot.getBoundingClientRect();
|
|
9308
|
+
if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
|
|
9309
|
+
const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
|
|
9310
|
+
if (book) {
|
|
9311
|
+
const br = book.getBoundingClientRect();
|
|
9312
|
+
if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
|
|
9313
|
+
deactivateRef.current();
|
|
9314
|
+
return;
|
|
9315
|
+
}
|
|
9316
|
+
}
|
|
9317
|
+
selectFrameRef.current(navContainer);
|
|
9318
|
+
return;
|
|
9319
|
+
}
|
|
9320
|
+
}
|
|
7889
9321
|
deactivateRef.current();
|
|
7890
9322
|
};
|
|
7891
9323
|
window.addEventListener("message", handleSave);
|
|
@@ -7895,7 +9327,7 @@ function OhhwellsBridge() {
|
|
|
7895
9327
|
window.addEventListener("message", handleClearSchedulingWidget);
|
|
7896
9328
|
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
7897
9329
|
window.addEventListener("message", handleImageUrl);
|
|
7898
|
-
window.addEventListener("message",
|
|
9330
|
+
window.addEventListener("message", handleImageUploading);
|
|
7899
9331
|
window.addEventListener("message", handleCanvasHeight);
|
|
7900
9332
|
window.addEventListener("message", handleParentScroll);
|
|
7901
9333
|
window.addEventListener("message", handlePointerSync);
|
|
@@ -7907,6 +9339,7 @@ function OhhwellsBridge() {
|
|
|
7907
9339
|
};
|
|
7908
9340
|
window.addEventListener("resize", handleViewportResize, { passive: true });
|
|
7909
9341
|
document.addEventListener("click", handleClick, true);
|
|
9342
|
+
document.addEventListener("dblclick", handleDblClick, true);
|
|
7910
9343
|
document.addEventListener("paste", handlePaste, true);
|
|
7911
9344
|
document.addEventListener("input", handleInput, true);
|
|
7912
9345
|
document.addEventListener("mouseover", handleMouseOver, true);
|
|
@@ -7921,6 +9354,7 @@ function OhhwellsBridge() {
|
|
|
7921
9354
|
window.addEventListener("scroll", handleScroll, true);
|
|
7922
9355
|
return () => {
|
|
7923
9356
|
document.removeEventListener("click", handleClick, true);
|
|
9357
|
+
document.removeEventListener("dblclick", handleDblClick, true);
|
|
7924
9358
|
document.removeEventListener("paste", handlePaste, true);
|
|
7925
9359
|
document.removeEventListener("input", handleInput, true);
|
|
7926
9360
|
document.removeEventListener("mouseover", handleMouseOver, true);
|
|
@@ -7940,7 +9374,7 @@ function OhhwellsBridge() {
|
|
|
7940
9374
|
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
7941
9375
|
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
7942
9376
|
window.removeEventListener("message", handleImageUrl);
|
|
7943
|
-
window.removeEventListener("message",
|
|
9377
|
+
window.removeEventListener("message", handleImageUploading);
|
|
7944
9378
|
window.removeEventListener("message", handleCanvasHeight);
|
|
7945
9379
|
window.removeEventListener("message", handleParentScroll);
|
|
7946
9380
|
window.removeEventListener("resize", handleViewportResize);
|
|
@@ -7954,7 +9388,7 @@ function OhhwellsBridge() {
|
|
|
7954
9388
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
7955
9389
|
};
|
|
7956
9390
|
}, [isEditMode, refreshStateRules]);
|
|
7957
|
-
(0,
|
|
9391
|
+
(0, import_react8.useEffect)(() => {
|
|
7958
9392
|
const handler = (e) => {
|
|
7959
9393
|
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7960
9394
|
const insertAfterVal = e.data.insertAfter;
|
|
@@ -7970,7 +9404,7 @@ function OhhwellsBridge() {
|
|
|
7970
9404
|
window.addEventListener("message", handler);
|
|
7971
9405
|
return () => window.removeEventListener("message", handler);
|
|
7972
9406
|
}, [processConfigRequest]);
|
|
7973
|
-
(0,
|
|
9407
|
+
(0, import_react8.useEffect)(() => {
|
|
7974
9408
|
if (!isEditMode) return;
|
|
7975
9409
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
7976
9410
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -7991,27 +9425,27 @@ function OhhwellsBridge() {
|
|
|
7991
9425
|
const next = { ...prev, [pathKey]: sections };
|
|
7992
9426
|
return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
|
|
7993
9427
|
});
|
|
7994
|
-
|
|
7995
|
-
|
|
9428
|
+
postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
|
|
9429
|
+
postToParent2({ type: "ow:request-site-pages" });
|
|
7996
9430
|
}, 150);
|
|
7997
9431
|
return () => {
|
|
7998
9432
|
cancelAnimationFrame(raf);
|
|
7999
9433
|
clearTimeout(timer);
|
|
8000
9434
|
};
|
|
8001
|
-
}, [pathname, isEditMode, refreshStateRules,
|
|
8002
|
-
(0,
|
|
9435
|
+
}, [pathname, isEditMode, refreshStateRules, postToParent2]);
|
|
9436
|
+
(0, import_react8.useEffect)(() => {
|
|
8003
9437
|
scrollToHashSectionWhenReady();
|
|
8004
9438
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
8005
9439
|
window.addEventListener("hashchange", onHashChange);
|
|
8006
9440
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
8007
9441
|
}, [pathname]);
|
|
8008
|
-
const handleCommand = (0,
|
|
9442
|
+
const handleCommand = (0, import_react8.useCallback)((cmd) => {
|
|
8009
9443
|
document.execCommand(cmd, false);
|
|
8010
9444
|
activeElRef.current?.focus();
|
|
8011
9445
|
if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
|
|
8012
9446
|
refreshActiveCommandsRef.current();
|
|
8013
9447
|
}, []);
|
|
8014
|
-
const handleStateChange = (0,
|
|
9448
|
+
const handleStateChange = (0, import_react8.useCallback)((state) => {
|
|
8015
9449
|
if (!activeStateElRef.current) return;
|
|
8016
9450
|
const el = activeStateElRef.current;
|
|
8017
9451
|
if (state === "Default") {
|
|
@@ -8024,18 +9458,22 @@ function OhhwellsBridge() {
|
|
|
8024
9458
|
}
|
|
8025
9459
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
8026
9460
|
}, [deactivate]);
|
|
8027
|
-
const closeLinkPopover = (0,
|
|
8028
|
-
|
|
9461
|
+
const closeLinkPopover = (0, import_react8.useCallback)(() => {
|
|
9462
|
+
addNavAfterAnchorRef.current = null;
|
|
9463
|
+
setLinkPopover(null);
|
|
9464
|
+
}, []);
|
|
9465
|
+
const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
|
|
8029
9466
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
8030
9467
|
if (!hrefCtx) return;
|
|
8031
9468
|
bumpLinkPopoverGrace();
|
|
8032
9469
|
setLinkPopover({
|
|
8033
9470
|
key: hrefCtx.key,
|
|
8034
|
-
|
|
9471
|
+
mode: "edit",
|
|
9472
|
+
target: getLinkHref2(hrefCtx.anchor)
|
|
8035
9473
|
});
|
|
8036
9474
|
deactivate();
|
|
8037
9475
|
}, [deactivate]);
|
|
8038
|
-
const openLinkPopoverForSelected = (0,
|
|
9476
|
+
const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
|
|
8039
9477
|
const anchor = selectedElRef.current;
|
|
8040
9478
|
if (!anchor) return;
|
|
8041
9479
|
const key = anchor.getAttribute("data-ohw-href-key");
|
|
@@ -8043,51 +9481,163 @@ function OhhwellsBridge() {
|
|
|
8043
9481
|
bumpLinkPopoverGrace();
|
|
8044
9482
|
setLinkPopover({
|
|
8045
9483
|
key,
|
|
8046
|
-
|
|
9484
|
+
mode: "edit",
|
|
9485
|
+
target: getLinkHref2(anchor)
|
|
8047
9486
|
});
|
|
8048
9487
|
deselect();
|
|
8049
9488
|
}, [deselect]);
|
|
8050
|
-
const handleLinkPopoverSubmit = (0,
|
|
9489
|
+
const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
|
|
8051
9490
|
(target) => {
|
|
8052
|
-
|
|
8053
|
-
|
|
9491
|
+
const session = linkPopoverSessionRef.current;
|
|
9492
|
+
if (!session) return;
|
|
9493
|
+
if (session.intent === "add-nav") {
|
|
9494
|
+
const trimmed = target.trim();
|
|
9495
|
+
let href = trimmed;
|
|
9496
|
+
let label = "Untitled";
|
|
9497
|
+
if (trimmed) {
|
|
9498
|
+
const { pageRoute, sectionId } = parseTarget(trimmed);
|
|
9499
|
+
const page = resolvePage(pageRoute, sitePages);
|
|
9500
|
+
const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
|
|
9501
|
+
const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
|
|
9502
|
+
label = section?.label ?? page.title;
|
|
9503
|
+
href = trimmed;
|
|
9504
|
+
}
|
|
9505
|
+
const afterAnchor = addNavAfterAnchorRef.current;
|
|
9506
|
+
const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
|
|
9507
|
+
applyLinkByKey(hrefKey, href);
|
|
9508
|
+
document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
|
|
9509
|
+
el.textContent = label;
|
|
9510
|
+
});
|
|
9511
|
+
const navCount = String(index + 1);
|
|
9512
|
+
const orderJson = JSON.stringify(order);
|
|
9513
|
+
editContentRef.current = {
|
|
9514
|
+
...editContentRef.current,
|
|
9515
|
+
[hrefKey]: href,
|
|
9516
|
+
[labelKey]: label,
|
|
9517
|
+
[NAV_COUNT_KEY]: navCount,
|
|
9518
|
+
[NAV_ORDER_KEY]: orderJson
|
|
9519
|
+
};
|
|
9520
|
+
postToParent2({
|
|
9521
|
+
type: "ow:change",
|
|
9522
|
+
nodes: [
|
|
9523
|
+
{ key: hrefKey, text: href },
|
|
9524
|
+
{ key: labelKey, text: label },
|
|
9525
|
+
{ key: NAV_COUNT_KEY, text: navCount },
|
|
9526
|
+
{ key: NAV_ORDER_KEY, text: orderJson }
|
|
9527
|
+
]
|
|
9528
|
+
});
|
|
9529
|
+
postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
|
|
9530
|
+
addNavAfterAnchorRef.current = null;
|
|
9531
|
+
setLinkPopover(null);
|
|
9532
|
+
enforceLinkHrefs();
|
|
9533
|
+
requestAnimationFrame(() => {
|
|
9534
|
+
selectRef.current(anchor);
|
|
9535
|
+
});
|
|
9536
|
+
return;
|
|
9537
|
+
}
|
|
9538
|
+
const { key } = session;
|
|
8054
9539
|
applyLinkByKey(key, target);
|
|
8055
|
-
|
|
9540
|
+
postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
|
|
8056
9541
|
setLinkPopover(null);
|
|
8057
9542
|
},
|
|
8058
|
-
[
|
|
9543
|
+
[postToParent2, sitePages, sectionsByPath]
|
|
8059
9544
|
);
|
|
8060
9545
|
const showEditLink = toolbarShowEditLink;
|
|
8061
9546
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
8062
9547
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
9548
|
+
const handleMediaReplace = (0, import_react8.useCallback)(
|
|
9549
|
+
(key) => {
|
|
9550
|
+
postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
|
|
9551
|
+
},
|
|
9552
|
+
[postToParent2, mediaHover?.elementType]
|
|
9553
|
+
);
|
|
9554
|
+
const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
|
|
9555
|
+
setUploadingRects((prev) => {
|
|
9556
|
+
if (!(key in prev)) return prev;
|
|
9557
|
+
const next = { ...prev };
|
|
9558
|
+
delete next[key];
|
|
9559
|
+
return next;
|
|
9560
|
+
});
|
|
9561
|
+
}, []);
|
|
9562
|
+
const handleVideoSettingsChange = (0, import_react8.useCallback)(
|
|
9563
|
+
(key, settings) => {
|
|
9564
|
+
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
9565
|
+
const video = getVideoEl(el);
|
|
9566
|
+
if (!video) return;
|
|
9567
|
+
if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
|
|
9568
|
+
if (typeof settings.muted === "boolean") video.muted = settings.muted;
|
|
9569
|
+
syncVideoPlayback(video);
|
|
9570
|
+
postToParent2({
|
|
9571
|
+
type: "ow:change",
|
|
9572
|
+
nodes: [
|
|
9573
|
+
{ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
|
|
9574
|
+
{ key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
|
|
9575
|
+
]
|
|
9576
|
+
});
|
|
9577
|
+
setMediaHover(
|
|
9578
|
+
(prev) => prev && prev.key === key ? { ...prev, videoAutoplay: video.autoplay, videoMuted: video.muted } : prev
|
|
9579
|
+
);
|
|
9580
|
+
});
|
|
9581
|
+
},
|
|
9582
|
+
[postToParent2]
|
|
9583
|
+
);
|
|
9584
|
+
return bridgeRoot ? (0, import_react_dom3.createPortal)(
|
|
9585
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
9586
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
9587
|
+
Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
9588
|
+
MediaOverlay,
|
|
9589
|
+
{
|
|
9590
|
+
hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
|
|
9591
|
+
isUploading: true,
|
|
9592
|
+
fadingOut,
|
|
9593
|
+
onFadeOutComplete: handleMediaFadeOutComplete,
|
|
9594
|
+
onReplace: handleMediaReplace
|
|
9595
|
+
},
|
|
9596
|
+
`uploading-${key}`
|
|
9597
|
+
)),
|
|
9598
|
+
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
9599
|
+
MediaOverlay,
|
|
9600
|
+
{
|
|
9601
|
+
hover: mediaHover,
|
|
9602
|
+
isUploading: false,
|
|
9603
|
+
onReplace: handleMediaReplace,
|
|
9604
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
9605
|
+
}
|
|
9606
|
+
),
|
|
9607
|
+
siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
|
|
9608
|
+
hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
|
|
9609
|
+
toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
9610
|
+
NavbarContainerChrome,
|
|
9611
|
+
{
|
|
9612
|
+
rect: toolbarRect,
|
|
9613
|
+
onAdd: handleAddTopLevelNavItem
|
|
9614
|
+
}
|
|
9615
|
+
),
|
|
9616
|
+
hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
|
|
9617
|
+
toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8069
9618
|
ItemInteractionLayer,
|
|
8070
9619
|
{
|
|
8071
9620
|
rect: toolbarRect,
|
|
8072
9621
|
elRef: glowElRef,
|
|
8073
9622
|
state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
|
|
8074
|
-
showHandle: Boolean(reorderHrefKey),
|
|
9623
|
+
showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
|
|
8075
9624
|
dragDisabled: reorderDragDisabled,
|
|
8076
9625
|
dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
|
|
8077
9626
|
onDragHandleDragStart: handleItemDragStart,
|
|
8078
9627
|
onDragHandleDragEnd: handleItemDragEnd,
|
|
8079
|
-
toolbar:
|
|
9628
|
+
toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8080
9629
|
ItemActionToolbar,
|
|
8081
9630
|
{
|
|
8082
9631
|
onEditLink: openLinkPopoverForSelected,
|
|
8083
|
-
|
|
8084
|
-
|
|
9632
|
+
addItemDisabled: true,
|
|
9633
|
+
editLinkDisabled: false,
|
|
9634
|
+
moreDisabled: true
|
|
8085
9635
|
}
|
|
8086
|
-
)
|
|
9636
|
+
) : void 0
|
|
8087
9637
|
}
|
|
8088
9638
|
),
|
|
8089
|
-
toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0,
|
|
8090
|
-
/* @__PURE__ */ (0,
|
|
9639
|
+
toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
9640
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8091
9641
|
EditGlowChrome,
|
|
8092
9642
|
{
|
|
8093
9643
|
rect: toolbarRect,
|
|
@@ -8096,7 +9646,7 @@ function OhhwellsBridge() {
|
|
|
8096
9646
|
dragDisabled: reorderDragDisabled
|
|
8097
9647
|
}
|
|
8098
9648
|
),
|
|
8099
|
-
/* @__PURE__ */ (0,
|
|
9649
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8100
9650
|
FloatingToolbar,
|
|
8101
9651
|
{
|
|
8102
9652
|
rect: toolbarRect,
|
|
@@ -8109,7 +9659,7 @@ function OhhwellsBridge() {
|
|
|
8109
9659
|
}
|
|
8110
9660
|
)
|
|
8111
9661
|
] }),
|
|
8112
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
9662
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
8113
9663
|
"div",
|
|
8114
9664
|
{
|
|
8115
9665
|
"data-ohw-max-badge": "",
|
|
@@ -8135,7 +9685,7 @@ function OhhwellsBridge() {
|
|
|
8135
9685
|
]
|
|
8136
9686
|
}
|
|
8137
9687
|
),
|
|
8138
|
-
toggleState && /* @__PURE__ */ (0,
|
|
9688
|
+
toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8139
9689
|
StateToggle,
|
|
8140
9690
|
{
|
|
8141
9691
|
rect: toggleState.rect,
|
|
@@ -8144,15 +9694,15 @@ function OhhwellsBridge() {
|
|
|
8144
9694
|
onStateChange: handleStateChange
|
|
8145
9695
|
}
|
|
8146
9696
|
),
|
|
8147
|
-
sectionGap && /* @__PURE__ */ (0,
|
|
9697
|
+
sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
8148
9698
|
"div",
|
|
8149
9699
|
{
|
|
8150
9700
|
"data-ohw-section-insert-line": "",
|
|
8151
9701
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
8152
9702
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
8153
9703
|
children: [
|
|
8154
|
-
/* @__PURE__ */ (0,
|
|
8155
|
-
/* @__PURE__ */ (0,
|
|
9704
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
9705
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8156
9706
|
Badge,
|
|
8157
9707
|
{
|
|
8158
9708
|
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",
|
|
@@ -8165,26 +9715,52 @@ function OhhwellsBridge() {
|
|
|
8165
9715
|
children: "Add Section"
|
|
8166
9716
|
}
|
|
8167
9717
|
),
|
|
8168
|
-
/* @__PURE__ */ (0,
|
|
9718
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
8169
9719
|
]
|
|
8170
9720
|
}
|
|
8171
9721
|
),
|
|
8172
|
-
linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0,
|
|
9722
|
+
linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8173
9723
|
LinkPopover,
|
|
8174
9724
|
{
|
|
8175
9725
|
panelRef: linkPopoverPanelRef,
|
|
8176
9726
|
portalContainer: dialogPortalContainer,
|
|
8177
9727
|
open: true,
|
|
8178
|
-
mode: "edit",
|
|
9728
|
+
mode: linkPopover.mode ?? "edit",
|
|
8179
9729
|
pages: sitePages,
|
|
8180
9730
|
sections: currentSections,
|
|
8181
9731
|
sectionsByPath,
|
|
8182
9732
|
initialTarget: linkPopover.target,
|
|
9733
|
+
existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
|
|
8183
9734
|
onClose: closeLinkPopover,
|
|
8184
9735
|
onSubmit: handleLinkPopoverSubmit
|
|
8185
9736
|
},
|
|
8186
|
-
|
|
8187
|
-
) : null
|
|
9737
|
+
linkPopover.key
|
|
9738
|
+
) : null,
|
|
9739
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
9740
|
+
"div",
|
|
9741
|
+
{
|
|
9742
|
+
"data-ohw-section-insert-line": "",
|
|
9743
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
9744
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
9745
|
+
children: [
|
|
9746
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
9747
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
9748
|
+
Badge,
|
|
9749
|
+
{
|
|
9750
|
+
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",
|
|
9751
|
+
onClick: () => {
|
|
9752
|
+
window.parent.postMessage(
|
|
9753
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
9754
|
+
"*"
|
|
9755
|
+
);
|
|
9756
|
+
},
|
|
9757
|
+
children: "Add Section"
|
|
9758
|
+
}
|
|
9759
|
+
),
|
|
9760
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
9761
|
+
]
|
|
9762
|
+
}
|
|
9763
|
+
)
|
|
8188
9764
|
] }),
|
|
8189
9765
|
bridgeRoot
|
|
8190
9766
|
) : null;
|