add data-attribute fallback to attribute parser

This commit is contained in:
Philipp Kühn 2020-10-24 23:53:56 +02:00
parent c85844ae11
commit 16d52d05a0
5 changed files with 51 additions and 53 deletions

View File

@ -6,12 +6,9 @@ export type Extensions = (Extension | NodeExtension | MarkExtension)[]
export type Attribute = { export type Attribute = {
default: any, default: any,
rendered?: false, rendered?: boolean,
} | { renderHTML?: ((attributes: { [key: string]: any }) => any) | null,
default: any, parseHTML?: ((node: HTMLElement) => { [key: string]: any }) | null,
rendered?: true,
renderHTML: (attributes: { [key: string]: any }) => any,
parseHTML: (node: HTMLElement) => { [key: string]: any },
} }
export type Attributes = { export type Attributes = {
@ -21,12 +18,7 @@ export type Attributes = {
export type ExtensionAttribute = { export type ExtensionAttribute = {
type: string, type: string,
name: string, name: string,
attribute: { attribute: Required<Attribute>,
default: any,
rendered: boolean,
renderHTML: (attributes: { [key: string]: any }) => any,
parseHTML: (node: HTMLElement) => { [key: string]: any },
},
} }
export type GlobalAttributes = { export type GlobalAttributes = {

View File

@ -14,8 +14,8 @@ export default function getAttributesFromExtensions(extensions: Extensions) {
const defaultAttribute: Required<Attribute> = { const defaultAttribute: Required<Attribute> = {
default: null, default: null,
rendered: true, rendered: true,
renderHTML: () => ({}), renderHTML: null,
parseHTML: () => ({}), parseHTML: null,
} }
extensions.forEach(extension => { extensions.forEach(extension => {

View File

@ -1,21 +1,18 @@
import { Node, Mark } from 'prosemirror-model' import { Node, Mark } from 'prosemirror-model'
import { ExtensionAttribute } from '../types' import { ExtensionAttribute } from '../types'
import mergeAttributes from './mergeAttributes' import mergeAttributes from './mergeAttributes'
import isEmptyObject from './isEmptyObject'
export default function getRenderedAttributes(nodeOrMark: Node | Mark, extensionAttributes: ExtensionAttribute[]): { [key: string]: any } { export default function getRenderedAttributes(nodeOrMark: Node | Mark, extensionAttributes: ExtensionAttribute[]): { [key: string]: any } {
return extensionAttributes return extensionAttributes
.filter(item => item.attribute.rendered) .filter(item => item.attribute.rendered)
.map(item => { .map(item => {
const renderedAttributes = item.attribute.renderHTML(nodeOrMark.attrs) if (!item.attribute.renderHTML) {
if (isEmptyObject(renderedAttributes)) {
return { return {
[`data-${item.name}`]: nodeOrMark.attrs[item.name], [`data-${item.name}`]: nodeOrMark.attrs[item.name],
} }
} }
return renderedAttributes return item.attribute.renderHTML(nodeOrMark.attrs)
}) })
.reduce((attributes, attribute) => { .reduce((attributes, attribute) => {
return mergeAttributes(attributes, attribute) return mergeAttributes(attributes, attribute)

View File

@ -1,11 +1,10 @@
import { import { NodeSpec, MarkSpec, Schema } from 'prosemirror-model'
NodeSpec, MarkSpec, Schema, ParseRule, import { Extensions } from '../types'
} from 'prosemirror-model'
import { ExtensionAttribute, Extensions } from '../types'
import splitExtensions from './splitExtensions' import splitExtensions from './splitExtensions'
import getAttributesFromExtensions from './getAttributesFromExtensions' import getAttributesFromExtensions from './getAttributesFromExtensions'
import getRenderedAttributes from './getRenderedAttributes' import getRenderedAttributes from './getRenderedAttributes'
import isEmptyObject from './isEmptyObject' import isEmptyObject from './isEmptyObject'
import injectExtensionAttributesToParseRule from './injectExtensionAttributesToParseRule'
function cleanUpSchemaItem<T>(data: T) { function cleanUpSchemaItem<T>(data: T) {
return Object.fromEntries(Object.entries(data).filter(([key, value]) => { return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
@ -17,34 +16,6 @@ function cleanUpSchemaItem<T>(data: T) {
})) as T })) as T
} }
function injectExtensionAttributes(parseRule: ParseRule, extensionAttributes: ExtensionAttribute[]): ParseRule {
if (parseRule.style) {
return parseRule
}
return {
...parseRule,
getAttrs: node => {
const oldAttributes = parseRule.getAttrs
? parseRule.getAttrs(node)
: parseRule.attrs
if (oldAttributes === false) {
return false
}
const newAttributes = extensionAttributes
.filter(item => item.attribute.rendered)
.reduce((items, item) => ({
...items,
...item.attribute.parseHTML(node as HTMLElement),
}), {})
return { ...oldAttributes, ...newAttributes }
},
}
}
export default function getSchema(extensions: Extensions): Schema { export default function getSchema(extensions: Extensions): Schema {
const allAttributes = getAttributesFromExtensions(extensions) const allAttributes = getAttributesFromExtensions(extensions)
const { nodeExtensions, markExtensions } = splitExtensions(extensions) const { nodeExtensions, markExtensions } = splitExtensions(extensions)
@ -72,7 +43,7 @@ export default function getSchema(extensions: Extensions): Schema {
if (extension.parseHTML) { if (extension.parseHTML) {
schema.parseDOM = extension.parseHTML schema.parseDOM = extension.parseHTML
.bind(context)() .bind(context)()
?.map(parseRule => injectExtensionAttributes(parseRule, extensionAttributes)) ?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
} }
if (extension.renderHTML) { if (extension.renderHTML) {
@ -101,7 +72,7 @@ export default function getSchema(extensions: Extensions): Schema {
if (extension.parseHTML) { if (extension.parseHTML) {
schema.parseDOM = extension.parseHTML schema.parseDOM = extension.parseHTML
.bind(context)() .bind(context)()
?.map(parseRule => injectExtensionAttributes(parseRule, extensionAttributes)) ?.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes))
} }
if (extension.renderHTML) { if (extension.renderHTML) {

View File

@ -0,0 +1,38 @@
import { ParseRule } from 'prosemirror-model'
import { ExtensionAttribute } from '../types'
export default function injectExtensionAttributesToParseRule(parseRule: ParseRule, extensionAttributes: ExtensionAttribute[]): ParseRule {
if (parseRule.style) {
return parseRule
}
return {
...parseRule,
getAttrs: node => {
const oldAttributes = parseRule.getAttrs
? parseRule.getAttrs(node)
: parseRule.attrs
if (oldAttributes === false) {
return false
}
const newAttributes = extensionAttributes
.filter(item => item.attribute.rendered)
.reduce((items, item) => {
const attributes = item.attribute.parseHTML
? item.attribute.parseHTML(node as HTMLElement)
: {
[item.name]: (node as HTMLElement).dataset[item.name],
}
return {
...items,
...attributes,
}
}, {})
return { ...oldAttributes, ...newAttributes }
},
}
}