diff --git a/packages/fern-docs/components/src/FernInput.tsx b/packages/fern-docs/components/src/FernInput.tsx index 0057ab1490..c71d8e16c9 100644 --- a/packages/fern-docs/components/src/FernInput.tsx +++ b/packages/fern-docs/components/src/FernInput.tsx @@ -59,7 +59,7 @@ export const FernInput = forwardRef<HTMLInputElement, FernInputProps>( <div className={cn("fern-input-group", className)}> {leftIcon && <span className="fern-input-icon">{leftIcon}</span>} <input - ref={composeRefs(forwardedRef, inputRef)} + ref={composeRefs(inputRef, forwardedRef)} {...props} className={cn("fern-input", inputClassName)} onChange={composeEventHandlers( diff --git a/packages/fern-docs/components/src/FernNumericInput.tsx b/packages/fern-docs/components/src/FernNumericInput.tsx index a92861676d..13a85b3177 100644 --- a/packages/fern-docs/components/src/FernNumericInput.tsx +++ b/packages/fern-docs/components/src/FernNumericInput.tsx @@ -1,4 +1,5 @@ import { useEventCallback } from "@fern-ui/react-commons"; +import { composeRefs } from "@radix-ui/react-compose-refs"; import cn from "clsx"; import { Minus, Plus } from "iconoir-react"; import { @@ -7,7 +8,6 @@ import { forwardRef, useCallback, useEffect, - useImperativeHandle, useRef, useState, } from "react"; @@ -35,11 +35,9 @@ export const FernNumericInput = forwardRef< disallowFloat, ...props }, - ref + forwardedRef ) { const inputRef = useRef<HTMLInputElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => inputRef.current!); const [internalValue, setInternalValue] = useState<number>(); const timeout = useRef<ReturnType<typeof setTimeout> | null>(null); @@ -127,7 +125,7 @@ export const FernNumericInput = forwardRef< /> )} <input - ref={inputRef} + ref={composeRefs(inputRef, forwardedRef)} type="number" className={cn("fern-input", inputClassName)} value={internalValue ?? value} diff --git a/packages/fern-docs/components/src/FernTextarea.tsx b/packages/fern-docs/components/src/FernTextarea.tsx index e4de0bf71e..46782ee017 100644 --- a/packages/fern-docs/components/src/FernTextarea.tsx +++ b/packages/fern-docs/components/src/FernTextarea.tsx @@ -1,11 +1,6 @@ +import { composeRefs } from "@radix-ui/react-compose-refs"; import cn from "clsx"; -import { - ComponentProps, - forwardRef, - useEffect, - useImperativeHandle, - useRef, -} from "react"; +import { ComponentProps, forwardRef, useEffect, useRef } from "react"; interface FernTextareaProps extends ComponentProps<"textarea"> { onValueChange?: (value: string) => void; @@ -15,12 +10,10 @@ interface FernTextareaProps extends ComponentProps<"textarea"> { export const FernTextarea = forwardRef<HTMLTextAreaElement, FernTextareaProps>( function FernTextarea( { className, onValueChange, minLines = 2, ...props }, - ref + forwardedRef ) { const inputRef = useRef<HTMLTextAreaElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => inputRef.current!); const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { onValueChange?.(e.target.value); props.onChange?.(e); @@ -30,7 +23,7 @@ export const FernTextarea = forwardRef<HTMLTextAreaElement, FernTextareaProps>( <div className={cn("fern-textarea-group", className)}> <textarea {...props} - ref={inputRef} + ref={composeRefs(inputRef, forwardedRef)} className="fern-textarea" onChange={handleChange} /> diff --git a/packages/fern-docs/search-ui/src/components/desktop/desktop-ask-ai.tsx b/packages/fern-docs/search-ui/src/components/desktop/desktop-ask-ai.tsx index ba5cb8f444..478f17e8bb 100644 --- a/packages/fern-docs/search-ui/src/components/desktop/desktop-ask-ai.tsx +++ b/packages/fern-docs/search-ui/src/components/desktop/desktop-ask-ai.tsx @@ -156,7 +156,7 @@ export const DesktopCommandWithAskAI = forwardRef< <DesktopCommandRoot label={askAI ? "Ask AI" : "Search"} {...props} - ref={composeRefs(forwardedRef, ref)} + ref={composeRefs(ref, forwardedRef)} shouldFilter={!askAI} disableAutoSelection={askAI} onPopState={ @@ -487,7 +487,7 @@ const AskAIComposer = forwardRef< > <DesktopCommandInput asChild> <TextArea - ref={composeRefs(forwardedRef, inputRef)} + ref={composeRefs(inputRef, forwardedRef)} autoFocus placeholder="Ask AI a question..." minLines={1} diff --git a/packages/fern-docs/search-ui/src/components/desktop/desktop-command-root.tsx b/packages/fern-docs/search-ui/src/components/desktop/desktop-command-root.tsx index 5d7a699fca..ea87b1e319 100644 --- a/packages/fern-docs/search-ui/src/components/desktop/desktop-command-root.tsx +++ b/packages/fern-docs/search-ui/src/components/desktop/desktop-command-root.tsx @@ -52,7 +52,7 @@ export const DesktopCommandRoot = forwardRef< > <Command.Root label="Search" - ref={composeRefs(forwardedRef, ref)} + ref={composeRefs(ref, forwardedRef)} {...props} id="fern-search-desktop-command" onKeyDown={composeEventHandlers( diff --git a/packages/fern-docs/search-ui/src/components/desktop/desktop-command.tsx b/packages/fern-docs/search-ui/src/components/desktop/desktop-command.tsx index 6bae49ce2b..0668ed0af1 100644 --- a/packages/fern-docs/search-ui/src/components/desktop/desktop-command.tsx +++ b/packages/fern-docs/search-ui/src/components/desktop/desktop-command.tsx @@ -65,7 +65,7 @@ const DesktopCommand = forwardRef< <DesktopCommandRoot label="Search" {...props} - ref={composeRefs(forwardedRef, ref)} + ref={composeRefs(ref, forwardedRef)} onPopState={composeEventHandlers(onPopState, handlePopFilters, { checkForDefaultPrevented: false, })} diff --git a/packages/fern-docs/search-ui/src/components/shared/command-link.tsx b/packages/fern-docs/search-ui/src/components/shared/command-link.tsx index fd98c8bc10..8bee5f9c5e 100644 --- a/packages/fern-docs/search-ui/src/components/shared/command-link.tsx +++ b/packages/fern-docs/search-ui/src/components/shared/command-link.tsx @@ -71,7 +71,7 @@ export const CommandLink = forwardRef< return ( <Command.Item {...props} value={href} asChild> <Comp - ref={composeRefs(forwardedRef, ref)} + ref={composeRefs(ref, forwardedRef)} href={href} target={target} rel={rel} diff --git a/packages/fern-docs/ui/src/api-reference/endpoints/EndpointUrl.tsx b/packages/fern-docs/ui/src/api-reference/endpoints/EndpointUrl.tsx index 122f547953..4415756a62 100644 --- a/packages/fern-docs/ui/src/api-reference/endpoints/EndpointUrl.tsx +++ b/packages/fern-docs/ui/src/api-reference/endpoints/EndpointUrl.tsx @@ -4,11 +4,11 @@ import visitDiscriminatedUnion from "@fern-api/ui-core-utils/visitDiscriminatedU import { CopyToClipboardButton } from "@fern-docs/components"; import { HttpMethodBadge } from "@fern-docs/components/badges"; import { useBooleanState } from "@fern-ui/react-commons"; +import { composeRefs } from "@radix-ui/react-compose-refs"; import cn from "clsx"; import React, { PropsWithChildren, ReactElement, - useImperativeHandle, useMemo, useRef, useState, @@ -44,13 +44,10 @@ export const EndpointUrl = React.forwardRef< showEnvironment, options, }, - parentRef + forwardedRef ) { const ref = useRef<HTMLDivElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(parentRef, () => ref.current!); - const [isHovered, setIsHovered] = useState(false); const isEditingEnvironment = useBooleanState(false); @@ -110,7 +107,10 @@ export const EndpointUrl = React.forwardRef< }, [options, environmentId, baseUrl]); return ( - <div ref={ref} className={cn("flex items-center gap-1 pr-2", className)}> + <div + ref={composeRefs(ref, forwardedRef)} + className={cn("flex items-center gap-1 pr-2", className)} + > <HttpMethodBadge method={method} /> <div className={cn("flex items-center")}> diff --git a/packages/fern-docs/ui/src/components/HorizontalOverflowMask.tsx b/packages/fern-docs/ui/src/components/HorizontalOverflowMask.tsx index b8f206c353..f0b37e3ee5 100644 --- a/packages/fern-docs/ui/src/components/HorizontalOverflowMask.tsx +++ b/packages/fern-docs/ui/src/components/HorizontalOverflowMask.tsx @@ -1,21 +1,14 @@ +import { composeRefs } from "@radix-ui/react-compose-refs"; import cn from "clsx"; import fastdom from "fastdom"; -import React, { - PropsWithChildren, - useEffect, - useImperativeHandle, - useRef, - useState, -} from "react"; +import React, { PropsWithChildren, useEffect, useRef, useState } from "react"; import { noop } from "ts-essentials"; export const HorizontalOverflowMask = React.forwardRef< HTMLDivElement, PropsWithChildren<{ className?: string }> ->(function HorizontalOverflowMask({ children, className }, parentRef) { +>(function HorizontalOverflowMask({ children, className }, forwardedRef) { const ref = useRef<HTMLDivElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(parentRef, () => ref.current!); const [showLeftMask, setShowLeftMask] = useState(false); const [hideRightMask, setHideRightMask] = useState(false); @@ -55,7 +48,7 @@ export const HorizontalOverflowMask = React.forwardRef< return ( <div - ref={ref} + ref={composeRefs(ref, forwardedRef)} className={cn( "fern-x-overflow", { diff --git a/packages/fern-docs/ui/src/header/Announcement.tsx b/packages/fern-docs/ui/src/header/Announcement.tsx index 8824c466d0..08f052abde 100644 --- a/packages/fern-docs/ui/src/header/Announcement.tsx +++ b/packages/fern-docs/ui/src/header/Announcement.tsx @@ -1,10 +1,11 @@ import { FernButton } from "@fern-docs/components"; import { useResizeObserver } from "@fern-ui/react-commons"; +import { composeRefs } from "@radix-ui/react-compose-refs"; import clsx from "clsx"; import { AnimatePresence, motion } from "framer-motion"; import { Xmark } from "iconoir-react"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; -import { ReactElement, forwardRef, useImperativeHandle, useRef } from "react"; +import { ReactElement, forwardRef, useRef } from "react"; import { ANNOUNCEMENT_CONFIG_ATOM, ANNOUNCEMENT_DISMISSED_ATOM, @@ -32,12 +33,9 @@ const AnnouncementInternal = forwardRef< } }); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(forwardedRef, () => ref.current!); - return ( <motion.div - ref={ref} + ref={composeRefs(ref, forwardedRef)} {...props} className={clsx("overflow-hidden", className)} > diff --git a/packages/fern-docs/ui/src/mdx/components/iframe/IFrame.tsx b/packages/fern-docs/ui/src/mdx/components/iframe/IFrame.tsx index ab52a74e1b..fee631dc6a 100644 --- a/packages/fern-docs/ui/src/mdx/components/iframe/IFrame.tsx +++ b/packages/fern-docs/ui/src/mdx/components/iframe/IFrame.tsx @@ -1,5 +1,6 @@ import { FernButton } from "@fern-docs/components"; import { usePrevious } from "@fern-ui/react-commons"; +import { composeRefs } from "@radix-ui/react-compose-refs"; import * as Tooltip from "@radix-ui/react-tooltip"; import { Expand } from "iconoir-react"; import { @@ -8,7 +9,6 @@ import { RefObject, forwardRef, useEffect, - useImperativeHandle, useRef, useState, } from "react"; @@ -29,11 +29,9 @@ export const IFrame = forwardRef<HTMLIFrameElement, IFrame.Props>( experimental_onReceiveMessage, ...props }, - ref + forwardedRef ): ReactElement => { const iframeRef = useRef<HTMLIFrameElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => iframeRef.current!); useEffect(() => { const contentWindow = iframeRef.current?.contentWindow; @@ -65,7 +63,13 @@ export const IFrame = forwardRef<HTMLIFrameElement, IFrame.Props>( } // prevent hydration mismatch by setting data-state to closed - return <iframe data-state="closed" ref={ref} {...props} />; + return ( + <iframe + data-state="closed" + ref={composeRefs(iframeRef, forwardedRef)} + {...props} + /> + ); } ); diff --git a/packages/fern-docs/ui/src/playground/endpoint/PlaygroundEndpointSelectorContent.tsx b/packages/fern-docs/ui/src/playground/endpoint/PlaygroundEndpointSelectorContent.tsx index 8709a00882..cf9185f2a9 100644 --- a/packages/fern-docs/ui/src/playground/endpoint/PlaygroundEndpointSelectorContent.tsx +++ b/packages/fern-docs/ui/src/playground/endpoint/PlaygroundEndpointSelectorContent.tsx @@ -8,14 +8,7 @@ import { } from "@fern-docs/components"; import cn, { clsx } from "clsx"; import { Search, Slash, Xmark } from "iconoir-react"; -import { - Fragment, - forwardRef, - useEffect, - useImperativeHandle, - useRef, - useState, -} from "react"; +import { Fragment, forwardRef, useEffect, useRef, useState } from "react"; import { BuiltWithFern } from "../../sidebar/BuiltWithFern"; import { ApiGroup } from "../utils/flatten-apis"; import { PlaygroundEndpointSelectorLeafNode } from "./PlaygroundEndpointSelectorLeafNode"; @@ -48,8 +41,6 @@ export const PlaygroundEndpointSelectorContent = forwardRef< PlaygroundEndpointSelectorContentProps >(({ apiGroups, closeDropdown, selectedEndpoint, className }, ref) => { const scrollRef = useRef<HTMLDivElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => scrollRef.current!); const [filterValue, setFilterValue] = useState<string>(""); diff --git a/packages/fern-docs/ui/src/search/algolia/SearchBox.tsx b/packages/fern-docs/ui/src/search/algolia/SearchBox.tsx index 81e28421c0..3587714337 100644 --- a/packages/fern-docs/ui/src/search/algolia/SearchBox.tsx +++ b/packages/fern-docs/ui/src/search/algolia/SearchBox.tsx @@ -1,6 +1,7 @@ import { getPlatform } from "@fern-api/ui-core-utils"; import { FernButton, FernInput } from "@fern-docs/components"; import { useKeyboardCommand, useKeyboardPress } from "@fern-ui/react-commons"; +import { composeRefs } from "@radix-ui/react-compose-refs"; import { Search, Xmark } from "iconoir-react"; import { atom, useSetAtom } from "jotai"; import { @@ -8,7 +9,6 @@ import { forwardRef, useCallback, useEffect, - useImperativeHandle, useRef, useState, } from "react"; @@ -26,7 +26,7 @@ export const SEARCH_BOX_MOUNTED = atom(false); export const SearchBox = forwardRef<HTMLInputElement, SearchBoxProps>( function SearchBox( { queryHook, className, inputClassName, placeholder }, - ref + forwardedRef ): ReactElement { const { query, refine } = useSearchBox({ queryHook }); const [inputValue, setInputValue] = useState(query); @@ -42,9 +42,6 @@ export const SearchBox = forwardRef<HTMLInputElement, SearchBoxProps>( }; }, [setMounted]); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => inputRef.current!); - const setQuery = useCallback( (newQuery: string) => { setInputValue(newQuery); @@ -113,7 +110,7 @@ export const SearchBox = forwardRef<HTMLInputElement, SearchBoxProps>( }} > <input - ref={inputRef} + ref={composeRefs(inputRef, forwardedRef)} className={inputClassName} autoComplete="off" autoCorrect="off" @@ -137,15 +134,12 @@ export const SearchBox = forwardRef<HTMLInputElement, SearchBoxProps>( export const SearchMobileBox = forwardRef<HTMLInputElement, SearchBoxProps>( function SearchBox( { queryHook, className, inputClassName, placeholder, onFocus }, - ref + forwardedRef ): ReactElement { const { query, refine } = useSearchBox({ queryHook }); const [inputValue, setInputValue] = useState(query); const inputRef = useRef<HTMLInputElement>(null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - useImperativeHandle(ref, () => inputRef.current!); - const setQuery = useCallback( (newQuery: string) => { setInputValue(newQuery); @@ -180,7 +174,7 @@ export const SearchMobileBox = forwardRef<HTMLInputElement, SearchBoxProps>( }} > <FernInput - ref={inputRef} + ref={composeRefs(inputRef, forwardedRef)} className={inputClassName} autoComplete="off" autoCorrect="off" diff --git a/packages/fern-docs/ui/src/sidebar/SidebarLink.tsx b/packages/fern-docs/ui/src/sidebar/SidebarLink.tsx index f151de9323..ee077933c4 100644 --- a/packages/fern-docs/ui/src/sidebar/SidebarLink.tsx +++ b/packages/fern-docs/ui/src/sidebar/SidebarLink.tsx @@ -65,7 +65,7 @@ type SidebarLinkProps = PropsWithChildren< >; const SidebarLinkInternal = forwardRef<HTMLDivElement, SidebarLinkProps>( - (props, forwardRef) => { + (props, forwardedRef) => { const { icon, className, @@ -171,7 +171,7 @@ const SidebarLinkInternal = forwardRef<HTMLDivElement, SidebarLinkProps>( return ( <div - ref={composeRefs(forwardRef, ref)} + ref={composeRefs(ref, forwardedRef)} className={cn("fern-sidebar-link-container", className)} data-state={selected ? "active" : "inactive"} > @@ -221,7 +221,7 @@ export const SidebarLink = memo(SidebarLinkInternal); export const SidebarSlugLink = forwardRef< HTMLDivElement, PropsWithChildren<SidebarSlugLinkProps> ->((props, forwardRef) => { +>((props, forwardedRef) => { const { slug, ...innerProps } = props; const ref = useRef<HTMLDivElement>(null); @@ -244,7 +244,7 @@ export const SidebarSlugLink = forwardRef< return ( <SidebarLink {...innerProps} - ref={composeRefs(forwardRef, ref)} + ref={composeRefs(ref, forwardedRef)} href={href} onClick={composeEventHandlers(innerProps.onClick, () => { if (href) {