@wordpress/block-editor 13.0.5 → 13.0.7

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 (48) hide show
  1. package/build/autocompleters/block.js +1 -1
  2. package/build/autocompleters/block.js.map +1 -1
  3. package/build/components/block-list/use-block-props/use-block-refs.js +9 -2
  4. package/build/components/block-list/use-block-props/use-block-refs.js.map +1 -1
  5. package/build/components/block-toolbar/shuffle.js +1 -1
  6. package/build/components/block-toolbar/shuffle.js.map +1 -1
  7. package/build/components/global-styles/hooks.js +5 -1
  8. package/build/components/global-styles/hooks.js.map +1 -1
  9. package/build/components/global-styles/use-global-styles-output.js +52 -8
  10. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  11. package/build/layouts/constrained.js +6 -2
  12. package/build/layouts/constrained.js.map +1 -1
  13. package/build/store/private-selectors.js +6 -7
  14. package/build/store/private-selectors.js.map +1 -1
  15. package/build/store/selectors.js +5 -28
  16. package/build/store/selectors.js.map +1 -1
  17. package/build/store/utils.js +36 -0
  18. package/build/store/utils.js.map +1 -1
  19. package/build-module/autocompleters/block.js +1 -1
  20. package/build-module/autocompleters/block.js.map +1 -1
  21. package/build-module/components/block-list/use-block-props/use-block-refs.js +11 -4
  22. package/build-module/components/block-list/use-block-props/use-block-refs.js.map +1 -1
  23. package/build-module/components/block-toolbar/shuffle.js +1 -1
  24. package/build-module/components/block-toolbar/shuffle.js.map +1 -1
  25. package/build-module/components/global-styles/hooks.js +5 -1
  26. package/build-module/components/global-styles/hooks.js.map +1 -1
  27. package/build-module/components/global-styles/use-global-styles-output.js +52 -8
  28. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  29. package/build-module/layouts/constrained.js +6 -2
  30. package/build-module/layouts/constrained.js.map +1 -1
  31. package/build-module/store/private-selectors.js +7 -8
  32. package/build-module/store/private-selectors.js.map +1 -1
  33. package/build-module/store/selectors.js +7 -30
  34. package/build-module/store/selectors.js.map +1 -1
  35. package/build-module/store/utils.js +35 -0
  36. package/build-module/store/utils.js.map +1 -1
  37. package/package.json +3 -3
  38. package/src/autocompleters/block.js +2 -1
  39. package/src/components/block-list/use-block-props/use-block-refs.js +19 -3
  40. package/src/components/block-toolbar/shuffle.js +2 -1
  41. package/src/components/global-styles/hooks.js +5 -1
  42. package/src/components/global-styles/test/use-global-styles-output.js +18 -8
  43. package/src/components/global-styles/use-global-styles-output.js +57 -8
  44. package/src/layouts/constrained.js +10 -2
  45. package/src/store/private-selectors.js +5 -5
  46. package/src/store/selectors.js +9 -40
  47. package/src/store/utils.js +38 -0
  48. package/src/utils/test/transform-styles.js +49 -0
@@ -1,3 +1,8 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { parse } from '@wordpress/blocks';
5
+
1
6
  /**
2
7
  * Internal dependencies
3
8
  */
@@ -5,6 +10,36 @@ import { selectBlockPatternsKey } from './private-keys';
5
10
  import { unlock } from '../lock-unlock';
6
11
  import { STORE_NAME } from './constants';
7
12
  export const withRootClientIdOptionKey = Symbol('withRootClientId');
