From f4e3c663012974a0c0765685af3ebc5ae39cf268 Mon Sep 17 00:00:00 2001 From: Jon Q Date: Wed, 10 Jun 2020 13:57:11 -0400 Subject: [PATCH 01/28] Try initial resizer visualizer tooltips --- package-lock.json | 1 + packages/components/package.json | 1 + .../components/src/resizable-box/index.js | 161 +++++++++++++++++- .../styles/resizable-box.styles.js | 0 4 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 packages/components/src/resizable-box/styles/resizable-box.styles.js diff --git a/package-lock.json b/package-lock.json index c64241f5885cb..f863cb0d5454f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10391,6 +10391,7 @@ "moment": "^2.22.1", "re-resizable": "^6.0.0", "react-dates": "^17.1.1", + "react-resize-aware": "^3.0.1", "react-spring": "^8.0.20", "react-use-gesture": "^7.0.15", "reakit": "^1.0.4", diff --git a/packages/components/package.json b/packages/components/package.json index 0c9e302bf1d78..ae89f9c844419 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -51,6 +51,7 @@ "moment": "^2.22.1", "re-resizable": "^6.0.0", "react-dates": "^17.1.1", + "react-resize-aware": "^3.0.1", "react-spring": "^8.0.20", "react-use-gesture": "^7.0.15", "reakit": "^1.0.4", diff --git a/packages/components/src/resizable-box/index.js b/packages/components/src/resizable-box/index.js index e9bc2a190f260..a358eb97b6f20 100644 --- a/packages/components/src/resizable-box/index.js +++ b/packages/components/src/resizable-box/index.js @@ -3,8 +3,21 @@ */ import classnames from 'classnames'; import { Resizable } from 're-resizable'; +import useResizeAware from 'react-resize-aware'; +import styled from '@emotion/styled'; -function ResizableBox( { className, showHandle = true, ...props } ) { +/** + * WordPress dependencies + */ +import { useEffect, useRef, useState } from '@wordpress/element'; + +function ResizableBox( { + children, + className, + showHandle = true, + showVisualizers = true, + ...props +} ) { // Removes the inline styles in the drag handles. const handleStylesOverrides = { width: null, @@ -83,8 +96,152 @@ function ResizableBox( { className, showHandle = true, ...props } ) { bottomLeft: handleStylesOverrides, } } { ...props } - /> + > + + { children } + + ); +} + +function Visualizer( { showVisualizers = false } ) { + const [ resizeListener, sizes ] = useResizeAware(); + const [ tooltipPosition, setTooltipPosition ] = useState( {} ); + const [ isRendered, setIsRendered ] = useState( false ); + const [ direction, setDirection ] = useState( '' ); + const [ isActive, setIsActive ] = useState( false ); + const { width, height } = sizes; + const tooltipRef = useRef(); + const widthRef = useRef( width ); + const heightRef = useRef( height ); + + const tooltipAnimationsActive = isActive; + + useEffect( () => { + if ( width !== null && height !== null && ! isRendered ) { + setIsRendered( true ); + heightRef.current = height; + widthRef.current = width; + } + }, [ width, height, isRendered ] ); + + useEffect( () => { + if ( ! isRendered ) return; + const didWidthChange = width !== widthRef.current; + const didHeightChange = height !== heightRef.current; + + let nextDirection = ''; + + if ( didWidthChange && didHeightChange ) { + nextDirection = 'both'; + } else if ( didWidthChange ) { + nextDirection = 'width'; + } else if ( didHeightChange ) { + nextDirection = 'height'; + } + + setDirection( nextDirection ); + }, [ width, height, isRendered ] ); + + useEffect( () => { + const unsetActiveState = () => { + setIsActive( false ); + }; + + const updateTooltipPosition = ( event ) => { + if ( ! isRendered ) return; + + if ( width !== widthRef.current || height !== heightRef.current ) { + setTooltipPosition( { x: event.clientX, y: event.clientY } ); + widthRef.current = width; + heightRef.current = height; + setIsActive( true ); + return; + } + + if ( event.which !== 1 ) { + setIsActive( false ); + } + }; + + window.addEventListener( 'mousemove', updateTooltipPosition ); + window.addEventListener( 'mousedown', unsetActiveState ); + window.addEventListener( 'mouseup', unsetActiveState ); + + return () => { + window.removeEventListener( 'mousemove', updateTooltipPosition ); + window.removeEventListener( 'mouseup', unsetActiveState ); + window.removeEventListener( 'mousedown', unsetActiveState ); + }; + }, [ width, height, isRendered ] ); + + if ( ! showVisualizers ) return null; + + const tooltipStyle = { + transform: `translate(${ + tooltipPosition.x - tooltipRef.current?.clientWidth / 2 + }px, ${ tooltipPosition.y - tooltipRef.current?.clientHeight - 12 }px)`, + }; + + let label; + if ( direction === 'both' ) { + label = `${ width } x ${ height }`; + } + if ( direction === 'width' ) { + label = `${ width }PX`; + } + if ( direction === 'height' ) { + label = `${ height }PX`; + } + + return ( + + { resizeListener } + + { label } + +
+
); } +const VisualizerView = styled.div` + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + z-index: 1000; +`; + +const VisualizerYLabelWrapperView = styled.div` + position: fixed; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + transition: opacity 120ms linear; + + ${ ( { isActive } ) => + isActive && + ` + opacity: 1; + ` } +`; + +const VisualizerLabelView = styled.div` + padding: 2px 4px; + background: rgba( 0, 0, 0, 0.4 ); + font-size: 13px; + color: white; + border-radius: 4px; +`; + export default ResizableBox; diff --git a/packages/components/src/resizable-box/styles/resizable-box.styles.js b/packages/components/src/resizable-box/styles/resizable-box.styles.js new file mode 100644 index 0000000000000..e69de29bb2d1d From c3e26278f008cf448c1f3b76964b2e2dce275ac3 Mon Sep 17 00:00:00 2001 From: Jon Q Date: Wed, 10 Jun 2020 16:12:42 -0400 Subject: [PATCH 02/28] Improve ResizableVisualizer implementation --- .../components/src/resizable-box/index.js | 161 +----------- .../src/resizable-visualizer/index.js | 59 +++++ .../src/resizable-visualizer/label.js | 87 +++++++ .../src/resizable-visualizer/stories/index.js | 78 ++++++ .../styles/resizable-visualizer.styles.js | 45 ++++ .../src/resizable-visualizer/utils.js | 229 ++++++++++++++++++ .../components/src/utils/colors-values.js | 2 + 7 files changed, 502 insertions(+), 159 deletions(-) create mode 100644 packages/components/src/resizable-visualizer/index.js create mode 100644 packages/components/src/resizable-visualizer/label.js create mode 100644 packages/components/src/resizable-visualizer/stories/index.js create mode 100644 packages/components/src/resizable-visualizer/styles/resizable-visualizer.styles.js create mode 100644 packages/components/src/resizable-visualizer/utils.js diff --git a/packages/components/src/resizable-box/index.js b/packages/components/src/resizable-box/index.js index a358eb97b6f20..e9bc2a190f260 100644 --- a/packages/components/src/resizable-box/index.js +++ b/packages/components/src/resizable-box/index.js @@ -3,21 +3,8 @@ */ import classnames from 'classnames'; import { Resizable } from 're-resizable'; -import useResizeAware from 'react-resize-aware'; -import styled from '@emotion/styled'; -/** - * WordPress dependencies - */ -import { useEffect, useRef, useState } from '@wordpress/element'; - -function ResizableBox( { - children, - className, - showHandle = true, - showVisualizers = true, - ...props -} ) { +function ResizableBox( { className, showHandle = true, ...props } ) { // Removes the inline styles in the drag handles. const handleStylesOverrides = { width: null, @@ -96,152 +83,8 @@ function ResizableBox( { bottomLeft: handleStylesOverrides, } } { ...props } - > - - { children } - - ); -} - -function Visualizer( { showVisualizers = false } ) { - const [ resizeListener, sizes ] = useResizeAware(); - const [ tooltipPosition, setTooltipPosition ] = useState( {} ); - const [ isRendered, setIsRendered ] = useState( false ); - const [ direction, setDirection ] = useState( '' ); - const [ isActive, setIsActive ] = useState( false ); - const { width, height } = sizes; - const tooltipRef = useRef(); - const widthRef = useRef( width ); - const heightRef = useRef( height ); - - const tooltipAnimationsActive = isActive; - - useEffect( () => { - if ( width !== null && height !== null && ! isRendered ) { - setIsRendered( true ); - heightRef.current = height; - widthRef.current = width; - } - }, [ width, height, isRendered ] ); - - useEffect( () => { - if ( ! isRendered ) return; - const didWidthChange = width !== widthRef.current; - const didHeightChange = height !== heightRef.current; - - let nextDirection = ''; - - if ( didWidthChange && didHeightChange ) { - nextDirection = 'both'; - } else if ( didWidthChange ) { - nextDirection = 'width'; - } else if ( didHeightChange ) { - nextDirection = 'height'; - } - - setDirection( nextDirection ); - }, [ width, height, isRendered ] ); - - useEffect( () => { - const unsetActiveState = () => { - setIsActive( false ); - }; - - const updateTooltipPosition = ( event ) => { - if ( ! isRendered ) return; - - if ( width !== widthRef.current || height !== heightRef.current ) { - setTooltipPosition( { x: event.clientX, y: event.clientY } ); - widthRef.current = width; - heightRef.current = height; - setIsActive( true ); - return; - } - - if ( event.which !== 1 ) { - setIsActive( false ); - } - }; - - window.addEventListener( 'mousemove', updateTooltipPosition ); - window.addEventListener( 'mousedown', unsetActiveState ); - window.addEventListener( 'mouseup', unsetActiveState ); - - return () => { - window.removeEventListener( 'mousemove', updateTooltipPosition ); - window.removeEventListener( 'mouseup', unsetActiveState ); - window.removeEventListener( 'mousedown', unsetActiveState ); - }; - }, [ width, height, isRendered ] ); - - if ( ! showVisualizers ) return null; - - const tooltipStyle = { - transform: `translate(${ - tooltipPosition.x - tooltipRef.current?.clientWidth / 2 - }px, ${ tooltipPosition.y - tooltipRef.current?.clientHeight - 12 }px)`, - }; - - let label; - if ( direction === 'both' ) { - label = `${ width } x ${ height }`; - } - if ( direction === 'width' ) { - label = `${ width }PX`; - } - if ( direction === 'height' ) { - label = `${ height }PX`; - } - - return ( - - { resizeListener } - - { label } - -
-
+ /> ); } -const VisualizerView = styled.div` - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - z-index: 1000; -`; - -const VisualizerYLabelWrapperView = styled.div` - position: fixed; - top: 0; - left: 0; - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - pointer-events: none; - transition: opacity 120ms linear; - - ${ ( { isActive } ) => - isActive && - ` - opacity: 1; - ` } -`; - -const VisualizerLabelView = styled.div` - padding: 2px 4px; - background: rgba( 0, 0, 0, 0.4 ); - font-size: 13px; - color: white; - border-radius: 4px; -`; - export default ResizableBox; diff --git a/packages/components/src/resizable-visualizer/index.js b/packages/components/src/resizable-visualizer/index.js new file mode 100644 index 0000000000000..308b52e71ccf0 --- /dev/null +++ b/packages/components/src/resizable-visualizer/index.js @@ -0,0 +1,59 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useCallback, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Label from './label'; +import { useResizeLabel, VARIANTS } from './utils'; +import { Root } from './styles/resizable-visualizer.styles'; + +function Visualizer( { + fadeTimeout = 180, + onMove = noop, + onResize = noop, + showPx = true, + variant = VARIANTS.cursor, + zIndex = 1000, +} ) { + const [ tooltipPosition, setTooltipPosition ] = useState( { x: 0, y: 0 } ); + + const handleOnMove = useCallback( + ( event ) => { + setTooltipPosition( { x: event.clientX, y: event.clientY } ); + onMove( event ); + }, + [ onMove, variant ] + ); + + const { isActive, label, resizeListener } = useResizeLabel( { + fadeTimeout, + onMove: handleOnMove, + onResize, + showPx, + variant, + } ); + + return ( + + ); +} + +export default Visualizer; diff --git a/packages/components/src/resizable-visualizer/label.js b/packages/components/src/resizable-visualizer/label.js new file mode 100644 index 0000000000000..cd1f82c070a90 --- /dev/null +++ b/packages/components/src/resizable-visualizer/label.js @@ -0,0 +1,87 @@ +/** + * External dependencies + */ +import { Portal } from 'reakit/Portal'; + +/** + * WordPress dependencies + */ +import { Fragment, useRef } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { VARIANTS } from './utils'; +import { TooltipWrapper, Tooltip } from './styles/resizable-visualizer.styles'; + +const CURSOR_OFFSET_TOP = 12; + +function Label( { + fadeTimeout = 180, + isActive = false, + label, + position = { x: 0, y: 0 }, + variant = VARIANTS.cursor, + zIndex = 1000, +} ) { + const tooltipRef = useRef(); + const tooltipWidth = tooltipRef.current?.clientWidth || 0; + const tooltipHeight = tooltipRef.current?.clientHeight || 0; + + const showLabel = isActive && label; + + const isCorner = variant === VARIANTS.corner; + const isCursor = variant === VARIANTS.cursor; + + const { x, y } = position; + + let style = { + transitionDelay: ! isActive ? `${ fadeTimeout }ms` : null, + zIndex, + }; + + if ( isCorner ) { + style = { + ...style, + position: 'absolute', + top: 4, + right: 4, + left: null, + }; + } + + if ( isCursor ) { + style = { + ...style, + position: 'fixed', + top: 0, + left: 0, + right: null, + transform: `translate(${ x - tooltipWidth / 2 }px, ${ + y - tooltipHeight - CURSOR_OFFSET_TOP + }px)`, + }; + } + + /** + * For "cursor" variants, we need to "Portal" the Tooltip to the document + * body root for more consistent style rendering. + * https://reakit.io/docs/portal/ + */ + const WrapperComponent = isCursor ? Portal : Fragment; + + return ( + + + + ); +} + +export default Label; diff --git a/packages/components/src/resizable-visualizer/stories/index.js b/packages/components/src/resizable-visualizer/stories/index.js new file mode 100644 index 0000000000000..da41f2578a48c --- /dev/null +++ b/packages/components/src/resizable-visualizer/stories/index.js @@ -0,0 +1,78 @@ +/** + * External dependencies + */ +import { select, text } from '@storybook/addon-knobs'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ResizableBox from '../../resizable-box'; +import ResizableVisualizer from '../'; + +export default { + title: 'Components/ResizableVisualizer', + component: ResizableVisualizer, +}; + +const Example = ( props ) => { + const [ attributes, setAttributes ] = useState( { + height: 200, + width: 400, + } ); + const { height, width } = attributes; + const { children, ...restProps } = props; + + return ( +
+ { + setAttributes( { + height: parseInt( height + delta.height, 10 ), + width: parseInt( width + delta.width, 10 ), + } ); + } } + > + { children } + +
+ ); +}; + +export const _default = () => { + const content = text( 'Example: Content', 'Resize' ); + const props = { + variant: select( + 'variant', + { cursor: 'cursor', corner: 'corner' }, + 'cursor' + ), + }; + + return ( + + +
+ { content } +
+
+ ); +}; diff --git a/packages/components/src/resizable-visualizer/styles/resizable-visualizer.styles.js b/packages/components/src/resizable-visualizer/styles/resizable-visualizer.styles.js new file mode 100644 index 0000000000000..a36d914b5ea7d --- /dev/null +++ b/packages/components/src/resizable-visualizer/styles/resizable-visualizer.styles.js @@ -0,0 +1,45 @@ +/** + * External dependencies + */ +import styled from '@emotion/styled'; + +/** + * Internal dependencies + */ +import { color } from '../../utils/style-mixins'; + +export const Root = styled.div` + bottom: 0; + box-sizing: border-box; + left: 0; + pointer-events: none; + position: absolute; + right: 0; + top: 0; +`; + +export const TooltipWrapper = styled.div` + align-items: center; + box-sizing: border-box; + display: inline-flex; + justify-content: center; + opacity: 0; + pointer-events: none; + transition: opacity 120ms linear; + + ${ ( { isActive } ) => + isActive && + ` + opacity: 1; +` } +`; + +export const Tooltip = styled.div` + background: ${ color( 'ui.border' ) }; + border-radius: 2px; + box-shadow: 0 0 0 1px rgba( 255, 255, 255, 0.2 ); + box-sizing: border-box; + color: ${ color( 'ui.textDark' ) }; + font-size: 13px; + padding: 2px 4px; +`; diff --git a/packages/components/src/resizable-visualizer/utils.js b/packages/components/src/resizable-visualizer/utils.js new file mode 100644 index 0000000000000..e780f9d6d1fb4 --- /dev/null +++ b/packages/components/src/resizable-visualizer/utils.js @@ -0,0 +1,229 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; +import useResizeAware from 'react-resize-aware'; + +/** + * WordPress dependencies + */ +import { useEffect, useRef, useState } from '@wordpress/element'; + +const { clearTimeout } = window; + +export const VARIANTS = { + cursor: 'cursor', + corner: 'corner', +}; + +/** + * @typedef {Object} UseResizeLabelProps + * + * @property {boolean} isActive The visibility of the label. + * @property {undefined|string} label The label value. + * @property {Function} resizeListener Element to be rendered for resize listening events. + */ + +/** + * Custom hook that manages resize listener events. It also provides a label + * based on current resize width x height values. + * + * @param {Object} props + * @param {number} props.fadeTimeout Duration (ms) before deactivating the resize label. + * @param {Function} props.onMove Callback when a resize occurs. Provides onMouseEvent event callback. + * @param {boolean} props.onResize Callback when a resize occurs. Provides { width, height } callback. + * @param {boolean} props.showPx Whether to add `PX` to the label. + * @param {string} props.variant Adjusts label value. + * + * @return {UseResizeLabelProps} Properties for hook. + */ +export function useResizeLabel( { + fadeTimeout = 200, + onMove = noop, + onResize = noop, + showPx = false, + variant = VARIANTS.cursor, +} ) { + const [ isActive, setIsActive ] = useState( false ); + /** + * The width/height values derive from this special useResizeAwere hook. + * This custom hook uses injects an iFrame into the element, allowing it + * to tap into the onResize (window) callback events. + */ + const [ resizeListener, sizes ] = useResizeAware(); + + /** + * The moveX and moveY values are used to track whether the label should + * display width, height, or width x height. + */ + const [ moveX, setMoveX ] = useState( false ); + const [ moveY, setMoveY ] = useState( false ); + + /** + * Cached dimension values to check for width/height updates from the + * sizes property from useResizeAware() + */ + const heightRef = useRef( height ); + const widthRef = useRef( width ); + + /** + * This timeout is used with setMoveX and setMoveY to determine of + * both width and height values have changed at (roughly) the same time. + */ + const moveTimeoutRef = useRef(); + + const { width, height } = sizes; + + /** + * On the initial render of useResizeAware, the height and width values are + * null. They are calculated then set using via an internal useEffect hook. + */ + const isRendered = width !== null && height !== null; + + useEffect( () => { + if ( width === null ) return; + + const didWidthChange = width !== widthRef.current; + const didHeightChange = height !== heightRef.current; + + if ( ! didWidthChange && ! didHeightChange ) return; + + if ( didWidthChange ) { + setMoveX( true ); + } + + if ( didHeightChange ) { + setMoveY( true ); + } + + onResize( { width, height } ); + }, [ width, height ] ); + + useEffect( () => { + if ( ! isRendered ) return; + + const handleOnMouseDown = () => { + if ( moveTimeoutRef.current ) { + clearTimeout( moveTimeoutRef.current ); + } + setMoveX( false ); + setMoveY( false ); + }; + + const handleOnMouseUp = () => { + setIsActive( false ); + + if ( moveTimeoutRef.current ) { + clearTimeout( moveTimeoutRef.current ); + } + + moveTimeoutRef.current = setTimeout( () => { + setMoveX( false ); + setMoveY( false ); + }, fadeTimeout * 2 ); + }; + + const handleOnMouseMove = ( event ) => { + /** + * Handles edge cases where a movement event occurs when the primary + * mouse button is let go during a move event. + */ + if ( event.which !== 1 && isActive ) { + handleOnMouseUp(); + return; + } + + if ( width !== widthRef.current || height !== heightRef.current ) { + widthRef.current = width; + heightRef.current = height; + + onMove( event ); + setIsActive( true ); + } + }; + + window.addEventListener( 'mousedown', handleOnMouseDown ); + window.addEventListener( 'mousemove', handleOnMouseMove ); + window.addEventListener( 'mouseup', handleOnMouseUp ); + + return () => { + window.removeEventListener( 'mousedown', handleOnMouseDown ); + window.removeEventListener( 'mousemove', handleOnMouseMove ); + window.removeEventListener( 'mouseup', handleOnMouseUp ); + }; + }, [ width, height, isActive, isRendered ] ); + + const label = getSizeLabel( { + moveX, + moveY, + width, + height, + showPx, + variant, + } ); + + return { + isActive, + label, + resizeListener, + }; +} + +/** + * Gets the resize label based on width and height values (as well as recent changes). + * + * @param {Object} props + * @param {number} props.height Height value. + * @param {boolean} props.moveX Recent width (x axis) changes. + * @param {boolean} props.moveY Recent width (y axis) changes. + * @param {boolean} props.showPx Whether to add `PX` to the label. + * @param {string} props.variant Adjusts label value. + * @param {number} props.width Width value. + * + * @return {undefined | string} The rendered label. + */ +function getSizeLabel( { + height, + moveX = false, + moveY = false, + showPx = false, + variant = VARIANTS.cursor, + width, +} ) { + let label; + + /** + * Corner variant... + * We want the label to appear like width x height. + */ + if ( variant === VARIANTS.corner ) { + return `${ width } x ${ height }`; + } + + /** + * Other variants... + * The label will combine both width x height values if both + * values have recently been changed. + * + * Otherwise, only width or height will be displayed. + * The `PX` unit will be added, if specified by the `showPx` prop. + */ + if ( moveX && moveY ) { + // Width x Height changes... + label = `${ width } x ${ height }`; + } else if ( moveY ) { + // Height changes... + label = `${ height }`; + if ( showPx ) { + label = `${ label } PX`; + } + } else if ( moveX ) { + // Width changes... + label = `${ width }`; + if ( showPx ) { + label = `${ label } PX`; + } + } + + return label; +} diff --git a/packages/components/src/utils/colors-values.js b/packages/components/src/utils/colors-values.js index f765d119bbfe0..ab20b2c89d2d0 100644 --- a/packages/components/src/utils/colors-values.js +++ b/packages/components/src/utils/colors-values.js @@ -144,6 +144,8 @@ export const UI = { borderLight: LIGHT_GRAY[ 600 ], label: DARK_GRAY[ 500 ], textDisabled: DARK_GRAY[ 150 ], + textDark: BASE.white, + textLight: BASE.black, }; export const COLORS = { From 1fdb5313b11a277904073f56e7410eb4129954eb Mon Sep 17 00:00:00 2001 From: Jon Q Date: Wed, 10 Jun 2020 16:30:16 -0400 Subject: [PATCH 03/28] Add README.md file --- .../src/resizable-visualizer/README.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 packages/components/src/resizable-visualizer/README.md diff --git a/packages/components/src/resizable-visualizer/README.md b/packages/components/src/resizable-visualizer/README.md new file mode 100644 index 0000000000000..680810182354f --- /dev/null +++ b/packages/components/src/resizable-visualizer/README.md @@ -0,0 +1,93 @@ +# ResizableVisualizer + +ResizableVisualizer displays the dimensions of an element whenever the width or height of the element changes. + +## Usage + +```jsx +import { ResizableVisualizer } from '@wordpress/components'; + +const Example = () => { + return ( +
+ + ... +
+ ); +}; +``` + +Be sure that the parent element containing `` has the `position` style property defined. This is important as `` uses `position` based techniques to determine size changes. + +### Variants + +`` has two style variants; + +- `cursor` +- `corner` + +##### `cursor` + +The `cursor` variant (default) renders the dimensions label right above the mouse cursor. + +##### `corner` + +The `corner` variant renders the dimensions label in the top-right corner of the (parent) element. + + fadeTimeout = 180, + labelRef, + +## Props + +### fadeTimeout + +Duration (in `ms`) before the label disappears after resize event. + +- Type: `Number` +- Required: No +- Default: `180` + +### labelRef + +Callback [Ref](https://reactjs.org/docs/forwarding-refs.html) for the label element. + +- Type: `Function` +- Required: No + +### onMove + +Callback function when the (observed) element resizes, specifically with a `mousemove` based event. + +- Type: `Function` +- Required: No + +### onResize + +Callback function when the (observed) element resizes. + +- Type: `Function` +- Required: No + +### showPx + +Renders a `PX` unit suffix after the width or height value in the label. + +- Type: `Boolean` +- Required: No +- Default: `true` + +### variant + +The style variant for the label. + +- Type: `String` +- Required: No +- Default: `cursor` + +### zIndex + +The `z-index` style property for the label. + +- Type: `Number` +- Required: No +- Default: `1000` From 5b1d2136cbe949b07c59e9e458b22ae0daedfc04 Mon Sep 17 00:00:00 2001 From: Jon Q Date: Wed, 10 Jun 2020 16:30:45 -0400 Subject: [PATCH 04/28] Clean up. Add default values. Forward refs. Add classNames. --- .../src/resizable-visualizer/index.js | 34 +++++++++++++------ .../src/resizable-visualizer/label.js | 33 ++++++++++++------ .../src/resizable-visualizer/stories/index.js | 7 ++-- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/components/src/resizable-visualizer/index.js b/packages/components/src/resizable-visualizer/index.js index 308b52e71ccf0..ab6c974ae6c37 100644 --- a/packages/components/src/resizable-visualizer/index.js +++ b/packages/components/src/resizable-visualizer/index.js @@ -2,11 +2,12 @@ * External dependencies */ import { noop } from 'lodash'; +import classnames from 'classnames'; /** * WordPress dependencies */ -import { useCallback, useState } from '@wordpress/element'; +import { forwardRef, useCallback, useState } from '@wordpress/element'; /** * Internal dependencies @@ -15,14 +16,20 @@ import Label from './label'; import { useResizeLabel, VARIANTS } from './utils'; import { Root } from './styles/resizable-visualizer.styles'; -function Visualizer( { - fadeTimeout = 180, - onMove = noop, - onResize = noop, - showPx = true, - variant = VARIANTS.cursor, - zIndex = 1000, -} ) { +function Visualizer( + { + className, + fadeTimeout = 180, + labelRef, + onMove = noop, + onResize = noop, + showPx = true, + variant = VARIANTS.cursor, + zIndex = 1000, + ...props + }, + ref +) { const [ tooltipPosition, setTooltipPosition ] = useState( { x: 0, y: 0 } ); const handleOnMove = useCallback( @@ -41,13 +48,16 @@ function Visualizer( { variant, } ); + const classes = classnames( 'components-resizable-visualizer', className ); + return ( -