This commit is contained in:
Nolann Biron
2025-06-08 20:19:12 +02:00
parent f5235756d4
commit 2d5fc43395
11 changed files with 77 additions and 52 deletions
@@ -7,6 +7,7 @@ import { useState } from 'react';
import { type ClassValue, tcls } from '@/lib/tailwind';
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
import { Slot } from '@radix-ui/react-slot';
import { Link, type LinkInsightsProps } from '../primitives';
@@ -25,13 +26,21 @@ export function DropdownMenu(props: {
children: React.ReactNode;
/** Custom styles */
className?: ClassValue;
/** Open the dropdown on hover */
/** Open the dropdown on hover
* @default false
*/
openOnHover?: boolean;
/** Whether to render the dropdown menu in a portal
* @default true
*/
withPortal?: boolean;
}) {
const { button, children, className, openOnHover = false } = props;
const { button, children, className, openOnHover = false, withPortal = true } = props;
const [hovered, setHovered] = useState(false);
const [clicked, setClicked] = useState(false);
const Portal = withPortal ? RadixDropdownMenu.Portal : Slot;
return (
<RadixDropdownMenu.Root
modal={false}
@@ -48,24 +57,27 @@ export function DropdownMenu(props: {
{button}
</RadixDropdownMenu.Trigger>
<RadixDropdownMenu.Content
data-testid="dropdown-menu"
hideWhenDetached
collisionPadding={8}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
align="start"
className="z-[9999] animate-present pt-2"
>
<div
className={tcls(
'flex max-h-80 min-w-40 max-w-[40vw] flex-col gap-1 overflow-auto rounded-lg straight-corners:rounded-sm bg-tint-base p-2 shadow-lg ring-1 ring-tint-subtle sm:min-w-52 sm:max-w-80',
className
)}
<Portal>
<RadixDropdownMenu.Content
data-testid="dropdown-menu"
hideWhenDetached
collisionPadding={8}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
align="start"
sideOffset={8}
className="z-40 animate-present"
>
{children}
</div>
</RadixDropdownMenu.Content>
<div
className={tcls(
'flex max-h-80 min-w-40 max-w-[40vw] flex-col gap-1 overflow-auto rounded-lg straight-corners:rounded-sm bg-tint-base p-2 shadow-lg ring-1 ring-tint-subtle sm:min-w-52 sm:max-w-80',
className
)}
>
{children}
</div>
</RadixDropdownMenu.Content>
</Portal>
</RadixDropdownMenu.Root>
);
}
@@ -77,7 +77,6 @@ export function Header(props: { context: GitBookSiteContext; withTopHeader?: boo
)}
>
<HeaderMobileMenuButton
pages={context.pages}
className={tcls(
'lg:hidden',
'-ml-2',
@@ -6,18 +6,14 @@ import { useEffect } from 'react';
import { useMobileMenuSheet } from '@/components/Header/mobile-menu/useMobileMenuSheet';
import { tString, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
import type { GitBookSiteContext } from '@v2/lib/context';
import { usePathname } from 'next/navigation';
/**
* Button to show/hide the table of content on mobile.
*/
export function HeaderMobileMenuButton({
pages,
...props
}: Partial<React.ButtonHTMLAttributes<HTMLButtonElement>> & {
pages: GitBookSiteContext['pages'];
}) {
export function HeaderMobileMenuButton(
props: Partial<React.ButtonHTMLAttributes<HTMLButtonElement>>
) {
const language = useLanguage();
const pathname = usePathname();
const { open, setOpen } = useMobileMenuSheet();
@@ -26,7 +22,7 @@ export function HeaderMobileMenuButton({
setOpen(!open);
};
// Close the navigation when navigating to a page
// biome-ignore lint/correctness/useExhaustiveDependencies: Close the navigation when navigating to a page
useEffect(() => {
if (!open) return;
setOpen(false);
@@ -11,8 +11,9 @@ export function SpacesDropdown(props: {
siteSpace: SiteSpace;
siteSpaces: SiteSpace[];
className?: string;
withPortal?: boolean;
}) {
const { context, siteSpace, siteSpaces, className } = props;
const { context, siteSpace, siteSpaces, className, withPortal } = props;
return (
<DropdownMenu
@@ -20,6 +21,7 @@ export function SpacesDropdown(props: {
'group-hover/dropdown:invisible', // Prevent hover from opening the dropdown, as it's annoying in this context
'group-focus-within/dropdown:group-hover/dropdown:visible' // When the dropdown is already open, it should remain visible when hovered
)}
withPortal={withPortal}
button={
<div
data-testid="space-dropdown-button"
@@ -9,7 +9,8 @@ import {
} from '@/components/utils/Sheet';
import { useMobileMenuSheet } from './useMobileMenuSheet';
export function MobileMenuSheet({ children }: { children: React.ReactNode }) {
export function MobileMenuSheet(props: { children: React.ReactNode }) {
const { children } = props;
const { open, setOpen } = useMobileMenuSheet();
return (
@@ -17,7 +18,7 @@ export function MobileMenuSheet({ children }: { children: React.ReactNode }) {
<SheetContent
aria-label="Mobile menu"
overlayClassName="lg:hidden"
className="lg:hidden"
className="pb-2 lg:hidden"
>
{/** Needed for screen readers */}
<SheetHeader className="sr-only">
@@ -73,13 +73,15 @@ export function SpaceLayout(props: {
innerHeader={
isMultiVariants ? (
<SpacesDropdown
withPortal={false}
context={context}
siteSpace={siteSpace}
siteSpaces={siteSpaces}
className={tcls(
'w-full',
'page-no-toc:hidden',
'site-header-none:page-no-toc:flex'
'site-header-none:page-no-toc:flex',
'h-8'
)}
/>
) : null
@@ -29,8 +29,8 @@ export function PageGroupItem(props: {
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
'[html.sidebar-filled.theme-muted_&]:bg-tint-base',
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
'[html.sidebar-default.theme-gradient_&]:bg-gradient-primary',
'[html.sidebar-default.theme-gradient.tint_&]:bg-gradient-tint'
'lg:[html.sidebar-default.theme-gradient_&]:bg-gradient-primary',
'lg:[html.sidebar-default.theme-gradient.tint_&]:bg-gradient-tint'
)}
>
<TOCPageIcon page={page} />
@@ -32,14 +32,14 @@ export function TOCScrollContent(props: {
)}
>
{!!innerHeader && (
<div className="inline-flex w-full flex-col gap-2 px-3 *:mt-0.5 max-lg:mt-3 max-lg:pr-12 lg:px-2 lg:pr-4">
<div className="inline-flex w-full flex-col gap-2 px-2 max-lg:mt-2 max-lg:pr-12 lg:pr-4 lg:*:mt-0.5">
{innerHeader}
</div>
)}
<TOCScrollContainer // The scrollview inside the sidebar
className={tcls(
'flex flex-grow flex-col p-3 lg:p-2',
'flex flex-grow flex-col p-2',
innerHeader ? 'mt-0' : 'mt-8',
customization.trademark.enabled && 'pb-20',
'gutter-stable overflow-y-auto',
@@ -24,14 +24,13 @@ export function Trademark(props: {
'z-[2]',
'absolute',
'lg:left-0',
'lg:bottom-0',
'left-2',
'right-2',
'bottom-2',
'bottom-0',
'lg:left-0',
'pt-2',
'lg:py-0',
'lg:pt-0',
'pointer-events-none',
'sidebar-filled:pl-2',
@@ -64,7 +64,7 @@ export function Button({
const sizes = {
default: ['text-base', 'py-2', 'circular-corners:px-6', iconOnly ? '!px-2' : 'px-5'],
medium: ['text-sm', 'py-1.5', 'circular-corners:px-4', iconOnly ? '!px-1.5' : 'px-3.5'],
small: ['text-xs', 'py-2', iconOnly ? 'px-2' : 'px-3'],
small: ['text-xs', 'py-2', iconOnly ? '!px-2' : 'px-3'],
};
const sizeClasses = sizes[size] || sizes.default;
+25 -11
View File
@@ -49,23 +49,38 @@ export function SheetContent({
side?: 'right' | 'left';
}) {
return (
<>
<SheetPortal>
<SheetOverlay className={overlayClassName} />
<SheetPrimitive.Content
data-slot="sheet-content"
className={tcls(
'fixed z-30 flex flex-col rounded-xl shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
'fixed',
'z-30',
'flex',
'flex-col',
'shadow-lg',
'depth-flat:shadow-none',
'transition ease-in-out',
'data-[state=closed]:duration-300 data-[state=open]:duration-500',
'border',
'border-tint-subtle',
'inset-x-1.5 inset-y-1.5 w-10/12 border sm:max-w-sm',
'inset-x-1.5',
'inset-y-1.5',
'w-10/12',
'sm:max-w-sm',
'bg-tint-base',
'theme-gradient:bg-gradient-primary',
'sidebar-filled:bg-tint-subtle',
'theme-muted:bg-tint-subtle',
'sidebar-filled:bg-tint-subtle',
'theme-muted:bg-tint-subtle',
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
'[html.sidebar-filled.theme-muted_&]:bg-tint-base',
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
'rounded-xl',
'circular-corners:rounded-2xl',
'straight-corners:rounded-none',
side === 'right' &&
@@ -80,18 +95,17 @@ export function SheetContent({
<SheetClose asChild>
<Button
data-slot="sheet-close"
variant="blank"
variant="secondary"
icon="close"
iconOnly
size="default"
className="absolute top-3 right-2 z-50"
autoFocus={false}
className="absolute top-2 right-2 z-50 bg-transparent text-tint opacity-8 shadow-none ring-transparent"
>
<span className="sr-only">Close</span>
</Button>
</SheetClose>
</SheetPrimitive.Content>
</>
</SheetPortal>
);
}