You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.3 KiB
121 lines
3.3 KiB
/* eslint-disable no-cond-assign */ |
|
/* eslint-disable no-plusplus */ |
|
import { undoItem, redoItem, icons, MenuItem } from 'prosemirror-menu'; |
|
import { toggleMark } from 'prosemirror-commands'; |
|
import { wrapInList } from 'prosemirror-schema-list'; |
|
import { openPrompt } from './prompt'; |
|
import { TextField } from './TextField'; |
|
|
|
// Helpers to create specific types of items |
|
|
|
function cmdItem(cmd, options) { |
|
let passedOptions = { |
|
label: options.title, |
|
run: cmd, |
|
}; |
|
Object.keys(options).reduce((acc, optionKey) => { |
|
acc[optionKey] = options[optionKey]; |
|
return acc; |
|
}, passedOptions); |
|
if ((!options.enable || options.enable === true) && !options.select) |
|
passedOptions[options.enable ? 'enable' : 'select'] = state => cmd(state); |
|
|
|
return new MenuItem(passedOptions); |
|
} |
|
|
|
function markActive(state, type) { |
|
let { from, $from, to, empty } = state.selection; |
|
if (empty) return type.isInSet(state.storedMarks || $from.marks()); |
|
return state.doc.rangeHasMark(from, to, type); |
|
} |
|
|
|
function markItem(markType, options) { |
|
let passedOptions = { |
|
active(state) { |
|
return markActive(state, markType); |
|
}, |
|
enable: true, |
|
}; |
|
Object.keys(options).reduce((acc, optionKey) => { |
|
acc[optionKey] = options[optionKey]; |
|
return acc; |
|
}, passedOptions); |
|
return cmdItem(toggleMark(markType), passedOptions); |
|
} |
|
|
|
function linkItem(markType) { |
|
return new MenuItem({ |
|
title: 'Add or remove link', |
|
icon: icons.link, |
|
active(state) { |
|
return markActive(state, markType); |
|
}, |
|
enable(state) { |
|
return !state.selection.empty; |
|
}, |
|
run(state, dispatch, view) { |
|
if (markActive(state, markType)) { |
|
toggleMark(markType)(state, dispatch); |
|
return true; |
|
} |
|
openPrompt({ |
|
title: 'Create a link', |
|
fields: { |
|
href: new TextField({ |
|
label: 'https://example.com', |
|
class: 'small', |
|
required: true, |
|
}), |
|
}, |
|
callback(attrs) { |
|
toggleMark(markType, attrs)(view.state, view.dispatch); |
|
view.focus(); |
|
}, |
|
}); |
|
return false; |
|
}, |
|
}); |
|
} |
|
|
|
function wrapListItem(nodeType, options) { |
|
return cmdItem(wrapInList(nodeType, options.attrs), options); |
|
} |
|
|
|
export function buildMenuItems(schema) { |
|
let r = {}; |
|
let type; |
|
if ((type = schema.marks.strong)) |
|
r.toggleStrong = markItem(type, { |
|
title: 'Toggle strong style', |
|
icon: icons.strong, |
|
}); |
|
if ((type = schema.marks.em)) |
|
r.toggleEm = markItem(type, { title: 'Toggle emphasis', icon: icons.em }); |
|
if ((type = schema.marks.code)) |
|
r.toggleCode = markItem(type, { |
|
title: 'Toggle code font', |
|
icon: icons.code, |
|
}); |
|
if ((type = schema.marks.link)) r.toggleLink = linkItem(type); |
|
|
|
if ((type = schema.nodes.bullet_list)) |
|
r.wrapBulletList = wrapListItem(type, { |
|
title: 'Wrap in bullet list', |
|
icon: icons.bulletList, |
|
}); |
|
if ((type = schema.nodes.ordered_list)) |
|
r.wrapOrderedList = wrapListItem(type, { |
|
title: 'Wrap in ordered list', |
|
icon: icons.orderedList, |
|
}); |
|
|
|
let cut = arr => arr.filter(x => x); |
|
|
|
r.inlineMenu = [ |
|
cut([r.toggleStrong, r.toggleEm, r.toggleCode, r.toggleLink]), |
|
]; |
|
r.blockMenu = [cut([r.wrapBulletList, r.wrapOrderedList])]; |
|
r.fullMenu = r.inlineMenu.concat([[undoItem, redoItem]], r.blockMenu); |
|
|
|
return r; |
|
}
|
|
|