@wordpress/block-editor 9.1.0 → 9.2.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 (170) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-draggable/draggable-chip.native.js +2 -1
  3. package/build/components/block-draggable/draggable-chip.native.js.map +1 -1
  4. package/build/components/block-draggable/index.native.js +7 -3
  5. package/build/components/block-draggable/index.native.js.map +1 -1
  6. package/build/components/block-list/block.native.js +2 -1
  7. package/build/components/block-list/block.native.js.map +1 -1
  8. package/build/components/block-list/index.js +34 -32
  9. package/build/components/block-list/index.js.map +1 -1
  10. package/build/components/block-mobile-toolbar/index.native.js +2 -1
  11. package/build/components/block-mobile-toolbar/index.native.js.map +1 -1
  12. package/build/components/block-popover/inbetween.js +9 -6
  13. package/build/components/block-popover/inbetween.js.map +1 -1
  14. package/build/components/block-popover/index.js +2 -1
  15. package/build/components/block-popover/index.js.map +1 -1
  16. package/build/components/block-tools/block-selection-button.js +1 -0
  17. package/build/components/block-tools/block-selection-button.js.map +1 -1
  18. package/build/components/block-variation-transforms/index.js +16 -2
  19. package/build/components/block-variation-transforms/index.js.map +1 -1
  20. package/build/components/colors-gradients/dropdown.js +70 -94
  21. package/build/components/colors-gradients/dropdown.js.map +1 -1
  22. package/build/components/colors-gradients/panel-color-gradient-settings.js +35 -60
  23. package/build/components/colors-gradients/panel-color-gradient-settings.js.map +1 -1
  24. package/build/components/convert-to-group-buttons/toolbar.js +22 -5
  25. package/build/components/convert-to-group-buttons/toolbar.js.map +1 -1
  26. package/build/components/index.js +9 -0
  27. package/build/components/index.js.map +1 -1
  28. package/build/components/media-placeholder/index.js +0 -2
  29. package/build/components/media-placeholder/index.js.map +1 -1
  30. package/build/components/media-replace-flow/index.js +0 -2
  31. package/build/components/media-replace-flow/index.js.map +1 -1
  32. package/build/components/media-upload/index.native.js +10 -4
  33. package/build/components/media-upload/index.native.js.map +1 -1
  34. package/build/components/plain-text/index.native.js +62 -7
  35. package/build/components/plain-text/index.native.js.map +1 -1
  36. package/build/components/publish-date-time-picker/index.js +55 -0
  37. package/build/components/publish-date-time-picker/index.js.map +1 -0
  38. package/build/components/rich-text/index.js +1 -1
  39. package/build/components/rich-text/index.js.map +1 -1
  40. package/build/components/rich-text/index.native.js +5 -1
  41. package/build/components/rich-text/index.native.js.map +1 -1
  42. package/build/components/writing-flow/use-input.js +15 -0
  43. package/build/components/writing-flow/use-input.js.map +1 -1
  44. package/build/elements/index.js +9 -0
  45. package/build/elements/index.js.map +1 -0
  46. package/build/hooks/color.js +8 -88
  47. package/build/hooks/color.js.map +1 -1
  48. package/build/hooks/dimensions.js +14 -4
  49. package/build/hooks/dimensions.js.map +1 -1
  50. package/build/index.js +14 -0
  51. package/build/index.js.map +1 -1
  52. package/build/layouts/flex.js +5 -2
  53. package/build/layouts/flex.js.map +1 -1
  54. package/build/store/actions.js +14 -0
  55. package/build/store/actions.js.map +1 -1
  56. package/build/store/reducer.js +17 -2
  57. package/build/store/reducer.js.map +1 -1
  58. package/build/store/selectors.js +29 -1
  59. package/build/store/selectors.js.map +1 -1
  60. package/build-module/components/block-draggable/draggable-chip.native.js +2 -1
  61. package/build-module/components/block-draggable/draggable-chip.native.js.map +1 -1
  62. package/build-module/components/block-draggable/index.native.js +7 -3
  63. package/build-module/components/block-draggable/index.native.js.map +1 -1
  64. package/build-module/components/block-list/block.native.js +2 -1
  65. package/build-module/components/block-list/block.native.js.map +1 -1
  66. package/build-module/components/block-list/index.js +35 -33
  67. package/build-module/components/block-list/index.js.map +1 -1
  68. package/build-module/components/block-mobile-toolbar/index.native.js +2 -1
  69. package/build-module/components/block-mobile-toolbar/index.native.js.map +1 -1
  70. package/build-module/components/block-popover/inbetween.js +9 -6
  71. package/build-module/components/block-popover/inbetween.js.map +1 -1
  72. package/build-module/components/block-popover/index.js +2 -1
  73. package/build-module/components/block-popover/index.js.map +1 -1
  74. package/build-module/components/block-tools/block-selection-button.js +1 -0
  75. package/build-module/components/block-tools/block-selection-button.js.map +1 -1
  76. package/build-module/components/block-variation-transforms/index.js +13 -2
  77. package/build-module/components/block-variation-transforms/index.js.map +1 -1
  78. package/build-module/components/colors-gradients/dropdown.js +72 -96
  79. package/build-module/components/colors-gradients/dropdown.js.map +1 -1
  80. package/build-module/components/colors-gradients/panel-color-gradient-settings.js +36 -64
  81. package/build-module/components/colors-gradients/panel-color-gradient-settings.js.map +1 -1
  82. package/build-module/components/convert-to-group-buttons/toolbar.js +23 -6
  83. package/build-module/components/convert-to-group-buttons/toolbar.js.map +1 -1
  84. package/build-module/components/index.js +1 -0
  85. package/build-module/components/index.js.map +1 -1
  86. package/build-module/components/media-placeholder/index.js +0 -2
  87. package/build-module/components/media-placeholder/index.js.map +1 -1
  88. package/build-module/components/media-replace-flow/index.js +0 -2
  89. package/build-module/components/media-replace-flow/index.js.map +1 -1
  90. package/build-module/components/media-upload/index.native.js +8 -3
  91. package/build-module/components/media-upload/index.native.js.map +1 -1
  92. package/build-module/components/plain-text/index.native.js +63 -8
  93. package/build-module/components/plain-text/index.native.js.map +1 -1
  94. package/build-module/components/publish-date-time-picker/index.js +42 -0
  95. package/build-module/components/publish-date-time-picker/index.js.map +1 -0
  96. package/build-module/components/rich-text/index.js +1 -1
  97. package/build-module/components/rich-text/index.js.map +1 -1
  98. package/build-module/components/rich-text/index.native.js +5 -1
  99. package/build-module/components/rich-text/index.native.js.map +1 -1
  100. package/build-module/components/writing-flow/use-input.js +15 -0
  101. package/build-module/components/writing-flow/use-input.js.map +1 -1
  102. package/build-module/elements/index.js +2 -0
  103. package/build-module/elements/index.js.map +1 -0
  104. package/build-module/hooks/color.js +8 -88
  105. package/build-module/hooks/color.js.map +1 -1
  106. package/build-module/hooks/dimensions.js +14 -4
  107. package/build-module/hooks/dimensions.js.map +1 -1
  108. package/build-module/index.js +1 -0
  109. package/build-module/index.js.map +1 -1
  110. package/build-module/layouts/flex.js +4 -2
  111. package/build-module/layouts/flex.js.map +1 -1
  112. package/build-module/store/actions.js +12 -0
  113. package/build-module/store/actions.js.map +1 -1
  114. package/build-module/store/reducer.js +17 -2
  115. package/build-module/store/reducer.js.map +1 -1
  116. package/build-module/store/selectors.js +23 -0
  117. package/build-module/store/selectors.js.map +1 -1
  118. package/build-style/style-rtl.css +37 -34
  119. package/build-style/style.css +37 -34
  120. package/package.json +30 -30
  121. package/src/components/block-draggable/draggable-chip.native.js +1 -1
  122. package/src/components/block-draggable/index.native.js +4 -0
  123. package/src/components/block-draggable/test/__snapshots__/index.native.js.snap +73 -0
  124. package/src/components/block-draggable/test/helpers.native.js +183 -0
  125. package/src/components/block-draggable/test/index.native.js +496 -0
  126. package/src/components/block-list/block.native.js +1 -0
  127. package/src/components/block-list/index.js +44 -44
  128. package/src/components/block-mobile-toolbar/index.native.js +1 -0
  129. package/src/components/block-mover/test/__snapshots__/index.native.js.snap +4 -0
  130. package/src/components/block-popover/inbetween.js +12 -7
  131. package/src/components/block-popover/index.js +1 -0
  132. package/src/components/block-tools/block-selection-button.js +1 -0
  133. package/src/components/block-variation-transforms/index.js +6 -2
  134. package/src/components/colors-gradients/dropdown.js +35 -61
  135. package/src/components/colors-gradients/panel-color-gradient-settings.js +30 -76
  136. package/src/components/colors-gradients/style.scss +11 -37
  137. package/src/components/convert-to-group-buttons/toolbar.js +30 -13
  138. package/src/components/index.js +1 -0
  139. package/src/components/media-placeholder/index.js +0 -2
  140. package/src/components/media-replace-flow/index.js +0 -2
  141. package/src/components/media-upload/index.native.js +6 -2
  142. package/src/components/media-upload/test/index.native.js +31 -6
  143. package/src/components/plain-text/index.native.js +64 -8
  144. package/src/components/publish-date-time-picker/README.md +52 -0
  145. package/src/components/publish-date-time-picker/index.js +50 -0
  146. package/src/components/publish-date-time-picker/style.scss +20 -0
  147. package/src/components/rich-text/index.js +2 -0
  148. package/src/components/rich-text/index.native.js +4 -0
  149. package/src/components/writing-flow/use-input.js +12 -0
  150. package/src/elements/index.js +1 -0
  151. package/src/hooks/color.js +5 -74
  152. package/src/hooks/color.scss +9 -0
  153. package/src/hooks/dimensions.js +11 -3
  154. package/src/index.js +1 -0
  155. package/src/layouts/flex.js +11 -3
  156. package/src/store/actions.js +12 -0
  157. package/src/store/reducer.js +14 -1
  158. package/src/store/selectors.js +28 -0
  159. package/src/store/test/reducer.js +5 -0
  160. package/src/style.scss +1 -0
  161. package/build/components/colors/color-panel.js +0 -82
  162. package/build/components/colors/color-panel.js.map +0 -1
  163. package/build/components/colors/color-panel.native.js +0 -11
  164. package/build/components/colors/color-panel.native.js.map +0 -1
  165. package/build-module/components/colors/color-panel.js +0 -70
  166. package/build-module/components/colors/color-panel.js.map +0 -1
  167. package/build-module/components/colors/color-panel.native.js +0 -4
  168. package/build-module/components/colors/color-panel.native.js.map +0 -1
  169. package/src/components/colors/color-panel.js +0 -91
  170. package/src/components/colors/color-panel.native.js +0 -3
