Refactor into a generic `DropDown` component
This commit is contained in:
parent
2c560264c0
commit
2999fbc380
|
@ -290,71 +290,6 @@ div.jsoneditor-menu-panel-right {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
/******************************* Action Menu **********************************/
|
||||
|
||||
div.jsoneditor-actionmenu {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
z-index: 99999;
|
||||
top: 20px;
|
||||
left: 18px; /* 20px - 2px where 2px half the difference between 24x24 icons of the menu and the 20x20 icons of the editor */
|
||||
background: white;
|
||||
|
||||
border: 1px solid #d3d3d3;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
|
||||
div.jsoneditor-actionmenu.jsoneditor-actionmenu-top {
|
||||
top: auto;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
div.jsoneditor-modemenu.jsoneditor-modemenu {
|
||||
top: 26px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu-item {
|
||||
line-height: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button {
|
||||
width: 136px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
|
||||
background: transparent;
|
||||
border: transparent;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
|
||||
cursor: pointer;
|
||||
color: #4d4d4d;
|
||||
|
||||
font-size: 10pt;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button:hover,
|
||||
button.jsoneditor-menu-button:focus {
|
||||
color: $black;
|
||||
background-color: #f5f5f5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button.jsoneditor-selected {
|
||||
color: white;
|
||||
background-color: #ee422e;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-default {
|
||||
width: 104px; /* 136px - 32px */
|
||||
}
|
||||
|
||||
/******************************* Floating Menu **********************************/
|
||||
|
||||
div.jsoneditor-node-container {
|
||||
|
@ -504,30 +439,6 @@ div.jsoneditor-node-container {
|
|||
|
||||
/******************************* **********************************/
|
||||
|
||||
div.jsoneditor-modes {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
button {
|
||||
background: none;
|
||||
width: auto;
|
||||
padding: 2px 6px;
|
||||
font-size: $fontSize;
|
||||
}
|
||||
|
||||
button.jsoneditor-type-modes {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
padding: 2px 6px;
|
||||
border-radius: 0;
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.jsoneditor-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import { Component, createElement as h } from 'react'
|
||||
import { toCapital } from '../../utils/stringUtils'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faChevronDown from '@fortawesome/fontawesome-free-solid/faChevronDown'
|
||||
import { keyComboFromEvent } from '../../utils/keyBindings'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import './Menu.css'
|
||||
import './DropDown.css'
|
||||
|
||||
fontawesome.library.add(faChevronDown)
|
||||
|
||||
const MENU_CLASS_NAME = 'jsoneditor-actionmenu'
|
||||
const MODE_MENU_CLASS_NAME = MENU_CLASS_NAME + ' jsoneditor-modemenu'
|
||||
export default class DropDown extends Component {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
options: PropTypes.arrayOf(PropTypes.shape({
|
||||
value: PropTypes.string.isRequired,
|
||||
text: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
})).isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onError: PropTypes.func
|
||||
}
|
||||
|
||||
export default class ModeDropDown extends Component {
|
||||
constructor (props) {
|
||||
super (props)
|
||||
|
||||
|
@ -29,14 +39,22 @@ export default class ModeDropDown extends Component {
|
|||
* props {{modes: string[], mode: string, onChangeMode: function, onError: function}}
|
||||
*/
|
||||
render () {
|
||||
return h('div', {className: 'jsoneditor-modes'}, [
|
||||
const selected = this.props.options
|
||||
? this.props.options.find(option => option.value === this.props.value)
|
||||
: null
|
||||
|
||||
const selectedText = selected
|
||||
? (selected.text || selected.value)
|
||||
: ''
|
||||
|
||||
return h('div', {className: 'jsoneditor-dropdown'}, [
|
||||
h('button', {
|
||||
key: 'button',
|
||||
className: 'current-mode',
|
||||
className: 'jsoneditor-dropdown-main-button',
|
||||
title: 'Switch mode',
|
||||
onClick: this.handleOpen
|
||||
}, [
|
||||
toCapital(this.props.mode) + ' ',
|
||||
toCapital(selectedText) + ' ',
|
||||
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
||||
]),
|
||||
|
||||
|
@ -63,28 +81,29 @@ export default class ModeDropDown extends Component {
|
|||
*/
|
||||
renderDropDown () {
|
||||
if (this.state.open) {
|
||||
const items = this.props.modes.map(mode => {
|
||||
const items = this.props.options.map(option => {
|
||||
return h('button', {
|
||||
key: mode,
|
||||
title: `Switch to ${mode} mode`,
|
||||
className: 'jsoneditor-menu-button jsoneditor-type-modes' +
|
||||
((mode === this.props.mode) ? ' jsoneditor-selected' : ''),
|
||||
key: option.value,
|
||||
ref: 'button',
|
||||
title: option.title || option.text || option.value,
|
||||
className: 'jsoneditor-menu-item' +
|
||||
((option.value === this.props.value) ? ' jsoneditor-menu-item-selected' : ''),
|
||||
onClick: () => {
|
||||
try {
|
||||
this.handleRequestClose()
|
||||
|
||||
this.props.onChangeMode(mode)
|
||||
this.props.onChange(option.value)
|
||||
}
|
||||
catch (err) {
|
||||
this.props.onError(err)
|
||||
}
|
||||
}
|
||||
}, toCapital(mode))
|
||||
}, toCapital(option.text || option.value))
|
||||
})
|
||||
|
||||
return h('div', {
|
||||
key: 'dropdown',
|
||||
className: MODE_MENU_CLASS_NAME,
|
||||
className: 'jsoneditor-dropdown-list',
|
||||
ref: 'menu',
|
||||
onKeyDown: this.handleKeyDown
|
||||
}, items)
|
||||
|
@ -112,6 +131,11 @@ export default class ModeDropDown extends Component {
|
|||
event.target.nextSibling.focus()
|
||||
}
|
||||
}
|
||||
|
||||
if (combo ==='Escape') {
|
||||
this.handleRequestClose()
|
||||
setTimeout(() => this.focusToDropDownButton()) // FIXME: doesn't work
|
||||
}
|
||||
}
|
||||
|
||||
addRequestCloseListener () {
|
||||
|
@ -141,5 +165,11 @@ export default class ModeDropDown extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
focusToDropDownButton() {
|
||||
if (this.refs.button) {
|
||||
this.refs.button.focus()
|
||||
}
|
||||
}
|
||||
|
||||
handleWindowRequestClose = null
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
@import '../style.scss';
|
||||
|
||||
.jsoneditor-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
button.jsoneditor-dropdown-main-button {
|
||||
background: none;
|
||||
width: auto;
|
||||
padding: 2px 6px;
|
||||
font-size: $fontSize;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.jsoneditor-dropdown-list {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
z-index: 99999;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
background: white;
|
||||
|
||||
border: 1px solid #d3d3d3;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
|
||||
button.jsoneditor-menu-item {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
width: 120px;
|
||||
height: auto;
|
||||
padding: 2px 6px;
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
|
||||
border-radius: 0;
|
||||
opacity: 1;
|
||||
background: transparent;
|
||||
border: transparent;
|
||||
color: #4d4d4d;
|
||||
font-size: 10pt;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: left;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-item:hover,
|
||||
button.jsoneditor-menu-item:focus {
|
||||
color: $black;
|
||||
background-color: #f5f5f5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-item.jsoneditor-menu-item-selected {
|
||||
color: white;
|
||||
background-color: #ee422e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
@import '../style.scss';
|
||||
@import './MenuButton.scss';
|
||||
|
||||
.jsoneditor-menu {
|
||||
// TODO: there is also css for this menu in jsoneditor.scss, move this here
|
||||
|
@ -13,4 +12,37 @@
|
|||
.jsoneditor-menu-group {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsoneditor-menu-group:not(:first-child) {
|
||||
border-left: 1px solid rgba(255,255,255, 0.1);
|
||||
margin-left: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.4);
|
||||
}
|
||||
button:focus,
|
||||
button:active {
|
||||
background-color: rgba(255,255,255,0.3);
|
||||
}
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
.jsoneditor-menu {
|
||||
|
||||
.jsoneditor-menu-group:not(:first-child) {
|
||||
border-left: 1px solid rgba(255,255,255, 0.1);
|
||||
margin-left: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
color: white;
|
||||
opacity: 0.8;
|
||||
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 16px;
|
||||
|
||||
&.current-mode {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.4);
|
||||
}
|
||||
button:focus,
|
||||
button:active {
|
||||
background-color: rgba(255,255,255,0.3);
|
||||
}
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import ModeDropDown from './ModeDropDown'
|
||||
import DropDown from './DropDown'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
|
@ -30,11 +30,11 @@ export default class TreeModeMenu extends PureComponent {
|
|||
if (this.props.modes ) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||
h(ModeDropDown, {
|
||||
h(DropDown, {
|
||||
key: 'mode',
|
||||
modes: this.props.modes,
|
||||
mode: this.props.mode,
|
||||
onChangeMode: this.props.onChangeMode,
|
||||
options: this.props.modes.map(mode => ({ value: mode })),
|
||||
value: this.props.mode,
|
||||
onChange: this.props.onChangeMode,
|
||||
onError: this.props.onError
|
||||
})
|
||||
])
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import ModeDropDown from './ModeDropDown'
|
||||
import DropDown from './DropDown'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
|
@ -69,11 +69,11 @@ export default class TreeModeMenu extends PureComponent {
|
|||
if (this.props.modes ) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||
h(ModeDropDown, {
|
||||
h(DropDown, {
|
||||
key: 'mode',
|
||||
modes: this.props.modes,
|
||||
mode: this.props.mode,
|
||||
onChangeMode: this.props.onChangeMode,
|
||||
options: this.props.modes.map(mode => ({ value: mode })),
|
||||
value: this.props.mode,
|
||||
onChange: this.props.onChangeMode,
|
||||
onError: this.props.onError
|
||||
})
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue