@wordpress/block-editor 14.17.0 → 14.18.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 (86) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-canvas/index.js +6 -3
  3. package/build/components/block-canvas/index.js.map +1 -1
  4. package/build/components/block-settings-menu/block-mode-toggle.js +3 -3
  5. package/build/components/block-settings-menu/block-mode-toggle.js.map +1 -1
  6. package/build/components/block-toolbar/use-has-block-toolbar.js +3 -12
  7. package/build/components/block-toolbar/use-has-block-toolbar.js.map +1 -1
  8. package/build/components/child-layout-control/index.js +1 -0
  9. package/build/components/child-layout-control/index.js.map +1 -1
  10. package/build/components/html-element-control/index.js +107 -0
  11. package/build/components/html-element-control/index.js.map +1 -0
  12. package/build/components/html-element-control/messages.js +25 -0
  13. package/build/components/html-element-control/messages.js.map +1 -0
  14. package/build/components/keyboard-shortcuts/index.js +2 -2
  15. package/build/components/keyboard-shortcuts/index.js.map +1 -1
  16. package/build/components/media-placeholder/index.js +2 -1
  17. package/build/components/media-placeholder/index.js.map +1 -1
  18. package/build/components/rich-text/event-listeners/delete.js +1 -9
  19. package/build/components/rich-text/event-listeners/delete.js.map +1 -1
  20. package/build/components/use-resize-canvas/index.js +1 -1
  21. package/build/components/use-resize-canvas/index.js.map +1 -1
  22. package/build/hooks/duotone.js +1 -1
  23. package/build/hooks/duotone.js.map +1 -1
  24. package/build/hooks/typography.js +1 -1
  25. package/build/hooks/typography.js.map +1 -1
  26. package/build/private-apis.js +2 -0
  27. package/build/private-apis.js.map +1 -1
  28. package/build/private-apis.native.js +4 -1
  29. package/build/private-apis.native.js.map +1 -1
  30. package/build/store/actions.js +1 -1
  31. package/build/store/actions.js.map +1 -1
  32. package/build-module/components/block-canvas/index.js +6 -3
  33. package/build-module/components/block-canvas/index.js.map +1 -1
  34. package/build-module/components/block-settings-menu/block-mode-toggle.js +3 -3
  35. package/build-module/components/block-settings-menu/block-mode-toggle.js.map +1 -1
  36. package/build-module/components/block-toolbar/use-has-block-toolbar.js +3 -12
  37. package/build-module/components/block-toolbar/use-has-block-toolbar.js.map +1 -1
  38. package/build-module/components/child-layout-control/index.js +1 -0
  39. package/build-module/components/child-layout-control/index.js.map +1 -1
  40. package/build-module/components/html-element-control/index.js +102 -0
  41. package/build-module/components/html-element-control/index.js.map +1 -0
  42. package/build-module/components/html-element-control/messages.js +19 -0
  43. package/build-module/components/html-element-control/messages.js.map +1 -0
  44. package/build-module/components/keyboard-shortcuts/index.js +2 -2
  45. package/build-module/components/keyboard-shortcuts/index.js.map +1 -1
  46. package/build-module/components/media-placeholder/index.js +2 -1
  47. package/build-module/components/media-placeholder/index.js.map +1 -1
  48. package/build-module/components/rich-text/event-listeners/delete.js +1 -9
  49. package/build-module/components/rich-text/event-listeners/delete.js.map +1 -1
  50. package/build-module/components/use-resize-canvas/index.js +1 -1
  51. package/build-module/components/use-resize-canvas/index.js.map +1 -1
  52. package/build-module/hooks/duotone.js +1 -1
  53. package/build-module/hooks/duotone.js.map +1 -1
  54. package/build-module/hooks/typography.js +1 -1
  55. package/build-module/hooks/typography.js.map +1 -1
  56. package/build-module/private-apis.js +2 -0
  57. package/build-module/private-apis.js.map +1 -1
  58. package/build-module/private-apis.native.js +3 -1
  59. package/build-module/private-apis.native.js.map +1 -1
  60. package/build-module/store/actions.js +1 -1
  61. package/build-module/store/actions.js.map +1 -1
  62. package/build-style/content-rtl.css +1 -0
  63. package/build-style/content.css +1 -0
  64. package/build-style/style-rtl.css +7 -2
  65. package/build-style/style.css +8 -2
  66. package/package.json +34 -34
  67. package/src/components/block-canvas/index.js +5 -3
  68. package/src/components/block-inspector/style.scss +4 -2
  69. package/src/components/block-list/content.scss +1 -0
  70. package/src/components/block-settings-menu/block-mode-toggle.js +3 -3
  71. package/src/components/block-settings-menu/test/block-mode-toggle.js +1 -1
  72. package/src/components/block-toolbar/use-has-block-toolbar.js +7 -13
  73. package/src/components/child-layout-control/index.js +1 -0
  74. package/src/components/html-element-control/index.js +109 -0
  75. package/src/components/html-element-control/messages.js +34 -0
  76. package/src/components/keyboard-shortcuts/index.js +2 -2
  77. package/src/components/media-placeholder/index.js +1 -1
  78. package/src/components/media-placeholder/style.scss +6 -0
  79. package/src/components/rich-text/event-listeners/delete.js +1 -6
  80. package/src/components/use-resize-canvas/index.js +1 -1
  81. package/src/hooks/duotone.js +4 -3
  82. package/src/hooks/typography.js +5 -3
  83. package/src/private-apis.js +2 -0
  84. package/src/private-apis.native.js +2 -0
  85. package/src/store/actions.js +5 -1
  86. package/src/store/test/actions.js +122 -0
