Add: setting page

This commit is contained in:
Dreamacro 2018-09-26 00:39:14 +08:00
parent c7ce6a1bca
commit e8bde550a3
14 changed files with 316 additions and 17 deletions

View File

@ -2,7 +2,10 @@
"extends": "stylelint-config-standard",
"rules": {
"indentation": 4,
"font-family-no-missing-generic-family-keyword": null
"font-family-no-missing-generic-family-keyword": null,
"at-rule-no-unknown": [true, {
"ignoreAtRules": ["for", "function", "if", "each", "include", "mixin"]
}]
},
"ignoreFiles": [
"**/*.ts",

5
package-lock.json generated
View File

@ -2888,6 +2888,11 @@
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=",
"dev": true
},
"dayjs": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.7.5.tgz",
"integrity": "sha512-OzkAcosqOgWgQF+dQTXO/iaSGa3hMs/sSkfzkxwWpZXqJEbaA0V6O1V+Ew2tGBlTz1r7Rb7opU3w8ympWb9d2Q=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",

View File

@ -63,6 +63,7 @@
},
"dependencies": {
"classnames": "^2.2.6",
"dayjs": "^1.7.5",
"i18next": "^11.9.0",
"i18next-browser-languagedetector": "^2.2.3",
"ini": "^1.3.5",

View File

@ -0,0 +1,43 @@
import * as React from 'react'
import { BaseComponentProps } from '@models/BaseProps'
import classnames from 'classnames'
import './style.scss'
export interface ButtonSelectOptions {
label: string,
value: any
}
export interface ButtonSelectProps extends BaseComponentProps {
// options
options: ButtonSelectOptions[]
// active value
value: any
// select callback
onSelect?: (value: any) => void
}
export class ButtonSelect extends React.Component<ButtonSelectProps, {}> {
render () {
const { options, value, onSelect } = this.props
return (
<div className="button-select">
{
options.map(option => (
<button
value={option.value}
key={option.value}
className={classnames('button-select-options', { actived: value === option.value })}
onClick={() => onSelect(option.value)}
>{option.label}</button>
))
}
</div>
)
}
}

View File

@ -0,0 +1,41 @@
@import '~@styles/variables';
.button-select {
display: flex;
flex-direction: row;
.button-select-options {
height: 30px;
padding: 0 15px;
color: $color-primary-darken;
font-size: 12px;
line-height: 30px;
background: $color-white;
border: 1px solid $color-primary-lightly;
border-right: none;
transition: all 300ms ease;
cursor: pointer;
outline: 0;
display: block;
}
.button-select-options:first-child {
border-radius: 3px 0 0 3px;
}
.button-select-options:last-child {
border-radius: 0 3px 3px 0;
border-right: 1px solid $color-primary-lightly;
}
.button-select-options.actived {
background: $color-primary;
color: $color-white;
border-color: $color-primary;
box-shadow: 0 2px 5px rgba($color: $color-primary, $alpha: 0.5);
&:active {
box-shadow: none;
}
}
}

View File

@ -0,0 +1,37 @@
import * as React from 'react'
import { BaseComponentProps } from '@models/BaseProps'
import classnames from 'classnames'
interface ColProps extends BaseComponentProps {
// left offset
offset?: number
// flex order
order?: number
span?: number
}
export const Col: React.SFC<ColProps> = props => {
const {
offset = 0,
order = 0,
span = 1,
className,
style: s,
children
} = props
const style = Object.assign({}, { order }, s)
return (
<div className={classnames(
'column',
`column-offset-${offset}`,
`column-span-${span}`,
className
)} style={style}>
{ children }
</div>
)
}

View File

@ -0,0 +1,38 @@
import * as React from 'react'
import { BaseComponentProps } from '@models/BaseProps'
import classnames from 'classnames'
import './style.scss'
interface RowProps extends BaseComponentProps {
// grid column
gutter?: number
// row align
align?: 'top' | 'middle' | 'bottom'
// column justify
justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between'
}
export const Row: React.SFC<RowProps> = props => {
const {
gutter = 24,
align = 'top',
justify = 'start',
className,
style,
children
} = props
return (
<div className={classnames(
'row',
`row-gutter-${gutter}`,
`row-align-${align}`,
`row-justify-${justify}`,
className
)} style={style}>
{ children }
</div>
)
}

View File

@ -0,0 +1,65 @@
@import '~@styles/variables';
$padding: 12px;
.row {
width: 100%;
display: flex;
flex-direction: row;
}
// gutter
@for $i from 1 through 24 {
.row-gutter-#{$i} {
padding: $padding $padding / 2;
.column {
padding: 0 $padding / 2;
display: flex;
}
@for $c from 1 through 24 {
.column-span-#{$c} {
width: (100% / $i) * $c;
}
.column-offset-#{$c} {
margin-left: (100% / $i) * $c;
}
}
}
}
// align
.row-align-top {
align-items: flex-start;
}
.row-align-middle {
align-items: center;
}
.row-align-bottom {
align-items: flex-end;
}
// justify
.row-justify-start {
justify-content: flex-start;
}
.row-justify-end {
justify-content: flex-end;
}
.row-justify-center {
justify-content: center;
}
.row-justify-space-around {
justify-content: space-around;
}
.row-justify-space-between {
justify-content: space-between;
}

