@ohhwells/bridge 0.1.114-next.307 → 0.1.114-next.310

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.js CHANGED
@@ -11117,6 +11117,17 @@ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMem
11117
11117
  import { createPortal as createPortal2 } from "react-dom";
11118
11118
  import { ArrowLeft as ArrowLeft2, Check as Check2 } from "lucide-react";
11119
11119
  import { usePathname, useRouter as useRouter2 } from "next/navigation";
11120
+
11121
+ // src/lib/parent-scroll.ts
11122
+ function getIframeVisibleClip(parentScroll) {
11123
+ if (!parentScroll) return null;
11124
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH, promptBarH = 0 } = parentScroll;
11125
+ const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11126
+ const clipBottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop) - promptBarH;
11127
+ return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
11128
+ }
11129
+
11130
+ // src/ui/link-modal/SectionPickerOverlay.tsx
11120
11131
  import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
11121
11132
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
11122
11133
  function rectsEqual(a, b) {
@@ -11254,20 +11265,22 @@ function SectionPickerOverlay({
11254
11265
  []
11255
11266
  );
11256
11267
  useEffect7(() => {
11257
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
11258
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11259
- const bottom = Math.min(
11260
- window.innerHeight,
11261
- visibleCanvasTop + canvasH - iframeOffsetTop
11262
- );
11268
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH, promptBarH = 0) => {
11269
+ const next = getIframeVisibleClip({
11270
+ iframeOffsetTop,
11271
+ headerH: visibleCanvasTop,
11272
+ canvasH,
11273
+ promptBarH
11274
+ });
11275
+ if (!next) return;
11263
11276
  setChromeClip(
11264
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
11277
+ (prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
11265
11278
  );
11266
11279
  };
11267
11280
  const onParentScroll = (e) => {
11268
11281
  if (e.data?.type !== "ow:parent-scroll") return;
11269
- const { iframeOffsetTop, headerH, canvasH } = e.data;
11270
- applyClip(iframeOffsetTop, headerH, canvasH);
11282
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
11283
+ applyClip(iframeOffsetTop, headerH, canvasH, promptBarH);
11271
11284
  iframeOffsetTopRef.current = iframeOffsetTop;
11272
11285
  if (!pointerScreenRef.current && pointerRef.current) {
11273
11286
  pointerScreenRef.current = {
@@ -14941,13 +14954,11 @@ var EDGE_MARGIN = 16;
14941
14954
  function getVisibleClip(parentScroll) {
14942
14955
  const left = 0;
14943
14956
  const right = window.innerWidth;
14944
- if (!parentScroll) {
14957
+ const band = getIframeVisibleClip(parentScroll);
14958
+ if (!band) {
14945
14959
  return { top: 0, bottom: window.innerHeight, left, right };
14946
14960
  }
14947
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
14948
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
14949
- const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
14950
- return { top, bottom: Math.max(top, bottom), left, right };
14961
+ return { ...band, left, right };
14951
14962
  }
14952
14963
  function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
14953
14964
  const clip = getVisibleClip(parentScroll);
@@ -15278,6 +15289,30 @@ function relativeLuminance(color) {
15278
15289
  };
15279
15290
  return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
15280
15291
  }
15292
+ function contrastRatio(a, b) {
15293
+ const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
15294
+ const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
15295
+ return (lighter + 0.05) / (darker + 0.05);
15296
+ }
15297
+ function compositeOver(fg, bg) {
15298
+ const a = fg.a + bg.a * (1 - fg.a);
15299
+ if (a < 1e-3) return { ...bg, a: 0 };
15300
+ return {
15301
+ r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / a,
15302
+ g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / a,
15303
+ b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / a,
15304
+ a
15305
+ };
15306
+ }
15307
+ function previewSlotForChrome(chrome, slotBackground) {
15308
+ const text = parseColor(chrome.color);
15309
+ if (!text || text.a < 0.2) return slotBackground;
15310
+ const slot = parseColor(slotBackground) ?? { r: 250, g: 250, b: 249, a: 1 };
15311
+ const fill = parseColor(chrome.background);
15312
+ const effective = fill && !isTransparent(fill) ? compositeOver(fill, slot) : slot;
15313
+ if (contrastRatio(text, effective) >= 3) return slotBackground;
15314
+ return relativeLuminance(text) > 0.55 ? "#2c2c2c" : "#f4f4f5";
15315
+ }
15281
15316
  function parseColor(input) {
15282
15317
  if (!input) return null;
15283
15318
  const value = input.trim().toLowerCase();
@@ -15354,10 +15389,10 @@ function declaredVariant(className) {
15354
15389
  return match && isButtonVariantId(match[1]) ? match[1] : null;
15355
15390
  }
15356
15391
  function fillSlot(sample) {
15357
- if (hasOpaqueBackground(sample.background)) return "primary";
15358
15392
  const width = parseFloat(sample.borderWidth);
15359
15393
  const border = parseColor(sample.borderColor);
15360
15394
  if (width > 0 && !isTransparent(border)) return "secondary";
15395
+ if (hasOpaqueBackground(sample.background)) return "primary";
15361
15396
  return "ghost";
15362
15397
  }
15363
15398
  function sampleSlot(sample) {
@@ -15381,6 +15416,15 @@ function paintColor(chrome) {
15381
15416
  const text = parseColor(chrome.color);
15382
15417
  return text ? toCss({ ...text, a: 1 }) : chrome.color;
15383
15418
  }
15419
+ function accentColor(chrome) {
15420
+ const bg = parseColor(chrome.background);
15421
+ if (bg && !isTransparent(bg) && relativeLuminance(bg) < 0.55) {
15422
+ return toCss({ ...bg, a: 1 });
15423
+ }
15424
+ const text = parseColor(chrome.color);
15425
+ if (text && !isTransparent(text)) return toCss({ ...text, a: 1 });
15426
+ return paintColor(chrome);
15427
+ }
15384
15428
  function chromeFrom(sample) {
15385
15429
  return {
15386
15430
  background: sample.background || TRANSPARENT,
@@ -15400,7 +15444,7 @@ function filledPrimary(seed) {
15400
15444
  };
15401
15445
  }
15402
15446
  function generatedSecondary(primary) {
15403
- const paint = paintColor(primary);
15447
+ const paint = accentColor(primary);
15404
15448
  return {
15405
15449
  background: TRANSPARENT,
15406
15450
  color: paint,
@@ -15411,13 +15455,13 @@ function generatedSecondary(primary) {
15411
15455
  function generatedGhost(primary) {
15412
15456
  return {
15413
15457
  background: TRANSPARENT,
15414
- color: paintColor(primary),
15458
+ color: accentColor(primary),
15415
15459
  borderColor: TRANSPARENT,
15416
15460
  borderWidth: "0px"
15417
15461
  };
15418
15462
  }
15419
15463
  function normalizeOutlineChrome(outline, primary) {
15420
- const paint = paintColor(primary);
15464
+ const paint = accentColor(primary);
15421
15465
  const text = parseColor(outline.color);
15422
15466
  const paintRgb = parseColor(paint);
15423
15467
  const primaryLabel = parseColor(primary.color);
@@ -15653,36 +15697,49 @@ function readSizeSample(el) {
15653
15697
  gap: parsePx(style.columnGap || style.gap, rootFont)
15654
15698
  };
15655
15699
  }
15700
+ function readTypography(el) {
15701
+ const style = getComputedStyle(el);
15702
+ return {
15703
+ fontFamily: style.fontFamily,
15704
+ fontWeight: style.fontWeight,
15705
+ letterSpacing: style.letterSpacing,
15706
+ textTransform: style.textTransform,
15707
+ textDecoration: style.textDecorationLine || style.textDecoration || "none"
15708
+ };
15709
+ }
15656
15710
  var buttonOriginByEl = /* @__PURE__ */ new WeakMap();
15657
- function hasEditorPaint(el) {
15658
- return el.hasAttribute("data-ohw-btn-variant") || el.hasAttribute("data-ohw-btn-size") || Boolean(el.style.background || el.style.padding || el.style.height);
15711
+ var ORIGIN_ATTR = "data-ohw-btn-origin";
15712
+ function parseOriginAttr(raw) {
15713
+ if (!raw) return null;
15714
+ try {
15715
+ const value = JSON.parse(raw);
15716
+ if (!value?.variant || !value.size || !value.typography) return null;
15717
+ return value;
15718
+ } catch {
15719
+ return null;
15720
+ }
15659
15721
  }
15660
- function measureSourceOrigin(el) {
15661
- const parent = el.parentElement;
15662
- if (!parent) {
15663
- return { variant: readVariantSample(el), size: readSizeSample(el) };
15664
- }
15665
- const probe = el.cloneNode(true);
15666
- probe.removeAttribute("style");
15667
- probe.removeAttribute("data-ohw-btn-variant");
15668
- probe.removeAttribute("data-ohw-btn-size");
15669
- probe.removeAttribute("data-ohw-selected");
15670
- probe.setAttribute("data-ohw-btn-probe", "");
15671
- for (const node of probe.querySelectorAll("[style]")) {
15672
- node.removeAttribute("style");
15673
- }
15674
- probe.style.cssText = "position:absolute;left:-99999px;top:0;visibility:hidden;pointer-events:none;display:inline-flex;";
15675
- parent.insertBefore(probe, el);
15676
- void probe.offsetHeight;
15677
- const origin = { variant: readVariantSample(probe), size: readSizeSample(probe) };
15678
- probe.remove();
15679
- return origin;
15722
+ function measureLiveOrigin(el) {
15723
+ return {
15724
+ variant: readVariantSample(el),
15725
+ size: readSizeSample(el),
15726
+ typography: readTypography(el)
15727
+ };
15680
15728
  }
15681
15729
  function captureButtonOrigin(el) {
15682
15730
  const cached = buttonOriginByEl.get(el);
15683
15731
  if (cached) return cached;
15684
- const origin = hasEditorPaint(el) ? measureSourceOrigin(el) : { variant: readVariantSample(el), size: readSizeSample(el) };
15732
+ const fromAttr = parseOriginAttr(el.getAttribute(ORIGIN_ATTR));
15733
+ if (fromAttr) {
15734
+ buttonOriginByEl.set(el, fromAttr);
15735
+ return fromAttr;
15736
+ }
15737
+ const origin = measureLiveOrigin(el);
15685
15738
  buttonOriginByEl.set(el, origin);
15739
+ try {
15740
+ el.setAttribute(ORIGIN_ATTR, JSON.stringify(origin));
15741
+ } catch {
15742
+ }
15686
15743
  return origin;
15687
15744
  }
15688
15745
  function listStyleButtons(doc) {
@@ -15777,12 +15834,18 @@ function assignmentFor(el, content, tokens) {
15777
15834
  function labelElement(el) {
15778
15835
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
15779
15836
  }
15780
- function applyButtonStyle(el, assignment, tokens) {
15781
- captureButtonOrigin(el);
15837
+ var paintLockByEl = /* @__PURE__ */ new WeakMap();
15838
+ var paintObserverByEl = /* @__PURE__ */ new WeakMap();
15839
+ var paintingButton = false;
15840
+ function paintButtonChrome(el, assignment, tokens) {
15841
+ const origin = captureButtonOrigin(el);
15782
15842
  const chrome = tokens.variants[assignment.variant];
15783
- const geometry = tokens.sizes[assignment.size];
15843
+ const originSlot = classifyHeight(origin.size.height);
15844
+ const geometry = assignment.size === originSlot ? geometryFromSample({ ...origin.size, borderRadius: origin.size.borderRadius, gap: origin.size.gap }) : tokens.sizes[assignment.size];
15845
+ const { typography } = origin;
15784
15846
  el.setAttribute("data-ohw-btn-variant", assignment.variant);
15785
15847
  el.setAttribute("data-ohw-btn-size", assignment.size);
15848
+ el.style.transition = "none";
15786
15849
  el.style.boxSizing = "border-box";
15787
15850
  el.style.display = "inline-flex";
15788
15851
  el.style.alignItems = "center";
@@ -15794,12 +15857,41 @@ function applyButtonStyle(el, assignment, tokens) {
15794
15857
  el.style.borderWidth = chrome.borderWidth;
15795
15858
  el.style.padding = `${geometry.paddingTop} ${geometry.paddingRight} ${geometry.paddingBottom} ${geometry.paddingLeft}`;
15796
15859
  el.style.fontSize = geometry.fontSize;
15797
- el.style.borderRadius = geometry.borderRadius;
15860
+ el.style.borderRadius = px(origin.size.borderRadius);
15798
15861
  el.style.gap = geometry.gap;
15799
15862
  el.style.height = px(geometry.height);
15863
+ el.style.fontFamily = typography.fontFamily;
15864
+ el.style.fontWeight = typography.fontWeight;
15865
+ el.style.letterSpacing = typography.letterSpacing;
15866
+ el.style.textTransform = typography.textTransform;
15867
+ if (typography.textDecoration && typography.textDecoration !== "none") {
15868
+ el.style.textDecoration = typography.textDecoration;
15869
+ } else {
15870
+ el.style.textDecoration = "none";
15871
+ }
15800
15872
  const label = labelElement(el);
15801
15873
  if (label && label !== el) label.style.color = chrome.color;
15802
15874
  }
15875
+ function guardTemplateHover(el) {
15876
+ if (paintObserverByEl.has(el)) return;
15877
+ const observer = new MutationObserver(() => {
15878
+ if (paintingButton) return;
15879
+ const lock = paintLockByEl.get(el);
15880
+ if (!lock) return;
15881
+ paintingButton = true;
15882
+ paintButtonChrome(el, lock.assignment, lock.tokens);
15883
+ paintingButton = false;
15884
+ });
15885
+ observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
15886
+ paintObserverByEl.set(el, observer);
15887
+ }
15888
+ function applyButtonStyle(el, assignment, tokens) {
15889
+ paintLockByEl.set(el, { assignment, tokens });
15890
+ paintingButton = true;
15891
+ paintButtonChrome(el, assignment, tokens);
15892
+ paintingButton = false;
15893
+ guardTemplateHover(el);
15894
+ }
15803
15895
  function applyButtonStylesFromContent(doc, content) {
15804
15896
  const assignments = readAssignments(content);
15805
15897
  for (const [id, assignment] of Object.entries(assignments)) {
@@ -15834,6 +15926,7 @@ function ButtonStylePanel({
15834
15926
  const chrome = variants[id];
15835
15927
  const selected = id === variant;
15836
15928
  const hasBorder = parseFloat(chrome.borderWidth) > 0;
15929
+ const cardSlot = previewSlotForChrome(chrome, slotBackground);
15837
15930
  return /* @__PURE__ */ jsxs20(
15838
15931
  "button",
15839
15932
  {
@@ -15854,7 +15947,7 @@ function ButtonStylePanel({
15854
15947
  "div",
15855
15948
  {
15856
15949
  className: "flex w-full items-center justify-center py-5",
15857
- style: { background: slotBackground },
15950
+ style: { background: cardSlot },
15858
15951
  children: /* @__PURE__ */ jsx30(
15859
15952
  "span",
15860
15953
  {
@@ -15988,6 +16081,9 @@ function isLastNavbarButton(el) {
15988
16081
  function canDeleteButton(el) {
15989
16082
  return !isLastNavbarButton(el);
15990
16083
  }
16084
+ function canDuplicateButton(el) {
16085
+ return isNavbarCta(el);
16086
+ }
15991
16087
  function labelElement2(el) {
15992
16088
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? el.querySelector("[data-ohw-key]");
15993
16089
  }
@@ -16042,12 +16138,27 @@ function nextCollectionId(content, doc) {
16042
16138
  while (used.has(`bc-${n}`)) n += 1;
16043
16139
  return `bc-${n}`;
16044
16140
  }
16141
+ var COLLECTION_GAP_MAX_PX = 12;
16142
+ var COLLECTION_GAP_DEFAULT = "12px";
16143
+ function usableGap(value) {
16144
+ if (!value || value === "normal") return null;
16145
+ const n = parseFloat(value);
16146
+ if (!Number.isFinite(n) || n <= 0) return null;
16147
+ if (n > COLLECTION_GAP_MAX_PX) return COLLECTION_GAP_DEFAULT;
16148
+ return `${Math.round(n * 100) / 100}px`;
16149
+ }
16045
16150
  function collectionGap(el, tokens) {
16046
- if (tokens) return tokens.sizes.m.gap;
16151
+ const fromCollection = usableGap(buttonCollectionOf(el)?.style.gap);
16152
+ if (fromCollection) return fromCollection;
16047
16153
  const parent = el.parentElement;
16048
- if (!parent) return "12px";
16049
- const gap = getComputedStyle(parent).columnGap || getComputedStyle(parent).gap;
16050
- return gap && gap !== "normal" ? gap : "12px";
16154
+ if (parent) {
16155
+ const style = getComputedStyle(parent);
16156
+ const fromParent = usableGap(style.columnGap) ?? usableGap(style.gap);
16157
+ if (fromParent) return fromParent;
16158
+ }
16159
+ const fromTokens = usableGap(tokens?.sizes.m.gap);
16160
+ if (fromTokens) return fromTokens;
16161
+ return COLLECTION_GAP_DEFAULT;
16051
16162
  }
16052
16163
  function styleCollection(collection, gap) {
16053
16164
  collection.style.display = "inline-flex";
@@ -16095,6 +16206,7 @@ function findButton(doc, id) {
16095
16206
  return doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
16096
16207
  }
16097
16208
  function duplicateButton(source, content) {
16209
+ if (!canDuplicateButton(source)) return null;
16098
16210
  const sourceId = buttonId(source);
16099
16211
  if (!sourceId) return null;
16100
16212
  const doc = source.ownerDocument;
@@ -16113,7 +16225,7 @@ function duplicateButton(source, content) {
16113
16225
  const collections = readCollections(content);
16114
16226
  let collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
16115
16227
  let members = collectionId && collections[collectionId] ? [...collections[collectionId].members] : [];
16116
- const gap = collection?.style.gap || collectionGap(source, tokens);
16228
+ const gap = collectionGap(source, tokens);
16117
16229
  if (!collection) {
16118
16230
  collectionId = nextCollectionId(content, doc);
16119
16231
  collection = createCollection(doc, collectionId, gap);
@@ -17099,12 +17211,6 @@ var SECTION_DRAG_EXCLUDED_SELECTOR = [
17099
17211
  "[data-ohw-media-overlay]",
17100
17212
  "[data-ohw-media-skeleton]"
17101
17213
  ].join(", ");
17102
- function visibleClip(ps) {
17103
- if (!ps) return null;
17104
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
17105
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
17106
- return { top, bottom: Math.max(top, bottom) };
17107
- }
17108
17214
  function useSectionDrag({
17109
17215
  isEditMode,
17110
17216
  editContentRef,
@@ -17141,7 +17247,7 @@ function useSectionDrag({
17141
17247
  }, [postToParentRef, stopAutoScroll]);
17142
17248
  const updateAutoScroll = useCallback8(
17143
17249
  (clientY) => {
17144
- const clip = visibleClip(parentScrollRef.current);
17250
+ const clip = getIframeVisibleClip(parentScrollRef.current);
17145
17251
  let delta = 0;
17146
17252
  if (clip) {
17147
17253
  const distTop = clientY - clip.top;
@@ -17553,7 +17659,14 @@ function collectEditableNodes(extraContent, root = document) {
17553
17659
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
17554
17660
  // had gained an item, and the published page went back to the template's own (OHH-736).
17555
17661
  SOCIALS_ORDER_KEY,
17556
- SOCIALS_DISPLAY_KEY
17662
+ SOCIALS_DISPLAY_KEY,
17663
+ // Button variant/size tokens and per-CTA assignments — JSON in editContent, applied on hydrate.
17664
+ BUTTON_VARIANTS_KEY,
17665
+ BUTTON_SIZES_KEY,
17666
+ BUTTON_ASSIGNMENTS_KEY,
17667
+ BUTTON_COLLECTIONS_KEY,
17668
+ BUTTON_SNAPSHOTS_KEY,
17669
+ BUTTON_HIDDEN_KEY
17557
17670
  ]) {
17558
17671
  const text = extraContent[key];
17559
17672
  if (typeof text === "string" && text.length > 0) {
@@ -19043,16 +19156,6 @@ function EditGlowChrome({
19043
19156
  }
19044
19157
  );
19045
19158
  }
19046
- function getIframeVisibleClip(parentScroll) {
19047
- if (!parentScroll) return null;
19048
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
19049
- const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
19050
- const clipBottom = Math.min(
19051
- window.innerHeight,
19052
- visibleCanvasTop + canvasH - iframeOffsetTop
19053
- );
19054
- return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
19055
- }
19056
19159
  function isRectOutsideClip(rect, clip) {
19057
19160
  if (!clip) return false;
19058
19161
  return rect.bottom < clip.top || rect.top > clip.bottom;
@@ -24801,10 +24904,10 @@ function OhhwellsBridge() {
24801
24904
  };
24802
24905
  const handleParentScroll = (e) => {
24803
24906
  if (e.data?.type !== "ow:parent-scroll") return;
24804
- const { iframeOffsetTop, headerH, canvasH } = e.data;
24805
- parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
24907
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
24908
+ parentScrollRef.current = { iframeOffsetTop, headerH, canvasH, promptBarH };
24806
24909
  if (floatingPanelOpenRef.current) {
24807
- setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
24910
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH, promptBarH });
24808
24911
  }
24809
24912
  if (visibleViewportRef.current) {
24810
24913
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -26097,7 +26200,7 @@ function OhhwellsBridge() {
26097
26200
  const social = getSocialItem(selectedElRef.current);
26098
26201
  return social ? !canRemoveSocialItem(social) : false;
26099
26202
  })(),
26100
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
26203
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDuplicateButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
26101
26204
  const social = getSocialItem(selectedElRef.current);
26102
26205
  const row = social ? findSocialsRow(social) : null;
26103
26206
  return row ? !canAddSocialItem(row) : false;