@wordpress/block-editor 14.21.1-next.719a03cbe.0 → 15.0.1-next.46f643fa0.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 (124) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/block-list/use-block-props/index.js +1 -3
  3. package/build/components/block-list/use-block-props/index.js.map +1 -1
  4. package/build/components/block-list/use-block-props/use-is-hovered.js +8 -25
  5. package/build/components/block-list/use-block-props/use-is-hovered.js.map +1 -1
  6. package/build/components/block-list/zoom-out-separator.js +0 -1
  7. package/build/components/block-list/zoom-out-separator.js.map +1 -1
  8. package/build/components/block-settings-menu/block-settings-dropdown.js +7 -4
  9. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  10. package/build/components/block-toolbar/index.js +6 -3
  11. package/build/components/block-toolbar/index.js.map +1 -1
  12. package/build/components/child-layout-control/index.js +53 -34
  13. package/build/components/child-layout-control/index.js.map +1 -1
  14. package/build/components/iframe/index.js +20 -15
  15. package/build/components/iframe/index.js.map +1 -1
  16. package/build/components/image-editor/use-save-image.js +49 -30
  17. package/build/components/image-editor/use-save-image.js.map +1 -1
  18. package/build/components/inserter-button/index.native.js +54 -64
  19. package/build/components/inserter-button/index.native.js.map +1 -1
  20. package/build/components/link-control/search-input.js +4 -2
  21. package/build/components/link-control/search-input.js.map +1 -1
  22. package/build/components/list-view/block.js +7 -5
  23. package/build/components/list-view/block.js.map +1 -1
  24. package/build/components/list-view/use-list-view-images.js +62 -30
  25. package/build/components/list-view/use-list-view-images.js.map +1 -1
  26. package/build/components/publish-date-time-picker/index.js +2 -1
  27. package/build/components/publish-date-time-picker/index.js.map +1 -1
  28. package/build/components/writing-flow/use-click-selection.js +3 -1
  29. package/build/components/writing-flow/use-click-selection.js.map +1 -1
  30. package/build/layouts/grid.js +2 -0
  31. package/build/layouts/grid.js.map +1 -1
  32. package/build/private-apis.js +2 -1
  33. package/build/private-apis.js.map +1 -1
  34. package/build/store/actions.js +12 -7
  35. package/build/store/actions.js.map +1 -1
  36. package/build/store/private-keys.js +2 -1
  37. package/build/store/private-keys.js.map +1 -1
  38. package/build/store/reducer.js +0 -18
  39. package/build/store/reducer.js.map +1 -1
  40. package/build/store/selectors.js +13 -9
  41. package/build/store/selectors.js.map +1 -1
  42. package/build/utils/block-bindings.js +2 -1
  43. package/build/utils/block-bindings.js.map +1 -1
  44. package/build-module/components/block-list/use-block-props/index.js +1 -3
  45. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  46. package/build-module/components/block-list/use-block-props/use-is-hovered.js +8 -26
  47. package/build-module/components/block-list/use-block-props/use-is-hovered.js.map +1 -1
  48. package/build-module/components/block-list/zoom-out-separator.js +0 -1
  49. package/build-module/components/block-list/zoom-out-separator.js.map +1 -1
  50. package/build-module/components/block-settings-menu/block-settings-dropdown.js +7 -4
  51. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  52. package/build-module/components/block-toolbar/index.js +6 -3
  53. package/build-module/components/block-toolbar/index.js.map +1 -1
  54. package/build-module/components/child-layout-control/index.js +54 -35
  55. package/build-module/components/child-layout-control/index.js.map +1 -1
  56. package/build-module/components/iframe/index.js +20 -15
  57. package/build-module/components/iframe/index.js.map +1 -1
  58. package/build-module/components/image-editor/use-save-image.js +50 -30
  59. package/build-module/components/image-editor/use-save-image.js.map +1 -1
  60. package/build-module/components/inserter-button/index.native.js +55 -65
  61. package/build-module/components/inserter-button/index.native.js.map +1 -1
  62. package/build-module/components/link-control/search-input.js +4 -2
  63. package/build-module/components/link-control/search-input.js.map +1 -1
  64. package/build-module/components/list-view/block.js +8 -6
  65. package/build-module/components/list-view/block.js.map +1 -1
  66. package/build-module/components/list-view/use-list-view-images.js +62 -30
  67. package/build-module/components/list-view/use-list-view-images.js.map +1 -1
  68. package/build-module/components/publish-date-time-picker/index.js +2 -1
  69. package/build-module/components/publish-date-time-picker/index.js.map +1 -1
  70. package/build-module/components/writing-flow/use-click-selection.js +3 -1
  71. package/build-module/components/writing-flow/use-click-selection.js.map +1 -1
  72. package/build-module/layouts/grid.js +2 -0
  73. package/build-module/layouts/grid.js.map +1 -1
  74. package/build-module/private-apis.js +3 -2
  75. package/build-module/private-apis.js.map +1 -1
  76. package/build-module/store/actions.js +12 -7
  77. package/build-module/store/actions.js.map +1 -1
  78. package/build-module/store/private-keys.js +1 -0
  79. package/build-module/store/private-keys.js.map +1 -1
  80. package/build-module/store/reducer.js +0 -17
  81. package/build-module/store/reducer.js.map +1 -1
  82. package/build-module/store/selectors.js +12 -9
  83. package/build-module/store/selectors.js.map +1 -1
  84. package/build-module/utils/block-bindings.js +2 -1
  85. package/build-module/utils/block-bindings.js.map +1 -1
  86. package/build-style/style-rtl.css +50 -26
  87. package/build-style/style.css +50 -26
  88. package/package.json +34 -34
  89. package/src/components/block-list/use-block-props/index.js +1 -1
  90. package/src/components/block-list/use-block-props/use-is-hovered.js +12 -26
  91. package/src/components/block-list/zoom-out-separator.js +0 -1
  92. package/src/components/block-lock/style.scss +10 -0
  93. package/src/components/block-settings-menu/block-settings-dropdown.js +4 -1
  94. package/src/components/block-toolbar/index.js +8 -2
  95. package/src/components/block-variation-transforms/style.scss +7 -0
  96. package/src/components/border-radius-control/style.scss +5 -0
  97. package/src/components/child-layout-control/index.js +66 -42
  98. package/src/components/colors-gradients/style.scss +5 -0
  99. package/src/components/grid/style.scss +3 -2
  100. package/src/components/iframe/index.js +25 -16
  101. package/src/components/image-editor/use-save-image.js +58 -31
  102. package/src/components/inserter/style.scss +1 -1
  103. package/src/components/inserter-button/index.native.js +69 -80
  104. package/src/components/inserter-list-item/style.scss +9 -6
  105. package/src/components/link-control/search-input.js +9 -2
  106. package/src/components/link-control/style.scss +7 -0
  107. package/src/components/link-control/test/index.js +44 -44
  108. package/src/components/list-view/block.js +10 -5
  109. package/src/components/list-view/style.scss +2 -20
  110. package/src/components/list-view/use-list-view-images.js +57 -36
  111. package/src/components/media-replace-flow/test/index.js +1 -1
  112. package/src/components/publish-date-time-picker/README.md +9 -0
  113. package/src/components/publish-date-time-picker/index.js +2 -1
  114. package/src/components/spacing-sizes-control/style.scss +5 -0
  115. package/src/components/writing-flow/use-click-selection.js +3 -1
  116. package/src/hooks/layout.scss +8 -0
  117. package/src/layouts/grid.js +2 -2
  118. package/src/private-apis.js +2 -0
  119. package/src/store/actions.js +15 -7
  120. package/src/store/private-keys.js +1 -0
  121. package/src/store/reducer.js +0 -18
  122. package/src/store/selectors.js +29 -31
  123. package/src/store/test/actions.js +15 -2
  124. package/src/utils/block-bindings.js +1 -0
