0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(console): implement tag component (#3853)

This commit is contained in:
Xiao Yijun 2023-05-17 11:30:08 +08:00 committed by GitHub
parent 21d1fa42c7
commit 05b3729260
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 153 additions and 122 deletions

View file

@ -1,32 +0,0 @@
@use '@/scss/underscore' as _;
.status {
display: flex;
align-items: center;
font: var(--font-body-2);
.icon {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: _.unit(2);
background: var(--color-success-70);
}
&.disabled {
.icon {
background: var(--color-neutral-70);
}
}
&.outlined {
padding: _.unit(0.5) _.unit(2);
border-radius: 10px;
background: var(--color-success-99);
font: var(--font-label-3);
&.disabled {
background: var(--color-neutral-variant-95);
}
}
}

View file

@ -1,21 +0,0 @@
import classNames from 'classnames';
import type { ReactNode } from 'react';
import * as styles from './index.module.scss';
type Props = {
status: 'enabled' | 'disabled';
children: ReactNode;
variant?: 'plain' | 'outlined';
};
function Status({ status, children, variant = 'plain' }: Props) {
return (
<div className={classNames(styles.status, styles[status], styles[variant])}>
<div className={styles.icon} />
<div>{children}</div>
</div>
);
}
export default Status;

View file

@ -0,0 +1,70 @@
@use '@/scss/underscore' as _;
.tag {
display: flex;
align-items: center;
font: var(--font-body-2);
.icon {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: _.unit(2);
background: var(--color-on-success-container);
}
.resultIcon {
width: 16px;
height: 16px;
border-radius: unset;
background-color: unset;
color: var(--color-on-success-container);
}
&.info {
.icon {
background: var(--color-on-info-container);
}
}
&.alert {
.icon {
background: var(--color-on-alert-container);
}
}
&.error {
.icon {
background: var(--color-on-error-container);
}
.resultIcon {
background: unset;
color: var(--color-on-error-container);
}
}
&.outlined {
padding: _.unit(0.5) _.unit(2);
border-radius: 10px;
background: var(--color-success-container);
font: var(--font-label-3);
&.info {
background: var(--color-info-container);
}
&.alert {
background: var(--color-alert-container);
}
&.error {
background: var(--color-error-container);
.resultIcon {
background: unset;
color: var(--color-on-error-container);
}
}
}
}

View file

@ -0,0 +1,41 @@
import { conditional } from '@silverhand/essentials';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import Failed from '@/assets/images/failed.svg';
import Success from '@/assets/images/success.svg';
import * as styles from './index.module.scss';
type Props = {
type?: 'property' | 'state' | 'result';
status?: 'info' | 'success' | 'alert' | 'error';
variant?: 'plain' | 'outlined';
className?: string;
children: ReactNode;
};
const ResultIconMap: Partial<Record<Required<Props>['status'], SvgComponent>> = {
success: Success,
error: Failed,
};
function Tag({
type = 'property',
status = 'info',
variant = 'outlined',
className,
children,
}: Props) {
const ResultIcon = conditional(type === 'result' && ResultIconMap[status]);
return (
<div className={classNames(styles.tag, styles[status], styles[variant], className)}>
{type === 'state' && <div className={styles.icon} />}
{ResultIcon && <ResultIcon className={classNames(styles.icon, styles.resultIcon)} />}
<div>{children}</div>
</div>
);
}
export default Tag;

View file

@ -67,14 +67,6 @@
margin-left: _.unit(2);
}
.type {
background-color: var(--color-surface-variant);
color: var(--color-text);
padding: _.unit(0.5) _.unit(2);
border-radius: 10px;
font: var(--font-label-3);
}
.text {
font: var(--font-label-2);
color: var(--color-text-secondary);

View file

@ -21,6 +21,7 @@ import DetailsPage from '@/components/DetailsPage';
import Drawer from '@/components/Drawer';
import PageMeta from '@/components/PageMeta';
import TabNav, { TabNavItem } from '@/components/TabNav';
import Tag from '@/components/Tag';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
@ -147,7 +148,7 @@ function ApplicationDetails() {
<div className={styles.metadata}>
<div className={styles.name}>{data.name}</div>
<div className={styles.details}>
<div className={styles.type}>{t(`${applicationTypeI18nKey[data.type]}.title`)}</div>
<Tag>{t(`${applicationTypeI18nKey[data.type]}.title`)}</Tag>
<div className={styles.verticalBar} />
<div className={styles.text}>App ID</div>
<CopyToClipboard size="small" value={data.id} />

View file

@ -1,9 +0,0 @@
@use '@/scss/underscore' as _;
.connectorType {
background: var(--color-surface-variant);
border-radius: 10px;
padding: _.unit(0.5) _.unit(2);
color: var(--color-text);
font: var(--font-label-3);
}

View file

@ -1,7 +1,7 @@
import { ConnectorType } from '@logto/schemas';
import { useTranslation } from 'react-i18next';
import * as styles from './index.module.scss';
import Tag from '@/components/Tag';
type Props = {
type: ConnectorType;
@ -11,11 +11,11 @@ function ConnectorTypeName({ type }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
return (
<div className={styles.connectorType}>
<Tag>
{type === ConnectorType.Email && t('connector_details.type_email')}
{type === ConnectorType.Sms && t('connector_details.type_sms')}
{type === ConnectorType.Social && t('connector_details.type_social')}
</div>
</Tag>
);
}

View file

@ -19,8 +19,8 @@ import DetailsPage from '@/components/DetailsPage';
import Drawer from '@/components/Drawer';
import Markdown from '@/components/Markdown';
import PageMeta from '@/components/PageMeta';
import Status from '@/components/Status';
import TabNav, { TabNavItem } from '@/components/TabNav';
import Tag from '@/components/Tag';
import UnnamedTrans from '@/components/UnnamedTrans';
import { ConnectorsTabs } from '@/consts/page-tabs';
import type { RequestError } from '@/hooks/use-api';
@ -129,19 +129,19 @@ function ConnectorDetails() {
<div className={styles.verticalBar} />
{connectorFactory && (
<>
<div className={styles.factoryName}>
<Tag>
<UnnamedTrans resource={connectorFactory.name} />
</div>
</Tag>
<div className={styles.verticalBar} />
</>
)}
<Status status={inUse ? 'enabled' : 'disabled'} variant="outlined">
<Tag type="state" status={inUse ? 'success' : 'info'}>
{t(
inUse
? 'connectors.connector_status_in_use'
: 'connectors.connector_status_not_in_use'
)}
</Status>
</Tag>
<div className={styles.verticalBar} />
<div className={styles.text}>ID</div>
<CopyToClipboard size="small" value={data.id} />

View file

@ -1,10 +1,9 @@
import { ConnectorType } from '@logto/connector-kit';
import { useTranslation } from 'react-i18next';
import Tag from '@/components/Tag';
import { Tooltip } from '@/components/Tip';
import * as styles from './DemoTag.module.scss';
type Props = {
connectorType: ConnectorType;
};
@ -17,7 +16,7 @@ function DemoTag({ connectorType }: Props) {
<Tooltip
content={<div>{t(isSocial ? 'connectors.social_demo_tip' : 'connectors.demo_tip')}</div>}
>
<div className={styles.tag}>{t('general.demo')}</div>
<Tag status="alert">{t('general.demo')}</Tag>
</Tooltip>
);
}

View file

@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import Status from '@/components/Status';
import Tag from '@/components/Tag';
import useConnectorInUse from '@/hooks/use-connector-in-use';
import type { ConnectorGroup } from '@/types/connector';
@ -18,9 +18,9 @@ function ConnectorStatus({ connectorGroup }: Props) {
const inUse = isConnectorInUse(firstConnector);
return firstConnector ? (
<Status status={inUse ? 'enabled' : 'disabled'}>
<Tag type="state" status={inUse ? 'success' : 'info'} variant="plain">
{t(inUse ? 'connectors.connector_status_in_use' : 'connectors.connector_status_not_in_use')}
</Status>
</Tag>
) : (
<span>-</span>
);

View file

@ -17,19 +17,10 @@
align-items: center;
> span {
margin-left: _.unit(2);
margin: _.unit(2);
font: var(--font-body-2);
color: var(--color-text-secondary);
}
.builtInFlag {
display: inline-block;
font: var(--font-label-3);
color: var(--color-text);
background-color: var(--color-surface-variant);
padding: _.unit(0.5) _.unit(2);
border-radius: 10px;
}
}
}

View file

@ -17,6 +17,7 @@ import Button from '@/components/Button';
import ConfirmModal from '@/components/ConfirmModal';
import IconButton from '@/components/IconButton';
import Table from '@/components/Table';
import Tag from '@/components/Tag';
import Textarea from '@/components/Textarea';
import { Tooltip } from '@/components/Tip';
import useApi, { RequestError } from '@/hooks/use-api';
@ -157,11 +158,7 @@ function LanguageDetails() {
<div className={styles.languageInfo}>
{uiLanguageNameMapping[selectedLanguage]}
<span>{selectedLanguage}</span>
{isBuiltIn && (
<span className={styles.builtInFlag}>
{t('sign_in_exp.others.manage_language.logto_provided')}
</span>
)}
{isBuiltIn && <Tag>{t('sign_in_exp.others.manage_language.logto_provided')}</Tag>}
</div>
{!isBuiltIn && (
<Tooltip content={t('sign_in_exp.others.manage_language.deletion_tip')}>

View file

@ -1,9 +0,0 @@
@use '@/scss/underscore' as _;
.suspended {
background: var(--color-error-container);
color: var(--color-text);
font: var(--font-label-3);
padding: _.unit(0.5) _.unit(1.5);
border-radius: 10px;
}

View file

@ -1,7 +1,6 @@
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import * as styles from './index.module.scss';
import Tag from '@/components/Tag';
type Props = {
className?: string;
@ -11,7 +10,9 @@ function SuspendedTag({ className }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
return (
<div className={classNames(styles.suspended, className)}>{t('user_details.suspended')}</div>
<Tag status="error" className={className}>
{t('user_details.suspended')}
</Tag>
);
}

View file

@ -20,8 +20,8 @@ import DeleteConfirmModal from '@/components/DeleteConfirmModal';
import DetailsPage from '@/components/DetailsPage';
import DynamicT from '@/components/DynamicT';
import PageMeta from '@/components/PageMeta';
import Status from '@/components/Status';
import TabNav, { TabNavItem } from '@/components/TabNav';
import Tag from '@/components/Tag';
import { WebhookDetailsTabs } from '@/consts';
import useApi, { type RequestError } from '@/hooks/use-api';
import useTheme from '@/hooks/use-theme';
@ -108,9 +108,9 @@ function WebhookDetails() {
{isEnabled ? (
<div>Success Rate (WIP)</div>
) : (
<Status status="disabled" variant="outlined">
<Tag type="state" status="info">
<DynamicT forKey="webhook_details.not_in_use" />
</Status>
</Tag>
)}
<div className={styles.verticalBar} />
<div className={styles.text}>ID</div>

View file

@ -105,10 +105,15 @@
--color-tertiary-container: var(--color-tertiary-90);
--color-on-tertiary-container: var(--color-tertiary-10);
--color-error: var(--color-error-40);
--color-on-error: var(--color-all-100);
--color-error-container: var(--color-error-90);
--color-on-error-container: var(--color-error-10);
--color-alert-container: var(--color-alert-99);
--color-error-hover: var(--color-error-50);
--color-error-container: var(--color-error-95);
--color-on-error-container: var(--color-error-50);
--color-alert-container: var(--color-alert-95);
--color-on-alert-container: var(--color-alert-70);
--color-success-container: var(--color-success-99);
--color-on-success-container: var(--color-success-70);
--color-info-container: var(--color-neutral-variant-90);
--color-on-info-container: var(--color-neutral-variant-60);
--color-background: var(--color-neutral-99);
--color-on-background: var(--color-neutral-10);
--color-surface: var(--color-neutral-99);
@ -278,10 +283,15 @@
--color-tertiary-container: var(--color-tertiary-90);
--color-on-tertiary-container: var(--color-tertiary-30);
--color-error: var(--color-error-70);
--color-on-error: var(--color-all-0);
--color-error-container: var(--color-error-90);
--color-on-error-container: var(--color-error-30);
--color-error-hover: var(--color-error-60);
--color-error-container: var(--color-error-95);
--color-on-error-container: var(--color-error-70);
--color-alert-container: var(--color-alert-90);
--color-on-alert-container: var(--color-alert-60);
--color-success-container: var(--color-success-90);
--color-on-success-container: var(--color-success-60);
--color-info-container: var(--color-neutral-variant-90);
--color-on-info-container: var(--color-neutral-variant-70);
--color-background: var(--color-neutral-99);
--color-on-background: var(--color-neutral-10);
--color-surface: var(--color-neutral-99);