tiny-markdown-editor 0.2.6 → 0.2.7

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.
@@ -0,0 +1,35 @@
1
+ import { Editor, Position } from "./TinyMDE";
2
+ export interface CommandAction {
3
+ (editor: Editor): void;
4
+ }
5
+ export interface CommandEnabled {
6
+ (editor: Editor, focus?: Position, anchor?: Position): boolean | null;
7
+ }
8
+ export interface CommandDefinition {
9
+ name: string;
10
+ action: string | CommandAction;
11
+ innerHTML: string;
12
+ title: string;
13
+ hotkey?: string;
14
+ enabled?: CommandEnabled;
15
+ }
16
+ export interface CommandBarProps {
17
+ element?: string | HTMLElement;
18
+ editor?: Editor;
19
+ commands?: (string | CommandDefinition)[];
20
+ }
21
+ export declare class CommandBar {
22
+ e: HTMLDivElement | null;
23
+ editor: Editor | null;
24
+ commands: Record<string, CommandDefinition>;
25
+ buttons: Record<string, HTMLDivElement>;
26
+ state: Record<string, boolean | null>;
27
+ private hotkeys;
28
+ constructor(props: CommandBarProps);
29
+ private createCommandBarElement;
30
+ private handleClick;
31
+ setEditor(editor: Editor): void;
32
+ private handleSelection;
33
+ private handleKeydown;
34
+ }
35
+ export default CommandBar;
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CommandBar = void 0;
7
+ const svg_1 = __importDefault(require("./svg/svg"));
8
+ const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(typeof navigator !== "undefined" ? navigator.platform : "");
9
+ const DefaultCommands = {
10
+ bold: {
11
+ name: "bold",
12
+ action: "bold",
13
+ innerHTML: svg_1.default.bold,
14
+ title: "Bold",
15
+ hotkey: "Mod-B",
16
+ },
17
+ italic: {
18
+ name: "italic",
19
+ action: "italic",
20
+ innerHTML: svg_1.default.italic,
21
+ title: "Italic",
22
+ hotkey: "Mod-I",
23
+ },
24
+ strikethrough: {
25
+ name: "strikethrough",
26
+ action: "strikethrough",
27
+ innerHTML: svg_1.default.strikethrough,
28
+ title: "Strikethrough",
29
+ hotkey: "Mod2-Shift-5",
30
+ },
31
+ code: {
32
+ name: "code",
33
+ action: "code",
34
+ innerHTML: svg_1.default.code,
35
+ title: "Format as code",
36
+ },
37
+ h1: {
38
+ name: "h1",
39
+ action: "h1",
40
+ innerHTML: svg_1.default.h1,
41
+ title: "Level 1 heading",
42
+ hotkey: "Mod-Shift-1",
43
+ },
44
+ h2: {
45
+ name: "h2",
46
+ action: "h2",
47
+ innerHTML: svg_1.default.h2,
48
+ title: "Level 2 heading",
49
+ hotkey: "Mod-Shift-2",
50
+ },
51
+ ul: {
52
+ name: "ul",
53
+ action: "ul",
54
+ innerHTML: svg_1.default.ul,
55
+ title: "Bulleted list",
56
+ },
57
+ ol: {
58
+ name: "ol",
59
+ action: "ol",
60
+ innerHTML: svg_1.default.ol,
61
+ title: "Numbered list",
62
+ },
63
+ blockquote: {
64
+ name: "blockquote",
65
+ action: "blockquote",
66
+ innerHTML: svg_1.default.blockquote,
67
+ title: "Quote",
68
+ hotkey: "Mod2-Shift-Q",
69
+ },
70
+ insertLink: {
71
+ name: "insertLink",
72
+ action: (editor) => {
73
+ if (editor.isInlineFormattingAllowed())
74
+ editor.wrapSelection("[", "]()");
75
+ },
76
+ enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
77
+ innerHTML: svg_1.default.link,
78
+ title: "Insert link",
79
+ hotkey: "Mod-K",
80
+ },
81
+ insertImage: {
82
+ name: "insertImage",
83
+ action: (editor) => {
84
+ if (editor.isInlineFormattingAllowed())
85
+ editor.wrapSelection("![", "]()");
86
+ },
87
+ enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
88
+ innerHTML: svg_1.default.image,
89
+ title: "Insert image",
90
+ hotkey: "Mod2-Shift-I",
91
+ },
92
+ hr: {
93
+ name: "hr",
94
+ action: (editor) => editor.paste("\n***\n"),
95
+ enabled: () => false,
96
+ innerHTML: svg_1.default.hr,
97
+ title: "Insert horizontal line",
98
+ hotkey: "Mod2-Shift-L",
99
+ },
100
+ undo: {
101
+ name: "undo",
102
+ action: (editor) => editor.undo(),
103
+ enabled: (editor) => (editor.canUndo ? false : null),
104
+ innerHTML: svg_1.default.undo,
105
+ title: "Undo",
106
+ },
107
+ redo: {
108
+ name: "redo",
109
+ action: (editor) => editor.redo(),
110
+ enabled: (editor) => (editor.canRedo ? false : null),
111
+ innerHTML: svg_1.default.redo,
112
+ title: "Redo",
113
+ },
114
+ };
115
+ class CommandBar {
116
+ constructor(props) {
117
+ this.e = null;
118
+ this.editor = null;
119
+ this.commands = {};
120
+ this.buttons = {};
121
+ this.state = {};
122
+ this.hotkeys = [];
123
+ this.e = null;
124
+ this.editor = null;
125
+ this.commands = {};
126
+ this.buttons = {};
127
+ this.state = {};
128
+ this.hotkeys = [];
129
+ let element = null;
130
+ if (typeof props.element === 'string') {
131
+ element = document.getElementById(props.element);
132
+ }
133
+ else if (props.element) {
134
+ element = props.element;
135
+ }
136
+ if (!element) {
137
+ element = document.body;
138
+ }
139
+ this.createCommandBarElement(element, props.commands || [
140
+ "bold",
141
+ "italic",
142
+ "strikethrough",
143
+ "|",
144
+ "code",
145
+ "|",
146
+ "h1",
147
+ "h2",
148
+ "|",
149
+ "ul",
150
+ "ol",
151
+ "|",
152
+ "blockquote",
153
+ "hr",
154
+ "|",
155
+ "undo",
156
+ "redo",
157
+ "|",
158
+ "insertLink",
159
+ "insertImage",
160
+ ]);
161
+ document.addEventListener("keydown", (e) => this.handleKeydown(e));
162
+ if (props.editor)
163
+ this.setEditor(props.editor);
164
+ }
165
+ createCommandBarElement(parentElement, commands) {
166
+ this.e = document.createElement("div");
167
+ this.e.className = "TMCommandBar";
168
+ for (let command of commands) {
169
+ if (command === "|") {
170
+ let el = document.createElement("div");
171
+ el.className = "TMCommandDivider";
172
+ this.e.appendChild(el);
173
+ }
174
+ else {
175
+ let commandName;
176
+ if (typeof command === "string") {
177
+ if (DefaultCommands[command]) {
178
+ commandName = command;
179
+ this.commands[commandName] = DefaultCommands[commandName];
180
+ }
181
+ else {
182
+ continue;
183
+ }
184
+ }
185
+ else if (typeof command === "object" && command.name) {
186
+ commandName = command.name;
187
+ this.commands[commandName] = {};
188
+ if (DefaultCommands[commandName]) {
189
+ Object.assign(this.commands[commandName], DefaultCommands[commandName]);
190
+ }
191
+ Object.assign(this.commands[commandName], command);
192
+ }
193
+ else {
194
+ continue;
195
+ }
196
+ let title = this.commands[commandName].title || commandName;
197
+ if (this.commands[commandName].hotkey) {
198
+ const keys = this.commands[commandName].hotkey.split("-");
199
+ let modifiers = [];
200
+ let modifierexplanation = [];
201
+ for (let i = 0; i < keys.length - 1; i++) {
202
+ switch (keys[i]) {
203
+ case "Ctrl":
204
+ modifiers.push("ctrlKey");
205
+ modifierexplanation.push("Ctrl");
206
+ break;
207
+ case "Cmd":
208
+ modifiers.push("metaKey");
209
+ modifierexplanation.push("⌘");
210
+ break;
211
+ case "Alt":
212
+ modifiers.push("altKey");
213
+ modifierexplanation.push("Alt");
214
+ break;
215
+ case "Option":
216
+ modifiers.push("altKey");
217
+ modifierexplanation.push("⌥");
218
+ break;
219
+ case "Win":
220
+ modifiers.push("metaKey");
221
+ modifierexplanation.push("⊞ Win");
222
+ break;
223
+ case "Shift":
224
+ modifiers.push("shiftKey");
225
+ modifierexplanation.push("⇧");
226
+ break;
227
+ case "Mod":
228
+ if (isMacLike) {
229
+ modifiers.push("metaKey");
230
+ modifierexplanation.push("⌘");
231
+ }
232
+ else {
233
+ modifiers.push("ctrlKey");
234
+ modifierexplanation.push("Ctrl");
235
+ }
236
+ break;
237
+ case "Mod2":
238
+ modifiers.push("altKey");
239
+ if (isMacLike)
240
+ modifierexplanation.push("⌥");
241
+ else
242
+ modifierexplanation.push("Alt");
243
+ break;
244
+ }
245
+ }
246
+ modifierexplanation.push(keys[keys.length - 1]);
247
+ let hotkey = {
248
+ modifiers: modifiers,
249
+ command: commandName,
250
+ };
251
+ if (keys[keys.length - 1].match(/^[0-9]$/)) {
252
+ hotkey.code = `Digit${keys[keys.length - 1]}`;
253
+ }
254
+ else {
255
+ hotkey.key = keys[keys.length - 1].toLowerCase();
256
+ }
257
+ this.hotkeys.push(hotkey);
258
+ title = title.concat(` (${modifierexplanation.join("+")})`);
259
+ }
260
+ this.buttons[commandName] = document.createElement("div");
261
+ this.buttons[commandName].className = "TMCommandButton TMCommandButton_Disabled";
262
+ this.buttons[commandName].title = title;
263
+ this.buttons[commandName].innerHTML = this.commands[commandName].innerHTML;
264
+ this.buttons[commandName].addEventListener("mousedown", (e) => this.handleClick(commandName, e));
265
+ this.e.appendChild(this.buttons[commandName]);
266
+ }
267
+ }
268
+ parentElement.appendChild(this.e);
269
+ }
270
+ handleClick(commandName, event) {
271
+ if (!this.editor)
272
+ return;
273
+ event.preventDefault();
274
+ if (typeof this.commands[commandName].action === "string") {
275
+ if (this.state[commandName] === false)
276
+ this.editor.setCommandState(commandName, true);
277
+ else
278
+ this.editor.setCommandState(commandName, false);
279
+ }
280
+ else if (typeof this.commands[commandName].action === "function") {
281
+ this.commands[commandName].action(this.editor);
282
+ }
283
+ }
284
+ setEditor(editor) {
285
+ this.editor = editor;
286
+ editor.addEventListener("selection", (e) => this.handleSelection(e));
287
+ }
288
+ handleSelection(event) {
289
+ if (event.commandState) {
290
+ for (let command in this.commands) {
291
+ if (event.commandState[command] === undefined) {
292
+ if (this.commands[command].enabled) {
293
+ this.state[command] = this.commands[command].enabled(this.editor, event.focus, event.anchor);
294
+ }
295
+ else {
296
+ this.state[command] = event.focus ? false : null;
297
+ }
298
+ }
299
+ else {
300
+ this.state[command] = event.commandState[command];
301
+ }
302
+ if (this.state[command] === true) {
303
+ this.buttons[command].className = "TMCommandButton TMCommandButton_Active";
304
+ }
305
+ else if (this.state[command] === false) {
306
+ this.buttons[command].className = "TMCommandButton TMCommandButton_Inactive";
307
+ }
308
+ else {
309
+ this.buttons[command].className = "TMCommandButton TMCommandButton_Disabled";
310
+ }
311
+ }
312
+ }
313
+ }
314
+ handleKeydown(event) {
315
+ outer: for (let hotkey of this.hotkeys) {
316
+ if ((hotkey.key && event.key.toLowerCase() === hotkey.key) ||
317
+ (hotkey.code && event.code === hotkey.code)) {
318
+ for (let modifier of hotkey.modifiers) {
319
+ if (!event[modifier])
320
+ continue outer;
321
+ }
322
+ this.handleClick(hotkey.command, event);
323
+ return;
324
+ }
325
+ }
326
+ }
327
+ }
328
+ exports.CommandBar = CommandBar;
329
+ exports.default = CommandBar;
@@ -0,0 +1,77 @@
1
+ export interface GrammarRule {
2
+ regexp: RegExp;
3
+ replacement: string;
4
+ labelPlaceholder?: number;
5
+ }
6
+ export interface HTMLBlockRule {
7
+ start: RegExp;
8
+ end: RegExp | false;
9
+ paraInterrupt: boolean;
10
+ }
11
+ export interface InlineCommand {
12
+ type: 'inline';
13
+ className: string;
14
+ set: {
15
+ pre: string;
16
+ post: string;
17
+ };
18
+ unset: {
19
+ prePattern: RegExp;
20
+ postPattern: RegExp;
21
+ };
22
+ }
23
+ export interface LineCommand {
24
+ type: 'line';
25
+ className: string;
26
+ set: {
27
+ pattern: RegExp;
28
+ replacement: string;
29
+ };
30
+ unset: {
31
+ pattern: RegExp;
32
+ replacement: string;
33
+ };
34
+ }
35
+ export type Command = InlineCommand | LineCommand;
36
+ export declare const punctuationLeading: RegExp;
37
+ export declare const punctuationTrailing: RegExp;
38
+ /**
39
+ * This is CommonMark's block grammar, but we're ignoring nested blocks here.
40
+ */
41
+ export declare const lineGrammar: Record<string, GrammarRule>;
42
+ /**
43
+ * HTML blocks have multiple different classes of opener and closer. This array defines all the cases
44
+ */
45
+ export declare const htmlBlockGrammar: HTMLBlockRule[];
46
+ /**
47
+ * Structure of the object:
48
+ * Top level entries are rules, each consisting of a regular expressions (in string format) as well as a replacement.
49
+ * In the regular expressions, replacements from the object 'replacements' will be processed before compiling into the property regexp.
50
+ */
51
+ export declare const inlineGrammar: Record<string, GrammarRule>;
52
+ /**
53
+ * Creates a merged inline grammar by combining the default inline grammar with custom rules.
54
+ * Custom rules are processed and their regexp patterns are expanded with replacements.
55
+ * @param customRules - Object containing custom inline grammar rules
56
+ * @returns Merged inline grammar object
57
+ */
58
+ export declare function createMergedInlineGrammar(customRules?: Record<string, GrammarRule>): Record<string, GrammarRule>;
59
+ /**
60
+ * Escapes HTML special characters (<, >, and &) in the string.
61
+ * @param {string} string The raw string to be escaped
62
+ * @returns {string} The string, ready to be used in HTML
63
+ */
64
+ export declare function htmlescape(string: string): string;
65
+ /**
66
+ * Contains the commands that can be sent to the editor. Contains objects with a name representing the name of the command.
67
+ * Each of the objects contains the following keys:
68
+ *
69
+ * - type: Can be either inline (for inline formatting) or line (for block / line formatting).
70
+ * - className: Used to determine whether the command is active at a given position.
71
+ * For line formatting, this looks at the class of the line element. For inline elements, tries to find an enclosing element with that class.
72
+ * - set / unset: Contain instructions how to set and unset the command. For line type commands, both consist of a pattern and replacement that
73
+ * will be applied to each line (using String.replace). For inline type commands, the set object contains a pre and post string which will
74
+ * be inserted before and after the selection. The unset object contains a prePattern and a postPattern. Both should be regular expressions and
75
+ * they will be applied to the portion of the line before and after the selection (using String.replace, with an empty replacement string).
76
+ */
77
+ export declare const commands: Record<string, Command>;