View File

@ -28,7 +28,7 @@ export class Switch extends React.Component<SwitchProps, {}> {
return (
<div className={classnames('switch', { checked, disabled }, className)} onClick={this.handleClick}>
<Icon className="switch-icon" type="check" size={8} />
<Icon className="switch-icon" type="check" size={8} style={{ fontWeight: 'bold' }} />
</div>
)
}

View File

@ -1,9 +1,9 @@
@import '~@styles/variables';
$height: 14px;
$switch-radius: 16px;
$height: 16px;
$switch-radius: 18px;
$switch-offset: 2px;
$width: 28px;
$width: 32px;
.switch {
display: inline-block;
@ -43,7 +43,7 @@ $width: 28px;
width: $switch-radius;
border-radius: $switch-radius / 2;
background-color: #fff;
box-shadow: 0 0 8px rgba($color-primary-dark, 0.25);
box-shadow: 0 0 8px rgba($color-primary-dark, 0.4);
transition: transform 0.3s ease;
transform: translateX($width - $switch-radius + $switch-offset);
}

View File

@ -2,3 +2,6 @@ export * from './Header'
export * from './Icon'
export * from './Switch'
export * from './Card'
export * from './Row'
export * from './Col'
export * from './ButtonSelect'

View File

@ -1,21 +1,77 @@
import * as React from 'react'
import { Header, Card } from '@components'
import { Header, Card, Row, Col, Switch, ButtonSelect, ButtonSelectOptions } from '@components'
import { translate } from 'react-i18next'
import { I18nProps } from '@i18n'
import './style.scss'
class Settings extends React.Component<I18nProps, {}> {
state = {
startAtLogin: false
startAtLogin: false,
language: 'en',
setAsSystemProxy: true,
allowConnectFromLan: true
}
languageOptions: ButtonSelectOptions[] = [
{ label: '中文', value: 'zh' },
{ label: 'English', value: 'en' }
]
render () {
const { t } = this.props
const {
startAtLogin,
language,
setAsSystemProxy,
allowConnectFromLan
} = this.state
return (
<div className="page">
<Header title={t('title')} />
<Card style={{ marginTop: 25 }}>
<Card className="settings-card">
<Row gutter={24} align="middle">
<Col span={6} offset={1}>
<span className="label">{t('labels.startAtLogin')}</span>
</Col>
<Col span={4} className="value-column">
<Switch
checked={startAtLogin}
onChange={startAtLogin => this.setState({ startAtLogin })}
/>
</Col>
<Col span={4} offset={1}>
<span className="label">{t('labels.language')}</span>
</Col>
<Col span={7} className="value-column">
<ButtonSelect
options={this.languageOptions}
value={language}
onSelect={language => this.setState({ language })}
/>
</Col>
</Row>
<Row gutter={24} align="middle">
<Col span={6} offset={1}>
<span className="label">{t('labels.setAsSystemProxy')}</span>
</Col>
<Col span={4} className="value-column">
<Switch
checked={setAsSystemProxy}
onChange={setAsSystemProxy => this.setState({ setAsSystemProxy })}
/>
</Col>
<Col span={7} offset={1}>
<span className="label">{t('labels.allowConnectFromLan')}</span>
</Col>
<Col span={4} className="value-column">
<Switch
checked={allowConnectFromLan}
onChange={allowConnectFromLan => this.setState({ allowConnectFromLan })}
/>
</Col>
</Row>
</Card>
</div>
)

View File

@ -1,12 +1,19 @@
@import '~@styles/variables';
.proxies-list {
margin: 10px 0;
display: flex;
flex-wrap: wrap;
list-style: none;
.settings-card {
margin-top: 25px;
padding: 20px 0;
li {
margin: 20px 15px 20px 0;
.column {
align-items: center;
}
.value-column {
justify-content: flex-end;
}
.label {
font-size: 14px;
color: $color-primary-darken;
}
}

View File

@ -8,7 +8,7 @@ $color-primary: #57befc;
$color-primary-dark: #2c8af8;
$color-primary-darken: #54759a;
$color-primary-light: #7fcae4;
$color-primary-lightly: #b4ddf5;
$color-primary-lightly: #e4eaef;
// common colors
$color-gray: #d8dee2;