Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extensible customer buttons #1215

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
113 changes: 113 additions & 0 deletions examples/25_custom_buttons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />

<title>JSONEditor | Custom buttons</title>

<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css" />
<script src="../dist/jsoneditor.js"></script>

<script src="https://bgrins.github.io/filereader.js/filereader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

<style type="text/css">
body {
width: 600px;
font: 11pt sans-serif;
}
#jsoneditor {
width: 100%;
height: 500px;
}

.jsoneditor-menu > button.jsoneditor-save {
background: url('image/save.svg');
background-size: cover;
}

.jsoneditor-menu > button.jsoneditor-load {
background: url('image/load.svg');
background-size: cover;
}
</style>
</head>
<body>
<h1>Custom buttons</h1>
<p>
This example demonstrates how to append custom buttons to the head menu
bar. The custom buttons is available in text mode.
</p>
<p>
This example use custom buttons to load/save local files. Powered by
<a href="http://bgrins.github.io/filereader.js/">FileReader.js</a> and
<a href="https://github.com/eligrey/FileSaver.js">FileSaver.js</a>.<br />
Only supported on modern browsers (Chrome, FireFox, IE10+, Safari 6.1+,
Opera 15+).
</p>

<div id="jsoneditor"></div>

<script>
const json = {
team: [
{
name: 'Joe',
age: 17
},
{
name: 'Sarah',
age: 13
},
{
name: 'Jack'
}
]
}
const options = {
mode: 'code',
modes: ['code', 'text', 'tree', 'view', 'form', 'preview'],
// disableButtons: ['transform', 'save'],
buttons: [
{
index: 7,
name: 'save',
title: 'Save as text.',
className: 'jsoneditor-save',
mode: ['code'],
target: function (editor) {
const fname = new Date().getTime() + '.json'
const blob = new Blob([editor.getText()], {
type: 'application/json;charset=utf-8'
})
saveAs(blob, fname)
}
},
{
index: 8,
name: 'load',
title: 'Upload a json file.',
mode: ['code'],
className: 'jsoneditor-load',
target: function (editor) {
var loadinput = document.createElement('input')
loadinput.type = 'file'
FileReaderJS.setupInput(loadinput, {
readAsDefault: 'Text',
on: {
load: function (event, file) {
editor.setText(event.target.result)
}
}
})
loadinput.click()
}
}
]
}
// create the editor
const container = document.getElementById('jsoneditor')
const editor = new JSONEditor(container, options, json)
</script>
</body>
</html>
4 changes: 4 additions & 0 deletions examples/image/load.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions examples/image/save.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 132 additions & 1 deletion src/js/JSONEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

const ace = require('./ace') // may be undefined in case of minimalist bundle
const VanillaPicker = require('./vanilla-picker') // may be undefined in case of minimalist bundle
const { ModeSwitcher } = require('./ModeSwitcher')
const { treeModeMixins } = require('./treemode')
const { textModeMixins } = require('./textmode')
const { previewModeMixins } = require('./previewmode')
const { clear, extend, getInnerText, getInternetExplorerVersion, parse } = require('./util')
const { tryRequireAjv } = require('./tryRequireAjv')
const { showTransformModal } = require('./showTransformModal')
const { showSortModal } = require('./showSortModal')
const { defaultOptions } = require('./defaultOptions')

const Ajv = tryRequireAjv()

Expand Down Expand Up @@ -187,7 +189,7 @@ JSONEditor.VALID_OPTIONS = [
'sortObjectKeys', 'navigationBar', 'statusBar', 'mainMenuBar', 'languages', 'language', 'enableSort', 'enableTransform', 'limitDragging',
'maxVisibleChilds', 'onValidationError',
'modalAnchor', 'popupAnchor',
'createQuery', 'executeQuery', 'queryDescription'
'createQuery', 'executeQuery', 'queryDescription', 'buttons', 'disableButtons'
]

