@wordpress/block-editor 15.3.0 → 15.3.1-next.6f42e1382.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.
Files changed (88) hide show
  1. package/build/autocompleters/block.js +2 -4
  2. package/build/autocompleters/block.js.map +1 -1
  3. package/build/components/block-list/index.js +3 -1
  4. package/build/components/block-list/index.js.map +1 -1
  5. package/build/components/block-popover/use-popover-scroll.js +11 -2
  6. package/build/components/block-popover/use-popover-scroll.js.map +1 -1
  7. package/build/components/block-switcher/index.js +10 -5
  8. package/build/components/block-switcher/index.js.map +1 -1
  9. package/build/components/block-toolbar/index.js +5 -2
  10. package/build/components/block-toolbar/index.js.map +1 -1
  11. package/build/components/block-tools/insertion-point.js +10 -0
  12. package/build/components/block-tools/insertion-point.js.map +1 -1
  13. package/build/components/block-tools/use-show-block-tools.js +1 -1
  14. package/build/components/block-tools/use-show-block-tools.js.map +1 -1
  15. package/build/components/block-variation-transforms/index.js +9 -5
  16. package/build/components/block-variation-transforms/index.js.map +1 -1
  17. package/build/components/inserter/hooks/use-insertion-point.js +1 -1
  18. package/build/components/inserter/hooks/use-insertion-point.js.map +1 -1
  19. package/build/components/inserter/index.js +1 -3
  20. package/build/components/inserter/index.js.map +1 -1
  21. package/build/components/link-control/search-item.js +30 -12
  22. package/build/components/link-control/search-item.js.map +1 -1
  23. package/build/components/list-view/appender.js +13 -3
  24. package/build/components/list-view/appender.js.map +1 -1
  25. package/build/store/private-selectors.js +7 -20
  26. package/build/store/private-selectors.js.map +1 -1
  27. package/build/store/reducer.js +60 -0
  28. package/build/store/reducer.js.map +1 -1
  29. package/build/store/selectors.js +16 -30
  30. package/build/store/selectors.js.map +1 -1
  31. package/build/store/utils.js +18 -0
  32. package/build/store/utils.js.map +1 -1
  33. package/build-module/autocompleters/block.js +3 -5
  34. package/build-module/autocompleters/block.js.map +1 -1
  35. package/build-module/components/block-list/index.js +3 -1
  36. package/build-module/components/block-list/index.js.map +1 -1
  37. package/build-module/components/block-popover/use-popover-scroll.js +11 -2
  38. package/build-module/components/block-popover/use-popover-scroll.js.map +1 -1
  39. package/build-module/components/block-switcher/index.js +10 -5
  40. package/build-module/components/block-switcher/index.js.map +1 -1
  41. package/build-module/components/block-toolbar/index.js +5 -2
  42. package/build-module/components/block-toolbar/index.js.map +1 -1
  43. package/build-module/components/block-tools/insertion-point.js +11 -1
  44. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  45. package/build-module/components/block-tools/use-show-block-tools.js +1 -1
  46. package/build-module/components/block-tools/use-show-block-tools.js.map +1 -1
  47. package/build-module/components/block-variation-transforms/index.js +9 -5
  48. package/build-module/components/block-variation-transforms/index.js.map +1 -1
  49. package/build-module/components/inserter/hooks/use-insertion-point.js +1 -1
  50. package/build-module/components/inserter/hooks/use-insertion-point.js.map +1 -1
  51. package/build-module/components/inserter/index.js +1 -3
  52. package/build-module/components/inserter/index.js.map +1 -1
  53. package/build-module/components/link-control/search-item.js +30 -12
  54. package/build-module/components/link-control/search-item.js.map +1 -1
  55. package/build-module/components/list-view/appender.js +13 -3
  56. package/build-module/components/list-view/appender.js.map +1 -1
  57. package/build-module/store/private-selectors.js +7 -20
  58. package/build-module/store/private-selectors.js.map +1 -1
  59. package/build-module/store/reducer.js +60 -0
  60. package/build-module/store/reducer.js.map +1 -1
  61. package/build-module/store/selectors.js +16 -30
  62. package/build-module/store/selectors.js.map +1 -1
  63. package/build-module/store/utils.js +17 -0
  64. package/build-module/store/utils.js.map +1 -1
  65. package/build-style/style-rtl.css +0 -1
  66. package/build-style/style.css +0 -1
  67. package/package.json +34 -34
  68. package/src/autocompleters/block.js +6 -11
  69. package/src/components/block-list/index.js +7 -1
  70. package/src/components/block-popover/use-popover-scroll.js +10 -2
  71. package/src/components/block-switcher/index.js +8 -1
  72. package/src/components/block-toolbar/index.js +11 -5
  73. package/src/components/block-tools/insertion-point.js +19 -1
  74. package/src/components/block-tools/use-show-block-tools.js +1 -1
  75. package/src/components/block-variation-transforms/index.js +38 -27
  76. package/src/components/inserter/hooks/use-insertion-point.js +1 -1
  77. package/src/components/inserter/index.js +1 -3
  78. package/src/components/link-control/search-item.js +31 -12
  79. package/src/components/link-control/style.scss +0 -1
  80. package/src/components/link-control/test/index.js +14 -2
  81. package/src/components/list-view/appender.js +13 -6
  82. package/src/store/private-selectors.js +10 -23
  83. package/src/store/reducer.js +105 -0
  84. package/src/store/selectors.js +38 -59
  85. package/src/store/test/reducer.js +107 -0
  86. package/src/store/test/selectors.js +65 -192
  87. package/src/store/utils.js +21 -0
  88. package/tsconfig.tsbuildinfo +1 -1
