]> BookStack Code Mirror - bookstack/blob - resources/js/editor/menu/index.js
Added list buttons
[bookstack] / resources / js / editor / menu / index.js
1 import {
2     MenuItem, Dropdown, DropdownSubmenu, renderGrouped, joinUpItem, liftItem, selectParentNodeItem,
3     undoItem, redoItem, wrapItem, blockTypeItem, setAttrItem,
4 } from "./menu"
5 import {icons} from "./icons";
6 import ColorPickerGrid from "./ColorPickerGrid";
7 import {toggleMark} from "prosemirror-commands";
8 import {menuBar} from "./menubar"
9 import schema from "../schema";
10
11
12 function cmdItem(cmd, options) {
13     const passedOptions = {
14         label: options.title,
15         run: cmd
16     };
17     for (const prop in options) {
18         passedOptions[prop] = options[prop];
19     }
20     if ((!options.enable || options.enable === true) && !options.select) {
21         passedOptions[options.enable ? "enable" : "select"] = function (state) {
22             return cmd(state);
23         };
24     }
25
26     return new MenuItem(passedOptions)
27 }
28
29 function markActive(state, type) {
30     const ref = state.selection;
31     const from = ref.from;
32     const $from = ref.$from;
33     const to = ref.to;
34     const empty = ref.empty;
35     if (empty) {
36         return type.isInSet(state.storedMarks || $from.marks())
37     } else {
38         return state.doc.rangeHasMark(from, to, type)
39     }
40 }
41
42 function markItem(markType, options) {
43     const passedOptions = {
44         active: function active(state) {
45             return markActive(state, markType)
46         },
47         enable: true
48     };
49     for (const prop in options) {
50         passedOptions[prop] = options[prop];
51     }
52
53     return cmdItem(toggleMark(markType, passedOptions.attrs), passedOptions)
54 }
55
56 const inlineStyles = [
57     markItem(schema.marks.strong, {title: "Bold", icon: icons.strong}),
58     markItem(schema.marks.em, {title: "Italic", icon: icons.em}),
59     markItem(schema.marks.underline, {title: "Underline", icon: icons.underline}),
60     markItem(schema.marks.strike, {title: "Strikethrough", icon: icons.strike}),
61     markItem(schema.marks.superscript, {title: "Superscript", icon: icons.superscript}),
62     markItem(schema.marks.subscript, {title: "Subscript", icon: icons.subscript}),
63 ];
64
65 const formats = [
66     blockTypeItem(schema.nodes.heading, {
67         label: "Header Large",
68         attrs: {level: 2}
69     }),
70     blockTypeItem(schema.nodes.heading, {
71         label: "Header Medium",
72         attrs: {level: 3}
73     }),
74     blockTypeItem(schema.nodes.heading, {
75         label: "Header Small",
76         attrs: {level: 4}
77     }),
78     blockTypeItem(schema.nodes.heading, {
79         label: "Header Tiny",
80         attrs: {level: 5}
81     }),
82     blockTypeItem(schema.nodes.paragraph, {
83         label: "Paragraph",
84         attrs: {}
85     }),
86     new DropdownSubmenu([
87         blockTypeItem(schema.nodes.callout, {
88             label: "Info Callout",
89             attrs: {type: 'info'}
90         }),
91         blockTypeItem(schema.nodes.callout, {
92             label: "Danger Callout",
93             attrs: {type: 'danger'}
94         }),
95         blockTypeItem(schema.nodes.callout, {
96             label: "Success Callout",
97             attrs: {type: 'success'}
98         }),
99         blockTypeItem(schema.nodes.callout, {
100             label: "Warning Callout",
101             attrs: {type: 'warning'}
102         })
103     ], { label: 'Callouts' }),
104 ];
105
106 const alignments = [
107     setAttrItem('align', 'left', {
108         icon: icons.align_left
109     }),
110     setAttrItem('align', 'center', {
111         icon: icons.align_center
112     }),
113     setAttrItem('align', 'right', {
114         icon: icons.align_right
115     }),
116     setAttrItem('align', 'justify', {
117         icon: icons.align_justify
118     }),
119 ];
120
121 const colorOptions = ["#000000","#993300","#333300","#003300","#003366","#000080","#333399","#333333","#800000","#FF6600","#808000","#008000","#008080","#0000FF","#666699","#808080","#FF0000","#FF9900","#99CC00","#339966","#33CCCC","#3366FF","#800080","#999999","#FF00FF","#FFCC00","#FFFF00","#00FF00","#00FFFF","#00CCFF","#993366","#FFFFFF","#FF99CC","#FFCC99","#FFFF99","#CCFFCC","#CCFFFF","#99CCFF","#CC99FF"];
122
123 const colors = [
124     new DropdownSubmenu([
125         new ColorPickerGrid(schema.marks.text_color, 'color', colorOptions),
126     ], {icon: icons.text_color}),
127     new DropdownSubmenu([
128         new ColorPickerGrid(schema.marks.background_color, 'color', colorOptions),
129     ], {icon: icons.background_color}),
130 ];
131
132 const lists = [
133     wrapItem(schema.nodes.bullet_list, {
134         title: "Bullet List",
135         icon: icons.bullet_list,
136     }),
137     wrapItem(schema.nodes.ordered_list, {
138         title: "Ordered List",
139         icon: icons.ordered_list,
140     }),
141 ];
142
143 const menu = menuBar({
144     floating: false,
145     content: [
146         [undoItem, redoItem],
147         [new DropdownSubmenu(formats, { label: 'Formats' })],
148         inlineStyles,
149         colors,
150         alignments,
151         lists,
152     ],
153 });
154
155 export default menu;
156
157 // !! This module defines a number of building blocks for ProseMirror
158 // menus, along with a [menu bar](#menu.menuBar) implementation.
159
160 // MenuElement:: interface
161 // The types defined in this module aren't the only thing you can
162 // display in your menu. Anything that conforms to this interface can
163 // be put into a menu structure.
164 //
165 //   render:: (pm: EditorView) → {dom: dom.Node, update: (EditorState) → bool}
166 //   Render the element for display in the menu. Must return a DOM
167 //   element and a function that can be used to update the element to
168 //   a new state. The `update` function will return false if the
169 //   update hid the entire element.