Skip to content

Commit

Permalink
feat: add tooltip component
Browse files Browse the repository at this point in the history
  • Loading branch information
PHILLIPS71 committed Jul 23, 2024
1 parent 7a987a1 commit 8889824
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
50 changes: 50 additions & 0 deletions packages/react/src/components/tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -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<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 ?? TooltipTrigger

const context = useTooltip({})

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

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

export type { ComponentOwnProps as TooltipOwnProps, ComponentProps as TooltipProps }
export default Component
45 changes: 45 additions & 0 deletions packages/react/src/components/tooltip/TooltipArrow.tsx
Original file line number Diff line number Diff line change
@@ -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<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 = React.forwardRef(
<TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>,
ref: Polymophic.Ref<TElement>
) => {
const { as, className, ...rest } = props

const Element = as ?? OverlayArrow

const { slots } = useTooltipContext()

return (
<Element {...rest} ref={ref}>
<svg className={slots.arrow({ className: className?.toString() })} height={8} viewBox="0 0 8 8" width={8}>
<path d="M0 0 L4 4 L8 0" />
</svg>
</Element>
)
}
)

export type { ComponentOwnProps as TableCellOwnProps, ComponentProps as TableCellProps }
export default Component
56 changes: 56 additions & 0 deletions packages/react/src/components/tooltip/TooltipPopover.tsx
Original file line number Diff line number Diff line change
@@ -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<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 = 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 ?? Tooltip

const { slots } = useTooltipContext()

const component = React.useMemo<TooltipProps>(
() => ({
className: slots.popover({ className: className?.toString() }),
...rest,
}),
[className, rest, slots]
)

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

{children}
</>
</Element>
)
}
)

export type { ComponentOwnProps as TooltipPopoverOwnProps, ComponentProps as TooltipPopoverProps }
export default Component
51 changes: 51 additions & 0 deletions packages/react/src/components/tooltip/TooltipTrigger.tsx
Original file line number Diff line number Diff line change
@@ -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<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 = 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 ?? Button

const { slots } = useTooltipContext()

const component = React.useMemo<ButtonProps>(
() => ({
className: slots.trigger({ className: className?.toString() }),
...rest,
}),
[className, rest, slots]
)

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

export type { ComponentOwnProps as TooltipTriggerOwnProps, ComponentProps as TooltipTriggerProps }
export default Component
3 changes: 3 additions & 0 deletions packages/react/src/components/tooltip/component.parts.ts
Original file line number Diff line number Diff line change
@@ -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'
5 changes: 5 additions & 0 deletions packages/react/src/components/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -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'
25 changes: 25 additions & 0 deletions packages/react/src/components/tooltip/use-tooltip.hook.ts
Original file line number Diff line number Diff line change
@@ -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<typeof useTooltip>

export const useTooltip = (_: UseTooltipProps) => {
const slots = React.useMemo(() => tooltip(), [])

return {
slots,
}
}

export const [TooltipContext, useTooltipContext] = createContext<UseTooltipReturn>({
name: 'TooltipContext',
strict: true,
errorMessage: 'useTooltipContext: `context` is undefined. Seems you forgot to wrap component within <Tooltip.Root />',
})
19 changes: 19 additions & 0 deletions packages/theme/src/components/tooltip.ts
Original file line number Diff line number Diff line change
@@ -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<typeof tooltip>
1 change: 1 addition & 0 deletions packages/theme/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

0 comments on commit 8889824

Please sign in to comment.