@wordpress/block-editor 12.10.2 → 12.10.4

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 (103) hide show
  1. package/build/components/block-styles/index.js +1 -2
  2. package/build/components/block-styles/index.js.map +1 -1
  3. package/build/components/global-styles/hooks.js +1 -1
  4. package/build/components/global-styles/hooks.js.map +1 -1
  5. package/build/components/global-styles/use-global-styles-output.js +17 -5
  6. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  7. package/build/components/global-styles/utils.js +22 -0
  8. package/build/components/global-styles/utils.js.map +1 -1
  9. package/build/components/iframe/index.js +9 -1
  10. package/build/components/iframe/index.js.map +1 -1
  11. package/build/components/inserter/block-patterns-filter.js +11 -10
  12. package/build/components/inserter/block-patterns-filter.js.map +1 -1
  13. package/build/components/inserter/block-patterns-tab.js +1 -1
  14. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  15. package/build/components/inserter/media-tab/hooks.js +4 -33
  16. package/build/components/inserter/media-tab/hooks.js.map +1 -1
  17. package/build/components/link-control/search-item.js +36 -1
  18. package/build/components/link-control/search-item.js.map +1 -1
  19. package/build/components/list-view/block.js +1 -32
  20. package/build/components/list-view/block.js.map +1 -1
  21. package/build/components/list-view/branch.js +8 -1
  22. package/build/components/list-view/branch.js.map +1 -1
  23. package/build/components/list-view/index.js +8 -3
  24. package/build/components/list-view/index.js.map +1 -1
  25. package/build/components/list-view/utils.js +35 -0
  26. package/build/components/list-view/utils.js.map +1 -1
  27. package/build/components/tool-selector/index.js +2 -1
  28. package/build/components/tool-selector/index.js.map +1 -1
  29. package/build/hooks/block-rename-ui.js +5 -2
  30. package/build/hooks/block-rename-ui.js.map +1 -1
  31. package/build/store/actions.js +8 -12
  32. package/build/store/actions.js.map +1 -1
  33. package/build/store/private-selectors.js +55 -1
  34. package/build/store/private-selectors.js.map +1 -1
  35. package/build/store/reducer.js +19 -1
  36. package/build/store/reducer.js.map +1 -1
  37. package/build/utils/transform-styles/transforms/wrap.js +5 -0
  38. package/build/utils/transform-styles/transforms/wrap.js.map +1 -1
  39. package/build-module/components/block-styles/index.js +1 -2
  40. package/build-module/components/block-styles/index.js.map +1 -1
  41. package/build-module/components/global-styles/hooks.js +1 -1
  42. package/build-module/components/global-styles/hooks.js.map +1 -1
  43. package/build-module/components/global-styles/use-global-styles-output.js +17 -6
  44. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  45. package/build-module/components/global-styles/utils.js +21 -0
  46. package/build-module/components/global-styles/utils.js.map +1 -1
  47. package/build-module/components/iframe/index.js +9 -1
  48. package/build-module/components/iframe/index.js.map +1 -1
  49. package/build-module/components/inserter/block-patterns-filter.js +13 -12
  50. package/build-module/components/inserter/block-patterns-filter.js.map +1 -1
  51. package/build-module/components/inserter/block-patterns-tab.js +1 -1
  52. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  53. package/build-module/components/inserter/media-tab/hooks.js +5 -34
  54. package/build-module/components/inserter/media-tab/hooks.js.map +1 -1
  55. package/build-module/components/link-control/search-item.js +37 -2
  56. package/build-module/components/link-control/search-item.js.map +1 -1
  57. package/build-module/components/list-view/block.js +3 -34
  58. package/build-module/components/list-view/block.js.map +1 -1
  59. package/build-module/components/list-view/branch.js +8 -1
  60. package/build-module/components/list-view/branch.js.map +1 -1
  61. package/build-module/components/list-view/index.js +8 -3
  62. package/build-module/components/list-view/index.js.map +1 -1
  63. package/build-module/components/list-view/utils.js +34 -0
  64. package/build-module/components/list-view/utils.js.map +1 -1
  65. package/build-module/components/tool-selector/index.js +2 -1
  66. package/build-module/components/tool-selector/index.js.map +1 -1
  67. package/build-module/hooks/block-rename-ui.js +5 -2
  68. package/build-module/hooks/block-rename-ui.js.map +1 -1
  69. package/build-module/store/actions.js +8 -12
  70. package/build-module/store/actions.js.map +1 -1
  71. package/build-module/store/private-selectors.js +52 -0
  72. package/build-module/store/private-selectors.js.map +1 -1
  73. package/build-module/store/reducer.js +18 -1
  74. package/build-module/store/reducer.js.map +1 -1
  75. package/build-module/utils/transform-styles/transforms/wrap.js +5 -0
  76. package/build-module/utils/transform-styles/transforms/wrap.js.map +1 -1
  77. package/build-style/style-rtl.css +1 -1
  78. package/build-style/style.css +1 -1
  79. package/package.json +32 -32
  80. package/src/components/block-styles/index.js +1 -4
  81. package/src/components/global-styles/hooks.js +1 -1
  82. package/src/components/global-styles/test/use-global-styles-output.js +39 -0
  83. package/src/components/global-styles/use-global-styles-output.js +26 -6
  84. package/src/components/global-styles/utils.js +21 -0
  85. package/src/components/iframe/index.js +8 -1
  86. package/src/components/inserter/block-patterns-filter.js +22 -10
  87. package/src/components/inserter/block-patterns-tab.js +0 -3
  88. package/src/components/inserter/media-tab/hooks.js +10 -44
  89. package/src/components/inserter/style.scss +1 -1
  90. package/src/components/link-control/search-item.js +55 -2
  91. package/src/components/list-view/block.js +3 -43
  92. package/src/components/list-view/branch.js +11 -1
  93. package/src/components/list-view/index.js +8 -4
  94. package/src/components/list-view/utils.js +37 -0
  95. package/src/components/tool-selector/index.js +1 -1
  96. package/src/hooks/block-rename-ui.js +14 -10
  97. package/src/store/actions.js +7 -11
  98. package/src/store/private-selectors.js +72 -0
  99. package/src/store/reducer.js +17 -0
  100. package/src/store/test/actions.js +10 -16
  101. package/src/utils/transform-styles/transforms/test/__snapshots__/wrap.js.snap +13 -6
  102. package/src/utils/transform-styles/transforms/test/wrap.js +9 -0
  103. package/src/utils/transform-styles/transforms/wrap.js +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "12.10.2",
