WiP #broken

This commit is contained in:
Thomas Nordquist
2019-01-08 16:39:18 +01:00
parent 25cc7ad277
commit b44f352804
16 changed files with 257 additions and 164 deletions

View File

@@ -1,4 +1,4 @@
import { ActionTypes } from '../reducers'
import { ActionTypes, NodeOrder } from '../reducers'
export const setAutoExpandLimit = (autoExpandLimit: number = 0) => {
return {
@@ -12,3 +12,10 @@ export const toggleSettingsVisibility = () => {
type: ActionTypes.toggleSettingsVisibility,
}
}
export const setNodeOrder = (nodeOrder: NodeOrder = NodeOrder.none) => {
return {
nodeOrder,
type: ActionTypes.setNodeOrder,
}
}

View File

@@ -42,13 +42,13 @@ class Notification extends React.Component<Props, State> {
vertical: 'bottom',
horizontal: 'left',
}}
open = {this.state.snackBarOpen}
autoHideDuration = {6000}
onClose = {() => { this.setState({ snackBarOpen: false }) }}
open={this.state.snackBarOpen}
autoHideDuration={6000}
onClose={() => { this.setState({ snackBarOpen: false }) }}
>
<SnackbarContent
className = {this.props.classes[this.props.type]}
message = {this.props.message}
className={this.props.classes[this.props.type]}
message={this.props.message}
/>
</Snackbar>
}

View File

@@ -32,8 +32,8 @@ class Copy extends React.Component<Props, State> {
public render() {
const icon = !this.state.didCopy
? <FileCopy fontSize="inherit" style={ { cursor: 'pointer' } } onClick={this.handleClick} />
: <Check fontSize="inherit" style={ { cursor: 'default' } } />
? <FileCopy fontSize="inherit" style={{ cursor: 'pointer' }} onClick={this.handleClick} />
: <Check fontSize="inherit" style={{ cursor: 'default' }} />
return <span>
<span style={{ fontSize: '16px' }}>{icon}</span>

View File

@@ -14,7 +14,7 @@ import { Typography, InputBase, Input, InputLabel } from '@material-ui/core'
import { withStyles, StyleRulesCallback } from '@material-ui/core/styles'
import { settingsActions } from '../actions'
import { AppState } from '../reducers'
import { AppState, NodeOrder } from '../reducers'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
@@ -40,16 +40,14 @@ const styles: StyleRulesCallback = theme => ({
interface Props {
actions?: any
autoExpandLimit: number,
visible: boolean,
store?: any,
autoExpandLimit: number
visible: boolean
store?: any
classes: any
nodeOrder: NodeOrder
}
interface State {
}
class Settings extends React.Component<Props, State> {
class Settings extends React.Component<Props, {}> {
constructor(props: any) {
super(props)
this.state = {}
@@ -57,59 +55,91 @@ class Settings extends React.Component<Props, State> {
public render() {
const { classes, actions, visible } = this.props
return <Drawer
anchor = "left"
className = { classes.drawer }
open = { visible }
variant = "persistent"
>
<div
className = { classes.paper }
tabIndex = { 0 }
role = "button"
onClick = {(e: React.MouseEvent) => e.stopPropagation()}
onKeyDown = {(e: React.KeyboardEvent) => e.stopPropagation()}
return (
<Drawer
anchor="left"
className={classes.drawer}
open={visible}
variant="persistent"
>
<div
className={classes.paper}
tabIndex={0}
role="button"
onClick={(e: React.MouseEvent) => e.stopPropagation()}
onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}
>
<Typography className = { classes.title } variant = "h6" color = "inherit">
<IconButton onClick = {actions.toggleSettingsVisibility}>
<ChevronRight />
</IconButton>
Settings
</Typography>
<Divider />
<Typography className={ classes.title } variant="h6" color="inherit">
<IconButton onClick={actions.toggleSettingsVisibility}>
<ChevronRight />
</IconButton>
Settings
</Typography>
<Divider />
{this.renderAutoExpand()}
</div>
</Drawer>
{this.renderAutoExpand()}
{this.renderNodeOrder()}
</div>
</Drawer>
)
}
private renderAutoExpand() {
const { classes, actions, autoExpandLimit } = this.props
return <div style={{ padding: '8px' }}>
<InputLabel htmlFor="auto-expand">Auto Expand</InputLabel>
return (
<div style={{ padding: '8px' }}>
<InputLabel htmlFor="auto-expand">Auto Expand</InputLabel>
<Select
value={autoExpandLimit}
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => actions.setAutoExpandLimit(e.target.value) }
input={<Input name="auto-expand" id="auto-expand-label-placeholder" />}
displayEmpty={true}
name="auto-expand"
className={classes.input}
>
<MenuItem value={0}><em>Disabled</em></MenuItem>
<MenuItem value={2}>Few</MenuItem>
<MenuItem value={3}>Some</MenuItem>
<MenuItem value={10}>Most</MenuItem>
<MenuItem value={1E6}>All</MenuItem>
</Select>
</div>
)
}
private renderNodeOrder() {
const { classes, actions, nodeOrder } = this.props
return (
<div style={{ padding: '8px' }}>
<InputLabel htmlFor="auto-expand">Topic order</InputLabel>
<Select
value = { autoExpandLimit }
onChange = { (e: React.ChangeEvent<HTMLSelectElement>) => actions.setAutoExpandLimit(e.target.value) }
input = {<Input name="auto-expand" id="auto-expand-label-placeholder" />}
displayEmpty
name = "auto-expand"
className = {classes.input}
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 = {0}><em>Disabled</em></MenuItem>
<MenuItem value = {2}>Few</MenuItem>
<MenuItem value = {3}>Some</MenuItem>
<MenuItem value = {10}>Most</MenuItem>
<MenuItem value = {1E6}>All</MenuItem>
<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>
</div>)
}
}
const mapStateToProps = (state: AppState) => {
return {
autoExpandLimit: state.settings.autoExpandLimit,
nodeOrder: state.settings.nodeOrder,
visible: state.settings.visible,
}
}

View File

@@ -9,6 +9,7 @@ import { AppState } from '../../reducers'
const MovingAverage = require('moving-average')
declare const performance: any
declare const window: any
const timeInterval = 10 * 1000
const average = MovingAverage(timeInterval)
@@ -48,14 +49,17 @@ class Tree extends React.Component<Props, TreeState> {
}
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)
this.updateTimer = setTimeout(() => {
this.lastUpdate = performance.now()
this.updateTimer && clearTimeout(this.updateTimer)
this.updateTimer = undefined
this.setState(state)
window.requestAnimationFrame(() => {
console.log('doRender')
this.lastUpdate = performance.now()
this.updateTimer && clearTimeout(this.updateTimer)
this.updateTimer = undefined
this.setState(state)
})
}, Math.max(0, timeUntilNextUpdate))
}
@@ -98,15 +102,15 @@ class Tree extends React.Component<Props, TreeState> {
cursor: 'default',
}
return <Typography style={ style }>
return <Typography style={style}>
<TreeNode
animateChages = { true }
autoExpandLimit = { this.props.autoExpandLimit }
isRoot = { true }
didSelectNode = { this.props.didSelectNode }
treeNode = { this.state.tree }
name = "/"
collapsed = { false }
animateChages={true}
autoExpandLimit={this.props.autoExpandLimit}
isRoot={true}
didSelectNode={this.props.didSelectNode}
treeNode={this.state.tree}
name="/"
collapsed={false}
key="rootNode"
performanceCallback={(ms: number) => {
average.push(Date.now(), ms)

View File

@@ -136,38 +136,41 @@ class TreeNode extends React.Component<Props, State> {
const { classes } = this.props
this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false
return <div
key={this.props.treeNode.hash()}
className={`${classes.node} ${!this.props.isRoot ? classes.hover : ''}`}
onClick={(event) => {
event.stopPropagation()
this.toggle()
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
}}
>
<span ref={this.titleRef} style={animationStyle}>
<TreeNodeTitle
onClick={() => this.toggle()}
collapsed={this.collapsed()}
treeNode={this.props.treeNode}
name={this.props.name}
didSelectNode={this.props.didSelectNode}
toggleCollapsed={() => this.toggle()}
/>
</span>
{ this.renderNodes() }
</div>
return (
<div
key={this.props.treeNode.hash()}
className={`${classes.node} ${!this.props.isRoot ? classes.hover : ''}`}
onClick={this.didClickNode}
>
<span ref={this.titleRef} style={animationStyle}>
<TreeNodeTitle
collapsed={this.collapsed()}
treeNode={this.props.treeNode}
name={this.props.name}
didSelectNode={this.props.didSelectNode}
/>
</span>
{this.renderNodes()}
</div>
)
}
private didClickNode = (event: React.MouseEvent) => {
event.stopPropagation()
this.toggle()
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
}
private renderNodes() {
return <TreeNodeSubnodes
animateChanges={this.props.animateChages}
collapsed={this.collapsed()}
autoExpandLimit={this.props.autoExpandLimit}
didSelectNode={this.props.didSelectNode}
toggleCollapsed={() => this.toggle()}
treeNode={this.props.treeNode}
/>
return (
<TreeNodeSubnodes
animateChanges={this.props.animateChages}
collapsed={this.collapsed()}
autoExpandLimit={this.props.autoExpandLimit}
didSelectNode={this.props.didSelectNode}
treeNode={this.props.treeNode}
/>
)
}
private indicatingChangeAnimationStyle(): React.CSSProperties {

View File

@@ -1,52 +1,75 @@
import * as React from 'react'
import { connect } from 'react-redux'
import * as q from '../../../../backend/src/Model'
import { withTheme, Theme } from '@material-ui/core/styles'
import { AppState, NodeOrder } from '../../reducers'
import TreeNode from './TreeNode'
export interface Props {
nodeOrder?: NodeOrder
animateChanges: boolean
treeNode: q.TreeNode
autoExpandLimit: number
collapsed?: boolean | undefined
didSelectNode?: (node: q.TreeNode) => void
toggleCollapsed: () => void
theme: Theme
}
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() {
const edges = Object.values(this.props.treeNode.edges)
if (edges.length === 0 || this.props.collapsed) {
return null
}
const listItemStyle = {
padding: '3px 8px 0px 8px',
}
if (edges.length > 0 && !this.props.collapsed) {
const listItems = edges
.map(edge => edge.target)
.map(node => (
<div
key={node.hash()}
style={listItemStyle}
>
<TreeNode
animateChages={this.props.animateChanges}
treeNode={node}
didSelectNode={this.props.didSelectNode}
autoExpandLimit={this.props.autoExpandLimit}
/>
</div>
))
const nodes = this.sortedNodes()
const listItems = nodes.map(node => (
<div key={node.hash()} style={listItemStyle}>
<TreeNode
animateChages={this.props.animateChanges}
treeNode={node}
didSelectNode={this.props.didSelectNode}
autoExpandLimit={this.props.autoExpandLimit}
/>
</div>
))
return <span
style={{ display: 'block', clear: 'both' }}
>
return (
<span style={{ display: 'block', clear: 'both' }} >
{this.props.collapsed ? null : listItems}
</span>
}
return null
)
}
}
export default withTheme()(TreeNodeSubnodes)
const mapStateToProps = (state: AppState) => {
return {
nodeOrder: state.settings.nodeOrder,
}
}
export default withTheme()(connect(mapStateToProps)(TreeNodeSubnodes))

View File

@@ -6,7 +6,6 @@ export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
treeNode: q.TreeNode
name?: string | undefined
collapsed?: boolean | undefined
toggleCollapsed: () => void
didSelectNode?: (node: q.TreeNode) => void
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() {
const style: React.CSSProperties = {
lineHeight: '1em',
whiteSpace: 'nowrap',
}
return <span
style={style}
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()}
</span>
return (
<span style={style} onMouseOver={this.didSelectNode}>
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
</span>
)
}
private renderSourceEdge() {
@@ -67,16 +63,10 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
display: 'inline-block',
}
return this.props.treeNode.message
? <span
style={style}
> = {this.props.treeNode.message.value.toString()}</span>
? <span style={style}> = {this.props.treeNode.message.value.toString()}</span>
: null
}
private toggle() {
this.props.toggleCollapsed()
}
private renderExpander() {
if (this.props.treeNode.edgeCount() === 0) {
return null

View File

@@ -4,7 +4,7 @@ import { Provider } from 'react-redux'
import { createStore } from 'redux'
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
import reducers from './reducers'
import reducers, { NodeOrder } from './reducers'
import App from './App'
declare var document: any
@@ -12,6 +12,7 @@ declare var document: any
const initialAppState = {
settings: {
autoExpandLimit: 0,
nodeOrder: NodeOrder.none,
visible: false,
},
}

View File

@@ -3,20 +3,30 @@ import { Reducer, Action } from 'redux'
export enum ActionTypes {
setAutoExpandLimit = 'SET_AUTO_EXPAND_LIMIT',
toggleSettingsVisibility = 'TOGGLE_SETTINGS_VISIBILITY',
setNodeOrder = 'SET_NODE_ORDER',
}
interface SettingsAction extends Action {
type: ActionTypes,
autoExpandLimit: number
autoExpandLimit?: number
nodeOrder?: NodeOrder
}
export interface AppState {
settings: SettingsModel
}
export enum NodeOrder {
none = 'none',
messages = '#messages',
abc = 'abc',
topics = '#topics',
}
export interface SettingsModel {
autoExpandLimit: number
visible: boolean
nodeOrder: NodeOrder
}
const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) => {
@@ -27,11 +37,15 @@ const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) =
switch (action.type) {
case ActionTypes.setAutoExpandLimit:
if (!action.autoExpandLimit) {
return state
}
return {
...state,
settings: {
visible: state.settings.visible,
autoExpandLimit: action.autoExpandLimit,
nodeOrder: state.settings.nodeOrder,
},
}
case ActionTypes.toggleSettingsVisibility:
@@ -40,6 +54,19 @@ const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) =
settings: {
visible: !state.settings.visible,
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: