87 lines
2.1 KiB
TypeScript
87 lines
2.1 KiB
TypeScript
import type { Schema } from 'prosemirror-model';
|
|
import { type Transaction, TextSelection, AllSelection } from 'prosemirror-state';
|
|
import type { EditorView } from 'prosemirror-view';
|
|
import { isList } from '../utils';
|
|
|
|
type IndentKey = 'indent' | 'textIndent';
|
|
|
|
function setNodeIndentMarkup(
|
|
tr: Transaction,
|
|
pos: number,
|
|
delta: number,
|
|
indentKey: IndentKey,
|
|
): Transaction {
|
|
if (!tr.doc) return tr;
|
|
|
|
const node = tr.doc.nodeAt(pos);
|
|
if (!node) return tr;
|
|
|
|
const minIndent = 0;
|
|
const maxIndent = 8;
|
|
|
|
let indent = (node.attrs[indentKey] || 0) + delta;
|
|
if (indent < minIndent) indent = minIndent;
|
|
if (indent > maxIndent) indent = maxIndent;
|
|
|
|
if (indent === node.attrs[indentKey]) return tr;
|
|
|
|
const nodeAttrs = {
|
|
...node.attrs,
|
|
[indentKey]: indent,
|
|
};
|
|
|
|
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
|
|
}
|
|
|
|
const setIndent = (
|
|
tr: Transaction,
|
|
schema: Schema,
|
|
delta: number,
|
|
indentKey: IndentKey,
|
|
): Transaction => {
|
|
const { selection, doc } = tr;
|
|
if (!selection || !doc) return tr;
|
|
|
|
if (!(selection instanceof TextSelection || selection instanceof AllSelection)) return tr;
|
|
|
|
const { from, to } = selection;
|
|
|
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
const nodeType = node.type;
|
|
|
|
if (nodeType.name === 'paragraph' || nodeType.name === 'blockquote') {
|
|
tr = setNodeIndentMarkup(tr, pos, delta, indentKey);
|
|
return false;
|
|
} else if (isList(node, schema)) return false;
|
|
return true;
|
|
});
|
|
|
|
return tr;
|
|
};
|
|
|
|
export const indentCommand = (view: EditorView, delta: number) => {
|
|
const { state } = view;
|
|
const { schema, selection } = state;
|
|
|
|
const tr = setIndent(state.tr.setSelection(selection), schema, delta, 'indent');
|
|
if (tr.docChanged) {
|
|
view.dispatch(tr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
export const textIndentCommand = (view: EditorView, delta: number) => {
|
|
const { state } = view;
|
|
const { schema, selection } = state;
|
|
|
|
const tr = setIndent(state.tr.setSelection(selection), schema, delta, 'textIndent');
|
|
if (tr.docChanged) {
|
|
view.dispatch(tr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|