Skip to content

Instantly share code, notes, and snippets.

@jackrobertscott
Created June 24, 2024 12:06
Show Gist options
  • Save jackrobertscott/a8e41968181dbe61cb36893c8ba17bc4 to your computer and use it in GitHub Desktop.
Save jackrobertscott/a8e41968181dbe61cb36893c8ba17bc4 to your computer and use it in GitHub Desktop.
Create callable react components.
import {css, CSSInterpolation} from "@emotion/css"
import {
Attributes,
ComponentClass,
createElement,
FC,
HTMLProps,
RefAttributes,
SVGProps,
} from "react"
import {toKebabCase} from "./changeCase"
import {MergeObjects} from "./sharedTypes"
export type ClassNameProp = string | undefined | null | false | ClassNameProp[]
export type ComponentProps<T> = T extends keyof HTMLElementTagNameMap
? MergeObjects<
HTMLProps<HTMLElementTagNameMap[T]> &
RefAttributes<HTMLElementTagNameMap[T]>,
{
data?: Record<string, any>
className?: ClassNameProp | ClassNameProp[]
css?: CSSInterpolation
}
>
: T extends keyof SVGElementTagNameMap
? SVGProps<SVGElementTagNameMap[T]> & RefAttributes<SVGElementTagNameMap[T]>
: T extends FC<infer P>
? P & Attributes
: T extends ComponentClass<infer P>
? P & Attributes
: unknown
function _createElement<T extends string | FC<any> | ComponentClass<any, any>>(
tag: T,
props?: ComponentProps<T>,
...children: any[]
) {
if (props?.children) {
children = children.concat(props?.children)
delete props.children
}
if (typeof tag === "string") {
if (props?.data) {
for (const [key, value] of Object.entries(props.data)) {
props["data-" + toKebabCase(key)] = value
}
delete props.data
}
if (props?.className) {
props.className = [props.className]
.flat(Infinity)
.filter(Boolean)
.join(" ")
}
if (props?.css) {
props.className = [props.className, css(props.css)]
.filter(Boolean)
.join(" ")
delete props.css
}
}
return createElement(tag, props, ...children)
}
export function createComponent<T extends {}>(tag: FC<T>): FC<T>
export function createComponent<T extends string>(tag: T): FC<ComponentProps<T>>
export function createComponent(tag: any): FC<any> {
return (props) => _createElement(tag, props)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment