Migration: base component

This commit is contained in:
Dreamacro 2019-07-02 16:56:17 +08:00
parent 658a26c2fc
commit 73d992ae94
13 changed files with 852 additions and 801 deletions

1306
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -33,11 +33,11 @@
"@babel/preset-env": "^7.4.5", "@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@types/classnames": "^2.2.8", "@types/classnames": "^2.2.8",
"@types/node": "^12.0.8", "@types/node": "^12.0.10",
"@types/react": "^16.8.19", "@types/react": "^16.8.22",
"@types/react-dom": "^16.8.4", "@types/react-dom": "^16.8.4",
"@types/react-i18next": "^8.1.0", "@types/react-i18next": "^8.1.0",
"@types/react-router-dom": "^4.3.3", "@types/react-router-dom": "^4.3.4",
"@types/react-sortable-hoc": "^0.6.5", "@types/react-sortable-hoc": "^0.6.5",
"@types/react-virtualized": "^9.21.2", "@types/react-virtualized": "^9.21.2",
"@types/yaml": "^1.0.2", "@types/yaml": "^1.0.2",
@ -53,20 +53,20 @@
"offline-plugin": "^5.0.7", "offline-plugin": "^5.0.7",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
"react-hot-loader": "^4.11.0", "react-hot-loader": "^4.12.0",
"sass": "^1.21.0", "sass": "^1.22.2",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"stylelint": "^10.1.0", "stylelint": "^10.1.0",
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^18.3.0",
"stylelint-webpack-plugin": "^0.10.5", "stylelint-webpack-plugin": "^0.10.5",
"tslint": "^5.17.0", "tslint": "^5.18.0",
"tslint-config-standard": "^8.0.1", "tslint-config-standard": "^8.0.1",
"tslint-loader": "^3.6.0", "tslint-loader": "^3.6.0",
"webpack": "^4.33.0", "webpack": "^4.35.2",
"webpack-cli": "^3.3.4", "webpack-cli": "^3.3.5",
"webpack-dev-middleware": "^3.7.0", "webpack-dev-middleware": "^3.7.0",
"webpack-dev-server": "^3.7.1", "webpack-dev-server": "^3.7.2",
"webpack-merge": "^4.2.1", "webpack-merge": "^4.2.1",
"webpack-pwa-manifest": "^4.0.0" "webpack-pwa-manifest": "^4.0.0"
}, },
@ -74,20 +74,20 @@
"axios": "^0.19.0", "axios": "^0.19.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"dayjs": "^1.8.14", "dayjs": "^1.8.14",
"eventemitter3": "^3.1.2", "eventemitter3": "^4.0.0",
"i18next": "^11.10.0", "i18next": "^17.0.6",
"i18next-browser-languagedetector": "^2.2.4", "i18next-browser-languagedetector": "^3.0.1",
"mobx": "^5.10.1", "mobx": "^5.10.1",
"mobx-react": "^6.0.3", "mobx-react": "^6.1.1",
"mobx-react-router": "^4.0.7", "mobx-react-router": "^4.0.7",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-i18next": "^7.12.0", "react-i18next": "^10.11.2",
"react-router-dom": "^5.0.1", "react-router-dom": "^5.0.1",
"react-sortable-hoc": "^1.9.1", "react-sortable-hoc": "^1.9.1",
"react-virtualized": "^9.21.1", "react-virtualized": "^9.21.1",
"terser-webpack-plugin": "^1.3.0", "terser-webpack-plugin": "^1.3.0",
"typescript": "^3.5.1", "typescript": "^3.5.2",
"yaml": "^1.6.0" "yaml": "^1.6.0"
} }
} }

View File

