@ohhwells/bridge 0.1.28 → 0.1.29-next.21
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 +362 -304
- package/dist/index.cjs +178 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +181 -111
- package/dist/index.js.map +1 -1
- package/dist/styles.css +8 -8
- package/package.json +48 -48
package/dist/index.d.cts
CHANGED
|
@@ -83,10 +83,10 @@ declare function ToggleGroupItem({ className, value, children, ...props }: {
|
|
|
83
83
|
children: React.ReactNode;
|
|
84
84
|
} & Omit<React.ComponentProps<typeof Toggle>, 'pressed' | 'onPressedChange'>): react_jsx_runtime.JSX.Element;
|
|
85
85
|
|
|
86
|
-
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter }: {
|
|
86
|
+
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter: insertAfterProp }: {
|
|
87
87
|
notifyOnConnect?: boolean;
|
|
88
88
|
initialScheduleId?: string | null;
|
|
89
|
-
insertAfter
|
|
90
|
-
}): react_jsx_runtime.JSX.Element;
|
|
89
|
+
insertAfter?: string;
|
|
90
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
91
91
|
|
|
92
92
|
export { LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, Toggle, ToggleGroup, ToggleGroupItem, buildTarget, filterAvailablePages, getEditModeInitialState, isValidUrl, parseTarget, toggleVariants, validateUrlInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -83,10 +83,10 @@ declare function ToggleGroupItem({ className, value, children, ...props }: {
|
|
|
83
83
|
children: React.ReactNode;
|
|
84
84
|
} & Omit<React.ComponentProps<typeof Toggle>, 'pressed' | 'onPressedChange'>): react_jsx_runtime.JSX.Element;
|
|
85
85
|
|
|
86
|
-
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter }: {
|
|
86
|
+
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter: insertAfterProp }: {
|
|
87
87
|
notifyOnConnect?: boolean;
|
|
88
88
|
initialScheduleId?: string | null;
|
|
89
|
-
insertAfter
|
|
90
|
-
}): react_jsx_runtime.JSX.Element;
|
|
89
|
+
insertAfter?: string;
|
|
90
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
91
91
|
|
|
92
92
|
export { LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, Toggle, ToggleGroup, ToggleGroupItem, buildTarget, filterAvailablePages, getEditModeInitialState, isValidUrl, parseTarget, toggleVariants, validateUrlInput };
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createRoot } from "react-dom/client";
|
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
7
|
|
|
8
8
|
// src/ui/SchedulingWidget.tsx
|
|
9
|
-
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
9
|
+
import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
|
|
10
10
|
|
|
11
11
|
// src/ui/EmailCaptureModal.tsx
|
|
12
12
|
import { useState } from "react";
|
|
@@ -164,13 +164,6 @@ 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
|
-
}
|
|
174
167
|
function classRunsOnDate(cls, date, type) {
|
|
175
168
|
if (type === "FIXED") {
|
|
176
169
|
const d = String(date.getDate()).padStart(2, "0");
|
|
@@ -216,10 +209,10 @@ function buildTimezoneLabel(tz) {
|
|
|
216
209
|
return tz;
|
|
217
210
|
}
|
|
218
211
|
}
|
|
219
|
-
function EmptyState({ inEditor }) {
|
|
212
|
+
function EmptyState({ inEditor, insertAfter }) {
|
|
220
213
|
const handleAddSchedule = () => {
|
|
221
214
|
if (inEditor) {
|
|
222
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
215
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
223
216
|
}
|
|
224
217
|
};
|
|
225
218
|
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: [
|
|
@@ -444,18 +437,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
444
437
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
445
438
|
const available = cls.maxParticipants - booked;
|
|
446
439
|
const isFull = available <= 0;
|
|
440
|
+
const isPrivate = cls.maxParticipants === 1;
|
|
447
441
|
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
448
442
|
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
449
443
|
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
450
|
-
/* @__PURE__ */
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
444
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 sm:w-[120px] sm:shrink-0", children: [
|
|
445
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }),
|
|
446
|
+
/* @__PURE__ */ jsx2(
|
|
447
|
+
"span",
|
|
448
|
+
{
|
|
449
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium w-fit",
|
|
450
|
+
style: isPrivate ? { background: "#DDFBE3", color: "#199130", border: "1px solid #199130", fontWeight: 500 } : { background: "#EEEDFF", color: "#5953FF", border: "1px solid #5953FF", fontWeight: 500 },
|
|
451
|
+
children: isPrivate ? "PRIVATE" : "GROUP"
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
] }),
|
|
459
455
|
/* @__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: [
|
|
460
456
|
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
461
457
|
available,
|
|
@@ -528,7 +524,9 @@ function CalendarFoldIcon() {
|
|
|
528
524
|
}
|
|
529
525
|
);
|
|
530
526
|
}
|
|
531
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
527
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
|
|
528
|
+
const autoId = useId();
|
|
529
|
+
const insertAfter = insertAfterProp ?? autoId;
|
|
532
530
|
const [schedule, setSchedule] = useState2(null);
|
|
533
531
|
const [loading, setLoading] = useState2(true);
|
|
534
532
|
const [inEditor, setInEditor] = useState2(false);
|
|
@@ -558,88 +556,99 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
558
556
|
if (typeof window === "undefined") return;
|
|
559
557
|
const isInEditor = window.self !== window.top;
|
|
560
558
|
setInEditor(isInEditor);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
559
|
+
function loadWithId(effectiveId) {
|
|
560
|
+
if (isInEditor) {
|
|
561
|
+
const startEmpty = effectiveId === null;
|
|
562
|
+
if (startEmpty) setLoading(false);
|
|
563
|
+
let initialized = startEmpty;
|
|
564
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
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;
|
|
567
588
|
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);
|
|
568
595
|
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 {
|
|
569
623
|
}
|
|
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);
|
|
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
624
|
setLoading(false);
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
625
|
+
})();
|
|
626
|
+
}
|
|
627
|
+
if (initialScheduleId === void 0) {
|
|
628
|
+
let done = false;
|
|
629
|
+
let innerCleanup;
|
|
630
|
+
const timer = setTimeout(() => {
|
|
631
|
+
if (!done) {
|
|
632
|
+
done = true;
|
|
633
|
+
setLoading(false);
|
|
604
634
|
}
|
|
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);
|
|
605
642
|
};
|
|
606
|
-
window.addEventListener("message",
|
|
607
|
-
|
|
643
|
+
window.addEventListener("message", configHandler);
|
|
644
|
+
window.postMessage({ type: "ow:request-schedule-config", insertAfter }, "*");
|
|
608
645
|
return () => {
|
|
609
|
-
window.removeEventListener("message",
|
|
610
|
-
|
|
646
|
+
window.removeEventListener("message", configHandler);
|
|
647
|
+
clearTimeout(timer);
|
|
648
|
+
innerCleanup?.();
|
|
611
649
|
};
|
|
612
650
|
}
|
|
613
|
-
|
|
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
|
-
})();
|
|
651
|
+
return loadWithId(initialScheduleId) ?? void 0;
|
|
643
652
|
}, []);
|
|
644
653
|
const timezoneLabel = schedule?.timezone ? (() => {
|
|
645
654
|
try {
|
|
@@ -674,6 +683,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
674
683
|
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
675
684
|
}
|
|
676
685
|
};
|
|
686
|
+
if (!inEditor && !loading && !schedule) return null;
|
|
677
687
|
const sectionId = `scheduling-${insertAfter}`;
|
|
678
688
|
return /* @__PURE__ */ jsxs2(
|
|
679
689
|
"section",
|
|
@@ -719,7 +729,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
719
729
|
] }),
|
|
720
730
|
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
721
731
|
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
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(
|
|
732
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor, insertAfter }) : /* @__PURE__ */ jsx2(
|
|
723
733
|
ScheduleView,
|
|
724
734
|
{
|
|
725
735
|
schedule,
|
|
@@ -4263,7 +4273,10 @@ function isValidUrl(urlString) {
|
|
|
4263
4273
|
const hostname = url.hostname;
|
|
4264
4274
|
if (!hostname || hostname.length === 0) return false;
|
|
4265
4275
|
if (hostname.includes(" ")) return false;
|
|
4266
|
-
|
|
4276
|
+
if (hostname === "localhost") return true;
|
|
4277
|
+
if (!hostname.includes(".")) return false;
|
|
4278
|
+
if (!/[a-z]/i.test(hostname)) return false;
|
|
4279
|
+
return true;
|
|
4267
4280
|
} catch {
|
|
4268
4281
|
return false;
|
|
4269
4282
|
}
|
|
@@ -4274,12 +4287,19 @@ function normalizeUrl(value) {
|
|
|
4274
4287
|
return `https://${trimmed}`;
|
|
4275
4288
|
}
|
|
4276
4289
|
function validateUrlInput(value, pages, allPages) {
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
if (
|
|
4290
|
+
const trimmed = value.trim();
|
|
4291
|
+
if (!trimmed) return "";
|
|
4292
|
+
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
4293
|
+
if (exactPage) return "";
|
|
4294
|
+
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
4295
|
+
if (hasPrefixMatch) {
|
|
4296
|
+
return "Select a page from the list or enter a valid URL";
|
|
4297
|
+
}
|
|
4298
|
+
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
4299
|
+
if (!hasScheme && !trimmed.includes(".")) {
|
|
4300
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4301
|
+
}
|
|
4302
|
+
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
4283
4303
|
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4284
4304
|
}
|
|
4285
4305
|
return "";
|
|
@@ -4582,7 +4602,7 @@ function SectionPickerList({ sections, onSelect }) {
|
|
|
4582
4602
|
}
|
|
4583
4603
|
|
|
4584
4604
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4585
|
-
import { useId, useRef as useRef2, useState as useState3 } from "react";
|
|
4605
|
+
import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
4586
4606
|
|
|
4587
4607
|
// src/ui/input.tsx
|
|
4588
4608
|
import * as React4 from "react";
|
|
@@ -4677,7 +4697,7 @@ function UrlOrPageInput({
|
|
|
4677
4697
|
readOnly = false,
|
|
4678
4698
|
urlError
|
|
4679
4699
|
}) {
|
|
4680
|
-
const inputId =
|
|
4700
|
+
const inputId = useId2();
|
|
4681
4701
|
const inputRef = useRef2(null);
|
|
4682
4702
|
const [isFocused, setIsFocused] = useState3(false);
|
|
4683
4703
|
const openDropdown = () => {
|
|
@@ -4822,9 +4842,8 @@ function useLinkModalState({
|
|
|
4822
4842
|
setUrlError("");
|
|
4823
4843
|
const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
|
|
4824
4844
|
setDropdownOpen(filtered.length > 0);
|
|
4825
|
-
if (value.trim()
|
|
4826
|
-
|
|
4827
|
-
if (error) setUrlError(error);
|
|
4845
|
+
if (value.trim()) {
|
|
4846
|
+
setUrlError(validateUrlInput(value, availablePages, pages));
|
|
4828
4847
|
}
|
|
4829
4848
|
};
|
|
4830
4849
|
const handlePageSelect = (page) => {
|
|
@@ -5268,9 +5287,14 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
5268
5287
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5269
5288
|
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5270
5289
|
const pageEntries = getPageSchedulingEntries(raw);
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5290
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
5291
|
+
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
5292
|
+
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
5293
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
5294
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
5295
|
+
for (const entry of preExisting) {
|
|
5296
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5297
|
+
}
|
|
5274
5298
|
} catch {
|
|
5275
5299
|
}
|
|
5276
5300
|
}
|
|
@@ -5329,8 +5353,9 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
5329
5353
|
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5330
5354
|
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5331
5355
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5332
|
-
const
|
|
5333
|
-
|
|
5356
|
+
const entry = pending[i];
|
|
5357
|
+
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
5358
|
+
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5334
5359
|
pending.splice(i, 1);
|
|
5335
5360
|
}
|
|
5336
5361
|
}
|
|
@@ -5808,6 +5833,8 @@ function OhhwellsBridge() {
|
|
|
5808
5833
|
});
|
|
5809
5834
|
const postToParentRef = useRef3(postToParent);
|
|
5810
5835
|
postToParentRef.current = postToParent;
|
|
5836
|
+
const sectionsLoadedRef = useRef3(false);
|
|
5837
|
+
const pendingScheduleConfigRequests = useRef3([]);
|
|
5811
5838
|
const [toolbarRect, setToolbarRect] = useState5(null);
|
|
5812
5839
|
const [toggleState, setToggleState] = useState5(null);
|
|
5813
5840
|
const [maxBadge, setMaxBadge] = useState5(null);
|
|
@@ -5913,6 +5940,29 @@ function OhhwellsBridge() {
|
|
|
5913
5940
|
const refreshStateRules = useCallback2(() => {
|
|
5914
5941
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
5915
5942
|
}, []);
|
|
5943
|
+
const processConfigRequest = useCallback2((insertAfterVal) => {
|
|
5944
|
+
const tracker = getSectionsTracker();
|
|
5945
|
+
let entries = [];
|
|
5946
|
+
try {
|
|
5947
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
5948
|
+
} catch {
|
|
5949
|
+
}
|
|
5950
|
+
const path = window.location.pathname;
|
|
5951
|
+
const entry = entries.find(
|
|
5952
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
5953
|
+
);
|
|
5954
|
+
if (entry) {
|
|
5955
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
5959
|
+
entries.push(newEntry);
|
|
5960
|
+
tracker.textContent = JSON.stringify(entries);
|
|
5961
|
+
if (isEditMode) {
|
|
5962
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5963
|
+
}
|
|
5964
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
5965
|
+
}, [isEditMode]);
|
|
5916
5966
|
const deactivate = useCallback2(() => {
|
|
5917
5967
|
const el = activeElRef.current;
|
|
5918
5968
|
if (!el) return;
|
|
@@ -5989,6 +6039,8 @@ function OhhwellsBridge() {
|
|
|
5989
6039
|
applyLinkByKey(key, val);
|
|
5990
6040
|
}
|
|
5991
6041
|
initSectionsFromContent(content, true);
|
|
6042
|
+
sectionsLoadedRef.current = true;
|
|
6043
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5992
6044
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5993
6045
|
}) : Promise.resolve();
|
|
5994
6046
|
};
|
|
@@ -6685,6 +6737,8 @@ function OhhwellsBridge() {
|
|
|
6685
6737
|
}
|
|
6686
6738
|
if (sectionsJson) {
|
|
6687
6739
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
6740
|
+
sectionsLoadedRef.current = true;
|
|
6741
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
6688
6742
|
}
|
|
6689
6743
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
6690
6744
|
};
|
|
@@ -6957,6 +7011,22 @@ function OhhwellsBridge() {
|
|
|
6957
7011
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
6958
7012
|
};
|
|
6959
7013
|
}, [isEditMode, refreshStateRules]);
|
|
7014
|
+
useEffect3(() => {
|
|
7015
|
+
const handler = (e) => {
|
|
7016
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7017
|
+
const insertAfterVal = e.data.insertAfter;
|
|
7018
|
+
if (!insertAfterVal) return;
|
|
7019
|
+
if (!sectionsLoadedRef.current) {
|
|
7020
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
7021
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
7022
|
+
}
|
|
7023
|
+
return;
|
|
7024
|
+
}
|
|
7025
|
+
processConfigRequest(insertAfterVal);
|
|
7026
|
+
};
|
|
7027
|
+
window.addEventListener("message", handler);
|
|
7028
|
+
return () => window.removeEventListener("message", handler);
|
|
7029
|
+
}, [processConfigRequest]);
|
|
6960
7030
|
useEffect3(() => {
|
|
6961
7031
|
if (!isEditMode) return;
|
|
6962
7032
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|