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

feat: init sign-in button

This commit is contained in:
Gao Sun 2021-07-18 18:15:11 +08:00
parent 6f88d1d1a5
commit 3ddc180d65
No known key found for this signature in database
GPG key ID: 0F0EFA2E36639F31
11 changed files with 141 additions and 8 deletions

View file

@ -13,6 +13,7 @@
--color-gradient: linear-gradient(12.07deg, #3c4ce3 8.81%, #717ce0 93.49%);
--color-button-background: #3c4ce3;
--color-button-background-disabled: #626fe8;
--color-button-background-hover: #2234df;
--color-button-text: #f5f5f5;
--color-button-text-disabled: #eee;
--color-error: #ff6b66;

View file

@ -0,0 +1,25 @@
@use '/src/scss/underscore' as _;
.button {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: _.unit(3) _.unit(8);
border-radius: 12px;
background: var(--color-button-background);
color: var(--color-button-text);
font: var(--font-heading-3);
box-shadow: var(--shadow-button);
transition: background 0.2s ease-in-out, color 0.2s ease-in-out;
cursor: pointer;
&:not(.disabled):hover {
background: var(--color-button-background-hover);
}
&.disabled {
background: var(--color-button-background-disabled);
color: var(--color-button-text-disabled);
}
}

View file

@ -0,0 +1,27 @@
import React from 'react';
import classNames from 'classnames';
import styles from './index.module.scss';
export type Props = {
isDisabled?: boolean;
className?: string;
value?: string;
onClick?: React.MouseEventHandler;
};
const Button = ({ isDisabled = false, className, value, onClick }: Props) => {
return (
<input
className={classNames(styles.button, isDisabled && styles.disabled, className)}
type="button"
value={value}
onClick={(event) => {
if (!isDisabled) {
onClick?.(event);
}
}}
/>
);
};
export default Button;

View file

@ -10,8 +10,6 @@
background: var(--color-control-background);
color: var(--color-heading);
font: var(--font-heading-3);
border: none;
outline: none;
&::placeholder {
color: var(--color-placeholder);

View file

@ -3,6 +3,7 @@ import React from 'react';
import styles from './index.module.scss';
export type Props = {
autoComplete?: AutoCompleteType;
className?: string;
placeholder?: string;
type?: InputType;
@ -10,13 +11,14 @@ export type Props = {
onChange: (value: string) => void;
};
const Input = ({ className, placeholder, type = 'text', value, onChange }: Props) => {
const Input = ({ autoComplete, className, placeholder, type = 'text', value, onChange }: Props) => {
return (
<input
className={classNames(styles.input, className)}
placeholder={placeholder}
type={type}
value={value}
autoComplete={autoComplete}
onChange={({ target: { value } }) => {
onChange(value);
}}

View file

@ -1,3 +1,4 @@
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
type InputType =
| 'button'
| 'checkbox'
@ -21,3 +22,50 @@ type InputType =
| 'time'
| 'url'
| 'week';
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute
type AutoCompleteType =
| 'name'
| 'honorific-prefix'
| 'given-name'
| 'additional-name'
| 'family-name'
| 'honorific-suffix'
| 'nickname'
| 'username'
| 'new-password'
| 'current-password'
| 'one-time-code'
| 'organization-title'
| 'organization'
| 'street-address'
| 'address-line1'
| 'address-line2'
| 'address-line3'
| 'address-level4'
| 'address-level3'
| 'address-level2'
| 'address-level1'
| 'country'
| 'country-name'
| 'postal-code'
| 'cc-name'
| 'cc-given-name'
| 'cc-additional-name'
| 'cc-family-name'
| 'cc-number'
| 'cc-exp'
| 'cc-exp-month'
| 'cc-exp-year'
| 'cc-csc'
| 'cc-type'
| 'transaction-currency'
| 'transaction-amount'
| 'language'
| 'bday'
| 'bday-day'
| 'bday-month'
| 'bday-year'
| 'sex'
| 'url'
| 'photo';

View file

@ -1,5 +1,7 @@
{
"translation": {
"sign-in": "Sign In",
"sign-in.loading": "Signing in...",
"sign-in.username": "Username",
"sign-in.password": "Password"
}

View file

@ -1,5 +1,7 @@
{
"translation": {
"sign-in": "登录",
"sign-in.loading": "登录中...",
"sign-in.username": "用户名",
"sign-in.password": "密码"
}

View file

@ -5,14 +5,21 @@
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.title {
font: var(--font-heading-1);
color: var(--color-heading);
margin-bottom: _.unit(9);
}
> input {
> input:not([type='button']) {
align-self: stretch;
margin: _.unit(1.5) 0;
margin-top: _.unit(3);
}
> input[type='button'] {
margin-top: _.unit(6);
}
}

View file

@ -1,3 +1,4 @@
import Button from '@/components/Button';
import Input from '@/components/Input';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -7,18 +8,33 @@ const Home = () => {
const { t } = useTranslation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isLoading, setIsLoading] = useState(false);
return (
<div className={styles.wrapper}>
<form className={styles.wrapper}>
<div className={styles.title}> Logto</div>
<Input placeholder={t('sign-in.username')} value={username} onChange={setUsername} />
<Input
autoComplete="username"
placeholder={t('sign-in.username')}
value={username}
onChange={setUsername}
/>
<Input
autoComplete="current-password"
placeholder={t('sign-in.password')}
type="password"
value={password}
onChange={setPassword}
/>
</div>
<Button
isDisabled={isLoading}
value={isLoading ? t('sign-in.loading') : t('sign-in')}
onClick={() => {
setIsLoading(true);
}}
/>
</form>
);
};

View file

@ -7,3 +7,8 @@ body {
* {
box-sizing: border-box;
}
input {
border: none;
outline: none;
}