diff --git a/package.json b/package.json index 29f860e..cf96bed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vectara/vectara-ui", - "version": "1.0.1", + "version": "1.0.2", "homepage": "https://vectara.github.io/vectara-ui/", "description": "Vectara's design system, codified as a React and Sass component library", "author": "Vectara", diff --git a/src/docs/pages/popover/Popover.tsx b/src/docs/pages/popover/Popover.tsx index 12f161b..54fc603 100644 --- a/src/docs/pages/popover/Popover.tsx +++ b/src/docs/pages/popover/Popover.tsx @@ -1,5 +1,14 @@ import { useState } from "react"; -import { VuiButtonSecondary, VuiIcon, VuiOptionsList, VuiPopover } from "../../../lib"; +import { + AnchorSide, + VuiButtonSecondary, + VuiFormGroup, + VuiIcon, + VuiOptionsList, + VuiPopover, + VuiSelect, + VuiSpacer +} from "../../../lib"; import { BiCaretDown } from "react-icons/bi"; const options = [ @@ -11,36 +20,54 @@ const options = [ export const Popover = () => { const [isOpen, setIsOpen] = useState(false); const [selectedOption, setSelectedOption] = useState("apples"); + const [anchorSide, setAnchorSide] = useState("right"); return ( - setIsOpen(!isOpen)} - header="Tribes" - button={ - - - - } - > - Tribe: {selectedOption} - - } - > - { - setIsOpen(false); - setSelectedOption(value); - }} - selected={selectedOption} - options={options} - /> - + <> + + setAnchorSide(event.target.value as AnchorSide)} + /> + + + + + setIsOpen(!isOpen)} + header="Tribes" + button={ + + + + } + > + Tribe: {selectedOption} + + } + > + { + setIsOpen(false); + setSelectedOption(value); + }} + selected={selectedOption} + options={options} + /> + + ); }; diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 2239d84..4b7940b 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -54,7 +54,7 @@ import { VuiOptionsButton } from "./optionsButton/OptionsButton"; import { VuiOptionsList } from "./optionsList/OptionsList"; import { VuiOptionsListItem } from "./optionsList/OptionsListItem"; import { OptionListItem } from "./optionsList/types"; -import { VuiPopover } from "./popover/Popover"; +import { VuiPopover, AnchorSide } from "./popover/Popover"; import { VuiPortal } from "./portal/Portal"; import { PROGRESS_BAR_COLOR, VuiProgressBar } from "./progressBar/ProgressBar"; import { VuiPrompt } from "./prompt/Prompt"; @@ -84,6 +84,7 @@ import { VuiToggle } from "./toggle/Toggle"; import { VuiTopicButton } from "./topicButton/TopicButton"; export type { + AnchorSide, AppContentPadding, ButtonColor, CalloutColor, diff --git a/src/lib/components/popover/Popover.tsx b/src/lib/components/popover/Popover.tsx index 75c4c16..b4bf0a3 100644 --- a/src/lib/components/popover/Popover.tsx +++ b/src/lib/components/popover/Popover.tsx @@ -3,6 +3,8 @@ import classNames from "classnames"; import { VuiPortal } from "../portal/Portal"; import { FocusOn } from "react-focus-on"; +export type AnchorSide = "left" | "right"; + export type Props = { button: React.ReactElement; children?: React.ReactNode; @@ -11,20 +13,28 @@ export type Props = { isOpen: boolean; setIsOpen: (isOpen: boolean) => void; padding?: boolean; + anchorSide?: AnchorSide; }; type Position = { - top: number; - right: number; + top: string; + left?: string; + right?: string; }; -const getPosition = (button: HTMLElement | null): Position | undefined => { +const calculatePopoverPosition = (button: HTMLElement | null, anchorSide: AnchorSide): Position | undefined => { if (!button) return undefined; - const { bottom, right } = button.getBoundingClientRect(); - return { - top: bottom + 2 + document.documentElement.scrollTop, - right: window.innerWidth - right - }; + + const buttonRect = button.getBoundingClientRect(); + const top = buttonRect.bottom + 2 + document.documentElement.scrollTop; + const left = buttonRect.left; + + if (anchorSide === "left") { + return { top: `${top}px`, left: `${left}px` }; + } + + const right = window.innerWidth - buttonRect.right; + return { top: `${top}px`, right: `${right}px` }; }; export const VuiPopover = ({ @@ -35,6 +45,7 @@ export const VuiPopover = ({ isOpen, setIsOpen, padding, + anchorSide = "right", ...rest }: Props) => { const returnFocusElRef = useRef(null); @@ -90,7 +101,7 @@ export const VuiPopover = ({ // Always keep menu position up to date. If we tried to cache this inside // a useEffect based on isOpen then there'd be a flicker if the width // of the button changes. - const position = getPosition(buttonRef.current); + const position = calculatePopoverPosition(buttonRef.current, anchorSide); const classes = classNames("vuiPopover", className); @@ -117,7 +128,7 @@ export const VuiPopover = ({ // Enable scrolling of the page. preventScrollOnFocus={false} > -
+
{header && typeof header === "string" ?
{header}
: header} {children &&
{children}
}