@@ -91,6 +91,7 @@ export function PrivateBlockToolbar( {
91
91
  getParentSectionBlock,
92
92
  isZoomOut,
93
93
  isNavigationMode: _isNavigationMode,
94
+ isSectionBlock,
94
95
  } = unlock( select( blockEditorStore ) );
95
96
  const selectedBlockClientIds = getSelectedBlockClientIds();
96
97
  const selectedBlockClientId = selectedBlockClientIds[ 0 ];
@@ -100,6 +101,7 @@ export function PrivateBlockToolbar( {
100
101
  const parentBlockName = getBlockName( parentClientId );
101
102
  const parentBlockType = getBlockType( parentBlockName );
102
103
  const editingMode = getBlockEditingMode( selectedBlockClientId );
104
+ const isNavigationModeEnabled = _isNavigationMode();
103
105
  const _isDefaultEditingMode = editingMode === 'default';
104
106
  const _blockName = getBlockName( selectedBlockClientId );
105
107
  const isValid = selectedBlockClientIds.every( ( id ) =>
@@ -151,9 +153,13 @@ export function PrivateBlockToolbar( {
151
153
  showSlots: ! _isZoomOut,
152
154
  showGroupButtons: ! _isZoomOut,
153
155
  showLockButtons: ! _isZoomOut,
154
- showSwitchSectionStyleButton: _isZoomOut,
156
+ showSwitchSectionStyleButton:
157
+ _isZoomOut ||
158
+ ( isNavigationModeEnabled &&
159
+ editingMode === 'contentOnly' &&
160
+ isSectionBlock( selectedBlockClientId ) ), // Zoom out or Write Mode Section Blocks
155
161
  hasFixedToolbar: getSettings().hasFixedToolbar,
156
- isNavigationMode: _isNavigationMode(),
162
+ isNavigationMode: isNavigationModeEnabled,
157
163
  };
158
164
  }, [] );
159
165
 
@@ -1,3 +1,10 @@
1
+ .block-editor-block-variation-transforms:where(fieldset) {
2
+ // Reset `fieldset` browser defaults.
3
+ border: 0;
4
+ padding: 0;
5
+ margin: 0;
6
+ }
7
+
1
8
  .block-editor-block-variation-transforms {
2
9
  padding: 0 $grid-unit-20 $grid-unit-20 52px;
3
10
  width: 100%;
@@ -1,4 +1,9 @@
1
1
  .components-border-radius-control {
2
+ // Reset `fieldset` browser defaults.
3
+ border: 0;
4
+ padding: 0;
5
+ margin: 0;
6
+
2
7
  margin-bottom: $grid-unit-15;
3
8
 
4
9
  legend {
@@ -6,7 +6,6 @@ import {
6
6
  __experimentalToggleGroupControlOption as ToggleGroupControlOption,
7
7
  __experimentalUnitControl as UnitControl,
8
8
  __experimentalInputControl as InputControl,
9
- __experimentalHStack as HStack,
10
9
  __experimentalVStack as VStack,
11
10
  __experimentalToolsPanelItem as ToolsPanelItem,
12
11
  __experimentalUseCustomUnits as useCustomUnits,
@@ -210,7 +209,7 @@ function GridControls( {
210
209
  panelId,
211
210
  } ) {
212
211
  const { columnStart, rowStart, columnSpan, rowSpan } = childLayout;
213
- const { columnCount = 3, rowCount } = parentLayout ?? {};
212
+ const { columnCount, rowCount } = parentLayout ?? {};
214
213
  const rootClientId = useSelect( ( select ) =>
215
214
  select( blockEditorStore ).getBlockRootClientId( panelId )
216
215
  );
@@ -218,7 +217,7 @@ function GridControls( {
218
217
  useDispatch( blockEditorStore );
219
218
  const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell(
220
219
  rootClientId,
221
- columnCount
220
+ columnCount || 3
222
221
  );
223
222
  const hasStartValue = () => !! columnStart || !! rowStart;
224
223
  const hasSpanValue = () => !! columnSpan || !! rowSpan;
@@ -235,9 +234,20 @@ function GridControls( {
235
234
  } );
236
235
  };
237
236
 
237
+ // Calculate max column span based on current position and grid width
238
+ const maxColumnSpan = columnCount
239
+ ? columnCount - ( columnStart ?? 1 ) + 1
240
+ : undefined;
241
+
242
+ // Calculate max row span based on current position and grid height
243
+ const maxRowSpan =
244
+ window.__experimentalEnableGridInteractivity && rowCount
245
+ ? rowCount - ( rowStart ?? 1 ) + 1
246
+ : undefined;
247
+
238
248
  return (
239
249
  <>
240
- <HStack
250
+ <Flex
241
251
  as={ ToolsPanelItem }
242
252
  hasValue={ hasSpanValue }
243
253
  label={ __( 'Grid span' ) }
@@ -245,44 +255,58 @@ function GridControls( {
245
255
  isShownByDefault={ isShownByDefault }
246
256
  panelId={ panelId }
247
257
  >
248
- <InputControl
249
- size="__unstable-large"
250
- label={ __( 'Column span' ) }
251
- type="number"
252
- onChange={ ( value ) => {
253
- // Don't allow unsetting.
254
- const newColumnSpan =
255
- value === '' ? 1 : parseInt( value, 10 );
256
- onChange( {
257
- columnStart,
258
- rowStart,
259
- rowSpan,
260
- columnSpan: newColumnSpan,
261
- } );
262
- } }
263
- value={ columnSpan ?? 1 }
264
- min={ 1 }
265
- />
266
- <InputControl
267
- size="__unstable-large"
268
- label={ __( 'Row span' ) }
269
- type="number"
270
- onChange={ ( value ) => {
271
- // Don't allow unsetting.
272
- const newRowSpan =
273
- value === '' ? 1 : parseInt( value, 10 );
274
- onChange( {
275
- columnStart,
276
- rowStart,
277
- columnSpan,
278
- rowSpan: newRowSpan,
279
- } );
280
- } }
281
- value={ rowSpan ?? 1 }
282
- min={ 1 }
283
- />
284
- </HStack>
285
- { window.__experimentalEnableGridInteractivity && columnCount && (
258
+ <FlexItem style={ { width: '50%' } }>
259
+ <InputControl
260
+ size="__unstable-large"
261
+ label={ __( 'Column span' ) }
262
+ type="number"
263
+ onChange={ ( value ) => {
264
+ // Don't allow unsetting.
265
+ const newColumnSpan =
266
+ value === '' ? 1 : parseInt( value, 10 );
267
+ const constrainedValue = maxColumnSpan
268
+ ? Math.min( newColumnSpan, maxColumnSpan )
269
+ : newColumnSpan;
270
+
271
+ onChange( {
272
+ columnStart,
273
+ rowStart,
274
+ rowSpan,
275
+ columnSpan: constrainedValue,
276
+ } );
277
+ } }
278
+ value={ columnSpan ?? 1 }
279
+ min={ 1 }
280
+ max={ maxColumnSpan }
281
+ />
282
+ </FlexItem>
283
+ <FlexItem style={ { width: '50%' } }>
284
+ <InputControl
285
+ size="__unstable-large"
286
+ label={ __( 'Row span' ) }
287
+ type="number"
288
+ onChange={ ( value ) => {
289
+ const newRowSpan =
290
+ value === '' ? 1 : parseInt( value, 10 );
291
+ const constrainedValue = maxRowSpan
292
+ ? Math.min( newRowSpan, maxRowSpan )
293
+ : newRowSpan;
294
+
295
+ onChange( {
296
+ columnStart,
297
+ rowStart,
298
+ columnSpan,
299
+ rowSpan: constrainedValue,
300
+ } );
301
+ } }
302
+ value={ rowSpan ?? 1 }
303
+ min={ 1 }
304
+ max={ maxRowSpan }
305
+ />
306
+ </FlexItem>
307
+ </Flex>
308
+
309
+ { window.__experimentalEnableGridInteractivity && (
286
310
  // Use Flex with an explicit width on the FlexItem instead of HStack to
287
311
  // work around an issue in webkit where inputs with a max attribute are
288
312
  // sized incorrectly.
@@ -10,6 +10,11 @@ $swatch-gap: 12px;
10
10
  }
11
11
 
12
12
  .block-editor-color-gradient-control__fieldset {
13
+ // Reset `fieldset` browser defaults.
14
+ border: 0;
15
+ padding: 0;
16
+ margin: 0;
17
+
13
18
  // Prevents the `fieldset` from growing beyond its parent's size
14
19
  // in order to fit its content.
15
20
  min-width: 0;
@@ -176,11 +176,12 @@
176
176
 
177
177
  .editor-collapsible-block-toolbar {
178
178
  .block-editor-grid-item-mover__move-vertical-button-container {
179
- // Move up a little to prevent the toolbar shift when focus is on the vertical movers.
179
+ // Reduce height and move up a little to prevent the toolbar
180
+ // shift when focus is on the vertical movers.
180
181
  @include break-small() {
181
182
  height: $grid-unit-50;
182
183
  position: relative;
183
- top: -5px; // Should be -4px, but that causes scrolling when focus lands on the movers, in a 60px header.
184
+ top: #{$grid-unit-05 * -1};
184
185
  }
185
186
  }
186
187
  }
@@ -131,6 +131,29 @@ function Iframe( {
131
131
  function preventFileDropDefault( event ) {
132
132
  event.preventDefault();
133
133
  }
134
+ // Prevent clicks on link fragments from navigating away. Note that links
135
+ // inside `contenteditable` are already disabled by the browser, so
136
+ // this is for links in blocks outside of `contenteditable`.
137
+ function interceptLinkClicks( event ) {
138
+ if (
139
+ event.target.tagName === 'A' &&
140
+ event.target.getAttribute( 'href' )?.startsWith( '#' )
141
+ ) {
142
+ event.preventDefault();
143
+ // Manually handle link fragment navigation within the iframe. The iframe's
144
+ // location is a blob URL, which can't be used to resolve relative links like
145
+ // `#hash`. The relative link would be resolved against the iframe's base URL
146
+ // or the parent frame's URL, causing the iframe to navigate to a completely
147
+ // different page. Setting the `location.hash` works because it really sets the
148
+ // blob URL's hash.
149
+ //
150
+ // Links with fragments are used for example with footnotes. Clicking on these
151
+ // links will scroll smoothly to the anchors in the editor canvas.
152
+ iFrameDocument.defaultView.location.hash = event.target
153
+ .getAttribute( 'href' )
154
+ .slice( 1 );
155
+ }
156
+ }
134
157
 
135
158
  const { ownerDocument } = node;
136
159
 
@@ -185,22 +208,7 @@ function Iframe( {
185
208
  preventFileDropDefault,
186
209
  false
187
210
  );
188
- // Prevent clicks on links from navigating away. Note that links
189
- // inside `contenteditable` are already disabled by the browser, so
190
- // this is for links in blocks outside of `contenteditable`.
191
- iFrameDocument.addEventListener( 'click', ( event ) => {
192
- if ( event.target.tagName === 'A' ) {
193
- event.preventDefault();
194
-
195
- // Appending a hash to the current URL will not reload the
196
- // page. This is useful for e.g. footnotes.
197
- const href = event.target.getAttribute( 'href' );
198
- if ( href?.startsWith( '#' ) ) {
199
- iFrameDocument.defaultView.location.hash =
200
- href.slice( 1 );
201
- }
202
- }
203
- } );
211
+ iFrameDocument.addEventListener( 'click', interceptLinkClicks );
204
212
  }
205
213
 
206
214
  node.addEventListener( 'load', onLoad );
@@ -216,6 +224,7 @@ function Iframe( {
216
224
  'drop',
217
225
  preventFileDropDefault
218
226
  );
227
+ iFrameDocument?.removeEventListener( 'click', interceptLinkClicks );
219
228
  };
220
229
  }, [] );
221
230
 
@@ -1,15 +1,18 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- // Disable Reason: Needs to be refactored.
5
- // eslint-disable-next-line no-restricted-imports
6
- import apiFetch from '@wordpress/api-fetch';
7
- import { useDispatch } from '@wordpress/data';
4
+ import { useDispatch, useSelect } from '@wordpress/data';
8
5
  import { useCallback, useMemo, useState } from '@wordpress/element';
9
6
  import { __, sprintf } from '@wordpress/i18n';
10
7
  import { store as noticesStore } from '@wordpress/notices';
11
8
  import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
12
9
 
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import { store as blockEditorStore } from '../../store';
14
+ import { mediaEditKey } from '../../store/private-keys';
15
+
13
16
  const messages = {
14
17
  crop: __( 'Image cropped.' ),
15
18
  rotate: __( 'Image rotated.' ),
@@ -27,13 +30,31 @@ export default function useSaveImage( {
27
30
  const { createErrorNotice, createSuccessNotice } =
28
31
  useDispatch( noticesStore );
29
32
  const [ isInProgress, setIsInProgress ] = useState( false );
33
+ const { editMediaEntity } = useSelect( ( select ) => {
34
+ const settings = select( blockEditorStore ).getSettings();
35
+ return {
36
+ editMediaEntity: settings?.[ mediaEditKey ],
37
+ };
38
+ }, [] );
30
39
 
31
40
  const cancel = useCallback( () => {
32
41
  setIsInProgress( false );
33
42
  onFinishEditing();
34
43
  }, [ onFinishEditing ] );
35
44
 
36
- const apply = useCallback( () => {
45
+ const apply = useCallback( async () => {
46
+ if ( ! editMediaEntity ) {
47
+ onFinishEditing();
48
+ createErrorNotice(
49
+ __( 'Sorry, you are not allowed to edit images on this site.' ),
50
+ {
51
+ id: 'image-editing-error',
52
+ type: 'snackbar',
53
+ }
54
+ );
55
+ return;
56
+ }
57
+
37
58
  setIsInProgress( true );
38
59
 
39
60
  const modifiers = [];
@@ -71,16 +92,22 @@ export default function useSaveImage( {
71
92
  const modifierType =
72
93
  modifiers.length === 1 ? modifiers[ 0 ].type : 'cropAndRotate';
73
94
 
74
- apiFetch( {
75
- path: `/wp/v2/media/${ id }/edit`,
76
- method: 'POST',
77
- data: { src: url, modifiers },
78
- } )
79
- .then( ( response ) => {
95
+ try {
96
+ const savedImage = await editMediaEntity(
97
+ id,
98
+ {
99
+ src: url,
100
+ modifiers,
101
+ },
102
+ { throwOnError: true }
103
+ );
104
+
105
+ if ( savedImage ) {
80
106
  onSaveImage( {
81
- id: response.id,
82
- url: response.source_url,
107
+ id: savedImage.id,
108
+ url: savedImage.source_url,
83
109
  } );
110
+
84
111
  createSuccessNotice( messages[ modifierType ], {
85
112
  type: 'snackbar',
86
113
  actions: [
@@ -95,24 +122,23 @@ export default function useSaveImage( {
95
122
  },
96
123
  ],
97
124
  } );
98
- } )
99
- .catch( ( error ) => {
100
- createErrorNotice(
101
- sprintf(
102
- /* translators: %s: Error message. */
103
- __( 'Could not edit image. %s' ),
104
- stripHTML( error.message )
105
- ),
106
- {
107
- id: 'image-editing-error',
108
- type: 'snackbar',
109
- }
110
- );
111
- } )
112
- .finally( () => {
113
- setIsInProgress( false );
114
- onFinishEditing();
115
- } );
125
+ }
126
+ } catch ( error ) {
127
+ createErrorNotice(
128
+ sprintf(
129
+ /* translators: %s: Error message. */
130
+ __( 'Could not edit image. %s' ),
131
+ stripHTML( error.message )
132
+ ),
133
+ {
134
+ id: 'image-editing-error',
135
+ type: 'snackbar',
136
+ }
137
+ );
138
+ } finally {
139
+ setIsInProgress( false );
140
+ onFinishEditing();
141
+ }
116
142
  }, [
117
143
  crop,
118
144
  rotation,
@@ -122,6 +148,7 @@ export default function useSaveImage( {
122
148
  createErrorNotice,
123
149
  createSuccessNotice,
124
150
  onFinishEditing,
151
+ editMediaEntity,
125
152
  ] );
126
153
 
127
154
  return useMemo(
@@ -373,7 +373,7 @@ $block-inserter-tabs-height: 44px;
373
373
  .block-editor-block-patterns-explorer {
374
374
  &__sidebar {
375
375
  position: absolute;
376
- top: $header-height + $grid-unit-15;
376
+ top: $header-height + $grid-unit-10;
377
377
  left: 0;
378
378
  bottom: 0;
379
379
  width: $sidebar-width;
@@ -6,7 +6,7 @@ import { View, TouchableHighlight, Text } from 'react-native';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { Component } from '@wordpress/element';
9
+ import { useCallback } from '@wordpress/element';
10
10
  import { Icon } from '@wordpress/components';
11
11
  import { withPreferredColorScheme } from '@wordpress/compose';
12
12
  import { __, sprintf } from '@wordpress/i18n';
@@ -17,93 +17,82 @@ import { __, sprintf } from '@wordpress/i18n';
17
17
  import { BlockIcon } from '../block-icon';
18
18
  import styles from './style.scss';
19
19
  import sparkles from './sparkles';
20
- class MenuItem extends Component {
21
- constructor() {
22
- super( ...arguments );
23
20
 
24
- this.onPress = this.onPress.bind( this );
25
- }
26
-
27
- onPress() {
28
- const { onSelect, item } = this.props;
21
+ function MenuItem( {
22
+ getStylesFromColorScheme,
23
+ item,
24
+ itemWidth,
25
+ maxWidth,
26
+ onSelect,
27
+ } ) {
28
+ const onPress = useCallback( () => {
29
29
  onSelect( item );
30
- }
31
-
32
- render() {
33
- const { getStylesFromColorScheme, item, itemWidth, maxWidth } =
34
- this.props;
30
+ }, [ onSelect, item ] );
35
31
 
36
- const modalIconWrapperStyle = getStylesFromColorScheme(
37
- styles.modalIconWrapper,
38
- styles.modalIconWrapperDark
39
- );
40
- const modalIconStyle = styles.modalIcon;
41
- const modalItemLabelStyle = getStylesFromColorScheme(
42
- styles.modalItemLabel,
43
- styles.modalItemLabelDark
44
- );
32
+ const modalIconWrapperStyle = getStylesFromColorScheme(
33
+ styles.modalIconWrapper,
34
+ styles.modalIconWrapperDark
35
+ );
36
+ const modalIconStyle = styles.modalIcon;
37
+ const modalItemLabelStyle = getStylesFromColorScheme(
38
+ styles.modalItemLabel,
39
+ styles.modalItemLabelDark
40
+ );
45
41
 
46
- const clipboardBlockStyles = getStylesFromColorScheme(
47
- styles.clipboardBlock,
48
- styles.clipboardBlockDark
49
- );
42
+ const clipboardBlockStyles = getStylesFromColorScheme(
43
+ styles.clipboardBlock,
44
+ styles.clipboardBlockDark
45
+ );
50
46
 
51
- const isClipboardBlock = item.id === 'clipboard';
52
- const blockTitle = isClipboardBlock ? __( 'Copied block' ) : item.title;
53
- const blockIsNew = item.isNew === true;
54
- const accessibilityLabelFormat = blockIsNew
55
- ? // translators: Newly available block name. %s: The localized block name
56
- __( '%s block, newly available' )
57
- : // translators: %s: Block name e.g. "Image block"
58
- __( '%s block' );
59
- const accessibilityLabel = sprintf(
60
- accessibilityLabelFormat,
61
- item.title
62
- );
47
+ const isClipboardBlock = item.id === 'clipboard';
48
+ const blockTitle = isClipboardBlock ? __( 'Copied block' ) : item.title;
49
+ const blockIsNew = item.isNew === true;
50
+ const accessibilityLabelFormat = blockIsNew
51
+ ? // translators: Newly available block name. %s: The localized block name
52
+ __( '%s block, newly available' )
53
+ : // translators: %s: Block name e.g. "Image block"
54
+ __( '%s block' );
55
+ const accessibilityLabel = sprintf( accessibilityLabelFormat, item.title );
63
56
 
64
- return (
65
- <TouchableHighlight
66
- style={ [
67
- styles.touchableArea,
68
- item.isDisabled ? styles.disabled : null,
69
- ] }
70
- underlayColor="transparent"
71
- activeOpacity={ 0.5 }
72
- accessibilityRole="button"
73
- accessibilityLabel={ accessibilityLabel }
74
- onPress={ this.onPress }
75
- disabled={ item.isDisabled }
76
- >
77
- <View style={ [ styles.modalItem, { width: maxWidth } ] }>
78
- <View
79
- style={ [
80
- modalIconWrapperStyle,
81
- itemWidth && {
82
- width: itemWidth,
83
- },
84
- isClipboardBlock && clipboardBlockStyles,
85
- ] }
86
- >
87
- { blockIsNew && (
88
- <Icon
89
- icon={ sparkles }
90
- style={ styles.newIndicator }
91
- />
92
- ) }
93
- <View style={ modalIconStyle }>
94
- <BlockIcon
95
- icon={ item.icon }
96
- size={ modalIconStyle.width }
97
- />
98
- </View>
57
+ return (
58
+ <TouchableHighlight
59
+ style={ [
60
+ styles.touchableArea,
61
+ item.isDisabled ? styles.disabled : null,
62
+ ] }
63
+ underlayColor="transparent"
64
+ activeOpacity={ 0.5 }
65
+ accessibilityRole="button"
66
+ accessibilityLabel={ accessibilityLabel }
67
+ onPress={ onPress }
68
+ disabled={ item.isDisabled }
69
+ >
70
+ <View style={ [ styles.modalItem, { width: maxWidth } ] }>
71
+ <View
72
+ style={ [
73
+ modalIconWrapperStyle,
74
+ itemWidth && {
75
+ width: itemWidth,
76
+ },
77
+ isClipboardBlock && clipboardBlockStyles,
78
+ ] }
79
+ >
80
+ { blockIsNew && (
81
+ <Icon icon={ sparkles } style={ styles.newIndicator } />
82
+ ) }
83
+ <View style={ modalIconStyle }>
84
+ <BlockIcon
85
+ icon={ item.icon }
86
+ size={ modalIconStyle.width }
87
+ />
99
88
  </View>
100
- <Text numberOfLines={ 3 } style={ modalItemLabelStyle }>
101
- { blockTitle }
102
- </Text>
103
89
  </View>
104
- </TouchableHighlight>
105
- );
106
- }
90
+ <Text numberOfLines={ 3 } style={ modalItemLabelStyle }>
91
+ { blockTitle }
92
+ </Text>
93
+ </View>
94
+ </TouchableHighlight>
95
+ );
107
96
  }
108
97
 
109
98
  const InserterButton = withPreferredColorScheme( MenuItem );
@@ -49,12 +49,13 @@
49
49
  position: relative;
50
50
  height: auto;
51
51
 
52
- &:disabled {
52
+ &:disabled,
53
+ &[aria-disabled="true"] {
53
54
  opacity: 0.6;
54
55
  cursor: default;
55
56
  }
56
57
 
57
- &:not(:disabled) {
58
+ &:not(:disabled, [aria-disabled="true"]) {
58
59
  &:hover {
59
60
  .block-editor-block-types-list__item-title {
60
61
  color: var(--wp-admin-theme-color) !important;
@@ -80,10 +81,6 @@
80
81
  }
81
82
  }
82
83
 
83
- &:focus {
84
- box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
85
- }
86
-
87
84
  &.is-active {
88
85
  color: $white;
89
86
  background: $gray-900;
@@ -93,6 +90,12 @@
93
90
  outline-offset: -2px;
94
91
  }
95
92
  }
93
+
94
+ &:not(:disabled) {
95
+ &:focus {
96
+ box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
97
+ }
98
+ }
96
99
  }
97
100
 
98
101
  .block-editor-block-types-list__item-icon {
@@ -114,16 +114,23 @@ const LinkControlSearchInput = forwardRef(
114
114
  }
115
115
  };
116
116
 
117
+ const _placeholder = placeholder ?? __( 'Search or type URL' );
118
+
119
+ const label =
120
+ hideLabelFromVision && placeholder !== ''
121
+ ? _placeholder
122
+ : __( 'Link' );
123
+
117
124
  return (
118
125
  <div className="block-editor-link-control__search-input-container">
119
126
  <URLInput
120
127
  disableSuggestions={ currentLink?.url === value }
121
- label={ __( 'Link' ) }
128
+ label={ label }
122
129
  hideLabelFromVision={ hideLabelFromVision }
123
130
  className={ className }
124
131
  value={ value }
125
132
  onChange={ onInputChange }
126
- placeholder={ placeholder ?? __( 'Search or type URL' ) }
133
+ placeholder={ _placeholder }
127
134
  __experimentalRenderSuggestions={
128
135
  showSuggestions ? handleRenderSuggestions : null
129
136
  }