@ohhwells/bridge 0.1.92-next.269 → 0.1.92-next.274

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 CHANGED
@@ -1400,7 +1400,12 @@ function ButtonEl({
1400
1400
  textDecoration: "none",
1401
1401
  cursor: "pointer",
1402
1402
  ...buttonShellStyle(ctx, fullWidth),
1403
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1403
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : (
1404
+ // Off-band, prefer the template button's measured fill/label pair — a template may
1405
+ // fill its CTAs with any token (hvac: accent bg, primary text). On an accent band
1406
+ // the flipped palette keeps contrast, so the brand-driven colours stay.
1407
+ !ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1408
+ )
1404
1409
  },
1405
1410
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1406
1411
  }
@@ -2472,10 +2477,12 @@ function renderNode(node, ctx, path) {
2472
2477
  cursor: "pointer",
2473
2478
  // Shape/padding/typography follow the host template's own buttons.
2474
2479
  ...buttonShellStyle(ctx),
2475
- // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
2476
- // reads correctly on custom palettes.
2477
- background: ctx.brand.palette.primary,
2478
- color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2480
+ // Brand-styled: the template button's measured fill/label pair off-band, else
2481
+ // primary fill with a brand-derived label so it reads on custom palettes.
2482
+ ...!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2483
+ background: ctx.brand.palette.primary,
2484
+ color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2485
+ }
2479
2486
  },
2480
2487
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2481
2488
  },
@@ -2684,11 +2691,19 @@ function deriveTemplateBrand() {
2684
2691
  }
2685
2692
  function deriveTemplateButtonStyle() {
2686
2693
  if (typeof document === "undefined") return null;
2687
- const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
2688
- (el) => !el.closest(`[${CONTAINER_ATTR}]`)
2689
- );
2694
+ const candidates = Array.from(
2695
+ document.querySelectorAll('[data-ohw-role="button"]')
2696
+ ).filter((el) => !el.closest(`[${CONTAINER_ATTR}]`));
2697
+ const isFilled = (el) => {
2698
+ const bg = getComputedStyle(el).backgroundColor;
2699
+ if (!bg || bg === "transparent") return false;
2700
+ const alpha = bg.match(/rgba?\([^)]*,\s*([\d.]+)\)$/);
2701
+ return !alpha || parseFloat(alpha[1]) > 0;
2702
+ };
2703
+ const btn = candidates.find(isFilled) ?? candidates[0];
2690
2704
  if (!btn) return null;
2691
2705
  const cs = getComputedStyle(btn);
