WiP #broken
This commit is contained in:
32
app/package-lock.json
generated
32
app/package-lock.json
generated
@@ -4172,14 +4172,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"version": "16.3.2",
|
"version": "16.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-16.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz",
|
||||||
"integrity": "sha512-o5GPdkhciQ3cEph6qgvYB7LTOHw/GB0qRI6ZFNugj49qJCFfgHwVNjZ5u+b7nif4vOeMIOuYj3CeYe2IBD74lg==",
|
"integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fbjs": "^0.8.16",
|
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"prop-types": "^15.6.0"
|
"prop-types": "^15.6.2",
|
||||||
|
"scheduler": "^0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-base16-styling": {
|
"react-base16-styling": {
|
||||||
@@ -4194,14 +4195,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.3.3",
|
"version": "16.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz",
|
||||||
"integrity": "sha512-ALCp7ZbSGkqRDtQoZozKVNgwXMxbxf/IGOUMC2A0yF6JHeZrS8e2cOotPT87Vf4b7PKCuUVKU4/RDEXxToA/yA==",
|
"integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fbjs": "^0.8.16",
|
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"prop-types": "^15.6.0"
|
"prop-types": "^15.6.2",
|
||||||
|
"scheduler": "^0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-event-listener": {
|
"react-event-listener": {
|
||||||
@@ -4438,6 +4440,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"scheduler": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz",
|
||||||
|
"integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.1.0",
|
||||||
|
"object-assign": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "0.4.7",
|
"version": "0.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
|
||||||
|
|||||||
@@ -15,8 +15,9 @@
|
|||||||
"@types/react": "^16.7.18",
|
"@types/react": "^16.7.18",
|
||||||
"@types/react-dom": "^16.0.11",
|
"@types/react-dom": "^16.0.11",
|
||||||
"@types/react-redux": "^6.0.12",
|
"@types/react-redux": "^6.0.12",
|
||||||
|
"react": "^16.7.0",
|
||||||
|
"react-dom": "^16.7.0",
|
||||||
"react-redux": "^6.0.0",
|
"react-redux": "^6.0.0",
|
||||||
"react-transition-group": "^2.5.2",
|
|
||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"webpack-livereload-plugin": "^2.2.0"
|
"webpack-livereload-plugin": "^2.2.0"
|
||||||
},
|
},
|
||||||
@@ -31,8 +32,6 @@
|
|||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"moving-average": "^1.0.0",
|
"moving-average": "^1.0.0",
|
||||||
"react": "^16.3.2",
|
|
||||||
"react-dom": "^16.3.3",
|
|
||||||
"react-json-view": "^1.19.1",
|
"react-json-view": "^1.19.1",
|
||||||
"sha1": "^1.1.1",
|
"sha1": "^1.1.1",
|
||||||
"socket.io-client": "^2.2.0",
|
"socket.io-client": "^2.2.0",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ActionTypes } from '../reducers'
|
import { ActionTypes, NodeOrder } from '../reducers'
|
||||||
|
|
||||||
export const setAutoExpandLimit = (autoExpandLimit: number = 0) => {
|
export const setAutoExpandLimit = (autoExpandLimit: number = 0) => {
|
||||||
return {
|
return {
|
||||||
@@ -12,3 +12,10 @@ export const toggleSettingsVisibility = () => {
|
|||||||
type: ActionTypes.toggleSettingsVisibility,
|
type: ActionTypes.toggleSettingsVisibility,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setNodeOrder = (nodeOrder: NodeOrder = NodeOrder.none) => {
|
||||||
|
return {
|
||||||
|
nodeOrder,
|
||||||
|
type: ActionTypes.setNodeOrder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ class Notification extends React.Component<Props, State> {
|
|||||||
vertical: 'bottom',
|
vertical: 'bottom',
|
||||||
horizontal: 'left',
|
horizontal: 'left',
|
||||||
}}
|
}}
|
||||||
open = {this.state.snackBarOpen}
|
open={this.state.snackBarOpen}
|
||||||
autoHideDuration = {6000}
|
autoHideDuration={6000}
|
||||||
onClose = {() => { this.setState({ snackBarOpen: false }) }}
|
onClose={() => { this.setState({ snackBarOpen: false }) }}
|
||||||
>
|
>
|
||||||
<SnackbarContent
|
<SnackbarContent
|
||||||
className = {this.props.classes[this.props.type]}
|
className={this.props.classes[this.props.type]}
|
||||||
message = {this.props.message}
|
message={this.props.message}
|
||||||
/>
|
/>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class Copy extends React.Component<Props, State> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const icon = !this.state.didCopy
|
const icon = !this.state.didCopy
|
||||||
? <FileCopy fontSize="inherit" style={ { cursor: 'pointer' } } onClick={this.handleClick} />
|
? <FileCopy fontSize="inherit" style={{ cursor: 'pointer' }} onClick={this.handleClick} />
|
||||||
: <Check fontSize="inherit" style={ { cursor: 'default' } } />
|
: <Check fontSize="inherit" style={{ cursor: 'default' }} />
|
||||||
|
|
||||||
return <span>
|
return <span>
|
||||||
<span style={{ fontSize: '16px' }}>{icon}</span>
|
<span style={{ fontSize: '16px' }}>{icon}</span>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Typography, InputBase, Input, InputLabel } from '@material-ui/core'
|
|||||||
import { withStyles, StyleRulesCallback } from '@material-ui/core/styles'
|
import { withStyles, StyleRulesCallback } from '@material-ui/core/styles'
|
||||||
|
|
||||||
import { settingsActions } from '../actions'
|
import { settingsActions } from '../actions'
|
||||||
import { AppState } from '../reducers'
|
import { AppState, NodeOrder } from '../reducers'
|
||||||
|
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
@@ -40,16 +40,14 @@ const styles: StyleRulesCallback = theme => ({
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
actions?: any
|
actions?: any
|
||||||
autoExpandLimit: number,
|
autoExpandLimit: number
|
||||||
visible: boolean,
|
visible: boolean
|
||||||
store?: any,
|
store?: any
|
||||||
classes: any
|
classes: any
|
||||||
|
nodeOrder: NodeOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
class Settings extends React.Component<Props, {}> {
|
||||||
}
|
|
||||||
|
|
||||||
class Settings extends React.Component<Props, State> {
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {}
|
this.state = {}
|
||||||
@@ -57,22 +55,23 @@ class Settings extends React.Component<Props, State> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { classes, actions, visible } = this.props
|
const { classes, actions, visible } = this.props
|
||||||
return <Drawer
|
return (
|
||||||
anchor = "left"
|
<Drawer
|
||||||
className = { classes.drawer }
|
anchor="left"
|
||||||
open = { visible }
|
className={classes.drawer}
|
||||||
variant = "persistent"
|
open={visible}
|
||||||
|
variant="persistent"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className = { classes.paper }
|
className={classes.paper}
|
||||||
tabIndex = { 0 }
|
tabIndex={0}
|
||||||
role = "button"
|
role="button"
|
||||||
onClick = {(e: React.MouseEvent) => e.stopPropagation()}
|
onClick={(e: React.MouseEvent) => e.stopPropagation()}
|
||||||
onKeyDown = {(e: React.KeyboardEvent) => e.stopPropagation()}
|
onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Typography className = { classes.title } variant = "h6" color = "inherit">
|
<Typography className={ classes.title } variant="h6" color="inherit">
|
||||||
<IconButton onClick = {actions.toggleSettingsVisibility}>
|
<IconButton onClick={actions.toggleSettingsVisibility}>
|
||||||
<ChevronRight />
|
<ChevronRight />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
Settings
|
Settings
|
||||||
@@ -80,36 +79,67 @@ class Settings extends React.Component<Props, State> {
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{this.renderAutoExpand()}
|
{this.renderAutoExpand()}
|
||||||
|
{this.renderNodeOrder()}
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAutoExpand() {
|
private renderAutoExpand() {
|
||||||
const { classes, actions, autoExpandLimit } = this.props
|
const { classes, actions, autoExpandLimit } = this.props
|
||||||
|
|
||||||
return <div style={{ padding: '8px' }}>
|
return (
|
||||||
|
<div style={{ padding: '8px' }}>
|
||||||
<InputLabel htmlFor="auto-expand">Auto Expand</InputLabel>
|
<InputLabel htmlFor="auto-expand">Auto Expand</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value = { autoExpandLimit }
|
value={autoExpandLimit}
|
||||||
onChange = { (e: React.ChangeEvent<HTMLSelectElement>) => actions.setAutoExpandLimit(e.target.value) }
|
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => actions.setAutoExpandLimit(e.target.value) }
|
||||||
input = {<Input name="auto-expand" id="auto-expand-label-placeholder" />}
|
input={<Input name="auto-expand" id="auto-expand-label-placeholder" />}
|
||||||
displayEmpty
|
displayEmpty={true}
|
||||||
name = "auto-expand"
|
name="auto-expand"
|
||||||
className = {classes.input}
|
className={classes.input}
|
||||||
>
|
>
|
||||||
<MenuItem value = {0}><em>Disabled</em></MenuItem>
|
<MenuItem value={0}><em>Disabled</em></MenuItem>
|
||||||
<MenuItem value = {2}>Few</MenuItem>
|
<MenuItem value={2}>Few</MenuItem>
|
||||||
<MenuItem value = {3}>Some</MenuItem>
|
<MenuItem value={3}>Some</MenuItem>
|
||||||
<MenuItem value = {10}>Most</MenuItem>
|
<MenuItem value={10}>Most</MenuItem>
|
||||||
<MenuItem value = {1E6}>All</MenuItem>
|
<MenuItem value={1E6}>All</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNodeOrder() {
|
||||||
|
const { classes, actions, nodeOrder } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '8px' }}>
|
||||||
|
<InputLabel htmlFor="auto-expand">Topic order</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={nodeOrder}
|
||||||
|
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
actions.setNodeOrder(e.target.value)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
input={<Input name="node-order" id="node-order-label-placeholder" />}
|
||||||
|
displayEmpty={true}
|
||||||
|
name="node-order"
|
||||||
|
className={classes.input}
|
||||||
|
>
|
||||||
|
<MenuItem value={NodeOrder.none}><em>default</em></MenuItem>
|
||||||
|
<MenuItem value={NodeOrder.abc}>a-z</MenuItem>
|
||||||
|
<MenuItem value={NodeOrder.messages}>{NodeOrder.messages}</MenuItem>
|
||||||
|
<MenuItem value={NodeOrder.topics}>{NodeOrder.topics}</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</div>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState) => {
|
const mapStateToProps = (state: AppState) => {
|
||||||
return {
|
return {
|
||||||
autoExpandLimit: state.settings.autoExpandLimit,
|
autoExpandLimit: state.settings.autoExpandLimit,
|
||||||
|
nodeOrder: state.settings.nodeOrder,
|
||||||
visible: state.settings.visible,
|
visible: state.settings.visible,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { AppState } from '../../reducers'
|
|||||||
const MovingAverage = require('moving-average')
|
const MovingAverage = require('moving-average')
|
||||||
|
|
||||||
declare const performance: any
|
declare const performance: any
|
||||||
|
declare const window: any
|
||||||
|
|
||||||
const timeInterval = 10 * 1000
|
const timeInterval = 10 * 1000
|
||||||
const average = MovingAverage(timeInterval)
|
const average = MovingAverage(timeInterval)
|
||||||
@@ -48,14 +49,17 @@ class Tree extends React.Component<Props, TreeState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const expectedRenderTime = average.forecast()
|
const expectedRenderTime = average.forecast()
|
||||||
const updateInterval = Math.max(expectedRenderTime * 5, 300)
|
const updateInterval = Math.max(expectedRenderTime * 20, 300)
|
||||||
const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate)
|
const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate)
|
||||||
|
|
||||||
this.updateTimer = setTimeout(() => {
|
this.updateTimer = setTimeout(() => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
console.log('doRender')
|
||||||
this.lastUpdate = performance.now()
|
this.lastUpdate = performance.now()
|
||||||
this.updateTimer && clearTimeout(this.updateTimer)
|
this.updateTimer && clearTimeout(this.updateTimer)
|
||||||
this.updateTimer = undefined
|
this.updateTimer = undefined
|
||||||
this.setState(state)
|
this.setState(state)
|
||||||
|
})
|
||||||
}, Math.max(0, timeUntilNextUpdate))
|
}, Math.max(0, timeUntilNextUpdate))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,15 +102,15 @@ class Tree extends React.Component<Props, TreeState> {
|
|||||||
cursor: 'default',
|
cursor: 'default',
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Typography style={ style }>
|
return <Typography style={style}>
|
||||||
<TreeNode
|
<TreeNode
|
||||||
animateChages = { true }
|
animateChages={true}
|
||||||
autoExpandLimit = { this.props.autoExpandLimit }
|
autoExpandLimit={this.props.autoExpandLimit}
|
||||||
isRoot = { true }
|
isRoot={true}
|
||||||
didSelectNode = { this.props.didSelectNode }
|
didSelectNode={this.props.didSelectNode}
|
||||||
treeNode = { this.state.tree }
|
treeNode={this.state.tree}
|
||||||
name = "/"
|
name="/"
|
||||||
collapsed = { false }
|
collapsed={false}
|
||||||
key="rootNode"
|
key="rootNode"
|
||||||
performanceCallback={(ms: number) => {
|
performanceCallback={(ms: number) => {
|
||||||
average.push(Date.now(), ms)
|
average.push(Date.now(), ms)
|
||||||
|
|||||||
@@ -136,38 +136,41 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
const { classes } = this.props
|
const { classes } = this.props
|
||||||
this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false
|
this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false
|
||||||
|
|
||||||
return <div
|
return (
|
||||||
|
<div
|
||||||
key={this.props.treeNode.hash()}
|
key={this.props.treeNode.hash()}
|
||||||
className={`${classes.node} ${!this.props.isRoot ? classes.hover : ''}`}
|
className={`${classes.node} ${!this.props.isRoot ? classes.hover : ''}`}
|
||||||
onClick={(event) => {
|
onClick={this.didClickNode}
|
||||||
event.stopPropagation()
|
|
||||||
this.toggle()
|
|
||||||
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span ref={this.titleRef} style={animationStyle}>
|
<span ref={this.titleRef} style={animationStyle}>
|
||||||
<TreeNodeTitle
|
<TreeNodeTitle
|
||||||
onClick={() => this.toggle()}
|
|
||||||
collapsed={this.collapsed()}
|
collapsed={this.collapsed()}
|
||||||
treeNode={this.props.treeNode}
|
treeNode={this.props.treeNode}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
didSelectNode={this.props.didSelectNode}
|
didSelectNode={this.props.didSelectNode}
|
||||||
toggleCollapsed={() => this.toggle()}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{ this.renderNodes() }
|
{this.renderNodes()}
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private didClickNode = (event: React.MouseEvent) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
this.toggle()
|
||||||
|
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderNodes() {
|
private renderNodes() {
|
||||||
return <TreeNodeSubnodes
|
return (
|
||||||
|
<TreeNodeSubnodes
|
||||||
animateChanges={this.props.animateChages}
|
animateChanges={this.props.animateChages}
|
||||||
collapsed={this.collapsed()}
|
collapsed={this.collapsed()}
|
||||||
autoExpandLimit={this.props.autoExpandLimit}
|
autoExpandLimit={this.props.autoExpandLimit}
|
||||||
didSelectNode={this.props.didSelectNode}
|
didSelectNode={this.props.didSelectNode}
|
||||||
toggleCollapsed={() => this.toggle()}
|
|
||||||
treeNode={this.props.treeNode}
|
treeNode={this.props.treeNode}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private indicatingChangeAnimationStyle(): React.CSSProperties {
|
private indicatingChangeAnimationStyle(): React.CSSProperties {
|
||||||
|
|||||||
@@ -1,34 +1,54 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
import * as q from '../../../../backend/src/Model'
|
import * as q from '../../../../backend/src/Model'
|
||||||
import { withTheme, Theme } from '@material-ui/core/styles'
|
import { withTheme, Theme } from '@material-ui/core/styles'
|
||||||
|
import { AppState, NodeOrder } from '../../reducers'
|
||||||
|
|
||||||
import TreeNode from './TreeNode'
|
import TreeNode from './TreeNode'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
nodeOrder?: NodeOrder
|
||||||
animateChanges: boolean
|
animateChanges: boolean
|
||||||
treeNode: q.TreeNode
|
treeNode: q.TreeNode
|
||||||
autoExpandLimit: number
|
autoExpandLimit: number
|
||||||
collapsed?: boolean | undefined
|
collapsed?: boolean | undefined
|
||||||
didSelectNode?: (node: q.TreeNode) => void
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
toggleCollapsed: () => void
|
|
||||||
theme: Theme
|
theme: Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
class TreeNodeSubnodes extends React.Component<Props, {}> {
|
class TreeNodeSubnodes extends React.Component<Props, {}> {
|
||||||
|
private sortedNodes(): q.TreeNode[] {
|
||||||
|
const { nodeOrder, treeNode } = this.props
|
||||||
|
|
||||||
|
let edges = Object.values(treeNode.edges)
|
||||||
|
if (nodeOrder === NodeOrder.abc) {
|
||||||
|
edges = edges.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodes = edges.map(edge => edge.target)
|
||||||
|
if (nodeOrder === NodeOrder.messages) {
|
||||||
|
nodes = nodes.sort((a, b) => b.leafMessageCount() - a.leafMessageCount())
|
||||||
|
}
|
||||||
|
if (nodeOrder === NodeOrder.topics) {
|
||||||
|
nodes = nodes.sort((a, b) => b.leafCount() - a.leafCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const edges = Object.values(this.props.treeNode.edges)
|
const edges = Object.values(this.props.treeNode.edges)
|
||||||
|
if (edges.length === 0 || this.props.collapsed) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const listItemStyle = {
|
const listItemStyle = {
|
||||||
padding: '3px 8px 0px 8px',
|
padding: '3px 8px 0px 8px',
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edges.length > 0 && !this.props.collapsed) {
|
const nodes = this.sortedNodes()
|
||||||
const listItems = edges
|
const listItems = nodes.map(node => (
|
||||||
.map(edge => edge.target)
|
<div key={node.hash()} style={listItemStyle}>
|
||||||
.map(node => (
|
|
||||||
<div
|
|
||||||
key={node.hash()}
|
|
||||||
style={listItemStyle}
|
|
||||||
>
|
|
||||||
<TreeNode
|
<TreeNode
|
||||||
animateChages={this.props.animateChanges}
|
animateChages={this.props.animateChanges}
|
||||||
treeNode={node}
|
treeNode={node}
|
||||||
@@ -38,15 +58,18 @@ class TreeNodeSubnodes extends React.Component<Props, {}> {
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
||||||
return <span
|
return (
|
||||||
style={{ display: 'block', clear: 'both' }}
|
<span style={{ display: 'block', clear: 'both' }} >
|
||||||
>
|
|
||||||
{this.props.collapsed ? null : listItems}
|
{this.props.collapsed ? null : listItems}
|
||||||
</span>
|
</span>
|
||||||
}
|
)
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme()(TreeNodeSubnodes)
|
const mapStateToProps = (state: AppState) => {
|
||||||
|
return {
|
||||||
|
nodeOrder: state.settings.nodeOrder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withTheme()(connect(mapStateToProps)(TreeNodeSubnodes))
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
|
|||||||
treeNode: q.TreeNode
|
treeNode: q.TreeNode
|
||||||
name?: string | undefined
|
name?: string | undefined
|
||||||
collapsed?: boolean | undefined
|
collapsed?: boolean | undefined
|
||||||
toggleCollapsed: () => void
|
|
||||||
didSelectNode?: (node: q.TreeNode) => void
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
theme: Theme
|
theme: Theme
|
||||||
}
|
}
|
||||||
@@ -24,25 +23,22 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private didSelectNode = () => {
|
||||||
|
if (this.props.treeNode.message) {
|
||||||
|
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const style: React.CSSProperties = {
|
const style: React.CSSProperties = {
|
||||||
lineHeight: '1em',
|
lineHeight: '1em',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}
|
}
|
||||||
return <span
|
return (
|
||||||
style={style}
|
<span style={style} onMouseOver={this.didSelectNode}>
|
||||||
onClick={() => {
|
|
||||||
this.toggle()
|
|
||||||
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
|
||||||
}}
|
|
||||||
onMouseOver={() => {
|
|
||||||
if (this.props.treeNode.message) {
|
|
||||||
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
||||||
</span>
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSourceEdge() {
|
private renderSourceEdge() {
|
||||||
@@ -67,16 +63,10 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
}
|
}
|
||||||
return this.props.treeNode.message
|
return this.props.treeNode.message
|
||||||
? <span
|
? <span style={style}> = {this.props.treeNode.message.value.toString()}</span>
|
||||||
style={style}
|
|
||||||
> = {this.props.treeNode.message.value.toString()}</span>
|
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggle() {
|
|
||||||
this.props.toggleCollapsed()
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderExpander() {
|
private renderExpander() {
|
||||||
if (this.props.treeNode.edgeCount() === 0) {
|
if (this.props.treeNode.edgeCount() === 0) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Provider } from 'react-redux'
|
|||||||
import { createStore } from 'redux'
|
import { createStore } from 'redux'
|
||||||
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
|
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
|
||||||
|
|
||||||
import reducers from './reducers'
|
import reducers, { NodeOrder } from './reducers'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
|
||||||
declare var document: any
|
declare var document: any
|
||||||
@@ -12,6 +12,7 @@ declare var document: any
|
|||||||
const initialAppState = {
|
const initialAppState = {
|
||||||
settings: {
|
settings: {
|
||||||
autoExpandLimit: 0,
|
autoExpandLimit: 0,
|
||||||
|
nodeOrder: NodeOrder.none,
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,30 @@ import { Reducer, Action } from 'redux'
|
|||||||
export enum ActionTypes {
|
export enum ActionTypes {
|
||||||
setAutoExpandLimit = 'SET_AUTO_EXPAND_LIMIT',
|
setAutoExpandLimit = 'SET_AUTO_EXPAND_LIMIT',
|
||||||
toggleSettingsVisibility = 'TOGGLE_SETTINGS_VISIBILITY',
|
toggleSettingsVisibility = 'TOGGLE_SETTINGS_VISIBILITY',
|
||||||
|
setNodeOrder = 'SET_NODE_ORDER',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SettingsAction extends Action {
|
interface SettingsAction extends Action {
|
||||||
type: ActionTypes,
|
type: ActionTypes,
|
||||||
autoExpandLimit: number
|
autoExpandLimit?: number
|
||||||
|
nodeOrder?: NodeOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
settings: SettingsModel
|
settings: SettingsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum NodeOrder {
|
||||||
|
none = 'none',
|
||||||
|
messages = '#messages',
|
||||||
|
abc = 'abc',
|
||||||
|
topics = '#topics',
|
||||||
|
}
|
||||||
|
|
||||||
export interface SettingsModel {
|
export interface SettingsModel {
|
||||||
autoExpandLimit: number
|
autoExpandLimit: number
|
||||||
visible: boolean
|
visible: boolean
|
||||||
|
nodeOrder: NodeOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) => {
|
const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) => {
|
||||||
@@ -27,11 +37,15 @@ const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) =
|
|||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.setAutoExpandLimit:
|
case ActionTypes.setAutoExpandLimit:
|
||||||
|
if (!action.autoExpandLimit) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
settings: {
|
settings: {
|
||||||
visible: state.settings.visible,
|
visible: state.settings.visible,
|
||||||
autoExpandLimit: action.autoExpandLimit,
|
autoExpandLimit: action.autoExpandLimit,
|
||||||
|
nodeOrder: state.settings.nodeOrder,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case ActionTypes.toggleSettingsVisibility:
|
case ActionTypes.toggleSettingsVisibility:
|
||||||
@@ -40,6 +54,19 @@ const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) =
|
|||||||
settings: {
|
settings: {
|
||||||
visible: !state.settings.visible,
|
visible: !state.settings.visible,
|
||||||
autoExpandLimit: state.settings.autoExpandLimit,
|
autoExpandLimit: state.settings.autoExpandLimit,
|
||||||
|
nodeOrder: state.settings.nodeOrder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case ActionTypes.setNodeOrder:
|
||||||
|
if (!action.nodeOrder) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
settings: {
|
||||||
|
visible: state.settings.visible,
|
||||||
|
autoExpandLimit: state.settings.autoExpandLimit,
|
||||||
|
nodeOrder: action.nodeOrder,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
6
build.ts
6
build.ts
@@ -27,13 +27,13 @@ const mac: builder.CliOptions = {
|
|||||||
arm64: false,
|
arm64: false,
|
||||||
mac: ['dmg'],
|
mac: ['dmg'],
|
||||||
projectDir: './build/clean',
|
projectDir: './build/clean',
|
||||||
publish: 'onTag',
|
publish: 'always',
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildAll() {
|
async function buildAll() {
|
||||||
await builder.build(linux)
|
// await builder.build(linux)
|
||||||
|
// await builder.build(win)
|
||||||
await builder.build(mac)
|
await builder.build(mac)
|
||||||
await builder.build(win)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildAll()
|
buildAll()
|
||||||
|
|||||||
@@ -18,11 +18,7 @@
|
|||||||
"appId": "mqtt-explorer",
|
"appId": "mqtt-explorer",
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "de.t7n.apps.mq-explorer",
|
"category": "de.t7n.apps.mq-explorer",
|
||||||
"files": [
|
"publish": ["github"]
|
||||||
"**/*",
|
|
||||||
"!**/*.ts",
|
|
||||||
"!app/node_modules"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"category": "Development",
|
"category": "Development",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ exit 0
|
|||||||
docker run --rm -ti \
|
docker run --rm -ti \
|
||||||
--env ELECTRON_CACHE="/root/.cache/electron" \
|
--env ELECTRON_CACHE="/root/.cache/electron" \
|
||||||
--env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \
|
--env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \
|
||||||
|
--env GH_TOKEN="$GH_TOKEN" \
|
||||||
-v ${PWD}:/project \
|
-v ${PWD}:/project \
|
||||||
-v ~/.cache/electron:/root/.cache/electron \
|
-v ~/.cache/electron:/root/.cache/electron \
|
||||||
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
|
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": "tslint-config-airbnb",
|
"extends": ["tslint-config-airbnb", "tslint-react"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"semicolon": [true, "never"],
|
"semicolon": [true, "never"],
|
||||||
"max-line-length": [true, 200],
|
"max-line-length": [true, 200],
|
||||||
|
|||||||
Reference in New Issue
Block a user