mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-28 14:04:43 -04:00
feat: import html-to-react from https://github.com/hedgedoc/html-to-react
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
5dc6526278
commit
84527f065c
37 changed files with 1388 additions and 0 deletions
50
html-to-react/src/utils/convertInlineStyleToMap.ts
Normal file
50
html-to-react/src/utils/convertInlineStyleToMap.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts an inline style string into an object of React style properties
|
||||
*
|
||||
* @param {String} inlineStyle='' The inline style to convert
|
||||
* @returns {Object} The converted style
|
||||
*/
|
||||
export function convertInlineStyleToMap(
|
||||
inlineStyle = ''
|
||||
): Record<string, string> {
|
||||
if (inlineStyle === '') {
|
||||
return {}
|
||||
}
|
||||
|
||||
return inlineStyle.split(';').reduce(
|
||||
(styleObject, stylePropertyValue) => {
|
||||
// extract the style property name and value
|
||||
const [property, value] = stylePropertyValue
|
||||
.split(/^([^:]+):/)
|
||||
.filter((val, i) => i > 0)
|
||||
.map((item) => item.trim())
|
||||
|
||||
// if there is no value (i.e. no : in the style) then ignore it
|
||||
if (value === undefined) {
|
||||
return styleObject
|
||||
}
|
||||
|
||||
// convert the property name into the correct React format
|
||||
// remove all hyphens and convert the letter immediately after each hyphen to upper case
|
||||
// additionally don't uppercase any -ms- prefix
|
||||
// e.g. -ms-style-property = msStyleProperty
|
||||
// -webkit-style-property = WebkitStyleProperty
|
||||
const replacedProperty = property
|
||||
.toLowerCase()
|
||||
.replace(/^-ms-/, 'ms-')
|
||||
.replace(/-(.)/g, (_, character) => character.toUpperCase())
|
||||
|
||||
// add the new style property and value to the style object
|
||||
styleObject[replacedProperty] = value
|
||||
|
||||
return styleObject
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
}
|
34
html-to-react/src/utils/generatePropsFromAttributes.ts
Normal file
34
html-to-react/src/utils/generatePropsFromAttributes.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { mapHtmlAttributesToReactElementAttributes } from './mapHtmlAttributesToReactElementAttributes.js'
|
||||
import { convertInlineStyleToMap } from './convertInlineStyleToMap.js'
|
||||
|
||||
/**
|
||||
* Generates props for a React element from an object of HTML attributes
|
||||
*
|
||||
* @param {Object} attributes The HTML attributes
|
||||
* @param {String} key The key to give the react element
|
||||
*/
|
||||
export function generatePropsFromAttributes(
|
||||
attributes: Record<string, string>,
|
||||
key: string | number
|
||||
): Record<string, string | Record<string, string>> {
|
||||
const props = Object.assign(
|
||||
{ key },
|
||||
mapHtmlAttributesToReactElementAttributes(attributes)
|
||||
) as Record<string, string | Record<string, string>>
|
||||
|
||||
if (props.style) {
|
||||
if (typeof props.style === 'string') {
|
||||
props.style = convertInlineStyleToMap(props.style)
|
||||
}
|
||||
} else {
|
||||
delete props.style
|
||||
}
|
||||
|
||||
return props
|
||||
}
|
15
html-to-react/src/utils/isValidTagOrAttributeName.ts
Normal file
15
html-to-react/src/utils/isValidTagOrAttributeName.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
const VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_.\-\d]*$/
|
||||
const nameCache: Record<string, boolean> = {}
|
||||
|
||||
export function isValidTagOrAttributeName(tagName: string): boolean {
|
||||
if (!(tagName in nameCache)) {
|
||||
nameCache[tagName] = VALID_TAG_REGEX.test(tagName)
|
||||
}
|
||||
return nameCache[tagName]
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import booleanAttributes from '../dom/attributes/booleanAttributes.js'
|
||||
import reactAttributes from '../dom/attributes/reactAttributes.js'
|
||||
import { isValidTagOrAttributeName } from './isValidTagOrAttributeName.js'
|
||||
|
||||
/**
|
||||
* Returns the parsed attribute value taking into account things like boolean attributes
|
||||
*
|
||||
* @param {string} attribute The name of the attribute
|
||||
* @param {string} value The value of the attribute from the HTML
|
||||
* @returns {string} The parsed attribute value
|
||||
*/
|
||||
function getParsedAttributeValue(attribute: string, value: string): string {
|
||||
if (booleanAttributes.has(attribute.toLowerCase())) {
|
||||
value = attribute
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't pass through event handler attributes at all (on...)
|
||||
* This is the same heuristic used by React:
|
||||
* https://github.com/facebook/react/blob/7a5b8227c7/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js#L23
|
||||
* @param {string} attribute The attribute name to check
|
||||
*/
|
||||
function isEventHandlerAttribute(attribute: string): boolean {
|
||||
return attribute.startsWith('on')
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object of standard HTML property names and converts them to their React counterpart. If the react
|
||||
* version does not exist for an attribute then just use it as it is
|
||||
*
|
||||
* @param {Object} attributes The HTML attributes to convert
|
||||
* @returns {Object} The React attributes
|
||||
*/
|
||||
export function mapHtmlAttributesToReactElementAttributes(
|
||||
attributes: Record<string, string>
|
||||
): Record<string, string> {
|
||||
return Object.keys(attributes)
|
||||
.filter(
|
||||
(attribute) =>
|
||||
!isEventHandlerAttribute(attribute) &&
|
||||
isValidTagOrAttributeName(attribute)
|
||||
)
|
||||
.reduce(
|
||||
(mappedAttributes, attribute) => {
|
||||
// lowercase the attribute name and find it in the react attribute map
|
||||
const lowerCaseAttribute = attribute.toLowerCase()
|
||||
|
||||
// format the attribute name
|
||||
const name = reactAttributes[lowerCaseAttribute] || attribute
|
||||
|
||||
// add the parsed attribute value to the mapped attributes
|
||||
mappedAttributes[name] = getParsedAttributeValue(
|
||||
name,
|
||||
attributes[attribute]
|
||||
)
|
||||
|
||||
return mappedAttributes
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue