vditor
Version:
♏ 易于使用的 Markdown 编辑器,为适配不同的应用场景而生
239 lines (216 loc) • 9.67 kB
text/typescript
import {Constants} from "../constants";
import {hidePanel} from "../toolbar/setToolbar";
import {isCtrl} from "../util/compatibility";
import {
fixBlockquote, fixCJKPosition,
fixCodeBlock, fixCursorDownInlineMath,
fixDelete, fixFirefoxArrowUpTable, fixGSKeyBackspace, fixHR,
fixList,
fixMarkdown,
fixTab,
fixTable,
fixTask,
insertAfterBlock, insertBeforeBlock, isFirstCell, isLastCell,
} from "../util/fixBrowserBehavior";
import {
hasClosestBlock,
hasClosestByAttribute,
hasClosestByClassName,
hasClosestByMatchTag,
} from "../util/hasClosest";
import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
import {matchHotKey} from "../util/hotKey";
import {getEditorRange, getSelectPosition, setSelectionFocus} from "../util/selection";
import {keydownToc} from "../util/toc";
import {expandMarker} from "./expandMarker";
import {processAfterRender, processHeading} from "./process";
export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
vditor.ir.composingLock = event.isComposing;
if (event.isComposing) {
return false;
}
// 添加第一次记录 undo 的光标
if (event.key.indexOf("Arrow") === -1 && event.key !== "Meta" && event.key !== "Control" && event.key !== "Alt" &&
event.key !== "Shift" && event.key !== "CapsLock" && event.key !== "Escape" && !/^F\d{1,2}$/.test(event.key)) {
vditor.undo.recordFirstPosition(vditor, event);
}
const range = getEditorRange(vditor);
const startContainer = range.startContainer;
if (!fixGSKeyBackspace(event, vditor, startContainer)) {
return false;
}
fixCJKPosition(range, vditor, event);
fixHR(range);
// 仅处理以下快捷键操作
if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
&& !isCtrl(event) && event.key !== "Escape" && event.key !== "Delete") {
return false;
}
// 斜体、粗体、内联代码块中换行
const newlineElement = hasClosestByAttribute(startContainer, "data-newline", "1");
if (!isCtrl(event) && !event.altKey && !event.shiftKey && event.key === "Enter" && newlineElement
&& range.startOffset < newlineElement.textContent.length) {
const beforeMarkerElement = newlineElement.previousElementSibling;
if (beforeMarkerElement) {
range.insertNode(document.createTextNode(beforeMarkerElement.textContent));
range.collapse(false);
}
const afterMarkerElement = newlineElement.nextSibling;
if (afterMarkerElement) {
range.insertNode(document.createTextNode(afterMarkerElement.textContent));
range.collapse(true);
}
}
const pElement = hasClosestByMatchTag(startContainer, "P");
// md 处理
if (fixMarkdown(event, vditor, pElement, range)) {
return true;
}
// li
if (fixList(range, vditor, pElement, event)) {
return true;
}
// blockquote
if (fixBlockquote(vditor, range, event, pElement)) {
return true;
}
// 代码块
const preRenderElement = hasClosestByClassName(startContainer, "vditor-ir__marker--pre");
if (preRenderElement && preRenderElement.tagName === "PRE") {
const codeRenderElement = preRenderElement.firstChild as HTMLElement;
if (fixCodeBlock(vditor, event, preRenderElement, range)) {
return true;
}
// 数学公式上无元素,按上或左将添加新块
if ((codeRenderElement.getAttribute("data-type") === "math-block"
|| codeRenderElement.getAttribute("data-type") === "html-block") &&
insertBeforeBlock(vditor, event, range, codeRenderElement, preRenderElement.parentElement)) {
return true;
}
// 代码块下无元素或者为代码块/table 元素,添加空块
if (insertAfterBlock(vditor, event, range, codeRenderElement, preRenderElement.parentElement)) {
return true;
}
}
// 代码块语言
const preBeforeElement = hasClosestByAttribute(startContainer, "data-type", "code-block-info");
if (preBeforeElement) {
if (event.key === "Enter" || event.key === "Tab") {
range.selectNodeContents(preBeforeElement.nextElementSibling.firstChild);
range.collapse(true);
event.preventDefault();
hidePanel(vditor, ["hint"]);
return true;
}
if (event.key === "Backspace") {
const start = getSelectPosition(preBeforeElement, vditor.ir.element).start;
if (start === 1) { // 删除零宽空格
range.setStart(startContainer, 0);
}
if (start === 2) { // 删除时清空自动补全语言
vditor.hint.recentLanguage = "";
}
}
if (insertBeforeBlock(vditor, event, range, preBeforeElement, preBeforeElement.parentElement)) {
// 上无元素,按上或左将添加新块
hidePanel(vditor, ["hint"]);
return true;
}
}
// table
const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
hasClosestByMatchTag(startContainer, "TH");
if (event.key.indexOf("Arrow") > -1 && cellElement) {
const tableElement = isFirstCell(cellElement);
if (tableElement && insertBeforeBlock(vditor, event, range, cellElement, tableElement)) {
return true;
}
const table2Element = isLastCell(cellElement);
if (table2Element && insertAfterBlock(vditor, event, range, cellElement, table2Element)) {
return true;
}
}
if (fixTable(vditor, event, range)) {
return true;
}
// task list
if (fixTask(vditor, range, event)) {
return true;
}
// tab
if (fixTab(vditor, range, event)) {
return true;
}
const headingElement = hasClosestByHeadings(startContainer);
if (headingElement) {
// enter++: 标题变大
if (matchHotKey("⌘=", event)) {
const headingMarkerElement = headingElement.querySelector(".vditor-ir__marker--heading");
if (headingMarkerElement && headingMarkerElement.textContent.trim().length > 1) {
processHeading(vditor, headingMarkerElement.textContent.substr(1));
}
event.preventDefault();
return true;
}
// enter++: 标题变小
if (matchHotKey("⌘-", event)) {
const headingMarkerElement = headingElement.querySelector(".vditor-ir__marker--heading");
if (headingMarkerElement && headingMarkerElement.textContent.trim().length < 6) {
processHeading(vditor, headingMarkerElement.textContent.trim() + "# ");
}
event.preventDefault();
return true;
}
}
const blockElement = hasClosestBlock(startContainer);
if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey && range.toString() === "") {
if (fixDelete(vditor, range, event, pElement)) {
return true;
}
if (blockElement && blockElement.previousElementSibling
&& blockElement.tagName !== "UL" && blockElement.tagName !== "OL"
&& (blockElement.previousElementSibling.getAttribute("data-type") === "code-block" ||
blockElement.previousElementSibling.getAttribute("data-type") === "math-block")) {
const rangeStart = getSelectPosition(blockElement, vditor.ir.element, range).start;
if (rangeStart === 0 || (rangeStart === 1 && blockElement.innerText.startsWith(Constants.ZWSP))) {
// 当前块删除后光标落于代码渲染块上,当前块会被删除,因此需要阻止事件,不能和 keyup 中的代码块处理合并
range.selectNodeContents(blockElement.previousElementSibling.querySelector(".vditor-ir__marker--pre code"));
range.collapse(false);
expandMarker(range, vditor);
if (blockElement.textContent.trim().replace(Constants.ZWSP, "") === "") {
// 当前块为空且不是最后一个时,需要删除
blockElement.remove();
processAfterRender(vditor);
}
event.preventDefault();
return true;
}
}
// 光标位于标题前,marker 后
if (headingElement) {
const headingLength = headingElement.firstElementChild.textContent.length;
if (getSelectPosition(headingElement, vditor.ir.element).start === headingLength && headingLength !== 0) {
range.setStart(headingElement.firstElementChild.firstChild, headingLength - 1);
range.collapse(true);
setSelectionFocus(range);
}
}
}
if ((event.key === "ArrowUp" || event.key === "ArrowDown") && blockElement) {
// https://github.com/Vanessa219/vditor/issues/358
blockElement.querySelectorAll(".vditor-ir__node").forEach((item: HTMLElement) => {
if (!item.contains(startContainer)) {
item.classList.add("vditor-ir__node--hidden");
}
});
if (fixFirefoxArrowUpTable(event, blockElement, range)) {
return true;
}
}
fixCursorDownInlineMath(range, event.key);
if (blockElement && keydownToc(blockElement, vditor, event, range)) {
event.preventDefault();
return true;
}
return false;
};