Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Jan 15, 2018
1 parent 5bbfe3f commit f25e33d
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 9 deletions.
26 changes: 21 additions & 5 deletions blocks/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export function createBlock( name, blockAttributes = {} ) {
* @param {Boolean} isMultiBlock Array of possible block transformations
* @return {Function} Predicate that receives a block type.
*/
const isTransformForBlockSource = ( sourceName, isMultiBlock = false ) => ( transform ) => (
transform.type === 'block' &&
const isTransformForBlockSource = ( sourceName, transformType, isMultiBlock = false ) => ( transform ) => (
transform.type === transformType &&
transform.blocks.indexOf( sourceName ) !== -1 &&
( ! isMultiBlock || transform.isMultiBlock )
);
Expand All @@ -83,10 +83,10 @@ const isTransformForBlockSource = ( sourceName, isMultiBlock = false ) => ( tran
* @param {Boolean} isMultiBlock Array of possible block transformations
* @return {Function} Predicate that receives a block type.
*/
const createIsTypeTransformableFrom = ( sourceName, isMultiBlock = false ) => ( type ) => (
const createIsTypeTransformableFrom = ( sourceName, transformType, isMultiBlock = false ) => ( type ) => (
!! find(
get( type, 'transforms.from', [] ),
isTransformForBlockSource( sourceName, isMultiBlock ),
isTransformForBlockSource( sourceName, transformType, isMultiBlock ),
)
);

Expand All @@ -111,7 +111,7 @@ export function getPossibleBlockTransformations( blocks ) {
//compute the block that have a from transformation able to transfer blocks passed as argument.
const blocksToBeTransformedFrom = filter(
getBlockTypes(),
createIsTypeTransformableFrom( sourceBlockName, isMultiBlock ),
createIsTypeTransformableFrom( sourceBlockName, 'block', isMultiBlock ),
).map( type => type.name );

const blockType = getBlockType( sourceBlockName );
Expand All @@ -136,6 +136,22 @@ export function getPossibleBlockTransformations( blocks ) {
}, [] );
}

/**
* Gets all possible shortcut transforms based on a block name.
*
* @param {String} name Block name.
* @return {Array} Array of transforms.
*/
export function getPossibleShortcutTransformations( name ) {
const transformsFrom = getBlockTypes()
.reduce( ( acc, blockType ) => [ ...acc, ...get( blockType, 'transforms.from', [] ) ], [] )
.filter( isTransformForBlockSource( name, 'shortcut', false ) );
const transformsTo = get( getBlockType( name ), 'transforms.to', [] )
.filter( ( { type } ) => type === 'shortcut' );

return [ ...transformsFrom, ...transformsTo ];
}

/**
* Switch one or more blocks into one or more blocks of the new block type.
*
Expand Down
8 changes: 7 additions & 1 deletion blocks/api/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export { createBlock, getPossibleBlockTransformations, switchToBlockType, createReusableBlock } from './factory';
export {
createBlock,
getPossibleBlockTransformations,
getPossibleShortcutTransformations,
switchToBlockType,
createReusableBlock,
} from './factory';
export { default as parse, getBlockAttributes } from './parser';
export { default as rawHandler } from './raw-handling';
export {
Expand Down
31 changes: 31 additions & 0 deletions blocks/library/heading/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ registerBlockType( 'core/heading', {
} );
},
},
...'23456'.split( '' ).map( ( level ) => ( {
type: 'shortcut',
blocks: [ 'core/paragraph' ],
shortcut: level,
transform( blockAttributes ) {
return blockAttributes.map( ( { content } ) => {
return createBlock( 'core/heading', {
nodeName: `H${ level }`,
content,
} );
} );
},
} ) ),
],
to: [
{
Expand All @@ -90,6 +103,24 @@ registerBlockType( 'core/heading', {
} );
},
},
...'23456'.split( '' ).map( ( level ) => ( {
type: 'shortcut',
shortcut: level,
transform( blockAttributes ) {
return blockAttributes.map( ( { content, nodeName } ) => {
if ( nodeName === `H${ level }` ) {
return createBlock( 'core/paragraph', {
content,
} );
} else {
return createBlock( 'core/heading', {
nodeName: `H${ level }`,
content,
} );
}
} );
},
} ) ),
],
},

Expand Down
23 changes: 23 additions & 0 deletions blocks/library/list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ registerBlockType( 'core/list', {
} );
},
},
...[ 'OL', 'UL' ].map( ( tag ) => ( {
type: 'shortcut',
blocks: [ 'core/paragraph' ],
shortcut: tag.charAt( 0 ).toLowerCase(),
transform( blockAttributes ) {
const items = blockAttributes.map( ( { content } ) => content );
const hasItems = ! items.every( isEmpty );
return createBlock( 'core/list', {
nodeName: tag,
values: hasItems ? items.map( ( content, index ) => <li key={ index }>{ content }</li> ) : [],
} );
},
} ) ),
{
type: 'block',
blocks: [ 'core/quote' ],
Expand Down Expand Up @@ -175,6 +188,16 @@ registerBlockType( 'core/list', {
} );
},
},
...[ 'OL', 'UL' ].map( ( tag ) => ( {
type: 'shortcut',
shortcut: tag.charAt( 0 ).toLowerCase(),
transform( blockAttributes ) {
return createBlock( 'core/list', {
nodeName: 'OL',
values: blockAttributes.reduce( ( acc, { values } ) => [ ...acc, ...values ], [] ),
} );
},
} ) ),
],
},

Expand Down
61 changes: 58 additions & 3 deletions editor/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
mapValues,
sortBy,
throttle,
find,
first,
castArray,
every,
} from 'lodash';
import scrollIntoView from 'dom-scroll-into-view';
import 'element-closest';
Expand All @@ -18,7 +22,8 @@ import 'element-closest';
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { serialize } from '@wordpress/blocks';
import { serialize, getPossibleShortcutTransformations } from '@wordpress/blocks';
import { keycodes } from '@wordpress/utils';

/**
* Internal dependencies
Expand All @@ -35,9 +40,11 @@ import {
getSelectedBlock,
isSelectionEnabled,
} from '../../store/selectors';
import { startMultiSelect, stopMultiSelect, multiSelect, selectBlock } from '../../store/actions';
import { startMultiSelect, stopMultiSelect, multiSelect, selectBlock, replaceBlocks } from '../../store/actions';
import { isInputField } from '../../utils/dom';

const { isAccess } = keycodes;

class BlockList extends Component {
constructor( props ) {
super( props );
Expand All @@ -53,6 +60,7 @@ class BlockList extends Component {
// Browser does not fire `*move` event when the pointer position changes
// relative to the document, so fire it with the last known position.
this.onScroll = () => this.onPointerMove( { clientY: this.lastClientY } );
this.onKeyDown = this.onKeyDown.bind( this );

this.lastClientY = 0;
this.nodes = {};
Expand All @@ -71,6 +79,32 @@ class BlockList extends Component {
}

componentWillReceiveProps( nextProps ) {
const prevCommonName = this.commonName;

if ( nextProps.selectedBlock ) {
this.blocks = [ nextProps.selectedBlock ];
this.commonName = nextProps.selectedBlock.name;
} else if ( nextProps.multiSelectedBlocks.length ) {
this.blocks = nextProps.multiSelectedBlocks;

const firstName = first( nextProps.multiSelectedBlocks ).name

if ( every( nextProps.multiSelectedBlocks, ( { name } ) => name === firstName ) ) {
this.commonName = firstName;
} else {
delete this.commonName;
}
} else {
delete this.blocks;
delete this.commonName;
}

if ( ! this.commonName ) {
delete this.shortcutTransforms;
} else if ( this.commonName !== prevCommonName ) {
this.shortcutTransforms = getPossibleShortcutTransformations( this.commonName );
}

if ( isEqual( this.props.multiSelectedBlockUids, nextProps.multiSelectedBlockUids ) ) {
return;
}
Expand Down Expand Up @@ -214,11 +248,29 @@ class BlockList extends Component {
}
}

onKeyDown( event ) {
const { onReplace } = this.props;

if ( ! this.shortcutTransforms ) {
return;
}

const transform = find( this.shortcutTransforms, ( { shortcut } ) => isAccess( event, shortcut ) );

if ( transform ) {
const blocks = castArray( transform.transform( this.blocks.map( ( { attributes } ) => attributes ) ) );

onReplace( this.blocks.map( ( { uid } ) => uid ), blocks );

return;
}
}

render() {
const { blocks, showContextualToolbar } = this.props;

return (
<div>
<div onKeyDown={ this.onKeyDown }>
{ !! blocks.length && <BlockListSiblingInserter /> }
{ map( blocks, ( uid ) => (
<BlockListBlock
Expand Down Expand Up @@ -261,5 +313,8 @@ export default connect(
onRemove( uids ) {
dispatch( { type: 'REMOVE_BLOCKS', uids } );
},
onReplace( uids, blocks ) {
dispatch( replaceBlocks( uids, blocks ) );
},
} )
)( BlockList );
26 changes: 26 additions & 0 deletions utils/keycodes.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Browser dependencies
*/
const { userAgent } = window.navigator;

const isMac = userAgent.indexOf( 'Mac' ) !== -1;

export const BACKSPACE = 8;
export const TAB = 9;
export const ENTER = 13;
Expand All @@ -10,3 +17,22 @@ export const DOWN = 40;
export const DELETE = 46;

export const F10 = 121;

/**
* Check if the access keys and the gives letter are presssed.
*
* @param {KeyboardEvent} event The event object.
* @param {String} letter The letter to check.
* @return {Boolean} True if the combination is pressed, false if not.
*/
export function isAccess( event, letter ) {
if ( isMac ) {
if ( ! event.ctrlKey || ! event.altKey ) {
return;
}
} else if ( ! event.shiftKey || ! event.altKey ) {
return;
}

return event.keyCode === letter.toUpperCase().charCodeAt( 0 );
}

0 comments on commit f25e33d

Please sign in to comment.