webapp/src/components/Editor/extensions/Footnote.ts

99 lines
2.4 KiB
TypeScript
Raw Normal View History

import { mergeAttributes, Node } from '@tiptap/core'
declare module '@tiptap/core' {
interface Commands<ReturnType> {
Footnote: {
setFootnote: (options: { value: string }) => ReturnType
updateFootnote: (options: { value: string }) => ReturnType
deleteFootnote: () => ReturnType
}
}
}
export const Footnote = Node.create({
name: 'footnote',
addOptions() {
return {
HTMLAttributes: {},
}
},
group: 'inline',
content: 'text*',
inline: true,
isolating: true,
addAttributes() {
return {
value: {
default: null,
parseHTML: (element) => element.dataset.value || null,
renderHTML: (attributes) => {
return {
'data-value': attributes.value,
}
},
},
}
},
parseHTML() {
return [
{
tag: 'footnote',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['footnote', mergeAttributes(HTMLAttributes), 0]
},
addCommands() {
return {
setFootnote:
(attributes) =>
({ tr, state }) => {
const { selection } = state
const position = selection.$to.pos
const node = this.type.create(attributes)
tr.insert(position, node)
return true
},
updateFootnote:
(newValue) =>
({ tr, state }) => {
const { selection } = state
const { $from, $to } = selection
if ($from.parent.type.name === 'footnote' || $to.parent.type.name === 'footnote') {
const node = $from.parent.type.name === 'footnote' ? $from.parent : $to.parent
const pos = $from.parent.type.name === 'footnote' ? $from.pos - 1 : $to.pos - 1
const newNode = node.type.create({ value: newValue })
tr.setNodeMarkup(pos, null, newNode.attrs)
return true
}
return false
},
deleteFootnote:
() =>
2023-11-04 15:37:28 +00:00
// eslint-disable-next-line unicorn/consistent-function-scoping
({ tr, state }) => {
const { selection } = state
const { $from, $to } = selection
if ($from.parent.type.name === 'footnote' || $to.parent.type.name === 'footnote') {
const startPos = $from.start($from.depth)
const endPos = $to.end($to.depth)
tr.delete(startPos, endPos)
return true
}
return false
},
}
},
})