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

feat(console): add suffix icon button on input component (#3257)

This commit is contained in:
Charles Zhao 2023-03-03 14:36:06 +08:00 committed by GitHub
parent 373febf707
commit 0adb31d55b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 6 deletions

View file

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM8.46447 9.87868C8.07394 9.48816 8.07394 8.85499 8.46447 8.46447C8.85499 8.07394 9.48816 8.07394 9.87868 8.46447L12 10.5858L14.1213 8.46447C14.5118 8.07394 15.145 8.07394 15.5355 8.46447C15.9261 8.85499 15.9261 9.48815 15.5355 9.87868L13.4142 12L15.5355 14.1213C15.9261 14.5118 15.9261 15.145 15.5355 15.5355C15.145 15.9261 14.5118 15.9261 14.1213 15.5355L12 13.4142L9.87868 15.5355C9.48816 15.9261 8.85499 15.9261 8.46447 15.5355C8.07394 15.145 8.07394 14.5118 8.46447 14.1213L10.5858 12L8.46447 9.87868Z" fill="currentcolor"/>
</svg>

After

Width:  |  Height:  |  Size: 772 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92879 3.92888C4.53826 4.3194 4.53826 4.95257 4.92879 5.34309L6.50507 6.91938C3.79112 8.79701 2 11.4523 2 12C2 12.866 6.47715 19 12 19C13.8197 19 15.5259 18.3341 16.9958 17.4101L17.6567 18.071C18.0472 18.4615 18.6804 18.4615 19.0709 18.071C19.4614 17.6805 19.4614 17.0473 19.0709 16.6568L6.343 3.92888C5.95248 3.53836 5.31931 3.53836 4.92879 3.92888ZM14.6137 15.028L8.97197 9.38628C8.36632 10.0873 8 11.0009 8 12C8 14.2091 9.79086 16 12 16C12.9991 16 13.9127 15.6337 14.6137 15.028ZM13.5568 8.31427C14.5141 8.7191 15.2809 9.48594 15.6857 10.4432L20.0754 14.8329C21.2856 13.5325 22 12.3459 22 12C22 11.134 17.5228 5 12 5C11.4584 5 10.9269 5.05898 10.4087 5.16619L13.5568 8.31427Z" fill="currentcolor"/>
</svg>

After

Width:  |  Height:  |  Size: 823 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 12C22 12.866 17.5228 19 12 19C6.47715 19 2 12.866 2 12C2 11.134 6.47715 5 12 5C17.5228 5 22 11.134 22 12ZM16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12ZM11 14C12.1046 14 13 13.1046 13 12C13 10.8954 12.1046 10 11 10C9.89543 10 9 10.8954 9 12C9 13.1046 9.89543 14 11 14Z" fill="currentcolor"/>
</svg>

After

Width:  |  Height:  |  Size: 481 B

View file

@ -1,13 +1,22 @@
@use '@/scss/underscore' as _;
.suffix {
width: _.unit(8);
height: _.unit(8);
display: none;
}
.container {
display: flex;
align-items: center;
border-radius: 6px;
border: 1px solid var(--color-border);
outline: 3px solid transparent;
transition-property: outline, border;
transition-timing-function: ease-in-out;
transition-duration: 0.2s;
padding: _.unit(2) _.unit(3);
padding: 0 _.unit(3);
height: 44px;
background: var(--color-layer-1);
font: var(--font-body-2);
@ -25,10 +34,14 @@
&:focus-within {
border-color: var(--color-primary);
outline-color: var(--color-focused-variant);
.suffix {
display: block;
}
}
input {
width: 100%;
flex: 1;
appearance: none;
color: var(--color-text);
font: var(--font-body-2);

View file

@ -1,13 +1,14 @@
import classNames from 'classnames';
import type { HTMLProps, ReactNode, ForwardedRef } from 'react';
import { forwardRef } from 'react';
import type { HTMLProps, ForwardedRef, ReactElement } from 'react';
import { cloneElement, forwardRef } from 'react';
import * as styles from './index.module.scss';
type Props = HTMLProps<HTMLInputElement> & {
hasError?: boolean;
errorMessage?: string;
icon?: ReactNode;
icon?: ReactElement;
suffix?: ReactElement;
};
const TextInput = (
@ -15,9 +16,11 @@ const TextInput = (
errorMessage,
hasError = Boolean(errorMessage),
icon,
suffix,
disabled,
className,
readOnly,
value,
...rest
}: Props,
reference: ForwardedRef<HTMLInputElement>
@ -35,6 +38,10 @@ const TextInput = (
>
{icon && <span className={styles.icon}>{icon}</span>}
<input type="text" {...rest} ref={reference} disabled={disabled} readOnly={readOnly} />
{suffix &&
cloneElement(suffix, {
className: classNames([suffix.props.className, styles.suffix]),
})}
</div>
{hasError && errorMessage && <div className={styles.errorMessage}>{errorMessage}</div>}
</div>

View file

@ -5,8 +5,10 @@ import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
import ClearInput from '@/assets/images/clear-input.svg';
import Button from '@/components/Button';
import Checkbox from '@/components/Checkbox';
import IconButton from '@/components/IconButton';
import TextInput from '@/components/TextInput';
import { adminTenantEndpoint, meApi } from '@/consts';
import { useStaticApi } from '@/hooks/use-api';
@ -35,6 +37,7 @@ const ChangePasswordModal = () => {
register,
clearErrors,
handleSubmit,
setValue,
formState: { errors, isSubmitting },
} = useForm<FormFields>({
reValidateMode: 'onBlur',
@ -82,6 +85,16 @@ const ChangePasswordModal = () => {
})}
type={showPassword ? 'text' : 'password'}
errorMessage={errors.newPassword?.message}
suffix={
<IconButton
onMouseDown={(event) => {
event.preventDefault();
setValue('newPassword', '');
}}
>
<ClearInput />
</IconButton>
}
onKeyDown={onKeyDown}
/>
<TextInput
@ -91,6 +104,16 @@ const ChangePasswordModal = () => {
})}
type={showPassword ? 'text' : 'password'}
errorMessage={errors.confirmPassword?.message}
suffix={
<IconButton
onMouseDown={(event) => {
event.preventDefault();
setValue('confirmPassword', '');
}}
>
<ClearInput />
</IconButton>
}
onKeyDown={onKeyDown}
/>
<Checkbox

View file

@ -1,10 +1,14 @@
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
import ArrowConnection from '@/assets/images/arrow-connection.svg';
import PasswordHideIcon from '@/assets/images/password-hide.svg';
import PasswordShowIcon from '@/assets/images/password-show.svg';
import Button from '@/components/Button';
import IconButton from '@/components/IconButton';
import TextInput from '@/components/TextInput';
import TextLink from '@/components/TextLink';
import { adminTenantEndpoint, meApi } from '@/consts';
@ -31,6 +35,7 @@ const VerifyPasswordModal = () => {
reValidateMode: 'onBlur',
});
const api = useStaticApi({ prefixUrl: adminTenantEndpoint, resourceIndicator: meApi.indicator });
const [showPassword, setShowPassword] = useState(false);
const email = conditional(checkLocationState(state) && state.email);
const onClose = () => {
@ -62,7 +67,17 @@ const VerifyPasswordModal = () => {
},
})}
errorMessage={errors.password?.message}
type="password"
type={showPassword ? 'text' : 'password'}
suffix={
<IconButton
onMouseDown={(event) => {
event.preventDefault();
setShowPassword((flag) => !flag);
}}
>
{showPassword ? <PasswordShowIcon /> : <PasswordHideIcon />}
</IconButton>
}
onKeyDown={(event) => {
if (event.key === 'Enter') {
onSubmit();