Fixes #164: Rewrote all CM view plugins to statefields
This commit is contained in:
+40
-73
@@ -1,73 +1,5 @@
|
||||
import {
|
||||
Decoration,
|
||||
DecorationSet,
|
||||
EditorView,
|
||||
NodeType,
|
||||
SyntaxNodeRef,
|
||||
ViewPlugin,
|
||||
ViewUpdate,
|
||||
WidgetType,
|
||||
} from "../deps.ts";
|
||||
import { isCursorInRange, iterateTreeInVisibleRanges } from "./util.ts";
|
||||
|
||||
// TODO: Find a nicer way to inject this on task handler into the class
|
||||
function TaskListsPluginFactory(onCheckboxClick: (pos: number) => void) {
|
||||
return class TaskListsPlugin {
|
||||
decorations: DecorationSet = Decoration.none;
|
||||
constructor(
|
||||
view: EditorView,
|
||||
) {
|
||||
this.decorations = this.addCheckboxes(view);
|
||||
}
|
||||
update(update: ViewUpdate) {
|
||||
if (update.docChanged || update.viewportChanged || update.selectionSet) {
|
||||
this.decorations = this.addCheckboxes(update.view);
|
||||
}
|
||||
}
|
||||
addCheckboxes(view: EditorView) {
|
||||
const widgets: any[] = [];
|
||||
iterateTreeInVisibleRanges(view, {
|
||||
enter: this.iterateTree(view, widgets),
|
||||
});
|
||||
return Decoration.set(widgets, true);
|
||||
}
|
||||
|
||||
private iterateTree(view: EditorView, widgets: any[]) {
|
||||
return ({ type, from, to, node }: SyntaxNodeRef) => {
|
||||
if (type.name !== "Task") return;
|
||||
let checked = false;
|
||||
// Iterate inside the task node to find the checkbox
|
||||
node.toTree().iterate({
|
||||
enter: (ref) => iterateInner(ref.type, ref.from, ref.to),
|
||||
});
|
||||
if (checked) {
|
||||
widgets.push(
|
||||
Decoration.mark({
|
||||
tagName: "span",
|
||||
class: "cm-task-checked",
|
||||
}).range(from, to),
|
||||
);
|
||||
}
|
||||
|
||||
function iterateInner(type: NodeType, nfrom: number, nto: number) {
|
||||
if (type.name !== "TaskMarker") return;
|
||||
if (isCursorInRange(view.state, [from + nfrom, from + nto])) return;
|
||||
const checkbox = view.state.sliceDoc(from + nfrom, from + nto);
|
||||
// Checkbox is checked if it has a 'x' in between the []
|
||||
if ("xX".includes(checkbox[1])) checked = true;
|
||||
const dec = Decoration.replace({
|
||||
widget: new CheckboxWidget(
|
||||
checked,
|
||||
from + nfrom + 1,
|
||||
onCheckboxClick,
|
||||
),
|
||||
});
|
||||
widgets.push(dec.range(from + nfrom, from + nto));
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
import { Decoration, NodeType, syntaxTree, WidgetType } from "../deps.ts";
|
||||
import { decoratorStateField, isCursorInRange } from "./util.ts";
|
||||
|
||||
/**
|
||||
* Widget to render checkbox for a task list item.
|
||||
@@ -80,7 +12,7 @@ class CheckboxWidget extends WidgetType {
|
||||
) {
|
||||
super();
|
||||
}
|
||||
toDOM(_view: EditorView): HTMLElement {
|
||||
toDOM(): HTMLElement {
|
||||
const wrap = document.createElement("span");
|
||||
wrap.classList.add("sb-checkbox");
|
||||
const checkbox = document.createElement("input");
|
||||
@@ -100,7 +32,42 @@ class CheckboxWidget extends WidgetType {
|
||||
export function taskListPlugin(
|
||||
{ onCheckboxClick }: { onCheckboxClick: (pos: number) => void },
|
||||
) {
|
||||
return ViewPlugin.fromClass(TaskListsPluginFactory(onCheckboxClick), {
|
||||
decorations: (v) => v.decorations,
|
||||
return decoratorStateField((state) => {
|
||||
const widgets: any[] = [];
|
||||
syntaxTree(state).iterate({
|
||||
enter({ type, from, to, node }) {
|
||||
if (type.name !== "Task") return;
|
||||
let checked = false;
|
||||
// Iterate inside the task node to find the checkbox
|
||||
node.toTree().iterate({
|
||||
enter: (ref) => iterateInner(ref.type, ref.from, ref.to),
|
||||
});
|
||||
if (checked) {
|
||||
widgets.push(
|
||||
Decoration.mark({
|
||||
tagName: "span",
|
||||
class: "cm-task-checked",
|
||||
}).range(from, to),
|
||||
);
|
||||
}
|
||||
|
||||
function iterateInner(type: NodeType, nfrom: number, nto: number) {
|
||||
if (type.name !== "TaskMarker") return;
|
||||
if (isCursorInRange(state, [from + nfrom, from + nto])) return;
|
||||
const checkbox = state.sliceDoc(from + nfrom, from + nto);
|
||||
// Checkbox is checked if it has a 'x' in between the []
|
||||
if ("xX".includes(checkbox[1])) checked = true;
|
||||
const dec = Decoration.replace({
|
||||
widget: new CheckboxWidget(
|
||||
checked,
|
||||
from + nfrom + 1,
|
||||
onCheckboxClick,
|
||||
),
|
||||
});
|
||||
widgets.push(dec.range(from + nfrom, from + nto));
|
||||
}
|
||||
},
|
||||
});
|
||||
return Decoration.set(widgets, true);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user