tooltip rip and replace

This commit is contained in:
Nate Kelley 2025-03-01 22:49:10 -07:00
parent 37a456d665
commit 098028ea63
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
17 changed files with 42 additions and 264 deletions

View File

@ -64,7 +64,7 @@ export const BigQueryForm: React.FC<{
<AppSelectTagInput className="w-full" tokenSeparators={[',']} suffixIcon={null} />
</Form.Item>
<Form.Item name="credentials_json" label="Credentials">
<AppTooltip title={isValidJson ? '' : 'Invalid JSON'} forceShow>
<AppTooltip title={isValidJson ? '' : 'Invalid JSON'}>
<div
className="h-[180px] w-full"
style={{

View File

@ -47,7 +47,7 @@ export const ShareMenu: React.FC<
/>
) : null
}>
<AppTooltip performant={false} title={!isOpen ? 'Share item' : ''}>
<AppTooltip title={!isOpen ? 'Share item' : ''}>
<div className="flex">{children}</div>
</AppTooltip>
</AppPopover>

View File

@ -63,7 +63,7 @@ export const FavoriteStar: React.FC<{
const tooltipText = isFavorited ? 'Remove from favorites' : 'Add to favorites';
return (
<AppTooltip title={tooltipText} performant key={tooltipText}>
<AppTooltip title={tooltipText} key={tooltipText}>
<Button
classNames={{
icon: 'text-inherit! mt-[-2px]!'

View File

@ -162,7 +162,7 @@ export const StatusBadgeIndicator: React.FC<{
: {};
return (
<AppTooltip title={showTooltip && isHovering ? tooltipText : ''} mouseEnterDelay={0.25}>
<AppTooltip title={showTooltip && isHovering ? tooltipText : ''}>
<div
{...mouseEvents}
className={`rounded-full ${className} ${sharedClass} ${isNotVerified ? '' : ''}`}

View File

@ -1,6 +1,7 @@
import { ErrorCard } from '@/components/ui/error/ErrorCard';
import { ReactNode } from 'react';
import { ErrorBoundary } from '../error/ErrorBoundary';
import React from 'react';
interface Props {
children: ReactNode;

View File

@ -1,127 +0,0 @@
import * as antColors from '@ant-design/colors';
import React, { useEffect, useState } from 'react';
import { Button, ColorPicker, Modal } from 'antd';
import { useMemoizedFn } from 'ahooks';
import pick from 'lodash/pick';
import shuffle from 'lodash/shuffle';
import { AppTooltip } from '../tooltip';
import { AppMaterialIcons } from '../icons';
import { Text } from '@/components/ui';
export const NewColorThemeModal: React.FC<{
open: boolean;
onClose: () => void;
modalTheme: string[] | null;
onEditTheme: (theme: string[]) => Promise<void>;
onCreateNewTheme: (theme: string[]) => Promise<void>;
}> = ({ open, onCreateNewTheme, modalTheme, onEditTheme, onClose }) => {
const onRandomize = useMemoizedFn((): string[] => {
const selectedPallets = pick(antColors, [
'blue',
'red',
'green',
'purple',
'gold',
'orange',
'volcano',
'magenta'
]);
const randomColors = Object.values(selectedPallets).reduce((acc, curr) => {
const middleColors = curr.slice(2, 8);
const randomColor = middleColors[Math.floor(Math.random() * middleColors.length)]!;
return [...acc, randomColor];
}, [] as string[]);
const allColors = Object.values(selectedPallets).flatMap((p) => p.slice(1, 8));
const numberOfColors = 10;
const numberOfColorsShort = numberOfColors - randomColors.length;
for (let i = 0; i < numberOfColorsShort; i++) {
const randomColor = allColors[Math.floor(Math.random() * allColors.length)]!;
randomColors.push(randomColor);
}
const shuffleColors = shuffle(randomColors);
return shuffleColors;
});
const [theme, setTheme] = useState<string[]>([]);
const [submitting, setSubmitting] = useState(false);
const onSubmitPreflight = async () => {
setSubmitting(true);
if (modalTheme) {
await onEditTheme(theme);
} else {
await onCreateNewTheme(theme);
}
setSubmitting(false);
};
useEffect(() => {
if (open && modalTheme && modalTheme.length > 0) {
setTheme(modalTheme);
} else if (open) {
setTheme(onRandomize());
}
}, [open]);
return (
<Modal
destroyOnClose
open={open}
footer={[
<div key="footer" className="flex w-full justify-between">
<AppTooltip title={'Generate Palette'} trigger={['hover']}>
<Button
onClick={() => {
setTheme(onRandomize());
}}
icon={<AppMaterialIcons icon="science" />}></Button>
</AppTooltip>
<Button
loading={submitting}
onClick={async () => {
onSubmitPreflight();
}}>
{modalTheme ? 'Save theme' : 'Create theme'}
</Button>
</div>
]}
onCancel={onClose}
width={730}
title="Create theme">
<Text className="">
It is recommended to choose a color pallete with a wide range of colors as it will be used
for charts.
</Text>
<div className="mt-5 flex">
{theme?.map((color, index) => {
return (
<ColorPicker
key={index}
value={color}
disabledAlpha
destroyTooltipOnHide
onChange={(v) => {
const newTheme = [...theme];
newTheme[index] = v.toHexString();
setTheme(newTheme);
}}>
<div
onClick={() => {}}
className="h-full min-h-[118px] w-full cursor-pointer transition first:rounded-l last:rounded-r hover:z-10 hover:scale-105 hover:shadow-lg"
style={{ backgroundColor: color }}></div>
</ColorPicker>
);
})}
</div>
</Modal>
);
};
export default NewColorThemeModal;

View File

@ -68,13 +68,7 @@ const TextWrapper: React.FC<
const title = tooltip ? (typeof tooltip === 'string' ? tooltip : (children as string)) : '';
return tooltip ? (
<AppTooltip performant title={title}>
{children}
</AppTooltip>
) : (
<>{children}</>
);
return tooltip ? <AppTooltip title={title}>{children}</AppTooltip> : <>{children}</>;
});
TextWrapper.displayName = 'TextWrapper';

View File

@ -1,10 +1,9 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { AppPopover } from './AppPopover';
import { useClickAway, useMemoizedFn } from 'ahooks';
import { useMemoizedFn } from 'ahooks';
import isString from 'lodash/isString';
import { AppMaterialIcons } from '../icons';
import { Button, PopoverProps } from 'antd';
import { ButtonProps } from 'antd/lib';
import { PopoverProps } from 'antd';
import { createStyles } from 'antd-style';
import { useAntToken } from '@/styles/useAntToken';
@ -109,7 +108,7 @@ export const AppPopoverOptions: React.FC<{
styles.item,
rowClass,
`${option.selected ? selectedClass : ''}`,
`flex select-none items-center justify-between space-x-2 rounded-sm p-1`
`flex items-center justify-between space-x-2 rounded-sm p-1 select-none`
)}
onClick={() => {
option.onClick();
@ -126,9 +125,9 @@ export const AppPopoverOptions: React.FC<{
</div>
<div
className={`flex flex-col space-y-0.5 ${option.description ? 'justify-start' : 'justify-center'}`}>
<div className="select-none text-base">{option.label}</div>
<div className="text-base select-none">{option.label}</div>
{option.description && (
<div className="select-none text-sm">{option.description}</div>
<div className="text-sm select-none">{option.description}</div>
)}
</div>
</div>
@ -142,7 +141,7 @@ export const AppPopoverOptions: React.FC<{
))}
</div>
{footer && <div className={cx('pb-2 pt-2', styles.footer)}>{footer}</div>}
{footer && <div className={cx('pt-2 pb-2', styles.footer)}>{footer}</div>}
</div>
}>
<div

View File

@ -1,88 +0,0 @@
'use client';
import React, { PropsWithChildren, useMemo, useState } from 'react';
import { Tooltip } from 'antd';
import type { TooltipProps } from 'antd';
import { KeyboardShortcutPill } from '../pills/KeyboardShortcutPills';
export type AppTooltipProps = TooltipProps & {
shortcuts?: string[];
title?: string;
forceShow?: boolean;
performant?: boolean;
};
export const AppTooltip = React.memo<PropsWithChildren<AppTooltipProps>>(
({ children, ...props }) => {
const [isHovering, setIsHovering] = useState(false);
const { title, shortcuts = [], forceShow, performant } = props;
if (!title && !shortcuts.length && !forceShow) {
return <>{children}</>;
}
if (performant && !isHovering) {
return <PerformanceTooltip setIsHovering={setIsHovering}>{children}</PerformanceTooltip>;
}
return <BusterTooltip {...props}>{children}</BusterTooltip>;
}
);
AppTooltip.displayName = 'AppTooltip';
const PerformanceTooltip: React.FC<
PropsWithChildren<{
setIsHovering: (v: boolean) => void;
}>
> = React.memo(({ children, setIsHovering }) => {
return (
<div
className="performance-tooltip flex"
onMouseEnter={() => {
setIsHovering(true);
}}>
{children}
</div>
);
});
PerformanceTooltip.displayName = 'PerformanceTooltip';
const BusterTooltip: React.FC<PropsWithChildren<AppTooltipProps>> = ({
children,
mouseEnterDelay = 0.75,
mouseLeaveDelay = 0.15,
destroyTooltipOnHide = true,
trigger = 'hover',
arrow = false,
title = '',
forceShow = false,
performant = false,
shortcuts = [], //⌘
align,
...props
}) => {
const memoizedTitle = useMemo(() => {
if (title) {
return (
<div className="flex items-center space-x-1">
<span className="text-sm">{title}</span>
<KeyboardShortcutPill shortcut={shortcuts} />
</div>
);
}
return null;
}, [title, shortcuts]);
return (
<Tooltip
{...props}
title={memoizedTitle}
mouseEnterDelay={mouseEnterDelay}
trigger={trigger}
arrow={arrow}
mouseLeaveDelay={mouseLeaveDelay}>
{children}
</Tooltip>
);
};
BusterTooltip.displayName = 'BusterTooltip';

View File

@ -25,9 +25,9 @@ const meta = {
control: 'text',
description: 'The text content of the tooltip'
},
shortcut: {
shortcuts: {
control: 'object',
description: 'Array of keyboard shortcuts to display'
description: 'Array of keyboard shortcutss to display'
},
children: {
control: 'object',
@ -68,20 +68,20 @@ export const Basic: Story = {
}
};
// With keyboard shortcuts
// With keyboard shortcutss
export const WithShortcut: Story = {
args: {
title: 'Save file',
shortcut: ['S'],
shortcuts: ['S'],
children: <Button>Save</Button>
}
};
// Multiple shortcuts
// Multiple shortcutss
export const MultipleShortcuts: Story = {
args: {
title: 'Undo action',
shortcut: ['⌘', 'Z'],
shortcuts: ['⌘', 'Z'],
children: <Button>Undo</Button>
}
};

View File

@ -12,8 +12,8 @@ export interface TooltipProps
extends Pick<React.ComponentProps<typeof TooltipContentBase>, 'align' | 'side' | 'sideOffset'>,
Pick<React.ComponentProps<typeof TooltipProvider>, 'delayDuration' | 'skipDelayDuration'> {
children: React.ReactNode;
title: string;
shortcut?: string[];
title: string | undefined;
shortcuts?: string[];
open?: boolean;
}
@ -22,21 +22,21 @@ export const Tooltip = React.memo<TooltipProps>(
children,
title,
sideOffset,
shortcut,
shortcuts,
delayDuration = 0,
skipDelayDuration,
align,
side,
open
}) => {
if (!title && !shortcut?.length) return children;
if (!title || (!title && !shortcuts?.length)) return children;
return (
<TooltipProvider delayDuration={delayDuration} skipDelayDuration={skipDelayDuration}>
<TooltipBase open={open}>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContentBase align={align} side={side} sideOffset={sideOffset}>
<TooltipContent title={title} shortcut={shortcut} />
<TooltipContent title={title} shortcut={shortcuts} />
</TooltipContentBase>
</TooltipBase>
</TooltipProvider>

View File

@ -1,4 +1,7 @@
export * from './AppTooltip';
export * from './AppPopover';
export * from './AppPopoverMenu';
export * from './Tooltip';
import { Tooltip } from './Tooltip';
export { Tooltip as AppTooltip };

View File

@ -15,12 +15,10 @@ export const EditPieShowInnerLabel = React.memo(
return (
<LabelAndInput label="Show inner label">
<div className="flex w-full items-center justify-end space-x-2.5">
<AppTooltip mouseEnterDelay={0.25}>
<Switch
defaultChecked={pieShowInnerLabel}
onChange={(value) => onUpdateChartConfig({ pieShowInnerLabel: value })}
/>
</AppTooltip>
<Switch
defaultChecked={pieShowInnerLabel}
onChange={(value) => onUpdateChartConfig({ pieShowInnerLabel: value })}
/>
</div>
</LabelAndInput>
);

View File

@ -147,7 +147,7 @@ const ChartButton: React.FC<{
const { styles, cx } = useStyles();
return (
<AppTooltip title={tooltipText} performant mouseEnterDelay={0.75}>
<AppTooltip title={tooltipText} delayDuration={0.65}>
<div
key={id}
onClick={() => !disabled && onSelectChartType(id)}

View File

@ -50,7 +50,7 @@ export const DatasetList: React.FC<{
))}
<DropdownSelect datasets={selectedDatasets} onChange={onChange}>
<AppTooltip className="flex! items-center justify-center" title={'Add a dataset'}>
<AppTooltip title={'Add a dataset'}>
{selectedDatasets.length === 0 ? (
<DropdownEmptyButton />
) : (

View File

@ -1,18 +1,16 @@
import React from 'react';
import { AppMaterialIcons, AppTooltip, AppTooltipProps } from '@/components/ui';
import { Text } from '@/components/ui';
const memoizedTrigger: AppTooltipProps['trigger'] = ['click'];
import { AppTooltip } from '@/components/ui/tooltip';
import { CircleQuestion } from '@/components/ui/icons';
import { Text } from '@/components/ui/typography';
export const TermIndividualHeaderSider: React.FC = () => {
return (
<div className="flex h-full w-full items-center justify-between">
<Text>Details</Text>
<div className="flex h-full items-center">
<AppTooltip trigger={memoizedTrigger} title="Edit">
<Text className="flex h-full! cursor-pointer items-center">
<AppMaterialIcons size={18} icon="help" />
<AppTooltip title="Edit">
<Text className="flex h-full! cursor-pointer items-center text-lg">
<CircleQuestion />
</Text>
</AppTooltip>
</div>

View File

@ -34,8 +34,8 @@ export const SubmitButton: React.FC<SubmitButtonProps> = React.memo(
<AppTooltip
title={tooltipText}
shortcuts={tooltipShortcuts}
mouseEnterDelay={1.75}
mouseLeaveDelay={0}>
delayDuration={1.5}
skipDelayDuration={0}>
<motion.button
onClick={onSubmitPreflight}
disabled={disableSendButton}