@contentful/experiences-visual-editor-react 3.1.1 → 3.2.0-dev-20250812T1256-c477cd9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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
- const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
636
- // check if the current breakpoint id is found in the rest of the array
637
- const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
638
- return breakpointIndex !== currentBreakpointIndex;
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;
650
+ }
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
+ });
662
+ }
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
+ });
672
+ }
645
673
  }
646
- // Extract the queries boundary by removing the special characters around it
647
- const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
648
- // sort updates queries array in place so we need to create a copy
649
- const originalQueries = [...queries];
650
- queries.sort((q1, q2) => {
651
- if (q1 === '*') {
652
- return -1;
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
+ });
653
681
  }
654
- if (q2 === '*') {
655
- return 1;
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
+ });
656
691
  }
657
- return q1 > q2 ? -1 : 1;
658
- });
659
- if (originalQueries.join('') !== queries.join('')) {
692
+ }
693
+ else if (!isMobileFirstStrategy && !isDesktopFirstStrategy) {
660
694
  ctx.addIssue({
661
695
  code: z.ZodIssueCode.custom,
662
- message: `Breakpoints should be ordered from largest to smallest pixel value`,
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(/^\*$|^<[0-9*]+px$/),
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
- // We assume that there will be a single breakpoint which uses the wildcard query.
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
- const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
3319
- // check if the current breakpoint id is found in the rest of the array
3320
- const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
3321
- return breakpointIndex !== currentBreakpointIndex;
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;
3367
+ }
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
+ });
3379
+ }
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
+ });
3389
+ }
3328
3390
  }
3329
- // Extract the queries boundary by removing the special characters around it
3330
- const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
3331
- // sort updates queries array in place so we need to create a copy
3332
- const originalQueries = [...queries];
3333
- queries.sort((q1, q2) => {
3334
- if (q1 === '*') {
3335
- return -1;
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
+ });
3336
3398
  }
3337
- if (q2 === '*') {
3338
- return 1;
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
+ });
3339
3408
  }
3340
- return q1 > q2 ? -1 : 1;
3341
- });
3342
- if (originalQueries.join('') !== queries.join('')) {
3409
+ }
3410
+ else if (!isMobileFirstStrategy && !isDesktopFirstStrategy) {
3343
3411
  ctx.addIssue({
3344
3412
  code: z.ZodIssueCode.custom,
3345
- message: `Breakpoints should be ordered from largest to smallest pixel value`,
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(/^\*$|^<[0-9*]+px$/),
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(),