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

Try adding a 'spotlight mode' type effect when template part or child is selected. #25656

Merged
merged 24 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
627dcf3
apply mode from blocklist + css
Addison-Stavlo Sep 25, 2020
6181fd4
added class to most active template part
Addison-Stavlo Sep 28, 2020
1f117bd
css rules to support nested/active template parts
Addison-Stavlo Sep 28, 2020
536a55b
gate template part check behind site editor experiment
Addison-Stavlo Sep 28, 2020
70c56e5
rebrand/refactor to support more entity blocks if necessary
Addison-Stavlo Sep 29, 2020
5bd9384
move logic to selector
Addison-Stavlo Oct 1, 2020
9a620d9
change selector name and add doc comments
Addison-Stavlo Oct 1, 2020
f30c4a1
update tests for updated selector
Addison-Stavlo Oct 1, 2020
1ef3695
add test for new selector
Addison-Stavlo Oct 1, 2020
05a921e
make test state reusable for updated parents tests
Addison-Stavlo Oct 1, 2020
27ea5d6
cleanup document-actions useSecondaryText to use new selector
Addison-Stavlo Oct 1, 2020
83f3c69
add dependency to selector for selectionEnd
Addison-Stavlo Oct 1, 2020
624b93b
remove unnecessary style selectors
Addison-Stavlo Oct 1, 2020
01a91f7
make selector experimental
Addison-Stavlo Oct 1, 2020
9f77800
refactor list of blocks to editor settings
Addison-Stavlo Oct 2, 2020
1646269
selector return early if no names are passed
Addison-Stavlo Oct 2, 2020
7a838f0
update css comment from template part to entity
Addison-Stavlo Oct 7, 2020
4c8e7c5
rename the settings list to include 'entity'
Addison-Stavlo Oct 7, 2020
13daf62
Merge branch 'master' into try/spotlight-mode-for-template-parts
Addison-Stavlo Oct 7, 2020
3a49591
Merge branch 'master' into try/spotlight-mode-for-template-parts
Addison-Stavlo Oct 7, 2020
7a1e61d
Merge branch 'master' into try/spotlight-mode-for-template-parts
Addison-Stavlo Oct 8, 2020
51ecd99
Merge branch 'master' into try/spotlight-mode-for-template-parts
Addison-Stavlo Oct 9, 2020
5c5dfb7
remove unused import
Addison-Stavlo Oct 9, 2020
9f83900
Merge branch 'master' into try/spotlight-mode-for-template-parts
Addison-Stavlo Oct 9, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,17 @@ _Returns_

<a name="getBlockParentsByBlockName" href="#getBlockParentsByBlockName">#</a> **getBlockParentsByBlockName**

Given a block client ID and a block name,
returns the list of all its parents from top to bottom,
filtered by the given name.
Given a block client ID and a block name, returns the list of all its parents
from top to bottom, filtered by the given name(s). For example, if passed
'core/group' as the blockName, it will only return parents which are group
blocks. If passed `[ 'core/group', 'core/cover']`, as the blockName, it will
return parents which are group blocks and parents which are cover blocks.

_Parameters_

- _state_ `Object`: Editor state.
- _clientId_ `string`: Block from which to find root client ID.
- _blockName_ `string`: Block name to filter.
- _blockName_ `(string|Array<string>)`: Block name(s) to filter.
- _ascending_ `boolean`: Order results from bottom to top (true) or top to bottom (false).

