Skip to content

Commit

Permalink
feat(statevis): improve layout of vis controls; add close button.
Browse files Browse the repository at this point in the history
  • Loading branch information
christopherthielen committed Dec 1, 2017
1 parent e1742d1 commit 0d97161
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 47 deletions.
82 changes: 40 additions & 42 deletions src/statevis/Controls.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,71 @@
import { h, Component } from 'preact';
import { StateSelector } from '../selector/StateSelector';
import { RENDERER_PRESETS, DEFAULT_RENDERER } from './renderers';
import { LayoutPrefs } from './LayoutPrefs';
import { Renderer } from './interface';
import { UIRouter } from '@uirouter/core';

import { ChevronDown } from './icons/ChevronDown';
import { Close} from './icons/Close';
import { Gear } from './icons/Gear';
import { Help } from './icons/Help';

export interface IControlsProps {
router: UIRouter;
onRendererChange: (renderer: Renderer) => void;
onMinimize: () => void;
onClose: () => void;
}

export interface IControlsState {
renderer: Renderer;
presetName: string;
export interface IControlsState {
showRendererPrefs: boolean;
}

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

export class Controls extends Component<IControlsProps, IControlsState> {
state = {
renderer: DEFAULT_RENDERER,
presetName: 'Tree',
}

componentDidMount() {
this.props.onRendererChange(this.state.renderer);
}

handleZoom(event: Event) {
let el = event.target;
let value = parseFloat(el['value']);
let renderer = { ...this.state.renderer, zoom: value };
this.setState({ renderer });
this.props.onRendererChange(renderer);
}

handleLayout(event: Event) {
let presetName = event.target['value'];
let settings = RENDERER_PRESETS[presetName];
let renderer = { ...this.state.renderer, ...settings };
this.setState({ renderer, presetName });
this.props.onRendererChange(renderer);
showRendererPrefs: false,
}

render() {
const zoomLevels = [2.0, 1.5, 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3];

return (
<div style={{ width: '100%' }}>
<div className="uirStateVisControls">
<div>Current State: <StateSelector router={this.props.router} /></div>
<div>
<select onChange={this.handleLayout.bind(this)} value={this.state.presetName} style={{ maxWidth: 100 }}>
{Object.keys(RENDERER_PRESETS).map(preset =>
<option value={preset}>{preset}</option>
)}
</select>
<StateSelector router={this.props.router} />
<div style={{ marginLeft: 'auto', cursor: 'pointer' }} className="uirStateVisIcons">
<span className="uirStateVisHover">
<Help/>
<div className="hoverBlock">
<ul>
<li>Click a node to activate that state.</li>
<li>Select a state from the dropdown to activate that state.</li>
<li>Double click a node to auto-collapse that section of the tree when inactive.
Collapsed nodes are displayed with a dotted outline and the count of collapsed children.</li>
<li>Lazy loaded states (including future states) are displayed with a dashed outline.</li>
</ul>
</div>
</span>

<select onChange={this.handleZoom.bind(this)} value={this.state.renderer.zoom + ''} style={{ maxWidth: 100 }}>
{zoomLevels.map(level =>
<option value={level + ""}>{level}x</option>
)}
</select>
<span className="uirStateVisHover">
<Gear/>
<div className="hoverBlock"><LayoutPrefs onRendererChange={this.props.onRendererChange}/></div>
</span>

<span className="uirStateVisHover" onClick={this.props.onMinimize}>
<ChevronDown/>
<div><span style={{float: 'right'}}>Minimize</span></div>
<div>Minimize</div>
</span>

<span className="uirStateVisHover" onClick={this.props.onClose}>
<Close/>
<div><span style={{float: 'right'}}>Close</span></div>
</span>
</div>

<button onClick={this.props.onMinimize}>
<img src={imgChevron} style={{ cursor: 'pointer' }} />
</button>
</div>
</div>
)
}
Expand Down
70 changes: 70 additions & 0 deletions src/statevis/LayoutPrefs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { h, Component } from 'preact';
import { StateSelector } from '../selector/StateSelector';
import { RENDERER_PRESETS, DEFAULT_RENDERER } from './renderers';
import { Renderer } from './interface';
import { UIRouter } from '@uirouter/core';

export interface ILayoutPrefsProps {
onRendererChange: (renderer: Renderer) => void;
}

export interface ILayoutPrefsState {
renderer: Renderer;
presetName: string;
}

export class LayoutPrefs extends Component<ILayoutPrefsProps, ILayoutPrefsState> {
state = {
renderer: DEFAULT_RENDERER,
presetName: 'Tree',
}

componentDidMount() {
this.props.onRendererChange(this.state.renderer);
}

handleZoom(event: Event) {
let el = event.target;
let value = parseFloat(el['value']);
let renderer = { ...this.state.renderer, zoom: value };
this.setState({ renderer });
this.props.onRendererChange(renderer);
}

handleLayout(event: Event) {
let presetName = event.target['value'];
let settings = RENDERER_PRESETS[presetName];
let renderer = { ...this.state.renderer, ...settings };
this.setState({ renderer, presetName });
this.props.onRendererChange(renderer);
}

render() {
return (
<div className="uirStateVisLayoutPrefs" style={{display: 'flex', flexFlow: 'column nowrap'}} onMouseDown={evt => evt.stopPropagation()}>
<div style={{flex: '1 1 auto', display: 'flex', flexFlow: 'row nowrap', alignItems: 'center'}}>
<div>Layout:</div>
<select style={{marginLeft: 'auto', maxWidth: '100px'}}
onChange={this.handleLayout.bind(this)}
value={this.state.presetName}>
{Object.keys(RENDERER_PRESETS).map(preset =>
<option value={preset}>{preset}</option>
)}
</select>
</div>

<div style={{flex: '1 1 auto', display: 'flex', flexFlow: 'row nowrap', alignItems: 'center'}}>
<span>Node size:</span>
<input style={{marginLeft: 'auto'}}
value={"" + this.state.renderer.zoom}
type="range"
min="0.3"
max="3.0"
step="0.1"
onInput={this.handleZoom.bind(this)} />
<span>{this.state.renderer.zoom}x</span>
</div>
</div>
)
}
}
2 changes: 1 addition & 1 deletion src/statevis/StateVisWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { h, render, Component } from "preact";
import "./stateVisualizer.css";
import "./statevis.css";
import { StateSelector } from "../selector/StateSelector";
import { toggleClass, addClass } from "../util/toggleClass";
import { draggable, dragActions } from "../util/draggable";
Expand Down
6 changes: 6 additions & 0 deletions src/statevis/StateVisualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ export class StateVisualizer extends Component<IProps, IState> {
return element;
}

dispose() {
let Nothing = () => null;
render(h(Nothing as any, null), document.body, this.window.el);
}


handleRendererChange(renderer: Renderer) {
this.setState({ renderer });
Expand Down Expand Up @@ -154,6 +159,7 @@ export class StateVisualizer extends Component<IProps, IState> {
router={this.props.router}
onRendererChange={this.handleRendererChange.bind(this)}
onMinimize={() => this.setState({ minimized: true })}
onClose={() => this.dispose()}
/>

<StateTree
Expand Down
3 changes: 3 additions & 0 deletions src/statevis/icons/ChevronDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { h } from 'preact';
export const ChevronDown = () =>
<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>
3 changes: 3 additions & 0 deletions src/statevis/icons/Close.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { h } from 'preact';
export const Close = () =>
<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>
3 changes: 3 additions & 0 deletions src/statevis/icons/Gear.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { h } from 'preact';
export const Gear = () =>
<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>
3 changes: 3 additions & 0 deletions src/statevis/icons/Help.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { h } from 'preact';
export const Help = () =>
<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>
57 changes: 53 additions & 4 deletions src/statevis/statevis.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,71 @@
resize: both;
}

.uirStateVisContainer.minimized {
cursor: pointer;
transform: scale(0.25);
}

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

.uirStateVisContainer.minimized {
cursor: pointer;
transform: scale(0.25);
.uirStateVisContainer:hover .uirStateVisControls {
visibility: visible;
}

.uirStateVisContainer .uirStateVisControls {
width: 100%;
visibility: hidden;
display: flex;
width: 100%;
flex-flow: row nowrap;
justify-content: space-between;
flex: 1 0 auto;
z-index: 1;
}

.uirStateVisContainer .uirStateVisControls .uirStateVisIcons span svg {
padding: 0.25em;
fill: #777777;
}

.uirStateVisContainer .uirStateVisControls .uirStateVisIcons span:hover svg {
fill: black;
}

.uirStateVisHover > div {
transition: opacity 500ms ease;
opacity: 0;
height: 0px;
padding: 0;

position: absolute;
top: 0;
right: 0;
overflow: hidden;

margin-top: 1.5em;
font-size: 0.8em;
}

.uirStateVisHover > div.hoverBlock {
left: 0;
border-bottom: none;
background: white;
}

.uirStateVisHover:hover > div.hoverBlock {
border-bottom: 2px solid lightgrey;
}


.uirStateVisHover:hover > div {
opacity: 1;
height: auto;
}

.uirStateVisHover .uirStateVisLayoutPrefs {
padding: 0.75em 1.5em;
}

.uirStateVisContainer .statevis {
Expand Down

0 comments on commit 0d97161

Please sign in to comment.