@wordpress/block-editor 12.19.2 → 12.19.4

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 (148) hide show
  1. package/build/components/block-edit/context.js +2 -1
  2. package/build/components/block-edit/context.js.map +1 -1
  3. package/build/components/block-edit/index.js +8 -3
  4. package/build/components/block-edit/index.js.map +1 -1
  5. package/build/components/block-inspector/index.js +5 -4
  6. package/build/components/block-inspector/index.js.map +1 -1
  7. package/build/components/block-preview/index.js +4 -0
  8. package/build/components/block-preview/index.js.map +1 -1
  9. package/build/components/block-tools/insertion-point.js +4 -1
  10. package/build/components/block-tools/insertion-point.js.map +1 -1
  11. package/build/components/global-styles/advanced-panel.js +5 -10
  12. package/build/components/global-styles/advanced-panel.js.map +1 -1
  13. package/build/components/global-styles/border-panel.js +21 -8
  14. package/build/components/global-styles/border-panel.js.map +1 -1
  15. package/build/components/global-styles/index.js +6 -0
  16. package/build/components/global-styles/index.js.map +1 -1
  17. package/build/components/global-styles/shadow-panel-components.js +80 -23
  18. package/build/components/global-styles/shadow-panel-components.js.map +1 -1
  19. package/build/components/inserter/media-tab/media-preview.js +1 -1
  20. package/build/components/inserter/media-tab/media-preview.js.map +1 -1
  21. package/build/components/inspector-controls-tabs/styles-tab.js +1 -1
  22. package/build/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  23. package/build/components/link-control/link-preview.js +4 -1
  24. package/build/components/link-control/link-preview.js.map +1 -1
  25. package/build/components/rich-text/index.js +52 -28
  26. package/build/components/rich-text/index.js.map +1 -1
  27. package/build/components/rich-text/use-enter.js +3 -0
  28. package/build/components/rich-text/use-enter.js.map +1 -1
  29. package/build/components/rich-text/use-paste-handler.js +25 -26
  30. package/build/components/rich-text/use-paste-handler.js.map +1 -1
  31. package/build/hooks/background.js +4 -2
  32. package/build/hooks/background.js.map +1 -1
  33. package/build/hooks/block-hooks.js +8 -6
  34. package/build/hooks/block-hooks.js.map +1 -1
  35. package/build/hooks/border.js +6 -4
  36. package/build/hooks/border.js.map +1 -1
  37. package/build/hooks/use-bindings-attributes.js +2 -4
  38. package/build/hooks/use-bindings-attributes.js.map +1 -1
  39. package/build/private-apis.js +5 -1
  40. package/build/private-apis.js.map +1 -1
  41. package/build/store/index.js +0 -2
  42. package/build/store/index.js.map +1 -1
  43. package/build/store/private-actions.js +0 -10
  44. package/build/store/private-actions.js.map +1 -1
  45. package/build/store/private-keys.js +8 -0
  46. package/build/store/private-keys.js.map +1 -0
  47. package/build/store/private-selectors.js +6 -17
  48. package/build/store/private-selectors.js.map +1 -1
  49. package/build/store/reducer.js +1 -24
  50. package/build/store/reducer.js.map +1 -1
  51. package/build/store/selectors.js +7 -12
  52. package/build/store/selectors.js.map +1 -1
  53. package/build/store/utils.js +7 -2
  54. package/build/store/utils.js.map +1 -1
  55. package/build-module/components/block-edit/context.js +1 -0
  56. package/build-module/components/block-edit/context.js.map +1 -1
  57. package/build-module/components/block-edit/index.js +9 -4
  58. package/build-module/components/block-edit/index.js.map +1 -1
  59. package/build-module/components/block-inspector/index.js +6 -5
  60. package/build-module/components/block-inspector/index.js.map +1 -1
  61. package/build-module/components/block-preview/index.js +4 -0
  62. package/build-module/components/block-preview/index.js.map +1 -1
  63. package/build-module/components/block-tools/insertion-point.js +4 -1
  64. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  65. package/build-module/components/global-styles/advanced-panel.js +6 -11
  66. package/build-module/components/global-styles/advanced-panel.js.map +1 -1
  67. package/build-module/components/global-styles/border-panel.js +22 -10
  68. package/build-module/components/global-styles/border-panel.js.map +1 -1
  69. package/build-module/components/global-styles/index.js +1 -1
  70. package/build-module/components/global-styles/index.js.map +1 -1
  71. package/build-module/components/global-styles/shadow-panel-components.js +82 -24
  72. package/build-module/components/global-styles/shadow-panel-components.js.map +1 -1
  73. package/build-module/components/inserter/media-tab/media-preview.js +1 -1
  74. package/build-module/components/inserter/media-tab/media-preview.js.map +1 -1
  75. package/build-module/components/inspector-controls-tabs/styles-tab.js +2 -2
  76. package/build-module/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  77. package/build-module/components/link-control/link-preview.js +5 -2
  78. package/build-module/components/link-control/link-preview.js.map +1 -1
  79. package/build-module/components/rich-text/index.js +52 -29
  80. package/build-module/components/rich-text/index.js.map +1 -1
  81. package/build-module/components/rich-text/use-enter.js +3 -0
  82. package/build-module/components/rich-text/use-enter.js.map +1 -1
  83. package/build-module/components/rich-text/use-paste-handler.js +25 -26
  84. package/build-module/components/rich-text/use-paste-handler.js.map +1 -1
  85. package/build-module/hooks/background.js +4 -2
  86. package/build-module/hooks/background.js.map +1 -1
  87. package/build-module/hooks/block-hooks.js +8 -6
  88. package/build-module/hooks/block-hooks.js.map +1 -1
  89. package/build-module/hooks/border.js +7 -5
  90. package/build-module/hooks/border.js.map +1 -1
  91. package/build-module/hooks/use-bindings-attributes.js +3 -5
  92. package/build-module/hooks/use-bindings-attributes.js.map +1 -1
  93. package/build-module/private-apis.js +5 -1
  94. package/build-module/private-apis.js.map +1 -1
  95. package/build-module/store/index.js +0 -2
  96. package/build-module/store/index.js.map +1 -1
  97. package/build-module/store/private-actions.js +0 -9
  98. package/build-module/store/private-actions.js.map +1 -1
  99. package/build-module/store/private-keys.js +2 -0
  100. package/build-module/store/private-keys.js.map +1 -0
  101. package/build-module/store/private-selectors.js +5 -12
  102. package/build-module/store/private-selectors.js.map +1 -1
  103. package/build-module/store/reducer.js +1 -24
  104. package/build-module/store/reducer.js.map +1 -1
  105. package/build-module/store/selectors.js +8 -13
  106. package/build-module/store/selectors.js.map +1 -1
  107. package/build-module/store/utils.js +6 -2
  108. package/build-module/store/utils.js.map +1 -1
  109. package/build-style/style-rtl.css +47 -24
  110. package/build-style/style.css +47 -24
  111. package/package.json +7 -7
  112. package/src/components/block-edit/context.js +1 -0
  113. package/src/components/block-edit/index.js +5 -1
  114. package/src/components/block-inspector/index.js +7 -5
  115. package/src/components/block-preview/index.js +6 -1
  116. package/src/components/block-toolbar/style.scss +11 -6
  117. package/src/components/block-tools/insertion-point.js +6 -1
  118. package/src/components/global-styles/advanced-panel.js +6 -12
  119. package/src/components/global-styles/border-panel.js +33 -22
  120. package/src/components/global-styles/index.js +5 -1
  121. package/src/components/global-styles/shadow-panel-components.js +92 -23
  122. package/src/components/global-styles/style.scss +33 -20
  123. package/src/components/inserter/media-tab/media-preview.js +6 -1
  124. package/src/components/inspector-controls-tabs/styles-tab.js +2 -2
  125. package/src/components/link-control/link-preview.js +9 -2
  126. package/src/components/link-control/style.scss +9 -0
  127. package/src/components/rich-text/index.js +74 -44
  128. package/src/components/rich-text/use-enter.js +4 -0
  129. package/src/components/rich-text/use-paste-handler.js +26 -25
  130. package/src/components/url-popover/style.scss +1 -0
  131. package/src/hooks/background.js +5 -2
  132. package/src/hooks/block-hooks.js +10 -5
  133. package/src/hooks/block-hooks.scss +6 -0
  134. package/src/hooks/border.js +16 -4
  135. package/src/hooks/use-bindings-attributes.js +5 -7
  136. package/src/private-apis.js +4 -0
  137. package/src/store/index.js +0 -2
  138. package/src/store/private-actions.js +0 -10
  139. package/src/store/private-keys.js +1 -0
  140. package/src/store/private-selectors.js +4 -15
  141. package/src/store/reducer.js +0 -25
  142. package/src/store/selectors.js +7 -15
  143. package/src/store/utils.js +7 -2
  144. package/build/store/resolvers.js +0 -27
  145. package/build/store/resolvers.js.map +0 -1
  146. package/build-module/store/resolvers.js +0 -20
  147. package/build-module/store/resolvers.js.map +0 -1
  148. package/src/store/resolvers.js +0 -17
