@ohhwells/bridge 0.1.28 → 0.1.29
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 +234 -1312
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -7
- package/dist/index.d.ts +1 -7
- package/dist/index.js +210 -1287
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -445
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1,752 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
5
|
-
import { createRoot } from "react-dom/client";
|
|
6
|
-
import { flushSync } from "react-dom";
|
|
7
|
-
|
|
8
|
-
// src/ui/SchedulingWidget.tsx
|
|
9
|
-
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
10
|
-
|
|
11
|
-
// src/ui/EmailCaptureModal.tsx
|
|
12
|
-
import { useState } from "react";
|
|
13
|
-
import { Dialog } from "radix-ui";
|
|
14
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
-
function Spinner() {
|
|
16
|
-
return /* @__PURE__ */ jsxs(
|
|
17
|
-
"svg",
|
|
18
|
-
{
|
|
19
|
-
width: "32",
|
|
20
|
-
height: "32",
|
|
21
|
-
viewBox: "0 0 32 32",
|
|
22
|
-
fill: "none",
|
|
23
|
-
style: { animation: "ohw-spin 0.75s linear infinite" },
|
|
24
|
-
children: [
|
|
25
|
-
/* @__PURE__ */ jsx("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
|
|
26
|
-
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
27
|
-
/* @__PURE__ */ jsx(
|
|
28
|
-
"path",
|
|
29
|
-
{
|
|
30
|
-
d: "M16 4a12 12 0 0 1 12 12",
|
|
31
|
-
stroke: "#3b82f6",
|
|
32
|
-
strokeWidth: "3",
|
|
33
|
-
strokeLinecap: "round"
|
|
34
|
-
}
|
|
35
|
-
)
|
|
36
|
-
]
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
41
|
-
const [email, setEmail] = useState("");
|
|
42
|
-
const [submittedEmail, setSubmittedEmail] = useState("");
|
|
43
|
-
const [loading, setLoading] = useState(false);
|
|
44
|
-
const [success, setSuccess] = useState(false);
|
|
45
|
-
const [error, setError] = useState(null);
|
|
46
|
-
const isBook = title === "Confirm your spot";
|
|
47
|
-
const handleSubmit = async () => {
|
|
48
|
-
if (!email.trim()) return;
|
|
49
|
-
setLoading(true);
|
|
50
|
-
setError(null);
|
|
51
|
-
try {
|
|
52
|
-
await onSubmit(email.trim());
|
|
53
|
-
setSubmittedEmail(email.trim());
|
|
54
|
-
setSuccess(true);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
57
|
-
} finally {
|
|
58
|
-
setLoading(false);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const handleKeyDown = (e) => {
|
|
62
|
-
if (e.key === "Enter" && !loading) handleSubmit();
|
|
63
|
-
};
|
|
64
|
-
return /* @__PURE__ */ jsx(Dialog.Root, { open: true, onOpenChange: (open) => {
|
|
65
|
-
if (!open) onClose();
|
|
66
|
-
}, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
67
|
-
/* @__PURE__ */ jsx(
|
|
68
|
-
Dialog.Overlay,
|
|
69
|
-
{
|
|
70
|
-
className: "fixed inset-0 z-50",
|
|
71
|
-
style: { background: "rgba(0,0,0,0.45)" }
|
|
72
|
-
}
|
|
73
|
-
),
|
|
74
|
-
/* @__PURE__ */ jsxs(
|
|
75
|
-
Dialog.Content,
|
|
76
|
-
{
|
|
77
|
-
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",
|
|
78
|
-
style: { maxWidth: 400 },
|
|
79
|
-
children: [
|
|
80
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6", children: [
|
|
81
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
82
|
-
/* @__PURE__ */ jsx(
|
|
83
|
-
Dialog.Title,
|
|
84
|
-
{
|
|
85
|
-
className: "font-body text-xl font-bold m-0 leading-tight",
|
|
86
|
-
style: { color: "var(--color-dark,#200C02)" },
|
|
87
|
-
children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
|
|
88
|
-
}
|
|
89
|
-
),
|
|
90
|
-
/* @__PURE__ */ jsx(
|
|
91
|
-
Dialog.Description,
|
|
92
|
-
{
|
|
93
|
-
className: "font-body text-sm m-0",
|
|
94
|
-
style: { color: "#6B7280" },
|
|
95
|
-
children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
|
|
96
|
-
}
|
|
97
|
-
)
|
|
98
|
-
] }),
|
|
99
|
-
/* @__PURE__ */ jsx(
|
|
100
|
-
Dialog.Close,
|
|
101
|
-
{
|
|
102
|
-
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",
|
|
103
|
-
style: { color: "#6B7280" },
|
|
104
|
-
"aria-label": "Close",
|
|
105
|
-
children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
106
|
-
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
107
|
-
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
108
|
-
] })
|
|
109
|
-
}
|
|
110
|
-
)
|
|
111
|
-
] }),
|
|
112
|
-
!success && (loading ? (
|
|
113
|
-
/* Loading state — full body replaced */
|
|
114
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
|
|
115
|
-
/* @__PURE__ */ jsx(Spinner, {}),
|
|
116
|
-
/* @__PURE__ */ jsx("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
|
|
117
|
-
] })
|
|
118
|
-
) : (
|
|
119
|
-
/* Email form */
|
|
120
|
-
/* @__PURE__ */ jsxs("div", { className: "px-7 pt-2 pb-6", children: [
|
|
121
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 mb-8", children: [
|
|
122
|
-
/* @__PURE__ */ jsx("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
|
|
123
|
-
/* @__PURE__ */ jsx(
|
|
124
|
-
"input",
|
|
125
|
-
{
|
|
126
|
-
type: "email",
|
|
127
|
-
value: email,
|
|
128
|
-
onChange: (e) => setEmail(e.target.value),
|
|
129
|
-
onKeyDown: handleKeyDown,
|
|
130
|
-
placeholder: "hello@gmail.com",
|
|
131
|
-
autoFocus: true,
|
|
132
|
-
className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
|
|
133
|
-
style: {
|
|
134
|
-
borderColor: "#E7E5E4",
|
|
135
|
-
background: "#fff",
|
|
136
|
-
color: "var(--color-dark,#200C02)"
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
),
|
|
140
|
-
error && /* @__PURE__ */ jsx("p", { className: "font-body text-xs text-red-500 m-0", children: error })
|
|
141
|
-
] }),
|
|
142
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
143
|
-
"button",
|
|
144
|
-
{
|
|
145
|
-
onClick: handleSubmit,
|
|
146
|
-
disabled: !email.trim(),
|
|
147
|
-
className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
|
|
148
|
-
style: {
|
|
149
|
-
background: "var(--color-primary,#3D312B)",
|
|
150
|
-
color: "var(--color-light,#FEFFF0)"
|
|
151
|
-
},
|
|
152
|
-
children: "Send"
|
|
153
|
-
}
|
|
154
|
-
) })
|
|
155
|
-
] })
|
|
156
|
-
))
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
)
|
|
160
|
-
] }) });
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// src/ui/SchedulingWidget.tsx
|
|
164
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
165
|
-
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
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
|
-
function classRunsOnDate(cls, date, type) {
|
|
175
|
-
if (type === "FIXED") {
|
|
176
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
177
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
178
|
-
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
179
|
-
}
|
|
180
|
-
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
181
|
-
}
|
|
182
|
-
function formatClassTime(cls) {
|
|
183
|
-
let h = parseInt(cls.startTime.hour, 10);
|
|
184
|
-
const m = parseInt(cls.startTime.minutes, 10);
|
|
185
|
-
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
186
|
-
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
187
|
-
const endTotal = h * 60 + m + cls.duration;
|
|
188
|
-
const eh = Math.floor(endTotal / 60) % 24;
|
|
189
|
-
const em = endTotal % 60;
|
|
190
|
-
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
191
|
-
}
|
|
192
|
-
function getBookingsOnDate(cls, date) {
|
|
193
|
-
if (!cls.bookings?.length) return 0;
|
|
194
|
-
const ds = date.toISOString().split("T")[0];
|
|
195
|
-
return cls.bookings.filter((b) => {
|
|
196
|
-
try {
|
|
197
|
-
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
198
|
-
} catch {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
}).length;
|
|
202
|
-
}
|
|
203
|
-
function buildTimezoneLabel(tz) {
|
|
204
|
-
try {
|
|
205
|
-
const now = /* @__PURE__ */ new Date();
|
|
206
|
-
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
207
|
-
hour: "2-digit",
|
|
208
|
-
minute: "2-digit",
|
|
209
|
-
hour12: false,
|
|
210
|
-
timeZone: tz
|
|
211
|
-
}).format(now);
|
|
212
|
-
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
213
|
-
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
214
|
-
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
215
|
-
} catch {
|
|
216
|
-
return tz;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function EmptyState({ inEditor }) {
|
|
220
|
-
const handleAddSchedule = () => {
|
|
221
|
-
if (inEditor) {
|
|
222
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
|
|
226
|
-
/* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
|
|
227
|
-
"svg",
|
|
228
|
-
{
|
|
229
|
-
width: "20",
|
|
230
|
-
height: "20",
|
|
231
|
-
viewBox: "0 0 24 24",
|
|
232
|
-
fill: "none",
|
|
233
|
-
stroke: "var(--color-accent, #A89B83)",
|
|
234
|
-
strokeWidth: "1.5",
|
|
235
|
-
strokeLinecap: "round",
|
|
236
|
-
strokeLinejoin: "round",
|
|
237
|
-
children: [
|
|
238
|
-
/* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
239
|
-
/* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
240
|
-
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
241
|
-
/* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
242
|
-
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
243
|
-
/* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
244
|
-
]
|
|
245
|
-
}
|
|
246
|
-
) }),
|
|
247
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
|
|
248
|
-
/* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
249
|
-
/* @__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." })
|
|
250
|
-
] }),
|
|
251
|
-
/* @__PURE__ */ jsx2(
|
|
252
|
-
"button",
|
|
253
|
-
{
|
|
254
|
-
onClick: handleAddSchedule,
|
|
255
|
-
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",
|
|
256
|
-
children: "Add Schedule"
|
|
257
|
-
}
|
|
258
|
-
)
|
|
259
|
-
] });
|
|
260
|
-
}
|
|
261
|
-
function LoadingSkeleton() {
|
|
262
|
-
const shimmer = {
|
|
263
|
-
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
264
|
-
backgroundSize: "200% 100%",
|
|
265
|
-
animation: "ohw-shimmer 1.5s infinite"
|
|
266
|
-
};
|
|
267
|
-
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
268
|
-
/* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
269
|
-
/* @__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: [
|
|
270
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
271
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
272
|
-
] }, i)) }),
|
|
273
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
274
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:hidden", children: [
|
|
275
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0", children: [
|
|
276
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
|
|
277
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
|
|
278
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
|
|
279
|
-
] }),
|
|
280
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
281
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
|
|
282
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
|
|
283
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
|
|
284
|
-
] })
|
|
285
|
-
] }),
|
|
286
|
-
/* @__PURE__ */ jsxs2("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
|
|
287
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
288
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
289
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
290
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
291
|
-
] }),
|
|
292
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
293
|
-
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
294
|
-
] }),
|
|
295
|
-
i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
296
|
-
] }, i))
|
|
297
|
-
] });
|
|
298
|
-
}
|
|
299
|
-
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
300
|
-
const todayMs = useMemo(() => {
|
|
301
|
-
const d = /* @__PURE__ */ new Date();
|
|
302
|
-
d.setHours(0, 0, 0, 0);
|
|
303
|
-
return d.getTime();
|
|
304
|
-
}, []);
|
|
305
|
-
const scrollRef = useRef(null);
|
|
306
|
-
const [canScrollLeft, setCanScrollLeft] = useState2(false);
|
|
307
|
-
const [canScrollRight, setCanScrollRight] = useState2(false);
|
|
308
|
-
const updateScrollState = () => {
|
|
309
|
-
const el = scrollRef.current;
|
|
310
|
-
if (!el) return;
|
|
311
|
-
setCanScrollLeft(el.scrollLeft > 0);
|
|
312
|
-
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
313
|
-
};
|
|
314
|
-
useEffect(() => {
|
|
315
|
-
const el = scrollRef.current;
|
|
316
|
-
if (!el) return;
|
|
317
|
-
updateScrollState();
|
|
318
|
-
el.addEventListener("scroll", updateScrollState);
|
|
319
|
-
const ro = new ResizeObserver(updateScrollState);
|
|
320
|
-
ro.observe(el);
|
|
321
|
-
return () => {
|
|
322
|
-
el.removeEventListener("scroll", updateScrollState);
|
|
323
|
-
ro.disconnect();
|
|
324
|
-
};
|
|
325
|
-
}, [dates]);
|
|
326
|
-
const scroll = (dir) => {
|
|
327
|
-
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
328
|
-
};
|
|
329
|
-
const isDragging = useRef(false);
|
|
330
|
-
const dragStartX = useRef(0);
|
|
331
|
-
const dragStartScrollLeft = useRef(0);
|
|
332
|
-
const onDragStart = (e) => {
|
|
333
|
-
if (!scrollRef.current) return;
|
|
334
|
-
isDragging.current = true;
|
|
335
|
-
dragStartX.current = e.clientX;
|
|
336
|
-
dragStartScrollLeft.current = scrollRef.current.scrollLeft;
|
|
337
|
-
scrollRef.current.style.cursor = "grabbing";
|
|
338
|
-
scrollRef.current.style.userSelect = "none";
|
|
339
|
-
};
|
|
340
|
-
const onDragMove = (e) => {
|
|
341
|
-
if (!isDragging.current || !scrollRef.current) return;
|
|
342
|
-
const delta = e.clientX - dragStartX.current;
|
|
343
|
-
scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
|
|
344
|
-
};
|
|
345
|
-
const onDragEnd = () => {
|
|
346
|
-
if (!scrollRef.current) return;
|
|
347
|
-
isDragging.current = false;
|
|
348
|
-
scrollRef.current.style.cursor = "grab";
|
|
349
|
-
scrollRef.current.style.userSelect = "";
|
|
350
|
-
};
|
|
351
|
-
const datesWithClasses = useMemo(
|
|
352
|
-
() => new Set(dates.map(
|
|
353
|
-
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
354
|
-
).filter((i) => i >= 0)),
|
|
355
|
-
[dates, schedule]
|
|
356
|
-
);
|
|
357
|
-
const selectedDate = dates[selectedIdx];
|
|
358
|
-
const selectedClasses = useMemo(
|
|
359
|
-
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
360
|
-
[schedule, selectedDate]
|
|
361
|
-
);
|
|
362
|
-
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
363
|
-
/* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
|
|
364
|
-
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
365
|
-
"button",
|
|
366
|
-
{
|
|
367
|
-
onClick: () => scroll("left"),
|
|
368
|
-
className: "hidden sm:flex absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
|
|
369
|
-
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
370
|
-
"aria-label": "Scroll left",
|
|
371
|
-
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" }) })
|
|
372
|
-
}
|
|
373
|
-
),
|
|
374
|
-
canScrollRight && /* @__PURE__ */ jsx2(
|
|
375
|
-
"button",
|
|
376
|
-
{
|
|
377
|
-
onClick: () => scroll("right"),
|
|
378
|
-
className: "hidden sm:flex absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
|
|
379
|
-
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
380
|
-
"aria-label": "Scroll right",
|
|
381
|
-
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" }) })
|
|
382
|
-
}
|
|
383
|
-
),
|
|
384
|
-
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
385
|
-
"div",
|
|
386
|
-
{
|
|
387
|
-
className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
388
|
-
style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
|
|
389
|
-
}
|
|
390
|
-
),
|
|
391
|
-
canScrollRight && /* @__PURE__ */ jsx2(
|
|
392
|
-
"div",
|
|
393
|
-
{
|
|
394
|
-
className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
395
|
-
style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
|
|
396
|
-
}
|
|
397
|
-
),
|
|
398
|
-
/* @__PURE__ */ jsx2(
|
|
399
|
-
"div",
|
|
400
|
-
{
|
|
401
|
-
ref: scrollRef,
|
|
402
|
-
className: "overflow-x-auto w-full",
|
|
403
|
-
style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
|
|
404
|
-
onMouseDown: onDragStart,
|
|
405
|
-
onMouseMove: onDragMove,
|
|
406
|
-
onMouseUp: onDragEnd,
|
|
407
|
-
onMouseLeave: onDragEnd,
|
|
408
|
-
children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
409
|
-
const isToday = date.getTime() === todayMs;
|
|
410
|
-
const isSelected = i === selectedIdx;
|
|
411
|
-
const clickable = datesWithClasses.has(i);
|
|
412
|
-
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
413
|
-
return /* @__PURE__ */ jsxs2(
|
|
414
|
-
"div",
|
|
415
|
-
{
|
|
416
|
-
onClick: () => clickable && onSelectDate(i),
|
|
417
|
-
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
418
|
-
children: [
|
|
419
|
-
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
420
|
-
/* @__PURE__ */ jsx2(
|
|
421
|
-
"div",
|
|
422
|
-
{
|
|
423
|
-
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
424
|
-
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
425
|
-
children: /* @__PURE__ */ jsx2(
|
|
426
|
-
"span",
|
|
427
|
-
{
|
|
428
|
-
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
429
|
-
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
430
|
-
children: date.getDate()
|
|
431
|
-
}
|
|
432
|
-
)
|
|
433
|
-
}
|
|
434
|
-
)
|
|
435
|
-
]
|
|
436
|
-
},
|
|
437
|
-
i
|
|
438
|
-
);
|
|
439
|
-
}) })
|
|
440
|
-
}
|
|
441
|
-
)
|
|
442
|
-
] }),
|
|
443
|
-
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) => {
|
|
444
|
-
const booked = getBookingsOnDate(cls, selectedDate);
|
|
445
|
-
const available = cls.maxParticipants - booked;
|
|
446
|
-
const isFull = available <= 0;
|
|
447
|
-
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
448
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
449
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
450
|
-
/* @__PURE__ */ jsx2("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
451
|
-
/* @__PURE__ */ jsx2(
|
|
452
|
-
"span",
|
|
453
|
-
{
|
|
454
|
-
className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
|
|
455
|
-
style: { borderColor: "#0885FE", color: "#0885FE" },
|
|
456
|
-
children: "GROUP"
|
|
457
|
-
}
|
|
458
|
-
),
|
|
459
|
-
/* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
460
|
-
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
461
|
-
available,
|
|
462
|
-
"/",
|
|
463
|
-
cls.maxParticipants
|
|
464
|
-
] }),
|
|
465
|
-
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
466
|
-
] }) })
|
|
467
|
-
] }),
|
|
468
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
469
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
470
|
-
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
471
|
-
cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
472
|
-
] }),
|
|
473
|
-
/* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 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: [
|
|
474
|
-
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
475
|
-
available,
|
|
476
|
-
"/",
|
|
477
|
-
cls.maxParticipants
|
|
478
|
-
] }),
|
|
479
|
-
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
480
|
-
] }) }),
|
|
481
|
-
/* @__PURE__ */ jsx2(
|
|
482
|
-
"button",
|
|
483
|
-
{
|
|
484
|
-
className: "w-full sm:w-[200px] sm:shrink-0 px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
|
|
485
|
-
style: isFull ? {
|
|
486
|
-
background: "transparent",
|
|
487
|
-
border: "1px solid var(--color-dark, #200C02)",
|
|
488
|
-
color: "var(--color-dark, #200C02)"
|
|
489
|
-
} : {
|
|
490
|
-
background: "var(--color-primary, #3D312B)",
|
|
491
|
-
border: "none",
|
|
492
|
-
color: "var(--color-light, #FEFFF0)"
|
|
493
|
-
},
|
|
494
|
-
onClick: () => {
|
|
495
|
-
if (!cls.id) return;
|
|
496
|
-
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
497
|
-
},
|
|
498
|
-
children: isFull ? "Join Waitlist" : "Book Now"
|
|
499
|
-
}
|
|
500
|
-
)
|
|
501
|
-
] })
|
|
502
|
-
] }),
|
|
503
|
-
i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
504
|
-
] }, cls.id ?? i);
|
|
505
|
-
}) })
|
|
506
|
-
] });
|
|
507
|
-
}
|
|
508
|
-
function CalendarFoldIcon() {
|
|
509
|
-
return /* @__PURE__ */ jsxs2(
|
|
510
|
-
"svg",
|
|
511
|
-
{
|
|
512
|
-
width: "16",
|
|
513
|
-
height: "16",
|
|
514
|
-
viewBox: "0 0 24 24",
|
|
515
|
-
fill: "none",
|
|
516
|
-
stroke: "currentColor",
|
|
517
|
-
strokeWidth: "2",
|
|
518
|
-
strokeLinecap: "round",
|
|
519
|
-
strokeLinejoin: "round",
|
|
520
|
-
style: { flexShrink: 0 },
|
|
521
|
-
children: [
|
|
522
|
-
/* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
|
|
523
|
-
/* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
|
|
524
|
-
/* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
|
|
525
|
-
/* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
|
|
526
|
-
/* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
527
|
-
]
|
|
528
|
-
}
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
532
|
-
const [schedule, setSchedule] = useState2(null);
|
|
533
|
-
const [loading, setLoading] = useState2(true);
|
|
534
|
-
const [inEditor, setInEditor] = useState2(false);
|
|
535
|
-
const [isHovered, setIsHovered] = useState2(false);
|
|
536
|
-
const [modalState, setModalState] = useState2(null);
|
|
537
|
-
const switchScheduleIdRef = useRef(null);
|
|
538
|
-
const dates = useMemo(() => {
|
|
539
|
-
const today = /* @__PURE__ */ new Date();
|
|
540
|
-
today.setHours(0, 0, 0, 0);
|
|
541
|
-
return Array.from({ length: 14 }, (_, i) => {
|
|
542
|
-
const d = new Date(today);
|
|
543
|
-
d.setDate(today.getDate() + i);
|
|
544
|
-
return d;
|
|
545
|
-
});
|
|
546
|
-
}, []);
|
|
547
|
-
const [selectedIdx, setSelectedIdx] = useState2(0);
|
|
548
|
-
useEffect(() => {
|
|
549
|
-
if (!schedule?.classes) return;
|
|
550
|
-
for (let i = 0; i < dates.length; i++) {
|
|
551
|
-
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
552
|
-
setSelectedIdx(i);
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}, [schedule, dates]);
|
|
557
|
-
useEffect(() => {
|
|
558
|
-
if (typeof window === "undefined") return;
|
|
559
|
-
const isInEditor = window.self !== window.top;
|
|
560
|
-
setInEditor(isInEditor);
|
|
561
|
-
if (isInEditor) {
|
|
562
|
-
const startEmpty = initialScheduleId === null;
|
|
563
|
-
if (startEmpty) setLoading(false);
|
|
564
|
-
let initialized = startEmpty;
|
|
565
|
-
const timer = !startEmpty ? setTimeout(() => {
|
|
566
|
-
if (!initialized) {
|
|
567
|
-
initialized = true;
|
|
568
|
-
setLoading(false);
|
|
569
|
-
}
|
|
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
|
-
setLoading(false);
|
|
597
|
-
if (notifyOnConnect && target && !isSwitching) {
|
|
598
|
-
window.parent.postMessage({
|
|
599
|
-
type: "ow:schedule-connected",
|
|
600
|
-
schedule: { id: target.id, name: target.name },
|
|
601
|
-
insertAfter
|
|
602
|
-
}, "*");
|
|
603
|
-
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
window.addEventListener("message", handler);
|
|
607
|
-
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
608
|
-
return () => {
|
|
609
|
-
window.removeEventListener("message", handler);
|
|
610
|
-
if (timer) clearTimeout(timer);
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
if (initialScheduleId === null) {
|
|
614
|
-
setLoading(false);
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
;
|
|
618
|
-
(async () => {
|
|
619
|
-
try {
|
|
620
|
-
if (initialScheduleId) {
|
|
621
|
-
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
622
|
-
const data = await res.json();
|
|
623
|
-
setSchedule(data?.id ? data : null);
|
|
624
|
-
} else {
|
|
625
|
-
const domain = getApiDomain();
|
|
626
|
-
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
627
|
-
const slugs = await sitemapRes.json();
|
|
628
|
-
if (!Array.isArray(slugs) || !slugs.length) {
|
|
629
|
-
setLoading(false);
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
const { slug } = [...slugs].sort(
|
|
633
|
-
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
634
|
-
)[0];
|
|
635
|
-
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
636
|
-
const data = await res.json();
|
|
637
|
-
setSchedule(data?.id ? data : null);
|
|
638
|
-
}
|
|
639
|
-
} catch {
|
|
640
|
-
}
|
|
641
|
-
setLoading(false);
|
|
642
|
-
})();
|
|
643
|
-
}, []);
|
|
644
|
-
const timezoneLabel = schedule?.timezone ? (() => {
|
|
645
|
-
try {
|
|
646
|
-
return buildTimezoneLabel(schedule.timezone);
|
|
647
|
-
} catch {
|
|
648
|
-
return void 0;
|
|
649
|
-
}
|
|
650
|
-
})() : void 0;
|
|
651
|
-
const handleModalSubmit = async (email) => {
|
|
652
|
-
if (!modalState) return;
|
|
653
|
-
const { mode, classId, classDate } = modalState;
|
|
654
|
-
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
655
|
-
const res = await fetch(endpoint, {
|
|
656
|
-
method: "POST",
|
|
657
|
-
headers: { "Content-Type": "application/json" },
|
|
658
|
-
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
659
|
-
});
|
|
660
|
-
if (!res.ok) {
|
|
661
|
-
const data = await res.json().catch(() => ({}));
|
|
662
|
-
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
const handleReplaceSchedule = () => {
|
|
666
|
-
setIsHovered(false);
|
|
667
|
-
if (schedule) {
|
|
668
|
-
window.parent.postMessage({
|
|
669
|
-
type: "ow:schedule-connected",
|
|
670
|
-
schedule: { id: schedule.id, name: schedule.name },
|
|
671
|
-
insertAfter
|
|
672
|
-
}, "*");
|
|
673
|
-
} else {
|
|
674
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
const sectionId = `scheduling-${insertAfter}`;
|
|
678
|
-
return /* @__PURE__ */ jsxs2(
|
|
679
|
-
"section",
|
|
680
|
-
{
|
|
681
|
-
"data-ohw-section": sectionId,
|
|
682
|
-
"data-ohw-scheduling-anchor": insertAfter,
|
|
683
|
-
className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
684
|
-
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
685
|
-
onMouseLeave: () => setIsHovered(false),
|
|
686
|
-
children: [
|
|
687
|
-
inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
|
|
688
|
-
"div",
|
|
689
|
-
{
|
|
690
|
-
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
691
|
-
onClick: handleReplaceSchedule,
|
|
692
|
-
children: [
|
|
693
|
-
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
694
|
-
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
695
|
-
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
|
|
696
|
-
"div",
|
|
697
|
-
{
|
|
698
|
-
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
699
|
-
style: {
|
|
700
|
-
fontSize: 14,
|
|
701
|
-
lineHeight: "24px",
|
|
702
|
-
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
703
|
-
fontWeight: 500,
|
|
704
|
-
color: "#0C0A09"
|
|
705
|
-
},
|
|
706
|
-
children: [
|
|
707
|
-
/* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
|
|
708
|
-
"Replace schedule"
|
|
709
|
-
]
|
|
710
|
-
}
|
|
711
|
-
) })
|
|
712
|
-
]
|
|
713
|
-
}
|
|
714
|
-
),
|
|
715
|
-
/* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
716
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
|
|
717
|
-
/* @__PURE__ */ jsx2("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
|
|
718
|
-
schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
719
|
-
] }),
|
|
720
|
-
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
721
|
-
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(
|
|
723
|
-
ScheduleView,
|
|
724
|
-
{
|
|
725
|
-
schedule,
|
|
726
|
-
dates,
|
|
727
|
-
selectedIdx,
|
|
728
|
-
onSelectDate: setSelectedIdx,
|
|
729
|
-
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
730
|
-
}
|
|
731
|
-
) })
|
|
732
|
-
] })
|
|
733
|
-
] }),
|
|
734
|
-
modalState && /* @__PURE__ */ jsx2(
|
|
735
|
-
EmailCaptureModal,
|
|
736
|
-
{
|
|
737
|
-
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
738
|
-
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
739
|
-
onSubmit: handleModalSubmit,
|
|
740
|
-
onClose: () => setModalState(null)
|
|
741
|
-
}
|
|
742
|
-
)
|
|
743
|
-
]
|
|
744
|
-
}
|
|
745
|
-
);
|
|
746
|
-
}
|
|
4
|
+
import React4, { useCallback as useCallback2, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
747
5
|
|
|
748
6
|
// src/ui/toggle-group.tsx
|
|
749
|
-
import * as
|
|
7
|
+
import * as React from "react";
|
|
750
8
|
|
|
751
9
|
// node_modules/clsx/dist/clsx.mjs
|
|
752
10
|
function r(e) {
|
|
@@ -4066,7 +3324,7 @@ var cva = (base, config) => (props) => {
|
|
|
4066
3324
|
|
|
4067
3325
|
// src/ui/toggle.tsx
|
|
4068
3326
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
4069
|
-
import { jsx
|
|
3327
|
+
import { jsx } from "react/jsx-runtime";
|
|
4070
3328
|
var toggleVariants = cva(
|
|
4071
3329
|
"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",
|
|
4072
3330
|
{
|
|
@@ -4093,7 +3351,7 @@ function Toggle({
|
|
|
4093
3351
|
size,
|
|
4094
3352
|
...props
|
|
4095
3353
|
}) {
|
|
4096
|
-
return /* @__PURE__ */
|
|
3354
|
+
return /* @__PURE__ */ jsx(
|
|
4097
3355
|
TogglePrimitive.Root,
|
|
4098
3356
|
{
|
|
4099
3357
|
"data-slot": "toggle",
|
|
@@ -4104,8 +3362,8 @@ function Toggle({
|
|
|
4104
3362
|
}
|
|
4105
3363
|
|
|
4106
3364
|
// src/ui/toggle-group.tsx
|
|
4107
|
-
import { jsx as
|
|
4108
|
-
var ToggleGroupContext =
|
|
3365
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3366
|
+
var ToggleGroupContext = React.createContext({
|
|
4109
3367
|
value: "",
|
|
4110
3368
|
onValueChange: () => {
|
|
4111
3369
|
}
|
|
@@ -4117,13 +3375,13 @@ function ToggleGroup({
|
|
|
4117
3375
|
children,
|
|
4118
3376
|
...props
|
|
4119
3377
|
}) {
|
|
4120
|
-
return /* @__PURE__ */
|
|
3378
|
+
return /* @__PURE__ */ jsx2(
|
|
4121
3379
|
"div",
|
|
4122
3380
|
{
|
|
4123
3381
|
role: "group",
|
|
4124
3382
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
4125
3383
|
...props,
|
|
4126
|
-
children: /* @__PURE__ */
|
|
3384
|
+
children: /* @__PURE__ */ jsx2(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
4127
3385
|
}
|
|
4128
3386
|
);
|
|
4129
3387
|
}
|
|
@@ -4133,8 +3391,8 @@ function ToggleGroupItem({
|
|
|
4133
3391
|
children,
|
|
4134
3392
|
...props
|
|
4135
3393
|
}) {
|
|
4136
|
-
const { value: groupValue, onValueChange } =
|
|
4137
|
-
return /* @__PURE__ */
|
|
3394
|
+
const { value: groupValue, onValueChange } = React.useContext(ToggleGroupContext);
|
|
3395
|
+
return /* @__PURE__ */ jsx2(
|
|
4138
3396
|
Toggle,
|
|
4139
3397
|
{
|
|
4140
3398
|
pressed: groupValue === value,
|
|
@@ -4263,7 +3521,10 @@ function isValidUrl(urlString) {
|
|
|
4263
3521
|
const hostname = url.hostname;
|
|
4264
3522
|
if (!hostname || hostname.length === 0) return false;
|
|
4265
3523
|
if (hostname.includes(" ")) return false;
|
|
4266
|
-
|
|
3524
|
+
if (hostname === "localhost") return true;
|
|
3525
|
+
if (!hostname.includes(".")) return false;
|
|
3526
|
+
if (!/[a-z]/i.test(hostname)) return false;
|
|
3527
|
+
return true;
|
|
4267
3528
|
} catch {
|
|
4268
3529
|
return false;
|
|
4269
3530
|
}
|
|
@@ -4274,12 +3535,19 @@ function normalizeUrl(value) {
|
|
|
4274
3535
|
return `https://${trimmed}`;
|
|
4275
3536
|
}
|
|
4276
3537
|
function validateUrlInput(value, pages, allPages) {
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
if (
|
|
3538
|
+
const trimmed = value.trim();
|
|
3539
|
+
if (!trimmed) return "";
|
|
3540
|
+
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
3541
|
+
if (exactPage) return "";
|
|
3542
|
+
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
3543
|
+
if (hasPrefixMatch) {
|
|
3544
|
+
return "Select a page from the list or enter a valid URL";
|
|
3545
|
+
}
|
|
3546
|
+
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
3547
|
+
if (!hasScheme && !trimmed.includes(".")) {
|
|
3548
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
3549
|
+
}
|
|
3550
|
+
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
4283
3551
|
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4284
3552
|
}
|
|
4285
3553
|
return "";
|
|
@@ -4438,9 +3706,9 @@ function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
|
|
|
4438
3706
|
}
|
|
4439
3707
|
|
|
4440
3708
|
// src/ui/button.tsx
|
|
4441
|
-
import * as
|
|
3709
|
+
import * as React2 from "react";
|
|
4442
3710
|
import { Slot } from "radix-ui";
|
|
4443
|
-
import { jsx as
|
|
3711
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
4444
3712
|
var buttonVariants = cva(
|
|
4445
3713
|
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
|
|
4446
3714
|
{
|
|
@@ -4461,10 +3729,10 @@ var buttonVariants = cva(
|
|
|
4461
3729
|
}
|
|
4462
3730
|
}
|
|
4463
3731
|
);
|
|
4464
|
-
var Button =
|
|
3732
|
+
var Button = React2.forwardRef(
|
|
4465
3733
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
4466
3734
|
const Comp = asChild ? Slot.Root : "button";
|
|
4467
|
-
return /* @__PURE__ */
|
|
3735
|
+
return /* @__PURE__ */ jsx3(
|
|
4468
3736
|
Comp,
|
|
4469
3737
|
{
|
|
4470
3738
|
ref,
|
|
@@ -4478,75 +3746,75 @@ var Button = React3.forwardRef(
|
|
|
4478
3746
|
Button.displayName = "Button";
|
|
4479
3747
|
|
|
4480
3748
|
// src/ui/icons.tsx
|
|
4481
|
-
import { jsx as
|
|
3749
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
4482
3750
|
function IconX({ className, ...props }) {
|
|
4483
|
-
return /* @__PURE__ */
|
|
4484
|
-
/* @__PURE__ */
|
|
4485
|
-
/* @__PURE__ */
|
|
3751
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3752
|
+
/* @__PURE__ */ jsx4("path", { d: "M18 6 6 18" }),
|
|
3753
|
+
/* @__PURE__ */ jsx4("path", { d: "m6 6 12 12" })
|
|
4486
3754
|
] });
|
|
4487
3755
|
}
|
|
4488
3756
|
function IconChevronDown({ className, ...props }) {
|
|
4489
|
-
return /* @__PURE__ */
|
|
3757
|
+
return /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" }) });
|
|
4490
3758
|
}
|
|
4491
3759
|
function IconFile({ className, ...props }) {
|
|
4492
|
-
return /* @__PURE__ */
|
|
4493
|
-
/* @__PURE__ */
|
|
4494
|
-
/* @__PURE__ */
|
|
3760
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3761
|
+
/* @__PURE__ */ jsx4("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
3762
|
+
/* @__PURE__ */ jsx4("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4495
3763
|
] });
|
|
4496
3764
|
}
|
|
4497
3765
|
function IconInfo({ className, ...props }) {
|
|
4498
|
-
return /* @__PURE__ */
|
|
4499
|
-
/* @__PURE__ */
|
|
4500
|
-
/* @__PURE__ */
|
|
4501
|
-
/* @__PURE__ */
|
|
3766
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3767
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3768
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 16v-4" }),
|
|
3769
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 8h.01" })
|
|
4502
3770
|
] });
|
|
4503
3771
|
}
|
|
4504
3772
|
function IconArrowRight({ className, ...props }) {
|
|
4505
|
-
return /* @__PURE__ */
|
|
4506
|
-
/* @__PURE__ */
|
|
4507
|
-
/* @__PURE__ */
|
|
3773
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3774
|
+
/* @__PURE__ */ jsx4("path", { d: "M5 12h14" }),
|
|
3775
|
+
/* @__PURE__ */ jsx4("path", { d: "m12 5 7 7-7 7" })
|
|
4508
3776
|
] });
|
|
4509
3777
|
}
|
|
4510
3778
|
function IconSection({ className, ...props }) {
|
|
4511
|
-
return /* @__PURE__ */
|
|
4512
|
-
/* @__PURE__ */
|
|
4513
|
-
/* @__PURE__ */
|
|
4514
|
-
/* @__PURE__ */
|
|
3779
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3780
|
+
/* @__PURE__ */ jsx4("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
3781
|
+
/* @__PURE__ */ jsx4("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
3782
|
+
/* @__PURE__ */ jsx4("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4515
3783
|
] });
|
|
4516
3784
|
}
|
|
4517
3785
|
|
|
4518
3786
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
4519
|
-
import { jsx as
|
|
3787
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4520
3788
|
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
4521
|
-
return /* @__PURE__ */
|
|
4522
|
-
/* @__PURE__ */
|
|
4523
|
-
/* @__PURE__ */
|
|
4524
|
-
/* @__PURE__ */
|
|
4525
|
-
/* @__PURE__ */
|
|
4526
|
-
/* @__PURE__ */
|
|
3789
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex w-full flex-col gap-2", children: [
|
|
3790
|
+
/* @__PURE__ */ jsx5("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
3791
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
|
|
3792
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
3793
|
+
/* @__PURE__ */ jsx5(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3794
|
+
/* @__PURE__ */ jsx5("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
4527
3795
|
] }),
|
|
4528
|
-
/* @__PURE__ */
|
|
4529
|
-
/* @__PURE__ */
|
|
4530
|
-
/* @__PURE__ */
|
|
4531
|
-
/* @__PURE__ */
|
|
3796
|
+
/* @__PURE__ */ jsx5(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
3797
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
3798
|
+
/* @__PURE__ */ jsx5(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3799
|
+
/* @__PURE__ */ jsx5("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
4532
3800
|
] })
|
|
4533
3801
|
] })
|
|
4534
3802
|
] });
|
|
4535
3803
|
}
|
|
4536
3804
|
|
|
4537
3805
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
4538
|
-
import { jsx as
|
|
3806
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4539
3807
|
function SectionTreeItem({ section, onSelect, selected }) {
|
|
4540
3808
|
const interactive = Boolean(onSelect);
|
|
4541
|
-
return /* @__PURE__ */
|
|
4542
|
-
/* @__PURE__ */
|
|
3809
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
3810
|
+
/* @__PURE__ */ jsx6(
|
|
4543
3811
|
"div",
|
|
4544
3812
|
{
|
|
4545
3813
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
4546
3814
|
"aria-hidden": true
|
|
4547
3815
|
}
|
|
4548
3816
|
),
|
|
4549
|
-
/* @__PURE__ */
|
|
3817
|
+
/* @__PURE__ */ jsxs3(
|
|
4550
3818
|
"div",
|
|
4551
3819
|
{
|
|
4552
3820
|
role: interactive ? "button" : void 0,
|
|
@@ -4564,8 +3832,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
4564
3832
|
interactive && selected && "border-primary"
|
|
4565
3833
|
),
|
|
4566
3834
|
children: [
|
|
4567
|
-
/* @__PURE__ */
|
|
4568
|
-
/* @__PURE__ */
|
|
3835
|
+
/* @__PURE__ */ jsx6(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3836
|
+
/* @__PURE__ */ jsx6("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
4569
3837
|
]
|
|
4570
3838
|
}
|
|
4571
3839
|
)
|
|
@@ -4573,23 +3841,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
4573
3841
|
}
|
|
4574
3842
|
function SectionPickerList({ sections, onSelect }) {
|
|
4575
3843
|
if (sections.length === 0) {
|
|
4576
|
-
return /* @__PURE__ */
|
|
3844
|
+
return /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
4577
3845
|
}
|
|
4578
|
-
return /* @__PURE__ */
|
|
4579
|
-
/* @__PURE__ */
|
|
4580
|
-
sections.map((section) => /* @__PURE__ */
|
|
3846
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
|
|
3847
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
3848
|
+
sections.map((section) => /* @__PURE__ */ jsx6(SectionTreeItem, { section, onSelect }, section.id))
|
|
4581
3849
|
] });
|
|
4582
3850
|
}
|
|
4583
3851
|
|
|
4584
3852
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4585
|
-
import { useId, useRef
|
|
3853
|
+
import { useId, useRef, useState } from "react";
|
|
4586
3854
|
|
|
4587
3855
|
// src/ui/input.tsx
|
|
4588
|
-
import * as
|
|
4589
|
-
import { jsx as
|
|
4590
|
-
var Input =
|
|
3856
|
+
import * as React3 from "react";
|
|
3857
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
3858
|
+
var Input = React3.forwardRef(
|
|
4591
3859
|
({ className, type, ...props }, ref) => {
|
|
4592
|
-
return /* @__PURE__ */
|
|
3860
|
+
return /* @__PURE__ */ jsx7(
|
|
4593
3861
|
"input",
|
|
4594
3862
|
{
|
|
4595
3863
|
type,
|
|
@@ -4608,9 +3876,9 @@ Input.displayName = "Input";
|
|
|
4608
3876
|
|
|
4609
3877
|
// src/ui/label.tsx
|
|
4610
3878
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
4611
|
-
import { jsx as
|
|
3879
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
4612
3880
|
function Label({ className, ...props }) {
|
|
4613
|
-
return /* @__PURE__ */
|
|
3881
|
+
return /* @__PURE__ */ jsx8(
|
|
4614
3882
|
LabelPrimitive.Root,
|
|
4615
3883
|
{
|
|
4616
3884
|
"data-slot": "label",
|
|
@@ -4622,12 +3890,12 @@ function Label({ className, ...props }) {
|
|
|
4622
3890
|
|
|
4623
3891
|
// src/ui/popover.tsx
|
|
4624
3892
|
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
4625
|
-
import { jsx as
|
|
3893
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4626
3894
|
function Popover({ ...props }) {
|
|
4627
|
-
return /* @__PURE__ */
|
|
3895
|
+
return /* @__PURE__ */ jsx9(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
4628
3896
|
}
|
|
4629
3897
|
function PopoverTrigger({ ...props }) {
|
|
4630
|
-
return /* @__PURE__ */
|
|
3898
|
+
return /* @__PURE__ */ jsx9(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4631
3899
|
}
|
|
4632
3900
|
function PopoverContent({
|
|
4633
3901
|
className,
|
|
@@ -4635,7 +3903,7 @@ function PopoverContent({
|
|
|
4635
3903
|
sideOffset = 6,
|
|
4636
3904
|
...props
|
|
4637
3905
|
}) {
|
|
4638
|
-
return /* @__PURE__ */
|
|
3906
|
+
return /* @__PURE__ */ jsx9(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx9(
|
|
4639
3907
|
PopoverPrimitive.Content,
|
|
4640
3908
|
{
|
|
4641
3909
|
"data-slot": "popover-content",
|
|
@@ -4651,9 +3919,9 @@ function PopoverContent({
|
|
|
4651
3919
|
}
|
|
4652
3920
|
|
|
4653
3921
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4654
|
-
import { jsx as
|
|
3922
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4655
3923
|
function FieldChevron({ onClick }) {
|
|
4656
|
-
return /* @__PURE__ */
|
|
3924
|
+
return /* @__PURE__ */ jsx10(
|
|
4657
3925
|
"button",
|
|
4658
3926
|
{
|
|
4659
3927
|
type: "button",
|
|
@@ -4661,7 +3929,7 @@ function FieldChevron({ onClick }) {
|
|
|
4661
3929
|
onClick,
|
|
4662
3930
|
"aria-label": "Open page list",
|
|
4663
3931
|
tabIndex: -1,
|
|
4664
|
-
children: /* @__PURE__ */
|
|
3932
|
+
children: /* @__PURE__ */ jsx10(IconChevronDown, {})
|
|
4665
3933
|
}
|
|
4666
3934
|
);
|
|
4667
3935
|
}
|
|
@@ -4678,8 +3946,8 @@ function UrlOrPageInput({
|
|
|
4678
3946
|
urlError
|
|
4679
3947
|
}) {
|
|
4680
3948
|
const inputId = useId();
|
|
4681
|
-
const inputRef =
|
|
4682
|
-
const [isFocused, setIsFocused] =
|
|
3949
|
+
const inputRef = useRef(null);
|
|
3950
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
4683
3951
|
const openDropdown = () => {
|
|
4684
3952
|
if (readOnly || filteredPages.length === 0) return;
|
|
4685
3953
|
onDropdownOpenChange(true);
|
|
@@ -4700,12 +3968,12 @@ function UrlOrPageInput({
|
|
|
4700
3968
|
"data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
|
|
4701
3969
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
4702
3970
|
);
|
|
4703
|
-
return /* @__PURE__ */
|
|
4704
|
-
/* @__PURE__ */
|
|
4705
|
-
/* @__PURE__ */
|
|
4706
|
-
/* @__PURE__ */
|
|
4707
|
-
selectedPage ? /* @__PURE__ */
|
|
4708
|
-
readOnly ? /* @__PURE__ */
|
|
3971
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
3972
|
+
/* @__PURE__ */ jsx10(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
3973
|
+
/* @__PURE__ */ jsxs4(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
|
|
3974
|
+
/* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
3975
|
+
selectedPage ? /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx10(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
3976
|
+
readOnly ? /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx10(
|
|
4709
3977
|
Input,
|
|
4710
3978
|
{
|
|
4711
3979
|
ref: inputRef,
|
|
@@ -4724,7 +3992,7 @@ function UrlOrPageInput({
|
|
|
4724
3992
|
)
|
|
4725
3993
|
}
|
|
4726
3994
|
),
|
|
4727
|
-
selectedPage && !readOnly ? /* @__PURE__ */
|
|
3995
|
+
selectedPage && !readOnly ? /* @__PURE__ */ jsx10(
|
|
4728
3996
|
"button",
|
|
4729
3997
|
{
|
|
4730
3998
|
type: "button",
|
|
@@ -4732,31 +4000,31 @@ function UrlOrPageInput({
|
|
|
4732
4000
|
onMouseDown: clearSelection,
|
|
4733
4001
|
"aria-label": "Clear selected page",
|
|
4734
4002
|
tabIndex: -1,
|
|
4735
|
-
children: /* @__PURE__ */
|
|
4003
|
+
children: /* @__PURE__ */ jsx10(IconX, { "aria-hidden": true })
|
|
4736
4004
|
}
|
|
4737
4005
|
) : null,
|
|
4738
|
-
!readOnly ? /* @__PURE__ */
|
|
4006
|
+
!readOnly ? /* @__PURE__ */ jsx10(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4739
4007
|
] }) }),
|
|
4740
|
-
filteredPages.length > 0 && /* @__PURE__ */
|
|
4008
|
+
filteredPages.length > 0 && /* @__PURE__ */ jsx10(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs4(
|
|
4741
4009
|
"button",
|
|
4742
4010
|
{
|
|
4743
4011
|
type: "button",
|
|
4744
4012
|
className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
|
|
4745
4013
|
onClick: () => onPageSelect(page),
|
|
4746
4014
|
children: [
|
|
4747
|
-
/* @__PURE__ */
|
|
4748
|
-
/* @__PURE__ */
|
|
4015
|
+
/* @__PURE__ */ jsx10(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4016
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate", children: page.title })
|
|
4749
4017
|
]
|
|
4750
4018
|
},
|
|
4751
4019
|
page.path
|
|
4752
4020
|
)) })
|
|
4753
4021
|
] }),
|
|
4754
|
-
urlError ? /* @__PURE__ */
|
|
4022
|
+
urlError ? /* @__PURE__ */ jsx10("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4755
4023
|
] });
|
|
4756
4024
|
}
|
|
4757
4025
|
|
|
4758
4026
|
// src/ui/link-modal/useLinkModalState.ts
|
|
4759
|
-
import { useCallback, useEffect
|
|
4027
|
+
import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
|
|
4760
4028
|
function useLinkModalState({
|
|
4761
4029
|
open,
|
|
4762
4030
|
mode,
|
|
@@ -4768,16 +4036,16 @@ function useLinkModalState({
|
|
|
4768
4036
|
onClose,
|
|
4769
4037
|
onSubmit
|
|
4770
4038
|
}) {
|
|
4771
|
-
const availablePages =
|
|
4039
|
+
const availablePages = useMemo(
|
|
4772
4040
|
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4773
4041
|
[mode, pages, existingTargets]
|
|
4774
4042
|
);
|
|
4775
|
-
const [searchValue, setSearchValue] =
|
|
4776
|
-
const [selectedPage, setSelectedPage] =
|
|
4777
|
-
const [selectedSection, setSelectedSection] =
|
|
4778
|
-
const [step, setStep] =
|
|
4779
|
-
const [dropdownOpen, setDropdownOpen] =
|
|
4780
|
-
const [urlError, setUrlError] =
|
|
4043
|
+
const [searchValue, setSearchValue] = useState2("");
|
|
4044
|
+
const [selectedPage, setSelectedPage] = useState2(null);
|
|
4045
|
+
const [selectedSection, setSelectedSection] = useState2(null);
|
|
4046
|
+
const [step, setStep] = useState2("input");
|
|
4047
|
+
const [dropdownOpen, setDropdownOpen] = useState2(false);
|
|
4048
|
+
const [urlError, setUrlError] = useState2("");
|
|
4781
4049
|
const reset = useCallback(() => {
|
|
4782
4050
|
setSearchValue("");
|
|
4783
4051
|
setSelectedPage(null);
|
|
@@ -4786,7 +4054,7 @@ function useLinkModalState({
|
|
|
4786
4054
|
setDropdownOpen(false);
|
|
4787
4055
|
setUrlError("");
|
|
4788
4056
|
}, []);
|
|
4789
|
-
|
|
4057
|
+
useEffect(() => {
|
|
4790
4058
|
if (!open) return;
|
|
4791
4059
|
if (mode === "edit" && initialTarget) {
|
|
4792
4060
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -4802,11 +4070,11 @@ function useLinkModalState({
|
|
|
4802
4070
|
setDropdownOpen(false);
|
|
4803
4071
|
setUrlError("");
|
|
4804
4072
|
}, [open, mode, initialTarget, pages, sectionsByPath, reset]);
|
|
4805
|
-
const filteredPages =
|
|
4073
|
+
const filteredPages = useMemo(() => {
|
|
4806
4074
|
if (!searchValue.trim()) return availablePages;
|
|
4807
4075
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4808
4076
|
}, [availablePages, searchValue]);
|
|
4809
|
-
const activeSections =
|
|
4077
|
+
const activeSections = useMemo(() => {
|
|
4810
4078
|
if (!selectedPage) return [];
|
|
4811
4079
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
4812
4080
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -4822,9 +4090,8 @@ function useLinkModalState({
|
|
|
4822
4090
|
setUrlError("");
|
|
4823
4091
|
const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
|
|
4824
4092
|
setDropdownOpen(filtered.length > 0);
|
|
4825
|
-
if (value.trim()
|
|
4826
|
-
|
|
4827
|
-
if (error) setUrlError(error);
|
|
4093
|
+
if (value.trim()) {
|
|
4094
|
+
setUrlError(validateUrlInput(value, availablePages, pages));
|
|
4828
4095
|
}
|
|
4829
4096
|
};
|
|
4830
4097
|
const handlePageSelect = (page) => {
|
|
@@ -4853,7 +4120,7 @@ function useLinkModalState({
|
|
|
4853
4120
|
reset();
|
|
4854
4121
|
onClose();
|
|
4855
4122
|
};
|
|
4856
|
-
const isValid =
|
|
4123
|
+
const isValid = useMemo(() => {
|
|
4857
4124
|
if (urlError) return false;
|
|
4858
4125
|
if (showBreadcrumb) return true;
|
|
4859
4126
|
if (selectedPage) return true;
|
|
@@ -4920,7 +4187,7 @@ function useLinkModalState({
|
|
|
4920
4187
|
}
|
|
4921
4188
|
|
|
4922
4189
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4923
|
-
import { Fragment
|
|
4190
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4924
4191
|
function LinkEditorPanel({
|
|
4925
4192
|
open = true,
|
|
4926
4193
|
mode = "create",
|
|
@@ -4944,26 +4211,26 @@ function LinkEditorPanel({
|
|
|
4944
4211
|
onSubmit
|
|
4945
4212
|
});
|
|
4946
4213
|
const isCancel = state.secondaryLabel === "Cancel";
|
|
4947
|
-
return /* @__PURE__ */
|
|
4948
|
-
/* @__PURE__ */
|
|
4214
|
+
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
4215
|
+
/* @__PURE__ */ jsx11(
|
|
4949
4216
|
"button",
|
|
4950
4217
|
{
|
|
4951
4218
|
type: "button",
|
|
4952
4219
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4953
4220
|
onClick: state.handleClose,
|
|
4954
4221
|
"aria-label": "Close",
|
|
4955
|
-
children: /* @__PURE__ */
|
|
4222
|
+
children: /* @__PURE__ */ jsx11(IconX, { "aria-hidden": true })
|
|
4956
4223
|
}
|
|
4957
4224
|
),
|
|
4958
|
-
/* @__PURE__ */
|
|
4959
|
-
/* @__PURE__ */
|
|
4960
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */
|
|
4225
|
+
/* @__PURE__ */ jsx11("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx11("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
|
|
4226
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
4227
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx11(
|
|
4961
4228
|
DestinationBreadcrumb,
|
|
4962
4229
|
{
|
|
4963
4230
|
pageTitle: state.selectedPage.title,
|
|
4964
4231
|
sectionLabel: state.selectedSection.label
|
|
4965
4232
|
}
|
|
4966
|
-
) : /* @__PURE__ */
|
|
4233
|
+
) : /* @__PURE__ */ jsx11(
|
|
4967
4234
|
UrlOrPageInput,
|
|
4968
4235
|
{
|
|
4969
4236
|
value: state.searchValue,
|
|
@@ -4976,18 +4243,18 @@ function LinkEditorPanel({
|
|
|
4976
4243
|
urlError: state.urlError
|
|
4977
4244
|
}
|
|
4978
4245
|
),
|
|
4979
|
-
state.showChooseSection ? /* @__PURE__ */
|
|
4980
|
-
/* @__PURE__ */
|
|
4981
|
-
/* @__PURE__ */
|
|
4982
|
-
/* @__PURE__ */
|
|
4983
|
-
/* @__PURE__ */
|
|
4246
|
+
state.showChooseSection ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
4247
|
+
/* @__PURE__ */ jsx11(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
4248
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
4249
|
+
/* @__PURE__ */ jsx11(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
4250
|
+
/* @__PURE__ */ jsx11("span", { children: "Pick a section this link should scroll to." })
|
|
4984
4251
|
] })
|
|
4985
4252
|
] }) : null,
|
|
4986
|
-
state.showSectionPicker ? /* @__PURE__ */
|
|
4987
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */
|
|
4253
|
+
state.showSectionPicker ? /* @__PURE__ */ jsx11(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
4254
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx11(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4988
4255
|
] }),
|
|
4989
|
-
/* @__PURE__ */
|
|
4990
|
-
/* @__PURE__ */
|
|
4256
|
+
/* @__PURE__ */ jsxs5("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
|
|
4257
|
+
/* @__PURE__ */ jsx11(
|
|
4991
4258
|
Button,
|
|
4992
4259
|
{
|
|
4993
4260
|
type: "button",
|
|
@@ -5002,7 +4269,7 @@ function LinkEditorPanel({
|
|
|
5002
4269
|
children: state.secondaryLabel
|
|
5003
4270
|
}
|
|
5004
4271
|
),
|
|
5005
|
-
/* @__PURE__ */
|
|
4272
|
+
/* @__PURE__ */ jsx11(
|
|
5006
4273
|
Button,
|
|
5007
4274
|
{
|
|
5008
4275
|
type: "button",
|
|
@@ -5021,7 +4288,7 @@ function LinkEditorPanel({
|
|
|
5021
4288
|
}
|
|
5022
4289
|
|
|
5023
4290
|
// src/ui/link-modal/LinkPopover.tsx
|
|
5024
|
-
import { jsx as
|
|
4291
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
5025
4292
|
function LinkPopover({
|
|
5026
4293
|
rect,
|
|
5027
4294
|
parentScroll,
|
|
@@ -5029,7 +4296,7 @@ function LinkPopover({
|
|
|
5029
4296
|
...editorProps
|
|
5030
4297
|
}) {
|
|
5031
4298
|
const { top, left, transform } = calcPopoverPos(rect, parentScroll);
|
|
5032
|
-
return /* @__PURE__ */
|
|
4299
|
+
return /* @__PURE__ */ jsx12(
|
|
5033
4300
|
"div",
|
|
5034
4301
|
{
|
|
5035
4302
|
ref: panelRef,
|
|
@@ -5042,7 +4309,7 @@ function LinkPopover({
|
|
|
5042
4309
|
),
|
|
5043
4310
|
style: { top, left, transform },
|
|
5044
4311
|
onMouseDown: (e) => e.stopPropagation(),
|
|
5045
|
-
children: /* @__PURE__ */
|
|
4312
|
+
children: /* @__PURE__ */ jsx12(LinkEditorPanel, { ...editorProps, open: true })
|
|
5046
4313
|
}
|
|
5047
4314
|
);
|
|
5048
4315
|
}
|
|
@@ -5105,30 +4372,8 @@ function shouldUseDevFixtures() {
|
|
|
5105
4372
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
5106
4373
|
}
|
|
5107
4374
|
|
|
5108
|
-
// src/ui/badge.tsx
|
|
5109
|
-
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
5110
|
-
var badgeVariants = cva(
|
|
5111
|
-
"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",
|
|
5112
|
-
{
|
|
5113
|
-
variants: {
|
|
5114
|
-
variant: {
|
|
5115
|
-
default: "border-transparent bg-primary text-primary-foreground",
|
|
5116
|
-
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
5117
|
-
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
5118
|
-
outline: "text-foreground"
|
|
5119
|
-
}
|
|
5120
|
-
},
|
|
5121
|
-
defaultVariants: {
|
|
5122
|
-
variant: "default"
|
|
5123
|
-
}
|
|
5124
|
-
}
|
|
5125
|
-
);
|
|
5126
|
-
function Badge({ className, variant, ...props }) {
|
|
5127
|
-
return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5128
|
-
}
|
|
5129
|
-
|
|
5130
4375
|
// src/OhhwellsBridge.tsx
|
|
5131
|
-
import { Fragment as
|
|
4376
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
5132
4377
|
var PRIMARY = "#0885FE";
|
|
5133
4378
|
var IMAGE_FADE_MS = 300;
|
|
5134
4379
|
function runOpacityFade(el, onDone) {
|
|
@@ -5179,163 +4424,6 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
5179
4424
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
5180
4425
|
});
|
|
5181
4426
|
}
|
|
5182
|
-
function getSectionsTracker() {
|
|
5183
|
-
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
5184
|
-
if (!el) {
|
|
5185
|
-
el = document.createElement("div");
|
|
5186
|
-
el.setAttribute("data-ohw-sections-tracker", "");
|
|
5187
|
-
el.style.display = "none";
|
|
5188
|
-
document.body.appendChild(el);
|
|
5189
|
-
}
|
|
5190
|
-
return el;
|
|
5191
|
-
}
|
|
5192
|
-
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
5193
|
-
const tracker = getSectionsTracker();
|
|
5194
|
-
let sections = [];
|
|
5195
|
-
try {
|
|
5196
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
5197
|
-
} catch {
|
|
5198
|
-
}
|
|
5199
|
-
const currentPath = window.location.pathname;
|
|
5200
|
-
const updated = sections.map((s) => {
|
|
5201
|
-
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
5202
|
-
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
5203
|
-
return { ...s, scheduleId };
|
|
5204
|
-
});
|
|
5205
|
-
tracker.textContent = JSON.stringify(updated);
|
|
5206
|
-
return tracker.textContent ?? "[]";
|
|
5207
|
-
}
|
|
5208
|
-
function schedulingSectionId(insertAfter) {
|
|
5209
|
-
return `scheduling-${insertAfter}`;
|
|
5210
|
-
}
|
|
5211
|
-
var INSERT_BEFORE_MARKER = ":before:";
|
|
5212
|
-
function parseSchedulingInsertAfter(insertAfter) {
|
|
5213
|
-
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
5214
|
-
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
5215
|
-
return {
|
|
5216
|
-
anchor: insertAfter.slice(0, idx),
|
|
5217
|
-
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
5218
|
-
};
|
|
5219
|
-
}
|
|
5220
|
-
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
5221
|
-
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
5222
|
-
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
5223
|
-
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
5224
|
-
return { effectiveInsertAfter, insertBefore };
|
|
5225
|
-
}
|
|
5226
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
5227
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
5228
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
5229
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
5230
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
5231
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
5232
|
-
}
|
|
5233
|
-
if (!anchorEl) return null;
|
|
5234
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
5235
|
-
}
|
|
5236
|
-
function schedulingMountDepth(insertAfter) {
|
|
5237
|
-
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
5238
|
-
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
5239
|
-
}
|
|
5240
|
-
function getPageSchedulingEntries(raw) {
|
|
5241
|
-
if (!raw) return [];
|
|
5242
|
-
try {
|
|
5243
|
-
const entries = JSON.parse(raw);
|
|
5244
|
-
const currentPath = window.location.pathname;
|
|
5245
|
-
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
5246
|
-
} catch {
|
|
5247
|
-
return [];
|
|
5248
|
-
}
|
|
5249
|
-
}
|
|
5250
|
-
function isSchedulingWidgetMissing(entry) {
|
|
5251
|
-
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
5252
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
5253
|
-
}
|
|
5254
|
-
function hasMissingSchedulingWidgets(entries) {
|
|
5255
|
-
return entries.some(isSchedulingWidgetMissing);
|
|
5256
|
-
}
|
|
5257
|
-
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
5258
|
-
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
5259
|
-
mountSchedulingEntries(entries, notifyOnConnect);
|
|
5260
|
-
}
|
|
5261
|
-
function initSectionsFromContent(content, removeExisting = false) {
|
|
5262
|
-
const raw = content["__ohw_sections"];
|
|
5263
|
-
if (!raw) return;
|
|
5264
|
-
try {
|
|
5265
|
-
if (removeExisting) {
|
|
5266
|
-
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
5267
|
-
}
|
|
5268
|
-
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5269
|
-
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5270
|
-
const pageEntries = getPageSchedulingEntries(raw);
|
|
5271
|
-
mountSchedulingEntries(pageEntries, false);
|
|
5272
|
-
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
|
|
5273
|
-
setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
|
|
5274
|
-
} catch {
|
|
5275
|
-
}
|
|
5276
|
-
}
|
|
5277
|
-
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
5278
|
-
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
5279
|
-
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
5280
|
-
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
5281
|
-
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
5282
|
-
if (!mountPoint) return false;
|
|
5283
|
-
const container = document.createElement("div");
|
|
5284
|
-
container.dataset.ohwSectionContainer = "scheduling";
|
|
5285
|
-
if (insertBefore) {
|
|
5286
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
5287
|
-
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
5288
|
-
if (!beforePoint) return false;
|
|
5289
|
-
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
5290
|
-
} else {
|
|
5291
|
-
let tail = mountPoint;
|
|
5292
|
-
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
5293
|
-
tail = tail.nextElementSibling;
|
|
5294
|
-
}
|
|
5295
|
-
tail.insertAdjacentElement("afterend", container);
|
|
5296
|
-
}
|
|
5297
|
-
const root = createRoot(container);
|
|
5298
|
-
flushSync(() => {
|
|
5299
|
-
root.render(
|
|
5300
|
-
/* @__PURE__ */ jsx16(
|
|
5301
|
-
SchedulingWidget,
|
|
5302
|
-
{
|
|
5303
|
-
notifyOnConnect,
|
|
5304
|
-
initialScheduleId: scheduleId,
|
|
5305
|
-
insertAfter: effectiveInsertAfter
|
|
5306
|
-
}
|
|
5307
|
-
)
|
|
5308
|
-
);
|
|
5309
|
-
});
|
|
5310
|
-
const tracker = getSectionsTracker();
|
|
5311
|
-
let sections = [];
|
|
5312
|
-
try {
|
|
5313
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
5314
|
-
} catch {
|
|
5315
|
-
}
|
|
5316
|
-
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5317
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
5318
|
-
sections.push({
|
|
5319
|
-
type: "scheduling",
|
|
5320
|
-
insertAfter: effectiveInsertAfter,
|
|
5321
|
-
pagePath: window.location.pathname,
|
|
5322
|
-
...scheduleId ? { scheduleId } : {}
|
|
5323
|
-
});
|
|
5324
|
-
tracker.textContent = JSON.stringify(sections);
|
|
5325
|
-
}
|
|
5326
|
-
return true;
|
|
5327
|
-
}
|
|
5328
|
-
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
5329
|
-
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5330
|
-
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5331
|
-
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5332
|
-
const { insertAfter, scheduleId } = pending[i];
|
|
5333
|
-
if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
|
|
5334
|
-
pending.splice(i, 1);
|
|
5335
|
-
}
|
|
5336
|
-
}
|
|
5337
|
-
}
|
|
5338
|
-
}
|
|
5339
4427
|
function getLinkHref(el) {
|
|
5340
4428
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5341
4429
|
return anchor?.getAttribute("href") ?? "";
|
|
@@ -5535,7 +4623,7 @@ var TOOLBAR_GROUPS = [
|
|
|
5535
4623
|
];
|
|
5536
4624
|
function GlowFrame({ rect, elRef }) {
|
|
5537
4625
|
const GAP = 6;
|
|
5538
|
-
return /* @__PURE__ */
|
|
4626
|
+
return /* @__PURE__ */ jsx13(
|
|
5539
4627
|
"div",
|
|
5540
4628
|
{
|
|
5541
4629
|
ref: elRef,
|
|
@@ -5588,7 +4676,7 @@ function FloatingToolbar({
|
|
|
5588
4676
|
onEditLink
|
|
5589
4677
|
}) {
|
|
5590
4678
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
5591
|
-
return /* @__PURE__ */
|
|
4679
|
+
return /* @__PURE__ */ jsxs6(
|
|
5592
4680
|
"div",
|
|
5593
4681
|
{
|
|
5594
4682
|
ref: elRef,
|
|
@@ -5613,11 +4701,11 @@ function FloatingToolbar({
|
|
|
5613
4701
|
},
|
|
5614
4702
|
onMouseDown: (e) => e.stopPropagation(),
|
|
5615
4703
|
children: [
|
|
5616
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
5617
|
-
gi > 0 && /* @__PURE__ */
|
|
4704
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
|
|
4705
|
+
gi > 0 && /* @__PURE__ */ jsx13("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5618
4706
|
btns.map((btn) => {
|
|
5619
4707
|
const isActive = activeCommands.has(btn.cmd);
|
|
5620
|
-
return /* @__PURE__ */
|
|
4708
|
+
return /* @__PURE__ */ jsx13(
|
|
5621
4709
|
"button",
|
|
5622
4710
|
{
|
|
5623
4711
|
title: btn.title,
|
|
@@ -5643,7 +4731,7 @@ function FloatingToolbar({
|
|
|
5643
4731
|
flexShrink: 0,
|
|
5644
4732
|
padding: 6
|
|
5645
4733
|
},
|
|
5646
|
-
children: /* @__PURE__ */
|
|
4734
|
+
children: /* @__PURE__ */ jsx13(
|
|
5647
4735
|
"svg",
|
|
5648
4736
|
{
|
|
5649
4737
|
width: "16",
|
|
@@ -5663,7 +4751,7 @@ function FloatingToolbar({
|
|
|
5663
4751
|
);
|
|
5664
4752
|
})
|
|
5665
4753
|
] }, gi)),
|
|
5666
|
-
showEditLink ? /* @__PURE__ */
|
|
4754
|
+
showEditLink ? /* @__PURE__ */ jsx13(
|
|
5667
4755
|
"button",
|
|
5668
4756
|
{
|
|
5669
4757
|
type: "button",
|
|
@@ -5695,9 +4783,9 @@ function FloatingToolbar({
|
|
|
5695
4783
|
flexShrink: 0,
|
|
5696
4784
|
padding: 6
|
|
5697
4785
|
},
|
|
5698
|
-
children: /* @__PURE__ */
|
|
5699
|
-
/* @__PURE__ */
|
|
5700
|
-
/* @__PURE__ */
|
|
4786
|
+
children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
4787
|
+
/* @__PURE__ */ jsx13("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
4788
|
+
/* @__PURE__ */ jsx13("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
5701
4789
|
] })
|
|
5702
4790
|
}
|
|
5703
4791
|
) : null
|
|
@@ -5715,7 +4803,7 @@ function StateToggle({
|
|
|
5715
4803
|
states,
|
|
5716
4804
|
onStateChange
|
|
5717
4805
|
}) {
|
|
5718
|
-
return /* @__PURE__ */
|
|
4806
|
+
return /* @__PURE__ */ jsx13(
|
|
5719
4807
|
ToggleGroup,
|
|
5720
4808
|
{
|
|
5721
4809
|
"data-ohw-state-toggle": "",
|
|
@@ -5729,35 +4817,18 @@ function StateToggle({
|
|
|
5729
4817
|
left: rect.right - 8,
|
|
5730
4818
|
transform: "translateX(-100%)"
|
|
5731
4819
|
},
|
|
5732
|
-
children: states.map((state) => /* @__PURE__ */
|
|
4820
|
+
children: states.map((state) => /* @__PURE__ */ jsx13(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
5733
4821
|
}
|
|
5734
4822
|
);
|
|
5735
4823
|
}
|
|
5736
4824
|
var contentCache = /* @__PURE__ */ new Map();
|
|
5737
|
-
function resolveSubdomain(subdomainFromQuery) {
|
|
5738
|
-
if (subdomainFromQuery) return subdomainFromQuery;
|
|
5739
|
-
if (typeof window !== "undefined") {
|
|
5740
|
-
const parts = window.location.hostname.split(".");
|
|
5741
|
-
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
5742
|
-
}
|
|
5743
|
-
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
5744
|
-
if (siteUrl) {
|
|
5745
|
-
try {
|
|
5746
|
-
const host = new URL(siteUrl).hostname;
|
|
5747
|
-
const siteParts = host.split(".");
|
|
5748
|
-
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
5749
|
-
} catch {
|
|
5750
|
-
}
|
|
5751
|
-
}
|
|
5752
|
-
return "";
|
|
5753
|
-
}
|
|
5754
4825
|
function OhhwellsBridge() {
|
|
5755
4826
|
const pathname = usePathname();
|
|
5756
4827
|
const router = useRouter();
|
|
5757
4828
|
const searchParams = useSearchParams();
|
|
5758
4829
|
const isEditMode = isEditSessionActive();
|
|
5759
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
5760
|
-
|
|
4830
|
+
const [bridgeRoot, setBridgeRoot] = useState3(null);
|
|
4831
|
+
useEffect2(() => {
|
|
5761
4832
|
const figtreeFontId = "ohw-figtree-font";
|
|
5762
4833
|
if (!document.getElementById(figtreeFontId)) {
|
|
5763
4834
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -5780,47 +4851,50 @@ function OhhwellsBridge() {
|
|
|
5780
4851
|
};
|
|
5781
4852
|
}, []);
|
|
5782
4853
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
5783
|
-
const subdomain =
|
|
4854
|
+
const subdomain = subdomainFromQuery ?? (() => {
|
|
4855
|
+
if (typeof window === "undefined") return "";
|
|
4856
|
+
const parts = window.location.hostname.split(".");
|
|
4857
|
+
return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
|
|
4858
|
+
})();
|
|
5784
4859
|
const postToParent = useCallback2((data) => {
|
|
5785
4860
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
5786
4861
|
window.parent.postMessage(data, "*");
|
|
5787
4862
|
}
|
|
5788
4863
|
}, []);
|
|
5789
|
-
const [fetchState, setFetchState] =
|
|
5790
|
-
const autoSaveTimers =
|
|
5791
|
-
const activeElRef =
|
|
5792
|
-
const originalContentRef =
|
|
5793
|
-
const activeStateElRef =
|
|
5794
|
-
const parentScrollRef =
|
|
5795
|
-
const toolbarElRef =
|
|
5796
|
-
const glowElRef =
|
|
5797
|
-
const hoveredImageRef =
|
|
5798
|
-
const hoveredImageHasTextOverlapRef =
|
|
5799
|
-
const
|
|
5800
|
-
const
|
|
5801
|
-
const
|
|
5802
|
-
const
|
|
5803
|
-
const activateRef = useRef3(() => {
|
|
4864
|
+
const [fetchState, setFetchState] = useState3("idle");
|
|
4865
|
+
const autoSaveTimers = useRef2(/* @__PURE__ */ new Map());
|
|
4866
|
+
const activeElRef = useRef2(null);
|
|
4867
|
+
const originalContentRef = useRef2(null);
|
|
4868
|
+
const activeStateElRef = useRef2(null);
|
|
4869
|
+
const parentScrollRef = useRef2(null);
|
|
4870
|
+
const toolbarElRef = useRef2(null);
|
|
4871
|
+
const glowElRef = useRef2(null);
|
|
4872
|
+
const hoveredImageRef = useRef2(null);
|
|
4873
|
+
const hoveredImageHasTextOverlapRef = useRef2(false);
|
|
4874
|
+
const imageUnhoverTimerRef = useRef2(null);
|
|
4875
|
+
const imageShowTimerRef = useRef2(null);
|
|
4876
|
+
const editStylesRef = useRef2(null);
|
|
4877
|
+
const activateRef = useRef2(() => {
|
|
5804
4878
|
});
|
|
5805
|
-
const deactivateRef =
|
|
4879
|
+
const deactivateRef = useRef2(() => {
|
|
5806
4880
|
});
|
|
5807
|
-
const refreshActiveCommandsRef =
|
|
4881
|
+
const refreshActiveCommandsRef = useRef2(() => {
|
|
5808
4882
|
});
|
|
5809
|
-
const postToParentRef =
|
|
4883
|
+
const postToParentRef = useRef2(postToParent);
|
|
5810
4884
|
postToParentRef.current = postToParent;
|
|
5811
|
-
const [toolbarRect, setToolbarRect] =
|
|
5812
|
-
const [toggleState, setToggleState] =
|
|
5813
|
-
const [maxBadge, setMaxBadge] =
|
|
5814
|
-
const [activeCommands, setActiveCommands] =
|
|
5815
|
-
const [toolbarShowEditLink, setToolbarShowEditLink] =
|
|
5816
|
-
const [linkPopover, setLinkPopover] =
|
|
5817
|
-
const [sitePages, setSitePages] =
|
|
5818
|
-
const [sectionsByPath, setSectionsByPath] =
|
|
5819
|
-
const sectionsPrefetchGenRef =
|
|
5820
|
-
const setLinkPopoverRef =
|
|
5821
|
-
const linkPopoverPanelRef =
|
|
5822
|
-
const linkPopoverOpenRef =
|
|
5823
|
-
const linkPopoverGraceUntilRef =
|
|
4885
|
+
const [toolbarRect, setToolbarRect] = useState3(null);
|
|
4886
|
+
const [toggleState, setToggleState] = useState3(null);
|
|
4887
|
+
const [maxBadge, setMaxBadge] = useState3(null);
|
|
4888
|
+
const [activeCommands, setActiveCommands] = useState3(/* @__PURE__ */ new Set());
|
|
4889
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = useState3(false);
|
|
4890
|
+
const [linkPopover, setLinkPopover] = useState3(null);
|
|
4891
|
+
const [sitePages, setSitePages] = useState3([]);
|
|
4892
|
+
const [sectionsByPath, setSectionsByPath] = useState3({});
|
|
4893
|
+
const sectionsPrefetchGenRef = useRef2(0);
|
|
4894
|
+
const setLinkPopoverRef = useRef2(setLinkPopover);
|
|
4895
|
+
const linkPopoverPanelRef = useRef2(null);
|
|
4896
|
+
const linkPopoverOpenRef = useRef2(false);
|
|
4897
|
+
const linkPopoverGraceUntilRef = useRef2(0);
|
|
5824
4898
|
setLinkPopoverRef.current = setLinkPopover;
|
|
5825
4899
|
const bumpLinkPopoverGrace = () => {
|
|
5826
4900
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
@@ -5844,9 +4918,9 @@ function OhhwellsBridge() {
|
|
|
5844
4918
|
);
|
|
5845
4919
|
});
|
|
5846
4920
|
}, [isEditMode, pathname]);
|
|
5847
|
-
const runSectionsPrefetchRef =
|
|
4921
|
+
const runSectionsPrefetchRef = useRef2(runSectionsPrefetch);
|
|
5848
4922
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
5849
|
-
|
|
4923
|
+
useEffect2(() => {
|
|
5850
4924
|
if (!linkPopover) return;
|
|
5851
4925
|
if (hoveredImageRef.current) {
|
|
5852
4926
|
hoveredImageRef.current = null;
|
|
@@ -5854,7 +4928,7 @@ function OhhwellsBridge() {
|
|
|
5854
4928
|
}
|
|
5855
4929
|
postToParent({ type: "ow:image-unhover" });
|
|
5856
4930
|
}, [linkPopover, postToParent]);
|
|
5857
|
-
|
|
4931
|
+
useEffect2(() => {
|
|
5858
4932
|
if (!isEditMode) return;
|
|
5859
4933
|
const useFixtures = shouldUseDevFixtures();
|
|
5860
4934
|
if (useFixtures) {
|
|
@@ -5878,15 +4952,14 @@ function OhhwellsBridge() {
|
|
|
5878
4952
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5879
4953
|
return () => window.removeEventListener("message", onSitePages);
|
|
5880
4954
|
}, [isEditMode, postToParent]);
|
|
5881
|
-
|
|
5882
|
-
useEffect3(() => {
|
|
4955
|
+
useEffect2(() => {
|
|
5883
4956
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
5884
4957
|
void loadAllSectionsManifest().then((manifest) => {
|
|
5885
4958
|
if (Object.keys(manifest).length === 0) return;
|
|
5886
4959
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
5887
4960
|
});
|
|
5888
4961
|
}, [isEditMode]);
|
|
5889
|
-
|
|
4962
|
+
useEffect2(() => {
|
|
5890
4963
|
const update = () => {
|
|
5891
4964
|
const el = activeElRef.current;
|
|
5892
4965
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -5894,12 +4967,6 @@ function OhhwellsBridge() {
|
|
|
5894
4967
|
if (!prev || !activeStateElRef.current) return prev;
|
|
5895
4968
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
5896
4969
|
});
|
|
5897
|
-
setSectionGap((prev) => {
|
|
5898
|
-
if (!prev) return prev;
|
|
5899
|
-
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
5900
|
-
if (!anchor) return prev;
|
|
5901
|
-
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
5902
|
-
});
|
|
5903
4970
|
};
|
|
5904
4971
|
const vvp = window.visualViewport;
|
|
5905
4972
|
if (!vvp) return;
|
|
@@ -5966,7 +5033,6 @@ function OhhwellsBridge() {
|
|
|
5966
5033
|
const applyContent = (content) => {
|
|
5967
5034
|
const imageLoads = [];
|
|
5968
5035
|
for (const [key, val] of Object.entries(content)) {
|
|
5969
|
-
if (key === "__ohw_sections") continue;
|
|
5970
5036
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5971
5037
|
if (el.dataset.ohwEditable === "image") {
|
|
5972
5038
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5988,7 +5054,6 @@ function OhhwellsBridge() {
|
|
|
5988
5054
|
});
|
|
5989
5055
|
applyLinkByKey(key, val);
|
|
5990
5056
|
}
|
|
5991
|
-
initSectionsFromContent(content, true);
|
|
5992
5057
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5993
5058
|
}) : Promise.resolve();
|
|
5994
5059
|
};
|
|
@@ -6013,15 +5078,12 @@ function OhhwellsBridge() {
|
|
|
6013
5078
|
cancelled = true;
|
|
6014
5079
|
};
|
|
6015
5080
|
}, [subdomain, isEditMode, pathname]);
|
|
6016
|
-
|
|
5081
|
+
useEffect2(() => {
|
|
6017
5082
|
if (!subdomain || isEditMode) return;
|
|
6018
|
-
let debounceTimer = null;
|
|
6019
5083
|
const applyFromCache = () => {
|
|
6020
5084
|
const content = contentCache.get(subdomain);
|
|
6021
5085
|
if (!content) return;
|
|
6022
|
-
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
6023
5086
|
for (const [key, val] of Object.entries(content)) {
|
|
6024
|
-
if (key === "__ohw_sections") continue;
|
|
6025
5087
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6026
5088
|
if (el.dataset.ohwEditable === "image") {
|
|
6027
5089
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6038,28 +5100,21 @@ function OhhwellsBridge() {
|
|
|
6038
5100
|
applyLinkByKey(key, val);
|
|
6039
5101
|
}
|
|
6040
5102
|
};
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6044
|
-
};
|
|
6045
|
-
scheduleApply();
|
|
6046
|
-
const observer = new MutationObserver(scheduleApply);
|
|
5103
|
+
applyFromCache();
|
|
5104
|
+
const observer = new MutationObserver(applyFromCache);
|
|
6047
5105
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
6048
|
-
return () =>
|
|
6049
|
-
|
|
6050
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6051
|
-
};
|
|
6052
|
-
}, [subdomain, isEditMode, pathname]);
|
|
5106
|
+
return () => observer.disconnect();
|
|
5107
|
+
}, [subdomain, isEditMode]);
|
|
6053
5108
|
useLayoutEffect(() => {
|
|
6054
5109
|
const el = document.getElementById("ohw-loader");
|
|
6055
5110
|
if (!el) return;
|
|
6056
5111
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
6057
5112
|
el.style.display = visible ? "flex" : "none";
|
|
6058
5113
|
}, [subdomain, fetchState]);
|
|
6059
|
-
|
|
5114
|
+
useEffect2(() => {
|
|
6060
5115
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
6061
5116
|
}, [pathname, postToParent]);
|
|
6062
|
-
|
|
5117
|
+
useEffect2(() => {
|
|
6063
5118
|
if (!isEditMode) return;
|
|
6064
5119
|
const measure = () => {
|
|
6065
5120
|
const h = document.body.scrollHeight;
|
|
@@ -6083,7 +5138,7 @@ function OhhwellsBridge() {
|
|
|
6083
5138
|
window.removeEventListener("resize", handleResize);
|
|
6084
5139
|
};
|
|
6085
5140
|
}, [pathname, isEditMode, postToParent]);
|
|
6086
|
-
|
|
5141
|
+
useEffect2(() => {
|
|
6087
5142
|
if (!subdomainFromQuery || isEditMode) return;
|
|
6088
5143
|
const handleClick = (e) => {
|
|
6089
5144
|
const anchor = e.target.closest("a");
|
|
@@ -6099,7 +5154,7 @@ function OhhwellsBridge() {
|
|
|
6099
5154
|
document.addEventListener("click", handleClick, true);
|
|
6100
5155
|
return () => document.removeEventListener("click", handleClick, true);
|
|
6101
5156
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
6102
|
-
|
|
5157
|
+
useEffect2(() => {
|
|
6103
5158
|
if (!isEditMode) {
|
|
6104
5159
|
editStylesRef.current?.base.remove();
|
|
6105
5160
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -6400,15 +5455,6 @@ function OhhwellsBridge() {
|
|
|
6400
5455
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
6401
5456
|
return;
|
|
6402
5457
|
}
|
|
6403
|
-
if (hoveredGapRef.current) {
|
|
6404
|
-
if (hoveredImageRef.current) {
|
|
6405
|
-
hoveredImageRef.current = null;
|
|
6406
|
-
resumeAnimTracks();
|
|
6407
|
-
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6408
|
-
}
|
|
6409
|
-
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6410
|
-
return;
|
|
6411
|
-
}
|
|
6412
5458
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6413
5459
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
6414
5460
|
hoveredImageRef.current = imgEl;
|
|
@@ -6474,30 +5520,8 @@ function OhhwellsBridge() {
|
|
|
6474
5520
|
}
|
|
6475
5521
|
}
|
|
6476
5522
|
};
|
|
6477
|
-
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6478
|
-
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6479
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6480
|
-
const ZONE = 20;
|
|
6481
|
-
for (let i = 0; i < sections.length; i++) {
|
|
6482
|
-
const a = sections[i];
|
|
6483
|
-
const b = sections[i + 1] ?? null;
|
|
6484
|
-
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6485
|
-
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6486
|
-
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6487
|
-
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6488
|
-
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6489
|
-
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6490
|
-
return;
|
|
6491
|
-
}
|
|
6492
|
-
}
|
|
6493
|
-
if (hoveredGapRef.current) {
|
|
6494
|
-
hoveredGapRef.current = null;
|
|
6495
|
-
setSectionGap(null);
|
|
6496
|
-
}
|
|
6497
|
-
};
|
|
6498
5523
|
const handleMouseMove = (e) => {
|
|
6499
5524
|
const { clientX, clientY } = e;
|
|
6500
|
-
probeSectionGapAt(clientX, clientY);
|
|
6501
5525
|
probeImageAt(clientX, clientY);
|
|
6502
5526
|
probeHoverCardsAt(clientX, clientY);
|
|
6503
5527
|
};
|
|
@@ -6505,7 +5529,6 @@ function OhhwellsBridge() {
|
|
|
6505
5529
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
6506
5530
|
const { clientX, clientY } = e.data;
|
|
6507
5531
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6508
|
-
probeSectionGapAt(clientX, clientY);
|
|
6509
5532
|
probeImageAt(clientX, clientY);
|
|
6510
5533
|
probeHoverCardsAt(clientX, clientY);
|
|
6511
5534
|
};
|
|
@@ -6663,12 +5686,7 @@ function OhhwellsBridge() {
|
|
|
6663
5686
|
if (e.data?.type !== "ow:hydrate") return;
|
|
6664
5687
|
const content = e.data.content;
|
|
6665
5688
|
if (!content) return;
|
|
6666
|
-
let sectionsJson = null;
|
|
6667
5689
|
for (const [key, val] of Object.entries(content)) {
|
|
6668
|
-
if (key === "__ohw_sections") {
|
|
6669
|
-
sectionsJson = val;
|
|
6670
|
-
continue;
|
|
6671
|
-
}
|
|
6672
5690
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6673
5691
|
if (el.dataset.ohwEditable === "image") {
|
|
6674
5692
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6683,9 +5701,6 @@ function OhhwellsBridge() {
|
|
|
6683
5701
|
});
|
|
6684
5702
|
applyLinkByKey(key, val);
|
|
6685
5703
|
}
|
|
6686
|
-
if (sectionsJson) {
|
|
6687
|
-
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
6688
|
-
}
|
|
6689
5704
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
6690
5705
|
};
|
|
6691
5706
|
window.addEventListener("message", handleHydrate);
|
|
@@ -6738,63 +5753,7 @@ function OhhwellsBridge() {
|
|
|
6738
5753
|
};
|
|
6739
5754
|
const handleSave = (e) => {
|
|
6740
5755
|
if (e.data?.type !== "ow:save") return;
|
|
6741
|
-
|
|
6742
|
-
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
6743
|
-
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
6744
|
-
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
6745
|
-
};
|
|
6746
|
-
const handleInsertSection = (e) => {
|
|
6747
|
-
if (e.data?.type !== "ow:insert-section") return;
|
|
6748
|
-
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
6749
|
-
if (widgetType !== "scheduling") return;
|
|
6750
|
-
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
6751
|
-
if (inserted) {
|
|
6752
|
-
const tracker = getSectionsTracker();
|
|
6753
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
6754
|
-
const h = document.documentElement.scrollHeight;
|
|
6755
|
-
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
6756
|
-
}
|
|
6757
|
-
};
|
|
6758
|
-
const handleSwitchSchedule = (e) => {
|
|
6759
|
-
if (e.data?.type !== "ow:switch-schedule") return;
|
|
6760
|
-
const scheduleId = e.data.scheduleId;
|
|
6761
|
-
const insertAfter = e.data.insertAfter;
|
|
6762
|
-
if (!scheduleId || !insertAfter) return;
|
|
6763
|
-
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6764
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6765
|
-
};
|
|
6766
|
-
const handleScheduleLinked = (e) => {
|
|
6767
|
-
if (e.data?.type !== "ow:schedule-linked") return;
|
|
6768
|
-
const scheduleId = e.data.scheduleId;
|
|
6769
|
-
const insertAfter = e.data.insertAfter;
|
|
6770
|
-
if (!scheduleId || !insertAfter) return;
|
|
6771
|
-
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6772
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6773
|
-
};
|
|
6774
|
-
const handleClearSchedulingWidget = (e) => {
|
|
6775
|
-
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
6776
|
-
const insertAfter = e.data.insertAfter;
|
|
6777
|
-
if (!insertAfter) return;
|
|
6778
|
-
const text = updateSectionScheduleId(insertAfter, null);
|
|
6779
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6780
|
-
};
|
|
6781
|
-
const handleRemoveSchedulingSection = (e) => {
|
|
6782
|
-
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
6783
|
-
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
6784
|
-
el.remove();
|
|
6785
|
-
});
|
|
6786
|
-
const tracker = getSectionsTracker();
|
|
6787
|
-
let sections = [];
|
|
6788
|
-
try {
|
|
6789
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
6790
|
-
} catch {
|
|
6791
|
-
}
|
|
6792
|
-
const currentPath = window.location.pathname;
|
|
6793
|
-
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
6794
|
-
tracker.textContent = JSON.stringify(updated);
|
|
6795
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6796
|
-
const h = document.documentElement.scrollHeight;
|
|
6797
|
-
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5756
|
+
postToParentRef.current({ type: "ow:save-result", nodes: collectEditableNodes() });
|
|
6798
5757
|
};
|
|
6799
5758
|
const handleSelectionChange = () => {
|
|
6800
5759
|
if (!activeElRef.current) return;
|
|
@@ -6899,11 +5858,6 @@ function OhhwellsBridge() {
|
|
|
6899
5858
|
deactivateRef.current();
|
|
6900
5859
|
};
|
|
6901
5860
|
window.addEventListener("message", handleSave);
|
|
6902
|
-
window.addEventListener("message", handleInsertSection);
|
|
6903
|
-
window.addEventListener("message", handleSwitchSchedule);
|
|
6904
|
-
window.addEventListener("message", handleScheduleLinked);
|
|
6905
|
-
window.addEventListener("message", handleClearSchedulingWidget);
|
|
6906
|
-
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
6907
5861
|
window.addEventListener("message", handleImageUrl);
|
|
6908
5862
|
window.addEventListener("message", handleAnimLock);
|
|
6909
5863
|
window.addEventListener("message", handleCanvasHeight);
|
|
@@ -6938,11 +5892,6 @@ function OhhwellsBridge() {
|
|
|
6938
5892
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
6939
5893
|
window.removeEventListener("scroll", handleScroll, true);
|
|
6940
5894
|
window.removeEventListener("message", handleSave);
|
|
6941
|
-
window.removeEventListener("message", handleInsertSection);
|
|
6942
|
-
window.removeEventListener("message", handleSwitchSchedule);
|
|
6943
|
-
window.removeEventListener("message", handleScheduleLinked);
|
|
6944
|
-
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
6945
|
-
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
6946
5895
|
window.removeEventListener("message", handleImageUrl);
|
|
6947
5896
|
window.removeEventListener("message", handleAnimLock);
|
|
6948
5897
|
window.removeEventListener("message", handleCanvasHeight);
|
|
@@ -6957,7 +5906,7 @@ function OhhwellsBridge() {
|
|
|
6957
5906
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
6958
5907
|
};
|
|
6959
5908
|
}, [isEditMode, refreshStateRules]);
|
|
6960
|
-
|
|
5909
|
+
useEffect2(() => {
|
|
6961
5910
|
if (!isEditMode) return;
|
|
6962
5911
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
6963
5912
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -6986,7 +5935,7 @@ function OhhwellsBridge() {
|
|
|
6986
5935
|
clearTimeout(timer);
|
|
6987
5936
|
};
|
|
6988
5937
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
6989
|
-
|
|
5938
|
+
useEffect2(() => {
|
|
6990
5939
|
scrollToHashSectionWhenReady();
|
|
6991
5940
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
6992
5941
|
window.addEventListener("hashchange", onHashChange);
|
|
@@ -7036,10 +5985,10 @@ function OhhwellsBridge() {
|
|
|
7036
5985
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
7037
5986
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
7038
5987
|
return bridgeRoot ? createPortal(
|
|
7039
|
-
/* @__PURE__ */
|
|
7040
|
-
toolbarRect && /* @__PURE__ */
|
|
7041
|
-
/* @__PURE__ */
|
|
7042
|
-
/* @__PURE__ */
|
|
5988
|
+
/* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
5989
|
+
toolbarRect && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
5990
|
+
/* @__PURE__ */ jsx13(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
5991
|
+
/* @__PURE__ */ jsx13(
|
|
7043
5992
|
FloatingToolbar,
|
|
7044
5993
|
{
|
|
7045
5994
|
rect: toolbarRect,
|
|
@@ -7052,7 +6001,7 @@ function OhhwellsBridge() {
|
|
|
7052
6001
|
}
|
|
7053
6002
|
)
|
|
7054
6003
|
] }),
|
|
7055
|
-
maxBadge && /* @__PURE__ */
|
|
6004
|
+
maxBadge && /* @__PURE__ */ jsxs6(
|
|
7056
6005
|
"div",
|
|
7057
6006
|
{
|
|
7058
6007
|
"data-ohw-max-badge": "",
|
|
@@ -7078,7 +6027,7 @@ function OhhwellsBridge() {
|
|
|
7078
6027
|
]
|
|
7079
6028
|
}
|
|
7080
6029
|
),
|
|
7081
|
-
toggleState && /* @__PURE__ */
|
|
6030
|
+
toggleState && /* @__PURE__ */ jsx13(
|
|
7082
6031
|
StateToggle,
|
|
7083
6032
|
{
|
|
7084
6033
|
rect: toggleState.rect,
|
|
@@ -7087,7 +6036,7 @@ function OhhwellsBridge() {
|
|
|
7087
6036
|
onStateChange: handleStateChange
|
|
7088
6037
|
}
|
|
7089
6038
|
),
|
|
7090
|
-
linkPopover ? /* @__PURE__ */
|
|
6039
|
+
linkPopover ? /* @__PURE__ */ jsx13(
|
|
7091
6040
|
LinkPopover,
|
|
7092
6041
|
{
|
|
7093
6042
|
rect: linkPopover.rect,
|
|
@@ -7102,32 +6051,7 @@ function OhhwellsBridge() {
|
|
|
7102
6051
|
onClose: closeLinkPopover,
|
|
7103
6052
|
onSubmit: handleLinkPopoverSubmit
|
|
7104
6053
|
}
|
|
7105
|
-
) : null
|
|
7106
|
-
sectionGap && /* @__PURE__ */ jsxs8(
|
|
7107
|
-
"div",
|
|
7108
|
-
{
|
|
7109
|
-
"data-ohw-section-insert-line": "",
|
|
7110
|
-
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7111
|
-
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7112
|
-
children: [
|
|
7113
|
-
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7114
|
-
/* @__PURE__ */ jsx16(
|
|
7115
|
-
Badge,
|
|
7116
|
-
{
|
|
7117
|
-
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",
|
|
7118
|
-
onClick: () => {
|
|
7119
|
-
window.parent.postMessage(
|
|
7120
|
-
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7121
|
-
"*"
|
|
7122
|
-
);
|
|
7123
|
-
},
|
|
7124
|
-
children: "Add Section"
|
|
7125
|
-
}
|
|
7126
|
-
),
|
|
7127
|
-
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7128
|
-
]
|
|
7129
|
-
}
|
|
7130
|
-
)
|
|
6054
|
+
) : null
|
|
7131
6055
|
] }),
|
|
7132
6056
|
bridgeRoot
|
|
7133
6057
|
) : null;
|
|
@@ -7136,7 +6060,6 @@ export {
|
|
|
7136
6060
|
LinkEditorPanel,
|
|
7137
6061
|
LinkPopover,
|
|
7138
6062
|
OhhwellsBridge,
|
|
7139
|
-
SchedulingWidget,
|
|
7140
6063
|
Toggle,
|
|
7141
6064
|
ToggleGroup,
|
|
7142
6065
|
ToggleGroupItem,
|