tippyjs-react
tippyjs-react copied to clipboard
Accept an array of refs for the `reference` prop
Similarly to the triggerTarget prop of Tippy.js, the reference prop should accept React.RefObject[] | Element[] parameters as well, to avoid having to do state-based ref management like below:
import Tippy, { TippyProps } from "@tippyjs/react";
import clsx from "clsx";
import { useMemo, useState } from "react";
export type FileInputProps = Omit<
React.ComponentPropsWithoutRef<"input">,
"type" | "children"
> &
Pick<TippyProps, "children">;
export function FileInput({
className,
children,
...restProps
}: FileInputProps) {
const [labelElement, setLabelElement] = useState<HTMLLabelElement | null>(
null,
);
const [inputElement, setinputElement] = useState<HTMLInputElement | null>(
null,
);
return (
<label
ref={(instance) => {
setLabelElement(instance);
}}
className={clsx("focus-within:ring", className)}
>
<Tippy
content="Upload file"
triggerTarget={useMemo(
() =>
labelElement && inputElement
? [labelElement, inputElement]
: undefined,
[inputElement, labelElement],
)}
>
{children}
</Tippy>
<input
ref={(instance) => {
setinputElement(instance);
}}
type="file"
className="sr-only"
{...restProps}
/>
</label>
);
}
And simplify the code as follows:
import Tippy, { TippyProps } from "@tippyjs/react";
import clsx from "clsx";
import { useMemo, useRef } from "react";
export type FileInputProps = Omit<
React.ComponentPropsWithoutRef<"input">,
"type" | "children"
> &
Pick<TippyProps, "children">;
export function FileInput({
className,
children,
...restProps
}: FileInputProps) {
const labelElement = useRef<HTMLLabelElement | null>(null);
const inputElement = useRef<HTMLInputElement | null>(null);
return (
<label ref={labelElement} className={clsx("focus-within:ring", className)}>
<Tippy
content="Upload file"
triggerTarget={useMemo(
() => [labelElement.current, inputElement.current],
[],
)}
>
{children}
</Tippy>
<input
ref={inputElement}
type="file"
className="sr-only"
{...restProps}
/>
</label>
);
}
Also allow null values as triggerTarget list elements implicitly.
const Demo = () => {
const [checkboxRef, setCheckboxRef] = useState<Element | null>(null);
const checkboxRefCallback = useCallback((ref) => setCheckboxRef(ref), []);
const [labelRef, setLabeRef] = useState<Element | null>(null);
const labelRefCallback = useCallback((ref) => setLabeRef(ref), []);
return (
<Tooltip triggerTarget={[checkboxRef, labelRef]}>
<label>
<input type="checkbox" ref={checkboxRefCallback} />
<span ref={labelRefCallback}>labeltext</span>
</label>
</Tooltip>
);
};