@@ -5,28 +5,36 @@ import { __ } from '@wordpress/i18n';
5
5
  import {
6
6
  __experimentalVStack as VStack,
7
7
  __experimentalHeading as Heading,
8
- __experimentalGrid as Grid,
9
8
  __experimentalHStack as HStack,
10
9
  __experimentalDropdownContentWrapper as DropdownContentWrapper,
11
10
  Button,
12
11
  FlexItem,
13
12
  Dropdown,
13
+ privateApis as componentsPrivateApis,
14
14
  } from '@wordpress/components';
15
+ import { useMemo } from '@wordpress/element';
15
16
  import { shadow as shadowIcon, Icon, check } from '@wordpress/icons';
17
+
16
18
  /**
17
19
  * External dependencies
18
20
  */
19
21
  import classNames from 'classnames';
20
22
 
21
- export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) {
22
- const defaultShadows = settings?.shadow?.presets?.default;
23
- const themeShadows = settings?.shadow?.presets?.theme;
24
- const defaultPresetsEnabled = settings?.shadow?.defaultPresets;
23
+ /**
24
+ * Internal dependencies
25
+ */
26
+ import { unlock } from '../../lock-unlock';
25
27
 
26
- const shadows = [
27
- ...( defaultPresetsEnabled ? defaultShadows : [] ),
28
- ...( themeShadows || [] ),
29
- ];
28
+ /**
29
+ * Shared reference to an empty array for cases where it is important to avoid
30
+ * returning a new array reference on every invocation.
31
+ *
32
+ * @type {Array}
33
+ */
34
+ const EMPTY_ARRAY = [];
35
+
36
+ export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) {
37
+ const shadows = useShadowPresets( settings );
30
38
 
31
39
  return (
32
40
  <div className="block-editor-global-styles__shadow-popover-container">
@@ -37,42 +45,76 @@ export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) {
37
45
  activeShadow={ shadow }
38
46
  onSelect={ onShadowChange }
39
47
  />
48
+ <div className="block-editor-global-styles__clear-shadow">
49
+ <Button
50
+ variant="tertiary"
51
+ onClick={ () => onShadowChange( undefined ) }
52
+ >
53
+ { __( 'Clear' ) }
54
+ </Button>
55
+ </div>
40
56
  </VStack>
41
57
  </div>
42
58
  );
43
59
  }
