Ensure topbanner is always on top of page layout rather than inside main content (#6098)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
co-authored by
Peer Richelsen
parent
48611d5c99
commit
795574ef88
@@ -1458,6 +1458,8 @@
|
||||
"event_type_duplicate_copy_text": "{{slug}}-copy",
|
||||
"set_as_default": "Set as default",
|
||||
"hide_eventtype_details": "Hide EventType Details",
|
||||
"show_navigation": "Show navigation",
|
||||
"hide_navigation": "Hide navigation",
|
||||
"verification_code_sent": "Verification code sent",
|
||||
"verified_successfully": "Verified successfully",
|
||||
"wrong_code": "Wong verification code",
|
||||
|
||||
@@ -16,9 +16,11 @@ export function TeamsUpgradeBanner() {
|
||||
showToast(error.message, "error");
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) return null;
|
||||
const [membership] = data;
|
||||
if (!membership) return null;
|
||||
|
||||
return (
|
||||
<TopBanner
|
||||
text={t("team_upgrade_banner_description", { teamName: membership.team.name })}
|
||||
|
||||
@@ -4,6 +4,8 @@ import classNames from "classnames";
|
||||
import noop from "lodash/noop";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { Icon } from "@calcom/ui";
|
||||
|
||||
export type TopBannerProps = {
|
||||
text: string;
|
||||
variant?: keyof typeof variantClassName;
|
||||
@@ -23,17 +25,20 @@ export function TopBanner(props: TopBannerProps) {
|
||||
<div
|
||||
data-testid="banner"
|
||||
className={classNames(
|
||||
"flex min-h-[40px] w-full items-start justify-between gap-8 bg-gray-50 py-2 px-4 text-center lg:items-center",
|
||||
"flex min-h-[40px] w-full items-start justify-between gap-8 py-2 px-4 text-center lg:items-center",
|
||||
variantClassName[variant]
|
||||
)}>
|
||||
<div className="flex flex-1 flex-col items-start justify-center gap-2 p-1 lg:flex-row lg:items-center">
|
||||
<p className="flex flex-col items-start justify-center gap-2 text-left font-sans text-sm font-medium leading-4 text-gray-900 lg:flex-row lg:items-center">
|
||||
{["warning", "error"].includes(variant) && (
|
||||
<ExclamationIcon className="h-5 w-5 text-black" aria-hidden="true" />
|
||||
{variant === "error" && (
|
||||
<Icon.FiAlertTriangle className="h-4 w-4 stroke-[2.5px] text-black" aria-hidden="true" />
|
||||
)}
|
||||
{variant === "warning" && (
|
||||
<Icon.FiInfo className="h-4 w-4 stroke-[2.5px] text-black" aria-hidden="true" />
|
||||
)}
|
||||
{text}
|
||||
</p>
|
||||
{actions && <div className="text-sm">{actions}</div>}
|
||||
{actions && <div className="text-sm font-medium">{actions}</div>}
|
||||
</div>
|
||||
{typeof onClose === "function" && (
|
||||
<button
|
||||
|
||||
@@ -153,12 +153,14 @@ const Layout = (props: LayoutProps) => {
|
||||
|
||||
{/* todo: only run this if timezone is different */}
|
||||
<TimezoneChangeDialog />
|
||||
<div className="h-screen overflow-hidden">
|
||||
<div className="flex h-screen overflow-hidden" data-testid="dashboard-shell">
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<div className="divide-y divide-black">
|
||||
<TeamsUpgradeBanner />
|
||||
<ImpersonatingBanner />
|
||||
</div>
|
||||
<div className="flex flex-1" data-testid="dashboard-shell">
|
||||
{props.SidebarContainer || <SideBarContainer />}
|
||||
<div className="flex w-0 flex-1 flex-col overflow-hidden">
|
||||
<TeamsUpgradeBanner />
|
||||
<ImpersonatingBanner />
|
||||
<div className="flex w-0 flex-1 flex-col">
|
||||
<MainContainer {...props} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -177,7 +179,6 @@ type LayoutProps = {
|
||||
children: ReactNode;
|
||||
CTA?: ReactNode;
|
||||
large?: boolean;
|
||||
SettingsSidebarContainer?: ReactNode;
|
||||
MobileNavigationContainer?: ReactNode;
|
||||
SidebarContainer?: ReactNode;
|
||||
TopNavContainer?: ReactNode;
|
||||
@@ -540,7 +541,7 @@ const { desktopNavigationItems, mobileNavigationBottomItems, mobileNavigationMor
|
||||
Record<string, NavigationItemType[]>
|
||||
>(
|
||||
(items, item, index) => {
|
||||
// We filter out the "more" separator in desktop navigation
|
||||
// We filter out the "more" separator in` desktop navigation
|
||||
if (item.name !== MORE_SEPARATOR_NAME) items.desktopNavigationItems.push(item);
|
||||
// Items for mobile bottom navigation
|
||||
if (index < moreSeparatorIndex + 1 && !item.onlyDesktop) items.mobileNavigationBottomItems.push(item);
|
||||
@@ -714,9 +715,6 @@ const MobileNavigationMoreItem: React.FC<{
|
||||
};
|
||||
|
||||
function DeploymentInfo() {
|
||||
const query = useMeQuery();
|
||||
const user = query.data;
|
||||
|
||||
return (
|
||||
<small
|
||||
style={{
|
||||
@@ -742,59 +740,59 @@ function SideBarContainer() {
|
||||
|
||||
function SideBar() {
|
||||
return (
|
||||
<aside className="desktop-transparent hidden w-14 flex-col border-r border-gray-100 bg-gray-50 md:flex lg:w-56 lg:flex-shrink-0 lg:px-4">
|
||||
<div className="flex h-0 flex-1 flex-col overflow-y-auto pt-3 pb-4 lg:pt-5">
|
||||
<header className="items-center justify-between md:hidden lg:flex">
|
||||
<div className="relative">
|
||||
<aside className="desktop-transparent top-0 hidden h-full max-h-screen w-14 flex-col overflow-y-auto border-r border-gray-100 bg-gray-50 md:sticky md:flex lg:w-56 lg:px-4">
|
||||
<div className="flex flex-col pt-3 pb-32 lg:pt-5">
|
||||
<header className="items-center justify-between md:hidden lg:flex">
|
||||
<Link href="/event-types">
|
||||
<a className="px-4">
|
||||
<Logo small />
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
color="minimal"
|
||||
onClick={() => window.history.back()}
|
||||
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
|
||||
<Icon.FiArrowLeft className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
|
||||
</button>
|
||||
<button
|
||||
color="minimal"
|
||||
onClick={() => window.history.forward()}
|
||||
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
|
||||
<Icon.FiArrowRight className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
|
||||
</button>
|
||||
<KBarTrigger />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<hr className="desktop-only absolute -left-3 -right-3 mt-4 block w-full border-gray-200" />
|
||||
|
||||
{/* logo icon for tablet */}
|
||||
<Link href="/event-types">
|
||||
<a className="px-4">
|
||||
<Logo small />
|
||||
<a className="text-center md:inline lg:hidden">
|
||||
<Logo small icon />
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
color="minimal"
|
||||
onClick={() => window.history.back()}
|
||||
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
|
||||
<Icon.FiArrowLeft className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
|
||||
</button>
|
||||
<button
|
||||
color="minimal"
|
||||
onClick={() => window.history.forward()}
|
||||
className="desktop-only group flex text-sm font-medium text-neutral-500 hover:text-neutral-900">
|
||||
<Icon.FiArrowRight className="h-4 w-4 flex-shrink-0 text-neutral-500 group-hover:text-neutral-900" />
|
||||
</button>
|
||||
<KBarTrigger />
|
||||
|
||||
<Navigation />
|
||||
</div>
|
||||
|
||||
{isCalcom && <Tips />}
|
||||
|
||||
<div className="fixed left-1 bottom-0 w-4 bg-gray-50 before:absolute before:left-0 before:-top-20 before:h-20 before:w-48 before:bg-gradient-to-t before:from-gray-50 before:to-transparent lg:left-4 lg:w-48">
|
||||
<div data-testid="user-dropdown-trigger">
|
||||
<span className="hidden lg:inline">
|
||||
<UserDropdown />
|
||||
</span>
|
||||
<span className="hidden md:inline lg:hidden">
|
||||
<UserDropdown small />
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<hr className="desktop-only absolute -left-3 -right-3 mt-4 block w-full border-gray-200" />
|
||||
|
||||
{/* logo icon for tablet */}
|
||||
<Link href="/event-types">
|
||||
<a className="text-center md:inline lg:hidden">
|
||||
<Logo small icon />
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Navigation />
|
||||
</div>
|
||||
|
||||
{isCalcom && <Tips />}
|
||||
{/* Save it for next preview version
|
||||
<div className="hidden mb-4 lg:block">
|
||||
<UserV2OptInBanner />
|
||||
</div> */}
|
||||
|
||||
<div data-testid="user-dropdown-trigger">
|
||||
<span className="hidden lg:inline">
|
||||
<UserDropdown />
|
||||
</span>
|
||||
<span className="hidden md:inline lg:hidden">
|
||||
<UserDropdown small />
|
||||
</span>
|
||||
</div>
|
||||
<DeploymentInfo />
|
||||
</aside>
|
||||
<DeploymentInfo />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -855,35 +853,17 @@ export function ShellMain(props: LayoutProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const SettingsSidebarContainerDefault = () => null;
|
||||
|
||||
function MainContainer({
|
||||
SettingsSidebarContainer: SettingsSidebarContainerProp = <SettingsSidebarContainerDefault />,
|
||||
MobileNavigationContainer: MobileNavigationContainerProp = <MobileNavigationContainer />,
|
||||
TopNavContainer: TopNavContainerProp = <TopNavContainer />,
|
||||
...props
|
||||
}: LayoutProps) {
|
||||
const [sideContainerOpen, setSideContainerOpen] = props.drawerState || [false, noop];
|
||||
|
||||
return (
|
||||
<main className="relative z-0 flex flex-1 flex-col overflow-y-auto bg-white focus:outline-none">
|
||||
<main className="relative z-0 flex-1 bg-white focus:outline-none">
|
||||
{/* show top navigation for md and smaller (tablet and phones) */}
|
||||
{TopNavContainerProp}
|
||||
{/* The following is used for settings navigation on medium and smaller screens */}
|
||||
<div
|
||||
className={classNames(
|
||||
"overflow-none fixed z-40 m-0 h-screen w-screen overscroll-none bg-black opacity-50",
|
||||
sideContainerOpen ? "" : "hidden"
|
||||
)}
|
||||
onClick={() => {
|
||||
setSideContainerOpen(false);
|
||||
}}
|
||||
/>
|
||||
{SettingsSidebarContainerProp}
|
||||
<div className="max-w-full px-4 py-2 lg:py-8 lg:px-12">
|
||||
<ErrorBoundary>
|
||||
{/* add padding to top for mobile when App Bar is fixed */}
|
||||
<div className="pt-14 sm:hidden" />
|
||||
{!props.withoutMain ? <ShellMain {...props}>{props.children}</ShellMain> : props.children}
|
||||
</ErrorBoundary>
|
||||
{/* show bottom navigation for md and smaller (tablet and phones) on pages where back button doesn't exist */}
|
||||
@@ -906,7 +886,7 @@ function TopNav() {
|
||||
<>
|
||||
<nav
|
||||
style={isEmbed ? { display: "none" } : {}}
|
||||
className="fixed z-40 flex w-full items-center justify-between border-b border-gray-200 bg-gray-50 bg-opacity-50 py-1.5 px-4 backdrop-blur-lg sm:relative sm:p-4 md:hidden">
|
||||
className="sticky top-0 z-40 flex w-full items-center justify-between border-b border-gray-200 bg-gray-50 bg-opacity-50 py-1.5 px-4 backdrop-blur-lg sm:p-4 md:hidden">
|
||||
<Link href="/event-types">
|
||||
<a>
|
||||
<Logo />
|
||||
|
||||
@@ -100,7 +100,15 @@ const useTabs = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const SettingsSidebarContainer = ({ className = "" }) => {
|
||||
interface SettingsSidebarContainerProps {
|
||||
className?: string;
|
||||
navigationIsOpenedOnMobile?: boolean;
|
||||
}
|
||||
|
||||
const SettingsSidebarContainer = ({
|
||||
className = "",
|
||||
navigationIsOpenedOnMobile,
|
||||
}: SettingsSidebarContainerProps) => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const tabsWithPermissions = useTabs();
|
||||
@@ -127,7 +135,13 @@ const SettingsSidebarContainer = ({ className = "" }) => {
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`no-scrollbar flex w-56 flex-col space-y-1 overflow-x-hidden overflow-y-scroll py-3 px-2 ${className}`}
|
||||
className={classNames(
|
||||
"no-scrollbar fixed left-0 top-0 z-10 flex max-h-screen w-56 flex-col space-y-1 overflow-x-hidden overflow-y-scroll bg-gray-50 py-3 px-2 transition-transform lg:sticky lg:flex",
|
||||
className,
|
||||
navigationIsOpenedOnMobile
|
||||
? "translate-x-0 opacity-100"
|
||||
: "-translate-x-full opacity-0 lg:translate-x-0 lg:opacity-100"
|
||||
)}
|
||||
aria-label="Tabs">
|
||||
<>
|
||||
<div className="desktop-only pt-4" />
|
||||
@@ -295,9 +309,12 @@ const MobileSettingsContainer = (props: { onSideContainerOpen?: () => void }) =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="fixed z-20 flex w-full items-center justify-between border-b border-gray-100 bg-gray-50 p-4 sm:relative lg:hidden">
|
||||
<nav className="sticky top-0 z-20 flex w-full items-center justify-between border-b border-gray-100 bg-gray-50 p-4 sm:relative lg:hidden">
|
||||
<div className="flex items-center space-x-3 ">
|
||||
<Button StartIcon={Icon.FiMenu} color="minimal" size="icon" onClick={props.onSideContainerOpen} />
|
||||
<Button StartIcon={Icon.FiMenu} color="minimal" size="icon" onClick={props.onSideContainerOpen}>
|
||||
<span className="sr-only">{t("show_navigation")}</span>
|
||||
</Button>
|
||||
|
||||
<a href="/" className="flex items-center space-x-2 rounded-md px-3 py-1 hover:bg-gray-200">
|
||||
<Icon.FiArrowLeft className="text-gray-700" />
|
||||
<p className="font-semibold text-black">{t("settings")}</p>
|
||||
@@ -314,6 +331,7 @@ export default function SettingsLayout({
|
||||
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
||||
const router = useRouter();
|
||||
const state = useState(false);
|
||||
const { t } = useLocale();
|
||||
const [sideContainerOpen, setSideContainerOpen] = state;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -340,18 +358,21 @@ export default function SettingsLayout({
|
||||
withoutSeo={true}
|
||||
flexChildrenContainer
|
||||
{...rest}
|
||||
SidebarContainer={<SettingsSidebarContainer className="hidden lg:flex" />}
|
||||
SidebarContainer={
|
||||
<>
|
||||
{/* Mobile backdrop */}
|
||||
{sideContainerOpen && (
|
||||
<button
|
||||
onClick={() => setSideContainerOpen(false)}
|
||||
className="fixed top-0 left-0 z-10 h-full w-full bg-black/50">
|
||||
<span className="sr-only">{t("hide_navigation")}</span>
|
||||
</button>
|
||||
)}
|
||||
<SettingsSidebarContainer navigationIsOpenedOnMobile={sideContainerOpen} />
|
||||
</>
|
||||
}
|
||||
drawerState={state}
|
||||
MobileNavigationContainer={null}
|
||||
SettingsSidebarContainer={
|
||||
<div
|
||||
className={classNames(
|
||||
"fixed inset-y-0 z-50 m-0 h-screen w-56 transform overflow-x-hidden overflow-y-scroll border-gray-100 bg-gray-50 transition duration-200 ease-in-out",
|
||||
sideContainerOpen ? "translate-x-0" : "-translate-x-full"
|
||||
)}>
|
||||
<SettingsSidebarContainer />
|
||||
</div>
|
||||
}
|
||||
TopNavContainer={
|
||||
<MobileSettingsContainer onSideContainerOpen={() => setSideContainerOpen(!sideContainerOpen)} />
|
||||
}>
|
||||
@@ -371,7 +392,7 @@ function ShellHeader() {
|
||||
const { meta } = useMeta();
|
||||
const { t, isLocaleReady } = useLocale();
|
||||
return (
|
||||
<header className="mx-auto block justify-between pt-12 sm:flex sm:pt-8">
|
||||
<header className="mx-auto block justify-between pt-8 sm:flex">
|
||||
<div className="mb-8 flex w-full items-center border-b border-gray-200 pb-8">
|
||||
{meta.backButton && (
|
||||
<a href="javascript:history.back()">
|
||||
|
||||
Reference in New Issue
Block a user