1
0
mirror of https://github.com/flarum/core.git synced 2025-05-08 16:35:26 +02:00

Sync with v2.1 of markdown-toolbar-element

This commit is contained in:
Alexander Skvortsov 2021-12-13 16:03:34 -05:00
parent fb82afa97f
commit 080442d085
No known key found for this signature in database
GPG Key ID: C4E3BBF9C3412B4C

View File

@ -16,6 +16,7 @@ interface StyleArgs {
scanFor: string; scanFor: string;
surroundWithNewlines: boolean; surroundWithNewlines: boolean;
orderedList: boolean; orderedList: boolean;
unorderedList: boolean;
trimFirst: boolean; trimFirst: boolean;
} }
@ -30,6 +31,7 @@ const defaults: StyleArgs = {
scanFor: '', scanFor: '',
surroundWithNewlines: false, surroundWithNewlines: false,
orderedList: false, orderedList: false,
unorderedList: false,
trimFirst: false, trimFirst: false,
}; };
@ -41,8 +43,8 @@ export default function styleSelectedText(textarea: HTMLTextAreaElement, styleAr
const text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd); const text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
let result; let result;
if (styleArgs.orderedList) { if (styleArgs.orderedList || styleArgs.unorderedList) {
result = orderedList(textarea); result = listStyle(textarea, styleArgs);
} else if (styleArgs.multiline && isMultipleLines(text)) { } else if (styleArgs.multiline && isMultipleLines(text)) {
result = multilineStyle(textarea, styleArgs); result = multilineStyle(textarea, styleArgs);
} else { } else {
@ -77,6 +79,21 @@ function wordSelectionEnd(text: string, i: number, multiline: boolean): number {
return index; return index;
} }
function expandSelectionToLine(textarea: HTMLTextAreaElement) {
const lines = textarea.value.split('\n');
let counter = 0;
for (let index = 0; index < lines.length; index++) {
const lineLength = lines[index].length + 1;
if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
textarea.selectionStart = counter;
}
if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
textarea.selectionEnd = counter + lineLength - 1;
}
counter += lineLength;
}
}
function expandSelectedText(textarea: HTMLTextAreaElement, prefixToUse: string, suffixToUse: string, multiline = false): string { function expandSelectedText(textarea: HTMLTextAreaElement, prefixToUse: string, suffixToUse: string, multiline = false): string {
if (textarea.selectionStart === textarea.selectionEnd) { if (textarea.selectionStart === textarea.selectionEnd) {
textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart); textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
@ -140,7 +157,9 @@ function blockStyle(textarea: HTMLTextAreaElement, arg: StyleArgs): SelectionRan
let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd); let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
let prefixToUse = isMultipleLines(selectedText) && blockPrefix.length > 0 ? `${blockPrefix}\n` : prefix; let prefixToUse = isMultipleLines(selectedText) && blockPrefix.length > 0 ? `${blockPrefix}\n` : prefix;
// CHANGED
let suffixToUse = isMultipleLines(selectedText) && blockSuffix.length > 0 ? `\n${blockSuffix}` : prefixToUse === prefix ? suffix : ''; let suffixToUse = isMultipleLines(selectedText) && blockSuffix.length > 0 ? `\n${blockSuffix}` : prefixToUse === prefix ? suffix : '';
// END CHANGED
if (prefixSpace) { if (prefixSpace) {
const beforeSelection = textarea.value[textarea.selectionStart - 1]; const beforeSelection = textarea.value[textarea.selectionStart - 1];
@ -203,16 +222,20 @@ function multilineStyle(textarea: HTMLTextAreaElement, arg: StyleArgs) {
let selectionStart = textarea.selectionStart; let selectionStart = textarea.selectionStart;
let selectionEnd = textarea.selectionEnd; let selectionEnd = textarea.selectionEnd;
const lines = text.split('\n'); const lines = text.split('\n');
// CHANGED
let prefixToUse = blockPrefix.length > 0 ? blockPrefix : prefix; let prefixToUse = blockPrefix.length > 0 ? blockPrefix : prefix;
let suffixToUse = blockSuffix.length > 0 ? blockSuffix : prefixToUse == prefix ? suffix : ''; let suffixToUse = blockSuffix.length > 0 ? blockSuffix : prefixToUse == prefix ? suffix : '';
const undoStyle = lines.every((line) => line.startsWith(prefixToUse) && line.endsWith(suffixToUse)); const undoStyle = lines.every((line) => line.startsWith(prefixToUse) && line.endsWith(suffixToUse));
// END CHANGED
if (undoStyle) { if (undoStyle) {
text = lines.map((line) => line.slice(prefixToUse.length, line.length - suffixToUse.length)).join('\n'); text = lines.map((line) => line.slice(prefixToUse.length, line.length - suffixToUse.length)).join('\n');
selectionEnd = selectionStart + text.length; selectionEnd = selectionStart + text.length;
} else { } else {
// CHANGED
text = lines.map((line) => prefixToUse + line + suffixToUse).join('\n'); text = lines.map((line) => prefixToUse + line + suffixToUse).join('\n');
if (surroundWithNewlines || suffixToUse === '') { if (surroundWithNewlines || suffixToUse === '') {
// END CHANGED
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea); const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
selectionStart += newlinesToAppend.length; selectionStart += newlinesToAppend.length;
selectionEnd = selectionStart + text.length; selectionEnd = selectionStart + text.length;
@ -223,54 +246,116 @@ function multilineStyle(textarea: HTMLTextAreaElement, arg: StyleArgs) {
return { text, selectionStart, selectionEnd }; return { text, selectionStart, selectionEnd };
} }
function orderedList(textarea: HTMLTextAreaElement): SelectionRange { interface UndoResult {
text: string;
processed: boolean;
}
function undoOrderedListStyle(text: string): UndoResult {
const lines = text.split('\n');
const orderedListRegex = /^\d+\.\s+/; const orderedListRegex = /^\d+\.\s+/;
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd; const shouldUndoOrderedList = lines.every((line) => orderedListRegex.test(line));
let selectionEnd; let result = lines;
let selectionStart; if (shouldUndoOrderedList) {
let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd); result = lines.map((line) => line.replace(orderedListRegex, ''));
let textToUnstyle = text;
let lines = text.split('\n');
let startOfLine, endOfLine;
if (noInitialSelection) {
const linesBefore = textarea.value.slice(0, textarea.selectionStart).split(/\n/);
startOfLine = textarea.selectionStart - linesBefore[linesBefore.length - 1].length;
endOfLine = wordSelectionEnd(textarea.value, textarea.selectionStart, true);
textToUnstyle = textarea.value.slice(startOfLine, endOfLine);
} }
const linesToUnstyle = textToUnstyle.split('\n');
const undoStyling = linesToUnstyle.every((line) => orderedListRegex.test(line));
if (undoStyling) { return {
lines = linesToUnstyle.map((line) => line.replace(orderedListRegex, '')); text: result.join('\n'),
text = lines.join('\n'); processed: shouldUndoOrderedList,
if (noInitialSelection && startOfLine && endOfLine) { };
const lengthDiff = linesToUnstyle[0].length - lines[0].length; }
selectionStart = selectionEnd = textarea.selectionStart - lengthDiff;
textarea.selectionStart = startOfLine; function undoUnorderedListStyle(text: string): UndoResult {
textarea.selectionEnd = endOfLine; const lines = text.split('\n');
const unorderedListPrefix = '- ';
const shouldUndoUnorderedList = lines.every((line) => line.startsWith(unorderedListPrefix));
let result = lines;
if (shouldUndoUnorderedList) {
result = lines.map((line) => line.slice(unorderedListPrefix.length, line.length));
} }
return {
text: result.join('\n'),
processed: shouldUndoUnorderedList,
};
}
function makePrefix(index: number, unorderedList: boolean): string {
if (unorderedList) {
return '- ';
} else { } else {
lines = numberedLines(lines); return `${index + 1}. `;
text = lines.join('\n'); }
}
function clearExistingListStyle(style: StyleArgs, selectedText: string): [UndoResult, UndoResult, string] {
let undoResultOpositeList: UndoResult;
let undoResult: UndoResult;
let pristineText;
if (style.orderedList) {
undoResult = undoOrderedListStyle(selectedText);
undoResultOpositeList = undoUnorderedListStyle(undoResult.text);
pristineText = undoResultOpositeList.text;
} else {
undoResult = undoUnorderedListStyle(selectedText);
undoResultOpositeList = undoOrderedListStyle(undoResult.text);
pristineText = undoResultOpositeList.text;
}
return [undoResult, undoResultOpositeList, pristineText];
}
function listStyle(textarea: HTMLTextAreaElement, style: StyleArgs): SelectionRange {
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
let selectionStart = textarea.selectionStart;
let selectionEnd = textarea.selectionEnd;
// Select whole line
expandSelectionToLine(textarea);
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
// If the user intent was to do an undo, we will stop after this.
// Otherwise, we will still undo to other list type to prevent list stacking
const [undoResult, undoResultOpositeList, pristineText] = clearExistingListStyle(style, selectedText);
const prefixedLines = pristineText.split('\n').map((value, index) => {
return `${makePrefix(index, style.unorderedList)}${value}`;
});
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
}, 0);
const totalPrefixLengthOpositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
}, 0);
if (undoResult.processed) {
if (noInitialSelection) {
selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
selectionEnd = selectionStart;
} else {
selectionStart = textarea.selectionStart;
selectionEnd = textarea.selectionEnd - totalPrefixLength;
}
return { text: pristineText, selectionStart, selectionEnd };
}
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea); const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
selectionStart = textarea.selectionStart + newlinesToAppend.length; const text = newlinesToAppend + prefixedLines.join('\n') + newlinesToPrepend;
selectionEnd = selectionStart + text.length;
if (noInitialSelection) selectionStart = selectionEnd; if (noInitialSelection) {
text = newlinesToAppend + text + newlinesToPrepend; selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
selectionEnd = selectionStart;
} else {
if (undoResultOpositeList.processed) {
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOpositeList;
} else {
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
}
} }
return { text, selectionStart, selectionEnd }; return { text, selectionStart, selectionEnd };
} }
function numberedLines(lines: string[]) {
let i;
let len;
let index;
const results = [];
for (index = i = 0, len = lines.length; i < len; index = ++i) {
const line = lines[index];
results.push(`${index + 1}. ${line}`);
}
return results;
}