diff options
author | Martin Hanzel <mhanzel@gitlab.com> | 2019-06-13 09:17:41 +0300 |
---|---|---|
committer | Martin Hanzel <mhanzel@gitlab.com> | 2019-06-13 09:17:41 +0300 |
commit | fe07976daa98a377a0d7892c1fbd5e40794a8426 (patch) | |
tree | d3951936f73a197eba93363e7101b136ba18883c | |
parent | 7d6b3cc3db40b569b968424b4c9fbd9480dfbec3 (diff) |
Add setRangeText custom implemenetation
-rw-r--r-- | app/assets/javascripts/gl_form.js | 160 | ||||
-rw-r--r-- | app/assets/javascripts/helpers/indent_helper.js | 69 |
2 files changed, 143 insertions, 86 deletions
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index b470c075da6..d941db6d0c0 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -3,9 +3,9 @@ import autosize from 'autosize'; import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete'; import dropzoneInput from './dropzone_input'; import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown'; -import IndentHelper from './helpers/indent_helper'; -import { getPlatformLeaderKeyHTML, keystroke } from './lib/utils/common_utils'; -import UndoStack from './lib/utils/undo_stack'; +// import IndentHelper from './helpers/indent_helper'; +import { getPlatformLeaderKeyHTML } from './lib/utils/common_utils'; +// import UndoStack from './lib/utils/undo_stack'; export default class GLForm { constructor(form, enableGFM = {}) { @@ -23,8 +23,8 @@ export default class GLForm { } }); - this.undoStack = new UndoStack(); - this.indentHelper = new IndentHelper(this.textarea[0]); + // this.undoStack = new UndoStack(); + // this.indentHelper = new IndentHelper(this.textarea[0]); // This shows the indent help text in the Wiki editor, since it's still a // HAML component @@ -100,83 +100,83 @@ export default class GLForm { clearEventListeners() { this.textarea.off('focus'); this.textarea.off('blur'); - this.textarea.off('keydown'); + // this.textarea.off('keydown'); removeMarkdownListeners(this.form); } - setState(state) { - const selection = [this.textarea[0].selectionStart, this.textarea[0].selectionEnd]; - this.textarea.val(state); - this.textarea[0].setSelectionRange(selection[0], selection[1]); - } - - handleUndo(event) { - /* - Custom undo/redo stack. - We need this because the toolbar buttons and indentation helpers mess with - the browser's native undo/redo capability. - */ - - const content = this.textarea.val(); - const { selectionStart, selectionEnd } = this.textarea[0]; - const stack = this.undoStack; - - if (stack.isEmpty()) { - // ==== Save initial state in undo history ==== - stack.save(content); - } - - if (keystroke(event, 'Leader+Z')) { - // ==== Undo ==== - event.preventDefault(); - stack.save(content); - if (stack.canUndo()) { - this.setState(stack.undo()); - } - } else if (keystroke(event, 'Leader+Shift+Z') || keystroke(event, 'Leader+Y')) { - // ==== Redo ==== - event.preventDefault(); - if (stack.canRedo()) { - this.setState(stack.redo()); - } - } else if (keystroke(event, 'Space') || keystroke(event, 'Enter')) { - // ==== Save after finishing a word ==== - stack.save(content); - } else if (selectionStart !== selectionEnd) { - // ==== Save if killing a large selection ==== - stack.save(content); - } else if (content === '') { - // ==== Save if deleting everything ==== - stack.save(''); - } else { - // ==== Save after 1 second of inactivity ==== - stack.scheduleSave(content); - } - } - - handleIndent(event) { - if (keystroke(event, 'Leader+[')) { - // ==== Unindent selected lines ==== - event.preventDefault(); - this.indentHelper.unindent(); - } else if (keystroke(event, 'Leader+]')) { - // ==== Indent selected lines ==== - event.preventDefault(); - this.indentHelper.indent(); - } else if (keystroke(event, 'Enter')) { - // ==== Auto-indent new lines ==== - event.preventDefault(); - this.indentHelper.newline(); - } else if (keystroke(event, 'Backspace')) { - // ==== Auto-delete indents at the beginning of the line ==== - this.indentHelper.backspace(event); - } - } - - handleKeyShortcuts(event) { - this.handleIndent(event); - this.handleUndo(event); - } + // setState(state) { + // const selection = [this.textarea[0].selectionStart, this.textarea[0].selectionEnd]; + // this.textarea.val(state); + // this.textarea[0].setSelectionRange(selection[0], selection[1]); + // } + + // handleUndo(event) { + // /* + // Custom undo/redo stack. + // We need this because the toolbar buttons and indentation helpers mess with + // the browser's native undo/redo capability. + // */ + // + // const content = this.textarea.val(); + // const { selectionStart, selectionEnd } = this.textarea[0]; + // const stack = this.undoStack; + // + // if (stack.isEmpty()) { + // // ==== Save initial state in undo history ==== + // stack.save(content); + // } + // + // if (keystroke(event, 'Leader+Z')) { + // // ==== Undo ==== + // event.preventDefault(); + // stack.save(content); + // if (stack.canUndo()) { + // this.setState(stack.undo()); + // } + // } else if (keystroke(event, 'Leader+Shift+Z') || keystroke(event, 'Leader+Y')) { + // // ==== Redo ==== + // event.preventDefault(); + // if (stack.canRedo()) { + // this.setState(stack.redo()); + // } + // } else if (keystroke(event, 'Space') || keystroke(event, 'Enter')) { + // // ==== Save after finishing a word ==== + // stack.save(content); + // } else if (selectionStart !== selectionEnd) { + // // ==== Save if killing a large selection ==== + // stack.save(content); + // } else if (content === '') { + // // ==== Save if deleting everything ==== + // stack.save(''); + // } else { + // // ==== Save after 1 second of inactivity ==== + // stack.scheduleSave(content); + // } + // } + // + // handleIndent(event) { + // if (keystroke(event, 'Leader+[')) { + // // ==== Unindent selected lines ==== + // event.preventDefault(); + // this.indentHelper.unindent(); + // } else if (keystroke(event, 'Leader+]')) { + // // ==== Indent selected lines ==== + // event.preventDefault(); + // this.indentHelper.indent(); + // } else if (keystroke(event, 'Enter')) { + // // ==== Auto-indent new lines ==== + // event.preventDefault(); + // this.indentHelper.newline(); + // } else if (keystroke(event, 'Backspace')) { + // // ==== Auto-delete indents at the beginning of the line ==== + // this.indentHelper.backspace(event); + // } + // } + // + // handleKeyShortcuts(event) { + // this.handleIndent(event); + // this.handleUndo(event); + // } addEventListeners() { this.textarea.on('focus', function focusTextArea() { @@ -189,6 +189,6 @@ export default class GLForm { .closest('.md-area') .removeClass('is-focused'); }); - this.textarea.on('keydown', e => this.handleKeyShortcuts(e.originalEvent)); + // this.textarea.on('keydown', e => this.handleKeyShortcuts(e.originalEvent)); } } diff --git a/app/assets/javascripts/helpers/indent_helper.js b/app/assets/javascripts/helpers/indent_helper.js index 3641551471f..203ec493a26 100644 --- a/app/assets/javascripts/helpers/indent_helper.js +++ b/app/assets/javascripts/helpers/indent_helper.js @@ -32,6 +32,63 @@ export default class IndentHelper { } /** + * Re-implementation of textarea's setRangeText method, because IE/Edge don't support it. + * + * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea%2Finput-setrangetext + */ + setRangeText(replacement, start, end, selectMode) { + // Disable eslint to remain as faithful as possible to the above linked spec + /* eslint-disable no-param-reassign, no-case-declarations */ + const text = this.element.value; + + if (start > end) { + throw new RangeError('setRangeText: start index must be less than or equal to end index'); + } + + // Clamp to [0, len] + start = Math.max(0, Math.min(start, text.length)); + end = Math.max(0, Math.min(end, text.length)); + + let selection = { start: this.element.selectionStart, end: this.element.selectionEnd }; + + this.element.value = text.slice(0, start) + replacement + text.slice(end); + + const newLength = replacement.length; + const newEnd = start + newLength; + + switch (selectMode) { + case 'select': + selection = { start, newEnd }; + break; + case 'start': + selection = { start, end: start }; + break; + case 'end': + selection = { start: newEnd, end: newEnd }; + break; + case 'preserve': + default: + const oldLength = end - start; + const delta = newLength - oldLength; + if (selection.start > end) { + selection.start += delta; + } else if (selection.start > start) { + selection.start = start; + } + if (selection.end > end) { + selection.end += delta; + } else if (selection.end > start) { + selection.end = newEnd; + } + } + + this.element.setSelectionRange(selection.start, selection.end); + + /* eslint-enable no-param-reassign, no-case-declarations */ + } + + + /** * Returns an array of lines in the textarea, with information about their * start/end offsets and whether they are included in the current selection. */ @@ -65,11 +122,11 @@ export default class IndentHelper { // Special case: if cursor is at the beginning of the line, move it one // indent right. const line = selectedLines[0]; - this.element.setRangeText(this.seq, line.start, line.start, 'end'); + this.setRangeText(this.seq, line.start, line.start, 'end'); } else { selectedLines.reverse(); selectedLines.forEach(line => { - this.element.setRangeText(INDENT_SEQUENCE, line.start, line.start, 'preserve'); + this.setRangeText(INDENT_SEQUENCE, line.start, line.start, 'preserve'); }); } } @@ -83,7 +140,7 @@ export default class IndentHelper { lines .filter(line => line.text.startsWith(this.seq)) .forEach(line => { - this.element.setRangeText('', line.start, line.start + this.seq.length, 'preserve'); + this.setRangeText('', line.start, line.start + this.seq.length, 'preserve'); }); } @@ -95,13 +152,13 @@ export default class IndentHelper { if (this.isRangeSelection()) { // Manually kill the selection before calculating the indent - this.element.setRangeText('', start, end, 'start'); + this.setRangeText('', start, end, 'start'); } // Auto-indent the next line const currentLine = this.splitLines().find(line => line.end >= start); const spaces = countLeftSpaces(currentLine.text); - this.element.setRangeText(`\n${' '.repeat(spaces)}`, start, start, 'end'); + this.setRangeText(`\n${' '.repeat(spaces)}`, start, start, 'end'); } /** @@ -123,7 +180,7 @@ export default class IndentHelper { if (spacesToDelete === 0) { spacesToDelete = this.seq.length; } - this.element.setRangeText('', start - spacesToDelete, start, 'start'); + this.setRangeText('', start - spacesToDelete, start, 'start'); } } } |