@wordpress/block-editor 8.5.1 → 8.5.2

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 (54) hide show
  1. package/build/components/block-alignment-control/ui.js +1 -1
  2. package/build/components/block-alignment-control/ui.js.map +1 -1
  3. package/build/components/block-lock/menu-item.js +1 -1
  4. package/build/components/block-lock/menu-item.js.map +1 -1
  5. package/build/components/block-lock/modal.js +1 -1
  6. package/build/components/block-lock/modal.js.map +1 -1
  7. package/build/components/block-lock/use-block-lock.js +3 -6
  8. package/build/components/block-lock/use-block-lock.js.map +1 -1
  9. package/build/components/copy-handler/index.js +44 -9
  10. package/build/components/copy-handler/index.js.map +1 -1
  11. package/build/components/link-control/index.js +6 -7
  12. package/build/components/link-control/index.js.map +1 -1
  13. package/build/components/list-view/block.js +13 -2
  14. package/build/components/list-view/block.js.map +1 -1
  15. package/build/store/actions.js +22 -29
  16. package/build/store/actions.js.map +1 -1
  17. package/build/store/selectors.js +96 -1
  18. package/build/store/selectors.js.map +1 -1
  19. package/build/store/utils.js +27 -0
  20. package/build/store/utils.js.map +1 -0
  21. package/build-module/components/block-alignment-control/ui.js +2 -2
  22. package/build-module/components/block-alignment-control/ui.js.map +1 -1
  23. package/build-module/components/block-lock/menu-item.js +1 -1
  24. package/build-module/components/block-lock/menu-item.js.map +1 -1
  25. package/build-module/components/block-lock/modal.js +1 -1
  26. package/build-module/components/block-lock/modal.js.map +1 -1
  27. package/build-module/components/block-lock/use-block-lock.js +3 -6
  28. package/build-module/components/block-lock/use-block-lock.js.map +1 -1
  29. package/build-module/components/copy-handler/index.js +44 -9
  30. package/build-module/components/copy-handler/index.js.map +1 -1
  31. package/build-module/components/link-control/index.js +6 -7
  32. package/build-module/components/link-control/index.js.map +1 -1
  33. package/build-module/components/list-view/block.js +13 -2
  34. package/build-module/components/list-view/block.js.map +1 -1
  35. package/build-module/store/actions.js +5 -14
  36. package/build-module/store/actions.js.map +1 -1
  37. package/build-module/store/selectors.js +88 -0
  38. package/build-module/store/selectors.js.map +1 -1
  39. package/build-module/store/utils.js +20 -0
  40. package/build-module/store/utils.js.map +1 -0
  41. package/build-style/style-rtl.css +1 -2
  42. package/build-style/style.css +1 -2
  43. package/package.json +28 -28
  44. package/src/components/block-alignment-control/ui.js +2 -2
  45. package/src/components/block-lock/menu-item.js +1 -1
  46. package/src/components/block-lock/modal.js +1 -1
  47. package/src/components/block-lock/style.scss +1 -2
  48. package/src/components/block-lock/use-block-lock.js +4 -8
  49. package/src/components/copy-handler/index.js +52 -10
  50. package/src/components/link-control/index.js +5 -5
  51. package/src/components/list-view/block.js +16 -7
  52. package/src/store/actions.js +5 -13
  53. package/src/store/selectors.js +126 -0
  54. package/src/store/utils.js +19 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Helper function that maps attribute definition properties to the
3
+ * ones used by RichText utils like `create, toHTMLString, etc..`.
4
+ *
5
+ * @param {Object} attributeDefinition A block's attribute definition object.
6
+ * @return {Object} The mapped object.
7
+ */
8
+ export function mapRichTextSettings(attributeDefinition) {
9
+ const {
10
+ multiline: multilineTag,
11
+ __unstableMultilineWrapperTags: multilineWrapperTags,
12
+ __unstablePreserveWhiteSpace: preserveWhiteSpace
13
+ } = attributeDefinition;
14
+ return {
15
+ multilineTag,
16
+ multilineWrapperTags,
17
+ preserveWhiteSpace
18
+ };
19
+ }
20
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["@wordpress/block-editor/src/store/utils.js"],"names":["mapRichTextSettings","attributeDefinition","multiline","multilineTag","__unstableMultilineWrapperTags","multilineWrapperTags","__unstablePreserveWhiteSpace","preserveWhiteSpace"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,mBAAT,CAA8BC,mBAA9B,EAAoD;AAC1D,QAAM;AACLC,IAAAA,SAAS,EAAEC,YADN;AAELC,IAAAA,8BAA8B,EAAEC,oBAF3B;AAGLC,IAAAA,4BAA4B,EAAEC;AAHzB,MAIFN,mBAJJ;AAKA,SAAO;AACNE,IAAAA,YADM;AAENE,IAAAA,oBAFM;AAGNE,IAAAA;AAHM,GAAP;AAKA","sourcesContent":["/**\n * Helper function that maps attribute definition properties to the\n * ones used by RichText utils like `create, toHTMLString, etc..`.\n *\n * @param {Object} attributeDefinition A block's attribute definition object.\n * @return {Object} The mapped object.\n */\nexport function mapRichTextSettings( attributeDefinition ) {\n\tconst {\n\t\tmultiline: multilineTag,\n\t\t__unstableMultilineWrapperTags: multilineWrapperTags,\n\t\t__unstablePreserveWhiteSpace: preserveWhiteSpace,\n\t} = attributeDefinition;\n\treturn {\n\t\tmultilineTag,\n\t\tmultilineWrapperTags,\n\t\tpreserveWhiteSpace,\n\t};\n}\n"]}
@@ -855,8 +855,7 @@
855
855
  padding-right: 0 !important;
856
856
  }
857
857
  .block-editor-block-lock-toolbar .components-button.has-icon:focus::before {
858
- right: 0 !important;
859
- left: 12px !important;
858
+ left: 8px !important;
860
859
  }
861
860
 
862
861
  .block-editor-block-breadcrumb {
@@ -855,8 +855,7 @@
855
855
  padding-left: 0 !important;
856
856
  }
857
857
  .block-editor-block-lock-toolbar .components-button.has-icon:focus::before {
858
- left: 0 !important;
859
- right: 12px !important;
858
+ right: 8px !important;
860
859
  }
861
860
 
