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:
parent
b5bdfcbc5b
commit
1661c8121a
14 changed files with 57 additions and 40 deletions
|
@ -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',
|
||||
|
|
|
@ -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: '重发验证码',
|
||||
|
|
|
@ -33,6 +33,10 @@ main {
|
|||
@include mobile.fonts;
|
||||
@include mobile.layout;
|
||||
|
||||
main {
|
||||
justify-content: normal;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -14,4 +14,8 @@
|
|||
.terms {
|
||||
margin: _.unit(8) 0 _.unit(4);
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
margin-top: _.unit(-2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -3,3 +3,8 @@
|
|||
background: var(--color-overlay);
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
:global(.ReactModal__Body--open) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue