Typescriptify components
This commit is contained in:
parent
08ae6d42bc
commit
51df6abd81
|
|
@ -1,17 +1,16 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import Button from 'material-ui/Button';
|
import Button from 'material-ui/Button';
|
||||||
import Dialog, {DialogActions, DialogContent, DialogContentText, DialogTitle} from 'material-ui/Dialog';
|
import Dialog, {DialogActions, DialogContent, DialogContentText, DialogTitle} from 'material-ui/Dialog';
|
||||||
import PropTypes from 'prop-types';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
export default class ConfirmDialog extends Component {
|
interface IProps {
|
||||||
static propTypes = {
|
title: string
|
||||||
title: PropTypes.string.isRequired,
|
text: string
|
||||||
text: PropTypes.string.isRequired,
|
fClose: VoidFunction
|
||||||
fClose: PropTypes.func.isRequired,
|
fOnSubmit: VoidFunction
|
||||||
fOnSubmit: PropTypes.func.isRequired,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
export default class ConfirmDialog extends Component<IProps> {
|
||||||
|
public render() {
|
||||||
const {title, text, fClose, fOnSubmit} = this.props;
|
const {title, text, fClose, fOnSubmit} = this.props;
|
||||||
const submitAndClose = () => {
|
const submitAndClose = () => {
|
||||||
fOnSubmit();
|
fOnSubmit();
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component} from 'react';
|
import {WithStyles} from "material-ui";
|
||||||
import {withStyles} from 'material-ui/styles';
|
|
||||||
import Paper from 'material-ui/Paper';
|
import Paper from 'material-ui/Paper';
|
||||||
import PropTypes from 'prop-types';
|
import {withStyles} from 'material-ui/styles';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
const styles = () => ({
|
const styles = () => ({
|
||||||
paper: {
|
paper: {
|
||||||
|
|
@ -9,14 +9,12 @@ const styles = () => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Container extends Component {
|
interface IProps {
|
||||||
static propTypes = {
|
style?: object,
|
||||||
classes: PropTypes.object.isRequired,
|
}
|
||||||
children: PropTypes.node,
|
|
||||||
style: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
class Container extends React.Component<IProps & WithStyles<'paper'>, {}> {
|
||||||
|
public render() {
|
||||||
const {classes, children, style} = this.props;
|
const {classes, children, style} = this.props;
|
||||||
return (
|
return (
|
||||||
<Paper elevation={6} className={classes.paper} style={style}>
|
<Paper elevation={6} className={classes.paper} style={style}>
|
||||||
|
|
@ -26,4 +24,4 @@ class Container extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(Container);
|
export default withStyles(styles)<IProps>(Container);
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import Button from 'material-ui/Button';
|
|
||||||
import Grid from 'material-ui/Grid';
|
|
||||||
import Typography from 'material-ui/Typography';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
export default class DefaultPage extends Component {
|
|
||||||
static defaultProps = {
|
|
||||||
buttonDisabled: false,
|
|
||||||
hideButton: false,
|
|
||||||
maxWidth: 700,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
buttonTitle: PropTypes.string,
|
|
||||||
fButton: PropTypes.func,
|
|
||||||
buttonDisabled: PropTypes.bool.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
hideButton: PropTypes.bool.isRequired,
|
|
||||||
children: PropTypes.oneOfType([
|
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
|
||||||
PropTypes.node,
|
|
||||||
]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {title, buttonTitle, fButton, buttonDisabled, maxWidth, hideButton, children} = this.props;
|
|
||||||
return (
|
|
||||||
<main style={{margin: '0 auto', maxWidth}}>
|
|
||||||
<Grid container spacing={24}>
|
|
||||||
<Grid item xs={12} style={{display: 'flex'}}>
|
|
||||||
<Typography variant="display1" style={{flex: 1}}>
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
{hideButton ? null : <Button variant="raised" color="primary" disabled={buttonDisabled}
|
|
||||||
onClick={fButton}>{buttonTitle}</Button>}
|
|
||||||
</Grid>
|
|
||||||
{children}
|
|
||||||
</Grid>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Button from 'material-ui/Button';
|
||||||
|
import Grid from 'material-ui/Grid';
|
||||||
|
import Typography from 'material-ui/Typography';
|
||||||
|
import React, {SFC} from 'react';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
title: string
|
||||||
|
buttonTitle?: string
|
||||||
|
fButton?: VoidFunction
|
||||||
|
buttonDisabled?: boolean
|
||||||
|
maxWidth?: number
|
||||||
|
hideButton?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultPage: SFC<IProps> = ({title, buttonTitle, fButton, buttonDisabled = false, maxWidth = 700, hideButton, children}) => (
|
||||||
|
<main style={{margin: '0 auto', maxWidth}}>
|
||||||
|
<Grid container spacing={24}>
|
||||||
|
<Grid item xs={12} style={{display: 'flex'}}>
|
||||||
|
<Typography variant="display1" style={{flex: 1}}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
{hideButton ? null : <Button variant="raised" color="primary" disabled={buttonDisabled}
|
||||||
|
onClick={fButton}>{buttonTitle}</Button>}
|
||||||
|
</Grid>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
export default DefaultPage;
|
||||||
|
|
@ -1,33 +1,41 @@
|
||||||
|
// @ts-ignore
|
||||||
import ReactList from 'react-list';
|
import ReactList from 'react-list';
|
||||||
|
|
||||||
// See also https://github.com/coderiety/react-list/blob/master/react-list.es6
|
// See also https://github.com/coderiety/react-list/blob/master/react-list.es6
|
||||||
class FixedReactList extends ReactList {
|
class FixedReactList extends ReactList {
|
||||||
// deleting a messages or adding a message (per shift) requires invalidating the cache, react-list sucks as it does
|
// deleting a messages or adding a message (per shift) requires invalidating the cache, react-list sucks as it does
|
||||||
// not provide such functionality, therefore we need to hack it inside there :(
|
// not provide such functionality, therefore we need to hack it inside there :(
|
||||||
ignoreNextCacheUpdate = false;
|
public ignoreNextCacheUpdate = false;
|
||||||
|
|
||||||
cacheSizes() {
|
public cacheSizes(): void {
|
||||||
if (this.ignoreNextCacheUpdate) {
|
if (this.ignoreNextCacheUpdate) {
|
||||||
this.ignoreNextCacheUpdate = false;
|
this.ignoreNextCacheUpdate = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// @ts-ignore accessing private member
|
||||||
super.cacheSizes();
|
super.cacheSizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCacheFromIndex(startIndex) {
|
|
||||||
|
public clearCacheFromIndex(startIndex: number): void {
|
||||||
this.ignoreNextCacheUpdate = true;
|
this.ignoreNextCacheUpdate = true;
|
||||||
|
|
||||||
if (startIndex === 0) {
|
if (startIndex === 0) {
|
||||||
|
// @ts-ignore accessing private member
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore accessing private member
|
||||||
Object.keys(this.cache).filter((index) => index >= startIndex).forEach((index) => {
|
Object.keys(this.cache).filter((index) => index >= startIndex).forEach((index) => {
|
||||||
|
// @ts-ignore accessing private member
|
||||||
delete this.cache[index];
|
delete this.cache[index];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate() {
|
public componentDidUpdate() {
|
||||||
|
// @ts-ignore accessing private member
|
||||||
const hasCacheForLastRenderedItem = Object.keys(this.cache).length && this.cache[this.getVisibleRange()[1]];
|
const hasCacheForLastRenderedItem = Object.keys(this.cache).length && this.cache[this.getVisibleRange()[1]];
|
||||||
|
// @ts-ignore accessing private member
|
||||||
super.componentDidUpdate();
|
super.componentDidUpdate();
|
||||||
if (!hasCacheForLastRenderedItem) {
|
if (!hasCacheForLastRenderedItem) {
|
||||||
// when there is no cache for the last rendered item, then its a new item, react-list doesn't know it size
|
// when there is no cache for the last rendered item, then its a new item, react-list doesn't know it size
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import React, {Component} from 'react';
|
import {Theme, WithStyles} from "material-ui";
|
||||||
import AppBar from 'material-ui/AppBar';
|
import AccountCircle from 'material-ui-icons/AccountCircle';
|
||||||
import Button from 'material-ui/Button';
|
|
||||||
import Toolbar from 'material-ui/Toolbar';
|
|
||||||
import Typography from 'material-ui/Typography';
|
|
||||||
import {withStyles} from 'material-ui/styles';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import IconButton from 'material-ui/IconButton';
|
|
||||||
import * as UserAction from '../actions/UserAction';
|
|
||||||
import {Link} from 'react-router-dom';
|
|
||||||
import Chat from 'material-ui-icons/Chat';
|
import Chat from 'material-ui-icons/Chat';
|
||||||
import SupervisorAccount from 'material-ui-icons/SupervisorAccount';
|
|
||||||
import DevicesOther from 'material-ui-icons/DevicesOther';
|
import DevicesOther from 'material-ui-icons/DevicesOther';
|
||||||
import ExitToApp from 'material-ui-icons/ExitToApp';
|
import ExitToApp from 'material-ui-icons/ExitToApp';
|
||||||
import AccountCircle from 'material-ui-icons/AccountCircle';
|
|
||||||
import LightbulbOutline from 'material-ui-icons/LightbulbOutline';
|
import LightbulbOutline from 'material-ui-icons/LightbulbOutline';
|
||||||
|
import SupervisorAccount from 'material-ui-icons/SupervisorAccount';
|
||||||
|
import AppBar from 'material-ui/AppBar';
|
||||||
|
import Button from 'material-ui/Button';
|
||||||
|
import IconButton from 'material-ui/IconButton';
|
||||||
|
import {withStyles} from 'material-ui/styles';
|
||||||
|
import Toolbar from 'material-ui/Toolbar';
|
||||||
|
import Typography from 'material-ui/Typography';
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
import * as UserAction from '../actions/UserAction';
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme: Theme) => ({
|
||||||
appBar: {
|
appBar: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
},
|
},
|
||||||
|
|
@ -33,38 +33,19 @@ const styles = (theme) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Header extends Component {
|
type Styles = WithStyles<'link' | 'titleName' | 'title' | 'appBar'>
|
||||||
static propTypes = {
|
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
loggedIn: PropTypes.bool.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
admin: PropTypes.bool.isRequired,
|
|
||||||
version: PropTypes.string.isRequired,
|
|
||||||
toggleTheme: PropTypes.func.isRequired,
|
|
||||||
showSettings: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
renderButtons(name, admin) {
|
interface IProps {
|
||||||
const {classes, showSettings} = this.props;
|
loggedIn: boolean
|
||||||
return (
|
name: string
|
||||||
<div>
|
admin: boolean
|
||||||
{admin
|
version: string
|
||||||
? <Link className={classes.link} to="/users">
|
toggleTheme: VoidFunction
|
||||||
<Button color="inherit"><SupervisorAccount/> users</Button></Link>
|
showSettings: VoidFunction
|
||||||
: ''}
|
|
||||||
<Link className={classes.link} to="/applications">
|
|
||||||
<Button color="inherit"><Chat/> apps</Button>
|
|
||||||
</Link>
|
|
||||||
<Link className={classes.link} to="/clients"><Button color="inherit">
|
|
||||||
<DevicesOther/> clients</Button>
|
|
||||||
</Link>
|
|
||||||
<Button color="inherit" onClick={showSettings}><AccountCircle/> {name}</Button>
|
|
||||||
<Button color="inherit" onClick={UserAction.logout}><ExitToApp/> Logout</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
class Header extends Component<IProps & Styles> {
|
||||||
|
public render() {
|
||||||
const {classes, version, name, loggedIn, admin, toggleTheme} = this.props;
|
const {classes, version, name, loggedIn, admin, toggleTheme} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -88,6 +69,26 @@ class Header extends Component {
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderButtons(name: string, admin: boolean) {
|
||||||
|
const {classes, showSettings} = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{admin
|
||||||
|
? <Link className={classes.link} to="/users">
|
||||||
|
<Button color="inherit"><SupervisorAccount/> users</Button></Link>
|
||||||
|
: ''}
|
||||||
|
<Link className={classes.link} to="/applications">
|
||||||
|
<Button color="inherit"><Chat/> apps</Button>
|
||||||
|
</Link>
|
||||||
|
<Link className={classes.link} to="/clients"><Button color="inherit">
|
||||||
|
<DevicesOther/> clients</Button>
|
||||||
|
</Link>
|
||||||
|
<Button color="inherit" onClick={showSettings}><AccountCircle/> {name}</Button>
|
||||||
|
<Button color="inherit" onClick={UserAction.logout}><ExitToApp/> Logout</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles, {withTheme: true})(Header);
|
export default withStyles(styles, {withTheme: true})<IProps>(Header);
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import {CircularProgress} from 'material-ui/Progress';
|
|
||||||
import DefaultPage from './DefaultPage';
|
|
||||||
import Grid from 'material-ui/Grid';
|
import Grid from 'material-ui/Grid';
|
||||||
|
import {CircularProgress} from 'material-ui/Progress';
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import DefaultPage from './DefaultPage';
|
||||||
|
|
||||||
class LoadingSpinner extends Component {
|
class LoadingSpinner extends Component {
|
||||||
render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<DefaultPage title="" maxWidth={250} hideButton={true}>
|
<DefaultPage title="" maxWidth={250} hideButton={true}>
|
||||||
<Grid item xs={12} style={{textAlign: 'center'}}>
|
<Grid item xs={12} style={{textAlign: 'center'}}>
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import React, {Component} from 'react';
|
import {WithStyles} from "material-ui";
|
||||||
|
import Delete from 'material-ui-icons/Delete';
|
||||||
|
import IconButton from 'material-ui/IconButton';
|
||||||
import {withStyles} from 'material-ui/styles';
|
import {withStyles} from 'material-ui/styles';
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from 'material-ui/Typography';
|
||||||
import IconButton from 'material-ui/IconButton';
|
import React, {Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Container from './Container';
|
|
||||||
import TimeAgo from 'react-timeago';
|
import TimeAgo from 'react-timeago';
|
||||||
import Delete from 'material-ui-icons/Delete';
|
import Container from './Container';
|
||||||
|
|
||||||
const styles = () => ({
|
const styles = () => ({
|
||||||
header: {
|
header: {
|
||||||
|
|
@ -32,16 +32,18 @@ const styles = () => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Message extends Component {
|
type Style = WithStyles<'header' | 'headerTitle' | 'trash' | 'wrapperPadding' | 'messageContentWrapper' | 'image' | 'imageWrapper'>;
|
||||||
static propTypes = {
|
|
||||||
classes: PropTypes.object.isRequired,
|
interface IProps {
|
||||||
title: PropTypes.string.isRequired,
|
title: string
|
||||||
image: PropTypes.string,
|
image?: string
|
||||||
date: PropTypes.string.isRequired,
|
date: string
|
||||||
content: PropTypes.string.isRequired,
|
content: string
|
||||||
fDelete: PropTypes.func.isRequired,
|
fDelete: VoidFunction
|
||||||
};
|
}
|
||||||
render() {
|
|
||||||
|
class Message extends Component<IProps & Style> {
|
||||||
|
public render() {
|
||||||
const {fDelete, classes, title, date, content, image} = this.props;
|
const {fDelete, classes, title, date, content, image} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -68,4 +70,4 @@ class Message extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(Message);
|
export default withStyles(styles)<IProps>(Message);
|
||||||
|
|
@ -1,47 +1,47 @@
|
||||||
import React, {Component} from 'react';
|
import {Theme, WithStyles} from "material-ui";
|
||||||
import Divider from 'material-ui/Divider';
|
import Divider from 'material-ui/Divider';
|
||||||
import Drawer from 'material-ui/Drawer';
|
import Drawer from 'material-ui/Drawer';
|
||||||
import {ListItem, ListItemText} from 'material-ui/List';
|
import {ListItem, ListItemText} from 'material-ui/List';
|
||||||
import {withStyles} from 'material-ui/styles';
|
import {withStyles} from 'material-ui/styles';
|
||||||
import PropTypes from 'prop-types';
|
import React, {Component} from 'react';
|
||||||
import AppStore from '../stores/AppStore';
|
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
|
import AppStore from '../stores/AppStore';
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme: Theme) => ({
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
position: 'relative',
|
position: 'relative' as 'relative',
|
||||||
width: 250,
|
width: 250,
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
},
|
},
|
||||||
toolbar: theme.mixins.toolbar,
|
toolbar: theme.mixins.toolbar as any,
|
||||||
link: {
|
link: {
|
||||||
color: 'inherit',
|
color: 'inherit',
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Navigation extends Component {
|
type Styles = WithStyles<'drawerPaper' | 'toolbar' | 'link'>
|
||||||
static propTypes = {
|
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
loggedIn: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
interface IProps {
|
||||||
super();
|
loggedIn: boolean
|
||||||
this.state = {apps: []};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
interface IState {
|
||||||
|
apps: IApplication[]
|
||||||
|
}
|
||||||
|
|
||||||
|
class Navigation extends Component<IProps & Styles, IState> {
|
||||||
|
public state: IState = {apps: []};
|
||||||
|
|
||||||
|
public componentWillMount() {
|
||||||
AppStore.on('change', this.updateApps);
|
AppStore.on('change', this.updateApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
AppStore.removeListener('change', this.updateApps);
|
AppStore.removeListener('change', this.updateApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateApps = () => this.setState({apps: AppStore.get()});
|
public render() {
|
||||||
|
|
||||||
render() {
|
|
||||||
const {classes, loggedIn} = this.props;
|
const {classes, loggedIn} = this.props;
|
||||||
const {apps} = this.state;
|
const {apps} = this.state;
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ class Navigation extends Component {
|
||||||
<ListItemText primary="you have no applications :("/>
|
<ListItemText primary="you have no applications :("/>
|
||||||
</ListItem>);
|
</ListItem>);
|
||||||
|
|
||||||
const userApps = apps.length === 0 ? empty : apps.map(function(app) {
|
const userApps = apps.length === 0 ? empty : apps.map((app) => {
|
||||||
return (
|
return (
|
||||||
<Link className={classes.link} to={'/messages/' + app.id} key={app.id}>
|
<Link className={classes.link} to={'/messages/' + app.id} key={app.id}>
|
||||||
<ListItem button>
|
<ListItem button>
|
||||||
|
|
@ -82,6 +82,8 @@ class Navigation extends Component {
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateApps = () => this.setState({apps: AppStore.get()});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles, {withTheme: true})(Navigation);
|
export default withStyles(styles,{withTheme: true})<IProps>(Navigation);
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import Button from 'material-ui/Button';
|
|
||||||
import KeyboardArrowUp from 'material-ui-icons/KeyboardArrowUp';
|
import KeyboardArrowUp from 'material-ui-icons/KeyboardArrowUp';
|
||||||
|
import Button from 'material-ui/Button';
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
class ScrollUpButton extends Component {
|
class ScrollUpButton extends Component {
|
||||||
render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<Button variant="fab" color="primary"
|
<Button variant="fab" color="primary"
|
||||||
style={{position: 'fixed', bottom: '30px', right: '30px', zIndex: 100000}}
|
style={{position: 'fixed', bottom: '30px', right: '30px', zIndex: 100000}}
|
||||||
onClick={() => window.scrollTo(0, 0)}>
|
onClick={this.scrollUp}>
|
||||||
<KeyboardArrowUp/>
|
<KeyboardArrowUp/>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private scrollUp = () => window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ScrollUpButton;
|
export default ScrollUpButton;
|
||||||
|
|
@ -1,28 +1,22 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import Button from 'material-ui/Button';
|
import Button from 'material-ui/Button';
|
||||||
|
import Dialog, {DialogActions, DialogContent, DialogTitle} from 'material-ui/Dialog';
|
||||||
import TextField from 'material-ui/TextField';
|
import TextField from 'material-ui/TextField';
|
||||||
import Tooltip from 'material-ui/Tooltip';
|
import Tooltip from 'material-ui/Tooltip';
|
||||||
import Dialog, {DialogActions, DialogContent, DialogTitle} from 'material-ui/Dialog';
|
import React, {ChangeEvent, Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as UserAction from '../actions/UserAction';
|
import * as UserAction from '../actions/UserAction';
|
||||||
|
|
||||||
export default class SettingsDialog extends Component {
|
interface IState {
|
||||||
static propTypes = {
|
pass: string
|
||||||
fClose: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.state = {pass: ''};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(propertyName, event) {
|
interface IProps {
|
||||||
const state = this.state;
|
fClose: VoidFunction
|
||||||
state[propertyName] = event.target.value;
|
|
||||||
this.setState(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
export default class SettingsDialog extends Component<IProps, IState> {
|
||||||
|
public state = {pass: ''};
|
||||||
|
|
||||||
|
public render() {
|
||||||
const {pass} = this.state;
|
const {pass} = this.state;
|
||||||
const {fClose} = this.props;
|
const {fClose} = this.props;
|
||||||
const submitAndClose = () => {
|
const submitAndClose = () => {
|
||||||
|
|
@ -50,4 +44,10 @@ export default class SettingsDialog extends Component {
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleChange(propertyName: string, event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
const state = this.state;
|
||||||
|
state[propertyName] = event.target.value;
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,54 +1,37 @@
|
||||||
|
import Close from 'material-ui-icons/Close';
|
||||||
|
import IconButton from 'material-ui/IconButton';
|
||||||
|
import Snackbar from 'material-ui/Snackbar';
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import SnackBarStore from '../stores/SnackBarStore';
|
import SnackBarStore from '../stores/SnackBarStore';
|
||||||
import Snackbar from 'material-ui/Snackbar';
|
|
||||||
import IconButton from 'material-ui/IconButton';
|
|
||||||
import Close from 'material-ui-icons/Close';
|
|
||||||
|
|
||||||
class SnackBarHandler extends Component {
|
|
||||||
static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000;
|
|
||||||
static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000;
|
|
||||||
|
|
||||||
state = {
|
interface IState {
|
||||||
|
current: string
|
||||||
|
hasNext: boolean
|
||||||
|
open: boolean
|
||||||
|
openWhen: number
|
||||||
|
}
|
||||||
|
|
||||||
|
class SnackBarHandler extends Component<{}, IState> {
|
||||||
|
private static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000;
|
||||||
|
private static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000;
|
||||||
|
|
||||||
|
public state = {
|
||||||
current: '',
|
current: '',
|
||||||
hasNext: false,
|
hasNext: false,
|
||||||
open: false,
|
open: false,
|
||||||
openWhen: 0,
|
openWhen: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount = () => SnackBarStore.on('change', this.onNewSnack);
|
public componentWillMount() {
|
||||||
componentWillUnmount = () => SnackBarStore.removeListener('change', this.onNewSnack);
|
SnackBarStore.on('change', this.onNewSnack);
|
||||||
|
|
||||||
onNewSnack = () => {
|
|
||||||
const {open, openWhen} = this.state;
|
|
||||||
|
|
||||||
if (!open) {
|
|
||||||
this.openNextSnack();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const snackOpenSince = Date.now() - openWhen;
|
public componentWillUnmount() {
|
||||||
if (snackOpenSince > SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS) {
|
SnackBarStore.removeListener('change', this.onNewSnack);
|
||||||
this.closeCurrentSnack();
|
|
||||||
} else {
|
|
||||||
setTimeout(this.closeCurrentSnack, SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS - snackOpenSince);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
openNextSnack = () => {
|
public render() {
|
||||||
if (SnackBarStore.hasNext()) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
open: true,
|
|
||||||
openWhen: Date.now(),
|
|
||||||
current: SnackBarStore.next(),
|
|
||||||
hasNext: SnackBarStore.hasNext(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
closeCurrentSnack = () => this.setState({...this.state, open: false});
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {open, current, hasNext} = this.state;
|
const {open, current, hasNext} = this.state;
|
||||||
const duration = hasNext
|
const duration = hasNext
|
||||||
? SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS
|
? SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS
|
||||||
|
|
@ -68,6 +51,36 @@ class SnackBarHandler extends Component {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onNewSnack = () => {
|
||||||
|
const {open, openWhen} = this.state;
|
||||||
|
|
||||||
|
if (!open) {
|
||||||
|
this.openNextSnack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snackOpenSince = Date.now() - openWhen;
|
||||||
|
if (snackOpenSince > SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS) {
|
||||||
|
this.closeCurrentSnack();
|
||||||
|
} else {
|
||||||
|
setTimeout(this.closeCurrentSnack, SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS - snackOpenSince);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private openNextSnack = () => {
|
||||||
|
if (SnackBarStore.hasNext()) {
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
open: true,
|
||||||
|
openWhen: Date.now(),
|
||||||
|
current: SnackBarStore.next(),
|
||||||
|
hasNext: SnackBarStore.hasNext(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private closeCurrentSnack = () => this.setState({...this.state, open: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SnackBarHandler;
|
export default SnackBarHandler;
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import Typography from 'material-ui/Typography';
|
|
||||||
import IconButton from 'material-ui/IconButton';
|
|
||||||
import VisibilityOff from 'material-ui-icons/VisibilityOff';
|
|
||||||
import Visibility from 'material-ui-icons/Visibility';
|
import Visibility from 'material-ui-icons/Visibility';
|
||||||
import PropTypes from 'prop-types';
|
import VisibilityOff from 'material-ui-icons/VisibilityOff';
|
||||||
|
import IconButton from 'material-ui/IconButton';
|
||||||
|
import Typography from 'material-ui/Typography';
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
class ToggleVisibility extends Component {
|
interface IProps {
|
||||||
static propTypes = {
|
value: string
|
||||||
value: PropTypes.string.isRequired,
|
style?: object
|
||||||
style: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.state = {visible: false};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVisibility = () => this.setState({visible: !this.state.visible});
|
interface IState {
|
||||||
|
visible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
class ToggleVisibility extends Component<IProps, IState> {
|
||||||
|
public state = {visible: false};
|
||||||
|
|
||||||
|
public render() {
|
||||||
const {value, style} = this.props;
|
const {value, style} = this.props;
|
||||||
const text = this.state.visible ? value : '•••••••••••••••';
|
const text = this.state.visible ? value : '•••••••••••••••';
|
||||||
return (
|
return (
|
||||||
|
|
@ -32,6 +30,8 @@ class ToggleVisibility extends Component {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleVisibility = () => this.setState({visible: !this.state.visible});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue