@ohhwells/bridge 0.1.29 → 0.1.31
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 +1834 -325
- 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 +1807 -299
- package/dist/index.js.map +1 -1
- package/dist/styles.css +505 -14
- package/package.json +48 -47
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
LinkEditorPanel: () => LinkEditorPanel,
|
|
35
35
|
LinkPopover: () => LinkPopover,
|
|
36
36
|
OhhwellsBridge: () => OhhwellsBridge,
|
|
37
|
+
SchedulingWidget: () => SchedulingWidget,
|
|
37
38
|
Toggle: () => Toggle,
|
|
38
39
|
ToggleGroup: () => ToggleGroup,
|
|
39
40
|
ToggleGroupItem: () => ToggleGroupItem,
|
|
@@ -48,10 +49,762 @@ __export(index_exports, {
|
|
|
48
49
|
module.exports = __toCommonJS(index_exports);
|
|
49
50
|
|
|
50
51
|
// src/OhhwellsBridge.tsx
|
|
51
|
-
var
|
|
52
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
53
|
+
var import_client = require("react-dom/client");
|
|
54
|
+
var import_react_dom = require("react-dom");
|
|
55
|
+
|
|
56
|
+
// src/ui/SchedulingWidget.tsx
|
|
57
|
+
var import_react2 = require("react");
|
|
58
|
+
|
|
59
|
+
// src/ui/EmailCaptureModal.tsx
|
|
60
|
+
var import_react = require("react");
|
|
61
|
+
var import_radix_ui = require("radix-ui");
|
|
62
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
63
|
+
function Spinner() {
|
|
64
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
65
|
+
"svg",
|
|
66
|
+
{
|
|
67
|
+
width: "32",
|
|
68
|
+
height: "32",
|
|
69
|
+
viewBox: "0 0 32 32",
|
|
70
|
+
fill: "none",
|
|
71
|
+
style: { animation: "ohw-spin 0.75s linear infinite" },
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
|
|
74
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
75
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
76
|
+
"path",
|
|
77
|
+
{
|
|
78
|
+
d: "M16 4a12 12 0 0 1 12 12",
|
|
79
|
+
stroke: "#3b82f6",
|
|
80
|
+
strokeWidth: "3",
|
|
81
|
+
strokeLinecap: "round"
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
89
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
90
|
+
const [submittedEmail, setSubmittedEmail] = (0, import_react.useState)("");
|
|
91
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
92
|
+
const [success, setSuccess] = (0, import_react.useState)(false);
|
|
93
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
94
|
+
const isBook = title === "Confirm your spot";
|
|
95
|
+
const handleSubmit = async () => {
|
|
96
|
+
if (!email.trim()) return;
|
|
97
|
+
setLoading(true);
|
|
98
|
+
setError(null);
|
|
99
|
+
try {
|
|
100
|
+
await onSubmit(email.trim());
|
|
101
|
+
setSubmittedEmail(email.trim());
|
|
102
|
+
setSuccess(true);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
105
|
+
} finally {
|
|
106
|
+
setLoading(false);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const handleKeyDown = (e) => {
|
|
110
|
+
if (e.key === "Enter" && !loading) handleSubmit();
|
|
111
|
+
};
|
|
112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Dialog.Root, { open: true, onOpenChange: (open) => {
|
|
113
|
+
if (!open) onClose();
|
|
114
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_radix_ui.Dialog.Portal, { children: [
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
116
|
+
import_radix_ui.Dialog.Overlay,
|
|
117
|
+
{
|
|
118
|
+
className: "fixed inset-0 z-50",
|
|
119
|
+
style: { background: "rgba(0,0,0,0.45)" }
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
123
|
+
import_radix_ui.Dialog.Content,
|
|
124
|
+
{
|
|
125
|
+
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",
|
|
126
|
+
style: { maxWidth: 400 },
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3 p-6", children: [
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
130
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
131
|
+
import_radix_ui.Dialog.Title,
|
|
132
|
+
{
|
|
133
|
+
className: "font-body text-xl font-bold m-0 leading-tight",
|
|
134
|
+
style: { color: "var(--color-dark,#200C02)" },
|
|
135
|
+
children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
139
|
+
import_radix_ui.Dialog.Description,
|
|
140
|
+
{
|
|
141
|
+
className: "font-body text-sm m-0",
|
|
142
|
+
style: { color: "#6B7280" },
|
|
143
|
+
children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] }),
|
|
147
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
148
|
+
import_radix_ui.Dialog.Close,
|
|
149
|
+
{
|
|
150
|
+
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",
|
|
151
|
+
style: { color: "#6B7280" },
|
|
152
|
+
"aria-label": "Close",
|
|
153
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
156
|
+
] })
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] }),
|
|
160
|
+
!success && (loading ? (
|
|
161
|
+
/* Loading state — full body replaced */
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
|
|
165
|
+
] })
|
|
166
|
+
) : (
|
|
167
|
+
/* Email form */
|
|
168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-7 pt-2 pb-6", children: [
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 mb-8", children: [
|
|
170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
172
|
+
"input",
|
|
173
|
+
{
|
|
174
|
+
type: "email",
|
|
175
|
+
value: email,
|
|
176
|
+
onChange: (e) => setEmail(e.target.value),
|
|
177
|
+
onKeyDown: handleKeyDown,
|
|
178
|
+
placeholder: "hello@gmail.com",
|
|
179
|
+
autoFocus: true,
|
|
180
|
+
className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
|
|
181
|
+
style: {
|
|
182
|
+
borderColor: "#E7E5E4",
|
|
183
|
+
background: "#fff",
|
|
184
|
+
color: "var(--color-dark,#200C02)"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-xs text-red-500 m-0", children: error })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
191
|
+
"button",
|
|
192
|
+
{
|
|
193
|
+
onClick: handleSubmit,
|
|
194
|
+
disabled: !email.trim(),
|
|
195
|
+
className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
|
|
196
|
+
style: {
|
|
197
|
+
background: "var(--color-primary,#3D312B)",
|
|
198
|
+
color: "var(--color-light,#FEFFF0)"
|
|
199
|
+
},
|
|
200
|
+
children: "Send"
|
|
201
|
+
}
|
|
202
|
+
) })
|
|
203
|
+
] })
|
|
204
|
+
))
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] }) });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/ui/SchedulingWidget.tsx
|
|
212
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
213
|
+
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
214
|
+
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
215
|
+
function classRunsOnDate(cls, date, type) {
|
|
216
|
+
if (type === "FIXED") {
|
|
217
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
218
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
219
|
+
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
220
|
+
}
|
|
221
|
+
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
222
|
+
}
|
|
223
|
+
function formatClassTime(cls) {
|
|
224
|
+
let h = parseInt(cls.startTime.hour, 10);
|
|
225
|
+
const m = parseInt(cls.startTime.minutes, 10);
|
|
226
|
+
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
227
|
+
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
228
|
+
const endTotal = h * 60 + m + cls.duration;
|
|
229
|
+
const eh = Math.floor(endTotal / 60) % 24;
|
|
230
|
+
const em = endTotal % 60;
|
|
231
|
+
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
232
|
+
}
|
|
233
|
+
function getBookingsOnDate(cls, date) {
|
|
234
|
+
if (!cls.bookings?.length) return 0;
|
|
235
|
+
const ds = date.toISOString().split("T")[0];
|
|
236
|
+
return cls.bookings.filter((b) => {
|
|
237
|
+
try {
|
|
238
|
+
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}).length;
|
|
243
|
+
}
|
|
244
|
+
function buildTimezoneLabel(tz) {
|
|
245
|
+
try {
|
|
246
|
+
const now = /* @__PURE__ */ new Date();
|
|
247
|
+
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
248
|
+
hour: "2-digit",
|
|
249
|
+
minute: "2-digit",
|
|
250
|
+
hour12: false,
|
|
251
|
+
timeZone: tz
|
|
252
|
+
}).format(now);
|
|
253
|
+
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
254
|
+
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
255
|
+
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
256
|
+
} catch {
|
|
257
|
+
return tz;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function EmptyState({ inEditor, insertAfter }) {
|
|
261
|
+
const handleAddSchedule = () => {
|
|
262
|
+
if (inEditor) {
|
|
263
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
268
|
+
"svg",
|
|
269
|
+
{
|
|
270
|
+
width: "20",
|
|
271
|
+
height: "20",
|
|
272
|
+
viewBox: "0 0 24 24",
|
|
273
|
+
fill: "none",
|
|
274
|
+
stroke: "var(--color-accent, #A89B83)",
|
|
275
|
+
strokeWidth: "1.5",
|
|
276
|
+
strokeLinecap: "round",
|
|
277
|
+
strokeLinejoin: "round",
|
|
278
|
+
children: [
|
|
279
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
280
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
283
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
284
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
) }),
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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." })
|
|
291
|
+
] }),
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
293
|
+
"button",
|
|
294
|
+
{
|
|
295
|
+
onClick: handleAddSchedule,
|
|
296
|
+
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",
|
|
297
|
+
children: "Add Schedule"
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
] });
|
|
301
|
+
}
|
|
302
|
+
function LoadingSkeleton() {
|
|
303
|
+
const shimmer = {
|
|
304
|
+
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
305
|
+
backgroundSize: "200% 100%",
|
|
306
|
+
animation: "ohw-shimmer 1.5s infinite"
|
|
307
|
+
};
|
|
308
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
309
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
310
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
|
|
311
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
312
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
313
|
+
] }, i)) }),
|
|
314
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
315
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:hidden", children: [
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0", children: [
|
|
317
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
|
|
318
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
|
|
319
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
|
|
320
|
+
] }),
|
|
321
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
322
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
|
|
323
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
|
|
324
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
|
|
325
|
+
] })
|
|
326
|
+
] }),
|
|
327
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
|
|
328
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
330
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
332
|
+
] }),
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
335
|
+
] }),
|
|
336
|
+
i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
337
|
+
] }, i))
|
|
338
|
+
] });
|
|
339
|
+
}
|
|
340
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
341
|
+
const todayMs = (0, import_react2.useMemo)(() => {
|
|
342
|
+
const d = /* @__PURE__ */ new Date();
|
|
343
|
+
d.setHours(0, 0, 0, 0);
|
|
344
|
+
return d.getTime();
|
|
345
|
+
}, []);
|
|
346
|
+
const scrollRef = (0, import_react2.useRef)(null);
|
|
347
|
+
const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
|
|
348
|
+
const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
|
|
349
|
+
const updateScrollState = () => {
|
|
350
|
+
const el = scrollRef.current;
|
|
351
|
+
if (!el) return;
|
|
352
|
+
setCanScrollLeft(el.scrollLeft > 0);
|
|
353
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
354
|
+
};
|
|
355
|
+
(0, import_react2.useEffect)(() => {
|
|
356
|
+
const el = scrollRef.current;
|
|
357
|
+
if (!el) return;
|
|
358
|
+
updateScrollState();
|
|
359
|
+
el.addEventListener("scroll", updateScrollState);
|
|
360
|
+
const ro = new ResizeObserver(updateScrollState);
|
|
361
|
+
ro.observe(el);
|
|
362
|
+
return () => {
|
|
363
|
+
el.removeEventListener("scroll", updateScrollState);
|
|
364
|
+
ro.disconnect();
|
|
365
|
+
};
|
|
366
|
+
}, [dates]);
|
|
367
|
+
const scroll = (dir) => {
|
|
368
|
+
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
369
|
+
};
|
|
370
|
+
const isDragging = (0, import_react2.useRef)(false);
|
|
371
|
+
const dragStartX = (0, import_react2.useRef)(0);
|
|
372
|
+
const dragStartScrollLeft = (0, import_react2.useRef)(0);
|
|
373
|
+
const onDragStart = (e) => {
|
|
374
|
+
if (!scrollRef.current) return;
|
|
375
|
+
isDragging.current = true;
|
|
376
|
+
dragStartX.current = e.clientX;
|
|
377
|
+
dragStartScrollLeft.current = scrollRef.current.scrollLeft;
|
|
378
|
+
scrollRef.current.style.cursor = "grabbing";
|
|
379
|
+
scrollRef.current.style.userSelect = "none";
|
|
380
|
+
};
|
|
381
|
+
const onDragMove = (e) => {
|
|
382
|
+
if (!isDragging.current || !scrollRef.current) return;
|
|
383
|
+
const delta = e.clientX - dragStartX.current;
|
|
384
|
+
scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
|
|
385
|
+
};
|
|
386
|
+
const onDragEnd = () => {
|
|
387
|
+
if (!scrollRef.current) return;
|
|
388
|
+
isDragging.current = false;
|
|
389
|
+
scrollRef.current.style.cursor = "grab";
|
|
390
|
+
scrollRef.current.style.userSelect = "";
|
|
391
|
+
};
|
|
392
|
+
const datesWithClasses = (0, import_react2.useMemo)(
|
|
393
|
+
() => new Set(dates.map(
|
|
394
|
+
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
395
|
+
).filter((i) => i >= 0)),
|
|
396
|
+
[dates, schedule]
|
|
397
|
+
);
|
|
398
|
+
const selectedDate = dates[selectedIdx];
|
|
399
|
+
const selectedClasses = (0, import_react2.useMemo)(
|
|
400
|
+
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
401
|
+
[schedule, selectedDate]
|
|
402
|
+
);
|
|
403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
404
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full", children: [
|
|
405
|
+
canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
onClick: () => scroll("left"),
|
|
409
|
+
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",
|
|
410
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
411
|
+
"aria-label": "Scroll left",
|
|
412
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "15 18 9 12 15 6" }) })
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
416
|
+
"button",
|
|
417
|
+
{
|
|
418
|
+
onClick: () => scroll("right"),
|
|
419
|
+
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",
|
|
420
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
421
|
+
"aria-label": "Scroll right",
|
|
422
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "9 18 15 12 9 6" }) })
|
|
423
|
+
}
|
|
424
|
+
),
|
|
425
|
+
canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
426
|
+
"div",
|
|
427
|
+
{
|
|
428
|
+
className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
429
|
+
style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
|
|
430
|
+
}
|
|
431
|
+
),
|
|
432
|
+
canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
433
|
+
"div",
|
|
434
|
+
{
|
|
435
|
+
className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
436
|
+
style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
|
|
437
|
+
}
|
|
438
|
+
),
|
|
439
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
440
|
+
"div",
|
|
441
|
+
{
|
|
442
|
+
ref: scrollRef,
|
|
443
|
+
className: "overflow-x-auto w-full",
|
|
444
|
+
style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
|
|
445
|
+
onMouseDown: onDragStart,
|
|
446
|
+
onMouseMove: onDragMove,
|
|
447
|
+
onMouseUp: onDragEnd,
|
|
448
|
+
onMouseLeave: onDragEnd,
|
|
449
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
450
|
+
const isToday = date.getTime() === todayMs;
|
|
451
|
+
const isSelected = i === selectedIdx;
|
|
452
|
+
const clickable = datesWithClasses.has(i);
|
|
453
|
+
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
454
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
455
|
+
"div",
|
|
456
|
+
{
|
|
457
|
+
onClick: () => clickable && onSelectDate(i),
|
|
458
|
+
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
459
|
+
children: [
|
|
460
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
461
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
462
|
+
"div",
|
|
463
|
+
{
|
|
464
|
+
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
465
|
+
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
466
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
467
|
+
"span",
|
|
468
|
+
{
|
|
469
|
+
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
470
|
+
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
471
|
+
children: date.getDate()
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
]
|
|
477
|
+
},
|
|
478
|
+
i
|
|
479
|
+
);
|
|
480
|
+
}) })
|
|
481
|
+
}
|
|
482
|
+
)
|
|
483
|
+
] }),
|
|
484
|
+
selectedClasses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
|
|
485
|
+
const booked = getBookingsOnDate(cls, selectedDate);
|
|
486
|
+
const available = cls.maxParticipants - booked;
|
|
487
|
+
const isFull = available <= 0;
|
|
488
|
+
const isPrivate = cls.maxParticipants === 1;
|
|
489
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
491
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
492
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 sm:w-[120px] sm:shrink-0", children: [
|
|
493
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }),
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
495
|
+
"span",
|
|
496
|
+
{
|
|
497
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium w-fit",
|
|
498
|
+
style: isPrivate ? { background: "#DDFBE3", color: "#199130", border: "1px solid #199130", fontWeight: 500 } : { background: "#EEEDFF", color: "#5953FF", border: "1px solid #5953FF", fontWeight: 500 },
|
|
499
|
+
children: isPrivate ? "PRIVATE" : "GROUP"
|
|
500
|
+
}
|
|
501
|
+
)
|
|
502
|
+
] }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
504
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
505
|
+
available,
|
|
506
|
+
"/",
|
|
507
|
+
cls.maxParticipants
|
|
508
|
+
] }),
|
|
509
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
510
|
+
] }) })
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
513
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
514
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
515
|
+
cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
516
|
+
] }),
|
|
517
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
518
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
519
|
+
available,
|
|
520
|
+
"/",
|
|
521
|
+
cls.maxParticipants
|
|
522
|
+
] }),
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
524
|
+
] }) }),
|
|
525
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
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",
|
|
529
|
+
style: isFull ? {
|
|
530
|
+
background: "transparent",
|
|
531
|
+
border: "1px solid var(--color-dark, #200C02)",
|
|
532
|
+
color: "var(--color-dark, #200C02)"
|
|
533
|
+
} : {
|
|
534
|
+
background: "var(--color-primary, #3D312B)",
|
|
535
|
+
border: "none",
|
|
536
|
+
color: "var(--color-light, #FEFFF0)"
|
|
537
|
+
},
|
|
538
|
+
onClick: () => {
|
|
539
|
+
if (!cls.id) return;
|
|
540
|
+
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
541
|
+
},
|
|
542
|
+
children: isFull ? "Join Waitlist" : "Book Now"
|
|
543
|
+
}
|
|
544
|
+
)
|
|
545
|
+
] })
|
|
546
|
+
] }),
|
|
547
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
548
|
+
] }, cls.id ?? i);
|
|
549
|
+
}) })
|
|
550
|
+
] });
|
|
551
|
+
}
|
|
552
|
+
function CalendarFoldIcon() {
|
|
553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
554
|
+
"svg",
|
|
555
|
+
{
|
|
556
|
+
width: "16",
|
|
557
|
+
height: "16",
|
|
558
|
+
viewBox: "0 0 24 24",
|
|
559
|
+
fill: "none",
|
|
560
|
+
stroke: "currentColor",
|
|
561
|
+
strokeWidth: "2",
|
|
562
|
+
strokeLinecap: "round",
|
|
563
|
+
strokeLinejoin: "round",
|
|
564
|
+
style: { flexShrink: 0 },
|
|
565
|
+
children: [
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v4" }),
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 2v4" }),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 10h18" }),
|
|
570
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
|
|
576
|
+
const autoId = (0, import_react2.useId)();
|
|
577
|
+
const insertAfter = insertAfterProp ?? autoId;
|
|
578
|
+
const [schedule, setSchedule] = (0, import_react2.useState)(null);
|
|
579
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
580
|
+
const [inEditor, setInEditor] = (0, import_react2.useState)(false);
|
|
581
|
+
const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
|
|
582
|
+
const [modalState, setModalState] = (0, import_react2.useState)(null);
|
|
583
|
+
const switchScheduleIdRef = (0, import_react2.useRef)(null);
|
|
584
|
+
const dates = (0, import_react2.useMemo)(() => {
|
|
585
|
+
const today = /* @__PURE__ */ new Date();
|
|
586
|
+
today.setHours(0, 0, 0, 0);
|
|
587
|
+
return Array.from({ length: 14 }, (_, i) => {
|
|
588
|
+
const d = new Date(today);
|
|
589
|
+
d.setDate(today.getDate() + i);
|
|
590
|
+
return d;
|
|
591
|
+
});
|
|
592
|
+
}, []);
|
|
593
|
+
const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
|
|
594
|
+
(0, import_react2.useEffect)(() => {
|
|
595
|
+
if (!schedule?.classes) return;
|
|
596
|
+
for (let i = 0; i < dates.length; i++) {
|
|
597
|
+
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
598
|
+
setSelectedIdx(i);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}, [schedule, dates]);
|
|
603
|
+
(0, import_react2.useEffect)(() => {
|
|
604
|
+
if (typeof window === "undefined") return;
|
|
605
|
+
const isInEditor = window.self !== window.top;
|
|
606
|
+
setInEditor(isInEditor);
|
|
607
|
+
function loadWithId(effectiveId) {
|
|
608
|
+
if (isInEditor) {
|
|
609
|
+
const startEmpty = effectiveId === null;
|
|
610
|
+
if (startEmpty) setLoading(false);
|
|
611
|
+
let initialized = startEmpty;
|
|
612
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
613
|
+
if (!initialized) {
|
|
614
|
+
initialized = true;
|
|
615
|
+
setLoading(false);
|
|
616
|
+
}
|
|
617
|
+
}, 5e3) : null;
|
|
618
|
+
const handler = (e) => {
|
|
619
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
620
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
621
|
+
setSchedule(null);
|
|
622
|
+
setLoading(false);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
626
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
627
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
628
|
+
initialized = false;
|
|
629
|
+
setLoading(true);
|
|
630
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
634
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
635
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
636
|
+
initialized = true;
|
|
637
|
+
if (timer) clearTimeout(timer);
|
|
638
|
+
const schedules = e.data.schedules ?? [];
|
|
639
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
640
|
+
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;
|
|
641
|
+
switchScheduleIdRef.current = null;
|
|
642
|
+
setSchedule(target);
|
|
643
|
+
setLoading(false);
|
|
644
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
645
|
+
window.parent.postMessage({
|
|
646
|
+
type: "ow:schedule-connected",
|
|
647
|
+
schedule: { id: target.id, name: target.name },
|
|
648
|
+
insertAfter
|
|
649
|
+
}, "*");
|
|
650
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
window.addEventListener("message", handler);
|
|
654
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
655
|
+
return () => {
|
|
656
|
+
window.removeEventListener("message", handler);
|
|
657
|
+
if (timer) clearTimeout(timer);
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (!effectiveId) {
|
|
661
|
+
setLoading(false);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
;
|
|
665
|
+
(async () => {
|
|
666
|
+
try {
|
|
667
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
668
|
+
const data = await res.json();
|
|
669
|
+
setSchedule(data?.id ? data : null);
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
setLoading(false);
|
|
673
|
+
})();
|
|
674
|
+
}
|
|
675
|
+
if (initialScheduleId === void 0) {
|
|
676
|
+
let done = false;
|
|
677
|
+
let innerCleanup;
|
|
678
|
+
const timer = setTimeout(() => {
|
|
679
|
+
if (!done) {
|
|
680
|
+
done = true;
|
|
681
|
+
setLoading(false);
|
|
682
|
+
}
|
|
683
|
+
}, 2e3);
|
|
684
|
+
const configHandler = (e) => {
|
|
685
|
+
if (e.data?.type !== "ow:schedule-config" || e.data.insertAfter !== insertAfter || done) return;
|
|
686
|
+
done = true;
|
|
687
|
+
clearTimeout(timer);
|
|
688
|
+
window.removeEventListener("message", configHandler);
|
|
689
|
+
innerCleanup = loadWithId(e.data.scheduleId ?? null);
|
|
690
|
+
};
|
|
691
|
+
window.addEventListener("message", configHandler);
|
|
692
|
+
window.postMessage({ type: "ow:request-schedule-config", insertAfter }, "*");
|
|
693
|
+
return () => {
|
|
694
|
+
window.removeEventListener("message", configHandler);
|
|
695
|
+
clearTimeout(timer);
|
|
696
|
+
innerCleanup?.();
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return loadWithId(initialScheduleId) ?? void 0;
|
|
700
|
+
}, []);
|
|
701
|
+
const timezoneLabel = schedule?.timezone ? (() => {
|
|
702
|
+
try {
|
|
703
|
+
return buildTimezoneLabel(schedule.timezone);
|
|
704
|
+
} catch {
|
|
705
|
+
return void 0;
|
|
706
|
+
}
|
|
707
|
+
})() : void 0;
|
|
708
|
+
const handleModalSubmit = async (email) => {
|
|
709
|
+
if (!modalState) return;
|
|
710
|
+
const { mode, classId, classDate } = modalState;
|
|
711
|
+
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
712
|
+
const res = await fetch(endpoint, {
|
|
713
|
+
method: "POST",
|
|
714
|
+
headers: { "Content-Type": "application/json" },
|
|
715
|
+
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
716
|
+
});
|
|
717
|
+
if (!res.ok) {
|
|
718
|
+
const data = await res.json().catch(() => ({}));
|
|
719
|
+
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
const handleReplaceSchedule = () => {
|
|
723
|
+
setIsHovered(false);
|
|
724
|
+
if (schedule) {
|
|
725
|
+
window.parent.postMessage({
|
|
726
|
+
type: "ow:schedule-connected",
|
|
727
|
+
schedule: { id: schedule.id, name: schedule.name },
|
|
728
|
+
insertAfter
|
|
729
|
+
}, "*");
|
|
730
|
+
} else {
|
|
731
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
if (!inEditor && !loading && !schedule) return null;
|
|
735
|
+
const sectionId = `scheduling-${insertAfter}`;
|
|
736
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
737
|
+
"section",
|
|
738
|
+
{
|
|
739
|
+
"data-ohw-section": sectionId,
|
|
740
|
+
"data-ohw-scheduling-anchor": insertAfter,
|
|
741
|
+
className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
742
|
+
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
743
|
+
onMouseLeave: () => setIsHovered(false),
|
|
744
|
+
children: [
|
|
745
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
746
|
+
"div",
|
|
747
|
+
{
|
|
748
|
+
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
749
|
+
onClick: handleReplaceSchedule,
|
|
750
|
+
children: [
|
|
751
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
754
|
+
"div",
|
|
755
|
+
{
|
|
756
|
+
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
757
|
+
style: {
|
|
758
|
+
fontSize: 14,
|
|
759
|
+
lineHeight: "24px",
|
|
760
|
+
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
761
|
+
fontWeight: 500,
|
|
762
|
+
color: "#0C0A09"
|
|
763
|
+
},
|
|
764
|
+
children: [
|
|
765
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
|
|
766
|
+
"Replace schedule"
|
|
767
|
+
]
|
|
768
|
+
}
|
|
769
|
+
) })
|
|
770
|
+
]
|
|
771
|
+
}
|
|
772
|
+
),
|
|
773
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
774
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
775
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
|
|
776
|
+
schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
777
|
+
] }),
|
|
778
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
779
|
+
timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
780
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyState, { inEditor, insertAfter }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
781
|
+
ScheduleView,
|
|
782
|
+
{
|
|
783
|
+
schedule,
|
|
784
|
+
dates,
|
|
785
|
+
selectedIdx,
|
|
786
|
+
onSelectDate: setSelectedIdx,
|
|
787
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
788
|
+
}
|
|
789
|
+
) })
|
|
790
|
+
] })
|
|
791
|
+
] }),
|
|
792
|
+
modalState && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
793
|
+
EmailCaptureModal,
|
|
794
|
+
{
|
|
795
|
+
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
796
|
+
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
797
|
+
onSubmit: handleModalSubmit,
|
|
798
|
+
onClose: () => setModalState(null)
|
|
799
|
+
}
|
|
800
|
+
)
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
}
|
|
52
805
|
|
|
53
806
|
// src/ui/toggle-group.tsx
|
|
54
|
-
var
|
|
807
|
+
var React2 = __toESM(require("react"), 1);
|
|
55
808
|
|
|
56
809
|
// node_modules/clsx/dist/clsx.mjs
|
|
57
810
|
function r(e) {
|
|
@@ -3370,8 +4123,8 @@ var cva = (base, config) => (props) => {
|
|
|
3370
4123
|
};
|
|
3371
4124
|
|
|
3372
4125
|
// src/ui/toggle.tsx
|
|
3373
|
-
var
|
|
3374
|
-
var
|
|
4126
|
+
var import_radix_ui2 = require("radix-ui");
|
|
4127
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3375
4128
|
var toggleVariants = cva(
|
|
3376
4129
|
"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",
|
|
3377
4130
|
{
|
|
@@ -3398,8 +4151,8 @@ function Toggle({
|
|
|
3398
4151
|
size,
|
|
3399
4152
|
...props
|
|
3400
4153
|
}) {
|
|
3401
|
-
return /* @__PURE__ */ (0,
|
|
3402
|
-
|
|
4154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4155
|
+
import_radix_ui2.Toggle.Root,
|
|
3403
4156
|
{
|
|
3404
4157
|
"data-slot": "toggle",
|
|
3405
4158
|
className: cn(toggleVariants({ variant, size, className })),
|
|
@@ -3409,8 +4162,8 @@ function Toggle({
|
|
|
3409
4162
|
}
|
|
3410
4163
|
|
|
3411
4164
|
// src/ui/toggle-group.tsx
|
|
3412
|
-
var
|
|
3413
|
-
var ToggleGroupContext =
|
|
4165
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
4166
|
+
var ToggleGroupContext = React2.createContext({
|
|
3414
4167
|
value: "",
|
|
3415
4168
|
onValueChange: () => {
|
|
3416
4169
|
}
|
|
@@ -3422,13 +4175,13 @@ function ToggleGroup({
|
|
|
3422
4175
|
children,
|
|
3423
4176
|
...props
|
|
3424
4177
|
}) {
|
|
3425
|
-
return /* @__PURE__ */ (0,
|
|
4178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3426
4179
|
"div",
|
|
3427
4180
|
{
|
|
3428
4181
|
role: "group",
|
|
3429
4182
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3430
4183
|
...props,
|
|
3431
|
-
children: /* @__PURE__ */ (0,
|
|
4184
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3432
4185
|
}
|
|
3433
4186
|
);
|
|
3434
4187
|
}
|
|
@@ -3438,8 +4191,8 @@ function ToggleGroupItem({
|
|
|
3438
4191
|
children,
|
|
3439
4192
|
...props
|
|
3440
4193
|
}) {
|
|
3441
|
-
const { value: groupValue, onValueChange } =
|
|
3442
|
-
return /* @__PURE__ */ (0,
|
|
4194
|
+
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
4195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3443
4196
|
Toggle,
|
|
3444
4197
|
{
|
|
3445
4198
|
pressed: groupValue === value,
|
|
@@ -3459,7 +4212,7 @@ function ToggleGroupItem({
|
|
|
3459
4212
|
}
|
|
3460
4213
|
|
|
3461
4214
|
// src/OhhwellsBridge.tsx
|
|
3462
|
-
var
|
|
4215
|
+
var import_react_dom2 = require("react-dom");
|
|
3463
4216
|
var import_navigation = require("next/navigation");
|
|
3464
4217
|
|
|
3465
4218
|
// src/lib/session-search.ts
|
|
@@ -3736,26 +4489,135 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
|
|
|
3736
4489
|
tick();
|
|
3737
4490
|
}
|
|
3738
4491
|
|
|
3739
|
-
// src/ui/
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
}
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
return {
|
|
4492
|
+
// src/ui/dialog.tsx
|
|
4493
|
+
var React3 = __toESM(require("react"), 1);
|
|
4494
|
+
var import_radix_ui3 = require("radix-ui");
|
|
4495
|
+
|
|
4496
|
+
// src/ui/icons.tsx
|
|
4497
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
4498
|
+
function IconX({ className, ...props }) {
|
|
4499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4500
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M18 6 6 18" }),
|
|
4501
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m6 6 12 12" })
|
|
4502
|
+
] });
|
|
4503
|
+
}
|
|
4504
|
+
function IconChevronDown({ className, ...props }) {
|
|
4505
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m6 9 6 6 6-6" }) });
|
|
3753
4506
|
}
|
|
4507
|
+
function IconFile({ className, ...props }) {
|
|
4508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4509
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4510
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4511
|
+
] });
|
|
4512
|
+
}
|
|
4513
|
+
function IconInfo({ className, ...props }) {
|
|
4514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4515
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4516
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 16v-4" }),
|
|
4517
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 8h.01" })
|
|
4518
|
+
] });
|
|
4519
|
+
}
|
|
4520
|
+
function IconArrowRight({ className, ...props }) {
|
|
4521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4522
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M5 12h14" }),
|
|
4523
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m12 5 7 7-7 7" })
|
|
4524
|
+
] });
|
|
4525
|
+
}
|
|
4526
|
+
function IconSection({ className, ...props }) {
|
|
4527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4528
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4529
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4530
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4531
|
+
] });
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4534
|
+
// src/ui/dialog.tsx
|
|
4535
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
4536
|
+
function Dialog2({ ...props }) {
|
|
4537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
4538
|
+
}
|
|
4539
|
+
function DialogPortal({ ...props }) {
|
|
4540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Portal, { ...props });
|
|
4541
|
+
}
|
|
4542
|
+
function DialogOverlay({
|
|
4543
|
+
className,
|
|
4544
|
+
...props
|
|
4545
|
+
}) {
|
|
4546
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4547
|
+
import_radix_ui3.Dialog.Overlay,
|
|
4548
|
+
{
|
|
4549
|
+
"data-slot": "dialog-overlay",
|
|
4550
|
+
"data-ohw-link-modal-root": "",
|
|
4551
|
+
className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
|
|
4552
|
+
...props
|
|
4553
|
+
}
|
|
4554
|
+
);
|
|
4555
|
+
}
|
|
4556
|
+
var DialogContent = React3.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
4557
|
+
const positionMode = container ? "absolute" : "fixed";
|
|
4558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
4559
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
4560
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4561
|
+
import_radix_ui3.Dialog.Content,
|
|
4562
|
+
{
|
|
4563
|
+
ref,
|
|
4564
|
+
"data-slot": "dialog-content",
|
|
4565
|
+
className: cn(
|
|
4566
|
+
positionMode,
|
|
4567
|
+
"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",
|
|
4568
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
4569
|
+
container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
|
|
4570
|
+
className
|
|
4571
|
+
),
|
|
4572
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4573
|
+
...props,
|
|
4574
|
+
children: [
|
|
4575
|
+
children,
|
|
4576
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4577
|
+
import_radix_ui3.Dialog.Close,
|
|
4578
|
+
{
|
|
4579
|
+
type: "button",
|
|
4580
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4581
|
+
"aria-label": "Close",
|
|
4582
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconX, { "aria-hidden": true })
|
|
4583
|
+
}
|
|
4584
|
+
) : null
|
|
4585
|
+
]
|
|
4586
|
+
}
|
|
4587
|
+
)
|
|
4588
|
+
] });
|
|
4589
|
+
});
|
|
4590
|
+
DialogContent.displayName = import_radix_ui3.Dialog.Content.displayName;
|
|
4591
|
+
function DialogHeader({ className, ...props }) {
|
|
4592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
4593
|
+
}
|
|
4594
|
+
function DialogFooter({ className, ...props }) {
|
|
4595
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
|
|
4596
|
+
}
|
|
4597
|
+
var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4598
|
+
import_radix_ui3.Dialog.Title,
|
|
4599
|
+
{
|
|
4600
|
+
ref,
|
|
4601
|
+
className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
|
|
4602
|
+
...props
|
|
4603
|
+
}
|
|
4604
|
+
));
|
|
4605
|
+
DialogTitle.displayName = import_radix_ui3.Dialog.Title.displayName;
|
|
4606
|
+
var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4607
|
+
import_radix_ui3.Dialog.Description,
|
|
4608
|
+
{
|
|
4609
|
+
ref,
|
|
4610
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
4611
|
+
...props
|
|
4612
|
+
}
|
|
4613
|
+
));
|
|
4614
|
+
DialogDescription.displayName = import_radix_ui3.Dialog.Description.displayName;
|
|
4615
|
+
var DialogClose = import_radix_ui3.Dialog.Close;
|
|
3754
4616
|
|
|
3755
4617
|
// src/ui/button.tsx
|
|
3756
|
-
var
|
|
3757
|
-
var
|
|
3758
|
-
var
|
|
4618
|
+
var React4 = __toESM(require("react"), 1);
|
|
4619
|
+
var import_radix_ui4 = require("radix-ui");
|
|
4620
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3759
4621
|
var buttonVariants = cva(
|
|
3760
4622
|
"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",
|
|
3761
4623
|
{
|
|
@@ -3776,10 +4638,10 @@ var buttonVariants = cva(
|
|
|
3776
4638
|
}
|
|
3777
4639
|
}
|
|
3778
4640
|
);
|
|
3779
|
-
var Button =
|
|
4641
|
+
var Button = React4.forwardRef(
|
|
3780
4642
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
3781
|
-
const Comp = asChild ?
|
|
3782
|
-
return /* @__PURE__ */ (0,
|
|
4643
|
+
const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
|
|
4644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3783
4645
|
Comp,
|
|
3784
4646
|
{
|
|
3785
4647
|
ref,
|
|
@@ -3792,76 +4654,38 @@ var Button = React2.forwardRef(
|
|
|
3792
4654
|
);
|
|
3793
4655
|
Button.displayName = "Button";
|
|
3794
4656
|
|
|
3795
|
-
// src/ui/
|
|
3796
|
-
var
|
|
3797
|
-
function
|
|
3798
|
-
return /* @__PURE__ */ (0,
|
|
3799
|
-
/* @__PURE__ */ (0,
|
|
3800
|
-
/* @__PURE__ */ (0,
|
|
3801
|
-
|
|
3802
|
-
}
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
}
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
}
|
|
3812
|
-
function IconInfo({ className, ...props }) {
|
|
3813
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3814
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3815
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 16v-4" }),
|
|
3816
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 8h.01" })
|
|
3817
|
-
] });
|
|
3818
|
-
}
|
|
3819
|
-
function IconArrowRight({ className, ...props }) {
|
|
3820
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3821
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M5 12h14" }),
|
|
3822
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m12 5 7 7-7 7" })
|
|
3823
|
-
] });
|
|
3824
|
-
}
|
|
3825
|
-
function IconSection({ className, ...props }) {
|
|
3826
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
3827
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
3828
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
3829
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
3830
|
-
] });
|
|
3831
|
-
}
|
|
3832
|
-
|
|
3833
|
-
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
3834
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3835
|
-
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
3836
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
|
|
3837
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
3838
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
3839
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3840
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3841
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
3842
|
-
] }),
|
|
3843
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
3844
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
3845
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3846
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
3847
|
-
] })
|
|
3848
|
-
] })
|
|
4657
|
+
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
4658
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4659
|
+
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
4660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
|
|
4661
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
4662
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
4663
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4664
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4665
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
4666
|
+
] }),
|
|
4667
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
4668
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
4669
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4670
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
4671
|
+
] })
|
|
4672
|
+
] })
|
|
3849
4673
|
] });
|
|
3850
4674
|
}
|
|
3851
4675
|
|
|
3852
4676
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
3853
|
-
var
|
|
4677
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
3854
4678
|
function SectionTreeItem({ section, onSelect, selected }) {
|
|
3855
4679
|
const interactive = Boolean(onSelect);
|
|
3856
|
-
return /* @__PURE__ */ (0,
|
|
3857
|
-
/* @__PURE__ */ (0,
|
|
4680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
4681
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3858
4682
|
"div",
|
|
3859
4683
|
{
|
|
3860
4684
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
3861
4685
|
"aria-hidden": true
|
|
3862
4686
|
}
|
|
3863
4687
|
),
|
|
3864
|
-
/* @__PURE__ */ (0,
|
|
4688
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3865
4689
|
"div",
|
|
3866
4690
|
{
|
|
3867
4691
|
role: interactive ? "button" : void 0,
|
|
@@ -3879,8 +4703,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3879
4703
|
interactive && selected && "border-primary"
|
|
3880
4704
|
),
|
|
3881
4705
|
children: [
|
|
3882
|
-
/* @__PURE__ */ (0,
|
|
3883
|
-
/* @__PURE__ */ (0,
|
|
4706
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4707
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
3884
4708
|
]
|
|
3885
4709
|
}
|
|
3886
4710
|
)
|
|
@@ -3888,23 +4712,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
3888
4712
|
}
|
|
3889
4713
|
function SectionPickerList({ sections, onSelect }) {
|
|
3890
4714
|
if (sections.length === 0) {
|
|
3891
|
-
return /* @__PURE__ */ (0,
|
|
4715
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
3892
4716
|
}
|
|
3893
|
-
return /* @__PURE__ */ (0,
|
|
3894
|
-
/* @__PURE__ */ (0,
|
|
3895
|
-
sections.map((section) => /* @__PURE__ */ (0,
|
|
4717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4718
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
4719
|
+
sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SectionTreeItem, { section, onSelect }, section.id))
|
|
3896
4720
|
] });
|
|
3897
4721
|
}
|
|
3898
4722
|
|
|
3899
4723
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3900
|
-
var
|
|
4724
|
+
var import_react3 = require("react");
|
|
3901
4725
|
|
|
3902
4726
|
// src/ui/input.tsx
|
|
3903
|
-
var
|
|
3904
|
-
var
|
|
3905
|
-
var Input =
|
|
4727
|
+
var React5 = __toESM(require("react"), 1);
|
|
4728
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4729
|
+
var Input = React5.forwardRef(
|
|
3906
4730
|
({ className, type, ...props }, ref) => {
|
|
3907
|
-
return /* @__PURE__ */ (0,
|
|
4731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3908
4732
|
"input",
|
|
3909
4733
|
{
|
|
3910
4734
|
type,
|
|
@@ -3922,11 +4746,11 @@ var Input = React3.forwardRef(
|
|
|
3922
4746
|
Input.displayName = "Input";
|
|
3923
4747
|
|
|
3924
4748
|
// src/ui/label.tsx
|
|
3925
|
-
var
|
|
3926
|
-
var
|
|
4749
|
+
var import_radix_ui5 = require("radix-ui");
|
|
4750
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
3927
4751
|
function Label({ className, ...props }) {
|
|
3928
|
-
return /* @__PURE__ */ (0,
|
|
3929
|
-
|
|
4752
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4753
|
+
import_radix_ui5.Label.Root,
|
|
3930
4754
|
{
|
|
3931
4755
|
"data-slot": "label",
|
|
3932
4756
|
className: cn("text-sm font-medium leading-5 text-foreground", className),
|
|
@@ -3935,40 +4759,10 @@ function Label({ className, ...props }) {
|
|
|
3935
4759
|
);
|
|
3936
4760
|
}
|
|
3937
4761
|
|
|
3938
|
-
// src/ui/popover.tsx
|
|
3939
|
-
var import_radix_ui4 = require("radix-ui");
|
|
3940
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
3941
|
-
function Popover({ ...props }) {
|
|
3942
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Root, { "data-slot": "popover", ...props });
|
|
3943
|
-
}
|
|
3944
|
-
function PopoverTrigger({ ...props }) {
|
|
3945
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
3946
|
-
}
|
|
3947
|
-
function PopoverContent({
|
|
3948
|
-
className,
|
|
3949
|
-
align = "start",
|
|
3950
|
-
sideOffset = 6,
|
|
3951
|
-
...props
|
|
3952
|
-
}) {
|
|
3953
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3954
|
-
import_radix_ui4.Popover.Content,
|
|
3955
|
-
{
|
|
3956
|
-
"data-slot": "popover-content",
|
|
3957
|
-
align,
|
|
3958
|
-
sideOffset,
|
|
3959
|
-
className: cn(
|
|
3960
|
-
"z-[2147483647] w-[var(--radix-popover-trigger-width)] overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg outline-none",
|
|
3961
|
-
className
|
|
3962
|
-
),
|
|
3963
|
-
...props
|
|
3964
|
-
}
|
|
3965
|
-
) });
|
|
3966
|
-
}
|
|
3967
|
-
|
|
3968
4762
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
3969
|
-
var
|
|
4763
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
3970
4764
|
function FieldChevron({ onClick }) {
|
|
3971
|
-
return /* @__PURE__ */ (0,
|
|
4765
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3972
4766
|
"button",
|
|
3973
4767
|
{
|
|
3974
4768
|
type: "button",
|
|
@@ -3976,7 +4770,7 @@ function FieldChevron({ onClick }) {
|
|
|
3976
4770
|
onClick,
|
|
3977
4771
|
"aria-label": "Open page list",
|
|
3978
4772
|
tabIndex: -1,
|
|
3979
|
-
children: /* @__PURE__ */ (0,
|
|
4773
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconChevronDown, {})
|
|
3980
4774
|
}
|
|
3981
4775
|
);
|
|
3982
4776
|
}
|
|
@@ -3992,9 +4786,9 @@ function UrlOrPageInput({
|
|
|
3992
4786
|
readOnly = false,
|
|
3993
4787
|
urlError
|
|
3994
4788
|
}) {
|
|
3995
|
-
const inputId = (0,
|
|
3996
|
-
const inputRef = (0,
|
|
3997
|
-
const [isFocused, setIsFocused] = (0,
|
|
4789
|
+
const inputId = (0, import_react3.useId)();
|
|
4790
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
4791
|
+
const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
|
|
3998
4792
|
const openDropdown = () => {
|
|
3999
4793
|
if (readOnly || filteredPages.length === 0) return;
|
|
4000
4794
|
onDropdownOpenChange(true);
|
|
@@ -4015,12 +4809,12 @@ function UrlOrPageInput({
|
|
|
4015
4809
|
"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]",
|
|
4016
4810
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
4017
4811
|
);
|
|
4018
|
-
return /* @__PURE__ */ (0,
|
|
4019
|
-
/* @__PURE__ */ (0,
|
|
4020
|
-
/* @__PURE__ */ (0,
|
|
4021
|
-
/* @__PURE__ */ (0,
|
|
4022
|
-
selectedPage ? /* @__PURE__ */ (0,
|
|
4023
|
-
readOnly ? /* @__PURE__ */ (0,
|
|
4812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4813
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4814
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative w-full", children: [
|
|
4815
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
4816
|
+
selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4817
|
+
readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
4024
4818
|
Input,
|
|
4025
4819
|
{
|
|
4026
4820
|
ref: inputRef,
|
|
@@ -4039,7 +4833,7 @@ function UrlOrPageInput({
|
|
|
4039
4833
|
)
|
|
4040
4834
|
}
|
|
4041
4835
|
),
|
|
4042
|
-
selectedPage && !readOnly ? /* @__PURE__ */ (0,
|
|
4836
|
+
selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
4043
4837
|
"button",
|
|
4044
4838
|
{
|
|
4045
4839
|
type: "button",
|
|
@@ -4047,31 +4841,39 @@ function UrlOrPageInput({
|
|
|
4047
4841
|
onMouseDown: clearSelection,
|
|
4048
4842
|
"aria-label": "Clear selected page",
|
|
4049
4843
|
tabIndex: -1,
|
|
4050
|
-
children: /* @__PURE__ */ (0,
|
|
4844
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconX, { "aria-hidden": true })
|
|
4051
4845
|
}
|
|
4052
4846
|
) : null,
|
|
4053
|
-
!readOnly ? /* @__PURE__ */ (0,
|
|
4054
|
-
] })
|
|
4055
|
-
filteredPages.length > 0
|
|
4056
|
-
"
|
|
4847
|
+
!readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4848
|
+
] }),
|
|
4849
|
+
dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
4850
|
+
"div",
|
|
4057
4851
|
{
|
|
4058
|
-
|
|
4059
|
-
className: "
|
|
4060
|
-
|
|
4061
|
-
children:
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4852
|
+
"data-ohw-link-page-dropdown": "",
|
|
4853
|
+
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",
|
|
4854
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4855
|
+
children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
4856
|
+
"button",
|
|
4857
|
+
{
|
|
4858
|
+
type: "button",
|
|
4859
|
+
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",
|
|
4860
|
+
onClick: () => onPageSelect(page),
|
|
4861
|
+
children: [
|
|
4862
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4863
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate", children: page.title })
|
|
4864
|
+
]
|
|
4865
|
+
},
|
|
4866
|
+
page.path
|
|
4867
|
+
))
|
|
4868
|
+
}
|
|
4869
|
+
) : null
|
|
4068
4870
|
] }),
|
|
4069
|
-
urlError ? /* @__PURE__ */ (0,
|
|
4871
|
+
urlError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4070
4872
|
] });
|
|
4071
4873
|
}
|
|
4072
4874
|
|
|
4073
4875
|
// src/ui/link-modal/useLinkModalState.ts
|
|
4074
|
-
var
|
|
4876
|
+
var import_react4 = require("react");
|
|
4075
4877
|
function useLinkModalState({
|
|
4076
4878
|
open,
|
|
4077
4879
|
mode,
|
|
@@ -4083,17 +4885,17 @@ function useLinkModalState({
|
|
|
4083
4885
|
onClose,
|
|
4084
4886
|
onSubmit
|
|
4085
4887
|
}) {
|
|
4086
|
-
const availablePages = (0,
|
|
4888
|
+
const availablePages = (0, import_react4.useMemo)(
|
|
4087
4889
|
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4088
4890
|
[mode, pages, existingTargets]
|
|
4089
4891
|
);
|
|
4090
|
-
const [searchValue, setSearchValue] = (0,
|
|
4091
|
-
const [selectedPage, setSelectedPage] = (0,
|
|
4092
|
-
const [selectedSection, setSelectedSection] = (0,
|
|
4093
|
-
const [step, setStep] = (0,
|
|
4094
|
-
const [dropdownOpen, setDropdownOpen] = (0,
|
|
4095
|
-
const [urlError, setUrlError] = (0,
|
|
4096
|
-
const reset = (0,
|
|
4892
|
+
const [searchValue, setSearchValue] = (0, import_react4.useState)("");
|
|
4893
|
+
const [selectedPage, setSelectedPage] = (0, import_react4.useState)(null);
|
|
4894
|
+
const [selectedSection, setSelectedSection] = (0, import_react4.useState)(null);
|
|
4895
|
+
const [step, setStep] = (0, import_react4.useState)("input");
|
|
4896
|
+
const [dropdownOpen, setDropdownOpen] = (0, import_react4.useState)(false);
|
|
4897
|
+
const [urlError, setUrlError] = (0, import_react4.useState)("");
|
|
4898
|
+
const reset = (0, import_react4.useCallback)(() => {
|
|
4097
4899
|
setSearchValue("");
|
|
4098
4900
|
setSelectedPage(null);
|
|
4099
4901
|
setSelectedSection(null);
|
|
@@ -4101,7 +4903,7 @@ function useLinkModalState({
|
|
|
4101
4903
|
setDropdownOpen(false);
|
|
4102
4904
|
setUrlError("");
|
|
4103
4905
|
}, []);
|
|
4104
|
-
(0,
|
|
4906
|
+
(0, import_react4.useEffect)(() => {
|
|
4105
4907
|
if (!open) return;
|
|
4106
4908
|
if (mode === "edit" && initialTarget) {
|
|
4107
4909
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -4117,11 +4919,11 @@ function useLinkModalState({
|
|
|
4117
4919
|
setDropdownOpen(false);
|
|
4118
4920
|
setUrlError("");
|
|
4119
4921
|
}, [open, mode, initialTarget, pages, sectionsByPath, reset]);
|
|
4120
|
-
const filteredPages = (0,
|
|
4922
|
+
const filteredPages = (0, import_react4.useMemo)(() => {
|
|
4121
4923
|
if (!searchValue.trim()) return availablePages;
|
|
4122
4924
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4123
4925
|
}, [availablePages, searchValue]);
|
|
4124
|
-
const activeSections = (0,
|
|
4926
|
+
const activeSections = (0, import_react4.useMemo)(() => {
|
|
4125
4927
|
if (!selectedPage) return [];
|
|
4126
4928
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
4127
4929
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -4167,7 +4969,7 @@ function useLinkModalState({
|
|
|
4167
4969
|
reset();
|
|
4168
4970
|
onClose();
|
|
4169
4971
|
};
|
|
4170
|
-
const isValid = (0,
|
|
4972
|
+
const isValid = (0, import_react4.useMemo)(() => {
|
|
4171
4973
|
if (urlError) return false;
|
|
4172
4974
|
if (showBreadcrumb) return true;
|
|
4173
4975
|
if (selectedPage) return true;
|
|
@@ -4234,7 +5036,7 @@ function useLinkModalState({
|
|
|
4234
5036
|
}
|
|
4235
5037
|
|
|
4236
5038
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4237
|
-
var
|
|
5039
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
4238
5040
|
function LinkEditorPanel({
|
|
4239
5041
|
open = true,
|
|
4240
5042
|
mode = "create",
|
|
@@ -4258,26 +5060,25 @@ function LinkEditorPanel({
|
|
|
4258
5060
|
onSubmit
|
|
4259
5061
|
});
|
|
4260
5062
|
const isCancel = state.secondaryLabel === "Cancel";
|
|
4261
|
-
return /* @__PURE__ */ (0,
|
|
4262
|
-
/* @__PURE__ */ (0,
|
|
5063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
5064
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4263
5065
|
"button",
|
|
4264
5066
|
{
|
|
4265
5067
|
type: "button",
|
|
4266
5068
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4267
|
-
onClick: state.handleClose,
|
|
4268
5069
|
"aria-label": "Close",
|
|
4269
|
-
children: /* @__PURE__ */ (0,
|
|
5070
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
|
|
4270
5071
|
}
|
|
4271
|
-
),
|
|
4272
|
-
/* @__PURE__ */ (0,
|
|
4273
|
-
/* @__PURE__ */ (0,
|
|
4274
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0,
|
|
5072
|
+
) }),
|
|
5073
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
|
|
5074
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
5075
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4275
5076
|
DestinationBreadcrumb,
|
|
4276
5077
|
{
|
|
4277
5078
|
pageTitle: state.selectedPage.title,
|
|
4278
5079
|
sectionLabel: state.selectedSection.label
|
|
4279
5080
|
}
|
|
4280
|
-
) : /* @__PURE__ */ (0,
|
|
5081
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4281
5082
|
UrlOrPageInput,
|
|
4282
5083
|
{
|
|
4283
5084
|
value: state.searchValue,
|
|
@@ -4290,18 +5091,18 @@ function LinkEditorPanel({
|
|
|
4290
5091
|
urlError: state.urlError
|
|
4291
5092
|
}
|
|
4292
5093
|
),
|
|
4293
|
-
state.showChooseSection ? /* @__PURE__ */ (0,
|
|
4294
|
-
/* @__PURE__ */ (0,
|
|
4295
|
-
/* @__PURE__ */ (0,
|
|
4296
|
-
/* @__PURE__ */ (0,
|
|
4297
|
-
/* @__PURE__ */ (0,
|
|
5094
|
+
state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
5095
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
5096
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
5097
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
5098
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Pick a section this link should scroll to." })
|
|
4298
5099
|
] })
|
|
4299
5100
|
] }) : null,
|
|
4300
|
-
state.showSectionPicker ? /* @__PURE__ */ (0,
|
|
4301
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0,
|
|
5101
|
+
state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
5102
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4302
5103
|
] }),
|
|
4303
|
-
/* @__PURE__ */ (0,
|
|
4304
|
-
/* @__PURE__ */ (0,
|
|
5104
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
|
|
5105
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4305
5106
|
Button,
|
|
4306
5107
|
{
|
|
4307
5108
|
type: "button",
|
|
@@ -4316,7 +5117,7 @@ function LinkEditorPanel({
|
|
|
4316
5117
|
children: state.secondaryLabel
|
|
4317
5118
|
}
|
|
4318
5119
|
),
|
|
4319
|
-
/* @__PURE__ */ (0,
|
|
5120
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4320
5121
|
Button,
|
|
4321
5122
|
{
|
|
4322
5123
|
type: "button",
|
|
@@ -4335,28 +5136,34 @@ function LinkEditorPanel({
|
|
|
4335
5136
|
}
|
|
4336
5137
|
|
|
4337
5138
|
// src/ui/link-modal/LinkPopover.tsx
|
|
4338
|
-
var
|
|
5139
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
4339
5140
|
function LinkPopover({
|
|
4340
|
-
|
|
4341
|
-
parentScroll,
|
|
5141
|
+
open = true,
|
|
4342
5142
|
panelRef,
|
|
5143
|
+
portalContainer,
|
|
5144
|
+
onClose,
|
|
4343
5145
|
...editorProps
|
|
4344
5146
|
}) {
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
"div",
|
|
5147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5148
|
+
Dialog2,
|
|
4348
5149
|
{
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
5150
|
+
open,
|
|
5151
|
+
onOpenChange: (next) => {
|
|
5152
|
+
if (!next) onClose?.();
|
|
5153
|
+
},
|
|
5154
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5155
|
+
DialogContent,
|
|
5156
|
+
{
|
|
5157
|
+
ref: panelRef,
|
|
5158
|
+
container: portalContainer,
|
|
5159
|
+
"data-ohw-link-popover-root": "",
|
|
5160
|
+
"data-ohw-link-modal-root": "",
|
|
5161
|
+
"data-ohw-bridge": "",
|
|
5162
|
+
showCloseButton: false,
|
|
5163
|
+
className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
|
|
5164
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
|
|
5165
|
+
}
|
|
5166
|
+
)
|
|
4360
5167
|
}
|
|
4361
5168
|
);
|
|
4362
5169
|
}
|
|
@@ -4419,8 +5226,30 @@ function shouldUseDevFixtures() {
|
|
|
4419
5226
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
4420
5227
|
}
|
|
4421
5228
|
|
|
5229
|
+
// src/ui/badge.tsx
|
|
5230
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
5231
|
+
var badgeVariants = cva(
|
|
5232
|
+
"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",
|
|
5233
|
+
{
|
|
5234
|
+
variants: {
|
|
5235
|
+
variant: {
|
|
5236
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
5237
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
5238
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
5239
|
+
outline: "text-foreground"
|
|
5240
|
+
}
|
|
5241
|
+
},
|
|
5242
|
+
defaultVariants: {
|
|
5243
|
+
variant: "default"
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
);
|
|
5247
|
+
function Badge({ className, variant, ...props }) {
|
|
5248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5249
|
+
}
|
|
5250
|
+
|
|
4422
5251
|
// src/OhhwellsBridge.tsx
|
|
4423
|
-
var
|
|
5252
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4424
5253
|
var PRIMARY = "#0885FE";
|
|
4425
5254
|
var IMAGE_FADE_MS = 300;
|
|
4426
5255
|
function runOpacityFade(el, onDone) {
|
|
@@ -4471,6 +5300,169 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
4471
5300
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
4472
5301
|
});
|
|
4473
5302
|
}
|
|
5303
|
+
function getSectionsTracker() {
|
|
5304
|
+
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
5305
|
+
if (!el) {
|
|
5306
|
+
el = document.createElement("div");
|
|
5307
|
+
el.setAttribute("data-ohw-sections-tracker", "");
|
|
5308
|
+
el.style.display = "none";
|
|
5309
|
+
document.body.appendChild(el);
|
|
5310
|
+
}
|
|
5311
|
+
return el;
|
|
5312
|
+
}
|
|
5313
|
+
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
5314
|
+
const tracker = getSectionsTracker();
|
|
5315
|
+
let sections = [];
|
|
5316
|
+
try {
|
|
5317
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5318
|
+
} catch {
|
|
5319
|
+
}
|
|
5320
|
+
const currentPath = window.location.pathname;
|
|
5321
|
+
const updated = sections.map((s) => {
|
|
5322
|
+
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
5323
|
+
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
5324
|
+
return { ...s, scheduleId };
|
|
5325
|
+
});
|
|
5326
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5327
|
+
return tracker.textContent ?? "[]";
|
|
5328
|
+
}
|
|
5329
|
+
function schedulingSectionId(insertAfter) {
|
|
5330
|
+
return `scheduling-${insertAfter}`;
|
|
5331
|
+
}
|
|
5332
|
+
var INSERT_BEFORE_MARKER = ":before:";
|
|
5333
|
+
function parseSchedulingInsertAfter(insertAfter) {
|
|
5334
|
+
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
5335
|
+
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
5336
|
+
return {
|
|
5337
|
+
anchor: insertAfter.slice(0, idx),
|
|
5338
|
+
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
5339
|
+
};
|
|
5340
|
+
}
|
|
5341
|
+
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
5342
|
+
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
5343
|
+
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
5344
|
+
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
5345
|
+
return { effectiveInsertAfter, insertBefore };
|
|
5346
|
+
}
|
|
5347
|
+
function getSchedulingMountPoint(insertAfter) {
|
|
5348
|
+
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
5349
|
+
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
5350
|
+
if (!anchorEl && anchor === "scheduling") {
|
|
5351
|
+
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
5352
|
+
anchorEl = widgets.at(-1) ?? null;
|
|
5353
|
+
}
|
|
5354
|
+
if (!anchorEl) return null;
|
|
5355
|
+
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
5356
|
+
}
|
|
5357
|
+
function schedulingMountDepth(insertAfter) {
|
|
5358
|
+
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
5359
|
+
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
5360
|
+
}
|
|
5361
|
+
function getPageSchedulingEntries(raw) {
|
|
5362
|
+
if (!raw) return [];
|
|
5363
|
+
try {
|
|
5364
|
+
const entries = JSON.parse(raw);
|
|
5365
|
+
const currentPath = window.location.pathname;
|
|
5366
|
+
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
5367
|
+
} catch {
|
|
5368
|
+
return [];
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
function isSchedulingWidgetMissing(entry) {
|
|
5372
|
+
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
5373
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
5374
|
+
}
|
|
5375
|
+
function hasMissingSchedulingWidgets(entries) {
|
|
5376
|
+
return entries.some(isSchedulingWidgetMissing);
|
|
5377
|
+
}
|
|
5378
|
+
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
5379
|
+
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
5380
|
+
mountSchedulingEntries(entries, notifyOnConnect);
|
|
5381
|
+
}
|
|
5382
|
+
function initSectionsFromContent(content, removeExisting = false) {
|
|
5383
|
+
const raw = content["__ohw_sections"];
|
|
5384
|
+
if (!raw) return;
|
|
5385
|
+
try {
|
|
5386
|
+
if (removeExisting) {
|
|
5387
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
5388
|
+
}
|
|
5389
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5390
|
+
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5391
|
+
const pageEntries = getPageSchedulingEntries(raw);
|
|
5392
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
5393
|
+
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
5394
|
+
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
5395
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
5396
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
5397
|
+
for (const entry of preExisting) {
|
|
5398
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
5399
|
+
}
|
|
5400
|
+
} catch {
|
|
5401
|
+
}
|
|
5402
|
+
}
|
|
5403
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
5404
|
+
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
5405
|
+
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
5406
|
+
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
5407
|
+
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
5408
|
+
if (!mountPoint) return false;
|
|
5409
|
+
const container = document.createElement("div");
|
|
5410
|
+
container.dataset.ohwSectionContainer = "scheduling";
|
|
5411
|
+
if (insertBefore) {
|
|
5412
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
5413
|
+
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
5414
|
+
if (!beforePoint) return false;
|
|
5415
|
+
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
5416
|
+
} else {
|
|
5417
|
+
let tail = mountPoint;
|
|
5418
|
+
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
5419
|
+
tail = tail.nextElementSibling;
|
|
5420
|
+
}
|
|
5421
|
+
tail.insertAdjacentElement("afterend", container);
|
|
5422
|
+
}
|
|
5423
|
+
const root = (0, import_client.createRoot)(container);
|
|
5424
|
+
(0, import_react_dom.flushSync)(() => {
|
|
5425
|
+
root.render(
|
|
5426
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5427
|
+
SchedulingWidget,
|
|
5428
|
+
{
|
|
5429
|
+
notifyOnConnect,
|
|
5430
|
+
initialScheduleId: scheduleId,
|
|
5431
|
+
insertAfter: effectiveInsertAfter
|
|
5432
|
+
}
|
|
5433
|
+
)
|
|
5434
|
+
);
|
|
5435
|
+
});
|
|
5436
|
+
const tracker = getSectionsTracker();
|
|
5437
|
+
let sections = [];
|
|
5438
|
+
try {
|
|
5439
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5440
|
+
} catch {
|
|
5441
|
+
}
|
|
5442
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5443
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
5444
|
+
sections.push({
|
|
5445
|
+
type: "scheduling",
|
|
5446
|
+
insertAfter: effectiveInsertAfter,
|
|
5447
|
+
pagePath: window.location.pathname,
|
|
5448
|
+
...scheduleId ? { scheduleId } : {}
|
|
5449
|
+
});
|
|
5450
|
+
tracker.textContent = JSON.stringify(sections);
|
|
5451
|
+
}
|
|
5452
|
+
return true;
|
|
5453
|
+
}
|
|
5454
|
+
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
5455
|
+
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5456
|
+
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5457
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5458
|
+
const entry = pending[i];
|
|
5459
|
+
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
5460
|
+
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5461
|
+
pending.splice(i, 1);
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
4474
5466
|
function getLinkHref(el) {
|
|
4475
5467
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
4476
5468
|
return anchor?.getAttribute("href") ?? "";
|
|
@@ -4517,7 +5509,7 @@ function applyLinkByKey(key, val) {
|
|
|
4517
5509
|
}
|
|
4518
5510
|
function isInsideLinkEditor(target) {
|
|
4519
5511
|
return Boolean(
|
|
4520
|
-
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest('[data-slot="popover-content"]')
|
|
5512
|
+
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"]')
|
|
4521
5513
|
);
|
|
4522
5514
|
}
|
|
4523
5515
|
function getHrefKeyFromElement(el) {
|
|
@@ -4528,6 +5520,15 @@ function getHrefKeyFromElement(el) {
|
|
|
4528
5520
|
if (!key) return null;
|
|
4529
5521
|
return { anchor, key };
|
|
4530
5522
|
}
|
|
5523
|
+
function isNavbarButton(el) {
|
|
5524
|
+
return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
|
|
5525
|
+
}
|
|
5526
|
+
function clearHrefKeyHover(anchor) {
|
|
5527
|
+
anchor.removeAttribute("data-ohw-hovered");
|
|
5528
|
+
anchor.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
|
|
5529
|
+
el.removeAttribute("data-ohw-hovered");
|
|
5530
|
+
});
|
|
5531
|
+
}
|
|
4531
5532
|
function collectSections() {
|
|
4532
5533
|
return collectSectionsFromDom();
|
|
4533
5534
|
}
|
|
@@ -4670,7 +5671,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4670
5671
|
];
|
|
4671
5672
|
function GlowFrame({ rect, elRef }) {
|
|
4672
5673
|
const GAP = 6;
|
|
4673
|
-
return /* @__PURE__ */ (0,
|
|
5674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4674
5675
|
"div",
|
|
4675
5676
|
{
|
|
4676
5677
|
ref: elRef,
|
|
@@ -4689,41 +5690,191 @@ function GlowFrame({ rect, elRef }) {
|
|
|
4689
5690
|
}
|
|
4690
5691
|
);
|
|
4691
5692
|
}
|
|
4692
|
-
function
|
|
5693
|
+
function getIframeVisibleClip(parentScroll) {
|
|
5694
|
+
if (!parentScroll) return null;
|
|
5695
|
+
const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
|
|
5696
|
+
const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
|
|
5697
|
+
const clipBottom = Math.min(
|
|
5698
|
+
window.innerHeight,
|
|
5699
|
+
visibleCanvasTop + canvasH - iframeOffsetTop
|
|
5700
|
+
);
|
|
5701
|
+
return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
|
|
5702
|
+
}
|
|
5703
|
+
function applyVisibleViewport(el, parentScroll) {
|
|
5704
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5705
|
+
const top = clip?.top ?? 0;
|
|
5706
|
+
const height = clip ? clip.bottom - clip.top : window.innerHeight;
|
|
5707
|
+
el.style.top = `${top}px`;
|
|
5708
|
+
el.style.height = `${height}px`;
|
|
5709
|
+
}
|
|
5710
|
+
function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
5711
|
+
const isAbove = transform.includes("translateY(-100%)");
|
|
5712
|
+
let visualTop = isAbove ? top - approxH : top;
|
|
5713
|
+
let visualBottom = isAbove ? top : top + approxH;
|
|
5714
|
+
const minTop = clip.top + gap;
|
|
5715
|
+
const maxBottom = clip.bottom - gap;
|
|
5716
|
+
if (visualTop >= minTop && visualBottom <= maxBottom) {
|
|
5717
|
+
return { top, transform };
|
|
5718
|
+
}
|
|
5719
|
+
const belowTop = rect.bottom + gap;
|
|
5720
|
+
if (belowTop + approxH <= maxBottom && belowTop >= minTop) {
|
|
5721
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5722
|
+
}
|
|
5723
|
+
const aboveTop = rect.top - gap;
|
|
5724
|
+
if (aboveTop - approxH >= minTop && aboveTop <= maxBottom) {
|
|
5725
|
+
return { top: aboveTop, transform: "translateX(-50%) translateY(-100%)" };
|
|
5726
|
+
}
|
|
5727
|
+
if (belowTop + approxH <= maxBottom) {
|
|
5728
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5729
|
+
}
|
|
5730
|
+
const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
|
|
5731
|
+
return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
|
|
5732
|
+
}
|
|
5733
|
+
function calcToolbarPos(rect, parentScroll, approxW = 330) {
|
|
4693
5734
|
const GAP = 8;
|
|
4694
5735
|
const APPROX_H = 36;
|
|
4695
|
-
const APPROX_W =
|
|
5736
|
+
const APPROX_W = approxW;
|
|
5737
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5738
|
+
const clipTop = clip?.top ?? 0;
|
|
4696
5739
|
const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
|
|
4697
5740
|
const visibleTop = rect.top - canvasTopInIframe;
|
|
4698
5741
|
const visibleBottom = rect.bottom - canvasTopInIframe;
|
|
4699
5742
|
const isTall = rect.height > APPROX_H + GAP;
|
|
5743
|
+
const spaceAbove = rect.top - (clipTop + GAP);
|
|
5744
|
+
const fitsAbove = spaceAbove >= APPROX_H + GAP;
|
|
4700
5745
|
let top;
|
|
4701
5746
|
let transform;
|
|
4702
|
-
if (visibleTop > 0 || !isTall) {
|
|
5747
|
+
if ((visibleTop > 0 || !isTall) && fitsAbove) {
|
|
4703
5748
|
top = rect.top - GAP;
|
|
4704
5749
|
transform = "translateX(-50%) translateY(-100%)";
|
|
5750
|
+
} else if (visibleTop > 0 || !isTall) {
|
|
5751
|
+
top = rect.bottom + GAP;
|
|
5752
|
+
transform = "translateX(-50%)";
|
|
4705
5753
|
} else if (visibleBottom > APPROX_H + GAP) {
|
|
4706
|
-
top =
|
|
5754
|
+
top = clipTop + GAP;
|
|
4707
5755
|
transform = "translateX(-50%)";
|
|
4708
5756
|
} else {
|
|
4709
5757
|
top = rect.bottom + GAP;
|
|
4710
5758
|
transform = "translateX(-50%)";
|
|
4711
5759
|
}
|
|
5760
|
+
if (clip) {
|
|
5761
|
+
const clamped = clampToolbarToClip(top, transform, rect, clip, APPROX_H, GAP);
|
|
5762
|
+
top = clamped.top;
|
|
5763
|
+
transform = clamped.transform;
|
|
5764
|
+
}
|
|
4712
5765
|
const rawLeft = rect.left + rect.width / 2;
|
|
4713
5766
|
const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
|
|
4714
5767
|
return { top, left, transform };
|
|
4715
5768
|
}
|
|
5769
|
+
var EDIT_LINK_ICON = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("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: [
|
|
5770
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
5771
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
5772
|
+
] });
|
|
4716
5773
|
function FloatingToolbar({
|
|
4717
5774
|
rect,
|
|
4718
5775
|
parentScroll,
|
|
4719
5776
|
elRef,
|
|
5777
|
+
variant = "rich-text",
|
|
4720
5778
|
onCommand,
|
|
4721
5779
|
activeCommands,
|
|
4722
5780
|
showEditLink,
|
|
4723
5781
|
onEditLink
|
|
4724
5782
|
}) {
|
|
4725
|
-
const
|
|
4726
|
-
|
|
5783
|
+
const approxW = variant === "link-action" ? 120 : 330;
|
|
5784
|
+
const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
|
|
5785
|
+
if (variant === "link-action") {
|
|
5786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5787
|
+
"div",
|
|
5788
|
+
{
|
|
5789
|
+
ref: elRef,
|
|
5790
|
+
"data-ohw-toolbar": "",
|
|
5791
|
+
style: {
|
|
5792
|
+
position: "fixed",
|
|
5793
|
+
top,
|
|
5794
|
+
left,
|
|
5795
|
+
transform,
|
|
5796
|
+
zIndex: 2147483647,
|
|
5797
|
+
background: "#fff",
|
|
5798
|
+
border: "1px solid #E7E5E4",
|
|
5799
|
+
borderRadius: 6,
|
|
5800
|
+
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
5801
|
+
display: "flex",
|
|
5802
|
+
alignItems: "center",
|
|
5803
|
+
padding: 4,
|
|
5804
|
+
gap: 6,
|
|
5805
|
+
fontFamily: "sans-serif",
|
|
5806
|
+
pointerEvents: "auto",
|
|
5807
|
+
whiteSpace: "nowrap"
|
|
5808
|
+
},
|
|
5809
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5810
|
+
children: [
|
|
5811
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5812
|
+
"button",
|
|
5813
|
+
{
|
|
5814
|
+
type: "button",
|
|
5815
|
+
title: "Edit link",
|
|
5816
|
+
onMouseDown: (e) => {
|
|
5817
|
+
e.preventDefault();
|
|
5818
|
+
e.stopPropagation();
|
|
5819
|
+
onEditLink?.();
|
|
5820
|
+
},
|
|
5821
|
+
onClick: (e) => {
|
|
5822
|
+
e.preventDefault();
|
|
5823
|
+
e.stopPropagation();
|
|
5824
|
+
},
|
|
5825
|
+
onMouseEnter: (e) => {
|
|
5826
|
+
e.currentTarget.style.background = "#F5F5F4";
|
|
5827
|
+
},
|
|
5828
|
+
onMouseLeave: (e) => {
|
|
5829
|
+
e.currentTarget.style.background = "transparent";
|
|
5830
|
+
},
|
|
5831
|
+
style: {
|
|
5832
|
+
display: "flex",
|
|
5833
|
+
alignItems: "center",
|
|
5834
|
+
justifyContent: "center",
|
|
5835
|
+
border: "none",
|
|
5836
|
+
background: "transparent",
|
|
5837
|
+
borderRadius: 4,
|
|
5838
|
+
cursor: "pointer",
|
|
5839
|
+
color: "#1C1917",
|
|
5840
|
+
flexShrink: 0,
|
|
5841
|
+
padding: 6
|
|
5842
|
+
},
|
|
5843
|
+
children: EDIT_LINK_ICON
|
|
5844
|
+
}
|
|
5845
|
+
),
|
|
5846
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5847
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5848
|
+
"button",
|
|
5849
|
+
{
|
|
5850
|
+
type: "button",
|
|
5851
|
+
title: "Coming soon",
|
|
5852
|
+
disabled: true,
|
|
5853
|
+
style: {
|
|
5854
|
+
display: "flex",
|
|
5855
|
+
alignItems: "center",
|
|
5856
|
+
justifyContent: "center",
|
|
5857
|
+
border: "none",
|
|
5858
|
+
background: "transparent",
|
|
5859
|
+
borderRadius: 4,
|
|
5860
|
+
cursor: "not-allowed",
|
|
5861
|
+
color: "#A8A29E",
|
|
5862
|
+
flexShrink: 0,
|
|
5863
|
+
padding: 6,
|
|
5864
|
+
opacity: 0.6
|
|
5865
|
+
},
|
|
5866
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
|
|
5867
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "5", cy: "12", r: "1.5" }),
|
|
5868
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
|
|
5869
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "19", cy: "12", r: "1.5" })
|
|
5870
|
+
] })
|
|
5871
|
+
}
|
|
5872
|
+
)
|
|
5873
|
+
]
|
|
5874
|
+
}
|
|
5875
|
+
);
|
|
5876
|
+
}
|
|
5877
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4727
5878
|
"div",
|
|
4728
5879
|
{
|
|
4729
5880
|
ref: elRef,
|
|
@@ -4748,11 +5899,11 @@ function FloatingToolbar({
|
|
|
4748
5899
|
},
|
|
4749
5900
|
onMouseDown: (e) => e.stopPropagation(),
|
|
4750
5901
|
children: [
|
|
4751
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
4752
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
5902
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react5.default.Fragment, { children: [
|
|
5903
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4753
5904
|
btns.map((btn) => {
|
|
4754
5905
|
const isActive = activeCommands.has(btn.cmd);
|
|
4755
|
-
return /* @__PURE__ */ (0,
|
|
5906
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4756
5907
|
"button",
|
|
4757
5908
|
{
|
|
4758
5909
|
title: btn.title,
|
|
@@ -4778,7 +5929,7 @@ function FloatingToolbar({
|
|
|
4778
5929
|
flexShrink: 0,
|
|
4779
5930
|
padding: 6
|
|
4780
5931
|
},
|
|
4781
|
-
children: /* @__PURE__ */ (0,
|
|
5932
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4782
5933
|
"svg",
|
|
4783
5934
|
{
|
|
4784
5935
|
width: "16",
|
|
@@ -4798,7 +5949,7 @@ function FloatingToolbar({
|
|
|
4798
5949
|
);
|
|
4799
5950
|
})
|
|
4800
5951
|
] }, gi)),
|
|
4801
|
-
showEditLink ? /* @__PURE__ */ (0,
|
|
5952
|
+
showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4802
5953
|
"button",
|
|
4803
5954
|
{
|
|
4804
5955
|
type: "button",
|
|
@@ -4830,10 +5981,7 @@ function FloatingToolbar({
|
|
|
4830
5981
|
flexShrink: 0,
|
|
4831
5982
|
padding: 6
|
|
4832
5983
|
},
|
|
4833
|
-
children:
|
|
4834
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
4835
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
4836
|
-
] })
|
|
5984
|
+
children: EDIT_LINK_ICON
|
|
4837
5985
|
}
|
|
4838
5986
|
) : null
|
|
4839
5987
|
]
|
|
@@ -4850,7 +5998,7 @@ function StateToggle({
|
|
|
4850
5998
|
states,
|
|
4851
5999
|
onStateChange
|
|
4852
6000
|
}) {
|
|
4853
|
-
return /* @__PURE__ */ (0,
|
|
6001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4854
6002
|
ToggleGroup,
|
|
4855
6003
|
{
|
|
4856
6004
|
"data-ohw-state-toggle": "",
|
|
@@ -4864,18 +6012,35 @@ function StateToggle({
|
|
|
4864
6012
|
left: rect.right - 8,
|
|
4865
6013
|
transform: "translateX(-100%)"
|
|
4866
6014
|
},
|
|
4867
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
6015
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4868
6016
|
}
|
|
4869
6017
|
);
|
|
4870
6018
|
}
|
|
4871
6019
|
var contentCache = /* @__PURE__ */ new Map();
|
|
6020
|
+
function resolveSubdomain(subdomainFromQuery) {
|
|
6021
|
+
if (subdomainFromQuery) return subdomainFromQuery;
|
|
6022
|
+
if (typeof window !== "undefined") {
|
|
6023
|
+
const parts = window.location.hostname.split(".");
|
|
6024
|
+
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
6025
|
+
}
|
|
6026
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
6027
|
+
if (siteUrl) {
|
|
6028
|
+
try {
|
|
6029
|
+
const host = new URL(siteUrl).hostname;
|
|
6030
|
+
const siteParts = host.split(".");
|
|
6031
|
+
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
6032
|
+
} catch {
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
return "";
|
|
6036
|
+
}
|
|
4872
6037
|
function OhhwellsBridge() {
|
|
4873
6038
|
const pathname = (0, import_navigation.usePathname)();
|
|
4874
6039
|
const router = (0, import_navigation.useRouter)();
|
|
4875
6040
|
const searchParams = (0, import_navigation.useSearchParams)();
|
|
4876
6041
|
const isEditMode = isEditSessionActive();
|
|
4877
|
-
const [bridgeRoot, setBridgeRoot] = (0,
|
|
4878
|
-
(0,
|
|
6042
|
+
const [bridgeRoot, setBridgeRoot] = (0, import_react5.useState)(null);
|
|
6043
|
+
(0, import_react5.useEffect)(() => {
|
|
4879
6044
|
const figtreeFontId = "ohw-figtree-font";
|
|
4880
6045
|
if (!document.getElementById(figtreeFontId)) {
|
|
4881
6046
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4898,55 +6063,70 @@ function OhhwellsBridge() {
|
|
|
4898
6063
|
};
|
|
4899
6064
|
}, []);
|
|
4900
6065
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
4901
|
-
const subdomain = subdomainFromQuery
|
|
4902
|
-
|
|
4903
|
-
const parts = window.location.hostname.split(".");
|
|
4904
|
-
return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
|
|
4905
|
-
})();
|
|
4906
|
-
const postToParent = (0, import_react3.useCallback)((data) => {
|
|
6066
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
6067
|
+
const postToParent = (0, import_react5.useCallback)((data) => {
|
|
4907
6068
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4908
6069
|
window.parent.postMessage(data, "*");
|
|
4909
6070
|
}
|
|
4910
6071
|
}, []);
|
|
4911
|
-
const [fetchState, setFetchState] = (0,
|
|
4912
|
-
const autoSaveTimers = (0,
|
|
4913
|
-
const activeElRef = (0,
|
|
4914
|
-
const
|
|
4915
|
-
const
|
|
4916
|
-
const
|
|
4917
|
-
const
|
|
4918
|
-
const
|
|
4919
|
-
const
|
|
4920
|
-
const
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
6072
|
+
const [fetchState, setFetchState] = (0, import_react5.useState)("idle");
|
|
6073
|
+
const autoSaveTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
6074
|
+
const activeElRef = (0, import_react5.useRef)(null);
|
|
6075
|
+
const selectedElRef = (0, import_react5.useRef)(null);
|
|
6076
|
+
const originalContentRef = (0, import_react5.useRef)(null);
|
|
6077
|
+
const activeStateElRef = (0, import_react5.useRef)(null);
|
|
6078
|
+
const parentScrollRef = (0, import_react5.useRef)(null);
|
|
6079
|
+
const visibleViewportRef = (0, import_react5.useRef)(null);
|
|
6080
|
+
const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react5.useState)(null);
|
|
6081
|
+
const attachVisibleViewport = (0, import_react5.useCallback)((node) => {
|
|
6082
|
+
visibleViewportRef.current = node;
|
|
6083
|
+
setDialogPortalContainer(node);
|
|
6084
|
+
if (node) applyVisibleViewport(node, parentScrollRef.current);
|
|
6085
|
+
}, []);
|
|
6086
|
+
const toolbarElRef = (0, import_react5.useRef)(null);
|
|
6087
|
+
const glowElRef = (0, import_react5.useRef)(null);
|
|
6088
|
+
const hoveredImageRef = (0, import_react5.useRef)(null);
|
|
6089
|
+
const hoveredImageHasTextOverlapRef = (0, import_react5.useRef)(false);
|
|
6090
|
+
const hoveredGapRef = (0, import_react5.useRef)(null);
|
|
6091
|
+
const imageUnhoverTimerRef = (0, import_react5.useRef)(null);
|
|
6092
|
+
const imageShowTimerRef = (0, import_react5.useRef)(null);
|
|
6093
|
+
const editStylesRef = (0, import_react5.useRef)(null);
|
|
6094
|
+
const activateRef = (0, import_react5.useRef)(() => {
|
|
6095
|
+
});
|
|
6096
|
+
const deactivateRef = (0, import_react5.useRef)(() => {
|
|
4925
6097
|
});
|
|
4926
|
-
const
|
|
6098
|
+
const selectRef = (0, import_react5.useRef)(() => {
|
|
4927
6099
|
});
|
|
4928
|
-
const
|
|
6100
|
+
const deselectRef = (0, import_react5.useRef)(() => {
|
|
4929
6101
|
});
|
|
4930
|
-
const
|
|
6102
|
+
const refreshActiveCommandsRef = (0, import_react5.useRef)(() => {
|
|
6103
|
+
});
|
|
6104
|
+
const postToParentRef = (0, import_react5.useRef)(postToParent);
|
|
4931
6105
|
postToParentRef.current = postToParent;
|
|
4932
|
-
const
|
|
4933
|
-
const
|
|
4934
|
-
const [
|
|
4935
|
-
const [
|
|
4936
|
-
const
|
|
4937
|
-
|
|
4938
|
-
const [
|
|
4939
|
-
const [
|
|
4940
|
-
const
|
|
4941
|
-
const
|
|
4942
|
-
const
|
|
4943
|
-
const
|
|
4944
|
-
const
|
|
6106
|
+
const sectionsLoadedRef = (0, import_react5.useRef)(false);
|
|
6107
|
+
const pendingScheduleConfigRequests = (0, import_react5.useRef)([]);
|
|
6108
|
+
const [toolbarRect, setToolbarRect] = (0, import_react5.useState)(null);
|
|
6109
|
+
const [toolbarVariant, setToolbarVariant] = (0, import_react5.useState)("none");
|
|
6110
|
+
const toolbarVariantRef = (0, import_react5.useRef)("none");
|
|
6111
|
+
toolbarVariantRef.current = toolbarVariant;
|
|
6112
|
+
const [toggleState, setToggleState] = (0, import_react5.useState)(null);
|
|
6113
|
+
const [maxBadge, setMaxBadge] = (0, import_react5.useState)(null);
|
|
6114
|
+
const [activeCommands, setActiveCommands] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
6115
|
+
const [sectionGap, setSectionGap] = (0, import_react5.useState)(null);
|
|
6116
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react5.useState)(false);
|
|
6117
|
+
const [linkPopover, setLinkPopover] = (0, import_react5.useState)(null);
|
|
6118
|
+
const [sitePages, setSitePages] = (0, import_react5.useState)([]);
|
|
6119
|
+
const [sectionsByPath, setSectionsByPath] = (0, import_react5.useState)({});
|
|
6120
|
+
const sectionsPrefetchGenRef = (0, import_react5.useRef)(0);
|
|
6121
|
+
const setLinkPopoverRef = (0, import_react5.useRef)(setLinkPopover);
|
|
6122
|
+
const linkPopoverPanelRef = (0, import_react5.useRef)(null);
|
|
6123
|
+
const linkPopoverOpenRef = (0, import_react5.useRef)(false);
|
|
6124
|
+
const linkPopoverGraceUntilRef = (0, import_react5.useRef)(0);
|
|
4945
6125
|
setLinkPopoverRef.current = setLinkPopover;
|
|
4946
6126
|
const bumpLinkPopoverGrace = () => {
|
|
4947
6127
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
4948
6128
|
};
|
|
4949
|
-
const runSectionsPrefetch = (0,
|
|
6129
|
+
const runSectionsPrefetch = (0, import_react5.useCallback)((pages) => {
|
|
4950
6130
|
if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
|
|
4951
6131
|
const gen = ++sectionsPrefetchGenRef.current;
|
|
4952
6132
|
const paths = pages.map((p) => p.path);
|
|
@@ -4965,17 +6145,43 @@ function OhhwellsBridge() {
|
|
|
4965
6145
|
);
|
|
4966
6146
|
});
|
|
4967
6147
|
}, [isEditMode, pathname]);
|
|
4968
|
-
const runSectionsPrefetchRef = (0,
|
|
6148
|
+
const runSectionsPrefetchRef = (0, import_react5.useRef)(runSectionsPrefetch);
|
|
4969
6149
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
4970
|
-
(0,
|
|
6150
|
+
(0, import_react5.useEffect)(() => {
|
|
4971
6151
|
if (!linkPopover) return;
|
|
4972
6152
|
if (hoveredImageRef.current) {
|
|
4973
6153
|
hoveredImageRef.current = null;
|
|
4974
6154
|
hoveredImageHasTextOverlapRef.current = false;
|
|
4975
6155
|
}
|
|
6156
|
+
hoveredGapRef.current = null;
|
|
6157
|
+
setSectionGap(null);
|
|
4976
6158
|
postToParent({ type: "ow:image-unhover" });
|
|
6159
|
+
postToParent({ type: "ow:link-modal-lock", locked: true });
|
|
6160
|
+
const html = document.documentElement;
|
|
6161
|
+
const body = document.body;
|
|
6162
|
+
const prevHtmlOverflow = html.style.overflow;
|
|
6163
|
+
const prevBodyOverflow = body.style.overflow;
|
|
6164
|
+
html.style.overflow = "hidden";
|
|
6165
|
+
body.style.overflow = "hidden";
|
|
6166
|
+
const preventBackgroundScroll = (e) => {
|
|
6167
|
+
const target = e.target;
|
|
6168
|
+
if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
|
|
6169
|
+
return;
|
|
6170
|
+
}
|
|
6171
|
+
e.preventDefault();
|
|
6172
|
+
};
|
|
6173
|
+
const scrollOpts = { passive: false, capture: true };
|
|
6174
|
+
document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6175
|
+
document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6176
|
+
return () => {
|
|
6177
|
+
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6178
|
+
html.style.overflow = prevHtmlOverflow;
|
|
6179
|
+
body.style.overflow = prevBodyOverflow;
|
|
6180
|
+
document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6181
|
+
document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6182
|
+
};
|
|
4977
6183
|
}, [linkPopover, postToParent]);
|
|
4978
|
-
(0,
|
|
6184
|
+
(0, import_react5.useEffect)(() => {
|
|
4979
6185
|
if (!isEditMode) return;
|
|
4980
6186
|
const useFixtures = shouldUseDevFixtures();
|
|
4981
6187
|
if (useFixtures) {
|
|
@@ -4999,21 +6205,27 @@ function OhhwellsBridge() {
|
|
|
4999
6205
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5000
6206
|
return () => window.removeEventListener("message", onSitePages);
|
|
5001
6207
|
}, [isEditMode, postToParent]);
|
|
5002
|
-
(0,
|
|
6208
|
+
(0, import_react5.useEffect)(() => {
|
|
5003
6209
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
5004
6210
|
void loadAllSectionsManifest().then((manifest) => {
|
|
5005
6211
|
if (Object.keys(manifest).length === 0) return;
|
|
5006
6212
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
5007
6213
|
});
|
|
5008
6214
|
}, [isEditMode]);
|
|
5009
|
-
(0,
|
|
6215
|
+
(0, import_react5.useEffect)(() => {
|
|
5010
6216
|
const update = () => {
|
|
5011
|
-
const el = activeElRef.current;
|
|
6217
|
+
const el = activeElRef.current ?? selectedElRef.current;
|
|
5012
6218
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
5013
6219
|
setToggleState((prev) => {
|
|
5014
6220
|
if (!prev || !activeStateElRef.current) return prev;
|
|
5015
6221
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
5016
6222
|
});
|
|
6223
|
+
setSectionGap((prev) => {
|
|
6224
|
+
if (!prev) return prev;
|
|
6225
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
6226
|
+
if (!anchor) return prev;
|
|
6227
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
6228
|
+
});
|
|
5017
6229
|
};
|
|
5018
6230
|
const vvp = window.visualViewport;
|
|
5019
6231
|
if (!vvp) return;
|
|
@@ -5024,10 +6236,33 @@ function OhhwellsBridge() {
|
|
|
5024
6236
|
vvp.removeEventListener("resize", update);
|
|
5025
6237
|
};
|
|
5026
6238
|
}, []);
|
|
5027
|
-
const refreshStateRules = (0,
|
|
6239
|
+
const refreshStateRules = (0, import_react5.useCallback)(() => {
|
|
5028
6240
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
5029
6241
|
}, []);
|
|
5030
|
-
const
|
|
6242
|
+
const processConfigRequest = (0, import_react5.useCallback)((insertAfterVal) => {
|
|
6243
|
+
const tracker = getSectionsTracker();
|
|
6244
|
+
let entries = [];
|
|
6245
|
+
try {
|
|
6246
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
6247
|
+
} catch {
|
|
6248
|
+
}
|
|
6249
|
+
const path = window.location.pathname;
|
|
6250
|
+
const entry = entries.find(
|
|
6251
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
6252
|
+
);
|
|
6253
|
+
if (entry) {
|
|
6254
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
6255
|
+
return;
|
|
6256
|
+
}
|
|
6257
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
6258
|
+
entries.push(newEntry);
|
|
6259
|
+
tracker.textContent = JSON.stringify(entries);
|
|
6260
|
+
if (isEditMode) {
|
|
6261
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6262
|
+
}
|
|
6263
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
6264
|
+
}, [isEditMode]);
|
|
6265
|
+
const deactivate = (0, import_react5.useCallback)(() => {
|
|
5031
6266
|
const el = activeElRef.current;
|
|
5032
6267
|
if (!el) return;
|
|
5033
6268
|
const key = el.dataset.ohwKey;
|
|
@@ -5047,19 +6282,41 @@ function OhhwellsBridge() {
|
|
|
5047
6282
|
}
|
|
5048
6283
|
el.removeAttribute("contenteditable");
|
|
5049
6284
|
activeElRef.current = null;
|
|
5050
|
-
|
|
6285
|
+
if (!selectedElRef.current) {
|
|
6286
|
+
setToolbarRect(null);
|
|
6287
|
+
setToolbarVariant("none");
|
|
6288
|
+
}
|
|
5051
6289
|
setMaxBadge(null);
|
|
5052
6290
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
5053
6291
|
setToolbarShowEditLink(false);
|
|
5054
6292
|
postToParent({ type: "ow:exit-edit" });
|
|
5055
6293
|
}, [postToParent]);
|
|
5056
|
-
const
|
|
6294
|
+
const deselect = (0, import_react5.useCallback)(() => {
|
|
6295
|
+
selectedElRef.current = null;
|
|
6296
|
+
if (!activeElRef.current) {
|
|
6297
|
+
setToolbarRect(null);
|
|
6298
|
+
setToolbarVariant("none");
|
|
6299
|
+
}
|
|
6300
|
+
}, []);
|
|
6301
|
+
const select = (0, import_react5.useCallback)((anchor) => {
|
|
6302
|
+
if (!isNavbarButton(anchor)) return;
|
|
6303
|
+
if (activeElRef.current) deactivate();
|
|
6304
|
+
selectedElRef.current = anchor;
|
|
6305
|
+
clearHrefKeyHover(anchor);
|
|
6306
|
+
setToolbarVariant("link-action");
|
|
6307
|
+
setToolbarRect(anchor.getBoundingClientRect());
|
|
6308
|
+
setToolbarShowEditLink(false);
|
|
6309
|
+
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6310
|
+
}, [deactivate]);
|
|
6311
|
+
const activate = (0, import_react5.useCallback)((el) => {
|
|
5057
6312
|
if (activeElRef.current === el) return;
|
|
6313
|
+
selectedElRef.current = null;
|
|
5058
6314
|
deactivate();
|
|
5059
6315
|
if (hoveredImageRef.current) {
|
|
5060
6316
|
hoveredImageRef.current = null;
|
|
5061
6317
|
postToParentRef.current({ type: "ow:image-unhover" });
|
|
5062
6318
|
}
|
|
6319
|
+
setToolbarVariant("rich-text");
|
|
5063
6320
|
el.setAttribute("contenteditable", "true");
|
|
5064
6321
|
el.removeAttribute("data-ohw-hovered");
|
|
5065
6322
|
activeElRef.current = el;
|
|
@@ -5072,7 +6329,9 @@ function OhhwellsBridge() {
|
|
|
5072
6329
|
}, [deactivate, postToParent]);
|
|
5073
6330
|
activateRef.current = activate;
|
|
5074
6331
|
deactivateRef.current = deactivate;
|
|
5075
|
-
|
|
6332
|
+
selectRef.current = select;
|
|
6333
|
+
deselectRef.current = deselect;
|
|
6334
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
5076
6335
|
if (!subdomain || isEditMode) {
|
|
5077
6336
|
setFetchState("done");
|
|
5078
6337
|
return;
|
|
@@ -5080,6 +6339,7 @@ function OhhwellsBridge() {
|
|
|
5080
6339
|
const applyContent = (content) => {
|
|
5081
6340
|
const imageLoads = [];
|
|
5082
6341
|
for (const [key, val] of Object.entries(content)) {
|
|
6342
|
+
if (key === "__ohw_sections") continue;
|
|
5083
6343
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5084
6344
|
if (el.dataset.ohwEditable === "image") {
|
|
5085
6345
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5101,6 +6361,9 @@ function OhhwellsBridge() {
|
|
|
5101
6361
|
});
|
|
5102
6362
|
applyLinkByKey(key, val);
|
|
5103
6363
|
}
|
|
6364
|
+
initSectionsFromContent(content, true);
|
|
6365
|
+
sectionsLoadedRef.current = true;
|
|
6366
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5104
6367
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5105
6368
|
}) : Promise.resolve();
|
|
5106
6369
|
};
|
|
@@ -5125,12 +6388,15 @@ function OhhwellsBridge() {
|
|
|
5125
6388
|
cancelled = true;
|
|
5126
6389
|
};
|
|
5127
6390
|
}, [subdomain, isEditMode, pathname]);
|
|
5128
|
-
(0,
|
|
6391
|
+
(0, import_react5.useEffect)(() => {
|
|
5129
6392
|
if (!subdomain || isEditMode) return;
|
|
6393
|
+
let debounceTimer = null;
|
|
5130
6394
|
const applyFromCache = () => {
|
|
5131
6395
|
const content = contentCache.get(subdomain);
|
|
5132
6396
|
if (!content) return;
|
|
6397
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
5133
6398
|
for (const [key, val] of Object.entries(content)) {
|
|
6399
|
+
if (key === "__ohw_sections") continue;
|
|
5134
6400
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5135
6401
|
if (el.dataset.ohwEditable === "image") {
|
|
5136
6402
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5147,21 +6413,34 @@ function OhhwellsBridge() {
|
|
|
5147
6413
|
applyLinkByKey(key, val);
|
|
5148
6414
|
}
|
|
5149
6415
|
};
|
|
5150
|
-
|
|
5151
|
-
|
|
6416
|
+
const scheduleApply = () => {
|
|
6417
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6418
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6419
|
+
};
|
|
6420
|
+
scheduleApply();
|
|
6421
|
+
const observer = new MutationObserver(scheduleApply);
|
|
5152
6422
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
5153
|
-
return () =>
|
|
5154
|
-
|
|
5155
|
-
|
|
6423
|
+
return () => {
|
|
6424
|
+
observer.disconnect();
|
|
6425
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6426
|
+
};
|
|
6427
|
+
}, [subdomain, isEditMode, pathname]);
|
|
6428
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
5156
6429
|
const el = document.getElementById("ohw-loader");
|
|
5157
6430
|
if (!el) return;
|
|
5158
6431
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
5159
6432
|
el.style.display = visible ? "flex" : "none";
|
|
5160
6433
|
}, [subdomain, fetchState]);
|
|
5161
|
-
(0,
|
|
6434
|
+
(0, import_react5.useEffect)(() => {
|
|
5162
6435
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
5163
6436
|
}, [pathname, postToParent]);
|
|
5164
|
-
(0,
|
|
6437
|
+
(0, import_react5.useEffect)(() => {
|
|
6438
|
+
if (!isEditMode) return;
|
|
6439
|
+
setLinkPopover(null);
|
|
6440
|
+
deselectRef.current();
|
|
6441
|
+
deactivateRef.current();
|
|
6442
|
+
}, [pathname, isEditMode]);
|
|
6443
|
+
(0, import_react5.useEffect)(() => {
|
|
5165
6444
|
if (!isEditMode) return;
|
|
5166
6445
|
const measure = () => {
|
|
5167
6446
|
const h = document.body.scrollHeight;
|
|
@@ -5185,7 +6464,7 @@ function OhhwellsBridge() {
|
|
|
5185
6464
|
window.removeEventListener("resize", handleResize);
|
|
5186
6465
|
};
|
|
5187
6466
|
}, [pathname, isEditMode, postToParent]);
|
|
5188
|
-
(0,
|
|
6467
|
+
(0, import_react5.useEffect)(() => {
|
|
5189
6468
|
if (!subdomainFromQuery || isEditMode) return;
|
|
5190
6469
|
const handleClick = (e) => {
|
|
5191
6470
|
const anchor = e.target.closest("a");
|
|
@@ -5201,7 +6480,7 @@ function OhhwellsBridge() {
|
|
|
5201
6480
|
document.addEventListener("click", handleClick, true);
|
|
5202
6481
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5203
6482
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5204
|
-
(0,
|
|
6483
|
+
(0, import_react5.useEffect)(() => {
|
|
5205
6484
|
if (!isEditMode) {
|
|
5206
6485
|
editStylesRef.current?.base.remove();
|
|
5207
6486
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5271,12 +6550,12 @@ function OhhwellsBridge() {
|
|
|
5271
6550
|
if (editable.dataset.ohwEditable === "link") {
|
|
5272
6551
|
e.preventDefault();
|
|
5273
6552
|
e.stopPropagation();
|
|
6553
|
+
deselectRef.current();
|
|
5274
6554
|
deactivateRef.current();
|
|
5275
6555
|
bumpLinkPopoverGrace();
|
|
5276
6556
|
setLinkPopoverRef.current({
|
|
5277
6557
|
key: editable.dataset.ohwKey ?? "",
|
|
5278
|
-
target: getLinkHref(editable)
|
|
5279
|
-
rect: editable.getBoundingClientRect()
|
|
6558
|
+
target: getLinkHref(editable)
|
|
5280
6559
|
});
|
|
5281
6560
|
return;
|
|
5282
6561
|
}
|
|
@@ -5286,11 +6565,31 @@ function OhhwellsBridge() {
|
|
|
5286
6565
|
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
|
|
5287
6566
|
return;
|
|
5288
6567
|
}
|
|
6568
|
+
const hrefCtx = getHrefKeyFromElement(editable);
|
|
6569
|
+
if (hrefCtx && isNavbarButton(hrefCtx.anchor)) {
|
|
6570
|
+
e.preventDefault();
|
|
6571
|
+
e.stopPropagation();
|
|
6572
|
+
const { anchor: anchor2 } = hrefCtx;
|
|
6573
|
+
if (selectedElRef.current === anchor2) {
|
|
6574
|
+
activateRef.current(editable);
|
|
6575
|
+
return;
|
|
6576
|
+
}
|
|
6577
|
+
selectRef.current(anchor2);
|
|
6578
|
+
return;
|
|
6579
|
+
}
|
|
5289
6580
|
e.preventDefault();
|
|
5290
6581
|
e.stopPropagation();
|
|
5291
6582
|
activateRef.current(editable);
|
|
5292
6583
|
return;
|
|
5293
6584
|
}
|
|
6585
|
+
const hrefAnchor = target.closest("[data-ohw-href-key]");
|
|
6586
|
+
if (hrefAnchor && isNavbarButton(hrefAnchor)) {
|
|
6587
|
+
e.preventDefault();
|
|
6588
|
+
e.stopPropagation();
|
|
6589
|
+
if (selectedElRef.current === hrefAnchor) return;
|
|
6590
|
+
selectRef.current(hrefAnchor);
|
|
6591
|
+
return;
|
|
6592
|
+
}
|
|
5294
6593
|
if (activeElRef.current) {
|
|
5295
6594
|
const sel = window.getSelection();
|
|
5296
6595
|
if (sel && !sel.isCollapsed) {
|
|
@@ -5305,15 +6604,22 @@ function OhhwellsBridge() {
|
|
|
5305
6604
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
5306
6605
|
if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
|
|
5307
6606
|
}
|
|
6607
|
+
if (linkPopoverOpenRef.current && selectedElRef.current) {
|
|
6608
|
+
const selected = selectedElRef.current;
|
|
6609
|
+
if (selected === target || selected.contains(target)) return;
|
|
6610
|
+
}
|
|
5308
6611
|
if (linkPopoverOpenRef.current) {
|
|
5309
6612
|
setLinkPopoverRef.current(null);
|
|
5310
6613
|
return;
|
|
5311
6614
|
}
|
|
6615
|
+
deselectRef.current();
|
|
5312
6616
|
deactivateRef.current();
|
|
5313
6617
|
};
|
|
5314
6618
|
const handleMouseOver = (e) => {
|
|
5315
6619
|
const editable = e.target.closest("[data-ohw-editable]");
|
|
5316
6620
|
if (!editable) return;
|
|
6621
|
+
const selected = selectedElRef.current;
|
|
6622
|
+
if (selected && (selected === editable || selected.contains(editable))) return;
|
|
5317
6623
|
if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
|
|
5318
6624
|
editable.setAttribute("data-ohw-hovered", "");
|
|
5319
6625
|
}
|
|
@@ -5449,7 +6755,7 @@ function OhhwellsBridge() {
|
|
|
5449
6755
|
}
|
|
5450
6756
|
return;
|
|
5451
6757
|
}
|
|
5452
|
-
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]")) {
|
|
6758
|
+
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]") || topEl?.closest("[data-ohw-link-page-dropdown]")) {
|
|
5453
6759
|
if (hoveredImageRef.current) {
|
|
5454
6760
|
hoveredImageRef.current = null;
|
|
5455
6761
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -5502,6 +6808,15 @@ function OhhwellsBridge() {
|
|
|
5502
6808
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
5503
6809
|
return;
|
|
5504
6810
|
}
|
|
6811
|
+
if (hoveredGapRef.current) {
|
|
6812
|
+
if (hoveredImageRef.current) {
|
|
6813
|
+
hoveredImageRef.current = null;
|
|
6814
|
+
resumeAnimTracks();
|
|
6815
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6816
|
+
}
|
|
6817
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6818
|
+
return;
|
|
6819
|
+
}
|
|
5505
6820
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5506
6821
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
5507
6822
|
hoveredImageRef.current = imgEl;
|
|
@@ -5567,8 +6882,37 @@ function OhhwellsBridge() {
|
|
|
5567
6882
|
}
|
|
5568
6883
|
}
|
|
5569
6884
|
};
|
|
6885
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6886
|
+
if (linkPopoverOpenRef.current) {
|
|
6887
|
+
if (hoveredGapRef.current) {
|
|
6888
|
+
hoveredGapRef.current = null;
|
|
6889
|
+
setSectionGap(null);
|
|
6890
|
+
}
|
|
6891
|
+
return;
|
|
6892
|
+
}
|
|
6893
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6894
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6895
|
+
const ZONE = 20;
|
|
6896
|
+
for (let i = 0; i < sections.length; i++) {
|
|
6897
|
+
const a = sections[i];
|
|
6898
|
+
const b = sections[i + 1] ?? null;
|
|
6899
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6900
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6901
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6902
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6903
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6904
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6905
|
+
return;
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
if (hoveredGapRef.current) {
|
|
6909
|
+
hoveredGapRef.current = null;
|
|
6910
|
+
setSectionGap(null);
|
|
6911
|
+
}
|
|
6912
|
+
};
|
|
5570
6913
|
const handleMouseMove = (e) => {
|
|
5571
6914
|
const { clientX, clientY } = e;
|
|
6915
|
+
probeSectionGapAt(clientX, clientY);
|
|
5572
6916
|
probeImageAt(clientX, clientY);
|
|
5573
6917
|
probeHoverCardsAt(clientX, clientY);
|
|
5574
6918
|
};
|
|
@@ -5576,6 +6920,7 @@ function OhhwellsBridge() {
|
|
|
5576
6920
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
5577
6921
|
const { clientX, clientY } = e.data;
|
|
5578
6922
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6923
|
+
probeSectionGapAt(clientX, clientY);
|
|
5579
6924
|
probeImageAt(clientX, clientY);
|
|
5580
6925
|
probeHoverCardsAt(clientX, clientY);
|
|
5581
6926
|
};
|
|
@@ -5733,7 +7078,12 @@ function OhhwellsBridge() {
|
|
|
5733
7078
|
if (e.data?.type !== "ow:hydrate") return;
|
|
5734
7079
|
const content = e.data.content;
|
|
5735
7080
|
if (!content) return;
|
|
7081
|
+
let sectionsJson = null;
|
|
5736
7082
|
for (const [key, val] of Object.entries(content)) {
|
|
7083
|
+
if (key === "__ohw_sections") {
|
|
7084
|
+
sectionsJson = val;
|
|
7085
|
+
continue;
|
|
7086
|
+
}
|
|
5737
7087
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5738
7088
|
if (el.dataset.ohwEditable === "image") {
|
|
5739
7089
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5748,6 +7098,11 @@ function OhhwellsBridge() {
|
|
|
5748
7098
|
});
|
|
5749
7099
|
applyLinkByKey(key, val);
|
|
5750
7100
|
}
|
|
7101
|
+
if (sectionsJson) {
|
|
7102
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
7103
|
+
sectionsLoadedRef.current = true;
|
|
7104
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
7105
|
+
}
|
|
5751
7106
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5752
7107
|
};
|
|
5753
7108
|
window.addEventListener("message", handleHydrate);
|
|
@@ -5758,6 +7113,7 @@ function OhhwellsBridge() {
|
|
|
5758
7113
|
setLinkPopoverRef.current(null);
|
|
5759
7114
|
return;
|
|
5760
7115
|
}
|
|
7116
|
+
deselectRef.current();
|
|
5761
7117
|
deactivateRef.current();
|
|
5762
7118
|
};
|
|
5763
7119
|
window.addEventListener("message", handleDeactivate);
|
|
@@ -5766,6 +7122,10 @@ function OhhwellsBridge() {
|
|
|
5766
7122
|
setLinkPopoverRef.current(null);
|
|
5767
7123
|
return;
|
|
5768
7124
|
}
|
|
7125
|
+
if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
|
|
7126
|
+
deselectRef.current();
|
|
7127
|
+
return;
|
|
7128
|
+
}
|
|
5769
7129
|
if (e.key !== "Escape") return;
|
|
5770
7130
|
const el = activeElRef.current;
|
|
5771
7131
|
if (el && originalContentRef.current !== null) {
|
|
@@ -5775,13 +7135,16 @@ function OhhwellsBridge() {
|
|
|
5775
7135
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
|
|
5776
7136
|
}
|
|
5777
7137
|
}
|
|
7138
|
+
deselectRef.current();
|
|
5778
7139
|
deactivateRef.current();
|
|
5779
7140
|
};
|
|
5780
7141
|
const handleScroll = () => {
|
|
5781
|
-
|
|
5782
|
-
|
|
7142
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7143
|
+
if (focusEl) {
|
|
7144
|
+
const r2 = focusEl.getBoundingClientRect();
|
|
5783
7145
|
applyToolbarPos(r2);
|
|
5784
|
-
|
|
7146
|
+
setToolbarRect(r2);
|
|
7147
|
+
setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
|
|
5785
7148
|
}
|
|
5786
7149
|
if (activeStateElRef.current) {
|
|
5787
7150
|
const rect = activeStateElRef.current.getBoundingClientRect();
|
|
@@ -5800,7 +7163,63 @@ function OhhwellsBridge() {
|
|
|
5800
7163
|
};
|
|
5801
7164
|
const handleSave = (e) => {
|
|
5802
7165
|
if (e.data?.type !== "ow:save") return;
|
|
5803
|
-
|
|
7166
|
+
const nodes = collectEditableNodes();
|
|
7167
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
7168
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
7169
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
7170
|
+
};
|
|
7171
|
+
const handleInsertSection = (e) => {
|
|
7172
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
7173
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
7174
|
+
if (widgetType !== "scheduling") return;
|
|
7175
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
7176
|
+
if (inserted) {
|
|
7177
|
+
const tracker = getSectionsTracker();
|
|
7178
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
7179
|
+
const h = document.documentElement.scrollHeight;
|
|
7180
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
7181
|
+
}
|
|
7182
|
+
};
|
|
7183
|
+
const handleSwitchSchedule = (e) => {
|
|
7184
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
7185
|
+
const scheduleId = e.data.scheduleId;
|
|
7186
|
+
const insertAfter = e.data.insertAfter;
|
|
7187
|
+
if (!scheduleId || !insertAfter) return;
|
|
7188
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7189
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7190
|
+
};
|
|
7191
|
+
const handleScheduleLinked = (e) => {
|
|
7192
|
+
if (e.data?.type !== "ow:schedule-linked") return;
|
|
7193
|
+
const scheduleId = e.data.scheduleId;
|
|
7194
|
+
const insertAfter = e.data.insertAfter;
|
|
7195
|
+
if (!scheduleId || !insertAfter) return;
|
|
7196
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7197
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7198
|
+
};
|
|
7199
|
+
const handleClearSchedulingWidget = (e) => {
|
|
7200
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
7201
|
+
const insertAfter = e.data.insertAfter;
|
|
7202
|
+
if (!insertAfter) return;
|
|
7203
|
+
const text = updateSectionScheduleId(insertAfter, null);
|
|
7204
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7205
|
+
};
|
|
7206
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
7207
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
7208
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
7209
|
+
el.remove();
|
|
7210
|
+
});
|
|
7211
|
+
const tracker = getSectionsTracker();
|
|
7212
|
+
let sections = [];
|
|
7213
|
+
try {
|
|
7214
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
7215
|
+
} catch {
|
|
7216
|
+
}
|
|
7217
|
+
const currentPath = window.location.pathname;
|
|
7218
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
7219
|
+
tracker.textContent = JSON.stringify(updated);
|
|
7220
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
7221
|
+
const h = document.documentElement.scrollHeight;
|
|
7222
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5804
7223
|
};
|
|
5805
7224
|
const handleSelectionChange = () => {
|
|
5806
7225
|
if (!activeElRef.current) return;
|
|
@@ -5850,8 +7269,9 @@ function OhhwellsBridge() {
|
|
|
5850
7269
|
};
|
|
5851
7270
|
const applyToolbarPos = (rect) => {
|
|
5852
7271
|
const ps = parentScrollRef.current;
|
|
7272
|
+
const approxW = toolbarVariantRef.current === "link-action" ? 120 : 330;
|
|
5853
7273
|
if (toolbarElRef.current) {
|
|
5854
|
-
const { top, left, transform } = calcToolbarPos(rect, ps);
|
|
7274
|
+
const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
|
|
5855
7275
|
toolbarElRef.current.style.top = `${top}px`;
|
|
5856
7276
|
toolbarElRef.current.style.left = `${left}px`;
|
|
5857
7277
|
toolbarElRef.current.style.transform = transform;
|
|
@@ -5866,7 +7286,11 @@ function OhhwellsBridge() {
|
|
|
5866
7286
|
if (e.data?.type !== "ow:parent-scroll") return;
|
|
5867
7287
|
const { iframeOffsetTop, headerH, canvasH } = e.data;
|
|
5868
7288
|
parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
|
|
5869
|
-
if (
|
|
7289
|
+
if (visibleViewportRef.current) {
|
|
7290
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7291
|
+
}
|
|
7292
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7293
|
+
if (focusEl) applyToolbarPos(focusEl.getBoundingClientRect());
|
|
5870
7294
|
};
|
|
5871
7295
|
const handleClickAt = (e) => {
|
|
5872
7296
|
if (e.data?.type !== "ow:click-at") return;
|
|
@@ -5905,12 +7329,23 @@ function OhhwellsBridge() {
|
|
|
5905
7329
|
deactivateRef.current();
|
|
5906
7330
|
};
|
|
5907
7331
|
window.addEventListener("message", handleSave);
|
|
7332
|
+
window.addEventListener("message", handleInsertSection);
|
|
7333
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
7334
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
7335
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
7336
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
5908
7337
|
window.addEventListener("message", handleImageUrl);
|
|
5909
7338
|
window.addEventListener("message", handleAnimLock);
|
|
5910
7339
|
window.addEventListener("message", handleCanvasHeight);
|
|
5911
7340
|
window.addEventListener("message", handleParentScroll);
|
|
5912
7341
|
window.addEventListener("message", handlePointerSync);
|
|
5913
7342
|
window.addEventListener("message", handleClickAt);
|
|
7343
|
+
const handleViewportResize = () => {
|
|
7344
|
+
if (visibleViewportRef.current) {
|
|
7345
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7346
|
+
}
|
|
7347
|
+
};
|
|
7348
|
+
window.addEventListener("resize", handleViewportResize, { passive: true });
|
|
5914
7349
|
document.addEventListener("click", handleClick, true);
|
|
5915
7350
|
document.addEventListener("paste", handlePaste, true);
|
|
5916
7351
|
document.addEventListener("input", handleInput, true);
|
|
@@ -5939,10 +7374,16 @@ function OhhwellsBridge() {
|
|
|
5939
7374
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
5940
7375
|
window.removeEventListener("scroll", handleScroll, true);
|
|
5941
7376
|
window.removeEventListener("message", handleSave);
|
|
7377
|
+
window.removeEventListener("message", handleInsertSection);
|
|
7378
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
7379
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
7380
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
7381
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
5942
7382
|
window.removeEventListener("message", handleImageUrl);
|
|
5943
7383
|
window.removeEventListener("message", handleAnimLock);
|
|
5944
7384
|
window.removeEventListener("message", handleCanvasHeight);
|
|
5945
7385
|
window.removeEventListener("message", handleParentScroll);
|
|
7386
|
+
window.removeEventListener("resize", handleViewportResize);
|
|
5946
7387
|
window.removeEventListener("message", handlePointerSync);
|
|
5947
7388
|
window.removeEventListener("message", handleClickAt);
|
|
5948
7389
|
window.removeEventListener("message", handleHydrate);
|
|
@@ -5953,7 +7394,23 @@ function OhhwellsBridge() {
|
|
|
5953
7394
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5954
7395
|
};
|
|
5955
7396
|
}, [isEditMode, refreshStateRules]);
|
|
5956
|
-
(0,
|
|
7397
|
+
(0, import_react5.useEffect)(() => {
|
|
7398
|
+
const handler = (e) => {
|
|
7399
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7400
|
+
const insertAfterVal = e.data.insertAfter;
|
|
7401
|
+
if (!insertAfterVal) return;
|
|
7402
|
+
if (!sectionsLoadedRef.current) {
|
|
7403
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
7404
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
7405
|
+
}
|
|
7406
|
+
return;
|
|
7407
|
+
}
|
|
7408
|
+
processConfigRequest(insertAfterVal);
|
|
7409
|
+
};
|
|
7410
|
+
window.addEventListener("message", handler);
|
|
7411
|
+
return () => window.removeEventListener("message", handler);
|
|
7412
|
+
}, [processConfigRequest]);
|
|
7413
|
+
(0, import_react5.useEffect)(() => {
|
|
5957
7414
|
if (!isEditMode) return;
|
|
5958
7415
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5959
7416
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5982,19 +7439,19 @@ function OhhwellsBridge() {
|
|
|
5982
7439
|
clearTimeout(timer);
|
|
5983
7440
|
};
|
|
5984
7441
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5985
|
-
(0,
|
|
7442
|
+
(0, import_react5.useEffect)(() => {
|
|
5986
7443
|
scrollToHashSectionWhenReady();
|
|
5987
7444
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
5988
7445
|
window.addEventListener("hashchange", onHashChange);
|
|
5989
7446
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
5990
7447
|
}, [pathname]);
|
|
5991
|
-
const handleCommand = (0,
|
|
7448
|
+
const handleCommand = (0, import_react5.useCallback)((cmd) => {
|
|
5992
7449
|
document.execCommand(cmd, false);
|
|
5993
7450
|
activeElRef.current?.focus();
|
|
5994
7451
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
5995
7452
|
refreshActiveCommandsRef.current();
|
|
5996
7453
|
}, []);
|
|
5997
|
-
const handleStateChange = (0,
|
|
7454
|
+
const handleStateChange = (0, import_react5.useCallback)((state) => {
|
|
5998
7455
|
if (!activeStateElRef.current) return;
|
|
5999
7456
|
const el = activeStateElRef.current;
|
|
6000
7457
|
if (state === "Default") {
|
|
@@ -6007,18 +7464,30 @@ function OhhwellsBridge() {
|
|
|
6007
7464
|
}
|
|
6008
7465
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
6009
7466
|
}, [deactivate]);
|
|
6010
|
-
const closeLinkPopover = (0,
|
|
6011
|
-
const openLinkPopoverForActive = (0,
|
|
7467
|
+
const closeLinkPopover = (0, import_react5.useCallback)(() => setLinkPopover(null), []);
|
|
7468
|
+
const openLinkPopoverForActive = (0, import_react5.useCallback)(() => {
|
|
6012
7469
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6013
7470
|
if (!hrefCtx) return;
|
|
6014
7471
|
bumpLinkPopoverGrace();
|
|
6015
7472
|
setLinkPopover({
|
|
6016
7473
|
key: hrefCtx.key,
|
|
6017
|
-
target: getLinkHref(hrefCtx.anchor)
|
|
6018
|
-
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
7474
|
+
target: getLinkHref(hrefCtx.anchor)
|
|
6019
7475
|
});
|
|
6020
|
-
|
|
6021
|
-
|
|
7476
|
+
deactivate();
|
|
7477
|
+
}, [deactivate]);
|
|
7478
|
+
const openLinkPopoverForSelected = (0, import_react5.useCallback)(() => {
|
|
7479
|
+
const anchor = selectedElRef.current;
|
|
7480
|
+
if (!anchor) return;
|
|
7481
|
+
const key = anchor.getAttribute("data-ohw-href-key");
|
|
7482
|
+
if (!key) return;
|
|
7483
|
+
bumpLinkPopoverGrace();
|
|
7484
|
+
setLinkPopover({
|
|
7485
|
+
key,
|
|
7486
|
+
target: getLinkHref(anchor)
|
|
7487
|
+
});
|
|
7488
|
+
deselect();
|
|
7489
|
+
}, [deselect]);
|
|
7490
|
+
const handleLinkPopoverSubmit = (0, import_react5.useCallback)(
|
|
6022
7491
|
(target) => {
|
|
6023
7492
|
if (!linkPopover) return;
|
|
6024
7493
|
const { key } = linkPopover;
|
|
@@ -6031,13 +7500,15 @@ function OhhwellsBridge() {
|
|
|
6031
7500
|
const showEditLink = toolbarShowEditLink;
|
|
6032
7501
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
6033
7502
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
6034
|
-
return bridgeRoot ? (0,
|
|
6035
|
-
/* @__PURE__ */ (0,
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
/* @__PURE__ */ (0,
|
|
7503
|
+
return bridgeRoot ? (0, import_react_dom2.createPortal)(
|
|
7504
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
7505
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
7506
|
+
toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
7507
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
7508
|
+
toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6039
7509
|
FloatingToolbar,
|
|
6040
7510
|
{
|
|
7511
|
+
variant: "rich-text",
|
|
6041
7512
|
rect: toolbarRect,
|
|
6042
7513
|
parentScroll: parentScrollRef.current,
|
|
6043
7514
|
elRef: toolbarElRef,
|
|
@@ -6046,9 +7517,21 @@ function OhhwellsBridge() {
|
|
|
6046
7517
|
showEditLink,
|
|
6047
7518
|
onEditLink: openLinkPopoverForActive
|
|
6048
7519
|
}
|
|
7520
|
+
),
|
|
7521
|
+
toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7522
|
+
FloatingToolbar,
|
|
7523
|
+
{
|
|
7524
|
+
variant: "link-action",
|
|
7525
|
+
rect: toolbarRect,
|
|
7526
|
+
parentScroll: parentScrollRef.current,
|
|
7527
|
+
elRef: toolbarElRef,
|
|
7528
|
+
onCommand: handleCommand,
|
|
7529
|
+
activeCommands,
|
|
7530
|
+
onEditLink: openLinkPopoverForSelected
|
|
7531
|
+
}
|
|
6049
7532
|
)
|
|
6050
7533
|
] }),
|
|
6051
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
7534
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
6052
7535
|
"div",
|
|
6053
7536
|
{
|
|
6054
7537
|
"data-ohw-max-badge": "",
|
|
@@ -6074,7 +7557,7 @@ function OhhwellsBridge() {
|
|
|
6074
7557
|
]
|
|
6075
7558
|
}
|
|
6076
7559
|
),
|
|
6077
|
-
toggleState && /* @__PURE__ */ (0,
|
|
7560
|
+
toggleState && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6078
7561
|
StateToggle,
|
|
6079
7562
|
{
|
|
6080
7563
|
rect: toggleState.rect,
|
|
@@ -6083,12 +7566,36 @@ function OhhwellsBridge() {
|
|
|
6083
7566
|
onStateChange: handleStateChange
|
|
6084
7567
|
}
|
|
6085
7568
|
),
|
|
6086
|
-
|
|
7569
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
7570
|
+
"div",
|
|
7571
|
+
{
|
|
7572
|
+
"data-ohw-section-insert-line": "",
|
|
7573
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7574
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7575
|
+
children: [
|
|
7576
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7577
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7578
|
+
Badge,
|
|
7579
|
+
{
|
|
7580
|
+
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",
|
|
7581
|
+
onClick: () => {
|
|
7582
|
+
window.parent.postMessage(
|
|
7583
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7584
|
+
"*"
|
|
7585
|
+
);
|
|
7586
|
+
},
|
|
7587
|
+
children: "Add Section"
|
|
7588
|
+
}
|
|
7589
|
+
),
|
|
7590
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7591
|
+
]
|
|
7592
|
+
}
|
|
7593
|
+
),
|
|
7594
|
+
linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6087
7595
|
LinkPopover,
|
|
6088
7596
|
{
|
|
6089
|
-
rect: linkPopover.rect,
|
|
6090
|
-
parentScroll: parentScrollRef.current,
|
|
6091
7597
|
panelRef: linkPopoverPanelRef,
|
|
7598
|
+
portalContainer: dialogPortalContainer,
|
|
6092
7599
|
open: true,
|
|
6093
7600
|
mode: "edit",
|
|
6094
7601
|
pages: sitePages,
|
|
@@ -6097,7 +7604,8 @@ function OhhwellsBridge() {
|
|
|
6097
7604
|
initialTarget: linkPopover.target,
|
|
6098
7605
|
onClose: closeLinkPopover,
|
|
6099
7606
|
onSubmit: handleLinkPopoverSubmit
|
|
6100
|
-
}
|
|
7607
|
+
},
|
|
7608
|
+
`${linkPopover.key}-${pathname}`
|
|
6101
7609
|
) : null
|
|
6102
7610
|
] }),
|
|
6103
7611
|
bridgeRoot
|
|
@@ -6108,6 +7616,7 @@ function OhhwellsBridge() {
|
|
|
6108
7616
|
LinkEditorPanel,
|
|
6109
7617
|
LinkPopover,
|
|
6110
7618
|
OhhwellsBridge,
|
|
7619
|
+
SchedulingWidget,
|
|
6111
7620
|
Toggle,
|
|
6112
7621
|
ToggleGroup,
|
|
6113
7622
|
ToggleGroupItem,
|