862
861
  .block-editor-block-breadcrumb {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "8.5.1",
3
+ "version": "8.5.2",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -33,32 +33,32 @@
33
33
  "dependencies": {
34
34
  "@babel/runtime": "^7.16.0",
35
35
  "@react-spring/web": "^9.2.4",
36
- "@wordpress/a11y": "^3.6.0",
37
- "@wordpress/api-fetch": "^6.3.0",
38
- "@wordpress/blob": "^3.6.0",
39
- "@wordpress/blocks": "^11.5.1",
40
- "@wordpress/components": "^19.8.0",
41
- "@wordpress/compose": "^5.4.0",
42
- "@wordpress/data": "^6.6.0",
43
- "@wordpress/date": "^4.6.0",
44
- "@wordpress/deprecated": "^3.6.0",
45
- "@wordpress/dom": "^3.6.0",
46
- "@wordpress/element": "^4.4.0",
47
- "@wordpress/hooks": "^3.6.0",
48
- "@wordpress/html-entities": "^3.6.0",
49
- "@wordpress/i18n": "^4.6.0",
50
- "@wordpress/icons": "^8.2.0",
51
- "@wordpress/is-shallow-equal": "^4.6.0",
52
- "@wordpress/keyboard-shortcuts": "^3.4.0",
53
- "@wordpress/keycodes": "^3.6.0",
54
- "@wordpress/notices": "^3.6.0",
55
- "@wordpress/rich-text": "^5.4.0",
56
- "@wordpress/shortcode": "^3.6.0",
57
- "@wordpress/style-engine": "^0.5.0",
58
- "@wordpress/token-list": "^2.6.0",
59
- "@wordpress/url": "^3.7.0",
60
- "@wordpress/warning": "^2.6.0",
61
- "@wordpress/wordcount": "^3.6.0",
36
+ "@wordpress/a11y": "^3.6.1",
37
+ "@wordpress/api-fetch": "^6.3.1",
38
+ "@wordpress/blob": "^3.6.1",
39
+ "@wordpress/blocks": "^11.5.2",
40
+ "@wordpress/components": "^19.8.1",
41
+ "@wordpress/compose": "^5.4.1",
42
+ "@wordpress/data": "^6.6.1",
43
+ "@wordpress/date": "^4.6.1",
44
+ "@wordpress/deprecated": "^3.6.1",
45
+ "@wordpress/dom": "^3.6.1",
46
+ "@wordpress/element": "^4.4.1",
47
+ "@wordpress/hooks": "^3.6.1",
48
+ "@wordpress/html-entities": "^3.6.1",
49
+ "@wordpress/i18n": "^4.6.1",
50
+ "@wordpress/icons": "^8.2.1",
51
+ "@wordpress/is-shallow-equal": "^4.6.1",
52
+ "@wordpress/keyboard-shortcuts": "^3.4.1",
53
+ "@wordpress/keycodes": "^3.6.1",
54
+ "@wordpress/notices": "^3.6.1",
55
+ "@wordpress/rich-text": "^5.4.1",
56
+ "@wordpress/shortcode": "^3.6.1",
57
+ "@wordpress/style-engine": "^0.5.1",
58
+ "@wordpress/token-list": "^2.6.1",
59
+ "@wordpress/url": "^3.7.1",
60
+ "@wordpress/warning": "^2.6.1",
61
+ "@wordpress/wordcount": "^3.6.1",
62
62
  "classnames": "^2.3.1",
63
63
  "colord": "^2.7.0",
64
64
  "diff": "^4.0.2",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "9c15c669843d53c5ca6024a4c486d01d819d123f"
80
+ "gitHead": "446565ecaa40370173c18926535e975ec5652b71"
81
81
  }
@@ -6,7 +6,7 @@ import classNames from 'classnames';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { __ } from '@wordpress/i18n';
9
+ import { __, _x } from '@wordpress/i18n';
10
10
  import {
11
11
  ToolbarDropdownMenu,
12
12
  ToolbarGroup,
@@ -31,7 +31,7 @@ import useAvailableAlignments from './use-available-alignments';
31
31
  const BLOCK_ALIGNMENTS_CONTROLS = {
32
32
  none: {
33
33
  icon: alignNone,
34
- title: __( 'None' ),
34
+ title: _x( 'None', 'Alignment option' ),
35
35
  },
36
36
  left: {
37
37
  icon: positionLeft,
@@ -13,7 +13,7 @@ import useBlockLock from './use-block-lock';
13
13
  import BlockLockModal from './modal';
14
14
 
15
15
  export default function BlockLockMenuItem( { clientId } ) {
16
- const { canLock, isLocked } = useBlockLock( clientId, true );
16
+ const { canLock, isLocked } = useBlockLock( clientId );
17
17
 
18
18
  const [ isModalOpen, toggleModal ] = useReducer(
19
19
  ( isActive ) => ! isActive,
@@ -25,7 +25,7 @@ import { store as blockEditorStore } from '../../store';
25
25
 
26
26
  export default function BlockLockModal( { clientId, onClose } ) {
27
27
  const [ lock, setLock ] = useState( { move: false, remove: false } );
28
- const { canEdit, canMove, canRemove } = useBlockLock( clientId, true );
28
+ const { canEdit, canMove, canRemove } = useBlockLock( clientId );
29
29
  const { isReusable } = useSelect(
30
30
  ( select ) => {
31
31
  const { getBlockName } = select( blockEditorStore );
@@ -63,8 +63,7 @@
63
63
  padding-left: 0 !important;
64
64
 
65
65
  &:focus::before {
66
- left: 0 !important;
67
- right: $grid-unit-15 !important;
66
+ right: $grid-unit-10 !important;
68
67
  }
69
68
  }
70
69
  }
@@ -11,13 +11,11 @@ import { store as blockEditorStore } from '../../store';
11
11
  /**
12
12
  * Return details about the block lock status.
13
13
  *
14
- * @param {string} clientId The block client Id.
15
- * @param {boolean} checkParent Optional. The status is derived from the parent `templateLock`
16
- * when the current block's lock state isn't defined.
14
+ * @param {string} clientId The block client Id.
17
15
  *
18
16
  * @return {Object} Block lock status
19
17
  */
20
- export default function useBlockLock( clientId, checkParent = false ) {
18
+ export default function useBlockLock( clientId ) {
21
19
  return useSelect(
22
20
  ( select ) => {
23
21
  const {
@@ -28,9 +26,7 @@ export default function useBlockLock( clientId, checkParent = false ) {
28
26
  getBlockName,
29
27
  getBlockRootClientId,
30
28
  } = select( blockEditorStore );
31
- const rootClientId = checkParent
32
- ? getBlockRootClientId( clientId )
33
- : null;
29
+ const rootClientId = getBlockRootClientId( clientId );
34
30
 
35
31
  const canEdit = canEditBlock( clientId );
36
32
  const canMove = canMoveBlock( clientId, rootClientId );
@@ -44,6 +40,6 @@ export default function useBlockLock( clientId, checkParent = false ) {
44
40
  isLocked: ! canEdit || ! canMove || ! canRemove,
45
41
  };
46
42
  },
47
- [ clientId, checkParent ]
43
+ [ clientId ]
48
44
  );
49
45
  }
@@ -78,10 +78,18 @@ export function useClipboardHandler() {
78
78
  getSelectedBlockClientIds,
79
79
  hasMultiSelection,
80
80
  getSettings,
81
+ __unstableIsFullySelected,
82
+ __unstableIsSelectionCollapsed,
83
+ __unstableIsSelectionMergeable,
84
+ __unstableGetSelectedBlocksWithPartialSelection,
81
85
  } = useSelect( blockEditorStore );
82
- const { flashBlock, removeBlocks, replaceBlocks } = useDispatch(
83
- blockEditorStore
84
- );
86
+ const {
87
+ flashBlock,
88
+ removeBlocks,
89
+ replaceBlocks,
90
+ __unstableDeleteSelection,
91
+ __unstableExpandSelection,
92
+ } = useDispatch( blockEditorStore );
85
93
  const notifyCopy = useNotifyCopy();
86
94
 
87
95
  return useRefEffect( ( node ) => {
@@ -116,20 +124,54 @@ export function useClipboardHandler() {
116
124
  const eventDefaultPrevented = event.defaultPrevented;
117
125
  event.preventDefault();
118
126
 
127
+ const isSelectionMergeable = __unstableIsSelectionMergeable();
128
+ const shouldHandleWholeBlocks =
129
+ __unstableIsSelectionCollapsed() || __unstableIsFullySelected();
130
+ const expandSelectionIsNeeded =
131
+ ! shouldHandleWholeBlocks && ! isSelectionMergeable;
119
132
  if ( event.type === 'copy' || event.type === 'cut' ) {
120
133
  if ( selectedBlockClientIds.length === 1 ) {
121
134
  flashBlock( selectedBlockClientIds[ 0 ] );
122
135
  }
123
- notifyCopy( event.type, selectedBlockClientIds );
124
- const blocks = getBlocksByClientId( selectedBlockClientIds );
125
- const serialized = serialize( blocks );
126
-
127
- event.clipboardData.setData( 'text/plain', serialized );
128
- event.clipboardData.setData( 'text/html', serialized );
136
+ // If we have a partial selection that is not mergeable, just
137
+ // expand the selection to the whole blocks.
138
+ if ( expandSelectionIsNeeded ) {
139
+ __unstableExpandSelection();
140
+ } else {
141
+ notifyCopy( event.type, selectedBlockClientIds );
142
+ let blocks;
143
+ // Check if we have partial selection.
144
+ if ( shouldHandleWholeBlocks ) {
145
+ blocks = getBlocksByClientId( selectedBlockClientIds );
146
+ } else {
147
+ const [
148
+ head,
149
+ tail,
150
+ ] = __unstableGetSelectedBlocksWithPartialSelection();
151
+ const inBetweenBlocks = getBlocksByClientId(
152
+ selectedBlockClientIds.slice(
153
+ 1,
154
+ selectedBlockClientIds.length - 1
155
+ )
156
+ );
157
+ blocks = [ head, ...inBetweenBlocks, tail ];
158
+ }
159
+ const serialized = serialize( blocks );
160
+
161
+ event.clipboardData.setData( 'text/plain', serialized );
162
+ event.clipboardData.setData( 'text/html', serialized );
163
+ }
129
164
  }
130
165
 
131
166
  if ( event.type === 'cut' ) {
132
- removeBlocks( selectedBlockClientIds );
167
+ // We need to also check if at the start we needed to
168
+ // expand the selection, as in this point we might have
169
+ // programmatically fully selected the blocks above.
170
+ if ( shouldHandleWholeBlocks && ! expandSelectionIsNeeded ) {
171
+ removeBlocks( selectedBlockClientIds );
172
+ } else {
173
+ __unstableDeleteSelection();
174
+ }
133
175
  } else if ( event.type === 'paste' ) {
134
176
  if ( eventDefaultPrevented ) {
135
177
  // This was likely already handled in rich-text/use-paste-handler.js.
@@ -148,6 +148,10 @@ function LinkControl( {
148
148
 
149
149
  const currentInputIsEmpty = ! currentInputValue?.trim()?.length;
150
150
 
151
+ const { createPage, isCreatingPage, errorMessage } = useCreatePage(
152
+ createSuggestion
153
+ );
154
+
151
155
  useEffect( () => {
152
156
  if (
153
157
  forceIsEditingLink !== undefined &&
@@ -185,7 +189,7 @@ function LinkControl( {
185
189
  nextFocusTarget.focus();
186
190
 
187
191
  isEndingEditWithFocus.current = false;
188
- }, [ isEditingLink ] );
192
+ }, [ isEditingLink, isCreatingPage ] );
189
193
 
190
194
  useEffect( () => {
191
195
  /**
@@ -217,10 +221,6 @@ function LinkControl( {
217
221
  setIsEditingLink( false );
218
222
  }
219
223
 
220
- const { createPage, isCreatingPage, errorMessage } = useCreatePage(
221
- createSuggestion
222
- );
223
-
224
224
  const handleSelectSuggestion = ( updatedValue ) => {
225
225
  onChange( {
226
226
  ...updatedValue,
@@ -36,6 +36,7 @@ import { useListViewContext } from './context';
36
36
  import { getBlockPositionDescription } from './utils';
37
37
  import { store as blockEditorStore } from '../../store';
38
38
  import useBlockDisplayInformation from '../use-block-display-information';
39
+ import { useBlockLock } from '../block-lock';
39
40
 
40
41
  function ListViewBlock( {
41
42
  block,
@@ -65,6 +66,7 @@ function ListViewBlock( {
65
66
  const { toggleBlockHighlight } = useDispatch( blockEditorStore );
66
67
 
67
68
  const blockInformation = useBlockDisplayInformation( clientId );
69
+ const { isLocked } = useBlockLock( clientId );
68
70
  const instanceId = useInstanceId( ListViewBlock );
69
71
  const descriptionId = `list-view-block-select-button__${ instanceId }`;
70
72
  const blockPositionDescription = getBlockPositionDescription(
@@ -73,13 +75,20 @@ function ListViewBlock( {
73
75
  level
74
76
  );
75
77
 
76
- const blockAriaLabel = blockInformation
77
- ? sprintf(
78
- // translators: %s: The title of the block. This string indicates a link to select the block.
79
- __( '%s link' ),
80
- blockInformation.title
81
- )
82
- : __( 'Link' );
78
+ let blockAriaLabel = __( 'Link' );
79
+ if ( blockInformation ) {
80
+ blockAriaLabel = isLocked
81
+ ? sprintf(
82
+ // translators: %s: The title of the block. This string indicates a link to select the locked block.
83
+ __( '%s link (locked)' ),
84
+ blockInformation.title
85
+ )
86
+ : sprintf(
87
+ // translators: %s: The title of the block. This string indicates a link to select the block.
88
+ __( '%s link' ),
89
+ blockInformation.title
90
+ );
91
+ }
83
92
 
84
93
  const settingsAriaLabel = blockInformation
85
94
  ? sprintf(
@@ -22,6 +22,11 @@ import { __, _n, sprintf } from '@wordpress/i18n';
22
22
  import { create, insert, remove, toHTMLString } from '@wordpress/rich-text';
23
23
  import deprecated from '@wordpress/deprecated';
24
24
 
25
+ /**
26
+ * Internal dependencies
27
+ */
28
+ import { mapRichTextSettings } from './utils';
29
+
25
30
  /**
26
31
  * Action which will insert a default block insert action if there
27
32
  * are no other blocks at the root of the editor. This action should be used
@@ -667,19 +672,6 @@ export const synchronizeTemplate = () => ( { select, dispatch } ) => {
667
672
  dispatch.resetBlocks( updatedBlockList );
668
673
  };
669
674
 
670
- function mapRichTextSettings( attributeDefinition ) {
671
- const {
672
- multiline: multilineTag,
673
- __unstableMultilineWrapperTags: multilineWrapperTags,
674
- __unstablePreserveWhiteSpace: preserveWhiteSpace,
675
- } = attributeDefinition;
676
- return {
677
- multilineTag,
678
- multilineWrapperTags,
679
- preserveWhiteSpace,
680
- };
681
- }
682
-
683
675
  /**
684
676
  * Delete the current selection.
685
677
  *
@@ -33,6 +33,12 @@ import { Platform } from '@wordpress/element';
33
33
  import { applyFilters } from '@wordpress/hooks';
34
34
  import { symbol } from '@wordpress/icons';
35
35
  import { __ } from '@wordpress/i18n';
36
+ import { create, remove, toHTMLString } from '@wordpress/rich-text';
37
+
38
+ /**
39
+ * Internal dependencies
40
+ */
41
+ import { mapRichTextSettings } from './utils';
36
42
 
37
43
  /**
38
44
  * A block selection object.
@@ -926,6 +932,25 @@ export function __unstableIsFullySelected( state ) {
926
932
  );
927
933
  }
928
934
 
935
+ /**
936
+ * Returns true if the selection is collapsed.
937
+ *
938
+ * @param {Object} state Editor state.
939
+ *
940
+ * @return {boolean} Whether the selection is collapsed.
941
+ */
942
+ export function __unstableIsSelectionCollapsed( state ) {
943
+ const selectionAnchor = getSelectionStart( state );
944
+ const selectionFocus = getSelectionEnd( state );
945
+ return (
946
+ !! selectionAnchor &&
947
+ !! selectionFocus &&
948
+ selectionAnchor.clientId === selectionFocus.clientId &&
949
+ selectionAnchor.attributeKey === selectionFocus.attributeKey &&
950
+ selectionAnchor.offset === selectionFocus.offset
951
+ );
952
+ }
953
+
929
954
  /**
930
955
  * Check whether the selection is mergeable.
931
956
  *
@@ -1004,6 +1029,107 @@ export function __unstableIsSelectionMergeable( state, isForward ) {
1004
1029
  return blocksToMerge && blocksToMerge.length;
1005
1030
  }
1006
1031
 
1032
+ /**
1033
+ * Get partial selected blocks with their content updated
1034
+ * based on the selection.
1035
+ *
1036
+ * @param {Object} state Editor state.
1037
+ *
1038
+ * @return {Object[]} Updated partial selected blocks.
1039
+ */
1040
+ export const __unstableGetSelectedBlocksWithPartialSelection = ( state ) => {
1041
+ const selectionAnchor = getSelectionStart( state );
1042
+ const selectionFocus = getSelectionEnd( state );
1043
+
1044
+ if ( selectionAnchor.clientId === selectionFocus.clientId ) {
1045
+ return EMPTY_ARRAY;
1046
+ }
1047
+
1048
+ // Can't split if the selection is not set.
1049
+ if (
1050
+ ! selectionAnchor.attributeKey ||
1051
+ ! selectionFocus.attributeKey ||
1052
+ typeof selectionAnchor.offset === 'undefined' ||
1053
+ typeof selectionFocus.offset === 'undefined'
1054
+ ) {
1055
+ return EMPTY_ARRAY;
1056
+ }
1057
+
1058
+ const anchorRootClientId = getBlockRootClientId(
1059
+ state,
1060
+ selectionAnchor.clientId
1061
+ );
1062
+ const focusRootClientId = getBlockRootClientId(
1063
+ state,
1064
+ selectionFocus.clientId
1065
+ );
1066
+
1067
+ // It's not splittable if the selection doesn't start and end in the same
1068
+ // block list. Maybe in the future it should be allowed.
1069
+ if ( anchorRootClientId !== focusRootClientId ) {
1070
+ return EMPTY_ARRAY;
1071
+ }
1072
+
1073
+ const blockOrder = getBlockOrder( state, anchorRootClientId );
1074
+ const anchorIndex = blockOrder.indexOf( selectionAnchor.clientId );
1075
+ const focusIndex = blockOrder.indexOf( selectionFocus.clientId );
1076
+
1077
+ // Reassign selection start and end based on order.
1078
+ const [ selectionStart, selectionEnd ] =
1079
+ anchorIndex > focusIndex
1080
+ ? [ selectionFocus, selectionAnchor ]
1081
+ : [ selectionAnchor, selectionFocus ];
1082
+
1083
+ const blockA = getBlock( state, selectionStart.clientId );
1084
+ const blockAType = getBlockType( blockA.name );
1085
+
1086
+ const blockB = getBlock( state, selectionEnd.clientId );
1087
+ const blockBType = getBlockType( blockB.name );
1088
+
1089
+ const htmlA = blockA.attributes[ selectionStart.attributeKey ];
1090
+ const htmlB = blockB.attributes[ selectionEnd.attributeKey ];
1091
+
1092
+ const attributeDefinitionA =
1093
+ blockAType.attributes[ selectionStart.attributeKey ];
1094
+ const attributeDefinitionB =
1095
+ blockBType.attributes[ selectionEnd.attributeKey ];
1096
+
1097
+ let valueA = create( {
1098
+ html: htmlA,
1099
+ ...mapRichTextSettings( attributeDefinitionA ),
1100
+ } );
1101
+ let valueB = create( {
1102
+ html: htmlB,
1103
+ ...mapRichTextSettings( attributeDefinitionB ),
1104
+ } );
1105
+
1106
+ valueA = remove( valueA, 0, selectionStart.offset );
1107
+ valueB = remove( valueB, selectionEnd.offset, valueB.text.length );
1108
+
1109
+ return [
1110
+ {
1111
+ ...blockA,
1112
+ attributes: {
1113
+ ...blockA.attributes,
1114
+ [ selectionStart.attributeKey ]: toHTMLString( {
1115
+ value: valueA,
1116
+ ...mapRichTextSettings( attributeDefinitionA ),
1117
+ } ),
1118
+ },
1119
+ },
1120
+ {
1121
+ ...blockB,
1122
+ attributes: {
1123
+ ...blockB.attributes,
1124
+ [ selectionEnd.attributeKey ]: toHTMLString( {
1125
+ value: valueB,
1126
+ ...mapRichTextSettings( attributeDefinitionB ),
1127
+ } ),
1128
+ },
1129
+ },
1130
+ ];
1131
+ };
1132
+
1007
1133
  /**
1008
1134
  * Returns an array containing all block client IDs in the editor in the order
1009
1135
  * they appear. Optionally accepts a root client ID of the block list for which
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Helper function that maps attribute definition properties to the
3
+ * ones used by RichText utils like `create, toHTMLString, etc..`.
4
+ *
5
+ * @param {Object} attributeDefinition A block's attribute definition object.
6
+ * @return {Object} The mapped object.
7
+ */
8
+ export function mapRichTextSettings( attributeDefinition ) {
9
+ const {
10
+ multiline: multilineTag,
11
+ __unstableMultilineWrapperTags: multilineWrapperTags,
12
+ __unstablePreserveWhiteSpace: preserveWhiteSpace,
13
+ } = attributeDefinition;
14
+ return {
15
+ multilineTag,
16
+ multilineWrapperTags,
17
+ preserveWhiteSpace,
18
+ };
19
+ }