@wordpress/block-editor 8.5.0 → 8.5.3

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 (123) hide show
  1. package/build/components/block-alignment-control/ui.js +1 -1
  2. package/build/components/block-alignment-control/ui.js.map +1 -1
  3. package/build/components/block-content-overlay/index.js +13 -4
  4. package/build/components/block-content-overlay/index.js.map +1 -1
  5. package/build/components/block-lock/index.js +8 -0
  6. package/build/components/block-lock/index.js.map +1 -1
  7. package/build/components/block-lock/menu-item.js +5 -20
  8. package/build/components/block-lock/menu-item.js.map +1 -1
  9. package/build/components/block-lock/modal.js +33 -12
  10. package/build/components/block-lock/modal.js.map +1 -1
  11. package/build/components/block-lock/toolbar.js +7 -20
  12. package/build/components/block-lock/toolbar.js.map +1 -1
  13. package/build/components/block-lock/use-block-lock.js +50 -0
  14. package/build/components/block-lock/use-block-lock.js.map +1 -0
  15. package/build/components/block-pattern-setup/index.js +37 -22
  16. package/build/components/block-pattern-setup/index.js.map +1 -1
  17. package/build/components/block-pattern-setup/setup-toolbar.js +1 -1
  18. package/build/components/block-pattern-setup/setup-toolbar.js.map +1 -1
  19. package/build/components/block-preview/auto.js +6 -3
  20. package/build/components/block-preview/auto.js.map +1 -1
  21. package/build/components/block-preview/index.js +4 -2
  22. package/build/components/block-preview/index.js.map +1 -1
  23. package/build/components/block-switcher/index.js +7 -2
  24. package/build/components/block-switcher/index.js.map +1 -1
  25. package/build/components/copy-handler/index.js +44 -9
  26. package/build/components/copy-handler/index.js.map +1 -1
  27. package/build/components/link-control/index.js +6 -7
  28. package/build/components/link-control/index.js.map +1 -1
  29. package/build/components/list-view/block-select-button.js +4 -10
  30. package/build/components/list-view/block-select-button.js.map +1 -1
  31. package/build/components/list-view/block.js +13 -2
  32. package/build/components/list-view/block.js.map +1 -1
  33. package/build/components/writing-flow/use-selection-observer.js +7 -1
  34. package/build/components/writing-flow/use-selection-observer.js.map +1 -1
  35. package/build/hooks/duotone.js +66 -16
  36. package/build/hooks/duotone.js.map +1 -1
  37. package/build/hooks/index.js +7 -1
  38. package/build/hooks/index.js.map +1 -1
  39. package/build/index.js +7 -0
  40. package/build/index.js.map +1 -1
  41. package/build/store/actions.js +22 -29
  42. package/build/store/actions.js.map +1 -1
  43. package/build/store/selectors.js +122 -3
  44. package/build/store/selectors.js.map +1 -1
  45. package/build/store/utils.js +27 -0
  46. package/build/store/utils.js.map +1 -0
  47. package/build-module/components/block-alignment-control/ui.js +2 -2
  48. package/build-module/components/block-alignment-control/ui.js.map +1 -1
  49. package/build-module/components/block-content-overlay/index.js +13 -4
  50. package/build-module/components/block-content-overlay/index.js.map +1 -1
  51. package/build-module/components/block-lock/index.js +1 -0
  52. package/build-module/components/block-lock/index.js.map +1 -1
  53. package/build-module/components/block-lock/menu-item.js +4 -18
  54. package/build-module/components/block-lock/menu-item.js.map +1 -1
  55. package/build-module/components/block-lock/modal.js +31 -12
  56. package/build-module/components/block-lock/modal.js.map +1 -1
  57. package/build-module/components/block-lock/toolbar.js +6 -18
  58. package/build-module/components/block-lock/toolbar.js.map +1 -1
  59. package/build-module/components/block-lock/use-block-lock.js +41 -0
  60. package/build-module/components/block-lock/use-block-lock.js.map +1 -0
  61. package/build-module/components/block-pattern-setup/index.js +39 -24
  62. package/build-module/components/block-pattern-setup/index.js.map +1 -1
  63. package/build-module/components/block-pattern-setup/setup-toolbar.js +1 -1
  64. package/build-module/components/block-pattern-setup/setup-toolbar.js.map +1 -1
  65. package/build-module/components/block-preview/auto.js +6 -3
  66. package/build-module/components/block-preview/auto.js.map +1 -1
  67. package/build-module/components/block-preview/index.js +4 -2
  68. package/build-module/components/block-preview/index.js.map +1 -1
  69. package/build-module/components/block-switcher/index.js +7 -2
  70. package/build-module/components/block-switcher/index.js.map +1 -1
  71. package/build-module/components/copy-handler/index.js +44 -9
  72. package/build-module/components/copy-handler/index.js.map +1 -1
  73. package/build-module/components/link-control/index.js +6 -7
  74. package/build-module/components/link-control/index.js.map +1 -1
  75. package/build-module/components/list-view/block-select-button.js +4 -9
  76. package/build-module/components/list-view/block-select-button.js.map +1 -1
  77. package/build-module/components/list-view/block.js +13 -2
  78. package/build-module/components/list-view/block.js.map +1 -1
  79. package/build-module/components/writing-flow/use-selection-observer.js +7 -1
  80. package/build-module/components/writing-flow/use-selection-observer.js.map +1 -1
  81. package/build-module/hooks/duotone.js +63 -16
  82. package/build-module/hooks/duotone.js.map +1 -1
  83. package/build-module/hooks/index.js +1 -0
  84. package/build-module/hooks/index.js.map +1 -1
  85. package/build-module/index.js +1 -1
  86. package/build-module/index.js.map +1 -1
  87. package/build-module/store/actions.js +5 -14
  88. package/build-module/store/actions.js.map +1 -1
  89. package/build-module/store/selectors.js +112 -2
  90. package/build-module/store/selectors.js.map +1 -1
  91. package/build-module/store/utils.js +20 -0
  92. package/build-module/store/utils.js.map +1 -0
  93. package/build-style/style-rtl.css +35 -28
  94. package/build-style/style.css +35 -28
  95. package/package.json +28 -28
  96. package/src/components/block-alignment-control/ui.js +2 -2
  97. package/src/components/block-content-overlay/index.js +19 -2
  98. package/src/components/block-content-overlay/style.scss +0 -1
  99. package/src/components/block-lock/index.js +1 -0
  100. package/src/components/block-lock/menu-item.js +3 -23
  101. package/src/components/block-lock/modal.js +37 -13
  102. package/src/components/block-lock/style.scss +1 -2
  103. package/src/components/block-lock/toolbar.js +4 -21
  104. package/src/components/block-lock/use-block-lock.js +45 -0
  105. package/src/components/block-pattern-setup/index.js +84 -59
  106. package/src/components/block-pattern-setup/setup-toolbar.js +3 -1
  107. package/src/components/block-pattern-setup/style.scss +32 -26
  108. package/src/components/block-preview/auto.js +10 -1
  109. package/src/components/block-preview/index.js +2 -0
  110. package/src/components/block-switcher/index.js +13 -1
  111. package/src/components/block-switcher/style.scss +7 -3
  112. package/src/components/block-switcher/test/__snapshots__/index.js.snap +15 -13
  113. package/src/components/copy-handler/index.js +52 -10
  114. package/src/components/link-control/index.js +5 -5
  115. package/src/components/list-view/block-select-button.js +2 -10
  116. package/src/components/list-view/block.js +16 -7
  117. package/src/components/writing-flow/use-selection-observer.js +7 -0
  118. package/src/hooks/duotone.js +98 -62
  119. package/src/hooks/index.js +1 -0
  120. package/src/index.js +1 -0
  121. package/src/store/actions.js +5 -13
  122. package/src/store/selectors.js +148 -2
  123. package/src/store/utils.js +19 -0
