Merge pull request #791 from acelaya-forks/feature/fix-ref-types

Improved types on element ref objects and their usage
This commit is contained in:
Alejandro Celaya 2023-01-10 20:11:36 +01:00 committed by GitHub
commit aac2832eb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 26 additions and 31 deletions

View file

@ -1,4 +1,4 @@
import { FC, useEffect, useRef, useState } from 'react';
import { FC, useEffect, useState } from 'react';
import { UncontrolledTooltip } from 'reactstrap';
import { ExternalLink } from 'react-external-link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@ -8,8 +8,8 @@ import {
faCircleNotch as loadingStatusIcon,
} from '@fortawesome/free-solid-svg-icons';
import { MediaMatcher } from '../../utils/types';
import { mutableRefToElementRef } from '../../utils/helpers/components';
import { DomainStatus } from '../data';
import { useElementRef } from '../../utils/helpers/hooks';
interface DomainStatusIconProps {
status: DomainStatus;
@ -17,7 +17,7 @@ interface DomainStatusIconProps {
}
export const DomainStatusIcon: FC<DomainStatusIconProps> = ({ status, matchMedia = window.matchMedia }) => {
const ref = useRef<HTMLSpanElement>();
const ref = useElementRef<HTMLSpanElement>();
const matchesMobile = () => matchMedia('(max-width: 991px)').matches;
const [isMobile, setIsMobile] = useState<boolean>(matchesMobile());
@ -35,13 +35,13 @@ export const DomainStatusIcon: FC<DomainStatusIconProps> = ({ status, matchMedia
return (
<>
<span ref={mutableRefToElementRef(ref)}>
<span ref={ref}>
{status === 'valid'
? <FontAwesomeIcon fixedWidth icon={checkIcon} className="text-muted" />
: <FontAwesomeIcon fixedWidth icon={invalidIcon} className="text-danger" />}
</span>
<UncontrolledTooltip
target={(() => ref.current) as any}
target={ref}
placement={isMobile ? 'top-start' : 'left'}
autohide={status === 'valid'}
>

View file

@ -1,10 +1,9 @@
import { useRef, ChangeEvent, useState, useEffect, FC, PropsWithChildren } from 'react';
import { ChangeEvent, useState, useEffect, FC, PropsWithChildren } from 'react';
import { Button, UncontrolledTooltip } from 'reactstrap';
import { complement, pipe } from 'ramda';
import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useToggle } from '../../utils/helpers/hooks';
import { mutableRefToElementRef } from '../../utils/helpers/components';
import { useElementRef, useToggle } from '../../utils/helpers/hooks';
import { ServersImporter } from '../services/ServersImporter';
import { ServerData, ServersMap } from '../data';
import { DuplicatedServersModal } from './DuplicatedServersModal';
@ -34,7 +33,7 @@ export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC
tooltipPlacement = 'bottom',
className = '',
}) => {
const ref = useRef<HTMLInputElement>();
const ref = useElementRef<HTMLInputElement>();
const [serversToCreate, setServersToCreate] = useState<ServerData[] | undefined>();
const [duplicatedServers, setDuplicatedServers] = useState<ServerData[]>([]);
const [isModalOpen,, showModal, hideModal] = useToggle();
@ -79,7 +78,7 @@ export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC
type="file"
accept="text/csv"
className="import-servers-btn__csv-select"
ref={mutableRefToElementRef(ref)}
ref={ref}
onChange={onFile}
/>

View file

@ -1,12 +1,12 @@
import { FC, ReactNode, useRef } from 'react';
import { FC, ReactNode } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
import { faLinkSlash, faCalendarXmark, faCheck } from '@fortawesome/free-solid-svg-icons';
import { UncontrolledTooltip } from 'reactstrap';
import { isBefore } from 'date-fns';
import { mutableRefToElementRef } from '../../utils/helpers/components';
import { ShortUrl } from '../data';
import { formatHumanFriendly, now, parseISO } from '../../utils/helpers/date';
import { useElementRef } from '../../utils/helpers/hooks';
interface ShortUrlStatusProps {
shortUrl: ShortUrl;
@ -70,15 +70,15 @@ const resolveShortUrlStatus = (shortUrl: ShortUrl): StatusResult => {
};
export const ShortUrlStatus: FC<ShortUrlStatusProps> = ({ shortUrl }) => {
const tooltipRef = useRef<HTMLElement | undefined>();
const tooltipRef = useElementRef<HTMLElement>();
const { icon, className, description } = resolveShortUrlStatus(shortUrl);
return (
<>
<span style={{ cursor: !description ? undefined : 'help' }} ref={mutableRefToElementRef(tooltipRef)}>
<span style={{ cursor: !description ? undefined : 'help' }} ref={tooltipRef}>
<FontAwesomeIcon icon={icon} className={className} />
</span>
<UncontrolledTooltip target={(() => tooltipRef.current) as any} placement="bottom">
<UncontrolledTooltip target={tooltipRef} placement="bottom">
{description}
</UncontrolledTooltip>
</>

View file

@ -1,4 +1,3 @@
import { useRef } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons';
import { UncontrolledTooltip } from 'reactstrap';
@ -7,8 +6,8 @@ import { prettify } from '../../utils/helpers/numbers';
import { ShortUrl } from '../data';
import { SelectedServer } from '../../servers/data';
import { ShortUrlDetailLink } from './ShortUrlDetailLink';
import { mutableRefToElementRef } from '../../utils/helpers/components';
import { formatHumanFriendly, parseISO } from '../../utils/helpers/date';
import { useElementRef } from '../../utils/helpers/hooks';
import './ShortUrlVisitsCount.scss';
interface ShortUrlVisitsCountProps {
@ -37,20 +36,20 @@ export const ShortUrlVisitsCount = (
return visitsLink;
}
const tooltipRef = useRef<HTMLElement | undefined>();
const tooltipRef = useElementRef<HTMLElement>();
return (
<>
<span className="indivisible">
{visitsLink}
<small className="short-urls-visits-count__max-visits-control" ref={mutableRefToElementRef(tooltipRef)}>
<small className="short-urls-visits-count__max-visits-control" ref={tooltipRef}>
{maxVisits && <> / {prettify(maxVisits)}</>}
<sup className="ms-1">
<FontAwesomeIcon icon={infoIcon} />
</sup>
</small>
</span>
<UncontrolledTooltip target={(() => tooltipRef.current) as any} placement="bottom">
<UncontrolledTooltip target={tooltipRef} placement="bottom">
<ul className="list-unstyled mb-0">
{maxVisits && (
<li className="short-url-visits-count__tooltip-list-item">

View file

@ -1,9 +1,9 @@
import { FC, PropsWithChildren, useRef } from 'react';
import { FC, PropsWithChildren } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons';
import { UncontrolledTooltip } from 'reactstrap';
import { Placement } from '@popperjs/core';
import { mutableRefToElementRef } from './helpers/components';
import { useElementRef } from './helpers/hooks';
export type InfoTooltipProps = PropsWithChildren<{
className?: string;
@ -11,14 +11,14 @@ export type InfoTooltipProps = PropsWithChildren<{
}>;
export const InfoTooltip: FC<InfoTooltipProps> = ({ className = '', placement, children }) => {
const ref = useRef<HTMLSpanElement | undefined>();
const ref = useElementRef<HTMLSpanElement>();
return (
<>
<span className={className} ref={mutableRefToElementRef(ref)}>
<span className={className} ref={ref}>
<FontAwesomeIcon icon={infoIcon} />
</span>
<UncontrolledTooltip target={(() => ref.current) as any} placement={placement}>{children}</UncontrolledTooltip>
<UncontrolledTooltip target={ref} placement={placement}>{children}</UncontrolledTooltip>
</>
);
};

View file

@ -1,5 +0,0 @@
import { MutableRefObject, Ref } from 'react';
export const mutableRefToElementRef = <T>(ref: MutableRefObject<T | undefined>): Ref<T> => (el) => {
ref.current = el ?? undefined; // eslint-disable-line no-param-reassign
};

View file

@ -1,4 +1,4 @@
import { useState, useRef, EffectCallback, DependencyList, useEffect } from 'react';
import { DependencyList, EffectCallback, useEffect, useRef, useState } from 'react';
import { useSwipeable as useReactSwipeable } from 'react-swipeable';
import { useLocation, useNavigate } from 'react-router-dom';
import { v4 as uuid } from 'uuid';
@ -91,3 +91,5 @@ export const useDomId = (): string => {
const { current: id } = useRef(`dom-${uuid()}`);
return id;
};
export const useElementRef = <T>() => useRef<T | null>(null);