diff --git a/src/components/index.ts b/src/components/index.ts index 68892f13..3e5d9489 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -10,12 +10,14 @@ export { IconButton } from "./icon-button"; export { InlineAlert } from "./inline-alert"; export { LastChangedInfo } from "./last-changed-info"; export { Menu } from "./menu"; +export { Navigation } from "./navigation"; export { Page } from "./page"; export { Panel } from "./panel"; export { Section } from "./section"; export { Sidebar } from "./sidebar"; export { SidebarContainer } from "./sidebar-container"; export { Sidesheet } from "./sidesheet"; +export { Skeleton } from "./skeleton"; export { Spinner } from "./spinner"; export { SpinnerOverlay } from "./spinner-overlay"; export { Tab } from "./tab"; @@ -24,6 +26,5 @@ export { TableVirtualized, TableVirtualizedProps } from "./table-virtualized"; export { Tag } from "./tag"; export { Toast } from "./toast"; export { Toggle } from "./toggle"; +export { Tooltip } from "./tooltip"; export { TopBar } from "./top-bar"; -export { Skeleton } from "./skeleton"; -export { Navigation } from "./navigation"; diff --git a/src/components/tooltip/index.ts b/src/components/tooltip/index.ts new file mode 100644 index 00000000..8402f319 --- /dev/null +++ b/src/components/tooltip/index.ts @@ -0,0 +1 @@ +export { Tooltip } from "./tooltip"; diff --git a/src/components/tooltip/tooltip.stories.tsx b/src/components/tooltip/tooltip.stories.tsx new file mode 100644 index 00000000..bcf2d8ff --- /dev/null +++ b/src/components/tooltip/tooltip.stories.tsx @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { getStoryDescription } from "../../util/storybook-utils"; +import { Tooltip } from "./tooltip"; +import { Button } from "../button"; + +const meta: Meta = { + title: "Tooltip", + component: Tooltip, + parameters: { + ...getStoryDescription( + "Tooltip component. By default displays a tooltip on hover, but it can be controlled with the `open` prop as well." + ), + }, + args: { + position: "right", + title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + className: "", + }, +}; + +export default meta; + +type Story = StoryObj; + +export const UncontrolledTooltip: Story = { + render: (args) => ( + +
+ Hover to toggle +
+
+ ), +}; + +const ControlledTooltipExample = () => { + const [open, setOpen] = React.useState(false); + + return ( + + + + ); +}; + +export const ControlledTooltip: Story = { + render: () => , +}; diff --git a/src/components/tooltip/tooltip.tsx b/src/components/tooltip/tooltip.tsx new file mode 100644 index 00000000..7739fdcf --- /dev/null +++ b/src/components/tooltip/tooltip.tsx @@ -0,0 +1,66 @@ +import { Transition } from "@headlessui/react"; +import { Placement } from "@popperjs/core"; +import React, { useEffect, useState } from "react"; +import { usePopper } from "react-popper"; +import { classNames } from "../../util/class-names"; + +interface TooltipProps { + children: React.ReactNode; + title: React.ReactNode; + position?: Placement; + className?: string; + open?: boolean; + onClose?: () => void; +} + +export const Tooltip = ({ children, title, position = "right", className, open }: TooltipProps) => { + const [referenceElement, setReferenceElement] = useState(); + const [popperElement, setPopperElement] = useState(); + const [show, setShow] = useState(false); + const [isControlled, setIsControlled] = useState(false); + + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: position, + modifiers: [{ name: "offset", options: { offset: [0, 8] } }], + }); + + useEffect(() => { + if (open !== undefined) { + setIsControlled(true); + setShow(open); + } + }, [open]); + + return ( +
+
el && setReferenceElement(el)} + onMouseEnter={() => !isControlled && setShow(true)} + onMouseLeave={() => !isControlled && setShow(false)} + > + {children} +
+ +
el && setPopperElement(el)} + className={classNames( + "rounded-lg bg-neutral-900 p-4 px-4 py-2 text-xs text-neutral-0 shadow", + className + )} + style={styles.popper} + {...attributes.popper} + > + {title} +
+
+
+ ); +};