0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(console): checkbox component (#2717)

This commit is contained in:
Charles Zhao 2022-12-26 01:54:59 +08:00 committed by GitHub
parent c53e208255
commit e7e656c6d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 163 additions and 133 deletions

View file

@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.66602" y="1.66663" width="16.6667" height="16.6667" rx="4" fill="#7958FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.31476 13.858L5.13295 10.441C4.95568 10.253 4.95568 9.947 5.13295 9.757L5.77568 9.074C5.95295 8.886 6.24113 8.886 6.4184 9.074L8.63657 11.466L13.5811 6.141C13.7584 5.953 14.0465 5.953 14.2238 6.141L14.8665 6.825C15.0438 7.013 15.0438 7.32 14.8665 7.507L8.95748 13.858C8.78021 14.046 8.49203 14.046 8.31476 13.858Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 571 B

View file

@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.66602" y="1.66663" width="16.6667" height="16.6667" rx="4" fill="#5C5F60" />
<path fillRule="evenodd" clipRule="evenodd"
d="M8.31476 13.858L5.13295 10.441C4.95568 10.253 4.95568 9.947 5.13295 9.757L5.77568 9.074C5.95295 8.886 6.24113 8.886 6.4184 9.074L8.63657 11.466L13.5811 6.141C13.7584 5.953 14.0465 5.953 14.2238 6.141L14.8665 6.825C15.0438 7.013 15.0438 7.32 14.8665 7.507L8.95748 13.858C8.78021 14.046 8.49203 14.046 8.31476 13.858Z"
fill="white" />
</svg>

Before

Width:  |  Height:  |  Size: 583 B

View file

@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.66602" y="1.66663" width="16.6667" height="16.6667" rx="4" fill="#C4C7C7" />
<path fillRule="evenodd" clipRule="evenodd"
d="M8.31476 13.858L5.13295 10.441C4.95568 10.253 4.95568 9.947 5.13295 9.757L5.77568 9.074C5.95295 8.886 6.24113 8.886 6.4184 9.074L8.63657 11.466L13.5811 6.141C13.7584 5.953 14.0465 5.953 14.2238 6.141L14.8665 6.825C15.0438 7.013 15.0438 7.32 14.8665 7.507L8.95748 13.858C8.78021 14.046 8.49203 14.046 8.31476 13.858Z"
fill="white" />
</svg>

Before

Width:  |  Height:  |  Size: 583 B

View file

@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.66602" y="1.66663" width="16.6667" height="16.6667" rx="4" fill="#5D34F2" />
<path fillRule="evenodd" clipRule="evenodd"
d="M8.31476 13.858L5.13295 10.441C4.95568 10.253 4.95568 9.947 5.13295 9.757L5.77568 9.074C5.95295 8.886 6.24113 8.886 6.4184 9.074L8.63657 11.466L13.5811 6.141C13.7584 5.953 14.0465 5.953 14.2238 6.141L14.8665 6.825C15.0438 7.013 15.0438 7.32 14.8665 7.507L8.95748 13.858C8.78021 14.046 8.49203 14.046 8.31476 13.858Z"
fill="white" />
</svg>

Before

Width:  |  Height:  |  Size: 583 B

View file

@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2463 3.20984H4.75244C3.90015 3.20984 3.20923 3.90076 3.20923 4.75305V15.2469C3.20923 16.0992 3.90015 16.7901 4.75244 16.7901H15.2463C16.0986 16.7901 16.7895 16.0992 16.7895 15.2469V4.75305C16.7895 3.90076 16.0986 3.20984 15.2463 3.20984ZM4.75244 1.66663C3.04785 1.66663 1.66602 3.04847 1.66602 4.75305V15.2469C1.66602 16.9515 3.04786 18.3333 4.75244 18.3333H15.2463C16.9508 18.3333 18.3327 16.9515 18.3327 15.2469V4.75305C18.3327 3.04846 16.9508 1.66663 15.2463 1.66663H4.75244Z" fill="#A9ACAC"/>
</svg>

Before

Width:  |  Height:  |  Size: 653 B

View file

@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.66602 4.75305C1.66602 3.04847 3.04785 1.66663 4.75244 1.66663H15.2463C16.9508 1.66663 18.3327 3.04846 18.3327 4.75305V15.2469C18.3327 16.9515 16.9508 18.3333 15.2463 18.3333H4.75244C3.04786 18.3333 1.66602 16.9515 1.66602 15.2469V4.75305Z" fill="#191C1D"/>
<path d="M1.66602 4.75305C1.66602 3.04847 3.04785 1.66663 4.75244 1.66663H15.2463C16.9508 1.66663 18.3327 3.04846 18.3327 4.75305V15.2469C18.3327 16.9515 16.9508 18.3333 15.2463 18.3333H4.75244C3.04786 18.3333 1.66602 16.9515 1.66602 15.2469V4.75305Z" fill="#C4C7C7" fill-opacity="0.02"/>
<path d="M1.66602 4.75305C1.66602 3.04847 3.04785 1.66663 4.75244 1.66663H15.2463C16.9508 1.66663 18.3327 3.04846 18.3327 4.75305V15.2469C18.3327 16.9515 16.9508 18.3333 15.2463 18.3333H4.75244C3.04786 18.3333 1.66602 16.9515 1.66602 15.2469V4.75305Z" fill="#CABEFF" fill-opacity="0.14"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2463 3.20984H4.75244C3.90015 3.20984 3.20923 3.90076 3.20923 4.75305V15.2469C3.20923 16.0992 3.90015 16.7901 4.75244 16.7901H15.2463C16.0986 16.7901 16.7895 16.0992 16.7895 15.2469V4.75305C16.7895 3.90076 16.0986 3.20984 15.2463 3.20984ZM4.75244 1.66663C3.04785 1.66663 1.66602 3.04847 1.66602 4.75305V15.2469C1.66602 16.9515 3.04786 18.3333 4.75244 18.3333H15.2463C16.9508 18.3333 18.3327 16.9515 18.3327 15.2469V4.75305C18.3327 3.04846 16.9508 1.66663 15.2463 1.66663H4.75244Z" fill="#5C5F60"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.66602 4.75305C1.66602 3.04847 3.04785 1.66663 4.75244 1.66663H15.2463C16.9508 1.66663 18.3327 3.04846 18.3327 4.75305V15.2469C18.3327 16.9515 16.9508 18.3333 15.2463 18.3333H4.75244C3.04786 18.3333 1.66602 16.9515 1.66602 15.2469V4.75305Z" fill="#EFF1F1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2463 3.20984H4.75244C3.90015 3.20984 3.20923 3.90076 3.20923 4.75305V15.2469C3.20923 16.0992 3.90015 16.7901 4.75244 16.7901H15.2463C16.0986 16.7901 16.7895 16.0992 16.7895 15.2469V4.75305C16.7895 3.90076 16.0986 3.20984 15.2463 3.20984ZM4.75244 1.66663C3.04785 1.66663 1.66602 3.04847 1.66602 4.75305V15.2469C1.66602 16.9515 3.04786 18.3333 4.75244 18.3333H15.2463C16.9508 18.3333 18.3327 16.9515 18.3327 15.2469V4.75305C18.3327 3.04846 16.9508 1.66663 15.2463 1.66663H4.75244Z" fill="#C4C7C7"/>
</svg>

Before

Width:  |  Height:  |  Size: 922 B

View file

@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2463 3.20984H4.75244C3.90015 3.20984 3.20923 3.90076 3.20923 4.75305V15.2469C3.20923 16.0992 3.90015 16.7901 4.75244 16.7901H15.2463C16.0986 16.7901 16.7895 16.0992 16.7895 15.2469V4.75305C16.7895 3.90076 16.0986 3.20984 15.2463 3.20984ZM4.75244 1.66663C3.04785 1.66663 1.66602 3.04847 1.66602 4.75305V15.2469C1.66602 16.9515 3.04786 18.3333 4.75244 18.3333H15.2463C16.9508 18.3333 18.3327 16.9515 18.3327 15.2469V4.75305C18.3327 3.04846 16.9508 1.66663 15.2463 1.66663H4.75244Z" fill="#747778"/>
</svg>

Before

Width:  |  Height:  |  Size: 653 B

View file

@ -1,31 +0,0 @@
import { AppearanceMode } from '@logto/schemas';
import CheckBoxSelectedDark from '@/assets/images/check-box-selected-dark.svg';
import CheckBoxSelectedDisabledDark from '@/assets/images/check-box-selected-disabled-dark.svg';
import CheckBoxSelectedDisabled from '@/assets/images/check-box-selected-disabled.svg';
import CheckBoxSelected from '@/assets/images/check-box-selected.svg';
import CheckBoxUnselectedDark from '@/assets/images/check-box-unselected-dark.svg';
import CheckBoxUnselectedDisabledDark from '@/assets/images/check-box-unselected-disabled-dark.svg';
import CheckBoxUnselectedDisabled from '@/assets/images/check-box-unselected-disabled.svg';
import CheckBoxUnselected from '@/assets/images/check-box-unselected.svg';
import { useTheme } from '@/hooks/use-theme';
type Props = {
className?: string;
};
const Icon = ({ className }: Props) => {
const theme = useTheme();
const isLightMode = theme === AppearanceMode.LightMode;
return (
<span className={className}>
{isLightMode ? <CheckBoxSelected /> : <CheckBoxSelectedDark />}
{isLightMode ? <CheckBoxUnselected /> : <CheckBoxUnselectedDark />}
{isLightMode ? <CheckBoxSelectedDisabled /> : <CheckBoxSelectedDisabledDark />}
{isLightMode ? <CheckBoxUnselectedDisabled /> : <CheckBoxUnselectedDisabledDark />}
</span>
);
};
export default Icon;

View file

@ -5,43 +5,61 @@
display: flex;
align-items: center;
input {
display: none;
}
.icon {
width: 20px;
height: 20px;
margin-right: _.unit(2);
> svg {
display: none;
color: var(--color-neutral-60);
.border {
fill: var(--color-text-secondary);
}
&:first-child {
color: var(--color-primary);
.background {
fill: var(--color-layer-1);
}
&.checked {
.background {
fill: var(--color-primary);
}
}
&.disabled {
.background {
fill: var(--color-checkbox-disabled-background);
}
.border {
fill: var(--color-border);
}
}
&.checked.disabled {
.background {
fill: var(--color-checkbox-checked-disabled-background);
}
}
}
input,
.disabledMask {
position: absolute;
width: 20px;
height: 20px;
// Note: add a left value to make the input element align with the icon
left: _.unit(0.5);
top: 0;
margin: 0;
opacity: 0%;
cursor: default;
.wrapper {
display: flex;
align-items: center;
cursor: pointer;
}
input:checked:not(:disabled) ~ .icon > svg:nth-child(1),
input:not(:checked):not(:disabled) ~ .icon > svg:nth-child(2),
input:checked:disabled ~ .icon > svg:nth-child(3),
input:not(:checked):disabled ~ .icon > svg:nth-child(4) {
display: block;
}
label {
.label {
font: var(--font-body-medium);
color: var(--color-text);
cursor: inherit;
}
&.disabled {
.wrapper {
cursor: not-allowed;
}
}
}

View file

@ -1,46 +1,112 @@
import classNames from 'classnames';
import { nanoid } from 'nanoid';
import type { ReactNode } from 'react';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { onKeyDownHandler } from '@/utilities/a11y';
import { Tooltip } from '../Tip';
import Icon from './Icon';
import * as styles from './index.module.scss';
type Props = {
// eslint-disable-next-line react/boolean-prop-naming
value: boolean;
name?: string;
/* eslint-disable react/boolean-prop-naming */
checked: boolean;
disabled: boolean;
indeterminate?: boolean;
/* eslint-enable react/boolean-prop-naming */
onChange: (value: boolean) => void;
label?: ReactNode;
// eslint-disable-next-line react/boolean-prop-naming
disabled: boolean;
className?: string;
disabledTooltip?: ReactNode;
tooltip?: ReactNode;
};
const Checkbox = ({ value, onChange, label, disabled, className, disabledTooltip }: Props) => {
const [id, setId] = useState(nanoid());
const Checkbox = ({
name,
checked,
disabled,
indeterminate,
onChange,
label,
className,
tooltip,
}: Props) => {
const [isIndeterminate, setIsIndeterminate] = useState(indeterminate);
useEffect(() => {
setIsIndeterminate(isIndeterminate);
}, [isIndeterminate]);
const handleChange = () => {
if (disabled) {
return;
}
if (isIndeterminate) {
onChange(false);
}
setIsIndeterminate(false);
onChange(!checked);
};
return (
<div className={classNames(styles.checkbox, className)}>
<input
id={id}
type="checkbox"
checked={value}
disabled={disabled}
onChange={(event) => {
onChange(event.target.checked);
}}
/>
{disabled && disabledTooltip && (
<Tooltip
horizontalAlign="start"
anchorClassName={styles.disabledMask}
content={disabledTooltip}
/>
)}
<Icon className={styles.icon} />
{label && <label htmlFor={id}>{label}</label>}
<div className={classNames(styles.checkbox, disabled && styles.disabled, className)}>
<Tooltip horizontalAlign="start" content={tooltip}>
<div
aria-checked={checked}
className={styles.wrapper}
role="checkbox"
tabIndex={0}
onClick={handleChange}
onKeyDown={onKeyDownHandler(handleChange)}
>
<input type="checkbox" checked={checked} disabled={disabled} name={name} />
<svg
className={classNames(
styles.icon,
(checked || isIndeterminate) && styles.checked,
disabled && styles.disabled
)}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
className={styles.background}
x="1.66663"
y="1.6665"
width="16.6667"
height="16.6667"
rx="4"
/>
{checked && !isIndeterminate && (
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.31476 13.858L5.13295 10.441C4.95568 10.253 4.95568 9.947 5.13295 9.757L5.77568 9.074C5.95295 8.886 6.24113 8.886 6.4184 9.074L8.63657 11.466L13.5811 6.141C13.7584 5.953 14.0465 5.953 14.2238 6.141L14.8665 6.825C15.0438 7.013 15.0438 7.32 14.8665 7.507L8.95748 13.858C8.78021 14.046 8.49203 14.046 8.31476 13.858Z"
fill="white"
/>
)}
{isIndeterminate && (
<path
d="M5 9.37516C5 9.14504 5.1599 8.9585 5.35714 8.9585H14.6429C14.8401 8.9585 15 9.14504 15 9.37516V10.6252C15 10.8553 14.8401 11.0418 14.6429 11.0418H5.35714C5.1599 11.0418 5 10.8553 5 10.6252V9.37516Z"
fill="white"
/>
)}
{!checked && !isIndeterminate && (
<path
className={styles.border}
fillRule="evenodd"
clipRule="evenodd"
d="M15.2469 3.20971H4.75305C3.90076 3.20971 3.20984 3.90063 3.20984 4.75293V15.2467C3.20984 16.099 3.90076 16.79 4.75305 16.79H15.2469C16.0992 16.79 16.7901 16.099 16.7901 15.2467V4.75292C16.7901 3.90063 16.0992 3.20971 15.2469 3.20971ZM4.75305 1.6665C3.04846 1.6665 1.66663 3.04834 1.66663 4.75293V15.2467C1.66663 16.9513 3.04847 18.3332 4.75305 18.3332H15.2469C16.9515 18.3332 18.3333 16.9513 18.3333 15.2467V4.75292C18.3333 3.04834 16.9515 1.6665 15.2469 1.6665H4.75305Z"
/>
)}
</svg>
{label && <span className={styles.label}>{label}</span>}
</div>
</Tooltip>
</div>
);
};

View file

@ -1,3 +1,4 @@
import { conditional } from '@silverhand/essentials';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { snakeCase } from 'snake-case';
@ -40,6 +41,8 @@ const SignUpForm = () => {
return null;
}
const isUsernamePasswordSignUp = signUpIdentifier === SignUpIdentifier.Username;
const postSignUpIdentifierChange = (signUpIdentifier: SignUpIdentifier) => {
if (signUpIdentifier === SignUpIdentifier.Username) {
setValue('signUp.password', true);
@ -172,9 +175,12 @@ const SignUpForm = () => {
render={({ field: { value, onChange } }) => (
<Checkbox
label={t('sign_in_exp.sign_up_and_sign_in.sign_up.set_a_password_option')}
disabled={signUpIdentifier === SignUpIdentifier.Username}
value={value}
disabledTooltip={t('sign_in_exp.sign_up_and_sign_in.tip.set_a_password')}
disabled={isUsernamePasswordSignUp}
checked={value}
tooltip={conditional(
isUsernamePasswordSignUp &&
t('sign_in_exp.sign_up_and_sign_in.tip.set_a_password')
)}
onChange={(value) => {
onChange(value);
refreshSignInMethods();
@ -182,16 +188,16 @@ const SignUpForm = () => {
/>
)}
/>
{signUpIdentifier !== SignUpIdentifier.Username && (
{isVerificationRequiredSignUpIdentifiers(signUpIdentifier) && (
<Controller
name="signUp.verify"
control={control}
render={({ field: { value, onChange } }) => (
<Checkbox
disabled
label={t('sign_in_exp.sign_up_and_sign_in.sign_up.verify_at_sign_up_option')}
value={value}
disabled={isVerificationRequiredSignUpIdentifiers(signUpIdentifier)}
disabledTooltip={t('sign_in_exp.sign_up_and_sign_in.tip.verify_at_sign_up')}
checked={value}
tooltip={t('sign_in_exp.sign_up_and_sign_in.tip.verify_at_sign_up')}
onChange={(value) => {
onChange(value);
refreshSignInMethods();

View file

@ -65,9 +65,11 @@ const SignInMethodItem = ({
<Checkbox
className={styles.checkBox}
label={t('sign_in_exp.sign_up_and_sign_in.sign_in.password_auth')}
value={password}
checked={password}
disabled={!isPasswordCheckable}
disabledTooltip={t('sign_in_exp.sign_up_and_sign_in.tip.password_auth')}
tooltip={conditional(
!isPasswordCheckable && t('sign_in_exp.sign_up_and_sign_in.tip.password_auth')
)}
onChange={(checked) => {
onVerificationStateChange('password', checked);
}}
@ -85,9 +87,12 @@ const SignInMethodItem = ({
<Checkbox
className={styles.checkBox}
label={t('sign_in_exp.sign_up_and_sign_in.sign_in.verification_code_auth')}
value={verificationCode}
checked={verificationCode}
disabled={!isVerificationCodeCheckable}
disabledTooltip={t('sign_in_exp.sign_up_and_sign_in.tip.verification_code_auth')}
tooltip={conditional(
!isVerificationCodeCheckable &&
t('sign_in_exp.sign_up_and_sign_in.tip.verification_code_auth')
)}
onChange={(checked) => {
onVerificationStateChange('verificationCode', checked);
}}

View file

@ -157,6 +157,8 @@
--shadow-3: 0 4px 16px rgba(0, 0, 0, 20%);
// Client specific variables (not available in design system)
--color-checkbox-disabled-background: var(--color-neutral-95);
--color-checkbox-checked-disabled-background: var(--color-primary-80);
--color-danger-toast-background: var(--color-error-95);
--color-danger-focused: rgba(186, 27, 27, 16%); // 16% Error-40
--color-tooltip-background: #34353f; // dark theme Surface-4
@ -327,6 +329,8 @@
--shadow-3: 0 4px 16px rgba(0, 0, 0, 20%);
// Client specific variables (not available in design system)
--color-checkbox-disabled-background: rgba(247, 248, 248, 8%);
--color-checkbox-checked-disabled-background: var(--color-primary-40);
--color-danger-toast-background: var(--color-error-99);
--color-danger-focused: rgba(255, 180, 169, 16%); // 16% Error-40
--color-tooltip-background: var(--color-surface-4);