@wordpress/block-editor 8.3.1 → 8.4.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 (174) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +1 -0
  3. package/build/components/block-list/block-html.js +4 -1
  4. package/build/components/block-list/block-html.js.map +1 -1
  5. package/build/components/block-list/block.js +4 -1
  6. package/build/components/block-list/block.js.map +1 -1
  7. package/build/components/block-list/use-block-props/use-focus-first-element.js +19 -0
  8. package/build/components/block-list/use-block-props/use-focus-first-element.js.map +1 -1
  9. package/build/components/block-lock/index.js +32 -0
  10. package/build/components/block-lock/index.js.map +1 -0
  11. package/build/components/block-lock/menu-item.js +58 -0
  12. package/build/components/block-lock/menu-item.js.map +1 -0
  13. package/build/components/block-lock/modal.js +143 -0
  14. package/build/components/block-lock/modal.js.map +1 -0
  15. package/build/components/block-lock/toolbar.js +70 -0
  16. package/build/components/block-lock/toolbar.js.map +1 -0
  17. package/build/components/block-settings-menu/block-settings-dropdown.js +26 -6
  18. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  19. package/build/components/block-settings-menu-controls/index.js +19 -9
  20. package/build/components/block-settings-menu-controls/index.js.map +1 -1
  21. package/build/components/block-title/use-block-display-title.js +7 -5
  22. package/build/components/block-title/use-block-display-title.js.map +1 -1
  23. package/build/components/block-toolbar/index.js +4 -0
  24. package/build/components/block-toolbar/index.js.map +1 -1
  25. package/build/components/border-radius-control/index.js +0 -1
  26. package/build/components/border-radius-control/index.js.map +1 -1
  27. package/build/components/border-radius-control/utils.js +1 -1
  28. package/build/components/border-radius-control/utils.js.map +1 -1
  29. package/build/components/colors-gradients/control.js +3 -1
  30. package/build/components/colors-gradients/control.js.map +1 -1
  31. package/build/components/date-format-picker/index.js +132 -0
  32. package/build/components/date-format-picker/index.js.map +1 -0
  33. package/build/components/index.js +9 -0
  34. package/build/components/index.js.map +1 -1
  35. package/build/components/line-height-control/index.js +5 -3
  36. package/build/components/line-height-control/index.js.map +1 -1
  37. package/build/components/list-view/block-select-button.js +4 -22
  38. package/build/components/list-view/block-select-button.js.map +1 -1
  39. package/build/components/list-view/block.js +33 -12
  40. package/build/components/list-view/block.js.map +1 -1
  41. package/build/components/list-view/branch.js +16 -13
  42. package/build/components/list-view/branch.js.map +1 -1
  43. package/build/components/list-view/index.js +7 -1
  44. package/build/components/list-view/index.js.map +1 -1
  45. package/build/components/list-view/use-block-selection.js +9 -2
  46. package/build/components/list-view/use-block-selection.js.map +1 -1
  47. package/build/components/rich-text/index.js +2 -2
  48. package/build/components/rich-text/index.js.map +1 -1
  49. package/build/components/rich-text/index.native.js +13 -9
  50. package/build/components/rich-text/index.native.js.map +1 -1
  51. package/build/components/url-popover/image-url-input-ui.js +11 -27
  52. package/build/components/url-popover/image-url-input-ui.js.map +1 -1
  53. package/build/hooks/anchor.js +7 -6
  54. package/build/hooks/anchor.js.map +1 -1
  55. package/build/hooks/gap.js +70 -5
  56. package/build/hooks/gap.js.map +1 -1
  57. package/build/layouts/flex.js +8 -5
  58. package/build/layouts/flex.js.map +1 -1
  59. package/build/layouts/flow.js +16 -12
  60. package/build/layouts/flow.js.map +1 -1
  61. package/build/store/defaults.js +1 -0
  62. package/build/store/defaults.js.map +1 -1
  63. package/build/store/selectors.js +29 -3
  64. package/build/store/selectors.js.map +1 -1
  65. package/build-module/components/block-list/block-html.js +5 -2
  66. package/build-module/components/block-list/block-html.js.map +1 -1
  67. package/build-module/components/block-list/block.js +5 -2
  68. package/build-module/components/block-list/block.js.map +1 -1
  69. package/build-module/components/block-list/use-block-props/use-focus-first-element.js +18 -0
  70. package/build-module/components/block-list/use-block-props/use-focus-first-element.js.map +1 -1
  71. package/build-module/components/block-lock/index.js +4 -0
  72. package/build-module/components/block-lock/index.js.map +1 -0
  73. package/build-module/components/block-lock/menu-item.js +44 -0
  74. package/build-module/components/block-lock/menu-item.js.map +1 -0
  75. package/build-module/components/block-lock/modal.js +128 -0
  76. package/build-module/components/block-lock/modal.js.map +1 -0
  77. package/build-module/components/block-lock/toolbar.js +55 -0
  78. package/build-module/components/block-lock/toolbar.js.map +1 -0
  79. package/build-module/components/block-settings-menu/block-settings-dropdown.js +26 -6
  80. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  81. package/build-module/components/block-settings-menu-controls/index.js +18 -9
  82. package/build-module/components/block-settings-menu-controls/index.js.map +1 -1
  83. package/build-module/components/block-title/use-block-display-title.js +7 -5
  84. package/build-module/components/block-title/use-block-display-title.js.map +1 -1
  85. package/build-module/components/block-toolbar/index.js +3 -0
  86. package/build-module/components/block-toolbar/index.js.map +1 -1
  87. package/build-module/components/border-radius-control/index.js +0 -1
  88. package/build-module/components/border-radius-control/index.js.map +1 -1
  89. package/build-module/components/border-radius-control/utils.js +1 -1
  90. package/build-module/components/border-radius-control/utils.js.map +1 -1
  91. package/build-module/components/colors-gradients/control.js +3 -1
  92. package/build-module/components/colors-gradients/control.js.map +1 -1
  93. package/build-module/components/date-format-picker/index.js +122 -0
  94. package/build-module/components/date-format-picker/index.js.map +1 -0
  95. package/build-module/components/index.js +1 -0
  96. package/build-module/components/index.js.map +1 -1
  97. package/build-module/components/line-height-control/index.js +5 -3
  98. package/build-module/components/line-height-control/index.js.map +1 -1
  99. package/build-module/components/list-view/block-select-button.js +5 -20
  100. package/build-module/components/list-view/block-select-button.js.map +1 -1
  101. package/build-module/components/list-view/block.js +31 -12
  102. package/build-module/components/list-view/block.js.map +1 -1
  103. package/build-module/components/list-view/branch.js +16 -13
  104. package/build-module/components/list-view/branch.js.map +1 -1
  105. package/build-module/components/list-view/index.js +7 -1
  106. package/build-module/components/list-view/index.js.map +1 -1
  107. package/build-module/components/list-view/use-block-selection.js +10 -3
  108. package/build-module/components/list-view/use-block-selection.js.map +1 -1
  109. package/build-module/components/rich-text/index.js +2 -2
  110. package/build-module/components/rich-text/index.js.map +1 -1
  111. package/build-module/components/rich-text/index.native.js +13 -9
  112. package/build-module/components/rich-text/index.native.js.map +1 -1
  113. package/build-module/components/url-popover/image-url-input-ui.js +12 -28
  114. package/build-module/components/url-popover/image-url-input-ui.js.map +1 -1
  115. package/build-module/hooks/anchor.js +7 -6
  116. package/build-module/hooks/anchor.js.map +1 -1
  117. package/build-module/hooks/gap.js +68 -7
  118. package/build-module/hooks/gap.js.map +1 -1
  119. package/build-module/layouts/flex.js +7 -5
  120. package/build-module/layouts/flex.js.map +1 -1
  121. package/build-module/layouts/flow.js +15 -12
  122. package/build-module/layouts/flow.js.map +1 -1
  123. package/build-module/store/defaults.js +1 -0
  124. package/build-module/store/defaults.js.map +1 -1
  125. package/build-module/store/selectors.js +24 -1
  126. package/build-module/store/selectors.js.map +1 -1
  127. package/build-style/style-rtl.css +157 -0
  128. package/build-style/style.css +157 -0
  129. package/package.json +28 -27
  130. package/src/components/block-list/block-html.js +8 -4
  131. package/src/components/block-list/block.js +5 -1
  132. package/src/components/block-list/use-block-props/use-focus-first-element.js +28 -0
  133. package/src/components/block-lock/index.js +3 -0
  134. package/src/components/block-lock/menu-item.js +52 -0
  135. package/src/components/block-lock/modal.js +165 -0
  136. package/src/components/block-lock/style.scss +67 -0
  137. package/src/components/block-lock/toolbar.js +58 -0
  138. package/src/components/block-settings-menu/block-settings-dropdown.js +47 -5
  139. package/src/components/block-settings-menu-controls/index.js +33 -12
  140. package/src/components/block-title/README.md +6 -1
  141. package/src/components/block-title/test/index.js +43 -1
  142. package/src/components/block-title/use-block-display-title.js +9 -6
  143. package/src/components/block-toolbar/index.js +6 -0
  144. package/src/components/block-toolbar/style.scss +4 -0
  145. package/src/components/block-tools/style.scss +29 -0
  146. package/src/components/border-radius-control/index.js +0 -1
  147. package/src/components/border-radius-control/test/utils.js +4 -0
  148. package/src/components/border-radius-control/utils.js +2 -1
  149. package/src/components/color-palette/test/__snapshots__/control.js.snap +70 -4
  150. package/src/components/colors-gradients/control.js +1 -1
  151. package/src/components/colors-gradients/style.scss +6 -0
  152. package/src/components/date-format-picker/README.md +58 -0
  153. package/src/components/date-format-picker/index.js +161 -0
  154. package/src/components/date-format-picker/style.scss +31 -0
  155. package/src/components/index.js +1 -0
  156. package/src/components/line-height-control/index.js +3 -3
  157. package/src/components/link-control/README.md +1 -1
  158. package/src/components/list-view/block-select-button.js +2 -29
  159. package/src/components/list-view/block.js +47 -12
  160. package/src/components/list-view/branch.js +37 -15
  161. package/src/components/list-view/index.js +6 -0
  162. package/src/components/list-view/use-block-selection.js +15 -2
  163. package/src/components/rich-text/index.js +1 -1
  164. package/src/components/rich-text/index.native.js +24 -8
  165. package/src/components/url-popover/image-url-input-ui.js +16 -29
  166. package/src/hooks/anchor.js +8 -6
  167. package/src/hooks/gap.js +83 -12
  168. package/src/hooks/test/gap.js +42 -0
  169. package/src/layouts/flex.js +6 -3
  170. package/src/layouts/flow.js +16 -11
  171. package/src/store/defaults.js +1 -0
  172. package/src/store/selectors.js +26 -1
  173. package/src/store/test/selectors.js +63 -0
  174. package/src/style.scss +2 -0