2706
+ const filled = isFilled(btn);
2692
2707
  const corners = [
2693
2708
  cs.borderTopLeftRadius,
2694
2709
  cs.borderTopRightRadius,
@@ -2705,6 +2720,7 @@ function deriveTemplateButtonStyle() {
2705
2720
  return {
2706
2721
  radius: radius || "10px",
2707
2722
  padding: `${padY}px ${padX}px`,
2723
+ ...filled ? { background: cs.backgroundColor, color: cs.color } : {},
2708
2724
  fontFamily: cs.fontFamily || "",
2709
2725
  fontSize: cs.fontSize || "",
2710
2726
  fontWeight: cs.fontWeight || "",
@@ -7506,9 +7522,7 @@ function ItemActionToolbar({
7506
7522
  ToggleGroup,
7507
7523
  {
7508
7524
  value: dropdownOpen ? "open" : "closed",
7509
- onValueChange: (value) => {
7510
- if (value !== "open" && value !== "closed") return;
7511
- onDropdownOpenChange?.(value === "open");
7525
+ onValueChange: () => {
7512
7526
  },
7513
7527
  className: "h-7 gap-0.5 rounded-[calc(var(--radius,0.5rem)-2px)] bg-muted p-0.5",
7514
7528
  onMouseDown: (e) => {
@@ -7523,6 +7537,11 @@ function ItemActionToolbar({
7523
7537
  size: "sm",
7524
7538
  "aria-label": "Closed",
7525
7539
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7540
+ onMouseDown: (e) => {
7541
+ e.preventDefault();
7542
+ e.stopPropagation();
7543
+ if (dropdownOpen !== false) onDropdownOpenChange?.(false);
7544
+ },
7526
7545
  children: "Closed"
7527
7546
  }
7528
7547
  ),
@@ -7533,6 +7552,11 @@ function ItemActionToolbar({
7533
7552
  size: "sm",
7534
7553
  "aria-label": "Open",
7535
7554
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7555
+ onMouseDown: (e) => {
7556
+ e.preventDefault();
7557
+ e.stopPropagation();
7558
+ if (dropdownOpen !== true) onDropdownOpenChange?.(true);
7559
+ },
7536
7560
  children: "Open"
7537
7561
  }
7538
7562
  )
@@ -8558,7 +8582,7 @@ function FieldTypePicker({ onPick }) {
8558
8582
  "div",
8559
8583
  {
8560
8584
  "data-ohw-field-type-picker": "",
8561
- className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8585
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8562
8586
  children: FIELD_TYPES.map((entry) => {
8563
8587
  const Icon = TYPE_ICONS[entry.type];
8564
8588
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
@@ -8566,7 +8590,7 @@ function FieldTypePicker({ onPick }) {
8566
8590
  {
8567
8591
  type: "button",
8568
8592
  onClick: () => onPick(entry.type),
8569
- className: "flex h-[104px] flex-col items-center justify-center gap-3 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8593
+ className: "flex h-[76px] flex-col items-center justify-center gap-2 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8570
8594
  children: [
8571
8595
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8572
8596
  entry.label
@@ -10920,6 +10944,32 @@ function getNavbarDesktopContainer() {
10920
10944
  function getNavbarDrawerContainer() {
10921
10945
  return document.querySelector("[data-ohw-nav-drawer]");
10922
10946
  }
10947
+ function isNavbarListContainerVisible(el) {
10948
+ if (el.hasAttribute("hidden")) return false;
10949
+ if (el.getClientRects().length === 0) return false;
10950
+ const style = window.getComputedStyle(el);
10951
+ return style.display !== "none" && style.visibility !== "hidden";
10952
+ }
10953
+ function getActiveNavbarListContainer(draggedEl) {
10954
+ const desktop = getNavbarDesktopContainer();
10955
+ const drawer = getNavbarDrawerContainer();
10956
+ if (draggedEl) {
10957
+ if (drawer?.contains(draggedEl)) return drawer;
10958
+ if (desktop?.contains(draggedEl)) return desktop;
10959
+ }
10960
+ if (desktop && isNavbarListContainerVisible(desktop)) return desktop;
10961
+ if (drawer && isNavbarListContainerVisible(drawer)) return drawer;
10962
+ return desktop ?? drawer;
10963
+ }
10964
+ function listActiveNavbarItems(draggedEl) {
10965
+ const container = getActiveNavbarListContainer(draggedEl);
10966
+ if (container) {
10967
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(
10968
+ isNavbarLinkItem
10969
+ );
10970
+ }
10971
+ return listNavbarItems();
10972
+ }
10923
10973
  function listNavbarItems() {
10924
10974
  const desktop = getNavbarDesktopContainer();
10925
10975
  if (desktop) {
@@ -12032,20 +12082,23 @@ function isEmptyLabelValue(value) {
12032
12082
  function applyStoredValues(item, content) {
12033
12083
  const hrefKey = socialHrefKey(item);
12034
12084
  const iconKey = socialIconKey(item);
12085
+ const baseKey = hrefKey?.replace(/-href$/, "");
12035
12086
  if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
12036
- if (iconKey) {
12087
+ const glyphKey = iconKey ?? baseKey ?? null;
12088
+ const glyphStored = glyphKey ? content[glyphKey]?.trim() : void 0;
12089
+ if (glyphStored) {
12090
+ ensureIconSlot(item);
12037
12091
  const glyph = item.querySelector(ICON_SELECTOR);
12038
- if (glyph && content[iconKey]) {
12039
- applyIconMarkup(glyph, content[iconKey]);
12092
+ if (glyph) {
12093
+ applyIconMarkup(glyph, glyphStored);
12040
12094
  glyph.removeAttribute(SOCIALS_ICON_PLACEHOLDER_ATTR);
12041
12095
  }
12042
- const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
12043
- label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
12044
- const stored = content[socialLabelKey(iconKey)];
12045
- if (label && stored !== void 0) {
12046
- const words = isEmptyLabelValue(stored) ? "" : stored;
12047
- if (label.innerHTML !== words) label.innerHTML = words;
12048
- }
12096
+ }
12097
+ const label = socialLabelElement(item);
12098
+ const labelKey = label?.getAttribute("data-ohw-key");
12099
+ if (label && labelKey && content[labelKey] !== void 0) {
12100
+ const words = isEmptyLabelValue(content[labelKey]) ? "" : content[labelKey];
12101
+ if (label.innerHTML !== words) label.innerHTML = words;
12049
12102
  }
12050
12103
  }
12051
12104
  function socialPlatformKey(iconKey) {
@@ -12134,6 +12187,9 @@ function insertSocialItem(row, after, content = {}, { placeholder = true, keys }
12134
12187
  if (placeholder) {
12135
12188
  const label = socialLabelElement(item);
12136
12189
  if (label) label.textContent = PLACEHOLDER_SOCIAL_LABEL;
12190
+ } else if (keys) {
12191
+ const label = socialLabelElement(item);
12192
+ if (label) label.textContent = "";
12137
12193
  }
12138
12194
  applySocialsDisplayToRow(row, display);
12139
12195
  return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
@@ -13991,8 +14047,8 @@ var lockFooterDuringDrag = lockItemDuringDrag;
13991
14047
  var unlockFooterDragInteraction = unlockItemDragInteraction;
13992
14048
 
13993
14049
  // src/lib/nav-dnd.ts
13994
- function listReorderableNavItems() {
13995
- return listNavbarItems().filter((el) => {
14050
+ function listReorderableNavItems(draggedEl) {
14051
+ return listActiveNavbarItems(draggedEl).filter((el) => {
13996
14052
  const key = el.getAttribute("data-ohw-href-key");
13997
14053
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
13998
14054
  });
@@ -14007,8 +14063,8 @@ function resolveParentNavHrefKey(child) {
14007
14063
  const key = trigger?.getAttribute("data-ohw-href-key");
14008
14064
  return key && isNavbarHrefKey(key) ? key : null;
14009
14065
  }
14010
- function listNavChildren(parentHrefKey) {
14011
- const items = listNavbarItems();
14066
+ function listNavChildren(parentHrefKey, draggedEl) {
14067
+ const items = listActiveNavbarItems(draggedEl);
14012
14068
  const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
14013
14069
  if (!parent) return [];
14014
14070
  const group = parent.closest("[data-ohw-nav-group]");
@@ -14018,20 +14074,83 @@ function listNavChildren(parentHrefKey) {
14018
14074
  (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
14019
14075
  );
14020
14076
  }
14021
- function activeNavListContainer() {
14022
- const desktop = getNavbarDesktopContainer();
14023
- if (desktop && desktop.getClientRects().length > 0) {
14024
- const style = window.getComputedStyle(desktop);
14025
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
14077
+ function isVerticalNavLayout(items, draggedEl) {
14078
+ const visible = items.filter((el) => el.getClientRects().length > 0);
14079
+ if (visible.length >= 2) {
14080
+ const a = visible[0].getBoundingClientRect();
14081
+ const b = visible[1].getBoundingClientRect();
14082
+ const verticalGap = b.top - a.bottom;
14083
+ const horizontalGap = b.left - a.right;
14084
+ return verticalGap > horizontalGap;
14085
+ }
14086
+ const container = getActiveNavbarListContainer(draggedEl);
14087
+ if (!container) return false;
14088
+ const style = window.getComputedStyle(container);
14089
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
14090
+ }
14091
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
14092
+ const slots = [];
14093
+ const barThickness = 3;
14094
+ if (items.length === 0) {
14095
+ const container = getActiveNavbarListContainer(draggedEl);
14096
+ if (!container) return slots;
14097
+ const rect = container.getBoundingClientRect();
14098
+ slots.push({
14099
+ insertIndex: 0,
14100
+ parentId: null,
14101
+ left: rect.left,
14102
+ top: rect.top + rect.height / 2 - barThickness / 2,
14103
+ width: Math.max(rect.width, 40),
14104
+ height: barThickness,
14105
+ direction: "horizontal"
14106
+ });
14107
+ return slots;
14108
+ }
14109
+ const edgeGap = (() => {
14110
+ if (items.length < 2) return 16;
14111
+ const a = items[0].getBoundingClientRect();
14112
+ const b = items[1].getBoundingClientRect();
14113
+ return Math.max(0, b.top - a.bottom);
14114
+ })();
14115
+ for (let i = 0; i <= items.length; i++) {
14116
+ let top;
14117
+ let width;
14118
+ let left;
14119
+ if (i === 0) {
14120
+ const first = items[0].getBoundingClientRect();
14121
+ top = first.top - edgeGap / 2 - barThickness / 2;
14122
+ left = first.left;
14123
+ width = first.width;
14124
+ } else if (i === items.length) {
14125
+ const last = items[items.length - 1].getBoundingClientRect();
14126
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
14127
+ top = last.bottom + lastGap / 2 - barThickness / 2;
14128
+ left = last.left;
14129
+ width = last.width;
14130
+ } else {
14131
+ const prev = items[i - 1].getBoundingClientRect();
14132
+ const next = items[i].getBoundingClientRect();
14133
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
14134
+ left = Math.min(prev.left, next.left);
14135
+ width = Math.max(prev.right, next.right) - left;
14136
+ }
14137
+ slots.push({
14138
+ insertIndex: i,
14139
+ parentId: null,
14140
+ left,
14141
+ top,
14142
+ width: Math.max(width, 40),
14143
+ height: barThickness,
14144
+ direction: "horizontal"
14145
+ });
14026
14146
  }
14027
- return getNavbarDrawerContainer();
14147
+ return slots;
14028
14148
  }
14029
- function buildRootNavDropSlots() {
14030
- const items = listReorderableNavItems();
14149
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
14031
14150
  const slots = [];
14032
14151
  const barThickness = 3;
14033
14152
  if (items.length === 0) {
14034
- const container = activeNavListContainer();
14153
+ const container = getActiveNavbarListContainer(draggedEl);
14035
14154
  if (!container) return slots;
14036
14155
  const rect = container.getBoundingClientRect();
14037
14156
  slots.push({
@@ -14062,10 +14181,7 @@ function buildRootNavDropSlots() {
14062
14181
  height = first.height;
14063
14182
  } else if (i === items.length) {
14064
14183
  const last = items[items.length - 1].getBoundingClientRect();
14065
- const lastGap = items.length >= 2 ? Math.max(
14066
- 0,
14067
- last.left - items[items.length - 2].getBoundingClientRect().right
14068
- ) : edgeGap;
14184
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
14069
14185
  left = last.right + lastGap / 2 - barThickness / 2;
14070
14186
  top = last.top;
14071
14187
  height = last.height;
@@ -14088,8 +14204,15 @@ function buildRootNavDropSlots() {
14088
14204
  }
14089
14205
  return slots;
14090
14206
  }
14091
- function buildChildNavDropSlots(parentHrefKey) {
14092
- const children = listNavChildren(parentHrefKey);
14207
+ function buildRootNavDropSlots(draggedEl) {
14208
+ const items = listReorderableNavItems(draggedEl);
14209
+ if (isVerticalNavLayout(items, draggedEl)) {
14210
+ return buildVerticalRootNavDropSlots(items, draggedEl);
14211
+ }
14212
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
14213
+ }
14214
+ function buildChildNavDropSlots(parentHrefKey, draggedEl) {
14215
+ const children = listNavChildren(parentHrefKey, draggedEl);
14093
14216
  const slots = [];
14094
14217
  const barThickness = 3;
14095
14218
  if (children.length === 0) return slots;
@@ -14110,10 +14233,7 @@ function buildChildNavDropSlots(parentHrefKey) {
14110
14233
  width = first.width;
14111
14234
  } else if (i === children.length) {
14112
14235
  const last = children[children.length - 1].getBoundingClientRect();
14113
- const lastGap = children.length >= 2 ? Math.max(
14114
- 0,
14115
- last.top - children[children.length - 2].getBoundingClientRect().bottom
14116
- ) : edgeGap;
14236
+ const lastGap = children.length >= 2 ? Math.max(0, last.top - children[children.length - 2].getBoundingClientRect().bottom) : edgeGap;
14117
14237
  top = last.bottom + lastGap / 2 - barThickness / 2;
14118
14238
  left = last.left;
14119
14239
  width = last.width;
@@ -14136,18 +14256,19 @@ function buildChildNavDropSlots(parentHrefKey) {
14136
14256
  }
14137
14257
  return slots;
14138
14258
  }
14139
- function buildAllNavDropSlots(draggedHrefKey) {
14140
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14141
- if (!dragged) return buildRootNavDropSlots();
14259
+ function buildAllNavDropSlots(draggedHrefKey, draggedEl) {
14260
+ const activeItems = listActiveNavbarItems(draggedEl);
14261
+ const dragged = (draggedEl && activeItems.includes(draggedEl) ? draggedEl : null) ?? activeItems.find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey) ?? listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14262
+ if (!dragged) return buildRootNavDropSlots(draggedEl);
14142
14263
  if (isNestedNavChild(dragged)) {
14143
14264
  const parentKey = resolveParentNavHrefKey(dragged);
14144
14265
  if (!parentKey) return [];
14145
- return buildChildNavDropSlots(parentKey);
14266
+ return buildChildNavDropSlots(parentKey, draggedEl);
14146
14267
  }
14147
- return buildRootNavDropSlots();
14268
+ return buildRootNavDropSlots(draggedEl);
14148
14269
  }
14149
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
14150
- const slots = buildAllNavDropSlots(draggedHrefKey);
14270
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey, draggedEl) {
14271
+ const slots = buildAllNavDropSlots(draggedHrefKey, draggedEl);
14151
14272
  let best = null;
14152
14273
  for (const slot of slots) {
14153
14274
  const cx2 = slot.left + slot.width / 2;
@@ -14198,6 +14319,12 @@ function useNavItemDrag({
14198
14319
  setIsItemDragging(false);
14199
14320
  if (keepOpenEl?.isConnected) {
14200
14321
  setNavGroupForceOpen(keepOpenEl, true);
14322
+ requestAnimationFrame(() => {
14323
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14324
+ requestAnimationFrame(() => {
14325
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14326
+ });
14327
+ });
14201
14328
  } else {
14202
14329
  setNavGroupForceOpen(null, false);
14203
14330
  }
@@ -14213,7 +14340,7 @@ function useNavItemDrag({
14213
14340
  }
14214
14341
  session.activeSlot = activeSlot;
14215
14342
  setSiblingHintRects([]);
14216
- const slots = buildAllNavDropSlots(session.hrefKey);
14343
+ const slots = buildAllNavDropSlots(session.hrefKey, session.draggedEl);
14217
14344
  setNavDropSlots(slots);
14218
14345
  const activeIdx = activeSlot ? slots.findIndex(
14219
14346
  (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
@@ -14249,7 +14376,12 @@ function useNavItemDrag({
14249
14376
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
14250
14377
  setToolbarRect(rect);
14251
14378
  }
14252
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
14379
+ const initialSlot = hitTestNavDropSlot(
14380
+ session.lastClientX,
14381
+ session.lastClientY,
14382
+ session.hrefKey,
14383
+ session.draggedEl
14384
+ );
14253
14385
  refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
14254
14386
  },
14255
14387
  [
@@ -14271,10 +14403,11 @@ function useNavItemDrag({
14271
14403
  }
14272
14404
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
14273
14405
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
14274
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
14406
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey, session.draggedEl);
14275
14407
  const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
14276
14408
  const wasSelected = session.wasSelected;
14277
14409
  const hrefKey = session.hrefKey;
14410
+ const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14278
14411
  const applySelectionAfterDrop = () => {
14279
14412
  if (!wasSelected) {
14280
14413
  deselectRef.current();
@@ -14308,6 +14441,14 @@ function useNavItemDrag({
14308
14441
  });
14309
14442
  applySelectionAfterDrop();
14310
14443
  clearNavDragVisuals();
14444
+ if (keepDropdownOpen) {
14445
+ requestAnimationFrame(() => {
14446
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14447
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14448
+ const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
14449
+ if (link) setNavGroupForceOpen(link, true);
14450
+ });
14451
+ }
14311
14452
  requestAnimationFrame(() => {
14312
14453
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14313
14454
  applyNavForest(planned.forest);
@@ -14347,7 +14488,7 @@ function useNavItemDrag({
14347
14488
  if (!session) return false;
14348
14489
  e.preventDefault();
14349
14490
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14350
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14491
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14351
14492
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14352
14493
  return true;
14353
14494
  },
@@ -14393,7 +14534,7 @@ function useNavItemDrag({
14393
14534
  clearTextSelection();
14394
14535
  const session = navDragRef.current;
14395
14536
  if (!session) return;
14396
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14537
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14397
14538
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14398
14539
  return;
14399
14540
  }
@@ -14409,7 +14550,8 @@ function useNavItemDrag({
14409
14550
  } catch {
14410
14551
  }
14411
14552
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14412
- if (activeElRef.current) deactivateRef.current();
14553
+ const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14554
+ if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14413
14555
  const key = pending.el.getAttribute("data-ohw-href-key");
14414
14556
  if (!key) return;
14415
14557
  beginNavDragRef.current({
@@ -14420,6 +14562,9 @@ function useNavItemDrag({
14420
14562
  lastClientY: e.clientY,
14421
14563
  activeSlot: null
14422
14564
  });
14565
+ if (activeElRef.current && isNestedDrag) {
14566
+ deactivateRef.current();
14567
+ }
14423
14568
  };
14424
14569
  const endPointerDrag = (e) => {
14425
14570
  const pending = navPointerDragRef.current;
@@ -14497,6 +14642,7 @@ function useNavItemDrag({
14497
14642
  );
14498
14643
  return {
14499
14644
  navDragRef,
14645
+ navPointerDragRef,
14500
14646
  navDropSlots,
14501
14647
  activeNavDropIndex,
14502
14648
  startNavLinkDrag,
@@ -15893,6 +16039,7 @@ function isNavDropdownPanelOpen(childrenRoot) {
15893
16039
  if (childrenRoot.closest("[data-ohw-nav-drawer]")) return true;
15894
16040
  const group = childrenRoot.closest("[data-ohw-nav-group]");
15895
16041
  if (group?.hasAttribute("data-ohw-nav-force-open")) return true;
16042
+ if (navDropdownsOpenOnClick()) return false;
15896
16043
  if (childrenRoot.clientHeight < 2 || childrenRoot.clientWidth < 2) return false;
15897
16044
  const style = window.getComputedStyle(childrenRoot);
15898
16045
  if (style.display === "none" || style.visibility === "hidden") return false;
@@ -15936,6 +16083,11 @@ function getNavigationItemAnchor(el) {
15936
16083
  function isNavigationItem2(el) {
15937
16084
  return getNavigationItemAnchor(el) !== null;
15938
16085
  }
16086
+ function isNavFooterScopedNavigationItem(el) {
16087
+ return Boolean(
16088
+ el.closest("nav, footer, [data-ohw-nav-container], [data-ohw-nav-drawer]")
16089
+ );
16090
+ }
15939
16091
  function socialWordsClicked(target, item) {
15940
16092
  const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
15941
16093
  return Boolean(text && item.contains(text));
@@ -16146,6 +16298,7 @@ function getNavigationSelectionParent(el) {
16146
16298
  }
16147
16299
  function collectNavigationItemSiblingHintRects(selected) {
16148
16300
  if (!isNavigationItem2(selected)) return [];
16301
+ if (!isNavFooterScopedNavigationItem(selected)) return [];
16149
16302
  const socialsRow = findSocialsRow(selected);
16150
16303
  if (socialsRow) {
16151
16304
  return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
@@ -17309,6 +17462,7 @@ function OhhwellsBridge() {
17309
17462
  }, []);
17310
17463
  const {
17311
17464
  navDragRef,
17465
+ navPointerDragRef,
17312
17466
  navDropSlots,
17313
17467
  activeNavDropIndex,
17314
17468
  startNavLinkDrag,
@@ -17511,7 +17665,12 @@ function OhhwellsBridge() {
17511
17665
  if (owner) persistFieldsRef.current(owner);
17512
17666
  }
17513
17667
  activeElRef.current = null;
17514
- setNavGroupForceOpen(null, false);
17668
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17669
+ selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
17670
+ );
17671
+ if (!preserveNavDropdown) {
17672
+ setNavGroupForceOpen(null, false);
17673
+ }
17515
17674
  setReorderHrefKey(null);
17516
17675
  setReorderDragDisabled(false);
17517
17676
  if (!selectedElRef.current) {
@@ -17633,7 +17792,10 @@ function OhhwellsBridge() {
17633
17792
  setSelectedIsSocialsRow(false);
17634
17793
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
17635
17794
  if (isNestedNavChild(navAnchor)) {
17636
- setNavGroupForceOpen(navAnchor, true);
17795
+ const group = navAnchor.closest("[data-ohw-nav-group]");
17796
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
17797
+ setNavGroupForceOpen(navAnchor, true);
17798
+ }
17637
17799
  setNavDropdownPreviewOpen(null);
17638
17800
  } else if (isDropdownTrigger) {
17639
17801
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -17715,6 +17877,10 @@ function OhhwellsBridge() {
17715
17877
  const handleNavDropdownOpenChange = (0, import_react17.useCallback)((open) => {
17716
17878
  const selected = selectedElRef.current;
17717
17879
  if (!selected || !isNavigationItem2(selected)) return;
17880
+ if (isNestedNavChild(selected)) return;
17881
+ const group = selected.closest("[data-ohw-nav-group]");
17882
+ const domOpen = Boolean(group?.hasAttribute("data-ohw-nav-force-open"));
17883
+ if (open === domOpen) return;
17718
17884
  setNavGroupForceOpen(selected, open);
17719
17885
  setNavDropdownPreviewOpen(open);
17720
17886
  requestAnimationFrame(() => {
@@ -18244,7 +18410,10 @@ function OhhwellsBridge() {
18244
18410
  clearHrefKeyHover(anchor);
18245
18411
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18246
18412
  if (isNestedNavChild(anchor)) {
18247
- setNavGroupForceOpen(anchor, true);
18413
+ const group = anchor.closest("[data-ohw-nav-group]");
18414
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
18415
+ setNavGroupForceOpen(anchor, true);
18416
+ }
18248
18417
  setNavDropdownPreviewOpen(null);
18249
18418
  } else if (isDropdownTrigger) {
18250
18419
  setNavGroupForceOpen(null, false);
@@ -19070,28 +19239,19 @@ function OhhwellsBridge() {
19070
19239
  return;
19071
19240
  }
19072
19241
  const existing = editStylesRef.current;
19073
- let initialVh = window.innerHeight;
19074
- if (existing?.base.textContent) {
19075
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
19076
- if (match) initialVh = parseInt(match[1], 10);
19077
- }
19242
+ const canvasHeight = "var(--ohw-canvas-h, 852px)";
19078
19243
  const baseCss = `
19079
19244
  html { height: auto !important; }
19080
19245
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
19081
- .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
19082
- .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
19083
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19084
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19085
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19086
- /* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
19087
- build a full-viewport section that still grows for its own content \u2014 matches the three
19088
- rules above on substring alone, since the fallback text contains "100svh" too. Forcing
19089
- a literal height on top of that turns "at least one screen" into "exactly one screen",
19090
- so content taller than one screen (a long mobile hero, say) overflows a centered flex
19091
- column upward, under whatever sits above it. Only the min-height half belongs to it. */
19092
- [style*="min-height"][style*="100vh"],
19093
- [style*="min-height"][style*="100svh"],
19094
- [style*="min-height"][style*="100dvh"] { height: auto !important; }
19246
+ .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${canvasHeight} !important; }
19247
+ .h-screen, .h-svh, .h-dvh { height: ${canvasHeight} !important; }
19248
+ /* Literal inline viewport units only \u2014 skip R11 sections that already reference --ohw-canvas-h
19249
+ (their style attribute contains "100svh" only as a var() fallback string). */
19250
+ [style*="100vh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19251
+ [style*="100svh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19252
+ [style*="100dvh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19253
+ /* R11: min-height via --ohw-canvas-h; height must grow with content taller than one screen. */
19254
+ [style*="min-height"][style*="--ohw-canvas-h"] { height: auto !important; }
19095
19255
  /* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
19096
19256
  couple of pixels, and getting back into it meant hunting for the caret with the mouse.
19097
19257
  Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
@@ -19238,6 +19398,10 @@ function OhhwellsBridge() {
19238
19398
  outline-offset: 0 !important;
19239
19399
  box-shadow: none !important;
19240
19400
  }
19401
+ /* Open nav dropdown panels paint above AI section selection chrome (2147483000). */
19402
+ [data-ohw-nav-group][data-ohw-nav-force-open] > [data-ohw-nav-children] {
19403
+ z-index: 2147483100 !important;
19404
+ }
19241
19405
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
19242
19406
  [data-ohw-editing],
19243
19407
  [data-ohw-editing] *,
@@ -20575,7 +20739,11 @@ function OhhwellsBridge() {
20575
20739
  const slotFromItem = hrefKey ? findSocialByHrefKey(hrefKey)?.querySelector('[data-ohw-editable="icon"]') : null;
20576
20740
  const requestedIsIconSlot = Boolean(requestedIconKey) && Boolean(document.querySelector(`[data-ohw-key="${requestedIconKey}"][data-ohw-editable="icon"]`));
20577
20741
  const requestedIsFree = Boolean(requestedIconKey) && !document.querySelector(`[data-ohw-key="${requestedIconKey}"]`);
20578
- const iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20742
+ let iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20743
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
20744
+ if (item && typeof iconMarkup === "string" && iconMarkup) {
20745
+ iconKey = ensureIconSlot(item) ?? iconKey;
20746
+ }
20579
20747
  if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
20580
20748
  document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
20581
20749
  applyIconMarkup(el, iconMarkup);
@@ -20585,7 +20753,7 @@ function OhhwellsBridge() {
20585
20753
  }
20586
20754
  if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
20587
20755
  if (iconKey && label) {
20588
- const labelKey = socialLabelKey(iconKey);
20756
+ const labelKey = (item ? socialLabelElement(item)?.getAttribute("data-ohw-key") : null) ?? socialLabelKey(iconKey);
20589
20757
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
20590
20758
  if (keepLabel && el.textContent?.trim()) return;
20591
20759
  el.textContent = label;
@@ -21411,7 +21579,12 @@ function OhhwellsBridge() {
21411
21579
  }
21412
21580
  if (navDragRef.current) {
21413
21581
  const session = navDragRef.current;
21414
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
21582
+ const slot = hitTestNavDropSlot(
21583
+ session.lastClientX,
21584
+ session.lastClientY,
21585
+ session.hrefKey,
21586
+ session.draggedEl
21587
+ );
21415
21588
  refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
21416
21589
  }
21417
21590
  if (hoveredImageRef.current) {
@@ -21617,18 +21790,21 @@ function OhhwellsBridge() {
21617
21790
  }
21618
21791
  return null;
21619
21792
  };
21793
+ const isPointInRect = (el, clientX, clientY) => {
21794
+ const r2 = el.getBoundingClientRect();
21795
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
21796
+ };
21620
21797
  const isPointOverEditable = (scope, clientX, clientY) => {
21621
21798
  const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
21622
21799
  for (const el of editables) {
21623
- const r2 = el.getBoundingClientRect();
21624
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
21800
+ if (isPointInRect(el, clientX, clientY)) return true;
21625
21801
  }
21626
21802
  return false;
21627
21803
  };
21628
21804
  const handleCarouselHover = (e) => {
21629
21805
  const container = findCarouselAtPoint(e.clientX, e.clientY);
21630
21806
  const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
21631
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
21807
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY) || isPointOverNavigation(e.clientX, e.clientY) || isPointOverBridgeChrome(e.clientX, e.clientY)) {
21632
21808
  setCarouselHover((prev) => prev ? null : prev);
21633
21809
  return;
21634
21810
  }