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
Changes from 1 commit
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
Next Next commit
misc cleanup
  • Loading branch information
Christopher J Baker committed Oct 25, 2023
commit 4f34b2f55fefe022a4b741571e997dc657368bb0
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"private": true,
"homepage": "https://www.bitovi.com/open-source/react-to-web-component",
"namespace": "r2wc",
"workspaces": [
"packages/*"
],
"private": true,
"scripts": {
"check-packages": "ts-node ./scripts/check-packages"
},
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"name": "@r2wc/core",
"version": "1.0.1",
"description": "Convert framework components to native Web Components.",
"homepage": "https://www.bitovi.com/frontend-javascript-consulting/react-consulting",
"homepage": "https://www.bitovi.com/open-source/react-to-web-component",
"author": "Bitovi",
"license": "MIT",
"keywords": [
37 changes: 16 additions & 21 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { R2WCType } from "./transforms"

import transforms from "./transforms"
import { toDashedCase } from "./utils"

type PropName<Props> = Extract<keyof Props, string>
type PropName<Props> = Exclude<Extract<keyof Props, string>, "container">
type PropNames<Props> = Array<PropName<Props>>

export interface R2WCOptions<Props> {
@@ -20,6 +21,10 @@ export interface R2WCRenderer<Props, Context> {
unmount: (context: Context) => void
}

export interface R2WCBaseProps {
container?: HTMLElement
}

const renderSymbol = Symbol.for("r2wc.render")
const connectedSymbol = Symbol.for("r2wc.connected")
const contextSymbol = Symbol.for("r2wc.context")
@@ -32,7 +37,7 @@ const propsSymbol = Symbol.for("r2wc.props")
* @param {String?} options.shadow - Shadow DOM mode as either open or closed.
* @param {Object|Array?} options.props - Array of camelCasedProps to watch as Strings or { [camelCasedProp]: "string" | "number" | "boolean" | "function" | "json" }
*/
export default function r2wc<Props, Context>(
export default function r2wc<Props extends R2WCBaseProps, Context>(
ReactComponent: React.ComponentType<Props>,
options: R2WCOptions<Props>,
renderer: R2WCRenderer<Props, Context>,
@@ -43,14 +48,9 @@ export default function r2wc<Props, Context>(
: []
}

const propNames = (
Array.isArray(options.props)
? options.props.slice()
: (Object.keys(options.props) as PropNames<Props>)
).filter((prop) => {
//@ts-ignore
return prop !== "container"
})
const propNames = Array.isArray(options.props)
? options.props.slice()
: (Object.keys(options.props) as PropNames<Props>)

const propTypes = {} as Record<PropName<Props>, R2WCType>
const mapPropAttribute = {} as Record<PropName<Props>, string>
@@ -60,7 +60,7 @@ export default function r2wc<Props, Context>(
? "string"
: options.props[prop]

const attribute = toDashedStyle(prop)
const attribute = toDashedCase(prop)

mapPropAttribute[prop] = attribute
mapAttributeProp[attribute] = prop
@@ -87,7 +87,6 @@ export default function r2wc<Props, Context>(
this.container = this
}

// @ts-ignore: There won't always be a container in the definition
this[propsSymbol].container = this.container

for (const prop of propNames) {
@@ -96,9 +95,9 @@ export default function r2wc<Props, Context>(
const type = propTypes[prop]
const transform = transforms[type]

if (value && transform?.parse) {
if (transform?.parse && value) {
//@ts-ignore
this[propsSymbol][prop] = transform.parse(value, this)
this[propsSymbol][prop] = transform.parse(value, attribute, this)
}
}
}
@@ -126,9 +125,9 @@ export default function r2wc<Props, Context>(
const type = propTypes[prop]
const transform = transforms[type]

if (prop in propTypes && transform?.parse) {
if (prop in propTypes && transform?.parse && value) {
//@ts-ignore
this[propsSymbol][prop] = transform.parse(value, this)
this[propsSymbol][prop] = transform.parse(value, attribute, this)

this[renderSymbol]()
}
@@ -165,7 +164,7 @@ export default function r2wc<Props, Context>(
const transform = transforms[type]
if (transform?.stringify) {
//@ts-ignore
const attributeValue = transform.stringify(value)
const attributeValue = transform.stringify(value, attribute, this)
const oldAttributeValue = this.getAttribute(attribute)

if (oldAttributeValue !== attributeValue) {
@@ -178,7 +177,3 @@ export default function r2wc<Props, Context>(

return ReactWebComponent
}

function toDashedStyle(camelCase = "") {
return camelCase.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
}
4 changes: 2 additions & 2 deletions packages/core/src/transforms/boolean.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Transform } from "./index"

const string: Transform<boolean> = {
const boolean: Transform<boolean> = {
stringify: (value) => (value ? "true" : "false"),
parse: (value) => /^[ty1-9]/i.test(value),
}

export default string
export default boolean
6 changes: 3 additions & 3 deletions packages/core/src/transforms/function.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Transform } from "./index"

const string: Transform<(...args: unknown[]) => unknown> = {
const function_: Transform<(...args: unknown[]) => unknown> = {
stringify: (value) => value.name,
parse: (value, element) => {
parse: (value, attribute, element) => {
const fn = (() => {
if (typeof window !== "undefined" && value in window) {
// @ts-expect-error
@@ -19,4 +19,4 @@ const string: Transform<(...args: unknown[]) => unknown> = {
},
}

export default string
export default function_
4 changes: 2 additions & 2 deletions packages/core/src/transforms/index.ts
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@ import function_ from "./function"
import json from "./json"

export interface Transform<Type> {
stringify?: (value: Type) => string
parse: (value: string, element: HTMLElement) => Type
stringify?: (value: Type, attribute: string, element: HTMLElement) => string
parse: (value: string, attribute: string, element: HTMLElement) => Type
}

const transforms = {
4 changes: 2 additions & 2 deletions packages/core/src/transforms/json.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Transform } from "./index"

const string: Transform<string> = {
const json: Transform<string> = {
stringify: (value) => JSON.stringify(value),
parse: (value) => JSON.parse(value),
}

export default string
export default json
4 changes: 2 additions & 2 deletions packages/core/src/transforms/number.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Transform } from "./index"

const string: Transform<number> = {
const number: Transform<number> = {
stringify: (value) => `${value}`,
parse: (value) => parseFloat(value),
}

export default string
export default number
10 changes: 10 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function toDashedCase(camelCase: string): string {
return camelCase.replace(
/([a-z0-9])([A-Z])/g,
(_, a, b) => `${a}-${b.toLowerCase()}`,
)
}

export function toCamelCase(dashedCase: string): string {
return dashedCase.replace(/[-:]([a-z])/g, (_, b) => `${b.toUpperCase()}`)
}
2 changes: 1 addition & 1 deletion packages/legacy/README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

`react-to-webcomponent` converts [React](https://reactjs.org/) components to [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)! It lets you share React components as native elements that **don't** require mounted being through React. The custom element acts as a wrapper for the underlying React component. Use these custom elements with any project that uses HTML even in any framework (vue, svelte, angular, ember, canjs) the same way you would use standard HTML elements.

> Note: This is a compatibility wrapper around our new, simpler API. We highly reccomend using the new [@r2wc/react-to-web-component](https://github.com/bitovi/react-to-web-component) package.
> Note: This is a compatibility wrapper around our new, simpler API. We highly reccomend using the new [@r2wc/react-to-web-component](https://www.npmjs.com/package/@r2wc/react-to-web-component) package.

`react-to-webcomponent`:

2 changes: 1 addition & 1 deletion packages/legacy/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"name": "react-to-webcomponent",
"version": "2.0.0",
"description": "Convert React components to native Web Components.",
"homepage": "https://www.bitovi.com/frontend-javascript-consulting/react-consulting",
"homepage": "https://www.bitovi.com/open-source/react-to-web-component",
"author": "Bitovi",
"license": "MIT",
"keywords": [
2 changes: 1 addition & 1 deletion packages/react-to-web-component/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"name": "@r2wc/react-to-web-component",
"version": "2.0.3",
"description": "Convert React components to native Web Components.",
"homepage": "https://www.bitovi.com/frontend-javascript-consulting/react-consulting",
"homepage": "https://www.bitovi.com/open-source/react-to-web-component",
"author": "Bitovi",
"license": "MIT",
"keywords": [
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
"composite": true,
"incremental": true,
"strict": true,

"allowJs": false,
"allowSyntheticDefaultImports": true,
"downlevelIteration": true,