3
+ "version": "12.10.4",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -35,36 +35,36 @@
35
35
  "@emotion/react": "^11.7.1",
36
36
  "@emotion/styled": "^11.6.0",
37
37
  "@react-spring/web": "^9.4.5",
38
- "@wordpress/a11y": "^3.42.2",
39
- "@wordpress/api-fetch": "^6.39.2",
40
- "@wordpress/blob": "^3.42.2",
41
- "@wordpress/blocks": "^12.19.2",
42
- "@wordpress/commands": "^0.13.2",
43
- "@wordpress/components": "^25.8.2",
44
- "@wordpress/compose": "^6.19.2",
45
- "@wordpress/data": "^9.12.2",
46
- "@wordpress/date": "^4.42.2",
47
- "@wordpress/deprecated": "^3.42.2",
48
- "@wordpress/dom": "^3.42.2",
49
- "@wordpress/element": "^5.19.2",
50
- "@wordpress/escape-html": "^2.42.2",
51
- "@wordpress/hooks": "^3.42.2",
52
- "@wordpress/html-entities": "^3.42.2",
53
- "@wordpress/i18n": "^4.42.2",
54
- "@wordpress/icons": "^9.33.2",
55
- "@wordpress/is-shallow-equal": "^4.42.2",
56
- "@wordpress/keyboard-shortcuts": "^4.19.2",
57
- "@wordpress/keycodes": "^3.42.2",
58
- "@wordpress/notices": "^4.10.2",
59
- "@wordpress/preferences": "^3.19.2",
60
- "@wordpress/private-apis": "^0.24.2",
61
- "@wordpress/rich-text": "^6.19.2",
62
- "@wordpress/shortcode": "^3.42.2",
63
- "@wordpress/style-engine": "^1.25.2",
64
- "@wordpress/token-list": "^2.42.2",
65
- "@wordpress/url": "^3.43.2",
66
- "@wordpress/warning": "^2.42.2",
67
- "@wordpress/wordcount": "^3.42.2",
38
+ "@wordpress/a11y": "^3.42.4",
39
+ "@wordpress/api-fetch": "^6.39.4",
40
+ "@wordpress/blob": "^3.42.4",
41
+ "@wordpress/blocks": "^12.19.4",
42
+ "@wordpress/commands": "^0.13.4",
43
+ "@wordpress/components": "^25.8.4",
44
+ "@wordpress/compose": "^6.19.4",
45
+ "@wordpress/data": "^9.12.4",
46
+ "@wordpress/date": "^4.42.4",
47
+ "@wordpress/deprecated": "^3.42.4",
48
+ "@wordpress/dom": "^3.42.4",
49
+ "@wordpress/element": "^5.19.4",
50
+ "@wordpress/escape-html": "^2.42.4",
51
+ "@wordpress/hooks": "^3.42.4",
52
+ "@wordpress/html-entities": "^3.42.4",
53
+ "@wordpress/i18n": "^4.42.4",
54
+ "@wordpress/icons": "^9.33.4",
55
+ "@wordpress/is-shallow-equal": "^4.42.4",
56
+ "@wordpress/keyboard-shortcuts": "^4.19.4",
57
+ "@wordpress/keycodes": "^3.42.4",
58
+ "@wordpress/notices": "^4.10.4",
59
+ "@wordpress/preferences": "^3.19.4",
60
+ "@wordpress/private-apis": "^0.24.4",
61
+ "@wordpress/rich-text": "^6.19.4",
62
+ "@wordpress/shortcode": "^3.42.4",
63
+ "@wordpress/style-engine": "^1.25.4",
64
+ "@wordpress/token-list": "^2.42.4",
65
+ "@wordpress/url": "^3.43.4",
66
+ "@wordpress/warning": "^2.42.4",
67
+ "@wordpress/wordcount": "^3.42.4",
68
68
  "change-case": "^4.1.2",
