Skip to content

Commit

Permalink
fix: deeplinked object property keys scroll (#584)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Mar 28, 2024
1 parent 3197c98 commit 1fb02fb
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 16 deletions.
13 changes: 11 additions & 2 deletions packages/ui/app/src/api-page/endpoints/EndpointParameter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { APIV1Read } from "@fern-api/fdr-sdk";
import cn from "clsx";
import { useRouter } from "next/router";
import { FC, PropsWithChildren, ReactNode, useEffect, useState } from "react";
import { FC, PropsWithChildren, ReactNode, useEffect, useRef, useState } from "react";
import { AbsolutelyPositionedAnchor } from "../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../commons/monospace/MonospaceText";
import { SerializedMdxContent } from "../../mdx/mdx";
Expand Down Expand Up @@ -66,13 +66,22 @@ export const EndpointParameterContent: FC<PropsWithChildren<EndpointParameter.Co
const anchorRoute = `${route}#${anchorId}`;
const router = useRouter();
const [isActive, setIsActive] = useState(false);
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
setIsActive(router.asPath.endsWith(`${route}#${anchorId}`));
const active = router.asPath.endsWith(`${route}#${anchorId}`);
setIsActive(active);

if (active) {
setTimeout(() => {
ref.current?.scrollIntoView({ block: "start", behavior: "smooth" });
}, 450);
}
}, [router.asPath, anchorId, route]);

return (
<div
ref={ref}
data-route={anchorRoute.toLowerCase()}
className={cn("scroll-mt-header-height-padded relative flex flex-col gap-2 py-3", {
"before:outline-border-accent-muted before:outline-1 before:outline before:outline-offset-0 before:content-[''] before:inset-y-0 before:-inset-x-2 before:rounded-sm":
Expand Down
31 changes: 18 additions & 13 deletions packages/ui/app/src/api-page/types/object/ObjectProperty.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cn from "clsx";
import { useRouter } from "next/router";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { AbsolutelyPositionedAnchor } from "../../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../../commons/monospace/MonospaceText";
import { FernErrorBoundary } from "../../../components/FernErrorBoundary";
Expand Down Expand Up @@ -36,29 +36,29 @@ export const ObjectProperty: React.FC<ObjectProperty.Props> = (props) => {
const router = useRouter();
const anchorId = getAnchorId(anchorIdParts);
const [isActive, setIsActive] = useState(false);
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
setIsActive(router.asPath.endsWith(`${route}#${anchorId}`));
const active = router.asPath.endsWith(`${route}#${anchorId}`);
setIsActive(active);

if (active) {
setTimeout(() => {
ref.current?.scrollIntoView({ block: "start", behavior: "smooth" });
}, 450);
}
}, [router.asPath, anchorId, route]);

return <ObjectPropertyInternal anchorId={anchorId} isActive={isActive} {...props} />;
return <ObjectPropertyInternal ref={ref} anchorId={anchorId} isActive={isActive} {...props} />;
};

interface ObjectPropertyInternalProps extends ObjectProperty.Props {
anchorId: string;
isActive: boolean;
}

const ObjectPropertyInternal = memo<ObjectPropertyInternalProps>(function ObjectPropertyInternal({
route,
property,
applyErrorStyles,
defaultExpandAll,
types,
anchorIdParts,
anchorId,
isActive,
}) {
const UnmemoizedObjectPropertyInternal = forwardRef<HTMLDivElement, ObjectPropertyInternalProps>((props, ref) => {
const { route, property, applyErrorStyles, defaultExpandAll, types, anchorIdParts, anchorId, isActive } = props;
const contextValue = useTypeDefinitionContext();
const jsonPropertyPath = useMemo(
(): JsonPropertyPath => [
Expand Down Expand Up @@ -102,6 +102,7 @@ const ObjectPropertyInternal = memo<ObjectPropertyInternalProps>(function Object

return (
<div
ref={ref}
data-route={anchorRoute.toLowerCase()}
className={cn("py-3 scroll-mt-header-height-padded space-y-2", {
"px-3": !contextValue.isRootTypeDefinition,
Expand Down Expand Up @@ -147,3 +148,7 @@ const ObjectPropertyInternal = memo<ObjectPropertyInternalProps>(function Object
</div>
);
});

UnmemoizedObjectPropertyInternal.displayName = "UnmemoizedObjectPropertyInternal";

export const ObjectPropertyInternal = memo(UnmemoizedObjectPropertyInternal);
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ function startScrollTracking(route: string, scrolledHere: boolean = false) {
userHasScrolled = scrolledHere;
let lastActiveNavigatableOffsetTop: number | undefined;
lastScrollY = window.scrollY;
let lastNode: HTMLElement | undefined;
function handleObservation() {
const { node } = getRouteNodeWithAnchor(route);
if (node != null) {
if (lastNode !== node) {
lastActiveNavigatableOffsetTop = undefined;
}
lastNode = node;
if (lastActiveNavigatableOffsetTop == null && !userHasScrolled) {
node.scrollIntoView({ behavior: "auto" });
}
Expand Down Expand Up @@ -103,7 +108,7 @@ export const NavigationContextProvider: React.FC<NavigationContextProvider.Props
),
);

const [, anchor] = resolvedPath.fullSlug.split("#");
const [, anchor] = router.asPath.split("#");
const selectedSlug = activeNavigatable?.slug.join("/") ?? "";
const resolvedRoute = `/${selectedSlug}${anchor != null ? `#${anchor}` : ""}`;

Expand Down

0 comments on commit 1fb02fb

Please sign in to comment.