From 08d5110d3a41a8dece06cc9ff35240dd097a8c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Zen?= Date: Tue, 2 Aug 2016 23:25:45 -0300 Subject: [PATCH] Hover menu --- package.json | 2 + src/components/Widgets/MarkdownControl.css | 39 ++++++++ src/components/Widgets/MarkdownControl.js | 106 +++++++++++++++------ 3 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 src/components/Widgets/MarkdownControl.css diff --git a/package.json b/package.json index cd998587..c3297e4d 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,8 @@ "localforage": "^1.4.2", "lodash": "^4.13.1", "pluralize": "^3.0.0", + "react-portal": "^2.2.1", + "selection-position": "^1.0.0", "slate": "^0.10.1", "slate-markdown-serializer": "^0.1.2" } diff --git a/src/components/Widgets/MarkdownControl.css b/src/components/Widgets/MarkdownControl.css new file mode 100644 index 00000000..c87888af --- /dev/null +++ b/src/components/Widgets/MarkdownControl.css @@ -0,0 +1,39 @@ + +.button { + color: #ccc; + cursor: pointer; +} + +.button[data-active="true"] { + color: black; +} + + +.menu > * { + display: inline-block; +} + +.menu > * + * { + margin-left: 10px; +} + +.hoverMenu { + padding: 8px 7px 6px; + position: absolute; + z-index: 1; + top: -10000px; + left: -10000px; + margin-top: -6px; + opacity: 0; + background-color: #222; + border-radius: 4px; + transition: opacity .75s; +} + +.hoverMenu .button { + color: #aaa; +} + +.hoverMenu .button[data-active="true"] { + color: #fff; +} diff --git a/src/components/Widgets/MarkdownControl.js b/src/components/Widgets/MarkdownControl.js index aac1d894..c971a4eb 100644 --- a/src/components/Widgets/MarkdownControl.js +++ b/src/components/Widgets/MarkdownControl.js @@ -2,7 +2,10 @@ import React, { PropTypes } from 'react'; import { Editor, Plain } from 'slate'; import Markdown from 'slate-markdown-serializer'; import Block from './MarkdownControlElements/Block'; +import Portal from 'react-portal'; +import position from 'selection-position'; import { Icon } from '../UI'; +import styles from './MarkdownControl.css'; const markdown = new Markdown(); /* @@ -69,13 +72,26 @@ class MarkdownControl extends React.Component { this.onClickMark = this.onClickMark.bind(this); this.onClickBlock = this.onClickBlock.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); - this.renderToolbar = this.renderToolbar.bind(this); + this.renderMenu = this.renderMenu.bind(this); this.renderMarkButton = this.renderMarkButton.bind(this); this.renderBlockButton = this.renderBlockButton.bind(this); this.renderNode = this.renderNode.bind(this); this.renderMark = this.renderMark.bind(this); + this.onOpen = this.onOpen.bind(this); + this.updateMenu = this.updateMenu.bind(this); } + /* + * On update, update the menu. + */ + componentDidMount() { + this.updateMenu(); + } + componentDidUpdate() { + this.updateMenu(); + } + + /* * Used to set toolbar buttons to active state */ @@ -184,20 +200,30 @@ class MarkdownControl extends React.Component { this.setState({ state }); } - renderToolbar() { - return ( -
- {this.renderMarkButton('bold', 'b')} - {this.renderMarkButton('italic', 'i')} - {this.renderMarkButton('code', 'code')} - {this.renderMarkButton('linebreak', 'break')} - {this.renderBlockButton('heading1', 'h1')} - {this.renderBlockButton('heading2', 'h2')} - {this.renderBlockButton('block-quote', 'blockquote')} - {this.renderBlockButton('bulleted-list', 'ul')} + /* + * When the portal opens, cache the menu element. + */ + onOpen(portal) { + this.setState({ menu: portal.firstChild }); + } -
- ); + renderMenu() { + const { state } = this.state + const isOpen = state.isExpanded && state.isFocused + return ( + +
+ {this.renderMarkButton('bold', 'b')} + {this.renderMarkButton('italic', 'i')} + {this.renderMarkButton('underlined', 'u')} + {this.renderMarkButton('code', 'code')} + {this.renderBlockButton('heading1', 'h1')} + {this.renderBlockButton('heading2', 'h2')} + {this.renderBlockButton('block-quote', 'blockquote')} + {this.renderBlockButton('bulleted-list', 'ul')} +
+
+ ) } renderMarkButton(type, icon) { @@ -205,9 +231,9 @@ class MarkdownControl extends React.Component { const onMouseDown = e => this.onClickMark(e, type); return ( - + ); } @@ -216,9 +242,9 @@ class MarkdownControl extends React.Component { const onMouseDown = e => this.onClickBlock(e, type); return ( - + + {icon} + ); } @@ -232,21 +258,39 @@ class MarkdownControl extends React.Component { return MARKS[mark.type]; } + /* + * Update the menu's absolute position. + */ + + updateMenu() { + const { menu, state } = this.state; + if (!menu) return; + + if (state.isBlurred || state.isCollapsed) { + menu.removeAttribute('style'); + return; + } + + const rect = position(); + menu.style.opacity = 1; + menu.style.top = `${rect.top + window.scrollY - menu.offsetHeight}px`; + menu.style.left = `${rect.left + window.scrollX - menu.offsetWidth / 2 + rect.width / 2}px`; + } + + render() { return (
- {this.renderToolbar()} -
- -
+ {this.renderMenu()} +
); }