@ohhwells/bridge 0.1.29 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -304
- package/dist/index.cjs +1362 -214
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1338 -191
- package/dist/index.js.map +1 -1
- package/dist/styles.css +453 -12
- package/package.json +48 -47
package/dist/index.js
CHANGED
|
@@ -1,10 +1,762 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
4
|
+
import React5, { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState5 } from "react";
|
|
5
|
+
import { createRoot } from "react-dom/client";
|
|
6
|
+
import { flushSync } from "react-dom";
|
|
7
|
+
|
|
8
|
+
// src/ui/SchedulingWidget.tsx
|
|
9
|
+
import { useEffect, useId, 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 classRunsOnDate(cls, date, type) {
|
|
168
|
+
if (type === "FIXED") {
|
|
169
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
170
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
171
|
+
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
172
|
+
}
|
|
173
|
+
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
174
|
+
}
|
|
175
|
+
function formatClassTime(cls) {
|
|
176
|
+
let h = parseInt(cls.startTime.hour, 10);
|
|
177
|
+
const m = parseInt(cls.startTime.minutes, 10);
|
|
178
|
+
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
179
|
+
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
180
|
+
const endTotal = h * 60 + m + cls.duration;
|
|
181
|
+
const eh = Math.floor(endTotal / 60) % 24;
|
|
182
|
+
const em = endTotal % 60;
|
|
183
|
+
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
184
|
+
}
|
|
185
|
+
function getBookingsOnDate(cls, date) {
|
|
186
|
+
if (!cls.bookings?.length) return 0;
|
|
187
|
+
const ds = date.toISOString().split("T")[0];
|
|
188
|
+
return cls.bookings.filter((b) => {
|
|
189
|
+
try {
|
|
190
|
+
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}).length;
|
|
195
|
+
}
|
|
196
|
+
function buildTimezoneLabel(tz) {
|
|
197
|
+
try {
|
|
198
|
+
const now = /* @__PURE__ */ new Date();
|
|
199
|
+
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
200
|
+
hour: "2-digit",
|
|
201
|
+
minute: "2-digit",
|
|
202
|
+
hour12: false,
|
|
203
|
+
timeZone: tz
|
|
204
|
+
}).format(now);
|
|
205
|
+
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
206
|
+
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
207
|
+
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
208
|
+
} catch {
|
|
209
|
+
return tz;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function EmptyState({ inEditor, insertAfter }) {
|
|
213
|
+
const handleAddSchedule = () => {
|
|
214
|
+
if (inEditor) {
|
|
215
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
|
|
219
|
+
/* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
|
|
220
|
+
"svg",
|
|
221
|
+
{
|
|
222
|
+
width: "20",
|
|
223
|
+
height: "20",
|
|
224
|
+
viewBox: "0 0 24 24",
|
|
225
|
+
fill: "none",
|
|
226
|
+
stroke: "var(--color-accent, #A89B83)",
|
|
227
|
+
strokeWidth: "1.5",
|
|
228
|
+
strokeLinecap: "round",
|
|
229
|
+
strokeLinejoin: "round",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
232
|
+
/* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
233
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
234
|
+
/* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
235
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
236
|
+
/* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
) }),
|
|
240
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
|
|
241
|
+
/* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
242
|
+
/* @__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." })
|
|
243
|
+
] }),
|
|
244
|
+
/* @__PURE__ */ jsx2(
|
|
245
|
+
"button",
|
|
246
|
+
{
|
|
247
|
+
onClick: handleAddSchedule,
|
|
248
|
+
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",
|
|
249
|
+
children: "Add Schedule"
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] });
|
|
253
|
+
}
|
|
254
|
+
function LoadingSkeleton() {
|
|
255
|
+
const shimmer = {
|
|
256
|
+
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
257
|
+
backgroundSize: "200% 100%",
|
|
258
|
+
animation: "ohw-shimmer 1.5s infinite"
|
|
259
|
+
};
|
|
260
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
261
|
+
/* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
262
|
+
/* @__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: [
|
|
263
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
264
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
265
|
+
] }, i)) }),
|
|
266
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
267
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:hidden", children: [
|
|
268
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0", children: [
|
|
269
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
|
|
270
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
|
|
271
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
|
|
272
|
+
] }),
|
|
273
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
274
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
|
|
275
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
|
|
276
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
|
|
277
|
+
] })
|
|
278
|
+
] }),
|
|
279
|
+
/* @__PURE__ */ jsxs2("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
|
|
280
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
281
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
282
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
283
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
286
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
287
|
+
] }),
|
|
288
|
+
i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
289
|
+
] }, i))
|
|
290
|
+
] });
|
|
291
|
+
}
|
|
292
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
293
|
+
const todayMs = useMemo(() => {
|
|
294
|
+
const d = /* @__PURE__ */ new Date();
|
|
295
|
+
d.setHours(0, 0, 0, 0);
|
|
296
|
+
return d.getTime();
|
|
297
|
+
}, []);
|
|
298
|
+
const scrollRef = useRef(null);
|
|
299
|
+
const [canScrollLeft, setCanScrollLeft] = useState2(false);
|
|
300
|
+
const [canScrollRight, setCanScrollRight] = useState2(false);
|
|
301
|
+
const updateScrollState = () => {
|
|
302
|
+
const el = scrollRef.current;
|
|
303
|
+
if (!el) return;
|
|
304
|
+
setCanScrollLeft(el.scrollLeft > 0);
|
|
305
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
306
|
+
};
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
const el = scrollRef.current;
|
|
309
|
+
if (!el) return;
|
|
310
|
+
updateScrollState();
|
|
311
|
+
el.addEventListener("scroll", updateScrollState);
|
|
312
|
+
const ro = new ResizeObserver(updateScrollState);
|
|
313
|
+
ro.observe(el);
|
|
314
|
+
return () => {
|
|
315
|
+
el.removeEventListener("scroll", updateScrollState);
|
|
316
|
+
ro.disconnect();
|
|
317
|
+
};
|
|
318
|
+
}, [dates]);
|
|
319
|
+
const scroll = (dir) => {
|
|
320
|
+
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
321
|
+
};
|
|
322
|
+
const isDragging = useRef(false);
|
|
323
|
+
const dragStartX = useRef(0);
|
|
324
|
+
const dragStartScrollLeft = useRef(0);
|
|
325
|
+
const onDragStart = (e) => {
|
|
326
|
+
if (!scrollRef.current) return;
|
|
327
|
+
isDragging.current = true;
|
|
328
|
+
dragStartX.current = e.clientX;
|
|
329
|
+
dragStartScrollLeft.current = scrollRef.current.scrollLeft;
|
|
330
|
+
scrollRef.current.style.cursor = "grabbing";
|
|
331
|
+
scrollRef.current.style.userSelect = "none";
|
|
332
|
+
};
|
|
333
|
+
const onDragMove = (e) => {
|
|
334
|
+
if (!isDragging.current || !scrollRef.current) return;
|
|
335
|
+
const delta = e.clientX - dragStartX.current;
|
|
336
|
+
scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
|
|
337
|
+
};
|
|
338
|
+
const onDragEnd = () => {
|
|
339
|
+
if (!scrollRef.current) return;
|
|
340
|
+
isDragging.current = false;
|
|
341
|
+
scrollRef.current.style.cursor = "grab";
|
|
342
|
+
scrollRef.current.style.userSelect = "";
|
|
343
|
+
};
|
|
344
|
+
const datesWithClasses = useMemo(
|
|
345
|
+
() => new Set(dates.map(
|
|
346
|
+
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
347
|
+
).filter((i) => i >= 0)),
|
|
348
|
+
[dates, schedule]
|
|
349
|
+
);
|
|
350
|
+
const selectedDate = dates[selectedIdx];
|
|
351
|
+
const selectedClasses = useMemo(
|
|
352
|
+
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
353
|
+
[schedule, selectedDate]
|
|
354
|
+
);
|
|
355
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
356
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
|
|
357
|
+
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
358
|
+
"button",
|
|
359
|
+
{
|
|
360
|
+
onClick: () => scroll("left"),
|
|
361
|
+
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",
|
|
362
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
363
|
+
"aria-label": "Scroll left",
|
|
364
|
+
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" }) })
|
|
365
|
+
}
|
|
366
|
+
),
|
|
367
|
+
canScrollRight && /* @__PURE__ */ jsx2(
|
|
368
|
+
"button",
|
|
369
|
+
{
|
|
370
|
+
onClick: () => scroll("right"),
|
|
371
|
+
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",
|
|
372
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
373
|
+
"aria-label": "Scroll right",
|
|
374
|
+
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" }) })
|
|
375
|
+
}
|
|
376
|
+
),
|
|
377
|
+
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
378
|
+
"div",
|
|
379
|
+
{
|
|
380
|
+
className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
381
|
+
style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
|
|
382
|
+
}
|
|
383
|
+
),
|
|
384
|
+
canScrollRight && /* @__PURE__ */ jsx2(
|
|
385
|
+
"div",
|
|
386
|
+
{
|
|
387
|
+
className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
388
|
+
style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
|
|
389
|
+
}
|
|
390
|
+
),
|
|
391
|
+
/* @__PURE__ */ jsx2(
|
|
392
|
+
"div",
|
|
393
|
+
{
|
|
394
|
+
ref: scrollRef,
|
|
395
|
+
className: "overflow-x-auto w-full",
|
|
396
|
+
style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
|
|
397
|
+
onMouseDown: onDragStart,
|
|
398
|
+
onMouseMove: onDragMove,
|
|
399
|
+
onMouseUp: onDragEnd,
|
|
400
|
+
onMouseLeave: onDragEnd,
|
|
401
|
+
children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
402
|
+
const isToday = date.getTime() === todayMs;
|
|
403
|
+
const isSelected = i === selectedIdx;
|
|
404
|
+
const clickable = datesWithClasses.has(i);
|
|
405
|
+
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
406
|
+
return /* @__PURE__ */ jsxs2(
|
|
407
|
+
"div",
|
|
408
|
+
{
|
|
409
|
+
onClick: () => clickable && onSelectDate(i),
|
|
410
|
+
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
413
|
+
/* @__PURE__ */ jsx2(
|
|
414
|
+
"div",
|
|
415
|
+
{
|
|
416
|
+
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
417
|
+
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
418
|
+
children: /* @__PURE__ */ jsx2(
|
|
419
|
+
"span",
|
|
420
|
+
{
|
|
421
|
+
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
422
|
+
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
423
|
+
children: date.getDate()
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
)
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
i
|
|
431
|
+
);
|
|
432
|
+
}) })
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
] }),
|
|
436
|
+
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) => {
|
|
437
|
+
const booked = getBookingsOnDate(cls, selectedDate);
|
|
438
|
+
const available = cls.maxParticipants - booked;
|
|
439
|
+
const isFull = available <= 0;
|
|
440
|
+
const isPrivate = cls.maxParticipants === 1;
|
|
441
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
442
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
443
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
444
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 sm:w-[120px] sm:shrink-0", children: [
|
|
445
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }),
|
|
446
|
+
/* @__PURE__ */ jsx2(
|
|
447
|
+
"span",
|
|
448
|
+
{
|
|
449
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium w-fit",
|
|
450
|
+
style: isPrivate ? { background: "#DDFBE3", color: "#199130", border: "1px solid #199130", fontWeight: 500 } : { background: "#EEEDFF", color: "#5953FF", border: "1px solid #5953FF", fontWeight: 500 },
|
|
451
|
+
children: isPrivate ? "PRIVATE" : "GROUP"
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
] }),
|
|
455
|
+
/* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
456
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
457
|
+
available,
|
|
458
|
+
"/",
|
|
459
|
+
cls.maxParticipants
|
|
460
|
+
] }),
|
|
461
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
462
|
+
] }) })
|
|
463
|
+
] }),
|
|
464
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
465
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
466
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
467
|
+
cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
468
|
+
] }),
|
|
469
|
+
/* @__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: [
|
|
470
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
471
|
+
available,
|
|
472
|
+
"/",
|
|
473
|
+
cls.maxParticipants
|
|
474
|
+
] }),
|
|
475
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
476
|
+
] }) }),
|
|
477
|
+
/* @__PURE__ */ jsx2(
|
|
478
|
+
"button",
|
|
479
|
+
{
|
|
480
|
+
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",
|
|
481
|
+
style: isFull ? {
|
|
482
|
+
background: "transparent",
|
|
483
|
+
border: "1px solid var(--color-dark, #200C02)",
|
|
484
|
+
color: "var(--color-dark, #200C02)"
|
|
485
|
+
} : {
|
|
486
|
+
background: "var(--color-primary, #3D312B)",
|
|
487
|
+
border: "none",
|
|
488
|
+
color: "var(--color-light, #FEFFF0)"
|
|
489
|
+
},
|
|
490
|
+
onClick: () => {
|
|
491
|
+
if (!cls.id) return;
|
|
492
|
+
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
493
|
+
},
|
|
494
|
+
children: isFull ? "Join Waitlist" : "Book Now"
|
|
495
|
+
}
|
|
496
|
+
)
|
|
497
|
+
] })
|
|
498
|
+
] }),
|
|
499
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
500
|
+
] }, cls.id ?? i);
|
|
501
|
+
}) })
|
|
502
|
+
] });
|
|
503
|
+
}
|
|
504
|
+
function CalendarFoldIcon() {
|
|
505
|
+
return /* @__PURE__ */ jsxs2(
|
|
506
|
+
"svg",
|
|
507
|
+
{
|
|
508
|
+
width: "16",
|
|
509
|
+
height: "16",
|
|
510
|
+
viewBox: "0 0 24 24",
|
|
511
|
+
fill: "none",
|
|
512
|
+
stroke: "currentColor",
|
|
513
|
+
strokeWidth: "2",
|
|
514
|
+
strokeLinecap: "round",
|
|
515
|
+
strokeLinejoin: "round",
|
|
516
|
+
style: { flexShrink: 0 },
|
|
517
|
+
children: [
|
|
518
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
|
|
519
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
|
|
520
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
|
|
521
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
|
|
522
|
+
/* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
|
|
528
|
+
const autoId = useId();
|
|
529
|
+
const insertAfter = insertAfterProp ?? autoId;
|
|
530
|
+
const [schedule, setSchedule] = useState2(null);
|
|
531
|
+
const [loading, setLoading] = useState2(true);
|
|
532
|
+
const [inEditor, setInEditor] = useState2(false);
|
|
533
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
534
|
+
const [modalState, setModalState] = useState2(null);
|
|
535
|
+
const switchScheduleIdRef = useRef(null);
|
|
536
|
+
const dates = useMemo(() => {
|
|
537
|
+
const today = /* @__PURE__ */ new Date();
|
|
538
|
+
today.setHours(0, 0, 0, 0);
|
|
539
|
+
return Array.from({ length: 14 }, (_, i) => {
|
|
540
|
+
const d = new Date(today);
|
|
541
|
+
d.setDate(today.getDate() + i);
|
|
542
|
+
return d;
|
|
543
|
+
});
|
|
544
|
+
}, []);
|
|
545
|
+
const [selectedIdx, setSelectedIdx] = useState2(0);
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
if (!schedule?.classes) return;
|
|
548
|
+
for (let i = 0; i < dates.length; i++) {
|
|
549
|
+
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
550
|
+
setSelectedIdx(i);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}, [schedule, dates]);
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
if (typeof window === "undefined") return;
|
|
557
|
+
const isInEditor = window.self !== window.top;
|
|
558
|
+
setInEditor(isInEditor);
|
|
559
|
+
function loadWithId(effectiveId) {
|
|
560
|
+
if (isInEditor) {
|
|
561
|
+
const startEmpty = effectiveId === null;
|
|
562
|
+
if (startEmpty) setLoading(false);
|
|
563
|
+
let initialized = startEmpty;
|
|
564
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
565
|
+
if (!initialized) {
|
|
566
|
+
initialized = true;
|
|
567
|
+
setLoading(false);
|
|
568
|
+
}
|
|
569
|
+
}, 5e3) : null;
|
|
570
|
+
const handler = (e) => {
|
|
571
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
572
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
573
|
+
setSchedule(null);
|
|
574
|
+
setLoading(false);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
578
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
579
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
580
|
+
initialized = false;
|
|
581
|
+
setLoading(true);
|
|
582
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
586
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
587
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
588
|
+
initialized = true;
|
|
589
|
+
if (timer) clearTimeout(timer);
|
|
590
|
+
const schedules = e.data.schedules ?? [];
|
|
591
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
592
|
+
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : effectiveId ? schedules.find((s) => s.id === effectiveId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
593
|
+
switchScheduleIdRef.current = null;
|
|
594
|
+
setSchedule(target);
|
|
595
|
+
setLoading(false);
|
|
596
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
597
|
+
window.parent.postMessage({
|
|
598
|
+
type: "ow:schedule-connected",
|
|
599
|
+
schedule: { id: target.id, name: target.name },
|
|
600
|
+
insertAfter
|
|
601
|
+
}, "*");
|
|
602
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
window.addEventListener("message", handler);
|
|
606
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
607
|
+
return () => {
|
|
608
|
+
window.removeEventListener("message", handler);
|
|
609
|
+
if (timer) clearTimeout(timer);
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (!effectiveId) {
|
|
613
|
+
setLoading(false);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
;
|
|
617
|
+
(async () => {
|
|
618
|
+
try {
|
|
619
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
620
|
+
const data = await res.json();
|
|
621
|
+
setSchedule(data?.id ? data : null);
|
|
622
|
+
} catch {
|
|
623
|
+
}
|
|
624
|
+
setLoading(false);
|
|
625
|
+
})();
|
|
626
|
+
}
|
|
627
|
+
if (initialScheduleId === void 0) {
|
|
628
|
+
let done = false;
|
|
629
|
+
let innerCleanup;
|
|
630
|
+
const timer = setTimeout(() => {
|
|
631
|
+
if (!done) {
|
|
632
|
+
done = true;
|
|
633
|
+
setLoading(false);
|
|
634
|
+
}
|
|
635
|
+
}, 2e3);
|
|
636
|
+
const configHandler = (e) => {
|
|
637
|
+
if (e.data?.type !== "ow:schedule-config" || e.data.insertAfter !== insertAfter || done) return;
|
|
638
|
+
done = true;
|
|
639
|
+
clearTimeout(timer);
|
|
640
|
+
window.removeEventListener("message", configHandler);
|
|
641
|
+
innerCleanup = loadWithId(e.data.scheduleId ?? null);
|
|
642
|
+
};
|
|
643
|
+
window.addEventListener("message", configHandler);
|
|
644
|
+
window.postMessage({ type: "ow:request-schedule-config", insertAfter }, "*");
|
|
645
|
+
return () => {
|
|
646
|
+
window.removeEventListener("message", configHandler);
|
|
647
|
+
clearTimeout(timer);
|
|
648
|
+
innerCleanup?.();
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
return loadWithId(initialScheduleId) ?? void 0;
|
|
652
|
+
}, []);
|
|
653
|
+
const timezoneLabel = schedule?.timezone ? (() => {
|
|
654
|
+
try {
|
|
655
|
+
return buildTimezoneLabel(schedule.timezone);
|
|
656
|
+
} catch {
|
|
657
|
+
return void 0;
|
|
658
|
+
}
|
|
659
|
+
})() : void 0;
|
|
660
|
+
const handleModalSubmit = async (email) => {
|
|
661
|
+
if (!modalState) return;
|
|
662
|
+
const { mode, classId, classDate } = modalState;
|
|
663
|
+
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
664
|
+
const res = await fetch(endpoint, {
|
|
665
|
+
method: "POST",
|
|
666
|
+
headers: { "Content-Type": "application/json" },
|
|
667
|
+
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
668
|
+
});
|
|
669
|
+
if (!res.ok) {
|
|
670
|
+
const data = await res.json().catch(() => ({}));
|
|
671
|
+
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
const handleReplaceSchedule = () => {
|
|
675
|
+
setIsHovered(false);
|
|
676
|
+
if (schedule) {
|
|
677
|
+
window.parent.postMessage({
|
|
678
|
+
type: "ow:schedule-connected",
|
|
679
|
+
schedule: { id: schedule.id, name: schedule.name },
|
|
680
|
+
insertAfter
|
|
681
|
+
}, "*");
|
|
682
|
+
} else {
|
|
683
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
if (!inEditor && !loading && !schedule) return null;
|
|
687
|
+
const sectionId = `scheduling-${insertAfter}`;
|
|
688
|
+
return /* @__PURE__ */ jsxs2(
|
|
689
|
+
"section",
|
|
690
|
+
{
|
|
691
|
+
"data-ohw-section": sectionId,
|
|
692
|
+
"data-ohw-scheduling-anchor": insertAfter,
|
|
693
|
+
className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
694
|
+
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
695
|
+
onMouseLeave: () => setIsHovered(false),
|
|
696
|
+
children: [
|
|
697
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
|
|
698
|
+
"div",
|
|
699
|
+
{
|
|
700
|
+
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
701
|
+
onClick: handleReplaceSchedule,
|
|
702
|
+
children: [
|
|
703
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
704
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
705
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
709
|
+
style: {
|
|
710
|
+
fontSize: 14,
|
|
711
|
+
lineHeight: "24px",
|
|
712
|
+
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
713
|
+
fontWeight: 500,
|
|
714
|
+
color: "#0C0A09"
|
|
715
|
+
},
|
|
716
|
+
children: [
|
|
717
|
+
/* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
|
|
718
|
+
"Replace schedule"
|
|
719
|
+
]
|
|
720
|
+
}
|
|
721
|
+
) })
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
/* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
726
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
|
|
727
|
+
/* @__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" }),
|
|
728
|
+
schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
729
|
+
] }),
|
|
730
|
+
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
731
|
+
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
732
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor, insertAfter }) : /* @__PURE__ */ jsx2(
|
|
733
|
+
ScheduleView,
|
|
734
|
+
{
|
|
735
|
+
schedule,
|
|
736
|
+
dates,
|
|
737
|
+
selectedIdx,
|
|
738
|
+
onSelectDate: setSelectedIdx,
|
|
739
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
740
|
+
}
|
|
741
|
+
) })
|
|
742
|
+
] })
|
|
743
|
+
] }),
|
|
744
|
+
modalState && /* @__PURE__ */ jsx2(
|
|
745
|
+
EmailCaptureModal,
|
|
746
|
+
{
|
|
747
|
+
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
748
|
+
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
749
|
+
onSubmit: handleModalSubmit,
|
|
750
|
+
onClose: () => setModalState(null)
|
|
751
|
+
}
|
|
752
|
+
)
|
|
753
|
+
]
|
|
754
|
+
}
|
|
755
|
+
);
|
|
756
|
+
}
|
|
5
757
|
|
|
6
758
|
// src/ui/toggle-group.tsx
|
|
7
|
-
import * as
|
|
759
|
+
import * as React2 from "react";
|
|
8
760
|
|
|
9
761
|
// node_modules/clsx/dist/clsx.mjs
|
|
10
762
|
function r(e) {
|
|
@@ -3324,7 +4076,7 @@ var cva = (base, config) => (props) => {
|
|
|
3324
4076
|
|
|
3325
4077
|
// src/ui/toggle.tsx
|
|
3326
4078
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
3327
|
-
import { jsx } from "react/jsx-runtime";
|
|
4079
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3328
4080
|
var toggleVariants = cva(
|
|
3329
4081
|
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
3330
4082
|
{
|
|
@@ -3351,7 +4103,7 @@ function Toggle({
|
|
|
3351
4103
|
size,
|
|
3352
4104
|
...props
|
|
3353
4105
|
}) {
|
|
3354
|
-
return /* @__PURE__ */
|
|
4106
|
+
return /* @__PURE__ */ jsx3(
|
|
3355
4107
|
TogglePrimitive.Root,
|
|
3356
4108
|
{
|
|
3357
4109
|
"data-slot": "toggle",
|
|
@@ -3362,8 +4114,8 @@ function Toggle({
|
|
|
3362
4114
|
}
|
|
3363
4115
|
|
|
3364
4116
|
// src/ui/toggle-group.tsx
|
|
3365
|
-
import { jsx as
|
|
3366
|
-
var ToggleGroupContext =
|
|
4117
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4118
|
+
var ToggleGroupContext = React2.createContext({
|
|
3367
4119
|
value: "",
|
|
3368
4120
|
onValueChange: () => {
|
|
3369
4121
|
}
|
|
@@ -3375,13 +4127,13 @@ function ToggleGroup({
|
|
|
3375
4127
|
children,
|
|
3376
4128
|
...props
|
|
3377
4129
|
}) {
|
|
3378
|
-
return /* @__PURE__ */
|
|
4130
|
+
return /* @__PURE__ */ jsx4(
|
|
3379
4131
|
"div",
|
|
3380
4132
|
{
|
|
3381
4133
|
role: "group",
|
|
3382
4134
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3383
4135
|
...props,
|
|
3384
|
-
children: /* @__PURE__ */
|
|
4136
|
+
children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3385
4137
|
}
|
|
3386
4138
|
);
|
|
3387
4139
|
}
|
|
@@ -3391,8 +4143,8 @@ function ToggleGroupItem({
|
|
|
3391
4143
|
children,
|
|
3392
4144
|
...props
|
|
3393
4145
|
}) {
|
|
3394
|
-
const { value: groupValue, onValueChange } =
|
|
3395
|
-
return /* @__PURE__ */
|
|
4146
|
+
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
4147
|
+
return /* @__PURE__ */ jsx4(
|
|
3396
4148
|
Toggle,
|
|
3397
4149
|
{
|
|
3398
4150
|
pressed: groupValue === value,
|
|
@@ -3706,9 +4458,9 @@ function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
|
|
|
3706
4458
|
}
|
|
3707
4459
|
|
|
3708
4460
|
// src/ui/button.tsx
|
|
3709
|
-
import * as
|
|
4461
|
+
import * as React3 from "react";
|
|
3710
4462
|
import { Slot } from "radix-ui";
|
|
3711
|
-
import { jsx as
|
|
4463
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3712
4464
|
var buttonVariants = cva(
|
|
3713
4465
|
"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",
|
|
3714
4466
|
{
|
|
@@ -3729,10 +4481,10 @@ var buttonVariants = cva(
|
|
|
3729
4481
|
}
|
|
3730
4482
|
}
|
|
3731
4483
|
);
|
|
3732
|
-
var Button =
|
|
4484
|
+
var Button = React3.forwardRef(
|
|
3733
4485
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
3734
4486
|
const Comp = asChild ? Slot.Root : "button";
|
|
3735
|
-
return /* @__PURE__ */
|
|
4487
|
+
return /* @__PURE__ */ jsx5(
|
|
3736
4488
|
Comp,
|
|
3737
4489
|
{
|
|
3738
4490
|
ref,
|
|
@@ -3746,75 +4498,75 @@ var Button = React2.forwardRef(
|
|
|
3746
4498
|
Button.displayName = "Button";
|
|
3747
4499
|
|
|
3748
4500
|
// src/ui/icons.tsx
|
|
3749
|
-
import { jsx as
|
|
4501
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3750
4502
|
function IconX({ className, ...props }) {
|
|
3751
|
-
return /* @__PURE__ */
|
|
3752
|
-
/* @__PURE__ */
|
|
3753
|
-
/* @__PURE__ */
|
|
4503
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4504
|
+
/* @__PURE__ */ jsx6("path", { d: "M18 6 6 18" }),
|
|
4505
|
+
/* @__PURE__ */ jsx6("path", { d: "m6 6 12 12" })
|
|
3754
4506
|
] });
|
|
3755
4507
|
}
|
|
3756
4508
|
function IconChevronDown({ className, ...props }) {
|
|
3757
|
-
return /* @__PURE__ */
|
|
4509
|
+
return /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
|
|
3758
4510
|
}
|
|
3759
4511
|
function IconFile({ className, ...props }) {
|
|
3760
|
-
return /* @__PURE__ */
|
|
3761
|
-
/* @__PURE__ */
|
|
3762
|
-
/* @__PURE__ */
|
|
4512
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4513
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4514
|
+
/* @__PURE__ */ jsx6("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
3763
4515
|
] });
|
|
3764
4516
|
}
|
|
3765
4517
|
function IconInfo({ className, ...props }) {
|
|
3766
|
-
return /* @__PURE__ */
|
|
3767
|
-
/* @__PURE__ */
|
|
3768
|
-
/* @__PURE__ */
|
|
3769
|
-
/* @__PURE__ */
|
|
4518
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4519
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4520
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 16v-4" }),
|
|
4521
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 8h.01" })
|
|
3770
4522
|
] });
|
|
3771
4523
|
}
|
|
3772
4524
|
function IconArrowRight({ className, ...props }) {
|
|
3773
|
-
return /* @__PURE__ */
|
|
3774
|
-
/* @__PURE__ */
|
|
3775
|
-
/* @__PURE__ */
|
|
4525
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4526
|
+
/* @__PURE__ */ jsx6("path", { d: "M5 12h14" }),
|
|
4527
|
+
/* @__PURE__ */ jsx6("path", { d: "m12 5 7 7-7 7" })
|
|
3776
4528
|
] });
|
|
3777
4529
|
}
|
|
3778
4530
|
function IconSection({ className, ...props }) {
|
|
3779
|
-
return /* @__PURE__ */
|
|
3780
|
-
/* @__PURE__ */
|
|
3781
|
-
/* @__PURE__ */
|
|
3782
|
-
/* @__PURE__ */
|
|
4531
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4532
|
+
/* @__PURE__ */ jsx6("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4533
|
+
/* @__PURE__ */ jsx6("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4534
|
+
/* @__PURE__ */ jsx6("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
3783
4535
|
] });
|
|
3784
4536
|
}
|
|
3785
4537
|
|
|
3786
4538
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
3787
|
-
import { jsx as
|
|
4539
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3788
4540
|
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
3789
|
-
return /* @__PURE__ */
|
|
3790
|
-
/* @__PURE__ */
|
|
3791
|
-
/* @__PURE__ */
|
|
3792
|
-
/* @__PURE__ */
|
|
3793
|
-
/* @__PURE__ */
|
|
3794
|
-
/* @__PURE__ */
|
|
4541
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2", children: [
|
|
4542
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
4543
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
4544
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
4545
|
+
/* @__PURE__ */ jsx7(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4546
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
3795
4547
|
] }),
|
|
3796
|
-
/* @__PURE__ */
|
|
3797
|
-
/* @__PURE__ */
|
|
3798
|
-
/* @__PURE__ */
|
|
3799
|
-
/* @__PURE__ */
|
|
4548
|
+
/* @__PURE__ */ jsx7(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
4549
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
4550
|
+
/* @__PURE__ */ jsx7(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4551
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
3800
4552
|
] })
|
|
3801
4553
|
] })
|
|
3802
4554
|
] });
|
|
3803
4555
|
}
|
|
3804
4556
|
|
|
3805
4557
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
3806
|
-
import { jsx as
|
|
4558
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3807
4559
|
function SectionTreeItem({ section, onSelect, selected }) {
|
|
3808
4560
|
const interactive = Boolean(onSelect);
|
|
3809
|
-
return /* @__PURE__ */
|
|
3810
|
-
/* @__PURE__ */
|
|
4561
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
4562
|
+
/* @__PURE__ */ jsx8(
|
|
3811
4563
|
"div",
|
|
3812
4564
|
{
|
|
3813
4565
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
3814
4566
|
"aria-hidden": true
|
|
3815
4567
|
}
|
|
3816
4568
|
),
|
|
3817
|
-
/* @__PURE__ */
|
|
4569
|
+
/* @__PURE__ */ jsxs5(
|
|
3818
4570
|
"div",
|
|
3819
4571
|
{
|
|
3820
4572
|
role: interactive ? "button" : void 0,
|
|
@@ -3832,8 +4584,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3832
4584
|
interactive && selected && "border-primary"
|
|
3833
4585
|
),
|
|
3834
4586
|
children: [
|
|
3835
|
-
/* @__PURE__ */
|
|
3836
|
-
/* @__PURE__ */
|
|
4587
|
+
/* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4588
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
3837
4589
|
]
|
|
3838
4590
|
}
|
|
3839
4591
|
)
|
|
@@ -3841,23 +4593,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3841
4593
|
}
|
|
3842
4594
|
function SectionPickerList({ sections, onSelect }) {
|
|
3843
4595
|
if (sections.length === 0) {
|
|
3844
|
-
return /* @__PURE__ */
|
|
4596
|
+
return /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
3845
4597
|
}
|
|
3846
|
-
return /* @__PURE__ */
|
|
3847
|
-
/* @__PURE__ */
|
|
3848
|
-
sections.map((section) => /* @__PURE__ */
|
|
4598
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
4599
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
4600
|
+
sections.map((section) => /* @__PURE__ */ jsx8(SectionTreeItem, { section, onSelect }, section.id))
|
|
3849
4601
|
] });
|
|
3850
4602
|
}
|
|
3851
4603
|
|
|
3852
4604
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3853
|
-
import { useId, useRef, useState } from "react";
|
|
4605
|
+
import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
3854
4606
|
|
|
3855
4607
|
// src/ui/input.tsx
|
|
3856
|
-
import * as
|
|
3857
|
-
import { jsx as
|
|
3858
|
-
var Input =
|
|
4608
|
+
import * as React4 from "react";
|
|
4609
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4610
|
+
var Input = React4.forwardRef(
|
|
3859
4611
|
({ className, type, ...props }, ref) => {
|
|
3860
|
-
return /* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ jsx9(
|
|
3861
4613
|
"input",
|
|
3862
4614
|
{
|
|
3863
4615
|
type,
|
|
@@ -3876,9 +4628,9 @@ Input.displayName = "Input";
|
|
|
3876
4628
|
|
|
3877
4629
|
// src/ui/label.tsx
|
|
3878
4630
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
3879
|
-
import { jsx as
|
|
4631
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3880
4632
|
function Label({ className, ...props }) {
|
|
3881
|
-
return /* @__PURE__ */
|
|
4633
|
+
return /* @__PURE__ */ jsx10(
|
|
3882
4634
|
LabelPrimitive.Root,
|
|
3883
4635
|
{
|
|
3884
4636
|
"data-slot": "label",
|
|
@@ -3890,12 +4642,12 @@ function Label({ className, ...props }) {
|
|
|
3890
4642
|
|
|
3891
4643
|
// src/ui/popover.tsx
|
|
3892
4644
|
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
3893
|
-
import { jsx as
|
|
4645
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
3894
4646
|
function Popover({ ...props }) {
|
|
3895
|
-
return /* @__PURE__ */
|
|
4647
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
3896
4648
|
}
|
|
3897
4649
|
function PopoverTrigger({ ...props }) {
|
|
3898
|
-
return /* @__PURE__ */
|
|
4650
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
3899
4651
|
}
|
|
3900
4652
|
function PopoverContent({
|
|
3901
4653
|
className,
|
|
@@ -3903,7 +4655,7 @@ function PopoverContent({
|
|
|
3903
4655
|
sideOffset = 6,
|
|
3904
4656
|
...props
|
|
3905
4657
|
}) {
|
|
3906
|
-
return /* @__PURE__ */
|
|
4658
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
|
|
3907
4659
|
PopoverPrimitive.Content,
|
|
3908
4660
|
{
|
|
3909
4661
|
"data-slot": "popover-content",
|
|
@@ -3919,9 +4671,9 @@ function PopoverContent({
|
|
|
3919
4671
|
}
|
|
3920
4672
|
|
|
3921
4673
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3922
|
-
import { jsx as
|
|
4674
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3923
4675
|
function FieldChevron({ onClick }) {
|
|
3924
|
-
return /* @__PURE__ */
|
|
4676
|
+
return /* @__PURE__ */ jsx12(
|
|
3925
4677
|
"button",
|
|
3926
4678
|
{
|
|
3927
4679
|
type: "button",
|
|
@@ -3929,7 +4681,7 @@ function FieldChevron({ onClick }) {
|
|
|
3929
4681
|
onClick,
|
|
3930
4682
|
"aria-label": "Open page list",
|
|
3931
4683
|
tabIndex: -1,
|
|
3932
|
-
children: /* @__PURE__ */
|
|
4684
|
+
children: /* @__PURE__ */ jsx12(IconChevronDown, {})
|
|
3933
4685
|
}
|
|
3934
4686
|
);
|
|
3935
4687
|
}
|
|
@@ -3945,9 +4697,9 @@ function UrlOrPageInput({
|
|
|
3945
4697
|
readOnly = false,
|
|
3946
4698
|
urlError
|
|
3947
4699
|
}) {
|
|
3948
|
-
const inputId =
|
|
3949
|
-
const inputRef =
|
|
3950
|
-
const [isFocused, setIsFocused] =
|
|
4700
|
+
const inputId = useId2();
|
|
4701
|
+
const inputRef = useRef2(null);
|
|
4702
|
+
const [isFocused, setIsFocused] = useState3(false);
|
|
3951
4703
|
const openDropdown = () => {
|
|
3952
4704
|
if (readOnly || filteredPages.length === 0) return;
|
|
3953
4705
|
onDropdownOpenChange(true);
|
|
@@ -3968,12 +4720,12 @@ function UrlOrPageInput({
|
|
|
3968
4720
|
"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]",
|
|
3969
4721
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
3970
4722
|
);
|
|
3971
|
-
return /* @__PURE__ */
|
|
3972
|
-
/* @__PURE__ */
|
|
3973
|
-
/* @__PURE__ */
|
|
3974
|
-
/* @__PURE__ */
|
|
3975
|
-
selectedPage ? /* @__PURE__ */
|
|
3976
|
-
readOnly ? /* @__PURE__ */
|
|
4723
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4724
|
+
/* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4725
|
+
/* @__PURE__ */ jsxs6(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
|
|
4726
|
+
/* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
4727
|
+
selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4728
|
+
readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
|
|
3977
4729
|
Input,
|
|
3978
4730
|
{
|
|
3979
4731
|
ref: inputRef,
|
|
@@ -3992,7 +4744,7 @@ function UrlOrPageInput({
|
|
|
3992
4744
|
)
|
|
3993
4745
|
}
|
|
3994
4746
|
),
|
|
3995
|
-
selectedPage && !readOnly ? /* @__PURE__ */
|
|
4747
|
+
selectedPage && !readOnly ? /* @__PURE__ */ jsx12(
|
|
3996
4748
|
"button",
|
|
3997
4749
|
{
|
|
3998
4750
|
type: "button",
|
|
@@ -4000,31 +4752,31 @@ function UrlOrPageInput({
|
|
|
4000
4752
|
onMouseDown: clearSelection,
|
|
4001
4753
|
"aria-label": "Clear selected page",
|
|
4002
4754
|
tabIndex: -1,
|
|
4003
|
-
children: /* @__PURE__ */
|
|
4755
|
+
children: /* @__PURE__ */ jsx12(IconX, { "aria-hidden": true })
|
|
4004
4756
|
}
|
|
4005
4757
|
) : null,
|
|
4006
|
-
!readOnly ? /* @__PURE__ */
|
|
4758
|
+
!readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4007
4759
|
] }) }),
|
|
4008
|
-
filteredPages.length > 0 && /* @__PURE__ */
|
|
4760
|
+
filteredPages.length > 0 && /* @__PURE__ */ jsx12(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs6(
|
|
4009
4761
|
"button",
|
|
4010
4762
|
{
|
|
4011
4763
|
type: "button",
|
|
4012
4764
|
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",
|
|
4013
4765
|
onClick: () => onPageSelect(page),
|
|
4014
4766
|
children: [
|
|
4015
|
-
/* @__PURE__ */
|
|
4016
|
-
/* @__PURE__ */
|
|
4767
|
+
/* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4768
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
|
|
4017
4769
|
]
|
|
4018
4770
|
},
|
|
4019
4771
|
page.path
|
|
4020
4772
|
)) })
|
|
4021
4773
|
] }),
|
|
4022
|
-
urlError ? /* @__PURE__ */
|
|
4774
|
+
urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4023
4775
|
] });
|
|
4024
4776
|
}
|
|
4025
4777
|
|
|
4026
4778
|
// src/ui/link-modal/useLinkModalState.ts
|
|
4027
|
-
import { useCallback, useEffect, useMemo, useState as
|
|
4779
|
+
import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
|
|
4028
4780
|
function useLinkModalState({
|
|
4029
4781
|
open,
|
|
4030
4782
|
mode,
|
|
@@ -4036,16 +4788,16 @@ function useLinkModalState({
|
|
|
4036
4788
|
onClose,
|
|
4037
4789
|
onSubmit
|
|
4038
4790
|
}) {
|
|
4039
|
-
const availablePages =
|
|
4791
|
+
const availablePages = useMemo2(
|
|
4040
4792
|
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4041
4793
|
[mode, pages, existingTargets]
|
|
4042
4794
|
);
|
|
4043
|
-
const [searchValue, setSearchValue] =
|
|
4044
|
-
const [selectedPage, setSelectedPage] =
|
|
4045
|
-
const [selectedSection, setSelectedSection] =
|
|
4046
|
-
const [step, setStep] =
|
|
4047
|
-
const [dropdownOpen, setDropdownOpen] =
|
|
4048
|
-
const [urlError, setUrlError] =
|
|
4795
|
+
const [searchValue, setSearchValue] = useState4("");
|
|
4796
|
+
const [selectedPage, setSelectedPage] = useState4(null);
|
|
4797
|
+
const [selectedSection, setSelectedSection] = useState4(null);
|
|
4798
|
+
const [step, setStep] = useState4("input");
|
|
4799
|
+
const [dropdownOpen, setDropdownOpen] = useState4(false);
|
|
4800
|
+
const [urlError, setUrlError] = useState4("");
|
|
4049
4801
|
const reset = useCallback(() => {
|
|
4050
4802
|
setSearchValue("");
|
|
4051
4803
|
setSelectedPage(null);
|
|
@@ -4054,7 +4806,7 @@ function useLinkModalState({
|
|
|
4054
4806
|
setDropdownOpen(false);
|
|
4055
4807
|
setUrlError("");
|
|
4056
4808
|
}, []);
|
|
4057
|
-
|
|
4809
|
+
useEffect2(() => {
|
|
4058
4810
|
if (!open) return;
|
|
4059
4811
|
if (mode === "edit" && initialTarget) {
|
|
4060
4812
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -4070,11 +4822,11 @@ function useLinkModalState({
|
|
|
4070
4822
|
setDropdownOpen(false);
|
|
4071
4823
|
setUrlError("");
|
|
4072
4824
|
}, [open, mode, initialTarget, pages, sectionsByPath, reset]);
|
|
4073
|
-
const filteredPages =
|
|
4825
|
+
const filteredPages = useMemo2(() => {
|
|
4074
4826
|
if (!searchValue.trim()) return availablePages;
|
|
4075
4827
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4076
4828
|
}, [availablePages, searchValue]);
|
|
4077
|
-
const activeSections =
|
|
4829
|
+
const activeSections = useMemo2(() => {
|
|
4078
4830
|
if (!selectedPage) return [];
|
|
4079
4831
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
4080
4832
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -4120,7 +4872,7 @@ function useLinkModalState({
|
|
|
4120
4872
|
reset();
|
|
4121
4873
|
onClose();
|
|
4122
4874
|
};
|
|
4123
|
-
const isValid =
|
|
4875
|
+
const isValid = useMemo2(() => {
|
|
4124
4876
|
if (urlError) return false;
|
|
4125
4877
|
if (showBreadcrumb) return true;
|
|
4126
4878
|
if (selectedPage) return true;
|
|
@@ -4187,7 +4939,7 @@ function useLinkModalState({
|
|
|
4187
4939
|
}
|
|
4188
4940
|
|
|
4189
4941
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4190
|
-
import { Fragment, jsx as
|
|
4942
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4191
4943
|
function LinkEditorPanel({
|
|
4192
4944
|
open = true,
|
|
4193
4945
|
mode = "create",
|
|
@@ -4211,26 +4963,26 @@ function LinkEditorPanel({
|
|
|
4211
4963
|
onSubmit
|
|
4212
4964
|
});
|
|
4213
4965
|
const isCancel = state.secondaryLabel === "Cancel";
|
|
4214
|
-
return /* @__PURE__ */
|
|
4215
|
-
/* @__PURE__ */
|
|
4966
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
4967
|
+
/* @__PURE__ */ jsx13(
|
|
4216
4968
|
"button",
|
|
4217
4969
|
{
|
|
4218
4970
|
type: "button",
|
|
4219
4971
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4220
4972
|
onClick: state.handleClose,
|
|
4221
4973
|
"aria-label": "Close",
|
|
4222
|
-
children: /* @__PURE__ */
|
|
4974
|
+
children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
|
|
4223
4975
|
}
|
|
4224
4976
|
),
|
|
4225
|
-
/* @__PURE__ */
|
|
4226
|
-
/* @__PURE__ */
|
|
4227
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */
|
|
4977
|
+
/* @__PURE__ */ jsx13("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
|
|
4978
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
4979
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
|
|
4228
4980
|
DestinationBreadcrumb,
|
|
4229
4981
|
{
|
|
4230
4982
|
pageTitle: state.selectedPage.title,
|
|
4231
4983
|
sectionLabel: state.selectedSection.label
|
|
4232
4984
|
}
|
|
4233
|
-
) : /* @__PURE__ */
|
|
4985
|
+
) : /* @__PURE__ */ jsx13(
|
|
4234
4986
|
UrlOrPageInput,
|
|
4235
4987
|
{
|
|
4236
4988
|
value: state.searchValue,
|
|
@@ -4243,18 +4995,18 @@ function LinkEditorPanel({
|
|
|
4243
4995
|
urlError: state.urlError
|
|
4244
4996
|
}
|
|
4245
4997
|
),
|
|
4246
|
-
state.showChooseSection ? /* @__PURE__ */
|
|
4247
|
-
/* @__PURE__ */
|
|
4248
|
-
/* @__PURE__ */
|
|
4249
|
-
/* @__PURE__ */
|
|
4250
|
-
/* @__PURE__ */
|
|
4998
|
+
state.showChooseSection ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
4999
|
+
/* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
5000
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
5001
|
+
/* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
5002
|
+
/* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
|
|
4251
5003
|
] })
|
|
4252
5004
|
] }) : null,
|
|
4253
|
-
state.showSectionPicker ? /* @__PURE__ */
|
|
4254
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */
|
|
5005
|
+
state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
5006
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4255
5007
|
] }),
|
|
4256
|
-
/* @__PURE__ */
|
|
4257
|
-
/* @__PURE__ */
|
|
5008
|
+
/* @__PURE__ */ jsxs7("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
|
|
5009
|
+
/* @__PURE__ */ jsx13(
|
|
4258
5010
|
Button,
|
|
4259
5011
|
{
|
|
4260
5012
|
type: "button",
|
|
@@ -4269,7 +5021,7 @@ function LinkEditorPanel({
|
|
|
4269
5021
|
children: state.secondaryLabel
|
|
4270
5022
|
}
|
|
4271
5023
|
),
|
|
4272
|
-
/* @__PURE__ */
|
|
5024
|
+
/* @__PURE__ */ jsx13(
|
|
4273
5025
|
Button,
|
|
4274
5026
|
{
|
|
4275
5027
|
type: "button",
|
|
@@ -4288,7 +5040,7 @@ function LinkEditorPanel({
|
|
|
4288
5040
|
}
|
|
4289
5041
|
|
|
4290
5042
|
// src/ui/link-modal/LinkPopover.tsx
|
|
4291
|
-
import { jsx as
|
|
5043
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4292
5044
|
function LinkPopover({
|
|
4293
5045
|
rect,
|
|
4294
5046
|
parentScroll,
|
|
@@ -4296,7 +5048,7 @@ function LinkPopover({
|
|
|
4296
5048
|
...editorProps
|
|
4297
5049
|
}) {
|
|
4298
5050
|
const { top, left, transform } = calcPopoverPos(rect, parentScroll);
|
|
4299
|
-
return /* @__PURE__ */
|
|
5051
|
+
return /* @__PURE__ */ jsx14(
|
|
4300
5052
|
"div",
|
|
4301
5053
|
{
|
|
4302
5054
|
ref: panelRef,
|
|
@@ -4309,7 +5061,7 @@ function LinkPopover({
|
|
|
4309
5061
|
),
|
|
4310
5062
|
style: { top, left, transform },
|
|
4311
5063
|
onMouseDown: (e) => e.stopPropagation(),
|
|
4312
|
-
children: /* @__PURE__ */
|
|
5064
|
+
children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open: true })
|
|
4313
5065
|
}
|
|
4314
5066
|
);
|
|
4315
5067
|
}
|
|
@@ -4372,8 +5124,30 @@ function shouldUseDevFixtures() {
|
|
|
4372
5124
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
4373
5125
|
}
|
|
4374
5126
|
|
|
5127
|
+
// src/ui/badge.tsx
|
|
5128
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
5129
|
+
var badgeVariants = cva(
|
|
5130
|
+
"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",
|
|
5131
|
+
{
|
|
5132
|
+
variants: {
|
|
5133
|
+
variant: {
|
|
5134
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
5135
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
5136
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
5137
|
+
outline: "text-foreground"
|
|
5138
|
+
}
|
|
5139
|
+
},
|
|
5140
|
+
defaultVariants: {
|
|
5141
|
+
variant: "default"
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
);
|
|
5145
|
+
function Badge({ className, variant, ...props }) {
|
|
5146
|
+
return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5147
|
+
}
|
|
5148
|
+
|
|
4375
5149
|
// src/OhhwellsBridge.tsx
|
|
4376
|
-
import { Fragment as
|
|
5150
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4377
5151
|
var PRIMARY = "#0885FE";
|
|
4378
5152
|
var IMAGE_FADE_MS = 300;
|
|
4379
5153
|
function runOpacityFade(el, onDone) {
|
|
@@ -4424,6 +5198,169 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
4424
5198
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
4425
5199
|
});
|
|
4426
5200
|
}
|
|
5201
|
+
function getSectionsTracker() {
|
|
5202
|
+
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
5203
|
+
if (!el) {
|
|
5204
|
+
el = document.createElement("div");
|
|
5205
|
+
el.setAttribute("data-ohw-sections-tracker", "");
|
|
5206
|
+
el.style.display = "none";
|
|
5207
|
+
document.body.appendChild(el);
|
|
5208
|
+
}
|
|
5209
|
+
return el;
|
|
5210
|
+
}
|
|
5211
|
+
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
5212
|
+
const tracker = getSectionsTracker();
|
|
5213
|
+
let sections = [];
|
|
5214
|
+
try {
|
|
5215
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5216
|
+
} catch {
|
|
5217
|
+
}
|
|
5218
|
+
const currentPath = window.location.pathname;
|
|
5219
|
+
const updated = sections.map((s) => {
|
|
5220
|
+
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
5221
|
+
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
5222
|
+
return { ...s, scheduleId };
|
|
5223
|
+
});
|
|
5224
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5225
|
+
return tracker.textContent ?? "[]";
|
|
5226
|
+
}
|
|
5227
|
+
function schedulingSectionId(insertAfter) {
|
|
5228
|
+
return `scheduling-${insertAfter}`;
|
|
5229
|
+
}
|
|
5230
|
+
var INSERT_BEFORE_MARKER = ":before:";
|
|
5231
|
+
function parseSchedulingInsertAfter(insertAfter) {
|
|
5232
|
+
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
5233
|
+
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
5234
|
+
return {
|
|
5235
|
+
anchor: insertAfter.slice(0, idx),
|
|
5236
|
+
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
5237
|
+
};
|
|
5238
|
+
}
|
|
5239
|
+
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
5240
|
+
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
5241
|
+
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
5242
|
+
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
5243
|
+
return { effectiveInsertAfter, insertBefore };
|
|
5244
|
+
}
|
|
5245
|
+
function getSchedulingMountPoint(insertAfter) {
|
|
5246
|
+
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
5247
|
+
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
5248
|
+
if (!anchorEl && anchor === "scheduling") {
|
|
5249
|
+
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
5250
|
+
anchorEl = widgets.at(-1) ?? null;
|
|
5251
|
+
}
|
|
5252
|
+
if (!anchorEl) return null;
|
|
5253
|
+
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
5254
|
+
}
|
|
5255
|
+
function schedulingMountDepth(insertAfter) {
|
|
5256
|
+
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
5257
|
+
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
5258
|
+
}
|
|
5259
|
+
function getPageSchedulingEntries(raw) {
|
|
5260
|
+
if (!raw) return [];
|
|
5261
|
+
try {
|
|
5262
|
+
const entries = JSON.parse(raw);
|
|
5263
|
+
const currentPath = window.location.pathname;
|
|
5264
|
+
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
5265
|
+
} catch {
|
|
5266
|
+
return [];
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
function isSchedulingWidgetMissing(entry) {
|
|
5270
|
+
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
5271
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
5272
|
+
}
|
|
5273
|
+
function hasMissingSchedulingWidgets(entries) {
|
|
5274
|
+
return entries.some(isSchedulingWidgetMissing);
|
|
5275
|
+
}
|
|
5276
|
+
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
5277
|
+
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
5278
|
+
mountSchedulingEntries(entries, notifyOnConnect);
|
|
5279
|
+
}
|
|
5280
|
+
function initSectionsFromContent(content, removeExisting = false) {
|
|
5281
|
+
const raw = content["__ohw_sections"];
|
|
5282
|
+
if (!raw) return;
|
|
5283
|
+
try {
|
|
5284
|
+
if (removeExisting) {
|
|
5285
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
5286
|
+
}
|
|
5287
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5288
|
+
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5289
|
+
const pageEntries = getPageSchedulingEntries(raw);
|
|
5290
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
5291
|
+
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
5292
|
+
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
5293
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
5294
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
5295
|
+
for (const entry of preExisting) {
|
|
5296
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5297
|
+
}
|
|
5298
|
+
} catch {
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
5301
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
5302
|
+
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
5303
|
+
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
5304
|
+
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
5305
|
+
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
5306
|
+
if (!mountPoint) return false;
|
|
5307
|
+
const container = document.createElement("div");
|
|
5308
|
+
container.dataset.ohwSectionContainer = "scheduling";
|
|
5309
|
+
if (insertBefore) {
|
|
5310
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
5311
|
+
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
5312
|
+
if (!beforePoint) return false;
|
|
5313
|
+
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
5314
|
+
} else {
|
|
5315
|
+
let tail = mountPoint;
|
|
5316
|
+
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
5317
|
+
tail = tail.nextElementSibling;
|
|
5318
|
+
}
|
|
5319
|
+
tail.insertAdjacentElement("afterend", container);
|
|
5320
|
+
}
|
|
5321
|
+
const root = createRoot(container);
|
|
5322
|
+
flushSync(() => {
|
|
5323
|
+
root.render(
|
|
5324
|
+
/* @__PURE__ */ jsx16(
|
|
5325
|
+
SchedulingWidget,
|
|
5326
|
+
{
|
|
5327
|
+
notifyOnConnect,
|
|
5328
|
+
initialScheduleId: scheduleId,
|
|
5329
|
+
insertAfter: effectiveInsertAfter
|
|
5330
|
+
}
|
|
5331
|
+
)
|
|
5332
|
+
);
|
|
5333
|
+
});
|
|
5334
|
+
const tracker = getSectionsTracker();
|
|
5335
|
+
let sections = [];
|
|
5336
|
+
try {
|
|
5337
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5338
|
+
} catch {
|
|
5339
|
+
}
|
|
5340
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5341
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
5342
|
+
sections.push({
|
|
5343
|
+
type: "scheduling",
|
|
5344
|
+
insertAfter: effectiveInsertAfter,
|
|
5345
|
+
pagePath: window.location.pathname,
|
|
5346
|
+
...scheduleId ? { scheduleId } : {}
|
|
5347
|
+
});
|
|
5348
|
+
tracker.textContent = JSON.stringify(sections);
|
|
5349
|
+
}
|
|
5350
|
+
return true;
|
|
5351
|
+
}
|
|
5352
|
+
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
5353
|
+
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5354
|
+
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5355
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5356
|
+
const entry = pending[i];
|
|
5357
|
+
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
5358
|
+
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5359
|
+
pending.splice(i, 1);
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
4427
5364
|
function getLinkHref(el) {
|
|
4428
5365
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
4429
5366
|
return anchor?.getAttribute("href") ?? "";
|
|
@@ -4623,7 +5560,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4623
5560
|
];
|
|
4624
5561
|
function GlowFrame({ rect, elRef }) {
|
|
4625
5562
|
const GAP = 6;
|
|
4626
|
-
return /* @__PURE__ */
|
|
5563
|
+
return /* @__PURE__ */ jsx16(
|
|
4627
5564
|
"div",
|
|
4628
5565
|
{
|
|
4629
5566
|
ref: elRef,
|
|
@@ -4676,7 +5613,7 @@ function FloatingToolbar({
|
|
|
4676
5613
|
onEditLink
|
|
4677
5614
|
}) {
|
|
4678
5615
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
4679
|
-
return /* @__PURE__ */
|
|
5616
|
+
return /* @__PURE__ */ jsxs8(
|
|
4680
5617
|
"div",
|
|
4681
5618
|
{
|
|
4682
5619
|
ref: elRef,
|
|
@@ -4701,11 +5638,11 @@ function FloatingToolbar({
|
|
|
4701
5638
|
},
|
|
4702
5639
|
onMouseDown: (e) => e.stopPropagation(),
|
|
4703
5640
|
children: [
|
|
4704
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
4705
|
-
gi > 0 && /* @__PURE__ */
|
|
5641
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs8(React5.Fragment, { children: [
|
|
5642
|
+
gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4706
5643
|
btns.map((btn) => {
|
|
4707
5644
|
const isActive = activeCommands.has(btn.cmd);
|
|
4708
|
-
return /* @__PURE__ */
|
|
5645
|
+
return /* @__PURE__ */ jsx16(
|
|
4709
5646
|
"button",
|
|
4710
5647
|
{
|
|
4711
5648
|
title: btn.title,
|
|
@@ -4731,7 +5668,7 @@ function FloatingToolbar({
|
|
|
4731
5668
|
flexShrink: 0,
|
|
4732
5669
|
padding: 6
|
|
4733
5670
|
},
|
|
4734
|
-
children: /* @__PURE__ */
|
|
5671
|
+
children: /* @__PURE__ */ jsx16(
|
|
4735
5672
|
"svg",
|
|
4736
5673
|
{
|
|
4737
5674
|
width: "16",
|
|
@@ -4751,7 +5688,7 @@ function FloatingToolbar({
|
|
|
4751
5688
|
);
|
|
4752
5689
|
})
|
|
4753
5690
|
] }, gi)),
|
|
4754
|
-
showEditLink ? /* @__PURE__ */
|
|
5691
|
+
showEditLink ? /* @__PURE__ */ jsx16(
|
|
4755
5692
|
"button",
|
|
4756
5693
|
{
|
|
4757
5694
|
type: "button",
|
|
@@ -4783,9 +5720,9 @@ function FloatingToolbar({
|
|
|
4783
5720
|
flexShrink: 0,
|
|
4784
5721
|
padding: 6
|
|
4785
5722
|
},
|
|
4786
|
-
children: /* @__PURE__ */
|
|
4787
|
-
/* @__PURE__ */
|
|
4788
|
-
/* @__PURE__ */
|
|
5723
|
+
children: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
5724
|
+
/* @__PURE__ */ jsx16("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
5725
|
+
/* @__PURE__ */ jsx16("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
4789
5726
|
] })
|
|
4790
5727
|
}
|
|
4791
5728
|
) : null
|
|
@@ -4803,7 +5740,7 @@ function StateToggle({
|
|
|
4803
5740
|
states,
|
|
4804
5741
|
onStateChange
|
|
4805
5742
|
}) {
|
|
4806
|
-
return /* @__PURE__ */
|
|
5743
|
+
return /* @__PURE__ */ jsx16(
|
|
4807
5744
|
ToggleGroup,
|
|
4808
5745
|
{
|
|
4809
5746
|
"data-ohw-state-toggle": "",
|
|
@@ -4817,18 +5754,35 @@ function StateToggle({
|
|
|
4817
5754
|
left: rect.right - 8,
|
|
4818
5755
|
transform: "translateX(-100%)"
|
|
4819
5756
|
},
|
|
4820
|
-
children: states.map((state) => /* @__PURE__ */
|
|
5757
|
+
children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4821
5758
|
}
|
|
4822
5759
|
);
|
|
4823
5760
|
}
|
|
4824
5761
|
var contentCache = /* @__PURE__ */ new Map();
|
|
5762
|
+
function resolveSubdomain(subdomainFromQuery) {
|
|
5763
|
+
if (subdomainFromQuery) return subdomainFromQuery;
|
|
5764
|
+
if (typeof window !== "undefined") {
|
|
5765
|
+
const parts = window.location.hostname.split(".");
|
|
5766
|
+
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
5767
|
+
}
|
|
5768
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
5769
|
+
if (siteUrl) {
|
|
5770
|
+
try {
|
|
5771
|
+
const host = new URL(siteUrl).hostname;
|
|
5772
|
+
const siteParts = host.split(".");
|
|
5773
|
+
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
5774
|
+
} catch {
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return "";
|
|
5778
|
+
}
|
|
4825
5779
|
function OhhwellsBridge() {
|
|
4826
5780
|
const pathname = usePathname();
|
|
4827
5781
|
const router = useRouter();
|
|
4828
5782
|
const searchParams = useSearchParams();
|
|
4829
5783
|
const isEditMode = isEditSessionActive();
|
|
4830
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
4831
|
-
|
|
5784
|
+
const [bridgeRoot, setBridgeRoot] = useState5(null);
|
|
5785
|
+
useEffect3(() => {
|
|
4832
5786
|
const figtreeFontId = "ohw-figtree-font";
|
|
4833
5787
|
if (!document.getElementById(figtreeFontId)) {
|
|
4834
5788
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4851,50 +5805,50 @@ function OhhwellsBridge() {
|
|
|
4851
5805
|
};
|
|
4852
5806
|
}, []);
|
|
4853
5807
|
const subdomainFromQuery = searchParams.get("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
|
-
})();
|
|
5808
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
4859
5809
|
const postToParent = useCallback2((data) => {
|
|
4860
5810
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4861
5811
|
window.parent.postMessage(data, "*");
|
|
4862
5812
|
}
|
|
4863
5813
|
}, []);
|
|
4864
|
-
const [fetchState, setFetchState] =
|
|
4865
|
-
const autoSaveTimers =
|
|
4866
|
-
const activeElRef =
|
|
4867
|
-
const originalContentRef =
|
|
4868
|
-
const activeStateElRef =
|
|
4869
|
-
const parentScrollRef =
|
|
4870
|
-
const toolbarElRef =
|
|
4871
|
-
const glowElRef =
|
|
4872
|
-
const hoveredImageRef =
|
|
4873
|
-
const hoveredImageHasTextOverlapRef =
|
|
4874
|
-
const
|
|
4875
|
-
const
|
|
4876
|
-
const
|
|
4877
|
-
const
|
|
5814
|
+
const [fetchState, setFetchState] = useState5("idle");
|
|
5815
|
+
const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
|
|
5816
|
+
const activeElRef = useRef3(null);
|
|
5817
|
+
const originalContentRef = useRef3(null);
|
|
5818
|
+
const activeStateElRef = useRef3(null);
|
|
5819
|
+
const parentScrollRef = useRef3(null);
|
|
5820
|
+
const toolbarElRef = useRef3(null);
|
|
5821
|
+
const glowElRef = useRef3(null);
|
|
5822
|
+
const hoveredImageRef = useRef3(null);
|
|
5823
|
+
const hoveredImageHasTextOverlapRef = useRef3(false);
|
|
5824
|
+
const hoveredGapRef = useRef3(null);
|
|
5825
|
+
const imageUnhoverTimerRef = useRef3(null);
|
|
5826
|
+
const imageShowTimerRef = useRef3(null);
|
|
5827
|
+
const editStylesRef = useRef3(null);
|
|
5828
|
+
const activateRef = useRef3(() => {
|
|
4878
5829
|
});
|
|
4879
|
-
const deactivateRef =
|
|
5830
|
+
const deactivateRef = useRef3(() => {
|
|
4880
5831
|
});
|
|
4881
|
-
const refreshActiveCommandsRef =
|
|
5832
|
+
const refreshActiveCommandsRef = useRef3(() => {
|
|
4882
5833
|
});
|
|
4883
|
-
const postToParentRef =
|
|
5834
|
+
const postToParentRef = useRef3(postToParent);
|
|
4884
5835
|
postToParentRef.current = postToParent;
|
|
4885
|
-
const
|
|
4886
|
-
const
|
|
4887
|
-
const [
|
|
4888
|
-
const [
|
|
4889
|
-
const [
|
|
4890
|
-
const [
|
|
4891
|
-
const [
|
|
4892
|
-
const [
|
|
4893
|
-
const
|
|
4894
|
-
const
|
|
4895
|
-
const
|
|
4896
|
-
const
|
|
4897
|
-
const
|
|
5836
|
+
const sectionsLoadedRef = useRef3(false);
|
|
5837
|
+
const pendingScheduleConfigRequests = useRef3([]);
|
|
5838
|
+
const [toolbarRect, setToolbarRect] = useState5(null);
|
|
5839
|
+
const [toggleState, setToggleState] = useState5(null);
|
|
5840
|
+
const [maxBadge, setMaxBadge] = useState5(null);
|
|
5841
|
+
const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
|
|
5842
|
+
const [sectionGap, setSectionGap] = useState5(null);
|
|
5843
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
|
|
5844
|
+
const [linkPopover, setLinkPopover] = useState5(null);
|
|
5845
|
+
const [sitePages, setSitePages] = useState5([]);
|
|
5846
|
+
const [sectionsByPath, setSectionsByPath] = useState5({});
|
|
5847
|
+
const sectionsPrefetchGenRef = useRef3(0);
|
|
5848
|
+
const setLinkPopoverRef = useRef3(setLinkPopover);
|
|
5849
|
+
const linkPopoverPanelRef = useRef3(null);
|
|
5850
|
+
const linkPopoverOpenRef = useRef3(false);
|
|
5851
|
+
const linkPopoverGraceUntilRef = useRef3(0);
|
|
4898
5852
|
setLinkPopoverRef.current = setLinkPopover;
|
|
4899
5853
|
const bumpLinkPopoverGrace = () => {
|
|
4900
5854
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
@@ -4918,9 +5872,9 @@ function OhhwellsBridge() {
|
|
|
4918
5872
|
);
|
|
4919
5873
|
});
|
|
4920
5874
|
}, [isEditMode, pathname]);
|
|
4921
|
-
const runSectionsPrefetchRef =
|
|
5875
|
+
const runSectionsPrefetchRef = useRef3(runSectionsPrefetch);
|
|
4922
5876
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
4923
|
-
|
|
5877
|
+
useEffect3(() => {
|
|
4924
5878
|
if (!linkPopover) return;
|
|
4925
5879
|
if (hoveredImageRef.current) {
|
|
4926
5880
|
hoveredImageRef.current = null;
|
|
@@ -4928,7 +5882,7 @@ function OhhwellsBridge() {
|
|
|
4928
5882
|
}
|
|
4929
5883
|
postToParent({ type: "ow:image-unhover" });
|
|
4930
5884
|
}, [linkPopover, postToParent]);
|
|
4931
|
-
|
|
5885
|
+
useEffect3(() => {
|
|
4932
5886
|
if (!isEditMode) return;
|
|
4933
5887
|
const useFixtures = shouldUseDevFixtures();
|
|
4934
5888
|
if (useFixtures) {
|
|
@@ -4952,14 +5906,14 @@ function OhhwellsBridge() {
|
|
|
4952
5906
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
4953
5907
|
return () => window.removeEventListener("message", onSitePages);
|
|
4954
5908
|
}, [isEditMode, postToParent]);
|
|
4955
|
-
|
|
5909
|
+
useEffect3(() => {
|
|
4956
5910
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
4957
5911
|
void loadAllSectionsManifest().then((manifest) => {
|
|
4958
5912
|
if (Object.keys(manifest).length === 0) return;
|
|
4959
5913
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
4960
5914
|
});
|
|
4961
5915
|
}, [isEditMode]);
|
|
4962
|
-
|
|
5916
|
+
useEffect3(() => {
|
|
4963
5917
|
const update = () => {
|
|
4964
5918
|
const el = activeElRef.current;
|
|
4965
5919
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -4967,6 +5921,12 @@ function OhhwellsBridge() {
|
|
|
4967
5921
|
if (!prev || !activeStateElRef.current) return prev;
|
|
4968
5922
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
4969
5923
|
});
|
|
5924
|
+
setSectionGap((prev) => {
|
|
5925
|
+
if (!prev) return prev;
|
|
5926
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
5927
|
+
if (!anchor) return prev;
|
|
5928
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
5929
|
+
});
|
|
4970
5930
|
};
|
|
4971
5931
|
const vvp = window.visualViewport;
|
|
4972
5932
|
if (!vvp) return;
|
|
@@ -4980,6 +5940,29 @@ function OhhwellsBridge() {
|
|
|
4980
5940
|
const refreshStateRules = useCallback2(() => {
|
|
4981
5941
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4982
5942
|
}, []);
|
|
5943
|
+
const processConfigRequest = useCallback2((insertAfterVal) => {
|
|
5944
|
+
const tracker = getSectionsTracker();
|
|
5945
|
+
let entries = [];
|
|
5946
|
+
try {
|
|
5947
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
5948
|
+
} catch {
|
|
5949
|
+
}
|
|
5950
|
+
const path = window.location.pathname;
|
|
5951
|
+
const entry = entries.find(
|
|
5952
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
5953
|
+
);
|
|
5954
|
+
if (entry) {
|
|
5955
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
5959
|
+
entries.push(newEntry);
|
|
5960
|
+
tracker.textContent = JSON.stringify(entries);
|
|
5961
|
+
if (isEditMode) {
|
|
5962
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5963
|
+
}
|
|
5964
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
5965
|
+
}, [isEditMode]);
|
|
4983
5966
|
const deactivate = useCallback2(() => {
|
|
4984
5967
|
const el = activeElRef.current;
|
|
4985
5968
|
if (!el) return;
|
|
@@ -5033,6 +6016,7 @@ function OhhwellsBridge() {
|
|
|
5033
6016
|
const applyContent = (content) => {
|
|
5034
6017
|
const imageLoads = [];
|
|
5035
6018
|
for (const [key, val] of Object.entries(content)) {
|
|
6019
|
+
if (key === "__ohw_sections") continue;
|
|
5036
6020
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5037
6021
|
if (el.dataset.ohwEditable === "image") {
|
|
5038
6022
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5054,6 +6038,9 @@ function OhhwellsBridge() {
|
|
|
5054
6038
|
});
|
|
5055
6039
|
applyLinkByKey(key, val);
|
|
5056
6040
|
}
|
|
6041
|
+
initSectionsFromContent(content, true);
|
|
6042
|
+
sectionsLoadedRef.current = true;
|
|
6043
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5057
6044
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5058
6045
|
}) : Promise.resolve();
|
|
5059
6046
|
};
|
|
@@ -5078,12 +6065,15 @@ function OhhwellsBridge() {
|
|
|
5078
6065
|
cancelled = true;
|
|
5079
6066
|
};
|
|
5080
6067
|
}, [subdomain, isEditMode, pathname]);
|
|
5081
|
-
|
|
6068
|
+
useEffect3(() => {
|
|
5082
6069
|
if (!subdomain || isEditMode) return;
|
|
6070
|
+
let debounceTimer = null;
|
|
5083
6071
|
const applyFromCache = () => {
|
|
5084
6072
|
const content = contentCache.get(subdomain);
|
|
5085
6073
|
if (!content) return;
|
|
6074
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
5086
6075
|
for (const [key, val] of Object.entries(content)) {
|
|
6076
|
+
if (key === "__ohw_sections") continue;
|
|
5087
6077
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5088
6078
|
if (el.dataset.ohwEditable === "image") {
|
|
5089
6079
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5100,21 +6090,28 @@ function OhhwellsBridge() {
|
|
|
5100
6090
|
applyLinkByKey(key, val);
|
|
5101
6091
|
}
|
|
5102
6092
|
};
|
|
5103
|
-
|
|
5104
|
-
|
|
6093
|
+
const scheduleApply = () => {
|
|
6094
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6095
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6096
|
+
};
|
|
6097
|
+
scheduleApply();
|
|
6098
|
+
const observer = new MutationObserver(scheduleApply);
|
|
5105
6099
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
5106
|
-
return () =>
|
|
5107
|
-
|
|
6100
|
+
return () => {
|
|
6101
|
+
observer.disconnect();
|
|
6102
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6103
|
+
};
|
|
6104
|
+
}, [subdomain, isEditMode, pathname]);
|
|
5108
6105
|
useLayoutEffect(() => {
|
|
5109
6106
|
const el = document.getElementById("ohw-loader");
|
|
5110
6107
|
if (!el) return;
|
|
5111
6108
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
5112
6109
|
el.style.display = visible ? "flex" : "none";
|
|
5113
6110
|
}, [subdomain, fetchState]);
|
|
5114
|
-
|
|
6111
|
+
useEffect3(() => {
|
|
5115
6112
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
5116
6113
|
}, [pathname, postToParent]);
|
|
5117
|
-
|
|
6114
|
+
useEffect3(() => {
|
|
5118
6115
|
if (!isEditMode) return;
|
|
5119
6116
|
const measure = () => {
|
|
5120
6117
|
const h = document.body.scrollHeight;
|
|
@@ -5138,7 +6135,7 @@ function OhhwellsBridge() {
|
|
|
5138
6135
|
window.removeEventListener("resize", handleResize);
|
|
5139
6136
|
};
|
|
5140
6137
|
}, [pathname, isEditMode, postToParent]);
|
|
5141
|
-
|
|
6138
|
+
useEffect3(() => {
|
|
5142
6139
|
if (!subdomainFromQuery || isEditMode) return;
|
|
5143
6140
|
const handleClick = (e) => {
|
|
5144
6141
|
const anchor = e.target.closest("a");
|
|
@@ -5154,7 +6151,7 @@ function OhhwellsBridge() {
|
|
|
5154
6151
|
document.addEventListener("click", handleClick, true);
|
|
5155
6152
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5156
6153
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5157
|
-
|
|
6154
|
+
useEffect3(() => {
|
|
5158
6155
|
if (!isEditMode) {
|
|
5159
6156
|
editStylesRef.current?.base.remove();
|
|
5160
6157
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5455,6 +6452,15 @@ function OhhwellsBridge() {
|
|
|
5455
6452
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
5456
6453
|
return;
|
|
5457
6454
|
}
|
|
6455
|
+
if (hoveredGapRef.current) {
|
|
6456
|
+
if (hoveredImageRef.current) {
|
|
6457
|
+
hoveredImageRef.current = null;
|
|
6458
|
+
resumeAnimTracks();
|
|
6459
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6460
|
+
}
|
|
6461
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6462
|
+
return;
|
|
6463
|
+
}
|
|
5458
6464
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5459
6465
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
5460
6466
|
hoveredImageRef.current = imgEl;
|
|
@@ -5520,8 +6526,30 @@ function OhhwellsBridge() {
|
|
|
5520
6526
|
}
|
|
5521
6527
|
}
|
|
5522
6528
|
};
|
|
6529
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6530
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6531
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6532
|
+
const ZONE = 20;
|
|
6533
|
+
for (let i = 0; i < sections.length; i++) {
|
|
6534
|
+
const a = sections[i];
|
|
6535
|
+
const b = sections[i + 1] ?? null;
|
|
6536
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6537
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6538
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6539
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6540
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6541
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6542
|
+
return;
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
if (hoveredGapRef.current) {
|
|
6546
|
+
hoveredGapRef.current = null;
|
|
6547
|
+
setSectionGap(null);
|
|
6548
|
+
}
|
|
6549
|
+
};
|
|
5523
6550
|
const handleMouseMove = (e) => {
|
|
5524
6551
|
const { clientX, clientY } = e;
|
|
6552
|
+
probeSectionGapAt(clientX, clientY);
|
|
5525
6553
|
probeImageAt(clientX, clientY);
|
|
5526
6554
|
probeHoverCardsAt(clientX, clientY);
|
|
5527
6555
|
};
|
|
@@ -5529,6 +6557,7 @@ function OhhwellsBridge() {
|
|
|
5529
6557
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
5530
6558
|
const { clientX, clientY } = e.data;
|
|
5531
6559
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6560
|
+
probeSectionGapAt(clientX, clientY);
|
|
5532
6561
|
probeImageAt(clientX, clientY);
|
|
5533
6562
|
probeHoverCardsAt(clientX, clientY);
|
|
5534
6563
|
};
|
|
@@ -5686,7 +6715,12 @@ function OhhwellsBridge() {
|
|
|
5686
6715
|
if (e.data?.type !== "ow:hydrate") return;
|
|
5687
6716
|
const content = e.data.content;
|
|
5688
6717
|
if (!content) return;
|
|
6718
|
+
let sectionsJson = null;
|
|
5689
6719
|
for (const [key, val] of Object.entries(content)) {
|
|
6720
|
+
if (key === "__ohw_sections") {
|
|
6721
|
+
sectionsJson = val;
|
|
6722
|
+
continue;
|
|
6723
|
+
}
|
|
5690
6724
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5691
6725
|
if (el.dataset.ohwEditable === "image") {
|
|
5692
6726
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5701,6 +6735,11 @@ function OhhwellsBridge() {
|
|
|
5701
6735
|
});
|
|
5702
6736
|
applyLinkByKey(key, val);
|
|
5703
6737
|
}
|
|
6738
|
+
if (sectionsJson) {
|
|
6739
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
6740
|
+
sectionsLoadedRef.current = true;
|
|
6741
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
6742
|
+
}
|
|
5704
6743
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5705
6744
|
};
|
|
5706
6745
|
window.addEventListener("message", handleHydrate);
|
|
@@ -5753,7 +6792,63 @@ function OhhwellsBridge() {
|
|
|
5753
6792
|
};
|
|
5754
6793
|
const handleSave = (e) => {
|
|
5755
6794
|
if (e.data?.type !== "ow:save") return;
|
|
5756
|
-
|
|
6795
|
+
const nodes = collectEditableNodes();
|
|
6796
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
6797
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
6798
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
6799
|
+
};
|
|
6800
|
+
const handleInsertSection = (e) => {
|
|
6801
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
6802
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
6803
|
+
if (widgetType !== "scheduling") return;
|
|
6804
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
6805
|
+
if (inserted) {
|
|
6806
|
+
const tracker = getSectionsTracker();
|
|
6807
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
6808
|
+
const h = document.documentElement.scrollHeight;
|
|
6809
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
6810
|
+
}
|
|
6811
|
+
};
|
|
6812
|
+
const handleSwitchSchedule = (e) => {
|
|
6813
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
6814
|
+
const scheduleId = e.data.scheduleId;
|
|
6815
|
+
const insertAfter = e.data.insertAfter;
|
|
6816
|
+
if (!scheduleId || !insertAfter) return;
|
|
6817
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6818
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6819
|
+
};
|
|
6820
|
+
const handleScheduleLinked = (e) => {
|
|
6821
|
+
if (e.data?.type !== "ow:schedule-linked") return;
|
|
6822
|
+
const scheduleId = e.data.scheduleId;
|
|
6823
|
+
const insertAfter = e.data.insertAfter;
|
|
6824
|
+
if (!scheduleId || !insertAfter) return;
|
|
6825
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6826
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6827
|
+
};
|
|
6828
|
+
const handleClearSchedulingWidget = (e) => {
|
|
6829
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
6830
|
+
const insertAfter = e.data.insertAfter;
|
|
6831
|
+
if (!insertAfter) return;
|
|
6832
|
+
const text = updateSectionScheduleId(insertAfter, null);
|
|
6833
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6834
|
+
};
|
|
6835
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
6836
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
6837
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
6838
|
+
el.remove();
|
|
6839
|
+
});
|
|
6840
|
+
const tracker = getSectionsTracker();
|
|
6841
|
+
let sections = [];
|
|
6842
|
+
try {
|
|
6843
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
6844
|
+
} catch {
|
|
6845
|
+
}
|
|
6846
|
+
const currentPath = window.location.pathname;
|
|
6847
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
6848
|
+
tracker.textContent = JSON.stringify(updated);
|
|
6849
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6850
|
+
const h = document.documentElement.scrollHeight;
|
|
6851
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5757
6852
|
};
|
|
5758
6853
|
const handleSelectionChange = () => {
|
|
5759
6854
|
if (!activeElRef.current) return;
|
|
@@ -5858,6 +6953,11 @@ function OhhwellsBridge() {
|
|
|
5858
6953
|
deactivateRef.current();
|
|
5859
6954
|
};
|
|
5860
6955
|
window.addEventListener("message", handleSave);
|
|
6956
|
+
window.addEventListener("message", handleInsertSection);
|
|
6957
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
6958
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
6959
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
6960
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
5861
6961
|
window.addEventListener("message", handleImageUrl);
|
|
5862
6962
|
window.addEventListener("message", handleAnimLock);
|
|
5863
6963
|
window.addEventListener("message", handleCanvasHeight);
|
|
@@ -5892,6 +6992,11 @@ function OhhwellsBridge() {
|
|
|
5892
6992
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
5893
6993
|
window.removeEventListener("scroll", handleScroll, true);
|
|
5894
6994
|
window.removeEventListener("message", handleSave);
|
|
6995
|
+
window.removeEventListener("message", handleInsertSection);
|
|
6996
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
6997
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
6998
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
6999
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
5895
7000
|
window.removeEventListener("message", handleImageUrl);
|
|
5896
7001
|
window.removeEventListener("message", handleAnimLock);
|
|
5897
7002
|
window.removeEventListener("message", handleCanvasHeight);
|
|
@@ -5906,7 +7011,23 @@ function OhhwellsBridge() {
|
|
|
5906
7011
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5907
7012
|
};
|
|
5908
7013
|
}, [isEditMode, refreshStateRules]);
|
|
5909
|
-
|
|
7014
|
+
useEffect3(() => {
|
|
7015
|
+
const handler = (e) => {
|
|
7016
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7017
|
+
const insertAfterVal = e.data.insertAfter;
|
|
7018
|
+
if (!insertAfterVal) return;
|
|
7019
|
+
if (!sectionsLoadedRef.current) {
|
|
7020
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
7021
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
7022
|
+
}
|
|
7023
|
+
return;
|
|
7024
|
+
}
|
|
7025
|
+
processConfigRequest(insertAfterVal);
|
|
7026
|
+
};
|
|
7027
|
+
window.addEventListener("message", handler);
|
|
7028
|
+
return () => window.removeEventListener("message", handler);
|
|
7029
|
+
}, [processConfigRequest]);
|
|
7030
|
+
useEffect3(() => {
|
|
5910
7031
|
if (!isEditMode) return;
|
|
5911
7032
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5912
7033
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5935,7 +7056,7 @@ function OhhwellsBridge() {
|
|
|
5935
7056
|
clearTimeout(timer);
|
|
5936
7057
|
};
|
|
5937
7058
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5938
|
-
|
|
7059
|
+
useEffect3(() => {
|
|
5939
7060
|
scrollToHashSectionWhenReady();
|
|
5940
7061
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
5941
7062
|
window.addEventListener("hashchange", onHashChange);
|
|
@@ -5985,10 +7106,10 @@ function OhhwellsBridge() {
|
|
|
5985
7106
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
5986
7107
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
5987
7108
|
return bridgeRoot ? createPortal(
|
|
5988
|
-
/* @__PURE__ */
|
|
5989
|
-
toolbarRect && /* @__PURE__ */
|
|
5990
|
-
/* @__PURE__ */
|
|
5991
|
-
/* @__PURE__ */
|
|
7109
|
+
/* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
7110
|
+
toolbarRect && /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
7111
|
+
/* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
7112
|
+
/* @__PURE__ */ jsx16(
|
|
5992
7113
|
FloatingToolbar,
|
|
5993
7114
|
{
|
|
5994
7115
|
rect: toolbarRect,
|
|
@@ -6001,7 +7122,7 @@ function OhhwellsBridge() {
|
|
|
6001
7122
|
}
|
|
6002
7123
|
)
|
|
6003
7124
|
] }),
|
|
6004
|
-
maxBadge && /* @__PURE__ */
|
|
7125
|
+
maxBadge && /* @__PURE__ */ jsxs8(
|
|
6005
7126
|
"div",
|
|
6006
7127
|
{
|
|
6007
7128
|
"data-ohw-max-badge": "",
|
|
@@ -6027,7 +7148,7 @@ function OhhwellsBridge() {
|
|
|
6027
7148
|
]
|
|
6028
7149
|
}
|
|
6029
7150
|
),
|
|
6030
|
-
toggleState && /* @__PURE__ */
|
|
7151
|
+
toggleState && /* @__PURE__ */ jsx16(
|
|
6031
7152
|
StateToggle,
|
|
6032
7153
|
{
|
|
6033
7154
|
rect: toggleState.rect,
|
|
@@ -6036,7 +7157,32 @@ function OhhwellsBridge() {
|
|
|
6036
7157
|
onStateChange: handleStateChange
|
|
6037
7158
|
}
|
|
6038
7159
|
),
|
|
6039
|
-
|
|
7160
|
+
sectionGap && /* @__PURE__ */ jsxs8(
|
|
7161
|
+
"div",
|
|
7162
|
+
{
|
|
7163
|
+
"data-ohw-section-insert-line": "",
|
|
7164
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7165
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7166
|
+
children: [
|
|
7167
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7168
|
+
/* @__PURE__ */ jsx16(
|
|
7169
|
+
Badge,
|
|
7170
|
+
{
|
|
7171
|
+
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",
|
|
7172
|
+
onClick: () => {
|
|
7173
|
+
window.parent.postMessage(
|
|
7174
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7175
|
+
"*"
|
|
7176
|
+
);
|
|
7177
|
+
},
|
|
7178
|
+
children: "Add Section"
|
|
7179
|
+
}
|
|
7180
|
+
),
|
|
7181
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7182
|
+
]
|
|
7183
|
+
}
|
|
7184
|
+
),
|
|
7185
|
+
linkPopover ? /* @__PURE__ */ jsx16(
|
|
6040
7186
|
LinkPopover,
|
|
6041
7187
|
{
|
|
6042
7188
|
rect: linkPopover.rect,
|
|
@@ -6060,6 +7206,7 @@ export {
|
|
|
6060
7206
|
LinkEditorPanel,
|
|
6061
7207
|
LinkPopover,
|
|
6062
7208
|
OhhwellsBridge,
|
|
7209
|
+
SchedulingWidget,
|
|
6063
7210
|
Toggle,
|
|
6064
7211
|
ToggleGroup,
|
|
6065
7212
|
ToggleGroupItem,
|