diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js index a16b5b9d3..a1b7ac27c 100644 --- a/client/app/scripts/actions/app-actions.js +++ b/client/app/scripts/actions/app-actions.js @@ -615,12 +615,6 @@ export function receiveNodes(nodes) { }; } -export function timeTravelStartTransition() { - return { - type: ActionTypes.TIME_TRAVEL_START_TRANSITION, - }; -} - export function jumpToTime(timestamp) { return (dispatch, getState) => { dispatch({ diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index eda6590c7..8a6ebd167 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -4,6 +4,9 @@ import classNames from 'classnames'; import { connect } from 'react-redux'; import { debounce } from 'lodash'; +import { ThemeProvider } from 'styled-components'; +import theme from 'weaveworks-ui-components/lib/theme'; + import Logo from './logo'; import Footer from './footer'; import Sidebar from './sidebar'; @@ -175,42 +178,44 @@ class App extends React.Component { const isIframe = window !== window.top; return ( -
- {showingDebugToolbar() && } + +
+ {showingDebugToolbar() && } - {showingHelp && } + {showingHelp && } - {showingTroubleshootingMenu && } + {showingTroubleshootingMenu && } - {showingDetails &&
} + {showingDetails &&
} -
- -
-
- {!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()} - - - - - -
+ + + + + + + + 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 ( -
- -
- UTC -
-
+ ); } } 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: