From 9c35f9abb78e922c867eb3d1607ee7e4084f211c Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 29 Sep 2017 19:36:58 +0200 Subject: [PATCH] Proof of concept side toolbar --- src/components/draftjs-editor/SideToolbar.js | 74 ++++++++++++++++++++ src/components/draftjs-editor/index.js | 37 ++++++---- src/components/draftjs-editor/style.js | 5 ++ 3 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 src/components/draftjs-editor/SideToolbar.js diff --git a/src/components/draftjs-editor/SideToolbar.js b/src/components/draftjs-editor/SideToolbar.js new file mode 100644 index 0000000000..c41cfb10a6 --- /dev/null +++ b/src/components/draftjs-editor/SideToolbar.js @@ -0,0 +1,74 @@ +// @flow +// This file was copy-and-pasted from the draft-js-side-toolbar-plugin and adapted +// for our codebase. +import React from 'react'; +import { findDOMNode } from 'react-dom'; +// $FlowIssue +import DraftOffsetKey from 'draft-js/lib/DraftOffsetKey'; + +type EditorState = Object; // Draft.js editor state +type EditorRef = any; // A reference to the editor DOM node + +type Props = { + editorState: EditorState, + children: Function, + editorRef: EditorRef, +}; + +type ToolbarPosition = { + top?: number, + left?: number, +}; + +type State = { + position: ?ToolbarPosition, +}; + +export default class Toolbar extends React.Component { + state = { + position: {}, + }; + + componentDidMount() { + this.setPosition(this.props.editorState, this.props.editorRef); + } + + componentDidUpdate() { + this.setPosition(this.props.editorState, this.props.editorRef); + } + + setPosition = (editorState: EditorState, editorRef: EditorRef) => { + if (!editorRef) return; + const selection = editorState.getSelection(); + if (!selection.getHasFocus()) return; + + const currentContent = editorState.getCurrentContent(); + const currentBlock = currentContent.getBlockForKey(selection.getStartKey()); + if (!currentBlock) return; + const offsetKey = DraftOffsetKey.encode(currentBlock.getKey(), 0, 0); + if (!offsetKey) return; + // NOTE(@juliankrispel): Need to wait on tick to make sure the DOM node has been create by Draft.js + setTimeout(() => { + const node = document.querySelectorAll( + `[data-offset-key="${offsetKey}"]` + )[0]; + if (!node) return; + const top = node.getBoundingClientRect().top; + const scrollY = + window.scrollY == null ? window.pageYOffset : window.scrollY; + const editor = findDOMNode(editorRef); + if (!editor) return; + this.setState({ + position: { + top: top + scrollY, + left: editor.getBoundingClientRect().left - 80, + }, + }); + }, 0); + }; + + render() { + const { position } = this.state; + return this.props.children({ style: position }); + } +} diff --git a/src/components/draftjs-editor/index.js b/src/components/draftjs-editor/index.js index 177ffbd59f..8bf88908f9 100644 --- a/src/components/draftjs-editor/index.js +++ b/src/components/draftjs-editor/index.js @@ -31,7 +31,8 @@ import prismGlobalCSS from '!!raw-loader!./prism-theme.css'; injectGlobal`${prismGlobalCSS}`; import Image from './Image'; -import { Wrapper, MediaRow, ComposerBase } from './style'; +import { Wrapper, MediaRow, ComposerBase, SideToolbarWrapper } from './style'; +import SideToolbar from './SideToolbar'; import MediaInput from '../mediaInput'; import { LinkPreview, LinkPreviewLoading } from '../linkPreview'; @@ -156,6 +157,16 @@ class Editor extends React.Component { }} {...rest} /> + {images !== false && + !rest.readOnly && ( + + {({ style }) => ( + + + + )} + + )} {showLinkPreview && linkPreview && linkPreview.loading ? ( ) : showLinkPreview && linkPreview && linkPreview.data ? ( @@ -168,12 +179,6 @@ class Editor extends React.Component { margin={'16px 0 24px 0'} /> ) : null} - {images !== false && - !this.props.readOnly && ( - - Add - - )} ); } else { @@ -201,6 +206,16 @@ class Editor extends React.Component { }} {...rest} /> + {images !== false && + !rest.readOnly && ( + + {({ style }) => ( + + + + )} + + )} {showLinkPreview && linkPreview && linkPreview.loading ? ( @@ -214,14 +229,6 @@ class Editor extends React.Component { margin={'16px 0 24px 0'} /> ) : null} - {images !== false && - !this.props.readOnly && ( - - - Add - - - )} ); } diff --git a/src/components/draftjs-editor/style.js b/src/components/draftjs-editor/style.js index 64fa8ee6fc..4bbf6498ce 100644 --- a/src/components/draftjs-editor/style.js +++ b/src/components/draftjs-editor/style.js @@ -43,3 +43,8 @@ export const ComposerBase = styled.div` color: ${props => props.theme.text.placeholder}; } `; + +export const SideToolbarWrapper = styled.div` + position: fixed; + margin-top: -0.5em; +`;