@contentful/experiences-visual-editor-react 1.36.0 → 1.37.0-dev-20250423T1119-4e477a9.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 +1113 -1113
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +2497 -2497
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
import React, { useEffect, useRef, useState, useCallback, forwardRef, useLayoutEffect, useMemo } from 'react';
|
|
3
|
-
import md5 from 'md5';
|
|
4
3
|
import { z } from 'zod';
|
|
5
|
-
import { BLOCKS } from '@contentful/rich-text-types';
|
|
6
4
|
import { omit, isArray, isEqual, get as get$1 } from 'lodash-es';
|
|
5
|
+
import md5 from 'md5';
|
|
6
|
+
import { BLOCKS } from '@contentful/rich-text-types';
|
|
7
7
|
import { create } from 'zustand';
|
|
8
8
|
import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd';
|
|
9
9
|
import { produce } from 'immer';
|
|
@@ -163,497 +163,181 @@ const isStructureWithRelativeHeight = (componentId, height) => {
|
|
|
163
163
|
return isContentfulStructureComponent(componentId) && !height?.toString().endsWith('px');
|
|
164
164
|
};
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
166
|
+
// These styles get added to every component, user custom or contentful provided
|
|
167
|
+
const builtInStyles = {
|
|
168
|
+
cfVerticalAlignment: {
|
|
169
|
+
validations: {
|
|
170
|
+
in: [
|
|
171
|
+
{
|
|
172
|
+
value: 'start',
|
|
173
|
+
displayName: 'Align left',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
value: 'center',
|
|
177
|
+
displayName: 'Align center',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
value: 'end',
|
|
181
|
+
displayName: 'Align right',
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
type: 'Text',
|
|
186
|
+
group: 'style',
|
|
187
|
+
description: 'The vertical alignment of the section',
|
|
188
|
+
defaultValue: 'center',
|
|
189
|
+
displayName: 'Vertical alignment',
|
|
190
|
+
},
|
|
191
|
+
cfHorizontalAlignment: {
|
|
192
|
+
validations: {
|
|
193
|
+
in: [
|
|
194
|
+
{
|
|
195
|
+
value: 'start',
|
|
196
|
+
displayName: 'Align top',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
value: 'center',
|
|
200
|
+
displayName: 'Align center',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
value: 'end',
|
|
204
|
+
displayName: 'Align bottom',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
type: 'Text',
|
|
209
|
+
group: 'style',
|
|
210
|
+
description: 'The horizontal alignment of the section',
|
|
211
|
+
defaultValue: 'center',
|
|
212
|
+
displayName: 'Horizontal alignment',
|
|
213
|
+
},
|
|
214
|
+
cfVisibility: {
|
|
215
|
+
displayName: 'Visibility toggle',
|
|
216
|
+
type: 'Boolean',
|
|
217
|
+
group: 'style',
|
|
218
|
+
defaultValue: true,
|
|
219
|
+
description: 'The visibility of the component',
|
|
220
|
+
},
|
|
221
|
+
cfMargin: {
|
|
222
|
+
displayName: 'Margin',
|
|
223
|
+
type: 'Text',
|
|
224
|
+
group: 'style',
|
|
225
|
+
description: 'The margin of the section',
|
|
226
|
+
defaultValue: '0 0 0 0',
|
|
227
|
+
},
|
|
228
|
+
cfPadding: {
|
|
229
|
+
displayName: 'Padding',
|
|
230
|
+
type: 'Text',
|
|
231
|
+
group: 'style',
|
|
232
|
+
description: 'The padding of the section',
|
|
233
|
+
defaultValue: '0 0 0 0',
|
|
234
|
+
},
|
|
235
|
+
cfBackgroundColor: {
|
|
236
|
+
displayName: 'Background color',
|
|
237
|
+
type: 'Text',
|
|
238
|
+
group: 'style',
|
|
239
|
+
description: 'The background color of the section',
|
|
240
|
+
defaultValue: 'rgba(0, 0, 0, 0)',
|
|
241
|
+
},
|
|
242
|
+
cfWidth: {
|
|
243
|
+
displayName: 'Width',
|
|
244
|
+
type: 'Text',
|
|
245
|
+
group: 'style',
|
|
246
|
+
description: 'The width of the section',
|
|
247
|
+
defaultValue: '100%',
|
|
248
|
+
},
|
|
249
|
+
cfHeight: {
|
|
250
|
+
displayName: 'Height',
|
|
251
|
+
type: 'Text',
|
|
252
|
+
group: 'style',
|
|
253
|
+
description: 'The height of the section',
|
|
254
|
+
defaultValue: 'fit-content',
|
|
255
|
+
},
|
|
256
|
+
cfMaxWidth: {
|
|
257
|
+
displayName: 'Max width',
|
|
258
|
+
type: 'Text',
|
|
259
|
+
group: 'style',
|
|
260
|
+
description: 'The max-width of the section',
|
|
261
|
+
defaultValue: 'none',
|
|
262
|
+
},
|
|
263
|
+
cfFlexDirection: {
|
|
264
|
+
displayName: 'Direction',
|
|
265
|
+
type: 'Text',
|
|
266
|
+
group: 'style',
|
|
267
|
+
description: 'The orientation of the section',
|
|
268
|
+
defaultValue: 'column',
|
|
269
|
+
},
|
|
270
|
+
cfFlexReverse: {
|
|
271
|
+
displayName: 'Reverse Direction',
|
|
272
|
+
type: 'Boolean',
|
|
273
|
+
group: 'style',
|
|
274
|
+
description: 'Toggle the flex direction to be reversed',
|
|
275
|
+
defaultValue: false,
|
|
276
|
+
},
|
|
277
|
+
cfFlexWrap: {
|
|
278
|
+
displayName: 'Wrap objects',
|
|
279
|
+
type: 'Text',
|
|
280
|
+
group: 'style',
|
|
281
|
+
description: 'Wrap objects',
|
|
282
|
+
defaultValue: 'nowrap',
|
|
283
|
+
},
|
|
284
|
+
cfBorder: {
|
|
285
|
+
displayName: 'Border',
|
|
286
|
+
type: 'Text',
|
|
287
|
+
group: 'style',
|
|
288
|
+
description: 'The border of the section',
|
|
289
|
+
defaultValue: '0px solid rgba(0, 0, 0, 0)',
|
|
290
|
+
},
|
|
291
|
+
cfGap: {
|
|
292
|
+
displayName: 'Gap',
|
|
293
|
+
type: 'Text',
|
|
294
|
+
group: 'style',
|
|
295
|
+
description: 'The spacing between the elements of the section',
|
|
296
|
+
defaultValue: '0px',
|
|
297
|
+
},
|
|
298
|
+
cfHyperlink: {
|
|
299
|
+
displayName: 'URL',
|
|
300
|
+
type: 'Hyperlink',
|
|
301
|
+
defaultValue: '',
|
|
302
|
+
validations: {
|
|
303
|
+
format: 'URL',
|
|
304
|
+
bindingSourceType: ['entry', 'experience', 'manual'],
|
|
305
|
+
},
|
|
306
|
+
description: 'hyperlink for section or container',
|
|
307
|
+
},
|
|
308
|
+
cfOpenInNewTab: {
|
|
309
|
+
displayName: 'URL behaviour',
|
|
310
|
+
type: 'Boolean',
|
|
311
|
+
defaultValue: false,
|
|
312
|
+
description: 'Open in new tab',
|
|
313
|
+
},
|
|
266
314
|
};
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
display: 'none !important',
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
// Don't explicitly set anything when visible to not overwrite values like `grid` or `flex`.
|
|
275
|
-
return {};
|
|
276
|
-
};
|
|
277
|
-
// Keep this for backwards compatibility - deleting this would be a breaking change
|
|
278
|
-
// because existing components on a users experience will have the width value as fill
|
|
279
|
-
// rather than 100%
|
|
280
|
-
const transformFill = (value) => (value === 'fill' ? '100%' : value);
|
|
281
|
-
const transformGridColumn = (span) => {
|
|
282
|
-
if (!span) {
|
|
283
|
-
return {};
|
|
284
|
-
}
|
|
285
|
-
return {
|
|
286
|
-
gridColumn: `span ${span}`,
|
|
287
|
-
};
|
|
288
|
-
};
|
|
289
|
-
const transformBorderStyle = (value) => {
|
|
290
|
-
if (!value)
|
|
291
|
-
return {};
|
|
292
|
-
const parts = value.split(' ');
|
|
293
|
-
// Just accept the passed value
|
|
294
|
-
if (parts.length < 3)
|
|
295
|
-
return { border: value };
|
|
296
|
-
const [borderSize, borderStyle, ...borderColorParts] = parts;
|
|
297
|
-
const borderColor = borderColorParts.join(' ');
|
|
298
|
-
return {
|
|
299
|
-
border: `${borderSize} ${borderStyle} ${borderColor}`,
|
|
300
|
-
};
|
|
301
|
-
};
|
|
302
|
-
const transformAlignment = (cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection = 'column') => cfFlexDirection === 'row'
|
|
303
|
-
? {
|
|
304
|
-
alignItems: cfHorizontalAlignment,
|
|
305
|
-
justifyContent: cfVerticalAlignment === 'center' ? `safe ${cfVerticalAlignment}` : cfVerticalAlignment,
|
|
306
|
-
}
|
|
307
|
-
: {
|
|
308
|
-
alignItems: cfVerticalAlignment,
|
|
309
|
-
justifyContent: cfHorizontalAlignment === 'center'
|
|
310
|
-
? `safe ${cfHorizontalAlignment}`
|
|
311
|
-
: cfHorizontalAlignment,
|
|
312
|
-
};
|
|
313
|
-
const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageOptions) => {
|
|
314
|
-
const matchBackgroundSize = (scaling) => {
|
|
315
|
-
if ('fill' === scaling)
|
|
316
|
-
return 'cover';
|
|
317
|
-
if ('fit' === scaling)
|
|
318
|
-
return 'contain';
|
|
319
|
-
};
|
|
320
|
-
const matchBackgroundPosition = (alignment) => {
|
|
321
|
-
if (!alignment || 'string' !== typeof alignment) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
let [horizontalAlignment, verticalAlignment] = alignment.trim().split(/\s+/, 2);
|
|
325
|
-
// Special case for handling single values
|
|
326
|
-
// for backwards compatibility with single values 'right','left', 'center', 'top','bottom'
|
|
327
|
-
if (horizontalAlignment && !verticalAlignment) {
|
|
328
|
-
const singleValue = horizontalAlignment;
|
|
329
|
-
switch (singleValue) {
|
|
330
|
-
case 'left':
|
|
331
|
-
horizontalAlignment = 'left';
|
|
332
|
-
verticalAlignment = 'center';
|
|
333
|
-
break;
|
|
334
|
-
case 'right':
|
|
335
|
-
horizontalAlignment = 'right';
|
|
336
|
-
verticalAlignment = 'center';
|
|
337
|
-
break;
|
|
338
|
-
case 'center':
|
|
339
|
-
horizontalAlignment = 'center';
|
|
340
|
-
verticalAlignment = 'center';
|
|
341
|
-
break;
|
|
342
|
-
case 'top':
|
|
343
|
-
horizontalAlignment = 'center';
|
|
344
|
-
verticalAlignment = 'top';
|
|
345
|
-
break;
|
|
346
|
-
case 'bottom':
|
|
347
|
-
horizontalAlignment = 'center';
|
|
348
|
-
verticalAlignment = 'bottom';
|
|
349
|
-
break;
|
|
350
|
-
// just fall down to the normal validation logic for horiz and vert
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const isHorizontalValid = ['left', 'right', 'center'].includes(horizontalAlignment);
|
|
354
|
-
const isVerticalValid = ['top', 'bottom', 'center'].includes(verticalAlignment);
|
|
355
|
-
horizontalAlignment = isHorizontalValid ? horizontalAlignment : 'left';
|
|
356
|
-
verticalAlignment = isVerticalValid ? verticalAlignment : 'top';
|
|
357
|
-
return `${horizontalAlignment} ${verticalAlignment}`;
|
|
358
|
-
};
|
|
359
|
-
if (!cfBackgroundImageUrl) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
let backgroundImage;
|
|
363
|
-
let backgroundImageSet;
|
|
364
|
-
if (typeof cfBackgroundImageUrl === 'string') {
|
|
365
|
-
backgroundImage = `url(${cfBackgroundImageUrl})`;
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
const imgSet = cfBackgroundImageUrl.srcSet?.join(',');
|
|
369
|
-
backgroundImage = `url(${cfBackgroundImageUrl.url})`;
|
|
370
|
-
backgroundImageSet = `image-set(${imgSet})`;
|
|
371
|
-
}
|
|
372
|
-
return {
|
|
373
|
-
backgroundImage,
|
|
374
|
-
backgroundImage2: backgroundImageSet,
|
|
375
|
-
backgroundRepeat: cfBackgroundImageOptions?.scaling === 'tile' ? 'repeat' : 'no-repeat',
|
|
376
|
-
backgroundPosition: matchBackgroundPosition(cfBackgroundImageOptions?.alignment),
|
|
377
|
-
backgroundSize: matchBackgroundSize(cfBackgroundImageOptions?.scaling),
|
|
378
|
-
};
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
const toCSSAttribute = (key) => {
|
|
382
|
-
let val = key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
|
|
383
|
-
// Remove the number from the end of the key to allow for overrides on style properties
|
|
384
|
-
val = val.replace(/\d+$/, '');
|
|
385
|
-
return val;
|
|
386
|
-
};
|
|
387
|
-
/**
|
|
388
|
-
* Turns a list of CSSProperties into a joined CSS string that can be
|
|
389
|
-
* used for <style> tags. Per default it creates a minimized version.
|
|
390
|
-
* For editor mode, use the `useWhitespaces` flag to create a more readable version.
|
|
391
|
-
*
|
|
392
|
-
* @param cssProperties list of CSS properties
|
|
393
|
-
* @param useWhitespaces adds whitespaces and newlines between each rule
|
|
394
|
-
* @returns a string of CSS rules
|
|
395
|
-
*/
|
|
396
|
-
const stringifyCssProperties = (cssProperties, useWhitespaces = false) => {
|
|
397
|
-
const rules = Object.entries(cssProperties)
|
|
398
|
-
.filter(([, value]) => value !== undefined)
|
|
399
|
-
.map(([key, value]) => useWhitespaces ? `${toCSSAttribute(key)}: ${value};` : `${toCSSAttribute(key)}:${value};`);
|
|
400
|
-
return rules.join(useWhitespaces ? '\n' : '');
|
|
401
|
-
};
|
|
402
|
-
const buildStyleTag = ({ styles, nodeId }) => {
|
|
403
|
-
const generatedStyles = stringifyCssProperties(styles, true);
|
|
404
|
-
const className = `cfstyles-${nodeId ? nodeId : md5(generatedStyles)}`;
|
|
405
|
-
const styleRule = `.${className}{ ${generatedStyles} }`;
|
|
406
|
-
return [className, styleRule];
|
|
407
|
-
};
|
|
408
|
-
/**
|
|
409
|
-
* Takes plain design values and transforms them into CSS properties. Undefined values will
|
|
410
|
-
* be filtered out.
|
|
411
|
-
*
|
|
412
|
-
* **Example Input**
|
|
413
|
-
* ```
|
|
414
|
-
* values = {
|
|
415
|
-
* cfVisibility: 'visible',
|
|
416
|
-
* cfMargin: '10px',
|
|
417
|
-
* cfFlexReverse: true,
|
|
418
|
-
* cfImageOptions: { objectFit: 'cover' },
|
|
419
|
-
* // ...
|
|
420
|
-
* }
|
|
421
|
-
* ```
|
|
422
|
-
* **Example Output**
|
|
423
|
-
* ```
|
|
424
|
-
* cssProperties = {
|
|
425
|
-
* margin: '10px',
|
|
426
|
-
* flexDirection: 'row-reverse',
|
|
427
|
-
* objectFit: 'cover',
|
|
428
|
-
* // ...
|
|
429
|
-
* }
|
|
430
|
-
* ```
|
|
431
|
-
*/
|
|
432
|
-
const buildCfStyles = (values) => {
|
|
433
|
-
const cssProperties = {
|
|
434
|
-
boxSizing: 'border-box',
|
|
435
|
-
...transformVisibility(values.cfVisibility),
|
|
436
|
-
margin: values.cfMargin,
|
|
437
|
-
padding: values.cfPadding,
|
|
438
|
-
backgroundColor: values.cfBackgroundColor,
|
|
439
|
-
width: transformFill(values.cfWidth || values.cfImageOptions?.width),
|
|
440
|
-
height: transformFill(values.cfHeight || values.cfImageOptions?.height),
|
|
441
|
-
maxWidth: values.cfMaxWidth,
|
|
442
|
-
...transformGridColumn(values.cfColumnSpan),
|
|
443
|
-
...transformBorderStyle(values.cfBorder),
|
|
444
|
-
borderRadius: values.cfBorderRadius,
|
|
445
|
-
gap: values.cfGap,
|
|
446
|
-
...transformAlignment(values.cfHorizontalAlignment, values.cfVerticalAlignment, values.cfFlexDirection),
|
|
447
|
-
flexDirection: values.cfFlexReverse && values.cfFlexDirection
|
|
448
|
-
? `${values.cfFlexDirection}-reverse`
|
|
449
|
-
: values.cfFlexDirection,
|
|
450
|
-
flexWrap: values.cfFlexWrap,
|
|
451
|
-
...transformBackgroundImage(values.cfBackgroundImageUrl, values.cfBackgroundImageOptions),
|
|
452
|
-
fontSize: values.cfFontSize,
|
|
453
|
-
fontWeight: values.cfTextBold ? 'bold' : values.cfFontWeight,
|
|
454
|
-
fontStyle: values.cfTextItalic ? 'italic' : undefined,
|
|
455
|
-
textDecoration: values.cfTextUnderline ? 'underline' : undefined,
|
|
456
|
-
lineHeight: values.cfLineHeight,
|
|
457
|
-
letterSpacing: values.cfLetterSpacing,
|
|
458
|
-
color: values.cfTextColor,
|
|
459
|
-
textAlign: values.cfTextAlign,
|
|
460
|
-
textTransform: values.cfTextTransform,
|
|
461
|
-
objectFit: values.cfImageOptions?.objectFit,
|
|
462
|
-
objectPosition: values.cfImageOptions?.objectPosition,
|
|
463
|
-
};
|
|
464
|
-
const cssPropertiesWithoutUndefined = Object.fromEntries(Object.entries(cssProperties).filter(([, value]) => value !== undefined));
|
|
465
|
-
return cssPropertiesWithoutUndefined;
|
|
466
|
-
};
|
|
467
|
-
/**
|
|
468
|
-
* Container/section default behavior:
|
|
469
|
-
* Default height => height: EMPTY_CONTAINER_HEIGHT
|
|
470
|
-
* If a container component has children => height: 'fit-content'
|
|
471
|
-
*/
|
|
472
|
-
const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
|
|
473
|
-
if (!blockId || !isContentfulStructureComponent(blockId) || value !== 'auto') {
|
|
474
|
-
return value;
|
|
475
|
-
}
|
|
476
|
-
if (children.length) {
|
|
477
|
-
return '100%';
|
|
478
|
-
}
|
|
479
|
-
return EMPTY_CONTAINER_HEIGHT$1;
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
// These styles get added to every component, user custom or contentful provided
|
|
483
|
-
const builtInStyles = {
|
|
484
|
-
cfVerticalAlignment: {
|
|
485
|
-
validations: {
|
|
486
|
-
in: [
|
|
487
|
-
{
|
|
488
|
-
value: 'start',
|
|
489
|
-
displayName: 'Align left',
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
value: 'center',
|
|
493
|
-
displayName: 'Align center',
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
value: 'end',
|
|
497
|
-
displayName: 'Align right',
|
|
498
|
-
},
|
|
499
|
-
],
|
|
500
|
-
},
|
|
315
|
+
const optionalBuiltInStyles = {
|
|
316
|
+
cfFontSize: {
|
|
317
|
+
displayName: 'Font Size',
|
|
501
318
|
type: 'Text',
|
|
502
319
|
group: 'style',
|
|
503
|
-
description: 'The
|
|
504
|
-
defaultValue: '
|
|
505
|
-
displayName: 'Vertical alignment',
|
|
320
|
+
description: 'The font size of the element',
|
|
321
|
+
defaultValue: '16px',
|
|
506
322
|
},
|
|
507
|
-
|
|
323
|
+
cfFontWeight: {
|
|
508
324
|
validations: {
|
|
509
325
|
in: [
|
|
510
326
|
{
|
|
511
|
-
value: '
|
|
512
|
-
displayName: '
|
|
327
|
+
value: '400',
|
|
328
|
+
displayName: 'Normal',
|
|
513
329
|
},
|
|
514
330
|
{
|
|
515
|
-
value: '
|
|
516
|
-
displayName: '
|
|
331
|
+
value: '500',
|
|
332
|
+
displayName: 'Medium',
|
|
517
333
|
},
|
|
518
334
|
{
|
|
519
|
-
value: '
|
|
520
|
-
displayName: '
|
|
335
|
+
value: '600',
|
|
336
|
+
displayName: 'Semi Bold',
|
|
521
337
|
},
|
|
522
338
|
],
|
|
523
339
|
},
|
|
524
|
-
|
|
525
|
-
group: 'style',
|
|
526
|
-
description: 'The horizontal alignment of the section',
|
|
527
|
-
defaultValue: 'center',
|
|
528
|
-
displayName: 'Horizontal alignment',
|
|
529
|
-
},
|
|
530
|
-
cfVisibility: {
|
|
531
|
-
displayName: 'Visibility toggle',
|
|
532
|
-
type: 'Boolean',
|
|
533
|
-
group: 'style',
|
|
534
|
-
defaultValue: true,
|
|
535
|
-
description: 'The visibility of the component',
|
|
536
|
-
},
|
|
537
|
-
cfMargin: {
|
|
538
|
-
displayName: 'Margin',
|
|
539
|
-
type: 'Text',
|
|
540
|
-
group: 'style',
|
|
541
|
-
description: 'The margin of the section',
|
|
542
|
-
defaultValue: '0 0 0 0',
|
|
543
|
-
},
|
|
544
|
-
cfPadding: {
|
|
545
|
-
displayName: 'Padding',
|
|
546
|
-
type: 'Text',
|
|
547
|
-
group: 'style',
|
|
548
|
-
description: 'The padding of the section',
|
|
549
|
-
defaultValue: '0 0 0 0',
|
|
550
|
-
},
|
|
551
|
-
cfBackgroundColor: {
|
|
552
|
-
displayName: 'Background color',
|
|
553
|
-
type: 'Text',
|
|
554
|
-
group: 'style',
|
|
555
|
-
description: 'The background color of the section',
|
|
556
|
-
defaultValue: 'rgba(0, 0, 0, 0)',
|
|
557
|
-
},
|
|
558
|
-
cfWidth: {
|
|
559
|
-
displayName: 'Width',
|
|
560
|
-
type: 'Text',
|
|
561
|
-
group: 'style',
|
|
562
|
-
description: 'The width of the section',
|
|
563
|
-
defaultValue: '100%',
|
|
564
|
-
},
|
|
565
|
-
cfHeight: {
|
|
566
|
-
displayName: 'Height',
|
|
567
|
-
type: 'Text',
|
|
568
|
-
group: 'style',
|
|
569
|
-
description: 'The height of the section',
|
|
570
|
-
defaultValue: 'fit-content',
|
|
571
|
-
},
|
|
572
|
-
cfMaxWidth: {
|
|
573
|
-
displayName: 'Max width',
|
|
574
|
-
type: 'Text',
|
|
575
|
-
group: 'style',
|
|
576
|
-
description: 'The max-width of the section',
|
|
577
|
-
defaultValue: 'none',
|
|
578
|
-
},
|
|
579
|
-
cfFlexDirection: {
|
|
580
|
-
displayName: 'Direction',
|
|
581
|
-
type: 'Text',
|
|
582
|
-
group: 'style',
|
|
583
|
-
description: 'The orientation of the section',
|
|
584
|
-
defaultValue: 'column',
|
|
585
|
-
},
|
|
586
|
-
cfFlexReverse: {
|
|
587
|
-
displayName: 'Reverse Direction',
|
|
588
|
-
type: 'Boolean',
|
|
589
|
-
group: 'style',
|
|
590
|
-
description: 'Toggle the flex direction to be reversed',
|
|
591
|
-
defaultValue: false,
|
|
592
|
-
},
|
|
593
|
-
cfFlexWrap: {
|
|
594
|
-
displayName: 'Wrap objects',
|
|
595
|
-
type: 'Text',
|
|
596
|
-
group: 'style',
|
|
597
|
-
description: 'Wrap objects',
|
|
598
|
-
defaultValue: 'nowrap',
|
|
599
|
-
},
|
|
600
|
-
cfBorder: {
|
|
601
|
-
displayName: 'Border',
|
|
602
|
-
type: 'Text',
|
|
603
|
-
group: 'style',
|
|
604
|
-
description: 'The border of the section',
|
|
605
|
-
defaultValue: '0px solid rgba(0, 0, 0, 0)',
|
|
606
|
-
},
|
|
607
|
-
cfGap: {
|
|
608
|
-
displayName: 'Gap',
|
|
609
|
-
type: 'Text',
|
|
610
|
-
group: 'style',
|
|
611
|
-
description: 'The spacing between the elements of the section',
|
|
612
|
-
defaultValue: '0px',
|
|
613
|
-
},
|
|
614
|
-
cfHyperlink: {
|
|
615
|
-
displayName: 'URL',
|
|
616
|
-
type: 'Hyperlink',
|
|
617
|
-
defaultValue: '',
|
|
618
|
-
validations: {
|
|
619
|
-
format: 'URL',
|
|
620
|
-
bindingSourceType: ['entry', 'experience', 'manual'],
|
|
621
|
-
},
|
|
622
|
-
description: 'hyperlink for section or container',
|
|
623
|
-
},
|
|
624
|
-
cfOpenInNewTab: {
|
|
625
|
-
displayName: 'URL behaviour',
|
|
626
|
-
type: 'Boolean',
|
|
627
|
-
defaultValue: false,
|
|
628
|
-
description: 'Open in new tab',
|
|
629
|
-
},
|
|
630
|
-
};
|
|
631
|
-
const optionalBuiltInStyles = {
|
|
632
|
-
cfFontSize: {
|
|
633
|
-
displayName: 'Font Size',
|
|
634
|
-
type: 'Text',
|
|
635
|
-
group: 'style',
|
|
636
|
-
description: 'The font size of the element',
|
|
637
|
-
defaultValue: '16px',
|
|
638
|
-
},
|
|
639
|
-
cfFontWeight: {
|
|
640
|
-
validations: {
|
|
641
|
-
in: [
|
|
642
|
-
{
|
|
643
|
-
value: '400',
|
|
644
|
-
displayName: 'Normal',
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
value: '500',
|
|
648
|
-
displayName: 'Medium',
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
value: '600',
|
|
652
|
-
displayName: 'Semi Bold',
|
|
653
|
-
},
|
|
654
|
-
],
|
|
655
|
-
},
|
|
656
|
-
displayName: 'Font Weight',
|
|
340
|
+
displayName: 'Font Weight',
|
|
657
341
|
type: 'Text',
|
|
658
342
|
group: 'style',
|
|
659
343
|
description: 'The font weight of the element',
|
|
@@ -1271,725 +955,1041 @@ var CodeNames$1;
|
|
|
1271
955
|
CodeNames["Custom"] = "custom";
|
|
1272
956
|
})(CodeNames$1 || (CodeNames$1 = {}));
|
|
1273
957
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (
|
|
1277
|
-
return obj;
|
|
1278
|
-
}
|
|
1279
|
-
try {
|
|
1280
|
-
const [currentPath, ...nextPath] = path;
|
|
1281
|
-
return get(obj[currentPath], nextPath);
|
|
1282
|
-
}
|
|
1283
|
-
catch (err) {
|
|
958
|
+
const MEDIA_QUERY_REGEXP = /(<|>)(\d{1,})(px|cm|mm|in|pt|pc)$/;
|
|
959
|
+
const toCSSMediaQuery = ({ query }) => {
|
|
960
|
+
if (query === '*')
|
|
1284
961
|
return undefined;
|
|
962
|
+
const match = query.match(MEDIA_QUERY_REGEXP);
|
|
963
|
+
if (!match)
|
|
964
|
+
return undefined;
|
|
965
|
+
const [, operator, value, unit] = match;
|
|
966
|
+
if (operator === '<') {
|
|
967
|
+
const maxScreenWidth = Number(value) - 1;
|
|
968
|
+
return `(max-width: ${maxScreenWidth}${unit})`;
|
|
1285
969
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
const value = get(entryOrAsset, path.split('/').slice(2, -1));
|
|
1290
|
-
return value && typeof value == 'object' && value.url
|
|
1291
|
-
? value.url
|
|
1292
|
-
: value;
|
|
1293
|
-
};
|
|
1294
|
-
|
|
1295
|
-
const transformRichText = (entryOrAsset, entityStore, path) => {
|
|
1296
|
-
const value = getBoundValue(entryOrAsset, path);
|
|
1297
|
-
if (typeof value === 'string') {
|
|
1298
|
-
return {
|
|
1299
|
-
data: {},
|
|
1300
|
-
content: [
|
|
1301
|
-
{
|
|
1302
|
-
nodeType: BLOCKS.PARAGRAPH,
|
|
1303
|
-
data: {},
|
|
1304
|
-
content: [
|
|
1305
|
-
{
|
|
1306
|
-
data: {},
|
|
1307
|
-
nodeType: 'text',
|
|
1308
|
-
value: value,
|
|
1309
|
-
marks: [],
|
|
1310
|
-
},
|
|
1311
|
-
],
|
|
1312
|
-
},
|
|
1313
|
-
],
|
|
1314
|
-
nodeType: BLOCKS.DOCUMENT,
|
|
1315
|
-
};
|
|
1316
|
-
}
|
|
1317
|
-
if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
|
|
1318
|
-
// resolve any links to assets/entries/hyperlinks
|
|
1319
|
-
const richTextDocument = value;
|
|
1320
|
-
resolveLinks(richTextDocument, entityStore);
|
|
1321
|
-
return richTextDocument;
|
|
970
|
+
else if (operator === '>') {
|
|
971
|
+
const minScreenWidth = Number(value) + 1;
|
|
972
|
+
return `(min-width: ${minScreenWidth}${unit})`;
|
|
1322
973
|
}
|
|
1323
974
|
return undefined;
|
|
1324
975
|
};
|
|
1325
|
-
|
|
1326
|
-
|
|
976
|
+
// Remove this helper when upgrading to TypeScript 5.0 - https://github.com/microsoft/TypeScript/issues/48829
|
|
977
|
+
const findLast = (array, predicate) => {
|
|
978
|
+
return array.reverse().find(predicate);
|
|
1327
979
|
};
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
const
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
}
|
|
980
|
+
// Initialise media query matchers. This won't include the always matching fallback breakpoint.
|
|
981
|
+
const mediaQueryMatcher = (breakpoints) => {
|
|
982
|
+
const mediaQueryMatches = {};
|
|
983
|
+
const mediaQueryMatchers = breakpoints
|
|
984
|
+
.map((breakpoint) => {
|
|
985
|
+
const cssMediaQuery = toCSSMediaQuery(breakpoint);
|
|
986
|
+
if (!cssMediaQuery)
|
|
987
|
+
return undefined;
|
|
988
|
+
if (typeof window === 'undefined')
|
|
989
|
+
return undefined;
|
|
990
|
+
const mediaQueryMatcher = window.matchMedia(cssMediaQuery);
|
|
991
|
+
mediaQueryMatches[breakpoint.id] = mediaQueryMatcher.matches;
|
|
992
|
+
return { id: breakpoint.id, signal: mediaQueryMatcher };
|
|
993
|
+
})
|
|
994
|
+
.filter((matcher) => !!matcher);
|
|
995
|
+
return [mediaQueryMatchers, mediaQueryMatches];
|
|
1342
996
|
};
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const optimizedBackgroundImageAsset = {
|
|
1387
|
-
url: returnedUrl,
|
|
1388
|
-
srcSet,
|
|
1389
|
-
file,
|
|
997
|
+
const getActiveBreakpointIndex = (breakpoints, mediaQueryMatches, fallbackBreakpointIndex) => {
|
|
998
|
+
// The breakpoints are ordered (desktop-first: descending by screen width)
|
|
999
|
+
const breakpointsWithMatches = breakpoints.map(({ id }, index) => ({
|
|
1000
|
+
id,
|
|
1001
|
+
index,
|
|
1002
|
+
// The fallback breakpoint with wildcard query will always match
|
|
1003
|
+
isMatch: mediaQueryMatches[id] ?? index === fallbackBreakpointIndex,
|
|
1004
|
+
}));
|
|
1005
|
+
// Find the last breakpoint in the list that matches (desktop-first: the narrowest one)
|
|
1006
|
+
const mostSpecificIndex = findLast(breakpointsWithMatches, ({ isMatch }) => isMatch)?.index;
|
|
1007
|
+
return mostSpecificIndex ?? fallbackBreakpointIndex;
|
|
1008
|
+
};
|
|
1009
|
+
const getFallbackBreakpointIndex = (breakpoints) => {
|
|
1010
|
+
// We assume that there will be a single breakpoint which uses the wildcard query.
|
|
1011
|
+
// If there is none, we just take the first one in the list.
|
|
1012
|
+
return Math.max(breakpoints.findIndex(({ query }) => query === '*'), 0);
|
|
1013
|
+
};
|
|
1014
|
+
const builtInStylesWithDesignTokens = [
|
|
1015
|
+
'cfMargin',
|
|
1016
|
+
'cfPadding',
|
|
1017
|
+
'cfGap',
|
|
1018
|
+
'cfWidth',
|
|
1019
|
+
'cfHeight',
|
|
1020
|
+
'cfBackgroundColor',
|
|
1021
|
+
'cfBorder',
|
|
1022
|
+
'cfBorderRadius',
|
|
1023
|
+
'cfFontSize',
|
|
1024
|
+
'cfLineHeight',
|
|
1025
|
+
'cfLetterSpacing',
|
|
1026
|
+
'cfTextColor',
|
|
1027
|
+
'cfMaxWidth',
|
|
1028
|
+
];
|
|
1029
|
+
const isValidBreakpointValue = (value) => {
|
|
1030
|
+
return value !== undefined && value !== null && value !== '';
|
|
1031
|
+
};
|
|
1032
|
+
const getValueForBreakpoint = (valuesByBreakpoint, breakpoints, activeBreakpointIndex, fallbackBreakpointIndex, variableName, resolveDesignTokens = true) => {
|
|
1033
|
+
const eventuallyResolveDesignTokens = (value) => {
|
|
1034
|
+
// For some built-in design properties, we support design tokens
|
|
1035
|
+
if (builtInStylesWithDesignTokens.includes(variableName)) {
|
|
1036
|
+
return getDesignTokenRegistration(value, variableName);
|
|
1037
|
+
}
|
|
1038
|
+
// For all other properties, we just return the breakpoint-specific value
|
|
1039
|
+
return value;
|
|
1390
1040
|
};
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
let
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1041
|
+
if (valuesByBreakpoint instanceof Object) {
|
|
1042
|
+
// Assume that the values are sorted by media query to apply the cascading CSS logic
|
|
1043
|
+
for (let index = activeBreakpointIndex; index >= 0; index--) {
|
|
1044
|
+
const breakpointId = breakpoints[index]?.id;
|
|
1045
|
+
if (isValidBreakpointValue(valuesByBreakpoint[breakpointId])) {
|
|
1046
|
+
// If the value is defined, we use it and stop the breakpoints cascade
|
|
1047
|
+
if (resolveDesignTokens) {
|
|
1048
|
+
return eventuallyResolveDesignTokens(valuesByBreakpoint[breakpointId]);
|
|
1049
|
+
}
|
|
1050
|
+
return valuesByBreakpoint[breakpointId];
|
|
1051
|
+
}
|
|
1398
1052
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1053
|
+
const fallbackBreakpointId = breakpoints[fallbackBreakpointIndex]?.id;
|
|
1054
|
+
if (isValidBreakpointValue(valuesByBreakpoint[fallbackBreakpointId])) {
|
|
1055
|
+
if (resolveDesignTokens) {
|
|
1056
|
+
return eventuallyResolveDesignTokens(valuesByBreakpoint[fallbackBreakpointId]);
|
|
1057
|
+
}
|
|
1058
|
+
return valuesByBreakpoint[fallbackBreakpointId];
|
|
1401
1059
|
}
|
|
1402
|
-
width2x = Math.min(width1x * 2, intrinsicImageWidth);
|
|
1403
|
-
return { width1x, width2x };
|
|
1404
1060
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
const getOptimizedImageAsset = ({ file, sizes, loading, quality = '100%', format, }) => {
|
|
1409
|
-
const qualityNumber = Number(quality.replace('%', ''));
|
|
1410
|
-
if (!validateParams(file, qualityNumber, format)) ;
|
|
1411
|
-
const url = file.url;
|
|
1412
|
-
const maxWidth = Math.min(file.details.image.width, MAX_WIDTH_ALLOWED);
|
|
1413
|
-
const numOfParts = Math.max(2, Math.ceil(maxWidth / 500));
|
|
1414
|
-
const widthParts = Array.from({ length: numOfParts }, (_, index) => Math.ceil((index + 1) * (maxWidth / numOfParts)));
|
|
1415
|
-
const srcSet = sizes
|
|
1416
|
-
? widthParts.map((width) => `${getOptimizedImageUrl(url, width, qualityNumber, format)} ${width}w`)
|
|
1417
|
-
: [];
|
|
1418
|
-
const intrinsicImageWidth = file.details.image.width;
|
|
1419
|
-
if (intrinsicImageWidth > MAX_WIDTH_ALLOWED) {
|
|
1420
|
-
srcSet.push(`${getOptimizedImageUrl(url, undefined, qualityNumber, format)} ${intrinsicImageWidth}w`);
|
|
1061
|
+
else {
|
|
1062
|
+
// Old design properties did not support breakpoints, keep for backward compatibility
|
|
1063
|
+
return valuesByBreakpoint;
|
|
1421
1064
|
}
|
|
1422
|
-
const returnedUrl = getOptimizedImageUrl(url, file.details.image.width > 2000 ? 2000 : undefined, qualityNumber, format);
|
|
1423
|
-
const optimizedImageAsset = {
|
|
1424
|
-
url: returnedUrl,
|
|
1425
|
-
srcSet,
|
|
1426
|
-
sizes,
|
|
1427
|
-
file,
|
|
1428
|
-
loading,
|
|
1429
|
-
};
|
|
1430
|
-
return optimizedImageAsset;
|
|
1431
1065
|
};
|
|
1432
1066
|
|
|
1433
|
-
const
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
|
|
1444
|
-
? variables[optionsVariableName].valuesByBreakpoint
|
|
1445
|
-
: {}, optionsVariableName);
|
|
1446
|
-
if (!options) {
|
|
1447
|
-
console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
|
|
1067
|
+
const CF_DEBUG_KEY$1 = 'cf_debug';
|
|
1068
|
+
let DebugLogger$1 = class DebugLogger {
|
|
1069
|
+
constructor() {
|
|
1070
|
+
// Public methods for logging
|
|
1071
|
+
this.error = this.logger('error');
|
|
1072
|
+
this.warn = this.logger('warn');
|
|
1073
|
+
this.log = this.logger('log');
|
|
1074
|
+
this.debug = this.logger('debug');
|
|
1075
|
+
if (typeof localStorage === 'undefined') {
|
|
1076
|
+
this.enabled = false;
|
|
1448
1077
|
return;
|
|
1449
1078
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
format: options.format,
|
|
1457
|
-
});
|
|
1458
|
-
return value;
|
|
1459
|
-
}
|
|
1460
|
-
catch (error) {
|
|
1461
|
-
console.error('Error transforming image asset', error);
|
|
1079
|
+
// Default to checking localStorage for the debug mode on initialization if in browser
|
|
1080
|
+
this.enabled = localStorage.getItem(CF_DEBUG_KEY$1) === 'true';
|
|
1081
|
+
}
|
|
1082
|
+
static getInstance() {
|
|
1083
|
+
if (this.instance === null) {
|
|
1084
|
+
this.instance = new DebugLogger();
|
|
1462
1085
|
}
|
|
1463
|
-
return;
|
|
1086
|
+
return this.instance;
|
|
1464
1087
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
if (!options) {
|
|
1472
|
-
console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
|
|
1088
|
+
getEnabled() {
|
|
1089
|
+
return this.enabled;
|
|
1090
|
+
}
|
|
1091
|
+
setEnabled(enabled) {
|
|
1092
|
+
this.enabled = enabled;
|
|
1093
|
+
if (typeof localStorage === 'undefined') {
|
|
1473
1094
|
return;
|
|
1474
1095
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
const assetDetails = asset.fields.file?.details;
|
|
1478
|
-
const assetWidth = assetDetails?.image?.width || 0; // This is always in px
|
|
1479
|
-
const targetWidthObject = parseCSSValue(options.targetSize); // Contains value and unit (px/rem/em) so convert and then compare to assetWidth
|
|
1480
|
-
const targetValue = targetWidthObject
|
|
1481
|
-
? getTargetValueInPixels(targetWidthObject)
|
|
1482
|
-
: assetWidth;
|
|
1483
|
-
if (targetValue < assetWidth)
|
|
1484
|
-
width = `${targetValue}px`;
|
|
1485
|
-
value = getOptimizedBackgroundImageAsset(asset.fields.file, width, options.quality, options.format);
|
|
1486
|
-
return value;
|
|
1096
|
+
if (enabled) {
|
|
1097
|
+
localStorage.setItem(CF_DEBUG_KEY$1, 'true');
|
|
1487
1098
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1099
|
+
else {
|
|
1100
|
+
localStorage.removeItem(CF_DEBUG_KEY$1);
|
|
1490
1101
|
}
|
|
1491
|
-
return;
|
|
1492
1102
|
}
|
|
1493
|
-
|
|
1103
|
+
// Log method for different levels (error, warn, log)
|
|
1104
|
+
logger(level) {
|
|
1105
|
+
return (...args) => {
|
|
1106
|
+
if (this.enabled) {
|
|
1107
|
+
console[level]('[cf-experiences-sdk]', ...args);
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1494
1111
|
};
|
|
1112
|
+
DebugLogger$1.instance = null;
|
|
1113
|
+
DebugLogger$1.getInstance();
|
|
1495
1114
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1115
|
+
const findOutermostCoordinates = (first, second) => {
|
|
1116
|
+
return {
|
|
1117
|
+
top: Math.min(first.top, second.top),
|
|
1118
|
+
right: Math.max(first.right, second.right),
|
|
1119
|
+
bottom: Math.max(first.bottom, second.bottom),
|
|
1120
|
+
left: Math.min(first.left, second.left),
|
|
1121
|
+
};
|
|
1122
|
+
};
|
|
1123
|
+
const getElementCoordinates = (element) => {
|
|
1124
|
+
const rect = element.getBoundingClientRect();
|
|
1125
|
+
/**
|
|
1126
|
+
* If element does not have children, or element has it's own width or height,
|
|
1127
|
+
* return the element's coordinates.
|
|
1128
|
+
*/
|
|
1129
|
+
if (element.children.length === 0 || rect.width !== 0 || rect.height !== 0) {
|
|
1130
|
+
return rect;
|
|
1499
1131
|
}
|
|
1500
|
-
const
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1132
|
+
const rects = [];
|
|
1133
|
+
/**
|
|
1134
|
+
* If element has children, or element does not have it's own width and height,
|
|
1135
|
+
* we find the cordinates of the children, and assume the outermost coordinates of the children
|
|
1136
|
+
* as the coordinate of the element.
|
|
1137
|
+
*
|
|
1138
|
+
* E.g child1 => {top: 2, bottom: 3, left: 4, right: 6} & child2 => {top: 1, bottom: 8, left: 12, right: 24}
|
|
1139
|
+
* The final assumed coordinates of the element would be => { top: 1, right: 24, bottom: 8, left: 4 }
|
|
1140
|
+
*/
|
|
1141
|
+
for (const child of element.children) {
|
|
1142
|
+
const childRect = getElementCoordinates(child);
|
|
1143
|
+
if (childRect.width !== 0 || childRect.height !== 0) {
|
|
1144
|
+
const { top, right, bottom, left } = childRect;
|
|
1145
|
+
rects.push({ top, right, bottom, left });
|
|
1146
|
+
}
|
|
1504
1147
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
if (!resolvedEntity) {
|
|
1508
|
-
return;
|
|
1148
|
+
if (rects.length === 0) {
|
|
1149
|
+
return rect;
|
|
1509
1150
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
resolvedEntity.fields[fieldKey] = entity;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
else if (field && Array.isArray(field)) {
|
|
1520
|
-
resolvedEntity.fields[fieldKey] = field.map((innerField) => {
|
|
1521
|
-
if (innerField && innerField.sys?.type === 'Link') {
|
|
1522
|
-
const entity = entityStore.getEntityFromLink(innerField);
|
|
1523
|
-
if (entity) {
|
|
1524
|
-
return entity;
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
return innerField;
|
|
1528
|
-
});
|
|
1529
|
-
}
|
|
1151
|
+
const { top, right, bottom, left } = rects.reduce(findOutermostCoordinates);
|
|
1152
|
+
return DOMRect.fromRect({
|
|
1153
|
+
x: left,
|
|
1154
|
+
y: top,
|
|
1155
|
+
height: bottom - top,
|
|
1156
|
+
width: right - left,
|
|
1530
1157
|
});
|
|
1531
|
-
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1161
|
+
const isLinkToAsset = (variable) => {
|
|
1162
|
+
if (!variable)
|
|
1163
|
+
return false;
|
|
1164
|
+
if (typeof variable !== 'object')
|
|
1165
|
+
return false;
|
|
1166
|
+
return (variable.sys?.linkType === 'Asset' &&
|
|
1167
|
+
typeof variable.sys?.id === 'string' &&
|
|
1168
|
+
!!variable.sys?.id &&
|
|
1169
|
+
variable.sys?.type === 'Link');
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
const isLink = (maybeLink) => {
|
|
1173
|
+
if (maybeLink === null)
|
|
1174
|
+
return false;
|
|
1175
|
+
if (typeof maybeLink !== 'object')
|
|
1176
|
+
return false;
|
|
1177
|
+
const link = maybeLink;
|
|
1178
|
+
return Boolean(link.sys?.id) && link.sys?.type === 'Link';
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* This module encapsulates format of the path to a deep reference.
|
|
1183
|
+
*/
|
|
1184
|
+
const parseDataSourcePathIntoFieldset = (path) => {
|
|
1185
|
+
const parsedPath = parseDeepPath(path);
|
|
1186
|
+
if (null === parsedPath) {
|
|
1187
|
+
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
1188
|
+
}
|
|
1189
|
+
return parsedPath.fields.map((field) => [null, field, '~locale']);
|
|
1190
|
+
};
|
|
1191
|
+
/**
|
|
1192
|
+
* Parse path into components, supports L1 references (one reference follow) atm.
|
|
1193
|
+
* @param path from data source. eg. `/uuid123/fields/image/~locale/fields/file/~locale`
|
|
1194
|
+
* eg. `/uuid123/fields/file/~locale/fields/title/~locale`
|
|
1195
|
+
* @returns
|
|
1196
|
+
*/
|
|
1197
|
+
const parseDataSourcePathWithL1DeepBindings = (path) => {
|
|
1198
|
+
const parsedPath = parseDeepPath(path);
|
|
1199
|
+
if (null === parsedPath) {
|
|
1200
|
+
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
1201
|
+
}
|
|
1202
|
+
return {
|
|
1203
|
+
key: parsedPath.key,
|
|
1204
|
+
field: parsedPath.fields[0],
|
|
1205
|
+
referentField: parsedPath.fields[1],
|
|
1206
|
+
};
|
|
1207
|
+
};
|
|
1208
|
+
/**
|
|
1209
|
+
* Detects if paths is valid deep-path, like:
|
|
1210
|
+
* - /gV6yKXp61hfYrR7rEyKxY/fields/mainStory/~locale/fields/cover/~locale/fields/title/~locale
|
|
1211
|
+
* or regular, like:
|
|
1212
|
+
* - /6J8eA60yXwdm5eyUh9fX6/fields/mainStory/~locale
|
|
1213
|
+
* @returns
|
|
1214
|
+
*/
|
|
1215
|
+
const isDeepPath = (deepPathCandidate) => {
|
|
1216
|
+
const deepPathParsed = parseDeepPath(deepPathCandidate);
|
|
1217
|
+
if (!deepPathParsed) {
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
return deepPathParsed.fields.length > 1;
|
|
1221
|
+
};
|
|
1222
|
+
const parseDeepPath = (deepPathCandidate) => {
|
|
1223
|
+
// ALGORITHM:
|
|
1224
|
+
// We start with deep path in form:
|
|
1225
|
+
// /uuid123/fields/mainStory/~locale/fields/cover/~locale/fields/title/~locale
|
|
1226
|
+
// First turn string into array of segments
|
|
1227
|
+
// ['', 'uuid123', 'fields', 'mainStory', '~locale', 'fields', 'cover', '~locale', 'fields', 'title', '~locale']
|
|
1228
|
+
// Then group segments into intermediate represenatation - chunks, where each non-initial chunk starts with 'fields'
|
|
1229
|
+
// [
|
|
1230
|
+
// [ "", "uuid123" ],
|
|
1231
|
+
// [ "fields", "mainStory", "~locale" ],
|
|
1232
|
+
// [ "fields", "cover", "~locale" ],
|
|
1233
|
+
// [ "fields", "title", "~locale" ]
|
|
1234
|
+
// ]
|
|
1235
|
+
// Then check "initial" chunk for corretness
|
|
1236
|
+
// Then check all "field-leading" chunks for correctness
|
|
1237
|
+
const isValidInitialChunk = (initialChunk) => {
|
|
1238
|
+
// must have start with '' and have at least 2 segments, second non-empty
|
|
1239
|
+
// eg. /-_432uuid123123
|
|
1240
|
+
return /^\/([^/^~]+)$/.test(initialChunk.join('/'));
|
|
1241
|
+
};
|
|
1242
|
+
const isValidFieldChunk = (fieldChunk) => {
|
|
1243
|
+
// must start with 'fields' and have at least 3 segments, second non-empty and last segment must be '~locale'
|
|
1244
|
+
// eg. fields/-32234mainStory/~locale
|
|
1245
|
+
return /^fields\/[^/^~]+\/~locale$/.test(fieldChunk.join('/'));
|
|
1246
|
+
};
|
|
1247
|
+
const deepPathSegments = deepPathCandidate.split('/');
|
|
1248
|
+
const chunks = chunkSegments(deepPathSegments, { startNextChunkOnElementEqualTo: 'fields' });
|
|
1249
|
+
if (chunks.length <= 1) {
|
|
1250
|
+
return null; // malformed path, even regular paths have at least 2 chunks
|
|
1251
|
+
}
|
|
1252
|
+
else if (chunks.length === 2) {
|
|
1253
|
+
return null; // deep paths have at least 3 chunks
|
|
1254
|
+
}
|
|
1255
|
+
// With 3+ chunks we can now check for deep path correctness
|
|
1256
|
+
const [initialChunk, ...fieldChunks] = chunks;
|
|
1257
|
+
if (!isValidInitialChunk(initialChunk)) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
if (!fieldChunks.every(isValidFieldChunk)) {
|
|
1261
|
+
return null;
|
|
1262
|
+
}
|
|
1263
|
+
return {
|
|
1264
|
+
key: initialChunk[1], // pick uuid from initial chunk ['','uuid123'],
|
|
1265
|
+
fields: fieldChunks.map((fieldChunk) => fieldChunk[1]), // pick only fieldName eg. from ['fields','mainStory', '~locale'] we pick `mainStory`
|
|
1266
|
+
};
|
|
1267
|
+
};
|
|
1268
|
+
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
1269
|
+
const chunks = [];
|
|
1270
|
+
let currentChunk = [];
|
|
1271
|
+
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
1272
|
+
const excludeEmptyChunks = (chunk) => chunk.length > 0;
|
|
1273
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1274
|
+
const isInitialElement = i === 0;
|
|
1275
|
+
const segment = segments[i];
|
|
1276
|
+
if (isInitialElement) {
|
|
1277
|
+
currentChunk = [segment];
|
|
1278
|
+
}
|
|
1279
|
+
else if (isSegmentBeginningOfChunk(segment)) {
|
|
1280
|
+
chunks.push(currentChunk);
|
|
1281
|
+
currentChunk = [segment];
|
|
1282
|
+
}
|
|
1283
|
+
else {
|
|
1284
|
+
currentChunk.push(segment);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
chunks.push(currentChunk);
|
|
1288
|
+
return chunks.filter(excludeEmptyChunks);
|
|
1289
|
+
};
|
|
1290
|
+
const lastPathNamedSegmentEq = (path, expectedName) => {
|
|
1291
|
+
// `/key123/fields/featureImage/~locale/fields/file/~locale`
|
|
1292
|
+
// ['', 'key123', 'fields', 'featureImage', '~locale', 'fields', 'file', '~locale']
|
|
1293
|
+
const segments = path.split('/');
|
|
1294
|
+
if (segments.length < 2) {
|
|
1295
|
+
console.warn(`[experiences-sdk-react] Attempting to check whether last named segment of the path (${path}) equals to '${expectedName}', but the path doesn't have enough segments.`);
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
const secondLast = segments[segments.length - 2]; // skipping trailing '~locale'
|
|
1299
|
+
return secondLast === expectedName;
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
const resolveHyperlinkPattern = (pattern, entry, locale) => {
|
|
1303
|
+
if (!entry || !locale)
|
|
1304
|
+
return null;
|
|
1305
|
+
const variables = {
|
|
1306
|
+
entry,
|
|
1307
|
+
locale,
|
|
1308
|
+
};
|
|
1309
|
+
return buildTemplate({ template: pattern, context: variables });
|
|
1310
|
+
};
|
|
1311
|
+
function getValue(obj, path) {
|
|
1312
|
+
return path
|
|
1313
|
+
.replace(/\[/g, '.')
|
|
1314
|
+
.replace(/\]/g, '')
|
|
1315
|
+
.split('.')
|
|
1316
|
+
.reduce((o, k) => (o || {})[k], obj);
|
|
1317
|
+
}
|
|
1318
|
+
function addLocale(str, locale) {
|
|
1319
|
+
const fieldsIndicator = 'fields';
|
|
1320
|
+
const fieldsIndex = str.indexOf(fieldsIndicator);
|
|
1321
|
+
if (fieldsIndex !== -1) {
|
|
1322
|
+
const dotIndex = str.indexOf('.', fieldsIndex + fieldsIndicator.length + 1); // +1 for '.'
|
|
1323
|
+
if (dotIndex !== -1) {
|
|
1324
|
+
return str.slice(0, dotIndex + 1) + locale + '.' + str.slice(dotIndex + 1);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return str;
|
|
1328
|
+
}
|
|
1329
|
+
function getTemplateValue(ctx, path) {
|
|
1330
|
+
const pathWithLocale = addLocale(path, ctx.locale);
|
|
1331
|
+
const retrievedValue = getValue(ctx, pathWithLocale);
|
|
1332
|
+
return typeof retrievedValue === 'object' && retrievedValue !== null
|
|
1333
|
+
? retrievedValue[ctx.locale]
|
|
1334
|
+
: retrievedValue;
|
|
1335
|
+
}
|
|
1336
|
+
function buildTemplate({ template, context, }) {
|
|
1337
|
+
const localeVariable = /{\s*locale\s*}/g;
|
|
1338
|
+
// e.g. "{ page.sys.id }"
|
|
1339
|
+
const variables = /{\s*([\S]+?)\s*}/g;
|
|
1340
|
+
return (template
|
|
1341
|
+
// first replace the locale pattern
|
|
1342
|
+
.replace(localeVariable, context.locale)
|
|
1343
|
+
// then resolve the remaining variables
|
|
1344
|
+
.replace(variables, (_, path) => {
|
|
1345
|
+
const fallback = path + '_NOT_FOUND';
|
|
1346
|
+
const value = getTemplateValue(context, path) ?? fallback;
|
|
1347
|
+
// using _.result didn't gave proper results so we run our own version of it
|
|
1348
|
+
return String(typeof value === 'function' ? value() : value);
|
|
1349
|
+
}));
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const stylesToKeep = ['cfImageAsset'];
|
|
1353
|
+
const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.includes(style));
|
|
1354
|
+
const propsToRemove = ['cfHyperlink', 'cfOpenInNewTab', 'cfSsrClassName'];
|
|
1355
|
+
const sanitizeNodeProps = (nodeProps) => {
|
|
1356
|
+
return omit(nodeProps, stylesToRemove, propsToRemove);
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const transformVisibility = (value) => {
|
|
1360
|
+
if (value === false) {
|
|
1361
|
+
return {
|
|
1362
|
+
display: 'none !important',
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
// Don't explicitly set anything when visible to not overwrite values like `grid` or `flex`.
|
|
1366
|
+
return {};
|
|
1367
|
+
};
|
|
1368
|
+
// Keep this for backwards compatibility - deleting this would be a breaking change
|
|
1369
|
+
// because existing components on a users experience will have the width value as fill
|
|
1370
|
+
// rather than 100%
|
|
1371
|
+
const transformFill = (value) => (value === 'fill' ? '100%' : value);
|
|
1372
|
+
const transformGridColumn = (span) => {
|
|
1373
|
+
if (!span) {
|
|
1374
|
+
return {};
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
gridColumn: `span ${span}`,
|
|
1378
|
+
};
|
|
1379
|
+
};
|
|
1380
|
+
const transformBorderStyle = (value) => {
|
|
1381
|
+
if (!value)
|
|
1382
|
+
return {};
|
|
1383
|
+
const parts = value.split(' ');
|
|
1384
|
+
// Just accept the passed value
|
|
1385
|
+
if (parts.length < 3)
|
|
1386
|
+
return { border: value };
|
|
1387
|
+
const [borderSize, borderStyle, ...borderColorParts] = parts;
|
|
1388
|
+
const borderColor = borderColorParts.join(' ');
|
|
1389
|
+
return {
|
|
1390
|
+
border: `${borderSize} ${borderStyle} ${borderColor}`,
|
|
1391
|
+
};
|
|
1392
|
+
};
|
|
1393
|
+
const transformAlignment = (cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection = 'column') => cfFlexDirection === 'row'
|
|
1394
|
+
? {
|
|
1395
|
+
alignItems: cfHorizontalAlignment,
|
|
1396
|
+
justifyContent: cfVerticalAlignment === 'center' ? `safe ${cfVerticalAlignment}` : cfVerticalAlignment,
|
|
1397
|
+
}
|
|
1398
|
+
: {
|
|
1399
|
+
alignItems: cfVerticalAlignment,
|
|
1400
|
+
justifyContent: cfHorizontalAlignment === 'center'
|
|
1401
|
+
? `safe ${cfHorizontalAlignment}`
|
|
1402
|
+
: cfHorizontalAlignment,
|
|
1403
|
+
};
|
|
1404
|
+
const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageOptions) => {
|
|
1405
|
+
const matchBackgroundSize = (scaling) => {
|
|
1406
|
+
if ('fill' === scaling)
|
|
1407
|
+
return 'cover';
|
|
1408
|
+
if ('fit' === scaling)
|
|
1409
|
+
return 'contain';
|
|
1410
|
+
};
|
|
1411
|
+
const matchBackgroundPosition = (alignment) => {
|
|
1412
|
+
if (!alignment || 'string' !== typeof alignment) {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
let [horizontalAlignment, verticalAlignment] = alignment.trim().split(/\s+/, 2);
|
|
1416
|
+
// Special case for handling single values
|
|
1417
|
+
// for backwards compatibility with single values 'right','left', 'center', 'top','bottom'
|
|
1418
|
+
if (horizontalAlignment && !verticalAlignment) {
|
|
1419
|
+
const singleValue = horizontalAlignment;
|
|
1420
|
+
switch (singleValue) {
|
|
1421
|
+
case 'left':
|
|
1422
|
+
horizontalAlignment = 'left';
|
|
1423
|
+
verticalAlignment = 'center';
|
|
1424
|
+
break;
|
|
1425
|
+
case 'right':
|
|
1426
|
+
horizontalAlignment = 'right';
|
|
1427
|
+
verticalAlignment = 'center';
|
|
1428
|
+
break;
|
|
1429
|
+
case 'center':
|
|
1430
|
+
horizontalAlignment = 'center';
|
|
1431
|
+
verticalAlignment = 'center';
|
|
1432
|
+
break;
|
|
1433
|
+
case 'top':
|
|
1434
|
+
horizontalAlignment = 'center';
|
|
1435
|
+
verticalAlignment = 'top';
|
|
1436
|
+
break;
|
|
1437
|
+
case 'bottom':
|
|
1438
|
+
horizontalAlignment = 'center';
|
|
1439
|
+
verticalAlignment = 'bottom';
|
|
1440
|
+
break;
|
|
1441
|
+
// just fall down to the normal validation logic for horiz and vert
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
const isHorizontalValid = ['left', 'right', 'center'].includes(horizontalAlignment);
|
|
1445
|
+
const isVerticalValid = ['top', 'bottom', 'center'].includes(verticalAlignment);
|
|
1446
|
+
horizontalAlignment = isHorizontalValid ? horizontalAlignment : 'left';
|
|
1447
|
+
verticalAlignment = isVerticalValid ? verticalAlignment : 'top';
|
|
1448
|
+
return `${horizontalAlignment} ${verticalAlignment}`;
|
|
1449
|
+
};
|
|
1450
|
+
if (!cfBackgroundImageUrl) {
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
let backgroundImage;
|
|
1454
|
+
let backgroundImageSet;
|
|
1455
|
+
if (typeof cfBackgroundImageUrl === 'string') {
|
|
1456
|
+
backgroundImage = `url(${cfBackgroundImageUrl})`;
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
const imgSet = cfBackgroundImageUrl.srcSet?.join(',');
|
|
1460
|
+
backgroundImage = `url(${cfBackgroundImageUrl.url})`;
|
|
1461
|
+
backgroundImageSet = `image-set(${imgSet})`;
|
|
1462
|
+
}
|
|
1463
|
+
return {
|
|
1464
|
+
backgroundImage,
|
|
1465
|
+
backgroundImage2: backgroundImageSet,
|
|
1466
|
+
backgroundRepeat: cfBackgroundImageOptions?.scaling === 'tile' ? 'repeat' : 'no-repeat',
|
|
1467
|
+
backgroundPosition: matchBackgroundPosition(cfBackgroundImageOptions?.alignment),
|
|
1468
|
+
backgroundSize: matchBackgroundSize(cfBackgroundImageOptions?.scaling),
|
|
1469
|
+
};
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
const toCSSAttribute = (key) => {
|
|
1473
|
+
let val = key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
|
|
1474
|
+
// Remove the number from the end of the key to allow for overrides on style properties
|
|
1475
|
+
val = val.replace(/\d+$/, '');
|
|
1476
|
+
return val;
|
|
1477
|
+
};
|
|
1478
|
+
/**
|
|
1479
|
+
* Turns a list of CSSProperties into a joined CSS string that can be
|
|
1480
|
+
* used for <style> tags. Per default it creates a minimized version.
|
|
1481
|
+
* For editor mode, use the `useWhitespaces` flag to create a more readable version.
|
|
1482
|
+
*
|
|
1483
|
+
* @param cssProperties list of CSS properties
|
|
1484
|
+
* @param useWhitespaces adds whitespaces and newlines between each rule
|
|
1485
|
+
* @returns a string of CSS rules
|
|
1486
|
+
*/
|
|
1487
|
+
const stringifyCssProperties = (cssProperties, useWhitespaces = false) => {
|
|
1488
|
+
const rules = Object.entries(cssProperties)
|
|
1489
|
+
.filter(([, value]) => value !== undefined)
|
|
1490
|
+
.map(([key, value]) => useWhitespaces ? `${toCSSAttribute(key)}: ${value};` : `${toCSSAttribute(key)}:${value};`);
|
|
1491
|
+
return rules.join(useWhitespaces ? '\n' : '');
|
|
1492
|
+
};
|
|
1493
|
+
const buildStyleTag = ({ styles, nodeId }) => {
|
|
1494
|
+
const generatedStyles = stringifyCssProperties(styles, true);
|
|
1495
|
+
const className = `cfstyles-${nodeId ? nodeId : md5(generatedStyles)}`;
|
|
1496
|
+
const styleRule = `.${className}{ ${generatedStyles} }`;
|
|
1497
|
+
return [className, styleRule];
|
|
1498
|
+
};
|
|
1499
|
+
/**
|
|
1500
|
+
* Takes plain design values and transforms them into CSS properties. Undefined values will
|
|
1501
|
+
* be filtered out.
|
|
1502
|
+
*
|
|
1503
|
+
* **Example Input**
|
|
1504
|
+
* ```
|
|
1505
|
+
* values = {
|
|
1506
|
+
* cfVisibility: 'visible',
|
|
1507
|
+
* cfMargin: '10px',
|
|
1508
|
+
* cfFlexReverse: true,
|
|
1509
|
+
* cfImageOptions: { objectFit: 'cover' },
|
|
1510
|
+
* // ...
|
|
1511
|
+
* }
|
|
1512
|
+
* ```
|
|
1513
|
+
* **Example Output**
|
|
1514
|
+
* ```
|
|
1515
|
+
* cssProperties = {
|
|
1516
|
+
* margin: '10px',
|
|
1517
|
+
* flexDirection: 'row-reverse',
|
|
1518
|
+
* objectFit: 'cover',
|
|
1519
|
+
* // ...
|
|
1520
|
+
* }
|
|
1521
|
+
* ```
|
|
1522
|
+
*/
|
|
1523
|
+
const buildCfStyles = (values) => {
|
|
1524
|
+
const cssProperties = {
|
|
1525
|
+
boxSizing: 'border-box',
|
|
1526
|
+
...transformVisibility(values.cfVisibility),
|
|
1527
|
+
margin: values.cfMargin,
|
|
1528
|
+
padding: values.cfPadding,
|
|
1529
|
+
backgroundColor: values.cfBackgroundColor,
|
|
1530
|
+
width: transformFill(values.cfWidth || values.cfImageOptions?.width),
|
|
1531
|
+
height: transformFill(values.cfHeight || values.cfImageOptions?.height),
|
|
1532
|
+
maxWidth: values.cfMaxWidth,
|
|
1533
|
+
...transformGridColumn(values.cfColumnSpan),
|
|
1534
|
+
...transformBorderStyle(values.cfBorder),
|
|
1535
|
+
borderRadius: values.cfBorderRadius,
|
|
1536
|
+
gap: values.cfGap,
|
|
1537
|
+
...transformAlignment(values.cfHorizontalAlignment, values.cfVerticalAlignment, values.cfFlexDirection),
|
|
1538
|
+
flexDirection: values.cfFlexReverse && values.cfFlexDirection
|
|
1539
|
+
? `${values.cfFlexDirection}-reverse`
|
|
1540
|
+
: values.cfFlexDirection,
|
|
1541
|
+
flexWrap: values.cfFlexWrap,
|
|
1542
|
+
...transformBackgroundImage(values.cfBackgroundImageUrl, values.cfBackgroundImageOptions),
|
|
1543
|
+
fontSize: values.cfFontSize,
|
|
1544
|
+
fontWeight: values.cfTextBold ? 'bold' : values.cfFontWeight,
|
|
1545
|
+
fontStyle: values.cfTextItalic ? 'italic' : undefined,
|
|
1546
|
+
textDecoration: values.cfTextUnderline ? 'underline' : undefined,
|
|
1547
|
+
lineHeight: values.cfLineHeight,
|
|
1548
|
+
letterSpacing: values.cfLetterSpacing,
|
|
1549
|
+
color: values.cfTextColor,
|
|
1550
|
+
textAlign: values.cfTextAlign,
|
|
1551
|
+
textTransform: values.cfTextTransform,
|
|
1552
|
+
objectFit: values.cfImageOptions?.objectFit,
|
|
1553
|
+
objectPosition: values.cfImageOptions?.objectPosition,
|
|
1554
|
+
};
|
|
1555
|
+
const cssPropertiesWithoutUndefined = Object.fromEntries(Object.entries(cssProperties).filter(([, value]) => value !== undefined));
|
|
1556
|
+
return cssPropertiesWithoutUndefined;
|
|
1557
|
+
};
|
|
1558
|
+
/**
|
|
1559
|
+
* Container/section default behavior:
|
|
1560
|
+
* Default height => height: EMPTY_CONTAINER_HEIGHT
|
|
1561
|
+
* If a container component has children => height: 'fit-content'
|
|
1562
|
+
*/
|
|
1563
|
+
const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
|
|
1564
|
+
if (!blockId || !isContentfulStructureComponent(blockId) || value !== 'auto') {
|
|
1565
|
+
return value;
|
|
1566
|
+
}
|
|
1567
|
+
if (children.length) {
|
|
1568
|
+
return '100%';
|
|
1569
|
+
}
|
|
1570
|
+
return EMPTY_CONTAINER_HEIGHT$1;
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
function getOptimizedImageUrl(url, width, quality, format) {
|
|
1574
|
+
if (url.startsWith('//')) {
|
|
1575
|
+
url = 'https:' + url;
|
|
1576
|
+
}
|
|
1577
|
+
const params = new URLSearchParams();
|
|
1578
|
+
if (width) {
|
|
1579
|
+
params.append('w', width.toString());
|
|
1580
|
+
}
|
|
1581
|
+
if (quality && quality > 0 && quality < 100) {
|
|
1582
|
+
params.append('q', quality.toString());
|
|
1583
|
+
}
|
|
1584
|
+
if (format) {
|
|
1585
|
+
params.append('fm', format);
|
|
1586
|
+
}
|
|
1587
|
+
const queryString = params.toString();
|
|
1588
|
+
return `${url}${queryString ? '?' + queryString : ''}`;
|
|
1532
1589
|
}
|
|
1533
1590
|
|
|
1534
|
-
function
|
|
1535
|
-
if (
|
|
1536
|
-
|
|
1591
|
+
function validateParams(file, quality, format) {
|
|
1592
|
+
if (!file.details.image) {
|
|
1593
|
+
throw Error('No image in file asset to transform');
|
|
1537
1594
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
console.warn(`Expected a value to be an array, but got: ${JSON.stringify(arrayValue)}`);
|
|
1541
|
-
return;
|
|
1595
|
+
if (quality < 0 || quality > 100) {
|
|
1596
|
+
throw Error('Quality must be between 0 and 100');
|
|
1542
1597
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1598
|
+
if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
|
|
1599
|
+
throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
|
|
1600
|
+
}
|
|
1601
|
+
return true;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
const MAX_WIDTH_ALLOWED$1 = 2000;
|
|
1605
|
+
const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', format) => {
|
|
1606
|
+
const qualityNumber = Number(quality.replace('%', ''));
|
|
1607
|
+
if (!validateParams(file, qualityNumber, format)) ;
|
|
1608
|
+
if (!validateParams(file, qualityNumber, format)) ;
|
|
1609
|
+
const url = file.url;
|
|
1610
|
+
const { width1x, width2x } = getWidths(widthStyle, file);
|
|
1611
|
+
const imageUrl1x = getOptimizedImageUrl(url, width1x, qualityNumber, format);
|
|
1612
|
+
const imageUrl2x = getOptimizedImageUrl(url, width2x, qualityNumber, format);
|
|
1613
|
+
const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
|
|
1614
|
+
const returnedUrl = getOptimizedImageUrl(url, width2x, qualityNumber, format);
|
|
1615
|
+
const optimizedBackgroundImageAsset = {
|
|
1616
|
+
url: returnedUrl,
|
|
1617
|
+
srcSet,
|
|
1618
|
+
file,
|
|
1619
|
+
};
|
|
1620
|
+
return optimizedBackgroundImageAsset;
|
|
1621
|
+
function getWidths(widthStyle, file) {
|
|
1622
|
+
let width1x = 0;
|
|
1623
|
+
let width2x = 0;
|
|
1624
|
+
const intrinsicImageWidth = file.details.image.width;
|
|
1625
|
+
if (widthStyle.endsWith('px')) {
|
|
1626
|
+
width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
|
|
1563
1627
|
}
|
|
1564
1628
|
else {
|
|
1565
|
-
|
|
1566
|
-
return undefined;
|
|
1629
|
+
width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
|
|
1567
1630
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableType, path) => {
|
|
1573
|
-
const entityOrAsset = entityStore.getEntryOrAsset(binding, path);
|
|
1574
|
-
if (!entityOrAsset)
|
|
1575
|
-
return;
|
|
1576
|
-
switch (variableType) {
|
|
1577
|
-
case 'Media':
|
|
1578
|
-
// If we bound a normal entry field to the media variable we just return the bound value
|
|
1579
|
-
if (entityOrAsset.sys.type === 'Entry') {
|
|
1580
|
-
return getBoundValue(entityOrAsset, path);
|
|
1581
|
-
}
|
|
1582
|
-
return transformMedia(entityOrAsset, variables, resolveDesignValue, variableName, path);
|
|
1583
|
-
case 'RichText':
|
|
1584
|
-
return transformRichText(entityOrAsset, entityStore, path);
|
|
1585
|
-
case 'Array':
|
|
1586
|
-
return getArrayValue(entityOrAsset, path, entityStore);
|
|
1587
|
-
case 'Link':
|
|
1588
|
-
return getResolvedEntryFromLink(entityOrAsset, path, entityStore);
|
|
1589
|
-
default:
|
|
1590
|
-
return getBoundValue(entityOrAsset, path);
|
|
1631
|
+
width2x = Math.min(width1x * 2, intrinsicImageWidth);
|
|
1632
|
+
return { width1x, width2x };
|
|
1591
1633
|
}
|
|
1592
1634
|
};
|
|
1593
1635
|
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
while (queue.length) {
|
|
1599
|
-
const node = queue.shift();
|
|
1600
|
-
if (!node) {
|
|
1601
|
-
continue;
|
|
1602
|
-
}
|
|
1603
|
-
dataSource = { ...dataSource, ...node.data.dataSource };
|
|
1604
|
-
unboundValues = { ...unboundValues, ...node.data.unboundValues };
|
|
1605
|
-
if (node.children.length) {
|
|
1606
|
-
queue.push(...node.children);
|
|
1607
|
-
}
|
|
1636
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1637
|
+
function get(obj, path) {
|
|
1638
|
+
if (!path.length) {
|
|
1639
|
+
return obj;
|
|
1608
1640
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
};
|
|
1613
|
-
};
|
|
1614
|
-
function parseCSSValue(input) {
|
|
1615
|
-
const regex = /^(\d+(\.\d+)?)(px|em|rem)$/;
|
|
1616
|
-
const match = input.match(regex);
|
|
1617
|
-
if (match) {
|
|
1618
|
-
return {
|
|
1619
|
-
value: parseFloat(match[1]),
|
|
1620
|
-
unit: match[3],
|
|
1621
|
-
};
|
|
1641
|
+
try {
|
|
1642
|
+
const [currentPath, ...nextPath] = path;
|
|
1643
|
+
return get(obj[currentPath], nextPath);
|
|
1622
1644
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
function getTargetValueInPixels(targetWidthObject) {
|
|
1626
|
-
switch (targetWidthObject.unit) {
|
|
1627
|
-
case 'px':
|
|
1628
|
-
return targetWidthObject.value;
|
|
1629
|
-
case 'em':
|
|
1630
|
-
return targetWidthObject.value * 16;
|
|
1631
|
-
case 'rem':
|
|
1632
|
-
return targetWidthObject.value * 16;
|
|
1633
|
-
default:
|
|
1634
|
-
return targetWidthObject.value;
|
|
1645
|
+
catch (err) {
|
|
1646
|
+
return undefined;
|
|
1635
1647
|
}
|
|
1636
1648
|
}
|
|
1637
1649
|
|
|
1638
|
-
const
|
|
1639
|
-
const
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
return
|
|
1650
|
+
const getBoundValue = (entryOrAsset, path) => {
|
|
1651
|
+
const value = get(entryOrAsset, path.split('/').slice(2, -1));
|
|
1652
|
+
return value && typeof value == 'object' && value.url
|
|
1653
|
+
? value.url
|
|
1654
|
+
: value;
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1657
|
+
const transformRichText = (entryOrAsset, entityStore, path) => {
|
|
1658
|
+
const value = getBoundValue(entryOrAsset, path);
|
|
1659
|
+
if (typeof value === 'string') {
|
|
1660
|
+
return {
|
|
1661
|
+
data: {},
|
|
1662
|
+
content: [
|
|
1663
|
+
{
|
|
1664
|
+
nodeType: BLOCKS.PARAGRAPH,
|
|
1665
|
+
data: {},
|
|
1666
|
+
content: [
|
|
1667
|
+
{
|
|
1668
|
+
data: {},
|
|
1669
|
+
nodeType: 'text',
|
|
1670
|
+
value: value,
|
|
1671
|
+
marks: [],
|
|
1672
|
+
},
|
|
1673
|
+
],
|
|
1674
|
+
},
|
|
1675
|
+
],
|
|
1676
|
+
nodeType: BLOCKS.DOCUMENT,
|
|
1677
|
+
};
|
|
1649
1678
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1679
|
+
if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
|
|
1680
|
+
// resolve any links to assets/entries/hyperlinks
|
|
1681
|
+
const richTextDocument = value;
|
|
1682
|
+
resolveLinks(richTextDocument, entityStore);
|
|
1683
|
+
return richTextDocument;
|
|
1653
1684
|
}
|
|
1654
1685
|
return undefined;
|
|
1655
1686
|
};
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
return array.reverse().find(predicate);
|
|
1659
|
-
};
|
|
1660
|
-
// Initialise media query matchers. This won't include the always matching fallback breakpoint.
|
|
1661
|
-
const mediaQueryMatcher = (breakpoints) => {
|
|
1662
|
-
const mediaQueryMatches = {};
|
|
1663
|
-
const mediaQueryMatchers = breakpoints
|
|
1664
|
-
.map((breakpoint) => {
|
|
1665
|
-
const cssMediaQuery = toCSSMediaQuery(breakpoint);
|
|
1666
|
-
if (!cssMediaQuery)
|
|
1667
|
-
return undefined;
|
|
1668
|
-
if (typeof window === 'undefined')
|
|
1669
|
-
return undefined;
|
|
1670
|
-
const mediaQueryMatcher = window.matchMedia(cssMediaQuery);
|
|
1671
|
-
mediaQueryMatches[breakpoint.id] = mediaQueryMatcher.matches;
|
|
1672
|
-
return { id: breakpoint.id, signal: mediaQueryMatcher };
|
|
1673
|
-
})
|
|
1674
|
-
.filter((matcher) => !!matcher);
|
|
1675
|
-
return [mediaQueryMatchers, mediaQueryMatches];
|
|
1676
|
-
};
|
|
1677
|
-
const getActiveBreakpointIndex = (breakpoints, mediaQueryMatches, fallbackBreakpointIndex) => {
|
|
1678
|
-
// The breakpoints are ordered (desktop-first: descending by screen width)
|
|
1679
|
-
const breakpointsWithMatches = breakpoints.map(({ id }, index) => ({
|
|
1680
|
-
id,
|
|
1681
|
-
index,
|
|
1682
|
-
// The fallback breakpoint with wildcard query will always match
|
|
1683
|
-
isMatch: mediaQueryMatches[id] ?? index === fallbackBreakpointIndex,
|
|
1684
|
-
}));
|
|
1685
|
-
// Find the last breakpoint in the list that matches (desktop-first: the narrowest one)
|
|
1686
|
-
const mostSpecificIndex = findLast(breakpointsWithMatches, ({ isMatch }) => isMatch)?.index;
|
|
1687
|
-
return mostSpecificIndex ?? fallbackBreakpointIndex;
|
|
1688
|
-
};
|
|
1689
|
-
const getFallbackBreakpointIndex = (breakpoints) => {
|
|
1690
|
-
// We assume that there will be a single breakpoint which uses the wildcard query.
|
|
1691
|
-
// If there is none, we just take the first one in the list.
|
|
1692
|
-
return Math.max(breakpoints.findIndex(({ query }) => query === '*'), 0);
|
|
1693
|
-
};
|
|
1694
|
-
const builtInStylesWithDesignTokens = [
|
|
1695
|
-
'cfMargin',
|
|
1696
|
-
'cfPadding',
|
|
1697
|
-
'cfGap',
|
|
1698
|
-
'cfWidth',
|
|
1699
|
-
'cfHeight',
|
|
1700
|
-
'cfBackgroundColor',
|
|
1701
|
-
'cfBorder',
|
|
1702
|
-
'cfBorderRadius',
|
|
1703
|
-
'cfFontSize',
|
|
1704
|
-
'cfLineHeight',
|
|
1705
|
-
'cfLetterSpacing',
|
|
1706
|
-
'cfTextColor',
|
|
1707
|
-
'cfMaxWidth',
|
|
1708
|
-
];
|
|
1709
|
-
const isValidBreakpointValue = (value) => {
|
|
1710
|
-
return value !== undefined && value !== null && value !== '';
|
|
1687
|
+
const isLinkTarget = (node) => {
|
|
1688
|
+
return node?.data?.target?.sys?.type === 'Link';
|
|
1711
1689
|
};
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
};
|
|
1721
|
-
if (valuesByBreakpoint instanceof Object) {
|
|
1722
|
-
// Assume that the values are sorted by media query to apply the cascading CSS logic
|
|
1723
|
-
for (let index = activeBreakpointIndex; index >= 0; index--) {
|
|
1724
|
-
const breakpointId = breakpoints[index]?.id;
|
|
1725
|
-
if (isValidBreakpointValue(valuesByBreakpoint[breakpointId])) {
|
|
1726
|
-
// If the value is defined, we use it and stop the breakpoints cascade
|
|
1727
|
-
if (resolveDesignTokens) {
|
|
1728
|
-
return eventuallyResolveDesignTokens(valuesByBreakpoint[breakpointId]);
|
|
1729
|
-
}
|
|
1730
|
-
return valuesByBreakpoint[breakpointId];
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
const fallbackBreakpointId = breakpoints[fallbackBreakpointIndex]?.id;
|
|
1734
|
-
if (isValidBreakpointValue(valuesByBreakpoint[fallbackBreakpointId])) {
|
|
1735
|
-
if (resolveDesignTokens) {
|
|
1736
|
-
return eventuallyResolveDesignTokens(valuesByBreakpoint[fallbackBreakpointId]);
|
|
1737
|
-
}
|
|
1738
|
-
return valuesByBreakpoint[fallbackBreakpointId];
|
|
1690
|
+
const resolveLinks = (node, entityStore) => {
|
|
1691
|
+
if (!node)
|
|
1692
|
+
return;
|
|
1693
|
+
// Resolve link if current node has one
|
|
1694
|
+
if (isLinkTarget(node)) {
|
|
1695
|
+
const entity = entityStore.getEntityFromLink(node.data.target);
|
|
1696
|
+
if (entity) {
|
|
1697
|
+
node.data.target = entity;
|
|
1739
1698
|
}
|
|
1740
1699
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1700
|
+
// Process content array if it exists
|
|
1701
|
+
if ('content' in node && Array.isArray(node.content)) {
|
|
1702
|
+
node.content.forEach((childNode) => resolveLinks(childNode, entityStore));
|
|
1744
1703
|
}
|
|
1745
1704
|
};
|
|
1746
1705
|
|
|
1747
|
-
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1706
|
+
const MAX_WIDTH_ALLOWED = 4000;
|
|
1707
|
+
const getOptimizedImageAsset = ({ file, sizes, loading, quality = '100%', format, }) => {
|
|
1708
|
+
const qualityNumber = Number(quality.replace('%', ''));
|
|
1709
|
+
if (!validateParams(file, qualityNumber, format)) ;
|
|
1710
|
+
const url = file.url;
|
|
1711
|
+
const maxWidth = Math.min(file.details.image.width, MAX_WIDTH_ALLOWED);
|
|
1712
|
+
const numOfParts = Math.max(2, Math.ceil(maxWidth / 500));
|
|
1713
|
+
const widthParts = Array.from({ length: numOfParts }, (_, index) => Math.ceil((index + 1) * (maxWidth / numOfParts)));
|
|
1714
|
+
const srcSet = sizes
|
|
1715
|
+
? widthParts.map((width) => `${getOptimizedImageUrl(url, width, qualityNumber, format)} ${width}w`)
|
|
1716
|
+
: [];
|
|
1717
|
+
const intrinsicImageWidth = file.details.image.width;
|
|
1718
|
+
if (intrinsicImageWidth > MAX_WIDTH_ALLOWED) {
|
|
1719
|
+
srcSet.push(`${getOptimizedImageUrl(url, undefined, qualityNumber, format)} ${intrinsicImageWidth}w`);
|
|
1720
|
+
}
|
|
1721
|
+
const returnedUrl = getOptimizedImageUrl(url, file.details.image.width > 2000 ? 2000 : undefined, qualityNumber, format);
|
|
1722
|
+
const optimizedImageAsset = {
|
|
1723
|
+
url: returnedUrl,
|
|
1724
|
+
srcSet,
|
|
1725
|
+
sizes,
|
|
1726
|
+
file,
|
|
1727
|
+
loading,
|
|
1728
|
+
};
|
|
1729
|
+
return optimizedImageAsset;
|
|
1766
1730
|
};
|
|
1767
1731
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
1732
|
+
const transformMedia = (asset, variables, resolveDesignValue, variableName, path) => {
|
|
1733
|
+
let value;
|
|
1734
|
+
// If it is not a deep path and not pointing to the file of the asset,
|
|
1735
|
+
// it is just pointing to a normal field and therefore we just resolve the value as normal field
|
|
1736
|
+
if (!isDeepPath(path) && !lastPathNamedSegmentEq(path, 'file')) {
|
|
1737
|
+
return getBoundValue(asset, path);
|
|
1775
1738
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1739
|
+
//TODO: this will be better served by injectable type transformers instead of if statement
|
|
1740
|
+
if (variableName === 'cfImageAsset') {
|
|
1741
|
+
const optionsVariableName = 'cfImageOptions';
|
|
1742
|
+
const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
|
|
1743
|
+
? variables[optionsVariableName].valuesByBreakpoint
|
|
1744
|
+
: {}, optionsVariableName);
|
|
1745
|
+
if (!options) {
|
|
1746
|
+
console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
try {
|
|
1750
|
+
value = getOptimizedImageAsset({
|
|
1751
|
+
file: asset.fields.file,
|
|
1752
|
+
loading: options.loading,
|
|
1753
|
+
sizes: options.targetSize,
|
|
1754
|
+
quality: options.quality,
|
|
1755
|
+
format: options.format,
|
|
1756
|
+
});
|
|
1757
|
+
return value;
|
|
1758
|
+
}
|
|
1759
|
+
catch (error) {
|
|
1760
|
+
console.error('Error transforming image asset', error);
|
|
1761
|
+
}
|
|
1762
|
+
return;
|
|
1788
1763
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
};
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
const
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1764
|
+
if (variableName === 'cfBackgroundImageUrl') {
|
|
1765
|
+
let width = resolveDesignValue(variables['cfWidth']?.type === 'DesignValue' ? variables['cfWidth'].valuesByBreakpoint : {}, 'cfWidth') || '100%';
|
|
1766
|
+
const optionsVariableName = 'cfBackgroundImageOptions';
|
|
1767
|
+
const options = resolveDesignValue(variables[optionsVariableName]?.type === 'DesignValue'
|
|
1768
|
+
? variables[optionsVariableName].valuesByBreakpoint
|
|
1769
|
+
: {}, optionsVariableName);
|
|
1770
|
+
if (!options) {
|
|
1771
|
+
console.error(`Error transforming image asset: Required variable [${optionsVariableName}] missing from component definition`);
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
try {
|
|
1775
|
+
// Target width (px/rem/em) will be applied to the css url if it's lower than the original image width (in px)
|
|
1776
|
+
const assetDetails = asset.fields.file?.details;
|
|
1777
|
+
const assetWidth = assetDetails?.image?.width || 0; // This is always in px
|
|
1778
|
+
const targetWidthObject = parseCSSValue(options.targetSize); // Contains value and unit (px/rem/em) so convert and then compare to assetWidth
|
|
1779
|
+
const targetValue = targetWidthObject
|
|
1780
|
+
? getTargetValueInPixels(targetWidthObject)
|
|
1781
|
+
: assetWidth;
|
|
1782
|
+
if (targetValue < assetWidth)
|
|
1783
|
+
width = `${targetValue}px`;
|
|
1784
|
+
value = getOptimizedBackgroundImageAsset(asset.fields.file, width, options.quality, options.format);
|
|
1785
|
+
return value;
|
|
1786
|
+
}
|
|
1787
|
+
catch (error) {
|
|
1788
|
+
console.error('Error transforming image asset', error);
|
|
1789
|
+
}
|
|
1790
|
+
return;
|
|
1806
1791
|
}
|
|
1807
|
-
return
|
|
1792
|
+
return asset.fields.file?.url;
|
|
1808
1793
|
};
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
// First turn string into array of segments
|
|
1814
|
-
// ['', 'uuid123', 'fields', 'mainStory', '~locale', 'fields', 'cover', '~locale', 'fields', 'title', '~locale']
|
|
1815
|
-
// Then group segments into intermediate represenatation - chunks, where each non-initial chunk starts with 'fields'
|
|
1816
|
-
// [
|
|
1817
|
-
// [ "", "uuid123" ],
|
|
1818
|
-
// [ "fields", "mainStory", "~locale" ],
|
|
1819
|
-
// [ "fields", "cover", "~locale" ],
|
|
1820
|
-
// [ "fields", "title", "~locale" ]
|
|
1821
|
-
// ]
|
|
1822
|
-
// Then check "initial" chunk for corretness
|
|
1823
|
-
// Then check all "field-leading" chunks for correctness
|
|
1824
|
-
const isValidInitialChunk = (initialChunk) => {
|
|
1825
|
-
// must have start with '' and have at least 2 segments, second non-empty
|
|
1826
|
-
// eg. /-_432uuid123123
|
|
1827
|
-
return /^\/([^/^~]+)$/.test(initialChunk.join('/'));
|
|
1828
|
-
};
|
|
1829
|
-
const isValidFieldChunk = (fieldChunk) => {
|
|
1830
|
-
// must start with 'fields' and have at least 3 segments, second non-empty and last segment must be '~locale'
|
|
1831
|
-
// eg. fields/-32234mainStory/~locale
|
|
1832
|
-
return /^fields\/[^/^~]+\/~locale$/.test(fieldChunk.join('/'));
|
|
1833
|
-
};
|
|
1834
|
-
const deepPathSegments = deepPathCandidate.split('/');
|
|
1835
|
-
const chunks = chunkSegments(deepPathSegments, { startNextChunkOnElementEqualTo: 'fields' });
|
|
1836
|
-
if (chunks.length <= 1) {
|
|
1837
|
-
return null; // malformed path, even regular paths have at least 2 chunks
|
|
1794
|
+
|
|
1795
|
+
function getResolvedEntryFromLink(entryOrAsset, path, entityStore) {
|
|
1796
|
+
if (entryOrAsset.sys.type === 'Asset') {
|
|
1797
|
+
return entryOrAsset;
|
|
1838
1798
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1799
|
+
const value = get(entryOrAsset, path.split('/').slice(2, -1));
|
|
1800
|
+
if (value?.sys.type !== 'Link') {
|
|
1801
|
+
console.warn(`Expected a link to a reference, but got: ${JSON.stringify(value)}`);
|
|
1802
|
+
return;
|
|
1841
1803
|
}
|
|
1842
|
-
//
|
|
1843
|
-
const
|
|
1844
|
-
if (!
|
|
1845
|
-
return
|
|
1804
|
+
//Look up the reference in the entity store
|
|
1805
|
+
const resolvedEntity = entityStore.getEntityFromLink(value);
|
|
1806
|
+
if (!resolvedEntity) {
|
|
1807
|
+
return;
|
|
1846
1808
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1809
|
+
//resolve any embedded links - we currently only support 2 levels deep
|
|
1810
|
+
const fields = resolvedEntity.fields || {};
|
|
1811
|
+
Object.entries(fields).forEach(([fieldKey, field]) => {
|
|
1812
|
+
if (field && field.sys?.type === 'Link') {
|
|
1813
|
+
const entity = entityStore.getEntityFromLink(field);
|
|
1814
|
+
if (entity) {
|
|
1815
|
+
resolvedEntity.fields[fieldKey] = entity;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
else if (field && Array.isArray(field)) {
|
|
1819
|
+
resolvedEntity.fields[fieldKey] = field.map((innerField) => {
|
|
1820
|
+
if (innerField && innerField.sys?.type === 'Link') {
|
|
1821
|
+
const entity = entityStore.getEntityFromLink(innerField);
|
|
1822
|
+
if (entity) {
|
|
1823
|
+
return entity;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
return innerField;
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
return resolvedEntity;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
function getArrayValue(entryOrAsset, path, entityStore) {
|
|
1834
|
+
if (entryOrAsset.sys.type === 'Asset') {
|
|
1835
|
+
return entryOrAsset;
|
|
1836
|
+
}
|
|
1837
|
+
const arrayValue = get(entryOrAsset, path.split('/').slice(2, -1));
|
|
1838
|
+
if (!isArray(arrayValue)) {
|
|
1839
|
+
console.warn(`Expected a value to be an array, but got: ${JSON.stringify(arrayValue)}`);
|
|
1840
|
+
return;
|
|
1849
1841
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
};
|
|
1854
|
-
};
|
|
1855
|
-
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
1856
|
-
const chunks = [];
|
|
1857
|
-
let currentChunk = [];
|
|
1858
|
-
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
1859
|
-
const excludeEmptyChunks = (chunk) => chunk.length > 0;
|
|
1860
|
-
for (let i = 0; i < segments.length; i++) {
|
|
1861
|
-
const isInitialElement = i === 0;
|
|
1862
|
-
const segment = segments[i];
|
|
1863
|
-
if (isInitialElement) {
|
|
1864
|
-
currentChunk = [segment];
|
|
1842
|
+
const result = arrayValue.map((value) => {
|
|
1843
|
+
if (typeof value === 'string') {
|
|
1844
|
+
return value;
|
|
1865
1845
|
}
|
|
1866
|
-
else if (
|
|
1867
|
-
|
|
1868
|
-
|
|
1846
|
+
else if (value?.sys?.type === 'Link') {
|
|
1847
|
+
const resolvedEntity = entityStore.getEntityFromLink(value);
|
|
1848
|
+
if (!resolvedEntity) {
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
//resolve any embedded links - we currently only support 2 levels deep
|
|
1852
|
+
const fields = resolvedEntity.fields || {};
|
|
1853
|
+
Object.entries(fields).forEach(([fieldKey, field]) => {
|
|
1854
|
+
if (field && field.sys?.type === 'Link') {
|
|
1855
|
+
const entity = entityStore.getEntityFromLink(field);
|
|
1856
|
+
if (entity) {
|
|
1857
|
+
resolvedEntity.fields[fieldKey] = entity;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
return resolvedEntity;
|
|
1869
1862
|
}
|
|
1870
1863
|
else {
|
|
1871
|
-
|
|
1864
|
+
console.warn(`Expected value to be a string or Link, but got: ${JSON.stringify(value)}`);
|
|
1865
|
+
return undefined;
|
|
1872
1866
|
}
|
|
1867
|
+
});
|
|
1868
|
+
return result;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableType, path) => {
|
|
1872
|
+
const entityOrAsset = entityStore.getEntryOrAsset(binding, path);
|
|
1873
|
+
if (!entityOrAsset)
|
|
1874
|
+
return;
|
|
1875
|
+
switch (variableType) {
|
|
1876
|
+
case 'Media':
|
|
1877
|
+
// If we bound a normal entry field to the media variable we just return the bound value
|
|
1878
|
+
if (entityOrAsset.sys.type === 'Entry') {
|
|
1879
|
+
return getBoundValue(entityOrAsset, path);
|
|
1880
|
+
}
|
|
1881
|
+
return transformMedia(entityOrAsset, variables, resolveDesignValue, variableName, path);
|
|
1882
|
+
case 'RichText':
|
|
1883
|
+
return transformRichText(entityOrAsset, entityStore, path);
|
|
1884
|
+
case 'Array':
|
|
1885
|
+
return getArrayValue(entityOrAsset, path, entityStore);
|
|
1886
|
+
case 'Link':
|
|
1887
|
+
return getResolvedEntryFromLink(entityOrAsset, path, entityStore);
|
|
1888
|
+
default:
|
|
1889
|
+
return getBoundValue(entityOrAsset, path);
|
|
1873
1890
|
}
|
|
1874
|
-
chunks.push(currentChunk);
|
|
1875
|
-
return chunks.filter(excludeEmptyChunks);
|
|
1876
|
-
};
|
|
1877
|
-
const lastPathNamedSegmentEq = (path, expectedName) => {
|
|
1878
|
-
// `/key123/fields/featureImage/~locale/fields/file/~locale`
|
|
1879
|
-
// ['', 'key123', 'fields', 'featureImage', '~locale', 'fields', 'file', '~locale']
|
|
1880
|
-
const segments = path.split('/');
|
|
1881
|
-
if (segments.length < 2) {
|
|
1882
|
-
console.warn(`[experiences-sdk-react] Attempting to check whether last named segment of the path (${path}) equals to '${expectedName}', but the path doesn't have enough segments.`);
|
|
1883
|
-
return false;
|
|
1884
|
-
}
|
|
1885
|
-
const secondLast = segments[segments.length - 2]; // skipping trailing '~locale'
|
|
1886
|
-
return secondLast === expectedName;
|
|
1887
1891
|
};
|
|
1888
1892
|
|
|
1889
|
-
const
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
const
|
|
1893
|
-
|
|
1894
|
-
|
|
1893
|
+
const getDataFromTree = (tree) => {
|
|
1894
|
+
let dataSource = {};
|
|
1895
|
+
let unboundValues = {};
|
|
1896
|
+
const queue = [...tree.root.children];
|
|
1897
|
+
while (queue.length) {
|
|
1898
|
+
const node = queue.shift();
|
|
1899
|
+
if (!node) {
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1902
|
+
dataSource = { ...dataSource, ...node.data.dataSource };
|
|
1903
|
+
unboundValues = { ...unboundValues, ...node.data.unboundValues };
|
|
1904
|
+
if (node.children.length) {
|
|
1905
|
+
queue.push(...node.children);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return {
|
|
1909
|
+
dataSource,
|
|
1910
|
+
unboundValues,
|
|
1895
1911
|
};
|
|
1896
|
-
return buildTemplate({ template: pattern, context: variables });
|
|
1897
1912
|
};
|
|
1898
|
-
function
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
const fieldsIndicator = 'fields';
|
|
1907
|
-
const fieldsIndex = str.indexOf(fieldsIndicator);
|
|
1908
|
-
if (fieldsIndex !== -1) {
|
|
1909
|
-
const dotIndex = str.indexOf('.', fieldsIndex + fieldsIndicator.length + 1); // +1 for '.'
|
|
1910
|
-
if (dotIndex !== -1) {
|
|
1911
|
-
return str.slice(0, dotIndex + 1) + locale + '.' + str.slice(dotIndex + 1);
|
|
1912
|
-
}
|
|
1913
|
+
function parseCSSValue(input) {
|
|
1914
|
+
const regex = /^(\d+(\.\d+)?)(px|em|rem)$/;
|
|
1915
|
+
const match = input.match(regex);
|
|
1916
|
+
if (match) {
|
|
1917
|
+
return {
|
|
1918
|
+
value: parseFloat(match[1]),
|
|
1919
|
+
unit: match[3],
|
|
1920
|
+
};
|
|
1913
1921
|
}
|
|
1914
|
-
return
|
|
1915
|
-
}
|
|
1916
|
-
function getTemplateValue(ctx, path) {
|
|
1917
|
-
const pathWithLocale = addLocale(path, ctx.locale);
|
|
1918
|
-
const retrievedValue = getValue(ctx, pathWithLocale);
|
|
1919
|
-
return typeof retrievedValue === 'object' && retrievedValue !== null
|
|
1920
|
-
? retrievedValue[ctx.locale]
|
|
1921
|
-
: retrievedValue;
|
|
1922
|
+
return null;
|
|
1922
1923
|
}
|
|
1923
|
-
function
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
// using _.result didn't gave proper results so we run our own version of it
|
|
1935
|
-
return String(typeof value === 'function' ? value() : value);
|
|
1936
|
-
}));
|
|
1924
|
+
function getTargetValueInPixels(targetWidthObject) {
|
|
1925
|
+
switch (targetWidthObject.unit) {
|
|
1926
|
+
case 'px':
|
|
1927
|
+
return targetWidthObject.value;
|
|
1928
|
+
case 'em':
|
|
1929
|
+
return targetWidthObject.value * 16;
|
|
1930
|
+
case 'rem':
|
|
1931
|
+
return targetWidthObject.value * 16;
|
|
1932
|
+
default:
|
|
1933
|
+
return targetWidthObject.value;
|
|
1934
|
+
}
|
|
1937
1935
|
}
|
|
1938
1936
|
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
const sanitizeNodeProps = (nodeProps) => {
|
|
1943
|
-
return omit(nodeProps, stylesToRemove, propsToRemove);
|
|
1944
|
-
};
|
|
1945
|
-
|
|
1946
|
-
const CF_DEBUG_KEY$1 = 'cf_debug';
|
|
1947
|
-
let DebugLogger$1 = class DebugLogger {
|
|
1948
|
-
constructor() {
|
|
1949
|
-
// Public methods for logging
|
|
1950
|
-
this.error = this.logger('error');
|
|
1951
|
-
this.warn = this.logger('warn');
|
|
1952
|
-
this.log = this.logger('log');
|
|
1953
|
-
this.debug = this.logger('debug');
|
|
1954
|
-
if (typeof localStorage === 'undefined') {
|
|
1955
|
-
this.enabled = false;
|
|
1956
|
-
return;
|
|
1957
|
-
}
|
|
1958
|
-
// Default to checking localStorage for the debug mode on initialization if in browser
|
|
1959
|
-
this.enabled = localStorage.getItem(CF_DEBUG_KEY$1) === 'true';
|
|
1937
|
+
class ParseError extends Error {
|
|
1938
|
+
constructor(message) {
|
|
1939
|
+
super(message);
|
|
1960
1940
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1941
|
+
}
|
|
1942
|
+
const isValidJsonObject = (s) => {
|
|
1943
|
+
try {
|
|
1944
|
+
const result = JSON.parse(s);
|
|
1945
|
+
if ('object' !== typeof result) {
|
|
1946
|
+
return false;
|
|
1964
1947
|
}
|
|
1965
|
-
return
|
|
1948
|
+
return true;
|
|
1966
1949
|
}
|
|
1967
|
-
|
|
1968
|
-
return
|
|
1950
|
+
catch (e) {
|
|
1951
|
+
return false;
|
|
1969
1952
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
localStorage.removeItem(CF_DEBUG_KEY$1);
|
|
1953
|
+
};
|
|
1954
|
+
const doesMismatchMessageSchema = (event) => {
|
|
1955
|
+
try {
|
|
1956
|
+
tryParseMessage(event);
|
|
1957
|
+
return false;
|
|
1958
|
+
}
|
|
1959
|
+
catch (e) {
|
|
1960
|
+
if (e instanceof ParseError) {
|
|
1961
|
+
return e.message;
|
|
1980
1962
|
}
|
|
1963
|
+
throw e;
|
|
1981
1964
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
};
|
|
1965
|
+
};
|
|
1966
|
+
const tryParseMessage = (event) => {
|
|
1967
|
+
if (!event.data) {
|
|
1968
|
+
throw new ParseError('Field event.data is missing');
|
|
1969
|
+
}
|
|
1970
|
+
if ('string' !== typeof event.data) {
|
|
1971
|
+
throw new ParseError(`Field event.data must be a string, instead of '${typeof event.data}'`);
|
|
1972
|
+
}
|
|
1973
|
+
if (!isValidJsonObject(event.data)) {
|
|
1974
|
+
throw new ParseError('Field event.data must be a valid JSON object serialized as string');
|
|
1975
|
+
}
|
|
1976
|
+
const eventData = JSON.parse(event.data);
|
|
1977
|
+
if (!eventData.source) {
|
|
1978
|
+
throw new ParseError(`Field eventData.source must be equal to 'composability-app'`);
|
|
1979
|
+
}
|
|
1980
|
+
if ('composability-app' !== eventData.source) {
|
|
1981
|
+
throw new ParseError(`Field eventData.source must be equal to 'composability-app', instead of '${eventData.source}'`);
|
|
1982
|
+
}
|
|
1983
|
+
// check eventData.eventType
|
|
1984
|
+
const supportedEventTypes = Object.values(INCOMING_EVENTS$1);
|
|
1985
|
+
if (!supportedEventTypes.includes(eventData.eventType)) {
|
|
1986
|
+
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
1987
|
+
if (eventData.eventType !== PostMessageMethods$3.REQUESTED_ENTITIES) {
|
|
1988
|
+
throw new ParseError(`Field eventData.eventType must be one of the supported values: [${supportedEventTypes.join(', ')}]`);
|
|
1989
|
+
}
|
|
1989
1990
|
}
|
|
1991
|
+
return eventData;
|
|
1990
1992
|
};
|
|
1991
|
-
DebugLogger$1.instance = null;
|
|
1992
|
-
DebugLogger$1.getInstance();
|
|
1993
1993
|
|
|
1994
1994
|
const sendMessage = (eventType, data) => {
|
|
1995
1995
|
if (typeof window === 'undefined') {
|