@@ -18,14 +18,15 @@ import {
18
18
  getClientIdsWithDescendants,
19
19
  isNavigationMode,
20
20
  getBlockRootClientId,
21
+ getBlockAttributes,
21
22
  } from './selectors';
22
23
  import {
23
24
  checkAllowListRecursive,
24
25
  getAllPatternsDependants,
25
26
  getInsertBlockTypeDependants,
26
27
  getGrammar,
28
+ mapUserPattern,
27
29
  } from './utils';
28
- import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils';
29
30
  import { STORE_NAME } from './constants';
30
31
  import { unlock } from '../lock-unlock';
31
32
  import {
@@ -144,8 +145,6 @@ export const getEnabledClientIdsTree = createRegistrySelector( ( select ) =>
144
145
  state.derivedBlockEditingModes,
145
146
  state.derivedNavModeBlockEditingModes,
146
147
  state.blockEditingModes,
147
- state.settings.templateLock,
148
- state.blockListSettings,
149
148
  select( STORE_NAME ).__unstableGetEditorMode( state ),
150
149
  ] )
151
150
  );
@@ -350,26 +349,6 @@ export const hasAllowedPatterns = createRegistrySelector( ( select ) =>
350
349
  )
351
350
  );
352
351
 
353
- function mapUserPattern(
354
- userPattern,
355
- __experimentalUserPatternCategories = []
356
- ) {
357
- return {
358
- name: `core/block/${ userPattern.id }`,
359
- id: userPattern.id,
360
- type: INSERTER_PATTERN_TYPES.user,
361
- title: userPattern.title.raw,
362
- categories: userPattern.wp_pattern_category?.map( ( catId ) => {
363
- const category = __experimentalUserPatternCategories.find(
364
- ( { id } ) => id === catId
365
- );
366
- return category ? category.slug : catId;
367
- } ),
368
- content: userPattern.content.raw,
369
- syncStatus: userPattern.wp_pattern_sync_status,
370
- };
371
- }
372
-
373
352
  export const getPatternBySlug = createRegistrySelector( ( select ) =>
374
353
  createSelector(
375
354
  ( state, patternName ) => {
@@ -539,6 +518,14 @@ export function isSectionBlock( state, clientId ) {
539
518
  return true;
540
519
  }
541
520
 
521
+ const attributes = getBlockAttributes( state, clientId );
522
+ if (
523
+ attributes?.metadata?.patternName &&
524
+ !! window?.__experimentalContentOnlyPatternInsertion
525
+ ) {
526
+ return true;
527
+ }
528
+
542
529
  // Template parts become sections in navigation mode.
543
530
  const _isNavigationMode = isNavigationMode( state );
544
531
  if ( _isNavigationMode && blockName === 'core/template-part' ) {
@@ -2267,6 +2267,26 @@ function getDerivedBlockEditingModesForTree(
2267
2267
  syncedPatternClientIds.push( clientId );
2268
2268
  }
2269
2269
  } );
2270
+ const contentOnlyTemplateLockedClientIds = Object.keys(
2271
+ state.blockListSettings
2272
+ ).filter(
2273
+ ( clientId ) =>
2274
+ state.blockListSettings[ clientId ]?.templateLock === 'contentOnly'
2275
+ );
2276
+ // Use array.from for better back compat. Older versions of the iterator returned
2277
+ // from `keys()` didn't have the `filter` method.
2278
+ const unsyncedPatternClientIds =
2279
+ !! window?.__experimentalContentOnlyPatternInsertion
2280
+ ? Array.from( state.blocks.attributes.keys() ).filter(
2281
+ ( clientId ) =>
2282
+ state.blocks.attributes.get( clientId )?.metadata
2283
+ ?.patternName
2284
+ )
2285
+ : [];
2286
+ const contentOnlyParents = [
2287
+ ...contentOnlyTemplateLockedClientIds,
2288
+ ...unsyncedPatternClientIds,
2289
+ ];
2270
2290
 
2271
2291
  traverseBlockTree( state, treeClientId, ( block ) => {
2272
2292
  const { clientId, name: blockName } = block;
@@ -2457,6 +2477,22 @@ function getDerivedBlockEditingModesForTree(
2457
2477
  derivedBlockEditingModes.set( clientId, 'disabled' );
2458
2478
  }
2459
2479
  }
2480
+
2481
+ // Handle `templateLock=contentOnly` blocks and unsynced patterns.
2482
+ if ( contentOnlyParents.length ) {
2483
+ const hasContentOnlyParent = !! findParentInClientIdsList(
2484
+ state,
2485
+ clientId,
2486
+ contentOnlyParents
2487
+ );
2488
+ if ( hasContentOnlyParent ) {
2489
+ if ( isContentBlock( blockName ) ) {
2490
+ derivedBlockEditingModes.set( clientId, 'contentOnly' );
2491
+ } else {
2492
+ derivedBlockEditingModes.set( clientId, 'disabled' );
2493
+ }
2494
+ }
2495
+ }
2460
2496
  } );
2461
2497
 
2462
2498
  return derivedBlockEditingModes;
@@ -2628,6 +2664,75 @@ export function withDerivedBlockEditingModes( reducer ) {
2628
2664
  }
2629
2665
  break;
2630
2666
  }
