Skip to content

Commit

Permalink
feat: add polymorphic component support (#21)
Browse files Browse the repository at this point in the history
* feat: add polymorphic types

* feat(button): add polymorphic component support

* feat(alert): add polymorphic component support

* feat(link): add polymorphic component support

* feat(alert): use Heading and Text base components

* feat(avatar): add polymorphic component support

* feat(table): add polymorphic component support

* feat(typography): add polymorphic component support

* feat(navigation): add polymorphic component support

* feat(switch): add polymorphic component support

* feat(select): add polymorphic component support

* feat(progress): add polymorphic component support

* feat(menu): add polymorphic component support

* feat(input): add polymorphic component support

* feat(form): add polymorphic component support

* feat(divider): add polymorphic component support

* feat(dialog): add polymorphic component support

* feat(chip): add polymorphic component support

* feat(checkbox): add polymorphic component support

* feat(card): add polymorphic component support

* feat(breadcrumb): add polymorphic component support

* fix(button): base button props missing
  • Loading branch information
PHILLIPS71 authored Apr 20, 2024
1 parent a5e3a10 commit 74bf50b
Show file tree
Hide file tree
Showing 85 changed files with 2,255 additions and 1,580 deletions.
70 changes: 53 additions & 17 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,61 @@
"env": {
"browser": false,
"es2021": true,
"node": true
"node": true,
},
"parser": "@typescript-eslint/parser",
"extends": ["airbnb", "airbnb-typescript", "airbnb/hooks", "prettier", "plugin:storybook/recommended"],
"parserOptions": {
"project": "tsconfig.json",
"ecmaFeatures": {
"jsx": true
"jsx": true,
},
"ecmaVersion": 12,
"sourceType": "module"
"sourceType": "module",
},
"settings": {
"react": {
"version": "detect"
}
"version": "detect",
},
},
"rules": {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "default",
"format": ["camelCase", "PascalCase", "snake_case", "UPPER_CASE"],
},
{
"selector": "import",
"format": ["camelCase", "PascalCase"],
},
{
"selector": "variable",
"format": ["camelCase", "PascalCase", "snake_case", "UPPER_CASE"],
"leadingUnderscore": "allowSingleOrDouble",
"trailingUnderscore": "allowSingleOrDouble",
},
{
"selector": "function",
"format": ["camelCase", "PascalCase"],
},
{
"selector": "typeLike",
"format": ["PascalCase"],
},
{
"selector": "enumMember",
"format": ["UPPER_CASE"],
},
{
"selector": "property",
"format": null,
},
],
"no-console": "warn",
"no-param-reassign": ["error", { "props": true, "ignorePropertyModificationsFor": ["accu"] }],
"no-underscore-dangle": ["error", { "allow": ["__ELEMENT_TYPE__"] }],
"react/prop-types": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
Expand All @@ -35,9 +70,10 @@
"callbacksLast": true,
"shorthandFirst": true,
"noSortAlphabetically": false,
"reservedFirst": true
}
"reservedFirst": true,
},
],
"import/prefer-default-export": "off",
"import/extensions": "off",
"import/order": [
"error",
Expand All @@ -47,19 +83,19 @@
{
"pattern": "~/**",
"group": "external",
"position": "after"
}
"position": "after",
},
],
"newlines-between": "always",
"alphabetize": { "order": "asc", "caseInsensitive": true }
}
"alphabetize": { "order": "asc", "caseInsensitive": true },
},
],
"sort-imports": [
"error",
{
"ignoreDeclarationSort": true,
"ignoreMemberSort": false
}
"ignoreMemberSort": false,
},
],
"padding-line-between-statements": [
"warn",
Expand All @@ -68,8 +104,8 @@
{
"blankLine": "any",
"prev": ["const", "let", "var"],
"next": ["const", "let", "var"]
}
]
}
"next": ["const", "let", "var"],
},
],
},
}
60 changes: 34 additions & 26 deletions packages/react/src/components/alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { UseAlertProps } from '@/components/alert/use-alert.hook'
import type { Component } from '@/utilities/types'
import type * as Polymophic from '@/utilities/polymorphic'
import type { AlertVariantProps } from '@giantnodes/theme'

import React from 'react'

Expand All @@ -10,36 +10,44 @@ import AlertList from '@/components/alert/AlertList'
import AlertText from '@/components/alert/AlertText'
import { AlertContext, useAlert } from '@/components/alert/use-alert.hook'

export type AlertProps = Component<'div'> & UseAlertProps
const __ELEMENT_TYPE__ = 'div'

const Alert = React.forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
const { as, children, className, color, ...rest } = props
type ComponentOwnProps = AlertVariantProps

const Component = as || 'div'
type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>

const context = useAlert({ color })
type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
) => React.ReactNode

const getProps = React.useCallback(
() => ({
ref,
className: context.slots.base({
class: className,
}),
...rest,
}),
[ref, context.slots, className, rest]
)

return (
<AlertContext.Provider value={context}>
<Component {...getProps()}>{children}</Component>
</AlertContext.Provider>
)
})
const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
const { as, children, className, color, ...rest } = props

Alert.displayName = 'Alert'
const Element = as ?? __ELEMENT_TYPE__

