@ohhwells/bridge 0.1.19 → 0.1.20
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 +415 -173
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +392 -150
- package/dist/index.js.map +1 -1
- package/dist/styles.css +114 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -41,12 +41,166 @@ __export(index_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(index_exports);
|
|
42
42
|
|
|
43
43
|
// src/OhhwellsBridge.tsx
|
|
44
|
-
var
|
|
44
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
45
45
|
var import_client = require("react-dom/client");
|
|
46
46
|
|
|
47
47
|
// src/ui/SchedulingWidget.tsx
|
|
48
|
-
var
|
|
48
|
+
var import_react2 = require("react");
|
|
49
|
+
|
|
50
|
+
// src/ui/EmailCaptureModal.tsx
|
|
51
|
+
var import_react = require("react");
|
|
52
|
+
var import_radix_ui = require("radix-ui");
|
|
49
53
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
54
|
+
function Spinner() {
|
|
55
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
56
|
+
"svg",
|
|
57
|
+
{
|
|
58
|
+
width: "32",
|
|
59
|
+
height: "32",
|
|
60
|
+
viewBox: "0 0 32 32",
|
|
61
|
+
fill: "none",
|
|
62
|
+
style: { animation: "ohw-spin 0.75s linear infinite" },
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
|
|
65
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
67
|
+
"path",
|
|
68
|
+
{
|
|
69
|
+
d: "M16 4a12 12 0 0 1 12 12",
|
|
70
|
+
stroke: "#3b82f6",
|
|
71
|
+
strokeWidth: "3",
|
|
72
|
+
strokeLinecap: "round"
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
80
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
81
|
+
const [submittedEmail, setSubmittedEmail] = (0, import_react.useState)("");
|
|
82
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
83
|
+
const [success, setSuccess] = (0, import_react.useState)(false);
|
|
84
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
85
|
+
const isBook = title === "Confirm your spot";
|
|
86
|
+
const handleSubmit = async () => {
|
|
87
|
+
if (!email.trim()) return;
|
|
88
|
+
setLoading(true);
|
|
89
|
+
setError(null);
|
|
90
|
+
try {
|
|
91
|
+
await onSubmit(email.trim());
|
|
92
|
+
setSubmittedEmail(email.trim());
|
|
93
|
+
setSuccess(true);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
96
|
+
} finally {
|
|
97
|
+
setLoading(false);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const handleKeyDown = (e) => {
|
|
101
|
+
if (e.key === "Enter" && !loading) handleSubmit();
|
|
102
|
+
};
|
|
103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Dialog.Root, { open: true, onOpenChange: (open) => {
|
|
104
|
+
if (!open) onClose();
|
|
105
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_radix_ui.Dialog.Portal, { children: [
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
107
|
+
import_radix_ui.Dialog.Overlay,
|
|
108
|
+
{
|
|
109
|
+
className: "fixed inset-0 z-50",
|
|
110
|
+
style: { background: "rgba(0,0,0,0.45)" }
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
114
|
+
import_radix_ui.Dialog.Content,
|
|
115
|
+
{
|
|
116
|
+
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",
|
|
117
|
+
style: { maxWidth: 400 },
|
|
118
|
+
children: [
|
|
119
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3 p-6", children: [
|
|
120
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
121
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
122
|
+
import_radix_ui.Dialog.Title,
|
|
123
|
+
{
|
|
124
|
+
className: "font-body text-xl font-bold m-0 leading-tight",
|
|
125
|
+
style: { color: "var(--color-dark,#200C02)" },
|
|
126
|
+
children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
130
|
+
import_radix_ui.Dialog.Description,
|
|
131
|
+
{
|
|
132
|
+
className: "font-body text-sm m-0",
|
|
133
|
+
style: { color: "#6B7280" },
|
|
134
|
+
children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
139
|
+
import_radix_ui.Dialog.Close,
|
|
140
|
+
{
|
|
141
|
+
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",
|
|
142
|
+
style: { color: "#6B7280" },
|
|
143
|
+
"aria-label": "Close",
|
|
144
|
+
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: [
|
|
145
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
146
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
147
|
+
] })
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
] }),
|
|
151
|
+
!success && (loading ? (
|
|
152
|
+
/* Loading state — full body replaced */
|
|
153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
|
|
156
|
+
] })
|
|
157
|
+
) : (
|
|
158
|
+
/* Email form */
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-7 pt-2 pb-6", children: [
|
|
160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 mb-8", children: [
|
|
161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
163
|
+
"input",
|
|
164
|
+
{
|
|
165
|
+
type: "email",
|
|
166
|
+
value: email,
|
|
167
|
+
onChange: (e) => setEmail(e.target.value),
|
|
168
|
+
onKeyDown: handleKeyDown,
|
|
169
|
+
placeholder: "hello@gmail.com",
|
|
170
|
+
autoFocus: true,
|
|
171
|
+
className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
|
|
172
|
+
style: {
|
|
173
|
+
borderColor: "#E7E5E4",
|
|
174
|
+
background: "#fff",
|
|
175
|
+
color: "var(--color-dark,#200C02)"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
),
|
|
179
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-xs text-red-500 m-0", children: error })
|
|
180
|
+
] }),
|
|
181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
182
|
+
"button",
|
|
183
|
+
{
|
|
184
|
+
onClick: handleSubmit,
|
|
185
|
+
disabled: !email.trim(),
|
|
186
|
+
className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
|
|
187
|
+
style: {
|
|
188
|
+
background: "var(--color-primary,#3D312B)",
|
|
189
|
+
color: "var(--color-light,#FEFFF0)"
|
|
190
|
+
},
|
|
191
|
+
children: "Send"
|
|
192
|
+
}
|
|
193
|
+
) })
|
|
194
|
+
] })
|
|
195
|
+
))
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
] }) });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/ui/SchedulingWidget.tsx
|
|
203
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
50
204
|
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
51
205
|
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
52
206
|
function getApiDomain() {
|
|
@@ -107,8 +261,8 @@ function EmptyState({ inEditor }) {
|
|
|
107
261
|
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
108
262
|
}
|
|
109
263
|
};
|
|
110
|
-
return /* @__PURE__ */ (0,
|
|
111
|
-
/* @__PURE__ */ (0,
|
|
264
|
+
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: [
|
|
265
|
+
/* @__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)(
|
|
112
266
|
"svg",
|
|
113
267
|
{
|
|
114
268
|
width: "20",
|
|
@@ -120,20 +274,20 @@ function EmptyState({ inEditor }) {
|
|
|
120
274
|
strokeLinecap: "round",
|
|
121
275
|
strokeLinejoin: "round",
|
|
122
276
|
children: [
|
|
123
|
-
/* @__PURE__ */ (0,
|
|
124
|
-
/* @__PURE__ */ (0,
|
|
125
|
-
/* @__PURE__ */ (0,
|
|
126
|
-
/* @__PURE__ */ (0,
|
|
127
|
-
/* @__PURE__ */ (0,
|
|
128
|
-
/* @__PURE__ */ (0,
|
|
277
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
278
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
279
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
280
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
129
283
|
]
|
|
130
284
|
}
|
|
131
285
|
) }),
|
|
132
|
-
/* @__PURE__ */ (0,
|
|
133
|
-
/* @__PURE__ */ (0,
|
|
134
|
-
/* @__PURE__ */ (0,
|
|
286
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
288
|
+
/* @__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." })
|
|
135
289
|
] }),
|
|
136
|
-
/* @__PURE__ */ (0,
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
137
291
|
"button",
|
|
138
292
|
{
|
|
139
293
|
onClick: handleAddSchedule,
|
|
@@ -149,96 +303,155 @@ function LoadingSkeleton() {
|
|
|
149
303
|
backgroundSize: "200% 100%",
|
|
150
304
|
animation: "ohw-shimmer 1.5s infinite"
|
|
151
305
|
};
|
|
152
|
-
return /* @__PURE__ */ (0,
|
|
153
|
-
/* @__PURE__ */ (0,
|
|
154
|
-
/* @__PURE__ */ (0,
|
|
155
|
-
/* @__PURE__ */ (0,
|
|
156
|
-
/* @__PURE__ */ (0,
|
|
306
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
308
|
+
/* @__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: [
|
|
309
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
310
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
157
311
|
] }, i)) }),
|
|
158
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
159
|
-
/* @__PURE__ */ (0,
|
|
160
|
-
/* @__PURE__ */ (0,
|
|
161
|
-
/* @__PURE__ */ (0,
|
|
162
|
-
/* @__PURE__ */ (0,
|
|
163
|
-
/* @__PURE__ */ (0,
|
|
312
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-[60px] py-4", children: [
|
|
314
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
315
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
317
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
164
318
|
] }),
|
|
165
|
-
/* @__PURE__ */ (0,
|
|
166
|
-
/* @__PURE__ */ (0,
|
|
319
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
320
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
167
321
|
] }),
|
|
168
|
-
i < 2 && /* @__PURE__ */ (0,
|
|
322
|
+
i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
169
323
|
] }, i))
|
|
170
324
|
] });
|
|
171
325
|
}
|
|
172
|
-
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
|
|
173
|
-
const todayMs = (0,
|
|
326
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
327
|
+
const todayMs = (0, import_react2.useMemo)(() => {
|
|
174
328
|
const d = /* @__PURE__ */ new Date();
|
|
175
329
|
d.setHours(0, 0, 0, 0);
|
|
176
330
|
return d.getTime();
|
|
177
331
|
}, []);
|
|
178
|
-
const
|
|
332
|
+
const scrollRef = (0, import_react2.useRef)(null);
|
|
333
|
+
const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
|
|
334
|
+
const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
|
|
335
|
+
const updateScrollState = () => {
|
|
336
|
+
const el = scrollRef.current;
|
|
337
|
+
if (!el) return;
|
|
338
|
+
setCanScrollLeft(el.scrollLeft > 0);
|
|
339
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
340
|
+
};
|
|
341
|
+
(0, import_react2.useEffect)(() => {
|
|
342
|
+
const el = scrollRef.current;
|
|
343
|
+
if (!el) return;
|
|
344
|
+
updateScrollState();
|
|
345
|
+
el.addEventListener("scroll", updateScrollState);
|
|
346
|
+
const ro = new ResizeObserver(updateScrollState);
|
|
347
|
+
ro.observe(el);
|
|
348
|
+
return () => {
|
|
349
|
+
el.removeEventListener("scroll", updateScrollState);
|
|
350
|
+
ro.disconnect();
|
|
351
|
+
};
|
|
352
|
+
}, [dates]);
|
|
353
|
+
const scroll = (dir) => {
|
|
354
|
+
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
355
|
+
};
|
|
356
|
+
const datesWithClasses = (0, import_react2.useMemo)(
|
|
179
357
|
() => new Set(dates.map(
|
|
180
358
|
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
181
359
|
).filter((i) => i >= 0)),
|
|
182
360
|
[dates, schedule]
|
|
183
361
|
);
|
|
184
362
|
const selectedDate = dates[selectedIdx];
|
|
185
|
-
const selectedClasses = (0,
|
|
363
|
+
const selectedClasses = (0, import_react2.useMemo)(
|
|
186
364
|
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
187
365
|
[schedule, selectedDate]
|
|
188
366
|
);
|
|
189
|
-
return /* @__PURE__ */ (0,
|
|
190
|
-
/* @__PURE__ */ (0,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
367
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
|
|
368
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full", children: [
|
|
369
|
+
canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
370
|
+
"button",
|
|
371
|
+
{
|
|
372
|
+
onClick: () => scroll("left"),
|
|
373
|
+
className: "absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
|
|
374
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
375
|
+
"aria-label": "Scroll left",
|
|
376
|
+
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" }) })
|
|
377
|
+
}
|
|
378
|
+
),
|
|
379
|
+
canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
380
|
+
"button",
|
|
381
|
+
{
|
|
382
|
+
onClick: () => scroll("right"),
|
|
383
|
+
className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
|
|
384
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
385
|
+
"aria-label": "Scroll right",
|
|
386
|
+
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" }) })
|
|
387
|
+
}
|
|
388
|
+
),
|
|
389
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
196
390
|
"div",
|
|
197
391
|
{
|
|
198
|
-
|
|
199
|
-
className:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
392
|
+
ref: scrollRef,
|
|
393
|
+
className: "overflow-x-auto w-full",
|
|
394
|
+
style: {
|
|
395
|
+
scrollbarWidth: "none",
|
|
396
|
+
msOverflowStyle: "none",
|
|
397
|
+
paddingLeft: canScrollLeft ? "36px" : 0,
|
|
398
|
+
paddingRight: canScrollRight ? "36px" : 0
|
|
399
|
+
},
|
|
400
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
401
|
+
const isToday = date.getTime() === todayMs;
|
|
402
|
+
const isSelected = i === selectedIdx;
|
|
403
|
+
const clickable = datesWithClasses.has(i);
|
|
404
|
+
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
203
406
|
"div",
|
|
204
407
|
{
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
children:
|
|
208
|
-
"span",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
408
|
+
onClick: () => clickable && onSelectDate(i),
|
|
409
|
+
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
410
|
+
children: [
|
|
411
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
413
|
+
"div",
|
|
414
|
+
{
|
|
415
|
+
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
416
|
+
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
417
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
418
|
+
"span",
|
|
419
|
+
{
|
|
420
|
+
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
421
|
+
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
422
|
+
children: date.getDate()
|
|
423
|
+
}
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
i
|
|
430
|
+
);
|
|
431
|
+
}) })
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
] }),
|
|
435
|
+
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) => {
|
|
223
436
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
224
437
|
const available = cls.maxParticipants - booked;
|
|
225
438
|
const isFull = available <= 0;
|
|
226
|
-
return /* @__PURE__ */ (0,
|
|
227
|
-
/* @__PURE__ */ (0,
|
|
228
|
-
/* @__PURE__ */ (0,
|
|
229
|
-
/* @__PURE__ */ (0,
|
|
230
|
-
/* @__PURE__ */ (0,
|
|
231
|
-
cls.hostName && /* @__PURE__ */ (0,
|
|
439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
440
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
|
|
441
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-[120px] 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) }) }),
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
444
|
+
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 })
|
|
232
445
|
] }),
|
|
233
|
-
/* @__PURE__ */ (0,
|
|
234
|
-
/* @__PURE__ */ (0,
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-14 shrink-0 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: [
|
|
447
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
235
448
|
available,
|
|
236
449
|
"/",
|
|
237
450
|
cls.maxParticipants
|
|
238
451
|
] }),
|
|
239
|
-
/* @__PURE__ */ (0,
|
|
452
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
240
453
|
] }) }),
|
|
241
|
-
/* @__PURE__ */ (0,
|
|
454
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
242
455
|
"button",
|
|
243
456
|
{
|
|
244
457
|
className: "shrink-0 w-[200px] px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
|
|
@@ -251,17 +464,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
|
|
|
251
464
|
border: "none",
|
|
252
465
|
color: "var(--color-light, #FEFFF0)"
|
|
253
466
|
},
|
|
467
|
+
onClick: () => {
|
|
468
|
+
if (!cls.id) return;
|
|
469
|
+
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
470
|
+
},
|
|
254
471
|
children: isFull ? "Join Waitlist" : "Book Now"
|
|
255
472
|
}
|
|
256
473
|
)
|
|
257
474
|
] }),
|
|
258
|
-
i < selectedClasses.length - 1 && /* @__PURE__ */ (0,
|
|
475
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
|
|
259
476
|
] }, cls.id ?? i);
|
|
260
477
|
}) })
|
|
261
478
|
] });
|
|
262
479
|
}
|
|
263
480
|
function CalendarFoldIcon() {
|
|
264
|
-
return /* @__PURE__ */ (0,
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
265
482
|
"svg",
|
|
266
483
|
{
|
|
267
484
|
width: "16",
|
|
@@ -274,22 +491,23 @@ function CalendarFoldIcon() {
|
|
|
274
491
|
strokeLinejoin: "round",
|
|
275
492
|
style: { flexShrink: 0 },
|
|
276
493
|
children: [
|
|
277
|
-
/* @__PURE__ */ (0,
|
|
278
|
-
/* @__PURE__ */ (0,
|
|
279
|
-
/* @__PURE__ */ (0,
|
|
280
|
-
/* @__PURE__ */ (0,
|
|
281
|
-
/* @__PURE__ */ (0,
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v4" }),
|
|
495
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 2v4" }),
|
|
496
|
+
/* @__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" }),
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 10h18" }),
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
282
499
|
]
|
|
283
500
|
}
|
|
284
501
|
);
|
|
285
502
|
}
|
|
286
503
|
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
287
|
-
const [schedule, setSchedule] = (0,
|
|
288
|
-
const [loading, setLoading] = (0,
|
|
289
|
-
const [inEditor, setInEditor] = (0,
|
|
290
|
-
const [isHovered, setIsHovered] = (0,
|
|
291
|
-
const
|
|
292
|
-
const
|
|
504
|
+
const [schedule, setSchedule] = (0, import_react2.useState)(null);
|
|
505
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
506
|
+
const [inEditor, setInEditor] = (0, import_react2.useState)(false);
|
|
507
|
+
const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
|
|
508
|
+
const [modalState, setModalState] = (0, import_react2.useState)(null);
|
|
509
|
+
const switchScheduleIdRef = (0, import_react2.useRef)(null);
|
|
510
|
+
const dates = (0, import_react2.useMemo)(() => {
|
|
293
511
|
const today = /* @__PURE__ */ new Date();
|
|
294
512
|
today.setHours(0, 0, 0, 0);
|
|
295
513
|
return Array.from({ length: 14 }, (_, i) => {
|
|
@@ -298,8 +516,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
298
516
|
return d;
|
|
299
517
|
});
|
|
300
518
|
}, []);
|
|
301
|
-
const [selectedIdx, setSelectedIdx] = (0,
|
|
302
|
-
(0,
|
|
519
|
+
const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
|
|
520
|
+
(0, import_react2.useEffect)(() => {
|
|
303
521
|
if (!schedule?.classes) return;
|
|
304
522
|
for (let i = 0; i < dates.length; i++) {
|
|
305
523
|
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
@@ -308,7 +526,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
308
526
|
}
|
|
309
527
|
}
|
|
310
528
|
}, [schedule, dates]);
|
|
311
|
-
(0,
|
|
529
|
+
(0, import_react2.useEffect)(() => {
|
|
312
530
|
if (typeof window === "undefined") return;
|
|
313
531
|
const isInEditor = window.self !== window.top;
|
|
314
532
|
setInEditor(isInEditor);
|
|
@@ -394,6 +612,20 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
394
612
|
return void 0;
|
|
395
613
|
}
|
|
396
614
|
})() : void 0;
|
|
615
|
+
const handleModalSubmit = async (email) => {
|
|
616
|
+
if (!modalState) return;
|
|
617
|
+
const { mode, classId, classDate } = modalState;
|
|
618
|
+
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
619
|
+
const res = await fetch(endpoint, {
|
|
620
|
+
method: "POST",
|
|
621
|
+
headers: { "Content-Type": "application/json" },
|
|
622
|
+
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
623
|
+
});
|
|
624
|
+
if (!res.ok) {
|
|
625
|
+
const data = await res.json().catch(() => ({}));
|
|
626
|
+
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
627
|
+
}
|
|
628
|
+
};
|
|
397
629
|
const handleReplaceSchedule = () => {
|
|
398
630
|
setIsHovered(false);
|
|
399
631
|
if (schedule) {
|
|
@@ -402,7 +634,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
402
634
|
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
403
635
|
}
|
|
404
636
|
};
|
|
405
|
-
return /* @__PURE__ */ (0,
|
|
637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
406
638
|
"section",
|
|
407
639
|
{
|
|
408
640
|
"data-ohw-section": "scheduling",
|
|
@@ -410,15 +642,15 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
410
642
|
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
411
643
|
onMouseLeave: () => setIsHovered(false),
|
|
412
644
|
children: [
|
|
413
|
-
inEditor && isHovered && !!schedule && /* @__PURE__ */ (0,
|
|
645
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
414
646
|
"div",
|
|
415
647
|
{
|
|
416
648
|
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
417
649
|
onClick: handleReplaceSchedule,
|
|
418
650
|
children: [
|
|
419
|
-
/* @__PURE__ */ (0,
|
|
420
|
-
/* @__PURE__ */ (0,
|
|
421
|
-
/* @__PURE__ */ (0,
|
|
651
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
652
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
653
|
+
/* @__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)(
|
|
422
654
|
"div",
|
|
423
655
|
{
|
|
424
656
|
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
@@ -430,7 +662,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
430
662
|
color: "#0C0A09"
|
|
431
663
|
},
|
|
432
664
|
children: [
|
|
433
|
-
/* @__PURE__ */ (0,
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
|
|
434
666
|
"Replace schedule"
|
|
435
667
|
]
|
|
436
668
|
}
|
|
@@ -438,24 +670,34 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
438
670
|
]
|
|
439
671
|
}
|
|
440
672
|
),
|
|
441
|
-
/* @__PURE__ */ (0,
|
|
442
|
-
/* @__PURE__ */ (0,
|
|
443
|
-
/* @__PURE__ */ (0,
|
|
444
|
-
schedule?.description && /* @__PURE__ */ (0,
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
|
|
676
|
+
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 })
|
|
445
677
|
] }),
|
|
446
|
-
/* @__PURE__ */ (0,
|
|
447
|
-
timezoneLabel && /* @__PURE__ */ (0,
|
|
448
|
-
/* @__PURE__ */ (0,
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
679
|
+
timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full 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)(
|
|
449
681
|
ScheduleView,
|
|
450
682
|
{
|
|
451
683
|
schedule,
|
|
452
684
|
dates,
|
|
453
685
|
selectedIdx,
|
|
454
|
-
onSelectDate: setSelectedIdx
|
|
686
|
+
onSelectDate: setSelectedIdx,
|
|
687
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
455
688
|
}
|
|
456
689
|
) })
|
|
457
690
|
] })
|
|
458
|
-
] })
|
|
691
|
+
] }),
|
|
692
|
+
modalState && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
693
|
+
EmailCaptureModal,
|
|
694
|
+
{
|
|
695
|
+
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
696
|
+
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
697
|
+
onSubmit: handleModalSubmit,
|
|
698
|
+
onClose: () => setModalState(null)
|
|
699
|
+
}
|
|
700
|
+
)
|
|
459
701
|
]
|
|
460
702
|
}
|
|
461
703
|
);
|
|
@@ -3781,8 +4023,8 @@ var cva = (base, config) => (props) => {
|
|
|
3781
4023
|
};
|
|
3782
4024
|
|
|
3783
4025
|
// src/ui/toggle.tsx
|
|
3784
|
-
var
|
|
3785
|
-
var
|
|
4026
|
+
var import_radix_ui2 = require("radix-ui");
|
|
4027
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3786
4028
|
var toggleVariants = cva(
|
|
3787
4029
|
"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",
|
|
3788
4030
|
{
|
|
@@ -3809,8 +4051,8 @@ function Toggle({
|
|
|
3809
4051
|
size,
|
|
3810
4052
|
...props
|
|
3811
4053
|
}) {
|
|
3812
|
-
return /* @__PURE__ */ (0,
|
|
3813
|
-
|
|
4054
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4055
|
+
import_radix_ui2.Toggle.Root,
|
|
3814
4056
|
{
|
|
3815
4057
|
"data-slot": "toggle",
|
|
3816
4058
|
className: cn(toggleVariants({ variant, size, className })),
|
|
@@ -3820,7 +4062,7 @@ function Toggle({
|
|
|
3820
4062
|
}
|
|
3821
4063
|
|
|
3822
4064
|
// src/ui/toggle-group.tsx
|
|
3823
|
-
var
|
|
4065
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3824
4066
|
var ToggleGroupContext = React2.createContext({
|
|
3825
4067
|
value: "",
|
|
3826
4068
|
onValueChange: () => {
|
|
@@ -3833,13 +4075,13 @@ function ToggleGroup({
|
|
|
3833
4075
|
children,
|
|
3834
4076
|
...props
|
|
3835
4077
|
}) {
|
|
3836
|
-
return /* @__PURE__ */ (0,
|
|
4078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3837
4079
|
"div",
|
|
3838
4080
|
{
|
|
3839
4081
|
role: "group",
|
|
3840
4082
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3841
4083
|
...props,
|
|
3842
|
-
children: /* @__PURE__ */ (0,
|
|
4084
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3843
4085
|
}
|
|
3844
4086
|
);
|
|
3845
4087
|
}
|
|
@@ -3850,7 +4092,7 @@ function ToggleGroupItem({
|
|
|
3850
4092
|
...props
|
|
3851
4093
|
}) {
|
|
3852
4094
|
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
3853
|
-
return /* @__PURE__ */ (0,
|
|
4095
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3854
4096
|
Toggle,
|
|
3855
4097
|
{
|
|
3856
4098
|
pressed: groupValue === value,
|
|
@@ -3892,7 +4134,7 @@ function isEditSessionActive() {
|
|
|
3892
4134
|
}
|
|
3893
4135
|
|
|
3894
4136
|
// src/ui/badge.tsx
|
|
3895
|
-
var
|
|
4137
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3896
4138
|
var badgeVariants = cva(
|
|
3897
4139
|
"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",
|
|
3898
4140
|
{
|
|
@@ -3910,11 +4152,11 @@ var badgeVariants = cva(
|
|
|
3910
4152
|
}
|
|
3911
4153
|
);
|
|
3912
4154
|
function Badge({ className, variant, ...props }) {
|
|
3913
|
-
return /* @__PURE__ */ (0,
|
|
4155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
3914
4156
|
}
|
|
3915
4157
|
|
|
3916
4158
|
// src/OhhwellsBridge.tsx
|
|
3917
|
-
var
|
|
4159
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3918
4160
|
var PRIMARY = "#0885FE";
|
|
3919
4161
|
var IMAGE_FADE_MS = 300;
|
|
3920
4162
|
function runOpacityFade(el, onDone) {
|
|
@@ -3982,7 +4224,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId)
|
|
|
3982
4224
|
const container = document.createElement("div");
|
|
3983
4225
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
3984
4226
|
anchor.insertAdjacentElement("afterend", container);
|
|
3985
|
-
(0, import_client.createRoot)(container).render(/* @__PURE__ */ (0,
|
|
4227
|
+
(0, import_client.createRoot)(container).render(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
|
|
3986
4228
|
const tracker = getSectionsTracker();
|
|
3987
4229
|
let sections = [];
|
|
3988
4230
|
try {
|
|
@@ -4157,7 +4399,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4157
4399
|
];
|
|
4158
4400
|
function GlowFrame({ rect, elRef }) {
|
|
4159
4401
|
const GAP = 6;
|
|
4160
|
-
return /* @__PURE__ */ (0,
|
|
4402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4161
4403
|
"div",
|
|
4162
4404
|
{
|
|
4163
4405
|
ref: elRef,
|
|
@@ -4208,7 +4450,7 @@ function FloatingToolbar({
|
|
|
4208
4450
|
activeCommands
|
|
4209
4451
|
}) {
|
|
4210
4452
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
4211
|
-
return /* @__PURE__ */ (0,
|
|
4453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4212
4454
|
"div",
|
|
4213
4455
|
{
|
|
4214
4456
|
ref: elRef,
|
|
@@ -4231,11 +4473,11 @@ function FloatingToolbar({
|
|
|
4231
4473
|
pointerEvents: "auto",
|
|
4232
4474
|
whiteSpace: "nowrap"
|
|
4233
4475
|
},
|
|
4234
|
-
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0,
|
|
4235
|
-
gi > 0 && /* @__PURE__ */ (0,
|
|
4476
|
+
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react3.default.Fragment, { children: [
|
|
4477
|
+
gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4236
4478
|
btns.map((btn) => {
|
|
4237
4479
|
const isActive = activeCommands.has(btn.cmd);
|
|
4238
|
-
return /* @__PURE__ */ (0,
|
|
4480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4239
4481
|
"button",
|
|
4240
4482
|
{
|
|
4241
4483
|
title: btn.title,
|
|
@@ -4261,7 +4503,7 @@ function FloatingToolbar({
|
|
|
4261
4503
|
flexShrink: 0,
|
|
4262
4504
|
padding: 6
|
|
4263
4505
|
},
|
|
4264
|
-
children: /* @__PURE__ */ (0,
|
|
4506
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4265
4507
|
"svg",
|
|
4266
4508
|
{
|
|
4267
4509
|
width: "16",
|
|
@@ -4294,7 +4536,7 @@ function StateToggle({
|
|
|
4294
4536
|
states,
|
|
4295
4537
|
onStateChange
|
|
4296
4538
|
}) {
|
|
4297
|
-
return /* @__PURE__ */ (0,
|
|
4539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4298
4540
|
ToggleGroup,
|
|
4299
4541
|
{
|
|
4300
4542
|
"data-ohw-state-toggle": "",
|
|
@@ -4308,7 +4550,7 @@ function StateToggle({
|
|
|
4308
4550
|
left: rect.right - 8,
|
|
4309
4551
|
transform: "translateX(-100%)"
|
|
4310
4552
|
},
|
|
4311
|
-
children: states.map((state) => /* @__PURE__ */ (0,
|
|
4553
|
+
children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4312
4554
|
}
|
|
4313
4555
|
);
|
|
4314
4556
|
}
|
|
@@ -4318,8 +4560,8 @@ function OhhwellsBridge() {
|
|
|
4318
4560
|
const router = (0, import_navigation.useRouter)();
|
|
4319
4561
|
const searchParams = (0, import_navigation.useSearchParams)();
|
|
4320
4562
|
const isEditMode = isEditSessionActive();
|
|
4321
|
-
const [bridgeRoot, setBridgeRoot] = (0,
|
|
4322
|
-
(0,
|
|
4563
|
+
const [bridgeRoot, setBridgeRoot] = (0, import_react3.useState)(null);
|
|
4564
|
+
(0, import_react3.useEffect)(() => {
|
|
4323
4565
|
const figtreeFontId = "ohw-figtree-font";
|
|
4324
4566
|
if (!document.getElementById(figtreeFontId)) {
|
|
4325
4567
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4347,39 +4589,39 @@ function OhhwellsBridge() {
|
|
|
4347
4589
|
const parts = window.location.hostname.split(".");
|
|
4348
4590
|
return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
|
|
4349
4591
|
})();
|
|
4350
|
-
const postToParent = (0,
|
|
4592
|
+
const postToParent = (0, import_react3.useCallback)((data) => {
|
|
4351
4593
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4352
4594
|
window.parent.postMessage(data, "*");
|
|
4353
4595
|
}
|
|
4354
4596
|
}, []);
|
|
4355
|
-
const [fetchState, setFetchState] = (0,
|
|
4356
|
-
const autoSaveTimers = (0,
|
|
4357
|
-
const activeElRef = (0,
|
|
4358
|
-
const originalContentRef = (0,
|
|
4359
|
-
const activeStateElRef = (0,
|
|
4360
|
-
const parentScrollRef = (0,
|
|
4361
|
-
const toolbarElRef = (0,
|
|
4362
|
-
const glowElRef = (0,
|
|
4363
|
-
const hoveredImageRef = (0,
|
|
4364
|
-
const hoveredImageHasTextOverlapRef = (0,
|
|
4365
|
-
const hoveredGapRef = (0,
|
|
4366
|
-
const imageUnhoverTimerRef = (0,
|
|
4367
|
-
const imageShowTimerRef = (0,
|
|
4368
|
-
const editStylesRef = (0,
|
|
4369
|
-
const activateRef = (0,
|
|
4597
|
+
const [fetchState, setFetchState] = (0, import_react3.useState)("idle");
|
|
4598
|
+
const autoSaveTimers = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
4599
|
+
const activeElRef = (0, import_react3.useRef)(null);
|
|
4600
|
+
const originalContentRef = (0, import_react3.useRef)(null);
|
|
4601
|
+
const activeStateElRef = (0, import_react3.useRef)(null);
|
|
4602
|
+
const parentScrollRef = (0, import_react3.useRef)(null);
|
|
4603
|
+
const toolbarElRef = (0, import_react3.useRef)(null);
|
|
4604
|
+
const glowElRef = (0, import_react3.useRef)(null);
|
|
4605
|
+
const hoveredImageRef = (0, import_react3.useRef)(null);
|
|
4606
|
+
const hoveredImageHasTextOverlapRef = (0, import_react3.useRef)(false);
|
|
4607
|
+
const hoveredGapRef = (0, import_react3.useRef)(null);
|
|
4608
|
+
const imageUnhoverTimerRef = (0, import_react3.useRef)(null);
|
|
4609
|
+
const imageShowTimerRef = (0, import_react3.useRef)(null);
|
|
4610
|
+
const editStylesRef = (0, import_react3.useRef)(null);
|
|
4611
|
+
const activateRef = (0, import_react3.useRef)(() => {
|
|
4370
4612
|
});
|
|
4371
|
-
const deactivateRef = (0,
|
|
4613
|
+
const deactivateRef = (0, import_react3.useRef)(() => {
|
|
4372
4614
|
});
|
|
4373
|
-
const refreshActiveCommandsRef = (0,
|
|
4615
|
+
const refreshActiveCommandsRef = (0, import_react3.useRef)(() => {
|
|
4374
4616
|
});
|
|
4375
|
-
const postToParentRef = (0,
|
|
4617
|
+
const postToParentRef = (0, import_react3.useRef)(postToParent);
|
|
4376
4618
|
postToParentRef.current = postToParent;
|
|
4377
|
-
const [toolbarRect, setToolbarRect] = (0,
|
|
4378
|
-
const [toggleState, setToggleState] = (0,
|
|
4379
|
-
const [maxBadge, setMaxBadge] = (0,
|
|
4380
|
-
const [activeCommands, setActiveCommands] = (0,
|
|
4381
|
-
const [sectionGap, setSectionGap] = (0,
|
|
4382
|
-
(0,
|
|
4619
|
+
const [toolbarRect, setToolbarRect] = (0, import_react3.useState)(null);
|
|
4620
|
+
const [toggleState, setToggleState] = (0, import_react3.useState)(null);
|
|
4621
|
+
const [maxBadge, setMaxBadge] = (0, import_react3.useState)(null);
|
|
4622
|
+
const [activeCommands, setActiveCommands] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
|
|
4623
|
+
const [sectionGap, setSectionGap] = (0, import_react3.useState)(null);
|
|
4624
|
+
(0, import_react3.useEffect)(() => {
|
|
4383
4625
|
const update = () => {
|
|
4384
4626
|
const el = activeElRef.current;
|
|
4385
4627
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -4403,10 +4645,10 @@ function OhhwellsBridge() {
|
|
|
4403
4645
|
vvp.removeEventListener("resize", update);
|
|
4404
4646
|
};
|
|
4405
4647
|
}, []);
|
|
4406
|
-
const refreshStateRules = (0,
|
|
4648
|
+
const refreshStateRules = (0, import_react3.useCallback)(() => {
|
|
4407
4649
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4408
4650
|
}, []);
|
|
4409
|
-
const deactivate = (0,
|
|
4651
|
+
const deactivate = (0, import_react3.useCallback)(() => {
|
|
4410
4652
|
const el = activeElRef.current;
|
|
4411
4653
|
if (!el) return;
|
|
4412
4654
|
const key = el.dataset.ohwKey;
|
|
@@ -4431,7 +4673,7 @@ function OhhwellsBridge() {
|
|
|
4431
4673
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
4432
4674
|
postToParent({ type: "ow:exit-edit" });
|
|
4433
4675
|
}, [postToParent]);
|
|
4434
|
-
const activate = (0,
|
|
4676
|
+
const activate = (0, import_react3.useCallback)((el) => {
|
|
4435
4677
|
if (activeElRef.current === el) return;
|
|
4436
4678
|
deactivate();
|
|
4437
4679
|
if (hoveredImageRef.current) {
|
|
@@ -4449,7 +4691,7 @@ function OhhwellsBridge() {
|
|
|
4449
4691
|
}, [deactivate, postToParent]);
|
|
4450
4692
|
activateRef.current = activate;
|
|
4451
4693
|
deactivateRef.current = deactivate;
|
|
4452
|
-
(0,
|
|
4694
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
4453
4695
|
if (!subdomain || isEditMode) {
|
|
4454
4696
|
setFetchState("done");
|
|
4455
4697
|
return;
|
|
@@ -4512,7 +4754,7 @@ function OhhwellsBridge() {
|
|
|
4512
4754
|
cancelled = true;
|
|
4513
4755
|
};
|
|
4514
4756
|
}, [subdomain, isEditMode, pathname]);
|
|
4515
|
-
(0,
|
|
4757
|
+
(0, import_react3.useEffect)(() => {
|
|
4516
4758
|
if (!subdomain || isEditMode) return;
|
|
4517
4759
|
const applyFromCache = () => {
|
|
4518
4760
|
const content = contentCache.get(subdomain);
|
|
@@ -4548,16 +4790,16 @@ function OhhwellsBridge() {
|
|
|
4548
4790
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
4549
4791
|
return () => observer.disconnect();
|
|
4550
4792
|
}, [subdomain, isEditMode]);
|
|
4551
|
-
(0,
|
|
4793
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
4552
4794
|
const el = document.getElementById("ohw-loader");
|
|
4553
4795
|
if (!el) return;
|
|
4554
4796
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
4555
4797
|
el.style.display = visible ? "flex" : "none";
|
|
4556
4798
|
}, [subdomain, fetchState]);
|
|
4557
|
-
(0,
|
|
4799
|
+
(0, import_react3.useEffect)(() => {
|
|
4558
4800
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
4559
4801
|
}, [pathname, postToParent]);
|
|
4560
|
-
(0,
|
|
4802
|
+
(0, import_react3.useEffect)(() => {
|
|
4561
4803
|
if (!isEditMode) return;
|
|
4562
4804
|
const measure = () => {
|
|
4563
4805
|
const h = document.body.scrollHeight;
|
|
@@ -4581,7 +4823,7 @@ function OhhwellsBridge() {
|
|
|
4581
4823
|
window.removeEventListener("resize", handleResize);
|
|
4582
4824
|
};
|
|
4583
4825
|
}, [pathname, isEditMode, postToParent]);
|
|
4584
|
-
(0,
|
|
4826
|
+
(0, import_react3.useEffect)(() => {
|
|
4585
4827
|
if (!subdomainFromQuery || isEditMode) return;
|
|
4586
4828
|
const handleClick = (e) => {
|
|
4587
4829
|
const anchor = e.target.closest("a");
|
|
@@ -4597,7 +4839,7 @@ function OhhwellsBridge() {
|
|
|
4597
4839
|
document.addEventListener("click", handleClick, true);
|
|
4598
4840
|
return () => document.removeEventListener("click", handleClick, true);
|
|
4599
4841
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
4600
|
-
(0,
|
|
4842
|
+
(0, import_react3.useEffect)(() => {
|
|
4601
4843
|
if (!isEditMode) {
|
|
4602
4844
|
editStylesRef.current?.base.remove();
|
|
4603
4845
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5412,7 +5654,7 @@ function OhhwellsBridge() {
|
|
|
5412
5654
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5413
5655
|
};
|
|
5414
5656
|
}, [isEditMode, refreshStateRules]);
|
|
5415
|
-
(0,
|
|
5657
|
+
(0, import_react3.useEffect)(() => {
|
|
5416
5658
|
if (!isEditMode) return;
|
|
5417
5659
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5418
5660
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5434,13 +5676,13 @@ function OhhwellsBridge() {
|
|
|
5434
5676
|
clearTimeout(timer);
|
|
5435
5677
|
};
|
|
5436
5678
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5437
|
-
const handleCommand = (0,
|
|
5679
|
+
const handleCommand = (0, import_react3.useCallback)((cmd) => {
|
|
5438
5680
|
document.execCommand(cmd, false);
|
|
5439
5681
|
activeElRef.current?.focus();
|
|
5440
5682
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
5441
5683
|
refreshActiveCommandsRef.current();
|
|
5442
5684
|
}, []);
|
|
5443
|
-
const handleStateChange = (0,
|
|
5685
|
+
const handleStateChange = (0, import_react3.useCallback)((state) => {
|
|
5444
5686
|
if (!activeStateElRef.current) return;
|
|
5445
5687
|
const el = activeStateElRef.current;
|
|
5446
5688
|
if (state === "Default") {
|
|
@@ -5454,12 +5696,12 @@ function OhhwellsBridge() {
|
|
|
5454
5696
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
5455
5697
|
}, [deactivate]);
|
|
5456
5698
|
return bridgeRoot ? (0, import_react_dom.createPortal)(
|
|
5457
|
-
/* @__PURE__ */ (0,
|
|
5458
|
-
toolbarRect && /* @__PURE__ */ (0,
|
|
5459
|
-
/* @__PURE__ */ (0,
|
|
5460
|
-
/* @__PURE__ */ (0,
|
|
5699
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5700
|
+
toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5701
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
5702
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
|
|
5461
5703
|
] }),
|
|
5462
|
-
maxBadge && /* @__PURE__ */ (0,
|
|
5704
|
+
maxBadge && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
5463
5705
|
"div",
|
|
5464
5706
|
{
|
|
5465
5707
|
"data-ohw-max-badge": "",
|
|
@@ -5485,7 +5727,7 @@ function OhhwellsBridge() {
|
|
|
5485
5727
|
]
|
|
5486
5728
|
}
|
|
5487
5729
|
),
|
|
5488
|
-
toggleState && /* @__PURE__ */ (0,
|
|
5730
|
+
toggleState && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5489
5731
|
StateToggle,
|
|
5490
5732
|
{
|
|
5491
5733
|
rect: toggleState.rect,
|
|
@@ -5494,15 +5736,15 @@ function OhhwellsBridge() {
|
|
|
5494
5736
|
onStateChange: handleStateChange
|
|
5495
5737
|
}
|
|
5496
5738
|
),
|
|
5497
|
-
sectionGap && /* @__PURE__ */ (0,
|
|
5739
|
+
sectionGap && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
5498
5740
|
"div",
|
|
5499
5741
|
{
|
|
5500
5742
|
"data-ohw-section-insert-line": "",
|
|
5501
5743
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
5502
5744
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
5503
5745
|
children: [
|
|
5504
|
-
/* @__PURE__ */ (0,
|
|
5505
|
-
/* @__PURE__ */ (0,
|
|
5746
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
5747
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5506
5748
|
Badge,
|
|
5507
5749
|
{
|
|
5508
5750
|
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",
|
|
@@ -5515,7 +5757,7 @@ function OhhwellsBridge() {
|
|
|
5515
5757
|
children: "Add Section"
|
|
5516
5758
|
}
|
|
5517
5759
|
),
|
|
5518
|
-
/* @__PURE__ */ (0,
|
|
5760
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
5519
5761
|
]
|
|
5520
5762
|
}
|
|
5521
5763
|
)
|