Section groups visual bug fix (#2821)

This commit is contained in:
Brett Jephson
2025-02-07 15:53:51 +00:00
committed by GitHub
parent 9943276c0c
commit aaab157d49
2 changed files with 25 additions and 16 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': patch
---
Visual fix for section group in Safari
@@ -22,26 +22,30 @@ export function SiteSectionTabs(props: { sections: SectionsList }) {
} = props;
const [value, setValue] = React.useState<string | null>();
const [offset, setOffset] = React.useState<number | null>(null);
const scrollableViewRef = React.useRef<HTMLDivElement>(null);
const onNodeUpdate = (
trigger: HTMLButtonElement | null,
itemValue: string,
size: number = 0,
) => {
const windowWidth = window.innerWidth;
const windowWidth = document.documentElement.clientWidth;
if (windowWidth < 768) {
// if the screen is small don't offset the menu
setOffset(0);
} else if (trigger && value === itemValue) {
const padding = 16;
const viewportWidth =
size < MIN_ITEMS_FOR_COLS ? VIEWPORT_ITEM_WIDTH : VIEWPORT_ITEM_WIDTH * 2;
const halfViewportWidth = viewportWidth / 2;
const viewportFreeZone = 10 /* buffer */ + 8 /* padding */ + halfViewportWidth;
const triggerOffsetRight = trigger.offsetLeft + trigger.offsetWidth / 2;
size < MIN_ITEMS_FOR_COLS
? VIEWPORT_ITEM_WIDTH + padding
: VIEWPORT_ITEM_WIDTH * 2 + padding;
const scrollLeft = scrollableViewRef.current?.scrollLeft ?? 0;
const triggerOffset = trigger.offsetLeft - scrollLeft; // offset of the trigger from the left edge including scrolling
const bufferLeft = 2; // offset the menu viewport should not pass on the left side of window
const bufferRight = windowWidth - (16 + viewportWidth); // offset the menu viewport should not pass on the right side of the window
setOffset(
Math.min(
Math.max(viewportFreeZone, Math.round(triggerOffsetRight)),
windowWidth - viewportFreeZone,
),
// constrain to within the window with some buffer on the left and right we don't want the menu to enter
Math.min(bufferRight, Math.max(bufferLeft, Math.round(triggerOffset))),
);
} else if (!value) {
setOffset(null);
@@ -55,6 +59,7 @@ export function SiteSectionTabs(props: { sections: SectionsList }) {
className="w-full relative z-10 flex flex-nowrap items-center max-w-screen-2xl mx-auto page-full-width:max-w-full"
>
<div
ref={scrollableViewRef}
className="w-full hide-scroll overflow-x-scroll overflow-y-hidden pb-4 -mb-4" /* Positive padding / negative margin allows the navigation menu indicator to show in a scroll view */
>
<NavigationMenu.List className="center m-0 flex list-none bg-transparent px-1 sm:px-3 md:px-5 gap-2">
@@ -112,7 +117,7 @@ export function SiteSectionTabs(props: { sections: SectionsList }) {
);
})}
<NavigationMenu.Indicator
className="top-full z-0 flex h-3 items-end justify-center motion-safe:transition-[width,_transform] data-[state=hidden]:motion-safe:animate-fadeOut data-[state=visible]:motion-safe:animate-fadeIn"
className="top-full z-0 flex h-3 items-end justify-center duration-150 motion-safe:transition-[width,_transform] data-[state=hidden]:motion-safe:animate-fadeOut data-[state=visible]:motion-safe:animate-fadeIn"
aria-hidden
>
<div className="bg-tint shadow-1xs shadow-dark/1 dark:shadow-dark/4 relative top-[70%] size-3 rotate-[225deg] rounded-tl-sm" />
@@ -120,18 +125,17 @@ export function SiteSectionTabs(props: { sections: SectionsList }) {
</NavigationMenu.List>
</div>
<div
className="absolute mx-4 top-full flex transition-transform duration-200 ease-in-out"
className="absolute top-full flex transition-transform duration-200 ease-in-out"
style={{
display: offset === null ? 'none' : undefined,
transform: offset ? `translateX(${offset}px)` : undefined,
}}
>
<NavigationMenu.Viewport
className="bg-tint rounded straight-corners:rounded-none shadow-1xs shadow-dark/1 dark:shadow-dark/4 relative mt-3 w-[calc(100vw_-_2rem)] md:w-[var(--radix-navigation-menu-viewport-width)] h-[var(--radix-navigation-menu-viewport-height)] origin-[top_center] overflow-hidden motion-safe:transition-[width,_height,_transform] duration-300 data-[state=closed]:motion-safe:animate-scaleOut data-[state=open]:motion-safe:animate-scaleIn"
className="bg-tint rounded straight-corners:rounded-none shadow-1xs shadow-dark/1 dark:shadow-dark/4 relative mt-3 ml-4 md:mx-0 w-[calc(100vw_-_2rem)] md:w-[var(--radix-navigation-menu-viewport-width)] h-[var(--radix-navigation-menu-viewport-height)] origin-[top_center] overflow-hidden motion-safe:transition-[width,_height,_transform] duration-250 data-[state=closed]:duration-150 data-[state=closed]:motion-safe:animate-scaleOut data-[state=open]:motion-safe:animate-scaleIn"
style={{
translate: offset
? '-50% 0'
: undefined /* don't move this to a Tailwind class as Radix renders viewport incorrectly for a few frames */,
translate:
undefined /* don't move this to a Tailwind class as Radix renders viewport incorrectly for a few frames */,
}}
/>
</div>
@@ -254,7 +258,7 @@ function SectionGroupTile(props: { section: SiteSection; isActive: boolean }) {
{icon ? <SectionIcon isActive={false} icon={icon as IconName} /> : null}
<span className="truncate min-w-0">{title}</span>
</div>
<p className="text-tint-subtle min-h-[2lh]">{section.description}</p>
<p className="text-tint-subtle">{section.description}</p>
</Link>
</li>
);