@contentful/experiences-visual-editor-react 1.17.0 → 1.17.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @contentful/experiences-visual-editor-react
2
2
 
3
+ ## Private Package Notice
4
+
5
+ *Note*: This package is not meant to be used directly by the end user. It is a dependency for the Studio Experiences packages. Changes to this package are not guaranteed to follow semantic versioning and may break without notice if used directly.
6
+
3
7
  ### Purpose
4
8
  - Handles the canvas interaction logic with dragging, hitboxes, dropzones, reparenting, and communicating with the parent frame to ensure a smooth drag and drop experience.
5
9
  - Note that this package is framework specific to React.
package/dist/index.js CHANGED
@@ -3,11 +3,12 @@ import React, { useEffect, useRef, useState, useCallback, forwardRef, useLayoutE
3
3
  import md5 from 'md5';
4
4
  import { z } from 'zod';
5
5
  import { BLOCKS } from '@contentful/rich-text-types';
6
- import { isArray, isEqual, get as get$1, omit } from 'lodash-es';
6
+ import { omit, isArray, isEqual, get as get$1 } from 'lodash-es';
7
7
  import { create } from 'zustand';
8
8
  import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd';
9
9
  import { produce } from 'immer';
10
10
  import '@contentful/rich-text-react-renderer';
11
+ import { css } from 'emotion';
11
12
  import { v4 } from 'uuid';
12
13
  import { createPortal } from 'react-dom';
13
14
  import classNames from 'classnames';
@@ -89,6 +90,44 @@ const CONTENTFUL_COMPONENTS$1 = {
89
90
  const ASSEMBLY_NODE_TYPE$1 = 'assembly';
90
91
  const ASSEMBLY_DEFAULT_CATEGORY$1 = 'Assemblies';
91
92
  const ASSEMBLY_BLOCK_NODE_TYPE$1 = 'assemblyBlock';
93
+ const CF_STYLE_ATTRIBUTES = [
94
+ 'cfVisibility',
95
+ 'cfHorizontalAlignment',
96
+ 'cfVerticalAlignment',
97
+ 'cfMargin',
98
+ 'cfPadding',
99
+ 'cfBackgroundColor',
100
+ 'cfWidth',
101
+ 'cfMaxWidth',
102
+ 'cfHeight',
103
+ 'cfImageAsset',
104
+ 'cfImageOptions',
105
+ 'cfBackgroundImageUrl',
106
+ 'cfBackgroundImageOptions',
107
+ 'cfFlexDirection',
108
+ 'cfFlexWrap',
109
+ 'cfBorder',
110
+ 'cfBorderRadius',
111
+ 'cfGap',
112
+ 'cfFontSize',
113
+ 'cfFontWeight',
114
+ 'cfLineHeight',
115
+ 'cfLetterSpacing',
116
+ 'cfTextColor',
117
+ 'cfTextAlign',
118
+ 'cfTextTransform',
119
+ 'cfTextBold',
120
+ 'cfTextItalic',
121
+ 'cfTextUnderline',
122
+ // For backwards compatibility
123
+ // we need to keep those in this constant array
124
+ // so that omit() in <VisualEditorBlock> and <CompositionBlock>
125
+ // can filter them out and not pass as props
126
+ 'cfBackgroundImageScaling',
127
+ 'cfBackgroundImageAlignment',
128
+ 'cfBackgroundImageAlignmentVertical',
129
+ 'cfBackgroundImageAlignmentHorizontal',
130
+ ];
92
131
  const EMPTY_CONTAINER_HEIGHT$1 = '80px';
93
132
  const DEFAULT_IMAGE_WIDTH = '500px';
94
133
  var PostMessageMethods$2;
@@ -1282,7 +1321,7 @@ const transformMedia = (asset, variables, resolveDesignValue, variableName, path
1282
1321
  return;
1283
1322
  }
1284
1323
  if (variableName === 'cfBackgroundImageUrl') {
1285
- const width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth');
1324
+ const width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth') || '100%';
1286
1325
  const optionsVariableName = 'cfBackgroundImageOptions';
1287
1326
  const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
1288
1327
  ? variables[optionsVariableName].valuesByBreakpoint
@@ -1731,6 +1770,13 @@ const deserializePatternVariables = ({ nodeVariables, componentInstanceProps, co
1731
1770
  return { childNodeVariable, dataSource, unboundValues };
1732
1771
  };
1733
1772
 
1773
+ const stylesToKeep = ['cfImageAsset'];
1774
+ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.includes(style));
1775
+ const propsToRemove = ['cfHyperlink', 'cfOpenInNewTab', 'cfSsrClassName'];
1776
+ const sanitizeNodeProps = (nodeProps) => {
1777
+ return omit(nodeProps, stylesToRemove, propsToRemove);
1778
+ };
1779
+
1734
1780
  const sendMessage = (eventType, data) => {
1735
1781
  if (typeof window === 'undefined') {
1736
1782
  return;
@@ -2346,44 +2392,6 @@ const ASSEMBLY_NODE_TYPE = 'assembly';
2346
2392
  const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
2347
2393
  const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
2348
2394
  const ASSEMBLY_NODE_TYPES = [ASSEMBLY_NODE_TYPE, ASSEMBLY_BLOCK_NODE_TYPE];
2349
- const CF_STYLE_ATTRIBUTES = [
2350
- 'cfVisibility',
2351
- 'cfHorizontalAlignment',
2352
- 'cfVerticalAlignment',
2353
- 'cfMargin',
2354
- 'cfPadding',
2355
- 'cfBackgroundColor',
2356
- 'cfWidth',
2357
- 'cfMaxWidth',
2358
- 'cfHeight',
2359
- 'cfImageAsset',
2360
- 'cfImageOptions',
2361
- 'cfBackgroundImageUrl',
2362
- 'cfBackgroundImageOptions',
2363
- 'cfFlexDirection',
2364
- 'cfFlexWrap',
2365
- 'cfBorder',
2366
- 'cfBorderRadius',
2367
- 'cfGap',
2368
- 'cfFontSize',
2369
- 'cfFontWeight',
2370
- 'cfLineHeight',
2371
- 'cfLetterSpacing',
2372
- 'cfTextColor',
2373
- 'cfTextAlign',
2374
- 'cfTextTransform',
2375
- 'cfTextBold',
2376
- 'cfTextItalic',
2377
- 'cfTextUnderline',
2378
- // For backwards compatibility
2379
- // we need to keep those in this constant array
2380
- // so that omit() in <VisualEditorBlock> and <CompositionBlock>
2381
- // can filter them out and not pass as props
2382
- 'cfBackgroundImageScaling',
2383
- 'cfBackgroundImageAlignment',
2384
- 'cfBackgroundImageAlignmentVertical',
2385
- 'cfBackgroundImageAlignmentHorizontal',
2386
- ];
2387
2395
  const EMPTY_CONTAINER_HEIGHT = '80px';
2388
2396
  const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
2389
2397
  var PostMessageMethods$1;
@@ -3046,7 +3054,7 @@ styleInject(css_248z$5);
3046
3054
  var css_248z$4 = ".cf-text {\n white-space: pre-line;\n}\n";
3047
3055
  styleInject(css_248z$4);
3048
3056
 
3049
- var css_248z$3 = "@import url(https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;1,400;1,600&display=swap);\n\n:root {\n /* All sizing comments based of 16px base body font size */\n\n /* color */\n --cf-color-white: #fff;\n --cf-color-black: #000;\n --cf-color-gray100: #f7f9fa;\n --cf-color-gray400: #aec1cc;\n --cf-color-gray400-rgb: 174, 193, 204;\n\n /* spacing */\n --cf-spacing-0: 0rem; /* 0px */\n --cf-spacing-1: 0.125rem; /* 2px */\n --cf-spacing-2: 0.25rem; /* 4px */\n --cf-spacing-3: 0.375rem; /* 6px */\n --cf-spacing-4: 0.5rem; /* 8px */\n --cf-spacing-5: 0.625rem; /* 10px */\n --cf-spacing-6: 0.75rem; /* 12px */\n --cf-spacing-7: 0.875rem; /* 14px */\n --cf-spacing-8: 1rem; /* 16px */\n --cf-spacing-9: 1.25rem; /* 20px */\n --cf-spacing-10: 1.5rem; /* 24px */\n --cf-spacing-11: 1.75rem; /* 28px */\n --cf-spacing-12: 2rem; /* 32px */\n --cf-spacing-13: 2.25rem; /* 36px */\n\n /* font-size */\n --cf-text-xs: 0.75rem; /* 12px */\n --cf-text-sm: 0.875rem; /* 14px */\n --cf-text-base: 1rem; /* 16px */\n --cf-text-lg: 1.125rem; /* 18px */\n --cf-text-xl: 1.25rem; /* 20px */\n --cf-text-2xl: 1.5rem; /* 24px */\n --cf-text-3xl: 2rem; /* 32px */\n --cf-text-4xl: 2.75rem; /* 44px */\n\n /* font-weight */\n --cf-font-light: 300;\n --cf-font-normal: 400;\n --cf-font-medium: 500;\n --cf-font-semibold: 600;\n --cf-font-bold: 700;\n --cf-font-extra-bold: 800;\n --cf-font-black: 900;\n\n /* border-radius */\n --cf-border-radius-none: 0px; /* none */\n --cf-border-radius-sm: 0.125rem; /* 2px */\n --cf-border-radius: 0.25rem; /* 4px */\n --cf-border-radius-md: 0.375rem; /* 6px */\n --cf-border-radius-lg: 0.5rem; /* 8px */\n --cf-border-radius-xl: 0.75rem; /* 12px */\n --cf-border-radius-2xl: 1rem; /* 16px */\n --cf-border-radius-3xl: 1.5rem; /* 24px */\n --cf-border-radius-full: 9999px; /* full */\n\n /* font-family */\n --cf-font-family-sans: Archivo, Helvetica, Arial, sans-serif;\n --cf-font-family-serif: Georgia, Cambria, Times New Roman, Times, serif;\n\n /* max widths */\n --cf-max-width-full: 100%;\n\n /* component specific colors */\n --cf-button-bg: var(--cf-color-black);\n --cf-button-color: var(--cf-color-white);\n --cf-text-color: var(--cf-color-black);\n}\n\n.cf-no-image {\n position: relative;\n}\n\n.cf-no-image img {\n background-color: var(--cf-color-gray100);\n outline-offset: -2px;\n outline: 2px solid rgba(var(--cf-color-gray400-rgb), 0.5);\n}\n\n.cf-no-image svg {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n height: var(--cf-text-3xl);\n width: var(--cf-text-3xl);\n max-height: 100%;\n max-width: 100%;\n}\n\n.cf-no-image svg path {\n fill: var(--cf-color-gray400);\n}\n";
3057
+ var css_248z$3 = "@import url(https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;1,400;1,600&display=swap);\n\n:root {\n /* All sizing comments based of 16px base body font size */\n\n /* color */\n --cf-color-white: #fff;\n --cf-color-black: #000;\n --cf-color-gray100: #f7f9fa;\n --cf-color-gray400: #aec1cc;\n --cf-color-gray400-rgb: 174, 193, 204;\n\n /* spacing */\n --cf-spacing-0: 0rem; /* 0px */\n --cf-spacing-1: 0.125rem; /* 2px */\n --cf-spacing-2: 0.25rem; /* 4px */\n --cf-spacing-3: 0.375rem; /* 6px */\n --cf-spacing-4: 0.5rem; /* 8px */\n --cf-spacing-5: 0.625rem; /* 10px */\n --cf-spacing-6: 0.75rem; /* 12px */\n --cf-spacing-7: 0.875rem; /* 14px */\n --cf-spacing-8: 1rem; /* 16px */\n --cf-spacing-9: 1.25rem; /* 20px */\n --cf-spacing-10: 1.5rem; /* 24px */\n --cf-spacing-11: 1.75rem; /* 28px */\n --cf-spacing-12: 2rem; /* 32px */\n --cf-spacing-13: 2.25rem; /* 36px */\n\n /* font-size */\n --cf-text-xs: 0.75rem; /* 12px */\n --cf-text-sm: 0.875rem; /* 14px */\n --cf-text-base: 1rem; /* 16px */\n --cf-text-lg: 1.125rem; /* 18px */\n --cf-text-xl: 1.25rem; /* 20px */\n --cf-text-2xl: 1.5rem; /* 24px */\n --cf-text-3xl: 2rem; /* 32px */\n --cf-text-4xl: 2.75rem; /* 44px */\n\n /* font-weight */\n --cf-font-light: 300;\n --cf-font-normal: 400;\n --cf-font-medium: 500;\n --cf-font-semibold: 600;\n --cf-font-bold: 700;\n --cf-font-extra-bold: 800;\n --cf-font-black: 900;\n\n /* border-radius */\n --cf-border-radius-none: 0px; /* none */\n --cf-border-radius-sm: 0.125rem; /* 2px */\n --cf-border-radius: 0.25rem; /* 4px */\n --cf-border-radius-md: 0.375rem; /* 6px */\n --cf-border-radius-lg: 0.5rem; /* 8px */\n --cf-border-radius-xl: 0.75rem; /* 12px */\n --cf-border-radius-2xl: 1rem; /* 16px */\n --cf-border-radius-3xl: 1.5rem; /* 24px */\n --cf-border-radius-full: 9999px; /* full */\n\n /* font-family */\n --cf-font-family-sans: Archivo, Helvetica, Arial, sans-serif;\n --cf-font-family-serif: Georgia, Cambria, Times New Roman, Times, serif;\n\n /* max widths */\n --cf-max-width-full: 100%;\n\n /* component specific colors */\n --cf-button-bg: var(--cf-color-black);\n --cf-button-color: var(--cf-color-white);\n --cf-text-color: var(--cf-color-black);\n}\n\nimg.cf-placeholder-image {\n background-color: var(--cf-color-gray100);\n outline-offset: -2px;\n outline: 2px solid rgba(var(--cf-color-gray400-rgb), 0.5);\n}\n\nsvg.cf-placeholder-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n height: var(--cf-text-3xl);\n width: var(--cf-text-3xl);\n max-height: 100%;\n max-width: 100%;\n}\n\nsvg.cf-placeholder-icon path {\n fill: var(--cf-color-gray400);\n}\n";
3050
3058
  styleInject(css_248z$3);
3051
3059
 
3052
3060
  var css_248z$2$1 = ".contentful-container {\n position: relative;\n display: flex;\n box-sizing: border-box;\n pointer-events: all;\n}\n\n.contentful-container::-webkit-scrollbar {\n display: none; /* Safari and Chrome */\n}\n\n.cf-single-column-wrapper {\n position: relative;\n}\n\n.cf-container-wrapper {\n position: relative;\n width: 100%;\n}\n\n.contentful-container:after {\n content: '';\n display: block;\n position: absolute;\n pointer-events: none;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n overflow-x: clip;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 12px;\n color: var(--exp-builder-gray400);\n z-index: 1;\n}\n\n.contentful-section-label:after {\n content: 'Section';\n}\n\n.contentful-container-label:after {\n content: 'Container';\n}\n\n/* used by ContentfulSectionAsHyperlink.tsx */\n\n.contentful-container-link,\n.contentful-container-link:active,\n.contentful-container-link:visited,\n.contentful-container-link:hover,\n.contentful-container-link:read-write,\n.contentful-container-link:focus-visible {\n color: inherit;\n text-decoration: unset;\n outline: unset;\n}\n";
@@ -3077,6 +3085,12 @@ Flex.displayName = 'Flex';
3077
3085
  var css_248z$1$1 = ".cf-divider {\n position: relative;\n width: 100%;\n height: 100%;\n border: none;\n}\n\n.cf-divider::before {\n content: \"\";\n position: absolute;\n top: -5px;\n left: -5px;\n bottom: -5px;\n right: -5px;\n pointer-events: all;\n}\n";
3078
3086
  styleInject(css_248z$1$1);
3079
3087
 
3088
+ ({
3089
+ hr: css({
3090
+ border: 'none',
3091
+ }),
3092
+ });
3093
+
3080
3094
  var css_248z$7 = ".cf-columns {\n display: flex;\n gap: 24px;\n grid-template-columns: repeat(12, 1fr);\n flex-direction: column;\n min-height: 0; /* NEW */\n min-width: 0; /* NEW; needed for Firefox */\n}\n\n@media (min-width: 768px) {\n .cf-columns {\n display: grid;\n }\n}\n\n.cf-single-column-wrapper {\n position: relative;\n display: flex;\n}\n\n.cf-single-column {\n pointer-events: all;\n}\n\n.cf-single-column-wrapper:after {\n content: '';\n display: block;\n position: absolute;\n pointer-events: none;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n overflow-x: clip;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 12px;\n color: var(--exp-builder-gray400);\n z-index: 1;\n}\n\n.cf-single-column-label:after {\n content: 'Column';\n}\n";
3081
3095
  styleInject(css_248z$7);
3082
3096
 
@@ -3973,18 +3987,29 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
3973
3987
  renderDropzone,
3974
3988
  ]);
3975
3989
  const cfStyles = useMemo(() => buildCfStyles(props), [props]);
3976
- const sizeStyles = {
3977
- width: cfStyles.width,
3978
- maxWidth: cfStyles.maxWidth,
3979
- maxHeight: cfStyles.maxHeight,
3980
- };
3981
3990
  const isAssemblyBlock = node.type === 'assemblyBlock';
3982
3991
  const isSingleColumn = node?.data.blockId === CONTENTFUL_COMPONENTS.columns.id;
3983
3992
  const isStructureComponent = isContentfulStructureComponent(node?.data.blockId);
3993
+ // Move size styles to the wrapping div and override the component styles
3994
+ const overrideStyles = {};
3995
+ const wrapperStyles = {
3996
+ width: cfStyles.width,
3997
+ maxWidth: cfStyles.maxWidth,
3998
+ };
3999
+ if (!isStructureComponent) {
4000
+ if (cfStyles.height) {
4001
+ wrapperStyles.height = cfStyles.height;
4002
+ overrideStyles.height = '100%';
4003
+ }
4004
+ if (cfStyles.width) {
4005
+ overrideStyles.width = '100%';
4006
+ }
4007
+ }
3984
4008
  // Styles that will be applied to the component element
3985
4009
  const componentClass = useEditorModeClassName({
3986
4010
  styles: {
3987
4011
  ...cfStyles,
4012
+ ...overrideStyles,
3988
4013
  ...(isEmptyZone &&
3989
4014
  isStructureWithRelativeHeight(node?.data.blockId, cfStyles.height) && {
3990
4015
  minHeight: EMPTY_CONTAINER_HEIGHT,
@@ -3998,9 +4023,6 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
3998
4023
  },
3999
4024
  nodeId: node.data.id,
4000
4025
  });
4001
- //List explicit style props that will end up being passed to the component
4002
- const stylesToKeep = ['cfImageAsset'];
4003
- const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.includes(style));
4004
4026
  const componentProps = {
4005
4027
  'data-cf-node-id': node.data.id,
4006
4028
  'data-cf-node-block-id': node.data.blockId,
@@ -4009,10 +4031,10 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
4009
4031
  editorMode: true,
4010
4032
  node,
4011
4033
  renderDropzone,
4012
- ...omit(props, stylesToRemove, ['cfHyperlink', 'cfOpenInNewTab', 'cfSsrClassName']),
4034
+ ...sanitizeNodeProps(props),
4013
4035
  ...(definition?.children ? { children: renderDropzone(node) } : {}),
4014
4036
  };
4015
- return { componentProps, sizeStyles };
4037
+ return { componentProps, wrapperStyles };
4016
4038
  };
4017
4039
  const addExtraDropzonePadding = (padding) => padding
4018
4040
  .split(' ')
@@ -4192,7 +4214,7 @@ const useComponent = ({ node: rawNode, resolveDesignValue, renderDropzone, userI
4192
4214
  return registration;
4193
4215
  }, [node]);
4194
4216
  const componentId = node.data.id;
4195
- const { componentProps, sizeStyles } = useComponentProps({
4217
+ const { componentProps, wrapperStyles } = useComponentProps({
4196
4218
  node,
4197
4219
  areEntitiesFetched,
4198
4220
  resolveDesignValue,
@@ -4218,7 +4240,7 @@ const useComponent = ({ node: rawNode, resolveDesignValue, renderDropzone, userI
4218
4240
  return element;
4219
4241
  }
4220
4242
  const { children, innerRef, Tag = 'div', ToolTipAndPlaceholder, style, ...rest } = dragProps;
4221
- return (React.createElement(Tag, { ...rest, style: { ...style, ...sizeStyles }, ref: (refNode) => {
4243
+ return (React.createElement(Tag, { ...rest, style: { ...style, ...wrapperStyles }, ref: (refNode) => {
4222
4244
  if (innerRef && refNode)
4223
4245
  innerRef(refNode);
4224
4246
  } },