_Returns_
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ function BlockListBlock( {
toggleSelection,
index,
enableAnimation,
activeEntityBlockId,
} ) {
// In addition to withSelect, we should favor using useSelect in this
// component going forward to avoid leaking new props to the public API
Expand Down Expand Up @@ -166,6 +167,7 @@ function BlockListBlock( {
isFocusMode && ( isSelected || isAncestorOfSelectedBlock ),
'is-focus-mode': isFocusMode,
'has-child-selected': isAncestorOfSelectedBlock && ! isDragging,
'is-active-entity': activeEntityBlockId === clientId,
},
className
);
Expand Down
11 changes: 11 additions & 0 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,21 @@ function Items( {
const {
getBlockOrder,
getBlockListSettings,
getSettings,
getSelectedBlockClientId,
getMultiSelectedBlockClientIds,
hasMultiSelection,
getGlobalBlockCount,
isTyping,
isDraggingBlocks,
__experimentalGetActiveBlockIdByBlockNames,
} = select( 'core/block-editor' );

// Determine if there is an active entity area to spotlight.
const activeEntityBlockId = __experimentalGetActiveBlockIdByBlockNames(
getSettings().__experimentalSpotlightEntityBlocks
);

return {
blockClientIds: getBlockOrder( rootClientId ),
selectedBlockClientId: getSelectedBlockClientId(),
Expand All @@ -82,6 +89,7 @@ function Items( {
! isTyping() &&
getGlobalBlockCount() <= BLOCK_ANIMATION_THRESHOLD,
isDraggingBlocks: isDraggingBlocks(),
activeEntityBlockId,
};
}

Expand All @@ -93,6 +101,7 @@ function Items( {
hasMultiSelection,
enableAnimation,
isDraggingBlocks,
activeEntityBlockId,
} = useSelect( selector, [ rootClientId ] );

const dropTargetIndex = useBlockDropZone( {
Expand Down Expand Up @@ -131,7 +140,9 @@ function Items( {
'is-dropping-horizontally':
isDropTarget &&
orientation === 'horizontal',
'has-active-entity': activeEntityBlockId,
} ) }
activeEntityBlockId={ activeEntityBlockId }
/>
</AsyncModeProvider>
);
Expand Down
15 changes: 15 additions & 0 deletions packages/block-editor/src/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@
opacity: 1;
}
}

// Active entity spotlight.
&.has-active-entity:not(.is-focus-mode) {
opacity: 0.5;
transition: opacity 0.1s linear;
@include reduce-motion("transition");

&.is-active-entity,
&.has-child-selected,
&:not(.has-child-selected) .block-editor-block-list__block,
&.is-active-entity .block-editor-block-list__block,
.is-active-entity .block-editor-block-list__block {
opacity: 1;
}
}
}

.block-editor-block-list__layout .block-editor-block-list__block,
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/store/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export const SETTINGS_DEFAULTS = {
__mobileEnablePageTemplates: false,
__experimentalBlockPatterns: [],
__experimentalBlockPatternCategories: [],
__experimentalSpotlightEntityBlocks: [],

// gradients setting is not used anymore now defaults are passed from theme.json on the server and core has its own defaults.
// The setting is only kept for backward compatibility purposes.
Expand Down
69 changes: 61 additions & 8 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,14 +512,16 @@ export const getBlockParents = createSelector(
);

/**
* Given a block client ID and a block name,
* returns the list of all its parents from top to bottom,
* filtered by the given name.
* Given a block client ID and a block name, returns the list of all its parents
* from top to bottom, filtered by the given name(s). For example, if passed
* 'core/group' as the blockName, it will only return parents which are group
* blocks. If passed `[ 'core/group', 'core/cover']`, as the blockName, it will
* return parents which are group blocks and parents which are cover blocks.
*
* @param {Object} state Editor state.
* @param {string} clientId Block from which to find root client ID.
* @param {string} blockName Block name to filter.
* @param {boolean} ascending Order results from bottom to top (true) or top to bottom (false).
* @param {Object} state Editor state.
* @param {string} clientId Block from which to find root client ID.
* @param {string|string[]} blockName Block name(s) to filter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: should we update this to blockNames then? Reading blockName.includes threw me off at first.

Copy link
Contributor Author

@Addison-Stavlo Addison-Stavlo Oct 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Im not sure tbh. The original use is a string of a single blockName and this is still supported. Since it can either be a string or an array of strings, whether we use blockName or blockNames it will still read a bit funky somewhere. ( like name === blockNames ) 🤷‍♀️ At least the blockName.includes is nested in an if ( isArray( blockName )

* @param {boolean} ascending Order results from bottom to top (true) or top to bottom (false).
*
* @return {Array} ClientIDs of the parent blocks.
*/
Expand All @@ -532,7 +534,12 @@ export const getBlockParentsByBlockName = createSelector(
id,
name: getBlockName( state, id ),
} ) ),
{ name: blockName }
( { name } ) => {
if ( Array.isArray( blockName ) ) {
return blockName.includes( name );
}
return name === blockName;
}
),
( { id } ) => id
);
Expand Down Expand Up @@ -1791,3 +1798,49 @@ export function isBlockHighlighted( state, clientId ) {
export function areInnerBlocksControlled( state, clientId ) {
return !! state.blocks.controlledInnerBlocks[ clientId ];
}

/**
* Returns the clientId for the first 'active' block of a given array of block names.
* A block is 'active' if it (or a child) is the selected block.
* Returns the first match moving up the DOM from the selected block.
*
* @param {Object} state Global application state.
* @param {string[]} validBlocksNames The names of block types to check for.
*
* @return {string} The matching block's clientId.
*/
export const __experimentalGetActiveBlockIdByBlockNames = createSelector(
( state, validBlockNames ) => {
if ( ! validBlockNames.length ) {
return null;
}
// Check if selected block is a valid entity area.
const selectedBlockClientId = getSelectedBlockClientId( state );
if (
validBlockNames.includes(
getBlockName( state, selectedBlockClientId )
)
) {
return selectedBlockClientId;
}
// Check if first selected block is a child of a valid entity area.
const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds(
state
);
const entityAreaParents = getBlockParentsByBlockName(
state,
selectedBlockClientId || multiSelectedBlockClientIds[ 0 ],
validBlockNames
);
if ( entityAreaParents ) {
// Last parent closest/most interior.
return last( entityAreaParents );
}
return null;
},
( state, validBlockNames ) => [
state.selectionStart.clientId,
state.selectionEnd.clientId,
validBlockNames,
]
);
Loading