diff --git a/CHANGELOG.md b/CHANGELOG.md index 01346b930f0..5999ee2f077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md). - Added URLs to the tabs in the dashboard. [#3183](https://github.com/scalableminds/webknossos/pull/3183) - Improved security by enabling http security headers. [#3084](https://github.com/scalableminds/webknossos/pull/3084) - Added the possibility to write markdown in the annotation description. [#3081](https://github.com/scalableminds/webknossos/pull/3081) +- Added customizable layouting to the tracing view. [#3070](https://github.com/scalableminds/webknossos/pull/3070) - Added the brush size to the settings on the left in volume tracing. The size can now also be adjusted by using only the keyboard. [#3126](https://github.com/scalableminds/webknossos/pull/3126) - Added a user documentation for webKnossos [#3011](https://github.com/scalableminds/webknossos/pull/3011) - Tree groups can now be activated. This allows to rename a tree group analogous to renaming a tree. Also, toggling the visibility of a tree group can now be done by using the shortcuts "1" and "2". [#3066](https://github.com/scalableminds/webknossos/pull/3066) diff --git a/app/assets/javascripts/admin/help/keyboardshortcut_view.js b/app/assets/javascripts/admin/help/keyboardshortcut_view.js index 5e6cbccb32b..effc7b05d98 100644 --- a/app/assets/javascripts/admin/help/keyboardshortcut_view.js +++ b/app/assets/javascripts/admin/help/keyboardshortcut_view.js @@ -48,7 +48,7 @@ const KeyboardShortcutView = () => { }, { keybinding: "K, L", - action: "Scale Up/down Viewport Size", + action: "Increase/Decrease Size of Layout", }, { keybinding: "B, J", diff --git a/app/assets/javascripts/libs/utils.js b/app/assets/javascripts/libs/utils.js index 91180ee4683..d6a9e0c943d 100644 --- a/app/assets/javascripts/libs/utils.js +++ b/app/assets/javascripts/libs/utils.js @@ -457,6 +457,18 @@ export function sortArray8(arr: Array): void { swap(arr, 3, 4); } +export function waitForSelector(selector: string): Promise<*> { + const tryToResolve = resolve => { + const el = document.querySelector(selector); + if (el) { + resolve(el); + } else { + window.requestAnimationFrame(() => tryToResolve(resolve)); + } + }; + return new Promise(tryToResolve); +} + export function convertDecToBase256(num: number): Vector4 { const divMod = n => [Math.floor(n / 256), n % 256]; let tmp = num; diff --git a/app/assets/javascripts/messages.js b/app/assets/javascripts/messages.js index 6fb1527fbd0..fc27408ea02 100644 --- a/app/assets/javascripts/messages.js +++ b/app/assets/javascripts/messages.js @@ -52,6 +52,8 @@ In order to restore the current window, a reload is necessary.`, "tracing.tree_viewer_no_cyclic_trees": "Cyclic trees are not supported by the abstract tree viewer.", "tracing.changed_move_value": "The move value was changed to: ", + "tracing.no_viewport_scaling_setting": + "Scaling the viewports via k/l is not supported anymore. Instead you can increase the viewport size by dragging the borders between the panes. You can also rearrange the panes by dragging the tabs.", "tracing.natural_sorting": "Correctly sort numbers in text (word2 < word10). This may be slow!", "tracing.cant_create_node_due_to_active_group": "You cannot create nodes, since no tree is active.", diff --git a/app/assets/javascripts/oxalis/api/api_latest.js b/app/assets/javascripts/oxalis/api/api_latest.js index 061d769fff9..2c684d77bdc 100644 --- a/app/assets/javascripts/oxalis/api/api_latest.js +++ b/app/assets/javascripts/oxalis/api/api_latest.js @@ -804,7 +804,7 @@ class UserApi { - moveValue3d - rotateValue - crosshairSize - - scaleValue + - layoutScaleValue - mouseRotateValue - clippingDistance - clippingDistanceArbitrary diff --git a/app/assets/javascripts/oxalis/api/api_v2.js b/app/assets/javascripts/oxalis/api/api_v2.js index 9680f65b230..5549b3796b0 100644 --- a/app/assets/javascripts/oxalis/api/api_v2.js +++ b/app/assets/javascripts/oxalis/api/api_v2.js @@ -695,7 +695,7 @@ class UserApi { - moveValue3d - rotateValue - crosshairSize - - scaleValue + - layoutScaleValue - mouseRotateValue - clippingDistance - clippingDistanceArbitrary diff --git a/app/assets/javascripts/oxalis/constants.js b/app/assets/javascripts/oxalis/constants.js index 4b640d223d0..a6296d1929d 100644 --- a/app/assets/javascripts/oxalis/constants.js +++ b/app/assets/javascripts/oxalis/constants.js @@ -17,6 +17,12 @@ export type BoundingBoxType = { min: Vector3, max: Vector3, }; +export type Rect = { + top: number, + left: number, + width: number, + height: number, +}; export const AnnotationContentTypes = ["skeleton", "volume", "hybrid"]; export const Vector2Indicies = [0, 1]; @@ -31,8 +37,10 @@ export const OrthoViews = { PLANE_XZ: "PLANE_XZ", TDView: "TDView", }; +export const ArbitraryViewport = "arbitraryViewport"; export type OrthoViewType = $Keys; export type OrthoViewMapType = { [key: OrthoViewType]: T }; +export type ViewportType = OrthoViewType | typeof ArbitraryViewport; export const OrthoViewValues: Array = Object.keys(OrthoViews); export const OrthoViewIndices = { PLANE_XY: OrthoViewValues.indexOf("PLANE_XY"), @@ -133,8 +141,8 @@ const Constants = { FPS: 50, - MIN_SCALE: 0.5, - MAX_SCALE: 20, + MIN_LAYOUT_SCALE: 1, + MAX_LAYOUT_SCALE: 5, MIN_BRUSH_SIZE: 5, MAX_BRUSH_SIZE: 5000, diff --git a/app/assets/javascripts/oxalis/controller.js b/app/assets/javascripts/oxalis/controller.js index 0af8e4cbba5..6aa6f936ee6 100644 --- a/app/assets/javascripts/oxalis/controller.js +++ b/app/assets/javascripts/oxalis/controller.js @@ -181,12 +181,11 @@ class Controller extends React.PureComponent { )); } - scaleTrianglesPlane(delta: number): void { - let scale = Store.getState().userConfiguration.scale + delta; - scale = Math.min(constants.MAX_SCALE, scale); - scale = Math.max(constants.MIN_SCALE, scale); - - Store.dispatch(updateUserSettingAction("scale", scale)); + setLayoutScale(multiplier: number): void { + let scale = Store.getState().userConfiguration.layoutScaleValue + 0.05 * multiplier; + scale = Math.min(constants.MAX_LAYOUT_SCALE, scale); + scale = Math.max(constants.MIN_LAYOUT_SCALE, scale); + Store.dispatch(updateUserSettingAction("layoutScaleValue", scale)); } isWebGlSupported() { @@ -276,16 +275,8 @@ class Controller extends React.PureComponent { this.keyboardNoLoop = new InputKeyboardNoLoop(keyboardControls); this.keyboard = new InputKeyboard({ - // Scale planes - l: timeFactor => { - const scaleValue = Store.getState().userConfiguration.scaleValue; - this.scaleTrianglesPlane(-scaleValue * timeFactor); - }, - - k: timeFactor => { - const scaleValue = Store.getState().userConfiguration.scaleValue; - this.scaleTrianglesPlane(scaleValue * timeFactor); - }, + l: () => this.setLayoutScale(-1), + k: () => this.setLayoutScale(1), }); } diff --git a/app/assets/javascripts/oxalis/controller/combinations/skeletontracing_plane_controller.js b/app/assets/javascripts/oxalis/controller/combinations/skeletontracing_plane_controller.js index 766843f92f4..cdb2fcd912f 100644 --- a/app/assets/javascripts/oxalis/controller/combinations/skeletontracing_plane_controller.js +++ b/app/assets/javascripts/oxalis/controller/combinations/skeletontracing_plane_controller.js @@ -35,6 +35,7 @@ import { } from "oxalis/model/accessors/skeletontracing_accessor"; import type { Point2, Vector3, OrthoViewType, OrthoViewMapType } from "oxalis/constants"; import api from "oxalis/api/internal_api"; +import { getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor"; import { V3 } from "libs/mjs"; const OrthoViewToNumber: OrthoViewMapType = { @@ -131,12 +132,16 @@ function onClick( const pickingScene = new THREE.Scene(); pickingScene.add(pickingNode); + let { width, height } = getInputCatcherRect(plane); + width = Math.round(width); + height = Math.round(height); + const buffer = planeView.renderOrthoViewToTexture(plane, pickingScene); // Beware of the fact that new browsers yield float numbers for the mouse position const [x, y] = [Math.round(position.x), Math.round(position.y)]; // compute the index of the pixel under the cursor, // while inverting along the y-axis, because OpenGL has its origin bottom-left :/ - const index = (x + (planeView.curWidth - y) * planeView.curWidth) * 4; + const index = (x + (width - y) * height) * 4; // the nodeId can be reconstructed by interpreting the RGB values of the pixel as a base-255 number const nodeId = buffer.subarray(index, index + 3).reduce((a, b) => a * 255 + b, 0); SceneController.skeleton.stopPicking(); diff --git a/app/assets/javascripts/oxalis/controller/combinations/volumetracing_plane_controller.js b/app/assets/javascripts/oxalis/controller/combinations/volumetracing_plane_controller.js index b9848573458..b1621e5fba9 100644 --- a/app/assets/javascripts/oxalis/controller/combinations/volumetracing_plane_controller.js +++ b/app/assets/javascripts/oxalis/controller/combinations/volumetracing_plane_controller.js @@ -7,6 +7,7 @@ import _ from "lodash"; import Store from "oxalis/store"; import * as Utils from "libs/utils"; import { OrthoViews, VolumeToolEnum, ContourModeEnum } from "oxalis/constants"; +import { getViewportScale } from "oxalis/model/accessors/view_mode_accessor"; import { calculateGlobalPos } from "oxalis/controller/viewmodes/plane_controller"; import Model from "oxalis/model"; import { getPosition, getRequestLogZoomStep } from "oxalis/model/accessors/flycam_accessor"; @@ -33,7 +34,7 @@ import type { OrthoViewType, Point2 } from "oxalis/constants"; const simulateTracing = async (): Promise => { Store.dispatch(setToolAction(VolumeToolEnum.TRACE)); - const controls = getPlaneMouseControls(); + const controls = getPlaneMouseControls(OrthoViews.PLANE_XY); let pos = (x, y) => ({ x, y }); controls.leftMouseDown(pos(100, 100), OrthoViews.PLANE_XY, ({}: any)); @@ -55,7 +56,7 @@ const simulateTracing = async (): Promise => { await simulateTracing(); }; -export function getPlaneMouseControls(): * { +export function getPlaneMouseControls(planeId: OrthoViewType): * { return { leftDownMove: (delta: Point2, pos: Point2) => { const { tracing } = Store.getState(); @@ -65,7 +66,7 @@ export function getPlaneMouseControls(): * { if (tool === VolumeToolEnum.MOVE) { const state = Store.getState(); - const viewportScale = state.userConfiguration.scale; + const viewportScale = getViewportScale(planeId); const { activeViewport } = state.viewModeData.plane; const v = [(delta.x * -1) / viewportScale, (delta.y * -1) / viewportScale, 0]; Store.dispatch(movePlaneFlycamOrthoAction(v, activeViewport, true)); diff --git a/app/assets/javascripts/oxalis/controller/renderer.js b/app/assets/javascripts/oxalis/controller/renderer.js index d355ffcb09e..16b1fbe28a1 100644 --- a/app/assets/javascripts/oxalis/controller/renderer.js +++ b/app/assets/javascripts/oxalis/controller/renderer.js @@ -10,6 +10,8 @@ function getRenderer() { typeof document !== "undefined" && document.getElementById ? new THREE.WebGLRenderer({ canvas: document.getElementById("render-canvas"), + // This prevents flickering when rendering to a buffer instead of the canvas + preserveDrawingBuffer: true, antialias: true, }) : {}; diff --git a/app/assets/javascripts/oxalis/controller/viewmodes/arbitrary_controller.js b/app/assets/javascripts/oxalis/controller/viewmodes/arbitrary_controller.js index 9c346c4d40e..06161748f83 100644 --- a/app/assets/javascripts/oxalis/controller/viewmodes/arbitrary_controller.js +++ b/app/assets/javascripts/oxalis/controller/viewmodes/arbitrary_controller.js @@ -13,6 +13,7 @@ import * as Utils from "libs/utils"; import Toast from "libs/toast"; import type { ModeType, Point2 } from "oxalis/constants"; import Store from "oxalis/store"; +import { getViewportScale } from "oxalis/model/accessors/view_mode_accessor"; import Model from "oxalis/model"; import { updateUserSettingAction, @@ -32,7 +33,7 @@ import ArbitraryPlane from "oxalis/geometries/arbitrary_plane"; import Crosshair from "oxalis/geometries/crosshair"; import app from "app"; import ArbitraryView from "oxalis/view/arbitrary_view"; -import constants from "oxalis/constants"; +import constants, { ArbitraryViewport } from "oxalis/constants"; import type { Matrix4x4 } from "libs/mjs"; import { yawFlycamAction, @@ -52,7 +53,7 @@ import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; import SceneController from "oxalis/controller/scene_controller"; import api from "oxalis/api/internal_api"; -const CANVAS_SELECTOR = "#render-canvas"; +const arbitraryViewportSelector = "#inputcatcher_arbitraryViewport"; type Props = { onRender: () => void, @@ -92,33 +93,33 @@ class ArbitraryController extends React.PureComponent { } initMouse(): void { - this.input.mouse = new InputMouse(CANVAS_SELECTOR, { - leftDownMove: (delta: Point2) => { - if (this.props.viewMode === constants.MODE_ARBITRARY) { - Store.dispatch( - yawFlycamAction(delta.x * Store.getState().userConfiguration.mouseRotateValue, true), - ); - Store.dispatch( - pitchFlycamAction( - delta.y * -1 * Store.getState().userConfiguration.mouseRotateValue, - true, - ), - ); - } else if (this.props.viewMode === constants.MODE_ARBITRARY_PLANE) { - const f = - Store.getState().flycam.zoomStep / - (this.arbitraryView.width / constants.VIEWPORT_WIDTH); - Store.dispatch(moveFlycamAction([delta.x * f, delta.y * f, 0])); - } - }, - scroll: this.scroll, - pinch: (delta: number) => { - if (delta < 0) { - Store.dispatch(zoomOutAction()); - } else { - Store.dispatch(zoomInAction()); - } - }, + Utils.waitForSelector(arbitraryViewportSelector).then(() => { + this.input.mouse = new InputMouse(arbitraryViewportSelector, { + leftDownMove: (delta: Point2) => { + if (this.props.viewMode === constants.MODE_ARBITRARY) { + Store.dispatch( + yawFlycamAction(delta.x * Store.getState().userConfiguration.mouseRotateValue, true), + ); + Store.dispatch( + pitchFlycamAction( + delta.y * -1 * Store.getState().userConfiguration.mouseRotateValue, + true, + ), + ); + } else if (this.props.viewMode === constants.MODE_ARBITRARY_PLANE) { + const f = Store.getState().flycam.zoomStep / getViewportScale(ArbitraryViewport); + Store.dispatch(moveFlycamAction([delta.x * f, delta.y * f, 0])); + } + }, + scroll: this.scroll, + pinch: (delta: number) => { + if (delta < 0) { + Store.dispatch(zoomOutAction()); + } else { + Store.dispatch(zoomInAction()); + } + }, + }); }); } @@ -290,7 +291,7 @@ class ArbitraryController extends React.PureComponent { this.setClippingDistance(clippingDistanceArbitrary); this.crosshair.setScale(crosshairSize); this.crosshair.setVisibility(displayCrosshair); - this.arbitraryView.resize(); + this.arbitraryView.resizeThrottled(); }, ), listenToStoreProperty( diff --git a/app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.js b/app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.js index 04adbaedd28..ababa4bb05a 100644 --- a/app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.js +++ b/app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.js @@ -5,6 +5,7 @@ import * as React from "react"; import { connect } from "react-redux"; +import { getViewportScale, getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor"; import BackboneEvents from "backbone-events-standalone"; import _ from "lodash"; import * as Utils from "libs/utils"; @@ -95,6 +96,7 @@ class PlaneController extends React.PureComponent { keyboard?: InputKeyboard, keyboardNoLoop?: InputKeyboardNoLoop, keyboardLoopDelayed?: InputKeyboard, + keyboardNoLoop?: InputKeyboardNoLoop, }; storePropertyUnsubscribers: Array; isStarted: boolean; @@ -132,22 +134,16 @@ class PlaneController extends React.PureComponent { } initMouse(): void { - for (const id of OrthoViewValues) { - if (id !== OrthoViews.TDView) { - const inputcatcherSelector = `#inputcatcher_${OrthoViews[id]}`; + OrthoViewValues.forEach(id => { + const inputcatcherSelector = `#inputcatcher_${OrthoViews[id]}`; + Utils.waitForSelector(inputcatcherSelector).then(() => { this.input.mouseControllers[id] = new InputMouse( inputcatcherSelector, - this.getPlaneMouseControls(id), - id, - ); - } else { - this.input.mouseControllers[id] = new InputMouse( - "#inputcatcher_TDView", - this.getTDViewMouseControls(), + id !== OrthoViews.TDView ? this.getPlaneMouseControls(id) : this.getTDViewMouseControls(), id, ); - } - } + }); + }); } getTDViewMouseControls(): Object { @@ -176,7 +172,7 @@ class PlaneController extends React.PureComponent { getPlaneMouseControls(planeId: OrthoViewType): Object { const baseControls = { leftDownMove: (delta: Point2) => { - const viewportScale = Store.getState().userConfiguration.scale; + const viewportScale = getViewportScale(planeId); return this.movePlane([(delta.x * -1) / viewportScale, (delta.y * -1) / viewportScale, 0]); }, @@ -198,7 +194,7 @@ class PlaneController extends React.PureComponent { const { leftClick: volumeLeftClick, ...volumeControls } = this.props.tracing.volume != null - ? volumeController.getPlaneMouseControls() + ? volumeController.getPlaneMouseControls(planeId) : emptyDefaultHandler; ensureNonConflictingHandlers(skeletonControls, volumeControls); @@ -250,23 +246,24 @@ class PlaneController extends React.PureComponent { } initTrackballControls(): void { - const view = document.getElementById("inputcatcher_TDView"); - const pos = voxelToNm(this.props.scale, getPosition(this.props.flycam)); - const tdCamera = this.planeView.getCameras()[OrthoViews.TDView]; - this.controls = new TrackballControls(tdCamera, view, new THREE.Vector3(...pos), () => { - // write threeJS camera into store - Store.dispatch(setTDCameraAction(threeCameraToCameraData(tdCamera))); + Utils.waitForSelector("#inputcatcher_TDView").then(view => { + const pos = voxelToNm(this.props.scale, getPosition(this.props.flycam)); + const tdCamera = this.planeView.getCameras()[OrthoViews.TDView]; + this.controls = new TrackballControls(tdCamera, view, new THREE.Vector3(...pos), () => { + // write threeJS camera into store + Store.dispatch(setTDCameraAction(threeCameraToCameraData(tdCamera))); + }); + + this.controls.noZoom = true; + this.controls.noPan = true; + this.controls.staticMoving = true; + + this.controls.target.set(...pos); + + // This is necessary, since we instantiated this.controls now. This should be removed + // when the workaround with requestAnimationFrame(initInputHandlers) is removed. + this.forceUpdate(); }); - - this.controls.noZoom = true; - this.controls.noPan = true; - this.controls.staticMoving = true; - - this.controls.target.set(...pos); - - // This is necessary, since we instantiated this.controls now. This should be removed - // when the workaround with requestAnimationFrame(initInputHandlers) is removed. - this.forceUpdate(); } initKeyboard(): void { @@ -405,15 +402,13 @@ class PlaneController extends React.PureComponent { // Workaround: defer mouse initialization to make sure DOM elements have // actually been rendered by React (InputCatchers Component) // DOM Elements get deleted when switching between ortho and arbitrary mode - const initInputHandlers = () => { - if (!document.getElementById("inputcatcher_TDView")) { - window.requestAnimationFrame(initInputHandlers); - } else if (this.isStarted) { + + Utils.waitForSelector("#inputcatcher_TDView").then(() => { + if (this.isStarted) { this.initTrackballControls(); this.initMouse(); } - }; - initInputHandlers(); + }); } stop(): void { @@ -499,12 +494,12 @@ class PlaneController extends React.PureComponent { if (zoomToMouse) { zoomToPosition = this.input.mouseControllers[OrthoViews.TDView].position; } - Store.dispatch(zoomTDViewAction(value, zoomToPosition, this.planeView.curWidth)); + const { width } = getInputCatcherRect(OrthoViews.TDView); + Store.dispatch(zoomTDViewAction(value, zoomToPosition, width)); } moveTDView(delta: Point2): void { - const state = Store.getState(); - const { scale } = state.userConfiguration; + const scale = getViewportScale(OrthoViews.TDView); Store.dispatch(moveTDViewXAction((delta.x / scale) * -1)); Store.dispatch(moveTDViewYAction((delta.y / scale) * -1)); } @@ -658,13 +653,13 @@ export function calculateGlobalPos(clickPos: Point2): Vector3 { const { activeViewport } = state.viewModeData.plane; const curGlobalPos = getPosition(state.flycam); const zoomFactor = getPlaneScalingFactor(state.flycam); - const viewportScale = state.userConfiguration.scale; + const actualWidth = getInputCatcherRect(activeViewport).width; + const viewportScale = actualWidth / constants.VIEWPORT_WIDTH; const planeRatio = getBaseVoxelFactors(state.dataset.dataSource.scale); - const diffX = - (((constants.VIEWPORT_WIDTH * viewportScale) / 2 - clickPos.x) / viewportScale) * zoomFactor; - const diffY = - (((constants.VIEWPORT_WIDTH * viewportScale) / 2 - clickPos.y) / viewportScale) * zoomFactor; + const center = (constants.VIEWPORT_WIDTH * viewportScale) / 2; + const diffX = ((center - clickPos.x) / viewportScale) * zoomFactor; + const diffY = ((center - clickPos.y) / viewportScale) * zoomFactor; switch (activeViewport) { case OrthoViews.PLANE_XY: diff --git a/app/assets/javascripts/oxalis/geometries/materials/node_shader.js b/app/assets/javascripts/oxalis/geometries/materials/node_shader.js index 6933c79ece0..2887e0e9b95 100644 --- a/app/assets/javascripts/oxalis/geometries/materials/node_shader.js +++ b/app/assets/javascripts/oxalis/geometries/materials/node_shader.js @@ -48,7 +48,7 @@ class NodeShader { }, viewportScale: { type: "f", - value: state.userConfiguration.scale, + value: 1, }, overrideNodeRadius: { type: "i", diff --git a/app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.js b/app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.js index a8e97b8340f..6c1bd7a3f79 100644 --- a/app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.js +++ b/app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.js @@ -8,6 +8,7 @@ import * as THREE from "three"; import * as Utils from "libs/utils"; import Model from "oxalis/model"; import Store from "oxalis/store"; +import { getViewportScale } from "oxalis/model/accessors/view_mode_accessor"; import AbstractPlaneMaterialFactory, { sanitizeName, } from "oxalis/geometries/materials/abstract_plane_material_factory"; @@ -301,7 +302,7 @@ class PlaneMaterialFactory extends AbstractPlaneMaterialFactory { this.storePropertyUnsubscribers.push( listenToStoreProperty( - storeState => getPlaneScalingFactor(storeState.flycam) / storeState.userConfiguration.scale, + storeState => getPlaneScalingFactor(storeState.flycam) / getViewportScale(this.planeID), pixelToVoxelFactor => { this.uniforms.pixelToVoxelFactor.value = pixelToVoxelFactor; }, diff --git a/app/assets/javascripts/oxalis/geometries/skeleton.js b/app/assets/javascripts/oxalis/geometries/skeleton.js index 0df7c964417..63f4b773c6d 100644 --- a/app/assets/javascripts/oxalis/geometries/skeleton.js +++ b/app/assets/javascripts/oxalis/geometries/skeleton.js @@ -372,7 +372,7 @@ class Skeleton { } // Uniforms - const { particleSize, scale, overrideNodeRadius } = state.userConfiguration; + const { particleSize, overrideNodeRadius } = state.userConfiguration; let activeNodeId = skeletonTracing.activeNodeId; activeNodeId = activeNodeId == null ? -1 : activeNodeId; let activeTreeId = skeletonTracing.activeTreeId; @@ -382,7 +382,6 @@ class Skeleton { nodeUniforms.planeZoomFactor.value = getPlaneScalingFactor(state.flycam); nodeUniforms.overrideParticleSize.value = particleSize; nodeUniforms.overrideNodeRadius.value = overrideNodeRadius; - nodeUniforms.viewportScale.value = scale; nodeUniforms.activeTreeId.value = activeTreeId; nodeUniforms.activeNodeId.value = activeNodeId; diff --git a/app/assets/javascripts/oxalis/model/accessors/view_mode_accessor.js b/app/assets/javascripts/oxalis/model/accessors/view_mode_accessor.js index 2cc3a5e52ac..246987f1c7b 100644 --- a/app/assets/javascripts/oxalis/model/accessors/view_mode_accessor.js +++ b/app/assets/javascripts/oxalis/model/accessors/view_mode_accessor.js @@ -1,6 +1,8 @@ // @flow import Store from "oxalis/store"; +import type { Rect, ViewportType } from "oxalis/constants"; +import constants, { ArbitraryViewport } from "oxalis/constants"; export function getTDViewportSize(): number { // the viewport is always quadratic @@ -8,4 +10,18 @@ export function getTDViewportSize(): number { return camera.right - camera.left; } +export function getInputCatcherRect(viewport: ViewportType): Rect { + if (viewport === ArbitraryViewport) { + return Store.getState().viewModeData.arbitrary.inputCatcherRect; + } else { + // $FlowFixMe Flow does not understand that viewport cannot be ArbitraryViewport at this point + return Store.getState().viewModeData.plane.inputCatcherRects[viewport]; + } +} + +export function getViewportScale(viewport: ViewportType): number { + const { width } = getInputCatcherRect(viewport); + return width / constants.VIEWPORT_WIDTH; +} + export default {}; diff --git a/app/assets/javascripts/oxalis/model/actions/view_mode_actions.js b/app/assets/javascripts/oxalis/model/actions/view_mode_actions.js index 68a8c87ef86..554262d8681 100644 --- a/app/assets/javascripts/oxalis/model/actions/view_mode_actions.js +++ b/app/assets/javascripts/oxalis/model/actions/view_mode_actions.js @@ -1,10 +1,10 @@ // @flow import constants from "oxalis/constants"; -import type { OrthoViewType } from "oxalis/constants"; import type { PartialCameraData } from "oxalis/store"; import * as THREE from "three"; import { getTDViewportSize } from "oxalis/model/accessors/view_mode_accessor"; +import type { Rect, OrthoViewType, ViewportType } from "oxalis/constants"; type SetViewportActionType = { type: "SET_VIEWPORT", @@ -33,6 +33,12 @@ type MoveTDViewByVectorActionType = { y: number, }; +type SetInputCatcherRect = { + type: "SET_INPUT_CATCHER_RECT", + viewport: ViewportType, + rect: Rect, +}; + export const setViewportAction = (viewport: OrthoViewType): SetViewportActionType => ({ type: "SET_VIEWPORT", viewport, @@ -70,11 +76,18 @@ export const moveTDViewXAction = (x: number): MoveTDViewByVectorActionType => export const moveTDViewYAction = (y: number): MoveTDViewByVectorActionType => moveTDViewByVectorAction(0, (-y * getTDViewportSize()) / constants.VIEWPORT_WIDTH); +export const setInputCatcherRect = (viewport: ViewportType, rect: Rect): SetInputCatcherRect => ({ + type: "SET_INPUT_CATCHER_RECT", + viewport, + rect, +}); + export type ViewModeActionType = | SetViewportActionType | SetTDCameraActionType | CenterTDViewActionType | ZoomTDViewActionType - | MoveTDViewByVectorActionType; + | MoveTDViewByVectorActionType + | SetInputCatcherRect; export default {}; diff --git a/app/assets/javascripts/oxalis/model/reducers/view_mode_reducer.js b/app/assets/javascripts/oxalis/model/reducers/view_mode_reducer.js index 6a35a51df00..0460498b05e 100644 --- a/app/assets/javascripts/oxalis/model/reducers/view_mode_reducer.js +++ b/app/assets/javascripts/oxalis/model/reducers/view_mode_reducer.js @@ -4,6 +4,8 @@ import update from "immutability-helper"; import type { OxalisState, PartialCameraData } from "oxalis/store"; import type { ActionType } from "oxalis/model/actions/actions"; import * as THREE from "three"; +import { ArbitraryViewport } from "oxalis/constants"; +import type { Rect, ViewportType } from "oxalis/constants"; import { getTDViewportSize } from "oxalis/model/accessors/view_mode_accessor"; function ViewModeReducer(state: OxalisState, action: ActionType): OxalisState { @@ -29,11 +31,40 @@ function ViewModeReducer(state: OxalisState, action: ActionType): OxalisState { case "MOVE_TD_VIEW_BY_VECTOR": { return moveTDViewByVectorReducer(state, action.x, action.y); } + case "SET_INPUT_CATCHER_RECT": { + return setInputCatcherRect(state, action.viewport, action.rect); + } default: return state; } } +function setInputCatcherRect(state: OxalisState, viewport: ViewportType, rect: Rect) { + if (viewport === ArbitraryViewport) { + return update(state, { + viewModeData: { + arbitrary: { + inputCatcherRect: { + $set: rect, + }, + }, + }, + }); + } else { + return update(state, { + viewModeData: { + plane: { + inputCatcherRects: { + [viewport]: { + $set: rect, + }, + }, + }, + }, + }); + } +} + function moveTDViewByVectorReducer(state: OxalisState, x: number, y: number): OxalisState { const camera = state.viewModeData.plane.tdCamera; return update(state, { diff --git a/app/assets/javascripts/oxalis/model/volumetracing/volumelayer.js b/app/assets/javascripts/oxalis/model/volumetracing/volumelayer.js index 0d5cd690ef9..3a78781fc88 100644 --- a/app/assets/javascripts/oxalis/model/volumetracing/volumelayer.js +++ b/app/assets/javascripts/oxalis/model/volumetracing/volumelayer.js @@ -12,6 +12,7 @@ import { getBaseVoxelFactors } from "oxalis/model/scaleinfo"; import { getPlaneScalingFactor } from "oxalis/model/accessors/flycam_accessor"; import { enforceVolumeTracing } from "oxalis/model/accessors/volumetracing_accessor"; import type { OrthoViewType, Vector2, Vector3 } from "oxalis/constants"; +import { getViewportScale } from "oxalis/model/accessors/view_mode_accessor"; export class VoxelIterator { hasNext: boolean = true; @@ -285,7 +286,7 @@ class VolumeLayer { pixelsToVoxels(pixels: number): number { const state = Store.getState(); const zoomFactor = getPlaneScalingFactor(state.flycam); - const viewportScale = state.userConfiguration.scale; + const viewportScale = getViewportScale(this.plane); return (pixels / viewportScale) * zoomFactor; } } diff --git a/app/assets/javascripts/oxalis/store.js b/app/assets/javascripts/oxalis/store.js index e95fd1e0b1c..424ceeaa4c3 100644 --- a/app/assets/javascripts/oxalis/store.js +++ b/app/assets/javascripts/oxalis/store.js @@ -29,6 +29,7 @@ import type { VolumeToolType, ControlModeType, BoundingBoxType, + Rect, } from "oxalis/constants"; import type { Matrix4x4 } from "libs/mjs"; import DiffableMap from "libs/diffable_map"; @@ -223,8 +224,7 @@ export type UserConfigurationType = {| +particleSize: number, +radius: number, +rotateValue: number, - +scale: number, - +scaleValue: number, + +layoutScaleValue: number, +sortCommentsAsc: boolean, +sortTreesByName: boolean, +sphericalCapRadius: number, @@ -313,15 +313,21 @@ export type PartialCameraData = { export type PlaneModeData = { +activeViewport: OrthoViewType, +tdCamera: CameraData, + +inputCatcherRects: { + +PLANE_XY: Rect, + +PLANE_YZ: Rect, + +PLANE_XZ: Rect, + +TDView: Rect, + }, }; -type ArbitraryModeData = null; -type FlightModeData = null; +type ArbitraryModeData = { + +inputCatcherRect: Rect, +}; export type ViewModeData = { +plane: PlaneModeData, - +arbitrary: ?ArbitraryModeData, - +flight: ?FlightModeData, + +arbitrary: ArbitraryModeData, }; type UiInformationType = { @@ -342,6 +348,13 @@ export type OxalisState = {| +uiInformation: UiInformationType, |}; +const defaultViewportRect = { + top: 0, + left: 0, + width: Constants.VIEWPORT_WIDTH, + height: Constants.VIEWPORT_WIDTH, +}; + const initialAnnotationInfo = { annotationId: "", restrictions: { @@ -387,8 +400,7 @@ export const defaultState: OxalisState = { particleSize: 5, radius: 5, rotateValue: 0.01, - scale: 1, - scaleValue: 0.05, + layoutScaleValue: 1, sortCommentsAsc: true, sortTreesByName: false, sphericalCapRadius: 140, @@ -486,9 +498,16 @@ export const defaultState: OxalisState = { lookAt: [0, 0, 0], position: [0, 0, 0], }, + inputCatcherRects: { + PLANE_XY: defaultViewportRect, + PLANE_YZ: defaultViewportRect, + PLANE_XZ: defaultViewportRect, + TDView: defaultViewportRect, + }, + }, + arbitrary: { + inputCatcherRect: defaultViewportRect, }, - arbitrary: null, - flight: null, }, activeUser: null, uiInformation: { diff --git a/app/assets/javascripts/oxalis/view/action-bar/dataset_position_view.js b/app/assets/javascripts/oxalis/view/action-bar/dataset_position_view.js index 892ee7be0ad..dcd3f39ca4c 100644 --- a/app/assets/javascripts/oxalis/view/action-bar/dataset_position_view.js +++ b/app/assets/javascripts/oxalis/view/action-bar/dataset_position_view.js @@ -10,7 +10,7 @@ import { V3 } from "libs/mjs"; import Store from "oxalis/store"; import { setPositionAction, setRotationAction } from "oxalis/model/actions/flycam_actions"; import { getPosition, getRotation } from "oxalis/model/accessors/flycam_accessor"; -import { Input, Tooltip } from "antd"; +import { Input, Tooltip, Icon } from "antd"; import { Vector3Input } from "libs/vector_input"; import ButtonComponent from "oxalis/view/components/button_component"; import message from "messages"; @@ -51,11 +51,15 @@ class DatasetPositionView extends PureComponent {
- Position + + +
@@ -64,11 +68,13 @@ class DatasetPositionView extends PureComponent {
- Rotation + + +
diff --git a/app/assets/javascripts/oxalis/view/action-bar/save_button.js b/app/assets/javascripts/oxalis/view/action-bar/save_button.js index 5caa19f9b21..839b9386023 100644 --- a/app/assets/javascripts/oxalis/view/action-bar/save_button.js +++ b/app/assets/javascripts/oxalis/view/action-bar/save_button.js @@ -74,7 +74,7 @@ class SaveButton extends React.PureComponent { % ) : ( - Save + Save )} ); diff --git a/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js b/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js index 64194ee1dc0..10862d377cd 100644 --- a/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js +++ b/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js @@ -17,6 +17,8 @@ import { copyAnnotationToUserAccount, finishAnnotation } from "admin/admin_rest_ import { location } from "libs/window"; import type { OxalisState, RestrictionsAndSettingsType, TaskType } from "oxalis/store"; import type { APIUserType, APITracingType } from "admin/api_flow_types"; +import { layoutEmitter } from "oxalis/view/layouting/layout_persistence"; +import { updateUserSettingAction } from "oxalis/model/actions/settings_actions"; type StateProps = { tracingType: APITracingType, @@ -232,6 +234,20 @@ class TracingActionsView extends PureComponent { ); } + elements.push( + +
{ + Store.dispatch(updateUserSettingAction("layoutScaleValue", 1)); + layoutEmitter.emit("resetLayout"); + }} + > + + Reset Layout +
+
, + ); + const menu = {elements}; return ( diff --git a/app/assets/javascripts/oxalis/view/action-bar/view_modes_view.js b/app/assets/javascripts/oxalis/view/action-bar/view_modes_view.js index 4538c1d1842..9c66d6b50a9 100644 --- a/app/assets/javascripts/oxalis/view/action-bar/view_modes_view.js +++ b/app/assets/javascripts/oxalis/view/action-bar/view_modes_view.js @@ -2,7 +2,7 @@ import React, { PureComponent } from "react"; import constants from "oxalis/constants"; import type { ModeType } from "oxalis/constants"; -import { Radio } from "antd"; +import { Menu, Radio, Icon, Dropdown } from "antd"; import { setViewModeAction } from "oxalis/model/actions/settings_actions"; import type { OxalisState, AllowedModeType } from "oxalis/store"; import Store from "oxalis/store"; @@ -14,7 +14,26 @@ type Props = { allowedModes: Array, }; -class ViewModesView extends PureComponent { +type State = { + arbitraryModeLabel: ModeType, +}; + +class ViewModesView extends PureComponent { + constructor() { + super(); + this.state = { + arbitraryModeLabel: constants.MODE_ARBITRARY, + }; + } + + componentWillReceiveProps(nextProps: Props) { + if (nextProps.viewMode !== constants.MODE_PLANE_TRACING) { + this.setState({ + arbitraryModeLabel: nextProps.viewMode, + }); + } + } + blurElement = (event: SyntheticInputEvent<>) => { event.target.blur(); }; @@ -28,19 +47,39 @@ class ViewModesView extends PureComponent { } render() { + const arbitraryMenu = ( + Store.dispatch(setViewModeAction(key))} + > + Flight + Oblique + + ); + const viewMode = this.props.viewMode; + return ( - {constants.MODES_SKELETON.map(mode => ( + + {Utils.capitalize(constants.MODE_PLANE_TRACING)} + + - {Utils.capitalize(mode)} + {Utils.capitalize(this.state.arbitraryModeLabel)} - ))} + ); } diff --git a/app/assets/javascripts/oxalis/view/arbitrary_view.js b/app/assets/javascripts/oxalis/view/arbitrary_view.js index ab075bfccb5..b56d10f4a8c 100644 --- a/app/assets/javascripts/oxalis/view/arbitrary_view.js +++ b/app/assets/javascripts/oxalis/view/arbitrary_view.js @@ -7,18 +7,22 @@ import app from "app"; import BackboneEvents from "backbone-events-standalone"; import * as THREE from "three"; import TWEEN from "tween.js"; -import Constants from "oxalis/constants"; +import Constants, { ArbitraryViewport } from "oxalis/constants"; import Store from "oxalis/store"; import SceneController from "oxalis/controller/scene_controller"; import { getZoomedMatrix } from "oxalis/model/accessors/flycam_accessor"; +import { getDesiredCanvasSize } from "oxalis/view/layouting/tracing_layout_view"; import window from "libs/window"; +import { getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor"; +import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; +import { clearCanvas, setupRenderArea } from "./plane_view"; class ArbitraryView { // Copied form backbone events (TODO: handle this better) trigger: Function; + unbindChangedScaleListener: () => void; animate: () => void; - resize: () => void; setClippingDistance: (value: number) => void; needsRerender: boolean; @@ -26,8 +30,6 @@ class ArbitraryView { isRunning: boolean = false; animationRequestId: ?number = null; - width: number; - height: number; scaleFactor: number; camDistance: number; @@ -38,7 +40,6 @@ class ArbitraryView { constructor() { this.animate = this.animateImpl.bind(this); - this.resize = this.resizeImpl.bind(this); this.setClippingDistance = this.setClippingDistanceImpl.bind(this); _.extend(this, BackboneEvents); @@ -73,12 +74,16 @@ class ArbitraryView { this.group.add(this.camera); SceneController.rootGroup.add(this.group); - this.resize(); + this.resizeImpl(); // start the rendering loop this.animationRequestId = window.requestAnimationFrame(this.animate); // Dont forget to handle window resizing! - window.addEventListener("resize", this.resize); + window.addEventListener("resize", this.resizeThrottled); + this.unbindChangedScaleListener = listenToStoreProperty( + store => store.userConfiguration.layoutScaleValue, + this.resizeThrottled, + ); } } @@ -92,7 +97,8 @@ class ArbitraryView { SceneController.rootGroup.remove(this.group); - window.removeEventListener("resize", this.resize); + window.removeEventListener("resize", this.resizeThrottled); + this.unbindChangedScaleListener(); } } @@ -141,12 +147,13 @@ class ArbitraryView { camera.matrix.multiply(new THREE.Matrix4().makeTranslation(...this.cameraPosition)); camera.matrixWorldNeedsUpdate = true; - renderer.setViewport(0, 0, this.width, this.width); - renderer.setScissor(0, 0, this.width, this.width); - renderer.setScissorTest(true); - renderer.setClearColor(0xffffff, 1); + clearCanvas(renderer); - renderer.render(scene, camera); + const { left, top, width, height } = getInputCatcherRect(ArbitraryViewport); + if (width > 0 && height > 0) { + setupRenderArea(renderer, left, top, Math.min(width, height), width, height, 0xffffff); + renderer.render(scene, camera); + } this.needsRerender = false; window.needsRerender = false; @@ -167,16 +174,17 @@ class ArbitraryView { geometry.addToScene(this.group); } - // throttle resize to avoid annoying flickering - resizeThrottled = _.throttle(() => this.resize(), Constants.RESIZE_THROTTLE_TIME); - - resizeImpl(): void { + resizeImpl = (): void => { // Call this after the canvas was resized to fix the viewport - // Needs to be bound - this.width = Store.getState().userConfiguration.scale * Constants.VIEWPORT_WIDTH; - SceneController.renderer.setSize(this.width, this.width); + getDesiredCanvasSize().map(([width, height]) => + SceneController.renderer.setSize(width, height), + ); + this.draw(); - } + }; + + // throttle resize to avoid annoying flickering + resizeThrottled = _.throttle(this.resizeImpl, Constants.RESIZE_THROTTLE_TIME); setClippingDistanceImpl(value: number): void { this.camera.near = this.camDistance - value; diff --git a/app/assets/javascripts/oxalis/view/input_catcher.js b/app/assets/javascripts/oxalis/view/input_catcher.js new file mode 100644 index 00000000000..84a49771ca0 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/input_catcher.js @@ -0,0 +1,88 @@ +// @flow + +import * as React from "react"; +import { setInputCatcherRect } from "oxalis/model/actions/view_mode_actions"; +import type { Rect, ViewportType } from "oxalis/constants"; +import Store from "oxalis/store"; +import makeRectRelativeToCanvas from "oxalis/view/layouting/layout_canvas_adapter"; +import Scalebar from "oxalis/view/scalebar"; + +type Props = { + viewportID: ViewportType, + children?: React.Node, + displayScalebars?: boolean, +}; + +function ignoreContextMenu(event: SyntheticInputEvent<>) { + // hide contextmenu, while rightclicking a canvas + event.preventDefault(); +} + +// makes the input catcher a square and returns its position within the document +// relative to the rendering canvas +function makeInputCatcherQuadratic(inputCatcherDOM: HTMLElement): Rect { + const noneOverflowWrapper = inputCatcherDOM.closest(".gl-dont-overflow"); + if (!noneOverflowWrapper) { + return { top: 0, left: 0, width: 0, height: 0 }; + } + + const { + width: wrapperWidth, + height: wrapperHeight, + } = noneOverflowWrapper.getBoundingClientRect(); + + const squareExtent = Math.round(Math.min(wrapperWidth - 10, wrapperHeight - 10)); + inputCatcherDOM.style.width = `${squareExtent}px`; + inputCatcherDOM.style.height = `${squareExtent}px`; + + return makeRectRelativeToCanvas(inputCatcherDOM.getBoundingClientRect()); +} + +const renderedInputCatchers = new Map(); + +export function recalculateInputCatcherSizes() { + for (const [viewportID, inputCatcher] of renderedInputCatchers.entries()) { + const rect = makeInputCatcherQuadratic(inputCatcher); + Store.dispatch(setInputCatcherRect(viewportID, rect)); + } +} + +class InputCatcher extends React.PureComponent { + domElement: ?HTMLElement; + + componentDidMount() { + if (this.domElement) { + renderedInputCatchers.set(this.props.viewportID, this.domElement); + } + } + + componentWillUnmount() { + if (this.domElement) { + renderedInputCatchers.delete(this.props.viewportID); + } + } + + render() { + const { viewportID } = this.props; + + return ( +
+
{ + this.domElement = domElement; + }} + onContextMenu={ignoreContextMenu} + data-value={viewportID} + className="inputcatcher" + style={{ position: "relative" }} + > + {this.props.displayScalebars ? : null} + {this.props.children} +
+
+ ); + } +} + +export default InputCatcher; diff --git a/app/assets/javascripts/oxalis/view/input_catchers.js b/app/assets/javascripts/oxalis/view/input_catchers.js deleted file mode 100644 index 835c0ad08bd..00000000000 --- a/app/assets/javascripts/oxalis/view/input_catchers.js +++ /dev/null @@ -1,108 +0,0 @@ -// @flow - -import * as React from "react"; -import { connect } from "react-redux"; -import { Button } from "antd"; -import Constants, { OrthoViews } from "oxalis/constants"; -import api from "oxalis/api/internal_api"; -import type { OrthoViewType } from "oxalis/constants"; -import type { OxalisState } from "oxalis/store"; -import Scalebar from "oxalis/view/scalebar"; - -const ButtonGroup = Button.Group; - -type Props = { - scale: number, - activeViewport: OrthoViewType, - displayScalebars: boolean, -}; - -type State = {}; - -class InputCatchers extends React.PureComponent { - render() { - const width = Math.round(this.props.scale * Constants.VIEWPORT_WIDTH); - const TDButtonStyle = { - width: width / 4 - 0.5, - }; - - const activeInputCatcher = this.props.activeViewport; - - return ( -
-
- {this.props.displayScalebars ? : null} -
-
- {this.props.displayScalebars ? : null} -
-
- {this.props.displayScalebars ? : null} -
-
- - - - - - -
-
- ); - } -} - -const mapStateToProps = (state: OxalisState): Props => ({ - scale: state.userConfiguration.scale, - activeViewport: state.viewModeData.plane.activeViewport, - displayScalebars: state.userConfiguration.displayScalebars, -}); - -export default connect(mapStateToProps)(InputCatchers); diff --git a/app/assets/javascripts/oxalis/view/layouting/default_layout_configs.js b/app/assets/javascripts/oxalis/view/layouting/default_layout_configs.js new file mode 100644 index 00000000000..46ff8018843 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/default_layout_configs.js @@ -0,0 +1,90 @@ +/* + * This file defines: + * - the main panes which can be arranged in WK Core + * - the different layout types which specify which panes exist in which layout and what their default arrangement is + * - a `determineLayout` function which decides which layout type has to be chosen + */ + +// @flow +import type { ControlModeType, ModeType } from "oxalis/constants"; +import Constants, { ControlModeEnum } from "oxalis/constants"; +import { Pane, Column, Row, Stack } from "./golden_layout_helpers"; + +// Increment this number to invalidate old layoutConfigs in localStorage +export const currentLayoutVersion = 5; + +const LayoutSettings = { + showPopoutIcon: false, + showCloseIcon: false, + showMaximiseIcon: false, +}; + +// While the first parameter to `Pane` is the title of the pane, the second one is an id +// which is used to match the children provided to GoldenLayoutAdapter (in tracing_layout_view) +// with the panes in the layout config. +const Panes = { + xy: Pane("XY", "xy"), + xz: Pane("XZ", "xz"), + yz: Pane("YZ", "yz"), + td: Pane("3D", "td"), + DatasetInfoTabView: Pane("Info", "DatasetInfoTabView"), + TreesTabView: Pane("Trees", "TreesTabView"), + CommentTabView: Pane("Comments", "CommentTabView"), + AbstractTreeTabView: Pane("Tree Viewer", "AbstractTreeTabView"), + arbitraryViewport: Pane("Arbitrary View", "arbitraryViewport"), + Mappings: Pane("Segmentation", "MappingInfoView"), +}; + +const OrthoViewsGrid = [Column(Panes.xy, Panes.xz), Column(Panes.yz, Panes.td)]; + +const SkeletonRightHandColumn = Stack( + Panes.DatasetInfoTabView, + Panes.TreesTabView, + Panes.CommentTabView, + Panes.AbstractTreeTabView, + Panes.Mappings, +); + +const NonSkeletonRightHandColumn = Stack(Panes.DatasetInfoTabView, Panes.Mappings); + +const createLayout = (...content: Array<*>) => ({ + settings: LayoutSettings, + dimensions: { + headerHeight: 31, + }, + content, +}); + +const OrthoLayout = createLayout(Row(...OrthoViewsGrid, SkeletonRightHandColumn)); +const OrthoLayoutView = createLayout(Row(...OrthoViewsGrid, NonSkeletonRightHandColumn)); +const VolumeTracingView = createLayout(Row(...OrthoViewsGrid, NonSkeletonRightHandColumn)); +const ArbitraryLayout = createLayout(Row(Panes.arbitraryViewport, SkeletonRightHandColumn)); + +const defaultLayouts = { + ArbitraryLayout, + OrthoLayout, + OrthoLayoutView, + VolumeTracingView, +}; + +type LayoutType = $Keys; + +export function determineLayout(controlMode: ControlModeType, viewMode: ModeType): LayoutType { + if (controlMode === ControlModeEnum.VIEW) { + return "OrthoLayoutView"; + } + + if (!Constants.MODES_SKELETON.includes(viewMode)) { + return "VolumeTracingView"; + } + + const isArbitraryMode = Constants.MODES_ARBITRARY.includes(viewMode); + if (isArbitraryMode) { + return "ArbitraryLayout"; + } else { + return "OrthoLayout"; + } +} + +export type LayoutKeysType = $Keys; +export default defaultLayouts; diff --git a/app/assets/javascripts/oxalis/view/layouting/golden_layout_adapter.js b/app/assets/javascripts/oxalis/view/layouting/golden_layout_adapter.js new file mode 100644 index 00000000000..9275853d1d1 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/golden_layout_adapter.js @@ -0,0 +1,167 @@ +// @flow + +import * as React from "react"; +import GoldenLayout from "golden-layout/dist/goldenlayout"; +import _ from "lodash"; +import Constants from "oxalis/constants"; +import Toast from "libs/toast"; +import window from "libs/window"; +import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; +import Store from "oxalis/store"; +import { PortalTarget, RenderToPortal } from "./portal_utils"; +import { layoutEmitter } from "./layout_persistence"; + +type Props = { + id: string, + layoutKey: KeyType, + layoutConfigGetter: (layoutKey: KeyType) => Object, + onLayoutChange?: (config: Object, layoutKey: string) => void, + children: React.Node, + style: Object, +}; + +const getGroundTruthLayoutRect = () => { + const mainContainer = document.querySelector(".ant-layout .ant-layout-has-sider"); + if (!mainContainer) { + return { width: 1000, height: 1000 }; + } + const { clientWidth: width, clientHeight: height } = mainContainer; + return { width, height }; +}; + +const monkeypatchGLSizeGetter = gl => { + const _oldWidth = gl.container.width; + gl.container.width = value => { + if (value) { + return _oldWidth.call(gl, value); + } else { + const { width } = getGroundTruthLayoutRect(); + return width * Store.getState().userConfiguration.layoutScaleValue; + } + }; + const _oldHeight = gl.container.height; + gl.container.height = value => { + if (value) { + return _oldHeight.call(gl, value); + } else { + const { height } = getGroundTruthLayoutRect(); + return height * Store.getState().userConfiguration.layoutScaleValue; + } + }; +}; + +const updateSizeForGl = gl => { + const container = document.querySelector("#layoutContainer"); + if (!container) { + return; + } + const { width, height } = getGroundTruthLayoutRect(); + const layoutScale = Store.getState().userConfiguration.layoutScaleValue; + console.log("updateSizeForGl with scale ===", layoutScale); + container.style.width = `${Math.floor(width * layoutScale)}px`; + container.style.height = `${Math.floor(height * layoutScale)}px`; + + gl.updateSize(width * layoutScale, height * layoutScale); +}; + +export class GoldenLayoutAdapter extends React.PureComponent, *> { + gl: GoldenLayout; + unbindListeners: Array<() => void>; + + componentDidMount() { + this.setupLayout(); + } + + componentDidUpdate(prevProps: Props<*>) { + if (prevProps.layoutKey !== this.props.layoutKey) { + this.rebuildLayout(); + } + } + + componentWillUnmount() { + this.unbind(); + } + + unbind() { + this.unbindListeners.forEach(unbind => unbind()); + } + + rebuildLayout() { + this.unbind(); + this.gl.destroy(); + this.setupLayout(); + } + + onStateChange() { + const { onLayoutChange } = this.props; + if (onLayoutChange != null) { + onLayoutChange(this.gl.toConfig(), this.props.layoutKey); + } + } + + setupLayout() { + const gl = new GoldenLayout( + this.props.layoutConfigGetter(this.props.layoutKey), + `#${this.props.id}`, + ); + this.gl = gl; + gl.registerComponent("PortalTarget", PortalTarget); + + const updateSize = () => updateSizeForGl(gl); + const updateSizeDebounced = _.debounce(updateSize, Constants.RESIZE_THROTTLE_TIME / 5); + window.addEventListener("resize", updateSize); + const unbindResizeListener = () => window.removeEventListener("resize", updateSize); + const unbindResetListener = layoutEmitter.on("resetLayout", () => this.rebuildLayout()); + const unbindChangedScaleListener = listenToStoreProperty( + store => store.userConfiguration.layoutScaleValue, + () => { + updateSizeDebounced(); + setTimeout(updateSizeDebounced, 1000); + }, + ); + + gl.on("stateChanged", () => this.onStateChange()); + + this.unbindListeners = [unbindResetListener, unbindChangedScaleListener, unbindResizeListener]; + + updateSize(); + // The timeout is necessary since react cannot deal with react.render calls (which goldenlayout executes) + // while being in the middle of a react lifecycle (componentDidMount) + setTimeout(() => { + try { + gl.init(); + } catch (exception) { + // This might happen when the serialized layout config is not compatible with the newest version. + // However, this should be mitigated by currentLayoutVersion in default_layout_configs.js + Toast.error("Layout couldn't be restored. The default layout is used instead."); + layoutEmitter.emit("resetLayout"); + console.error(exception); + return; + } + // Rerender since the portals just became available + this.forceUpdate(); + // Trigger initial state update so that the size of the input catchers are set up correctly + this.onStateChange(); + // Monkeypatch the gl size getters so that the layout can be larger then the viewport (k/l scaling) + monkeypatchGLSizeGetter(gl); + // Ensure that the size is correct + updateSize(); + }, 10); + } + + render() { + const layoutContainer = ( +
+ ); + const portals = React.Children.toArray(this.props.children).map(child => ( + + {child} + + )); + return [layoutContainer, ...portals]; + } +} + +// Warning: Don't use a default export for this component. Otherwise, when webpack is run in prod mode, +// UglifyJS will break this component by re-mounting it on every state change of the parent +export default {}; diff --git a/app/assets/javascripts/oxalis/view/layouting/golden_layout_helpers.js b/app/assets/javascripts/oxalis/view/layouting/golden_layout_helpers.js new file mode 100644 index 00000000000..7f8058f0667 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/golden_layout_helpers.js @@ -0,0 +1,35 @@ +// @flow + +function createLayoutHelper(type) { + return function(...content: Array<*>) { + return { + type, + content, + }; + }; +} + +// These functions make the API surface to golden layout a bit more concise. +// For example, instead of writing +// +// { +// type: "column", +// content: [...] +// } +// +// you can simply write +// +// Column(...) + +export const Column = createLayoutHelper("column"); +export const Row = createLayoutHelper("row"); +export const Stack = createLayoutHelper("stack"); + +export function Pane(title: string, portalId: string) { + return { + type: "react-component", + component: "PortalTarget", + title, + props: { portalId }, + }; +} diff --git a/app/assets/javascripts/oxalis/view/layouting/layout_canvas_adapter.js b/app/assets/javascripts/oxalis/view/layouting/layout_canvas_adapter.js new file mode 100644 index 00000000000..259a1349798 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/layout_canvas_adapter.js @@ -0,0 +1,21 @@ +// @flow + +import type { Rect } from "oxalis/constants"; + +export default function makeRectRelativeToCanvas(rect: Rect): Rect { + const layoutContainerDOM = document.getElementById("layoutContainer"); + if (!layoutContainerDOM) { + return rect; + } + const { left: containerX, top: containerY } = layoutContainerDOM.getBoundingClientRect(); + + const borderWidth = 2; + + // Since we want to paint inside the InputCatcher we have to subtract the border + return { + left: rect.left - containerX + borderWidth + 1, // it's not entirely clear to my why the +1 + top: rect.top - containerY + borderWidth - 1, // and -1 are necessary. + width: rect.width - 2 * borderWidth, + height: rect.height - 2 * borderWidth, + }; +} diff --git a/app/assets/javascripts/oxalis/view/layouting/layout_persistence.js b/app/assets/javascripts/oxalis/view/layouting/layout_persistence.js new file mode 100644 index 00000000000..c579844a298 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/layout_persistence.js @@ -0,0 +1,67 @@ +// @flow +import _ from "lodash"; +import NanoEvents from "nanoevents"; + +import defaultLayouts, { currentLayoutVersion } from "./default_layout_configs"; +import type { LayoutKeysType } from "./default_layout_configs"; + +export const layoutEmitter = new NanoEvents(); + +// For debugging purposes: +const disableLayoutPersistance = false; + +const localStorageKeys = { + currentLayoutVersion: "currentLayoutVersion", + goldenWkLayouts: "goldenWkLayouts", +}; + +function readStoredLayoutConfigs() { + const storedLayoutVersion = localStorage.getItem(localStorageKeys.currentLayoutVersion); + if (!storedLayoutVersion || disableLayoutPersistance) { + return {}; + } + if (currentLayoutVersion > JSON.parse(storedLayoutVersion)) { + return {}; + } + const layoutString = localStorage.getItem(localStorageKeys.goldenWkLayouts); + if (layoutString) { + try { + return JSON.parse(layoutString); + } catch (ex) { + // This should only happen if someone tinkers with localStorage manually + console.warn("Layout config could not be deserialized."); + } + } + return {}; +} + +let storedLayouts = readStoredLayoutConfigs(); + +function persistLayoutConfigs() { + localStorage.setItem(localStorageKeys.goldenWkLayouts, JSON.stringify(storedLayouts)); + localStorage.setItem(localStorageKeys.currentLayoutVersion, JSON.stringify(currentLayoutVersion)); +} + +function clearStoredLayouts() { + localStorage.removeItem(localStorageKeys.goldenWkLayouts); + storedLayouts = readStoredLayoutConfigs(); +} + +layoutEmitter.on("resetLayout", () => { + clearStoredLayouts(); +}); + +const persistLayoutConfigsDebounced = _.debounce(persistLayoutConfigs, 1000); + +export function getLayoutConfig(layoutKey: LayoutKeysType) { + if (storedLayouts[layoutKey]) { + return storedLayouts[layoutKey]; + } + + return defaultLayouts[layoutKey]; +} + +export function storeLayoutConfig(layoutConfig: Object, layoutKey: string) { + storedLayouts[layoutKey] = layoutConfig; + persistLayoutConfigsDebounced(); +} diff --git a/app/assets/javascripts/oxalis/view/layouting/portal_utils.js b/app/assets/javascripts/oxalis/view/layouting/portal_utils.js new file mode 100644 index 00000000000..15c8e4981cd --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/portal_utils.js @@ -0,0 +1,46 @@ +// @flow +import * as React from "react"; +import ReactDOM from "react-dom"; + +// The actual content of a layout pane is a portal target, +// to which is rendered within GoldenLayoutAdapter. +// The actual portal targets are reused to avoid that components +// are re-mounted when the layout changes. +const getPortalId = id => `portal-${id}`; +const portalTargetNodes = {}; +function getOrCreatePortalTargetNode(id) { + if (!portalTargetNodes[id]) { + const newNode = document.createElement("div"); + newNode.id = getPortalId(id); + portalTargetNodes[id] = newNode; + } + + return portalTargetNodes[id]; +} + +// This is the placeholder component which is registered with and rendered by GoldenLayout +export class PortalTarget extends React.Component<*, *> { + componentWillUnmount() { + const child = getOrCreatePortalTargetNode(this.props.portalId); + child.parentNode.removeChild(child); + } + render() { + return ( +
{ + if (!node) { + return; + } + const child = getOrCreatePortalTargetNode(this.props.portalId); + node.appendChild(child); + }} + /> + ); + } +} + +// This component is used to render the provided children into a PortalTarget (referenced by id) if that portal exists +export function RenderToPortal({ children, portalId }: { children: React.Node, portalId: string }) { + const portalEl = document.getElementById(getPortalId(portalId)); + return portalEl && ReactDOM.createPortal(children, portalEl); +} diff --git a/app/assets/javascripts/oxalis/view/layouting/tracing_layout_view.js b/app/assets/javascripts/oxalis/view/layouting/tracing_layout_view.js new file mode 100644 index 00000000000..8ff5aefa30b --- /dev/null +++ b/app/assets/javascripts/oxalis/view/layouting/tracing_layout_view.js @@ -0,0 +1,196 @@ +/** + * tracing_layout_view.js + * @flow + */ + +import * as React from "react"; +import Maybe from "data.maybe"; +import OxalisController from "oxalis/controller"; +import SettingsView from "oxalis/view/settings/settings_view"; +import ActionBarView from "oxalis/view/action_bar_view"; +import TracingView from "oxalis/view/tracing_view"; +import { Layout, Icon } from "antd"; +import { location } from "libs/window"; +import { withRouter } from "react-router-dom"; +import Toast from "libs/toast"; +import messages from "messages"; +import ButtonComponent from "oxalis/view/components/button_component"; +import { connect } from "react-redux"; +import CommentTabView from "oxalis/view/right-menu/comment_tab/comment_tab_view"; +import AbstractTreeTabView from "oxalis/view/right-menu/abstract_tree_tab_view"; +import TreesTabView, { importNmls } from "oxalis/view/right-menu/trees_tab_view"; +import MappingInfoView from "oxalis/view/right-menu/mapping_info_view"; +import DatasetInfoTabView from "oxalis/view/right-menu/dataset_info_tab_view"; +import InputCatcher, { recalculateInputCatcherSizes } from "oxalis/view/input_catcher"; +import { ArbitraryViewport, OrthoViews } from "oxalis/constants"; +import type { OxalisState, TracingTypeTracingType } from "oxalis/store"; +import type { ControlModeType, ModeType } from "oxalis/constants"; +import RecordingSwitch from "oxalis/view/recording_switch"; +import TDViewControls from "oxalis/view/td_view_controls"; +import NmlUploadZoneContainer from "oxalis/view/nml_upload_zone_container"; +import { GoldenLayoutAdapter } from "./golden_layout_adapter"; +import { getLayoutConfig, storeLayoutConfig } from "./layout_persistence"; +import { determineLayout } from "./default_layout_configs"; + +const { Header, Sider } = Layout; + +type StateProps = { + viewMode: ModeType, + displayScalebars: boolean, + isUpdateTracingAllowed: boolean, +}; + +type Props = StateProps & { + initialTracingType: TracingTypeTracingType, + initialAnnotationId: string, + initialControlmode: ControlModeType, +}; + +type State = { + isSettingsCollapsed: boolean, +}; + +const canvasAndLayoutContainerID = "canvasAndLayoutContainer"; +export function getDesiredCanvasSize(): Maybe<[number, number]> { + // const canvasAndLayoutContainer = document.getElementById(canvasAndLayoutContainerID); + const canvasAndLayoutContainer = document.querySelector("#layoutContainer"); + + if (canvasAndLayoutContainer) { + const { scrollWidth, scrollHeight } = canvasAndLayoutContainer; + return Maybe.Just([scrollWidth, scrollHeight]); + } + return Maybe.Nothing(); +} + +const GOLDEN_LAYOUT_ADAPTER_STYLE = { + display: "block", + height: "100%", + width: "100%", + flex: "1 1 auto", +}; + +class TracingLayoutView extends React.PureComponent { + state = { + isSettingsCollapsed: true, + }; + + componentDidCatch() { + Toast.error(messages["react.rendering_error"]); + } + + componentWillUnmount() { + // do a complete page refresh to make sure all tracing data is garbage + // collected and all events are canceled, etc. + location.reload(); + } + + handleSettingsCollapse = () => { + this.setState(prevState => ({ + isSettingsCollapsed: !prevState.isSettingsCollapsed, + })); + }; + + onLayoutChange = (layoutConfig, layoutKey) => { + recalculateInputCatcherSizes(); + window.needsRerender = true; + storeLayoutConfig(layoutConfig, layoutKey); + }; + + render() { + const layoutType = determineLayout(this.props.initialControlmode, this.props.viewMode); + const { displayScalebars } = this.props; + + return ( + + + + +
+ + + Settings + + +
+ + + + + +
+ + + {/* + * All possible layout panes are passed here. Depending on the actual layout, + * the components are rendered or not. + */} + + + + + + + + + + + + + + + + + +
+
+
+
+ ); + } +} + +function mapStateToProps(state: OxalisState): StateProps { + return { + viewMode: state.temporaryConfiguration.viewMode, + displayScalebars: state.userConfiguration.displayScalebars, + isUpdateTracingAllowed: state.tracing.restrictions.allowUpdate, + }; +} + +export default connect(mapStateToProps)(withRouter(TracingLayoutView)); diff --git a/app/assets/javascripts/oxalis/view/plane_view.js b/app/assets/javascripts/oxalis/view/plane_view.js index eced9adacda..22a6d751eea 100644 --- a/app/assets/javascripts/oxalis/view/plane_view.js +++ b/app/assets/javascripts/oxalis/view/plane_view.js @@ -9,29 +9,57 @@ import TWEEN from "tween.js"; import * as THREE from "three"; import Store from "oxalis/store"; import Constants, { OrthoViews, OrthoViewValues, OrthoViewColors } from "oxalis/constants"; -import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; -import type { OrthoViewType, OrthoViewMapType, Vector2 } from "oxalis/constants"; +import type { OrthoViewType, OrthoViewMapType } from "oxalis/constants"; import SceneController from "oxalis/controller/scene_controller"; +import { getDesiredCanvasSize } from "oxalis/view/layouting/tracing_layout_view"; +import { getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor"; +import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; + +export const setupRenderArea = ( + renderer: THREE.WebGLRenderer, + x: number, + y: number, + fullExtent: number, + width: number, + height: number, + color: number, +) => { + renderer.setViewport(x, y, fullExtent, fullExtent); + renderer.setScissor(x, y, width, height); + renderer.setScissorTest(true); + renderer.setClearColor(color, 1); +}; + +export const clearCanvas = (renderer: THREE.WebGLRenderer) => { + const rendererSize = renderer.getSize(); + setupRenderArea( + renderer, + 0, + 0, + renderer.domElement.width, + rendererSize.width, + rendererSize.height, + 0xffffff, + ); + renderer.clear(); +}; class PlaneView { // Copied form backbone events (TODO: handle this better) trigger: Function; listenTo: Function; + unbindChangedScaleListener: () => void; cameras: OrthoViewMapType; running: boolean; needsRerender: boolean; - curWidth: number; constructor() { _.extend(this, BackboneEvents); this.running = false; - const { scene, renderer } = SceneController; - - // Create a 4x4 grid - this.curWidth = Constants.VIEWPORT_WIDTH; + const { scene } = SceneController; // Initialize main THREE.js components this.cameras = {}; @@ -55,12 +83,6 @@ class PlaneView { this.cameras[plane].lookAt(new THREE.Vector3(0, 0, 0)); } - // Attach the canvas to the container - renderer.setSize( - 2 * this.curWidth + Constants.VIEWPORT_GAP_WIDTH, - 2 * this.curWidth + Constants.VIEWPORT_GAP_WIDTH, - ); - this.needsRerender = true; app.vent.on("rerender", () => { this.needsRerender = true; @@ -71,15 +93,6 @@ class PlaneView { this.needsRerender = true; }); }); - - listenToStoreProperty( - store => store.userConfiguration.scale, - () => { - if (this.running) { - this.resizeThrottled(); - } - }, - ); } animate(): void { @@ -96,16 +109,20 @@ class PlaneView { const { renderer } = SceneController; renderer.autoClear = true; - renderer.setViewport(0, 0, this.curWidth, this.curWidth); + let { width, height } = getInputCatcherRect(plane); + width = Math.round(width); + height = Math.round(height); + + renderer.setViewport(0, 0, width, height); renderer.setScissorTest(false); renderer.setClearColor(0x000000, 1); - const renderTarget = new THREE.WebGLRenderTarget(this.curWidth, this.curWidth); - const buffer = new Uint8Array(this.curWidth * this.curWidth * 4); + const renderTarget = new THREE.WebGLRenderTarget(width, height); + const buffer = new Uint8Array(width * height * 4); SceneController.updateSceneForCam(plane); renderer.render(scene, this.cameras[plane], renderTarget); - renderer.readRenderTargetPixels(renderTarget, 0, 0, this.curWidth, this.curWidth, buffer); + renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, buffer); return buffer; } @@ -126,37 +143,32 @@ class PlaneView { this.trigger("render"); - const viewport: OrthoViewMapType = { - [OrthoViews.PLANE_XY]: [0, 0], - [OrthoViews.PLANE_YZ]: [this.curWidth + Constants.VIEWPORT_GAP_WIDTH, 0], - [OrthoViews.PLANE_XZ]: [0, this.curWidth + Constants.VIEWPORT_GAP_WIDTH], - [OrthoViews.TDView]: [ - this.curWidth + Constants.VIEWPORT_GAP_WIDTH, - this.curWidth + Constants.VIEWPORT_GAP_WIDTH, - ], + const viewport = { + [OrthoViews.PLANE_XY]: getInputCatcherRect("PLANE_XY"), + [OrthoViews.PLANE_YZ]: getInputCatcherRect("PLANE_YZ"), + [OrthoViews.PLANE_XZ]: getInputCatcherRect("PLANE_XZ"), + [OrthoViews.TDView]: getInputCatcherRect("TDView"), }; - renderer.autoClear = true; - const setupRenderArea = (x, y, width, color) => { - renderer.setViewport(x, y, width, width); - renderer.setScissor(x, y, width, width); - renderer.setScissorTest(true); - renderer.setClearColor(color, 1); - }; + renderer.autoClear = true; - setupRenderArea(0, 0, renderer.domElement.width, 0xffffff); - renderer.clear(); + clearCanvas(renderer); for (const plane of OrthoViewValues) { SceneController.updateSceneForCam(plane); - - setupRenderArea( - viewport[plane][0], - viewport[plane][1], - this.curWidth, - OrthoViewColors[plane], - ); - renderer.render(scene, this.cameras[plane]); + const { left, top, width, height } = viewport[plane]; + if (width > 0 && height > 0) { + setupRenderArea( + renderer, + left, + top, + Math.min(width, height), + width, + height, + OrthoViewColors[plane], + ); + renderer.render(scene, this.cameras[plane]); + } } this.needsRerender = false; @@ -168,21 +180,18 @@ class PlaneView { } resizeThrottled = _.throttle((): void => { + // todo: is this still called? // throttle resize to avoid annoying flickering this.resize(); }, Constants.RESIZE_THROTTLE_TIME); resize = (): void => { - // Call this after the canvas was resized to fix the viewport - const viewportWidth = Math.round( - Store.getState().userConfiguration.scale * Constants.VIEWPORT_WIDTH, + getDesiredCanvasSize().map(([width, height]) => + SceneController.renderer.setSize(width, height), ); - const canvasWidth = viewportWidth * 2 + Constants.VIEWPORT_GAP_WIDTH; - this.curWidth = viewportWidth; - SceneController.renderer.setSize(canvasWidth, canvasWidth); for (const plane of OrthoViewValues) { - this.cameras[plane].aspect = canvasWidth / canvasWidth; + this.cameras[plane].aspect = 1; this.cameras[plane].updateProjectionMatrix(); } this.draw(); @@ -198,13 +207,20 @@ class PlaneView { for (const plane of OrthoViewValues) { SceneController.scene.remove(this.cameras[plane]); } + window.removeEventListener("resize", this.resizeThrottled); + this.unbindChangedScaleListener(); } start(): void { this.running = true; - this.resize(); this.animate(); + + window.addEventListener("resize", this.resizeThrottled); + this.unbindChangedScaleListener = listenToStoreProperty( + store => store.userConfiguration.layoutScaleValue, + this.resizeThrottled, + ); } } diff --git a/app/assets/javascripts/oxalis/view/recording_switch.js b/app/assets/javascripts/oxalis/view/recording_switch.js new file mode 100644 index 00000000000..c5ca91fcbba --- /dev/null +++ b/app/assets/javascripts/oxalis/view/recording_switch.js @@ -0,0 +1,38 @@ +// @flow +import { connect } from "react-redux"; +import * as React from "react"; +import { Switch } from "antd"; +import type { OxalisState } from "oxalis/store"; +import { setFlightmodeRecordingAction } from "oxalis/model/actions/settings_actions"; + +type Props = { + flightmodeRecording: boolean, + onChangeFlightmodeRecording: ?Function, +}; + +function RecordingSwitch({ flightmodeRecording, onChangeFlightmodeRecording }: Props) { + return ( + + ); +} + +const mapDispatchToProps = (dispatch: Dispatch<*>) => ({ + onChangeFlightmodeRecording(value) { + dispatch(setFlightmodeRecordingAction(value)); + }, +}); + +const mapStateToProps = (state: OxalisState) => ({ + flightmodeRecording: state.temporaryConfiguration.flightmodeRecording, +}); + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(RecordingSwitch); diff --git a/app/assets/javascripts/oxalis/view/right-menu/abstract_tree_renderer.js b/app/assets/javascripts/oxalis/view/right-menu/abstract_tree_renderer.js index d3f2f4806dc..06c1b51ef94 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/abstract_tree_renderer.js +++ b/app/assets/javascripts/oxalis/view/right-menu/abstract_tree_renderer.js @@ -597,7 +597,6 @@ class AbstractTreeRenderer { * @param {Number} height */ setDimensions(width: number, height: number): void { - this.canvas.style.width = `${width}px`; this.canvas.style.height = `${height}px`; this.canvas.width = width; this.canvas.height = height; diff --git a/app/assets/javascripts/oxalis/view/right-menu/comment_tab/comment_tab_view.js b/app/assets/javascripts/oxalis/view/right-menu/comment_tab/comment_tab_view.js index 4aa2f3886d0..0ccfc648492 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/comment_tab/comment_tab_view.js +++ b/app/assets/javascripts/oxalis/view/right-menu/comment_tab/comment_tab_view.js @@ -332,7 +332,7 @@ class CommentTabView extends React.PureComponent { ); return ( -
+
{this.renderMarkdownModal()} { const tracingDescription = this.props.tracing.description || ""; return ( -
+

{annotationTypeLabel}

diff --git a/app/assets/javascripts/oxalis/view/right-menu/mapping_info_view.js b/app/assets/javascripts/oxalis/view/right-menu/mapping_info_view.js index cc85abcceb8..9401e1f151a 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/mapping_info_view.js +++ b/app/assets/javascripts/oxalis/view/right-menu/mapping_info_view.js @@ -2,7 +2,7 @@ * mapping_info_view.js * @flow */ -import React, { Component } from "react"; +import React from "react"; import { Table, Tooltip, Icon } from "antd"; import { connect } from "react-redux"; import Cube from "oxalis/model/bucket_data_handling/data_cube"; @@ -43,14 +43,22 @@ const convertCellIdToHSV = (id: number, customColors: ?Array) => { return `hsla(${value * 360}, 100%, 50%, 0.15)`; }; -class MappingInfoView extends Component { +const hasSegmentation = () => Model.getSegmentationLayer() != null; + +class MappingInfoView extends React.Component { componentDidMount() { + if (!hasSegmentation()) { + return; + } const cube = this.getSegmentationCube(); cube.on("bucketLoaded", this._forceUpdate); cube.on("volumeLabeled", this._forceUpdate); } componentWillUnmount() { + if (!hasSegmentation()) { + return; + } const cube = this.getSegmentationCube(); cube.off("bucketLoaded", this._forceUpdate); cube.off("volumeLabeled", this._forceUpdate); @@ -143,6 +151,9 @@ class MappingInfoView extends Component { } render() { + if (!hasSegmentation()) { + return "No segmentation available"; + } const hasMapping = this.props.mapping != null; return ( @@ -187,4 +198,8 @@ const debounceTime = 100; export default connect( mapStateToProps, mapDispatchToProps, + null, + { + pure: false, + }, )(debounceRender(MappingInfoView, debounceTime)); diff --git a/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js b/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js index 38eb649c92f..0080b8fe0ad 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js +++ b/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js @@ -12,11 +12,7 @@ import InputComponent from "oxalis/view/components/input_component"; import ButtonComponent from "oxalis/view/components/button_component"; import { updateUserSettingAction } from "oxalis/model/actions/settings_actions"; import { setDropzoneModalVisibilityAction } from "oxalis/model/actions/ui_actions"; -import { - enforceSkeletonTracing, - getActiveTree, - getActiveGroup, -} from "oxalis/model/accessors/skeletontracing_accessor"; +import { getActiveTree, getActiveGroup } from "oxalis/model/accessors/skeletontracing_accessor"; import { setTreeNameAction, createTreeAction, @@ -57,7 +53,7 @@ type Props = { onDeleteTree: () => void, onChangeTreeName: string => void, annotation: TracingType, - skeletonTracing: SkeletonTracingType, + skeletonTracing?: SkeletonTracingType, userConfiguration: UserConfigurationType, onSetActiveTree: number => void, showDropzoneModal: () => void, @@ -114,6 +110,9 @@ class TreesTabView extends React.PureComponent { }; handleChangeTreeName = evt => { + if (!this.props.skeletonTracing) { + return; + } const { activeGroupId } = this.props.skeletonTracing; if (activeGroupId != null) { api.tracing.renameGroup(activeGroupId, evt.target.value); @@ -127,6 +126,9 @@ class TreesTabView extends React.PureComponent { }; shuffleTreeColor = () => { + if (!this.props.skeletonTracing) { + return; + } getActiveTree(this.props.skeletonTracing).map(activeTree => this.props.onShuffleTreeColor(activeTree.treeId), ); @@ -145,11 +147,15 @@ class TreesTabView extends React.PureComponent { } handleNmlDownload = async () => { + const { skeletonTracing } = this.props; + if (!skeletonTracing) { + return; + } await this.setState({ isDownloading: true }); // Wait 1 second for the Modal to render const [buildInfo] = await Promise.all([getBuildInfo(), Utils.sleep(1000)]); const state = Store.getState(); - const nml = serializeToNml(state, this.props.annotation, this.props.skeletonTracing, buildInfo); + const nml = serializeToNml(state, this.props.annotation, skeletonTracing, buildInfo); this.setState({ isDownloading: false }); const blob = new Blob([nml], { type: "text/plain;charset=utf-8" }); @@ -157,6 +163,9 @@ class TreesTabView extends React.PureComponent { }; getTreesComponents() { + if (!this.props.skeletonTracing) { + return null; + } const orderAttribute = this.props.userConfiguration.sortTreesByName ? "name" : "timestamp"; return ( @@ -216,10 +225,14 @@ class TreesTabView extends React.PureComponent { } render() { - const activeTreeName = getActiveTree(this.props.skeletonTracing) + const { skeletonTracing } = this.props; + if (!skeletonTracing) { + return null; + } + const activeTreeName = getActiveTree(skeletonTracing) .map(activeTree => activeTree.name) .getOrElse(""); - const activeGroupName = getActiveGroup(this.props.skeletonTracing) + const activeGroupName = getActiveGroup(skeletonTracing) .map(activeGroup => activeGroup.name) .getOrElse(""); @@ -232,7 +245,7 @@ class TreesTabView extends React.PureComponent { } return ( -

+
{ { -
    {this.getTreesComponents()}
+
    + {this.getTreesComponents()} +
); } @@ -305,7 +320,7 @@ class TreesTabView extends React.PureComponent { const mapStateToProps = (state: OxalisState) => ({ annotation: state.tracing, - skeletonTracing: enforceSkeletonTracing(state.tracing), + skeletonTracing: state.tracing.skeleton, userConfiguration: state.userConfiguration, }); diff --git a/app/assets/javascripts/oxalis/view/right_menu_view.js b/app/assets/javascripts/oxalis/view/right_menu_view.js deleted file mode 100644 index 62109d2d1c0..00000000000 --- a/app/assets/javascripts/oxalis/view/right_menu_view.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * right_menu_view.js - * @flow - */ - -import * as React from "react"; -import { Tabs } from "antd"; -import CommentTabView from "oxalis/view/right-menu/comment_tab/comment_tab_view"; -import AbstractTreeTabView from "oxalis/view/right-menu/abstract_tree_tab_view"; -import TreesTabView from "oxalis/view/right-menu/trees_tab_view"; -import DatasetInfoTabView from "oxalis/view/right-menu/dataset_info_tab_view"; -import MappingInfoView from "oxalis/view/right-menu/mapping_info_view"; -import type { ControlModeType, ModeType } from "oxalis/constants"; -import Constants, { ControlModeEnum } from "oxalis/constants"; -import type { OxalisState } from "oxalis/store"; -import { connect } from "react-redux"; -import Model from "oxalis/model"; - -const TabPane = Tabs.TabPane; - -type Props = { - controlMode: ControlModeType, - viewMode: ModeType, -}; - -class RightMenuView extends React.Component { - getTabs() { - const tabs = []; - if (this.props.controlMode !== ControlModeEnum.VIEW) { - if (Constants.MODES_SKELETON.includes(this.props.viewMode)) { - tabs.push( - - - , - - - , - - - , - ); - } - } - - const hasSegmentation = Model.getSegmentationLayer() != null; - if (hasSegmentation) { - tabs.push( - - - , - ); - } - - return tabs; - } - - render() { - return ( - { - if (document.activeElement) { - document.activeElement.blur(); - } - }} - > - - - - {this.getTabs()} - - ); - } -} - -function mapStateToProps(state: OxalisState): Props { - return { - controlMode: state.temporaryConfiguration.controlMode, - viewMode: state.temporaryConfiguration.viewMode, - }; -} - -export default connect( - mapStateToProps, - null, - null, - { pure: false }, -)(RightMenuView); diff --git a/app/assets/javascripts/oxalis/view/settings/user_settings_view.js b/app/assets/javascripts/oxalis/view/settings/user_settings_view.js index c087a330d4d..0ded276fb9c 100644 --- a/app/assets/javascripts/oxalis/view/settings/user_settings_view.js +++ b/app/assets/javascripts/oxalis/view/settings/user_settings_view.js @@ -83,15 +83,6 @@ class UserSettingsView extends PureComponent { value={this.props.zoomStep} onChange={this.props.onChangeZoomStep} /> - { value={this.props.zoomStep} onChange={this.props.onChangeZoomStep} /> - { value={this.props.zoomStep} onChange={this.props.onChangeZoomStep} /> - ) => ({ onChangeZoomStep(zoomStep: number) { dispatch(setZoomStepAction(zoomStep)); }, - onChangeRadius(radius: any) { + onChangeRadius(radius: number) { dispatch(setNodeRadiusAction(radius)); }, }); diff --git a/app/assets/javascripts/oxalis/view/td_view_controls.js b/app/assets/javascripts/oxalis/view/td_view_controls.js new file mode 100644 index 00000000000..1d47c115356 --- /dev/null +++ b/app/assets/javascripts/oxalis/view/td_view_controls.js @@ -0,0 +1,27 @@ +// @flow +import * as React from "react"; +import api from "oxalis/api/internal_api"; +import { Button } from "antd"; + +const ButtonGroup = Button.Group; + +function TDViewControls() { + return ( + + + + + + + ); +} + +export default TDViewControls; diff --git a/app/assets/javascripts/oxalis/view/tracing_layout_view.js b/app/assets/javascripts/oxalis/view/tracing_layout_view.js deleted file mode 100644 index 1e635882dae..00000000000 --- a/app/assets/javascripts/oxalis/view/tracing_layout_view.js +++ /dev/null @@ -1,110 +0,0 @@ -// @flow - -import * as React from "react"; -import OxalisController from "oxalis/controller"; -import { connect } from "react-redux"; -import SettingsView from "oxalis/view/settings/settings_view"; -import ActionBarView from "oxalis/view/action_bar_view"; -import RightMenuView from "oxalis/view/right_menu_view"; -import TracingView from "oxalis/view/tracing_view"; -import { Layout, Icon } from "antd"; -import { location } from "libs/window"; -import ButtonComponent from "oxalis/view/components/button_component"; -import type { TracingTypeTracingType, OxalisState } from "oxalis/store"; -import type { ControlModeType } from "oxalis/constants"; -import Toast from "libs/toast"; -import messages from "messages"; -import NmlUploadZoneContainer from "oxalis/view/nml_upload_zone_container"; -import { importNmls } from "oxalis/view/right-menu/trees_tab_view"; - -const { Header, Sider } = Layout; - -type StateProps = { - isUpdateTracingAllowed: boolean, -}; - -type Props = StateProps & { - initialTracingType: TracingTypeTracingType, - initialAnnotationId: string, - initialControlmode: ControlModeType, -}; - -type State = { - isSettingsCollapsed: boolean, -}; - -class TracingLayoutView extends React.PureComponent { - state = { - isSettingsCollapsed: true, - }; - - componentDidCatch() { - Toast.error(messages["react.rendering_error"]); - } - - componentWillUnmount() { - // do a complete page refresh to make sure all tracing data is garbage - // collected and all events are canceled, etc. - location.reload(); - } - - handleSettingsCollapse = () => { - this.setState(prevState => ({ - isSettingsCollapsed: !prevState.isSettingsCollapsed, - })); - }; - - render() { - return ( - - - - -
- - - Settings - - -
- - - - -
-
- -
-
- -
-
-
-
-
- ); - } -} -const mapStateToProps = (state: OxalisState): StateProps => ({ - isUpdateTracingAllowed: state.tracing.restrictions.allowUpdate, -}); - -export default connect(mapStateToProps)(TracingLayoutView); diff --git a/app/assets/javascripts/oxalis/view/tracing_view.js b/app/assets/javascripts/oxalis/view/tracing_view.js index 1a7a5092d3d..0e78afddc54 100644 --- a/app/assets/javascripts/oxalis/view/tracing_view.js +++ b/app/assets/javascripts/oxalis/view/tracing_view.js @@ -6,22 +6,12 @@ import * as React from "react"; import classnames from "classnames"; import { connect } from "react-redux"; -import { Switch } from "antd"; -import Constants from "oxalis/constants"; -import { setFlightmodeRecordingAction } from "oxalis/model/actions/settings_actions"; -import InputCatchers from "oxalis/view/input_catchers"; import { isVolumeTracingDisallowed } from "oxalis/model/accessors/volumetracing_accessor"; import type { OxalisState } from "oxalis/store"; -import type { ModeType } from "oxalis/constants"; -import type { Dispatch } from "redux"; import Toast from "libs/toast"; import messages from "messages"; type Props = { - flightmodeRecording: boolean, - onChangeFlightmodeRecording: ?Function, - viewMode: ModeType, - scale: number, isVolumeTracingDisallowed: boolean, }; @@ -45,54 +35,31 @@ class TracingView extends React.PureComponent { event.preventDefault(); } - getRecordingSwitch = () => ( - - ); - render() { - const isArbitraryMode = Constants.MODES_ARBITRARY.includes(this.props.viewMode); - const inputCatchers = !isArbitraryMode ? : null; - const flightModeRecordingSwitch = isArbitraryMode ? this.getRecordingSwitch() : null; - const divClassName = classnames({ "zoomstep-warning": this.props.isVolumeTracingDisallowed }); - - const canvasWidth = isArbitraryMode - ? Math.round(this.props.scale * Constants.VIEWPORT_WIDTH) - : Math.round(this.props.scale * Constants.VIEWPORT_WIDTH) * 2 + Constants.VIEWPORT_GAP_WIDTH; + const divClassName = classnames({ + "zoomstep-warning": this.props.isVolumeTracingDisallowed, + }); const canvasStyle = { - width: canvasWidth, - height: canvasWidth, + width: "100%", + position: "absolute", + top: 0, + left: 0, }; return ( -
- {inputCatchers} - {flightModeRecordingSwitch} - +
+
); } } - -const mapDispatchToProps = (dispatch: Dispatch<*>) => ({ - onChangeFlightmodeRecording(value) { - dispatch(setFlightmodeRecordingAction(value)); - }, -}); - -const mapStateToProps = (state: OxalisState) => ({ - viewMode: state.temporaryConfiguration.viewMode, - flightmodeRecording: state.temporaryConfiguration.flightmodeRecording, - scale: state.userConfiguration.scale, +const mapStateToProps = (state: OxalisState): Props => ({ isVolumeTracingDisallowed: state.tracing.volume != null && isVolumeTracingDisallowed(state), }); -export default connect( - mapStateToProps, - mapDispatchToProps, -)(TracingView); +export default connect(mapStateToProps)(TracingView); diff --git a/app/assets/javascripts/router.js b/app/assets/javascripts/router.js index 03f8b5ab46a..2a4df4c4c28 100644 --- a/app/assets/javascripts/router.js +++ b/app/assets/javascripts/router.js @@ -15,7 +15,7 @@ import SecuredRoute from "components/secured_route"; import Navbar from "navbar"; import { Imprint, Privacy } from "components/legal"; -import TracingLayoutView from "oxalis/view/tracing_layout_view"; +import TracingLayoutView from "oxalis/view/layouting/tracing_layout_view"; import DashboardView, { urlTokenToTabKeyMap } from "dashboard/dashboard_view"; import SpotlightView from "dashboard/spotlight_view"; import LoginView from "admin/auth/login_view"; diff --git a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md index 865706452c3..87978e9c26b 100644 --- a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md +++ b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md @@ -456,8 +456,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ ␊ @@ -577,7 +577,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ ␊ ␊ ␊ ␊ ␊ ␊
␊ @@ -730,12 +730,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -899,12 +899,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -1009,12 +1009,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -1119,12 +1119,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -1229,12 +1229,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -1339,7 +1339,7 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ +
␊ @@ -1658,8 +1658,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ ␊ @@ -1779,7 +1779,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ ␊ ␊ ␊ ␊ ␊ ␊
␊ @@ -1932,12 +1932,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -2101,12 +2101,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -2211,12 +2211,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -2321,12 +2321,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -2431,12 +2431,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -2541,7 +2541,7 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ +
␊ @@ -3140,8 +3140,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ ␊ @@ -3235,7 +3235,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -3367,7 +3367,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -3805,8 +3805,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ @@ -3926,7 +3926,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ ␊ ␊ ␊ ␊ ␊ ␊
␊ @@ -4079,12 +4079,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -4248,12 +4248,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -4358,12 +4358,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -4468,12 +4468,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -4578,12 +4578,12 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ + ␊ - ␊ +
␊ @@ -4688,7 +4688,7 @@ Generated by [AVA](https://ava.li). ␊ ␊ ␊ - ␊ +
␊ @@ -5316,8 +5316,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ ␊ @@ -5453,7 +5453,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -5566,7 +5566,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -5673,7 +5673,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -5913,8 +5913,8 @@ Generated by [AVA](https://ava.li).
␊ - ␊ -
␊ + ␊ +
␊ @@ -6124,7 +6124,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -6242,7 +6242,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -6350,7 +6350,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ @@ -6458,7 +6458,7 @@ Generated by [AVA](https://ava.li). ␊ - ␊ + ␊ diff --git a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap index f4c2b2ca816..ce43759e608 100644 Binary files a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap and b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap differ diff --git a/app/assets/stylesheets/goldenlayout_overwrites.less b/app/assets/stylesheets/goldenlayout_overwrites.less new file mode 100644 index 00000000000..9c7a65f031b --- /dev/null +++ b/app/assets/stylesheets/goldenlayout_overwrites.less @@ -0,0 +1,84 @@ +.lm_goldenlayout.lm_goldenlayout.lm_goldenlayout, +.lm_content.lm_content.lm_content.lm_content { + background: transparent; +} + +// Hide content of drag preview and color +// its background gray instead +.lm_dragProxy { + background: #8080808a; + + * { + opacity: 0; + } +} + +.lm_content { + // Panes should have a white background + background: white; + border: 1px solid #ababab3d; + &:hover { + border: 1px solid #d3d3d3; + } + + & > div, + & > div > div { + // Make sure that the wrapping divs within a content pane + // have the same height as the pane. + height: inherit; + } + + & > div > div { + // By default, panes should get a scrollbar if the content is too large. + // This can be disabled (e.g., for the input catchers) by setting gl-dont-overflow + // as the class of the pane's child + overflow: auto; + } +} + +.gl-dont-overflow { + overflow: hidden; + height: inherit; +} + +.lm_header { + background: white; + + .lm_tab { + border: 1px solid #cccccc00; + background: transparent; + height: 25px !important; + line-height: 25px; + + .lm_close_tab { + display: none; + } + + .lm_title { + padding: 0px 5px; + // These properties are copied from antd's styling: + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-weight: 400; + font-family: "Monospaced Number", "Chinese Quote", -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", + Helvetica, Arial, sans-serif; + &:hover { + cursor: move; + } + } + } + + .lm_tab.lm_active { + box-shadow: none; + } +} + +.lm_tab:hover, +.lm_tab.lm_active { + .lm_title { + background: white !important; + color: #1890ff !important; + border-bottom: 1px solid #1890ff; + } +} diff --git a/app/assets/stylesheets/main.less b/app/assets/stylesheets/main.less index 7a3fbe96846..2505304c2fd 100644 --- a/app/assets/stylesheets/main.less +++ b/app/assets/stylesheets/main.less @@ -1,5 +1,7 @@ @import "../../../node_modules/antd/dist/antd.less"; @import "../../../node_modules/font-awesome/less/font-awesome.less"; +@import "../../../node_modules/golden-layout/src/css/goldenlayout-base.css"; +@import "../../../node_modules/golden-layout/src/css/goldenlayout-light-theme.css"; @import "../../../node_modules/react-sortable-tree/style.css"; // To update the antd iconfont: @@ -19,6 +21,7 @@ @import "trace_view/_right_menu.less"; @import "_help.less"; @import "antd_overwrites.less"; +@import "goldenlayout_overwrites.less"; @import "react_sortable_tree_overwrites.less"; @import "_admin.less"; @@ -31,7 +34,7 @@ body { font-weight: 400; font-size: 16px; -webkit-font-smoothing: antialised; - height: ~"calc(100vh - 41px)"; + height: 100vh; padding-top: @navbar-height; } @@ -168,7 +171,6 @@ i { .flex-overflow { overflow-y: auto; flex-grow: 1; - height: 0; } .monospace-id { @@ -200,11 +202,15 @@ img.img-50 { } } -@media screen and (max-width: 960px) { +@media screen and (max-width: 968px) { .container { width: auto; margin: 0 10px; } + + .hide-on-small-screen { + display: none; + } } .container { diff --git a/app/assets/stylesheets/trace_view/_right_menu.less b/app/assets/stylesheets/trace_view/_right_menu.less index 7e154a47a6c..4f488904462 100644 --- a/app/assets/stylesheets/trace_view/_right_menu.less +++ b/app/assets/stylesheets/trace_view/_right_menu.less @@ -8,6 +8,9 @@ } #tree-list { + height: inherit; + display: flex; + flex-direction: column; .fa-angle-right { width: 11px; margin-left: 5px; diff --git a/app/assets/stylesheets/trace_view/_tracing_view.less b/app/assets/stylesheets/trace_view/_tracing_view.less index c9577292a78..3086929c988 100644 --- a/app/assets/stylesheets/trace_view/_tracing_view.less +++ b/app/assets/stylesheets/trace_view/_tracing_view.less @@ -4,67 +4,63 @@ vertical-align: top; margin: 0 20px; +} - canvas { - z-index: 10; - } +#flightmode-switch { + position: absolute; + top: 12px; + left: 12px; +} - #flightmode-switch { - position: absolute; - top: 12px; - left: 12px; - } +#inputcatchers { + position: absolute; + width: 100%; +} +.inputcatcher { + border-style: solid; + border-width: 2px; + z-index: 20; + box-sizing: border-box; + -moz-box-sizing: border-box; + float: left; - #inputcatchers { - position: absolute; - width: 100%; - } - .inputcatcher { - width: @standardViewportWidth; - height: @standardViewportWidth; - border-style: solid; - border-width: 2px; - z-index: 20; - box-sizing: border-box; - -moz-box-sizing: border-box; - float: left; - } + margin-right: 20px; + margin-bottom: 20px; + border-color: white; - #inputcatcher_PLANE_YZ { - margin-left: 20px; - } - #inputcatcher_PLANE_XZ { - margin-top: 20px; + &:hover { + border-color: #ff0; } - #inputcatcher_TDView { - margin-top: 20px; - margin-left: 20px; - position: relative; - } - #TDViewControls { - float: left; - height: 20px; - position: absolute; - top: 0px; - z-index: 30; +} - .ant-btn { - & > .colored-dot { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 50%; - margin-right: 5px; - } - &:nth-child(2) .colored-dot { - background-color: red; - } - &:nth-child(3) .colored-dot { - background-color: blue; - } - &:nth-child(4) .colored-dot { - background-color: #0f0; - } +#inputcatcher_TDView { + position: relative; +} +#TDViewControls { + float: left; + height: 20px; + position: absolute; + top: 0px; + z-index: 30; + width: 100%; + + .ant-btn { + width: 25%; + & > .colored-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 5px; + } + &:nth-child(2) .colored-dot { + background-color: red; + } + &:nth-child(3) .colored-dot { + background-color: blue; + } + &:nth-child(4) .colored-dot { + background-color: #0f0; } } } @@ -81,10 +77,9 @@ } .tracing-layout { - padding-bottom: 20px; - padding-right: 20px; - padding-right: 20px; - height: ~"calc(100vh - 41px)"; + height: ~"calc(100vh - 60px)"; + display: flex; + .ant-layout-header { height: auto; min-height: 64px; @@ -100,6 +95,11 @@ overflow-x: hidden; overflow-y: auto; } + .ant-layout-has-sider { + overflow-y: auto; + overflow-x: auto; + flex: 1 1 auto; + } } .tracing-settings-menu { width: @left-menu-width; diff --git a/app/models/configuration/UserConfiguration.scala b/app/models/configuration/UserConfiguration.scala index 07fff78bcb3..8e66b74611d 100644 --- a/app/models/configuration/UserConfiguration.scala +++ b/app/models/configuration/UserConfiguration.scala @@ -40,6 +40,6 @@ object UserConfiguration { "sortTreesByName" -> JsBoolean(false), "sortCommentsAsc" -> JsBoolean(true), "sphericalCapRadius" -> JsNumber(140), + "layoutScaleValue" -> JsNumber(1), "renderComments" -> JsBoolean(false))) - } diff --git a/package.json b/package.json index 6322e47f3b4..2ec101f0d4e 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "sinon": "^1.17.3", "style-loader": "^0.20.2", "tmp": "0.0.33", - "uglifyjs-webpack-plugin": "^1.2.2", + "uglifyjs-webpack-plugin": "^1.2.5", "url-join": "^4.0.0", "url-loader": "^1.0.1", "webpack": "^4.7.0", @@ -131,6 +131,7 @@ "es6-promise": "^3.0.2", "file-saver": "^1.3.3", "font-awesome": "^4.5.0", + "golden-layout": "^1.5.9", "glsl-parser": "git+https://github.com/anvaka/glslx.git#glsl-parser", "hammerjs": "^2.0.8", "history": "^4.7.2", @@ -143,6 +144,7 @@ "mini-css-extract-plugin": "^0.4.0", "mjs": "^1.0.0", "moment": "^2.21.0", + "nanoevents": "^1.0.2", "pako": "^0.2.8", "pretty-bytes": "^5.1.0", "protobufjs": "^6.8.6", diff --git a/webpack.config.js b/webpack.config.js index 80d15295e6a..ae1b9bb090a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,6 +21,15 @@ module.exports = function(env = {}) { filename: "[name].css", chunkFilename: "[name].css", }), + + // GoldenLayout requires these libraries to be available in + // the global scope + new webpack.ProvidePlugin({ + React: "react", + ReactDOM: "react-dom", + $: "jquery", + jQuery: "jquery", + }), ]; if (env.production) { diff --git a/yarn.lock b/yarn.lock index dd09aeb1fa0..6763a96c643 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,11 +38,11 @@ imurmurhash "^0.1.4" slide "^1.1.5" -"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" +"@babel/code-frame@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" dependencies: - "@babel/highlight" "7.0.0-beta.40" + "@babel/highlight" "7.0.0-beta.44" "@babel/code-frame@7.0.0-beta.51": version "7.0.0-beta.51" @@ -50,11 +50,11 @@ dependencies: "@babel/highlight" "7.0.0-beta.51" -"@babel/generator@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" +"@babel/generator@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.44" jsesc "^2.5.1" lodash "^4.2.0" source-map "^0.5.0" @@ -70,13 +70,13 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/helper-function-name@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" +"@babel/helper-function-name@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd" dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.40" - "@babel/template" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" + "@babel/helper-get-function-arity" "7.0.0-beta.44" + "@babel/template" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" "@babel/helper-function-name@7.0.0-beta.51": version "7.0.0-beta.51" @@ -86,11 +86,11 @@ "@babel/template" "7.0.0-beta.51" "@babel/types" "7.0.0-beta.51" -"@babel/helper-get-function-arity@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" +"@babel/helper-get-function-arity@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.44" "@babel/helper-get-function-arity@7.0.0-beta.51": version "7.0.0-beta.51" @@ -99,21 +99,27 @@ "@babel/types" "7.0.0-beta.51" "@babel/helper-module-imports@^7.0.0-beta.34": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.40.tgz#251cbb6404599282e8f7356a5b32c9381bef5d2d" + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.44.tgz#60edc68cdf17e13eaca5be813c96127303085133" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.44" lodash "^4.2.0" +"@babel/helper-split-export-declaration@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" + dependencies: + "@babel/types" "7.0.0-beta.44" + "@babel/helper-split-export-declaration@7.0.0-beta.51": version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.51.tgz#8a6c3f66c4d265352fc077484f9f6e80a51ab978" dependencies: "@babel/types" "7.0.0-beta.51" -"@babel/highlight@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255" +"@babel/highlight@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" dependencies: chalk "^2.0.0" esutils "^2.0.2" @@ -131,13 +137,13 @@ version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-beta.51.tgz#27cec2df409df60af58270ed8f6aa55409ea86f6" -"@babel/template@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" +"@babel/template@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" + "@babel/code-frame" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" lodash "^4.2.0" "@babel/template@7.0.0-beta.51": @@ -149,6 +155,21 @@ "@babel/types" "7.0.0-beta.51" lodash "^4.17.5" +"@babel/traverse@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" + dependencies: + "@babel/code-frame" "7.0.0-beta.44" + "@babel/generator" "7.0.0-beta.44" + "@babel/helper-function-name" "7.0.0-beta.44" + "@babel/helper-split-export-declaration" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" + debug "^3.1.0" + globals "^11.1.0" + invariant "^2.2.0" + lodash "^4.2.0" + "@babel/traverse@7.0.0-beta.51": version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.51.tgz#981daf2cec347a6231d3aa1d9e1803b03aaaa4a8" @@ -164,23 +185,9 @@ invariant "^2.2.0" lodash "^4.17.5" -"@babel/traverse@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" - dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/generator" "7.0.0-beta.40" - "@babel/helper-function-name" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" - debug "^3.0.1" - globals "^11.1.0" - invariant "^2.2.0" - lodash "^4.2.0" - -"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" +"@babel/types@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" dependencies: esutils "^2.0.2" lodash "^4.2.0" @@ -288,8 +295,8 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69" "@types/node@*": - version "9.4.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.7.tgz#57d81cd98719df2c9de118f2d5f3b1120dcd7275" + version "9.6.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d" "@types/node@^8.9.4": version "8.10.17" @@ -394,12 +401,13 @@ ajv@^6.0.1, ajv@^6.5.0: uri-js "^4.2.1" ajv@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.1.tgz#28a6abc493a2abe0fb4c8507acaedb43fa550671" + version "6.4.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.4.0.tgz#d3aff78e9277549771daf0164cff48482b754fc6" dependencies: fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" + uri-js "^3.0.2" align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" @@ -428,8 +436,8 @@ ansi-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" ansi-escapes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" ansi-html@^0.0.7: version "0.0.7" @@ -620,8 +628,8 @@ array-includes@^3.0.3: es-abstract "^1.7.0" array-iterate@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.1.tgz#865bf7f8af39d6b0982c60902914ac76bc0108f6" + version "1.1.2" + resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.2.tgz#f66a57e84426f8097f4197fbb6c051b8e5cdf7d8" array-tree-filter@^1.0.0: version "1.0.1" @@ -739,8 +747,8 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" atob@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + version "2.1.0" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.0.tgz#ab2b150e51d7b122b9efc8d7340c06b6c41076bc" attr-accept@^1.0.3: version "1.1.3" @@ -874,8 +882,8 @@ aws-sign2@~0.7.0: resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" aws4@^1.2.1, aws4@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + version "1.7.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" axobject-query@^2.0.1: version "2.0.1" @@ -937,13 +945,13 @@ babel-core@^6.17.0, babel-core@^6.24.1, babel-core@^6.26.0: source-map "^0.5.6" babel-eslint@^8.0.3: - version "8.2.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" + version "8.2.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.3.tgz#1a2e6681cc9bc4473c32899e59915e19cd6733cf" dependencies: - "@babel/code-frame" "^7.0.0-beta.40" - "@babel/traverse" "^7.0.0-beta.40" - "@babel/types" "^7.0.0-beta.40" - babylon "^7.0.0-beta.40" + "@babel/code-frame" "7.0.0-beta.44" + "@babel/traverse" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" eslint-scope "~3.7.1" eslint-visitor-keys "^1.0.0" @@ -1130,8 +1138,8 @@ babel-plugin-espower@^2.3.2: estraverse "^4.1.1" babel-plugin-import@^1.1.0: - version "1.6.6" - resolved "https://registry.yarnpkg.com/babel-plugin-import/-/babel-plugin-import-1.6.6.tgz#b57e9f9f76f8cd48ca00f2b2b6594a194f3a6769" + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-plugin-import/-/babel-plugin-import-1.7.0.tgz#7da886c26d46a048bc9ed126890fa7e419a6133d" dependencies: "@babel/helper-module-imports" "^7.0.0-beta.34" @@ -1727,9 +1735,9 @@ babelify@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/babelify/-/babelify-8.0.0.tgz#6f60f5f062bfe7695754ef2403b842014a580ed3" -babylon@7.0.0-beta.40, babylon@^7.0.0-beta.30, babylon@^7.0.0-beta.40: - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" +babylon@7.0.0-beta.44, babylon@^7.0.0-beta.30: + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" babylon@^6.1.0, babylon@^6.17.3, babylon@^6.18.0: version "6.18.0" @@ -1748,8 +1756,8 @@ badge-up@2.3.0: svgo "~0.4.5" bail@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.2.tgz#f7d6c1731630a9f9f0d4d35ed1f962e2074a1764" + version "1.0.3" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" balanced-match@^0.4.2: version "0.4.2" @@ -1760,8 +1768,8 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" base64-js@^1.0.2, base64-js@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801" + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" base@^0.11.1: version "0.11.2" @@ -1881,16 +1889,14 @@ braces@^1.8.2: repeat-element "^1.1.2" braces@^2.3.0, braces@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" - define-property "^1.0.0" extend-shallow "^2.0.1" fill-range "^4.0.0" isobject "^3.0.1" - kind-of "^6.0.2" repeat-element "^1.1.2" snapdragon "^0.8.1" snapdragon-node "^2.0.1" @@ -1912,8 +1918,8 @@ browser-resolve@^1.7.0: resolve "1.1.7" browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" @@ -1923,16 +1929,16 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4: safe-buffer "^5.0.1" browserify-cipher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" evp_bytestokey "^1.0.0" browserify-des@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.1.tgz#3343124db6d7ad53e26a8826318712bdc8450f9c" dependencies: cipher-base "^1.0.1" des.js "^1.0.0" @@ -2150,12 +2156,12 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000814" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000814.tgz#2c9eed7fbc2724066474cb7e1a924f0ea12fe4a2" + version "1.0.30000830" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000830.tgz#6e45255b345649fd15ff59072da1e12bb3de2f13" caniuse-lite@^1.0.30000792: - version "1.0.30000814" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000814.tgz#73eb6925ac2e54d495218f1ea0007da3940e488b" + version "1.0.30000830" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000830.tgz#cb96b8a2dd3cbfe04acea2af3c4e894249095328" capture-stack-trace@^1.0.0: version "1.0.0" @@ -2194,7 +2200,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" dependencies: @@ -2202,7 +2208,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.1: +chalk@^2.3.1, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -2211,16 +2217,16 @@ chalk@^2.4.1: supports-color "^5.3.0" character-entities-html4@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.1.tgz#359a2a4a0f7e29d3dc2ac99bdbe21ee39438ea50" + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" character-entities-legacy@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz#f40779df1a101872bb510a3d295e1fccf147202f" character-entities@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.1.tgz#f76871be5ef66ddb7f8f8e3478ecc374c27d6dca" + version "1.2.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" character-reference-invalid@^1.0.0: version "1.1.1" @@ -2257,8 +2263,8 @@ chokidar@^1.4.2, chokidar@^1.6.1: fsevents "^1.0.0" chokidar@^2.0.0, chokidar@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + version "2.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176" dependencies: anymatch "^2.0.0" async-each "^1.0.0" @@ -2272,15 +2278,15 @@ chokidar@^2.0.0, chokidar@^2.0.2: readdirp "^2.0.0" upath "^1.0.0" optionalDependencies: - fsevents "^1.0.0" + fsevents "^1.1.2" chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" chrome-trace-event@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-0.1.2.tgz#90f36885d5345a50621332f0717b595883d5d982" + version "0.1.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz#d395af2d31c87b90a716c831fe326f69768ec084" ci-info@^1.0.0: version "1.1.3" @@ -2349,8 +2355,8 @@ cli-spinners@^0.1.2: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" cli-spinners@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.1.0.tgz#f1847b168844d917a671eb9d147e3df497c90d06" + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" cli-table@^0.3.1: version "0.3.1" @@ -2423,16 +2429,16 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" clone@^1.0.0, clone@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" cloneable-readable@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.1.tgz#c27a4f3a943ca37bed9b01c7d572ee61b1302b15" + version "1.1.2" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" dependencies: inherits "^2.0.1" process-nextick-args "^2.0.0" @@ -2471,8 +2477,8 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.3.tgz#4b906f670e5a963a87b76b0e1689643341b6023c" + version "1.0.4" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" collection-visit@^1.0.0: version "1.0.0" @@ -2544,16 +2550,12 @@ comlinkjs@^3.0.3: resolved "https://registry.yarnpkg.com/comlinkjs/-/comlinkjs-3.0.3.tgz#a976952d9368e5e8c1d6c0ba78f3a9a70df797ed" comma-separated-tokens@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.4.tgz#72083e58d4a462f01866f6617f4d98a3cd3b8a46" + version "1.0.5" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz#b13793131d9ea2d2431cf5b507ddec258f0ce0db" dependencies: trim "0.0.1" -commander@^2.11.0: - version "2.14.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" - -commander@^2.14.1, commander@^2.9.0: +commander@^2.11.0, commander@^2.14.1, commander@^2.9.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -2641,8 +2643,8 @@ concordance@^3.0.0: well-known-symbols "^1.0.0" configstore@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" + version "3.1.2" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" dependencies: dot-prop "^4.1.0" graceful-fs "^4.1.2" @@ -2669,10 +2671,6 @@ contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" -content-type-parser@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" - continuable-cache@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" @@ -2712,8 +2710,8 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" core-js@^2.0.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" + version "2.5.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2739,8 +2737,8 @@ coveralls@^3.0.2: request "^2.85.0" create-ecdh@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + version "4.0.1" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.1.tgz#44223dfed533193ba5ba54e0df5709b89acf1f82" dependencies: bn.js "^4.1.0" elliptic "^6.0.0" @@ -2752,17 +2750,18 @@ create-error-class@^3.0.0: capture-stack-trace "^1.0.0" create-hash@^1.1.0, create-hash@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" dependencies: cipher-base "^1.0.1" inherits "^2.0.1" - ripemd160 "^2.0.0" + md5.js "^1.3.4" + ripemd160 "^2.0.1" sha.js "^2.4.0" create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.6" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" dependencies: cipher-base "^1.0.3" create-hash "^1.1.0" @@ -2848,8 +2847,8 @@ css-color-names@0.0.4, css-color-names@~0.0.3: resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" css-loader@^0.28.1: - version "0.28.10" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.10.tgz#40282e79230f7bcb4e483efa631d670b735ebf42" + version "0.28.11" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" dependencies: babel-code-frame "^6.26.0" css-selector-tokenizer "^0.7.0" @@ -2969,6 +2968,14 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-urls@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.0.tgz#24802de4e81c298ea8a9388bb0d8e461c774684f" + dependencies: + abab "^1.0.4" + whatwg-mimetype "^2.0.0" + whatwg-url "^6.4.0" + data.maybe@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/data.maybe/-/data.maybe-1.2.2.tgz#f955e4b5572b2eb5047eab93d8bb3a1cc76a2dc3" @@ -3170,8 +3177,8 @@ diff@^3.3.1, diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" diffie-hellman@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" @@ -3405,8 +3412,8 @@ ejs@^2.5.9: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: - version "1.3.37" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.37.tgz#4a92734e0044c8cf0b1553be57eae21a4c6e5fab" + version "1.3.42" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz#95c33bf01d0cc405556aec899fe61fd4d76ea0f9" elegant-spinner@^1.0.1: version "1.0.1" @@ -3562,8 +3569,8 @@ es-abstract@^1.10.0: is-regex "^1.0.4" es-abstract@^1.6.1, es-abstract@^1.7.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" + version "1.11.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" dependencies: es-to-primitive "^1.1.1" function-bind "^1.1.1" @@ -4065,8 +4072,8 @@ file-loader@^1.1.5: schema-utils "^0.4.5" file-saver@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.3.tgz#cdd4c44d3aa264eac2f68ec165bc791c34af1232" + version "1.3.8" + resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8" filename-regex@^2.0.0: version "2.0.1" @@ -4199,12 +4206,12 @@ flow-coverage-report@^0.5.0: yargs "8.0.1" flow-parser@^0.*: - version "0.67.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.67.1.tgz#191fed56ccfd8d097dc9d487f2da3b0dae745849" + version "0.70.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.70.0.tgz#9c310187efe4380ba9a251284e9b83b95c49e857" flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" dependencies: inherits "^2.0.1" readable-stream "^2.0.4" @@ -4309,7 +4316,7 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@^1.0.0: +fsevents@^1.0.0, fsevents@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" dependencies: @@ -4415,10 +4422,11 @@ git-up@^2.0.0: parse-url "^1.3.0" git-url-parse@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-8.2.0.tgz#74969b0105f805df58873ee5b96bc1a4dc0573b1" + version "8.3.1" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-8.3.1.tgz#9d7d762993dc498aab16937c844e11afe3748817" dependencies: git-up "^2.0.0" + parse-domain "^2.0.0" github-from-package@0.0.0: version "0.0.0" @@ -4625,6 +4633,12 @@ glsl-tokenizer@^2.0.2: dependencies: through2 "^0.6.3" +golden-layout@^1.5.9: + version "1.5.9" + resolved "https://registry.yarnpkg.com/golden-layout/-/golden-layout-1.5.9.tgz#a39bc1f6a67e6f886b797c016dd924e9426ba77f" + dependencies: + jquery "*" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -4949,8 +4963,8 @@ html-encoding-sniffer@^1.0.2: whatwg-encoding "^1.0.1" html-void-elements@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.2.tgz#9d22e0ca32acc95b3f45b8d5b4f6fbdc05affd55" + version "1.0.3" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.3.tgz#956707dbecd10cf658c92c5d27fee763aa6aa982" htmlparser2@^3.9.1: version "3.9.2" @@ -5025,10 +5039,16 @@ husky@^0.14.3: normalize-path "^1.0.0" strip-indent "^2.0.0" -iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13: +iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" +iconv-lite@^0.4.17, iconv-lite@~0.4.13: + version "0.4.21" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798" + dependencies: + safer-buffer "^2.1.0" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -5040,8 +5060,8 @@ icss-utils@^2.1.0: postcss "^6.0.1" ieee754@^1.1.4: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + version "1.1.11" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455" iferr@^0.1.5: version "0.1.5" @@ -5155,25 +5175,7 @@ inquirer@^3.3.0: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.1.0.tgz#19da508931892328abbbdd4c477f1efc65abfd67" - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.1.0" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^5.5.2" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -inquirer@^5.2.0: +inquirer@^5.1.0, inquirer@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" dependencies: @@ -5419,6 +5421,10 @@ is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" @@ -5474,8 +5480,8 @@ is-path-cwd@^1.0.0: resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" dependencies: is-path-inside "^1.0.0" @@ -5580,8 +5586,8 @@ is-unc-path@^1.0.0: unc-path-regex "^0.1.2" is-url@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.2.tgz#498905a593bf47cc2d9e7f738372bbf7696c7f26" + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" @@ -5739,6 +5745,10 @@ jest-validate@^23.5.0: leven "^2.1.0" pretty-format "^23.5.0" +jquery@*: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" + js-base64@^2.1.9: version "2.4.3" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" @@ -5828,17 +5838,16 @@ jscodeshift@^0.5.0: write-file-atomic "^1.2.0" jsdom@^11.5.1: - version "11.6.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.6.2.tgz#25d1ef332d48adf77fc5221fe2619967923f16bb" + version "11.8.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.8.0.tgz#a52e9a7d2b931284f62c80dad5f17d7390499d8b" dependencies: abab "^1.0.4" acorn "^5.3.0" acorn-globals "^4.1.0" array-equal "^1.0.0" - browser-process-hrtime "^0.1.2" - content-type-parser "^1.0.2" cssom ">= 0.3.2 < 0.4.0" cssstyle ">= 0.2.37 < 0.3.0" + data-urls "^1.0.0" domexception "^1.0.0" escodegen "^1.9.0" html-encoding-sniffer "^1.0.2" @@ -5854,6 +5863,7 @@ jsdom@^11.5.1: w3c-hr-time "^1.0.1" webidl-conversions "^4.0.2" whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" whatwg-url "^6.4.0" ws "^4.0.0" xml-name-validator "^3.0.0" @@ -5879,8 +5889,8 @@ json-loader@^0.5.7: resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" json-parse-better-errors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" json-schema-traverse@^0.3.0: version "0.3.1" @@ -6014,8 +6024,8 @@ lead@^1.0.0: flush-write-stream "^1.0.2" left-pad@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" less-loader@^4.1.0: version "4.1.0" @@ -6205,8 +6215,8 @@ loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0: json5 "^0.5.0" loadjs@^3.3.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/loadjs/-/loadjs-3.5.2.tgz#add0e0e0322859840b362598f762c02f5539ce0b" + version "3.5.4" + resolved "https://registry.yarnpkg.com/loadjs/-/loadjs-3.5.4.tgz#ef0f4eb5a6ac2b86c7597a3d4de97b83816e36b8" locate-path@^2.0.0: version "2.0.0" @@ -6223,8 +6233,8 @@ locate-path@^3.0.0: path-exists "^3.0.0" lodash-es@^4.17.5, lodash-es@^4.2.1: - version "4.17.7" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.7.tgz#db240a3252c3dd8360201ac9feef91ac977ea856" + version "4.17.8" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45" lodash._getnative@^3.0.0: version "3.9.1" @@ -6362,18 +6372,15 @@ loud-rejection@^1.0.0, loud-rejection@^1.2.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lowercase-keys@1.0.0, lowercase-keys@^1.0.0: +lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" -lru-cache@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" -lru-cache@^4.1.1: +lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" dependencies: @@ -6660,8 +6667,8 @@ mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" mime@^2.0.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b" + version "2.3.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" mimic-fn@^1.0.0: version "1.2.0" @@ -6687,8 +6694,8 @@ mini-store@^1.0.2, mini-store@^1.1.0: shallowequal "^1.0.2" minimalistic-assert@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" @@ -6774,8 +6781,8 @@ module-deps-sortable@4.0.6: xtend "^4.0.0" moment@2.x, moment@^2.19.3, moment@^2.21.0: - version "2.21.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" + version "2.22.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad" move-concurrently@^1.0.1: version "1.0.1" @@ -6813,14 +6820,14 @@ n-gram@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/n-gram/-/n-gram-1.0.1.tgz#d29a465e4debcff7f9eed9fb57d9da6c806f4112" -nan@^2.3.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" - -nan@^2.6.2: +nan@^2.3.0, nan@^2.6.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" +nanoevents@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nanoevents/-/nanoevents-1.0.2.tgz#5078a4f8d3e4318aa585c5f2c633cc57fc06ac0f" + nanomatch@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" @@ -6852,8 +6859,8 @@ nearley@^2.7.10: semver "^5.4.1" neo-async@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" nice-try@^1.0.4: version "1.0.4" @@ -7063,8 +7070,8 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" nwmatcher@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" + version "1.4.4" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" nyc@^12.0.2: version "12.0.2" @@ -7193,7 +7200,7 @@ once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: onetime@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" onetime@^2.0.0: version "2.0.1" @@ -7278,8 +7285,8 @@ p-cancelable@^0.3.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" p-cancelable@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.0.tgz#bcb41d35bf6097fc4367a065b6eb84b9b124eff0" + version "0.4.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" p-each-series@^1.0.0: version "1.0.0" @@ -7402,8 +7409,8 @@ parents@^1.0.0: path-platform "~0.11.15" parse-asn1@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -7411,6 +7418,10 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" +parse-domain@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-domain/-/parse-domain-2.0.0.tgz#e9f42f697c30f7c2051dc5c55ff4d8a80da7943c" + parse-entities@^1.0.2, parse-entities@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.1.tgz#8112d88471319f27abae4d64964b122fe4e1b890" @@ -7943,12 +7954,12 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 supports-color "^3.2.3" postcss@^6.0.1: - version "6.0.19" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" + version "6.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.21.tgz#8265662694eddf9e9a5960db6da33c39e4cd069d" dependencies: - chalk "^2.3.1" + chalk "^2.3.2" source-map "^0.6.1" - supports-color "^5.2.0" + supports-color "^5.3.0" postgres-array@~1.0.0: version "1.0.2" @@ -8009,8 +8020,8 @@ prettier@1.13.7: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281" prettier@^1.5.3: - version "1.11.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" + version "1.12.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" pretty-bytes@^4.0.2: version "4.0.2" @@ -8138,8 +8149,8 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" public-encrypt@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" @@ -8342,8 +8353,8 @@ rc-collapse@~1.9.0: rc-animate "2.x" rc-dialog@~7.1.0: - version "7.1.3" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.1.3.tgz#e5472a1bda251acb577d69007cba6c8de53b1bdf" + version "7.1.4" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.1.4.tgz#240c67461d875e49f5d3dd1677351963bcce790a" dependencies: babel-runtime "6.x" rc-animate "2.x" @@ -8382,8 +8393,8 @@ rc-editor-mention@^1.0.2: rc-editor-core "~0.8.3" rc-form@^2.1.0: - version "2.1.7" - resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.1.7.tgz#3e01c5e19838038b65a58014e2802deb088b60b7" + version "2.2.0" + resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.2.0.tgz#ea596c6c92c7df6092f95cbbf8f15014ea07e9f5" dependencies: async-validator "1.x" babel-runtime "6.x" @@ -8402,11 +8413,12 @@ rc-hammerjs@~0.6.0: prop-types "^15.5.9" rc-input-number@~4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.0.2.tgz#09651d5fc05c7f6a1e2111dbfad7ed5743fa7595" + version "4.0.5" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.0.5.tgz#aea46512b73a573462b52827b04d78ac7e6ba6e6" dependencies: babel-runtime "6.x" classnames "^2.2.0" + is-negative-zero "^2.0.0" prop-types "^15.5.7" rmc-feedback "^1.0.0" @@ -8485,12 +8497,12 @@ rc-slider@~8.6.0: warning "^3.0.0" rc-steps@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.1.0.tgz#b204c9ef066287c5cc30dcbe485b2c1bf86e01c6" + version "3.1.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.1.1.tgz#79583ad808309d82b8e011676321d153fd7ca403" dependencies: babel-runtime "^6.23.0" classnames "^2.2.3" - lodash.debounce "^4.0.8" + lodash "^4.17.5" prop-types "^15.5.7" rc-switch@~1.6.0: @@ -8502,8 +8514,8 @@ rc-switch@~1.6.0: prop-types "^15.5.6" rc-table@~6.1.0: - version "6.1.7" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.1.7.tgz#02e33b2d2c3cffc5c6e05f62c0f61a8de0a1daf7" + version "6.1.10" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.1.10.tgz#b2749f127cfb76d7f331fd2e80cfe48c30b6edef" dependencies: babel-runtime "6.x" component-classes "^1.2.6" @@ -8511,6 +8523,7 @@ rc-table@~6.1.0: mini-store "^1.0.2" prop-types "^15.5.8" rc-util "^4.0.4" + react-lifecycles-compat "^3.0.2" shallowequal "^1.0.2" warning "^3.0.0" @@ -8538,8 +8551,8 @@ rc-time-picker@~3.3.0: rc-trigger "^2.2.0" rc-tooltip@^3.7.0, rc-tooltip@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.0.tgz#3afbf109865f7cdcfe43752f3f3f501f7be37aaa" + version "3.7.2" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.2.tgz#3698656d4bacd51b72d9e327bed15d1d5a9f1b27" dependencies: babel-runtime "6.x" prop-types "^15.5.8" @@ -8600,8 +8613,8 @@ rc-upload@~2.4.0: warning "2.x" rc-util@^4.0.2, rc-util@^4.0.4, rc-util@^4.1.0, rc-util@^4.1.1, rc-util@^4.3.0, rc-util@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.4.0.tgz#f6a320a67100cfceaaa1b0a955b01e9be643576c" + version "4.5.0" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.5.0.tgz#3183e6ec806f382efb2d3e85770d95875fa6180f" dependencies: add-dom-event-listener "1.x" babel-runtime "6.x" @@ -8663,14 +8676,14 @@ react-dom@15.5.4: object-assign "^4.1.0" prop-types "~15.5.7" -react-dom@^15.3.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730" +"react-dom@^15.3.2 || ^16": + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" dependencies: - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.0" react-dom@^16.3.0: version "16.4.0" @@ -8689,14 +8702,18 @@ react-dropzone@^4.2.13: prop-types "^15.5.7" react-google-charts@^1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/react-google-charts/-/react-google-charts-1.5.5.tgz#ffeaeca752a820a05029bd6a23516be2432d7df5" + version "1.5.7" + resolved "https://registry.yarnpkg.com/react-google-charts/-/react-google-charts-1.5.7.tgz#70d5daf1e362a9ef199576f15e37769185888067" dependencies: debug "^2.2.0" loadjs "^3.3.1" prop-types "^15.5.8" - react "^15.3.2" - react-dom "^15.3.2" + react "^15.3.2 || ^16" + react-dom "^15.3.2 || ^16" + +react-is@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" react-lazy-load@^3.0.12: version "3.0.13" @@ -8782,12 +8799,13 @@ react-sortable-tree@^2.1.1: react-virtualized "^9.18.5" react-test-renderer@^16.0.0-0, react-test-renderer@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211" + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.2.tgz#3d1ed74fda8db42521fdf03328e933312214749a" dependencies: fbjs "^0.8.16" object-assign "^4.1.1" prop-types "^15.6.0" + react-is "^16.3.2" react-virtualized@^9.18.5: version "9.18.5" @@ -8819,15 +8837,14 @@ react@15.5.4: object-assign "^4.1.0" prop-types "^15.5.7" -react@^15.3.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" +"react@^15.3.2 || ^16": + version "16.3.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" dependencies: - create-react-class "^15.6.0" - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.0" react@^16.3.0: version "16.4.0" @@ -8978,8 +8995,8 @@ recast@^0.12.5: source-map "~0.6.1" recast@^0.14.1: - version "0.14.5" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.5.tgz#53f1f6edf7810bdfb39a25d0ff97d315bad7c314" + version "0.14.7" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" dependencies: ast-types "0.11.3" esprima "~4.0.0" @@ -9521,8 +9538,8 @@ rx-lite@*, rx-lite@^4.0.8: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" rxjs@^5.4.2, rxjs@^5.5.2: - version "5.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.7.tgz#afb3d1642b069b2fbf203903d6501d1acb4cda27" + version "5.5.10" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045" dependencies: symbol-observable "1.0.1" @@ -9546,6 +9563,10 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + samsam@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" @@ -9611,8 +9632,8 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" serialize-javascript@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005" + version "1.5.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -9645,8 +9666,8 @@ setimmediate@^1.0.4, setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.10" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -9806,8 +9827,8 @@ source-map-support@^0.4.15: source-map "^0.5.6" source-map-support@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76" + version "0.5.4" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.4.tgz#54456efa89caa9270af7cd624cc2f123e51fbae8" dependencies: source-map "^0.6.0" @@ -9830,8 +9851,8 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" space-separated-tokens@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.1.tgz#9695b9df9e65aec1811d4c3f9ce52520bc2f7e4d" + version "1.1.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz#e95ab9d19ae841e200808cd96bc7bd0adbbb3412" dependencies: trim "0.0.1" @@ -9963,8 +9984,8 @@ stream-each@^1.1.0: stream-shift "^1.0.0" stream-http@^2.7.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" + version "2.8.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.1.tgz#d0441be1a457a73a733a8a7b53570bebd9ef66a4" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -10033,9 +10054,9 @@ string_decoder@0.10, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.0.tgz#384f322ee8a848e500effde99901bba849c5d403" +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" @@ -10045,12 +10066,6 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - dependencies: - safe-buffer "~5.1.0" - stringify-entities@^1.0.1: version "1.3.1" resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.1.tgz#b150ec2d72ac4c1b5f324b51fb6b28c9cdff058c" @@ -10168,9 +10183,9 @@ supports-color@^4.1.0: dependencies: has-flag "^2.0.0" -supports-color@^5.0.0, supports-color@^5.2.0, supports-color@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" +supports-color@^5.0.0, supports-color@^5.3.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" dependencies: has-flag "^3.0.0" @@ -10349,8 +10364,8 @@ timed-out@^4.0.0, timed-out@^4.0.1: resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" timers-browserify@^2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.6.tgz#241e76927d9ca05f4d959819022f5b3664b64bae" + version "2.0.9" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.9.tgz#3dea6faaab8038589dc6d0c4ccbc08b02698bab3" dependencies: setimmediate "^1.0.4" @@ -10422,13 +10437,7 @@ to-through@^2.0.0: dependencies: through2 "^2.0.3" -tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" - dependencies: - punycode "^1.4.1" - -tough-cookie@~2.3.0: +tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: @@ -10524,20 +10533,7 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -uglifyjs-webpack-plugin@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.3.tgz#bf23197b37a8fc953fecfbcbab66e506f9a0ae72" - dependencies: - cacache "^10.0.4" - find-cache-dir "^1.0.0" - schema-utils "^0.4.5" - serialize-javascript "^1.4.0" - source-map "^0.6.1" - uglify-es "^3.3.4" - webpack-sources "^1.1.0" - worker-farm "^1.5.2" - -uglifyjs-webpack-plugin@^1.2.4: +uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz#2ef8387c8f1a903ec5e44fa36f9f3cbdcea67641" dependencies: @@ -10713,19 +10709,26 @@ upath@^1.0.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" update-notifier@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" + version "2.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" dependencies: boxen "^1.2.1" chalk "^2.0.1" configstore "^3.0.0" import-lazy "^2.1.0" + is-ci "^1.0.10" is-installed-globally "^0.1.0" is-npm "^1.0.0" latest-version "^3.0.0" semver-diff "^2.0.0" xdg-basedir "^3.0.0" +uri-js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-3.0.2.tgz#f90b858507f81dea4dcfbb3c4c3dbfa2b557faaa" + dependencies: + punycode "^2.1.0" + uri-js@^4.2.1: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -11065,13 +11068,17 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: iconv-lite "0.4.19" whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" whatwg-fetch@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.1.1.tgz#ac3c9d39f320c6dce5339969d054ef43dd333319" +whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4" + whatwg-url@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" @@ -11361,8 +11368,8 @@ yauzl@2.4.1: fd-slicer "~1.0.1" yeoman-environment@^2.0.0, yeoman-environment@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.0.5.tgz#84f22bafa84088971fe99ea85f654a3a3dd2b693" + version "2.0.6" + resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.0.6.tgz#ae1b21d826b363f3d637f88a7fc9ea7414cb5377" dependencies: chalk "^2.1.0" debug "^3.1.0"