react-headless-mde 1.0.3 → 2.0.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/dist/esm/index.js CHANGED
@@ -1,74 +1,25 @@
1
1
  import { useRef, useMemo } from 'react';
2
2
 
3
- var CommandController = /** @class */ (function () {
4
- function CommandController(textController) {
3
+ class CommandController {
4
+ constructor(textController, commandMap) {
5
5
  this.textController = textController;
6
+ this.commandMap = commandMap;
6
7
  }
7
- CommandController.prototype.executeCommand = function (command, context) {
8
+ executeCommand(commandName) {
8
9
  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);
10
+ const command = this.commandMap[commandName];
11
+ if (!command) {
12
+ throw new Error(`Cannot execute command. Command not found: ${commandName}`);
13
+ }
14
+ if (command.undo && ((_a = command.shouldUndo) === null || _a === void 0 ? void 0 : _a.call(command, this.textController))) {
15
+ command.undo(this.textController);
15
16
  }
16
17
  else {
17
- command.execute(executeOptions, context);
18
+ command.do(this.textController);
18
19
  }
19
- };
20
- return CommandController;
21
- }());
22
-
23
- var TextAreaTextController = /** @class */ (function () {
24
- function TextAreaTextController(textAreaRef) {
25
- this.textAreaRef = textAreaRef;
26
20
  }
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
21
  }
22
+
72
23
  /**
73
24
  * Inserts the given text at the cursor. If the element contains a selection, the selection
74
25
  * will be replaced by the text.
@@ -76,13 +27,13 @@ function getStateFromTextArea(textArea) {
76
27
  * Copyright (c) 2018 Dmitriy Kubyshkin
77
28
  * Copied from https://github.com/grassator/insert-text-at-cursor
78
29
  */
79
- function insertText(input, text) {
30
+ function insertToSelection(input, text) {
80
31
  var _a, _b, _c, _d;
81
32
  // Most of the used APIs only work with the field selected
82
33
  input.focus();
83
34
  // IE 8-10
84
35
  if (document.selection) {
85
- var ieRange = document.selection.createRange();
36
+ const ieRange = document.selection.createRange();
86
37
  ieRange.text = text;
87
38
  // Move cursor after the inserted text
88
39
  ieRange.collapse(false /* to the end */);
@@ -90,31 +41,31 @@ function insertText(input, text) {
90
41
  return;
91
42
  }
92
43
  // Webkit + Edge
93
- var isSuccess = document.execCommand('insertText', false, text);
44
+ const isSuccess = document.execCommand('insertText', false, text);
94
45
  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;
46
+ const start = (_a = input.selectionStart) !== null && _a !== void 0 ? _a : 0;
47
+ const end = (_b = input.selectionEnd) !== null && _b !== void 0 ? _b : 0;
97
48
  // Firefox (non-standard method)
98
49
  if (typeof input.setRangeText === 'function') {
99
50
  input.setRangeText(text);
100
51
  }
101
52
  else {
102
53
  if (canManipulateViaTextNodes(input)) {
103
- var textNode = document.createTextNode(text);
104
- var node = input.firstChild;
54
+ const textNode = document.createTextNode(text);
55
+ let node = input.firstChild;
105
56
  // If textarea is empty, just insert the text
106
57
  if (!node) {
107
58
  input.appendChild(textNode);
108
59
  }
109
60
  else {
110
61
  // Otherwise, we need to find a nodes for start and end
111
- var offset = 0;
112
- var startNode = null;
113
- var endNode = null;
62
+ let offset = 0;
63
+ let startNode = null;
64
+ let endNode = null;
114
65
  // To make a change we just need a Range, not a Selection
115
- var range = document.createRange();
66
+ const range = document.createRange();
116
67
  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;
68
+ const nodeLength = (_d = (_c = node.nodeValue) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
118
69
  // if start of the selection falls into current node
119
70
  if (start >= offset && start <= offset + nodeLength) {
120
71
  range.setStart((startNode = node), start - offset);
@@ -137,14 +88,14 @@ function insertText(input, text) {
137
88
  }
138
89
  else {
139
90
  // For the text input the only way is to replace the whole value :(
140
- var value = input.value;
91
+ const value = input.value;
141
92
  input.value = value.slice(0, start) + text + value.slice(end);
142
93
  }
143
94
  }
144
95
  // Correct the cursor position to be at the end of the insertion
145
96
  input.setSelectionRange(start + text.length, start + text.length);
146
97
  // Notify any possible listeners of the change
147
- var e = document.createEvent('UIEvent');
98
+ const e = document.createEvent('UIEvent');
148
99
  e.initEvent('input', true, false);
149
100
  input.dispatchEvent(e);
150
101
  }
@@ -158,117 +109,77 @@ function canManipulateViaTextNodes(input) {
158
109
  if (input.nodeName !== 'TEXTAREA') {
159
110
  return false;
160
111
  }
161
- var browserSupportsTextareaTextNodes;
112
+ let browserSupportsTextareaTextNodes;
162
113
  if (typeof browserSupportsTextareaTextNodes === 'undefined') {
163
- var textarea = document.createElement('textarea');
114
+ const textarea = document.createElement('textarea');
164
115
  textarea.value = '1';
165
116
  browserSupportsTextareaTextNodes = !!textarea.firstChild;
166
117
  }
167
118
  return browserSupportsTextareaTextNodes;
168
119
  }
169
120
 
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
121
  // A list of helpers for manipulating Markdown text.
207
122
  // 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,
123
+ // Check if char is "space" or "new line".
124
+ // Char is optional because we pass a char by index,
210
125
  // 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); };
212
- function getSurroundingWord(text, position) {
126
+ const isWordDelimiter = (char) => !!char && (char === ' ' || char.charCodeAt(0) === 10);
127
+ const getSurroundingSelection = (text, position) => {
213
128
  if (text.length === 0)
214
129
  throw Error("Argument 'text' should be truthy");
215
130
  // leftIndex is initialized to 0 because if selection is 0, it won't even enter the iteration
216
- var start = 0;
131
+ let start = 0;
217
132
  // rightIndex is initialized to text.length because if selection is equal to text.length it won't even enter the interation
218
- var end = text.length;
133
+ let end = text.length;
219
134
  // iterate to the left
220
- for (var i = position; i - 1 > -1; i--) {
135
+ for (let i = position; i - 1 > -1; i--) {
221
136
  if (isWordDelimiter(text[i - 1])) {
222
137
  start = i;
223
138
  break;
224
139
  }
225
140
  }
226
141
  // iterate to the right
227
- for (var i = position; i < text.length; i++) {
142
+ for (let i = position; i < text.length; i++) {
228
143
  if (isWordDelimiter(text[i])) {
229
144
  end = i;
230
145
  break;
231
146
  }
232
147
  }
233
- return { start: start, end: end };
234
- }
148
+ return { start, end };
149
+ };
235
150
  // If the cursor is inside a word and (selection.start === selection.end)
236
151
  // returns a new Selection where the whole word is selected
237
- function selectWord(_a) {
238
- var text = _a.text, selection = _a.selection;
152
+ function getWordSelection({ text, selection }) {
239
153
  if (text.length !== 0 && selection.start === selection.end) {
240
154
  // the user is pointing to a word
241
- return getSurroundingWord(text, selection.start);
155
+ return getSurroundingSelection(text, selection.start);
242
156
  }
243
157
  return selection;
244
158
  }
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;
159
+ // Returns a new Selection where (selection.start === selection.end) but the position is after word
160
+ function selectAfterWord({ text, selection }) {
249
161
  if (text.length !== 0) {
250
162
  // the user is pointing to a word
251
- var end = getSurroundingWord(text, selection.end).end;
163
+ const { end } = getSurroundingSelection(text, selection.end);
252
164
  return {
253
165
  start: end,
254
- end: end,
166
+ end,
255
167
  };
256
168
  }
257
169
  return selection;
258
170
  }
259
171
  // Gets the number of line-breaks that would have to be inserted before the given 'startPosition'
260
172
  // to make sure there's an empty line between 'startPosition' and the previous text
261
- function getBreaksNeededForEmptyLineBefore(text, startPosition) {
262
- if (text === void 0) { text = ''; }
173
+ function getBreaksNeededForEmptyLineBefore(text = '', startPosition) {
263
174
  if (startPosition === 0)
264
175
  return 0;
265
- // rules:
176
+ // Rules:
266
177
  // - If we're in the first line, no breaks are needed
267
178
  // - Otherwise there must be 2 breaks before the previous character. Depending on how many breaks exist already, we
268
179
  // may need to insert 0, 1 or 2 breaks
269
- var neededBreaks = 2;
270
- var isInFirstLine = true;
271
- for (var i = startPosition - 1; i >= 0 && neededBreaks >= 0; i--) {
180
+ let neededBreaks = 2;
181
+ let isInFirstLine = true;
182
+ for (let i = startPosition - 1; i >= 0 && neededBreaks >= 0; i--) {
272
183
  switch (text.charCodeAt(i)) {
273
184
  case 32: // blank space
274
185
  continue;
@@ -284,17 +195,16 @@ function getBreaksNeededForEmptyLineBefore(text, startPosition) {
284
195
  }
285
196
  // Gets the number of line-breaks that would have to be inserted after the given 'startPosition'
286
197
  // to make sure there's an empty line between 'startPosition' and the next text
287
- function getBreaksNeededForEmptyLineAfter(text, startPosition) {
288
- if (text === void 0) { text = ''; }
198
+ function getBreaksNeededForEmptyLineAfter(text = '', startPosition) {
289
199
  if (startPosition === text.length - 1)
290
200
  return 0;
291
- // rules:
201
+ // Rules:
292
202
  // - If we're in the first line, no breaks are needed
293
203
  // - Otherwise there must be 2 breaks before the previous character. Depending on how many breaks exist already, we
294
204
  // may need to insert 0, 1 or 2 breaks
295
- var neededBreaks = 2;
296
- var isInLastLine = true;
297
- for (var i = startPosition; i < text.length && neededBreaks >= 0; i++) {
205
+ let neededBreaks = 2;
206
+ let isInLastLine = true;
207
+ for (let i = startPosition; i < text.length && neededBreaks >= 0; i++) {
298
208
  switch (text.charCodeAt(i)) {
299
209
  case 32:
300
210
  continue;
@@ -312,438 +222,389 @@ function getBreaksNeededForEmptyLineAfter(text, startPosition) {
312
222
  function getSelectedText(textSection) {
313
223
  return textSection.text.slice(textSection.selection.start, textSection.selection.end);
314
224
  }
315
- function getCharactersBeforeSelection(textState, characters) {
225
+ function getStringBeforeSelection(textState, characters) {
316
226
  return textState.text.slice(textState.selection.start - characters, textState.selection.start);
317
227
  }
318
- function getCharactersAfterSelection(textState, characters) {
228
+ function getStringAfterSelection(textState, characters) {
319
229
  return textState.text.slice(textState.selection.end, textState.selection.end + characters);
320
230
  }
321
231
  // Inserts insertionString before each line
322
232
  function insertBeforeEachLine(selectedText, insertBefore) {
323
- var lines = selectedText.split(/\n/);
324
- var insertionLength = 0;
325
- var modifiedText = lines
326
- .map(function (item, index) {
233
+ const lines = selectedText.split(/\n/);
234
+ let insertionLength = 0;
235
+ const modifiedText = lines
236
+ .map((item, index) => {
327
237
  if (typeof insertBefore === 'string') {
328
238
  insertionLength += insertBefore.length;
329
239
  return insertBefore + item;
330
240
  }
331
241
  else if (typeof insertBefore === 'function') {
332
- var insertionResult = insertBefore(item, index);
242
+ const insertionResult = insertBefore(item, index);
333
243
  insertionLength += insertionResult.length;
334
244
  return insertBefore(item, index) + item;
335
245
  }
336
246
  throw Error('insertion is expected to be either a string or a function');
337
247
  })
338
248
  .join('\n');
339
- return { modifiedText: modifiedText, insertionLength: insertionLength };
249
+ return { modifiedText, insertionLength };
340
250
  }
341
251
 
342
- var textHelpers = /*#__PURE__*/Object.freeze({
252
+ var selectionAndText = /*#__PURE__*/Object.freeze({
343
253
  __proto__: null,
344
- getSurroundingWord: getSurroundingWord,
345
- selectWord: selectWord,
254
+ getWordSelection: getWordSelection,
346
255
  selectAfterWord: selectAfterWord,
347
256
  getBreaksNeededForEmptyLineBefore: getBreaksNeededForEmptyLineBefore,
348
257
  getBreaksNeededForEmptyLineAfter: getBreaksNeededForEmptyLineAfter,
349
258
  getSelectedText: getSelectedText,
350
- getCharactersBeforeSelection: getCharactersBeforeSelection,
351
- getCharactersAfterSelection: getCharactersAfterSelection,
259
+ getStringBeforeSelection: getStringBeforeSelection,
260
+ getStringAfterSelection: getStringAfterSelection,
352
261
  insertBeforeEachLine: insertBeforeEachLine
353
262
  });
354
263
 
355
- var boldCommand = {
356
- shouldUndo: function (options) {
357
- return (getCharactersBeforeSelection(options.initialState, 2) === '**' &&
358
- getCharactersAfterSelection(options.initialState, 2) === '**');
359
- },
360
- execute: function (_a) {
361
- var initialState = _a.initialState, textApi = _a.textApi;
264
+ function getStateFromTextArea(textArea) {
265
+ return {
266
+ selection: {
267
+ start: textArea.selectionStart,
268
+ end: textArea.selectionEnd,
269
+ },
270
+ text: textArea.value,
271
+ };
272
+ }
273
+ class TextareaController {
274
+ constructor(textAreaRef) {
275
+ this.textAreaRef = textAreaRef;
276
+ }
277
+ get textArea() {
278
+ var _a;
279
+ const textArea = (_a = this.textAreaRef) === null || _a === void 0 ? void 0 : _a.current;
280
+ if (!textArea) {
281
+ throw new Error('No TextAreaRef');
282
+ }
283
+ return textArea;
284
+ }
285
+ selectWordByCursor() {
286
+ const initialState = this.getState();
362
287
  // Adjust the selection to encompass the whole word if the caret is inside one
363
- var newSelectionRange = selectWord({
288
+ const newSelectionRange = getWordSelection({
364
289
  text: initialState.text,
365
290
  selection: initialState.selection,
366
291
  });
367
- var state1 = textApi.setSelection(newSelectionRange);
368
- // Replaces the current selection with the bold mark up
369
- var state2 = textApi.replaceSelection("**".concat(getSelectedText(state1), "**"));
292
+ return this.setSelection(newSelectionRange);
293
+ }
294
+ setSelection({ start, end }) {
295
+ const textArea = this.textArea;
296
+ textArea.focus();
297
+ textArea.selectionStart = start;
298
+ textArea.selectionEnd = end;
299
+ return getStateFromTextArea(textArea);
300
+ }
301
+ replaceSelection(text) {
302
+ const textArea = this.textArea;
303
+ insertToSelection(textArea, text);
304
+ return getStateFromTextArea(textArea);
305
+ }
306
+ replaceText(searchString, replaceString) {
307
+ const textArea = this.textArea;
308
+ const startIndex = textArea.value.indexOf(searchString);
309
+ if (startIndex === -1)
310
+ return getStateFromTextArea(textArea);
311
+ this.setSelection({ start: startIndex, end: startIndex + searchString.length });
312
+ this.replaceSelection(replaceString);
313
+ return getStateFromTextArea(textArea);
314
+ }
315
+ wrapSelection(prefix, suffix) {
316
+ const state1 = this.getState();
317
+ // Replaces the current selection with new string
318
+ const state2 = this.replaceSelection(`${prefix}${getSelectedText(state1)}${suffix}`);
370
319
  // Adjust the selection to not contain the **
371
- textApi.setSelection({
372
- start: state2.selection.end - 2 - getSelectedText(state1).length,
373
- end: state2.selection.end - 2,
320
+ return this.setSelection({
321
+ start: state2.selection.end - prefix.length - getSelectedText(state1).length,
322
+ end: state2.selection.end - suffix.length,
374
323
  });
375
- },
376
- undo: function (_a) {
377
- var initialState = _a.initialState, textApi = _a.textApi;
378
- var text = getSelectedText(initialState);
379
- textApi.setSelection({
380
- start: initialState.selection.start - 2,
381
- end: initialState.selection.end + 2,
324
+ }
325
+ unwrapSelection(prefixLength, suffixLength) {
326
+ const initialState = this.getState();
327
+ const text = getSelectedText(initialState);
328
+ this.setSelection({
329
+ start: initialState.selection.start - prefixLength,
330
+ end: initialState.selection.end + suffixLength,
382
331
  });
383
- textApi.replaceSelection(text);
384
- textApi.setSelection({
385
- start: initialState.selection.start - 2,
386
- end: initialState.selection.end - 2,
332
+ this.replaceSelection(text);
333
+ return this.setSelection({
334
+ start: initialState.selection.start - prefixLength,
335
+ end: initialState.selection.end - suffixLength,
387
336
  });
337
+ }
338
+ getState() {
339
+ const textArea = this.textArea;
340
+ return getStateFromTextArea(textArea);
341
+ }
342
+ moveCursorToTheEnd() {
343
+ const textArea = this.textArea;
344
+ textArea.focus();
345
+ textArea.selectionEnd = textArea.selectionStart = textArea.value.length;
346
+ return getStateFromTextArea(textArea);
347
+ }
348
+ }
349
+
350
+ function useTextAreaMarkdownEditor(options) {
351
+ const textAreaRef = useRef(null);
352
+ return useMemo(() => {
353
+ const textController = new TextareaController(textAreaRef);
354
+ const commandController = new CommandController(textController, options.commandMap);
355
+ return {
356
+ ref: textAreaRef,
357
+ textController,
358
+ commandController,
359
+ };
360
+ }, [textAreaRef]);
361
+ }
362
+
363
+ function makeList(state0, textController, insertBefore) {
364
+ // Adjust the selection to encompass the whole word if the caret is inside one
365
+ const newSelectionRange = getWordSelection({
366
+ text: state0.text,
367
+ selection: state0.selection,
368
+ });
369
+ const state1 = textController.setSelection(newSelectionRange);
370
+ const breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
371
+ const breaksBefore = Array(breaksBeforeCount + 1).join('\n');
372
+ const breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
373
+ const breaksAfter = Array(breaksAfterCount + 1).join('\n');
374
+ const modifiedText = insertBeforeEachLine(getSelectedText(state1), insertBefore);
375
+ textController.replaceSelection(`${breaksBefore}${modifiedText.modifiedText}${breaksAfter}`);
376
+ // Specifically when the text has only one line, we can exclude the "- ", for example, from the selection
377
+ const oneLinerOffset = !getSelectedText(state1).includes('\n') ? modifiedText.insertionLength : 0;
378
+ const selectionStart = state1.selection.start + breaksBeforeCount + oneLinerOffset;
379
+ const selectionEnd = selectionStart + modifiedText.modifiedText.length - oneLinerOffset;
380
+ // Adjust the selection to not contain the **
381
+ textController.setSelection({
382
+ start: selectionStart,
383
+ end: selectionEnd,
384
+ });
385
+ }
386
+
387
+ var list = /*#__PURE__*/Object.freeze({
388
+ __proto__: null,
389
+ makeList: makeList
390
+ });
391
+
392
+ function setHeader(initialState, api, prefix) {
393
+ // Adjust the selection to encompass the whole word if the caret is inside one
394
+ const newSelectionRange = getWordSelection({
395
+ text: initialState.text,
396
+ selection: initialState.selection,
397
+ });
398
+ const state1 = api.setSelection(newSelectionRange);
399
+ // Add the prefix to the selection
400
+ const state2 = api.replaceSelection(`${prefix}${getSelectedText(state1)}`);
401
+ // Adjust the selection to not contain the prefix
402
+ api.setSelection({
403
+ start: state2.selection.end - getSelectedText(state1).length,
404
+ end: state2.selection.end,
405
+ });
406
+ }
407
+
408
+ var header = /*#__PURE__*/Object.freeze({
409
+ __proto__: null,
410
+ setHeader: setHeader
411
+ });
412
+
413
+ const boldCommand = {
414
+ shouldUndo: textCtrl => {
415
+ const wordSelectionState = textCtrl.selectWordByCursor();
416
+ return (getStringBeforeSelection(wordSelectionState, 2) === '**' &&
417
+ getStringAfterSelection(wordSelectionState, 2) === '**');
418
+ },
419
+ do: textCtrl => {
420
+ textCtrl.wrapSelection('**', '**');
421
+ },
422
+ undo: textCtrl => {
423
+ textCtrl.unwrapSelection(2, 2);
388
424
  },
389
425
  };
390
- var bold = commandsService.createCommandFn(boldCommand);
391
426
 
392
- var italicCommand = {
393
- shouldUndo: function (options) {
394
- return (getCharactersBeforeSelection(options.initialState, 1) === '*' &&
395
- getCharactersAfterSelection(options.initialState, 1) === '*');
427
+ const italicCommand = {
428
+ shouldUndo: textCtrl => {
429
+ const wordSelectionState = textCtrl.selectWordByCursor();
430
+ return (getStringBeforeSelection(wordSelectionState, 1) === '*' && getStringAfterSelection(wordSelectionState, 1) === '*');
396
431
  },
397
- execute: function (_a) {
398
- var initialState = _a.initialState, textApi = _a.textApi;
399
- // Adjust the selection to encompass the whole word if the caret is inside one
400
- var newSelectionRange = selectWord({
401
- text: initialState.text,
402
- selection: initialState.selection,
403
- });
404
- var state1 = textApi.setSelection(newSelectionRange);
405
- // Replaces the current selection with the italic mark up
406
- var state2 = textApi.replaceSelection("*".concat(getSelectedText(state1), "*"));
407
- // Adjust the selection to not contain the *
408
- textApi.setSelection({
409
- start: state2.selection.end - 1 - getSelectedText(state1).length,
410
- end: state2.selection.end - 1,
411
- });
432
+ do(textCtrl) {
433
+ textCtrl.wrapSelection('*', '*');
412
434
  },
413
- undo: function (_a) {
414
- var initialState = _a.initialState, textApi = _a.textApi;
415
- var text = getSelectedText(initialState);
416
- textApi.setSelection({
417
- start: initialState.selection.start - 1,
418
- end: initialState.selection.end + 1,
419
- });
420
- textApi.replaceSelection(text);
421
- textApi.setSelection({
422
- start: initialState.selection.start - 1,
423
- end: initialState.selection.end - 1,
424
- });
435
+ undo(textCtrl) {
436
+ textCtrl.unwrapSelection(1, 1);
425
437
  },
426
438
  };
427
- var italic = commandsService.createCommandFn(italicCommand);
428
439
 
429
- var strikethroughCommand = {
430
- execute: function (_a) {
431
- var initialState = _a.initialState, textApi = _a.textApi;
432
- // Adjust the selection to encompass the whole word if the caret is inside one
433
- var newSelectionRange = selectWord({
434
- text: initialState.text,
435
- selection: initialState.selection,
436
- });
437
- var state1 = textApi.setSelection(newSelectionRange);
438
- // Replaces the current selection with the strikethrough mark up
439
- var state2 = textApi.replaceSelection("~~".concat(getSelectedText(state1), "~~"));
440
- // Adjust the selection to not contain the ~~
441
- textApi.setSelection({
442
- start: state2.selection.end - 2 - getSelectedText(state1).length,
443
- end: state2.selection.end - 2,
444
- });
440
+ const strikethroughCommand = {
441
+ shouldUndo(textCtrl) {
442
+ const wordSelectionState = textCtrl.selectWordByCursor();
443
+ return (getStringBeforeSelection(wordSelectionState, 2) === '~~' &&
444
+ getStringAfterSelection(wordSelectionState, 2) === '~~');
445
+ },
446
+ do(textCtrl) {
447
+ textCtrl.wrapSelection('~~', '~~');
448
+ },
449
+ undo(textCtrl) {
450
+ textCtrl.unwrapSelection(2, 2);
445
451
  },
446
452
  };
447
- var strikethrough = commandsService.createCommandFn(strikethroughCommand);
448
453
 
449
- var linkCommand = {
450
- execute: function (_a) {
451
- var initialState = _a.initialState, textApi = _a.textApi;
454
+ const linkCommand = {
455
+ do: textApi => {
452
456
  // Adjust the selection to encompass the whole word if the caret is inside one
453
- var newSelectionRange = selectWord({
454
- text: initialState.text,
455
- selection: initialState.selection,
456
- });
457
- var state1 = textApi.setSelection(newSelectionRange);
457
+ const wordSelectionState = textApi.selectWordByCursor();
458
458
  // Replaces the current selection with the bold mark up
459
- var state2 = textApi.replaceSelection("[".concat(getSelectedText(state1), "](url)"));
459
+ const state2 = textApi.replaceSelection(`[${getSelectedText(wordSelectionState)}](url)`);
460
460
  // Adjust the selection to not contain the **
461
461
  textApi.setSelection({
462
- start: state2.selection.end - 6 - getSelectedText(state1).length,
462
+ start: state2.selection.end - 6 - getSelectedText(wordSelectionState).length,
463
463
  end: state2.selection.end - 6,
464
464
  });
465
465
  },
466
466
  };
467
- var link = commandsService.createCommandFn(linkCommand);
468
467
 
469
- var quoteCommand = {
470
- execute: function (_a) {
471
- var initialState = _a.initialState, textApi = _a.textApi;
468
+ // Todo: try to rewrite with setHeader()
469
+ const quoteCommand = {
470
+ do: textApi => {
472
471
  // Adjust the selection to encompass the whole word if the caret is inside one
473
- var newSelectionRange = selectWord({
474
- text: initialState.text,
475
- selection: initialState.selection,
476
- });
477
- var state1 = textApi.setSelection(newSelectionRange);
478
- var breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
479
- var breaksBefore = Array(breaksBeforeCount + 1).join('\n');
480
- var breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
481
- var breaksAfter = Array(breaksAfterCount + 1).join('\n');
472
+ const state = textApi.selectWordByCursor();
473
+ const breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state.text, state.selection.start);
474
+ const breaksBefore = Array(breaksBeforeCount + 1).join('\n');
475
+ const breaksAfterCount = getBreaksNeededForEmptyLineAfter(state.text, state.selection.end);
476
+ const breaksAfter = Array(breaksAfterCount + 1).join('\n');
482
477
  // Replaces the current selection with the quote mark up
483
- textApi.replaceSelection("".concat(breaksBefore, "> ").concat(getSelectedText(state1)).concat(breaksAfter));
484
- var selectionStart = state1.selection.start + breaksBeforeCount + 2;
485
- var selectionEnd = selectionStart + getSelectedText(state1).length;
478
+ textApi.replaceSelection(`${breaksBefore}> ${getSelectedText(state)}${breaksAfter}`);
479
+ const selectionStart = state.selection.start + breaksBeforeCount + 2;
480
+ const selectionEnd = selectionStart + getSelectedText(state).length;
486
481
  textApi.setSelection({
487
482
  start: selectionStart,
488
483
  end: selectionEnd,
489
484
  });
490
485
  },
491
486
  };
492
- var quote = commandsService.createCommandFn(quoteCommand);
493
487
 
494
- var imageCommand = {
495
- execute: function (_a) {
496
- var initialState = _a.initialState, textApi = _a.textApi;
497
- // Replaces the current selection with the whole word selected
498
- var state1 = textApi.setSelection(selectWord({
499
- text: initialState.text,
500
- selection: initialState.selection,
501
- }));
502
- // Replaces the current selection with the image
503
- var imageTemplate = getSelectedText(state1) || 'https://example.com/your-image.png';
504
- textApi.replaceSelection("![](".concat(imageTemplate, ")"));
505
- // Adjust the selection to not contain the **
506
- textApi.setSelection({
507
- start: state1.selection.start + 4,
508
- end: state1.selection.start + 4 + imageTemplate.length,
509
- });
488
+ const codeCommand = {
489
+ shouldUndo: textCtrl => {
490
+ const wordSelectionState = textCtrl.selectWordByCursor();
491
+ return (getStringBeforeSelection(wordSelectionState, 1) === '`' && getStringAfterSelection(wordSelectionState, 1) === '`');
510
492
  },
511
- };
512
- var image = commandsService.createCommandFn(imageCommand);
513
-
514
- var codeCommand = {
515
- shouldUndo: function (options) {
516
- return (getCharactersBeforeSelection(options.initialState, 1) === '`' &&
517
- getCharactersAfterSelection(options.initialState, 1) === '`');
493
+ do(textCtrl) {
494
+ textCtrl.wrapSelection('`', '`');
518
495
  },
519
- execute: function (_a) {
520
- var initialState = _a.initialState, textApi = _a.textApi;
521
- // Adjust the selection to encompass the whole word if the caret is inside one
522
- var newSelectionRange = selectWord({
523
- text: initialState.text,
524
- selection: initialState.selection,
525
- });
526
- var state1 = textApi.setSelection(newSelectionRange);
527
- // Replaces the current selection with the italic mark up
528
- var state2 = textApi.replaceSelection("`".concat(getSelectedText(state1), "`"));
529
- // Adjust the selection to not contain the *
530
- textApi.setSelection({
531
- start: state2.selection.end - 1 - getSelectedText(state1).length,
532
- end: state2.selection.end - 1,
533
- });
534
- },
535
- undo: function (_a) {
536
- var initialState = _a.initialState, textApi = _a.textApi;
537
- var text = getSelectedText(initialState);
538
- textApi.setSelection({
539
- start: initialState.selection.start - 1,
540
- end: initialState.selection.end + 1,
541
- });
542
- textApi.replaceSelection(text);
543
- textApi.setSelection({
544
- start: initialState.selection.start - 1,
545
- end: initialState.selection.end - 1,
546
- });
496
+ undo(textCtrl) {
497
+ textCtrl.unwrapSelection(1, 1);
547
498
  },
548
499
  };
549
- var code = commandsService.createCommandFn(codeCommand);
550
500
 
551
- var codeBlockCommand = {
552
- execute: function (_a) {
553
- var textApi = _a.textApi, initialState = _a.initialState;
501
+ const codeBlockCommand = {
502
+ do: textApi => {
554
503
  // 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);
504
+ const state1 = textApi.selectWordByCursor();
560
505
  // when there's no breaking line
561
506
  if (!getSelectedText(state1).includes('\n')) {
562
- textApi.replaceSelection("`".concat(getSelectedText(state1), "`"));
507
+ textApi.replaceSelection(`\`${getSelectedText(state1)}\``);
563
508
  // Adjust the selection to not contain the **
564
- var selectionStart_1 = state1.selection.start + 1;
565
- var selectionEnd_1 = selectionStart_1 + getSelectedText(state1).length;
509
+ const selectionStart = state1.selection.start + 1;
510
+ const selectionEnd = selectionStart + getSelectedText(state1).length;
566
511
  textApi.setSelection({
567
- start: selectionStart_1,
568
- end: selectionEnd_1,
512
+ start: selectionStart,
513
+ end: selectionEnd,
569
514
  });
570
515
  return;
571
516
  }
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;
517
+ const breaksBeforeCount = getBreaksNeededForEmptyLineBefore(state1.text, state1.selection.start);
518
+ const breaksBefore = Array(breaksBeforeCount + 1).join('\n');
519
+ const breaksAfterCount = getBreaksNeededForEmptyLineAfter(state1.text, state1.selection.end);
520
+ const breaksAfter = Array(breaksAfterCount + 1).join('\n');
521
+ textApi.replaceSelection(`${breaksBefore}\`\`\`\n${getSelectedText(state1)}\n\`\`\`${breaksAfter}`);
522
+ const selectionStart = state1.selection.start + breaksBeforeCount + 4;
523
+ const selectionEnd = selectionStart + getSelectedText(state1).length;
579
524
  textApi.setSelection({
580
525
  start: selectionStart,
581
526
  end: selectionEnd,
582
527
  });
583
528
  },
584
529
  };
585
- var codeBlock = commandsService.createCommandFn(codeBlockCommand);
586
530
 
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
- });
615
-
616
- var checkedListCommand = {
617
- execute: function (_a) {
618
- var initialState = _a.initialState, textApi = _a.textApi;
619
- makeList(initialState, textApi, function () { return "- [ ] "; });
531
+ const checkedListCommand = {
532
+ do: textApi => {
533
+ const initialState = textApi.getState();
534
+ makeList(initialState, textApi, () => `- [ ] `);
620
535
  },
621
536
  };
622
- var checkedList = commandsService.createCommandFn(checkedListCommand);
623
537
 
624
- var orderedListCommand = {
625
- execute: function (_a) {
626
- var initialState = _a.initialState, textApi = _a.textApi;
627
- makeList(initialState, textApi, function (item, index) { return "".concat(index + 1, ". "); });
538
+ const orderedListCommand = {
539
+ do: textApi => {
540
+ const initialState = textApi.getState();
541
+ makeList(initialState, textApi, (item, index) => `${index + 1}. `);
628
542
  },
629
543
  };
630
- var orderedList = commandsService.createCommandFn(orderedListCommand);
631
544
 
632
- var unorderedListCommand = {
633
- execute: function (_a) {
634
- var initialState = _a.initialState, textApi = _a.textApi;
545
+ const unorderedListCommand = {
546
+ do: textApi => {
547
+ const initialState = textApi.getState();
635
548
  makeList(initialState, textApi, '- ');
636
549
  },
637
550
  };
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
551
 
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, '# ');
552
+ const imageCommand = {
553
+ do: textApi => {
554
+ // Replaces the current selection with the whole word selected
555
+ const wordSelectionState = textApi.selectWordByCursor();
556
+ // Replaces the current selection with the image
557
+ const imageTemplate = getSelectedText(wordSelectionState) || 'https://example.com/your-image.png';
558
+ textApi.replaceSelection(`![](${imageTemplate})`);
559
+ // Adjust the selection to not contain the **
560
+ textApi.setSelection({
561
+ start: wordSelectionState.selection.start + 4,
562
+ end: wordSelectionState.selection.start + 4 + imageTemplate.length,
563
+ });
665
564
  },
666
565
  };
667
- var headingLevel1 = commandsService.createCommandFn(headingLevel1Command);
668
566
 
669
- var headingLevel2Command = {
670
- execute: function (_a) {
671
- var initialState = _a.initialState, textApi = _a.textApi;
672
- setHeader(initialState, textApi, '## ');
567
+ const headingLevel1Command = {
568
+ do: textCtrl => {
569
+ const initialState = textCtrl.getState();
570
+ setHeader(initialState, textCtrl, '# ');
673
571
  },
674
572
  };
675
- var headingLevel2 = commandsService.createCommandFn(headingLevel2Command);
676
573
 
677
- var headingLevel3Command = {
678
- execute: function (_a) {
679
- var initialState = _a.initialState, textApi = _a.textApi;
680
- setHeader(initialState, textApi, '### ');
574
+ const headingLevel2Command = {
575
+ do: textCtrl => {
576
+ const initialState = textCtrl.getState();
577
+ setHeader(initialState, textCtrl, '## ');
681
578
  },
682
579
  };
683
- var headingLevel3 = commandsService.createCommandFn(headingLevel3Command);
684
580
 
685
- var headingLevel4Command = {
686
- execute: function (_a) {
687
- var initialState = _a.initialState, textApi = _a.textApi;
688
- setHeader(initialState, textApi, '#### ');
581
+ const headingLevel3Command = {
582
+ do: textCtrl => {
583
+ const initialState = textCtrl.getState();
584
+ setHeader(initialState, textCtrl, '### ');
689
585
  },
690
586
  };
691
- var headingLevel4 = commandsService.createCommandFn(headingLevel4Command);
692
587
 
693
- var headingLevel5Command = {
694
- execute: function (_a) {
695
- var initialState = _a.initialState, textApi = _a.textApi;
696
- setHeader(initialState, textApi, '##### ');
588
+ const headingLevel4Command = {
589
+ do: textCtrl => {
590
+ const initialState = textCtrl.getState();
591
+ setHeader(initialState, textCtrl, '#### ');
697
592
  },
698
593
  };
699
- var headingLevel5 = commandsService.createCommandFn(headingLevel5Command);
700
594
 
701
- var headingLevel6Command = {
702
- execute: function (_a) {
703
- var initialState = _a.initialState, textApi = _a.textApi;
704
- setHeader(initialState, textApi, '###### ');
595
+ const headingLevel5Command = {
596
+ do: textCtrl => {
597
+ const initialState = textCtrl.getState();
598
+ setHeader(initialState, textCtrl, '##### ');
705
599
  },
706
600
  };
707
- var headingLevel6 = commandsService.createCommandFn(headingLevel6Command);
708
601
 
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
- });
602
+ const headingLevel6Command = {
603
+ do: textCtrl => {
604
+ const initialState = textCtrl.getState();
605
+ setHeader(initialState, textCtrl, '###### ');
744
606
  },
745
607
  };
746
- var attachment = commandsService.createCommandFn(attachmentCommand);
747
608
 
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 };
609
+ export { TextareaController, boldCommand, checkedListCommand, codeBlockCommand, codeCommand, header as headerHelpers, headingLevel1Command, headingLevel2Command, headingLevel3Command, headingLevel4Command, headingLevel5Command, headingLevel6Command, imageCommand, italicCommand, linkCommand, list as listHelpers, orderedListCommand, quoteCommand, strikethroughCommand, selectionAndText as textHelpers, unorderedListCommand, useTextAreaMarkdownEditor };
749
610
  //# sourceMappingURL=index.js.map