@wordpress/edit-post 8.39.0 → 8.39.1-next.v.20260206T143.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/edit-post",
3
- "version": "8.39.0",
3
+ "version": "8.39.1-next.v.20260206T143.0+81f8de885",
4
4
  "description": "Edit Post module for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -48,37 +48,37 @@
48
48
  "postbox"
49
49
  ],
50
50
  "dependencies": {
51
- "@wordpress/a11y": "^4.39.0",
52
- "@wordpress/admin-ui": "^1.7.0",
53
- "@wordpress/api-fetch": "^7.39.0",
54
- "@wordpress/base-styles": "^6.15.0",
55
- "@wordpress/block-editor": "^15.12.0",
56
- "@wordpress/block-library": "^9.39.0",
57
- "@wordpress/blocks": "^15.12.0",
58
- "@wordpress/commands": "^1.39.0",
59
- "@wordpress/components": "^32.1.0",
60
- "@wordpress/compose": "^7.39.0",
61
- "@wordpress/core-data": "^7.39.0",
62
- "@wordpress/data": "^10.39.0",
63
- "@wordpress/deprecated": "^4.39.0",
64
- "@wordpress/dom": "^4.39.0",
65
- "@wordpress/editor": "^14.39.0",
66
- "@wordpress/element": "^6.39.0",
67
- "@wordpress/global-styles-engine": "^1.6.0",
68
- "@wordpress/hooks": "^4.39.0",
69
- "@wordpress/html-entities": "^4.39.0",
70
- "@wordpress/i18n": "^6.12.0",
71
- "@wordpress/icons": "^11.6.0",
72
- "@wordpress/keyboard-shortcuts": "^5.39.0",
73
- "@wordpress/keycodes": "^4.39.0",
74
- "@wordpress/notices": "^5.39.0",
75
- "@wordpress/plugins": "^7.39.0",
76
- "@wordpress/preferences": "^4.39.0",
77
- "@wordpress/private-apis": "^1.39.0",
78
- "@wordpress/url": "^4.39.0",
79
- "@wordpress/viewport": "^6.39.0",
80
- "@wordpress/warning": "^3.39.0",
81
- "@wordpress/widgets": "^4.39.0",
51
+ "@wordpress/a11y": "^4.39.1-next.v.20260206T143.0+81f8de885",
52
+ "@wordpress/admin-ui": "^1.8.1-next.v.20260206T143.0+81f8de885",
53
+ "@wordpress/api-fetch": "^7.39.1-next.v.20260206T143.0+81f8de885",
54
+ "@wordpress/base-styles": "^6.15.1-next.v.20260206T143.0+81f8de885",
55
+ "@wordpress/block-editor": "^15.12.2-next.v.20260206T143.0+81f8de885",
56
+ "@wordpress/block-library": "^9.39.1-next.v.20260206T143.0+81f8de885",
57
+ "@wordpress/blocks": "^15.12.1-next.v.20260206T143.0+81f8de885",
58
+ "@wordpress/commands": "^1.39.1-next.v.20260206T143.0+81f8de885",
59
+ "@wordpress/components": "^32.2.1-next.v.20260206T143.0+81f8de885",
60
+ "@wordpress/compose": "^7.39.1-next.v.20260206T143.0+81f8de885",
61
+ "@wordpress/core-data": "^7.39.1-next.v.20260206T143.0+81f8de885",
62
+ "@wordpress/data": "^10.39.1-next.v.20260206T143.0+81f8de885",
63
+ "@wordpress/deprecated": "^4.39.1-next.v.20260206T143.0+81f8de885",
64
+ "@wordpress/dom": "^4.39.1-next.v.20260206T143.0+81f8de885",
65
+ "@wordpress/editor": "^14.39.1-next.v.20260206T143.0+81f8de885",
66
+ "@wordpress/element": "^6.39.1-next.v.20260206T143.0+81f8de885",
67
+ "@wordpress/global-styles-engine": "^1.6.1-next.v.20260206T143.0+81f8de885",
68
+ "@wordpress/hooks": "^4.39.1-next.v.20260206T143.0+81f8de885",
69
+ "@wordpress/html-entities": "^4.39.1-next.v.20260206T143.0+81f8de885",
70
+ "@wordpress/i18n": "^6.12.1-next.v.20260206T143.0+81f8de885",
71
+ "@wordpress/icons": "^11.6.1-next.v.20260206T143.0+81f8de885",
72
+ "@wordpress/keyboard-shortcuts": "^5.39.1-next.v.20260206T143.0+81f8de885",
73
+ "@wordpress/keycodes": "^4.39.1-next.v.20260206T143.0+81f8de885",
74
+ "@wordpress/notices": "^5.39.1-next.v.20260206T143.0+81f8de885",
75
+ "@wordpress/plugins": "^7.39.1-next.v.20260206T143.0+81f8de885",
76
+ "@wordpress/preferences": "^4.39.1-next.v.20260206T143.0+81f8de885",
77
+ "@wordpress/private-apis": "^1.39.1-next.v.20260206T143.0+81f8de885",
78
+ "@wordpress/url": "^4.39.1-next.v.20260206T143.0+81f8de885",
79
+ "@wordpress/viewport": "^6.39.1-next.v.20260206T143.0+81f8de885",
80
+ "@wordpress/warning": "^3.39.1-next.v.20260206T143.0+81f8de885",
81
+ "@wordpress/widgets": "^4.39.1-next.v.20260206T143.0+81f8de885",
82
82
  "clsx": "^2.1.1",
83
83
  "memize": "^2.1.0"
84
84
  },
