@ohhwells/bridge 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -269
- package/dist/index.cjs +1208 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -3
- package/dist/index.d.ts +63 -3
- package/dist/index.js +1197 -132
- package/dist/index.js.map +1 -1
- package/dist/styles.css +322 -2
- package/package.json +48 -48
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
4
|
+
import React5, { useCallback as useCallback2, useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as useState5 } from "react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
7
|
|
|
@@ -216,10 +216,10 @@ function buildTimezoneLabel(tz) {
|
|
|
216
216
|
return tz;
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
-
function EmptyState({ inEditor
|
|
219
|
+
function EmptyState({ inEditor }) {
|
|
220
220
|
const handleAddSchedule = () => {
|
|
221
221
|
if (inEditor) {
|
|
222
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected"
|
|
222
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
223
223
|
}
|
|
224
224
|
};
|
|
225
225
|
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
|
|
@@ -444,21 +444,18 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
444
444
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
445
445
|
const available = cls.maxParticipants - booked;
|
|
446
446
|
const isFull = available <= 0;
|
|
447
|
-
const isPrivate = cls.maxParticipants === 1;
|
|
448
447
|
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
449
448
|
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
450
449
|
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
451
|
-
/* @__PURE__ */
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
)
|
|
461
|
-
] }),
|
|
450
|
+
/* @__PURE__ */ jsx2("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
451
|
+
/* @__PURE__ */ jsx2(
|
|
452
|
+
"span",
|
|
453
|
+
{
|
|
454
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
|
|
455
|
+
style: { borderColor: "#0885FE", color: "#0885FE" },
|
|
456
|
+
children: "GROUP"
|
|
457
|
+
}
|
|
458
|
+
),
|
|
462
459
|
/* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
463
460
|
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
464
461
|
available,
|
|
@@ -677,7 +674,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
677
674
|
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
678
675
|
}
|
|
679
676
|
};
|
|
680
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
681
677
|
const sectionId = `scheduling-${insertAfter}`;
|
|
682
678
|
return /* @__PURE__ */ jsxs2(
|
|
683
679
|
"section",
|
|
@@ -723,7 +719,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
723
719
|
] }),
|
|
724
720
|
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
725
721
|
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
726
|
-
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor
|
|
722
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
|
|
727
723
|
ScheduleView,
|
|
728
724
|
{
|
|
729
725
|
schedule,
|
|
@@ -4179,8 +4175,844 @@ function isEditSessionActive() {
|
|
|
4179
4175
|
return new URLSearchParams(q).get("mode") === "edit";
|
|
4180
4176
|
}
|
|
4181
4177
|
|
|
4182
|
-
// src/ui/
|
|
4178
|
+
// src/ui/link-modal/calcPopoverPos.ts
|
|
4179
|
+
function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
|
|
4180
|
+
const GAP = 8;
|
|
4181
|
+
const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
|
|
4182
|
+
const visibleTop = rect.top - canvasTopInIframe;
|
|
4183
|
+
let top = rect.top - GAP;
|
|
4184
|
+
let transform = "translateX(-50%) translateY(-100%)";
|
|
4185
|
+
if (visibleTop < approxH + GAP) {
|
|
4186
|
+
top = rect.bottom + GAP;
|
|
4187
|
+
transform = "translateX(-50%)";
|
|
4188
|
+
}
|
|
4189
|
+
const rawLeft = rect.left + rect.width / 2;
|
|
4190
|
+
const left = Math.max(GAP + approxW / 2, Math.min(rawLeft, window.innerWidth - GAP - approxW / 2));
|
|
4191
|
+
return { top, left, transform };
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
// src/ui/button.tsx
|
|
4195
|
+
import * as React3 from "react";
|
|
4196
|
+
import { Slot } from "radix-ui";
|
|
4183
4197
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
4198
|
+
var buttonVariants = cva(
|
|
4199
|
+
"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",
|
|
4200
|
+
{
|
|
4201
|
+
variants: {
|
|
4202
|
+
variant: {
|
|
4203
|
+
default: "bg-primary text-primary-foreground hover:opacity-90",
|
|
4204
|
+
outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
|
|
4205
|
+
ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
|
|
4206
|
+
},
|
|
4207
|
+
size: {
|
|
4208
|
+
default: "h-9",
|
|
4209
|
+
sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
|
|
4210
|
+
}
|
|
4211
|
+
},
|
|
4212
|
+
defaultVariants: {
|
|
4213
|
+
variant: "default",
|
|
4214
|
+
size: "default"
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
);
|
|
4218
|
+
var Button = React3.forwardRef(
|
|
4219
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
4220
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
4221
|
+
return /* @__PURE__ */ jsx5(
|
|
4222
|
+
Comp,
|
|
4223
|
+
{
|
|
4224
|
+
ref,
|
|
4225
|
+
"data-slot": "button",
|
|
4226
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
4227
|
+
...props
|
|
4228
|
+
}
|
|
4229
|
+
);
|
|
4230
|
+
}
|
|
4231
|
+
);
|
|
4232
|
+
Button.displayName = "Button";
|
|
4233
|
+
|
|
4234
|
+
// src/ui/icons.tsx
|
|
4235
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4236
|
+
function IconX({ className, ...props }) {
|
|
4237
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4238
|
+
/* @__PURE__ */ jsx6("path", { d: "M18 6 6 18" }),
|
|
4239
|
+
/* @__PURE__ */ jsx6("path", { d: "m6 6 12 12" })
|
|
4240
|
+
] });
|
|
4241
|
+
}
|
|
4242
|
+
function IconChevronDown({ className, ...props }) {
|
|
4243
|
+
return /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
|
|
4244
|
+
}
|
|
4245
|
+
function IconFile({ className, ...props }) {
|
|
4246
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4247
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4248
|
+
/* @__PURE__ */ jsx6("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4249
|
+
] });
|
|
4250
|
+
}
|
|
4251
|
+
function IconInfo({ className, ...props }) {
|
|
4252
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4253
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
|
|
4254
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 16v-4" }),
|
|
4255
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 8h.01" })
|
|
4256
|
+
] });
|
|
4257
|
+
}
|
|
4258
|
+
function IconArrowRight({ className, ...props }) {
|
|
4259
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4260
|
+
/* @__PURE__ */ jsx6("path", { d: "M5 12h14" }),
|
|
4261
|
+
/* @__PURE__ */ jsx6("path", { d: "m12 5 7 7-7 7" })
|
|
4262
|
+
] });
|
|
4263
|
+
}
|
|
4264
|
+
function IconSection({ className, ...props }) {
|
|
4265
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
|
|
4266
|
+
/* @__PURE__ */ jsx6("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
4267
|
+
/* @__PURE__ */ jsx6("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
4268
|
+
/* @__PURE__ */ jsx6("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
4269
|
+
] });
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
4273
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4274
|
+
function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
|
|
4275
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2", children: [
|
|
4276
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
|
|
4277
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
4278
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
4279
|
+
/* @__PURE__ */ jsx7(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4280
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
|
|
4281
|
+
] }),
|
|
4282
|
+
/* @__PURE__ */ jsx7(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
4283
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
4284
|
+
/* @__PURE__ */ jsx7(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4285
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
4286
|
+
] })
|
|
4287
|
+
] })
|
|
4288
|
+
] });
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
// src/ui/link-modal/SectionTreeItem.tsx
|
|
4292
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4293
|
+
function SectionTreeItem({ section, onSelect, selected }) {
|
|
4294
|
+
const interactive = Boolean(onSelect);
|
|
4295
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
4296
|
+
/* @__PURE__ */ jsx8(
|
|
4297
|
+
"div",
|
|
4298
|
+
{
|
|
4299
|
+
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
4300
|
+
"aria-hidden": true
|
|
4301
|
+
}
|
|
4302
|
+
),
|
|
4303
|
+
/* @__PURE__ */ jsxs5(
|
|
4304
|
+
"div",
|
|
4305
|
+
{
|
|
4306
|
+
role: interactive ? "button" : void 0,
|
|
4307
|
+
tabIndex: interactive ? 0 : void 0,
|
|
4308
|
+
onClick: interactive ? () => onSelect?.(section) : void 0,
|
|
4309
|
+
onKeyDown: interactive ? (e) => {
|
|
4310
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4311
|
+
e.preventDefault();
|
|
4312
|
+
onSelect?.(section);
|
|
4313
|
+
}
|
|
4314
|
+
} : void 0,
|
|
4315
|
+
className: cn(
|
|
4316
|
+
"flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
|
|
4317
|
+
interactive && "cursor-pointer hover:bg-muted/30",
|
|
4318
|
+
interactive && selected && "border-primary"
|
|
4319
|
+
),
|
|
4320
|
+
children: [
|
|
4321
|
+
/* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
4322
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
4323
|
+
]
|
|
4324
|
+
}
|
|
4325
|
+
)
|
|
4326
|
+
] });
|
|
4327
|
+
}
|
|
4328
|
+
function SectionPickerList({ sections, onSelect }) {
|
|
4329
|
+
if (sections.length === 0) {
|
|
4330
|
+
return /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
|
|
4331
|
+
}
|
|
4332
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
4333
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
|
|
4334
|
+
sections.map((section) => /* @__PURE__ */ jsx8(SectionTreeItem, { section, onSelect }, section.id))
|
|
4335
|
+
] });
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4339
|
+
import { useEffect as useEffect2, useId, useRef as useRef2, useState as useState3 } from "react";
|
|
4340
|
+
|
|
4341
|
+
// src/ui/input.tsx
|
|
4342
|
+
import * as React4 from "react";
|
|
4343
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4344
|
+
var Input = React4.forwardRef(
|
|
4345
|
+
({ className, type, ...props }, ref) => {
|
|
4346
|
+
return /* @__PURE__ */ jsx9(
|
|
4347
|
+
"input",
|
|
4348
|
+
{
|
|
4349
|
+
type,
|
|
4350
|
+
ref,
|
|
4351
|
+
"data-slot": "input",
|
|
4352
|
+
className: cn(
|
|
4353
|
+
"flex h-full w-full min-w-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground",
|
|
4354
|
+
className
|
|
4355
|
+
),
|
|
4356
|
+
...props
|
|
4357
|
+
}
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
);
|
|
4361
|
+
Input.displayName = "Input";
|
|
4362
|
+
|
|
4363
|
+
// src/ui/label.tsx
|
|
4364
|
+
import { Label as LabelPrimitive } from "radix-ui";
|
|
4365
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4366
|
+
function Label({ className, ...props }) {
|
|
4367
|
+
return /* @__PURE__ */ jsx10(
|
|
4368
|
+
LabelPrimitive.Root,
|
|
4369
|
+
{
|
|
4370
|
+
"data-slot": "label",
|
|
4371
|
+
className: cn("text-sm font-medium leading-5 text-foreground", className),
|
|
4372
|
+
...props
|
|
4373
|
+
}
|
|
4374
|
+
);
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
// src/ui/popover.tsx
|
|
4378
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
4379
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
4380
|
+
function Popover({ ...props }) {
|
|
4381
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
4382
|
+
}
|
|
4383
|
+
function PopoverTrigger({ ...props }) {
|
|
4384
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4385
|
+
}
|
|
4386
|
+
function PopoverContent({
|
|
4387
|
+
className,
|
|
4388
|
+
align = "start",
|
|
4389
|
+
sideOffset = 6,
|
|
4390
|
+
...props
|
|
4391
|
+
}) {
|
|
4392
|
+
return /* @__PURE__ */ jsx11(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
|
|
4393
|
+
PopoverPrimitive.Content,
|
|
4394
|
+
{
|
|
4395
|
+
"data-slot": "popover-content",
|
|
4396
|
+
align,
|
|
4397
|
+
sideOffset,
|
|
4398
|
+
className: cn(
|
|
4399
|
+
"z-[2147483647] w-[var(--radix-popover-trigger-width)] overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg outline-none",
|
|
4400
|
+
className
|
|
4401
|
+
),
|
|
4402
|
+
...props
|
|
4403
|
+
}
|
|
4404
|
+
) });
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
4408
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4409
|
+
function FieldChevron({ onClick }) {
|
|
4410
|
+
return /* @__PURE__ */ jsx12(
|
|
4411
|
+
"button",
|
|
4412
|
+
{
|
|
4413
|
+
type: "button",
|
|
4414
|
+
className: "flex shrink-0 items-center justify-end border-0 bg-transparent p-0 pl-4 text-muted-foreground outline-none",
|
|
4415
|
+
onClick,
|
|
4416
|
+
"aria-label": "Open page list",
|
|
4417
|
+
tabIndex: -1,
|
|
4418
|
+
children: /* @__PURE__ */ jsx12(IconChevronDown, {})
|
|
4419
|
+
}
|
|
4420
|
+
);
|
|
4421
|
+
}
|
|
4422
|
+
function UrlOrPageInput({
|
|
4423
|
+
value,
|
|
4424
|
+
selectedPage,
|
|
4425
|
+
placeholder = "Paste a URL or choose a page",
|
|
4426
|
+
dropdownOpen,
|
|
4427
|
+
onDropdownOpenChange,
|
|
4428
|
+
filteredPages,
|
|
4429
|
+
onInputChange,
|
|
4430
|
+
onPageSelect,
|
|
4431
|
+
readOnly = false,
|
|
4432
|
+
urlError
|
|
4433
|
+
}) {
|
|
4434
|
+
const inputId = useId();
|
|
4435
|
+
const inputRef = useRef2(null);
|
|
4436
|
+
const [isEditing, setIsEditing] = useState3(false);
|
|
4437
|
+
const [isFocused, setIsFocused] = useState3(false);
|
|
4438
|
+
const showFilledDisplay = Boolean(selectedPage) && !isEditing && !readOnly;
|
|
4439
|
+
useEffect2(() => {
|
|
4440
|
+
if (selectedPage) setIsEditing(false);
|
|
4441
|
+
}, [selectedPage]);
|
|
4442
|
+
const openDropdown = () => {
|
|
4443
|
+
if (readOnly || filteredPages.length === 0) return;
|
|
4444
|
+
onDropdownOpenChange(true);
|
|
4445
|
+
};
|
|
4446
|
+
const toggleDropdown = (e) => {
|
|
4447
|
+
e.stopPropagation();
|
|
4448
|
+
e.preventDefault();
|
|
4449
|
+
if (readOnly || filteredPages.length === 0) return;
|
|
4450
|
+
onDropdownOpenChange(!dropdownOpen);
|
|
4451
|
+
};
|
|
4452
|
+
const enterEditMode = () => {
|
|
4453
|
+
if (readOnly) return;
|
|
4454
|
+
setIsEditing(true);
|
|
4455
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4456
|
+
};
|
|
4457
|
+
const fieldClassName = cn(
|
|
4458
|
+
"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]",
|
|
4459
|
+
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
4460
|
+
);
|
|
4461
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
4462
|
+
/* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
4463
|
+
/* @__PURE__ */ jsxs6(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
|
|
4464
|
+
/* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: showFilledDisplay ? /* @__PURE__ */ jsxs6(
|
|
4465
|
+
"button",
|
|
4466
|
+
{
|
|
4467
|
+
type: "button",
|
|
4468
|
+
"data-ohw-link-field": true,
|
|
4469
|
+
className: cn(fieldClassName, "m-0 cursor-pointer border-solid text-left"),
|
|
4470
|
+
onClick: openDropdown,
|
|
4471
|
+
onFocus: () => setIsFocused(true),
|
|
4472
|
+
onBlur: () => setIsFocused(false),
|
|
4473
|
+
onKeyDown: (e) => {
|
|
4474
|
+
if (e.key === "Backspace" || e.key === "Delete" || e.key.length === 1) {
|
|
4475
|
+
enterEditMode();
|
|
4476
|
+
}
|
|
4477
|
+
},
|
|
4478
|
+
children: [
|
|
4479
|
+
/* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }),
|
|
4480
|
+
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm font-normal text-foreground", children: selectedPage.title }),
|
|
4481
|
+
/* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown })
|
|
4482
|
+
]
|
|
4483
|
+
}
|
|
4484
|
+
) : /* @__PURE__ */ jsxs6(
|
|
4485
|
+
"div",
|
|
4486
|
+
{
|
|
4487
|
+
"data-ohw-link-field": true,
|
|
4488
|
+
className: fieldClassName,
|
|
4489
|
+
onMouseDown: (e) => {
|
|
4490
|
+
if (readOnly) return;
|
|
4491
|
+
if (e.target === e.currentTarget) enterEditMode();
|
|
4492
|
+
},
|
|
4493
|
+
children: [
|
|
4494
|
+
selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
|
|
4495
|
+
readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
|
|
4496
|
+
Input,
|
|
4497
|
+
{
|
|
4498
|
+
ref: inputRef,
|
|
4499
|
+
id: inputId,
|
|
4500
|
+
value,
|
|
4501
|
+
onChange: (e) => onInputChange(e.target.value),
|
|
4502
|
+
onFocus: () => {
|
|
4503
|
+
setIsFocused(true);
|
|
4504
|
+
setIsEditing(true);
|
|
4505
|
+
openDropdown();
|
|
4506
|
+
},
|
|
4507
|
+
onBlur: () => setIsFocused(false),
|
|
4508
|
+
placeholder,
|
|
4509
|
+
className: cn(
|
|
4510
|
+
"min-w-0 flex-1 truncate border-0 bg-transparent p-0 text-sm font-normal leading-5 shadow-none outline-none ring-0",
|
|
4511
|
+
value ? "text-foreground" : "text-muted-foreground placeholder:text-muted-foreground"
|
|
4512
|
+
)
|
|
4513
|
+
}
|
|
4514
|
+
),
|
|
4515
|
+
!readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
|
|
4516
|
+
]
|
|
4517
|
+
}
|
|
4518
|
+
) }),
|
|
4519
|
+
/* @__PURE__ */ jsx12(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs6(
|
|
4520
|
+
"button",
|
|
4521
|
+
{
|
|
4522
|
+
type: "button",
|
|
4523
|
+
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",
|
|
4524
|
+
onClick: () => onPageSelect(page),
|
|
4525
|
+
children: [
|
|
4526
|
+
/* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
|
|
4527
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
|
|
4528
|
+
]
|
|
4529
|
+
},
|
|
4530
|
+
page.path
|
|
4531
|
+
)) })
|
|
4532
|
+
] }),
|
|
4533
|
+
urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
4534
|
+
] });
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
// src/ui/link-modal/useLinkModalState.ts
|
|
4538
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
|
|
4539
|
+
|
|
4540
|
+
// src/ui/link-modal/linkModal.utils.ts
|
|
4541
|
+
function parseTarget(target) {
|
|
4542
|
+
if (!target) return { pageRoute: "", sectionId: null };
|
|
4543
|
+
const hashIndex = target.indexOf("#");
|
|
4544
|
+
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
4545
|
+
return {
|
|
4546
|
+
pageRoute: target.slice(0, hashIndex),
|
|
4547
|
+
sectionId: target.slice(hashIndex + 1) || null
|
|
4548
|
+
};
|
|
4549
|
+
}
|
|
4550
|
+
function normalizePath(path) {
|
|
4551
|
+
const trimmed = path.trim();
|
|
4552
|
+
if (!trimmed) return "/";
|
|
4553
|
+
try {
|
|
4554
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
4555
|
+
return new URL(trimmed).pathname || "/";
|
|
4556
|
+
}
|
|
4557
|
+
} catch {
|
|
4558
|
+
}
|
|
4559
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
4560
|
+
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
4561
|
+
return withSlash;
|
|
4562
|
+
}
|
|
4563
|
+
function findPageByPath(pages, path) {
|
|
4564
|
+
const normalized = normalizePath(path);
|
|
4565
|
+
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
4566
|
+
}
|
|
4567
|
+
function inferPageTitleFromPath(path) {
|
|
4568
|
+
const normalized = normalizePath(path);
|
|
4569
|
+
if (normalized === "/") return "Home";
|
|
4570
|
+
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
4571
|
+
if (!segment) return "Home";
|
|
4572
|
+
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
4573
|
+
}
|
|
4574
|
+
function isInternalPath(route) {
|
|
4575
|
+
const trimmed = route.trim();
|
|
4576
|
+
if (!trimmed) return false;
|
|
4577
|
+
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
4578
|
+
if (trimmed.startsWith("/")) return true;
|
|
4579
|
+
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
4580
|
+
return true;
|
|
4581
|
+
}
|
|
4582
|
+
function resolvePage(route, pages) {
|
|
4583
|
+
const found = findPageByPath(pages, route);
|
|
4584
|
+
if (found) return { path: found.path, title: found.title };
|
|
4585
|
+
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
4586
|
+
}
|
|
4587
|
+
function getSectionsForPath(sectionsByPath, pageRoute, fallback) {
|
|
4588
|
+
if (!sectionsByPath) return fallback;
|
|
4589
|
+
const normalized = normalizePath(pageRoute);
|
|
4590
|
+
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? fallback;
|
|
4591
|
+
}
|
|
4592
|
+
function buildTarget(page, section) {
|
|
4593
|
+
if (section) return `${page.path}#${section.id}`;
|
|
4594
|
+
return page.path;
|
|
4595
|
+
}
|
|
4596
|
+
function isValidUrl(urlString) {
|
|
4597
|
+
try {
|
|
4598
|
+
const url = new URL(urlString);
|
|
4599
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
4600
|
+
const hostname = url.hostname;
|
|
4601
|
+
if (!hostname || hostname.length === 0) return false;
|
|
4602
|
+
if (hostname.includes(" ")) return false;
|
|
4603
|
+
return hostname.includes(".") || hostname === "localhost";
|
|
4604
|
+
} catch {
|
|
4605
|
+
return false;
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
function normalizeUrl(value) {
|
|
4609
|
+
const trimmed = value.trim();
|
|
4610
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
4611
|
+
return `https://${trimmed}`;
|
|
4612
|
+
}
|
|
4613
|
+
function validateUrlInput(value, pages, allPages) {
|
|
4614
|
+
if (!value.trim()) return "";
|
|
4615
|
+
const filtered = pages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase()));
|
|
4616
|
+
if (filtered.length > 0) return "";
|
|
4617
|
+
const matchingPage = allPages.find((p) => p.title.toLowerCase() === value.toLowerCase());
|
|
4618
|
+
if (matchingPage) return "";
|
|
4619
|
+
if (!isValidUrl(normalizeUrl(value))) {
|
|
4620
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
4621
|
+
}
|
|
4622
|
+
return "";
|
|
4623
|
+
}
|
|
4624
|
+
function getEditModeInitialState(target, pages, sections) {
|
|
4625
|
+
const empty = {
|
|
4626
|
+
searchValue: "",
|
|
4627
|
+
selectedPage: null,
|
|
4628
|
+
selectedSection: null,
|
|
4629
|
+
step: "input"
|
|
4630
|
+
};
|
|
4631
|
+
if (!target) return empty;
|
|
4632
|
+
if (isValidUrl(normalizeUrl(target))) {
|
|
4633
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4634
|
+
}
|
|
4635
|
+
const { pageRoute, sectionId } = parseTarget(target);
|
|
4636
|
+
if (sectionId) {
|
|
4637
|
+
if (isInternalPath(pageRoute)) {
|
|
4638
|
+
const page = resolvePage(pageRoute, pages);
|
|
4639
|
+
const sec = sections.find((s) => s.id === sectionId);
|
|
4640
|
+
return {
|
|
4641
|
+
searchValue: page.title,
|
|
4642
|
+
selectedPage: page,
|
|
4643
|
+
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
4644
|
+
step: "input"
|
|
4645
|
+
};
|
|
4646
|
+
}
|
|
4647
|
+
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4648
|
+
}
|
|
4649
|
+
if (isInternalPath(pageRoute)) {
|
|
4650
|
+
const page = resolvePage(pageRoute, pages);
|
|
4651
|
+
return {
|
|
4652
|
+
searchValue: page.title,
|
|
4653
|
+
selectedPage: page,
|
|
4654
|
+
selectedSection: null,
|
|
4655
|
+
step: "input"
|
|
4656
|
+
};
|
|
4657
|
+
}
|
|
4658
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
4659
|
+
}
|
|
4660
|
+
function filterAvailablePages(pages, existingTargets) {
|
|
4661
|
+
const taken = new Set(existingTargets);
|
|
4662
|
+
return pages.filter((p) => !taken.has(p.path));
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4665
|
+
// src/ui/link-modal/useLinkModalState.ts
|
|
4666
|
+
function useLinkModalState({
|
|
4667
|
+
open,
|
|
4668
|
+
mode,
|
|
4669
|
+
pages,
|
|
4670
|
+
sections,
|
|
4671
|
+
sectionsByPath,
|
|
4672
|
+
initialTarget,
|
|
4673
|
+
existingTargets,
|
|
4674
|
+
onClose,
|
|
4675
|
+
onSubmit
|
|
4676
|
+
}) {
|
|
4677
|
+
const availablePages = useMemo2(
|
|
4678
|
+
() => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
|
|
4679
|
+
[mode, pages, existingTargets]
|
|
4680
|
+
);
|
|
4681
|
+
const [searchValue, setSearchValue] = useState4("");
|
|
4682
|
+
const [selectedPage, setSelectedPage] = useState4(null);
|
|
4683
|
+
const [selectedSection, setSelectedSection] = useState4(null);
|
|
4684
|
+
const [step, setStep] = useState4("input");
|
|
4685
|
+
const [dropdownOpen, setDropdownOpen] = useState4(false);
|
|
4686
|
+
const [urlError, setUrlError] = useState4("");
|
|
4687
|
+
const reset = useCallback(() => {
|
|
4688
|
+
setSearchValue("");
|
|
4689
|
+
setSelectedPage(null);
|
|
4690
|
+
setSelectedSection(null);
|
|
4691
|
+
setStep("input");
|
|
4692
|
+
setDropdownOpen(false);
|
|
4693
|
+
setUrlError("");
|
|
4694
|
+
}, []);
|
|
4695
|
+
useEffect3(() => {
|
|
4696
|
+
if (!open) return;
|
|
4697
|
+
if (mode === "edit" && initialTarget) {
|
|
4698
|
+
const { pageRoute } = parseTarget(initialTarget);
|
|
4699
|
+
const targetSections = getSectionsForPath(sectionsByPath, pageRoute, sections);
|
|
4700
|
+
const init = getEditModeInitialState(initialTarget, pages, targetSections);
|
|
4701
|
+
setSearchValue(init.searchValue);
|
|
4702
|
+
setSelectedPage(init.selectedPage);
|
|
4703
|
+
setSelectedSection(init.selectedSection);
|
|
4704
|
+
setStep(init.step);
|
|
4705
|
+
} else {
|
|
4706
|
+
reset();
|
|
4707
|
+
}
|
|
4708
|
+
setDropdownOpen(false);
|
|
4709
|
+
setUrlError("");
|
|
4710
|
+
}, [open, mode, initialTarget, pages, sections, sectionsByPath, reset]);
|
|
4711
|
+
const filteredPages = useMemo2(() => {
|
|
4712
|
+
if (!searchValue.trim()) return availablePages;
|
|
4713
|
+
return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
|
|
4714
|
+
}, [availablePages, searchValue]);
|
|
4715
|
+
const activeSections = useMemo2(() => {
|
|
4716
|
+
if (selectedPage && sectionsByPath) {
|
|
4717
|
+
return getSectionsForPath(sectionsByPath, selectedPage.path, sections);
|
|
4718
|
+
}
|
|
4719
|
+
return sections;
|
|
4720
|
+
}, [selectedPage, sectionsByPath, sections]);
|
|
4721
|
+
const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
|
|
4722
|
+
const showSectionPicker = step === "sectionPicker" && selectedPage;
|
|
4723
|
+
const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
|
|
4724
|
+
const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow;
|
|
4725
|
+
const handleInputChange = (value) => {
|
|
4726
|
+
setSearchValue(value);
|
|
4727
|
+
setSelectedPage(null);
|
|
4728
|
+
setSelectedSection(null);
|
|
4729
|
+
setStep("input");
|
|
4730
|
+
setUrlError("");
|
|
4731
|
+
const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
|
|
4732
|
+
setDropdownOpen(filtered.length > 0);
|
|
4733
|
+
if (value.trim() && filtered.length === 0) {
|
|
4734
|
+
const error = validateUrlInput(value, availablePages, pages);
|
|
4735
|
+
if (error) setUrlError(error);
|
|
4736
|
+
}
|
|
4737
|
+
};
|
|
4738
|
+
const handlePageSelect = (page) => {
|
|
4739
|
+
setSelectedPage({ path: page.path, title: page.title });
|
|
4740
|
+
setSelectedSection(null);
|
|
4741
|
+
setSearchValue(page.title);
|
|
4742
|
+
setStep("input");
|
|
4743
|
+
setDropdownOpen(false);
|
|
4744
|
+
setUrlError("");
|
|
4745
|
+
};
|
|
4746
|
+
const handleChooseSection = () => {
|
|
4747
|
+
setStep("sectionPicker");
|
|
4748
|
+
};
|
|
4749
|
+
const handleSectionSelect = (section) => {
|
|
4750
|
+
setSelectedSection({ id: section.id, label: section.label });
|
|
4751
|
+
if (mode === "create") {
|
|
4752
|
+
setStep("confirmed");
|
|
4753
|
+
} else {
|
|
4754
|
+
setStep("input");
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
const handleBackToSections = () => {
|
|
4758
|
+
setStep("sectionPicker");
|
|
4759
|
+
};
|
|
4760
|
+
const handleClose = () => {
|
|
4761
|
+
reset();
|
|
4762
|
+
onClose();
|
|
4763
|
+
};
|
|
4764
|
+
const isValid = useMemo2(() => {
|
|
4765
|
+
if (urlError) return false;
|
|
4766
|
+
if (showBreadcrumb) return true;
|
|
4767
|
+
if (selectedPage) return true;
|
|
4768
|
+
if (!searchValue.trim()) return false;
|
|
4769
|
+
return validateUrlInput(searchValue, availablePages, pages) === "";
|
|
4770
|
+
}, [urlError, showBreadcrumb, selectedPage, searchValue, availablePages, pages]);
|
|
4771
|
+
const handleSubmit = () => {
|
|
4772
|
+
if (selectedPage && selectedSection) {
|
|
4773
|
+
onSubmit(buildTarget(selectedPage, selectedSection));
|
|
4774
|
+
handleClose();
|
|
4775
|
+
return;
|
|
4776
|
+
}
|
|
4777
|
+
if (selectedPage) {
|
|
4778
|
+
onSubmit(buildTarget(selectedPage, null));
|
|
4779
|
+
handleClose();
|
|
4780
|
+
return;
|
|
4781
|
+
}
|
|
4782
|
+
if (!searchValue.trim()) return;
|
|
4783
|
+
const error = validateUrlInput(searchValue, availablePages, pages);
|
|
4784
|
+
if (error) {
|
|
4785
|
+
setUrlError(error);
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
const matchingPage = pages.find((p) => p.title.toLowerCase() === searchValue.toLowerCase());
|
|
4789
|
+
if (matchingPage) {
|
|
4790
|
+
onSubmit(matchingPage.path);
|
|
4791
|
+
handleClose();
|
|
4792
|
+
return;
|
|
4793
|
+
}
|
|
4794
|
+
onSubmit(normalizeUrl(searchValue));
|
|
4795
|
+
handleClose();
|
|
4796
|
+
};
|
|
4797
|
+
const submitLabel = mode === "edit" ? "Save" : "Create";
|
|
4798
|
+
const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
|
|
4799
|
+
const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
|
|
4800
|
+
const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
|
|
4801
|
+
return {
|
|
4802
|
+
title,
|
|
4803
|
+
submitLabel,
|
|
4804
|
+
secondaryLabel,
|
|
4805
|
+
searchValue,
|
|
4806
|
+
selectedPage,
|
|
4807
|
+
selectedSection,
|
|
4808
|
+
step,
|
|
4809
|
+
dropdownOpen,
|
|
4810
|
+
setDropdownOpen,
|
|
4811
|
+
urlError,
|
|
4812
|
+
filteredPages,
|
|
4813
|
+
showBreadcrumb,
|
|
4814
|
+
showSectionPicker,
|
|
4815
|
+
showChooseSection,
|
|
4816
|
+
showSectionRow,
|
|
4817
|
+
isValid,
|
|
4818
|
+
activeSections,
|
|
4819
|
+
handleInputChange,
|
|
4820
|
+
handlePageSelect,
|
|
4821
|
+
handleChooseSection,
|
|
4822
|
+
handleSectionSelect,
|
|
4823
|
+
handleBackToSections,
|
|
4824
|
+
handleClose,
|
|
4825
|
+
handleSecondary,
|
|
4826
|
+
handleSubmit
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
4831
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4832
|
+
function LinkEditorPanel({
|
|
4833
|
+
open = true,
|
|
4834
|
+
mode = "create",
|
|
4835
|
+
pages,
|
|
4836
|
+
sections = [],
|
|
4837
|
+
sectionsByPath,
|
|
4838
|
+
initialTarget,
|
|
4839
|
+
existingTargets = [],
|
|
4840
|
+
onClose,
|
|
4841
|
+
onSubmit
|
|
4842
|
+
}) {
|
|
4843
|
+
const state = useLinkModalState({
|
|
4844
|
+
open,
|
|
4845
|
+
mode,
|
|
4846
|
+
pages,
|
|
4847
|
+
sections,
|
|
4848
|
+
sectionsByPath,
|
|
4849
|
+
initialTarget,
|
|
4850
|
+
existingTargets,
|
|
4851
|
+
onClose,
|
|
4852
|
+
onSubmit
|
|
4853
|
+
});
|
|
4854
|
+
const isCancel = state.secondaryLabel === "Cancel";
|
|
4855
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
4856
|
+
/* @__PURE__ */ jsx13(
|
|
4857
|
+
"button",
|
|
4858
|
+
{
|
|
4859
|
+
type: "button",
|
|
4860
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
4861
|
+
onClick: state.handleClose,
|
|
4862
|
+
"aria-label": "Close",
|
|
4863
|
+
children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
|
|
4864
|
+
}
|
|
4865
|
+
),
|
|
4866
|
+
/* @__PURE__ */ jsx13("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
|
|
4867
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
4868
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
|
|
4869
|
+
DestinationBreadcrumb,
|
|
4870
|
+
{
|
|
4871
|
+
pageTitle: state.selectedPage.title,
|
|
4872
|
+
sectionLabel: state.selectedSection.label
|
|
4873
|
+
}
|
|
4874
|
+
) : /* @__PURE__ */ jsx13(
|
|
4875
|
+
UrlOrPageInput,
|
|
4876
|
+
{
|
|
4877
|
+
value: state.searchValue,
|
|
4878
|
+
selectedPage: state.selectedPage,
|
|
4879
|
+
dropdownOpen: state.dropdownOpen,
|
|
4880
|
+
onDropdownOpenChange: state.setDropdownOpen,
|
|
4881
|
+
filteredPages: state.filteredPages,
|
|
4882
|
+
onInputChange: state.handleInputChange,
|
|
4883
|
+
onPageSelect: state.handlePageSelect,
|
|
4884
|
+
urlError: state.urlError
|
|
4885
|
+
}
|
|
4886
|
+
),
|
|
4887
|
+
state.showChooseSection ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
4888
|
+
/* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
|
|
4889
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
4890
|
+
/* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
|
|
4891
|
+
/* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
|
|
4892
|
+
] })
|
|
4893
|
+
] }) : null,
|
|
4894
|
+
state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
|
|
4895
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
4896
|
+
] }),
|
|
4897
|
+
/* @__PURE__ */ jsxs7("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
|
|
4898
|
+
/* @__PURE__ */ jsx13(
|
|
4899
|
+
Button,
|
|
4900
|
+
{
|
|
4901
|
+
type: "button",
|
|
4902
|
+
variant: isCancel ? "outline" : "ghost",
|
|
4903
|
+
className: "leading-6 shadow-none cursor-pointer",
|
|
4904
|
+
style: isCancel ? {
|
|
4905
|
+
backgroundColor: "var(--ohw-background)",
|
|
4906
|
+
borderColor: "var(--ohw-border)",
|
|
4907
|
+
color: "var(--ohw-foreground)"
|
|
4908
|
+
} : void 0,
|
|
4909
|
+
onClick: state.handleSecondary,
|
|
4910
|
+
children: state.secondaryLabel
|
|
4911
|
+
}
|
|
4912
|
+
),
|
|
4913
|
+
/* @__PURE__ */ jsx13(
|
|
4914
|
+
Button,
|
|
4915
|
+
{
|
|
4916
|
+
type: "button",
|
|
4917
|
+
className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
|
|
4918
|
+
style: {
|
|
4919
|
+
backgroundColor: "var(--ohw-primary)",
|
|
4920
|
+
color: "var(--ohw-primary-foreground)"
|
|
4921
|
+
},
|
|
4922
|
+
disabled: !state.isValid,
|
|
4923
|
+
onClick: state.handleSubmit,
|
|
4924
|
+
children: state.submitLabel
|
|
4925
|
+
}
|
|
4926
|
+
)
|
|
4927
|
+
] })
|
|
4928
|
+
] });
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
// src/ui/link-modal/LinkPopover.tsx
|
|
4932
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4933
|
+
function LinkPopover({
|
|
4934
|
+
rect,
|
|
4935
|
+
parentScroll,
|
|
4936
|
+
panelRef,
|
|
4937
|
+
...editorProps
|
|
4938
|
+
}) {
|
|
4939
|
+
const { top, left, transform } = calcPopoverPos(rect, parentScroll);
|
|
4940
|
+
return /* @__PURE__ */ jsx14(
|
|
4941
|
+
"div",
|
|
4942
|
+
{
|
|
4943
|
+
ref: panelRef,
|
|
4944
|
+
"data-ohw-link-popover-root": "",
|
|
4945
|
+
"data-ohw-link-modal-root": "",
|
|
4946
|
+
"data-ohw-bridge": "",
|
|
4947
|
+
className: cn(
|
|
4948
|
+
"pointer-events-auto fixed z-[2147483647] flex w-[483px] max-w-[calc(100vw-16px)] flex-col overflow-hidden",
|
|
4949
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none"
|
|
4950
|
+
),
|
|
4951
|
+
style: { top, left, transform },
|
|
4952
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
4953
|
+
children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open: true })
|
|
4954
|
+
}
|
|
4955
|
+
);
|
|
4956
|
+
}
|
|
4957
|
+
|
|
4958
|
+
// src/ui/link-modal/devFixtures.ts
|
|
4959
|
+
var DEV_SITE_PAGES = [
|
|
4960
|
+
{ path: "/", title: "Home" },
|
|
4961
|
+
{ path: "/about", title: "About" },
|
|
4962
|
+
{ path: "/classes", title: "Classes" },
|
|
4963
|
+
{ path: "/pricing", title: "Pricing" },
|
|
4964
|
+
{ path: "/studio", title: "Studio" },
|
|
4965
|
+
{ path: "/instructors", title: "Instructors" },
|
|
4966
|
+
{ path: "/contact", title: "Contact" }
|
|
4967
|
+
];
|
|
4968
|
+
var DEV_SECTIONS_BY_PATH = {
|
|
4969
|
+
"/": [
|
|
4970
|
+
{ id: "hero", label: "Hero" },
|
|
4971
|
+
{ id: "lagree-intro", label: "Lagree Intro" },
|
|
4972
|
+
{ id: "classes-strip", label: "Classes" },
|
|
4973
|
+
{ id: "feel-split", label: "Feel Split" },
|
|
4974
|
+
{ id: "founder-teaser", label: "Founder" },
|
|
4975
|
+
{ id: "plan-form", label: "Plan Form" },
|
|
4976
|
+
{ id: "testimonials", label: "Testimonials" },
|
|
4977
|
+
{ id: "wordmark", label: "Wordmark" }
|
|
4978
|
+
],
|
|
4979
|
+
"/about": [
|
|
4980
|
+
{ id: "manifesto", label: "Manifesto" },
|
|
4981
|
+
{ id: "story-letter", label: "Our Story" },
|
|
4982
|
+
{ id: "lagree-explainer", label: "Lagree Explainer" },
|
|
4983
|
+
{ id: "waitlist-cta", label: "Waitlist" },
|
|
4984
|
+
{ id: "personal-training", label: "Personal training" }
|
|
4985
|
+
],
|
|
4986
|
+
"/classes": [{ id: "class-library", label: "Class Library" }],
|
|
4987
|
+
"/pricing": [
|
|
4988
|
+
{ id: "benefits-marquee", label: "Benefits" },
|
|
4989
|
+
{ id: "monthly-memberships", label: "Memberships" },
|
|
4990
|
+
{ id: "package-matcher", label: "Packages" },
|
|
4991
|
+
{ id: "specialty-programs", label: "Specialty Programs" },
|
|
4992
|
+
{ id: "free-class-cta", label: "Free Class" }
|
|
4993
|
+
],
|
|
4994
|
+
"/studio": [
|
|
4995
|
+
{ id: "studio-intro", label: "Studio Intro" },
|
|
4996
|
+
{ id: "studio-features", label: "Studio Features" },
|
|
4997
|
+
{ id: "studio-gallery", label: "Studio Gallery" },
|
|
4998
|
+
{ id: "studio-visit", label: "Visit Studio" }
|
|
4999
|
+
],
|
|
5000
|
+
"/instructors": [{ id: "instructor-grid", label: "Instructors" }],
|
|
5001
|
+
"/contact": [
|
|
5002
|
+
{ id: "hello-hero", label: "Hello" },
|
|
5003
|
+
{ id: "contact-form", label: "Contact Form" },
|
|
5004
|
+
{ id: "faq", label: "FAQ" }
|
|
5005
|
+
]
|
|
5006
|
+
};
|
|
5007
|
+
function shouldUseDevFixtures() {
|
|
5008
|
+
if (typeof window === "undefined") return false;
|
|
5009
|
+
const raw = readPreservedSearch();
|
|
5010
|
+
const q = raw.startsWith("?") ? raw.slice(1) : raw;
|
|
5011
|
+
return new URLSearchParams(q).get("ohw-fixtures") === "1";
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// src/ui/badge.tsx
|
|
5015
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4184
5016
|
var badgeVariants = cva(
|
|
4185
5017
|
"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",
|
|
4186
5018
|
{
|
|
@@ -4198,11 +5030,11 @@ var badgeVariants = cva(
|
|
|
4198
5030
|
}
|
|
4199
5031
|
);
|
|
4200
5032
|
function Badge({ className, variant, ...props }) {
|
|
4201
|
-
return /* @__PURE__ */
|
|
5033
|
+
return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
4202
5034
|
}
|
|
4203
5035
|
|
|
4204
5036
|
// src/OhhwellsBridge.tsx
|
|
4205
|
-
import { Fragment as
|
|
5037
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4206
5038
|
var PRIMARY = "#0885FE";
|
|
4207
5039
|
var IMAGE_FADE_MS = 300;
|
|
4208
5040
|
function runOpacityFade(el, onDone) {
|
|
@@ -4342,10 +5174,9 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
4342
5174
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4343
5175
|
if (inEditor) getSectionsTracker().textContent = raw;
|
|
4344
5176
|
const pageEntries = getPageSchedulingEntries(raw);
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
5177
|
+
mountSchedulingEntries(pageEntries, false);
|
|
5178
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
|
|
5179
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
|
|
4349
5180
|
} catch {
|
|
4350
5181
|
}
|
|
4351
5182
|
}
|
|
@@ -4372,7 +5203,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
4372
5203
|
const root = createRoot(container);
|
|
4373
5204
|
flushSync(() => {
|
|
4374
5205
|
root.render(
|
|
4375
|
-
/* @__PURE__ */
|
|
5206
|
+
/* @__PURE__ */ jsx16(
|
|
4376
5207
|
SchedulingWidget,
|
|
4377
5208
|
{
|
|
4378
5209
|
notifyOnConnect,
|
|
@@ -4404,16 +5235,23 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
4404
5235
|
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
4405
5236
|
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
4406
5237
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4407
|
-
const
|
|
4408
|
-
|
|
4409
|
-
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
5238
|
+
const { insertAfter, scheduleId } = pending[i];
|
|
5239
|
+
if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
|
|
4410
5240
|
pending.splice(i, 1);
|
|
4411
5241
|
}
|
|
4412
5242
|
}
|
|
4413
5243
|
}
|
|
4414
5244
|
}
|
|
5245
|
+
function getLinkHref(el) {
|
|
5246
|
+
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5247
|
+
return anchor?.getAttribute("href") ?? "";
|
|
5248
|
+
}
|
|
5249
|
+
function applyLinkHref(el, val) {
|
|
5250
|
+
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
5251
|
+
if (anchor) anchor.setAttribute("href", val);
|
|
5252
|
+
}
|
|
4415
5253
|
function collectEditableNodes() {
|
|
4416
|
-
|
|
5254
|
+
const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
|
|
4417
5255
|
if (el.dataset.ohwEditable === "image") {
|
|
4418
5256
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
4419
5257
|
return { key: el.dataset.ohwKey ?? "", type: "image", text: img?.src ?? "" };
|
|
@@ -4423,12 +5261,52 @@ function collectEditableNodes() {
|
|
|
4423
5261
|
const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
|
|
4424
5262
|
return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
|
|
4425
5263
|
}
|
|
5264
|
+
if (el.dataset.ohwEditable === "link") {
|
|
5265
|
+
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
|
|
5266
|
+
}
|
|
4426
5267
|
return {
|
|
4427
5268
|
key: el.dataset.ohwKey ?? "",
|
|
4428
5269
|
type: el.dataset.ohwEditable ?? "text",
|
|
4429
5270
|
text: el.dataset.ohwEditable === "plain" ? el.innerText ?? "" : el.innerHTML
|
|
4430
5271
|
};
|
|
4431
5272
|
});
|
|
5273
|
+
document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
|
|
5274
|
+
const key = el.getAttribute("data-ohw-href-key") ?? "";
|
|
5275
|
+
if (!key) return;
|
|
5276
|
+
nodes.push({ key, type: "link", text: getLinkHref(el) });
|
|
5277
|
+
});
|
|
5278
|
+
return nodes;
|
|
5279
|
+
}
|
|
5280
|
+
function applyLinkByKey(key, val) {
|
|
5281
|
+
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
5282
|
+
if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
|
|
5283
|
+
});
|
|
5284
|
+
document.querySelectorAll(`[data-ohw-href-key="${key}"]`).forEach((el) => {
|
|
5285
|
+
applyLinkHref(el, val);
|
|
5286
|
+
});
|
|
5287
|
+
}
|
|
5288
|
+
function isInsideLinkEditor(target) {
|
|
5289
|
+
return Boolean(
|
|
5290
|
+
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest('[data-slot="popover-content"]')
|
|
5291
|
+
);
|
|
5292
|
+
}
|
|
5293
|
+
function getHrefKeyFromElement(el) {
|
|
5294
|
+
if (!el) return null;
|
|
5295
|
+
const anchor = el.closest("[data-ohw-href-key]");
|
|
5296
|
+
if (!anchor) return null;
|
|
5297
|
+
const key = anchor.getAttribute("data-ohw-href-key");
|
|
5298
|
+
if (!key) return null;
|
|
5299
|
+
return { anchor, key };
|
|
5300
|
+
}
|
|
5301
|
+
function titleCaseSectionId(id) {
|
|
5302
|
+
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
5303
|
+
}
|
|
5304
|
+
function collectSections() {
|
|
5305
|
+
return Array.from(document.querySelectorAll("[data-ohw-section]")).map((el) => {
|
|
5306
|
+
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
5307
|
+
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
5308
|
+
return { id, label };
|
|
5309
|
+
});
|
|
4432
5310
|
}
|
|
4433
5311
|
var FORCE_PSEUDO_STATES = [
|
|
4434
5312
|
{ pseudo: ":hover", state: "hover" },
|
|
@@ -4569,7 +5447,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4569
5447
|
];
|
|
4570
5448
|
function GlowFrame({ rect, elRef }) {
|
|
4571
5449
|
const GAP = 6;
|
|
4572
|
-
return /* @__PURE__ */
|
|
5450
|
+
return /* @__PURE__ */ jsx16(
|
|
4573
5451
|
"div",
|
|
4574
5452
|
{
|
|
4575
5453
|
ref: elRef,
|
|
@@ -4617,10 +5495,12 @@ function FloatingToolbar({
|
|
|
4617
5495
|
parentScroll,
|
|
4618
5496
|
elRef,
|
|
4619
5497
|
onCommand,
|
|
4620
|
-
activeCommands
|
|
5498
|
+
activeCommands,
|
|
5499
|
+
showEditLink,
|
|
5500
|
+
onEditLink
|
|
4621
5501
|
}) {
|
|
4622
5502
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
4623
|
-
return /* @__PURE__ */
|
|
5503
|
+
return /* @__PURE__ */ jsxs8(
|
|
4624
5504
|
"div",
|
|
4625
5505
|
{
|
|
4626
5506
|
ref: elRef,
|
|
@@ -4643,56 +5523,97 @@ function FloatingToolbar({
|
|
|
4643
5523
|
pointerEvents: "auto",
|
|
4644
5524
|
whiteSpace: "nowrap"
|
|
4645
5525
|
},
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
5526
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
5527
|
+
children: [
|
|
5528
|
+
TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs8(React5.Fragment, { children: [
|
|
5529
|
+
gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
5530
|
+
btns.map((btn) => {
|
|
5531
|
+
const isActive = activeCommands.has(btn.cmd);
|
|
5532
|
+
return /* @__PURE__ */ jsx16(
|
|
5533
|
+
"button",
|
|
5534
|
+
{
|
|
5535
|
+
title: btn.title,
|
|
5536
|
+
onMouseDown: (e) => {
|
|
5537
|
+
e.preventDefault();
|
|
5538
|
+
onCommand(btn.cmd);
|
|
5539
|
+
},
|
|
5540
|
+
onMouseEnter: (e) => {
|
|
5541
|
+
if (!isActive) e.currentTarget.style.background = "#F5F5F4";
|
|
5542
|
+
},
|
|
5543
|
+
onMouseLeave: (e) => {
|
|
5544
|
+
if (!isActive) e.currentTarget.style.background = "transparent";
|
|
5545
|
+
},
|
|
5546
|
+
style: {
|
|
5547
|
+
display: "flex",
|
|
5548
|
+
alignItems: "center",
|
|
5549
|
+
justifyContent: "center",
|
|
5550
|
+
border: "none",
|
|
5551
|
+
background: isActive ? PRIMARY : "transparent",
|
|
5552
|
+
borderRadius: 4,
|
|
5553
|
+
cursor: "pointer",
|
|
5554
|
+
color: isActive ? "#FFFFFF" : "#1C1917",
|
|
5555
|
+
flexShrink: 0,
|
|
5556
|
+
padding: 6
|
|
5557
|
+
},
|
|
5558
|
+
children: /* @__PURE__ */ jsx16(
|
|
5559
|
+
"svg",
|
|
5560
|
+
{
|
|
5561
|
+
width: "16",
|
|
5562
|
+
height: "16",
|
|
5563
|
+
viewBox: "0 0 24 24",
|
|
5564
|
+
fill: "none",
|
|
5565
|
+
stroke: isActive ? "#FFFFFF" : "#1C1917",
|
|
5566
|
+
strokeWidth: "2.5",
|
|
5567
|
+
strokeLinecap: "round",
|
|
5568
|
+
strokeLinejoin: "round",
|
|
5569
|
+
style: isActive && gi > 0 ? { filter: "drop-shadow(0 0 0.5px #fff)" } : void 0,
|
|
5570
|
+
dangerouslySetInnerHTML: { __html: ICONS[btn.cmd] }
|
|
5571
|
+
}
|
|
5572
|
+
)
|
|
4675
5573
|
},
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
5574
|
+
btn.cmd
|
|
5575
|
+
);
|
|
5576
|
+
})
|
|
5577
|
+
] }, gi)),
|
|
5578
|
+
showEditLink ? /* @__PURE__ */ jsx16(
|
|
5579
|
+
"button",
|
|
5580
|
+
{
|
|
5581
|
+
type: "button",
|
|
5582
|
+
title: "Edit link",
|
|
5583
|
+
onMouseDown: (e) => {
|
|
5584
|
+
e.preventDefault();
|
|
5585
|
+
e.stopPropagation();
|
|
5586
|
+
onEditLink?.();
|
|
5587
|
+
},
|
|
5588
|
+
onClick: (e) => {
|
|
5589
|
+
e.preventDefault();
|
|
5590
|
+
e.stopPropagation();
|
|
5591
|
+
},
|
|
5592
|
+
onMouseEnter: (e) => {
|
|
5593
|
+
e.currentTarget.style.background = "#F5F5F4";
|
|
5594
|
+
},
|
|
5595
|
+
onMouseLeave: (e) => {
|
|
5596
|
+
e.currentTarget.style.background = "transparent";
|
|
4691
5597
|
},
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
5598
|
+
style: {
|
|
5599
|
+
display: "flex",
|
|
5600
|
+
alignItems: "center",
|
|
5601
|
+
justifyContent: "center",
|
|
5602
|
+
border: "none",
|
|
5603
|
+
background: "transparent",
|
|
5604
|
+
borderRadius: 4,
|
|
5605
|
+
cursor: "pointer",
|
|
5606
|
+
color: "#1C1917",
|
|
5607
|
+
flexShrink: 0,
|
|
5608
|
+
padding: 6
|
|
5609
|
+
},
|
|
5610
|
+
children: /* @__PURE__ */ jsxs8("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: [
|
|
5611
|
+
/* @__PURE__ */ jsx16("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
5612
|
+
/* @__PURE__ */ jsx16("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
5613
|
+
] })
|
|
5614
|
+
}
|
|
5615
|
+
) : null
|
|
5616
|
+
]
|
|
4696
5617
|
}
|
|
4697
5618
|
);
|
|
4698
5619
|
}
|
|
@@ -4706,7 +5627,7 @@ function StateToggle({
|
|
|
4706
5627
|
states,
|
|
4707
5628
|
onStateChange
|
|
4708
5629
|
}) {
|
|
4709
|
-
return /* @__PURE__ */
|
|
5630
|
+
return /* @__PURE__ */ jsx16(
|
|
4710
5631
|
ToggleGroup,
|
|
4711
5632
|
{
|
|
4712
5633
|
"data-ohw-state-toggle": "",
|
|
@@ -4720,7 +5641,7 @@ function StateToggle({
|
|
|
4720
5641
|
left: rect.right - 8,
|
|
4721
5642
|
transform: "translateX(-100%)"
|
|
4722
5643
|
},
|
|
4723
|
-
children: states.map((state) => /* @__PURE__ */
|
|
5644
|
+
children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4724
5645
|
}
|
|
4725
5646
|
);
|
|
4726
5647
|
}
|
|
@@ -4747,8 +5668,8 @@ function OhhwellsBridge() {
|
|
|
4747
5668
|
const router = useRouter();
|
|
4748
5669
|
const searchParams = useSearchParams();
|
|
4749
5670
|
const isEditMode = isEditSessionActive();
|
|
4750
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
4751
|
-
|
|
5671
|
+
const [bridgeRoot, setBridgeRoot] = useState5(null);
|
|
5672
|
+
useEffect4(() => {
|
|
4752
5673
|
const figtreeFontId = "ohw-figtree-font";
|
|
4753
5674
|
if (!document.getElementById(figtreeFontId)) {
|
|
4754
5675
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -4772,39 +5693,75 @@ function OhhwellsBridge() {
|
|
|
4772
5693
|
}, []);
|
|
4773
5694
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
4774
5695
|
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
4775
|
-
const postToParent =
|
|
5696
|
+
const postToParent = useCallback2((data) => {
|
|
4776
5697
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4777
5698
|
window.parent.postMessage(data, "*");
|
|
4778
5699
|
}
|
|
4779
5700
|
}, []);
|
|
4780
|
-
const [fetchState, setFetchState] =
|
|
4781
|
-
const autoSaveTimers =
|
|
4782
|
-
const activeElRef =
|
|
4783
|
-
const originalContentRef =
|
|
4784
|
-
const activeStateElRef =
|
|
4785
|
-
const parentScrollRef =
|
|
4786
|
-
const toolbarElRef =
|
|
4787
|
-
const glowElRef =
|
|
4788
|
-
const hoveredImageRef =
|
|
4789
|
-
const hoveredImageHasTextOverlapRef =
|
|
4790
|
-
const hoveredGapRef =
|
|
4791
|
-
const imageUnhoverTimerRef =
|
|
4792
|
-
const imageShowTimerRef =
|
|
4793
|
-
const editStylesRef =
|
|
4794
|
-
const activateRef =
|
|
5701
|
+
const [fetchState, setFetchState] = useState5("idle");
|
|
5702
|
+
const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
|
|
5703
|
+
const activeElRef = useRef3(null);
|
|
5704
|
+
const originalContentRef = useRef3(null);
|
|
5705
|
+
const activeStateElRef = useRef3(null);
|
|
5706
|
+
const parentScrollRef = useRef3(null);
|
|
5707
|
+
const toolbarElRef = useRef3(null);
|
|
5708
|
+
const glowElRef = useRef3(null);
|
|
5709
|
+
const hoveredImageRef = useRef3(null);
|
|
5710
|
+
const hoveredImageHasTextOverlapRef = useRef3(false);
|
|
5711
|
+
const hoveredGapRef = useRef3(null);
|
|
5712
|
+
const imageUnhoverTimerRef = useRef3(null);
|
|
5713
|
+
const imageShowTimerRef = useRef3(null);
|
|
5714
|
+
const editStylesRef = useRef3(null);
|
|
5715
|
+
const activateRef = useRef3(() => {
|
|
4795
5716
|
});
|
|
4796
|
-
const deactivateRef =
|
|
5717
|
+
const deactivateRef = useRef3(() => {
|
|
4797
5718
|
});
|
|
4798
|
-
const refreshActiveCommandsRef =
|
|
5719
|
+
const refreshActiveCommandsRef = useRef3(() => {
|
|
4799
5720
|
});
|
|
4800
|
-
const postToParentRef =
|
|
5721
|
+
const postToParentRef = useRef3(postToParent);
|
|
4801
5722
|
postToParentRef.current = postToParent;
|
|
4802
|
-
const [toolbarRect, setToolbarRect] =
|
|
4803
|
-
const [toggleState, setToggleState] =
|
|
4804
|
-
const [maxBadge, setMaxBadge] =
|
|
4805
|
-
const [activeCommands, setActiveCommands] =
|
|
4806
|
-
const [
|
|
4807
|
-
|
|
5723
|
+
const [toolbarRect, setToolbarRect] = useState5(null);
|
|
5724
|
+
const [toggleState, setToggleState] = useState5(null);
|
|
5725
|
+
const [maxBadge, setMaxBadge] = useState5(null);
|
|
5726
|
+
const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
|
|
5727
|
+
const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
|
|
5728
|
+
const [linkPopover, setLinkPopover] = useState5(null);
|
|
5729
|
+
const [sitePages, setSitePages] = useState5([]);
|
|
5730
|
+
const [sectionsByPath, setSectionsByPath] = useState5({});
|
|
5731
|
+
const setLinkPopoverRef = useRef3(setLinkPopover);
|
|
5732
|
+
const linkPopoverPanelRef = useRef3(null);
|
|
5733
|
+
const linkPopoverOpenRef = useRef3(false);
|
|
5734
|
+
const linkPopoverGraceUntilRef = useRef3(0);
|
|
5735
|
+
setLinkPopoverRef.current = setLinkPopover;
|
|
5736
|
+
const bumpLinkPopoverGrace = () => {
|
|
5737
|
+
linkPopoverGraceUntilRef.current = Date.now() + 350;
|
|
5738
|
+
};
|
|
5739
|
+
useEffect4(() => {
|
|
5740
|
+
if (!isEditMode) return;
|
|
5741
|
+
const useFixtures = shouldUseDevFixtures();
|
|
5742
|
+
if (useFixtures) {
|
|
5743
|
+
setSitePages(DEV_SITE_PAGES);
|
|
5744
|
+
setSectionsByPath(DEV_SECTIONS_BY_PATH);
|
|
5745
|
+
if (process.env.NODE_ENV === "development") {
|
|
5746
|
+
console.info("[ohhwells-bridge] Dev fixtures loaded (ohw-fixtures=1)");
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
const onSitePages = (e) => {
|
|
5750
|
+
if (e.data?.type !== "ow:site-pages" || !Array.isArray(e.data.pages)) return;
|
|
5751
|
+
if (useFixtures) return;
|
|
5752
|
+
setSitePages(
|
|
5753
|
+
e.data.pages.map((p) => ({
|
|
5754
|
+
path: p.path,
|
|
5755
|
+
title: p.title
|
|
5756
|
+
}))
|
|
5757
|
+
);
|
|
5758
|
+
};
|
|
5759
|
+
window.addEventListener("message", onSitePages);
|
|
5760
|
+
if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
|
|
5761
|
+
return () => window.removeEventListener("message", onSitePages);
|
|
5762
|
+
}, [isEditMode, postToParent]);
|
|
5763
|
+
const [sectionGap, setSectionGap] = useState5(null);
|
|
5764
|
+
useEffect4(() => {
|
|
4808
5765
|
const update = () => {
|
|
4809
5766
|
const el = activeElRef.current;
|
|
4810
5767
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -4828,10 +5785,10 @@ function OhhwellsBridge() {
|
|
|
4828
5785
|
vvp.removeEventListener("resize", update);
|
|
4829
5786
|
};
|
|
4830
5787
|
}, []);
|
|
4831
|
-
const refreshStateRules =
|
|
5788
|
+
const refreshStateRules = useCallback2(() => {
|
|
4832
5789
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4833
5790
|
}, []);
|
|
4834
|
-
const deactivate =
|
|
5791
|
+
const deactivate = useCallback2(() => {
|
|
4835
5792
|
const el = activeElRef.current;
|
|
4836
5793
|
if (!el) return;
|
|
4837
5794
|
const key = el.dataset.ohwKey;
|
|
@@ -4854,9 +5811,10 @@ function OhhwellsBridge() {
|
|
|
4854
5811
|
setToolbarRect(null);
|
|
4855
5812
|
setMaxBadge(null);
|
|
4856
5813
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
5814
|
+
setToolbarShowEditLink(false);
|
|
4857
5815
|
postToParent({ type: "ow:exit-edit" });
|
|
4858
5816
|
}, [postToParent]);
|
|
4859
|
-
const activate =
|
|
5817
|
+
const activate = useCallback2((el) => {
|
|
4860
5818
|
if (activeElRef.current === el) return;
|
|
4861
5819
|
deactivate();
|
|
4862
5820
|
if (hoveredImageRef.current) {
|
|
@@ -4869,6 +5827,7 @@ function OhhwellsBridge() {
|
|
|
4869
5827
|
originalContentRef.current = el.innerHTML;
|
|
4870
5828
|
el.focus();
|
|
4871
5829
|
setToolbarRect(el.getBoundingClientRect());
|
|
5830
|
+
setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
|
|
4872
5831
|
postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
|
|
4873
5832
|
requestAnimationFrame(() => refreshActiveCommandsRef.current());
|
|
4874
5833
|
}, [deactivate, postToParent]);
|
|
@@ -4896,10 +5855,13 @@ function OhhwellsBridge() {
|
|
|
4896
5855
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
4897
5856
|
const next = `url('${val}')`;
|
|
4898
5857
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
5858
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
5859
|
+
applyLinkHref(el, val);
|
|
4899
5860
|
} else if (el.innerHTML !== val) {
|
|
4900
5861
|
el.innerHTML = val;
|
|
4901
5862
|
}
|
|
4902
5863
|
});
|
|
5864
|
+
applyLinkByKey(key, val);
|
|
4903
5865
|
}
|
|
4904
5866
|
initSectionsFromContent(content, true);
|
|
4905
5867
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
@@ -4926,7 +5888,7 @@ function OhhwellsBridge() {
|
|
|
4926
5888
|
cancelled = true;
|
|
4927
5889
|
};
|
|
4928
5890
|
}, [subdomain, isEditMode, pathname]);
|
|
4929
|
-
|
|
5891
|
+
useEffect4(() => {
|
|
4930
5892
|
if (!subdomain || isEditMode) return;
|
|
4931
5893
|
let debounceTimer = null;
|
|
4932
5894
|
const applyFromCache = () => {
|
|
@@ -4942,10 +5904,13 @@ function OhhwellsBridge() {
|
|
|
4942
5904
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
4943
5905
|
const next = `url('${val}')`;
|
|
4944
5906
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
5907
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
5908
|
+
applyLinkHref(el, val);
|
|
4945
5909
|
} else if (el.innerHTML !== val) {
|
|
4946
5910
|
el.innerHTML = val;
|
|
4947
5911
|
}
|
|
4948
5912
|
});
|
|
5913
|
+
applyLinkByKey(key, val);
|
|
4949
5914
|
}
|
|
4950
5915
|
};
|
|
4951
5916
|
const scheduleApply = () => {
|
|
@@ -4966,10 +5931,10 @@ function OhhwellsBridge() {
|
|
|
4966
5931
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
4967
5932
|
el.style.display = visible ? "flex" : "none";
|
|
4968
5933
|
}, [subdomain, fetchState]);
|
|
4969
|
-
|
|
5934
|
+
useEffect4(() => {
|
|
4970
5935
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
4971
5936
|
}, [pathname, postToParent]);
|
|
4972
|
-
|
|
5937
|
+
useEffect4(() => {
|
|
4973
5938
|
if (!isEditMode) return;
|
|
4974
5939
|
const measure = () => {
|
|
4975
5940
|
const h = document.body.scrollHeight;
|
|
@@ -4993,7 +5958,7 @@ function OhhwellsBridge() {
|
|
|
4993
5958
|
window.removeEventListener("resize", handleResize);
|
|
4994
5959
|
};
|
|
4995
5960
|
}, [pathname, isEditMode, postToParent]);
|
|
4996
|
-
|
|
5961
|
+
useEffect4(() => {
|
|
4997
5962
|
if (!subdomainFromQuery || isEditMode) return;
|
|
4998
5963
|
const handleClick = (e) => {
|
|
4999
5964
|
const anchor = e.target.closest("a");
|
|
@@ -5009,7 +5974,7 @@ function OhhwellsBridge() {
|
|
|
5009
5974
|
document.addEventListener("click", handleClick, true);
|
|
5010
5975
|
return () => document.removeEventListener("click", handleClick, true);
|
|
5011
5976
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
5012
|
-
|
|
5977
|
+
useEffect4(() => {
|
|
5013
5978
|
if (!isEditMode) {
|
|
5014
5979
|
editStylesRef.current?.base.remove();
|
|
5015
5980
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -5035,6 +6000,7 @@ function OhhwellsBridge() {
|
|
|
5035
6000
|
[data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
|
|
5036
6001
|
[data-ohw-editable="image"], [data-ohw-editable="image"] *,
|
|
5037
6002
|
[data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
|
|
6003
|
+
[data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
|
|
5038
6004
|
[data-ohw-hovered]:not([contenteditable]) {
|
|
5039
6005
|
outline: 2px dashed ${PRIMARY} !important;
|
|
5040
6006
|
outline-offset: 4px;
|
|
@@ -5072,8 +6038,21 @@ function OhhwellsBridge() {
|
|
|
5072
6038
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
5073
6039
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
5074
6040
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
6041
|
+
if (isInsideLinkEditor(target)) return;
|
|
5075
6042
|
const editable = target.closest("[data-ohw-editable]");
|
|
5076
6043
|
if (editable) {
|
|
6044
|
+
if (editable.dataset.ohwEditable === "link") {
|
|
6045
|
+
e.preventDefault();
|
|
6046
|
+
e.stopPropagation();
|
|
6047
|
+
deactivateRef.current();
|
|
6048
|
+
bumpLinkPopoverGrace();
|
|
6049
|
+
setLinkPopoverRef.current({
|
|
6050
|
+
key: editable.dataset.ohwKey ?? "",
|
|
6051
|
+
target: getLinkHref(editable),
|
|
6052
|
+
rect: editable.getBoundingClientRect()
|
|
6053
|
+
});
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
5077
6056
|
if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
|
|
5078
6057
|
e.preventDefault();
|
|
5079
6058
|
e.stopPropagation();
|
|
@@ -5094,6 +6073,15 @@ function OhhwellsBridge() {
|
|
|
5094
6073
|
}
|
|
5095
6074
|
const anchor = target.closest("a");
|
|
5096
6075
|
if (anchor) e.preventDefault();
|
|
6076
|
+
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
6077
|
+
if (linkPopoverOpenRef.current && activeElRef.current) {
|
|
6078
|
+
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6079
|
+
if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
|
|
6080
|
+
}
|
|
6081
|
+
if (linkPopoverOpenRef.current) {
|
|
6082
|
+
setLinkPopoverRef.current(null);
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
5097
6085
|
deactivateRef.current();
|
|
5098
6086
|
};
|
|
5099
6087
|
const handleMouseOver = (e) => {
|
|
@@ -5544,10 +6532,13 @@ function OhhwellsBridge() {
|
|
|
5544
6532
|
if (img) img.src = val;
|
|
5545
6533
|
} else if (el.dataset.ohwEditable === "bg-image") {
|
|
5546
6534
|
el.style.backgroundImage = `url('${val}')`;
|
|
6535
|
+
} else if (el.dataset.ohwEditable === "link") {
|
|
6536
|
+
applyLinkHref(el, val);
|
|
5547
6537
|
} else {
|
|
5548
6538
|
el.innerHTML = val;
|
|
5549
6539
|
}
|
|
5550
6540
|
});
|
|
6541
|
+
applyLinkByKey(key, val);
|
|
5551
6542
|
}
|
|
5552
6543
|
if (sectionsJson) {
|
|
5553
6544
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
@@ -5557,10 +6548,19 @@ function OhhwellsBridge() {
|
|
|
5557
6548
|
window.addEventListener("message", handleHydrate);
|
|
5558
6549
|
const handleDeactivate = (e) => {
|
|
5559
6550
|
if (e.data?.type !== "ow:deactivate") return;
|
|
6551
|
+
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
6552
|
+
if (linkPopoverOpenRef.current) {
|
|
6553
|
+
setLinkPopoverRef.current(null);
|
|
6554
|
+
return;
|
|
6555
|
+
}
|
|
5560
6556
|
deactivateRef.current();
|
|
5561
6557
|
};
|
|
5562
6558
|
window.addEventListener("message", handleDeactivate);
|
|
5563
6559
|
const handleKeyDown = (e) => {
|
|
6560
|
+
if (e.key === "Escape" && linkPopoverOpenRef.current) {
|
|
6561
|
+
setLinkPopoverRef.current(null);
|
|
6562
|
+
return;
|
|
6563
|
+
}
|
|
5564
6564
|
if (e.key !== "Escape") return;
|
|
5565
6565
|
const el = activeElRef.current;
|
|
5566
6566
|
if (el && originalContentRef.current !== null) {
|
|
@@ -5814,7 +6814,7 @@ function OhhwellsBridge() {
|
|
|
5814
6814
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5815
6815
|
};
|
|
5816
6816
|
}, [isEditMode, refreshStateRules]);
|
|
5817
|
-
|
|
6817
|
+
useEffect4(() => {
|
|
5818
6818
|
if (!isEditMode) return;
|
|
5819
6819
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
5820
6820
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -5829,20 +6829,26 @@ function OhhwellsBridge() {
|
|
|
5829
6829
|
const raf = requestAnimationFrame(() => refreshStateRules());
|
|
5830
6830
|
const timer = setTimeout(() => {
|
|
5831
6831
|
refreshStateRules();
|
|
5832
|
-
|
|
6832
|
+
const sections = collectSections();
|
|
6833
|
+
setSectionsByPath((prev) => {
|
|
6834
|
+
const next = { ...prev, [pathname]: sections };
|
|
6835
|
+
return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
|
|
6836
|
+
});
|
|
6837
|
+
postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
|
|
6838
|
+
postToParent({ type: "ow:request-site-pages" });
|
|
5833
6839
|
}, 150);
|
|
5834
6840
|
return () => {
|
|
5835
6841
|
cancelAnimationFrame(raf);
|
|
5836
6842
|
clearTimeout(timer);
|
|
5837
6843
|
};
|
|
5838
6844
|
}, [pathname, isEditMode, refreshStateRules, postToParent]);
|
|
5839
|
-
const handleCommand =
|
|
6845
|
+
const handleCommand = useCallback2((cmd) => {
|
|
5840
6846
|
document.execCommand(cmd, false);
|
|
5841
6847
|
activeElRef.current?.focus();
|
|
5842
6848
|
if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
|
|
5843
6849
|
refreshActiveCommandsRef.current();
|
|
5844
6850
|
}, []);
|
|
5845
|
-
const handleStateChange =
|
|
6851
|
+
const handleStateChange = useCallback2((state) => {
|
|
5846
6852
|
if (!activeStateElRef.current) return;
|
|
5847
6853
|
const el = activeStateElRef.current;
|
|
5848
6854
|
if (state === "Default") {
|
|
@@ -5855,13 +6861,48 @@ function OhhwellsBridge() {
|
|
|
5855
6861
|
}
|
|
5856
6862
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
5857
6863
|
}, [deactivate]);
|
|
6864
|
+
const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
|
|
6865
|
+
const openLinkPopoverForActive = useCallback2(() => {
|
|
6866
|
+
const hrefCtx = getHrefKeyFromElement(activeElRef.current);
|
|
6867
|
+
if (!hrefCtx) return;
|
|
6868
|
+
bumpLinkPopoverGrace();
|
|
6869
|
+
setLinkPopover({
|
|
6870
|
+
key: hrefCtx.key,
|
|
6871
|
+
target: getLinkHref(hrefCtx.anchor),
|
|
6872
|
+
rect: hrefCtx.anchor.getBoundingClientRect()
|
|
6873
|
+
});
|
|
6874
|
+
}, []);
|
|
6875
|
+
const handleLinkPopoverSubmit = useCallback2(
|
|
6876
|
+
(target) => {
|
|
6877
|
+
if (!linkPopover) return;
|
|
6878
|
+
const { key } = linkPopover;
|
|
6879
|
+
applyLinkByKey(key, target);
|
|
6880
|
+
postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
|
|
6881
|
+
setLinkPopover(null);
|
|
6882
|
+
},
|
|
6883
|
+
[linkPopover, postToParent]
|
|
6884
|
+
);
|
|
6885
|
+
const showEditLink = toolbarShowEditLink;
|
|
6886
|
+
const currentSections = sectionsByPath[pathname] ?? [];
|
|
6887
|
+
linkPopoverOpenRef.current = linkPopover !== null;
|
|
5858
6888
|
return bridgeRoot ? createPortal(
|
|
5859
|
-
/* @__PURE__ */
|
|
5860
|
-
toolbarRect && /* @__PURE__ */
|
|
5861
|
-
/* @__PURE__ */
|
|
5862
|
-
/* @__PURE__ */
|
|
6889
|
+
/* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6890
|
+
toolbarRect && /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6891
|
+
/* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
6892
|
+
/* @__PURE__ */ jsx16(
|
|
6893
|
+
FloatingToolbar,
|
|
6894
|
+
{
|
|
6895
|
+
rect: toolbarRect,
|
|
6896
|
+
parentScroll: parentScrollRef.current,
|
|
6897
|
+
elRef: toolbarElRef,
|
|
6898
|
+
onCommand: handleCommand,
|
|
6899
|
+
activeCommands,
|
|
6900
|
+
showEditLink,
|
|
6901
|
+
onEditLink: openLinkPopoverForActive
|
|
6902
|
+
}
|
|
6903
|
+
)
|
|
5863
6904
|
] }),
|
|
5864
|
-
maxBadge && /* @__PURE__ */
|
|
6905
|
+
maxBadge && /* @__PURE__ */ jsxs8(
|
|
5865
6906
|
"div",
|
|
5866
6907
|
{
|
|
5867
6908
|
"data-ohw-max-badge": "",
|
|
@@ -5887,7 +6928,7 @@ function OhhwellsBridge() {
|
|
|
5887
6928
|
]
|
|
5888
6929
|
}
|
|
5889
6930
|
),
|
|
5890
|
-
toggleState && /* @__PURE__ */
|
|
6931
|
+
toggleState && /* @__PURE__ */ jsx16(
|
|
5891
6932
|
StateToggle,
|
|
5892
6933
|
{
|
|
5893
6934
|
rect: toggleState.rect,
|
|
@@ -5896,15 +6937,31 @@ function OhhwellsBridge() {
|
|
|
5896
6937
|
onStateChange: handleStateChange
|
|
5897
6938
|
}
|
|
5898
6939
|
),
|
|
5899
|
-
|
|
6940
|
+
linkPopover ? /* @__PURE__ */ jsx16(
|
|
6941
|
+
LinkPopover,
|
|
6942
|
+
{
|
|
6943
|
+
rect: linkPopover.rect,
|
|
6944
|
+
parentScroll: parentScrollRef.current,
|
|
6945
|
+
panelRef: linkPopoverPanelRef,
|
|
6946
|
+
open: true,
|
|
6947
|
+
mode: "edit",
|
|
6948
|
+
pages: sitePages,
|
|
6949
|
+
sections: currentSections,
|
|
6950
|
+
sectionsByPath,
|
|
6951
|
+
initialTarget: linkPopover.target,
|
|
6952
|
+
onClose: closeLinkPopover,
|
|
6953
|
+
onSubmit: handleLinkPopoverSubmit
|
|
6954
|
+
}
|
|
6955
|
+
) : null,
|
|
6956
|
+
sectionGap && /* @__PURE__ */ jsxs8(
|
|
5900
6957
|
"div",
|
|
5901
6958
|
{
|
|
5902
6959
|
"data-ohw-section-insert-line": "",
|
|
5903
6960
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
5904
6961
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
5905
6962
|
children: [
|
|
5906
|
-
/* @__PURE__ */
|
|
5907
|
-
/* @__PURE__ */
|
|
6963
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
6964
|
+
/* @__PURE__ */ jsx16(
|
|
5908
6965
|
Badge,
|
|
5909
6966
|
{
|
|
5910
6967
|
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",
|
|
@@ -5917,7 +6974,7 @@ function OhhwellsBridge() {
|
|
|
5917
6974
|
children: "Add Section"
|
|
5918
6975
|
}
|
|
5919
6976
|
),
|
|
5920
|
-
/* @__PURE__ */
|
|
6977
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
5921
6978
|
]
|
|
5922
6979
|
}
|
|
5923
6980
|
)
|
|
@@ -5926,11 +6983,19 @@ function OhhwellsBridge() {
|
|
|
5926
6983
|
) : null;
|
|
5927
6984
|
}
|
|
5928
6985
|
export {
|
|
6986
|
+
LinkEditorPanel,
|
|
6987
|
+
LinkPopover,
|
|
5929
6988
|
OhhwellsBridge,
|
|
5930
6989
|
SchedulingWidget,
|
|
5931
6990
|
Toggle,
|
|
5932
6991
|
ToggleGroup,
|
|
5933
6992
|
ToggleGroupItem,
|
|
5934
|
-
|
|
6993
|
+
buildTarget,
|
|
6994
|
+
filterAvailablePages,
|
|
6995
|
+
getEditModeInitialState,
|
|
6996
|
+
isValidUrl,
|
|
6997
|
+
parseTarget,
|
|
6998
|
+
toggleVariants,
|
|
6999
|
+
validateUrlInput
|
|
5935
7000
|
};
|
|
5936
7001
|
//# sourceMappingURL=index.js.map
|