Fix Sidebar & Mouse event target area

Fix clipboard
Fix invalid state in sidebar due to missing event termination
This commit is contained in:
Thomas Nordquist
2019-01-10 10:34:09 +01:00
parent 269061bdc8
commit eb375073f9
11 changed files with 111 additions and 60 deletions

View File

@@ -91,7 +91,7 @@ class App extends React.Component<Props, State> {
}} /> }} />
</div> </div>
<div style={this.getStyles().right}> <div style={this.getStyles().right}>
<Sidebar node={this.state.selectedNode} /> <Sidebar />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,3 +1,4 @@
import * as settingsActions from './Settings' import * as settingsActions from './Settings'
import * as treeActions from './Tree'
export { settingsActions } export { settingsActions, treeActions }

View File

@@ -1,6 +1,5 @@
import * as React from 'react' import * as React from 'react'
import * as q from '../../../../backend/src/Model' import * as q from '../../../../backend/src/Model'
// import Drawer from '@material-ui/core/Drawer'
import { Typography } from '@material-ui/core' import { Typography } from '@material-ui/core'
interface Props { interface Props {
@@ -15,11 +14,13 @@ class NodeStats extends React.Component<Props, {}> {
public render() { public render() {
const { node } = this.props const { node } = this.props
return <div> return (
<div>
<Typography>Messages: #{node.messages}</Typography> <Typography>Messages: #{node.messages}</Typography>
<Typography>Subtopics: {node.leafCount()}</Typography> <Typography>Subtopics: {node.leafCount()}</Typography>
<Typography>Messages Subtopics: #{node.leafMessageCount()}</Typography> <Typography>Messages Subtopics: #{node.leafMessageCount()}</Typography>
</div> </div>
)
} }
} }

View File

@@ -1,4 +1,6 @@
import * as React from 'react' import * as React from 'react'
import { connect } from 'react-redux'
import { AppState } from '../../reducers'
import * as q from '../../../../backend/src/Model' import * as q from '../../../../backend/src/Model'
import { ExpansionPanel, ExpansionPanelDetails, ExpansionPanelSummary, Typography } from '@material-ui/core' import { ExpansionPanel, ExpansionPanelDetails, ExpansionPanelSummary, Typography } from '@material-ui/core'
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles' import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
@@ -63,9 +65,11 @@ class Sidebar extends React.Component<Props, State> {
} }
public render() { public render() {
return <div className={this.props.classes.drawer}> return (
<div className={this.props.classes.drawer}>
{this.renderNode()} {this.renderNode()}
</div> </div>
)
} }
private renderNode() { private renderNode() {
@@ -74,7 +78,8 @@ class Sidebar extends React.Component<Props, State> {
const copyTopic = node ? <Copy value={node.path()} /> : null const copyTopic = node ? <Copy value={node.path()} /> : null
const copyValue = node && node.message ? <Copy value={node.message.value} /> : null const copyValue = node && node.message ? <Copy value={node.message.value} /> : null
return <div> return (
<div>
<ExpansionPanel key="topic" defaultExpanded={true}> <ExpansionPanel key="topic" defaultExpanded={true}>
<ExpansionPanelSummary expandIcon={<ExpandMore />}> <ExpansionPanelSummary expandIcon={<ExpandMore />}>
<Typography className={classes.heading}>Topic {copyTopic}</Typography> <Typography className={classes.heading}>Topic {copyTopic}</Typography>
@@ -88,7 +93,7 @@ class Sidebar extends React.Component<Props, State> {
<Typography className={classes.heading}>Value {copyValue}</Typography> <Typography className={classes.heading}>Value {copyValue}</Typography>
</ExpansionPanelSummary> </ExpansionPanelSummary>
<ExpansionPanelDetails> <ExpansionPanelDetails>
<ValueRenderer node={this.state.node} /> <ValueRenderer node={this.props.node} />
</ExpansionPanelDetails> </ExpansionPanelDetails>
</ExpansionPanel> </ExpansionPanel>
<ExpansionPanel defaultExpanded={true}> <ExpansionPanel defaultExpanded={true}>
@@ -96,11 +101,18 @@ class Sidebar extends React.Component<Props, State> {
<Typography className={classes.heading}>Stats</Typography> <Typography className={classes.heading}>Stats</Typography>
</ExpansionPanelSummary> </ExpansionPanelSummary>
<ExpansionPanelDetails> <ExpansionPanelDetails>
<NodeStats node={this.state.node} /> {this.props.node ? <NodeStats node={this.props.node} /> : null}
</ExpansionPanelDetails> </ExpansionPanelDetails>
</ExpansionPanel> </ExpansionPanel>
</div> </div>
)
} }
} }
export default withStyles(Sidebar.styles, { withTheme: true })(Sidebar) const mapStateToProps = (state: AppState) => {
return {
node: state.selectedTopic,
}
}
export default withStyles(Sidebar.styles, { withTheme: true })(connect(mapStateToProps)(Sidebar))

View File

@@ -31,7 +31,8 @@ class Topic extends React.Component<Props, {}> {
.map(node => node.sourceEdge) .map(node => node.sourceEdge)
.filter(edge => Boolean(edge)) .filter(edge => Boolean(edge))
.map(edge => .map(edge =>
[<Button [(
<Button
onClick={() => this.setState({ node: edge!.target })} onClick={() => this.setState({ node: edge!.target })}
size="small" size="small"
color="secondary" color="secondary"
@@ -39,7 +40,8 @@ class Topic extends React.Component<Props, {}> {
key={edge!.hash()} key={edge!.hash()}
> >
{edge!.name} {edge!.name}
</Button>], </Button>
)],
) )
if (breadCrumps.length === 0) { if (breadCrumps.length === 0) {

View File

@@ -118,8 +118,7 @@ class Tree extends React.Component<Props, TreeState> {
name="/" name="/"
collapsed={false} collapsed={false}
key="rootNode" key="rootNode"
lastUpdate={0} lastUpdate={this.state.tree.lastUpdate}
performanceCallback={this.performanceCallback}
/> />
</Typography> </Typography>
) )

View File

@@ -1,4 +1,7 @@
import * as React from 'react' import * as React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { treeActions } from '../../actions'
import * as q from '../../../../backend/src/Model' import * as q from '../../../../backend/src/Model'
import { withStyles, Theme } from '@material-ui/core/styles' import { withStyles, Theme } from '@material-ui/core/styles'
@@ -29,6 +32,7 @@ const styles = (theme: Theme) => {
} }
interface Props { interface Props {
actions: any
lastUpdate: number lastUpdate: number
animateChages: boolean animateChages: boolean
isRoot?: boolean isRoot?: boolean
@@ -39,6 +43,7 @@ interface Props {
didSelectNode?: (node: q.TreeNode) => void didSelectNode?: (node: q.TreeNode) => void
classes: any classes: any
autoExpandLimit: number autoExpandLimit: number
style?: React.CSSProperties
} }
interface State { interface State {
@@ -168,13 +173,13 @@ class TreeNode extends React.Component<Props, State> {
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={this.didClickNode} onClick={this.didClickNode}
style={this.props.style}
> >
<span ref={this.titleRef} style={animation}> <span ref={this.titleRef} style={animation}>
<TreeNodeTitle <TreeNodeTitle
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}
/> />
</span> </span>
{this.renderNodes()} {this.renderNodes()}
@@ -185,7 +190,7 @@ class TreeNode extends React.Component<Props, State> {
private didClickNode = (event: React.MouseEvent) => { private didClickNode = (event: React.MouseEvent) => {
event.stopPropagation() event.stopPropagation()
this.toggle() this.toggle()
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) this.props.actions.selectTopic(this.props.treeNode)
} }
private renderNodes() { private renderNodes() {
@@ -202,4 +207,10 @@ class TreeNode extends React.Component<Props, State> {
} }
} }
export default withStyles(styles)(TreeNode) const mapDispatchToProps = (dispatch: any) => {
return {
actions: bindActionCreators(treeActions, dispatch),
}
}
export default withStyles(styles)(connect(null, mapDispatchToProps)(TreeNode))

View File

@@ -49,13 +49,14 @@ class TreeNodeSubnodes extends React.Component<Props, {}> {
const nodes = this.sortedNodes() const nodes = this.sortedNodes()
const listItems = nodes.map(node => ( const listItems = nodes.map(node => (
<div key={node.hash()} style={listItemStyle}> <div key={node.hash()}>
<TreeNode <TreeNode
animateChages={this.props.animateChanges} animateChages={this.props.animateChanges}
treeNode={node} treeNode={node}
didSelectNode={this.props.didSelectNode} didSelectNode={this.props.didSelectNode}
autoExpandLimit={this.props.autoExpandLimit} autoExpandLimit={this.props.autoExpandLimit}
lastUpdate={node.lastUpdate} lastUpdate={node.lastUpdate}
style={listItemStyle}
/> />
</div> </div>
)) ))

View File

@@ -1,12 +1,15 @@
import * as React from 'react' import * as React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { treeActions } from '../../actions'
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'
export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> { export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
treeNode: q.TreeNode treeNode: q.TreeNode
actions: any
name?: string | undefined name?: string | undefined
collapsed?: boolean | undefined collapsed?: boolean | undefined
didSelectNode?: (node: q.TreeNode) => void
theme: Theme theme: Theme
} }
@@ -23,9 +26,10 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
} }
} }
private didSelectNode = () => { private didSelectNode = (event: React.MouseEvent) => {
event.stopPropagation()
if (this.props.treeNode.message) { if (this.props.treeNode.message) {
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) this.props.actions.selectTopic(this.props.treeNode)
} }
} }
@@ -59,7 +63,7 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
padding: '0', padding: '0',
paddingLeft: '5px', marginLeft: '5px',
display: 'inline-block', display: 'inline-block',
} }
return this.props.treeNode.message return this.props.treeNode.message
@@ -85,4 +89,10 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
} }
} }
export default withTheme()(TreeNodeTitle) const mapDispatchToProps = (dispatch: any) => {
return {
actions: bindActionCreators(treeActions, dispatch),
}
}
export default withTheme()(connect(null, mapDispatchToProps)(TreeNodeTitle))

