File size: 2,118 Bytes
f56a29b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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;
};