@contentful/experiences-visual-editor-react 1.7.1 → 1.7.2-dev-20240613T0816-4d3b2da.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,8 +1,8 @@
1
1
  import styleInject from 'style-inject';
2
2
  import React, { useRef, useMemo, useEffect, useState, useLayoutEffect, forwardRef, useCallback } from 'react';
3
3
  import md5 from 'md5';
4
- import { BLOCKS } from '@contentful/rich-text-types';
5
4
  import { z } from 'zod';
5
+ import { BLOCKS } from '@contentful/rich-text-types';
6
6
  import { isEqual, get as get$1, omit } from 'lodash-es';
7
7
  import { Draggable, Droppable, DragDropContext } from '@hello-pangea/dnd';
8
8
  import classNames from 'classnames';
@@ -361,237 +361,6 @@ const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
361
361
  return EMPTY_CONTAINER_HEIGHT$1;
362
362
  };
363
363
 
364
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
365
- function get(obj, path) {
366
- if (!path.length) {
367
- return obj;
368
- }
369
- try {
370
- const [currentPath, ...nextPath] = path;
371
- return get(obj[currentPath], nextPath);
372
- }
373
- catch (err) {
374
- return undefined;
375
- }
376
- }
377
-
378
- const getBoundValue = (entryOrAsset, path) => {
379
- const value = get(entryOrAsset, path.split('/').slice(2, -1));
380
- return value && typeof value == 'object' && value.url
381
- ? value.url
382
- : value;
383
- };
384
-
385
- const transformRichText = (entryOrAsset, path) => {
386
- const value = getBoundValue(entryOrAsset, path);
387
- if (typeof value === 'string') {
388
- return {
389
- data: {},
390
- content: [
391
- {
392
- nodeType: BLOCKS.PARAGRAPH,
393
- data: {},
394
- content: [
395
- {
396
- data: {},
397
- nodeType: 'text',
398
- value: value,
399
- marks: [],
400
- },
401
- ],
402
- },
403
- ],
404
- nodeType: BLOCKS.DOCUMENT,
405
- };
406
- }
407
- if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
408
- return value;
409
- }
410
- return undefined;
411
- };
412
-
413
- function getOptimizedImageUrl(url, width, quality, format) {
414
- if (url.startsWith('//')) {
415
- url = 'https:' + url;
416
- }
417
- const params = new URLSearchParams();
418
- if (width) {
419
- params.append('w', width.toString());
420
- }
421
- if (quality && quality > 0 && quality < 100) {
422
- params.append('q', quality.toString());
423
- }
424
- if (format) {
425
- params.append('fm', format);
426
- }
427
- const queryString = params.toString();
428
- return `${url}${queryString ? '?' + queryString : ''}`;
429
- }
430
-
431
- function validateParams(file, quality, format) {
432
- if (!file.details.image) {
433
- throw Error('No image in file asset to transform');
434
- }
435
- if (quality < 0 || quality > 100) {
436
- throw Error('Quality must be between 0 and 100');
437
- }
438
- if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
439
- throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
440
- }
441
- return true;
442
- }
443
-
444
- const MAX_WIDTH_ALLOWED$1 = 2000;
445
- const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', format) => {
446
- const qualityNumber = Number(quality.replace('%', ''));
447
- if (!validateParams(file, qualityNumber, format)) ;
448
- if (!validateParams(file, qualityNumber, format)) ;
449
- const url = file.url;
450
- const { width1x, width2x } = getWidths(widthStyle, file);
451
- const imageUrl1x = getOptimizedImageUrl(url, width1x, qualityNumber, format);
452
- const imageUrl2x = getOptimizedImageUrl(url, width2x, qualityNumber, format);
453
- const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
454
- const returnedUrl = getOptimizedImageUrl(url, width2x, qualityNumber, format);
455
- const optimizedBackgroundImageAsset = {
456
- url: returnedUrl,
457
- srcSet,
458
- file,
459
- };
460
- return optimizedBackgroundImageAsset;
461
- function getWidths(widthStyle, file) {
462
- let width1x = 0;
463
- let width2x = 0;
464
- const intrinsicImageWidth = file.details.image.width;
465
- if (widthStyle.endsWith('px')) {
466
- width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
467
- }
468
- else {
469
- width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
470
- }
471
- width2x = Math.min(width1x * 2, intrinsicImageWidth);
472
- return { width1x, width2x };
473
- }
474
- };
475
-
476
- const MAX_WIDTH_ALLOWED = 4000;
477
- const getOptimizedImageAsset = ({ file, sizes, loading, quality = '100%', format, }) => {
478
- const qualityNumber = Number(quality.replace('%', ''));
479
- if (!validateParams(file, qualityNumber, format)) ;
480
- const url = file.url;
481
- const maxWidth = Math.min(file.details.image.width, MAX_WIDTH_ALLOWED);
482
- const numOfParts = Math.max(2, Math.ceil(maxWidth / 500));
483
- const widthParts = Array.from({ length: numOfParts }, (_, index) => Math.ceil((index + 1) * (maxWidth / numOfParts)));
484
- const srcSet = sizes
485
- ? widthParts.map((width) => `${getOptimizedImageUrl(url, width, qualityNumber, format)} ${width}w`)
486
- : [];
487
- const intrinsicImageWidth = file.details.image.width;
488
- if (intrinsicImageWidth > MAX_WIDTH_ALLOWED) {
489
- srcSet.push(`${getOptimizedImageUrl(url, undefined, qualityNumber, format)} ${intrinsicImageWidth}w`);
490
- }
491
- const returnedUrl = getOptimizedImageUrl(url, file.details.image.width > 2000 ? 2000 : undefined, qualityNumber, format);
492
- const optimizedImageAsset = {
493
- url: returnedUrl,
494
- srcSet,
495
- sizes,
496
- file,
497
- loading,
498
- };
499
- return optimizedImageAsset;
500
- };
501
-
502
- const transformMedia = (asset, variables, resolveDesignValue, variableName, path) => {
503
- let value;
504
- // If it is not a deep path and not pointing to the file of the asset,
505
- // it is just pointing to a normal field and therefore we just resolve the value as normal field
506
- if (!isDeepPath(path) && !lastPathNamedSegmentEq(path, 'file')) {
507
- return getBoundValue(asset, path);
508
- }
509
- //TODO: this will be better served by injectable type transformers instead of if statement
510
- if (variableName === 'cfImageAsset') {
511
- const optionsVariableName = 'cfImageOptions';
512
- const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
513
- ? variables[optionsVariableName].valuesByBreakpoint
514
- : {}, optionsVariableName);
515
- if (!options) {
516
- console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
517
- return;
518
- }
519
- try {
520
- value = getOptimizedImageAsset({
521
- file: asset.fields.file,
522
- loading: options.loading,
523
- sizes: options.targetSize,
524
- quality: options.quality,
525
- format: options.format,
526
- });
527
- return value;
528
- }
529
- catch (error) {
530
- console.error('Error transforming image asset', error);
531
- }
532
- return;
533
- }
534
- if (variableName === 'cfBackgroundImageUrl') {
535
- const width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth');
536
- const optionsVariableName = 'cfBackgroundImageOptions';
537
- const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
538
- ? variables[optionsVariableName].valuesByBreakpoint
539
- : {}, optionsVariableName);
540
- if (!options) {
541
- console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
542
- return;
543
- }
544
- try {
545
- value = getOptimizedBackgroundImageAsset(asset.fields.file, width, options.quality, options.format);
546
- return value;
547
- }
548
- catch (error) {
549
- console.error('Error transforming image asset', error);
550
- }
551
- return;
552
- }
553
- return asset.fields.file?.url;
554
- };
555
-
556
- const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableDefinition, path) => {
557
- const entityOrAsset = entityStore.getEntryOrAsset(binding, path);
558
- if (!entityOrAsset)
559
- return;
560
- switch (variableDefinition.type) {
561
- case 'Media':
562
- // If we bound a normal entry field to the media veriable we just return the bound value
563
- if (entityOrAsset.sys.type === 'Entry') {
564
- return getBoundValue(entityOrAsset, path);
565
- }
566
- return transformMedia(entityOrAsset, variables, resolveDesignValue, variableName, path);
567
- case 'RichText':
568
- return transformRichText(entityOrAsset, path);
569
- default:
570
- return getBoundValue(entityOrAsset, path);
571
- }
572
- };
573
-
574
- const getDataFromTree = (tree) => {
575
- let dataSource = {};
576
- let unboundValues = {};
577
- const queue = [...tree.root.children];
578
- while (queue.length) {
579
- const node = queue.shift();
580
- if (!node) {
581
- continue;
582
- }
583
- dataSource = { ...dataSource, ...node.data.dataSource };
584
- unboundValues = { ...unboundValues, ...node.data.unboundValues };
585
- if (node.children.length) {
586
- queue.push(...node.children);
587
- }
588
- }
589
- return {
590
- dataSource,
591
- unboundValues,
592
- };
593
- };
594
-
595
364
  // These styles get added to every component, user custom or contentful provided
