Skip to content

Commit

Permalink
feat: add modal component
Browse files Browse the repository at this point in the history
  • Loading branch information
PHILLIPS71 committed Aug 26, 2024
1 parent 5465797 commit c5ec5a3
Show file tree
Hide file tree
Showing 19 changed files with 251 additions and 131 deletions.
8 changes: 4 additions & 4 deletions packages/react/src/components/dialog/Dialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const Component: Meta<typeof Dialog.Root> = {
const defaultProps = {}

export const Default: StoryFn<DialogProps> = (args) => (
<Dialog.Root {...args}>
<Dialog.Trigger>
<Button>Open</Button>

<Dialog.Content>
<Dialog.Root {...args}>
{({ close }) => (
<Card.Root>
<Card.Header>Dialog</Card.Header>
Expand All @@ -26,8 +26,8 @@ export const Default: StoryFn<DialogProps> = (args) => (
</Card.Footer>
</Card.Root>
)}
</Dialog.Content>
</Dialog.Root>
</Dialog.Root>
</Dialog.Trigger>
)

Default.args = {
Expand Down
58 changes: 31 additions & 27 deletions packages/react/src/components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
'use client'

import type { DialogVariantProps } from '@giantnodes/theme'
import type { DialogTriggerProps } from 'react-aria-components'
import type { DialogProps } from 'react-aria-components'
import React from 'react'
import { DialogTrigger } from 'react-aria-components'
import { Dialog } from 'react-aria-components'

import type * as Polymophic from '~/utilities/polymorphic'
import { DialogContext, useDialog } from '~/components/dialog/use-dialog.hook'

const __ELEMENT_TYPE__ = 'div'

type ComponentOwnProps = DialogTriggerProps & DialogVariantProps
type ComponentOwnProps = DialogProps

type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
Expand All @@ -21,29 +20,34 @@ type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE
props: ComponentProps<TElement>
) => React.ReactNode

const Component: ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => {
const { as, children, className, size, blur, placement, ...rest } = props

const Element = as ?? DialogTrigger

const context = useDialog({ size, blur, placement })

const component = React.useMemo<Omit<DialogTriggerProps, 'children'>>(
() => ({
className: context.slots.dialog({ className }),
...rest,
}),
[context.slots, className, rest]
)

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

const Element = as ?? Dialog

const context = useDialog({})

const component = React.useMemo<DialogProps>(
() => ({
className: context.slots.root({ className }),
...rest,
}),
[context.slots, className, rest]
)

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

export type { ComponentOwnProps as DialogOwnProps, ComponentProps as DialogProps }
export default Component
55 changes: 0 additions & 55 deletions packages/react/src/components/dialog/DialogContent.tsx

This file was deleted.

34 changes: 34 additions & 0 deletions packages/react/src/components/dialog/DialogTrigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client'

import type { DialogVariantProps } from '@giantnodes/theme'
import type { DialogTriggerProps } from 'react-aria-components'
import React from 'react'
import { DialogTrigger } from 'react-aria-components'

import type * as Polymophic from '~/utilities/polymorphic'

const __ELEMENT_TYPE__ = 'div'

type ComponentOwnProps = DialogTriggerProps & DialogVariantProps

type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
ComponentOwnProps
>

type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => React.ReactNode

const Component: ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => {
const { as, children, ...component } = props

const Element = as ?? DialogTrigger

return <Element {...component}>{children}</Element>
}

export type { ComponentOwnProps as DialogTriggerOwnProps, ComponentProps as DialogTriggerProps }
export default Component
2 changes: 1 addition & 1 deletion packages/react/src/components/dialog/component.parts.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as Root } from '~/components/dialog/Dialog'
export { default as Content } from '~/components/dialog/DialogContent'
export { default as Trigger } from '~/components/dialog/DialogTrigger'
2 changes: 1 addition & 1 deletion packages/react/src/components/dialog/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type * from '~/components/dialog/DialogTrigger'
export type * from '~/components/dialog/Dialog'
export type * from '~/components/dialog/DialogContent'

export * as Dialog from '~/components/dialog/component.parts'
8 changes: 3 additions & 5 deletions packages/react/src/components/dialog/use-dialog.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ export type UseDialogProps = DialogVariantProps

export type UseDialogReturn = ReturnType<typeof useDialog>

export const useDialog = (props: UseDialogProps) => {
const { size, blur, placement } = props

const slots = React.useMemo(() => dialog({ size, blur, placement }), [size, blur, placement])
export const useDialog = (_: UseDialogProps) => {
const slots = React.useMemo(() => dialog(), [])

return {
slots,
Expand All @@ -23,5 +21,5 @@ export const useDialog = (props: UseDialogProps) => {
export const [DialogContext, useDialogContext] = createContext<UseDialogReturn>({
name: 'DialogContext',
strict: true,
errorMessage: 'useDialogContext: `context` is undefined. Seems you forgot to wrap component within <Dialog />',
errorMessage: 'useDialogContext: `context` is undefined. Seems you forgot to wrap component within <Dialog.Root />',
})
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from '~/components/form'
export * from '~/components/input'
export * from '~/components/link'
export * from '~/components/menu'
export * from '~/components/modal'
export * from '~/components/navigation'
export * from '~/components/progress'
export * from '~/components/select'
Expand Down
50 changes: 50 additions & 0 deletions packages/react/src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client'

import type { ModalVariantProps } from '@giantnodes/theme'
import type { ModalOverlayProps } from 'react-aria-components'
import React from 'react'
import { ModalOverlay } from 'react-aria-components'

import type * as Polymophic from '~/utilities/polymorphic'
import { ModalContext, useModal } from '~/components/modal/use-modal.hook'
import { cn } from '~/utilities'

const __ELEMENT_TYPE__ = 'div'

type ComponentOwnProps = ModalOverlayProps & ModalVariantProps

type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
ComponentOwnProps
>

type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => React.ReactNode

const Component: ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => {
const { as, children, className, blur, placement, position, ...rest } = props

const Element = as ?? ModalOverlay

const context = useModal({ blur, placement, position })

const component = React.useMemo<ModalOverlayProps>(
() => ({
className: context.slots.root({ className: cn(className) }),
...rest,
}),
[context.slots, className, rest]
)

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

export type { ComponentOwnProps as ModalOwnProps, ComponentProps as ModalProps }
export default Component
46 changes: 46 additions & 0 deletions packages/react/src/components/modal/ModalContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client'

import type { ModalVariantProps } from '@giantnodes/theme'
import type { ModalOverlayProps } from 'react-aria-components'
import React from 'react'
import { Modal } from 'react-aria-components'

import type * as Polymophic from '~/utilities/polymorphic'
import { useModalContext } from '~/components/modal/use-modal.hook'
import { cn } from '~/utilities'

const __ELEMENT_TYPE__ = 'div'

type ComponentOwnProps = ModalOverlayProps & ModalVariantProps

type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
ComponentOwnProps
>

type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => React.ReactNode

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

const Element = as ?? Modal

const { slots } = useModalContext()

const component = React.useMemo<ModalOverlayProps>(
() => ({
className: slots.content({ className: cn(className) }),
...rest,
}),
[slots, className, rest]
)

return <Element {...component}>{children}</Element>
}

export type { ComponentOwnProps as ModalContentOwnProps, ComponentProps as ModalContentProps }
export default Component
2 changes: 2 additions & 0 deletions packages/react/src/components/modal/component.parts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Root } from '~/components/modal/Modal'
export { default as Content } from '~/components/modal/ModalContent'
4 changes: 4 additions & 0 deletions packages/react/src/components/modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type * from '~/components/modal/Modal'
export type * from '~/components/modal/ModalContent'

export * as Modal from '~/components/modal/component.parts'
27 changes: 27 additions & 0 deletions packages/react/src/components/modal/use-modal.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client'

import type { ModalVariantProps } from '@giantnodes/theme'
import React from 'react'
import { modal } from '@giantnodes/theme'

import { createContext } from '~/utilities/context'

export type UseModalProps = ModalVariantProps

export type UseModalReturn = ReturnType<typeof useModal>

export const useModal = (props: UseModalProps) => {
const { blur, placement, position } = props

const slots = React.useMemo(() => modal({ blur, placement, position }), [blur, placement, position])

return {
slots,
}
}

export const [ModalContext, useModalContext] = createContext<UseModalReturn>({
name: 'ModalContext',
strict: true,
errorMessage: 'useModalContext: `context` is undefined. Seems you forgot to wrap component within <Modal.Root />',
})
1 change: 0 additions & 1 deletion packages/react/src/components/select/SelectValue.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
'use client'

import type { SelectValueProps } from 'react-aria-components'
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/select/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type * from '~/components/select/Select'
export type * from '~/components/select/SelectOption'
export type * from '~/components/select/SelectValue'

export * as Select from '~/components/select/component.parts'
Loading

0 comments on commit c5ec5a3

Please sign in to comment.