Skip to content
Draft
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
353 changes: 278 additions & 75 deletions lib/block-supports/states.php

Large diffs are not rendered by default.

82 changes: 70 additions & 12 deletions packages/block-editor/src/components/block-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
hasBlockSupport,
store as blocksStore,
} from '@wordpress/blocks';
import { __unstableMotion as motion } from '@wordpress/components';
import {
ToggleControl,
__experimentalSpacer as Spacer,
__unstableMotion as motion,
} from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { useRef } from '@wordpress/element';

Expand All @@ -33,10 +37,12 @@ import AdvancedControls from '../inspector-controls-tabs/advanced-controls-panel
import PositionControls from '../inspector-controls-tabs/position-controls-panel';
import useBlockInspectorAnimationSettings from './useBlockInspectorAnimationSettings';
import { useBorderPanelLabel } from '../../hooks/border';
import { BlockStatesControl } from '../../hooks/states';
import { BlockStateBadges, BlockStatesControl } from '../../hooks/states';
import ContentTab from '../inspector-controls-tabs/content-tab';
import ViewportVisibilityInfo from '../block-visibility/viewport-visibility-info';
import { unlock } from '../../lock-unlock';
import { isDefaultBlockStyleState } from '../../hooks/block-style-state';
import { onViewportStateChangeKey } from '../../store/private-keys';