596
365
  const builtInStyles = {
597
366
  cfVerticalAlignment: {
@@ -1178,6 +947,237 @@ var CodeNames;
1178
947
  CodeNames["Custom"] = "custom";
1179
948
  })(CodeNames || (CodeNames = {}));
1180
949
 
950
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
951
+ function get(obj, path) {
952
+ if (!path.length) {
953
+ return obj;
954
+ }
955
+ try {
956
+ const [currentPath, ...nextPath] = path;
957
+ return get(obj[currentPath], nextPath);
958
+ }
959
+ catch (err) {
960
+ return undefined;
961
+ }
962
+ }
963
+
964
+ const getBoundValue = (entryOrAsset, path) => {
965
+ const value = get(entryOrAsset, path.split('/').slice(2, -1));
966
+ return value && typeof value == 'object' && value.url
967
+ ? value.url
968
+ : value;
969
+ };
970
+
971
+ const transformRichText = (entryOrAsset, path) => {
972
+ const value = getBoundValue(entryOrAsset, path);
973
+ if (typeof value === 'string') {
974
+ return {
975
+ data: {},
976
+ content: [
977
+ {
978
+ nodeType: BLOCKS.PARAGRAPH,
979
+ data: {},
980
+ content: [
981
+ {
982
+ data: {},
983
+ nodeType: 'text',
984
+ value: value,
985
+ marks: [],
986
+ },
987
+ ],
988
+ },
989
+ ],
990
+ nodeType: BLOCKS.DOCUMENT,
991
+ };
992
+ }
993
+ if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
994
+ return value;
995
+ }
996
+ return undefined;
997
+ };
998
+
999
+ function getOptimizedImageUrl(url, width, quality, format) {
1000
+ if (url.startsWith('//')) {
1001
+ url = 'https:' + url;
1002
+ }
1003
+ const params = new URLSearchParams();
1004
+ if (width) {
1005
+ params.append('w', width.toString());
1006
+ }
1007
+ if (quality && quality > 0 && quality < 100) {
1008
+ params.append('q', quality.toString());
1009
+ }
1010
+ if (format) {
1011
+ params.append('fm', format);
1012
+ }
1013
+ const queryString = params.toString();
1014
+ return `${url}${queryString ? '?' + queryString : ''}`;
1015
+ }
1016
+
1017
+ function validateParams(file, quality, format) {
1018
+ if (!file.details.image) {
1019
+ throw Error('No image in file asset to transform');
1020
+ }
1021
+ if (quality < 0 || quality > 100) {
1022
+ throw Error('Quality must be between 0 and 100');
1023
+ }
1024
+ if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
1025
+ throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
1026
+ }
1027
+ return true;
1028
+ }
1029
+
1030
+ const MAX_WIDTH_ALLOWED$1 = 2000;
1031
+ const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', format) => {
1032
+ const qualityNumber = Number(quality.replace('%', ''));
1033
+ if (!validateParams(file, qualityNumber, format)) ;
1034
+ if (!validateParams(file, qualityNumber, format)) ;
1035
+ const url = file.url;
1036
+ const { width1x, width2x } = getWidths(widthStyle, file);
1037
+ const imageUrl1x = getOptimizedImageUrl(url, width1x, qualityNumber, format);
1038
+ const imageUrl2x = getOptimizedImageUrl(url, width2x, qualityNumber, format);
1039
+ const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
1040
+ const returnedUrl = getOptimizedImageUrl(url, width2x, qualityNumber, format);
1041
+ const optimizedBackgroundImageAsset = {
1042
+ url: returnedUrl,
1043
+ srcSet,
1044
+ file,
1045
+ };
1046
+ return optimizedBackgroundImageAsset;
1047
+ function getWidths(widthStyle, file) {
1048
+ let width1x = 0;
1049
+ let width2x = 0;
1050
+ const intrinsicImageWidth = file.details.image.width;
1051
+ if (widthStyle.endsWith('px')) {
1052
+ width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
1053
+ }
1054
+ else {
1055
+ width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
1056
+ }
1057
+ width2x = Math.min(width1x * 2, intrinsicImageWidth);
1058
+ return { width1x, width2x };
1059
+ }
1060
+ };
1061
+
1062
+ const MAX_WIDTH_ALLOWED = 4000;
1063
+ const getOptimizedImageAsset = ({ file, sizes, loading, quality = '100%', format, }) => {
1064
+ const qualityNumber = Number(quality.replace('%', ''));
1065
+ if (!validateParams(file, qualityNumber, format)) ;
1066
+ const url = file.url;
1067
+ const maxWidth = Math.min(file.details.image.width, MAX_WIDTH_ALLOWED);
1068
+ const numOfParts = Math.max(2, Math.ceil(maxWidth / 500));
1069
+ const widthParts = Array.from({ length: numOfParts }, (_, index) => Math.ceil((index + 1) * (maxWidth / numOfParts)));
1070
+ const srcSet = sizes
1071
+ ? widthParts.map((width) => `${getOptimizedImageUrl(url, width, qualityNumber, format)} ${width}w`)
1072
+ : [];
1073
+ const intrinsicImageWidth = file.details.image.width;
1074
+ if (intrinsicImageWidth > MAX_WIDTH_ALLOWED) {
1075
+ srcSet.push(`${getOptimizedImageUrl(url, undefined, qualityNumber, format)} ${intrinsicImageWidth}w`);
1076
+ }
1077
+ const returnedUrl = getOptimizedImageUrl(url, file.details.image.width > 2000 ? 2000 : undefined, qualityNumber, format);
1078
+ const optimizedImageAsset = {
1079
+ url: returnedUrl,
1080
+ srcSet,
1081
+ sizes,
1082
+ file,
1083
+ loading,
1084
+ };
1085
+ return optimizedImageAsset;
1086
+ };
1087
+
1088
+ const transformMedia = (asset, variables, resolveDesignValue, variableName, path) => {
1089
+ let value;
1090
+ // If it is not a deep path and not pointing to the file of the asset,
1091
+ // it is just pointing to a normal field and therefore we just resolve the value as normal field
1092
+ if (!isDeepPath(path) && !lastPathNamedSegmentEq(path, 'file')) {
1093
+ return getBoundValue(asset, path);
1094
+ }
1095
+ //TODO: this will be better served by injectable type transformers instead of if statement
1096
+ if (variableName === 'cfImageAsset') {
1097
+ const optionsVariableName = 'cfImageOptions';
1098
+ const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
1099
+ ? variables[optionsVariableName].valuesByBreakpoint
1100
+ : {}, optionsVariableName);
1101
+ if (!options) {
1102
+ console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
1103
+ return;
1104
+ }
1105
+ try {
1106
+ value = getOptimizedImageAsset({
1107
+ file: asset.fields.file,
1108
+ loading: options.loading,
1109
+ sizes: options.targetSize,
1110
+ quality: options.quality,
1111
+ format: options.format,
1112
+ });
1113
+ return value;
1114
+ }
1115
+ catch (error) {
1116
+ console.error('Error transforming image asset', error);
1117
+ }
1118
+ return;
1119
+ }
1120
+ if (variableName === 'cfBackgroundImageUrl') {
1121
+ const width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth');
1122
+ const optionsVariableName = 'cfBackgroundImageOptions';
1123
+ const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
1124
+ ? variables[optionsVariableName].valuesByBreakpoint
1125
+ : {}, optionsVariableName);
1126
+ if (!options) {
1127
+ console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
1128
+ return;
1129
+ }
1130
+ try {
1131
+ value = getOptimizedBackgroundImageAsset(asset.fields.file, width, options.quality, options.format);
1132
+ return value;
1133
+ }
1134
+ catch (error) {
1135
+ console.error('Error transforming image asset', error);
1136
+ }
1137
+ return;
1138
+ }
1139
+ return asset.fields.file?.url;
1140
+ };
1141
+
1142
+ const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableDefinition, path) => {
1143
+ const entityOrAsset = entityStore.getEntryOrAsset(binding, path);
1144
+ if (!entityOrAsset)
1145
+ return;
1146
+ switch (variableDefinition.type) {
1147
+ case 'Media':
1148
+ // If we bound a normal entry field to the media veriable we just return the bound value
1149
+ if (entityOrAsset.sys.type === 'Entry') {
1150
+ return getBoundValue(entityOrAsset, path);
1151
+ }
1152
+ return transformMedia(entityOrAsset, variables, resolveDesignValue, variableName, path);
1153
+ case 'RichText':
1154
+ return transformRichText(entityOrAsset, path);
1155
+ default:
1156
+ return getBoundValue(entityOrAsset, path);
1157
+ }
1158
+ };
1159
+
1160
+ const getDataFromTree = (tree) => {
1161
+ let dataSource = {};
1162
+ let unboundValues = {};
1163
+ const queue = [...tree.root.children];
1164
+ while (queue.length) {
1165
+ const node = queue.shift();
1166
+ if (!node) {
1167
+ continue;
1168
+ }
1169
+ dataSource = { ...dataSource, ...node.data.dataSource };
1170
+ unboundValues = { ...unboundValues, ...node.data.unboundValues };
1171
+ if (node.children.length) {
1172
+ queue.push(...node.children);
1173
+ }
1174
+ }
1175
+ return {
1176
+ dataSource,
1177
+ unboundValues,
1178
+ };
1179
+ };
1180
+
1181
1181
  const MEDIA_QUERY_REGEXP = /(<|>)(\d{1,})(px|cm|mm|in|pt|pc)$/;
1182
1182
  const toCSSMediaQuery = ({ query }) => {
1183
1183
  if (query === '*')