@contentful/experiences-visual-editor-react 3.7.0-dev-20250919T0822-6a47406.0 → 3.7.0-dev-20250919T1400-8d76591.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 +334 -371
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +300 -337
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ import { produce } from 'immer';
|
|
|
9
9
|
import { isEqual, get as get$2, debounce } from 'lodash-es';
|
|
10
10
|
import '@contentful/rich-text-react-renderer';
|
|
11
11
|
|
|
12
|
-
var css_248z$
|
|
13
|
-
styleInject(css_248z$
|
|
12
|
+
var css_248z$b = "html,\nbody {\n margin: 0;\n padding: 0;\n}\n\n/*\n * All of these variables are tokens from Forma-36 and should not be adjusted as these\n * are global variables that may affect multiple places.\n * As our customers may use other design libraries, we try to avoid overlapping global\n * variables by always using the prefix `--exp-builder-` inside this SDK.\n */\n\n:root {\n /* Color tokens from Forma 36: https://f36.contentful.com/tokens/color-system */\n --exp-builder-blue100: #e8f5ff;\n --exp-builder-blue200: #ceecff;\n --exp-builder-blue300: #98cbff;\n --exp-builder-blue400: #40a0ff;\n --exp-builder-blue500: #036fe3;\n --exp-builder-blue600: #0059c8;\n --exp-builder-blue700: #0041ab;\n --exp-builder-blue800: #003298;\n --exp-builder-blue900: #002a8e;\n --exp-builder-gray100: #f7f9fa;\n --exp-builder-gray200: #e7ebee;\n --exp-builder-gray300: #cfd9e0;\n --exp-builder-gray400: #aec1cc;\n --exp-builder-gray500: #67728a;\n --exp-builder-gray600: #5a657c;\n --exp-builder-gray700: #414d63;\n --exp-builder-gray800: #1b273a;\n --exp-builder-gray900: #111b2b;\n --exp-builder-purple600: #6c3ecf;\n --exp-builder-red200: #ffe0e0;\n --exp-builder-red800: #7f0010;\n --exp-builder-color-white: #ffffff;\n --exp-builder-glow-primary: 0px 0px 0px 3px #e8f5ff;\n\n /* RGB colors for applying opacity */\n --exp-builder-blue100-rgb: 232, 245, 255;\n --exp-builder-blue300-rgb: 152, 203, 255;\n\n /* Spacing tokens from Forma 36: https://f36.contentful.com/tokens/spacing */\n --exp-builder-spacing-s: 0.75rem;\n --exp-builder-spacing-2xs: 0.25rem;\n\n /* Typography tokens from Forma 36: https://f36.contentful.com/tokens/typography */\n --exp-builder-font-size-l: 1rem;\n --exp-builder-font-size-m: 0.875rem;\n --exp-builder-font-stack-primary: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;\n --exp-builder-line-height-condensed: 1.25;\n}\n";
|
|
13
|
+
styleInject(css_248z$b);
|
|
14
14
|
|
|
15
15
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
16
16
|
const INCOMING_EVENTS$1 = {
|
|
@@ -34,7 +34,9 @@ const INCOMING_EVENTS$1 = {
|
|
|
34
34
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
35
35
|
HoverComponent: 'hoverComponent',
|
|
36
36
|
UpdatedEntity: 'updatedEntity',
|
|
37
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
37
38
|
AssembliesAdded: 'assembliesAdded',
|
|
39
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
38
40
|
AssembliesRegistered: 'assembliesRegistered',
|
|
39
41
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
40
42
|
MouseMove: 'mouseMove',
|
|
@@ -97,6 +99,7 @@ const CONTENTFUL_COMPONENTS$1 = {
|
|
|
97
99
|
name: 'Carousel',
|
|
98
100
|
},
|
|
99
101
|
};
|
|
102
|
+
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
100
103
|
const CF_STYLE_ATTRIBUTES = [
|
|
101
104
|
'cfVisibility',
|
|
102
105
|
'cfHorizontalAlignment',
|
|
@@ -2282,6 +2285,19 @@ function getTargetValueInPixels(targetWidthObject) {
|
|
|
2282
2285
|
return targetWidthObject.value;
|
|
2283
2286
|
}
|
|
2284
2287
|
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Creates a component definition for an assembly. As all assemblies use the same definition in the SDK,
|
|
2290
|
+
* all should be registered via this function.
|
|
2291
|
+
*/
|
|
2292
|
+
const createAssemblyDefinition = (definitionId) => {
|
|
2293
|
+
return {
|
|
2294
|
+
id: definitionId,
|
|
2295
|
+
name: 'Component',
|
|
2296
|
+
variables: {},
|
|
2297
|
+
children: true,
|
|
2298
|
+
category: ASSEMBLY_DEFAULT_CATEGORY,
|
|
2299
|
+
};
|
|
2300
|
+
};
|
|
2285
2301
|
|
|
2286
2302
|
class ParseError extends Error {
|
|
2287
2303
|
constructor(message) {
|
|
@@ -3190,7 +3206,9 @@ const INCOMING_EVENTS = {
|
|
|
3190
3206
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3191
3207
|
HoverComponent: 'hoverComponent',
|
|
3192
3208
|
UpdatedEntity: 'updatedEntity',
|
|
3209
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3193
3210
|
AssembliesAdded: 'assembliesAdded',
|
|
3211
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3194
3212
|
AssembliesRegistered: 'assembliesRegistered',
|
|
3195
3213
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3196
3214
|
MouseMove: 'mouseMove',
|
|
@@ -3215,7 +3233,6 @@ var StudioCanvasMode$2;
|
|
|
3215
3233
|
StudioCanvasMode["NONE"] = "none";
|
|
3216
3234
|
})(StudioCanvasMode$2 || (StudioCanvasMode$2 = {}));
|
|
3217
3235
|
const ASSEMBLY_NODE_TYPE = 'assembly';
|
|
3218
|
-
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
3219
3236
|
const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
|
|
3220
3237
|
const EMPTY_CONTAINER_SIZE = '80px';
|
|
3221
3238
|
const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
|
|
@@ -3374,34 +3391,7 @@ const useBreakpoints = (breakpoints) => {
|
|
|
3374
3391
|
return { resolveDesignValue };
|
|
3375
3392
|
};
|
|
3376
3393
|
|
|
3377
|
-
// Note: During development, the hot reloading might empty this and it
|
|
3378
|
-
// stays empty leading to not rendering assemblies. Ideally, this is
|
|
3379
|
-
// integrated into the state machine to keep track of its state.
|
|
3380
|
-
const assembliesRegistry = new Map([]);
|
|
3381
|
-
const setAssemblies = (assemblies) => {
|
|
3382
|
-
for (const assembly of assemblies) {
|
|
3383
|
-
assembliesRegistry.set(assembly.sys.id, assembly);
|
|
3384
|
-
}
|
|
3385
|
-
};
|
|
3386
3394
|
const componentRegistry = new Map();
|
|
3387
|
-
const addComponentRegistration = (componentRegistration) => {
|
|
3388
|
-
componentRegistry.set(componentRegistration.definition.id, componentRegistration);
|
|
3389
|
-
};
|
|
3390
|
-
const createAssemblyRegistration = ({ definitionId, definitionName, component, }) => {
|
|
3391
|
-
const componentRegistration = componentRegistry.get(definitionId);
|
|
3392
|
-
if (componentRegistration) {
|
|
3393
|
-
return componentRegistration;
|
|
3394
|
-
}
|
|
3395
|
-
const definition = {
|
|
3396
|
-
id: definitionId,
|
|
3397
|
-
name: definitionName || 'Component',
|
|
3398
|
-
variables: {},
|
|
3399
|
-
children: true,
|
|
3400
|
-
category: ASSEMBLY_DEFAULT_CATEGORY,
|
|
3401
|
-
};
|
|
3402
|
-
addComponentRegistration({ component, definition });
|
|
3403
|
-
return componentRegistry.get(definitionId);
|
|
3404
|
-
};
|
|
3405
3395
|
|
|
3406
3396
|
const useEditorStore = create((set, get) => ({
|
|
3407
3397
|
dataSource: {},
|
|
@@ -3441,41 +3431,313 @@ const useEditorStore = create((set, get) => ({
|
|
|
3441
3431
|
},
|
|
3442
3432
|
}));
|
|
3443
3433
|
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3434
|
+
function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
3435
|
+
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
3436
|
+
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
3437
|
+
const setEntitiesFetched = inMemoryEntitiesStore((state) => state.setEntitiesFetched);
|
|
3438
|
+
const resetEntityStore = inMemoryEntitiesStore((state) => state.resetEntityStore);
|
|
3439
|
+
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
3440
|
+
updateTree: state.updateTree,
|
|
3441
|
+
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
3442
|
+
}));
|
|
3443
|
+
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
3444
|
+
const dataSource = useEditorStore((state) => state.dataSource);
|
|
3445
|
+
const setLocale = useEditorStore((state) => state.setLocale);
|
|
3446
|
+
const setUnboundValues = useEditorStore((state) => state.setUnboundValues);
|
|
3447
|
+
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
3448
|
+
const reloadApp = () => {
|
|
3449
|
+
sendMessage(OUTGOING_EVENTS.CanvasReload, undefined);
|
|
3450
|
+
// Wait a moment to ensure that the message was sent
|
|
3451
|
+
setTimeout(() => {
|
|
3452
|
+
// Received a hot reload message from webpack dev server -> reload the canvas
|
|
3453
|
+
window.location.reload();
|
|
3454
|
+
}, 50);
|
|
3455
|
+
};
|
|
3456
|
+
useEffect(() => {
|
|
3457
|
+
sendMessage(OUTGOING_EVENTS.RequestComponentTreeUpdate, undefined);
|
|
3458
|
+
}, []);
|
|
3459
|
+
/**
|
|
3460
|
+
* Fills up entityStore with entities from newDataSource and from the tree.
|
|
3461
|
+
* Also manages "entity status" variables (areEntitiesFetched, isFetchingEntities)
|
|
3462
|
+
*/
|
|
3463
|
+
const fetchMissingEntities = useCallback(async (entityStore, newDataSource, tree) => {
|
|
3464
|
+
// if we realize that there's nothing missing and nothing to fill-fetch before we do any async call,
|
|
3465
|
+
// then we can simply return and not lock the EntityStore at all.
|
|
3466
|
+
const startFetching = () => {
|
|
3467
|
+
setEntitiesFetched(false);
|
|
3468
|
+
};
|
|
3469
|
+
const endFetching = () => {
|
|
3470
|
+
setEntitiesFetched(true);
|
|
3471
|
+
};
|
|
3472
|
+
// Prepare L1 entities and deepReferences
|
|
3473
|
+
const entityLinksL1 = Object.values(newDataSource);
|
|
3474
|
+
/**
|
|
3475
|
+
* Checks only for _missing_ L1 entities
|
|
3476
|
+
* WARNING: Does NOT check for entity staleness/versions. If an entity is stale, it will NOT be considered missing.
|
|
3477
|
+
* If ExperienceBuilder wants to update stale entities, it should post `▼UPDATED_ENTITY` message to SDK.
|
|
3478
|
+
*/
|
|
3479
|
+
const isMissingL1Entities = (entityLinks) => {
|
|
3480
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinks);
|
|
3481
|
+
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
3482
|
+
};
|
|
3483
|
+
/**
|
|
3484
|
+
* PRECONDITION: all L1 entities are fetched
|
|
3485
|
+
*/
|
|
3486
|
+
const isMissingL2Entities = (deepReferences) => {
|
|
3487
|
+
const referentLinks = deepReferences
|
|
3488
|
+
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
3489
|
+
.filter(isLink$1);
|
|
3490
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
3491
|
+
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
3492
|
+
};
|
|
3493
|
+
/**
|
|
3494
|
+
* POST_CONDITION: entityStore is has all L1 entities (aka headEntities)
|
|
3495
|
+
*/
|
|
3496
|
+
const fillupL1 = async ({ entityLinksL1, }) => {
|
|
3497
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinksL1);
|
|
3498
|
+
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
3499
|
+
};
|
|
3500
|
+
/**
|
|
3501
|
+
* PRECONDITION: all L1 entites are fetched
|
|
3502
|
+
*/
|
|
3503
|
+
const fillupL2 = async ({ deepReferences }) => {
|
|
3504
|
+
const referentLinks = deepReferences
|
|
3505
|
+
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
3506
|
+
.filter(isLink$1);
|
|
3507
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
3508
|
+
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
3509
|
+
};
|
|
3510
|
+
try {
|
|
3511
|
+
if (isMissingL1Entities(entityLinksL1)) {
|
|
3512
|
+
startFetching();
|
|
3513
|
+
await fillupL1({ entityLinksL1 });
|
|
3514
|
+
}
|
|
3515
|
+
const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource, entityStore.getEntityFromLink.bind(entityStore));
|
|
3516
|
+
if (isMissingL2Entities(deepReferences)) {
|
|
3517
|
+
startFetching();
|
|
3518
|
+
await fillupL2({ deepReferences });
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
catch (error) {
|
|
3522
|
+
debug$1.error('[experiences-visual-editor-react::useEditorSubscriber] Failed fetching entities', { error });
|
|
3523
|
+
throw error; // TODO: The original catch didn't let's rethrow; for the moment throw to see if we have any errors
|
|
3524
|
+
}
|
|
3525
|
+
finally {
|
|
3526
|
+
endFetching();
|
|
3527
|
+
}
|
|
3528
|
+
}, [setEntitiesFetched]);
|
|
3529
|
+
useEffect(() => {
|
|
3530
|
+
const onMessage = async (event) => {
|
|
3531
|
+
let reason;
|
|
3532
|
+
if ((reason = doesMismatchMessageSchema(event))) {
|
|
3533
|
+
if (event.origin.startsWith('http://localhost') &&
|
|
3534
|
+
`${event.data}`.includes('webpackHotUpdate')) {
|
|
3535
|
+
reloadApp();
|
|
3536
|
+
}
|
|
3537
|
+
else {
|
|
3538
|
+
debug$1.warn(`[experiences-visual-editor-react::onMessage] Ignoring alien incoming message from origin [${event.origin}], due to: [${reason}]`, event);
|
|
3539
|
+
}
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
const eventData = tryParseMessage(event);
|
|
3543
|
+
debug$1.debug(`[experiences-visual-editor-react::onMessage] Received message [${eventData.eventType}]`, eventData);
|
|
3544
|
+
if (eventData.eventType === PostMessageMethods$2.REQUESTED_ENTITIES) {
|
|
3545
|
+
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
switch (eventData.eventType) {
|
|
3549
|
+
case INCOMING_EVENTS.ExperienceUpdated: {
|
|
3550
|
+
const { tree, locale, changedNode, changedValueType } = eventData.payload;
|
|
3551
|
+
let newEntityStore = entityStore;
|
|
3552
|
+
if (entityStore.locale !== locale) {
|
|
3553
|
+
newEntityStore = new EditorModeEntityStore({ locale, entities: [] });
|
|
3554
|
+
setLocale(locale);
|
|
3555
|
+
resetEntityStore(newEntityStore);
|
|
3556
|
+
}
|
|
3557
|
+
// Below are mutually exclusive cases
|
|
3558
|
+
if (changedNode) {
|
|
3559
|
+
/**
|
|
3560
|
+
* On single node updates, we want to skip the process of getting the data (datasource and unbound values)
|
|
3561
|
+
* from tree. Since we know the updated node, we can skip that recursion everytime the tree updates and
|
|
3562
|
+
* just update the relevant data we need from the relevant node.
|
|
3563
|
+
*
|
|
3564
|
+
* We still update the tree here so we don't have a stale "tree"
|
|
3565
|
+
*/
|
|
3566
|
+
if (changedValueType === 'BoundValue') {
|
|
3567
|
+
const newDataSource = { ...dataSource, ...changedNode.data.dataSource };
|
|
3568
|
+
setDataSource(newDataSource);
|
|
3569
|
+
await fetchMissingEntities(newEntityStore, newDataSource, tree);
|
|
3570
|
+
}
|
|
3571
|
+
else if (changedValueType === 'UnboundValue') {
|
|
3572
|
+
setUnboundValues({
|
|
3573
|
+
...unboundValues,
|
|
3574
|
+
...changedNode.data.unboundValues,
|
|
3575
|
+
});
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
else {
|
|
3579
|
+
const { dataSource, unboundValues } = getDataFromTree(tree);
|
|
3580
|
+
setDataSource(dataSource);
|
|
3581
|
+
setUnboundValues(unboundValues);
|
|
3582
|
+
await fetchMissingEntities(newEntityStore, dataSource, tree);
|
|
3583
|
+
}
|
|
3584
|
+
// Update the tree when all necessary data is fetched and ready for rendering.
|
|
3585
|
+
updateTree(tree);
|
|
3586
|
+
break;
|
|
3587
|
+
}
|
|
3588
|
+
case INCOMING_EVENTS.AssembliesRegistered: {
|
|
3589
|
+
// Not necessary anymore since `patternResolution` which was introduced in 2024.
|
|
3590
|
+
break;
|
|
3591
|
+
}
|
|
3592
|
+
case INCOMING_EVENTS.AssembliesAdded: {
|
|
3593
|
+
// Not necessary anymore since `patternResolution` which was introduced in 2024.
|
|
3594
|
+
break;
|
|
3595
|
+
}
|
|
3596
|
+
case INCOMING_EVENTS.UpdatedEntity: {
|
|
3597
|
+
const { entity: updatedEntity, shouldRerender } = eventData.payload;
|
|
3598
|
+
if (updatedEntity) {
|
|
3599
|
+
const storedEntity = entityStore.entities.find((entity) => entity.sys.id === updatedEntity.sys.id);
|
|
3600
|
+
const didEntityChange = storedEntity?.sys.version !== updatedEntity.sys.version;
|
|
3601
|
+
entityStore.updateEntity(updatedEntity);
|
|
3602
|
+
// We traverse the whole tree, so this is a opt-in feature to only use it when required.
|
|
3603
|
+
if (shouldRerender && didEntityChange) {
|
|
3604
|
+
updateNodesByUpdatedEntity(updatedEntity.sys.id);
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
break;
|
|
3608
|
+
}
|
|
3609
|
+
case INCOMING_EVENTS.RequestEditorMode: {
|
|
3610
|
+
break;
|
|
3611
|
+
}
|
|
3612
|
+
default: {
|
|
3613
|
+
const knownEvents = Object.values(INCOMING_EVENTS);
|
|
3614
|
+
const isDeprecatedMessage = knownEvents.includes(eventData.eventType);
|
|
3615
|
+
if (!isDeprecatedMessage) {
|
|
3616
|
+
debug$1.error(`[experiences-visual-editor-react::onMessage] Logic error, unsupported eventType: [${eventData.eventType}]`);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
window.addEventListener('message', onMessage);
|
|
3622
|
+
return () => {
|
|
3623
|
+
window.removeEventListener('message', onMessage);
|
|
3624
|
+
};
|
|
3625
|
+
}, [
|
|
3626
|
+
entityStore,
|
|
3627
|
+
setDataSource,
|
|
3628
|
+
setLocale,
|
|
3629
|
+
dataSource,
|
|
3630
|
+
areEntitiesFetched,
|
|
3631
|
+
fetchMissingEntities,
|
|
3632
|
+
setUnboundValues,
|
|
3633
|
+
unboundValues,
|
|
3634
|
+
updateTree,
|
|
3635
|
+
updateNodesByUpdatedEntity,
|
|
3636
|
+
resetEntityStore,
|
|
3637
|
+
]);
|
|
3638
|
+
}
|
|
3476
3639
|
|
|
3477
|
-
|
|
3478
|
-
|
|
3640
|
+
const CircularDependencyErrorPlaceholder = ({ wrappingPatternIds, ...props }) => {
|
|
3641
|
+
return (React.createElement("div", { ...props, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
3642
|
+
border: '1px solid red',
|
|
3643
|
+
background: 'rgba(255, 0, 0, 0.1)',
|
|
3644
|
+
padding: '1rem 1rem 0 1rem',
|
|
3645
|
+
width: '100%',
|
|
3646
|
+
height: '100%',
|
|
3647
|
+
} },
|
|
3648
|
+
"Circular usage of patterns detected:",
|
|
3649
|
+
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
3650
|
+
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
3651
|
+
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
3652
|
+
const entryTitle = entry?.fields?.title;
|
|
3653
|
+
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
3654
|
+
return React.createElement("li", { key: patternId }, text);
|
|
3655
|
+
}))));
|
|
3656
|
+
};
|
|
3657
|
+
|
|
3658
|
+
class ImportedComponentError extends Error {
|
|
3659
|
+
constructor(message) {
|
|
3660
|
+
super(message);
|
|
3661
|
+
this.name = 'ImportedComponentError';
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
class ExperienceSDKError extends Error {
|
|
3665
|
+
constructor(message) {
|
|
3666
|
+
super(message);
|
|
3667
|
+
this.name = 'ExperienceSDKError';
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
class ImportedComponentErrorBoundary extends React.Component {
|
|
3671
|
+
componentDidCatch(error, _errorInfo) {
|
|
3672
|
+
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
3673
|
+
// This error was already handled by a nested error boundary and should be passed upwards
|
|
3674
|
+
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
3675
|
+
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
3676
|
+
throw error;
|
|
3677
|
+
}
|
|
3678
|
+
// Differentiate between custom and SDK-provided components for error tracking
|
|
3679
|
+
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
3680
|
+
? ExperienceSDKError
|
|
3681
|
+
: ImportedComponentError;
|
|
3682
|
+
const err = new ErrorClass(error.message);
|
|
3683
|
+
err.stack = error.stack;
|
|
3684
|
+
throw err;
|
|
3685
|
+
}
|
|
3686
|
+
render() {
|
|
3687
|
+
return this.props.children;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
|
|
3691
|
+
const MissingComponentPlaceholder = ({ blockId }) => {
|
|
3692
|
+
return (React.createElement("div", { style: {
|
|
3693
|
+
border: '1px solid red',
|
|
3694
|
+
width: '100%',
|
|
3695
|
+
height: '100%',
|
|
3696
|
+
} },
|
|
3697
|
+
"Missing component '",
|
|
3698
|
+
blockId,
|
|
3699
|
+
"'"));
|
|
3700
|
+
};
|
|
3701
|
+
|
|
3702
|
+
var css_248z$a = ".EditorBlock-module_emptySlot__za-Bi {\n min-height: 80px;\n min-width: 80px;\n}\n";
|
|
3703
|
+
var styles$1 = {"emptySlot":"EditorBlock-module_emptySlot__za-Bi"};
|
|
3704
|
+
styleInject(css_248z$a);
|
|
3705
|
+
|
|
3706
|
+
var css_248z$8 = ":root{--cf-color-white:#fff;--cf-color-black:#000;--cf-color-gray100:#f7f9fa;--cf-color-gray400:#aec1cc;--cf-color-gray400-rgb:174,193,204;--cf-spacing-0:0rem;--cf-spacing-1:0.125rem;--cf-spacing-2:0.25rem;--cf-spacing-3:0.375rem;--cf-spacing-4:0.5rem;--cf-spacing-5:0.625rem;--cf-spacing-6:0.75rem;--cf-spacing-7:0.875rem;--cf-spacing-8:1rem;--cf-spacing-9:1.25rem;--cf-spacing-10:1.5rem;--cf-spacing-11:1.75rem;--cf-spacing-12:2rem;--cf-spacing-13:2.25rem;--cf-text-xs:0.75rem;--cf-text-sm:0.875rem;--cf-text-base:1rem;--cf-text-lg:1.125rem;--cf-text-xl:1.25rem;--cf-text-2xl:1.5rem;--cf-text-3xl:2rem;--cf-text-4xl:2.75rem;--cf-font-light:300;--cf-font-normal:400;--cf-font-medium:500;--cf-font-semibold:600;--cf-font-bold:700;--cf-font-extra-bold:800;--cf-font-black:900;--cf-border-radius-none:0px;--cf-border-radius-sm:0.125rem;--cf-border-radius:0.25rem;--cf-border-radius-md:0.375rem;--cf-border-radius-lg:0.5rem;--cf-border-radius-xl:0.75rem;--cf-border-radius-2xl:1rem;--cf-border-radius-3xl:1.5rem;--cf-border-radius-full:9999px;--cf-max-width-full:100%;--cf-button-bg:var(--cf-color-black);--cf-button-color:var(--cf-color-white);--cf-text-color:var(--cf-color-black)}*{box-sizing:border-box}";
|
|
3707
|
+
styleInject(css_248z$8);
|
|
3708
|
+
|
|
3709
|
+
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3710
|
+
/**
|
|
3711
|
+
* These modes are ONLY intended to be internally used within the context of
|
|
3712
|
+
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
3713
|
+
* intentionally do not include preview/delivery modes.
|
|
3714
|
+
*/
|
|
3715
|
+
var StudioCanvasMode$1;
|
|
3716
|
+
(function (StudioCanvasMode) {
|
|
3717
|
+
StudioCanvasMode["READ_ONLY"] = "readOnlyMode";
|
|
3718
|
+
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
3719
|
+
StudioCanvasMode["NONE"] = "none";
|
|
3720
|
+
})(StudioCanvasMode$1 || (StudioCanvasMode$1 = {}));
|
|
3721
|
+
var PostMessageMethods$1;
|
|
3722
|
+
(function (PostMessageMethods) {
|
|
3723
|
+
PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
|
|
3724
|
+
PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
|
|
3725
|
+
})(PostMessageMethods$1 || (PostMessageMethods$1 = {}));
|
|
3726
|
+
|
|
3727
|
+
var css_248z$7 = ".cf-button:empty:before{content:\"\";display:inline-block}";
|
|
3728
|
+
styleInject(css_248z$7);
|
|
3729
|
+
|
|
3730
|
+
var css_248z$6 = ".cf-heading{white-space:pre-line}";
|
|
3731
|
+
styleInject(css_248z$6);
|
|
3732
|
+
|
|
3733
|
+
var css_248z$5 = ".cf-richtext{white-space:pre-line}.cf-richtext>:first-child{margin-top:0}.cf-richtext>:last-child{margin-bottom:0}";
|
|
3734
|
+
styleInject(css_248z$5);
|
|
3735
|
+
|
|
3736
|
+
var css_248z$4 = ".cf-text{white-space:pre-line}.cf-text-link .cf-text{margin:0}";
|
|
3737
|
+
styleInject(css_248z$4);
|
|
3738
|
+
|
|
3739
|
+
var css_248z$3 = "div.cf-placeholder-wrapper{outline:2px solid rgba(var(--cf-color-gray400-rgb),.5);outline-offset:-2px;overflow:hidden;position:relative}img.cf-placeholder-image{background-color:var(--cf-color-gray100);height:100%;width:100%}svg.cf-placeholder-icon{height:var(--cf-text-3xl);left:50%;max-height:100%;max-width:100%;position:absolute;top:50%;transform:translate(-50%,-50%);width:var(--cf-text-3xl)}svg.cf-placeholder-icon path{fill:var(--cf-color-gray400)}";
|
|
3740
|
+
styleInject(css_248z$3);
|
|
3479
3741
|
|
|
3480
3742
|
/**
|
|
3481
3743
|
* These modes are ONLY intended to be internally used within the context of
|
|
@@ -4648,8 +4910,8 @@ var VisualEditorMode;
|
|
|
4648
4910
|
VisualEditorMode["InjectScript"] = "injectScript";
|
|
4649
4911
|
})(VisualEditorMode || (VisualEditorMode = {}));
|
|
4650
4912
|
|
|
4651
|
-
var css_248z$2
|
|
4652
|
-
styleInject(css_248z$2
|
|
4913
|
+
var css_248z$2 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.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}";
|
|
4914
|
+
styleInject(css_248z$2);
|
|
4653
4915
|
|
|
4654
4916
|
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) => {
|
|
4655
4917
|
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
@@ -4686,315 +4948,16 @@ const Assembly = (props) => {
|
|
|
4686
4948
|
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4687
4949
|
};
|
|
4688
4950
|
|
|
4689
|
-
function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
4690
|
-
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
4691
|
-
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
4692
|
-
const setEntitiesFetched = inMemoryEntitiesStore((state) => state.setEntitiesFetched);
|
|
4693
|
-
const resetEntityStore = inMemoryEntitiesStore((state) => state.resetEntityStore);
|
|
4694
|
-
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
4695
|
-
updateTree: state.updateTree,
|
|
4696
|
-
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
4697
|
-
}));
|
|
4698
|
-
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
4699
|
-
const dataSource = useEditorStore((state) => state.dataSource);
|
|
4700
|
-
const setLocale = useEditorStore((state) => state.setLocale);
|
|
4701
|
-
const setUnboundValues = useEditorStore((state) => state.setUnboundValues);
|
|
4702
|
-
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
4703
|
-
const reloadApp = () => {
|
|
4704
|
-
sendMessage(OUTGOING_EVENTS.CanvasReload, undefined);
|
|
4705
|
-
// Wait a moment to ensure that the message was sent
|
|
4706
|
-
setTimeout(() => {
|
|
4707
|
-
// Received a hot reload message from webpack dev server -> reload the canvas
|
|
4708
|
-
window.location.reload();
|
|
4709
|
-
}, 50);
|
|
4710
|
-
};
|
|
4711
|
-
useEffect(() => {
|
|
4712
|
-
sendMessage(OUTGOING_EVENTS.RequestComponentTreeUpdate, undefined);
|
|
4713
|
-
}, []);
|
|
4714
|
-
/**
|
|
4715
|
-
* Fills up entityStore with entities from newDataSource and from the tree.
|
|
4716
|
-
* Also manages "entity status" variables (areEntitiesFetched, isFetchingEntities)
|
|
4717
|
-
*/
|
|
4718
|
-
const fetchMissingEntities = useCallback(async (entityStore, newDataSource, tree) => {
|
|
4719
|
-
// if we realize that there's nothing missing and nothing to fill-fetch before we do any async call,
|
|
4720
|
-
// then we can simply return and not lock the EntityStore at all.
|
|
4721
|
-
const startFetching = () => {
|
|
4722
|
-
setEntitiesFetched(false);
|
|
4723
|
-
};
|
|
4724
|
-
const endFetching = () => {
|
|
4725
|
-
setEntitiesFetched(true);
|
|
4726
|
-
};
|
|
4727
|
-
// Prepare L1 entities and deepReferences
|
|
4728
|
-
const entityLinksL1 = [
|
|
4729
|
-
...Object.values(newDataSource),
|
|
4730
|
-
...assembliesRegistry.values(), // we count assemblies here as "L1 entities", for convenience. Even though they're not headEntities.
|
|
4731
|
-
];
|
|
4732
|
-
/**
|
|
4733
|
-
* Checks only for _missing_ L1 entities
|
|
4734
|
-
* WARNING: Does NOT check for entity staleness/versions. If an entity is stale, it will NOT be considered missing.
|
|
4735
|
-
* If ExperienceBuilder wants to update stale entities, it should post `▼UPDATED_ENTITY` message to SDK.
|
|
4736
|
-
*/
|
|
4737
|
-
const isMissingL1Entities = (entityLinks) => {
|
|
4738
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinks);
|
|
4739
|
-
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
4740
|
-
};
|
|
4741
|
-
/**
|
|
4742
|
-
* PRECONDITION: all L1 entities are fetched
|
|
4743
|
-
*/
|
|
4744
|
-
const isMissingL2Entities = (deepReferences) => {
|
|
4745
|
-
const referentLinks = deepReferences
|
|
4746
|
-
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4747
|
-
.filter(isLink$1);
|
|
4748
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4749
|
-
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
4750
|
-
};
|
|
4751
|
-
/**
|
|
4752
|
-
* POST_CONDITION: entityStore is has all L1 entities (aka headEntities)
|
|
4753
|
-
*/
|
|
4754
|
-
const fillupL1 = async ({ entityLinksL1, }) => {
|
|
4755
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinksL1);
|
|
4756
|
-
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
4757
|
-
};
|
|
4758
|
-
/**
|
|
4759
|
-
* PRECONDITION: all L1 entites are fetched
|
|
4760
|
-
*/
|
|
4761
|
-
const fillupL2 = async ({ deepReferences }) => {
|
|
4762
|
-
const referentLinks = deepReferences
|
|
4763
|
-
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4764
|
-
.filter(isLink$1);
|
|
4765
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4766
|
-
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
4767
|
-
};
|
|
4768
|
-
try {
|
|
4769
|
-
if (isMissingL1Entities(entityLinksL1)) {
|
|
4770
|
-
startFetching();
|
|
4771
|
-
await fillupL1({ entityLinksL1 });
|
|
4772
|
-
}
|
|
4773
|
-
const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource, entityStore.getEntityFromLink.bind(entityStore));
|
|
4774
|
-
if (isMissingL2Entities(deepReferences)) {
|
|
4775
|
-
startFetching();
|
|
4776
|
-
await fillupL2({ deepReferences });
|
|
4777
|
-
}
|
|
4778
|
-
}
|
|
4779
|
-
catch (error) {
|
|
4780
|
-
debug$1.error('[experiences-visual-editor-react::useEditorSubscriber] Failed fetching entities', { error });
|
|
4781
|
-
throw error; // TODO: The original catch didn't let's rethrow; for the moment throw to see if we have any errors
|
|
4782
|
-
}
|
|
4783
|
-
finally {
|
|
4784
|
-
endFetching();
|
|
4785
|
-
}
|
|
4786
|
-
}, [setEntitiesFetched /* setFetchingEntities, assembliesRegistry */]);
|
|
4787
|
-
useEffect(() => {
|
|
4788
|
-
const onMessage = async (event) => {
|
|
4789
|
-
let reason;
|
|
4790
|
-
if ((reason = doesMismatchMessageSchema(event))) {
|
|
4791
|
-
if (event.origin.startsWith('http://localhost') &&
|
|
4792
|
-
`${event.data}`.includes('webpackHotUpdate')) {
|
|
4793
|
-
reloadApp();
|
|
4794
|
-
}
|
|
4795
|
-
else {
|
|
4796
|
-
debug$1.warn(`[experiences-visual-editor-react::onMessage] Ignoring alien incoming message from origin [${event.origin}], due to: [${reason}]`, event);
|
|
4797
|
-
}
|
|
4798
|
-
return;
|
|
4799
|
-
}
|
|
4800
|
-
const eventData = tryParseMessage(event);
|
|
4801
|
-
debug$1.debug(`[experiences-visual-editor-react::onMessage] Received message [${eventData.eventType}]`, eventData);
|
|
4802
|
-
if (eventData.eventType === PostMessageMethods$2.REQUESTED_ENTITIES) {
|
|
4803
|
-
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
4804
|
-
return;
|
|
4805
|
-
}
|
|
4806
|
-
switch (eventData.eventType) {
|
|
4807
|
-
case INCOMING_EVENTS.ExperienceUpdated: {
|
|
4808
|
-
const { tree, locale, changedNode, changedValueType, assemblies } = eventData.payload;
|
|
4809
|
-
// Make sure to first store the assemblies before setting the tree and thus triggering a rerender
|
|
4810
|
-
if (assemblies) {
|
|
4811
|
-
setAssemblies(assemblies);
|
|
4812
|
-
// If the assemblyEntry is not yet fetched, this will be done below by
|
|
4813
|
-
// the imperative calls to fetchMissingEntities.
|
|
4814
|
-
}
|
|
4815
|
-
let newEntityStore = entityStore;
|
|
4816
|
-
if (entityStore.locale !== locale) {
|
|
4817
|
-
newEntityStore = new EditorModeEntityStore({ locale, entities: [] });
|
|
4818
|
-
setLocale(locale);
|
|
4819
|
-
resetEntityStore(newEntityStore);
|
|
4820
|
-
}
|
|
4821
|
-
// Below are mutually exclusive cases
|
|
4822
|
-
if (changedNode) {
|
|
4823
|
-
/**
|
|
4824
|
-
* On single node updates, we want to skip the process of getting the data (datasource and unbound values)
|
|
4825
|
-
* from tree. Since we know the updated node, we can skip that recursion everytime the tree updates and
|
|
4826
|
-
* just update the relevant data we need from the relevant node.
|
|
4827
|
-
*
|
|
4828
|
-
* We still update the tree here so we don't have a stale "tree"
|
|
4829
|
-
*/
|
|
4830
|
-
if (changedValueType === 'BoundValue') {
|
|
4831
|
-
const newDataSource = { ...dataSource, ...changedNode.data.dataSource };
|
|
4832
|
-
setDataSource(newDataSource);
|
|
4833
|
-
await fetchMissingEntities(newEntityStore, newDataSource, tree);
|
|
4834
|
-
}
|
|
4835
|
-
else if (changedValueType === 'UnboundValue') {
|
|
4836
|
-
setUnboundValues({
|
|
4837
|
-
...unboundValues,
|
|
4838
|
-
...changedNode.data.unboundValues,
|
|
4839
|
-
});
|
|
4840
|
-
}
|
|
4841
|
-
}
|
|
4842
|
-
else {
|
|
4843
|
-
const { dataSource, unboundValues } = getDataFromTree(tree);
|
|
4844
|
-
setDataSource(dataSource);
|
|
4845
|
-
setUnboundValues(unboundValues);
|
|
4846
|
-
await fetchMissingEntities(newEntityStore, dataSource, tree);
|
|
4847
|
-
}
|
|
4848
|
-
// Update the tree when all necessary data is fetched and ready for rendering.
|
|
4849
|
-
updateTree(tree);
|
|
4850
|
-
break;
|
|
4851
|
-
}
|
|
4852
|
-
case INCOMING_EVENTS.AssembliesRegistered: {
|
|
4853
|
-
const { assemblies } = eventData.payload;
|
|
4854
|
-
assemblies.forEach((definition) => {
|
|
4855
|
-
addComponentRegistration({
|
|
4856
|
-
component: Assembly,
|
|
4857
|
-
definition,
|
|
4858
|
-
});
|
|
4859
|
-
});
|
|
4860
|
-
break;
|
|
4861
|
-
}
|
|
4862
|
-
case INCOMING_EVENTS.AssembliesAdded: {
|
|
4863
|
-
const { assembly, assemblyDefinition, } = eventData.payload;
|
|
4864
|
-
entityStore.updateEntity(assembly);
|
|
4865
|
-
// Using a Map here to avoid setting state and rerending all existing assemblies when a new assembly is added
|
|
4866
|
-
// TODO: Figure out if we can extend this love to data source and unbound values. Maybe that'll solve the blink
|
|
4867
|
-
// of all bound and unbound values when new values are added
|
|
4868
|
-
assembliesRegistry.set(assembly.sys.id, {
|
|
4869
|
-
sys: { id: assembly.sys.id, linkType: 'Entry', type: 'Link' },
|
|
4870
|
-
});
|
|
4871
|
-
if (assemblyDefinition) {
|
|
4872
|
-
addComponentRegistration({
|
|
4873
|
-
component: Assembly,
|
|
4874
|
-
definition: assemblyDefinition,
|
|
4875
|
-
});
|
|
4876
|
-
}
|
|
4877
|
-
break;
|
|
4878
|
-
}
|
|
4879
|
-
case INCOMING_EVENTS.UpdatedEntity: {
|
|
4880
|
-
const { entity: updatedEntity, shouldRerender } = eventData.payload;
|
|
4881
|
-
if (updatedEntity) {
|
|
4882
|
-
const storedEntity = entityStore.entities.find((entity) => entity.sys.id === updatedEntity.sys.id);
|
|
4883
|
-
const didEntityChange = storedEntity?.sys.version !== updatedEntity.sys.version;
|
|
4884
|
-
entityStore.updateEntity(updatedEntity);
|
|
4885
|
-
// We traverse the whole tree, so this is a opt-in feature to only use it when required.
|
|
4886
|
-
if (shouldRerender && didEntityChange) {
|
|
4887
|
-
updateNodesByUpdatedEntity(updatedEntity.sys.id);
|
|
4888
|
-
}
|
|
4889
|
-
}
|
|
4890
|
-
break;
|
|
4891
|
-
}
|
|
4892
|
-
case INCOMING_EVENTS.RequestEditorMode: {
|
|
4893
|
-
break;
|
|
4894
|
-
}
|
|
4895
|
-
default: {
|
|
4896
|
-
const knownEvents = Object.values(INCOMING_EVENTS);
|
|
4897
|
-
const isDeprecatedMessage = knownEvents.includes(eventData.eventType);
|
|
4898
|
-
if (!isDeprecatedMessage) {
|
|
4899
|
-
debug$1.error(`[experiences-visual-editor-react::onMessage] Logic error, unsupported eventType: [${eventData.eventType}]`);
|
|
4900
|
-
}
|
|
4901
|
-
}
|
|
4902
|
-
}
|
|
4903
|
-
};
|
|
4904
|
-
window.addEventListener('message', onMessage);
|
|
4905
|
-
return () => {
|
|
4906
|
-
window.removeEventListener('message', onMessage);
|
|
4907
|
-
};
|
|
4908
|
-
}, [
|
|
4909
|
-
entityStore,
|
|
4910
|
-
setDataSource,
|
|
4911
|
-
setLocale,
|
|
4912
|
-
dataSource,
|
|
4913
|
-
areEntitiesFetched,
|
|
4914
|
-
fetchMissingEntities,
|
|
4915
|
-
setUnboundValues,
|
|
4916
|
-
unboundValues,
|
|
4917
|
-
updateTree,
|
|
4918
|
-
updateNodesByUpdatedEntity,
|
|
4919
|
-
resetEntityStore,
|
|
4920
|
-
]);
|
|
4921
|
-
}
|
|
4922
|
-
|
|
4923
|
-
const CircularDependencyErrorPlaceholder = ({ wrappingPatternIds, ...props }) => {
|
|
4924
|
-
return (React.createElement("div", { ...props, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
4925
|
-
border: '1px solid red',
|
|
4926
|
-
background: 'rgba(255, 0, 0, 0.1)',
|
|
4927
|
-
padding: '1rem 1rem 0 1rem',
|
|
4928
|
-
width: '100%',
|
|
4929
|
-
height: '100%',
|
|
4930
|
-
} },
|
|
4931
|
-
"Circular usage of patterns detected:",
|
|
4932
|
-
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
4933
|
-
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
4934
|
-
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
4935
|
-
const entryTitle = entry?.fields?.title;
|
|
4936
|
-
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
4937
|
-
return React.createElement("li", { key: patternId }, text);
|
|
4938
|
-
}))));
|
|
4939
|
-
};
|
|
4940
|
-
|
|
4941
|
-
class ImportedComponentError extends Error {
|
|
4942
|
-
constructor(message) {
|
|
4943
|
-
super(message);
|
|
4944
|
-
this.name = 'ImportedComponentError';
|
|
4945
|
-
}
|
|
4946
|
-
}
|
|
4947
|
-
class ExperienceSDKError extends Error {
|
|
4948
|
-
constructor(message) {
|
|
4949
|
-
super(message);
|
|
4950
|
-
this.name = 'ExperienceSDKError';
|
|
4951
|
-
}
|
|
4952
|
-
}
|
|
4953
|
-
class ImportedComponentErrorBoundary extends React.Component {
|
|
4954
|
-
componentDidCatch(error, _errorInfo) {
|
|
4955
|
-
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
4956
|
-
// This error was already handled by a nested error boundary and should be passed upwards
|
|
4957
|
-
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
4958
|
-
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
4959
|
-
throw error;
|
|
4960
|
-
}
|
|
4961
|
-
// Differentiate between custom and SDK-provided components for error tracking
|
|
4962
|
-
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
4963
|
-
? ExperienceSDKError
|
|
4964
|
-
: ImportedComponentError;
|
|
4965
|
-
const err = new ErrorClass(error.message);
|
|
4966
|
-
err.stack = error.stack;
|
|
4967
|
-
throw err;
|
|
4968
|
-
}
|
|
4969
|
-
render() {
|
|
4970
|
-
return this.props.children;
|
|
4971
|
-
}
|
|
4972
|
-
}
|
|
4973
|
-
|
|
4974
|
-
const MissingComponentPlaceholder = ({ blockId }) => {
|
|
4975
|
-
return (React.createElement("div", { style: {
|
|
4976
|
-
border: '1px solid red',
|
|
4977
|
-
width: '100%',
|
|
4978
|
-
height: '100%',
|
|
4979
|
-
} },
|
|
4980
|
-
"Missing component '",
|
|
4981
|
-
blockId,
|
|
4982
|
-
"'"));
|
|
4983
|
-
};
|
|
4984
|
-
|
|
4985
|
-
var css_248z$2 = ".EditorBlock-module_emptySlot__za-Bi {\n min-height: 80px;\n min-width: 80px;\n}\n";
|
|
4986
|
-
var styles$1 = {"emptySlot":"EditorBlock-module_emptySlot__za-Bi"};
|
|
4987
|
-
styleInject(css_248z$2);
|
|
4988
|
-
|
|
4989
4951
|
const useComponentRegistration = (node) => {
|
|
4990
4952
|
return useMemo(() => {
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
definitionId: node.data.blockId,
|
|
4953
|
+
if (node.type === ASSEMBLY_NODE_TYPE) {
|
|
4954
|
+
// The definition and component are the same for all assemblies
|
|
4955
|
+
return {
|
|
4995
4956
|
component: Assembly,
|
|
4996
|
-
|
|
4957
|
+
definition: createAssemblyDefinition(node.data.blockId),
|
|
4958
|
+
};
|
|
4997
4959
|
}
|
|
4960
|
+
const registration = componentRegistry.get(node.data.blockId);
|
|
4998
4961
|
if (!registration) {
|
|
4999
4962
|
debug$1.warn(`[experiences-visual-editor-react::useComponentRegistration] Component registration not found for component with id: "${node.data.blockId}". The registered component might have been removed from the code. To proceed, remove the component manually from the layers tab.`);
|
|
5000
4963
|
return undefined;
|