@ohhwells/bridge 0.1.18 → 0.1.20
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/dist/index.cjs +891 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +880 -66
- package/dist/index.js.map +1 -1
- package/dist/styles.css +366 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,670 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
4
|
+
import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
5
|
+
import { createRoot } from "react-dom/client";
|
|
6
|
+
|
|
7
|
+
// src/ui/SchedulingWidget.tsx
|
|
8
|
+
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
9
|
+
|
|
10
|
+
// src/ui/EmailCaptureModal.tsx
|
|
11
|
+
import { useState } from "react";
|
|
12
|
+
import { Dialog } from "radix-ui";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function Spinner() {
|
|
15
|
+
return /* @__PURE__ */ jsxs(
|
|
16
|
+
"svg",
|
|
17
|
+
{
|
|
18
|
+
width: "32",
|
|
19
|
+
height: "32",
|
|
20
|
+
viewBox: "0 0 32 32",
|
|
21
|
+
fill: "none",
|
|
22
|
+
style: { animation: "ohw-spin 0.75s linear infinite" },
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
|
|
25
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
26
|
+
/* @__PURE__ */ jsx(
|
|
27
|
+
"path",
|
|
28
|
+
{
|
|
29
|
+
d: "M16 4a12 12 0 0 1 12 12",
|
|
30
|
+
stroke: "#3b82f6",
|
|
31
|
+
strokeWidth: "3",
|
|
32
|
+
strokeLinecap: "round"
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
40
|
+
const [email, setEmail] = useState("");
|
|
41
|
+
const [submittedEmail, setSubmittedEmail] = useState("");
|
|
42
|
+
const [loading, setLoading] = useState(false);
|
|
43
|
+
const [success, setSuccess] = useState(false);
|
|
44
|
+
const [error, setError] = useState(null);
|
|
45
|
+
const isBook = title === "Confirm your spot";
|
|
46
|
+
const handleSubmit = async () => {
|
|
47
|
+
if (!email.trim()) return;
|
|
48
|
+
setLoading(true);
|
|
49
|
+
setError(null);
|
|
50
|
+
try {
|
|
51
|
+
await onSubmit(email.trim());
|
|
52
|
+
setSubmittedEmail(email.trim());
|
|
53
|
+
setSuccess(true);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
56
|
+
} finally {
|
|
57
|
+
setLoading(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const handleKeyDown = (e) => {
|
|
61
|
+
if (e.key === "Enter" && !loading) handleSubmit();
|
|
62
|
+
};
|
|
63
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open: true, onOpenChange: (open) => {
|
|
64
|
+
if (!open) onClose();
|
|
65
|
+
}, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
66
|
+
/* @__PURE__ */ jsx(
|
|
67
|
+
Dialog.Overlay,
|
|
68
|
+
{
|
|
69
|
+
className: "fixed inset-0 z-50",
|
|
70
|
+
style: { background: "rgba(0,0,0,0.45)" }
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/* @__PURE__ */ jsxs(
|
|
74
|
+
Dialog.Content,
|
|
75
|
+
{
|
|
76
|
+
className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
|
|
77
|
+
style: { maxWidth: 400 },
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6", children: [
|
|
80
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
Dialog.Title,
|
|
83
|
+
{
|
|
84
|
+
className: "font-body text-xl font-bold m-0 leading-tight",
|
|
85
|
+
style: { color: "var(--color-dark,#200C02)" },
|
|
86
|
+
children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
Dialog.Description,
|
|
91
|
+
{
|
|
92
|
+
className: "font-body text-sm m-0",
|
|
93
|
+
style: { color: "#6B7280" },
|
|
94
|
+
children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
Dialog.Close,
|
|
100
|
+
{
|
|
101
|
+
className: "shrink-0 w-7 h-7 flex items-center justify-center rounded-full bg-transparent border-none cursor-pointer mt-0.5 absolute top-2.5 right-1.5",
|
|
102
|
+
style: { color: "#6B7280" },
|
|
103
|
+
"aria-label": "Close",
|
|
104
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
105
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
106
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
107
|
+
] })
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] }),
|
|
111
|
+
!success && (loading ? (
|
|
112
|
+
/* Loading state — full body replaced */
|
|
113
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
|
|
114
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
115
|
+
/* @__PURE__ */ jsx("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
|
|
116
|
+
] })
|
|
117
|
+
) : (
|
|
118
|
+
/* Email form */
|
|
119
|
+
/* @__PURE__ */ jsxs("div", { className: "px-7 pt-2 pb-6", children: [
|
|
120
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 mb-8", children: [
|
|
121
|
+
/* @__PURE__ */ jsx("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
|
|
122
|
+
/* @__PURE__ */ jsx(
|
|
123
|
+
"input",
|
|
124
|
+
{
|
|
125
|
+
type: "email",
|
|
126
|
+
value: email,
|
|
127
|
+
onChange: (e) => setEmail(e.target.value),
|
|
128
|
+
onKeyDown: handleKeyDown,
|
|
129
|
+
placeholder: "hello@gmail.com",
|
|
130
|
+
autoFocus: true,
|
|
131
|
+
className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
|
|
132
|
+
style: {
|
|
133
|
+
borderColor: "#E7E5E4",
|
|
134
|
+
background: "#fff",
|
|
135
|
+
color: "var(--color-dark,#200C02)"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
error && /* @__PURE__ */ jsx("p", { className: "font-body text-xs text-red-500 m-0", children: error })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
142
|
+
"button",
|
|
143
|
+
{
|
|
144
|
+
onClick: handleSubmit,
|
|
145
|
+
disabled: !email.trim(),
|
|
146
|
+
className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
|
|
147
|
+
style: {
|
|
148
|
+
background: "var(--color-primary,#3D312B)",
|
|
149
|
+
color: "var(--color-light,#FEFFF0)"
|
|
150
|
+
},
|
|
151
|
+
children: "Send"
|
|
152
|
+
}
|
|
153
|
+
) })
|
|
154
|
+
] })
|
|
155
|
+
))
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] }) });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/ui/SchedulingWidget.tsx
|
|
163
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
164
|
+
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
165
|
+
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
166
|
+
function getApiDomain() {
|
|
167
|
+
const h = window.location.hostname;
|
|
168
|
+
for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
|
|
169
|
+
if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
|
|
170
|
+
}
|
|
171
|
+
return h;
|
|
172
|
+
}
|
|
173
|
+
function classRunsOnDate(cls, date, type) {
|
|
174
|
+
if (type === "FIXED") {
|
|
175
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
176
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
177
|
+
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
178
|
+
}
|
|
179
|
+
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
180
|
+
}
|
|
181
|
+
function formatClassTime(cls) {
|
|
182
|
+
let h = parseInt(cls.startTime.hour, 10);
|
|
183
|
+
const m = parseInt(cls.startTime.minutes, 10);
|
|
184
|
+
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
185
|
+
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
186
|
+
const endTotal = h * 60 + m + cls.duration;
|
|
187
|
+
const eh = Math.floor(endTotal / 60) % 24;
|
|
188
|
+
const em = endTotal % 60;
|
|
189
|
+
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
190
|
+
}
|
|
191
|
+
function getBookingsOnDate(cls, date) {
|
|
192
|
+
if (!cls.bookings?.length) return 0;
|
|
193
|
+
const ds = date.toISOString().split("T")[0];
|
|
194
|
+
return cls.bookings.filter((b) => {
|
|
195
|
+
try {
|
|
196
|
+
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}).length;
|
|
201
|
+
}
|
|
202
|
+
function buildTimezoneLabel(tz) {
|
|
203
|
+
try {
|
|
204
|
+
const now = /* @__PURE__ */ new Date();
|
|
205
|
+
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
206
|
+
hour: "2-digit",
|
|
207
|
+
minute: "2-digit",
|
|
208
|
+
hour12: false,
|
|
209
|
+
timeZone: tz
|
|
210
|
+
}).format(now);
|
|
211
|
+
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
212
|
+
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
213
|
+
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
214
|
+
} catch {
|
|
215
|
+
return tz;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function EmptyState({ inEditor }) {
|
|
219
|
+
const handleAddSchedule = () => {
|
|
220
|
+
if (inEditor) {
|
|
221
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
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: [
|
|
225
|
+
/* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
|
|
226
|
+
"svg",
|
|
227
|
+
{
|
|
228
|
+
width: "20",
|
|
229
|
+
height: "20",
|
|
230
|
+
viewBox: "0 0 24 24",
|
|
231
|
+
fill: "none",
|
|
232
|
+
stroke: "var(--color-accent, #A89B83)",
|
|
233
|
+
strokeWidth: "1.5",
|
|
234
|
+
strokeLinecap: "round",
|
|
235
|
+
strokeLinejoin: "round",
|
|
236
|
+
children: [
|
|
237
|
+
/* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
238
|
+
/* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
239
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
240
|
+
/* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
241
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
242
|
+
/* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
) }),
|
|
246
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
|
|
247
|
+
/* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
248
|
+
/* @__PURE__ */ jsx2("p", { className: "font-body text-base font-normal text-center text-(--color-accent,#A89B83) m-0", children: "Add a scheduling widget to get instant bookings." })
|
|
249
|
+
] }),
|
|
250
|
+
/* @__PURE__ */ jsx2(
|
|
251
|
+
"button",
|
|
252
|
+
{
|
|
253
|
+
onClick: handleAddSchedule,
|
|
254
|
+
className: "h-9 px-2 py-1.5 flex items-center justify-center bg-white border border-[#E7E5E4] rounded-md font-body text-sm font-medium text-(--color-dark,#200C02) cursor-pointer",
|
|
255
|
+
children: "Add Schedule"
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
] });
|
|
259
|
+
}
|
|
260
|
+
function LoadingSkeleton() {
|
|
261
|
+
const shimmer = {
|
|
262
|
+
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
263
|
+
backgroundSize: "200% 100%",
|
|
264
|
+
animation: "ohw-shimmer 1.5s infinite"
|
|
265
|
+
};
|
|
266
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
267
|
+
/* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
268
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
|
|
269
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
270
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
271
|
+
] }, i)) }),
|
|
272
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
273
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-[60px] py-4", children: [
|
|
274
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
275
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
276
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
277
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
278
|
+
] }),
|
|
279
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
280
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
281
|
+
] }),
|
|
282
|
+
i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
283
|
+
] }, i))
|
|
284
|
+
] });
|
|
285
|
+
}
|
|
286
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
287
|
+
const todayMs = useMemo(() => {
|
|
288
|
+
const d = /* @__PURE__ */ new Date();
|
|
289
|
+
d.setHours(0, 0, 0, 0);
|
|
290
|
+
return d.getTime();
|
|
291
|
+
}, []);
|
|
292
|
+
const scrollRef = useRef(null);
|
|
293
|
+
const [canScrollLeft, setCanScrollLeft] = useState2(false);
|
|
294
|
+
const [canScrollRight, setCanScrollRight] = useState2(false);
|
|
295
|
+
const updateScrollState = () => {
|
|
296
|
+
const el = scrollRef.current;
|
|
297
|
+
if (!el) return;
|
|
298
|
+
setCanScrollLeft(el.scrollLeft > 0);
|
|
299
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
300
|
+
};
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const el = scrollRef.current;
|
|
303
|
+
if (!el) return;
|
|
304
|
+
updateScrollState();
|
|
305
|
+
el.addEventListener("scroll", updateScrollState);
|
|
306
|
+
const ro = new ResizeObserver(updateScrollState);
|
|
307
|
+
ro.observe(el);
|
|
308
|
+
return () => {
|
|
309
|
+
el.removeEventListener("scroll", updateScrollState);
|
|
310
|
+
ro.disconnect();
|
|
311
|
+
};
|
|
312
|
+
}, [dates]);
|
|
313
|
+
const scroll = (dir) => {
|
|
314
|
+
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
315
|
+
};
|
|
316
|
+
const datesWithClasses = useMemo(
|
|
317
|
+
() => new Set(dates.map(
|
|
318
|
+
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
319
|
+
).filter((i) => i >= 0)),
|
|
320
|
+
[dates, schedule]
|
|
321
|
+
);
|
|
322
|
+
const selectedDate = dates[selectedIdx];
|
|
323
|
+
const selectedClasses = useMemo(
|
|
324
|
+
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
325
|
+
[schedule, selectedDate]
|
|
326
|
+
);
|
|
327
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
328
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
|
|
329
|
+
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
330
|
+
"button",
|
|
331
|
+
{
|
|
332
|
+
onClick: () => scroll("left"),
|
|
333
|
+
className: "absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
|
|
334
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
335
|
+
"aria-label": "Scroll left",
|
|
336
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "15 18 9 12 15 6" }) })
|
|
337
|
+
}
|
|
338
|
+
),
|
|
339
|
+
canScrollRight && /* @__PURE__ */ jsx2(
|
|
340
|
+
"button",
|
|
341
|
+
{
|
|
342
|
+
onClick: () => scroll("right"),
|
|
343
|
+
className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
|
|
344
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
345
|
+
"aria-label": "Scroll right",
|
|
346
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "9 18 15 12 9 6" }) })
|
|
347
|
+
}
|
|
348
|
+
),
|
|
349
|
+
/* @__PURE__ */ jsx2(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
ref: scrollRef,
|
|
353
|
+
className: "overflow-x-auto w-full",
|
|
354
|
+
style: {
|
|
355
|
+
scrollbarWidth: "none",
|
|
356
|
+
msOverflowStyle: "none",
|
|
357
|
+
paddingLeft: canScrollLeft ? "36px" : 0,
|
|
358
|
+
paddingRight: canScrollRight ? "36px" : 0
|
|
359
|
+
},
|
|
360
|
+
children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
361
|
+
const isToday = date.getTime() === todayMs;
|
|
362
|
+
const isSelected = i === selectedIdx;
|
|
363
|
+
const clickable = datesWithClasses.has(i);
|
|
364
|
+
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
365
|
+
return /* @__PURE__ */ jsxs2(
|
|
366
|
+
"div",
|
|
367
|
+
{
|
|
368
|
+
onClick: () => clickable && onSelectDate(i),
|
|
369
|
+
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
372
|
+
/* @__PURE__ */ jsx2(
|
|
373
|
+
"div",
|
|
374
|
+
{
|
|
375
|
+
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
376
|
+
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
377
|
+
children: /* @__PURE__ */ jsx2(
|
|
378
|
+
"span",
|
|
379
|
+
{
|
|
380
|
+
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
381
|
+
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
382
|
+
children: date.getDate()
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
)
|
|
387
|
+
]
|
|
388
|
+
},
|
|
389
|
+
i
|
|
390
|
+
);
|
|
391
|
+
}) })
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
] }),
|
|
395
|
+
selectedClasses.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
|
|
396
|
+
const booked = getBookingsOnDate(cls, selectedDate);
|
|
397
|
+
const available = cls.maxParticipants - booked;
|
|
398
|
+
const isFull = available <= 0;
|
|
399
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
400
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
|
|
401
|
+
/* @__PURE__ */ jsx2("div", { className: "w-[120px] shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
402
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
|
|
403
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
404
|
+
cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
405
|
+
] }),
|
|
406
|
+
/* @__PURE__ */ jsx2("div", { className: "w-14 shrink-0 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: [
|
|
407
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
408
|
+
available,
|
|
409
|
+
"/",
|
|
410
|
+
cls.maxParticipants
|
|
411
|
+
] }),
|
|
412
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
413
|
+
] }) }),
|
|
414
|
+
/* @__PURE__ */ jsx2(
|
|
415
|
+
"button",
|
|
416
|
+
{
|
|
417
|
+
className: "shrink-0 w-[200px] px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
|
|
418
|
+
style: isFull ? {
|
|
419
|
+
background: "transparent",
|
|
420
|
+
border: "1px solid var(--color-dark, #200C02)",
|
|
421
|
+
color: "var(--color-dark, #200C02)"
|
|
422
|
+
} : {
|
|
423
|
+
background: "var(--color-primary, #3D312B)",
|
|
424
|
+
border: "none",
|
|
425
|
+
color: "var(--color-light, #FEFFF0)"
|
|
426
|
+
},
|
|
427
|
+
onClick: () => {
|
|
428
|
+
if (!cls.id) return;
|
|
429
|
+
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
430
|
+
},
|
|
431
|
+
children: isFull ? "Join Waitlist" : "Book Now"
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
] }),
|
|
435
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
436
|
+
] }, cls.id ?? i);
|
|
437
|
+
}) })
|
|
438
|
+
] });
|
|
439
|
+
}
|
|
440
|
+
function CalendarFoldIcon() {
|
|
441
|
+
return /* @__PURE__ */ jsxs2(
|
|
442
|
+
"svg",
|
|
443
|
+
{
|
|
444
|
+
width: "16",
|
|
445
|
+
height: "16",
|
|
446
|
+
viewBox: "0 0 24 24",
|
|
447
|
+
fill: "none",
|
|
448
|
+
stroke: "currentColor",
|
|
449
|
+
strokeWidth: "2",
|
|
450
|
+
strokeLinecap: "round",
|
|
451
|
+
strokeLinejoin: "round",
|
|
452
|
+
style: { flexShrink: 0 },
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
|
|
455
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
|
|
456
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
|
|
457
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
|
|
458
|
+
/* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
464
|
+
const [schedule, setSchedule] = useState2(null);
|
|
465
|
+
const [loading, setLoading] = useState2(true);
|
|
466
|
+
const [inEditor, setInEditor] = useState2(false);
|
|
467
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
468
|
+
const [modalState, setModalState] = useState2(null);
|
|
469
|
+
const switchScheduleIdRef = useRef(null);
|
|
470
|
+
const dates = useMemo(() => {
|
|
471
|
+
const today = /* @__PURE__ */ new Date();
|
|
472
|
+
today.setHours(0, 0, 0, 0);
|
|
473
|
+
return Array.from({ length: 14 }, (_, i) => {
|
|
474
|
+
const d = new Date(today);
|
|
475
|
+
d.setDate(today.getDate() + i);
|
|
476
|
+
return d;
|
|
477
|
+
});
|
|
478
|
+
}, []);
|
|
479
|
+
const [selectedIdx, setSelectedIdx] = useState2(0);
|
|
480
|
+
useEffect(() => {
|
|
481
|
+
if (!schedule?.classes) return;
|
|
482
|
+
for (let i = 0; i < dates.length; i++) {
|
|
483
|
+
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
484
|
+
setSelectedIdx(i);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}, [schedule, dates]);
|
|
489
|
+
useEffect(() => {
|
|
490
|
+
if (typeof window === "undefined") return;
|
|
491
|
+
const isInEditor = window.self !== window.top;
|
|
492
|
+
setInEditor(isInEditor);
|
|
493
|
+
if (isInEditor) {
|
|
494
|
+
const startEmpty = initialScheduleId === null;
|
|
495
|
+
if (startEmpty) setLoading(false);
|
|
496
|
+
let initialized = startEmpty;
|
|
497
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
498
|
+
if (!initialized) {
|
|
499
|
+
initialized = true;
|
|
500
|
+
setLoading(false);
|
|
501
|
+
}
|
|
502
|
+
}, 5e3) : null;
|
|
503
|
+
const handler = (e) => {
|
|
504
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
505
|
+
setSchedule(null);
|
|
506
|
+
setLoading(false);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
510
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
511
|
+
initialized = false;
|
|
512
|
+
setLoading(true);
|
|
513
|
+
window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
517
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
518
|
+
initialized = true;
|
|
519
|
+
if (timer) clearTimeout(timer);
|
|
520
|
+
const schedules = e.data.schedules ?? [];
|
|
521
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
522
|
+
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;
|
|
523
|
+
switchScheduleIdRef.current = null;
|
|
524
|
+
setSchedule(target);
|
|
525
|
+
setLoading(false);
|
|
526
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
527
|
+
window.parent.postMessage({ type: "ow:schedule-connected", schedule: { id: target.id, name: target.name } }, "*");
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
window.addEventListener("message", handler);
|
|
531
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
|
|
532
|
+
return () => {
|
|
533
|
+
window.removeEventListener("message", handler);
|
|
534
|
+
if (timer) clearTimeout(timer);
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
if (initialScheduleId === null) {
|
|
538
|
+
setLoading(false);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
;
|
|
542
|
+
(async () => {
|
|
543
|
+
try {
|
|
544
|
+
if (initialScheduleId) {
|
|
545
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
546
|
+
const data = await res.json();
|
|
547
|
+
setSchedule(data?.id ? data : null);
|
|
548
|
+
} else {
|
|
549
|
+
const domain = getApiDomain();
|
|
550
|
+
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
551
|
+
const slugs = await sitemapRes.json();
|
|
552
|
+
if (!Array.isArray(slugs) || !slugs.length) {
|
|
553
|
+
setLoading(false);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const { slug } = [...slugs].sort(
|
|
557
|
+
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
558
|
+
)[0];
|
|
559
|
+
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
560
|
+
const data = await res.json();
|
|
561
|
+
setSchedule(data?.id ? data : null);
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
setLoading(false);
|
|
566
|
+
})();
|
|
567
|
+
}, []);
|
|
568
|
+
const timezoneLabel = schedule?.timezone ? (() => {
|
|
569
|
+
try {
|
|
570
|
+
return buildTimezoneLabel(schedule.timezone);
|
|
571
|
+
} catch {
|
|
572
|
+
return void 0;
|
|
573
|
+
}
|
|
574
|
+
})() : void 0;
|
|
575
|
+
const handleModalSubmit = async (email) => {
|
|
576
|
+
if (!modalState) return;
|
|
577
|
+
const { mode, classId, classDate } = modalState;
|
|
578
|
+
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
579
|
+
const res = await fetch(endpoint, {
|
|
580
|
+
method: "POST",
|
|
581
|
+
headers: { "Content-Type": "application/json" },
|
|
582
|
+
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
583
|
+
});
|
|
584
|
+
if (!res.ok) {
|
|
585
|
+
const data = await res.json().catch(() => ({}));
|
|
586
|
+
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
const handleReplaceSchedule = () => {
|
|
590
|
+
setIsHovered(false);
|
|
591
|
+
if (schedule) {
|
|
592
|
+
window.parent.postMessage({ type: "ow:schedule-connected", schedule: { id: schedule.id, name: schedule.name } }, "*");
|
|
593
|
+
} else {
|
|
594
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
return /* @__PURE__ */ jsxs2(
|
|
598
|
+
"section",
|
|
599
|
+
{
|
|
600
|
+
"data-ohw-section": "scheduling",
|
|
601
|
+
className: "w-full box-border py-20 px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
602
|
+
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
603
|
+
onMouseLeave: () => setIsHovered(false),
|
|
604
|
+
children: [
|
|
605
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
|
|
606
|
+
"div",
|
|
607
|
+
{
|
|
608
|
+
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
609
|
+
onClick: handleReplaceSchedule,
|
|
610
|
+
children: [
|
|
611
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
612
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
613
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
|
|
614
|
+
"div",
|
|
615
|
+
{
|
|
616
|
+
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
617
|
+
style: {
|
|
618
|
+
fontSize: 14,
|
|
619
|
+
lineHeight: "24px",
|
|
620
|
+
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
621
|
+
fontWeight: 500,
|
|
622
|
+
color: "#0C0A09"
|
|
623
|
+
},
|
|
624
|
+
children: [
|
|
625
|
+
/* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
|
|
626
|
+
"Replace schedule"
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
) })
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
),
|
|
633
|
+
/* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
634
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
|
|
635
|
+
/* @__PURE__ */ jsx2("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
|
|
636
|
+
schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
637
|
+
] }),
|
|
638
|
+
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
639
|
+
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
640
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
|
|
641
|
+
ScheduleView,
|
|
642
|
+
{
|
|
643
|
+
schedule,
|
|
644
|
+
dates,
|
|
645
|
+
selectedIdx,
|
|
646
|
+
onSelectDate: setSelectedIdx,
|
|
647
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
648
|
+
}
|
|
649
|
+
) })
|
|
650
|
+
] })
|
|
651
|
+
] }),
|
|
652
|
+
modalState && /* @__PURE__ */ jsx2(
|
|
653
|
+
EmailCaptureModal,
|
|
654
|
+
{
|
|
655
|
+
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
656
|
+
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
657
|
+
onSubmit: handleModalSubmit,
|
|
658
|
+
onClose: () => setModalState(null)
|
|
659
|
+
}
|
|
660
|
+
)
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
);
|
|
664
|
+
}
|
|
5
665
|
|
|
6
666
|
// src/ui/toggle-group.tsx
|
|
7
|
-
import * as
|
|
667
|
+
import * as React2 from "react";
|
|
8
668
|
|
|
9
669
|
// node_modules/clsx/dist/clsx.mjs
|
|
10
670
|
function r(e) {
|
|
@@ -3324,7 +3984,7 @@ var cva = (base, config) => (props) => {
|
|
|
3324
3984
|
|
|
3325
3985
|
// src/ui/toggle.tsx
|
|
3326
3986
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
3327
|
-
import { jsx } from "react/jsx-runtime";
|
|
3987
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3328
3988
|
var toggleVariants = cva(
|
|
3329
3989
|
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
3330
3990
|
{
|
|
@@ -3351,7 +4011,7 @@ function Toggle({
|
|
|
3351
4011
|
size,
|
|
3352
4012
|
...props
|
|
3353
4013
|
}) {
|
|
3354
|
-
return /* @__PURE__ */
|
|
4014
|
+
return /* @__PURE__ */ jsx3(
|
|
3355
4015
|
TogglePrimitive.Root,
|
|
3356
4016
|
{
|
|
3357
4017
|
"data-slot": "toggle",
|
|
@@ -3362,8 +4022,8 @@ function Toggle({
|
|
|
3362
4022
|
}
|
|
3363
4023
|
|
|
3364
4024
|
// src/ui/toggle-group.tsx
|
|
3365
|
-
import { jsx as
|
|
3366
|
-
var ToggleGroupContext =
|
|
4025
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4026
|
+
var ToggleGroupContext = React2.createContext({
|
|
3367
4027
|
value: "",
|
|
3368
4028
|
onValueChange: () => {
|
|
3369
4029
|
}
|
|
@@ -3375,13 +4035,13 @@ function ToggleGroup({
|
|
|
3375
4035
|
children,
|
|
3376
4036
|
...props
|
|
3377
4037
|
}) {
|
|
3378
|
-
return /* @__PURE__ */
|
|
4038
|
+
return /* @__PURE__ */ jsx4(
|
|
3379
4039
|
"div",
|
|
3380
4040
|
{
|
|
3381
4041
|
role: "group",
|
|
3382
4042
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3383
4043
|
...props,
|
|
3384
|
-
children: /* @__PURE__ */
|
|
4044
|
+
children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3385
4045
|
}
|
|
3386
4046
|
);
|
|
3387
4047
|
}
|
|
@@ -3391,8 +4051,8 @@ function ToggleGroupItem({
|
|
|
3391
4051
|
children,
|
|
3392
4052
|
...props
|
|
3393
4053
|
}) {
|
|
3394
|
-
const { value: groupValue, onValueChange } =
|
|
3395
|
-
return /* @__PURE__ */
|
|
4054
|
+
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
4055
|
+
return /* @__PURE__ */ jsx4(
|
|
3396
4056
|
Toggle,
|
|
3397
4057
|
{
|
|
3398
4058
|
pressed: groupValue === value,
|
|
@@ -3434,7 +4094,7 @@ function isEditSessionActive() {
|
|
|
3434
4094
|
}
|
|
3435
4095
|
|
|
3436
4096
|
// src/ui/badge.tsx
|
|
3437
|
-
import { jsx as
|
|
4097
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3438
4098
|
var badgeVariants = cva(
|
|
3439
4099
|
"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",
|
|
3440
4100
|
{
|
|
@@ -3452,11 +4112,11 @@ var badgeVariants = cva(
|
|
|
3452
4112
|
}
|
|
3453
4113
|
);
|
|
3454
4114
|
function Badge({ className, variant, ...props }) {
|
|
3455
|
-
return /* @__PURE__ */
|
|
4115
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
3456
4116
|
}
|
|
3457
4117
|
|
|
3458
4118
|
// src/OhhwellsBridge.tsx
|
|
3459
|
-
import { Fragment, jsx as
|
|
4119
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3460
4120
|
var PRIMARY = "#0885FE";
|
|
3461
4121
|
var IMAGE_FADE_MS = 300;
|
|
3462
4122
|
function runOpacityFade(el, onDone) {
|
|
@@ -3507,6 +4167,41 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
3507
4167
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
3508
4168
|
});
|
|
3509
4169
|
}
|
|
4170
|
+
function getSectionsTracker() {
|
|
4171
|
+
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
4172
|
+
if (!el) {
|
|
4173
|
+
el = document.createElement("div");
|
|
4174
|
+
el.setAttribute("data-ohw-sections-tracker", "");
|
|
4175
|
+
el.style.display = "none";
|
|
4176
|
+
document.body.appendChild(el);
|
|
4177
|
+
}
|
|
4178
|
+
return el;
|
|
4179
|
+
}
|
|
4180
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId) {
|
|
4181
|
+
const anchor = document.querySelector(`[data-ohw-section="${insertAfter}"]`);
|
|
4182
|
+
if (!anchor) return false;
|
|
4183
|
+
if (anchor.nextElementSibling?.dataset.ohwSectionContainer) return false;
|
|
4184
|
+
const container = document.createElement("div");
|
|
4185
|
+
container.dataset.ohwSectionContainer = "scheduling";
|
|
4186
|
+
anchor.insertAdjacentElement("afterend", container);
|
|
4187
|
+
createRoot(container).render(/* @__PURE__ */ jsx6(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
|
|
4188
|
+
const tracker = getSectionsTracker();
|
|
4189
|
+
let sections = [];
|
|
4190
|
+
try {
|
|
4191
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
4192
|
+
} catch {
|
|
4193
|
+
}
|
|
4194
|
+
if (!sections.find((s) => s.type === "scheduling" && s.insertAfter === insertAfter)) {
|
|
4195
|
+
sections.push({
|
|
4196
|
+
type: "scheduling",
|
|
4197
|
+
insertAfter,
|
|
4198
|
+
pagePath: window.location.pathname,
|
|
4199
|
+
...scheduleId ? { scheduleId } : {}
|
|
4200
|
+
});
|
|
4201
|
+
tracker.textContent = JSON.stringify(sections);
|
|
4202
|
+
}
|
|
4203
|
+
return true;
|
|
4204
|
+
}
|
|
3510
4205
|
function collectEditableNodes() {
|
|
3511
4206
|
return Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
|
|
3512
4207
|
if (el.dataset.ohwEditable === "image") {
|
|
@@ -3664,7 +4359,7 @@ var TOOLBAR_GROUPS = [
|
|
|
3664
4359
|
];
|
|
3665
4360
|
function GlowFrame({ rect, elRef }) {
|
|
3666
4361
|
const GAP = 6;
|
|
3667
|
-
return /* @__PURE__ */
|
|
4362
|
+
return /* @__PURE__ */ jsx6(
|
|
3668
4363
|
"div",
|
|
3669
4364
|
{
|
|
3670
4365
|
ref: elRef,
|
|
@@ -3715,7 +4410,7 @@ function FloatingToolbar({
|
|
|
3715
4410
|
activeCommands
|
|
3716
4411
|
}) {
|
|
3717
4412
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
3718
|
-
return /* @__PURE__ */
|
|
4413
|
+
return /* @__PURE__ */ jsx6(
|
|
3719
4414
|
"div",
|
|
3720
4415
|
{
|
|
3721
4416
|
ref: elRef,
|
|
@@ -3738,11 +4433,11 @@ function FloatingToolbar({
|
|
|
3738
4433
|
pointerEvents: "auto",
|
|
3739
4434
|
whiteSpace: "nowrap"
|
|
3740
4435
|
},
|
|
3741
|
-
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
3742
|
-
gi > 0 && /* @__PURE__ */
|
|
4436
|
+
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
|
|
4437
|
+
gi > 0 && /* @__PURE__ */ jsx6("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
3743
4438
|
btns.map((btn) => {
|
|
3744
4439
|
const isActive = activeCommands.has(btn.cmd);
|
|
3745
|
-
return /* @__PURE__ */
|
|
4440
|
+
return /* @__PURE__ */ jsx6(
|
|
3746
4441
|
"button",
|
|
3747
4442
|
{
|
|
3748
4443
|
title: btn.title,
|
|
@@ -3768,7 +4463,7 @@ function FloatingToolbar({
|
|
|
3768
4463
|
flexShrink: 0,
|
|
3769
4464
|
padding: 6
|
|
3770
4465
|
},
|
|
3771
|
-
children: /* @__PURE__ */
|
|
4466
|
+
children: /* @__PURE__ */ jsx6(
|
|
3772
4467
|
"svg",
|
|
3773
4468
|
{
|
|
3774
4469
|
width: "16",
|
|
@@ -3801,7 +4496,7 @@ function StateToggle({
|
|
|
3801
4496
|
states,
|
|
3802
4497
|
onStateChange
|
|
3803
4498
|
}) {
|
|
3804
|
-
return /* @__PURE__ */
|
|
4499
|
+
return /* @__PURE__ */ jsx6(
|
|
3805
4500
|
ToggleGroup,
|
|
3806
4501
|
{
|
|
3807
4502
|
"data-ohw-state-toggle": "",
|
|
@@ -3815,7 +4510,7 @@ function StateToggle({
|
|
|
3815
4510
|
left: rect.right - 8,
|
|
3816
4511
|
transform: "translateX(-100%)"
|
|
3817
4512
|
},
|
|
3818
|
-
children: states.map((state) => /* @__PURE__ */
|
|
4513
|
+
children: states.map((state) => /* @__PURE__ */ jsx6(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
3819
4514
|
}
|
|
3820
4515
|
);
|
|
3821
4516
|
}
|
|
@@ -3825,8 +4520,8 @@ function OhhwellsBridge() {
|
|
|
3825
4520
|
const router = useRouter();
|
|
3826
4521
|
const searchParams = useSearchParams();
|
|
3827
4522
|
const isEditMode = isEditSessionActive();
|
|
3828
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
3829
|
-
|
|
4523
|
+
const [bridgeRoot, setBridgeRoot] = useState3(null);
|
|
4524
|
+
useEffect2(() => {
|
|
3830
4525
|
const figtreeFontId = "ohw-figtree-font";
|
|
3831
4526
|
if (!document.getElementById(figtreeFontId)) {
|
|
3832
4527
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -3859,34 +4554,34 @@ function OhhwellsBridge() {
|
|
|
3859
4554
|
window.parent.postMessage(data, "*");
|
|
3860
4555
|
}
|
|
3861
4556
|
}, []);
|
|
3862
|
-
const [fetchState, setFetchState] =
|
|
3863
|
-
const autoSaveTimers =
|
|
3864
|
-
const activeElRef =
|
|
3865
|
-
const originalContentRef =
|
|
3866
|
-
const activeStateElRef =
|
|
3867
|
-
const parentScrollRef =
|
|
3868
|
-
const toolbarElRef =
|
|
3869
|
-
const glowElRef =
|
|
3870
|
-
const hoveredImageRef =
|
|
3871
|
-
const hoveredImageHasTextOverlapRef =
|
|
3872
|
-
const hoveredGapRef =
|
|
3873
|
-
const imageUnhoverTimerRef =
|
|
3874
|
-
const imageShowTimerRef =
|
|
3875
|
-
const editStylesRef =
|
|
3876
|
-
const activateRef =
|
|
4557
|
+
const [fetchState, setFetchState] = useState3("idle");
|
|
4558
|
+
const autoSaveTimers = useRef2(/* @__PURE__ */ new Map());
|
|
4559
|
+
const activeElRef = useRef2(null);
|
|
4560
|
+
const originalContentRef = useRef2(null);
|
|
4561
|
+
const activeStateElRef = useRef2(null);
|
|
4562
|
+
const parentScrollRef = useRef2(null);
|
|
4563
|
+
const toolbarElRef = useRef2(null);
|
|
4564
|
+
const glowElRef = useRef2(null);
|
|
4565
|
+
const hoveredImageRef = useRef2(null);
|
|
4566
|
+
const hoveredImageHasTextOverlapRef = useRef2(false);
|
|
4567
|
+
const hoveredGapRef = useRef2(null);
|
|
4568
|
+
const imageUnhoverTimerRef = useRef2(null);
|
|
4569
|
+
const imageShowTimerRef = useRef2(null);
|
|
4570
|
+
const editStylesRef = useRef2(null);
|
|
4571
|
+
const activateRef = useRef2(() => {
|
|
3877
4572
|
});
|
|
3878
|
-
const deactivateRef =
|
|
4573
|
+
const deactivateRef = useRef2(() => {
|
|
3879
4574
|
});
|
|
3880
|
-
const refreshActiveCommandsRef =
|
|
4575
|
+
const refreshActiveCommandsRef = useRef2(() => {
|
|
3881
4576
|
});
|
|
3882
|
-
const postToParentRef =
|
|
4577
|
+
const postToParentRef = useRef2(postToParent);
|
|
3883
4578
|
postToParentRef.current = postToParent;
|
|
3884
|
-
const [toolbarRect, setToolbarRect] =
|
|
3885
|
-
const [toggleState, setToggleState] =
|
|
3886
|
-
const [maxBadge, setMaxBadge] =
|
|
3887
|
-
const [activeCommands, setActiveCommands] =
|
|
3888
|
-
const [sectionGap, setSectionGap] =
|
|
3889
|
-
|
|
4579
|
+
const [toolbarRect, setToolbarRect] = useState3(null);
|
|
4580
|
+
const [toggleState, setToggleState] = useState3(null);
|
|
4581
|
+
const [maxBadge, setMaxBadge] = useState3(null);
|
|
4582
|
+
const [activeCommands, setActiveCommands] = useState3(/* @__PURE__ */ new Set());
|
|
4583
|
+
const [sectionGap, setSectionGap] = useState3(null);
|
|
4584
|
+
useEffect2(() => {
|
|
3890
4585
|
const update = () => {
|
|
3891
4586
|
const el = activeElRef.current;
|
|
3892
4587
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -3963,7 +4658,20 @@ function OhhwellsBridge() {
|
|
|
3963
4658
|
}
|
|
3964
4659
|
const applyContent = (content) => {
|
|
3965
4660
|
const imageLoads = [];
|
|
4661
|
+
document.querySelectorAll("[data-ohw-section-container]").forEach((el) => el.remove());
|
|
4662
|
+
if (content["__ohw_sections"]) {
|
|
4663
|
+
try {
|
|
4664
|
+
const entries = JSON.parse(content["__ohw_sections"]);
|
|
4665
|
+
const currentPath = window.location.pathname;
|
|
4666
|
+
entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
|
|
4667
|
+
if (pagePath && pagePath !== currentPath) return;
|
|
4668
|
+
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
4669
|
+
});
|
|
4670
|
+
} catch {
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
3966
4673
|
for (const [key, val] of Object.entries(content)) {
|
|
4674
|
+
if (key === "__ohw_sections") continue;
|
|
3967
4675
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
3968
4676
|
if (el.dataset.ohwEditable === "image") {
|
|
3969
4677
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -4006,12 +4714,24 @@ function OhhwellsBridge() {
|
|
|
4006
4714
|
cancelled = true;
|
|
4007
4715
|
};
|
|
4008
4716
|
}, [subdomain, isEditMode, pathname]);
|
|
4009
|
-
|
|
4717
|
+
useEffect2(() => {
|
|
4010
4718
|
if (!subdomain || isEditMode) return;
|
|
4011
4719
|
const applyFromCache = () => {
|
|
4012
4720
|
const content = contentCache.get(subdomain);
|
|
4013
4721
|
if (!content) return;
|
|
4722
|
+
if (content["__ohw_sections"]) {
|
|
4723
|
+
try {
|
|
4724
|
+
const entries = JSON.parse(content["__ohw_sections"]);
|
|
4725
|
+
const currentPath = window.location.pathname;
|
|
4726
|
+
entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
|
|
4727
|
+
if (pagePath && pagePath !== currentPath) return;
|
|
4728
|
+
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
4729
|
+
});
|
|
4730
|
+
} catch {
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4014
4733
|
for (const [key, val] of Object.entries(content)) {
|
|
4734
|
+
if (key === "__ohw_sections") continue;
|
|
4015
4735
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
4016
4736
|
if (el.dataset.ohwEditable === "image") {
|
|
4017
4737
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -4036,10 +4756,10 @@ function OhhwellsBridge() {
|
|
|
4036
4756
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
4037
4757
|
el.style.display = visible ? "flex" : "none";
|
|
4038
4758
|
}, [subdomain, fetchState]);
|
|
4039
|
-
|
|
4759
|
+
useEffect2(() => {
|
|
4040
4760
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
4041
4761
|
}, [pathname, postToParent]);
|
|
4042
|
-
|
|
4762
|
+
useEffect2(() => {
|
|
4043
4763
|
if (!isEditMode) return;
|
|
4044
4764
|
const measure = () => {
|
|
4045
4765
|
const h = document.body.scrollHeight;
|
|
@@ -4063,7 +4783,7 @@ function OhhwellsBridge() {
|
|
|
4063
4783
|
window.removeEventListener("resize", handleResize);
|
|
4064
4784
|
};
|
|
4065
4785
|
}, [pathname, isEditMode, postToParent]);
|
|
4066
|
-
|
|
4786
|
+
useEffect2(() => {
|
|
4067
4787
|
if (!subdomainFromQuery || isEditMode) return;
|
|
4068
4788
|
const handleClick = (e) => {
|
|
4069
4789
|
const anchor = e.target.closest("a");
|
|
@@ -4079,7 +4799,7 @@ function OhhwellsBridge() {
|
|
|
4079
4799
|
document.addEventListener("click", handleClick, true);
|
|
4080
4800
|
return () => document.removeEventListener("click", handleClick, true);
|
|
4081
4801
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
4082
|
-
|
|
4802
|
+
useEffect2(() => {
|
|
4083
4803
|
if (!isEditMode) {
|
|
4084
4804
|
editStylesRef.current?.base.remove();
|
|
4085
4805
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -4339,6 +5059,15 @@ function OhhwellsBridge() {
|
|
|
4339
5059
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
4340
5060
|
return;
|
|
4341
5061
|
}
|
|
5062
|
+
if (hoveredGapRef.current) {
|
|
5063
|
+
if (hoveredImageRef.current) {
|
|
5064
|
+
hoveredImageRef.current = null;
|
|
5065
|
+
resumeAnimTracks();
|
|
5066
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
5067
|
+
}
|
|
5068
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5069
|
+
return;
|
|
5070
|
+
}
|
|
4342
5071
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4343
5072
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
4344
5073
|
hoveredImageRef.current = imgEl;
|
|
@@ -4427,17 +5156,17 @@ function OhhwellsBridge() {
|
|
|
4427
5156
|
};
|
|
4428
5157
|
const handleMouseMove = (e) => {
|
|
4429
5158
|
const { clientX, clientY } = e;
|
|
5159
|
+
probeSectionGapAt(clientX, clientY);
|
|
4430
5160
|
probeImageAt(clientX, clientY);
|
|
4431
5161
|
probeHoverCardsAt(clientX, clientY);
|
|
4432
|
-
probeSectionGapAt(clientX, clientY);
|
|
4433
5162
|
};
|
|
4434
5163
|
const handlePointerSync = (e) => {
|
|
4435
5164
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
4436
5165
|
const { clientX, clientY } = e.data;
|
|
4437
5166
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
5167
|
+
probeSectionGapAt(clientX, clientY);
|
|
4438
5168
|
probeImageAt(clientX, clientY);
|
|
4439
5169
|
probeHoverCardsAt(clientX, clientY);
|
|
4440
|
-
probeSectionGapAt(clientX, clientY);
|
|
4441
5170
|
};
|
|
4442
5171
|
const handleDragOver = (e) => {
|
|
4443
5172
|
e.preventDefault();
|
|
@@ -4594,6 +5323,18 @@ function OhhwellsBridge() {
|
|
|
4594
5323
|
const content = e.data.content;
|
|
4595
5324
|
if (!content) return;
|
|
4596
5325
|
for (const [key, val] of Object.entries(content)) {
|
|
5326
|
+
if (key === "__ohw_sections") {
|
|
5327
|
+
try {
|
|
5328
|
+
const entries = JSON.parse(val);
|
|
5329
|
+
const tracker = getSectionsTracker();
|
|
5330
|
+
tracker.textContent = val;
|
|
5331
|
+
entries.forEach(({ type, insertAfter, scheduleId }) => {
|
|
5332
|
+
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
5333
|
+
});
|
|
5334
|
+
} catch {
|
|
5335
|
+
}
|
|
5336
|
+
continue;
|
|
5337
|
+
}
|
|
4597
5338
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
4598
5339
|
if (el.dataset.ohwEditable === "image") {
|
|
4599
5340
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -4648,7 +5389,71 @@ function OhhwellsBridge() {
|
|
|
4648
5389
|
};
|
|
4649
5390
|
const handleSave = (e) => {
|
|
4650
5391
|
if (e.data?.type !== "ow:save") return;
|
|
4651
|
-
|
|
5392
|
+
const nodes = collectEditableNodes();
|
|
5393
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
5394
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
5395
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
5396
|
+
};
|
|
5397
|
+
const handleInsertSection = (e) => {
|
|
5398
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
5399
|
+
const { widgetType, insertAfter } = e.data;
|
|
5400
|
+
if (widgetType !== "scheduling") return;
|
|
5401
|
+
const inserted = mountSchedulingWidget(insertAfter, true);
|
|
5402
|
+
if (inserted) {
|
|
5403
|
+
const tracker = getSectionsTracker();
|
|
5404
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
5405
|
+
const h = document.documentElement.scrollHeight;
|
|
5406
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5407
|
+
}
|
|
5408
|
+
};
|
|
5409
|
+
const handleSwitchSchedule = (e) => {
|
|
5410
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
5411
|
+
const scheduleId = e.data.scheduleId;
|
|
5412
|
+
if (!scheduleId) return;
|
|
5413
|
+
const tracker = getSectionsTracker();
|
|
5414
|
+
let sections = [];
|
|
5415
|
+
try {
|
|
5416
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5417
|
+
} catch {
|
|
5418
|
+
}
|
|
5419
|
+
const currentPath = window.location.pathname;
|
|
5420
|
+
const updated = sections.map(
|
|
5421
|
+
(s) => s.type === "scheduling" && s.pagePath === currentPath ? { ...s, scheduleId } : s
|
|
5422
|
+
);
|
|
5423
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5424
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5425
|
+
};
|
|
5426
|
+
const handleClearSchedulingWidget = (e) => {
|
|
5427
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
5428
|
+
const tracker = getSectionsTracker();
|
|
5429
|
+
let sections = [];
|
|
5430
|
+
try {
|
|
5431
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5432
|
+
} catch {
|
|
5433
|
+
}
|
|
5434
|
+
const currentPath = window.location.pathname;
|
|
5435
|
+
const updated = sections.map(
|
|
5436
|
+
(s) => s.type === "scheduling" && s.pagePath === currentPath ? { ...s, scheduleId: null } : s
|
|
5437
|
+
);
|
|
5438
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5439
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5440
|
+
};
|
|
5441
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
5442
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
5443
|
+
const els = document.querySelectorAll('[data-ohw-section="scheduling"]');
|
|
5444
|
+
els.forEach((el) => el.parentElement?.removeChild(el));
|
|
5445
|
+
const tracker = getSectionsTracker();
|
|
5446
|
+
let sections = [];
|
|
5447
|
+
try {
|
|
5448
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5449
|
+
} catch {
|
|
5450
|
+
}
|
|
5451
|
+
const currentPath = window.location.pathname;
|
|
5452
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
5453
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5454
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5455
|
+
const h = document.documentElement.scrollHeight;
|
|
5456
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
4652
5457
|
};
|
|
4653
5458
|
const handleSelectionChange = () => {
|
|
4654
5459
|
if (!activeElRef.current) return;
|
|
@@ -4753,6 +5558,10 @@ function OhhwellsBridge() {
|
|
|
4753
5558
|
deactivateRef.current();
|
|
4754
5559
|
};
|
|
4755
5560
|
window.addEventListener("message", handleSave);
|
|
5561
|
+
window.addEventListener("message", handleInsertSection);
|
|
5562
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
5563
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
5564
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
4756
5565
|
window.addEventListener("message", handleImageUrl);
|
|
4757
5566
|
window.addEventListener("message", handleAnimLock);
|
|
4758
5567
|
window.addEventListener("message", handleCanvasHeight);
|
|
@@ -4787,6 +5596,10 @@ function OhhwellsBridge() {
|
|
|
4787
5596
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
4788
5597
|
window.removeEventListener("scroll", handleScroll, true);
|
|
4789
5598
|
window.removeEventListener("message", handleSave);
|
|
5599
|
+
window.removeEventListener("message", handleInsertSection);
|
|
5600
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
5601
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
5602
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
4790
5603
|
window.removeEventListener("message", handleImageUrl);
|
|
4791
5604
|
window.removeEventListener("message", handleAnimLock);
|
|
4792
5605
|
window.removeEventListener("message", handleCanvasHeight);
|
|
@@ -4801,7 +5614,7 @@ function OhhwellsBridge() {
|
|
|
4801
5614
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
4802
5615
|
};
|
|
4803
5616
|
}, [isEditMode, refreshStateRules]);
|
|
4804
|
-
|
|
5617
|
+
useEffect2(() => {
|
|
4805
5618
|
if (!isEditMode) return;
|
|
4806
5619
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
4807
5620
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -4843,12 +5656,12 @@ function OhhwellsBridge() {
|
|
|
4843
5656
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
4844
5657
|
}, [deactivate]);
|
|
4845
5658
|
return bridgeRoot ? createPortal(
|
|
4846
|
-
/* @__PURE__ */
|
|
4847
|
-
toolbarRect && /* @__PURE__ */
|
|
4848
|
-
/* @__PURE__ */
|
|
4849
|
-
/* @__PURE__ */
|
|
5659
|
+
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
5660
|
+
toolbarRect && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
5661
|
+
/* @__PURE__ */ jsx6(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
5662
|
+
/* @__PURE__ */ jsx6(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
|
|
4850
5663
|
] }),
|
|
4851
|
-
maxBadge && /* @__PURE__ */
|
|
5664
|
+
maxBadge && /* @__PURE__ */ jsxs3(
|
|
4852
5665
|
"div",
|
|
4853
5666
|
{
|
|
4854
5667
|
"data-ohw-max-badge": "",
|
|
@@ -4874,7 +5687,7 @@ function OhhwellsBridge() {
|
|
|
4874
5687
|
]
|
|
4875
5688
|
}
|
|
4876
5689
|
),
|
|
4877
|
-
toggleState && /* @__PURE__ */
|
|
5690
|
+
toggleState && /* @__PURE__ */ jsx6(
|
|
4878
5691
|
StateToggle,
|
|
4879
5692
|
{
|
|
4880
5693
|
rect: toggleState.rect,
|
|
@@ -4883,15 +5696,15 @@ function OhhwellsBridge() {
|
|
|
4883
5696
|
onStateChange: handleStateChange
|
|
4884
5697
|
}
|
|
4885
5698
|
),
|
|
4886
|
-
sectionGap && /* @__PURE__ */
|
|
5699
|
+
sectionGap && /* @__PURE__ */ jsxs3(
|
|
4887
5700
|
"div",
|
|
4888
5701
|
{
|
|
4889
5702
|
"data-ohw-section-insert-line": "",
|
|
4890
5703
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
4891
5704
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
4892
5705
|
children: [
|
|
4893
|
-
/* @__PURE__ */
|
|
4894
|
-
/* @__PURE__ */
|
|
5706
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
5707
|
+
/* @__PURE__ */ jsx6(
|
|
4895
5708
|
Badge,
|
|
4896
5709
|
{
|
|
4897
5710
|
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",
|
|
@@ -4904,7 +5717,7 @@ function OhhwellsBridge() {
|
|
|
4904
5717
|
children: "Add Section"
|
|
4905
5718
|
}
|
|
4906
5719
|
),
|
|
4907
|
-
/* @__PURE__ */
|
|
5720
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
4908
5721
|
]
|
|
4909
5722
|
}
|
|
4910
5723
|
)
|
|
@@ -4914,6 +5727,7 @@ function OhhwellsBridge() {
|
|
|
4914
5727
|
}
|
|
4915
5728
|
export {
|
|
4916
5729
|
OhhwellsBridge,
|
|
5730
|
+
SchedulingWidget,
|
|
4917
5731
|
Toggle,
|
|
4918
5732
|
ToggleGroup,
|
|
4919
5733
|
ToggleGroupItem,
|