0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat: init input and i18n

This commit is contained in:
Gao Sun 2021-07-18 17:22:17 +08:00
parent 7bd68b5747
commit 6f88d1d1a5
No known key found for this signature in database
GPG key ID: 0F0EFA2E36639F31
16 changed files with 208 additions and 11 deletions

18
packages/ui/.vscode/tsx.code-snippets vendored Normal file
View file

@ -0,0 +1,18 @@
{
// Place your ui workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
"Import SCSS styles": {
"scope": "javascriptreact,typescriptreact",
"prefix": "isc",
"body": [
"import styles from './index.module.scss';",
"$0"
],
"description": "Import SCSS styles from the same directory."
}
}

View file

@ -1,3 +1,3 @@
# @logto/ui
The core register / sign in experience for end-users.
The core register / sign-in experience for end-users.

View file

@ -15,8 +15,11 @@
},
"dependencies": {
"classnames": "^2.3.1",
"i18next": "^20.3.3",
"i18next-browser-languagedetector": "^6.1.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.11.3",
"react-router-dom": "^5.2.0"
},
"devDependencies": {

View file

@ -1,9 +1,12 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import AppContent from './components/AppContent';
import initI18n from './init/i18n';
import Home from './pages/Home';
import './scss/normalized.scss';
initI18n();
const App = () => (
<AppContent theme="dark">
<Switch>

View file

@ -55,7 +55,7 @@ $font-family: 'PingFang SC', 'SF Pro Text', sans-serif;
.mobile {
--font-headline: 600 40px/56px #{$font-family};
--font-heading-1: 600 28px/39.2px #{$font-family};
--font-heading-1: 600 28px/39px #{$font-family};
--font-heading-2: 600 20px/28px #{$font-family};
--font-heading-3: 600 16px/22.4px #{$font-family};
--font-body: 400 12px/16px #{$font-family};

View file

@ -0,0 +1,19 @@
@use '/src/scss/underscore' as _;
.input {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: _.unit(3) _.unit(5);
border-radius: _.unit(2);
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

@ -0,0 +1,27 @@
import classNames from 'classnames';
import React from 'react';
import styles from './index.module.scss';
export type Props = {
className?: string;
placeholder?: string;
type?: InputType;
value: string;
onChange: (value: string) => void;
};
const Input = ({ className, placeholder, type = 'text', value, onChange }: Props) => {
return (
<input
className={classNames(styles.input, className)}
placeholder={placeholder}
type={type}
value={value}
onChange={({ target: { value } }) => {
onChange(value);
}}
/>
);
};
export default Input;

23
packages/ui/src/include.d/dom.d.ts vendored Normal file
View file

@ -0,0 +1,23 @@
type InputType =
| 'button'
| 'checkbox'
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'radio'
| 'range'
| 'reset'
| 'search'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week';

View file

@ -0,0 +1,11 @@
// https://react.i18next.com/latest/typescript#create-a-declaration-file
// eslint-disable-next-line import/no-unassigned-import
import 'react-i18next';
import en from '@/locales/en.json';
declare module 'react-i18next' {
interface CustomTypeOptions {
resources: typeof en;
}
}

View file

@ -0,0 +1,23 @@
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import en from '@/locales/en.json';
import zhCN from '@/locales/zh-CN.json';
const initI18n = () => {
void i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
resources: {
en,
'zh-CN': zhCN,
},
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});
};
export default initI18n;

View file

@ -0,0 +1,6 @@
{
"translation": {
"sign-in.username": "Username",
"sign-in.password": "Password"
}
}

View file

@ -0,0 +1,6 @@
{
"translation": {
"sign-in.username": "用户名",
"sign-in.password": "密码"
}
}

View file

@ -1,11 +1,18 @@
@use '/src/scss/underscore' as _;
.wrapper {
text-align: center;
padding: _.unit(5);
}
padding: _.unit(8);
display: flex;
flex-direction: column;
align-items: center;
.title {
font: var(--font-headline);
font: var(--font-heading-1);
color: var(--color-heading);
}
> input {
align-self: stretch;
margin: _.unit(1.5) 0;
}
}

View file

@ -1,10 +1,23 @@
import React from 'react';
import Input from '@/components/Input';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
const Home = () => {
const { t } = useTranslation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
return (
<div className={styles.wrapper}>
<div className={styles.title}>Logto</div>
<div className={styles.title}> Logto</div>
<Input placeholder={t('sign-in.username')} value={username} onChange={setUsername} />
<Input
placeholder={t('sign-in.password')}
type="password"
value={password}
onChange={setPassword}
/>
</div>
);
};

View file

@ -3,3 +3,7 @@ body {
padding: 0;
font-family: sans-serif;
}
* {
box-sizing: border-box;
}

View file

@ -955,7 +955,7 @@
"@babel/helper-validator-option" "^7.14.5"
"@babel/plugin-transform-typescript" "^7.14.5"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
@ -5707,6 +5707,13 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7"
terser "^4.6.3"
html-parse-stringify@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
dependencies:
void-elements "3.1.0"
html-tags@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140"
@ -5847,6 +5854,20 @@ husky@^7.0.1:
resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c"
integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA==
i18next-browser-languagedetector@^6.1.2:
version "6.1.2"
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz#68565a28b929cbc98ab6a56826ef2faf0e927ff8"
integrity sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g==
dependencies:
"@babel/runtime" "^7.14.6"
i18next@^20.3.3:
version "20.3.3"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.3.3.tgz#e3fdae045f9f0893e74826ce224715e43c62862b"
integrity sha512-tx9EUhHeaipvZ5pFLTaN9Xdm5Ssal774MpujaTA1Wv/ST/1My5SnoBmliY1lOpyEP5Z51Dq1gXifk/y4Yt3agQ==
dependencies:
"@babel/runtime" "^7.12.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -9355,6 +9376,14 @@ react-error-overlay@^6.0.7, react-error-overlay@^6.0.9:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
react-i18next@^11.11.3:
version "11.11.3"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.11.3.tgz#38d083bb079c3e6ee376b3321b0d6e409d798f68"
integrity sha512-upzG5/SpyOlYP5oSF4K8TZBvDWVhnCo38JNV+KnWjrg0+IaJCBltyh6lRGZDO5ovLyA4dU6Ip0bwbUCjb6Yyxw==
dependencies:
"@babel/runtime" "^7.14.5"
html-parse-stringify "^3.0.1"
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -11503,6 +11532,11 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
void-elements@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"