@@ -20,6 +20,7 @@ import {
20
20
  OPTION_TAKE_VIDEO,
21
21
  OPTION_TAKE_PHOTO,
22
22
  OPTION_INSERT_FROM_URL,
23
+ OPTION_WORDPRESS_MEDIA_LIBRARY,
23
24
  } from '../index';
24
25
 
25
26
  const MEDIA_URL = 'http://host.media.type';
@@ -33,8 +34,11 @@ describe( 'MediaUpload component', () => {
33
34
  expect( wrapper ).toBeTruthy();
34
35
  } );
35
36
 
36
- it( 'shows right media capture option for media type', () => {
37
- const expectOptionForMediaType = ( mediaType, expectedOption ) => {
37
+ describe( 'Media capture options for different media block types', () => {
38
+ const expectOptionForMediaType = async (
39
+ mediaType,
40
+ expectedOptions
41
+ ) => {
38
42
  const wrapper = render(
39
43
  <MediaUpload
40
44
  allowedTypes={ [ mediaType ] }
@@ -52,11 +56,32 @@ describe( 'MediaUpload component', () => {
52
56
  );
53
57
  fireEvent.press( wrapper.getByText( 'Open Picker' ) );
54
58
 
55
- wrapper.getByText( expectedOption );
59
+ expectedOptions.forEach( ( item ) => {
60
+ const option = wrapper.getByText( item );
61
+ expect( option ).toBeVisible();
62
+ } );
56
63
  };
57
- expectOptionForMediaType( MEDIA_TYPE_IMAGE, OPTION_TAKE_PHOTO );
58
- expectOptionForMediaType( MEDIA_TYPE_VIDEO, OPTION_TAKE_VIDEO );
59
- expectOptionForMediaType( MEDIA_TYPE_AUDIO, OPTION_INSERT_FROM_URL );
64
+
65
+ it( 'shows the correct media capture options for the Image block', () => {
66
+ expectOptionForMediaType( MEDIA_TYPE_IMAGE, [
67
+ OPTION_TAKE_PHOTO,
68
+ OPTION_WORDPRESS_MEDIA_LIBRARY,
69
+ OPTION_INSERT_FROM_URL,
70
+ ] );
71
+ } );
72
+
73
+ it( 'shows the correct media capture options for the Video block', () => {
74
+ expectOptionForMediaType( MEDIA_TYPE_VIDEO, [
75
+ OPTION_TAKE_VIDEO,
76
+ OPTION_WORDPRESS_MEDIA_LIBRARY,
77
+ ] );
78
+ } );
79
+
80
+ it( 'shows the correct media capture options for the Audio block', () => {
81
+ expectOptionForMediaType( MEDIA_TYPE_AUDIO, [
82
+ OPTION_INSERT_FROM_URL,
83
+ ] );
84
+ } );
60
85
  } );
61
86
 
62
87
  const expectMediaPickerForOption = (
@@ -7,7 +7,7 @@ import { TextInput, Platform, Dimensions } from 'react-native';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { Component } from '@wordpress/element';
10
- import { getPxFromCssUnit } from '@wordpress/block-editor';
10
+ import { RichText, getPxFromCssUnit } from '@wordpress/block-editor';
11
11
 
12
12
  /**
13
13
  * Internal dependencies
@@ -18,6 +18,9 @@ export default class PlainText extends Component {
18
18
  constructor() {
19
19
  super( ...arguments );
20
20
  this.isAndroid = Platform.OS === 'android';
21
+
22
+ this.onChangeTextInput = this.onChangeTextInput.bind( this );
23
+ this.onChangeRichText = this.onChangeRichText.bind( this );
21
24
  }
22
25
 
23
26
  componentDidMount() {
@@ -44,7 +47,7 @@ export default class PlainText extends Component {
44
47
 
45
48
  componentDidUpdate( prevProps ) {
46
49
  if ( ! this.props.isSelected && prevProps.isSelected ) {
47
- this._input.blur();
50
+ this._input?.blur();
48
51
  }
49
52
  }
50
53
 
@@ -55,11 +58,11 @@ export default class PlainText extends Component {
55
58
  }
56
59
 
57
60
  focus() {
58
- this._input.focus();
61
+ this._input?.focus();
59
62
  }
60
63
 
61
64
  blur() {
62
- this._input.blur();
65
+ this._input?.blur();
63
66
  }
64
67
 
65
68
  getFontSize() {
@@ -79,20 +82,73 @@ export default class PlainText extends Component {
79
82
  };
80
83
  }
81
84
 
85
+ replaceLineBreakTags( value ) {
86
+ return value?.replace( RegExp( '<br>', 'gim' ), '\n' );
87
+ }
88
+
89
+ onChangeTextInput( event ) {
90
+ const { onChange } = this.props;
91
+ onChange( event.nativeEvent.text );
92
+ }
93
+
94
+ onChangeRichText( value ) {
95
+ const { onChange } = this.props;
96
+ // The <br> tags have to be replaced with new line characters
97
+ // as the content of plain text shouldn't contain HTML tags.
98
+ onChange( this.replaceLineBreakTags( value ) );
99
+ }
100
+
82
101
  render() {
83
- const { style } = this.props;
102
+ const {
103
+ style,
104
+ __experimentalVersion,
105
+ onFocus,
106
+ ...otherProps
107
+ } = this.props;
84
108
  const textStyles = [
85
109
  style || styles[ 'block-editor-plain-text' ],
86
110
  this.getFontSize(),
87
111
  ];
88
112
 
113
+ if ( __experimentalVersion === 2 ) {
114
+ const disableFormattingProps = {
115
+ withoutInteractiveFormatting: true,
116
+ disableEditingMenu: true,
117
+ __unstableDisableFormats: true,
118
+ disableSuggestions: true,
119
+ };
120
+
121
+ const forcePlainTextProps = {
122
+ preserveWhiteSpace: true,
123
+ __unstablePastePlainText: true,
124
+ multiline: false,
125
+ };
126
+
127
+ const fontProps = {
128
+ fontFamily: style?.fontFamily,
129
+ fontSize: style?.fontSize,
130
+ fontWeight: style?.fontWeight,
131
+ };
132
+
133
+ return (
134
+ <RichText
135
+ { ...otherProps }
136
+ { ...disableFormattingProps }
137
+ { ...forcePlainTextProps }
138
+ { ...fontProps }
139
+ identifier="content"
140
+ style={ style }
141
+ onChange={ this.onChangeRichText }
142
+ unstableOnFocus={ onFocus }
143
+ />
144
+ );
145
+ }
146
+
89
147
  return (
90
148
  <TextInput
91
149
  { ...this.props }
92
150
  ref={ ( x ) => ( this._input = x ) }
93
- onChange={ ( event ) => {
94
- this.props.onChange( event.nativeEvent.text );
95
- } }
151
+ onChange={ this.onChangeTextInput }
96
152
  onFocus={ this.props.onFocus } // Always assign onFocus as a props.
97
153
  onBlur={ this.props.onBlur } // Always assign onBlur as a props.
98
154
  fontFamily={
@@ -0,0 +1,52 @@
1
+ # `PublishDateTimePicker`
2
+
3
+ `<PublishDateTimePicker />` is a component used to select the date and time that
4
+ a post will be published. It wraps the `<DateTimePicker />` component found in
5
+ `@wordpress/components` and adds additional post-specific controls.
6
+
7
+ See [the documentation for DateTimePicker](/packages/components/src/date-time)
8
+ for more information.
9
+
10
+ ## Usage
11
+
12
+ ```jsx
13
+ import { Dropdown, Button } from '@wordpress/components';
14
+ import { __experimentalPublishDateTimePicker as PublishDateTimePicker } from '@wordpress/block-editor';
15
+ import { useState } from '@wordpress/element';
16
+
17
+ const MyDateTimePicker = () => {
18
+ const [ date, setDate ] = useState( new Date() );
19
+
20
+ return (
21
+ <Dropdown
22
+ renderToggle={ ( { isOpen, onToggle } ) => (
23
+ <Button
24
+ onClick={ onToggle }
25
+ aria-expanded={ isOpen }
26
+ >
27
+ Select post date
28
+ </Button>
29
+ ) }
30
+ renderContent={ ( { onClose } ) => (
31
+ <PublishDateTimePicker
32
+ currentDate={ date }
33
+ onChange={ ( newDate ) => setDate( newDate ) }
34
+ onClose={ onClose }
35
+ />
36
+ ) }
37
+ />
38
+ );
39
+ };
40
+ ```
41
+
42
+ ## Props
43
+
44
+ `PublishDateTimePicker` supports all of the props that
45
+ [`DateTimePicker`](/packages/components/src/date-time#Props) supports, plus:
46
+
47
+ ### onClose
48
+
49
+ Called when the user presses the close button.
50
+
51
+ - Type: `Function`
52
+ - Required: Yes
@@ -0,0 +1,50 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ DateTimePicker,
6
+ __experimentalHStack as HStack,
7
+ __experimentalSpacer as Spacer,
8
+ Button,
9
+ } from '@wordpress/components';
10
+ import { closeSmall } from '@wordpress/icons';
11
+ import { __ } from '@wordpress/i18n';
12
+ import { forwardRef } from '@wordpress/element';
13
+
14
+ function PublishDateTimePicker(
15
+ { onClose, onChange, ...additionalProps },
16
+ ref
17
+ ) {
18
+ return (
19
+ <div ref={ ref } className="block-editor-publish-date-time-picker">
20
+ { /* TODO: This header is essentially the same as the one in <PostVisiblity />. DRY it up. */ }
21
+ <HStack className="block-editor-publish-date-time-picker__header">
22
+ <h2 className="block-editor-publish-date-time-picker__heading">
23
+ { __( 'Publish' ) }
24
+ </h2>
25
+ <Spacer />
26
+ <Button
27
+ className="block-editor-publish-date-time-picker__reset"
28
+ variant="tertiary"
29
+ onClick={ () => onChange?.( null ) }
30
+ >
31
+ { __( 'Now' ) }
32
+ </Button>
33
+ <Button
34
+ className="block-editor-publish-date-time-picker__close"
35
+ icon={ closeSmall }
36
+ label={ __( 'Close' ) }
37
+ onClick={ onClose }
38
+ />
39
+ </HStack>
40
+ <DateTimePicker
41
+ __nextRemoveHelpButton
42
+ __nextRemoveResetButton
43
+ onChange={ onChange }
44
+ { ...additionalProps }
45
+ />
46
+ </div>
47
+ );
48
+ }
49
+
50
+ export default forwardRef( PublishDateTimePicker );
@@ -0,0 +1,20 @@
1
+ .block-editor-publish-date-time-picker__header {
2
+ margin-bottom: 1em;
3
+ }
4
+
5
+ .block-editor-publish-date-time-picker__heading {
6
+ font-size: $default-font-size;
7
+ margin: 0;
8
+ }
9
+
10
+ .block-editor-publish-date-time-picker__reset {
11
+ height: $icon-size;
12
+ margin: 0;
13
+ text-decoration: underline;
14
+ }
15
+
16
+ [class].block-editor-publish-date-time-picker__close {
17
+ height: $icon-size;
18
+ min-width: $icon-size;
19
+ padding: 0;
20
+ }
@@ -76,6 +76,8 @@ function removeNativeProps( props ) {
76
76
  'minWidth',
77
77
  'maxWidth',
78
78
  'setRef',
79
+ 'disableSuggestions',
80
+ 'disableAutocorrection',
79
81
  ] );
80
82
  }
81
83
 
@@ -111,6 +111,8 @@ function RichTextWrapper(
111
111
  maxWidth,
112
112
  onBlur,
113
113
  setRef,
114
+ disableSuggestions,
115
+ disableAutocorrection,
114
116
  ...props
115
117
  },
116
118
  forwardedRef
@@ -635,6 +637,8 @@ function RichTextWrapper(
635
637
  maxWidth={ maxWidth }
636
638
  onBlur={ onBlur }
637
639
  setRef={ setRef }
640
+ disableSuggestions={ disableSuggestions }
641
+ disableAutocorrection={ disableAutocorrection }
638
642
  // Props to be set on the editable container are destructured on the
639
643
  // element itself for web (see below), but passed through rich text
640
644
  // for native.
@@ -30,6 +30,16 @@ export default function useInput() {
30
30
  } = useDispatch( blockEditorStore );
31
31
 
32
32
  return useRefEffect( ( node ) => {
33
+ function onBeforeInput( event ) {
34
+ if ( ! hasMultiSelection() ) {
35
+ return;
36
+ }
37
+ // Prevent the browser to format something when we have multiselection.
38
+ if ( event.inputType?.startsWith( 'format' ) ) {
39
+ event.preventDefault();
40
+ }
41
+ }
42
+
33
43
  function onKeyDown( event ) {
34
44
  if ( event.defaultPrevented ) {
35
45
  return;
@@ -102,9 +112,11 @@ export default function useInput() {
102
112
  }
103
113
  }
104
114
 
115
+ node.addEventListener( 'beforeinput', onBeforeInput );
105
116
  node.addEventListener( 'keydown', onKeyDown );
106
117
  node.addEventListener( 'compositionstart', onCompositionStart );
107
118
  return () => {
119
+ node.removeEventListener( 'beforeinput', onBeforeInput );
108
120
  node.removeEventListener( 'keydown', onKeyDown );
109
121
  node.removeEventListener( 'compositionstart', onCompositionStart );
110
122
  };
@@ -0,0 +1 @@
1
+ export const __experimentalElementButtonClassName = 'wp-element-button';
@@ -76,34 +76,6 @@ const hasTextColorSupport = ( blockType ) => {
76
76
  return colorSupport && colorSupport.text !== false;
77
77
  };
78
78
 
79
- /**
80
- * Checks whether a color has been set either with a named preset color in
81
- * a top level block attribute or as a custom value within the style attribute
82
- * object.
83
- *
84
- * @param {string} name Name of the color to check.
85
- * @return {boolean} Whether or not a color has a value.
86
- */
87
- const hasColor = ( name ) => ( props ) => {
88
- if ( name === 'background' ) {
89
- return (
90
- !! props.attributes.backgroundColor ||
91
- !! props.attributes.style?.color?.background ||
92
- !! props.attributes.gradient ||
93
- !! props.attributes.style?.color?.gradient
94
- );
95
- }
96
-
97
- if ( name === 'link' ) {
98
- return !! props.attributes.style?.elements?.link?.color?.text;
99
- }
100
-
101
- return (
102
- !! props.attributes[ `${ name }Color` ] ||
103
- !! props.attributes.style?.color?.[ name ]
104
- );
105
- };
106
-
107
79
  /**
108
80
  * Clears a single color property from a style object.
109
81
  *
@@ -114,20 +86,6 @@ const hasColor = ( name ) => ( props ) => {
114
86
  const clearColorFromStyles = ( path, style ) =>
115
87
  cleanEmptyObject( immutableSet( style, path, undefined ) );
116
88
 
117
- /**
118
- * Resets the block attributes for text color.
119
- *
120
- * @param {Object} props Current block props.
121
- * @param {Object} props.attributes Block attributes.
122
- * @param {Function} props.setAttributes Block's setAttributes prop used to apply reset.
123
- */
124
- const resetTextColor = ( { attributes, setAttributes } ) => {
125
- setAttributes( {
126
- textColor: undefined,
127
- style: clearColorFromStyles( [ 'color', 'text' ], attributes.style ),
128
- } );
129
- };
130
-
131
89
  /**
132
90
  * Clears text color related properties from supplied attributes.
133
91
  *
@@ -139,18 +97,6 @@ const resetAllTextFilter = ( attributes ) => ( {
139
97
  style: clearColorFromStyles( [ 'color', 'text' ], attributes.style ),
140
98
  } );
141
99
 
142
- /**
143
- * Resets the block attributes for link color.
144
- *
145
- * @param {Object} props Current block props.
146
- * @param {Object} props.attributes Block attributes.
147
- * @param {Function} props.setAttributes Block's setAttributes prop used to apply reset.
148
- */
149
- const resetLinkColor = ( { attributes, setAttributes } ) => {
150
- const path = [ 'elements', 'link', 'color', 'text' ];
151
- setAttributes( { style: clearColorFromStyles( path, attributes.style ) } );
152
- };
153
-
154
100
  /**
155
101
  * Clears link color related properties from supplied attributes.
156
102
  *
@@ -184,17 +130,6 @@ const clearBackgroundAndGradient = ( attributes ) => ( {
184
130
  },
185
131
  } );
186
132
 
187
- /**
188
- * Resets the block attributes for both background color and gradient.
189
- *
190
- * @param {Object} props Current block props.
191
- * @param {Object} props.attributes Block attributes.
192
- * @param {Function} props.setAttributes Block's setAttributes prop used to apply reset.
193
- */
194
- const resetBackgroundAndGradient = ( { attributes, setAttributes } ) => {
195
- setAttributes( clearBackgroundAndGradient( attributes ) );
196
- };
197
-
198
133
  /**
199
134
  * Filters registered block settings, extending attributes to include
200
135
  * `backgroundColor` and `textColor` attribute.
@@ -496,12 +431,16 @@ export function ColorEdit( props ) {
496
431
 
497
432
  const newStyle = cleanEmptyObject(
498
433
  immutableSet(
499
- style,
434
+ localAttributes.current?.style,
500
435
  [ 'elements', 'link', 'color', 'text' ],
501
436
  newLinkColorValue
502
437
  )
503
438
  );
504
439
  props.setAttributes( { style: newStyle } );
440
+ localAttributes.current = {
441
+ ...localAttributes.current,
442
+ ...{ style: newStyle },
443
+ };
505
444
  };
506
445
 
507
446
  const enableContrastChecking =
@@ -529,8 +468,6 @@ export function ColorEdit( props ) {
529
468
  style?.color?.text
530
469
  ).color,
531
470
  isShownByDefault: defaultColorControls?.text,
532
- hasValue: () => hasColor( 'text' )( props ),
533
- onDeselect: () => resetTextColor( props ),
534
471
  resetAllFilter: resetAllTextFilter,
535
472
  },
536
473
  ]
@@ -553,10 +490,6 @@ export function ColorEdit( props ) {
553
490
  : undefined,
554
491
  isShownByDefault:
555
492
  defaultColorControls?.background,
556
- hasValue: () =>
557
- hasColor( 'background' )( props ),
558
- onDeselect: () =>
559
- resetBackgroundAndGradient( props ),
560
493
  resetAllFilter: clearBackgroundAndGradient,
561
494
  },
562
495
  ]
@@ -573,8 +506,6 @@ export function ColorEdit( props ) {
573
506
  clearable: !! style?.elements?.link?.color
574
507
  ?.text,
575
508
  isShownByDefault: defaultColorControls?.link,
576
- hasValue: () => hasColor( 'link' )( props ),
577
- onDeselect: () => resetLinkColor( props ),
578
509
  resetAllFilter: resetAllLinkFilter,
579
510
  },
580
511
  ]
@@ -20,4 +20,13 @@
20
20
  row-gap: 0;
21
21
  }
22
22
  }
23
+
24
+ /**
25
+ * After converting PanelColorGradientSettings to render as a ToolsPanel
26
+ * we need to remove the top margin when wrapping inner content due to
27
+ * rendering via SlotFills.
28
+ */
29
+ .block-editor-tools-panel-color-gradient-settings__item.first {
30
+ margin-top: 0;
31
+ }
23
32
  }
@@ -153,7 +153,7 @@ const useIsDimensionsDisabled = ( props = {} ) => {
153
153
  };
154
154
 
155
155
  /**
156
- * Custom hook to retrieve which padding/margin is supported
156
+ * Custom hook to retrieve which padding/margin/blockGap is supported
157
157
  * e.g. top, right, bottom or left.
158
158
  *
159
159
  * Sides are opted into by default. It is only if a specific side is set to
@@ -162,7 +162,7 @@ const useIsDimensionsDisabled = ( props = {} ) => {
162
162
  * @param {string} blockName Block name.
163
163
  * @param {string} feature The feature custom sides relate to e.g. padding or margins.
164
164
  *
165
- * @return {Object} Sides supporting custom margin.
165
+ * @return {?string[]} Strings representing the custom sides available.
166
166
  */
167
167
  export function useCustomSides( blockName, feature ) {
168
168
  const support = getBlockSupport( blockName, SPACING_SUPPORT_KEY );
@@ -172,7 +172,15 @@ export function useCustomSides( blockName, feature ) {
172
172
  return;
173
173
  }
174
174
 
175
- return support[ feature ];
175
+ // Return if the setting is an array of sides (e.g. `[ 'top', 'bottom' ]`).
176
+ if ( Array.isArray( support[ feature ] ) ) {
177
+ return support[ feature ];
178
+ }
179
+
180
+ // Finally, attempt to return `.sides` if the setting is an object.
181
+ if ( support[ feature ]?.sides ) {
182
+ return support[ feature ].sides;
183
+ }
176
184
  }
177
185
 
178
186
  /**
package/src/index.js CHANGED
@@ -13,6 +13,7 @@ export {
13
13
  useCachedTruthy,
14
14
  } from './hooks';
15
15
  export * from './components';
16
+ export * from './elements';
16
17
  export * from './utils';
17
18
  export { storeConfig, store } from './store';
18
19
  export { SETTINGS_DEFAULTS } from './store/defaults';
@@ -11,6 +11,7 @@ import {
11
11
  arrowDown,
12
12
  } from '@wordpress/icons';
13
13
  import { Button, ToggleControl, Flex, FlexItem } from '@wordpress/components';
14
+ import { getBlockSupport } from '@wordpress/blocks';
14
15
 
15
16
  /**
16
17
  * Internal dependencies
@@ -109,14 +110,21 @@ export default {
109
110
  save: function FlexLayoutStyle( { selector, layout, style, blockName } ) {
110
111
  const { orientation = 'horizontal' } = layout;
111
112
  const blockGapSupport = useSetting( 'spacing.blockGap' );
113
+ const fallbackValue =
114
+ getBlockSupport( blockName, [
115
+ 'spacing',
116
+ 'blockGap',
117
+ '__experimentalDefault',
118
+ ] ) || '0.5em';
119
+
112
120
  const hasBlockGapStylesSupport = blockGapSupport !== null;
113
121
  // If a block's block.json skips serialization for spacing or spacing.blockGap,
114
122
  // don't apply the user-defined value to the styles.
115
123
  const blockGapValue =
116
124
  style?.spacing?.blockGap &&
117
125
  ! shouldSkipSerialization( blockName, 'spacing', 'blockGap' )
118
- ? getGapCSSValue( style?.spacing?.blockGap, '0.5em' )
119
- : 'var( --wp--style--block-gap, 0.5em )';
126
+ ? getGapCSSValue( style?.spacing?.blockGap, fallbackValue )
127
+ : `var( --wp--style--block-gap, ${ fallbackValue } )`;
120
128
  const justifyContent =
121
129
  justifyContentMap[ layout.justifyContent ] ||
122
130
  justifyContentMap.left;
@@ -143,7 +151,7 @@ export default {
143
151
  ${ appendSelectors( selector ) } {
144
152
  display: flex;
145
153
  flex-wrap: ${ flexWrap };
146
- gap: ${ hasBlockGapStylesSupport ? blockGapValue : '0.5em' };
154
+ gap: ${ hasBlockGapStylesSupport ? blockGapValue : fallbackValue };
147
155
  ${ orientation === 'horizontal' ? rowOrientation : columnOrientation }
148
156
  }
149
157
 
@@ -1603,3 +1603,15 @@ export function setHasControlledInnerBlocks(
1603
1603
  clientId,
1604
1604
  };
1605
1605
  }
1606
+
1607
+ /**
1608
+ * Action that sets whether given blocks are visible on the canvas.
1609
+ *
1610
+ * @param {Record<string,boolean>} updates For each block's clientId, its new visibility setting.
1611
+ */
1612
+ export function setBlockVisibility( updates ) {
1613
+ return {
1614
+ type: 'SET_BLOCK_VISIBILITY',
1615
+ updates,
1616
+ };
1617
+ }
@@ -606,6 +606,7 @@ const withBlockReset = ( reducer ) => ( state, action ) => {
606
606
  order: mapBlockOrder( action.blocks ),
607
607
  parents: mapBlockParents( action.blocks ),
608
608
  controlledInnerBlocks: {},
609
+ visibility: {},
609
610
  };
610
611
 
611
612
  const subTree = buildBlockTree( newState, action.blocks );
@@ -1139,6 +1140,17 @@ export const blocks = flow(
1139
1140
  }
1140
1141
  return state;
1141
1142
  },
1143
+
1144
+ visibility( state = {}, action ) {
1145
+ if ( action.type === 'SET_BLOCK_VISIBILITY' ) {
1146
+ return {
1147
+ ...state,
1148
+ ...action.updates,
1149
+ };
1150
+ }
1151
+
1152
+ return state;
1153
+ },
1142
1154
  } );
1143
1155
 
1144
1156
  /**
@@ -1678,7 +1690,8 @@ export function automaticChangeStatus( state, action ) {
1678
1690
 
1679
1691
  return;
1680
1692
  // Undoing an automatic change should still be possible after mouse
1681
- // move.
1693
+ // move or after visibility change.
1694
+ case 'SET_BLOCK_VISIBILITY':
1682
1695
  case 'START_TYPING':
1683
1696
  case 'STOP_TYPING':
1684
1697
  return state;
@@ -2651,3 +2651,31 @@ export function wasBlockJustInserted( state, clientId, source ) {
2651
2651
  lastBlockInserted.source === source
2652
2652
  );
2653
2653
  }
2654
+
2655
+ /**
2656
+ * Tells if the block is visible on the canvas or not.
2657
+ *
2658
+ * @param {Object} state Global application state.
2659
+ * @param {Object} clientId Client Id of the block.
2660
+ * @return {boolean} True if the block is visible.
2661
+ */
2662
+ export function isBlockVisible( state, clientId ) {
2663
+ return state.blocks.visibility?.[ clientId ] ?? true;
2664
+ }
2665
+
2666
+ /**
2667
+ * Returns the list of all hidden blocks.
2668
+ *
2669
+ * @param {Object} state Global application state.
2670
+ * @return {[string]} List of hidden blocks.
2671
+ */
2672
+ export const __unstableGetVisibleBlocks = createSelector(
2673
+ ( state ) => {
2674
+ return new Set(
2675
+ Object.keys( state.blocks.visibility ).filter(
2676
+ ( key ) => state.blocks.visibility[ key ]
2677
+ )
2678
+ );
2679
+ },
2680
+ ( state ) => [ state.blocks.visibility ]
2681
+ );