Skip to content

Commit

Permalink
fix: forward props correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
cschroeter committed Aug 1, 2024
1 parent 295bb32 commit 8283ab3
Show file tree
Hide file tree
Showing 12 changed files with 37 additions and 15 deletions.
2 changes: 1 addition & 1 deletion components/react/src/components/ui/styled/pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLElement,
Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>
>(Pagination.Root, 'root')
>(Pagination.Root, 'root', { forwardProps: ['page'] })

export const Ellipsis = withContext<
HTMLDivElement,
Expand Down
2 changes: 1 addition & 1 deletion components/react/src/components/ui/styled/pin-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootBaseProps>, PinInputVariantProps>
>(PinInput.Root, 'root')
>(PinInput.Root, 'root', { forwardProps: ['mask'] })

export const Control = withContext<
HTMLDivElement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import {
useContext,
} from 'react'
import { cx } from 'styled-system/css'
import { styled } from 'styled-system/jsx'
import { type StyledComponent, isCssProperty, styled } from 'styled-system/jsx'

type Props = Record<string, unknown>
type Recipe = {
(props?: Props): Props
splitVariantProps: (props: Props) => [Props, Props]
}
type Slot<R extends Recipe> = keyof ReturnType<R>
type Options = { forwardProps?: string[] }

const shouldForwardProp = (prop: string, variantKeys: string[], options: Options = {}) =>
options.forwardProps?.includes(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))

