mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-06-07 17:43:49 +08:00
feat(react): allow attrs to be a callback
This commit is contained in:
parent
c99627d7ce
commit
4ff2a4eaa1
5
.changeset/warm-bananas-call.md
Normal file
5
.changeset/warm-bananas-call.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tiptap/react": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
ReactNodeViewRenderer now accepts a callback for attrs of the wrapping element to be updated on each node view update
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
DecorationWithType,
|
DecorationWithType,
|
||||||
Editor,
|
Editor,
|
||||||
|
getRenderedAttributes,
|
||||||
NodeView,
|
NodeView,
|
||||||
NodeViewProps,
|
NodeViewProps,
|
||||||
NodeViewRenderer,
|
NodeViewRenderer,
|
||||||
@ -27,7 +28,12 @@ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|||||||
| null
|
| null
|
||||||
as?: string
|
as?: string
|
||||||
className?: string
|
className?: string
|
||||||
attrs?: Record<string, string>
|
attrs?:
|
||||||
|
| Record<string, string>
|
||||||
|
| ((props: {
|
||||||
|
node: ProseMirrorNode
|
||||||
|
HTMLAttributes: Record<string, any>
|
||||||
|
}) => Record<string, string>)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReactNodeView extends NodeView<
|
class ReactNodeView extends NodeView<
|
||||||
@ -110,8 +116,9 @@ class ReactNodeView extends NodeView<
|
|||||||
props,
|
props,
|
||||||
as,
|
as,
|
||||||
className: `node-${this.node.type.name} ${className}`.trim(),
|
className: `node-${this.node.type.name} ${className}`.trim(),
|
||||||
attrs: this.options.attrs,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.updateElementAttributes()
|
||||||
}
|
}
|
||||||
|
|
||||||
get dom() {
|
get dom() {
|
||||||
@ -154,6 +161,9 @@ class ReactNodeView extends NodeView<
|
|||||||
update(node: ProseMirrorNode, decorations: DecorationWithType[]) {
|
update(node: ProseMirrorNode, decorations: DecorationWithType[]) {
|
||||||
const updateProps = (props?: Record<string, any>) => {
|
const updateProps = (props?: Record<string, any>) => {
|
||||||
this.renderer.updateProps(props)
|
this.renderer.updateProps(props)
|
||||||
|
if (typeof this.options.attrs === 'function') {
|
||||||
|
this.updateElementAttributes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.type !== this.node.type) {
|
if (node.type !== this.node.type) {
|
||||||
@ -207,6 +217,23 @@ class ReactNodeView extends NodeView<
|
|||||||
this.editor.off('selectionUpdate', this.handleSelectionUpdate)
|
this.editor.off('selectionUpdate', this.handleSelectionUpdate)
|
||||||
this.contentDOMElement = null
|
this.contentDOMElement = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateElementAttributes() {
|
||||||
|
if (this.options.attrs) {
|
||||||
|
let attrsObj: Record<string, string> = {}
|
||||||
|
|
||||||
|
if (typeof this.options.attrs === 'function') {
|
||||||
|
const extensionAttributes = this.editor.extensionManager.attributes
|
||||||
|
const HTMLAttributes = getRenderedAttributes(this.node, extensionAttributes)
|
||||||
|
|
||||||
|
attrsObj = this.options.attrs({ node: this.node, HTMLAttributes })
|
||||||
|
} else {
|
||||||
|
attrsObj = this.options.attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderer.updateAttributes(attrsObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReactNodeViewRenderer(
|
export function ReactNodeViewRenderer(
|
||||||
|
@ -57,14 +57,6 @@ export interface ReactRendererOptions {
|
|||||||
* @example 'foo bar'
|
* @example 'foo bar'
|
||||||
*/
|
*/
|
||||||
className?: string,
|
className?: string,
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes of the element.
|
|
||||||
* @type {Record<string, string>}
|
|
||||||
* @default {}
|
|
||||||
* @example { 'data-foo': 'bar' }
|
|
||||||
*/
|
|
||||||
attrs?: Record<string, string>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComponentType<R, P> =
|
type ComponentType<R, P> =
|
||||||
@ -103,7 +95,6 @@ export class ReactRenderer<R = unknown, P = unknown> {
|
|||||||
props = {},
|
props = {},
|
||||||
as = 'div',
|
as = 'div',
|
||||||
className = '',
|
className = '',
|
||||||
attrs,
|
|
||||||
}: ReactRendererOptions) {
|
}: ReactRendererOptions) {
|
||||||
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
||||||
this.component = component
|
this.component = component
|
||||||
@ -116,12 +107,6 @@ export class ReactRenderer<R = unknown, P = unknown> {
|
|||||||
this.element.classList.add(...className.split(' '))
|
this.element.classList.add(...className.split(' '))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs) {
|
|
||||||
Object.keys(attrs).forEach(key => {
|
|
||||||
this.element.setAttribute(key, attrs[key])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.editor.isInitialized) {
|
if (this.editor.isInitialized) {
|
||||||
// On first render, we need to flush the render synchronously
|
// On first render, we need to flush the render synchronously
|
||||||
// Renders afterwards can be async, but this fixes a cursor positioning issue
|
// Renders afterwards can be async, but this fixes a cursor positioning issue
|
||||||
@ -163,4 +148,10 @@ export class ReactRenderer<R = unknown, P = unknown> {
|
|||||||
|
|
||||||
editor?.contentComponent?.removeRenderer(this.id)
|
editor?.contentComponent?.removeRenderer(this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAttributes(attributes: Record<string, string>): void {
|
||||||
|
Object.keys(attributes).forEach(key => {
|
||||||
|
this.element.setAttribute(key, attributes[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user