@kerebron/extension-menu-legacy 0.4.0
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 +82 -0
- package/assets/menu.css +539 -0
- package/esm/editor/src/CoreEditor.d.ts +40 -0
- package/esm/editor/src/CoreEditor.d.ts.map +1 -0
- package/esm/editor/src/CoreEditor.js +252 -0
- package/esm/editor/src/DummyEditorView.d.ts +60 -0
- package/esm/editor/src/DummyEditorView.d.ts.map +1 -0
- package/esm/editor/src/DummyEditorView.js +277 -0
- package/esm/editor/src/Extension.d.ts +31 -0
- package/esm/editor/src/Extension.d.ts.map +1 -0
- package/esm/editor/src/Extension.js +53 -0
- package/esm/editor/src/ExtensionManager.d.ts +32 -0
- package/esm/editor/src/ExtensionManager.d.ts.map +1 -0
- package/esm/editor/src/ExtensionManager.js +260 -0
- package/esm/editor/src/Mark.d.ts +23 -0
- package/esm/editor/src/Mark.d.ts.map +1 -0
- package/esm/editor/src/Mark.js +51 -0
- package/esm/editor/src/Node.d.ts +32 -0
- package/esm/editor/src/Node.d.ts.map +1 -0
- package/esm/editor/src/Node.js +60 -0
- package/esm/editor/src/commands/CommandManager.d.ts +23 -0
- package/esm/editor/src/commands/CommandManager.d.ts.map +1 -0
- package/esm/editor/src/commands/CommandManager.js +118 -0
- package/esm/editor/src/commands/baseCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/baseCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/baseCommandFactories.js +836 -0
- package/esm/editor/src/commands/createChainableState.d.ts +3 -0
- package/esm/editor/src/commands/createChainableState.d.ts.map +1 -0
- package/esm/editor/src/commands/createChainableState.js +29 -0
- package/esm/editor/src/commands/keyCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/keyCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/keyCommandFactories.js +10 -0
- package/esm/editor/src/commands/mod.d.ts +7 -0
- package/esm/editor/src/commands/mod.d.ts.map +1 -0
- package/esm/editor/src/commands/mod.js +76 -0
- package/esm/editor/src/commands/replaceCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/replaceCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/replaceCommandFactories.js +94 -0
- package/esm/editor/src/commands/types.d.ts +18 -0
- package/esm/editor/src/commands/types.d.ts.map +1 -0
- package/esm/editor/src/commands/types.js +1 -0
- package/esm/editor/src/mod.d.ts +9 -0
- package/esm/editor/src/mod.d.ts.map +1 -0
- package/esm/editor/src/mod.js +8 -0
- package/esm/editor/src/nodeToTreeString.d.ts +10 -0
- package/esm/editor/src/nodeToTreeString.d.ts.map +1 -0
- package/esm/editor/src/nodeToTreeString.js +74 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts +6 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts.map +1 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.js +24 -0
- package/esm/editor/src/plugins/input-rules/InputRulesPlugin.d.ts +23 -0
- package/esm/editor/src/plugins/input-rules/InputRulesPlugin.d.ts.map +1 -0
- package/esm/editor/src/plugins/input-rules/InputRulesPlugin.js +163 -0
- package/esm/editor/src/plugins/keymap/keymap.d.ts +11 -0
- package/esm/editor/src/plugins/keymap/keymap.d.ts.map +1 -0
- package/esm/editor/src/plugins/keymap/keymap.js +125 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts +4 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts.map +1 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.js +124 -0
- package/esm/editor/src/types.d.ts +52 -0
- package/esm/editor/src/types.d.ts.map +1 -0
- package/esm/editor/src/types.js +1 -0
- package/esm/editor/src/ui.d.ts +15 -0
- package/esm/editor/src/ui.d.ts.map +1 -0
- package/esm/editor/src/ui.js +16 -0
- package/esm/editor/src/utilities/SmartOutput.d.ts +41 -0
- package/esm/editor/src/utilities/SmartOutput.d.ts.map +1 -0
- package/esm/editor/src/utilities/SmartOutput.js +228 -0
- package/esm/editor/src/utilities/createNodeFromContent.d.ts +9 -0
- package/esm/editor/src/utilities/createNodeFromContent.d.ts.map +1 -0
- package/esm/editor/src/utilities/createNodeFromContent.js +32 -0
- package/esm/editor/src/utilities/getHtmlAttributes.d.ts +9 -0
- package/esm/editor/src/utilities/getHtmlAttributes.d.ts.map +1 -0
- package/esm/editor/src/utilities/getHtmlAttributes.js +47 -0
- package/esm/extension-menu-legacy/src/ExtensionMenuLegacy.d.ts +14 -0
- package/esm/extension-menu-legacy/src/ExtensionMenuLegacy.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/ExtensionMenuLegacy.js +32 -0
- package/esm/extension-menu-legacy/src/MenuPlugin.d.ts +9 -0
- package/esm/extension-menu-legacy/src/MenuPlugin.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/MenuPlugin.js +227 -0
- package/esm/extension-menu-legacy/src/buildMenu.d.ts +5 -0
- package/esm/extension-menu-legacy/src/buildMenu.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/buildMenu.js +331 -0
- package/esm/extension-menu-legacy/src/icons.d.ts +15 -0
- package/esm/extension-menu-legacy/src/icons.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/icons.js +118 -0
- package/esm/extension-menu-legacy/src/menu.d.ts +81 -0
- package/esm/extension-menu-legacy/src/menu.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/menu.js +350 -0
- package/esm/extension-menu-legacy/src/mod.d.ts +3 -0
- package/esm/extension-menu-legacy/src/mod.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/mod.js +2 -0
- package/esm/extension-menu-legacy/src/prompt.d.ts +36 -0
- package/esm/extension-menu-legacy/src/prompt.d.ts.map +1 -0
- package/esm/extension-menu-legacy/src/prompt.js +158 -0
- package/esm/package.json +3 -0
- package/package.json +22 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { getIcon } from './icons.js';
|
|
2
|
+
/// An icon or label that, when clicked, executes a command.
|
|
3
|
+
export class MenuItem {
|
|
4
|
+
/// Create a menu item.
|
|
5
|
+
constructor(
|
|
6
|
+
/// The spec used to create this item.
|
|
7
|
+
spec) {
|
|
8
|
+
Object.defineProperty(this, "spec", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: spec
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "CSS_PREFIX", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: 'kb-menu__button'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/// Renders the icon according to its [display
|
|
22
|
+
/// spec](#menu.MenuItemSpec.display), and adds an event handler which
|
|
23
|
+
/// executes the command when the representation is clicked.
|
|
24
|
+
render(view) {
|
|
25
|
+
let spec = this.spec;
|
|
26
|
+
let dom = spec.render ? spec.render(view) : null;
|
|
27
|
+
if (!dom) {
|
|
28
|
+
// Create a proper button element for better accessibility
|
|
29
|
+
dom = document.createElement('button');
|
|
30
|
+
dom.setAttribute('type', 'button');
|
|
31
|
+
// Add our new CSS classes while maintaining backward compatibility
|
|
32
|
+
dom.classList.add(this.CSS_PREFIX);
|
|
33
|
+
if (spec.icon) {
|
|
34
|
+
const icon = getIcon(view.root, spec.icon);
|
|
35
|
+
dom.appendChild(icon);
|
|
36
|
+
dom.classList.add(this.CSS_PREFIX + '--icon-only');
|
|
37
|
+
}
|
|
38
|
+
if (spec.label) {
|
|
39
|
+
const labelSpan = document.createElement('span');
|
|
40
|
+
labelSpan.appendChild(document.createTextNode(translate(view, spec.label)));
|
|
41
|
+
dom.appendChild(labelSpan);
|
|
42
|
+
if (spec.icon) {
|
|
43
|
+
dom.classList.remove(this.CSS_PREFIX + '--icon-only');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!spec.icon && !spec.label) {
|
|
47
|
+
throw new RangeError('MenuItem without icon or label property');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (spec.title) {
|
|
51
|
+
const title = typeof spec.title === 'function'
|
|
52
|
+
? spec.title(view.state)
|
|
53
|
+
: spec.title;
|
|
54
|
+
dom.setAttribute('title', translate(view, title));
|
|
55
|
+
dom.setAttribute('aria-label', translate(view, title));
|
|
56
|
+
}
|
|
57
|
+
if (spec.class)
|
|
58
|
+
dom.classList.add(spec.class);
|
|
59
|
+
if (spec.css)
|
|
60
|
+
dom.style.cssText += spec.css;
|
|
61
|
+
dom.addEventListener('mousedown', (e) => {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
if (!dom.classList.contains(this.CSS_PREFIX + '--disabled')) {
|
|
64
|
+
spec.run(view.state, view.dispatch);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const update = (state) => {
|
|
68
|
+
if (spec.select) {
|
|
69
|
+
let selected = spec.select(state);
|
|
70
|
+
dom.style.display = selected ? '' : 'none';
|
|
71
|
+
if (!selected)
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
let enabled = true;
|
|
75
|
+
if (spec.enable) {
|
|
76
|
+
enabled = spec.enable(state) || false;
|
|
77
|
+
setClass(dom, this.CSS_PREFIX + '--disabled', !enabled);
|
|
78
|
+
dom.setAttribute('aria-disabled', (!enabled).toString());
|
|
79
|
+
}
|
|
80
|
+
if (spec.active) {
|
|
81
|
+
let active = enabled && spec.active(state) || false;
|
|
82
|
+
setClass(dom, this.CSS_PREFIX + '--active', active);
|
|
83
|
+
dom.setAttribute('aria-pressed', active.toString());
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
return { dom, update };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function translate(view, text) {
|
|
91
|
+
return view._props.translate
|
|
92
|
+
? view._props.translate(text)
|
|
93
|
+
: text;
|
|
94
|
+
}
|
|
95
|
+
let lastMenuEvent = {
|
|
96
|
+
time: 0,
|
|
97
|
+
node: null,
|
|
98
|
+
};
|
|
99
|
+
function markMenuEvent(e) {
|
|
100
|
+
lastMenuEvent.time = Date.now();
|
|
101
|
+
lastMenuEvent.node = e.target;
|
|
102
|
+
}
|
|
103
|
+
function isMenuEvent(wrapper) {
|
|
104
|
+
return Date.now() - 100 < lastMenuEvent.time &&
|
|
105
|
+
lastMenuEvent.node && wrapper.contains(lastMenuEvent.node);
|
|
106
|
+
}
|
|
107
|
+
/// A drop-down menu, displayed as a label with a downwards-pointing
|
|
108
|
+
/// triangle to the right of it.
|
|
109
|
+
export class Dropdown {
|
|
110
|
+
/// Create a dropdown wrapping the elements.
|
|
111
|
+
constructor(content,
|
|
112
|
+
/// @internal
|
|
113
|
+
options = {}) {
|
|
114
|
+
Object.defineProperty(this, "options", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
writable: true,
|
|
118
|
+
value: options
|
|
119
|
+
});
|
|
120
|
+
Object.defineProperty(this, "CSS_PREFIX", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
configurable: true,
|
|
123
|
+
writable: true,
|
|
124
|
+
value: 'kb-dropdown'
|
|
125
|
+
});
|
|
126
|
+
/// @internal
|
|
127
|
+
Object.defineProperty(this, "content", {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
configurable: true,
|
|
130
|
+
writable: true,
|
|
131
|
+
value: void 0
|
|
132
|
+
});
|
|
133
|
+
this.options = options || {};
|
|
134
|
+
this.content = Array.isArray(content) ? content : [content];
|
|
135
|
+
}
|
|
136
|
+
/// Render the dropdown menu and sub-items.
|
|
137
|
+
render(view) {
|
|
138
|
+
let content = renderDropdownItems(this.content, view);
|
|
139
|
+
let win = view.dom.ownerDocument.defaultView;
|
|
140
|
+
// Create a button element instead of div for better accessibility
|
|
141
|
+
let label = document.createElement('button');
|
|
142
|
+
label.setAttribute('type', 'button');
|
|
143
|
+
label.classList.add(this.CSS_PREFIX + '__label');
|
|
144
|
+
if (this.options.class) {
|
|
145
|
+
label.classList.add(this.options.class);
|
|
146
|
+
}
|
|
147
|
+
if (this.options.css)
|
|
148
|
+
label.setAttribute('style', this.options.css);
|
|
149
|
+
label.appendChild(document.createTextNode(translate(view, this.options.label || '')));
|
|
150
|
+
if (this.options.title) {
|
|
151
|
+
const title = translate(view, this.options.title);
|
|
152
|
+
label.setAttribute('title', title);
|
|
153
|
+
label.setAttribute('aria-label', title);
|
|
154
|
+
}
|
|
155
|
+
// Set ARIA attributes for accessibility
|
|
156
|
+
label.setAttribute('aria-haspopup', 'true');
|
|
157
|
+
label.setAttribute('aria-expanded', 'false');
|
|
158
|
+
let wrap = document.createElement('div');
|
|
159
|
+
wrap.classList.add(this.CSS_PREFIX);
|
|
160
|
+
wrap.appendChild(label);
|
|
161
|
+
let open = null;
|
|
162
|
+
let listeningOnClose = null;
|
|
163
|
+
let close = () => {
|
|
164
|
+
if (open && open.close()) {
|
|
165
|
+
open = null;
|
|
166
|
+
win.removeEventListener('mousedown', listeningOnClose);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
label.addEventListener('mousedown', (e) => {
|
|
170
|
+
e.preventDefault();
|
|
171
|
+
markMenuEvent(e);
|
|
172
|
+
if (open) {
|
|
173
|
+
close();
|
|
174
|
+
wrap.classList.remove('kb-dropdown--open');
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
open = this.expand(wrap, content.dom);
|
|
178
|
+
wrap.classList.add('kb-dropdown--open');
|
|
179
|
+
win.addEventListener('mousedown', listeningOnClose = () => {
|
|
180
|
+
if (!isMenuEvent(wrap))
|
|
181
|
+
close();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
function update(state) {
|
|
186
|
+
let inner = content.update(state);
|
|
187
|
+
wrap.style.display = inner ? '' : 'none';
|
|
188
|
+
return inner;
|
|
189
|
+
}
|
|
190
|
+
return { dom: wrap, update };
|
|
191
|
+
}
|
|
192
|
+
/// @internal
|
|
193
|
+
expand(dom, items) {
|
|
194
|
+
const menuDOM = document.createElement('div');
|
|
195
|
+
menuDOM.classList.add('kb-dropdown__menu');
|
|
196
|
+
menuDOM.setAttribute('role', 'menu');
|
|
197
|
+
if (this.options.class) {
|
|
198
|
+
menuDOM.classList.add(this.options.class);
|
|
199
|
+
}
|
|
200
|
+
items.forEach((item) => menuDOM.appendChild(item));
|
|
201
|
+
let done = false;
|
|
202
|
+
function close() {
|
|
203
|
+
if (done)
|
|
204
|
+
return false;
|
|
205
|
+
done = true;
|
|
206
|
+
dom.removeChild(menuDOM);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
dom.appendChild(menuDOM);
|
|
210
|
+
return { close, node: menuDOM };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function renderDropdownItems(items, view) {
|
|
214
|
+
let rendered = [], updates = [];
|
|
215
|
+
for (let i = 0; i < items.length; i++) {
|
|
216
|
+
let { dom, update } = items[i].render(view);
|
|
217
|
+
const item = document.createElement('div');
|
|
218
|
+
item.classList.add('kb-dropdown__item');
|
|
219
|
+
item.appendChild(dom);
|
|
220
|
+
rendered.push(item);
|
|
221
|
+
updates.push(update);
|
|
222
|
+
}
|
|
223
|
+
return { dom: rendered, update: combineUpdates(updates, rendered) };
|
|
224
|
+
}
|
|
225
|
+
function combineUpdates(updates, nodes) {
|
|
226
|
+
return (state) => {
|
|
227
|
+
let something = false;
|
|
228
|
+
for (let i = 0; i < updates.length; i++) {
|
|
229
|
+
let up = updates[i](state);
|
|
230
|
+
nodes[i].style.display = up ? '' : 'none';
|
|
231
|
+
if (up)
|
|
232
|
+
something = true;
|
|
233
|
+
}
|
|
234
|
+
return something;
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/// Represents a submenu wrapping a group of elements that start
|
|
238
|
+
/// hidden and expand to the right when hovered over or tapped.
|
|
239
|
+
export class DropdownSubmenu {
|
|
240
|
+
/// Creates a submenu for the given group of menu elements. The
|
|
241
|
+
/// following options are recognized:
|
|
242
|
+
constructor(content,
|
|
243
|
+
/// @internal
|
|
244
|
+
options = {}) {
|
|
245
|
+
Object.defineProperty(this, "options", {
|
|
246
|
+
enumerable: true,
|
|
247
|
+
configurable: true,
|
|
248
|
+
writable: true,
|
|
249
|
+
value: options
|
|
250
|
+
});
|
|
251
|
+
/// @internal
|
|
252
|
+
Object.defineProperty(this, "content", {
|
|
253
|
+
enumerable: true,
|
|
254
|
+
configurable: true,
|
|
255
|
+
writable: true,
|
|
256
|
+
value: void 0
|
|
257
|
+
});
|
|
258
|
+
this.content = Array.isArray(content) ? content : [content];
|
|
259
|
+
}
|
|
260
|
+
/// Renders the submenu.
|
|
261
|
+
render(view) {
|
|
262
|
+
const CSS_PREFIX = 'kb-submenu';
|
|
263
|
+
const items = renderDropdownItems(this.content, view);
|
|
264
|
+
const win = view.dom.ownerDocument.defaultView;
|
|
265
|
+
const wrap = document.createElement('div');
|
|
266
|
+
wrap.classList.add(CSS_PREFIX);
|
|
267
|
+
const label = document.createElement('div');
|
|
268
|
+
label.classList.add(CSS_PREFIX + '__label');
|
|
269
|
+
label.appendChild(document.createTextNode(translate(view, this.options.label || '')));
|
|
270
|
+
wrap.appendChild(label);
|
|
271
|
+
const submenu = document.createElement('div');
|
|
272
|
+
submenu.classList.add(CSS_PREFIX + '__content');
|
|
273
|
+
items.dom.forEach((item) => submenu.appendChild(item));
|
|
274
|
+
wrap.appendChild(submenu);
|
|
275
|
+
let listeningOnClose = null;
|
|
276
|
+
label.addEventListener('mousedown', (e) => {
|
|
277
|
+
e.preventDefault();
|
|
278
|
+
markMenuEvent(e);
|
|
279
|
+
const isOpen = wrap.classList.contains(CSS_PREFIX + '--open');
|
|
280
|
+
setClass(wrap, CSS_PREFIX + '--open', !isOpen);
|
|
281
|
+
if (!isOpen && !listeningOnClose) {
|
|
282
|
+
win.addEventListener('mousedown', listeningOnClose = () => {
|
|
283
|
+
if (!isMenuEvent(wrap)) {
|
|
284
|
+
wrap.classList.remove(CSS_PREFIX + '--open');
|
|
285
|
+
win.removeEventListener('mousedown', listeningOnClose);
|
|
286
|
+
listeningOnClose = null;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
function update(state) {
|
|
292
|
+
let inner = items.update(state);
|
|
293
|
+
wrap.style.display = inner ? '' : 'none';
|
|
294
|
+
return inner;
|
|
295
|
+
}
|
|
296
|
+
return { dom: wrap, update };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/// Render the given, possibly nested, array of menu elements into a
|
|
300
|
+
/// document fragment, placing separators between them (and ensuring no
|
|
301
|
+
/// superfluous separators appear when some of the groups turn out to
|
|
302
|
+
/// be empty).
|
|
303
|
+
export function renderGrouped(view, content) {
|
|
304
|
+
let result = document.createDocumentFragment();
|
|
305
|
+
let updates = [], separators = [];
|
|
306
|
+
for (let i = 0; i < content.length; i++) {
|
|
307
|
+
let items = content[i], localUpdates = [], localNodes = [];
|
|
308
|
+
for (let j = 0; j < items.length; j++) {
|
|
309
|
+
let { dom, update } = items[j].render(view);
|
|
310
|
+
let span = document.createElement('span');
|
|
311
|
+
span.classList.add('kb-menu__item');
|
|
312
|
+
span.appendChild(dom);
|
|
313
|
+
result.appendChild(span);
|
|
314
|
+
localNodes.push(span);
|
|
315
|
+
localUpdates.push(update);
|
|
316
|
+
}
|
|
317
|
+
if (localUpdates.length) {
|
|
318
|
+
updates.push(combineUpdates(localUpdates, localNodes));
|
|
319
|
+
if (i < content.length - 1) {
|
|
320
|
+
separators.push(result.appendChild(separator()));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function update(state) {
|
|
325
|
+
let something = false, needSep = false;
|
|
326
|
+
for (let i = 0; i < updates.length; i++) {
|
|
327
|
+
let hasContent = updates[i](state);
|
|
328
|
+
if (i) {
|
|
329
|
+
separators[i - 1].style.display = needSep && hasContent ? '' : 'none';
|
|
330
|
+
}
|
|
331
|
+
needSep = hasContent;
|
|
332
|
+
if (hasContent)
|
|
333
|
+
something = true;
|
|
334
|
+
}
|
|
335
|
+
return something;
|
|
336
|
+
}
|
|
337
|
+
return { dom: result, update };
|
|
338
|
+
}
|
|
339
|
+
function separator() {
|
|
340
|
+
const elem = document.createElement('div');
|
|
341
|
+
elem.classList.add('kb-menu__separator');
|
|
342
|
+
return elem;
|
|
343
|
+
}
|
|
344
|
+
// Work around classList.toggle being broken in IE11
|
|
345
|
+
function setClass(dom, cls, on) {
|
|
346
|
+
if (on)
|
|
347
|
+
dom.classList.add(cls);
|
|
348
|
+
else
|
|
349
|
+
dom.classList.remove(cls);
|
|
350
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/extension-menu-legacy/src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,eAAe,EACf,KAAK,WAAW,EAChB,QAAQ,EACR,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AAEnB,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Attrs } from 'prosemirror-model';
|
|
2
|
+
export declare function openPrompt(options: {
|
|
3
|
+
title: string;
|
|
4
|
+
fields: {
|
|
5
|
+
[name: string]: Field;
|
|
6
|
+
};
|
|
7
|
+
callback: (attrs: Attrs) => void;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare abstract class Field {
|
|
10
|
+
readonly options: {
|
|
11
|
+
value?: any;
|
|
12
|
+
label: string;
|
|
13
|
+
required?: boolean;
|
|
14
|
+
validate?: (value: any) => string | null;
|
|
15
|
+
clean?: (value: any) => any;
|
|
16
|
+
};
|
|
17
|
+
constructor(options: {
|
|
18
|
+
value?: any;
|
|
19
|
+
label: string;
|
|
20
|
+
required?: boolean;
|
|
21
|
+
validate?: (value: any) => string | null;
|
|
22
|
+
clean?: (value: any) => any;
|
|
23
|
+
});
|
|
24
|
+
abstract render(): HTMLElement;
|
|
25
|
+
read(dom: HTMLElement): any;
|
|
26
|
+
validateType(value: any): string | null;
|
|
27
|
+
validate(value: any): string | null;
|
|
28
|
+
clean(value: any): any;
|
|
29
|
+
}
|
|
30
|
+
export declare class TextField extends Field {
|
|
31
|
+
render(): any;
|
|
32
|
+
}
|
|
33
|
+
export declare class SelectField extends Field {
|
|
34
|
+
render(): any;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/extension-menu-legacy/src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI1C,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAA;KAAE,CAAC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,QA0EA;AA+BD,8BAAsB,KAAK;IAKvB,QAAQ,CAAC,OAAO,EAAE;QAEhB,KAAK,CAAC,EAAE,GAAG,CAAC;QAGZ,KAAK,EAAE,MAAM,CAAC;QAGd,QAAQ,CAAC,EAAE,OAAO,CAAC;QAInB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC;QAGzC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;KAC7B;gBAhBQ,OAAO,EAAE;QAEhB,KAAK,CAAC,EAAE,GAAG,CAAC;QAGZ,KAAK,EAAE,MAAM,CAAC;QAGd,QAAQ,CAAC,EAAE,OAAO,CAAC;QAInB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC;QAGzC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;KAC7B;IAIH,QAAQ,CAAC,MAAM,IAAI,WAAW;IAG9B,IAAI,CAAC,GAAG,EAAE,WAAW;IAKrB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI;IAKvC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI;IAQnC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAGvB;AAGD,qBAAa,SAAU,SAAQ,KAAK;IAClC,MAAM;CAQP;AAMD,qBAAa,WAAY,SAAQ,KAAK;IACpC,MAAM;CAWP"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const CSS_PREFIX = 'kb-prompt';
|
|
2
|
+
export function openPrompt(options) {
|
|
3
|
+
let wrapper = document.body.appendChild(document.createElement('div'));
|
|
4
|
+
wrapper.className = CSS_PREFIX;
|
|
5
|
+
let mouseOutside = (e) => {
|
|
6
|
+
if (!wrapper.contains(e.target))
|
|
7
|
+
close();
|
|
8
|
+
};
|
|
9
|
+
setTimeout(() => globalThis.addEventListener('mousedown', mouseOutside), 50);
|
|
10
|
+
let close = () => {
|
|
11
|
+
globalThis.removeEventListener('mousedown', mouseOutside);
|
|
12
|
+
if (wrapper.parentNode)
|
|
13
|
+
wrapper.parentNode.removeChild(wrapper);
|
|
14
|
+
};
|
|
15
|
+
let domFields = [];
|
|
16
|
+
for (let name in options.fields) {
|
|
17
|
+
domFields.push(options.fields[name].render());
|
|
18
|
+
}
|
|
19
|
+
let submitButton = document.createElement('button');
|
|
20
|
+
submitButton.type = 'submit';
|
|
21
|
+
submitButton.className = CSS_PREFIX + '--submit';
|
|
22
|
+
submitButton.textContent = 'OK';
|
|
23
|
+
let cancelButton = document.createElement('button');
|
|
24
|
+
cancelButton.type = 'button';
|
|
25
|
+
cancelButton.className = CSS_PREFIX + '--cancel';
|
|
26
|
+
cancelButton.textContent = 'Cancel';
|
|
27
|
+
cancelButton.addEventListener('click', close);
|
|
28
|
+
let form = wrapper.appendChild(document.createElement('form'));
|
|
29
|
+
if (options.title) {
|
|
30
|
+
form.appendChild(document.createElement('h5')).textContent = options.title;
|
|
31
|
+
}
|
|
32
|
+
domFields.forEach((field) => {
|
|
33
|
+
form.appendChild(document.createElement('div')).appendChild(field);
|
|
34
|
+
});
|
|
35
|
+
let buttons = form.appendChild(document.createElement('div'));
|
|
36
|
+
buttons.className = CSS_PREFIX + '__buttons';
|
|
37
|
+
buttons.appendChild(submitButton);
|
|
38
|
+
buttons.appendChild(document.createTextNode(' '));
|
|
39
|
+
buttons.appendChild(cancelButton);
|
|
40
|
+
let box = wrapper.getBoundingClientRect();
|
|
41
|
+
wrapper.style.top = ((globalThis.innerHeight - box.height) / 2) + 'px';
|
|
42
|
+
wrapper.style.left = ((globalThis.innerWidth - box.width) / 2) + 'px';
|
|
43
|
+
let submit = () => {
|
|
44
|
+
let params = getValues(options.fields, domFields);
|
|
45
|
+
if (params) {
|
|
46
|
+
close();
|
|
47
|
+
options.callback(params);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
form.addEventListener('submit', (e) => {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
submit();
|
|
53
|
+
});
|
|
54
|
+
form.addEventListener('keydown', (e) => {
|
|
55
|
+
if (e.keyCode == 27) {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
close();
|
|
58
|
+
}
|
|
59
|
+
else if (e.keyCode == 13 && !(e.ctrlKey || e.metaKey || e.shiftKey)) {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
submit();
|
|
62
|
+
}
|
|
63
|
+
else if (e.keyCode == 9) {
|
|
64
|
+
globalThis.setTimeout(() => {
|
|
65
|
+
if (!wrapper.contains(document.activeElement))
|
|
66
|
+
close();
|
|
67
|
+
}, 500);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
let input = form.elements[0];
|
|
71
|
+
if (input)
|
|
72
|
+
input.focus();
|
|
73
|
+
}
|
|
74
|
+
function getValues(fields, domFields) {
|
|
75
|
+
let result = Object.create(null), i = 0;
|
|
76
|
+
for (let name in fields) {
|
|
77
|
+
let field = fields[name], dom = domFields[i++];
|
|
78
|
+
let value = field.read(dom), bad = field.validate(value);
|
|
79
|
+
if (bad) {
|
|
80
|
+
reportInvalid(dom, bad);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
result[name] = field.clean(value);
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function reportInvalid(dom, message) {
|
|
88
|
+
// FIXME this is awful and needs a lot more work
|
|
89
|
+
let parent = dom.parentNode;
|
|
90
|
+
let msg = parent.appendChild(document.createElement('div'));
|
|
91
|
+
msg.style.left = (dom.offsetLeft + dom.offsetWidth + 2) + 'px';
|
|
92
|
+
msg.style.top = (dom.offsetTop - 5) + 'px';
|
|
93
|
+
msg.className = 'kb-invalid';
|
|
94
|
+
msg.textContent = message;
|
|
95
|
+
setTimeout(() => parent.removeChild(msg), 1500);
|
|
96
|
+
}
|
|
97
|
+
/// The type of field that `openPrompt` expects to be passed to it.
|
|
98
|
+
export class Field {
|
|
99
|
+
/// Create a field with the given options. Options support by all
|
|
100
|
+
/// field types are:
|
|
101
|
+
constructor(
|
|
102
|
+
/// @internal
|
|
103
|
+
options) {
|
|
104
|
+
Object.defineProperty(this, "options", {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value: options
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/// Read the field's value from its DOM node.
|
|
112
|
+
read(dom) {
|
|
113
|
+
return dom.value;
|
|
114
|
+
}
|
|
115
|
+
/// A field-type-specific validation function.
|
|
116
|
+
validateType(value) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
/// @internal
|
|
120
|
+
validate(value) {
|
|
121
|
+
if (!value && this.options.required) {
|
|
122
|
+
return 'Required field';
|
|
123
|
+
}
|
|
124
|
+
return this.validateType(value) ||
|
|
125
|
+
(this.options.validate ? this.options.validate(value) : null);
|
|
126
|
+
}
|
|
127
|
+
clean(value) {
|
|
128
|
+
return this.options.clean ? this.options.clean(value) : value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/// A field class for single-line text fields.
|
|
132
|
+
export class TextField extends Field {
|
|
133
|
+
render() {
|
|
134
|
+
let input = document.createElement('input');
|
|
135
|
+
input.type = 'text';
|
|
136
|
+
input.placeholder = this.options.label;
|
|
137
|
+
input.value = this.options.value || '';
|
|
138
|
+
input.autocomplete = 'off';
|
|
139
|
+
return input;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/// A field class for dropdown fields based on a plain `<select>`
|
|
143
|
+
/// tag. Expects an option `options`, which should be an array of
|
|
144
|
+
/// `{value: string, label: string}` objects, or a function taking a
|
|
145
|
+
/// `ProseMirror` instance and returning such an array.
|
|
146
|
+
export class SelectField extends Field {
|
|
147
|
+
render() {
|
|
148
|
+
let select = document.createElement('select');
|
|
149
|
+
this.options.options
|
|
150
|
+
.forEach((o) => {
|
|
151
|
+
let opt = select.appendChild(document.createElement('option'));
|
|
152
|
+
opt.value = o.value;
|
|
153
|
+
opt.selected = o.value == this.options.value;
|
|
154
|
+
opt.label = o.label;
|
|
155
|
+
});
|
|
156
|
+
return select;
|
|
157
|
+
}
|
|
158
|
+
}
|
package/esm/package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kerebron/extension-menu-legacy",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"module": "./esm/extension-menu-legacy/src/mod.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./esm/extension-menu-legacy/src/mod.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"scripts": {},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"prosemirror-model": "1.25.3",
|
|
14
|
+
"prosemirror-state": "1.4.3",
|
|
15
|
+
"prosemirror-transform": "1.10.4",
|
|
16
|
+
"prosemirror-view": "1.40.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^20.9.0"
|
|
20
|
+
},
|
|
21
|
+
"_generatedBy": "dnt@dev"
|
|
22
|
+
}
|