View File

@@ -15,6 +15,7 @@ const initialAppState = {
nodeOrder: NodeOrder.none, nodeOrder: NodeOrder.none,
visible: false, visible: false,
}, },
selectedNode: undefined,
} }
const store = createStore(reducers, initialAppState) const store = createStore(reducers, initialAppState)

View File

@@ -1,19 +1,29 @@
import { Reducer, Action } from 'redux' import { Reducer, Action } from 'redux'
import * as q from '../../../backend/src/Model'
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', setNodeOrder = 'SET_NODE_ORDER',
selectTopic = 'SELECT_TOPIC',
} }
interface SettingsAction extends Action { interface CustomAction extends Action {
type: ActionTypes, type: ActionTypes,
autoExpandLimit?: number autoExpandLimit?: number
nodeOrder?: NodeOrder nodeOrder?: NodeOrder
selectedTopic?: q.TreeNode
} }
export interface AppState { export interface AppState {
settings: SettingsModel settings: SettingsState,
selectedTopic?: q.TreeNode
}
export interface SettingsState {
autoExpandLimit: number
visible: boolean
nodeOrder: NodeOrder
} }
export enum NodeOrder { export enum NodeOrder {
@@ -23,13 +33,7 @@ export enum NodeOrder {
topics = '#topics', topics = '#topics',
} }
export interface SettingsModel { const reducer: Reducer<AppState | undefined, CustomAction> = (state, action) => {
autoExpandLimit: number
visible: boolean
nodeOrder: NodeOrder
}
const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) => {
if (!state) { if (!state) {
throw Error('No initial state') throw Error('No initial state')
} }
@@ -57,6 +61,15 @@ const reducer: Reducer<AppState | undefined, SettingsAction> = (state, action) =
nodeOrder: state.settings.nodeOrder, nodeOrder: state.settings.nodeOrder,
}, },
} }
case ActionTypes.selectTopic:
if (!action.selectedTopic) {
return state
}
return {
...state,
settings: state.settings,
selectedTopic: action.selectedTopic,
}
case ActionTypes.setNodeOrder: case ActionTypes.setNodeOrder:
if (!action.nodeOrder) { if (!action.nodeOrder) {
return state return state