@wordpress/block-editor 9.5.0 → 9.6.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 (147) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-lock/modal.js +2 -2
  3. package/build/components/block-lock/modal.js.map +1 -1
  4. package/build/components/block-mobile-toolbar/block-actions-menu.native.js +1 -1
  5. package/build/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  6. package/build/components/block-mover/mover-description.js +95 -32
  7. package/build/components/block-mover/mover-description.js.map +1 -1
  8. package/build/components/block-settings-menu-controls/index.js +1 -1
  9. package/build/components/block-settings-menu-controls/index.js.map +1 -1
  10. package/build/components/block-styles/preview.native.js +1 -3
  11. package/build/components/block-styles/preview.native.js.map +1 -1
  12. package/build/components/block-tools/block-selection-button.js +1 -7
  13. package/build/components/block-tools/block-selection-button.js.map +1 -1
  14. package/build/components/block-tools/index.js +4 -1
  15. package/build/components/block-tools/index.js.map +1 -1
  16. package/build/components/block-types-list/index.native.js +65 -23
  17. package/build/components/block-types-list/index.native.js.map +1 -1
  18. package/build/components/colors-gradients/control.js +1 -0
  19. package/build/components/colors-gradients/control.js.map +1 -1
  20. package/build/components/colors-gradients/dropdown.js +5 -2
  21. package/build/components/colors-gradients/dropdown.js.map +1 -1
  22. package/build/components/image-editor/cropper.js +4 -3
  23. package/build/components/image-editor/cropper.js.map +1 -1
  24. package/build/components/image-editor/index.js +3 -1
  25. package/build/components/image-editor/index.js.map +1 -1
  26. package/build/components/inserter/block-types-tab.native.js +30 -16
  27. package/build/components/inserter/block-types-tab.native.js.map +1 -1
  28. package/build/components/inserter/preview-panel.js +8 -8
  29. package/build/components/inserter/preview-panel.js.map +1 -1
  30. package/build/components/inserter/reusable-blocks-tab.native.js +5 -1
  31. package/build/components/inserter/reusable-blocks-tab.native.js.map +1 -1
  32. package/build/components/inserter/search-results.native.js +5 -2
  33. package/build/components/inserter/search-results.native.js.map +1 -1
  34. package/build/components/inserter/utils.native.js +21 -0
  35. package/build/components/inserter/utils.native.js.map +1 -1
  36. package/build/components/inserter-list-item/index.js +5 -1
  37. package/build/components/inserter-list-item/index.js.map +1 -1
  38. package/build/components/list-view/branch.js +1 -7
  39. package/build/components/list-view/branch.js.map +1 -1
  40. package/build/components/observe-typing/index.js +22 -8
  41. package/build/components/observe-typing/index.js.map +1 -1
  42. package/build/components/rich-text/format-toolbar-container.js +61 -12
  43. package/build/components/rich-text/format-toolbar-container.js.map +1 -1
  44. package/build/components/rich-text/index.js +2 -1
  45. package/build/components/rich-text/index.js.map +1 -1
  46. package/build/components/rich-text/use-format-types.js +36 -16
  47. package/build/components/rich-text/use-format-types.js.map +1 -1
  48. package/build/components/writing-flow/use-tab-nav.js +1 -1
  49. package/build/components/writing-flow/use-tab-nav.js.map +1 -1
  50. package/build/hooks/layout.js +14 -2
  51. package/build/hooks/layout.js.map +1 -1
  52. package/build/hooks/style.js +41 -31
  53. package/build/hooks/style.js.map +1 -1
  54. package/build/layouts/flow.js +26 -3
  55. package/build/layouts/flow.js.map +1 -1
  56. package/build-module/components/block-lock/modal.js +2 -2
  57. package/build-module/components/block-lock/modal.js.map +1 -1
  58. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js +2 -2
  59. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  60. package/build-module/components/block-mover/mover-description.js +97 -33
  61. package/build-module/components/block-mover/mover-description.js.map +1 -1
  62. package/build-module/components/block-settings-menu-controls/index.js +2 -2
  63. package/build-module/components/block-settings-menu-controls/index.js.map +1 -1
  64. package/build-module/components/block-styles/preview.native.js +2 -3
  65. package/build-module/components/block-styles/preview.native.js.map +1 -1
  66. package/build-module/components/block-tools/block-selection-button.js +1 -7
  67. package/build-module/components/block-tools/block-selection-button.js.map +1 -1
  68. package/build-module/components/block-tools/index.js +4 -1
  69. package/build-module/components/block-tools/index.js.map +1 -1
  70. package/build-module/components/block-types-list/index.native.js +67 -25
  71. package/build-module/components/block-types-list/index.native.js.map +1 -1
  72. package/build-module/components/colors-gradients/control.js +1 -0
  73. package/build-module/components/colors-gradients/control.js.map +1 -1
  74. package/build-module/components/colors-gradients/dropdown.js +6 -3
  75. package/build-module/components/colors-gradients/dropdown.js.map +1 -1
  76. package/build-module/components/image-editor/cropper.js +4 -3
  77. package/build-module/components/image-editor/cropper.js.map +1 -1
  78. package/build-module/components/image-editor/index.js +3 -1
  79. package/build-module/components/image-editor/index.js.map +1 -1
  80. package/build-module/components/inserter/block-types-tab.native.js +31 -15
  81. package/build-module/components/inserter/block-types-tab.native.js.map +1 -1
  82. package/build-module/components/inserter/preview-panel.js +9 -9
  83. package/build-module/components/inserter/preview-panel.js.map +1 -1
  84. package/build-module/components/inserter/reusable-blocks-tab.native.js +6 -2
  85. package/build-module/components/inserter/reusable-blocks-tab.native.js.map +1 -1
  86. package/build-module/components/inserter/search-results.native.js +6 -3
  87. package/build-module/components/inserter/search-results.native.js.map +1 -1
  88. package/build-module/components/inserter/utils.native.js +19 -0
  89. package/build-module/components/inserter/utils.native.js.map +1 -1
  90. package/build-module/components/inserter-list-item/index.js +4 -1
  91. package/build-module/components/inserter-list-item/index.js.map +1 -1
  92. package/build-module/components/list-view/branch.js +1 -6
  93. package/build-module/components/list-view/branch.js.map +1 -1
  94. package/build-module/components/observe-typing/index.js +22 -8
  95. package/build-module/components/observe-typing/index.js.map +1 -1
  96. package/build-module/components/rich-text/format-toolbar-container.js +58 -12
  97. package/build-module/components/rich-text/format-toolbar-container.js.map +1 -1
  98. package/build-module/components/rich-text/index.js +2 -1
  99. package/build-module/components/rich-text/index.js.map +1 -1
  100. package/build-module/components/rich-text/use-format-types.js +37 -18
  101. package/build-module/components/rich-text/use-format-types.js.map +1 -1
  102. package/build-module/components/writing-flow/use-tab-nav.js +1 -1
  103. package/build-module/components/writing-flow/use-tab-nav.js.map +1 -1
  104. package/build-module/hooks/layout.js +14 -2
  105. package/build-module/hooks/layout.js.map +1 -1
  106. package/build-module/hooks/style.js +44 -35
  107. package/build-module/hooks/style.js.map +1 -1
  108. package/build-module/layouts/flow.js +25 -3
  109. package/build-module/layouts/flow.js.map +1 -1
  110. package/build-style/style-rtl.css +2 -2
  111. package/build-style/style.css +2 -2
  112. package/package.json +28 -28
  113. package/src/components/block-lock/modal.js +5 -5
  114. package/src/components/block-mobile-toolbar/block-actions-menu.native.js +3 -3
  115. package/src/components/block-mover/mover-description.js +131 -48
  116. package/src/components/block-mover/test/mover-description.js +55 -3
  117. package/src/components/block-settings-menu-controls/index.js +2 -2
  118. package/src/components/block-styles/preview.native.js +2 -2
  119. package/src/components/block-tools/block-selection-button.js +0 -5
  120. package/src/components/block-tools/index.js +4 -1
  121. package/src/components/block-types-list/index.native.js +76 -24
  122. package/src/components/block-types-list/style.native.scss +18 -0
  123. package/src/components/color-palette/test/__snapshots__/control.js.snap +0 -4
  124. package/src/components/colors/test/__snapshots__/with-colors.js.snap +1 -1
  125. package/src/components/colors/test/with-colors.js +1 -1
  126. package/src/components/colors-gradients/control.js +1 -0
  127. package/src/components/colors-gradients/dropdown.js +8 -2
  128. package/src/components/colors-gradients/style.scss +7 -8
  129. package/src/components/image-editor/cropper.js +9 -3
  130. package/src/components/image-editor/index.js +2 -0
  131. package/src/components/inserter/block-types-tab.native.js +42 -21
  132. package/src/components/inserter/preview-panel.js +6 -14
  133. package/src/components/inserter/reusable-blocks-tab.native.js +4 -2
  134. package/src/components/inserter/search-results.native.js +4 -2
  135. package/src/components/inserter/test/block-types-tab.native.js +2 -0
  136. package/src/components/inserter/test/utils.native.js +37 -0
  137. package/src/components/inserter/utils.native.js +11 -0
  138. package/src/components/inserter-list-item/index.js +4 -1
  139. package/src/components/list-view/branch.js +1 -6
  140. package/src/components/observe-typing/index.js +17 -14
  141. package/src/components/rich-text/format-toolbar-container.js +63 -14
  142. package/src/components/rich-text/index.js +1 -0
  143. package/src/components/rich-text/use-format-types.js +38 -17
  144. package/src/components/writing-flow/use-tab-nav.js +1 -1
  145. package/src/hooks/layout.js +14 -3
  146. package/src/hooks/style.js +46 -39
  147. package/src/layouts/flow.js +23 -1