2667
+ case 'UPDATE_BLOCK_LIST_SETTINGS': {
2668
+ // Handle the addition and removal of contentOnly template locked blocks.
2669
+ const addedBlocks = [];
2670
+ const removedClientIds = [];
2671
+
2672
+ const updates =
2673
+ typeof action.clientId === 'string'
2674
+ ? { [ action.clientId ]: action.settings }
2675
+ : action.clientId;
2676
+
2677
+ for ( const clientId in updates ) {
2678
+ const isNewContentOnlyBlock =
2679
+ state.blockListSettings[ clientId ]?.templateLock !==
2680
+ 'contentOnly' &&
2681
+ nextState.blockListSettings[ clientId ]
2682
+ ?.templateLock === 'contentOnly';
2683
+
2684
+ const wasContentOnlyBlock =
2685
+ state.blockListSettings[ clientId ]?.templateLock ===
2686
+ 'contentOnly' &&
2687
+ nextState.blockListSettings[ clientId ]
2688
+ ?.templateLock !== 'contentOnly';
2689
+
2690
+ if ( isNewContentOnlyBlock ) {
2691
+ addedBlocks.push(
2692
+ nextState.blocks.tree.get( clientId )
2693
+ );
2694
+ } else if ( wasContentOnlyBlock ) {
2695
+ removedClientIds.push( clientId );
2696
+ }
2697
+ }
2698
+
2699
+ if ( ! addedBlocks.length && ! removedClientIds.length ) {
2700
+ break;
2701
+ }
2702
+
2703
+ const nextDerivedBlockEditingModes =
2704
+ getDerivedBlockEditingModesUpdates( {
2705
+ prevState: state,
2706
+ nextState,
2707
+ addedBlocks,
2708
+ removedClientIds,
2709
+ isNavMode: false,
2710
+ } );
2711
+ const nextDerivedNavModeBlockEditingModes =
2712
+ getDerivedBlockEditingModesUpdates( {
2713
+ prevState: state,
2714
+ nextState,
2715
+ addedBlocks,
2716
+ removedClientIds,
2717
+ isNavMode: true,
2718
+ } );
2719
+
2720
+ if (
2721
+ nextDerivedBlockEditingModes ||
2722
+ nextDerivedNavModeBlockEditingModes
2723
+ ) {
2724
+ return {
2725
+ ...nextState,
2726
+ derivedBlockEditingModes:
2727
+ nextDerivedBlockEditingModes ??
2728
+ state.derivedBlockEditingModes,
2729
+ derivedNavModeBlockEditingModes:
2730
+ nextDerivedNavModeBlockEditingModes ??
2731
+ state.derivedNavModeBlockEditingModes,
2732
+ };
2733
+ }
2734
+ break;
2735
+ }
2631
2736
  case 'SET_BLOCK_EDITING_MODE':
