Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize meta box pane without ResizableBox #66735

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/components/src/private-apis.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { useDrag } from '@use-gesture/react';

/**
* Internal dependencies
*/
Expand All @@ -19,4 +24,5 @@ lock( privateApis, {
Theme,
Menu,
kebabCase,
useDrag,
} );
171 changes: 76 additions & 95 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ import { decodeEntities } from '@wordpress/html-entities';
import { store as coreStore } from '@wordpress/core-data';
import {
Icon,
ResizableBox,
SlotFillProvider,
Tooltip,
VisuallyHidden,
__unstableUseNavigateRegions as useNavigateRegions,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import {
useMediaQuery,
Expand Down Expand Up @@ -78,6 +78,7 @@ import useNavigateToEntityRecord from '../../hooks/use-navigate-to-entity-record
const { getLayoutStyles } = unlock( blockEditorPrivateApis );
const { useCommands } = unlock( coreCommandsPrivateApis );
const { useCommandContext } = unlock( commandsPrivateApis );
const { useDrag } = unlock( componentsPrivateApis );
const { Editor, FullscreenMode, NavigableRegion } = unlock( editorPrivateApis );
const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis );
const DESIGN_POST_TYPES = [
Expand Down Expand Up @@ -202,29 +203,54 @@ function MetaBoxesMain( { isLegacy } ) {
const separatorRef = useRef();
const separatorHelpId = useId();

const [ isUntouched, setIsUntouched ] = useState( true );
const applyHeight = ( candidateHeight, isPersistent, isInstant ) => {
const heightRef = useRef();
const applyHeight = ( candidateHeight, isPersistent ) => {
const nextHeight = Math.min( max, Math.max( min, candidateHeight ) );
heightRef.current = nextHeight;
if ( isPersistent ) {
setPreference(
'core/edit-post',
'metaBoxesMainOpenHeight',
nextHeight
);
} else {
metaBoxesMainRef.current.style.height = `${ nextHeight }px`;
separatorRef.current.ariaValueNow = getAriaValueNow( nextHeight );
}
if ( isInstant ) {
metaBoxesMainRef.current.updateSize( {
height: nextHeight,
// Oddly, when the event that triggered this was not from the mouse (e.g. keydown),
// if `width` is left unspecified a subsequent drag gesture applies a fixed
// width and the pane fails to widen/narrow with parent width changes from
// sidebars opening/closing or window resizes.
width: 'auto',
} );
}
};
const [ isDragging, setIsDragging ] = useState( false );
// useDrag includes basic keyboard support with arrow keys emulating a drag.
// TODO: Support more/all keyboard interactions from the window splitter pattern:
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
const bindDragGesture = useDrag( ( state ) => {
const { movement, first, last, memo, swipe } = state;
const [ , yMovement ] = movement;
if ( first ) {
setIsDragging( true );
let fromHeight = heightRef.current;
if ( fromHeight === undefined ) {
fromHeight = metaBoxesMainRef.current.offsetHeight;
} else if ( heightRef.current > max ) {
// Starts from max in case shortening the window has imposed it.
fromHeight = max;
}
if ( yMovement !== 0 ) {
applyHeight( fromHeight - yMovement );
}
return { fromHeight: fromHeight ?? heightRef.current };
}
if ( ! first && ! last ) {
applyHeight( memo.fromHeight - yMovement );
return memo;
}
setIsDragging( false );
const [ , swipeY ] = swipe;
if ( swipeY ) {
applyHeight( swipeY === -1 ? max : min, true );
} else if ( yMovement !== 0 ) {
applyHeight( heightRef.current, true );
}
} );

if ( ! hasAnyVisible ) {
return;
Expand Down Expand Up @@ -252,7 +278,7 @@ function MetaBoxesMain( { isLegacy } ) {
let usedMax = '50%'; // Approximation before max has a value.
if ( max !== undefined ) {
// Halves the available max height until a user height is set.
usedMax = isAutoHeight && isUntouched ? max / 2 : max;
usedMax = isAutoHeight && ! isDragging ? max / 2 : max;
}

const getAriaValueNow = ( height ) =>
Expand All @@ -263,104 +289,59 @@ function MetaBoxesMain( { isLegacy } ) {
const toggle = () =>
setPreference( 'core/edit-post', 'metaBoxesMainIsOpen', ! isOpen );

// TODO: Support more/all keyboard interactions from the window splitter pattern:
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
const onSeparatorKeyDown = ( event ) => {
const delta = { ArrowUp: 20, ArrowDown: -20 }[ event.key ];
if ( delta ) {
const pane = metaBoxesMainRef.current.resizable;
const fromHeight = isAutoHeight ? pane.offsetHeight : openHeight;
const nextHeight = delta + fromHeight;
applyHeight( nextHeight, true, true );
event.preventDefault();
}
};
const className = 'edit-post-meta-boxes-main';
const paneLabel = __( 'Meta Boxes' );
let Pane, paneProps;
let paneProps, paneButtonProps;
if ( isShort ) {
Pane = NavigableRegion;
paneProps = {
className: clsx( className, 'is-toggle-only' ),
};
paneButtonProps = {
'aria-expanded': isOpen,
onClick: toggle,
children: (
<>
{ paneLabel }
<Icon icon={ isOpen ? chevronUp : chevronDown } />
</>
),
};
} else {
Pane = ResizableBox;
paneProps = /** @type {Parameters<typeof ResizableBox>[0]} */ ( {
as: NavigableRegion,
paneProps = {
ref: metaBoxesMainRef,
className: clsx( className, 'is-resizable' ),
defaultSize: { height: openHeight },
minHeight: min,
maxHeight: usedMax,
enable: {
top: true,
right: false,
bottom: false,
left: false,
topLeft: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
},
handleClasses: { top: 'edit-post-meta-boxes-main__presenter' },
handleComponent: {
top: (
<>
<Tooltip text={ __( 'Drag to resize' ) }>
<button // eslint-disable-line jsx-a11y/role-supports-aria-props
ref={ separatorRef }
role="separator" // eslint-disable-line jsx-a11y/no-interactive-element-to-noninteractive-role
aria-valuenow={ usedAriaValueNow }
aria-label={ __( 'Drag to resize' ) }
aria-describedby={ separatorHelpId }
onKeyDown={ onSeparatorKeyDown }
/>
</Tooltip>
style: { height: openHeight, maxHeight: usedMax },
};
paneButtonProps = {
ref: separatorRef,
role: 'separator',
'aria-valuenow': usedAriaValueNow,
'aria-label': __( 'Drag to resize' ),
'aria-describedby': separatorHelpId,
...bindDragGesture(),
children: (
<Tooltip text={ __( 'Drag to resize' ) }>
<div tabIndex={ -1 }>
<VisuallyHidden id={ separatorHelpId }>
{ __(
'Use up and down arrow keys to resize the meta box panel.'
'Use up and down arrow keys to resize the meta box pane.'
) }
</VisuallyHidden>
</>
),
},
// Avoids hiccups while dragging over objects like iframes and ensures that
// the event to end the drag is captured by the target (resize handle)
// whether or not it’s under the pointer.
onPointerDown: ( { pointerId, target } ) => {
target.setPointerCapture( pointerId );
},
onResizeStart: ( event, direction, elementRef ) => {
if ( isAutoHeight ) {
// Sets the starting height to avoid visual jumps in height and
// aria-valuenow being `NaN` for the first (few) resize events.
applyHeight( elementRef.offsetHeight, false, true );
setIsUntouched( false );
}
},
onResize: () =>
applyHeight( metaBoxesMainRef.current.state.height ),
onResizeStop: () =>
applyHeight( metaBoxesMainRef.current.state.height, true ),
} );
</div>
</Tooltip>
),
};
}

return (
<Pane aria-label={ paneLabel } { ...paneProps }>
{ isShort ? (
<button
aria-expanded={ isOpen }
className="edit-post-meta-boxes-main__presenter"
onClick={ toggle }
>
{ paneLabel }
<Icon icon={ isOpen ? chevronUp : chevronDown } />
</button>
) : (
<meta ref={ effectSizeConstraints } />
) }
<NavigableRegion aria-label={ paneLabel } { ...paneProps }>
{ ! isShort && <meta ref={ effectSizeConstraints } /> }
<button
className="edit-post-meta-boxes-main__presenter"
{ ...paneButtonProps }
/>
{ contents }
</Pane>
</NavigableRegion>
);
}

Expand Down
44 changes: 14 additions & 30 deletions packages/edit-post/src/components/layout/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
display: flex;
flex-direction: column;
overflow: hidden;

&.is-resizable {
padding-block-start: $grid-unit-30;
}
}

.edit-post-meta-boxes-main__presenter {
Expand All @@ -19,16 +15,10 @@
outline: 1px solid transparent;
position: relative;
z-index: 1;

// Button style reset for both toggle or resizable.
.is-toggle-only > &,
.is-resizable.edit-post-meta-boxes-main & > button {
appearance: none;
padding: 0;
border: none;
outline: none;
background-color: transparent;
}
appearance: none;
padding: 0;
border: none;
background-color: transparent;

.is-toggle-only > & {
flex-shrink: 0;
Expand All @@ -52,13 +42,17 @@
}
}

.is-resizable.edit-post-meta-boxes-main & {
inset: 0 0 auto;
.is-resizable > & {
cursor: row-resize;
touch-action: none;
height: $grid-unit-30;
@media (pointer: coarse) {
height: $button-size-compact;
}

> button {
cursor: inherit;
> div {
width: $grid-unit-80;
height: $grid-unit-30;
height: inherit;
margin: auto;

&::before {
Expand All @@ -78,23 +72,13 @@
}
}

&:is(:hover, :focus-within) > button::before {
&:is(:hover, :focus) > div::before {
background-color: var(--wp-admin-theme-color);
width: $grid-unit-80 + $grid-unit-20;
}
}
}

@media (pointer: coarse) {
.is-resizable.edit-post-meta-boxes-main {
padding-block-start: $button-size-compact;

.edit-post-meta-boxes-main__presenter > button {
height: $button-size-compact;
}
}
}

.edit-post-meta-boxes-main__liner {
overflow: auto;
// Keep the contents behind the resize handle or details summary.
Expand Down
Loading