mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console,core,demo-app,elements,experience): improve rtl support (#6549)
* refactor(console,experience): improve rtl support * chore: add changeset
This commit is contained in:
parent
3b9714b993
commit
fae8725a44
211 changed files with 701 additions and 355 deletions
9
.changeset/rare-poems-happen.md
Normal file
9
.changeset/rare-poems-happen.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
"@logto/experience": patch
|
||||
"@logto/demo-app": patch
|
||||
"@logto/elements": patch
|
||||
"@logto/console": patch
|
||||
"@logto/core": patch
|
||||
---
|
||||
|
||||
improve RTL language support
|
|
@ -123,6 +123,7 @@ function Providers() {
|
|||
// hook will cause a re-render following some bugs here. This still works for the
|
||||
// initial render, so we're good for now. Consider refactoring this in the future.
|
||||
lang: i18next.language,
|
||||
dir: i18next.dir(),
|
||||
}}
|
||||
/>
|
||||
<Toast />
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
}
|
||||
|
||||
.tag {
|
||||
margin-left: _.unit(-2);
|
||||
margin-inline-start: _.unit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
.expander {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
margin-top: _.unit(2);
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: _.unit(1);
|
||||
margin-inline-end: _.unit(1);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
|
||||
.eventSelector {
|
||||
width: 300px;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
|
||||
.applicationSelector {
|
||||
width: 250px;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: _.unit(3);
|
||||
}
|
||||
gap: _.unit(3);
|
||||
|
||||
.content {
|
||||
span {
|
||||
|
@ -26,7 +23,7 @@
|
|||
}
|
||||
|
||||
> ul {
|
||||
padding-left: _.unit(3);
|
||||
padding-inline-start: _.unit(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,9 +103,7 @@ function BasicForm({ isAllowEditTarget, isStandard, conflictConnectorName }: Pro
|
|||
</div>
|
||||
{conflictConnectorName && (
|
||||
<div className={styles.error}>
|
||||
<div className={styles.icon}>
|
||||
<Error />
|
||||
</div>
|
||||
<Error />
|
||||
<div className={styles.content}>
|
||||
<Trans
|
||||
components={{
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
.multiSelect {
|
||||
padding-top: _.unit(1);
|
||||
padding-left: _.unit(1);
|
||||
padding-inline-start: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
|
||||
.send {
|
||||
margin-left: _.unit(1.5);
|
||||
margin-inline-start: _.unit(1.5);
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
.connector {
|
||||
font: var(--font-body-2);
|
||||
display: flex;
|
||||
gap: _.unit(3);
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: _.unit(3);
|
||||
|
||||
.name {
|
||||
font: var(--font-label-2);
|
||||
@include _.multi-line-ellipsis(1);
|
||||
padding-right: _.unit(3);
|
||||
}
|
||||
|
||||
.connectorId {
|
||||
|
|
|
@ -39,5 +39,5 @@
|
|||
}
|
||||
|
||||
.planNameTag {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { TenantsContext } from '@/contexts/TenantsProvider';
|
|||
import Button, { type Props as ButtonProps } from '@/ds-components/Button';
|
||||
import DangerousRaw from '@/ds-components/DangerousRaw';
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import TextLink from '@/ds-components/TextLink';
|
||||
|
||||
import FeaturedSkuContent from './FeaturedSkuContent';
|
||||
|
@ -67,7 +68,11 @@ function SkuCardItem({ sku, onSelect, buttonProps }: Props) {
|
|||
isTrailingIcon
|
||||
href={pricingLink}
|
||||
targetBlank="noopener"
|
||||
icon={<ArrowRight className={styles.linkIcon} />}
|
||||
icon={
|
||||
<FlipOnRtl>
|
||||
<ArrowRight className={styles.linkIcon} />
|
||||
</FlipOnRtl>
|
||||
}
|
||||
className={styles.link}
|
||||
>
|
||||
<DynamicT forKey="upsell.create_tenant.view_all_features" />
|
||||
|
|
|
@ -105,12 +105,13 @@ function DetailsPageHeader({
|
|||
additionalCustomElement,
|
||||
actionMenuItems,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { t, i18n } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const [showIcon, setShowIcon] = useState(true);
|
||||
const [isCompact, setIsCompact] = useState(false);
|
||||
const [showAdditionalCustomElement, setShowAdditionalCustomElement] = useState(true);
|
||||
const identifierRef = useRef<HTMLDivElement>(null);
|
||||
const operationRef = useRef<HTMLDivElement>(null);
|
||||
const isRtl = i18n.dir() === 'rtl';
|
||||
|
||||
useWindowResize(() => {
|
||||
if (!identifierRef.current || !operationRef.current) {
|
||||
|
@ -119,37 +120,45 @@ function DetailsPageHeader({
|
|||
|
||||
// Dynamically handle the visibility of the icon and action button styles. Sources:
|
||||
// https://www.figma.com/file/hqAWH3Di8gkiV5TXAt6juO/%F0%9F%8C%B9-%5BAC%5D-Layout-UI-Optimization?type=design&node-id=896-75673
|
||||
const { right: identifierRightEdge, width: identifierWidth } =
|
||||
identifierRef.current.getBoundingClientRect();
|
||||
const operationLeftEdge = operationRef.current.getBoundingClientRect().left;
|
||||
const {
|
||||
left: identifierLeftEdge,
|
||||
right: identifierRightEdge,
|
||||
width: identifierWidth,
|
||||
} = identifierRef.current.getBoundingClientRect();
|
||||
|
||||
const { left: operationLeftEdge, right: operationRightEdge } =
|
||||
operationRef.current.getBoundingClientRect();
|
||||
|
||||
const identifierEdge = isRtl ? identifierLeftEdge : identifierRightEdge;
|
||||
const operationEdge = isRtl ? operationRightEdge : operationLeftEdge;
|
||||
|
||||
// When the operation buttons are in regular form, and the gap between the operation area and the identifier copy box is
|
||||
// only 24px. This means the window is shrinking and reaching the 1st breakpoint. Set operation buttons to compact form.
|
||||
if (!isCompact && operationLeftEdge - identifierRightEdge <= 24) {
|
||||
if (!isCompact && Math.abs(operationEdge - identifierEdge) <= 24) {
|
||||
setIsCompact(true);
|
||||
}
|
||||
|
||||
// When the operation buttons are compact, and the gap between the operation area and the identifier copy box is only 24px.
|
||||
// This means the window keeps shrinking and reaching the 2nd breakpoint. Hide the main icon on the very left.
|
||||
if (isCompact && showIcon && operationLeftEdge - identifierRightEdge <= 24) {
|
||||
if (isCompact && showIcon && Math.abs(operationEdge - identifierEdge) <= 24) {
|
||||
setShowIcon(false);
|
||||
}
|
||||
|
||||
// When the identifier copy box is 50px, and the gap between the operation area and the identifier copy box is only 24px.
|
||||
// This is when the page header is extremely narrow and barely has space to hold the identifier. Hide the additional custom element.
|
||||
if (identifierWidth <= 50 && operationLeftEdge - identifierRightEdge <= 24) {
|
||||
if (identifierWidth <= 50 && Math.abs(operationEdge - identifierEdge) <= 24) {
|
||||
setShowAdditionalCustomElement(false);
|
||||
}
|
||||
|
||||
// When the gap between the operation buttons and the identifier copy box is greater than 80px, show the additional custom element.
|
||||
if (!showAdditionalCustomElement && operationLeftEdge - identifierRightEdge > 80) {
|
||||
if (!showAdditionalCustomElement && Math.abs(operationEdge - identifierEdge) > 80) {
|
||||
setShowAdditionalCustomElement(true);
|
||||
}
|
||||
|
||||
// When the operation buttons are compact, icon is hidden, and the operation area is 120px away from the identifier copy box.
|
||||
// This means the window is enlarging and there is enough room to hold the icon. Show the icon.
|
||||
// 120px is a bit greater than the space required to hold the icon (60px + 24px padding), in order to avoid jittering.
|
||||
if (isCompact && !showIcon && operationLeftEdge - identifierRightEdge > 120) {
|
||||
if (isCompact && !showIcon && Math.abs(operationEdge - identifierEdge) > 120) {
|
||||
setShowIcon(true);
|
||||
}
|
||||
|
||||
|
@ -160,7 +169,7 @@ function DetailsPageHeader({
|
|||
if (
|
||||
isCompact &&
|
||||
showIcon &&
|
||||
operationLeftEdge - identifierRightEdge > (additionalCustomElement ? 240 : 180)
|
||||
Math.abs(operationEdge - identifierEdge) > (additionalCustomElement ? 240 : 180)
|
||||
) {
|
||||
setIsCompact(false);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
padding: _.unit(6);
|
||||
border-radius: 16px;
|
||||
background-color: var(--color-layer-1);
|
||||
gap: _.unit(6);
|
||||
|
||||
.icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 12px;
|
||||
margin-right: _.unit(6);
|
||||
@include _.shimmering-animation;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { type To } from 'react-router-dom';
|
|||
import Back from '@/assets/icons/back.svg?react';
|
||||
import type DangerousRaw from '@/ds-components/DangerousRaw';
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import TextLink from '@/ds-components/TextLink';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
|
||||
|
@ -35,7 +36,15 @@ function DetailsPage({
|
|||
}: Props) {
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
<TextLink to={backLink} icon={<Back />} className={styles.backLink}>
|
||||
<TextLink
|
||||
to={backLink}
|
||||
icon={
|
||||
<FlipOnRtl>
|
||||
<Back />
|
||||
</FlipOnRtl>
|
||||
}
|
||||
className={styles.backLink}
|
||||
>
|
||||
{typeof backLinkTitle === 'string' ? <DynamicT forKey={backLinkTitle} /> : backLinkTitle}
|
||||
</TextLink>
|
||||
{isLoading ? (
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
flex: 1 1 0;
|
||||
font: var(--font-body-2);
|
||||
@include _.text-ellipsis;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.suspended {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
flex: 1 1 0;
|
||||
font: var(--font-body-2);
|
||||
@include _.text-ellipsis;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.suspended {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
flex: 1 1 0;
|
||||
font: var(--font-body-2);
|
||||
@include _.text-ellipsis;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,12 +58,12 @@
|
|||
display: flex;
|
||||
align-items: flex-start; // align name and the feature tag to the top
|
||||
justify-content: space-between;
|
||||
gap: _.unit(1);
|
||||
}
|
||||
|
||||
.name {
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text);
|
||||
margin-right: _.unit(1);
|
||||
}
|
||||
|
||||
.description {
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
justify-content: space-between;
|
||||
max-width: dim.$guide-main-content-max-width;
|
||||
margin: 0 auto;
|
||||
gap: _.unit(3);
|
||||
}
|
||||
|
||||
.text {
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text);
|
||||
margin-right: _.unit(3);
|
||||
@include _.multi-line-ellipsis(2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.requestSdkButton {
|
||||
margin-right: _.unit(15);
|
||||
margin-inline-end: _.unit(15);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 918px) {
|
||||
.requestSdkButton {
|
||||
margin-right: 0;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
background-color: var(--color-layer-1);
|
||||
max-width: dim.$guide-main-content-max-width;
|
||||
margin: 0 auto;
|
||||
gap: _.unit(4);
|
||||
|
||||
.index {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
margin-right: _.unit(4);
|
||||
@include _.shimmering-animation;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,8 +88,7 @@
|
|||
max-width: dim.$guide-main-content-max-width;
|
||||
|
||||
> button {
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
margin-inline: auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
}
|
||||
|
||||
> div:not(:first-child) {
|
||||
margin-left: _.unit(3);
|
||||
margin-inline-start: _.unit(3);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-right: _.unit(4);
|
||||
padding-inline-end: _.unit(4);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -22,7 +22,7 @@
|
|||
margin-top: _.unit(-1);
|
||||
|
||||
> div:not(:last-child) {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
}
|
||||
|
||||
.meta {
|
||||
|
@ -56,7 +56,7 @@
|
|||
align-items: baseline;
|
||||
|
||||
.title {
|
||||
margin-right: _.unit(1);
|
||||
margin-inline-end: _.unit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ExternalLinkIcon from '@/assets/icons/external-link.svg?react';
|
|||
import { AppDataContext } from '@/contexts/AppDataProvider';
|
||||
import type { Props as ButtonProps, ButtonType } from '@/ds-components/Button';
|
||||
import Button from '@/ds-components/Button';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import { Tooltip } from '@/ds-components/Tip';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
@ -29,12 +30,14 @@ function LivePreviewButton({ size, type, isDisabled }: Props) {
|
|||
disabled={isDisabled}
|
||||
title="sign_in_exp.preview.live_preview"
|
||||
trailingIcon={
|
||||
<ExternalLinkIcon
|
||||
className={conditional(
|
||||
type !== 'violet' &&
|
||||
classNames(styles.defaultIcon, isDisabled && styles.disabledDefaultIcon)
|
||||
)}
|
||||
/>
|
||||
<FlipOnRtl>
|
||||
<ExternalLinkIcon
|
||||
className={conditional(
|
||||
type !== 'violet' &&
|
||||
classNames(styles.defaultIcon, isDisabled && styles.disabledDefaultIcon)
|
||||
)}
|
||||
/>
|
||||
</FlipOnRtl>
|
||||
}
|
||||
onClick={() => {
|
||||
window.open(new URL('/demo-app', tenantEndpoint), '_blank');
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
color: var(--color-text);
|
||||
padding: _.unit(3);
|
||||
border-bottom: 1px solid var(--color-divider);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
.factorIcon {
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: _.unit(3);
|
||||
margin-inline-end: _.unit(3);
|
||||
}
|
||||
|
||||
.factorTip {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
.delete {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: _.unit(-0.5);
|
||||
margin-inline-end: _.unit(-0.5);
|
||||
}
|
||||
|
||||
input {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.headlineWithMultiInputs {
|
||||
padding-right: _.unit(9);
|
||||
padding-inline-end: _.unit(9);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ExternalLinkIcon from '@/assets/icons/external-link.svg?react';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import IconButton from '@/ds-components/IconButton';
|
||||
import { Tooltip } from '@/ds-components/Tip';
|
||||
|
||||
|
@ -18,7 +19,9 @@ function OpenExternalLink({ link }: Props) {
|
|||
window.open(link, '_blank');
|
||||
}}
|
||||
>
|
||||
<ExternalLinkIcon />
|
||||
<FlipOnRtl>
|
||||
<ExternalLinkIcon />
|
||||
</FlipOnRtl>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
align-items: center;
|
||||
|
||||
.createButton {
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,6 @@
|
|||
}
|
||||
|
||||
.actionColumn {
|
||||
text-align: right;
|
||||
text-align: end;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
font: var(--font-label-2);
|
||||
|
||||
.comingSoon {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
overflow: hidden;
|
||||
|
||||
.caret {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
}
|
||||
|
||||
.name {
|
||||
|
@ -28,7 +28,7 @@
|
|||
flex-shrink: 0;
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@
|
|||
}
|
||||
|
||||
.closeIcon {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
// Todo @xiaoyijun Remove this `count` style together with the dev feature flag
|
||||
.count {
|
||||
flex-shrink: 0;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.flag {
|
||||
flex-shrink: 0;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
color: var(--color-text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
width: _screenSize.$web-iframe-width;
|
||||
height: _screenSize.$web-iframe-height;
|
||||
transform: scaleX(_screenSize.$web-scale-x) scaleY(_screenSize.$web-scale-y);
|
||||
margin-left: _screenSize.$web-to-wrapper-offset-x;
|
||||
margin-inline-start: _screenSize.$web-to-wrapper-offset-x;
|
||||
margin-top: _screenSize.$web-to-wrapper-offset-y;
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@
|
|||
width: _screenSize.$mobile-iframe-width;
|
||||
height: _screenSize.$mobile-iframe-height;
|
||||
transform: scaleX(_screenSize.$mobile-scale-x) scaleY(_screenSize.$mobile-scale-y);
|
||||
margin-left: _screenSize.$mobile-to-wrapper-offset-x;
|
||||
margin-inline-start: _screenSize.$mobile-to-wrapper-offset-x;
|
||||
margin-top: _screenSize.$mobile-to-wrapper-offset-y;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,7 @@
|
|||
border-radius: 12px 12px 0 0;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
|
||||
> button + button {
|
||||
margin-left: _.unit(3);
|
||||
}
|
||||
gap: _.unit(3);
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
padding: _.unit(3) _.unit(4);
|
||||
|
||||
.icon {
|
||||
margin-right: _.unit(6);
|
||||
margin-inline-end: _.unit(6);
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: _.unit(4);
|
||||
margin-inline-end: _.unit(4);
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
|
|
|
@ -11,8 +11,8 @@ $dropdown-item-height: 40px;
|
|||
display: flex;
|
||||
align-items: center;
|
||||
padding: _.unit(1);
|
||||
padding-left: _.unit(2);
|
||||
margin-left: _.unit(4);
|
||||
padding-inline-start: _.unit(2);
|
||||
margin-inline-start: _.unit(4);
|
||||
max-width: 500px;
|
||||
border-radius: _.unit(2);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
@ -62,6 +62,13 @@ $dropdown-item-height: 40px;
|
|||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
&::before {
|
||||
left: unset;
|
||||
right: _.unit(-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { OrganizationInvitationStatus } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -20,7 +21,7 @@ import TenantInvitationDropdownItem from './TenantInvitationDropdownItem';
|
|||
import styles from './index.module.scss';
|
||||
|
||||
export default function TenantSelector() {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { t, i18n } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const {
|
||||
tenants,
|
||||
prependTenant,
|
||||
|
@ -44,7 +45,7 @@ export default function TenantSelector() {
|
|||
<div
|
||||
ref={anchorRef}
|
||||
tabIndex={0}
|
||||
className={styles.currentTenantCard}
|
||||
className={classNames(styles.currentTenantCard, styles[i18n.dir()])}
|
||||
role="button"
|
||||
onKeyDown={onKeyDownHandler(() => {
|
||||
setShowDropdown(true);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
font: var(--font-body-2);
|
||||
border-radius: _.unit(2);
|
||||
cursor: pointer;
|
||||
gap: _.unit(4);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-hover);
|
||||
|
@ -22,7 +23,6 @@
|
|||
|
||||
.title {
|
||||
font: var(--font-body-2);
|
||||
margin-left: _.unit(4);
|
||||
}
|
||||
|
||||
.menu {
|
||||
|
@ -38,6 +38,11 @@
|
|||
&.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
right: unset;
|
||||
left: calc(100% + 5px);
|
||||
}
|
||||
}
|
||||
|
||||
.menuOption {
|
||||
|
@ -55,4 +60,13 @@
|
|||
left: 8px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
padding: _.unit(2.5) _.unit(8) _.unit(2.5) _.unit(5.5);
|
||||
|
||||
.tick {
|
||||
left: unset;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import type { AdminConsoleKey } from '@logto/phrases';
|
||||
import classNames from 'classnames';
|
||||
import { type ReactNode, useCallback, useState, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ArrowRight from '@/assets/icons/arrow-right.svg?react';
|
||||
import Tick from '@/assets/icons/tick.svg?react';
|
||||
import { DropdownItem } from '@/ds-components/Dropdown';
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
import type { Option } from '@/ds-components/Select';
|
||||
import Spacer from '@/ds-components/Spacer';
|
||||
|
@ -42,6 +44,7 @@ function SubMenu<T extends string>({
|
|||
const mouseEnterTimeoutRef = useRef(0);
|
||||
const mouseLeaveTimeoutRef = useRef(0);
|
||||
const [menuHeight, setMenuHeight] = useState<number>();
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const calculateDropdownHeight = useCallback(() => {
|
||||
if (anchorRef.current) {
|
||||
|
@ -90,9 +93,11 @@ function SubMenu<T extends string>({
|
|||
<DynamicT forKey={title} />
|
||||
</span>
|
||||
<Spacer />
|
||||
<ArrowRight className={styles.icon} />
|
||||
<FlipOnRtl>
|
||||
<ArrowRight className={styles.icon} />
|
||||
</FlipOnRtl>
|
||||
<OverlayScrollbar
|
||||
className={classNames(styles.menu, showMenu && styles.visible)}
|
||||
className={classNames(styles.menu, showMenu && styles.visible, styles[i18n.dir()])}
|
||||
style={{ maxHeight: menuHeight }}
|
||||
>
|
||||
{options.map(({ value, title }) => {
|
||||
|
@ -104,7 +109,8 @@ function SubMenu<T extends string>({
|
|||
className={classNames(
|
||||
styles.menuOption,
|
||||
selected && styles.selected,
|
||||
menuItemClassName
|
||||
menuItemClassName,
|
||||
styles[i18n.dir()]
|
||||
)}
|
||||
onClick={() => {
|
||||
onItemClick(value);
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
padding: _.unit(2);
|
||||
margin-left: _.unit(4);
|
||||
margin-inline-start: _.unit(4);
|
||||
border-radius: 8px;
|
||||
|
||||
.image {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
border-radius: 6px;
|
||||
@include _.shimmering-animation;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,6 @@
|
|||
}
|
||||
|
||||
.spinner {
|
||||
margin-left: _.unit(6);
|
||||
margin-inline-start: _.unit(6);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import UserInfoCard from '@/components/UserInfoCard';
|
|||
import { isCloud } from '@/consts/env';
|
||||
import Divider from '@/ds-components/Divider';
|
||||
import Dropdown, { DropdownItem } from '@/ds-components/Dropdown';
|
||||
import FlipOnRtl from '@/ds-components/FlipOnRtl';
|
||||
import Spacer from '@/ds-components/Spacer';
|
||||
import { Ring as Spinner } from '@/ds-components/Spinner';
|
||||
import useCurrentUser from '@/hooks/use-current-user';
|
||||
|
@ -90,7 +91,9 @@ function UserInfo() {
|
|||
{t('menu.profile')}
|
||||
<Spacer />
|
||||
<div className={styles.icon}>
|
||||
<ExternalLinkIcon />
|
||||
<FlipOnRtl>
|
||||
<ExternalLinkIcon />
|
||||
</FlipOnRtl>
|
||||
</div>
|
||||
</DropdownItem>
|
||||
<Divider />
|
||||
|
@ -132,7 +135,11 @@ function UserInfo() {
|
|||
<Divider />
|
||||
<DropdownItem
|
||||
className={classNames(styles.dropdownItem, isLoading && styles.loading)}
|
||||
icon={<SignOut className={styles.icon} />}
|
||||
icon={
|
||||
<FlipOnRtl>
|
||||
<SignOut className={styles.icon} />
|
||||
</FlipOnRtl>
|
||||
}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
user-select: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
margin-left: _.unit(-1);
|
||||
margin-inline-start: _.unit(-1);
|
||||
text-decoration: none;
|
||||
gap: _.unit(1);
|
||||
font: var(--font-label-2);
|
||||
|
@ -71,5 +71,5 @@
|
|||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-error);
|
||||
margin-left: _.unit(0.5);
|
||||
margin-inline-start: _.unit(0.5);
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
|
||||
.infoContent {
|
||||
font: var(--font-label-2);
|
||||
padding-left: _.unit(1);
|
||||
padding-inline-start: _.unit(1);
|
||||
}
|
||||
|
||||
.operation {
|
||||
padding-left: _.unit(1);
|
||||
padding-inline-start: _.unit(1);
|
||||
}
|
||||
|
||||
.eyeIcon {
|
||||
|
|
|
@ -45,7 +45,11 @@
|
|||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: cover;
|
||||
transform-origin: 0 0;
|
||||
transform-origin: top left;
|
||||
|
||||
&.rtl {
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
&.micro {
|
||||
transform: scale(0.416);
|
||||
|
@ -90,6 +94,6 @@
|
|||
.value {
|
||||
// Fixed font color should used in Tooltip component as the color does not change when theme changes.
|
||||
color: #f7f8f8;
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ function UserInfoTipContent({ user }: { readonly user: Partial<UserInfo> }) {
|
|||
}
|
||||
|
||||
function UserAvatar({ className, size = 'medium', user, hasTooltip = false }: Props) {
|
||||
const avatarClassName = classNames(styles.avatar, styles[size]);
|
||||
const { i18n } = useTranslation();
|
||||
const avatarClassName = classNames(styles.avatar, styles[size], styles[i18n.dir()]);
|
||||
const wrapperClassName = classNames(styles.wrapper, styles[size], className);
|
||||
const defaultColorPalette = [
|
||||
'#E74C3C',
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
align-items: center;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
gap: _.unit(3);
|
||||
}
|
||||
|
||||
.nameWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: _.unit(3);
|
||||
|
||||
.name {
|
||||
font: var(--font-label-2);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
@include _.text-ellipsis;
|
||||
|
||||
span {
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,6 @@
|
|||
.errorMessage {
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-error);
|
||||
margin-left: _.unit(0.5);
|
||||
margin-inline-start: _.unit(0.5);
|
||||
margin-top: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
background: none;
|
||||
border: none;
|
||||
width: calc(100% - _.unit(10));
|
||||
gap: _.unit(4);
|
||||
|
||||
&.rtl {
|
||||
margin: _.unit(1) _.unit(4) _.unit(1) _.unit(6);
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: _.unit(5);
|
||||
|
@ -38,10 +43,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
> div + div {
|
||||
margin-left: _.unit(4);
|
||||
}
|
||||
|
||||
.title {
|
||||
font: var(--font-label-2);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||
};
|
||||
|
||||
function Item({ icon, titleKey, modal, externalLink, isActive = false }: Props) {
|
||||
const { t } = useTranslation(undefined, {
|
||||
const { t, i18n } = useTranslation(undefined, {
|
||||
keyPrefix: 'admin_console.tabs',
|
||||
});
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
@ -37,7 +37,7 @@ function Item({ icon, titleKey, modal, externalLink, isActive = false }: Props)
|
|||
return (
|
||||
<>
|
||||
<button
|
||||
className={styles.row}
|
||||
className={classNames(styles.row, styles[i18n.dir()])}
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
|
@ -60,7 +60,10 @@ function Item({ icon, titleKey, modal, externalLink, isActive = false }: Props)
|
|||
}
|
||||
|
||||
return (
|
||||
<Link to={getPath(titleKey)} className={classNames(styles.row, isActive && styles.active)}>
|
||||
<Link
|
||||
to={getPath(titleKey)}
|
||||
className={classNames(styles.row, isActive && styles.active, styles[i18n.dir()])}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
height: 100%;
|
||||
padding: 0 _.unit(6) 0 _.unit(2);
|
||||
|
||||
&.rtl {
|
||||
padding: 0 _.unit(2) 0 _.unit(6);
|
||||
}
|
||||
|
||||
> :not(svg) {
|
||||
@include _.main-content-width;
|
||||
}
|
||||
|
@ -30,6 +34,11 @@
|
|||
position: absolute;
|
||||
bottom: _.unit(3);
|
||||
left: _.unit(4);
|
||||
|
||||
&.rtl {
|
||||
left: unset;
|
||||
right: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
||||
.daisy {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOutletContext, useRoutes } from 'react-router-dom';
|
||||
import { safeLazy } from 'react-safe-lazy';
|
||||
|
||||
|
@ -21,6 +23,8 @@ function ConsoleContent() {
|
|||
const { scrollableContent } = useOutletContext<AppContentOutletContext>();
|
||||
const routeObjects = useConsoleRoutes();
|
||||
const routes = useRoutes(routeObjects);
|
||||
const { i18n } = useTranslation();
|
||||
const direction = i18n.dir();
|
||||
|
||||
usePlausiblePageview(routeObjects, ':tenantId');
|
||||
// Use this hook here to make sure console listens to user tenant scope changes.
|
||||
|
@ -32,12 +36,17 @@ function ConsoleContent() {
|
|||
<Sidebar />
|
||||
</Suspense>
|
||||
<OverlayScrollbar className={styles.overlayScrollbarWrapper}>
|
||||
<div ref={scrollableContent} className={styles.main}>
|
||||
<div ref={scrollableContent} className={classNames(styles.main, styles[direction])}>
|
||||
<Suspense fallback={<DelayedSuspenseFallback />}>{routes}</Suspense>
|
||||
</div>
|
||||
</OverlayScrollbar>
|
||||
{isDevFeaturesEnabled && (
|
||||
<Tag type="state" status="success" variant="plain" className={styles.devStatus}>
|
||||
<Tag
|
||||
type="state"
|
||||
status="success"
|
||||
variant="plain"
|
||||
className={classNames(styles.devStatus, styles[direction])}
|
||||
>
|
||||
Dev features enabled
|
||||
</Tag>
|
||||
)}
|
||||
|
|
|
@ -205,7 +205,7 @@
|
|||
|
||||
&.text {
|
||||
background: none;
|
||||
border-color: none;
|
||||
border-color: transparent;
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text-link);
|
||||
padding: _.unit(0.5) _.unit(1);
|
||||
|
|
|
@ -58,7 +58,7 @@ function CardTitle({
|
|||
{learnMoreLink?.href && (
|
||||
<>
|
||||
{/* Use a space to keep the link and the text separate.
|
||||
* Avoid using `margin-left` since it will cause an unexpected gap when the "learn more" text is at the start of a new line
|
||||
* Avoid using `margin-inline-start` since it will cause an unexpected gap when the "learn more" text is at the start of a new line
|
||||
*/}{' '}
|
||||
<TextLink href={learnMoreLink.href} targetBlank={learnMoreLink.targetBlank}>
|
||||
{t('general.learn_more')}
|
||||
|
|
|
@ -44,12 +44,12 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
gap: _.unit(2);
|
||||
}
|
||||
|
||||
.tooltipAnchor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: _.unit(2);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
background: #34353f;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
direction: ltr;
|
||||
|
||||
.title {
|
||||
margin-top: _.unit(-2);
|
||||
|
|
|
@ -108,9 +108,9 @@ function CodeEditor({
|
|||
value={value}
|
||||
style={
|
||||
isShowingPlaceholder
|
||||
? { marginLeft: '8px', width: 'calc(100% - 8px)' }
|
||||
? { marginInlineStart: '8px', width: 'calc(100% - 8px)' }
|
||||
: {
|
||||
marginLeft: `calc(${maxLineNumberDigits}ch + 20px)`,
|
||||
marginInlineStart: `calc(${maxLineNumberDigits}ch + 20px)`,
|
||||
width: `calc(100% - ${maxLineNumberDigits}ch - 20px)`,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,17 @@ export const lineNumberContainerStyle = (): CSSProperties => {
|
|||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
textAlign: 'right',
|
||||
paddingLeft: '0px',
|
||||
paddingRight: '0px',
|
||||
paddingInlineStart: '0px',
|
||||
paddingInlineEnd: '0px',
|
||||
};
|
||||
};
|
||||
|
||||
export const lineNumberStyle = (numberOfLines: number): CSSProperties => {
|
||||
return {
|
||||
minWidth: `calc(${numberOfLines}ch + 20px)`,
|
||||
marginLeft: '0px',
|
||||
paddingRight: '20px',
|
||||
paddingLeft: '0px',
|
||||
marginInlineStart: '0px',
|
||||
paddingInlineEnd: '20px',
|
||||
paddingInlineStart: '0px',
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'flex-end',
|
||||
counterIncrement: 'line',
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
font: var(--font-body-2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: _.unit(2);
|
||||
|
||||
&:focus,
|
||||
&.highlight {
|
||||
|
@ -25,7 +26,6 @@
|
|||
height: 24px;
|
||||
border: 1px solid var(--color-divider);
|
||||
border-radius: 4px;
|
||||
margin-right: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: _.unit(2);
|
||||
margin-right: _.unit(1);
|
||||
margin-inline-end: _.unit(1);
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&.wrapContent {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
overflow: hidden;
|
||||
|
||||
.caret {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
}
|
||||
|
||||
.name {
|
||||
|
@ -28,7 +28,7 @@
|
|||
flex-shrink: 0;
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,5 +39,5 @@
|
|||
}
|
||||
|
||||
.dataList {
|
||||
padding-left: _.unit(10);
|
||||
padding-inline-start: _.unit(10);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
gap: _.unit(4);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-hover);
|
||||
|
@ -20,6 +21,5 @@
|
|||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.flip {
|
||||
transform: scaleX(-1);
|
||||
}
|
38
packages/console/src/ds-components/FlipOnRtl/index.tsx
Normal file
38
packages/console/src/ds-components/FlipOnRtl/index.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import classNames from 'classnames';
|
||||
import { cloneElement, isValidElement, type ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
readonly children: ReactElement<HTMLElement>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This component flips its child element horizontally if the browser's text direction is RTL (right-to-left).
|
||||
*
|
||||
* @component
|
||||
* @example
|
||||
* ```tsx
|
||||
* <FlipOnRtl>
|
||||
* <SVG />
|
||||
* </FlipOnRtl>
|
||||
* ```
|
||||
*
|
||||
* @param {React.ReactNode} children - The SVG or other HTML content to render and flip if RTL.
|
||||
* @returns {JSX.Element} The flipped content.
|
||||
*/
|
||||
function FlipOnRtl({ children }: Props) {
|
||||
const { i18n } = useTranslation();
|
||||
const isRtl = i18n.dir() === 'rtl';
|
||||
|
||||
if (!isValidElement(children)) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return cloneElement(children, {
|
||||
className: classNames(children.props.className, isRtl && styles.flip),
|
||||
});
|
||||
}
|
||||
|
||||
export default FlipOnRtl;
|
|
@ -20,14 +20,14 @@
|
|||
color: var(--color-text);
|
||||
|
||||
.multiple {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.toggleTipButton {
|
||||
margin-left: _.unit(0.5);
|
||||
margin-inline-start: _.unit(0.5);
|
||||
}
|
||||
|
||||
.required {
|
||||
|
@ -38,7 +38,7 @@
|
|||
.tagsWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
gap: _.unit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
border-color: none;
|
||||
border-color: transparent;
|
||||
color: var(--color-primary);
|
||||
border-radius: 6px;
|
||||
font: var(--font-label-2);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
justify-content: flex-end;
|
||||
|
||||
> :not(:first-child) {
|
||||
margin-left: _.unit(4);
|
||||
margin-inline-start: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
}
|
||||
|
||||
.firstFieldWithMultiInputs {
|
||||
padding-right: _.unit(9);
|
||||
padding-inline-end: _.unit(9);
|
||||
}
|
||||
|
||||
.deletableInput {
|
||||
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
|
||||
> :not(:first-child) {
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import classNames from 'classnames';
|
||||
import type { OverlayScrollbarsComponentProps } from 'overlayscrollbars-react';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function OverlayScrollbar(props: OverlayScrollbarsComponentProps) {
|
||||
const { i18n } = useTranslation();
|
||||
const isRtl = i18n.dir() === 'rtl';
|
||||
|
||||
return (
|
||||
<OverlayScrollbarsComponent
|
||||
options={{ scrollbars: { autoHide: 'leave', autoHideDelay: 0 } }}
|
||||
{...props}
|
||||
className={classNames(props.className, isRtl && 'os-scrollbar-rtl')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
list-style: none;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
|
||||
.button {
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: _.unit(2);
|
||||
|
||||
.indicator {
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--color-neutral-60);
|
||||
display: inline-block;
|
||||
margin-right: _.unit(2);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
|
@ -33,7 +33,7 @@
|
|||
|
||||
.icon,
|
||||
.trailingIcon {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
> svg {
|
||||
|
@ -42,8 +42,8 @@
|
|||
}
|
||||
|
||||
.trailingIcon {
|
||||
margin-right: unset;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-end: unset;
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@
|
|||
border-radius: unset;
|
||||
border: unset;
|
||||
display: block;
|
||||
margin-right: unset;
|
||||
margin-inline-end: unset;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
@ -82,7 +82,7 @@
|
|||
|
||||
.icon,
|
||||
.trailingIcon {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
vertical-align: middle;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
|
@ -92,8 +92,8 @@
|
|||
}
|
||||
|
||||
.trailingIcon {
|
||||
margin-right: unset;
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-end: unset;
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
|
||||
.disabledLabel {
|
||||
|
@ -104,6 +104,11 @@
|
|||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
&.rtl .content .indicator {
|
||||
right: unset;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.compact {
|
||||
|
@ -128,16 +133,31 @@
|
|||
margin-bottom: unset;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
&:first-child {
|
||||
border-radius: 0 12px 12px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 12px 0 0 12px;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid var(--color-border);
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: _.unit(5);
|
||||
height: 100%;
|
||||
|
||||
.icon {
|
||||
margin-right: _.unit(4);
|
||||
margin-inline-end: _.unit(4);
|
||||
}
|
||||
|
||||
.trailingIcon {
|
||||
margin-left: _.unit(4);
|
||||
margin-inline-start: _.unit(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +185,21 @@
|
|||
margin-bottom: unset;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
&:first-child {
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid var(--color-border);
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
@ -219,6 +254,17 @@
|
|||
bottom: -1px;
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid var(--color-primary);
|
||||
|
||||
&::before {
|
||||
left: unset;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.small.checked {
|
||||
|
@ -235,6 +281,17 @@
|
|||
bottom: -1px;
|
||||
background-color: var(--color-text-link);
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
&:not(:first-child) {
|
||||
border-color: var(--color-text-link);
|
||||
|
||||
&::before {
|
||||
left: unset;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { AdminConsoleKey } from '@logto/phrases';
|
|||
import classNames from 'classnames';
|
||||
import type { KeyboardEventHandler, ReactElement, ReactNode } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type DangerousRaw from '../DangerousRaw';
|
||||
import DynamicT from '../DynamicT';
|
||||
|
@ -52,6 +53,7 @@ function Radio({
|
|||
trailingIcon,
|
||||
hasCheckIconForCard = true,
|
||||
}: Props) {
|
||||
const { i18n } = useTranslation();
|
||||
const handleKeyPress: KeyboardEventHandler<HTMLDivElement> = useCallback(
|
||||
(event) => {
|
||||
if (isDisabled) {
|
||||
|
@ -73,7 +75,8 @@ function Radio({
|
|||
styles[type],
|
||||
isChecked && styles.checked,
|
||||
isDisabled && styles.disabled,
|
||||
className
|
||||
className,
|
||||
styles[i18n.dir()]
|
||||
)}
|
||||
// eslint-disable-next-line jsx-a11y/role-has-required-aria-props
|
||||
role="radio"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
align-items: center;
|
||||
|
||||
>:not(:first-child) {
|
||||
margin-left: _.unit(2);
|
||||
margin-inline-start: _.unit(2);
|
||||
}
|
||||
|
||||
.searchIcon {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 _.unit(2) 0 _.unit(3);
|
||||
padding: 0 _.unit(2);
|
||||
background: var(--color-layer-1);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
|
@ -46,7 +46,7 @@
|
|||
.delete {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: _.unit(-0.5);
|
||||
margin-inline-end: _.unit(-0.5);
|
||||
}
|
||||
|
||||
input {
|
||||
|
@ -63,6 +63,7 @@
|
|||
}
|
||||
|
||||
.title {
|
||||
padding: 0 _.unit(1);
|
||||
@include _.text-ellipsis;
|
||||
}
|
||||
|
||||
|
@ -88,7 +89,6 @@
|
|||
|
||||
.icon {
|
||||
display: flex;
|
||||
margin-left: _.unit(1);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@
|
|||
align-items: center;
|
||||
|
||||
.search {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
color: var(--color-text-secondary);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
|
|
@ -76,7 +76,8 @@ function Select<T extends string>({
|
|||
}
|
||||
const element = anchorRef.current;
|
||||
const cs = getComputedStyle(element);
|
||||
const paddingX = Number.parseFloat(cs.paddingLeft) + Number.parseFloat(cs.paddingRight);
|
||||
const paddingX =
|
||||
Number.parseFloat(cs.paddingInlineStart) + Number.parseFloat(cs.paddingInlineEnd);
|
||||
const paddingY = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
|
||||
const borderX = Number.parseFloat(cs.borderLeftWidth) + Number.parseFloat(cs.borderRightWidth);
|
||||
const borderY = Number.parseFloat(cs.borderTopWidth) + Number.parseFloat(cs.borderBottomWidth);
|
||||
|
@ -92,7 +93,7 @@ function Select<T extends string>({
|
|||
left: `${
|
||||
element.getBoundingClientRect().left +
|
||||
Number.parseFloat(cs.borderLeftWidth) +
|
||||
Number.parseFloat(cs.paddingLeft)
|
||||
Number.parseFloat(cs.paddingInlineStart)
|
||||
}px`,
|
||||
backgroundColor: cs.backgroundColor,
|
||||
};
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.rtl .slider::before {
|
||||
left: unset;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: var(--color-success-70);
|
||||
}
|
||||
|
@ -50,6 +55,10 @@
|
|||
background-color: var(--color-specific-toggle-thumb-disabled);
|
||||
box-shadow: unset;
|
||||
}
|
||||
|
||||
&.rtl input:checked + .slider::before {
|
||||
transform: translateX(-16px);
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { type AdminConsoleKey } from '@logto/phrases';
|
|||
import classNames from 'classnames';
|
||||
import type { HTMLProps, ReactElement, ReactNode, Ref } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import DynamicT from '../DynamicT';
|
||||
|
||||
|
@ -20,6 +21,7 @@ type Props =
|
|||
|
||||
function Switch(props: Props, ref?: Ref<HTMLInputElement>) {
|
||||
const { label, hasError, ...rest } = props;
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.wrapper, hasError && styles.error)}>
|
||||
|
@ -33,7 +35,7 @@ function Switch(props: Props, ref?: Ref<HTMLInputElement>) {
|
|||
</div>
|
||||
)}
|
||||
{label && <div className={styles.label}>{label}</div>}
|
||||
<label className={styles.switch}>
|
||||
<label className={classNames(styles.switch, styles[i18n.dir()])}>
|
||||
<input type="checkbox" {...rest} ref={ref} />
|
||||
<span className={styles.slider} />
|
||||
</label>
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: _.unit(6);
|
||||
}
|
||||
|
||||
.link {
|
||||
font: var(--font-label-2);
|
||||
padding: _.unit(0.5) _.unit(1.5);
|
||||
|
@ -48,7 +44,7 @@
|
|||
}
|
||||
|
||||
.errors {
|
||||
margin-left: _.unit(0.5);
|
||||
margin-inline-start: _.unit(0.5);
|
||||
font: var(--font-label-3);
|
||||
color: var(--color-white);
|
||||
padding: _.unit(0.5) _.unit(1.5);
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
border-bottom: 1px solid var(--color-surface-5);
|
||||
display: flex;
|
||||
margin-top: _.unit(1);
|
||||
gap: _.unit(6);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: _.unit(4);
|
||||
margin-inline-end: _.unit(4);
|
||||
border-radius: 12px;
|
||||
flex-shrink: 0;
|
||||
@include _.shimmering-animation;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
color: var(--color-text);
|
||||
border-bottom: unset;
|
||||
padding: _.unit(3);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +104,11 @@
|
|||
border: 1px solid var(--color-divider);
|
||||
|
||||
tr th:first-child {
|
||||
padding-left: _.unit(7);
|
||||
padding-inline-start: _.unit(7);
|
||||
}
|
||||
|
||||
tr th:last-child {
|
||||
padding-right: _.unit(7);
|
||||
padding-inline-end: _.unit(7);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,11 +126,11 @@
|
|||
}
|
||||
|
||||
tr td:first-child {
|
||||
padding-left: _.unit(7);
|
||||
padding-inline-start: _.unit(7);
|
||||
}
|
||||
|
||||
tr td:last-child {
|
||||
padding-right: _.unit(7);
|
||||
padding-inline-end: _.unit(7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,3 +174,17 @@
|
|||
.pagination {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
|
||||
.container.rtl {
|
||||
.tableContainer {
|
||||
tr.hoverEffect:hover {
|
||||
td:first-child {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
|||
import type { ReactNode } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import type { FieldPath, FieldValues } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { Props as PaginationProps } from '@/ds-components/Pagination';
|
||||
import Pagination from '@/ds-components/Pagination';
|
||||
|
@ -98,6 +99,7 @@ function Table<
|
|||
onRetry,
|
||||
footer,
|
||||
}: Props<TFieldValues, TName>) {
|
||||
const { i18n } = useTranslation();
|
||||
const totalColspan = columns.reduce((result, { colSpan }) => {
|
||||
return result + (colSpan ?? 1);
|
||||
}, 0);
|
||||
|
@ -108,7 +110,7 @@ function Table<
|
|||
const isLoaded = !isLoading && hasData;
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
<div className={classNames(styles.container, className, styles[i18n.dir()])}>
|
||||
<div className={classNames(styles.tableContainer, hasBorder && styles.hasBorder)}>
|
||||
{filter && (
|
||||
<div className={styles.filterContainer}>
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
font: var(--font-body-2);
|
||||
gap: _.unit(2);
|
||||
@include _.text-ellipsis;
|
||||
|
||||
.icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: _.unit(2);
|
||||
background: var(--color-on-success-container);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
top: 1px;
|
||||
bottom: 1px;
|
||||
|
||||
&.rtl {
|
||||
right: unset;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.button {
|
||||
user-select: none;
|
||||
width: 32px;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import CaretDown from '@/assets/icons/caret-down.svg?react';
|
||||
import CaretUp from '@/assets/icons/caret-up.svg?react';
|
||||
|
@ -47,6 +48,7 @@ type Props = Omit<ComponentProps<typeof TextInput>, 'type' | 'suffix'> & {
|
|||
|
||||
/** A numeric text input with up and down buttons for incrementing and decrementing the value. */
|
||||
function NumericInput({ onValueUp, onValueDown, ...props }: Props) {
|
||||
const { i18n } = useTranslation();
|
||||
const isDisabled = Boolean(props.disabled) || Boolean(props.readOnly);
|
||||
|
||||
return (
|
||||
|
@ -55,7 +57,7 @@ function NumericInput({ onValueUp, onValueDown, ...props }: Props) {
|
|||
alwaysShowSuffix
|
||||
type="number"
|
||||
suffix={
|
||||
<div className={styles.container}>
|
||||
<div className={classNames(styles.container, styles[i18n.dir()])}>
|
||||
<Button
|
||||
className={styles.up}
|
||||
isDisabled={
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
height: 36px;
|
||||
background-color: var(--color-layer-1);
|
||||
font: var(--font-body-2);
|
||||
gap: _.unit(2);
|
||||
|
||||
&.withIcon {
|
||||
display: flex;
|
||||
|
@ -38,7 +39,6 @@
|
|||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
// Leave space for scrollbar by setting `box-sizing` and `padding-right`
|
||||
box-sizing: content-box;
|
||||
padding-right: 10px;
|
||||
padding-inline-end: 10px;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-placeholder);
|
||||
|
|
|
@ -29,6 +29,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
.delete {
|
||||
left: _.unit(2);
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.delete {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { AllowedUploadMimeType } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Delete from '@/assets/icons/delete.svg?react';
|
||||
import ImageWithErrorFallback from '@/ds-components/ImageWithErrorFallback';
|
||||
|
@ -29,9 +30,13 @@ function ImageUploader({
|
|||
uploadedClassName,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { i18n } = useTranslation();
|
||||
const { allowedMimeTypes } = useImageMimeTypes(imageMimeTypes);
|
||||
|
||||
return value ? (
|
||||
<div className={classNames(styles.imageUploader, className, uploadedClassName)}>
|
||||
<div
|
||||
className={classNames(styles.imageUploader, className, uploadedClassName, styles[i18n.dir()])}
|
||||
>
|
||||
<ImageWithErrorFallback
|
||||
containerClassName={styles.container}
|
||||
src={value}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
cursor: pointer;
|
||||
|
||||
.arrow {
|
||||
margin-right: _.unit(2);
|
||||
margin-inline-end: _.unit(2);
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
position: sticky;
|
||||
top: _.unit(6);
|
||||
flex-shrink: 0;
|
||||
margin-right: _.unit(7.5);
|
||||
margin-inline-end: _.unit(7.5);
|
||||
width: 220px;
|
||||
|
||||
> :not(:last-child) {
|
||||
|
@ -61,7 +61,7 @@
|
|||
@media screen and (max-width: dim.$guide-content-max-width) {
|
||||
.content {
|
||||
margin: 0;
|
||||
padding-right: dim.$guide-content-padding;
|
||||
padding-inline-end: dim.$guide-content-padding;
|
||||
max-width: calc(dim.$guide-main-content-max-width + dim.$guide-sidebar-width + dim.$guide-panel-gap + 2 * dim.$guide-content-padding);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
display: flex;
|
||||
margin: _.unit(1) 0;
|
||||
padding: 0;
|
||||
gap: _.unit(6);
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
margin-right: _.unit(6);
|
||||
padding-bottom: _.unit(1);
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text-secondary);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.icon {
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: _.unit(4);
|
||||
margin-inline-end: _.unit(4);
|
||||
vertical-align: middle;
|
||||
|
||||
> svg {
|
||||
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
|
||||
.trailingTag {
|
||||
margin-left: _.unit(1);
|
||||
margin-inline-start: _.unit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
justify-content: space-between;
|
||||
|
||||
.inspireContent {
|
||||
margin-right: _.unit(6);
|
||||
margin-inline-end: _.unit(6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
|||
.button {
|
||||
border-color: var(--color-neutral-variant-80);
|
||||
// Note: this is a special case for the inspire me button since the bulb icon does not follow the standard icon size
|
||||
padding-right: _.unit(7);
|
||||
padding-inline-end: _.unit(7);
|
||||
|
||||
&:not(:disabled) {
|
||||
&:not(:active),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue