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%;
|
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 **********************************/
|
/******************************* Floating Menu **********************************/
|
||||||
|
|
||||||
div.jsoneditor-node-container {
|
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 {
|
textarea.jsoneditor-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 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 { toCapital } from '../../utils/stringUtils'
|
||||||
|
|
||||||
import fontawesome from '@fortawesome/fontawesome'
|
import fontawesome from '@fortawesome/fontawesome'
|
||||||
import faChevronDown from '@fortawesome/fontawesome-free-solid/faChevronDown'
|
import faChevronDown from '@fortawesome/fontawesome-free-solid/faChevronDown'
|
||||||
import { keyComboFromEvent } from '../../utils/keyBindings'
|
import { keyComboFromEvent } from '../../utils/keyBindings'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import './Menu.css'
|
import './DropDown.css'
|
||||||
|
|
||||||
fontawesome.library.add(faChevronDown)
|
fontawesome.library.add(faChevronDown)
|
||||||
|
|
||||||
const MENU_CLASS_NAME = 'jsoneditor-actionmenu'
|
export default class DropDown extends Component {
|
||||||
const MODE_MENU_CLASS_NAME = MENU_CLASS_NAME + ' jsoneditor-modemenu'
|
|
||||||
|
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) {
|
constructor (props) {
|
||||||
super (props)
|
super (props)
|
||||||
|
|
||||||
|
@ -29,14 +39,22 @@ export default class ModeDropDown extends Component {
|
||||||
* props {{modes: string[], mode: string, onChangeMode: function, onError: function}}
|
* props {{modes: string[], mode: string, onChangeMode: function, onError: function}}
|
||||||
*/
|
*/
|
||||||
render () {
|
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', {
|
h('button', {
|
||||||
key: 'button',
|
key: 'button',
|
||||||
className: 'current-mode',
|
className: 'jsoneditor-dropdown-main-button',
|
||||||
title: 'Switch mode',
|
title: 'Switch mode',
|
||||||
onClick: this.handleOpen
|
onClick: this.handleOpen
|
||||||
}, [
|
}, [
|
||||||
toCapital(this.props.mode) + ' ',
|
toCapital(selectedText) + ' ',
|
||||||
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -63,28 +81,29 @@ export default class ModeDropDown extends Component {
|
||||||
*/
|
*/
|
||||||
renderDropDown () {
|
renderDropDown () {
|
||||||
if (this.state.open) {
|
if (this.state.open) {
|
||||||
const items = this.props.modes.map(mode => {
|
const items = this.props.options.map(option => {
|
||||||
return h('button', {
|
return h('button', {
|
||||||
key: mode,
|
key: option.value,
|
||||||
title: `Switch to ${mode} mode`,
|
ref: 'button',
|
||||||
className: 'jsoneditor-menu-button jsoneditor-type-modes' +
|
title: option.title || option.text || option.value,
|
||||||
((mode === this.props.mode) ? ' jsoneditor-selected' : ''),
|
className: 'jsoneditor-menu-item' +
|
||||||
|
((option.value === this.props.value) ? ' jsoneditor-menu-item-selected' : ''),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
try {
|
try {
|
||||||
this.handleRequestClose()
|
this.handleRequestClose()
|
||||||
|
|
||||||
this.props.onChangeMode(mode)
|
this.props.onChange(option.value)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.props.onError(err)
|
this.props.onError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, toCapital(mode))
|
}, toCapital(option.text || option.value))
|
||||||
})
|
})
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
key: 'dropdown',
|
key: 'dropdown',
|
||||||
className: MODE_MENU_CLASS_NAME,
|
className: 'jsoneditor-dropdown-list',
|
||||||
ref: 'menu',
|
ref: 'menu',
|
||||||
onKeyDown: this.handleKeyDown
|
onKeyDown: this.handleKeyDown
|
||||||
}, items)
|
}, items)
|
||||||
|
@ -112,6 +131,11 @@ export default class ModeDropDown extends Component {
|
||||||
event.target.nextSibling.focus()
|
event.target.nextSibling.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (combo ==='Escape') {
|
||||||
|
this.handleRequestClose()
|
||||||
|
setTimeout(() => this.focusToDropDownButton()) // FIXME: doesn't work
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addRequestCloseListener () {
|
addRequestCloseListener () {
|
||||||
|
@ -141,5 +165,11 @@ export default class ModeDropDown extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusToDropDownButton() {
|
||||||
|
if (this.refs.button) {
|
||||||
|
this.refs.button.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleWindowRequestClose = null
|
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 '../style.scss';
|
||||||
@import './MenuButton.scss';
|
|
||||||
|
|
||||||
.jsoneditor-menu {
|
.jsoneditor-menu {
|
||||||
// TODO: there is also css for this menu in jsoneditor.scss, move this here
|
// TODO: there is also css for this menu in jsoneditor.scss, move this here
|
||||||
|
@ -13,4 +12,37 @@
|
||||||
.jsoneditor-menu-group {
|
.jsoneditor-menu-group {
|
||||||
display: inline-block;
|
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 { createElement as h, PureComponent } from 'react'
|
||||||
import ModeDropDown from './ModeDropDown'
|
import DropDown from './DropDown'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import fontawesome from '@fortawesome/fontawesome'
|
import fontawesome from '@fortawesome/fontawesome'
|
||||||
|
@ -30,11 +30,11 @@ export default class TreeModeMenu extends PureComponent {
|
||||||
if (this.props.modes ) {
|
if (this.props.modes ) {
|
||||||
items = items.concat([
|
items = items.concat([
|
||||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||||
h(ModeDropDown, {
|
h(DropDown, {
|
||||||
key: 'mode',
|
key: 'mode',
|
||||||
modes: this.props.modes,
|
options: this.props.modes.map(mode => ({ value: mode })),
|
||||||
mode: this.props.mode,
|
value: this.props.mode,
|
||||||
onChangeMode: this.props.onChangeMode,
|
onChange: this.props.onChangeMode,
|
||||||
onError: this.props.onError
|
onError: this.props.onError
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createElement as h, PureComponent } from 'react'
|
import { createElement as h, PureComponent } from 'react'
|
||||||
import ModeDropDown from './ModeDropDown'
|
import DropDown from './DropDown'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import fontawesome from '@fortawesome/fontawesome'
|
import fontawesome from '@fortawesome/fontawesome'
|
||||||
|
@ -69,11 +69,11 @@ export default class TreeModeMenu extends PureComponent {
|
||||||
if (this.props.modes ) {
|
if (this.props.modes ) {
|
||||||
items = items.concat([
|
items = items.concat([
|
||||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||||
h(ModeDropDown, {
|
h(DropDown, {
|
||||||
key: 'mode',
|
key: 'mode',
|
||||||
modes: this.props.modes,
|
options: this.props.modes.map(mode => ({ value: mode })),
|
||||||
mode: this.props.mode,
|
value: this.props.mode,
|
||||||
onChangeMode: this.props.onChangeMode,
|
onChange: this.props.onChangeMode,
|
||||||
onError: this.props.onError
|
onError: this.props.onError
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in New Issue