From 7323e484f6c6a499ff57f14f323c8fa3f5934529 Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Fri, 24 Mar 2023 12:26:41 +1100
Subject: [PATCH] Sticky Position: Try re-enabling non-root sticky position,
and add a visualizer to define sticky area
---
packages/block-editor/src/hooks/position.js | 125 ++++++++++++++++--
packages/block-editor/src/hooks/position.scss | 19 +++
2 files changed, 133 insertions(+), 11 deletions(-)
diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js
index 2c5589c1920bbd..3f5e44cf514918 100644
--- a/packages/block-editor/src/hooks/position.js
+++ b/packages/block-editor/src/hooks/position.js
@@ -13,21 +13,26 @@ import {
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose';
-import { useSelect } from '@wordpress/data';
+import { useDispatch, useSelect } from '@wordpress/data';
import {
useContext,
+ useEffect,
useMemo,
+ useRef,
+ useState,
createPortal,
Platform,
} from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
-
+import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
*/
import BlockList from '../components/block-list';
+import BlockPopover from '../components/block-popover';
import useSetting from '../components/use-setting';
import InspectorControls from '../components/inspector-controls';
+import useBlockDisplayInformation from '../components/use-block-display-information';
import { cleanEmptyObject } from './utils';
import { unlock } from '../lock-unlock';
import { store as blockEditorStore } from '../store';
@@ -203,6 +208,71 @@ export function useIsPositionDisabled( { name: blockName } = {} ) {
return ! hasPositionSupport( blockName ) || isDisabled;
}
+function useVisualizer() {
+ const [ property, setProperty ] = useState( false );
+ const { hideBlockInterface, showBlockInterface } = unlock(
+ useDispatch( blockEditorStore )
+ );
+ useEffect( () => {
+ if ( ! property ) {
+ showBlockInterface();
+ } else {
+ hideBlockInterface();
+ }
+ }, [ property, showBlockInterface, hideBlockInterface ] );
+
+ return [ property, setProperty ];
+}
+
+export function PositionVisualizer( { clientId, attributes, forceShow } ) {
+ const positionType = attributes?.style?.position?.type;
+
+ const [ isActive, setIsActive ] = useState( false );
+ const valueRef = useRef( positionType );
+ const timeoutRef = useRef();
+
+ const clearTimer = () => {
+ if ( timeoutRef.current ) {
+ window.clearTimeout( timeoutRef.current );
+ }
+ };
+
+ useEffect( () => {
+ if (
+ ! isShallowEqual( positionType, valueRef.current ) &&
+ ! forceShow
+ ) {
+ setIsActive( true );
+ valueRef.current = positionType;
+
+ timeoutRef.current = setTimeout( () => {
+ setIsActive( false );
+ }, 400 );
+ }
+
+ return () => {
+ setIsActive( false );
+ clearTimer();
+ };
+ }, [ positionType, forceShow ] );
+
+ if ( ! isActive && ! forceShow ) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
+
/*
* Position controls rendered in an inspector control panel.
*
@@ -222,32 +292,39 @@ export function PositionPanel( props ) {
const allowSticky = hasStickyPositionSupport( blockName );
const value = style?.position?.type;
- const { hasParents } = useSelect(
+ const { firstParentClientId } = useSelect(
( select ) => {
const { getBlockParents } = select( blockEditorStore );
const parents = getBlockParents( clientId );
- return {
- hasParents: parents.length,
- };
+ return { firstParentClientId: parents[ parents.length - 1 ] };
},
[ clientId ]
);
+ const blockInformation = useBlockDisplayInformation( firstParentClientId );
+ const stickyHelpText =
+ allowSticky && value === 'sticky' && blockInformation
+ ? sprintf(
+ /* translators: %s: the name of the parent block. */
+ __(
+ 'The block will stick to the scrollable area of the parent %s block.'
+ ),
+ blockInformation.title
+ )
+ : null;
+
const options = useMemo( () => {
const availableOptions = [ DEFAULT_OPTION ];
// Only display sticky option if the block has no parents (is at the root of the document),
// or if the block already has a sticky position value set.
- if (
- ( allowSticky && ! hasParents ) ||
- value === STICKY_OPTION.value
- ) {
+ if ( allowSticky || value === STICKY_OPTION.value ) {
availableOptions.push( STICKY_OPTION );
}
if ( allowFixed || value === FIXED_OPTION.value ) {
availableOptions.push( FIXED_OPTION );
}
return availableOptions;
- }, [ allowFixed, allowSticky, hasParents, value ] );
+ }, [ allowFixed, allowSticky, value ] );
const onChangeType = ( next ) => {
// For now, use a hard-coded `0px` value for the position.
@@ -272,6 +349,17 @@ export function PositionPanel( props ) {
} );
};
+ const [ isPositionVisualizerActive, setIsPositionVisualizerActive ] =
+ useVisualizer();
+
+ const onMouseOverPosition = () => {
+ setIsPositionVisualizerActive( true );
+ };
+
+ const onMouseLeaveControls = () => {
+ setIsPositionVisualizerActive( false );
+ };
+
const selectedOption = value
? options.find( ( option ) => option.value === value ) || DEFAULT_OPTION
: DEFAULT_OPTION;
@@ -281,6 +369,13 @@ export function PositionPanel( props ) {
web:
options.length > 1 ? (
+ { firstParentClientId && value === 'sticky' ? (
+
+ ) : null }
{
onChangeType( selectedItem.value );
} }
+ onFocus={ onMouseOverPosition }
+ onBlur={ onMouseLeaveControls }
+ onMouseOver={ onMouseOverPosition }
size={ '__unstable-large' }
/>
+ { stickyHelpText && (
+
+ { stickyHelpText }
+
+ ) }
) : null,
native: null,
diff --git a/packages/block-editor/src/hooks/position.scss b/packages/block-editor/src/hooks/position.scss
index b3bd6b1b9ef041..fe6be4a14fb77b 100644
--- a/packages/block-editor/src/hooks/position.scss
+++ b/packages/block-editor/src/hooks/position.scss
@@ -16,3 +16,22 @@
text-align: left;
}
}
+
+.block-editor__sticky-position-visualizer {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ opacity: 0.5;
+ border-color: var(--wp-admin-theme-color);
+ border-style: solid;
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.block-editor-hooks__position-helptext {
+ color: $gray-700;
+ font-size: $helptext-font-size;
+ margin-bottom: $grid-unit-20;
+}