@contentful/experiences-visual-editor-react 3.3.0-dev-20250820T1056-fd92026.0 → 3.3.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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import styleInject from 'style-inject';
2
2
  import React, { useState, useEffect, useCallback, forwardRef, useMemo, useLayoutEffect, useRef } from 'react';
3
3
  import { z } from 'zod';
4
- import { omit, isArray, isEqual, get as get$2, debounce } from 'lodash-es';
4
+ import { cloneDeep, omit, isArray, isEqual, get as get$2, debounce } from 'lodash-es';
5
5
  import md5 from 'md5';
6
6
  import { BLOCKS } from '@contentful/rich-text-types';
7
7
  import { create, useStore } from 'zustand';
@@ -1011,6 +1011,18 @@ var CodeNames$1;
1011
1011
  CodeNames["Custom"] = "custom";
1012
1012
  })(CodeNames$1 || (CodeNames$1 = {}));
1013
1013
 
1014
+ const sdkOptionsRegistry = {};
1015
+ /**
1016
+ * Used inside defineComponents to forward registry arguments to this registry
1017
+ * of SDK options.
1018
+ */
1019
+ const defineSdkOptions = (options) => {
1020
+ Object.assign(sdkOptionsRegistry, options);
1021
+ };
1022
+ const getSdkOptions = () => {
1023
+ return { ...sdkOptionsRegistry };
1024
+ };
1025
+
1014
1026
  const MEDIA_QUERY_REGEXP = /(<|>)(\d{1,})(px|cm|mm|in|pt|pc)$/;
1015
1027
  const toCSSMediaQuery = ({ query }) => {
1016
1028
  if (query === '*')
@@ -1595,6 +1607,20 @@ const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageOptions
1595
1607
  backgroundSize: matchBackgroundSize(cfBackgroundImageOptions?.scaling),
1596
1608
  };
1597
1609
  };
1610
+ const transformTextAlign = (value) => {
1611
+ if (!value)
1612
+ return undefined;
1613
+ const sdkOptions = getSdkOptions();
1614
+ // New behavior: translate left/right to start/end
1615
+ // Customer can opt-out by activating this global option toggle
1616
+ if (!sdkOptions.__disableTextAlignmentTransform) {
1617
+ if (value === 'left')
1618
+ return 'start';
1619
+ if (value === 'right')
1620
+ return 'end';
1621
+ }
1622
+ return value;
1623
+ };
1598
1624
 
