@contentful/experiences-visual-editor-react 0.0.1-alpha.7 → 0.0.1-alpha.8

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
@@ -11,8 +11,8 @@ import { produce } from 'immer';
11
11
  import { createPortal } from 'react-dom';
12
12
  import { v4 } from 'uuid';
13
13
 
14
- var css_248z$7 = "html,\nbody {\n margin: 0;\n padding: 0;\n}\n\n/*\n * All of these variables are tokens from Forma-36 and should not be adjusted as these\n * are global variables that may affect multiple places.\n * As our customers may use other design libraries, we try to avoid overlapping global\n * variables by always using the prefix `--exp-builder-` inside this SDK.\n */\n\n:root {\n /* Color tokens from Forma 36: https://f36.contentful.com/tokens/color-system */\n --exp-builder-blue100: #e8f5ff;\n --exp-builder-blue200: #ceecff;\n --exp-builder-blue300: #98cbff;\n --exp-builder-blue400: #40a0ff;\n --exp-builder-blue500: #036fe3;\n --exp-builder-blue600: #0059c8;\n --exp-builder-blue700: #0041ab;\n --exp-builder-blue800: #003298;\n --exp-builder-blue900: #002a8e;\n --exp-builder-gray100: #f7f9fa;\n --exp-builder-gray200: #e7ebee;\n --exp-builder-gray300: #cfd9e0;\n --exp-builder-gray400: #aec1cc;\n --exp-builder-gray500: #67728a;\n --exp-builder-gray600: #5a657c;\n --exp-builder-gray700: #414d63;\n --exp-builder-gray800: #1b273a;\n --exp-builder-gray900: #111b2b;\n --exp-builder-purple600: #6c3ecf;\n --exp-builder-red200: #ffe0e0;\n --exp-builder-red800: #7f0010;\n --exp-builder-color-white: #ffffff;\n --exp-builder-glow-primary: 0px 0px 0px 3px #e8f5ff;\n\n /* RGB colors for applying opacity */\n --exp-builder-blue100-rgb: 232, 245, 255;\n --exp-builder-blue300-rgb: 152, 203, 255;\n\n /* Spacing tokens from Forma 36: https://f36.contentful.com/tokens/spacing */\n --exp-builder-spacing-s: 0.75rem;\n --exp-builder-spacing-2xs: 0.25rem;\n\n /* Typography tokens from Forma 36: https://f36.contentful.com/tokens/typography */\n --exp-builder-font-size-l: 1rem;\n --exp-builder-font-size-m: 0.875rem;\n --exp-builder-font-stack-primary: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;\n --exp-builder-line-height-condensed: 1.25;\n}\n";
15
- styleInject(css_248z$7);
14
+ var css_248z$8 = "html,\nbody {\n margin: 0;\n padding: 0;\n}\n\n/*\n * All of these variables are tokens from Forma-36 and should not be adjusted as these\n * are global variables that may affect multiple places.\n * As our customers may use other design libraries, we try to avoid overlapping global\n * variables by always using the prefix `--exp-builder-` inside this SDK.\n */\n\n:root {\n /* Color tokens from Forma 36: https://f36.contentful.com/tokens/color-system */\n --exp-builder-blue100: #e8f5ff;\n --exp-builder-blue200: #ceecff;\n --exp-builder-blue300: #98cbff;\n --exp-builder-blue400: #40a0ff;\n --exp-builder-blue500: #036fe3;\n --exp-builder-blue600: #0059c8;\n --exp-builder-blue700: #0041ab;\n --exp-builder-blue800: #003298;\n --exp-builder-blue900: #002a8e;\n --exp-builder-gray100: #f7f9fa;\n --exp-builder-gray200: #e7ebee;\n --exp-builder-gray300: #cfd9e0;\n --exp-builder-gray400: #aec1cc;\n --exp-builder-gray500: #67728a;\n --exp-builder-gray600: #5a657c;\n --exp-builder-gray700: #414d63;\n --exp-builder-gray800: #1b273a;\n --exp-builder-gray900: #111b2b;\n --exp-builder-purple600: #6c3ecf;\n --exp-builder-red200: #ffe0e0;\n --exp-builder-red800: #7f0010;\n --exp-builder-color-white: #ffffff;\n --exp-builder-glow-primary: 0px 0px 0px 3px #e8f5ff;\n\n /* RGB colors for applying opacity */\n --exp-builder-blue100-rgb: 232, 245, 255;\n --exp-builder-blue300-rgb: 152, 203, 255;\n\n /* Spacing tokens from Forma 36: https://f36.contentful.com/tokens/spacing */\n --exp-builder-spacing-s: 0.75rem;\n --exp-builder-spacing-2xs: 0.25rem;\n\n /* Typography tokens from Forma 36: https://f36.contentful.com/tokens/typography */\n --exp-builder-font-size-l: 1rem;\n --exp-builder-font-size-m: 0.875rem;\n --exp-builder-font-stack-primary: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;\n --exp-builder-line-height-condensed: 1.25;\n}\n";
15
+ styleInject(css_248z$8);
16
16
 
17
17
  const INCOMING_EVENTS$1 = {
18
18
  RequestEditorMode: 'requestEditorMode',
@@ -70,11 +70,13 @@ const CONTENTFUL_COMPONENTS$1 = {
70
70
  },
71
71
  };
72
72
  const EMPTY_CONTAINER_HEIGHT$1 = '80px';
73
+ const DEFAULT_IMAGE_WIDTH = '500px';
73
74
  var PostMessageMethods$2;
74
75
  (function (PostMessageMethods) {
75
76
  PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
76
77
  PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
77
78
  })(PostMessageMethods$2 || (PostMessageMethods$2 = {}));
79
+ const SUPPORTED_IMAGE_FORMATS = ['jpg', 'png', 'webp', 'gif', 'avif'];
78
80
 