2632
2737
  case 'UNSET_BLOCK_EDITING_MODE':
2633
2738
  case 'SET_HAS_CONTROLLED_INNER_BLOCKS': {
@@ -30,6 +30,7 @@ import {
30
30
  getInsertBlockTypeDependants,
31
31
  getParsedPattern,
32
32
  getGrammar,
33
+ mapUserPattern,
33
34
  } from './utils';
34
35
  import { orderBy } from '../utils/sorting';
35
36
  import { STORE_NAME } from './constants';
@@ -2155,27 +2156,31 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
2155
2156
  foreground: 'var(--wp-block-synced-color)',
2156
2157
  }
2157
2158
  : symbol;
2158
- const id = `core/block/${ reusableBlock.id }`;
2159
- const { time, count = 0 } = getInsertUsage( state, id ) || {};
2159
+ const userPattern = mapUserPattern( reusableBlock );
2160
+ const { time, count = 0 } =
2161
+ getInsertUsage( state, userPattern.name ) || {};
2160
2162
  const frecency = calculateFrecency( time, count );
2161
2163
 
2162
2164
  return {
2163
- id,
2165
+ id: userPattern.name,
2164
2166
  name: 'core/block',
2165
2167
  initialAttributes: { ref: reusableBlock.id },
2166
- title: reusableBlock.title?.raw,
2168
+ title: userPattern.title,
2167
2169
  icon,
2168
2170
  category: 'reusable',
2169
2171
  keywords: [ 'reusable' ],
2170
2172
  isDisabled: false,
2171
2173
  utility: 1, // Deprecated.
2172
2174
  frecency,
2173
- content: reusableBlock.content?.raw,
2174
- syncStatus: reusableBlock.wp_pattern_sync_status,
2175
+ content: userPattern.content,
2176
+ get blocks() {
2177
+ return getParsedPattern( userPattern ).blocks;
2178
+ },
2179
+ syncStatus: userPattern.syncStatus,
2175
2180
  };
2176
2181
  };
2177
2182
 
2178
- const syncedPatternInserterItems = canInsertBlockTypeUnmemoized(
2183
+ const patternInserterItems = canInsertBlockTypeUnmemoized(
2179
2184
  state,
2180
2185
  'core/block',
2181
2186
  rootClientId
@@ -2261,7 +2266,7 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
2261
2266
  { core: [], noncore: [] }
2262
2267
  );
2263
2268
  const sortedBlockTypes = [ ...coreItems, ...nonCoreItems ];
2264
- return [ ...sortedBlockTypes, ...syncedPatternInserterItems ];
2269
+ return [ ...sortedBlockTypes, ...patternInserterItems ];
2265
2270
  },
