{"version":3,"sources":["../../src/text-style/index.ts"],"sourcesContent":["import { Mark, mergeAttributes } from '@tiptap/core'\n\nimport type { TextStyleAttributes } from '../index.js'\n\nexport interface TextStyleOptions {\n  /**\n   * HTML attributes to add to the span element.\n   * @default {}\n   * @example { class: 'foo' }\n   */\n  HTMLAttributes: Record<string, any>\n  /**\n   * When enabled, merges the styles of nested spans into the child span during HTML parsing.\n   * This prioritizes the style of the child span.\n   * Used when parsing content created in other editors.\n   * (Fix for ProseMirror's default behavior.)\n   * @default true\n   */\n  mergeNestedSpanStyles: boolean\n}\n\ndeclare module '@tiptap/core' {\n  interface Commands<ReturnType> {\n    textStyle: {\n      /**\n       * Remove spans without inline style attributes.\n       * @example editor.commands.removeEmptyTextStyle()\n       */\n      removeEmptyTextStyle: () => ReturnType\n      /**\n       * Toggle a text style\n       * @param attributes The text style attributes\n       * @example editor.commands.toggleTextStyle({ fontWeight: 'bold' })\n       */\n      toggleTextStyle: (attributes?: TextStyleAttributes) => ReturnType\n    }\n  }\n}\n\nconst MAX_FIND_CHILD_SPAN_DEPTH = 20\n\n/**\n * Returns all next child spans, either direct children or nested deeper\n * but won't traverse deeper into child spans found, will only go MAX_FIND_CHILD_SPAN_DEPTH levels deep (default: 20)\n */\nconst findChildSpans = (element: HTMLElement, depth = 0): HTMLElement[] => {\n  const childSpans: HTMLElement[] = []\n\n  if (!element.children.length || depth > MAX_FIND_CHILD_SPAN_DEPTH) {\n    return childSpans\n  }\n\n  Array.from(element.children).forEach(child => {\n    if (child.tagName === 'SPAN') {\n      childSpans.push(child as HTMLElement)\n    } else if (child.children.length) {\n      childSpans.push(...findChildSpans(child as HTMLElement, depth + 1))\n    }\n  })\n\n  return childSpans\n}\n\nconst mergeNestedSpanStyles = (element: HTMLElement) => {\n  if (!element.children.length) {\n    return\n  }\n\n  const childSpans = findChildSpans(element)\n\n  if (!childSpans) {\n    return\n  }\n\n  childSpans.forEach(childSpan => {\n    const childStyle = childSpan.getAttribute('style')\n    const closestParentSpanStyleOfChild = childSpan.parentElement?.closest('span')?.getAttribute('style')\n\n    childSpan.setAttribute('style', `${closestParentSpanStyleOfChild};${childStyle}`)\n  })\n}\n\n/**\n * This extension allows you to create text styles. It is required by default\n * for the `text-color` and `font-family` extensions.\n * @see https://www.tiptap.dev/api/marks/text-style\n */\nexport const TextStyle = Mark.create<TextStyleOptions>({\n  name: 'textStyle',\n\n  priority: 101,\n\n  addOptions() {\n    return {\n      HTMLAttributes: {},\n      mergeNestedSpanStyles: true,\n    }\n  },\n\n  parseHTML() {\n    return [\n      {\n        tag: 'span',\n        consuming: false,\n        getAttrs: element => {\n          const hasStyles = (element as HTMLElement).hasAttribute('style')\n\n          if (!hasStyles) {\n            return false\n          }\n\n          if (this.options.mergeNestedSpanStyles) {\n            mergeNestedSpanStyles(element)\n          }\n\n          return {}\n        },\n      },\n    ]\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n  },\n\n  addCommands() {\n    return {\n      toggleTextStyle:\n        attributes =>\n        ({ commands }) => {\n          return commands.toggleMark(this.name, attributes)\n        },\n      removeEmptyTextStyle:\n        () =>\n        ({ tr }) => {\n          const { selection } = tr\n\n          // Gather all of the nodes within the selection range.\n          // We would need to go through each node individually\n          // to check if it has any inline style attributes.\n          // Otherwise, calling commands.unsetMark(this.name)\n          // removes everything from all the nodes\n          // within the selection range.\n          tr.doc.nodesBetween(selection.from, selection.to, (node, pos) => {\n            // Check if it's a paragraph element, if so, skip this node as we apply\n            // the text style to inline text nodes only (span).\n            if (node.isTextblock) {\n              return true\n            }\n\n            // Check if the node has no inline style attributes.\n            // Filter out non-`textStyle` marks.\n            if (\n              !node.marks\n                .filter(mark => mark.type === this.type)\n                .some(mark => Object.values(mark.attrs).some(value => !!value))\n            ) {\n              // Proceed with the removal of the `textStyle` mark for this node only\n              tr.removeMark(pos, pos + node.nodeSize, this.type)\n            }\n          })\n\n          return true\n        },\n    }\n  },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAsC;AAuCtC,IAAM,4BAA4B;AAMlC,IAAM,iBAAiB,CAAC,SAAsB,QAAQ,MAAqB;AACzE,QAAM,aAA4B,CAAC;AAEnC,MAAI,CAAC,QAAQ,SAAS,UAAU,QAAQ,2BAA2B;AACjE,WAAO;AAAA,EACT;AAEA,QAAM,KAAK,QAAQ,QAAQ,EAAE,QAAQ,WAAS;AAC5C,QAAI,MAAM,YAAY,QAAQ;AAC5B,iBAAW,KAAK,KAAoB;AAAA,IACtC,WAAW,MAAM,SAAS,QAAQ;AAChC,iBAAW,KAAK,GAAG,eAAe,OAAsB,QAAQ,CAAC,CAAC;AAAA,IACpE;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEA,IAAM,wBAAwB,CAAC,YAAyB;AACtD,MAAI,CAAC,QAAQ,SAAS,QAAQ;AAC5B;AAAA,EACF;AAEA,QAAM,aAAa,eAAe,OAAO;AAEzC,MAAI,CAAC,YAAY;AACf;AAAA,EACF;AAEA,aAAW,QAAQ,eAAa;AA1ElC;AA2EI,UAAM,aAAa,UAAU,aAAa,OAAO;AACjD,UAAM,iCAAgC,qBAAU,kBAAV,mBAAyB,QAAQ,YAAjC,mBAA0C,aAAa;AAE7F,cAAU,aAAa,SAAS,GAAG,6BAA6B,IAAI,UAAU,EAAE;AAAA,EAClF,CAAC;AACH;AAOO,IAAM,YAAY,iBAAK,OAAyB;AAAA,EACrD,MAAM;AAAA,EAEN,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,uBAAuB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO;AAAA,MACL;AAAA,QACE,KAAK;AAAA,QACL,WAAW;AAAA,QACX,UAAU,aAAW;AACnB,gBAAM,YAAa,QAAwB,aAAa,OAAO;AAE/D,cAAI,CAAC,WAAW;AACd,mBAAO;AAAA,UACT;AAEA,cAAI,KAAK,QAAQ,uBAAuB;AACtC,kCAAsB,OAAO;AAAA,UAC/B;AAEA,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO,CAAC,YAAQ,6BAAgB,KAAK,QAAQ,gBAAgB,cAAc,GAAG,CAAC;AAAA,EACjF;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,iBACE,gBACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,WAAW,KAAK,MAAM,UAAU;AAAA,MAClD;AAAA,MACF,sBACE,MACA,CAAC,EAAE,GAAG,MAAM;AACV,cAAM,EAAE,UAAU,IAAI;AAQtB,WAAG,IAAI,aAAa,UAAU,MAAM,UAAU,IAAI,CAAC,MAAM,QAAQ;AAG/D,cAAI,KAAK,aAAa;AACpB,mBAAO;AAAA,UACT;AAIA,cACE,CAAC,KAAK,MACH,OAAO,UAAQ,KAAK,SAAS,KAAK,IAAI,EACtC,KAAK,UAAQ,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK,WAAS,CAAC,CAAC,KAAK,CAAC,GAChE;AAEA,eAAG,WAAW,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,UACnD;AAAA,QACF,CAAC;AAED,eAAO;AAAA,MACT;AAAA,IACJ;AAAA,EACF;AACF,CAAC;","names":[]}