@ohhwells/bridge 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +234 -1312
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -7
- package/dist/index.d.ts +1 -7
- package/dist/index.js +210 -1287
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -445
- package/package.json +1 -2
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,6 @@ __export(index_exports, {
|
|
|
34
34
|
LinkEditorPanel: () => LinkEditorPanel,
|
|
35
35
|
LinkPopover: () => LinkPopover,
|
|
36
36
|
OhhwellsBridge: () => OhhwellsBridge,
|
|
37
|
-
SchedulingWidget: () => SchedulingWidget,
|
|
38
37
|
Toggle: () => Toggle,
|
|
39
38
|
ToggleGroup: () => ToggleGroup,
|
|
40
39
|
ToggleGroupItem: () => ToggleGroupItem,
|
|
@@ -49,752 +48,10 @@ __export(index_exports, {
|
|
|
49
48
|
module.exports = __toCommonJS(index_exports);
|
|
50
49
|
|
|
51
50
|
// src/OhhwellsBridge.tsx
|
|
52
|
-
var
|
|
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 getApiDomain() {
|
|
216
|
-
const h = window.location.hostname;
|
|
217
|
-
for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
|
|
218
|
-
if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
|
|
219
|
-
}
|
|
220
|
-
return h;
|
|
221
|
-
}
|
|
222
|
-
function classRunsOnDate(cls, date, type) {
|
|
223
|
-
if (type === "FIXED") {
|
|
224
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
225
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
226
|
-
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
227
|
-
}
|
|
228
|
-
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
229
|
-
}
|
|
230
|
-
function formatClassTime(cls) {
|
|
231
|
-
let h = parseInt(cls.startTime.hour, 10);
|
|
232
|
-
const m = parseInt(cls.startTime.minutes, 10);
|
|
233
|
-
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
234
|
-
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
235
|
-
const endTotal = h * 60 + m + cls.duration;
|
|
236
|
-
const eh = Math.floor(endTotal / 60) % 24;
|
|
237
|
-
const em = endTotal % 60;
|
|
238
|
-
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
239
|
-
}
|
|
240
|
-
function getBookingsOnDate(cls, date) {
|
|
241
|
-
if (!cls.bookings?.length) return 0;
|
|
242
|
-
const ds = date.toISOString().split("T")[0];
|
|
243
|
-
return cls.bookings.filter((b) => {
|
|
244
|
-
try {
|
|
245
|
-
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
246
|
-
} catch {
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
}).length;
|
|
250
|
-
}
|
|
251
|
-
function buildTimezoneLabel(tz) {
|
|
252
|
-
try {
|
|
253
|
-
const now = /* @__PURE__ */ new Date();
|
|
254
|
-
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
255
|
-
hour: "2-digit",
|
|
256
|
-
minute: "2-digit",
|
|
257
|
-
hour12: false,
|
|
258
|
-
timeZone: tz
|
|
259
|
-
}).format(now);
|
|
260
|
-
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
261
|
-
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
262
|
-
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
263
|
-
} catch {
|
|
264
|
-
return tz;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
function EmptyState({ inEditor }) {
|
|
268
|
-
const handleAddSchedule = () => {
|
|
269
|
-
if (inEditor) {
|
|
270
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
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: [
|
|
274
|
-
/* @__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)(
|
|
275
|
-
"svg",
|
|
276
|
-
{
|
|
277
|
-
width: "20",
|
|
278
|
-
height: "20",
|
|
279
|
-
viewBox: "0 0 24 24",
|
|
280
|
-
fill: "none",
|
|
281
|
-
stroke: "var(--color-accent, #A89B83)",
|
|
282
|
-
strokeWidth: "1.5",
|
|
283
|
-
strokeLinecap: "round",
|
|
284
|
-
strokeLinejoin: "round",
|
|
285
|
-
children: [
|
|
286
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
287
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
288
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
289
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
290
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
291
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
292
|
-
]
|
|
293
|
-
}
|
|
294
|
-
) }),
|
|
295
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
|
|
296
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
297
|
-
/* @__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." })
|
|
298
|
-
] }),
|
|
299
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
300
|
-
"button",
|
|
301
|
-
{
|
|
302
|
-
onClick: handleAddSchedule,
|
|
303
|
-
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",
|
|
304
|
-
children: "Add Schedule"
|
|
305
|
-
}
|
|
306
|
-
)
|
|
307
|
-
] });
|
|
308
|
-
}
|
|
309
|
-
function LoadingSkeleton() {
|
|
310
|
-
const shimmer = {
|
|
311
|
-
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
312
|
-
backgroundSize: "200% 100%",
|
|
313
|
-
animation: "ohw-shimmer 1.5s infinite"
|
|
314
|
-
};
|
|
315
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
316
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
317
|
-
/* @__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: [
|
|
318
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
319
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
320
|
-
] }, i)) }),
|
|
321
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
322
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:hidden", children: [
|
|
323
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0", children: [
|
|
324
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
|
|
325
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
|
|
326
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
|
|
327
|
-
] }),
|
|
328
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
329
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
|
|
330
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
|
|
331
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
|
|
332
|
-
] })
|
|
333
|
-
] }),
|
|
334
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
|
|
335
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
336
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
337
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
338
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
339
|
-
] }),
|
|
340
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
341
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
342
|
-
] }),
|
|
343
|
-
i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
344
|
-
] }, i))
|
|
345
|
-
] });
|
|
346
|
-
}
|
|
347
|
-
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
348
|
-
const todayMs = (0, import_react2.useMemo)(() => {
|
|
349
|
-
const d = /* @__PURE__ */ new Date();
|
|
350
|
-
d.setHours(0, 0, 0, 0);
|
|
351
|
-
return d.getTime();
|
|
352
|
-
}, []);
|
|
353
|
-
const scrollRef = (0, import_react2.useRef)(null);
|
|
354
|
-
const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
|
|
355
|
-
const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
|
|
356
|
-
const updateScrollState = () => {
|
|
357
|
-
const el = scrollRef.current;
|
|
358
|
-
if (!el) return;
|
|
359
|
-
setCanScrollLeft(el.scrollLeft > 0);
|
|
360
|
-
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
361
|
-
};
|
|
362
|
-
(0, import_react2.useEffect)(() => {
|
|
363
|
-
const el = scrollRef.current;
|
|
364
|
-
if (!el) return;
|
|
365
|
-
updateScrollState();
|
|
366
|
-
el.addEventListener("scroll", updateScrollState);
|
|
367
|
-
const ro = new ResizeObserver(updateScrollState);
|
|
368
|
-
ro.observe(el);
|
|
369
|
-
return () => {
|
|
370
|
-
el.removeEventListener("scroll", updateScrollState);
|
|
371
|
-
ro.disconnect();
|
|
372
|
-
};
|
|
373
|
-
}, [dates]);
|
|
374
|
-
const scroll = (dir) => {
|
|
375
|
-
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
376
|
-
};
|
|
377
|
-
const isDragging = (0, import_react2.useRef)(false);
|
|
378
|
-
const dragStartX = (0, import_react2.useRef)(0);
|
|
379
|
-
const dragStartScrollLeft = (0, import_react2.useRef)(0);
|
|
380
|
-
const onDragStart = (e) => {
|
|
381
|
-
if (!scrollRef.current) return;
|
|
382
|
-
isDragging.current = true;
|
|
383
|
-
dragStartX.current = e.clientX;
|
|
384
|
-
dragStartScrollLeft.current = scrollRef.current.scrollLeft;
|
|
385
|
-
scrollRef.current.style.cursor = "grabbing";
|
|
386
|
-
scrollRef.current.style.userSelect = "none";
|
|
387
|
-
};
|
|
388
|
-
const onDragMove = (e) => {
|
|
389
|
-
if (!isDragging.current || !scrollRef.current) return;
|
|
390
|
-
const delta = e.clientX - dragStartX.current;
|
|
391
|
-
scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
|
|
392
|
-
};
|
|
393
|
-
const onDragEnd = () => {
|
|
394
|
-
if (!scrollRef.current) return;
|
|
395
|
-
isDragging.current = false;
|
|
396
|
-
scrollRef.current.style.cursor = "grab";
|
|
397
|
-
scrollRef.current.style.userSelect = "";
|
|
398
|
-
};
|
|
399
|
-
const datesWithClasses = (0, import_react2.useMemo)(
|
|
400
|
-
() => new Set(dates.map(
|
|
401
|
-
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
402
|
-
).filter((i) => i >= 0)),
|
|
403
|
-
[dates, schedule]
|
|
404
|
-
);
|
|
405
|
-
const selectedDate = dates[selectedIdx];
|
|
406
|
-
const selectedClasses = (0, import_react2.useMemo)(
|
|
407
|
-
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
408
|
-
[schedule, selectedDate]
|
|
409
|
-
);
|
|
410
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
411
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full", children: [
|
|
412
|
-
canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
413
|
-
"button",
|
|
414
|
-
{
|
|
415
|
-
onClick: () => scroll("left"),
|
|
416
|
-
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",
|
|
417
|
-
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
418
|
-
"aria-label": "Scroll left",
|
|
419
|
-
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" }) })
|
|
420
|
-
}
|
|
421
|
-
),
|
|
422
|
-
canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
423
|
-
"button",
|
|
424
|
-
{
|
|
425
|
-
onClick: () => scroll("right"),
|
|
426
|
-
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",
|
|
427
|
-
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
428
|
-
"aria-label": "Scroll right",
|
|
429
|
-
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" }) })
|
|
430
|
-
}
|
|
431
|
-
),
|
|
432
|
-
canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
433
|
-
"div",
|
|
434
|
-
{
|
|
435
|
-
className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
436
|
-
style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
|
|
437
|
-
}
|
|
438
|
-
),
|
|
439
|
-
canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
440
|
-
"div",
|
|
441
|
-
{
|
|
442
|
-
className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
443
|
-
style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
|
|
444
|
-
}
|
|
445
|
-
),
|
|
446
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
447
|
-
"div",
|
|
448
|
-
{
|
|
449
|
-
ref: scrollRef,
|
|
450
|
-
className: "overflow-x-auto w-full",
|
|
451
|
-
style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
|
|
452
|
-
onMouseDown: onDragStart,
|
|
453
|
-
onMouseMove: onDragMove,
|
|
454
|
-
onMouseUp: onDragEnd,
|
|
455
|
-
onMouseLeave: onDragEnd,
|
|
456
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
457
|
-
const isToday = date.getTime() === todayMs;
|
|
458
|
-
const isSelected = i === selectedIdx;
|
|
459
|
-
const clickable = datesWithClasses.has(i);
|
|
460
|
-
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
461
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
462
|
-
"div",
|
|
463
|
-
{
|
|
464
|
-
onClick: () => clickable && onSelectDate(i),
|
|
465
|
-
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
466
|
-
children: [
|
|
467
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
468
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
469
|
-
"div",
|
|
470
|
-
{
|
|
471
|
-
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
472
|
-
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
473
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
474
|
-
"span",
|
|
475
|
-
{
|
|
476
|
-
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
477
|
-
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
478
|
-
children: date.getDate()
|
|
479
|
-
}
|
|
480
|
-
)
|
|
481
|
-
}
|
|
482
|
-
)
|
|
483
|
-
]
|
|
484
|
-
},
|
|
485
|
-
i
|
|
486
|
-
);
|
|
487
|
-
}) })
|
|
488
|
-
}
|
|
489
|
-
)
|
|
490
|
-
] }),
|
|
491
|
-
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) => {
|
|
492
|
-
const booked = getBookingsOnDate(cls, selectedDate);
|
|
493
|
-
const available = cls.maxParticipants - booked;
|
|
494
|
-
const isFull = available <= 0;
|
|
495
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
496
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
497
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
498
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
499
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
500
|
-
"span",
|
|
501
|
-
{
|
|
502
|
-
className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
|
|
503
|
-
style: { borderColor: "#0885FE", color: "#0885FE" },
|
|
504
|
-
children: "GROUP"
|
|
505
|
-
}
|
|
506
|
-
),
|
|
507
|
-
/* @__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: [
|
|
508
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
509
|
-
available,
|
|
510
|
-
"/",
|
|
511
|
-
cls.maxParticipants
|
|
512
|
-
] }),
|
|
513
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
514
|
-
] }) })
|
|
515
|
-
] }),
|
|
516
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
517
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
518
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
519
|
-
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 })
|
|
520
|
-
] }),
|
|
521
|
-
/* @__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: [
|
|
522
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
523
|
-
available,
|
|
524
|
-
"/",
|
|
525
|
-
cls.maxParticipants
|
|
526
|
-
] }),
|
|
527
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
528
|
-
] }) }),
|
|
529
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
530
|
-
"button",
|
|
531
|
-
{
|
|
532
|
-
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",
|
|
533
|
-
style: isFull ? {
|
|
534
|
-
background: "transparent",
|
|
535
|
-
border: "1px solid var(--color-dark, #200C02)",
|
|
536
|
-
color: "var(--color-dark, #200C02)"
|
|
537
|
-
} : {
|
|
538
|
-
background: "var(--color-primary, #3D312B)",
|
|
539
|
-
border: "none",
|
|
540
|
-
color: "var(--color-light, #FEFFF0)"
|
|
541
|
-
},
|
|
542
|
-
onClick: () => {
|
|
543
|
-
if (!cls.id) return;
|
|
544
|
-
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
545
|
-
},
|
|
546
|
-
children: isFull ? "Join Waitlist" : "Book Now"
|
|
547
|
-
}
|
|
548
|
-
)
|
|
549
|
-
] })
|
|
550
|
-
] }),
|
|
551
|
-
i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
552
|
-
] }, cls.id ?? i);
|
|
553
|
-
}) })
|
|
554
|
-
] });
|
|
555
|
-
}
|
|
556
|
-
function CalendarFoldIcon() {
|
|
557
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
558
|
-
"svg",
|
|
559
|
-
{
|
|
560
|
-
width: "16",
|
|
561
|
-
height: "16",
|
|
562
|
-
viewBox: "0 0 24 24",
|
|
563
|
-
fill: "none",
|
|
564
|
-
stroke: "currentColor",
|
|
565
|
-
strokeWidth: "2",
|
|
566
|
-
strokeLinecap: "round",
|
|
567
|
-
strokeLinejoin: "round",
|
|
568
|
-
style: { flexShrink: 0 },
|
|
569
|
-
children: [
|
|
570
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v4" }),
|
|
571
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 2v4" }),
|
|
572
|
-
/* @__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" }),
|
|
573
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 10h18" }),
|
|
574
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
575
|
-
]
|
|
576
|
-
}
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
580
|
-
const [schedule, setSchedule] = (0, import_react2.useState)(null);
|
|
581
|
-
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
582
|
-
const [inEditor, setInEditor] = (0, import_react2.useState)(false);
|
|
583
|
-
const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
|
|
584
|
-
const [modalState, setModalState] = (0, import_react2.useState)(null);
|
|
585
|
-
const switchScheduleIdRef = (0, import_react2.useRef)(null);
|
|
586
|
-
const dates = (0, import_react2.useMemo)(() => {
|
|
587
|
-
const today = /* @__PURE__ */ new Date();
|
|
588
|
-
today.setHours(0, 0, 0, 0);
|
|
589
|
-
return Array.from({ length: 14 }, (_, i) => {
|
|
590
|
-
const d = new Date(today);
|
|
591
|
-
d.setDate(today.getDate() + i);
|
|
592
|
-
return d;
|
|
593
|
-
});
|
|
594
|
-
}, []);
|
|
595
|
-
const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
|
|
596
|
-
(0, import_react2.useEffect)(() => {
|
|
597
|
-
if (!schedule?.classes) return;
|
|
598
|
-
for (let i = 0; i < dates.length; i++) {
|
|
599
|
-
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
600
|
-
setSelectedIdx(i);
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}, [schedule, dates]);
|
|
605
|
-
(0, import_react2.useEffect)(() => {
|
|
606
|
-
if (typeof window === "undefined") return;
|
|
607
|
-
const isInEditor = window.self !== window.top;
|
|
608
|
-
setInEditor(isInEditor);
|
|
609
|
-
if (isInEditor) {
|
|
610
|
-
const startEmpty = initialScheduleId === null;
|
|
611
|
-
if (startEmpty) setLoading(false);
|
|
612
|
-
let initialized = startEmpty;
|
|
613
|
-
const timer = !startEmpty ? setTimeout(() => {
|
|
614
|
-
if (!initialized) {
|
|
615
|
-
initialized = true;
|
|
616
|
-
setLoading(false);
|
|
617
|
-
}
|
|
618
|
-
}, 5e3) : null;
|
|
619
|
-
const handler = (e) => {
|
|
620
|
-
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
621
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
622
|
-
setSchedule(null);
|
|
623
|
-
setLoading(false);
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
if (e.data?.type === "ow:switch-schedule") {
|
|
627
|
-
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
628
|
-
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
629
|
-
initialized = false;
|
|
630
|
-
setLoading(true);
|
|
631
|
-
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
635
|
-
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
636
|
-
if (initialized && switchScheduleIdRef.current === null) return;
|
|
637
|
-
initialized = true;
|
|
638
|
-
if (timer) clearTimeout(timer);
|
|
639
|
-
const schedules = e.data.schedules ?? [];
|
|
640
|
-
const isSwitching = switchScheduleIdRef.current !== null;
|
|
641
|
-
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : initialScheduleId ? schedules.find((s) => s.id === initialScheduleId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
642
|
-
switchScheduleIdRef.current = null;
|
|
643
|
-
setSchedule(target);
|
|
644
|
-
setLoading(false);
|
|
645
|
-
if (notifyOnConnect && target && !isSwitching) {
|
|
646
|
-
window.parent.postMessage({
|
|
647
|
-
type: "ow:schedule-connected",
|
|
648
|
-
schedule: { id: target.id, name: target.name },
|
|
649
|
-
insertAfter
|
|
650
|
-
}, "*");
|
|
651
|
-
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
window.addEventListener("message", handler);
|
|
655
|
-
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
656
|
-
return () => {
|
|
657
|
-
window.removeEventListener("message", handler);
|
|
658
|
-
if (timer) clearTimeout(timer);
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
if (initialScheduleId === null) {
|
|
662
|
-
setLoading(false);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
;
|
|
666
|
-
(async () => {
|
|
667
|
-
try {
|
|
668
|
-
if (initialScheduleId) {
|
|
669
|
-
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
670
|
-
const data = await res.json();
|
|
671
|
-
setSchedule(data?.id ? data : null);
|
|
672
|
-
} else {
|
|
673
|
-
const domain = getApiDomain();
|
|
674
|
-
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
675
|
-
const slugs = await sitemapRes.json();
|
|
676
|
-
if (!Array.isArray(slugs) || !slugs.length) {
|
|
677
|
-
setLoading(false);
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
const { slug } = [...slugs].sort(
|
|
681
|
-
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
682
|
-
)[0];
|
|
683
|
-
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
684
|
-
const data = await res.json();
|
|
685
|
-
setSchedule(data?.id ? data : null);
|
|
686
|
-
}
|
|
687
|
-
} catch {
|
|
688
|
-
}
|
|
689
|
-
setLoading(false);
|
|
690
|
-
})();
|
|
691
|
-
}, []);
|
|
692
|
-
const timezoneLabel = schedule?.timezone ? (() => {
|
|
693
|
-
try {
|
|
694
|
-
return buildTimezoneLabel(schedule.timezone);
|
|
695
|
-
} catch {
|
|
696
|
-
return void 0;
|
|
697
|
-
}
|
|
698
|
-
})() : void 0;
|
|
699
|
-
const handleModalSubmit = async (email) => {
|
|
700
|
-
if (!modalState) return;
|
|
701
|
-
const { mode, classId, classDate } = modalState;
|
|
702
|
-
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
703
|
-
const res = await fetch(endpoint, {
|
|
704
|
-
method: "POST",
|
|
705
|
-
headers: { "Content-Type": "application/json" },
|
|
706
|
-
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
707
|
-
});
|
|
708
|
-
if (!res.ok) {
|
|
709
|
-
const data = await res.json().catch(() => ({}));
|
|
710
|
-
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
711
|
-
}
|
|
712
|
-
};
|
|
713
|
-
const handleReplaceSchedule = () => {
|
|
714
|
-
setIsHovered(false);
|
|
715
|
-
if (schedule) {
|
|
716
|
-
window.parent.postMessage({
|
|
717
|
-
type: "ow:schedule-connected",
|
|
718
|
-
schedule: { id: schedule.id, name: schedule.name },
|
|
719
|
-
insertAfter
|
|
720
|
-
}, "*");
|
|
721
|
-
} else {
|
|
722
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
const sectionId = `scheduling-${insertAfter}`;
|
|
726
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
727
|
-
"section",
|
|
728
|
-
{
|
|
729
|
-
"data-ohw-section": sectionId,
|
|
730
|
-
"data-ohw-scheduling-anchor": insertAfter,
|
|
731
|
-
className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
732
|
-
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
733
|
-
onMouseLeave: () => setIsHovered(false),
|
|
734
|
-
children: [
|
|
735
|
-
inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
736
|
-
"div",
|
|
737
|
-
{
|
|
738
|
-
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
739
|
-
onClick: handleReplaceSchedule,
|
|
740
|
-
children: [
|
|
741
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
742
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
743
|
-
/* @__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)(
|
|
744
|
-
"div",
|
|
745
|
-
{
|
|
746
|
-
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
747
|
-
style: {
|
|
748
|
-
fontSize: 14,
|
|
749
|
-
lineHeight: "24px",
|
|
750
|
-
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
751
|
-
fontWeight: 500,
|
|
752
|
-
color: "#0C0A09"
|
|
753
|
-
},
|
|
754
|
-
children: [
|
|
755
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
|
|
756
|
-
"Replace schedule"
|
|
757
|
-
]
|
|
758
|
-
}
|
|
759
|
-
) })
|
|
760
|
-
]
|
|
761
|
-
}
|
|
762
|
-
),
|
|
763
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
764
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
765
|
-
/* @__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" }),
|
|
766
|
-
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 })
|
|
767
|
-
] }),
|
|
768
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
769
|
-
timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
770
|
-
/* @__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 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
771
|
-
ScheduleView,
|
|
772
|
-
{
|
|
773
|
-
schedule,
|
|
774
|
-
dates,
|
|
775
|
-
selectedIdx,
|
|
776
|
-
onSelectDate: setSelectedIdx,
|
|
777
|
-
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
778
|
-
}
|
|
779
|
-
) })
|
|
780
|
-
] })
|
|
781
|
-
] }),
|
|
782
|
-
modalState && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
783
|
-
EmailCaptureModal,
|
|
784
|
-
{
|
|
785
|
-
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
786
|
-
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
787
|
-
onSubmit: handleModalSubmit,
|
|
788
|
-
onClose: () => setModalState(null)
|
|
789
|
-
}
|
|
790
|
-
)
|
|
791
|
-
]
|
|
792
|
-
}
|
|
793
|
-
);
|
|
794
|
-
}
|
|
51
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
795
52
|
|
|
796
53
|
// src/ui/toggle-group.tsx
|
|
797
|
-
var
|
|
54
|
+
var React = __toESM(require("react"), 1);
|
|
798
55
|
|
|
799
56
|
// node_modules/clsx/dist/clsx.mjs
|
|
800
57
|
function r(e) {
|
|
@@ -4113,8 +3370,8 @@ var cva = (base, config) => (props) => {
|
|
|
4113
3370
|
};
|
|
4114
3371
|
|
|
4115
3372
|
// src/ui/toggle.tsx
|
|
4116
|
-
var
|
|
4117
|
-
var
|
|
3373
|
+
var import_radix_ui = require("radix-ui");
|
|
3374
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
4118
3375
|
var toggleVariants = cva(
|
|
4119
3376
|
"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",
|
|
4120
3377
|
{
|
|
@@ -4141,8 +3398,8 @@ function Toggle({
|
|
|
4141
3398
|
size,
|
|
4142
3399
|
...props
|
|
4143
3400
|
}) {
|
|
4144
|
-
return /* @__PURE__ */ (0,
|
|
4145
|
-
|
|
3401
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3402
|
+
import_radix_ui.Toggle.Root,
|
|
4146
3403
|
{
|
|
4147
3404
|
"data-slot": "toggle",
|
|
4148
3405
|
className: cn(toggleVariants({ variant, size, className })),
|
|
@@ -4152,8 +3409,8 @@ function Toggle({
|
|
|
4152
3409
|
}
|
|
4153
3410
|
|
|
4154
3411
|
// src/ui/toggle-group.tsx
|
|
4155
|
-
var
|
|
4156
|
-
var ToggleGroupContext =
|
|
3412
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
3413
|
+
var ToggleGroupContext = React.createContext({
|
|
4157
3414
|
value: "",
|
|
4158
3415
|
onValueChange: () => {
|
|
4159
3416
|
}
|
|
@@ -4165,13 +3422,13 @@ function ToggleGroup({
|
|
|
4165
3422
|
children,
|
|
4166
3423
|
...props
|
|
4167
3424
|
}) {
|
|
4168
|
-
return /* @__PURE__ */ (0,
|
|
3425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
4169
3426
|
"div",
|
|
4170
3427
|
{
|
|
4171
3428
|
role: "group",
|
|
4172
3429
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
4173
3430
|
...props,
|
|
4174
|
-
children: /* @__PURE__ */ (0,
|
|
3431
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
4175
3432
|
}
|
|
4176
3433
|
);
|
|
4177
3434
|
}
|
|
@@ -4181,8 +3438,8 @@ function ToggleGroupItem({
|
|
|
4181
3438
|
children,
|
|
4182
3439
|
...props
|
|
4183
3440
|
}) {
|
|
4184
|
-
const { value: groupValue, onValueChange } =
|
|
4185
|
-
return /* @__PURE__ */ (0,
|
|
3441
|
+
const { value: groupValue, onValueChange } = React.useContext(ToggleGroupContext);
|
|
3442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
4186
3443
|
Toggle,
|
|
4187
3444
|
{
|
|
4188
3445
|
pressed: groupValue === value,
|
|
@@ -4202,7 +3459,7 @@ function ToggleGroupItem({
|
|
|
4202
3459
|
}
|
|
4203
3460
|
|
|
4204
3461
|
// src/OhhwellsBridge.tsx
|
|
4205
|
-
var
|
|
3462
|
+
var import_react_dom = require("react-dom");
|
|
4206
3463
|
var import_navigation = require("next/navigation");
|
|
4207
3464
|
|
|
4208
3465
|
// src/lib/session-search.ts
|
|
@@ -4311,7 +3568,10 @@ function isValidUrl(urlString) {
|
|
|
4311
3568
|
const hostname = url.hostname;
|
|
4312
3569
|
if (!hostname || hostname.length === 0) return false;
|
|
4313
3570
|
if (hostname.includes(" ")) return false;
|
|
4314
|
-
|
|
3571
|
+
if (hostname === "localhost") return true;
|
|
3572
|
+
if (!hostname.includes(".")) return false;
|
|
3573
|
+
if (!/[a-z]/i.test(hostname)) return false;
|
|
3574
|
+
return true;
|
|
4315
3575
|
} catch {
|
|
4316
3576
|
return false;
|
|
4317
3577
|
}
|
|
@@ -4322,12 +3582,19 @@ function normalizeUrl(value) {
|
|
|
4322
3582
|
return `https://${trimmed}`;
|
|
4323
3583
|
}
|
|
4324
3584
|
function validateUrlInput(value, pages, allPages) {
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
if (
|
|
3585
|
+
const trimmed = value.trim();
|
|
3586
|
+
if (!trimmed) return "";
|
|
3587
|
+
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
3588
|
+
if (exactPage) return "";
|
|
3589
|
+
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
3590
|
+
if (hasPrefixMatch) {
|
|
3591
|
+
return "Select a page from the list or enter a valid URL";
|
|
3592
|
+
}
|
|
3593
|
+
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
3594
|
+
if (!hasScheme && !trimmed.includes(".")) {
|
|
3595
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
3596
|
+
}
|
|
3597
|
+
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
4331
3598
|
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4332
3599
|
}
|
|
4333
3600
|
return "";
|
|
@@ -4486,9 +3753,9 @@ function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
|
|
|
4486
3753
|
}
|
|
4487
3754
|
|
|
4488
3755
|
// src/ui/button.tsx
|
|
4489
|
-
var
|
|
4490
|
-
var
|
|
4491
|
-
var
|
|
3756
|
+
var React2 = __toESM(require("react"), 1);
|
|
3757
|
+
var import_radix_ui2 = require("radix-ui");
|
|
3758
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
4492
3759
|
var buttonVariants = cva(
|
|
4493
3760
|
"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",
|
|
4494
3761
|
{
|
|
@@ -4509,10 +3776,10 @@ var buttonVariants = cva(
|
|
|
4509
3776
|
}
|
|
4510
3777
|
}
|
|
4511
3778
|
);
|
|
4512
|
-
var Button =
|
|
3779
|
+
var Button = React2.forwardRef(
|
|
4513
3780
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
4514
|
-
const Comp = asChild ?
|
|
4515
|
-
return /* @__PURE__ */ (0,
|
|
3781
|
+
const Comp = asChild ? import_radix_ui2.Slot.Root : "button";
|
|
3782
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4516
3783
|
Comp,
|
|
4517
3784
|
{
|
|
4518
3785
|
ref,
|
|
@@ -4526,75 +3793,75 @@ var Button = React3.forwardRef(
|
|
|
4526
3793
|
Button.displayName = "Button";
|
|
4527
3794
|
|
|
4528
3795
|
// src/ui/icons.tsx
|
|
4529
|
-
var
|
|
3796
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
4530
3797
|
function IconX({ className, ...props }) {
|
|
4531
|
-
return /* @__PURE__ */ (0,
|
|
4532
|
-
/* @__PURE__ */ (0,
|
|
4533
|
-
/* @__PURE__ */ (0,
|
|
3798
|
+
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: [
|
|
3799
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M18 6 6 18" }),
|
|
3800
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m6 6 12 12" })
|
|
4534
3801
|
] });
|
|
4535
3802
|
}
|
|
4536
3803
|
function IconChevronDown({ className, ...props }) {
|
|
4537
|
-
return /* @__PURE__ */ (0,
|
|
3804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.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_runtime4.jsx)("path", { d: "m6 9 6 6 6-6" }) });
|
|
4538
3805
|
}
|
|
4539
3806
|
function IconFile({ className, ...props }) {
|
|
4540
|
-
return /* @__PURE__ */ (0,
|
|
4541
|
-
/* @__PURE__ */ (0,
|
|
4542
|
-
/* @__PURE__ */ (0,
|
|
3807
|
+
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: [
|
|
3808
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
3809
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4543
3810
|
] });
|
|
4544
3811
|
}
|
|
4545
3812
|
function IconInfo({ className, ...props }) {
|
|
4546
|
-
return /* @__PURE__ */ (0,
|
|
4547
|
-
/* @__PURE__ */ (0,
|
|
4548
|
-
/* @__PURE__ */ (0,
|
|
4549
|
-
/* @__PURE__ */ (0,
|
|
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" })
|
|
4550
3817
|
] });
|
|
4551
3818
|
}
|
|
4552
3819
|
function IconArrowRight({ className, ...props }) {
|
|
4553
|
-
return /* @__PURE__ */ (0,
|
|
4554
|
-
/* @__PURE__ */ (0,
|
|
4555
|
-
/* @__PURE__ */ (0,
|
|
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" })
|
|
4556
3823
|
] });
|
|
4557
3824
|
}
|
|
4558
3825
|
function IconSection({ className, ...props }) {
|
|
4559
|
-
return /* @__PURE__ */ (0,
|
|
4560
|
-
/* @__PURE__ */ (0,
|
|
4561
|
-
/* @__PURE__ */ (0,
|
|
4562
|
-
/* @__PURE__ */ (0,
|
|
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" })
|
|
4563
3830
|
] });
|
|
4564
3831
|
}
|
|
4565
3832
|
|
|
4566
3833
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
4567
|
-
var
|
|
3834
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
4568
3835
|
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
4569
|
-
return /* @__PURE__ */ (0,
|
|
4570
|
-
/* @__PURE__ */ (0,
|
|
4571
|
-
/* @__PURE__ */ (0,
|
|
4572
|
-
/* @__PURE__ */ (0,
|
|
4573
|
-
/* @__PURE__ */ (0,
|
|
4574
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
4575
3842
|
] }),
|
|
4576
|
-
/* @__PURE__ */ (0,
|
|
4577
|
-
/* @__PURE__ */ (0,
|
|
4578
|
-
/* @__PURE__ */ (0,
|
|
4579
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
4580
3847
|
] })
|
|
4581
3848
|
] })
|
|
4582
3849
|
] });
|
|
4583
3850
|
}
|
|
4584
3851
|
|
|
4585
3852
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
4586
|
-
var
|
|
3853
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
4587
3854
|
function SectionTreeItem({ section, onSelect, selected }) {
|
|
4588
3855
|
const interactive = Boolean(onSelect);
|
|
4589
|
-
return /* @__PURE__ */ (0,
|
|
4590
|
-
/* @__PURE__ */ (0,
|
|
3856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
3857
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4591
3858
|
"div",
|
|
4592
3859
|
{
|
|
4593
3860
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
4594
3861
|
"aria-hidden": true
|
|
4595
3862
|
}
|
|
4596
3863
|
),
|
|
4597
|
-
/* @__PURE__ */ (0,
|
|
3864
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
4598
3865
|
"div",
|
|
4599
3866
|
{
|
|
4600
3867
|
role: interactive ? "button" : void 0,
|
|
@@ -4612,8 +3879,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
4612
3879
|
interactive && selected && "border-primary"
|
|
4613
3880
|
),
|
|
4614
3881
|
children: [
|
|
4615
|
-
/* @__PURE__ */ (0,
|
|
4616
|
-
/* @__PURE__ */ (0,
|
|
3882
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
3883
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
4617
3884
|
]
|
|
4618
3885
|
}
|
|
4619
3886
|
)
|
|
@@ -4621,23 +3888,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
|
|
|
4621
3888
|
}
|
|
4622
3889
|
function SectionPickerList({ sections, onSelect }) {
|
|
4623
3890
|
if (sections.length === 0) {
|
|
4624
|
-
return /* @__PURE__ */ (0,
|
|
3891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
4625
3892
|
}
|
|
4626
|
-
return /* @__PURE__ */ (0,
|
|
4627
|
-
/* @__PURE__ */ (0,
|
|
4628
|
-
sections.map((section) => /* @__PURE__ */ (0,
|
|
3893
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3894
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
3895
|
+
sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionTreeItem, { section, onSelect }, section.id))
|
|
4629
3896
|
] });
|
|
4630
3897
|
}
|
|
4631
3898
|
|
|
4632
3899
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4633
|
-
var
|
|
3900
|
+
var import_react = require("react");
|
|
4634
3901
|
|
|
4635
3902
|
// src/ui/input.tsx
|
|
4636
|
-
var
|
|
4637
|
-
var
|
|
4638
|
-
var Input =
|
|
3903
|
+
var React3 = __toESM(require("react"), 1);
|
|
3904
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3905
|
+
var Input = React3.forwardRef(
|
|
4639
3906
|
({ className, type, ...props }, ref) => {
|
|
4640
|
-
return /* @__PURE__ */ (0,
|
|
3907
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4641
3908
|
"input",
|
|
4642
3909
|
{
|
|
4643
3910
|
type,
|
|
@@ -4655,11 +3922,11 @@ var Input = React4.forwardRef(
|
|
|
4655
3922
|
Input.displayName = "Input";
|
|
4656
3923
|
|
|
4657
3924
|
// src/ui/label.tsx
|
|
4658
|
-
var
|
|
4659
|
-
var
|
|
3925
|
+
var import_radix_ui3 = require("radix-ui");
|
|
3926
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4660
3927
|
function Label({ className, ...props }) {
|
|
4661
|
-
return /* @__PURE__ */ (0,
|
|
4662
|
-
|
|
3928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3929
|
+
import_radix_ui3.Label.Root,
|
|
4663
3930
|
{
|
|
4664
3931
|
"data-slot": "label",
|
|
4665
3932
|
className: cn("text-sm font-medium leading-5 text-foreground", className),
|
|
@@ -4669,13 +3936,13 @@ function Label({ className, ...props }) {
|
|
|
4669
3936
|
}
|
|
4670
3937
|
|
|
4671
3938
|
// src/ui/popover.tsx
|
|
4672
|
-
var
|
|
4673
|
-
var
|
|
3939
|
+
var import_radix_ui4 = require("radix-ui");
|
|
3940
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4674
3941
|
function Popover({ ...props }) {
|
|
4675
|
-
return /* @__PURE__ */ (0,
|
|
3942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Root, { "data-slot": "popover", ...props });
|
|
4676
3943
|
}
|
|
4677
3944
|
function PopoverTrigger({ ...props }) {
|
|
4678
|
-
return /* @__PURE__ */ (0,
|
|
3945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4679
3946
|
}
|
|
4680
3947
|
function PopoverContent({
|
|
4681
3948
|
className,
|
|
@@ -4683,8 +3950,8 @@ function PopoverContent({
|
|
|
4683
3950
|
sideOffset = 6,
|
|
4684
3951
|
...props
|
|
4685
3952
|
}) {
|
|
4686
|
-
return /* @__PURE__ */ (0,
|
|
4687
|
-
|
|
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,
|
|
4688
3955
|
{
|
|
4689
3956
|
"data-slot": "popover-content",
|
|
4690
3957
|
align,
|
|
@@ -4699,9 +3966,9 @@ function PopoverContent({
|
|
|
4699
3966
|
}
|
|
4700
3967
|
|
|
4701
3968
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4702
|
-
var
|
|
3969
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4703
3970
|
function FieldChevron({ onClick }) {
|
|
4704
|
-
return /* @__PURE__ */ (0,
|
|
3971
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4705
3972
|
"button",
|
|
4706
3973
|
{
|
|
4707
3974
|
type: "button",
|
|
@@ -4709,7 +3976,7 @@ function FieldChevron({ onClick }) {
|
|
|
4709
3976
|
onClick,
|
|
4710
3977
|
"aria-label": "Open page list",
|
|
4711
3978
|
tabIndex: -1,
|
|
4712
|
-
children: /* @__PURE__ */ (0,
|
|
3979
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconChevronDown, {})
|
|
4713
3980
|
}
|
|
4714
3981
|
);
|
|
4715
3982
|
}
|
|
@@ -4725,9 +3992,9 @@ function UrlOrPageInput({
|
|
|
4725
3992
|
readOnly = false,
|
|
4726
3993
|
urlError
|
|
4727
3994
|
}) {
|
|
4728
|
-
const inputId = (0,
|
|
4729
|
-
const inputRef = (0,
|
|
4730
|
-
const [isFocused, setIsFocused] = (0,
|
|
3995
|
+
const inputId = (0, import_react.useId)();
|
|
3996
|
+
const inputRef = (0, import_react.useRef)(null);
|
|
3997
|
+
const [isFocused, setIsFocused] = (0, import_react.useState)(false);
|
|
4731
3998
|
const openDropdown = () => {
|
|
4732
3999
|
if (readOnly || filteredPages.length === 0) return;
|
|
4733
4000
|
onDropdownOpenChange(true);
|
|
@@ -4748,12 +4015,12 @@ function UrlOrPageInput({
|
|
|
4748
4015
|
"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]",
|
|
4749
4016
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
4750
4017
|
);
|
|
4751
|
-
return /* @__PURE__ */ (0,
|
|
4752
|
-
/* @__PURE__ */ (0,
|
|
4753
|
-
/* @__PURE__ */ (0,
|
|
4754
|
-
/* @__PURE__ */ (0,
|
|
4755
|
-
selectedPage ? /* @__PURE__ */ (0,
|
|
4756
|
-
readOnly ? /* @__PURE__ */ (0,
|
|
4018
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4019
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4020
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
|
|
4021
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
4022
|
+
selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4023
|
+
readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4757
4024
|
Input,
|
|
4758
4025
|
{
|
|
4759
4026
|
ref: inputRef,
|
|
@@ -4772,7 +4039,7 @@ function UrlOrPageInput({
|
|
|
4772
4039
|
)
|
|
4773
4040
|
}
|
|
4774
4041
|
),
|
|
4775
|
-
selectedPage && !readOnly ? /* @__PURE__ */ (0,
|
|
4042
|
+
selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4776
4043
|
"button",
|
|
4777
4044
|
{
|
|
4778
4045
|
type: "button",
|
|
@@ -4780,31 +4047,31 @@ function UrlOrPageInput({
|
|
|
4780
4047
|
onMouseDown: clearSelection,
|
|
4781
4048
|
"aria-label": "Clear selected page",
|
|
4782
4049
|
tabIndex: -1,
|
|
4783
|
-
children: /* @__PURE__ */ (0,
|
|
4050
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconX, { "aria-hidden": true })
|
|
4784
4051
|
}
|
|
4785
4052
|
) : null,
|
|
4786
|
-
!readOnly ? /* @__PURE__ */ (0,
|
|
4053
|
+
!readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4787
4054
|
] }) }),
|
|
4788
|
-
filteredPages.length > 0 && /* @__PURE__ */ (0,
|
|
4055
|
+
filteredPages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
4789
4056
|
"button",
|
|
4790
4057
|
{
|
|
4791
4058
|
type: "button",
|
|
4792
4059
|
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",
|
|
4793
4060
|
onClick: () => onPageSelect(page),
|
|
4794
4061
|
children: [
|
|
4795
|
-
/* @__PURE__ */ (0,
|
|
4796
|
-
/* @__PURE__ */ (0,
|
|
4062
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4063
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate", children: page.title })
|
|
4797
4064
|
]
|
|
4798
4065
|
},
|
|
4799
4066
|
page.path
|
|
4800
4067
|
)) })
|
|
4801
4068
|
] }),
|
|
4802
|
-
urlError ? /* @__PURE__ */ (0,
|
|
4069
|
+
urlError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4803
4070
|
] });
|
|
4804
4071
|
}
|
|
4805
4072
|
|
|
4806
4073
|
// src/ui/link-modal/useLinkModalState.ts
|
|
4807
|
-
var
|
|
4074
|
+
var import_react2 = require("react");
|
|
4808
4075
|
function useLinkModalState({
|
|
4809
4076
|
open,
|
|
4810
4077
|
mode,
|
|
@@ -4816,17 +4083,17 @@ function useLinkModalState({
|
|
|
4816
4083
|
onClose,
|
|
4817
4084
|
onSubmit
|
|
4818
4085
|
}) {
|
|
4819
|
-
const availablePages = (0,
|
|
4086
|
+
const availablePages = (0, import_react2.useMemo)(
|
|
4820
4087
|
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4821
4088
|
[mode, pages, existingTargets]
|
|
4822
4089
|
);
|
|
4823
|
-
const [searchValue, setSearchValue] = (0,
|
|
4824
|
-
const [selectedPage, setSelectedPage] = (0,
|
|
4825
|
-
const [selectedSection, setSelectedSection] = (0,
|
|
4826
|
-
const [step, setStep] = (0,
|
|
4827
|
-
const [dropdownOpen, setDropdownOpen] = (0,
|
|
4828
|
-
const [urlError, setUrlError] = (0,
|
|
4829
|
-
const reset = (0,
|
|
4090
|
+
const [searchValue, setSearchValue] = (0, import_react2.useState)("");
|
|
4091
|
+
const [selectedPage, setSelectedPage] = (0, import_react2.useState)(null);
|
|
4092
|
+
const [selectedSection, setSelectedSection] = (0, import_react2.useState)(null);
|
|
4093
|
+
const [step, setStep] = (0, import_react2.useState)("input");
|
|
4094
|
+
const [dropdownOpen, setDropdownOpen] = (0, import_react2.useState)(false);
|
|
4095
|
+
const [urlError, setUrlError] = (0, import_react2.useState)("");
|
|
4096
|
+
const reset = (0, import_react2.useCallback)(() => {
|
|
4830
4097
|
setSearchValue("");
|
|
4831
4098
|
setSelectedPage(null);
|
|
4832
4099
|
setSelectedSection(null);
|
|
@@ -4834,7 +4101,7 @@ function useLinkModalState({
|
|
|
4834
4101
|
setDropdownOpen(false);
|
|
4835
4102
|
setUrlError("");
|
|
4836
4103
|
}, []);
|
|
4837
|
-
(0,
|
|
4104
|
+
(0, import_react2.useEffect)(() => {
|
|
4838
4105
|
if (!open) return;
|
|
4839
4106
|
if (mode === "edit" && initialTarget) {
|
|
4840
4107
|
const { pageRoute } = parseTarget(initialTarget);
|
|
@@ -4850,11 +4117,11 @@ function useLinkModalState({
|
|
|
4850
4117
|
setDropdownOpen(false);
|
|
4851
4118
|
setUrlError("");
|
|
4852
4119
|
}, [open, mode, initialTarget, pages, sectionsByPath, reset]);
|
|
4853
|
-
const filteredPages = (0,
|
|
4120
|
+
const filteredPages = (0, import_react2.useMemo)(() => {
|
|
4854
4121
|
if (!searchValue.trim()) return availablePages;
|
|
4855
4122
|
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4856
4123
|
}, [availablePages, searchValue]);
|
|
4857
|
-
const activeSections = (0,
|
|
4124
|
+
const activeSections = (0, import_react2.useMemo)(() => {
|
|
4858
4125
|
if (!selectedPage) return [];
|
|
4859
4126
|
return getSectionsForPath(sectionsByPath, selectedPage.path);
|
|
4860
4127
|
}, [selectedPage, sectionsByPath]);
|
|
@@ -4870,9 +4137,8 @@ function useLinkModalState({
|
|
|
4870
4137
|
setUrlError("");
|
|
4871
4138
|
const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
|
|
4872
4139
|
setDropdownOpen(filtered.length > 0);
|
|
4873
|
-
if (value.trim()
|
|
4874
|
-
|
|
4875
|
-
if (error) setUrlError(error);
|
|
4140
|
+
if (value.trim()) {
|
|
4141
|
+
setUrlError(validateUrlInput(value, availablePages, pages));
|
|
4876
4142
|
}
|
|
4877
4143
|
};
|
|
4878
4144
|
const handlePageSelect = (page) => {
|
|
@@ -4901,7 +4167,7 @@ function useLinkModalState({
|
|
|
4901
4167
|
reset();
|
|
4902
4168
|
onClose();
|
|
4903
4169
|
};
|
|
4904
|
-
const isValid = (0,
|
|
4170
|
+
const isValid = (0, import_react2.useMemo)(() => {
|
|
4905
4171
|
if (urlError) return false;
|
|
4906
4172
|
if (showBreadcrumb) return true;
|
|
4907
4173
|
if (selectedPage) return true;
|
|
@@ -4968,7 +4234,7 @@ function useLinkModalState({
|
|
|
4968
4234
|
}
|
|
4969
4235
|
|
|
4970
4236
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4971
|
-
var
|
|
4237
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4972
4238
|
function LinkEditorPanel({
|
|
4973
4239
|
open = true,
|
|
4974
4240
|
mode = "create",
|
|
@@ -4992,26 +4258,26 @@ function LinkEditorPanel({
|
|
|
4992
4258
|
onSubmit
|
|
4993
4259
|
});
|
|
4994
4260
|
const isCancel = state.secondaryLabel === "Cancel";
|
|
4995
|
-
return /* @__PURE__ */ (0,
|
|
4996
|
-
/* @__PURE__ */ (0,
|
|
4261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
4262
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4997
4263
|
"button",
|
|
4998
4264
|
{
|
|
4999
4265
|
type: "button",
|
|
5000
4266
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
5001
4267
|
onClick: state.handleClose,
|
|
5002
4268
|
"aria-label": "Close",
|
|
5003
|
-
children: /* @__PURE__ */ (0,
|
|
4269
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconX, { "aria-hidden": true })
|
|
5004
4270
|
}
|
|
5005
4271
|
),
|
|
5006
|
-
/* @__PURE__ */ (0,
|
|
5007
|
-
/* @__PURE__ */ (0,
|
|
5008
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0,
|
|
4272
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
|
|
4273
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
4274
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
5009
4275
|
DestinationBreadcrumb,
|
|
5010
4276
|
{
|
|
5011
4277
|
pageTitle: state.selectedPage.title,
|
|
5012
4278
|
sectionLabel: state.selectedSection.label
|
|
5013
4279
|
}
|
|
5014
|
-
) : /* @__PURE__ */ (0,
|
|
4280
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
5015
4281
|
UrlOrPageInput,
|
|
5016
4282
|
{
|
|
5017
4283
|
value: state.searchValue,
|
|
@@ -5024,18 +4290,18 @@ function LinkEditorPanel({
|
|
|
5024
4290
|
urlError: state.urlError
|
|
5025
4291
|
}
|
|
5026
4292
|
),
|
|
5027
|
-
state.showChooseSection ? /* @__PURE__ */ (0,
|
|
5028
|
-
/* @__PURE__ */ (0,
|
|
5029
|
-
/* @__PURE__ */ (0,
|
|
5030
|
-
/* @__PURE__ */ (0,
|
|
5031
|
-
/* @__PURE__ */ (0,
|
|
4293
|
+
state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
4294
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
4295
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
4296
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
4297
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: "Pick a section this link should scroll to." })
|
|
5032
4298
|
] })
|
|
5033
4299
|
] }) : null,
|
|
5034
|
-
state.showSectionPicker ? /* @__PURE__ */ (0,
|
|
5035
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0,
|
|
4300
|
+
state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
4301
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
5036
4302
|
] }),
|
|
5037
|
-
/* @__PURE__ */ (0,
|
|
5038
|
-
/* @__PURE__ */ (0,
|
|
4303
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
|
|
4304
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
5039
4305
|
Button,
|
|
5040
4306
|
{
|
|
5041
4307
|
type: "button",
|
|
@@ -5050,7 +4316,7 @@ function LinkEditorPanel({
|
|
|
5050
4316
|
children: state.secondaryLabel
|
|
5051
4317
|
}
|
|
5052
4318
|
),
|
|
5053
|
-
/* @__PURE__ */ (0,
|
|
4319
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
5054
4320
|
Button,
|
|
5055
4321
|
{
|
|
5056
4322
|
type: "button",
|
|
@@ -5069,7 +4335,7 @@ function LinkEditorPanel({
|
|
|
5069
4335
|
}
|
|
5070
4336
|
|
|
5071
4337
|
// src/ui/link-modal/LinkPopover.tsx
|
|
5072
|
-
var
|
|
4338
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
5073
4339
|
function LinkPopover({
|
|
5074
4340
|
rect,
|
|
5075
4341
|
parentScroll,
|
|
@@ -5077,7 +4343,7 @@ function LinkPopover({
|
|
|
5077
4343
|
...editorProps
|
|
5078
4344
|
}) {
|
|
5079
4345
|
const { top, left, transform } = calcPopoverPos(rect, parentScroll);
|
|
5080
|
-
return /* @__PURE__ */ (0,
|
|
4346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
5081
4347
|
"div",
|
|
5082
4348
|
{
|
|
5083
4349
|
ref: panelRef,
|
|
@@ -5090,7 +4356,7 @@ function LinkPopover({
|
|
|
5090
4356
|
),
|
|
5091
4357
|
style: { top, left, transform },
|
|
5092
4358
|
onMouseDown: (e) => e.stopPropagation(),
|
|
5093
|
-
children: /* @__PURE__ */ (0,
|
|
4359
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LinkEditorPanel, { ...editorProps, open: true })
|
|
5094
4360
|
}
|
|
5095
4361
|
);
|
|
5096
4362
|
}
|
|
@@ -5153,30 +4419,8 @@ function shouldUseDevFixtures() {
|
|
|
5153
4419
|
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
5154
4420
|
}
|
|
5155
4421
|
|
|
5156
|
-
// src/ui/badge.tsx
|
|
5157
|
-
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
5158
|
-
var badgeVariants = cva(
|
|
5159
|
-
"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",
|
|
5160
|
-
{
|
|
5161
|
-
variants: {
|
|
5162
|
-
variant: {
|
|
5163
|
-
default: "border-transparent bg-primary text-primary-foreground",
|
|
5164
|
-
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
5165
|
-
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
5166
|
-
outline: "text-foreground"
|
|
5167
|
-
}
|
|
5168
|
-
},
|
|
5169
|
-
defaultVariants: {
|
|
5170
|
-
variant: "default"
|
|
5171
|
-
}
|
|
5172
|
-
}
|
|
5173
|
-
);
|
|
5174
|
-
function Badge({ className, variant, ...props }) {
|
|
5175
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
5176
|
-
}
|
|
5177
|
-
|
|
5178
4422
|
// src/OhhwellsBridge.tsx
|
|
5179
|
-
var
|
|
4423
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
5180
4424
|
var PRIMARY = "#0885FE";
|
|
5181
4425
|
var IMAGE_FADE_MS = 300;
|
|
5182
4426
|
function runOpacityFade(el, onDone) {
|
|
@@ -5227,163 +4471,6 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
5227
4471
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
5228
4472
|
});
|
|
5229
4473
|
}
|
|
5230
|
-
function getSectionsTracker() {
|
|
5231
|
-
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
5232
|
-
if (!el) {
|
|
5233
|
-
el = document.createElement("div");
|
|
5234
|
-
el.setAttribute("data-ohw-sections-tracker", "");
|
|
5235
|
-
el.style.display = "none";
|
|
5236
|
-
document.body.appendChild(el);
|
|
5237
|
-
}
|
|
5238
|
-
return el;
|
|
5239
|
-
}
|
|
5240
|
-
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
5241
|
-
const tracker = getSectionsTracker();
|
|
5242
|
-
let sections = [];
|
|
5243
|
-
try {
|
|
5244
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
5245
|
-
} catch {
|
|
5246
|
-
}
|
|
5247
|
-
const currentPath = window.location.pathname;
|
|
5248
|
-
const updated = sections.map((s) => {
|
|
5249
|
-
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
5250
|
-
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
5251
|
-
return { ...s, scheduleId };
|
|
5252
|
-
});
|
|
5253
|
-
tracker.textContent = JSON.stringify(updated);
|
|
5254
|
-
return tracker.textContent ?? "[]";
|
|
5255
|
-
}
|
|
5256
|
-
function schedulingSectionId(insertAfter) {
|
|
5257
|
-
return `scheduling-${insertAfter}`;
|
|
5258
|
-
}
|
|
5259
|
-
var INSERT_BEFORE_MARKER = ":before:";
|
|
5260
|
-
function parseSchedulingInsertAfter(insertAfter) {
|
|
5261
|
-
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
5262
|
-
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
5263
|
-
return {
|
|
5264
|
-
anchor: insertAfter.slice(0, idx),
|
|
5265
|
-
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
5266
|
-
};
|
|
5267
|
-
}
|
|
5268
|
-
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
5269
|
-
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
5270
|
-
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
5271
|
-
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
5272
|
-
return { effectiveInsertAfter, insertBefore };
|
|
5273
|
-
}
|
|
5274
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
5275
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
5276
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
5277
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
5278
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
5279
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
5280
|
-
}
|
|
5281
|
-
if (!anchorEl) return null;
|
|
5282
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
5283
|
-
}
|
|
5284
|
-
function schedulingMountDepth(insertAfter) {
|
|
5285
|
-
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
5286
|
-
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
5287
|
-
}
|
|
5288
|
-
function getPageSchedulingEntries(raw) {
|
|
5289
|
-
if (!raw) return [];
|
|
5290
|
-
try {
|
|
5291
|
-
const entries = JSON.parse(raw);
|
|
5292
|
-
const currentPath = window.location.pathname;
|
|
5293
|
-
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
5294
|
-
} catch {
|
|
5295
|
-
return [];
|
|
5296
|
-
}
|
|
5297
|
-
}
|
|
5298
|
-
function isSchedulingWidgetMissing(entry) {
|
|
5299
|
-
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
5300
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
5301
|
-
}
|
|
5302
|
-
function hasMissingSchedulingWidgets(entries) {
|
|
5303
|
-
return entries.some(isSchedulingWidgetMissing);
|
|
5304
|
-
}
|
|
5305
|
-
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
5306
|
-
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
5307
|
-
mountSchedulingEntries(entries, notifyOnConnect);
|
|
5308
|
-
}
|
|
5309
|
-
function initSectionsFromContent(content, removeExisting = false) {
|
|
5310
|
-
const raw = content["__ohw_sections"];
|
|
5311
|
-
if (!raw) return;
|
|
5312
|
-
try {
|
|
5313
|
-
if (removeExisting) {
|
|
5314
|
-
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
5315
|
-
}
|
|
5316
|
-
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5317
|
-
if (inEditor) getSectionsTracker().textContent = raw;
|
|
5318
|
-
const pageEntries = getPageSchedulingEntries(raw);
|
|
5319
|
-
mountSchedulingEntries(pageEntries, false);
|
|
5320
|
-
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
|
|
5321
|
-
setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
|
|
5322
|
-
} catch {
|
|
5323
|
-
}
|
|
5324
|
-
}
|
|
5325
|
-
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
5326
|
-
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
5327
|
-
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
5328
|
-
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
5329
|
-
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
5330
|
-
if (!mountPoint) return false;
|
|
5331
|
-
const container = document.createElement("div");
|
|
5332
|
-
container.dataset.ohwSectionContainer = "scheduling";
|
|
5333
|
-
if (insertBefore) {
|
|
5334
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
5335
|
-
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
5336
|
-
if (!beforePoint) return false;
|
|
5337
|
-
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
5338
|
-
} else {
|
|
5339
|
-
let tail = mountPoint;
|
|
5340
|
-
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
5341
|
-
tail = tail.nextElementSibling;
|
|
5342
|
-
}
|
|
5343
|
-
tail.insertAdjacentElement("afterend", container);
|
|
5344
|
-
}
|
|
5345
|
-
const root = (0, import_client.createRoot)(container);
|
|
5346
|
-
(0, import_react_dom.flushSync)(() => {
|
|
5347
|
-
root.render(
|
|
5348
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5349
|
-
SchedulingWidget,
|
|
5350
|
-
{
|
|
5351
|
-
notifyOnConnect,
|
|
5352
|
-
initialScheduleId: scheduleId,
|
|
5353
|
-
insertAfter: effectiveInsertAfter
|
|
5354
|
-
}
|
|
5355
|
-
)
|
|
5356
|
-
);
|
|
5357
|
-
});
|
|
5358
|
-
const tracker = getSectionsTracker();
|
|
5359
|
-
let sections = [];
|
|
5360
|
-
try {
|
|
5361
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
5362
|
-
} catch {
|
|
5363
|
-
}
|
|
5364
|
-
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
5365
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
5366
|
-
sections.push({
|
|
5367
|
-
type: "scheduling",
|
|
5368
|
-
insertAfter: effectiveInsertAfter,
|
|
5369
|
-
pagePath: window.location.pathname,
|
|
5370
|
-
...scheduleId ? { scheduleId } : {}
|
|
5371
|
-
});
|
|
5372
|
-
tracker.textContent = JSON.stringify(sections);
|
|
5373
|
-
}
|
|
5374
|
-
return true;
|
|
5375
|
-
}
|
|
5376
|
-
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
5377
|
-
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
5378
|
-
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
5379
|
-
for (let i = pending.length - 1; i >= 0; i--) {
|
|
5380
|
-
const { insertAfter, scheduleId } = pending[i];
|
|
5381
|
-
if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
|
|
5382
|
-
pending.splice(i, 1);
|
|
5383
|
-
}
|
|
5384
|
-
}
|
|
5385
|
-
}
|
|
5386
|
-
}
|
|
5387
4474
|
function getLinkHref(el) {
|
|
5388
4475
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5389
4476
|
return anchor?.getAttribute("href") ?? "";
|
|
@@ -5583,7 +4670,7 @@ var TOOLBAR_GROUPS = [
|
|
|
5583
4670
|
];
|
|
5584
4671
|
function GlowFrame({ rect, elRef }) {
|
|
5585
4672
|
const GAP = 6;
|
|
5586
|
-
return /* @__PURE__ */ (0,
|
|
4673
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5587
4674
|
"div",
|
|
5588
4675
|
{
|
|
5589
4676
|
ref: elRef,
|
|
@@ -5636,7 +4723,7 @@ function FloatingToolbar({
|
|
|
5636
4723
|
onEditLink
|
|
5637
4724
|
}) {
|
|
5638
4725
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
5639
|
-
return /* @__PURE__ */ (0,
|
|
4726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
5640
4727
|
"div",
|
|
5641
4728
|
{
|
|
5642
4729
|
ref: elRef,
|
|
@@ -5661,11 +4748,11 @@ function FloatingToolbar({
|
|
|
5661
4748
|
},
|
|
5662
4749
|
onMouseDown: (e) => e.stopPropagation(),
|
|
5663
4750
|
children: [
|
|
5664
|
-
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
5665
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
4751
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react3.default.Fragment, { children: [
|
|
4752
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5666
4753
|
btns.map((btn) => {
|
|
5667
4754
|
const isActive = activeCommands.has(btn.cmd);
|
|
5668
|
-
return /* @__PURE__ */ (0,
|
|
4755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5669
4756
|
"button",
|
|
5670
4757
|
{
|
|
5671
4758
|
title: btn.title,
|
|
@@ -5691,7 +4778,7 @@ function FloatingToolbar({
|
|
|
5691
4778
|
flexShrink: 0,
|
|
5692
4779
|
padding: 6
|
|
5693
4780
|
},
|
|
5694
|
-
children: /* @__PURE__ */ (0,
|
|
4781
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5695
4782
|
"svg",
|
|
5696
4783
|
{
|
|
5697
4784
|
width: "16",
|
|
@@ -5711,7 +4798,7 @@ function FloatingToolbar({
|
|
|
5711
4798
|
);
|
|
5712
4799
|
})
|
|
5713
4800
|
] }, gi)),
|
|
5714
|
-
showEditLink ? /* @__PURE__ */ (0,
|
|
4801
|
+
showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5715
4802
|
"button",
|
|
5716
4803
|
{
|
|
5717
4804
|
type: "button",
|
|
@@ -5743,9 +4830,9 @@ function FloatingToolbar({
|
|
|
5743
4830
|
flexShrink: 0,
|
|
5744
4831
|
padding: 6
|
|
5745
4832
|
},
|
|
5746
|
-
children: /* @__PURE__ */ (0,
|
|
5747
|
-
/* @__PURE__ */ (0,
|
|
5748
|
-
/* @__PURE__ */ (0,
|
|
4833
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
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" })
|
|
5749
4836
|
] })
|
|
5750
4837
|
}
|
|
5751
4838
|
) : null
|
|
@@ -5763,7 +4850,7 @@ function StateToggle({
|
|
|
5763
4850
|
states,
|
|
5764
4851
|
onStateChange
|
|
5765
4852
|
}) {
|
|
5766
|
-
return /* @__PURE__ */ (0,
|
|
4853
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5767
4854
|
ToggleGroup,
|
|
5768
4855
|
{
|
|
5769
4856
|
"data-ohw-state-toggle": "",
|
|
@@ -5777,35 +4864,18 @@ function StateToggle({
|
|
|
5777
4864
|
left: rect.right - 8,
|
|
5778
4865
|
transform: "translateX(-100%)"
|
|
5779
4866
|
},
|
|
5780
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
4867
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
5781
4868
|
}
|
|
5782
4869
|
);
|
|
5783
4870
|
}
|
|
5784
4871
|
var contentCache = /* @__PURE__ */ new Map();
|
|
5785
|
-
function resolveSubdomain(subdomainFromQuery) {
|
|
5786
|
-
if (subdomainFromQuery) return subdomainFromQuery;
|
|
5787
|
-
if (typeof window !== "undefined") {
|
|
5788
|
-
const parts = window.location.hostname.split(".");
|
|
5789
|
-
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
5790
|
-
}
|
|
5791
|
-
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
5792
|
-
if (siteUrl) {
|
|
5793
|
-
try {
|
|
5794
|
-
const host = new URL(siteUrl).hostname;
|
|
5795
|
-
const siteParts = host.split(".");
|
|
5796
|
-
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
5797
|
-
} catch {
|
|
5798
|
-
}
|
|
5799
|
-
}
|
|
5800
|
-
return "";
|
|
5801
|
-
}
|
|
5802
4872
|
function OhhwellsBridge() {
|
|
5803
4873
|
const pathname = (0, import_navigation.usePathname)();
|
|
5804
4874
|
const router = (0, import_navigation.useRouter)();
|
|
5805
4875
|
const searchParams = (0, import_navigation.useSearchParams)();
|
|
5806
4876
|
const isEditMode = isEditSessionActive();
|
|
5807
|
-
const [bridgeRoot, setBridgeRoot] = (0,
|
|
5808
|
-
(0,
|
|
4877
|
+
const [bridgeRoot, setBridgeRoot] = (0, import_react3.useState)(null);
|
|
4878
|
+
(0, import_react3.useEffect)(() => {
|
|
5809
4879
|
const figtreeFontId = "ohw-figtree-font";
|
|
5810
4880
|
if (!document.getElementById(figtreeFontId)) {
|
|
5811
4881
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -5828,52 +4898,55 @@ function OhhwellsBridge() {
|
|
|
5828
4898
|
};
|
|
5829
4899
|
}, []);
|
|
5830
4900
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
5831
|
-
const subdomain =
|
|
5832
|
-
|
|
4901
|
+
const subdomain = subdomainFromQuery ?? (() => {
|
|
4902
|
+
if (typeof window === "undefined") return "";
|
|
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) => {
|
|
5833
4907
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
5834
4908
|
window.parent.postMessage(data, "*");
|
|
5835
4909
|
}
|
|
5836
4910
|
}, []);
|
|
5837
|
-
const [fetchState, setFetchState] = (0,
|
|
5838
|
-
const autoSaveTimers = (0,
|
|
5839
|
-
const activeElRef = (0,
|
|
5840
|
-
const originalContentRef = (0,
|
|
5841
|
-
const activeStateElRef = (0,
|
|
5842
|
-
const parentScrollRef = (0,
|
|
5843
|
-
const toolbarElRef = (0,
|
|
5844
|
-
const glowElRef = (0,
|
|
5845
|
-
const hoveredImageRef = (0,
|
|
5846
|
-
const hoveredImageHasTextOverlapRef = (0,
|
|
5847
|
-
const
|
|
5848
|
-
const
|
|
5849
|
-
const
|
|
5850
|
-
const
|
|
5851
|
-
const activateRef = (0, import_react5.useRef)(() => {
|
|
4911
|
+
const [fetchState, setFetchState] = (0, import_react3.useState)("idle");
|
|
4912
|
+
const autoSaveTimers = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
4913
|
+
const activeElRef = (0, import_react3.useRef)(null);
|
|
4914
|
+
const originalContentRef = (0, import_react3.useRef)(null);
|
|
4915
|
+
const activeStateElRef = (0, import_react3.useRef)(null);
|
|
4916
|
+
const parentScrollRef = (0, import_react3.useRef)(null);
|
|
4917
|
+
const toolbarElRef = (0, import_react3.useRef)(null);
|
|
4918
|
+
const glowElRef = (0, import_react3.useRef)(null);
|
|
4919
|
+
const hoveredImageRef = (0, import_react3.useRef)(null);
|
|
4920
|
+
const hoveredImageHasTextOverlapRef = (0, import_react3.useRef)(false);
|
|
4921
|
+
const imageUnhoverTimerRef = (0, import_react3.useRef)(null);
|
|
4922
|
+
const imageShowTimerRef = (0, import_react3.useRef)(null);
|
|
4923
|
+
const editStylesRef = (0, import_react3.useRef)(null);
|
|
4924
|
+
const activateRef = (0, import_react3.useRef)(() => {
|
|
5852
4925
|
});
|
|
5853
|
-
const deactivateRef = (0,
|
|
4926
|
+
const deactivateRef = (0, import_react3.useRef)(() => {
|
|
5854
4927
|
});
|
|
5855
|
-
const refreshActiveCommandsRef = (0,
|
|
4928
|
+
const refreshActiveCommandsRef = (0, import_react3.useRef)(() => {
|
|
5856
4929
|
});
|
|
5857
|
-
const postToParentRef = (0,
|
|
4930
|
+
const postToParentRef = (0, import_react3.useRef)(postToParent);
|
|
5858
4931
|
postToParentRef.current = postToParent;
|
|
5859
|
-
const [toolbarRect, setToolbarRect] = (0,
|
|
5860
|
-
const [toggleState, setToggleState] = (0,
|
|
5861
|
-
const [maxBadge, setMaxBadge] = (0,
|
|
5862
|
-
const [activeCommands, setActiveCommands] = (0,
|
|
5863
|
-
const [toolbarShowEditLink, setToolbarShowEditLink] = (0,
|
|
5864
|
-
const [linkPopover, setLinkPopover] = (0,
|
|
5865
|
-
const [sitePages, setSitePages] = (0,
|
|
5866
|
-
const [sectionsByPath, setSectionsByPath] = (0,
|
|
5867
|
-
const sectionsPrefetchGenRef = (0,
|
|
5868
|
-
const setLinkPopoverRef = (0,
|
|
5869
|
-
const linkPopoverPanelRef = (0,
|
|
5870
|
-
const linkPopoverOpenRef = (0,
|
|
5871
|
-
const linkPopoverGraceUntilRef = (0,
|
|
4932
|
+
const [toolbarRect, setToolbarRect] = (0, import_react3.useState)(null);
|
|
4933
|
+
const [toggleState, setToggleState] = (0, import_react3.useState)(null);
|
|
4934
|
+
const [maxBadge, setMaxBadge] = (0, import_react3.useState)(null);
|
|
4935
|
+
const [activeCommands, setActiveCommands] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
|
|
4936
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react3.useState)(false);
|
|
4937
|
+
const [linkPopover, setLinkPopover] = (0, import_react3.useState)(null);
|
|
4938
|
+
const [sitePages, setSitePages] = (0, import_react3.useState)([]);
|
|
4939
|
+
const [sectionsByPath, setSectionsByPath] = (0, import_react3.useState)({});
|
|
4940
|
+
const sectionsPrefetchGenRef = (0, import_react3.useRef)(0);
|
|
4941
|
+
const setLinkPopoverRef = (0, import_react3.useRef)(setLinkPopover);
|
|
4942
|
+
const linkPopoverPanelRef = (0, import_react3.useRef)(null);
|
|
4943
|
+
const linkPopoverOpenRef = (0, import_react3.useRef)(false);
|
|
4944
|
+
const linkPopoverGraceUntilRef = (0, import_react3.useRef)(0);
|
|
5872
4945
|
setLinkPopoverRef.current = setLinkPopover;
|
|
5873
4946
|
const bumpLinkPopoverGrace = () => {
|
|
5874
4947
|
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
5875
4948
|
};
|
|
5876
|
-
const runSectionsPrefetch = (0,
|
|
4949
|
+
const runSectionsPrefetch = (0, import_react3.useCallback)((pages) => {
|
|
5877
4950
|
if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
|
|
5878
4951
|
const gen = ++sectionsPrefetchGenRef.current;
|
|
5879
4952
|
const paths = pages.map((p) => p.path);
|
|
@@ -5892,9 +4965,9 @@ function OhhwellsBridge() {
|
|
|
5892
4965
|
);
|
|
5893
4966
|
});
|
|
5894
4967
|
}, [isEditMode, pathname]);
|
|
5895
|
-
const runSectionsPrefetchRef = (0,
|
|
4968
|
+
const runSectionsPrefetchRef = (0, import_react3.useRef)(runSectionsPrefetch);
|
|
5896
4969
|
runSectionsPrefetchRef.current = runSectionsPrefetch;
|
|
5897
|
-
(0,
|
|
4970
|
+
(0, import_react3.useEffect)(() => {
|
|
5898
4971
|
if (!linkPopover) return;
|
|
5899
4972
|
if (hoveredImageRef.current) {
|
|
5900
4973
|
hoveredImageRef.current = null;
|
|
@@ -5902,7 +4975,7 @@ function OhhwellsBridge() {
|
|
|
5902
4975
|
}
|
|
5903
4976
|
postToParent({ type: "ow:image-unhover" });
|
|
5904
4977
|
}, [linkPopover, postToParent]);
|
|
5905
|
-
(0,
|
|
4978
|
+
(0, import_react3.useEffect)(() => {
|
|
5906
4979
|
if (!isEditMode) return;
|
|
5907
4980
|
const useFixtures = shouldUseDevFixtures();
|
|
5908
4981
|
if (useFixtures) {
|
|
@@ -5926,15 +4999,14 @@ function OhhwellsBridge() {
|
|
|
5926
4999
|
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5927
5000
|
return () => window.removeEventListener("message", onSitePages);
|
|
5928
5001
|
}, [isEditMode, postToParent]);
|
|
5929
|
-
|
|
5930
|
-
(0, import_react5.useEffect)(() => {
|
|
5002
|
+
(0, import_react3.useEffect)(() => {
|
|
5931
5003
|
if (!isEditMode || shouldUseDevFixtures()) return;
|
|
5932
5004
|
void loadAllSectionsManifest().then((manifest) => {
|
|
5933
5005
|
if (Object.keys(manifest).length === 0) return;
|
|
5934
5006
|
setSectionsByPath((prev) => ({ ...manifest, ...prev }));
|
|
5935
5007
|
});
|
|
5936
5008
|
}, [isEditMode]);
|
|
5937
|
-
(0,
|
|
5009
|
+
(0, import_react3.useEffect)(() => {
|
|
5938
5010
|
const update = () => {
|
|
5939
5011
|
const el = activeElRef.current;
|
|
5940
5012
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -5942,12 +5014,6 @@ function OhhwellsBridge() {
|
|
|
5942
5014
|
if (!prev || !activeStateElRef.current) return prev;
|
|
5943
5015
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
5944
5016
|
});
|
|
5945
|
-
setSectionGap((prev) => {
|
|
5946
|
-
if (!prev) return prev;
|
|
5947
|
-
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
5948
|
-
if (!anchor) return prev;
|
|
5949
|
-
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
5950
|
-
});
|
|
5951
5017
|
};
|
|
5952
5018
|
const vvp = window.visualViewport;
|
|
5953
5019
|
if (!vvp) return;
|
|
@@ -5958,10 +5024,10 @@ function OhhwellsBridge() {
|
|
|
5958
5024
|
vvp.removeEventListener("resize", update);
|
|
5959
5025
|
};
|
|
5960
5026
|
}, []);
|
|
5961
|
-
const refreshStateRules = (0,
|
|
5027
|
+
const refreshStateRules = (0, import_react3.useCallback)(() => {
|
|
5962
5028
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
5963
5029
|
}, []);
|
|
5964
|
-
const deactivate = (0,
|
|
5030
|
+
const deactivate = (0, import_react3.useCallback)(() => {
|
|
5965
5031
|
const el = activeElRef.current;
|
|
5966
5032
|
if (!el) return;
|
|
5967
5033
|
const key = el.dataset.ohwKey;
|
|
@@ -5987,7 +5053,7 @@ function OhhwellsBridge() {
|
|
|
5987
5053
|
setToolbarShowEditLink(false);
|
|
5988
5054
|
postToParent({ type: "ow:exit-edit" });
|
|
5989
5055
|
}, [postToParent]);
|
|
5990
|
-
const activate = (0,
|
|
5056
|
+
const activate = (0, import_react3.useCallback)((el) => {
|
|
5991
5057
|
if (activeElRef.current === el) return;
|
|
5992
5058
|
deactivate();
|
|
5993
5059
|
if (hoveredImageRef.current) {
|
|
@@ -6006,7 +5072,7 @@ function OhhwellsBridge() {
|
|
|
6006
5072
|
}, [deactivate, postToParent]);
|
|
6007
5073
|
activateRef.current = activate;
|
|
6008
5074
|
deactivateRef.current = deactivate;
|
|
6009
|
-
(0,
|
|
5075
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
6010
5076
|
if (!subdomain || isEditMode) {
|
|
6011
5077
|
setFetchState("done");
|
|
6012
5078
|
return;
|
|
@@ -6014,7 +5080,6 @@ function OhhwellsBridge() {
|
|
|
6014
5080
|
const applyContent = (content) => {
|
|
6015
5081
|
const imageLoads = [];
|
|
6016
5082
|
for (const [key, val] of Object.entries(content)) {
|
|
6017
|
-
if (key === "__ohw_sections") continue;
|
|
6018
5083
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6019
5084
|
if (el.dataset.ohwEditable === "image") {
|
|
6020
5085
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6036,7 +5101,6 @@ function OhhwellsBridge() {
|
|
|
6036
5101
|
});
|
|
6037
5102
|
applyLinkByKey(key, val);
|
|
6038
5103
|
}
|
|
6039
|
-
initSectionsFromContent(content, true);
|
|
6040
5104
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
6041
5105
|
}) : Promise.resolve();
|
|
6042
5106
|
};
|
|
@@ -6061,15 +5125,12 @@ function OhhwellsBridge() {
|
|
|
6061
5125
|
cancelled = true;
|
|
6062
5126
|
};
|
|
6063
5127
|
}, [subdomain, isEditMode, pathname]);
|
|
6064
|
-
(0,
|
|
5128
|
+
(0, import_react3.useEffect)(() => {
|
|
6065
5129
|
if (!subdomain || isEditMode) return;
|
|
6066
|
-
let debounceTimer = null;
|
|
6067
5130
|
const applyFromCache = () => {
|
|
6068
5131
|
const content = contentCache.get(subdomain);
|
|
6069
5132
|
if (!content) return;
|
|
6070
|
-
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
6071
5133
|
for (const [key, val] of Object.entries(content)) {
|
|
6072
|
-
if (key === "__ohw_sections") continue;
|
|
6073
5134
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6074
5135
|
if (el.dataset.ohwEditable === "image") {
|
|
6075
5136
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6086,28 +5147,21 @@ function OhhwellsBridge() {
|
|
|
6086
5147
|
applyLinkByKey(key, val);
|
|
6087
5148
|
}
|
|
6088
5149
|
};
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
debounceTimer = setTimeout(applyFromCache, 150);
|
|
6092
|
-
};
|
|
6093
|
-
scheduleApply();
|
|
6094
|
-
const observer = new MutationObserver(scheduleApply);
|
|
5150
|
+
applyFromCache();
|
|
5151
|
+
const observer = new MutationObserver(applyFromCache);
|
|
6095
5152
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
6096
|
-
return () =>
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
};
|
|
6100
|
-
}, [subdomain, isEditMode, pathname]);
|
|
6101
|
-
(0, import_react5.useLayoutEffect)(() => {
|
|
5153
|
+
return () => observer.disconnect();
|
|
5154
|
+
}, [subdomain, isEditMode]);
|
|
5155
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
6102
5156
|
const el = document.getElementById("ohw-loader");
|
|
6103
5157
|
if (!el) return;
|
|
6104
5158
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
6105
5159
|
el.style.display = visible ? "flex" : "none";
|
|
6106
5160
|
}, [subdomain, fetchState]);
|
|
6107
|
-
(0,
|
|
5161
|
+
(0, import_react3.useEffect)(() => {
|
|
6108
5162
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
6109
5163
|
}, [pathname, postToParent]);
|
|
6110
|
-
(0,
|
|
5164
|
+
(0, import_react3.useEffect)(() => {
|
|
6111
5165
|
if (!isEditMode) return;
|
|
6112
5166
|
const measure = () => {
|
|
6113
5167
|
const h = document.body.scrollHeight;
|
|
@@ -6131,7 +5185,7 @@ function OhhwellsBridge() {
|
|
|
6131
5185
|
window.removeEventListener("resize", handleResize);
|
|
6132
5186
|
};
|
|
6133
5187
|
}, [pathname, isEditMode, postToParent]);
|
|
6134
|
-
(0,
|
|
5188
|
+
(0, import_react3.useEffect)(() => {
|
|
6135
5189
|
if (!subdomainFromQuery || isEditMode) return;
|
|
6136
5190
|
const handleClick = (e) => {
|
|
6137
5191
|
const anchor = e.target.closest("a");
|
|
@@ -6147,7 +5201,7 @@ function OhhwellsBridge() {
|
|
|
6147
5201
|
document.addEventListener("click", handleClick, true);
|
|
6148
5202
|
return () => document.removeEventListener("click", handleClick, true);
|
|
6149
5203
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
6150
|
-
(0,
|
|
5204
|
+
(0, import_react3.useEffect)(() => {
|
|
6151
5205
|
if (!isEditMode) {
|
|
6152
5206
|
editStylesRef.current?.base.remove();
|
|
6153
5207
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -6448,15 +5502,6 @@ function OhhwellsBridge() {
|
|
|
6448
5502
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
6449
5503
|
return;
|
|
6450
5504
|
}
|
|
6451
|
-
if (hoveredGapRef.current) {
|
|
6452
|
-
if (hoveredImageRef.current) {
|
|
6453
|
-
hoveredImageRef.current = null;
|
|
6454
|
-
resumeAnimTracks();
|
|
6455
|
-
postToParentRef.current({ type: "ow:image-unhover" });
|
|
6456
|
-
}
|
|
6457
|
-
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6458
|
-
return;
|
|
6459
|
-
}
|
|
6460
5505
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
6461
5506
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
6462
5507
|
hoveredImageRef.current = imgEl;
|
|
@@ -6522,30 +5567,8 @@ function OhhwellsBridge() {
|
|
|
6522
5567
|
}
|
|
6523
5568
|
}
|
|
6524
5569
|
};
|
|
6525
|
-
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
6526
|
-
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
6527
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
6528
|
-
const ZONE = 20;
|
|
6529
|
-
for (let i = 0; i < sections.length; i++) {
|
|
6530
|
-
const a = sections[i];
|
|
6531
|
-
const b = sections[i + 1] ?? null;
|
|
6532
|
-
const boundaryY = a.getBoundingClientRect().bottom;
|
|
6533
|
-
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
6534
|
-
const insertAfter = a.dataset.ohwSection ?? "";
|
|
6535
|
-
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
6536
|
-
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
6537
|
-
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
6538
|
-
return;
|
|
6539
|
-
}
|
|
6540
|
-
}
|
|
6541
|
-
if (hoveredGapRef.current) {
|
|
6542
|
-
hoveredGapRef.current = null;
|
|
6543
|
-
setSectionGap(null);
|
|
6544
|
-
}
|
|
6545
|
-
};
|
|
6546
5570
|
const handleMouseMove = (e) => {
|
|
6547
5571
|
const { clientX, clientY } = e;
|
|
6548
|
-
probeSectionGapAt(clientX, clientY);
|
|
6549
5572
|
probeImageAt(clientX, clientY);
|
|
6550
5573
|
probeHoverCardsAt(clientX, clientY);
|
|
6551
5574
|
};
|
|
@@ -6553,7 +5576,6 @@ function OhhwellsBridge() {
|
|
|
6553
5576
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
6554
5577
|
const { clientX, clientY } = e.data;
|
|
6555
5578
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
6556
|
-
probeSectionGapAt(clientX, clientY);
|
|
6557
5579
|
probeImageAt(clientX, clientY);
|
|
6558
5580
|
probeHoverCardsAt(clientX, clientY);
|
|
6559
5581
|
};
|
|
@@ -6711,12 +5733,7 @@ function OhhwellsBridge() {
|
|
|
6711
5733
|
if (e.data?.type !== "ow:hydrate") return;
|
|
6712
5734
|
const content = e.data.content;
|
|
6713
5735
|
if (!content) return;
|
|
6714
|
-
let sectionsJson = null;
|
|
6715
5736
|
for (const [key, val] of Object.entries(content)) {
|
|
6716
|
-
if (key === "__ohw_sections") {
|
|
6717
|
-
sectionsJson = val;
|
|
6718
|
-
continue;
|
|
6719
|
-
}
|
|
6720
5737
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
6721
5738
|
if (el.dataset.ohwEditable === "image") {
|
|
6722
5739
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -6731,9 +5748,6 @@ function OhhwellsBridge() {
|
|
|
6731
5748
|
});
|
|
6732
5749
|
applyLinkByKey(key, val);
|
|
6733
5750
|
}
|
|
6734
|
-
if (sectionsJson) {
|
|
6735
|
-
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
6736
|
-
}
|
|
6737
5751
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
6738
5752
|
};
|
|
6739
5753
|
window.addEventListener("message", handleHydrate);
|
|
@@ -6786,63 +5800,7 @@ function OhhwellsBridge() {
|
|
|
6786
5800
|
};
|
|
6787
5801
|
const handleSave = (e) => {
|
|
6788
5802
|
if (e.data?.type !== "ow:save") return;
|
|
6789
|
-
|
|
6790
|
-
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
6791
|
-
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
6792
|
-
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
6793
|
-
};
|
|
6794
|
-
const handleInsertSection = (e) => {
|
|
6795
|
-
if (e.data?.type !== "ow:insert-section") return;
|
|
6796
|
-
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
6797
|
-
if (widgetType !== "scheduling") return;
|
|
6798
|
-
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
6799
|
-
if (inserted) {
|
|
6800
|
-
const tracker = getSectionsTracker();
|
|
6801
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
6802
|
-
const h = document.documentElement.scrollHeight;
|
|
6803
|
-
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
6804
|
-
}
|
|
6805
|
-
};
|
|
6806
|
-
const handleSwitchSchedule = (e) => {
|
|
6807
|
-
if (e.data?.type !== "ow:switch-schedule") return;
|
|
6808
|
-
const scheduleId = e.data.scheduleId;
|
|
6809
|
-
const insertAfter = e.data.insertAfter;
|
|
6810
|
-
if (!scheduleId || !insertAfter) return;
|
|
6811
|
-
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6812
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6813
|
-
};
|
|
6814
|
-
const handleScheduleLinked = (e) => {
|
|
6815
|
-
if (e.data?.type !== "ow:schedule-linked") return;
|
|
6816
|
-
const scheduleId = e.data.scheduleId;
|
|
6817
|
-
const insertAfter = e.data.insertAfter;
|
|
6818
|
-
if (!scheduleId || !insertAfter) return;
|
|
6819
|
-
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
6820
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6821
|
-
};
|
|
6822
|
-
const handleClearSchedulingWidget = (e) => {
|
|
6823
|
-
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
6824
|
-
const insertAfter = e.data.insertAfter;
|
|
6825
|
-
if (!insertAfter) return;
|
|
6826
|
-
const text = updateSectionScheduleId(insertAfter, null);
|
|
6827
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
6828
|
-
};
|
|
6829
|
-
const handleRemoveSchedulingSection = (e) => {
|
|
6830
|
-
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
6831
|
-
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
6832
|
-
el.remove();
|
|
6833
|
-
});
|
|
6834
|
-
const tracker = getSectionsTracker();
|
|
6835
|
-
let sections = [];
|
|
6836
|
-
try {
|
|
6837
|
-
sections = JSON.parse(tracker.textContent || "[]");
|
|
6838
|
-
} catch {
|
|
6839
|
-
}
|
|
6840
|
-
const currentPath = window.location.pathname;
|
|
6841
|
-
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
6842
|
-
tracker.textContent = JSON.stringify(updated);
|
|
6843
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
6844
|
-
const h = document.documentElement.scrollHeight;
|
|
6845
|
-
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5803
|
+
postToParentRef.current({ type: "ow:save-result", nodes: collectEditableNodes() });
|
|
6846
5804
|
};
|
|
6847
5805
|
const handleSelectionChange = () => {
|
|
6848
5806
|
if (!activeElRef.current) return;
|
|
@@ -6947,11 +5905,6 @@ function OhhwellsBridge() {
|
|
|
6947
5905
|
deactivateRef.current();
|
|
6948
5906
|
};
|
|
6949
5907
|
window.addEventListener("message", handleSave);
|
|
6950
|
-
window.addEventListener("message", handleInsertSection);
|
|
6951
|
-
window.addEventListener("message", handleSwitchSchedule);
|
|
6952
|
-
window.addEventListener("message", handleScheduleLinked);
|
|
6953
|
-
window.addEventListener("message", handleClearSchedulingWidget);
|
|
6954
|
-
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
6955
5908
|
window.addEventListener("message", handleImageUrl);
|
|
6956
5909
|
window.addEventListener("message", handleAnimLock);
|
|
6957
5910
|
window.addEventListener("message", handleCanvasHeight);
|
|
@@ -6986,11 +5939,6 @@ function OhhwellsBridge() {
|
|
|
6986
5939
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
6987
5940
|
window.removeEventListener("scroll", handleScroll, true);
|
|
6988
5941
|
window.removeEventListener("message", handleSave);
|
|
6989
|
-
window.removeEventListener("message", handleInsertSection);
|
|
6990
|
-
window.removeEventListener("message", handleSwitchSchedule);
|
|
6991
|
-
window.removeEventListener("message", handleScheduleLinked);
|
|
6992
|
-
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
6993
|
-
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
6994
5942
|
window.removeEventListener("message", handleImageUrl);
|
|
6995
5943
|
window.removeEventListener("message", handleAnimLock);
|
|
6996
5944
|
window.removeEventListener("message", handleCanvasHeight);
|
|
@@ -7005,7 +5953,7 @@ function OhhwellsBridge() {
|
|
|
7005
5953
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
7006
5954
|
};
|
|
7007
5955
|
}, [isEditMode, refreshStateRules]);
|
|
7008
|
-
(0,
|
|
5956
|
+
(0, import_react3.useEffect)(() => {
|
|
7009
5957
|
if (!isEditMode) return;
|
|
7010
5958
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
7011
5959
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -7034,19 +5982,19 @@ function OhhwellsBridge() {
|
|
|
7034
5982
|
clearTimeout(timer);
|
|
7035
5983
|
};
|
|
7036
5984
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
7037
|
-
(0,
|
|
5985
|
+
(0, import_react3.useEffect)(() => {
|
|
7038
5986
|
scrollToHashSectionWhenReady();
|
|
7039
5987
|
const onHashChange = () => scrollToHashSectionWhenReady();
|
|
7040
5988
|
window.addEventListener("hashchange", onHashChange);
|
|
7041
5989
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
7042
5990
|
}, [pathname]);
|
|
7043
|
-
const handleCommand = (0,
|
|
5991
|
+
const handleCommand = (0, import_react3.useCallback)((cmd) => {
|
|
7044
5992
|
document.execCommand(cmd, false);
|
|
7045
5993
|
activeElRef.current?.focus();
|
|
7046
5994
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
7047
5995
|
refreshActiveCommandsRef.current();
|
|
7048
5996
|
}, []);
|
|
7049
|
-
const handleStateChange = (0,
|
|
5997
|
+
const handleStateChange = (0, import_react3.useCallback)((state) => {
|
|
7050
5998
|
if (!activeStateElRef.current) return;
|
|
7051
5999
|
const el = activeStateElRef.current;
|
|
7052
6000
|
if (state === "Default") {
|
|
@@ -7059,8 +6007,8 @@ function OhhwellsBridge() {
|
|
|
7059
6007
|
}
|
|
7060
6008
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
7061
6009
|
}, [deactivate]);
|
|
7062
|
-
const closeLinkPopover = (0,
|
|
7063
|
-
const openLinkPopoverForActive = (0,
|
|
6010
|
+
const closeLinkPopover = (0, import_react3.useCallback)(() => setLinkPopover(null), []);
|
|
6011
|
+
const openLinkPopoverForActive = (0, import_react3.useCallback)(() => {
|
|
7064
6012
|
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
7065
6013
|
if (!hrefCtx) return;
|
|
7066
6014
|
bumpLinkPopoverGrace();
|
|
@@ -7070,7 +6018,7 @@ function OhhwellsBridge() {
|
|
|
7070
6018
|
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
7071
6019
|
});
|
|
7072
6020
|
}, []);
|
|
7073
|
-
const handleLinkPopoverSubmit = (0,
|
|
6021
|
+
const handleLinkPopoverSubmit = (0, import_react3.useCallback)(
|
|
7074
6022
|
(target) => {
|
|
7075
6023
|
if (!linkPopover) return;
|
|
7076
6024
|
const { key } = linkPopover;
|
|
@@ -7083,11 +6031,11 @@ function OhhwellsBridge() {
|
|
|
7083
6031
|
const showEditLink = toolbarShowEditLink;
|
|
7084
6032
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
7085
6033
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
7086
|
-
return bridgeRoot ? (0,
|
|
7087
|
-
/* @__PURE__ */ (0,
|
|
7088
|
-
toolbarRect && /* @__PURE__ */ (0,
|
|
7089
|
-
/* @__PURE__ */ (0,
|
|
7090
|
-
/* @__PURE__ */ (0,
|
|
6034
|
+
return bridgeRoot ? (0, import_react_dom.createPortal)(
|
|
6035
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
6036
|
+
toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
6037
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
6038
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
7091
6039
|
FloatingToolbar,
|
|
7092
6040
|
{
|
|
7093
6041
|
rect: toolbarRect,
|
|
@@ -7100,7 +6048,7 @@ function OhhwellsBridge() {
|
|
|
7100
6048
|
}
|
|
7101
6049
|
)
|
|
7102
6050
|
] }),
|
|
7103
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
6051
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
7104
6052
|
"div",
|
|
7105
6053
|
{
|
|
7106
6054
|
"data-ohw-max-badge": "",
|
|
@@ -7126,7 +6074,7 @@ function OhhwellsBridge() {
|
|
|
7126
6074
|
]
|
|
7127
6075
|
}
|
|
7128
6076
|
),
|
|
7129
|
-
toggleState && /* @__PURE__ */ (0,
|
|
6077
|
+
toggleState && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
7130
6078
|
StateToggle,
|
|
7131
6079
|
{
|
|
7132
6080
|
rect: toggleState.rect,
|
|
@@ -7135,7 +6083,7 @@ function OhhwellsBridge() {
|
|
|
7135
6083
|
onStateChange: handleStateChange
|
|
7136
6084
|
}
|
|
7137
6085
|
),
|
|
7138
|
-
linkPopover ? /* @__PURE__ */ (0,
|
|
6086
|
+
linkPopover ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
7139
6087
|
LinkPopover,
|
|
7140
6088
|
{
|
|
7141
6089
|
rect: linkPopover.rect,
|
|
@@ -7150,32 +6098,7 @@ function OhhwellsBridge() {
|
|
|
7150
6098
|
onClose: closeLinkPopover,
|
|
7151
6099
|
onSubmit: handleLinkPopoverSubmit
|
|
7152
6100
|
}
|
|
7153
|
-
) : null
|
|
7154
|
-
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
7155
|
-
"div",
|
|
7156
|
-
{
|
|
7157
|
-
"data-ohw-section-insert-line": "",
|
|
7158
|
-
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
7159
|
-
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
7160
|
-
children: [
|
|
7161
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
7162
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7163
|
-
Badge,
|
|
7164
|
-
{
|
|
7165
|
-
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",
|
|
7166
|
-
onClick: () => {
|
|
7167
|
-
window.parent.postMessage(
|
|
7168
|
-
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
7169
|
-
"*"
|
|
7170
|
-
);
|
|
7171
|
-
},
|
|
7172
|
-
children: "Add Section"
|
|
7173
|
-
}
|
|
7174
|
-
),
|
|
7175
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
7176
|
-
]
|
|
7177
|
-
}
|
|
7178
|
-
)
|
|
6101
|
+
) : null
|
|
7179
6102
|
] }),
|
|
7180
6103
|
bridgeRoot
|
|
7181
6104
|
) : null;
|
|
@@ -7185,7 +6108,6 @@ function OhhwellsBridge() {
|
|
|
7185
6108
|
LinkEditorPanel,
|
|
7186
6109
|
LinkPopover,
|
|
7187
6110
|
OhhwellsBridge,
|
|
7188
|
-
SchedulingWidget,
|
|
7189
6111
|
Toggle,
|
|
7190
6112
|
ToggleGroup,
|
|
7191
6113
|
ToggleGroupItem,
|