162 lines
3.8 KiB
TypeScript
162 lines
3.8 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
import styled, { AnyStyledComponent } from 'styled-components';
|
|
import { Content, Portal, Provider, Root, Trigger, Arrow } from '@radix-ui/react-tooltip';
|
|
|
|
import { STRING_KEYS } from '@/constants/localization';
|
|
import { tooltipStrings } from '@/constants/tooltips';
|
|
|
|
import { useStringGetter, useURLConfigs } from '@/hooks';
|
|
|
|
import { Icon, IconName } from '@/components/Icon';
|
|
import { Link } from '@/components/Link';
|
|
|
|
import { layoutMixins } from '@/styles/layoutMixins';
|
|
import { popoverMixins } from '@/styles/popoverMixins';
|
|
|
|
type ElementProps = {
|
|
tooltip?: keyof typeof tooltipStrings;
|
|
tooltipString?: string;
|
|
stringParams?: Record<string, string>;
|
|
withIcon?: boolean;
|
|
children?: ReactNode;
|
|
slotTooltip?: ReactNode;
|
|
};
|
|
|
|
type StyleProps = {
|
|
align?: 'start' | 'center' | 'end';
|
|
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
className?: string;
|
|
};
|
|
|
|
export const WithTooltip = ({
|
|
tooltip,
|
|
tooltipString,
|
|
stringParams,
|
|
withIcon,
|
|
children,
|
|
align,
|
|
side,
|
|
className,
|
|
slotTooltip,
|
|
}: ElementProps & StyleProps) => {
|
|
const stringGetter = useStringGetter();
|
|
const urlConfigs = useURLConfigs();
|
|
|
|
const getTooltipStrings = tooltip && tooltipStrings[tooltip];
|
|
if (!getTooltipStrings && !tooltipString && !slotTooltip) return <>{children}</>;
|
|
|
|
let tooltipTitle;
|
|
let tooltipBody;
|
|
let tooltipLearnMore;
|
|
|
|
if (getTooltipStrings) {
|
|
const { title, body, learnMoreLink } = getTooltipStrings({
|
|
stringGetter,
|
|
stringParams,
|
|
urlConfigs,
|
|
});
|
|
tooltipTitle = title;
|
|
tooltipBody = body;
|
|
tooltipLearnMore = learnMoreLink;
|
|
} else {
|
|
tooltipBody = tooltipString;
|
|
}
|
|
|
|
return (
|
|
<Provider>
|
|
<Root delayDuration={300}>
|
|
<Trigger asChild>
|
|
<Styled.Abbr>
|
|
{children}
|
|
{withIcon && <Styled.Icon iconName={IconName.HelpCircle} />}
|
|
</Styled.Abbr>
|
|
</Trigger>
|
|
|
|
<Portal>
|
|
<Styled.Content sideOffset={8} side={side} align={align} className={className} asChild>
|
|
{slotTooltip ?? (
|
|
<dl>
|
|
{tooltipTitle && <dt>{tooltipTitle}</dt>}
|
|
{tooltipBody && <dd>{tooltipBody}</dd>}
|
|
{tooltipLearnMore && (
|
|
<dd>
|
|
<Styled.LearnMore href={tooltipLearnMore}>
|
|
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
|
</Styled.LearnMore>
|
|
</dd>
|
|
)}
|
|
<Styled.Arrow />
|
|
</dl>
|
|
)}
|
|
</Styled.Content>
|
|
</Portal>
|
|
</Root>
|
|
</Provider>
|
|
);
|
|
};
|
|
|
|
const Styled: Record<string, AnyStyledComponent> = {};
|
|
|
|
Styled.Abbr = styled.abbr`
|
|
${layoutMixins.inlineRow}
|
|
|
|
text-decoration: underline dashed 0px;
|
|
text-underline-position: under;
|
|
text-decoration-color: var(--color-text-0);
|
|
text-decoration-skip-ink: all;
|
|
|
|
cursor: help;
|
|
`;
|
|
|
|
Styled.Content = styled(Content)`
|
|
--tooltip-backgroundColor: var(--color-layer-4);
|
|
--tooltip-backgroundColor: hsl(
|
|
var(--layer-base-hue),
|
|
var(--layer-base-saturation),
|
|
calc(var(--layer-base-lightness) + 4%),
|
|
0.66
|
|
);
|
|
|
|
${popoverMixins.popover}
|
|
--popover-backgroundColor: var(--tooltip-backgroundColor);
|
|
--popover-textColor: var(--color-text-1);
|
|
|
|
${popoverMixins.popoverAnimation}
|
|
--popover-closed-height: auto;
|
|
|
|
max-width: 30ch;
|
|
display: grid;
|
|
align-items: end;
|
|
gap: 0.25rem;
|
|
padding: 0.75em;
|
|
|
|
font-size: 0.8125em;
|
|
|
|
border-radius: 0.33em;
|
|
|
|
dt {
|
|
font: var(--font-small-book);
|
|
}
|
|
|
|
dd {
|
|
font: var(--font-mini-book);
|
|
}
|
|
`;
|
|
|
|
Styled.Arrow = styled(Arrow)`
|
|
width: 0.75rem;
|
|
height: 0.375rem;
|
|
|
|
polygon {
|
|
fill: var(--tooltip-backgroundColor);
|
|
}
|
|
`;
|
|
|
|
Styled.Icon = styled(Icon)`
|
|
color: var(--color-text-0);
|
|
`;
|
|
|
|
Styled.LearnMore = styled(Link)`
|
|
--link-color: var(--color-accent);
|
|
`;
|