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