@contentful/experiences-visual-editor-react 1.28.3 → 1.29.0-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
@@ -947,6 +947,9 @@ const BaseComponentTreeNodeSchema$1 = z.object({
947
947
  const ComponentTreeNodeSchema$1 = BaseComponentTreeNodeSchema$1.extend({
948
948
  children: z.lazy(() => ComponentTreeNodeSchema$1.array()),
949
949
  });
950
+ const BindingSourceTypeEnumSchema$1 = z
951
+ .array(z.enum(['entry', 'asset', 'manual', 'experience']))
952
+ .nonempty();
950
953
  const ComponentVariableSchema$1 = z.object({
951
954
  displayName: z.string().optional(),
952
955
  type: DefinitionPropertyTypeSchema$1,
@@ -955,6 +958,7 @@ const ComponentVariableSchema$1 = z.object({
955
958
  defaultValue: PrimitiveValueSchema$1.or(ComponentPropertyValueSchema$1).optional(),
956
959
  validations: z
957
960
  .object({
961
+ bindingSourceType: BindingSourceTypeEnumSchema$1.optional(),
958
962
  required: z.boolean().optional(),
959
963
  format: z.literal('URL').optional(),
960
964
  in: z
@@ -2716,12 +2720,11 @@ function compareNodes({ currentNode, updatedNode, originalTree, differences = []
2716
2720
  const nodeRemoved = currentNodeCount > updatedNodeCount;
2717
2721
  const nodeAdded = currentNodeCount < updatedNodeCount;
2718
2722
  const parentNodeId = updatedNode.data.id;
2719
- const isRoot = currentNode.data.id === ROOT_ID;
2720
2723
  /**
2721
2724
  * The data of the current node has changed, we need to update
2722
2725
  * this node to reflect the data change. (design, content, unbound values)
2723
2726
  */
2724
- if (!isRoot && !isEqual(currentNode.data, updatedNode.data)) {
2727
+ if (!isEqual(currentNode.data, updatedNode.data)) {
2725
2728
  differences.push({
2726
2729
  type: TreeAction.UPDATE_NODE,
2727
2730
  nodeId: currentNode.data.id,
@@ -3321,6 +3324,9 @@ const BaseComponentTreeNodeSchema = z.object({
3321
3324
  const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
3322
3325
  children: z.lazy(() => ComponentTreeNodeSchema.array()),
3323
3326
  });
3327
+ const BindingSourceTypeEnumSchema = z
3328
+ .array(z.enum(['entry', 'asset', 'manual', 'experience']))
3329
+ .nonempty();
3324
3330
  const ComponentVariableSchema = z.object({
3325
3331
  displayName: z.string().optional(),
3326
3332
  type: DefinitionPropertyTypeSchema,
@@ -3329,6 +3335,7 @@ const ComponentVariableSchema = z.object({
3329
3335
  defaultValue: PrimitiveValueSchema.or(ComponentPropertyValueSchema).optional(),
3330
3336
  validations: z
3331
3337
  .object({
3338
+ bindingSourceType: BindingSourceTypeEnumSchema.optional(),
3332
3339
  required: z.boolean().optional(),
3333
3340
  format: z.literal('URL').optional(),
3334
3341
  in: z
@@ -4623,7 +4630,29 @@ const MissingComponentPlaceholder = ({ blockId }) => {
4623
4630
  "'"));
4624
4631
  };
4625
4632
 
4626
- const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging, }) => {
4633
+ const CircularDependencyErrorPlaceholder = forwardRef(({ wrappingPatternIds, ...props }, ref) => {
4634
+ const entityStore = useEntityStore((state) => state.entityStore);
4635
+ return (React.createElement("div", { ...props,
4636
+ // Pass through ref to avoid DND errors being logged
4637
+ ref: ref, "data-cf-node-error": "circular-pattern-dependency", style: {
4638
+ border: '1px solid red',
4639
+ background: 'rgba(255, 0, 0, 0.1)',
4640
+ padding: '1rem 1rem 0 1rem',
4641
+ width: '100%',
4642
+ height: '100%',
4643
+ } },
4644
+ "Circular usage of patterns detected:",
4645
+ React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
4646
+ const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
4647
+ const entry = entityStore.getEntityFromLink(entryLink);
4648
+ const entryTitle = entry?.fields?.title;
4649
+ const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
4650
+ return React.createElement("li", { key: patternId }, text);
4651
+ }))));
4652
+ });
4653
+ CircularDependencyErrorPlaceholder.displayName = 'CircularDependencyErrorPlaceholder';
4654
+
4655
+ const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging, wrappingPatternIds, }) => {
4627
4656
  const areEntitiesFetched = useEntityStore((state) => state.areEntitiesFetched);
4628
4657
  const tree = useTreeStore((state) => state.tree);
4629
4658
  const componentRegistration = useMemo(() => {
@@ -4659,10 +4688,19 @@ const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging
4659
4688
  requiresDragWrapper,
4660
4689
  });
4661
4690
  const elementToRender = (props) => {
4691
+ const { dragProps = {} } = props || {};
4692
+ const { children, innerRef, Tag = 'div', ToolTipAndPlaceholder, style, ...rest } = dragProps;
4693
+ const { 'data-cf-node-block-id': dataCfNodeBlockId, 'data-cf-node-block-type': dataCfNodeBlockType, 'data-cf-node-id': dataCfNodeId, } = componentProps;
4694
+ const refCallback = (refNode) => {
4695
+ if (innerRef && refNode)
4696
+ innerRef(refNode);
4697
+ };
4662
4698
  if (!componentRegistration) {
4663
4699
  return React.createElement(MissingComponentPlaceholder, { blockId: node.data.blockId });
4664
4700
  }
4665
- const { dragProps = {} } = props || {};
4701
+ if (node.data.blockId && wrappingPatternIds.has(node.data.blockId)) {
4702
+ return (React.createElement(CircularDependencyErrorPlaceholder, { ref: refCallback, "data-cf-node-id": dataCfNodeId, "data-cf-node-block-id": dataCfNodeBlockId, "data-cf-node-block-type": dataCfNodeBlockType, wrappingPatternIds: wrappingPatternIds }));
4703
+ }
4666
4704
  const element = React.createElement(ImportedComponentErrorBoundary, { componentId: node.data.blockId }, React.createElement(componentRegistration.component, {
4667
4705
  ...componentProps,
4668
4706
  dragProps,
@@ -4670,12 +4708,7 @@ const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging
4670
4708
  if (!requiresDragWrapper) {
4671
4709
  return element;
4672
4710
  }
4673
- const { children, innerRef, Tag = 'div', ToolTipAndPlaceholder, style, ...rest } = dragProps;
4674
- const { 'data-cf-node-block-id': dataCfNodeBlockId, 'data-cf-node-block-type': dataCfNodeBlockType, 'data-cf-node-id': dataCfNodeId, } = componentProps;
4675
- return (React.createElement(Tag, { ...rest, style: { ...style, ...wrapperStyles }, ref: (refNode) => {
4676
- if (innerRef && refNode)
4677
- innerRef(refNode);
4678
- }, "data-cf-node-id": dataCfNodeId, "data-cf-node-block-id": dataCfNodeBlockId, "data-cf-node-block-type": dataCfNodeBlockType },
4711
+ return (React.createElement(Tag, { ...rest, style: { ...style, ...wrapperStyles }, ref: refCallback, "data-cf-node-id": dataCfNodeId, "data-cf-node-block-id": dataCfNodeBlockId, "data-cf-node-block-type": dataCfNodeBlockType },
4679
4712
  ToolTipAndPlaceholder,
4680
4713
  element));
4681
4714
  };
@@ -5126,7 +5159,7 @@ function getStyle$1(style, snapshot) {
5126
5159
  transitionDuration: `0.001s`,
5127
5160
  };
5128
5161
  }
5129
- const EditorBlock = ({ node: rawNode, resolveDesignValue, renderDropzone, index, zoneId, userIsDragging, placeholder, }) => {
5162
+ const EditorBlock = ({ node: rawNode, resolveDesignValue, renderDropzone, index, zoneId, userIsDragging, placeholder, wrappingPatternIds, }) => {
5130
5163
  const { slotId } = parseZoneId(zoneId);
5131
5164
  const ref = useRef(null);
5132
5165
  const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
@@ -5136,6 +5169,7 @@ const EditorBlock = ({ node: rawNode, resolveDesignValue, renderDropzone, index,
5136
5169
  resolveDesignValue,
5137
5170
  renderDropzone,
5138
5171
  userIsDragging,
5172
+ wrappingPatternIds,
5139
5173
  });
5140
5174
  const { isSingleColumn, isWrapped } = useSingleColumn(node, resolveDesignValue);
5141
5175
  const setDomRect = useDraggedItemStore((state) => state.setDomRect);
@@ -5272,13 +5306,14 @@ function getStyle(style = {}, snapshot) {
5272
5306
  transitionDuration: `0.001s`,
5273
5307
  };
5274
5308
  }
5275
- const EditorBlockClone = ({ node: rawNode, resolveDesignValue, snapshot, provided, renderDropzone, }) => {
5309
+ const EditorBlockClone = ({ node: rawNode, resolveDesignValue, snapshot, provided, renderDropzone, wrappingPatternIds, }) => {
5276
5310
  const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
5277
5311
  const { node, elementToRender } = useComponent({
5278
5312
  node: rawNode,
5279
5313
  resolveDesignValue,
5280
5314
  renderDropzone,
5281
5315
  userIsDragging,
5316
+ wrappingPatternIds,
5282
5317
  });
5283
5318
  const isAssemblyBlock = node.type === ASSEMBLY_BLOCK_NODE_TYPE;
5284
5319
  return elementToRender({
@@ -5311,7 +5346,7 @@ const getHtmlComponentProps = (props) => {
5311
5346
  return {};
5312
5347
  };
5313
5348
 
5314
- function DropzoneClone({ node, zoneId, resolveDesignValue, WrapperComponent = 'div', renderDropzone, dragProps, ...rest }) {
5349
+ function DropzoneClone({ node, zoneId, resolveDesignValue, WrapperComponent = 'div', renderDropzone, dragProps, wrappingPatternIds, ...rest }) {
5315
5350
  const tree = useTreeStore((state) => state.tree);
5316
5351
  const content = node?.children || tree.root?.children || [];
5317
5352
  const { slotId } = parseZoneId(zoneId);
@@ -5332,11 +5367,11 @@ function DropzoneClone({ node, zoneId, resolveDesignValue, WrapperComponent = 'd
5332
5367
  .filter((node) => node.data.slotId === slotId)
5333
5368
  .map((item) => {
5334
5369
  const componentId = item.data.id;
5335
- return (React.createElement(EditorBlockClone, { key: componentId, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone }));
5370
+ return (React.createElement(EditorBlockClone, { key: componentId, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds }));
5336
5371
  })));
5337
5372
  }
5338
5373
 
5339
- function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponent = 'div', dragProps, ...rest }) {
5374
+ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponent = 'div', dragProps, wrappingPatternIds: parentWrappingPatternIds = new Set(), ...rest }) {
5340
5375
  const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
5341
5376
  const draggedItem = useDraggedItemStore((state) => state.draggedItem);
5342
5377
  const isDraggingNewComponent = useDraggedItemStore((state) => Boolean(state.componentId));
@@ -5358,13 +5393,24 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
5358
5393
  const isRootAssembly = node?.type === ASSEMBLY_NODE_TYPE;
5359
5394
  const htmlDraggableProps = getHtmlDragProps(dragProps);
5360
5395
  const htmlProps = getHtmlComponentProps(rest);
5396
+ const wrappingPatternIds = useMemo(() => {
5397
+ // On the top level, the node is not defined. If the root blockId is not the default string,
5398
+ // we assume that it is the entry ID of the experience/ pattern to properly detect circular dependencies
5399
+ if (!node && tree.root.data.blockId && tree.root.data.blockId !== ROOT_ID) {
5400
+ return new Set([tree.root.data.blockId, ...parentWrappingPatternIds]);
5401
+ }
5402
+ if (isRootAssembly && node?.data.blockId) {
5403
+ return new Set([node.data.blockId, ...parentWrappingPatternIds]);
5404
+ }
5405
+ return parentWrappingPatternIds;
5406
+ }, [isRootAssembly, node, parentWrappingPatternIds, tree.root.data.blockId]);
5361
5407
  // To avoid a circular dependency, we create the recursive rendering function here and trickle it down
5362
5408
  const renderDropzone = useCallback((node, props) => {
5363
- return (React.createElement(Dropzone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, ...props }));
5364
- }, [resolveDesignValue]);
5409
+ return (React.createElement(Dropzone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, ...props }));
5410
+ }, [wrappingPatternIds, resolveDesignValue]);
5365
5411
  const renderClonedDropzone = useCallback((node, props) => {
5366
- return (React.createElement(DropzoneClone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, renderDropzone: renderClonedDropzone, ...props }));
5367
- }, [resolveDesignValue]);
5412
+ return (React.createElement(DropzoneClone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds, ...props }));
5413
+ }, [resolveDesignValue, wrappingPatternIds]);
5368
5414
  const isDropzoneEnabled = useMemo(() => {
5369
5415
  const isColumns = node?.data.blockId === CONTENTFUL_COMPONENTS$1.columns.id;
5370
5416
  const isDraggingSingleColumn = draggedNode?.data.blockId === CONTENTFUL_COMPONENTS$1.singleColumn.id;
@@ -5398,7 +5444,7 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
5398
5444
  if (!resolveDesignValue) {
5399
5445
  return null;
5400
5446
  }
5401
- return (React.createElement(Droppable, { droppableId: zoneId, direction: direction, isDropDisabled: !isDropzoneEnabled, renderClone: (provided, snapshot, rubic) => (React.createElement(EditorBlockClone, { node: content[rubic.source.index], resolveDesignValue: resolveDesignValue, provided: provided, snapshot: snapshot, renderDropzone: renderClonedDropzone })) }, (provided, snapshot) => {
5447
+ return (React.createElement(Droppable, { droppableId: zoneId, direction: direction, isDropDisabled: !isDropzoneEnabled, renderClone: (provided, snapshot, rubic) => (React.createElement(EditorBlockClone, { node: content[rubic.source.index], resolveDesignValue: resolveDesignValue, provided: provided, snapshot: snapshot, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds })) }, (provided, snapshot) => {
5402
5448
  return (React.createElement(WrapperComponent, { ...(provided || { droppableProps: {} }).droppableProps, ...htmlDraggableProps, ...htmlProps, ref: (refNode) => {
5403
5449
  if (dragProps?.innerRef) {
5404
5450
  dragProps.innerRef(refNode);
@@ -5421,7 +5467,7 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
5421
5467
  elementIndex: i,
5422
5468
  dropzoneElementId: zoneId,
5423
5469
  direction,
5424
- }, index: i, zoneId: zoneId, key: item.data.id, userIsDragging: userIsDragging, draggingNewComponent: isDraggingNewComponent, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone })))),
5470
+ }, index: i, zoneId: zoneId, key: item.data.id, userIsDragging: userIsDragging, draggingNewComponent: isDraggingNewComponent, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds })))),
5425
5471
  provided?.placeholder,
5426
5472
  dragProps?.ToolTipAndPlaceholder));
5427
5473
  }));