@@ -319,12 +319,14 @@ iframe[name=editor-canvas] {
319
319
  margin-bottom: 1.5em;
320
320
  }
321
321
  .block-editor-block-inspector .components-base-control:where(:not(:last-child)),
322
- .block-editor-block-inspector .components-radio-control:where(:not(:last-child)) {
322
+ .block-editor-block-inspector .components-radio-control:where(:not(:last-child)),
323
+ .block-editor-block-inspector .block-editor-html-element-control:where(:not(:last-child)) {
323
324
  margin-bottom: 16px;
324
325
  }
325
326
  .block-editor-block-inspector .components-focal-point-picker-control .components-base-control,
326
327
  .block-editor-block-inspector .components-query-controls .components-base-control,
327
- .block-editor-block-inspector .components-range-control .components-base-control {
328
+ .block-editor-block-inspector .components-range-control .components-base-control,
329
+ .block-editor-block-inspector .block-editor-html-element-control .components-base-control {
328
330
  margin-bottom: 0;
329
331
  }
330
332
  .block-editor-block-inspector .components-panel__body {
@@ -3030,6 +3032,10 @@ iframe[name=editor-canvas] {
3030
3032
  width: 300px;
3031
3033
  }
3032
3034
  }
3035
+ .block-editor-media-placeholder__url-input-form input {
3036
+ /* rtl:ignore */
3037
+ direction: ltr;
3038
+ }
3033
3039
 
3034
3040
  .modal-open .block-editor-media-replace-flow__options {
3035
3041
  display: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "14.17.0",
3
+ "version": "14.18.0",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -37,38 +37,38 @@
37
37
  "@emotion/react": "^11.7.1",
38
38
  "@emotion/styled": "^11.6.0",
39
39
  "@react-spring/web": "^9.4.5",
40
- "@wordpress/a11y": "^4.22.0",
41
- "@wordpress/api-fetch": "^7.22.0",
42
- "@wordpress/blob": "^4.22.0",
43
- "@wordpress/block-serialization-default-parser": "^5.22.0",
44
- "@wordpress/blocks": "^14.11.0",
45
- "@wordpress/commands": "^1.22.0",
46
- "@wordpress/components": "^29.8.0",
47
- "@wordpress/compose": "^7.22.0",
48
- "@wordpress/data": "^10.22.0",
49
- "@wordpress/date": "^5.22.0",
50
- "@wordpress/deprecated": "^4.22.0",
51
- "@wordpress/dom": "^4.22.0",
52
- "@wordpress/element": "^6.22.0",
53
- "@wordpress/escape-html": "^3.22.0",
54
- "@wordpress/hooks": "^4.22.0",
55
- "@wordpress/html-entities": "^4.22.0",
56
- "@wordpress/i18n": "^5.22.0",
57
- "@wordpress/icons": "^10.22.0",
58
- "@wordpress/is-shallow-equal": "^5.22.0",
59
- "@wordpress/keyboard-shortcuts": "^5.22.0",
60
- "@wordpress/keycodes": "^4.22.0",
61
- "@wordpress/notices": "^5.22.0",
62
- "@wordpress/preferences": "^4.22.0",
63
- "@wordpress/priority-queue": "^3.22.0",
64
- "@wordpress/private-apis": "^1.22.0",
65
- "@wordpress/rich-text": "^7.22.0",
66
- "@wordpress/style-engine": "^2.22.0",
67
- "@wordpress/token-list": "^3.22.0",
68
- "@wordpress/upload-media": "^0.7.0",
69
- "@wordpress/url": "^4.22.0",
70
- "@wordpress/warning": "^3.22.0",
71
- "@wordpress/wordcount": "^4.22.0",
40
+ "@wordpress/a11y": "^4.23.0",
41
+ "@wordpress/api-fetch": "^7.23.0",
42
+ "@wordpress/blob": "^4.23.0",
43
+ "@wordpress/block-serialization-default-parser": "^5.23.0",
44
+ "@wordpress/blocks": "^14.12.0",
45
+ "@wordpress/commands": "^1.23.0",
46
+ "@wordpress/components": "^29.9.0",
47
+ "@wordpress/compose": "^7.23.0",
48
+ "@wordpress/data": "^10.23.0",
49
+ "@wordpress/date": "^5.23.0",
50
+ "@wordpress/deprecated": "^4.23.0",
51
+ "@wordpress/dom": "^4.23.0",
52
+ "@wordpress/element": "^6.23.0",
53
+ "@wordpress/escape-html": "^3.23.0",
54
+ "@wordpress/hooks": "^4.23.0",
55
+ "@wordpress/html-entities": "^4.23.0",
56
+ "@wordpress/i18n": "^5.23.0",
57
+ "@wordpress/icons": "^10.23.0",
58
+ "@wordpress/is-shallow-equal": "^5.23.0",
59
+ "@wordpress/keyboard-shortcuts": "^5.23.0",
60
+ "@wordpress/keycodes": "^4.23.0",
61
+ "@wordpress/notices": "^5.23.0",
62
+ "@wordpress/preferences": "^4.23.0",
63
+ "@wordpress/priority-queue": "^3.23.0",
64
+ "@wordpress/private-apis": "^1.23.0",
65
+ "@wordpress/rich-text": "^7.23.0",
66
+ "@wordpress/style-engine": "^2.23.0",
67
+ "@wordpress/token-list": "^3.23.0",
68
+ "@wordpress/upload-media": "^0.8.0",
69
+ "@wordpress/url": "^4.23.0",
70
+ "@wordpress/warning": "^3.23.0",
71
+ "@wordpress/wordcount": "^4.23.0",
72
72
  "change-case": "^4.1.2",
73
73
  "clsx": "^2.1.1",
74
74
  "colord": "^2.7.0",
@@ -91,5 +91,5 @@
91
91
  "publishConfig": {
92
92
  "access": "public"
93
93
  },
94
- "gitHead": "01a314d7e46a50101e328fdb11959c441e49372d"
94
+ "gitHead": "ab5c79cd40adbb68898536c50e035b0a734338ea"
95
95
  }
@@ -56,8 +56,7 @@ export function ExperimentalBlockCanvas( {
56
56
  return (
57
57
  <BlockTools
58
58
  __unstableContentRef={ localRef }
59
- className="block-editor-block-canvas"
60
- style={ { height } }
59
+ style={ { height, display: 'flex' } }
61
60
  >
62
61
  <EditorStyles
63
62
  styles={ styles }
@@ -68,6 +67,10 @@ export function ExperimentalBlockCanvas( {
68
67
  ref={ contentRef }
69
68
  className="editor-styles-wrapper"
70
69
  tabIndex={ -1 }
70
+ style={ {
71
+ height: '100%',
72
+ width: '100%',
73
+ } }
71
74
  >
72
75
  { children }
73
76
  </WritingFlow>
@@ -78,7 +81,6 @@ export function ExperimentalBlockCanvas( {
78
81
  return (
79
82
  <BlockTools
80
83
  __unstableContentRef={ localRef }
81
- className="block-editor-block-canvas"
82
84
  style={ { height, display: 'flex' } }
83
85
  >
84
86
  <Iframe
@@ -11,7 +11,8 @@
11
11
  }
12
12
 
13
13
  .components-base-control,
14
- .components-radio-control {
14
+ .components-radio-control,
15
+ .block-editor-html-element-control {
15
16
  &:where(:not(:last-child)) {
16
17
  margin-bottom: $grid-unit-20;
17
18
  }
@@ -20,7 +21,8 @@
20
21
  // Reset unwanted margin-bottom from being applied to BaseControls within certain components.
21
22
  .components-focal-point-picker-control,
22
23
  .components-query-controls,
23
- .components-range-control {
24
+ .components-range-control,
25
+ .block-editor-html-element-control {
24
26
  .components-base-control {
25
27
  margin-bottom: 0;
26
28
  }
@@ -365,6 +365,7 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection="true"] .b
365
365
  border: none;
366
366
  outline: none;
367
367
  border-radius: $radius-small;
368
+ box-sizing: border-box;
368
369
  box-shadow: inset 0 0 0 $border-width $gray-900;
369
370
  resize: none;
370
371
  overflow: hidden;
@@ -14,7 +14,7 @@ import { store as blockEditorStore } from '../../store';
14
14
  const noop = () => {};
15
15
 
16
16
  export default function BlockModeToggle( { clientId, onToggle = noop } ) {
17
- const { blockType, mode, isCodeEditingEnabled } = useSelect(
17
+ const { blockType, mode, enabled } = useSelect(
18
18
  ( select ) => {
19
19
  const { getBlock, getBlockMode, getSettings } =
20
20
  select( blockEditorStore );
@@ -23,7 +23,7 @@ export default function BlockModeToggle( { clientId, onToggle = noop } ) {
23
23
  return {
24
24
  mode: getBlockMode( clientId ),
25
25
  blockType: block ? getBlockType( block.name ) : null,
26
- isCodeEditingEnabled: getSettings().codeEditingEnabled,
26
+ enabled: getSettings().codeEditingEnabled && !! block?.isValid,
27
27
  };
28
28
  },
29
29
  [ clientId ]
@@ -33,7 +33,7 @@ export default function BlockModeToggle( { clientId, onToggle = noop } ) {
33
33
  if (
34
34
  ! blockType ||
35
35
  ! hasBlockSupport( blockType, 'html', true ) ||
36
- ! isCodeEditingEnabled
36
+ ! enabled
37
37
  ) {
38
38
  return null;
39
39
  }
@@ -20,7 +20,7 @@ function setupUseSelectMock( mode, blockType, codeEditingEnabled = true ) {
20
20
  return {
21
21
  mode,
22
22
  blockType,
23
- isCodeEditingEnabled: codeEditingEnabled,
23
+ enabled: codeEditingEnabled,
24
24
  };
25
25
  } );
26
26
  }
@@ -14,7 +14,7 @@ import { store as blockEditorStore } from '../../store';
14
14
  * @return {boolean} Whether the block toolbar component will be rendered.
15
15
  */
16
16
  export function useHasBlockToolbar() {
17
- const { isToolbarEnabled, isBlockDisabled } = useSelect( ( select ) => {
17
+ const enabled = useSelect( ( select ) => {
18
18
  const { getBlockEditingMode, getBlockName, getBlockSelectionStart } =
19
19
  select( blockEditorStore );
20
20
 
@@ -27,18 +27,12 @@ export function useHasBlockToolbar() {
27
27
  selectedBlockClientId &&
28
28
  getBlockType( getBlockName( selectedBlockClientId ) );
29
29
 
30
- return {
31
- isToolbarEnabled:
32
- blockType &&
33
- hasBlockSupport( blockType, '__experimentalToolbar', true ),
34
- isBlockDisabled:
35
- getBlockEditingMode( selectedBlockClientId ) === 'disabled',
36
- };
30
+ return (
31
+ blockType &&
32
+ hasBlockSupport( blockType, '__experimentalToolbar', true ) &&
33
+ getBlockEditingMode( selectedBlockClientId ) !== 'disabled'
34
+ );
37
35
  }, [] );
38
36
 
39
- if ( ! isToolbarEnabled || isBlockDisabled ) {
40
- return false;
41
- }
42
-
43
- return true;
37
+ return enabled;
44
38
  }
@@ -188,6 +188,7 @@ function FlexControls( {
188
188
  } );
189
189
  } }
190
190
  value={ flexSize }
191
+ min={ 0 }
191
192
  label={ flexResetLabel }
192
193
  hideLabelFromVision
193
194
  />
@@ -0,0 +1,109 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __, sprintf } from '@wordpress/i18n';
5
+ import {
6
+ SelectControl,
7
+ Notice,
8
+ __experimentalVStack as VStack,
9
+ } from '@wordpress/components';
10
+ import { useSelect } from '@wordpress/data';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { store as blockEditorStore } from '../../store';
16
+ import { htmlElementMessages } from './messages';
17
+
18
+ /**
19
+ * Renders a SelectControl for choosing HTML elements with validation
20
+ * to prevent duplicate <main> elements.
21
+ *
22
+ * @param {Object} props Component props.
23
+ * @param {string} props.tagName The current HTML tag name.
24
+ * @param {Function} props.onChange Function to call when the tag is changed.
25
+ * @param {string} props.clientId The client ID of the current block.
26
+ * @param {Array} props.options SelectControl options (optional).
27
+ *
28
+ * @return {Component} The HTML element select control with validation.
29
+ */
30
+ export default function HTMLElementControl( {
31
+ tagName,
32
+ onChange,
33
+ clientId,
34
+ options = [
35
+ { label: __( 'Default (<div>)' ), value: 'div' },
36
+ { label: '<header>', value: 'header' },
37
+ { label: '<main>', value: 'main' },
38
+ { label: '<section>', value: 'section' },
39
+ { label: '<article>', value: 'article' },
40
+ { label: '<aside>', value: 'aside' },
41
+ { label: '<footer>', value: 'footer' },
42
+ ],
43
+ } ) {
44
+ const checkForMainTag =
45
+ !! clientId && options.some( ( option ) => option.value === 'main' );
46
+
47
+ const hasMainElementElsewhere = useSelect(
48
+ ( select ) => {
49
+ if ( ! checkForMainTag ) {
50
+ return false;
51
+ }
52
+
53
+ const { getClientIdsWithDescendants, getBlockAttributes } =
54
+ select( blockEditorStore );
55
+
56
+ return getClientIdsWithDescendants().some( ( id ) => {
57
+ // Skip the current block.
58
+ if ( id === clientId ) {
59
+ return false;
60
+ }
61
+
62
+ return getBlockAttributes( id )?.tagName === 'main';
63
+ } );
64
+ },
65
+ [ clientId, checkForMainTag ]
66
+ );
67
+
68
+ // Create a modified options array that disables the main option if needed.
69
+ const modifiedOptions = options.map( ( option ) => {
70
+ if (
71
+ option.value === 'main' &&
72
+ hasMainElementElsewhere &&
73
+ tagName !== 'main'
74
+ ) {
75
+ return {
76
+ ...option,
77
+ disabled: true,
78
+ label: sprintf(
79
+ /* translators: %s: HTML element name */
80
+ __( '%s (Already in use)' ),
81
+ option.label
82
+ ),
83
+ };
84
+ }
85
+ return option;
86
+ } );
87
+
88
+ return (
89
+ <VStack spacing={ 2 } className="block-editor-html-element-control">
90
+ <SelectControl
91
+ __nextHasNoMarginBottom
92
+ __next40pxDefaultSize
93
+ label={ __( 'HTML element' ) }
94
+ options={ modifiedOptions }
95
+ value={ tagName }
96
+ onChange={ onChange }
97
+ help={ htmlElementMessages[ tagName ] }
98
+ />
99
+
100
+ { tagName === 'main' && hasMainElementElsewhere && (
101
+ <Notice status="warning" isDismissible={ false }>
102
+ { __(
103
+ 'Multiple <main> elements detected. The duplicate may be in your content or template. This is not valid HTML and may cause accessibility issues. Please change this HTML element.'
104
+ ) }
105
+ </Notice>
106
+ ) }
107
+ </VStack>
108
+ );
109
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ /**
7
+ * Messages providing helpful descriptions for HTML elements.
8
+ */
9
+ export const htmlElementMessages = {
10
+ article: __(
11
+ 'The <article> element should represent a self-contained, syndicatable portion of the document.'
12
+ ),
13
+ aside: __(
14
+ "The <aside> element should represent a portion of a document whose content is only indirectly related to the document's main content."
15
+ ),
16
+ div: __(
17
+ 'The <div> element should only be used if the block is a design element with no semantic meaning.'
18
+ ),
19
+ footer: __(
20
+ 'The <footer> element should represent a footer for its nearest sectioning element (e.g.: <section>, <article>, <main> etc.).'
21
+ ),
22
+ header: __(
23
+ 'The <header> element should represent introductory content, typically a group of introductory or navigational aids.'
24
+ ),
25
+ main: __(
26
+ 'The <main> element should be used for the primary content of your document only.'
27
+ ),
28
+ nav: __(
29
+ 'The <nav> element should be used to identify groups of links that are intended to be used for website or page content navigation.'
30
+ ),
31
+ section: __(
32
+ "The <section> element should represent a standalone portion of the document that can't be better represented by another element."
33
+ ),
34
+ };
@@ -59,8 +59,8 @@ function KeyboardShortcutsRegister() {
59
59
  category: 'block',
60
60
  description: __( 'Remove the selected block(s).' ),
61
61
  keyCombination: {
62
- modifier: 'primaryShift',
63
- character: 'backspace',
62
+ modifier: 'access',
63
+ character: 'z',
64
64
  },
65
65
  } );
66
66
 
@@ -47,7 +47,7 @@ const InsertFromURLPopover = ( {
47
47
  <InputControl
48
48
  __next40pxDefaultSize
49
49
  label={ __( 'URL' ) }
50
- type="url"
50
+ type="text" // Use text instead of URL to allow relative paths (e.g., /image/image.jpg)
51
51
  hideLabelFromVision
52
52
  placeholder={ __( 'Paste or type URL' ) }
53
53
  onChange={ onChange }
@@ -4,4 +4,10 @@
4
4
  @include break-small() {
5
5
  width: 300px;
6
6
  }
7
+
8
+ input {
9
+ // Targeting input as we're using text instead of URLs for relative paths.
10
+ /* rtl:ignore */
11
+ direction: ltr;
12
+ }
7
13
  }
@@ -6,7 +6,7 @@ import { isCollapsed, isEmpty } from '@wordpress/rich-text';
6
6
 
7
7
  export default ( props ) => ( element ) => {
8
8
  function onKeyDown( event ) {
9
- const { keyCode, shiftKey, ctrlKey, metaKey } = event;
9
+ const { keyCode } = event;
10
10
 
11
11
  if ( event.defaultPrevented ) {
12
12
  return;
@@ -30,11 +30,6 @@ export default ( props ) => ( element ) => {
30
30
  return;
31
31
  }
32
32
 
33
- // Exclude (command|ctrl)+shift+backspace as they are shortcuts for deleting blocks.
34
- if ( shiftKey && ( ctrlKey || metaKey ) ) {
35
- return;
36
- }
37
-
38
33
  if ( onMerge ) {
39
34
  onMerge( ! isReverse );
40
35
  }
@@ -60,7 +60,7 @@ export default function useResizeCanvas( deviceType ) {
60
60
  marginLeft: marginHorizontal,
61
61
  marginRight: marginHorizontal,
62
62
  height,
63
- maxWidth: '100%',
63
+ overflowY: 'auto',
64
64
  };
65
65
  default:
66
66
  return {
@@ -124,9 +124,10 @@ function DuotonePanelPure( { style, setAttributes, name } ) {
124
124
  return null;
125
125
  }
126
126
 
127
- const duotonePresetOrColors = ! Array.isArray( duotoneStyle )
128
- ? getColorsFromDuotonePreset( duotoneStyle, duotonePalette )
129
- : duotoneStyle;
127
+ const duotonePresetOrColors =
128
+ duotoneStyle === 'unset' || Array.isArray( duotoneStyle )
129
+ ? duotoneStyle
130
+ : getColorsFromDuotonePreset( duotoneStyle, duotonePalette );
130
131
 
131
132
  return (
132
133
  <>
@@ -53,9 +53,11 @@ function styleToAttributes( style ) {
53
53
  const updatedStyle = { ...omit( style, [ 'fontFamily' ] ) };
54
54
  const fontSizeValue = style?.typography?.fontSize;
55
55
  const fontFamilyValue = style?.typography?.fontFamily;
56
- const fontSizeSlug = fontSizeValue?.startsWith( 'var:preset|font-size|' )
57
- ? fontSizeValue.substring( 'var:preset|font-size|'.length )
58
- : undefined;
56
+ const fontSizeSlug =
57
+ typeof fontSizeValue === 'string' &&
58
+ fontSizeValue?.startsWith( 'var:preset|font-size|' )
59
+ ? fontSizeValue.substring( 'var:preset|font-size|'.length )
60
+ : undefined;
59
61
  const fontFamilySlug = fontFamilyValue?.startsWith(
60
62
  'var:preset|font-family|'
61
63
  )
@@ -50,6 +50,7 @@ import useBlockDisplayTitle from './components/block-title/use-block-display-tit
50
50
  import TabbedSidebar from './components/tabbed-sidebar';
51
51
  import CommentIconSlotFill from './components/collab/block-comment-icon-slot';
52
52
  import CommentIconToolbarSlotFill from './components/collab/block-comment-icon-toolbar-slot';
53
+ import HTMLElementControl from './components/html-element-control';
53
54
  /**
54
55
  * Private @wordpress/block-editor APIs.
55
56
  */
@@ -80,6 +81,7 @@ lock( privateApis, {
80
81
  TextAlignmentControl,
81
82
  usesContextKey,
82
83
  useFlashEditableBlocks,
84
+ HTMLElementControl,
83
85
  useZoomOut,
84
86
  globalStylesDataKey,
85
87
  globalStylesLinksDataKey,
@@ -6,6 +6,7 @@ import { ExperimentalBlockEditorProvider } from './components/provider';
6
6
  import { getRichTextValues } from './components/rich-text/get-rich-text-values';
7
7
  import { lock } from './lock-unlock';
8
8
  import { PrivateRichText } from './components/rich-text/';
9
+ import HTMLElementControl from './components/html-element-control';
9
10
 
10
11
  /**
11
12
  * Private @wordpress/block-editor APIs.
@@ -16,4 +17,5 @@ lock( privateApis, {
16
17
  ExperimentalBlockEditorProvider,
17
18
  getRichTextValues,
18
19
  PrivateRichText,
20
+ HTMLElementControl,
19
21
  } );
@@ -1157,7 +1157,11 @@ export const mergeBlocks =
1157
1157
  const blockA = select.getBlock( clientIdA );
1158
1158
  const blockAType = getBlockType( blockA.name );
1159
1159
 
1160
- if ( ! blockAType ) {
1160
+ if (
1161
+ ! blockAType ||
1162
+ select.getBlockEditingMode( clientIdA ) === 'disabled' ||
1163
+ select.getBlockEditingMode( clientIdB ) === 'disabled'
1164
+ ) {
1161
1165
  return;
1162
1166
  }
1163
1167