mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
style(ui): add dark mode social icon (#1265)
* style(ui): add dark mode social icon add dark mode social icon * style(ui): social landing icon social landing icon * fix(ui): dark connector icon cr update dark connector icon cr update * fix(ui): cr fix cr fix
This commit is contained in:
parent
5e819665c7
commit
a5e44cf478
10 changed files with 85 additions and 55 deletions
|
@ -1,6 +1,5 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
|
||||
.socialButton {
|
||||
border-radius: 50%;
|
||||
@include _.flex-column;
|
||||
|
@ -9,6 +8,10 @@
|
|||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
&.inverse {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
@ -20,13 +23,11 @@
|
|||
|
||||
:global(body.desktop) {
|
||||
.socialButton {
|
||||
background: var(--color-layer);
|
||||
|
||||
&:active {
|
||||
&:not(.inverse):active {
|
||||
background: var(--color-pressed);
|
||||
}
|
||||
|
||||
&:hover:not(:active) {
|
||||
&:not(.inverse):hover:not(:active) {
|
||||
background: var(--color-hover);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import { ConnectorData } from '@/types';
|
||||
import { isAppleConnector } from '@/utils/social-connectors';
|
||||
|
||||
import * as styles from './SocialIconButton.module.scss';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
connector: ConnectorData;
|
||||
logo: string;
|
||||
target?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const SocialIconButton = ({ className, connector, onClick }: Props) => {
|
||||
const { target, logo } = connector;
|
||||
|
||||
return (
|
||||
<button className={classNames(styles.socialButton, className)} onClick={onClick}>
|
||||
{logo && <img src={logo} alt={target} className={styles.icon} />}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
const SocialIconButton = ({ className, logo, target, onClick }: Props) => (
|
||||
<button
|
||||
className={classNames(
|
||||
styles.socialButton,
|
||||
isAppleConnector(target) && styles.inverse,
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{logo && <img src={logo} alt={target} className={styles.icon} />}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default SocialIconButton;
|
||||
|
|
|
@ -2,28 +2,25 @@ import classNames from 'classnames';
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ConnectorData } from '@/types';
|
||||
|
||||
import * as socialLinkButtonStyles from './SocialLinkButton.module.scss';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type Props = {
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
connector: ConnectorData;
|
||||
target: string;
|
||||
logo: string;
|
||||
name: Record<string, string>;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const SocialLinkButton = ({ isDisabled, className, connector, onClick }: Props) => {
|
||||
const { target, name, logo } = connector;
|
||||
|
||||
const SocialLinkButton = ({ isDisabled, className, target, name, logo, onClick }: Props) => {
|
||||
const {
|
||||
t,
|
||||
i18n: { language },
|
||||
} = useTranslation();
|
||||
// TODO: LOG-2393 should fix name[locale] syntax error
|
||||
const foundName = Object.entries(name).find(([lang]) => lang === language);
|
||||
const localName = foundName ? foundName[1] : name.en;
|
||||
|
||||
const localName = name[language] ?? target;
|
||||
|
||||
return (
|
||||
<button
|
||||
|
|
|
@ -28,7 +28,12 @@ describe('Button Component', () => {
|
|||
it('render SocialLinkButton', () => {
|
||||
const connector = mockSocialConnectorData;
|
||||
const { queryByText, container } = render(
|
||||
<SocialLinkButton connector={connector} onClick={onClick} />
|
||||
<SocialLinkButton
|
||||
logo={connector.logo}
|
||||
name={connector.name}
|
||||
target={connector.target}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(queryByText('action.sign_in_with')).not.toBeNull();
|
||||
|
|
|
@ -13,13 +13,17 @@ type Props = {
|
|||
};
|
||||
|
||||
const SocialLanding = ({ className, connectorId, isLoading = false }: Props) => {
|
||||
const { experienceSettings } = useContext(PageContext);
|
||||
const { experienceSettings, theme } = useContext(PageContext);
|
||||
const connector = experienceSettings?.socialConnectors.find(({ id }) => id === connectorId);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
<div className={styles.connector}>
|
||||
{connector?.logo ? <img src={connector.logo} /> : connectorId}
|
||||
{connector ? (
|
||||
<img src={theme === 'dark' ? connector.logoDark ?? connector.logo : connector.logo} />
|
||||
) : (
|
||||
connectorId
|
||||
)}
|
||||
</div>
|
||||
{isLoading && <LoadingIcon />}
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@ const SocialSignInDropdown = ({ isOpen, onClose, connectors, anchorRef }: Props)
|
|||
i18n: { language },
|
||||
} = useTranslation();
|
||||
const [contentStyle, setContentStyle] = useState<{ top?: number; left?: number }>();
|
||||
const { invokeSocialSignIn } = useSocial();
|
||||
const { invokeSocialSignIn, theme } = useSocial();
|
||||
|
||||
const adjustPosition = useCallback(() => {
|
||||
if (anchorRef?.current) {
|
||||
|
@ -46,7 +46,7 @@ const SocialSignInDropdown = ({ isOpen, onClose, connectors, anchorRef }: Props)
|
|||
}}
|
||||
>
|
||||
{connectors.map((connector) => {
|
||||
const { id, name, logo } = connector;
|
||||
const { id, name, logo, logoDark } = connector;
|
||||
const languageKey = Object.keys(name).find((key) => key === language) ?? 'en';
|
||||
const localName = name[languageKey as Language];
|
||||
|
||||
|
@ -58,7 +58,11 @@ const SocialSignInDropdown = ({ isOpen, onClose, connectors, anchorRef }: Props)
|
|||
onClose();
|
||||
}}
|
||||
>
|
||||
<img src={logo} alt={id} className={styles.socialLogo} />
|
||||
<img
|
||||
src={theme === 'dark' ? logoDark ?? logo : logo}
|
||||
alt={id}
|
||||
className={styles.socialLogo}
|
||||
/>
|
||||
<span>{localName}</span>
|
||||
</DropdownItem>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ import IconButton from '@/components/Button/IconButton';
|
|||
import SocialIconButton from '@/components/Button/SocialIconButton';
|
||||
import useSocial from '@/hooks/use-social';
|
||||
import { ConnectorData } from '@/types';
|
||||
import { isAppleConnector } from '@/utils/social-connectors';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -24,20 +25,25 @@ const SocialSignInIconList = ({
|
|||
moreButtonRef,
|
||||
onMoreButtonClick,
|
||||
}: Props) => {
|
||||
const { invokeSocialSignIn } = useSocial();
|
||||
const { invokeSocialSignIn, theme } = useSocial();
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.socialIconList, className)}>
|
||||
{connectors.map((connector) => (
|
||||
<SocialIconButton
|
||||
key={connector.id}
|
||||
className={styles.socialButton}
|
||||
connector={connector}
|
||||
onClick={() => {
|
||||
void invokeSocialSignIn(connector);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{connectors.map((connector) => {
|
||||
const { id, target, logo, logoDark } = connector;
|
||||
|
||||
return (
|
||||
<SocialIconButton
|
||||
key={id}
|
||||
className={styles.socialButton}
|
||||
logo={(theme === 'dark' && !isAppleConnector(target) && logoDark) || logo}
|
||||
target={target}
|
||||
onClick={() => {
|
||||
void invokeSocialSignIn(connector);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{hasMore && (
|
||||
<IconButton ref={moreButtonRef} className={styles.moreButton} onClick={onMoreButtonClick}>
|
||||
<MoreSocialIcon />
|
||||
|
|
|
@ -25,7 +25,7 @@ const SocialSignInList = ({
|
|||
onSocialSignInCallback,
|
||||
}: Props) => {
|
||||
const [expand, setExpand] = useState(false);
|
||||
const { invokeSocialSignIn } = useSocial();
|
||||
const { invokeSocialSignIn, theme } = useSocial();
|
||||
const isOverSize = socialConnectors.length > defaultSize;
|
||||
const displayAll = !isOverSize || !isCollapseEnabled;
|
||||
|
||||
|
@ -39,17 +39,23 @@ const SocialSignInList = ({
|
|||
|
||||
return (
|
||||
<div className={classNames(styles.socialLinkList, className)}>
|
||||
{displayConnectors.map((connector) => (
|
||||
<SocialLinkButton
|
||||
key={connector.id}
|
||||
className={styles.socialLinkButton}
|
||||
connector={connector}
|
||||
onClick={() => {
|
||||
void invokeSocialSignIn(connector);
|
||||
onSocialSignInCallback?.();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{displayConnectors.map((connector) => {
|
||||
const { id, name, logo, logoDark, target } = connector;
|
||||
|
||||
return (
|
||||
<SocialLinkButton
|
||||
key={id}
|
||||
className={styles.socialLinkButton}
|
||||
name={name}
|
||||
logo={(theme === 'dark' && logoDark) || logo}
|
||||
target={target}
|
||||
onClick={() => {
|
||||
void invokeSocialSignIn(connector);
|
||||
onSocialSignInCallback?.();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{!displayAll && (
|
||||
<IconButton
|
||||
className={classNames(styles.expandIcon, expand && styles.expanded)}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { PageContext } from './use-page-context';
|
|||
import useTerms from './use-terms';
|
||||
|
||||
const useSocial = () => {
|
||||
const { experienceSettings } = useContext(PageContext);
|
||||
const { experienceSettings, theme } = useContext(PageContext);
|
||||
const { termsValidation } = useTerms();
|
||||
|
||||
const { run: asyncInvokeSocialSignIn } = useApi(invokeSocialSignIn);
|
||||
|
@ -64,6 +64,7 @@ const useSocial = () => {
|
|||
);
|
||||
|
||||
return {
|
||||
theme,
|
||||
socialConnectors: experienceSettings?.socialConnectors ?? [],
|
||||
invokeSocialSignIn: invokeSocialSignInHandler,
|
||||
};
|
||||
|
|
|
@ -196,3 +196,5 @@ export const filterPreviewSocialConnectors = (
|
|||
|
||||
return Array.from(connectorMap.values());
|
||||
};
|
||||
|
||||
export const isAppleConnector = (target?: string) => target === 'apple';
|
||||
|
|
Loading…
Reference in a new issue