Implemented custom key bindings

This commit is contained in:
jos 2017-07-24 15:50:18 +02:00
parent 239b702040
commit 8462bcda2c
7 changed files with 170 additions and 25 deletions

View File

@ -37,7 +37,7 @@ Constructs a new JSONEditor.
library used for JSON schema validation. Example:
```js
var options = {
const options = {
ajv: Ajv({ allErrors: true, verbose: true })
}
```
@ -72,6 +72,25 @@ Constructs a new JSONEditor.
Enables history, adds a button Undo and Redo to the menu of the JSONEditor. True by default. Only applicable when `mode` is 'tree' or 'form'.
- `{Object<String, String[]>} keyBindings`
Override default key bindings. For example to replace the binding to duplicate a node from `Ctrl+D` to `Ctrl+Shift+D`:
```js
const options = {
keyBindings: {
duplicate: ['Ctrl+Shift+D', 'Command+Shift+D']
}
}
```
It's important to define bindings for both Windows and Mac. The meta keys on Windows are `Ctrl`, `Shift`, `Alt`, and on Mac they are respectively `Command`, `Shift`, and `Option`.
All available key bindings are described on the page [Key Bindings](#key_bindings.md).
Key bindings are case insensitive.
- `{String} mode`
Set the editor mode. Available values: 'tree' (default), 'view', 'form', 'code', 'text'. In 'view' mode, the data and datastructure is read-only. In 'form' mode, only the value can be changed, the datastructure is read-only. Mode 'code' requires the Ace editor to be loaded on the page. Mode 'text' shows the data as plain text.
@ -229,12 +248,12 @@ Get JSON data as string.
A tree editor:
```js
var options = {
const options = {
mode: 'tree',
search: true
}
var editor = new JSONEditor(container, options)
var json = {
const editor = new JSONEditor(container, options)
let json = {
"Array": [1, 2, 3],
"Boolean": true,
"Null": null,
@ -245,18 +264,18 @@ var json = {
editor.set(json)
editor.expandAll()
var json = editor.get(json)
json = editor.get(json)
```
A text editor:
```js
var options = {
const options = {
mode: 'text',
indentation: 2
}
var editor = new JSONEditor(container, options)
var json = {
const editor = new JSONEditor(container, options)
let json = {
"Array": [1, 2, 3],
"Boolean": true,
"Null": null,
@ -266,7 +285,7 @@ var json = {
}
editor.set(json)
var json = editor.get()
json = editor.get()
```
## JSON parsing and stringification
@ -274,17 +293,17 @@ var json = editor.get()
In general to parse or stringify JSON data, the browsers built in JSON parser can be used. To create a formatted string from a JSON object, use:
```js
var formattedString = JSON.stringify(json, null, 2)
const formattedString = JSON.stringify(json, null, 2)
```
to create a compacted string from a JSON object, use:
```js
var compactString = JSON.stringify(json)
const compactString = JSON.stringify(json)
```
To parse a String to a JSON object, use:
```js
var json = JSON.parse(string)
const json = JSON.parse(string)
```

View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Custom key bindings | JSONEditor</title>
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
body {
width: 100%;
max-width: 500px;
margin: 0;
padding: 10px;
box-sizing: border-box;
font-family: sans-serif;
}
h1 {
font-size: 120%;
}
</style>
</head>
<body>
<h1>Custom key bindings</h1>
<p>
In this example, the key bindings for <code>format</code> and <code>compact</code> are changed to respectively <code>Ctrl+Alt+1</code> and <code>Ctrl+Alt+2</code> instead of the default <code>Ctrl+\</code> and <code>Ctrl+Shift+\</code>.
</p>
<p>
It's important to define key bindings for both Windows and Mac: <code>Command+Option+1</code> and <code>Command+Option+2</code> in this case.
</p>
<div id="jsoneditor"></div>
<script>
// create the editor
var container = document.getElementById('jsoneditor')
var options = {
modes: ['code', 'text'],
keyBindings: {
compact: ['Ctrl+Alt+1', 'Command+Option+1'],
format: ['Ctrl+Alt+2', 'Command+Option+2']
}
}
var json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
}
var editor = jsoneditor(container, options)
editor.set(json)
</script>
</body>
</html>

View File

