/**
* 获取输入框选中文本的开始与结束的下标
* @param {HTMLTextAreaElement} el
* @returns {number[]}
*/
export const getCursorPosition = (el: HTMLTextAreaElement): number[] => {
const { selectionStart, selectionEnd } = el
return [selectionStart, selectionEnd]
}
/**
* 处理尾部多余的换行符
* @param {string} str
* @returns {string}
*/
export const clearEndNullText = (str: string): string => {
let arr = str.split('\n').reverse();
let index = arr.findIndex(item => item.length > 0) - 1;
arr.splice(0, index);
return arr.reverse().join('\n');
}
/**
* 获取焦点,并设置选中的文本
* @param {HTMLTextAreaElement} el
* @param {number} selectionStart
* @param {number} selectionEnd
*/
export const setSelectionRange = (el: HTMLTextAreaElement, selectionStart: number, selectionEnd: number): void => {
let timer = setTimeout(()=>{
let { scrollTop } = el;
el.focus();
el.scrollTop = scrollTop;
el.setSelectionRange(selectionStart, selectionEnd, 'none')
timer && clearInterval(timer)
}, 0)
}
/**
* 添加加粗、斜体、中划线的方法
* @param {HTMLTextAreaElement} el
* @param {string} symbol
* @param {string} txt
* @param {Function} setValue
*/
export const handleText = (el: HTMLTextAreaElement, symbol: string, txt: string, setValue: Function): void => {
const [start, end] = getCursorPosition(el)
let value = start === end
? `${el.value.slice(0, start)}\n${symbol}${txt}${symbol}\n${el.value.slice(end)}`
: `${el.value.slice(0, start)}${symbol}${el.value.slice(start, end)}${symbol}${el.value.slice(end)}`;
let selectionStart = start === end ? start + symbol.length + 1 : start + symbol.length;
let selectionEnd = start === end ? selectionStart + txt.length : end + symbol.length;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加标题
* @param {HTMLTextAreaElement} el
* @param {string} symbol
* @param {string} txt
* @param {Function} setValue
*/
export const addTitle = (el: HTMLTextAreaElement, symbol: string, txt: string, setValue: Function): void => {
const [start, end] = getCursorPosition(el)
let value = start === end
? `${el.value.slice(0, start)}\n${symbol} ${txt}\n${el.value.slice(end)}`
: `${el.value.slice(0, start)}\n${symbol} ${el.value.slice(start, end)}\n${el.value.slice(end)}`;
let selectionStart = start + symbol.length + 2;
let selectionEnd = start === end ? selectionStart + txt.length : end + symbol.length + 1;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加有序列表、无序列表, tab缩进
* @param {HTMLTextAreaElement} el
* @param {string} symbol
* @param {string} txt
* @param {Function} setValue
* @param {1 | 2} type 1: 有序、无序列表 2: 其他,如tab缩进
*/
export const addList = (el: HTMLTextAreaElement, symbol: string, setValue: Function, type: 1 | 2 = 1): void => {
const [start, end] = getCursorPosition(el)
let paragraph: string[] = el.value.split('\n'),
activeStart: number = start,
flag: boolean = start === end,
value: string = ``,
selectionStart: number = start,
selectionEnd: number = end,
len: number = paragraph.length,
stringCount:number = 0,
addSpaceCount: number = 0;
activeStart = el.value.slice(0, start).lastIndexOf('\n') === -1 ? 0 : el.value.slice(0, start).lastIndexOf('\n')
activeStart = activeStart === 0 ? 0 : activeStart + 1;
if(flag){
if(type === 1){
value = `${el.value.slice(0, activeStart)}${symbol}${el.value.slice(activeStart)}`
}else if(type === 2){
value = `${el.value.slice(0, start)}${symbol}${el.value.slice(end)}`
}
selectionStart += symbol.length;
selectionEnd += symbol.length;
}else{
for(let i = 0; i < len; i++){
let item = paragraph[i],
nextStringCount = stringCount + item.length + 1;
if(nextStringCount > start && stringCount < end){
let newItem = `${symbol}${item}`;
addSpaceCount += symbol.length;
paragraph[i] = newItem;
if(start > stringCount) selectionStart += symbol.length;
if(nextStringCount > end) selectionEnd += addSpaceCount;
}else if(stringCount > end){
break;
}
stringCount = nextStringCount;
}
value = paragraph.join('\n')
}
value = clearEndNullText(value);
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加超链接
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
*/
export const addLink = (el: HTMLTextAreaElement, setValue: Function): void => {
const [start, end] = getCursorPosition(el)
let value = start === end
? `${el.value.slice(0, start)}[链接文字](url)${el.value.slice(end)}`
: `${el.value.slice(0, start)}[${el.value.slice(start, end)}](url)${el.value.slice(end)}`;
let selectionStart = start === end ? start + 7 : end + 3;
let selectionEnd = start === end ? end + 10 : end + 6;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加图片
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
*/
export const addPhoto = (el: HTMLTextAreaElement, setValue: Function): void => {
const [start, end] = getCursorPosition(el)
let value = start === end
? `${el.value.slice(0, start)}\n\n${el.value.slice(end)}`
: `${el.value.slice(0, start)}\n\n${el.value.slice(end)}`;
let selectionStart = start === end ? start + 10 : end + 5;
let selectionEnd = start === end ? end + 13 : end + 8;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加表格
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
* @param {number} row
* @param {number} col
*/
export const addTable = (el: HTMLTextAreaElement,setValue: Function, row: number = 2, col: number = 3): void => {
const [start, end] = getCursorPosition(el);
let tableStr: string = ``;
for(let i = 0; i < row; i++){
for(let j = 0; j < col; j++){
let value = (i === 0 && j === 0) ? (start === end ? `${i+1}-${j+1}` : `${el.value.slice(start, end)}`) : `${i+1}-${j+1}`
tableStr += j === col - 1 ? `|${value}|` : `|${value}`;
}
tableStr += `\n`
if(i === 0){
for(let j = 0; j < col; j++){
tableStr += j === col - 1 ? `| -- |` : `| -- `
}
tableStr += `\n`
}
}
let value = `${el.value.slice(0, start)}\n${tableStr}\n${el.value.slice(end)}`
let selectionStart = start + 2;
let selectionEnd = start === end ? selectionStart + 3 : selectionStart + end - start;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 添加代码块
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
*/
export const addCode = (el: HTMLTextAreaElement, setValue: Function): void => {
const [start, end] = getCursorPosition(el);
let value = start === end
? `${el.value.slice(0, start)}\n\`\`\`\n\n\`\`\`\n${el.value.slice(end)}`
: `${el.value.slice(0, start)}\n\`\`\`\n${el.value.slice(start, end)}\n\`\`\`\n${el.value.slice(end)}`
let selectionStart = start + 5;
let selectionEnd = selectionStart;
value = clearEndNullText(value)
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* shift + tab 清除段落最开始的空字符
* @param {HTMLTextAreaElement} el
* @param {number} tabSpaceCount
* @param {Function} setValue
*/
export const cancelTabSpace = (el: HTMLTextAreaElement, tabSpaceCount: number, setValue: Function): void => {
const [start, end] = getCursorPosition(el);
let paragraph: string[] = el.value.split('\n'),
selectionStart: number = start,
selectionEnd: number = end,
value: string = ``,
stringCount: number = 0,
cancelSpaceCount: number = 0,
len: number = paragraph.length;
for(let i = 0; i < len; i++){
let item = paragraph[i]
let nextStringCount = stringCount + item.length + 1
if(nextStringCount > start && stringCount < end){
let spaces = item.split(' '.repeat(tabSpaceCount))
if(spaces.length !== 1 && spaces[0] === ""){
spaces.shift();
cancelSpaceCount += tabSpaceCount
}else{
let oldlen = spaces[0].length
spaces[0] = spaces[0].trimLeft();
let newlen = spaces[0].length
cancelSpaceCount += (oldlen - newlen)
}
let newParagraph = spaces.join(' '.repeat(tabSpaceCount))
paragraph[i] = newParagraph
if(start > stringCount) selectionStart -= item.length - newParagraph.length;
if(end < nextStringCount) selectionEnd -= cancelSpaceCount;
}else if(stringCount > end){
break;
}
stringCount = nextStringCount
}
value = paragraph.join('\n')
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 重写按下回车执行的操作
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
* @param {number} tabSpace
*/
export const clickEnter = (el: HTMLTextAreaElement, setValue: Function, tabSpace: number): void => {
setTimeout(()=>{
const [start, end] = getCursorPosition(el);
let flag = start === end,
value,
selectionStart: number = start,
selectionEnd: number = end;
if(flag){
let activeStr = el.value[start-1];
const spaceStr = getActiveLineBeginSpaces(el, tabSpace)
if(activeStr === '{'){
if(el.value[start] === '}'){
value = `${el.value.slice(0, start)}\n${' '.repeat(tabSpace) + spaceStr}\n${spaceStr}${el.value.slice(end)}`
selectionStart = start + 1 + tabSpace + spaceStr.length
selectionEnd = end + 1 + tabSpace + spaceStr.length
}else{
value = `${el.value.slice(0, start)}\n${' '.repeat(tabSpace) + spaceStr}\n${el.value.slice(end).trimLeft()[0] === '}' ? el.value.slice(end) : spaceStr + (el.value.slice(end))}`
selectionStart = start + 1 + tabSpace + spaceStr.length
selectionEnd = end + 1 + tabSpace + spaceStr.length
}
}else{
value = `${el.value.slice(0, start)}\n${spaceStr}${el.value.slice(end)}`
selectionStart = start + 1 + spaceStr.length
selectionEnd = end + 1 + spaceStr.length
}
}else{
value = `${el.value.slice(0, start)}\n${el.value.slice(end)}`
selectionStart = start + 1
selectionEnd = start + 1
}
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}, 0)
}
/**
* 自动补全括号、引号 () [] {} '' ""
* @param {HTMLTextAreaElement} el
* @param {Function} setValue
* @param {string[]} bracket
*/
export const autoComplementBracket = (el: HTMLTextAreaElement, setValue: Function, bracket: string[])=>{
const [start, end] = getCursorPosition(el)
let flag = start === end,
value: string = el.value,
selectionStart: number = start,
selectionEnd: number = end;
if(flag){
value = `${el.value.slice(0, start)}${bracket.join('')}${el.value.slice(end)}`
selectionStart = start + bracket[0].length;
selectionEnd = end + bracket[0].length;
}else{
value = `${el.value.slice(0, start)}${bracket[0]}${el.value.slice(start, end)}${bracket[1]}${el.value.slice(end)}`
selectionStart = start + bracket[0].length;
selectionEnd = end + bracket[0].length;
}
setSelectionRange(el, selectionStart, selectionEnd)
setValue(value, selectionStart, selectionEnd)
}
/**
* 获取光标所在行前面的tab字符
* @param {HTMLTextAreaElement} el
* @param {number} tabSpace
* @returns {string}
*/
const getActiveLineBeginSpaces = (el: HTMLTextAreaElement, tabSpace: number): string => {
const [start, end] = getCursorPosition(el)
let flag = start === end,
stringCount: number = 0,
nextStringCount: number = 0,
paragraph = el.value.split('\n'),
spaces : number = 0;
if(flag){
let len = paragraph.length;
for(let i = 0; i < len; i++){
stringCount += (i === 0 ? 0 : (paragraph[i-1].length + 1));
nextStringCount = stringCount + paragraph[i].length + 1;
if(stringCount < start && nextStringCount > start){
let items = paragraph[i].split(' '.repeat(tabSpace))
for(let j = 0; j < items.length; j++){
if(items[j] === ""){
spaces++
}else{
break
}
}
if(items[items.length - 1] === ""){
spaces--
}
break
}
}
}
return spaces === 0 ? '' : ' '.repeat(spaces * tabSpace);
}