Search focus and quick keys working
This commit is contained in:
parent
b4a0dd6a3d
commit
263f38f45c
|
@ -40,7 +40,6 @@ import {
|
|||
moveRight,
|
||||
moveUp,
|
||||
searchHasFocus,
|
||||
selectFind,
|
||||
setSelection
|
||||
} from './utils/domSelector'
|
||||
import { createFindKeyBinding } from '../utils/keyBindings'
|
||||
|
@ -319,6 +318,7 @@ export default class TreeMode extends PureComponent {
|
|||
|
||||
return h(Search, {
|
||||
key: 'search',
|
||||
ref: 'search',
|
||||
|
||||
text: this.state.searchResult.text,
|
||||
resultCount: this.state.searchResult.matches
|
||||
|
@ -327,6 +327,7 @@ export default class TreeMode extends PureComponent {
|
|||
onNext: this.handleSearchNext,
|
||||
onPrevious: this.handleSearchPrevious,
|
||||
onClose: this.handleCloseSearch,
|
||||
onFocusActive: this.handleSearchFocusActive,
|
||||
findKeyBinding: this.props.findKeyBinding,
|
||||
delay: SEARCH_DEBOUNCE
|
||||
})
|
||||
|
@ -681,7 +682,14 @@ export default class TreeMode extends PureComponent {
|
|||
|
||||
handleFocusFind = (event) => {
|
||||
event.preventDefault()
|
||||
selectFind(event.target)
|
||||
|
||||
if (this.refs.search) {
|
||||
this.refs.search.select()
|
||||
}
|
||||
else {
|
||||
// search will select automatically when created
|
||||
this.setState({ showSearch: true })
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchNext = (event) => {
|
||||
|
@ -733,9 +741,7 @@ export default class TreeMode extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleCloseSearch = (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
handleCloseSearch = () => {
|
||||
const { eson, searchResult } = search(this.state.eson, '')
|
||||
|
||||
this.setState({
|
||||
|
@ -745,6 +751,13 @@ export default class TreeMode extends PureComponent {
|
|||
})
|
||||
}
|
||||
|
||||
handleSearchFocusActive = () => {
|
||||
const active = this.state.searchResult.active
|
||||
if (active && active.area) {
|
||||
setSelection(this.refs.contents, active.path, active.area)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a JSONPatch to the current JSON document and emit a change event
|
||||
* @param {JSONPatchDocument} operations
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class DropDown extends Component {
|
|||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
title: PropTypes.string,
|
||||
options: PropTypes.arrayOf(PropTypes.shape({
|
||||
value: PropTypes.string.isRequired,
|
||||
|
@ -56,10 +56,10 @@ export default class DropDown extends Component {
|
|||
title: this.props.title,
|
||||
onClick: this.handleOpen
|
||||
}, [
|
||||
typeof selectedText === 'string'
|
||||
h('span', {key: 'text'}, typeof selectedText === 'string'
|
||||
? toCapital(selectedText)
|
||||
: selectedText,
|
||||
'\u00A0\u00A0',
|
||||
'\u00A0\u00A0'),
|
||||
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
||||
]),
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import { Component, createElement as h } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { keyComboFromEvent } from '../../utils/keyBindings'
|
||||
import { findEditorContainer, setSelection } from '../utils/domSelector'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faSearch from '@fortawesome/fontawesome-free-solid/faSearch'
|
||||
|
@ -16,6 +15,17 @@ import './Search.css'
|
|||
fontawesome.library.add(faSearch, faChevronUp, faChevronDown, faTimes)
|
||||
|
||||
export default class Search extends Component {
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onNext: PropTypes.func.isRequired,
|
||||
onPrevious: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onFocusActive: PropTypes.func.isRequired,
|
||||
delay: PropTypes.number,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super (props)
|
||||
|
||||
|
@ -24,6 +34,17 @@ export default class Search extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.select()
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.text !== this.props.text) {
|
||||
// clear a pending onChange callback (if any)
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return h('div', {className: 'jsoneditor-search'},
|
||||
h('form', {
|
||||
|
@ -37,6 +58,7 @@ export default class Search extends Component {
|
|||
h('input', {
|
||||
key: 'input',
|
||||
type: 'text',
|
||||
ref: this.setSearchInputRef,
|
||||
className: 'jsoneditor-search-text',
|
||||
value: this.state.text,
|
||||
onInput: this.handleChange,
|
||||
|
@ -61,7 +83,7 @@ export default class Search extends Component {
|
|||
type: 'button',
|
||||
className: 'jsoneditor-search-close',
|
||||
title: 'Close search',
|
||||
onClick: this.props.onClose
|
||||
onClick: this.handleClose
|
||||
}, h('i', {className: 'fa fa-times'})),
|
||||
]),
|
||||
// this.renderResultsCount(this.props.resultCount) // FIXME: show result count
|
||||
|
@ -86,10 +108,15 @@ export default class Search extends Component {
|
|||
return null
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.text !== this.props.text) {
|
||||
// clear a pending onChange callback (if any)
|
||||
clearTimeout(this.timeout)
|
||||
searchInput = null
|
||||
|
||||
setSearchInputRef = (element) => {
|
||||
this.searchInput = element
|
||||
}
|
||||
|
||||
select () {
|
||||
if (this.searchInput) {
|
||||
this.searchInput.select()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,16 +140,22 @@ export default class Search extends Component {
|
|||
this.timeout = setTimeout(this.debouncedOnChange, delay)
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
// TODO: make submit (Enter) and focus to search result (Ctrl+Enter) customizable
|
||||
const combo = keyComboFromEvent(event)
|
||||
if (combo === 'Ctrl+Enter' || combo === 'Command+Enter') {
|
||||
event.preventDefault()
|
||||
const active = this.props.searchResults[0]
|
||||
if (active) {
|
||||
const container = findEditorContainer(event.target)
|
||||
setSelection(container, active.path, active.type)
|
||||
}
|
||||
this.props.onFocusActive()
|
||||
}
|
||||
|
||||
if (combo === 'Escape') {
|
||||
event.preventDefault()
|
||||
|
||||
this.handleClose()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import { compileJSONPointer, parseJSONPointer } from '../../jsonPointer'
|
|||
let lastInputName = null
|
||||
|
||||
// TODO: create a constants file with the CSS names that are used in domSelector
|
||||
const SEARCH_TEXT_CLASS_NAME = 'jsoneditor-search-text'
|
||||
const SEARCH_COMPONENT_CLASS_NAME = 'jsoneditor-search'
|
||||
const NODE_CONTAINER_CLASS_NAME = 'jsoneditor-node-container'
|
||||
const CONTENTS_CONTAINER_CLASS_NAME = 'jsoneditor-tree-contents'
|
||||
|
@ -142,15 +141,6 @@ export function moveRight (fromElement) {
|
|||
setSelection(container, path, lastInputName)
|
||||
}
|
||||
|
||||
export function selectFind (eventTarget) {
|
||||
const container = findEditorContainer(eventTarget)
|
||||
const searchText = container.querySelector('input.' + SEARCH_TEXT_CLASS_NAME)
|
||||
|
||||
if (searchText) {
|
||||
searchText.select()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selection to a specific node and input field
|
||||
* @param {Element} container
|
||||
|
@ -215,6 +205,10 @@ function findPreviousNode (element) {
|
|||
const container = findContentsContainer(element)
|
||||
const node = findBaseNode(element)
|
||||
|
||||
if (!container || !node) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: is the following querySelectorAll a performance bottleneck?
|
||||
const all = Array.from(container.querySelectorAll('div.' + NODE_CONTAINER_CLASS_NAME))
|
||||
const index = all.indexOf(node)
|
||||
|
@ -226,6 +220,10 @@ function findNextNode (element) {
|
|||
const container = findContentsContainer(element)
|
||||
const node = findBaseNode(element)
|
||||
|
||||
if (!container || !node) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: is the following querySelectorAll a performance bottleneck?
|
||||
const all = Array.from(container.querySelectorAll('div.' + NODE_CONTAINER_CLASS_NAME))
|
||||
const index = all.indexOf(node)
|
||||
|
@ -236,6 +234,10 @@ function findNextNode (element) {
|
|||
function findFirstNode (element) {
|
||||
const container = findContentsContainer(element)
|
||||
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: is the following querySelectorAll a performance bottleneck?
|
||||
const all = Array.from(container.querySelectorAll('div.' + NODE_CONTAINER_CLASS_NAME))
|
||||
|
||||
|
@ -245,6 +247,10 @@ function findFirstNode (element) {
|
|||
function findLastNode (element) {
|
||||
const container = findContentsContainer(element)
|
||||
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: is the following querySelectorAll a performance bottleneck?
|
||||
const all = Array.from(container.querySelectorAll('div.' + NODE_CONTAINER_CLASS_NAME))
|
||||
|
||||
|
@ -255,6 +261,10 @@ function findNextSibling (element) {
|
|||
const container = findContentsContainer(element)
|
||||
const node = findBaseNode(element)
|
||||
|
||||
if (!container || !node) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: is the following querySelectorAll a performance bottleneck?
|
||||
const all = Array.from(container.querySelectorAll('div.' + NODE_CONTAINER_CLASS_NAME))
|
||||
const index = all.indexOf(node)
|
||||
|
|
Loading…
Reference in New Issue