@ -10,28 +10,20 @@ interface AlertProps extends BaseComponentProps {
inside?: boolean inside?: boolean
} }
export class Alert extends React.Component<AlertProps, {}> { const iconMap = {
static defaultProps: AlertProps = {
message: '',
type: 'info',
inside: false
}
iconMap = {
success: 'check', success: 'check',
info: 'info', info: 'info',
warning: 'info', warning: 'info',
error: 'close' error: 'close'
} }
render () {
const { message, type, inside, children, className, style } = this.props
export function Alert (props: AlertProps) {
const { message = '', type = 'info', inside = false, children, className, style } = props
const classname = classnames('alert', `alert-${inside ? 'note' : 'box'}-${type}`, className)
return ( return (
<div className={classnames('alert', `alert-${inside ? 'note' : 'box'}-${type}`, className)} style={style}> <div className={classname} style={style}>
<span className="alert-icon"> <span className="alert-icon">
<Icon type={this.iconMap[type]} size={26} /> <Icon type={iconMap[type]} size={26} />
</span> </span>
{ {
message message
@ -40,6 +32,4 @@ export class Alert extends React.Component<AlertProps, {}> {
} }
</div> </div>
) )
}
} }

View File

@ -1,6 +1,7 @@
import * as React from 'react' import * as React from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import { BaseComponentProps } from '@models' import { BaseComponentProps } from '@models'
import { noop } from '@lib/helper'
import './style.scss' import './style.scss'
interface ButtonProps extends BaseComponentProps { interface ButtonProps extends BaseComponentProps {
@ -8,23 +9,15 @@ interface ButtonProps extends BaseComponentProps {
onClick?: React.MouseEventHandler<HTMLButtonElement> onClick?: React.MouseEventHandler<HTMLButtonElement>
} }
export class Button extends React.Component<ButtonProps, {}> { export function Button (props: ButtonProps) {
const { type = 'normal', onClick = noop, children, className, style } = props
static defaultProps: ButtonProps = { const classname = classnames('button', `button-${type}`, className)
type: 'normal',
onClick: () => {}
}
render () {
const { type, onClick, children, className, style } = this.props
return ( return (
<button <button
className={classnames('button', `button-${type}`, className)} className={classname}
style={style} style={style}
onClick={onClick} onClick={onClick}
>{children}</button> >{children}</button>
) )
}
} }

View File

@ -19,10 +19,8 @@ export interface ButtonSelectProps extends BaseComponentProps {
onSelect?: (value: any) => void onSelect?: (value: any) => void
} }
export class ButtonSelect extends React.Component<ButtonSelectProps, {}> { export function ButtonSelect (props: ButtonSelectProps) {
const { options, value, onSelect } = props
render () {
const { options, value, onSelect } = this.props
return ( return (
<div className="button-select"> <div className="button-select">
@ -39,6 +37,4 @@ export class ButtonSelect extends React.Component<ButtonSelectProps, {}> {
} }
</div> </div>
) )
}
} }

View File

