@wordpress/block-editor 8.5.2 → 8.5.5

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 (64) hide show
  1. package/build/components/block-content-overlay/index.js +4 -13
  2. package/build/components/block-content-overlay/index.js.map +1 -1
  3. package/build/components/block-lock/modal.js +4 -34
  4. package/build/components/block-lock/modal.js.map +1 -1
  5. package/build/components/block-lock/toolbar.js +1 -2
  6. package/build/components/block-lock/toolbar.js.map +1 -1
  7. package/build/components/block-lock/use-block-lock.js +1 -4
  8. package/build/components/block-lock/use-block-lock.js.map +1 -1
  9. package/build/components/inserter/index.js +21 -7
  10. package/build/components/inserter/index.js.map +1 -1
  11. package/build/components/inserter/quick-inserter.js +4 -5
  12. package/build/components/inserter/quick-inserter.js.map +1 -1
  13. package/build/components/writing-flow/use-click-selection.js +1 -3
  14. package/build/components/writing-flow/use-click-selection.js.map +1 -1
  15. package/build/components/writing-flow/use-selection-observer.js +49 -8
  16. package/build/components/writing-flow/use-selection-observer.js.map +1 -1
  17. package/build/hooks/duotone.js +66 -16
  18. package/build/hooks/duotone.js.map +1 -1
  19. package/build/hooks/index.js +7 -1
  20. package/build/hooks/index.js.map +1 -1
  21. package/build/index.js +7 -0
  22. package/build/index.js.map +1 -1
  23. package/build/store/selectors.js +0 -24
  24. package/build/store/selectors.js.map +1 -1
  25. package/build-module/components/block-content-overlay/index.js +4 -13
  26. package/build-module/components/block-content-overlay/index.js.map +1 -1
  27. package/build-module/components/block-lock/modal.js +5 -34
  28. package/build-module/components/block-lock/modal.js.map +1 -1
  29. package/build-module/components/block-lock/toolbar.js +1 -2
  30. package/build-module/components/block-lock/toolbar.js.map +1 -1
  31. package/build-module/components/block-lock/use-block-lock.js +1 -4
  32. package/build-module/components/block-lock/use-block-lock.js.map +1 -1
  33. package/build-module/components/inserter/index.js +21 -7
  34. package/build-module/components/inserter/index.js.map +1 -1
  35. package/build-module/components/inserter/quick-inserter.js +4 -5
  36. package/build-module/components/inserter/quick-inserter.js.map +1 -1
  37. package/build-module/components/writing-flow/use-click-selection.js +1 -3
  38. package/build-module/components/writing-flow/use-click-selection.js.map +1 -1
  39. package/build-module/components/writing-flow/use-selection-observer.js +49 -8
  40. package/build-module/components/writing-flow/use-selection-observer.js.map +1 -1
  41. package/build-module/hooks/duotone.js +63 -16
  42. package/build-module/hooks/duotone.js.map +1 -1
  43. package/build-module/hooks/index.js +1 -0
  44. package/build-module/hooks/index.js.map +1 -1
  45. package/build-module/index.js +1 -1
  46. package/build-module/index.js.map +1 -1
  47. package/build-module/store/selectors.js +0 -22
  48. package/build-module/store/selectors.js.map +1 -1
  49. package/build-style/style-rtl.css +0 -1
  50. package/build-style/style.css +0 -1
  51. package/package.json +6 -6
  52. package/src/components/block-content-overlay/index.js +2 -19
  53. package/src/components/block-content-overlay/style.scss +0 -1
  54. package/src/components/block-lock/modal.js +3 -42
  55. package/src/components/block-lock/toolbar.js +2 -2
  56. package/src/components/block-lock/use-block-lock.js +1 -4
  57. package/src/components/inserter/index.js +20 -0
  58. package/src/components/inserter/quick-inserter.js +3 -11
  59. package/src/components/writing-flow/use-click-selection.js +1 -4
  60. package/src/components/writing-flow/use-selection-observer.js +55 -10
  61. package/src/hooks/duotone.js +98 -62
  62. package/src/hooks/index.js +1 -0
  63. package/src/index.js +1 -0
  64. package/src/store/selectors.js +0 -20
