Skip to content

Commit

Permalink
feat: allow all dom elements for the chakra factory (#5508)
Browse files Browse the repository at this point in the history
* feat: replace hardcoded element list with proxy

* chore: polish

* chore: rename interface

* docs: add example
  • Loading branch information
TimKolberger authored Feb 17, 2022
1 parent be02865 commit e5e0f25
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 97 deletions.
13 changes: 13 additions & 0 deletions .changeset/unlucky-dingos-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@chakra-ui/system": minor
---

Allow all `JSX.IntrinsicElements` for the chakra factory. This allows to use
[every DOM element](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/30a2f70db2f9ac223fd923ff1f8bcc175c082fd0/types/react/index.d.ts#L3111-L3288)
with the shorthand version:

```jsx live=false
<chakra.header>Header</chakra.header>
<chakra.main>Main</chakra.main>
<chakra.footer>Many more</chakra.footer>
```
37 changes: 37 additions & 0 deletions packages/system/src/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { DOMElements } from "./system.utils"
import { ChakraStyledOptions, HTMLChakraComponents, styled } from "./system"
import { As, ChakraComponent } from "./system.types"

type ChakraFactory = {
<T extends As, P = {}>(
component: T,
options?: ChakraStyledOptions,
): ChakraComponent<T, P>
}

function factory() {
const cache = new Map<DOMElements, ChakraComponent<DOMElements>>()

return new Proxy(styled, {
/**
* @example
* const Div = chakra("div")
* const WithChakra = chakra(AnotherComponent)
*/
apply(target, thisArg, argArray: [DOMElements, ChakraStyledOptions]) {
return styled(...argArray)
},
/**
* @example
* <chakra.div />
*/
get(_, element: DOMElements) {
if (!cache.has(element)) {
cache.set(element, styled(element))
}
return cache.get(element)
},
}) as ChakraFactory & HTMLChakraComponents
}

export const chakra = factory()
1 change: 1 addition & 0 deletions packages/system/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export { omitThemingProps } from "./system.utils"
export * from "./system"
export * from "./forward-ref"
export * from "./use-style-config"
export * from "./factory"
export { shouldForwardProp } from "./should-forward-prop"
52 changes: 20 additions & 32 deletions packages/system/src/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {
StyleProps,
SystemStyleObject,
} from "@chakra-ui/styled-system"
import { filterUndefined, objectFilter, runIfFn } from "@chakra-ui/utils"
import { Dict, filterUndefined, objectFilter, runIfFn } from "@chakra-ui/utils"
import _styled, { CSSObject, FunctionInterpolation } from "@emotion/styled"
import { shouldForwardProp } from "./should-forward-prop"
import { As, ChakraComponent, ChakraProps, PropsOf } from "./system.types"
import { domElements, DOMElements } from "./system.utils"
import { DOMElements } from "./system.utils"

type StyleResolverProps = SystemStyleObject & {
__css?: SystemStyleObject
Expand Down Expand Up @@ -38,22 +38,24 @@ interface GetStyleObject {
* behaviors. Right now, the `sx` prop has the highest priority so the resolved
* fontSize will be `40px`
*/
export const toCSSObject: GetStyleObject = ({ baseStyle }) => (props) => {
const { theme, css: cssProp, __css, sx, ...rest } = props
const styleProps = objectFilter(rest, (_, prop) => isStyleProp(prop))
const finalBaseStyle = runIfFn(baseStyle, props)
const finalStyles = Object.assign(
{},
__css,
finalBaseStyle,
filterUndefined(styleProps),
sx,
)
const computedCSS = css(finalStyles)(props.theme)
return cssProp ? [computedCSS, cssProp] : computedCSS
}
export const toCSSObject: GetStyleObject =
({ baseStyle }) =>
(props) => {
const { theme, css: cssProp, __css, sx, ...rest } = props
const styleProps = objectFilter(rest, (_, prop) => isStyleProp(prop))
const finalBaseStyle = runIfFn(baseStyle, props)
const finalStyles = Object.assign(
{},
__css,
finalBaseStyle,
filterUndefined(styleProps),
sx,
)
const computedCSS = css(finalStyles)(props.theme)
return cssProp ? [computedCSS, cssProp] : computedCSS
}

interface StyledOptions {
export interface ChakraStyledOptions extends Dict {
shouldForwardProp?(prop: string): boolean
label?: string
baseStyle?:
Expand All @@ -63,7 +65,7 @@ interface StyledOptions {

export function styled<T extends As, P = {}>(
component: T,
options?: StyledOptions,
options?: ChakraStyledOptions,
) {
const { baseStyle, ...styledOptions } = options ?? {}

Expand All @@ -89,17 +91,3 @@ export type HTMLChakraProps<T extends As> = Omit<
: "ref" | keyof StyleProps
> &
ChakraProps & { as?: As }

type ChakraFactory = {
<T extends As, P = {}>(
component: T,
options?: StyledOptions,
): ChakraComponent<T, P>
}

export const chakra = (styled as unknown) as ChakraFactory &
HTMLChakraComponents

domElements.forEach((tag) => {
chakra[tag] = chakra(tag)
})
68 changes: 3 additions & 65 deletions packages/system/src/system.utils.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,12 @@
import { isString, omit, UnionStringArray, __DEV__ } from "@chakra-ui/utils"
import { isString, omit, __DEV__ } from "@chakra-ui/utils"
import * as React from "react"
import { ThemingProps } from "./system.types"

/**
* Carefully selected html elements for chakra components.
* All html and svg elements for chakra components.
* This is mostly for `chakra.<element>` syntax.
*/
export const domElements = [
"a",
"b",
"article",
"aside",
"blockquote",
"button",
"caption",
"cite",
"circle",
"code",
"dd",
"div",
"dl",
"dt",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"header",
"hr",
"img",
"input",
"kbd",
"label",
"li",
"main",
"mark",
"nav",
"ol",
"p",
"path",
"pre",
"q",
"rect",
"s",
"svg",
"section",
"select",
"strong",
"small",
"span",
"sub",
"sup",
"table",
"tbody",
"td",
"textarea",
"tfoot",
"th",
"thead",
"tr",
"ul",
] as const

export type DOMElements = UnionStringArray<typeof domElements>
export type DOMElements = keyof JSX.IntrinsicElements

export function omitThemingProps<T extends ThemingProps>(props: T) {
return omit(props, ["styleConfig", "size", "variant", "colorScheme"])
Expand Down

1 comment on commit e5e0f25

@vercel
Copy link

@vercel vercel bot commented on e5e0f25 Feb 17, 2022

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.