@ohhwells/bridge 0.1.24 → 0.1.25
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 +304 -327
- package/dist/index.cjs +1289 -271
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -5
- package/dist/index.d.ts +65 -5
- package/dist/index.js +1279 -269
- package/dist/index.js.map +1 -1
- package/dist/styles.css +322 -2
- package/package.json +48 -48
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
4
|
+
import React5, { useCallback as useCallback2, useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as useState5 } from "react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
7
|
|
|
8
8
|
// src/ui/SchedulingWidget.tsx
|
|
9
|
-
import { useEffect,
|
|
9
|
+
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
10
10
|
|
|
11
11
|
// src/ui/EmailCaptureModal.tsx
|
|
12
12
|
import { useState } from "react";
|
|
@@ -164,6 +164,13 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
164
164
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
165
165
|
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
166
166
|
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
167
|
+
function getApiDomain() {
|
|
168
|
+
const h = window.location.hostname;
|
|
169
|
+
for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
|
|
170
|
+
if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
|
|
171
|
+
}
|
|
172
|
+
return h;
|
|
173
|
+
}
|
|
167
174
|
function classRunsOnDate(cls, date, type) {
|
|
168
175
|
if (type === "FIXED") {
|
|
169
176
|
const d = String(date.getDate()).padStart(2, "0");
|
|
@@ -209,10 +216,10 @@ function buildTimezoneLabel(tz) {
|
|
|
209
216
|
return tz;
|
|
210
217
|
}
|
|
211
218
|
}
|
|
212
|
-
function EmptyState({ inEditor
|
|
219
|
+
function EmptyState({ inEditor }) {
|
|
213
220
|
const handleAddSchedule = () => {
|
|
214
221
|
if (inEditor) {
|
|
215
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected"
|
|
222
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
216
223
|
}
|
|
217
224
|
};
|
|
218
225
|
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
|
|
@@ -437,21 +444,18 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
437
444
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
438
445
|
const available = cls.maxParticipants - booked;
|
|
439
446
|
const isFull = available <= 0;
|
|
440
|
-
const isPrivate = cls.maxParticipants === 1;
|
|
441
447
|
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
442
448
|
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
443
449
|
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
444
|
-
/* @__PURE__ */
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
)
|
|
454
|
-
] }),
|
|
450
|
+
/* @__PURE__ */ jsx2("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
451
|
+
/* @__PURE__ */ jsx2(
|
|
452
|
+
"span",
|
|
453
|
+
{
|
|
454
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
|
|
455
|
+
style: { borderColor: "#0885FE", color: "#0885FE" },
|
|
456
|
+
children: "GROUP"
|
|
457
|
+
}
|
|
458
|
+
),
|
|
455
459
|
/* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
456
460
|
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
457
461
|
available,
|
|
@@ -524,9 +528,7 @@ function CalendarFoldIcon() {
|
|
|
524
528
|
}
|
|
525
529
|
);
|
|
526
530
|
}
|
|
527
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter
|
|
528
|
-
const autoId = useId();
|
|
529
|
-
const insertAfter = insertAfterProp ?? autoId;
|
|
531
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
530
532
|
const [schedule, setSchedule] = useState2(null);
|
|
531
533
|
const [loading, setLoading] = useState2(true);
|
|
532
534
|
const [inEditor, setInEditor] = useState2(false);
|
|
@@ -556,99 +558,88 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
556
558
|
if (typeof window === "undefined") return;
|
|
557
559
|
const isInEditor = window.self !== window.top;
|
|
558
560
|
setInEditor(isInEditor);
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (!initialized) {
|
|
566
|
-
initialized = true;
|
|
567
|
-
setLoading(false);
|
|
568
|
-
}
|
|
569
|
-
}, 5e3) : null;
|
|
570
|
-
const handler = (e) => {
|
|
571
|
-
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
572
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
573
|
-
setSchedule(null);
|
|
574
|
-
setLoading(false);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
if (e.data?.type === "ow:switch-schedule") {
|
|
578
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
579
|
-
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
580
|
-
initialized = false;
|
|
581
|
-
setLoading(true);
|
|
582
|
-
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
586
|
-
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
587
|
-
if (initialized && switchScheduleIdRef.current === null) return;
|
|
561
|
+
if (isInEditor) {
|
|
562
|
+
const startEmpty = initialScheduleId === null;
|
|
563
|
+
if (startEmpty) setLoading(false);
|
|
564
|
+
let initialized = startEmpty;
|
|
565
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
566
|
+
if (!initialized) {
|
|
588
567
|
initialized = true;
|
|
589
|
-
if (timer) clearTimeout(timer);
|
|
590
|
-
const schedules = e.data.schedules ?? [];
|
|
591
|
-
const isSwitching = switchScheduleIdRef.current !== null;
|
|
592
|
-
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : effectiveId ? schedules.find((s) => s.id === effectiveId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
593
|
-
switchScheduleIdRef.current = null;
|
|
594
|
-
setSchedule(target);
|
|
595
568
|
setLoading(false);
|
|
596
|
-
if (notifyOnConnect && target && !isSwitching) {
|
|
597
|
-
window.parent.postMessage({
|
|
598
|
-
type: "ow:schedule-connected",
|
|
599
|
-
schedule: { id: target.id, name: target.name },
|
|
600
|
-
insertAfter
|
|
601
|
-
}, "*");
|
|
602
|
-
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
603
|
-
}
|
|
604
|
-
};
|
|
605
|
-
window.addEventListener("message", handler);
|
|
606
|
-
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
607
|
-
return () => {
|
|
608
|
-
window.removeEventListener("message", handler);
|
|
609
|
-
if (timer) clearTimeout(timer);
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
if (!effectiveId) {
|
|
613
|
-
setLoading(false);
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
;
|
|
617
|
-
(async () => {
|
|
618
|
-
try {
|
|
619
|
-
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
620
|
-
const data = await res.json();
|
|
621
|
-
setSchedule(data?.id ? data : null);
|
|
622
|
-
} catch {
|
|
623
569
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
let innerCleanup;
|
|
630
|
-
const timer = setTimeout(() => {
|
|
631
|
-
if (!done) {
|
|
632
|
-
done = true;
|
|
570
|
+
}, 5e3) : null;
|
|
571
|
+
const handler = (e) => {
|
|
572
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
573
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
574
|
+
setSchedule(null);
|
|
633
575
|
setLoading(false);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
579
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
580
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
581
|
+
initialized = false;
|
|
582
|
+
setLoading(true);
|
|
583
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
587
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
588
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
589
|
+
initialized = true;
|
|
590
|
+
if (timer) clearTimeout(timer);
|
|
591
|
+
const schedules = e.data.schedules ?? [];
|
|
592
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
593
|
+
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : initialScheduleId ? schedules.find((s) => s.id === initialScheduleId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
594
|
+
switchScheduleIdRef.current = null;
|
|
595
|
+
setSchedule(target);
|
|
596
|
+
setLoading(false);
|
|
597
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
598
|
+
window.parent.postMessage({
|
|
599
|
+
type: "ow:schedule-connected",
|
|
600
|
+
schedule: { id: target.id, name: target.name },
|
|
601
|
+
insertAfter
|
|
602
|
+
}, "*");
|
|
603
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
634
604
|
}
|
|
635
|
-
}, 2e3);
|
|
636
|
-
const configHandler = (e) => {
|
|
637
|
-
if (e.data?.type !== "ow:schedule-config" || e.data.insertAfter !== insertAfter || done) return;
|
|
638
|
-
done = true;
|
|
639
|
-
clearTimeout(timer);
|
|
640
|
-
window.removeEventListener("message", configHandler);
|
|
641
|
-
innerCleanup = loadWithId(e.data.scheduleId ?? null);
|
|
642
605
|
};
|
|
643
|
-
window.addEventListener("message",
|
|
644
|
-
window.postMessage({ type: "ow:request-
|
|
606
|
+
window.addEventListener("message", handler);
|
|
607
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
645
608
|
return () => {
|
|
646
|
-
window.removeEventListener("message",
|
|
647
|
-
clearTimeout(timer);
|
|
648
|
-
innerCleanup?.();
|
|
609
|
+
window.removeEventListener("message", handler);
|
|
610
|
+
if (timer) clearTimeout(timer);
|
|
649
611
|
};
|
|
650
612
|
}
|
|
651
|
-
|
|
613
|
+
if (initialScheduleId === null) {
|
|
614
|
+
setLoading(false);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
;
|
|
618
|
+
(async () => {
|
|
619
|
+
try {
|
|
620
|
+
if (initialScheduleId) {
|
|
621
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
622
|
+
const data = await res.json();
|
|
623
|
+
setSchedule(data?.id ? data : null);
|
|
624
|
+
} else {
|
|
625
|
+
const domain = getApiDomain();
|
|
626
|
+
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
627
|
+
const slugs = await sitemapRes.json();
|
|
628
|
+
if (!Array.isArray(slugs) || !slugs.length) {
|
|
629
|
+
setLoading(false);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const { slug } = [...slugs].sort(
|
|
633
|
+
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
634
|
+
)[0];
|
|
635
|
+
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
636
|
+
const data = await res.json();
|
|
637
|
+
setSchedule(data?.id ? data : null);
|
|
638
|
+
}
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
setLoading(false);
|
|
642
|
+
})();
|
|
652
643
|
}, []);
|
|
653
644
|
const timezoneLabel = schedule?.timezone ? (() => {
|
|
654
645
|
try {
|
|
@@ -683,7 +674,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
683
674
|
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
684
675
|
}
|
|
685
676
|
};
|
|
686
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
687
677
|
const sectionId = `scheduling-${insertAfter}`;
|
|
688
678
|
return /* @__PURE__ */ jsxs2(
|
|
689
679
|
"section",
|
|
@@ -729,7 +719,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
729
719
|
] }),
|
|
730
720
|
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
731
721
|
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
732
|
-
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor
|
|
722
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
|
|
733
723
|
ScheduleView,
|
|
734
724
|
{
|
|
735
725
|
schedule,
|
|
@@ -4185,8 +4175,844 @@ function isEditSessionActive() {
|
|
|
4185
4175
|
return new URLSearchParams(q).get("mode") === "edit";
|
|
4186
4176
|
}
|
|
4187
4177
|
|
|
4188
|
-
// src/ui/
|
|
4178
|
+
// src/ui/link-modal/calcPopoverPos.ts
|
|
4179
|
+
function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
|
|
4180
|
+
const GAP = 8;
|
|
4181
|
+
const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
|
|
4182
|
+
const visibleTop = rect.top - canvasTopInIframe;
|
|
4183
|
+
let top = rect.top - GAP;
|
|
4184
|
+
let transform = "translateX(-50%) translateY(-100%)";
|
|
4185
|
+
if (visibleTop < approxH + GAP) {
|
|
4186
|
+
top = rect.bottom + GAP;
|
|
4187
|
+
transform = "translateX(-50%)";
|
|
4188
|
+
}
|
|
4189
|
+
const rawLeft = rect.left + rect.width / 2;
|
|
4190
|
+
const left = Math.max(GAP + approxW / 2, Math.min(rawLeft, window.innerWidth - GAP - approxW / 2));
|
|
4191
|
+
return { top, left, transform };
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
// src/ui/button.tsx
|
|
4195
|
+
import * as React3 from "react";
|
|
4196
|
+
import { Slot } from "radix-ui";
|
|
4189
4197
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
4198
|
+
var buttonVariants = cva(
|
|
4199
|
+
"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",
|
|
4200
|
+
{
|
|
4201
|
+
variants: {
|
|
4202
|
+
variant: {
|
|
4203
|
+
default: "bg-primary text-primary-foreground hover:opacity-90",
|
|
4204
|
+
outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
|
|
4205
|
+
ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
|
|
4206
|
+
},
|
|
4207
|
+
size: {
|
|
4208
|
+
default: "h-9",
|
|
4209
|
+
sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
|
|
4210
|
+
}
|
|
4211
|
+
},
|
|
4212
|
+
defaultVariants: {
|
|
4213
|
+
variant: "default",
|
|
4214
|
+
size: "default"
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
);
|
|
4218
|
+
var Button = React3.forwardRef(
|
|
4219
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
4220
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
4221
|
+
return /* @__PURE__ */ jsx5(
|
|
4222
|
+
Comp,
|
|
4223
|
+
{
|
|
4224
|
+
ref,
|
|
4225
|
+
"data-slot": "button",
|
|
4226
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
4227
|
+
...props
|
|
4228
|
+
}
|
|
4229
|
+
);
|
|
4230
|
+
}
|
|
4231
|
+
);
|
|
4232
|
+
Button.displayName = "Button";
|
|
4233
|
+
|
|
4234
|
+
// src/ui/icons.tsx
|
|
4235
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4236
|
+
function IconX({ className, ...props }) {
|
|
4237
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4238
|
+
/* @__PURE__ */ jsx6("path", { d: "M18 6 6 18" }),
|
|
4239
|
+
/* @__PURE__ */ jsx6("path", { d: "m6 6 12 12" })
|
|
4240
|
+
] });
|
|
4241
|
+
}
|
|
4242
|
+
function IconChevronDown({ className, ...props }) {
|
|
4243
|
+
return /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
|
|
4244
|
+
}
|
|
4245
|
+
function IconFile({ className, ...props }) {
|
|
4246
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4247
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4248
|
+
/* @__PURE__ */ jsx6("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4249
|
+
] });
|
|
4250
|
+
}
|
|
4251
|
+
function IconInfo({ className, ...props }) {
|
|
4252
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4253
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4254
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 16v-4" }),
|
|
4255
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 8h.01" })
|
|
4256
|
+
] });
|
|
4257
|
+
}
|
|
4258
|
+
function IconArrowRight({ className, ...props }) {
|
|
4259
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4260
|
+
/* @__PURE__ */ jsx6("path", { d: "M5 12h14" }),
|
|
4261
|
+
/* @__PURE__ */ jsx6("path", { d: "m12 5 7 7-7 7" })
|
|
4262
|
+
] });
|
|
4263
|
+
}
|
|
4264
|
+
function IconSection({ className, ...props }) {
|
|
4265
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4266
|
+
/* @__PURE__ */ jsx6("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4267
|
+
/* @__PURE__ */ jsx6("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4268
|
+
/* @__PURE__ */ jsx6("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4269
|
+
] });
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
4273
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4274
|
+
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
4275
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2", children: [
|
|
4276
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
4277
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
4278
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
4279
|
+
/* @__PURE__ */ jsx7(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4280
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
4281
|
+
] }),
|
|
4282
|
+
/* @__PURE__ */ jsx7(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
4283
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
4284
|
+
/* @__PURE__ */ jsx7(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4285
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
4286
|
+
] })
|
|
4287
|
+
] })
|
|
4288
|
+
] });
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
// src/ui/link-modal/SectionTreeItem.tsx
|
|
4292
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4293
|
+
function SectionTreeItem({ section, onSelect, selected }) {
|
|
4294
|
+
const interactive = Boolean(onSelect);
|
|
4295
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
4296
|
+
/* @__PURE__ */ jsx8(
|
|
4297
|
+
"div",
|
|
4298
|
+
{
|
|
4299
|
+
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
4300
|
+
"aria-hidden": true
|
|
4301
|
+
}
|
|
4302
|
+
),
|
|
4303
|
+
/* @__PURE__ */ jsxs5(
|
|
4304
|
+
"div",
|
|
4305
|
+
{
|
|
4306
|
+
role: interactive ? "button" : void 0,
|
|
4307
|
+
tabIndex: interactive ? 0 : void 0,
|
|
4308
|
+
onClick: interactive ? () => onSelect?.(section) : void 0,
|
|
4309
|
+
onKeyDown: interactive ? (e) => {
|
|
4310
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4311
|
+
e.preventDefault();
|
|
4312
|
+
onSelect?.(section);
|
|
4313
|
+
}
|
|
4314
|
+
} : void 0,
|
|
4315
|
+
className: cn(
|
|
4316
|
+
"flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
|
|
4317
|
+
interactive && "cursor-pointer hover:bg-muted/30",
|
|
4318
|
+
interactive && selected && "border-primary"
|
|
4319
|
+
),
|
|
4320
|
+
children: [
|
|
4321
|
+
/* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4322
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
4323
|
+
]
|
|
4324
|
+
}
|
|
4325
|
+
)
|
|
4326
|
+
] });
|
|
4327
|
+
}
|
|
4328
|
+
function SectionPickerList({ sections, onSelect }) {
|
|
4329
|
+
if (sections.length === 0) {
|
|
4330
|
+
return /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
4331
|
+
}
|
|
4332
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
4333
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
4334
|
+
sections.map((section) => /* @__PURE__ */ jsx8(SectionTreeItem, { section, onSelect }, section.id))
|
|
4335
|
+
] });
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4339
|
+
import { useEffect as useEffect2, useId, useRef as useRef2, useState as useState3 } from "react";
|
|
4340
|
+
|
|
4341
|
+
// src/ui/input.tsx
|
|
4342
|
+
import * as React4 from "react";
|
|
4343
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4344
|
+
var Input = React4.forwardRef(
|
|
4345
|
+
({ className, type, ...props }, ref) => {
|
|
4346
|
+
return /* @__PURE__ */ jsx9(
|
|
4347
|
+
"input",
|
|
4348
|
+
{
|
|
4349
|
+
type,
|
|
4350
|
+
ref,
|
|
4351
|
+
"data-slot": "input",
|
|
4352
|
+
className: cn(
|
|
4353
|
+
"flex h-full w-full min-w-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground",
|
|
4354
|
+
className
|
|
4355
|
+
),
|
|
4356
|
+
...props
|
|
4357
|
+
}
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
);
|
|
4361
|
+
Input.displayName = "Input";
|
|
4362
|
+
|
|
4363
|
+
// src/ui/label.tsx
|
|
4364
|
+
import { Label as LabelPrimitive } from "radix-ui";
|
|
4365
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4366
|
+
function Label({ className, ...props }) {
|
|
4367
|
+
return /* @__PURE__ */ jsx10(
|
|
4368
|
+
LabelPrimitive.Root,
|
|
4369
|
+
{
|
|
4370
|
+
"data-slot": "label",
|
|
4371
|
+
className: cn("text-sm font-medium leading-5 text-foreground", className),
|
|
4372
|
+
...props
|
|
4373
|
+
}
|
|
4374
|
+
);
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
// src/ui/popover.tsx
|
|
4378
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
4379
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
4380
|
+
function Popover({ ...props }) {
|
|
4381
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
4382
|
+
}
|
|
4383
|
+
function PopoverTrigger({ ...props }) {
|
|
4384
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4385
|
+
}
|
|
4386
|
+
function PopoverContent({
|
|
4387
|
+
className,
|
|
4388
|
+
align = "start",
|
|
4389
|
+
sideOffset = 6,
|
|
4390
|
+
...props
|
|
4391
|
+
}) {
|
|
4392
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
|
|
4393
|
+
PopoverPrimitive.Content,
|
|
4394
|
+
{
|
|
4395
|
+
"data-slot": "popover-content",
|
|
4396
|
+
align,
|
|
4397
|
+
sideOffset,
|
|
4398
|
+
className: cn(
|
|
4399
|
+
"z-[2147483647] w-[var(--radix-popover-trigger-width)] overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg outline-none",
|
|
4400
|
+
className
|
|
4401
|
+
),
|
|
4402
|
+
...props
|
|
4403
|
+
}
|
|
4404
|
+
) });
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4408
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4409
|
+
function FieldChevron({ onClick }) {
|
|
4410
|
+
return /* @__PURE__ */ jsx12(
|
|
4411
|
+
"button",
|
|
4412
|
+
{
|
|
4413
|
+
type: "button",
|
|
4414
|
+
className: "flex shrink-0 items-center justify-end border-0 bg-transparent p-0 pl-4 text-muted-foreground outline-none",
|
|
4415
|
+
onClick,
|
|
4416
|
+
"aria-label": "Open page list",
|
|
4417
|
+
tabIndex: -1,
|
|
4418
|
+
children: /* @__PURE__ */ jsx12(IconChevronDown, {})
|
|
4419
|
+
}
|
|
4420
|
+
);
|
|
4421
|
+
}
|
|
4422
|
+
function UrlOrPageInput({
|
|
4423
|
+
value,
|
|
4424
|
+
selectedPage,
|
|
4425
|
+
placeholder = "Paste a URL or choose a page",
|
|
4426
|
+
dropdownOpen,
|
|
4427
|
+
onDropdownOpenChange,
|
|
4428
|
+
filteredPages,
|
|
4429
|
+
onInputChange,
|
|
4430
|
+
onPageSelect,
|
|
4431
|
+
readOnly = false,
|
|
4432
|
+
urlError
|
|
4433
|
+
}) {
|
|
4434
|
+
const inputId = useId();
|
|
4435
|
+
const inputRef = useRef2(null);
|
|
4436
|
+
const [isEditing, setIsEditing] = useState3(false);
|
|
4437
|
+
const [isFocused, setIsFocused] = useState3(false);
|
|
4438
|
+
const showFilledDisplay = Boolean(selectedPage) && !isEditing && !readOnly;
|
|
4439
|
+
useEffect2(() => {
|
|
4440
|
+
if (selectedPage) setIsEditing(false);
|
|
4441
|
+
}, [selectedPage]);
|
|
4442
|
+
const openDropdown = () => {
|
|
4443
|
+
if (readOnly || filteredPages.length === 0) return;
|
|
4444
|
+
onDropdownOpenChange(true);
|
|
4445
|
+
};
|
|
4446
|
+
const toggleDropdown = (e) => {
|
|
4447
|
+
e.stopPropagation();
|
|
4448
|
+
e.preventDefault();
|
|
4449
|
+
if (readOnly || filteredPages.length === 0) return;
|
|
4450
|
+
onDropdownOpenChange(!dropdownOpen);
|
|
4451
|
+
};
|
|
4452
|
+
const enterEditMode = () => {
|
|
4453
|
+
if (readOnly) return;
|
|
4454
|
+
setIsEditing(true);
|
|
4455
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4456
|
+
};
|
|
4457
|
+
const fieldClassName = cn(
|
|
4458
|
+
"data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
|
|
4459
|
+
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
4460
|
+
);
|
|
4461
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4462
|
+
/* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4463
|
+
/* @__PURE__ */ jsxs6(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
|
|
4464
|
+
/* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: showFilledDisplay ? /* @__PURE__ */ jsxs6(
|
|
4465
|
+
"button",
|
|
4466
|
+
{
|
|
4467
|
+
type: "button",
|
|
4468
|
+
"data-ohw-link-field": true,
|
|
4469
|
+
className: cn(fieldClassName, "m-0 cursor-pointer border-solid text-left"),
|
|
4470
|
+
onClick: openDropdown,
|
|
4471
|
+
onFocus: () => setIsFocused(true),
|
|
4472
|
+
onBlur: () => setIsFocused(false),
|
|
4473
|
+
onKeyDown: (e) => {
|
|
4474
|
+
if (e.key === "Backspace" || e.key === "Delete" || e.key.length === 1) {
|
|
4475
|
+
enterEditMode();
|
|
4476
|
+
}
|
|
4477
|
+
},
|
|
4478
|
+
children: [
|
|
4479
|
+
/* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }),
|
|
4480
|
+
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm font-normal text-foreground", children: selectedPage.title }),
|
|
4481
|
+
/* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown })
|
|
4482
|
+
]
|
|
4483
|
+
}
|
|
4484
|
+
) : /* @__PURE__ */ jsxs6(
|
|
4485
|
+
"div",
|
|
4486
|
+
{
|
|
4487
|
+
"data-ohw-link-field": true,
|
|
4488
|
+
className: fieldClassName,
|
|
4489
|
+
onMouseDown: (e) => {
|
|
4490
|
+
if (readOnly) return;
|
|
4491
|
+
if (e.target === e.currentTarget) enterEditMode();
|
|
4492
|
+
},
|
|
4493
|
+
children: [
|
|
4494
|
+
selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4495
|
+
readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
|
|
4496
|
+
Input,
|
|
4497
|
+
{
|
|
4498
|
+
ref: inputRef,
|
|
4499
|
+
id: inputId,
|
|
4500
|
+
value,
|
|
4501
|
+
onChange: (e) => onInputChange(e.target.value),
|
|
4502
|
+
onFocus: () => {
|
|
4503
|
+
setIsFocused(true);
|
|
4504
|
+
setIsEditing(true);
|
|
4505
|
+
openDropdown();
|
|
4506
|
+
},
|
|
4507
|
+
onBlur: () => setIsFocused(false),
|
|
4508
|
+
placeholder,
|
|
4509
|
+
className: cn(
|
|
4510
|
+
"min-w-0 flex-1 truncate border-0 bg-transparent p-0 text-sm font-normal leading-5 shadow-none outline-none ring-0",
|
|
4511
|
+
value ? "text-foreground" : "text-muted-foreground placeholder:text-muted-foreground"
|
|
4512
|
+
)
|
|
4513
|
+
}
|
|
4514
|
+
),
|
|
4515
|
+
!readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4516
|
+
]
|
|
4517
|
+
}
|
|
4518
|
+
) }),
|
|
4519
|
+
/* @__PURE__ */ jsx12(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs6(
|
|
4520
|
+
"button",
|
|
4521
|
+
{
|
|
4522
|
+
type: "button",
|
|
4523
|
+
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",
|
|
4524
|
+
onClick: () => onPageSelect(page),
|
|
4525
|
+
children: [
|
|
4526
|
+
/* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4527
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
|
|
4528
|
+
]
|
|
4529
|
+
},
|
|
4530
|
+
page.path
|
|
4531
|
+
)) })
|
|
4532
|
+
] }),
|
|
4533
|
+
urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4534
|
+
] });
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
// src/ui/link-modal/useLinkModalState.ts
|
|
4538
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
|
|
4539
|
+
|
|
4540
|
+
// src/ui/link-modal/linkModal.utils.ts
|
|
4541
|
+
function parseTarget(target) {
|
|
4542
|
+
if (!target) return { pageRoute: "", sectionId: null };
|
|
4543
|
+
const hashIndex = target.indexOf("#");
|
|
4544
|
+
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
4545
|
+
return {
|
|
4546
|
+
pageRoute: target.slice(0, hashIndex),
|
|
4547
|
+
sectionId: target.slice(hashIndex + 1) || null
|
|
4548
|
+
};
|
|
4549
|
+
}
|
|
4550
|
+
function normalizePath(path) {
|
|
4551
|
+
const trimmed = path.trim();
|
|
4552
|
+
if (!trimmed) return "/";
|
|
4553
|
+
try {
|
|
4554
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
4555
|
+
return new URL(trimmed).pathname || "/";
|
|
4556
|
+
}
|
|
4557
|
+
} catch {
|
|
4558
|
+
}
|
|
4559
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
4560
|
+
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
4561
|
+
return withSlash;
|
|
4562
|
+
}
|
|
4563
|
+
function findPageByPath(pages, path) {
|
|
4564
|
+
const normalized = normalizePath(path);
|
|
4565
|
+
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
4566
|
+
}
|
|
4567
|
+
function inferPageTitleFromPath(path) {
|
|
4568
|
+
const normalized = normalizePath(path);
|
|
4569
|
+
if (normalized === "/") return "Home";
|
|
4570
|
+
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
4571
|
+
if (!segment) return "Home";
|
|
4572
|
+
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
4573
|
+
}
|
|
4574
|
+
function isInternalPath(route) {
|
|
4575
|
+
const trimmed = route.trim();
|
|
4576
|
+
if (!trimmed) return false;
|
|
4577
|
+
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
4578
|
+
if (trimmed.startsWith("/")) return true;
|
|
4579
|
+
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
4580
|
+
return true;
|
|
4581
|
+
}
|
|
4582
|
+
function resolvePage(route, pages) {
|
|
4583
|
+
const found = findPageByPath(pages, route);
|
|
4584
|
+
if (found) return { path: found.path, title: found.title };
|
|
4585
|
+
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
4586
|
+
}
|
|
4587
|
+
function getSectionsForPath(sectionsByPath, pageRoute, fallback) {
|
|
4588
|
+
if (!sectionsByPath) return fallback;
|
|
4589
|
+
const normalized = normalizePath(pageRoute);
|
|
4590
|
+
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? fallback;
|
|
4591
|
+
}
|
|
4592
|
+
function buildTarget(page, section) {
|
|
4593
|
+
if (section) return `${page.path}#${section.id}`;
|
|
4594
|
+
return page.path;
|
|
4595
|
+
}
|
|
4596
|
+
function isValidUrl(urlString) {
|
|
4597
|
+
try {
|
|
4598
|
+
const url = new URL(urlString);
|
|
4599
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
4600
|
+
const hostname = url.hostname;
|
|
4601
|
+
if (!hostname || hostname.length === 0) return false;
|
|
4602
|
+
if (hostname.includes(" ")) return false;
|
|
4603
|
+
return hostname.includes(".") || hostname === "localhost";
|
|
4604
|
+
} catch {
|
|
4605
|
+
return false;
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
function normalizeUrl(value) {
|
|
4609
|
+
const trimmed = value.trim();
|
|
4610
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
4611
|
+
return `https://${trimmed}`;
|
|
4612
|
+
}
|
|
4613
|
+
function validateUrlInput(value, pages, allPages) {
|
|
4614
|
+
if (!value.trim()) return "";
|
|
4615
|
+
const filtered = pages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase()));
|
|
4616
|
+
if (filtered.length > 0) return "";
|
|
4617
|
+
const matchingPage = allPages.find((p) => p.title.toLowerCase() === value.toLowerCase());
|
|
4618
|
+
if (matchingPage) return "";
|
|
4619
|
+
if (!isValidUrl(normalizeUrl(value))) {
|
|
4620
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4621
|
+
}
|
|
4622
|
+
return "";
|
|
4623
|
+
}
|
|
4624
|
+
function getEditModeInitialState(target, pages, sections) {
|
|
4625
|
+
const empty = {
|
|
4626
|
+
searchValue: "",
|
|
4627
|
+
selectedPage: null,
|
|
4628
|
+
selectedSection: null,
|
|
4629
|
+
step: "input"
|
|
4630
|
+
};
|
|
4631
|
+
if (!target) return empty;
|
|
4632
|
+
if (isValidUrl(normalizeUrl(target))) {
|
|
4633
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4634
|
+
}
|
|
4635
|
+
const { pageRoute, sectionId } = parseTarget(target);
|
|
4636
|
+
if (sectionId) {
|
|
4637
|
+
if (isInternalPath(pageRoute)) {
|
|
4638
|
+
const page = resolvePage(pageRoute, pages);
|
|
4639
|
+
const sec = sections.find((s) => s.id === sectionId);
|
|
4640
|
+
return {
|
|
4641
|
+
searchValue: page.title,
|
|
4642
|
+
selectedPage: page,
|
|
4643
|
+
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
4644
|
+
step: "input"
|
|
4645
|
+
};
|
|
4646
|
+
}
|
|
4647
|
+
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4648
|
+
}
|
|
4649
|
+
if (isInternalPath(pageRoute)) {
|
|
4650
|
+
const page = resolvePage(pageRoute, pages);
|
|
4651
|
+
return {
|
|
4652
|
+
searchValue: page.title,
|
|
4653
|
+
selectedPage: page,
|
|
4654
|
+
selectedSection: null,
|
|
4655
|
+
step: "input"
|
|
4656
|
+
};
|
|
4657
|
+
}
|
|
4658
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4659
|
+
}
|
|
4660
|
+
function filterAvailablePages(pages, existingTargets) {
|
|
4661
|
+
const taken = new Set(existingTargets);
|
|
4662
|
+
return pages.filter((p) => !taken.has(p.path));
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4665
|
+
// src/ui/link-modal/useLinkModalState.ts
|
|
4666
|
+
function useLinkModalState({
|
|
4667
|
+
open,
|
|
4668
|
+
mode,
|
|
4669
|
+
pages,
|
|
4670
|
+
sections,
|
|
4671
|
+
sectionsByPath,
|
|
4672
|
+
initialTarget,
|
|
4673
|
+
existingTargets,
|
|
4674
|
+
onClose,
|
|
4675
|
+
onSubmit
|
|
4676
|
+
}) {
|
|
4677
|
+
const availablePages = useMemo2(
|
|
4678
|
+
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4679
|
+
[mode, pages, existingTargets]
|
|
4680
|
+
);
|
|
4681
|
+
const [searchValue, setSearchValue] = useState4("");
|
|
4682
|
+
const [selectedPage, setSelectedPage] = useState4(null);
|
|
4683
|
+
const [selectedSection, setSelectedSection] = useState4(null);
|
|
4684
|
+
const [step, setStep] = useState4("input");
|
|
4685
|
+
const [dropdownOpen, setDropdownOpen] = useState4(false);
|
|
4686
|
+
const [urlError, setUrlError] = useState4("");
|
|
4687
|
+
const reset = useCallback(() => {
|
|
4688
|
+
setSearchValue("");
|
|
4689
|
+
setSelectedPage(null);
|
|
4690
|
+
setSelectedSection(null);
|
|
4691
|
+
setStep("input");
|
|
4692
|
+
setDropdownOpen(false);
|
|
4693
|
+
setUrlError("");
|
|
4694
|
+
}, []);
|
|
4695
|
+
useEffect3(() => {
|
|
4696
|
+
if (!open) return;
|
|
4697
|
+
if (mode === "edit" && initialTarget) {
|
|
4698
|
+
const { pageRoute } = parseTarget(initialTarget);
|
|
4699
|
+
const targetSections = getSectionsForPath(sectionsByPath, pageRoute, sections);
|
|
4700
|
+
const init = getEditModeInitialState(initialTarget, pages, targetSections);
|
|
4701
|
+
setSearchValue(init.searchValue);
|
|
4702
|
+
setSelectedPage(init.selectedPage);
|
|
4703
|
+
setSelectedSection(init.selectedSection);
|
|
4704
|
+
setStep(init.step);
|
|
4705
|
+
} else {
|
|
4706
|
+
reset();
|
|
4707
|
+
}
|
|
4708
|
+
setDropdownOpen(false);
|
|
4709
|
+
setUrlError("");
|
|
4710
|
+
}, [open, mode, initialTarget, pages, sections, sectionsByPath, reset]);
|
|
4711
|
+
const filteredPages = useMemo2(() => {
|
|
4712
|
+
if (!searchValue.trim()) return availablePages;
|
|
4713
|
+
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4714
|
+
}, [availablePages, searchValue]);
|
|
4715
|
+
const activeSections = useMemo2(() => {
|
|
4716
|
+
if (selectedPage && sectionsByPath) {
|
|
4717
|
+
return getSectionsForPath(sectionsByPath, selectedPage.path, sections);
|
|
4718
|
+
}
|
|
4719
|
+
return sections;
|
|
4720
|
+
}, [selectedPage, sectionsByPath, sections]);
|
|
4721
|
+
const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
|
|
4722
|
+
const showSectionPicker = step === "sectionPicker" && selectedPage;
|
|
4723
|
+
const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
|
|
4724
|
+
const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow;
|
|
4725
|
+
const handleInputChange = (value) => {
|
|
4726
|
+
setSearchValue(value);
|
|
4727
|
+
setSelectedPage(null);
|
|
4728
|
+
setSelectedSection(null);
|
|
4729
|
+
setStep("input");
|
|
4730
|
+
setUrlError("");
|
|
4731
|
+
const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
|
|
4732
|
+
setDropdownOpen(filtered.length > 0);
|
|
4733
|
+
if (value.trim() && filtered.length === 0) {
|
|
4734
|
+
const error = validateUrlInput(value, availablePages, pages);
|
|
4735
|
+
if (error) setUrlError(error);
|
|
4736
|
+
}
|
|
4737
|
+
};
|
|
4738
|
+
const handlePageSelect = (page) => {
|
|
4739
|
+
setSelectedPage({ path: page.path, title: page.title });
|
|
4740
|
+
setSelectedSection(null);
|
|
4741
|
+
setSearchValue(page.title);
|
|
4742
|
+
setStep("input");
|
|
4743
|
+
setDropdownOpen(false);
|
|
4744
|
+
setUrlError("");
|
|
4745
|
+
};
|
|
4746
|
+
const handleChooseSection = () => {
|
|
4747
|
+
setStep("sectionPicker");
|
|
4748
|
+
};
|
|
4749
|
+
const handleSectionSelect = (section) => {
|
|
4750
|
+
setSelectedSection({ id: section.id, label: section.label });
|
|
4751
|
+
if (mode === "create") {
|
|
4752
|
+
setStep("confirmed");
|
|
4753
|
+
} else {
|
|
4754
|
+
setStep("input");
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
const handleBackToSections = () => {
|
|
4758
|
+
setStep("sectionPicker");
|
|
4759
|
+
};
|
|
4760
|
+
const handleClose = () => {
|
|
4761
|
+
reset();
|
|
4762
|
+
onClose();
|
|
4763
|
+
};
|
|
4764
|
+
const isValid = useMemo2(() => {
|
|
4765
|
+
if (urlError) return false;
|
|
4766
|
+
if (showBreadcrumb) return true;
|
|
4767
|
+
if (selectedPage) return true;
|
|
4768
|
+
if (!searchValue.trim()) return false;
|
|
4769
|
+
return validateUrlInput(searchValue, availablePages, pages) === "";
|
|
4770
|
+
}, [urlError, showBreadcrumb, selectedPage, searchValue, availablePages, pages]);
|
|
4771
|
+
const handleSubmit = () => {
|
|
4772
|
+
if (selectedPage && selectedSection) {
|
|
4773
|
+
onSubmit(buildTarget(selectedPage, selectedSection));
|
|
4774
|
+
handleClose();
|
|
4775
|
+
return;
|
|
4776
|
+
}
|
|
4777
|
+
if (selectedPage) {
|
|
4778
|
+
onSubmit(buildTarget(selectedPage, null));
|
|
4779
|
+
handleClose();
|
|
4780
|
+
return;
|
|
4781
|
+
}
|
|
4782
|
+
if (!searchValue.trim()) return;
|
|
4783
|
+
const error = validateUrlInput(searchValue, availablePages, pages);
|
|
4784
|
+
if (error) {
|
|
4785
|
+
setUrlError(error);
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
const matchingPage = pages.find((p) => p.title.toLowerCase() === searchValue.toLowerCase());
|
|
4789
|
+
if (matchingPage) {
|
|
4790
|
+
onSubmit(matchingPage.path);
|
|
4791
|
+
handleClose();
|
|
4792
|
+
return;
|
|
4793
|
+
}
|
|
4794
|
+
onSubmit(normalizeUrl(searchValue));
|
|
4795
|
+
handleClose();
|
|
4796
|
+
};
|
|
4797
|
+
const submitLabel = mode === "edit" ? "Save" : "Create";
|
|
4798
|
+
const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
|
|
4799
|
+
const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
|
|
4800
|
+
const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
|
|
4801
|
+
return {
|
|
4802
|
+
title,
|
|
4803
|
+
submitLabel,
|
|
4804
|
+
secondaryLabel,
|
|
4805
|
+
searchValue,
|
|
4806
|
+
selectedPage,
|
|
4807
|
+
selectedSection,
|
|
4808
|
+
step,
|
|
4809
|
+
dropdownOpen,
|
|
4810
|
+
setDropdownOpen,
|
|
4811
|
+
urlError,
|
|
4812
|
+
filteredPages,
|
|
4813
|
+
showBreadcrumb,
|
|
4814
|
+
showSectionPicker,
|
|
4815
|
+
showChooseSection,
|
|
4816
|
+
showSectionRow,
|
|
4817
|
+
isValid,
|
|
4818
|
+
activeSections,
|
|
4819
|
+
handleInputChange,
|
|
4820
|
+
handlePageSelect,
|
|
4821
|
+
handleChooseSection,
|
|
4822
|
+
handleSectionSelect,
|
|
4823
|
+
handleBackToSections,
|
|
4824
|
+
handleClose,
|
|
4825
|
+
handleSecondary,
|
|
4826
|
+
handleSubmit
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4831
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4832
|
+
function LinkEditorPanel({
|
|
4833
|
+
open = true,
|
|
4834
|
+
mode = "create",
|
|
4835
|
+
pages,
|
|
4836
|
+
sections = [],
|
|
4837
|
+
sectionsByPath,
|
|
4838
|
+
initialTarget,
|
|
4839
|
+
existingTargets = [],
|
|
4840
|
+
onClose,
|
|
4841
|
+
onSubmit
|
|
4842
|
+
}) {
|
|
4843
|
+
const state = useLinkModalState({
|
|
4844
|
+
open,
|
|
4845
|
+
mode,
|
|
4846
|
+
pages,
|
|
4847
|
+
sections,
|
|
4848
|
+
sectionsByPath,
|
|
4849
|
+
initialTarget,
|
|
4850
|
+
existingTargets,
|
|
4851
|
+
onClose,
|
|
4852
|
+
onSubmit
|
|
4853
|
+
});
|
|
4854
|
+
const isCancel = state.secondaryLabel === "Cancel";
|
|
4855
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
4856
|
+
/* @__PURE__ */ jsx13(
|
|
4857
|
+
"button",
|
|
4858
|
+
{
|
|
4859
|
+
type: "button",
|
|
4860
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4861
|
+
onClick: state.handleClose,
|
|
4862
|
+
"aria-label": "Close",
|
|
4863
|
+
children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
|
|
4864
|
+
}
|
|
4865
|
+
),
|
|
4866
|
+
/* @__PURE__ */ jsx13("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
|
|
4867
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
4868
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
|
|
4869
|
+
DestinationBreadcrumb,
|
|
4870
|
+
{
|
|
4871
|
+
pageTitle: state.selectedPage.title,
|
|
4872
|
+
sectionLabel: state.selectedSection.label
|
|
4873
|
+
}
|
|
4874
|
+
) : /* @__PURE__ */ jsx13(
|
|
4875
|
+
UrlOrPageInput,
|
|
4876
|
+
{
|
|
4877
|
+
value: state.searchValue,
|
|
4878
|
+
selectedPage: state.selectedPage,
|
|
4879
|
+
dropdownOpen: state.dropdownOpen,
|
|
4880
|
+
onDropdownOpenChange: state.setDropdownOpen,
|
|
4881
|
+
filteredPages: state.filteredPages,
|
|
4882
|
+
onInputChange: state.handleInputChange,
|
|
4883
|
+
onPageSelect: state.handlePageSelect,
|
|
4884
|
+
urlError: state.urlError
|
|
4885
|
+
}
|
|
4886
|
+
),
|
|
4887
|
+
state.showChooseSection ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
4888
|
+
/* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
4889
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
4890
|
+
/* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
4891
|
+
/* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
|
|
4892
|
+
] })
|
|
4893
|
+
] }) : null,
|
|
4894
|
+
state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
4895
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4896
|
+
] }),
|
|
4897
|
+
/* @__PURE__ */ jsxs7("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
|
|
4898
|
+
/* @__PURE__ */ jsx13(
|
|
4899
|
+
Button,
|
|
4900
|
+
{
|
|
4901
|
+
type: "button",
|
|
4902
|
+
variant: isCancel ? "outline" : "ghost",
|
|
4903
|
+
className: "leading-6 shadow-none cursor-pointer",
|
|
4904
|
+
style: isCancel ? {
|
|
4905
|
+
backgroundColor: "var(--ohw-background)",
|
|
4906
|
+
borderColor: "var(--ohw-border)",
|
|
4907
|
+
color: "var(--ohw-foreground)"
|
|
4908
|
+
} : void 0,
|
|
4909
|
+
onClick: state.handleSecondary,
|
|
4910
|
+
children: state.secondaryLabel
|
|
4911
|
+
}
|
|
4912
|
+
),
|
|
4913
|
+
/* @__PURE__ */ jsx13(
|
|
4914
|
+
Button,
|
|
4915
|
+
{
|
|
4916
|
+
type: "button",
|
|
4917
|
+
className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
|
|
4918
|
+
style: {
|
|
4919
|
+
backgroundColor: "var(--ohw-primary)",
|
|
4920
|
+
color: "var(--ohw-primary-foreground)"
|
|
4921
|
+
},
|
|
4922
|
+
disabled: !state.isValid,
|
|
4923
|
+
onClick: state.handleSubmit,
|
|
4924
|
+
children: state.submitLabel
|
|
4925
|
+
}
|
|
4926
|
+
)
|
|
4927
|
+
] })
|
|
4928
|
+
] });
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
// src/ui/link-modal/LinkPopover.tsx
|
|
4932
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4933
|
+
function LinkPopover({
|
|
4934
|
+
rect,
|
|
4935
|
+
parentScroll,
|
|
4936
|
+
panelRef,
|
|
4937
|
+
...editorProps
|
|
4938
|
+
}) {
|
|
4939
|
+
const { top, left, transform } = calcPopoverPos(rect, parentScroll);
|
|
4940
|
+
return /* @__PURE__ */ jsx14(
|
|
4941
|
+
"div",
|
|
4942
|
+
{
|
|
4943
|
+
ref: panelRef,
|
|
4944
|
+
"data-ohw-link-popover-root": "",
|
|
4945
|
+
"data-ohw-link-modal-root": "",
|
|
4946
|
+
"data-ohw-bridge": "",
|
|
4947
|
+
className: cn(
|
|
4948
|
+
"pointer-events-auto fixed z-[2147483647] flex w-[483px] max-w-[calc(100vw-16px)] flex-col overflow-hidden",
|
|
4949
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none"
|
|
4950
|
+
),
|
|
4951
|
+
style: { top, left, transform },
|
|
4952
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4953
|
+
children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open: true })
|
|
4954
|
+
}
|
|
4955
|
+
);
|
|
4956
|
+
}
|
|
4957
|
+
|
|
4958
|
+
// src/ui/link-modal/devFixtures.ts
|
|
4959
|
+
var DEV_SITE_PAGES = [
|
|
4960
|
+
{ path: "/", title: "Home" },
|
|
4961
|
+
{ path: "/about", title: "About" },
|
|
4962
|
+
{ path: "/classes", title: "Classes" },
|
|
4963
|
+
{ path: "/pricing", title: "Pricing" },
|
|
4964
|
+
{ path: "/studio", title: "Studio" },
|
|
4965
|
+
{ path: "/instructors", title: "Instructors" },
|
|
4966
|
+
{ path: "/contact", title: "Contact" }
|
|
4967
|
+
];
|
|
4968
|
+
var DEV_SECTIONS_BY_PATH = {
|
|
4969
|
+
"/": [
|
|
4970
|
+
{ id: "hero", label: "Hero" },
|
|
4971
|
+
{ id: "lagree-intro", label: "Lagree Intro" },
|
|
4972
|
+
{ id: "classes-strip", label: "Classes" },
|
|
4973
|
+
{ id: "feel-split", label: "Feel Split" },
|
|
4974
|
+
{ id: "founder-teaser", label: "Founder" },
|
|
4975
|
+
{ id: "plan-form", label: "Plan Form" },
|
|
4976
|
+
{ id: "testimonials", label: "Testimonials" },
|
|
4977
|
+
{ id: "wordmark", label: "Wordmark" }
|
|
4978
|
+
],
|
|
4979
|
+
"/about": [
|
|
4980
|
+
{ id: "manifesto", label: "Manifesto" },
|
|
4981
|
+
{ id: "story-letter", label: "Our Story" },
|
|
4982
|
+
{ id: "lagree-explainer", label: "Lagree Explainer" },
|
|
4983
|
+
{ id: "waitlist-cta", label: "Waitlist" },
|
|
4984
|
+
{ id: "personal-training", label: "Personal training" }
|
|
4985
|
+
],
|
|
4986
|
+
"/classes": [{ id: "class-library", label: "Class Library" }],
|
|
4987
|
+
"/pricing": [
|
|
4988
|
+
{ id: "benefits-marquee", label: "Benefits" },
|
|
4989
|
+
{ id: "monthly-memberships", label: "Memberships" },
|
|
4990
|
+
{ id: "package-matcher", label: "Packages" },
|
|
4991
|
+
{ id: "specialty-programs", label: "Specialty Programs" },
|
|
4992
|
+
{ id: "free-class-cta", label: "Free Class" }
|
|
4993
|
+
],
|
|
4994
|
+
"/studio": [
|
|
4995
|
+
{ id: "studio-intro", label: "Studio Intro" },
|
|
4996
|
+
{ id: "studio-features", label: "Studio Features" },
|
|
4997
|
+
{ id: "studio-gallery", label: "Studio Gallery" },
|
|
4998
|
+
{ id: "studio-visit", label: "Visit Studio" }
|
|
4999
|
+
],
|
|
5000
|
+
"/instructors": [{ id: "instructor-grid", label: "Instructors" }],
|
|
5001
|
+
"/contact": [
|
|
5002
|
+
{ id: "hello-hero", label: "Hello" },
|
|
5003
|
+
{ id: "contact-form", label: "Contact Form" },
|
|
5004
|
+
{ id: "faq", label: "FAQ" }
|
|
5005
|
+
]
|
|
5006
|
+
};
|
|
5007
|
+
function shouldUseDevFixtures() {
|
|
5008
|
+
if (typeof window === "undefined") return false;
|
|
5009
|
+
const raw = readPreservedSearch();
|
|
5010
|
+
const q = raw.startsWith("?") ? raw.slice(1) : raw;
|
|
5011
|
+
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// src/ui/badge.tsx
|
|
5015
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4190
5016
|
var badgeVariants = cva(
|
|
4191
5017
|
"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",
|
|
4192
5018
|
{
|
|
@@ -4204,11 +5030,11 @@ var badgeVariants = cva(
|
|
|
4204
5030
|
}
|
|
4205
5031
|
);
|
|
4206
5032
|
function Badge({ className, variant, ...props }) {
|
|
4207
|
-
return /* @__PURE__ */
|
|
5033
|
+
return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
4208
5034
|
}
|
|
4209
5035
|
|
|
4210
5036
|
// src/OhhwellsBridge.tsx
|
|
4211
|
-
import { Fragment as
|
|
5037
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4212
5038
|
var PRIMARY = "#0885FE";
|
|
4213
5039
|
var IMAGE_FADE_MS = 300;
|
|
4214
5040
|
function runOpacityFade(el, onDone) {
|
|
@@ -4348,14 +5174,9 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
4348
5174
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4349
5175
|
if (inEditor) getSectionsTracker().textContent = raw;
|
|
4350
5176
|
const pageEntries = getPageSchedulingEntries(raw);
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
4355
|
-
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
4356
|
-
for (const entry of preExisting) {
|
|
4357
|
-
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4358
|
-
}
|
|
5177
|
+
mountSchedulingEntries(pageEntries, false);
|
|
5178
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
|
|
5179
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
|
|
4359
5180
|
} catch {
|
|
4360
5181
|
}
|
|
4361
5182
|
}
|
|
@@ -4382,7 +5203,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
4382
5203
|
const root = createRoot(container);
|
|
4383
5204
|
flushSync(() => {
|
|
4384
5205
|
root.render(
|
|
4385
|
-
/* @__PURE__ */
|
|
5206
|
+
/* @__PURE__ */ jsx16(
|
|
4386
5207
|
SchedulingWidget,
|
|
4387
5208
|
{
|
|
4388
5209
|
notifyOnConnect,
|
|
@@ -4414,16 +5235,23 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
4414
5235
|
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
4415
5236
|
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
4416
5237
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4417
|
-
const
|
|
4418
|
-
|
|
4419
|
-
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5238
|
+
const { insertAfter, scheduleId } = pending[i];
|
|
5239
|
+
if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
|
|
4420
5240
|
pending.splice(i, 1);
|
|
4421
5241
|
}
|
|
4422
5242
|
}
|
|
4423
5243
|
}
|
|
4424
5244
|
}
|
|
5245
|
+
function getLinkHref(el) {
|
|
5246
|
+
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5247
|
+
return anchor?.getAttribute("href") ?? "";
|
|
5248
|
+
}
|
|
5249
|
+
function applyLinkHref(el, val) {
|
|
5250
|
+
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5251
|
+
if (anchor) anchor.setAttribute("href", val);
|
|
5252
|
+
}
|
|
4425
5253
|
function collectEditableNodes() {
|
|
4426
|
-
|
|
5254
|
+
const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
|
|
4427
5255
|
if (el.dataset.ohwEditable === "image") {
|
|
4428
5256
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
4429
5257
|
return { key: el.dataset.ohwKey ?? "", type: "image", text: img?.src ?? "" };
|
|
@@ -4433,12 +5261,52 @@ function collectEditableNodes() {
|
|
|
4433
5261
|
const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
|
|
4434
5262
|
return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
|
|
4435
5263
|
}
|
|
5264
|
+
if (el.dataset.ohwEditable === "link") {
|
|
5265
|
+
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
|
|
5266
|
+
}
|
|
4436
5267
|
return {
|
|
4437
5268
|
key: el.dataset.ohwKey ?? "",
|
|
4438
5269
|
type: el.dataset.ohwEditable ?? "text",
|
|
4439
5270
|
text: el.dataset.ohwEditable === "plain" ? el.innerText ?? "" : el.innerHTML
|
|
4440
5271
|
};
|
|
4441
5272
|
});
|
|
5273
|
+
document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
|
|
5274
|
+
const key = el.getAttribute("data-ohw-href-key") ?? "";
|
|
5275
|
+
if (!key) return;
|
|
5276
|
+
nodes.push({ key, type: "link", text: getLinkHref(el) });
|
|
5277
|
+
});
|
|
5278
|
+
return nodes;
|
|
5279
|
+
}
|
|
5280
|
+
function applyLinkByKey(key, val) {
|
|
5281
|
+
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5282
|
+
if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
|
|
5283
|
+
});
|
|
5284
|
+
document.querySelectorAll(`[data-ohw-href-key="${key}"]`).forEach((el) => {
|
|
5285
|
+
applyLinkHref(el, val);
|
|
5286
|
+
});
|
|
5287
|
+
}
|
|
5288
|
+
function isInsideLinkEditor(target) {
|
|
5289
|
+
return Boolean(
|
|
5290
|
+
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest('[data-slot="popover-content"]')
|
|
5291
|
+
);
|
|
5292
|
+
}
|
|
5293
|
+
function getHrefKeyFromElement(el) {
|
|
5294
|
+
if (!el) return null;
|
|
5295
|
+
const anchor = el.closest("[data-ohw-href-key]");
|
|
5296
|
+
if (!anchor) return null;
|
|
5297
|
+
const key = anchor.getAttribute("data-ohw-href-key");
|
|
5298
|
+
if (!key) return null;
|
|
5299
|
+
return { anchor, key };
|
|
5300
|
+
}
|
|
5301
|
+
function titleCaseSectionId(id) {
|
|
5302
|
+
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
5303
|
+
}
|
|
5304
|
+
function collectSections() {
|
|
5305
|
+
return Array.from(document.querySelectorAll("[data-ohw-section]")).map((el) => {
|
|
5306
|
+
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
5307
|
+
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
5308
|
+
return { id, label };
|
|
5309
|
+
});
|
|
4442
5310
|
}
|
|
4443
5311
|
var FORCE_PSEUDO_STATES = [
|
|
4444
5312
|
{ pseudo: ":hover", state: "hover" },
|
|
@@ -4579,7 +5447,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4579
5447
|
];
|
|
4580
5448
|
function GlowFrame({ rect, elRef }) {
|
|
4581
5449
|
const GAP = 6;
|
|
4582
|
-
return /* @__PURE__ */
|
|
5450
|
+
return /* @__PURE__ */ jsx16(
|
|
4583
5451
|
"div",
|
|
4584
5452
|
{
|
|
4585
5453
|
ref: elRef,
|
|
@@ -4627,10 +5495,12 @@ function FloatingToolbar({
|
|
|
4627
5495
|
parentScroll,
|
|
4628
5496
|
elRef,
|
|
4629
5497
|
onCommand,
|
|
4630
|
-
activeCommands
|
|
5498
|
+
activeCommands,
|
|
5499
|
+
showEditLink,
|
|
5500
|
+
onEditLink
|
|
4631
5501
|
}) {
|
|
4632
5502
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
4633
|
-
return /* @__PURE__ */
|
|
5503
|
+
return /* @__PURE__ */ jsxs8(
|
|
4634
5504
|
"div",
|
|
4635
5505
|
{
|
|
4636
5506
|
ref: elRef,
|
|
@@ -4653,56 +5523,97 @@ function FloatingToolbar({
|
|
|
4653
5523
|
pointerEvents: "auto",
|
|
4654
5524
|
whiteSpace: "nowrap"
|
|
4655
5525
|
},
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
5526
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5527
|
+
children: [
|
|
5528
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs8(React5.Fragment, { children: [
|
|
5529
|
+
gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5530
|
+
btns.map((btn) => {
|
|
5531
|
+
const isActive = activeCommands.has(btn.cmd);
|
|
5532
|
+
return /* @__PURE__ */ jsx16(
|
|
5533
|
+
"button",
|
|
5534
|
+
{
|
|
5535
|
+
title: btn.title,
|
|
5536
|
+
onMouseDown: (e) => {
|
|
5537
|
+
e.preventDefault();
|
|
5538
|
+
onCommand(btn.cmd);
|
|
5539
|
+
},
|
|
5540
|
+
onMouseEnter: (e) => {
|
|
5541
|
+
if (!isActive) e.currentTarget.style.background = "#F5F5F4";
|
|
5542
|
+
},
|
|
5543
|
+
onMouseLeave: (e) => {
|
|
5544
|
+
if (!isActive) e.currentTarget.style.background = "transparent";
|
|
5545
|
+
},
|
|
5546
|
+
style: {
|
|
5547
|
+
display: "flex",
|
|
5548
|
+
alignItems: "center",
|
|
5549
|
+
justifyContent: "center",
|
|
5550
|
+
border: "none",
|
|
5551
|
+
background: isActive ? PRIMARY : "transparent",
|
|
5552
|
+
borderRadius: 4,
|
|
5553
|
+
cursor: "pointer",
|
|
5554
|
+
color: isActive ? "#FFFFFF" : "#1C1917",
|
|
5555
|
+
flexShrink: 0,
|
|
5556
|
+
padding: 6
|
|
5557
|
+
},
|
|
5558
|
+
children: /* @__PURE__ */ jsx16(
|
|
5559
|
+
"svg",
|
|
5560
|
+
{
|
|
5561
|
+
width: "16",
|
|
5562
|
+
height: "16",
|
|
5563
|
+
viewBox: "0 0 24 24",
|
|
5564
|
+
fill: "none",
|
|
5565
|
+
stroke: isActive ? "#FFFFFF" : "#1C1917",
|
|
5566
|
+
strokeWidth: "2.5",
|
|
5567
|
+
strokeLinecap: "round",
|
|
5568
|
+
strokeLinejoin: "round",
|
|
5569
|
+
style: isActive && gi > 0 ? { filter: "drop-shadow(0 0 0.5px #fff)" } : void 0,
|
|
5570
|
+
dangerouslySetInnerHTML: { __html: ICONS[btn.cmd] }
|
|
5571
|
+
}
|
|
5572
|
+
)
|
|
4685
5573
|
},
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
5574
|
+
btn.cmd
|
|
5575
|
+
);
|
|
5576
|
+
})
|
|
5577
|
+
] }, gi)),
|
|
5578
|
+
showEditLink ? /* @__PURE__ */ jsx16(
|
|
5579
|
+
"button",
|
|
5580
|
+
{
|
|
5581
|
+
type: "button",
|
|
5582
|
+
title: "Edit link",
|
|
5583
|
+
onMouseDown: (e) => {
|
|
5584
|
+
e.preventDefault();
|
|
5585
|
+
e.stopPropagation();
|
|
5586
|
+
onEditLink?.();
|
|
5587
|
+
},
|
|
5588
|
+
onClick: (e) => {
|
|
5589
|
+
e.preventDefault();
|
|
5590
|
+
e.stopPropagation();
|
|
5591
|
+
},
|
|
5592
|
+
onMouseEnter: (e) => {
|
|
5593
|
+
e.currentTarget.style.background = "#F5F5F4";
|
|
5594
|
+
},
|
|
5595
|
+
onMouseLeave: (e) => {
|
|
5596
|
+
e.currentTarget.style.background = "transparent";
|
|
5597
|
+
},
|
|
5598
|
+
style: {
|
|
5599
|
+
display: "flex",
|
|
5600
|
+
alignItems: "center",
|
|
5601
|
+
justifyContent: "center",
|
|
5602
|
+
border: "none",
|
|
5603
|
+
background: "transparent",
|
|
5604
|
+
borderRadius: 4,
|
|
5605
|
+
cursor: "pointer",
|
|
5606
|
+
color: "#1C1917",
|
|
5607
|
+
flexShrink: 0,
|
|
5608
|
+
padding: 6
|
|
4701
5609
|
},
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
5610
|
+
children: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
5611
|
+
/* @__PURE__ */ jsx16("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
5612
|
+
/* @__PURE__ */ jsx16("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
5613
|
+
] })
|
|
5614
|
+
}
|
|
5615
|
+
) : null
|
|
5616
|
+
]
|
|
4706
5617
|
}
|
|
4707
5618
|
);
|
|
4708
5619
|
}
|
|
@@ -4716,7 +5627,7 @@ function StateToggle({
|
|
|
4716
5627
|
states,
|
|
4717
5628
|
onStateChange
|
|
4718
5629
|
}) {
|
|
4719
|
-
return /* @__PURE__ */
|
|
5630
|
+
return /* @__PURE__ */ jsx16(
|
|
4720
5631
|
ToggleGroup,
|
|
4721
5632
|
{
|
|
4722
5633
|
"data-ohw-state-toggle": "",
|
|
@@ -4730,7 +5641,7 @@ function StateToggle({
|
|
|
4730
5641
|
left: rect.right - 8,
|
|
4731
5642
|
transform: "translateX(-100%)"
|
|
4732
5643
|
},
|
|
4733
|
-
children: states.map((state) => /* @__PURE__ */
|
|
5644
|
+
children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4734
5645
|
}
|
|
4735
5646
|
);
|
|
4736
5647
|
}
|
|
@@ -4757,8 +5668,8 @@ function OhhwellsBridge() {
|
|
|
4757
5668
|
const router = useRouter();
|
|
4758
5669
|
const searchParams = useSearchParams();
|
|
4759
5670
|
const isEditMode = isEditSessionActive();
|
|
4760
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
4761
|
-
|
|
5671
|
+
const [bridgeRoot, setBridgeRoot] = useState5(null);
|
|
5672
|
+
useEffect4(() => {
|
|
4762
5673
|
const figtreeFontId = "ohw-figtree-font";
|
|
4763
5674
|
if (!document.getElementById(figtreeFontId)) {
|
|
4764
5675
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4782,41 +5693,75 @@ function OhhwellsBridge() {
|
|
|
4782
5693
|
}, []);
|
|
4783
5694
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
4784
5695
|
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
4785
|
-
const postToParent =
|
|
5696
|
+
const postToParent = useCallback2((data) => {
|
|
4786
5697
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4787
5698
|
window.parent.postMessage(data, "*");
|
|
4788
5699
|
}
|
|
4789
5700
|
}, []);
|
|
4790
|
-
const [fetchState, setFetchState] =
|
|
4791
|
-
const autoSaveTimers =
|
|
4792
|
-
const activeElRef =
|
|
4793
|
-
const originalContentRef =
|
|
4794
|
-
const activeStateElRef =
|
|
4795
|
-
const parentScrollRef =
|
|
4796
|
-
const toolbarElRef =
|
|
4797
|
-
const glowElRef =
|
|
4798
|
-
const hoveredImageRef =
|
|
4799
|
-
const hoveredImageHasTextOverlapRef =
|
|
4800
|
-
const hoveredGapRef =
|
|
4801
|
-
const imageUnhoverTimerRef =
|
|
4802
|
-
const imageShowTimerRef =
|
|
4803
|
-
const editStylesRef =
|
|
4804
|
-
const activateRef =
|
|
5701
|
+
const [fetchState, setFetchState] = useState5("idle");
|
|
5702
|
+
const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
|
|
5703
|
+
const activeElRef = useRef3(null);
|
|
5704
|
+
const originalContentRef = useRef3(null);
|
|
5705
|
+
const activeStateElRef = useRef3(null);
|
|
5706
|
+
const parentScrollRef = useRef3(null);
|
|
5707
|
+
const toolbarElRef = useRef3(null);
|
|
5708
|
+
const glowElRef = useRef3(null);
|
|
5709
|
+
const hoveredImageRef = useRef3(null);
|
|
5710
|
+
const hoveredImageHasTextOverlapRef = useRef3(false);
|
|
5711
|
+
const hoveredGapRef = useRef3(null);
|
|
5712
|
+
const imageUnhoverTimerRef = useRef3(null);
|
|
5713
|
+
const imageShowTimerRef = useRef3(null);
|
|
5714
|
+
const editStylesRef = useRef3(null);
|
|
5715
|
+
const activateRef = useRef3(() => {
|
|
4805
5716
|
});
|
|
4806
|
-
const deactivateRef =
|
|
5717
|
+
const deactivateRef = useRef3(() => {
|
|
4807
5718
|
});
|
|
4808
|
-
const refreshActiveCommandsRef =
|
|
5719
|
+
const refreshActiveCommandsRef = useRef3(() => {
|
|
4809
5720
|
});
|
|
4810
|
-
const postToParentRef =
|
|
5721
|
+
const postToParentRef = useRef3(postToParent);
|
|
4811
5722
|
postToParentRef.current = postToParent;
|
|
4812
|
-
const
|
|
4813
|
-
const
|
|
4814
|
-
const [
|
|
4815
|
-
const [
|
|
4816
|
-
const [
|
|
4817
|
-
const [
|
|
4818
|
-
const [
|
|
4819
|
-
|
|
5723
|
+
const [toolbarRect, setToolbarRect] = useState5(null);
|
|
5724
|
+
const [toggleState, setToggleState] = useState5(null);
|
|
5725
|
+
const [maxBadge, setMaxBadge] = useState5(null);
|
|
5726
|
+
const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
|
|
5727
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
|
|
5728
|
+
const [linkPopover, setLinkPopover] = useState5(null);
|
|
5729
|
+
const [sitePages, setSitePages] = useState5([]);
|
|
5730
|
+
const [sectionsByPath, setSectionsByPath] = useState5({});
|
|
5731
|
+
const setLinkPopoverRef = useRef3(setLinkPopover);
|
|
5732
|
+
const linkPopoverPanelRef = useRef3(null);
|
|
5733
|
+
const linkPopoverOpenRef = useRef3(false);
|
|
5734
|
+
const linkPopoverGraceUntilRef = useRef3(0);
|
|
5735
|
+
setLinkPopoverRef.current = setLinkPopover;
|
|
5736
|
+
const bumpLinkPopoverGrace = () => {
|
|
5737
|
+
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
5738
|
+
};
|
|
5739
|
+
useEffect4(() => {
|
|
5740
|
+
if (!isEditMode) return;
|
|
5741
|
+
const useFixtures = shouldUseDevFixtures();
|
|
5742
|
+
if (useFixtures) {
|
|
5743
|
+
setSitePages(DEV_SITE_PAGES);
|
|
5744
|
+
setSectionsByPath(DEV_SECTIONS_BY_PATH);
|
|
5745
|
+
if (process.env.NODE_ENV === "development") {
|
|
5746
|
+
console.info("[ohhwells-bridge] Dev fixtures loaded (ohw-fixtures=1)");
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
const onSitePages = (e) => {
|
|
5750
|
+
if (e.data?.type !== "ow:site-pages" || !Array.isArray(e.data.pages)) return;
|
|
5751
|
+
if (useFixtures) return;
|
|
5752
|
+
setSitePages(
|
|
5753
|
+
e.data.pages.map((p) => ({
|
|
5754
|
+
path: p.path,
|
|
5755
|
+
title: p.title
|
|
5756
|
+
}))
|
|
5757
|
+
);
|
|
5758
|
+
};
|
|
5759
|
+
window.addEventListener("message", onSitePages);
|
|
5760
|
+
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5761
|
+
return () => window.removeEventListener("message", onSitePages);
|
|
5762
|
+
}, [isEditMode, postToParent]);
|
|
5763
|
+
const [sectionGap, setSectionGap] = useState5(null);
|
|
5764
|
+
useEffect4(() => {
|
|
4820
5765
|
const update = () => {
|
|
4821
5766
|
const el = activeElRef.current;
|
|
4822
5767
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -4840,33 +5785,10 @@ function OhhwellsBridge() {
|
|
|
4840
5785
|
vvp.removeEventListener("resize", update);
|
|
4841
5786
|
};
|
|
4842
5787
|
}, []);
|
|
4843
|
-
const refreshStateRules =
|
|
5788
|
+
const refreshStateRules = useCallback2(() => {
|
|
4844
5789
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4845
5790
|
}, []);
|
|
4846
|
-
const
|
|
4847
|
-
const tracker = getSectionsTracker();
|
|
4848
|
-
let entries = [];
|
|
4849
|
-
try {
|
|
4850
|
-
entries = JSON.parse(tracker.textContent || "[]");
|
|
4851
|
-
} catch {
|
|
4852
|
-
}
|
|
4853
|
-
const path = window.location.pathname;
|
|
4854
|
-
const entry = entries.find(
|
|
4855
|
-
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
4856
|
-
);
|
|
4857
|
-
if (entry) {
|
|
4858
|
-
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4859
|
-
return;
|
|
4860
|
-
}
|
|
4861
|
-
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
4862
|
-
entries.push(newEntry);
|
|
4863
|
-
tracker.textContent = JSON.stringify(entries);
|
|
4864
|
-
if (isEditMode) {
|
|
4865
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
4866
|
-
}
|
|
4867
|
-
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
4868
|
-
}, [isEditMode]);
|
|
4869
|
-
const deactivate = useCallback(() => {
|
|
5791
|
+
const deactivate = useCallback2(() => {
|
|
4870
5792
|
const el = activeElRef.current;
|
|
4871
5793
|
if (!el) return;
|
|
4872
5794
|
const key = el.dataset.ohwKey;
|
|
@@ -4889,9 +5811,10 @@ function OhhwellsBridge() {
|
|
|
4889
5811
|
setToolbarRect(null);
|
|
4890
5812
|
setMaxBadge(null);
|
|
4891
5813
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
5814
|
+
setToolbarShowEditLink(false);
|
|
4892
5815
|
postToParent({ type: "ow:exit-edit" });
|
|
4893
5816
|
}, [postToParent]);
|
|
4894
|
-
const activate =
|
|
5817
|
+
const activate = useCallback2((el) => {
|
|
4895
5818
|
if (activeElRef.current === el) return;
|
|
4896
5819
|
deactivate();
|
|
4897
5820
|
if (hoveredImageRef.current) {
|
|
@@ -4904,6 +5827,7 @@ function OhhwellsBridge() {
|
|
|
4904
5827
|
originalContentRef.current = el.innerHTML;
|
|
4905
5828
|
el.focus();
|
|
4906
5829
|
setToolbarRect(el.getBoundingClientRect());
|
|
5830
|
+
setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
|
|
4907
5831
|
postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
|
|
4908
5832
|
requestAnimationFrame(() => refreshActiveCommandsRef.current());
|
|
4909
5833
|
}, [deactivate, postToParent]);
|
|
@@ -4931,14 +5855,15 @@ function OhhwellsBridge() {
|
|
|
4931
5855
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
4932
5856
|
const next = `url('${val}')`;
|
|
4933
5857
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
5858
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
5859
|
+
applyLinkHref(el, val);
|
|
4934
5860
|
} else if (el.innerHTML !== val) {
|
|
4935
5861
|
el.innerHTML = val;
|
|
4936
5862
|
}
|
|
4937
5863
|
});
|
|
5864
|
+
applyLinkByKey(key, val);
|
|
4938
5865
|
}
|
|
4939
5866
|
initSectionsFromContent(content, true);
|
|
4940
|
-
sectionsLoadedRef.current = true;
|
|
4941
|
-
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
4942
5867
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
4943
5868
|
}) : Promise.resolve();
|
|
4944
5869
|
};
|
|
@@ -4963,7 +5888,7 @@ function OhhwellsBridge() {
|
|
|
4963
5888
|
cancelled = true;
|
|
4964
5889
|
};
|
|
4965
5890
|
}, [subdomain, isEditMode, pathname]);
|
|
4966
|
-
|
|
5891
|
+
useEffect4(() => {
|
|
4967
5892
|
if (!subdomain || isEditMode) return;
|
|
4968
5893
|
let debounceTimer = null;
|
|
4969
5894
|
const applyFromCache = () => {
|
|
@@ -4979,10 +5904,13 @@ function OhhwellsBridge() {
|
|
|
4979
5904
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
4980
5905
|
const next = `url('${val}')`;
|
|
4981
5906
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
5907
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
5908
|
+
applyLinkHref(el, val);
|
|
4982
5909
|
} else if (el.innerHTML !== val) {
|
|
4983
5910
|
el.innerHTML = val;
|
|
4984
5911
|
}
|
|
4985
5912
|
});
|
|
5913
|
+
applyLinkByKey(key, val);
|
|
4986
5914
|
}
|
|
4987
5915
|
};
|
|
4988
5916
|
const scheduleApply = () => {
|
|
@@ -5003,10 +5931,10 @@ function OhhwellsBridge() {
|
|
|
5003
5931
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
5004
5932
|
el.style.display = visible ? "flex" : "none";
|
|
5005
5933
|
}, [subdomain, fetchState]);
|
|
5006
|
-
|
|
5934
|
+
useEffect4(() => {
|
|
5007
5935
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
5008
5936
|
}, [pathname, postToParent]);
|
|
5009
|
-
|
|
5937
|
+
useEffect4(() => {
|
|
5010
5938
|
if (!isEditMode) return;
|
|
5011
5939
|
const measure = () => {
|
|
5012
5940
|
const h = document.body.scrollHeight;
|
|
@@ -5030,7 +5958,7 @@ function OhhwellsBridge() {
|
|
|
5030
5958
|
window.removeEventListener("resize", handleResize);
|
|
5031
5959
|
};
|
|
5032
5960
|
}, [pathname, isEditMode, postToParent]);
|
|
5033
|
-
|
|
5961
|
+
useEffect4(() => {
|
|
5034
5962
|
if (!subdomainFromQuery || isEditMode) return;
|
|
5035
5963
|
const handleClick = (e) => {
|
|
5036
5964
|
const anchor = e.target.closest("a");
|
|
@@ -5046,7 +5974,7 @@ function OhhwellsBridge() {
|
|
|
5046
5974
|
document.addEventListener("click", handleClick, true);
|
|
5047
5975
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5048
5976
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5049
|
-
|
|
5977
|
+
useEffect4(() => {
|
|
5050
5978
|
if (!isEditMode) {
|
|
5051
5979
|
editStylesRef.current?.base.remove();
|
|
5052
5980
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5072,6 +6000,7 @@ function OhhwellsBridge() {
|
|
|
5072
6000
|
[data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
|
|
5073
6001
|
[data-ohw-editable="image"], [data-ohw-editable="image"] *,
|
|
5074
6002
|
[data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
|
|
6003
|
+
[data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
|
|
5075
6004
|
[data-ohw-hovered]:not([contenteditable]) {
|
|
5076
6005
|
outline: 2px dashed ${PRIMARY} !important;
|
|
5077
6006
|
outline-offset: 4px;
|
|
@@ -5109,8 +6038,21 @@ function OhhwellsBridge() {
|
|
|
5109
6038
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
5110
6039
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
5111
6040
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
6041
|
+
if (isInsideLinkEditor(target)) return;
|
|
5112
6042
|
const editable = target.closest("[data-ohw-editable]");
|
|
5113
6043
|
if (editable) {
|
|
6044
|
+
if (editable.dataset.ohwEditable === "link") {
|
|
6045
|
+
e.preventDefault();
|
|
6046
|
+
e.stopPropagation();
|
|
6047
|
+
deactivateRef.current();
|
|
6048
|
+
bumpLinkPopoverGrace();
|
|
6049
|
+
setLinkPopoverRef.current({
|
|
6050
|
+
key: editable.dataset.ohwKey ?? "",
|
|
6051
|
+
target: getLinkHref(editable),
|
|
6052
|
+
rect: editable.getBoundingClientRect()
|
|
6053
|
+
});
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
5114
6056
|
if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
|
|
5115
6057
|
e.preventDefault();
|
|
5116
6058
|
e.stopPropagation();
|
|
@@ -5131,6 +6073,15 @@ function OhhwellsBridge() {
|
|
|
5131
6073
|
}
|
|
5132
6074
|
const anchor = target.closest("a");
|
|
5133
6075
|
if (anchor) e.preventDefault();
|
|
6076
|
+
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
6077
|
+
if (linkPopoverOpenRef.current && activeElRef.current) {
|
|
6078
|
+
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6079
|
+
if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
|
|
6080
|
+
}
|
|
6081
|
+
if (linkPopoverOpenRef.current) {
|
|
6082
|
+
setLinkPopoverRef.current(null);
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
5134
6085
|
deactivateRef.current();
|
|
5135
6086
|
};
|
|
5136
6087
|
const handleMouseOver = (e) => {
|
|
@@ -5581,25 +6532,35 @@ function OhhwellsBridge() {
|
|
|
5581
6532
|
if (img) img.src = val;
|
|
5582
6533
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
5583
6534
|
el.style.backgroundImage = `url('${val}')`;
|
|
6535
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
6536
|
+
applyLinkHref(el, val);
|
|
5584
6537
|
} else {
|
|
5585
6538
|
el.innerHTML = val;
|
|
5586
6539
|
}
|
|
5587
6540
|
});
|
|
6541
|
+
applyLinkByKey(key, val);
|
|
5588
6542
|
}
|
|
5589
6543
|
if (sectionsJson) {
|
|
5590
6544
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
5591
|
-
sectionsLoadedRef.current = true;
|
|
5592
|
-
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5593
6545
|
}
|
|
5594
6546
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5595
6547
|
};
|
|
5596
6548
|
window.addEventListener("message", handleHydrate);
|
|
5597
6549
|
const handleDeactivate = (e) => {
|
|
5598
6550
|
if (e.data?.type !== "ow:deactivate") return;
|
|
6551
|
+
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
6552
|
+
if (linkPopoverOpenRef.current) {
|
|
6553
|
+
setLinkPopoverRef.current(null);
|
|
6554
|
+
return;
|
|
6555
|
+
}
|
|
5599
6556
|
deactivateRef.current();
|
|
5600
6557
|
};
|
|
5601
6558
|
window.addEventListener("message", handleDeactivate);
|
|
5602
6559
|
const handleKeyDown = (e) => {
|
|
6560
|
+
if (e.key === "Escape" && linkPopoverOpenRef.current) {
|
|
6561
|
+
setLinkPopoverRef.current(null);
|
|
6562
|
+
return;
|
|
6563
|
+
}
|
|
5603
6564
|
if (e.key !== "Escape") return;
|
|
5604
6565
|
const el = activeElRef.current;
|
|
5605
6566
|
if (el && originalContentRef.current !== null) {
|
|
@@ -5853,23 +6814,7 @@ function OhhwellsBridge() {
|
|
|
5853
6814
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5854
6815
|
};
|
|
5855
6816
|
}, [isEditMode, refreshStateRules]);
|
|
5856
|
-
|
|
5857
|
-
const handler = (e) => {
|
|
5858
|
-
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
5859
|
-
const insertAfterVal = e.data.insertAfter;
|
|
5860
|
-
if (!insertAfterVal) return;
|
|
5861
|
-
if (!sectionsLoadedRef.current) {
|
|
5862
|
-
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
5863
|
-
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
5864
|
-
}
|
|
5865
|
-
return;
|
|
5866
|
-
}
|
|
5867
|
-
processConfigRequest(insertAfterVal);
|
|
5868
|
-
};
|
|
5869
|
-
window.addEventListener("message", handler);
|
|
5870
|
-
return () => window.removeEventListener("message", handler);
|
|
5871
|
-
}, [processConfigRequest]);
|
|
5872
|
-
useEffect2(() => {
|
|
6817
|
+
useEffect4(() => {
|
|
5873
6818
|
if (!isEditMode) return;
|
|
5874
6819
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5875
6820
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5884,20 +6829,26 @@ function OhhwellsBridge() {
|
|
|
5884
6829
|
const raf = requestAnimationFrame(() => refreshStateRules());
|
|
5885
6830
|
const timer = setTimeout(() => {
|
|
5886
6831
|
refreshStateRules();
|
|
5887
|
-
|
|
6832
|
+
const sections = collectSections();
|
|
6833
|
+
setSectionsByPath((prev) => {
|
|
6834
|
+
const next = { ...prev, [pathname]: sections };
|
|
6835
|
+
return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
|
|
6836
|
+
});
|
|
6837
|
+
postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
|
|
6838
|
+
postToParent({ type: "ow:request-site-pages" });
|
|
5888
6839
|
}, 150);
|
|
5889
6840
|
return () => {
|
|
5890
6841
|
cancelAnimationFrame(raf);
|
|
5891
6842
|
clearTimeout(timer);
|
|
5892
6843
|
};
|
|
5893
6844
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5894
|
-
const handleCommand =
|
|
6845
|
+
const handleCommand = useCallback2((cmd) => {
|
|
5895
6846
|
document.execCommand(cmd, false);
|
|
5896
6847
|
activeElRef.current?.focus();
|
|
5897
6848
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
5898
6849
|
refreshActiveCommandsRef.current();
|
|
5899
6850
|
}, []);
|
|
5900
|
-
const handleStateChange =
|
|
6851
|
+
const handleStateChange = useCallback2((state) => {
|
|
5901
6852
|
if (!activeStateElRef.current) return;
|
|
5902
6853
|
const el = activeStateElRef.current;
|
|
5903
6854
|
if (state === "Default") {
|
|
@@ -5910,13 +6861,48 @@ function OhhwellsBridge() {
|
|
|
5910
6861
|
}
|
|
5911
6862
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
5912
6863
|
}, [deactivate]);
|
|
6864
|
+
const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
|
|
6865
|
+
const openLinkPopoverForActive = useCallback2(() => {
|
|
6866
|
+
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6867
|
+
if (!hrefCtx) return;
|
|
6868
|
+
bumpLinkPopoverGrace();
|
|
6869
|
+
setLinkPopover({
|
|
6870
|
+
key: hrefCtx.key,
|
|
6871
|
+
target: getLinkHref(hrefCtx.anchor),
|
|
6872
|
+
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
6873
|
+
});
|
|
6874
|
+
}, []);
|
|
6875
|
+
const handleLinkPopoverSubmit = useCallback2(
|
|
6876
|
+
(target) => {
|
|
6877
|
+
if (!linkPopover) return;
|
|
6878
|
+
const { key } = linkPopover;
|
|
6879
|
+
applyLinkByKey(key, target);
|
|
6880
|
+
postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
|
|
6881
|
+
setLinkPopover(null);
|
|
6882
|
+
},
|
|
6883
|
+
[linkPopover, postToParent]
|
|
6884
|
+
);
|
|
6885
|
+
const showEditLink = toolbarShowEditLink;
|
|
6886
|
+
const currentSections = sectionsByPath[pathname] ?? [];
|
|
6887
|
+
linkPopoverOpenRef.current = linkPopover !== null;
|
|
5913
6888
|
return bridgeRoot ? createPortal(
|
|
5914
|
-
/* @__PURE__ */
|
|
5915
|
-
toolbarRect && /* @__PURE__ */
|
|
5916
|
-
/* @__PURE__ */
|
|
5917
|
-
/* @__PURE__ */
|
|
6889
|
+
/* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6890
|
+
toolbarRect && /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6891
|
+
/* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
6892
|
+
/* @__PURE__ */ jsx16(
|
|
6893
|
+
FloatingToolbar,
|
|
6894
|
+
{
|
|
6895
|
+
rect: toolbarRect,
|
|
6896
|
+
parentScroll: parentScrollRef.current,
|
|
6897
|
+
elRef: toolbarElRef,
|
|
6898
|
+
onCommand: handleCommand,
|
|
6899
|
+
activeCommands,
|
|
6900
|
+
showEditLink,
|
|
6901
|
+
onEditLink: openLinkPopoverForActive
|
|
6902
|
+
}
|
|
6903
|
+
)
|
|
5918
6904
|
] }),
|
|
5919
|
-
maxBadge && /* @__PURE__ */
|
|
6905
|
+
maxBadge && /* @__PURE__ */ jsxs8(
|
|
5920
6906
|
"div",
|
|
5921
6907
|
{
|
|
5922
6908
|
"data-ohw-max-badge": "",
|
|
@@ -5942,7 +6928,7 @@ function OhhwellsBridge() {
|
|
|
5942
6928
|
]
|
|
5943
6929
|
}
|
|
5944
6930
|
),
|
|
5945
|
-
toggleState && /* @__PURE__ */
|
|
6931
|
+
toggleState && /* @__PURE__ */ jsx16(
|
|
5946
6932
|
StateToggle,
|
|
5947
6933
|
{
|
|
5948
6934
|
rect: toggleState.rect,
|
|
@@ -5951,15 +6937,31 @@ function OhhwellsBridge() {
|
|
|
5951
6937
|
onStateChange: handleStateChange
|
|
5952
6938
|
}
|
|
5953
6939
|
),
|
|
5954
|
-
|
|
6940
|
+
linkPopover ? /* @__PURE__ */ jsx16(
|
|
6941
|
+
LinkPopover,
|
|
6942
|
+
{
|
|
6943
|
+
rect: linkPopover.rect,
|
|
6944
|
+
parentScroll: parentScrollRef.current,
|
|
6945
|
+
panelRef: linkPopoverPanelRef,
|
|
6946
|
+
open: true,
|
|
6947
|
+
mode: "edit",
|
|
6948
|
+
pages: sitePages,
|
|
6949
|
+
sections: currentSections,
|
|
6950
|
+
sectionsByPath,
|
|
6951
|
+
initialTarget: linkPopover.target,
|
|
6952
|
+
onClose: closeLinkPopover,
|
|
6953
|
+
onSubmit: handleLinkPopoverSubmit
|
|
6954
|
+
}
|
|
6955
|
+
) : null,
|
|
6956
|
+
sectionGap && /* @__PURE__ */ jsxs8(
|
|
5955
6957
|
"div",
|
|
5956
6958
|
{
|
|
5957
6959
|
"data-ohw-section-insert-line": "",
|
|
5958
6960
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
5959
6961
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
5960
6962
|
children: [
|
|
5961
|
-
/* @__PURE__ */
|
|
5962
|
-
/* @__PURE__ */
|
|
6963
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
6964
|
+
/* @__PURE__ */ jsx16(
|
|
5963
6965
|
Badge,
|
|
5964
6966
|
{
|
|
5965
6967
|
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",
|
|
@@ -5972,7 +6974,7 @@ function OhhwellsBridge() {
|
|
|
5972
6974
|
children: "Add Section"
|
|
5973
6975
|
}
|
|
5974
6976
|
),
|
|
5975
|
-
/* @__PURE__ */
|
|
6977
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
5976
6978
|
]
|
|
5977
6979
|
}
|
|
5978
6980
|
)
|
|
@@ -5981,11 +6983,19 @@ function OhhwellsBridge() {
|
|
|
5981
6983
|
) : null;
|
|
5982
6984
|
}
|
|
5983
6985
|
export {
|
|
6986
|
+
LinkEditorPanel,
|
|
6987
|
+
LinkPopover,
|
|
5984
6988
|
OhhwellsBridge,
|
|
5985
6989
|
SchedulingWidget,
|
|
5986
6990
|
Toggle,
|
|
5987
6991
|
ToggleGroup,
|
|
5988
6992
|
ToggleGroupItem,
|
|
5989
|
-
|
|
6993
|
+
buildTarget,
|
|
6994
|
+
filterAvailablePages,
|
|
6995
|
+
getEditModeInitialState,
|
|
6996
|
+
isValidUrl,
|
|
6997
|
+
parseTarget,
|
|
6998
|
+
toggleVariants,
|
|
6999
|
+
validateUrlInput
|
|
5990
7000
|
};
|
|
5991
7001
|
//# sourceMappingURL=index.js.map
|