From 39ecfff0fdb4458ec5fb3e84cc5c2189ae86b4ec Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Sat, 8 Jul 2023 09:13:48 +0900 Subject: [PATCH 01/14] ResizableFrame: Make keyboard accessible --- .../src/components/resizable-frame/index.js | 83 +++++++++++++------ .../src/components/resizable-frame/style.scss | 11 +-- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index a13881e7905ffa..85411d24587fed 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -9,9 +9,11 @@ import classnames from 'classnames'; import { useState, useRef, useEffect } from '@wordpress/element'; import { ResizableBox, + Tooltip, __unstableMotion as motion, } from '@wordpress/components'; import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -42,6 +44,8 @@ const FRAME_TARGET_ASPECT_RATIO = 9 / 19.5; // viewport's edge. If the frame is resized to be closer to the viewport's edge // than this distance, then "canvas mode" will be enabled. const SNAP_TO_EDIT_CANVAS_MODE_THRESHOLD = 200; +// Default size for the `frameSize` state. +const INITIAL_FRAME_SIZE = { width: '100%', height: '100%' }; function calculateNewHeight( width, initialAspectRatio ) { const lerp = ( a, b, amount ) => { @@ -78,14 +82,11 @@ function ResizableFrame( { oversizedClassName, innerContentStyle, } ) { - const [ frameSize, setFrameSize ] = useState( { - width: '100%', - height: '100%', - } ); + const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE ); // The width of the resizable frame when a new resize gesture starts. const [ startingWidth, setStartingWidth ] = useState(); const [ isResizing, setIsResizing ] = useState( false ); - const [ isHovering, setIsHovering ] = useState( false ); + const [ shouldShowHandle, setShouldShowHandle ] = useState( false ); const [ isOversized, setIsOversized ] = useState( false ); const [ resizeRatio, setResizeRatio ] = useState( 1 ); const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); @@ -154,13 +155,43 @@ function ResizableFrame( { if ( remainingWidth > SNAP_TO_EDIT_CANVAS_MODE_THRESHOLD ) { // Reset the initial aspect ratio if the frame is resized slightly // above the sidebar but not far enough to trigger full screen. - setFrameSize( { width: '100%', height: '100%' } ); + setFrameSize( INITIAL_FRAME_SIZE ); } else { // Trigger full screen if the frame is resized far enough to the left. setCanvasMode( 'edit' ); } }; + // Handle resize by arrow keys + const handleResizableHandleKeyDown = ( event ) => { + if ( ! [ 'ArrowLeft', 'ArrowRight' ].includes( event.key ) ) { + return; + } + + const step = 20 * ( event.shiftKey ? 5 : 1 ); + const delta = step * ( event.key === 'ArrowLeft' ? 1 : -1 ); + const newWidth = Math.max( + FRAME_MIN_WIDTH, + frameRef.current.resizable.offsetWidth + delta + ); + + setFrameSize( { + width: newWidth, + height: calculateNewHeight( + newWidth, + initialAspectRatioRef.current + ), + } ); + + // If oversized, immediately switch to edit mode + if ( newWidth > initialComputedWidthRef.current ) { + setFrameSize( INITIAL_FRAME_SIZE ); + setCanvasMode( 'edit' ); + // TODO: Confirm if this is the focus behavior we want + document.querySelector( 'iframe[name=editor-canvas]' ).focus(); + } + }; + const frameAnimationVariants = { default: { flexGrow: 0, @@ -173,11 +204,15 @@ function ResizableFrame( { }; const resizeHandleVariants = { - default: { + hidden: { + opacity: 0, + left: 0, + }, + visible: { opacity: 1, left: -16, }, - resizing: { + active: { opacity: 1, left: -16, scaleY: 1.3, @@ -217,28 +252,26 @@ function ResizableFrame( { minWidth={ FRAME_MIN_WIDTH } maxWidth={ isFullWidth ? '100%' : '150%' } maxHeight={ '100%' } - onMouseOver={ () => setIsHovering( true ) } - onMouseOut={ () => setIsHovering( false ) } + onFocus={ () => setShouldShowHandle( true ) } + onBlur={ () => setShouldShowHandle( false ) } + onMouseOver={ () => setShouldShowHandle( true ) } + onMouseOut={ () => setShouldShowHandle( false ) } handleComponent={ { - left: - isHovering || isResizing ? ( - + - ) : null, + + ), } } onResizeStart={ handleResizeStart } onResize={ handleResize } diff --git a/packages/edit-site/src/components/resizable-frame/style.scss b/packages/edit-site/src/components/resizable-frame/style.scss index 9a959afe845e47..b8d0b2bad4b854 100644 --- a/packages/edit-site/src/components/resizable-frame/style.scss +++ b/packages/edit-site/src/components/resizable-frame/style.scss @@ -30,11 +30,13 @@ .edit-site-resizable-frame__handle { align-items: center; background-color: rgba($gray-700, 0.4); + border: 0; border-radius: $grid-unit-05; cursor: col-resize; display: flex; height: $grid-unit-80; justify-content: flex-end; + padding: 0; position: absolute; top: calc(50% - #{$grid-unit-40}); width: $grid-unit-05; @@ -57,15 +59,8 @@ } &:hover, + &:focus, .is-resizing & { background-color: var(--wp-admin-theme-color); } - - .edit-site-resizable-frame__handle-label { - background: var(--wp-admin-theme-color); - border-radius: 2px; - color: #fff; - margin-right: $grid-unit-10; - padding: 4px 8px; - } } From 89735717ced54542cd4d3148f0401a966b19c5e7 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Sat, 8 Jul 2023 10:38:19 +0900 Subject: [PATCH 02/14] Fix outline in Safari --- packages/edit-site/src/components/resizable-frame/style.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/edit-site/src/components/resizable-frame/style.scss b/packages/edit-site/src/components/resizable-frame/style.scss index b8d0b2bad4b854..250e7552025afb 100644 --- a/packages/edit-site/src/components/resizable-frame/style.scss +++ b/packages/edit-site/src/components/resizable-frame/style.scss @@ -58,6 +58,11 @@ width: $grid-unit-40; } + &:focus-visible { + // Works with Windows high contrast mode while also hiding weird outline in Safari. + outline: 2px solid transparent; + } + &:hover, &:focus, .is-resizing & { From 50cb20f13b1f7609bff70f72bd81b7850ac80cd5 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Sat, 8 Jul 2023 10:38:44 +0900 Subject: [PATCH 03/14] Use proper CSS modifier --- packages/edit-site/src/components/resizable-frame/index.js | 5 ++++- packages/edit-site/src/components/resizable-frame/style.scss | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 85411d24587fed..4a72794af6f66a 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -261,7 +261,10 @@ function ResizableFrame( { Date: Sat, 8 Jul 2023 10:39:03 +0900 Subject: [PATCH 04/14] Add aria-label to button --- packages/edit-site/src/components/resizable-frame/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 4a72794af6f66a..fb0916d82fddae 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -267,6 +267,7 @@ function ResizableFrame( { ) } variants={ resizeHandleVariants } animate={ shouldShowHandle ? 'visible' : 'hidden' } + aria-label={ __( 'Drag to resize' ) } onKeyDown={ handleResizableHandleKeyDown } initial="hidden" exit="hidden" From 74fccc8208c85b36a3c26d94bba92cc229e29bc3 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Sat, 8 Jul 2023 10:39:37 +0900 Subject: [PATCH 05/14] Keep handle enlarged when resizing (Safari) --- .../edit-site/src/components/resizable-frame/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index fb0916d82fddae..93fbc497ed6708 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -218,6 +218,12 @@ function ResizableFrame( { scaleY: 1.3, }, }; + const currentResizeHandleVariant = ( () => { + if ( isResizing ) { + return 'active'; + } + return shouldShowHandle ? 'visible' : 'hidden'; + } )(); return ( Date: Sat, 8 Jul 2023 23:24:41 +0900 Subject: [PATCH 06/14] Add back visually hidden help text --- .../src/components/resizable-frame/index.js | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 93fbc497ed6708..22567dbe967a7c 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -10,8 +10,10 @@ import { useState, useRef, useEffect } from '@wordpress/element'; import { ResizableBox, Tooltip, + VisuallyHidden, __unstableMotion as motion, } from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -95,6 +97,10 @@ function ResizableFrame( { const initialComputedWidthRef = useRef( null ); const FRAME_TRANSITION = { type: 'tween', duration: isResizing ? 0 : 0.5 }; const frameRef = useRef( null ); + const resizableHandleHelpId = useInstanceId( + ResizableFrame, + 'edit-site-resizable-frame-handle-help' + ); // Remember frame dimensions on initial render. useEffect( () => { @@ -264,23 +270,32 @@ function ResizableFrame( { onMouseOut={ () => setShouldShowHandle( false ) } handleComponent={ { left: ( - - + + + + + { __( + 'Use left and right arrow keys to resize the canvas.' ) } - variants={ resizeHandleVariants } - animate={ currentResizeHandleVariant } - aria-label={ __( 'Drag to resize' ) } - onKeyDown={ handleResizableHandleKeyDown } - initial="hidden" - exit="hidden" - whileFocus="active" - whileHover="active" - /> - + + ), } } onResizeStart={ handleResizeStart } From 641c68254b3fb6ecb80a3d45336a30647a8eb38e Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 11 Jul 2023 21:29:45 +0900 Subject: [PATCH 07/14] Don't switch to edit mode --- .../src/components/resizable-frame/index.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 22567dbe967a7c..cb5f5120ee1aae 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -176,9 +176,12 @@ function ResizableFrame( { const step = 20 * ( event.shiftKey ? 5 : 1 ); const delta = step * ( event.key === 'ArrowLeft' ? 1 : -1 ); - const newWidth = Math.max( - FRAME_MIN_WIDTH, - frameRef.current.resizable.offsetWidth + delta + const newWidth = Math.min( + Math.max( + FRAME_MIN_WIDTH, + frameRef.current.resizable.offsetWidth + delta + ), + initialComputedWidthRef.current ); setFrameSize( { @@ -188,14 +191,6 @@ function ResizableFrame( { initialAspectRatioRef.current ), } ); - - // If oversized, immediately switch to edit mode - if ( newWidth > initialComputedWidthRef.current ) { - setFrameSize( INITIAL_FRAME_SIZE ); - setCanvasMode( 'edit' ); - // TODO: Confirm if this is the focus behavior we want - document.querySelector( 'iframe[name=editor-canvas]' ).focus(); - } }; const frameAnimationVariants = { From d73d15ef8077af1375b29127d6656c89550ed9af Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 11 Jul 2023 23:21:36 +0900 Subject: [PATCH 08/14] Make the handle a role="separator" --- .../src/components/resizable-frame/index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index cb5f5120ee1aae..e66b08698437eb 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -267,9 +267,13 @@ function ResizableFrame( { left: ( <> - Date: Thu, 13 Jul 2023 00:54:43 +0900 Subject: [PATCH 09/14] Revert to `button` --- packages/edit-site/src/components/resizable-frame/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index e66b08698437eb..3ba461244b8b80 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -269,10 +269,9 @@ function ResizableFrame( { { /* Disable reason: role="separator" does in fact support aria-valuenow */ } { /* eslint-disable-next-line jsx-a11y/role-supports-aria-props */ } - Date: Thu, 13 Jul 2023 01:05:40 +0900 Subject: [PATCH 10/14] Switch description text to `div hidden` --- packages/edit-site/src/components/resizable-frame/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 3ba461244b8b80..053dfddee61bbb 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -10,7 +10,6 @@ import { useState, useRef, useEffect } from '@wordpress/element'; import { ResizableBox, Tooltip, - VisuallyHidden, __unstableMotion as motion, } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; @@ -296,11 +295,11 @@ function ResizableFrame( { whileHover="active" /> - + ), } } From f5b7a8fa8cb9a85f2542ea933d2f7b23190d4085 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 13 Jul 2023 01:09:05 +0900 Subject: [PATCH 11/14] Prevent keydown event default when right/left arrow --- packages/edit-site/src/components/resizable-frame/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 053dfddee61bbb..f910033655cd46 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -173,6 +173,8 @@ function ResizableFrame( { return; } + event.preventDefault(); + const step = 20 * ( event.shiftKey ? 5 : 1 ); const delta = step * ( event.key === 'ArrowLeft' ? 1 : -1 ); const newWidth = Math.min( From 5fa0e8d3f442be951ac1c7ceb1b90c708ae747f7 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 13 Jul 2023 01:16:13 +0900 Subject: [PATCH 12/14] Change minimum frame width to 320px --- packages/edit-site/src/components/resizable-frame/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index f910033655cd46..c8b8bb8eb7ccdb 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -36,7 +36,7 @@ const HANDLE_STYLES_OVERRIDE = { }; // The minimum width of the frame (in px) while resizing. -const FRAME_MIN_WIDTH = 340; +const FRAME_MIN_WIDTH = 320; // The reference width of the frame (in px) used to calculate the aspect ratio. const FRAME_REFERENCE_WIDTH = 1300; // 9 : 19.5 is the target aspect ratio enforced (when possible) while resizing. From f5bbecdf55ae190ba23d033efec2caa5cd0ea55c Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 13 Jul 2023 01:22:53 +0900 Subject: [PATCH 13/14] Mention shift key in description text --- packages/edit-site/src/components/resizable-frame/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index c8b8bb8eb7ccdb..56147de79807bb 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -299,7 +299,7 @@ function ResizableFrame( { From 5acbbfd2bff482c40de0f6a1f1c965a2eb6b1c5e Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 13 Jul 2023 05:45:21 +0900 Subject: [PATCH 14/14] Only render resize handle when in View mode --- .../edit-site/src/components/resizable-frame/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 56147de79807bb..1bb9315a07e38a 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -13,7 +13,7 @@ import { __unstableMotion as motion, } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; /** @@ -90,6 +90,10 @@ function ResizableFrame( { const [ shouldShowHandle, setShouldShowHandle ] = useState( false ); const [ isOversized, setIsOversized ] = useState( false ); const [ resizeRatio, setResizeRatio ] = useState( 1 ); + const canvasMode = useSelect( + ( select ) => unlock( select( editSiteStore ) ).getCanvasMode(), + [] + ); const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const initialAspectRatioRef = useRef( null ); // The width of the resizable frame on initial render. @@ -265,7 +269,7 @@ function ResizableFrame( { onMouseOver={ () => setShouldShowHandle( true ) } onMouseOut={ () => setShouldShowHandle( false ) } handleComponent={ { - left: ( + left: canvasMode === 'view' && ( <> { /* Disable reason: role="separator" does in fact support aria-valuenow */ }