2266
2271
  ( state, rootClientId ) => [
2267
2272
  getBlockTypes(),
@@ -3076,62 +3081,36 @@ export function __unstableIsWithinBlockOverlay( state, clientId ) {
3076
3081
  * @return {BlockEditingMode} The block editing mode. One of `'disabled'`,
3077
3082
  * `'contentOnly'`, or `'default'`.
3078
3083
  */
3079
- export const getBlockEditingMode = createRegistrySelector(
3080
- ( select ) =>
3081
- ( state, clientId = '' ) => {
3082
- // Some selectors that call this provide `null` as the default
3083
- // rootClientId, but the default rootClientId is actually `''`.
3084
- if ( clientId === null ) {
3085
- clientId = '';
3086
- }
3084
+ export function getBlockEditingMode( state, clientId = '' ) {
3085
+ // Some selectors that call this provide `null` as the default
3086
+ // rootClientId, but the default rootClientId is actually `''`.
3087
+ if ( clientId === null ) {
3088
+ clientId = '';
3089
+ }
3087
3090
 
3088
- const isNavMode = isNavigationMode( state );
3089
-
3090
- // If the editor is currently not in navigation mode, check if the clientId
3091
- // has an editing mode set in the regular derived map.
3092
- // There may be an editing mode set here for synced patterns or in zoomed out
3093
- // mode.
3094
- if (
3095
- ! isNavMode &&
3096
- state.derivedBlockEditingModes?.has( clientId )
3097
- ) {
3098
- return state.derivedBlockEditingModes.get( clientId );
3099
- }
3091
+ const isNavMode = isNavigationMode( state );
3100
3092
 
3101
- // If the editor *is* in navigation mode, the block editing mode states
3102
- // are stored in the derivedNavModeBlockEditingModes map.
3103
- if (
3104
- isNavMode &&
3105
- state.derivedNavModeBlockEditingModes?.has( clientId )
3106
- ) {
3107
- return state.derivedNavModeBlockEditingModes.get( clientId );
3108
- }
3093
+ // If the editor is currently not in navigation mode, check if the clientId
3094
+ // has an editing mode set in the regular derived map.
3095
+ // There may be an editing mode set here for synced patterns or in zoomed out
3096
+ // mode.
3097
+ if ( ! isNavMode && state.derivedBlockEditingModes?.has( clientId ) ) {
3098
+ return state.derivedBlockEditingModes.get( clientId );
3099
+ }
3109
3100
 
3110
- // In normal mode, consider that an explicitly set editing mode takes over.
3111
- const blockEditingMode = state.blockEditingModes.get( clientId );
3112
- if ( blockEditingMode ) {
3113
- return blockEditingMode;
3114
- }
3101
+ // If the editor *is* in navigation mode, the block editing mode states
3102
+ // are stored in the derivedNavModeBlockEditingModes map.
3103
+ if ( isNavMode && state.derivedNavModeBlockEditingModes?.has( clientId ) ) {
3104
+ return state.derivedNavModeBlockEditingModes.get( clientId );
3105
+ }
3115
3106
 
3116
- // In normal mode, top level is default mode.
3117
- if ( clientId === '' ) {
3118
- return 'default';
3119
- }
3107
+ // In normal mode, consider that an explicitly set editing mode takes over.
3108
+ if ( state.blockEditingModes.has( clientId ) ) {
3109
+ return state.blockEditingModes.get( clientId );
3110
+ }
3120
3111
 
3121
- const rootClientId = getBlockRootClientId( state, clientId );
3122
- const templateLock = getTemplateLock( state, rootClientId );
3123
- // If the parent of the block is contentOnly locked, check whether it's a content block.
3124
- if ( templateLock === 'contentOnly' ) {
3125
- const name = getBlockName( state, clientId );
3126
- const { hasContentRoleAttribute } = unlock(
3127
- select( blocksStore )
3128
- );
3129
- const isContent = hasContentRoleAttribute( name );
3130
- return isContent ? 'contentOnly' : 'disabled';
3131
- }
3132
- return 'default';
3133
- }
3134
- );
3112
+ return 'default';
3113
+ }
3135
3114
 
3136
3115
  /**
3137
3116
  * Indicates if a block is ungroupable.
@@ -3575,6 +3575,7 @@ describe( 'state', () => {
3575
3575
  blocks,
3576
3576
  settings,
3577
3577
  zoomLevel,
3578
+ blockListSettings,
3578
3579
  blockEditingModes,
3579
3580
  } )
3580
3581
  );
@@ -3885,6 +3886,112 @@ describe( 'state', () => {
3885
3886
  } );
3886
3887
  } );
3887
3888
 
3889
+ describe( 'contentOnly template locking', () => {
3890
+ let initialState;
3891
+ beforeAll( () => {
3892
+ initialState = dispatchActions(
3893
+ [
3894
+ {
3895
+ type: 'RESET_BLOCKS',
3896
+ blocks: [
3897
+ {
3898
+ name: 'core/group',
3899
+ clientId: 'group-1',
3900
+ attributes: {},
3901
+ innerBlocks: [
3902
+ {
3903
+ name: 'core/paragraph',
3904
+ clientId: 'paragraph-1',
3905
+ attributes: {},
3906
+ innerBlocks: [],
3907
+ },
3908
+ {
3909
+ name: 'core/group',
3910
+ clientId: 'group-2',
3911
+ attributes: {},
3912
+ innerBlocks: [
3913
+ {
3914
+ name: 'core/paragraph',
3915
+ clientId: 'paragraph-2',
3916
+ attributes: {},
3917
+ innerBlocks: [],
3918
+ },
3919
+ ],
3920
+ },
3921
+ ],
3922
+ },
3923
+ ],
3924
+ },
3925
+ {
3926
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
3927
+ clientId: 'group-1',
3928
+ settings: {
3929
+ templateLock: 'contentOnly',
3930
+ },
3931
+ },
3932
+ ],
3933
+ testReducer,
3934
+ initialState
3935
+ );
3936
+ } );
3937
+
3938
+ it( 'returns the expected block editing modes for a parent block with contentOnly template locking', () => {
3939
+ // Only the parent pattern and its own children that have bindings
3940
+ // are in contentOnly mode. All other blocks are disabled.
3941
+ expect( initialState.derivedBlockEditingModes ).toEqual(
3942
+ new Map(
3943
+ Object.entries( {
3944
+ 'paragraph-1': 'contentOnly',
3945
+ 'group-2': 'disabled',
3946
+ 'paragraph-2': 'contentOnly',
3947
+ } )
3948
+ )
3949
+ );
3950
+ } );
3951
+
3952
+ it( 'removes block editing modes when template locking is removed', () => {
3953
+ const { derivedBlockEditingModes } = dispatchActions(
3954
+ [
3955
+ {
3956
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
3957
+ clientId: 'group-1',
3958
+ settings: {
3959
+ templateLock: false,
3960
+ },
3961
+ },
3962
+ ],
3963
+ testReducer,
3964
+ initialState
3965
+ );
3966
+
3967
+ expect( derivedBlockEditingModes ).toEqual( new Map() );
3968
+ } );
3969
+
3970
+ it( 'allows explicitly set blockEditingModes to override the contentOnly template locking', () => {
3971
+ const { derivedBlockEditingModes } = dispatchActions(
3972
+ [
3973
+ {
3974
+ type: 'SET_BLOCK_EDITING_MODE',
3975
+ clientId: 'paragraph-2',
3976
+ mode: 'disabled',
3977
+ },
3978
+ ],
3979
+ testReducer,
3980
+ initialState
3981
+ );
3982
+
3983
+ expect( derivedBlockEditingModes ).toEqual(
3984
+ new Map(
3985
+ Object.entries( {
3986
+ 'paragraph-1': 'contentOnly',
3987
+ 'group-2': 'disabled',
3988
+ // Paragraph 2 already has an explicit mode, so isn't set as a derived mode.
3989
+ } )
3990
+ )
3991
+ );
3992
+ } );
3993
+ } );
3994
+
3888
3995
  describe( 'navigation mode', () => {
3889
3996
  let initialState;
3890
3997