@@ -89,5 +89,5 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
- "gitHead": "eee1cfb1472f11183e40fb77465a5f13145df7ad"
92
+ "gitHead": "6ae3fbb907e6c309ff6a0685e5e5ff0c2ee23b15"
93
93
  }
@@ -25,7 +25,6 @@ import { PluginArea } from '@wordpress/plugins';
25
25
  import { __, sprintf } from '@wordpress/i18n';
26
26
  import {
27
27
  useCallback,
28
- useEffect,
29
28
  useMemo,
30
29
  useId,
31
30
  useRef,
@@ -41,15 +40,15 @@ import { decodeEntities } from '@wordpress/html-entities';
41
40
  import { store as coreStore } from '@wordpress/core-data';
42
41
  import {
43
42
  Icon,
44
- ResizableBox,
45
43
  SlotFillProvider,
46
44
  Tooltip,
47
45
  VisuallyHidden,
48
46
  __unstableUseNavigateRegions as useNavigateRegions,
47
+ privateApis as componentsPrivateApis,
49
48
  } from '@wordpress/components';
50
49
  import {
51
- useEvent,
52
50
  useMediaQuery,
51
+ useMergeRefs,
53
52
  useRefEffect,
54
53
  useViewportMatch,
55
54
  } from '@wordpress/compose';
@@ -73,6 +72,8 @@ import useNavigateToEntityRecord from '../../hooks/use-navigate-to-entity-record
73
72
  import { useMetaBoxInitialization } from '../meta-boxes/use-meta-box-initialization';
74
73
 
75
74
  const { useCommandContext } = unlock( commandsPrivateApis );
75
+ /** @type {{} & {useDrag: import('@use-gesture/react').useDrag}} */
76
+ const { useDrag } = unlock( componentsPrivateApis );
76
77
  const { Editor, FullscreenMode } = unlock( editorPrivateApis );
77
78
  const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis );
78
79
  const DESIGN_POST_TYPES = [
@@ -148,10 +149,10 @@ function MetaBoxesMain( { isLegacy } ) {
148
149
  ];
149
150
  }, [] );
150
151
  const { set: setPreference } = useDispatch( preferencesStore );
151
- const metaBoxesMainRef = useRef();
152
+
152
153
  const isShort = useMediaQuery( '(max-height: 549px)' );
153
154
 
154
- const [ { min, max }, setHeightConstraints ] = useState( () => ( {} ) );
155
+ const [ { min = 0, max }, setHeightConstraints ] = useState( () => ( {} ) );
155
156
  // Keeps the resizable area’s size constraints updated taking into account
