diff --git a/packages/react/src/components/dialog/Dialog.stories.tsx b/packages/react/src/components/dialog/Dialog.stories.tsx index 6aac43d..0590dc4 100644 --- a/packages/react/src/components/dialog/Dialog.stories.tsx +++ b/packages/react/src/components/dialog/Dialog.stories.tsx @@ -11,10 +11,10 @@ const Component: Meta = { const defaultProps = {} export const Default: StoryFn = (args) => ( - + - + {({ close }) => ( Dialog @@ -26,8 +26,8 @@ export const Default: StoryFn = (args) => ( )} - - + + ) Default.args = { diff --git a/packages/react/src/components/dialog/Dialog.tsx b/packages/react/src/components/dialog/Dialog.tsx index 3e9cc26..64beca8 100644 --- a/packages/react/src/components/dialog/Dialog.tsx +++ b/packages/react/src/components/dialog/Dialog.tsx @@ -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 = Polymophic.ComponentPropsWithRef< TElement, @@ -21,29 +20,34 @@ type ComponentType = ) => React.ReactNode -const Component: ComponentType = ( - props: ComponentProps -) => { - const { as, children, className, size, blur, placement, ...rest } = props - - const Element = as ?? DialogTrigger - - const context = useDialog({ size, blur, placement }) - - const component = React.useMemo>( - () => ({ - className: context.slots.dialog({ className }), - ...rest, - }), - [context.slots, className, rest] - ) - - return ( - - {children} - - ) -} +const Component: ComponentType = React.forwardRef( + ( + props: ComponentProps, + ref: Polymophic.Ref + ) => { + const { as, children, className, ...rest } = props + + const Element = as ?? Dialog + + const context = useDialog({}) + + const component = React.useMemo( + () => ({ + className: context.slots.root({ className }), + ...rest, + }), + [context.slots, className, rest] + ) + + return ( + + + {children} + + + ) + } +) export type { ComponentOwnProps as DialogOwnProps, ComponentProps as DialogProps } export default Component diff --git a/packages/react/src/components/dialog/DialogContent.tsx b/packages/react/src/components/dialog/DialogContent.tsx deleted file mode 100644 index cec51e2..0000000 --- a/packages/react/src/components/dialog/DialogContent.tsx +++ /dev/null @@ -1,55 +0,0 @@ -'use client' - -import type { DialogProps } from 'react-aria-components' -import React from 'react' -import { Dialog, Modal, ModalOverlay } from 'react-aria-components' - -import type * as Polymophic from '~/utilities/polymorphic' -import { useDialogContext } from '~/components/dialog/use-dialog.hook' - -const __ELEMENT_TYPE__ = 'div' - -type ComponentOwnProps = DialogProps - -type ComponentProps = Polymophic.ComponentPropsWithRef< - TElement, - ComponentOwnProps -> - -type ComponentType = ( - props: ComponentProps -) => React.ReactNode - -const Component: ComponentType = React.forwardRef( - ( - props: ComponentProps, - ref: Polymophic.Ref - ) => { - const { as, children, className, ...rest } = props - - const Element = as ?? Dialog - - const { slots } = useDialogContext() - - const component = React.useMemo( - () => ({ - className: slots.content({ className }), - ...rest, - }), - [className, rest, slots] - ) - - return ( - - - - {children} - - - - ) - } -) - -export type { ComponentOwnProps as DialogContentOwnProps, ComponentProps as DialogContentProps } -export default Component diff --git a/packages/react/src/components/dialog/DialogTrigger.tsx b/packages/react/src/components/dialog/DialogTrigger.tsx new file mode 100644 index 0000000..40c83c8 --- /dev/null +++ b/packages/react/src/components/dialog/DialogTrigger.tsx @@ -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 = Polymophic.ComponentPropsWithRef< + TElement, + ComponentOwnProps +> + +type ComponentType = ( + props: ComponentProps +) => React.ReactNode + +const Component: ComponentType = ( + props: ComponentProps +) => { + const { as, children, ...component } = props + + const Element = as ?? DialogTrigger + + return {children} +} + +export type { ComponentOwnProps as DialogTriggerOwnProps, ComponentProps as DialogTriggerProps } +export default Component diff --git a/packages/react/src/components/dialog/component.parts.ts b/packages/react/src/components/dialog/component.parts.ts index 3a61d4d..d941e36 100644 --- a/packages/react/src/components/dialog/component.parts.ts +++ b/packages/react/src/components/dialog/component.parts.ts @@ -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' diff --git a/packages/react/src/components/dialog/index.ts b/packages/react/src/components/dialog/index.ts index 879826a..548ec0a 100644 --- a/packages/react/src/components/dialog/index.ts +++ b/packages/react/src/components/dialog/index.ts @@ -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' diff --git a/packages/react/src/components/dialog/use-dialog.hook.ts b/packages/react/src/components/dialog/use-dialog.hook.ts index ee855d6..c71259b 100644 --- a/packages/react/src/components/dialog/use-dialog.hook.ts +++ b/packages/react/src/components/dialog/use-dialog.hook.ts @@ -10,10 +10,8 @@ export type UseDialogProps = DialogVariantProps export type UseDialogReturn = ReturnType -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, @@ -23,5 +21,5 @@ export const useDialog = (props: UseDialogProps) => { export const [DialogContext, useDialogContext] = createContext({ name: 'DialogContext', strict: true, - errorMessage: 'useDialogContext: `context` is undefined. Seems you forgot to wrap component within ', + errorMessage: 'useDialogContext: `context` is undefined. Seems you forgot to wrap component within ', }) diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 67db93a..6556612 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -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' diff --git a/packages/react/src/components/modal/Modal.tsx b/packages/react/src/components/modal/Modal.tsx new file mode 100644 index 0000000..8767cc9 --- /dev/null +++ b/packages/react/src/components/modal/Modal.tsx @@ -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 = Polymophic.ComponentPropsWithRef< + TElement, + ComponentOwnProps +> + +type ComponentType = ( + props: ComponentProps +) => React.ReactNode + +const Component: ComponentType = ( + props: ComponentProps +) => { + const { as, children, className, blur, placement, position, ...rest } = props + + const Element = as ?? ModalOverlay + + const context = useModal({ blur, placement, position }) + + const component = React.useMemo( + () => ({ + className: context.slots.root({ className: cn(className) }), + ...rest, + }), + [context.slots, className, rest] + ) + + return ( + + {children} + + ) +} + +export type { ComponentOwnProps as ModalOwnProps, ComponentProps as ModalProps } +export default Component diff --git a/packages/react/src/components/modal/ModalContent.tsx b/packages/react/src/components/modal/ModalContent.tsx new file mode 100644 index 0000000..a5ec8d8 --- /dev/null +++ b/packages/react/src/components/modal/ModalContent.tsx @@ -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 = Polymophic.ComponentPropsWithRef< + TElement, + ComponentOwnProps +> + +type ComponentType = ( + props: ComponentProps +) => React.ReactNode + +const Component: ComponentType = ( + props: ComponentProps +) => { + const { as, children, className, ...rest } = props + + const Element = as ?? Modal + + const { slots } = useModalContext() + + const component = React.useMemo( + () => ({ + className: slots.content({ className: cn(className) }), + ...rest, + }), + [slots, className, rest] + ) + + return {children} +} + +export type { ComponentOwnProps as ModalContentOwnProps, ComponentProps as ModalContentProps } +export default Component diff --git a/packages/react/src/components/modal/component.parts.ts b/packages/react/src/components/modal/component.parts.ts new file mode 100644 index 0000000..a0eb1af --- /dev/null +++ b/packages/react/src/components/modal/component.parts.ts @@ -0,0 +1,2 @@ +export { default as Root } from '~/components/modal/Modal' +export { default as Content } from '~/components/modal/ModalContent' diff --git a/packages/react/src/components/modal/index.ts b/packages/react/src/components/modal/index.ts new file mode 100644 index 0000000..57b93ec --- /dev/null +++ b/packages/react/src/components/modal/index.ts @@ -0,0 +1,4 @@ +export type * from '~/components/modal/Modal' +export type * from '~/components/modal/ModalContent' + +export * as Modal from '~/components/modal/component.parts' diff --git a/packages/react/src/components/modal/use-modal.hook.ts b/packages/react/src/components/modal/use-modal.hook.ts new file mode 100644 index 0000000..68e5ab4 --- /dev/null +++ b/packages/react/src/components/modal/use-modal.hook.ts @@ -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 + +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({ + name: 'ModalContext', + strict: true, + errorMessage: 'useModalContext: `context` is undefined. Seems you forgot to wrap component within ', +}) diff --git a/packages/react/src/components/select/SelectValue.tsx b/packages/react/src/components/select/SelectValue.tsx index 9e2b6f1..9da62cd 100644 --- a/packages/react/src/components/select/SelectValue.tsx +++ b/packages/react/src/components/select/SelectValue.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ 'use client' import type { SelectValueProps } from 'react-aria-components' diff --git a/packages/react/src/components/select/index.ts b/packages/react/src/components/select/index.ts index c9b8d3a..eb747e4 100644 --- a/packages/react/src/components/select/index.ts +++ b/packages/react/src/components/select/index.ts @@ -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' diff --git a/packages/theme/src/components/dialog.ts b/packages/theme/src/components/dialog.ts index 27aec88..8d28214 100644 --- a/packages/theme/src/components/dialog.ts +++ b/packages/theme/src/components/dialog.ts @@ -3,43 +3,10 @@ import { tv } from 'tailwind-variants' export const dialog = tv({ slots: { - dialog: [], - overlay: ['flex', 'fixed inset-0 z-50', 'bg-black/50', 'min-h-full', 'overflow-y-auto', 'p-3'], - modal: ['w-full', 'overflow-hidden'], - content: ['relative', 'outline-none'], - }, - variants: { - size: { - sm: { - modal: ['max-w-2xl'], - }, - md: { - modal: ['max-w-4xl'], - }, - lg: { - modal: ['max-w-6xl'], - }, - }, - blur: { - true: { - overlay: ['backdrop-blur-sm'], - }, - }, - placement: { - center: { - overlay: ['justify-center items-center'], - }, - right: { - overlay: ['justify-end'], - content: ['h-full', '*:h-full'], - }, - }, - }, - defaultVariants: { - size: 'md', - blur: false, - placement: 'center', + root: ['relative', 'outline-none'], }, + variants: {}, + defaultVariants: {}, }) export type DialogVariantProps = VariantProps diff --git a/packages/theme/src/components/modal.ts b/packages/theme/src/components/modal.ts new file mode 100644 index 0000000..49d266d --- /dev/null +++ b/packages/theme/src/components/modal.ts @@ -0,0 +1,41 @@ +import type { VariantProps } from 'tailwind-variants' +import { tv } from 'tailwind-variants' + +export const modal = tv({ + slots: { + root: ['flex', 'fixed inset-0 z-50', 'bg-black/50', 'min-h-full', 'overflow-y-auto'], + content: ['relative', 'outline-none'], + }, + variants: { + blur: { + true: { + root: ['backdrop-blur-sm'], + }, + }, + placement: { + center: { + root: ['justify-center items-center'], + }, + left: { + root: ['justify-start'], + }, + right: { + root: ['justify-end'], + content: ['h-full', '*:h-full'], + }, + }, + position: { + none: {}, + floating: { + root: ['p-3'], + }, + }, + }, + defaultVariants: { + blur: false, + placement: 'center', + position: 'floating', + }, +}) + +export type ModalVariantProps = VariantProps diff --git a/packages/theme/src/components/navigation.ts b/packages/theme/src/components/navigation.ts index e09257b..867ae23 100644 --- a/packages/theme/src/components/navigation.ts +++ b/packages/theme/src/components/navigation.ts @@ -4,7 +4,7 @@ import { tv } from 'tailwind-variants' export const navigation = tv({ slots: { navigation: ['relative', 'flex', 'text-sm text-content', 'z-10'], - wrapper: ['relative', 'flex gap-3 md:gap-4', 'bg-middleground', 'z-10'], + wrapper: ['relative', 'flex gap-3', 'bg-middleground', 'z-10'], brand: ['flex flex-row justify-center flex-nowrap shrink-0'], segment: ['flex gap-2', 'list-none'], title: ['font-semibold'], diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index d9aba80..dedf6ef 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -15,6 +15,7 @@ export * from '~/components/heading' 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'