Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { useCallback, useEffect, useRef } from "react";
import { BookOpen01, ChevronSelectorVertical, LogOut01, Plus, Settings01, User01 } from "@untitledui/icons";
import { useFocusManager } from "react-aria";
import type { DialogProps as AriaDialogProps, Placement as AriaPlacement } from "react-aria-components";
import { Button as AriaButton, Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Popover as AriaPopover } from "react-aria-components";
import { Button as AriaButton, Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger } from "react-aria-components";
import { AvatarLabelGroup } from "@/components/base/avatar/avatar-label-group";
import { Button } from "@/components/base/buttons/button";
import { RadioButtonBase } from "@/components/base/radio-buttons/radio-buttons";
import { useBreakpoint } from "@/hooks/use-breakpoint";
import { cx } from "@/utils/cx";
import { OverlayPopover } from "@/utils/overlay-popover";

export type NavAccountType = {
/** Unique identifier for the nav item. */
Expand Down Expand Up @@ -189,7 +190,7 @@ export const NavAccountCard = ({
<AriaButton className="absolute top-2 right-2 flex cursor-pointer items-center justify-center rounded-md p-1.5 text-fg-quaternary outline-focus-ring transition duration-100 ease-linear hover:bg-primary_hover hover:text-fg-quaternary_hover focus-visible:outline-2 focus-visible:outline-offset-2 pressed:bg-primary_hover pressed:text-fg-quaternary_hover">
<ChevronSelectorVertical className="size-4 shrink-0 stroke-[2.25px]" />
</AriaButton>
<AriaPopover
<OverlayPopover
placement={popoverPlacement ?? (isDesktop ? "right bottom" : "top right")}
triggerRef={triggerRef}
offset={8}
Expand All @@ -204,7 +205,7 @@ export const NavAccountCard = ({
}
>
<NavAccountMenu selectedAccountId={selectedAccountId} accounts={items} />
</AriaPopover>
</OverlayPopover>
</AriaDialogTrigger>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import type { FC } from "react";
import { useState } from "react";
import { DotsVertical, LifeBuoy01, Settings01 } from "@untitledui/icons";
import { AnimatePresence, motion } from "motion/react";
import { Button as AriaButton, DialogTrigger as AriaDialogTrigger, Popover as AriaPopover } from "react-aria-components";
import { Button as AriaButton, DialogTrigger as AriaDialogTrigger } from "react-aria-components";
import { Avatar } from "@/components/base/avatar/avatar";
import { ButtonUtility } from "@/components/base/buttons/button-utility";
import { UntitledLogo } from "@/components/foundations/logo/untitledui-logo";
import { UntitledLogoMinimal } from "@/components/foundations/logo/untitledui-logo-minimal";
import { cx } from "@/utils/cx";
import { OverlayPopover } from "@/utils/overlay-popover";
import { MobileNavigationHeader } from "../base-components/mobile-header";
import { NavAccountCard, NavAccountMenu } from "../base-components/nav-account-card";
import { NavButton } from "../base-components/nav-button";
Expand Down Expand Up @@ -104,7 +105,7 @@ export const SidebarNavigationSlim = ({ activeUrl, items, footerItems = [], hide
alt="Olivia Rhye"
/>
</AriaButton>
<AriaPopover
<OverlayPopover
placement="right bottom"
offset={8}
crossOffset={6}
Expand All @@ -119,7 +120,7 @@ export const SidebarNavigationSlim = ({ activeUrl, items, footerItems = [], hide
}
>
<NavAccountMenu />
</AriaPopover>
</OverlayPopover>
</AriaDialogTrigger>
</div>
</div>
Expand Down
9 changes: 5 additions & 4 deletions components/application/date-picker/date-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { useControlledState } from "@react-stately/utils";
import { Calendar as CalendarIcon } from "@untitledui/icons";
import { useDateFormatter } from "react-aria";
import type { DatePickerProps as AriaDatePickerProps, DateValue } from "react-aria-components";
import { DatePicker as AriaDatePicker, Dialog as AriaDialog, Group as AriaGroup, Popover as AriaPopover } from "react-aria-components";
import { DatePicker as AriaDatePicker, Dialog as AriaDialog, Group as AriaGroup } from "react-aria-components";
import { Button, type ButtonProps } from "@/components/base/buttons/button";
import { cx } from "@/utils/cx";
import { OverlayPopover } from "@/utils/overlay-popover";
import { Calendar } from "./calendar";

const highlightedDates = [today(getLocalTimeZone())];
Expand Down Expand Up @@ -37,9 +38,9 @@ export const DatePicker = ({ value: valueProp, defaultValue, onChange, onApply,
{formattedDate}
</Button>
</AriaGroup>
<AriaPopover
<OverlayPopover
offset={8}
placement="bottom right"
placement="bottom end"
className={({ isEntering, isExiting }) =>
cx(
"origin-(--trigger-anchor-point) will-change-transform",
Expand Down Expand Up @@ -81,7 +82,7 @@ export const DatePicker = ({ value: valueProp, defaultValue, onChange, onApply,
</>
)}
</AriaDialog>
</AriaPopover>
</OverlayPopover>
</AriaDatePicker>
);
};
9 changes: 5 additions & 4 deletions components/application/date-picker/date-range-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { useControlledState } from "@react-stately/utils";
import { Calendar as CalendarIcon } from "@untitledui/icons";
import { useDateFormatter } from "react-aria";
import type { DateRangePickerProps as AriaDateRangePickerProps, DateValue } from "react-aria-components";
import { DateRangePicker as AriaDateRangePicker, Dialog as AriaDialog, Group as AriaGroup, Popover as AriaPopover, useLocale } from "react-aria-components";
import { DateRangePicker as AriaDateRangePicker, Dialog as AriaDialog, Group as AriaGroup, useLocale } from "react-aria-components";
import { Button, type ButtonProps } from "@/components/base/buttons/button";
import { InputDateBase } from "@/components/base/input/input-date";
import { cx } from "@/utils/cx";
import { OverlayPopover } from "@/utils/overlay-popover";
import { RangeCalendar, RangePresetButton } from "./range-calendar";

const now = today(getLocalTimeZone());
Expand Down Expand Up @@ -83,8 +84,8 @@ export const DateRangePicker = ({ value: valueProp, defaultValue, onChange, onAp
{!value ? <span className="text-placeholder">Select dates</span> : `${formattedStartDate} – ${formattedEndDate}`}
</Button>
</AriaGroup>
<AriaPopover
placement="bottom right"
<OverlayPopover
placement="bottom end"
offset={8}
className={({ isEntering, isExiting }) =>
cx(
Expand Down Expand Up @@ -157,7 +158,7 @@ export const DateRangePicker = ({ value: valueProp, defaultValue, onChange, onAp
</>
)}
</AriaDialog>
</AriaPopover>
</OverlayPopover>
</AriaDateRangePicker>
);
};
44 changes: 28 additions & 16 deletions components/application/modals/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
import type { DialogProps as AriaDialogProps, ModalOverlayProps as AriaModalOverlayProps } from "react-aria-components";
import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Modal as AriaModal, ModalOverlay as AriaModalOverlay } from "react-aria-components";
import { cx } from "@/utils/cx";
import { IN_HOST_LAYER, useFocusedHostLayer, useForeignOverlaysOnTop, withHostLayerOffset } from "@/utils/overlay-host";

export const DialogTrigger = AriaDialogTrigger;

export const ModalOverlay = (props: AriaModalOverlayProps) => {
// Inside another library's modal layer (e.g. a shadcn/ui Dialog), render into it.
const host = useFocusedHostLayer();

return (
<AriaModalOverlay
{...props}
{...(host && !props.UNSTABLE_portalContainer
? { UNSTABLE_portalContainer: host, style: withHostLayerOffset(host, props.style), [IN_HOST_LAYER]: "" }
: {})}
className={(state) =>
cx(
"fixed inset-0 z-50 flex min-h-dvh w-full items-end justify-center bg-overlay/70 px-4 outline-hidden backdrop-blur-[6px] sm:items-center sm:justify-center sm:px-8",
Expand All @@ -25,22 +32,27 @@ export const ModalOverlay = (props: AriaModalOverlayProps) => {
);
};

export const Modal = (props: AriaModalOverlayProps) => (
<AriaModal
{...props}
className={(state) =>
cx(
"w-full rounded-xl bg-primary align-middle shadow-xl outline-hidden max-sm:overflow-y-auto sm:rounded-2xl",
// Max height based on parent's vertical padding
"max-h-[calc(var(--visual-viewport-height)-var(--modal-pt)-var(--modal-pb))]",
// Animations
state.isEntering && "duration-300 ease-out animate-in zoom-in-95",
state.isExiting && "duration-200 ease-in animate-out zoom-out-95",
typeof props.className === "function" ? props.className(state) : props.className,
)
}
/>
);
export const Modal = (props: AriaModalOverlayProps) => {
// Keeps menus and popovers of other libraries opened from inside the modal usable.
useForeignOverlaysOnTop();

return (
<AriaModal
{...props}
className={(state) =>
cx(
"w-full rounded-xl bg-primary align-middle shadow-xl outline-hidden max-sm:overflow-y-auto sm:rounded-2xl",
// Max height based on parent's vertical padding
"max-h-[calc(var(--visual-viewport-height)-var(--modal-pt)-var(--modal-pb))]",
// Animations
state.isEntering && "duration-300 ease-out animate-in zoom-in-95",
state.isExiting && "duration-200 ease-in animate-out zoom-out-95",
typeof props.className === "function" ? props.className(state) : props.className,
)
}
/>
);
};

export const Dialog = (props: AriaDialogProps) => (
<AriaDialog {...props} className={cx("relative max-h-[inherit] w-full overflow-y-auto outline-hidden", props.className)} />
Expand Down
38 changes: 25 additions & 13 deletions components/application/slideout-menus/slideout-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ import type {
import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Modal as AriaModal, ModalOverlay as AriaModalOverlay } from "react-aria-components";
import { CloseButton } from "@/components/base/buttons/close-button";
import { cx } from "@/utils/cx";
import { IN_HOST_LAYER, useFocusedHostLayer, useForeignOverlaysOnTop, withHostLayerOffset } from "@/utils/overlay-host";

interface ModalOverlayProps extends AriaModalOverlayProps, RefAttributes<HTMLDivElement> {}

export const ModalOverlay = (props: ModalOverlayProps) => {
// Inside another library's modal layer (e.g. a shadcn/ui Sheet), render into it.
const host = useFocusedHostLayer();

return (
<AriaModalOverlay
{...props}
{...(host && !props.UNSTABLE_portalContainer
? { UNSTABLE_portalContainer: host, style: withHostLayerOffset(host, props.style), [IN_HOST_LAYER]: "" }
: {})}
className={(state) =>
cx(
"fixed inset-0 flex min-h-dvh w-full items-center justify-end bg-overlay/70 pl-6 outline-hidden ease-linear md:pl-10",
Expand All @@ -31,19 +38,24 @@ ModalOverlay.displayName = "ModalOverlay";

interface ModalProps extends AriaModalOverlayProps, RefAttributes<HTMLDivElement> {}

export const Modal = (props: ModalProps) => (
<AriaModal
{...props}
className={(state) =>
cx(
"inset-y-0 right-0 h-full w-full max-w-100 shadow-xl transition",
state.isEntering && "duration-300 animate-in slide-in-from-right",
state.isExiting && "duration-500 animate-out slide-out-to-right",
typeof props.className === "function" ? props.className(state) : props.className,
)
}
/>
);
export const Modal = (props: ModalProps) => {
// Keeps menus and popovers of other libraries opened from inside the slideout usable.
useForeignOverlaysOnTop();

return (
<AriaModal
{...props}
className={(state) =>
cx(
"inset-y-0 right-0 h-full w-full max-w-100 shadow-xl transition",
state.isEntering && "duration-300 animate-in slide-in-from-right",
state.isExiting && "duration-500 animate-out slide-out-to-right",
typeof props.className === "function" ? props.className(state) : props.className,
)
}
/>
);
};
Modal.displayName = "Modal";

interface DialogProps extends AriaDialogProps, RefAttributes<HTMLElement> {}
Expand Down
2 changes: 1 addition & 1 deletion components/base/dropdown/dropdown-account-breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const DropdownAccountBreadcrumb = () => {
<ChevronSelectorVertical className="size-3 shrink-0 stroke-3 text-fg-quaternary" />
</AriaButton>

<Dropdown.Popover className="w-62" placement="bottom left">
<Dropdown.Popover className="w-62" placement="bottom start">
<Dropdown.Menu
disallowEmptySelection
selectionMode="single"
Expand Down
2 changes: 1 addition & 1 deletion components/base/dropdown/dropdown-account-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const DropdownAccountButton = () => {
<SubmenuTrigger>
<Dropdown.Item icon={HelpCircle}>Support</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Help center</Dropdown.Item>
<Dropdown.Item>Contact support</Dropdown.Item>
Expand Down
4 changes: 2 additions & 2 deletions components/base/dropdown/dropdown-account-card-md.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const DropdownAccountCardMD = () => {
<SubmenuTrigger>
<Dropdown.Item icon={HelpCircle}>Support</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Help center</Dropdown.Item>
<Dropdown.Item>Contact support</Dropdown.Item>
Expand All @@ -81,7 +81,7 @@ export const DropdownAccountCardMD = () => {
<SubmenuTrigger>
<Dropdown.Item icon={LogOut01}>Sign out</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Current device</Dropdown.Item>
<Dropdown.Item>All devices</Dropdown.Item>
Expand Down
4 changes: 2 additions & 2 deletions components/base/dropdown/dropdown-account-card-sm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const DropdownAccountCardSM = () => {
<SubmenuTrigger>
<Dropdown.Item icon={HelpCircle}>Support</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Help center</Dropdown.Item>
<Dropdown.Item>Contact support</Dropdown.Item>
Expand Down Expand Up @@ -83,7 +83,7 @@ export const DropdownAccountCardSM = () => {
<SubmenuTrigger>
<Dropdown.Item icon={LogOut01}>Sign out</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Current device</Dropdown.Item>
<Dropdown.Item>All devices</Dropdown.Item>
Expand Down
2 changes: 1 addition & 1 deletion components/base/dropdown/dropdown-account-card-xs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const DropdownAccountCardXS = () => {
<SubmenuTrigger>
<Dropdown.Item icon={LogOut01}>Sign out</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Current device</Dropdown.Item>
<Dropdown.Item>All devices</Dropdown.Item>
Expand Down
2 changes: 1 addition & 1 deletion components/base/dropdown/dropdown-avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const DropdownAvatar = () => {
<SubmenuTrigger>
<Dropdown.Item icon={HelpCircle}>Support</Dropdown.Item>

<Dropdown.Popover placement="right top" offset={-6}>
<Dropdown.Popover placement="end top" offset={-6}>
<Dropdown.Menu>
<Dropdown.Item>Help center</Dropdown.Item>
<Dropdown.Item>Contact support</Dropdown.Item>
Expand Down
6 changes: 3 additions & 3 deletions components/base/dropdown/dropdown-button-advanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ export const DropdownButtonAdvanced = () => {
<Dropdown.Section>
<SubmenuTrigger>
<Dropdown.Item icon={Cube01}>More tools</Dropdown.Item>
<Dropdown.Popover placement="right top" offset={-6} className="w-50">
<Dropdown.Popover placement="end top" offset={-6} className="w-50">
<Dropdown.Menu selectionMode="none">
<SubmenuTrigger>
<Dropdown.Item icon={Download01}>Save as</Dropdown.Item>
<Dropdown.Popover placement="right top" offset={-6} className="w-50">
<Dropdown.Popover placement="end top" offset={-6} className="w-50">
<Dropdown.Menu selectionMode="none">
<Dropdown.Item>PDF</Dropdown.Item>
<Dropdown.Item>HTML</Dropdown.Item>
Expand All @@ -98,7 +98,7 @@ export const DropdownButtonAdvanced = () => {

<SubmenuTrigger>
<Dropdown.Item icon={Code02}>Developer</Dropdown.Item>
<Dropdown.Popover placement="right top" offset={-6} className="w-50">
<Dropdown.Popover placement="end top" offset={-6} className="w-50">
<Dropdown.Menu selectionMode="none">
<Dropdown.Item>View source</Dropdown.Item>
<Dropdown.Item>Developer tools</Dropdown.Item>
Expand Down
2 changes: 1 addition & 1 deletion components/base/dropdown/dropdown-button-simple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const DropdownButtonSimple = () => (
<Dropdown.Section>
<SubmenuTrigger>
<Dropdown.Item>View details</Dropdown.Item>
<Dropdown.Popover placement="right top" offset={-6} className="w-50">
<Dropdown.Popover placement="end top" offset={-6} className="w-50">
<Dropdown.Menu>
<Dropdown.Item>Share</Dropdown.Item>
<Dropdown.Item>Save as</Dropdown.Item>
Expand Down
Loading
Loading