@@ -1018,7 +1018,6 @@
1018
1018
  border: none;
1019
1019
  border-radius: 2px;
1020
1020
  z-index: 10;
1021
- pointer-events: none;
1022
1021
  }
1023
1022
  .block-editor-block-content-overlay:hover:not(.is-dragging-blocks).overlay-active::before, .block-editor-block-content-overlay.parent-highlighted.overlay-active::before {
1024
1023
  background: rgba(var(--wp-admin-theme-color--rgb), 0.1);
@@ -1018,7 +1018,6 @@
1018
1018
  border: none;
1019
1019
  border-radius: 2px;
1020
1020
  z-index: 10;
1021
- pointer-events: none;
1022
1021
  }
1023
1022
  .block-editor-block-content-overlay:hover:not(.is-dragging-blocks).overlay-active::before, .block-editor-block-content-overlay.parent-highlighted.overlay-active::before {
1024
1023
  background: rgba(var(--wp-admin-theme-color--rgb), 0.1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "8.5.2",
3
+ "version": "8.5.5",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -36,8 +36,8 @@
36
36
  "@wordpress/a11y": "^3.6.1",
37
37
  "@wordpress/api-fetch": "^6.3.1",
38
38
  "@wordpress/blob": "^3.6.1",
39
- "@wordpress/blocks": "^11.5.2",
40
- "@wordpress/components": "^19.8.1",
39
+ "@wordpress/blocks": "^11.5.3",
40
+ "@wordpress/components": "^19.8.4",
41
41
  "@wordpress/compose": "^5.4.1",
42
42
  "@wordpress/data": "^6.6.1",
43
43
  "@wordpress/date": "^4.6.1",
@@ -47,12 +47,12 @@
47
47
  "@wordpress/hooks": "^3.6.1",
48
48
  "@wordpress/html-entities": "^3.6.1",
49
49
  "@wordpress/i18n": "^4.6.1",
50
- "@wordpress/icons": "^8.2.1",
50
+ "@wordpress/icons": "^8.2.2",
51
51
  "@wordpress/is-shallow-equal": "^4.6.1",
52
52
  "@wordpress/keyboard-shortcuts": "^3.4.1",
53
53
  "@wordpress/keycodes": "^3.6.1",
54
54
  "@wordpress/notices": "^3.6.1",
55
- "@wordpress/rich-text": "^5.4.1",
55
+ "@wordpress/rich-text": "^5.4.2",
56
56
  "@wordpress/shortcode": "^3.6.1",
57
57
  "@wordpress/style-engine": "^0.5.1",
58
58
  "@wordpress/token-list": "^2.6.1",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "446565ecaa40370173c18926535e975ec5652b71"
80
+ "gitHead": "b97f41a0716be45abd8bf897c7f135b86dc0760c"
81
81
  }
@@ -25,7 +25,6 @@ export default function BlockContentOverlay( {
25
25
  const [ isHovered, setIsHovered ] = useState( false );
26
26
 
27
27
  const {
28
- canEdit,
29
28
  isParentSelected,
30
29
  hasChildSelected,
31
30
  isDraggingBlocks,
@@ -37,10 +36,8 @@ export default function BlockContentOverlay( {
37
36
  hasSelectedInnerBlock,
38
37
  isDraggingBlocks: _isDraggingBlocks,
39
38
  isBlockHighlighted,
40
- canEditBlock,
41
39
  } = select( blockEditorStore );
42
40
  return {
43
- canEdit: canEditBlock( clientId ),
44
41
  isParentSelected: isBlockSelected( clientId ),
45
42
  hasChildSelected: hasSelectedInnerBlock( clientId, true ),
46
43
  isDraggingBlocks: _isDraggingBlocks(),
@@ -62,12 +59,6 @@ export default function BlockContentOverlay( {
62
59
  );
63
60
 
64
61
  useEffect( () => {
65
- // The overlay is always active when editing is locked.
66
- if ( ! canEdit ) {
67
- setIsOverlayActive( true );
68
- return;
69
- }
70
-
71
62
  // Reenable when blocks are not in use.
72
63
  if ( ! isParentSelected && ! hasChildSelected && ! isOverlayActive ) {
73
64
  setIsOverlayActive( true );
@@ -84,13 +75,7 @@ export default function BlockContentOverlay( {
84
75
  if ( hasChildSelected && isOverlayActive ) {
85
76
  setIsOverlayActive( false );
86
77
  }
87
- }, [
88
- isParentSelected,
89
- hasChildSelected,
90
- isOverlayActive,
91
- isHovered,
92
- canEdit,
93
- ] );
78
+ }, [ isParentSelected, hasChildSelected, isOverlayActive, isHovered ] );
94
79
 
95
80
  // Disabled because the overlay div doesn't actually have a role or functionality
96
81
  // as far as the a11y is concerned. We're just catching the first click so that
@@ -103,9 +88,7 @@ export default function BlockContentOverlay( {
103
88
  onMouseEnter={ () => setIsHovered( true ) }
104
89
  onMouseLeave={ () => setIsHovered( false ) }
105
90
  onMouseUp={
106
- isOverlayActive && canEdit
107
- ? () => setIsOverlayActive( false )
108
- : undefined
91
+ isOverlayActive ? () => setIsOverlayActive( false ) : undefined
109
92
  }
110
93
  >
111
94
  { wrapperProps?.children }
@@ -10,7 +10,6 @@
10
10
  border: none;
11
11
  border-radius: $radius-block-ui;
12
12
  z-index: z-index(".block-editor-block-content-overlay__overlay");
13
- pointer-events: none;
14
13
  }
15
14
 
16
15
  &:hover:not(.is-dragging-blocks).overlay-active::before,
@@ -13,8 +13,7 @@ import {
13
13
  } from '@wordpress/components';
14
14
  import { lock as lockIcon, unlock as unlockIcon } from '@wordpress/icons';
15
15
  import { useInstanceId } from '@wordpress/compose';
16
- import { useDispatch, useSelect } from '@wordpress/data';
17
- import { isReusableBlock, getBlockType } from '@wordpress/blocks';
16
+ import { useDispatch } from '@wordpress/data';
18
17
 
19
18
  /**
20
19
  * Internal dependencies
@@ -25,18 +24,7 @@ import { store as blockEditorStore } from '../../store';
25
24
 
26
25
  export default function BlockLockModal( { clientId, onClose } ) {
27
26
  const [ lock, setLock ] = useState( { move: false, remove: false } );
28
- const { canEdit, canMove, canRemove } = useBlockLock( clientId );
29
- const { isReusable } = useSelect(
30
- ( select ) => {
31
- const { getBlockName } = select( blockEditorStore );
32
- const blockName = getBlockName( clientId );
33
-
34
- return {
35
- isReusable: isReusableBlock( getBlockType( blockName ) ),
36
- };
37
- },
38
- [ clientId ]
39
- );
27
+ const { canMove, canRemove } = useBlockLock( clientId );
40
28
  const { updateBlockAttributes } = useDispatch( blockEditorStore );
41
29
  const blockInformation = useBlockDisplayInformation( clientId );
42
30
  const instanceId = useInstanceId(
@@ -48,9 +36,8 @@ export default function BlockLockModal( { clientId, onClose } ) {
48
36
  setLock( {
49
37
  move: ! canMove,
50
38
  remove: ! canRemove,
51
- ...( isReusable ? { edit: ! canEdit } : {} ),
52
39
  } );
53
- }, [ canEdit, canMove, canRemove, isReusable ] );
40
+ }, [ canMove, canRemove ] );
54
41
 
55
42
  const isAllChecked = Object.values( lock ).every( Boolean );
56
43
  const isMixed = Object.values( lock ).some( Boolean ) && ! isAllChecked;
@@ -94,36 +81,10 @@ export default function BlockLockModal( { clientId, onClose } ) {
94
81
  setLock( {
95
82
  move: newValue,
96
83
  remove: newValue,
97
- ...( isReusable ? { edit: newValue } : {} ),
98
84
  } )
99
85
  }
100
86
  />
101
87
  <ul className="block-editor-block-lock-modal__checklist">
102
- { isReusable && (
103
- <li className="block-editor-block-lock-modal__checklist-item">
104
- <CheckboxControl
105
- label={
106
- <>
107
- { __( 'Restrict editing' ) }
108
- <Icon
109
- icon={
110
- lock.edit
111
- ? lockIcon
112
- : unlockIcon
113
- }
114
- />
115
- </>
116
- }
117
- checked={ !! lock.edit }
118
- onChange={ ( edit ) =>
119
- setLock( ( prevLock ) => ( {
120
- ...prevLock,
121
- edit,
122
- } ) )
123
- }
124
- />
125
- </li>
126
- ) }
127
88
  <li className="block-editor-block-lock-modal__checklist-item">
128
89
  <CheckboxControl
129
90
  label={
@@ -15,7 +15,7 @@ import useBlockDisplayInformation from '../use-block-display-information';
15
15
 
16
16
  export default function BlockLockToolbar( { clientId } ) {
17
17
  const blockInformation = useBlockDisplayInformation( clientId );
18
- const { canEdit, canMove, canRemove, canLock } = useBlockLock( clientId );
18
+ const { canMove, canRemove, canLock } = useBlockLock( clientId );
19
19
 
20
20
  const [ isModalOpen, toggleModal ] = useReducer(
21
21
  ( isActive ) => ! isActive,
@@ -26,7 +26,7 @@ export default function BlockLockToolbar( { clientId } ) {
26
26
  return null;
27
27
  }
28
28
 
29
- if ( canEdit && canMove && canRemove ) {
29
+ if ( canMove && canRemove ) {
30
30
  return null;
31
31
  }
32
32
 
@@ -19,7 +19,6 @@ export default function useBlockLock( clientId ) {
19
19
  return useSelect(
20
20
  ( select ) => {
21
21
  const {
22
- canEditBlock,
23
22
  canMoveBlock,
24
23
  canRemoveBlock,
25
24
  canLockBlockType,
@@ -28,16 +27,14 @@ export default function useBlockLock( clientId ) {
28
27
  } = select( blockEditorStore );
29
28
  const rootClientId = getBlockRootClientId( clientId );
30
29
 
31
- const canEdit = canEditBlock( clientId );
32
30
  const canMove = canMoveBlock( clientId, rootClientId );
33
31
  const canRemove = canRemoveBlock( clientId, rootClientId );
34
32
 
35
33
  return {
36
- canEdit,
37
34
  canMove,
38
35
  canRemove,
39
36
  canLock: canLockBlockType( getBlockName( clientId ) ),
40
- isLocked: ! canEdit || ! canMove || ! canRemove,
37
+ isLocked: ! canMove || ! canRemove,
41
38
  };
42
39
  },
43
40
  [ clientId ]
@@ -30,6 +30,7 @@ const defaultRenderToggle = ( {
30
30
  blockTitle,
31
31
  hasSingleBlockType,
32
32
  toggleProps = {},
33
+ prioritizePatterns,
33
34
  } ) => {
34
35
  let label;
35
36
  if ( hasSingleBlockType ) {
@@ -38,6 +39,8 @@ const defaultRenderToggle = ( {
38
39
  _x( 'Add %s', 'directly add the only allowed block' ),
39
40
  blockTitle
40
41
  );
42
+ } else if ( prioritizePatterns ) {
43
+ label = __( 'Add pattern' );
41
44
  } else {
42
45
  label = _x( 'Add block', 'Generic label for block inserter button' );
43
46
  }
@@ -106,6 +109,7 @@ class Inserter extends Component {
106
109
  toggleProps,
107
110
  hasItems,
108
111
  renderToggle = defaultRenderToggle,
112
+ prioritizePatterns,
109
113
  } = this.props;
110
114
 
111
115
  return renderToggle( {
@@ -116,6 +120,7 @@ class Inserter extends Component {
116
120
  hasSingleBlockType,
117
121
  directInsertBlock,
118
122
  toggleProps,
123
+ prioritizePatterns,
119
124
  } );
120
125
  }
121
126
 
@@ -138,6 +143,7 @@ class Inserter extends Component {
138
143
  // This prop is experimental to give some time for the quick inserter to mature
139
144
  // Feel free to make them stable after a few releases.
140
145
  __experimentalIsQuick: isQuick,
146
+ prioritizePatterns,
141
147
  } = this.props;
142
148
 
143
149
  if ( isQuick ) {
@@ -149,6 +155,7 @@ class Inserter extends Component {
149
155
  rootClientId={ rootClientId }
150
156
  clientId={ clientId }
151
157
  isAppender={ isAppender }
158
+ prioritizePatterns={ prioritizePatterns }
152
159
  />
153
160
  );
154
161
  }
@@ -206,7 +213,11 @@ export default compose( [
206
213
  hasInserterItems,
207
214
  __experimentalGetAllowedBlocks,
208
215
  __experimentalGetDirectInsertBlock,
216
+ getBlockIndex,
217
+ getBlockCount,
218
+ getSettings,
209
219
  } = select( blockEditorStore );
220
+
210
221
  const { getBlockVariations } = select( blocksStore );
211
222
 
212
223
  rootClientId =
@@ -218,6 +229,10 @@ export default compose( [
218
229
  rootClientId
219
230
  );
220
231
 
232
+ const index = getBlockIndex( clientId );
233
+ const blockCount = getBlockCount();
234
+ const settings = getSettings();
235
+
221
236
  const hasSingleBlockType =
222
237
  size( allowedBlocks ) === 1 &&
223
238
  size(
@@ -236,6 +251,11 @@ export default compose( [
236
251
  allowedBlockType,
237
252
  directInsertBlock,
238
253
  rootClientId,
254
+ prioritizePatterns:
255
+ settings.__experimentalPreferPatternsOnRoot &&
256
+ ! rootClientId &&
257
+ index > 0 &&
258
+ ( index < blockCount || blockCount === 0 ),
239
259
  };
240
260
  } ),
241
261
  withDispatch( ( dispatch, ownProps, { select } ) => {
@@ -30,6 +30,7 @@ export default function QuickInserter( {
30
30
  rootClientId,
31
31
  clientId,
32
32
  isAppender,
33
+ prioritizePatterns,
33
34
  } ) {
34
35
  const [ filterValue, setFilterValue ] = useState( '' );
35
36
  const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( {
@@ -48,11 +49,7 @@ export default function QuickInserter( {
48
49
  destinationRootClientId
49
50
  );
50
51
 
51
- const {
52
- setInserterIsOpened,
53
- insertionIndex,
54
- prioritizePatterns,
55
- } = useSelect(
52
+ const { setInserterIsOpened, insertionIndex } = useSelect(
56
53
  ( select ) => {
57
54
  const { getSettings, getBlockIndex, getBlockCount } = select(
58
55
  blockEditorStore
@@ -63,15 +60,10 @@ export default function QuickInserter( {
63
60
 
64
61
  return {
65
62
  setInserterIsOpened: settings.__experimentalSetIsInserterOpened,
66
- prioritizePatterns:
67
- settings.__experimentalPreferPatternsOnRoot &&
68
- ! rootClientId &&
69
- index > 0 &&
70
- ( index < blockCount || blockCount === 0 ),
71
63
  insertionIndex: index === -1 ? blockCount : index,
72
64
  };
73
65
  },
74
- [ clientId, rootClientId ]
66
+ [ clientId ]
75
67
  );
76
68
 
77
69
  const showPatterns =
@@ -11,10 +11,9 @@ import { store as blockEditorStore } from '../../store';
11
11
  import { getBlockClientId } from '../../utils/dom';
12
12
 
13
13
  export default function useClickSelection() {
14
- const { multiSelect, selectBlock } = useDispatch( blockEditorStore );
14
+ const { selectBlock } = useDispatch( blockEditorStore );
15
15
  const {
16
16
  isSelectionEnabled,
17
- getBlockParents,
18
17
  getBlockSelectionStart,
19
18
  hasMultiSelection,
20
19
  } = useSelect( blockEditorStore );
@@ -54,10 +53,8 @@ export default function useClickSelection() {
54
53
  };
55
54
  },
56
55
  [
57
- multiSelect,
58
56
  selectBlock,
59
57
  isSelectionEnabled,
60
- getBlockParents,
61
58
  getBlockSelectionStart,
62
59
  hasMultiSelection,
63
60
  ]
@@ -76,36 +76,81 @@ export default function useSelectionObserver() {
76
76
  const { multiSelect, selectBlock, selectionChange } = useDispatch(
77
77
  blockEditorStore
78
78
  );
79
- const { getBlockParents } = useSelect( blockEditorStore );
79
+ const { getBlockParents, getBlockSelectionStart } = useSelect(
80
+ blockEditorStore
81
+ );
80
82
  return useRefEffect(
81
83
  ( node ) => {
82
84
  const { ownerDocument } = node;
83
85
  const { defaultView } = ownerDocument;
84
86
 
85
- function onSelectionChange() {
87
+ function onSelectionChange( event ) {
86
88
  const selection = defaultView.getSelection();
87
-
88
89
  // If no selection is found, end multi selection and disable the
89
90
  // contentEditable wrapper.
90
- if ( ! selection.rangeCount || selection.isCollapsed ) {
91
+ if ( ! selection.rangeCount ) {
92
+ setContentEditableWrapper( node, false );
93
+ return;
94
+ }
95
+ // If selection is collapsed and we haven't used `shift+click`,
96
+ // end multi selection and disable the contentEditable wrapper.
97
+ // We have to check about `shift+click` case because elements
98
+ // that don't support text selection might be involved, and we might
99
+ // update the clientIds to multi-select blocks.
100
+ // For now we check if the event is a `mouse` event.
101
+ const isClickShift = event.shiftKey && event.type === 'mouseup';
102
+ if ( selection.isCollapsed && ! isClickShift ) {
91
103
  setContentEditableWrapper( node, false );
92
104
  return;
93
105
  }
94
106
 
95
- const clientId = getBlockClientId(
107
+ let startClientId = getBlockClientId(
96
108
  extractSelectionStartNode( selection )
97
109
  );
98
- const endClientId = getBlockClientId(
110
+ let endClientId = getBlockClientId(
99
111
  extractSelectionEndNode( selection )
100
112
  );
101
- const isSingularSelection = clientId === endClientId;
113
+ // If the selection has changed and we had pressed `shift+click`,
114
+ // we need to check if in an element that doesn't support
115
+ // text selection has been clicked.
116
+ if ( isClickShift ) {
117
+ const selectedClientId = getBlockSelectionStart();
118
+ const clickedClientId = getBlockClientId( event.target );
119
+ // `endClientId` is not defined if we end the selection by clicking a non-selectable block.
120
+ // We need to check if there was already a selection with a non-selectable focusNode.
121
+ const focusNodeIsNonSelectable =
122
+ clickedClientId !== endClientId;
123
+ if (
124
+ ( startClientId === endClientId &&
125
+ selection.isCollapsed ) ||
126
+ ! endClientId ||
127
+ focusNodeIsNonSelectable
128
+ ) {
129
+ endClientId = clickedClientId;
130
+ }
131
+ // Handle the case when we have a non-selectable block
132
+ // selected and click another one.
133
+ if ( startClientId !== selectedClientId ) {
134
+ startClientId = selectedClientId;
135
+ }
136
+ }
137
+
138
+ // If the selection did not involve a block, return.
139
+ if (
140
+ startClientId === undefined &&
141
+ endClientId === undefined
142
+ ) {
143
+ setContentEditableWrapper( node, false );
144
+ return;
145
+ }
102
146
 
147
+ const isSingularSelection = startClientId === endClientId;
103
148
  if ( isSingularSelection ) {
104
- selectBlock( clientId );
149
+ selectBlock( startClientId );
105
150
  } else {
106
151
  const startPath = [
107
- ...getBlockParents( clientId ),
108
- clientId,
152
+ ...getBlockParents( startClientId ),
153
+ startClientId,
109
154
  ];
110
155
  const endPath = [
111
156
  ...getBlockParents( endClientId ),
@@ -60,82 +60,109 @@ export function getValuesFromColors( colors = [] ) {
60
60
  */
61
61
 
62
62
  /**
63
- * SVG and stylesheet needed for rendering the duotone filter.
63
+ * Stylesheet for rendering the duotone filter.
64
64
  *
65
65
  * @param {Object} props Duotone props.
66
66
  * @param {string} props.selector Selector to apply the filter to.
67
67
  * @param {string} props.id Unique id for this duotone filter.
68
- * @param {Values} props.values R, G, B, and A values to filter with.
69
68
  *
70
69
  * @return {WPElement} Duotone element.
71
70
  */
72
- function DuotoneFilter( { selector, id, values } ) {
73
- const stylesheet = `
71
+ function DuotoneStylesheet( { selector, id } ) {
72
+ const css = `
74
73
  ${ selector } {
75
74
  filter: url( #${ id } );
76
75
  }
77
76
  `;
77
+ return <style>{ css }</style>;
78
+ }
78
79
 
80
+ /**
81
+ * SVG for rendering the duotone filter.
82
+ *
83
+ * @param {Object} props Duotone props.
84
+ * @param {string} props.id Unique id for this duotone filter.
85
+ * @param {Values} props.values R, G, B, and A values to filter with.
86
+ *
87
+ * @return {WPElement} Duotone element.
88
+ */
89
+ function DuotoneFilter( { id, values } ) {
79
90
  return (
80
- <>
81
- <SVG
82
- xmlnsXlink="http://www.w3.org/1999/xlink"
83
- viewBox="0 0 0 0"
84
- width="0"
85
- height="0"
86
- focusable="false"
87
- role="none"
88
- style={ {
89
- visibility: 'hidden',
90
- position: 'absolute',
91
- left: '-9999px',
92
- overflow: 'hidden',
93
- } }
94
- >
95
- <defs>
96
- <filter id={ id }>
97
- <feColorMatrix
98
- // Use sRGB instead of linearRGB so transparency looks correct.
99
- colorInterpolationFilters="sRGB"
100
- type="matrix"
101
- // Use perceptual brightness to convert to grayscale.
102
- values="
103
- .299 .587 .114 0 0
104
- .299 .587 .114 0 0
105
- .299 .587 .114 0 0
106
- .299 .587 .114 0 0
107
- "
91
+ <SVG
92
+ xmlnsXlink="http://www.w3.org/1999/xlink"
93
+ viewBox="0 0 0 0"
94
+ width="0"
95
+ height="0"
96
+ focusable="false"
97
+ role="none"
98
+ style={ {
99
+ visibility: 'hidden',
100
+ position: 'absolute',
101
+ left: '-9999px',
102
+ overflow: 'hidden',
103
+ } }
104
+ >
105
+ <defs>
106
+ <filter id={ id }>
107
+ <feColorMatrix
108
+ // Use sRGB instead of linearRGB so transparency looks correct.
109
+ colorInterpolationFilters="sRGB"
110
+ type="matrix"
111
+ // Use perceptual brightness to convert to grayscale.
112
+ values="
113
+ .299 .587 .114 0 0
114
+ .299 .587 .114 0 0
115
+ .299 .587 .114 0 0
116
+ .299 .587 .114 0 0
117
+ "
118
+ />
119
+ <feComponentTransfer
120
+ // Use sRGB instead of linearRGB to be consistent with how CSS gradients work.
121
+ colorInterpolationFilters="sRGB"
122
+ >
123
+ <feFuncR
124
+ type="table"
125
+ tableValues={ values.r.join( ' ' ) }
126
+ />
127
+ <feFuncG
128
+ type="table"
129
+ tableValues={ values.g.join( ' ' ) }
130
+ />
131
+ <feFuncB
132
+ type="table"
133
+ tableValues={ values.b.join( ' ' ) }
108
134
  />
109
- <feComponentTransfer
110
- // Use sRGB instead of linearRGB to be consistent with how CSS gradients work.
111
- colorInterpolationFilters="sRGB"
112
- >
113
- <feFuncR
114
- type="table"
115
- tableValues={ values.r.join( ' ' ) }
116
- />
117
- <feFuncG
118
- type="table"
119
- tableValues={ values.g.join( ' ' ) }
120
- />
121
- <feFuncB
122
- type="table"
123
- tableValues={ values.b.join( ' ' ) }
124
- />
125
- <feFuncA
126
- type="table"
127
- tableValues={ values.a.join( ' ' ) }
128
- />
129
- </feComponentTransfer>
130
- <feComposite
131
- // Re-mask the image with the original transparency since the feColorMatrix above loses that information.
132
- in2="SourceGraphic"
133
- operator="in"
135
+ <feFuncA
136
+ type="table"
137
+ tableValues={ values.a.join( ' ' ) }
134
138
  />
135
- </filter>
136
- </defs>
137
- </SVG>
138
- <style dangerouslySetInnerHTML={ { __html: stylesheet } } />
139
+ </feComponentTransfer>
140
+ <feComposite
141
+ // Re-mask the image with the original transparency since the feColorMatrix above loses that information.
142
+ in2="SourceGraphic"
143
+ operator="in"
144
+ />
145
+ </filter>
146
+ </defs>
147
+ </SVG>
148
+ );
149
+ }
150
+
151
+ /**
152
+ * SVG and stylesheet needed for rendering the duotone filter.
153
+ *
154
+ * @param {Object} props Duotone props.
155
+ * @param {string} props.selector Selector to apply the filter to.
156
+ * @param {string} props.id Unique id for this duotone filter.
157
+ * @param {Values} props.values R, G, B, and A values to filter with.
158
+ *
159
+ * @return {WPElement} Duotone element.
160
+ */
161
+ function InlineDuotone( { selector, id, values } ) {
162
+ return (
163
+ <>
164
+ <DuotoneFilter id={ id } values={ values } />
165
+ <DuotoneStylesheet id={ id } selector={ selector } />
139
166
  </>
140
167
  );
141
168
  }
@@ -321,7 +348,7 @@ const withDuotoneStyles = createHigherOrderComponent(
321
348
  <>
322
349
  { element &&
323
350
  createPortal(
324
- <DuotoneFilter
351
+ <InlineDuotone
325
352
  selector={ selectorsGroup }
326
353
  id={ id }
327
354
  values={ getValuesFromColors( values ) }
@@ -335,6 +362,15 @@ const withDuotoneStyles = createHigherOrderComponent(
335
362
  'withDuotoneStyles'
336
363
  );
337
364
 
365
+ export function PresetDuotoneFilter( { preset } ) {
366
+ return (
367
+ <DuotoneFilter
368
+ id={ `wp-duotone-${ preset.slug }` }
369
+ values={ getValuesFromColors( preset.colors ) }
370
+ />
371
+ );
372
+ }
373
+
338
374
  addFilter(
339
375
  'blocks.registerBlockType',
340
376
  'core/editor/duotone/add-attributes',
@@ -19,3 +19,4 @@ export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
19
19
  export { getColorClassesAndStyles, useColorProps } from './use-color-props';
20
20
  export { getSpacingClassesAndStyles } from './use-spacing-props';
21
21
  export { useCachedTruthy } from './use-cached-truthy';
22
+ export { PresetDuotoneFilter } from './duotone';