@wordpress/block-editor 15.1.0 → 15.1.1-next.0f6f9d12c.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 (94) hide show
  1. package/build/components/block-settings-menu/block-settings-dropdown.js +6 -2
  2. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  3. package/build/components/block-toolbar/block-toolbar-menu.native.js +2 -2
  4. package/build/components/block-toolbar/block-toolbar-menu.native.js.map +1 -1
  5. package/build/components/border-radius-control/constants.js +41 -0
  6. package/build/components/border-radius-control/constants.js.map +1 -0
  7. package/build/components/border-radius-control/index.js +49 -58
  8. package/build/components/border-radius-control/index.js.map +1 -1
  9. package/build/components/border-radius-control/linked-button.js +1 -1
  10. package/build/components/border-radius-control/linked-button.js.map +1 -1
  11. package/build/components/border-radius-control/single-input-control.js +204 -0
  12. package/build/components/border-radius-control/single-input-control.js.map +1 -0
  13. package/build/components/border-radius-control/utils.js +161 -4
  14. package/build/components/border-radius-control/utils.js.map +1 -1
  15. package/build/components/global-styles/border-panel.js +12 -1
  16. package/build/components/global-styles/border-panel.js.map +1 -1
  17. package/build/components/global-styles/hooks.js +1 -1
  18. package/build/components/global-styles/hooks.js.map +1 -1
  19. package/build/components/global-styles/utils.js +5 -3
  20. package/build/components/global-styles/utils.js.map +1 -1
  21. package/build/components/rich-text/index.js +9 -4
  22. package/build/components/rich-text/index.js.map +1 -1
  23. package/build/components/rich-text/use-format-types.js +19 -8
  24. package/build/components/rich-text/use-format-types.js.map +1 -1
  25. package/build/hooks/duotone.js +2 -2
  26. package/build/hooks/duotone.js.map +1 -1
  27. package/build/hooks/utils.js +4 -3
  28. package/build/hooks/utils.js.map +1 -1
  29. package/build/private-apis.js +2 -1
  30. package/build/private-apis.js.map +1 -1
  31. package/build/store/private-keys.js +2 -1
  32. package/build/store/private-keys.js.map +1 -1
  33. package/build-module/components/block-settings-menu/block-settings-dropdown.js +6 -2
  34. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  35. package/build-module/components/block-toolbar/block-toolbar-menu.native.js +3 -3
  36. package/build-module/components/block-toolbar/block-toolbar-menu.native.js.map +1 -1
  37. package/build-module/components/border-radius-control/constants.js +34 -0
  38. package/build-module/components/border-radius-control/constants.js.map +1 -0
  39. package/build-module/components/border-radius-control/index.js +53 -62
  40. package/build-module/components/border-radius-control/index.js.map +1 -1
  41. package/build-module/components/border-radius-control/linked-button.js +1 -1
  42. package/build-module/components/border-radius-control/linked-button.js.map +1 -1
  43. package/build-module/components/border-radius-control/single-input-control.js +197 -0
  44. package/build-module/components/border-radius-control/single-input-control.js.map +1 -0
  45. package/build-module/components/border-radius-control/utils.js +154 -4
  46. package/build-module/components/border-radius-control/utils.js.map +1 -1
  47. package/build-module/components/global-styles/border-panel.js +12 -1
  48. package/build-module/components/global-styles/border-panel.js.map +1 -1
  49. package/build-module/components/global-styles/hooks.js +1 -1
  50. package/build-module/components/global-styles/hooks.js.map +1 -1
  51. package/build-module/components/global-styles/utils.js +5 -3
  52. package/build-module/components/global-styles/utils.js.map +1 -1
  53. package/build-module/components/rich-text/index.js +9 -4
  54. package/build-module/components/rich-text/index.js.map +1 -1
  55. package/build-module/components/rich-text/use-format-types.js +19 -8
  56. package/build-module/components/rich-text/use-format-types.js.map +1 -1
  57. package/build-module/hooks/duotone.js +3 -3
  58. package/build-module/hooks/duotone.js.map +1 -1
  59. package/build-module/hooks/utils.js +4 -3
  60. package/build-module/hooks/utils.js.map +1 -1
  61. package/build-module/private-apis.js +3 -2
  62. package/build-module/private-apis.js.map +1 -1
  63. package/build-module/store/private-keys.js +1 -0
  64. package/build-module/store/private-keys.js.map +1 -1
  65. package/build-style/style-rtl.css +16 -18
  66. package/build-style/style.css +16 -18
  67. package/package.json +34 -34
  68. package/src/components/block-settings-menu/block-settings-dropdown.js +22 -14
  69. package/src/components/block-toolbar/block-toolbar-menu.native.js +3 -3
  70. package/src/components/border-radius-control/constants.js +46 -0
  71. package/src/components/border-radius-control/index.js +71 -72
  72. package/src/components/border-radius-control/linked-button.js +1 -1
  73. package/src/components/border-radius-control/single-input-control.js +277 -0
  74. package/src/components/border-radius-control/style.scss +16 -21
  75. package/src/components/border-radius-control/utils.js +178 -5
  76. package/src/components/global-styles/border-panel.js +12 -1
  77. package/src/components/global-styles/hooks.js +1 -0
  78. package/src/components/global-styles/utils.js +6 -1
  79. package/src/components/rich-text/index.js +19 -10
  80. package/src/components/rich-text/use-format-types.js +42 -19
  81. package/src/hooks/duotone.js +11 -3
  82. package/src/hooks/utils.js +4 -0
  83. package/src/private-apis.js +2 -0
  84. package/src/store/private-keys.js +1 -0
  85. package/build/components/border-radius-control/all-input-control.js +0 -65
  86. package/build/components/border-radius-control/all-input-control.js.map +0 -1
  87. package/build/components/border-radius-control/input-controls.js +0 -82
  88. package/build/components/border-radius-control/input-controls.js.map +0 -1
  89. package/build-module/components/border-radius-control/all-input-control.js +0 -58
  90. package/build-module/components/border-radius-control/all-input-control.js.map +0 -1
  91. package/build-module/components/border-radius-control/input-controls.js +0 -75
  92. package/build-module/components/border-radius-control/input-controls.js.map +0 -1
  93. package/src/components/border-radius-control/all-input-control.js +0 -67
  94. package/src/components/border-radius-control/input-controls.js +0 -91
