Add pane resizing
Refactor styles Update autoExpand with filtered result count Add redux batch reducer
This commit is contained in:
@@ -58,6 +58,57 @@
|
|||||||
0% {opacity: 1;}
|
0% {opacity: 1;}
|
||||||
100% {opacity: 0;}
|
100% {opacity: 0;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Resizer {
|
||||||
|
background: #eee;
|
||||||
|
opacity: .2;
|
||||||
|
z-index: 1;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-background-clip: padding;
|
||||||
|
-webkit-background-clip: padding;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer:hover {
|
||||||
|
-webkit-transition: all 0.3s ease-out;
|
||||||
|
transition: all 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.horizontal {
|
||||||
|
height: 11px;
|
||||||
|
margin: -5px 0;
|
||||||
|
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: row-resize;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.horizontal:hover {
|
||||||
|
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical {
|
||||||
|
width: 11px;
|
||||||
|
margin: 0 -5px;
|
||||||
|
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical:hover {
|
||||||
|
border-left: 5px solid rgba(255, 255, 255, 0.5);
|
||||||
|
border-right: 5px solid rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
.Resizer.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.Resizer.disabled:hover {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script src="http://localhost:35729/livereload.js"></script>
|
<script src="http://localhost:35729/livereload.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
48
app/package-lock.json
generated
48
app/package-lock.json
generated
@@ -266,6 +266,14 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-split-pane": {
|
||||||
|
"version": "0.1.67",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-split-pane/-/react-split-pane-0.1.67.tgz",
|
||||||
|
"integrity": "sha512-2vq9mohqYZ7kR+XcedfjyI2M7/E8W5owr0KupGGP+TfFp/O9f6c/S06MCB3FKWiBwazA7+Zyj50OTxvYy1kGLA==",
|
||||||
|
"requires": {
|
||||||
|
"react-split-pane": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-transition-group": {
|
"@types/react-transition-group": {
|
||||||
"version": "2.0.15",
|
"version": "2.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.15.tgz",
|
||||||
@@ -5768,7 +5776,6 @@
|
|||||||
"version": "16.7.0",
|
"version": "16.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz",
|
||||||
"integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==",
|
"integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
@@ -5851,6 +5858,39 @@
|
|||||||
"resize-observer-polyfill": "^1.5.1"
|
"resize-observer-polyfill": "^1.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-split-pane": {
|
||||||
|
"version": "0.1.85",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.85.tgz",
|
||||||
|
"integrity": "sha512-3GhaYs6+eVNrewgN4eQKJoNMQ4pcegNMTMhR5bO/NFO91K6/98qdD1sCuWPpsefCjzxNTjkvVYWQC0bMaC45mA==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react": "^16.6.3",
|
||||||
|
"react-dom": "^16.6.3",
|
||||||
|
"react-lifecycles-compat": "^3.0.4",
|
||||||
|
"react-style-proptype": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": {
|
||||||
|
"version": "16.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz",
|
||||||
|
"integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.1.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"scheduler": "^0.12.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-style-proptype": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-style-proptype/-/react-style-proptype-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-textarea-autosize": {
|
"react-textarea-autosize": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz",
|
||||||
@@ -6002,6 +6042,11 @@
|
|||||||
"symbol-observable": "^1.2.0"
|
"symbol-observable": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux-batched-actions": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-batched-actions/-/redux-batched-actions-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-r6tLDyBP3U9cXNLEHs0n1mX5TQfmk6xE0Y9uinYZ5HOyAWDgIJxYqRRkU/bC6XrJ4nS7tasNbxaHJHVmf9UdkA=="
|
||||||
|
},
|
||||||
"reflect-metadata": {
|
"reflect-metadata": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||||
@@ -6211,7 +6256,6 @@
|
|||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz",
|
||||||
"integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==",
|
"integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"@material-ui/styles": "^3.0.0-alpha.8",
|
"@material-ui/styles": "^3.0.0-alpha.8",
|
||||||
"@types/node": "^10.12.18",
|
"@types/node": "^10.12.18",
|
||||||
"@types/react-resize-detector": "^3.1.0",
|
"@types/react-resize-detector": "^3.1.0",
|
||||||
|
"@types/react-split-pane": "^0.1.67",
|
||||||
"@types/sha1": "^1.1.1",
|
"@types/sha1": "^1.1.1",
|
||||||
"@types/socket.io-client": "^1.4.32",
|
"@types/socket.io-client": "^1.4.32",
|
||||||
"@types/vis": "^4.21.9",
|
"@types/vis": "^4.21.9",
|
||||||
@@ -42,6 +43,8 @@
|
|||||||
"react-ace": "^6.3.2",
|
"react-ace": "^6.3.2",
|
||||||
"react-json-view": "^1.19.1",
|
"react-json-view": "^1.19.1",
|
||||||
"react-resize-detector": "^3.4.0",
|
"react-resize-detector": "^3.4.0",
|
||||||
|
"react-split-pane": "^0.1.85",
|
||||||
|
"redux-batched-actions": "^0.4.1",
|
||||||
"sha1": "^1.1.1",
|
"sha1": "^1.1.1",
|
||||||
"socket.io-client": "^2.2.0",
|
"socket.io-client": "^2.2.0",
|
||||||
"source-map-loader": "^0.2.4",
|
"source-map-loader": "^0.2.4",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Tree from './components/Tree/Tree'
|
|||||||
import UpdateNotifier from './UpdateNotifier'
|
import UpdateNotifier from './UpdateNotifier'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import ErrorBoundary from './ErrorBoundary'
|
import ErrorBoundary from './ErrorBoundary'
|
||||||
|
import { default as SplitPane } from 'react-split-pane'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string
|
name: string
|
||||||
@@ -29,22 +30,33 @@ class App extends React.Component<Props, {}> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { settingsVisible } = this.props
|
const { settingsVisible } = this.props
|
||||||
const { content, contentShift, centerContent, left, right } = this.props.classes
|
const { content, contentShift, centerContent, paneDefaults, heightProperty } = this.props.classes
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={centerContent}>
|
<div className={centerContent}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Settings />
|
<Settings />
|
||||||
<div className={settingsVisible ? contentShift : content}>
|
<div className={`${settingsVisible ? contentShift : content} ${heightProperty}`}>
|
||||||
<TitleBar />
|
<TitleBar />
|
||||||
<div className={centerContent}>
|
<div className={centerContent}>
|
||||||
<div className={this.props.classes.left}>
|
<SplitPane
|
||||||
|
step={48}
|
||||||
|
primary="second"
|
||||||
|
className={heightProperty}
|
||||||
|
split="vertical"
|
||||||
|
minSize={250}
|
||||||
|
defaultSize={500}
|
||||||
|
allowResize={true}
|
||||||
|
pane1Style={{ overflow: 'hidden' }}
|
||||||
|
>
|
||||||
|
<div className={paneDefaults}>
|
||||||
<Tree />
|
<Tree />
|
||||||
</div>
|
</div>
|
||||||
<div className={right}>
|
<div className={paneDefaults}>
|
||||||
<Sidebar connectionId={this.props.connectionId} />
|
<Sidebar connectionId={this.props.connectionId} />
|
||||||
</div>
|
</div>
|
||||||
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UpdateNotifier />
|
<UpdateNotifier />
|
||||||
@@ -65,24 +77,16 @@ const mapStateToProps = (state: AppState) => {
|
|||||||
const styles = (theme: Theme) => {
|
const styles = (theme: Theme) => {
|
||||||
const drawerWidth = 300
|
const drawerWidth = 300
|
||||||
return {
|
return {
|
||||||
left: {
|
heightProperty: {
|
||||||
|
height: 'calc(100vh - 64px) !important',
|
||||||
|
},
|
||||||
|
paneDefaults: {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
height: 'calc(100vh - 64px)',
|
|
||||||
float: 'left' as 'left',
|
|
||||||
overflowY: 'scroll' as 'scroll',
|
overflowY: 'scroll' as 'scroll',
|
||||||
overflowX: 'hidden' as 'hidden',
|
overflowX: 'hidden' as 'hidden',
|
||||||
display: 'block' as 'block',
|
display: 'block' as 'block',
|
||||||
width: '60vw',
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
height: 'calc(100vh - 64px)',
|
height: 'calc(100vh - 64px)',
|
||||||
color: theme.palette.text.primary,
|
|
||||||
float: 'left' as 'left',
|
|
||||||
width: '40vw',
|
|
||||||
overflowY: 'scroll' as 'scroll',
|
|
||||||
overflowX: 'hidden' as 'hidden',
|
|
||||||
display: 'block' as 'block',
|
|
||||||
},
|
},
|
||||||
centerContent: {
|
centerContent: {
|
||||||
width: '100vw',
|
width: '100vw',
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { Dispatch } from 'redux'
|
|||||||
import { showTree } from './Tree'
|
import { showTree } from './Tree'
|
||||||
import { AppState } from '../reducers'
|
import { AppState } from '../reducers'
|
||||||
import * as q from '../../../backend/src/Model'
|
import * as q from '../../../backend/src/Model'
|
||||||
|
import { batchActions, enableBatching, batchDispatchMiddleware } from 'redux-batched-actions';
|
||||||
|
import { autoExpandLimitSet } from '../components/Settings';
|
||||||
|
|
||||||
export const setAutoExpandLimit = (autoExpandLimit: number = 0): Action => {
|
export const setAutoExpandLimit = (autoExpandLimit: number = 0): Action => {
|
||||||
return {
|
return {
|
||||||
@@ -34,7 +36,7 @@ export const filterTopics = (filterStr: string) => (dispatch: Dispatch<any>, get
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!filterStr || !tree) {
|
if (!filterStr || !tree) {
|
||||||
dispatch(showTree(tree))
|
dispatch(batchActions([setAutoExpandLimit(0), showTree(tree)]))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,5 +69,21 @@ export const filterTopics = (filterStr: string) => (dispatch: Dispatch<any>, get
|
|||||||
nextTree.updateWithConnection(tree.updateSource, tree.connectionId, nodeFilter)
|
nextTree.updateWithConnection(tree.updateSource, tree.connectionId, nodeFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(showTree(nextTree))
|
dispatch(batchActions([setAutoExpandLimit(autoExpandLimitForTree(nextTree)), showTree(nextTree)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoExpandLimitForTree(tree: q.Tree) {
|
||||||
|
if (!tree) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
function closestExistingLimit(i: number): number {
|
||||||
|
const sorted = autoExpandLimitSet.sort((a, b) => Math.abs(a.limit - i) - Math.abs(b.limit - i))
|
||||||
|
console.log('sorted', i, sorted)
|
||||||
|
return sorted[0]!.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = tree.childTopicCount()
|
||||||
|
const calculatedLimit = Math.max(7 - Math.log(count), 0) * 2
|
||||||
|
|
||||||
|
return closestExistingLimit(calculatedLimit)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,23 @@ import { settingsActions, treeActions } from '../actions'
|
|||||||
import { TopicOrder } from '../reducers/Settings'
|
import { TopicOrder } from '../reducers/Settings'
|
||||||
import BrokerStatistics from './BrokerStatistics'
|
import BrokerStatistics from './BrokerStatistics'
|
||||||
|
|
||||||
|
export const autoExpandLimitSet = [{
|
||||||
|
limit: 0,
|
||||||
|
name: 'Collapsed',
|
||||||
|
}, {
|
||||||
|
limit: 2,
|
||||||
|
name: 'Few',
|
||||||
|
}, {
|
||||||
|
limit: 3,
|
||||||
|
name: 'Some',
|
||||||
|
}, {
|
||||||
|
limit: 10,
|
||||||
|
name: 'Most',
|
||||||
|
}, {
|
||||||
|
limit: 1E6,
|
||||||
|
name: 'All',
|
||||||
|
}]
|
||||||
|
|
||||||
const styles: StyleRulesCallback = theme => ({
|
const styles: StyleRulesCallback = theme => ({
|
||||||
drawer: {
|
drawer: {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
@@ -87,6 +104,8 @@ class Settings extends React.Component<Props, {}> {
|
|||||||
|
|
||||||
private renderAutoExpand() {
|
private renderAutoExpand() {
|
||||||
const { classes, autoExpandLimit } = this.props
|
const { classes, autoExpandLimit } = this.props
|
||||||
|
|
||||||
|
const limits = autoExpandLimitSet.map(limit => <MenuItem key={limit.limit} value={limit.limit}>{limit.name}</MenuItem>)
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '8px', display: 'flex' }}>
|
<div style={{ padding: '8px', display: 'flex' }}>
|
||||||
<InputLabel htmlFor="auto-expand" style={{ flex: '1', marginTop: '8px' }}>Auto Expand</InputLabel>
|
<InputLabel htmlFor="auto-expand" style={{ flex: '1', marginTop: '8px' }}>Auto Expand</InputLabel>
|
||||||
@@ -98,11 +117,7 @@ class Settings extends React.Component<Props, {}> {
|
|||||||
className={classes.input}
|
className={classes.input}
|
||||||
style={{ flex: '1' }}
|
style={{ flex: '1' }}
|
||||||
>
|
>
|
||||||
<MenuItem value={0}><em>Collapsed</em></MenuItem>
|
{limits}
|
||||||
<MenuItem value={2}>Few</MenuItem>
|
|
||||||
<MenuItem value={3}>Some</MenuItem>
|
|
||||||
<MenuItem value={10}>Most</MenuItem>
|
|
||||||
<MenuItem value={1E6}>All</MenuItem>
|
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ const styles = (theme: Theme) => {
|
|||||||
display: 'block',
|
display: 'block',
|
||||||
marginLeft: '10px',
|
marginLeft: '10px',
|
||||||
},
|
},
|
||||||
hover: {
|
// hover: {
|
||||||
'&:hover': {
|
// '&:hover': {
|
||||||
backgroundColor: 'rgba(80, 80, 80, 0.35)',
|
// backgroundColor: 'rgba(80, 80, 80, 0.35)',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
topicSelect: {
|
topicSelect: {
|
||||||
float: 'right' as 'right',
|
float: 'right' as 'right',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
@@ -51,7 +51,7 @@ interface Props {
|
|||||||
performanceCallback?: ((ms: number) => void) | undefined
|
performanceCallback?: ((ms: number) => void) | undefined
|
||||||
autoExpandLimit: number
|
autoExpandLimit: number
|
||||||
classes: any
|
classes: any
|
||||||
style?: React.CSSProperties
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -68,6 +68,7 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
|
|
||||||
private willUpdateTime: number = performance.now()
|
private willUpdateTime: number = performance.now()
|
||||||
private titleRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
|
private titleRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
|
||||||
|
private nodeRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
|
||||||
private topicSelectRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
|
private topicSelectRef?: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>()
|
||||||
|
|
||||||
private subnodesDidchange = () => {
|
private subnodesDidchange = () => {
|
||||||
@@ -119,6 +120,7 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
this.removeSubscriber(treeNode)
|
this.removeSubscriber(treeNode)
|
||||||
this.topicSelectRef = undefined
|
this.topicSelectRef = undefined
|
||||||
this.titleRef = undefined
|
this.titleRef = undefined
|
||||||
|
this.nodeRef = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
private stateHasChanged(newState: State) {
|
private stateHasChanged(newState: State) {
|
||||||
@@ -177,11 +179,11 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={this.props.treeNode.hash()}
|
key={this.props.treeNode.hash()}
|
||||||
className={`${classes.node} ${!this.props.isRoot ? classes.hover : ''}`}
|
className={`${classes.node} ${this.props.className}`}
|
||||||
onClick={this.didClickNode}
|
onClick={this.didClickNode}
|
||||||
style={this.props.style}
|
|
||||||
onMouseOver={this.mouseOver}
|
onMouseOver={this.mouseOver}
|
||||||
onMouseOut={this.mouseOut}
|
onMouseOut={this.mouseOut}
|
||||||
|
ref={this.nodeRef}
|
||||||
>
|
>
|
||||||
<span ref={this.titleRef} style={animation}>
|
<span ref={this.titleRef} style={animation}>
|
||||||
<TreeNodeTitle
|
<TreeNodeTitle
|
||||||
@@ -191,14 +193,6 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
lastUpdate={this.props.treeNode.lastUpdate}
|
lastUpdate={this.props.treeNode.lastUpdate}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div
|
|
||||||
className={this.props.classes.topicSelect}
|
|
||||||
ref={this.topicSelectRef}
|
|
||||||
onClick={this.didSelectNode}
|
|
||||||
title="Select topic"
|
|
||||||
>
|
|
||||||
<LabelImportant style={{ fontSize: '14px' }} />
|
|
||||||
</div>
|
|
||||||
{this.renderNodes()}
|
{this.renderNodes()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -206,12 +200,18 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
|
|
||||||
private mouseOver = (event: React.MouseEvent) => {
|
private mouseOver = (event: React.MouseEvent) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
if (this.nodeRef && this.nodeRef.current) {
|
||||||
|
this.nodeRef.current.style.backgroundColor = 'rgba(100, 100, 100, 0.55)'
|
||||||
|
}
|
||||||
if (this.topicSelectRef && this.topicSelectRef.current) {
|
if (this.topicSelectRef && this.topicSelectRef.current) {
|
||||||
this.topicSelectRef.current.style.opacity = '1'
|
this.topicSelectRef.current.style.opacity = '1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private mouseOut = (event: React.MouseEvent) => {
|
private mouseOut = (event: React.MouseEvent) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
if (this.nodeRef && this.nodeRef.current) {
|
||||||
|
this.nodeRef.current.style.backgroundColor = 'inherit'
|
||||||
|
}
|
||||||
if (this.topicSelectRef && this.topicSelectRef.current) {
|
if (this.topicSelectRef && this.topicSelectRef.current) {
|
||||||
this.topicSelectRef.current.style.opacity = '0'
|
this.topicSelectRef.current.style.opacity = '0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AppState } from '../../reducers'
|
|||||||
import TreeNode from './TreeNode'
|
import TreeNode from './TreeNode'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { TopicOrder } from '../../reducers/Settings'
|
import { TopicOrder } from '../../reducers/Settings'
|
||||||
|
import { Theme, withStyles } from '@material-ui/core'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
lastUpdate: number
|
lastUpdate: number
|
||||||
@@ -16,6 +17,7 @@ export interface Props {
|
|||||||
filter?: string
|
filter?: string
|
||||||
collapsed?: boolean | undefined
|
collapsed?: boolean | undefined
|
||||||
didSelectNode?: (node: q.TreeNode) => void
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
|
classes: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -69,24 +71,19 @@ class TreeNodeSubnodes extends React.Component<Props, State> {
|
|||||||
this.renderMore()
|
this.renderMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
const listItemStyle = {
|
|
||||||
padding: '3px 0px 0px 8px',
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodes = this.sortedNodes().slice(0, this.state.alreadyAdded)
|
const nodes = this.sortedNodes().slice(0, this.state.alreadyAdded)
|
||||||
const listItems = nodes.map(node => (
|
const listItems = nodes.map(node => (
|
||||||
<div key={`${node.hash()}-${this.props.filter}`}>
|
<TreeNode
|
||||||
<TreeNode
|
key={`${node.hash()}-${this.props.filter}`}
|
||||||
animateChages={this.props.animateChanges}
|
animateChages={this.props.animateChanges}
|
||||||
treeNode={node}
|
treeNode={node}
|
||||||
lastUpdate={node.lastUpdate}
|
lastUpdate={node.lastUpdate}
|
||||||
style={listItemStyle}
|
className={this.props.classes.listItem}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
))
|
))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span style={{ display: 'block', clear: 'both' }} >
|
<span className={this.props.classes.list}>
|
||||||
{listItems}
|
{listItems}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
@@ -100,4 +97,14 @@ const mapStateToProps = (state: AppState) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps)(TreeNodeSubnodes)
|
const styles = (theme: Theme) => ({
|
||||||
|
list: {
|
||||||
|
display: 'block' as 'block',
|
||||||
|
clear: 'both' as 'both',
|
||||||
|
},
|
||||||
|
listItem: {
|
||||||
|
padding: '3px 0px 0px 8px',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default withStyles(styles)(connect(mapStateToProps)(TreeNodeSubnodes))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { connect } from 'react-redux'
|
|||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import { treeActions } from '../../actions'
|
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'
|
||||||
|
|
||||||
export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
|
export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
treeNode: q.TreeNode
|
treeNode: q.TreeNode
|
||||||
@@ -10,62 +11,33 @@ export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
|
|||||||
name?: string | undefined
|
name?: string | undefined
|
||||||
collapsed?: boolean | undefined
|
collapsed?: boolean | undefined
|
||||||
lastUpdate: number
|
lastUpdate: number
|
||||||
|
classes: any
|
||||||
}
|
}
|
||||||
|
|
||||||
class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
||||||
private getStyles() {
|
private mouseOver = (event: React.MouseEvent) => {
|
||||||
return {
|
|
||||||
collapsedSubnodes: {
|
|
||||||
color: 'white', // theme.palette.text.secondary,
|
|
||||||
},
|
|
||||||
container: {
|
|
||||||
display: 'block',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private didSelectNode = (event: React.MouseEvent) => {
|
|
||||||
event.stopPropagation()
|
|
||||||
if (this.props.treeNode.message) {
|
if (this.props.treeNode.message) {
|
||||||
this.props.actions.selectTopic(this.props.treeNode)
|
this.props.actions.selectTopic(this.props.treeNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const style: React.CSSProperties = {
|
|
||||||
lineHeight: '1em',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<span style={style} onMouseOver={this.didSelectNode}>
|
<span className={this.props.classes.title} onMouseOver={this.mouseOver}>
|
||||||
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSourceEdge() {
|
private renderSourceEdge() {
|
||||||
const style: React.CSSProperties = {
|
|
||||||
fontWeight: 'bold',
|
|
||||||
overflow: 'hidden',
|
|
||||||
display: 'inline-block',
|
|
||||||
}
|
|
||||||
const name = this.props.name || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
|
const name = this.props.name || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
|
||||||
|
|
||||||
return <span style={style}>{name}</span>
|
return <span className={this.props.classes.sourceEdge}>{name}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderValue() {
|
private renderValue() {
|
||||||
const style: React.CSSProperties = {
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
padding: '0',
|
|
||||||
marginLeft: '5px',
|
|
||||||
display: 'inline-block',
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.props.treeNode.message && this.props.treeNode.message.length > 0
|
return this.props.treeNode.message && this.props.treeNode.message.length > 0
|
||||||
? <span style={style}> = {this.props.treeNode.message.value.toString()}</span>
|
? <span className={this.props.classes.value}> = {this.props.treeNode.message.value.toString().slice(0, 120)}</span>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +55,7 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const messages = this.props.treeNode.leafMessageCount()
|
const messages = this.props.treeNode.leafMessageCount()
|
||||||
return <span style={this.getStyles().collapsedSubnodes}>({this.props.treeNode.childTopicCount()} topics, {messages} messages)</span>
|
return <span className={this.props.classes.collapsedSubnodes}>({this.props.treeNode.childTopicCount()} topics, {messages} messages)</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,4 +65,27 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(TreeNodeTitle)
|
const styles = (theme: Theme) => ({
|
||||||
|
value: {
|
||||||
|
whiteSpace: 'nowrap' as 'nowrap',
|
||||||
|
overflow: 'hidden' as 'hidden',
|
||||||
|
textOverflow: 'ellipsis' as 'ellipsis',
|
||||||
|
padding: '0',
|
||||||
|
marginLeft: '5px',
|
||||||
|
display: 'inline-block' as 'inline-block',
|
||||||
|
},
|
||||||
|
sourceEdge: {
|
||||||
|
fontWeight: 'bold' as 'bold',
|
||||||
|
overflow: 'hidden' as 'hidden',
|
||||||
|
display: 'inline-block' as 'inline-block',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
lineHeight: '1em',
|
||||||
|
whiteSpace: 'nowrap' as 'nowrap',
|
||||||
|
},
|
||||||
|
collapsedSubnodes: {
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default withStyles(styles)(connect(null, mapDispatchToProps)(TreeNodeTitle))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import './tracking'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import * as ReactDOM from 'react-dom'
|
import * as ReactDOM from 'react-dom'
|
||||||
import reduxThunk from 'redux-thunk'
|
import reduxThunk from 'redux-thunk'
|
||||||
|
import { batchDispatchMiddleware } from 'redux-batched-actions';
|
||||||
|
|
||||||
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
|
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
|
||||||
import reducers from './reducers'
|
import reducers from './reducers'
|
||||||
@@ -17,6 +18,7 @@ const store = createStore(
|
|||||||
composeEnhancers(
|
composeEnhancers(
|
||||||
applyMiddleware(
|
applyMiddleware(
|
||||||
reduxThunk,
|
reduxThunk,
|
||||||
|
batchDispatchMiddleware,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user