Skip to content

Commit

Permalink
feat(jsx): support 'style' and 'styles' props (#674)
Browse files Browse the repository at this point in the history
* feat(jsx): support 'style' and 'styles' props

* feat(jsx): add $attrs attribute
  • Loading branch information
krulod authored Oct 20, 2023
1 parent 399d36f commit a3ad7d8
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 32 deletions.
81 changes: 49 additions & 32 deletions packages/jsx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Action,
atom,
createCtx,
Ctx,
Expand All @@ -13,7 +14,7 @@ import { parseAtoms } from '@reatom/lens'
declare type JSXElement = JSX.Element
export type { JSXElement, JSX }

type ElementTag = keyof JSX.HTMLElementTags | keyof JSX.SVGElementTags
export type ElementTag = keyof JSX.HTMLElementTags | keyof JSX.SVGElementTags

export type Component<Props> = (props: Props) => JSXElement

Expand Down Expand Up @@ -42,6 +43,47 @@ export const reatomJsx = (ctx: Ctx) => {
})
}

let create = (tag: string, props: Rec) => {
let element =
tag === 'svg'
? document.createElementNS('http://www.w3.org/2000/svg', tag)
: document.createElement(tag)

for (let k in props) {
if (k === 'children') continue
bindAttr(element, k, props[k])
}

render(element, props.children ?? [])

return element
}

const bindAttr = (element: any, key: any, val: any) => {
if (key === '$attrs') {
const recs = Array.isArray(val) ? val : [val]
for (const rec of recs) {
for (const k in rec) bindAttr(element, k, rec[k])
}
} else if (isAtom(val)) {
if (val.__reatom.isAction) {
element[key] = (...args: any) => (val as Action)(ctx, ...args)
} else {
// TODO handle unsubscribe!
var un: undefined | Unsubscribe = ctx.subscribe(val, (val) =>
!un || element.isConnected ? renderAttr(element, key, val) : un(),
)
unlink(element, un)
}
} else renderAttr(element, key, val)
}

const renderAttr = (element: any, key: any, val: any) => {
if (key === 'style') {
for (const style in val) element.style.setProperty(style, val[style])
} else element[key] = val
}

let render = (parent: Element, children: JSXElement[]) => {
// TODO support Atom<Array>
let walk = (child: any) => {
Expand Down Expand Up @@ -82,47 +124,22 @@ export const reatomJsx = (ctx: Ctx) => {
children.forEach(walk)
}

let h = ((tag: any, props: Rec, ...children: any[]) => {
let h = ((tag: any, props: Rec, ...children: JSXElement[]) => {
if (tag === hf) return children

props.children = children

if (typeof tag === 'function') {
;(props ??= {}).children = children
return tag(props)
}

let element =
tag === 'svg'
? document.createElementNS('http://www.w3.org/2000/svg', tag)
: document.createElement(tag)

for (let k in props) {
let prop = props[k]
if (isAtom(prop)) {
if (prop.__reatom.isAction) {
element[k] = (...a: any[]) => prop(ctx, ...a)
} else {
// TODO handle unsubscribe!
var un: undefined | Unsubscribe = ctx.subscribe(prop, (v) =>
!un || element.isConnected ? (element[k] = v) : un(),
)

unlink(element, un)
}
} else {
element[k] = prop
}
}

render(element, children)

return element
return create(tag, props)
}) as <T extends ComponentLike>(
tag: T,
props: InferProps<T>,
...children: JSXElement[]
) => Element

/** Fragment */
let hf = noop

let mount = (target: Element, child: JSXElement) => {
Expand All @@ -144,8 +161,8 @@ export const reatomJsx = (ctx: Ctx) => {
})
}

return { h, hf, mount }
return { h, hf, mount, create }
}

export const ctx = createCtx()
export const { h, hf, mount } = reatomJsx(ctx)
export const { h, hf, mount, create } = reatomJsx(ctx)
1 change: 1 addition & 0 deletions packages/jsx/src/jsx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export namespace JSX {
OnAttributes<T>,
OnCaptureAttributes<T>, */
extends CustomEventHandlers<T> {
$attrs?: Atom<this | this[]>
children?: Element
innerHTML?: string
innerText?: string | number
Expand Down

1 comment on commit a3ad7d8

@vercel
Copy link

@vercel vercel bot commented on a3ad7d8 Oct 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.