mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-12 06:08:37 +00:00
88 lines
2.3 KiB
JavaScript
88 lines
2.3 KiB
JavaScript
import { WidgetType } from '@codemirror/view';
|
|
import { ViewPlugin, Decoration } from '@codemirror/view';
|
|
import { syntaxTree } from '@codemirror/language';
|
|
|
|
export class CheckboxWidget extends WidgetType {
|
|
constructor(checked) {
|
|
super();
|
|
this.checked = checked;
|
|
}
|
|
|
|
eq(other) {
|
|
return other.checked == this.checked;
|
|
}
|
|
|
|
toDOM() {
|
|
let wrap = document.createElement('span');
|
|
wrap.setAttribute('aria-hidden', 'true');
|
|
wrap.className = 'cm-boolean-toggle';
|
|
let box = wrap.appendChild(document.createElement('input'));
|
|
box.type = 'checkbox';
|
|
box.checked = this.checked;
|
|
return wrap;
|
|
}
|
|
|
|
ignoreEvent() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// EditorView
|
|
export function checkboxes(view) {
|
|
let widgets = [];
|
|
for (let { from, to } of view.visibleRanges) {
|
|
syntaxTree(view.state).iterate({
|
|
from,
|
|
to,
|
|
enter: (node) => {
|
|
if (node.name == 'BooleanLiteral') {
|
|
let isTrue = view.state.doc.sliceString(node.from, node.to) == 'true';
|
|
let deco = Decoration.widget({
|
|
widget: new CheckboxWidget(isTrue),
|
|
side: 1,
|
|
});
|
|
widgets.push(deco.range(node.from));
|
|
}
|
|
},
|
|
});
|
|
}
|
|
return Decoration.set(widgets);
|
|
}
|
|
|
|
export const checkboxPlugin = ViewPlugin.fromClass(
|
|
class {
|
|
decorations; //: DecorationSet
|
|
|
|
constructor(view /* : EditorView */) {
|
|
this.decorations = checkboxes(view);
|
|
}
|
|
|
|
update(update /* : ViewUpdate */) {
|
|
if (update.docChanged || update.viewportChanged) this.decorations = checkboxes(update.view);
|
|
}
|
|
},
|
|
{
|
|
decorations: (v) => v.decorations,
|
|
|
|
eventHandlers: {
|
|
mousedown: (e, view) => {
|
|
let target = e.target; /* as HTMLElement */
|
|
if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-boolean-toggle'))
|
|
return toggleBoolean(view, view.posAtDOM(target));
|
|
},
|
|
},
|
|
},
|
|
);
|
|
|
|
function toggleBoolean(view /* : EditorView */, pos /* : number */) {
|
|
let before = view.state.doc.sliceString(Math.max(0, pos), pos + 5).trim();
|
|
let change;
|
|
if (!['true', 'false'].includes(before)) {
|
|
return false;
|
|
}
|
|
let insert = before === 'true' ? 'false' : 'true';
|
|
change = { from: pos, to: pos + before.length, insert };
|
|
view.dispatch({ changes: change });
|
|
return true;
|
|
}
|