13
+ const parsedPatternCache = new WeakMap();
14
+ function parsePattern(pattern) {
15
+ const blocks = parse(pattern.content, {
16
+ __unstableSkipMigrationLogs: true
17
+ });
18
+ if (blocks.length === 1) {
19
+ blocks[0].attributes = {
20
+ ...blocks[0].attributes,
21
+ metadata: {
22
+ ...(blocks[0].attributes.metadata || {}),
23
+ categories: pattern.categories,
24
+ patternName: pattern.name,
25
+ name: blocks[0].attributes.metadata?.name || pattern.title
26
+ }
27
+ };
28
+ }
29
+ return {
30
+ ...pattern,
31
+ blocks
32
+ };
33
+ }
34
+ export function getParsedPattern(pattern) {
35
+ let parsedPattern = parsedPatternCache.get(pattern);
36
+ if (parsedPattern) {
37
+ return parsedPattern;
38
+ }
39
+ parsedPattern = parsePattern(pattern);
40
+ parsedPatternCache.set(pattern, parsedPattern);
41
+ return parsedPattern;
42
+ }
8
43
  export const checkAllowList = (list, item, defaultResult = null) => {
9
44
  if (typeof list === 'boolean') {
10
45
  return list;
@@ -1 +1 @@
1
- {"version":3,"names":["selectBlockPatternsKey","unlock","STORE_NAME","withRootClientIdOptionKey","Symbol","checkAllowList","list","item","defaultResult","Array","isArray","includes","checkAllowListRecursive","blocks","allowedBlockTypes","blocksQueue","length","block","shift","isAllowed","name","blockName","innerBlocks","forEach","innerBlock","push","getAllPatternsDependants","select","state","settings","__experimentalBlockPatterns","__experimentalUserPatternCategories","__experimentalReusableBlocks","blockPatterns","getReusableBlocks","getInsertBlockTypeDependants","rootClientId","blockListSettings","byClientId","get","templateLock","blockEditingModes"],"sources":["@wordpress/block-editor/src/store/utils.js"],"sourcesContent":["/**\n * Internal dependencies\n */\nimport { selectBlockPatternsKey } from './private-keys';\nimport { unlock } from '../lock-unlock';\nimport { STORE_NAME } from './constants';\n\nexport const withRootClientIdOptionKey = Symbol( 'withRootClientId' );\n\nexport const checkAllowList = ( list, item, defaultResult = null ) => {\n\tif ( typeof list === 'boolean' ) {\n\t\treturn list;\n\t}\n\tif ( Array.isArray( list ) ) {\n\t\t// TODO: when there is a canonical way to detect that we are editing a post\n\t\t// the following check should be changed to something like:\n\t\t// if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null )\n\t\tif ( list.includes( 'core/post-content' ) && item === null ) {\n\t\t\treturn true;\n\t\t}\n\t\treturn list.includes( item );\n\t}\n\treturn defaultResult;\n};\n\nexport const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => {\n\tif ( typeof allowedBlockTypes === 'boolean' ) {\n\t\treturn allowedBlockTypes;\n\t}\n\n\tconst blocksQueue = [ ...blocks ];\n\twhile ( blocksQueue.length > 0 ) {\n\t\tconst block = blocksQueue.shift();\n\n\t\tconst isAllowed = checkAllowList(\n\t\t\tallowedBlockTypes,\n\t\t\tblock.name || block.blockName,\n\t\t\ttrue\n\t\t);\n\t\tif ( ! isAllowed ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tblock.innerBlocks?.forEach( ( innerBlock ) => {\n\t\t\tblocksQueue.push( innerBlock );\n\t\t} );\n\t}\n\n\treturn true;\n};\n\nexport const getAllPatternsDependants = ( select ) => ( state ) => {\n\treturn [\n\t\tstate.settings.__experimentalBlockPatterns,\n\t\tstate.settings.__experimentalUserPatternCategories,\n\t\tstate.settings.__experimentalReusableBlocks,\n\t\tstate.settings[ selectBlockPatternsKey ]?.( select ),\n\t\tstate.blockPatterns,\n\t\tunlock( select( STORE_NAME ) ).getReusableBlocks(),\n\t];\n};\n\nexport function getInsertBlockTypeDependants( state, rootClientId ) {\n\treturn [\n\t\tstate.blockListSettings[ rootClientId ],\n\t\tstate.blocks.byClientId.get( rootClientId ),\n\t\tstate.settings.allowedBlockTypes,\n\t\tstate.settings.templateLock,\n\t\tstate.blockEditingModes,\n\t];\n}\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,sBAAsB,QAAQ,gBAAgB;AACvD,SAASC,MAAM,QAAQ,gBAAgB;AACvC,SAASC,UAAU,QAAQ,aAAa;AAExC,OAAO,MAAMC,yBAAyB,GAAGC,MAAM,CAAE,kBAAmB,CAAC;AAErE,OAAO,MAAMC,cAAc,GAAGA,CAAEC,IAAI,EAAEC,IAAI,EAAEC,aAAa,GAAG,IAAI,KAAM;EACrE,IAAK,OAAOF,IAAI,KAAK,SAAS,EAAG;IAChC,OAAOA,IAAI;EACZ;EACA,IAAKG,KAAK,CAACC,OAAO,CAAEJ,IAAK,CAAC,EAAG;IAC5B;IACA;IACA;IACA,IAAKA,IAAI,CAACK,QAAQ,CAAE,mBAAoB,CAAC,IAAIJ,IAAI,KAAK,IAAI,EAAG;MAC5D,OAAO,IAAI;IACZ;IACA,OAAOD,IAAI,CAACK,QAAQ,CAAEJ,IAAK,CAAC;EAC7B;EACA,OAAOC,aAAa;AACrB,CAAC;AAED,OAAO,MAAMI,uBAAuB,GAAGA,CAAEC,MAAM,EAAEC,iBAAiB,KAAM;EACvE,IAAK,OAAOA,iBAAiB,KAAK,SAAS,EAAG;IAC7C,OAAOA,iBAAiB;EACzB;EAEA,MAAMC,WAAW,GAAG,CAAE,GAAGF,MAAM,CAAE;EACjC,OAAQE,WAAW,CAACC,MAAM,GAAG,CAAC,EAAG;IAChC,MAAMC,KAAK,GAAGF,WAAW,CAACG,KAAK,CAAC,CAAC;IAEjC,MAAMC,SAAS,GAAGd,cAAc,CAC/BS,iBAAiB,EACjBG,KAAK,CAACG,IAAI,IAAIH,KAAK,CAACI,SAAS,EAC7B,IACD,CAAC;IACD,IAAK,CAAEF,SAAS,EAAG;MAClB,OAAO,KAAK;IACb;IAEAF,KAAK,CAACK,WAAW,EAAEC,OAAO,CAAIC,UAAU,IAAM;MAC7CT,WAAW,CAACU,IAAI,CAAED,UAAW,CAAC;IAC/B,CAAE,CAAC;EACJ;EAEA,OAAO,IAAI;AACZ,CAAC;AAED,OAAO,MAAME,wBAAwB,GAAKC,MAAM,IAAQC,KAAK,IAAM;EAClE,OAAO,CACNA,KAAK,CAACC,QAAQ,CAACC,2BAA2B,EAC1CF,KAAK,CAACC,QAAQ,CAACE,mCAAmC,EAClDH,KAAK,CAACC,QAAQ,CAACG,4BAA4B,EAC3CJ,KAAK,CAACC,QAAQ,CAAE7B,sBAAsB,CAAE,GAAI2B,MAAO,CAAC,EACpDC,KAAK,CAACK,aAAa,EACnBhC,MAAM,CAAE0B,MAAM,CAAEzB,UAAW,CAAE,CAAC,CAACgC,iBAAiB,CAAC,CAAC,CAClD;AACF,CAAC;AAED,OAAO,SAASC,4BAA4BA,CAAEP,KAAK,EAAEQ,YAAY,EAAG;EACnE,OAAO,CACNR,KAAK,CAACS,iBAAiB,CAAED,YAAY,CAAE,EACvCR,KAAK,CAACf,MAAM,CAACyB,UAAU,CAACC,GAAG,CAAEH,YAAa,CAAC,EAC3CR,KAAK,CAACC,QAAQ,CAACf,iBAAiB,EAChCc,KAAK,CAACC,QAAQ,CAACW,YAAY,EAC3BZ,KAAK,CAACa,iBAAiB,CACvB;AACF","ignoreList":[]}
1
+ {"version":3,"names":["parse","selectBlockPatternsKey","unlock","STORE_NAME","withRootClientIdOptionKey","Symbol","parsedPatternCache","WeakMap","parsePattern","pattern","blocks","content","__unstableSkipMigrationLogs","length","attributes","metadata","categories","patternName","name","title","getParsedPattern","parsedPattern","get","set","checkAllowList","list","item","defaultResult","Array","isArray","includes","checkAllowListRecursive","allowedBlockTypes","blocksQueue","block","shift","isAllowed","blockName","innerBlocks","forEach","innerBlock","push","getAllPatternsDependants","select","state","settings","__experimentalBlockPatterns","__experimentalUserPatternCategories","__experimentalReusableBlocks","blockPatterns","getReusableBlocks","getInsertBlockTypeDependants","rootClientId","blockListSettings","byClientId","templateLock","blockEditingModes"],"sources":["@wordpress/block-editor/src/store/utils.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { parse } from '@wordpress/blocks';\n\n/**\n * Internal dependencies\n */\nimport { selectBlockPatternsKey } from './private-keys';\nimport { unlock } from '../lock-unlock';\nimport { STORE_NAME } from './constants';\n\nexport const withRootClientIdOptionKey = Symbol( 'withRootClientId' );\n\nconst parsedPatternCache = new WeakMap();\n\nfunction parsePattern( pattern ) {\n\tconst blocks = parse( pattern.content, {\n\t\t__unstableSkipMigrationLogs: true,\n\t} );\n\tif ( blocks.length === 1 ) {\n\t\tblocks[ 0 ].attributes = {\n\t\t\t...blocks[ 0 ].attributes,\n\t\t\tmetadata: {\n\t\t\t\t...( blocks[ 0 ].attributes.metadata || {} ),\n\t\t\t\tcategories: pattern.categories,\n\t\t\t\tpatternName: pattern.name,\n\t\t\t\tname: blocks[ 0 ].attributes.metadata?.name || pattern.title,\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\t...pattern,\n\t\tblocks,\n\t};\n}\n\nexport function getParsedPattern( pattern ) {\n\tlet parsedPattern = parsedPatternCache.get( pattern );\n\tif ( parsedPattern ) {\n\t\treturn parsedPattern;\n\t}\n\tparsedPattern = parsePattern( pattern );\n\tparsedPatternCache.set( pattern, parsedPattern );\n\treturn parsedPattern;\n}\n\nexport const checkAllowList = ( list, item, defaultResult = null ) => {\n\tif ( typeof list === 'boolean' ) {\n\t\treturn list;\n\t}\n\tif ( Array.isArray( list ) ) {\n\t\t// TODO: when there is a canonical way to detect that we are editing a post\n\t\t// the following check should be changed to something like:\n\t\t// if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null )\n\t\tif ( list.includes( 'core/post-content' ) && item === null ) {\n\t\t\treturn true;\n\t\t}\n\t\treturn list.includes( item );\n\t}\n\treturn defaultResult;\n};\n\nexport const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => {\n\tif ( typeof allowedBlockTypes === 'boolean' ) {\n\t\treturn allowedBlockTypes;\n\t}\n\n\tconst blocksQueue = [ ...blocks ];\n\twhile ( blocksQueue.length > 0 ) {\n\t\tconst block = blocksQueue.shift();\n\n\t\tconst isAllowed = checkAllowList(\n\t\t\tallowedBlockTypes,\n\t\t\tblock.name || block.blockName,\n\t\t\ttrue\n\t\t);\n\t\tif ( ! isAllowed ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tblock.innerBlocks?.forEach( ( innerBlock ) => {\n\t\t\tblocksQueue.push( innerBlock );\n\t\t} );\n\t}\n\n\treturn true;\n};\n\nexport const getAllPatternsDependants = ( select ) => ( state ) => {\n\treturn [\n\t\tstate.settings.__experimentalBlockPatterns,\n\t\tstate.settings.__experimentalUserPatternCategories,\n\t\tstate.settings.__experimentalReusableBlocks,\n\t\tstate.settings[ selectBlockPatternsKey ]?.( select ),\n\t\tstate.blockPatterns,\n\t\tunlock( select( STORE_NAME ) ).getReusableBlocks(),\n\t];\n};\n\nexport function getInsertBlockTypeDependants( state, rootClientId ) {\n\treturn [\n\t\tstate.blockListSettings[ rootClientId ],\n\t\tstate.blocks.byClientId.get( rootClientId ),\n\t\tstate.settings.allowedBlockTypes,\n\t\tstate.settings.templateLock,\n\t\tstate.blockEditingModes,\n\t];\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "13.0.5",
3
+ "version": "13.0.7",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -73,7 +73,7 @@
73
73
  "fast-deep-equal": "^3.1.3",
74
74
  "memize": "^2.1.0",
75
75
  "postcss": "^8.4.21",
76
- "postcss-prefixwrap": "^1.41.0",
76
+ "postcss-prefixwrap": "^1.51.0",
77
77
  "postcss-urlrebase": "^1.0.0",
78
78
  "react-autosize-textarea": "^7.1.0",
79
79
  "react-easy-crop": "^5.0.6",
@@ -86,5 +86,5 @@
86
86
  "publishConfig": {
87
87
  "access": "public"
88
88
  },
89
- "gitHead": "38063ae466e1b62d400b910136757a4cafdbe7fa"
89
+ "gitHead": "a74a70ed203bbcbb5f3d34335df9631178647acb"
90
90
  }
@@ -60,7 +60,8 @@ function createBlockCompleter() {
60
60
  }, [] );
61
61
  const [ items, categories, collections ] = useBlockTypesState(
62
62
  rootClientId,
63
- noop
63
+ noop,
64
+ true
64
65
  );
65
66
 
66
67
  const filteredItems = useMemo( () => {
@@ -1,8 +1,14 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useContext, useMemo, useRef } from '@wordpress/element';
5
- import { useRefEffect, useObservableValue } from '@wordpress/compose';
4
+ import {
5
+ useContext,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ useLayoutEffect,
10
+ } from '@wordpress/element';
11
+ import { useRefEffect } from '@wordpress/compose';
6
12
 
7
13
  /**
8
14
  * Internal dependencies
@@ -67,7 +73,17 @@ function useBlockRef( clientId ) {
67
73
  */
68
74
  function useBlockElement( clientId ) {
69
75
  const { refsMap } = useContext( BlockRefs );
70
- return useObservableValue( refsMap, clientId ) ?? null;
76
+ const [ blockElement, setBlockElement ] = useState( null );
77
+ // Delay setting the resulting `blockElement` until an effect. If the block element
78
+ // changes (i.e., the block is unmounted and re-mounted), this allows enough time
79
+ // for the ref callbacks to clean up the old element and set the new one.
80
+ useLayoutEffect( () => {
81
+ setBlockElement( refsMap.get( clientId ) );
82
+ return refsMap.subscribe( clientId, () =>
83
+ setBlockElement( refsMap.get( clientId ) )
84
+ );
85
+ }, [ refsMap, clientId ] );
86
+ return blockElement;
71
87
  }
72
88
 
73
89
  export { useBlockRef as __unstableUseBlockRef };
@@ -66,7 +66,8 @@ export default function Shuffle( { clientId, as = Container } ) {
66
66
  );
67
67
  } );
68
68
  }, [ categories, patterns ] );
