From e5e0f255c95f5e41c3b17adbda28fd09f7251642 Mon Sep 17 00:00:00 2001 From: Tim Kolberger Date: Thu, 17 Feb 2022 20:14:22 +0000 Subject: [PATCH] feat: allow all dom elements for the chakra factory (#5508) * feat: replace hardcoded element list with proxy * chore: polish * chore: rename interface * docs: add example --- .changeset/unlucky-dingos-listen.md | 13 ++++++ packages/system/src/factory.ts | 37 ++++++++++++++++ packages/system/src/index.ts | 1 + packages/system/src/system.ts | 52 +++++++++------------- packages/system/src/system.utils.ts | 68 ++--------------------------- 5 files changed, 74 insertions(+), 97 deletions(-) create mode 100644 .changeset/unlucky-dingos-listen.md create mode 100644 packages/system/src/factory.ts diff --git a/.changeset/unlucky-dingos-listen.md b/.changeset/unlucky-dingos-listen.md new file mode 100644 index 00000000000..6fe967b4ba9 --- /dev/null +++ b/.changeset/unlucky-dingos-listen.md @@ -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 +Header +Main +Many more +``` diff --git a/packages/system/src/factory.ts b/packages/system/src/factory.ts new file mode 100644 index 00000000000..5d09c9543c7 --- /dev/null +++ b/packages/system/src/factory.ts @@ -0,0 +1,37 @@ +import { DOMElements } from "./system.utils" +import { ChakraStyledOptions, HTMLChakraComponents, styled } from "./system" +import { As, ChakraComponent } from "./system.types" + +type ChakraFactory = { + ( + component: T, + options?: ChakraStyledOptions, + ): ChakraComponent +} + +function factory() { + const cache = new Map>() + + return new Proxy(styled, { + /** + * @example + * const Div = chakra("div") + * const WithChakra = chakra(AnotherComponent) + */ + apply(target, thisArg, argArray: [DOMElements, ChakraStyledOptions]) { + return styled(...argArray) + }, + /** + * @example + * + */ + get(_, element: DOMElements) { + if (!cache.has(element)) { + cache.set(element, styled(element)) + } + return cache.get(element) + }, + }) as ChakraFactory & HTMLChakraComponents +} + +export const chakra = factory() diff --git a/packages/system/src/index.ts b/packages/system/src/index.ts index 3649148dd0e..73ef44fc50f 100644 --- a/packages/system/src/index.ts +++ b/packages/system/src/index.ts @@ -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" diff --git a/packages/system/src/system.ts b/packages/system/src/system.ts index 7136e94805e..adab906ad2f 100644 --- a/packages/system/src/system.ts +++ b/packages/system/src/system.ts @@ -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 @@ -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?: @@ -63,7 +65,7 @@ interface StyledOptions { export function styled( component: T, - options?: StyledOptions, + options?: ChakraStyledOptions, ) { const { baseStyle, ...styledOptions } = options ?? {} @@ -89,17 +91,3 @@ export type HTMLChakraProps = Omit< : "ref" | keyof StyleProps > & ChakraProps & { as?: As } - -type ChakraFactory = { - ( - component: T, - options?: StyledOptions, - ): ChakraComponent -} - -export const chakra = (styled as unknown) as ChakraFactory & - HTMLChakraComponents - -domElements.forEach((tag) => { - chakra[tag] = chakra(tag) -}) diff --git a/packages/system/src/system.utils.ts b/packages/system/src/system.utils.ts index f55e303763e..ca08cef4100 100644 --- a/packages/system/src/system.utils.ts +++ b/packages/system/src/system.utils.ts @@ -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.` 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 +export type DOMElements = keyof JSX.IntrinsicElements export function omitThemingProps(props: T) { return omit(props, ["styleConfig", "size", "variant", "colorScheme"])