@kerebron/extension-menu 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +3 -0
- package/esm/ExtensionMenu.d.ts +10 -0
- package/esm/ExtensionMenu.d.ts.map +1 -0
- package/esm/ExtensionMenu.js +302 -0
- package/esm/MenuPlugin.d.ts +9 -0
- package/esm/MenuPlugin.d.ts.map +1 -0
- package/esm/MenuPlugin.js +247 -0
- package/esm/_dnt.shims.d.ts +6 -0
- package/esm/_dnt.shims.d.ts.map +1 -0
- package/esm/_dnt.shims.js +61 -0
- package/esm/icons.d.ts +15 -0
- package/esm/icons.d.ts.map +1 -0
- package/esm/icons.js +118 -0
- package/esm/menu.d.ts +86 -0
- package/esm/menu.d.ts.map +1 -0
- package/esm/menu.js +354 -0
- package/esm/package.json +3 -0
- package/esm/prompt.d.ts +36 -0
- package/esm/prompt.d.ts.map +1 -0
- package/esm/prompt.js +158 -0
- package/package.json +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Permission is hereby granted, free of charge, to any
|
|
2
|
+
person obtaining a copy of this software and associated
|
|
3
|
+
documentation files (the "Software"), to deal in the
|
|
4
|
+
Software without restriction, including without
|
|
5
|
+
limitation the rights to use, copy, modify, merge,
|
|
6
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
the Software, and to permit persons to whom the Software
|
|
8
|
+
is furnished to do so, subject to the following
|
|
9
|
+
conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
shall be included in all copies or substantial portions
|
|
13
|
+
of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
17
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
18
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
19
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
22
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23
|
+
DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MenuElement } from './menu.js';
|
|
2
|
+
import { Plugin } from 'prosemirror-state';
|
|
3
|
+
import { Schema } from 'prosemirror-model';
|
|
4
|
+
import { type CoreEditor, Extension } from '@kerebron/editor';
|
|
5
|
+
export declare function buildMenu(editor: CoreEditor, schema: Schema): MenuElement[][];
|
|
6
|
+
export declare class ExtensionMenu extends Extension {
|
|
7
|
+
name: string;
|
|
8
|
+
getProseMirrorPlugins(editor: CoreEditor, schema: Schema): Plugin[];
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ExtensionMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExtensionMenu.d.ts","sourceRoot":"","sources":["../src/ExtensionMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EAIZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAuC,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAsB,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAuD9D,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,EAAE,CA0R7E;AAED,qBAAa,aAAc,SAAQ,SAAS;IAC1C,IAAI,SAAU;IAEL,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;CAQ7E"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { blockTypeItem, Dropdown, DropdownSubmenu, MenuItem, wrapItem, } from './menu.js';
|
|
2
|
+
import { NodeSelection } from 'prosemirror-state';
|
|
3
|
+
import { Extension } from '@kerebron/editor';
|
|
4
|
+
import { toggleMark, wrapInList } from '@kerebron/editor/commands';
|
|
5
|
+
import { MenuPlugin } from './MenuPlugin.js';
|
|
6
|
+
import { icons } from './icons.js';
|
|
7
|
+
import { openPrompt, TextField } from './prompt.js';
|
|
8
|
+
function canInsert(state, nodeType) {
|
|
9
|
+
let $from = state.selection.$from;
|
|
10
|
+
for (let d = $from.depth; d >= 0; d--) {
|
|
11
|
+
let index = $from.index(d);
|
|
12
|
+
if ($from.node(d).canReplaceWith(index, index, nodeType))
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function cmdItem(cmd, options) {
|
|
18
|
+
let passedOptions = {
|
|
19
|
+
label: options.title,
|
|
20
|
+
run: cmd,
|
|
21
|
+
};
|
|
22
|
+
for (let prop in options) {
|
|
23
|
+
passedOptions[prop] = options[prop];
|
|
24
|
+
}
|
|
25
|
+
if (!options.enable && !options.select) {
|
|
26
|
+
passedOptions[options.enable ? 'enable' : 'select'] = (state) => cmd(state);
|
|
27
|
+
}
|
|
28
|
+
return new MenuItem(passedOptions);
|
|
29
|
+
}
|
|
30
|
+
function markActive(state, type) {
|
|
31
|
+
let { from, $from, to, empty } = state.selection;
|
|
32
|
+
if (empty)
|
|
33
|
+
return !!type.isInSet(state.storedMarks || $from.marks());
|
|
34
|
+
else
|
|
35
|
+
return state.doc.rangeHasMark(from, to, type);
|
|
36
|
+
}
|
|
37
|
+
function markItem(markType, options) {
|
|
38
|
+
let passedOptions = {
|
|
39
|
+
active(state) {
|
|
40
|
+
return markActive(state, markType);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
for (let prop in options) {
|
|
44
|
+
passedOptions[prop] = options[prop];
|
|
45
|
+
}
|
|
46
|
+
return cmdItem(toggleMark(markType), passedOptions);
|
|
47
|
+
}
|
|
48
|
+
function wrapListItem(nodeType, options) {
|
|
49
|
+
return cmdItem(wrapInList(nodeType, options.attrs), options);
|
|
50
|
+
}
|
|
51
|
+
const cut = (arr) => arr.filter((x) => x);
|
|
52
|
+
export function buildMenu(editor, schema) {
|
|
53
|
+
const menu = [];
|
|
54
|
+
if (schema.marks.strong) {
|
|
55
|
+
menu.push(new MenuItem({
|
|
56
|
+
title: 'Toggle strong assets',
|
|
57
|
+
run: () => editor.chain().toggleStrong().run(),
|
|
58
|
+
enable: (state) => editor.can().toggleStrong().run(),
|
|
59
|
+
icon: icons.strong,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
if (schema.marks.em) {
|
|
63
|
+
menu.push(new MenuItem({
|
|
64
|
+
title: 'Toggle emphasis',
|
|
65
|
+
run: () => editor.chain().toggleItalic().run(),
|
|
66
|
+
enable: (state) => editor.can().toggleItalic().run(),
|
|
67
|
+
icon: icons.em,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
if (schema.marks.underline) {
|
|
71
|
+
menu.push(new MenuItem({
|
|
72
|
+
title: 'Toggle underline',
|
|
73
|
+
label: '_',
|
|
74
|
+
run: () => editor.chain().toggleUnderline().run(),
|
|
75
|
+
enable: (state) => editor.can().toggleUnderline().run(),
|
|
76
|
+
// icon: icons.underline
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
if (schema.marks.code) {
|
|
80
|
+
menu.push(markItem(schema.marks.code, {
|
|
81
|
+
title: 'Toggle code font',
|
|
82
|
+
icon: icons.code,
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
if (schema.marks.link) {
|
|
86
|
+
const markType = schema.marks.link;
|
|
87
|
+
menu.push(new MenuItem({
|
|
88
|
+
title: 'Add or remove link',
|
|
89
|
+
icon: icons.link,
|
|
90
|
+
active(state) {
|
|
91
|
+
return markActive(state, markType);
|
|
92
|
+
},
|
|
93
|
+
enable(state) {
|
|
94
|
+
return !state.selection.empty;
|
|
95
|
+
},
|
|
96
|
+
run(state, dispatch, view) {
|
|
97
|
+
if (markActive(state, markType)) {
|
|
98
|
+
toggleMark(markType)(state, dispatch);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
openPrompt({
|
|
102
|
+
title: 'Create a link',
|
|
103
|
+
fields: {
|
|
104
|
+
href: new TextField({
|
|
105
|
+
label: 'Link target',
|
|
106
|
+
required: true,
|
|
107
|
+
}),
|
|
108
|
+
title: new TextField({ label: 'Title' }),
|
|
109
|
+
},
|
|
110
|
+
callback(attrs) {
|
|
111
|
+
toggleMark(markType, attrs)(view.state, view.dispatch);
|
|
112
|
+
view.focus();
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
const blockMenu = [];
|
|
119
|
+
const insertMenu = [];
|
|
120
|
+
const typeMenu = [];
|
|
121
|
+
if (schema.nodes.bullet_list) {
|
|
122
|
+
blockMenu.push(wrapListItem(schema.nodes.bullet_list, {
|
|
123
|
+
title: 'Wrap in bullet list',
|
|
124
|
+
icon: icons.bulletList,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
if (schema.nodes.ordered_list) {
|
|
128
|
+
blockMenu.push(wrapListItem(schema.nodes.ordered_list, {
|
|
129
|
+
title: 'Wrap in ordered list',
|
|
130
|
+
icon: icons.orderedList,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
if (schema.nodes.blockquote) {
|
|
134
|
+
blockMenu.push(wrapItem(schema.nodes.blockquote, {
|
|
135
|
+
title: 'Wrap in block quote',
|
|
136
|
+
icon: icons.blockquote,
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
if (schema.nodes.paragraph) {
|
|
140
|
+
typeMenu.push(blockTypeItem(schema.nodes.paragraph, {
|
|
141
|
+
title: 'Change to paragraph',
|
|
142
|
+
label: 'Plain',
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
if (schema.nodes.code_block) {
|
|
146
|
+
typeMenu.push(blockTypeItem(schema.nodes.code_block, {
|
|
147
|
+
title: 'Change to code block',
|
|
148
|
+
label: 'Code',
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (schema.nodes.heading) {
|
|
152
|
+
const makeHeadMenu = [];
|
|
153
|
+
for (let i = 1; i <= 6; i++) {
|
|
154
|
+
makeHeadMenu.push(blockTypeItem(schema.nodes.heading, {
|
|
155
|
+
title: 'Change to heading ' + i,
|
|
156
|
+
label: 'Level ' + i,
|
|
157
|
+
attrs: { level: i },
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
typeMenu.push(new DropdownSubmenu(makeHeadMenu, { label: 'Heading' }));
|
|
161
|
+
}
|
|
162
|
+
blockMenu.push(new MenuItem({
|
|
163
|
+
title: 'Join with above block',
|
|
164
|
+
run: () => editor.chain().joinUp().run(),
|
|
165
|
+
select: () => editor.can().joinUp().run(),
|
|
166
|
+
icon: icons.join,
|
|
167
|
+
}));
|
|
168
|
+
blockMenu.push(new MenuItem({
|
|
169
|
+
title: 'Lift out of enclosing block',
|
|
170
|
+
run: () => editor.chain().lift().run(),
|
|
171
|
+
select: () => editor.can().lift().run(),
|
|
172
|
+
icon: icons.lift,
|
|
173
|
+
}));
|
|
174
|
+
blockMenu.push(new MenuItem({
|
|
175
|
+
title: 'Select parent node',
|
|
176
|
+
run: () => editor.chain().selectParentNode().run(),
|
|
177
|
+
select: () => editor.can().selectParentNode().run(),
|
|
178
|
+
icon: icons.selectParentNode,
|
|
179
|
+
}));
|
|
180
|
+
if (schema.nodes.image) {
|
|
181
|
+
const nodeType = schema.nodes.image;
|
|
182
|
+
insertMenu.push(new MenuItem({
|
|
183
|
+
title: 'Insert image',
|
|
184
|
+
label: 'Image',
|
|
185
|
+
// enable: (state) => editor.can().setHorizontalRule().run(),
|
|
186
|
+
enable: (state) => canInsert(state, nodeType),
|
|
187
|
+
run(state, _, view) {
|
|
188
|
+
let { from, to } = state.selection, attrs = null;
|
|
189
|
+
if (state.selection instanceof NodeSelection &&
|
|
190
|
+
state.selection.node.type == nodeType) {
|
|
191
|
+
attrs = state.selection.node.attrs;
|
|
192
|
+
}
|
|
193
|
+
openPrompt({
|
|
194
|
+
title: 'Insert image',
|
|
195
|
+
fields: {
|
|
196
|
+
src: new TextField({
|
|
197
|
+
label: 'Location',
|
|
198
|
+
required: true,
|
|
199
|
+
value: attrs && attrs.src,
|
|
200
|
+
}),
|
|
201
|
+
title: new TextField({
|
|
202
|
+
label: 'Title',
|
|
203
|
+
value: attrs && attrs.title,
|
|
204
|
+
}),
|
|
205
|
+
alt: new TextField({
|
|
206
|
+
label: 'Description',
|
|
207
|
+
value: attrs ? attrs.alt : state.doc.textBetween(from, to, ' '),
|
|
208
|
+
}),
|
|
209
|
+
},
|
|
210
|
+
callback(attrs) {
|
|
211
|
+
view.dispatch(view.state.tr.replaceSelectionWith(nodeType.createAndFill(attrs)));
|
|
212
|
+
view.focus();
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
if (schema.nodes.hr) {
|
|
219
|
+
insertMenu.push(new MenuItem({
|
|
220
|
+
title: 'Insert horizontal rule',
|
|
221
|
+
label: 'Horizontal rule',
|
|
222
|
+
run: () => editor.chain().setHorizontalRule().run(),
|
|
223
|
+
enable: (state) => editor.can().setHorizontalRule().run(),
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
menu.push(new Dropdown(cut(insertMenu), {
|
|
227
|
+
label: 'Insert',
|
|
228
|
+
}));
|
|
229
|
+
menu.push(new Dropdown(cut(typeMenu), {
|
|
230
|
+
label: 'Type...',
|
|
231
|
+
}));
|
|
232
|
+
/*
|
|
233
|
+
r.blockMenu = [
|
|
234
|
+
cut([
|
|
235
|
+
r.wrapBulletList,
|
|
236
|
+
r.wrapOrderedList,
|
|
237
|
+
r.wrapBlockQuote,
|
|
238
|
+
joinUpItem,
|
|
239
|
+
liftItem,
|
|
240
|
+
selectParentNodeItem,
|
|
241
|
+
]),
|
|
242
|
+
];
|
|
243
|
+
r.fullMenu = r.inlineMenu.concat([[r.insertMenu, r.typeMenu]], [[
|
|
244
|
+
undoItem,
|
|
245
|
+
redoItem,
|
|
246
|
+
]], r.blockMenu);
|
|
247
|
+
*/
|
|
248
|
+
menu.push(new MenuItem({
|
|
249
|
+
title: 'Undo last change',
|
|
250
|
+
run: () => editor.chain().undo().run(),
|
|
251
|
+
enable: () => editor.can().undo().run(),
|
|
252
|
+
icon: icons.undo,
|
|
253
|
+
}));
|
|
254
|
+
menu.push(new MenuItem({
|
|
255
|
+
title: 'Redo last undone change',
|
|
256
|
+
run: () => editor.chain().redo().run(),
|
|
257
|
+
enable: () => editor.can().redo().run(),
|
|
258
|
+
icon: icons.redo,
|
|
259
|
+
}));
|
|
260
|
+
/*
|
|
261
|
+
function item(label: string, cmd: (state: EditorState) => boolean) {
|
|
262
|
+
return new MenuItem({ label, select: cmd, run: cmd });
|
|
263
|
+
}
|
|
264
|
+
const tableMenu = [
|
|
265
|
+
item('Insert column before', addColumnBefore),
|
|
266
|
+
item('Insert column after', addColumnAfter),
|
|
267
|
+
item('Delete column', deleteColumn),
|
|
268
|
+
item('Insert row before', addRowBefore),
|
|
269
|
+
item('Insert row after', addRowAfter),
|
|
270
|
+
item('Delete row', deleteRow),
|
|
271
|
+
item('Delete table', deleteTable),
|
|
272
|
+
item('Merge cells', mergeCells),
|
|
273
|
+
item('Split cell', splitCell),
|
|
274
|
+
item('Toggle header column', toggleHeaderColumn),
|
|
275
|
+
item('Toggle header row', toggleHeaderRow),
|
|
276
|
+
item('Toggle header cells', toggleHeaderCell),
|
|
277
|
+
item('Make cell green', setCellAttr('background', '#dfd')),
|
|
278
|
+
item('Make cell not-green', setCellAttr('background', null)),
|
|
279
|
+
];
|
|
280
|
+
menu.splice(2, 0, [new Dropdown(tableMenu, { label: 'Table' })]);
|
|
281
|
+
*/
|
|
282
|
+
return [menu, blockMenu];
|
|
283
|
+
}
|
|
284
|
+
export class ExtensionMenu extends Extension {
|
|
285
|
+
constructor() {
|
|
286
|
+
super(...arguments);
|
|
287
|
+
Object.defineProperty(this, "name", {
|
|
288
|
+
enumerable: true,
|
|
289
|
+
configurable: true,
|
|
290
|
+
writable: true,
|
|
291
|
+
value: 'menu'
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
getProseMirrorPlugins(editor, schema) {
|
|
295
|
+
return [
|
|
296
|
+
new MenuPlugin({
|
|
297
|
+
content: buildMenu(editor, schema),
|
|
298
|
+
floating: true,
|
|
299
|
+
}),
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Plugin } from 'prosemirror-state';
|
|
2
|
+
import { MenuElement } from './menu.js';
|
|
3
|
+
export declare class MenuPlugin extends Plugin {
|
|
4
|
+
constructor(options: {
|
|
5
|
+
content: readonly (readonly MenuElement[])[];
|
|
6
|
+
floating?: boolean;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=MenuPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuPlugin.d.ts","sourceRoot":"","sources":["../src/MenuPlugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,MAAM,EAAa,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAiB,MAAM,WAAW,CAAC;AA0LvD,qBAAa,UAAW,SAAQ,MAAM;gBACxB,OAAO,EAAE;QAGnB,OAAO,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,CAAC;QAK7C,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;CAOF"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
2
|
+
import { Plugin } from 'prosemirror-state';
|
|
3
|
+
import { renderGrouped } from './menu.js';
|
|
4
|
+
const prefix = 'ProseMirror-menubar';
|
|
5
|
+
function isIOS() {
|
|
6
|
+
if (typeof navigator == 'undefined')
|
|
7
|
+
return false;
|
|
8
|
+
let agent = navigator.userAgent;
|
|
9
|
+
return !/Edge\/\d/.test(agent) && /AppleWebKit/.test(agent) &&
|
|
10
|
+
/Mobile\/\w+/.test(agent);
|
|
11
|
+
}
|
|
12
|
+
class MenuBarView {
|
|
13
|
+
constructor(editorView, options) {
|
|
14
|
+
Object.defineProperty(this, "editorView", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: editorView
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "options", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: options
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "wrapper", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "menu", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "spacer", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: null
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "maxHeight", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: 0
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "widthForMaxHeight", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: 0
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "floating", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: false
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "contentUpdate", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "scrollHandler", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: null
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(this, "root", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
value: void 0
|
|
79
|
+
});
|
|
80
|
+
this.root = editorView.root;
|
|
81
|
+
this.wrapper = document.createElement('div');
|
|
82
|
+
this.wrapper.classList.add(prefix + '-wrapper');
|
|
83
|
+
this.menu = document.createElement('div');
|
|
84
|
+
this.menu.classList.add(prefix);
|
|
85
|
+
this.wrapper.appendChild(this.menu);
|
|
86
|
+
this.menu.className = prefix;
|
|
87
|
+
if (editorView.dom.parentNode) {
|
|
88
|
+
editorView.dom.parentNode.replaceChild(this.wrapper, editorView.dom);
|
|
89
|
+
}
|
|
90
|
+
this.wrapper.appendChild(editorView.dom);
|
|
91
|
+
let { dom, update } = renderGrouped(this.editorView, this.options.content);
|
|
92
|
+
this.contentUpdate = update;
|
|
93
|
+
this.menu.appendChild(dom);
|
|
94
|
+
this.update();
|
|
95
|
+
if (options.floating && !isIOS()) {
|
|
96
|
+
this.updateFloat();
|
|
97
|
+
let potentialScrollers = getAllWrapping(this.wrapper);
|
|
98
|
+
this.scrollHandler = (e) => {
|
|
99
|
+
let root = this.editorView.root;
|
|
100
|
+
if (!(root.body || root).contains(this.wrapper)) {
|
|
101
|
+
potentialScrollers.forEach((el) => el.removeEventListener('scroll', this.scrollHandler));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.updateFloat(e.target.getBoundingClientRect
|
|
105
|
+
? e.target
|
|
106
|
+
: undefined);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
potentialScrollers.forEach((el) => el.addEventListener('scroll', this.scrollHandler));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
update() {
|
|
113
|
+
if (this.editorView.root != this.root) {
|
|
114
|
+
let { dom, update } = renderGrouped(this.editorView, this.options.content);
|
|
115
|
+
this.contentUpdate = update;
|
|
116
|
+
this.menu.replaceChild(dom, this.menu.firstChild);
|
|
117
|
+
this.root = this.editorView.root;
|
|
118
|
+
}
|
|
119
|
+
// this.contentUpdate(this.editorView.state);
|
|
120
|
+
if (this.floating) {
|
|
121
|
+
this.updateScrollCursor();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
if (this.menu.offsetWidth != this.widthForMaxHeight) {
|
|
125
|
+
this.widthForMaxHeight = this.menu.offsetWidth;
|
|
126
|
+
this.maxHeight = 0;
|
|
127
|
+
}
|
|
128
|
+
if (this.menu.offsetHeight > this.maxHeight) {
|
|
129
|
+
this.maxHeight = this.menu.offsetHeight;
|
|
130
|
+
this.menu.style.minHeight = this.maxHeight + 'px';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
updateScrollCursor() {
|
|
135
|
+
let selection = this.editorView.root.getSelection();
|
|
136
|
+
if (!selection.focusNode)
|
|
137
|
+
return;
|
|
138
|
+
let rects = selection.getRangeAt(0).getClientRects();
|
|
139
|
+
let selRect = rects[selectionIsInverted(selection) ? 0 : rects.length - 1];
|
|
140
|
+
if (!selRect)
|
|
141
|
+
return;
|
|
142
|
+
let menuRect = this.menu.getBoundingClientRect();
|
|
143
|
+
if (selRect.top < menuRect.bottom && selRect.bottom > menuRect.top) {
|
|
144
|
+
let scrollable = findWrappingScrollable(this.wrapper);
|
|
145
|
+
if (scrollable)
|
|
146
|
+
scrollable.scrollTop -= menuRect.bottom - selRect.top;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
updateFloat(scrollAncestor) {
|
|
150
|
+
let parent = this.wrapper, editorRect = parent.getBoundingClientRect(), top = scrollAncestor
|
|
151
|
+
? Math.max(0, scrollAncestor.getBoundingClientRect().top)
|
|
152
|
+
: 0;
|
|
153
|
+
if (this.floating) {
|
|
154
|
+
if (editorRect.top >= top || editorRect.bottom < this.menu.offsetHeight + 10) {
|
|
155
|
+
this.floating = false;
|
|
156
|
+
this.menu.style.position =
|
|
157
|
+
this.menu.style.left =
|
|
158
|
+
this.menu.style.top =
|
|
159
|
+
this.menu.style.width =
|
|
160
|
+
'';
|
|
161
|
+
this.menu.style.display = '';
|
|
162
|
+
this.spacer.parentNode.removeChild(this.spacer);
|
|
163
|
+
this.spacer = null;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
let border = (parent.offsetWidth - parent.clientWidth) / 2;
|
|
167
|
+
this.menu.style.left = (editorRect.left + border) + 'px';
|
|
168
|
+
this.menu.style.display = editorRect.top >
|
|
169
|
+
(this.editorView.dom.ownerDocument.defaultView || dntShim.dntGlobalThis)
|
|
170
|
+
.innerHeight
|
|
171
|
+
? 'none'
|
|
172
|
+
: '';
|
|
173
|
+
if (scrollAncestor)
|
|
174
|
+
this.menu.style.top = top + 'px';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
if (editorRect.top < top && editorRect.bottom >= this.menu.offsetHeight + 10) {
|
|
179
|
+
this.floating = true;
|
|
180
|
+
let menuRect = this.menu.getBoundingClientRect();
|
|
181
|
+
this.menu.style.left = menuRect.left + 'px';
|
|
182
|
+
this.menu.style.width = menuRect.width + 'px';
|
|
183
|
+
if (scrollAncestor)
|
|
184
|
+
this.menu.style.top = top + 'px';
|
|
185
|
+
this.menu.style.position = 'fixed';
|
|
186
|
+
this.spacer = document.createElement('div');
|
|
187
|
+
this.spacer.classList.add(prefix + '-spacer');
|
|
188
|
+
this.spacer.style.height = `${menuRect.height}px`;
|
|
189
|
+
parent.insertBefore(this.spacer, this.menu);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
destroy() {
|
|
194
|
+
if (this.wrapper.parentNode) {
|
|
195
|
+
this.wrapper.parentNode.replaceChild(this.editorView.dom, this.wrapper);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Not precise, but close enough
|
|
200
|
+
function selectionIsInverted(selection) {
|
|
201
|
+
if (selection.anchorNode == selection.focusNode) {
|
|
202
|
+
return selection.anchorOffset > selection.focusOffset;
|
|
203
|
+
}
|
|
204
|
+
return selection.anchorNode.compareDocumentPosition(selection.focusNode) ==
|
|
205
|
+
Node.DOCUMENT_POSITION_FOLLOWING;
|
|
206
|
+
}
|
|
207
|
+
function findWrappingScrollable(node) {
|
|
208
|
+
for (let cur = node.parentNode; cur; cur = cur.parentNode) {
|
|
209
|
+
if (cur.scrollHeight > cur.clientHeight) {
|
|
210
|
+
return cur;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function getAllWrapping(node) {
|
|
215
|
+
let res = [node.ownerDocument.defaultView || dntShim.dntGlobalThis];
|
|
216
|
+
for (let cur = node.parentNode; cur; cur = cur.parentNode) {
|
|
217
|
+
res.push(cur);
|
|
218
|
+
}
|
|
219
|
+
return res;
|
|
220
|
+
}
|
|
221
|
+
export class MenuPlugin extends Plugin {
|
|
222
|
+
constructor(options) {
|
|
223
|
+
super({
|
|
224
|
+
view(editorView) {
|
|
225
|
+
return new MenuBarView(editorView, options);
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/*
|
|
231
|
+
/// A plugin that will place a menu bar above the editor. Note that
|
|
232
|
+
/// this involves wrapping the editor in an additional `<div>`.
|
|
233
|
+
export function menuBar(options: {
|
|
234
|
+
/// Provides the content of the menu, as a nested array to be
|
|
235
|
+
/// passed to `renderGrouped`.
|
|
236
|
+
content: readonly (readonly MenuElement[])[]
|
|
237
|
+
|
|
238
|
+
/// Determines whether the menu floats, i.e. whether it sticks to
|
|
239
|
+
/// the top of the viewport when the editor is partially scrolled
|
|
240
|
+
/// out of view.
|
|
241
|
+
floating?: boolean
|
|
242
|
+
}): Plugin {
|
|
243
|
+
return new Plugin({
|
|
244
|
+
view(editorView) { return new MenuBarView(editorView, options) }
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAKvC,eAAO,MAAM,aAAa;;CAA2C,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Deno } from "@deno/shim-deno";
|
|
2
|
+
export { Deno } from "@deno/shim-deno";
|
|
3
|
+
const dntGlobals = {
|
|
4
|
+
Deno,
|
|
5
|
+
};
|
|
6
|
+
export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
7
|
+
function createMergeProxy(baseObj, extObj) {
|
|
8
|
+
return new Proxy(baseObj, {
|
|
9
|
+
get(_target, prop, _receiver) {
|
|
10
|
+
if (prop in extObj) {
|
|
11
|
+
return extObj[prop];
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return baseObj[prop];
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
set(_target, prop, value) {
|
|
18
|
+
if (prop in extObj) {
|
|
19
|
+
delete extObj[prop];
|
|
20
|
+
}
|
|
21
|
+
baseObj[prop] = value;
|
|
22
|
+
return true;
|
|
23
|
+
},
|
|
24
|
+
deleteProperty(_target, prop) {
|
|
25
|
+
let success = false;
|
|
26
|
+
if (prop in extObj) {
|
|
27
|
+
delete extObj[prop];
|
|
28
|
+
success = true;
|
|
29
|
+
}
|
|
30
|
+
if (prop in baseObj) {
|
|
31
|
+
delete baseObj[prop];
|
|
32
|
+
success = true;
|
|
33
|
+
}
|
|
34
|
+
return success;
|
|
35
|
+
},
|
|
36
|
+
ownKeys(_target) {
|
|
37
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
38
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
39
|
+
const extKeysSet = new Set(extKeys);
|
|
40
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
41
|
+
},
|
|
42
|
+
defineProperty(_target, prop, desc) {
|
|
43
|
+
if (prop in extObj) {
|
|
44
|
+
delete extObj[prop];
|
|
45
|
+
}
|
|
46
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
47
|
+
return true;
|
|
48
|
+
},
|
|
49
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
50
|
+
if (prop in extObj) {
|
|
51
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
has(_target, prop) {
|
|
58
|
+
return prop in extObj || prop in baseObj;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|