@@ -5,45 +5,28 @@ import { __, sprintf } from '@wordpress/i18n';
5
5
  import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
6
6
  import { useReducer } from '@wordpress/element';
7
7
  import { lock } from '@wordpress/icons';
8
- import { useSelect } from '@wordpress/data';
9
8
 
10
9
  /**
11
10
  * Internal dependencies
12
11
  */
13
12
  import BlockLockModal from './modal';
13
+ import useBlockLock from './use-block-lock';
14
14
  import useBlockDisplayInformation from '../use-block-display-information';
15
- import { store as blockEditorStore } from '../../store';
16
15
 
17
16
  export default function BlockLockToolbar( { clientId } ) {
18
17
  const blockInformation = useBlockDisplayInformation( clientId );
19
- const { canMove, canRemove, canLockBlock } = useSelect(
20
- ( select ) => {
21
- const {
22
- canMoveBlock,
23
- canRemoveBlock,
24
- canLockBlockType,
25
- getBlockName,
26
- } = select( blockEditorStore );
27
-
28
- return {
29
- canMove: canMoveBlock( clientId ),
30
- canRemove: canRemoveBlock( clientId ),
31
- canLockBlock: canLockBlockType( getBlockName( clientId ) ),
32
- };
33
- },
34
- [ clientId ]
35
- );
18
+ const { canEdit, canMove, canRemove, canLock } = useBlockLock( clientId );
36
19
 
37
20
  const [ isModalOpen, toggleModal ] = useReducer(
38
21
  ( isActive ) => ! isActive,
39
22
  false
40
23
  );
41
24
 
42
- if ( ! canLockBlock ) {
25
+ if ( ! canLock ) {
43
26
  return null;
44
27
  }
45
28
 
46
- if ( canMove && canRemove ) {
29
+ if ( canEdit && canMove && canRemove ) {
47
30
  return null;
48
31
  }
49
32
 
@@ -0,0 +1,45 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { store as blockEditorStore } from '../../store';
10
+
11
+ /**
12
+ * Return details about the block lock status.
13
+ *
14
+ * @param {string} clientId The block client Id.
15
+ *
16
+ * @return {Object} Block lock status
17
+ */
18
+ export default function useBlockLock( clientId ) {
19
+ return useSelect(
20
+ ( select ) => {
21
+ const {
22
+ canEditBlock,
23
+ canMoveBlock,
24
+ canRemoveBlock,
25
+ canLockBlockType,
26
+ getBlockName,
27
+ getBlockRootClientId,
28
+ } = select( blockEditorStore );
29
+ const rootClientId = getBlockRootClientId( clientId );
30
+
31
+ const canEdit = canEditBlock( clientId );
32
+ const canMove = canMoveBlock( clientId, rootClientId );
33
+ const canRemove = canRemoveBlock( clientId, rootClientId );
34
+
35
+ return {
36
+ canEdit,
37
+ canMove,
38
+ canRemove,
39
+ canLock: canLockBlockType( getBlockName( clientId ) ),
40
+ isLocked: ! canEdit || ! canMove || ! canRemove,
41
+ };
42
+ },
43
+ [ clientId ]
44
+ );
45
+ }
@@ -11,7 +11,7 @@ import {
11
11
  } from '@wordpress/components';
12
12
 
13
13
  import { useState } from '@wordpress/element';
14
- import { useInstanceId } from '@wordpress/compose';
14
+ import { useInstanceId, useResizeObserver } from '@wordpress/compose';
15
15
  import { __ } from '@wordpress/i18n';
16
16
 
17
17
  /**
@@ -28,6 +28,7 @@ const SetupContent = ( {
28
28
  activeSlide,
29
29
  patterns,
30
30
  onBlockPatternSelect,
31
+ height,
31
32
  } ) => {
32
33
  const composite = useCompositeState();
33
34
  const containerClass = 'block-editor-block-pattern-setup__container';
@@ -38,41 +39,52 @@ const SetupContent = ( {
38
39
  [ activeSlide + 1, 'next-slide' ],
39
40
  ] );
40
41
  return (
41
- <div className={ containerClass }>
42
- <ul className="carousel-container">
43
- { patterns.map( ( pattern, index ) => (
44
- <BlockPatternSlide
45
- className={ slideClass.get( index ) || '' }
46
- key={ pattern.name }
47
- pattern={ pattern }
48
- />
49
- ) ) }
50
- </ul>
42
+ <div
43
+ className="block-editor-block-pattern-setup__carousel"
44
+ style={ { height } }
45
+ >
46
+ <div className={ containerClass }>
47
+ <ul className="carousel-container">
48
+ { patterns.map( ( pattern, index ) => (
49
+ <BlockPatternSlide
50
+ className={ slideClass.get( index ) || '' }
51
+ key={ pattern.name }
52
+ pattern={ pattern }
53
+ minHeight={ height }
54
+ />
55
+ ) ) }
56
+ </ul>
57
+ </div>
51
58
  </div>
52
59
  );
53
60
  }
54
61
  return (
55
- <Composite
56
- { ...composite }
57
- role="listbox"
58
- className={ containerClass }
59
- aria-label={ __( 'Patterns list' ) }
62
+ <div
63
+ style={ { height } }
64
+ className="block-editor-block-pattern-setup__grid"
60
65
  >
61
- { patterns.map( ( pattern ) => (
62
- <BlockPattern
63
- key={ pattern.name }
64
- pattern={ pattern }
65
- onSelect={ onBlockPatternSelect }
66
- composite={ composite }
67
- />
68
- ) ) }
69
- </Composite>
66
+ <Composite
67
+ { ...composite }
68
+ role="listbox"
69
+ className={ containerClass }
70
+ aria-label={ __( 'Patterns list' ) }
71
+ >
72
+ { patterns.map( ( pattern ) => (
73
+ <BlockPattern
74
+ key={ pattern.name }
75
+ pattern={ pattern }
76
+ onSelect={ onBlockPatternSelect }
77
+ composite={ composite }
78
+ />
79
+ ) ) }
80
+ </Composite>
81
+ </div>
70
82
  );
71
83
  };
72
84
 
73
85
  function BlockPattern( { pattern, onSelect, composite } ) {
74
86
  const baseClassName = 'block-editor-block-pattern-setup-list';
75
- const { blocks, title, description, viewportWidth = 700 } = pattern;
87
+ const { blocks, description, viewportWidth = 700 } = pattern;
76
88
  const descriptionId = useInstanceId(
77
89
  BlockPattern,
78
90
  `${ baseClassName }__item-description`
@@ -94,9 +106,6 @@ function BlockPattern( { pattern, onSelect, composite } ) {
94
106
  blocks={ blocks }
95
107
  viewportWidth={ viewportWidth }
96
108
  />
97
- <div className={ `${ baseClassName }__item-title` }>
98
- { title }
99
- </div>
100
109
  </CompositeItem>
101
110
  { !! description && (
102
111
  <VisuallyHidden id={ descriptionId }>
@@ -107,7 +116,7 @@ function BlockPattern( { pattern, onSelect, composite } ) {
107
116
  );
108
117
  }
109
118
 
110
- function BlockPatternSlide( { className, pattern } ) {
119
+ function BlockPatternSlide( { className, pattern, minHeight } ) {
111
120
  const { blocks, title, description } = pattern;
112
121
  const descriptionId = useInstanceId(
113
122
  BlockPatternSlide,
@@ -119,7 +128,10 @@ function BlockPatternSlide( { className, pattern } ) {
119
128
  aria-label={ title }
120
129
  aria-describedby={ description ? descriptionId : undefined }
121
130
  >
122
- <BlockPreview blocks={ blocks } __experimentalLive />
131
+ <BlockPreview
132
+ blocks={ blocks }
133
+ __experimentalMinHeight={ minHeight }
134
+ />
123
135
  { !! description && (
124
136
  <VisuallyHidden id={ descriptionId }>
125
137
  { description }
@@ -141,6 +153,10 @@ const BlockPatternSetup = ( {
141
153
  const [ showBlank, setShowBlank ] = useState( false );
142
154
  const { replaceBlock } = useDispatch( blockEditorStore );
143
155
  const patterns = usePatternsSetup( clientId, blockName, filterPatternsFn );
156
+ const [
157
+ contentResizeListener,
158
+ { height: contentHeight },
159
+ ] = useResizeObserver();
144
160
 
145
161
  if ( ! patterns?.length || showBlank ) {
146
162
  return startBlankComponent;
@@ -152,35 +168,44 @@ const BlockPatternSetup = ( {
152
168
  };
153
169
  const onPatternSelectCallback =
154
170
  onBlockPatternSelect || onBlockPatternSelectDefault;
171
+ const onStartBlank = startBlankComponent
172
+ ? () => {
173
+ setShowBlank( true );
174
+ }
175
+ : undefined;
155
176
  return (
156
- <div
157
- className={ `block-editor-block-pattern-setup view-mode-${ viewMode }` }
158
- >
159
- <SetupToolbar
160
- viewMode={ viewMode }
161
- setViewMode={ setViewMode }
162
- activeSlide={ activeSlide }
163
- totalSlides={ patterns.length }
164
- handleNext={ () => {
165
- setActiveSlide( ( active ) => active + 1 );
166
- } }
167
- handlePrevious={ () => {
168
- setActiveSlide( ( active ) => active - 1 );
169
- } }
170
- onBlockPatternSelect={ () => {
171
- onPatternSelectCallback( patterns[ activeSlide ].blocks );
172
- } }
173
- onStartBlank={ () => {
174
- setShowBlank( true );
175
- } }
176
- />
177
- <SetupContent
178
- viewMode={ viewMode }
179
- activeSlide={ activeSlide }
180
- patterns={ patterns }
181
- onBlockPatternSelect={ onPatternSelectCallback }
182
- />
183
- </div>
177
+ <>
178
+ { contentResizeListener }
179
+ <div
180
+ className={ `block-editor-block-pattern-setup view-mode-${ viewMode }` }
181
+ >
182
+ <SetupContent
183
+ viewMode={ viewMode }
184
+ activeSlide={ activeSlide }
185
+ patterns={ patterns }
186
+ onBlockPatternSelect={ onPatternSelectCallback }
187
+ height={ contentHeight - 2 * 60 }
188
+ />
189
+ <SetupToolbar
190
+ viewMode={ viewMode }
191
+ setViewMode={ setViewMode }
192
+ activeSlide={ activeSlide }
193
+ totalSlides={ patterns.length }
194
+ handleNext={ () => {
195
+ setActiveSlide( ( active ) => active + 1 );
196
+ } }
197
+ handlePrevious={ () => {
198
+ setActiveSlide( ( active ) => active - 1 );
199
+ } }
200
+ onBlockPatternSelect={ () => {
201
+ onPatternSelectCallback(
202
+ patterns[ activeSlide ].blocks
203
+ );
204
+ } }
205
+ onStartBlank={ onStartBlank }
206
+ />
207
+ </div>
208
+ </>
184
209
  );
185
210
  };
186
211
 
@@ -17,7 +17,9 @@ import { VIEWMODES } from './constants';
17
17
 
18
18
  const Actions = ( { onStartBlank, onBlockPatternSelect } ) => (
19
19
  <div className="block-editor-block-pattern-setup__actions">
20
- <Button onClick={ onStartBlank }>{ __( 'Start blank' ) }</Button>
20
+ { onStartBlank && (
21
+ <Button onClick={ onStartBlank }>{ __( 'Start blank' ) }</Button>
22
+ ) }
21
23
  <Button variant="primary" onClick={ onBlockPatternSelect }>
22
24
  { __( 'Choose' ) }
23
25
  </Button>
@@ -5,8 +5,6 @@
5
5
  align-items: flex-start;
6
6
  width: 100%;
7
7
  border-radius: $radius-block-ui;
8
- box-shadow: inset 0 0 0 $border-width $gray-900;
9
- outline: 1px solid transparent; // Shown for Windows 10 High Contrast mode.
10
8
 
11
9
  // TODO change to check parent.
12
10
  &.view-mode-grid {
@@ -15,37 +13,41 @@
15
13
  }
16
14
 
17
15
  .block-editor-block-pattern-setup__container {
18
- display: grid;
19
- grid-template-columns: 1fr 1fr;
20
- grid-gap: $grid-unit-20;
21
- padding: $grid-unit-20;
22
- max-height: 550px;
23
- overflow: auto;
24
- margin: 0 $border-width $border-width $border-width;
25
- width: calc(100% - #{ $border-width * 2 });
26
- background: $white;
16
+ column-gap: $grid-unit-30;
17
+ display: block;
18
+ width: 100%;
19
+ padding: $grid-unit-40;
20
+ column-count: 2;
21
+
22
+ @include break-huge() {
23
+ column-count: 3;
24
+ }
27
25
 
28
26
  .block-editor-block-preview__container,
29
27
  div[role="button"] {
30
28
  cursor: pointer;
31
29
  }
32
30
 
33
- .block-editor-block-pattern-setup-list__item-title {
34
- padding: $grid-unit-05;
35
- font-size: $helptext-font-size;
36
- text-align: center;
37
- }
31
+ .block-editor-block-pattern-setup-list__list-item {
32
+ break-inside: avoid-column;
33
+ margin-bottom: $grid-unit-30;
34
+
35
+ .block-editor-block-preview__container {
36
+ min-height: 100px;
37
+ border-radius: $radius-block-ui;
38
+ border: $border-width solid $gray-300;
39
+ }
38
40
 
39
- .block-editor-block-preview__container {
40
- border-radius: $radius-block-ui;
41
- border: $border-width solid $gray-300;
41
+ .block-editor-block-preview__content {
42
+ width: 100%;
43
+ }
42
44
  }
43
45
  }
44
46
  }
45
47
 
46
48
  .block-editor-block-pattern-setup__toolbar {
49
+ height: $header-height;
47
50
  box-sizing: border-box;
48
- position: relative;
49
51
  padding: $grid-unit-20;
50
52
  width: 100%;
51
53
  text-align: left;
@@ -54,13 +56,12 @@
54
56
  // Block UI appearance.
55
57
  border-radius: $radius-block-ui $radius-block-ui 0 0;
56
58
  background-color: $white;
57
- box-shadow: inset 0 0 0 $border-width $gray-900;
58
- outline: 1px solid transparent; // Shown for Windows 10 High Contrast mode.
59
-
60
59
  display: flex;
61
60
  flex-direction: row;
62
61
  align-items: center;
63
62
  justify-content: space-between;
63
+ border-top: 1px solid $gray-300;
64
+ align-self: flex-end;
64
65
 
65
66
  .block-editor-block-pattern-setup__display-controls {
66
67
  display: flex;
@@ -93,13 +94,12 @@
93
94
  box-sizing: border-box;
94
95
  }
95
96
  .pattern-slide {
96
- opacity: 0;
97
97
  position: absolute;
98
98
  top: 0;
99
99
  width: 100%;
100
100
  margin: auto;
101
- padding: $grid-unit-20;
102
- transition: transform 0.5s, opacity 0.5s, z-index 0.5s;
101
+ padding: 0;
102
+ transition: transform 0.5s, z-index 0.5s;
103
103
  z-index: z-index(".block-editor-block-pattern-setup .pattern-slide");
104
104
 
105
105
  &.active-slide {
@@ -125,3 +125,9 @@
125
125
  }
126
126
  }
127
127
  }
128
+
129
+ .block-editor-block-pattern-setup__carousel,
130
+ .block-editor-block-pattern-setup__grid {
131
+ width: 100%;
132
+ overflow-y: auto;
133
+ }
@@ -19,7 +19,11 @@ let MemoizedBlockList;
19
19
 
20
20
  const MAX_HEIGHT = 2000;
21
21
 
22
- function AutoBlockPreview( { viewportWidth, __experimentalPadding } ) {
22
+ function AutoBlockPreview( {
23
+ viewportWidth,
24
+ __experimentalPadding,
25
+ __experimentalMinHeight,
26
+ } ) {
23
27
  const [
24
28
  containerResizeListener,
25
29
  { width: containerWidth },
@@ -68,6 +72,7 @@ function AutoBlockPreview( { viewportWidth, __experimentalPadding } ) {
68
72
  contentHeight > MAX_HEIGHT
69
73
  ? MAX_HEIGHT * scale
70
74
  : undefined,
75
+ minHeight: __experimentalMinHeight,
71
76
  } }
72
77
  >
73
78
  <Iframe
@@ -98,6 +103,10 @@ function AutoBlockPreview( { viewportWidth, __experimentalPadding } ) {
98
103
  // This is a catch-all max-height for patterns.
99
104
  // See: https://github.com/WordPress/gutenberg/pull/38175.
100
105
  maxHeight: MAX_HEIGHT,
106
+ minHeight:
107
+ scale < 1 && __experimentalMinHeight
108
+ ? __experimentalMinHeight / scale
109
+ : __experimentalMinHeight,
101
110
  } }
102
111
  >
103
112
  { contentResizeListener }
@@ -29,6 +29,7 @@ export function BlockPreview( {
29
29
  viewportWidth = 1200,
30
30
  __experimentalLive = false,
31
31
  __experimentalOnClick,
32
+ __experimentalMinHeight,
32
33
  } ) {
33
34
  const originalSettings = useSelect(
34
35
  ( select ) => select( blockEditorStore ).getSettings(),
@@ -51,6 +52,7 @@ export function BlockPreview( {
51
52
  <AutoHeightBlockPreview
52
53
  viewportWidth={ viewportWidth }
53
54
  __experimentalPadding={ __experimentalPadding }
55
+ __experimentalMinHeight={ __experimentalMinHeight }
54
56
  />
55
57
  ) }
56
58
  </BlockEditorProvider>
@@ -108,7 +108,19 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
108
108
  disabled
109
109
  className="block-editor-block-switcher__no-switcher-icon"
110
110
  title={ blockTitle }
111
- icon={ <BlockIcon icon={ icon } showColors /> }
111
+ icon={
112
+ <>
113
+ <BlockIcon icon={ icon } showColors />
114
+ { ( isReusable || isTemplate ) && (
115
+ <span className="block-editor-block-switcher__toggle-text">
116
+ <BlockTitle
117
+ clientId={ clientIds }
118
+ maximumLength={ 35 }
119
+ />
120
+ </span>
121
+ ) }
122
+ </>
123
+ }
112
124
  />
113
125
  </ToolbarGroup>
114
126
  );
@@ -46,11 +46,15 @@
46
46
  }
47
47
 
48
48
  .components-button.block-editor-block-switcher__no-switcher-icon {
49
- width: $block-toolbar-height;
49
+ display: flex;
50
+ // The `!important` is used to vastly simplify the overriding of an inherited selector.
51
+ // Can be removed if we refactor .block-editor-block-toolbar .components-toolbar-group .components-button.has-icon.has-icon
52
+ padding: ($grid-unit-15 * 0.5) $grid-unit-15 !important;
50
53
 
51
- .block-editor-blocks-icon {
54
+ .block-editor-block-icon {
52
55
  margin-right: auto;
53
56
  margin-left: auto;
57
+ min-width: $icon-size !important;
54
58
  }
55
59
  }
56
60
 
@@ -172,7 +176,7 @@
172
176
  // The block switcher in the contextual toolbar should be bigger.
173
177
  .block-editor-block-contextual-toolbar {
174
178
  .components-button.block-editor-block-switcher__no-switcher-icon {
175
- width: $grid-unit-60;
179
+ min-width: $button-size;
176
180
  }
177
181
 
178
182
  .components-button.block-editor-block-switcher__no-switcher-icon,
@@ -6,19 +6,21 @@ exports[`BlockSwitcherDropdownMenu should render disabled block switcher with mu
6
6
  className="block-editor-block-switcher__no-switcher-icon"
7
7
  disabled={true}
8
8
  icon={
9
- <Memo(BlockIcon)
10
- icon={
11
- <SVG
12
- viewBox="0 0 24 24"
13
- xmlns="http://www.w3.org/2000/svg"
14
- >
15
- <Path
16
- d="M20.2 8v11c0 .7-.6 1.2-1.2 1.2H6v1.5h13c1.5 0 2.7-1.2 2.7-2.8V8zM18 16.4V4.6c0-.9-.7-1.6-1.6-1.6H4.6C3.7 3 3 3.7 3 4.6v11.8c0 .9.7 1.6 1.6 1.6h11.8c.9 0 1.6-.7 1.6-1.6zm-13.5 0V4.6c0-.1.1-.1.1-.1h11.8c.1 0 .1.1.1.1v11.8c0 .1-.1.1-.1.1H4.6l-.1-.1z"
17
- />
18
- </SVG>
19
- }
20
- showColors={true}
21
- />
9
+ <React.Fragment>
10
+ <Memo(BlockIcon)
11
+ icon={
12
+ <SVG
13
+ viewBox="0 0 24 24"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ >
16
+ <Path
17
+ d="M20.2 8v11c0 .7-.6 1.2-1.2 1.2H6v1.5h13c1.5 0 2.7-1.2 2.7-2.8V8zM18 16.4V4.6c0-.9-.7-1.6-1.6-1.6H4.6C3.7 3 3 3.7 3 4.6v11.8c0 .9.7 1.6 1.6 1.6h11.8c.9 0 1.6-.7 1.6-1.6zm-13.5 0V4.6c0-.1.1-.1.1-.1h11.8c.1 0 .1.1.1.1v11.8c0 .1-.1.1-.1.1H4.6l-.1-.1z"
18
+ />
19
+ </SVG>
20
+ }
21
+ showColors={true}
22
+ />
23
+ </React.Fragment>
22
24
  }
23
25
  />
24
26
  </ToolbarGroup>
@@ -78,10 +78,18 @@ export function useClipboardHandler() {
78
78
  getSelectedBlockClientIds,
79
79
  hasMultiSelection,
80
80
  getSettings,
81
+ __unstableIsFullySelected,
82
+ __unstableIsSelectionCollapsed,
83
+ __unstableIsSelectionMergeable,
84
+ __unstableGetSelectedBlocksWithPartialSelection,
81
85
  } = useSelect( blockEditorStore );
82
- const { flashBlock, removeBlocks, replaceBlocks } = useDispatch(
83
- blockEditorStore
84
- );
86
+ const {
87
+ flashBlock,
88
+ removeBlocks,
89
+ replaceBlocks,
90
+ __unstableDeleteSelection,
91
+ __unstableExpandSelection,
92
+ } = useDispatch( blockEditorStore );
85
93
  const notifyCopy = useNotifyCopy();
86
94
 
87
95
  return useRefEffect( ( node ) => {
@@ -116,20 +124,54 @@ export function useClipboardHandler() {
116
124
  const eventDefaultPrevented = event.defaultPrevented;
117
125
  event.preventDefault();
118
126
 
127
+ const isSelectionMergeable = __unstableIsSelectionMergeable();
128
+ const shouldHandleWholeBlocks =
129
+ __unstableIsSelectionCollapsed() || __unstableIsFullySelected();
130
+ const expandSelectionIsNeeded =
131
+ ! shouldHandleWholeBlocks && ! isSelectionMergeable;
119
132
  if ( event.type === 'copy' || event.type === 'cut' ) {
120
133
  if ( selectedBlockClientIds.length === 1 ) {
121
134
  flashBlock( selectedBlockClientIds[ 0 ] );
122
135
  }
123
- notifyCopy( event.type, selectedBlockClientIds );
124
- const blocks = getBlocksByClientId( selectedBlockClientIds );
125
- const serialized = serialize( blocks );
126
-
127
- event.clipboardData.setData( 'text/plain', serialized );
128
- event.clipboardData.setData( 'text/html', serialized );
136
+ // If we have a partial selection that is not mergeable, just
137
+ // expand the selection to the whole blocks.
138
+ if ( expandSelectionIsNeeded ) {
139
+ __unstableExpandSelection();
140
+ } else {
141
+ notifyCopy( event.type, selectedBlockClientIds );
142
+ let blocks;
143
+ // Check if we have partial selection.
144
+ if ( shouldHandleWholeBlocks ) {
145
+ blocks = getBlocksByClientId( selectedBlockClientIds );
146
+ } else {
147
+ const [
148
+ head,
149
+ tail,
150
+ ] = __unstableGetSelectedBlocksWithPartialSelection();
151
+ const inBetweenBlocks = getBlocksByClientId(
152
+ selectedBlockClientIds.slice(
153
+ 1,
154
+ selectedBlockClientIds.length - 1
155
+ )
156
+ );
157
+ blocks = [ head, ...inBetweenBlocks, tail ];
158
+ }
159
+ const serialized = serialize( blocks );
160
+
161
+ event.clipboardData.setData( 'text/plain', serialized );
162
+ event.clipboardData.setData( 'text/html', serialized );
163
+ }
129
164
  }
130
165
 
131
166
  if ( event.type === 'cut' ) {
132
- removeBlocks( selectedBlockClientIds );
167
+ // We need to also check if at the start we needed to
168
+ // expand the selection, as in this point we might have
169
+ // programmatically fully selected the blocks above.
170
+ if ( shouldHandleWholeBlocks && ! expandSelectionIsNeeded ) {
171
+ removeBlocks( selectedBlockClientIds );
172
+ } else {
173
+ __unstableDeleteSelection();
174
+ }
133
175
  } else if ( event.type === 'paste' ) {
134
176
  if ( eventDefaultPrevented ) {
135
177
  // This was likely already handled in rich-text/use-paste-handler.js.
@@ -148,6 +148,10 @@ function LinkControl( {
148
148
 
149
149
  const currentInputIsEmpty = ! currentInputValue?.trim()?.length;
150
150
 
151
+ const { createPage, isCreatingPage, errorMessage } = useCreatePage(
152
+ createSuggestion
153
+ );
154
+
151
155
  useEffect( () => {
152
156
  if (
153
157
  forceIsEditingLink !== undefined &&
@@ -185,7 +189,7 @@ function LinkControl( {
185
189
  nextFocusTarget.focus();
186
190
 
187
191
  isEndingEditWithFocus.current = false;
188
- }, [ isEditingLink ] );
192
+ }, [ isEditingLink, isCreatingPage ] );
189
193
 
190
194
  useEffect( () => {
191
195
  /**
@@ -217,10 +221,6 @@ function LinkControl( {
217
221
  setIsEditingLink( false );
218
222
  }
219
223
 
220
- const { createPage, isCreatingPage, errorMessage } = useCreatePage(
221
- createSuggestion
222
- );
223
-
224
224
  const handleSelectSuggestion = ( updatedValue ) => {
225
225
  onChange( {
226
226
  ...updatedValue,