69
- if ( sameCategoryPatternsWithSingleWrapper.length === 0 ) {
69
+
70
+ if ( sameCategoryPatternsWithSingleWrapper.length < 2 ) {
70
71
  return null;
71
72
  }
72
73
 
@@ -85,7 +85,11 @@ const VALID_SETTINGS = [
85
85
  ];
86
86
 
87
87
  export const useGlobalStylesReset = () => {
88
- const { user: config, setUserConfig } = useContext( GlobalStylesContext );
88
+ const { user, setUserConfig } = useContext( GlobalStylesContext );
89
+ const config = {
90
+ settings: user.settings,
91
+ styles: user.styles,
92
+ };
89
93
  const canReset = !! config && ! fastDeepEqual( config, EMPTY_CONFIG );
90
94
  return [
91
95
  canReset,
@@ -107,6 +107,7 @@ describe( 'global styles renderer', () => {
107
107
  },
108
108
  },
109
109
  selector: ROOT_BLOCK_SELECTOR,
110
+ skipSelectorWrapper: true,
110
111
  },
111
112
  {
112
113
  styles: {
@@ -128,6 +129,7 @@ describe( 'global styles renderer', () => {
128
129
  },
129
130
  },
130
131
  selector: ELEMENTS.link,
132
+ skipSelectorWrapper: true,
131
133
  },
132
134
  {
133
135
  styles: {
@@ -480,7 +482,7 @@ describe( 'global styles renderer', () => {
480
482
  };
481
483
 
482
484
  expect( toStyles( tree, blockSelectors ) ).toEqual(
483
- ':where(body) {margin: 0;}.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-constrained > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-constrained > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > :is(*, div) { margin: 0; }body .is-layout-grid { display:grid; }.is-layout-grid > :is(*, div) { margin: 0; }:root :where(body){background-color: red;margin: 10px;padding: 10px;}:root :where(a:where(:not(.wp-element-button))){color: blue;}a:where(:not(.wp-element-button)):hover{color: orange;}a:where(:not(.wp-element-button)):focus{color: orange;}:root :where(h1){font-size: 42px;}:root :where(.wp-block-group){margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}:root :where(h1,h2,h3,h4,h5,h6){color: orange;}:root :where(h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button))){color: hotpink;}h1 a:where(:not(.wp-element-button)):hover,h2 a:where(:not(.wp-element-button)):hover,h3 a:where(:not(.wp-element-button)):hover,h4 a:where(:not(.wp-element-button)):hover,h5 a:where(:not(.wp-element-button)):hover,h6 a:where(:not(.wp-element-button)):hover{color: red;}h1 a:where(:not(.wp-element-button)):focus,h2 a:where(:not(.wp-element-button)):focus,h3 a:where(:not(.wp-element-button)):focus,h4 a:where(:not(.wp-element-button)):focus,h5 a:where(:not(.wp-element-button)):focus,h6 a:where(:not(.wp-element-button)):focus{color: red;}:root :where(.wp-block-image img, .wp-block-image .wp-crop-area){border-radius: 9999px;}:root :where(.wp-block-image){color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
485
+ ':where(body) {margin: 0;}.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-constrained > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-constrained > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > :is(*, div) { margin: 0; }body .is-layout-grid { display:grid; }.is-layout-grid > :is(*, div) { margin: 0; }body{background-color: red;margin: 10px;padding: 10px;}a:where(:not(.wp-element-button)){color: blue;}:root :where(a:where(:not(.wp-element-button)):hover){color: orange;}:root :where(a:where(:not(.wp-element-button)):focus){color: orange;}h1{font-size: 42px;}:root :where(.wp-block-group){margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}:root :where(h1,h2,h3,h4,h5,h6){color: orange;}:root :where(h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button))){color: hotpink;}:root :where(h1 a:where(:not(.wp-element-button)):hover,h2 a:where(:not(.wp-element-button)):hover,h3 a:where(:not(.wp-element-button)):hover,h4 a:where(:not(.wp-element-button)):hover,h5 a:where(:not(.wp-element-button)):hover,h6 a:where(:not(.wp-element-button)):hover){color: red;}:root :where(h1 a:where(:not(.wp-element-button)):focus,h2 a:where(:not(.wp-element-button)):focus,h3 a:where(:not(.wp-element-button)):focus,h4 a:where(:not(.wp-element-button)):focus,h5 a:where(:not(.wp-element-button)):focus,h6 a:where(:not(.wp-element-button)):focus){color: red;}:root :where(.wp-block-image img, .wp-block-image .wp-crop-area){border-radius: 9999px;}:root :where(.wp-block-image){color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
484
486
  );
485
487
  } );
486
488
 
@@ -761,7 +763,7 @@ describe( 'global styles renderer', () => {
761
763
  } );
762
764
 
763
765
  expect( layoutStyles ).toEqual(
764
- '.is-layout-flow > * { margin-block-start: 0; margin-block-end: 0; }.is-layout-flow > * + * { margin-block-start: 0.5em; margin-block-end: 0; }.is-layout-flex { gap: 0.5em; }:root { --wp--style--block-gap: 0.5em; }.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > * { margin: 0; }'
766
+ ':root :where(.is-layout-flow) > * { margin-block-start: 0; margin-block-end: 0; }:root :where(.is-layout-flow) > * + * { margin-block-start: 0.5em; margin-block-end: 0; }:root :where(.is-layout-flex) { gap: 0.5em; }:root { --wp--style--block-gap: 0.5em; }.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > * { margin: 0; }'
765
767
  );
766
768
  } );
767
769
 
@@ -778,7 +780,7 @@ describe( 'global styles renderer', () => {
778
780
  } );
779
781
 
780
782
  expect( layoutStyles ).toEqual(
781
- '.is-layout-flow > * { margin-block-start: 0; margin-block-end: 0; }.is-layout-flow > * + * { margin-block-start: 12px; margin-block-end: 0; }.is-layout-flex { gap: 12px; }:root { --wp--style--block-gap: 12px; }.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > * { margin: 0; }'
783
+ ':root :where(.is-layout-flow) > * { margin-block-start: 0; margin-block-end: 0; }:root :where(.is-layout-flow) > * + * { margin-block-start: 12px; margin-block-end: 0; }:root :where(.is-layout-flex) { gap: 12px; }:root { --wp--style--block-gap: 12px; }.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > * { margin: 0; }'
782
784
  );
783
785
  } );