44
60
 
45
61
  export function ShadowPresets( { presets, activeShadow, onSelect } ) {
62
+ const { CompositeV2: Composite, useCompositeStoreV2: useCompositeStore } =
63
+ unlock( componentsPrivateApis );
64
+ const compositeStore = useCompositeStore();
46
65
  return ! presets ? null : (
47
- <Grid columns={ 6 } gap={ 0 } align="center" justify="center">
66
+ <Composite
67
+ store={ compositeStore }
68
+ role="listbox"
69
+ className="block-editor-global-styles__shadow__list"
70
+ aria-label={ __( 'Drop shadows' ) }
71
+ >
48
72
  { presets.map( ( { name, slug, shadow } ) => (
49
73
  <ShadowIndicator
50
74
  key={ slug }
51
75
  label={ name }
52
76
  isActive={ shadow === activeShadow }
77
+ type={ slug === 'unset' ? 'unset' : 'preset' }
53
78
  onSelect={ () =>
54
79
  onSelect( shadow === activeShadow ? undefined : shadow )
55
80
  }
56
81
  shadow={ shadow }
57
82
  />
58
83
  ) ) }
59
- </Grid>
84
+ </Composite>
60
85
  );
61
86
  }
62
87
 
63
- export function ShadowIndicator( { label, isActive, onSelect, shadow } ) {
88
+ export function ShadowIndicator( { type, label, isActive, onSelect, shadow } ) {
89
+ const { CompositeItemV2: CompositeItem } = unlock( componentsPrivateApis );
64
90
  return (
65
- <div className="block-editor-global-styles__shadow-indicator-wrapper">
66
- <Button
67
- className="block-editor-global-styles__shadow-indicator"
68
- onClick={ onSelect }
69
- label={ label }
70
- style={ { boxShadow: shadow } }
71
- showTooltip
72
- >
73
- { isActive && <Icon icon={ check } /> }
74
- </Button>
75
- </div>
91
+ <CompositeItem
92
+ role="option"
93
+ aria-label={ label }
94
+ aria-selected={ isActive }
95
+ className={ classNames(
96
+ 'block-editor-global-styles__shadow__item',
97
+ {
98
+ 'is-active': isActive,
99
+ }
100
+ ) }
101
+ render={
102
+ <Button
103
+ className={ classNames(
104
+ 'block-editor-global-styles__shadow-indicator',
105
+ {
106
+ unset: type === 'unset',
107
+ }
108
+ ) }
109
+ onClick={ onSelect }
110
+ label={ label }
111
+ style={ { boxShadow: shadow } }
112
+ showTooltip
113
+ >
114
+ { isActive && <Icon icon={ check } /> }
115
+ </Button>
116
+ }
117
+ />
76
118
  );
77
119
  }
78
120
 
@@ -123,3 +165,30 @@ function renderShadowToggle() {
123
165
  );
124
166
  };
