0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

fix(ui): ui refinement (#855)

* fix(ui): ui refinement

ui refinement

* fix(ui): fix mobile page jumpping issue

fix mobile page jumpping issue
This commit is contained in:
simeng-li 2022-05-18 10:03:37 +08:00 committed by GitHub
parent b5bdfcbc5b
commit 1661c8121a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 57 additions and 40 deletions

View file

@ -44,6 +44,9 @@ const translation = {
nav_back: 'Back',
},
description: {
email: 'email',
phone: 'phone',
phone_number: 'phone number',
reminder: 'Reminder',
not_found: '404 Not Found',
loading: 'Loading...',
@ -54,7 +57,7 @@ const translation = {
create_account: 'Create Account',
forgot_password: 'Forgot Password?',
or: 'or',
enter_passcode: 'The passcode has been sent to {{address}}',
enter_passcode: 'The passcode has been sent to your {{address}}',
passcode_sent: 'The passcode has been sent',
resend_after_seconds: 'Resend after {{seconds}} seconds',
resend_passcode: 'Resend Passcode',

View file

@ -46,6 +46,9 @@ const translation = {
nav_back: '返回',
},
description: {
email: '邮箱',
phone: '手机',
phone_number: '手机',
reminder: '提示',
not_found: '404 页面不存在',
loading: '读取中...',
@ -56,7 +59,7 @@ const translation = {
create_account: '创建账号',
forgot_password: '忘记密码?',
or: '或',
enter_passcode: '验证码已经发送至 {{ address }}',
enter_passcode: '验证码已经发送至您的{{ address }}',
passcode_sent: '验证码已经发送',
resend_after_seconds: '在 {{ seconds }} 秒后重发',
resend_passcode: '重发验证码',

View file

@ -33,6 +33,10 @@ main {
@include mobile.fonts;
@include mobile.layout;
main {
justify-content: normal;
}
.content {
flex: 1;
align-self: stretch;

View file

@ -28,12 +28,6 @@ const AcModal = ({
className={classNames(styles.modal, className)}
overlayClassName={classNames(modalStyles.overlay, styles.overlay)}
appElement={document.querySelector('main') ?? undefined}
onAfterOpen={() => {
document.body.classList.add('static');
}}
onAfterClose={() => {
document.body.classList.remove('static');
}}
>
<div className={styles.container}>
<div className={styles.header}>

View file

@ -27,12 +27,6 @@ const MobileModal = ({
className={classNames(styles.modal, className)}
overlayClassName={classNames(modalStyles.overlay, styles.overlay)}
appElement={document.querySelector('main') ?? undefined}
onAfterOpen={() => {
document.body.classList.add('static');
}}
onAfterClose={() => {
document.body.classList.remove('static');
}}
>
<div className={styles.container}>
<div className={styles.content}>{children}</div>

View file

@ -25,12 +25,6 @@ const Drawer = ({ className, isOpen = false, children, onClose }: Props) => {
appElement={document.querySelector('main') ?? undefined}
closeTimeoutMS={300}
onRequestClose={onClose}
onAfterOpen={() => {
document.body.classList.add('static');
}}
onAfterClose={() => {
document.body.classList.remove('static');
}}
>
<div className={styles.container}>
<div className={styles.header}>

View file

@ -85,6 +85,10 @@
height: 22px;
}
&.error {
border: _.border(var(--color-error));
}
&:focus-within {
border-color: var(--color-primary);
outline-color: var(--color-focused-variant);

View file

@ -7,6 +7,8 @@
input {
width: 44px;
height: 44px;
border-radius: _.unit(2);
border: _.border();
text-align: center;
font: var(--font-body);
color: var(--color-text);
@ -35,10 +37,6 @@
:global(body.mobile) {
.passcode {
input {
width: 44px;
height: 44px;
border-radius: _.unit(2);
border: _.border();
background: var(--color-layer);
}
}
@ -47,9 +45,6 @@
:global(body.desktop) {
.passcode {
input {
width: 44px;
height: 46px;
border-radius: _.unit(2);
border: _.border(var(--color-border));
outline: 3px solid transparent;
background: transparent;

View file

@ -20,7 +20,6 @@ const PasswordlessConfirmModal = ({ className, isOpen, type, method, value, onCl
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
const sendPasscode = getSendPasscodeApi(type, method);
const navigate = useNavigate();
const methodLocalName = t(`input.${method === 'email' ? 'email' : 'phone_number'}`);
const { result, run: asyncSendPasscode } = useApi(sendPasscode);
@ -44,7 +43,7 @@ const PasswordlessConfirmModal = ({ className, isOpen, type, method, value, onCl
<ConfirmModal
className={className}
isOpen={isOpen}
confirmText={type === 'sign-in' ? 'action.sign_in' : 'action.continue'}
confirmText={type === 'sign-in' ? 'action.sign_in' : 'action.create'}
onClose={onClose}
onConfirm={onConfirmHandler}
>
@ -52,7 +51,10 @@ const PasswordlessConfirmModal = ({ className, isOpen, type, method, value, onCl
type === 'sign-in'
? 'description.create_account_id_exists'
: 'description.sign_in_id_does_not_exists',
{ type: methodLocalName, value }
{
type: t(`description.${method === 'email' ? 'email' : 'phone_number'}`),
value: `${method === 'sms' ? '+' : ''}${value}`,
}
)}
</ConfirmModal>
);

View file

@ -14,4 +14,8 @@
.terms {
margin: _.unit(8) 0 _.unit(4);
}
.formErrors {
margin-top: _.unit(-2);
}
}

View file

@ -36,25 +36,27 @@ const UsernameSignin = ({ className }: Props) => {
const { termsValidation } = useTerms();
const {
fieldValue,
responseErrorMessage,
formErrorMessage,
setFieldValue,
register,
validateForm,
setResponseErrorMessage,
setFormErrorMessage,
} = useForm(defaultState);
const errorHandlers: ErrorHandlers = useMemo(
() => ({
'session.invalid_credentials': (error) => {
setResponseErrorMessage(error.message);
setFormErrorMessage(error.message);
},
}),
[setResponseErrorMessage]
[setFormErrorMessage]
);
const { result, run: asyncSignInBasic } = useApi(signInBasic, errorHandlers);
const onSubmitHandler = useCallback(async () => {
setFormErrorMessage(undefined);
if (!validateForm()) {
return;
}
@ -66,7 +68,14 @@ const UsernameSignin = ({ className }: Props) => {
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
void asyncSignInBasic(fieldValue.username, fieldValue.password, socialToBind);
}, [validateForm, termsValidation, asyncSignInBasic, fieldValue]);
}, [
setFormErrorMessage,
validateForm,
termsValidation,
asyncSignInBasic,
fieldValue.username,
fieldValue.password,
]);
useEffect(() => {
if (result?.redirectTo) {
@ -93,7 +102,9 @@ const UsernameSignin = ({ className }: Props) => {
placeholder={t('input.password')}
{...register('password', (value) => requiredValidation('password', value))}
/>
{responseErrorMessage && <ErrorMessage>{responseErrorMessage}</ErrorMessage>}
{formErrorMessage && (
<ErrorMessage className={styles.formErrors}>{formErrorMessage}</ErrorMessage>
)}
<TermsOfUse className={styles.terms} />
<Button onClick={onSubmitHandler}>{t('action.sign_in')}</Button>

View file

@ -14,7 +14,7 @@ const useForm = <T>(initialState: T) => {
const [fieldValue, setFieldValue] = useState<T>(initialState);
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
const [responseErrorMessage, setResponseErrorMessage] = useState<string>();
const [formErrorMessage, setFormErrorMessage] = useState<string>();
const fieldValidationsRef = useRef<FieldValidations>({});
@ -62,11 +62,11 @@ const useForm = <T>(initialState: T) => {
return {
fieldValue,
fieldErrors,
responseErrorMessage,
formErrorMessage,
validateForm,
setFieldValue,
setFieldErrors,
setResponseErrorMessage,
setFormErrorMessage,
register,
};
};

View file

@ -47,7 +47,11 @@ const Passcode = () => {
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('action.enter_passcode')}</div>
<div className={styles.detail}>{t('description.enter_passcode', { address: target })}</div>
<div className={styles.detail}>
{t('description.enter_passcode', {
address: t(`description.${method === 'email' ? 'email' : 'phone'}`),
})}
</div>
<PasscodeValidation type={type} method={method} target={target} />
</div>
</div>

View file

@ -3,3 +3,8 @@
background: var(--color-overlay);
inset: 0;
}
/* stylelint-disable-next-line selector-class-pattern */
:global(.ReactModal__Body--open) {
overflow: hidden;
}