godo.js/src-js/godo-menus.mjs

178 lines
4.6 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
}
};
function setHeader(type, level, icon_id) {
return {
run: setBlockType(type, {level}), dom: icon(icon_id)
}
}
// 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;
}
function blocks( schema, h_levels ) {
let menu = {
name : "blocks",
h_levels
};
let type, i = {};
if (type = schema.nodes.heading) {
let icon_id = 'h';
h_levels.forEach( (level) => {
i["setHeader"+level] = setHeader(type, level, icon_id);
icon_id = icon_id + "h";
});
}
if (type = schema.nodes.paragraph) {
i.setP = {run: setBlockType(type), dom: icon("p")};
}
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 };