Refactor project structure
This commit is contained in:
28
app/src/components/Layout/ContentView.tsx
Normal file
28
app/src/components/Layout/ContentView.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
51
app/src/components/Layout/Notification.tsx
Normal file
51
app/src/components/Layout/Notification.tsx
Normal 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)
|
||||
108
app/src/components/Layout/PauseButton.tsx
Normal file
108
app/src/components/Layout/PauseButton.tsx
Normal 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))
|
||||
158
app/src/components/Layout/TitleBar.tsx
Normal file
158
app/src/components/Layout/TitleBar.tsx
Normal 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))
|
||||
Reference in New Issue
Block a user