function StyleInspectorSlots( {
blockName,
Expand Down Expand Up @@ -121,6 +127,8 @@ function BlockInspector() {
editedContentOnlySection,
blockEditingMode,
selectedBlockStyleState,
showStateOnCanvas,
onViewportStateChange,
} = useSelect( ( select ) => {
const {
getSelectedBlockClientId,
Expand All @@ -133,7 +141,9 @@ function BlockInspector() {
isWithinEditedContentOnlySection,
getBlockEditingMode,
getSelectedBlockStyleState,
isSelectedBlockStyleStateShownOnCanvas,
} = unlock( select( blockEditorStore ) );
const blockEditorSettings = select( blockEditorStore ).getSettings();
const { getBlockStyles } = select( blocksStore );
const _selectedBlockClientId = getSelectedBlockClientId();
const isWithinEditedSection = isWithinEditedContentOnlySection(
Expand Down Expand Up @@ -168,6 +178,11 @@ function BlockInspector() {
selectedBlockStyleState: getSelectedBlockStyleState(
_renderedBlockClientId
),
showStateOnCanvas: isSelectedBlockStyleStateShownOnCanvas(
_renderedBlockClientId
),
onViewportStateChange:
blockEditorSettings?.[ onViewportStateChangeKey ],
};
}, [] );

Expand Down Expand Up @@ -236,7 +251,9 @@ function BlockInspector() {
useBlockInspectorAnimationSettings( blockType );

const hasSelectedBlocks = selectedBlockCount > 1;
const isBlockStyleStateSelected = selectedBlockStyleState !== 'default';
const isBlockStyleStateSelected = ! isDefaultBlockStyleState(
selectedBlockStyleState
);

if ( hasSelectedBlocks && ! isSectionBlockInSelection ) {
return (
Expand Down Expand Up @@ -306,6 +323,8 @@ function BlockInspector() {
editedContentOnlySection={ editedContentOnlySection }
blockEditingMode={ blockEditingMode }
selectedBlockStyleState={ selectedBlockStyleState }
showStateOnCanvas={ showStateOnCanvas }
onViewportStateChange={ onViewportStateChange }
isBlockStyleStateSelected={ isBlockStyleStateSelected }
/>
</BlockInspectorSingleBlockWrapper>
Expand Down Expand Up @@ -360,6 +379,8 @@ const BlockInspectorSingleBlock = ( {
editedContentOnlySection,
blockEditingMode,
selectedBlockStyleState,
showStateOnCanvas,
onViewportStateChange,
isBlockStyleStateSelected,
} ) => {
const listViewRef = useRef( null );
Expand All @@ -374,9 +395,38 @@ const BlockInspectorSingleBlock = ( {
renderedBlockClientId
);
const isBlockSynced = blockInformation.isSynced;
const { setSelectedBlockStyleState } = unlock(
useDispatch( blockEditorStore )
);
const {
setSelectedBlockStyleState,
setSelectedBlockStyleStateCanvasPreview,
} = unlock( useDispatch( blockEditorStore ) );
const onBlockStyleStateChange = ( value ) => {
const nextSelectedBlockStyleState = {
...selectedBlockStyleState,
...value,
};

setSelectedBlockStyleState(
renderedBlockClientId,
nextSelectedBlockStyleState
);

if ( value.viewport ) {
onViewportStateChange?.( {
viewport: nextSelectedBlockStyleState.viewport,
showStateOnCanvas,
} );
}
};
const onShowStateOnCanvasChange = ( value ) => {
setSelectedBlockStyleStateCanvasPreview( renderedBlockClientId, value );

if ( value ) {
onViewportStateChange?.( {
viewport: selectedBlockStyleState.viewport,
showStateOnCanvas: value,
} );
}
};

return (
<div className="block-editor-block-inspector">
Expand All @@ -400,16 +450,24 @@ const BlockInspectorSingleBlock = ( {
<BlockStatesControl
name={ blockName }
value={ selectedBlockStyleState }
onChange={ ( value ) =>
setSelectedBlockStyleState(
renderedBlockClientId,
value
)
}
onChange={ onBlockStyleStateChange }
/>
)
}
/>
{ blockEditingMode === 'default' && isBlockStyleStateSelected && (
<Spacer paddingX={ 4 } paddingY={ 2 }>
<ToggleControl
label={ __( 'Show state on canvas' ) }
checked={ showStateOnCanvas }
onChange={ onShowStateOnCanvasChange }
/>
<BlockStateBadges
name={ blockName }
value={ selectedBlockStyleState }
/>
</Spacer>
) }
<ViewportVisibilityInfo clientId={ renderedBlockClientId } />
<EditContents clientId={ renderedBlockClientId } />
<BlockVariationTransforms blockClientId={ renderedBlockClientId } />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ import ChildLayoutControl from '../child-layout-control';
import AspectRatioTool from '../dimensions-tool/aspect-ratio-tool';
import { cleanEmptyObject } from '../../hooks/utils';
import { setImmutably } from '../../utils/object';
import {
DEFAULT_BLOCK_STYLE_STATE,
isDefaultBlockStyleState,
} from '../../hooks/block-style-state';

const AXIAL_SIDES = [ 'horizontal', 'vertical' ];

export function useHasDimensionsPanel( settings, styleState = 'default' ) {
export function useHasDimensionsPanel(
settings,
styleState = DEFAULT_BLOCK_STYLE_STATE
) {
return (
Platform.OS === 'web' &&
( hasContentSize( settings ) ||
Expand Down Expand Up @@ -89,8 +96,8 @@ function hasAspectRatio( settings ) {
return settings?.dimensions?.aspectRatio;
}

function hasChildLayout( settings, styleState = 'default' ) {
if ( styleState !== 'default' ) {
function hasChildLayout( settings, styleState = DEFAULT_BLOCK_STYLE_STATE ) {
if ( ! isDefaultBlockStyleState( styleState ) ) {
return false;
}

Expand Down Expand Up @@ -235,7 +242,7 @@ export default function DimensionsPanel( {
// Special case because the layout controls are not part of the dimensions panel
// in global styles but not in block inspector.
includeLayoutControls = false,
styleState = 'default',
styleState = DEFAULT_BLOCK_STYLE_STATE,
} ) {
const { dimensions, spacing } = settings;

Expand Down
5 changes: 3 additions & 2 deletions packages/block-editor/src/hooks/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { globalStylesDataKey } from '../store/private-keys';
import {
getStyleForState,
isDefaultBlockStyleState,
setStyleForState,
useBlockStyleState,
} from './block-style-state';
Expand Down Expand Up @@ -193,8 +194,6 @@ export function BackgroundImagePanel( {
[ clientId, name ]
);

const isStateSelected = selectedState !== 'default';

const backgroundGradientSupported = hasBackgroundSupport(
name,
'gradient'
Expand All @@ -221,6 +220,8 @@ export function BackgroundImagePanel( {
return null;
}

const isStateSelected = ! isDefaultBlockStyleState( selectedState );

const onChange = isStateSelected
? ( newStyle ) => {
setAttributes( {
Expand Down
93 changes: 75 additions & 18 deletions packages/block-editor/src/hooks/block-style-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import { createContext, useContext } from '@wordpress/element';
* Internal dependencies
*/
import { cleanEmptyObject } from './utils';
import { getValueFromObjectPath, setImmutably } from '../utils/object';

export const DEFAULT_BLOCK_STYLE_STATE = 'default';
const DEFAULT_STATE_VALUE = 'default';

export const DEFAULT_BLOCK_STYLE_STATE = {
viewport: DEFAULT_STATE_VALUE,
pseudo: DEFAULT_STATE_VALUE,
};

const BlockStyleStateContext = createContext( DEFAULT_BLOCK_STYLE_STATE );

Expand All @@ -18,34 +24,84 @@ export function useBlockStyleState() {
return useContext( BlockStyleStateContext );
}

/**
* Returns true when a viewport style state is selected.
*
* @param {Object} selectedState Selected block style state.
* @return {boolean} Whether a viewport state is selected.
*/
export function hasViewportBlockStyleState( selectedState ) {
return (
!! selectedState?.viewport &&
selectedState.viewport !== DEFAULT_STATE_VALUE
);
}

/**
* Returns true when a pseudo style state is selected.
*
* @param {Object} selectedState Selected block style state.
* @return {boolean} Whether a pseudo state is selected.
*/
export function hasPseudoBlockStyleState( selectedState ) {
return (
!! selectedState?.pseudo && selectedState.pseudo !== DEFAULT_STATE_VALUE
);
}

/**
* Returns true when the default style state is selected.
*
* @param {Object} selectedState Selected block style state.
* @return {boolean} Whether the default style state is selected.
*/
export function isDefaultBlockStyleState( selectedState ) {
return (
! hasViewportBlockStyleState( selectedState ) &&
! hasPseudoBlockStyleState( selectedState )
);
}

/**
* Returns the style object path for the selected block style state.
*
* @param {Object} selectedState Selected block style state.
* @return {string[]} Object path for the selected state styles.
*/
function getStyleStatePath( selectedState ) {
if ( isDefaultBlockStyleState( selectedState ) ) {
return [];
}

return [ selectedState.viewport, selectedState.pseudo ].filter(
( state ) => state && state !== DEFAULT_STATE_VALUE
);
}

export function getStyleForState( style, selectedState ) {
if ( ! selectedState || selectedState === DEFAULT_BLOCK_STYLE_STATE ) {
const path = getStyleStatePath( selectedState );
if ( ! path.length ) {
return style;
}
return style?.[ selectedState ];
return getValueFromObjectPath( style, path );
}

export function setStyleForState( style, selectedState, newStyle ) {
if ( ! selectedState || selectedState === DEFAULT_BLOCK_STYLE_STATE ) {
const path = getStyleStatePath( selectedState );
if ( ! path.length ) {
return cleanEmptyObject( newStyle );
}
return cleanEmptyObject( {
...style,
[ selectedState ]: newStyle,
} );
return cleanEmptyObject( setImmutably( style, path, newStyle ) );
}

export function scopeResetAllFilterToState( selectedState, resetAllFilter ) {
if (
! resetAllFilter ||
! selectedState ||
selectedState === DEFAULT_BLOCK_STYLE_STATE
) {
if ( ! resetAllFilter || isDefaultBlockStyleState( selectedState ) ) {
return resetAllFilter;
}

return ( attributes ) => {
const existingStateStyle = attributes?.style?.[ selectedState ] || {};
const existingStateStyle =
getStyleForState( attributes?.style, selectedState ) || {};
const updatedStateAttributes = resetAllFilter( {
style: existingStateStyle,
} );
Expand All @@ -61,10 +117,11 @@ export function scopeResetAllFilterToState( selectedState, resetAllFilter ) {
: updatedStateAttributes;

return {
style: cleanEmptyObject( {
...attributes?.style,
[ selectedState ]: updatedStateStyle,
} ),
style: setStyleForState(
attributes?.style,
selectedState,
updatedStateStyle
),
};
};
}
3 changes: 2 additions & 1 deletion packages/block-editor/src/hooks/border.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
import { store as blockEditorStore } from '../store';
import {
getStyleForState,
isDefaultBlockStyleState,
setStyleForState,
useBlockStyleState,
} from './block-style-state';
Expand Down Expand Up @@ -161,7 +162,7 @@ export function BorderPanel( { clientId, name, setAttributes, settings } ) {
[ clientId, isEnabled ]
);

const isStateSelected = selectedState !== 'default';
const isStateSelected = ! isDefaultBlockStyleState( selectedState );

const value = useMemo( () => {
if ( isStateSelected ) {
Expand Down
3 changes: 2 additions & 1 deletion packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import BlockColorContrastChecker from './contrast-checker';
import { store as blockEditorStore } from '../store';
import {
getStyleForState,
isDefaultBlockStyleState,
setStyleForState,
useBlockStyleState,
} from './block-style-state';
Expand Down Expand Up @@ -294,7 +295,7 @@ export function ColorEdit( {
[ clientId, isEnabled ]
);

const isStateSelected = selectedState !== 'default';
const isStateSelected = ! isDefaultBlockStyleState( selectedState );

const value = useMemo( () => {
if ( isStateSelected ) {
Expand Down
Loading
Loading