-
-
-
- {!isIframe &&
}
+
+
+
+
+ {!isIframe && }
+
+
+
+
+
-
-
-
-
+
+
+
+
+ {showingNetworkSelector && isGraphViewMode && }
+ {!isResourceViewMode && }
+ {!isResourceViewMode && }
+
+
+
+
+
-
-
-
-
- {showingNetworkSelector && isGraphViewMode && }
- {!isResourceViewMode && }
- {!isResourceViewMode && }
-
-
-
-
-
-
+
);
}
}
diff --git a/client/app/scripts/components/time-control.js b/client/app/scripts/components/time-control.js
index 8c231182d..62edd37ae 100644
--- a/client/app/scripts/components/time-control.js
+++ b/client/app/scripts/components/time-control.js
@@ -6,8 +6,6 @@ import { connect } from 'react-redux';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { pauseTimeAtNow, resumeTime, startTimeTravel } from '../actions/app-actions';
-import { TIMELINE_TICK_INTERVAL } from '../constants/timer';
-
const className = isSelected => (
classNames('time-control-action', { 'time-control-action-selected': isSelected })
@@ -24,8 +22,8 @@ class TimeControl extends React.Component {
}
componentDidMount() {
- // Force periodic for the paused info.
- this.timer = setInterval(() => { this.forceUpdate(); }, TIMELINE_TICK_INTERVAL);
+ // Force periodic updates every one second for the paused info.
+ this.timer = setInterval(() => { this.forceUpdate(); }, 1000);
}
componentWillUnmount() {
diff --git a/client/app/scripts/components/time-travel-timeline.js b/client/app/scripts/components/time-travel-component.js
similarity index 68%
rename from client/app/scripts/components/time-travel-timeline.js
rename to client/app/scripts/components/time-travel-component.js
index d859351ed..6c0d7c8a9 100644
--- a/client/app/scripts/components/time-travel-timeline.js
+++ b/client/app/scripts/components/time-travel-component.js
@@ -1,8 +1,7 @@
import React from 'react';
import moment from 'moment';
-import classNames from 'classnames';
+import styled from 'styled-components';
import { map, clamp, find, last, debounce } from 'lodash';
-import { connect } from 'react-redux';
import { drag } from 'd3-drag';
import { scaleUtc } from 'd3-scale';
import { event as d3Event, select } from 'd3-selection';
@@ -11,15 +10,12 @@ import { Motion } from 'react-motion';
import { zoomFactor } from '../utils/zoom-utils';
import { strongSpring } from '../utils/animation-utils';
import { linearGradientValue } from '../utils/math-utils';
-import { trackAnalyticsEvent } from '../utils/tracking-utils';
import {
nowInSecondsPrecision,
clampToNowInSecondsPrecision,
scaleDuration,
} from '../utils/time-utils';
-import { TIMELINE_TICK_INTERVAL, ZOOM_TRACK_DEBOUNCE_INTERVAL } from '../constants/timer';
-
const TICK_SETTINGS_PER_PERIOD = {
year: {
@@ -61,6 +57,11 @@ const TICK_SETTINGS_PER_PERIOD = {
},
};
+const ZOOM_TRACK_DEBOUNCE_INTERVAL = 5000;
+const TIMELINE_DEBOUNCE_INTERVAL = 500;
+const TIMELINE_TICK_INTERVAL = 1000;
+
+const TIMELINE_HEIGHT = '55px';
const MIN_DURATION_PER_PX = moment.duration(250, 'milliseconds');
const INIT_DURATION_PER_PX = moment.duration(1, 'minute');
const MAX_DURATION_PER_PX = moment.duration(3, 'days');
@@ -71,6 +72,117 @@ const TICKS_ROW_SPACING = 16;
const MAX_TICK_ROWS = 3;
+// From https://stackoverflow.com/a/18294634
+const FullyPannableCanvas = styled.svg`
+ width: 100%;
+ height: 100%;
+ cursor: move;
+ cursor: grab;
+ cursor: -moz-grab;
+ cursor: -webkit-grab;
+
+ ${props => props.panning && `
+ cursor: grabbing;
+ cursor: -moz-grabbing;
+ cursor: -webkit-grabbing;
+ `}
+`;
+
+const TimeTravelContainer = styled.div`
+ transition: all .15s ease-in-out;
+ position: relative;
+ margin-bottom: 15px;
+ overflow: hidden;
+ z-index: 2001;
+ height: 0;
+
+ ${props => props.visible && `
+ height: calc(${TIMELINE_HEIGHT} + 35px);
+ margin-bottom: 15px;
+ margin-top: -5px;
+ `}
+`;
+
+const TimelineContainer = styled.div`
+ align-items: center;
+ display: flex;
+ height: ${TIMELINE_HEIGHT};
+
+ &:before, &:after {
+ border: 1px solid ${props => props.theme.colors.white};
+ background-color: ${props => props.theme.colors.accent.orange};
+ content: '';
+ position: absolute;
+ display: block;
+ left: 50%;
+ border-top: 0;
+ border-bottom: 0;
+ margin-left: -1px;
+ width: 3px;
+ }
+
+ &:before {
+ top: 0;
+ height: ${TIMELINE_HEIGHT};
+ }
+
+ &:after {
+ top: ${TIMELINE_HEIGHT};
+ height: 9px;
+ opacity: 0.15;
+ }
+`;
+
+const Timeline = FullyPannableCanvas.extend`
+ background-color: rgba(255, 255, 255, 0.85);
+ box-shadow: inset 0 0 7px ${props => props.theme.colors.gray};
+ pointer-events: all;
+ margin: 0 7px;
+`;
+
+const DisabledRange = styled.rect`
+ fill: ${props => props.theme.colors.gray};
+ fill-opacity: 0.15;
+`;
+
+const TimestampLabel = styled.a`
+ margin-left: 2px;
+ padding: 3px;
+
+ &[disabled] {
+ color: ${props => props.theme.colors.gray};
+ cursor: inherit;
+ }
+`;
+
+const TimelinePanButton = styled.a`
+ pointer-events: all;
+ padding: 2px;
+`;
+
+const TimestampContainer = styled.div`
+ background-color: ${props => props.theme.colors.white};
+ border: 1px solid ${props => props.theme.colors.gray};
+ border-radius: 4px;
+ padding: 2px 8px;
+ pointer-events: all;
+ margin: 8px 0 25px 50%;
+ transform: translateX(-50%);
+ opacity: 0.8;
+ display: inline-block;
+`;
+
+const TimestampInput = styled.input`
+ background-color: transparent;
+ font-family: "Roboto", sans-serif;
+ text-align: center;
+ font-size: 1rem;
+ width: 165px;
+ border: 0;
+ outline: 0;
+`;
+
+
function getTimeScale({ focusedTimestamp, durationPerPixel }) {
const roundedTimestamp = moment(focusedTimestamp).utc().startOf('second');
const startDate = moment(roundedTimestamp).subtract(durationPerPixel);
@@ -85,8 +197,13 @@ function findOptimalDurationFit(durations, { durationPerPixel }) {
return find(durations, d => d >= minimalDuration);
}
+function getInputValue(timestamp) {
+ return {
+ inputValue: (timestamp ? moment(timestamp) : moment()).utc().format(),
+ };
+}
-class TimeTravelTimeline extends React.Component {
+export default class TimeTravelComponent extends React.Component {
constructor(props, context) {
super(props, context);
@@ -96,6 +213,7 @@ class TimeTravelTimeline extends React.Component {
durationPerPixel: INIT_DURATION_PER_PX,
boundingRect: { width: 0, height: 0 },
isPanning: false,
+ ...getInputValue(props.timestamp),
};
this.jumpRelativePixels = this.jumpRelativePixels.bind(this);
@@ -110,6 +228,15 @@ class TimeTravelTimeline extends React.Component {
this.saveSvgRef = this.saveSvgRef.bind(this);
this.debouncedTrackZoom = debounce(this.trackZoom.bind(this), ZOOM_TRACK_DEBOUNCE_INTERVAL);
+
+ this.handleInputChange = this.handleInputChange.bind(this);
+ this.handleTimelinePan = this.handleTimelinePan.bind(this);
+ this.handleTimelinePanEnd = this.handleTimelinePanEnd.bind(this);
+ this.handleInstantJump = this.handleInstantJump.bind(this);
+
+ this.instantUpdateTimestamp = this.instantUpdateTimestamp.bind(this);
+ this.debouncedUpdateTimestamp = debounce(
+ this.instantUpdateTimestamp.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
}
componentDidMount() {
@@ -131,23 +258,37 @@ class TimeTravelTimeline extends React.Component {
}
componentWillReceiveProps(nextProps) {
+ // Update the input value
+ this.setState(getInputValue(nextProps.timestamp));
// Don't update the focused timestamp if we're not paused (so the timeline is hidden).
- if (nextProps.pausedAt) {
- this.setState({ focusedTimestamp: nextProps.pausedAt });
+ if (nextProps.timestamp) {
+ this.setState({ focusedTimestamp: nextProps.timestamp });
}
// Always update the timeline dimension information.
this.setState({ boundingRect: this.svgRef.getBoundingClientRect() });
}
- saveSvgRef(ref) {
- this.svgRef = ref;
+ handleInputChange(ev) {
+ const timestamp = moment(ev.target.value);
+ this.setState({ inputValue: ev.target.value });
+
+ if (timestamp.isValid()) {
+ const clampedTimestamp = clampToNowInSecondsPrecision(timestamp);
+ this.instantUpdateTimestamp(clampedTimestamp, this.props.trackTimestampEdit);
+ }
}
- trackZoom() {
- const periods = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'];
- const duration = scaleDuration(this.state.durationPerPixel, MAX_TICK_SPACING_PX);
- const zoomedPeriod = find(periods, period => Math.floor(duration.get(period)) && period);
- trackAnalyticsEvent('scope.time.timeline.zoom', { zoomedPeriod });
+ handleTimelinePan(timestamp) {
+ this.setState(getInputValue(timestamp));
+ this.debouncedUpdateTimestamp(timestamp);
+ }
+
+ handleTimelinePanEnd(timestamp) {
+ this.instantUpdateTimestamp(timestamp, this.props.trackTimelinePan);
+ }
+
+ handleInstantJump(timestamp) {
+ this.instantUpdateTimestamp(timestamp, this.props.trackTimelineClick);
}
handlePanStart() {
@@ -155,7 +296,7 @@ class TimeTravelTimeline extends React.Component {
}
handlePanEnd() {
- this.props.onTimelinePanEnd(this.state.focusedTimestamp);
+ this.handleTimelinePanEnd(this.state.focusedTimestamp);
this.setState({ isPanning: false });
}
@@ -163,7 +304,7 @@ class TimeTravelTimeline extends React.Component {
const dragDuration = scaleDuration(this.state.durationPerPixel, -d3Event.dx);
const timestamp = moment(this.state.focusedTimestamp).add(dragDuration);
const focusedTimestamp = clampToNowInSecondsPrecision(timestamp);
- this.props.onTimelinePan(focusedTimestamp);
+ this.handleTimelinePan(focusedTimestamp);
this.setState({ focusedTimestamp });
}
@@ -177,9 +318,31 @@ class TimeTravelTimeline extends React.Component {
ev.preventDefault();
}
+ instantUpdateTimestamp(timestamp, callback) {
+ if (!timestamp.isSame(this.props.timestamp)) {
+ this.debouncedUpdateTimestamp.cancel();
+ this.setState(getInputValue(timestamp));
+ this.props.changeTimestamp(moment(timestamp));
+
+ // Used for tracking.
+ if (callback) callback();
+ }
+ }
+
+ saveSvgRef(ref) {
+ this.svgRef = ref;
+ }
+
+ trackZoom() {
+ const periods = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'];
+ const duration = scaleDuration(this.state.durationPerPixel, MAX_TICK_SPACING_PX);
+ const zoomedPeriod = find(periods, period => Math.floor(duration.get(period)) && period);
+ this.props.trackTimelineZoom(zoomedPeriod);
+ }
+
jumpTo(timestamp) {
const focusedTimestamp = clampToNowInSecondsPrecision(timestamp);
- this.props.onInstantJump(focusedTimestamp);
+ this.handleInstantJump(focusedTimestamp);
this.setState({ focusedTimestamp });
}
@@ -288,9 +451,9 @@ class TimeTravelTimeline extends React.Component {
{!isBehind &&
}
{!disabled &&
Jump to {timestamp.utc().format()}}
-
+
{timestamp.utc().format(periodFormat)}
-
+
);
@@ -323,11 +486,7 @@ class TimeTravelTimeline extends React.Component {
const { width, height } = this.state.boundingRect;
return (
-
+
);
}
@@ -371,35 +530,32 @@ class TimeTravelTimeline extends React.Component {
}
render() {
- const className = classNames({ panning: this.state.isPanning });
- const halfWidth = this.state.boundingRect.width / 2;
+ const { isPanning, boundingRect } = this.state;
+ const halfWidth = boundingRect.width / 2;
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Scroll to zoom, drag to pan
+ {this.renderAnimatedContent()}
+
+
+
+
+
+
+
+ UTC
+
+
);
}
}
-
-
-function mapStateToProps(state) {
- return {
- // Used only to trigger recalculations on window resize.
- viewportWidth: state.getIn(['viewport', 'width']),
- pausedAt: state.get('pausedAt'),
- };
-}
-
-export default connect(mapStateToProps)(TimeTravelTimeline);
diff --git a/client/app/scripts/components/time-travel.js b/client/app/scripts/components/time-travel.js
index 79528bb2a..d52b4b5ab 100644
--- a/client/app/scripts/components/time-travel.js
+++ b/client/app/scripts/components/time-travel.js
@@ -1,85 +1,24 @@
import React from 'react';
-import moment from 'moment';
-import classNames from 'classnames';
import { connect } from 'react-redux';
-import { debounce } from 'lodash';
-import TimeTravelTimeline from './time-travel-timeline';
+import TimeTravelComponent from './time-travel-component';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
-import { clampToNowInSecondsPrecision } from '../utils/time-utils';
-import {
- jumpToTime,
- resumeTime,
- timeTravelStartTransition,
-} from '../actions/app-actions';
+import { jumpToTime } from '../actions/app-actions';
-import { TIMELINE_DEBOUNCE_INTERVAL } from '../constants/timer';
-
-
-const getTimestampStates = (timestamp) => {
- timestamp = timestamp || moment();
- return {
- inputValue: moment(timestamp).utc().format(),
- };
-};
class TimeTravel extends React.Component {
constructor(props, context) {
super(props, context);
- this.state = getTimestampStates(props.pausedAt);
-
- this.handleInputChange = this.handleInputChange.bind(this);
- this.handleTimelinePan = this.handleTimelinePan.bind(this);
- this.handleTimelinePanEnd = this.handleTimelinePanEnd.bind(this);
- this.handleInstantJump = this.handleInstantJump.bind(this);
-
+ this.changeTimestamp = this.changeTimestamp.bind(this);
this.trackTimestampEdit = this.trackTimestampEdit.bind(this);
this.trackTimelineClick = this.trackTimelineClick.bind(this);
+ this.trackTimelineZoom = this.trackTimelineZoom.bind(this);
this.trackTimelinePan = this.trackTimelinePan.bind(this);
-
- this.instantUpdateTimestamp = this.instantUpdateTimestamp.bind(this);
- this.debouncedUpdateTimestamp = debounce(
- this.instantUpdateTimestamp.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
}
- componentWillReceiveProps(props) {
- this.setState(getTimestampStates(props.pausedAt));
- }
-
- handleInputChange(ev) {
- const timestamp = moment(ev.target.value);
- this.setState({ inputValue: ev.target.value });
-
- if (timestamp.isValid()) {
- const clampedTimestamp = clampToNowInSecondsPrecision(timestamp);
- this.instantUpdateTimestamp(clampedTimestamp, this.trackTimestampEdit);
- }
- }
-
- handleTimelinePan(timestamp) {
- this.setState(getTimestampStates(timestamp));
- this.debouncedUpdateTimestamp(timestamp);
- }
-
- handleTimelinePanEnd(timestamp) {
- this.instantUpdateTimestamp(timestamp, this.trackTimelinePan);
- }
-
- handleInstantJump(timestamp) {
- this.instantUpdateTimestamp(timestamp, this.trackTimelineClick);
- }
-
- instantUpdateTimestamp(timestamp, callback) {
- if (!timestamp.isSame(this.props.pausedAt)) {
- this.debouncedUpdateTimestamp.cancel();
- this.setState(getTimestampStates(timestamp));
- this.props.timeTravelStartTransition();
- this.props.jumpToTime(moment(timestamp));
-
- // Used for tracking.
- if (callback) callback();
- }
+ changeTimestamp(timestamp) {
+ this.props.jumpToTime(timestamp);
}
trackTimestampEdit() {
@@ -106,39 +45,45 @@ class TimeTravel extends React.Component {
});
}
+ trackTimelineZoom(zoomedPeriod) {
+ trackAnalyticsEvent('scope.time.timeline.zoom', {
+ layout: this.props.topologyViewMode,
+ topologyId: this.props.currentTopology.get('id'),
+ parentTopologyId: this.props.currentTopology.get('parentId'),
+ zoomedPeriod,
+ });
+ }
+
render() {
+ const { visible, timestamp, viewportWidth } = this.props;
+
return (
-
+
);
}
}
function mapStateToProps(state) {
return {
- showingTimeTravel: state.get('showingTimeTravel'),
+ visible: state.get('showingTimeTravel'),
topologyViewMode: state.get('topologyViewMode'),
currentTopology: state.get('currentTopology'),
- pausedAt: state.get('pausedAt'),
+ timestamp: state.get('pausedAt'),
+ // Used only to trigger recalculations on window resize.
+ viewportWidth: state.getIn(['viewport', 'width']),
};
}
export default connect(
mapStateToProps,
- {
- jumpToTime,
- resumeTime,
- timeTravelStartTransition,
- }
+ { jumpToTime },
)(TimeTravel);
diff --git a/client/app/scripts/constants/action-types.js b/client/app/scripts/constants/action-types.js
index 5238ad391..7ae350ae2 100644
--- a/client/app/scripts/constants/action-types.js
+++ b/client/app/scripts/constants/action-types.js
@@ -63,7 +63,6 @@ const ACTION_TYPES = [
'SHUTDOWN',
'SORT_ORDER_CHANGED',
'START_TIME_TRAVEL',
- 'TIME_TRAVEL_START_TRANSITION',
'TOGGLE_CONTRAST_MODE',
'TOGGLE_TROUBLESHOOTING_MENU',
'UNHOVER_METRIC',
diff --git a/client/app/scripts/constants/timer.js b/client/app/scripts/constants/timer.js
index 887f3df9b..81c6f8fbf 100644
--- a/client/app/scripts/constants/timer.js
+++ b/client/app/scripts/constants/timer.js
@@ -8,7 +8,3 @@ export const TABLE_ROW_FOCUS_DEBOUNCE_INTERVAL = 10;
export const VIEWPORT_RESIZE_DEBOUNCE_INTERVAL = 200;
export const ZOOM_CACHE_DEBOUNCE_INTERVAL = 500;
-export const ZOOM_TRACK_DEBOUNCE_INTERVAL = 10000;
-
-export const TIMELINE_DEBOUNCE_INTERVAL = 500;
-export const TIMELINE_TICK_INTERVAL = 1000;
diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js
index bf38e412a..fc558d118 100644
--- a/client/app/scripts/reducers/root.js
+++ b/client/app/scripts/reducers/root.js
@@ -379,13 +379,10 @@ export function rootReducer(state = initialState, action) {
}
case ActionTypes.JUMP_TO_TIME: {
+ state = state.set('timeTravelTransitioning', true);
return state.set('pausedAt', action.timestamp);
}
- case ActionTypes.TIME_TRAVEL_START_TRANSITION: {
- return state.set('timeTravelTransitioning', true);
- }
-
case ActionTypes.FINISH_TIME_TRAVEL_TRANSITION: {
state = state.set('timeTravelTransitioning', false);
return clearNodes(state);
diff --git a/client/app/styles/_base.scss b/client/app/styles/_base.scss
index 0834a3b81..25822eda3 100644
--- a/client/app/styles/_base.scss
+++ b/client/app/styles/_base.scss
@@ -51,24 +51,21 @@ a {
}
// From https://stackoverflow.com/a/18294634
-.grabbable {
- cursor: move; /* fallback if grab cursor is unsupported */
- cursor: grab;
- cursor: -moz-grab;
- cursor: -webkit-grab;
-}
-.grabbing {
- cursor: grabbing;
- cursor: -moz-grabbing;
- cursor: -webkit-grabbing;
-}
-
.fully-pannable {
width: 100%;
height: 100%;
- @extend .grabbable;
+ // Grabbable
+ cursor: move; /* fallback if grab cursor is unsupported */
+ cursor: grab;
+ cursor: -moz-grab;
+ cursor: -webkit-grab;
- &.panning { @extend .grabbing; }
+ &.panning {
+ // Grabbing
+ cursor: grabbing;
+ cursor: -moz-grabbing;
+ cursor: -webkit-grabbing;
+ }
}
.shadow-2 {
@@ -308,102 +305,6 @@ a {
}
}
-.time-travel {
- position: relative;
- margin-bottom: 15px;
- z-index: 2001;
-
- transition: all .15s $base-ease;
- overflow: hidden;
- height: 0;
-
- &.visible {
- height: $timeline-height + 35px;
- margin-bottom: 15px;
- margin-top: -5px;
- }
-
- .button {
- padding: 2px;
- pointer-events: all;
- }
-
- .time-travel-timeline {
- align-items: center;
- display: flex;
- height: $timeline-height;
-
- svg {
- @extend .fully-pannable;
- background-color: rgba(255, 255, 255, 0.85);
- box-shadow: inset 0 0 7px #aaa;
- pointer-events: all;
- margin: 0 7px;
-
- .available-range {
- fill: #888;
- fill-opacity: 0.1;
- }
-
- .timestamp-label {
- margin-left: 2px;
- padding: 3px;
-
- &[disabled] {
- color: #aaa;
- cursor: inherit;
- }
- }
- }
-
- &:before, &:after {
- content: '';
- position: absolute;
- display: block;
- left: 50%;
- border: 1px solid white;
- border-top: 0;
- border-bottom: 0;
- background-color: red;
- margin-left: -1px;
- width: 3px;
- }
-
- &:before {
- top: 0;
- height: $timeline-height;
- }
-
- &:after {
- top: $timeline-height;
- height: 9px;
- opacity: 0.15;
- }
- }
-
- &-timestamp {
- background-color: $background-lighter-color;
- border: 1px solid #ccc;
- border-radius: 4px;
- padding: 2px 8px;
- pointer-events: all;
- margin: 8px 0 25px 50%;
- transform: translateX(-50%);
- opacity: 0.8;
- display: inline-block;
-
- input {
- border: 0;
- background-color: transparent;
- font-size: 1rem;
- width: 165px;
- font-family: "Roboto", sans-serif;
- text-align: center;
- outline: 0;
- }
- }
-}
-
.zoomable-canvas svg {
@extend .fully-pannable;
}
diff --git a/client/package.json b/client/package.json
index aee1066ce..1bfce232b 100644
--- a/client/package.json
+++ b/client/package.json
@@ -43,7 +43,8 @@
"reqwest": "2.0.5",
"reselect": "3.0.0",
"reselect-map": "1.0.1",
- "weaveworks-ui-components": "git+https://github.com/weaveworks/ui-components.git#v0.1.28",
+ "styled-components": "^2.2.1",
+ "weaveworks-ui-components": "git+https://github.com/weaveworks/ui-components.git#v0.1.45",
"whatwg-fetch": "2.0.3",
"xterm": "2.5.0"
},
diff --git a/client/yarn.lock b/client/yarn.lock
index 4f8e833d6..9074d831d 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -710,6 +710,13 @@ babel-plugin-transform-object-rest-spread@6.23.0:
babel-plugin-syntax-object-rest-spread "^6.8.0"
babel-runtime "^6.22.0"
+babel-plugin-transform-object-rest-spread@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
+ dependencies:
+ babel-plugin-syntax-object-rest-spread "^6.8.0"
+ babel-runtime "^6.26.0"
+
babel-plugin-transform-react-display-name@^6.23.0, babel-plugin-transform-react-display-name@^6.3.13:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.23.0.tgz#4398910c358441dc4cef18787264d0412ed36b37"
@@ -871,6 +878,13 @@ babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
+babel-runtime@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.11.0"
+
babel-template@^6.16.0, babel-template@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
@@ -1107,6 +1121,13 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
+buffer@^5.0.3:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24"
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@@ -1543,6 +1564,10 @@ css-animation@^1.3.0:
dependencies:
component-classes "^1.2.5"
+css-color-keywords@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -1590,6 +1615,14 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
+css-to-react-native@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.4.tgz#cf4cc407558b3474d4ba8be1a2cd3b6ce713101b"
+ dependencies:
+ css-color-keywords "^1.0.0"
+ fbjs "^0.8.5"
+ postcss-value-parser "^3.3.0"
+
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
@@ -2425,6 +2458,18 @@ fbjs@^0.8.4, fbjs@^0.8.9:
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
+fbjs@^0.8.5:
+ version "0.8.16"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.9"
+
figures@^1.3.5:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
@@ -2851,7 +2896,7 @@ hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
-hoist-non-react-statics@^1.0.3:
+hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
@@ -3156,6 +3201,10 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+is-function@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
+
is-generator-function@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.6.tgz#9e71653cd15fff341c79c4151460a131d31e9fc4"
@@ -4200,9 +4249,9 @@ node-pre-gyp@^0.6.29:
tar "^2.2.1"
tar-pack "^3.4.0"
-node-sass@3.13.0:
- version "3.13.0"
- resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-3.13.0.tgz#d08b95bdebf40941571bd2c16a9334b980f8924f"
+node-sass@4.5.2:
+ version "4.5.2"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.2.tgz#4012fa2bd129b1d6365117e88d9da0500d99da64"
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@@ -4213,17 +4262,19 @@ node-sass@3.13.0:
in-publish "^2.0.0"
lodash.assign "^4.2.0"
lodash.clonedeep "^4.3.2"
+ lodash.mergewith "^4.6.0"
meow "^3.7.0"
mkdirp "^0.5.1"
nan "^2.3.2"
node-gyp "^3.3.1"
npmlog "^4.0.0"
- request "^2.61.0"
+ request "^2.79.0"
sass-graph "^2.1.1"
+ stdout-stream "^1.4.0"
-node-sass@4.5.2:
- version "4.5.2"
- resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.2.tgz#4012fa2bd129b1d6365117e88d9da0500d99da64"
+node-sass@4.5.3:
+ version "4.5.3"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568"
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@@ -4595,6 +4646,10 @@ pluralize@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
+polished@^1.7.0:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/polished/-/polished-1.8.1.tgz#e50b9f789d37d98da64912f1be2bf759d8bfae6c"
+
postcss-calc@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
@@ -5288,6 +5343,10 @@ regenerator-runtime@^0.10.0:
version "0.10.3"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e"
+regenerator-runtime@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
+
regenerator-transform@0.9.11:
version "0.9.11"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
@@ -5357,7 +5416,7 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request@2, request@^2.53.0, request@^2.61.0, request@^2.65.0, request@^2.72.0, request@^2.79.0, request@^2.81.0:
+request@2, request@^2.53.0, request@^2.65.0, request@^2.72.0, request@^2.79.0, request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies:
@@ -5818,6 +5877,24 @@ style-loader@0.17.0:
dependencies:
loader-utils "^1.0.2"
+styled-components@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.2.1.tgz#f4835f1001c37bcc301ac3865b5d93466de4dd5b"
+ dependencies:
+ buffer "^5.0.3"
+ css-to-react-native "^2.0.3"
+ fbjs "^0.8.9"
+ hoist-non-react-statics "^1.2.0"
+ is-function "^1.0.1"
+ is-plain-object "^2.0.1"
+ prop-types "^15.5.4"
+ stylis "^3.2.1"
+ supports-color "^3.2.3"
+
+stylis@^3.2.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.3.2.tgz#95ef285836e98243f8b8f64a9a72706ea6c893ea"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -6171,16 +6248,18 @@ wd@^0.4.0:
underscore.string "~3.0.3"
vargs "~0.1.0"
-"weaveworks-ui-components@git+https://github.com/weaveworks/ui-components.git#v0.1.28":
- version "0.1.28"
- resolved "git+https://github.com/weaveworks/ui-components.git#164a0d2770ee5c2318b2f1ab1948fb6046059909"
+"weaveworks-ui-components@git+https://github.com/weaveworks/ui-components.git#v0.1.45":
+ version "0.1.45"
+ resolved "git+https://github.com/weaveworks/ui-components.git#9b700e6231599d0f7b353d8e2bb5f466394dc081"
dependencies:
babel-cli "^6.18.0"
babel-plugin-transform-export-extensions "6.8.0"
+ babel-plugin-transform-object-rest-spread "^6.26.0"
babel-preset-es2015 "6.18.0"
babel-preset-react "6.16.0"
classnames "^2.2.5"
- node-sass "3.13.0"
+ node-sass "4.5.3"
+ polished "^1.7.0"
prop-types "^15.5.8"
webidl-conversions@^3.0.0: