@ohhwells/bridge 0.1.100 → 0.1.102

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
@@ -1380,7 +1380,12 @@ function ButtonEl({
1380
1380
  textDecoration: "none",
1381
1381
  cursor: "pointer",
1382
1382
  ...buttonShellStyle(ctx, fullWidth),
1383
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1383
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : (
1384
+ // Off-band, prefer the template button's measured fill/label pair — a template may
1385
+ // fill its CTAs with any token (hvac: accent bg, primary text). On an accent band
1386
+ // the flipped palette keeps contrast, so the brand-driven colours stay.
1387
+ !ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1388
+ )
1384
1389
  },
1385
1390
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1386
1391
  }
@@ -2452,10 +2457,12 @@ function renderNode(node, ctx, path) {
2452
2457
  cursor: "pointer",
2453
2458
  // Shape/padding/typography follow the host template's own buttons.
2454
2459
  ...buttonShellStyle(ctx),
2455
- // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
2456
- // reads correctly on custom palettes.
2457
- background: ctx.brand.palette.primary,
2458
- color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2460
+ // Brand-styled: the template button's measured fill/label pair off-band, else
2461
+ // primary fill with a brand-derived label so it reads on custom palettes.
2462
+ ...!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2463
+ background: ctx.brand.palette.primary,
2464
+ color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2465
+ }
2459
2466
  },
2460
2467
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2461
2468
  },
@@ -2662,11 +2669,19 @@ function deriveTemplateBrand() {
2662
2669
  }
2663
2670
  function deriveTemplateButtonStyle() {
2664
2671
  if (typeof document === "undefined") return null;
2665
- const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
2666
- (el) => !el.closest(`[${CONTAINER_ATTR}]`)
2667
- );
2672
+ const candidates = Array.from(
2673
+ document.querySelectorAll('[data-ohw-role="button"]')
2674
+ ).filter((el) => !el.closest(`[${CONTAINER_ATTR}]`));
2675
+ const isFilled = (el) => {
2676
+ const bg = getComputedStyle(el).backgroundColor;
2677
+ if (!bg || bg === "transparent") return false;
2678
+ const alpha = bg.match(/rgba?\([^)]*,\s*([\d.]+)\)$/);
2679
+ return !alpha || parseFloat(alpha[1]) > 0;
2680
+ };
2681
+ const btn = candidates.find(isFilled) ?? candidates[0];
2668
2682
  if (!btn) return null;
2669
2683
  const cs = getComputedStyle(btn);
2684
+ const filled = isFilled(btn);
2670
2685
  const corners = [
2671
2686
  cs.borderTopLeftRadius,
2672
2687
  cs.borderTopRightRadius,
@@ -2683,6 +2698,7 @@ function deriveTemplateButtonStyle() {
2683
2698
  return {
2684
2699
  radius: radius || "10px",
2685
2700
  padding: `${padY}px ${padX}px`,
2701
+ ...filled ? { background: cs.backgroundColor, color: cs.color } : {},
2686
2702
  fontFamily: cs.fontFamily || "",
2687
2703
  fontSize: cs.fontSize || "",
2688
2704
  fontWeight: cs.fontWeight || "",
@@ -7500,9 +7516,7 @@ function ItemActionToolbar({
7500
7516
  ToggleGroup,
7501
7517
  {
7502
7518
  value: dropdownOpen ? "open" : "closed",
7503
- onValueChange: (value) => {
7504
- if (value !== "open" && value !== "closed") return;
7505
- onDropdownOpenChange?.(value === "open");
7519
+ onValueChange: () => {
7506
7520
  },
7507
7521
  className: "h-7 gap-0.5 rounded-[calc(var(--radius,0.5rem)-2px)] bg-muted p-0.5",
7508
7522
  onMouseDown: (e) => {
@@ -7517,6 +7531,11 @@ function ItemActionToolbar({
7517
7531
  size: "sm",
7518
7532
  "aria-label": "Closed",
7519
7533
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7534
+ onMouseDown: (e) => {
7535
+ e.preventDefault();
7536
+ e.stopPropagation();
7537
+ if (dropdownOpen !== false) onDropdownOpenChange?.(false);
7538
+ },
7520
7539
  children: "Closed"
7521
7540
  }
7522
7541
  ),
@@ -7527,6 +7546,11 @@ function ItemActionToolbar({
7527
7546
  size: "sm",
7528
7547
  "aria-label": "Open",
7529
7548
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7549
+ onMouseDown: (e) => {
7550
+ e.preventDefault();
7551
+ e.stopPropagation();
7552
+ if (dropdownOpen !== true) onDropdownOpenChange?.(true);
7553
+ },
7530
7554
  children: "Open"
7531
7555
  }
7532
7556
  )
@@ -8552,7 +8576,7 @@ function FieldTypePicker({ onPick }) {
8552
8576
  "div",
8553
8577
  {
8554
8578
  "data-ohw-field-type-picker": "",
8555
- className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8579
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8556
8580
  children: FIELD_TYPES.map((entry) => {
8557
8581
  const Icon = TYPE_ICONS[entry.type];
8558
8582
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
@@ -8560,7 +8584,7 @@ function FieldTypePicker({ onPick }) {
8560
8584
  {
8561
8585
  type: "button",
8562
8586
  onClick: () => onPick(entry.type),
8563
- 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",
8587
+ 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",
8564
8588
  children: [
8565
8589
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8566
8590
  entry.label
@@ -10913,6 +10937,32 @@ function getNavbarDesktopContainer() {
10913
10937
  function getNavbarDrawerContainer() {
10914
10938
  return document.querySelector("[data-ohw-nav-drawer]");
10915
10939
  }
10940
+ function isNavbarListContainerVisible(el) {
10941
+ if (el.hasAttribute("hidden")) return false;
10942
+ if (el.getClientRects().length === 0) return false;
10943
+ const style = window.getComputedStyle(el);
10944
+ return style.display !== "none" && style.visibility !== "hidden";
10945
+ }
10946
+ function getActiveNavbarListContainer(draggedEl) {
10947
+ const desktop = getNavbarDesktopContainer();
10948
+ const drawer = getNavbarDrawerContainer();
10949
+ if (draggedEl) {
10950
+ if (drawer?.contains(draggedEl)) return drawer;
10951
+ if (desktop?.contains(draggedEl)) return desktop;
10952
+ }
10953
+ if (desktop && isNavbarListContainerVisible(desktop)) return desktop;
10954
+ if (drawer && isNavbarListContainerVisible(drawer)) return drawer;
10955
+ return desktop ?? drawer;
10956
+ }
10957
+ function listActiveNavbarItems(draggedEl) {
10958
+ const container = getActiveNavbarListContainer(draggedEl);
10959
+ if (container) {
10960
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(
10961
+ isNavbarLinkItem
10962
+ );
10963
+ }
10964
+ return listNavbarItems();
10965
+ }
10916
10966
  function listNavbarItems() {
10917
10967
  const desktop = getNavbarDesktopContainer();
10918
10968
  if (desktop) {
@@ -12025,20 +12075,23 @@ function isEmptyLabelValue(value) {
12025
12075
  function applyStoredValues(item, content) {
12026
12076
  const hrefKey = socialHrefKey(item);
12027
12077
  const iconKey = socialIconKey(item);
12078
+ const baseKey = hrefKey?.replace(/-href$/, "");
12028
12079
  if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
12029
- if (iconKey) {
12080
+ const glyphKey = iconKey ?? baseKey ?? null;
12081
+ const glyphStored = glyphKey ? content[glyphKey]?.trim() : void 0;
12082
+ if (glyphStored) {
12083
+ ensureIconSlot(item);
12030
12084
  const glyph = item.querySelector(ICON_SELECTOR);
12031
- if (glyph && content[iconKey]) {
12032
- applyIconMarkup(glyph, content[iconKey]);
12085
+ if (glyph) {
12086
+ applyIconMarkup(glyph, glyphStored);
12033
12087
  glyph.removeAttribute(SOCIALS_ICON_PLACEHOLDER_ATTR);
12034
12088
  }
12035
- const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
12036
- label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
12037
- const stored = content[socialLabelKey(iconKey)];
12038
- if (label && stored !== void 0) {
12039
- const words = isEmptyLabelValue(stored) ? "" : stored;
12040
- if (label.innerHTML !== words) label.innerHTML = words;
12041
- }
12089
+ }
12090
+ const label = socialLabelElement(item);
12091
+ const labelKey = label?.getAttribute("data-ohw-key");
12092
+ if (label && labelKey && content[labelKey] !== void 0) {
12093
+ const words = isEmptyLabelValue(content[labelKey]) ? "" : content[labelKey];
12094
+ if (label.innerHTML !== words) label.innerHTML = words;
12042
12095
  }
12043
12096
  }
12044
12097
  function socialPlatformKey(iconKey) {
@@ -12127,6 +12180,9 @@ function insertSocialItem(row, after, content = {}, { placeholder = true, keys }
12127
12180
  if (placeholder) {
12128
12181
  const label = socialLabelElement(item);
12129
12182
  if (label) label.textContent = PLACEHOLDER_SOCIAL_LABEL;
12183
+ } else if (keys) {
12184
+ const label = socialLabelElement(item);
12185
+ if (label) label.textContent = "";
12130
12186
  }
12131
12187
  applySocialsDisplayToRow(row, display);
12132
12188
  return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
@@ -13983,8 +14039,8 @@ var lockFooterDuringDrag = lockItemDuringDrag;
13983
14039
  var unlockFooterDragInteraction = unlockItemDragInteraction;
13984
14040
 
13985
14041
  // src/lib/nav-dnd.ts
13986
- function listReorderableNavItems() {
13987
- return listNavbarItems().filter((el) => {
14042
+ function listReorderableNavItems(draggedEl) {
14043
+ return listActiveNavbarItems(draggedEl).filter((el) => {
13988
14044
  const key = el.getAttribute("data-ohw-href-key");
13989
14045
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
13990
14046
  });
@@ -13999,8 +14055,8 @@ function resolveParentNavHrefKey(child) {
13999
14055
  const key = trigger?.getAttribute("data-ohw-href-key");
14000
14056
  return key && isNavbarHrefKey(key) ? key : null;
14001
14057
  }
14002
- function listNavChildren(parentHrefKey) {
14003
- const items = listNavbarItems();
14058
+ function listNavChildren(parentHrefKey, draggedEl) {
14059
+ const items = listActiveNavbarItems(draggedEl);
14004
14060
  const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
14005
14061
  if (!parent) return [];
14006
14062
  const group = parent.closest("[data-ohw-nav-group]");
@@ -14010,20 +14066,83 @@ function listNavChildren(parentHrefKey) {
14010
14066
  (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
14011
14067
  );
14012
14068
  }
14013
- function activeNavListContainer() {
14014
- const desktop = getNavbarDesktopContainer();
14015
- if (desktop && desktop.getClientRects().length > 0) {
14016
- const style = window.getComputedStyle(desktop);
14017
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
14069
+ function isVerticalNavLayout(items, draggedEl) {
14070
+ const visible = items.filter((el) => el.getClientRects().length > 0);
14071
+ if (visible.length >= 2) {
14072
+ const a = visible[0].getBoundingClientRect();
14073
+ const b = visible[1].getBoundingClientRect();
14074
+ const verticalGap = b.top - a.bottom;
14075
+ const horizontalGap = b.left - a.right;
14076
+ return verticalGap > horizontalGap;
14077
+ }
14078
+ const container = getActiveNavbarListContainer(draggedEl);
14079
+ if (!container) return false;
14080
+ const style = window.getComputedStyle(container);
14081
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
14082
+ }
14083
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
14084
+ const slots = [];
14085
+ const barThickness = 3;
14086
+ if (items.length === 0) {
14087
+ const container = getActiveNavbarListContainer(draggedEl);
14088
+ if (!container) return slots;
14089
+ const rect = container.getBoundingClientRect();
14090
+ slots.push({
14091
+ insertIndex: 0,
14092
+ parentId: null,
14093
+ left: rect.left,
14094
+ top: rect.top + rect.height / 2 - barThickness / 2,
14095
+ width: Math.max(rect.width, 40),
14096
+ height: barThickness,
14097
+ direction: "horizontal"
14098
+ });
14099
+ return slots;
14100
+ }
14101
+ const edgeGap = (() => {
14102
+ if (items.length < 2) return 16;
14103
+ const a = items[0].getBoundingClientRect();
14104
+ const b = items[1].getBoundingClientRect();
14105
+ return Math.max(0, b.top - a.bottom);
14106
+ })();
14107
+ for (let i = 0; i <= items.length; i++) {
14108
+ let top;
14109
+ let width;
14110
+ let left;
14111
+ if (i === 0) {
14112
+ const first = items[0].getBoundingClientRect();
14113
+ top = first.top - edgeGap / 2 - barThickness / 2;
14114
+ left = first.left;
14115
+ width = first.width;
14116
+ } else if (i === items.length) {
14117
+ const last = items[items.length - 1].getBoundingClientRect();
14118
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
14119
+ top = last.bottom + lastGap / 2 - barThickness / 2;
14120
+ left = last.left;
14121
+ width = last.width;
14122
+ } else {
14123
+ const prev = items[i - 1].getBoundingClientRect();
14124
+ const next = items[i].getBoundingClientRect();
14125
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
14126
+ left = Math.min(prev.left, next.left);
14127
+ width = Math.max(prev.right, next.right) - left;
14128
+ }
14129
+ slots.push({
14130
+ insertIndex: i,
14131
+ parentId: null,
14132
+ left,
14133
+ top,
14134
+ width: Math.max(width, 40),
14135
+ height: barThickness,
14136
+ direction: "horizontal"
14137
+ });
14018
14138
  }
14019
- return getNavbarDrawerContainer();
14139
+ return slots;
14020
14140
  }
14021
- function buildRootNavDropSlots() {
14022
- const items = listReorderableNavItems();
14141
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
14023
14142
  const slots = [];
14024
14143
  const barThickness = 3;
14025
14144
  if (items.length === 0) {
14026
- const container = activeNavListContainer();
14145
+ const container = getActiveNavbarListContainer(draggedEl);
14027
14146
  if (!container) return slots;
14028
14147
  const rect = container.getBoundingClientRect();
14029
14148
  slots.push({
@@ -14054,10 +14173,7 @@ function buildRootNavDropSlots() {
14054
14173
  height = first.height;
14055
14174
  } else if (i === items.length) {
14056
14175
  const last = items[items.length - 1].getBoundingClientRect();
14057
- const lastGap = items.length >= 2 ? Math.max(
14058
- 0,
14059
- last.left - items[items.length - 2].getBoundingClientRect().right
14060
- ) : edgeGap;
14176
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
14061
14177
  left = last.right + lastGap / 2 - barThickness / 2;
14062
14178
  top = last.top;
14063
14179
  height = last.height;
@@ -14080,8 +14196,15 @@ function buildRootNavDropSlots() {
14080
14196
  }
14081
14197
  return slots;
14082
14198
  }
14083
- function buildChildNavDropSlots(parentHrefKey) {
14084
- const children = listNavChildren(parentHrefKey);
14199
+ function buildRootNavDropSlots(draggedEl) {
14200
+ const items = listReorderableNavItems(draggedEl);
14201
+ if (isVerticalNavLayout(items, draggedEl)) {
14202
+ return buildVerticalRootNavDropSlots(items, draggedEl);
14203
+ }
14204
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
14205
+ }
14206
+ function buildChildNavDropSlots(parentHrefKey, draggedEl) {
14207
+ const children = listNavChildren(parentHrefKey, draggedEl);
14085
14208
  const slots = [];
14086
14209
  const barThickness = 3;
14087
14210
  if (children.length === 0) return slots;
@@ -14102,10 +14225,7 @@ function buildChildNavDropSlots(parentHrefKey) {
14102
14225
  width = first.width;
14103
14226
  } else if (i === children.length) {
14104
14227
  const last = children[children.length - 1].getBoundingClientRect();
14105
- const lastGap = children.length >= 2 ? Math.max(
14106
- 0,
14107
- last.top - children[children.length - 2].getBoundingClientRect().bottom
14108
- ) : edgeGap;
14228
+ const lastGap = children.length >= 2 ? Math.max(0, last.top - children[children.length - 2].getBoundingClientRect().bottom) : edgeGap;
14109
14229
  top = last.bottom + lastGap / 2 - barThickness / 2;
14110
14230
  left = last.left;
14111
14231
  width = last.width;
@@ -14128,18 +14248,19 @@ function buildChildNavDropSlots(parentHrefKey) {
14128
14248
  }
14129
14249
  return slots;
14130
14250
  }
14131
- function buildAllNavDropSlots(draggedHrefKey) {
14132
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14133
- if (!dragged) return buildRootNavDropSlots();
14251
+ function buildAllNavDropSlots(draggedHrefKey, draggedEl) {
14252
+ const activeItems = listActiveNavbarItems(draggedEl);
14253
+ 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);
14254
+ if (!dragged) return buildRootNavDropSlots(draggedEl);
14134
14255
  if (isNestedNavChild(dragged)) {
14135
14256
  const parentKey = resolveParentNavHrefKey(dragged);
14136
14257
  if (!parentKey) return [];
14137
- return buildChildNavDropSlots(parentKey);
14258
+ return buildChildNavDropSlots(parentKey, draggedEl);
14138
14259
  }
14139
- return buildRootNavDropSlots();
14260
+ return buildRootNavDropSlots(draggedEl);
14140
14261
  }
14141
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
14142
- const slots = buildAllNavDropSlots(draggedHrefKey);
14262
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey, draggedEl) {
14263
+ const slots = buildAllNavDropSlots(draggedHrefKey, draggedEl);
14143
14264
  let best = null;
14144
14265
  for (const slot of slots) {
14145
14266
  const cx2 = slot.left + slot.width / 2;
@@ -14190,6 +14311,12 @@ function useNavItemDrag({
14190
14311
  setIsItemDragging(false);
14191
14312
  if (keepOpenEl?.isConnected) {
14192
14313
  setNavGroupForceOpen(keepOpenEl, true);
14314
+ requestAnimationFrame(() => {
14315
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14316
+ requestAnimationFrame(() => {
14317
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14318
+ });
14319
+ });
14193
14320
  } else {
14194
14321
  setNavGroupForceOpen(null, false);
14195
14322
  }
@@ -14205,7 +14332,7 @@ function useNavItemDrag({
14205
14332
  }
14206
14333
  session.activeSlot = activeSlot;
14207
14334
  setSiblingHintRects([]);
14208
- const slots = buildAllNavDropSlots(session.hrefKey);
14335
+ const slots = buildAllNavDropSlots(session.hrefKey, session.draggedEl);
14209
14336
  setNavDropSlots(slots);
14210
14337
  const activeIdx = activeSlot ? slots.findIndex(
14211
14338
  (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
@@ -14241,7 +14368,12 @@ function useNavItemDrag({
14241
14368
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
14242
14369
  setToolbarRect(rect);
14243
14370
  }
14244
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
14371
+ const initialSlot = hitTestNavDropSlot(
14372
+ session.lastClientX,
14373
+ session.lastClientY,
14374
+ session.hrefKey,
14375
+ session.draggedEl
14376
+ );
14245
14377
  refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
14246
14378
  },
14247
14379
  [
@@ -14263,10 +14395,11 @@ function useNavItemDrag({
14263
14395
  }
14264
14396
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
14265
14397
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
14266
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
14398
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey, session.draggedEl);
14267
14399
  const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
14268
14400
  const wasSelected = session.wasSelected;
14269
14401
  const hrefKey = session.hrefKey;
14402
+ const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14270
14403
  const applySelectionAfterDrop = () => {
14271
14404
  if (!wasSelected) {
14272
14405
  deselectRef.current();
@@ -14300,6 +14433,14 @@ function useNavItemDrag({
14300
14433
  });
14301
14434
  applySelectionAfterDrop();
14302
14435
  clearNavDragVisuals();
14436
+ if (keepDropdownOpen) {
14437
+ requestAnimationFrame(() => {
14438
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14439
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14440
+ 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)}"]`);
14441
+ if (link) setNavGroupForceOpen(link, true);
14442
+ });
14443
+ }
14303
14444
  requestAnimationFrame(() => {
14304
14445
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14305
14446
  applyNavForest(planned.forest);
@@ -14339,7 +14480,7 @@ function useNavItemDrag({
14339
14480
  if (!session) return false;
14340
14481
  e.preventDefault();
14341
14482
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14342
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14483
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14343
14484
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14344
14485
  return true;
14345
14486
  },
@@ -14385,7 +14526,7 @@ function useNavItemDrag({
14385
14526
  clearTextSelection();
14386
14527
  const session = navDragRef.current;
14387
14528
  if (!session) return;
14388
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14529
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14389
14530
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14390
14531
  return;
14391
14532
  }
@@ -14401,7 +14542,8 @@ function useNavItemDrag({
14401
14542
  } catch {
14402
14543
  }
14403
14544
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14404
- if (activeElRef.current) deactivateRef.current();
14545
+ const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14546
+ if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14405
14547
  const key = pending.el.getAttribute("data-ohw-href-key");
14406
14548
  if (!key) return;
14407
14549
  beginNavDragRef.current({
@@ -14412,6 +14554,9 @@ function useNavItemDrag({
14412
14554
  lastClientY: e.clientY,
14413
14555
  activeSlot: null
14414
14556
  });
14557
+ if (activeElRef.current && isNestedDrag) {
14558
+ deactivateRef.current();
14559
+ }
14415
14560
  };
14416
14561
  const endPointerDrag = (e) => {
14417
14562
  const pending = navPointerDragRef.current;
@@ -14489,6 +14634,7 @@ function useNavItemDrag({
14489
14634
  );
14490
14635
  return {
14491
14636
  navDragRef,
14637
+ navPointerDragRef,
14492
14638
  navDropSlots,
14493
14639
  activeNavDropIndex,
14494
14640
  startNavLinkDrag,
@@ -15887,6 +16033,7 @@ function isNavDropdownPanelOpen(childrenRoot) {
15887
16033
  if (childrenRoot.closest("[data-ohw-nav-drawer]")) return true;
15888
16034
  const group = childrenRoot.closest("[data-ohw-nav-group]");
15889
16035
  if (group?.hasAttribute("data-ohw-nav-force-open")) return true;
16036
+ if (navDropdownsOpenOnClick()) return false;
15890
16037
  if (childrenRoot.clientHeight < 2 || childrenRoot.clientWidth < 2) return false;
15891
16038
  const style = window.getComputedStyle(childrenRoot);
15892
16039
  if (style.display === "none" || style.visibility === "hidden") return false;
@@ -15930,6 +16077,11 @@ function getNavigationItemAnchor(el) {
15930
16077
  function isNavigationItem2(el) {
15931
16078
  return getNavigationItemAnchor(el) !== null;
15932
16079
  }
16080
+ function isNavFooterScopedNavigationItem(el) {
16081
+ return Boolean(
16082
+ el.closest("nav, footer, [data-ohw-nav-container], [data-ohw-nav-drawer]")
16083
+ );
16084
+ }
15933
16085
  function socialWordsClicked(target, item) {
15934
16086
  const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
15935
16087
  return Boolean(text && item.contains(text));
@@ -16140,6 +16292,7 @@ function getNavigationSelectionParent(el) {
16140
16292
  }
16141
16293
  function collectNavigationItemSiblingHintRects(selected) {
16142
16294
  if (!isNavigationItem2(selected)) return [];
16295
+ if (!isNavFooterScopedNavigationItem(selected)) return [];
16143
16296
  const socialsRow = findSocialsRow(selected);
16144
16297
  if (socialsRow) {
16145
16298
  return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
@@ -17311,6 +17464,7 @@ function OhhwellsBridge() {
17311
17464
  linkPopoverSessionRef.current = linkPopover;
17312
17465
  const {
17313
17466
  navDragRef,
17467
+ navPointerDragRef,
17314
17468
  navDropSlots,
17315
17469
  activeNavDropIndex,
17316
17470
  startNavLinkDrag,
@@ -17513,7 +17667,12 @@ function OhhwellsBridge() {
17513
17667
  if (owner) persistFieldsRef.current(owner);
17514
17668
  }
17515
17669
  activeElRef.current = null;
17516
- setNavGroupForceOpen(null, false);
17670
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17671
+ selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
17672
+ );
17673
+ if (!preserveNavDropdown) {
17674
+ setNavGroupForceOpen(null, false);
17675
+ }
17517
17676
  setReorderHrefKey(null);
17518
17677
  setReorderDragDisabled(false);
17519
17678
  if (!selectedElRef.current) {
@@ -17635,7 +17794,10 @@ function OhhwellsBridge() {
17635
17794
  setSelectedIsSocialsRow(false);
17636
17795
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
17637
17796
  if (isNestedNavChild(navAnchor)) {
17638
- setNavGroupForceOpen(navAnchor, true);
17797
+ const group = navAnchor.closest("[data-ohw-nav-group]");
17798
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
17799
+ setNavGroupForceOpen(navAnchor, true);
17800
+ }
17639
17801
  setNavDropdownPreviewOpen(null);
17640
17802
  } else if (isDropdownTrigger) {
17641
17803
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -17717,6 +17879,10 @@ function OhhwellsBridge() {
17717
17879
  const handleNavDropdownOpenChange = (0, import_react17.useCallback)((open) => {
17718
17880
  const selected = selectedElRef.current;
17719
17881
  if (!selected || !isNavigationItem2(selected)) return;
17882
+ if (isNestedNavChild(selected)) return;
17883
+ const group = selected.closest("[data-ohw-nav-group]");
17884
+ const domOpen = Boolean(group?.hasAttribute("data-ohw-nav-force-open"));
17885
+ if (open === domOpen) return;
17720
17886
  setNavGroupForceOpen(selected, open);
17721
17887
  setNavDropdownPreviewOpen(open);
17722
17888
  requestAnimationFrame(() => {
@@ -18246,7 +18412,10 @@ function OhhwellsBridge() {
18246
18412
  clearHrefKeyHover(anchor);
18247
18413
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18248
18414
  if (isNestedNavChild(anchor)) {
18249
- setNavGroupForceOpen(anchor, true);
18415
+ const group = anchor.closest("[data-ohw-nav-group]");
18416
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
18417
+ setNavGroupForceOpen(anchor, true);
18418
+ }
18250
18419
  setNavDropdownPreviewOpen(null);
18251
18420
  } else if (isDropdownTrigger) {
18252
18421
  setNavGroupForceOpen(null, false);
@@ -19088,28 +19257,19 @@ function OhhwellsBridge() {
19088
19257
  return;
19089
19258
  }
19090
19259
  const existing = editStylesRef.current;
19091
- let initialVh = window.innerHeight;
19092
- if (existing?.base.textContent) {
19093
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
19094
- if (match) initialVh = parseInt(match[1], 10);
19095
- }
19260
+ const canvasHeight = "var(--ohw-canvas-h, 852px)";
19096
19261
  const baseCss = `
19097
19262
  html { height: auto !important; }
19098
19263
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
19099
- .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
19100
- .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
19101
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19102
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19103
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19104
- /* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
19105
- build a full-viewport section that still grows for its own content \u2014 matches the three
19106
- rules above on substring alone, since the fallback text contains "100svh" too. Forcing
19107
- a literal height on top of that turns "at least one screen" into "exactly one screen",
19108
- so content taller than one screen (a long mobile hero, say) overflows a centered flex
19109
- column upward, under whatever sits above it. Only the min-height half belongs to it. */
19110
- [style*="min-height"][style*="100vh"],
19111
- [style*="min-height"][style*="100svh"],
19112
- [style*="min-height"][style*="100dvh"] { height: auto !important; }
19264
+ .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${canvasHeight} !important; }
19265
+ .h-screen, .h-svh, .h-dvh { height: ${canvasHeight} !important; }
19266
+ /* Literal inline viewport units only \u2014 skip R11 sections that already reference --ohw-canvas-h
19267
+ (their style attribute contains "100svh" only as a var() fallback string). */
19268
+ [style*="100vh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19269
+ [style*="100svh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19270
+ [style*="100dvh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19271
+ /* R11: min-height via --ohw-canvas-h; height must grow with content taller than one screen. */
19272
+ [style*="min-height"][style*="--ohw-canvas-h"] { height: auto !important; }
19113
19273
  /* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
19114
19274
  couple of pixels, and getting back into it meant hunting for the caret with the mouse.
19115
19275
  Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
@@ -19256,6 +19416,10 @@ function OhhwellsBridge() {
19256
19416
  outline-offset: 0 !important;
19257
19417
  box-shadow: none !important;
19258
19418
  }
19419
+ /* Open nav dropdown panels paint above AI section selection chrome (2147483000). */
19420
+ [data-ohw-nav-group][data-ohw-nav-force-open] > [data-ohw-nav-children] {
19421
+ z-index: 2147483100 !important;
19422
+ }
19259
19423
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
19260
19424
  [data-ohw-editing],
19261
19425
  [data-ohw-editing] *,
@@ -20594,7 +20758,11 @@ function OhhwellsBridge() {
20594
20758
  const slotFromItem = hrefKey ? findSocialByHrefKey(hrefKey)?.querySelector('[data-ohw-editable="icon"]') : null;
20595
20759
  const requestedIsIconSlot = Boolean(requestedIconKey) && Boolean(document.querySelector(`[data-ohw-key="${requestedIconKey}"][data-ohw-editable="icon"]`));
20596
20760
  const requestedIsFree = Boolean(requestedIconKey) && !document.querySelector(`[data-ohw-key="${requestedIconKey}"]`);
20597
- const iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20761
+ let iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20762
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
20763
+ if (item && typeof iconMarkup === "string" && iconMarkup) {
20764
+ iconKey = ensureIconSlot(item) ?? iconKey;
20765
+ }
20598
20766
  if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
20599
20767
  document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
20600
20768
  applyIconMarkup(el, iconMarkup);
@@ -20604,7 +20772,7 @@ function OhhwellsBridge() {
20604
20772
  }
20605
20773
  if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
20606
20774
  if (iconKey && label) {
20607
- const labelKey = socialLabelKey(iconKey);
20775
+ const labelKey = (item ? socialLabelElement(item)?.getAttribute("data-ohw-key") : null) ?? socialLabelKey(iconKey);
20608
20776
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
20609
20777
  if (keepLabel && el.textContent?.trim()) return;
20610
20778
  el.textContent = label;
@@ -21423,7 +21591,12 @@ function OhhwellsBridge() {
21423
21591
  }
21424
21592
  if (navDragRef.current) {
21425
21593
  const session = navDragRef.current;
21426
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
21594
+ const slot = hitTestNavDropSlot(
21595
+ session.lastClientX,
21596
+ session.lastClientY,
21597
+ session.hrefKey,
21598
+ session.draggedEl
21599
+ );
21427
21600
  refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
21428
21601
  }
21429
21602
  if (hoveredImageRef.current) {
@@ -21625,18 +21798,21 @@ function OhhwellsBridge() {
21625
21798
  }
21626
21799
  return null;
21627
21800
  };
21801
+ const isPointInRect = (el, clientX, clientY) => {
21802
+ const r2 = el.getBoundingClientRect();
21803
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
21804
+ };
21628
21805
  const isPointOverEditable = (scope, clientX, clientY) => {
21629
21806
  const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
21630
21807
  for (const el of editables) {
21631
- const r2 = el.getBoundingClientRect();
21632
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
21808
+ if (isPointInRect(el, clientX, clientY)) return true;
21633
21809
  }
21634
21810
  return false;
21635
21811
  };
21636
21812
  const handleCarouselHover = (e) => {
21637
21813
  const container = findCarouselAtPoint(e.clientX, e.clientY);
21638
21814
  const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
21639
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
21815
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY) || isPointOverNavigation(e.clientX, e.clientY) || isPointOverBridgeChrome(e.clientX, e.clientY)) {
21640
21816
  setCarouselHover((prev) => prev ? null : prev);
21641
21817
  return;
21642
21818
  }
@@ -22085,7 +22261,7 @@ function OhhwellsBridge() {
22085
22261
  postToParent2({
22086
22262
  type: "ow:ready",
22087
22263
  version: "1",
22088
- bridgeVersion: "0.1.99",
22264
+ bridgeVersion: "0.1.101",
22089
22265
  path: pathname,
22090
22266
  nodes: collectEditableNodes(editContentRef.current),
22091
22267
  sections