Refactor project structure

This commit is contained in:
Thomas Nordquist
2019-04-07 20:16:48 +02:00
parent 16c72fa9be
commit e2c60cca64
44 changed files with 306 additions and 529 deletions

View File

@@ -0,0 +1,28 @@
import * as React from 'react'
import ReactSplitPane from 'react-split-pane'
import { Sidebar } from '../Sidebar'
import Tree from '../Tree/Tree'
export default function ContentView(props: {heightProperty: any, paneDefaults: any, connectionId: any}) {
return (
<ReactSplitPane
step={20}
primary="second"
className={props.heightProperty}
split="vertical"
minSize={250}
defaultSize={500}
resizerStyle={{ width: '2px', margin: '0 -10px 0 0px' }}
allowResize={true}
style={{ position: 'relative' }}
pane1Style={{ overflow: 'hidden' }}
>
<div className={props.paneDefaults}>
<Tree />
</div>
<div className={props.paneDefaults}>
<Sidebar connectionId={props.connectionId} />
</div>
</ReactSplitPane>
)
}

View File

@@ -0,0 +1,51 @@
import * as React from 'react'
import { Snackbar, SnackbarContent } from '@material-ui/core'
import { Theme, withStyles } from '@material-ui/core/styles'
import { green, red } from '@material-ui/core/colors'
interface Props {
message?: string
onClose: () => void
classes: any
}
class Notification extends React.Component<Props, {}> {
constructor(props: Props) {
super(props)
}
public static styles = (theme: Theme) => ({
success: {
backgroundColor: green[600],
color: theme.typography.button.color,
},
error: {
backgroundColor: red[600],
color: theme.typography.button.color,
},
})
public render() {
const snackbarAnchor = {
vertical: 'bottom' as 'bottom',
horizontal: 'left' as 'left',
}
return (
<Snackbar
anchorOrigin={snackbarAnchor}
open={Boolean(this.props.message)}
autoHideDuration={10000}
onClose={this.props.onClose}
>
<SnackbarContent
className={this.props.classes.error}
message={this.props.message}
/>
</Snackbar>
)
}
}
export default withStyles(Notification.styles)(Notification)

View File

@@ -0,0 +1,108 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
import CustomIconButton from '../helper/CustomIconButton'
import Pause from '@material-ui/icons/PauseCircleFilled'
import Resume from '@material-ui/icons/PlayCircleFilled'
import { AppState } from '../../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { treeActions } from '../../actions'
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
import { Tooltip } from '@material-ui/core'
const styles: StyleRulesCallback = theme => ({ })
interface Props {
classes: any
actions: {
tree: typeof treeActions,
}
paused: boolean
tree?: q.Tree<any>
}
class PauseButton extends React.Component<Props, {changes: number}> {
private timer?: any
constructor(props: Props) {
super(props)
this.state = { changes: 0 }
}
private renderResume() {
return (
<Tooltip title="Resumes updating the tree, after applying all recorded changes">
<Resume />
</Tooltip>
)
}
private renderPause() {
return (
<Tooltip title="Stops all updates, records changes until the buffer is full.">
<Pause />
</Tooltip>
)
}
private renderBufferStats() {
if (!this.props.tree) {
return
}
return (
<span>
{this.state.changes} changes<br />
buffer at {Math.round(this.props.tree.unmergedChanges().fillState() * 10000) / 100}%
</span>
)
}
public componentDidMount() {
this.timer = setInterval(() => {
if (!this.props.paused || !this.props.tree) {
return
}
const changes = this.props.tree.unmergedChanges()
if (this.state.changes !== changes.length) {
this.setState({ changes: changes.length })
}
}, 300)
}
public componentWillUnmount() {
this.timer && clearInterval(this.timer)
}
public render() {
const { actions, classes } = this.props
return (
<div style={{ display: 'inline-flex' }}>
<span>
<CustomIconButton onClick={this.props.actions.tree.togglePause} >
{this.props.paused ? this.renderResume() : this.renderPause()}
</CustomIconButton>
</span>
{this.props.paused ? this.renderBufferStats() : null}
</div>
)
}
}
const mapStateToProps = (state: AppState) => {
return {
paused: state.tree.paused,
tree: state.tree.tree,
}
}
const mapDispatchToProps = (dispatch: any) => {
return {
actions: {
tree: bindActionCreators(treeActions, dispatch),
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(PauseButton))

View File

@@ -0,0 +1,158 @@
import * as React from 'react'
import ClearAdornment from '../helper/ClearAdornment'
import CloudOff from '@material-ui/icons/CloudOff'
import ConnectionHealthIndicator from '../helper/ConnectionHealthIndicator'
import Menu from '@material-ui/icons/Menu'
import Search from '@material-ui/icons/Search'
import { AppState } from '../../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { connectionActions, settingsActions, treeActions } from '../../actions'
import { fade } from '@material-ui/core/styles/colorManipulator'
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
import {
AppBar,
Button,
IconButton,
InputBase,
Toolbar,
Typography,
} from '@material-ui/core'
import PauseButton from './PauseButton'
const styles: StyleRulesCallback = theme => ({
title: {
display: 'none',
[theme.breakpoints.up('sm')]: {
display: 'block',
},
},
search: {
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(3),
width: 'auto',
},
},
searchIcon: {
width: theme.spacing(8),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
inputRoot: {
color: 'inherit',
width: '100%',
},
inputInput: {
paddingTop: theme.spacing(1),
paddingRight: theme.spacing(1),
paddingBottom: theme.spacing(1),
paddingLeft: theme.spacing(10),
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('md')]: {
width: 200,
},
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
disconnect: {
margin: 'auto 8px auto auto',
color: theme.palette.primary.contrastText,
},
})
interface Props {
classes: any
actions: {
settings: typeof settingsActions,
connection: typeof connectionActions,
}
topicFilter?: string
}
class TitleBar extends React.Component<Props, {}> {
constructor(props: any) {
super(props)
this.state = { }
}
private renderSearch() {
const { classes, topicFilter } = this.props
return (
<div className={classes.search}>
<div className={classes.searchIcon}>
<Search />
</div>
<InputBase
value={topicFilter}
onChange={this.onFilterChange}
placeholder="Search…"
endAdornment={<div style={{ width: '24px', paddingRight: '8px' }}><ClearAdornment action={this.clearFilter} value={topicFilter} /></div>}
classes={{ root: classes.inputRoot, input: classes.inputInput }}
/>
</div>
)
}
private clearFilter = () => {
this.props.actions.settings.filterTopics('')
}
private onFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.props.actions.settings.filterTopics(event.target.value)
}
public render() {
const { actions, classes } = this.props
return (
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu" onClick={actions.settings.toggleSettingsVisibility}>
<Menu />
</IconButton>
<Typography className={classes.title} variant="h6" color="inherit">MQTT Explorer</Typography>
{this.renderSearch()}
<PauseButton />
<Button className={classes.disconnect} onClick={actions.connection.disconnect}>
Disconnect <CloudOff style={{ marginRight: '8px', paddingLeft: '8px' }}/>
</Button>
<ConnectionHealthIndicator withBackground={true} />
</Toolbar>
</AppBar>
)
}
}
const mapStateToProps = (state: AppState) => {
return {
topicFilter: state.settings.topicFilter,
}
}
const mapDispatchToProps = (dispatch: any) => {
return {
actions: {
settings: bindActionCreators(settingsActions, dispatch),
connection: bindActionCreators(connectionActions, dispatch),
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(TitleBar))