@ohhwells/bridge 0.1.29 → 0.1.31-next.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -304
- package/dist/index.cjs +1864 -328
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.js +1840 -305
- package/dist/index.js.map +1 -1
- package/dist/styles.css +505 -14
- 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 React6, { 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,
|
|
@@ -3689,26 +4441,135 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
|
|
|
3689
4441
|
tick();
|
|
3690
4442
|
}
|
|
3691
4443
|
|
|
3692
|
-
// src/ui/
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
return {
|
|
4444
|
+
// src/ui/dialog.tsx
|
|
4445
|
+
import * as React3 from "react";
|
|
4446
|
+
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
4447
|
+
|
|
4448
|
+
// src/ui/icons.tsx
|
|
4449
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4450
|
+
function IconX({ className, ...props }) {
|
|
4451
|
+
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: [
|
|
4452
|
+
/* @__PURE__ */ jsx5("path", { d: "M18 6 6 18" }),
|
|
4453
|
+
/* @__PURE__ */ jsx5("path", { d: "m6 6 12 12" })
|
|
4454
|
+
] });
|
|
4455
|
+
}
|
|
4456
|
+
function IconChevronDown({ className, ...props }) {
|
|
4457
|
+
return /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx5("path", { d: "m6 9 6 6 6-6" }) });
|
|
4458
|
+
}
|
|
4459
|
+
function IconFile({ className, ...props }) {
|
|
4460
|
+
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: [
|
|
4461
|
+
/* @__PURE__ */ jsx5("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4462
|
+
/* @__PURE__ */ jsx5("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4463
|
+
] });
|
|
4464
|
+
}
|
|
4465
|
+
function IconInfo({ className, ...props }) {
|
|
4466
|
+
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: [
|
|
4467
|
+
/* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4468
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 16v-4" }),
|
|
4469
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 8h.01" })
|
|
4470
|
+
] });
|
|
4471
|
+
}
|
|
4472
|
+
function IconArrowRight({ className, ...props }) {
|
|
4473
|
+
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: [
|
|
4474
|
+
/* @__PURE__ */ jsx5("path", { d: "M5 12h14" }),
|
|
4475
|
+
/* @__PURE__ */ jsx5("path", { d: "m12 5 7 7-7 7" })
|
|
4476
|
+
] });
|
|
4477
|
+
}
|
|
4478
|
+
function IconSection({ className, ...props }) {
|
|
4479
|
+
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: [
|
|
4480
|
+
/* @__PURE__ */ jsx5("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4481
|
+
/* @__PURE__ */ jsx5("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4482
|
+
/* @__PURE__ */ jsx5("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4483
|
+
] });
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
// src/ui/dialog.tsx
|
|
4487
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4488
|
+
function Dialog2({ ...props }) {
|
|
4489
|
+
return /* @__PURE__ */ jsx6(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
3706
4490
|
}
|
|
4491
|
+
function DialogPortal({ ...props }) {
|
|
4492
|
+
return /* @__PURE__ */ jsx6(DialogPrimitive.Portal, { ...props });
|
|
4493
|
+
}
|
|
4494
|
+
function DialogOverlay({
|
|
4495
|
+
className,
|
|
4496
|
+
...props
|
|
4497
|
+
}) {
|
|
4498
|
+
return /* @__PURE__ */ jsx6(
|
|
4499
|
+
DialogPrimitive.Overlay,
|
|
4500
|
+
{
|
|
4501
|
+
"data-slot": "dialog-overlay",
|
|
4502
|
+
"data-ohw-link-modal-root": "",
|
|
4503
|
+
className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
|
|
4504
|
+
...props
|
|
4505
|
+
}
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
var DialogContent = React3.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
4509
|
+
const positionMode = container ? "absolute" : "fixed";
|
|
4510
|
+
return /* @__PURE__ */ jsxs4(DialogPortal, { container: container ?? void 0, children: [
|
|
4511
|
+
/* @__PURE__ */ jsx6(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
4512
|
+
/* @__PURE__ */ jsxs4(
|
|
4513
|
+
DialogPrimitive.Content,
|
|
4514
|
+
{
|
|
4515
|
+
ref,
|
|
4516
|
+
"data-slot": "dialog-content",
|
|
4517
|
+
className: cn(
|
|
4518
|
+
positionMode,
|
|
4519
|
+
"left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[483px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
|
|
4520
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
4521
|
+
container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
|
|
4522
|
+
className
|
|
4523
|
+
),
|
|
4524
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4525
|
+
...props,
|
|
4526
|
+
children: [
|
|
4527
|
+
children,
|
|
4528
|
+
showCloseButton ? /* @__PURE__ */ jsx6(
|
|
4529
|
+
DialogPrimitive.Close,
|
|
4530
|
+
{
|
|
4531
|
+
type: "button",
|
|
4532
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4533
|
+
"aria-label": "Close",
|
|
4534
|
+
children: /* @__PURE__ */ jsx6(IconX, { "aria-hidden": true })
|
|
4535
|
+
}
|
|
4536
|
+
) : null
|
|
4537
|
+
]
|
|
4538
|
+
}
|
|
4539
|
+
)
|
|
4540
|
+
] });
|
|
4541
|
+
});
|
|
4542
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
4543
|
+
function DialogHeader({ className, ...props }) {
|
|
4544
|
+
return /* @__PURE__ */ jsx6("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
4545
|
+
}
|
|
4546
|
+
function DialogFooter({ className, ...props }) {
|
|
4547
|
+
return /* @__PURE__ */ jsx6("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
|
|
4548
|
+
}
|
|
4549
|
+
var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
4550
|
+
DialogPrimitive.Title,
|
|
4551
|
+
{
|
|
4552
|
+
ref,
|
|
4553
|
+
className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
|
|
4554
|
+
...props
|
|
4555
|
+
}
|
|
4556
|
+
));
|
|
4557
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
4558
|
+
var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
4559
|
+
DialogPrimitive.Description,
|
|
4560
|
+
{
|
|
4561
|
+
ref,
|
|
4562
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
4563
|
+
...props
|
|
4564
|
+
}
|
|
4565
|
+
));
|
|
4566
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
4567
|
+
var DialogClose = DialogPrimitive.Close;
|
|
3707
4568
|
|
|
3708
4569
|
// src/ui/button.tsx
|
|
3709
|
-
import * as
|
|
4570
|
+
import * as React4 from "react";
|
|
3710
4571
|
import { Slot } from "radix-ui";
|
|
3711
|
-
import { jsx as
|
|
4572
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
3712
4573
|
var buttonVariants = cva(
|
|
3713
4574
|
"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
4575
|
{
|
|
@@ -3729,10 +4590,10 @@ var buttonVariants = cva(
|
|
|
3729
4590
|
}
|
|
3730
4591
|
}
|
|
3731
4592
|
);
|
|
3732
|
-
var Button =
|
|
4593
|
+
var Button = React4.forwardRef(
|
|
3733
4594
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
3734
4595
|
const Comp = asChild ? Slot.Root : "button";
|
|
3735
|
-
return /* @__PURE__ */
|
|
4596
|
+
return /* @__PURE__ */ jsx7(
|
|
3736
4597
|
Comp,
|
|
3737
4598
|
{
|
|
3738
4599
|
ref,
|
|
@@ -3745,76 +4606,38 @@ var Button = React2.forwardRef(
|
|
|
3745
4606
|
);
|
|
3746
4607
|
Button.displayName = "Button";
|
|
3747
4608
|
|
|
3748
|
-
// src/ui/icons.tsx
|
|
3749
|
-
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
3750
|
-
function IconX({ className, ...props }) {
|
|
3751
|
-
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3752
|
-
/* @__PURE__ */ jsx4("path", { d: "M18 6 6 18" }),
|
|
3753
|
-
/* @__PURE__ */ jsx4("path", { d: "m6 6 12 12" })
|
|
3754
|
-
] });
|
|
3755
|
-
}
|
|
3756
|
-
function IconChevronDown({ className, ...props }) {
|
|
3757
|
-
return /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" }) });
|
|
3758
|
-
}
|
|
3759
|
-
function IconFile({ className, ...props }) {
|
|
3760
|
-
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3761
|
-
/* @__PURE__ */ jsx4("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
3762
|
-
/* @__PURE__ */ jsx4("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
3763
|
-
] });
|
|
3764
|
-
}
|
|
3765
|
-
function IconInfo({ className, ...props }) {
|
|
3766
|
-
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3767
|
-
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3768
|
-
/* @__PURE__ */ jsx4("path", { d: "M12 16v-4" }),
|
|
3769
|
-
/* @__PURE__ */ jsx4("path", { d: "M12 8h.01" })
|
|
3770
|
-
] });
|
|
3771
|
-
}
|
|
3772
|
-
function IconArrowRight({ className, ...props }) {
|
|
3773
|
-
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3774
|
-
/* @__PURE__ */ jsx4("path", { d: "M5 12h14" }),
|
|
3775
|
-
/* @__PURE__ */ jsx4("path", { d: "m12 5 7 7-7 7" })
|
|
3776
|
-
] });
|
|
3777
|
-
}
|
|
3778
|
-
function IconSection({ className, ...props }) {
|
|
3779
|
-
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3780
|
-
/* @__PURE__ */ jsx4("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
3781
|
-
/* @__PURE__ */ jsx4("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
3782
|
-
/* @__PURE__ */ jsx4("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
3783
|
-
] });
|
|
3784
|
-
}
|
|
3785
|
-
|
|
3786
4609
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
3787
|
-
import { jsx as
|
|
4610
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3788
4611
|
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
3789
|
-
return /* @__PURE__ */
|
|
3790
|
-
/* @__PURE__ */
|
|
3791
|
-
/* @__PURE__ */
|
|
3792
|
-
/* @__PURE__ */
|
|
3793
|
-
/* @__PURE__ */
|
|
3794
|
-
/* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-2", children: [
|
|
4613
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
4614
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
|
|
4615
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
4616
|
+
/* @__PURE__ */ jsx8(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4617
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
3795
4618
|
] }),
|
|
3796
|
-
/* @__PURE__ */
|
|
3797
|
-
/* @__PURE__ */
|
|
3798
|
-
/* @__PURE__ */
|
|
3799
|
-
/* @__PURE__ */
|
|
4619
|
+
/* @__PURE__ */ jsx8(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
4620
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
4621
|
+
/* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4622
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
3800
4623
|
] })
|
|
3801
4624
|
] })
|
|
3802
4625
|
] });
|
|
3803
4626
|
}
|
|
3804
4627
|
|
|
3805
4628
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
3806
|
-
import { jsx as
|
|
4629
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3807
4630
|
function SectionTreeItem({ section, onSelect, selected }) {
|
|
3808
4631
|
const interactive = Boolean(onSelect);
|
|
3809
|
-
return /* @__PURE__ */
|
|
3810
|
-
/* @__PURE__ */
|
|
4632
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
4633
|
+
/* @__PURE__ */ jsx9(
|
|
3811
4634
|
"div",
|
|
3812
4635
|
{
|
|
3813
4636
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
3814
4637
|
"aria-hidden": true
|
|
3815
4638
|
}
|
|
3816
4639
|
),
|
|
3817
|
-
/* @__PURE__ */
|
|
4640
|
+
/* @__PURE__ */ jsxs6(
|
|
3818
4641
|
"div",
|
|
3819
4642
|
{
|
|
3820
4643
|
role: interactive ? "button" : void 0,
|
|
@@ -3832,8 +4655,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3832
4655
|
interactive && selected && "border-primary"
|
|
3833
4656
|
),
|
|
3834
4657
|
children: [
|
|
3835
|
-
/* @__PURE__ */
|
|
3836
|
-
/* @__PURE__ */
|
|
4658
|
+
/* @__PURE__ */ jsx9(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4659
|
+
/* @__PURE__ */ jsx9("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
3837
4660
|
]
|
|
3838
4661
|
}
|
|
3839
4662
|
)
|
|
@@ -3841,23 +4664,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3841
4664
|
}
|
|
3842
4665
|
function SectionPickerList({ sections, onSelect }) {
|
|
3843
4666
|
if (sections.length === 0) {
|
|
3844
|
-
return /* @__PURE__ */
|
|
4667
|
+
return /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
3845
4668
|
}
|
|
3846
|
-
return /* @__PURE__ */
|
|
3847
|
-
/* @__PURE__ */
|
|
3848
|
-
sections.map((section) => /* @__PURE__ */
|
|
4669
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
|
|
4670
|
+
/* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
4671
|
+
sections.map((section) => /* @__PURE__ */ jsx9(SectionTreeItem, { section, onSelect }, section.id))
|
|
3849
4672
|
] });
|
|
3850
4673
|
}
|
|
3851
4674
|
|
|
3852
4675
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3853
|
-
import { useId, useRef, useState } from "react";
|
|
4676
|
+
import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
3854
4677
|
|
|
3855
4678
|
// src/ui/input.tsx
|
|
3856
|
-
import * as
|
|
3857
|
-
import { jsx as
|
|
3858
|
-
var Input =
|
|
4679
|
+
import * as React5 from "react";
|
|
4680
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4681
|
+
var Input = React5.forwardRef(
|
|
3859
4682
|
({ className, type, ...props }, ref) => {
|
|
3860
|
-
return /* @__PURE__ */
|
|
4683
|
+
return /* @__PURE__ */ jsx10(
|
|
3861
4684
|
"input",
|
|
3862
4685
|
{
|
|
3863
4686
|
type,
|
|
@@ -3876,9 +4699,9 @@ Input.displayName = "Input";
|
|
|
3876
4699
|
|
|
3877
4700
|
// src/ui/label.tsx
|
|
3878
4701
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
3879
|
-
import { jsx as
|
|
4702
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
3880
4703
|
function Label({ className, ...props }) {
|
|
3881
|
-
return /* @__PURE__ */
|
|
4704
|
+
return /* @__PURE__ */ jsx11(
|
|
3882
4705
|
LabelPrimitive.Root,
|
|
3883
4706
|
{
|
|
3884
4707
|
"data-slot": "label",
|
|
@@ -3888,48 +4711,18 @@ function Label({ className, ...props }) {
|
|
|
3888
4711
|
);
|
|
3889
4712
|
}
|
|
3890
4713
|
|
|
3891
|
-
// src/ui/
|
|
3892
|
-
import {
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
}
|
|
3897
|
-
function PopoverTrigger({ ...props }) {
|
|
3898
|
-
return /* @__PURE__ */ jsx9(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
3899
|
-
}
|
|
3900
|
-
function PopoverContent({
|
|
3901
|
-
className,
|
|
3902
|
-
align = "start",
|
|
3903
|
-
sideOffset = 6,
|
|
3904
|
-
...props
|
|
3905
|
-
}) {
|
|
3906
|
-
return /* @__PURE__ */ jsx9(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx9(
|
|
3907
|
-
PopoverPrimitive.Content,
|
|
3908
|
-
{
|
|
3909
|
-
"data-slot": "popover-content",
|
|
3910
|
-
align,
|
|
3911
|
-
sideOffset,
|
|
3912
|
-
className: cn(
|
|
3913
|
-
"z-[2147483647] w-[var(--radix-popover-trigger-width)] overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg outline-none",
|
|
3914
|
-
className
|
|
3915
|
-
),
|
|
3916
|
-
...props
|
|
3917
|
-
}
|
|
3918
|
-
) });
|
|
3919
|
-
}
|
|
3920
|
-
|
|
3921
|
-
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3922
|
-
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3923
|
-
function FieldChevron({ onClick }) {
|
|
3924
|
-
return /* @__PURE__ */ jsx10(
|
|
3925
|
-
"button",
|
|
4714
|
+
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4715
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4716
|
+
function FieldChevron({ onClick }) {
|
|
4717
|
+
return /* @__PURE__ */ jsx12(
|
|
4718
|
+
"button",
|
|
3926
4719
|
{
|
|
3927
4720
|
type: "button",
|
|
3928
4721
|
className: "flex shrink-0 items-center justify-end border-0 bg-transparent p-0 pl-4 text-muted-foreground outline-none",
|
|
3929
4722
|
onClick,
|
|
3930
4723
|
"aria-label": "Open page list",
|
|
3931
4724
|
tabIndex: -1,
|
|
3932
|
-
children: /* @__PURE__ */
|
|
4725
|
+
children: /* @__PURE__ */ jsx12(IconChevronDown, {})
|
|
3933
4726
|
}
|
|
3934
4727
|
);
|
|
3935
4728
|
}
|
|
@@ -3945,9 +4738,9 @@ function UrlOrPageInput({
|
|
|
3945
4738
|
readOnly = false,
|
|
3946
4739
|
urlError
|
|
3947
4740
|
}) {
|
|
3948
|
-
const inputId =
|
|
3949
|
-
const inputRef =
|
|
3950
|
-
const [isFocused, setIsFocused] =
|
|
4741
|
+
const inputId = useId2();
|
|
4742
|
+
const inputRef = useRef2(null);
|
|
4743
|
+
const [isFocused, setIsFocused] = useState3(false);
|
|
3951
4744
|
const openDropdown = () => {
|
|
3952
4745
|
if (readOnly || filteredPages.length === 0) return;
|
|
3953
4746
|
onDropdownOpenChange(true);
|
|
@@ -3968,12 +4761,12 @@ function UrlOrPageInput({
|
|
|
3968
4761
|
"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
4762
|
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
4763
|
);
|
|
3971
|
-
return /* @__PURE__ */
|
|
3972
|
-
/* @__PURE__ */
|
|
3973
|
-
/* @__PURE__ */
|
|
3974
|
-
/* @__PURE__ */
|
|
3975
|
-
selectedPage ? /* @__PURE__ */
|
|
3976
|
-
readOnly ? /* @__PURE__ */
|
|
4764
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4765
|
+
/* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4766
|
+
/* @__PURE__ */ jsxs7("div", { className: "relative w-full", children: [
|
|
4767
|
+
/* @__PURE__ */ jsxs7("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
4768
|
+
selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4769
|
+
readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
|
|
3977
4770
|
Input,
|
|
3978
4771
|
{
|
|
3979
4772
|
ref: inputRef,
|
|
@@ -3992,7 +4785,7 @@ function UrlOrPageInput({
|
|
|
3992
4785
|
)
|
|
3993
4786
|
}
|
|
3994
4787
|
),
|
|
3995
|
-
selectedPage && !readOnly ? /* @__PURE__ */
|
|
4788
|
+
selectedPage && !readOnly ? /* @__PURE__ */ jsx12(
|
|
3996
4789
|
"button",
|
|
3997
4790
|
{
|
|
3998
4791
|
type: "button",
|
|
@@ -4000,31 +4793,39 @@ function UrlOrPageInput({
|
|
|
4000
4793
|
onMouseDown: clearSelection,
|
|
4001
4794
|
"aria-label": "Clear selected page",
|
|
4002
4795
|
tabIndex: -1,
|
|
4003
|
-
children: /* @__PURE__ */
|
|
4796
|
+
children: /* @__PURE__ */ jsx12(IconX, { "aria-hidden": true })
|
|
4004
4797
|
}
|
|
4005
4798
|
) : null,
|
|
4006
|
-
!readOnly ? /* @__PURE__ */
|
|
4007
|
-
] })
|
|
4008
|
-
|
|
4009
|
-
"
|
|
4799
|
+
!readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4800
|
+
] }),
|
|
4801
|
+
dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx12(
|
|
4802
|
+
"div",
|
|
4010
4803
|
{
|
|
4011
|
-
|
|
4012
|
-
className: "
|
|
4013
|
-
|
|
4014
|
-
children:
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4804
|
+
"data-ohw-link-page-dropdown": "",
|
|
4805
|
+
className: "absolute left-0 right-0 h-20 top-[calc(100%+4px)] z-10 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
|
|
4806
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4807
|
+
children: filteredPages.map((page) => /* @__PURE__ */ jsxs7(
|
|
4808
|
+
"button",
|
|
4809
|
+
{
|
|
4810
|
+
type: "button",
|
|
4811
|
+
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",
|
|
4812
|
+
onClick: () => onPageSelect(page),
|
|
4813
|
+
children: [
|
|
4814
|
+
/* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4815
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
|
|
4816
|
+
]
|
|
4817
|
+
},
|
|
4818
|
+
page.path
|
|
4819
|
+
))
|
|
4820
|
+
}
|
|
4821
|
+
) : null
|
|
4021
4822
|
] }),
|
|
4022
|
-
urlError ? /* @__PURE__ */
|
|
4823
|
+
urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4023
4824
|
] });
|
|
4024
4825
|
}
|
|
4025
4826
|
|
|
4026
4827
|
// src/ui/link-modal/useLinkModalState.ts
|
|
4027
|
-
import { useCallback, useEffect, useMemo, useState as
|
|
4828
|
+
import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
|
|
4028
4829
|
function useLinkModalState({
|
|
4029
4830
|
open,
|
|
4030
4831
|
mode,
|
|
@@ -4036,16 +4837,16 @@ function useLinkModalState({
|
|
|
4036
4837
|
onClose,
|
|
4037
4838
|
onSubmit
|
|
4038
4839
|
}) {
|
|
4039
|
-
const availablePages =
|
|
4840
|
+
const availablePages = useMemo2(
|
|
4040
4841
|
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4041
4842
|
[mode, pages, existingTargets]
|
|
4042
4843
|
);
|
|
4043
|
-
const [searchValue, setSearchValue] =
|
|
4044
|
-
const [selectedPage, setSelectedPage] =
|
|
4045
|
-
const [selectedSection, setSelectedSection] =
|
|
4046
|
-
const [step, setStep] =
|
|
4047
|
-
const [dropdownOpen, setDropdownOpen] =
|
|
4048
|
-
const [urlError, setUrlError] =
|
|
4844
|
+
const [searchValue, setSearchValue] = useState4("");
|
|
4845
|
+
const [selectedPage, setSelectedPage] = useState4(null);
|
|
4846
|
+
const [selectedSection, setSelectedSection] = useState4(null);
|
|
4847
|
+
const [step, setStep] = useState4("input");
|
|
4848
|
+
const [dropdownOpen, setDropdownOpen] = useState4(false);
|
|
4849
|
+
const [urlError, setUrlError] = useState4("");
|
|
4049
4850
|
const reset = useCallback(() => {
|
|
4050
4851
|
setSearchValue("");
|
|
4051
4852
|
setSelectedPage(null);
|
|
@@ -4054,7 +4855,7 @@ function useLinkModalState({
|
|
|
4054
4855
|
setDropdownOpen(false);
|
|
4055
4856
|
setUrlError("");
|
|
4056
4857
|
}, []);
|
|
4057
|
-
|
|
4858
|
+
useEffect2(() => {
|
|
4058
4859
|
if (!open) return;
|
|
4059
4860
|
if (mode === "edit" && initialTarget) {
|
|
4060
4861
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -4070,11 +4871,11 @@ function useLinkModalState({
|
|
|
4070
4871
|
setDropdownOpen(false);
|
|
4071
4872
|
setUrlError("");
|
|
4072
4873
|
}, [open, mode, initialTarget, pages, sectionsByPath, reset]);
|
|
4073
|
-
const filteredPages =
|
|
4874
|
+
const filteredPages = useMemo2(() => {
|
|
4074
4875
|
if (!searchValue.trim()) return availablePages;
|
|
4075
4876
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4076
4877
|
}, [availablePages, searchValue]);
|
|
4077
|
-
const activeSections =
|
|
4878
|
+
const activeSections = useMemo2(() => {
|
|
4078
4879
|
if (!selectedPage) return [];
|
|
4079
4880
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
4080
4881
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -4120,7 +4921,7 @@ function useLinkModalState({
|
|
|
4120
4921
|
reset();
|
|
4121
4922
|
onClose();
|
|
4122
4923
|
};
|
|
4123
|
-
const isValid =
|
|
4924
|
+
const isValid = useMemo2(() => {
|
|
4124
4925
|
if (urlError) return false;
|
|
4125
4926
|
if (showBreadcrumb) return true;
|
|
4126
4927
|
if (selectedPage) return true;
|
|
@@ -4187,7 +4988,7 @@ function useLinkModalState({
|
|
|
4187
4988
|
}
|
|
4188
4989
|
|
|
4189
4990
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4190
|
-
import { Fragment, jsx as
|
|
4991
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4191
4992
|
function LinkEditorPanel({
|
|
4192
4993
|
open = true,
|
|
4193
4994
|
mode = "create",
|
|
@@ -4211,26 +5012,25 @@ function LinkEditorPanel({
|
|
|
4211
5012
|
onSubmit
|
|
4212
5013
|
});
|
|
4213
5014
|
const isCancel = state.secondaryLabel === "Cancel";
|
|
4214
|
-
return /* @__PURE__ */
|
|
4215
|
-
/* @__PURE__ */
|
|
5015
|
+
return /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
5016
|
+
/* @__PURE__ */ jsx13(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx13(
|
|
4216
5017
|
"button",
|
|
4217
5018
|
{
|
|
4218
5019
|
type: "button",
|
|
4219
5020
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4220
|
-
onClick: state.handleClose,
|
|
4221
5021
|
"aria-label": "Close",
|
|
4222
|
-
children: /* @__PURE__ */
|
|
5022
|
+
children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
|
|
4223
5023
|
}
|
|
4224
|
-
),
|
|
4225
|
-
/* @__PURE__ */
|
|
4226
|
-
/* @__PURE__ */
|
|
4227
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */
|
|
5024
|
+
) }),
|
|
5025
|
+
/* @__PURE__ */ jsx13(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
|
|
5026
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
5027
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
|
|
4228
5028
|
DestinationBreadcrumb,
|
|
4229
5029
|
{
|
|
4230
5030
|
pageTitle: state.selectedPage.title,
|
|
4231
5031
|
sectionLabel: state.selectedSection.label
|
|
4232
5032
|
}
|
|
4233
|
-
) : /* @__PURE__ */
|
|
5033
|
+
) : /* @__PURE__ */ jsx13(
|
|
4234
5034
|
UrlOrPageInput,
|
|
4235
5035
|
{
|
|
4236
5036
|
value: state.searchValue,
|
|
@@ -4243,18 +5043,18 @@ function LinkEditorPanel({
|
|
|
4243
5043
|
urlError: state.urlError
|
|
4244
5044
|
}
|
|
4245
5045
|
),
|
|
4246
|
-
state.showChooseSection ? /* @__PURE__ */
|
|
4247
|
-
/* @__PURE__ */
|
|
4248
|
-
/* @__PURE__ */
|
|
4249
|
-
/* @__PURE__ */
|
|
4250
|
-
/* @__PURE__ */
|
|
5046
|
+
state.showChooseSection ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
5047
|
+
/* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
5048
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
5049
|
+
/* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
5050
|
+
/* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
|
|
4251
5051
|
] })
|
|
4252
5052
|
] }) : null,
|
|
4253
|
-
state.showSectionPicker ? /* @__PURE__ */
|
|
4254
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */
|
|
5053
|
+
state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
5054
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4255
5055
|
] }),
|
|
4256
|
-
/* @__PURE__ */
|
|
4257
|
-
/* @__PURE__ */
|
|
5056
|
+
/* @__PURE__ */ jsxs8(DialogFooter, { className: "w-full px-6 pb-6", children: [
|
|
5057
|
+
/* @__PURE__ */ jsx13(
|
|
4258
5058
|
Button,
|
|
4259
5059
|
{
|
|
4260
5060
|
type: "button",
|
|
@@ -4269,7 +5069,7 @@ function LinkEditorPanel({
|
|
|
4269
5069
|
children: state.secondaryLabel
|
|
4270
5070
|
}
|
|
4271
5071
|
),
|
|
4272
|
-
/* @__PURE__ */
|
|
5072
|
+
/* @__PURE__ */ jsx13(
|
|
4273
5073
|
Button,
|
|
4274
5074
|
{
|
|
4275
5075
|
type: "button",
|
|
@@ -4288,28 +5088,34 @@ function LinkEditorPanel({
|
|
|
4288
5088
|
}
|
|
4289
5089
|
|
|
4290
5090
|
// src/ui/link-modal/LinkPopover.tsx
|
|
4291
|
-
import { jsx as
|
|
5091
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4292
5092
|
function LinkPopover({
|
|
4293
|
-
|
|
4294
|
-
parentScroll,
|
|
5093
|
+
open = true,
|
|
4295
5094
|
panelRef,
|
|
5095
|
+
portalContainer,
|
|
5096
|
+
onClose,
|
|
4296
5097
|
...editorProps
|
|
4297
5098
|
}) {
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
"div",
|
|
5099
|
+
return /* @__PURE__ */ jsx14(
|
|
5100
|
+
Dialog2,
|
|
4301
5101
|
{
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
5102
|
+
open,
|
|
5103
|
+
onOpenChange: (next) => {
|
|
5104
|
+
if (!next) onClose?.();
|
|
5105
|
+
},
|
|
5106
|
+
children: /* @__PURE__ */ jsx14(
|
|
5107
|
+
DialogContent,
|
|
5108
|
+
{
|
|
5109
|
+
ref: panelRef,
|
|
5110
|
+
container: portalContainer,
|
|
5111
|
+
"data-ohw-link-popover-root": "",
|
|
5112
|
+
"data-ohw-link-modal-root": "",
|
|
5113
|
+
"data-ohw-bridge": "",
|
|
5114
|
+
showCloseButton: false,
|
|
5115
|
+
className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
|
|
5116
|
+
children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open, onClose })
|
|
5117
|
+
}
|
|
5118
|
+
)
|
|
4313
5119
|
}
|
|
4314
5120
|
);
|
|
4315
5121
|
}
|
|
@@ -4372,8 +5178,30 @@ function shouldUseDevFixtures() {
|
|
|
4372
5178
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
4373
5179
|
}
|
|
4374
5180
|
|
|
5181
|
+
// src/ui/badge.tsx
|
|
5182
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
5183
|
+
var badgeVariants = cva(
|
|
5184
|
+
"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",
|
|
5185
|
+
{
|
|
5186
|
+
variants: {
|
|
5187
|
+
variant: {
|
|
5188
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
5189
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
5190
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
5191
|
+
outline: "text-foreground"
|
|
5192
|
+
}
|
|
5193
|
+
},
|
|
5194
|
+
defaultVariants: {
|
|
5195
|
+
variant: "default"
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
);
|
|
5199
|
+
function Badge({ className, variant, ...props }) {
|
|
5200
|
+
return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5201
|
+
}
|
|
5202
|
+
|
|
4375
5203
|
// src/OhhwellsBridge.tsx
|
|
4376
|
-
import { Fragment as
|
|
5204
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4377
5205
|
var PRIMARY = "#0885FE";
|
|
4378
5206
|
var IMAGE_FADE_MS = 300;
|
|
4379
5207
|
function runOpacityFade(el, onDone) {
|
|
@@ -4424,6 +5252,169 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
4424
5252
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
4425
5253
|
});
|
|
4426
5254
|
}
|
|
5255
|
+
function getSectionsTracker() {
|
|
5256
|
+
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
5257
|
+
if (!el) {
|
|
5258
|
+
el = document.createElement("div");
|
|
5259
|
+
el.setAttribute("data-ohw-sections-tracker", "");
|
|
5260
|
+
el.style.display = "none";
|
|
5261
|
+
document.body.appendChild(el);
|
|
5262
|
+
}
|
|
5263
|
+
return el;
|
|
5264
|
+
}
|
|
5265
|
+
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
5266
|
+
const tracker = getSectionsTracker();
|
|
5267
|
+
let sections = [];
|
|
5268
|
+
try {
|
|
5269
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5270
|
+
} catch {
|
|
5271
|
+
}
|
|
5272
|
+
const currentPath = window.location.pathname;
|
|
5273
|
+
const updated = sections.map((s) => {
|
|
5274
|
+
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
5275
|
+
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
5276
|
+
return { ...s, scheduleId };
|
|
5277
|
+
});
|
|
5278
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5279
|
+
return tracker.textContent ?? "[]";
|
|
5280
|
+
}
|
|
5281
|
+
function schedulingSectionId(insertAfter) {
|
|
5282
|
+
return `scheduling-${insertAfter}`;
|
|
5283
|
+
}
|
|
5284
|
+
var INSERT_BEFORE_MARKER = ":before:";
|
|
5285
|
+
function parseSchedulingInsertAfter(insertAfter) {
|
|
5286
|
+
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
5287
|
+
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
5288
|
+
return {
|
|
5289
|
+
anchor: insertAfter.slice(0, idx),
|
|
5290
|
+
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
5294
|
+
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
5295
|
+
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
5296
|
+
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
5297
|
+
return { effectiveInsertAfter, insertBefore };
|
|
5298
|
+
}
|
|
5299
|
+
function getSchedulingMountPoint(insertAfter) {
|
|
5300
|
+
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
5301
|
+
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
5302
|
+
if (!anchorEl && anchor === "scheduling") {
|
|
5303
|
+
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
5304
|
+
anchorEl = widgets.at(-1) ?? null;
|
|
5305
|
+
}
|
|
5306
|
+
if (!anchorEl) return null;
|
|
5307
|
+
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
5308
|
+
}
|
|
5309
|
+
function schedulingMountDepth(insertAfter) {
|
|
5310
|
+
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
5311
|
+
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
5312
|
+
}
|
|
5313
|
+
function getPageSchedulingEntries(raw) {
|
|
5314
|
+
if (!raw) return [];
|
|
5315
|
+
try {
|
|
5316
|
+
const entries = JSON.parse(raw);
|
|
5317
|
+
const currentPath = window.location.pathname;
|
|
5318
|
+
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
5319
|
+
} catch {
|
|
5320
|
+
return [];
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
function isSchedulingWidgetMissing(entry) {
|
|
5324
|
+
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
5325
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
5326
|
+
}
|
|
5327
|
+
function hasMissingSchedulingWidgets(entries) {
|
|
5328
|
+
return entries.some(isSchedulingWidgetMissing);
|
|
5329
|
+
}
|
|
5330
|
+
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
5331
|
+
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
5332
|
+
mountSchedulingEntries(entries, notifyOnConnect);
|
|
5333
|
+
}
|
|
5334
|
+
function initSectionsFromContent(content, removeExisting = false) {
|
|
5335
|
+
const raw = content["__ohw_sections"];
|
|
5336
|
+
if (!raw) return;
|
|
5337
|
+
try {
|
|
5338
|
+
if (removeExisting) {
|
|
5339
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
5340
|
+
}
|
|
5341
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5342
|
+
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5343
|
+
const pageEntries = getPageSchedulingEntries(raw);
|
|
5344
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
5345
|
+
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
5346
|
+
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
5347
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
5348
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
5349
|
+
for (const entry of preExisting) {
|
|
5350
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5351
|
+
}
|
|
5352
|
+
} catch {
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
5356
|
+
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
5357
|
+
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
5358
|
+
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
5359
|
+
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
5360
|
+
if (!mountPoint) return false;
|
|
5361
|
+
const container = document.createElement("div");
|
|
5362
|
+
container.dataset.ohwSectionContainer = "scheduling";
|
|
5363
|
+
if (insertBefore) {
|
|
5364
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
5365
|
+
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
5366
|
+
if (!beforePoint) return false;
|
|
5367
|
+
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
5368
|
+
} else {
|
|
5369
|
+
let tail = mountPoint;
|
|
5370
|
+
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
5371
|
+
tail = tail.nextElementSibling;
|
|
5372
|
+
}
|
|
5373
|
+
tail.insertAdjacentElement("afterend", container);
|
|
5374
|
+
}
|
|
5375
|
+
const root = createRoot(container);
|
|
5376
|
+
flushSync(() => {
|
|
5377
|
+
root.render(
|
|
5378
|
+
/* @__PURE__ */ jsx16(
|
|
5379
|
+
SchedulingWidget,
|
|
5380
|
+
{
|
|
5381
|
+
notifyOnConnect,
|
|
5382
|
+
initialScheduleId: scheduleId,
|
|
5383
|
+
insertAfter: effectiveInsertAfter
|
|
5384
|
+
}
|
|
5385
|
+
)
|
|
5386
|
+
);
|
|
5387
|
+
});
|
|
5388
|
+
const tracker = getSectionsTracker();
|
|
5389
|
+
let sections = [];
|
|
5390
|
+
try {
|
|
5391
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5392
|
+
} catch {
|
|
5393
|
+
}
|
|
5394
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5395
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
5396
|
+
sections.push({
|
|
5397
|
+
type: "scheduling",
|
|
5398
|
+
insertAfter: effectiveInsertAfter,
|
|
5399
|
+
pagePath: window.location.pathname,
|
|
5400
|
+
...scheduleId ? { scheduleId } : {}
|
|
5401
|
+
});
|
|
5402
|
+
tracker.textContent = JSON.stringify(sections);
|
|
5403
|
+
}
|
|
5404
|
+
return true;
|
|
5405
|
+
}
|
|
5406
|
+
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
5407
|
+
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5408
|
+
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5409
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5410
|
+
const entry = pending[i];
|
|
5411
|
+
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
5412
|
+
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5413
|
+
pending.splice(i, 1);
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
4427
5418
|
function getLinkHref(el) {
|
|
4428
5419
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
4429
5420
|
return anchor?.getAttribute("href") ?? "";
|
|
@@ -4470,7 +5461,7 @@ function applyLinkByKey(key, val) {
|
|
|
4470
5461
|
}
|
|
4471
5462
|
function isInsideLinkEditor(target) {
|
|
4472
5463
|
return Boolean(
|
|
4473
|
-
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest('[data-slot="popover-content"]')
|
|
5464
|
+
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
4474
5465
|
);
|
|
4475
5466
|
}
|
|
4476
5467
|
function getHrefKeyFromElement(el) {
|
|
@@ -4481,6 +5472,15 @@ function getHrefKeyFromElement(el) {
|
|
|
4481
5472
|
if (!key) return null;
|
|
4482
5473
|
return { anchor, key };
|
|
4483
5474
|
}
|
|
5475
|
+
function isNavbarButton(el) {
|
|
5476
|
+
return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
|
|
5477
|
+
}
|
|
5478
|
+
function clearHrefKeyHover(anchor) {
|
|
5479
|
+
anchor.removeAttribute("data-ohw-hovered");
|
|
5480
|
+
anchor.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
|
|
5481
|
+
el.removeAttribute("data-ohw-hovered");
|
|
5482
|
+
});
|
|
5483
|
+
}
|
|
4484
5484
|
function collectSections() {
|
|
4485
5485
|
return collectSectionsFromDom();
|
|
4486
5486
|
}
|
|
@@ -4604,6 +5604,8 @@ var ICONS = {
|
|
|
4604
5604
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
4605
5605
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
4606
5606
|
};
|
|
5607
|
+
var TOOLBAR_PILL_PADDING = 2;
|
|
5608
|
+
var TOOLBAR_TOGGLE_GAP = 4;
|
|
4607
5609
|
var TOOLBAR_GROUPS = [
|
|
4608
5610
|
[
|
|
4609
5611
|
{ cmd: "bold", title: "Bold" },
|
|
@@ -4623,7 +5625,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4623
5625
|
];
|
|
4624
5626
|
function GlowFrame({ rect, elRef }) {
|
|
4625
5627
|
const GAP = 6;
|
|
4626
|
-
return /* @__PURE__ */
|
|
5628
|
+
return /* @__PURE__ */ jsx16(
|
|
4627
5629
|
"div",
|
|
4628
5630
|
{
|
|
4629
5631
|
ref: elRef,
|
|
@@ -4642,41 +5644,191 @@ function GlowFrame({ rect, elRef }) {
|
|
|
4642
5644
|
}
|
|
4643
5645
|
);
|
|
4644
5646
|
}
|
|
4645
|
-
function
|
|
5647
|
+
function getIframeVisibleClip(parentScroll) {
|
|
5648
|
+
if (!parentScroll) return null;
|
|
5649
|
+
const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
|
|
5650
|
+
const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
|
|
5651
|
+
const clipBottom = Math.min(
|
|
5652
|
+
window.innerHeight,
|
|
5653
|
+
visibleCanvasTop + canvasH - iframeOffsetTop
|
|
5654
|
+
);
|
|
5655
|
+
return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
|
|
5656
|
+
}
|
|
5657
|
+
function applyVisibleViewport(el, parentScroll) {
|
|
5658
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5659
|
+
const top = clip?.top ?? 0;
|
|
5660
|
+
const height = clip ? clip.bottom - clip.top : window.innerHeight;
|
|
5661
|
+
el.style.top = `${top}px`;
|
|
5662
|
+
el.style.height = `${height}px`;
|
|
5663
|
+
}
|
|
5664
|
+
function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
5665
|
+
const isAbove = transform.includes("translateY(-100%)");
|
|
5666
|
+
let visualTop = isAbove ? top - approxH : top;
|
|
5667
|
+
let visualBottom = isAbove ? top : top + approxH;
|
|
5668
|
+
const minTop = clip.top + gap;
|
|
5669
|
+
const maxBottom = clip.bottom - gap;
|
|
5670
|
+
if (visualTop >= minTop && visualBottom <= maxBottom) {
|
|
5671
|
+
return { top, transform };
|
|
5672
|
+
}
|
|
5673
|
+
const belowTop = rect.bottom + gap;
|
|
5674
|
+
if (belowTop + approxH <= maxBottom && belowTop >= minTop) {
|
|
5675
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5676
|
+
}
|
|
5677
|
+
const aboveTop = rect.top - gap;
|
|
5678
|
+
if (aboveTop - approxH >= minTop && aboveTop <= maxBottom) {
|
|
5679
|
+
return { top: aboveTop, transform: "translateX(-50%) translateY(-100%)" };
|
|
5680
|
+
}
|
|
5681
|
+
if (belowTop + approxH <= maxBottom) {
|
|
5682
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5683
|
+
}
|
|
5684
|
+
const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
|
|
5685
|
+
return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
|
|
5686
|
+
}
|
|
5687
|
+
function calcToolbarPos(rect, parentScroll, approxW = 306) {
|
|
4646
5688
|
const GAP = 8;
|
|
4647
|
-
const APPROX_H =
|
|
4648
|
-
const APPROX_W =
|
|
5689
|
+
const APPROX_H = 32;
|
|
5690
|
+
const APPROX_W = approxW;
|
|
5691
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5692
|
+
const clipTop = clip?.top ?? 0;
|
|
4649
5693
|
const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
|
|
4650
5694
|
const visibleTop = rect.top - canvasTopInIframe;
|
|
4651
5695
|
const visibleBottom = rect.bottom - canvasTopInIframe;
|
|
4652
5696
|
const isTall = rect.height > APPROX_H + GAP;
|
|
5697
|
+
const spaceAbove = rect.top - (clipTop + GAP);
|
|
5698
|
+
const fitsAbove = spaceAbove >= APPROX_H + GAP;
|
|
4653
5699
|
let top;
|
|
4654
5700
|
let transform;
|
|
4655
|
-
if (visibleTop > 0 || !isTall) {
|
|
5701
|
+
if ((visibleTop > 0 || !isTall) && fitsAbove) {
|
|
4656
5702
|
top = rect.top - GAP;
|
|
4657
5703
|
transform = "translateX(-50%) translateY(-100%)";
|
|
5704
|
+
} else if (visibleTop > 0 || !isTall) {
|
|
5705
|
+
top = rect.bottom + GAP;
|
|
5706
|
+
transform = "translateX(-50%)";
|
|
4658
5707
|
} else if (visibleBottom > APPROX_H + GAP) {
|
|
4659
|
-
top =
|
|
5708
|
+
top = clipTop + GAP;
|
|
4660
5709
|
transform = "translateX(-50%)";
|
|
4661
5710
|
} else {
|
|
4662
5711
|
top = rect.bottom + GAP;
|
|
4663
5712
|
transform = "translateX(-50%)";
|
|
4664
5713
|
}
|
|
5714
|
+
if (clip) {
|
|
5715
|
+
const clamped = clampToolbarToClip(top, transform, rect, clip, APPROX_H, GAP);
|
|
5716
|
+
top = clamped.top;
|
|
5717
|
+
transform = clamped.transform;
|
|
5718
|
+
}
|
|
4665
5719
|
const rawLeft = rect.left + rect.width / 2;
|
|
4666
5720
|
const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
|
|
4667
5721
|
return { top, left, transform };
|
|
4668
5722
|
}
|
|
5723
|
+
var EDIT_LINK_ICON = /* @__PURE__ */ jsxs9("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" })
|
|
5726
|
+
] });
|
|
4669
5727
|
function FloatingToolbar({
|
|
4670
5728
|
rect,
|
|
4671
5729
|
parentScroll,
|
|
4672
5730
|
elRef,
|
|
5731
|
+
variant = "rich-text",
|
|
4673
5732
|
onCommand,
|
|
4674
5733
|
activeCommands,
|
|
4675
5734
|
showEditLink,
|
|
4676
5735
|
onEditLink
|
|
4677
5736
|
}) {
|
|
4678
|
-
const
|
|
4679
|
-
|
|
5737
|
+
const approxW = variant === "link-action" ? 112 : 306;
|
|
5738
|
+
const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
|
|
5739
|
+
if (variant === "link-action") {
|
|
5740
|
+
return /* @__PURE__ */ jsxs9(
|
|
5741
|
+
"div",
|
|
5742
|
+
{
|
|
5743
|
+
ref: elRef,
|
|
5744
|
+
"data-ohw-toolbar": "",
|
|
5745
|
+
style: {
|
|
5746
|
+
position: "fixed",
|
|
5747
|
+
top,
|
|
5748
|
+
left,
|
|
5749
|
+
transform,
|
|
5750
|
+
zIndex: 2147483647,
|
|
5751
|
+
background: "#fff",
|
|
5752
|
+
border: "1px solid #E7E5E4",
|
|
5753
|
+
borderRadius: 6,
|
|
5754
|
+
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
5755
|
+
display: "flex",
|
|
5756
|
+
alignItems: "center",
|
|
5757
|
+
padding: TOOLBAR_PILL_PADDING,
|
|
5758
|
+
gap: TOOLBAR_TOGGLE_GAP,
|
|
5759
|
+
fontFamily: "sans-serif",
|
|
5760
|
+
pointerEvents: "auto",
|
|
5761
|
+
whiteSpace: "nowrap"
|
|
5762
|
+
},
|
|
5763
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5764
|
+
children: [
|
|
5765
|
+
/* @__PURE__ */ jsx16(
|
|
5766
|
+
"button",
|
|
5767
|
+
{
|
|
5768
|
+
type: "button",
|
|
5769
|
+
title: "Edit link",
|
|
5770
|
+
onMouseDown: (e) => {
|
|
5771
|
+
e.preventDefault();
|
|
5772
|
+
e.stopPropagation();
|
|
5773
|
+
onEditLink?.();
|
|
5774
|
+
},
|
|
5775
|
+
onClick: (e) => {
|
|
5776
|
+
e.preventDefault();
|
|
5777
|
+
e.stopPropagation();
|
|
5778
|
+
},
|
|
5779
|
+
onMouseEnter: (e) => {
|
|
5780
|
+
e.currentTarget.style.background = "#F5F5F4";
|
|
5781
|
+
},
|
|
5782
|
+
onMouseLeave: (e) => {
|
|
5783
|
+
e.currentTarget.style.background = "transparent";
|
|
5784
|
+
},
|
|
5785
|
+
style: {
|
|
5786
|
+
display: "flex",
|
|
5787
|
+
alignItems: "center",
|
|
5788
|
+
justifyContent: "center",
|
|
5789
|
+
border: "none",
|
|
5790
|
+
background: "transparent",
|
|
5791
|
+
borderRadius: 4,
|
|
5792
|
+
cursor: "pointer",
|
|
5793
|
+
color: "#1C1917",
|
|
5794
|
+
flexShrink: 0,
|
|
5795
|
+
padding: 6
|
|
5796
|
+
},
|
|
5797
|
+
children: EDIT_LINK_ICON
|
|
5798
|
+
}
|
|
5799
|
+
),
|
|
5800
|
+
/* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5801
|
+
/* @__PURE__ */ jsx16(
|
|
5802
|
+
"button",
|
|
5803
|
+
{
|
|
5804
|
+
type: "button",
|
|
5805
|
+
title: "Coming soon",
|
|
5806
|
+
disabled: true,
|
|
5807
|
+
style: {
|
|
5808
|
+
display: "flex",
|
|
5809
|
+
alignItems: "center",
|
|
5810
|
+
justifyContent: "center",
|
|
5811
|
+
border: "none",
|
|
5812
|
+
background: "transparent",
|
|
5813
|
+
borderRadius: 4,
|
|
5814
|
+
cursor: "not-allowed",
|
|
5815
|
+
color: "#A8A29E",
|
|
5816
|
+
flexShrink: 0,
|
|
5817
|
+
padding: 6,
|
|
5818
|
+
opacity: 0.6
|
|
5819
|
+
},
|
|
5820
|
+
children: /* @__PURE__ */ jsxs9("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
|
|
5821
|
+
/* @__PURE__ */ jsx16("circle", { cx: "5", cy: "12", r: "1.5" }),
|
|
5822
|
+
/* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "1.5" }),
|
|
5823
|
+
/* @__PURE__ */ jsx16("circle", { cx: "19", cy: "12", r: "1.5" })
|
|
5824
|
+
] })
|
|
5825
|
+
}
|
|
5826
|
+
)
|
|
5827
|
+
]
|
|
5828
|
+
}
|
|
5829
|
+
);
|
|
5830
|
+
}
|
|
5831
|
+
return /* @__PURE__ */ jsxs9(
|
|
4680
5832
|
"div",
|
|
4681
5833
|
{
|
|
4682
5834
|
ref: elRef,
|
|
@@ -4693,19 +5845,19 @@ function FloatingToolbar({
|
|
|
4693
5845
|
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
4694
5846
|
display: "flex",
|
|
4695
5847
|
alignItems: "center",
|
|
4696
|
-
padding:
|
|
4697
|
-
gap:
|
|
5848
|
+
padding: TOOLBAR_PILL_PADDING,
|
|
5849
|
+
gap: TOOLBAR_TOGGLE_GAP,
|
|
4698
5850
|
fontFamily: "sans-serif",
|
|
4699
5851
|
pointerEvents: "auto",
|
|
4700
5852
|
whiteSpace: "nowrap"
|
|
4701
5853
|
},
|
|
4702
5854
|
onMouseDown: (e) => e.stopPropagation(),
|
|
4703
5855
|
children: [
|
|
4704
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
4705
|
-
gi > 0 && /* @__PURE__ */
|
|
5856
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs9(React6.Fragment, { children: [
|
|
5857
|
+
gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4706
5858
|
btns.map((btn) => {
|
|
4707
5859
|
const isActive = activeCommands.has(btn.cmd);
|
|
4708
|
-
return /* @__PURE__ */
|
|
5860
|
+
return /* @__PURE__ */ jsx16(
|
|
4709
5861
|
"button",
|
|
4710
5862
|
{
|
|
4711
5863
|
title: btn.title,
|
|
@@ -4731,7 +5883,7 @@ function FloatingToolbar({
|
|
|
4731
5883
|
flexShrink: 0,
|
|
4732
5884
|
padding: 6
|
|
4733
5885
|
},
|
|
4734
|
-
children: /* @__PURE__ */
|
|
5886
|
+
children: /* @__PURE__ */ jsx16(
|
|
4735
5887
|
"svg",
|
|
4736
5888
|
{
|
|
4737
5889
|
width: "16",
|
|
@@ -4751,7 +5903,7 @@ function FloatingToolbar({
|
|
|
4751
5903
|
);
|
|
4752
5904
|
})
|
|
4753
5905
|
] }, gi)),
|
|
4754
|
-
showEditLink ? /* @__PURE__ */
|
|
5906
|
+
showEditLink ? /* @__PURE__ */ jsx16(
|
|
4755
5907
|
"button",
|
|
4756
5908
|
{
|
|
4757
5909
|
type: "button",
|
|
@@ -4783,10 +5935,7 @@ function FloatingToolbar({
|
|
|
4783
5935
|
flexShrink: 0,
|
|
4784
5936
|
padding: 6
|
|
4785
5937
|
},
|
|
4786
|
-
children:
|
|
4787
|
-
/* @__PURE__ */ jsx13("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
4788
|
-
/* @__PURE__ */ jsx13("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
4789
|
-
] })
|
|
5938
|
+
children: EDIT_LINK_ICON
|
|
4790
5939
|
}
|
|
4791
5940
|
) : null
|
|
4792
5941
|
]
|
|
@@ -4803,7 +5952,7 @@ function StateToggle({
|
|
|
4803
5952
|
states,
|
|
4804
5953
|
onStateChange
|
|
4805
5954
|
}) {
|
|
4806
|
-
return /* @__PURE__ */
|
|
5955
|
+
return /* @__PURE__ */ jsx16(
|
|
4807
5956
|
ToggleGroup,
|
|
4808
5957
|
{
|
|
4809
5958
|
"data-ohw-state-toggle": "",
|
|
@@ -4817,18 +5966,35 @@ function StateToggle({
|
|
|
4817
5966
|
left: rect.right - 8,
|
|
4818
5967
|
transform: "translateX(-100%)"
|
|
4819
5968
|
},
|
|
4820
|
-
children: states.map((state) => /* @__PURE__ */
|
|
5969
|
+
children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4821
5970
|
}
|
|
4822
5971
|
);
|
|
4823
5972
|
}
|
|
4824
5973
|
var contentCache = /* @__PURE__ */ new Map();
|
|
5974
|
+
function resolveSubdomain(subdomainFromQuery) {
|
|
5975
|
+
if (subdomainFromQuery) return subdomainFromQuery;
|
|
5976
|
+
if (typeof window !== "undefined") {
|
|
5977
|
+
const parts = window.location.hostname.split(".");
|
|
5978
|
+
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
5979
|
+
}
|
|
5980
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
5981
|
+
if (siteUrl) {
|
|
5982
|
+
try {
|
|
5983
|
+
const host = new URL(siteUrl).hostname;
|
|
5984
|
+
const siteParts = host.split(".");
|
|
5985
|
+
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
5986
|
+
} catch {
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
return "";
|
|
5990
|
+
}
|
|
4825
5991
|
function OhhwellsBridge() {
|
|
4826
5992
|
const pathname = usePathname();
|
|
4827
5993
|
const router = useRouter();
|
|
4828
5994
|
const searchParams = useSearchParams();
|
|
4829
5995
|
const isEditMode = isEditSessionActive();
|
|
4830
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
4831
|
-
|
|
5996
|
+
const [bridgeRoot, setBridgeRoot] = useState5(null);
|
|
5997
|
+
useEffect3(() => {
|
|
4832
5998
|
const figtreeFontId = "ohw-figtree-font";
|
|
4833
5999
|
if (!document.getElementById(figtreeFontId)) {
|
|
4834
6000
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4851,50 +6017,65 @@ function OhhwellsBridge() {
|
|
|
4851
6017
|
};
|
|
4852
6018
|
}, []);
|
|
4853
6019
|
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
|
-
})();
|
|
6020
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
4859
6021
|
const postToParent = useCallback2((data) => {
|
|
4860
6022
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4861
6023
|
window.parent.postMessage(data, "*");
|
|
4862
6024
|
}
|
|
4863
6025
|
}, []);
|
|
4864
|
-
const [fetchState, setFetchState] =
|
|
4865
|
-
const autoSaveTimers =
|
|
4866
|
-
const activeElRef =
|
|
4867
|
-
const
|
|
4868
|
-
const
|
|
4869
|
-
const
|
|
4870
|
-
const
|
|
4871
|
-
const
|
|
4872
|
-
const
|
|
4873
|
-
const
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
6026
|
+
const [fetchState, setFetchState] = useState5("idle");
|
|
6027
|
+
const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
|
|
6028
|
+
const activeElRef = useRef3(null);
|
|
6029
|
+
const selectedElRef = useRef3(null);
|
|
6030
|
+
const originalContentRef = useRef3(null);
|
|
6031
|
+
const activeStateElRef = useRef3(null);
|
|
6032
|
+
const parentScrollRef = useRef3(null);
|
|
6033
|
+
const visibleViewportRef = useRef3(null);
|
|
6034
|
+
const [dialogPortalContainer, setDialogPortalContainer] = useState5(null);
|
|
6035
|
+
const attachVisibleViewport = useCallback2((node) => {
|
|
6036
|
+
visibleViewportRef.current = node;
|
|
6037
|
+
setDialogPortalContainer(node);
|
|
6038
|
+
if (node) applyVisibleViewport(node, parentScrollRef.current);
|
|
6039
|
+
}, []);
|
|
6040
|
+
const toolbarElRef = useRef3(null);
|
|
6041
|
+
const glowElRef = useRef3(null);
|
|
6042
|
+
const hoveredImageRef = useRef3(null);
|
|
6043
|
+
const hoveredImageHasTextOverlapRef = useRef3(false);
|
|
6044
|
+
const hoveredGapRef = useRef3(null);
|
|
6045
|
+
const imageUnhoverTimerRef = useRef3(null);
|
|
6046
|
+
const imageShowTimerRef = useRef3(null);
|
|
6047
|
+
const editStylesRef = useRef3(null);
|
|
6048
|
+
const activateRef = useRef3(() => {
|
|
6049
|
+
});
|
|
6050
|
+
const deactivateRef = useRef3(() => {
|
|
4878
6051
|
});
|
|
4879
|
-
const
|
|
6052
|
+
const selectRef = useRef3(() => {
|
|
4880
6053
|
});
|
|
4881
|
-
const
|
|
6054
|
+
const deselectRef = useRef3(() => {
|
|
4882
6055
|
});
|
|
4883
|
-
const
|
|
6056
|
+
const refreshActiveCommandsRef = useRef3(() => {
|
|
6057
|
+
});
|
|
6058
|
+
const postToParentRef = useRef3(postToParent);
|
|
4884
6059
|
postToParentRef.current = postToParent;
|
|
4885
|
-
const
|
|
4886
|
-
const
|
|
4887
|
-
const [
|
|
4888
|
-
const [
|
|
4889
|
-
const
|
|
4890
|
-
|
|
4891
|
-
const [
|
|
4892
|
-
const [
|
|
4893
|
-
const
|
|
4894
|
-
const
|
|
4895
|
-
const
|
|
4896
|
-
const
|
|
4897
|
-
const
|
|
6060
|
+
const sectionsLoadedRef = useRef3(false);
|
|
6061
|
+
const pendingScheduleConfigRequests = useRef3([]);
|
|
6062
|
+
const [toolbarRect, setToolbarRect] = useState5(null);
|
|
6063
|
+
const [toolbarVariant, setToolbarVariant] = useState5("none");
|
|
6064
|
+
const toolbarVariantRef = useRef3("none");
|
|
6065
|
+
toolbarVariantRef.current = toolbarVariant;
|
|
6066
|
+
const [toggleState, setToggleState] = useState5(null);
|
|
6067
|
+
const [maxBadge, setMaxBadge] = useState5(null);
|
|
6068
|
+
const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
|
|
6069
|
+
const [sectionGap, setSectionGap] = useState5(null);
|
|
6070
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
|
|
6071
|
+
const [linkPopover, setLinkPopover] = useState5(null);
|
|
6072
|
+
const [sitePages, setSitePages] = useState5([]);
|
|
6073
|
+
const [sectionsByPath, setSectionsByPath] = useState5({});
|
|
6074
|
+
const sectionsPrefetchGenRef = useRef3(0);
|
|
6075
|
+
const setLinkPopoverRef = useRef3(setLinkPopover);
|
|
6076
|
+
const linkPopoverPanelRef = useRef3(null);
|
|
6077
|
+
const linkPopoverOpenRef = useRef3(false);
|
|
6078
|
+
const linkPopoverGraceUntilRef = useRef3(0);
|
|
4898
6079
|
setLinkPopoverRef.current = setLinkPopover;
|
|
4899
6080
|
const bumpLinkPopoverGrace = () => {
|
|
4900
6081
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
@@ -4918,17 +6099,43 @@ function OhhwellsBridge() {
|
|
|
4918
6099
|
);
|
|
4919
6100
|
});
|
|
4920
6101
|
}, [isEditMode, pathname]);
|
|
4921
|
-
const runSectionsPrefetchRef =
|
|
6102
|
+
const runSectionsPrefetchRef = useRef3(runSectionsPrefetch);
|
|
4922
6103
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
4923
|
-
|
|
6104
|
+
useEffect3(() => {
|
|
4924
6105
|
if (!linkPopover) return;
|
|
4925
6106
|
if (hoveredImageRef.current) {
|
|
4926
6107
|
hoveredImageRef.current = null;
|
|
4927
6108
|
hoveredImageHasTextOverlapRef.current = false;
|
|
4928
6109
|
}
|
|
6110
|
+
hoveredGapRef.current = null;
|
|
6111
|
+
setSectionGap(null);
|
|
4929
6112
|
postToParent({ type: "ow:image-unhover" });
|
|
6113
|
+
postToParent({ type: "ow:link-modal-lock", locked: true });
|
|
6114
|
+
const html = document.documentElement;
|
|
6115
|
+
const body = document.body;
|
|
6116
|
+
const prevHtmlOverflow = html.style.overflow;
|
|
6117
|
+
const prevBodyOverflow = body.style.overflow;
|
|
6118
|
+
html.style.overflow = "hidden";
|
|
6119
|
+
body.style.overflow = "hidden";
|
|
6120
|
+
const preventBackgroundScroll = (e) => {
|
|
6121
|
+
const target = e.target;
|
|
6122
|
+
if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
|
|
6123
|
+
return;
|
|
6124
|
+
}
|
|
6125
|
+
e.preventDefault();
|
|
6126
|
+
};
|
|
6127
|
+
const scrollOpts = { passive: false, capture: true };
|
|
6128
|
+
document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6129
|
+
document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6130
|
+
return () => {
|
|
6131
|
+
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6132
|
+
html.style.overflow = prevHtmlOverflow;
|
|
6133
|
+
body.style.overflow = prevBodyOverflow;
|
|
6134
|
+
document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6135
|
+
document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6136
|
+
};
|
|
4930
6137
|
}, [linkPopover, postToParent]);
|
|
4931
|
-
|
|
6138
|
+
useEffect3(() => {
|
|
4932
6139
|
if (!isEditMode) return;
|
|
4933
6140
|
const useFixtures = shouldUseDevFixtures();
|
|
4934
6141
|
if (useFixtures) {
|
|
@@ -4952,21 +6159,27 @@ function OhhwellsBridge() {
|
|
|
4952
6159
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
4953
6160
|
return () => window.removeEventListener("message", onSitePages);
|
|
4954
6161
|
}, [isEditMode, postToParent]);
|
|
4955
|
-
|
|
6162
|
+
useEffect3(() => {
|
|
4956
6163
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
4957
6164
|
void loadAllSectionsManifest().then((manifest) => {
|
|
4958
6165
|
if (Object.keys(manifest).length === 0) return;
|
|
4959
6166
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
4960
6167
|
});
|
|
4961
6168
|
}, [isEditMode]);
|
|
4962
|
-
|
|
6169
|
+
useEffect3(() => {
|
|
4963
6170
|
const update = () => {
|
|
4964
|
-
const el = activeElRef.current;
|
|
6171
|
+
const el = activeElRef.current ?? selectedElRef.current;
|
|
4965
6172
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
4966
6173
|
setToggleState((prev) => {
|
|
4967
6174
|
if (!prev || !activeStateElRef.current) return prev;
|
|
4968
6175
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
4969
6176
|
});
|
|
6177
|
+
setSectionGap((prev) => {
|
|
6178
|
+
if (!prev) return prev;
|
|
6179
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
6180
|
+
if (!anchor) return prev;
|
|
6181
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
6182
|
+
});
|
|
4970
6183
|
};
|
|
4971
6184
|
const vvp = window.visualViewport;
|
|
4972
6185
|
if (!vvp) return;
|
|
@@ -4980,6 +6193,29 @@ function OhhwellsBridge() {
|
|
|
4980
6193
|
const refreshStateRules = useCallback2(() => {
|
|
4981
6194
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4982
6195
|
}, []);
|
|
6196
|
+
const processConfigRequest = useCallback2((insertAfterVal) => {
|
|
6197
|
+
const tracker = getSectionsTracker();
|
|
6198
|
+
let entries = [];
|
|
6199
|
+
try {
|
|
6200
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
6201
|
+
} catch {
|
|
6202
|
+
}
|
|
6203
|
+
const path = window.location.pathname;
|
|
6204
|
+
const entry = entries.find(
|
|
6205
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
6206
|
+
);
|
|
6207
|
+
if (entry) {
|
|
6208
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
6209
|
+
return;
|
|
6210
|
+
}
|
|
6211
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
6212
|
+
entries.push(newEntry);
|
|
6213
|
+
tracker.textContent = JSON.stringify(entries);
|
|
6214
|
+
if (isEditMode) {
|
|
6215
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6216
|
+
}
|
|
6217
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
6218
|
+
}, [isEditMode]);
|
|
4983
6219
|
const deactivate = useCallback2(() => {
|
|
4984
6220
|
const el = activeElRef.current;
|
|
4985
6221
|
if (!el) return;
|
|
@@ -5000,19 +6236,41 @@ function OhhwellsBridge() {
|
|
|
5000
6236
|
}
|
|
5001
6237
|
el.removeAttribute("contenteditable");
|
|
5002
6238
|
activeElRef.current = null;
|
|
5003
|
-
|
|
6239
|
+
if (!selectedElRef.current) {
|
|
6240
|
+
setToolbarRect(null);
|
|
6241
|
+
setToolbarVariant("none");
|
|
6242
|
+
}
|
|
5004
6243
|
setMaxBadge(null);
|
|
5005
6244
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
5006
6245
|
setToolbarShowEditLink(false);
|
|
5007
6246
|
postToParent({ type: "ow:exit-edit" });
|
|
5008
6247
|
}, [postToParent]);
|
|
6248
|
+
const deselect = useCallback2(() => {
|
|
6249
|
+
selectedElRef.current = null;
|
|
6250
|
+
if (!activeElRef.current) {
|
|
6251
|
+
setToolbarRect(null);
|
|
6252
|
+
setToolbarVariant("none");
|
|
6253
|
+
}
|
|
6254
|
+
}, []);
|
|
6255
|
+
const select = useCallback2((anchor) => {
|
|
6256
|
+
if (!isNavbarButton(anchor)) return;
|
|
6257
|
+
if (activeElRef.current) deactivate();
|
|
6258
|
+
selectedElRef.current = anchor;
|
|
6259
|
+
clearHrefKeyHover(anchor);
|
|
6260
|
+
setToolbarVariant("link-action");
|
|
6261
|
+
setToolbarRect(anchor.getBoundingClientRect());
|
|
6262
|
+
setToolbarShowEditLink(false);
|
|
6263
|
+
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6264
|
+
}, [deactivate]);
|
|
5009
6265
|
const activate = useCallback2((el) => {
|
|
5010
6266
|
if (activeElRef.current === el) return;
|
|
6267
|
+
selectedElRef.current = null;
|
|
5011
6268
|
deactivate();
|
|
5012
6269
|
if (hoveredImageRef.current) {
|
|
5013
6270
|
hoveredImageRef.current = null;
|
|
5014
6271
|
postToParentRef.current({ type: "ow:image-unhover" });
|
|
5015
6272
|
}
|
|
6273
|
+
setToolbarVariant("rich-text");
|
|
5016
6274
|
el.setAttribute("contenteditable", "true");
|
|
5017
6275
|
el.removeAttribute("data-ohw-hovered");
|
|
5018
6276
|
activeElRef.current = el;
|
|
@@ -5025,6 +6283,8 @@ function OhhwellsBridge() {
|
|
|
5025
6283
|
}, [deactivate, postToParent]);
|
|
5026
6284
|
activateRef.current = activate;
|
|
5027
6285
|
deactivateRef.current = deactivate;
|
|
6286
|
+
selectRef.current = select;
|
|
6287
|
+
deselectRef.current = deselect;
|
|
5028
6288
|
useLayoutEffect(() => {
|
|
5029
6289
|
if (!subdomain || isEditMode) {
|
|
5030
6290
|
setFetchState("done");
|
|
@@ -5033,6 +6293,7 @@ function OhhwellsBridge() {
|
|
|
5033
6293
|
const applyContent = (content) => {
|
|
5034
6294
|
const imageLoads = [];
|
|
5035
6295
|
for (const [key, val] of Object.entries(content)) {
|
|
6296
|
+
if (key === "__ohw_sections") continue;
|
|
5036
6297
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5037
6298
|
if (el.dataset.ohwEditable === "image") {
|
|
5038
6299
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5054,6 +6315,9 @@ function OhhwellsBridge() {
|
|
|
5054
6315
|
});
|
|
5055
6316
|
applyLinkByKey(key, val);
|
|
5056
6317
|
}
|
|
6318
|
+
initSectionsFromContent(content, true);
|
|
6319
|
+
sectionsLoadedRef.current = true;
|
|
6320
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5057
6321
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5058
6322
|
}) : Promise.resolve();
|
|
5059
6323
|
};
|
|
@@ -5078,12 +6342,15 @@ function OhhwellsBridge() {
|
|
|
5078
6342
|
cancelled = true;
|
|
5079
6343
|
};
|
|
5080
6344
|
}, [subdomain, isEditMode, pathname]);
|
|
5081
|
-
|
|
6345
|
+
useEffect3(() => {
|
|
5082
6346
|
if (!subdomain || isEditMode) return;
|
|
6347
|
+
let debounceTimer = null;
|
|
5083
6348
|
const applyFromCache = () => {
|
|
5084
6349
|
const content = contentCache.get(subdomain);
|
|
5085
6350
|
if (!content) return;
|
|
6351
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
5086
6352
|
for (const [key, val] of Object.entries(content)) {
|
|
6353
|
+
if (key === "__ohw_sections") continue;
|
|
5087
6354
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5088
6355
|
if (el.dataset.ohwEditable === "image") {
|
|
5089
6356
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5100,21 +6367,34 @@ function OhhwellsBridge() {
|
|
|
5100
6367
|
applyLinkByKey(key, val);
|
|
5101
6368
|
}
|
|
5102
6369
|
};
|
|
5103
|
-
|
|
5104
|
-
|
|
6370
|
+
const scheduleApply = () => {
|
|
6371
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6372
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6373
|
+
};
|
|
6374
|
+
scheduleApply();
|
|
6375
|
+
const observer = new MutationObserver(scheduleApply);
|
|
5105
6376
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
5106
|
-
return () =>
|
|
5107
|
-
|
|
6377
|
+
return () => {
|
|
6378
|
+
observer.disconnect();
|
|
6379
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6380
|
+
};
|
|
6381
|
+
}, [subdomain, isEditMode, pathname]);
|
|
5108
6382
|
useLayoutEffect(() => {
|
|
5109
6383
|
const el = document.getElementById("ohw-loader");
|
|
5110
6384
|
if (!el) return;
|
|
5111
6385
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
5112
6386
|
el.style.display = visible ? "flex" : "none";
|
|
5113
6387
|
}, [subdomain, fetchState]);
|
|
5114
|
-
|
|
6388
|
+
useEffect3(() => {
|
|
5115
6389
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
5116
6390
|
}, [pathname, postToParent]);
|
|
5117
|
-
|
|
6391
|
+
useEffect3(() => {
|
|
6392
|
+
if (!isEditMode) return;
|
|
6393
|
+
setLinkPopover(null);
|
|
6394
|
+
deselectRef.current();
|
|
6395
|
+
deactivateRef.current();
|
|
6396
|
+
}, [pathname, isEditMode]);
|
|
6397
|
+
useEffect3(() => {
|
|
5118
6398
|
if (!isEditMode) return;
|
|
5119
6399
|
const measure = () => {
|
|
5120
6400
|
const h = document.body.scrollHeight;
|
|
@@ -5138,7 +6418,7 @@ function OhhwellsBridge() {
|
|
|
5138
6418
|
window.removeEventListener("resize", handleResize);
|
|
5139
6419
|
};
|
|
5140
6420
|
}, [pathname, isEditMode, postToParent]);
|
|
5141
|
-
|
|
6421
|
+
useEffect3(() => {
|
|
5142
6422
|
if (!subdomainFromQuery || isEditMode) return;
|
|
5143
6423
|
const handleClick = (e) => {
|
|
5144
6424
|
const anchor = e.target.closest("a");
|
|
@@ -5154,7 +6434,7 @@ function OhhwellsBridge() {
|
|
|
5154
6434
|
document.addEventListener("click", handleClick, true);
|
|
5155
6435
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5156
6436
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5157
|
-
|
|
6437
|
+
useEffect3(() => {
|
|
5158
6438
|
if (!isEditMode) {
|
|
5159
6439
|
editStylesRef.current?.base.remove();
|
|
5160
6440
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5224,12 +6504,12 @@ function OhhwellsBridge() {
|
|
|
5224
6504
|
if (editable.dataset.ohwEditable === "link") {
|
|
5225
6505
|
e.preventDefault();
|
|
5226
6506
|
e.stopPropagation();
|
|
6507
|
+
deselectRef.current();
|
|
5227
6508
|
deactivateRef.current();
|
|
5228
6509
|
bumpLinkPopoverGrace();
|
|
5229
6510
|
setLinkPopoverRef.current({
|
|
5230
6511
|
key: editable.dataset.ohwKey ?? "",
|
|
5231
|
-
target: getLinkHref(editable)
|
|
5232
|
-
rect: editable.getBoundingClientRect()
|
|
6512
|
+
target: getLinkHref(editable)
|
|
5233
6513
|
});
|
|
5234
6514
|
return;
|
|
5235
6515
|
}
|
|
@@ -5239,11 +6519,31 @@ function OhhwellsBridge() {
|
|
|
5239
6519
|
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
|
|
5240
6520
|
return;
|
|
5241
6521
|
}
|
|
6522
|
+
const hrefCtx = getHrefKeyFromElement(editable);
|
|
6523
|
+
if (hrefCtx && isNavbarButton(hrefCtx.anchor)) {
|
|
6524
|
+
e.preventDefault();
|
|
6525
|
+
e.stopPropagation();
|
|
6526
|
+
const { anchor: anchor2 } = hrefCtx;
|
|
6527
|
+
if (selectedElRef.current === anchor2) {
|
|
6528
|
+
activateRef.current(editable);
|
|
6529
|
+
return;
|
|
6530
|
+
}
|
|
6531
|
+
selectRef.current(anchor2);
|
|
6532
|
+
return;
|
|
6533
|
+
}
|
|
5242
6534
|
e.preventDefault();
|
|
5243
6535
|
e.stopPropagation();
|
|
5244
6536
|
activateRef.current(editable);
|
|
5245
6537
|
return;
|
|
5246
6538
|
}
|
|
6539
|
+
const hrefAnchor = target.closest("[data-ohw-href-key]");
|
|
6540
|
+
if (hrefAnchor && isNavbarButton(hrefAnchor)) {
|
|
6541
|
+
e.preventDefault();
|
|
6542
|
+
e.stopPropagation();
|
|
6543
|
+
if (selectedElRef.current === hrefAnchor) return;
|
|
6544
|
+
selectRef.current(hrefAnchor);
|
|
6545
|
+
return;
|
|
6546
|
+
}
|
|
5247
6547
|
if (activeElRef.current) {
|
|
5248
6548
|
const sel = window.getSelection();
|
|
5249
6549
|
if (sel && !sel.isCollapsed) {
|
|
@@ -5258,15 +6558,22 @@ function OhhwellsBridge() {
|
|
|
5258
6558
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
5259
6559
|
if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
|
|
5260
6560
|
}
|
|
6561
|
+
if (linkPopoverOpenRef.current && selectedElRef.current) {
|
|
6562
|
+
const selected = selectedElRef.current;
|
|
6563
|
+
if (selected === target || selected.contains(target)) return;
|
|
6564
|
+
}
|
|
5261
6565
|
if (linkPopoverOpenRef.current) {
|
|
5262
6566
|
setLinkPopoverRef.current(null);
|
|
5263
6567
|
return;
|
|
5264
6568
|
}
|
|
6569
|
+
deselectRef.current();
|
|
5265
6570
|
deactivateRef.current();
|
|
5266
6571
|
};
|
|
5267
6572
|
const handleMouseOver = (e) => {
|
|
5268
6573
|
const editable = e.target.closest("[data-ohw-editable]");
|
|
5269
6574
|
if (!editable) return;
|
|
6575
|
+
const selected = selectedElRef.current;
|
|
6576
|
+
if (selected && (selected === editable || selected.contains(editable))) return;
|
|
5270
6577
|
if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
|
|
5271
6578
|
editable.setAttribute("data-ohw-hovered", "");
|
|
5272
6579
|
}
|
|
@@ -5402,7 +6709,7 @@ function OhhwellsBridge() {
|
|
|
5402
6709
|
}
|
|
5403
6710
|
return;
|
|
5404
6711
|
}
|
|
5405
|
-
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]")) {
|
|
6712
|
+
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]") || topEl?.closest("[data-ohw-link-page-dropdown]")) {
|
|
5406
6713
|
if (hoveredImageRef.current) {
|
|
5407
6714
|
hoveredImageRef.current = null;
|
|
5408
6715
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -5455,6 +6762,15 @@ function OhhwellsBridge() {
|
|
|
5455
6762
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
5456
6763
|
return;
|
|
5457
6764
|
}
|
|
6765
|
+
if (hoveredGapRef.current) {
|
|
6766
|
+
if (hoveredImageRef.current) {
|
|
6767
|
+
hoveredImageRef.current = null;
|
|
6768
|
+
resumeAnimTracks();
|
|
6769
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6770
|
+
}
|
|
6771
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6772
|
+
return;
|
|
6773
|
+
}
|
|
5458
6774
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5459
6775
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
5460
6776
|
hoveredImageRef.current = imgEl;
|
|
@@ -5520,8 +6836,37 @@ function OhhwellsBridge() {
|
|
|
5520
6836
|
}
|
|
5521
6837
|
}
|
|
5522
6838
|
};
|
|
6839
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6840
|
+
if (linkPopoverOpenRef.current) {
|
|
6841
|
+
if (hoveredGapRef.current) {
|
|
6842
|
+
hoveredGapRef.current = null;
|
|
6843
|
+
setSectionGap(null);
|
|
6844
|
+
}
|
|
6845
|
+
return;
|
|
6846
|
+
}
|
|
6847
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6848
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6849
|
+
const ZONE = 20;
|
|
6850
|
+
for (let i = 0; i < sections.length; i++) {
|
|
6851
|
+
const a = sections[i];
|
|
6852
|
+
const b = sections[i + 1] ?? null;
|
|
6853
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6854
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6855
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6856
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6857
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6858
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
}
|
|
6862
|
+
if (hoveredGapRef.current) {
|
|
6863
|
+
hoveredGapRef.current = null;
|
|
6864
|
+
setSectionGap(null);
|
|
6865
|
+
}
|
|
6866
|
+
};
|
|
5523
6867
|
const handleMouseMove = (e) => {
|
|
5524
6868
|
const { clientX, clientY } = e;
|
|
6869
|
+
probeSectionGapAt(clientX, clientY);
|
|
5525
6870
|
probeImageAt(clientX, clientY);
|
|
5526
6871
|
probeHoverCardsAt(clientX, clientY);
|
|
5527
6872
|
};
|
|
@@ -5529,6 +6874,7 @@ function OhhwellsBridge() {
|
|
|
5529
6874
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
5530
6875
|
const { clientX, clientY } = e.data;
|
|
5531
6876
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6877
|
+
probeSectionGapAt(clientX, clientY);
|
|
5532
6878
|
probeImageAt(clientX, clientY);
|
|
5533
6879
|
probeHoverCardsAt(clientX, clientY);
|
|
5534
6880
|
};
|
|
@@ -5686,7 +7032,12 @@ function OhhwellsBridge() {
|
|
|
5686
7032
|
if (e.data?.type !== "ow:hydrate") return;
|
|
5687
7033
|
const content = e.data.content;
|
|
5688
7034
|
if (!content) return;
|
|
7035
|
+
let sectionsJson = null;
|
|
5689
7036
|
for (const [key, val] of Object.entries(content)) {
|
|
7037
|
+
if (key === "__ohw_sections") {
|
|
7038
|
+
sectionsJson = val;
|
|
7039
|
+
continue;
|
|
7040
|
+
}
|
|
5690
7041
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5691
7042
|
if (el.dataset.ohwEditable === "image") {
|
|
5692
7043
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5701,6 +7052,11 @@ function OhhwellsBridge() {
|
|
|
5701
7052
|
});
|
|
5702
7053
|
applyLinkByKey(key, val);
|
|
5703
7054
|
}
|
|
7055
|
+
if (sectionsJson) {
|
|
7056
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
7057
|
+
sectionsLoadedRef.current = true;
|
|
7058
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
7059
|
+
}
|
|
5704
7060
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5705
7061
|
};
|
|
5706
7062
|
window.addEventListener("message", handleHydrate);
|
|
@@ -5711,6 +7067,7 @@ function OhhwellsBridge() {
|
|
|
5711
7067
|
setLinkPopoverRef.current(null);
|
|
5712
7068
|
return;
|
|
5713
7069
|
}
|
|
7070
|
+
deselectRef.current();
|
|
5714
7071
|
deactivateRef.current();
|
|
5715
7072
|
};
|
|
5716
7073
|
window.addEventListener("message", handleDeactivate);
|
|
@@ -5719,6 +7076,10 @@ function OhhwellsBridge() {
|
|
|
5719
7076
|
setLinkPopoverRef.current(null);
|
|
5720
7077
|
return;
|
|
5721
7078
|
}
|
|
7079
|
+
if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
|
|
7080
|
+
deselectRef.current();
|
|
7081
|
+
return;
|
|
7082
|
+
}
|
|
5722
7083
|
if (e.key !== "Escape") return;
|
|
5723
7084
|
const el = activeElRef.current;
|
|
5724
7085
|
if (el && originalContentRef.current !== null) {
|
|
@@ -5728,13 +7089,16 @@ function OhhwellsBridge() {
|
|
|
5728
7089
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
|
|
5729
7090
|
}
|
|
5730
7091
|
}
|
|
7092
|
+
deselectRef.current();
|
|
5731
7093
|
deactivateRef.current();
|
|
5732
7094
|
};
|
|
5733
7095
|
const handleScroll = () => {
|
|
5734
|
-
|
|
5735
|
-
|
|
7096
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7097
|
+
if (focusEl) {
|
|
7098
|
+
const r2 = focusEl.getBoundingClientRect();
|
|
5736
7099
|
applyToolbarPos(r2);
|
|
5737
|
-
|
|
7100
|
+
setToolbarRect(r2);
|
|
7101
|
+
setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
|
|
5738
7102
|
}
|
|
5739
7103
|
if (activeStateElRef.current) {
|
|
5740
7104
|
const rect = activeStateElRef.current.getBoundingClientRect();
|
|
@@ -5753,7 +7117,63 @@ function OhhwellsBridge() {
|
|
|
5753
7117
|
};
|
|
5754
7118
|
const handleSave = (e) => {
|
|
5755
7119
|
if (e.data?.type !== "ow:save") return;
|
|
5756
|
-
|
|
7120
|
+
const nodes = collectEditableNodes();
|
|
7121
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
7122
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
7123
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
7124
|
+
};
|
|
7125
|
+
const handleInsertSection = (e) => {
|
|
7126
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
7127
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
7128
|
+
if (widgetType !== "scheduling") return;
|
|
7129
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
7130
|
+
if (inserted) {
|
|
7131
|
+
const tracker = getSectionsTracker();
|
|
7132
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
7133
|
+
const h = document.documentElement.scrollHeight;
|
|
7134
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
7135
|
+
}
|
|
7136
|
+
};
|
|
7137
|
+
const handleSwitchSchedule = (e) => {
|
|
7138
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
7139
|
+
const scheduleId = e.data.scheduleId;
|
|
7140
|
+
const insertAfter = e.data.insertAfter;
|
|
7141
|
+
if (!scheduleId || !insertAfter) return;
|
|
7142
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7143
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7144
|
+
};
|
|
7145
|
+
const handleScheduleLinked = (e) => {
|
|
7146
|
+
if (e.data?.type !== "ow:schedule-linked") return;
|
|
7147
|
+
const scheduleId = e.data.scheduleId;
|
|
7148
|
+
const insertAfter = e.data.insertAfter;
|
|
7149
|
+
if (!scheduleId || !insertAfter) return;
|
|
7150
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7151
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7152
|
+
};
|
|
7153
|
+
const handleClearSchedulingWidget = (e) => {
|
|
7154
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
7155
|
+
const insertAfter = e.data.insertAfter;
|
|
7156
|
+
if (!insertAfter) return;
|
|
7157
|
+
const text = updateSectionScheduleId(insertAfter, null);
|
|
7158
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7159
|
+
};
|
|
7160
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
7161
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
7162
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
7163
|
+
el.remove();
|
|
7164
|
+
});
|
|
7165
|
+
const tracker = getSectionsTracker();
|
|
7166
|
+
let sections = [];
|
|
7167
|
+
try {
|
|
7168
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
7169
|
+
} catch {
|
|
7170
|
+
}
|
|
7171
|
+
const currentPath = window.location.pathname;
|
|
7172
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
7173
|
+
tracker.textContent = JSON.stringify(updated);
|
|
7174
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
7175
|
+
const h = document.documentElement.scrollHeight;
|
|
7176
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5757
7177
|
};
|
|
5758
7178
|
const handleSelectionChange = () => {
|
|
5759
7179
|
if (!activeElRef.current) return;
|
|
@@ -5803,8 +7223,9 @@ function OhhwellsBridge() {
|
|
|
5803
7223
|
};
|
|
5804
7224
|
const applyToolbarPos = (rect) => {
|
|
5805
7225
|
const ps = parentScrollRef.current;
|
|
7226
|
+
const approxW = toolbarVariantRef.current === "link-action" ? 112 : 306;
|
|
5806
7227
|
if (toolbarElRef.current) {
|
|
5807
|
-
const { top, left, transform } = calcToolbarPos(rect, ps);
|
|
7228
|
+
const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
|
|
5808
7229
|
toolbarElRef.current.style.top = `${top}px`;
|
|
5809
7230
|
toolbarElRef.current.style.left = `${left}px`;
|
|
5810
7231
|
toolbarElRef.current.style.transform = transform;
|
|
@@ -5819,7 +7240,11 @@ function OhhwellsBridge() {
|
|
|
5819
7240
|
if (e.data?.type !== "ow:parent-scroll") return;
|
|
5820
7241
|
const { iframeOffsetTop, headerH, canvasH } = e.data;
|
|
5821
7242
|
parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
|
|
5822
|
-
if (
|
|
7243
|
+
if (visibleViewportRef.current) {
|
|
7244
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7245
|
+
}
|
|
7246
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7247
|
+
if (focusEl) applyToolbarPos(focusEl.getBoundingClientRect());
|
|
5823
7248
|
};
|
|
5824
7249
|
const handleClickAt = (e) => {
|
|
5825
7250
|
if (e.data?.type !== "ow:click-at") return;
|
|
@@ -5858,12 +7283,23 @@ function OhhwellsBridge() {
|
|
|
5858
7283
|
deactivateRef.current();
|
|
5859
7284
|
};
|
|
5860
7285
|
window.addEventListener("message", handleSave);
|
|
7286
|
+
window.addEventListener("message", handleInsertSection);
|
|
7287
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
7288
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
7289
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
7290
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
5861
7291
|
window.addEventListener("message", handleImageUrl);
|
|
5862
7292
|
window.addEventListener("message", handleAnimLock);
|
|
5863
7293
|
window.addEventListener("message", handleCanvasHeight);
|
|
5864
7294
|
window.addEventListener("message", handleParentScroll);
|
|
5865
7295
|
window.addEventListener("message", handlePointerSync);
|
|
5866
7296
|
window.addEventListener("message", handleClickAt);
|
|
7297
|
+
const handleViewportResize = () => {
|
|
7298
|
+
if (visibleViewportRef.current) {
|
|
7299
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7300
|
+
}
|
|
7301
|
+
};
|
|
7302
|
+
window.addEventListener("resize", handleViewportResize, { passive: true });
|
|
5867
7303
|
document.addEventListener("click", handleClick, true);
|
|
5868
7304
|
document.addEventListener("paste", handlePaste, true);
|
|
5869
7305
|
document.addEventListener("input", handleInput, true);
|
|
@@ -5892,10 +7328,16 @@ function OhhwellsBridge() {
|
|
|
5892
7328
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
5893
7329
|
window.removeEventListener("scroll", handleScroll, true);
|
|
5894
7330
|
window.removeEventListener("message", handleSave);
|
|
7331
|
+
window.removeEventListener("message", handleInsertSection);
|
|
7332
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
7333
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
7334
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
7335
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
5895
7336
|
window.removeEventListener("message", handleImageUrl);
|
|
5896
7337
|
window.removeEventListener("message", handleAnimLock);
|
|
5897
7338
|
window.removeEventListener("message", handleCanvasHeight);
|
|
5898
7339
|
window.removeEventListener("message", handleParentScroll);
|
|
7340
|
+
window.removeEventListener("resize", handleViewportResize);
|
|
5899
7341
|
window.removeEventListener("message", handlePointerSync);
|
|
5900
7342
|
window.removeEventListener("message", handleClickAt);
|
|
5901
7343
|
window.removeEventListener("message", handleHydrate);
|
|
@@ -5906,7 +7348,23 @@ function OhhwellsBridge() {
|
|
|
5906
7348
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5907
7349
|
};
|
|
5908
7350
|
}, [isEditMode, refreshStateRules]);
|
|
5909
|
-
|
|
7351
|
+
useEffect3(() => {
|
|
7352
|
+
const handler = (e) => {
|
|
7353
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7354
|
+
const insertAfterVal = e.data.insertAfter;
|
|
7355
|
+
if (!insertAfterVal) return;
|
|
7356
|
+
if (!sectionsLoadedRef.current) {
|
|
7357
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
7358
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
7359
|
+
}
|
|
7360
|
+
return;
|
|
7361
|
+
}
|
|
7362
|
+
processConfigRequest(insertAfterVal);
|
|
7363
|
+
};
|
|
7364
|
+
window.addEventListener("message", handler);
|
|
7365
|
+
return () => window.removeEventListener("message", handler);
|
|
7366
|
+
}, [processConfigRequest]);
|
|
7367
|
+
useEffect3(() => {
|
|
5910
7368
|
if (!isEditMode) return;
|
|
5911
7369
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5912
7370
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5935,7 +7393,7 @@ function OhhwellsBridge() {
|
|
|
5935
7393
|
clearTimeout(timer);
|
|
5936
7394
|
};
|
|
5937
7395
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5938
|
-
|
|
7396
|
+
useEffect3(() => {
|
|
5939
7397
|
scrollToHashSectionWhenReady();
|
|
5940
7398
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
5941
7399
|
window.addEventListener("hashchange", onHashChange);
|
|
@@ -5967,10 +7425,22 @@ function OhhwellsBridge() {
|
|
|
5967
7425
|
bumpLinkPopoverGrace();
|
|
5968
7426
|
setLinkPopover({
|
|
5969
7427
|
key: hrefCtx.key,
|
|
5970
|
-
target: getLinkHref(hrefCtx.anchor)
|
|
5971
|
-
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
7428
|
+
target: getLinkHref(hrefCtx.anchor)
|
|
5972
7429
|
});
|
|
5973
|
-
|
|
7430
|
+
deactivate();
|
|
7431
|
+
}, [deactivate]);
|
|
7432
|
+
const openLinkPopoverForSelected = useCallback2(() => {
|
|
7433
|
+
const anchor = selectedElRef.current;
|
|
7434
|
+
if (!anchor) return;
|
|
7435
|
+
const key = anchor.getAttribute("data-ohw-href-key");
|
|
7436
|
+
if (!key) return;
|
|
7437
|
+
bumpLinkPopoverGrace();
|
|
7438
|
+
setLinkPopover({
|
|
7439
|
+
key,
|
|
7440
|
+
target: getLinkHref(anchor)
|
|
7441
|
+
});
|
|
7442
|
+
deselect();
|
|
7443
|
+
}, [deselect]);
|
|
5974
7444
|
const handleLinkPopoverSubmit = useCallback2(
|
|
5975
7445
|
(target) => {
|
|
5976
7446
|
if (!linkPopover) return;
|
|
@@ -5985,12 +7455,14 @@ function OhhwellsBridge() {
|
|
|
5985
7455
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
5986
7456
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
5987
7457
|
return bridgeRoot ? createPortal(
|
|
5988
|
-
/* @__PURE__ */
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
/* @__PURE__ */
|
|
7458
|
+
/* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
7459
|
+
/* @__PURE__ */ jsx16("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
7460
|
+
toolbarRect && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
7461
|
+
/* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
7462
|
+
toolbarVariant === "rich-text" && /* @__PURE__ */ jsx16(
|
|
5992
7463
|
FloatingToolbar,
|
|
5993
7464
|
{
|
|
7465
|
+
variant: "rich-text",
|
|
5994
7466
|
rect: toolbarRect,
|
|
5995
7467
|
parentScroll: parentScrollRef.current,
|
|
5996
7468
|
elRef: toolbarElRef,
|
|
@@ -5999,9 +7471,21 @@ function OhhwellsBridge() {
|
|
|
5999
7471
|
showEditLink,
|
|
6000
7472
|
onEditLink: openLinkPopoverForActive
|
|
6001
7473
|
}
|
|
7474
|
+
),
|
|
7475
|
+
toolbarVariant === "link-action" && /* @__PURE__ */ jsx16(
|
|
7476
|
+
FloatingToolbar,
|
|
7477
|
+
{
|
|
7478
|
+
variant: "link-action",
|
|
7479
|
+
rect: toolbarRect,
|
|
7480
|
+
parentScroll: parentScrollRef.current,
|
|
7481
|
+
elRef: toolbarElRef,
|
|
7482
|
+
onCommand: handleCommand,
|
|
7483
|
+
activeCommands,
|
|
7484
|
+
onEditLink: openLinkPopoverForSelected
|
|
7485
|
+
}
|
|
6002
7486
|
)
|
|
6003
7487
|
] }),
|
|
6004
|
-
maxBadge && /* @__PURE__ */
|
|
7488
|
+
maxBadge && /* @__PURE__ */ jsxs9(
|
|
6005
7489
|
"div",
|
|
6006
7490
|
{
|
|
6007
7491
|
"data-ohw-max-badge": "",
|
|
@@ -6027,7 +7511,7 @@ function OhhwellsBridge() {
|
|
|
6027
7511
|
]
|
|
6028
7512
|
}
|
|
6029
7513
|
),
|
|
6030
|
-
toggleState && /* @__PURE__ */
|
|
7514
|
+
toggleState && /* @__PURE__ */ jsx16(
|
|
6031
7515
|
StateToggle,
|
|
6032
7516
|
{
|
|
6033
7517
|
rect: toggleState.rect,
|
|
@@ -6036,12 +7520,36 @@ function OhhwellsBridge() {
|
|
|
6036
7520
|
onStateChange: handleStateChange
|
|
6037
7521
|
}
|
|
6038
7522
|
),
|
|
6039
|
-
|
|
7523
|
+
sectionGap && /* @__PURE__ */ jsxs9(
|
|
7524
|
+
"div",
|
|
7525
|
+
{
|
|
7526
|
+
"data-ohw-section-insert-line": "",
|
|
7527
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7528
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7529
|
+
children: [
|
|
7530
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7531
|
+
/* @__PURE__ */ jsx16(
|
|
7532
|
+
Badge,
|
|
7533
|
+
{
|
|
7534
|
+
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",
|
|
7535
|
+
onClick: () => {
|
|
7536
|
+
window.parent.postMessage(
|
|
7537
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7538
|
+
"*"
|
|
7539
|
+
);
|
|
7540
|
+
},
|
|
7541
|
+
children: "Add Section"
|
|
7542
|
+
}
|
|
7543
|
+
),
|
|
7544
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7545
|
+
]
|
|
7546
|
+
}
|
|
7547
|
+
),
|
|
7548
|
+
linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx16(
|
|
6040
7549
|
LinkPopover,
|
|
6041
7550
|
{
|
|
6042
|
-
rect: linkPopover.rect,
|
|
6043
|
-
parentScroll: parentScrollRef.current,
|
|
6044
7551
|
panelRef: linkPopoverPanelRef,
|
|
7552
|
+
portalContainer: dialogPortalContainer,
|
|
6045
7553
|
open: true,
|
|
6046
7554
|
mode: "edit",
|
|
6047
7555
|
pages: sitePages,
|
|
@@ -6050,8 +7558,34 @@ function OhhwellsBridge() {
|
|
|
6050
7558
|
initialTarget: linkPopover.target,
|
|
6051
7559
|
onClose: closeLinkPopover,
|
|
6052
7560
|
onSubmit: handleLinkPopoverSubmit
|
|
7561
|
+
},
|
|
7562
|
+
`${linkPopover.key}-${pathname}`
|
|
7563
|
+
) : null,
|
|
7564
|
+
sectionGap && /* @__PURE__ */ jsxs9(
|
|
7565
|
+
"div",
|
|
7566
|
+
{
|
|
7567
|
+
"data-ohw-section-insert-line": "",
|
|
7568
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7569
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7570
|
+
children: [
|
|
7571
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7572
|
+
/* @__PURE__ */ jsx16(
|
|
7573
|
+
Badge,
|
|
7574
|
+
{
|
|
7575
|
+
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",
|
|
7576
|
+
onClick: () => {
|
|
7577
|
+
window.parent.postMessage(
|
|
7578
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7579
|
+
"*"
|
|
7580
|
+
);
|
|
7581
|
+
},
|
|
7582
|
+
children: "Add Section"
|
|
7583
|
+
}
|
|
7584
|
+
),
|
|
7585
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7586
|
+
]
|
|
6053
7587
|
}
|
|
6054
|
-
)
|
|
7588
|
+
)
|
|
6055
7589
|
] }),
|
|
6056
7590
|
bridgeRoot
|
|
6057
7591
|
) : null;
|
|
@@ -6060,6 +7594,7 @@ export {
|
|
|
6060
7594
|
LinkEditorPanel,
|
|
6061
7595
|
LinkPopover,
|
|
6062
7596
|
OhhwellsBridge,
|
|
7597
|
+
SchedulingWidget,
|
|
6063
7598
|
Toggle,
|
|
6064
7599
|
ToggleGroup,
|
|
6065
7600
|
ToggleGroupItem,
|