@@ -53,6 +53,8 @@ export default function BlockTools( {
53
53
  } = useDispatch( blockEditorStore );
54
54
 
55
55
  function onKeyDown( event ) {
56
+ if ( event.defaultPrevented ) return;
57
+
56
58
  if ( isMatch( 'core/block-editor/move-up', event ) ) {
57
59
  const clientIds = getSelectedBlockClientIds();
58
60
  if ( clientIds.length ) {
@@ -93,12 +95,13 @@ export default function BlockTools( {
93
95
  }
94
96
  } else if ( isMatch( 'core/block-editor/unselect', event ) ) {
95
97
  const clientIds = getSelectedBlockClientIds();
96
- if ( clientIds.length > 1 ) {
98
+ if ( clientIds.length ) {
97
99
  event.preventDefault();
98
100
  clearSelectedBlock();
99
101
  event.target.ownerDocument.defaultView
100
102
  .getSelection()
101
103
  .removeAllRanges();
104
+ __unstableContentRef?.current.focus();
102
105
  }
103
106
  }
104
107
  }
@@ -4,7 +4,9 @@
4
4
  import {
5
5
  Dimensions,
6
6
  FlatList,
7
+ SectionList,
7
8
  StyleSheet,
9
+ Text,
8
10
  TouchableWithoutFeedback,
9
11
  View,
10
12
  } from 'react-native';
@@ -13,7 +15,11 @@ import {
13
15
  * WordPress dependencies
14
16
  */
15
17
  import { useState, useEffect } from '@wordpress/element';
16
- import { BottomSheet, InserterButton } from '@wordpress/components';
18
+ import { BottomSheet, Gradient, InserterButton } from '@wordpress/components';
19
+ import {
20
+ usePreferredColorScheme,
21
+ usePreferredColorSchemeStyle,
22
+ } from '@wordpress/compose';
17
23
 
18
24
  /**
19
25
  * Internal dependencies
@@ -24,7 +30,7 @@ const MIN_COL_NUM = 3;
24
30
 
25
31
  export default function BlockTypesList( {
26
32
  name,
27
- items,
33
+ sections,
28
34
  onSelect,
29
35
  listProps,
30
36
  initialNumToRender = 3,
@@ -80,33 +86,79 @@ export default function BlockTypesList( {
80
86
  listProps.contentContainerStyle
81
87
  );
82
88
 
89
+ const renderSection = ( { item } ) => {
90
+ return (
91
+ <TouchableWithoutFeedback accessible={ false }>
92
+ <FlatList
93
+ data={ item.list }
94
+ key={ `InserterUI-${ name }-${ numberOfColumns }` } // Re-render when numberOfColumns changes.
95
+ numColumns={ numberOfColumns }
96
+ ItemSeparatorComponent={ () => (
97
+ <TouchableWithoutFeedback accessible={ false }>
98
+ <View
99
+ style={
100
+ styles[ 'block-types-list__row-separator' ]
101
+ }
102
+ />
103
+ </TouchableWithoutFeedback>
104
+ ) }
105
+ scrollEnabled={ false }
106
+ renderItem={ renderListItem }
107
+ />
108
+ </TouchableWithoutFeedback>
109
+ );
110
+ };
111
+
112
+ const renderListItem = ( { item } ) => {
113
+ return (
114
+ <InserterButton
115
+ item={ item }
116
+ itemWidth={ itemWidth }
117
+ maxWidth={ maxWidth }
118
+ onSelect={ onSelect }
119
+ />
120
+ );
121
+ };
122
+
123
+ const colorScheme = usePreferredColorScheme();
124
+ const sectionHeaderGradientValue =
125
+ colorScheme === 'light'
126
+ ? 'linear-gradient(#fff 70%, rgba(255, 255, 255, 0))'
127
+ : 'linear-gradient(#2e2e2e 70%, rgba(46, 46, 46, 0))';
128
+ const sectionHeaderTitleStyles = usePreferredColorSchemeStyle(
129
+ styles[ 'block-types-list__section-header-title' ],
130
+ styles[ 'block-types-list__section-header-title--dark' ]
131
+ );
132
+
133
+ const renderSectionHeader = ( { section: { metadata } } ) => {
134
+ if ( ! metadata?.icon || ! metadata?.title ) {
135
+ return null;
136
+ }
137
+
138
+ return (
139
+ <TouchableWithoutFeedback accessible={ false }>
140
+ <Gradient
141
+ gradientValue={ sectionHeaderGradientValue }
142
+ style={ styles[ 'block-types-list__section-header' ] }
143
+ >
144
+ { metadata?.icon }
145
+ <Text style={ sectionHeaderTitleStyles }>
146
+ { metadata?.title }
147
+ </Text>
148
+ </Gradient>
149
+ </TouchableWithoutFeedback>
150
+ );
151
+ };
152
+
83
153
  return (
84
- <FlatList
154
+ <SectionList
85
155
  onLayout={ onLayout }
86
- key={ `InserterUI-${ name }-${ numberOfColumns }` } // Re-render when numberOfColumns changes.
87
156
  testID={ `InserterUI-${ name }` }
88
157
  keyboardShouldPersistTaps="always"
89
- numColumns={ numberOfColumns }
90
- data={ items }
158
+ sections={ sections }
91
159
  initialNumToRender={ initialNumToRender }
92
- ItemSeparatorComponent={ () => (
93
- <TouchableWithoutFeedback accessible={ false }>
94
- <View
95
- style={ styles[ 'block-types-list__row-separator' ] }
96
- />
97
- </TouchableWithoutFeedback>
98
- ) }
99
- keyExtractor={ ( item ) => item.id }
100
- renderItem={ ( { item } ) => (
101
- <InserterButton
102
- { ...{
103
- item,
104
- itemWidth,
105
- maxWidth,
106
- onSelect,
107
- } }
108
- />
109
- ) }
160
+ renderItem={ renderSection }
161
+ renderSectionHeader={ renderSectionHeader }
110
162
  { ...listProps }
111
163
  contentContainerStyle={ {
112
164
  ...contentContainerStyle,
@@ -5,3 +5,21 @@
5
5
  .block-types-list__column {
6
6
  padding: $grid-unit-20;
7
7
  }
8
+
9
+ .block-types-list__section-header {
10
+ align-items: center;
11
+ flex-direction: row;
12
+ justify-content: center;
13
+ padding-bottom: 16;
14
+ padding-top: 32;
15
+ }
16
+
17
+ .block-types-list__section-header-title {
18
+ color: $black;
19
+ font-size: 16px;
20
+ margin-left: 10px;
21
+ }
22
+
23
+ .block-types-list__section-header-title--dark {
24
+ color: $white;
25
+ }
@@ -6,10 +6,6 @@ exports[`ColorPaletteControl matches the snapshot 1`] = `
6
6
  font-size: 13px;
7
7
  }
8
8
 
9
- .emotion-2 {
10
- margin-bottom: calc(4px * 2);
11
- }
12
-
13
9
  .components-panel__row .emotion-2 {
14
10
  margin-bottom: inherit;
15
11
  }
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`createCustomColorsHOC provides the the wrapped component with color values and setter functions as props 1`] = `
3
+ exports[`createCustomColorsHOC provides the wrapped component with color values and setter functions as props 1`] = `
4
4
  <Component
5
5
  attributes={
6
6
  Object {
@@ -9,7 +9,7 @@ import { shallow, mount } from 'enzyme';
9
9
  import { createCustomColorsHOC } from '../with-colors';
10
10
 
11
11
  describe( 'createCustomColorsHOC', () => {
12
- it( 'provides the the wrapped component with color values and setter functions as props', () => {
12
+ it( 'provides the wrapped component with color values and setter functions as props', () => {
13
13
  const withCustomColors = createCustomColorsHOC( [
14
14
  { name: 'Red', slug: 'red', color: 'ff0000' },
15
15
  ] );
@@ -115,6 +115,7 @@ function ColorGradientControlInner( {
115
115
 
116
116
  return (
117
117
  <BaseControl
118
+ __nextHasNoMarginBottom
118
119
  className={ classnames(
119
120
  'block-editor-color-gradient-control',
120
121
  className
@@ -11,6 +11,7 @@ import {
11
11
  ColorIndicator,
12
12
  Dropdown,
13
13
  FlexItem,
14
+ __experimentalDropdownContentWrapper as DropdownContentWrapper,
14
15
  __experimentalHStack as HStack,
15
16
  __experimentalToolsPanelItem as ToolsPanelItem,
16
17
  } from '@wordpress/components';
@@ -155,10 +156,15 @@ export default function ColorGradientSettingsDropdown( {
155
156
  <Dropdown
156
157
  popoverProps={ popoverProps }
157
158
  className="block-editor-tools-panel-color-gradient-settings__dropdown"
158
- contentClassName="block-editor-panel-color-gradient-settings__dropdown-content"
159
159
  renderToggle={ renderToggle( toggleSettings ) }
160
160
  renderContent={ () => (
161
- <ColorGradientControl { ...controlProps } />
161
+ <DropdownContentWrapper paddingSize="medium">
162
+ <div className="block-editor-panel-color-gradient-settings__dropdown-content">
163
+ <ColorGradientControl
164
+ { ...controlProps }
165
+ />
166
+ </div>
167
+ </DropdownContentWrapper>
162
168
  ) }
163
169
  />
164
170
  </WithToolsPanelItem>
@@ -1,3 +1,8 @@
1
+ // Must equal $color-palette-circle-size from:
2
+ // @wordpress/components/src/circular-option-picker/style.scss
3
+ $swatch-size: 28px;
4
+ $swatch-gap: 12px;
5
+
1
6
  .block-editor-color-gradient-control {
2
7
  .block-editor-color-gradient-control__color-indicator {
3
8
  margin-bottom: $grid-unit-15;
@@ -18,11 +23,6 @@
18
23
  }
19
24
 
20
25
  .block-editor-panel-color-gradient-settings {
21
-
22
- // Must equal $color-palette-circle-size from:
23
- // @wordpress/components/src/circular-option-picker/style.scss
24
- $swatch-size: 28px;
25
-
26
26
  @media screen and (min-width: $break-medium) {
27
27
  .components-circular-option-picker__swatches {
28
28
  display: grid;
@@ -40,11 +40,10 @@
40
40
 
41
41
  }
42
42
 
43
- .block-editor-panel-color-gradient-settings__dropdown-content .components-popover__content {
44
- width: $sidebar-width;
43
+ .block-editor-panel-color-gradient-settings__dropdown-content {
44
+ width: ( $swatch-size * 6 ) + ( $swatch-gap * 5 ); // Ensure the popover perfectly wraps the swatches.
45
45
  }
46
46
 
47
-
48
47
  .block-editor-panel-color-gradient-settings__color-indicator {
49
48
  // Show a diagonal line (crossed out) for empty swatches.
50
49
  background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
@@ -23,6 +23,7 @@ export default function ImageCropper( {
23
23
  clientWidth,
24
24
  naturalHeight,
25
25
  naturalWidth,
26
+ borderProps,
26
27
  } ) {
27
28
  const {
28
29
  isInProgress,
@@ -44,10 +45,15 @@ export default function ImageCropper( {
44
45
 
45
46
  return (
46
47
  <div
47
- className={ classnames( 'wp-block-image__crop-area', {
48
- 'is-applying': isInProgress,
49
- } ) }
48
+ className={ classnames(
49
+ 'wp-block-image__crop-area',
50
+ borderProps?.className,
51
+ {
52
+ 'is-applying': isInProgress,
53
+ }
54
+ ) }
50
55
  style={ {
56
+ ...borderProps?.style,
51
57
  width: width || clientWidth,
52
58
  height: editedHeight,
53
59
  } }
@@ -20,10 +20,12 @@ export default function ImageEditor( {
20
20
  clientWidth,
21
21
  naturalHeight,
22
22
  naturalWidth,
23
+ borderProps,
23
24
  } ) {
24
25
  return (
25
26
  <>
26
27
  <Cropper
28
+ borderProps={ borderProps }
27
29
  url={ url }
28
30
  width={ width }
29
31
  height={ height }
@@ -1,36 +1,29 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useSelect } from '@wordpress/data';
4
+ import { useMemo } from '@wordpress/element';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
9
  import BlockTypesList from '../block-types-list';
10
10
  import useClipboardBlock from './hooks/use-clipboard-block';
11
- import { store as blockEditorStore } from '../../store';
12
11
  import useBlockTypeImpressions from './hooks/use-block-type-impressions';
13
- import { filterInserterItems } from './utils';
12
+ import { createInserterSection, filterInserterItems } from './utils';
13
+ import useBlockTypesState from './hooks/use-block-types-state';
14
14
 
15
- function BlockTypesTab( { onSelect, rootClientId, listProps } ) {
16
- const clipboardBlock = useClipboardBlock( rootClientId );
17
-
18
- const { blockTypes } = useSelect(
19
- ( select ) => {
20
- const { getInserterItems } = select( blockEditorStore );
21
- const blockItems = filterInserterItems(
22
- getInserterItems( rootClientId )
23
- );
15
+ const getBlockNamespace = ( item ) => item.name.split( '/' )[ 0 ];
24
16
 
25
- return {
26
- blockTypes: clipboardBlock
27
- ? [ clipboardBlock, ...blockItems ]
28
- : blockItems,
29
- };
30
- },
31
- [ rootClientId ]
17
+ function BlockTypesTab( { onSelect, rootClientId, listProps } ) {
18
+ const [ rawBlockTypes, , collections ] = useBlockTypesState(
19
+ rootClientId,
20
+ onSelect
32
21
  );
33
-
22
+ const clipboardBlock = useClipboardBlock( rootClientId );
23
+ const filteredBlockTypes = filterInserterItems( rawBlockTypes );
24
+ const blockTypes = clipboardBlock
25
+ ? [ clipboardBlock, ...filteredBlockTypes ]
26
+ : filteredBlockTypes;
34
27
  const { items, trackBlockTypeSelected } =
35
28
  useBlockTypeImpressions( blockTypes );
36
29
 
@@ -39,10 +32,38 @@ function BlockTypesTab( { onSelect, rootClientId, listProps } ) {
39
32
  onSelect( ...args );
40
33
  };
41
34
 
35
+ const collectionSections = useMemo( () => {
36
+ const result = [];
37
+ Object.keys( collections ).forEach( ( namespace ) => {
38
+ const data = items.filter(
39
+ ( item ) => getBlockNamespace( item ) === namespace
40
+ );
41
+ if ( data.length > 0 ) {
42
+ result.push(
43
+ createInserterSection( {
44
+ key: `collection-${ namespace }`,
45
+ metadata: {
46
+ icon: collections[ namespace ].icon,
47
+ title: collections[ namespace ].title,
48
+ },
49
+ items: data,
50
+ } )
51
+ );
52
+ }
53
+ } );
54
+
55
+ return result;
56
+ }, [ items, collections ] );
57
+
58
+ const sections = [
59
+ createInserterSection( { key: 'default', items } ),
60
+ ...collectionSections,
61
+ ];
62
+
42
63
  return (
43
64
  <BlockTypesList
44
65
  name="Blocks"
45
- items={ items }
66
+ sections={ sections }
46
67
  onSelect={ handleSelect }
47
68
  listProps={ listProps }
48
69
  />
@@ -5,7 +5,6 @@ import {
5
5
  isReusableBlock,
6
6
  createBlock,
7
7
  getBlockFromExample,
8
- getBlockType,
9
8
  } from '@wordpress/blocks';
10
9
  import { __ } from '@wordpress/i18n';
11
10
 
@@ -16,31 +15,24 @@ import BlockCard from '../block-card';
16
15
  import BlockPreview from '../block-preview';
17
16
 
18
17
  function InserterPreviewPanel( { item } ) {
19
- const { name, title, icon, description, initialAttributes } = item;
20
- const hoveredItemBlockType = getBlockType( name );
18
+ const { name, title, icon, description, initialAttributes, example } = item;
21
19
  const isReusable = isReusableBlock( item );
22
20
  return (
23
21
  <div className="block-editor-inserter__preview-container">
24
22
  <div className="block-editor-inserter__preview">
25
- { isReusable || hoveredItemBlockType?.example ? (
23
+ { isReusable || example ? (
26
24
  <div className="block-editor-inserter__preview-content">
27
25
  <BlockPreview
28
26
  __experimentalPadding={ 16 }
29
- viewportWidth={
30
- hoveredItemBlockType.example?.viewportWidth ??
31
- 500
32
- }
27
+ viewportWidth={ example?.viewportWidth ?? 500 }
33
28
  blocks={
34
- hoveredItemBlockType.example
29
+ example
35
30
  ? getBlockFromExample( item.name, {
36
31
  attributes: {
37
- ...hoveredItemBlockType.example
38
- .attributes,
32
+ ...example.attributes,
39
33
  ...initialAttributes,
40
34
  },
41
- innerBlocks:
42
- hoveredItemBlockType.example
43
- .innerBlocks,
35
+ innerBlocks: example.innerBlocks,
44
36
  } )
45
37
  : createBlock( name, initialAttributes )
46
38
  }
@@ -8,7 +8,7 @@ import { useSelect } from '@wordpress/data';
8
8
  */
9
9
  import BlockTypesList from '../block-types-list';
10
10
  import { store as blockEditorStore } from '../../store';
11
- import { filterInserterItems } from './utils';
11
+ import { createInserterSection, filterInserterItems } from './utils';
12
12
 
13
13
  function ReusableBlocksTab( { onSelect, rootClientId, listProps } ) {
14
14
  const { items } = useSelect(
@@ -23,10 +23,12 @@ function ReusableBlocksTab( { onSelect, rootClientId, listProps } ) {
23
23
  [ rootClientId ]
24
24
  );
25
25
 
26
+ const sections = [ createInserterSection( { key: 'reuseable', items } ) ];
27
+
26
28
  return (
27
29
  <BlockTypesList
28
30
  name="ReusableBlocks"
29
- items={ items }
31
+ sections={ sections }
30
32
  onSelect={ onSelect }
31
33
  listProps={ listProps }
32
34
  />
@@ -11,7 +11,7 @@ import BlockTypesList from '../block-types-list';
11
11
  import InserterNoResults from './no-results';
12
12
  import { store as blockEditorStore } from '../../store';
13
13
  import useBlockTypeImpressions from './hooks/use-block-type-impressions';
14
- import { filterInserterItems } from './utils';
14
+ import { createInserterSection, filterInserterItems } from './utils';
15
15
 
16
16
  function InserterSearchResults( {
17
17
  filterValue,
@@ -51,7 +51,9 @@ function InserterSearchResults( {
51
51
  <BlockTypesList
52
52
  name="Blocks"
53
53
  initialNumToRender={ isFullScreen ? 10 : 3 }
54
- { ...{ items, onSelect: handleSelect, listProps } }
54
+ sections={ [ createInserterSection( { key: 'search', items } ) ] }
55
+ onSelect={ handleSelect }
56
+ listProps={ listProps }
55
57
  />
56
58
  );
57
59
  }
@@ -18,6 +18,8 @@ jest.mock( '../hooks/use-clipboard-block' );
18
18
  jest.mock( '@wordpress/data/src/components/use-select' );
19
19
 
20
20
  const selectMock = {
21
+ getCategories: jest.fn().mockReturnValue( [] ),
22
+ getCollections: jest.fn().mockReturnValue( [] ),
21
23
  getInserterItems: jest.fn().mockReturnValue( [] ),
22
24
  canInsertBlockType: jest.fn(),
23
25
  getBlockType: jest.fn(),
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { createInserterSection } from '../utils';
5
+
6
+ describe( 'createInserterSection', () => {
7
+ it( 'returns the expected object shape', () => {
8
+ const key = 'mock-1';
9
+ const items = [ 1, 2, 3 ];
10
+ const metadata = { icon: 'icon-mock', title: 'Title Mock' };
11
+
12
+ expect( createInserterSection( { key, metadata, items } ) ).toEqual(
13
+ expect.objectContaining( {
14
+ metadata,
15
+ data: [ { key, list: items } ],
16
+ } )
17
+ );
18
+ } );
19
+
20
+ it( 'return always includes metadata', () => {
21
+ const key = 'mock-1';
22
+ const items = [ 1, 2, 3 ];
23
+
24
+ expect( createInserterSection( { key, items } ) ).toEqual(
25
+ expect.objectContaining( {
26
+ metadata: {},
27
+ data: [ { key, list: items } ],
28
+ } )
29
+ );
30
+ } );
31
+
32
+ it( 'requires a unique key', () => {
33
+ expect( () => {
34
+ createInserterSection( { items: [ 1, 2, 3 ] } );
35
+ } ).toThrow( 'A unique key for the section must be provided.' );
36
+ } );
37
+ } );
@@ -33,3 +33,14 @@ export function filterInserterItems(
33
33
  blockAllowed( block, { onlyReusable, allowReusable } )
34
34
  );
35
35
  }
36
+
37
+ export function createInserterSection( { key, metadata = {}, items } ) {
38
+ if ( ! key ) {
39
+ throw new Error( 'A unique key for the section must be provided.' );
40
+ }
41
+
42
+ return {
43
+ metadata,
44
+ data: [ { key, list: items } ],
45
+ };
46
+ }
@@ -11,6 +11,7 @@ import {
11
11
  createBlock,
12
12
  createBlocksFromInnerBlocksTemplate,
13
13
  } from '@wordpress/blocks';
14
+ import { __experimentalTruncate as Truncate } from '@wordpress/components';
14
15
  import { ENTER } from '@wordpress/keycodes';
15
16
 
16
17
  /**
@@ -135,7 +136,9 @@ function InserterListItem( {
135
136
  <BlockIcon icon={ item.icon } showColors />
136
137
  </span>
137
138
  <span className="block-editor-block-types-list__item-title">
138
- { item.title }
139
+ <Truncate numberOfLines={ 3 }>
140
+ { item.title }
141
+ </Truncate>
139
142
  </span>
140
143
  </InserterListboxItem>
141
144
  </div>
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { compact } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -95,7 +90,7 @@ function ListViewBranch( props ) {
95
90
 
96
91
  const { expandedState, draggedClientIds } = useListViewContext();
97
92
 
98
- const filteredBlocks = compact( blocks );
93
+ const filteredBlocks = blocks.filter( Boolean );
99
94
  const blockCount = filteredBlocks.length;
100
95
  let nextPosition = listPosition;
101
96
 
@@ -115,9 +115,13 @@ export function useMouseMoveTypingReset() {
115
115
  * field, presses ESC or TAB, or moves the mouse in the document.
116
116
  */
117
117
  export function useTypingObserver() {
118
- const isTyping = useSelect( ( select ) =>
119
- select( blockEditorStore ).isTyping()
120
- );
118
+ const { isTyping, hasInlineToolbar } = useSelect( ( select ) => {
119
+ const { isTyping: _isTyping, getSettings } = select( blockEditorStore );
120
+ return {
121
+ isTyping: _isTyping(),
122
+ hasInlineToolbar: getSettings().hasInlineToolbar,
123
+ };
124
+ }, [] );
121
125
  const { startTyping, stopTyping } = useDispatch( blockEditorStore );
122
126
 
123
127
  const ref1 = useMouseMoveTypingReset();
@@ -125,6 +129,7 @@ export function useTypingObserver() {
125
129
  ( node ) => {
126
130
  const { ownerDocument } = node;
127
131
  const { defaultView } = ownerDocument;
132
+ const selection = defaultView.getSelection();
128
133
 
129
134
  // Listeners to stop typing should only be added when typing.
130
135
  // Listeners to start typing should only be added when not typing.
@@ -170,22 +175,20 @@ export function useTypingObserver() {
170
175
  * uncollapsed (shift) selection.
171
176
  */
172
177
  function stopTypingOnSelectionUncollapse() {
173
- const selection = defaultView.getSelection();
174
- const isCollapsed =
175
- selection.rangeCount > 0 &&
176
- selection.getRangeAt( 0 ).collapsed;
177
-
178
- if ( ! isCollapsed ) {
178
+ if ( ! selection.isCollapsed ) {
179
179
  stopTyping();
180
180
  }
181
181
  }
182
182
 
183
183
  node.addEventListener( 'focus', stopTypingOnNonTextField );
184
184
  node.addEventListener( 'keydown', stopTypingOnEscapeKey );
185
- ownerDocument.addEventListener(
186
- 'selectionchange',
187
- stopTypingOnSelectionUncollapse
188
- );
185
+
186
+ if ( ! hasInlineToolbar ) {
187
+ ownerDocument.addEventListener(
188
+ 'selectionchange',
189
+ stopTypingOnSelectionUncollapse
190
+ );
191
+ }
189
192
 
190
193
  return () => {
191
194
  defaultView.clearTimeout( timerId );
@@ -242,7 +245,7 @@ export function useTypingObserver() {
242
245
  node.removeEventListener( 'keydown', startTypingInTextField );
243
246
  };
244
247
  },
245
- [ isTyping, startTyping, stopTyping ]
248
+ [ isTyping, hasInlineToolbar, startTyping, stopTyping ]
246
249
  );
247
250
 
248
251
  return useMergeRefs( [ ref1, ref2 ] );