mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-06 01:08:03 +00:00
Merge pull request #2204 from weaveworks/contrast-as-component
Added dynamic contrast-mode toggle
This commit is contained in:
@@ -643,6 +643,17 @@ export function receiveNotFound(nodeId) {
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleContrastMode(enabled) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ActionTypes.TOGGLE_CONTRAST_MODE,
|
||||
enabled,
|
||||
});
|
||||
|
||||
updateRoute(getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function route(urlState) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
@@ -664,6 +675,10 @@ export function route(urlState) {
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
|
||||
if (urlState.contrastMode) {
|
||||
dispatch(toggleContrastMode(true));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { connect } from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { enterEdge, leaveEdge } from '../actions/app-actions';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
|
||||
class Edge extends React.Component {
|
||||
@@ -15,9 +14,9 @@ class Edge extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id, path, highlighted, blurred, focused, scale } = this.props;
|
||||
const { id, path, highlighted, blurred, focused, scale, contrastMode } = this.props;
|
||||
const className = classNames('edge', { highlighted, blurred, focused });
|
||||
const thickness = scale * (isContrastMode() ? 0.02 : 0.01) * NODE_BASE_SIZE;
|
||||
const thickness = scale * (contrastMode ? 0.02 : 0.01) * NODE_BASE_SIZE;
|
||||
|
||||
// Draws the edge so that its thickness reflects the zoom scale.
|
||||
// Edge shadow is always made 10x thicker than the edge itself.
|
||||
@@ -41,7 +40,13 @@ class Edge extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapStateToProps,
|
||||
{ enterEdge, leaveEdge }
|
||||
)(Edge);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { scaleBand } from 'd3-scale';
|
||||
import { List as makeList } from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getNetworkColor } from '../utils/color-utils';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
|
||||
// Min size is about a quarter of the width, feels about right.
|
||||
@@ -13,7 +14,7 @@ const borderRadius = 0.01;
|
||||
const offset = 0.67;
|
||||
const x = scaleBand();
|
||||
|
||||
function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
function NodeNetworksOverlay({ stack, networks = makeList(), contrastMode }) {
|
||||
const barWidth = Math.max(1, minBarWidth * networks.size);
|
||||
const yPosition = offset - (barHeight * 0.5);
|
||||
|
||||
@@ -37,7 +38,7 @@ function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
/>
|
||||
));
|
||||
|
||||
const translateY = stack && isContrastMode() ? 0.15 : 0;
|
||||
const translateY = stack && contrastMode ? 0.15 : 0;
|
||||
return (
|
||||
<g transform={`translate(0, ${translateY}) scale(${NODE_BASE_SIZE})`}>
|
||||
{bars.toJS()}
|
||||
@@ -45,4 +46,10 @@ function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default NodeNetworksOverlay;
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NodeNetworksOverlay);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
|
||||
export default function NodeShapeStack(props) {
|
||||
const shift = isContrastMode() ? 0.15 : 0.1;
|
||||
function NodeShapeStack(props) {
|
||||
const shift = props.contrastMode ? 0.15 : 0.1;
|
||||
const highlightScale = [1, 1 + shift];
|
||||
const dy = NODE_BASE_SIZE * shift;
|
||||
|
||||
@@ -26,3 +26,11 @@ export default function NodeShapeStack(props) {
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NodeShapeStack);
|
||||
|
||||
@@ -13,7 +13,7 @@ import Topologies from './topologies';
|
||||
import TopologyOptions from './topology-options';
|
||||
import { getApiDetails, getTopologies } from '../utils/web-api-utils';
|
||||
import { focusSearch, pinNextMetric, hitBackspace, hitEnter, hitEsc, unpinMetric,
|
||||
selectMetric, toggleHelp, toggleGridMode } from '../actions/app-actions';
|
||||
selectMetric, toggleHelp, toggleGridMode, toggleContrastMode } from '../actions/app-actions';
|
||||
import Details from './details';
|
||||
import Nodes from './nodes';
|
||||
import GridModeSelector from './grid-mode-selector';
|
||||
@@ -153,11 +153,12 @@ function mapStateToProps(state) {
|
||||
showingMetricsSelector: state.get('availableCanvasMetrics').count() > 0,
|
||||
showingNetworkSelector: state.get('availableNetworks').count() > 0,
|
||||
showingTerminal: state.get('controlPipes').size > 0,
|
||||
urlState: getUrlState(state)
|
||||
urlState: getUrlState(state),
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
mapStateToProps,
|
||||
dispatch => ({ dispatch, toggleContrastMode })
|
||||
)(App);
|
||||
|
||||
@@ -4,19 +4,22 @@ import moment from 'moment';
|
||||
|
||||
import Plugins from './plugins';
|
||||
import { getUpdateBufferSize } from '../utils/update-buffer-utils';
|
||||
import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils';
|
||||
import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate,
|
||||
clickResumeUpdate, toggleHelp, toggleTroubleshootingMenu } from '../actions/app-actions';
|
||||
import { basePathSlash } from '../utils/web-api-utils';
|
||||
clickResumeUpdate, toggleHelp, toggleTroubleshootingMenu, toggleContrastMode } from '../actions/app-actions';
|
||||
|
||||
class Footer extends React.Component {
|
||||
render() {
|
||||
const { hostname, updatePausedAt, version, versionUpdate } = this.props;
|
||||
const contrastMode = isContrastMode();
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.handleContrastClick = this.handleContrastClick.bind(this);
|
||||
}
|
||||
handleContrastClick(e) {
|
||||
e.preventDefault();
|
||||
this.props.toggleContrastMode(!this.props.contrastMode);
|
||||
}
|
||||
render() {
|
||||
const { hostname, updatePausedAt, version, versionUpdate, contrastMode } = this.props;
|
||||
|
||||
// link url to switch contrast with current UI state
|
||||
const otherContrastModeUrl = contrastMode
|
||||
? basePathSlash(window.location.pathname) : contrastModeUrl;
|
||||
const otherContrastModeTitle = contrastMode
|
||||
? 'Switch to normal contrast' : 'Switch to high contrast';
|
||||
const forceRelayoutTitle = 'Force re-layout (might reduce edge crossings, '
|
||||
@@ -76,7 +79,7 @@ class Footer extends React.Component {
|
||||
title={forceRelayoutTitle}>
|
||||
<span className="fa fa-refresh" />
|
||||
</a>
|
||||
<a className="footer-icon" href={otherContrastModeUrl} title={otherContrastModeTitle}>
|
||||
<a onClick={this.handleContrastClick} className="footer-icon" title={otherContrastModeTitle}>
|
||||
<span className="fa fa-adjust" />
|
||||
</a>
|
||||
<a
|
||||
@@ -101,7 +104,8 @@ function mapStateToProps(state) {
|
||||
hostname: state.get('hostname'),
|
||||
updatePausedAt: state.get('updatePausedAt'),
|
||||
version: state.get('version'),
|
||||
versionUpdate: state.get('versionUpdate')
|
||||
versionUpdate: state.get('versionUpdate'),
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,6 +117,7 @@ export default connect(
|
||||
clickPauseUpdate,
|
||||
clickResumeUpdate,
|
||||
toggleHelp,
|
||||
toggleTroubleshootingMenu
|
||||
toggleTroubleshootingMenu,
|
||||
toggleContrastMode
|
||||
}
|
||||
)(Footer);
|
||||
|
||||
@@ -58,6 +58,7 @@ const ACTION_TYPES = [
|
||||
'SET_RECEIVED_NODES_DELTA',
|
||||
'SORT_ORDER_CHANGED',
|
||||
'SET_GRID_MODE',
|
||||
'TOGGLE_CONTRAST_MODE'
|
||||
];
|
||||
|
||||
export default zipObject(ACTION_TYPES, ACTION_TYPES);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'babel-polyfill';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import '../styles/contrast.scss';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
function renderApp() {
|
||||
const App = require('./components/app').default;
|
||||
ReactDOM.render((
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
), document.getElementById('app'));
|
||||
}
|
||||
|
||||
renderApp();
|
||||
if (module.hot) {
|
||||
module.hot.accept('./components/app', renderApp);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax, import/no-unresolved */
|
||||
import debug from 'debug';
|
||||
import { size, each, includes } from 'lodash';
|
||||
import { fromJS, is as isDeepEqual, List as makeList, Map as makeMap,
|
||||
@@ -30,6 +31,7 @@ const topologySorter = topology => topology.get('rank');
|
||||
export const initialState = makeMap({
|
||||
availableCanvasMetrics: makeList(),
|
||||
availableNetworks: makeList(),
|
||||
contrastMode: false,
|
||||
controlPipes: makeOrderedMap(), // pipeId -> controlPipe
|
||||
controlStatus: makeMap(),
|
||||
currentTopology: null,
|
||||
@@ -721,6 +723,33 @@ export function rootReducer(state = initialState, action) {
|
||||
return state.set('showingTroubleshootingMenu', !state.get('showingTroubleshootingMenu'));
|
||||
}
|
||||
|
||||
case ActionTypes.TOGGLE_CONTRAST_MODE: {
|
||||
const modules = [
|
||||
require.resolve('../../styles/main.scss'),
|
||||
require.resolve('../../styles/contrast.scss')
|
||||
];
|
||||
// Bust the webpack require cache to for a re-download of the stylesheets
|
||||
modules.forEach((i) => {
|
||||
const children = require.cache[i] ? require.cache[i].children : [];
|
||||
children.forEach((c) => {
|
||||
delete require.cache[c];
|
||||
});
|
||||
delete require.cache[i];
|
||||
});
|
||||
|
||||
if (action.enabled) {
|
||||
require.ensure([], () => {
|
||||
require('../../styles/contrast.scss');
|
||||
});
|
||||
} else {
|
||||
require.ensure([], () => {
|
||||
require('../../styles/main.scss');
|
||||
});
|
||||
}
|
||||
|
||||
return state.set('contrastMode', action.enabled);
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export const contrastModeUrl = 'contrast.html';
|
||||
|
||||
const contrastMode = window.location.pathname.indexOf(contrastModeUrl) > -1;
|
||||
|
||||
export function isContrastMode() {
|
||||
return contrastMode;
|
||||
}
|
||||
@@ -19,11 +19,18 @@ function encodeURL(url) {
|
||||
.replace(new RegExp(SLASH, 'g'), SLASH_REPLACEMENT);
|
||||
}
|
||||
|
||||
function decodeURL(url) {
|
||||
export function decodeURL(url) {
|
||||
return decodeURIComponent(url.replace(new RegExp(SLASH_REPLACEMENT, 'g'), SLASH))
|
||||
.replace(new RegExp(PERCENT_REPLACEMENT, 'g'), PERCENT);
|
||||
}
|
||||
|
||||
export function parseHashState(hash = window.location.hash) {
|
||||
const urlStateString = hash
|
||||
.replace('#!/state/', '')
|
||||
.replace('#!/', '') || '{}';
|
||||
return JSON.parse(decodeURL(urlStateString));
|
||||
}
|
||||
|
||||
function shouldReplaceState(prevState, nextState) {
|
||||
// Opening a new terminal while an existing one is open.
|
||||
const terminalToTerminal = (prevState.controlPipe && nextState.controlPipe);
|
||||
@@ -50,7 +57,8 @@ export function getUrlState(state) {
|
||||
gridSortedBy: state.get('gridSortedBy'),
|
||||
gridSortedDesc: state.get('gridSortedDesc'),
|
||||
topologyId: state.get('currentTopologyId'),
|
||||
topologyOptions: state.get('topologyOptions').toJS() // all options
|
||||
topologyOptions: state.get('topologyOptions').toJS(), // all options,
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
|
||||
if (state.get('showingNetworks')) {
|
||||
@@ -67,10 +75,7 @@ export function updateRoute(getState) {
|
||||
const state = getUrlState(getState());
|
||||
const stateUrl = encodeURL(JSON.stringify(state));
|
||||
const dispatch = false;
|
||||
const urlStateString = window.location.hash
|
||||
.replace('#!/state/', '')
|
||||
.replace('#!/', '') || '{}';
|
||||
const prevState = JSON.parse(decodeURL(urlStateString));
|
||||
const prevState = parseHashState();
|
||||
|
||||
// back up state in storage as well
|
||||
storageSet(STORAGE_STATE_KEY, stateUrl);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 741 B |
@@ -2,7 +2,6 @@ const webpack = require('webpack');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
/**
|
||||
* This is the Webpack configuration file for local development.
|
||||
* It contains local-specific configuration which includes:
|
||||
@@ -28,10 +27,6 @@ module.exports = {
|
||||
'./app/scripts/main.dev',
|
||||
'webpack-hot-middleware/client'
|
||||
],
|
||||
'contrast-app': [
|
||||
'./app/scripts/contrast-main',
|
||||
'webpack-hot-middleware/client'
|
||||
],
|
||||
'terminal-app': [
|
||||
'./app/scripts/terminal-main',
|
||||
'webpack-hot-middleware/client'
|
||||
@@ -45,7 +40,7 @@ module.exports = {
|
||||
// Used by Webpack Dev Middleware
|
||||
output: {
|
||||
publicPath: '',
|
||||
path: '/',
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
|
||||
@@ -56,11 +51,6 @@ module.exports = {
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'contrast-app'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'contrast.html'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'terminal-app'],
|
||||
template: 'app/html/index.html',
|
||||
|
||||
@@ -31,7 +31,6 @@ module.exports = {
|
||||
|
||||
entry: {
|
||||
app: './app/scripts/main',
|
||||
'contrast-app': './app/scripts/contrast-main',
|
||||
'terminal-app': './app/scripts/terminal-main',
|
||||
// keep only some in here, to make vendors and app bundles roughly same size
|
||||
vendors: ['babel-polyfill', 'classnames', 'immutable',
|
||||
@@ -112,12 +111,6 @@ module.exports = {
|
||||
}
|
||||
}),
|
||||
new ExtractTextPlugin('style-[name]-[chunkhash].css'),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
chunks: ['vendors', 'contrast-app'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'contrast.html'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
chunks: ['vendors', 'terminal-app'],
|
||||
|
||||
Reference in New Issue
Block a user