@contentful/experiences-visual-editor-react 3.1.1 → 3.2.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.js +128 -53
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +128 -53
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -631,35 +631,69 @@ const breakpointsRefinement$1 = (value, ctx) => {
|
|
|
631
631
|
code: z.ZodIssueCode.custom,
|
|
632
632
|
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
633
633
|
});
|
|
634
|
+
return;
|
|
634
635
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
return
|
|
639
|
-
}
|
|
636
|
+
// Return early if there's only one generic breakpoint
|
|
637
|
+
const hasNoBreakpointsStrategy = value.length === 1;
|
|
638
|
+
if (hasNoBreakpointsStrategy) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
// Check if any breakpoint id occurs twice
|
|
642
|
+
const ids = value.map((breakpoint) => breakpoint.id);
|
|
643
|
+
const hasDuplicateIds = new Set(ids).size !== ids.length;
|
|
640
644
|
if (hasDuplicateIds) {
|
|
641
645
|
ctx.addIssue({
|
|
642
646
|
code: z.ZodIssueCode.custom,
|
|
643
647
|
message: `Breakpoint IDs must be unique`,
|
|
644
648
|
});
|
|
649
|
+
return;
|
|
645
650
|
}
|
|
646
|
-
//
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
// Skip the first one which is guaranteed to be a wildcard query
|
|
652
|
+
const nonBaseBreakpoints = value.slice(1);
|
|
653
|
+
const isMobileFirstStrategy = nonBaseBreakpoints[0].query.startsWith('>');
|
|
654
|
+
const isDesktopFirstStrategy = nonBaseBreakpoints[0].query.startsWith('<');
|
|
655
|
+
if (isMobileFirstStrategy) {
|
|
656
|
+
const areOperatorsEqual = nonBaseBreakpoints.every(({ query }) => query.startsWith('>'));
|
|
657
|
+
if (!areOperatorsEqual) {
|
|
658
|
+
ctx.addIssue({
|
|
659
|
+
code: z.ZodIssueCode.custom,
|
|
660
|
+
message: `Breakpoint queries must be in the format ">[size]px" for mobile-first strategy`,
|
|
661
|
+
});
|
|
653
662
|
}
|
|
654
|
-
|
|
655
|
-
|
|
663
|
+
// Extract the queries boundary by removing the special characters around it
|
|
664
|
+
const queries = nonBaseBreakpoints.map((bp) => parseInt(bp.query.replace(/px|<|>/, '')));
|
|
665
|
+
// Starting with the third breakpoint, check that every query is higher than the one above
|
|
666
|
+
const isIncreasing = queries.every((value, index, array) => index === 0 || value > array[index - 1]);
|
|
667
|
+
if (!isIncreasing) {
|
|
668
|
+
ctx.addIssue({
|
|
669
|
+
code: z.ZodIssueCode.custom,
|
|
670
|
+
message: `When using a mobile-first strategy, all breakpoints must have strictly increasing pixel values`,
|
|
671
|
+
});
|
|
656
672
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
673
|
+
}
|
|
674
|
+
else if (isDesktopFirstStrategy) {
|
|
675
|
+
const areOperatorsEqual = nonBaseBreakpoints.every(({ query }) => query.startsWith('<'));
|
|
676
|
+
if (!areOperatorsEqual) {
|
|
677
|
+
ctx.addIssue({
|
|
678
|
+
code: z.ZodIssueCode.custom,
|
|
679
|
+
message: `Breakpoint queries must be in the format "<[size]px" for desktop-first strategy`,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
// Extract the queries boundary by removing the special characters around it
|
|
683
|
+
const queries = nonBaseBreakpoints.map((bp) => parseInt(bp.query.replace(/px|<|>/, '')));
|
|
684
|
+
// Starting with the third breakpoint, check that every query is lower than the one above
|
|
685
|
+
const isDecreasing = queries.every((value, index, array) => index === 0 || value < array[index - 1]);
|
|
686
|
+
if (!isDecreasing) {
|
|
687
|
+
ctx.addIssue({
|
|
688
|
+
code: z.ZodIssueCode.custom,
|
|
689
|
+
message: `When using a desktop-first strategy, all breakpoints must have strictly decreasing pixel values`,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
else if (!isMobileFirstStrategy && !isDesktopFirstStrategy) {
|
|
660
694
|
ctx.addIssue({
|
|
661
695
|
code: z.ZodIssueCode.custom,
|
|
662
|
-
message: `
|
|
696
|
+
message: `You may only use a mobile-first or desktop-first strategy for breakpoints using '<' or '>' queries`,
|
|
663
697
|
});
|
|
664
698
|
}
|
|
665
699
|
};
|
|
@@ -718,7 +752,7 @@ const ParametersSchema$1 = z.record(propertyKeySchema$1, ParameterSchema$1);
|
|
|
718
752
|
const BreakpointSchema$1 = z
|
|
719
753
|
.object({
|
|
720
754
|
id: propertyKeySchema$1,
|
|
721
|
-
query: z.string().regex(
|
|
755
|
+
query: z.string().regex(/^\*$|^[<>][0-9*]+px$/),
|
|
722
756
|
previewSize: z.string(),
|
|
723
757
|
displayName: z.string(),
|
|
724
758
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
@@ -996,7 +1030,7 @@ const toCSSMediaQuery = ({ query }) => {
|
|
|
996
1030
|
};
|
|
997
1031
|
// Remove this helper when upgrading to TypeScript 5.0 - https://github.com/microsoft/TypeScript/issues/48829
|
|
998
1032
|
const findLast = (array, predicate) => {
|
|
999
|
-
return array.reverse().find(predicate);
|
|
1033
|
+
return [...array].reverse().find(predicate);
|
|
1000
1034
|
};
|
|
1001
1035
|
// Initialise media query matchers. This won't include the always matching fallback breakpoint.
|
|
1002
1036
|
const mediaQueryMatcher = (breakpoints) => {
|
|
@@ -1016,19 +1050,19 @@ const mediaQueryMatcher = (breakpoints) => {
|
|
|
1016
1050
|
return [mediaQueryMatchers, mediaQueryMatches];
|
|
1017
1051
|
};
|
|
1018
1052
|
const getActiveBreakpointIndex = (breakpoints, mediaQueryMatches, fallbackBreakpointIndex) => {
|
|
1019
|
-
// The breakpoints are ordered (desktop-first: descending by screen width)
|
|
1053
|
+
// The breakpoints are ordered (desktop-first: descending by screen width, mobile-first: ascending by screen width).
|
|
1020
1054
|
const breakpointsWithMatches = breakpoints.map(({ id }, index) => ({
|
|
1021
1055
|
id,
|
|
1022
1056
|
index,
|
|
1023
1057
|
// The fallback breakpoint with wildcard query will always match
|
|
1024
1058
|
isMatch: mediaQueryMatches[id] ?? index === fallbackBreakpointIndex,
|
|
1025
1059
|
}));
|
|
1026
|
-
// Find the last breakpoint in the list that matches (desktop-first: the narrowest one)
|
|
1060
|
+
// Find the last breakpoint in the list that matches (desktop-first: the narrowest one, mobile-first: the widest one)
|
|
1027
1061
|
const mostSpecificIndex = findLast(breakpointsWithMatches, ({ isMatch }) => isMatch)?.index;
|
|
1028
1062
|
return mostSpecificIndex ?? fallbackBreakpointIndex;
|
|
1029
1063
|
};
|
|
1030
1064
|
const getFallbackBreakpointIndex = (breakpoints) => {
|
|
1031
|
-
//
|
|
1065
|
+
// The validation ensures that there will be exactly one breakpoint using the wildcard query.
|
|
1032
1066
|
// If there is none, we just take the first one in the list.
|
|
1033
1067
|
return Math.max(breakpoints.findIndex(({ query }) => query === '*'), 0);
|
|
1034
1068
|
};
|
|
@@ -3314,35 +3348,69 @@ const breakpointsRefinement = (value, ctx) => {
|
|
|
3314
3348
|
code: z.ZodIssueCode.custom,
|
|
3315
3349
|
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
3316
3350
|
});
|
|
3351
|
+
return;
|
|
3317
3352
|
}
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
return
|
|
3322
|
-
}
|
|
3353
|
+
// Return early if there's only one generic breakpoint
|
|
3354
|
+
const hasNoBreakpointsStrategy = value.length === 1;
|
|
3355
|
+
if (hasNoBreakpointsStrategy) {
|
|
3356
|
+
return;
|
|
3357
|
+
}
|
|
3358
|
+
// Check if any breakpoint id occurs twice
|
|
3359
|
+
const ids = value.map((breakpoint) => breakpoint.id);
|
|
3360
|
+
const hasDuplicateIds = new Set(ids).size !== ids.length;
|
|
3323
3361
|
if (hasDuplicateIds) {
|
|
3324
3362
|
ctx.addIssue({
|
|
3325
3363
|
code: z.ZodIssueCode.custom,
|
|
3326
3364
|
message: `Breakpoint IDs must be unique`,
|
|
3327
3365
|
});
|
|
3366
|
+
return;
|
|
3328
3367
|
}
|
|
3329
|
-
//
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
const
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3368
|
+
// Skip the first one which is guaranteed to be a wildcard query
|
|
3369
|
+
const nonBaseBreakpoints = value.slice(1);
|
|
3370
|
+
const isMobileFirstStrategy = nonBaseBreakpoints[0].query.startsWith('>');
|
|
3371
|
+
const isDesktopFirstStrategy = nonBaseBreakpoints[0].query.startsWith('<');
|
|
3372
|
+
if (isMobileFirstStrategy) {
|
|
3373
|
+
const areOperatorsEqual = nonBaseBreakpoints.every(({ query }) => query.startsWith('>'));
|
|
3374
|
+
if (!areOperatorsEqual) {
|
|
3375
|
+
ctx.addIssue({
|
|
3376
|
+
code: z.ZodIssueCode.custom,
|
|
3377
|
+
message: `Breakpoint queries must be in the format ">[size]px" for mobile-first strategy`,
|
|
3378
|
+
});
|
|
3336
3379
|
}
|
|
3337
|
-
|
|
3338
|
-
|
|
3380
|
+
// Extract the queries boundary by removing the special characters around it
|
|
3381
|
+
const queries = nonBaseBreakpoints.map((bp) => parseInt(bp.query.replace(/px|<|>/, '')));
|
|
3382
|
+
// Starting with the third breakpoint, check that every query is higher than the one above
|
|
3383
|
+
const isIncreasing = queries.every((value, index, array) => index === 0 || value > array[index - 1]);
|
|
3384
|
+
if (!isIncreasing) {
|
|
3385
|
+
ctx.addIssue({
|
|
3386
|
+
code: z.ZodIssueCode.custom,
|
|
3387
|
+
message: `When using a mobile-first strategy, all breakpoints must have strictly increasing pixel values`,
|
|
3388
|
+
});
|
|
3339
3389
|
}
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3390
|
+
}
|
|
3391
|
+
else if (isDesktopFirstStrategy) {
|
|
3392
|
+
const areOperatorsEqual = nonBaseBreakpoints.every(({ query }) => query.startsWith('<'));
|
|
3393
|
+
if (!areOperatorsEqual) {
|
|
3394
|
+
ctx.addIssue({
|
|
3395
|
+
code: z.ZodIssueCode.custom,
|
|
3396
|
+
message: `Breakpoint queries must be in the format "<[size]px" for desktop-first strategy`,
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
// Extract the queries boundary by removing the special characters around it
|
|
3400
|
+
const queries = nonBaseBreakpoints.map((bp) => parseInt(bp.query.replace(/px|<|>/, '')));
|
|
3401
|
+
// Starting with the third breakpoint, check that every query is lower than the one above
|
|
3402
|
+
const isDecreasing = queries.every((value, index, array) => index === 0 || value < array[index - 1]);
|
|
3403
|
+
if (!isDecreasing) {
|
|
3404
|
+
ctx.addIssue({
|
|
3405
|
+
code: z.ZodIssueCode.custom,
|
|
3406
|
+
message: `When using a desktop-first strategy, all breakpoints must have strictly decreasing pixel values`,
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
else if (!isMobileFirstStrategy && !isDesktopFirstStrategy) {
|
|
3343
3411
|
ctx.addIssue({
|
|
3344
3412
|
code: z.ZodIssueCode.custom,
|
|
3345
|
-
message: `
|
|
3413
|
+
message: `You may only use a mobile-first or desktop-first strategy for breakpoints using '<' or '>' queries`,
|
|
3346
3414
|
});
|
|
3347
3415
|
}
|
|
3348
3416
|
};
|
|
@@ -3401,7 +3469,7 @@ const ParametersSchema = z.record(propertyKeySchema, ParameterSchema);
|
|
|
3401
3469
|
const BreakpointSchema = z
|
|
3402
3470
|
.object({
|
|
3403
3471
|
id: propertyKeySchema,
|
|
3404
|
-
query: z.string().regex(
|
|
3472
|
+
query: z.string().regex(/^\*$|^[<>][0-9*]+px$/),
|
|
3405
3473
|
previewSize: z.string(),
|
|
3406
3474
|
displayName: z.string(),
|
|
3407
3475
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
@@ -4852,7 +4920,7 @@ const collectNodeCoordinates = (node, nodeToCoordinatesMap) => {
|
|
|
4852
4920
|
node.children.forEach((child) => collectNodeCoordinates(child, nodeToCoordinatesMap));
|
|
4853
4921
|
};
|
|
4854
4922
|
function waitForImageToBeLoaded(imageNode) {
|
|
4855
|
-
if (imageNode.complete) {
|
|
4923
|
+
if (imageNode.complete && (imageNode.naturalWidth > 0 || imageNode.naturalHeight > 0)) {
|
|
4856
4924
|
return Promise.resolve();
|
|
4857
4925
|
}
|
|
4858
4926
|
return new Promise((resolve, reject) => {
|
|
@@ -4886,8 +4954,8 @@ const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
|
|
|
4886
4954
|
trailing: true,
|
|
4887
4955
|
}), []);
|
|
4888
4956
|
const debouncedCollectImages = useMemo(() => debounce(() => {
|
|
4889
|
-
|
|
4890
|
-
}, 300, {
|
|
4957
|
+
setImages((prev) => ({ ...prev, allImages: findAllImages() }));
|
|
4958
|
+
}, 300, { trailing: true }), []);
|
|
4891
4959
|
// Store tree in a ref to avoid the need to deactivate & reactivate the mutation observer
|
|
4892
4960
|
// when the tree changes. This is important to avoid missing out on some mutation events.
|
|
4893
4961
|
const treeRef = useRef(tree);
|
|
@@ -4896,13 +4964,17 @@ const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
|
|
|
4896
4964
|
}, [tree]);
|
|
4897
4965
|
// Handling window resize events
|
|
4898
4966
|
useEffect(() => {
|
|
4899
|
-
const resizeEventListener = () =>
|
|
4967
|
+
const resizeEventListener = () => {
|
|
4968
|
+
debouncedUpdateGeometry(treeRef.current, 'resize');
|
|
4969
|
+
// find all images on resize
|
|
4970
|
+
debouncedCollectImages();
|
|
4971
|
+
};
|
|
4900
4972
|
window.addEventListener('resize', resizeEventListener);
|
|
4901
4973
|
return () => window.removeEventListener('resize', resizeEventListener);
|
|
4902
|
-
}, [debouncedUpdateGeometry]);
|
|
4974
|
+
}, [debouncedCollectImages, debouncedUpdateGeometry]);
|
|
4903
4975
|
const [{ allImages, loadedImages }, setImages] = useState(() => {
|
|
4904
|
-
const allImages =
|
|
4905
|
-
const loadedImages = new
|
|
4976
|
+
const allImages = findAllImages();
|
|
4977
|
+
const loadedImages = new WeakMap();
|
|
4906
4978
|
return { allImages, loadedImages };
|
|
4907
4979
|
});
|
|
4908
4980
|
// Handling DOM mutations
|
|
@@ -4910,8 +4982,7 @@ const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
|
|
|
4910
4982
|
const observer = new MutationObserver(() => {
|
|
4911
4983
|
debouncedUpdateGeometry(treeRef.current, 'mutation');
|
|
4912
4984
|
// find all images on any DOM change
|
|
4913
|
-
|
|
4914
|
-
setImages((prevState) => ({ ...prevState, allImages }));
|
|
4985
|
+
debouncedCollectImages();
|
|
4915
4986
|
});
|
|
4916
4987
|
// send initial geometry in case the tree is empty
|
|
4917
4988
|
debouncedUpdateGeometry(treeRef.current, 'mutation');
|
|
@@ -4927,13 +4998,14 @@ const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
|
|
|
4927
4998
|
useEffect(() => {
|
|
4928
4999
|
let isCurrent = true;
|
|
4929
5000
|
allImages.forEach(async (imageNode) => {
|
|
4930
|
-
|
|
5001
|
+
const lastSrc = loadedImages.get(imageNode);
|
|
5002
|
+
if (lastSrc === imageNode.currentSrc) {
|
|
4931
5003
|
return;
|
|
4932
5004
|
}
|
|
4933
5005
|
// update the geometry after each image is loaded, as it can shift the layout
|
|
4934
5006
|
await waitForImageToBeLoaded(imageNode);
|
|
4935
5007
|
if (isCurrent) {
|
|
4936
|
-
loadedImages.
|
|
5008
|
+
loadedImages.set(imageNode, imageNode.currentSrc);
|
|
4937
5009
|
debouncedUpdateGeometry(treeRef.current, 'imageLoad');
|
|
4938
5010
|
}
|
|
4939
5011
|
});
|
|
@@ -4960,6 +5032,9 @@ const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
|
|
|
4960
5032
|
return () => document.removeEventListener('wheel', onWheel);
|
|
4961
5033
|
}, [canvasMode]);
|
|
4962
5034
|
};
|
|
5035
|
+
function findAllImages() {
|
|
5036
|
+
return Array.from(document.querySelectorAll('img'));
|
|
5037
|
+
}
|
|
4963
5038
|
|
|
4964
5039
|
const RootRenderer = ({ inMemoryEntitiesStore, canvasMode }) => {
|
|
4965
5040
|
useEditorSubscriber(inMemoryEntitiesStore);
|