react-headless-mde 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.
@@ -0,0 +1,741 @@
1
+ import { useRef, useMemo } from 'react';
2
+
3
+ /**
4
+ * A list of helpers for manipulating markdown text.
5
+ * These helpers do not interface with a textarea. For that, see
6
+ */
7
+ function getSurroundingWord(text, position) {
8
+ if (!text)
9
+ throw Error("Argument 'text' should be truthy");
10
+ var isWordDelimiter = function (c) { return c === " " || c.charCodeAt(0) === 10; };
11
+ // leftIndex is initialized to 0 because if selection is 0, it won't even enter the iteration
12
+ var start = 0;
13
+ // rightIndex is initialized to text.length because if selection is equal to text.length it won't even enter the interation
14
+ var end = text.length;
15
+ // iterate to the left
16
+ for (var i = position; i - 1 > -1; i--) {
17
+ if (isWordDelimiter(text[i - 1])) {
18
+ start = i;
19
+ break;
20
+ }
21
+ }
22
+ // iterate to the right
23
+ for (var i = position; i < text.length; i++) {
24
+ if (isWordDelimiter(text[i])) {
25
+ end = i;
26
+ break;
27
+ }
28
+ }
29
+ return { start: start, end: end };
30
+ }
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
+ */
37
+ function selectWord(_a) {
38
+ var text = _a.text, selection = _a.selection;
39
+ if (text && text.length && selection.start === selection.end) {
40
+ // the user is pointing to a word
41
+ return getSurroundingWord(text, selection.start);
42
+ }
43
+ return selection;
44
+ }
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
+ */
49
+ function getBreaksNeededForEmptyLineBefore(text, startPosition) {
50
+ if (text === void 0) { text = ""; }
51
+ if (startPosition === 0)
52
+ return 0;
53
+ // rules:
54
+ // - If we're in the first line, no breaks are needed
55
+ // - Otherwise there must be 2 breaks before the previous character. Depending on how many breaks exist already, we
56
+ // may need to insert 0, 1 or 2 breaks
57
+ var neededBreaks = 2;
58
+ var isInFirstLine = true;
59
+ for (var i = startPosition - 1; i >= 0 && neededBreaks >= 0; i--) {
60
+ switch (text.charCodeAt(i)) {
61
+ case 32: // blank space
62
+ continue;
63
+ case 10: // line break
64
+ neededBreaks--;
65
+ isInFirstLine = false;
66
+ break;
67
+ default:
68
+ return neededBreaks;
69
+ }
70
+ }
71
+ return isInFirstLine ? 0 : neededBreaks;
72
+ }
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
+ */
77
+ function getBreaksNeededForEmptyLineAfter(text, startPosition) {
78
+ if (text === void 0) { text = ""; }
79
+ if (startPosition === text.length - 1)
80
+ return 0;
81
+ // rules:
82
+ // - If we're in the first line, no breaks are needed
83
+ // - Otherwise there must be 2 breaks before the previous character. Depending on how many breaks exist already, we
84
+ // may need to insert 0, 1 or 2 breaks
85
+ var neededBreaks = 2;
86
+ var isInLastLine = true;
87
+ for (var i = startPosition; i < text.length && neededBreaks >= 0; i++) {
88
+ switch (text.charCodeAt(i)) {
89
+ case 32:
90
+ continue;
91
+ case 10: {
92
+ neededBreaks--;
93
+ isInLastLine = false;
94
+ break;
95
+ }
96
+ default:
97
+ return neededBreaks;
98
+ }
99
+ }
100
+ return isInLastLine ? 0 : neededBreaks;
101
+ }
102
+ function getSelectedText(textSection) {
103
+ return textSection.text.slice(textSection.selection.start, textSection.selection.end);
104
+ }
105
+ function getCharactersBeforeSelection(textState, characters) {
106
+ return textState.text.slice(textState.selection.start - characters, textState.selection.start);
107
+ }
108
+ function getCharactersAfterSelection(textState, characters) {
109
+ return textState.text.slice(textState.selection.end, textState.selection.end + characters);
110
+ }
111
+ /**
112
+ * Inserts insertionString before each line
113
+ */
114
+ function insertBeforeEachLine(selectedText, insertBefore) {
115
+ var lines = selectedText.split(/\n/);
116
+ var insertionLength = 0;
117
+ var modifiedText = lines
118
+ .map(function (item, index) {
119
+ if (typeof insertBefore === "string") {
120
+ insertionLength += insertBefore.length;
121
+ return insertBefore + item;
122
+ }
123
+ else if (typeof insertBefore === "function") {
124
+ var insertionResult = insertBefore(item, index);
125
+ insertionLength += insertionResult.length;
126
+ return insertBefore(item, index) + item;
127
+ }
128
+ throw Error("insertion is expected to be either a string or a function");
129
+ })
130
+ .join("\n");
131
+ return { modifiedText: modifiedText, insertionLength: insertionLength };
132
+ }
133
+
134
+ var textHelpers = /*#__PURE__*/Object.freeze({
135
+ __proto__: null,
136
+ getSurroundingWord: getSurroundingWord,
137
+ selectWord: selectWord,
138
+ getBreaksNeededForEmptyLineBefore: getBreaksNeededForEmptyLineBefore,
139
+ getBreaksNeededForEmptyLineAfter: getBreaksNeededForEmptyLineAfter,
140
+ getSelectedText: getSelectedText,
141
+ getCharactersBeforeSelection: getCharactersBeforeSelection,
142
+ getCharactersAfterSelection: getCharactersAfterSelection,
143
+ insertBeforeEachLine: insertBeforeEachLine
144
+ });
145
+
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("" + prefix + 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
+ var boldCommand = {
175
+ shouldUndo: function (options) {
176
+ return (getCharactersBeforeSelection(options.initialState, 2) === "**" &&
177
+ getCharactersAfterSelection(options.initialState, 2) === "**");
178
+ },
179
+ execute: function (_a) {
180
+ var initialState = _a.initialState, textApi = _a.textApi;
181
+ // Adjust the selection to encompass the whole word if the caret is inside one
182
+ var newSelectionRange = selectWord({
183
+ text: initialState.text,
184
+ selection: initialState.selection
185
+ });
186
+ var state1 = textApi.setSelectionRange(newSelectionRange);
187
+ // Replaces the current selection with the bold mark up
188
+ var state2 = textApi.replaceSelection("**" + getSelectedText(state1) + "**");
189
+ // Adjust the selection to not contain the **
190
+ textApi.setSelectionRange({
191
+ start: state2.selection.end - 2 - getSelectedText(state1).length,
192
+ end: state2.selection.end - 2
193
+ });
194
+ },
195
+ undo: function (_a) {
196
+ var initialState = _a.initialState, textApi = _a.textApi;
197
+ var text = getSelectedText(initialState);
198
+ textApi.setSelectionRange({
199
+ start: initialState.selection.start - 2,
200
+ end: initialState.selection.end + 2
201
+ });
202
+ textApi.replaceSelection(text);
203
+ textApi.setSelectionRange({
204
+ start: initialState.selection.start - 2,
205
+ end: initialState.selection.end - 2
206
+ });
207
+ }
208
+ };
209
+
210
+ var italicCommand = {
211
+ shouldUndo: function (options) {
212
+ return (getCharactersBeforeSelection(options.initialState, 1) === "*" &&
213
+ getCharactersAfterSelection(options.initialState, 1) === "*");
214
+ },
215
+ execute: function (_a) {
216
+ var initialState = _a.initialState, textApi = _a.textApi;
217
+ // Adjust the selection to encompass the whole word if the caret is inside one
218
+ var newSelectionRange = selectWord({
219
+ text: initialState.text,
220
+ selection: initialState.selection
221
+ });
222
+ var state1 = textApi.setSelectionRange(newSelectionRange);
223
+ // Replaces the current selection with the italic mark up
224
+ var state2 = textApi.replaceSelection("*" + getSelectedText(state1) + "*");
225
+ // Adjust the selection to not contain the *
226
+ textApi.setSelectionRange({
227
+ start: state2.selection.end - 1 - getSelectedText(state1).length,
228
+ end: state2.selection.end - 1
229
+ });
230
+ },
231
+ undo: function (_a) {
232
+ var initialState = _a.initialState, textApi = _a.textApi;
233
+ var text = getSelectedText(initialState);
234
+ textApi.setSelectionRange({
235
+ start: initialState.selection.start - 1,
236
+ end: initialState.selection.end + 1
237
+ });
238
+ textApi.replaceSelection(text);
239
+ textApi.setSelectionRange({
240
+ start: initialState.selection.start - 1,
241
+ end: initialState.selection.end - 1
242
+ });
243
+ }
244
+ };
245
+
246
+ var strikethroughCommand = {
247
+ execute: function (_a) {
248
+ var initialState = _a.initialState, textApi = _a.textApi;
249
+ // Adjust the selection to encompass the whole word if the caret is inside one
250
+ var newSelectionRange = selectWord({
251
+ text: initialState.text,
252
+ selection: initialState.selection
253
+ });
254
+ var state1 = textApi.setSelectionRange(newSelectionRange);
255
+ // Replaces the current selection with the strikethrough mark up
256
+ var state2 = textApi.replaceSelection("~~" + getSelectedText(state1) + "~~");
257
+ // Adjust the selection to not contain the ~~
258
+ textApi.setSelectionRange({
259
+ start: state2.selection.end - 2 - getSelectedText(state1).length,
260
+ end: state2.selection.end - 2
261
+ });
262
+ }
263
+ };
264
+
265
+ var linkCommand = {
266
+ execute: function (_a) {
267
+ var initialState = _a.initialState, textApi = _a.textApi;
268
+ // Adjust the selection to encompass the whole word if the caret is inside one
269
+ var newSelectionRange = selectWord({
270
+ text: initialState.text,
271
+ selection: initialState.selection
272
+ });
273
+ var state1 = textApi.setSelectionRange(newSelectionRange);
274
+ // Replaces the current selection with the bold mark up
275
+ var state2 = textApi.replaceSelection("[" + getSelectedText(state1) + "](url)");
276
+ // Adjust the selection to not contain the **
277
+ textApi.setSelectionRange({
278
+ start: state2.selection.end - 6 - getSelectedText(state1).length,
279
+ end: state2.selection.end - 6
280
+ });
281
+ }
282
+ };
283
+
284
+ var quoteCommand = {
285
+ execute: function (_a) {
286
+ var initialState = _a.initialState, textApi = _a.textApi;
287
+ // Adjust the selection to encompass the whole word if the caret is inside one
288
+ var newSelectionRange = selectWord({
289
+ text: initialState.text,
290
+ selection: initialState.selection
291
+ });
292
+ var state1 = textApi.setSelectionRange(newSelectionRange);
293
+ var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
294
+ var breaksBefore = Array(breaksBeforeCount + 1).join("\n");
295
+ var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
296
+ var breaksAfter = Array(breaksAfterCount + 1).join("\n");
297
+ // Replaces the current selection with the quote mark up
298
+ textApi.replaceSelection(breaksBefore + "> " + getSelectedText(state1) + breaksAfter);
299
+ var selectionStart = state1.selection.start + breaksBeforeCount + 2;
300
+ var selectionEnd = selectionStart + getSelectedText(state1).length;
301
+ textApi.setSelectionRange({
302
+ start: selectionStart,
303
+ end: selectionEnd
304
+ });
305
+ }
306
+ };
307
+
308
+ var imageCommand = {
309
+ execute: function (_a) {
310
+ var initialState = _a.initialState, textApi = _a.textApi;
311
+ // Replaces the current selection with the whole word selected
312
+ var state1 = textApi.setSelectionRange(selectWord({
313
+ text: initialState.text,
314
+ selection: initialState.selection
315
+ }));
316
+ // Replaces the current selection with the image
317
+ var imageTemplate = getSelectedText(state1) || "https://example.com/your-image.png";
318
+ textApi.replaceSelection("![](" + imageTemplate + ")");
319
+ // Adjust the selection to not contain the **
320
+ textApi.setSelectionRange({
321
+ start: state1.selection.start + 4,
322
+ end: state1.selection.start + 4 + imageTemplate.length
323
+ });
324
+ }
325
+ };
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 (_) 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
+ /**
383
+ * Indicates whether there is a command currently executing
384
+ */
385
+ this.isExecuting = false;
386
+ this.textController = textController;
387
+ this.commandMap = commandMap;
388
+ }
389
+ CommandController.prototype.executeCommand = function (commandName, context) {
390
+ var _a;
391
+ return __awaiter(this, void 0, void 0, function () {
392
+ var command, executeOptions;
393
+ return __generator(this, function (_b) {
394
+ switch (_b.label) {
395
+ case 0:
396
+ if (this.isExecuting) {
397
+ // The simplest thing to do is to ignore commands while
398
+ // there is already a command execu
399
+ // ting. The alternative would be to queue commands
400
+ // but there is no guarantee that the state after one command executes will still be compatible
401
+ // with the next one. In fact, it is likely not to be.
402
+ return [2 /*return*/];
403
+ }
404
+ command = this.commandMap[commandName];
405
+ if (!command) {
406
+ throw new Error("Cannot execute command. Command not found: " + commandName);
407
+ }
408
+ executeOptions = {
409
+ initialState: this.textController.getState(),
410
+ textApi: this.textController
411
+ };
412
+ 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];
413
+ command.undo(executeOptions);
414
+ return [3 /*break*/, 3];
415
+ case 1: return [4 /*yield*/, command.execute(executeOptions)];
416
+ case 2:
417
+ _b.sent();
418
+ _b.label = 3;
419
+ case 3: return [2 /*return*/];
420
+ }
421
+ });
422
+ });
423
+ };
424
+ return CommandController;
425
+ }());
426
+
427
+ var TextAreaTextController = /** @class */ (function () {
428
+ function TextAreaTextController(textAreaRef) {
429
+ this.textAreaRef = textAreaRef;
430
+ }
431
+ TextAreaTextController.prototype.replaceSelection = function (text) {
432
+ var textArea = this.textAreaRef.current;
433
+ if (!textArea) {
434
+ throw new Error("TextAreaRef is not set");
435
+ }
436
+ insertText(textArea, text);
437
+ return getStateFromTextArea(textArea);
438
+ };
439
+ TextAreaTextController.prototype.setSelectionRange = function (selection) {
440
+ var textArea = this.textAreaRef.current;
441
+ if (!textArea) {
442
+ throw new Error("TextAreaRef is not set");
443
+ }
444
+ textArea.focus();
445
+ textArea.selectionStart = selection.start;
446
+ textArea.selectionEnd = selection.end;
447
+ return getStateFromTextArea(textArea);
448
+ };
449
+ TextAreaTextController.prototype.getState = function () {
450
+ var textArea = this.textAreaRef.current;
451
+ if (!textArea) {
452
+ throw new Error("TextAreaRef is not set");
453
+ }
454
+ return getStateFromTextArea(textArea);
455
+ };
456
+ return TextAreaTextController;
457
+ }());
458
+ function getStateFromTextArea(textArea) {
459
+ return {
460
+ selection: {
461
+ start: textArea.selectionStart,
462
+ end: textArea.selectionEnd
463
+ },
464
+ text: textArea.value
465
+ };
466
+ }
467
+ /**
468
+ * Inserts the given text at the cursor. If the element contains a selection, the selection
469
+ * will be replaced by the text.
470
+ * The MIT License
471
+ * Copyright (c) 2018 Dmitriy Kubyshkin
472
+ * Copied from https://github.com/grassator/insert-text-at-cursor
473
+ */
474
+ function insertText(input, text) {
475
+ var _a;
476
+ // Most of the used APIs only work with the field selected
477
+ input.focus();
478
+ // IE 8-10
479
+ if (document.selection) {
480
+ var ieRange = document.selection.createRange();
481
+ ieRange.text = text;
482
+ // Move cursor after the inserted text
483
+ ieRange.collapse(false /* to the end */);
484
+ ieRange.select();
485
+ return;
486
+ }
487
+ // Webkit + Edge
488
+ var isSuccess = document.execCommand("insertText", false, text);
489
+ if (!isSuccess) {
490
+ var start = input.selectionStart || 0;
491
+ var end = input.selectionEnd || 0;
492
+ // Firefox (non-standard method)
493
+ if (typeof input.setRangeText === "function") {
494
+ input.setRangeText(text);
495
+ }
496
+ else {
497
+ if (canManipulateViaTextNodes(input)) {
498
+ var textNode = document.createTextNode(text);
499
+ var node = input.firstChild;
500
+ // If textarea is empty, just insert the text
501
+ if (!node) {
502
+ input.appendChild(textNode);
503
+ }
504
+ else {
505
+ // Otherwise, we need to find a nodes for start and end
506
+ var offset = 0;
507
+ var startNode = null;
508
+ var endNode = null;
509
+ // To make a change we just need a Range, not a Selection
510
+ var range = document.createRange();
511
+ while (node && (startNode === null || endNode === null)) {
512
+ var nodeLength = ((_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length) || 0;
513
+ // if start of the selection falls into current node
514
+ if (start >= offset && start <= offset + nodeLength) {
515
+ range.setStart((startNode = node), start - offset);
516
+ }
517
+ // if end of the selection falls into current node
518
+ if (end >= offset && end <= offset + nodeLength) {
519
+ range.setEnd((endNode = node), end - offset);
520
+ }
521
+ offset += nodeLength;
522
+ node = node.nextSibling;
523
+ }
524
+ // If there is some text selected, remove it as we should replace it
525
+ if (start !== end) {
526
+ range.deleteContents();
527
+ }
528
+ // Finally insert a new node. The browser will automatically
529
+ // split start and end nodes into two if necessary
530
+ range.insertNode(textNode);
531
+ }
532
+ }
533
+ else {
534
+ // For the text input the only way is to replace the whole value :(
535
+ var value = input.value;
536
+ input.value = value.slice(0, start) + text + value.slice(end);
537
+ }
538
+ }
539
+ // Correct the cursor position to be at the end of the insertion
540
+ input.setSelectionRange(start + text.length, start + text.length);
541
+ // Notify any possible listeners of the change
542
+ var e = document.createEvent("UIEvent");
543
+ e.initEvent("input", true, false);
544
+ input.dispatchEvent(e);
545
+ }
546
+ }
547
+ /**
548
+ * The MIT License
549
+ * Copyright (c) 2018 Dmitriy Kubyshkin
550
+ * Copied from https://github.com/grassator/insert-text-at-cursor
551
+ */
552
+ function canManipulateViaTextNodes(input) {
553
+ if (input.nodeName !== "TEXTAREA") {
554
+ return false;
555
+ }
556
+ var browserSupportsTextareaTextNodes;
557
+ if (typeof browserSupportsTextareaTextNodes === "undefined") {
558
+ var textarea = document.createElement("textarea");
559
+ textarea.value = "1";
560
+ browserSupportsTextareaTextNodes = !!textarea.firstChild;
561
+ }
562
+ return browserSupportsTextareaTextNodes;
563
+ }
564
+
565
+ function makeList(state0, textController, insertBefore) {
566
+ // Adjust the selection to encompass the whole word if the caret is inside one
567
+ var newSelectionRange = selectWord({
568
+ text: state0.text,
569
+ selection: state0.selection
570
+ });
571
+ var state1 = textController.setSelectionRange(newSelectionRange);
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
+ var modifiedText = insertBeforeEachLine(getSelectedText(state1), insertBefore);
577
+ textController.replaceSelection("" + breaksBefore + modifiedText.modifiedText + breaksAfter);
578
+ // Specifically when the text has only one line, we can exclude the "- ", for example, from the selection
579
+ var oneLinerOffset = getSelectedText(state1).indexOf("\n") === -1
580
+ ? modifiedText.insertionLength
581
+ : 0;
582
+ var selectionStart = state1.selection.start + breaksBeforeCount + oneLinerOffset;
583
+ var selectionEnd = selectionStart + modifiedText.modifiedText.length - oneLinerOffset;
584
+ // Adjust the selection to not contain the **
585
+ textController.setSelectionRange({
586
+ start: selectionStart,
587
+ end: selectionEnd
588
+ });
589
+ }
590
+
591
+ var listHelpers = /*#__PURE__*/Object.freeze({
592
+ __proto__: null,
593
+ makeList: makeList
594
+ });
595
+
596
+ var codeCommand = {
597
+ shouldUndo: function (options) {
598
+ return (getCharactersBeforeSelection(options.initialState, 1) === "`" &&
599
+ getCharactersAfterSelection(options.initialState, 1) === "`");
600
+ },
601
+ execute: function (_a) {
602
+ var initialState = _a.initialState, textApi = _a.textApi;
603
+ // Adjust the selection to encompass the whole word if the caret is inside one
604
+ var newSelectionRange = selectWord({
605
+ text: initialState.text,
606
+ selection: initialState.selection
607
+ });
608
+ var state1 = textApi.setSelectionRange(newSelectionRange);
609
+ // Replaces the current selection with the italic mark up
610
+ var state2 = textApi.replaceSelection("`" + getSelectedText(state1) + "`");
611
+ // Adjust the selection to not contain the *
612
+ textApi.setSelectionRange({
613
+ start: state2.selection.end - 1 - getSelectedText(state1).length,
614
+ end: state2.selection.end - 1
615
+ });
616
+ },
617
+ undo: function (_a) {
618
+ var initialState = _a.initialState, textApi = _a.textApi;
619
+ var text = getSelectedText(initialState);
620
+ textApi.setSelectionRange({
621
+ start: initialState.selection.start - 1,
622
+ end: initialState.selection.end + 1
623
+ });
624
+ textApi.replaceSelection(text);
625
+ textApi.setSelectionRange({
626
+ start: initialState.selection.start - 1,
627
+ end: initialState.selection.end - 1
628
+ });
629
+ }
630
+ };
631
+
632
+ function useTextAreaMarkdownEditor(options) {
633
+ var textAreaRef = useRef(null);
634
+ var textController = useMemo(function () {
635
+ return new TextAreaTextController(textAreaRef);
636
+ }, [textAreaRef]);
637
+ var commandController = useMemo(function () { return new CommandController(textController, options.commandMap); }, [textAreaRef]);
638
+ return {
639
+ textController: textController,
640
+ commandController: commandController,
641
+ ref: textAreaRef
642
+ };
643
+ }
644
+
645
+ var codeBlockCommand = {
646
+ execute: function (_a) {
647
+ var textApi = _a.textApi, initialState = _a.initialState;
648
+ return __awaiter(void 0, void 0, void 0, function () {
649
+ var newSelectionRange, state1, selectionStart_1, selectionEnd_1, breaksBeforeCount, breaksBefore, breaksAfterCount, breaksAfter, selectionStart, selectionEnd;
650
+ return __generator(this, function (_b) {
651
+ newSelectionRange = selectWord({
652
+ text: initialState.text,
653
+ selection: initialState.selection
654
+ });
655
+ state1 = textApi.setSelectionRange(newSelectionRange);
656
+ // when there's no breaking line
657
+ if (getSelectedText(state1).indexOf("\n") === -1) {
658
+ textApi.replaceSelection("`" + getSelectedText(state1) + "`");
659
+ selectionStart_1 = state1.selection.start + 1;
660
+ selectionEnd_1 = selectionStart_1 + getSelectedText(state1).length;
661
+ textApi.setSelectionRange({
662
+ start: selectionStart_1,
663
+ end: selectionEnd_1
664
+ });
665
+ return [2 /*return*/];
666
+ }
667
+ breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
668
+ breaksBefore = Array(breaksBeforeCount + 1).join("\n");
669
+ breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
670
+ breaksAfter = Array(breaksAfterCount + 1).join("\n");
671
+ textApi.replaceSelection(breaksBefore + "```\n" + getSelectedText(state1) + "\n```" + breaksAfter);
672
+ selectionStart = state1.selection.start + breaksBeforeCount + 4;
673
+ selectionEnd = selectionStart + getSelectedText(state1).length;
674
+ textApi.setSelectionRange({
675
+ start: selectionStart,
676
+ end: selectionEnd
677
+ });
678
+ return [2 /*return*/];
679
+ });
680
+ });
681
+ }
682
+ };
683
+
684
+ var checkedListCommand = {
685
+ execute: function (_a) {
686
+ var initialState = _a.initialState, textApi = _a.textApi;
687
+ makeList(initialState, textApi, function () { return "- [ ] "; });
688
+ }
689
+ };
690
+
691
+ var orderedListCommand = {
692
+ execute: function (_a) {
693
+ var initialState = _a.initialState, textApi = _a.textApi;
694
+ makeList(initialState, textApi, function (item, index) { return index + 1 + ". "; });
695
+ }
696
+ };
697
+
698
+ var unorderedListCommand = {
699
+ execute: function (_a) {
700
+ var initialState = _a.initialState, textApi = _a.textApi;
701
+ makeList(initialState, textApi, "- ");
702
+ }
703
+ };
704
+
705
+ var headingLevel2Command = {
706
+ execute: function (_a) {
707
+ var initialState = _a.initialState, textApi = _a.textApi;
708
+ setHeader(initialState, textApi, "## ");
709
+ }
710
+ };
711
+
712
+ var headingLevel3Command = {
713
+ execute: function (_a) {
714
+ var initialState = _a.initialState, textApi = _a.textApi;
715
+ setHeader(initialState, textApi, "### ");
716
+ }
717
+ };
718
+
719
+ var headingLevel4Command = {
720
+ execute: function (_a) {
721
+ var initialState = _a.initialState, textApi = _a.textApi;
722
+ setHeader(initialState, textApi, "#### ");
723
+ }
724
+ };
725
+
726
+ var headingLevel5Command = {
727
+ execute: function (_a) {
728
+ var initialState = _a.initialState, textApi = _a.textApi;
729
+ setHeader(initialState, textApi, "##### ");
730
+ }
731
+ };
732
+
733
+ var headingLevel6Command = {
734
+ execute: function (_a) {
735
+ var initialState = _a.initialState, textApi = _a.textApi;
736
+ setHeader(initialState, textApi, "###### ");
737
+ }
738
+ };
739
+
740
+ export { CommandController, TextAreaTextController, boldCommand, checkedListCommand, codeBlockCommand, codeCommand, headerHelpers, headingLevel1Command, headingLevel2Command, headingLevel3Command, headingLevel4Command, headingLevel5Command, headingLevel6Command, imageCommand, italicCommand, linkCommand, listHelpers, orderedListCommand, quoteCommand, strikethroughCommand, textHelpers, unorderedListCommand, useTextAreaMarkdownEditor };
741
+ //# sourceMappingURL=index.js.map