UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

131 lines (122 loc) 3.66 kB
/** * A class to help with text selection and manipulation of textareas and inputs */ export class TextSelection { elm: HTMLTextAreaElement; start: number; end: number; value: string; constructor(elm: HTMLTextAreaElement) { const { selectionStart, selectionEnd } = elm; this.elm = elm; this.start = selectionStart; this.end = selectionEnd; this.value = this.elm.value; } /** * Sets the position of the text selection within an element. * * @param {number} [start] - The start position of the selection. * @param {number} [end] - The end position of the selection. * @returns {Object} - Returns the instance of the object. */ position(start?: number, end?: number) { const { selectionStart, selectionEnd } = this.elm; this.start = typeof start === 'number' && !isNaN(start) ? start : selectionStart; this.end = typeof end === 'number' && !isNaN(end) ? end : selectionEnd; this.elm.selectionStart = this.start; this.elm.selectionEnd = this.end; return this; } /** * Inserts text at the current cursor position * @param text the text to insert * @returns the TextSelection instance */ insertText(text: string) { // Most of the used APIs only work with the field selected this.elm.focus(); this.elm.setRangeText(text); this.value = this.elm.value; this.position(); return this; } /** * Gets the selected text value from a textarea / input * @param start number start index * @param end number end index * @returns the selected text */ getSelectedValue(start?: number, end?: number) { const { selectionStart, selectionEnd } = this.elm; return this.value.slice( typeof start === 'number' && !isNaN(start) ? start : selectionStart, typeof end === 'number' && !isNaN(end) ? end : selectionEnd ); } /** * Gets the start index of the current line where the text is selected * @returns the start index of the current line */ getLineStartNumber() { let start = this.start; while (start > 0) { start--; if (this.value.charAt(start) === '\n') { start++; break; } } return start; } /** Indent on new lines */ getIndentText() { const start = this.getLineStartNumber(); const str = this.getSelectedValue(start); let indent = ''; str.replace(/(^(\s)+)/, (_str, old) => (indent = old)); return indent; } lineStarInsert(text: string) { if (text) { const oldStart = this.start; const start = this.getLineStartNumber(); const str = this.getSelectedValue(start); this.position(start, this.end) .insertText( str .split('\n') .map((txt) => text + txt) .join('\n') ) .position(oldStart + text.length, this.end); } return this; } lineStarRemove(text: string) { if (text) { const oldStart = this.start; const start = this.getLineStartNumber(); const str = this.getSelectedValue(start); const reg = new RegExp(`^${text}`, 'g'); let newStart = oldStart - text.length; if (!reg.test(str)) { newStart = oldStart; } this.position(start, this.end) .insertText( str .split('\n') .map((txt) => txt.replace(reg, '')) .join('\n') ) .position(newStart, this.end + newStart - oldStart); } } /** Notify any possible listeners of the change */ notifyChange() { const event = new Event('input', { bubbles: true, cancelable: false }); this.elm.dispatchEvent(event); } }