178 lines
4.5 KiB
JavaScript
178 lines
4.5 KiB
JavaScript
import {toggleMark, setBlockType, wrapIn} from "prosemirror-commands";
|
|
import getLanguage from "./godo-menus-language.mjs";
|
|
|
|
class Menu {
|
|
constructor(menu, editorView) {
|
|
this.items = menu.items;
|
|
this.editorView = editorView;
|
|
this.name = menu.name;
|
|
|
|
this.el = document.createElement("div");
|
|
this.el.className = `godo-${this.name}-menu`;
|
|
this.el.wrapper = document.createElement("div");
|
|
this.el.wrapper.className = "menuicons";
|
|
for (const item in this.items) {
|
|
this.el.wrapper.appendChild(this.items[item].dom);
|
|
}
|
|
this.el.appendChild(this.el.wrapper);
|
|
|
|
this.update(editorView, null);
|
|
|
|
this.el.addEventListener("mousedown", e => {
|
|
e.preventDefault();
|
|
for (const item in this.items) {
|
|
const {dom, run} = this.items[item];
|
|
if (dom.contains(e.target)) {
|
|
run(editorView.state, editorView.dispatch, editorView);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
update( view, lastState ) {
|
|
let state = view.state;
|
|
|
|
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
|
return;
|
|
}
|
|
|
|
if (this.name === "marks") {
|
|
this.update_marks(view, state);
|
|
}
|
|
|
|
if (this.name === "blocks") {
|
|
this.update_blocks(view, state);
|
|
}
|
|
}
|
|
|
|
set_menu_item( menuItem, is_active, is_disabled = true ) {
|
|
if (is_active) {
|
|
menuItem.classList.add('active');
|
|
} else {
|
|
menuItem.classList.remove('active');
|
|
}
|
|
menuItem.disabled = (is_disabled) ? true : false;
|
|
}
|
|
|
|
update_blocks(view, state) {
|
|
for (const item in this.items) {
|
|
const {dom, run} = this.items[item];
|
|
const is_active = !run(state, null, view);
|
|
const is_disabled = is_active;
|
|
this.set_menu_item(dom, is_active, is_disabled);
|
|
}
|
|
}
|
|
|
|
update_marks( view, state ) {
|
|
let $sel = state.selection;
|
|
const empty = $sel.empty;
|
|
const $cursor = $sel.$cursor;
|
|
const ranges = $sel.ranges;
|
|
let get_is_active;
|
|
|
|
// if single cursor
|
|
if ($cursor && $cursor.marks()) {
|
|
const activeMarks = $cursor.marks().map( m => m.type.name);
|
|
get_is_active = (type) => activeMarks.includes(type.name) ? true : false;;
|
|
this.el.classList.add("fade");
|
|
|
|
// else = select range
|
|
} else {
|
|
const range = ranges[0];
|
|
if (range && ranges.length === 1){
|
|
get_is_active = (type) => state.doc.rangeHasMark(range.$from.pos, range.$to.pos, type);
|
|
this.el.classList.remove("fade");
|
|
}
|
|
}
|
|
|
|
for (const item in this.items) {
|
|
const {dom, type, run} = this.items[item];
|
|
const is_active = get_is_active(type);
|
|
let is_disabled = !run(state, null, view);
|
|
this.set_menu_item(dom, is_active, is_disabled);
|
|
}
|
|
|
|
// move marks nav above selection
|
|
let {from, to} = $sel;
|
|
let start = view.coordsAtPos(from);
|
|
let end = view.coordsAtPos(to);
|
|
let left = Math.max((start.left + end.left) / 2, start.left + 3);
|
|
this.el.style.transform = `translate(${left}px, ${end.bottom}px)`;
|
|
|
|
// if (view.hasFocus()) this.el.hidden = false;
|
|
this.el.hidden = (view.hasFocus()) ? false : true;
|
|
}
|
|
|
|
destroy() { this.el.remove(); }
|
|
}
|
|
|
|
|
|
function linkItem( type ) {
|
|
return {
|
|
dom: icon("a"),
|
|
run( state, dispatch, view ) {
|
|
let chref = prompt("href");
|
|
toggleMark(type, {href: chref})(state, dispatch);
|
|
},
|
|
type
|
|
}
|
|
};
|
|
|
|
|
|
// Helper function to create menu icons
|
|
// id: defined in languageContent
|
|
function icon(id) {
|
|
let menuicon = document.createElement("button")
|
|
menuicon.className = "menuicon menuicon-" + id;
|
|
menuicon.setAttribute("type", "button");
|
|
menuicon.title = getLanguage(id).text;
|
|
menuicon.textContent = getLanguage(id).icon;
|
|
return menuicon;
|
|
}
|
|
|
|
// Create an icon for a heading at the given level
|
|
function heading( level, type ) {
|
|
return {
|
|
run: setBlockType(type, {level}),
|
|
dom: icon("h")
|
|
}
|
|
}
|
|
|
|
function blocks( schema ) {
|
|
let menu = {
|
|
name : "blocks",
|
|
};
|
|
let type, i = {};
|
|
|
|
if (type = schema.nodes.paragraph) {
|
|
i.setP = {run: setBlockType(type), dom: icon("p")};
|
|
}
|
|
if (type = schema.nodes.heading) {
|
|
i.setH1 = heading(1, type);
|
|
i.seth2 = heading(2, type);
|
|
i.setH3 = heading(3, type);
|
|
}
|
|
|
|
menu.items = i;
|
|
return menu
|
|
}
|
|
|
|
function marks( schema ) {
|
|
let menu = {
|
|
name : "marks",
|
|
};
|
|
let type, i = {};
|
|
|
|
if (type = schema.marks.strong)
|
|
i.toggleStrong = {run: toggleMark(type), dom: icon("b"), type};
|
|
if (type = schema.marks.em)
|
|
i.toggleEm = {run: toggleMark(type), dom: icon("i"), type};
|
|
if (type = schema.marks.link)
|
|
i.toggleLink = linkItem(type);
|
|
|
|
menu.items = i;
|
|
return menu
|
|
}
|
|
|
|
export { Menu, blocks, marks };
|