784
786
 
@@ -795,7 +797,7 @@ describe( 'global styles renderer', () => {
795
797
  } );
796
798
 
797
799
  expect( layoutStyles ).toEqual(
798
- '.wp-block-group-is-layout-flow > * { margin-block-start: 0; margin-block-end: 0; }.wp-block-group-is-layout-flow > * + * { margin-block-start: 12px; margin-block-end: 0; }.wp-block-group-is-layout-flex { gap: 12px; }'
800
+ ':root :where(.wp-block-group-is-layout-flow) > * { margin-block-start: 0; margin-block-end: 0; }:root :where(.wp-block-group-is-layout-flow) > * + * { margin-block-start: 12px; margin-block-end: 0; }:root :where(.wp-block-group-is-layout-flex) { gap: 12px; }'
799
801
  );
800
802
  } );
801
803
 
@@ -1029,11 +1031,19 @@ describe( 'global styles renderer', () => {
1029
1031
  } );
1030
1032
 
1031
1033
  describe( 'processCSSNesting', () => {
1034
+ it( 'should return empty string when supplied css is empty', () => {
1035
+ expect( processCSSNesting( '', '.foo' ) ).toEqual( '' );
1036
+ } );
1032
1037
  it( 'should return processed CSS without any nested selectors', () => {
1033
1038
  expect(
1034
1039
  processCSSNesting( 'color: red; margin: auto;', '.foo' )
1035
1040
  ).toEqual( ':root :where(.foo){color: red; margin: auto;}' );
1036
1041
  } );
1042
+ it( 'should return processed CSS when there are no root selectors', () => {
1043
+ expect(
1044
+ processCSSNesting( '&::before{color: red;}', '.foo' )
1045
+ ).toEqual( ':root :where(.foo)::before{color: red;}' );
1046
+ } );
1037
1047
  it( 'should return processed CSS with nested selectors', () => {
1038
1048
  expect(
1039
1049
  processCSSNesting(
@@ -1047,21 +1057,21 @@ describe( 'global styles renderer', () => {
1047
1057
  it( 'should return processed CSS with pseudo elements', () => {
1048
1058
  expect(
1049
1059
  processCSSNesting(
1050
- 'color: red; margin: auto; &::before{color: blue;} & ::before{color: green;} &.one::before{color: yellow;} & .two::before{color: purple;}',
1060
+ 'color: red; margin: auto; &::before{color: blue;} & ::before{color: green;} &.one::before{color: yellow;} & .two::before{color: purple;} & > ::before{color: darkseagreen;}',
1051
1061
  '.foo'
1052
1062
  )
1053
1063
  ).toEqual(
1054
- ':root :where(.foo){color: red; margin: auto;}:root :where(.foo::before){color: blue;}:root :where(.foo ::before){color: green;}:root :where(.foo.one::before){color: yellow;}:root :where(.foo .two::before){color: purple;}'
1064
+ ':root :where(.foo){color: red; margin: auto;}:root :where(.foo)::before{color: blue;}:root :where(.foo) ::before{color: green;}:root :where(.foo.one)::before{color: yellow;}:root :where(.foo .two)::before{color: purple;}:root :where(.foo) > ::before{color: darkseagreen;}'
1055
1065
  );
1056
1066
  } );
1057
1067
  it( 'should return processed CSS with multiple root selectors', () => {
1058
1068
  expect(
1059
1069
  processCSSNesting(
1060
- 'color: red; margin: auto; &.one{color: blue;} & .two{color: green;} &::before{color: yellow;} & ::before{color: purple;} &.three::before{color: orange;} & .four::before{color: skyblue;}',
1070
+ 'color: red; margin: auto; &.one{color: blue;} & .two{color: green;} &::before{color: yellow;} & ::before{color: purple;} &.three::before{color: orange;} & .four::before{color: skyblue;} & > ::before{color: darkseagreen;}',
1061
1071
  '.foo, .bar'
1062
1072
  )
1063
1073
  ).toEqual(
1064
- ':root :where(.foo, .bar){color: red; margin: auto;}:root :where(.foo.one, .bar.one){color: blue;}:root :where(.foo .two, .bar .two){color: green;}:root :where(.foo::before, .bar::before){color: yellow;}:root :where(.foo ::before, .bar ::before){color: purple;}:root :where(.foo.three::before, .bar.three::before){color: orange;}:root :where(.foo .four::before, .bar .four::before){color: skyblue;}'
1074
+ ':root :where(.foo, .bar){color: red; margin: auto;}:root :where(.foo.one, .bar.one){color: blue;}:root :where(.foo .two, .bar .two){color: green;}:root :where(.foo, .bar)::before{color: yellow;}:root :where(.foo, .bar) ::before{color: purple;}:root :where(.foo.three, .bar.three)::before{color: orange;}:root :where(.foo .four, .bar .four)::before{color: skyblue;}:root :where(.foo, .bar) > ::before{color: darkseagreen;}'
1065
1075
  );
1066
1076
  } );
1067
1077
  } );
@@ -37,6 +37,12 @@ import { getValueFromObjectPath, setImmutably } from '../../utils/object';
37
37
  import { unlock } from '../../lock-unlock';
38
38
  import { setThemeFileUris } from './theme-file-uri-utils';
39
39
 
40
+ // Elements that rely on class names in their selectors.
41
+ const ELEMENT_CLASS_NAMES = {
42
+ button: 'wp-element-button',
43
+ caption: 'wp-element-caption',
44
+ };
45
+
40
46
  // List of block support features that can have their related styles
41
47
  // generated under their own feature level selector rather than the block's.
42
48
  const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = {
@@ -523,10 +529,10 @@ export function getLayoutStyles( {
523
529
  } else {
524
530
  combinedSelector =
525
531
  selector === ROOT_BLOCK_SELECTOR
526
- ? `.${ className }${
532
+ ? `:root :where(.${ className })${
527
533
  spacingStyle?.selector || ''
528
534
  }`
529
- : `${ selector }-${ className }${
535
+ : `:root :where(${ selector }-${ className })${
530
536
  spacingStyle?.selector || ''
531
537
  }`;
532
538
  }
@@ -628,6 +634,9 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
628
634
  nodes.push( {
629
635
  styles,
630
636
  selector: ROOT_BLOCK_SELECTOR,
637
+ // Root selector (body) styles should not be wrapped in `:root where()` to keep
638
+ // specificity at (0,0,1) and maintain backwards compatibility.
639
+ skipSelectorWrapper: true,
631
640
  } );
632
641
  }
633
642
 
@@ -636,6 +645,9 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
636
645
  nodes.push( {
637
646
  styles: tree.styles?.elements?.[ name ],
638
647
  selector,
648
+ // Top level elements that don't use a class name should not receive the
649
+ // `:root :where()` wrapper to maintain backwards compatibility.
650
+ skipSelectorWrapper: ! ELEMENT_CLASS_NAMES[ name ],
639
651
  } );
640
652
  }
641
653
  } );
@@ -942,6 +954,7 @@ export const toStyles = (
942
954
  hasLayoutSupport,
943
955
  featureSelectors,
944
956
  styleVariationSelectors,
957
+ skipSelectorWrapper,
945
958
  } ) => {
946
959
  // Process styles for block support features with custom feature level
947
960
  // CSS selectors set.
@@ -1000,7 +1013,10 @@ export const toStyles = (
1000
1013
  disableRootPadding
1001
1014
  );
1002
1015
  if ( styleDeclarations?.length ) {
1003
- ruleset += `:root :where(${ selector }){${ styleDeclarations.join(
1016
+ const generalSelector = skipSelectorWrapper
1017
+ ? selector
1018
+ : `:root :where(${ selector })`;
1019
+ ruleset += `${ generalSelector }{${ styleDeclarations.join(
1004
1020
  ';'
1005
1021
  ) };}`;
1006
1022
  }
@@ -1094,7 +1110,11 @@ export const toStyles = (
1094
1110
  .map( ( sel ) => sel + pseudoKey )
1095
1111
  .join( ',' );
1096
1112
 
1097
- const pseudoRule = `${ _selector }{${ pseudoDeclarations.join(
1113
+ // As pseudo classes such as :hover, :focus etc. have class-level
1114
+ // specificity, they must use the `:root :where()` wrapper. This.
1115
+ // caps the specificity at `0-1-0` to allow proper nesting of variations
1116
+ // and block type element styles.
1117
+ const pseudoRule = `:root :where(${ _selector }){${ pseudoDeclarations.join(
1098
1118
  ';'
1099
1119
  ) };}`;
1100
1120
 
@@ -1292,9 +1312,17 @@ function updateConfigWithSeparator( config ) {
1292
1312
  export function processCSSNesting( css, blockSelector ) {
1293
1313
  let processedCSS = '';
1294
1314
 
1315
+ if ( ! css || css.trim() === '' ) {
1316
+ return processedCSS;
1317
+ }
1318
+
1295
1319
  // Split CSS nested rules.
1296
1320
  const parts = css.split( '&' );
1297
1321
  parts.forEach( ( part ) => {
1322
+ if ( ! part || part.trim() === '' ) {
1323
+ return;
1324
+ }
1325
+
1298
1326
  const isRootCss = ! part.includes( '{' );
1299
1327
  if ( isRootCss ) {
1300
1328
  // If the part doesn't contain braces, it applies to the root level.
@@ -1307,11 +1335,32 @@ export function processCSSNesting( css, blockSelector ) {
1307
1335
  }
1308
1336
 
1309
1337
  const [ nestedSelector, cssValue ] = splittedPart;
1310
- const combinedSelector = nestedSelector.startsWith( ' ' )
1311
- ? scopeSelector( blockSelector, nestedSelector )
1312
- : appendToSelector( blockSelector, nestedSelector );
1313
1338
 
1314
- processedCSS += `:root :where(${ combinedSelector }){${ cssValue.trim() }}`;
1339
+ // Handle pseudo elements such as ::before, ::after, etc. Regex will also
1340
+ // capture any leading combinator such as >, +, or ~, as well as spaces.
1341
+ // This allows pseudo elements as descendants e.g. `.parent ::before`.
1342
+ const matches = nestedSelector.match( /([>+~\s]*::[a-zA-Z-]+)/ );
1343
+ const pseudoPart = matches ? matches[ 1 ] : '';
1344
+ const withoutPseudoElement = matches
1345
+ ? nestedSelector.replace( pseudoPart, '' ).trim()
1346
+ : nestedSelector.trim();
1347
+
1348
+ let combinedSelector;
1349
+ if ( withoutPseudoElement === '' ) {
1350
+ // Only contained a pseudo element to use the block selector to form
1351
+ // the final `:root :where()` selector.
1352
+ combinedSelector = blockSelector;
1353
+ } else {
1354
+ // If the nested selector is a descendant of the block scope it with the
1355
+ // block selector. Otherwise append it to the block selector.
1356
+ combinedSelector = nestedSelector.startsWith( ' ' )
1357
+ ? scopeSelector( blockSelector, withoutPseudoElement )
1358
+ : appendToSelector( blockSelector, withoutPseudoElement );
1359
+ }
1360
+
1361
+ // Build final rule, re-adding any pseudo element outside the `:where()`
1362
+ // to maintain valid CSS selector.
1363
+ processedCSS += `:root :where(${ combinedSelector })${ pseudoPart }{${ cssValue.trim() }}`;
1315
1364
  }
1316
1365
  } );
1317
1366
  return processedCSS;
@@ -217,15 +217,23 @@ export default {
217
217
  const paddingValues = getCSSRules( style );
218
218
  paddingValues.forEach( ( rule ) => {
219
219
  if ( rule.key === 'paddingRight' ) {
220
+ // Add unit if 0, to avoid calc(0 * -1) which is invalid.
221
+ const paddingRightValue =
222
+ rule.value === '0' ? '0px' : rule.value;
223
+
220
224
  output += `
221
225
  ${ appendSelectors( selector, '> .alignfull' ) } {
222
- margin-right: calc(${ rule.value } * -1);
226
+ margin-right: calc(${ paddingRightValue } * -1);
223
227
  }
224
228
  `;
225
229
  } else if ( rule.key === 'paddingLeft' ) {
230
+ // Add unit if 0, to avoid calc(0 * -1) which is invalid.
231
+ const paddingLeftValue =
232
+ rule.value === '0' ? '0px' : rule.value;
233
+
226
234
  output += `
227
235
  ${ appendSelectors( selector, '> .alignfull' ) } {
228
- margin-left: calc(${ rule.value } * -1);
236
+ margin-left: calc(${ paddingLeftValue } * -1);
229
237
  }
230
238
  `;
231
239
  }
@@ -20,6 +20,7 @@ import {
20
20
  checkAllowListRecursive,
21
21
  getAllPatternsDependants,
22
22
  getInsertBlockTypeDependants,
23
+ getParsedPattern,
23
24
  } from './utils';
24
25
  import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils';
25
26
  import { STORE_NAME } from './constants';
@@ -291,16 +292,15 @@ export const getInserterMediaCategories = createSelector(
291
292
  export const hasAllowedPatterns = createRegistrySelector( ( select ) =>
292
293
  createSelector(
293
294
  ( state, rootClientId = null ) => {
294
- const { getAllPatterns, __experimentalGetParsedPattern } = unlock(
295
- select( STORE_NAME )
296
- );
295
+ const { getAllPatterns } = unlock( select( STORE_NAME ) );
297
296
  const patterns = getAllPatterns();
298
297
  const { allowedBlockTypes } = getSettings( state );
299
- return patterns.some( ( { name, inserter = true } ) => {
298
+ return patterns.some( ( pattern ) => {
299
+ const { inserter = true } = pattern;
300
300
  if ( ! inserter ) {
301
301
  return false;
302
302
  }
303
- const { blocks } = __experimentalGetParsedPattern( name );
303
+ const { blocks } = getParsedPattern( pattern );
304
304
  return (
305
305
  checkAllowListRecursive( blocks, allowedBlockTypes ) &&
306
306
  blocks.every( ( { name: blockName } ) =>
@@ -7,7 +7,6 @@ import {
7
7
  getBlockVariations,
8
8
  hasBlockSupport,
9
9
  getPossibleBlockTransformations,
10
- parse,
11
10
  switchToBlockType,
12
11
  store as blocksStore,
13
12
  } from '@wordpress/blocks';
@@ -27,6 +26,7 @@ import {
27
26
  checkAllowList,
28
27
  getAllPatternsDependants,
29
28
  getInsertBlockTypeDependants,
29
+ getParsedPattern,
30
30
  } from './utils';
31
31
  import { orderBy } from '../utils/sorting';
32
32
  import { STORE_NAME } from './constants';
@@ -2349,40 +2349,12 @@ export function __experimentalGetDirectInsertBlock(
2349
2349
  }
2350
2350
 
2351
2351
  export const __experimentalGetParsedPattern = createRegistrySelector(
2352
- ( select ) =>
2353
- createSelector(
2354
- ( state, patternName ) => {
2355
- const pattern = unlock( select( STORE_NAME ) ).getPatternBySlug(
2356
- patternName
2357
- );
2358
- if ( ! pattern ) {
2359
- return null;
2360
- }
2361
- const blocks = parse( pattern.content, {
2362
- __unstableSkipMigrationLogs: true,
2363
- } );
2364
- if ( blocks.length === 1 ) {
2365
- blocks[ 0 ].attributes = {
2366
- ...blocks[ 0 ].attributes,
2367
- metadata: {
2368
- ...( blocks[ 0 ].attributes.metadata || {} ),
2369
- categories: pattern.categories,
2370
- patternName: pattern.name,
2371
- name:
2372
- blocks[ 0 ].attributes.metadata?.name ||
2373
- pattern.title,
2374
- },
2375
- };
2376
- }
2377
- return {
2378
- ...pattern,
2379
- blocks,
2380
- };
2381
- },
2382
- ( state, patternName ) => [
2383
- unlock( select( STORE_NAME ) ).getPatternBySlug( patternName ),
2384
- ]
2385
- )
2352
+ ( select ) => ( state, patternName ) => {
2353
+ const pattern = unlock( select( STORE_NAME ) ).getPatternBySlug(
2354
+ patternName
2355
+ );
2356
+ return pattern ? getParsedPattern( pattern ) : null;
2357
+ }
2386
2358
  );
2387
2359
 
2388
2360
  const getAllowedPatternsDependants = ( select ) => ( state, rootClientId ) => [
@@ -2401,16 +2373,13 @@ const getAllowedPatternsDependants = ( select ) => ( state, rootClientId ) => [
2401
2373
  export const __experimentalGetAllowedPatterns = createRegistrySelector(
2402
2374
  ( select ) => {
2403
2375
  return createSelector( ( state, rootClientId = null ) => {
2404
- const {
2405
- getAllPatterns,
2406
- __experimentalGetParsedPattern: getParsedPattern,
2407
- } = unlock( select( STORE_NAME ) );
2376
+ const { getAllPatterns } = unlock( select( STORE_NAME ) );
2408
2377
  const patterns = getAllPatterns();
2409
2378
  const { allowedBlockTypes } = getSettings( state );
2410
2379
 
2411
2380
  const parsedPatterns = patterns
2412
2381
  .filter( ( { inserter = true } ) => !! inserter )
2413
- .map( ( { name } ) => getParsedPattern( name ) );
2382
+ .map( getParsedPattern );
2414
2383
  const availableParsedPatterns = parsedPatterns.filter(
2415
2384
  ( { blocks } ) =>
2416
2385
  checkAllowListRecursive( blocks, allowedBlockTypes )
@@ -1,3 +1,8 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { parse } from '@wordpress/blocks';
5
+
1
6
  /**
2
7
  * Internal dependencies
3
8
  */
@@ -7,6 +12,39 @@ import { STORE_NAME } from './constants';
7
12
 
8
13
  export const withRootClientIdOptionKey = Symbol( 'withRootClientId' );
9
14
 
15
+ const parsedPatternCache = new WeakMap();
16
+
17
+ function parsePattern( pattern ) {
18
+ const blocks = parse( pattern.content, {
19
+ __unstableSkipMigrationLogs: true,
20
+ } );
21
+ if ( blocks.length === 1 ) {
22
+ blocks[ 0 ].attributes = {
23
+ ...blocks[ 0 ].attributes,
24
+ metadata: {
25
+ ...( blocks[ 0 ].attributes.metadata || {} ),
26
+ categories: pattern.categories,
27
+ patternName: pattern.name,
28
+ name: blocks[ 0 ].attributes.metadata?.name || pattern.title,
29
+ },
30
+ };
31
+ }
32
+ return {
33
+ ...pattern,
34
+ blocks,
35
+ };
36
+ }
37
+
38
+ export function getParsedPattern( pattern ) {
39
+ let parsedPattern = parsedPatternCache.get( pattern );
40
+ if ( parsedPattern ) {
41
+ return parsedPattern;
42
+ }
43
+ parsedPattern = parsePattern( pattern );
44
+ parsedPatternCache.set( pattern, parsedPattern );
45
+ return parsedPattern;
46
+ }
47
+
10
48
  export const checkAllowList = ( list, item, defaultResult = null ) => {
11
49
  if ( typeof list === 'boolean' ) {
12
50
  return list;