/**
Expand All @@ -203,6 +205,7 @@ JSONEditor.prototype._create = function (container, options, json) {
this.json = json || {}

const mode = this.options.mode || (this.options.modes && this.options.modes[0]) || 'tree'
this._initButtons()
this.setMode(mode)
}

Expand Down Expand Up @@ -471,6 +474,134 @@ JSONEditor.registerMode = mode => {
}
}

/**
* Create menu buttons by options.buttons
*/
JSONEditor.prototype._createButtons = function () {
if (this.options && this.options.buttons) {
this.options.buttons.forEach((buttonCustomConf) => {
// Check mode setting
let shouldCreateByModeConf = false
if (buttonCustomConf.mode === undefined) {
shouldCreateByModeConf = true
} else if (
buttonCustomConf.mode === this.options.mode ||
buttonCustomConf.mode.indexOf(this.options.mode) !== -1
) {
shouldCreateByModeConf = true
}

// Check enable condition
let shouldCreateByEnableKey = false
if (buttonCustomConf.checkEnableKey) {
if (this.options[buttonCustomConf.checkEnableKey]) {
shouldCreateByEnableKey = true
} else {
shouldCreateByEnableKey = false
}
} else {
shouldCreateByEnableKey = true
}

// Check the key in editor condition
let shouldCreateByInEditorKey = false
if (buttonCustomConf.checkInEditorKey) {
if (this[buttonCustomConf.checkInEditorKey]) {
shouldCreateByInEditorKey = true
} else {
shouldCreateByInEditorKey = false
}
} else {
shouldCreateByInEditorKey = true
}

if (
shouldCreateByModeConf &&
shouldCreateByEnableKey &&
shouldCreateByInEditorKey
) {
// If the config is for children
if (buttonCustomConf.children) {
const createdButtonMap = {}
buttonCustomConf.children.forEach((childConf) => {
const buttonCustom = this._createButtonElement(childConf)
createdButtonMap[childConf.name] = buttonCustom
})
if (buttonCustomConf.afterCreate) {
buttonCustomConf.afterCreate(this, createdButtonMap)
}
} else {
this._createButtonElement(buttonCustomConf)
}
}
})
}
if (this.options && this.options.modes && this.options.modes.length) {
const me = this
this.modeSwitcher = new ModeSwitcher(
this.menu,
this.options.modes,
this.options.mode,
function onSwitch (mode) {
// switch mode and restore focus
me.setMode(mode)
me.modeSwitcher.focus()
}
)
}
}

/**
* Create dom element by the button configuration
* @param {Object} buttonCustomConf The configuration of a button.
* @return {Element} The button dom element created
*/
JSONEditor.prototype._createButtonElement = function (buttonCustomConf) {
const buttonCustom = document.createElement('button')
buttonCustom.type = 'button'
buttonCustom.className = buttonCustomConf.className
buttonCustom.title = buttonCustomConf.title
buttonCustom.name = buttonCustomConf.name
this.menu.appendChild(buttonCustom)
buttonCustom.onclick = () => {
buttonCustomConf.target(this, buttonCustom)
}
if (buttonCustomConf.afterCreate) {
buttonCustomConf.afterCreate(this, buttonCustom)
}
return buttonCustom
}

/**
* Initialize options.buttons array
*/
JSONEditor.prototype._initButtons = function () {
// Merge,filter,sort the custom buttons and built-in buttons
if (this.options) {
// Filter the default button name in the options.disableButtons
let deaultButtons = defaultOptions.buttons
let customButtons = this.options.buttons || []
if (this.options.disableButtons) {
deaultButtons = deaultButtons.filter(
(buttonOption) =>
this.options.disableButtons.indexOf(buttonOption.name) < 0
)
customButtons = customButtons.filter(
(buttonOption) =>
this.options.disableButtons.indexOf(buttonOption.name) < 0
)
}
// Insert the custom buttons into deault buttons by the index of the custom button option
if (customButtons) {
customButtons.forEach((element) => {
deaultButtons.splice(element.index, 0, element)
})
}
console.log(deaultButtons)
this.options.buttons = deaultButtons
}
}

// register tree, text, and preview modes
JSONEditor.registerMode(treeModeMixins)
JSONEditor.registerMode(textModeMixins)
Expand Down