@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.cjs CHANGED
@@ -11191,6 +11191,17 @@ var import_react11 = require("react");
11191
11191
  var import_react_dom3 = require("react-dom");
11192
11192
  var import_lucide_react14 = require("lucide-react");
11193
11193
  var import_navigation2 = require("next/navigation");
11194
+
11195
+ // src/lib/parent-scroll.ts
11196
+ function getIframeVisibleClip(parentScroll) {
11197
+ if (!parentScroll) return null;
11198
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH, promptBarH = 0 } = parentScroll;
11199
+ const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11200
+ const clipBottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop) - promptBarH;
11201
+ return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
11202
+ }
11203
+
11204
+ // src/ui/link-modal/SectionPickerOverlay.tsx
11194
11205
  var import_jsx_runtime26 = require("react/jsx-runtime");
11195
11206
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
11196
11207
  function rectsEqual(a, b) {
@@ -11328,20 +11339,22 @@ function SectionPickerOverlay({
11328
11339
  []
11329
11340
  );
11330
11341
  (0, import_react11.useEffect)(() => {
11331
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
11332
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11333
- const bottom = Math.min(
11334
- window.innerHeight,
11335
- visibleCanvasTop + canvasH - iframeOffsetTop
11336
- );
11342
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH, promptBarH = 0) => {
11343
+ const next = getIframeVisibleClip({
11344
+ iframeOffsetTop,
11345
+ headerH: visibleCanvasTop,
11346
+ canvasH,
11347
+ promptBarH
11348
+ });
11349
+ if (!next) return;
11337
11350
  setChromeClip(
11338
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
11351
+ (prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
11339
11352
  );
11340
11353
  };
11341
11354
  const onParentScroll = (e) => {
11342
11355
  if (e.data?.type !== "ow:parent-scroll") return;
11343
- const { iframeOffsetTop, headerH, canvasH } = e.data;
11344
- applyClip(iframeOffsetTop, headerH, canvasH);
11356
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
11357
+ applyClip(iframeOffsetTop, headerH, canvasH, promptBarH);
11345
11358
  iframeOffsetTopRef.current = iframeOffsetTop;
11346
11359
  if (!pointerScreenRef.current && pointerRef.current) {
11347
11360
  pointerScreenRef.current = {
@@ -15009,13 +15022,11 @@ var EDGE_MARGIN = 16;
15009
15022
  function getVisibleClip(parentScroll) {
15010
15023
  const left = 0;
15011
15024
  const right = window.innerWidth;
15012
- if (!parentScroll) {
15025
+ const band = getIframeVisibleClip(parentScroll);
15026
+ if (!band) {
15013
15027
  return { top: 0, bottom: window.innerHeight, left, right };
15014
15028
  }
15015
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
15016
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
15017
- const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
15018
- return { top, bottom: Math.max(top, bottom), left, right };
15029
+ return { ...band, left, right };
15019
15030
  }
15020
15031
  function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
15021
15032
  const clip = getVisibleClip(parentScroll);
@@ -15346,6 +15357,30 @@ function relativeLuminance(color) {
15346
15357
  };
15347
15358
  return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
15348
15359
  }
15360
+ function contrastRatio(a, b) {
15361
+ const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
15362
+ const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
15363
+ return (lighter + 0.05) / (darker + 0.05);
15364
+ }
15365
+ function compositeOver(fg, bg) {
15366
+ const a = fg.a + bg.a * (1 - fg.a);
15367
+ if (a < 1e-3) return { ...bg, a: 0 };
15368
+ return {
15369
+ r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / a,
15370
+ g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / a,
15371
+ b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / a,
15372
+ a
15373
+ };
15374
+ }
15375
+ function previewSlotForChrome(chrome, slotBackground) {
15376
+ const text = parseColor(chrome.color);
15377
+ if (!text || text.a < 0.2) return slotBackground;
15378
+ const slot = parseColor(slotBackground) ?? { r: 250, g: 250, b: 249, a: 1 };
15379
+ const fill = parseColor(chrome.background);
15380
+ const effective = fill && !isTransparent(fill) ? compositeOver(fill, slot) : slot;
15381
+ if (contrastRatio(text, effective) >= 3) return slotBackground;
15382
+ return relativeLuminance(text) > 0.55 ? "#2c2c2c" : "#f4f4f5";
15383
+ }
15349
15384
  function parseColor(input) {
15350
15385
  if (!input) return null;
15351
15386
  const value = input.trim().toLowerCase();
@@ -15422,10 +15457,10 @@ function declaredVariant(className) {
15422
15457
  return match && isButtonVariantId(match[1]) ? match[1] : null;
15423
15458
  }
15424
15459
  function fillSlot(sample) {
15425
- if (hasOpaqueBackground(sample.background)) return "primary";
15426
15460
  const width = parseFloat(sample.borderWidth);
15427
15461
  const border = parseColor(sample.borderColor);
15428
15462
  if (width > 0 && !isTransparent(border)) return "secondary";
15463
+ if (hasOpaqueBackground(sample.background)) return "primary";
15429
15464
  return "ghost";
15430
15465
  }
15431
15466
  function sampleSlot(sample) {
@@ -15449,6 +15484,15 @@ function paintColor(chrome) {
15449
15484
  const text = parseColor(chrome.color);
15450
15485
  return text ? toCss({ ...text, a: 1 }) : chrome.color;
15451
15486
  }
15487
+ function accentColor(chrome) {
15488
+ const bg = parseColor(chrome.background);
15489
+ if (bg && !isTransparent(bg) && relativeLuminance(bg) < 0.55) {
15490
+ return toCss({ ...bg, a: 1 });
15491
+ }
15492
+ const text = parseColor(chrome.color);
15493
+ if (text && !isTransparent(text)) return toCss({ ...text, a: 1 });
15494
+ return paintColor(chrome);
15495
+ }
15452
15496
  function chromeFrom(sample) {
15453
15497
  return {
15454
15498
  background: sample.background || TRANSPARENT,
@@ -15468,7 +15512,7 @@ function filledPrimary(seed) {
15468
15512
  };
15469
15513
  }
15470
15514
  function generatedSecondary(primary) {
15471
- const paint = paintColor(primary);
15515
+ const paint = accentColor(primary);
15472
15516
  return {
15473
15517
  background: TRANSPARENT,
15474
15518
  color: paint,
@@ -15479,13 +15523,13 @@ function generatedSecondary(primary) {
15479
15523
  function generatedGhost(primary) {
15480
15524
  return {
15481
15525
  background: TRANSPARENT,
15482
- color: paintColor(primary),
15526
+ color: accentColor(primary),
15483
15527
  borderColor: TRANSPARENT,
15484
15528
  borderWidth: "0px"
15485
15529
  };
15486
15530
  }
15487
15531
  function normalizeOutlineChrome(outline, primary) {
15488
- const paint = paintColor(primary);
15532
+ const paint = accentColor(primary);
15489
15533
  const text = parseColor(outline.color);
15490
15534
  const paintRgb = parseColor(paint);
15491
15535
  const primaryLabel = parseColor(primary.color);
@@ -15721,36 +15765,49 @@ function readSizeSample(el) {
15721
15765
  gap: parsePx(style.columnGap || style.gap, rootFont)
15722
15766
  };
15723
15767
  }
15768
+ function readTypography(el) {
15769
+ const style = getComputedStyle(el);
15770
+ return {
15771
+ fontFamily: style.fontFamily,
15772
+ fontWeight: style.fontWeight,
15773
+ letterSpacing: style.letterSpacing,
15774
+ textTransform: style.textTransform,
15775
+ textDecoration: style.textDecorationLine || style.textDecoration || "none"
15776
+ };
15777
+ }
15724
15778
  var buttonOriginByEl = /* @__PURE__ */ new WeakMap();
15725
- function hasEditorPaint(el) {
15726
- return el.hasAttribute("data-ohw-btn-variant") || el.hasAttribute("data-ohw-btn-size") || Boolean(el.style.background || el.style.padding || el.style.height);
15779
+ var ORIGIN_ATTR = "data-ohw-btn-origin";
15780
+ function parseOriginAttr(raw) {
15781
+ if (!raw) return null;
15782
+ try {
15783
+ const value = JSON.parse(raw);
15784
+ if (!value?.variant || !value.size || !value.typography) return null;
15785
+ return value;
15786
+ } catch {
15787
+ return null;
15788
+ }
15727
15789
  }
15728
- function measureSourceOrigin(el) {
15729
- const parent = el.parentElement;
15730
- if (!parent) {
15731
- return { variant: readVariantSample(el), size: readSizeSample(el) };
15732
- }
15733
- const probe = el.cloneNode(true);
15734
- probe.removeAttribute("style");
15735
- probe.removeAttribute("data-ohw-btn-variant");
15736
- probe.removeAttribute("data-ohw-btn-size");
15737
- probe.removeAttribute("data-ohw-selected");
15738
- probe.setAttribute("data-ohw-btn-probe", "");
15739
- for (const node of probe.querySelectorAll("[style]")) {
15740
- node.removeAttribute("style");
15741
- }
15742
- probe.style.cssText = "position:absolute;left:-99999px;top:0;visibility:hidden;pointer-events:none;display:inline-flex;";
15743
- parent.insertBefore(probe, el);
15744
- void probe.offsetHeight;
15745
- const origin = { variant: readVariantSample(probe), size: readSizeSample(probe) };
15746
- probe.remove();
15747
- return origin;
15790
+ function measureLiveOrigin(el) {
15791
+ return {
15792
+ variant: readVariantSample(el),
15793
+ size: readSizeSample(el),
15794
+ typography: readTypography(el)
15795
+ };
15748
15796
  }
15749
15797
  function captureButtonOrigin(el) {
15750
15798
  const cached = buttonOriginByEl.get(el);
15751
15799
  if (cached) return cached;
15752
- const origin = hasEditorPaint(el) ? measureSourceOrigin(el) : { variant: readVariantSample(el), size: readSizeSample(el) };
15800
+ const fromAttr = parseOriginAttr(el.getAttribute(ORIGIN_ATTR));
15801
+ if (fromAttr) {
15802
+ buttonOriginByEl.set(el, fromAttr);
15803
+ return fromAttr;
15804
+ }
15805
+ const origin = measureLiveOrigin(el);
15753
15806
  buttonOriginByEl.set(el, origin);
15807
+ try {
15808
+ el.setAttribute(ORIGIN_ATTR, JSON.stringify(origin));
15809
+ } catch {
15810
+ }
15754
15811
  return origin;
15755
15812
  }
15756
15813
  function listStyleButtons(doc) {
@@ -15845,12 +15902,18 @@ function assignmentFor(el, content, tokens) {
15845
15902
  function labelElement(el) {
15846
15903
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
15847
15904
  }
15848
- function applyButtonStyle(el, assignment, tokens) {
15849
- captureButtonOrigin(el);
15905
+ var paintLockByEl = /* @__PURE__ */ new WeakMap();
15906
+ var paintObserverByEl = /* @__PURE__ */ new WeakMap();
15907
+ var paintingButton = false;
15908
+ function paintButtonChrome(el, assignment, tokens) {
15909
+ const origin = captureButtonOrigin(el);
15850
15910
  const chrome = tokens.variants[assignment.variant];
15851
- const geometry = tokens.sizes[assignment.size];
15911
+ const originSlot = classifyHeight(origin.size.height);
15912
+ const geometry = assignment.size === originSlot ? geometryFromSample({ ...origin.size, borderRadius: origin.size.borderRadius, gap: origin.size.gap }) : tokens.sizes[assignment.size];
15913
+ const { typography } = origin;
15852
15914
  el.setAttribute("data-ohw-btn-variant", assignment.variant);
15853
15915
  el.setAttribute("data-ohw-btn-size", assignment.size);
15916
+ el.style.transition = "none";
15854
15917
  el.style.boxSizing = "border-box";
15855
15918
  el.style.display = "inline-flex";
15856
15919
  el.style.alignItems = "center";
@@ -15862,12 +15925,41 @@ function applyButtonStyle(el, assignment, tokens) {
15862
15925
  el.style.borderWidth = chrome.borderWidth;
15863
15926
  el.style.padding = `${geometry.paddingTop} ${geometry.paddingRight} ${geometry.paddingBottom} ${geometry.paddingLeft}`;
15864
15927
  el.style.fontSize = geometry.fontSize;
15865
- el.style.borderRadius = geometry.borderRadius;
15928
+ el.style.borderRadius = px(origin.size.borderRadius);
15866
15929
  el.style.gap = geometry.gap;
15867
15930
  el.style.height = px(geometry.height);
15931
+ el.style.fontFamily = typography.fontFamily;
15932
+ el.style.fontWeight = typography.fontWeight;
15933
+ el.style.letterSpacing = typography.letterSpacing;
15934
+ el.style.textTransform = typography.textTransform;
15935
+ if (typography.textDecoration && typography.textDecoration !== "none") {
15936
+ el.style.textDecoration = typography.textDecoration;
15937
+ } else {
15938
+ el.style.textDecoration = "none";
15939
+ }
15868
15940
  const label = labelElement(el);
15869
15941
  if (label && label !== el) label.style.color = chrome.color;
15870
15942
  }
15943
+ function guardTemplateHover(el) {
15944
+ if (paintObserverByEl.has(el)) return;
15945
+ const observer = new MutationObserver(() => {
15946
+ if (paintingButton) return;
15947
+ const lock = paintLockByEl.get(el);
15948
+ if (!lock) return;
15949
+ paintingButton = true;
15950
+ paintButtonChrome(el, lock.assignment, lock.tokens);
15951
+ paintingButton = false;
15952
+ });
15953
+ observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
15954
+ paintObserverByEl.set(el, observer);
15955
+ }
15956
+ function applyButtonStyle(el, assignment, tokens) {
15957
+ paintLockByEl.set(el, { assignment, tokens });
15958
+ paintingButton = true;
15959
+ paintButtonChrome(el, assignment, tokens);
15960
+ paintingButton = false;
15961
+ guardTemplateHover(el);
15962
+ }
15871
15963
  function applyButtonStylesFromContent(doc, content) {
15872
15964
  const assignments = readAssignments(content);
15873
15965
  for (const [id, assignment] of Object.entries(assignments)) {
@@ -15902,6 +15994,7 @@ function ButtonStylePanel({
15902
15994
  const chrome = variants[id];
15903
15995
  const selected = id === variant;
15904
15996
  const hasBorder = parseFloat(chrome.borderWidth) > 0;
15997
+ const cardSlot = previewSlotForChrome(chrome, slotBackground);
15905
15998
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
15906
15999
  "button",
15907
16000
  {
@@ -15922,7 +16015,7 @@ function ButtonStylePanel({
15922
16015
  "div",
15923
16016
  {
15924
16017
  className: "flex w-full items-center justify-center py-5",
15925
- style: { background: slotBackground },
16018
+ style: { background: cardSlot },
15926
16019
  children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
15927
16020
  "span",
15928
16021
  {
@@ -16056,6 +16149,9 @@ function isLastNavbarButton(el) {
16056
16149
  function canDeleteButton(el) {
16057
16150
  return !isLastNavbarButton(el);
16058
16151
  }
16152
+ function canDuplicateButton(el) {
16153
+ return isNavbarCta(el);
16154
+ }
16059
16155
  function labelElement2(el) {
16060
16156
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? el.querySelector("[data-ohw-key]");
16061
16157
  }
@@ -16110,12 +16206,27 @@ function nextCollectionId(content, doc) {
16110
16206
  while (used.has(`bc-${n}`)) n += 1;
16111
16207
  return `bc-${n}`;
16112
16208
  }
16209
+ var COLLECTION_GAP_MAX_PX = 12;
16210
+ var COLLECTION_GAP_DEFAULT = "12px";
16211
+ function usableGap(value) {
16212
+ if (!value || value === "normal") return null;
16213
+ const n = parseFloat(value);
16214
+ if (!Number.isFinite(n) || n <= 0) return null;
16215
+ if (n > COLLECTION_GAP_MAX_PX) return COLLECTION_GAP_DEFAULT;
16216
+ return `${Math.round(n * 100) / 100}px`;
16217
+ }
16113
16218
  function collectionGap(el, tokens) {
16114
- if (tokens) return tokens.sizes.m.gap;
16219
+ const fromCollection = usableGap(buttonCollectionOf(el)?.style.gap);
16220
+ if (fromCollection) return fromCollection;
16115
16221
  const parent = el.parentElement;
16116
- if (!parent) return "12px";
16117
- const gap = getComputedStyle(parent).columnGap || getComputedStyle(parent).gap;
16118
- return gap && gap !== "normal" ? gap : "12px";
16222
+ if (parent) {
16223
+ const style = getComputedStyle(parent);
16224
+ const fromParent = usableGap(style.columnGap) ?? usableGap(style.gap);
16225
+ if (fromParent) return fromParent;
16226
+ }
16227
+ const fromTokens = usableGap(tokens?.sizes.m.gap);
16228
+ if (fromTokens) return fromTokens;
16229
+ return COLLECTION_GAP_DEFAULT;
16119
16230
  }
16120
16231
  function styleCollection(collection, gap) {
16121
16232
  collection.style.display = "inline-flex";
@@ -16163,6 +16274,7 @@ function findButton(doc, id) {
16163
16274
  return doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
16164
16275
  }
16165
16276
  function duplicateButton(source, content) {
16277
+ if (!canDuplicateButton(source)) return null;
16166
16278
  const sourceId = buttonId(source);
16167
16279
  if (!sourceId) return null;
16168
16280
  const doc = source.ownerDocument;
@@ -16181,7 +16293,7 @@ function duplicateButton(source, content) {
16181
16293
  const collections = readCollections(content);
16182
16294
  let collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
16183
16295
  let members = collectionId && collections[collectionId] ? [...collections[collectionId].members] : [];
16184
- const gap = collection?.style.gap || collectionGap(source, tokens);
16296
+ const gap = collectionGap(source, tokens);
16185
16297
  if (!collection) {
16186
16298
  collectionId = nextCollectionId(content, doc);
16187
16299
  collection = createCollection(doc, collectionId, gap);
@@ -17167,12 +17279,6 @@ var SECTION_DRAG_EXCLUDED_SELECTOR = [
17167
17279
  "[data-ohw-media-overlay]",
17168
17280
  "[data-ohw-media-skeleton]"
17169
17281
  ].join(", ");
17170
- function visibleClip(ps) {
17171
- if (!ps) return null;
17172
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
17173
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
17174
- return { top, bottom: Math.max(top, bottom) };
17175
- }
17176
17282
  function useSectionDrag({
17177
17283
  isEditMode,
17178
17284
  editContentRef,
@@ -17209,7 +17315,7 @@ function useSectionDrag({
17209
17315
  }, [postToParentRef, stopAutoScroll]);
17210
17316
  const updateAutoScroll = (0, import_react16.useCallback)(
17211
17317
  (clientY) => {
17212
- const clip = visibleClip(parentScrollRef.current);
17318
+ const clip = getIframeVisibleClip(parentScrollRef.current);
17213
17319
  let delta = 0;
17214
17320
  if (clip) {
17215
17321
  const distTop = clientY - clip.top;
@@ -17621,7 +17727,14 @@ function collectEditableNodes(extraContent, root = document) {
17621
17727
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
17622
17728
  // had gained an item, and the published page went back to the template's own (OHH-736).
17623
17729
  SOCIALS_ORDER_KEY,
17624
- SOCIALS_DISPLAY_KEY
17730
+ SOCIALS_DISPLAY_KEY,
17731
+ // Button variant/size tokens and per-CTA assignments — JSON in editContent, applied on hydrate.
17732
+ BUTTON_VARIANTS_KEY,
17733
+ BUTTON_SIZES_KEY,
17734
+ BUTTON_ASSIGNMENTS_KEY,
17735
+ BUTTON_COLLECTIONS_KEY,
17736
+ BUTTON_SNAPSHOTS_KEY,
17737
+ BUTTON_HIDDEN_KEY
17625
17738
  ]) {
17626
17739
  const text = extraContent[key];
17627
17740
  if (typeof text === "string" && text.length > 0) {
@@ -19111,16 +19224,6 @@ function EditGlowChrome({
19111
19224
  }
19112
19225
  );
19113
19226
  }
19114
- function getIframeVisibleClip(parentScroll) {
19115
- if (!parentScroll) return null;
19116
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
19117
- const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
19118
- const clipBottom = Math.min(
19119
- window.innerHeight,
19120
- visibleCanvasTop + canvasH - iframeOffsetTop
19121
- );
19122
- return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
19123
- }
19124
19227
  function isRectOutsideClip(rect, clip) {
19125
19228
  if (!clip) return false;
19126
19229
  return rect.bottom < clip.top || rect.top > clip.bottom;
@@ -24869,10 +24972,10 @@ function OhhwellsBridge() {
24869
24972
  };
24870
24973
  const handleParentScroll = (e) => {
24871
24974
  if (e.data?.type !== "ow:parent-scroll") return;
24872
- const { iframeOffsetTop, headerH, canvasH } = e.data;
24873
- parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
24975
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
24976
+ parentScrollRef.current = { iframeOffsetTop, headerH, canvasH, promptBarH };
24874
24977
  if (floatingPanelOpenRef.current) {
24875
- setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
24978
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH, promptBarH });
24876
24979
  }
24877
24980
  if (visibleViewportRef.current) {
24878
24981
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -26165,7 +26268,7 @@ function OhhwellsBridge() {
26165
26268
  const social = getSocialItem(selectedElRef.current);
26166
26269
  return social ? !canRemoveSocialItem(social) : false;
26167
26270
  })(),
26168
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
26271
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDuplicateButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
26169
26272
  const social = getSocialItem(selectedElRef.current);
26170
26273
  const row = social ? findSocialsRow(social) : null;
26171
26274
  return row ? !canAddSocialItem(row) : false;