@ohhwells/bridge 0.1.29 → 0.1.31-next.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -304
- package/dist/index.cjs +1864 -328
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.js +1840 -305
- package/dist/index.js.map +1 -1
- package/dist/styles.css +505 -14
- package/package.json +48 -47
package/dist/index.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
|
}
|
|
@@ -4651,6 +5652,8 @@ var ICONS = {
|
|
|
4651
5652
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
4652
5653
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
4653
5654
|
};
|
|
5655
|
+
var TOOLBAR_PILL_PADDING = 2;
|
|
5656
|
+
var TOOLBAR_TOGGLE_GAP = 4;
|
|
4654
5657
|
var TOOLBAR_GROUPS = [
|
|
4655
5658
|
[
|
|
4656
5659
|
{ cmd: "bold", title: "Bold" },
|
|
@@ -4670,7 +5673,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4670
5673
|
];
|
|
4671
5674
|
function GlowFrame({ rect, elRef }) {
|
|
4672
5675
|
const GAP = 6;
|
|
4673
|
-
return /* @__PURE__ */ (0,
|
|
5676
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4674
5677
|
"div",
|
|
4675
5678
|
{
|
|
4676
5679
|
ref: elRef,
|
|
@@ -4689,41 +5692,191 @@ function GlowFrame({ rect, elRef }) {
|
|
|
4689
5692
|
}
|
|
4690
5693
|
);
|
|
4691
5694
|
}
|
|
4692
|
-
function
|
|
5695
|
+
function getIframeVisibleClip(parentScroll) {
|
|
5696
|
+
if (!parentScroll) return null;
|
|
5697
|
+
const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
|
|
5698
|
+
const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
|
|
5699
|
+
const clipBottom = Math.min(
|
|
5700
|
+
window.innerHeight,
|
|
5701
|
+
visibleCanvasTop + canvasH - iframeOffsetTop
|
|
5702
|
+
);
|
|
5703
|
+
return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
|
|
5704
|
+
}
|
|
5705
|
+
function applyVisibleViewport(el, parentScroll) {
|
|
5706
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5707
|
+
const top = clip?.top ?? 0;
|
|
5708
|
+
const height = clip ? clip.bottom - clip.top : window.innerHeight;
|
|
5709
|
+
el.style.top = `${top}px`;
|
|
5710
|
+
el.style.height = `${height}px`;
|
|
5711
|
+
}
|
|
5712
|
+
function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
5713
|
+
const isAbove = transform.includes("translateY(-100%)");
|
|
5714
|
+
let visualTop = isAbove ? top - approxH : top;
|
|
5715
|
+
let visualBottom = isAbove ? top : top + approxH;
|
|
5716
|
+
const minTop = clip.top + gap;
|
|
5717
|
+
const maxBottom = clip.bottom - gap;
|
|
5718
|
+
if (visualTop >= minTop && visualBottom <= maxBottom) {
|
|
5719
|
+
return { top, transform };
|
|
5720
|
+
}
|
|
5721
|
+
const belowTop = rect.bottom + gap;
|
|
5722
|
+
if (belowTop + approxH <= maxBottom && belowTop >= minTop) {
|
|
5723
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5724
|
+
}
|
|
5725
|
+
const aboveTop = rect.top - gap;
|
|
5726
|
+
if (aboveTop - approxH >= minTop && aboveTop <= maxBottom) {
|
|
5727
|
+
return { top: aboveTop, transform: "translateX(-50%) translateY(-100%)" };
|
|
5728
|
+
}
|
|
5729
|
+
if (belowTop + approxH <= maxBottom) {
|
|
5730
|
+
return { top: belowTop, transform: "translateX(-50%)" };
|
|
5731
|
+
}
|
|
5732
|
+
const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
|
|
5733
|
+
return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
|
|
5734
|
+
}
|
|
5735
|
+
function calcToolbarPos(rect, parentScroll, approxW = 306) {
|
|
4693
5736
|
const GAP = 8;
|
|
4694
|
-
const APPROX_H =
|
|
4695
|
-
const APPROX_W =
|
|
5737
|
+
const APPROX_H = 32;
|
|
5738
|
+
const APPROX_W = approxW;
|
|
5739
|
+
const clip = getIframeVisibleClip(parentScroll);
|
|
5740
|
+
const clipTop = clip?.top ?? 0;
|
|
4696
5741
|
const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
|
|
4697
5742
|
const visibleTop = rect.top - canvasTopInIframe;
|
|
4698
5743
|
const visibleBottom = rect.bottom - canvasTopInIframe;
|
|
4699
5744
|
const isTall = rect.height > APPROX_H + GAP;
|
|
5745
|
+
const spaceAbove = rect.top - (clipTop + GAP);
|
|
5746
|
+
const fitsAbove = spaceAbove >= APPROX_H + GAP;
|
|
4700
5747
|
let top;
|
|
4701
5748
|
let transform;
|
|
4702
|
-
if (visibleTop > 0 || !isTall) {
|
|
5749
|
+
if ((visibleTop > 0 || !isTall) && fitsAbove) {
|
|
4703
5750
|
top = rect.top - GAP;
|
|
4704
5751
|
transform = "translateX(-50%) translateY(-100%)";
|
|
5752
|
+
} else if (visibleTop > 0 || !isTall) {
|
|
5753
|
+
top = rect.bottom + GAP;
|
|
5754
|
+
transform = "translateX(-50%)";
|
|
4705
5755
|
} else if (visibleBottom > APPROX_H + GAP) {
|
|
4706
|
-
top =
|
|
5756
|
+
top = clipTop + GAP;
|
|
4707
5757
|
transform = "translateX(-50%)";
|
|
4708
5758
|
} else {
|
|
4709
5759
|
top = rect.bottom + GAP;
|
|
4710
5760
|
transform = "translateX(-50%)";
|
|
4711
5761
|
}
|
|
5762
|
+
if (clip) {
|
|
5763
|
+
const clamped = clampToolbarToClip(top, transform, rect, clip, APPROX_H, GAP);
|
|
5764
|
+
top = clamped.top;
|
|
5765
|
+
transform = clamped.transform;
|
|
5766
|
+
}
|
|
4712
5767
|
const rawLeft = rect.left + rect.width / 2;
|
|
4713
5768
|
const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
|
|
4714
5769
|
return { top, left, transform };
|
|
4715
5770
|
}
|
|
5771
|
+
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: [
|
|
5772
|
+
/* @__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" }),
|
|
5773
|
+
/* @__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" })
|
|
5774
|
+
] });
|
|
4716
5775
|
function FloatingToolbar({
|
|
4717
5776
|
rect,
|
|
4718
5777
|
parentScroll,
|
|
4719
5778
|
elRef,
|
|
5779
|
+
variant = "rich-text",
|
|
4720
5780
|
onCommand,
|
|
4721
5781
|
activeCommands,
|
|
4722
5782
|
showEditLink,
|
|
4723
5783
|
onEditLink
|
|
4724
5784
|
}) {
|
|
4725
|
-
const
|
|
4726
|
-
|
|
5785
|
+
const approxW = variant === "link-action" ? 112 : 306;
|
|
5786
|
+
const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
|
|
5787
|
+
if (variant === "link-action") {
|
|
5788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
5789
|
+
"div",
|
|
5790
|
+
{
|
|
5791
|
+
ref: elRef,
|
|
5792
|
+
"data-ohw-toolbar": "",
|
|
5793
|
+
style: {
|
|
5794
|
+
position: "fixed",
|
|
5795
|
+
top,
|
|
5796
|
+
left,
|
|
5797
|
+
transform,
|
|
5798
|
+
zIndex: 2147483647,
|
|
5799
|
+
background: "#fff",
|
|
5800
|
+
border: "1px solid #E7E5E4",
|
|
5801
|
+
borderRadius: 6,
|
|
5802
|
+
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
5803
|
+
display: "flex",
|
|
5804
|
+
alignItems: "center",
|
|
5805
|
+
padding: TOOLBAR_PILL_PADDING,
|
|
5806
|
+
gap: TOOLBAR_TOGGLE_GAP,
|
|
5807
|
+
fontFamily: "sans-serif",
|
|
5808
|
+
pointerEvents: "auto",
|
|
5809
|
+
whiteSpace: "nowrap"
|
|
5810
|
+
},
|
|
5811
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5812
|
+
children: [
|
|
5813
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5814
|
+
"button",
|
|
5815
|
+
{
|
|
5816
|
+
type: "button",
|
|
5817
|
+
title: "Edit link",
|
|
5818
|
+
onMouseDown: (e) => {
|
|
5819
|
+
e.preventDefault();
|
|
5820
|
+
e.stopPropagation();
|
|
5821
|
+
onEditLink?.();
|
|
5822
|
+
},
|
|
5823
|
+
onClick: (e) => {
|
|
5824
|
+
e.preventDefault();
|
|
5825
|
+
e.stopPropagation();
|
|
5826
|
+
},
|
|
5827
|
+
onMouseEnter: (e) => {
|
|
5828
|
+
e.currentTarget.style.background = "#F5F5F4";
|
|
5829
|
+
},
|
|
5830
|
+
onMouseLeave: (e) => {
|
|
5831
|
+
e.currentTarget.style.background = "transparent";
|
|
5832
|
+
},
|
|
5833
|
+
style: {
|
|
5834
|
+
display: "flex",
|
|
5835
|
+
alignItems: "center",
|
|
5836
|
+
justifyContent: "center",
|
|
5837
|
+
border: "none",
|
|
5838
|
+
background: "transparent",
|
|
5839
|
+
borderRadius: 4,
|
|
5840
|
+
cursor: "pointer",
|
|
5841
|
+
color: "#1C1917",
|
|
5842
|
+
flexShrink: 0,
|
|
5843
|
+
padding: 6
|
|
5844
|
+
},
|
|
5845
|
+
children: EDIT_LINK_ICON
|
|
5846
|
+
}
|
|
5847
|
+
),
|
|
5848
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5849
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5850
|
+
"button",
|
|
5851
|
+
{
|
|
5852
|
+
type: "button",
|
|
5853
|
+
title: "Coming soon",
|
|
5854
|
+
disabled: true,
|
|
5855
|
+
style: {
|
|
5856
|
+
display: "flex",
|
|
5857
|
+
alignItems: "center",
|
|
5858
|
+
justifyContent: "center",
|
|
5859
|
+
border: "none",
|
|
5860
|
+
background: "transparent",
|
|
5861
|
+
borderRadius: 4,
|
|
5862
|
+
cursor: "not-allowed",
|
|
5863
|
+
color: "#A8A29E",
|
|
5864
|
+
flexShrink: 0,
|
|
5865
|
+
padding: 6,
|
|
5866
|
+
opacity: 0.6
|
|
5867
|
+
},
|
|
5868
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
|
|
5869
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "5", cy: "12", r: "1.5" }),
|
|
5870
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
|
|
5871
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "19", cy: "12", r: "1.5" })
|
|
5872
|
+
] })
|
|
5873
|
+
}
|
|
5874
|
+
)
|
|
5875
|
+
]
|
|
5876
|
+
}
|
|
5877
|
+
);
|
|
5878
|
+
}
|
|
5879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4727
5880
|
"div",
|
|
4728
5881
|
{
|
|
4729
5882
|
ref: elRef,
|
|
@@ -4740,19 +5893,19 @@ function FloatingToolbar({
|
|
|
4740
5893
|
boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
|
|
4741
5894
|
display: "flex",
|
|
4742
5895
|
alignItems: "center",
|
|
4743
|
-
padding:
|
|
4744
|
-
gap:
|
|
5896
|
+
padding: TOOLBAR_PILL_PADDING,
|
|
5897
|
+
gap: TOOLBAR_TOGGLE_GAP,
|
|
4745
5898
|
fontFamily: "sans-serif",
|
|
4746
5899
|
pointerEvents: "auto",
|
|
4747
5900
|
whiteSpace: "nowrap"
|
|
4748
5901
|
},
|
|
4749
5902
|
onMouseDown: (e) => e.stopPropagation(),
|
|
4750
5903
|
children: [
|
|
4751
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
4752
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
5904
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react5.default.Fragment, { children: [
|
|
5905
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4753
5906
|
btns.map((btn) => {
|
|
4754
5907
|
const isActive = activeCommands.has(btn.cmd);
|
|
4755
|
-
return /* @__PURE__ */ (0,
|
|
5908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4756
5909
|
"button",
|
|
4757
5910
|
{
|
|
4758
5911
|
title: btn.title,
|
|
@@ -4778,7 +5931,7 @@ function FloatingToolbar({
|
|
|
4778
5931
|
flexShrink: 0,
|
|
4779
5932
|
padding: 6
|
|
4780
5933
|
},
|
|
4781
|
-
children: /* @__PURE__ */ (0,
|
|
5934
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4782
5935
|
"svg",
|
|
4783
5936
|
{
|
|
4784
5937
|
width: "16",
|
|
@@ -4798,7 +5951,7 @@ function FloatingToolbar({
|
|
|
4798
5951
|
);
|
|
4799
5952
|
})
|
|
4800
5953
|
] }, gi)),
|
|
4801
|
-
showEditLink ? /* @__PURE__ */ (0,
|
|
5954
|
+
showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4802
5955
|
"button",
|
|
4803
5956
|
{
|
|
4804
5957
|
type: "button",
|
|
@@ -4830,10 +5983,7 @@ function FloatingToolbar({
|
|
|
4830
5983
|
flexShrink: 0,
|
|
4831
5984
|
padding: 6
|
|
4832
5985
|
},
|
|
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
|
-
] })
|
|
5986
|
+
children: EDIT_LINK_ICON
|
|
4837
5987
|
}
|
|
4838
5988
|
) : null
|
|
4839
5989
|
]
|
|
@@ -4850,7 +6000,7 @@ function StateToggle({
|
|
|
4850
6000
|
states,
|
|
4851
6001
|
onStateChange
|
|
4852
6002
|
}) {
|
|
4853
|
-
return /* @__PURE__ */ (0,
|
|
6003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4854
6004
|
ToggleGroup,
|
|
4855
6005
|
{
|
|
4856
6006
|
"data-ohw-state-toggle": "",
|
|
@@ -4864,18 +6014,35 @@ function StateToggle({
|
|
|
4864
6014
|
left: rect.right - 8,
|
|
4865
6015
|
transform: "translateX(-100%)"
|
|
4866
6016
|
},
|
|
4867
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
6017
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4868
6018
|
}
|
|
4869
6019
|
);
|
|
4870
6020
|
}
|
|
4871
6021
|
var contentCache = /* @__PURE__ */ new Map();
|
|
6022
|
+
function resolveSubdomain(subdomainFromQuery) {
|
|
6023
|
+
if (subdomainFromQuery) return subdomainFromQuery;
|
|
6024
|
+
if (typeof window !== "undefined") {
|
|
6025
|
+
const parts = window.location.hostname.split(".");
|
|
6026
|
+
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
6027
|
+
}
|
|
6028
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
6029
|
+
if (siteUrl) {
|
|
6030
|
+
try {
|
|
6031
|
+
const host = new URL(siteUrl).hostname;
|
|
6032
|
+
const siteParts = host.split(".");
|
|
6033
|
+
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
6034
|
+
} catch {
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
return "";
|
|
6038
|
+
}
|
|
4872
6039
|
function OhhwellsBridge() {
|
|
4873
6040
|
const pathname = (0, import_navigation.usePathname)();
|
|
4874
6041
|
const router = (0, import_navigation.useRouter)();
|
|
4875
6042
|
const searchParams = (0, import_navigation.useSearchParams)();
|
|
4876
6043
|
const isEditMode = isEditSessionActive();
|
|
4877
|
-
const [bridgeRoot, setBridgeRoot] = (0,
|
|
4878
|
-
(0,
|
|
6044
|
+
const [bridgeRoot, setBridgeRoot] = (0, import_react5.useState)(null);
|
|
6045
|
+
(0, import_react5.useEffect)(() => {
|
|
4879
6046
|
const figtreeFontId = "ohw-figtree-font";
|
|
4880
6047
|
if (!document.getElementById(figtreeFontId)) {
|
|
4881
6048
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4898,55 +6065,70 @@ function OhhwellsBridge() {
|
|
|
4898
6065
|
};
|
|
4899
6066
|
}, []);
|
|
4900
6067
|
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) => {
|
|
6068
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
6069
|
+
const postToParent = (0, import_react5.useCallback)((data) => {
|
|
4907
6070
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4908
6071
|
window.parent.postMessage(data, "*");
|
|
4909
6072
|
}
|
|
4910
6073
|
}, []);
|
|
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
|
-
|
|
6074
|
+
const [fetchState, setFetchState] = (0, import_react5.useState)("idle");
|
|
6075
|
+
const autoSaveTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
6076
|
+
const activeElRef = (0, import_react5.useRef)(null);
|
|
6077
|
+
const selectedElRef = (0, import_react5.useRef)(null);
|
|
6078
|
+
const originalContentRef = (0, import_react5.useRef)(null);
|
|
6079
|
+
const activeStateElRef = (0, import_react5.useRef)(null);
|
|
6080
|
+
const parentScrollRef = (0, import_react5.useRef)(null);
|
|
6081
|
+
const visibleViewportRef = (0, import_react5.useRef)(null);
|
|
6082
|
+
const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react5.useState)(null);
|
|
6083
|
+
const attachVisibleViewport = (0, import_react5.useCallback)((node) => {
|
|
6084
|
+
visibleViewportRef.current = node;
|
|
6085
|
+
setDialogPortalContainer(node);
|
|
6086
|
+
if (node) applyVisibleViewport(node, parentScrollRef.current);
|
|
6087
|
+
}, []);
|
|
6088
|
+
const toolbarElRef = (0, import_react5.useRef)(null);
|
|
6089
|
+
const glowElRef = (0, import_react5.useRef)(null);
|
|
6090
|
+
const hoveredImageRef = (0, import_react5.useRef)(null);
|
|
6091
|
+
const hoveredImageHasTextOverlapRef = (0, import_react5.useRef)(false);
|
|
6092
|
+
const hoveredGapRef = (0, import_react5.useRef)(null);
|
|
6093
|
+
const imageUnhoverTimerRef = (0, import_react5.useRef)(null);
|
|
6094
|
+
const imageShowTimerRef = (0, import_react5.useRef)(null);
|
|
6095
|
+
const editStylesRef = (0, import_react5.useRef)(null);
|
|
6096
|
+
const activateRef = (0, import_react5.useRef)(() => {
|
|
6097
|
+
});
|
|
6098
|
+
const deactivateRef = (0, import_react5.useRef)(() => {
|
|
4925
6099
|
});
|
|
4926
|
-
const
|
|
6100
|
+
const selectRef = (0, import_react5.useRef)(() => {
|
|
4927
6101
|
});
|
|
4928
|
-
const
|
|
6102
|
+
const deselectRef = (0, import_react5.useRef)(() => {
|
|
4929
6103
|
});
|
|
4930
|
-
const
|
|
6104
|
+
const refreshActiveCommandsRef = (0, import_react5.useRef)(() => {
|
|
6105
|
+
});
|
|
6106
|
+
const postToParentRef = (0, import_react5.useRef)(postToParent);
|
|
4931
6107
|
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
|
|
6108
|
+
const sectionsLoadedRef = (0, import_react5.useRef)(false);
|
|
6109
|
+
const pendingScheduleConfigRequests = (0, import_react5.useRef)([]);
|
|
6110
|
+
const [toolbarRect, setToolbarRect] = (0, import_react5.useState)(null);
|
|
6111
|
+
const [toolbarVariant, setToolbarVariant] = (0, import_react5.useState)("none");
|
|
6112
|
+
const toolbarVariantRef = (0, import_react5.useRef)("none");
|
|
6113
|
+
toolbarVariantRef.current = toolbarVariant;
|
|
6114
|
+
const [toggleState, setToggleState] = (0, import_react5.useState)(null);
|
|
6115
|
+
const [maxBadge, setMaxBadge] = (0, import_react5.useState)(null);
|
|
6116
|
+
const [activeCommands, setActiveCommands] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
6117
|
+
const [sectionGap, setSectionGap] = (0, import_react5.useState)(null);
|
|
6118
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react5.useState)(false);
|
|
6119
|
+
const [linkPopover, setLinkPopover] = (0, import_react5.useState)(null);
|
|
6120
|
+
const [sitePages, setSitePages] = (0, import_react5.useState)([]);
|
|
6121
|
+
const [sectionsByPath, setSectionsByPath] = (0, import_react5.useState)({});
|
|
6122
|
+
const sectionsPrefetchGenRef = (0, import_react5.useRef)(0);
|
|
6123
|
+
const setLinkPopoverRef = (0, import_react5.useRef)(setLinkPopover);
|
|
6124
|
+
const linkPopoverPanelRef = (0, import_react5.useRef)(null);
|
|
6125
|
+
const linkPopoverOpenRef = (0, import_react5.useRef)(false);
|
|
6126
|
+
const linkPopoverGraceUntilRef = (0, import_react5.useRef)(0);
|
|
4945
6127
|
setLinkPopoverRef.current = setLinkPopover;
|
|
4946
6128
|
const bumpLinkPopoverGrace = () => {
|
|
4947
6129
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
4948
6130
|
};
|
|
4949
|
-
const runSectionsPrefetch = (0,
|
|
6131
|
+
const runSectionsPrefetch = (0, import_react5.useCallback)((pages) => {
|
|
4950
6132
|
if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
|
|
4951
6133
|
const gen = ++sectionsPrefetchGenRef.current;
|
|
4952
6134
|
const paths = pages.map((p) => p.path);
|
|
@@ -4965,17 +6147,43 @@ function OhhwellsBridge() {
|
|
|
4965
6147
|
);
|
|
4966
6148
|
});
|
|
4967
6149
|
}, [isEditMode, pathname]);
|
|
4968
|
-
const runSectionsPrefetchRef = (0,
|
|
6150
|
+
const runSectionsPrefetchRef = (0, import_react5.useRef)(runSectionsPrefetch);
|
|
4969
6151
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
4970
|
-
(0,
|
|
6152
|
+
(0, import_react5.useEffect)(() => {
|
|
4971
6153
|
if (!linkPopover) return;
|
|
4972
6154
|
if (hoveredImageRef.current) {
|
|
4973
6155
|
hoveredImageRef.current = null;
|
|
4974
6156
|
hoveredImageHasTextOverlapRef.current = false;
|
|
4975
6157
|
}
|
|
6158
|
+
hoveredGapRef.current = null;
|
|
6159
|
+
setSectionGap(null);
|
|
4976
6160
|
postToParent({ type: "ow:image-unhover" });
|
|
6161
|
+
postToParent({ type: "ow:link-modal-lock", locked: true });
|
|
6162
|
+
const html = document.documentElement;
|
|
6163
|
+
const body = document.body;
|
|
6164
|
+
const prevHtmlOverflow = html.style.overflow;
|
|
6165
|
+
const prevBodyOverflow = body.style.overflow;
|
|
6166
|
+
html.style.overflow = "hidden";
|
|
6167
|
+
body.style.overflow = "hidden";
|
|
6168
|
+
const preventBackgroundScroll = (e) => {
|
|
6169
|
+
const target = e.target;
|
|
6170
|
+
if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
|
|
6171
|
+
return;
|
|
6172
|
+
}
|
|
6173
|
+
e.preventDefault();
|
|
6174
|
+
};
|
|
6175
|
+
const scrollOpts = { passive: false, capture: true };
|
|
6176
|
+
document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6177
|
+
document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6178
|
+
return () => {
|
|
6179
|
+
postToParent({ type: "ow:link-modal-lock", locked: false });
|
|
6180
|
+
html.style.overflow = prevHtmlOverflow;
|
|
6181
|
+
body.style.overflow = prevBodyOverflow;
|
|
6182
|
+
document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
|
|
6183
|
+
document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
|
|
6184
|
+
};
|
|
4977
6185
|
}, [linkPopover, postToParent]);
|
|
4978
|
-
(0,
|
|
6186
|
+
(0, import_react5.useEffect)(() => {
|
|
4979
6187
|
if (!isEditMode) return;
|
|
4980
6188
|
const useFixtures = shouldUseDevFixtures();
|
|
4981
6189
|
if (useFixtures) {
|
|
@@ -4999,21 +6207,27 @@ function OhhwellsBridge() {
|
|
|
4999
6207
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5000
6208
|
return () => window.removeEventListener("message", onSitePages);
|
|
5001
6209
|
}, [isEditMode, postToParent]);
|
|
5002
|
-
(0,
|
|
6210
|
+
(0, import_react5.useEffect)(() => {
|
|
5003
6211
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
5004
6212
|
void loadAllSectionsManifest().then((manifest) => {
|
|
5005
6213
|
if (Object.keys(manifest).length === 0) return;
|
|
5006
6214
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
5007
6215
|
});
|
|
5008
6216
|
}, [isEditMode]);
|
|
5009
|
-
(0,
|
|
6217
|
+
(0, import_react5.useEffect)(() => {
|
|
5010
6218
|
const update = () => {
|
|
5011
|
-
const el = activeElRef.current;
|
|
6219
|
+
const el = activeElRef.current ?? selectedElRef.current;
|
|
5012
6220
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
5013
6221
|
setToggleState((prev) => {
|
|
5014
6222
|
if (!prev || !activeStateElRef.current) return prev;
|
|
5015
6223
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
5016
6224
|
});
|
|
6225
|
+
setSectionGap((prev) => {
|
|
6226
|
+
if (!prev) return prev;
|
|
6227
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
6228
|
+
if (!anchor) return prev;
|
|
6229
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
6230
|
+
});
|
|
5017
6231
|
};
|
|
5018
6232
|
const vvp = window.visualViewport;
|
|
5019
6233
|
if (!vvp) return;
|
|
@@ -5024,10 +6238,33 @@ function OhhwellsBridge() {
|
|
|
5024
6238
|
vvp.removeEventListener("resize", update);
|
|
5025
6239
|
};
|
|
5026
6240
|
}, []);
|
|
5027
|
-
const refreshStateRules = (0,
|
|
6241
|
+
const refreshStateRules = (0, import_react5.useCallback)(() => {
|
|
5028
6242
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
5029
6243
|
}, []);
|
|
5030
|
-
const
|
|
6244
|
+
const processConfigRequest = (0, import_react5.useCallback)((insertAfterVal) => {
|
|
6245
|
+
const tracker = getSectionsTracker();
|
|
6246
|
+
let entries = [];
|
|
6247
|
+
try {
|
|
6248
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
6249
|
+
} catch {
|
|
6250
|
+
}
|
|
6251
|
+
const path = window.location.pathname;
|
|
6252
|
+
const entry = entries.find(
|
|
6253
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
6254
|
+
);
|
|
6255
|
+
if (entry) {
|
|
6256
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
6257
|
+
return;
|
|
6258
|
+
}
|
|
6259
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
6260
|
+
entries.push(newEntry);
|
|
6261
|
+
tracker.textContent = JSON.stringify(entries);
|
|
6262
|
+
if (isEditMode) {
|
|
6263
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6264
|
+
}
|
|
6265
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
6266
|
+
}, [isEditMode]);
|
|
6267
|
+
const deactivate = (0, import_react5.useCallback)(() => {
|
|
5031
6268
|
const el = activeElRef.current;
|
|
5032
6269
|
if (!el) return;
|
|
5033
6270
|
const key = el.dataset.ohwKey;
|
|
@@ -5047,19 +6284,41 @@ function OhhwellsBridge() {
|
|
|
5047
6284
|
}
|
|
5048
6285
|
el.removeAttribute("contenteditable");
|
|
5049
6286
|
activeElRef.current = null;
|
|
5050
|
-
|
|
6287
|
+
if (!selectedElRef.current) {
|
|
6288
|
+
setToolbarRect(null);
|
|
6289
|
+
setToolbarVariant("none");
|
|
6290
|
+
}
|
|
5051
6291
|
setMaxBadge(null);
|
|
5052
6292
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
5053
6293
|
setToolbarShowEditLink(false);
|
|
5054
6294
|
postToParent({ type: "ow:exit-edit" });
|
|
5055
6295
|
}, [postToParent]);
|
|
5056
|
-
const
|
|
6296
|
+
const deselect = (0, import_react5.useCallback)(() => {
|
|
6297
|
+
selectedElRef.current = null;
|
|
6298
|
+
if (!activeElRef.current) {
|
|
6299
|
+
setToolbarRect(null);
|
|
6300
|
+
setToolbarVariant("none");
|
|
6301
|
+
}
|
|
6302
|
+
}, []);
|
|
6303
|
+
const select = (0, import_react5.useCallback)((anchor) => {
|
|
6304
|
+
if (!isNavbarButton(anchor)) return;
|
|
6305
|
+
if (activeElRef.current) deactivate();
|
|
6306
|
+
selectedElRef.current = anchor;
|
|
6307
|
+
clearHrefKeyHover(anchor);
|
|
6308
|
+
setToolbarVariant("link-action");
|
|
6309
|
+
setToolbarRect(anchor.getBoundingClientRect());
|
|
6310
|
+
setToolbarShowEditLink(false);
|
|
6311
|
+
setActiveCommands(/* @__PURE__ */ new Set());
|
|
6312
|
+
}, [deactivate]);
|
|
6313
|
+
const activate = (0, import_react5.useCallback)((el) => {
|
|
5057
6314
|
if (activeElRef.current === el) return;
|
|
6315
|
+
selectedElRef.current = null;
|
|
5058
6316
|
deactivate();
|
|
5059
6317
|
if (hoveredImageRef.current) {
|
|
5060
6318
|
hoveredImageRef.current = null;
|
|
5061
6319
|
postToParentRef.current({ type: "ow:image-unhover" });
|
|
5062
6320
|
}
|
|
6321
|
+
setToolbarVariant("rich-text");
|
|
5063
6322
|
el.setAttribute("contenteditable", "true");
|
|
5064
6323
|
el.removeAttribute("data-ohw-hovered");
|
|
5065
6324
|
activeElRef.current = el;
|
|
@@ -5072,7 +6331,9 @@ function OhhwellsBridge() {
|
|
|
5072
6331
|
}, [deactivate, postToParent]);
|
|
5073
6332
|
activateRef.current = activate;
|
|
5074
6333
|
deactivateRef.current = deactivate;
|
|
5075
|
-
|
|
6334
|
+
selectRef.current = select;
|
|
6335
|
+
deselectRef.current = deselect;
|
|
6336
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
5076
6337
|
if (!subdomain || isEditMode) {
|
|
5077
6338
|
setFetchState("done");
|
|
5078
6339
|
return;
|
|
@@ -5080,6 +6341,7 @@ function OhhwellsBridge() {
|
|
|
5080
6341
|
const applyContent = (content) => {
|
|
5081
6342
|
const imageLoads = [];
|
|
5082
6343
|
for (const [key, val] of Object.entries(content)) {
|
|
6344
|
+
if (key === "__ohw_sections") continue;
|
|
5083
6345
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5084
6346
|
if (el.dataset.ohwEditable === "image") {
|
|
5085
6347
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5101,6 +6363,9 @@ function OhhwellsBridge() {
|
|
|
5101
6363
|
});
|
|
5102
6364
|
applyLinkByKey(key, val);
|
|
5103
6365
|
}
|
|
6366
|
+
initSectionsFromContent(content, true);
|
|
6367
|
+
sectionsLoadedRef.current = true;
|
|
6368
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5104
6369
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
5105
6370
|
}) : Promise.resolve();
|
|
5106
6371
|
};
|
|
@@ -5125,12 +6390,15 @@ function OhhwellsBridge() {
|
|
|
5125
6390
|
cancelled = true;
|
|
5126
6391
|
};
|
|
5127
6392
|
}, [subdomain, isEditMode, pathname]);
|
|
5128
|
-
(0,
|
|
6393
|
+
(0, import_react5.useEffect)(() => {
|
|
5129
6394
|
if (!subdomain || isEditMode) return;
|
|
6395
|
+
let debounceTimer = null;
|
|
5130
6396
|
const applyFromCache = () => {
|
|
5131
6397
|
const content = contentCache.get(subdomain);
|
|
5132
6398
|
if (!content) return;
|
|
6399
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
5133
6400
|
for (const [key, val] of Object.entries(content)) {
|
|
6401
|
+
if (key === "__ohw_sections") continue;
|
|
5134
6402
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5135
6403
|
if (el.dataset.ohwEditable === "image") {
|
|
5136
6404
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5147,21 +6415,34 @@ function OhhwellsBridge() {
|
|
|
5147
6415
|
applyLinkByKey(key, val);
|
|
5148
6416
|
}
|
|
5149
6417
|
};
|
|
5150
|
-
|
|
5151
|
-
|
|
6418
|
+
const scheduleApply = () => {
|
|
6419
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6420
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6421
|
+
};
|
|
6422
|
+
scheduleApply();
|
|
6423
|
+
const observer = new MutationObserver(scheduleApply);
|
|
5152
6424
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
5153
|
-
return () =>
|
|
5154
|
-
|
|
5155
|
-
|
|
6425
|
+
return () => {
|
|
6426
|
+
observer.disconnect();
|
|
6427
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
6428
|
+
};
|
|
6429
|
+
}, [subdomain, isEditMode, pathname]);
|
|
6430
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
5156
6431
|
const el = document.getElementById("ohw-loader");
|
|
5157
6432
|
if (!el) return;
|
|
5158
6433
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
5159
6434
|
el.style.display = visible ? "flex" : "none";
|
|
5160
6435
|
}, [subdomain, fetchState]);
|
|
5161
|
-
(0,
|
|
6436
|
+
(0, import_react5.useEffect)(() => {
|
|
5162
6437
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
5163
6438
|
}, [pathname, postToParent]);
|
|
5164
|
-
(0,
|
|
6439
|
+
(0, import_react5.useEffect)(() => {
|
|
6440
|
+
if (!isEditMode) return;
|
|
6441
|
+
setLinkPopover(null);
|
|
6442
|
+
deselectRef.current();
|
|
6443
|
+
deactivateRef.current();
|
|
6444
|
+
}, [pathname, isEditMode]);
|
|
6445
|
+
(0, import_react5.useEffect)(() => {
|
|
5165
6446
|
if (!isEditMode) return;
|
|
5166
6447
|
const measure = () => {
|
|
5167
6448
|
const h = document.body.scrollHeight;
|
|
@@ -5185,7 +6466,7 @@ function OhhwellsBridge() {
|
|
|
5185
6466
|
window.removeEventListener("resize", handleResize);
|
|
5186
6467
|
};
|
|
5187
6468
|
}, [pathname, isEditMode, postToParent]);
|
|
5188
|
-
(0,
|
|
6469
|
+
(0, import_react5.useEffect)(() => {
|
|
5189
6470
|
if (!subdomainFromQuery || isEditMode) return;
|
|
5190
6471
|
const handleClick = (e) => {
|
|
5191
6472
|
const anchor = e.target.closest("a");
|
|
@@ -5201,7 +6482,7 @@ function OhhwellsBridge() {
|
|
|
5201
6482
|
document.addEventListener("click", handleClick, true);
|
|
5202
6483
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5203
6484
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5204
|
-
(0,
|
|
6485
|
+
(0, import_react5.useEffect)(() => {
|
|
5205
6486
|
if (!isEditMode) {
|
|
5206
6487
|
editStylesRef.current?.base.remove();
|
|
5207
6488
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5271,12 +6552,12 @@ function OhhwellsBridge() {
|
|
|
5271
6552
|
if (editable.dataset.ohwEditable === "link") {
|
|
5272
6553
|
e.preventDefault();
|
|
5273
6554
|
e.stopPropagation();
|
|
6555
|
+
deselectRef.current();
|
|
5274
6556
|
deactivateRef.current();
|
|
5275
6557
|
bumpLinkPopoverGrace();
|
|
5276
6558
|
setLinkPopoverRef.current({
|
|
5277
6559
|
key: editable.dataset.ohwKey ?? "",
|
|
5278
|
-
target: getLinkHref(editable)
|
|
5279
|
-
rect: editable.getBoundingClientRect()
|
|
6560
|
+
target: getLinkHref(editable)
|
|
5280
6561
|
});
|
|
5281
6562
|
return;
|
|
5282
6563
|
}
|
|
@@ -5286,11 +6567,31 @@ function OhhwellsBridge() {
|
|
|
5286
6567
|
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
|
|
5287
6568
|
return;
|
|
5288
6569
|
}
|
|
6570
|
+
const hrefCtx = getHrefKeyFromElement(editable);
|
|
6571
|
+
if (hrefCtx && isNavbarButton(hrefCtx.anchor)) {
|
|
6572
|
+
e.preventDefault();
|
|
6573
|
+
e.stopPropagation();
|
|
6574
|
+
const { anchor: anchor2 } = hrefCtx;
|
|
6575
|
+
if (selectedElRef.current === anchor2) {
|
|
6576
|
+
activateRef.current(editable);
|
|
6577
|
+
return;
|
|
6578
|
+
}
|
|
6579
|
+
selectRef.current(anchor2);
|
|
6580
|
+
return;
|
|
6581
|
+
}
|
|
5289
6582
|
e.preventDefault();
|
|
5290
6583
|
e.stopPropagation();
|
|
5291
6584
|
activateRef.current(editable);
|
|
5292
6585
|
return;
|
|
5293
6586
|
}
|
|
6587
|
+
const hrefAnchor = target.closest("[data-ohw-href-key]");
|
|
6588
|
+
if (hrefAnchor && isNavbarButton(hrefAnchor)) {
|
|
6589
|
+
e.preventDefault();
|
|
6590
|
+
e.stopPropagation();
|
|
6591
|
+
if (selectedElRef.current === hrefAnchor) return;
|
|
6592
|
+
selectRef.current(hrefAnchor);
|
|
6593
|
+
return;
|
|
6594
|
+
}
|
|
5294
6595
|
if (activeElRef.current) {
|
|
5295
6596
|
const sel = window.getSelection();
|
|
5296
6597
|
if (sel && !sel.isCollapsed) {
|
|
@@ -5305,15 +6606,22 @@ function OhhwellsBridge() {
|
|
|
5305
6606
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
5306
6607
|
if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
|
|
5307
6608
|
}
|
|
6609
|
+
if (linkPopoverOpenRef.current && selectedElRef.current) {
|
|
6610
|
+
const selected = selectedElRef.current;
|
|
6611
|
+
if (selected === target || selected.contains(target)) return;
|
|
6612
|
+
}
|
|
5308
6613
|
if (linkPopoverOpenRef.current) {
|
|
5309
6614
|
setLinkPopoverRef.current(null);
|
|
5310
6615
|
return;
|
|
5311
6616
|
}
|
|
6617
|
+
deselectRef.current();
|
|
5312
6618
|
deactivateRef.current();
|
|
5313
6619
|
};
|
|
5314
6620
|
const handleMouseOver = (e) => {
|
|
5315
6621
|
const editable = e.target.closest("[data-ohw-editable]");
|
|
5316
6622
|
if (!editable) return;
|
|
6623
|
+
const selected = selectedElRef.current;
|
|
6624
|
+
if (selected && (selected === editable || selected.contains(editable))) return;
|
|
5317
6625
|
if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
|
|
5318
6626
|
editable.setAttribute("data-ohw-hovered", "");
|
|
5319
6627
|
}
|
|
@@ -5449,7 +6757,7 @@ function OhhwellsBridge() {
|
|
|
5449
6757
|
}
|
|
5450
6758
|
return;
|
|
5451
6759
|
}
|
|
5452
|
-
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]")) {
|
|
6760
|
+
if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]") || topEl?.closest("[data-ohw-link-page-dropdown]")) {
|
|
5453
6761
|
if (hoveredImageRef.current) {
|
|
5454
6762
|
hoveredImageRef.current = null;
|
|
5455
6763
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -5502,6 +6810,15 @@ function OhhwellsBridge() {
|
|
|
5502
6810
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
5503
6811
|
return;
|
|
5504
6812
|
}
|
|
6813
|
+
if (hoveredGapRef.current) {
|
|
6814
|
+
if (hoveredImageRef.current) {
|
|
6815
|
+
hoveredImageRef.current = null;
|
|
6816
|
+
resumeAnimTracks();
|
|
6817
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6818
|
+
}
|
|
6819
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6820
|
+
return;
|
|
6821
|
+
}
|
|
5505
6822
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5506
6823
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
5507
6824
|
hoveredImageRef.current = imgEl;
|
|
@@ -5567,8 +6884,37 @@ function OhhwellsBridge() {
|
|
|
5567
6884
|
}
|
|
5568
6885
|
}
|
|
5569
6886
|
};
|
|
6887
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6888
|
+
if (linkPopoverOpenRef.current) {
|
|
6889
|
+
if (hoveredGapRef.current) {
|
|
6890
|
+
hoveredGapRef.current = null;
|
|
6891
|
+
setSectionGap(null);
|
|
6892
|
+
}
|
|
6893
|
+
return;
|
|
6894
|
+
}
|
|
6895
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6896
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6897
|
+
const ZONE = 20;
|
|
6898
|
+
for (let i = 0; i < sections.length; i++) {
|
|
6899
|
+
const a = sections[i];
|
|
6900
|
+
const b = sections[i + 1] ?? null;
|
|
6901
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6902
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6903
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6904
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6905
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6906
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6907
|
+
return;
|
|
6908
|
+
}
|
|
6909
|
+
}
|
|
6910
|
+
if (hoveredGapRef.current) {
|
|
6911
|
+
hoveredGapRef.current = null;
|
|
6912
|
+
setSectionGap(null);
|
|
6913
|
+
}
|
|
6914
|
+
};
|
|
5570
6915
|
const handleMouseMove = (e) => {
|
|
5571
6916
|
const { clientX, clientY } = e;
|
|
6917
|
+
probeSectionGapAt(clientX, clientY);
|
|
5572
6918
|
probeImageAt(clientX, clientY);
|
|
5573
6919
|
probeHoverCardsAt(clientX, clientY);
|
|
5574
6920
|
};
|
|
@@ -5576,6 +6922,7 @@ function OhhwellsBridge() {
|
|
|
5576
6922
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
5577
6923
|
const { clientX, clientY } = e.data;
|
|
5578
6924
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6925
|
+
probeSectionGapAt(clientX, clientY);
|
|
5579
6926
|
probeImageAt(clientX, clientY);
|
|
5580
6927
|
probeHoverCardsAt(clientX, clientY);
|
|
5581
6928
|
};
|
|
@@ -5733,7 +7080,12 @@ function OhhwellsBridge() {
|
|
|
5733
7080
|
if (e.data?.type !== "ow:hydrate") return;
|
|
5734
7081
|
const content = e.data.content;
|
|
5735
7082
|
if (!content) return;
|
|
7083
|
+
let sectionsJson = null;
|
|
5736
7084
|
for (const [key, val] of Object.entries(content)) {
|
|
7085
|
+
if (key === "__ohw_sections") {
|
|
7086
|
+
sectionsJson = val;
|
|
7087
|
+
continue;
|
|
7088
|
+
}
|
|
5737
7089
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5738
7090
|
if (el.dataset.ohwEditable === "image") {
|
|
5739
7091
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -5748,6 +7100,11 @@ function OhhwellsBridge() {
|
|
|
5748
7100
|
});
|
|
5749
7101
|
applyLinkByKey(key, val);
|
|
5750
7102
|
}
|
|
7103
|
+
if (sectionsJson) {
|
|
7104
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
7105
|
+
sectionsLoadedRef.current = true;
|
|
7106
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
7107
|
+
}
|
|
5751
7108
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5752
7109
|
};
|
|
5753
7110
|
window.addEventListener("message", handleHydrate);
|
|
@@ -5758,6 +7115,7 @@ function OhhwellsBridge() {
|
|
|
5758
7115
|
setLinkPopoverRef.current(null);
|
|
5759
7116
|
return;
|
|
5760
7117
|
}
|
|
7118
|
+
deselectRef.current();
|
|
5761
7119
|
deactivateRef.current();
|
|
5762
7120
|
};
|
|
5763
7121
|
window.addEventListener("message", handleDeactivate);
|
|
@@ -5766,6 +7124,10 @@ function OhhwellsBridge() {
|
|
|
5766
7124
|
setLinkPopoverRef.current(null);
|
|
5767
7125
|
return;
|
|
5768
7126
|
}
|
|
7127
|
+
if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
|
|
7128
|
+
deselectRef.current();
|
|
7129
|
+
return;
|
|
7130
|
+
}
|
|
5769
7131
|
if (e.key !== "Escape") return;
|
|
5770
7132
|
const el = activeElRef.current;
|
|
5771
7133
|
if (el && originalContentRef.current !== null) {
|
|
@@ -5775,13 +7137,16 @@ function OhhwellsBridge() {
|
|
|
5775
7137
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
|
|
5776
7138
|
}
|
|
5777
7139
|
}
|
|
7140
|
+
deselectRef.current();
|
|
5778
7141
|
deactivateRef.current();
|
|
5779
7142
|
};
|
|
5780
7143
|
const handleScroll = () => {
|
|
5781
|
-
|
|
5782
|
-
|
|
7144
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7145
|
+
if (focusEl) {
|
|
7146
|
+
const r2 = focusEl.getBoundingClientRect();
|
|
5783
7147
|
applyToolbarPos(r2);
|
|
5784
|
-
|
|
7148
|
+
setToolbarRect(r2);
|
|
7149
|
+
setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
|
|
5785
7150
|
}
|
|
5786
7151
|
if (activeStateElRef.current) {
|
|
5787
7152
|
const rect = activeStateElRef.current.getBoundingClientRect();
|
|
@@ -5800,7 +7165,63 @@ function OhhwellsBridge() {
|
|
|
5800
7165
|
};
|
|
5801
7166
|
const handleSave = (e) => {
|
|
5802
7167
|
if (e.data?.type !== "ow:save") return;
|
|
5803
|
-
|
|
7168
|
+
const nodes = collectEditableNodes();
|
|
7169
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
7170
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
7171
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
7172
|
+
};
|
|
7173
|
+
const handleInsertSection = (e) => {
|
|
7174
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
7175
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
7176
|
+
if (widgetType !== "scheduling") return;
|
|
7177
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
7178
|
+
if (inserted) {
|
|
7179
|
+
const tracker = getSectionsTracker();
|
|
7180
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
7181
|
+
const h = document.documentElement.scrollHeight;
|
|
7182
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
7183
|
+
}
|
|
7184
|
+
};
|
|
7185
|
+
const handleSwitchSchedule = (e) => {
|
|
7186
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
7187
|
+
const scheduleId = e.data.scheduleId;
|
|
7188
|
+
const insertAfter = e.data.insertAfter;
|
|
7189
|
+
if (!scheduleId || !insertAfter) return;
|
|
7190
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7191
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7192
|
+
};
|
|
7193
|
+
const handleScheduleLinked = (e) => {
|
|
7194
|
+
if (e.data?.type !== "ow:schedule-linked") return;
|
|
7195
|
+
const scheduleId = e.data.scheduleId;
|
|
7196
|
+
const insertAfter = e.data.insertAfter;
|
|
7197
|
+
if (!scheduleId || !insertAfter) return;
|
|
7198
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
7199
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7200
|
+
};
|
|
7201
|
+
const handleClearSchedulingWidget = (e) => {
|
|
7202
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
7203
|
+
const insertAfter = e.data.insertAfter;
|
|
7204
|
+
if (!insertAfter) return;
|
|
7205
|
+
const text = updateSectionScheduleId(insertAfter, null);
|
|
7206
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
7207
|
+
};
|
|
7208
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
7209
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
7210
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
7211
|
+
el.remove();
|
|
7212
|
+
});
|
|
7213
|
+
const tracker = getSectionsTracker();
|
|
7214
|
+
let sections = [];
|
|
7215
|
+
try {
|
|
7216
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
7217
|
+
} catch {
|
|
7218
|
+
}
|
|
7219
|
+
const currentPath = window.location.pathname;
|
|
7220
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
7221
|
+
tracker.textContent = JSON.stringify(updated);
|
|
7222
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
7223
|
+
const h = document.documentElement.scrollHeight;
|
|
7224
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5804
7225
|
};
|
|
5805
7226
|
const handleSelectionChange = () => {
|
|
5806
7227
|
if (!activeElRef.current) return;
|
|
@@ -5850,8 +7271,9 @@ function OhhwellsBridge() {
|
|
|
5850
7271
|
};
|
|
5851
7272
|
const applyToolbarPos = (rect) => {
|
|
5852
7273
|
const ps = parentScrollRef.current;
|
|
7274
|
+
const approxW = toolbarVariantRef.current === "link-action" ? 112 : 306;
|
|
5853
7275
|
if (toolbarElRef.current) {
|
|
5854
|
-
const { top, left, transform } = calcToolbarPos(rect, ps);
|
|
7276
|
+
const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
|
|
5855
7277
|
toolbarElRef.current.style.top = `${top}px`;
|
|
5856
7278
|
toolbarElRef.current.style.left = `${left}px`;
|
|
5857
7279
|
toolbarElRef.current.style.transform = transform;
|
|
@@ -5866,7 +7288,11 @@ function OhhwellsBridge() {
|
|
|
5866
7288
|
if (e.data?.type !== "ow:parent-scroll") return;
|
|
5867
7289
|
const { iframeOffsetTop, headerH, canvasH } = e.data;
|
|
5868
7290
|
parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
|
|
5869
|
-
if (
|
|
7291
|
+
if (visibleViewportRef.current) {
|
|
7292
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7293
|
+
}
|
|
7294
|
+
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
7295
|
+
if (focusEl) applyToolbarPos(focusEl.getBoundingClientRect());
|
|
5870
7296
|
};
|
|
5871
7297
|
const handleClickAt = (e) => {
|
|
5872
7298
|
if (e.data?.type !== "ow:click-at") return;
|
|
@@ -5905,12 +7331,23 @@ function OhhwellsBridge() {
|
|
|
5905
7331
|
deactivateRef.current();
|
|
5906
7332
|
};
|
|
5907
7333
|
window.addEventListener("message", handleSave);
|
|
7334
|
+
window.addEventListener("message", handleInsertSection);
|
|
7335
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
7336
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
7337
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
7338
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
5908
7339
|
window.addEventListener("message", handleImageUrl);
|
|
5909
7340
|
window.addEventListener("message", handleAnimLock);
|
|
5910
7341
|
window.addEventListener("message", handleCanvasHeight);
|
|
5911
7342
|
window.addEventListener("message", handleParentScroll);
|
|
5912
7343
|
window.addEventListener("message", handlePointerSync);
|
|
5913
7344
|
window.addEventListener("message", handleClickAt);
|
|
7345
|
+
const handleViewportResize = () => {
|
|
7346
|
+
if (visibleViewportRef.current) {
|
|
7347
|
+
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
7348
|
+
}
|
|
7349
|
+
};
|
|
7350
|
+
window.addEventListener("resize", handleViewportResize, { passive: true });
|
|
5914
7351
|
document.addEventListener("click", handleClick, true);
|
|
5915
7352
|
document.addEventListener("paste", handlePaste, true);
|
|
5916
7353
|
document.addEventListener("input", handleInput, true);
|
|
@@ -5939,10 +7376,16 @@ function OhhwellsBridge() {
|
|
|
5939
7376
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
5940
7377
|
window.removeEventListener("scroll", handleScroll, true);
|
|
5941
7378
|
window.removeEventListener("message", handleSave);
|
|
7379
|
+
window.removeEventListener("message", handleInsertSection);
|
|
7380
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
7381
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
7382
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
7383
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
5942
7384
|
window.removeEventListener("message", handleImageUrl);
|
|
5943
7385
|
window.removeEventListener("message", handleAnimLock);
|
|
5944
7386
|
window.removeEventListener("message", handleCanvasHeight);
|
|
5945
7387
|
window.removeEventListener("message", handleParentScroll);
|
|
7388
|
+
window.removeEventListener("resize", handleViewportResize);
|
|
5946
7389
|
window.removeEventListener("message", handlePointerSync);
|
|
5947
7390
|
window.removeEventListener("message", handleClickAt);
|
|
5948
7391
|
window.removeEventListener("message", handleHydrate);
|
|
@@ -5953,7 +7396,23 @@ function OhhwellsBridge() {
|
|
|
5953
7396
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5954
7397
|
};
|
|
5955
7398
|
}, [isEditMode, refreshStateRules]);
|
|
5956
|
-
(0,
|
|
7399
|
+
(0, import_react5.useEffect)(() => {
|
|
7400
|
+
const handler = (e) => {
|
|
7401
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
7402
|
+
const insertAfterVal = e.data.insertAfter;
|
|
7403
|
+
if (!insertAfterVal) return;
|
|
7404
|
+
if (!sectionsLoadedRef.current) {
|
|
7405
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
7406
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
7407
|
+
}
|
|
7408
|
+
return;
|
|
7409
|
+
}
|
|
7410
|
+
processConfigRequest(insertAfterVal);
|
|
7411
|
+
};
|
|
7412
|
+
window.addEventListener("message", handler);
|
|
7413
|
+
return () => window.removeEventListener("message", handler);
|
|
7414
|
+
}, [processConfigRequest]);
|
|
7415
|
+
(0, import_react5.useEffect)(() => {
|
|
5957
7416
|
if (!isEditMode) return;
|
|
5958
7417
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5959
7418
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5982,19 +7441,19 @@ function OhhwellsBridge() {
|
|
|
5982
7441
|
clearTimeout(timer);
|
|
5983
7442
|
};
|
|
5984
7443
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5985
|
-
(0,
|
|
7444
|
+
(0, import_react5.useEffect)(() => {
|
|
5986
7445
|
scrollToHashSectionWhenReady();
|
|
5987
7446
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
5988
7447
|
window.addEventListener("hashchange", onHashChange);
|
|
5989
7448
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
5990
7449
|
}, [pathname]);
|
|
5991
|
-
const handleCommand = (0,
|
|
7450
|
+
const handleCommand = (0, import_react5.useCallback)((cmd) => {
|
|
5992
7451
|
document.execCommand(cmd, false);
|
|
5993
7452
|
activeElRef.current?.focus();
|
|
5994
7453
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
5995
7454
|
refreshActiveCommandsRef.current();
|
|
5996
7455
|
}, []);
|
|
5997
|
-
const handleStateChange = (0,
|
|
7456
|
+
const handleStateChange = (0, import_react5.useCallback)((state) => {
|
|
5998
7457
|
if (!activeStateElRef.current) return;
|
|
5999
7458
|
const el = activeStateElRef.current;
|
|
6000
7459
|
if (state === "Default") {
|
|
@@ -6007,18 +7466,30 @@ function OhhwellsBridge() {
|
|
|
6007
7466
|
}
|
|
6008
7467
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
6009
7468
|
}, [deactivate]);
|
|
6010
|
-
const closeLinkPopover = (0,
|
|
6011
|
-
const openLinkPopoverForActive = (0,
|
|
7469
|
+
const closeLinkPopover = (0, import_react5.useCallback)(() => setLinkPopover(null), []);
|
|
7470
|
+
const openLinkPopoverForActive = (0, import_react5.useCallback)(() => {
|
|
6012
7471
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6013
7472
|
if (!hrefCtx) return;
|
|
6014
7473
|
bumpLinkPopoverGrace();
|
|
6015
7474
|
setLinkPopover({
|
|
6016
7475
|
key: hrefCtx.key,
|
|
6017
|
-
target: getLinkHref(hrefCtx.anchor)
|
|
6018
|
-
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
7476
|
+
target: getLinkHref(hrefCtx.anchor)
|
|
6019
7477
|
});
|
|
6020
|
-
|
|
6021
|
-
|
|
7478
|
+
deactivate();
|
|
7479
|
+
}, [deactivate]);
|
|
7480
|
+
const openLinkPopoverForSelected = (0, import_react5.useCallback)(() => {
|
|
7481
|
+
const anchor = selectedElRef.current;
|
|
7482
|
+
if (!anchor) return;
|
|
7483
|
+
const key = anchor.getAttribute("data-ohw-href-key");
|
|
7484
|
+
if (!key) return;
|
|
7485
|
+
bumpLinkPopoverGrace();
|
|
7486
|
+
setLinkPopover({
|
|
7487
|
+
key,
|
|
7488
|
+
target: getLinkHref(anchor)
|
|
7489
|
+
});
|
|
7490
|
+
deselect();
|
|
7491
|
+
}, [deselect]);
|
|
7492
|
+
const handleLinkPopoverSubmit = (0, import_react5.useCallback)(
|
|
6022
7493
|
(target) => {
|
|
6023
7494
|
if (!linkPopover) return;
|
|
6024
7495
|
const { key } = linkPopover;
|
|
@@ -6031,13 +7502,15 @@ function OhhwellsBridge() {
|
|
|
6031
7502
|
const showEditLink = toolbarShowEditLink;
|
|
6032
7503
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
6033
7504
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
6034
|
-
return bridgeRoot ? (0,
|
|
6035
|
-
/* @__PURE__ */ (0,
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
/* @__PURE__ */ (0,
|
|
7505
|
+
return bridgeRoot ? (0, import_react_dom2.createPortal)(
|
|
7506
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
7507
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
7508
|
+
toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
7509
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
7510
|
+
toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6039
7511
|
FloatingToolbar,
|
|
6040
7512
|
{
|
|
7513
|
+
variant: "rich-text",
|
|
6041
7514
|
rect: toolbarRect,
|
|
6042
7515
|
parentScroll: parentScrollRef.current,
|
|
6043
7516
|
elRef: toolbarElRef,
|
|
@@ -6046,9 +7519,21 @@ function OhhwellsBridge() {
|
|
|
6046
7519
|
showEditLink,
|
|
6047
7520
|
onEditLink: openLinkPopoverForActive
|
|
6048
7521
|
}
|
|
7522
|
+
),
|
|
7523
|
+
toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7524
|
+
FloatingToolbar,
|
|
7525
|
+
{
|
|
7526
|
+
variant: "link-action",
|
|
7527
|
+
rect: toolbarRect,
|
|
7528
|
+
parentScroll: parentScrollRef.current,
|
|
7529
|
+
elRef: toolbarElRef,
|
|
7530
|
+
onCommand: handleCommand,
|
|
7531
|
+
activeCommands,
|
|
7532
|
+
onEditLink: openLinkPopoverForSelected
|
|
7533
|
+
}
|
|
6049
7534
|
)
|
|
6050
7535
|
] }),
|
|
6051
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
7536
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
6052
7537
|
"div",
|
|
6053
7538
|
{
|
|
6054
7539
|
"data-ohw-max-badge": "",
|
|
@@ -6074,7 +7559,7 @@ function OhhwellsBridge() {
|
|
|
6074
7559
|
]
|
|
6075
7560
|
}
|
|
6076
7561
|
),
|
|
6077
|
-
toggleState && /* @__PURE__ */ (0,
|
|
7562
|
+
toggleState && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6078
7563
|
StateToggle,
|
|
6079
7564
|
{
|
|
6080
7565
|
rect: toggleState.rect,
|
|
@@ -6083,12 +7568,36 @@ function OhhwellsBridge() {
|
|
|
6083
7568
|
onStateChange: handleStateChange
|
|
6084
7569
|
}
|
|
6085
7570
|
),
|
|
6086
|
-
|
|
7571
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
7572
|
+
"div",
|
|
7573
|
+
{
|
|
7574
|
+
"data-ohw-section-insert-line": "",
|
|
7575
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7576
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7577
|
+
children: [
|
|
7578
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7579
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7580
|
+
Badge,
|
|
7581
|
+
{
|
|
7582
|
+
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",
|
|
7583
|
+
onClick: () => {
|
|
7584
|
+
window.parent.postMessage(
|
|
7585
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7586
|
+
"*"
|
|
7587
|
+
);
|
|
7588
|
+
},
|
|
7589
|
+
children: "Add Section"
|
|
7590
|
+
}
|
|
7591
|
+
),
|
|
7592
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7593
|
+
]
|
|
7594
|
+
}
|
|
7595
|
+
),
|
|
7596
|
+
linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6087
7597
|
LinkPopover,
|
|
6088
7598
|
{
|
|
6089
|
-
rect: linkPopover.rect,
|
|
6090
|
-
parentScroll: parentScrollRef.current,
|
|
6091
7599
|
panelRef: linkPopoverPanelRef,
|
|
7600
|
+
portalContainer: dialogPortalContainer,
|
|
6092
7601
|
open: true,
|
|
6093
7602
|
mode: "edit",
|
|
6094
7603
|
pages: sitePages,
|
|
@@ -6097,8 +7606,34 @@ function OhhwellsBridge() {
|
|
|
6097
7606
|
initialTarget: linkPopover.target,
|
|
6098
7607
|
onClose: closeLinkPopover,
|
|
6099
7608
|
onSubmit: handleLinkPopoverSubmit
|
|
7609
|
+
},
|
|
7610
|
+
`${linkPopover.key}-${pathname}`
|
|
7611
|
+
) : null,
|
|
7612
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
7613
|
+
"div",
|
|
7614
|
+
{
|
|
7615
|
+
"data-ohw-section-insert-line": "",
|
|
7616
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7617
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7618
|
+
children: [
|
|
7619
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7620
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7621
|
+
Badge,
|
|
7622
|
+
{
|
|
7623
|
+
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",
|
|
7624
|
+
onClick: () => {
|
|
7625
|
+
window.parent.postMessage(
|
|
7626
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7627
|
+
"*"
|
|
7628
|
+
);
|
|
7629
|
+
},
|
|
7630
|
+
children: "Add Section"
|
|
7631
|
+
}
|
|
7632
|
+
),
|
|
7633
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7634
|
+
]
|
|
6100
7635
|
}
|
|
6101
|
-
)
|
|
7636
|
+
)
|
|
6102
7637
|
] }),
|
|
6103
7638
|
bridgeRoot
|
|
6104
7639
|
) : null;
|
|
@@ -6108,6 +7643,7 @@ function OhhwellsBridge() {
|
|
|
6108
7643
|
LinkEditorPanel,
|
|
6109
7644
|
LinkPopover,
|
|
6110
7645
|
OhhwellsBridge,
|
|
7646
|
+
SchedulingWidget,
|
|
6111
7647
|
Toggle,
|
|
6112
7648
|
ToggleGroup,
|
|
6113
7649
|
ToggleGroupItem,
|