@@ -0,0 +1,277 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
6
+ __experimentalUnitControl as UnitControl,
7
+ __experimentalHStack as HStack,
8
+ Icon,
9
+ Tooltip,
10
+ RangeControl,
11
+ Button,
12
+ CustomSelectControl,
13
+ } from '@wordpress/components';
14
+ import { __ } from '@wordpress/i18n';
15
+ import { useState } from '@wordpress/element';
16
+ import { settings } from '@wordpress/icons';
17
+
18
+ /**
19
+ * Internal dependencies
20
+ */
21
+ import {
22
+ getAllValue,
23
+ getCustomValueFromPreset,
24
+ getPresetValueFromControlValue,
25
+ getPresetValueFromCustomValue,
26
+ getSliderValueFromPreset,
27
+ isValuePreset,
28
+ convertPresetsToCustomValues,
29
+ } from './utils';
30
+ import {
31
+ CORNERS,
32
+ ICONS,
33
+ MIN_BORDER_RADIUS_VALUE,
34
+ MAX_BORDER_RADIUS_VALUES,
35
+ RANGE_CONTROL_MAX_SIZE,
36
+ } from './constants';
37
+
38
+ export default function SingleInputControl( {
39
+ corner,
40
+ onChange,
41
+ selectedUnits,
42
+ setSelectedUnits,
43
+ values: valuesProp,
44
+ units,
45
+ presets,
46
+ } ) {
47
+ const changeCornerValue = ( validatedValue ) => {
48
+ if ( corner === 'all' ) {
49
+ onChange( {
50
+ topLeft: validatedValue,
51
+ topRight: validatedValue,
52
+ bottomLeft: validatedValue,
53
+ bottomRight: validatedValue,
54
+ } );
55
+ } else {
56
+ onChange( {
57
+ ...values,
58
+ [ corner ]: validatedValue,
59
+ } );
60
+ }
61
+ };
62
+
63
+ const onChangeValue = ( next ) => {
64
+ if ( ! onChange ) {
65
+ return;
66
+ }
67
+
68
+ // Filter out CSS-unit-only values to prevent invalid styles.
69
+ const isNumeric = ! isNaN( parseFloat( next ) );
70
+ const nextValue = isNumeric ? next : undefined;
71
+ changeCornerValue( nextValue );
72
+ };
73
+
74
+ const onChangeUnit = ( next ) => {
75
+ const newUnits = { ...selectedUnits };
76
+ if ( corner === 'all' ) {
77
+ newUnits.topLeft = next;
78
+ newUnits.topRight = next;
79
+ newUnits.bottomLeft = next;
80
+ newUnits.bottomRight = next;
81
+ } else {
82
+ newUnits[ corner ] = next;
83
+ }
84
+ setSelectedUnits( newUnits );
85
+ };
86
+
87
+ // For shorthand style & backwards compatibility, handle flat string value.
88
+ const values =
89
+ typeof valuesProp !== 'string'
90
+ ? valuesProp
91
+ : {
92
+ topLeft: valuesProp,
93
+ topRight: valuesProp,
94
+ bottomLeft: valuesProp,
95
+ bottomRight: valuesProp,
96
+ };
97
+
98
+ // For 'all' corner, convert presets to custom values before calling getAllValue
99
+ // For individual corners, check if the value should be converted to a preset
100
+ let value;
101
+ if ( corner === 'all' ) {
102
+ const convertedValues = convertPresetsToCustomValues( values, presets );
103
+ const customValue = getAllValue( convertedValues );
104
+ value = getPresetValueFromCustomValue( customValue, presets );
105
+ } else {
106
+ value = getPresetValueFromCustomValue( values[ corner ], presets );
107
+ }
108
+ const resolvedPresetValue = isValuePreset( value )
109
+ ? getCustomValueFromPreset( value, presets )
110
+ : value;
111
+ const [ parsedQuantity, parsedUnit ] =
112
+ parseQuantityAndUnitFromRawValue( resolvedPresetValue );
113
+ const computedUnit = value
114
+ ? parsedUnit
115
+ : selectedUnits[ corner ] || selectedUnits.flat || 'px';
116
+ const unitConfig =
117
+ units && units.find( ( item ) => item.value === computedUnit );
118
+ const step = unitConfig?.step || 1;
119
+ const [ showCustomValueControl, setShowCustomValueControl ] = useState(
120
+ value !== undefined && ! isValuePreset( value )
121
+ );
122
+ const showRangeControl = presets.length <= RANGE_CONTROL_MAX_SIZE;
123
+ const presetIndex = getSliderValueFromPreset( value, presets );
124
+ const rangeTooltip = ( newValue ) =>
125
+ value === undefined ? undefined : presets[ newValue ]?.name;
126
+ const marks = presets
127
+ .slice( 1, presets.length - 1 )
128
+ .map( ( _newValue, index ) => ( {
129
+ value: index + 1,
130
+ label: undefined,
131
+ } ) );
132
+ const hasPresets = marks.length > 0;
133
+ let options = [];
134
+ if ( ! showRangeControl ) {
135
+ options = [
136
+ ...presets,
137
+ {
138
+ name: __( 'Custom' ),
139
+ slug: 'custom',
140
+ size: resolvedPresetValue,
141
+ },
142
+ ].map( ( size, index ) => ( {
143
+ key: index,
144
+ name: size.name,
145
+ } ) );
146
+ }
147
+ const icon = ICONS[ corner ];
148
+
149
+ const handleSliderChange = ( next ) => {
150
+ const val =
151
+ next !== undefined ? `${ next }${ computedUnit }` : undefined;
152
+ changeCornerValue( val );
153
+ };
154
+
155
+ // Controls are wrapped in tooltips as visible labels aren't desired here.
156
+ // Tooltip rendering also requires the UnitControl to be wrapped. See:
157
+ // https://github.com/WordPress/gutenberg/pull/24966#issuecomment-685875026
158
+ return (
159
+ <HStack>
160
+ { icon && (
161
+ <Icon
162
+ className="components-border-radius-control__icon"
163
+ icon={ icon }
164
+ size={ 24 }
165
+ />
166
+ ) }
167
+ { ( ! hasPresets || showCustomValueControl ) && (
168
+ <div className="components-border-radius-control__input-controls-wrapper">
169
+ <Tooltip text={ CORNERS[ corner ] } placement="top">
170
+ <div className="components-border-radius-control__tooltip-wrapper">
171
+ <UnitControl
172
+ className="components-border-radius-control__unit-control"
173
+ aria-label={ CORNERS[ corner ] }
174
+ value={ [ parsedQuantity, computedUnit ].join(
175
+ ''
176
+ ) }
177
+ onChange={ onChangeValue }
178
+ onUnitChange={ onChangeUnit }
179
+ size="__unstable-large"
180
+ min={ MIN_BORDER_RADIUS_VALUE }
181
+ units={ units }
182
+ />
183
+ </div>
184
+ </Tooltip>
185
+ <RangeControl
186
+ __next40pxDefaultSize
187
+ label={ __( 'Border radius' ) }
188
+ hideLabelFromVision
189
+ className="components-border-radius-control__range-control"
190
+ value={ parsedQuantity ?? '' }
191
+ min={ MIN_BORDER_RADIUS_VALUE }
192
+ max={ MAX_BORDER_RADIUS_VALUES[ computedUnit ] }
193
+ initialPosition={ 0 }
194
+ withInputField={ false }
195
+ onChange={ handleSliderChange }
196
+ step={ step }
197
+ __nextHasNoMarginBottom
198
+ />
199
+ </div>
200
+ ) }
201
+ { hasPresets && showRangeControl && ! showCustomValueControl && (
202
+ <RangeControl
203
+ __next40pxDefaultSize
204
+ className="components-border-radius-control__range-control"
205
+ value={ presetIndex }
206
+ onChange={ ( newSize ) => {
207
+ changeCornerValue(
208
+ getPresetValueFromControlValue(
209
+ newSize,
210
+ 'range',
211
+ presets
212
+ )
213
+ );
214
+ } }
215
+ withInputField={ false }
216
+ aria-valuenow={ presetIndex }
217
+ aria-valuetext={ presets[ presetIndex ]?.name }
218
+ renderTooltipContent={ rangeTooltip }
219
+ min={ 0 }
220
+ max={ presets.length - 1 }
221
+ marks={ marks }
222
+ label={ CORNERS[ corner ] }
223
+ hideLabelFromVision
224
+ __nextHasNoMarginBottom
225
+ />
226
+ ) }
227
+
228
+ { ! showRangeControl && ! showCustomValueControl && (
229
+ <CustomSelectControl
230
+ className="components-border-radius-control__custom-select-control"
231
+ value={
232
+ options.find(
233
+ ( option ) => option.key === presetIndex
234
+ ) || options[ options.length - 1 ]
235
+ }
236
+ onChange={ ( selection ) => {
237
+ if (
238
+ selection.selectedItem.key ===
239
+ options.length - 1
240
+ ) {
241
+ setShowCustomValueControl( true );
242
+ } else {
243
+ changeCornerValue(
244
+ getPresetValueFromControlValue(
245
+ selection.selectedItem.key,
246
+ 'selectList',
247
+ presets
248
+ )
249
+ );
250
+ }
251
+ } }
252
+ options={ options }
253
+ label={ CORNERS[ corner ] }
254
+ hideLabelFromVision
255
+ size="__unstable-large"
256
+ />
257
+ ) }
258
+ { hasPresets && (
259
+ <Button
260
+ label={
261
+ showCustomValueControl
262
+ ? __( 'Use border radius preset' )
263
+ : __( 'Set custom border radius' )
264
+ }
265
+ icon={ settings }
266
+ onClick={ () => {
267
+ setShowCustomValueControl( ! showCustomValueControl );
268
+ } }
269
+ isPressed={ showCustomValueControl }
270
+ size="small"
271
+ className="components-border-radius-control__custom-toggle"
272
+ iconSize={ 24 }
273
+ />
274
+ ) }
275
+ </HStack>
276
+ );
277
+ }
@@ -1,3 +1,8 @@
1
+ .components-border-radius-control__header {
2
+ height: $grid-unit-20;
3
+ margin-bottom: $grid-unit-15;
4
+ }
5
+
1
6
  .components-border-radius-control {
2
7
  // Reset `fieldset` browser defaults.
3
8
  border: 0;
@@ -7,25 +12,7 @@
7
12
  margin-bottom: $grid-unit-15;
8
13
 
9
14
  legend {
10
- margin-bottom: $grid-unit-10;
11
- }
12
-
13
- .components-border-radius-control__wrapper {
14
- display: flex;
15
- justify-content: space-between;
16
- align-items: flex-start;
17
-
18
- .components-border-radius-control__unit-control {
19
- width: calc((100% - #{$grid-unit-20}) / 2);
20
- margin-bottom: 0;
21
- margin-right: $grid-unit-20;
22
- flex-shrink: 0;
23
- }
24
-
25
- .components-border-radius-control__range-control {
26
- flex: 1;
27
- margin-right: $grid-unit-15;
28
- }
15
+ margin-bottom: 0;
29
16
  }
30
17
 
31
18
  .components-border-radius-control__input-controls-wrapper {
@@ -35,13 +22,21 @@
35
22
  margin-right: $grid-unit-15;
36
23
  }
37
24
 
38
- .component-border-radius-control__linked-button {
25
+ .components-border-radius-control__linked-button {
39
26
  display: flex;
40
27
  justify-content: center;
41
- margin-top: $grid-unit-10;
42
28
 
43
29
  svg {
44
30
  margin-right: 0;
45
31
  }
46
32
  }
47
33
  }
34
+
35
+ .components-border-radius-control__custom-select-control,
36
+ .components-border-radius-control__range-control {
37
+ flex: 1;
38
+ }
39
+
40
+ .components-border-radius-control__icon {
41
+ flex: 0 0 auto;
42
+ }
@@ -74,7 +74,8 @@ export function getAllValue( values = {} ) {
74
74
  : '';
75
75
  const unit = mode( allUnits );
76
76
 
77
- const allValue = value === 0 || value ? `${ value }${ unit }` : undefined;
77
+ const allValue =
78
+ value === 0 || value ? `${ value }${ unit || '' }` : undefined;
78
79
 
79
80
  return allValue;
80
81
  }
@@ -86,11 +87,26 @@ export function getAllValue( values = {} ) {
86
87
  * @return {boolean} Whether values are mixed.
87
88
  */
88
89
  export function hasMixedValues( values = {} ) {
89
- const allValue = getAllValue( values );
90
- const isMixed =
91
- typeof values === 'string' ? false : isNaN( parseFloat( allValue ) );
90
+ if ( typeof values === 'string' ) {
91
+ return false;
92
+ }
93
+
94
+ if ( ! values || typeof values !== 'object' ) {
95
+ return false;
96
+ }
97
+
98
+ const cornerValues = Object.values( values );
99
+
100
+ if ( cornerValues.length === 0 ) {
101
+ return false;
102
+ }
92
103
 
93
- return isMixed;
104
+ const firstValue = cornerValues[ 0 ];
105
+
106
+ // Check if all values are exactly the same (including undefined)
107
+ const allSame = cornerValues.every( ( value ) => value === firstValue );
108
+
109
+ return ! allSame;
94
110
  }
95
111
 
96
112
  /**
@@ -117,3 +133,160 @@ export function hasDefinedValues( values ) {
117
133
 
118
134
  return !! filteredValues.length;
119
135
  }
136
+
137
+ /**
138
+ * Checks is given value is a radius preset.
139
+ *
140
+ * @param {string} value Value to check
141
+ *
142
+ * @return {boolean} Return true if value is string in format var:preset|border-radius|.
143
+ */
144
+ export function isValuePreset( value ) {
145
+ if ( ! value?.includes ) {
146
+ return false;
147
+ }
148
+ return value === '0' || value.includes( 'var:preset|border-radius|' );
149
+ }
150
+
151
+ /**
152
+ * Returns the slug section of the given preset string.
153
+ *
154
+ * @param {string} value Value to extract slug from.
155
+ *
156
+ * @return {string|undefined} The int value of the slug from given preset.
157
+ */
158
+ export function getPresetSlug( value ) {
159
+ if ( ! value ) {
160
+ return;
161
+ }
162
+
163
+ if ( value === '0' || value === 'default' ) {
164
+ return value;
165
+ }
166
+
167
+ const slug = value.match( /var:preset\|border-radius\|(.+)/ );
168
+
169
+ return slug ? slug[ 1 ] : undefined;
170
+ }
171
+
172
+ /**
173
+ * Converts radius preset value into a Range component value .
174
+ *
175
+ * @param {string} presetValue Value to convert to Range value.
176
+ * @param {Array} presets Array of current radius preset value objects.
177
+ *
178
+ * @return {number} The int value for use in Range control.
179
+ */
180
+ export function getSliderValueFromPreset( presetValue, presets ) {
181
+ if ( presetValue === undefined ) {
182
+ return 0;
183
+ }
184
+ const slug =
185
+ parseFloat( presetValue, 10 ) === 0
186
+ ? '0'
187
+ : getPresetSlug( presetValue );
188
+ const sliderValue = presets.findIndex( ( size ) => {
189
+ return String( size.slug ) === slug;
190
+ } );
191
+
192
+ // Returning NaN rather than undefined as undefined makes range control thumb sit in center
193
+ return sliderValue !== -1 ? sliderValue : NaN;
194
+ }
195
+
196
+ /**
197
+ * Converts a preset into a custom value.
198
+ *
199
+ * @param {string} value Value to convert
200
+ * @param {Array} presets Array of the current radius preset objects
201
+ *
202
+ * @return {string} Mapping of the radius preset to its equivalent custom value.
203
+ */
204
+ export function getCustomValueFromPreset( value, presets ) {
205
+ if ( ! isValuePreset( value ) ) {
206
+ return value;
207
+ }
208
+
209
+ const slug = parseFloat( value, 10 ) === 0 ? '0' : getPresetSlug( value );
210
+ const radiusSize = presets.find( ( size ) => String( size.slug ) === slug );
211
+
212
+ return radiusSize?.size;
213
+ }
214
+
215
+ /**
216
+ * Converts a control value into a preset value.
217
+ *
218
+ * @param {number} controlValue to convert to preset value.
219
+ * @param {string} controlType Type of control
220
+ * @param {Array} presets Array of current radius preset value objects.
221
+ *
222
+ * @return {string} The custom value for use in Range control.
223
+ */
224
+ export function getPresetValueFromControlValue(
225
+ controlValue,
226
+ controlType,
227
+ presets
228
+ ) {
229
+ const size = parseInt( controlValue, 10 );
230
+ if ( controlType === 'selectList' ) {
231
+ if ( size === 0 ) {
232
+ return undefined;
233
+ }
234
+ } else if ( size === 0 ) {
235
+ return '0';
236
+ }
237
+ return `var:preset|border-radius|${ presets[ controlValue ]?.slug }`;
238
+ }
239
+
240
+ /**
241
+ * Converts a custom value to preset value if one can be found.
242
+ *
243
+ * Returns value as-is if no match is found.
244
+ *
245
+ * @param {string} value Value to convert
246
+ * @param {Array} presets Array of the current border radius preset objects
247
+ *
248
+ * @return {string} The preset value if it can be found.
249
+ */
250
+ export function getPresetValueFromCustomValue( value, presets ) {
251
+ // Return value as-is if it is undefined or is already a preset, or '0';
252
+ if ( ! value || isValuePreset( value ) || value === '0' ) {
253
+ return value;
254
+ }
255
+
256
+ const spacingMatch = presets.find(
257
+ ( size ) => String( size.size ) === String( value )
258
+ );
259
+
260
+ if ( spacingMatch?.slug ) {
261
+ return `var:preset|border-radius|${ spacingMatch.slug }`;
262
+ }
263
+
264
+ return value;
265
+ }
266
+
267
+ /**
268
+ * Converts all preset values in a values object to their custom equivalents.
269
+ *
270
+ * @param {Object} values Values object to convert
271
+ * @param {Array} presets Array of current border radius preset objects
272
+ *
273
+ * @return {Object} Values with presets converted to custom values
274
+ */
275
+ export function convertPresetsToCustomValues( values, presets ) {
276
+ if ( ! values || typeof values !== 'object' ) {
277
+ return values;
278
+ }
279
+
280
+ const converted = {};
281
+ Object.keys( values ).forEach( ( key ) => {
282
+ const value = values[ key ];
283
+ if ( isValuePreset( value ) ) {
284
+ const customValue = getCustomValueFromPreset( value, presets );
285
+ converted[ key ] = customValue !== undefined ? customValue : value;
286
+ } else {
287
+ converted[ key ] = value;
288
+ }
289
+ } );
290
+
291
+ return converted;
292
+ }
@@ -145,7 +145,17 @@ export default function BorderPanel( {
145
145
 
146
146
  // Border radius.
147
147
  const showBorderRadius = useHasBorderRadiusControl( settings );
148
- const borderRadiusValues = decodeValue( border?.radius );
148
+ const borderRadiusValues = useMemo( () => {
149
+ if ( typeof border?.radius !== 'object' ) {
150
+ return decodeValue( border?.radius );
151
+ }
152
+ return {
153
+ topLeft: decodeValue( border?.radius?.topLeft ),
154
+ topRight: decodeValue( border?.radius?.topRight ),
155
+ bottomLeft: decodeValue( border?.radius?.bottomLeft ),
156
+ bottomRight: decodeValue( border?.radius?.bottomRight ),
157
+ };
158
+ }, [ border?.radius, decodeValue ] );
149
159
  const setBorderRadius = ( newBorderRadius ) =>
150
160
  setBorder( { ...border, radius: newBorderRadius } );
151
161
  const hasBorderRadius = () => {
@@ -276,6 +286,7 @@ export default function BorderPanel( {
276
286
  panelId={ panelId }
277
287
  >
278
288
  <BorderRadiusControl
289
+ presets={ settings?.border?.radiusSizes }
279
290
  values={ borderRadiusValues }
280
291
  onChange={ ( newValue ) => {
281
292
  setBorderRadius( newValue || undefined );
@@ -32,6 +32,7 @@ const VALID_SETTINGS = [
32
32
  'border.radius',
33
33
  'border.style',
34
34
  'border.width',
35
+ 'border.radiusSizes',
35
36
  'shadow.presets',
36
37
  'shadow.defaultPresets',
37
38
  'color.background',
@@ -80,7 +80,12 @@ export const PRESET_METADATA = [
80
80
  path: [ 'spacing', 'spacingSizes' ],
81
81
  valueKey: 'size',
82
82
  cssVarInfix: 'spacing',
83
- valueFunc: ( { size } ) => size,
83
+ classes: [],
84
+ },
85
+ {
86
+ path: [ 'border', 'radiusSizes' ],
87
+ valueKey: 'size',
88
+ cssVarInfix: 'border-radius',
84
89
  classes: [],
85
90
  },
86
91
  ];
@@ -132,8 +132,12 @@ export function RichTextWrapper(
132
132
  return { isSelected: false };
133
133
  }
134
134
 
135
- const { getSelectionStart, getSelectionEnd } =
136
- select( blockEditorStore );
135
+ const {
136
+ getSelectionStart,
137
+ getSelectionEnd,
138
+ getBlockEditingMode,
139
+ isNavigationMode,
140
+ } = select( blockEditorStore );
137
141
  const selectionStart = getSelectionStart();
138
142
  const selectionEnd = getSelectionEnd();
139
143
 
@@ -154,15 +158,19 @@ export function RichTextWrapper(
154
158
  selectionStart: isSelected ? selectionStart.offset : undefined,
155
159
  selectionEnd: isSelected ? selectionEnd.offset : undefined,
156
160
  isSelected,
161
+ isContentOnlyWriteMode:
162
+ isNavigationMode() &&
163
+ getBlockEditingMode( clientId ) === 'contentOnly',
157
164
  };
158
165
  };
159
- const { selectionStart, selectionEnd, isSelected } = useSelect( selector, [
160
- clientId,
161
- identifier,
162
- instanceId,
163
- originalIsSelected,
164
- isBlockSelected,
165
- ] );
166
+ const { selectionStart, selectionEnd, isSelected, isContentOnlyWriteMode } =
167
+ useSelect( selector, [
168
+ clientId,
169
+ identifier,
170
+ instanceId,
171
+ originalIsSelected,
172
+ isBlockSelected,
173
+ ] );
166
174
 
167
175
  const { disableBoundBlock, bindingsPlaceholder, bindingsLabel } = useSelect(
168
176
  ( select ) => {
@@ -323,8 +331,9 @@ export function RichTextWrapper(
323
331
  } = useFormatTypes( {
324
332
  clientId,
325
333
  identifier,
326
- withoutInteractiveFormatting,
327
334
  allowedFormats: adjustedAllowedFormats,
335
+ withoutInteractiveFormatting,
336
+ disableNoneEssentialFormatting: isContentOnlyWriteMode,
328
337
  } );
329
338
 
330
339
  function addEditorOnlyFormats( value ) {