125
167
  }
168
+
169
+ export function useShadowPresets( settings ) {
170
+ return useMemo( () => {
171
+ if ( ! settings?.shadow ) {
172
+ return EMPTY_ARRAY;
173
+ }
174
+
175
+ const defaultPresetsEnabled = settings?.shadow?.defaultPresets;
176
+ const { default: defaultShadows, theme: themeShadows } =
177
+ settings?.shadow?.presets ?? {};
178
+ const unsetShadow = {
179
+ name: __( 'Unset' ),
180
+ slug: 'unset',
181
+ shadow: 'none',
182
+ };
183
+
184
+ const shadowPresets = [
185
+ ...( ( defaultPresetsEnabled && defaultShadows ) || EMPTY_ARRAY ),
186
+ ...( themeShadows || EMPTY_ARRAY ),
187
+ ];
188
+ if ( shadowPresets.length ) {
189
+ shadowPresets.unshift( unsetShadow );
190
+ }
191
+
192
+ return shadowPresets;
193
+ }, [ settings ] );
194
+ }
@@ -2,10 +2,24 @@
2
2
  fill: currentColor;
3
3
  }
4
4
 
5
+ // @todo: Ideally, popover, swatch size, and gap values should be CSS variables
6
+ // to apply precise grid layouts.
7
+ // https://github.com/WordPress/gutenberg/blob/954ecae571abbddc113d3a4bd8e1a72230180554/packages/block-editor/src/components/duotone-control/style.scss#L3-L9
5
8
  .block-editor-global-styles__shadow-popover-container {
6
9
  width: 230px;
7
10
  }
8
11
 
12
+ .block-editor-global-styles__shadow__list {
13
+ display: flex;
14
+ gap: 12px;
15
+ flex-wrap: wrap;
16
+ padding-bottom: $grid-unit-10;
17
+ }
18
+
19
+ .block-editor-global-styles__clear-shadow {
20
+ text-align: right;
21
+ }
22
+
9
23
  .block-editor-global-styles-filters-panel__dropdown,
10
24
  .block-editor-global-styles__shadow-dropdown {
11
25
  display: block;
@@ -21,14 +35,6 @@
21
35
  }
22
36
  }
23
37
 
24
- // wrapper to clip the shadow beyond 6px
25
- .block-editor-global-styles__shadow-indicator-wrapper {
26
- padding: $grid-unit-15 * 0.5;
27
- display: flex;
28
- align-items: center;
29
- justify-content: center;
30
- }
31
-
32
38
  // These styles are similar to the color palette.
33
39
  .block-editor-global-styles__shadow-indicator {
34
40
  color: $gray-800;
@@ -37,8 +43,25 @@
37
43
  cursor: pointer;
38
44
  padding: 0;
39
45
 
40
- height: $button-size-small;
41
- width: $button-size-small;
46
+ height: $button-size-small + 2 * $border-width;
47
+ width: $button-size-small + 2 * $border-width;
48
+ box-sizing: border-box;
49
+
50
+ transform: scale(1);
51
+ transition: transform 0.1s ease;
52
+ will-change: transform;
53
+
54
+ &:focus {
55
+ border: #{ $border-width * 2 } solid $gray-700;
56
+ }
57
+
58
+ &:hover {
59
+ transform: scale(1.2);
60
+ }
61
+
62
+ &.unset {
63
+ background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
64
+ }
42
65
  }
43
66
 
44
67
  .block-editor-global-styles-advanced-panel__custom-css-input textarea {
@@ -47,13 +70,3 @@
47
70
  /*rtl:ignore*/
48
71
  direction: ltr;
49
72
  }
50
-
51
- .block-editor-global-styles-advanced-panel__custom-css-validation-wrapper {
52
- position: absolute;
53
- bottom: $grid-unit-20;
54
- right: $grid-unit * 3;
55
- }
56
-
57
- .block-editor-global-styles-advanced-panel__custom-css-validation-icon {
58
- fill: $alert-red;
59
- }
@@ -195,7 +195,12 @@ export function MediaPreview( { media, onClick, category } ) {
195
195
  createSuccessNotice,
196
196
  ]
