@wordpress/block-library 9.45.1-next.v.202605131032.0 → 9.47.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 (182) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/button/edit.cjs +7 -4
  3. package/build/button/edit.cjs.map +3 -3
  4. package/build/columns/edit.cjs +4 -10
  5. package/build/columns/edit.cjs.map +2 -2
  6. package/build/comments/edit/placeholder.cjs +1 -1
  7. package/build/comments/edit/placeholder.cjs.map +2 -2
  8. package/build/cover/edit/inspector-controls.cjs +4 -2
  9. package/build/cover/edit/inspector-controls.cjs.map +2 -2
  10. package/build/freeform/migration-notice.cjs +1 -1
  11. package/build/freeform/migration-notice.cjs.map +1 -1
  12. package/build/home-link/block.json +7 -0
  13. package/build/home-link/edit.cjs +167 -24
  14. package/build/home-link/edit.cjs.map +3 -3
  15. package/build/html/edit.cjs +2 -4
  16. package/build/html/edit.cjs.map +2 -2
  17. package/build/html/modal.cjs +0 -4
  18. package/build/html/modal.cjs.map +2 -2
  19. package/build/image/block.json +4 -0
  20. package/build/image/deprecated.cjs +202 -4
  21. package/build/image/deprecated.cjs.map +3 -3
  22. package/build/image/image.cjs +80 -27
  23. package/build/image/image.cjs.map +2 -2
  24. package/build/image/index.cjs +23 -4
  25. package/build/image/index.cjs.map +2 -2
  26. package/build/image/save.cjs +25 -10
  27. package/build/image/save.cjs.map +2 -2
  28. package/build/image/transforms.cjs +15 -3
  29. package/build/image/transforms.cjs.map +2 -2
  30. package/build/image/use-open-image-media-editor-modal.cjs +29 -12
  31. package/build/image/use-open-image-media-editor-modal.cjs.map +2 -2
  32. package/build/list-item/hooks/use-enter.cjs +8 -4
  33. package/build/list-item/hooks/use-enter.cjs.map +3 -3
  34. package/build/list-item/hooks/use-space.cjs +8 -4
  35. package/build/list-item/hooks/use-space.cjs.map +3 -3
  36. package/build/navigation-link/edit.cjs +2 -1
  37. package/build/navigation-link/edit.cjs.map +2 -2
  38. package/build/navigation-link/shared/use-handle-link-change.cjs +19 -3
  39. package/build/navigation-link/shared/use-handle-link-change.cjs.map +3 -3
  40. package/build/navigation-submenu/edit.cjs +8 -22
  41. package/build/navigation-submenu/edit.cjs.map +2 -2
  42. package/build/paragraph/use-enter.cjs +8 -4
  43. package/build/paragraph/use-enter.cjs.map +3 -3
  44. package/build/post-date/edit.cjs +9 -1
  45. package/build/post-date/edit.cjs.map +2 -2
  46. package/build/post-featured-image/edit.cjs +0 -1
  47. package/build/post-featured-image/edit.cjs.map +2 -2
  48. package/build/site-logo/edit.cjs +1 -1
  49. package/build/site-logo/edit.cjs.map +2 -2
  50. package/build/social-link/edit.cjs.map +3 -3
  51. package/build/tab-list/edit.cjs +2 -0
  52. package/build/tab-list/edit.cjs.map +2 -2
  53. package/build/tab-panels/edit.cjs +5 -1
  54. package/build/tab-panels/edit.cjs.map +2 -2
  55. package/build/table/edit.cjs +1 -0
  56. package/build/table/edit.cjs.map +2 -2
  57. package/build/tabs/edit.cjs +1 -36
  58. package/build/tabs/edit.cjs.map +2 -2
  59. package/build-module/button/edit.mjs +12 -5
  60. package/build-module/button/edit.mjs.map +2 -2
  61. package/build-module/columns/edit.mjs +4 -10
  62. package/build-module/columns/edit.mjs.map +2 -2
  63. package/build-module/comments/edit/placeholder.mjs +1 -1
  64. package/build-module/comments/edit/placeholder.mjs.map +2 -2
  65. package/build-module/cover/edit/inspector-controls.mjs +4 -3
  66. package/build-module/cover/edit/inspector-controls.mjs.map +2 -2
  67. package/build-module/freeform/migration-notice.mjs +1 -1
  68. package/build-module/freeform/migration-notice.mjs.map +1 -1
  69. package/build-module/home-link/block.json +7 -0
  70. package/build-module/home-link/edit.mjs +181 -26
  71. package/build-module/home-link/edit.mjs.map +2 -2
  72. package/build-module/html/edit.mjs +2 -4
  73. package/build-module/html/edit.mjs.map +2 -2
  74. package/build-module/html/modal.mjs +0 -4
  75. package/build-module/html/modal.mjs.map +2 -2
  76. package/build-module/image/block.json +4 -0
  77. package/build-module/image/deprecated.mjs +204 -5
  78. package/build-module/image/deprecated.mjs.map +2 -2
  79. package/build-module/image/image.mjs +81 -27
  80. package/build-module/image/image.mjs.map +2 -2
  81. package/build-module/image/index.mjs +23 -4
  82. package/build-module/image/index.mjs.map +2 -2
  83. package/build-module/image/save.mjs +25 -10
  84. package/build-module/image/save.mjs.map +2 -2
  85. package/build-module/image/transforms.mjs +15 -3
  86. package/build-module/image/transforms.mjs.map +2 -2
  87. package/build-module/image/use-open-image-media-editor-modal.mjs +29 -12
  88. package/build-module/image/use-open-image-media-editor-modal.mjs.map +2 -2
  89. package/build-module/list-item/hooks/use-enter.mjs +12 -5
  90. package/build-module/list-item/hooks/use-enter.mjs.map +2 -2
  91. package/build-module/list-item/hooks/use-space.mjs +12 -5
  92. package/build-module/list-item/hooks/use-space.mjs.map +2 -2
  93. package/build-module/navigation-link/edit.mjs +2 -1
  94. package/build-module/navigation-link/edit.mjs.map +2 -2
  95. package/build-module/navigation-link/shared/use-handle-link-change.mjs +19 -3
  96. package/build-module/navigation-link/shared/use-handle-link-change.mjs.map +2 -2
  97. package/build-module/navigation-submenu/edit.mjs +9 -23
  98. package/build-module/navigation-submenu/edit.mjs.map +2 -2
  99. package/build-module/paragraph/use-enter.mjs +12 -5
  100. package/build-module/paragraph/use-enter.mjs.map +2 -2
  101. package/build-module/post-date/edit.mjs +9 -1
  102. package/build-module/post-date/edit.mjs.map +2 -2
  103. package/build-module/post-featured-image/edit.mjs +0 -1
  104. package/build-module/post-featured-image/edit.mjs.map +2 -2
  105. package/build-module/site-logo/edit.mjs +1 -1
  106. package/build-module/site-logo/edit.mjs.map +2 -2
  107. package/build-module/social-link/edit.mjs +2 -2
  108. package/build-module/social-link/edit.mjs.map +2 -2
  109. package/build-module/tab-list/edit.mjs +2 -0
  110. package/build-module/tab-list/edit.mjs.map +2 -2
  111. package/build-module/tab-panels/edit.mjs +5 -1
  112. package/build-module/tab-panels/edit.mjs.map +2 -2
  113. package/build-module/table/edit.mjs +1 -0
  114. package/build-module/table/edit.mjs.map +2 -2
  115. package/build-module/tabs/edit.mjs +2 -37
  116. package/build-module/tabs/edit.mjs.map +2 -2
  117. package/build-style/breadcrumbs/style-rtl.css +1 -1
  118. package/build-style/breadcrumbs/style.css +1 -1
  119. package/build-style/editor-rtl.css +0 -11
  120. package/build-style/editor.css +0 -11
  121. package/build-style/gallery/editor-rtl.css +0 -11
  122. package/build-style/gallery/editor.css +0 -11
  123. package/build-style/style-rtl.css +1 -1
  124. package/build-style/style.css +1 -1
  125. package/package.json +42 -42
  126. package/src/block/edit-title.native.js +3 -3
  127. package/src/block/edit.native.js +2 -2
  128. package/src/breadcrumbs/style.scss +1 -1
  129. package/src/button/edit.js +14 -5
  130. package/src/columns/edit.js +3 -9
  131. package/src/comments/edit/placeholder.js +1 -1
  132. package/src/cover/controls.native.js +2 -2
  133. package/src/cover/edit/inspector-controls.js +8 -7
  134. package/src/cover/edit.native.js +6 -4
  135. package/src/cover/focal-point-settings-button.native.js +2 -2
  136. package/src/cover/test/edit.js +32 -31
  137. package/src/embed/embed-no-preview.native.js +7 -3
  138. package/src/embed/embed-placeholder.native.js +2 -2
  139. package/src/file/edit.native.js +2 -2
  140. package/src/freeform/migration-notice.js +1 -1
  141. package/src/gallery/editor.scss +0 -14
  142. package/src/home-link/block.json +7 -0
  143. package/src/home-link/edit.js +185 -22
  144. package/src/home-link/index.php +14 -2
  145. package/src/html/edit.js +14 -12
  146. package/src/html/modal.js +0 -5
  147. package/src/image/block.json +4 -0
  148. package/src/image/deprecated.js +236 -4
  149. package/src/image/edit.native.js +2 -2
  150. package/src/image/image.js +116 -41
  151. package/src/image/index.js +20 -1
  152. package/src/image/index.php +1 -1
  153. package/src/image/save.js +39 -12
  154. package/src/image/test/use-open-image-media-editor-modal.js +60 -0
  155. package/src/image/transforms.js +21 -5
  156. package/src/image/use-open-image-media-editor-modal.js +34 -16
  157. package/src/latest-posts/edit.native.js +2 -2
  158. package/src/list-item/hooks/use-enter.js +15 -5
  159. package/src/list-item/hooks/use-space.js +15 -5
  160. package/src/list-item/list-style-type.native.js +2 -2
  161. package/src/media-text/media-container.native.js +7 -3
  162. package/src/missing/edit.native.js +4 -4
  163. package/src/missing/test/edit.native.js +3 -3
  164. package/src/navigation/test/use-navigation-menu.js +8 -2
  165. package/src/navigation-link/edit.js +1 -0
  166. package/src/navigation-link/shared/test/use-handle-link-change.test.js +212 -0
  167. package/src/navigation-link/shared/use-handle-link-change.js +36 -9
  168. package/src/navigation-link/test/__snapshots__/hooks.js.snap +134 -45
  169. package/src/navigation-submenu/edit.js +11 -28
  170. package/src/navigation-submenu/index.php +13 -0
  171. package/src/paragraph/use-enter.js +19 -5
  172. package/src/post-date/edit.js +7 -3
  173. package/src/post-featured-image/edit.js +0 -1
  174. package/src/search/edit.native.js +2 -2
  175. package/src/search/test/edit.native.js +2 -2
  176. package/src/site-logo/edit.js +2 -1
  177. package/src/social-link/edit.js +2 -2
  178. package/src/tab-list/edit.js +3 -0
  179. package/src/tab-panels/edit.js +10 -1
  180. package/src/table/edit.js +1 -0
  181. package/src/tabs/edit.js +14 -42
  182. package/src/video/edit.native.js +3 -3
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import { useMemo } from '@wordpress/element';
5
5
  import {
6
- ExternalLink,
7
6
  FocalPointPicker,
8
7
  RangeControl,
9
8
  TextareaControl,
@@ -27,6 +26,7 @@ import {
27
26
  import { __ } from '@wordpress/i18n';
28
27
  import { useSelect } from '@wordpress/data';
29
28
  import { store as coreStore } from '@wordpress/core-data';
29
+ import { Link } from '@wordpress/ui';
30
30
 
31
31
  /**
32
32
  * Internal dependencies
@@ -193,8 +193,8 @@ export default function CoverInspectorControls( {
193
193
 
194
194
  return (
195
195
  <>
196
- <InspectorControls>
197
- { ( !! url || useFeaturedImage ) && (
196
+ { ( !! url || useFeaturedImage ) && (
197
+ <InspectorControls>
198
198
  <ToolsPanel
199
199
  label={ __( 'Settings' ) }
200
200
  resetAll={ () => {
@@ -299,7 +299,8 @@ export default function CoverInspectorControls( {
299
299
  }
300
300
  help={
301
301
  <>
302
- <ExternalLink
302
+ <Link
303
+ openInNewTab
303
304
  href={
304
305
  // translators: Localized tutorial, if one exists. W3C Web Accessibility Initiative link has list of existing translations.
305
306
  __(
@@ -310,7 +311,7 @@ export default function CoverInspectorControls( {
310
311
  { __(
311
312
  'Describe the purpose of the image.'
312
313
  ) }
313
- </ExternalLink>
314
+ </Link>
314
315
  <br />
315
316
  { __(
316
317
  'Leave empty if decorative.'
@@ -329,8 +330,8 @@ export default function CoverInspectorControls( {
329
330
  />
330
331
  ) }
331
332
  </ToolsPanel>
332
- ) }
333
- </InspectorControls>
333
+ </InspectorControls>
334
+ ) }
334
335
  { colorGradientSettings.hasColorsOrGradients && (
335
336
  <InspectorControls group="color">
336
337
  <ColorGradientSettingsDropdown
@@ -23,7 +23,7 @@ import {
23
23
  } from '@wordpress/react-native-bridge';
24
24
  import { __ } from '@wordpress/i18n';
25
25
  import {
26
- Icon,
26
+ Icon as WCIcon,
27
27
  Image,
28
28
  ImageEditingButton,
29
29
  IMAGE_DEFAULT_FOCAL_POINT,
@@ -354,7 +354,9 @@ const Cover = ( {
354
354
  styles.iconDark
355
355
  );
356
356
 
357
- const placeholderIcon = <Icon icon={ icon } { ...placeholderIconStyle } />;
357
+ const placeholderIcon = (
358
+ <WCIcon icon={ icon } { ...placeholderIconStyle } />
359
+ );
358
360
 
359
361
  const toolbarControls = ( open ) => (
360
362
  <BlockControls group="other">
@@ -380,7 +382,7 @@ const Cover = ( {
380
382
  >
381
383
  <View style={ styles.selectImageContainer }>
382
384
  <View style={ styles.selectImage }>
383
- <Icon
385
+ <WCIcon
384
386
  size={ 16 }
385
387
  icon={ image }
386
388
  { ...styles.selectImageIcon }
@@ -665,7 +667,7 @@ const Cover = ( {
665
667
  style={ styles.uploadFailedContainer }
666
668
  >
667
669
  <View style={ styles.uploadFailed }>
668
- <Icon
670
+ <WCIcon
669
671
  icon={ cautionFilled }
670
672
  { ...styles.uploadFailedIcon }
671
673
  />
@@ -8,7 +8,7 @@ import { View } from 'react-native';
8
8
  * WordPress dependencies
9
9
  */
10
10
  import { __ } from '@wordpress/i18n';
11
- import { Icon, BottomSheet } from '@wordpress/components';
11
+ import { Icon as WCIcon, BottomSheet } from '@wordpress/components';
12
12
  import { blockSettingsScreens } from '@wordpress/block-editor';
13
13
  import { chevronRight } from '@wordpress/icons';
14
14
 
@@ -44,7 +44,7 @@ function FocalPointSettingsButton( {
44
44
  * issue: https://github.com/react-native-svg/react-native-svg/issues/1345
45
45
  */ }
46
46
  <View style={ disabled && styles.dimmedActionButton }>
47
- <Icon icon={ chevronRight } />
47
+ <WCIcon icon={ chevronRight } />
48
48
  </View>
49
49
  </BottomSheet.Cell>
50
50
  );
@@ -54,6 +54,16 @@ async function createAndSelectBlock() {
54
54
  await selectBlock( 'Block: Cover' );
55
55
  }
56
56
 
57
+ async function openStylesTabIfAvailable() {
58
+ const stylesTab = screen.queryByRole( 'tab', {
59
+ name: 'Styles',
60
+ } );
61
+
62
+ if ( stylesTab ) {
63
+ await userEvent.click( stylesTab );
64
+ }
65
+ }
66
+
57
67
  describe( 'Cover block', () => {
58
68
  describe( 'Editor canvas', () => {
59
69
  test( 'shows placeholder if background image and color not set', async () => {
@@ -189,6 +199,21 @@ describe( 'Cover block', () => {
189
199
  } )
190
200
  ).not.toBeInTheDocument();
191
201
  } );
202
+ test( 'does not display settings tab when media settings are empty', async () => {
203
+ await setup();
204
+ await createAndSelectBlock();
205
+
206
+ expect(
207
+ screen.queryByRole( 'tab', {
208
+ name: 'Settings',
209
+ } )
210
+ ).not.toBeInTheDocument();
211
+ expect(
212
+ screen.getByRole( 'button', {
213
+ name: 'Advanced',
214
+ } )
215
+ ).toBeInTheDocument();
216
+ } );
192
217
  test( 'displays media settings panel if url is set', async () => {
193
218
  await setup( {
194
219
  url: 'http://localhost/my-image.jpg',
@@ -275,11 +300,7 @@ describe( 'Cover block', () => {
275
300
 
276
301
  expect( overlay[ 0 ] ).toHaveClass( 'has-background-dim-100' );
277
302
 
278
- await userEvent.click(
279
- screen.getByRole( 'tab', {
280
- name: 'Styles',
281
- } )
282
- );
303
+ await openStylesTabIfAvailable();
283
304
  // Need act here as the isDark method is async.
284
305
  // eslint-disable-next-line testing-library/no-unnecessary-act
285
306
  await act( async () => {
@@ -308,11 +329,7 @@ describe( 'Cover block', () => {
308
329
 
309
330
  expect( overlay[ 0 ] ).toHaveClass( 'has-background-dim-100' );
310
331
 
311
- await userEvent.click(
312
- screen.getByRole( 'tab', {
313
- name: 'Styles',
314
- } )
315
- );
332
+ await openStylesTabIfAvailable();
316
333
 
317
334
  // Need act here as the isDark method is async.
318
335
  // eslint-disable-next-line testing-library/no-unnecessary-act
@@ -332,9 +349,7 @@ describe( 'Cover block', () => {
332
349
  test( 'does not render overlay control', async () => {
333
350
  await setup( undefined, true, disabledColorSettings );
334
351
  await selectBlock( 'Block: Cover' );
335
- await userEvent.click(
336
- screen.getByRole( 'tab', { name: 'Styles' } )
337
- );
352
+ await openStylesTabIfAvailable();
338
353
 
339
354
  const overlayControl = screen.queryByRole( 'button', {
340
355
  name: 'Overlay',
@@ -345,9 +360,7 @@ describe( 'Cover block', () => {
345
360
  test( 'does not render opacity control', async () => {
346
361
  await setup( undefined, true, disabledColorSettings );
347
362
  await selectBlock( 'Block: Cover' );
348
- await userEvent.click(
349
- screen.getByRole( 'tab', { name: 'Styles' } )
350
- );
363
+ await openStylesTabIfAvailable();
351
364
 
352
365
  const opacityControl = screen.queryByRole( 'slider', {
353
366
  name: 'Overlay opacity',
@@ -362,11 +375,7 @@ describe( 'Cover block', () => {
362
375
  test( 'sets minHeight attribute when number control value changed', async () => {
363
376
  await setup();
364
377
  await createAndSelectBlock();
365
- await userEvent.click(
366
- screen.getByRole( 'tab', {
367
- name: 'Styles',
368
- } )
369
- );
378
+ await openStylesTabIfAvailable();
370
379
  await userEvent.clear(
371
380
  screen.getByLabelText( 'Minimum height' )
372
381
  );
@@ -395,11 +404,7 @@ describe( 'Cover block', () => {
395
404
  expect( coverBlock ).toHaveClass( 'is-light' );
396
405
 
397
406
  await selectBlock( 'Block: Cover' );
398
- await userEvent.click(
399
- screen.getByRole( 'tab', {
400
- name: 'Styles',
401
- } )
402
- );
407
+ await openStylesTabIfAvailable();
403
408
  await userEvent.click( screen.getByText( 'Overlay' ) );
404
409
  const popupColorPicker = screen.getByRole( 'option', {
405
410
  name: 'Black',
@@ -416,11 +421,7 @@ describe( 'Cover block', () => {
416
421
  const coverBlock = screen.getByLabelText( 'Block: Cover' );
417
422
  expect( coverBlock ).toHaveClass( 'is-light' );
418
423
  await selectBlock( 'Block: Cover' );
419
- await userEvent.click(
420
- screen.getByRole( 'tab', {
421
- name: 'Styles',
422
- } )
423
- );
424
+ await openStylesTabIfAvailable();
424
425
  await userEvent.click( screen.getByText( 'Overlay' ) );
425
426
  // The default color is black, so clicking the black color button will remove the background color,
426
427
  // which should remove the isDark setting and assign the is-light class.
@@ -14,7 +14,11 @@ import { usePreferredColorSchemeStyle } from '@wordpress/compose';
14
14
  import { requestPreview } from '@wordpress/react-native-bridge';
15
15
  import { useSelect } from '@wordpress/data';
16
16
  import { store as editorStore } from '@wordpress/editor';
17
- import { BottomSheet, Icon, TextControl } from '@wordpress/components';
17
+ import {
18
+ BottomSheet,
19
+ Icon as WCIcon,
20
+ TextControl,
21
+ } from '@wordpress/components';
18
22
  import { help } from '@wordpress/icons';
19
23
  import { BlockIcon } from '@wordpress/block-editor';
20
24
 
@@ -150,7 +154,7 @@ const EmbedNoPreview = ( {
150
154
  onPress={ onPressHelp }
151
155
  style={ helpIconStyle }
152
156
  >
153
- <Icon
157
+ <WCIcon
154
158
  icon={ help }
155
159
  fill={ helpIconStyle.fill }
156
160
  size={ helpIconStyle.width }
@@ -167,7 +171,7 @@ const EmbedNoPreview = ( {
167
171
  >
168
172
  <View style={ styles[ 'embed-no-preview__container' ] }>
169
173
  <View style={ sheetIconStyle }>
170
- <Icon
174
+ <WCIcon
171
175
  icon={ help }
172
176
  fill={ sheetIconStyle.fill }
173
177
  size={ sheetIconStyle.width }
@@ -8,7 +8,7 @@ import { View, Text, TouchableOpacity } from 'react-native';
8
8
  */
9
9
  import { __ } from '@wordpress/i18n';
10
10
  import { usePreferredColorSchemeStyle } from '@wordpress/compose';
11
- import { Icon, Picker } from '@wordpress/components';
11
+ import { Icon as WCIcon, Picker } from '@wordpress/components';
12
12
  import { BlockIcon } from '@wordpress/block-editor';
13
13
  import { useRef } from '@wordpress/element';
14
14
 
@@ -110,7 +110,7 @@ const EmbedPlaceholder = ( {
110
110
  <View style={ containerStyle }>
111
111
  { cannotEmbed ? (
112
112
  <>
113
- <Icon
113
+ <WCIcon
114
114
  icon={ noticeOutline }
115
115
  fill={ embedIconErrorStyle.fill }
116
116
  style={ embedIconErrorStyle }
@@ -31,7 +31,7 @@ import {
31
31
  ToggleControl,
32
32
  TextControl,
33
33
  SelectControl,
34
- Icon,
34
+ Icon as WCIcon,
35
35
  } from '@wordpress/components';
36
36
  import {
37
37
  file as icon,
@@ -476,7 +476,7 @@ export class FileEdit extends Component {
476
476
  />
477
477
  { isUploadFailed && (
478
478
  <View style={ styles.errorContainer }>
479
- <Icon
479
+ <WCIcon
480
480
  icon={ cautionFilled }
481
481
  style={ errorIconStyle }
482
482
  />
@@ -44,7 +44,7 @@ export default function MigrationNotice( { content, onReplace } ) {
44
44
  return (
45
45
  <Warning actions={ actions }>
46
46
  { __(
47
- 'The Classic block is being phased out. Convert this content to blocks for the best editing experience, or move it to a Custom HTML block to preserve the markup as-is.'
47
+ 'The Classic block is being phased out. Convert this content to blocks for the best editing experience, or move it to a Custom HTML block to preserve the original markup.'
48
48
  ) }
49
49
  </Warning>
50
50
  );
@@ -21,20 +21,6 @@
21
21
  flex-basis: 100%;
22
22
  }
23
23
 
24
- .wp-block-image {
25
- .components-notice.is-error {
26
- display: block;
27
- }
28
- .components-notice__content {
29
- margin: 4px 0;
30
- }
31
- .components-notice__dismiss {
32
- position: absolute;
33
- top: 0;
34
- right: 5px;
35
- }
36
- }
37
-
38
24
  // @todo this deserves a refactor, by being moved to the toolbar.
39
25
  .block-editor-media-placeholder.is-appender {
40
26
  .components-placeholder__label {
@@ -11,6 +11,13 @@
11
11
  "label": {
12
12
  "type": "string",
13
13
  "role": "content"
14
+ },
15
+ "opensInNewTab": {
16
+ "type": "boolean",
17
+ "default": false
18
+ },
19
+ "description": {
20
+ "type": "string"
14
21
  }
15
22
  },
16
23
  "usesContext": [
@@ -6,21 +6,74 @@ import clsx from 'clsx';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { RichText, useBlockProps } from '@wordpress/block-editor';
9
+ import {
10
+ InspectorControls,
11
+ RichText,
12
+ useBlockProps,
13
+ store as blockEditorStore,
14
+ } from '@wordpress/block-editor';
15
+ import {
16
+ Button,
17
+ CheckboxControl,
18
+ TextControl,
19
+ TextareaControl,
20
+ __experimentalToolsPanel as ToolsPanel,
21
+ __experimentalToolsPanelItem as ToolsPanelItem,
22
+ } from '@wordpress/components';
10
23
  import { __ } from '@wordpress/i18n';
11
24
  import { useSelect } from '@wordpress/data';
12
25
  import { store as coreStore } from '@wordpress/core-data';
26
+ import { external } from '@wordpress/icons';
27
+ import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
28
+
29
+ /**
30
+ * Internal dependencies
31
+ */
32
+ import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
13
33
 
14
34
  const preventDefault = ( event ) => event.preventDefault();
15
35
 
16
36
  export default function HomeEdit( { attributes, setAttributes, context } ) {
17
- const homeUrl = useSelect( ( select ) => {
37
+ const {
38
+ homeUrl,
39
+ onNavigateToEntityRecord,
40
+ frontPageId,
41
+ frontPageTemplateId,
42
+ } = useSelect( ( select ) => {
43
+ const { getEntityRecord, getDefaultTemplateId, canUser } =
44
+ select( coreStore );
45
+
18
46
  // Site index.
19
- return select( coreStore ).getEntityRecord( 'root', '__unstableBase' )
20
- ?.home;
47
+ const baseUrl = getEntityRecord( 'root', '__unstableBase' )?.home;
48
+
49
+ // Front-page data (only available if the user can read site settings).
50
+ const canReadSettings = canUser( 'read', {
51
+ kind: 'root',
52
+ name: 'site',
53
+ } );
54
+ const site = canReadSettings ? getEntityRecord( 'root', 'site' ) : null;
55
+ const resolvedFrontPageId =
56
+ site?.show_on_front === 'page' ? site?.page_on_front : null;
57
+
58
+ // When no specific front page is set, fall back to the front-page template.
59
+ const resolvedFrontPageTemplateId = ! resolvedFrontPageId
60
+ ? getDefaultTemplateId( { slug: 'front-page' } )
61
+ : null;
62
+
63
+ return {
64
+ homeUrl: baseUrl,
65
+ onNavigateToEntityRecord:
66
+ select( blockEditorStore ).getSettings()
67
+ .onNavigateToEntityRecord,
68
+ frontPageId: resolvedFrontPageId,
69
+ frontPageTemplateId: resolvedFrontPageTemplateId,
70
+ };
21
71
  }, [] );
22
72
 
23
73
  const { textColor, backgroundColor, style } = context;
74
+ const { label, opensInNewTab, description } = attributes;
75
+ const dropdownMenuProps = useToolsPanelDropdownMenuProps();
76
+
24
77
  const blockProps = useBlockProps( {
25
78
  className: clsx( 'wp-block-navigation-item', {
26
79
  'has-text-color': !! textColor || !! style?.color?.text,
@@ -35,24 +88,134 @@ export default function HomeEdit( { attributes, setAttributes, context } ) {
35
88
  } );
36
89
 
37
90
  return (
38
- <div { ...blockProps }>
39
- <a
40
- className="wp-block-home-link__content wp-block-navigation-item__content"
41
- href={ homeUrl }
42
- onClick={ preventDefault }
43
- >
44
- <RichText
45
- identifier="label"
46
- className="wp-block-home-link__label"
47
- value={ attributes.label ?? __( 'Home' ) }
48
- onChange={ ( labelValue ) => {
49
- setAttributes( { label: labelValue } );
91
+ <>
92
+ <InspectorControls group="content">
93
+ <ToolsPanel
94
+ label={ __( 'Settings' ) }
95
+ resetAll={ () => {
96
+ setAttributes( {
97
+ label: '',
98
+ opensInNewTab: false,
99
+ description: '',
100
+ } );
50
101
  } }
51
- aria-label={ __( 'Home link text' ) }
52
- placeholder={ __( 'Add home link' ) }
53
- withoutInteractiveFormatting
54
- />
55
- </a>
56
- </div>
102
+ dropdownMenuProps={ dropdownMenuProps }
103
+ >
104
+ <ToolsPanelItem
105
+ hasValue={ () => !! label }
106
+ label={ __( 'Text' ) }
107
+ onDeselect={ () => setAttributes( { label: '' } ) }
108
+ isShownByDefault
109
+ >
110
+ <TextControl
111
+ __next40pxDefaultSize
112
+ label={ __( 'Text' ) }
113
+ value={ label ? stripHTML( label ) : '' }
114
+ onChange={ ( labelValue ) => {
115
+ setAttributes( { label: labelValue } );
116
+ } }
117
+ autoComplete="off"
118
+ />
119
+ </ToolsPanelItem>
120
+ <ToolsPanelItem
121
+ hasValue={ () => !! opensInNewTab }
122
+ label={ __( 'Open in new tab' ) }
123
+ onDeselect={ () =>
124
+ setAttributes( { opensInNewTab: false } )
125
+ }
126
+ isShownByDefault
127
+ >
128
+ <CheckboxControl
129
+ label={ __( 'Open in new tab' ) }
130
+ checked={ opensInNewTab }
131
+ onChange={ ( value ) =>
132
+ setAttributes( { opensInNewTab: value } )
133
+ }
134
+ />
135
+ </ToolsPanelItem>
136
+ { onNavigateToEntityRecord &&
137
+ ( frontPageId || frontPageTemplateId ) && (
138
+ <Button
139
+ variant="secondary"
140
+ onClick={ () => {
141
+ if ( frontPageId ) {
142
+ onNavigateToEntityRecord( {
143
+ postId: frontPageId,
144
+ postType: 'page',
145
+ } );
146
+ } else {
147
+ onNavigateToEntityRecord( {
148
+ postId: frontPageTemplateId,
149
+ postType: 'wp_template',
150
+ } );
151
+ }
152
+ } }
153
+ __next40pxDefaultSize
154
+ className="navigation-link-to__action-button"
155
+ >
156
+ { __( 'Edit' ) }
157
+ </Button>
158
+ ) }
159
+ { homeUrl && (
160
+ <Button
161
+ variant="secondary"
162
+ href={ homeUrl }
163
+ target="_blank"
164
+ icon={ external }
165
+ iconPosition="right"
166
+ __next40pxDefaultSize
167
+ className="navigation-link-to__action-button"
168
+ >
169
+ { __( 'View' ) }
170
+ </Button>
171
+ ) }
172
+ <ToolsPanelItem
173
+ hasValue={ () => !! description }
174
+ label={ __( 'Description' ) }
175
+ onDeselect={ () =>
176
+ setAttributes( { description: '' } )
177
+ }
178
+ isShownByDefault
179
+ >
180
+ <TextareaControl
181
+ label={ __( 'Description' ) }
182
+ value={ description || '' }
183
+ onChange={ ( descriptionValue ) => {
184
+ setAttributes( {
185
+ description: descriptionValue,
186
+ } );
187
+ } }
188
+ help={ __(
189
+ 'The description will be displayed in the menu if the current theme supports it.'
190
+ ) }
191
+ />
192
+ </ToolsPanelItem>
193
+ </ToolsPanel>
194
+ </InspectorControls>
195
+ <div { ...blockProps }>
196
+ <a
197
+ className="wp-block-home-link__content wp-block-navigation-item__content"
198
+ href={ homeUrl }
199
+ onClick={ preventDefault }
200
+ >
201
+ <RichText
202
+ identifier="label"
203
+ className="wp-block-home-link__label"
204
+ value={ label ?? __( 'Home' ) }
205
+ onChange={ ( labelValue ) => {
206
+ setAttributes( { label: labelValue } );
207
+ } }
208
+ aria-label={ __( 'Home link text' ) }
209
+ placeholder={ __( 'Add label…' ) }
210
+ withoutInteractiveFormatting
211
+ />
212
+ { description && (
213
+ <span className="wp-block-navigation-item__description">
214
+ { description }
215
+ </span>
216
+ ) }
217
+ </a>
218
+ </div>
219
+ </>
57
220
  );
58
221
  }
@@ -129,12 +129,24 @@ function render_block_core_home_link( $attributes, $content, $block ) {
129
129
  $aria_current = ' aria-current="page"';
130
130
  }
131
131
 
132
+ $target = '';
133
+ if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) {
134
+ $target = ' target="_blank"';
135
+ }
136
+
137
+ $description = '';
138
+ if ( ! empty( $attributes['description'] ) ) {
139
+ $description = '<span class="wp-block-navigation-item__description">' . wp_kses_post( $attributes['description'] ) . '</span>';
140
+ }
141
+
132
142
  return sprintf(
133
- '<li %1$s><a class="wp-block-home-link__content wp-block-navigation-item__content" href="%2$s" rel="home"%3$s>%4$s</a></li>',
143
+ '<li %1$s><a class="wp-block-home-link__content wp-block-navigation-item__content" href="%2$s" rel="home" %3$s%4$s>%5$s%6$s</a></li>',
134
144
  block_core_home_link_build_li_wrapper_attributes( $block->context ),
135
145
  esc_url( home_url() ),
146
+ $target,
136
147
  $aria_current,
137
- wp_kses_post( $attributes['label'] )
148
+ wp_kses_post( $attributes['label'] ),
149
+ $description
138
150
  );
139
151
  }
140
152
 
package/src/html/edit.js CHANGED
@@ -49,12 +49,13 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) {
49
49
  { __( 'Edit HTML' ) }
50
50
  </Button>
51
51
  </Placeholder>
52
- <HTMLEditModal
53
- isOpen={ isModalOpen }
54
- onRequestClose={ () => setIsModalOpen( false ) }
55
- content={ attributes.content }
56
- setAttributes={ setAttributes }
57
- />
52
+ { isModalOpen && (
53
+ <HTMLEditModal
54
+ onRequestClose={ () => setIsModalOpen( false ) }
55
+ content={ attributes.content }
56
+ setAttributes={ setAttributes }
57
+ />
58
+ ) }
58
59
  </div>
59
60
  );
60
61
  }
@@ -84,12 +85,13 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) {
84
85
  </VStack>
85
86
  </InspectorControls>
86
87
  <Preview content={ attributes.content } isSelected={ isSelected } />
87
- <HTMLEditModal
88
- isOpen={ isModalOpen }
89
- onRequestClose={ () => setIsModalOpen( false ) }
90
- content={ attributes.content }
91
- setAttributes={ setAttributes }
92
- />
88
+ { isModalOpen && (
89
+ <HTMLEditModal
90
+ onRequestClose={ () => setIsModalOpen( false ) }
91
+ content={ attributes.content }
92
+ setAttributes={ setAttributes }
93
+ />
94
+ ) }
93
95
  </div>
94
96
  );
95
97
  }
package/src/html/modal.js CHANGED
@@ -27,7 +27,6 @@ import { parseContent, serializeContent } from './utils';
27
27
  const { Tabs } = unlock( componentsPrivateApis );
28
28
 
29
29
  export default function HTMLEditModal( {
30
- isOpen,
31
30
  onRequestClose,
32
31
  content,
33
32
  setAttributes,
@@ -54,10 +53,6 @@ export default function HTMLEditModal( {
54
53
  const hasRestrictedContent =
55
54
  ! canUserUseUnfilteredHTML && ( css.trim() || js.trim() );
56
55
 
57
- if ( ! isOpen ) {
58
- return null;
59
- }
60
-
61
56
  const handleUpdate = () => {
62
57
  // For users without unfiltered_html capability, strip CSS and JS content
63
58
  // to prevent kses from leaving broken content
@@ -105,6 +105,10 @@
105
105
  "source": "attribute",
106
106
  "selector": "figure > a",
107
107
  "attribute": "target"
108
+ },
109
+ "isDecorative": {
110
+ "type": "boolean",
111
+ "default": false
108
112
  }
109
113
  },
110
114
  "supports": {