105 lines
2.5 KiB
TypeScript
105 lines
2.5 KiB
TypeScript
![]() |
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) => {
|
||
|
return {
|
||
|
value: element.dataset.value
|
||
|
}
|
||
|
},
|
||
|
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
|
||
|
|
||
|
console.log('!!! attributes:', attributes)
|
||
|
const node = this.type.create(attributes)
|
||
|
tr.insert(position, node)
|
||
|
tr.insertText('\u00A0', position + 1) // it's make selection visible
|
||
|
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:
|
||
|
() =>
|
||
|
({ 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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|