Skip to content

Commit

Permalink
Editor: Implement EntityProvider and use it to refactor custom sour…
Browse files Browse the repository at this point in the history
…ces with a backwards compatibility hook for meta sources.
  • Loading branch information
epiqueras committed Aug 22, 2019
1 parent be1c8ab commit 9988855
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 302 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
"@wordpress/compose": "file:../compose",
"@wordpress/core-data": "file:../core-data",
"@wordpress/data": "file:../data",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
Expand Down
61 changes: 60 additions & 1 deletion packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ import { animated } from 'react-spring/web.cjs';
/**
* WordPress dependencies
*/
import { useRef, useEffect, useLayoutEffect, useState } from '@wordpress/element';
import { useEntityProp } from '@wordpress/core-data';
import {
useMemo,
useCallback,
useRef,
useEffect,
useLayoutEffect,
useState,
} from '@wordpress/element';
import {
focus,
isTextField,
Expand Down Expand Up @@ -53,6 +61,51 @@ import useHoveredArea from './hover-area';
import { isInsideRootBlock } from '../../utils/dom';
import useMovingAnimation from './moving-animation';

const EMPTY_OBJECT = {};
function useMetaAttributeSourceBackwardsCompatibility(
name,
_attributes,
_setAttributes
) {
const { attributes: attributeTypes = EMPTY_OBJECT } =
getBlockType( name ) || EMPTY_OBJECT;
let [ attributes, setAttributes ] = [ _attributes, _setAttributes ];

if ( Object.values( attributeTypes ).some( ( type ) => type.source === 'meta' ) ) {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [ meta, setMeta ] = useEntityProp( 'postType', 'post', 'meta' );

// eslint-disable-next-line react-hooks/rules-of-hooks
attributes = useMemo(
() => ( {
..._attributes,
...Object.keys( attributeTypes ).reduce( ( acc, key ) => {
if ( attributeTypes[ key ].source === 'meta' ) {
acc[ key ] = meta[ attributeTypes[ key ].meta ];
}
return acc;
}, {} ),
} ),
[ attributeTypes, meta, _attributes ]
);

// eslint-disable-next-line react-hooks/rules-of-hooks
setAttributes = useCallback(
( ...args ) => {
Object.keys( args[ 0 ] ).forEach( ( key ) => {
if ( attributeTypes[ key ].source === 'meta' ) {
setMeta( { [ attributeTypes[ key ].meta ]: args[ 0 ][ key ] } );
}
} );
return _setAttributes( ...args );
},
[ attributeTypes, setMeta, _setAttributes ]
);
}

return [ attributes, setAttributes ];
}

/**
* Prevents default dragging behavior within a block to allow for multi-
* selection to take effect unhampered.
Expand Down Expand Up @@ -104,6 +157,12 @@ function BlockListBlock( {
isNavigationMode,
enableNavigationMode,
} ) {
[ attributes, setAttributes ] = useMetaAttributeSourceBackwardsCompatibility(
name,
attributes,
setAttributes
);

// Random state used to rerender the component if needed, ideally we don't need this
const [ , updateRerenderState ] = useState( {} );
const rerender = () => updateRerenderState( {} );
Expand Down
1 change: 1 addition & 0 deletions packages/core-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@wordpress/api-fetch": "file:../api-fetch",
"@wordpress/data": "file:../data",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/element": "file:../element",
"@wordpress/url": "file:../url",
"equivalent-key-map": "^0.2.2",
"lodash": "^4.17.14",
Expand Down
76 changes: 76 additions & 0 deletions packages/core-data/src/entity-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* WordPress dependencies
*/
import { createContext, useContext, useCallback } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { defaultEntities } from './entities';

const entities = defaultEntities.reduce(
( acc, entity ) => {
if ( ! acc[ entity.kind ] ) {
acc[ entity.kind ] = {};
}
acc[ entity.kind ][ entity.name ] = { context: createContext() };
return acc;
},
{ postType: { post: { context: createContext() } } }
);

/**
* Context provider component for providing
* an entity for a specific entity type.
*
* @param {Object} props The component's props.
* @param {string} props.kind The entity kind.
* @param {string} props.type The entity type.
* @param {number} props.id The entity ID.
* @param {*} props.children The children to wrap.
*
* @return {Object} The provided children, wrapped with
* the entity's context provider.
*/
export default function EntityProvider( { kind, type, id, children } ) {
const Provider = entities[ kind ][ type ].context.Provider;
return <Provider value={ id }>{ children }</Provider>;
}

/**
* Hook that returns the value and a setter for the
* specified property of the nearest provided
* entity of the specified type.
*
* @param {string} kind The entity kind.
* @param {string} type The entity type.
* @param {string} prop The property name.
*
* @return {[*, Function]} A tuple where the first item is the
* property value and the second is the
* setter.
*/
export function useEntityProp( kind, type, prop ) {
const id = useContext( entities[ kind ][ type ].context );

const value = useSelect(
( select ) => {
const entity = select( 'core' ).getEditedEntityRecord( kind, type, id );
return entity && entity[ prop ];
},
[ kind, type, id, prop ]
);

const { editEntityRecord } = useDispatch( 'core' );
const setValue = useCallback(
( newValue ) => {
editEntityRecord( kind, type, id, {
[ prop ]: newValue,
} );
},
[ kind, type, id, prop ]
);

return [ value, setValue ];
}
2 changes: 2 additions & 0 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ registerStore( REDUCER_KEY, {
selectors: { ...selectors, ...entitySelectors },
resolvers: { ...resolvers, ...entityResolvers },
} );

export { default as EntityProvider, useEntityProp } from './entity-provider';
26 changes: 15 additions & 11 deletions packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { compose } from '@wordpress/compose';
import { Component } from '@wordpress/element';
import { withDispatch, withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { EntityProvider } from '@wordpress/core-data';
import { BlockEditorProvider, transformStyles } from '@wordpress/block-editor';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
Expand Down Expand Up @@ -142,6 +143,7 @@ class EditorProvider extends Component {
const {
canUserUseUnfilteredHTML,
children,
post,
blocks,
resetEditorBlocks,
isReady,
Expand All @@ -163,17 +165,19 @@ class EditorProvider extends Component {
);

return (
<BlockEditorProvider
value={ blocks }
onInput={ resetEditorBlocksWithoutUndoLevel }
onChange={ resetEditorBlocks }
settings={ editorSettings }
useSubRegistry={ false }
>
{ children }
<ReusableBlocksButtons />
<ConvertToGroupButtons />
</BlockEditorProvider>
<EntityProvider kind="postType" type={ post.type } id={ post.id }>
<BlockEditorProvider
value={ blocks }
onInput={ resetEditorBlocksWithoutUndoLevel }
onChange={ resetEditorBlocks }
settings={ editorSettings }
useSubRegistry={ false }
>
{ children }
<ReusableBlocksButtons />
<ConvertToGroupButtons />
</BlockEditorProvider>
</EntityProvider>
);
}
}
Expand Down
Loading

0 comments on commit 9988855

Please sign in to comment.