69
69
  "classnames": "^2.3.1",
70
70
  "colord": "^2.7.0",
@@ -86,5 +86,5 @@
86
86
  "publishConfig": {
87
87
  "access": "public"
88
88
  },
89
- "gitHead": "5d2e3d07cc97af8090fc32c1e5d5013a2967e752"
89
+ "gitHead": "d1072bbcaf037a18d414da7865ebaf366a0e7062"
90
90
  }
@@ -14,7 +14,6 @@ import {
14
14
  Popover,
15
15
  } from '@wordpress/components';
16
16
  import deprecated from '@wordpress/deprecated';
17
- import { __ } from '@wordpress/i18n';
18
17
 
19
18
  /**
20
19
  * Internal dependencies
@@ -65,9 +64,7 @@ function BlockStyles( { clientId, onSwitch = noop, onHoverClassName = noop } ) {
65
64
  <div className="block-editor-block-styles">
66
65
  <div className="block-editor-block-styles__variants">
67
66
  { stylesToRender.map( ( style ) => {
68
- const buttonText = style.isDefault
69
- ? __( 'Default' )
70
- : style.label || style.name;
67
+ const buttonText = style.label || style.name;
71
68
 
72
69
  return (
73
70
  <Button
@@ -117,7 +117,7 @@ export function useGlobalSetting( propertyPath, blockName, source = 'all' ) {
117
117
  `settings${ appendedBlockPath }.${ setting }`
118
118
  ) ??
119
119
  getValueFromObjectPath( configToUse, `settings.${ setting }` );
120
- if ( value ) {
120
+ if ( value !== undefined ) {
121
121
  result = setImmutably( result, setting.split( '.' ), value );
122
122
  }
123
123
  } );
@@ -14,6 +14,7 @@ import {
14
14
  toCustomProperties,
15
15
  toStyles,
16
16
  getStylesDeclarations,
17
+ processCSSNesting,
17
18
  } from '../use-global-styles-output';
18
19
  import { ROOT_BLOCK_SELECTOR } from '../utils';
19
20
 
@@ -967,4 +968,42 @@ describe( 'global styles renderer', () => {
967
968
  ] );
968
969
  } );
969
970
  } );
971
+
972
+ describe( 'processCSSNesting', () => {
973
+ it( 'should return processed CSS without any nested selectors', () => {
974
+ expect(
975
+ processCSSNesting( 'color: red; margin: auto;', '.foo' )
976
+ ).toEqual( '.foo{color: red; margin: auto;}' );
977
+ } );
978
+ it( 'should return processed CSS with nested selectors', () => {
979
+ expect(
980
+ processCSSNesting(
981
+ 'color: red; margin: auto; &.one{color: blue;} & .two{color: green;}',
982
+ '.foo'
983
+ )
984
+ ).toEqual(
985
+ '.foo{color: red; margin: auto;}.foo.one{color: blue;}.foo .two{color: green;}'
986
+ );
987
+ } );
988
+ it( 'should return processed CSS with pseudo elements', () => {
989
+ expect(
990
+ processCSSNesting(
991
+ 'color: red; margin: auto; &::before{color: blue;} & ::before{color: green;} &.one::before{color: yellow;} & .two::before{color: purple;}',
992
+ '.foo'
993
+ )
994
+ ).toEqual(
995
+ '.foo{color: red; margin: auto;}.foo::before{color: blue;}.foo ::before{color: green;}.foo.one::before{color: yellow;}.foo .two::before{color: purple;}'
996
+ );
997
+ } );
998
+ it( 'should return processed CSS with multiple root selectors', () => {
999
+ expect(
1000
+ processCSSNesting(
1001
+ 'color: red; margin: auto; &.one{color: blue;} & .two{color: green;} &::before{color: yellow;} & ::before{color: purple;} &.three::before{color: orange;} & .four::before{color: skyblue;}',
1002
+ '.foo, .bar'
1003
+ )
1004
+ ).toEqual(
1005
+ '.foo, .bar{color: red; margin: auto;}.foo.one, .bar.one{color: blue;}.foo .two, .bar .two{color: green;}.foo::before, .bar::before{color: yellow;}.foo ::before, .bar ::before{color: purple;}.foo.three::before, .bar.three::before{color: orange;}.foo .four::before, .bar .four::before{color: skyblue;}'
1006
+ );
1007
+ } );
1008
+ } );
970
1009
  } );
@@ -15,7 +15,12 @@ import { getCSSRules } from '@wordpress/style-engine';
15
15
  /**
16
16
  * Internal dependencies
17
17
  */