197
197
  );
198
- const title = media.title?.rendered || media.title;
198
+
199
+ const title =
200
+ typeof media.title === 'string'
201
+ ? media.title
202
+ : media.title?.rendered || __( 'no title' );
203
+
199
204
  let truncatedTitle;
200
205
  if ( title.length > MAXIMUM_TITLE_LENGTH ) {
201
206
  const omission = '...';
@@ -11,10 +11,10 @@ import { __ } from '@wordpress/i18n';
11
11
  import BlockStyles from '../block-styles';
12
12
  import DefaultStylePicker from '../default-style-picker';
13
13
  import InspectorControls from '../inspector-controls';
14
- import { getBorderPanelLabel } from '../../hooks/border';
14
+ import { useBorderPanelLabel } from '../../hooks/border';
15
15
 
16
16
  const StylesTab = ( { blockName, clientId, hasBlockStyles } ) => {
17
- const borderPanelLabel = getBorderPanelLabel( { blockName } );
17
+ const borderPanelLabel = useBorderPanelLabel( { blockName } );
18
18
 
19
19
  return (
20
20
  <>
@@ -16,8 +16,9 @@ import { useCopyToClipboard } from '@wordpress/compose';
16
16
  import { filterURLForDisplay, safeDecodeURI } from '@wordpress/url';
17
17
  import { Icon, globe, info, linkOff, edit, copySmall } from '@wordpress/icons';
18
18
  import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
19
- import { useDispatch } from '@wordpress/data';
19
+ import { useDispatch, useSelect } from '@wordpress/data';
20
20
  import { store as noticesStore } from '@wordpress/notices';
21
+ import { store as preferencesStore } from '@wordpress/preferences';
21
22
 
22
23
  /**
23
24
  * Internal dependencies
@@ -33,6 +34,12 @@ export default function LinkPreview( {
33
34
  hasUnlinkControl = false,
34
35
  onRemove,
35
36
  } ) {
37
+ const showIconLabels = useSelect(
38
+ ( select ) =>
39
+ select( preferencesStore ).get( 'core', 'showIconLabels' ),
40
+ []
41
+ );
42
+
36
43
  // Avoid fetching if rich previews are not desired.
37
44
  const showRichPreviews = hasRichPreviews ? value?.url : null;
38
45
 
@@ -139,7 +146,7 @@ export default function LinkPreview( {
139
146
  label={ sprintf(
140
147
  // Translators: %s is a placeholder for the link URL and an optional colon, (if a Link URL is present).
141
148
  __( 'Copy link%s' ), // Ends up looking like "Copy link: https://example.com".
142
- isEmptyURL ? '' : ': ' + value.url
149
+ isEmptyURL || showIconLabels ? '' : ': ' + value.url
143
150
  ) }
144
151
  ref={ ref }
145
152
  disabled={ isEmptyURL }
@@ -34,6 +34,15 @@ $block-editor-link-control-number-of-actions: 1;
34
34
  content: attr(aria-label);
35
35
  }
36
36
  }
37
+
38
+ .block-editor-link-control__search-item-top {
39
+ gap: $grid-unit-10;
40
+
41
+ .components-button.has-icon {
42
+ min-width: inherit;
43
+ width: min-content;
44
+ }
45
+ }
37
46
  }
38
47
  }
39
48
 
@@ -19,13 +19,14 @@ import {
19
19
  removeFormat,
20
20
  } from '@wordpress/rich-text';
21
21
  import { Popover } from '@wordpress/components';
22
- import { getBlockType } from '@wordpress/blocks';
22
+ import { getBlockType, store as blocksStore } from '@wordpress/blocks';
23
23
 
24
24
  /**
25
25
  * Internal dependencies
26
26
  */
27
27
  import { useBlockEditorAutocompleteProps } from '../autocomplete';
28
28
  import { useBlockEditContext } from '../block-edit';
29
+ import { blockBindingsKey } from '../block-edit/context';
29
30
  import FormatToolbarContainer from './format-toolbar-container';
30
31
  import { store as blockEditorStore } from '../../store';
31
32
  import { useUndoAutomaticChange } from './use-undo-automatic-change';
@@ -109,6 +110,7 @@ export function RichTextWrapper(
109
110
  __unstableDisableFormats: disableFormats,
110
111
  disableLineBreaks,
111
112
  __unstableAllowPrefixTransformations,
113
+ disableEditing,
112
114
  ...props
113
115
  },
114
116
  forwardedRef
@@ -116,11 +118,9 @@ export function RichTextWrapper(
116
118
  props = removeNativeProps( props );
117
119
 
118
120
  const anchorRef = useRef();
119
- const {
120
- clientId,
121
- isSelected: isBlockSelected,
122
- name: blockName,
123
- } = useBlockEditContext();
121
+ const context = useBlockEditContext();
122
+ const { clientId, isSelected: isBlockSelected, name: blockName } = context;
123
+ const blockBindings = context[ blockBindingsKey ];
124
124
  const selector = ( select ) => {
125
125
  // Avoid subscribing to the block editor store if the block is not
126
126
  // selected.
@@ -128,12 +128,10 @@ export function RichTextWrapper(
128
128
  return { isSelected: false };
129
129
  }
130
130
 
131
- const { getSelectionStart, getSelectionEnd, getBlockAttributes } =
131
+ const { getSelectionStart, getSelectionEnd } =
132
132
  select( blockEditorStore );
133
133
  const selectionStart = getSelectionStart();
134
134
  const selectionEnd = getSelectionEnd();
135
- const blockBindings =
136
- getBlockAttributes( clientId )?.metadata?.bindings;
137
135
 
138
136
  let isSelected;
139
137
 
@@ -146,43 +144,60 @@ export function RichTextWrapper(
146
144
  isSelected = selectionStart.clientId === clientId;
147
145
  }
148
146
 
149
- // Disable Rich Text editing if block bindings specify that.
150
- let shouldDisableEditing = false;
151
- if ( blockBindings && blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS ) {
152
- const blockTypeAttributes = getBlockType( blockName ).attributes;
153
- const { getBlockBindingsSource } = unlock(
154
- select( blockEditorStore )
155
- );
156
- for ( const [ attribute, args ] of Object.entries(
157
- blockBindings
158
- ) ) {
159
- // If any of the attributes with source "rich-text" is part of the bindings,
160
- // has a source with `lockAttributesEditing`, disable it.
161
- if (
162
- blockTypeAttributes?.[ attribute ]?.source ===
163
- 'rich-text' &&
164
- getBlockBindingsSource( args.source )?.lockAttributesEditing
165
- ) {
166
- shouldDisableEditing = true;
167
- break;
168
- }
169
- }
170
- }
171
-
172
147
  return {
173
148
  selectionStart: isSelected ? selectionStart.offset : undefined,
174
149
  selectionEnd: isSelected ? selectionEnd.offset : undefined,
175
150
  isSelected,
176
- shouldDisableEditing,
177
151
  };
178
152
  };
179
- const { selectionStart, selectionEnd, isSelected, shouldDisableEditing } =
180
- useSelect( selector, [
181
- clientId,
182
- identifier,
183
- originalIsSelected,
184
- isBlockSelected,
185
- ] );
153
+ const { selectionStart, selectionEnd, isSelected } = useSelect( selector, [
154
+ clientId,
155
+ identifier,
156
+ originalIsSelected,
157
+ isBlockSelected,
158
+ ] );
159
+
160
+ const disableBoundBlocks = useSelect(
161
+ ( select ) => {
162
+ // Disable Rich Text editing if block bindings specify that.
163
+ let _disableBoundBlocks = false;
164
+ if ( blockBindings && blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS ) {
165
+ const blockTypeAttributes =
166
+ getBlockType( blockName ).attributes;
167
+ const { getBlockBindingsSource } = unlock(
168
+ select( blocksStore )
169
+ );
170
+ for ( const [ attribute, args ] of Object.entries(
171
+ blockBindings
172
+ ) ) {
173
+ if (
174
+ blockTypeAttributes?.[ attribute ]?.source !==
175
+ 'rich-text'
176
+ ) {
177
+ break;
178
+ }
179
+
180
+ // If the source is not defined, or if its value of `lockAttributesEditing` is `true`, disable it.
181
+ const blockBindingsSource = getBlockBindingsSource(
182
+ args.source
183
+ );
184
+ if (
185
+ ! blockBindingsSource ||
186
+ blockBindingsSource.lockAttributesEditing
187
+ ) {
188
+ _disableBoundBlocks = true;
189
+ break;
190
+ }
191
+ }
192
+ }
193
+
194
+ return _disableBoundBlocks;
195
+ },
196
+ [ blockBindings, blockName ]
197
+ );
198
+
199
+ const shouldDisableEditing = disableEditing || disableBoundBlocks;
200
+
186
201
  const { getSelectionStart, getSelectionEnd, getBlockRootClientId } =
187
202
  useSelect( blockEditorStore );
188
203
  const { selectionChange } = useDispatch( blockEditorStore );
@@ -435,19 +450,34 @@ export function RichTextWrapper(
435
450
  );
436
451
  }
437
452
 
438
- const ForwardedRichTextContainer = withDeprecations(
453
+ // This is the private API for the RichText component.
454
+ // It allows access to all props, not just the public ones.
455
+ export const PrivateRichText = withDeprecations(
439
456
  forwardRef( RichTextWrapper )
440
457
  );
441
458
 
442
- ForwardedRichTextContainer.Content = Content;
443
- ForwardedRichTextContainer.isEmpty = ( value ) => {
459
+ PrivateRichText.Content = Content;
460
+ PrivateRichText.isEmpty = ( value ) => {
444
461
  return ! value || value.length === 0;
445
462
  };
446
463
 
464
+ // This is the public API for the RichText component.
465
+ // We wrap the PrivateRichText component to hide some props from the public API.
447
466
  /**
448
467
  * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/rich-text/README.md
449
468
  */
450
- export default ForwardedRichTextContainer;
469
+ const PublicForwardedRichTextContainer = forwardRef( ( props, ref ) => {
470
+ return (
471
+ <PrivateRichText ref={ ref } { ...props } disableEditing={ false } />
472
+ );
473
+ } );
474
+
475
+ PublicForwardedRichTextContainer.Content = Content;
476
+ PublicForwardedRichTextContainer.isEmpty = ( value ) => {
477
+ return ! value || value.length === 0;
478
+ };
479
+
480
+ export default PublicForwardedRichTextContainer;
451
481
  export { RichTextShortcut } from './shortcut';
452
482
  export { RichTextToolbarButton } from './toolbar-button';
453
483
  export { __unstableRichTextInputEvent } from './input-event';
@@ -21,6 +21,10 @@ export function useEnter( props ) {
21
21
  propsRef.current = props;
22
22
  return useRefEffect( ( element ) => {
23
23
  function onKeyDown( event ) {
24
+ if ( event.target.contentEditable !== 'true' ) {
25
+ return;
26
+ }
27
+
24
28
  if ( event.defaultPrevented ) {
25
29
  return;
26
30
  }
@@ -58,13 +58,35 @@ export function usePasteHandler( props ) {
58
58
  const isInternal =
59
59
  event.clipboardData.getData( 'rich-text' ) === 'true';
60
60
 
61
+ function pasteInline( content ) {
62
+ const transformed = formatTypes.reduce(
63
+ ( accumulator, { __unstablePasteRule } ) => {
64
+ // Only allow one transform.
65
+ if ( __unstablePasteRule && accumulator === value ) {
66
+ accumulator = __unstablePasteRule( value, {
67
+ html,
68
+ plainText,
69
+ } );
70
+ }
71
+
72
+ return accumulator;
73
+ },
74
+ value
75
+ );
76
+ if ( transformed !== value ) {
77
+ onChange( transformed );
78
+ } else {
79
+ const valueToInsert = create( { html: content } );
80
+ addActiveFormats( valueToInsert, value.activeFormats );
81
+ onChange( insert( value, valueToInsert ) );
82
+ }
83
+ }
84
+
61
85
  // If the data comes from a rich text instance, we can directly use it
62
86
  // without filtering the data. The filters are only meant for externally
63
87
  // pasted content and remove inline styles.
64
88
  if ( isInternal ) {
65
- const pastedValue = create( { html } );
66
- addActiveFormats( pastedValue, value.activeFormats );
67
- onChange( insert( value, pastedValue ) );
89
+ pasteInline( html );
68
90
  return;
69
91
  }
70
92
 
@@ -135,28 +157,7 @@ export function usePasteHandler( props ) {
135
157
  } );
136
158
 
137
159
  if ( typeof content === 'string' ) {
138
- const transformed = formatTypes.reduce(
139
- ( accumlator, { __unstablePasteRule } ) => {
140
- // Only allow one transform.
141
- if ( __unstablePasteRule && accumlator === value ) {
142
- accumlator = __unstablePasteRule( value, {
143
- html,
144
- plainText,
145
- } );
146
- }
147
-
148
- return accumlator;
149
- },
150
- value
151
- );
152
-
153
- if ( transformed !== value ) {
154
- onChange( transformed );
155
- } else {
156
- const valueToInsert = create( { html: content } );
157
- addActiveFormats( valueToInsert, value.activeFormats );
158
- onChange( insert( value, valueToInsert ) );
159
- }
160
+ pasteInline( content );
160
161
  } else if ( content.length > 0 ) {
161
162
  if ( onReplace && isEmpty( value ) ) {
162
163
  onReplace( content, content.length - 1, -1 );
@@ -58,6 +58,7 @@
58
58
  text-overflow: ellipsis;
59
59
  white-space: nowrap;
60
60
  margin-right: $grid-unit-10;
61
+ min-width: 150px;
61
62
  // Avoids the popover from growing too wide when the URL is long.
62
63
  // See https://github.com/WordPress/gutenberg/issues/58599
63
64
  max-width: $modal-min-width;
@@ -374,11 +374,14 @@ function backgroundSizeHelpText( value ) {
374
374
  }
375
375
 
376
376
  export const coordsToBackgroundPosition = ( value ) => {
377
- if ( ! value || isNaN( value.x ) || isNaN( value.y ) ) {
377
+ if ( ! value || ( isNaN( value.x ) && isNaN( value.y ) ) ) {
378
378
  return undefined;
379
379
  }
380
380
 
381
- return `${ value.x * 100 }% ${ value.y * 100 }%`;
381
+ const x = isNaN( value.x ) ? 0.5 : value.x;
382
+ const y = isNaN( value.y ) ? 0.5 : value.y;
383
+
384
+ return `${ x * 100 }% ${ y * 100 }%`;
382
385
  };
383
386
 
384
387
  export const backgroundPositionToCoords = ( value ) => {
@@ -35,12 +35,12 @@ function BlockHooksControlPure( { name, clientId } ) {
35
35
 
36
36
  const { blockIndex, rootClientId, innerBlocksLength } = useSelect(
37
37
  ( select ) => {
38
- const { getBlock, getBlockIndex, getBlockRootClientId } =
38
+ const { getBlocks, getBlockIndex, getBlockRootClientId } =
39
39
  select( blockEditorStore );
40
40
 
41
41
  return {
42
42
  blockIndex: getBlockIndex( clientId ),
43
- innerBlocksLength: getBlock( clientId )?.innerBlocks?.length,
43
+ innerBlocksLength: getBlocks( clientId )?.length,
44
44
  rootClientId: getBlockRootClientId( clientId ),
45
45
  };
46
46
  },
@@ -49,7 +49,7 @@ function BlockHooksControlPure( { name, clientId } ) {
49
49
 
50
50
  const hookedBlockClientIds = useSelect(
51
51
  ( select ) => {
52
- const { getBlock, getGlobalBlockCount } =
52
+ const { getBlocks, getGlobalBlockCount } =
53
53
  select( blockEditorStore );
54
54
 
55
55
  const _hookedBlockClientIds = hookedBlocksForCurrentBlock.reduce(
@@ -69,7 +69,7 @@ function BlockHooksControlPure( { name, clientId } ) {
69
69
  // Any of the current block's siblings (with the right block type) qualifies
70
70
  // as a hooked block (inserted `before` or `after` the current one), as the block
71
71
  // might've been automatically inserted and then moved around a bit by the user.
72
- candidates = getBlock( rootClientId )?.innerBlocks;
72
+ candidates = getBlocks( rootClientId );
73
73
  break;
74
74
 
75
75
  case 'first_child':
@@ -77,7 +77,7 @@ function BlockHooksControlPure( { name, clientId } ) {
77
77
  // Any of the current block's child blocks (with the right block type) qualifies
78
78
  // as a hooked first or last child block, as the block might've been automatically
79
79
  // inserted and then moved around a bit by the user.
80
- candidates = getBlock( clientId ).innerBlocks;
80
+ candidates = getBlocks( clientId );
81
81
  break;
82
82
  }
83
83
 
@@ -161,6 +161,11 @@ function BlockHooksControlPure( { name, clientId } ) {
161
161
  title={ __( 'Plugins' ) }
162
162
  initialOpen={ true }
163
163
  >
164
+ <p className="block-editor-hooks__block-hooks-helptext">
165
+ { __(
166
+ 'Manage the inclusion of blocks added automatically by plugins.'
167
+ ) }
168
+ </p>
164
169
  { Object.keys( groupedHookedBlocks ).map( ( vendor ) => {
165
170
  return (
166
171
  <Fragment key={ vendor }>
@@ -13,4 +13,10 @@
13
13
  .components-toggle-control .components-h-stack .components-h-stack {
14
14
  flex-direction: row;
15
15
  }
16
+
17
+ .block-editor-hooks__block-hooks-helptext {
18
+ color: $gray-700;
19
+ font-size: $helptext-font-size;
20
+ margin-bottom: $grid-unit-20;
21
+ }
16
22
  }