diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 6601f1e..12ed405 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -1,3 +1,4 @@ +import parseChildren from "./parseChildren" import transforms, { R2WCType } from "./transforms" import { toDashedCase } from "./utils" @@ -7,6 +8,7 @@ type PropNames = Array> export interface R2WCOptions { shadow?: "open" | "closed" props?: PropNames | Partial, R2WCType>> + experimentalChildren?: boolean } export interface R2WCRenderer { @@ -21,6 +23,7 @@ export interface R2WCRenderer { export interface R2WCBaseProps { container?: HTMLElement + children?: React.ReactNode } const renderSymbol = Symbol.for("r2wc.render") @@ -98,6 +101,10 @@ export default function r2wc( this[propsSymbol][prop] = transform.parse(value, attribute, this) } } + + if (options.experimentalChildren) { + this[propsSymbol].children = parseChildren(this.childNodes) + } } connectedCallback() { diff --git a/packages/core/src/parseChildren.ts b/packages/core/src/parseChildren.ts new file mode 100755 index 0000000..0d9045d --- /dev/null +++ b/packages/core/src/parseChildren.ts @@ -0,0 +1,76 @@ +import React from "react" + +import { toCamelCase } from "./utils" + +const ELEMENT_NODE = 1 +const TEXT_NODE = 3 +const COMMENT_NODE = 8 + +export default function parseChildren( + input: NodeListOf, +): React.ReactNode[] { + const output: React.ReactNode[] = [] + + for (let index = 0; index < input.length; index++) { + const child = input[index] + output.push(parseChild(child, index)) + } + + return output +} + +function parseChild( + input: ChildNode, + index: number, +): React.ReactNode | undefined { + if (input.nodeType === TEXT_NODE || input.nodeType === COMMENT_NODE) { + return input.nodeValue + } + + if (input.nodeType === ELEMENT_NODE) { + const node = input as HTMLElement + const nodeName = node.localName + const children = parseChildren(node.childNodes) + + const props: Record = { key: index } + for (let { name, value } of node.attributes) { + if (name === "class") name = "class-name" + if (name === "for") name = "html-for" + if (name === "colspan") name = "colSpan" + if (name === "rowspan") name = "rowSpan" + if (nodeName === "input" && name === "value") name = "default-value" + if (nodeName === "input" && name === "checked") name = "default-checked" + + if (!name.startsWith("data-")) name = toCamelCase(name) + + if (name === "style") { + const input = value + .split(";") + .filter((value) => value.length > 0) + .map((value) => + value + .trim() + .split(":") + .map((value) => value.trim()), + ) + + const styles = {} + for (const [key, value] of input) { + const camelKey = toCamelCase(key) + + // @ts-ignore + styles[camelKey] = value + } + + // @ts-ignore + value = styles + } + + props[name] = value + } + + return React.createElement(nodeName, props, ...children) + } + + return undefined +}