export const createStyleContext = <R extends Recipe>(recipe: R) => {
const StyleContext = createContext<Record<Slot<R>, string> | null>(null)
Expand All @@ -37,8 +41,15 @@ export const createStyleContext = <R extends Recipe>(recipe: R) => {
const withProvider = <T, P extends { className?: string | undefined }>(
Component: ElementType,
slot: Slot<R>,
options?: Options,
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
const StyledComponent = styled(Component)
const StyledComponent = styled(
Component,
{},
{
shouldForwardProp: (prop, variantKeys) => shouldForwardProp(prop, variantKeys, options),
},
) as StyledComponent<ElementType>
const StyledSlotProvider = forwardRef<T, P>((props, ref) => {
const [variantProps, otherProps] = recipe.splitVariantProps(props)
const slotStyles = recipe(variantProps) as Record<Slot<R>, string>
Expand Down
2 changes: 1 addition & 1 deletion components/solid/src/components/ui/styled/pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const RootProvider = withProvider<
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>
>(Pagination.Root, 'root')
>(Pagination.Root, 'root', { forwardProps: ['page'] })

export const Ellipsis = withContext<Assign<HTMLStyledProps<'div'>, Pagination.EllipsisBaseProps>>(
Pagination.Ellipsis,
Expand Down
2 changes: 1 addition & 1 deletion components/solid/src/components/ui/styled/pin-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const RootProvider = withProvider<
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootBaseProps>, PinInputVariantProps>
>(PinInput.Root, 'root')
>(PinInput.Root, 'root', { forwardProps: ['mask'] })

export const Control = withContext<Assign<HTMLStyledProps<'div'>, PinInput.ControlBaseProps>>(
PinInput.Control,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type JSX, createContext, useContext } from 'solid-js'
import { Dynamic } from 'solid-js/web'
import { cx } from 'styled-system/css'
import { styled } from 'styled-system/jsx'
import type { ElementType } from 'styled-system/types'
import { isCssProperty, styled } from 'styled-system/jsx'
import type { ElementType, StyledComponent } from 'styled-system/types'

type Props = Record<string, unknown>
type Recipe = {
Expand All @@ -11,6 +11,10 @@ type Recipe = {
}

type Slot<R extends Recipe> = keyof ReturnType<R>
type Options = { forwardProps?: string[] }

const shouldForwardProp = (prop: string, variantKeys: string[], options: Options = {}) =>
options.forwardProps?.includes(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))

export const createStyleContext = <R extends Recipe>(recipe: R) => {
const StyleContext = createContext<Record<Slot<R>, string> | null>(null)
Expand All @@ -32,8 +36,15 @@ export const createStyleContext = <R extends Recipe>(recipe: R) => {
const withProvider = <P extends { class?: string }>(
Component: ElementType,
slot: Slot<R>,
options?: Options,
): ((props: P) => JSX.Element) => {
const StyledComponent = styled(Component)
const StyledComponent = styled(
Component,
{},
{
shouldForwardProp: (prop, variantKeys) => shouldForwardProp(prop, variantKeys, options),
},
) as StyledComponent<ElementType>

return (props: P) => {
const [variantProps, localProps] = recipe.splitVariantProps(props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"variants": [
{
"file": "styled/pagination.tsx",
"content": "'use client'\nimport type { Assign } from '@ark-ui/react'\nimport { Pagination } from '@ark-ui/react/pagination'\nimport { type PaginationVariantProps, pagination } from 'styled-system/recipes'\nimport type { ComponentProps, HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pagination)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n HTMLElement,\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootProviderBaseProps>, PaginationVariantProps>\n>(Pagination.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n HTMLElement,\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>\n>(Pagination.Root, 'root')\n\nexport const Ellipsis = withContext<\n HTMLDivElement,\n Assign<HTMLStyledProps<'div'>, Pagination.EllipsisBaseProps>\n>(Pagination.Ellipsis, 'ellipsis')\n\nexport const Item = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.ItemBaseProps>\n>(Pagination.Item, 'item')\n\nexport const NextTrigger = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.NextTriggerBaseProps>\n>(Pagination.NextTrigger, 'nextTrigger')\n\nexport const PrevTrigger = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.PrevTriggerBaseProps>\n>(Pagination.PrevTrigger, 'prevTrigger')\n\nexport { PaginationContext as Context } from '@ark-ui/react/pagination'\n"
"content": "'use client'\nimport type { Assign } from '@ark-ui/react'\nimport { Pagination } from '@ark-ui/react/pagination'\nimport { type PaginationVariantProps, pagination } from 'styled-system/recipes'\nimport type { ComponentProps, HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pagination)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n HTMLElement,\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootProviderBaseProps>, PaginationVariantProps>\n>(Pagination.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n HTMLElement,\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>\n>(Pagination.Root, 'root', { forwardProps: ['page'] })\n\nexport const Ellipsis = withContext<\n HTMLDivElement,\n Assign<HTMLStyledProps<'div'>, Pagination.EllipsisBaseProps>\n>(Pagination.Ellipsis, 'ellipsis')\n\nexport const Item = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.ItemBaseProps>\n>(Pagination.Item, 'item')\n\nexport const NextTrigger = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.NextTriggerBaseProps>\n>(Pagination.NextTrigger, 'nextTrigger')\n\nexport const PrevTrigger = withContext<\n HTMLButtonElement,\n Assign<HTMLStyledProps<'button'>, Pagination.PrevTriggerBaseProps>\n>(Pagination.PrevTrigger, 'prevTrigger')\n\nexport { PaginationContext as Context } from '@ark-ui/react/pagination'\n"
},
{
"file": "pagination.tsx",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"variants": [
{
"file": "styled/pin-input.tsx",
"content": "'use client'\nimport type { Assign } from '@ark-ui/react'\nimport { PinInput } from '@ark-ui/react/pin-input'\nimport { type PinInputVariantProps, pinInput } from 'styled-system/recipes'\nimport type { ComponentProps, HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pinInput)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n HTMLDivElement,\n Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootProviderBaseProps>, PinInputVariantProps>\n>(PinInput.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n HTMLDivElement,\n Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootBaseProps>, PinInputVariantProps>\n>(PinInput.Root, 'root')\n\nexport const Control = withContext<\n HTMLDivElement,\n Assign<HTMLStyledProps<'div'>, PinInput.ControlBaseProps>\n>(PinInput.Control, 'control')\n\nexport const Input = withContext<\n HTMLInputElement,\n Assign<HTMLStyledProps<'input'>, PinInput.InputBaseProps>\n>(PinInput.Input, 'input')\n\nexport const Label = withContext<\n HTMLLabelElement,\n Assign<HTMLStyledProps<'label'>, PinInput.LabelBaseProps>\n>(PinInput.Label, 'label')\n\nexport {\n PinInputContext as Context,\n PinInputHiddenInput as HiddenInput,\n} from '@ark-ui/react/pin-input'\n"
"content": "'use client'\nimport type { Assign } from '@ark-ui/react'\nimport { PinInput } from '@ark-ui/react/pin-input'\nimport { type PinInputVariantProps, pinInput } from 'styled-system/recipes'\nimport type { ComponentProps, HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pinInput)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n HTMLDivElement,\n Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootProviderBaseProps>, PinInputVariantProps>\n>(PinInput.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n HTMLDivElement,\n Assign<Assign<HTMLStyledProps<'div'>, PinInput.RootBaseProps>, PinInputVariantProps>\n>(PinInput.Root, 'root', { forwardProps: ['mask'] })\n\nexport const Control = withContext<\n HTMLDivElement,\n Assign<HTMLStyledProps<'div'>, PinInput.ControlBaseProps>\n>(PinInput.Control, 'control')\n\nexport const Input = withContext<\n HTMLInputElement,\n Assign<HTMLStyledProps<'input'>, PinInput.InputBaseProps>\n>(PinInput.Input, 'input')\n\nexport const Label = withContext<\n HTMLLabelElement,\n Assign<HTMLStyledProps<'label'>, PinInput.LabelBaseProps>\n>(PinInput.Label, 'label')\n\nexport {\n PinInputContext as Context,\n PinInputHiddenInput as HiddenInput,\n} from '@ark-ui/react/pin-input'\n"
},
{
"file": "pin-input.tsx",
Expand Down
2 changes: 1 addition & 1 deletion website/public/registry/latest/react/utils/index.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"filename": "styled/utils/create-style-context.tsx",
"content": "import {\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type RefAttributes,\n createContext,\n forwardRef,\n useContext,\n} from 'react'\nimport { cx } from 'styled-system/css'\nimport { styled } from 'styled-system/jsx'\n\ntype Props = Record<string, unknown>\ntype Recipe = {\n (props?: Props): Props\n splitVariantProps: (props: Props) => [Props, Props]\n}\ntype Slot<R extends Recipe> = keyof ReturnType<R>\n\nexport const createStyleContext = <R extends Recipe>(recipe: R) => {\n const StyleContext = createContext<Record<Slot<R>, string> | null>(null)\n\n const withRootProvider = <P extends {}>(Component: ElementType) => {\n const StyledComponent = (props: P) => {\n const [variantProps, otherProps] = recipe.splitVariantProps(props)\n const slotStyles = recipe(variantProps) as Record<Slot<R>, string>\n\n return (\n <StyleContext.Provider value={slotStyles}>\n <Component {...otherProps} />\n </StyleContext.Provider>\n )\n }\n return StyledComponent\n }\n\n const withProvider = <T, P extends { className?: string | undefined }>(\n Component: ElementType,\n slot: Slot<R>,\n ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {\n const StyledComponent = styled(Component)\n const StyledSlotProvider = forwardRef<T, P>((props, ref) => {\n const [variantProps, otherProps] = recipe.splitVariantProps(props)\n const slotStyles = recipe(variantProps) as Record<Slot<R>, string>\n\n return (\n <StyleContext.Provider value={slotStyles}>\n <StyledComponent\n {...otherProps}\n ref={ref}\n className={cx(slotStyles?.[slot], props.className)}\n />\n </StyleContext.Provider>\n )\n })\n // @ts-expect-error\n StyledSlotProvider.displayName = Component.displayName || Component.name\n\n return StyledSlotProvider\n }\n\n const withContext = <T, P extends { className?: string | undefined }>(\n Component: ElementType,\n slot: Slot<R>,\n ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {\n const StyledComponent = styled(Component)\n const StyledSlotComponent = forwardRef<T, P>((props, ref) => {\n const slotStyles = useContext(StyleContext)\n return (\n <StyledComponent {...props} ref={ref} className={cx(slotStyles?.[slot], props.className)} />\n )\n })\n // @ts-expect-error\n StyledSlotComponent.displayName = Component.displayName || Component.name\n\n return StyledSlotComponent\n }\n\n return {\n withRootProvider,\n withProvider,\n withContext,\n }\n}\n"
"content": "import {\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type RefAttributes,\n createContext,\n forwardRef,\n useContext,\n} from 'react'\nimport { cx } from 'styled-system/css'\nimport { type StyledComponent, isCssProperty, styled } from 'styled-system/jsx'\n\ntype Props = Record<string, unknown>\ntype Recipe = {\n (props?: Props): Props\n splitVariantProps: (props: Props) => [Props, Props]\n}\ntype Slot<R extends Recipe> = keyof ReturnType<R>\ntype Options = { forwardProps?: string[] }\n\nconst shouldForwardProp = (prop: string, variantKeys: string[], options: Options = {}) =>\n options.forwardProps?.includes(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))\n\nexport const createStyleContext = <R extends Recipe>(recipe: R) => {\n const StyleContext = createContext<Record<Slot<R>, string> | null>(null)\n\n const withRootProvider = <P extends {}>(Component: ElementType) => {\n const StyledComponent = (props: P) => {\n const [variantProps, otherProps] = recipe.splitVariantProps(props)\n const slotStyles = recipe(variantProps) as Record<Slot<R>, string>\n\n return (\n <StyleContext.Provider value={slotStyles}>\n <Component {...otherProps} />\n </StyleContext.Provider>\n )\n }\n return StyledComponent\n }\n\n const withProvider = <T, P extends { className?: string | undefined }>(\n Component: ElementType,\n slot: Slot<R>,\n options?: Options,\n ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {\n const StyledComponent = styled(\n Component,\n {},\n {\n shouldForwardProp: (prop, variantKeys) => shouldForwardProp(prop, variantKeys, options),\n },\n ) as StyledComponent<ElementType>\n const StyledSlotProvider = forwardRef<T, P>((props, ref) => {\n const [variantProps, otherProps] = recipe.splitVariantProps(props)\n const slotStyles = recipe(variantProps) as Record<Slot<R>, string>\n\n return (\n <StyleContext.Provider value={slotStyles}>\n <StyledComponent\n {...otherProps}\n ref={ref}\n className={cx(slotStyles?.[slot], props.className)}\n />\n </StyleContext.Provider>\n )\n })\n // @ts-expect-error\n StyledSlotProvider.displayName = Component.displayName || Component.name\n\n return StyledSlotProvider\n }\n\n const withContext = <T, P extends { className?: string | undefined }>(\n Component: ElementType,\n slot: Slot<R>,\n ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {\n const StyledComponent = styled(Component)\n const StyledSlotComponent = forwardRef<T, P>((props, ref) => {\n const slotStyles = useContext(StyleContext)\n return (\n <StyledComponent {...props} ref={ref} className={cx(slotStyles?.[slot], props.className)} />\n )\n })\n // @ts-expect-error\n StyledSlotComponent.displayName = Component.displayName || Component.name\n\n return StyledSlotComponent\n }\n\n return {\n withRootProvider,\n withProvider,\n withContext,\n }\n}\n"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"variants": [
{
"file": "styled/pagination.tsx",
"content": "import { type Assign, Pagination } from '@ark-ui/solid'\nimport type { ComponentProps } from 'solid-js'\nimport { type PaginationVariantProps, pagination } from 'styled-system/recipes'\nimport type { HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pagination)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootProviderBaseProps>, PaginationVariantProps>\n>(Pagination.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>\n>(Pagination.Root, 'root')\n\nexport const Ellipsis = withContext<Assign<HTMLStyledProps<'div'>, Pagination.EllipsisBaseProps>>(\n Pagination.Ellipsis,\n 'ellipsis',\n)\n\nexport const Item = withContext<Assign<HTMLStyledProps<'button'>, Pagination.ItemBaseProps>>(\n Pagination.Item,\n 'item',\n)\n\nexport const NextTrigger = withContext<\n Assign<HTMLStyledProps<'button'>, Pagination.NextTriggerBaseProps>\n>(Pagination.NextTrigger, 'nextTrigger')\n\nexport const PrevTrigger = withContext<\n Assign<HTMLStyledProps<'button'>, Pagination.PrevTriggerBaseProps>\n>(Pagination.PrevTrigger, 'prevTrigger')\n\nexport { PaginationContext as Context } from '@ark-ui/solid'\n"
"content": "import { type Assign, Pagination } from '@ark-ui/solid'\nimport type { ComponentProps } from 'solid-js'\nimport { type PaginationVariantProps, pagination } from 'styled-system/recipes'\nimport type { HTMLStyledProps } from 'styled-system/types'\nimport { createStyleContext } from './utils/create-style-context'\n\nconst { withProvider, withContext } = createStyleContext(pagination)\n\nexport type RootProviderProps = ComponentProps<typeof RootProvider>\nexport const RootProvider = withProvider<\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootProviderBaseProps>, PaginationVariantProps>\n>(Pagination.RootProvider, 'root')\n\nexport type RootProps = ComponentProps<typeof Root>\nexport const Root = withProvider<\n Assign<Assign<HTMLStyledProps<'nav'>, Pagination.RootBaseProps>, PaginationVariantProps>\n>(Pagination.Root, 'root', { forwardProps: ['page'] })\n\nexport const Ellipsis = withContext<Assign<HTMLStyledProps<'div'>, Pagination.EllipsisBaseProps>>(\n Pagination.Ellipsis,\n 'ellipsis',\n)\n\nexport const Item = withContext<Assign<HTMLStyledProps<'button'>, Pagination.ItemBaseProps>>(\n Pagination.Item,\n 'item',\n)\n\nexport const NextTrigger = withContext<\n Assign<HTMLStyledProps<'button'>, Pagination.NextTriggerBaseProps>\n>(Pagination.NextTrigger, 'nextTrigger')\n\nexport const PrevTrigger = withContext<\n Assign<HTMLStyledProps<'button'>, Pagination.PrevTriggerBaseProps>\n>(Pagination.PrevTrigger, 'prevTrigger')\n\nexport { PaginationContext as Context } from '@ark-ui/solid'\n"
},
{
"file": "pagination.tsx",
Expand Down
Loading

0 comments on commit 8283ab3

Please sign in to comment.