@ -1,11 +1,11 @@
import * as React from 'react' import * as React from 'react'
import { BaseComponentProps } from '@models/BaseProps' import { BaseComponentProps } from '@models/BaseProps'
import { noop } from '@lib/helper'
import classnames from 'classnames' import classnames from 'classnames'
import './style.scss' import './style.scss'
interface InputProps extends BaseComponentProps { interface InputProps extends BaseComponentProps {
value?: string | number value?: string | number
disabled?: boolean
align?: 'left' | 'center' | 'right' align?: 'left' | 'center' | 'right'
inside?: boolean inside?: boolean
autoFocus?: boolean autoFocus?: boolean
@ -14,34 +14,23 @@ interface InputProps extends BaseComponentProps {
onBlur?: (event?: React.FocusEvent<HTMLInputElement>) => void onBlur?: (event?: React.FocusEvent<HTMLInputElement>) => void
} }
export class Input extends React.Component<InputProps, {}> { export function Input (props: InputProps) {
static defaultProps: InputProps = {
value: '',
disabled: false,
align: 'center',
inside: false,
autoFocus: false,
type: 'text',
onChange: () => {},
onBlur: () => {}
}
render () {
const { const {
className, className,
style, style,
value, value = '',
align, align = 'center',
inside, inside = false,
autoFocus, autoFocus = false,
type, type = 'text',
onChange, onChange = noop,
onBlur onBlur = noop
} = this.props } = props
const classname = classnames('input', `input-align-${align}`, { 'input-inside': inside }, className)
return ( return (
<input <input
className={classnames('input', `input-align-${align}`, { 'input-inside': inside }, className)} className={classname}
style={style} style={style}
value={value} value={value}
autoFocus={autoFocus} autoFocus={autoFocus}
@ -50,5 +39,4 @@ export class Input extends React.Component<InputProps, {}> {
onBlur={onBlur} onBlur={onBlur}
/> />
) )
}
} }

View File

@ -1,6 +1,7 @@
import * as React from 'react' import * as React from 'react'
import { BaseComponentProps } from '@models/BaseProps' import { BaseComponentProps } from '@models/BaseProps'
import { Icon } from '@components' import { Icon } from '@components'
import { noop } from '@lib/helper'
import classnames from 'classnames' import classnames from 'classnames'
import './style.scss' import './style.scss'
@ -10,26 +11,19 @@ interface SwitchProps extends BaseComponentProps {
onChange?: (checked: boolean) => void onChange?: (checked: boolean) => void
} }
export class Switch extends React.Component<SwitchProps, {}> { export function Switch (props: SwitchProps) {
static defaultProps: SwitchProps = { const { className, checked = false, disabled = false, onChange = noop } = props
checked: false, const classname = classnames('switch', { checked, disabled }, className)
disabled: false,
onChange: () => {}
}
handleClick = () => { function handleClick () {
if (!this.props.disabled) { if (!disabled) {
this.props.onChange(!this.props.checked) onChange(!checked)
} }
} }
render () {
const { className, checked, disabled } = this.props
return ( return (
<div className={classnames('switch', { checked, disabled }, className)} onClick={this.handleClick}> <div className={classname} onClick={handleClick}>
<Icon className="switch-icon" type="check" size={20} style={{ fontWeight: 'bold' }} /> <Icon className="switch-icon" type="check" size={20} style={{ fontWeight: 'bold' }} />
</div> </div>
) )
}
} }

View File

@ -1,5 +1,5 @@
import * as React from 'react' import React, { useState, useRef, useMemo } from 'react'
import { translate } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { BaseComponentProps, I18nProps } from '@models' import { BaseComponentProps, I18nProps } from '@models'
import { noop } from '@lib/helper' import { noop } from '@lib/helper'
import classnames from 'classnames' import classnames from 'classnames'
@ -13,33 +13,22 @@ interface TagsProps extends BaseComponentProps, I18nProps {
canClick: boolean canClick: boolean
} }
interface TagsState { export function Tags (props: TagsProps) {
expand: boolean const { className, data, onClick, select, canClick } = props
showExtend: boolean
ulRef: React.RefObject<HTMLUListElement>
}
class TagsClass extends React.Component<TagsProps, TagsState> { const { t } = useTranslation()
state: TagsState = { const [expand, setExpand] = useState(false)
expand: false,
showExtend: true,
ulRef: React.createRef<HTMLUListElement>()
}
toggleExtend = () => { const ulRef = useRef<HTMLUListElement>()
this.setState({ expand: !this.state.expand }) const showExtend = useMemo(() => ulRef.current.offsetHeight > 30, [ulRef])
}
componentDidMount () {
this.setState({ showExtend: this.state.ulRef.current.offsetHeight > 30 })
}
render () {
const { t, className, data, onClick, select, canClick } = this.props
const { expand } = this.state
const rowHeight = this.state.expand ? 'auto' : this.props.rowHeight const rowHeight = this.state.expand ? 'auto' : this.props.rowHeight
const handleClick = canClick ? onClick : noop const handleClick = canClick ? onClick : noop
function toggleExtend () {
setExpand(!expand)
}
const tags = data const tags = data
.map(t => { .map(t => {
const tagClass = classnames({ 'tags-selected': select === t, 'can-click': canClick }) const tagClass = classnames({ 'tags-selected': select === t, 'can-click': canClick })
@ -52,16 +41,13 @@ class TagsClass extends React.Component<TagsProps, TagsState> {
return ( return (
<div className={classnames('tags-container', className)} style={{ height: rowHeight }}> <div className={classnames('tags-container', className)} style={{ height: rowHeight }}>
<ul ref={this.state.ulRef} className={classnames('tags', { expand })}> <ul ref={ulRef} className={classnames('tags', { expand })}>
{ tags } { tags }
</ul> </ul>
{ {
this.state.showExtend && showExtend &&
<span className="tags-expand" onClick={this.toggleExtend}>{ this.state.expand ? t('collapseText') : t('expandText') }</span> <span className="tags-expand" onClick={toggleExtend}>{ expand ? t('collapseText') : t('expandText') }</span>
} }
</div> </div>
) )
}
} }
export const Tags = translate(['Proxies'])(TagsClass)

View File

@ -1,5 +1,5 @@
import * as React from 'react' import * as React from 'react'
import { translate } from 'react-i18next' import { withTranslation } from 'react-i18next'
import { inject, observer } from 'mobx-react' import { inject, observer } from 'mobx-react'
import { storeKeys } from '@lib/createStore' import { storeKeys } from '@lib/createStore'
import { Modal, Input, Row, Col, Alert } from '@components' import { Modal, Input, Row, Col, Alert } from '@components'
@ -93,4 +93,4 @@ class ExternalController extends React.Component<ExternalControllerModalProps, E
} }
} }
export default translate(['Settings'])(ExternalController) export default withTranslation(['Settings'])(ExternalController)

View File

@ -1,4 +1,4 @@
import * as EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
export enum Action { export enum Action {
SPEED_NOTIFY = 'speed-notify' SPEED_NOTIFY = 'speed-notify'

View File

@ -1,5 +1,5 @@
import { to } from '@lib/helper' import { to } from '@lib/helper'
import * as EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
export interface Config { export interface Config {
url: string url: string

View File

@ -1,4 +1,4 @@
import { CSSProperties } from 'react' import { CSSProperties, ReactNode } from 'react'
import { RouteComponentProps } from 'react-router' import { RouteComponentProps } from 'react-router'
import { RouterStore, ConfigStore } from '@stores' import { RouterStore, ConfigStore } from '@stores'
@ -19,5 +19,6 @@ export interface BaseProps extends BaseComponentProps {
export interface BaseComponentProps { export interface BaseComponentProps {
className?: string className?: string
children?: ReactNode
style?: CSSProperties style?: CSSProperties
} }

View File

@ -2,6 +2,7 @@
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/", "outDir": "./dist/",
"sourceMap": true, "sourceMap": true,
"esModuleInterop": true,
"noImplicitAny": false, "noImplicitAny": false,
"noUnusedLocals": true, "noUnusedLocals": true,
"module": "commonjs", "module": "commonjs",