diff options
author | Vinicius Reis <vinicius.reis@nextcloud.com> | 2022-08-16 22:10:58 +0300 |
---|---|---|
committer | Vinicius Reis <vinicius.reis@nextcloud.com> | 2022-08-17 17:42:59 +0300 |
commit | 381797c4c6730c918e0c0fa57e3e838059d828d2 (patch) | |
tree | 30145e9f80568f7f4893857382b6527b884f2c31 /src/nodes | |
parent | 27fbf269951d7384b0bb99f5f79fb86b0002bd24 (diff) |
♻️ (#107): split heading logic: extractHeadings
Signed-off-by: Vinicius Reis <vinicius.reis@nextcloud.com>
Diffstat (limited to 'src/nodes')
-rw-r--r-- | src/nodes/Heading.js | 107 | ||||
-rw-r--r-- | src/nodes/Heading/extractor.js | 68 | ||||
-rw-r--r-- | src/nodes/Heading/index.js | 40 | ||||
-rw-r--r-- | src/nodes/Heading/slug.js | 10 |
4 files changed, 118 insertions, 107 deletions
diff --git a/src/nodes/Heading.js b/src/nodes/Heading.js deleted file mode 100644 index 6270731aa..000000000 --- a/src/nodes/Heading.js +++ /dev/null @@ -1,107 +0,0 @@ -import TipTapHeading from '@tiptap/extension-heading' -import debounce from 'debounce' -import { v4 as uuidv4 } from 'uuid' - -import store from '../store/index.js' - -const regexSpaces = /\s+/g -const regexInvalidCaracters = /[^\p{Letter}\p{Mark}\w\s-]/gu - -const slugify = (str) => String(str) - .toLowerCase() - .replace(regexInvalidCaracters, '') - .trim() - .replace(regexSpaces, '-') - -const setHeadings = (val) => store.dispatch('setHeadings', val) - -const Heading = TipTapHeading.extend({ - addAttributes() { - return { - ...this.parent(), - id: { - default: undefined, - rendered: true, - }, - uuid: { - default: undefined, - rendered: false, - }, - } - }, - addKeyboardShortcuts() { - return this.options.levels.reduce((items, level) => ({ - ...items, - [`Mod-Shift-${level}`]: () => this.editor.commands.toggleHeading({ level }), - }), {}) - }, - - onDestroy() { - setHeadings([]) - - if (this.parent) { - this.parent() - } - }, - - onUpdate: debounce(function onUpdate({ editor }) { - const counter = new Map() - const headings = [] - const tr = editor.state.tr - - const getId = text => { - const id = slugify(text) - - if (counter.has(id)) { - const next = counter.get(id) - - // increment counter - counter.set(id, next + 1) - - return `${id}--${next}` - } - - // define counter - counter.set(id, 1) - - return id - } - - editor.state.doc.descendants((node, position) => { - if (node.type.name === 'heading') { - const text = node.textContent - const id = getId(text) - const uuid = node.attrs.uuid ?? uuidv4() - - if (node.attrs.id !== id || !node.attrs.uuid) { - const attrs = { - ...node.attrs, - uuid, - id, - } - - tr.setNodeMarkup(position, undefined, attrs) - } - - headings.push(Object.freeze({ - level: node.attrs.level, - position, - text, - id, - uuid, - })) - } - }) - - tr.setMeta('addToHistory', false) - tr.setMeta('preventUpdate', true) - - editor.view.dispatch(tr) - - setHeadings(headings) - - }, 900), - -}) - -export default Heading diff --git a/src/nodes/Heading/extractor.js b/src/nodes/Heading/extractor.js new file mode 100644 index 000000000..ae9245154 --- /dev/null +++ b/src/nodes/Heading/extractor.js @@ -0,0 +1,68 @@ + +import store from '../../store/index.js' +import { slugify } from './slug.js' +import { v4 as uuidv4 } from 'uuid' + +const setHeadings = (val) => store.dispatch('setHeadings', val) + +const extractHeadings = (editor) => { + const counter = new Map() + const headings = [] + const tr = editor.state.tr + + const getId = text => { + const id = slugify(text) + + if (counter.has(id)) { + const next = counter.get(id) + + // increment counter + counter.set(id, next + 1) + + return `${id}--${next}` + } + + // define counter + counter.set(id, 1) + + return id + } + + editor.state.doc.descendants((node, position) => { + if (node.type.name === 'heading') { + const text = node.textContent + const id = getId(text) + const uuid = node.attrs.uuid ?? uuidv4() + + if (node.attrs.id !== id || !node.attrs.uuid) { + const attrs = { + ...node.attrs, + uuid, + id, + } + + tr.setNodeMarkup(position, undefined, attrs) + } + + headings.push(Object.freeze({ + level: node.attrs.level, + position, + text, + id, + uuid, + })) + } + }) + + tr.setMeta('addToHistory', false) + tr.setMeta('preventUpdate', true) + + editor.view.dispatch(tr) + + setHeadings(headings) +} + +export { + extractHeadings, + setHeadings, +} diff --git a/src/nodes/Heading/index.js b/src/nodes/Heading/index.js new file mode 100644 index 000000000..258b094fb --- /dev/null +++ b/src/nodes/Heading/index.js @@ -0,0 +1,40 @@ +import TipTapHeading from '@tiptap/extension-heading' +import debounce from 'debounce' +import { setHeadings, extractHeadings } from './extractor.js' + +const Heading = TipTapHeading.extend({ + addAttributes() { + return { + ...this.parent(), + id: { + default: undefined, + rendered: true, + }, + uuid: { + default: undefined, + rendered: false, + }, + } + }, + addKeyboardShortcuts() { + return this.options.levels.reduce((items, level) => ({ + ...items, + [`Mod-Shift-${level}`]: () => this.editor.commands.toggleHeading({ level }), + }), {}) + }, + + onDestroy() { + setHeadings([]) + + if (this.parent) { + this.parent() + } + }, + + onUpdate: debounce(function onUpdate({ editor }) { + extractHeadings(editor) + }, 900), + +}) + +export default Heading diff --git a/src/nodes/Heading/slug.js b/src/nodes/Heading/slug.js new file mode 100644 index 000000000..266a42ed3 --- /dev/null +++ b/src/nodes/Heading/slug.js @@ -0,0 +1,10 @@ +const regexSpaces = /\s+/g +const regexInvalidCaracters = /[^\p{Letter}\p{Mark}\w\s-]/gu + +const slugify = (str) => String(str) + .toLowerCase() + .replace(regexInvalidCaracters, '') + .trim() + .replace(regexSpaces, '-') + +export { slugify } |