18
- import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils';
18
+ import {
19
+ PRESET_METADATA,
20
+ ROOT_BLOCK_SELECTOR,
21
+ scopeSelector,
22
+ appendToSelector,
23
+ } from './utils';
19
24
  import { getBlockCSSSelector } from './get-block-css-selector';
20
25
  import {
21
26
  getTypographyFontSizeValue,
@@ -1124,18 +1129,33 @@ function updateConfigWithSeparator( config ) {
1124
1129
  return config;
1125
1130
  }
1126
1131
 
1127
- const processCSSNesting = ( css, blockSelector ) => {
1132
+ export function processCSSNesting( css, blockSelector ) {
1128
1133
  let processedCSS = '';
1129
1134
 
1130
1135
  // Split CSS nested rules.
1131
1136
  const parts = css.split( '&' );
1132
1137
  parts.forEach( ( part ) => {
1133
- processedCSS += ! part.includes( '{' )
1134
- ? blockSelector + '{' + part + '}' // If the part doesn't contain braces, it applies to the root level.
1135
- : blockSelector + part; // Prepend the selector, which effectively replaces the "&" character.
1138
+ const isRootCss = ! part.includes( '{' );
1139
+ if ( isRootCss ) {
1140
+ // If the part doesn't contain braces, it applies to the root level.
1141
+ processedCSS += `${ blockSelector }{${ part.trim() }}`;
1142
+ } else {
1143
+ // If the part contains braces, it's a nested CSS rule.
1144
+ const splittedPart = part.replace( '}', '' ).split( '{' );
1145
+ if ( splittedPart.length !== 2 ) {
1146
+ return;
1147
+ }
1148
+
1149
+ const [ nestedSelector, cssValue ] = splittedPart;
1150
+ const combinedSelector = nestedSelector.startsWith( ' ' )
1151
+ ? scopeSelector( blockSelector, nestedSelector )
1152
+ : appendToSelector( blockSelector, nestedSelector );
1153
+
1154
+ processedCSS += `${ combinedSelector }{${ cssValue.trim() }}`;
1155
+ }
1136
1156
  } );
1137
1157
  return processedCSS;
1138
- };
1158
+ }
1139
1159
 
1140
1160
  /**
1141
1161
  * Returns the global styles output using a global styles configuration.
@@ -393,6 +393,27 @@ export function scopeSelector( scope, selector ) {
393
393
  return selectorsScoped.join( ', ' );
394
394
  }
395
395
 
396
+ /**
397
+ * Appends a sub-selector to an existing one.
398
+ *
399
+ * Given the compounded `selector` "h1, h2, h3"
400
+ * and the `toAppend` selector ".some-class" the result will be
401
+ * "h1.some-class, h2.some-class, h3.some-class".
402
+ *
403
+ * @param {string} selector Original selector.
404
+ * @param {string} toAppend Selector to append.
405
+ *
406
+ * @return {string} The new selector.
407
+ */
408
+ export function appendToSelector( selector, toAppend ) {
409
+ if ( ! selector.includes( ',' ) ) {
410
+ return selector + toAppend;
411
+ }
412
+ const selectors = selector.split( ',' );
413
+ const newSelectors = selectors.map( ( sel ) => sel + toAppend );
414
+ return newSelectors.join( ',' );
415
+ }
416
+
396
417
  /**
397
418
  * Compares global style variations according to their styles and settings properties.
398
419
  *
@@ -38,7 +38,14 @@ function bubbleEvent( event, Constructor, frame ) {
38
38
  init[ key ] = event[ key ];
39
39
  }
40
40
 
41
- if ( event instanceof frame.ownerDocument.defaultView.MouseEvent ) {
41
+ // Check if the event is a MouseEvent generated within the iframe.
42
+ // If so, adjust the coordinates to be relative to the position of
43
+ // the iframe. This ensures that components such as Draggable
44
+ // receive coordinates relative to the window, instead of relative
45
+ // to the iframe. Without this, the Draggable event handler would
46
+ // result in components "jumping" position as soon as the user
47
+ // drags over the iframe.
48
+ if ( event instanceof frame.contentDocument.defaultView.MouseEvent ) {
42
49
  const rect = frame.getBoundingClientRect();
43
50
  init.clientX += rect.left;
44
51
  init.clientY += rect.top;
@@ -7,10 +7,11 @@ import {
7
7
  DropdownMenu,
8
8
  MenuGroup,
9
9
  MenuItemsChoice,
10
+ ExternalLink,
10
11
  } from '@wordpress/components';
11
12
  import { __ } from '@wordpress/i18n';
12
13
  import { Icon } from '@wordpress/icons';
13
- import { useMemo } from '@wordpress/element';
14
+ import { useMemo, createInterpolateElement } from '@wordpress/element';
14
15
 
15
16
  /**
16
17
  * Internal dependencies
@@ -73,13 +74,11 @@ export function BlockPatternsSyncFilter( {
73
74
  {
74
75
  value: SYNC_TYPES.full,
75
76
  label: __( 'Synced' ),
76
- info: __( 'Updated everywhere' ),
77
77
  disabled: shouldDisableSyncFilter,
78
78
  },
79
79
  {
80
80
  value: SYNC_TYPES.unsynced,
81
- label: __( 'Standard' ),
82
- info: __( 'Edit freely' ),
81
+ label: __( 'Not synced' ),
83
82
  disabled: shouldDisableSyncFilter,
84
83
  },
85
84
  ],
@@ -95,20 +94,17 @@ export function BlockPatternsSyncFilter( {
95
94
  },
96
95
  {
97
96
  value: PATTERN_TYPES.directory,
98
- label: __( 'Directory' ),
99
- info: __( 'Pattern directory & core' ),
97
+ label: __( 'Pattern Directory' ),
100
98
  disabled: shouldDisableNonUserSources,
101
99
  },
102
100
  {
103
101
  value: PATTERN_TYPES.theme,
104
- label: __( 'Theme' ),
105
- info: __( 'Bundled with the theme' ),
102
+ label: __( 'Theme & Plugins' ),
106
103
  disabled: shouldDisableNonUserSources,
107
104
  },
108
105
  {
109
106
  value: PATTERN_TYPES.user,
110
107
  label: __( 'User' ),
111
- info: __( 'Custom created' ),
112
108
  },
113
109
  ],
114
110
  [ shouldDisableNonUserSources ]
@@ -149,7 +145,7 @@ export function BlockPatternsSyncFilter( {
149
145
  >
150
146
  { () => (
151
147
  <>
152
- <MenuGroup label={ __( 'Author' ) }>
148
+ <MenuGroup label={ __( 'Source' ) }>
153
149
  <MenuItemsChoice
154
150
  choices={ patternSourceMenuOptions }
155
151
  onSelect={ ( value ) => {
@@ -175,6 +171,22 @@ export function BlockPatternsSyncFilter( {
175
171
  value={ patternSyncFilter }
176
172
  />
177
173
  </MenuGroup>
174
+ <div className="block-editor-tool-selector__help">
175
+ { createInterpolateElement(
176
+ __(
177
+ 'Patterns are available from the <Link>WordPress.org Pattern Directory</Link>, bundled in the active theme, or created by users on this site. Only patterns created on this site can be synced.'
178
+ ),
179
+ {
180
+ Link: (
181
+ <ExternalLink
182
+ href={ __(
183
+ 'https://wordpress.org/patterns/'
184
+ ) }
185
+ />
186
+ ),
187
+ }
188
+ ) }
189
+ </div>
178
190
  </>
179
191
  ) }
180
192
  </DropdownMenu>
@@ -321,9 +321,6 @@ export function BlockPatternsCategoryPanel( {
321
321
  category={ category }
322
322
  />
323
323
  </HStack>
324
- { category.description && (
325
- <Text>{ category.description }</Text>
326
- ) }
327
324
  { ! currentCategoryPatterns.length && (
328
325
  <Text
329
326
  variant="muted"
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useEffect, useState, useRef, useMemo } from '@wordpress/element';
4
+ import { useEffect, useState, useRef } from '@wordpress/element';
5
5
  import { useSelect } from '@wordpress/data';
6
6
 
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
10
  import { store as blockEditorStore } from '../../../store';
11
+ import { unlock } from '../../../lock-unlock';
11
12
 
12
- /** @typedef {import('./api').InserterMediaRequest} InserterMediaRequest */
13
- /** @typedef {import('./api').InserterMediaItem} InserterMediaItem */
13
+ /** @typedef {import('../../../store/actions').InserterMediaRequest} InserterMediaRequest */
14
+ /** @typedef {import('../../../store/actions').InserterMediaItem} InserterMediaItem */
14
15
 
15
16
  /**
16
17
  * Fetches media items based on the provided category.
@@ -50,48 +51,14 @@ export function useMediaResults( category, query = {} ) {
50
51
  return { mediaList, isLoading };
51
52
  }
52
53
 
53
- function useInserterMediaCategories() {
54
- const {
55
- inserterMediaCategories,
56
- allowedMimeTypes,
57
- enableOpenverseMediaCategory,
58
- } = useSelect( ( select ) => {
59
- const settings = select( blockEditorStore ).getSettings();
60
- return {
61
- inserterMediaCategories: settings.inserterMediaCategories,
62
- allowedMimeTypes: settings.allowedMimeTypes,
63
- enableOpenverseMediaCategory: settings.enableOpenverseMediaCategory,
64
- };
65
- }, [] );
66
- // The allowed `mime_types` can be altered by `upload_mimes` filter and restrict
67
- // some of them. In this case we shouldn't add the category to the available media
68
- // categories list in the inserter.
69
- const allowedCategories = useMemo( () => {
70
- if ( ! inserterMediaCategories || ! allowedMimeTypes ) {
71
- return;
72
- }
73
- return inserterMediaCategories.filter( ( category ) => {
74
- // Check if Openverse category is enabled.
75
- if (
76
- ! enableOpenverseMediaCategory &&
77
- category.name === 'openverse'
78
- ) {
79
- return false;
80
- }
81
- return Object.values( allowedMimeTypes ).some( ( mimeType ) =>
82
- mimeType.startsWith( `${ category.mediaType }/` )
83
- );
84
- } );
85
- }, [
86
- inserterMediaCategories,
87
- allowedMimeTypes,
88
- enableOpenverseMediaCategory,
89
- ] );
90
- return allowedCategories;
91
- }
92
-
93
54
  export function useMediaCategories( rootClientId ) {
94
55
  const [ categories, setCategories ] = useState( [] );
56
+
57
+ const inserterMediaCategories = useSelect(
58
+ ( select ) =>
59
+ unlock( select( blockEditorStore ) ).getInserterMediaCategories(),
60
+ []
61
+ );
95
62
  const { canInsertImage, canInsertVideo, canInsertAudio } = useSelect(
96
63
  ( select ) => {
97
64
  const { canInsertBlockType } = select( blockEditorStore );
@@ -112,7 +79,6 @@ export function useMediaCategories( rootClientId ) {
112
79
  },
113
80
  [ rootClientId ]
114
81
  );
115
- const inserterMediaCategories = useInserterMediaCategories();
116
82
  useEffect( () => {
117
83
  ( async () => {
118
84
  const _categories = [];
@@ -314,7 +314,7 @@ $block-inserter-tabs-height: 44px;
314
314
  overflow-y: auto;
315
315
  flex-grow: 1;
316
316
  height: 100%;
317
- padding: $grid-unit-40 $grid-unit-30;
317
+ padding: $grid-unit-20 $grid-unit-30;
318
318
  }
319
319
  }
320
320
 
@@ -13,7 +13,8 @@ import {
13
13
  file,
14
14
  } from '@wordpress/icons';
15
15
  import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
16
- import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url';
16
+ import { safeDecodeURI, filterURLForDisplay, getPath } from '@wordpress/url';
17
+ import { pipe } from '@wordpress/compose';
17
18
 
18
19
  const ICONS_MAP = {
19
20
  post: postList,
@@ -44,6 +45,58 @@ function SearchItemIcon( { isURL, suggestion } ) {
44
45
  return null;
45
46
  }
46
47
 
48
+ /**
49
+ * Adds a leading slash to a url if it doesn't already have one.
50
+ * @param {string} url the url to add a leading slash to.
51
+ * @return {string} the url with a leading slash.
52
+ */
53
+ function addLeadingSlash( url ) {
54
+ const trimmedURL = url?.trim();
55
+
56
+ if ( ! trimmedURL?.length ) return url;
57
+
58
+ return url?.replace( /^\/?/, '/' );
59
+ }
60
+
61
+ function removeTrailingSlash( url ) {
62
+ const trimmedURL = url?.trim();
63
+
64
+ if ( ! trimmedURL?.length ) return url;
65
+
66
+ return url?.replace( /\/$/, '' );
67
+ }
68
+
69
+ const partialRight =
70
+ ( fn, ...partialArgs ) =>
71
+ ( ...args ) =>
72
+ fn( ...args, ...partialArgs );
73
+
74
+ const defaultTo = ( d ) => ( v ) => {
75
+ return v === null || v === undefined || v !== v ? d : v;
76
+ };
77
+
78
+ /**
79
+ * Prepares a URL for display in the UI.
80
+ * - decodes the URL.
81
+ * - filters it (removes protocol, www, etc.).
82
+ * - truncates it if necessary.
83
+ * - adds a leading slash.
84
+ * @param {string} url the url.
85
+ * @return {string} the processed url to display.
86
+ */
87
+ function getURLForDisplay( url ) {
88
+ if ( ! url ) return url;
89
+
90
+ return pipe(
91
+ safeDecodeURI,
92
+ getPath,
93
+ defaultTo( '' ),
94
+ partialRight( filterURLForDisplay, 24 ),
95
+ removeTrailingSlash,
96
+ addLeadingSlash
97
+ )( url );
98
+ }
99
+
47
100
  export const LinkControlSearchItem = ( {
48
101
  itemProps,
49
102
  suggestion,
@@ -54,7 +107,7 @@ export const LinkControlSearchItem = ( {
54
107
  } ) => {
55
108
  const info = isURL
56
109
  ? __( 'Press ENTER to add this link' )
57
- : filterURLForDisplay( safeDecodeURI( suggestion?.url ), 24 );
110
+ : getURLForDisplay( suggestion.url );
58
111
 
59
112
  return (
60
113
  <MenuItem
@@ -13,16 +13,9 @@ import {
13
13
  } from '@wordpress/components';
14
14
  import { useInstanceId } from '@wordpress/compose';
15
15
  import { moreVertical } from '@wordpress/icons';
16
- import {
17
- useState,
18
- useRef,
19
- useEffect,
20
- useCallback,
21
- memo,
22
- } from '@wordpress/element';
16
+ import { useState, useRef, useCallback, memo } from '@wordpress/element';
23
17
  import { useDispatch, useSelect } from '@wordpress/data';
24
18
  import { sprintf, __ } from '@wordpress/i18n';
25
- import { focus } from '@wordpress/dom';
26
19
  import { ESCAPE } from '@wordpress/keycodes';
27
20
 
28
21
  /**
@@ -36,7 +29,7 @@ import {
36
29
  } from '../block-mover/button';
37
30
  import ListViewBlockContents from './block-contents';
38
31
  import { useListViewContext } from './context';
39
- import { getBlockPositionDescription } from './utils';
32
+ import { getBlockPositionDescription, focusListItem } from './utils';
40
33
  import { store as blockEditorStore } from '../../store';
41
34
  import useBlockDisplayInformation from '../use-block-display-information';
42
35
  import { useBlockLock } from '../block-lock';
@@ -120,7 +113,6 @@ function ListViewBlock( {
120
113
  );
121
114
 
122
115
  const {
123
- isTreeGridMounted,
124
116
  expand,
125
117
  collapse,
126
118
  BlockSettingsMenu,
@@ -142,15 +134,6 @@ function ListViewBlock( {
142
134
  { 'is-visible': isHovered || isFirstSelectedBlock }
143
135
  );
144
136
 
145
- // If ListView has experimental features related to the Persistent List View,
146
- // only focus the selected list item on mount; otherwise the list would always
147
- // try to steal the focus from the editor canvas.
148
- useEffect( () => {
149
- if ( ! isTreeGridMounted && isSelected ) {
150
- cellRef.current.focus();
151
- }
152
- }, [] );
153
-
154
137
  // If multiple blocks are selected, deselect all blocks when the user
155
138
  // presses the escape key.
156
139
  const onKeyDown = ( event ) => {
@@ -188,30 +171,7 @@ function ListViewBlock( {
188
171
  selectBlock( undefined, focusClientId, null, null );
189
172
  }
190
173
 
191
- const getFocusElement = () => {
192
- const row = treeGridElementRef.current?.querySelector(
193
- `[role=row][data-block="${ focusClientId }"]`
194
- );
195
- if ( ! row ) return null;
196
- // Focus the first focusable in the row, which is the ListViewBlockSelectButton.
197
- return focus.focusable.find( row )[ 0 ];
198
- };
199
-
200
- let focusElement = getFocusElement();
201
- if ( focusElement ) {
202
- focusElement.focus();
203
- } else {
204
- // The element hasn't been painted yet. Defer focusing on the next frame.
205
- // This could happen when all blocks have been deleted and the default block
206
- // hasn't been added to the editor yet.
207
- window.requestAnimationFrame( () => {
208
- focusElement = getFocusElement();
209
- // Ignore if the element still doesn't exist.
210
- if ( focusElement ) {
211
- focusElement.focus();
212
- }
213
- } );
214
- }
174
+ focusListItem( focusClientId, treeGridElementRef );
215
175
  },
216
176
  [ selectBlock, treeGridElementRef ]
217
177
  );
@@ -168,8 +168,18 @@ function ListViewBranch( props ) {
168
168
  );
169
169
  const isSelectedBranch =
170
170
  isBranchSelected || ( isSelected && hasNestedBlocks );
171
+
172
+ // To avoid performance issues, we only render blocks that are in view,
173
+ // or blocks that are selected or dragged. If a block is selected,
174
+ // it is only counted if it is the first of the block selection.
175
+ // This prevents the entire tree from being rendered when a branch is
176
+ // selected, or a user selects all blocks, while still enabling scroll
177
+ // into view behavior when selecting a block or opening the list view.
171
178
  const showBlock =
172
- isDragged || blockInView || isSelected || isBranchDragged;
179
+ isDragged ||
180
+ blockInView ||
181
+ isBranchDragged ||
182
+ ( isSelected && clientId === selectedClientIds[ 0 ] );
173
183
  return (
174
184
  <AsyncModeProvider key={ clientId } value={ ! isSelected }>
175
185
  { showBlock && (
@@ -32,6 +32,7 @@ import useListViewDropZone from './use-list-view-drop-zone';
32
32
  import useListViewExpandSelectedItem from './use-list-view-expand-selected-item';
33
33
  import { store as blockEditorStore } from '../../store';
34
34
  import { BlockSettingsDropdown } from '../block-settings-menu/block-settings-dropdown';
35
+ import { focusListItem } from './utils';
35
36
 
36
37
  const expanded = ( state, action ) => {
37
38
  if ( Array.isArray( action.clientIds ) ) {
@@ -132,8 +133,6 @@ function ListViewComponent(
132
133
  const elementRef = useRef();
133
134
  const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] );
134
135
 
135
- const isMounted = useRef( false );
136
-
137
136
  const [ insertedBlock, setInsertedBlock ] = useState( null );
138
137
 
139
138
  const { setSelectedTreeId } = useListViewExpandSelectedItem( {
@@ -156,7 +155,13 @@ function ListViewComponent(
156
155
  [ setSelectedTreeId, updateBlockSelection, onSelect, getBlock ]
157
156
  );
158
157
  useEffect( () => {
159
- isMounted.current = true;
158
+ // If a blocks are already selected when the list view is initially
159
+ // mounted, shift focus to the first selected block.
160
+ if ( selectedClientIds?.length ) {
161
+ focusListItem( selectedClientIds[ 0 ], elementRef );
162
+ }
163
+ // Disable reason: Only focus on the selected item when the list view is mounted.
164
+ // eslint-disable-next-line react-hooks/exhaustive-deps
160
165
  }, [] );
161
166
 
162
167
  const expand = useCallback(
@@ -204,7 +209,6 @@ function ListViewComponent(
204
209
 
205
210
  const contextValue = useMemo(
206
211
  () => ( {
207
- isTreeGridMounted: isMounted.current,
208
212
  draggedClientIds,
209
213
  expandedState,
210
214
  expand,