Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Experimental Children #154

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import parseChildren from "./parseChildren"
import transforms, { R2WCType } from "./transforms"
import { toDashedCase } from "./utils"

Expand All @@ -7,6 +8,7 @@ type PropNames<Props> = Array<PropName<Props>>
export interface R2WCOptions<Props> {
shadow?: "open" | "closed"
props?: PropNames<Props> | Partial<Record<PropName<Props>, R2WCType>>
experimentalChildren?: boolean
}

export interface R2WCRenderer<Props, Context> {
Expand All @@ -21,6 +23,7 @@ export interface R2WCRenderer<Props, Context> {

export interface R2WCBaseProps {
container?: HTMLElement
children?: React.ReactNode
}

const renderSymbol = Symbol.for("r2wc.render")
Expand Down Expand Up @@ -98,6 +101,10 @@ export default function r2wc<Props extends R2WCBaseProps, Context>(
this[propsSymbol][prop] = transform.parse(value, attribute, this)
}
}

if (options.experimentalChildren) {
this[propsSymbol].children = parseChildren(this.childNodes)
}
}

connectedCallback() {
Expand Down
76 changes: 76 additions & 0 deletions packages/core/src/parseChildren.ts
Original file line number Diff line number Diff line change
@@ -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<ChildNode>,
): 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<string, unknown> = { 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
}
Loading