@contentful/experiences-visual-editor-react 1.42.4-prerelease-20250702T1207-37b3bfc.0 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -2
- package/dist/index.js +849 -290
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +3602 -3043
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
import React, { useEffect, useRef, useState, useCallback, forwardRef, useLayoutEffect, useMemo } from 'react';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { omit, isArray, isEqual, get as get$
|
|
4
|
+
import { omit, isArray, isEqual, get as get$2 } from 'lodash-es';
|
|
5
5
|
import md5 from 'md5';
|
|
6
6
|
import { BLOCKS } from '@contentful/rich-text-types';
|
|
7
|
-
import { create } from 'zustand';
|
|
7
|
+
import { create, useStore } from 'zustand';
|
|
8
8
|
import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd';
|
|
9
9
|
import { produce } from 'immer';
|
|
10
10
|
import '@contentful/rich-text-react-renderer';
|
|
@@ -126,14 +126,6 @@ const CF_STYLE_ATTRIBUTES = [
|
|
|
126
126
|
'cfTextBold',
|
|
127
127
|
'cfTextItalic',
|
|
128
128
|
'cfTextUnderline',
|
|
129
|
-
// For backwards compatibility
|
|
130
|
-
// we need to keep those in this constant array
|
|
131
|
-
// so that omit() in <VisualEditorBlock> and <CompositionBlock>
|
|
132
|
-
// can filter them out and not pass as props
|
|
133
|
-
'cfBackgroundImageScaling',
|
|
134
|
-
'cfBackgroundImageAlignment',
|
|
135
|
-
'cfBackgroundImageAlignmentVertical',
|
|
136
|
-
'cfBackgroundImageAlignmentHorizontal',
|
|
137
129
|
];
|
|
138
130
|
const EMPTY_CONTAINER_HEIGHT$1 = '80px';
|
|
139
131
|
const DEFAULT_IMAGE_WIDTH = '500px';
|
|
@@ -836,7 +828,13 @@ const ParameterDefinitionSchema$1 = z.object({
|
|
|
836
828
|
}),
|
|
837
829
|
})
|
|
838
830
|
.optional(),
|
|
839
|
-
contentTypes: z.
|
|
831
|
+
contentTypes: z.record(z.string(), z.object({
|
|
832
|
+
sys: z.object({
|
|
833
|
+
type: z.literal('Link'),
|
|
834
|
+
id: z.string(),
|
|
835
|
+
linkType: z.enum(['ContentType']),
|
|
836
|
+
}),
|
|
837
|
+
})),
|
|
840
838
|
passToNodes: z.array(PassToNodeSchema$1).optional(),
|
|
841
839
|
});
|
|
842
840
|
const ParameterDefinitionsSchema$1 = z.record(propertyKeySchema$1, ParameterDefinitionSchema$1);
|
|
@@ -856,7 +854,7 @@ const ComponentSettingsSchema$1 = z
|
|
|
856
854
|
variableDefinitions: ComponentVariablesSchema$1,
|
|
857
855
|
thumbnailId: z.enum(THUMBNAIL_IDS$1).optional(),
|
|
858
856
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
859
|
-
prebindingDefinitions: z.array(PrebindingDefinitionSchema$1).
|
|
857
|
+
prebindingDefinitions: z.array(PrebindingDefinitionSchema$1).max(1).optional(),
|
|
860
858
|
})
|
|
861
859
|
.strict();
|
|
862
860
|
z.object({
|
|
@@ -1099,6 +1097,24 @@ propertyName, resolveDesignTokens = true) => {
|
|
|
1099
1097
|
return valuesByBreakpoint;
|
|
1100
1098
|
}
|
|
1101
1099
|
};
|
|
1100
|
+
/** Overwrites the default value breakpoint by breakpoint. If a breakpoint
|
|
1101
|
+
* is not overwritten, it will fall back to the default. */
|
|
1102
|
+
function mergeDesignValuesByBreakpoint(defaultValue, overwriteValue) {
|
|
1103
|
+
if (!defaultValue || !overwriteValue) {
|
|
1104
|
+
return defaultValue ?? overwriteValue;
|
|
1105
|
+
}
|
|
1106
|
+
const mergedValuesByBreakpoint = { ...defaultValue.valuesByBreakpoint };
|
|
1107
|
+
for (const [breakpointId, value] of Object.entries(overwriteValue.valuesByBreakpoint)) {
|
|
1108
|
+
if (!isValidBreakpointValue(value)) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
mergedValuesByBreakpoint[breakpointId] = value;
|
|
1112
|
+
}
|
|
1113
|
+
return {
|
|
1114
|
+
type: 'DesignValue',
|
|
1115
|
+
valuesByBreakpoint: mergedValuesByBreakpoint,
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1102
1118
|
|
|
1103
1119
|
const CF_DEBUG_KEY$1 = 'cf_debug';
|
|
1104
1120
|
/**
|
|
@@ -1220,11 +1236,19 @@ const getElementCoordinates = (element) => {
|
|
|
1220
1236
|
});
|
|
1221
1237
|
};
|
|
1222
1238
|
|
|
1223
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1224
1239
|
const isLinkToAsset = (variable) => {
|
|
1225
|
-
if (
|
|
1240
|
+
if (variable === null || typeof variable !== 'object')
|
|
1241
|
+
return false;
|
|
1242
|
+
// The `'prop' in` pattern is informing TypeScript of the object shape, no need to cast `as`.
|
|
1243
|
+
if (!('sys' in variable))
|
|
1244
|
+
return false;
|
|
1245
|
+
if (variable.sys === null || typeof variable.sys !== 'object')
|
|
1246
|
+
return false;
|
|
1247
|
+
if (!('linkType' in variable.sys))
|
|
1226
1248
|
return false;
|
|
1227
|
-
if (
|
|
1249
|
+
if (!('id' in variable.sys))
|
|
1250
|
+
return false;
|
|
1251
|
+
if (!('type' in variable.sys))
|
|
1228
1252
|
return false;
|
|
1229
1253
|
return (variable.sys?.linkType === 'Asset' &&
|
|
1230
1254
|
typeof variable.sys?.id === 'string' &&
|
|
@@ -1232,20 +1256,20 @@ const isLinkToAsset = (variable) => {
|
|
|
1232
1256
|
variable.sys?.type === 'Link');
|
|
1233
1257
|
};
|
|
1234
1258
|
|
|
1235
|
-
const isLink = (maybeLink) => {
|
|
1259
|
+
const isLink$1 = (maybeLink) => {
|
|
1236
1260
|
if (maybeLink === null)
|
|
1237
1261
|
return false;
|
|
1238
1262
|
if (typeof maybeLink !== 'object')
|
|
1239
1263
|
return false;
|
|
1240
1264
|
const link = maybeLink;
|
|
1241
|
-
return Boolean(link.sys?.id) && link.sys?.type === 'Link';
|
|
1265
|
+
return Boolean(link.sys?.id) && link.sys?.type === 'Link' && Boolean(link.sys?.linkType);
|
|
1242
1266
|
};
|
|
1243
1267
|
|
|
1244
1268
|
/**
|
|
1245
1269
|
* This module encapsulates format of the path to a deep reference.
|
|
1246
1270
|
*/
|
|
1247
|
-
const parseDataSourcePathIntoFieldset = (path) => {
|
|
1248
|
-
const parsedPath = parseDeepPath(path);
|
|
1271
|
+
const parseDataSourcePathIntoFieldset$1 = (path) => {
|
|
1272
|
+
const parsedPath = parseDeepPath$1(path);
|
|
1249
1273
|
if (null === parsedPath) {
|
|
1250
1274
|
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
1251
1275
|
}
|
|
@@ -1258,7 +1282,7 @@ const parseDataSourcePathIntoFieldset = (path) => {
|
|
|
1258
1282
|
* @returns
|
|
1259
1283
|
*/
|
|
1260
1284
|
const parseDataSourcePathWithL1DeepBindings = (path) => {
|
|
1261
|
-
const parsedPath = parseDeepPath(path);
|
|
1285
|
+
const parsedPath = parseDeepPath$1(path);
|
|
1262
1286
|
if (null === parsedPath) {
|
|
1263
1287
|
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
1264
1288
|
}
|
|
@@ -1275,14 +1299,14 @@ const parseDataSourcePathWithL1DeepBindings = (path) => {
|
|
|
1275
1299
|
* - /6J8eA60yXwdm5eyUh9fX6/fields/mainStory/~locale
|
|
1276
1300
|
* @returns
|
|
1277
1301
|
*/
|
|
1278
|
-
const isDeepPath = (deepPathCandidate) => {
|
|
1279
|
-
const deepPathParsed = parseDeepPath(deepPathCandidate);
|
|
1302
|
+
const isDeepPath$1 = (deepPathCandidate) => {
|
|
1303
|
+
const deepPathParsed = parseDeepPath$1(deepPathCandidate);
|
|
1280
1304
|
if (!deepPathParsed) {
|
|
1281
1305
|
return false;
|
|
1282
1306
|
}
|
|
1283
1307
|
return deepPathParsed.fields.length > 1;
|
|
1284
1308
|
};
|
|
1285
|
-
const parseDeepPath = (deepPathCandidate) => {
|
|
1309
|
+
const parseDeepPath$1 = (deepPathCandidate) => {
|
|
1286
1310
|
// ALGORITHM:
|
|
1287
1311
|
// We start with deep path in form:
|
|
1288
1312
|
// /uuid123/fields/mainStory/~locale/fields/cover/~locale/fields/title/~locale
|
|
@@ -1308,7 +1332,7 @@ const parseDeepPath = (deepPathCandidate) => {
|
|
|
1308
1332
|
return /^fields\/[^/^~]+\/~locale$/.test(fieldChunk.join('/'));
|
|
1309
1333
|
};
|
|
1310
1334
|
const deepPathSegments = deepPathCandidate.split('/');
|
|
1311
|
-
const chunks = chunkSegments(deepPathSegments, { startNextChunkOnElementEqualTo: 'fields' });
|
|
1335
|
+
const chunks = chunkSegments$1(deepPathSegments, { startNextChunkOnElementEqualTo: 'fields' });
|
|
1312
1336
|
if (chunks.length <= 1) {
|
|
1313
1337
|
return null; // malformed path, even regular paths have at least 2 chunks
|
|
1314
1338
|
}
|
|
@@ -1328,7 +1352,7 @@ const parseDeepPath = (deepPathCandidate) => {
|
|
|
1328
1352
|
fields: fieldChunks.map((fieldChunk) => fieldChunk[1]), // pick only fieldName eg. from ['fields','mainStory', '~locale'] we pick `mainStory`
|
|
1329
1353
|
};
|
|
1330
1354
|
};
|
|
1331
|
-
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
1355
|
+
const chunkSegments$1 = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
1332
1356
|
const chunks = [];
|
|
1333
1357
|
let currentChunk = [];
|
|
1334
1358
|
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
@@ -1429,11 +1453,6 @@ const transformVisibility = (value) => {
|
|
|
1429
1453
|
// Don't explicitly set anything when visible to not overwrite values like `grid` or `flex`.
|
|
1430
1454
|
return {};
|
|
1431
1455
|
};
|
|
1432
|
-
// TODO: Remove in next major version v2 since the change is 17 months old
|
|
1433
|
-
// Keep this for backwards compatibility - deleting this would be a breaking change
|
|
1434
|
-
// because existing components on a users experience will have the width value as fill
|
|
1435
|
-
// rather than 100%
|
|
1436
|
-
const transformFill = (value) => (value === 'fill' ? '100%' : value);
|
|
1437
1456
|
const transformGridColumn = (span) => {
|
|
1438
1457
|
if (!span) {
|
|
1439
1458
|
return {};
|
|
@@ -1478,34 +1497,6 @@ const transformBackgroundImage = (cfBackgroundImageUrl, cfBackgroundImageOptions
|
|
|
1478
1497
|
return;
|
|
1479
1498
|
}
|
|
1480
1499
|
let [horizontalAlignment, verticalAlignment] = alignment.trim().split(/\s+/, 2);
|
|
1481
|
-
// Special case for handling single values
|
|
1482
|
-
// for backwards compatibility with single values 'right','left', 'center', 'top','bottom'
|
|
1483
|
-
if (horizontalAlignment && !verticalAlignment) {
|
|
1484
|
-
const singleValue = horizontalAlignment;
|
|
1485
|
-
switch (singleValue) {
|
|
1486
|
-
case 'left':
|
|
1487
|
-
horizontalAlignment = 'left';
|
|
1488
|
-
verticalAlignment = 'center';
|
|
1489
|
-
break;
|
|
1490
|
-
case 'right':
|
|
1491
|
-
horizontalAlignment = 'right';
|
|
1492
|
-
verticalAlignment = 'center';
|
|
1493
|
-
break;
|
|
1494
|
-
case 'center':
|
|
1495
|
-
horizontalAlignment = 'center';
|
|
1496
|
-
verticalAlignment = 'center';
|
|
1497
|
-
break;
|
|
1498
|
-
case 'top':
|
|
1499
|
-
horizontalAlignment = 'center';
|
|
1500
|
-
verticalAlignment = 'top';
|
|
1501
|
-
break;
|
|
1502
|
-
case 'bottom':
|
|
1503
|
-
horizontalAlignment = 'center';
|
|
1504
|
-
verticalAlignment = 'bottom';
|
|
1505
|
-
break;
|
|
1506
|
-
// just fall down to the normal validation logic for horiz and vert
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
1500
|
const isHorizontalValid = ['left', 'right', 'center'].includes(horizontalAlignment);
|
|
1510
1501
|
const isVerticalValid = ['top', 'bottom', 'center'].includes(verticalAlignment);
|
|
1511
1502
|
horizontalAlignment = isHorizontalValid ? horizontalAlignment : 'left';
|
|
@@ -1590,8 +1581,8 @@ const buildCfStyles = (values) => {
|
|
|
1590
1581
|
margin: values.cfMargin,
|
|
1591
1582
|
padding: values.cfPadding,
|
|
1592
1583
|
backgroundColor: values.cfBackgroundColor,
|
|
1593
|
-
width:
|
|
1594
|
-
height:
|
|
1584
|
+
width: values.cfWidth || values.cfImageOptions?.width,
|
|
1585
|
+
height: values.cfHeight || values.cfImageOptions?.height,
|
|
1595
1586
|
maxWidth: values.cfMaxWidth,
|
|
1596
1587
|
...transformGridColumn(values.cfColumnSpan),
|
|
1597
1588
|
...transformBorderStyle(values.cfBorder),
|
|
@@ -1697,13 +1688,13 @@ const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', fo
|
|
|
1697
1688
|
};
|
|
1698
1689
|
|
|
1699
1690
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1700
|
-
function get(obj, path) {
|
|
1691
|
+
function get$1(obj, path) {
|
|
1701
1692
|
if (!path.length) {
|
|
1702
1693
|
return obj;
|
|
1703
1694
|
}
|
|
1704
1695
|
try {
|
|
1705
1696
|
const [currentPath, ...nextPath] = path;
|
|
1706
|
-
return get(obj[currentPath], nextPath);
|
|
1697
|
+
return get$1(obj[currentPath], nextPath);
|
|
1707
1698
|
}
|
|
1708
1699
|
catch (err) {
|
|
1709
1700
|
return undefined;
|
|
@@ -1711,7 +1702,7 @@ function get(obj, path) {
|
|
|
1711
1702
|
}
|
|
1712
1703
|
|
|
1713
1704
|
const getBoundValue = (entryOrAsset, path) => {
|
|
1714
|
-
const value = get(entryOrAsset, path.split('/').slice(2, -1));
|
|
1705
|
+
const value = get$1(entryOrAsset, path.split('/').slice(2, -1));
|
|
1715
1706
|
return value && typeof value == 'object' && value.url
|
|
1716
1707
|
? value.url
|
|
1717
1708
|
: value;
|
|
@@ -1741,7 +1732,9 @@ const transformRichText = (entryOrAsset, entityStore, path) => {
|
|
|
1741
1732
|
}
|
|
1742
1733
|
if (typeof value === 'object' && value.nodeType === BLOCKS.DOCUMENT) {
|
|
1743
1734
|
// resolve any links to assets/entries/hyperlinks
|
|
1744
|
-
|
|
1735
|
+
// we need to clone, as we want to keep the original Entity in the EntityStore intact,
|
|
1736
|
+
// and resolveLinks() is mutating the node object.
|
|
1737
|
+
const richTextDocument = structuredClone(value);
|
|
1745
1738
|
resolveLinks(richTextDocument, entityStore);
|
|
1746
1739
|
return richTextDocument;
|
|
1747
1740
|
}
|
|
@@ -1796,7 +1789,7 @@ const transformMedia = (asset, variables, resolveDesignValue, variableName, path
|
|
|
1796
1789
|
let value;
|
|
1797
1790
|
// If it is not a deep path and not pointing to the file of the asset,
|
|
1798
1791
|
// it is just pointing to a normal field and therefore we just resolve the value as normal field
|
|
1799
|
-
if (!isDeepPath(path) && !lastPathNamedSegmentEq(path, 'file')) {
|
|
1792
|
+
if (!isDeepPath$1(path) && !lastPathNamedSegmentEq(path, 'file')) {
|
|
1800
1793
|
return getBoundValue(asset, path);
|
|
1801
1794
|
}
|
|
1802
1795
|
//TODO: this will be better served by injectable type transformers instead of if statement
|
|
@@ -1854,104 +1847,97 @@ const transformMedia = (asset, variables, resolveDesignValue, variableName, path
|
|
|
1854
1847
|
}
|
|
1855
1848
|
return asset.fields.file?.url;
|
|
1856
1849
|
};
|
|
1857
|
-
|
|
1858
|
-
const isAsset = (value) => {
|
|
1850
|
+
const isEntry$1 = (value) => {
|
|
1859
1851
|
return (null !== value &&
|
|
1860
1852
|
typeof value === 'object' &&
|
|
1861
1853
|
'sys' in value &&
|
|
1862
|
-
value.sys?.type === '
|
|
1854
|
+
value.sys?.type === 'Entry');
|
|
1863
1855
|
};
|
|
1864
|
-
const
|
|
1856
|
+
const isAsset$1 = (value) => {
|
|
1865
1857
|
return (null !== value &&
|
|
1866
1858
|
typeof value === 'object' &&
|
|
1867
1859
|
'sys' in value &&
|
|
1868
|
-
value.sys?.type === '
|
|
1860
|
+
value.sys?.type === 'Asset');
|
|
1869
1861
|
};
|
|
1870
1862
|
|
|
1871
1863
|
function getResolvedEntryFromLink(entryOrAsset, path, entityStore) {
|
|
1872
|
-
if (isAsset(entryOrAsset)) {
|
|
1864
|
+
if (isAsset$1(entryOrAsset)) {
|
|
1873
1865
|
return entryOrAsset;
|
|
1874
1866
|
}
|
|
1875
|
-
else if (!isEntry(entryOrAsset)) {
|
|
1867
|
+
else if (!isEntry$1(entryOrAsset)) {
|
|
1876
1868
|
throw new Error(`Expected an Entry or Asset, but got: ${JSON.stringify(entryOrAsset)}`);
|
|
1877
1869
|
}
|
|
1878
|
-
const
|
|
1870
|
+
const fieldName = path.split('/').slice(2, -1);
|
|
1871
|
+
const value = get$1(entryOrAsset, fieldName);
|
|
1879
1872
|
let resolvedEntity;
|
|
1880
|
-
if (isAsset(value) || isEntry(value)) {
|
|
1873
|
+
if (isAsset$1(value) || isEntry$1(value)) {
|
|
1881
1874
|
// In some cases, reference fields are already resolved
|
|
1882
1875
|
resolvedEntity = value;
|
|
1883
1876
|
}
|
|
1884
1877
|
else if (value?.sys.type === 'Link') {
|
|
1885
1878
|
// Look up the reference in the entity store
|
|
1886
1879
|
resolvedEntity = entityStore.getEntityFromLink(value);
|
|
1887
|
-
if (!resolvedEntity) {
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
1890
1880
|
}
|
|
1891
1881
|
else {
|
|
1892
|
-
console.warn(`
|
|
1882
|
+
console.warn(`When attempting to follow link in field '${fieldName}' of entity, the value is expected to be a link, but got: ${JSON.stringify(value)}`, { entity: entryOrAsset });
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
// no need to make structuredClone(entityStore.getEntityFromLink(value)) because
|
|
1886
|
+
// we provide component with the original Object.frozen object of the entity.
|
|
1887
|
+
// As we don't resolve L3 and don't mutate the entity before returning anymore,
|
|
1888
|
+
// we don't need to make a copy of the entity. And even provide better referential integrity
|
|
1889
|
+
// for the component for the same entity.
|
|
1890
|
+
if (!resolvedEntity) {
|
|
1893
1891
|
return;
|
|
1894
1892
|
}
|
|
1895
|
-
//resolve any embedded links - we currently only support 2 levels deep
|
|
1896
|
-
const fields = resolvedEntity.fields || {};
|
|
1897
|
-
Object.entries(fields).forEach(([fieldKey, field]) => {
|
|
1898
|
-
if (field && field.sys?.type === 'Link') {
|
|
1899
|
-
const entity = entityStore.getEntityFromLink(field);
|
|
1900
|
-
if (entity) {
|
|
1901
|
-
resolvedEntity.fields[fieldKey] = entity;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
else if (field && Array.isArray(field)) {
|
|
1905
|
-
resolvedEntity.fields[fieldKey] = field.map((innerField) => {
|
|
1906
|
-
if (innerField && innerField.sys?.type === 'Link') {
|
|
1907
|
-
const entity = entityStore.getEntityFromLink(innerField);
|
|
1908
|
-
if (entity) {
|
|
1909
|
-
return entity;
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
return innerField;
|
|
1913
|
-
});
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1916
1893
|
return resolvedEntity;
|
|
1917
1894
|
}
|
|
1918
1895
|
|
|
1896
|
+
const excludeUndefined = (value) => {
|
|
1897
|
+
return value !== undefined;
|
|
1898
|
+
};
|
|
1919
1899
|
function getArrayValue(entryOrAsset, path, entityStore) {
|
|
1900
|
+
// NOTE: Not sure if we need this if-statement,
|
|
1901
|
+
// as it is NOT possible to bind to Array variable an Asset
|
|
1902
|
+
// (as Assets don't have multi-reference fields) unless it's a degenerate case.
|
|
1920
1903
|
if (entryOrAsset.sys.type === 'Asset') {
|
|
1921
1904
|
return entryOrAsset;
|
|
1922
1905
|
}
|
|
1923
|
-
const
|
|
1906
|
+
const fieldName = path.split('/').slice(2, -1);
|
|
1907
|
+
const arrayValue = get$1(entryOrAsset, fieldName);
|
|
1924
1908
|
if (!isArray(arrayValue)) {
|
|
1925
|
-
console.warn(`Expected
|
|
1909
|
+
console.warn(`A field '${fieldName}' of an entity was bound to an Array variable. Expected value of that field to be an array, but got: ${JSON.stringify(arrayValue)}`, { entity: entryOrAsset });
|
|
1926
1910
|
return;
|
|
1927
1911
|
}
|
|
1928
|
-
const result = arrayValue
|
|
1912
|
+
const result = arrayValue
|
|
1913
|
+
.map((value) => {
|
|
1929
1914
|
if (typeof value === 'string') {
|
|
1930
|
-
return value;
|
|
1915
|
+
return value; // handles case where Text array is bound (in [Content Model] tab of the platform, select Text and make it a list)
|
|
1931
1916
|
}
|
|
1932
1917
|
else if (value?.sys?.type === 'Link') {
|
|
1933
1918
|
const resolvedEntity = entityStore.getEntityFromLink(value);
|
|
1934
1919
|
if (!resolvedEntity) {
|
|
1920
|
+
// We return undefined, which means that entity wasn't availble in the Entity Store due to:
|
|
1921
|
+
// - because it's archived entity (and they normally wouldn't be sent to the Entity Store)
|
|
1922
|
+
// - bug where some entity wasn't added to the Entity Store
|
|
1923
|
+
// BTW, deleted entities shouldn't even be possible here as they require CT deletion first and that shouldn't allow us to load them at all)
|
|
1935
1924
|
return;
|
|
1936
1925
|
}
|
|
1937
|
-
//resolve any embedded links - we currently only support 2 levels deep
|
|
1938
|
-
const fields = resolvedEntity.fields || {};
|
|
1939
|
-
Object.entries(fields).forEach(([fieldKey, field]) => {
|
|
1940
|
-
if (field && field.sys?.type === 'Link') {
|
|
1941
|
-
const entity = entityStore.getEntityFromLink(field);
|
|
1942
|
-
if (entity) {
|
|
1943
|
-
resolvedEntity.fields[fieldKey] = entity;
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
});
|
|
1947
1926
|
return resolvedEntity;
|
|
1948
1927
|
}
|
|
1949
1928
|
else {
|
|
1950
1929
|
console.warn(`Expected value to be a string or Link, but got: ${JSON.stringify(value)}`);
|
|
1951
1930
|
return undefined;
|
|
1952
1931
|
}
|
|
1953
|
-
})
|
|
1954
|
-
|
|
1932
|
+
})
|
|
1933
|
+
.filter(excludeUndefined);
|
|
1934
|
+
// eg. imagine you have multi-referene field with 3 links to archived entries,
|
|
1935
|
+
// all of them will be undefined on previous step and will be filtered out
|
|
1936
|
+
// of resultWithoutUndefined. Instead of passing to component an empty array,
|
|
1937
|
+
// we pass undefined. This means that develloper making custom component
|
|
1938
|
+
// does not have to handle empty array case. But only undefiened, which signals:
|
|
1939
|
+
// user didn't bind anything; user bound to reference field which is unset; all references are archived
|
|
1940
|
+
return result.length > 0 ? result : undefined;
|
|
1955
1941
|
}
|
|
1956
1942
|
|
|
1957
1943
|
const transformBoundContentValue = (variables, entityStore, binding, resolveDesignValue, variableName, variableType, path) => {
|
|
@@ -2110,11 +2096,22 @@ const sendMessage = (eventType, data) => {
|
|
|
2110
2096
|
}, '*');
|
|
2111
2097
|
};
|
|
2112
2098
|
|
|
2099
|
+
function deepFreeze$1(obj) {
|
|
2100
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
2101
|
+
for (const name of propNames) {
|
|
2102
|
+
const value = obj[name];
|
|
2103
|
+
if (value && typeof value === 'object') {
|
|
2104
|
+
deepFreeze$1(value);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
return Object.freeze(obj);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2113
2110
|
/**
|
|
2114
2111
|
* Base Store for entities
|
|
2115
2112
|
* Can be extended for the different loading behaviours (editor, production, ..)
|
|
2116
2113
|
*/
|
|
2117
|
-
class EntityStoreBase {
|
|
2114
|
+
let EntityStoreBase$1 = class EntityStoreBase {
|
|
2118
2115
|
constructor({ entities, locale }) {
|
|
2119
2116
|
/* serialized */ this.entryMap = new Map();
|
|
2120
2117
|
/* serialized */ this.assetMap = new Map();
|
|
@@ -2130,11 +2127,11 @@ class EntityStoreBase {
|
|
|
2130
2127
|
this.addEntity(entity);
|
|
2131
2128
|
}
|
|
2132
2129
|
getEntryOrAsset(linkOrEntryOrAsset, path) {
|
|
2133
|
-
if (isDeepPath(path)) {
|
|
2130
|
+
if (isDeepPath$1(path)) {
|
|
2134
2131
|
return this.getDeepEntry(linkOrEntryOrAsset, path);
|
|
2135
2132
|
}
|
|
2136
2133
|
let entity;
|
|
2137
|
-
if (isLink(linkOrEntryOrAsset)) {
|
|
2134
|
+
if (isLink$1(linkOrEntryOrAsset)) {
|
|
2138
2135
|
const resolvedEntity = linkOrEntryOrAsset.sys.linkType === 'Entry'
|
|
2139
2136
|
? this.entryMap.get(linkOrEntryOrAsset.sys.id)
|
|
2140
2137
|
: this.assetMap.get(linkOrEntryOrAsset.sys.id);
|
|
@@ -2144,7 +2141,7 @@ class EntityStoreBase {
|
|
|
2144
2141
|
}
|
|
2145
2142
|
entity = resolvedEntity;
|
|
2146
2143
|
}
|
|
2147
|
-
else if (isAsset(linkOrEntryOrAsset) || isEntry(linkOrEntryOrAsset)) {
|
|
2144
|
+
else if (isAsset$1(linkOrEntryOrAsset) || isEntry$1(linkOrEntryOrAsset)) {
|
|
2148
2145
|
// We already have the complete entity in preview & delivery (resolved by the CMA client)
|
|
2149
2146
|
entity = linkOrEntryOrAsset;
|
|
2150
2147
|
}
|
|
@@ -2166,7 +2163,7 @@ class EntityStoreBase {
|
|
|
2166
2163
|
console.warn(`Unresolved entity reference: ${entityLink.sys.linkType} with ID ${entityLink.sys.id}`);
|
|
2167
2164
|
return;
|
|
2168
2165
|
}
|
|
2169
|
-
return get(entity, path);
|
|
2166
|
+
return get$1(entity, path);
|
|
2170
2167
|
}
|
|
2171
2168
|
getEntityFromLink(link) {
|
|
2172
2169
|
const resolvedEntity = link.sys.linkType === 'Entry'
|
|
@@ -2178,6 +2175,22 @@ class EntityStoreBase {
|
|
|
2178
2175
|
}
|
|
2179
2176
|
return resolvedEntity;
|
|
2180
2177
|
}
|
|
2178
|
+
getAssetById(assetId) {
|
|
2179
|
+
const asset = this.assetMap.get(assetId);
|
|
2180
|
+
if (!asset) {
|
|
2181
|
+
console.warn(`Asset with ID "${assetId}" is not found in the store`);
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
return asset;
|
|
2185
|
+
}
|
|
2186
|
+
getEntryById(entryId) {
|
|
2187
|
+
const entry = this.entryMap.get(entryId);
|
|
2188
|
+
if (!entry) {
|
|
2189
|
+
console.warn(`Entry with ID "${entryId}" is not found in the store`);
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
return entry;
|
|
2193
|
+
}
|
|
2181
2194
|
getEntitiesFromMap(type, ids) {
|
|
2182
2195
|
const resolved = [];
|
|
2183
2196
|
const missing = [];
|
|
@@ -2196,11 +2209,13 @@ class EntityStoreBase {
|
|
|
2196
2209
|
};
|
|
2197
2210
|
}
|
|
2198
2211
|
addEntity(entity) {
|
|
2199
|
-
if (isAsset(entity)) {
|
|
2200
|
-
|
|
2212
|
+
if (isAsset$1(entity)) {
|
|
2213
|
+
// cloned and frozen
|
|
2214
|
+
this.assetMap.set(entity.sys.id, deepFreeze$1(structuredClone(entity)));
|
|
2201
2215
|
}
|
|
2202
|
-
else if (isEntry(entity)) {
|
|
2203
|
-
|
|
2216
|
+
else if (isEntry$1(entity)) {
|
|
2217
|
+
// cloned and frozen
|
|
2218
|
+
this.entryMap.set(entity.sys.id, deepFreeze$1(structuredClone(entity)));
|
|
2204
2219
|
}
|
|
2205
2220
|
else {
|
|
2206
2221
|
throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
|
|
@@ -2253,7 +2268,7 @@ class EntityStoreBase {
|
|
|
2253
2268
|
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
2254
2269
|
break;
|
|
2255
2270
|
}
|
|
2256
|
-
const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
|
|
2271
|
+
const fieldValue = get$1(entityToResolveFieldsFrom, ['fields', field]);
|
|
2257
2272
|
if (undefined === fieldValue) {
|
|
2258
2273
|
return {
|
|
2259
2274
|
resolvedFieldset,
|
|
@@ -2261,7 +2276,7 @@ class EntityStoreBase {
|
|
|
2261
2276
|
reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
|
|
2262
2277
|
};
|
|
2263
2278
|
}
|
|
2264
|
-
else if (isLink(fieldValue)) {
|
|
2279
|
+
else if (isLink$1(fieldValue)) {
|
|
2265
2280
|
const entity = this.getEntityFromLink(fieldValue);
|
|
2266
2281
|
if (entity === undefined) {
|
|
2267
2282
|
return {
|
|
@@ -2273,7 +2288,7 @@ class EntityStoreBase {
|
|
|
2273
2288
|
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
2274
2289
|
entityToResolveFieldsFrom = entity; // we move up
|
|
2275
2290
|
}
|
|
2276
|
-
else if (isAsset(fieldValue) || isEntry(fieldValue)) {
|
|
2291
|
+
else if (isAsset$1(fieldValue) || isEntry$1(fieldValue)) {
|
|
2277
2292
|
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
2278
2293
|
entityToResolveFieldsFrom = fieldValue; // we move up
|
|
2279
2294
|
}
|
|
@@ -2290,13 +2305,13 @@ class EntityStoreBase {
|
|
|
2290
2305
|
isFullyResolved: true,
|
|
2291
2306
|
};
|
|
2292
2307
|
};
|
|
2293
|
-
const headEntity = isLink(linkOrEntryOrAsset)
|
|
2308
|
+
const headEntity = isLink$1(linkOrEntryOrAsset)
|
|
2294
2309
|
? this.getEntityFromLink(linkOrEntryOrAsset)
|
|
2295
2310
|
: linkOrEntryOrAsset;
|
|
2296
2311
|
if (undefined === headEntity) {
|
|
2297
2312
|
return;
|
|
2298
2313
|
}
|
|
2299
|
-
const unresolvedFieldset = parseDataSourcePathIntoFieldset(path);
|
|
2314
|
+
const unresolvedFieldset = parseDataSourcePathIntoFieldset$1(path);
|
|
2300
2315
|
// The purpose here is to take this intermediate representation of the deep-path
|
|
2301
2316
|
// and to follow the links to the leaf-entity and field
|
|
2302
2317
|
// in case we can't follow till the end, we should signal that there was null-reference in the path
|
|
@@ -2322,13 +2337,13 @@ class EntityStoreBase {
|
|
|
2322
2337
|
locale: this.locale,
|
|
2323
2338
|
};
|
|
2324
2339
|
}
|
|
2325
|
-
}
|
|
2340
|
+
};
|
|
2326
2341
|
|
|
2327
2342
|
/**
|
|
2328
2343
|
* EntityStore which resolves entries and assets from the editor
|
|
2329
2344
|
* over the sendMessage and subscribe functions.
|
|
2330
2345
|
*/
|
|
2331
|
-
class EditorEntityStore extends EntityStoreBase {
|
|
2346
|
+
class EditorEntityStore extends EntityStoreBase$1 {
|
|
2332
2347
|
constructor({ entities, locale, sendMessage, subscribe, timeoutDuration = 3000, }) {
|
|
2333
2348
|
super({ entities, locale });
|
|
2334
2349
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -2465,7 +2480,6 @@ class EditorModeEntityStore extends EditorEntityStore {
|
|
|
2465
2480
|
};
|
|
2466
2481
|
};
|
|
2467
2482
|
super({ entities, sendMessage, subscribe, locale, timeoutDuration: REQUEST_TIMEOUT });
|
|
2468
|
-
this.locale = locale;
|
|
2469
2483
|
}
|
|
2470
2484
|
/**
|
|
2471
2485
|
* This function collects and returns the list of requested entries and assets. Additionally, it checks
|
|
@@ -2495,11 +2509,91 @@ class EditorModeEntityStore extends EditorEntityStore {
|
|
|
2495
2509
|
if (!entity) {
|
|
2496
2510
|
return;
|
|
2497
2511
|
}
|
|
2498
|
-
const fieldValue = get(entity, path);
|
|
2512
|
+
const fieldValue = get$1(entity, path);
|
|
2499
2513
|
return transformAssetFileToUrl(fieldValue);
|
|
2500
2514
|
}
|
|
2501
2515
|
}
|
|
2502
2516
|
|
|
2517
|
+
let UninitializedEntityStore$1 = class UninitializedEntityStore extends EntityStoreBase$1 {
|
|
2518
|
+
constructor() {
|
|
2519
|
+
super({ entities: [], locale: 'uninitialized-locale-in-uninitialized-entity-store' });
|
|
2520
|
+
}
|
|
2521
|
+
};
|
|
2522
|
+
|
|
2523
|
+
const inMemoryEntitiesStore = create((set, get) => ({
|
|
2524
|
+
// The UninitializedEntityStore is a placeholder instance and is here to highlight the
|
|
2525
|
+
// // fact that it's not used by anything until during loading lifecycle it'sreplaced by real entity store:
|
|
2526
|
+
// - in Preview+Delivery mode: right after we fetch Expereince and it entities
|
|
2527
|
+
// - in EDITOR (VisualEditor) mode: right after the VisualEditor is async imported and initialize event happens
|
|
2528
|
+
entityStore: new UninitializedEntityStore$1(),
|
|
2529
|
+
areEntitiesFetched: false,
|
|
2530
|
+
setEntitiesFetched(fetched) {
|
|
2531
|
+
set({ areEntitiesFetched: fetched });
|
|
2532
|
+
},
|
|
2533
|
+
resolveAssetById(assetId) {
|
|
2534
|
+
if (!assetId)
|
|
2535
|
+
return undefined;
|
|
2536
|
+
const { entityStore } = get();
|
|
2537
|
+
return entityStore.getAssetById(assetId);
|
|
2538
|
+
},
|
|
2539
|
+
resolveEntryById(entryId) {
|
|
2540
|
+
if (!entryId)
|
|
2541
|
+
return undefined;
|
|
2542
|
+
const { entityStore } = get();
|
|
2543
|
+
return entityStore.getEntryById(entryId);
|
|
2544
|
+
},
|
|
2545
|
+
resolveEntity(link) {
|
|
2546
|
+
if (!link)
|
|
2547
|
+
return undefined;
|
|
2548
|
+
const { entityStore } = get();
|
|
2549
|
+
return entityStore.getEntityFromLink(link);
|
|
2550
|
+
},
|
|
2551
|
+
resetEntityStore(entityStore) {
|
|
2552
|
+
set({
|
|
2553
|
+
entityStore,
|
|
2554
|
+
areEntitiesFetched: false,
|
|
2555
|
+
});
|
|
2556
|
+
},
|
|
2557
|
+
}));
|
|
2558
|
+
|
|
2559
|
+
function maybeResolveLink(maybeLink) {
|
|
2560
|
+
if (!isLink$1(maybeLink)) {
|
|
2561
|
+
console.warn('maybeResolveLink function must receive Link shape. Provided argument does not match the Link shape: ', maybeLink);
|
|
2562
|
+
return undefined;
|
|
2563
|
+
}
|
|
2564
|
+
return inMemoryEntitiesStore.getState().resolveEntity(maybeLink);
|
|
2565
|
+
}
|
|
2566
|
+
function maybeResolveByAssetId(assetId) {
|
|
2567
|
+
return inMemoryEntitiesStore.getState().resolveAssetById(assetId);
|
|
2568
|
+
}
|
|
2569
|
+
function maybeResolveByEntryId(entryId) {
|
|
2570
|
+
return inMemoryEntitiesStore.getState().resolveEntryById(entryId);
|
|
2571
|
+
}
|
|
2572
|
+
function hasEntry(entryId) {
|
|
2573
|
+
return Boolean(maybeResolveByEntryId(entryId));
|
|
2574
|
+
}
|
|
2575
|
+
function hasAsset(assetId) {
|
|
2576
|
+
return Boolean(maybeResolveByAssetId(assetId));
|
|
2577
|
+
}
|
|
2578
|
+
function addEntities(entities) {
|
|
2579
|
+
if (!Array.isArray(entities) || entities.length === 0) {
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
const { entityStore } = inMemoryEntitiesStore.getState();
|
|
2583
|
+
const definedEntities = entities.filter(Boolean);
|
|
2584
|
+
for (const entity of definedEntities) {
|
|
2585
|
+
entityStore.updateEntity(entity);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
const inMemoryEntities = {
|
|
2589
|
+
maybeResolveLink,
|
|
2590
|
+
maybeResolveByAssetId,
|
|
2591
|
+
maybeResolveByEntryId,
|
|
2592
|
+
hasEntry,
|
|
2593
|
+
hasAsset,
|
|
2594
|
+
addEntities,
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2503
2597
|
var VisualEditorMode$1;
|
|
2504
2598
|
(function (VisualEditorMode) {
|
|
2505
2599
|
VisualEditorMode["LazyLoad"] = "lazyLoad";
|
|
@@ -2529,7 +2623,7 @@ class DeepReference {
|
|
|
2529
2623
|
// field references nothing (or even field doesn't exist)
|
|
2530
2624
|
return undefined;
|
|
2531
2625
|
}
|
|
2532
|
-
if (!isLink(maybeReferentLink)) {
|
|
2626
|
+
if (!isLink$1(maybeReferentLink)) {
|
|
2533
2627
|
// Scenario of "impostor referent", where one of the deepPath's segments is not a Link but some other type
|
|
2534
2628
|
// Under normal circumstance we expect field to be a Link, but it could be an "impostor"
|
|
2535
2629
|
// eg. `Text` or `Number` or anything like that; could be due to CT changes or manual path creation via CMA
|
|
@@ -2549,7 +2643,7 @@ function gatherDeepReferencesFromTree(startingNode, dataSource) {
|
|
|
2549
2643
|
for (const [, variableMapping] of Object.entries(node.data.props)) {
|
|
2550
2644
|
if (variableMapping.type !== 'BoundValue')
|
|
2551
2645
|
continue;
|
|
2552
|
-
if (!isDeepPath(variableMapping.path))
|
|
2646
|
+
if (!isDeepPath$1(variableMapping.path))
|
|
2553
2647
|
continue;
|
|
2554
2648
|
deepReferences.push(DeepReference.from({
|
|
2555
2649
|
path: variableMapping.path,
|
|
@@ -3158,6 +3252,26 @@ const useTreeStore = create((set, get) => ({
|
|
|
3158
3252
|
reparentChildNode(sourceIndex, destinationIndex, sourceParentId, destinationParentId, state.tree.root);
|
|
3159
3253
|
}));
|
|
3160
3254
|
},
|
|
3255
|
+
// breadth first search
|
|
3256
|
+
findNodeById(nodeId) {
|
|
3257
|
+
if (!nodeId) {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
const rootNode = get().tree.root;
|
|
3261
|
+
const visitedNodeIds = [];
|
|
3262
|
+
const queue = [];
|
|
3263
|
+
let currentNode = rootNode;
|
|
3264
|
+
queue.push(currentNode);
|
|
3265
|
+
while (queue.length) {
|
|
3266
|
+
currentNode = queue.shift();
|
|
3267
|
+
visitedNodeIds.push(currentNode.data.id);
|
|
3268
|
+
if (currentNode.data.id === nodeId) {
|
|
3269
|
+
return currentNode;
|
|
3270
|
+
}
|
|
3271
|
+
queue.push(...currentNode.children);
|
|
3272
|
+
}
|
|
3273
|
+
return null;
|
|
3274
|
+
},
|
|
3161
3275
|
}));
|
|
3162
3276
|
const hasBreakpointDiffs = (currentTree, newTree) => {
|
|
3163
3277
|
const currentBreakpoints = currentTree?.root?.data?.breakpoints ?? [];
|
|
@@ -3706,7 +3820,13 @@ const ParameterDefinitionSchema = z.object({
|
|
|
3706
3820
|
}),
|
|
3707
3821
|
})
|
|
3708
3822
|
.optional(),
|
|
3709
|
-
contentTypes: z.
|
|
3823
|
+
contentTypes: z.record(z.string(), z.object({
|
|
3824
|
+
sys: z.object({
|
|
3825
|
+
type: z.literal('Link'),
|
|
3826
|
+
id: z.string(),
|
|
3827
|
+
linkType: z.enum(['ContentType']),
|
|
3828
|
+
}),
|
|
3829
|
+
})),
|
|
3710
3830
|
passToNodes: z.array(PassToNodeSchema).optional(),
|
|
3711
3831
|
});
|
|
3712
3832
|
const ParameterDefinitionsSchema = z.record(propertyKeySchema, ParameterDefinitionSchema);
|
|
@@ -3726,7 +3846,7 @@ const ComponentSettingsSchema = z
|
|
|
3726
3846
|
variableDefinitions: ComponentVariablesSchema,
|
|
3727
3847
|
thumbnailId: z.enum(THUMBNAIL_IDS).optional(),
|
|
3728
3848
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
3729
|
-
prebindingDefinitions: z.array(PrebindingDefinitionSchema).
|
|
3849
|
+
prebindingDefinitions: z.array(PrebindingDefinitionSchema).max(1).optional(),
|
|
3730
3850
|
})
|
|
3731
3851
|
.strict();
|
|
3732
3852
|
z.object({
|
|
@@ -3932,124 +4052,520 @@ class DebugLogger {
|
|
|
3932
4052
|
DebugLogger.instance = null;
|
|
3933
4053
|
DebugLogger.getInstance();
|
|
3934
4054
|
|
|
3935
|
-
|
|
3936
|
-
(
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
styleInject(css_248z$2$1);
|
|
3943
|
-
|
|
3944
|
-
const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave, onMouseDown, onClick, flex, flexBasis, flexShrink, flexDirection, gap, justifyContent, justifyItems, justifySelf, alignItems, alignSelf, alignContent, order, flexWrap, flexGrow, className, cssStyles, ...props }, ref) => {
|
|
3945
|
-
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
3946
|
-
display: 'flex',
|
|
3947
|
-
flex,
|
|
3948
|
-
flexBasis,
|
|
3949
|
-
flexShrink,
|
|
3950
|
-
flexDirection,
|
|
3951
|
-
gap,
|
|
3952
|
-
justifyContent,
|
|
3953
|
-
justifyItems,
|
|
3954
|
-
justifySelf,
|
|
3955
|
-
alignItems,
|
|
3956
|
-
alignSelf,
|
|
3957
|
-
alignContent,
|
|
3958
|
-
order,
|
|
3959
|
-
flexWrap,
|
|
3960
|
-
flexGrow,
|
|
3961
|
-
...cssStyles,
|
|
3962
|
-
}, className: className, onMouseEnter: onMouseEnter, onMouseUp: onMouseUp, onMouseDown: onMouseDown, onMouseLeave: onMouseLeave, onClick: onClick, ...props }, children));
|
|
3963
|
-
});
|
|
3964
|
-
Flex.displayName = 'Flex';
|
|
3965
|
-
|
|
3966
|
-
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}[data-ctfl-zone-id=root] .cf-divider:before{bottom:-5px;content:\"\";left:-5px;pointer-events:all;position:absolute;right:-5px;top:-5px}";
|
|
3967
|
-
styleInject(css_248z$1$1);
|
|
3968
|
-
|
|
3969
|
-
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{display:flex;position:relative}.cf-single-column{pointer-events:all}.cf-single-column-wrapper:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.cf-single-column-label:after{content:\"Column\"}";
|
|
3970
|
-
styleInject(css_248z$9);
|
|
3971
|
-
|
|
3972
|
-
const ColumnWrapper = forwardRef((props, ref) => {
|
|
3973
|
-
return (React.createElement("div", { ref: ref, ...props, style: {
|
|
3974
|
-
...(props.style || {}),
|
|
3975
|
-
display: 'grid',
|
|
3976
|
-
gridTemplateColumns: 'repeat(12, [col-start] 1fr)',
|
|
3977
|
-
} }, props.children));
|
|
3978
|
-
});
|
|
3979
|
-
ColumnWrapper.displayName = 'ColumnWrapper';
|
|
3980
|
-
|
|
3981
|
-
const assemblyStyle = { display: 'contents' };
|
|
3982
|
-
// Feel free to do any magic as regards variable definitions for assemblies
|
|
3983
|
-
// Or if this isn't necessary by the time we figure that part out, we can bid this part farewell
|
|
3984
|
-
const Assembly = (props) => {
|
|
3985
|
-
if (props.editorMode) {
|
|
3986
|
-
const { node, dragProps, ...editorModeProps } = props;
|
|
3987
|
-
return props.renderDropzone(node, {
|
|
3988
|
-
...editorModeProps,
|
|
3989
|
-
['data-test-id']: 'contentful-assembly',
|
|
3990
|
-
className: props.className,
|
|
3991
|
-
dragProps,
|
|
3992
|
-
});
|
|
3993
|
-
}
|
|
3994
|
-
// Using a display contents so assembly content/children
|
|
3995
|
-
// can appear as if they are direct children of the div wrapper's parent
|
|
3996
|
-
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4055
|
+
const isLink = (maybeLink) => {
|
|
4056
|
+
if (maybeLink === null)
|
|
4057
|
+
return false;
|
|
4058
|
+
if (typeof maybeLink !== 'object')
|
|
4059
|
+
return false;
|
|
4060
|
+
const link = maybeLink;
|
|
4061
|
+
return Boolean(link.sys?.id) && link.sys?.type === 'Link' && Boolean(link.sys?.linkType);
|
|
3997
4062
|
};
|
|
3998
4063
|
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
console.debug(`[experiences-sdk-react] Resetting entity store because the locale changed to '${locale}'.`);
|
|
4007
|
-
const newEntityStore = new EditorModeEntityStore({ locale, entities });
|
|
4008
|
-
set({
|
|
4009
|
-
entityStore: newEntityStore,
|
|
4010
|
-
areEntitiesFetched: false,
|
|
4011
|
-
});
|
|
4012
|
-
return newEntityStore;
|
|
4013
|
-
},
|
|
4014
|
-
}));
|
|
4015
|
-
|
|
4016
|
-
class DragState {
|
|
4017
|
-
constructor() {
|
|
4018
|
-
this.isDragStartedOnParent = false;
|
|
4019
|
-
this.isDraggingItem = false;
|
|
4020
|
-
}
|
|
4021
|
-
get isDragging() {
|
|
4022
|
-
return this.isDraggingItem;
|
|
4023
|
-
}
|
|
4024
|
-
get isDraggingOnParent() {
|
|
4025
|
-
return this.isDragStartedOnParent;
|
|
4026
|
-
}
|
|
4027
|
-
updateIsDragging(isDraggingItem) {
|
|
4028
|
-
this.isDraggingItem = isDraggingItem;
|
|
4029
|
-
}
|
|
4030
|
-
updateIsDragStartedOnParent(isDragStartedOnParent) {
|
|
4031
|
-
this.isDragStartedOnParent = isDragStartedOnParent;
|
|
4032
|
-
}
|
|
4033
|
-
resetState() {
|
|
4034
|
-
this.isDraggingItem = false;
|
|
4035
|
-
this.isDragStartedOnParent = false;
|
|
4036
|
-
}
|
|
4037
|
-
}
|
|
4038
|
-
|
|
4039
|
-
class SimulateDnD extends DragState {
|
|
4040
|
-
constructor() {
|
|
4041
|
-
super();
|
|
4042
|
-
this.draggingElement = null;
|
|
4043
|
-
}
|
|
4044
|
-
setupDrag() {
|
|
4045
|
-
this.updateIsDragStartedOnParent(true);
|
|
4064
|
+
/**
|
|
4065
|
+
* This module encapsulates format of the path to a deep reference.
|
|
4066
|
+
*/
|
|
4067
|
+
const parseDataSourcePathIntoFieldset = (path) => {
|
|
4068
|
+
const parsedPath = parseDeepPath(path);
|
|
4069
|
+
if (null === parsedPath) {
|
|
4070
|
+
throw new Error(`Cannot parse path '${path}' as deep path`);
|
|
4046
4071
|
}
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4072
|
+
return parsedPath.fields.map((field) => [null, field, '~locale']);
|
|
4073
|
+
};
|
|
4074
|
+
/**
|
|
4075
|
+
* Detects if paths is valid deep-path, like:
|
|
4076
|
+
* - /gV6yKXp61hfYrR7rEyKxY/fields/mainStory/~locale/fields/cover/~locale/fields/title/~locale
|
|
4077
|
+
* or regular, like:
|
|
4078
|
+
* - /6J8eA60yXwdm5eyUh9fX6/fields/mainStory/~locale
|
|
4079
|
+
* @returns
|
|
4080
|
+
*/
|
|
4081
|
+
const isDeepPath = (deepPathCandidate) => {
|
|
4082
|
+
const deepPathParsed = parseDeepPath(deepPathCandidate);
|
|
4083
|
+
if (!deepPathParsed) {
|
|
4084
|
+
return false;
|
|
4051
4085
|
}
|
|
4052
|
-
|
|
4086
|
+
return deepPathParsed.fields.length > 1;
|
|
4087
|
+
};
|
|
4088
|
+
const parseDeepPath = (deepPathCandidate) => {
|
|
4089
|
+
// ALGORITHM:
|
|
4090
|
+
// We start with deep path in form:
|
|
4091
|
+
// /uuid123/fields/mainStory/~locale/fields/cover/~locale/fields/title/~locale
|
|
4092
|
+
// First turn string into array of segments
|
|
4093
|
+
// ['', 'uuid123', 'fields', 'mainStory', '~locale', 'fields', 'cover', '~locale', 'fields', 'title', '~locale']
|
|
4094
|
+
// Then group segments into intermediate represenatation - chunks, where each non-initial chunk starts with 'fields'
|
|
4095
|
+
// [
|
|
4096
|
+
// [ "", "uuid123" ],
|
|
4097
|
+
// [ "fields", "mainStory", "~locale" ],
|
|
4098
|
+
// [ "fields", "cover", "~locale" ],
|
|
4099
|
+
// [ "fields", "title", "~locale" ]
|
|
4100
|
+
// ]
|
|
4101
|
+
// Then check "initial" chunk for corretness
|
|
4102
|
+
// Then check all "field-leading" chunks for correctness
|
|
4103
|
+
const isValidInitialChunk = (initialChunk) => {
|
|
4104
|
+
// must have start with '' and have at least 2 segments, second non-empty
|
|
4105
|
+
// eg. /-_432uuid123123
|
|
4106
|
+
return /^\/([^/^~]+)$/.test(initialChunk.join('/'));
|
|
4107
|
+
};
|
|
4108
|
+
const isValidFieldChunk = (fieldChunk) => {
|
|
4109
|
+
// must start with 'fields' and have at least 3 segments, second non-empty and last segment must be '~locale'
|
|
4110
|
+
// eg. fields/-32234mainStory/~locale
|
|
4111
|
+
return /^fields\/[^/^~]+\/~locale$/.test(fieldChunk.join('/'));
|
|
4112
|
+
};
|
|
4113
|
+
const deepPathSegments = deepPathCandidate.split('/');
|
|
4114
|
+
const chunks = chunkSegments(deepPathSegments, { startNextChunkOnElementEqualTo: 'fields' });
|
|
4115
|
+
if (chunks.length <= 1) {
|
|
4116
|
+
return null; // malformed path, even regular paths have at least 2 chunks
|
|
4117
|
+
}
|
|
4118
|
+
else if (chunks.length === 2) {
|
|
4119
|
+
return null; // deep paths have at least 3 chunks
|
|
4120
|
+
}
|
|
4121
|
+
// With 3+ chunks we can now check for deep path correctness
|
|
4122
|
+
const [initialChunk, ...fieldChunks] = chunks;
|
|
4123
|
+
if (!isValidInitialChunk(initialChunk)) {
|
|
4124
|
+
return null;
|
|
4125
|
+
}
|
|
4126
|
+
if (!fieldChunks.every(isValidFieldChunk)) {
|
|
4127
|
+
return null;
|
|
4128
|
+
}
|
|
4129
|
+
return {
|
|
4130
|
+
key: initialChunk[1], // pick uuid from initial chunk ['','uuid123'],
|
|
4131
|
+
fields: fieldChunks.map((fieldChunk) => fieldChunk[1]), // pick only fieldName eg. from ['fields','mainStory', '~locale'] we pick `mainStory`
|
|
4132
|
+
};
|
|
4133
|
+
};
|
|
4134
|
+
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
4135
|
+
const chunks = [];
|
|
4136
|
+
let currentChunk = [];
|
|
4137
|
+
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
4138
|
+
const excludeEmptyChunks = (chunk) => chunk.length > 0;
|
|
4139
|
+
for (let i = 0; i < segments.length; i++) {
|
|
4140
|
+
const isInitialElement = i === 0;
|
|
4141
|
+
const segment = segments[i];
|
|
4142
|
+
if (isInitialElement) {
|
|
4143
|
+
currentChunk = [segment];
|
|
4144
|
+
}
|
|
4145
|
+
else if (isSegmentBeginningOfChunk(segment)) {
|
|
4146
|
+
chunks.push(currentChunk);
|
|
4147
|
+
currentChunk = [segment];
|
|
4148
|
+
}
|
|
4149
|
+
else {
|
|
4150
|
+
currentChunk.push(segment);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
chunks.push(currentChunk);
|
|
4154
|
+
return chunks.filter(excludeEmptyChunks);
|
|
4155
|
+
};
|
|
4156
|
+
|
|
4157
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4158
|
+
function get(obj, path) {
|
|
4159
|
+
if (!path.length) {
|
|
4160
|
+
return obj;
|
|
4161
|
+
}
|
|
4162
|
+
try {
|
|
4163
|
+
const [currentPath, ...nextPath] = path;
|
|
4164
|
+
return get(obj[currentPath], nextPath);
|
|
4165
|
+
}
|
|
4166
|
+
catch (err) {
|
|
4167
|
+
return undefined;
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
const isEntry = (value) => {
|
|
4171
|
+
return (null !== value &&
|
|
4172
|
+
typeof value === 'object' &&
|
|
4173
|
+
'sys' in value &&
|
|
4174
|
+
value.sys?.type === 'Entry');
|
|
4175
|
+
};
|
|
4176
|
+
const isAsset = (value) => {
|
|
4177
|
+
return (null !== value &&
|
|
4178
|
+
typeof value === 'object' &&
|
|
4179
|
+
'sys' in value &&
|
|
4180
|
+
value.sys?.type === 'Asset');
|
|
4181
|
+
};
|
|
4182
|
+
|
|
4183
|
+
function deepFreeze(obj) {
|
|
4184
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
4185
|
+
for (const name of propNames) {
|
|
4186
|
+
const value = obj[name];
|
|
4187
|
+
if (value && typeof value === 'object') {
|
|
4188
|
+
deepFreeze(value);
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
return Object.freeze(obj);
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
/**
|
|
4195
|
+
* Base Store for entities
|
|
4196
|
+
* Can be extended for the different loading behaviours (editor, production, ..)
|
|
4197
|
+
*/
|
|
4198
|
+
class EntityStoreBase {
|
|
4199
|
+
constructor({ entities, locale }) {
|
|
4200
|
+
/* serialized */ this.entryMap = new Map();
|
|
4201
|
+
/* serialized */ this.assetMap = new Map();
|
|
4202
|
+
this.locale = locale;
|
|
4203
|
+
for (const entity of entities) {
|
|
4204
|
+
this.addEntity(entity);
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
get entities() {
|
|
4208
|
+
return [...this.entryMap.values(), ...this.assetMap.values()];
|
|
4209
|
+
}
|
|
4210
|
+
updateEntity(entity) {
|
|
4211
|
+
this.addEntity(entity);
|
|
4212
|
+
}
|
|
4213
|
+
getEntryOrAsset(linkOrEntryOrAsset, path) {
|
|
4214
|
+
if (isDeepPath(path)) {
|
|
4215
|
+
return this.getDeepEntry(linkOrEntryOrAsset, path);
|
|
4216
|
+
}
|
|
4217
|
+
let entity;
|
|
4218
|
+
if (isLink(linkOrEntryOrAsset)) {
|
|
4219
|
+
const resolvedEntity = linkOrEntryOrAsset.sys.linkType === 'Entry'
|
|
4220
|
+
? this.entryMap.get(linkOrEntryOrAsset.sys.id)
|
|
4221
|
+
: this.assetMap.get(linkOrEntryOrAsset.sys.id);
|
|
4222
|
+
if (!resolvedEntity || resolvedEntity.sys.type !== linkOrEntryOrAsset.sys.linkType) {
|
|
4223
|
+
console.warn(`Experience references unresolved entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4224
|
+
return;
|
|
4225
|
+
}
|
|
4226
|
+
entity = resolvedEntity;
|
|
4227
|
+
}
|
|
4228
|
+
else if (isAsset(linkOrEntryOrAsset) || isEntry(linkOrEntryOrAsset)) {
|
|
4229
|
+
// We already have the complete entity in preview & delivery (resolved by the CMA client)
|
|
4230
|
+
entity = linkOrEntryOrAsset;
|
|
4231
|
+
}
|
|
4232
|
+
else {
|
|
4233
|
+
throw new Error(`Unexpected object when resolving entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4234
|
+
}
|
|
4235
|
+
return entity;
|
|
4236
|
+
}
|
|
4237
|
+
/**
|
|
4238
|
+
* @deprecated in the base class this should be simply an abstract method
|
|
4239
|
+
* @param entityLink
|
|
4240
|
+
* @param path
|
|
4241
|
+
* @returns
|
|
4242
|
+
*/
|
|
4243
|
+
getValue(entityLink, path) {
|
|
4244
|
+
const entity = this.getEntity(entityLink.sys.linkType, entityLink.sys.id);
|
|
4245
|
+
if (!entity) {
|
|
4246
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4247
|
+
console.warn(`Unresolved entity reference: ${entityLink.sys.linkType} with ID ${entityLink.sys.id}`);
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
4250
|
+
return get(entity, path);
|
|
4251
|
+
}
|
|
4252
|
+
getEntityFromLink(link) {
|
|
4253
|
+
const resolvedEntity = link.sys.linkType === 'Entry'
|
|
4254
|
+
? this.entryMap.get(link.sys.id)
|
|
4255
|
+
: this.assetMap.get(link.sys.id);
|
|
4256
|
+
if (!resolvedEntity || resolvedEntity.sys.type !== link.sys.linkType) {
|
|
4257
|
+
console.warn(`Experience references unresolved entity: ${JSON.stringify(link)}`);
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
4260
|
+
return resolvedEntity;
|
|
4261
|
+
}
|
|
4262
|
+
getAssetById(assetId) {
|
|
4263
|
+
const asset = this.assetMap.get(assetId);
|
|
4264
|
+
if (!asset) {
|
|
4265
|
+
console.warn(`Asset with ID "${assetId}" is not found in the store`);
|
|
4266
|
+
return;
|
|
4267
|
+
}
|
|
4268
|
+
return asset;
|
|
4269
|
+
}
|
|
4270
|
+
getEntryById(entryId) {
|
|
4271
|
+
const entry = this.entryMap.get(entryId);
|
|
4272
|
+
if (!entry) {
|
|
4273
|
+
console.warn(`Entry with ID "${entryId}" is not found in the store`);
|
|
4274
|
+
return;
|
|
4275
|
+
}
|
|
4276
|
+
return entry;
|
|
4277
|
+
}
|
|
4278
|
+
getEntitiesFromMap(type, ids) {
|
|
4279
|
+
const resolved = [];
|
|
4280
|
+
const missing = [];
|
|
4281
|
+
for (const id of ids) {
|
|
4282
|
+
const entity = this.getEntity(type, id);
|
|
4283
|
+
if (entity) {
|
|
4284
|
+
resolved.push(entity);
|
|
4285
|
+
}
|
|
4286
|
+
else {
|
|
4287
|
+
missing.push(id);
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
return {
|
|
4291
|
+
resolved,
|
|
4292
|
+
missing,
|
|
4293
|
+
};
|
|
4294
|
+
}
|
|
4295
|
+
addEntity(entity) {
|
|
4296
|
+
if (isAsset(entity)) {
|
|
4297
|
+
// cloned and frozen
|
|
4298
|
+
this.assetMap.set(entity.sys.id, deepFreeze(structuredClone(entity)));
|
|
4299
|
+
}
|
|
4300
|
+
else if (isEntry(entity)) {
|
|
4301
|
+
// cloned and frozen
|
|
4302
|
+
this.entryMap.set(entity.sys.id, deepFreeze(structuredClone(entity)));
|
|
4303
|
+
}
|
|
4304
|
+
else {
|
|
4305
|
+
throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
async fetchAsset(id) {
|
|
4309
|
+
const { resolved, missing } = this.getEntitiesFromMap('Asset', [id]);
|
|
4310
|
+
if (missing.length) {
|
|
4311
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4312
|
+
console.warn(`Asset "${id}" is not in the store`);
|
|
4313
|
+
return;
|
|
4314
|
+
}
|
|
4315
|
+
return resolved[0];
|
|
4316
|
+
}
|
|
4317
|
+
async fetchAssets(ids) {
|
|
4318
|
+
const { resolved, missing } = this.getEntitiesFromMap('Asset', ids);
|
|
4319
|
+
if (missing.length) {
|
|
4320
|
+
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4321
|
+
}
|
|
4322
|
+
return resolved;
|
|
4323
|
+
}
|
|
4324
|
+
async fetchEntry(id) {
|
|
4325
|
+
const { resolved, missing } = this.getEntitiesFromMap('Entry', [id]);
|
|
4326
|
+
if (missing.length) {
|
|
4327
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4328
|
+
console.warn(`Entry "${id}" is not in the store`);
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4331
|
+
return resolved[0];
|
|
4332
|
+
}
|
|
4333
|
+
async fetchEntries(ids) {
|
|
4334
|
+
const { resolved, missing } = this.getEntitiesFromMap('Entry', ids);
|
|
4335
|
+
if (missing.length) {
|
|
4336
|
+
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4337
|
+
}
|
|
4338
|
+
return resolved;
|
|
4339
|
+
}
|
|
4340
|
+
getDeepEntry(linkOrEntryOrAsset, path) {
|
|
4341
|
+
const resolveFieldset = (unresolvedFieldset, headEntry) => {
|
|
4342
|
+
const resolvedFieldset = [];
|
|
4343
|
+
let entityToResolveFieldsFrom = headEntry;
|
|
4344
|
+
for (let i = 0; i < unresolvedFieldset.length; i++) {
|
|
4345
|
+
const isLeaf = i === unresolvedFieldset.length - 1; // with last row, we are not expecting a link, but a value
|
|
4346
|
+
const row = unresolvedFieldset[i];
|
|
4347
|
+
const [, field, _localeQualifier] = row;
|
|
4348
|
+
if (!entityToResolveFieldsFrom) {
|
|
4349
|
+
throw new Error(`Logic Error: Cannot resolve field ${field} of a fieldset as there is no entity to resolve it from.`);
|
|
4350
|
+
}
|
|
4351
|
+
if (isLeaf) {
|
|
4352
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4353
|
+
break;
|
|
4354
|
+
}
|
|
4355
|
+
const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
|
|
4356
|
+
if (undefined === fieldValue) {
|
|
4357
|
+
return {
|
|
4358
|
+
resolvedFieldset,
|
|
4359
|
+
isFullyResolved: false,
|
|
4360
|
+
reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
else if (isLink(fieldValue)) {
|
|
4364
|
+
const entity = this.getEntityFromLink(fieldValue);
|
|
4365
|
+
if (entity === undefined) {
|
|
4366
|
+
return {
|
|
4367
|
+
resolvedFieldset,
|
|
4368
|
+
isFullyResolved: false,
|
|
4369
|
+
reason: `Field reference Link (sys.id=${fieldValue.sys.id}) not found in the EntityStore, waiting...`,
|
|
4370
|
+
};
|
|
4371
|
+
}
|
|
4372
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4373
|
+
entityToResolveFieldsFrom = entity; // we move up
|
|
4374
|
+
}
|
|
4375
|
+
else if (isAsset(fieldValue) || isEntry(fieldValue)) {
|
|
4376
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4377
|
+
entityToResolveFieldsFrom = fieldValue; // we move up
|
|
4378
|
+
}
|
|
4379
|
+
else {
|
|
4380
|
+
return {
|
|
4381
|
+
resolvedFieldset,
|
|
4382
|
+
isFullyResolved: false,
|
|
4383
|
+
reason: `Deep path points to an invalid field value of type '${typeof fieldValue}' (value=${fieldValue})`,
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
return {
|
|
4388
|
+
resolvedFieldset,
|
|
4389
|
+
isFullyResolved: true,
|
|
4390
|
+
};
|
|
4391
|
+
};
|
|
4392
|
+
const headEntity = isLink(linkOrEntryOrAsset)
|
|
4393
|
+
? this.getEntityFromLink(linkOrEntryOrAsset)
|
|
4394
|
+
: linkOrEntryOrAsset;
|
|
4395
|
+
if (undefined === headEntity) {
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
const unresolvedFieldset = parseDataSourcePathIntoFieldset(path);
|
|
4399
|
+
// The purpose here is to take this intermediate representation of the deep-path
|
|
4400
|
+
// and to follow the links to the leaf-entity and field
|
|
4401
|
+
// in case we can't follow till the end, we should signal that there was null-reference in the path
|
|
4402
|
+
const { resolvedFieldset, isFullyResolved, reason } = resolveFieldset(unresolvedFieldset, headEntity);
|
|
4403
|
+
if (!isFullyResolved) {
|
|
4404
|
+
reason &&
|
|
4405
|
+
console.debug(`[exp-builder.sdk::EntityStoreBased::getValueDeep()] Deep path wasn't resolved till leaf node, falling back to undefined, because: ${reason}`);
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
const [leafEntity] = resolvedFieldset[resolvedFieldset.length - 1];
|
|
4409
|
+
return leafEntity;
|
|
4410
|
+
}
|
|
4411
|
+
getEntity(type, id) {
|
|
4412
|
+
if (type === 'Asset') {
|
|
4413
|
+
return this.assetMap.get(id);
|
|
4414
|
+
}
|
|
4415
|
+
return this.entryMap.get(id);
|
|
4416
|
+
}
|
|
4417
|
+
toJSON() {
|
|
4418
|
+
return {
|
|
4419
|
+
entryMap: Object.fromEntries(this.entryMap),
|
|
4420
|
+
assetMap: Object.fromEntries(this.assetMap),
|
|
4421
|
+
locale: this.locale,
|
|
4422
|
+
};
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
|
|
4426
|
+
class UninitializedEntityStore extends EntityStoreBase {
|
|
4427
|
+
constructor() {
|
|
4428
|
+
super({ entities: [], locale: 'uninitialized-locale-in-uninitialized-entity-store' });
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
create((set, get) => ({
|
|
4433
|
+
// The UninitializedEntityStore is a placeholder instance and is here to highlight the
|
|
4434
|
+
// // fact that it's not used by anything until during loading lifecycle it'sreplaced by real entity store:
|
|
4435
|
+
// - in Preview+Delivery mode: right after we fetch Expereince and it entities
|
|
4436
|
+
// - in EDITOR (VisualEditor) mode: right after the VisualEditor is async imported and initialize event happens
|
|
4437
|
+
entityStore: new UninitializedEntityStore(),
|
|
4438
|
+
areEntitiesFetched: false,
|
|
4439
|
+
setEntitiesFetched(fetched) {
|
|
4440
|
+
set({ areEntitiesFetched: fetched });
|
|
4441
|
+
},
|
|
4442
|
+
resolveAssetById(assetId) {
|
|
4443
|
+
if (!assetId)
|
|
4444
|
+
return undefined;
|
|
4445
|
+
const { entityStore } = get();
|
|
4446
|
+
return entityStore.getAssetById(assetId);
|
|
4447
|
+
},
|
|
4448
|
+
resolveEntryById(entryId) {
|
|
4449
|
+
if (!entryId)
|
|
4450
|
+
return undefined;
|
|
4451
|
+
const { entityStore } = get();
|
|
4452
|
+
return entityStore.getEntryById(entryId);
|
|
4453
|
+
},
|
|
4454
|
+
resolveEntity(link) {
|
|
4455
|
+
if (!link)
|
|
4456
|
+
return undefined;
|
|
4457
|
+
const { entityStore } = get();
|
|
4458
|
+
return entityStore.getEntityFromLink(link);
|
|
4459
|
+
},
|
|
4460
|
+
resetEntityStore(entityStore) {
|
|
4461
|
+
set({
|
|
4462
|
+
entityStore,
|
|
4463
|
+
areEntitiesFetched: false,
|
|
4464
|
+
});
|
|
4465
|
+
},
|
|
4466
|
+
}));
|
|
4467
|
+
|
|
4468
|
+
var VisualEditorMode;
|
|
4469
|
+
(function (VisualEditorMode) {
|
|
4470
|
+
VisualEditorMode["LazyLoad"] = "lazyLoad";
|
|
4471
|
+
VisualEditorMode["InjectScript"] = "injectScript";
|
|
4472
|
+
})(VisualEditorMode || (VisualEditorMode = {}));
|
|
4473
|
+
|
|
4474
|
+
var css_248z$2$1 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.cf-single-column-wrapper{position:relative}.cf-container-wrapper{position:relative;width:100%}.contentful-container:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.contentful-section-label:after{content:\"Section\"}.contentful-container-label:after{content:\"Container\"}.contentful-container-link,.contentful-container-link:active,.contentful-container-link:focus-visible,.contentful-container-link:hover,.contentful-container-link:read-write,.contentful-container-link:visited{color:inherit;outline:unset;text-decoration:unset}";
|
|
4475
|
+
styleInject(css_248z$2$1);
|
|
4476
|
+
|
|
4477
|
+
const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave, onMouseDown, onClick, flex, flexBasis, flexShrink, flexDirection, gap, justifyContent, justifyItems, justifySelf, alignItems, alignSelf, alignContent, order, flexWrap, flexGrow, className, cssStyles, ...props }, ref) => {
|
|
4478
|
+
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
4479
|
+
display: 'flex',
|
|
4480
|
+
flex,
|
|
4481
|
+
flexBasis,
|
|
4482
|
+
flexShrink,
|
|
4483
|
+
flexDirection,
|
|
4484
|
+
gap,
|
|
4485
|
+
justifyContent,
|
|
4486
|
+
justifyItems,
|
|
4487
|
+
justifySelf,
|
|
4488
|
+
alignItems,
|
|
4489
|
+
alignSelf,
|
|
4490
|
+
alignContent,
|
|
4491
|
+
order,
|
|
4492
|
+
flexWrap,
|
|
4493
|
+
flexGrow,
|
|
4494
|
+
...cssStyles,
|
|
4495
|
+
}, className: className, onMouseEnter: onMouseEnter, onMouseUp: onMouseUp, onMouseDown: onMouseDown, onMouseLeave: onMouseLeave, onClick: onClick, ...props }, children));
|
|
4496
|
+
});
|
|
4497
|
+
Flex.displayName = 'Flex';
|
|
4498
|
+
|
|
4499
|
+
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}[data-ctfl-zone-id=root] .cf-divider:before{bottom:-5px;content:\"\";left:-5px;pointer-events:all;position:absolute;right:-5px;top:-5px}";
|
|
4500
|
+
styleInject(css_248z$1$1);
|
|
4501
|
+
|
|
4502
|
+
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{display:flex;position:relative}.cf-single-column{pointer-events:all}.cf-single-column-wrapper:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.cf-single-column-label:after{content:\"Column\"}";
|
|
4503
|
+
styleInject(css_248z$9);
|
|
4504
|
+
|
|
4505
|
+
const ColumnWrapper = forwardRef((props, ref) => {
|
|
4506
|
+
return (React.createElement("div", { ref: ref, ...props, style: {
|
|
4507
|
+
...(props.style || {}),
|
|
4508
|
+
display: 'grid',
|
|
4509
|
+
gridTemplateColumns: 'repeat(12, [col-start] 1fr)',
|
|
4510
|
+
} }, props.children));
|
|
4511
|
+
});
|
|
4512
|
+
ColumnWrapper.displayName = 'ColumnWrapper';
|
|
4513
|
+
|
|
4514
|
+
const assemblyStyle = { display: 'contents' };
|
|
4515
|
+
// Feel free to do any magic as regards variable definitions for assemblies
|
|
4516
|
+
// Or if this isn't necessary by the time we figure that part out, we can bid this part farewell
|
|
4517
|
+
const Assembly = (props) => {
|
|
4518
|
+
if (props.editorMode) {
|
|
4519
|
+
const { node, dragProps, ...editorModeProps } = props;
|
|
4520
|
+
return props.renderDropzone(node, {
|
|
4521
|
+
...editorModeProps,
|
|
4522
|
+
['data-test-id']: 'contentful-assembly',
|
|
4523
|
+
className: props.className,
|
|
4524
|
+
dragProps,
|
|
4525
|
+
});
|
|
4526
|
+
}
|
|
4527
|
+
// Using a display contents so assembly content/children
|
|
4528
|
+
// can appear as if they are direct children of the div wrapper's parent
|
|
4529
|
+
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4530
|
+
};
|
|
4531
|
+
|
|
4532
|
+
class DragState {
|
|
4533
|
+
constructor() {
|
|
4534
|
+
this.isDragStartedOnParent = false;
|
|
4535
|
+
this.isDraggingItem = false;
|
|
4536
|
+
}
|
|
4537
|
+
get isDragging() {
|
|
4538
|
+
return this.isDraggingItem;
|
|
4539
|
+
}
|
|
4540
|
+
get isDraggingOnParent() {
|
|
4541
|
+
return this.isDragStartedOnParent;
|
|
4542
|
+
}
|
|
4543
|
+
updateIsDragging(isDraggingItem) {
|
|
4544
|
+
this.isDraggingItem = isDraggingItem;
|
|
4545
|
+
}
|
|
4546
|
+
updateIsDragStartedOnParent(isDragStartedOnParent) {
|
|
4547
|
+
this.isDragStartedOnParent = isDragStartedOnParent;
|
|
4548
|
+
}
|
|
4549
|
+
resetState() {
|
|
4550
|
+
this.isDraggingItem = false;
|
|
4551
|
+
this.isDragStartedOnParent = false;
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
class SimulateDnD extends DragState {
|
|
4556
|
+
constructor() {
|
|
4557
|
+
super();
|
|
4558
|
+
this.draggingElement = null;
|
|
4559
|
+
}
|
|
4560
|
+
setupDrag() {
|
|
4561
|
+
this.updateIsDragStartedOnParent(true);
|
|
4562
|
+
}
|
|
4563
|
+
startDrag(coordX, coordY) {
|
|
4564
|
+
this.draggingElement = document.getElementById(NEW_COMPONENT_ID);
|
|
4565
|
+
this.updateIsDragging(true);
|
|
4566
|
+
this.simulateMouseEvent(coordX, coordY, 'mousedown');
|
|
4567
|
+
}
|
|
4568
|
+
updateDrag(coordX, coordY) {
|
|
4053
4569
|
if (!this.draggingElement) {
|
|
4054
4570
|
this.draggingElement = document.querySelector(`[${CTFL_DRAGGING_ELEMENT}]`);
|
|
4055
4571
|
}
|
|
@@ -4082,10 +4598,11 @@ class SimulateDnD extends DragState {
|
|
|
4082
4598
|
}
|
|
4083
4599
|
var SimulateDnD$1 = new SimulateDnD();
|
|
4084
4600
|
|
|
4085
|
-
function useEditorSubscriber() {
|
|
4086
|
-
const entityStore =
|
|
4087
|
-
const areEntitiesFetched =
|
|
4088
|
-
const setEntitiesFetched =
|
|
4601
|
+
function useEditorSubscriber(entityCache) {
|
|
4602
|
+
const entityStore = entityCache((state) => state.entityStore);
|
|
4603
|
+
const areEntitiesFetched = entityCache((state) => state.areEntitiesFetched);
|
|
4604
|
+
const setEntitiesFetched = entityCache((state) => state.setEntitiesFetched);
|
|
4605
|
+
const resetEntityStore = entityCache((state) => state.resetEntityStore);
|
|
4089
4606
|
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
4090
4607
|
updateTree: state.updateTree,
|
|
4091
4608
|
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
@@ -4097,7 +4614,6 @@ function useEditorSubscriber() {
|
|
|
4097
4614
|
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
4098
4615
|
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
4099
4616
|
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
4100
|
-
const resetEntityStore = useEntityStore((state) => state.resetEntityStore);
|
|
4101
4617
|
const setComponentId = useDraggedItemStore((state) => state.setComponentId);
|
|
4102
4618
|
const setHoveredComponentId = useDraggedItemStore((state) => state.setHoveredComponentId);
|
|
4103
4619
|
const setDraggingOnCanvas = useDraggedItemStore((state) => state.setDraggingOnCanvas);
|
|
@@ -4148,7 +4664,7 @@ function useEditorSubscriber() {
|
|
|
4148
4664
|
const isMissingL2Entities = (deepReferences) => {
|
|
4149
4665
|
const referentLinks = deepReferences
|
|
4150
4666
|
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4151
|
-
.filter(isLink);
|
|
4667
|
+
.filter(isLink$1);
|
|
4152
4668
|
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4153
4669
|
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
4154
4670
|
};
|
|
@@ -4165,7 +4681,7 @@ function useEditorSubscriber() {
|
|
|
4165
4681
|
const fillupL2 = async ({ deepReferences }) => {
|
|
4166
4682
|
const referentLinks = deepReferences
|
|
4167
4683
|
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4168
|
-
.filter(isLink);
|
|
4684
|
+
.filter(isLink$1);
|
|
4169
4685
|
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4170
4686
|
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
4171
4687
|
};
|
|
@@ -4218,8 +4734,9 @@ function useEditorSubscriber() {
|
|
|
4218
4734
|
}
|
|
4219
4735
|
let newEntityStore = entityStore;
|
|
4220
4736
|
if (entityStore.locale !== locale) {
|
|
4221
|
-
newEntityStore =
|
|
4737
|
+
newEntityStore = new EditorModeEntityStore({ locale, entities: [] });
|
|
4222
4738
|
setLocale(locale);
|
|
4739
|
+
resetEntityStore(newEntityStore);
|
|
4223
4740
|
}
|
|
4224
4741
|
// Below are mutually exclusive cases
|
|
4225
4742
|
if (changedNode) {
|
|
@@ -4749,17 +5266,49 @@ const addStylesTag = (className, styleRules) => {
|
|
|
4749
5266
|
|
|
4750
5267
|
const getUnboundValues = ({ key, fallback, unboundValues, }) => {
|
|
4751
5268
|
const lodashPath = `${key}.value`;
|
|
4752
|
-
return get$
|
|
5269
|
+
return get$2(unboundValues, lodashPath, fallback);
|
|
4753
5270
|
};
|
|
4754
5271
|
|
|
4755
|
-
|
|
5272
|
+
/**
|
|
5273
|
+
* When node is a pattern block, we need to look up for default values of the pattern variables
|
|
5274
|
+
* and merge them with the updated node props.
|
|
5275
|
+
* While loop is making sure that we look up for the updated default values in the parent pattern
|
|
5276
|
+
* component settings as well.
|
|
5277
|
+
*/
|
|
5278
|
+
const maybeMergePatternDefaultDesignValues = ({ variableName, variableMapping, node, findNodeById, }) => {
|
|
5279
|
+
if (node.type === ASSEMBLY_BLOCK_NODE_TYPE) {
|
|
5280
|
+
const patternId = node.data.pattern?.id;
|
|
5281
|
+
const exposedPropertyName = node['exposedPropertyNameToKeyMap'][variableName];
|
|
5282
|
+
if (!exposedPropertyName || !patternId) {
|
|
5283
|
+
return variableMapping.valuesByBreakpoint;
|
|
5284
|
+
}
|
|
5285
|
+
const exposedVariableDefinition = componentRegistry.get(patternId)?.definition.variables[exposedPropertyName];
|
|
5286
|
+
let exposedDefaultValue = exposedVariableDefinition?.defaultValue;
|
|
5287
|
+
let parentPatternNode = findNodeById(node.data.pattern?.nodeId);
|
|
5288
|
+
while (parentPatternNode) {
|
|
5289
|
+
const parentPatternId = parentPatternNode.data.pattern?.id;
|
|
5290
|
+
const nextKey = parentPatternNode['exposedPropertyNameToKeyMap'][exposedPropertyName];
|
|
5291
|
+
if (!parentPatternId || !nextKey) {
|
|
5292
|
+
break;
|
|
5293
|
+
}
|
|
5294
|
+
const parentPatternVariableDefinition = componentRegistry.get(parentPatternId)?.definition.variables[nextKey];
|
|
5295
|
+
exposedDefaultValue = mergeDesignValuesByBreakpoint(parentPatternVariableDefinition?.defaultValue, exposedDefaultValue);
|
|
5296
|
+
parentPatternNode = findNodeById(parentPatternNode.data.pattern?.nodeId);
|
|
5297
|
+
}
|
|
5298
|
+
const mergedDesignValue = mergeDesignValuesByBreakpoint(exposedDefaultValue, variableMapping);
|
|
5299
|
+
return mergedDesignValue?.valuesByBreakpoint;
|
|
5300
|
+
}
|
|
5301
|
+
return variableMapping.valuesByBreakpoint;
|
|
5302
|
+
};
|
|
5303
|
+
|
|
5304
|
+
const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, definition, options, userIsDragging, requiresDragWrapper, }) => {
|
|
4756
5305
|
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
4757
5306
|
const hyperlinkPattern = useEditorStore((state) => state.hyperLinkPattern);
|
|
4758
5307
|
const locale = useEditorStore((state) => state.locale);
|
|
4759
5308
|
const dataSource = useEditorStore((state) => state.dataSource);
|
|
4760
|
-
const entityStore = useEntityStore((state) => state.entityStore);
|
|
4761
5309
|
const draggingId = useDraggedItemStore((state) => state.onBeforeCaptureId);
|
|
4762
5310
|
const nodeRect = useDraggedItemStore((state) => state.domRect);
|
|
5311
|
+
const findNodeById = useTreeStore((state) => state.findNodeById);
|
|
4763
5312
|
const isEmptyZone = !node.children.length;
|
|
4764
5313
|
const props = useMemo(() => {
|
|
4765
5314
|
const propsBase = {
|
|
@@ -4783,7 +5332,13 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
|
|
|
4783
5332
|
};
|
|
4784
5333
|
}
|
|
4785
5334
|
if (variableMapping.type === 'DesignValue') {
|
|
4786
|
-
const
|
|
5335
|
+
const value = maybeMergePatternDefaultDesignValues({
|
|
5336
|
+
variableName,
|
|
5337
|
+
variableMapping,
|
|
5338
|
+
node,
|
|
5339
|
+
findNodeById,
|
|
5340
|
+
});
|
|
5341
|
+
const valuesByBreakpoint = resolveDesignValue(value, variableName);
|
|
4787
5342
|
const designValue = variableName === 'cfHeight'
|
|
4788
5343
|
? calculateNodeDefaultHeight({
|
|
4789
5344
|
blockId: node.data.blockId,
|
|
@@ -4874,6 +5429,7 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
|
|
|
4874
5429
|
unboundValues,
|
|
4875
5430
|
entityStore,
|
|
4876
5431
|
renderDropzone,
|
|
5432
|
+
findNodeById,
|
|
4877
5433
|
]);
|
|
4878
5434
|
const cfStyles = useMemo(() => ({
|
|
4879
5435
|
...buildCfStyles(props),
|
|
@@ -5046,7 +5602,6 @@ const MissingComponentPlaceholder = ({ blockId }) => {
|
|
|
5046
5602
|
};
|
|
5047
5603
|
|
|
5048
5604
|
const CircularDependencyErrorPlaceholder = forwardRef(({ wrappingPatternIds, ...props }, ref) => {
|
|
5049
|
-
const entityStore = useEntityStore((state) => state.entityStore);
|
|
5050
5605
|
return (React.createElement("div", { ...props,
|
|
5051
5606
|
// Pass through ref to avoid DND errors being logged
|
|
5052
5607
|
ref: ref, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
@@ -5059,7 +5614,7 @@ const CircularDependencyErrorPlaceholder = forwardRef(({ wrappingPatternIds, ...
|
|
|
5059
5614
|
"Circular usage of patterns detected:",
|
|
5060
5615
|
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
5061
5616
|
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
5062
|
-
const entry =
|
|
5617
|
+
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
5063
5618
|
const entryTitle = entry?.fields?.title;
|
|
5064
5619
|
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
5065
5620
|
return React.createElement("li", { key: patternId }, text);
|
|
@@ -5067,8 +5622,7 @@ const CircularDependencyErrorPlaceholder = forwardRef(({ wrappingPatternIds, ...
|
|
|
5067
5622
|
});
|
|
5068
5623
|
CircularDependencyErrorPlaceholder.displayName = 'CircularDependencyErrorPlaceholder';
|
|
5069
5624
|
|
|
5070
|
-
const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging, wrappingPatternIds, }) => {
|
|
5071
|
-
const areEntitiesFetched = useEntityStore((state) => state.areEntitiesFetched);
|
|
5625
|
+
const useComponent = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, userIsDragging, wrappingPatternIds, }) => {
|
|
5072
5626
|
const tree = useTreeStore((state) => state.tree);
|
|
5073
5627
|
const componentRegistration = useMemo(() => {
|
|
5074
5628
|
let registration = componentRegistry.get(node.data.blockId);
|
|
@@ -5094,6 +5648,7 @@ const useComponent = ({ node, resolveDesignValue, renderDropzone, userIsDragging
|
|
|
5094
5648
|
const requiresDragWrapper = !isPatternNode && !isStructureComponent && !componentRegistration?.options?.wrapComponent;
|
|
5095
5649
|
const { componentProps, wrapperStyles } = useComponentProps({
|
|
5096
5650
|
node,
|
|
5651
|
+
entityStore,
|
|
5097
5652
|
areEntitiesFetched,
|
|
5098
5653
|
resolveDesignValue,
|
|
5099
5654
|
renderDropzone,
|
|
@@ -5568,13 +6123,15 @@ function getStyle$1(style, snapshot) {
|
|
|
5568
6123
|
transitionDuration: `0.001s`,
|
|
5569
6124
|
};
|
|
5570
6125
|
}
|
|
5571
|
-
const EditorBlock = ({ node: rawNode, resolveDesignValue, renderDropzone, index, zoneId, userIsDragging, placeholder, wrappingPatternIds, }) => {
|
|
6126
|
+
const EditorBlock = ({ node: rawNode, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, index, zoneId, userIsDragging, placeholder, wrappingPatternIds, }) => {
|
|
5572
6127
|
const { slotId } = parseZoneId(zoneId);
|
|
5573
6128
|
const ref = useRef(null);
|
|
5574
6129
|
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
5575
6130
|
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
5576
6131
|
const { node, componentId, elementToRender, definition, isPatternNode, isPatternComponent, isNestedPattern, } = useComponent({
|
|
5577
6132
|
node: rawNode,
|
|
6133
|
+
entityStore,
|
|
6134
|
+
areEntitiesFetched,
|
|
5578
6135
|
resolveDesignValue,
|
|
5579
6136
|
renderDropzone,
|
|
5580
6137
|
userIsDragging,
|
|
@@ -5715,10 +6272,12 @@ function getStyle(style = {}, snapshot) {
|
|
|
5715
6272
|
transitionDuration: `0.001s`,
|
|
5716
6273
|
};
|
|
5717
6274
|
}
|
|
5718
|
-
const EditorBlockClone = ({ node: rawNode, resolveDesignValue, snapshot, provided, renderDropzone, wrappingPatternIds, }) => {
|
|
6275
|
+
const EditorBlockClone = ({ node: rawNode, entityStore, areEntitiesFetched, resolveDesignValue, snapshot, provided, renderDropzone, wrappingPatternIds, }) => {
|
|
5719
6276
|
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
5720
6277
|
const { node, elementToRender } = useComponent({
|
|
5721
6278
|
node: rawNode,
|
|
6279
|
+
entityStore,
|
|
6280
|
+
areEntitiesFetched,
|
|
5722
6281
|
resolveDesignValue,
|
|
5723
6282
|
renderDropzone,
|
|
5724
6283
|
userIsDragging,
|
|
@@ -5755,7 +6314,7 @@ const getHtmlComponentProps = (props) => {
|
|
|
5755
6314
|
return {};
|
|
5756
6315
|
};
|
|
5757
6316
|
|
|
5758
|
-
function DropzoneClone({ node, zoneId, resolveDesignValue, WrapperComponent = 'div', renderDropzone, dragProps, wrappingPatternIds, ...rest }) {
|
|
6317
|
+
function DropzoneClone({ node, entityStore, zoneId, resolveDesignValue, WrapperComponent = 'div', renderDropzone, dragProps, wrappingPatternIds, areEntitiesFetched, ...rest }) {
|
|
5759
6318
|
const tree = useTreeStore((state) => state.tree);
|
|
5760
6319
|
const content = node?.children || tree.root?.children || [];
|
|
5761
6320
|
const { slotId } = parseZoneId(zoneId);
|
|
@@ -5776,11 +6335,11 @@ function DropzoneClone({ node, zoneId, resolveDesignValue, WrapperComponent = 'd
|
|
|
5776
6335
|
.filter((node) => node.data.slotId === slotId)
|
|
5777
6336
|
.map((item) => {
|
|
5778
6337
|
const componentId = item.data.id;
|
|
5779
|
-
return (React.createElement(EditorBlockClone, { key: componentId, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds }));
|
|
6338
|
+
return (React.createElement(EditorBlockClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, key: componentId, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds }));
|
|
5780
6339
|
})));
|
|
5781
6340
|
}
|
|
5782
6341
|
|
|
5783
|
-
function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponent = 'div', dragProps, wrappingPatternIds: parentWrappingPatternIds = new Set(), ...rest }) {
|
|
6342
|
+
function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponent = 'div', dragProps, entityStore, areEntitiesFetched, wrappingPatternIds: parentWrappingPatternIds = new Set(), ...rest }) {
|
|
5784
6343
|
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
5785
6344
|
const draggedItem = useDraggedItemStore((state) => state.draggedItem);
|
|
5786
6345
|
const isDraggingNewComponent = useDraggedItemStore((state) => Boolean(state.componentId));
|
|
@@ -5815,11 +6374,11 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
|
|
|
5815
6374
|
}, [isRootAssembly, node, parentWrappingPatternIds, tree.root.data.blockId]);
|
|
5816
6375
|
// To avoid a circular dependency, we create the recursive rendering function here and trickle it down
|
|
5817
6376
|
const renderDropzone = useCallback((node, props) => {
|
|
5818
|
-
return (React.createElement(Dropzone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, ...props }));
|
|
5819
|
-
}, [wrappingPatternIds, resolveDesignValue]);
|
|
6377
|
+
return (React.createElement(Dropzone, { zoneId: node.data.id, entityStore: entityStore, node: node, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, areEntitiesFetched: areEntitiesFetched, ...props }));
|
|
6378
|
+
}, [wrappingPatternIds, resolveDesignValue, entityStore, areEntitiesFetched]);
|
|
5820
6379
|
const renderClonedDropzone = useCallback((node, props) => {
|
|
5821
|
-
return (React.createElement(DropzoneClone, { zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds, ...props }));
|
|
5822
|
-
}, [resolveDesignValue, wrappingPatternIds]);
|
|
6380
|
+
return (React.createElement(DropzoneClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds, ...props }));
|
|
6381
|
+
}, [resolveDesignValue, wrappingPatternIds, entityStore, areEntitiesFetched]);
|
|
5823
6382
|
const isDropzoneEnabled = useMemo(() => {
|
|
5824
6383
|
const isColumns = node?.data.blockId === CONTENTFUL_COMPONENTS$1.columns.id;
|
|
5825
6384
|
const isDraggingSingleColumn = draggedNode?.data.blockId === CONTENTFUL_COMPONENTS$1.singleColumn.id;
|
|
@@ -5861,7 +6420,7 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
|
|
|
5861
6420
|
? node.children.length === 1 &&
|
|
5862
6421
|
resolveDesignValue(node?.children[0]?.data.props.cfWidth?.valuesByBreakpoint ?? {}, 'cfWidth') === '100%'
|
|
5863
6422
|
: false;
|
|
5864
|
-
return (React.createElement(Droppable, { droppableId: zoneId, direction: direction, isDropDisabled: !isDropzoneEnabled, renderClone: (provided, snapshot, rubic) => (React.createElement(EditorBlockClone, { node: content[rubic.source.index], resolveDesignValue: resolveDesignValue, provided: provided, snapshot: snapshot, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds })) }, (provided, snapshot) => {
|
|
6423
|
+
return (React.createElement(Droppable, { droppableId: zoneId, direction: direction, isDropDisabled: !isDropzoneEnabled, renderClone: (provided, snapshot, rubic) => (React.createElement(EditorBlockClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, node: content[rubic.source.index], resolveDesignValue: resolveDesignValue, provided: provided, snapshot: snapshot, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds })) }, (provided, snapshot) => {
|
|
5865
6424
|
return (React.createElement(WrapperComponent, { ...(provided || { droppableProps: {} }).droppableProps, ...htmlDraggableProps, ...htmlProps, ref: (refNode) => {
|
|
5866
6425
|
if (dragProps?.innerRef) {
|
|
5867
6426
|
dragProps.innerRef(refNode);
|
|
@@ -5879,7 +6438,7 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
|
|
|
5879
6438
|
}) },
|
|
5880
6439
|
isEmptyCanvas ? (React.createElement(EmptyContainer, { isDragging: isRootZone && userIsDragging })) : (content
|
|
5881
6440
|
.filter((node) => node.data.slotId === slotId)
|
|
5882
|
-
.map((item, i) => (React.createElement(EditorBlock, { placeholder: {
|
|
6441
|
+
.map((item, i) => (React.createElement(EditorBlock, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, placeholder: {
|
|
5883
6442
|
isDraggingOver: snapshot?.isDraggingOver,
|
|
5884
6443
|
totalIndexes: content.length,
|
|
5885
6444
|
elementIndex: i,
|
|
@@ -5891,8 +6450,8 @@ function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponen
|
|
|
5891
6450
|
}));
|
|
5892
6451
|
}
|
|
5893
6452
|
|
|
5894
|
-
const RootRenderer = ({ onChange }) => {
|
|
5895
|
-
useEditorSubscriber();
|
|
6453
|
+
const RootRenderer = ({ onChange, inMemoryEntitiesStore, }) => {
|
|
6454
|
+
useEditorSubscriber(inMemoryEntitiesStore);
|
|
5896
6455
|
const dragItem = useDraggedItemStore((state) => state.componentId);
|
|
5897
6456
|
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
5898
6457
|
const setHoveredComponentId = useDraggedItemStore((state) => state.setHoveredComponentId);
|
|
@@ -5972,28 +6531,28 @@ const RootRenderer = ({ onChange }) => {
|
|
|
5972
6531
|
handleResizeCanvas();
|
|
5973
6532
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5974
6533
|
}, [containerRef.current]);
|
|
6534
|
+
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
6535
|
+
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
5975
6536
|
return (React.createElement(DNDProvider, null,
|
|
5976
6537
|
dragItem && React.createElement(DraggableContainer, { id: dragItem }),
|
|
5977
6538
|
React.createElement("div", { "data-ctfl-root": true, className: styles$3.container, ref: containerRef, style: containerStyles },
|
|
5978
6539
|
userIsDragging && React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.hitbox }),
|
|
5979
|
-
React.createElement(Dropzone, { zoneId: ROOT_ID, resolveDesignValue: resolveDesignValue }),
|
|
6540
|
+
React.createElement(Dropzone, { zoneId: ROOT_ID, resolveDesignValue: resolveDesignValue, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched }),
|
|
5980
6541
|
userIsDragging && (React.createElement(React.Fragment, null,
|
|
5981
6542
|
React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.hitboxLower }),
|
|
5982
6543
|
React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.canvasBottomSpacer })))),
|
|
5983
6544
|
React.createElement("div", { "data-ctfl-hitboxes": true })));
|
|
5984
6545
|
};
|
|
5985
6546
|
|
|
5986
|
-
const useInitializeEditor = () => {
|
|
6547
|
+
const useInitializeEditor = (inMemoryEntitiesStore) => {
|
|
5987
6548
|
const initializeEditor = useEditorStore((state) => state.initializeEditor);
|
|
5988
6549
|
const [initialized, setInitialized] = useState(false);
|
|
5989
|
-
const resetEntityStore =
|
|
6550
|
+
const resetEntityStore = useStore(inMemoryEntitiesStore, (state) => state.resetEntityStore);
|
|
5990
6551
|
useEffect(() => {
|
|
5991
6552
|
const onVisualEditorInitialize = (event) => {
|
|
5992
6553
|
if (!event.detail)
|
|
5993
6554
|
return;
|
|
5994
|
-
const { componentRegistry, designTokens, locale: initialLocale,
|
|
5995
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5996
|
-
entities, } = event.detail;
|
|
6555
|
+
const { componentRegistry, designTokens, locale: initialLocale, entities } = event.detail;
|
|
5997
6556
|
initializeEditor({
|
|
5998
6557
|
initialLocale,
|
|
5999
6558
|
componentRegistry,
|
|
@@ -6001,7 +6560,7 @@ const useInitializeEditor = () => {
|
|
|
6001
6560
|
});
|
|
6002
6561
|
// if entities is set to [], then everything will still work as EntityStore will
|
|
6003
6562
|
// request entities on demand via ▲REQUEST_ENTITY
|
|
6004
|
-
resetEntityStore(initialLocale, entities);
|
|
6563
|
+
resetEntityStore(new EditorModeEntityStore({ locale: initialLocale, entities }));
|
|
6005
6564
|
setInitialized(true);
|
|
6006
6565
|
};
|
|
6007
6566
|
// Listen for VisualEditorComponents internal event
|
|
@@ -6021,8 +6580,8 @@ const useInitializeEditor = () => {
|
|
|
6021
6580
|
return initialized;
|
|
6022
6581
|
};
|
|
6023
6582
|
|
|
6024
|
-
const VisualEditorRoot = ({ experience }) => {
|
|
6025
|
-
const initialized = useInitializeEditor();
|
|
6583
|
+
const VisualEditorRoot = ({ experience, inMemoryEntitiesStore: inMemoryEntitiesStore$1 = inMemoryEntitiesStore, }) => {
|
|
6584
|
+
const initialized = useInitializeEditor(inMemoryEntitiesStore$1);
|
|
6026
6585
|
const setHyperLinkPattern = useEditorStore((state) => state.setHyperLinkPattern);
|
|
6027
6586
|
const setMousePosition = useDraggedItemStore((state) => state.setMousePosition);
|
|
6028
6587
|
const setHoveringZone = useZoneStore((state) => state.setHoveringZone);
|
|
@@ -6058,7 +6617,7 @@ const VisualEditorRoot = ({ experience }) => {
|
|
|
6058
6617
|
}, [setHoveringZone, setMousePosition]);
|
|
6059
6618
|
if (!initialized)
|
|
6060
6619
|
return null;
|
|
6061
|
-
return React.createElement(RootRenderer,
|
|
6620
|
+
return React.createElement(RootRenderer, { inMemoryEntitiesStore: inMemoryEntitiesStore$1 });
|
|
6062
6621
|
};
|
|
6063
6622
|
|
|
6064
6623
|
export { VisualEditorRoot as default };
|