react-headless-mde 1.0.0 → 1.0.2

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/dist/esm/index.js CHANGED
@@ -1,13 +1,217 @@
1
1
  import { useRef, useMemo } from 'react';
2
2
 
3
+ var CommandController = /** @class */ (function () {
4
+ function CommandController(textController) {
5
+ this.textController = textController;
6
+ }
7
+ CommandController.prototype.executeCommand = function (command, context) {
8
+ var _a;
9
+ var executeOptions = {
10
+ initialState: this.textController.getState(),
11
+ textApi: this.textController,
12
+ };
13
+ if (command.undo && ((_a = command.shouldUndo) === null || _a === void 0 ? void 0 : _a.call(command, executeOptions))) {
14
+ command.undo(executeOptions);
15
+ }
16
+ else {
17
+ command.execute(executeOptions, context);
18
+ }
19
+ };
20
+ return CommandController;
21
+ }());
22
+
23
+ var TextAreaTextController = /** @class */ (function () {
24
+ function TextAreaTextController(textAreaRef) {
25
+ this.textAreaRef = textAreaRef;
26
+ }
27
+ TextAreaTextController.prototype.getTextArea = function () {
28
+ var textArea = this.textAreaRef.current;
29
+ if (!textArea) {
30
+ throw new Error('TextAreaRef is not set');
31
+ }
32
+ return textArea;
33
+ };
34
+ TextAreaTextController.prototype.setSelection = function (selection) {
35
+ var textArea = this.getTextArea();
36
+ textArea.focus();
37
+ textArea.selectionStart = selection.start;
38
+ textArea.selectionEnd = selection.end;
39
+ return getStateFromTextArea(textArea);
40
+ };
41
+ TextAreaTextController.prototype.replaceSelection = function (text) {
42
+ var textArea = this.getTextArea();
43
+ insertText(textArea, text);
44
+ return getStateFromTextArea(textArea);
45
+ };
46
+ TextAreaTextController.prototype.replaceText = function (text, newText) {
47
+ var textArea = this.getTextArea();
48
+ textArea.value = textArea.value.replace(text, newText);
49
+ return getStateFromTextArea(textArea);
50
+ };
51
+ TextAreaTextController.prototype.getState = function () {
52
+ var textArea = this.getTextArea();
53
+ return getStateFromTextArea(textArea);
54
+ };
55
+ TextAreaTextController.prototype.moveCursorToTheEnd = function () {
56
+ var textArea = this.getTextArea();
57
+ textArea.focus();
58
+ textArea.selectionEnd = textArea.selectionStart = textArea.value.length;
59
+ return getStateFromTextArea(textArea);
60
+ };
61
+ return TextAreaTextController;
62
+ }());
63
+ function getStateFromTextArea(textArea) {
64
+ return {
65
+ selection: {
66
+ start: textArea.selectionStart,
67
+ end: textArea.selectionEnd,
68
+ },
69
+ text: textArea.value,
70
+ };
71
+ }
72
+ /**
73
+ * Inserts the given text at the cursor. If the element contains a selection, the selection
74
+ * will be replaced by the text.
75
+ * The MIT License
76
+ * Copyright (c) 2018 Dmitriy Kubyshkin
77
+ * Copied from https://github.com/grassator/insert-text-at-cursor
78
+ */
79
+ function insertText(input, text) {
80
+ var _a, _b, _c, _d;
81
+ // Most of the used APIs only work with the field selected
82
+ input.focus();
83
+ // IE 8-10
84
+ if (document.selection) {
85
+ var ieRange = document.selection.createRange();
86
+ ieRange.text = text;
87
+ // Move cursor after the inserted text
88
+ ieRange.collapse(false /* to the end */);
89
+ ieRange.select();
90
+ return;
91
+ }
92
+ // Webkit + Edge
93
+ var isSuccess = document.execCommand('insertText', false, text);
94
+ if (!isSuccess) {
95
+ var start = (_a = input.selectionStart) !== null && _a !== void 0 ? _a : 0;
96
+ var end = (_b = input.selectionEnd) !== null && _b !== void 0 ? _b : 0;
97
+ // Firefox (non-standard method)
98
+ if (typeof input.setRangeText === 'function') {
99
+ input.setRangeText(text);
100
+ }
101
+ else {
102
+ if (canManipulateViaTextNodes(input)) {
103
+ var textNode = document.createTextNode(text);
104
+ var node = input.firstChild;
105
+ // If textarea is empty, just insert the text
106
+ if (!node) {
107
+ input.appendChild(textNode);
108
+ }
109
+ else {
110
+ // Otherwise, we need to find a nodes for start and end
111
+ var offset = 0;
112
+ var startNode = null;
113
+ var endNode = null;
114
+ // To make a change we just need a Range, not a Selection
115
+ var range = document.createRange();
116
+ while (node && (startNode === null || endNode === null)) {
117
+ var nodeLength = (_d = (_c = node.nodeValue) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
118
+ // if start of the selection falls into current node
119
+ if (start >= offset && start <= offset + nodeLength) {
120
+ range.setStart((startNode = node), start - offset);
121
+ }
122
+ // if end of the selection falls into current node
123
+ if (end >= offset && end <= offset + nodeLength) {
124
+ range.setEnd((endNode = node), end - offset);
125
+ }
126
+ offset += nodeLength;
127
+ node = node.nextSibling;
128
+ }
129
+ // If there is some text selected, remove it as we should replace it
130
+ if (start !== end) {
131
+ range.deleteContents();
132
+ }
133
+ // Finally insert a new node. The browser will automatically
134
+ // split start and end nodes into two if necessary
135
+ range.insertNode(textNode);
136
+ }
137
+ }
138
+ else {
139
+ // For the text input the only way is to replace the whole value :(
140
+ var value = input.value;
141
+ input.value = value.slice(0, start) + text + value.slice(end);
142
+ }
143
+ }
144
+ // Correct the cursor position to be at the end of the insertion
145
+ input.setSelectionRange(start + text.length, start + text.length);
146
+ // Notify any possible listeners of the change
147
+ var e = document.createEvent('UIEvent');
148
+ e.initEvent('input', true, false);
149
+ input.dispatchEvent(e);
150
+ }
151
+ }
3
152
  /**
4
- * A list of helpers for manipulating markdown text.
5
- * These helpers do not interface with a textarea. For that, see
153
+ * The MIT License
154
+ * Copyright (c) 2018 Dmitriy Kubyshkin
155
+ * Copied from https://github.com/grassator/insert-text-at-cursor
6
156
  */
157
+ function canManipulateViaTextNodes(input) {
158
+ if (input.nodeName !== 'TEXTAREA') {
159
+ return false;
160
+ }
161
+ var browserSupportsTextareaTextNodes;
162
+ if (typeof browserSupportsTextareaTextNodes === 'undefined') {
163
+ var textarea = document.createElement('textarea');
164
+ textarea.value = '1';
165
+ browserSupportsTextareaTextNodes = !!textarea.firstChild;
166
+ }
167
+ return browserSupportsTextareaTextNodes;
168
+ }
169
+
170
+ var CommandsService = /** @class */ (function () {
171
+ function CommandsService() {
172
+ this.commandController = null;
173
+ this.textController = null;
174
+ }
175
+ CommandsService.prototype.init = function (commandController, textController) {
176
+ this.commandController = commandController;
177
+ this.textController = textController;
178
+ };
179
+ CommandsService.prototype.createCommandFn = function (command) {
180
+ var _this = this;
181
+ return function (context) {
182
+ if (!_this.commandController) {
183
+ throw new Error('Cannot execute command. CommandsService is not initialized');
184
+ }
185
+ _this.commandController.executeCommand(command, context);
186
+ };
187
+ };
188
+ return CommandsService;
189
+ }());
190
+ var commandsService = new CommandsService();
191
+
192
+ function useTextAreaMarkdownEditor() {
193
+ var textAreaRef = useRef(null);
194
+ var textController = useMemo(function () {
195
+ var textAreaController = new TextAreaTextController(textAreaRef);
196
+ var commandController = new CommandController(textAreaController);
197
+ commandsService.init(commandController, textAreaController);
198
+ return textAreaController;
199
+ }, [textAreaRef]);
200
+ return {
201
+ textController: textController,
202
+ ref: textAreaRef,
203
+ };
204
+ }
205
+
206
+ // A list of helpers for manipulating Markdown text.
207
+ // These helpers do not interface with a textarea.
208
+ // Check if symbol is "space" or "new line"
209
+ // c is optional because we pass a char by index,
210
+ // and theoretically index can be outside the string range. See text[i - 1] and text[i].
211
+ var isWordDelimiter = function (c) { return !!c && (c === ' ' || c.charCodeAt(0) === 10); };
7
212
  function getSurroundingWord(text, position) {
8
- if (!text)
213
+ if (text.length === 0)
9
214
  throw Error("Argument 'text' should be truthy");
10
- var isWordDelimiter = function (c) { return c === ' ' || c.charCodeAt(0) === 10; };
11
215
  // leftIndex is initialized to 0 because if selection is 0, it won't even enter the iteration
12
216
  var start = 0;
13
217
  // rightIndex is initialized to text.length because if selection is equal to text.length it won't even enter the interation
@@ -28,24 +232,32 @@ function getSurroundingWord(text, position) {
28
232
  }
29
233
  return { start: start, end: end };
30
234
  }
31
- /**
32
- * If the cursor is inside a word and (selection.start === selection.end)
33
- * returns a new Selection where the whole word is selected
34
- * @param text
35
- * @param selection
36
- */
235
+ // If the cursor is inside a word and (selection.start === selection.end)
236
+ // returns a new Selection where the whole word is selected
37
237
  function selectWord(_a) {
38
238
  var text = _a.text, selection = _a.selection;
39
- if (text && text.length && selection.start === selection.end) {
239
+ if (text.length !== 0 && selection.start === selection.end) {
40
240
  // the user is pointing to a word
41
241
  return getSurroundingWord(text, selection.start);
42
242
  }
43
243
  return selection;
44
244
  }
45
- /**
46
- * Gets the number of line-breaks that would have to be inserted before the given 'startPosition'
47
- * to make sure there's an empty line between 'startPosition' and the previous text
48
- */
245
+ // If the cursor is inside a word (selection.start === selection.end) or there is a selection
246
+ // returns a new Selection where (selection.start === selection.end) but the position is after word
247
+ function selectAfterWord(_a) {
248
+ var text = _a.text, selection = _a.selection;
249
+ if (text.length !== 0) {
250
+ // the user is pointing to a word
251
+ var end = getSurroundingWord(text, selection.end).end;
252
+ return {
253
+ start: end,
254
+ end: end,
255
+ };
256
+ }
257
+ return selection;
258
+ }
259
+ // Gets the number of line-breaks that would have to be inserted before the given 'startPosition'
260
+ // to make sure there's an empty line between 'startPosition' and the previous text
49
261
  function getBreaksNeededForEmptyLineBefore(text, startPosition) {
50
262
  if (text === void 0) { text = ''; }
51
263
  if (startPosition === 0)
@@ -70,10 +282,8 @@ function getBreaksNeededForEmptyLineBefore(text, startPosition) {
70
282
  }
71
283
  return isInFirstLine ? 0 : neededBreaks;
72
284
  }
73
- /**
74
- * Gets the number of line-breaks that would have to be inserted after the given 'startPosition'
75
- * to make sure there's an empty line between 'startPosition' and the next text
76
- */
285
+ // Gets the number of line-breaks that would have to be inserted after the given 'startPosition'
286
+ // to make sure there's an empty line between 'startPosition' and the next text
77
287
  function getBreaksNeededForEmptyLineAfter(text, startPosition) {
78
288
  if (text === void 0) { text = ''; }
79
289
  if (startPosition === text.length - 1)
@@ -108,9 +318,7 @@ function getCharactersBeforeSelection(textState, characters) {
108
318
  function getCharactersAfterSelection(textState, characters) {
109
319
  return textState.text.slice(textState.selection.end, textState.selection.end + characters);
110
320
  }
111
- /**
112
- * Inserts insertionString before each line
113
- */
321
+ // Inserts insertionString before each line
114
322
  function insertBeforeEachLine(selectedText, insertBefore) {
115
323
  var lines = selectedText.split(/\n/);
116
324
  var insertionLength = 0;
@@ -135,6 +343,7 @@ var textHelpers = /*#__PURE__*/Object.freeze({
135
343
  __proto__: null,
136
344
  getSurroundingWord: getSurroundingWord,
137
345
  selectWord: selectWord,
346
+ selectAfterWord: selectAfterWord,
138
347
  getBreaksNeededForEmptyLineBefore: getBreaksNeededForEmptyLineBefore,
139
348
  getBreaksNeededForEmptyLineAfter: getBreaksNeededForEmptyLineAfter,
140
349
  getSelectedText: getSelectedText,
@@ -143,34 +352,6 @@ var textHelpers = /*#__PURE__*/Object.freeze({
143
352
  insertBeforeEachLine: insertBeforeEachLine
144
353
  });
145
354
 
146
- function setHeader(initialState, api, prefix) {
147
- // Adjust the selection to encompass the whole word if the caret is inside one
148
- var newSelectionRange = selectWord({
149
- text: initialState.text,
150
- selection: initialState.selection,
151
- });
152
- var state1 = api.setSelectionRange(newSelectionRange);
153
- // Add the prefix to the selection
154
- var state2 = api.replaceSelection("".concat(prefix).concat(getSelectedText(state1)));
155
- // Adjust the selection to not contain the prefix
156
- api.setSelectionRange({
157
- start: state2.selection.end - getSelectedText(state1).length,
158
- end: state2.selection.end,
159
- });
160
- }
161
-
162
- var headerHelpers = /*#__PURE__*/Object.freeze({
163
- __proto__: null,
164
- setHeader: setHeader
165
- });
166
-
167
- var headingLevel1Command = {
168
- execute: function (_a) {
169
- var initialState = _a.initialState, textApi = _a.textApi;
170
- setHeader(initialState, textApi, '# ');
171
- },
172
- };
173
-
174
355
  var boldCommand = {
175
356
  shouldUndo: function (options) {
176
357
  return (getCharactersBeforeSelection(options.initialState, 2) === '**' &&
@@ -183,11 +364,11 @@ var boldCommand = {
183
364
  text: initialState.text,
184
365
  selection: initialState.selection,
185
366
  });
186
- var state1 = textApi.setSelectionRange(newSelectionRange);
367
+ var state1 = textApi.setSelection(newSelectionRange);
187
368
  // Replaces the current selection with the bold mark up
188
369
  var state2 = textApi.replaceSelection("**".concat(getSelectedText(state1), "**"));
189
370
  // Adjust the selection to not contain the **
190
- textApi.setSelectionRange({
371
+ textApi.setSelection({
191
372
  start: state2.selection.end - 2 - getSelectedText(state1).length,
192
373
  end: state2.selection.end - 2,
193
374
  });
@@ -195,17 +376,18 @@ var boldCommand = {
195
376
  undo: function (_a) {
196
377
  var initialState = _a.initialState, textApi = _a.textApi;
197
378
  var text = getSelectedText(initialState);
198
- textApi.setSelectionRange({
379
+ textApi.setSelection({
199
380
  start: initialState.selection.start - 2,
200
381
  end: initialState.selection.end + 2,
201
382
  });
202
383
  textApi.replaceSelection(text);
203
- textApi.setSelectionRange({
384
+ textApi.setSelection({
204
385
  start: initialState.selection.start - 2,
205
386
  end: initialState.selection.end - 2,
206
387
  });
207
388
  },
208
389
  };
390
+ var bold = commandsService.createCommandFn(boldCommand);
209
391
 
210
392
  var italicCommand = {
211
393
  shouldUndo: function (options) {
@@ -219,11 +401,11 @@ var italicCommand = {
219
401
  text: initialState.text,
220
402
  selection: initialState.selection,
221
403
  });
222
- var state1 = textApi.setSelectionRange(newSelectionRange);
404
+ var state1 = textApi.setSelection(newSelectionRange);
223
405
  // Replaces the current selection with the italic mark up
224
406
  var state2 = textApi.replaceSelection("*".concat(getSelectedText(state1), "*"));
225
407
  // Adjust the selection to not contain the *
226
- textApi.setSelectionRange({
408
+ textApi.setSelection({
227
409
  start: state2.selection.end - 1 - getSelectedText(state1).length,
228
410
  end: state2.selection.end - 1,
229
411
  });
@@ -231,17 +413,18 @@ var italicCommand = {
231
413
  undo: function (_a) {
232
414
  var initialState = _a.initialState, textApi = _a.textApi;
233
415
  var text = getSelectedText(initialState);
234
- textApi.setSelectionRange({
416
+ textApi.setSelection({
235
417
  start: initialState.selection.start - 1,
236
418
  end: initialState.selection.end + 1,
237
419
  });
238
420
  textApi.replaceSelection(text);
239
- textApi.setSelectionRange({
421
+ textApi.setSelection({
240
422
  start: initialState.selection.start - 1,
241
423
  end: initialState.selection.end - 1,
242
424
  });
243
425
  },
244
426
  };
427
+ var italic = commandsService.createCommandFn(italicCommand);
245
428
 
246
429
  var strikethroughCommand = {
247
430
  execute: function (_a) {
@@ -251,16 +434,17 @@ var strikethroughCommand = {
251
434
  text: initialState.text,
252
435
  selection: initialState.selection,
253
436
  });
254
- var state1 = textApi.setSelectionRange(newSelectionRange);
437
+ var state1 = textApi.setSelection(newSelectionRange);
255
438
  // Replaces the current selection with the strikethrough mark up
256
439
  var state2 = textApi.replaceSelection("~~".concat(getSelectedText(state1), "~~"));
257
440
  // Adjust the selection to not contain the ~~
258
- textApi.setSelectionRange({
441
+ textApi.setSelection({
259
442
  start: state2.selection.end - 2 - getSelectedText(state1).length,
260
443
  end: state2.selection.end - 2,
261
444
  });
262
445
  },
263
446
  };
447
+ var strikethrough = commandsService.createCommandFn(strikethroughCommand);
264
448
 
265
449
  var linkCommand = {
266
450
  execute: function (_a) {
@@ -270,16 +454,17 @@ var linkCommand = {
270
454
  text: initialState.text,
271
455
  selection: initialState.selection,
272
456
  });
273
- var state1 = textApi.setSelectionRange(newSelectionRange);
457
+ var state1 = textApi.setSelection(newSelectionRange);
274
458
  // Replaces the current selection with the bold mark up
275
459
  var state2 = textApi.replaceSelection("[".concat(getSelectedText(state1), "](url)"));
276
460
  // Adjust the selection to not contain the **
277
- textApi.setSelectionRange({
461
+ textApi.setSelection({
278
462
  start: state2.selection.end - 6 - getSelectedText(state1).length,
279
463
  end: state2.selection.end - 6,
280
464
  });
281
465
  },
282
466
  };
467
+ var link = commandsService.createCommandFn(linkCommand);
283
468
 
284
469
  var quoteCommand = {
285
470
  execute: function (_a) {
@@ -289,7 +474,7 @@ var quoteCommand = {
289
474
  text: initialState.text,
290
475
  selection: initialState.selection,
291
476
  });
292
- var state1 = textApi.setSelectionRange(newSelectionRange);
477
+ var state1 = textApi.setSelection(newSelectionRange);
293
478
  var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
294
479
  var breaksBefore = Array(breaksBeforeCount + 1).join('\n');
295
480
  var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
@@ -298,18 +483,19 @@ var quoteCommand = {
298
483
  textApi.replaceSelection("".concat(breaksBefore, "> ").concat(getSelectedText(state1)).concat(breaksAfter));
299
484
  var selectionStart = state1.selection.start + breaksBeforeCount + 2;
300
485
  var selectionEnd = selectionStart + getSelectedText(state1).length;
301
- textApi.setSelectionRange({
486
+ textApi.setSelection({
302
487
  start: selectionStart,
303
488
  end: selectionEnd,
304
489
  });
305
490
  },
306
491
  };
492
+ var quote = commandsService.createCommandFn(quoteCommand);
307
493
 
308
494
  var imageCommand = {
309
495
  execute: function (_a) {
310
496
  var initialState = _a.initialState, textApi = _a.textApi;
311
497
  // Replaces the current selection with the whole word selected
312
- var state1 = textApi.setSelectionRange(selectWord({
498
+ var state1 = textApi.setSelection(selectWord({
313
499
  text: initialState.text,
314
500
  selection: initialState.selection,
315
501
  }));
@@ -317,277 +503,13 @@ var imageCommand = {
317
503
  var imageTemplate = getSelectedText(state1) || 'https://example.com/your-image.png';
318
504
  textApi.replaceSelection("![](".concat(imageTemplate, ")"));
319
505
  // Adjust the selection to not contain the **
320
- textApi.setSelectionRange({
506
+ textApi.setSelection({
321
507
  start: state1.selection.start + 4,
322
508
  end: state1.selection.start + 4 + imageTemplate.length,
323
509
  });
324
510
  },
325
511
  };
326
-
327
- /******************************************************************************
328
- Copyright (c) Microsoft Corporation.
329
-
330
- Permission to use, copy, modify, and/or distribute this software for any
331
- purpose with or without fee is hereby granted.
332
-
333
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
334
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
335
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
336
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
337
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
338
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
339
- PERFORMANCE OF THIS SOFTWARE.
340
- ***************************************************************************** */
341
-
342
- function __awaiter(thisArg, _arguments, P, generator) {
343
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
344
- return new (P || (P = Promise))(function (resolve, reject) {
345
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
346
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
347
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
348
- step((generator = generator.apply(thisArg, _arguments || [])).next());
349
- });
350
- }
351
-
352
- function __generator(thisArg, body) {
353
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
354
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
355
- function verb(n) { return function (v) { return step([n, v]); }; }
356
- function step(op) {
357
- if (f) throw new TypeError("Generator is already executing.");
358
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
359
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
360
- if (y = 0, t) op = [op[0] & 2, t.value];
361
- switch (op[0]) {
362
- case 0: case 1: t = op; break;
363
- case 4: _.label++; return { value: op[1], done: false };
364
- case 5: _.label++; y = op[1]; op = [0]; continue;
365
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
366
- default:
367
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
368
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
369
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
370
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
371
- if (t[2]) _.ops.pop();
372
- _.trys.pop(); continue;
373
- }
374
- op = body.call(thisArg, _);
375
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
376
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
377
- }
378
- }
379
-
380
- var CommandController = /** @class */ (function () {
381
- function CommandController(textController, commandMap) {
382
- // Indicates whether there is a command currently executing
383
- this.isExecuting = false;
384
- this.textController = textController;
385
- this.commandMap = commandMap;
386
- }
387
- CommandController.prototype.executeCommand = function (commandName, context) {
388
- var _a;
389
- return __awaiter(this, void 0, void 0, function () {
390
- var command, executeOptions;
391
- return __generator(this, function (_b) {
392
- switch (_b.label) {
393
- case 0:
394
- if (this.isExecuting) {
395
- // The simplest thing to do is to ignore commands while
396
- // there is already a command executing.
397
- // The alternative would be to queue commands
398
- // but there is no guarantee that the state after one command executes will still be compatible
399
- // with the next one. In fact, it is likely not to be.
400
- return [2 /*return*/];
401
- }
402
- command = this.commandMap[commandName];
403
- if (!command) {
404
- throw new Error("Cannot execute command. Command not found: ".concat(commandName));
405
- }
406
- executeOptions = {
407
- initialState: this.textController.getState(),
408
- textApi: this.textController,
409
- };
410
- if (!(((_a = command.shouldUndo) === null || _a === void 0 ? void 0 : _a.call(command, executeOptions)) && (command === null || command === void 0 ? void 0 : command.undo))) return [3 /*break*/, 1];
411
- command.undo(executeOptions);
412
- return [3 /*break*/, 3];
413
- case 1: return [4 /*yield*/, command.execute(executeOptions)];
414
- case 2:
415
- _b.sent();
416
- _b.label = 3;
417
- case 3: return [2 /*return*/];
418
- }
419
- });
420
- });
421
- };
422
- return CommandController;
423
- }());
424
-
425
- var TextAreaTextController = /** @class */ (function () {
426
- function TextAreaTextController(textAreaRef) {
427
- this.textAreaRef = textAreaRef;
428
- }
429
- TextAreaTextController.prototype.replaceSelection = function (text) {
430
- var textArea = this.textAreaRef.current;
431
- if (!textArea) {
432
- throw new Error('TextAreaRef is not set');
433
- }
434
- insertText(textArea, text);
435
- return getStateFromTextArea(textArea);
436
- };
437
- TextAreaTextController.prototype.setSelectionRange = function (selection) {
438
- var textArea = this.textAreaRef.current;
439
- if (!textArea) {
440
- throw new Error('TextAreaRef is not set');
441
- }
442
- textArea.focus();
443
- textArea.selectionStart = selection.start;
444
- textArea.selectionEnd = selection.end;
445
- return getStateFromTextArea(textArea);
446
- };
447
- TextAreaTextController.prototype.getState = function () {
448
- var textArea = this.textAreaRef.current;
449
- if (!textArea) {
450
- throw new Error('TextAreaRef is not set');
451
- }
452
- return getStateFromTextArea(textArea);
453
- };
454
- return TextAreaTextController;
455
- }());
456
- function getStateFromTextArea(textArea) {
457
- return {
458
- selection: {
459
- start: textArea.selectionStart,
460
- end: textArea.selectionEnd,
461
- },
462
- text: textArea.value,
463
- };
464
- }
465
- /**
466
- * Inserts the given text at the cursor. If the element contains a selection, the selection
467
- * will be replaced by the text.
468
- * The MIT License
469
- * Copyright (c) 2018 Dmitriy Kubyshkin
470
- * Copied from https://github.com/grassator/insert-text-at-cursor
471
- */
472
- function insertText(input, text) {
473
- var _a;
474
- // Most of the used APIs only work with the field selected
475
- input.focus();
476
- // IE 8-10
477
- if (document.selection) {
478
- var ieRange = document.selection.createRange();
479
- ieRange.text = text;
480
- // Move cursor after the inserted text
481
- ieRange.collapse(false /* to the end */);
482
- ieRange.select();
483
- return;
484
- }
485
- // Webkit + Edge
486
- var isSuccess = document.execCommand('insertText', false, text);
487
- if (!isSuccess) {
488
- var start = input.selectionStart || 0;
489
- var end = input.selectionEnd || 0;
490
- // Firefox (non-standard method)
491
- if (typeof input.setRangeText === 'function') {
492
- input.setRangeText(text);
493
- }
494
- else {
495
- if (canManipulateViaTextNodes(input)) {
496
- var textNode = document.createTextNode(text);
497
- var node = input.firstChild;
498
- // If textarea is empty, just insert the text
499
- if (!node) {
500
- input.appendChild(textNode);
501
- }
502
- else {
503
- // Otherwise, we need to find a nodes for start and end
504
- var offset = 0;
505
- var startNode = null;
506
- var endNode = null;
507
- // To make a change we just need a Range, not a Selection
508
- var range = document.createRange();
509
- while (node && (startNode === null || endNode === null)) {
510
- var nodeLength = ((_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length) || 0;
511
- // if start of the selection falls into current node
512
- if (start >= offset && start <= offset + nodeLength) {
513
- range.setStart((startNode = node), start - offset);
514
- }
515
- // if end of the selection falls into current node
516
- if (end >= offset && end <= offset + nodeLength) {
517
- range.setEnd((endNode = node), end - offset);
518
- }
519
- offset += nodeLength;
520
- node = node.nextSibling;
521
- }
522
- // If there is some text selected, remove it as we should replace it
523
- if (start !== end) {
524
- range.deleteContents();
525
- }
526
- // Finally insert a new node. The browser will automatically
527
- // split start and end nodes into two if necessary
528
- range.insertNode(textNode);
529
- }
530
- }
531
- else {
532
- // For the text input the only way is to replace the whole value :(
533
- var value = input.value;
534
- input.value = value.slice(0, start) + text + value.slice(end);
535
- }
536
- }
537
- // Correct the cursor position to be at the end of the insertion
538
- input.setSelectionRange(start + text.length, start + text.length);
539
- // Notify any possible listeners of the change
540
- var e = document.createEvent('UIEvent');
541
- e.initEvent('input', true, false);
542
- input.dispatchEvent(e);
543
- }
544
- }
545
- /**
546
- * The MIT License
547
- * Copyright (c) 2018 Dmitriy Kubyshkin
548
- * Copied from https://github.com/grassator/insert-text-at-cursor
549
- */
550
- function canManipulateViaTextNodes(input) {
551
- if (input.nodeName !== 'TEXTAREA') {
552
- return false;
553
- }
554
- var browserSupportsTextareaTextNodes;
555
- if (typeof browserSupportsTextareaTextNodes === 'undefined') {
556
- var textarea = document.createElement('textarea');
557
- textarea.value = '1';
558
- browserSupportsTextareaTextNodes = !!textarea.firstChild;
559
- }
560
- return browserSupportsTextareaTextNodes;
561
- }
562
-
563
- function makeList(state0, textController, insertBefore) {
564
- // Adjust the selection to encompass the whole word if the caret is inside one
565
- var newSelectionRange = selectWord({
566
- text: state0.text,
567
- selection: state0.selection,
568
- });
569
- var state1 = textController.setSelectionRange(newSelectionRange);
570
- var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
571
- var breaksBefore = Array(breaksBeforeCount + 1).join('\n');
572
- var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
573
- var breaksAfter = Array(breaksAfterCount + 1).join('\n');
574
- var modifiedText = insertBeforeEachLine(getSelectedText(state1), insertBefore);
575
- textController.replaceSelection("".concat(breaksBefore).concat(modifiedText.modifiedText).concat(breaksAfter));
576
- // Specifically when the text has only one line, we can exclude the "- ", for example, from the selection
577
- var oneLinerOffset = getSelectedText(state1).indexOf('\n') === -1 ? modifiedText.insertionLength : 0;
578
- var selectionStart = state1.selection.start + breaksBeforeCount + oneLinerOffset;
579
- var selectionEnd = selectionStart + modifiedText.modifiedText.length - oneLinerOffset;
580
- // Adjust the selection to not contain the **
581
- textController.setSelectionRange({
582
- start: selectionStart,
583
- end: selectionEnd,
584
- });
585
- }
586
-
587
- var listHelpers = /*#__PURE__*/Object.freeze({
588
- __proto__: null,
589
- makeList: makeList
590
- });
512
+ var image = commandsService.createCommandFn(imageCommand);
591
513
 
592
514
  var codeCommand = {
593
515
  shouldUndo: function (options) {
@@ -601,11 +523,11 @@ var codeCommand = {
601
523
  text: initialState.text,
602
524
  selection: initialState.selection,
603
525
  });
604
- var state1 = textApi.setSelectionRange(newSelectionRange);
526
+ var state1 = textApi.setSelection(newSelectionRange);
605
527
  // Replaces the current selection with the italic mark up
606
528
  var state2 = textApi.replaceSelection("`".concat(getSelectedText(state1), "`"));
607
529
  // Adjust the selection to not contain the *
608
- textApi.setSelectionRange({
530
+ textApi.setSelection({
609
531
  start: state2.selection.end - 1 - getSelectedText(state1).length,
610
532
  end: state2.selection.end - 1,
611
533
  });
@@ -613,69 +535,83 @@ var codeCommand = {
613
535
  undo: function (_a) {
614
536
  var initialState = _a.initialState, textApi = _a.textApi;
615
537
  var text = getSelectedText(initialState);
616
- textApi.setSelectionRange({
538
+ textApi.setSelection({
617
539
  start: initialState.selection.start - 1,
618
540
  end: initialState.selection.end + 1,
619
541
  });
620
542
  textApi.replaceSelection(text);
621
- textApi.setSelectionRange({
543
+ textApi.setSelection({
622
544
  start: initialState.selection.start - 1,
623
545
  end: initialState.selection.end - 1,
624
546
  });
625
547
  },
626
548
  };
627
-
628
- function useTextAreaMarkdownEditor(options) {
629
- var textAreaRef = useRef(null);
630
- var textController = useMemo(function () {
631
- return new TextAreaTextController(textAreaRef);
632
- }, [textAreaRef]);
633
- var commandController = useMemo(function () { return new CommandController(textController, options.commandMap); }, [textAreaRef]);
634
- return {
635
- textController: textController,
636
- commandController: commandController,
637
- ref: textAreaRef,
638
- };
639
- }
549
+ var code = commandsService.createCommandFn(codeCommand);
640
550
 
641
551
  var codeBlockCommand = {
642
552
  execute: function (_a) {
643
553
  var textApi = _a.textApi, initialState = _a.initialState;
644
- return __awaiter(void 0, void 0, void 0, function () {
645
- var newSelectionRange, state1, selectionStart_1, selectionEnd_1, breaksBeforeCount, breaksBefore, breaksAfterCount, breaksAfter, selectionStart, selectionEnd;
646
- return __generator(this, function (_b) {
647
- newSelectionRange = selectWord({
648
- text: initialState.text,
649
- selection: initialState.selection,
650
- });
651
- state1 = textApi.setSelectionRange(newSelectionRange);
652
- // when there's no breaking line
653
- if (getSelectedText(state1).indexOf('\n') === -1) {
654
- textApi.replaceSelection("`".concat(getSelectedText(state1), "`"));
655
- selectionStart_1 = state1.selection.start + 1;
656
- selectionEnd_1 = selectionStart_1 + getSelectedText(state1).length;
657
- textApi.setSelectionRange({
658
- start: selectionStart_1,
659
- end: selectionEnd_1,
660
- });
661
- return [2 /*return*/];
662
- }
663
- breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
664
- breaksBefore = Array(breaksBeforeCount + 1).join('\n');
665
- breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
666
- breaksAfter = Array(breaksAfterCount + 1).join('\n');
667
- textApi.replaceSelection("".concat(breaksBefore, "```\n").concat(getSelectedText(state1), "\n```").concat(breaksAfter));
668
- selectionStart = state1.selection.start + breaksBeforeCount + 4;
669
- selectionEnd = selectionStart + getSelectedText(state1).length;
670
- textApi.setSelectionRange({
671
- start: selectionStart,
672
- end: selectionEnd,
673
- });
674
- return [2 /*return*/];
554
+ // Adjust the selection to encompass the whole word if the caret is inside one
555
+ var newSelectionRange = selectWord({
556
+ text: initialState.text,
557
+ selection: initialState.selection,
558
+ });
559
+ var state1 = textApi.setSelection(newSelectionRange);
560
+ // when there's no breaking line
561
+ if (!getSelectedText(state1).includes('\n')) {
562
+ textApi.replaceSelection("`".concat(getSelectedText(state1), "`"));
563
+ // Adjust the selection to not contain the **
564
+ var selectionStart_1 = state1.selection.start + 1;
565
+ var selectionEnd_1 = selectionStart_1 + getSelectedText(state1).length;
566
+ textApi.setSelection({
567
+ start: selectionStart_1,
568
+ end: selectionEnd_1,
675
569
  });
570
+ return;
571
+ }
572
+ var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
573
+ var breaksBefore = Array(breaksBeforeCount + 1).join('\n');
574
+ var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
575
+ var breaksAfter = Array(breaksAfterCount + 1).join('\n');
576
+ textApi.replaceSelection("".concat(breaksBefore, "```\n").concat(getSelectedText(state1), "\n```").concat(breaksAfter));
577
+ var selectionStart = state1.selection.start + breaksBeforeCount + 4;
578
+ var selectionEnd = selectionStart + getSelectedText(state1).length;
579
+ textApi.setSelection({
580
+ start: selectionStart,
581
+ end: selectionEnd,
676
582
  });
677
583
  },
678
584
  };
585
+ var codeBlock = commandsService.createCommandFn(codeBlockCommand);
586
+
587
+ function makeList(state0, textController, insertBefore) {
588
+ // Adjust the selection to encompass the whole word if the caret is inside one
589
+ var newSelectionRange = selectWord({
590
+ text: state0.text,
591
+ selection: state0.selection,
592
+ });
593
+ var state1 = textController.setSelection(newSelectionRange);
594
+ var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
595
+ var breaksBefore = Array(breaksBeforeCount + 1).join('\n');
596
+ var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
597
+ var breaksAfter = Array(breaksAfterCount + 1).join('\n');
598
+ var modifiedText = insertBeforeEachLine(getSelectedText(state1), insertBefore);
599
+ textController.replaceSelection("".concat(breaksBefore).concat(modifiedText.modifiedText).concat(breaksAfter));
600
+ // Specifically when the text has only one line, we can exclude the "- ", for example, from the selection
601
+ var oneLinerOffset = !getSelectedText(state1).includes('\n') ? modifiedText.insertionLength : 0;
602
+ var selectionStart = state1.selection.start + breaksBeforeCount + oneLinerOffset;
603
+ var selectionEnd = selectionStart + modifiedText.modifiedText.length - oneLinerOffset;
604
+ // Adjust the selection to not contain the **
605
+ textController.setSelection({
606
+ start: selectionStart,
607
+ end: selectionEnd,
608
+ });
609
+ }
610
+
611
+ var listHelpers = /*#__PURE__*/Object.freeze({
612
+ __proto__: null,
613
+ makeList: makeList
614
+ });
679
615
 
680
616
  var checkedListCommand = {
681
617
  execute: function (_a) {
@@ -683,6 +619,7 @@ var checkedListCommand = {
683
619
  makeList(initialState, textApi, function () { return "- [ ] "; });
684
620
  },
685
621
  };
622
+ var checkedList = commandsService.createCommandFn(checkedListCommand);
686
623
 
687
624
  var orderedListCommand = {
688
625
  execute: function (_a) {
@@ -690,6 +627,7 @@ var orderedListCommand = {
690
627
  makeList(initialState, textApi, function (item, index) { return "".concat(index + 1, ". "); });
691
628
  },
692
629
  };
630
+ var orderedList = commandsService.createCommandFn(orderedListCommand);
693
631
 
694
632
  var unorderedListCommand = {
695
633
  execute: function (_a) {
@@ -697,6 +635,36 @@ var unorderedListCommand = {
697
635
  makeList(initialState, textApi, '- ');
698
636
  },
699
637
  };
638
+ var unorderedList = commandsService.createCommandFn(unorderedListCommand);
639
+
640
+ function setHeader(initialState, api, prefix) {
641
+ // Adjust the selection to encompass the whole word if the caret is inside one
642
+ var newSelectionRange = selectWord({
643
+ text: initialState.text,
644
+ selection: initialState.selection,
645
+ });
646
+ var state1 = api.setSelection(newSelectionRange);
647
+ // Add the prefix to the selection
648
+ var state2 = api.replaceSelection("".concat(prefix).concat(getSelectedText(state1)));
649
+ // Adjust the selection to not contain the prefix
650
+ api.setSelection({
651
+ start: state2.selection.end - getSelectedText(state1).length,
652
+ end: state2.selection.end,
653
+ });
654
+ }
655
+
656
+ var headerHelpers = /*#__PURE__*/Object.freeze({
657
+ __proto__: null,
658
+ setHeader: setHeader
659
+ });
660
+
661
+ var headingLevel1Command = {
662
+ execute: function (_a) {
663
+ var initialState = _a.initialState, textApi = _a.textApi;
664
+ setHeader(initialState, textApi, '# ');
665
+ },
666
+ };
667
+ var headingLevel1 = commandsService.createCommandFn(headingLevel1Command);
700
668
 
701
669
  var headingLevel2Command = {
702
670
  execute: function (_a) {
@@ -704,6 +672,7 @@ var headingLevel2Command = {
704
672
  setHeader(initialState, textApi, '## ');
705
673
  },
706
674
  };
675
+ var headingLevel2 = commandsService.createCommandFn(headingLevel2Command);
707
676
 
708
677
  var headingLevel3Command = {
709
678
  execute: function (_a) {
@@ -711,6 +680,7 @@ var headingLevel3Command = {
711
680
  setHeader(initialState, textApi, '### ');
712
681
  },
713
682
  };
683
+ var headingLevel3 = commandsService.createCommandFn(headingLevel3Command);
714
684
 
715
685
  var headingLevel4Command = {
716
686
  execute: function (_a) {
@@ -718,6 +688,7 @@ var headingLevel4Command = {
718
688
  setHeader(initialState, textApi, '#### ');
719
689
  },
720
690
  };
691
+ var headingLevel4 = commandsService.createCommandFn(headingLevel4Command);
721
692
 
722
693
  var headingLevel5Command = {
723
694
  execute: function (_a) {
@@ -725,6 +696,7 @@ var headingLevel5Command = {
725
696
  setHeader(initialState, textApi, '##### ');
726
697
  },
727
698
  };
699
+ var headingLevel5 = commandsService.createCommandFn(headingLevel5Command);
728
700
 
729
701
  var headingLevel6Command = {
730
702
  execute: function (_a) {
@@ -732,6 +704,46 @@ var headingLevel6Command = {
732
704
  setHeader(initialState, textApi, '###### ');
733
705
  },
734
706
  };
707
+ var headingLevel6 = commandsService.createCommandFn(headingLevel6Command);
708
+
709
+ // attachmentCommand does to following:
710
+ // 1. Sets cursor to the end of word
711
+ // 2. Inserts string "![Uploading test 1.jpg…]()"
712
+ // 3. When request is fulfilled replace "![Uploading test 1.jpg…]()" with real markdown url
713
+ // or add the markdown url to the end of text
714
+ // 4. If request is failed delete "![Uploading test 1.jpg…]()" and console.error
715
+ var attachmentCommand = {
716
+ execute: function (_a, _b) {
717
+ var initialState = _a.initialState, textApi = _a.textApi;
718
+ var fileName = _b.fileName, fileUrlPromise = _b.fileUrlPromise;
719
+ // Replaces the current selection with the empty selection
720
+ // and sets the cursor position to the end of the word
721
+ textApi.setSelection(selectAfterWord({
722
+ text: initialState.text,
723
+ selection: initialState.selection,
724
+ }));
725
+ var uploadingString = "![Uploading ".concat(fileName, "\u2026]()");
726
+ textApi.replaceSelection(' ' + uploadingString);
727
+ fileUrlPromise
728
+ .then(function (url) {
729
+ var fileLinkString = "![".concat(fileName, "](").concat(url, ")");
730
+ if (textApi.getState().text.includes(uploadingString)) {
731
+ textApi.replaceText(uploadingString, fileLinkString);
732
+ }
733
+ else {
734
+ textApi.moveCursorToTheEnd();
735
+ textApi.replaceSelection("\n".concat(fileLinkString));
736
+ }
737
+ })
738
+ .catch(function (error) {
739
+ if (textApi.getState().text.includes(uploadingString)) {
740
+ textApi.replaceText(uploadingString, '');
741
+ }
742
+ console.error(error);
743
+ });
744
+ },
745
+ };
746
+ var attachment = commandsService.createCommandFn(attachmentCommand);
735
747
 
736
- export { CommandController, TextAreaTextController, boldCommand, checkedListCommand, codeBlockCommand, codeCommand, headerHelpers, headingLevel1Command, headingLevel2Command, headingLevel3Command, headingLevel4Command, headingLevel5Command, headingLevel6Command, imageCommand, italicCommand, linkCommand, listHelpers, orderedListCommand, quoteCommand, strikethroughCommand, textHelpers, unorderedListCommand, useTextAreaMarkdownEditor };
748
+ export { TextAreaTextController, attachment, bold, checkedList, code, codeBlock, headerHelpers, headingLevel1, headingLevel2, headingLevel3, headingLevel4, headingLevel5, headingLevel6, image, italic, link, listHelpers, orderedList, quote, strikethrough, textHelpers, unorderedList, useTextAreaMarkdownEditor };
737
749
  //# sourceMappingURL=index.js.map