export default Object.assign(Alert, {
const context = useAlert({ color })

const component = React.useMemo<React.ComponentPropsWithoutRef<typeof __ELEMENT_TYPE__>>(
() => ({
className: context.slots.base({ className }),
...rest,
}),
[context.slots, className, rest]
)

return (
<AlertContext.Provider value={context}>
<Element {...component} ref={ref}>
{children}
</Element>
</AlertContext.Provider>
)
}
)

export type { ComponentOwnProps as AlertProps }
export default Object.assign(Component, {
Body: AlertBody,
Heading: AlertHeading,
Item: AlertItem,
Expand Down
51 changes: 31 additions & 20 deletions packages/react/src/components/alert/AlertBody.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import type { Component } from '@/utilities/types'
import type * as Polymophic from '@/utilities/polymorphic'

import React from 'react'

import { useAlertContext } from '@/components/alert/use-alert.hook'

export type AlertBodyProps = Component<'div'>
const __ELEMENT_TYPE__ = 'div'

const AlertBody = React.forwardRef<HTMLDivElement, AlertBodyProps>((props, ref) => {
const { as, children, className, ...rest } = props
const { slots } = useAlertContext()
type ComponentOwnProps = {}

const Component = as || 'div'
type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>

const getProps = React.useCallback(
() => ({
ref,
className: slots.body({
class: className,
}),
...rest,
}),
[ref, slots, className, rest]
)
type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
) => React.ReactNode

const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
const { as, children, className, ...rest } = props

return <Component {...getProps()}>{children}</Component>
})
const Element = as ?? __ELEMENT_TYPE__

AlertBody.displayName = 'Alert.Body'
const { slots } = useAlertContext()

export default AlertBody
const component = React.useMemo<React.ComponentPropsWithoutRef<typeof __ELEMENT_TYPE__>>(
() => ({
className: slots.body({ className }),
...rest,
}),
[className, rest, slots]
)

return (
<Element {...component} ref={ref}>
{children}
</Element>
)
}
)

export type { ComponentOwnProps as AlertBodyProps }
export default Component
54 changes: 34 additions & 20 deletions packages/react/src/components/alert/AlertHeading.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import type { Component } from '@/utilities/types'
import type * as Polymophic from '@/utilities/polymorphic'
import type { HeadingProps } from 'react-aria-components'

import React from 'react'
import { Heading } from 'react-aria-components'

import { useAlertContext } from '@/components/alert/use-alert.hook'

export type AlertHeadingProps = Component<'h3'>
const __ELEMENT_TYPE__ = 'h1'

const AlertHeading = React.forwardRef<HTMLHeadingElement, AlertHeadingProps>((props, ref) => {
const { as, children, className, ...rest } = props
const { slots } = useAlertContext()
type ComponentOwnProps = HeadingProps

const Component = as || 'h3'
type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>

const getProps = React.useCallback(
() => ({
ref,
className: slots.heading({
class: className,
}),
...rest,
}),
[ref, slots, className, rest]
)
type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
) => React.ReactNode

const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
const { as, children, className, level = 3, ...rest } = props

return <Component {...getProps()}>{children}</Component>
})
const Element = as ?? Heading

AlertHeading.displayName = 'Alert.Heading'
const { slots } = useAlertContext()

export default AlertHeading
const component = React.useMemo<React.ComponentPropsWithoutRef<typeof __ELEMENT_TYPE__>>(
() => ({
level,
className: slots.heading({ className }),
...rest,
}),
[className, level, rest, slots]
)

return (
<Element {...component} ref={ref}>
{children}
</Element>
)
}
)

export type { ComponentOwnProps as AlertHeadingProps }
export default Component
55 changes: 31 additions & 24 deletions packages/react/src/components/alert/AlertItem.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
import type { Component } from '@/utilities/types'
import type * as Polymophic from '@/utilities/polymorphic'

import React from 'react'

import { useAlertContext } from '@/components/alert/use-alert.hook'

export type AlertItemProps = Component<'li'>
const __ELEMENT_TYPE__ = 'li'

const AlertItem = React.forwardRef<HTMLLIElement, AlertItemProps>((props, ref) => {
const { as, children, className, ...rest } = props
const { slots } = useAlertContext()
type ComponentOwnProps = {}

const Component = as || 'li'
type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>

const getProps = React.useCallback(
() => ({
ref,
className: slots.item({
class: className,
}),
...rest,
}),
[ref, slots, className, rest]
)
type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
) => React.ReactNode

const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
const { as, children, className, ...rest } = props

return (
<Component role="listitem" {...getProps()}>
{children}
</Component>
)
})
const Element = as ?? __ELEMENT_TYPE__

AlertItem.displayName = 'Alert.Item'
const { slots } = useAlertContext()

export default AlertItem
const component = React.useMemo<React.ComponentPropsWithoutRef<typeof __ELEMENT_TYPE__>>(
() => ({
className: slots.item({ className }),
...rest,
}),
[className, rest, slots]
)

return (
<Element {...component} ref={ref}>
{children}
</Element>
)
}
)

export type { ComponentOwnProps as AlertItemProps }
export default Component
Loading

0 comments on commit 74bf50b

Please sign in to comment.