Files
weave-scope/client/app/scripts/components/app.js
Simon Howe d0b99969ea Grid-mode tuning!
- Change scrolling behaviour to lock headers in place
- Enable filtering (hitting enter in the search bar) in grid-mode
- Little more top-margin for k8s (can have 3 topos) + taller rows.
- Trying out rank-color + node.relatives in the grid-mode
- First pass at selecting rows.
  - Needs a bit more a fiddle, colors + click areas
- Store grid sort direction (asc/desc) in url state
- Simplify node selection to one method. (over-ride existing card)
  - Remove clicking on name directly (links) to overlay new cards for now.
- Playing w/ grid-mode-toggle icons and labels
- Improves rendering in ff, change of shortcut keys for grid-mode-toggle
- Playing w/ clearer selection colors for grid-mode
- Slight change to selection-ui
- Fixes showNodeInTopology button visibility on the details-panel
  - Was using an old heuristic. Table-mode allows you to open child cards
    before the parent.
- Make it clear what the default sort is in tables
  - E.g. always show a sorting caret
- Sort grid-mode columns, first meta then metrics
- dancing-nodes rememdy #1: pause updates onRowHover
- Splits relatives out into their own columns
- Take into account scrollbar width for grid-mode col header position
- Tooltips on table column headers
- grid-mode: fixes first column headers (proc/container/c-by-image)
- Disable pause-on-hover, too aggresive
- reduce label column width a bit (33pc -> 25pc) for big tables
- Filter grid-mode onSearchChange
  - Rather than previous behaviour of waiting for an <enter>
- Show label_minor on pseudo nodes, that might not have much other info
- grid-mode: further reduce width of id column.
- Fixes go tests, properly moves parents into node-summary
- Fixes sorting of string columns w/ missing fields.
  - E.g. uptime. Where -1e-10 > '3days' doesn't work.
2016-08-03 08:50:37 +02:00

166 lines
5.2 KiB
JavaScript

import debug from 'debug';
import React from 'react';
import { connect } from 'react-redux';
import Logo from './logo';
import Footer from './footer.js';
import Sidebar from './sidebar.js';
import HelpPanel from './help-panel';
import Search from './search';
import Status from './status.js';
import Topologies from './topologies.js';
import TopologyOptions from './topology-options.js';
import { getApiDetails, getTopologies } from '../utils/web-api-utils';
import { focusSearch, pinNextMetric, hitBackspace, hitEnter, hitEsc, unpinMetric,
selectMetric, toggleHelp, toggleGridMode } from '../actions/app-actions';
import Details from './details';
import Nodes from './nodes';
import GridModeSelector from './grid-mode-selector';
import MetricSelector from './metric-selector';
import NetworkSelector from './networks-selector';
import EmbeddedTerminal from './embedded-terminal';
import { getRouter } from '../utils/router-utils';
import DebugToolbar, { showingDebugToolbar,
toggleDebugToolbar } from './debug-toolbar.js';
import { getUrlState } from '../utils/router-utils';
import { getActiveTopologyOptions } from '../utils/topology-utils';
const BACKSPACE_KEY_CODE = 8;
const ENTER_KEY_CODE = 13;
const ESC_KEY_CODE = 27;
const keyPressLog = debug('scope:app-key-press');
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.onKeyPress = this.onKeyPress.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
}
componentDidMount() {
window.addEventListener('keypress', this.onKeyPress);
window.addEventListener('keyup', this.onKeyUp);
getRouter(this.props.dispatch, this.props.urlState).start({hashbang: true});
if (!this.props.routeSet) {
// dont request topologies when already done via router
getTopologies(this.props.activeTopologyOptions, this.props.dispatch);
}
getApiDetails(this.props.dispatch);
}
componentWillUnmount() {
window.removeEventListener('keypress', this.onKeyPress);
window.removeEventListener('keyup', this.onKeyUp);
}
onKeyUp(ev) {
const { showingTerminal } = this.props;
keyPressLog('onKeyUp', 'keyCode', ev.keyCode, ev);
// don't get esc in onKeyPress
if (ev.keyCode === ESC_KEY_CODE) {
this.props.dispatch(hitEsc());
} else if (ev.keyCode === ENTER_KEY_CODE) {
this.props.dispatch(hitEnter());
} else if (ev.keyCode === BACKSPACE_KEY_CODE) {
this.props.dispatch(hitBackspace());
} else if (ev.code === 'KeyD' && ev.ctrlKey && !showingTerminal) {
toggleDebugToolbar();
this.forceUpdate();
}
}
onKeyPress(ev) {
const { dispatch, searchFocused } = this.props;
//
// keyup gives 'key'
// keypress gives 'char'
// Distinction is important for international keyboard layouts where there
// is often a different {key: char} mapping.
//
if (!searchFocused) {
keyPressLog('onKeyPress', 'keyCode', ev.keyCode, ev);
const char = String.fromCharCode(ev.charCode);
if (char === '<') {
dispatch(pinNextMetric(-1));
} else if (char === '>') {
dispatch(pinNextMetric(1));
} else if (char === 'v') {
dispatch(toggleGridMode(false));
} else if (char === 't') {
dispatch(toggleGridMode(true));
} else if (char === 'q') {
dispatch(unpinMetric());
dispatch(selectMetric(null));
} else if (char === '/') {
ev.preventDefault();
dispatch(focusSearch());
} else if (char === '?') {
dispatch(toggleHelp());
}
}
}
render() {
const { gridMode, showingDetails, showingHelp, showingMetricsSelector,
showingNetworkSelector, showingTerminal } = this.props;
const isIframe = window !== window.top;
return (
<div className="app">
{showingDebugToolbar() && <DebugToolbar />}
{showingHelp && <HelpPanel />}
{showingDetails && <Details />}
{showingTerminal && <EmbeddedTerminal />}
<div className="header">
<div className="logo">
{!isIframe && <svg width="100%" height="100%" viewBox="0 0 1089 217">
<Logo />
</svg>}
</div>
<Search />
<Topologies />
</div>
<Nodes />
<Sidebar classNames={gridMode ? 'sidebar-gridmode' : ''}>
{showingMetricsSelector && !gridMode && <MetricSelector />}
{showingNetworkSelector && !gridMode && <NetworkSelector />}
<Status />
<GridModeSelector />
<TopologyOptions />
</Sidebar>
<Footer />
</div>
);
}
}
function mapStateToProps(state) {
return {
activeTopologyOptions: getActiveTopologyOptions(state),
gridMode: state.get('gridMode'),
routeSet: state.get('routeSet'),
searchFocused: state.get('searchFocused'),
searchQuery: state.get('searchQuery'),
showingDetails: state.get('nodeDetails').size > 0,
showingHelp: state.get('showingHelp'),
showingMetricsSelector: state.get('availableCanvasMetrics').count() > 0,
showingNetworkSelector: state.get('availableNetworks').count() > 0,
showingTerminal: state.get('controlPipes').size > 0,
urlState: getUrlState(state)
};
}
export default connect(
mapStateToProps
)(App);