Merge pull request #954 from weaveworks/contrast-mode

High contrast mode switch
This commit is contained in:
David
2016-02-15 16:21:40 +00:00
12 changed files with 152 additions and 69 deletions

View File

@@ -51,7 +51,7 @@ export default class App extends React.Component {
AppStore.addListener(this.onChange);
window.addEventListener('keyup', this.onKeyPress);
getRouter().start({hashbang: true});
getRouter(this.props.base).start({hashbang: true});
if (!AppStore.isRouteSet()) {
// dont request topologies when already done via router
getTopologies(AppStore.getActiveTopologyOptions());
@@ -75,6 +75,10 @@ export default class App extends React.Component {
// width of details panel blocking a view
const detailsWidth = showingDetails ? 450 : 0;
const topMargin = 100;
const contrastMode = this.props.base.indexOf('contrast') > -1;
// link url to switch contrast with current UI state
const otherContrastModeUrl = contrastMode ? '/' : '/contrast.html';
const otherContrastModeTitle = contrastMode ? 'Switch to normal contrast' : 'Switch to high contrast';
return (
<div className="app">
@@ -112,9 +116,12 @@ export default class App extends React.Component {
{this.state.version}
<span className="footer-label">on</span>
{this.state.hostname}
&nbsp;&nbsp;
<a className="footer-label" href="https://gitreports.com/issue/weaveworks/scope" target="_blank">
Report an issue
&nbsp;
<a className="footer-label footer-label-icon" href={otherContrastModeUrl} title={otherContrastModeTitle}>
<span className="fa fa-adjust" />
</a>
<a className="footer-label footer-label-icon" href="https://gitreports.com/issue/weaveworks/scope" target="_blank" title="Report an issue">
<span className="fa fa-bug" />
</a>
</div>
</div>

View File

@@ -0,0 +1,9 @@
require('font-awesome-webpack');
require('../styles/contrast.less');
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app.js';
ReactDOM.render(<App base="/contrast.html" />, document.getElementById('app'));

View File

@@ -6,4 +6,4 @@ import ReactDOM from 'react-dom';
import App from './components/app.js';
ReactDOM.render(<App/>, document.getElementById('app'));
ReactDOM.render(<App base="" />, document.getElementById('app'));

View File

@@ -59,7 +59,9 @@ export function getNodeColorDark(text = '', secondText = '', isPseudo = false) {
let hsl = color.hsl();
// ensure darkness
if (hsl.l > 0.7) {
if (hsl.h > 20 && hsl.h < 120) {
hsl = hsl.darker(2);
} else if (hsl.l > 0.7) {
hsl = hsl.darker(1.5);
} else {
hsl = hsl.darker(1);

View File

@@ -16,7 +16,9 @@ export function updateRoute() {
const state = AppStore.getAppState();
const stateUrl = JSON.stringify(state);
const dispatch = false;
const urlStateString = window.location.hash.replace('#!/state/', '') || '{}';
const urlStateString = window.location.hash
.replace('#!/state/', '')
.replace('#!/', '') || '{}';
const prevState = JSON.parse(urlStateString);
if (shouldReplaceState(prevState, state)) {
@@ -36,6 +38,7 @@ page('/state/:state', function(ctx) {
route(state);
});
export function getRouter() {
export function getRouter(base) {
page.base(base);
return page;
}

View File

@@ -8,8 +8,6 @@ import { clearControlError, closeWebsocket, openWebsocket, receiveError,
import { API_INTERVAL, TOPOLOGY_INTERVAL } from '../constants/timer';
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = wsProto + '://' + location.host + location.pathname.replace(/\/$/, '');
const log = debug('scope:web-api-utils');
const reconnectTimerInterval = 5000;
@@ -47,6 +45,9 @@ export function basePath(urlPath) {
return parts.join('/').replace(/\/$/, '');
}
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = wsProto + '://' + location.host + basePath(location.pathname);
function createWebsocket(topologyUrl, optionsQuery) {
if (socket) {
socket.onclose = null;

View File

@@ -0,0 +1,31 @@
@import "main";
@background-color: lighten(@primary-color, 75%);
@background-lighter-color: lighten(@background-color, 10%);
@background-darker-color: darken(@background-color, 20%);
@background-darker-secondary-color: darken(@background-color, 15%);
@background-dark-color: @primary-color;
@text-color: darken(@primary-color, 20%);
@text-secondary-color: lighten(@text-color, 10%);
@text-tertiary-color: lighten(@text-color, 20%);
@border-light-color: lighten(@text-color, 50%);
@text-darker-color: darken(@text-color, 20%);
@white: @background-lighter-color;
@node-opacity-blurred: 0.6;
@node-highlight-fill-opacity: 0.3;
@node-highlight-stroke-opacity: 0.5;
@node-highlight-stroke-width: 3px;
@node-border-stroke-width: 6px;
@node-pseudo-border-stroke-width: 2px;
@node-pseudo-opacity: 1;
@edge-highlight-opacity: 0.3;
@edge-opacity-blurred: 0;
@edge-opacity: 0.5;
@edge-link-stroke-width: 3px;
@btn-opacity-default: 1;
@btn-opacity-hover: 1;
@btn-opacity-selected: 1;
@link-opacity-default: 1;

View File

@@ -27,7 +27,6 @@
@background-darker-color: darken(@background-color, 8%);
@background-darker-secondary-color: darken(@background-color, 4%);
@background-dark-color: @primary-color;
@background-medium-color: lighten(@background-dark-color, 55%);
@text-color: lighten(@primary-color, 10%);
@text-secondary-color: lighten(@primary-color, 27%);
@text-tertiary-color: lighten(@primary-color, 50%);
@@ -40,6 +39,24 @@
@terminal-header-height: 34px;
@node-opacity-blurred: 0.25;
@node-highlight-fill-opacity: 0.1;
@node-highlight-stroke-opacity: 0.4;
@node-highlight-stroke-width: 1px;
@node-border-stroke-width: 3px;
@node-pseudo-border-stroke-width: 1px;
@node-pseudo-opacity: 0.8;
@edge-highlight-opacity: 0.1;
@edge-opacity-blurred: 0.2;
@edge-opacity: 0.5;
@edge-link-stroke-width: 1px;
@btn-opacity-default: 0.7;
@btn-opacity-hover: 1;
@btn-opacity-selected: 0.9;
@link-opacity-default: 0.8;
/* add this class to truncate text with ellipsis, container needs width */
.truncate {
white-space: nowrap;
@@ -77,12 +94,12 @@
.btn-opacity {
.palable;
opacity: 0.8;
opacity: @btn-opacity-default;
&-selected {
opacity: 0.9;
opacity: @btn-opacity-selected;
}
&:hover {
opacity: 1;
opacity: @btn-opacity-hover;
}
}
@@ -170,6 +187,23 @@ h2 {
&-label {
text-transform: uppercase;
margin: 0 0.25em;
&-icon {
margin-left: 0.5em;
padding: 4px 3px;
color: @text-color;
position: relative;
border: 1px solid transparent;
border-radius: 10%;
&:hover {
border: 1px solid @text-secondary-color;
}
span {
font-size: 150%;
position: relative;
top: 2px;
}
}
}
}
@@ -206,10 +240,9 @@ h2 {
.topologies-sub-item {
// border: 1px solid @background-darker-secondary-color;
color: @text-secondary-color;
.palable;
.btn-opacity;
cursor: pointer;
padding: 4px 8px;
opacity: 0.7;
border-radius: 4px;
opacity: 0.8;
margin-bottom: 3px;
@@ -222,10 +255,6 @@ h2 {
&-active {
opacity: 0.85;
}
&:hover {
opacity: 1;
}
}
.topologies-sub-item {
@@ -280,7 +309,7 @@ h2 {
transition: opacity .5s ease-in-out;
&.pseudo {
opacity: 0.8;
opacity: @node-pseudo-opacity;
cursor: default;
.node-label {
@@ -293,12 +322,12 @@ h2 {
.border {
stroke: @text-tertiary-color;
stroke-width: 1px;
stroke-width: @node-pseudo-border-stroke-width;
}
}
&.blurred {
opacity: 0.25;
opacity: @node-opacity-blurred;
}
}
@@ -306,14 +335,14 @@ h2 {
transition: opacity .5s ease-in-out;
&.blurred {
opacity: 0.2;
opacity: @edge-opacity-blurred;
}
.link {
stroke: @text-secondary-color;
stroke-width: 1px;
stroke-width: @edge-link-stroke-width;
fill: none;
stroke-opacity: 0.5;
stroke-opacity: @edge-opacity;
}
.shadow {
stroke: @weave-blue;
@@ -323,14 +352,14 @@ h2 {
}
&.highlighted {
.shadow {
stroke-opacity: 0.1;
stroke-opacity: @edge-highlight-opacity;
}
}
}
circle.border {
stroke-width: 3px;
stroke-width: @node-border-stroke-width;
fill: none;
}
@@ -345,10 +374,10 @@ h2 {
circle.highlighted {
fill: @weave-blue;
fill-opacity: 0.1;
fill-opacity: @node-highlight-fill-opacity;
stroke: @weave-blue;
stroke-width: 1px;
stroke-opacity: 0.4;
stroke-width: @node-highlight-stroke-width;
stroke-opacity: @node-highlight-stroke-opacity;
}
}
@@ -390,18 +419,16 @@ h2 {
right: 8px;
span {
.palable;
.btn-opacity;
padding: 4px 5px;
margin-left: 2px;
font-size: 110%;
color: @white;
cursor: pointer;
opacity: 0.7;
border: 1px solid rgba(255, 255, 255, 0);
border-radius: 10%;
&:hover {
opacity: 1;
border-color: rgba(255, 255, 255, 0.6);
}
}
@@ -440,34 +467,25 @@ h2 {
&-link {
.truncate;
.palable;
.btn-opacity;
display: inline-block;
margin-right: 0.5em;
cursor: pointer;
text-decoration: underline;
opacity: 0.8;
opacity: @link-opacity-default;
max-width: 12em;
&:hover {
opacity: 1;
}
}
&-more {
.palable;
.btn-opacity;
padding: 0 2px;
text-transform: uppercase;
cursor: pointer;
opacity: 0.7;
font-size: 60%;
font-weight: bold;
display: inline-block;
position: relative;
top: -5px;
&:hover {
opacity: 1;
}
}
}
@@ -480,17 +498,15 @@ h2 {
}
.node-control-button {
.palable;
.btn-opacity;
padding: 6px;
margin-left: 2px;
font-size: 110%;
color: @white;
cursor: pointer;
opacity: 0.7;
border: 1px solid rgba(255, 255, 255, 0);
border-radius: 10%;
&:hover {
opacity: 1;
border-color: rgba(255, 255, 255, 0.6);
}
&-pending, &-pending:hover {
@@ -534,8 +550,7 @@ h2 {
margin-top: 48px;
text-align: center;
font-size: 48px;
color: @background-medium-color;
opacity: 0.7;
color: @text-tertiary-color;
}
&-section {
@@ -557,7 +572,7 @@ h2 {
text-align: center;
&-expand {
.palable;
.btn-opacity;
margin: 4px 16px 0;
border-top: 1px solid @border-light-color;
text-transform: uppercase;
@@ -565,15 +580,11 @@ h2 {
color: @text-secondary-color;
width: 100%;
cursor: pointer;
opacity: 0.8;
&:hover {
opacity: 1.0;
}
opacity: @link-opacity-default;
}
&-overflow {
.palable;
.btn-opacity;
display: flex;
flex-direction: row;
flex-wrap: wrap;
@@ -584,10 +595,6 @@ h2 {
position: relative;
padding-bottom: 16px;
&:hover {
opacity: 1;
}
&-expand {
text-transform: uppercase;
font-size: 70%;
@@ -728,14 +735,10 @@ h2 {
}
&-link {
.palable;
.btn-opacity;
text-decoration: underline;
cursor: pointer;
opacity: 0.8;
&:hover {
opacity: 1;
}
opacity: @link-opacity-default;
}
&-value, &-metric {
@@ -917,6 +920,7 @@ h2 {
cursor: pointer;
font-size: 90%;
margin-left: 0.5em;
opacity: @link-opacity-default;
}
}
}

View File

@@ -0,0 +1,20 @@
<!doctype html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>Weave Scope</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!--[if lt IE 10]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<div class="wrap">
<div id="app"></div>
</div>
<script src="vendors.js"></script>
<script src="contrast-app.js"></script>
</body>
</html>

View File

@@ -19,7 +19,7 @@ var app = express();
// Serve application file depending on environment
app.get(/(app|terminal-app|vendors).js/, function(req, res) {
app.get(/(app|contrast-app|terminal-app|vendors).js/, function(req, res) {
var filename = req.originalUrl;
if (process.env.NODE_ENV === 'production') {
res.sendFile(__dirname + '/build' + filename);

View File

@@ -28,6 +28,11 @@ module.exports = {
'webpack-dev-server/client?http://localhost:4041',
'webpack/hot/only-dev-server'
],
'contrast-app': [
'./app/scripts/contrast-main',
'webpack-dev-server/client?http://localhost:4041',
'webpack/hot/only-dev-server'
],
'terminal-app': [
'./app/scripts/terminal-main',
'webpack-dev-server/client?http://localhost:4041',

View File

@@ -18,6 +18,7 @@ module.exports = {
entry: {
app: './app/scripts/main',
'contrast-app': './app/scripts/contrast-main',
'terminal-app': './app/scripts/terminal-main',
vendors: ['classnames', 'd3', 'dagre', 'flux', 'immutable',
'lodash', 'page', 'react', 'react-dom', 'react-motion']