@ohhwells/bridge 0.1.22 → 0.1.24
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 +58 -0
- package/dist/index.cjs +148 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +149 -91
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,6 +177,64 @@ Add `data-ohw-editable` and `data-ohw-key` to any element the studio owner shoul
|
|
|
177
177
|
|
|
178
178
|
---
|
|
179
179
|
|
|
180
|
+
## SchedulingWidget (vibe-coder placement)
|
|
181
|
+
|
|
182
|
+
`SchedulingWidget` lets a template builder embed a scheduling/booking section directly in their template JSX, without going through the "Add Section" flow. The canvas editor treats it exactly like a dynamically-inserted scheduling widget — the studio owner can connect a schedule, switch it, and clear it.
|
|
183
|
+
|
|
184
|
+
### Basic usage
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { SchedulingWidget } from '@ohhwells/bridge'
|
|
188
|
+
|
|
189
|
+
export default function ClassesPage() {
|
|
190
|
+
return (
|
|
191
|
+
<>
|
|
192
|
+
<PageHeader ... />
|
|
193
|
+
<ClassLibrary ... />
|
|
194
|
+
<SchedulingWidget />
|
|
195
|
+
<WordmarkBand ... />
|
|
196
|
+
</>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
No props are required. The widget auto-generates a stable identity via React's `useId()` so the bridge can track which schedule is connected to it across saves.
|
|
202
|
+
|
|
203
|
+
### Props
|
|
204
|
+
|
|
205
|
+
| Prop | Type | Default | Description |
|
|
206
|
+
|---|---|---|---|
|
|
207
|
+
| `insertAfter` | `string` | auto | Stable identifier used as the tracker key. Only set this manually if you need two `SchedulingWidget`s on the same page. |
|
|
208
|
+
| `initialScheduleId` | `string \| null` | `undefined` | Set by the bridge internally after hydration. Do not pass this yourself. |
|
|
209
|
+
| `notifyOnConnect` | `boolean` | `false` | Set by the bridge internally. Do not pass this yourself. |
|
|
210
|
+
|
|
211
|
+
### How it works
|
|
212
|
+
|
|
213
|
+
1. The widget renders immediately with a loading skeleton.
|
|
214
|
+
2. It sends `ow:request-schedule-config` to the bridge (running in the same window).
|
|
215
|
+
3. The bridge looks up the tracker for a saved `scheduleId` for this widget:
|
|
216
|
+
- **Found** → responds with `ow:schedule-config { scheduleId }` → widget loads that schedule.
|
|
217
|
+
- **Not found (first time)** → bridge adopts the widget (adds it to the tracker, notifies the canvas editor), responds with `scheduleId: null` → widget shows empty state with an "Add Schedule" button.
|
|
218
|
+
4. In the canvas editor, the studio owner clicks "Add Schedule" → selects a schedule → the widget updates and the connection is saved.
|
|
219
|
+
5. On the live site, the widget fetches the saved schedule by ID and renders it.
|
|
220
|
+
|
|
221
|
+
### Multiple widgets on one page
|
|
222
|
+
|
|
223
|
+
Each `SchedulingWidget` must have a unique `insertAfter` to be tracked independently:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
<SchedulingWidget insertAfter="classes-morning" />
|
|
227
|
+
<SchedulingWidget insertAfter="classes-evening" />
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
If you omit `insertAfter` on both, `useId()` auto-generates different stable IDs for each, so they are tracked separately anyway.
|
|
231
|
+
|
|
232
|
+
### Empty state on live site
|
|
233
|
+
|
|
234
|
+
If the studio owner has never connected a schedule to this widget, it renders nothing on the live site (no empty placeholder is shown to visitors).
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
180
238
|
## Editable states (advanced)
|
|
181
239
|
|
|
182
240
|
For elements with multiple display states (e.g. a contact form with default/success/error views), wrap each state in a `data-ohw-state-view` and mark the container with `data-ohw-editable-state`:
|
package/dist/index.cjs
CHANGED
|
@@ -204,13 +204,6 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
204
204
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
205
205
|
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
206
206
|
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
207
|
-
function getApiDomain() {
|
|
208
|
-
const h = window.location.hostname;
|
|
209
|
-
for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
|
|
210
|
-
if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
|
|
211
|
-
}
|
|
212
|
-
return h;
|
|
213
|
-
}
|
|
214
207
|
function classRunsOnDate(cls, date, type) {
|
|
215
208
|
if (type === "FIXED") {
|
|
216
209
|
const d = String(date.getDate()).padStart(2, "0");
|
|
@@ -484,18 +477,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
484
477
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
485
478
|
const available = cls.maxParticipants - booked;
|
|
486
479
|
const isFull = available <= 0;
|
|
480
|
+
const isPrivate = cls.maxParticipants === 1;
|
|
487
481
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
488
482
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
489
483
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
490
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
484
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 sm:w-[120px] sm:shrink-0", children: [
|
|
485
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }),
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
487
|
+
"span",
|
|
488
|
+
{
|
|
489
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium w-fit",
|
|
490
|
+
style: isPrivate ? { background: "#DDFBE3", color: "#199130", border: "1px solid #199130", fontWeight: 500 } : { background: "#EEEDFF", color: "#5953FF", border: "1px solid #5953FF", fontWeight: 500 },
|
|
491
|
+
children: isPrivate ? "PRIVATE" : "GROUP"
|
|
492
|
+
}
|
|
493
|
+
)
|
|
494
|
+
] }),
|
|
499
495
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
500
496
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
501
497
|
available,
|
|
@@ -568,7 +564,9 @@ function CalendarFoldIcon() {
|
|
|
568
564
|
}
|
|
569
565
|
);
|
|
570
566
|
}
|
|
571
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
567
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
|
|
568
|
+
const autoId = (0, import_react2.useId)();
|
|
569
|
+
const insertAfter = insertAfterProp ?? autoId;
|
|
572
570
|
const [schedule, setSchedule] = (0, import_react2.useState)(null);
|
|
573
571
|
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
574
572
|
const [inEditor, setInEditor] = (0, import_react2.useState)(false);
|
|
@@ -598,88 +596,99 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
598
596
|
if (typeof window === "undefined") return;
|
|
599
597
|
const isInEditor = window.self !== window.top;
|
|
600
598
|
setInEditor(isInEditor);
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
599
|
+
function loadWithId(effectiveId) {
|
|
600
|
+
if (isInEditor) {
|
|
601
|
+
const startEmpty = effectiveId === null;
|
|
602
|
+
if (startEmpty) setLoading(false);
|
|
603
|
+
let initialized = startEmpty;
|
|
604
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
605
|
+
if (!initialized) {
|
|
606
|
+
initialized = true;
|
|
607
|
+
setLoading(false);
|
|
608
|
+
}
|
|
609
|
+
}, 5e3) : null;
|
|
610
|
+
const handler = (e) => {
|
|
611
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
612
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
613
|
+
setSchedule(null);
|
|
614
|
+
setLoading(false);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
618
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
619
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
620
|
+
initialized = false;
|
|
621
|
+
setLoading(true);
|
|
622
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
626
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
627
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
607
628
|
initialized = true;
|
|
629
|
+
if (timer) clearTimeout(timer);
|
|
630
|
+
const schedules = e.data.schedules ?? [];
|
|
631
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
632
|
+
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;
|
|
633
|
+
switchScheduleIdRef.current = null;
|
|
634
|
+
setSchedule(target);
|
|
608
635
|
setLoading(false);
|
|
636
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
637
|
+
window.parent.postMessage({
|
|
638
|
+
type: "ow:schedule-connected",
|
|
639
|
+
schedule: { id: target.id, name: target.name },
|
|
640
|
+
insertAfter
|
|
641
|
+
}, "*");
|
|
642
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
window.addEventListener("message", handler);
|
|
646
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
647
|
+
return () => {
|
|
648
|
+
window.removeEventListener("message", handler);
|
|
649
|
+
if (timer) clearTimeout(timer);
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (!effectiveId) {
|
|
653
|
+
setLoading(false);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
;
|
|
657
|
+
(async () => {
|
|
658
|
+
try {
|
|
659
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
660
|
+
const data = await res.json();
|
|
661
|
+
setSchedule(data?.id ? data : null);
|
|
662
|
+
} catch {
|
|
609
663
|
}
|
|
610
|
-
}, 5e3) : null;
|
|
611
|
-
const handler = (e) => {
|
|
612
|
-
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
613
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
614
|
-
setSchedule(null);
|
|
615
|
-
setLoading(false);
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
if (e.data?.type === "ow:switch-schedule") {
|
|
619
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
620
|
-
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
621
|
-
initialized = false;
|
|
622
|
-
setLoading(true);
|
|
623
|
-
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
627
|
-
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
628
|
-
if (initialized && switchScheduleIdRef.current === null) return;
|
|
629
|
-
initialized = true;
|
|
630
|
-
if (timer) clearTimeout(timer);
|
|
631
|
-
const schedules = e.data.schedules ?? [];
|
|
632
|
-
const isSwitching = switchScheduleIdRef.current !== null;
|
|
633
|
-
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;
|
|
634
|
-
switchScheduleIdRef.current = null;
|
|
635
|
-
setSchedule(target);
|
|
636
664
|
setLoading(false);
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
665
|
+
})();
|
|
666
|
+
}
|
|
667
|
+
if (initialScheduleId === void 0) {
|
|
668
|
+
let done = false;
|
|
669
|
+
let innerCleanup;
|
|
670
|
+
const timer = setTimeout(() => {
|
|
671
|
+
if (!done) {
|
|
672
|
+
done = true;
|
|
673
|
+
setLoading(false);
|
|
644
674
|
}
|
|
675
|
+
}, 2e3);
|
|
676
|
+
const configHandler = (e) => {
|
|
677
|
+
if (e.data?.type !== "ow:schedule-config" || e.data.insertAfter !== insertAfter || done) return;
|
|
678
|
+
done = true;
|
|
679
|
+
clearTimeout(timer);
|
|
680
|
+
window.removeEventListener("message", configHandler);
|
|
681
|
+
innerCleanup = loadWithId(e.data.scheduleId ?? null);
|
|
645
682
|
};
|
|
646
|
-
window.addEventListener("message",
|
|
647
|
-
|
|
683
|
+
window.addEventListener("message", configHandler);
|
|
684
|
+
window.postMessage({ type: "ow:request-schedule-config", insertAfter }, "*");
|
|
648
685
|
return () => {
|
|
649
|
-
window.removeEventListener("message",
|
|
650
|
-
|
|
686
|
+
window.removeEventListener("message", configHandler);
|
|
687
|
+
clearTimeout(timer);
|
|
688
|
+
innerCleanup?.();
|
|
651
689
|
};
|
|
652
690
|
}
|
|
653
|
-
|
|
654
|
-
setLoading(false);
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
;
|
|
658
|
-
(async () => {
|
|
659
|
-
try {
|
|
660
|
-
if (initialScheduleId) {
|
|
661
|
-
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
662
|
-
const data = await res.json();
|
|
663
|
-
setSchedule(data?.id ? data : null);
|
|
664
|
-
} else {
|
|
665
|
-
const domain = getApiDomain();
|
|
666
|
-
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
667
|
-
const slugs = await sitemapRes.json();
|
|
668
|
-
if (!Array.isArray(slugs) || !slugs.length) {
|
|
669
|
-
setLoading(false);
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
const { slug } = [...slugs].sort(
|
|
673
|
-
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
674
|
-
)[0];
|
|
675
|
-
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
676
|
-
const data = await res.json();
|
|
677
|
-
setSchedule(data?.id ? data : null);
|
|
678
|
-
}
|
|
679
|
-
} catch {
|
|
680
|
-
}
|
|
681
|
-
setLoading(false);
|
|
682
|
-
})();
|
|
691
|
+
return loadWithId(initialScheduleId) ?? void 0;
|
|
683
692
|
}, []);
|
|
684
693
|
const timezoneLabel = schedule?.timezone ? (() => {
|
|
685
694
|
try {
|
|
@@ -4379,10 +4388,14 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
4379
4388
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4380
4389
|
if (inEditor) getSectionsTracker().textContent = raw;
|
|
4381
4390
|
const pageEntries = getPageSchedulingEntries(raw);
|
|
4391
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
4382
4392
|
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
4383
4393
|
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
4384
4394
|
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
4385
4395
|
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
4396
|
+
for (const entry of preExisting) {
|
|
4397
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4398
|
+
}
|
|
4386
4399
|
} catch {
|
|
4387
4400
|
}
|
|
4388
4401
|
}
|
|
@@ -4836,6 +4849,8 @@ function OhhwellsBridge() {
|
|
|
4836
4849
|
});
|
|
4837
4850
|
const postToParentRef = (0, import_react3.useRef)(postToParent);
|
|
4838
4851
|
postToParentRef.current = postToParent;
|
|
4852
|
+
const sectionsLoadedRef = (0, import_react3.useRef)(false);
|
|
4853
|
+
const pendingScheduleConfigRequests = (0, import_react3.useRef)([]);
|
|
4839
4854
|
const [toolbarRect, setToolbarRect] = (0, import_react3.useState)(null);
|
|
4840
4855
|
const [toggleState, setToggleState] = (0, import_react3.useState)(null);
|
|
4841
4856
|
const [maxBadge, setMaxBadge] = (0, import_react3.useState)(null);
|
|
@@ -4868,6 +4883,29 @@ function OhhwellsBridge() {
|
|
|
4868
4883
|
const refreshStateRules = (0, import_react3.useCallback)(() => {
|
|
4869
4884
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4870
4885
|
}, []);
|
|
4886
|
+
const processConfigRequest = (0, import_react3.useCallback)((insertAfterVal) => {
|
|
4887
|
+
const tracker = getSectionsTracker();
|
|
4888
|
+
let entries = [];
|
|
4889
|
+
try {
|
|
4890
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
4891
|
+
} catch {
|
|
4892
|
+
}
|
|
4893
|
+
const path = window.location.pathname;
|
|
4894
|
+
const entry = entries.find(
|
|
4895
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
4896
|
+
);
|
|
4897
|
+
if (entry) {
|
|
4898
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4899
|
+
return;
|
|
4900
|
+
}
|
|
4901
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
4902
|
+
entries.push(newEntry);
|
|
4903
|
+
tracker.textContent = JSON.stringify(entries);
|
|
4904
|
+
if (isEditMode) {
|
|
4905
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
4906
|
+
}
|
|
4907
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
4908
|
+
}, [isEditMode]);
|
|
4871
4909
|
const deactivate = (0, import_react3.useCallback)(() => {
|
|
4872
4910
|
const el = activeElRef.current;
|
|
4873
4911
|
if (!el) return;
|
|
@@ -4939,6 +4977,8 @@ function OhhwellsBridge() {
|
|
|
4939
4977
|
});
|
|
4940
4978
|
}
|
|
4941
4979
|
initSectionsFromContent(content, true);
|
|
4980
|
+
sectionsLoadedRef.current = true;
|
|
4981
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
4942
4982
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
4943
4983
|
}) : Promise.resolve();
|
|
4944
4984
|
};
|
|
@@ -5588,6 +5628,8 @@ function OhhwellsBridge() {
|
|
|
5588
5628
|
}
|
|
5589
5629
|
if (sectionsJson) {
|
|
5590
5630
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
5631
|
+
sectionsLoadedRef.current = true;
|
|
5632
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5591
5633
|
}
|
|
5592
5634
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5593
5635
|
};
|
|
@@ -5851,6 +5893,22 @@ function OhhwellsBridge() {
|
|
|
5851
5893
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5852
5894
|
};
|
|
5853
5895
|
}, [isEditMode, refreshStateRules]);
|
|
5896
|
+
(0, import_react3.useEffect)(() => {
|
|
5897
|
+
const handler = (e) => {
|
|
5898
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
5899
|
+
const insertAfterVal = e.data.insertAfter;
|
|
5900
|
+
if (!insertAfterVal) return;
|
|
5901
|
+
if (!sectionsLoadedRef.current) {
|
|
5902
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
5903
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
5904
|
+
}
|
|
5905
|
+
return;
|
|
5906
|
+
}
|
|
5907
|
+
processConfigRequest(insertAfterVal);
|
|
5908
|
+
};
|
|
5909
|
+
window.addEventListener("message", handler);
|
|
5910
|
+
return () => window.removeEventListener("message", handler);
|
|
5911
|
+
}, [processConfigRequest]);
|
|
5854
5912
|
(0, import_react3.useEffect)(() => {
|
|
5855
5913
|
if (!isEditMode) return;
|
|
5856
5914
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|