156
157
  // editor notices. The constraints are also used to derive the value for the
157
158
  // aria-valuenow attribute on the separator.
@@ -184,25 +185,30 @@ function MetaBoxesMain( { isLegacy } ) {
184
185
  }
185
186
  return () => observer.disconnect();
186
187
  }, [] );
188
+ const metaBoxesMainRef = useRef();
189
+ const setMainRefs = useMergeRefs( [
190
+ metaBoxesMainRef,
191
+ effectSizeConstraints,
192
+ ] );
187
193
 
188
- const resizeDataRef = useRef( {} );
189
194
  const separatorRef = useRef();
190
195
  const separatorHelpId = useId();
191
196
 
197
+ const heightRef = useRef();
198
+
192
199
  /**
193
200
  * @param {number|'auto'} [candidateHeight] Height in pixels or 'auto'.
194
201
  * @param {boolean} isPersistent Whether to persist the height in preferences.
195
- * @param {boolean} isInstant Whether to update the height in the DOM.
196
202
  */
197
- const applyHeight = (
198
- candidateHeight = 'auto',
199
- isPersistent,
200
- isInstant
201
- ) => {
203
+ const applyHeight = ( candidateHeight = 'auto', isPersistent ) => {
204
+ let styleHeight;
202
205
  if ( candidateHeight === 'auto' ) {
203
206
  isPersistent = false; // Just in case — “auto” should never persist.
207
+ styleHeight = candidateHeight;
204
208
  } else {
205
209
  candidateHeight = Math.min( max, Math.max( min, candidateHeight ) );
210
+ heightRef.current = candidateHeight;
211
+ styleHeight = `${ candidateHeight }px`;
206
212
  }
207
213
  if ( isPersistent ) {
208
214
  setPreference(
@@ -211,35 +217,60 @@ function MetaBoxesMain( { isLegacy } ) {
211
217
  candidateHeight
212
218
  );
213
219
  }
214
- // Updates aria-valuenow only when not persisting the value because otherwise
215
- // it's done by the render that persisting the value causes.
216
- else if ( ! isShort ) {
217
- separatorRef.current.ariaValueNow =
218
- getAriaValueNow( candidateHeight );
219
- }
220
- if ( isInstant ) {
221
- metaBoxesMainRef.current.updateSize( {
222
- height: candidateHeight,
223
- // Oddly, when the event that triggered this was not from the mouse (e.g. keydown),
224
- // if `width` is left unspecified a subsequent drag gesture applies a fixed
225
- // width and the pane fails to widen/narrow with parent width changes from
226
- // sidebars opening/closing or window resizes.
227
- width: 'auto',
228
- } );
220
+ // Applies imperative DOM updates only when not persisting the value
221
+ // because otherwise it's done by the subsequent render.
222
+ else {
223
+ metaBoxesMainRef.current.style.height = styleHeight;
224
+ if ( ! isShort ) {
225
+ separatorRef.current.ariaValueNow =
226
+ getAriaValueNow( candidateHeight );
227
+ }
229
228
  }
230
229
  };
231
- const getRenderValues = useEvent( () => ( { isOpen, openHeight, min } ) );
232
- // Sets the height to 'auto' when not resizable (isShort) and to the
233
- // preferred height when resizable.
234
- useEffect( () => {
235
- const fresh = getRenderValues();
236
- // Tests for `min` having a value to skip the first render.
237
- if ( fresh.min !== undefined && metaBoxesMainRef.current ) {
238
- const usedOpenHeight = isShort ? 'auto' : fresh.openHeight;
239
- const usedHeight = fresh.isOpen ? usedOpenHeight : fresh.min;
240
- applyHeight( usedHeight, false, true );
241
- }
242
- }, [ isShort ] );
230
+
231
+ // useDrag includes keyboard support with arrow keys emulating a drag.
232
+ // TODO: Support more/all keyboard interactions from the window splitter pattern:
233
+ // https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
234
+ const bindDragGesture = useDrag(
235
+ ( { movement, first, last, memo, tap, args } ) => {
236
+ const pane = metaBoxesMainRef.current;
237
+ const [ , yMovement ] = movement;
238
+ if ( first ) {
239
+ pane.classList.add( 'is-resizing' );
240
+ let fromHeight = heightRef.current ?? pane.offsetHeight;
241
+ if ( isOpen ) {
242
+ // Starts from max in case shortening the window has imposed it.
243
+ if ( fromHeight > max ) {
244
+ fromHeight = max;
245
+ }
246
+ } else {
247
+ fromHeight = min;
248
+ }
249
+ applyHeight( fromHeight - yMovement );
250
+ return { fromHeight };
251
+ }
252
+
253
+ if ( ! first && ! last && ! tap ) {
254
+ applyHeight( memo.fromHeight - yMovement );
255
+ return memo;
256
+ }
257
+ // Here, `last === true` – it’s the final event of the gesture.
258
+
259
+ pane.classList.remove( 'is-resizing' );
260
+ if ( tap ) {
261
+ const [ onTap ] = args;
262
+ onTap?.();
263
+ return;
264
+ }
265
+ const nextIsOpen = heightRef.current > min;
266
+ persistIsOpen( nextIsOpen );
267
+ // Persists height only if still open. This is so that when closed by a drag the
268
+ // prior height can be restored by the toggle button instead of having to drag
269
+ // the pane open again.
270
+ applyHeight( heightRef.current, nextIsOpen );
271
+ },
272
+ { keyboardDisplacement: 20, filterTaps: true }
273
+ );
243
274
 
244
275
  if ( ! hasAnyVisible ) {
245
276
  return;
@@ -261,52 +292,41 @@ function MetaBoxesMain( { isLegacy } ) {
261
292
  }
262
293
 
263
294
  const isAutoHeight = openHeight === undefined;
295
+ const usedOpenHeight = isShort ? 'auto' : openHeight;
296
+ const usedHeight = isOpen ? usedOpenHeight : min;
297
+
264
298
  const getAriaValueNow = ( height ) =>
265
299
  Math.round( ( ( height - min ) / ( max - min ) ) * 100 );
266
300
  const usedAriaValueNow =
267
- max === undefined || isAutoHeight ? 50 : getAriaValueNow( openHeight );
301
+ max === undefined || isAutoHeight ? 50 : getAriaValueNow( usedHeight );
268
302
 
269
303
  const persistIsOpen = ( to = ! isOpen ) =>
270
304
  setPreference( 'core/edit-post', 'metaBoxesMainIsOpen', to );
271
305
 
272
- // TODO: Support more/all keyboard interactions from the window splitter pattern:
273
- // https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
274
- const onSeparatorKeyDown = ( event ) => {
275
- const delta = { ArrowUp: 20, ArrowDown: -20 }[ event.key ];
276
- if ( delta ) {
277
- const pane = metaBoxesMainRef.current.resizable;
278
- const fromHeight = isAutoHeight ? pane.offsetHeight : openHeight;
279
- const nextHeight = delta + fromHeight;
280
- applyHeight( nextHeight, true, true );
281
- persistIsOpen( nextHeight > min );
282
- event.preventDefault();
283
- }
284
- };
285
306
  const paneLabel = __( 'Meta Boxes' );
286
307
 
308
+ // The toggle button. It also resizes when the viewport is tall to provide
309
+ // a larger hit area than the small separator button.
287
310
  const toggle = (
288
311
  <button
289
312
  aria-expanded={ isOpen }
313
+ // Toggles for all clicks when short and only keyboard “clicks” when
314
+ // resizable because pointer input is handled by the drag gesture.
290
315
  onClick={ ( { detail } ) => {
291
- const { isToggleInferred } = resizeDataRef.current;
292
- if ( isShort || ! detail || isToggleInferred ) {
316
+ if ( isShort || ! detail ) {
293
317
  persistIsOpen();
294
- const usedOpenHeight = isShort ? 'auto' : openHeight;
295
- const usedHeight = isOpen ? min : usedOpenHeight;
296
- applyHeight( usedHeight, false, true );
297
318
  }
298
319
  } }
299
- // Prevents resizing in short viewports.
300
- { ...( isShort && {
301
- onMouseDown: ( event ) => event.stopPropagation(),
302
- onTouchStart: ( event ) => event.stopPropagation(),
303
- } ) }
320
+ // Passes a toggle callback that the drag gesture handler calls when
321
+ // it interprets the input as a click/tap.
322
+ { ...( ! isShort && bindDragGesture( persistIsOpen ) ) }
304
323
  >
305
324
  { paneLabel }
306
325
  <Icon icon={ isOpen ? chevronUp : chevronDown } />
307
326
  </button>
308
327
  );
309
328
 
329
+ // The separator button that provides a11y for resizing.
310
330
  const separator = ! isShort && (
311
331
  <>
312
332
  <Tooltip text={ __( 'Drag to resize' ) }>
@@ -316,86 +336,33 @@ function MetaBoxesMain( { isLegacy } ) {
316
336
  aria-valuenow={ usedAriaValueNow }
317
337
  aria-label={ __( 'Drag to resize' ) }
318
338
  aria-describedby={ separatorHelpId }
319
- onKeyDown={ onSeparatorKeyDown }
339
+ { ...bindDragGesture() }
320
340
  />
321
341
  </Tooltip>
322
342
  <VisuallyHidden id={ separatorHelpId }>
323
343
  { __(
324
- 'Use up and down arrow keys to resize the meta box panel.'
344
+ 'Use up and down arrow keys to resize the meta box pane.'
325
345
  ) }
326
346
  </VisuallyHidden>
327
347
  </>
328
348
  );
329
349
 
330
- const paneProps = /** @type {Parameters<typeof ResizableBox>[0]} */ ( {
331
- as: NavigableRegion,
332
- ref: metaBoxesMainRef,
333
- className: 'edit-post-meta-boxes-main',
334
- defaultSize: { height: isOpen ? openHeight : 0 },
335
- minHeight: min,
336
- maxHeight: max,
337
- enable: { top: true },
338
- handleClasses: { top: 'edit-post-meta-boxes-main__presenter' },
339
- handleComponent: {
340
- top: (
341
- <>
342
- { toggle }
343
- { separator }
344
- </>
345
- ),
346
- },
347
- // Avoids hiccups while dragging over objects like iframes and ensures that
348
- // the event to end the drag is captured by the target (resize handle)
349
- // whether or not it’s under the pointer.
350
- onPointerDown: ( { pointerId, target } ) => {
351
- if ( separatorRef.current?.parentElement.contains( target ) ) {
352
- target.setPointerCapture( pointerId );
353
- }
354
- },
355
- onResizeStart: ( { timeStamp }, direction, elementRef ) => {
356
- if ( isAutoHeight ) {
357
- // Sets the starting height to avoid visual jumps in height and
358
- // aria-valuenow being `NaN` for the first (few) resize events.
359
- applyHeight( elementRef.offsetHeight, false, true );
360
- }
361
- elementRef.classList.add( 'is-resizing' );
362
- resizeDataRef.current = { timeStamp, maxDelta: 0 };
363
- },
364
- onResize: ( event, direction, elementRef, delta ) => {
365
- const { maxDelta } = resizeDataRef.current;
366
- const newDelta = Math.abs( delta.height );
367
- resizeDataRef.current.maxDelta = Math.max( maxDelta, newDelta );
368
- applyHeight( metaBoxesMainRef.current.state.height );
369
- },
370
- onResizeStop: ( event, direction, elementRef ) => {
371
- elementRef.classList.remove( 'is-resizing' );
372
- const duration = event.timeStamp - resizeDataRef.current.timeStamp;
373
- const wasSeparator = event.target === separatorRef.current;
374
- const { maxDelta } = resizeDataRef.current;
375
- const isToggleInferred =
376
- maxDelta < 1 || ( duration < 144 && maxDelta < 5 );
377
- if ( isShort || ( ! wasSeparator && isToggleInferred ) ) {
378
- resizeDataRef.current.isToggleInferred = true;
379
- } else {
380
- const { height } = metaBoxesMainRef.current.state;
381
- const nextIsOpen = height > min;
382
- persistIsOpen( nextIsOpen );
383
- // Persists height only if still open. This is so that when closed by a drag the
384
- // prior height can be restored by the toggle button instead of having to drag
385
- // the pane open again. Also, if already closed, a click on the separator won’t
386
- // persist the height as the minimum.
387
- if ( nextIsOpen ) {
388
- applyHeight( height, true );
389
- }
390
- }
391
- },
392
- } );
393
-
394
350
  return (
395
- <ResizableBox aria-label={ paneLabel } { ...paneProps }>
396
- <meta ref={ effectSizeConstraints } />
351
+ <NavigableRegion
352
+ aria-label={ paneLabel }
353
+ ref={ setMainRefs }
354
+ className={ clsx(
355
+ 'edit-post-meta-boxes-main',
356
+ ! isShort && 'is-resizable'
357
+ ) }
358
+ style={ { height: usedHeight } }
359
+ >
360
+ <div className="edit-post-meta-boxes-main__presenter">
361
+ { toggle }
362
+ { separator }
363
+ </div>
397
364
  { contents }
398
- </ResizableBox>
365
+ </NavigableRegion>
399
366
  );
400
367
  }
401
368
 
@@ -10,20 +10,22 @@
10
10
  display: flex;
11
11
  flex-direction: column;
12
12
  overflow: hidden;
13
- padding-block-start: $button-size-compact;
14
13
  }
15
14
 
16
15
  .edit-post-meta-boxes-main__presenter {
16
+ flex-shrink: 0;
17
17
  display: flex;
18
+ justify-content: center;
18
19
  box-shadow: 0 $border-width $gray-300;
19
20
  // Windows High Contrast mode will show this outline, but not the shadow.
20
21
  outline: 1px solid transparent;
21
22
  position: relative;
22
- // Re-resizable adds an invisible overlay during resizes (apparently meant to avoid text selections)
23
- // and this has to appear in front of it or else click events will be blocked on touch devices.
24
- z-index: 10000;
25
- inset: 0 0 auto;
23
+ z-index: 1;
26
24
  height: $button-size-compact;
25
+ // Enlarge the toggle/resizable hit area on touch devices.
26
+ @media (pointer: coarse) {
27
+ height: $button-size;
28
+ }
27
29
 
28
30
  // Style reset for both toggle and separator buttons.
29
31
  > button {
@@ -32,6 +34,8 @@
32
34
  border: none;
33
35
  outline: none;
34
36
  background-color: transparent;
37
+ // Ensure the drag gesture behaves as expected for touch input.
38
+ touch-action: none;
35
39
  }
36
40
 
37
41
  > button[aria-expanded] {
@@ -56,12 +60,12 @@
56
60
  }
57
61
  }
58
62
 
59
- button[role="separator"] {
63
+ // This button is only present when resizable.
64
+ > button[role="separator"] {
60
65
  cursor: row-resize;
61
- width: $grid-unit-80;
62
- margin: auto;
63
66
  position: absolute;
64
- inset: 0;
67
+ width: $grid-unit-80;
68
+ height: inherit;
65
69
 
66
70
  &::before {
67
71
  content: "";
@@ -80,23 +84,13 @@
80
84
  }
81
85
  }
82
86
 
83
- &:is(:hover, :focus-visible)::before {
87
+ &:is(:hover, :focus)::before {
84
88
  background-color: var(--wp-admin-theme-color);
85
89
  width: $grid-unit-80 + $grid-unit-20;
86
90
  }
87
91
  }
88
92
  }
89
93
 
90
- // Enlarge the toggle/resizable hit area on touch devices.
91
- @media (pointer: coarse) {
92
- .edit-post-meta-boxes-main {
93
- padding-block-start: $button-size;
94
- }
95
- .edit-post-meta-boxes-main__presenter {
96
- height: $button-size;
97
- }
98
- }
99
-
100
94
  .edit-post-meta-boxes-main__liner {
101
95
  // Enable scrolling only for the split view.
102
96
  .edit-post-meta-boxes-main & {