]> BookStack Code Mirror - bookstack/blob - resources/js/editor/menu/item-anchor-button.js
Added radio options for anchor target option
[bookstack] / resources / js / editor / menu / item-anchor-button.js
1 import DialogBox from "./DialogBox";
2 import DialogForm from "./DialogForm";
3 import DialogInput from "./DialogInput";
4 import DialogRadioOptions from "./DialogRadioOptions";
5 import schema from "../schema";
6
7 import {MenuItem} from "./menu";
8 import {icons} from "./icons";
9
10 /**
11  * @param {PmMarkType} markType
12  * @param {String} attribute
13  * @return {(function(PmEditorState): (string|null))}
14  */
15 function getMarkAttribute(markType, attribute) {
16     return function (state) {
17         const marks = state.selection.$head.marks();
18         for (const mark of marks) {
19             if (mark.type === markType) {
20                 return mark.attrs[attribute];
21             }
22         }
23
24         return null;
25     };
26 }
27
28 /**
29  * @param {(function(FormData))} submitter
30  * @param {Function} closer
31  * @return {DialogBox}
32  */
33 function getLinkDialog(submitter, closer) {
34     return new DialogBox([
35         new DialogForm([
36             new DialogInput({
37                 label: 'URL',
38                 id: 'href',
39                 value: getMarkAttribute(schema.marks.link, 'href'),
40             }),
41             new DialogInput({
42                 label: 'Hover Label',
43                 id: 'title',
44                 value: getMarkAttribute(schema.marks.link, 'title'),
45             }),
46             new DialogRadioOptions({
47                 "Same tab or window": "",
48                 "New tab or window": "_blank",
49             },{
50                 label: 'Behaviour',
51                 id: 'target',
52                 value: getMarkAttribute(schema.marks.link, 'target'),
53             })
54         ], {
55             canceler: closer,
56             action: submitter,
57         }),
58     ], {
59         label: 'Insert Link',
60         closer: closer,
61     });
62 }
63
64 /**
65  * @param {FormData} formData
66  * @param {PmEditorState} state
67  * @param {PmDispatchFunction} dispatch
68  * @return {boolean}
69  */
70 function applyLink(formData, state, dispatch) {
71     const selection = state.selection;
72     const attrs = Object.fromEntries(formData);
73     if (dispatch) {
74         const tr = state.tr;
75
76         if (attrs.href) {
77             tr.addMark(selection.from, selection.to, schema.marks.link.create(attrs));
78         } else {
79             tr.removeMark(selection.from, selection.to, schema.marks.link);
80         }
81         dispatch(tr);
82     }
83     return true;
84 }
85
86 /**
87  * @param {PmEditorState} state
88  * @param {PmDispatchFunction} dispatch
89  * @param {PmView} view
90  * @param {Event} e
91  */
92 function onPress(state, dispatch, view, e) {
93     const dialog = getLinkDialog((data) => {
94         applyLink(data, state, dispatch);
95         dom.remove();
96     }, () => {
97         dom.remove();
98     })
99
100     const {dom, update} = dialog.render(view);
101     update(state);
102     document.body.appendChild(dom);
103 }
104
105 /**
106  * @return {MenuItem}
107  */
108 function anchorButtonItem() {
109     return new MenuItem({
110         title: "Insert/Edit Anchor Link",
111         run: onPress,
112         enable: state => true,
113         icon: icons.link,
114     });
115 }
116
117 export default anchorButtonItem;