diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 35d51cc..67db93a 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -18,4 +18,5 @@ export * from '~/components/select' export * from '~/components/spinner' export * from '~/components/switch' export * from '~/components/table' +export * from '~/components/tooltip' export * from '~/components/typography' diff --git a/packages/react/src/components/tooltip/Tooltip.tsx b/packages/react/src/components/tooltip/Tooltip.tsx new file mode 100644 index 0000000..ab66346 --- /dev/null +++ b/packages/react/src/components/tooltip/Tooltip.tsx @@ -0,0 +1,50 @@ +'use client' + +import type { TooltipVariantProps } from '@giantnodes/theme' +import type { TooltipTriggerComponentProps } from 'react-aria-components' +import React from 'react' +import { TooltipTrigger } from 'react-aria-components' + +import type * as Polymophic from '~/utilities/polymorphic' +import { TooltipContext, useTooltip } from '~/components/tooltip/use-tooltip.hook' +import { cn } from '~/utilities' + +const __ELEMENT_TYPE__ = 'div' + +type ComponentOwnProps = TooltipVariantProps & TooltipTriggerComponentProps + +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 ?? TooltipTrigger + + const context = useTooltip({}) + + const component = React.useMemo>( + () => ({ + className: context.slots.root({ className: cn(className) }), + ...rest, + }), + [className, context.slots, rest] + ) + + return ( + + {children} + + ) +} + +export type { ComponentOwnProps as TooltipOwnProps, ComponentProps as TooltipProps } +export default Component diff --git a/packages/react/src/components/tooltip/TooltipArrow.tsx b/packages/react/src/components/tooltip/TooltipArrow.tsx new file mode 100644 index 0000000..a84fa62 --- /dev/null +++ b/packages/react/src/components/tooltip/TooltipArrow.tsx @@ -0,0 +1,45 @@ +'use client' + +import type { OverlayArrowProps } from 'react-aria-components' +import React from 'react' +import { OverlayArrow } from 'react-aria-components' + +import type * as Polymophic from '~/utilities/polymorphic' +import { useTooltipContext } from '~/components/tooltip/use-tooltip.hook' + +const __ELEMENT_TYPE__ = 'div' + +type ComponentOwnProps = OverlayArrowProps + +type ComponentProps = Polymophic.ComponentPropsWithRef< + TElement, + ComponentOwnProps +> + +type ComponentType = ( + props: ComponentProps +) => React.ReactNode + +const Component: ComponentType = React.forwardRef( + ( + props: ComponentProps, + ref: Polymophic.Ref + ) => { + const { as, className, ...rest } = props + + const Element = as ?? OverlayArrow + + const { slots } = useTooltipContext() + + return ( + + + + + + ) + } +) + +export type { ComponentOwnProps as TableCellOwnProps, ComponentProps as TableCellProps } +export default Component diff --git a/packages/react/src/components/tooltip/TooltipPopover.tsx b/packages/react/src/components/tooltip/TooltipPopover.tsx new file mode 100644 index 0000000..e899a73 --- /dev/null +++ b/packages/react/src/components/tooltip/TooltipPopover.tsx @@ -0,0 +1,56 @@ +'use client' + +import type { TooltipProps } from 'react-aria-components' +import React from 'react' +import { Tooltip } from 'react-aria-components' + +import type * as Polymophic from '~/utilities/polymorphic' +import TooltipArrow from '~/components/tooltip/TooltipArrow' +import { useTooltipContext } from '~/components/tooltip/use-tooltip.hook' + +const __ELEMENT_TYPE__ = 'div' + +type ComponentOwnProps = TooltipProps + +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 ?? Tooltip + + const { slots } = useTooltipContext() + + const component = React.useMemo( + () => ({ + className: slots.popover({ className: className?.toString() }), + ...rest, + }), + [className, rest, slots] + ) + + return ( + + <> + + + {children} + + + ) + } +) + +export type { ComponentOwnProps as TooltipPopoverOwnProps, ComponentProps as TooltipPopoverProps } +export default Component diff --git a/packages/react/src/components/tooltip/TooltipTrigger.tsx b/packages/react/src/components/tooltip/TooltipTrigger.tsx new file mode 100644 index 0000000..d28d3c2 --- /dev/null +++ b/packages/react/src/components/tooltip/TooltipTrigger.tsx @@ -0,0 +1,51 @@ +'use client' + +import type { ButtonProps } from 'react-aria-components' +import React from 'react' +import { Button } from 'react-aria-components' + +import type * as Polymophic from '~/utilities/polymorphic' +import { useTooltipContext } from '~/components/tooltip/use-tooltip.hook' + +const __ELEMENT_TYPE__ = 'button' + +type ComponentOwnProps = ButtonProps + +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 ?? Button + + const { slots } = useTooltipContext() + + const component = React.useMemo( + () => ({ + className: slots.trigger({ className: className?.toString() }), + ...rest, + }), + [className, rest, slots] + ) + + return ( + + {children} + + ) + } +) + +export type { ComponentOwnProps as TooltipTriggerOwnProps, ComponentProps as TooltipTriggerProps } +export default Component diff --git a/packages/react/src/components/tooltip/component.parts.ts b/packages/react/src/components/tooltip/component.parts.ts new file mode 100644 index 0000000..6b6c7d4 --- /dev/null +++ b/packages/react/src/components/tooltip/component.parts.ts @@ -0,0 +1,3 @@ +export { default as Root } from '~/components/tooltip/Tooltip' +export { default as Popover } from '~/components/tooltip/TooltipPopover' +export { default as Trigger } from '~/components/tooltip/TooltipTrigger' diff --git a/packages/react/src/components/tooltip/index.ts b/packages/react/src/components/tooltip/index.ts new file mode 100644 index 0000000..c4d6036 --- /dev/null +++ b/packages/react/src/components/tooltip/index.ts @@ -0,0 +1,5 @@ +export type * from '~/components/tooltip/Tooltip' +export type * from '~/components/tooltip/TooltipTrigger' +export type * from '~/components/tooltip/TooltipPopover' + +export * as Tooltip from '~/components/tooltip/component.parts' diff --git a/packages/react/src/components/tooltip/use-tooltip.hook.ts b/packages/react/src/components/tooltip/use-tooltip.hook.ts new file mode 100644 index 0000000..693183a --- /dev/null +++ b/packages/react/src/components/tooltip/use-tooltip.hook.ts @@ -0,0 +1,25 @@ +'use client' + +import type { TooltipVariantProps } from '@giantnodes/theme' +import React from 'react' +import { tooltip } from '@giantnodes/theme' + +import { createContext } from '~/utilities/context' + +type UseTooltipProps = TooltipVariantProps + +type UseTooltipReturn = ReturnType + +export const useTooltip = (_: UseTooltipProps) => { + const slots = React.useMemo(() => tooltip(), []) + + return { + slots, + } +} + +export const [TooltipContext, useTooltipContext] = createContext({ + name: 'TooltipContext', + strict: true, + errorMessage: 'useTooltipContext: `context` is undefined. Seems you forgot to wrap component within ', +}) diff --git a/packages/theme/src/components/tooltip.ts b/packages/theme/src/components/tooltip.ts new file mode 100644 index 0000000..145d278 --- /dev/null +++ b/packages/theme/src/components/tooltip.ts @@ -0,0 +1,19 @@ +import type { VariantProps } from 'tailwind-variants' +import { tv } from 'tailwind-variants' + +export const tooltip = tv({ + slots: { + root: [], + trigger: ['flex', 'focus:outline-none'], + popover: ['group', 'bg-foreground', 'border border-partition', 'rounded-md', 'px-3 py-1'], + arrow: [ + 'fill-foreground', + 'stroke-partition', + 'group-placement-bottom:rotate-180 group-placement-left:-rotate-90 group-placement-right:rotate-90', + ], + }, + variants: {}, + defaultVariants: {}, +}) + +export type TooltipVariantProps = VariantProps diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index 05f5b04..d9aba80 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -21,4 +21,5 @@ export * from '~/components/select' export * from '~/components/spinner' export * from '~/components/table' export * from '~/components/toggle' +export * from '~/components/tooltip' export * from '~/components/typography'