我正在构建一个 Grapesjs 插件,并向按钮组件添加了一个“jscript”特征,该组件显示为代码镜像文本区域。这个想法是让用户能够编辑一些与按钮相关联的 JavaScript 代码。我似乎无法拦截 codemirror 区域的change
事件,至少,不是正确的 codemirror 特定版本。
onEvent
令人高兴的是,当我编辑代码镜像区域并更改焦点时,一个常规的“更改”事件会触发我插件中的Grapejs处理程序editor.TraitManager.addType('jcodemirror-editor', {}
- 很好。然后我可以将 codemirror 区域的内容存储到特征中。
onEvent({ elInput, component, event }) {
let code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
},
但是,如果我们在代码镜像区域粘贴、退格或删除等,则永远不会发出常规的“更改”事件!
因此,我试图拦截更深层次的代码镜像特定“更改”事件,该事件通常通过拦截cm.on("change", function (cm, changeObj) {}
并且更可靠地触发(不幸的是每次击键时也是如此)。如何连接此 codemirror 特定事件以触发通常的onEvent({ elInput, component, event }) {}
代码?
我的https://jsfiddle.net/tcab/1rh7mn5b/中有一个解决方法,但想知道执行此操作的正确方法。
我的插件:
function customScriptPlugin(editor) {
const codemirrorEnabled = true // otherwise trait editor is just a plain textarea
const script = function (props) {
this.onclick = function () {
eval(props.jscript)
}
};
editor.DomComponents.addType("customScript", {
isComponent: el => el.tagName == 'BUTTON' && el.hasAttribute && el.hasAttribute("data-scriptable"),
model: {
defaults: {
traits: [
{
// type: 'text',
type: 'jcodemirror-editor', // defined below
name: 'jscript',
changeProp: true,
}
],
script,
jscript: `let res = 1 + 3; console.log('result is', res);`,
'script-props': ['jscript'],
},
},
});
editor.TraitManager.addType('jcodemirror-editor', {
createInput({ trait }) {
const el = document.createElement('div');
el.innerHTML = `
<form>
<textarea id="myjscript" name="myjscript" rows="14">
</textarea>
</form>
</div>
`
if (codemirrorEnabled) {
const textareaEl = el.querySelector('textarea');
var myCodeMirror = CodeMirror.fromTextArea(textareaEl, {
mode: "javascript",
lineWrapping: true,
});
// This is the 'more accurate' codemirror 'change' event
// which is triggered key by key. We need it cos if we paste
// or backspace or delete etc. in codemirror then the
// regular 'change' event is never issued! But how do we get
// this event to trigger the proper, usual 'onEvent' below?
// Currently cheating and doing the onEvent work here with
// this special handler.
myCodeMirror.on("change", function (cm, changeObj) { // HACK
const component = editor.getSelected()
const code_to_run = myCodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
console.log('onEvent hack - (myCodeMirror change event) updating jscript trait to be:', code_to_run)
})
}
return el;
},
// UI textarea & codemirror 'change' events trigger this function,
// so that we can update the component 'jscript' trait property.
onEvent({ elInput, component, event }) {
let code_to_run
if (codemirrorEnabled)
code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
else
code_to_run = elInput.querySelector('textarea').value
console.log('onEvent - updating jscript trait to be:', code_to_run)
component.getTrait('jscript').set('value', code_to_run);
}, // onEvent
// Updates the trait area UI based on what is in the component.
onUpdate({ elInput, component }) {
console.log('onUpdate - component trait jscript -> UI', component.get('jscript'))
if (codemirrorEnabled) {
const cm = elInput.querySelector(".CodeMirror").CodeMirror
cm.setValue(component.get('jscript'))
// codemirror content doesn't appear till you click on it - fix with this trick
setTimeout(function () {
cm.refresh();
}, 1);
}
else {
const textareaEl = elInput.querySelector('textarea');
textareaEl.value = component.get('jscript')
// actually is this even needed as things still update automatically without it?
// textareaEl.dispatchEvent(new CustomEvent('change'));
}
}, // onUpdate
}) // addType
editor.BlockManager.add(
'btnRegular',
{
category: 'Basic',
label: 'Regular Button',
attributes: { class: "fa fa-square-o" },
content: '<button type="button">Click Me</button>',
});
editor.BlockManager.add(
'btnScriptable',
{
category: 'Scriptable',
label: 'Scriptable Button',
attributes: { class: "fa fa-rocket" },
content: '<button type="button" data-scriptable="true">Run Script</button>',
});
}
const editor = grapesjs.init({
container: '#gjs',
fromElement: 1,
height: '100%',
storageManager: { type: 0 },
plugins: ['gjs-blocks-basic', 'customScriptPlugin']
});