Skip to content
Snippets Groups Projects
hanging-indent.ts 2.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • import { Command, Extension } from '@tiptap/core';
    import { TextSelection, AllSelection, Transaction } from 'prosemirror-state';
    
    declare module '@tiptap/core' {
      interface Commands<ReturnType> {
        hangingIndent: {
    
          hangIndent: (indentSize: number) => ReturnType;
          unhangIndent: (indentSize: number) => ReturnType;
    
        };
      }
    }
    
    export const HangingIndent = Extension.create({
      name: 'hangingIndent',
    
    
      addOptions() {
        return {
          types: ['paragraph']
        };
    
      },
    
      addGlobalAttributes() {
        return [
          {
            types: this.options.types,
            attributes: {
              hangingIndent: {
                default: false,
                renderHTML: attributes => (attributes.hangingIndent ?
    
                  { style: `text-indent: -${attributes.indentSize}px` } :
                  { style: 'text-indent: 0px' }
    
                parseHTML: element => element.style.textIndent !== '0px'
              },
              indentSize: {
                default: 20,
                parseHTML: element => Number(element.getAttribute('indentSize'))
    
        const setNodeIndentMarkup =
          (tr: Transaction, pos: number, hangingIndent: boolean, indentSize: number): Transaction => {
            const node = tr?.doc?.nodeAt(pos);
            if (node) {
              if (hangingIndent !== node.attrs.indent) {
                const nodeAttrs = { ...node.attrs, hangingIndent, indentSize };
                return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
              }
    
        const updateIndentLevel = (tr: Transaction, hangingIndent: boolean, indentSize: number): Transaction => {
    
          const { doc, selection } = tr;
    
    
    rhenck's avatar
    rhenck committed
          if (doc && selection && (selection instanceof TextSelection)) {
    
            const { from, to } = selection;
            doc.nodesBetween(from, to, (node, pos) => {
    
              setNodeIndentMarkup(tr, pos, hangingIndent, indentSize);
    
        const applyIndent: (hangingIndent: boolean, indentSize: number) => () => Command =
          (hangingIndent, indentSize) => () => ({ tr, state, dispatch }) => {
    
            const { selection } = state;
    
    rhenck's avatar
    rhenck committed
            let transaction;
            transaction = tr.setSelection(selection);
            transaction = updateIndentLevel(transaction, hangingIndent, indentSize);
    
    rhenck's avatar
    rhenck committed
            if (transaction.docChanged) {
              dispatch?.(transaction);
    
          hangIndent: indentSize => applyIndent(true, indentSize)(),
          unhangIndent: indentSize => applyIndent(false, indentSize)()