@@ -23,39 +23,59 @@ import { isClientIdSelected } from './utils';
23
23
  * When a block is collapsed, we do not count their children as part of that total. In the current drag
24
24
  * implementation dragged blocks and their children are not counted.
25
25
  *
26
- * @param {Object} block block tree
27
- * @param {Object} expandedState state that notes which branches are collapsed
28
- * @param {Array} draggedClientIds a list of dragged client ids
26
+ * @param {Object} block block tree
27
+ * @param {Object} expandedState state that notes which branches are collapsed
28
+ * @param {Array} draggedClientIds a list of dragged client ids
29
+ * @param {boolean} isExpandedByDefault flag to determine the default fallback expanded state.
29
30
  * @return {number} block count
30
31
  */
31
- function countBlocks( block, expandedState, draggedClientIds ) {
32
+ function countBlocks(
33
+ block,
34
+ expandedState,
35
+ draggedClientIds,
36
+ isExpandedByDefault
37
+ ) {
32
38
  const isDragged = draggedClientIds?.includes( block.clientId );
33
39
  if ( isDragged ) {
34
40
  return 0;
35
41
  }
36
- const isExpanded = expandedState[ block.clientId ] ?? true;
42
+ const isExpanded = expandedState[ block.clientId ] ?? isExpandedByDefault;
43
+
37
44
  if ( isExpanded ) {
38
45
  return (
39
46
  1 +
40
47
  block.innerBlocks.reduce(
41
- countReducer( expandedState, draggedClientIds ),
48
+ countReducer(
49
+ expandedState,
50
+ draggedClientIds,
51
+ isExpandedByDefault
52
+ ),
42
53
  0
43
54
  )
44
55
  );
45
56
  }
46
57
  return 1;
47
58
  }
48
- const countReducer = ( expandedState, draggedClientIds ) => (
49
- count,
50
- block
51
- ) => {
59
+ const countReducer = (
60
+ expandedState,
61
+ draggedClientIds,
62
+ isExpandedByDefault
63
+ ) => ( count, block ) => {
52
64
  const isDragged = draggedClientIds?.includes( block.clientId );
53
65
  if ( isDragged ) {
54
66
  return count;
55
67
  }
56
- const isExpanded = expandedState[ block.clientId ] ?? true;
68
+ const isExpanded = expandedState[ block.clientId ] ?? isExpandedByDefault;
57
69
  if ( isExpanded && block.innerBlocks.length > 0 ) {
58
- return count + countBlocks( block, expandedState, draggedClientIds );
70
+ return (
71
+ count +
72
+ countBlocks(
73
+ block,
74
+ expandedState,
75
+ draggedClientIds,
76
+ isExpandedByDefault
77
+ )
78
+ );
59
79
  }
60
80
  return count + 1;
61
81
  };
@@ -72,6 +92,7 @@ function ListViewBranch( props ) {
72
92
  isBranchSelected = false,
73
93
  listPosition = 0,
74
94
  fixedListWindow,
95
+ expandNested = true,
75
96
  } = props;
76
97
 
77
98
  const {
@@ -93,14 +114,14 @@ function ListViewBranch( props ) {
93
114
  nextPosition += countBlocks(
94
115
  filteredBlocks[ index - 1 ],
95
116
  expandedState,
96
- draggedClientIds
117
+ draggedClientIds,
118
+ expandNested
97
119
  );
98
120
  }
99
121
 
100
122
  const usesWindowing = __experimentalPersistentListViewFeatures;
101
123
 
102
124
  const { itemInView } = fixedListWindow;
103
-
104
125
  const blockInView =
105
126
  ! usesWindowing || itemInView( nextPosition );
106
127
 
@@ -113,7 +134,7 @@ function ListViewBranch( props ) {
113
134
  showNestedBlocks && !! innerBlocks && !! innerBlocks.length;
114
135
 
115
136
  const isExpanded = hasNestedBlocks
116
- ? expandedState[ clientId ] ?? true
137
+ ? expandedState[ clientId ] ?? expandNested
117
138
  : undefined;
118
139
 
119
140
  const isDragged = !! draggedClientIds?.includes( clientId );
@@ -165,6 +186,7 @@ function ListViewBranch( props ) {
165
186
  fixedListWindow={ fixedListWindow }
166
187
  isBranchSelected={ isSelectedBranch }
167
188
  selectedClientIds={ selectedClientIds }
189
+ expandNested={ expandNested }
168
190
  />
169
191
  ) }
170
192
  </AsyncModeProvider>
@@ -59,6 +59,8 @@ export const BLOCK_LIST_ITEM_HEIGHT = 36;
59
59
  * @param {boolean} props.__experimentalFeatures Flag to enable experimental features.
60
60
  * @param {boolean} props.__experimentalPersistentListViewFeatures Flag to enable features for the Persistent List View experiment.
61
61
  * @param {boolean} props.__experimentalHideContainerBlockActions Flag to hide actions of top level blocks (like core/widget-area)
62
+ * @param {string} props.id Unique identifier for the root list element (primarily for a11y purposes).
63
+ * @param {boolean} props.expandNested Flag to determine whether nested levels are expanded by default.
62
64
  * @param {Object} ref Forwarded ref
63
65
  */
64
66
  function ListView(
@@ -69,6 +71,8 @@ function ListView(
69
71
  __experimentalHideContainerBlockActions,
70
72
  showNestedBlocks,
71
73
  showBlockMovers,
74
+ id,
75
+ expandNested = false,
72
76
  ...props
73
77
  },
74
78
  ref
@@ -205,6 +209,7 @@ function ListView(
205
209
  blockDropTarget={ blockDropTarget }
206
210
  />
207
211
  <TreeGrid
212
+ id={ id }
208
213
  className="block-editor-list-view-tree"
209
214
  aria-label={ __( 'Block navigation structure' ) }
210
215
  ref={ treeGridRef }
@@ -220,6 +225,7 @@ function ListView(
220
225
  showBlockMovers={ showBlockMovers }
221
226
  fixedListWindow={ fixedListWindow }
222
227
  selectedClientIds={ selectedClientIds }
228
+ expandNested={ expandNested }
223
229
  { ...props }
224
230
  />
225
231
  </ListViewContext.Provider>
@@ -10,7 +10,7 @@ import { speak } from '@wordpress/a11y';
10
10
  import { __, sprintf } from '@wordpress/i18n';
11
11
  import { useDispatch, useSelect } from '@wordpress/data';
12
12
  import { useCallback } from '@wordpress/element';
13
- import { UP, DOWN } from '@wordpress/keycodes';
13
+ import { UP, DOWN, HOME, END } from '@wordpress/keycodes';
14
14
  import { store as blocksStore } from '@wordpress/blocks';
15
15
 
16
16
  /**
@@ -49,7 +49,10 @@ export default function useBlockSelection() {
49
49
 
50
50
  const isKeyPress =
51
51
  event.type === 'keydown' &&
52
- ( event.keyCode === UP || event.keyCode === DOWN );
52
+ ( event.keyCode === UP ||
53
+ event.keyCode === DOWN ||
54
+ event.keyCode === HOME ||
55
+ event.keyCode === END );
53
56
 
54
57
  // Handle clicking on a block when no blocks are selected, and return early.
55
58
  if (
@@ -114,6 +117,16 @@ export default function useBlockSelection() {
114
117
  // the total number of blocks deselected is greater than one.
115
118
  const updatedSelectedBlocks = getSelectedBlockClientIds();
116
119
 
120
+ // If the selection is greater than 1 and the Home or End keys
121
+ // were used to generate the selection, then skip announcing the
122
+ // deselected blocks.
123
+ if (
124
+ ( event.keyCode === HOME || event.keyCode === END ) &&
125
+ updatedSelectedBlocks.length > 1
126
+ ) {
127
+ return;
128
+ }
129
+
117
130
  const selectionDiff = difference(
118
131
  selectedBlocks,
119
132
  updatedSelectedBlocks
@@ -330,6 +330,7 @@ function RichTextWrapper(
330
330
  { ...props }
331
331
  { ...autocompleteProps }
332
332
  ref={ useMergeRefs( [
333
+ forwardedRef,
333
334
  autocompleteProps.ref,
334
335
  props.ref,
335
336
  richTextRef,
@@ -371,7 +372,6 @@ function RichTextWrapper(
371
372
  onSplitAtEnd,
372
373
  } ),
373
374
  anchorRef,
374
- forwardedRef,
375
375
  ] ) }
376
376
  contentEditable={ true }
377
377
  suppressContentEditableWarning={ true }
@@ -66,6 +66,8 @@ function RichTextWrapper(
66
66
  {
67
67
  children,
68
68
  tagName,
69
+ start,
70
+ reversed,
69
71
  value: originalValue,
70
72
  onChange: originalOnChange,
71
73
  isSelected: originalIsSelected,
@@ -214,8 +216,13 @@ function RichTextWrapper(
214
216
  }
215
217
 
216
218
  const onSelectionChange = useCallback(
217
- ( start, end ) => {
218
- selectionChange( clientId, identifier, start, end );
219
+ ( selectionChangeStart, selectionChangeEnd ) => {
220
+ selectionChange(
221
+ clientId,
222
+ identifier,
223
+ selectionChangeStart,
224
+ selectionChangeEnd
225
+ );
219
226
  },
220
227
  [ clientId, identifier ]
221
228
  );
@@ -349,9 +356,11 @@ function RichTextWrapper(
349
356
  onChange( insertLineSeparator( value ) );
350
357
  }
351
358
  } else {
352
- const { text, start, end } = value;
359
+ const { text, start: splitStart, end: splitEnd } = value;
353
360
  const canSplitAtEnd =
354
- onSplitAtEnd && start === end && end === text.length;
361
+ onSplitAtEnd &&
362
+ splitStart === splitEnd &&
363
+ splitEnd === text.length;
355
364
 
356
365
  if ( shiftKey || ( ! canSplit && ! canSplitAtEnd ) ) {
357
366
  if ( ! disableLineBreaks ) {
@@ -530,15 +539,18 @@ function RichTextWrapper(
530
539
  return;
531
540
  }
532
541
 
533
- const { start, text } = value;
534
- const characterBefore = text.slice( start - 1, start );
542
+ const { start: startPosition, text } = value;
543
+ const characterBefore = text.slice(
544
+ startPosition - 1,
545
+ startPosition
546
+ );
535
547
 
536
548
  // The character right before the caret must be a plain space.
537
549
  if ( characterBefore !== ' ' ) {
538
550
  return;
539
551
  }
540
552
 
541
- const trimmedTextBefore = text.slice( 0, start ).trim();
553
+ const trimmedTextBefore = text.slice( 0, startPosition ).trim();
542
554
  const prefixTransforms = getBlockTransforms( 'from' ).filter(
543
555
  ( { type } ) => type === 'prefix'
544
556
  );
@@ -553,7 +565,9 @@ function RichTextWrapper(
553
565
  return;
554
566
  }
555
567
 
556
- const content = valueToFormat( slice( value, start, text.length ) );
568
+ const content = valueToFormat(
569
+ slice( value, startPosition, text.length )
570
+ );
557
571
  const block = transformation.transform( content );
558
572
 
559
573
  onReplace( [ block ] );
@@ -575,6 +589,8 @@ function RichTextWrapper(
575
589
  selectionEnd={ selectionEnd }
576
590
  onSelectionChange={ onSelectionChange }
577
591
  tagName={ tagName }
592
+ start={ start }
593
+ reversed={ reversed }
578
594
  placeholder={ placeholder }
579
595
  allowedFormats={ adjustedAllowedFormats }
580
596
  withoutInteractiveFormatting={ withoutInteractiveFormatting }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { find, isEmpty, each, map } from 'lodash';
4
+ import { find, map } from 'lodash';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -80,38 +80,25 @@ const ImageURLInputUI = ( {
80
80
  setIsOpen( false );
81
81
  } );
82
82
 
83
- const removeNewTabRel = ( currentRel ) => {
84
- let newRel = currentRel;
85
-
86
- if ( currentRel !== undefined && ! isEmpty( newRel ) ) {
87
- if ( ! isEmpty( newRel ) ) {
88
- each( NEW_TAB_REL, ( relVal ) => {
89
- const regExp = new RegExp( '\\b' + relVal + '\\b', 'gi' );
90
- newRel = newRel.replace( regExp, '' );
91
- } );
92
-
93
- // Only trim if NEW_TAB_REL values was replaced.
94
- if ( newRel !== currentRel ) {
95
- newRel = newRel.trim();
96
- }
97
-
98
- if ( isEmpty( newRel ) ) {
99
- newRel = undefined;
100
- }
101
- }
102
- }
103
-
104
- return newRel;
105
- };
106
-
107
83
  const getUpdatedLinkTargetSettings = ( value ) => {
108
84
  const newLinkTarget = value ? '_blank' : undefined;
109
85
 
110
86
  let updatedRel;
111
- if ( ! newLinkTarget && ! rel ) {
112
- updatedRel = undefined;
87
+ if ( newLinkTarget ) {
88
+ const rels = ( rel ?? '' ).split( ' ' );
89
+ NEW_TAB_REL.forEach( ( relVal ) => {
90
+ if ( ! rels.includes( relVal ) ) {
91
+ rels.push( relVal );
92
+ }
93
+ } );
94
+ updatedRel = rels.join( ' ' );
113
95
  } else {
114
- updatedRel = removeNewTabRel( rel );
96
+ const rels = ( rel ?? '' )
97
+ .split( ' ' )
98
+ .filter(
99
+ ( relVal ) => NEW_TAB_REL.includes( relVal ) === false
100
+ );
101
+ updatedRel = rels.length ? rels.join( ' ' ) : undefined;
115
102
  }
116
103
 
117
104
  return {
@@ -232,7 +219,7 @@ const ImageURLInputUI = ( {
232
219
  />
233
220
  <TextControl
234
221
  label={ __( 'Link Rel' ) }
235
- value={ removeNewTabRel( rel ) || '' }
222
+ value={ rel ?? '' }
236
223
  onChange={ onSetLinkRel }
237
224
  />
238
225
  <TextControl
@@ -25,6 +25,13 @@ import { InspectorControls } from '../components';
25
25
  */
26
26
  const ANCHOR_REGEX = /[\s#]/g;
27
27
 
28
+ const ANCHOR_SCHEMA = {
29
+ type: 'string',
30
+ source: 'attribute',
31
+ attribute: 'id',
32
+ selector: '*',
33
+ };
34
+
28
35
  /**
29
36
  * Filters registered block settings, extending attributes with anchor using ID
30
37
  * of the first node.
@@ -42,12 +49,7 @@ export function addAttribute( settings ) {
42
49
  // Gracefully handle if settings.attributes is undefined.
43
50
  settings.attributes = {
44
51
  ...settings.attributes,
45
- anchor: {
46
- type: 'string',
47
- source: 'attribute',
48
- attribute: 'id',
49
- selector: '*',
50
- },
52
+ anchor: ANCHOR_SCHEMA,
51
53
  };
52
54
  }
53
55
 
package/src/hooks/gap.js CHANGED
@@ -6,6 +6,7 @@ import { Platform } from '@wordpress/element';
6
6
  import { getBlockSupport } from '@wordpress/blocks';
7
7
  import {
8
8
  __experimentalUseCustomUnits as useCustomUnits,
9
+ __experimentalBoxControl as BoxControl,
9
10
  __experimentalUnitControl as UnitControl,
10
11
  } from '@wordpress/components';
11
12
 
@@ -14,14 +15,14 @@ import {
14
15
  */
15
16
  import { __unstableUseBlockRef as useBlockRef } from '../components/block-list/use-block-props/use-block-refs';
16
17
  import useSetting from '../components/use-setting';
17
- import { SPACING_SUPPORT_KEY } from './dimensions';
18
+ import { AXIAL_SIDES, SPACING_SUPPORT_KEY, useCustomSides } from './dimensions';
18
19
  import { cleanEmptyObject } from './utils';
19
20
 
20
21
  /**
21
22
  * Determines if there is gap support.
22
23
  *
23
24
  * @param {string|Object} blockType Block name or Block Type object.
24
- * @return {boolean} Whether there is support.
25
+ * @return {boolean} Whether there is support.
25
26
  */
26
27
  export function hasGapSupport( blockType ) {
27
28
  const support = getBlockSupport( blockType, SPACING_SUPPORT_KEY );
@@ -38,6 +39,45 @@ export function hasGapValue( props ) {
38
39
  return props.attributes.style?.spacing?.blockGap !== undefined;
39
40
  }
40
41
 
42
+ /**
43
+ * Returns a BoxControl object value from a given blockGap style.
44
+ * The string check is for backwards compatibility before Gutenberg supported
45
+ * split gap values (row and column) and the value was a string n + unit.
46
+ *
47
+ * @param {string? | Object?} rawBlockGapValue A style object.
48
+ * @return {Object?} A value to pass to the BoxControl component.
49
+ */
50
+ export function getGapValueFromStyle( rawBlockGapValue ) {
51
+ if ( ! rawBlockGapValue ) {
52
+ return rawBlockGapValue;
53
+ }
54
+
55
+ const isValueString = typeof rawBlockGapValue === 'string';
56
+ return {
57
+ top: isValueString ? rawBlockGapValue : rawBlockGapValue?.top,
58
+ left: isValueString ? rawBlockGapValue : rawBlockGapValue?.left,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Returns a CSS value for the `gap` property from a given blockGap style.
64
+ *
65
+ * @param {string? | Object?} blockGapValue A style object.
66
+ * @param {string?} defaultValue A default gap value.
67
+ * @return {string|null} The concatenated gap value (row and column).
68
+ */
69
+ export function getGapCSSValue( blockGapValue, defaultValue = '0' ) {
70
+ const blockGapBoxControlValue = getGapValueFromStyle( blockGapValue );
71
+ if ( ! blockGapBoxControlValue ) {
72
+ return null;
73
+ }
74
+
75
+ const row = blockGapBoxControlValue?.top || defaultValue;
76
+ const column = blockGapBoxControlValue?.left || defaultValue;
77
+
78
+ return row === column ? row : `${ row } ${ column }`;
79
+ }
80
+
41
81
  /**
42
82
  * Resets the gap block support attribute. This can be used when disabling
43
83
  * the gap support controls for a block via a progressive discovery panel.
@@ -82,6 +122,7 @@ export function GapEdit( props ) {
82
122
  const {
83
123
  clientId,
84
124
  attributes: { style },
125
+ name: blockName,
85
126
  setAttributes,
86
127
  } = props;
87
128
 
@@ -94,7 +135,7 @@ export function GapEdit( props ) {
94
135
  'vw',
95
136
  ],
96
137
  } );
97
-
138
+ const sides = useCustomSides( blockName, 'blockGap' );
98
139
  const ref = useBlockRef( clientId );
99
140
 
100
141
  if ( useIsGapDisabled( props ) ) {
@@ -106,7 +147,9 @@ export function GapEdit( props ) {
106
147
  ...style,
107
148
  spacing: {
108
149
  ...style?.spacing,
109
- blockGap: next,
150
+ blockGap: {
151
+ ...getGapValueFromStyle( next ),
152
+ },
110
153
  },
111
154
  };
112
155
 
@@ -128,17 +171,45 @@ export function GapEdit( props ) {
128
171
  }
129
172
  };
130
173
 
174
+ const splitOnAxis =
175
+ sides && sides.some( ( side ) => AXIAL_SIDES.includes( side ) );
176
+ const gapValue = getGapValueFromStyle( style?.spacing?.blockGap );
177
+
178
+ // The BoxControl component expects a full complement of side values.
179
+ // Gap row and column values translate to top/bottom and left/right respectively.
180
+ const boxControlGapValue = splitOnAxis
181
+ ? {
182
+ ...gapValue,
183
+ right: gapValue?.left,
184
+ bottom: gapValue?.top,
185
+ }
186
+ : gapValue?.top;
187
+
131
188
  return Platform.select( {
132
189
  web: (
133
190
  <>
134
- <UnitControl
135
- label={ __( 'Block spacing' ) }
136
- __unstableInputWidth="80px"
137
- min={ 0 }
138
- onChange={ onChange }
139
- units={ units }
140
- value={ style?.spacing?.blockGap }
141
- />
191
+ { splitOnAxis ? (
192
+ <BoxControl
193
+ label={ __( 'Block spacing' ) }
194
+ min={ 0 }
195
+ onChange={ onChange }
196
+ units={ units }
197
+ sides={ sides }
198
+ values={ boxControlGapValue }
199
+ allowReset={ false }
200
+ splitOnAxis={ splitOnAxis }
201
+ />
202
+ ) : (
203
+ <UnitControl
204
+ label={ __( 'Block spacing' ) }
205
+ __unstableInputWidth="80px"
206
+ min={ 0 }
207
+ onChange={ onChange }
208
+ units={ units }
209
+ // Default to `row` for combined values.
210
+ value={ boxControlGapValue }
211
+ />
212
+ ) }
142
213
  </>
143
214
  ),
144
215
  native: null,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getGapCSSValue } from '../gap';
5
+
6
+ describe( 'gap', () => {
7
+ describe( 'getGapCSSValue()', () => {
8
+ it( 'should return `null` if argument is falsey', () => {
9
+ expect( getGapCSSValue( undefined ) ).toBeNull();
10
+ expect( getGapCSSValue( '' ) ).toBeNull();
11
+ } );
12
+
13
+ it( 'should return single value for gap if argument is valid string', () => {
14
+ expect( getGapCSSValue( '88rem' ) ).toEqual( '88rem' );
15
+ } );
16
+
17
+ it( 'should return single value for gap if row and column are the same', () => {
18
+ const blockGapValue = {
19
+ top: '88rem',
20
+ left: '88rem',
21
+ };
22
+ expect( getGapCSSValue( blockGapValue ) ).toEqual( '88rem' );
23
+ } );
24
+
25
+ it( 'should return shorthand value for gap if row and column are different', () => {
26
+ const blockGapValue = {
27
+ top: '88px',
28
+ left: '88rem',
29
+ };
30
+ expect( getGapCSSValue( blockGapValue ) ).toEqual( '88px 88rem' );
31
+ } );
32
+
33
+ it( 'should return default value if a top or left is missing', () => {
34
+ const blockGapValue = {
35
+ top: '88px',
36
+ };
37
+ expect( getGapCSSValue( blockGapValue, '1px' ) ).toEqual(
38
+ '88px 1px'
39
+ );
40
+ } );
41
+ } );
42
+ } );
@@ -16,6 +16,7 @@ import { Button, ToggleControl, Flex, FlexItem } from '@wordpress/components';
16
16
  * Internal dependencies
17
17
  */
18
18
  import { appendSelectors } from './utils';
19
+ import { getGapCSSValue } from '../hooks/gap';
19
20
  import useSetting from '../components/use-setting';
20
21
  import { BlockControls, JustifyContentControl } from '../components';
21
22
 
@@ -42,8 +43,9 @@ export default {
42
43
  inspectorControls: function FlexLayoutInspectorControls( {
43
44
  layout = {},
44
45
  onChange,
46
+ layoutBlockSupport = {},
45
47
  } ) {
46
- const { allowOrientation = true } = layout;
48
+ const { allowOrientation = true } = layoutBlockSupport;
47
49
  return (
48
50
  <>
49
51
  <Flex>
@@ -89,7 +91,8 @@ export default {
89
91
  const blockGapSupport = useSetting( 'spacing.blockGap' );
90
92
  const hasBlockGapStylesSupport = blockGapSupport !== null;
91
93
  const blockGapValue =
92
- style?.spacing?.blockGap ?? 'var( --wp--style--block-gap, 0.5em )';
94
+ getGapCSSValue( style?.spacing?.blockGap, '0.5em' ) ??
95
+ 'var( --wp--style--block-gap, 0.5em )';
93
96
  const justifyContent =
94
97
  justifyContentMap[ layout.justifyContent ] ||
95
98
  justifyContentMap.left;
@@ -112,8 +115,8 @@ export default {
112
115
  <style>{ `
113
116
  ${ appendSelectors( selector ) } {
114
117
  display: flex;
115
- gap: ${ hasBlockGapStylesSupport ? blockGapValue : '0.5em' };
116
118
  flex-wrap: ${ flexWrap };
119
+ gap: ${ hasBlockGapStylesSupport ? blockGapValue : '0.5em' };
117
120
  ${ orientation === 'horizontal' ? rowOrientation : columnOrientation }
118
121
  }
119
122
 
@@ -14,6 +14,7 @@ import { Icon, positionCenter, stretchWide } from '@wordpress/icons';
14
14
  */
15
15
  import useSetting from '../components/use-setting';
16
16
  import { appendSelectors } from './utils';
17
+ import { getGapValueFromStyle } from '../hooks/gap';
17
18
 
18
19
  export default {
19
20
  name: 'default',
@@ -109,8 +110,11 @@ export default {
109
110
  const { contentSize, wideSize } = layout;
110
111
  const blockGapSupport = useSetting( 'spacing.blockGap' );
111
112
  const hasBlockGapStylesSupport = blockGapSupport !== null;
113
+ const blockGapStyleValue = getGapValueFromStyle(
114
+ style?.spacing?.blockGap
115
+ );
112
116
  const blockGapValue =
113
- style?.spacing?.blockGap ?? 'var( --wp--style--block-gap )';
117
+ blockGapStyleValue?.top ?? 'var( --wp--style--block-gap )';
114
118
 
115
119
  let output =
116
120
  !! contentSize || !! wideSize
@@ -123,11 +127,9 @@ export default {
123
127
  margin-left: auto !important;
124
128
  margin-right: auto !important;
125
129
  }
126
-
127
130
  ${ appendSelectors( selector, '> .alignwide' ) } {
128
131
  max-width: ${ wideSize ?? contentSize };
129
132
  }
130
-
131
133
  ${ appendSelectors( selector, '> .alignfull' ) } {
132
134
  max-width: none;
133
135
  }
@@ -137,26 +139,29 @@ export default {
137
139
  output += `
138
140
  ${ appendSelectors( selector, '> .alignleft' ) } {
139
141
  float: left;
140
- margin-right: 2em;
141
- margin-left: 0;
142
+ margin-inline-start: 0;
143
+ margin-inline-end: 2em;
142
144
  }
143
-
144
145
  ${ appendSelectors( selector, '> .alignright' ) } {
145
146
  float: right;
146
- margin-left: 2em;
147
- margin-right: 0;
147
+ margin-inline-start: 2em;
148
+ margin-inline-end: 0;
148
149
  }
149
150
 
151
+ ${ appendSelectors( selector, '> .aligncenter' ) } {
152
+ margin-left: auto !important;
153
+ margin-right: auto !important;
154
+ }
150
155
  `;
151
156
 
152
157
  if ( hasBlockGapStylesSupport ) {
153
158
  output += `
154
159
  ${ appendSelectors( selector, '> *' ) } {
155
- margin-top: 0;
156
- margin-bottom: 0;
160
+ margin-block-start: 0;
161
+ margin-block-end: 0;
157
162
  }
158
163
  ${ appendSelectors( selector, '> * + *' ) } {
159
- margin-top: ${ blockGapValue };
164
+ margin-block-start: ${ blockGapValue };
160
165
  }
161
166
  `;
162
167
  }
@@ -28,6 +28,7 @@ export const PREFERENCES_DEFAULTS = {
28
28
  * @property {boolean} __experimentalBlockDirectory Whether the user has enabled the Block Directory
29
29
  * @property {Array} __experimentalBlockPatterns Array of objects representing the block patterns
30
30
  * @property {Array} __experimentalBlockPatternCategories Array of objects representing the block pattern categories
31
+ * @property {boolean} __experimentalGenerateAnchors Enable/Disable auto anchor generation for Heading blocks
31
32
  * @property {boolean} __unstableGalleryWithImageBlocks Whether the user has enabled the refactored gallery block which uses InnerBlocks
32
33
  */
33
34
  export const SETTINGS_DEFAULTS = {