@ -54,9 +54,6 @@ export default class TextMode extends Component {
constructor (props) {
super(props)
// TODO: make key bindings customizable
this.findKeyBinding = createFindKeyBinding(KEY_BINDINGS)
this.state = {
text: '{}',
compiledSchema: null
@ -88,6 +85,14 @@ export default class TextMode extends Component {
this.setSchema(nextProps.schema)
}
// Apply key bindings
if (!this.findKeyBinding ||
JSON.stringify(nextProps.keyBindings) !== JSON.stringify(currentProps.keyBindings)) {
// merge default and custom key bindings
const keyBindings = Object.assign({}, KEY_BINDINGS, nextProps.keyBindings)
this.findKeyBinding = createFindKeyBinding(keyBindings)
}
// TODO: apply patchText
}

View File

@ -66,9 +66,6 @@ export default class TreeMode extends Component {
this.id = Math.round(Math.random() * 1e5) // TODO: create a uuid here?
// TODO: make key bindings customizable
this.findKeyBinding = createFindKeyBinding(KEY_BINDINGS)
this.state = {
data,
@ -88,7 +85,7 @@ export default class TreeMode extends Component {
onExpand: this.handleExpand,
// TODO: now we're passing not just events but also other methods. reorganize this or rename 'state.events'
findKeyBinding: this.findKeyBinding
findKeyBinding: this.handleFindKeyBinding
},
search: {
@ -106,7 +103,7 @@ export default class TreeMode extends Component {
this.applyProps(nextProps, this.props)
}
// TODO: create some sort of watcher structure for these props? Is there a Reactpattern for that?
// TODO: create some sort of watcher structure for these props? Is there a React pattern for that?
applyProps (nextProps, currentProps) {
// Apply text
if (nextProps.text !== currentProps.text) {
@ -131,6 +128,14 @@ export default class TreeMode extends Component {
this.setSchema(nextProps.schema)
}
// Apply key bindings
if (!this.findKeyBinding ||
JSON.stringify(nextProps.keyBindings) !== JSON.stringify(currentProps.keyBindings)) {
// merge default and custom key bindings
const keyBindings = Object.assign({}, KEY_BINDINGS, nextProps.keyBindings)
this.findKeyBinding = createFindKeyBinding(keyBindings)
}
// TODO: apply patchText
// TODO: apply patch
}
@ -370,6 +375,12 @@ export default class TreeMode extends Component {
}
}
/** @private */
handleFindKeyBinding = (event) => {
// findKeyBinding can change on the fly, so we can't bind it statically
return this.findKeyBinding (event)
}
/** @private */
handleExpandAll = () => {
const expanded = true

View File

@ -80,7 +80,11 @@
alert(err)
},
mode: mode,
modes: ['text', 'code', 'tree', 'form', 'view'],
modes: ['text', 'code', 'tree', 'form', 'view'], keyBindings: {
compact: ['Ctrl+\\', 'Command+\\', 'Ctrl+Alt+1', 'Command+Option+1'],
format: ['Ctrl+Shift+\\', 'Command+Shift+\\', 'Ctrl+Alt+2', 'Command+Option+2'],
duplicate: ['Ctrl+D', 'Ctrl+Shift+D', 'Command+D', 'Command+Shift+D']
},
indentation: 4,
escapeUnicode: true,
history: true,

View File

@ -255,7 +255,8 @@ function jsoneditor (container, options = {}) {
unmountComponentAtNode(editor._container)
}
editor.setMode(options && options.mode || 'tree')
const mode = options && options.mode || (options.modes && options.modes[0]) || 'tree';
editor.setMode(mode)
return editor
}

View File

@ -1,5 +1,9 @@
// inspiration: https://github.com/andrepolischuk/keycomb
// TODO: write unit tests for keyBindings
// FIXME: implement an escape sequence for the separator +
/**
* Get a named key from a key code.
* For example:
@ -44,16 +48,36 @@ export function createFindKeyBinding (keyBindings) {
// turn the map with key bindings by name (multiple per binding) into a map by key combo
const keyCombos = {}
Object.keys(keyBindings).forEach ((name) => {
keyBindings[name].forEach(combo => keyCombos[combo.toUpperCase()] = name)
keyBindings[name].forEach(combo => keyCombos[normalizeKeyCombo(combo)] = name)
})
return function findKeyBinding (event){
return function findKeyBinding (event) {
const keyCombo = keyComboFromEvent(event)
console.log('keyCombo', keyCombo)
return keyCombos[keyCombo.toUpperCase()] || null
return keyCombos[normalizeKeyCombo(keyCombo)] || null
}
}
/**
* Normalize a key combo:
*
* - to upper case
* - replace aliases like "?" with "/"
*
* @param {string} combo
* @return {string}
*/
function normalizeKeyCombo (combo) {
const upper = combo.toUpperCase()
const last = upper[upper.length - 1]
if (last in aliases) {
return upper.substring(0, upper.length - 1) + aliases[last]
}
return upper
}
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
@ -165,3 +189,28 @@ const codes = {
'221': ']',
'222': '\''
}
// all secondary characters of the keyboard buttons (used via Shift)
const aliases = {
'~': '`',
'!': '1',
'@': '2',
'#': '3',
'$': '4',
'%': '5',
'^': '6',
'&': '7',
'*': '8',
'(': '9',
')': '0',
'_': '-',
'+': '=',
'{': '[',
'}': ']',
'|': '\\',
':': ';',
'"': '',
'<': ',',
'>': '.',
'?': '/'
}