mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console): improve tip components usage (#2608)
This commit is contained in:
parent
63cbd9a832
commit
6ae4919762
26 changed files with 257 additions and 279 deletions
|
@ -1,9 +1,9 @@
|
|||
import classNames from 'classnames';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Tooltip from '../Tooltip';
|
||||
import { Tooltip } from '../Tip';
|
||||
import Icon from './Icon';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -21,8 +21,6 @@ type Props = {
|
|||
const Checkbox = ({ value, onChange, label, disabled, className, disabledTooltip }: Props) => {
|
||||
const [id, setId] = useState(nanoid());
|
||||
|
||||
const tipRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.checkbox, className)}>
|
||||
<input
|
||||
|
@ -35,10 +33,11 @@ const Checkbox = ({ value, onChange, label, disabled, className, disabledTooltip
|
|||
}}
|
||||
/>
|
||||
{disabled && disabledTooltip && (
|
||||
<>
|
||||
<div ref={tipRef} className={styles.disabledMask} />
|
||||
<Tooltip anchorRef={tipRef} content={disabledTooltip} />
|
||||
</>
|
||||
<Tooltip
|
||||
horizontalAlign="start"
|
||||
anchorClassName={styles.disabledMask}
|
||||
content={disabledTooltip}
|
||||
/>
|
||||
)}
|
||||
<Icon className={styles.icon} />
|
||||
{label && <label htmlFor={id}>{label}</label>}
|
||||
|
|
|
@ -31,8 +31,11 @@
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.copyIconButton {
|
||||
.copyToolTipAnchor {
|
||||
margin-left: _.unit(3);
|
||||
}
|
||||
|
||||
.copyIconButton {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import Eye from '@/assets/images/eye.svg';
|
|||
import { onKeyDownHandler } from '@/utilities/a11y';
|
||||
|
||||
import IconButton from '../IconButton';
|
||||
import Tooltip from '../Tooltip';
|
||||
import { Tooltip } from '../Tip';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
|
@ -78,20 +78,20 @@ const CopyToClipboard = ({
|
|||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
<IconButton
|
||||
ref={copyIconReference}
|
||||
className={styles.copyIconButton}
|
||||
iconClassName={styles.copyIcon}
|
||||
onClick={copy}
|
||||
>
|
||||
<Copy />
|
||||
</IconButton>
|
||||
<Tooltip
|
||||
anchorRef={copyIconReference}
|
||||
content={t(copyState)}
|
||||
horizontalAlign="center"
|
||||
className={classNames(copyState === 'copied' && styles.successfulTooltip)}
|
||||
/>
|
||||
anchorClassName={styles.copyToolTipAnchor}
|
||||
content={t(copyState)}
|
||||
>
|
||||
<IconButton
|
||||
ref={copyIconReference}
|
||||
className={styles.copyIconButton}
|
||||
iconClassName={styles.copyIcon}
|
||||
onClick={copy}
|
||||
>
|
||||
<Copy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
}
|
||||
|
||||
.toggleTipButton {
|
||||
margin-left: _.unit(1);
|
||||
margin-left: _.unit(0.5);
|
||||
}
|
||||
|
||||
.required {
|
||||
|
|
|
@ -3,9 +3,12 @@ import classNames from 'classnames';
|
|||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Tip from '@/assets/images/tip.svg';
|
||||
|
||||
import type DangerousRaw from '../DangerousRaw';
|
||||
import IconButton from '../IconButton';
|
||||
import Spacer from '../Spacer';
|
||||
import ToggleTipButton from '../ToggleTipButton';
|
||||
import { ToggleTip } from '../Tip';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type Props = {
|
||||
|
@ -25,7 +28,11 @@ const FormField = ({ title, children, isRequired, className, tip, headlineClassN
|
|||
<div className={classNames(styles.headline, headlineClassName)}>
|
||||
<div className={styles.title}>{typeof title === 'string' ? t(title) : title}</div>
|
||||
{tip && (
|
||||
<ToggleTipButton className={styles.toggleTipButton} render={() => <div>{t(tip)}</div>} />
|
||||
<ToggleTip anchorClassName={styles.toggleTipButton} content={<div>{t(tip)}</div>}>
|
||||
<IconButton size="small">
|
||||
<Tip />
|
||||
</IconButton>
|
||||
</ToggleTip>
|
||||
)}
|
||||
<Spacer />
|
||||
{isRequired && <div className={styles.required}>{t('general.required')}</div>}
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
import classNames from 'classnames';
|
||||
import type { ForwardedRef, HTMLProps, ReactNode } from 'react';
|
||||
import type { ForwardedRef, HTMLProps } from 'react';
|
||||
import { forwardRef, useRef } from 'react';
|
||||
|
||||
import Tooltip from '../Tooltip';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type Props = Omit<HTMLProps<HTMLButtonElement>, 'size' | 'type'> & {
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
tooltip?: ReactNode;
|
||||
iconClassName?: string;
|
||||
};
|
||||
|
||||
const IconButton = (
|
||||
{ size = 'medium', children, className, tooltip, iconClassName, ...rest }: Props,
|
||||
{ size = 'medium', children, className, iconClassName, ...rest }: Props,
|
||||
reference: ForwardedRef<HTMLButtonElement>
|
||||
) => {
|
||||
const tipRef = useRef<HTMLDivElement>(null);
|
||||
|
@ -27,9 +25,6 @@ const IconButton = (
|
|||
<div ref={tipRef} className={classNames(styles.icon, iconClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
{tooltip && (
|
||||
<Tooltip anchorRef={tipRef} content={tooltip} position="top" horizontalAlign="center" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
104
packages/console/src/components/Tip/ToggleTip/index.tsx
Normal file
104
packages/console/src/components/Tip/ToggleTip/index.tsx
Normal file
|
@ -0,0 +1,104 @@
|
|||
import type { ReactNode } from 'react';
|
||||
import { useCallback, useState, useRef } from 'react';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import type { HorizontalAlignment } from '@/hooks/use-position';
|
||||
import usePosition from '@/hooks/use-position';
|
||||
import { onKeyDownHandler } from '@/utilities/a11y';
|
||||
|
||||
import type { TipBubblePosition } from '../TipBubble';
|
||||
import TipBubble from '../TipBubble';
|
||||
import {
|
||||
getVerticalAlignment,
|
||||
getHorizontalAlignment,
|
||||
getVerticalOffset,
|
||||
getHorizontalOffset,
|
||||
} from '../TipBubble/utils';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
anchorClassName?: string;
|
||||
position?: TipBubblePosition;
|
||||
horizontalAlign?: HorizontalAlignment;
|
||||
content?: ((closeTip: () => void) => ReactNode) | ReactNode;
|
||||
};
|
||||
|
||||
const ToggleTip = ({
|
||||
children,
|
||||
className,
|
||||
anchorClassName,
|
||||
position = 'top',
|
||||
horizontalAlign = 'center',
|
||||
content,
|
||||
}: Props) => {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
const anchorRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
}, [setIsOpen]);
|
||||
|
||||
const {
|
||||
position: layoutPosition,
|
||||
positionState,
|
||||
mutate,
|
||||
} = usePosition({
|
||||
verticalAlign: getVerticalAlignment(position),
|
||||
horizontalAlign: getHorizontalAlignment(position, horizontalAlign),
|
||||
offset: {
|
||||
vertical: getVerticalOffset(position),
|
||||
horizontal: getHorizontalOffset(position, horizontalAlign),
|
||||
},
|
||||
anchorRef,
|
||||
overlayRef,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={anchorRef}
|
||||
role="tab"
|
||||
tabIndex={0}
|
||||
className={anchorClassName}
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
onKeyDown={onKeyDownHandler(() => {
|
||||
setIsOpen(true);
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<ReactModal
|
||||
shouldCloseOnOverlayClick
|
||||
shouldCloseOnEsc
|
||||
isOpen={isOpen}
|
||||
style={{
|
||||
content: {
|
||||
...(!layoutPosition && { opacity: 0 }),
|
||||
...layoutPosition,
|
||||
},
|
||||
}}
|
||||
className={styles.content}
|
||||
overlayClassName={styles.overlay}
|
||||
onRequestClose={onClose}
|
||||
onAfterOpen={mutate}
|
||||
>
|
||||
<TipBubble
|
||||
ref={overlayRef}
|
||||
position={position}
|
||||
className={className}
|
||||
horizontalAlignment={positionState.horizontalAlign}
|
||||
>
|
||||
{typeof content === 'function' ? content(onClose) : content}
|
||||
</TipBubble>
|
||||
</ReactModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleTip;
|
|
@ -1,4 +1,4 @@
|
|||
import type { ReactNode, RefObject } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
|
@ -16,23 +16,26 @@ import {
|
|||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
content: ReactNode | Record<string, unknown>;
|
||||
anchorRef: RefObject<Element>;
|
||||
className?: string;
|
||||
isKeepOpen?: boolean;
|
||||
position?: TipBubblePosition;
|
||||
horizontalAlign?: HorizontalAlignment;
|
||||
anchorClassName?: string;
|
||||
children?: ReactNode;
|
||||
content?: ReactNode;
|
||||
};
|
||||
|
||||
const Tooltip = ({
|
||||
content,
|
||||
anchorRef,
|
||||
className,
|
||||
isKeepOpen = false,
|
||||
position = 'top',
|
||||
horizontalAlign = 'start',
|
||||
horizontalAlign = 'center',
|
||||
anchorClassName,
|
||||
children,
|
||||
content,
|
||||
}: Props) => {
|
||||
const [tooltipDom, setTooltipDom] = useState<HTMLDivElement>();
|
||||
const anchorRef = useRef<HTMLDivElement>(null);
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
|
@ -119,25 +122,30 @@ const Tooltip = ({
|
|||
|
||||
useLayoutEffect(() => {
|
||||
mutate();
|
||||
}, [content, mutate]);
|
||||
}, [mutate, content]);
|
||||
|
||||
if (!tooltipDom) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<div className={styles.tooltip}>
|
||||
<TipBubble
|
||||
ref={tooltipRef}
|
||||
className={className}
|
||||
style={{ ...(!layoutPosition && { opacity: 0 }), ...layoutPosition }}
|
||||
position={position}
|
||||
horizontalAlignment={positionState.horizontalAlign}
|
||||
>
|
||||
<div className={styles.content}>{content}</div>
|
||||
</TipBubble>
|
||||
</div>,
|
||||
tooltipDom
|
||||
return (
|
||||
<>
|
||||
<div ref={anchorRef} className={anchorClassName}>
|
||||
{children}
|
||||
</div>
|
||||
{tooltipDom &&
|
||||
content &&
|
||||
createPortal(
|
||||
<div className={styles.tooltip}>
|
||||
<TipBubble
|
||||
ref={tooltipRef}
|
||||
className={className}
|
||||
style={{ ...(!layoutPosition && { opacity: 0 }), ...layoutPosition }}
|
||||
position={position}
|
||||
horizontalAlignment={positionState.horizontalAlign}
|
||||
>
|
||||
<div className={styles.content}>{content}</div>
|
||||
</TipBubble>
|
||||
</div>,
|
||||
tooltipDom
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
2
packages/console/src/components/Tip/index.ts
Normal file
2
packages/console/src/components/Tip/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default as Tooltip } from './Tooltip';
|
||||
export { default as ToggleTip } from './ToggleTip';
|
|
@ -1,79 +0,0 @@
|
|||
import type { HTMLProps, ReactNode, RefObject } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import type { HorizontalAlignment } from '@/hooks/use-position';
|
||||
import usePosition from '@/hooks/use-position';
|
||||
|
||||
import type { TipBubblePosition } from '../TipBubble';
|
||||
import TipBubble from '../TipBubble';
|
||||
import {
|
||||
getVerticalAlignment,
|
||||
getHorizontalAlignment,
|
||||
getVerticalOffset,
|
||||
getHorizontalOffset,
|
||||
} from '../TipBubble/utils';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = HTMLProps<HTMLDivElement> & {
|
||||
children: ReactNode;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
anchorRef: RefObject<HTMLElement>;
|
||||
position?: TipBubblePosition;
|
||||
horizontalAlign?: HorizontalAlignment;
|
||||
};
|
||||
|
||||
const ToggleTip = ({
|
||||
children,
|
||||
isOpen,
|
||||
onClose,
|
||||
anchorRef,
|
||||
position = 'top',
|
||||
horizontalAlign = 'start',
|
||||
}: Props) => {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
position: layoutPosition,
|
||||
positionState,
|
||||
mutate,
|
||||
} = usePosition({
|
||||
verticalAlign: getVerticalAlignment(position),
|
||||
horizontalAlign: getHorizontalAlignment(position, horizontalAlign),
|
||||
offset: {
|
||||
vertical: getVerticalOffset(position),
|
||||
horizontal: getHorizontalOffset(position, horizontalAlign),
|
||||
},
|
||||
anchorRef,
|
||||
overlayRef,
|
||||
});
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
shouldCloseOnOverlayClick
|
||||
shouldCloseOnEsc
|
||||
isOpen={isOpen}
|
||||
style={{
|
||||
content: {
|
||||
...(!layoutPosition && { opacity: 0 }),
|
||||
...layoutPosition,
|
||||
},
|
||||
}}
|
||||
className={styles.content}
|
||||
overlayClassName={styles.overlay}
|
||||
onRequestClose={onClose}
|
||||
onAfterOpen={mutate}
|
||||
>
|
||||
<TipBubble
|
||||
ref={overlayRef}
|
||||
position={position}
|
||||
horizontalAlignment={positionState.horizontalAlign}
|
||||
>
|
||||
{children}
|
||||
</TipBubble>
|
||||
</ReactModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleTip;
|
|
@ -1,19 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.toggleTipButton {
|
||||
border-radius: 4px;
|
||||
padding: _.unit(1);
|
||||
|
||||
.icon {
|
||||
> svg {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--color-hover);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import Tip from '@/assets/images/tip.svg';
|
||||
import type { HorizontalAlignment } from '@/hooks/use-position';
|
||||
import { onKeyDownHandler } from '@/utilities/a11y';
|
||||
|
||||
import type { TipBubblePosition } from '../TipBubble';
|
||||
import ToggleTip from '../ToggleTip';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
render: (closeTipHandler: () => void) => ReactElement;
|
||||
className?: string;
|
||||
tipPosition?: TipBubblePosition;
|
||||
tipHorizontalAlignment?: HorizontalAlignment;
|
||||
};
|
||||
|
||||
const ToggleTipButton = ({ render, className, tipPosition, tipHorizontalAlignment }: Props) => {
|
||||
const anchorRef = useRef<HTMLDivElement>(null);
|
||||
const [isTipOpen, setIsTipOpen] = useState(false);
|
||||
|
||||
const closeTipHandler = () => {
|
||||
setIsTipOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.toggleTipButton, className)}>
|
||||
<div
|
||||
ref={anchorRef}
|
||||
role="tab"
|
||||
tabIndex={0}
|
||||
className={styles.icon}
|
||||
onClick={() => {
|
||||
setIsTipOpen(true);
|
||||
}}
|
||||
onKeyDown={onKeyDownHandler(() => {
|
||||
setIsTipOpen(true);
|
||||
})}
|
||||
>
|
||||
<Tip />
|
||||
</div>
|
||||
<ToggleTip
|
||||
isOpen={isTipOpen}
|
||||
anchorRef={anchorRef}
|
||||
position={tipPosition}
|
||||
horizontalAlign={tipHorizontalAlignment}
|
||||
onClose={() => {
|
||||
setIsTipOpen(false);
|
||||
}}
|
||||
>
|
||||
{render(closeTipHandler)}
|
||||
</ToggleTip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleTipButton;
|
|
@ -17,9 +17,11 @@
|
|||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.githubIcon {
|
||||
.githubToolTipAnchor {
|
||||
margin-right: _.unit(4);
|
||||
}
|
||||
|
||||
.githubIcon {
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Close from '@/assets/images/close.svg';
|
||||
|
@ -8,7 +7,7 @@ import CardTitle from '@/components/CardTitle';
|
|||
import DangerousRaw from '@/components/DangerousRaw';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import Spacer from '@/components/Spacer';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import Tooltip from '@/components/Tip/Tooltip';
|
||||
import { SupportedSdk } from '@/types/applications';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -47,7 +46,6 @@ const getSampleProjectUrl = (sdk: SupportedSdk) => {
|
|||
|
||||
const GuideHeader = ({ appName, selectedSdk, isCompact = false, onClose }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const tipRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onClickGetSample = () => {
|
||||
const sampleUrl = getSampleProjectUrl(selectedSdk);
|
||||
|
@ -64,12 +62,15 @@ const GuideHeader = ({ appName, selectedSdk, isCompact = false, onClose }: Props
|
|||
subtitle="applications.guide.header_description"
|
||||
/>
|
||||
<Spacer />
|
||||
<IconButton className={styles.githubIcon} size="large" onClick={onClickGetSample}>
|
||||
<div ref={tipRef}>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
anchorClassName={styles.githubToolTipAnchor}
|
||||
content={t('applications.guide.get_sample_file')}
|
||||
>
|
||||
<IconButton className={styles.githubIcon} size="large" onClick={onClickGetSample}>
|
||||
<GetSample />
|
||||
</div>
|
||||
<Tooltip anchorRef={tipRef} content={t('applications.guide.get_sample_file')} />
|
||||
</IconButton>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<IconButton size="large" onClick={onClose}>
|
||||
<Close className={styles.closeIcon} />
|
||||
</IconButton>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { phoneRegEx, emailRegEx } from '@logto/core-kit';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -9,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import Button from '@/components/Button';
|
||||
import FormField from '@/components/FormField';
|
||||
import TextInput from '@/components/TextInput';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import { Tooltip } from '@/components/Tip';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { safeParseJson } from '@/utilities/json';
|
||||
|
||||
|
@ -27,7 +28,6 @@ type FormData = {
|
|||
};
|
||||
|
||||
const SenderTester = ({ connectorId, connectorType, config, className }: Props) => {
|
||||
const buttonPosReference = useRef(null);
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const {
|
||||
handleSubmit,
|
||||
|
@ -100,23 +100,19 @@ const SenderTester = ({ connectorId, connectorType, config, className }: Props)
|
|||
})}
|
||||
/>
|
||||
</FormField>
|
||||
<div ref={buttonPosReference} className={styles.send}>
|
||||
<Tooltip
|
||||
isKeepOpen
|
||||
anchorClassName={styles.send}
|
||||
className={styles.successfulTooltip}
|
||||
content={conditional(showTooltip && t('connector_details.test_message_sent'))}
|
||||
>
|
||||
<Button
|
||||
isLoading={isSubmitting}
|
||||
title="connector_details.send"
|
||||
type="outline"
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
</div>
|
||||
{showTooltip && (
|
||||
<Tooltip
|
||||
isKeepOpen
|
||||
horizontalAlign="center"
|
||||
className={styles.successfulTooltip}
|
||||
anchorRef={buttonPosReference}
|
||||
content={t('connector_details.test_message_sent')}
|
||||
/>
|
||||
)}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={classNames(inputError?.message ? styles.error : styles.description)}>
|
||||
{inputError?.message ?? t('connector_details.test_sender_description')}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
align-items: center;
|
||||
|
||||
.tipButton {
|
||||
margin-left: _.unit(1);
|
||||
margin-left: _.unit(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import Tip from '@/assets/images/tip.svg';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import ToggleTipButton from '@/components/ToggleTipButton';
|
||||
import { ToggleTip } from '@/components/Tip';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -11,10 +13,9 @@ const ConnectorStatusField = () => {
|
|||
return (
|
||||
<div className={styles.field}>
|
||||
{t('connectors.connector_status')}
|
||||
<ToggleTipButton
|
||||
tipHorizontalAlignment="center"
|
||||
className={styles.tipButton}
|
||||
render={(closeTipHandler) => (
|
||||
<ToggleTip
|
||||
anchorClassName={styles.tipButton}
|
||||
content={(closeTipHandler) => (
|
||||
<>
|
||||
<div className={styles.title}>{t('connectors.connector_status')}</div>
|
||||
<div className={styles.content}>
|
||||
|
@ -37,7 +38,11 @@ const ConnectorStatusField = () => {
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
>
|
||||
<IconButton size="small">
|
||||
<Tip />
|
||||
</IconButton>
|
||||
</ToggleTip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
align-items: center;
|
||||
|
||||
.toggleTipButton {
|
||||
margin-left: _.unit(1);
|
||||
margin-left: _.unit(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import ArrowDown from '@/assets/images/arrow-down.svg';
|
||||
import ArrowUp from '@/assets/images/arrow-up.svg';
|
||||
import Tip from '@/assets/images/tip.svg';
|
||||
import Card from '@/components/Card';
|
||||
import ToggleTipButton from '@/components/ToggleTipButton';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import { ToggleTip } from '@/components/Tip';
|
||||
import { formatNumberWithComma } from '@/utilities/number';
|
||||
|
||||
import * as styles from './Block.module.scss';
|
||||
|
@ -29,7 +31,11 @@ const Block = ({ variant = 'default', count, delta, title, tip }: Props) => {
|
|||
<div className={styles.title}>
|
||||
{t(title)}
|
||||
{tip && (
|
||||
<ToggleTipButton className={styles.toggleTipButton} render={() => <div>{t(tip)}</div>} />
|
||||
<ToggleTip anchorClassName={styles.toggleTipButton} content={<div>{t(tip)}</div>}>
|
||||
<IconButton size="small">
|
||||
<Tip />
|
||||
</IconButton>
|
||||
</ToggleTip>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
|
|
|
@ -16,6 +16,7 @@ import Delete from '@/assets/images/delete.svg';
|
|||
import Button from '@/components/Button';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import { Tooltip } from '@/components/Tip';
|
||||
import useApi, { RequestError } from '@/hooks/use-api';
|
||||
import useUiLanguages from '@/hooks/use-ui-languages';
|
||||
import {
|
||||
|
@ -162,14 +163,15 @@ const LanguageDetails = () => {
|
|||
)}
|
||||
</div>
|
||||
{!isBuiltIn && (
|
||||
<IconButton
|
||||
tooltip={t('sign_in_exp.others.manage_language.deletion_tip')}
|
||||
onClick={() => {
|
||||
setIsDeletionAlertOpen(true);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<Tooltip content={t('sign_in_exp.others.manage_language.deletion_tip')}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setIsDeletionAlertOpen(true);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<form
|
||||
|
@ -189,20 +191,23 @@ const LanguageDetails = () => {
|
|||
<th>
|
||||
<span className={style.customValuesColumn}>
|
||||
{t('sign_in_exp.others.manage_language.custom_values')}
|
||||
<IconButton
|
||||
size="small"
|
||||
className={style.clearButton}
|
||||
tooltip={t('sign_in_exp.others.manage_language.clear_all_tip')}
|
||||
onClick={() => {
|
||||
for (const [key, value] of Object.entries(
|
||||
flattenTranslation(emptyUiTranslation)
|
||||
)) {
|
||||
setValue(key, value, { shouldDirty: true });
|
||||
}
|
||||
}}
|
||||
<Tooltip
|
||||
anchorClassName={style.clearButton}
|
||||
content={t('sign_in_exp.others.manage_language.clear_all_tip')}
|
||||
>
|
||||
<Clear className={style.clearIcon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
for (const [key, value] of Object.entries(
|
||||
flattenTranslation(emptyUiTranslation)
|
||||
)) {
|
||||
setValue(key, value, { shouldDirty: true });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Clear className={style.clearIcon} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
|
|
|
@ -10,6 +10,7 @@ import Minus from '@/assets/images/minus.svg';
|
|||
import SwitchArrowIcon from '@/assets/images/switch-arrow.svg';
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import { Tooltip } from '@/components/Tip';
|
||||
import type { SignInMethod } from '@/pages/SignInExperience/types';
|
||||
|
||||
import ConnectorSetupWarning from '../ConnectorSetupWarning';
|
||||
|
@ -73,13 +74,14 @@ const SignInMethodItem = ({
|
|||
/>
|
||||
{identifier !== SignInIdentifier.Username && (
|
||||
<>
|
||||
<IconButton
|
||||
className={styles.swapButton}
|
||||
tooltip={t('sign_in_exp.sign_up_and_sign_in.sign_in.auth_swap_tip')}
|
||||
onClick={onToggleVerificationPrimary}
|
||||
<Tooltip
|
||||
anchorClassName={styles.swapButton}
|
||||
content={t('sign_in_exp.sign_up_and_sign_in.sign_in.auth_swap_tip')}
|
||||
>
|
||||
<SwitchArrowIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={onToggleVerificationPrimary}>
|
||||
<SwitchArrowIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Checkbox
|
||||
className={styles.checkBox}
|
||||
label={t('sign_in_exp.sign_up_and_sign_in.sign_in.verification_code_auth')}
|
||||
|
@ -94,9 +96,8 @@ const SignInMethodItem = ({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<IconButton
|
||||
disabled={!isDeletable}
|
||||
tooltip={conditional(
|
||||
<Tooltip
|
||||
content={conditional(
|
||||
!isDeletable &&
|
||||
t('sign_in_exp.sign_up_and_sign_in.tip.delete_sign_in_method', {
|
||||
identifier: t('sign_in_exp.sign_up_and_sign_in.identifiers', {
|
||||
|
@ -104,10 +105,11 @@ const SignInMethodItem = ({
|
|||
}).toLocaleLowerCase(),
|
||||
})
|
||||
)}
|
||||
onClick={onDelete}
|
||||
>
|
||||
<Minus />
|
||||
</IconButton>
|
||||
<IconButton disabled={!isDeletable} onClick={onDelete}>
|
||||
<Minus />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{errorMessage && <div className={styles.errorMessage}>{errorMessage}</div>}
|
||||
<ConnectorSetupWarning requiredConnectors={requiredConnectors} />
|
||||
|
|
Loading…
Reference in a new issue