1599
1625
  const toCSSAttribute = (key) => {
1600
1626
  let val = key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
@@ -1672,7 +1698,7 @@ const buildCfStyles = (values) => {
1672
1698
  lineHeight: values.cfLineHeight,
1673
1699
  letterSpacing: values.cfLetterSpacing,
1674
1700
  color: values.cfTextColor,
1675
- textAlign: values.cfTextAlign,
1701
+ textAlign: transformTextAlign(values.cfTextAlign),
1676
1702
  textTransform: values.cfTextTransform,
1677
1703
  objectFit: values.cfImageOptions?.objectFit,
1678
1704
  objectPosition: values.cfImageOptions?.objectPosition,
@@ -1805,7 +1831,7 @@ const transformRichText = (entryOrAsset, entityStore, path) => {
1805
1831
  // resolve any links to assets/entries/hyperlinks
1806
1832
  // we need to clone, as we want to keep the original Entity in the EntityStore intact,
1807
1833
  // and resolveLinks() is mutating the node object.
1808
- const richTextDocument = structuredClone(value);
1834
+ const richTextDocument = cloneDeep(value);
1809
1835
  resolveLinks(richTextDocument, entityStore);
1810
1836
  return richTextDocument;
1811
1837
  }
@@ -2151,6 +2177,27 @@ const tryParseMessage = (event) => {
2151
2177
  return eventData;
2152
2178
  };
2153
2179
 
2180
+ const splitDirectAndSlotChildren = (allChildNodes, componentDefinition) => {
2181
+ // Bridge difference between editor and delivery mode
2182
+ const getSlotId = (child) => {
2183
+ if ('data' in child)
2184
+ return child.data.slotId;
2185
+ return child.slotId;
2186
+ };
2187
+ const slotNodesMap = {};
2188
+ for (const slotId in componentDefinition.slots) {
2189
+ // We only allow one component per slot (container). This is just safer to not render components twice or not at all
2190
+ const nodes = allChildNodes.filter((child) => getSlotId(child) === slotId);
2191
+ slotNodesMap[slotId] = nodes.length ? nodes : null;
2192
+ }
2193
+ const directChildNodes = allChildNodes.filter((child) => getSlotId(child) === undefined);
2194
+ if (!componentDefinition.children || !directChildNodes.length) {
2195
+ // If there are no children allowed in the component or no children added, render as undefined
2196
+ return { slotNodesMap, directChildNodes: undefined };
2197
+ }
2198
+ return { slotNodesMap, directChildNodes };
2199
+ };
2200
+
2154
2201
  const sendMessage = (eventType, data) => {
2155
2202
  if (typeof window === 'undefined') {
2156
2203
  return;
@@ -2282,11 +2329,11 @@ let EntityStoreBase$1 = class EntityStoreBase {
2282
2329
  addEntity(entity) {
2283
2330
  if (isAsset$1(entity)) {
2284
2331
  // cloned and frozen
2285
- this.assetMap.set(entity.sys.id, deepFreeze$1(structuredClone(entity)));
2332
+ this.assetMap.set(entity.sys.id, deepFreeze$1(cloneDeep(entity)));
2286
2333
  }
2287
2334
  else if (isEntry$1(entity)) {
2288
2335
  // cloned and frozen
2289
- this.entryMap.set(entity.sys.id, deepFreeze$1(structuredClone(entity)));
2336
+ this.entryMap.set(entity.sys.id, deepFreeze$1(cloneDeep(entity)));
2290
2337
  }
2291
2338
  else {
2292
2339
  throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
@@ -3219,12 +3266,14 @@ const useEditorStore = create((set, get) => ({
3219
3266
  }
3220
3267
  set({ locale });
3221
3268
  },
3222
- initializeEditor({ componentRegistry: initialRegistry, designTokens, initialLocale }) {
3269
+ initializeEditor({ componentRegistry: initialRegistry, designTokens, sdkOptions, initialLocale, }) {
3223
3270
  initialRegistry.forEach((registration) => {
3224
3271
  componentRegistry.set(registration.definition.id, registration);
3225
3272
  });
3226
3273
  // Re-register the design tokens with the Visual Editor's instance of the experiences-core package
3227
3274
  defineDesignTokens(designTokens);
3275
+ // Same copy over from one instance to the other is necessary for the sdk options
3276
+ defineSdkOptions(sdkOptions);
3228
3277
  set({ locale: initialLocale });
3229
3278
  },
3230
3279
  }));
@@ -4093,11 +4142,11 @@ class EntityStoreBase {
4093
4142
  addEntity(entity) {
4094
4143
  if (isAsset(entity)) {
4095
4144
  // cloned and frozen
4096
- this.assetMap.set(entity.sys.id, deepFreeze(structuredClone(entity)));
4145
+ this.assetMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
4097
4146
  }
4098
4147
  else if (isEntry(entity)) {
4099
4148
  // cloned and frozen
4100
- this.entryMap.set(entity.sys.id, deepFreeze(structuredClone(entity)));
4149
+ this.entryMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
4101
4150
  }
4102
4151
  else {
4103
4152
  throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
@@ -4905,18 +4954,19 @@ function EditorBlock({ node, resolveDesignValue, wrappingPatternIds: parentWrapp
4905
4954
  if (isRootAssemblyNode && node.data.blockId && parentWrappingPatternIds.has(node.data.blockId)) {
4906
4955
  return React.createElement(CircularDependencyErrorPlaceholder, { wrappingPatternIds: wrappingPatternIds });
4907
4956
  }
4908
- const slotNodes = {};
4909
- for (const slotId in componentRegistration.definition.slots) {
4910
- const nodes = node.children.filter((child) => child.data.slotId === slotId);
4911
- slotNodes[slotId] =
4912
- nodes.length === 0 ? (React.createElement("div", { className: styles$1.emptySlot })) : (React.createElement(React.Fragment, null, nodes.map((slotChildNode) => (React.createElement(EditorBlock, { key: slotChildNode.data.id, node: slotChildNode, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched })))));
4913
- }
4914
- const children = componentRegistration.definition.children
4915
- ? node.children
4916
- .filter((node) => node.data.slotId === undefined)
4917
- .map((childNode) => (React.createElement(EditorBlock, { key: childNode.data.id, node: childNode, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched })))
4918
- : null;
4919
- return (React.createElement(RegistrationComponent, { node: node, resolveDesignValue: resolveDesignValue, componentRegistration: componentRegistration, slotNodes: slotNodes, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched }, children));
4957
+ const { slotNodesMap, directChildNodes } = splitDirectAndSlotChildren(node.children, componentRegistration.definition);
4958
+ const renderChildNode = (childNode) => (React.createElement(EditorBlock, { key: childNode.data.id, node: childNode, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched }));
4959
+ const renderedSlotNodesMap = Object.entries(slotNodesMap).reduce((acc, [slotId, nodes]) => {
4960
+ if (nodes?.length) {
4961
+ acc[slotId] = React.createElement(React.Fragment, null, nodes.map((slotChildNode) => renderChildNode(slotChildNode)));
4962
+ }
4963
+ else {
4964
+ acc[slotId] = React.createElement("div", { className: styles$1.emptySlot });
4965
+ }
4966
+ return acc;
4967
+ }, {});
4968
+ const renderedChildren = directChildNodes?.map((childNode) => renderChildNode(childNode));
4969
+ return (React.createElement(RegistrationComponent, { node: node, resolveDesignValue: resolveDesignValue, componentRegistration: componentRegistration, slotNodes: renderedSlotNodesMap, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched }, renderedChildren));
4920
4970
  }
4921
4971
  const RegistrationComponent = ({ node, resolveDesignValue, componentRegistration, slotNodes, children, entityStore, areEntitiesFetched, }) => {
4922
4972
  const { componentProps } = useComponentProps({
@@ -5149,11 +5199,12 @@ const useInitializeEditor = (inMemoryEntitiesStore) => {
5149
5199
  const onVisualEditorInitialize = (event) => {
5150
5200
  if (!event.detail)
5151
5201
  return;
5152
- const { componentRegistry, designTokens, locale: initialLocale, entities } = event.detail;
5202
+ const { componentRegistry, designTokens, sdkOptions, locale: initialLocale, entities, } = event.detail;
5153
5203
  initializeEditor({
5154
5204
  initialLocale,
5155
5205
  componentRegistry,
5156
5206
  designTokens,
5207
+ sdkOptions,
5157
5208
  });
5158
5209
  // if entities is set to [], then everything will still work as EntityStore will
5159
5210
  // request entities on demand via ▲REQUEST_ENTITY