Skip to content

Commit 0d97161

Browse files
feat(statevis): improve layout of vis controls; add close button.
1 parent e1742d1 commit 0d97161

File tree

9 files changed

+182
-47
lines changed

9 files changed

+182
-47
lines changed

src/statevis/Controls.tsx

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,71 @@
11
import { h, Component } from 'preact';
22
import { StateSelector } from '../selector/StateSelector';
33
import { RENDERER_PRESETS, DEFAULT_RENDERER } from './renderers';
4+
import { LayoutPrefs } from './LayoutPrefs';
45
import { Renderer } from './interface';
56
import { UIRouter } from '@uirouter/core';
67

8+
import { ChevronDown } from './icons/ChevronDown';
9+
import { Close} from './icons/Close';
10+
import { Gear } from './icons/Gear';
11+
import { Help } from './icons/Help';
12+
713
export interface IControlsProps {
814
router: UIRouter;
915
onRendererChange: (renderer: Renderer) => void;
1016
onMinimize: () => void;
17+
onClose: () => void;
1118
}
1219

13-
export interface IControlsState {
14-
renderer: Renderer;
15-
presetName: string;
20+
export interface IControlsState {
21+
showRendererPrefs: boolean;
1622
}
1723

1824
declare function require(string): string;
1925
const imgChevron = require('../../images/16/chevron-down.png');
2026

2127
export class Controls extends Component<IControlsProps, IControlsState> {
2228
state = {
23-
renderer: DEFAULT_RENDERER,
24-
presetName: 'Tree',
25-
}
26-
27-
componentDidMount() {
28-
this.props.onRendererChange(this.state.renderer);
29-
}
30-
31-
handleZoom(event: Event) {
32-
let el = event.target;
33-
let value = parseFloat(el['value']);
34-
let renderer = { ...this.state.renderer, zoom: value };
35-
this.setState({ renderer });
36-
this.props.onRendererChange(renderer);
37-
}
38-
39-
handleLayout(event: Event) {
40-
let presetName = event.target['value'];
41-
let settings = RENDERER_PRESETS[presetName];
42-
let renderer = { ...this.state.renderer, ...settings };
43-
this.setState({ renderer, presetName });
44-
this.props.onRendererChange(renderer);
29+
showRendererPrefs: false,
4530
}
4631

4732
render() {
48-
const zoomLevels = [2.0, 1.5, 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3];
49-
5033
return (
34+
<div style={{ width: '100%' }}>
5135
<div className="uirStateVisControls">
52-
<div>Current State: <StateSelector router={this.props.router} /></div>
53-
<div>
54-
<select onChange={this.handleLayout.bind(this)} value={this.state.presetName} style={{ maxWidth: 100 }}>
55-
{Object.keys(RENDERER_PRESETS).map(preset =>
56-
<option value={preset}>{preset}</option>
57-
)}
58-
</select>
36+
<StateSelector router={this.props.router} />
37+
<div style={{ marginLeft: 'auto', cursor: 'pointer' }} className="uirStateVisIcons">
38+
<span className="uirStateVisHover">
39+
<Help/>
40+
<div className="hoverBlock">
41+
<ul>
42+
<li>Click a node to activate that state.</li>
43+
<li>Select a state from the dropdown to activate that state.</li>
44+
<li>Double click a node to auto-collapse that section of the tree when inactive.
45+
Collapsed nodes are displayed with a dotted outline and the count of collapsed children.</li>
46+
<li>Lazy loaded states (including future states) are displayed with a dashed outline.</li>
47+
</ul>
48+
</div>
49+
</span>
5950

60-
<select onChange={this.handleZoom.bind(this)} value={this.state.renderer.zoom + ''} style={{ maxWidth: 100 }}>
61-
{zoomLevels.map(level =>
62-
<option value={level + ""}>{level}x</option>
63-
)}
64-
</select>
51+
<span className="uirStateVisHover">
52+
<Gear/>
53+
<div className="hoverBlock"><LayoutPrefs onRendererChange={this.props.onRendererChange}/></div>
54+
</span>
6555

56+
<span className="uirStateVisHover" onClick={this.props.onMinimize}>
57+
<ChevronDown/>
58+
<div><span style={{float: 'right'}}>Minimize</span></div>
59+
<div>Minimize</div>
60+
</span>
61+
62+
<span className="uirStateVisHover" onClick={this.props.onClose}>
63+
<Close/>
64+
<div><span style={{float: 'right'}}>Close</span></div>
65+
</span>
6666
</div>
6767

68-
<button onClick={this.props.onMinimize}>
69-
<img src={imgChevron} style={{ cursor: 'pointer' }} />
70-
</button>
68+
</div>
7169
</div>
7270
)
7371
}

src/statevis/LayoutPrefs.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { h, Component } from 'preact';
2+
import { StateSelector } from '../selector/StateSelector';
3+
import { RENDERER_PRESETS, DEFAULT_RENDERER } from './renderers';
4+
import { Renderer } from './interface';
5+
import { UIRouter } from '@uirouter/core';
6+
7+
export interface ILayoutPrefsProps {
8+
onRendererChange: (renderer: Renderer) => void;
9+
}
10+
11+
export interface ILayoutPrefsState {
12+
renderer: Renderer;
13+
presetName: string;
14+
}
15+
16+
export class LayoutPrefs extends Component<ILayoutPrefsProps, ILayoutPrefsState> {
17+
state = {
18+
renderer: DEFAULT_RENDERER,
19+
presetName: 'Tree',
20+
}
21+
22+
componentDidMount() {
23+
this.props.onRendererChange(this.state.renderer);
24+
}
25+
26+
handleZoom(event: Event) {
27+
let el = event.target;
28+
let value = parseFloat(el['value']);
29+
let renderer = { ...this.state.renderer, zoom: value };
30+
this.setState({ renderer });
31+
this.props.onRendererChange(renderer);
32+
}
33+
34+
handleLayout(event: Event) {
35+
let presetName = event.target['value'];
36+
let settings = RENDERER_PRESETS[presetName];
37+
let renderer = { ...this.state.renderer, ...settings };
38+
this.setState({ renderer, presetName });
39+
this.props.onRendererChange(renderer);
40+
}
41+
42+
render() {
43+
return (
44+
<div className="uirStateVisLayoutPrefs" style={{display: 'flex', flexFlow: 'column nowrap'}} onMouseDown={evt => evt.stopPropagation()}>
45+
<div style={{flex: '1 1 auto', display: 'flex', flexFlow: 'row nowrap', alignItems: 'center'}}>
46+
<div>Layout:</div>
47+
<select style={{marginLeft: 'auto', maxWidth: '100px'}}
48+
onChange={this.handleLayout.bind(this)}
49+
value={this.state.presetName}>
50+
{Object.keys(RENDERER_PRESETS).map(preset =>
51+
<option value={preset}>{preset}</option>
52+
)}
53+
</select>
54+
</div>
55+
56+
<div style={{flex: '1 1 auto', display: 'flex', flexFlow: 'row nowrap', alignItems: 'center'}}>
57+
<span>Node size:</span>
58+
<input style={{marginLeft: 'auto'}}
59+
value={"" + this.state.renderer.zoom}
60+
type="range"
61+
min="0.3"
62+
max="3.0"
63+
step="0.1"
64+
onInput={this.handleZoom.bind(this)} />
65+
<span>{this.state.renderer.zoom}x</span>
66+
</div>
67+
</div>
68+
)
69+
}
70+
}

src/statevis/StateVisWindow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { h, render, Component } from "preact";
2-
import "./stateVisualizer.css";
2+
import "./statevis.css";
33
import { StateSelector } from "../selector/StateSelector";
44
import { toggleClass, addClass } from "../util/toggleClass";
55
import { draggable, dragActions } from "../util/draggable";

src/statevis/StateVisualizer.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ export class StateVisualizer extends Component<IProps, IState> {
107107
return element;
108108
}
109109

110+
dispose() {
111+
let Nothing = () => null;
112+
render(h(Nothing as any, null), document.body, this.window.el);
113+
}
114+
110115

111116
handleRendererChange(renderer: Renderer) {
112117
this.setState({ renderer });
@@ -154,6 +159,7 @@ export class StateVisualizer extends Component<IProps, IState> {
154159
router={this.props.router}
155160
onRendererChange={this.handleRendererChange.bind(this)}
156161
onMinimize={() => this.setState({ minimized: true })}
162+
onClose={() => this.dispose()}
157163
/>
158164

159165
<StateTree

src/statevis/icons/ChevronDown.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { h } from 'preact';
2+
export const ChevronDown = () =>
3+
<svg width="1em" height="1em" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"/></svg>

src/statevis/icons/Close.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { h } from 'preact';
2+
export const Close = () =>
3+
<svg width="1em" height="1em" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"/></svg>

src/statevis/icons/Gear.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { h } from 'preact';
2+
export const Gear = () =>
3+
<svg width="1em" height="1em" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z"/></svg>

src/statevis/icons/Help.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { h } from 'preact';
2+
export const Help = () =>
3+
<svg width="1em" height="1em" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1008 1200v160q0 14-9 23t-23 9h-160q-14 0-23-9t-9-23v-160q0-14 9-23t23-9h160q14 0 23 9t9 23zm256-496q0 50-15 90t-45.5 69-52 44-59.5 36q-32 18-46.5 28t-26 24-11.5 29v32q0 14-9 23t-23 9h-160q-14 0-23-9t-9-23v-68q0-35 10.5-64.5t24-47.5 39-35.5 41-25.5 44.5-21q53-25 75-43t22-49q0-42-43.5-71.5t-95.5-29.5q-56 0-95 27-29 20-80 83-9 12-25 12-11 0-19-6l-108-82q-10-7-12-20t5-23q122-192 349-192 129 0 238.5 89.5t109.5 214.5zm-368-448q-130 0-248.5 51t-204 136.5-136.5 204-51 248.5 51 248.5 136.5 204 204 136.5 248.5 51 248.5-51 204-136.5 136.5-204 51-248.5-51-248.5-136.5-204-204-136.5-248.5-51zm768 640q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>

src/statevis/statevis.css

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,71 @@
1515
resize: both;
1616
}
1717

18+
.uirStateVisContainer.minimized {
19+
cursor: pointer;
20+
transform: scale(0.25);
21+
}
1822

1923
.uirStateVisContainer:hover {
2024
outline: 3px solid rgba(0,0,0,0.35)
2125
}
2226

23-
.uirStateVisContainer.minimized {
24-
cursor: pointer;
25-
transform: scale(0.25);
27+
.uirStateVisContainer:hover .uirStateVisControls {
28+
visibility: visible;
2629
}
2730

2831
.uirStateVisContainer .uirStateVisControls {
29-
width: 100%;
32+
visibility: hidden;
3033
display: flex;
34+
width: 100%;
3135
flex-flow: row nowrap;
3236
justify-content: space-between;
3337
flex: 1 0 auto;
38+
z-index: 1;
39+
}
40+
41+
.uirStateVisContainer .uirStateVisControls .uirStateVisIcons span svg {
42+
padding: 0.25em;
43+
fill: #777777;
44+
}
45+
46+
.uirStateVisContainer .uirStateVisControls .uirStateVisIcons span:hover svg {
47+
fill: black;
48+
}
49+
50+
.uirStateVisHover > div {
51+
transition: opacity 500ms ease;
52+
opacity: 0;
53+
height: 0px;
54+
padding: 0;
55+
56+
position: absolute;
57+
top: 0;
58+
right: 0;
59+
overflow: hidden;
60+
61+
margin-top: 1.5em;
62+
font-size: 0.8em;
63+
}
64+
65+
.uirStateVisHover > div.hoverBlock {
66+
left: 0;
67+
border-bottom: none;
68+
background: white;
69+
}
70+
71+
.uirStateVisHover:hover > div.hoverBlock {
72+
border-bottom: 2px solid lightgrey;
73+
}
74+
75+
76+
.uirStateVisHover:hover > div {
77+
opacity: 1;
78+
height: auto;
79+
}
80+
81+
.uirStateVisHover .uirStateVisLayoutPrefs {
82+
padding: 0.75em 1.5em;
3483
}
3584

3685
.uirStateVisContainer .statevis {

0 commit comments

Comments
 (0)