diff --git a/packages/tooltip/src/components/popover/Popover2.module.css b/packages/tooltip/src/components/popover/Popover2.module.css new file mode 100644 index 000000000..7a6d81145 --- /dev/null +++ b/packages/tooltip/src/components/popover/Popover2.module.css @@ -0,0 +1,54 @@ +.floating { + display: flex; + flex-direction: row; + align-items: center; + background: white; + color: white; + border-radius: var(--swui-border-radius); + padding: var(--swui-metrics-spacing) calc(var(--swui-metrics-indent) * 2); + box-shadow: var(--swui-shadow-popover); + + &.withIcon { + padding: calc(var(--swui-metrics-spacing) * 0.5); + padding-right: calc(var(--swui-metrics-indent) * 2); + } +} + +.iconWrapper { + /* State vars */ + --current-bg-color: transparent; + --current-border-color: var(--lhds-color-ui-50); + --current-icon-color: var(--lhds-color-ui-50); + + /* Styling */ + display: inline-flex; + flex-direction: row; + white-space: nowrap; + background-color: var(--current-bg-color); + border: 1px solid var(--current-border-color); + border-radius: var(--swui-border-radius); + padding: calc(var(--swui-metrics-spacing) * 0.5); + align-items: flex-start; + + svg { + color: var(--current-icon-color); + } + + &.info { + --current-border-color: var(--lhds-color-blue-300); + --current-bg-color: var(--lhds-color-blue-100); + --current-icon-color: var(--lhds-color-blue-700); + } + + &.warning { + --current-border-color: var(--lhds-color-orange-300); + --current-bg-color: var(--lhds-color-orange-100); + --current-icon-color: var(--lhds-color-orange-700); + } + + &.error { + --current-border-color: var(--lhds-color-red-300); + --current-bg-color: var(--lhds-color-red-100); + --current-icon-color: var(--lhds-color-red-700); + } +} diff --git a/packages/tooltip/src/components/popover/Popover2.stories.tsx b/packages/tooltip/src/components/popover/Popover2.stories.tsx new file mode 100644 index 000000000..2b66208ba --- /dev/null +++ b/packages/tooltip/src/components/popover/Popover2.stories.tsx @@ -0,0 +1,147 @@ +import { + Box, + Column, + Indent, + Row, + SeparatorLine, + Spacing, + Text, +} from "@stenajs-webui/core"; +import { FlatButton, Icon, stenaTrash } from "@stenajs-webui/elements"; +import * as React from "react"; +import { ActionPrompt } from "./ActionPrompt"; +import { Popover2 } from "./Popover2"; +import { PopoverButton } from "./PopoverButton"; + +export default { + title: "tooltip/Popover2", + component: Popover2, + subcomponents: { PopoverButton, ActionPrompt }, +}; + +export const Standard = () => ( + + } + renderButton={() => } + /> + +); + +export const OnClick = () => ( + + } + renderButton={({ onClick }) => ( + + )} + /> + +); + +export const NoArrow = () => ( + + } + renderButton={({ onClick }) => ( + + )} + arrow={false} + /> + +); + +export const NoPadding = () => ( + + + + The line has + + + + no padding + + + } + renderButton={({ onClick }) => ( + + )} + /> + +); + +export const Variants = () => ( + + } + renderButton={({ onClick }) => ( + + )} + /> + + + + } + renderButton={({ onClick }) => ( + + )} + variant={"info"} + /> + + + + Some warning.} + variant={"warning"} + renderButton={({ onClick }) => ( + + )} + /> + + + + + + + Something went wrong. + + } + variant={"error"} + renderButton={({ onClick }) => ( + + )} + /> + +); + +export const ControlOpen = () => ( + + } visible> + + + +); + +const Alerter = () => { + alert("I was rendered."); + + return I alert when I am rendered.; +}; + +export const Lazy = () => ( + + } + lazy + renderButton={({ onClick }) => ( + + )} + /> + +); diff --git a/packages/tooltip/src/components/popover/Popover2.tsx b/packages/tooltip/src/components/popover/Popover2.tsx new file mode 100644 index 000000000..d7f87259a --- /dev/null +++ b/packages/tooltip/src/components/popover/Popover2.tsx @@ -0,0 +1,109 @@ +import * as React from "react"; +import { PropsWithChildren, ReactNode, useRef } from "react"; +import { Placement } from "../tooltip/Tooltip"; +import { + arrow, + autoUpdate, + flip, + FloatingArrow, + FloatingFocusManager, + offset, + shift, + useDismiss, + useFloating, + useInteractions, + useRole, + useTransitionStyles, +} from "@floating-ui/react"; +import cx from "classnames"; +import moduleStyles from "./Popover2.module.css"; + +export type PopoverVariant = + | "standard" + | "info" + | "warning" + | "error" + | "outlined"; + +export type PopoverTheme = "light" | "dark"; + +export interface Popover2Props extends PropsWithChildren { + visible?: boolean; + onRequestClose?: () => void; + placement?: Placement; + arrow?: boolean; + disablePadding?: boolean; + lazy?: boolean; + variant?: PopoverVariant; + theme?: PopoverTheme; + content: ReactNode; +} + +const ARROW_WIDTH = 12; +const ARROW_HEIGHT = 8; +const GAP = 2; + +export const Popover2: React.FC = ({ + children, + variant, + content, + visible, + arrow: arrowVisible = true, +}) => { + const arrowRef = useRef(null); + + const { refs, floatingStyles, context } = useFloating({ + open: visible, + middleware: [ + offset(ARROW_HEIGHT + GAP), + flip({ padding: 5 }), + shift({ padding: 5 }), + arrow({ element: arrowRef }), + ], + whileElementsMounted: autoUpdate, + }); + + const { isMounted, styles } = useTransitionStyles(context, { + initial: { + opacity: 0, + }, + }); + + const dismiss = useDismiss(context); + const role = useRole(context); + + const { getReferenceProps } = useInteractions([dismiss, role]); + + return ( + <> + + {children} + + + {isMounted && ( + + + + {content} + {arrowVisible && ( + + )} + + + + )} + > + ); +}; diff --git a/packages/tooltip/src/components/popover/PopoverButton.tsx b/packages/tooltip/src/components/popover/PopoverButton.tsx new file mode 100644 index 000000000..0c58bd62e --- /dev/null +++ b/packages/tooltip/src/components/popover/PopoverButton.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import { ReactNode } from "react"; +import { Popover2, Popover2Props } from "./Popover2"; +import { useBoolean } from "@stenajs-webui/core"; + +export type PopoverButtonRenderer = ( + args: PopoverButtonRendererArgs +) => ReactNode; + +export interface PopoverButtonRendererArgs { + onClick: () => void; +} + +export interface PopoverButtonProps extends Omit { + renderButton: PopoverButtonRenderer; +} + +export const PopoverButton: React.FC = ({ + renderButton, + ...popoverProps +}) => { + const [isOpen, open, close] = useBoolean(false); + return ( + + {renderButton({ onClick: open })} + + ); +}; diff --git a/packages/tooltip/src/components/tooltip/Tooltip.tsx b/packages/tooltip/src/components/tooltip/Tooltip.tsx index 505d7dbfb..87e15236d 100644 --- a/packages/tooltip/src/components/tooltip/Tooltip.tsx +++ b/packages/tooltip/src/components/tooltip/Tooltip.tsx @@ -25,9 +25,13 @@ import { export interface TooltipProps extends PropsWithChildren { placement?: Placement; + visible?: boolean; label: string; variant?: TooltipVariant; maxWidth?: CSSProperties["maxWidth"]; + // Remove + appendTo?: HTMLElement; + zIndex?: number | string; } type TooltipVariant = "info" | "warning" | "error"; @@ -46,6 +50,7 @@ export type Placement = "top" | "right" | "bottom" | "left"; export const Tooltip: React.FC = ({ children, + visible, placement, label, variant, @@ -57,7 +62,7 @@ export const Tooltip: React.FC = ({ const { refs, floatingStyles, context } = useFloating({ placement, - open: isOpen, + open: visible ?? isOpen, onOpenChange: setIsOpen, middleware: [ offset(ARROW_HEIGHT + GAP), @@ -115,6 +120,7 @@ export const Tooltip: React.FC = ({ context={context} width={ARROW_WIDTH} height={ARROW_HEIGHT} + fill={"#333"} />