From 871a6ad48db1680fb1853338594554660c314327 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Wed, 27 Mar 2019 13:18:49 +0100 Subject: [PATCH 1/6] Native support for BlockList --- .../block-inspector/button.native.js | 42 +++ .../block-inspector/index.native.js | 77 +++++ .../block-inspector/style.native.scss | 29 ++ .../src/components/block-list/block.native.js | 252 ++++++++++++++++ .../components/block-list/block.native.scss | 40 +++ .../src/components/block-list/index.native.js | 273 ++++++++++++++++++ .../components/block-list/style.native.scss | 62 ++++ .../components/block-toolbar/index.native.js | 95 ++++++ .../block-toolbar/style.native.scss | 20 ++ .../default-block-appender/index.native.js | 2 +- .../src/components/index.native.js | 7 +- .../src/components/inserter/index.native.js | 110 +++++++ .../src/components/inserter/style.native.scss | 57 ++++ .../src/components/rich-text/index.native.js | 2 +- .../block-library/src/image/edit.native.js | 7 +- packages/blocks/src/api/index.native.js | 4 + packages/components/src/index.native.js | 7 + .../src}/mobile/bottom-sheet/button.native.js | 0 .../src}/mobile/bottom-sheet/cell.native.js | 2 +- .../bottom-sheet/cellStyles.android.scss | 0 .../mobile/bottom-sheet/cellStyles.ios.scss | 0 .../src}/mobile/bottom-sheet/index.native.js | 2 +- .../keyboard-avoiding-view.native.js | 0 .../mobile/bottom-sheet/picker-cell.native.js | 0 .../mobile/bottom-sheet/styles.native.scss | 0 .../mobile/keyboard-avoiding-view.android.js | 6 + .../src/mobile/keyboard-avoiding-view.ios.js | 16 + .../keyboard-aware-flat-list.android.js | 23 ++ .../mobile/keyboard-aware-flat-list.ios.js | 60 ++++ .../src}/mobile/picker/index.android.js | 6 +- .../src}/mobile/picker/index.ios.js | 0 .../mobile/readable-content-view.native.js | 27 ++ .../mobile/readable-content-view.native.scss | 8 + packages/edit-post/src/index.native.js | 1 + .../editor/src/components/index.native.js | 3 + .../format-library/src/link/modal.native.js | 2 +- 36 files changed, 1229 insertions(+), 13 deletions(-) create mode 100644 packages/block-editor/src/components/block-inspector/button.native.js create mode 100644 packages/block-editor/src/components/block-inspector/index.native.js create mode 100644 packages/block-editor/src/components/block-inspector/style.native.scss create mode 100644 packages/block-editor/src/components/block-list/block.native.js create mode 100644 packages/block-editor/src/components/block-list/block.native.scss create mode 100644 packages/block-editor/src/components/block-list/index.native.js create mode 100644 packages/block-editor/src/components/block-list/style.native.scss create mode 100644 packages/block-editor/src/components/block-toolbar/index.native.js create mode 100644 packages/block-editor/src/components/block-toolbar/style.native.scss create mode 100644 packages/block-editor/src/components/inserter/index.native.js create mode 100644 packages/block-editor/src/components/inserter/style.native.scss rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/button.native.js (100%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/cell.native.js (98%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/cellStyles.android.scss (100%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/cellStyles.ios.scss (100%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/index.native.js (98%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/keyboard-avoiding-view.native.js (100%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/picker-cell.native.js (100%) rename packages/{block-editor/src/components => components/src}/mobile/bottom-sheet/styles.native.scss (100%) create mode 100644 packages/components/src/mobile/keyboard-avoiding-view.android.js create mode 100644 packages/components/src/mobile/keyboard-avoiding-view.ios.js create mode 100644 packages/components/src/mobile/keyboard-aware-flat-list.android.js create mode 100644 packages/components/src/mobile/keyboard-aware-flat-list.ios.js rename packages/{block-editor/src/components => components/src}/mobile/picker/index.android.js (94%) rename packages/{block-editor/src/components => components/src}/mobile/picker/index.ios.js (100%) create mode 100644 packages/components/src/mobile/readable-content-view.native.js create mode 100644 packages/components/src/mobile/readable-content-view.native.scss diff --git a/packages/block-editor/src/components/block-inspector/button.native.js b/packages/block-editor/src/components/block-inspector/button.native.js new file mode 100644 index 00000000000000..a5ed9eecbd0924 --- /dev/null +++ b/packages/block-editor/src/components/block-inspector/button.native.js @@ -0,0 +1,42 @@ + +/** + * External dependencies + */ +import { View, TouchableOpacity } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { Dashicon } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +export default class BlockInspectorButton extends Component { + render() { + const { disabled, icon, onPress } = this.props; + + // There are better ways to write this, but it looks like RNSVG + // can't take an array of styles without crashing so we're doing + // that work instead ¯\_(ツ)_/¯ + let iconStyle = styles.icon; + if ( disabled ) { + iconStyle = { ...iconStyle, ...styles.iconDisabled }; + } + + return ( + + + + + + ); + } +} + +BlockInspectorButton.defaultProps = { + disabled: false, +}; diff --git a/packages/block-editor/src/components/block-inspector/index.native.js b/packages/block-editor/src/components/block-inspector/index.native.js new file mode 100644 index 00000000000000..bda8f8483c7031 --- /dev/null +++ b/packages/block-editor/src/components/block-inspector/index.native.js @@ -0,0 +1,77 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { ToolbarButton } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; +import InspectorControls from '../inspector-controls'; + +const BlockInspectorActions = { + UP: 1, + DOWN: 2, + DELETE: 3, +}; + +export { BlockInspectorActions }; + +export default class BlockInspector extends Component { + constructor( ...args ) { + super( ...args ); + + this.onUpPressed = this.onUpPressed.bind( this ); + this.onDownPressed = this.onDownPressed.bind( this ); + this.onDeletePressed = this.onDeletePressed.bind( this ); + } + + onUpPressed() { + this.props.onButtonPressed( BlockInspectorActions.UP ); + } + + onDownPressed() { + this.props.onButtonPressed( BlockInspectorActions.DOWN ); + } + + onDeletePressed() { + this.props.onButtonPressed( BlockInspectorActions.DELETE ); + } + + render() { + return ( + + + + + + + + + + + + ); + } +} diff --git a/packages/block-editor/src/components/block-inspector/style.native.scss b/packages/block-editor/src/components/block-inspector/style.native.scss new file mode 100644 index 00000000000000..5d55964d76dabd --- /dev/null +++ b/packages/block-editor/src/components/block-inspector/style.native.scss @@ -0,0 +1,29 @@ +/** @format */ + +.toolbar { + flex-direction: row; + height: 44px; + align-items: flex-start; + margin-left: 2px; + margin-right: 2px; +} + +.spacer { + flex-grow: 1; +} + +.button { + width: 44px; + height: 44px; + justify-content: center; + align-items: center; +} + +.icon { + color: $toolbar-button; + fill: currentColor; +} + +.iconDisabled { + color: $toolbar-button-disabled; +} diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js new file mode 100644 index 00000000000000..250b766b801864 --- /dev/null +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -0,0 +1,252 @@ +/** + * External dependencies + */ +import { + View, + Text, + TouchableWithoutFeedback, +} from 'react-native'; +import TextInputState from 'react-native/lib/TextInputState'; +import { + requestImageUploadCancel, +} from 'react-native-gutenberg-bridge'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { withDispatch, withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { addAction, removeAction, hasAction } from '@wordpress/hooks'; +import { getBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import styles from './block.scss'; +import BlockEdit from '../block-edit'; +import { default as BlockInspector, BlockInspectorActions } from '../block-inspector'; + +export class BlockListBlock extends Component { + constructor( props ) { + super( props ); + + this.onFocus = this.onFocus.bind( this ); + this.insertBlocksAfter = this.insertBlocksAfter.bind( this ); + this.mergeBlocks = this.mergeBlocks.bind( this ); + this.onRemoveBlockCheckUpload = this.onRemoveBlockCheckUpload.bind( this ); + this.onBlockInspectorButtonPressed = this.onBlockInspectorButtonPressed.bind( this ); + + this.state = { + isFullyBordered: false, + }; + } + + onFocus( event ) { + if ( event ) { + // == Hack for the Alpha == + // When moving the focus from a TextInput field to another kind of field the call that hides the keyboard is not invoked + // properly, resulting in keyboard up when it should not be there. + // The code below dismisses the keyboard (calling blur on the last TextInput field) when the field that now gets the focus is a non-textual field + const currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); + if ( event.nativeEvent.target !== currentlyFocusedTextInput && ! TextInputState.isTextInput( event.nativeEvent.target ) ) { + TextInputState.blurTextInput( currentlyFocusedTextInput ); + } + } + this.props.onSelect( this.props.clientId ); + } + + onRemoveBlockCheckUpload( mediaId ) { + if ( hasAction( 'blocks.onRemoveBlockCheckUpload' ) ) { + // now remove the action as it's a one-shot use and won't be needed anymore + removeAction( 'blocks.onRemoveBlockCheckUpload', 'gutenberg-mobile/blocks' ); + requestImageUploadCancel( mediaId ); + } + } + + onBlockInspectorButtonPressed( button ) { + switch ( button ) { + case BlockInspectorActions.UP: + this.props.moveBlockUp(); + break; + case BlockInspectorActions.DOWN: + this.props.moveBlockDown(); + break; + case BlockInspectorActions.DELETE: + // adding a action that will exist for as long as it takes for the block to be removed and the component unmounted + // this acts as a flag for the code using the action to know of its existence + addAction( 'blocks.onRemoveBlockCheckUpload', 'gutenberg-mobile/blocks', this.onRemoveBlockCheckUpload ); + this.props.removeBlock(); + break; + } + } + + insertBlocksAfter( blocks ) { + const order = this.props.getBlockIndex( this.props.clientId, this.props.rootClientId ); + this.props.onInsertBlocks( blocks, order + 1 ); + + if ( blocks[ 0 ] ) { + // focus on the first block inserted + this.props.onSelect( blocks[ 0 ].clientId ); + } + } + + mergeBlocks( forward = false ) { + const { + clientId, + getPreviousBlockClientId, + getNextBlockClientId, + mergeBlocks, + } = this.props; + + const previousBlockClientId = getPreviousBlockClientId( clientId ); + const nextBlockClientId = getNextBlockClientId( clientId ); + + // Do nothing when it's the first block. + if ( + ( ! forward && ! previousBlockClientId ) || + ( forward && ! nextBlockClientId ) + ) { + return; + } + + if ( forward ) { + mergeBlocks( clientId, nextBlockClientId ); + } else { + const name = this.props.getBlockName( previousBlockClientId ); + const blockType = getBlockType( name ); + // The default implementation does only focus the previous block if it's not mergeable + // We don't want to move the focus for now, just keep for and caret at the beginning of the current block. + if ( ! blockType.merge ) { + return; + } + mergeBlocks( previousBlockClientId, clientId ); + } + } + + renderToolbar() { + if ( ! this.props.isSelected ) { + return null; + } + + return ( + + ); + } + + getBlockForType() { + return ( + + ); + } + + renderBlockTitle() { + return ( + + BlockType: { this.props.name } + + ); + } + + render() { + const { isSelected, borderStyle, focusedBorderColor } = this.props; + + const borderColor = isSelected ? focusedBorderColor : 'transparent'; + + return ( + + + { this.props.showTitle && this.renderBlockTitle() } + { this.getBlockForType() } + { this.renderToolbar() } + + + ); + } +} + +export default compose( [ + withSelect( ( select, { clientId, rootClientId } ) => { + const { + getBlockAttributes, + getBlockName, + getBlockIndex, + getBlocks, + getPreviousBlockClientId, + getNextBlockClientId, + isBlockSelected, + } = select( 'core/block-editor' ); + const name = getBlockName( clientId ); + const attributes = getBlockAttributes( clientId ); + const order = getBlockIndex( clientId, rootClientId ); + const isSelected = isBlockSelected( clientId ); + const isFirstBlock = order === 0; + const isLastBlock = order === getBlocks().length - 1; + + return { + attributes, + getBlockIndex, + getBlockName, + getPreviousBlockClientId, + getNextBlockClientId, + isFirstBlock, + isLastBlock, + isSelected, + name, + }; + } ), + withDispatch( ( dispatch, { clientId, rootClientId } ) => { + const { + clearSelectedBlock, + insertBlocks, + mergeBlocks, + moveBlocksDown, + moveBlocksUp, + removeBlock, + replaceBlocks, + selectBlock, + updateBlockAttributes, + } = dispatch( 'core/block-editor' ); + + return { + mergeBlocks, + moveBlockDown() { + moveBlocksDown( clientId ); + }, + moveBlockUp() { + moveBlocksUp( clientId ); + }, + removeBlock() { + removeBlock( clientId ); + }, + onInsertBlocks( blocks, index ) { + insertBlocks( blocks, index, rootClientId ); + }, + onSelect: ( selectedClientId ) => { + clearSelectedBlock(); + selectBlock( selectedClientId ); + }, + onChange: ( attributes ) => { + updateBlockAttributes( clientId, attributes ); + }, + onReplace( blocks ) { + replaceBlocks( [ clientId ], blocks ); + }, + }; + } ), +] )( BlockListBlock ); diff --git a/packages/block-editor/src/components/block-list/block.native.scss b/packages/block-editor/src/components/block-list/block.native.scss new file mode 100644 index 00000000000000..b98791e7bbe467 --- /dev/null +++ b/packages/block-editor/src/components/block-list/block.native.scss @@ -0,0 +1,40 @@ +/** @format */ + +.blockCode { + font-family: $default-monospace-font; +} + +.blockText { + min-height: 50; +} + +.blockHolder { + flex: 1 1 auto; +} + +.blockContainer { + background-color: $white; + padding-left: 16; + padding-right: 16; + padding-top: 12; + padding-bottom: 12; +} + +.blockContainerFocused { + background-color: $white; + padding-left: 16; + padding-right: 16; + padding-top: 12; + padding-bottom: 0; // will be flushed into inline toolbar height +} + +.blockTitle { + background-color: $gray; + padding-left: 8; + padding-top: 4; + padding-bottom: 4; +} + +.aztec_container { + flex: 1; +} diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js new file mode 100644 index 00000000000000..367fac2e704bce --- /dev/null +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -0,0 +1,273 @@ +/** + * External dependencies + */ +import { identity } from 'lodash'; +import { Text, View, Keyboard, SafeAreaView, Platform } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Component, Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { withDispatch, withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { createBlock, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; +import { KeyboardAvoidingView, KeyboardAwareFlatList, ReadableContentView } from '@wordpress/components'; +import { subscribeMediaAppend } from 'react-native-gutenberg-bridge'; + +/** + * Internal dependencies + */ +import BlockListBlock from './block'; +import styles from './style.scss'; +import blockHolderStyles from './block.scss'; +import inlineToolbarStyles from '../block-inspector/style.scss'; +import toolbarStyles from '../block-toolbar/style.scss'; +import BlockToolbar from '../block-toolbar'; +import DefaultBlockAppender from '../default-block-appender'; +import Inserter from '../inserter'; + +export class BlockList extends Component { + constructor( ...args ) { + super( ...args ); + + this.renderItem = this.renderItem.bind( this ); + this.shouldFlatListPreventAutomaticScroll = this.shouldFlatListPreventAutomaticScroll.bind( this ); + this.renderDefaultBlockAppender = this.renderDefaultBlockAppender.bind( this ); + this.onBlockTypeSelected = this.onBlockTypeSelected.bind( this ); + this.keyboardDidShow = this.keyboardDidShow.bind( this ); + this.keyboardDidHide = this.keyboardDidHide.bind( this ); + this.onCaretVerticalPositionChange = this.onCaretVerticalPositionChange.bind( this ); + this.scrollViewInnerRef = this.scrollViewInnerRef.bind( this ); + + this.state = { + blockTypePickerVisible: false, + isKeyboardVisible: false, + }; + } + + // TODO: in the near future this will likely be changed to onShowBlockTypePicker and bound to this.props + // once we move the action to the toolbar + showBlockTypePicker( show ) { + this.setState( { blockTypePickerVisible: show } ); + } + + onBlockTypeSelected( itemValue ) { + this.setState( { blockTypePickerVisible: false } ); + + // create an empty block of the selected type + const newBlock = createBlock( itemValue ); + + this.finishBlockAppendingOrReplacing( newBlock ); + } + + finishBlockAppendingOrReplacing( newBlock ) { + // now determine whether we need to replace the currently selected block (if it's empty) + // or just add a new block as usual + if ( this.isReplaceable( this.props.selectedBlock ) ) { + // do replace here + this.props.replaceBlock( this.props.selectedBlockClientId, newBlock ); + } else { + const indexAfterSelected = this.props.selectedBlockOrder + 1; + const insertionIndex = indexAfterSelected || this.props.blockCount; + this.props.insertBlock( newBlock, insertionIndex ); + } + + // now set the focus + this.props.focusBlock( newBlock.clientId ); + } + + blockHolderBorderStyle() { + return this.props.isFullyBordered ? styles.blockHolderFullBordered : styles.blockHolderSemiBordered; + } + + componentDidMount() { + this._isMounted = true; + Keyboard.addListener( 'keyboardDidShow', this.keyboardDidShow ); + Keyboard.addListener( 'keyboardDidHide', this.keyboardDidHide ); + + this.subscriptionParentMediaAppend = subscribeMediaAppend( ( payload ) => { + // create an empty image block + const newImageBlock = createBlock( 'core/image' ); + + // now set the url and id + newImageBlock.attributes.url = payload.mediaUrl; + newImageBlock.attributes.id = payload.mediaId; + + // finally append or replace as appropriate + this.finishBlockAppendingOrReplacing( newImageBlock ); + } ); + } + + componentWillUnmount() { + Keyboard.removeListener( 'keyboardDidShow', this.keyboardDidShow ); + Keyboard.removeListener( 'keyboardDidHide', this.keyboardDidHide ); + + if ( this.subscriptionParentMediaAppend ) { + this.subscriptionParentMediaAppend.remove(); + } + this._isMounted = false; + } + + keyboardDidShow() { + this.setState( { isKeyboardVisible: true } ); + } + + keyboardDidHide() { + this.setState( { isKeyboardVisible: false } ); + } + + onCaretVerticalPositionChange( targetId, caretY, previousCaretY ) { + KeyboardAwareFlatList.handleCaretVerticalPositionChange( this.scrollViewRef, targetId, caretY, previousCaretY ); + } + + scrollViewInnerRef( ref ) { + this.scrollViewRef = ref; + } + + shouldFlatListPreventAutomaticScroll() { + return this.state.blockTypePickerVisible; + } + + renderDefaultBlockAppender() { + return ( + + + + ); + } + + renderList() { + return ( + + + + + + + { + this.showBlockTypePicker( true ); + } } + showKeyboardHideButton={ this.state.isKeyboardVisible } + /> + + + ); + } + + render() { + return ( + + { this.renderList() } + { this.state.blockTypePickerVisible && ( + this.showBlockTypePicker( false ) } + onValueSelected={ this.onBlockTypeSelected } + isReplacement={ this.isReplaceable( this.props.selectedBlock ) } + addExtraBottomPadding={ this.props.safeAreaBottomInset === 0 } + /> + ) } + + ); + } + + isReplaceable( block ) { + if ( ! block ) { + return false; + } + return isUnmodifiedDefaultBlock( block ); + } + + renderItem( value ) { + const clientId = value.item; + + return ( + + + { this.state.blockTypePickerVisible && this.props.isBlockSelected( clientId ) && ( + + + { __( 'ADD BLOCK HERE' ) } + + + ) } + + ); + } +} + +export default compose( [ + withSelect( ( select, { rootClientId } ) => { + const { + getBlockCount, + getBlockIndex, + getBlockOrder, + getSelectedBlock, + getSelectedBlockClientId, + isBlockSelected, + } = select( 'core/block-editor' ); + + const selectedBlockClientId = getSelectedBlockClientId(); + + return { + blockClientIds: getBlockOrder( rootClientId ), + blockCount: getBlockCount( rootClientId ), + isBlockSelected, + selectedBlock: getSelectedBlock(), + selectedBlockClientId, + selectedBlockOrder: getBlockIndex( selectedBlockClientId ), + }; + } ), + withDispatch( ( dispatch ) => { + const { + clearSelectedBlock, + insertBlock, + replaceBlock, + selectBlock, + } = dispatch( 'core/block-editor' ); + + return { + insertBlock, + focusBlock: ( clientId ) => { + clearSelectedBlock(); + selectBlock( clientId ); + }, + replaceBlock, + }; + } ), +] )( BlockList ); diff --git a/packages/block-editor/src/components/block-list/style.native.scss b/packages/block-editor/src/components/block-list/style.native.scss new file mode 100644 index 00000000000000..74a10eaba0aced --- /dev/null +++ b/packages/block-editor/src/components/block-list/style.native.scss @@ -0,0 +1,62 @@ +/** @format */ + +.list { + flex: 1; +} + +.switch { + flex-direction: row; + justify-content: flex-start; + align-items: center; + margin: 10px; +} + +.switchLabel { + margin-left: 10px; +} + +.lineStyleAddHere { + flex: 1; + background-color: #0087be; // blue_wordpress + align-self: center; + height: 2px; +} + +.labelStyleAddHere { + flex: 1; + text-align: center; + font-family: $default-monospace-font; + font-size: 12px; + font-weight: bold; +} + +.containerStyleAddHere { + flex: 1; + flex-direction: row; + background-color: $white; +} + +.blockToolbarKeyboardAvoidingView { + position: absolute; + bottom: 0; + right: 0; + left: 0; +} + +.blockHolderSemiBordered { + border-top-width: 1px; + border-bottom-width: 1px; + border-left-width: 0; + border-right-width: 0; +} + +.blockHolderFullBordered { + border-top-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-right-width: 1px; +} + +.blockHolderFocused { + border-color: $gray-lighten-30; +} diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js new file mode 100644 index 00000000000000..fe7e03a8f51260 --- /dev/null +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -0,0 +1,95 @@ +/** + * External dependencies + */ +import { View, ScrollView, Keyboard } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { withSelect, withDispatch } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { Toolbar, ToolbarButton } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; +import BlockFormatControls from '../block-format-controls'; +import BlockControls from '../block-controls'; + +export class BlockToolbar extends Component { + constructor( ...args ) { + super( ...args ); + + this.onKeyboardHide = this.onKeyboardHide.bind( this ); + } + + onKeyboardHide() { + Keyboard.dismiss(); + } + + render() { + const { + hasRedo, + hasUndo, + redo, + undo, + onInsertClick, + showKeyboardHideButton, + } = this.props; + + return ( + + + + + + + + + + + { showKeyboardHideButton && + ( + + ) } + + ); + } +} + +export default compose( [ + withSelect( ( select ) => ( { + hasRedo: select( 'core/editor' ).hasEditorRedo(), + hasUndo: select( 'core/editor' ).hasEditorUndo(), + } ) ), + withDispatch( ( dispatch ) => ( { + redo: dispatch( 'core/editor' ).redo, + undo: dispatch( 'core/editor' ).undo, + } ) ), +] )( BlockToolbar ); diff --git a/packages/block-editor/src/components/block-toolbar/style.native.scss b/packages/block-editor/src/components/block-toolbar/style.native.scss new file mode 100644 index 00000000000000..134dd85823421a --- /dev/null +++ b/packages/block-editor/src/components/block-toolbar/style.native.scss @@ -0,0 +1,20 @@ +.container { + height: 44; + flex-direction: row; + background-color: $white; + border-top-color: #e9eff3; + border-top-width: 1; +} + +.scrollableContent { + flex-grow: 1; // Fixes RTL issue on Android. +} + +.keyboardHideContainer { + padding-right: 0; + padding-left: 0; + padding-top: 4px; + width: 44; + justify-content: center; + align-items: center; +} diff --git a/packages/block-editor/src/components/default-block-appender/index.native.js b/packages/block-editor/src/components/default-block-appender/index.native.js index 1319fd3fd2ce9f..4dca46adb6154e 100644 --- a/packages/block-editor/src/components/default-block-appender/index.native.js +++ b/packages/block-editor/src/components/default-block-appender/index.native.js @@ -7,7 +7,6 @@ import { TouchableWithoutFeedback, View } from 'react-native'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { RichText } from '@wordpress/block-editor'; import { compose } from '@wordpress/compose'; import { decodeEntities } from '@wordpress/html-entities'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -16,6 +15,7 @@ import { withSelect, withDispatch } from '@wordpress/data'; * Internal dependencies */ import styles from './style.scss'; +import RichText from '../rich-text'; export function DefaultBlockAppender( { isLocked, diff --git a/packages/block-editor/src/components/index.native.js b/packages/block-editor/src/components/index.native.js index cb6169440765af..a79218429e42a4 100644 --- a/packages/block-editor/src/components/index.native.js +++ b/packages/block-editor/src/components/index.native.js @@ -1,6 +1,7 @@ // Block Creation Components export { default as BlockControls } from './block-controls'; export { default as BlockEdit } from './block-edit'; +export { default as BlockList } from './block-list'; export { default as BlockFormatControls } from './block-format-controls'; export * from './colors'; export * from './font-sizes'; @@ -16,11 +17,9 @@ export { default as MediaPlaceholder } from './media-placeholder'; export { default as URLInput } from './url-input'; // Content Related Components +export { default as BlockInspector, BlockInspectorActions } from './block-inspector'; export { default as DefaultBlockAppender } from './default-block-appender'; +export { default as Inserter } from './inserter'; // State Related Components export { default as BlockEditorProvider } from './provider'; - -// Mobile Editor Related Components -export { default as BottomSheet } from './mobile/bottom-sheet'; -export { default as Picker } from './mobile/picker'; diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js new file mode 100644 index 00000000000000..d93c7f9d3a6f9d --- /dev/null +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +import { FlatList, Text, TouchableHighlight, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { SVG, BottomSheet } from '@wordpress/components'; +import { Component } from '@wordpress/element'; +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { getUnregisteredTypeHandlerName } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +class Inserter extends Component { + render() { + const numberOfColumns = this.calculateNumberOfColumns(); + const bottomPadding = this.props.addExtraBottomPadding && styles.contentBottomPadding; + + return ( + + + + } + keyExtractor={ ( item ) => item.name } + renderItem={ ( { item } ) => + this.props.onValueSelected( item.name ) }> + + + + { this.iconWithUpdatedFillColor( styles.modalIcon.fill, item.icon ) } + + + { item.title } + + + } + /> + + ); + } + + iconWithUpdatedFillColor( color, icon ) { + return ( + + { icon.src.props.children } + + ); + } + + calculateNumberOfColumns() { + const bottomSheetWidth = BottomSheet.getWidth(); + const { paddingLeft: itemPaddingLeft, paddingRight: itemPaddingRight } = styles.modalItem; + const { paddingLeft: containerPaddingLeft, paddingRight: containerPaddingRight } = styles.content; + const { width: itemWidth } = styles.modalIconWrapper; + const itemTotalWidth = itemWidth + itemPaddingLeft + itemPaddingRight; + const containerTotalWidth = bottomSheetWidth - ( containerPaddingLeft + containerPaddingRight ); + return Math.floor( containerTotalWidth / itemTotalWidth ); + } +} + +export default compose( [ + withSelect( ( select, { clientId, isAppender, rootClientId } ) => { + const { + getInserterItems, + getBlockName, + getBlockRootClientId, + getBlockSelectionEnd, + } = select( 'core/block-editor' ); + const { + getChildBlockNames, + } = select( 'core/blocks' ); + + let destinationRootClientId = rootClientId; + if ( ! destinationRootClientId && ! clientId && ! isAppender ) { + const end = getBlockSelectionEnd(); + if ( end ) { + destinationRootClientId = getBlockRootClientId( end ) || undefined; + } + } + const destinationRootBlockName = getBlockName( destinationRootClientId ); + const inserterItems = getInserterItems( destinationRootClientId ); + + return { + rootChildBlocks: getChildBlockNames( destinationRootBlockName ), + items: inserterItems.filter( ( { name } ) => name !== getUnregisteredTypeHandlerName() ), + destinationRootClientId, + }; + } ), +] )( Inserter ); diff --git a/packages/block-editor/src/components/inserter/style.native.scss b/packages/block-editor/src/components/inserter/style.native.scss new file mode 100644 index 00000000000000..82d5fa58226504 --- /dev/null +++ b/packages/block-editor/src/components/inserter/style.native.scss @@ -0,0 +1,57 @@ +/** @format */ + +.touchableArea { + border-radius: 8px 8px 8px 8px; +} + +.content { + padding: 0 0 0 0; + align-items: center; + justify-content: space-evenly; +} + +.contentBottomPadding { + padding-bottom: 20px; +} + +.rowSeparator { + height: 12px; +} + +.modalItem { + flex-direction: column; + justify-content: center; + align-items: center; + padding-left: 8; + padding-right: 8; + padding-top: 0; + padding-bottom: 0; +} + +.modalIconWrapper { + width: 104px; + height: 64px; + background-color: $gray-light; //#f3f6f8 + border-radius: 8px 8px 8px 8px; + justify-content: center; + align-items: center; +} + +.modalIcon { + width: 32px; + height: 32px; + justify-content: center; + align-items: center; + fill: $gray-dark; +} + +.modalItemLabel { + background-color: transparent; + padding-left: 2; + padding-right: 2; + padding-top: 4; + padding-bottom: 0; + justify-content: center; + font-size: 12; + color: $gray-dark; +} diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js index 95a2d91d2f2ff8..221e11759fa05b 100644 --- a/packages/block-editor/src/components/rich-text/index.native.js +++ b/packages/block-editor/src/components/rich-text/index.native.js @@ -11,7 +11,6 @@ import { View, Platform } from 'react-native'; */ import { Component, RawHTML } from '@wordpress/element'; import { withInstanceId, compose } from '@wordpress/compose'; -import { BlockFormatControls } from '@wordpress/block-editor'; import { withSelect } from '@wordpress/data'; import { applyFormat, @@ -38,6 +37,7 @@ import FormatEdit from './format-edit'; import FormatToolbar from './format-toolbar'; import { withBlockEditContext } from '../block-edit/context'; import { ListEdit } from './list-edit'; +import BlockFormatControls from '../block-format-controls'; import styles from './style.scss'; diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index af26201f1d35de..7881f11a18ddef 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -17,19 +17,20 @@ import { /** * WordPress dependencies */ +import { Component } from '@wordpress/element'; import { Toolbar, ToolbarButton, Spinner, Dashicon, + BottomSheet, + Picker, } from '@wordpress/components'; import { MediaPlaceholder, RichText, BlockControls, InspectorControls, - BottomSheet, - Picker, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { isURL } from '@wordpress/url'; @@ -53,7 +54,7 @@ const MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY = 'wordpress_media_libr const LINK_DESTINATION_CUSTOM = 'custom'; const LINK_DESTINATION_NONE = 'none'; -class ImageEdit extends React.Component { +class ImageEdit extends Component { constructor( props ) { super( props ); diff --git a/packages/blocks/src/api/index.native.js b/packages/blocks/src/api/index.native.js index 123be1e132a992..b99fccc531c217 100644 --- a/packages/blocks/src/api/index.native.js +++ b/packages/blocks/src/api/index.native.js @@ -21,8 +21,12 @@ export { getUnregisteredTypeHandlerName, getBlockType, getBlockTypes, + getBlockSupport, hasBlockSupport, isReusableBlock, + getChildBlockNames, + hasChildBlocks, + hasChildBlocksWithInserterSupport, setDefaultBlockName, getDefaultBlockName, } from './registration'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index b68e70efb00118..a45f888cfead09 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -18,3 +18,10 @@ export { default as withFocusOutside } from './higher-order/with-focus-outside'; export { default as withFocusReturn } from './higher-order/with-focus-return'; export { default as withNotices } from './higher-order/with-notices'; export { default as withSpokenMessages } from './higher-order/with-spoken-messages'; + +// Mobile Specific Components +export { default as KeyboardAvoidingView } from './mobile/keyboard-avoiding-view'; +export { default as KeyboardAwareFlatList } from './mobile/keyboard-aware-flat-list'; +export { default as ReadableContentView } from './mobile/readable-content-view'; +export { default as BottomSheet } from './mobile/bottom-sheet'; +export { default as Picker } from './mobile/picker'; diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/button.native.js b/packages/components/src/mobile/bottom-sheet/button.native.js similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/button.native.js rename to packages/components/src/mobile/bottom-sheet/button.native.js diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js similarity index 98% rename from packages/block-editor/src/components/mobile/bottom-sheet/cell.native.js rename to packages/components/src/mobile/bottom-sheet/cell.native.js index 9acc94bbfb3bbd..dbe14a4b7fbe7e 100644 --- a/packages/block-editor/src/components/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -6,12 +6,12 @@ import { TouchableOpacity, Text, View, TextInput, I18nManager } from 'react-nati /** * WordPress dependencies */ -import { Dashicon } from '@wordpress/components'; import { Component } from '@wordpress/element'; /** * Internal dependencies */ +import Dashicon from '../../dashicon'; import styles from './styles.scss'; import platformStyles from './cellStyles.scss'; diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/cellStyles.android.scss b/packages/components/src/mobile/bottom-sheet/cellStyles.android.scss similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/cellStyles.android.scss rename to packages/components/src/mobile/bottom-sheet/cellStyles.android.scss diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/cellStyles.ios.scss b/packages/components/src/mobile/bottom-sheet/cellStyles.ios.scss similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/cellStyles.ios.scss rename to packages/components/src/mobile/bottom-sheet/cellStyles.ios.scss diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/index.native.js b/packages/components/src/mobile/bottom-sheet/index.native.js similarity index 98% rename from packages/block-editor/src/components/mobile/bottom-sheet/index.native.js rename to packages/components/src/mobile/bottom-sheet/index.native.js index fbda22cc7aa66a..86b28b6d05f818 100644 --- a/packages/block-editor/src/components/mobile/bottom-sheet/index.native.js +++ b/packages/components/src/mobile/bottom-sheet/index.native.js @@ -17,7 +17,7 @@ import styles from './styles.scss'; import Button from './button'; import Cell from './cell'; import PickerCell from './picker-cell'; -import KeyboardAvoidingView from './keyboard-avoiding-view'; +import KeyboardAvoidingView from '../keyboard-avoiding-view'; class BottomSheet extends Component { constructor() { diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/keyboard-avoiding-view.native.js b/packages/components/src/mobile/bottom-sheet/keyboard-avoiding-view.native.js similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/keyboard-avoiding-view.native.js rename to packages/components/src/mobile/bottom-sheet/keyboard-avoiding-view.native.js diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/picker-cell.native.js b/packages/components/src/mobile/bottom-sheet/picker-cell.native.js similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/picker-cell.native.js rename to packages/components/src/mobile/bottom-sheet/picker-cell.native.js diff --git a/packages/block-editor/src/components/mobile/bottom-sheet/styles.native.scss b/packages/components/src/mobile/bottom-sheet/styles.native.scss similarity index 100% rename from packages/block-editor/src/components/mobile/bottom-sheet/styles.native.scss rename to packages/components/src/mobile/bottom-sheet/styles.native.scss diff --git a/packages/components/src/mobile/keyboard-avoiding-view.android.js b/packages/components/src/mobile/keyboard-avoiding-view.android.js new file mode 100644 index 00000000000000..83e2d980c68c5c --- /dev/null +++ b/packages/components/src/mobile/keyboard-avoiding-view.android.js @@ -0,0 +1,6 @@ +/** + * External dependencies + */ +import { KeyboardAvoidingView } from 'react-native'; + +export default KeyboardAvoidingView; diff --git a/packages/components/src/mobile/keyboard-avoiding-view.ios.js b/packages/components/src/mobile/keyboard-avoiding-view.ios.js new file mode 100644 index 00000000000000..127ca05ac3fece --- /dev/null +++ b/packages/components/src/mobile/keyboard-avoiding-view.ios.js @@ -0,0 +1,16 @@ +/** + * External dependencies + */ +import { KeyboardAvoidingView as IOSKeyboardAvoidingView, Dimensions } from 'react-native'; + +const KeyboardAvoidingView = ( props ) => { + const { parentHeight, ...otherProps } = props; + const { height: fullHeight } = Dimensions.get( 'window' ); + const keyboardVerticalOffset = fullHeight - parentHeight; + + return ( + + ); +}; + +export default KeyboardAvoidingView; diff --git a/packages/components/src/mobile/keyboard-aware-flat-list.android.js b/packages/components/src/mobile/keyboard-aware-flat-list.android.js new file mode 100644 index 00000000000000..8463e2ccd70741 --- /dev/null +++ b/packages/components/src/mobile/keyboard-aware-flat-list.android.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import { FlatList } from 'react-native'; + +/** + * Internal dependencies + */ +import KeyboardAvoidingView from './keyboard-avoiding-view'; + +const KeyboardAwareFlatList = ( props ) => { + return ( + + + + ); +}; + +KeyboardAwareFlatList.handleCaretVerticalPositionChange = () => { + //no need to handle on Android, it is system managed +}; + +export default KeyboardAwareFlatList; diff --git a/packages/components/src/mobile/keyboard-aware-flat-list.ios.js b/packages/components/src/mobile/keyboard-aware-flat-list.ios.js new file mode 100644 index 00000000000000..da315dfe577ccc --- /dev/null +++ b/packages/components/src/mobile/keyboard-aware-flat-list.ios.js @@ -0,0 +1,60 @@ +/** + * External dependencies + */ +import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { FlatList } from 'react-native'; + +const KeyboardAwareFlatList = ( props ) => { + const { + blockToolbarHeight, + innerToolbarHeight, + shouldPreventAutomaticScroll, + innerRef, + ...listProps + } = props; + + return ( + { + this.scrollViewRef = ref; + innerRef( ref ); + } } + onKeyboardWillHide={ () => { + this.keyboardWillShowIndicator = false; + } } + onKeyboardDidHide={ () => { + setTimeout( () => { + if ( ! this.keyboardWillShowIndicator && + this.latestContentOffsetY !== undefined && + ! shouldPreventAutomaticScroll() ) { + // Reset the content position if keyboard is still closed + this.scrollViewRef.props.scrollToPosition( 0, this.latestContentOffsetY, true ); + } + }, 50 ); + } } + onKeyboardWillShow={ () => { + this.keyboardWillShowIndicator = true; + } } + onScroll={ ( event ) => { + this.latestContentOffsetY = event.nativeEvent.contentOffset.y; + } } > + + + ); +}; + +KeyboardAwareFlatList.handleCaretVerticalPositionChange = ( scrollView, targetId, caretY, previousCaretY ) => { + if ( previousCaretY ) { //if this is not the first tap + scrollView.props.refreshScrollForField( targetId ); + } +}; + +export default KeyboardAwareFlatList; diff --git a/packages/block-editor/src/components/mobile/picker/index.android.js b/packages/components/src/mobile/picker/index.android.js similarity index 94% rename from packages/block-editor/src/components/mobile/picker/index.android.js rename to packages/components/src/mobile/picker/index.android.js index 50f264adaa55d7..3fad1cc2b23bf0 100644 --- a/packages/block-editor/src/components/mobile/picker/index.android.js +++ b/packages/components/src/mobile/picker/index.android.js @@ -9,7 +9,11 @@ import { View } from 'react-native'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { BottomSheet } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import BottomSheet from '../bottom-sheet'; export default class Picker extends Component { constructor() { diff --git a/packages/block-editor/src/components/mobile/picker/index.ios.js b/packages/components/src/mobile/picker/index.ios.js similarity index 100% rename from packages/block-editor/src/components/mobile/picker/index.ios.js rename to packages/components/src/mobile/picker/index.ios.js diff --git a/packages/components/src/mobile/readable-content-view.native.js b/packages/components/src/mobile/readable-content-view.native.js new file mode 100644 index 00000000000000..8f9e861258b6ab --- /dev/null +++ b/packages/components/src/mobile/readable-content-view.native.js @@ -0,0 +1,27 @@ +/** + * External dependencies + */ +import { View, Dimensions } from 'react-native'; + +/** + * Internal dependencies + */ +import styles from './readable-content-view.scss'; + +const ReadableContentView = ( props ) => { + return ( + + + { props.children } + + + ); +}; + +const isContentMaxWidth = () => { + const { width } = Dimensions.get( 'window' ); + return width > styles.centeredContent.maxWidth; +}; + +ReadableContentView.isContentMaxWidth = isContentMaxWidth; +export default ReadableContentView; diff --git a/packages/components/src/mobile/readable-content-view.native.scss b/packages/components/src/mobile/readable-content-view.native.scss new file mode 100644 index 00000000000000..611d4054a470fe --- /dev/null +++ b/packages/components/src/mobile/readable-content-view.native.scss @@ -0,0 +1,8 @@ +.container { + align-items: center; +} + +.centeredContent { + width: 100%; + max-width: 580; +} diff --git a/packages/edit-post/src/index.native.js b/packages/edit-post/src/index.native.js index 6ada9eb995eadd..e7414c8f15c588 100644 --- a/packages/edit-post/src/index.native.js +++ b/packages/edit-post/src/index.native.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import '@wordpress/core-data'; +import '@wordpress/block-editor'; import '@wordpress/notices'; import { registerCoreBlocks } from '@wordpress/block-library'; import { unregisterBlockType } from '@wordpress/blocks'; diff --git a/packages/editor/src/components/index.native.js b/packages/editor/src/components/index.native.js index 85a33d69bf2515..23e908c31bdc80 100644 --- a/packages/editor/src/components/index.native.js +++ b/packages/editor/src/components/index.native.js @@ -1,3 +1,6 @@ +/** + * Internal dependencies + */ // Post Related Components export { default as PostTitle } from './post-title'; diff --git a/packages/format-library/src/link/modal.native.js b/packages/format-library/src/link/modal.native.js index 57bc01b9f28ef2..71142f55bcaa99 100644 --- a/packages/format-library/src/link/modal.native.js +++ b/packages/format-library/src/link/modal.native.js @@ -9,9 +9,9 @@ import { Switch, Platform } from 'react-native'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { BottomSheet } from '@wordpress/block-editor'; import { prependHTTP } from '@wordpress/url'; import { + BottomSheet, withSpokenMessages, } from '@wordpress/components'; import { From 3b160a69a7076ec8d9f30779b5e346069b908bf7 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Wed, 27 Mar 2019 18:19:12 +0100 Subject: [PATCH 2/6] Bring last updates in --- .../src/components/block-toolbar/index.native.js | 4 ++-- .../src/components/block-toolbar/style.native.scss | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js index fe7e03a8f51260..aa8f53122d08b7 100644 --- a/packages/block-editor/src/components/block-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -9,7 +9,7 @@ import { View, ScrollView, Keyboard } from 'react-native'; import { Component } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { Toolbar, ToolbarButton } from '@wordpress/components'; +import { Toolbar, ToolbarButton, Dashicon } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; /** @@ -52,7 +52,7 @@ export class BlockToolbar extends Component { ) } onClick={ onInsertClick } /> Date: Mon, 1 Apr 2019 09:13:58 +0200 Subject: [PATCH 3/6] Bring last changes to BlockToolbar in --- .../src/components/block-toolbar/index.native.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js index aa8f53122d08b7..5519696add8f68 100644 --- a/packages/block-editor/src/components/block-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View, ScrollView, Keyboard } from 'react-native'; +import { View, ScrollView, Keyboard, Platform } from 'react-native'; /** * WordPress dependencies @@ -27,7 +27,11 @@ export class BlockToolbar extends Component { } onKeyboardHide() { - Keyboard.dismiss(); + this.props.clearSelectedBlock(); + if ( Platform.OS === 'android' ) { + // Avoiding extra blur calls on iOS but still needed for android. + Keyboard.dismiss(); + } } render() { @@ -91,5 +95,6 @@ export default compose( [ withDispatch( ( dispatch ) => ( { redo: dispatch( 'core/editor' ).redo, undo: dispatch( 'core/editor' ).undo, + clearSelectedBlock: dispatch( 'core/editor' ).clearSelectedBlock, } ) ), ] )( BlockToolbar ); From d0d787eca23cdb3906bb770918c9a9c1a32caff6 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Fri, 5 Apr 2019 15:45:58 +0200 Subject: [PATCH 4/6] Get latest changes from gb mobile --- .../src/components/block-list/block.native.js | 3 +++ .../src/components/block-toolbar/index.native.js | 8 +++++--- .../block-editor/src/components/inserter/index.native.js | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 250b766b801864..93dfed612b6c41 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -5,6 +5,7 @@ import { View, Text, TouchableWithoutFeedback, + Keyboard, } from 'react-native'; import TextInputState from 'react-native/lib/TextInputState'; import { @@ -65,6 +66,7 @@ export class BlockListBlock extends Component { } onBlockInspectorButtonPressed( button ) { + Keyboard.dismiss(); switch ( button ) { case BlockInspectorActions.UP: this.props.moveBlockUp(); @@ -151,6 +153,7 @@ export class BlockListBlock extends Component { insertBlocksAfter={ this.insertBlocksAfter } mergeBlocks={ this.mergeBlocks } onCaretVerticalPositionChange={ this.props.onCaretVerticalPositionChange } + clientId={ this.props.clientId } /> ); } diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js index 5519696add8f68..67192e4399b3cc 100644 --- a/packages/block-editor/src/components/block-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -55,18 +55,19 @@ export class BlockToolbar extends Component { > ) } onClick={ onInsertClick } + extraProps={ { hint: __( 'Double tap to add a block' ) } } /> diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index d93c7f9d3a6f9d..180e52b6e2991d 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -7,6 +7,7 @@ import { FlatList, Text, TouchableHighlight, View } from 'react-native'; * WordPress dependencies */ import { SVG, BottomSheet } from '@wordpress/components'; +import { getUnregisteredTypeHandlerName } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; From d1dd0f7054269b2a626ab1f8655179aa30346d9b Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Fri, 5 Apr 2019 17:12:34 +0200 Subject: [PATCH 5/6] Fix duplicate import --- packages/block-editor/src/components/inserter/index.native.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index 180e52b6e2991d..d93c7f9d3a6f9d 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -7,7 +7,6 @@ import { FlatList, Text, TouchableHighlight, View } from 'react-native'; * WordPress dependencies */ import { SVG, BottomSheet } from '@wordpress/components'; -import { getUnregisteredTypeHandlerName } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; From 61128c0a9d5d7199f5cfbab77c382dcdecf056d3 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Wed, 10 Apr 2019 13:12:16 +0200 Subject: [PATCH 6/6] Get latest changes from gb mobile --- .../components/block-inspector/index.native.js | 1 + .../src/components/block-list/block.native.js | 16 ++++++++++++++-- .../src/components/block-list/index.native.js | 5 +++++ .../src/components/block-toolbar/index.native.js | 5 ++++- .../src/components/inserter/index.native.js | 1 + 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-inspector/index.native.js b/packages/block-editor/src/components/block-inspector/index.native.js index bda8f8483c7031..a3bfe466e8fe74 100644 --- a/packages/block-editor/src/components/block-inspector/index.native.js +++ b/packages/block-editor/src/components/block-inspector/index.native.js @@ -49,6 +49,7 @@ export default class BlockInspector extends Component { return ( + // accessible prop needs to be false to access children + // https://facebook.github.io/react-native/docs/accessibility#accessible-ios-android + { this.props.showTitle && this.renderBlockTitle() } - { this.getBlockForType() } + + { this.getBlockForType() } + { this.renderToolbar() } diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js index 367fac2e704bce..09a95c436f8b19 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -149,6 +149,7 @@ export class BlockList extends Component { @@ -214,6 +216,7 @@ export class BlockList extends Component { key={ clientId } showTitle={ false } clientId={ clientId } + testID={ testID } rootClientId={ this.props.rootClientId } onCaretVerticalPositionChange={ this.onCaretVerticalPositionChange } borderStyle={ this.blockHolderBorderStyle() } @@ -235,6 +238,7 @@ export default compose( [ withSelect( ( select, { rootClientId } ) => { const { getBlockCount, + getBlockName, getBlockIndex, getBlockOrder, getSelectedBlock, @@ -247,6 +251,7 @@ export default compose( [ return { blockClientIds: getBlockOrder( rootClientId ), blockCount: getBlockCount( rootClientId ), + getBlockName, isBlockSelected, selectedBlock: getSelectedBlock(), selectedBlockClientId, diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js index 67192e4399b3cc..d75e84a1be4d79 100644 --- a/packages/block-editor/src/components/block-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -53,7 +53,7 @@ export class BlockToolbar extends Component { alwaysBounceHorizontal={ false } contentContainerStyle={ styles.scrollableContent } > - + ) } @@ -65,12 +65,14 @@ export class BlockToolbar extends Component { icon="undo" isDisabled={ ! hasUndo } onClick={ undo } + extraProps={ { hint: __( 'Double tap to undo last change' ) } } /> @@ -82,6 +84,7 @@ export class BlockToolbar extends Component { title={ __( 'Hide keyboard' ) } icon="keyboard-hide" onClick={ this.onKeyboardHide } + extraProps={ { hint: __( 'Tap to hide the keyboard' ) } } /> ) } diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index d93c7f9d3a6f9d..4e8c8856abb833 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -44,6 +44,7 @@ class Inserter extends Component { style={ styles.touchableArea } underlayColor={ 'transparent' } activeOpacity={ .5 } + accessibilityLabel={ item.title } onPress={ () => this.props.onValueSelected( item.name ) }>