]> BookStack Code Mirror - bookstack/blob - resources/js/editor/menu/item-iframe-button.js
Added support for iframe node blocks
[bookstack] / resources / js / editor / menu / item-iframe-button.js
1 import DialogBox from "./DialogBox";
2 import DialogForm from "./DialogForm";
3 import DialogInput from "./DialogInput";
4 import schema from "../schema";
5
6 import {MenuItem} from "./menu";
7 import {icons} from "./icons";
8 import {nullifyEmptyValues} from "../util";
9
10 /**
11  * @param {PmNodeType} nodeType
12  * @param {String} attribute
13  * @return {(function(PmEditorState): (string|null))}
14  */
15 function getNodeAttribute(nodeType, attribute) {
16     return function (state) {
17         const node = state.selection.node;
18         if (node && node.type === nodeType) {
19             return node.attrs[attribute];
20         }
21
22         return null;
23     };
24 }
25
26 /**
27  * @param {(function(FormData))} submitter
28  * @param {Function} closer
29  * @return {DialogBox}
30  */
31 function getLinkDialog(submitter, closer) {
32     return new DialogBox([
33         new DialogForm([
34             new DialogInput({
35                 label: 'Source URL',
36                 id: 'src',
37                 value: getNodeAttribute(schema.nodes.iframe, 'src'),
38             }),
39             new DialogInput({
40                 label: 'Hover Label',
41                 id: 'title',
42                 value: getNodeAttribute(schema.nodes.iframe, 'title'),
43             }),
44             new DialogInput({
45                 label: 'Width',
46                 id: 'width',
47                 value: getNodeAttribute(schema.nodes.iframe, 'width'),
48             }),
49             new DialogInput({
50                 label: 'Height',
51                 id: 'height',
52                 value: getNodeAttribute(schema.nodes.iframe, 'height'),
53             }),
54         ], {
55             canceler: closer,
56             action: submitter,
57         }),
58     ], {
59         label: 'Insert Embedded Content',
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 applyIframe(formData, state, dispatch) {
71     const attrs = nullifyEmptyValues(Object.fromEntries(formData));
72     if (!dispatch) return true;
73
74     const tr = state.tr;
75     const currentNodeAttrs = state.selection?.nodes?.attrs || {};
76     const newAttrs = Object.assign({}, currentNodeAttrs, attrs);
77     tr.replaceSelectionWith(schema.nodes.iframe.create(newAttrs));
78
79     dispatch(tr);
80     return true;
81 }
82
83 /**
84  * @param {PmEditorState} state
85  * @param {PmDispatchFunction} dispatch
86  * @param {PmView} view
87  * @param {Event} e
88  */
89 function onPress(state, dispatch, view, e) {
90     const dialog = getLinkDialog((data) => {
91         applyIframe(data, state, dispatch);
92         dom.remove();
93     }, () => {
94         dom.remove();
95     })
96
97     const {dom, update} = dialog.render(view);
98     update(state);
99     document.body.appendChild(dom);
100 }
101
102 /**
103  * @return {MenuItem}
104  */
105 function iframeButtonItem() {
106     return new MenuItem({
107         title: "Embed Content",
108         run: onPress,
109         enable: state => true,
110         icon: icons.iframe,
111     });
112 }
113
114 export default iframeButtonItem;