79
81
  const structureComponents = new Set([
80
82
  CONTENTFUL_COMPONENTS$1.section.id,
@@ -225,24 +227,18 @@ const transformAlignment = (cfHorizontalAlignment, cfVerticalAlignment, cfFlexDi
225
227
  ? `safe ${cfHorizontalAlignment}`
226
228
  : cfHorizontalAlignment,
227
229
  };
228
- const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageScaling, cfBackgroundImageAlignment) => {
229
- const matchBackgroundSize = (backgroundImageScaling) => {
230
- if ('fill' === backgroundImageScaling)
230
+ const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageOptions) => {
231
+ const matchBackgroundSize = (scaling) => {
232
+ if ('fill' === scaling)
231
233
  return 'cover';
232
- if ('fit' === backgroundImageScaling)
234
+ if ('fit' === scaling)
233
235
  return 'contain';
234
- return undefined;
235
236
  };
236
- const matchBackgroundPosition = (cfBackgroundImageAlignment) => {
237
- if (!cfBackgroundImageAlignment) {
238
- return undefined;
239
- }
240
- if ('string' !== typeof cfBackgroundImageAlignment) {
241
- return undefined;
237
+ const matchBackgroundPosition = (alignment) => {
238
+ if (!alignment || 'string' !== typeof alignment) {
239
+ return;
242
240
  }
243
- let [horizontalAlignment, verticalAlignment] = cfBackgroundImageAlignment
244
- .trim()
245
- .split(/\s+/, 2);
241
+ let [horizontalAlignment, verticalAlignment] = alignment.trim().split(/\s+/, 2);
246
242
  // Special case for handling single values
247
243
  // for backwards compatibility with single values 'right','left', 'center', 'top','bottom'
248
244
  if (horizontalAlignment && !verticalAlignment) {
@@ -278,50 +274,29 @@ const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageScaling
278
274
  return `${horizontalAlignment} ${verticalAlignment}`;
279
275
  };
280
276
  if (!cfBackgroundImageUrl) {
281
- return undefined;
282
- }
283
- return {
284
- backgroundImage: `url(${cfBackgroundImageUrl})`,
285
- backgroundRepeat: cfBackgroundImageScaling === 'tile' ? 'repeat' : 'no-repeat',
286
- backgroundPosition: matchBackgroundPosition(cfBackgroundImageAlignment),
287
- backgroundSize: matchBackgroundSize(cfBackgroundImageScaling),
288
- };
289
- };
290
- const transformContentValue = (value, variableDefinition) => {
291
- if (variableDefinition.type === 'RichText') {
292
- return transformRichText(value);
277
+ return;
293
278
  }
294
- return value;
295
- };
296
- const transformRichText = (value) => {
297
- if (typeof value === 'string') {
298
- return {
299
- data: {},
300
- content: [
301
- {
302
- nodeType: BLOCKS.PARAGRAPH,
303
- data: {},
304
- content: [
305
- {
306
- data: {},
307
- nodeType: 'text',
308
- value: value,
309
- marks: [],
310
- },
311
- ],
312
- },
313
- ],
314
- nodeType: BLOCKS.DOCUMENT,
315
- };
279
+ let backgroundImage;
280
+ let backgroundImageSet;
281
+ if (typeof cfBackgroundImageUrl === 'string') {
282
+ backgroundImage = `url(${cfBackgroundImageUrl})`;
316
283
  }
317
- if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
318
- return value;
284
+ else {
285
+ const imgSet = cfBackgroundImageUrl.srcSet?.join(',');
286
+ backgroundImage = `url(${cfBackgroundImageUrl.url})`;
287
+ backgroundImageSet = `image-set(${imgSet})`;
319
288
  }
320
- return undefined;
289
+ return {
290
+ backgroundImage,
291
+ backgroundImage2: backgroundImageSet,
292
+ backgroundRepeat: cfBackgroundImageOptions?.scaling === 'tile' ? 'repeat' : 'no-repeat',
293
+ backgroundPosition: matchBackgroundPosition(cfBackgroundImageOptions?.alignment),
294
+ backgroundSize: matchBackgroundSize(cfBackgroundImageOptions?.scaling),
295
+ };
321
296
  };
322
297
  const transformWidthSizing = ({ value, cfMargin, }) => {
323
298
  if (!value || !cfMargin)
324
- return undefined;
299
+ return;
325
300
  const transformedValue = transformFill(value);
326
301
  const marginValues = cfMargin.split(' ');
327
302
  const rightMargin = marginValues[1] || '0px';
@@ -339,7 +314,12 @@ const transformWidthSizing = ({ value, cfMargin, }) => {
339
314
  return transformedValue;
340
315
  };
341
316
 
342
- const toCSSAttribute = (key) => key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
317
+ const toCSSAttribute = (key) => {
318
+ let val = key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
319
+ // Remove the number from the end of the key to allow for overrides on style properties
320
+ val = val.replace(/\d+$/, '');
321
+ return val;
322
+ };
343
323
  const buildStyleTag = ({ styles, nodeId }) => {
344
324
  const stylesStr = Object.entries(styles)
345
325
  .filter(([, value]) => value !== undefined)
@@ -349,13 +329,13 @@ const buildStyleTag = ({ styles, nodeId }) => {
349
329
  const styleRule = `.${className}{ ${stylesStr} }`;
350
330
  return [className, styleRule];
351
331
  };
352
- const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection, cfFlexWrap, cfMargin, cfPadding, cfBackgroundColor, cfWidth, cfHeight, cfMaxWidth, cfBorder, cfGap, cfBackgroundImageUrl, cfBackgroundImageAlignment, cfBackgroundImageScaling, cfFontSize, cfFontWeight, cfLineHeight, cfLetterSpacing, cfTextColor, cfTextAlign, cfTextTransform, cfTextBold, cfTextItalic, cfTextUnderline, cfColumnSpan, }) => {
332
+ const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection, cfFlexWrap, cfMargin, cfPadding, cfBackgroundColor, cfWidth, cfHeight, cfMaxWidth, cfBorder, cfGap, cfBackgroundImageUrl, cfBackgroundImageOptions, cfFontSize, cfFontWeight, cfImageOptions, cfLineHeight, cfLetterSpacing, cfTextColor, cfTextAlign, cfTextTransform, cfTextBold, cfTextItalic, cfTextUnderline, cfColumnSpan, }) => {
353
333
  return {
354
334
  margin: cfMargin,
355
335
  padding: cfPadding,
356
336
  backgroundColor: cfBackgroundColor,
357
- width: transformWidthSizing({ value: cfWidth, cfMargin }),
358
- height: transformFill(cfHeight),
337
+ width: transformWidthSizing({ value: cfWidth || cfImageOptions?.width, cfMargin }),
338
+ height: transformFill(cfHeight || cfImageOptions?.height),
359
339
  maxWidth: cfMaxWidth,
360
340
  ...transformGridColumn(cfColumnSpan),
361
341
  ...transformBorderStyle(cfBorder),
@@ -363,7 +343,7 @@ const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirec
363
343
  ...transformAlignment(cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection),
364
344
  flexDirection: cfFlexDirection,
365
345
  flexWrap: cfFlexWrap,
366
- ...transformBackgroundImage(cfBackgroundImageUrl, cfBackgroundImageScaling, cfBackgroundImageAlignment),
346
+ ...transformBackgroundImage(cfBackgroundImageUrl, cfBackgroundImageOptions),
367
347
  fontSize: cfFontSize,
368
348
  fontWeight: cfTextBold ? 'bold' : cfFontWeight,
369
349
  fontStyle: cfTextItalic ? 'italic' : 'normal',
@@ -374,10 +354,12 @@ const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirec
374
354
  textTransform: cfTextTransform,
375
355
  textDecoration: cfTextUnderline ? 'underline' : 'none',
376
356
  boxSizing: 'border-box',
357
+ objectFit: cfImageOptions?.objectFit,
358
+ objectPosition: cfImageOptions?.objectPosition,
377
359
  };
378
360
  };
379
361
  /**
380
- * Container/section default behaviour:
362
+ * Container/section default behavior:
381
363
  * Default height => height: EMPTY_CONTAINER_HEIGHT (120px)
382
364
  * If a container component has children => height: 'fit-content'
383
365
  */
@@ -391,6 +373,206 @@ const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
391
373
  return EMPTY_CONTAINER_HEIGHT$1;
392
374
  };
393
375
 
376
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
377
+ function get(obj, path) {
378
+ if (!path.length) {
379
+ return obj;
380
+ }
381
+ try {
382
+ const [currentPath, ...nextPath] = path;
383
+ return get(obj[currentPath], nextPath);
384
+ }
385
+ catch (err) {
386
+ return undefined;
387
+ }
388
+ }
389
+
390
+ const getBoundValue = (entryOrAsset, path) => {
391
+ const value = get(entryOrAsset, path.split('/').slice(2, -1));
392
+ return value && typeof value == 'object' && value.url
393
+ ? value.url
394
+ : value;
395
+ };
396
+
397
+ const transformRichText = (entryOrAsset, path) => {
398
+ const value = getBoundValue(entryOrAsset, path);
399
+ if (typeof value === 'string') {
400
+ return {
401
+ data: {},
402
+ content: [
403
+ {
404
+ nodeType: BLOCKS.PARAGRAPH,
405
+ data: {},
406
+ content: [
407
+ {
408
+ data: {},
409
+ nodeType: 'text',
410
+ value: value,
411
+ marks: [],
412
+ },
413
+ ],
414
+ },
415
+ ],
416
+ nodeType: BLOCKS.DOCUMENT,
417
+ };
418
+ }
419
+ if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
420
+ return value;
421
+ }
422
+ return undefined;
423
+ };
424
+
425
+ function getOptimizedImageUrl(url, width, quality, format) {
426
+ const params = new URLSearchParams();
427
+ if (width) {
428
+ params.append('w', width.toString());
429
+ }
430
+ if (quality && quality > 0 && quality < 100) {
431
+ params.append('q', quality.toString());
432
+ }
433
+ if (format) {
434
+ params.append('fm', format);
435
+ }
436
+ const queryString = params.toString();
437
+ return `${url}${queryString ? '?' + queryString : ''}`;
438
+ }
439
+
440
+ const MAX_WIDTH_ALLOWED$1 = 2000;
441
+ const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = 100, format) => {
442
+ if (!validateParams(file, quality, format)) ;
443
+ const url = file.url;
444
+ const { width1x, width2x } = getWidths(widthStyle, file);
445
+ const imageUrl1x = getOptimizedImageUrl(url, width1x, quality, format);
446
+ const imageUrl2x = getOptimizedImageUrl(url, width2x, quality, format);
447
+ const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
448
+ const returnedUrlImageUrl = getOptimizedImageUrl(url, width2x, quality, format);
449
+ const optimizedBackgroundImageAsset = {
450
+ url: returnedUrlImageUrl,
451
+ srcSet,
452
+ file,
453
+ };
454
+ return optimizedBackgroundImageAsset;
455
+ function validateParams(file, quality, format) {
456
+ if (!file.details.image) {
457
+ throw Error('No image in file asset to transform');
458
+ }
459
+ if (quality < 0 || quality > 100) {
460
+ throw Error('Quality must be between 0 and 100');
461
+ }
462
+ if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
463
+ throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
464
+ }
465
+ return true;
466
+ }
467
+ };
468
+ function getWidths(widthStyle, file) {
469
+ let width1x = 0;
470
+ let width2x = 0;
471
+ const intrinsicImageWidth = file.details.image.width;
472
+ if (widthStyle.endsWith('px')) {
473
+ width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
474
+ }
475
+ else {
476
+ width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
477
+ }
478
+ width2x = Math.min(width1x * 2, intrinsicImageWidth);
479
+ return { width1x, width2x };
480
+ }
481
+
482
+ const MAX_WIDTH_ALLOWED = 4000;
483
+ const getOptimizedImageAsset = (file, sizes, quality = 100, format) => {
484
+ if (!validateParams(file, quality, format)) ;
485
+ const url = file.url;
486
+ const maxWidth = Math.min(file.details.image.width, MAX_WIDTH_ALLOWED);
487
+ const numOfParts = Math.max(2, Math.ceil(maxWidth / 500));
488
+ const widthParts = Array.from({ length: numOfParts }, (_, index) => Math.ceil((index + 1) * (maxWidth / numOfParts)));
489
+ const srcSet = sizes
490
+ ? widthParts.map((width) => `${getOptimizedImageUrl(url, width, quality, format)} ${width}w`)
491
+ : [];
492
+ const intrinsicImageWidth = file.details.image.width;
493
+ if (intrinsicImageWidth > MAX_WIDTH_ALLOWED) {
494
+ srcSet.push(`${getOptimizedImageUrl(url, undefined, quality, format)} ${intrinsicImageWidth}w`);
495
+ }
496
+ const returnedUrl = getOptimizedImageUrl(url, file.details.image.width > 2000 ? 2000 : undefined, quality, format);
497
+ const optimizedImageAsset = {
498
+ url: returnedUrl,
499
+ srcSet,
500
+ sizes,
501
+ file,
502
+ };
503
+ return optimizedImageAsset;
504
+ function validateParams(file, quality, format) {
505
+ if (!file.details.image) {
506
+ throw Error('No image in file asset to transform');
507
+ }
508
+ if (quality < 0 || quality > 100) {
509
+ throw Error('Quality must be between 0 and 100');
510
+ }
511
+ if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
512
+ throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
513
+ }
514
+ return true;
515
+ }
516
+ };
517
+
518
+ const transformMedia = (asset, variables, resolveDesignValue, variableName, path) => {
519
+ let value;
520
+ //TODO: this will be better served by injectable type transformers instead of if statement
521
+ if (variableName === 'cfImageAsset') {
522
+ const optionsVariableName = 'cfImageOptions';
523
+ const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
524
+ ? variables[optionsVariableName].valuesByBreakpoint
525
+ : {}, optionsVariableName);
526
+ if (!options) {
527
+ console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
528
+ return;
529
+ }
530
+ try {
531
+ value = getOptimizedImageAsset(asset.fields.file, options.targetSize, Number(options.quality), options.format);
532
+ return value;
533
+ }
534
+ catch (error) {
535
+ console.error('Error transforming image asset', error);
536
+ }
537
+ return;
538
+ }
539
+ if (variableName === 'cfBackgroundImageUrl') {
540
+ const width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth');
541
+ const optionsVariableName = 'cfBackgroundImageOptions';
542
+ const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
543
+ ? variables[optionsVariableName].valuesByBreakpoint
544
+ : {}, optionsVariableName);
545
+ if (!options) {
546
+ console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
547
+ return;
548
+ }
549
+ try {
550
+ value = getOptimizedBackgroundImageAsset(asset.fields.file, width, Number(options.quality), options.format);
551
+ return value;
552
+ }
553
+ catch (error) {
554
+ console.error('Error transforming image asset', error);
555
+ }
556
+ return;
557
+ }
558
+ // return getBoundValue(asset, entityStore, binding, path);
559
+ return getBoundValue(asset, path);
560
+ };
561
+
562
+ const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableDefinition, path) => {
563
+ const entityOrAsset = entityStore.getEntryOrAsset(binding, path);
564
+ if (!entityOrAsset)
565
+ return;
566
+ switch (variableDefinition.type) {
567
+ case 'Media':
568
+ return transformMedia(entityOrAsset, variables, resolveDesignValue, variableName, path);
569
+ case 'RichText':
570
+ return transformRichText(entityOrAsset, path);
571
+ default:
572
+ return getBoundValue(entityOrAsset, path);
573
+ }
574
+ };
575
+
394
576
  const getDataFromTree = (tree) => {
395
577
  let dataSource = {};
396
578
  let unboundValues = {};
@@ -529,42 +711,6 @@ const builtInStyles = {
529
711
  description: 'The spacing between the elements of the section',
530
712
  defaultValue: '0px',
531
713
  },
532
- cfBackgroundImageUrl: {
533
- displayName: 'Background image',
534
- type: 'Text',
535
- defaultValue: '',
536
- description: 'Background image for section or container',
537
- },
538
- cfBackgroundImageScaling: {
539
- displayName: 'Image scaling',
540
- type: 'Text',
541
- group: 'style',
542
- description: 'Adjust background image to fit, fill or tile the container',
543
- defaultValue: 'fit',
544
- validations: {
545
- in: [
546
- {
547
- value: 'fill',
548
- displayName: 'Fill',
549
- },
550
- {
551
- value: 'fit',
552
- displayName: 'Fit',
553
- },
554
- {
555
- value: 'tile',
556
- displayName: 'Tile',
557
- },
558
- ],
559
- },
560
- },
561
- cfBackgroundImageAlignment: {
562
- displayName: 'Image alignment',
563
- type: 'Text',
564
- group: 'style',
565
- description: 'Align background image to the edges of the container',
566
- defaultValue: 'left top',
567
- },
568
714
  cfHyperlink: {
569
715
  displayName: 'Hyperlink',
570
716
  type: 'Text',
@@ -612,6 +758,40 @@ const optionalBuiltInStyles = {
612
758
  description: 'The font weight of the element',
613
759
  defaultValue: '400',
614
760
  },
761
+ cfImageAsset: {
762
+ displayName: 'Image',
763
+ type: 'Media',
764
+ description: 'Image to display',
765
+ },
766
+ cfImageOptions: {
767
+ displayName: 'Image options',
768
+ type: 'Object',
769
+ group: 'style',
770
+ defaultValue: {
771
+ width: DEFAULT_IMAGE_WIDTH,
772
+ height: '100%',
773
+ objectFit: 'none',
774
+ objectPosition: 'center center',
775
+ quality: '100',
776
+ targetSize: DEFAULT_IMAGE_WIDTH,
777
+ },
778
+ },
779
+ cfBackgroundImageUrl: {
780
+ displayName: 'Background image',
781
+ type: 'Media',
782
+ description: 'Background image for component',
783
+ },
784
+ cfBackgroundImageOptions: {
785
+ displayName: 'Background image options',
786
+ type: 'Object',
787
+ group: 'style',
788
+ defaultValue: {
789
+ scaling: 'fill',
790
+ alignment: 'left top',
791
+ quality: '100',
792
+ targetSize: '2000px',
793
+ },
794
+ },
615
795
  cfLineHeight: {
616
796
  displayName: 'Line Height',
617
797
  type: 'Text',
@@ -994,29 +1174,9 @@ const sendMessage = (eventType, data) => {
994
1174
  }, '*');
995
1175
  };
996
1176
 
997
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
998
- function get(obj, path) {
999
- if (!path.length) {
1000
- return obj;
1001
- }
1002
- try {
1003
- const [currentPath, ...nextPath] = path;
1004
- return get(obj[currentPath], nextPath);
1005
- }
1006
- catch (err) {
1007
- return undefined;
1008
- }
1009
- }
1010
-
1011
- function transformAssetFileToUrl(fieldValue) {
1012
- return fieldValue && typeof fieldValue == 'object' && fieldValue.url
1013
- ? fieldValue.url
1014
- : fieldValue;
1015
- }
1016
-
1017
1177
  /**
1018
1178
  * Base Store for entities
1019
- * Can be extened for the different loading behaviours (editor, production, ..)
1179
+ * Can be extended for the different loading behaviours (editor, production, ..)
1020
1180
  */
1021
1181
  class EntityStoreBase {
1022
1182
  constructor({ entities, locale }) {
@@ -1033,68 +1193,26 @@ class EntityStoreBase {
1033
1193
  updateEntity(entity) {
1034
1194
  this.addEntity(entity);
1035
1195
  }
1036
- getValueDeep(headLinkOrEntity, deepPath) {
1037
- const resolveFieldset = (unresolvedFieldset, headEntity) => {
1038
- const resolvedFieldset = [];
1039
- let entityToResolveFieldsFrom = headEntity;
1040
- for (let i = 0; i < unresolvedFieldset.length; i++) {
1041
- const isLeaf = i === unresolvedFieldset.length - 1; // with last row, we are not expecting a link, but a value
1042
- const row = unresolvedFieldset[i];
1043
- const [, field, _localeQualifier] = row;
1044
- if (!entityToResolveFieldsFrom) {
1045
- throw new Error(`Logic Error: Cannot resolve field ${field} of a fieldset as there is no entity to resolve it from.`);
1046
- }
1047
- if (isLeaf) {
1048
- resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
1049
- break;
1050
- }
1051
- const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
1052
- if (undefined === fieldValue) {
1053
- return {
1054
- resolvedFieldset,
1055
- isFullyResolved: false,
1056
- reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
1057
- };
1058
- }
1059
- else if (isLink(fieldValue)) {
1060
- const entity = this.getEntityFromLink(fieldValue);
1061
- if (entity === undefined) {
1062
- throw new Error(`Logic Error: Broken Precondition [by the time resolution of deep path happens all referents should be in EntityStore]: Cannot resolve field ${field} of a fieldset row [${JSON.stringify(row)}] as linked entity not found in the EntityStore. ${JSON.stringify({
1063
- link: fieldValue,
1064
- })}`);
1065
- }
1066
- resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
1067
- entityToResolveFieldsFrom = entity; // we move up
1068
- }
1069
- else {
1070
- // TODO: Eg. when someone changed the schema and the field is not a link anymore, what should we return then?
1071
- throw new Error(`LogicError: Invalid value of a field we consider a reference field. Cannot resolve field ${field} of a fieldset as it is not a link, neither undefined.`);
1072
- }
1196
+ getEntryOrAsset(linkOrEntryOrAsset, path) {
1197
+ if (isDeepPath(path)) {
1198
+ return this.getDeepEntry(linkOrEntryOrAsset, path);
1199
+ }
1200
+ let entity;
1201
+ if (isLink(linkOrEntryOrAsset)) {
1202
+ const resolvedEntity = linkOrEntryOrAsset.sys.linkType === 'Entry'
1203
+ ? this.entryMap.get(linkOrEntryOrAsset.sys.id)
1204
+ : this.assetMap.get(linkOrEntryOrAsset.sys.id);
1205
+ if (!resolvedEntity || resolvedEntity.sys.type !== linkOrEntryOrAsset.sys.linkType) {
1206
+ console.warn(`Experience references unresolved entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
1207
+ return;
1073
1208
  }
1074
- return {
1075
- resolvedFieldset,
1076
- isFullyResolved: true,
1077
- };
1078
- };
1079
- const headEntity = isLink(headLinkOrEntity)
1080
- ? this.getEntityFromLink(headLinkOrEntity)
1081
- : headLinkOrEntity;
1082
- if (undefined === headEntity) {
1083
- return;
1209
+ entity = resolvedEntity;
1084
1210
  }
1085
- const unresolvedFieldset = parseDataSourcePathIntoFieldset(deepPath);
1086
- // The purpose here is to take this intermediate representation of the deep-path
1087
- // and to follow the links to the leaf-entity and field
1088
- // in case we can't follow till the end, we should signal that there was null-reference in the path
1089
- const { resolvedFieldset, isFullyResolved, reason } = resolveFieldset(unresolvedFieldset, headEntity);
1090
- if (!isFullyResolved) {
1091
- reason &&
1092
- console.debug(`[experiences-sdk-react::EntityStoreBased::getValueDeep()] Deep path wasn't resolved till leaf node, falling back to undefined, because: ${reason}`);
1093
- return undefined;
1211
+ else {
1212
+ // We already have the complete entity in preview & delivery (resolved by the CMA client)
1213
+ entity = linkOrEntryOrAsset;
1094
1214
  }
1095
- const [leafEntity, field /* localeQualifier */] = resolvedFieldset[resolvedFieldset.length - 1];
1096
- const fieldValue = get(leafEntity, ['fields', field]); // is allowed to be undefined (when non-required field not set; or even when field does NOT exist on the type)
1097
- return transformAssetFileToUrl(fieldValue);
1215
+ return entity;
1098
1216
  }
1099
1217
  /**
1100
1218
  * @deprecated in the base class this should be simply an abstract method
@@ -1151,7 +1269,7 @@ class EntityStoreBase {
1151
1269
  if (missing.length) {
1152
1270
  // TODO: move to `debug` utils once it is extracted
1153
1271
  console.warn(`Asset "${id}" is not in the store`);
1154
- return undefined;
1272
+ return;
1155
1273
  }
1156
1274
  return resolved[0];
1157
1275
  }
@@ -1167,7 +1285,7 @@ class EntityStoreBase {
1167
1285
  if (missing.length) {
1168
1286
  // TODO: move to `debug` utils once it is extracted
1169
1287
  console.warn(`Entry "${id}" is not in the store`);
1170
- return undefined;
1288
+ return;
1171
1289
  }
1172
1290
  return resolved[0];
1173
1291
  }
@@ -1178,6 +1296,70 @@ class EntityStoreBase {
1178
1296
  }
1179
1297
  return resolved;
1180
1298
  }
1299
+ getDeepEntry(linkOrEntryOrAsset, path) {
1300
+ const resolveFieldset = (unresolvedFieldset, headEntry) => {
1301
+ const resolvedFieldset = [];
1302
+ let entityToResolveFieldsFrom = headEntry;
1303
+ for (let i = 0; i < unresolvedFieldset.length; i++) {
1304
+ const isLeaf = i === unresolvedFieldset.length - 1; // with last row, we are not expecting a link, but a value
1305
+ const row = unresolvedFieldset[i];
1306
+ const [, field, _localeQualifier] = row;
1307
+ if (!entityToResolveFieldsFrom) {
1308
+ throw new Error(`Logic Error: Cannot resolve field ${field} of a fieldset as there is no entity to resolve it from.`);
1309
+ }
1310
+ if (isLeaf) {
1311
+ resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
1312
+ break;
1313
+ }
1314
+ const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
1315
+ if (undefined === fieldValue) {
1316
+ return {
1317
+ resolvedFieldset,
1318
+ isFullyResolved: false,
1319
+ reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
1320
+ };
1321
+ }
1322
+ else if (isLink(fieldValue)) {
1323
+ const entity = this.getEntityFromLink(fieldValue);
1324
+ if (entity === undefined) {
1325
+ return {
1326
+ resolvedFieldset,
1327
+ isFullyResolved: false,
1328
+ reason: `Field reference Link (sys.id=${fieldValue.sys.id}) not found in the EntityStore, waiting...`,
1329
+ };
1330
+ }
1331
+ resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
1332
+ entityToResolveFieldsFrom = entity; // we move up
1333
+ }
1334
+ else {
1335
+ // TODO: Eg. when someone changed the schema and the field is not a link anymore, what should we return then?
1336
+ throw new Error(`LogicError: Invalid value of a field we consider a reference field. Cannot resolve field ${field} of a fieldset as it is not a link, neither undefined.`);
1337
+ }
1338
+ }
1339
+ return {
1340
+ resolvedFieldset,
1341
+ isFullyResolved: true,
1342
+ };
1343
+ };
1344
+ const headEntity = isLink(linkOrEntryOrAsset)
1345
+ ? this.getEntityFromLink(linkOrEntryOrAsset)
1346
+ : linkOrEntryOrAsset;
1347
+ if (undefined === headEntity) {
1348
+ return;
1349
+ }
1350
+ const unresolvedFieldset = parseDataSourcePathIntoFieldset(path);
1351
+ // The purpose here is to take this intermediate representation of the deep-path
1352
+ // and to follow the links to the leaf-entity and field
1353
+ // in case we can't follow till the end, we should signal that there was null-reference in the path
1354
+ const { resolvedFieldset, isFullyResolved, reason } = resolveFieldset(unresolvedFieldset, headEntity);
1355
+ if (!isFullyResolved) {
1356
+ reason &&
1357
+ console.debug(`[exp-builder.sdk::EntityStoreBased::getValueDeep()] Deep path wasn't resolved till leaf node, falling back to undefined, because: ${reason}`);
1358
+ return;
1359
+ }
1360
+ const [leafEntity] = resolvedFieldset[resolvedFieldset.length - 1];
1361
+ return leafEntity;
1362
+ }
1181
1363
  isAsset(entity) {
1182
1364
  return entity.sys.type === 'Asset';
1183
1365
  }
@@ -1356,11 +1538,16 @@ class EditorModeEntityStore extends EditorEntityStore {
1356
1538
  const { missing: missingAssetIds } = this.getEntitiesFromMap('Asset', uniqueAssetIds);
1357
1539
  return { missingEntryIds, missingAssetIds };
1358
1540
  }
1359
- getValue(entityLink, path) {
1360
- if (!entityLink || !entityLink.sys)
1541
+ getValue(entityLinkOrEntity, path) {
1542
+ const entity = this.getEntryOrAsset(entityLinkOrEntity, path.join('/'));
1543
+ if (!entity) {
1361
1544
  return;
1362
- const fieldValue = super.getValue(entityLink, path);
1363
- return transformAssetFileToUrl(fieldValue);
1545
+ }
1546
+ const fieldValue = get(entity, path);
1547
+ // walk around to render asset files
1548
+ return fieldValue && typeof fieldValue == 'object' && fieldValue.url
1549
+ ? fieldValue.url
1550
+ : fieldValue;
1364
1551
  }
1365
1552
  }
1366
1553
 
@@ -1442,9 +1629,9 @@ function gatherDeepReferencesFromTree(startingNode, dataSource) {
1442
1629
  return deepReferences;
1443
1630
  }
1444
1631
 
1445
- var css_248z$6 = ".styles-module_DraggableComponent__m5-dA {\n pointer-events: all;\n position: relative;\n transition: outline 0.2s;\n cursor: grab;\n box-sizing: border-box;\n display: flex;\n}\n\n.styles-module_DraggableComponent__m5-dA:before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n outline-offset: -2px;\n outline: 2px solid transparent;\n z-index: 1;\n pointer-events: none;\n}\n\n.styles-module_DraggableClone__X8zTA:before {\n outline: 2px solid var(--exp-builder-blue500);\n}\n\n.styles-module_DraggableClone__X8zTA,\n.styles-module_DraggableClone__X8zTA * {\n pointer-events: none !important;\n}\n\n.styles-module_DraggableComponent__m5-dA:not(.styles-module_userIsDragging__lqbjG) :not(.styles-module_DraggableComponent__m5-dA) {\n pointer-events: none;\n}\n\n.styles-module_isDragging__WHjPU {\n overflow: hidden;\n}\n\n.styles-module_isSelected__BzICQ:before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_overlay__r4th9 {\n position: absolute;\n display: flex;\n align-items: center;\n min-width: max-content;\n height: 24px;\n z-index: 1;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 14px;\n font-weight: 500;\n background-color: var(--exp-builder-gray500);\n color: var(--exp-builder-color-white);\n border-radius: 0 0 2px 0;\n padding: 4px 12px 4px 12px;\n transition: opacity 0.2s;\n opacity: 0;\n text-wrap: nowrap;\n}\n\n.styles-module_overlayContainer__eiX-5 {\n opacity: 0;\n}\n\n.styles-module_overlayAssembly__tOzZU {\n background-color: var(--exp-builder-purple600);\n}\n\n.styles-module_userIsDragging__lqbjG > .styles-module_overlay__r4th9,\n.styles-module_userIsDragging__lqbjG > .styles-module_overlayContainer__eiX-5 {\n opacity: 0 !important;\n}\n\n.styles-module_userIsDragging__lqbjG:before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:not(:has(div[data-rfd-draggable-id]:hover)) > .styles-module_overlay__r4th9 {\n opacity: 1;\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:before,\n.styles-module_DraggableComponent__m5-dA:hover div[data-rfd-draggable-id]:before {\n outline: 2px dashed var(--exp-builder-gray500);\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:not(:has(div[data-rfd-draggable-id]:hover)):before {\n outline: 2px solid var(--exp-builder-gray500);\n}\n\n.styles-module_isAssemblyBlock__Y3Avk:hover:before,\n.styles-module_isAssemblyBlock__Y3Avk:hover div[data-rfd-draggable-id]:before,\n.styles-module_DraggableComponent__m5-dA:hover div[data-rfd-draggable-id][data-cf-node-block-type^='assembly']:before {\n outline: 2px dashed var(--exp-builder-purple600);\n}\n\n.styles-module_isAssemblyBlock__Y3Avk:hover:not(:has(div[data-rfd-draggable-id]:hover)):before {\n outline: 2px solid var(--exp-builder-purple600);\n}\n";
1632
+ var css_248z$7 = ".styles-module_DraggableComponent__m5-dA {\n pointer-events: all;\n position: relative;\n transition: outline 0.2s;\n cursor: grab;\n box-sizing: border-box;\n display: flex;\n}\n\n.styles-module_DraggableComponent__m5-dA:before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n outline-offset: -2px;\n outline: 2px solid transparent;\n z-index: 1;\n pointer-events: none;\n}\n\n.styles-module_DraggableClone__X8zTA:before {\n outline: 2px solid var(--exp-builder-blue500);\n}\n\n.styles-module_DraggableClone__X8zTA,\n.styles-module_DraggableClone__X8zTA * {\n pointer-events: none !important;\n}\n\n.styles-module_DraggableComponent__m5-dA:not(.styles-module_userIsDragging__lqbjG) :not(.styles-module_DraggableComponent__m5-dA) {\n pointer-events: none;\n}\n\n.styles-module_isDragging__WHjPU {\n overflow: hidden;\n}\n\n.styles-module_isSelected__BzICQ:before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_overlay__r4th9 {\n position: absolute;\n display: flex;\n align-items: center;\n min-width: max-content;\n height: 24px;\n z-index: 1;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 14px;\n font-weight: 500;\n background-color: var(--exp-builder-gray500);\n color: var(--exp-builder-color-white);\n border-radius: 0 0 2px 0;\n padding: 4px 12px 4px 12px;\n transition: opacity 0.2s;\n opacity: 0;\n text-wrap: nowrap;\n}\n\n.styles-module_overlayContainer__eiX-5 {\n opacity: 0;\n}\n\n.styles-module_overlayAssembly__tOzZU {\n background-color: var(--exp-builder-purple600);\n}\n\n.styles-module_userIsDragging__lqbjG > .styles-module_overlay__r4th9,\n.styles-module_userIsDragging__lqbjG > .styles-module_overlayContainer__eiX-5 {\n opacity: 0 !important;\n}\n\n.styles-module_userIsDragging__lqbjG:before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:not(:has(div[data-rfd-draggable-id]:hover)) > .styles-module_overlay__r4th9 {\n opacity: 1;\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:before,\n.styles-module_DraggableComponent__m5-dA:hover div[data-rfd-draggable-id]:before {\n outline: 2px dashed var(--exp-builder-gray500);\n}\n\n.styles-module_DraggableComponent__m5-dA:hover:not(:has(div[data-rfd-draggable-id]:hover)):before {\n outline: 2px solid var(--exp-builder-gray500);\n}\n\n.styles-module_isAssemblyBlock__Y3Avk:hover:before,\n.styles-module_isAssemblyBlock__Y3Avk:hover div[data-rfd-draggable-id]:before,\n.styles-module_DraggableComponent__m5-dA:hover div[data-rfd-draggable-id][data-cf-node-block-type^='assembly']:before {\n outline: 2px dashed var(--exp-builder-purple600);\n}\n\n.styles-module_isAssemblyBlock__Y3Avk:hover:not(:has(div[data-rfd-draggable-id]:hover)):before {\n outline: 2px solid var(--exp-builder-purple600);\n}\n";
1446
1633
  var styles$3 = {"DraggableComponent":"styles-module_DraggableComponent__m5-dA","DraggableClone":"styles-module_DraggableClone__X8zTA","userIsDragging":"styles-module_userIsDragging__lqbjG","isDragging":"styles-module_isDragging__WHjPU","isSelected":"styles-module_isSelected__BzICQ","overlay":"styles-module_overlay__r4th9","overlayContainer":"styles-module_overlayContainer__eiX-5","overlayAssembly":"styles-module_overlayAssembly__tOzZU","isAssemblyBlock":"styles-module_isAssemblyBlock__Y3Avk"};
1447
- styleInject(css_248z$6);
1634
+ styleInject(css_248z$7);
1448
1635
 
1449
1636
  const SCROLL_STATES = {
1450
1637
  Start: 'scrollStart',
@@ -1547,13 +1734,14 @@ const CF_STYLE_ATTRIBUTES = [
1547
1734
  'cfWidth',
1548
1735
  'cfMaxWidth',
1549
1736
  'cfHeight',
1737
+ 'cfImageAsset',
1738
+ 'cfImageOptions',
1739
+ 'cfBackgroundImageUrl',
1740
+ 'cfBackgroundImageOptions',
1550
1741
  'cfFlexDirection',
1551
1742
  'cfFlexWrap',
1552
1743
  'cfBorder',
1553
1744
  'cfGap',
1554
- 'cfBackgroundImageUrl',
1555
- 'cfBackgroundImageScaling',
1556
- 'cfBackgroundImageAlignment',
1557
1745
  'cfFontSize',
1558
1746
  'cfFontWeight',
1559
1747
  'cfLineHeight',
@@ -1568,6 +1756,8 @@ const CF_STYLE_ATTRIBUTES = [
1568
1756
  // we need to keep those in this constant array
1569
1757
  // so that omit() in <VisualEditorBlock> and <CompositionBlock>
1570
1758
  // can filter them out and not pass as props
1759
+ 'cfBackgroundImageScaling',
1760
+ 'cfBackgroundImageAlignment',
1571
1761
  'cfBackgroundImageAlignmentVertical',
1572
1762
  'cfBackgroundImageAlignmentHorizontal',
1573
1763
  ];
@@ -2161,48 +2351,25 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
2161
2351
  };
2162
2352
  }
2163
2353
  else if (variableMapping.type === 'BoundValue') {
2164
- if (!areEntitiesFetched) {
2165
- console.debug(`[experiences-sdk-react::useComponentProps] Idle-cycle: as entities are not fetched(areEntitiesFetched=${areEntitiesFetched}), we cannot resolve bound values for ${variableName} so we just resolve them to default values.`);
2166
- // Just forcing default value (if we're in idle-cycle, entities are missing)
2167
- return {
2168
- ...acc,
2169
- [variableName]: transformContentValue(variableDefinition.defaultValue, variableDefinition),
2170
- };
2171
- }
2172
- if (isDeepPath(variableMapping.path)) {
2173
- const [, uuid] = variableMapping.path.split('/');
2174
- const link = dataSource[uuid];
2175
- const boundValue = entityStore?.getValueDeep(link, variableMapping.path);
2176
- const value = boundValue || variableDefinition.defaultValue;
2177
- return {
2178
- ...acc,
2179
- [variableName]: transformContentValue(value, variableDefinition),
2180
- };
2181
- }
2182
- // // take value from the datasource for both bound and unbound value types
2183
- const [, uuid, ...path] = variableMapping.path.split('/');
2354
+ const [, uuid, path] = variableMapping.path.split('/');
2184
2355
  const binding = dataSource[uuid];
2185
- let boundValue = areEntitiesFetched
2186
- ? entityStore.getValue(binding, path.slice(0, -1))
2187
- : undefined;
2356
+ const variableDefinition = definition.variables[variableName];
2357
+ let boundValue = transformBoundContentValue(node.data.props, entityStore, binding, resolveDesignValue, variableName, variableDefinition, variableMapping.path);
2188
2358
  // In some cases, there may be an asset linked in the path, so we need to consider this scenario:
2189
2359
  // If no 'boundValue' is found, we also attempt to extract the value associated with the second-to-last item in the path.
2190
2360
  // If successful, it means we have identified the linked asset.
2191
2361
  if (!boundValue) {
2192
2362
  const maybeBoundAsset = areEntitiesFetched
2193
- ? entityStore.getValue(binding, path.slice(0, -2))
2363
+ ? entityStore.getValue(binding, path.split('/').slice(0, -2))
2194
2364
  : undefined;
2195
2365
  if (isLinkToAsset(maybeBoundAsset)) {
2196
2366
  boundValue = maybeBoundAsset;
2197
2367
  }
2198
2368
  }
2199
- if (typeof boundValue === 'object' && boundValue.sys?.linkType === 'Asset') {
2200
- boundValue = entityStore?.getValue(boundValue, ['fields', 'file']);
2201
- }
2202
2369
  const value = boundValue || variableDefinition.defaultValue;
2203
2370
  return {
2204
2371
  ...acc,
2205
- [variableName]: transformContentValue(value, variableDefinition),
2372
+ [variableName]: value,
2206
2373
  };
2207
2374
  }
2208
2375
  else {
@@ -2275,12 +2442,15 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
2275
2442
  'data-cf-node-block-id': node.data.blockId,
2276
2443
  'data-cf-node-block-type': node.type,
2277
2444
  };
2445
+ //List explicit style props that will end up being passed to the component
2446
+ const stylesToKeep = ['cfImageAsset'];
2447
+ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.includes(style));
2278
2448
  const componentProps = {
2279
2449
  className: componentClass,
2280
2450
  editorMode: true,
2281
2451
  node,
2282
2452
  renderDropzone,
2283
- ...omit(props, CF_STYLE_ATTRIBUTES, ['cfHyperlink', 'cfOpenInNewTab']),
2453
+ ...omit(props, stylesToRemove, ['cfHyperlink', 'cfOpenInNewTab']),
2284
2454
  ...(definition.children ? { children: renderDropzone(node) } : {}),
2285
2455
  };
2286
2456
  return { componentProps, wrapperProps };
@@ -2302,13 +2472,16 @@ var PostMessageMethods;
2302
2472
  PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
2303
2473
  })(PostMessageMethods || (PostMessageMethods = {}));
2304
2474
 
2305
- var css_248z$4 = ".cf-heading {\n white-space: pre-line;\n}\n";
2475
+ var css_248z$5 = ".cf-heading {\n white-space: pre-line;\n}\n";
2476
+ styleInject(css_248z$5);
2477
+
2478
+ var css_248z$4 = ".cf-richtext {\n white-space: pre-line;\n}\n";
2306
2479
  styleInject(css_248z$4);
2307
2480
 
2308
- var css_248z$3 = ".cf-richtext {\n white-space: pre-line;\n}\n";
2481
+ var css_248z$3 = ".cf-text {\n white-space: pre-line;\n}\n";
2309
2482
  styleInject(css_248z$3);
2310
2483
 
2311
- var css_248z$2$1 = ".cf-text {\n white-space: pre-line;\n}\n";
2484
+ var css_248z$2$1 = ".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[data-ctfl-draggable-id] .cf-no-image {\n width: 100%;\n height: 100%;\n}\n\n[data-ctfl-draggable-id] .cf-no-image img {\n width: 100%;\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";
2312
2485
  styleInject(css_248z$2$1);
2313
2486
 
2314
2487
  var css_248z$1$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.cf-container-label {\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: 10;\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";
@@ -2336,8 +2509,8 @@ const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave,
2336
2509
  });
2337
2510
  Flex.displayName = 'Flex';
2338
2511
 
2339
- var css_248z$5 = ".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 .Columns {\n display: grid;\n }\n}\n\n.cf-single-column-wrapper {\n position: relative;\n}\n\n.cf-single-column {\n pointer-events: all;\n}\n\n.cf-single-column-label {\n pointer-events: none;\n position: absolute;\n z-index: -1;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 12px;\n color: var(--exp-builder-gray400);\n z-index: 100;\n}\n";
2340
- styleInject(css_248z$5);
2512
+ var css_248z$6 = ".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 .Columns {\n display: grid;\n }\n}\n\n.cf-single-column-wrapper {\n position: relative;\n}\n\n.cf-single-column {\n pointer-events: all;\n}\n\n.cf-single-column-label {\n pointer-events: none;\n position: absolute;\n z-index: -1;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 12px;\n color: var(--exp-builder-gray400);\n z-index: 100;\n}\n";
2513
+ styleInject(css_248z$6);
2341
2514
 
2342
2515
  const ColumnWrapper = forwardRef((props, ref) => {
2343
2516
  return (React.createElement("div", { ref: ref, ...props, style: {
@@ -2560,7 +2733,7 @@ const DraggableChildComponent = (props) => {
2560
2733
  })));
2561
2734
  };
2562
2735
 
2563
- var css_248z$2 = ".styles-module_container__te-1H {\n margin-left: auto;\n margin-right: auto;\n position: relative;\n height: 100%;\n width: 100%;\n background-color: transparent;\n transition: background-color 0.2s;\n pointer-events: all !important;\n}\n\n.styles-module_container__te-1H:not(.styles-module_isRoot__5cn-i):before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n outline-offset: -1px;\n outline: 2px solid transparent;\n z-index: 1;\n transition: outline 0.2s;\n pointer-events: none;\n}\n\n.styles-module_isRoot__5cn-i,\n.styles-module_isEmptyCanvas__0XHZR {\n flex: 1;\n}\n\n.styles-module_isEmptyZone__zVpnZ {\n min-height: 80px;\n}\n\n.styles-module_isDragging__Gm8v5:not(.styles-module_isRoot__5cn-i):before {\n outline: 2px dashed var(--exp-builder-gray300);\n}\n\n.styles-module_isDestination__5sCQx:not(.styles-module_isRoot__5cn-i):before {\n transition:\n outline 0.2s,\n background-color 0.2s;\n outline: 2px dashed var(--exp-builder-blue400);\n background-color: rgba(var(--exp-builder-blue100-rgb), 0.5);\n z-index: 2;\n}\n\n.styles-module_hitbox__YQ-1Z {\n position: fixed;\n pointer-events: all !important;\n}\n";
2736
+ var css_248z$2 = ".styles-module_container__te-1H {\n margin-left: auto;\n margin-right: auto;\n position: relative;\n height: 100%;\n width: 100%;\n background-color: transparent;\n transition: background-color 0.2s;\n pointer-events: all !important;\n}\n\n.styles-module_container__te-1H:not(.styles-module_isRoot__5cn-i):before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n outline-offset: -1px;\n outline: 2px solid transparent;\n z-index: 1;\n transition: outline 0.2s;\n pointer-events: none;\n}\n\n.styles-module_isRoot__5cn-i,\n.styles-module_isEmptyCanvas__0XHZR {\n flex: 1;\n}\n\n.styles-module_isEmptyZone__zVpnZ {\n min-height: 80px;\n}\n\n.styles-module_isDragging__Gm8v5:not(.styles-module_isRoot__5cn-i):before {\n outline: 2px dashed var(--exp-builder-gray300);\n}\n\n.styles-module_isDestination__5sCQx:not(.styles-module_isRoot__5cn-i):before {\n transition:\n outline 0.2s,\n background-color 0.2s;\n outline: 2px dashed var(--exp-builder-blue400);\n background-color: rgba(var(--exp-builder-blue100-rgb), 0.5);\n z-index: 2;\n}\n\n.styles-module_hitbox__YQ-1Z {\n position: fixed;\n pointer-events: all !important;\n}\n\n.styles-module_hitbox__YQ-1Z {\n position: fixed;\n pointer-events: all !important;\n}\n";
2564
2737
  var styles$2 = {"container":"styles-module_container__te-1H","isRoot":"styles-module_isRoot__5cn-i","isEmptyCanvas":"styles-module_isEmptyCanvas__0XHZR","isEmptyZone":"styles-module_isEmptyZone__zVpnZ","isDragging":"styles-module_isDragging__Gm8v5","isDestination":"styles-module_isDestination__5sCQx","hitbox":"styles-module_hitbox__YQ-1Z"};
2565
2738
  styleInject(css_248z$2);
2566
2739