mirror of https://github.com/halo-dev/halo
feat: add the first line indent to the Tab shortcut key (#6388)
#### What type of PR is this? /kind feature /area editor #### What this PR does / why we need it: 支持在默认编辑器中使用 Tab 键实现首行缩进的功能。 当光标处于文本首行时,按下 Tab 键会触发首行缩进。 在首行缩进的情况下或者选中一段文本,再次按下 Tab 键会触发整段缩进。 #### How to test it? 测试文本块及区域标题块首行按下 Tab 键是否可以正常触发首行缩进 #### Which issue(s) this PR fixes: Fixes #6316 #### Does this PR introduce a user-facing change? ```release-note 默认编辑器增加 Tab 快捷键首行缩进功能 ```pull/6408/head
parent
687de1c550
commit
59b3f460fb
|
@ -24,6 +24,7 @@ type IndentOptions = {
|
||||||
maxIndentLevel: number;
|
maxIndentLevel: number;
|
||||||
defaultIndentLevel: number;
|
defaultIndentLevel: number;
|
||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
|
firstLineIndent: boolean;
|
||||||
};
|
};
|
||||||
const Indent = Extension.create<IndentOptions, never>({
|
const Indent = Extension.create<IndentOptions, never>({
|
||||||
name: "indent",
|
name: "indent",
|
||||||
|
@ -36,6 +37,7 @@ const Indent = Extension.create<IndentOptions, never>({
|
||||||
maxIndentLevel: 24 * 10,
|
maxIndentLevel: 24 * 10,
|
||||||
defaultIndentLevel: 0,
|
defaultIndentLevel: 0,
|
||||||
HTMLAttributes: {},
|
HTMLAttributes: {},
|
||||||
|
firstLineIndent: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -56,6 +58,13 @@ const Indent = Extension.create<IndentOptions, never>({
|
||||||
parseInt(element.style.marginLeft, 10) ||
|
parseInt(element.style.marginLeft, 10) ||
|
||||||
this.options.defaultIndentLevel,
|
this.options.defaultIndentLevel,
|
||||||
},
|
},
|
||||||
|
lineIndent: {
|
||||||
|
default: false,
|
||||||
|
renderHTML: (attributes) => ({
|
||||||
|
style: attributes.lineIndent ? "text-indent: 2em" : "",
|
||||||
|
}),
|
||||||
|
parseHTML: (element) => element.style.textIndent === "2em",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -76,9 +85,8 @@ const Indent = Extension.create<IndentOptions, never>({
|
||||||
);
|
);
|
||||||
if (tr.docChanged && dispatch) {
|
if (tr.docChanged && dispatch) {
|
||||||
dispatch(tr);
|
dispatch(tr);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
},
|
},
|
||||||
outdent:
|
outdent:
|
||||||
() =>
|
() =>
|
||||||
|
@ -93,9 +101,8 @@ const Indent = Extension.create<IndentOptions, never>({
|
||||||
);
|
);
|
||||||
if (tr.docChanged && dispatch) {
|
if (tr.docChanged && dispatch) {
|
||||||
dispatch(tr);
|
dispatch(tr);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -133,22 +140,48 @@ export const clamp = (val: number, min: number, max: number): number => {
|
||||||
function setNodeIndentMarkup(
|
function setNodeIndentMarkup(
|
||||||
tr: Transaction,
|
tr: Transaction,
|
||||||
pos: number,
|
pos: number,
|
||||||
delta: number,
|
dir: number,
|
||||||
min: number,
|
options: IndentOptions
|
||||||
max: number
|
|
||||||
): Transaction {
|
): Transaction {
|
||||||
if (!tr.doc) return tr;
|
if (!tr.doc) {
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
const node = tr.doc.nodeAt(pos);
|
const node = tr.doc.nodeAt(pos);
|
||||||
if (!node) return tr;
|
if (!node) {
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
if (options.firstLineIndent && isLineIndent(tr)) {
|
||||||
|
if (node.attrs.lineIndent !== dir > 0) {
|
||||||
|
const nodeAttrs = { ...node.attrs, lineIndent: dir > 0 };
|
||||||
|
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const delta = options.indentRange * dir;
|
||||||
|
const min = options.minIndentLevel;
|
||||||
|
const max = options.maxIndentLevel;
|
||||||
const indent = clamp((node.attrs.indent || 0) + delta, min, max);
|
const indent = clamp((node.attrs.indent || 0) + delta, min, max);
|
||||||
if (indent === node.attrs.indent) return tr;
|
if (indent === node.attrs.indent) {
|
||||||
const nodeAttrs = {
|
return tr;
|
||||||
...node.attrs,
|
}
|
||||||
indent,
|
const nodeAttrs = { ...node.attrs, indent };
|
||||||
};
|
|
||||||
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
|
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isLineIndent = (tr: Transaction) => {
|
||||||
|
const { selection } = tr;
|
||||||
|
const { $from, from, to } = selection;
|
||||||
|
if (from == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from != to) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $from.textOffset == 0;
|
||||||
|
};
|
||||||
|
|
||||||
type IndentType = "indent" | "outdent";
|
type IndentType = "indent" | "outdent";
|
||||||
const updateIndentLevel = (
|
const updateIndentLevel = (
|
||||||
tr: Transaction,
|
tr: Transaction,
|
||||||
|
@ -167,13 +200,7 @@ const updateIndentLevel = (
|
||||||
if (isTextIndent(tr, pos) && type === "indent") {
|
if (isTextIndent(tr, pos) && type === "indent") {
|
||||||
tr.insertText("\t", from, to);
|
tr.insertText("\t", from, to);
|
||||||
} else {
|
} else {
|
||||||
tr = setNodeIndentMarkup(
|
tr = setNodeIndentMarkup(tr, pos, type === "indent" ? 1 : -1, options);
|
||||||
tr,
|
|
||||||
pos,
|
|
||||||
options.indentRange * (type === "indent" ? 1 : -1),
|
|
||||||
options.minIndentLevel,
|
|
||||||
options.maxIndentLevel
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -210,11 +237,9 @@ const isFilterActive = (editor: CoreEditor) => {
|
||||||
export const getIndent: () => KeyboardShortcutCommand =
|
export const getIndent: () => KeyboardShortcutCommand =
|
||||||
() =>
|
() =>
|
||||||
({ editor }) => {
|
({ editor }) => {
|
||||||
// @ts-ignore
|
|
||||||
if (isFilterActive(editor)) {
|
if (isFilterActive(editor)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
|
||||||
if (isListActive(editor)) {
|
if (isListActive(editor)) {
|
||||||
const name = editor.can().sinkListItem("listItem")
|
const name = editor.can().sinkListItem("listItem")
|
||||||
? "listItem"
|
? "listItem"
|
||||||
|
|
Loading…
Reference in New Issue