diff --git a/.circleci/config.yml b/.circleci/config.yml index 9bf6879ce..88fc4360b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,9 @@ jobs: - run: .circleci/find-screenshots-compare-commit.sh - run: echo $REG_SUIT_EXPECTED_KEY - - run: docker run -v `pwd`:`pwd` -w `pwd` -it --rm -e CI -e CIRCLE_SHA1 -e REG_SUIT_EXPECTED_KEY -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_DEFAULT_REGION cruise/webviz-ci:0.0.8 npm run ci + - run: + command: docker run -v `pwd`:`pwd` -w `pwd` -it --rm -e CI -e CIRCLE_SHA1 -e REG_SUIT_EXPECTED_KEY -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_DEFAULT_REGION cruise/webviz-ci:0.0.8 npm run ci + no_output_timeout: 30m - save_cache: paths: ["node_modules"] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..eb2863f6e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.flowconfig b/.flowconfig index 3903f2795..59ac87b4f 100755 --- a/.flowconfig +++ b/.flowconfig @@ -12,6 +12,7 @@ dist emoji=true esproposal.class_static_fields=enable esproposal.class_instance_fields=enable +esproposal.optional_chaining=enable # Cheap workaround to make flow typechecking work across packages in the monorepo module.system.node.resolve_dirname=node_modules module.system.node.resolve_dirname=./packages diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..89822bab9 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,31 @@ +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [10.x] + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: npm bootstrap & build + run: | + npm run bootstrap + npm run build --if-present + #npm run docs (runs webserver) + env: + CI: true + - uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: cruise-automation/webviz/webwiz:latest + registry: docker.pkg.github.com + dockerfile: Dockerfile + username: warpoe + password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json index ead583118..0a3d51149 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ { "editor.formatOnSave": true, - "eslint.autoFixOnSave": true, "flow.useNPMPackagedFlow": true, "javascript.validate.enable": false, "typescript.validate.enable": false, @@ -8,6 +7,7 @@ "*.mdx": "markdown" }, "editor.codeActionsOnSave": { - "source.organizeImports": false + "source.organizeImports": false, + "source.fixAll.eslint": true } } diff --git a/README.md b/README.md index 96a2b958c..14177628b 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # [Webviz](https://webviz.io/) [![CircleCI](https://circleci.com/gh/cruise-automation/webviz.svg?style=svg)](https://circleci.com/gh/cruise-automation/webviz) -**Drag and drop your own bag files into [Webviz](https://webviz.io/try/) to explore your robotics data.** +**Drag and drop your own bag files into [Webviz](https://webviz.io/app/) to explore your robotics data, or connect to a live robot or simulation using the [rosbridge_server](http://wiki.ros.org/rosbridge_suite/Tutorials/RunningRosbridge).** -**View a demo of Webviz in action [here](https://webviz.io/try/?demo).** +**View a demo of Webviz in action [here](https://webviz.io/app/?demo).** **Webviz** is a web-based application for playback and visualization of [ROS](http://www.ros.org/) [bag files](http://wiki.ros.org/Bags). This repository also contains some libraries that can be used independently to build web-based visualization tools. -- **webviz-core** ([homepage](https://webviz.io/), [tool](https://webviz.io/try), [github](https://github.com/cruise-automation/webviz/tree/master/packages/webviz-core)): A tool to inspect [ROS bags](http://wiki.ros.org/ROS/Tutorials/Recording%20and%20playing%20back%20data). +- **webviz-core** ([homepage](https://webviz.io/), [tool](https://webviz.io/app), [github](https://github.com/cruise-automation/webviz/tree/master/packages/webviz-core)): A tool to inspect [ROS bags](http://wiki.ros.org/ROS/Tutorials/Recording%20and%20playing%20back%20data). - **regl-worldview** ([homepage](https://webviz.io/worldview/), [npm](https://www.npmjs.com/package/regl-worldview), [github](https://github.com/cruise-automation/webviz/tree/master/packages/regl-worldview)): React library for rendering 2D and 3D scenes using [regl](https://github.com/regl-project/regl). - **react-key-listener** ([npm](https://www.npmjs.com/package/react-key-listener), [github](https://github.com/cruise-automation/webviz/tree/master/packages/react-key-listener)): React component for handling keyboard events, without interfering with editable fields and buttons. - **@cruise-automation/hooks** ([npm](https://www.npmjs.com/package/@cruise-automation/hooks), [github](https://github.com/cruise-automation/webviz/tree/master/packages/@cruise-automation/hooks)): A list of resusable React hooks. @@ -19,7 +19,7 @@ Please see the individual package READMEs for details on how to install and use - `npm run bootstrap` in the root directory to install dependencies. - `npm run build` to run a single build or `npm run watch` to watch and build. -- `npm run docs` to run the docs app (e.g. go to http://localhost:8080/try to open Webviz). Requires `build` to be run first. +- `npm run docs` to run the docs app (e.g. go to http://localhost:8080/app to open Webviz). Requires `build` to be run first. - `npm run storybook` to run storybook. Requires `build` to be run first. - `npm run screenshot-debug` to generate screenshots from stories. - `npm run lint` to run the linters (and `npm run lint:fix` to automatically fix issues). @@ -35,3 +35,5 @@ If you have the right permissions, you can publish: ## Contributing PRs, bug reports, and feature requests are welcome! Please observe [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) when making a contribution. + +Note that while it's possible to fork Webviz to make your own custom version, we'd encourage you to use webviz.io/app and propose generic solutions that everyone would benefit from. Cruise also still has its own fork of Webviz, but long term we'd like to move away from that. For examples of generic features, see the Node Playground panel, using generic RViz markers in the 3d panel, streaming in bags from any cloud service, loading layouts hosted on arbitrary URLs, and so on. We'd love your creative ideas for making Webviz widely useful! diff --git a/babel.config.js b/babel.config.js index 34da8ffa9..2beb91213 100644 --- a/babel.config.js +++ b/babel.config.js @@ -9,6 +9,8 @@ module.exports = { plugins: [ "@babel/plugin-transform-modules-commonjs", "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-nullish-coalescing-operator", + "@babel/plugin-proposal-optional-chaining", "@babel/plugin-syntax-dynamic-import", ["@babel/plugin-proposal-object-rest-spread", { useBuiltIns: true }], ], diff --git a/docs/public/app/favicon.ico b/docs/public/app/favicon.ico new file mode 100644 index 000000000..9b72dd8e6 Binary files /dev/null and b/docs/public/app/favicon.ico differ diff --git a/docs/public/app/index.html b/docs/public/app/index.html new file mode 120000 index 000000000..b06b78b73 --- /dev/null +++ b/docs/public/app/index.html @@ -0,0 +1 @@ +../../../packages/webviz-core/public/index.html \ No newline at end of file diff --git a/docs/public/index.html b/docs/public/index.html index 0d4c4550d..50b4d08e8 100644 --- a/docs/public/index.html +++ b/docs/public/index.html @@ -9,15 +9,24 @@ + + + + - Webviz + webviz - + @@ -563,7 +572,7 @@
- Use Webviz + Use Webviz Github Cruise
@@ -573,7 +582,7 @@

WEBVIZ

Visualizing robotics data in the browser

- + @@ -595,7 +604,8 @@

Cruise uses Webviz to visualize thousands of complex decisions our vehicles make–both on the road and in simulation. Drag and drop your own ROS bag - files into Webviz to get immediate visual insight into your robotics data. + files into Webviz to get immediate visual insight into your robotics data. Or even connect to + a live robot or simulation.

@@ -681,7 +691,7 @@

Engineering Productivity team.

- + @@ -690,7 +700,7 @@

diff --git a/docs/public/try/index.html b/docs/public/try/index.html deleted file mode 120000 index b06b78b73..000000000 --- a/docs/public/try/index.html +++ /dev/null @@ -1 +0,0 @@ -../../../packages/webviz-core/public/index.html \ No newline at end of file diff --git a/docs/public/try/index.html b/docs/public/try/index.html new file mode 100644 index 000000000..d90480d31 --- /dev/null +++ b/docs/public/try/index.html @@ -0,0 +1,23 @@ + + + + + + + + webviz + + + + + + + diff --git a/docs/public/worldview/index.html b/docs/public/worldview/index.html index 292762cd3..1c38f2008 100755 --- a/docs/public/worldview/index.html +++ b/docs/public/worldview/index.html @@ -8,6 +8,15 @@ + + + + diff --git a/docs/src/2.4.ManagingTheCamera.mdx b/docs/src/2.4.ManagingTheCamera.mdx index 55e78ca07..7d2f9de85 100644 --- a/docs/src/2.4.ManagingTheCamera.mdx +++ b/docs/src/2.4.ManagingTheCamera.mdx @@ -1,5 +1,5 @@ -import CodeSandboxEmbed from './jsx/utils/CodeSandboxEmbed.js'; -import { MoveCamea, FollowObject, FollowObjectOrientation } from './jsx/allLiveEditors' +import CodeSandboxEmbed from "./jsx/utils/CodeSandboxEmbed.js"; +import { MoveCamera, FollowObject, FollowObjectOrientation } from "./jsx/allLiveEditors"; # Managing the Camera @@ -33,7 +33,7 @@ import duckModel from "common/fixtures/Duck.glb"; // Webpack magic: we actually To move the camera, we'll use a handy [useAnimationFrame](https://github.com/cruise-automation/webviz/tree/master/packages/%40cruise-automation/hooks) hook that accepts a callback which will be called at each browser repaint. In our case, whenever the callback is called, we'll increase a `count` state by 1, and use the `count` state to adjust the camera's `phi` and `thetaOffset`. - + Now the camera is moving around the scene, giving us a 360° view of the duck model! diff --git a/docs/src/3.4.MouseEvents.mdx b/docs/src/3.4.MouseEvents.mdx index a3b9ef310..1f43373c1 100755 --- a/docs/src/3.4.MouseEvents.mdx +++ b/docs/src/3.4.MouseEvents.mdx @@ -1,17 +1,17 @@ -import { MouseEvents, MouseEventsInstanced } from './jsx/allLiveEditors'; +import { MouseEvents, MouseEventsInstanced } from "./jsx/allLiveEditors"; # MouseEvents Currently Worldview supports the following mouse event handlers: `onDoubleClick`, `onMouseDown`, `onMouseUp`, `onMouseMove`, and `onClick`. When a supported DOM event is fired, the event handler will be triggered with the original event object and additional event information which can help build interactive views. -| Name | type | Default | Description | -| --------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `onDoubleClick` | `MouseHandler` | | | -| `onMouseDown` | `MouseHandler` | | | -| `onMouseUp` | `MouseHandler` | | | -| `onMouseMove` | `MouseHandler` | | only available if the `hitmapOnMouseMove` prop is set to `true` on the Worldview component | -| `onClick` | `MouseHandler` | | | -| `getChildrenForHitmap` | `GetChildrenForHitmap` | | Maps the `children` to props to be drawn on the hitmap. Optional, but required for any mouse interactions. Provided by default by all included Commands. See more below. | +| Name | type | Default | Description | +| ---------------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `onDoubleClick` | `MouseHandler` | | | +| `onMouseDown` | `MouseHandler` | | | +| `onMouseUp` | `MouseHandler` | | | +| `onMouseMove` | `MouseHandler` | | only available if the `hitmapOnMouseMove` prop is set to `true` on the Worldview component | +| `onClick` | `MouseHandler` | | | +| `getChildrenForHitmap` | `GetChildrenForHitmap` | | Maps the `children` to props to be drawn on the hitmap. Optional, but required for any mouse interactions. Provided by default by all included Commands. See more below. | ## Event Handler @@ -27,7 +27,6 @@ There are two kinds of event handlers: - `ray`: the raw raycasting information including `dir` (direction), `origin`, and `point` (all in `Vec3` format). - `objects`: an array of objects (`{ object, instanceIndex }`) from this command that you interacted with. There will always be at least one object. The `enableStackedObjectEvents` option must be set to `true` on `` for this handler to return more than one object. - ```js console.log("You clicked on", objects.length && objects[0].object.id)}> console.log("You clicked on", objects[0].object.id)}>{cubes} @@ -46,8 +45,9 @@ Example: ```js console.log(`You clicked on ${objects[0].object.id} at index ${objects[0].instanceIndex}`)} - > + onClick={(evt, { objects }) => + console.log(`You clicked on ${objects[0].object.id} at index ${objects[0].instanceIndex}`) + }> {cubes} @@ -59,23 +59,60 @@ How do we detect which object was interacted with? We use a process called picki The Worldview library allows a lot of control over this interaction, but most Commands can use one of three default `getChildrenForHitmap` functions. -| Name | Description | Usage | -| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| `nonInstancedGetChildrenForHitmap` | A default `getChildrenForHitmap` function for non-instanced commands. | `` | -| `getChildrenForHitmapWithOriginalMarker` | Almost identical to nonInstancedGetChildrenForHitmap, but instead the object passed to event callbacks is the object at `prop.originalMarker`, not just `prop`. This is useful for composing commands: see the code for `Arrows` for an example. | `` | -| `createInstancedGetChildrenForHitmap` | A factory function for instanced commands. It takes the number of points per instance: for example, a triangle has 3 points, whereas a sphere has 1 point (just the center). | `` | +| Name | Description | Usage | +| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | +| `nonInstancedGetChildrenForHitmap` | A default `getChildrenForHitmap` function for non-instanced commands. | `` | +| `getChildrenForHitmapWithOriginalMarker` | Almost identical to nonInstancedGetChildrenForHitmap, but instead the object passed to event callbacks is the object at `prop.originalMarker`, not just `prop`. This is useful for composing commands: see the code for `Arrows` for an example. | `` | +| `createInstancedGetChildrenForHitmap` | A factory function for instanced commands. It takes the number of points per instance: for example, a triangle has 3 points, whereas a sphere has 1 point (just the center). | `` | ##### Turning off interactivity for a layer All built in commands allow overriding the `getChildrenForHitmap` function, and we recommend that you also allow users to override it. If a user passes in `null` or `undefined` for `getChildrenForHitmap` in a command, any objects drawn by that command instance will not be visible for interaction purposes. Disabling interaction in this way may help improve performance. +##### event.stopPropagation() + +`event.stopPropagation()` on Command Component Level Handlers will prevent other handlers from firing. + +```js +/* + Command Component handler stopping the Worldview level one +*/ + console.log("This wont fire")}> + { + evt.stopPropagation(); + console.log("This will fire"); + }}> + {cubes} + + +``` + +```js +/* + Command Component handler stopping another one (only happens when enableStackedObjectEvents={true}) +*/ + + { + evt.stopPropagation(); + console.log("This will fire"); + }}> + {cubesInFront} + + console.log("This wont fire")}>{cubesBehind} + +``` + ##### Customizing the getChildrenForHitmap function Each Command is passed the `reglCommand` function and `children`. Worldview compiles the `reglCommand` once by calling it with the `regl` object as an argument, and calls the resulting command each frame with `children`. `getChildrenForHitmap` takes in those `children` and returns new version of them that will be rendered into the hitmap. Some important things to remember: - * The first argument of `getChildrenForHitmap` is the `children`. - * The second argument is a function that helps us assign colors to each object. Each object or instance that you want to differentiate should receive a unique color, since these colors function as unique IDs. + +- The first argument of `getChildrenForHitmap` is the `children`. +- The second argument is a function that helps us assign colors to each object. Each object or instance that you want to differentiate should receive a unique color, since these colors function as unique IDs. + ```js /* * object: the object to pass to event callbacks when this object is interacted with. @@ -84,8 +121,9 @@ Some important things to remember: */ type AssignNextColorsFn = (object: Object, count: number) => Vec4[]; ``` - * * Each object that you want to support mouse events on requires a unique ID. If your command actually renders multiple items per input object (such as when using [instanced drawing](https://github.com/regl-project/regl/blob/gh-pages/example/instance-triangle.js)), you may want a separate ID for each item — in this case, call `assignNextColors` with a count gretaer than 1. - * Once you have this ID, pass it to `intToRGB()` to get the color to paint your object. Don't apply any transparency or shading to your object or this will mess up the hitmap. + +- - Each object that you want to support mouse events on requires a unique color. This color serves as the unique object ID. If your command actually renders multiple items per input object (such as when using [instanced drawing](https://github.com/regl-project/regl/blob/gh-pages/example/instance-triangle.js)), you may want a separate color for each item — in this case, call `assignNextColors` with a count greater than 1. +- Once you have this color, make sure to use it as the color of your object. Don't apply any transparency or shading to your object or this will mess up the hitmap. Example using composition of the default `getChildrenForHitmap` functions: @@ -100,8 +138,7 @@ Example using composition of the default `getChildrenForHitmap` functions: hitmapProp.scale = { x: hitmapProps.scale.x * 1.1, y: hitmapProps.scale.y * 1.1, z: hitmapProps.scale.z * 1.1 }; } return hitmapProps; - }} - > + }}> {points} @@ -124,10 +161,8 @@ Example using a custom `getChildrenForHitmap` function: } // We copy each point array. const hitmapProp = { ...prop }; - // We get 1 new ID, and pass in the original prop so it will be the object passed to any event handlers. - const [id] = assignNextColors(object, 1); - // We turn the objectHitmapId into a color and use it for the points. - const hitmapColor = intToRGB(id); + // We get 1 new color, and pass in the original prop so it will be the object passed to any event handlers. + const [hitmapColor] = assignNextColors(object, 1); hitmapProp.color = hitmapColor; if (hitmapProp.points) { hitmapProp.colors = new Array(hitmapProp.points.length).fill(hitmapColor); diff --git a/docs/src/4.11.Triangles.mdx b/docs/src/4.11.Triangles.mdx index ea06e65dd..6a575ef3c 100755 --- a/docs/src/4.11.Triangles.mdx +++ b/docs/src/4.11.Triangles.mdx @@ -34,6 +34,10 @@ type Triangle = { color: Color, colors?: Color[], points: [{ x: number, y: number, z: number }] + // Pass true to not render the triangles to the screen - just the hitmap. + // This can be used to render the interior of a group of lines as transparent during normal rendering, but as + // clickable for hitmap purposes. + onlyRenderInHitmap?: boolean, } ``` diff --git a/docs/src/4.15.GLText.mdx b/docs/src/4.15.GLText.mdx new file mode 100644 index 000000000..a542017ee --- /dev/null +++ b/docs/src/4.15.GLText.mdx @@ -0,0 +1,9 @@ +import { GLText } from "./jsx/allLiveEditors"; + +# Text + +`` renders text in the 3D scene (unlike ``, which renders additional DOM nodes). + +The prop types are identical to ``, except that additionally each Text object supports an optional `billboard?: boolean` property. If true (the default), text will always face the camera; if false, text may be rotated in 3D according to `pose.orientation`. + + diff --git a/docs/src/4.6.Lines.mdx b/docs/src/4.6.Lines.mdx index 7d3753fe4..7bf51912c 100755 --- a/docs/src/4.6.Lines.mdx +++ b/docs/src/4.6.Lines.mdx @@ -1,4 +1,4 @@ -import { LinesDemo, LinesWireframe, LinesHitmap } from "./jsx/allLiveEditors"; +import { LinesDemo, LinesWireframe, LinesHitmap, LinesPoses } from "./jsx/allLiveEditors"; # Lines @@ -22,6 +22,13 @@ type Line = { position: { x: number, y: number, z: number }, orientation: { x: number, y: number, z: number, w: number } } + // Lines can optionally include multiple poses. + poses: [ + { + position: { x: number, y: number, z: number }, + orientation: { x: number, y: number, z: number, w: number } + } + ], scale: { x: number, y: number, @@ -45,6 +52,10 @@ type Line = { +## Multi-Pose Example + + + ## Mouse Interaction diff --git a/docs/src/jsx/allDemos.stories.js b/docs/src/jsx/allDemos.stories.js index bc551c33d..1d5cb9f06 100644 --- a/docs/src/jsx/allDemos.stories.js +++ b/docs/src/jsx/allDemos.stories.js @@ -17,8 +17,10 @@ import Cones from "./commands/Cones"; import Cubes from "./commands/Cubes"; import Cylinders from "./commands/Cylinders"; import FilledPolygons from "./commands/FilledPolygons"; +import GLText from "./commands/GLText"; import GLTFScene from "./commands/GLTFScene"; import LinesDemo from "./commands/LinesDemo"; +import LinesPoses from "./commands/LinesPoses"; import LinesWireframe from "./commands/LinesWireframe"; import Overlay from "./commands/Overlay"; import Points from "./commands/Points"; @@ -46,6 +48,7 @@ const allDemos = { FilledPolygons, Hitmap, LinesDemo, + LinesPoses, LinesWireframe, MouseEvents, Overlay, @@ -54,6 +57,7 @@ const allDemos = { SpheresInstancedColor, SpheresSingle, Text, + GLText, Triangles, GLTFScene, }; diff --git a/docs/src/jsx/allLiveEditors.js b/docs/src/jsx/allLiveEditors.js index 63569e876..2a18365b9 100755 --- a/docs/src/jsx/allLiveEditors.js +++ b/docs/src/jsx/allLiveEditors.js @@ -10,7 +10,7 @@ import React from "react"; import WorldviewCodeEditor from "./utils/WorldviewCodeEditor"; -function makeCodeComponent(raw, componentName, { isRowView, insertCodeSandboxStyle } = {}) { +function makeCodeComponent(raw, componentName, { isRowView, insertCodeSandboxStyle, noInline } = {}) { const code = raw .split("// #BEGIN EXAMPLE")[1] .split("// #END EXAMPLE")[0] @@ -23,11 +23,16 @@ function makeCodeComponent(raw, componentName, { isRowView, insertCodeSandboxSty // eslint-disable-next-line react/display-name return () => ( ); } @@ -73,6 +78,8 @@ export const FilledPolygonsHitmap = makeCodeComponent( "FilledPolygonsHitmap" ); +export const GLText = makeCodeComponent(require("!!raw-loader!./commands/GLText"), "GLText", { noInline: true }); + export const GLTFScene = makeCodeComponent(require("!!raw-loader!./commands/GLTFScene"), "GLTFScene"); export const GLTFSceneHitmap = makeCodeComponent(require("!!raw-loader!./commands/GLTFSceneHitmap"), "GLTFSceneHitmap"); @@ -83,6 +90,8 @@ export const LinesDemo = makeCodeComponent(require("!!raw-loader!./commands/Line export const LinesHitmap = makeCodeComponent(require("!!raw-loader!./commands/LinesHitmap"), "LinesHitmap"); +export const LinesPoses = makeCodeComponent(require("!!raw-loader!./commands/LinesPoses"), "LinesPoses"); + export const LinesWireframe = makeCodeComponent(require("!!raw-loader!./commands/LinesWireframe"), "LinesWireframe"); export const Overlay = makeCodeComponent(require("!!raw-loader!./commands/Overlay"), "Overlay"); @@ -140,7 +149,7 @@ export const InstancedRendering = makeCodeComponent( "RenderingObjects" ); -export const MoveCamea = makeCodeComponent(require("!!raw-loader!./tutorials/MoveCamea"), "ManagingTheCamera"); +export const MoveCamera = makeCodeComponent(require("!!raw-loader!./tutorials/MoveCamera"), "ManagingTheCamera"); export const StopReleaseDuck = makeCodeComponent( require("!!raw-loader!./tutorials/StopReleaseDuck"), diff --git a/docs/src/jsx/commands/GLText.js b/docs/src/jsx/commands/GLText.js new file mode 100644 index 000000000..f93b7b3e0 --- /dev/null +++ b/docs/src/jsx/commands/GLText.js @@ -0,0 +1,184 @@ +// Copyright (c) 2018-present, GM Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +// #BEGIN EXAMPLE +import { useAnimationFrame } from "@cruise-automation/hooks"; +import { quat } from "gl-matrix"; +import React, { useState, useRef } from "react"; +import Worldview, { GLText, DEFAULT_CAMERA_STATE } from "regl-worldview"; +import seedrandom from "seedrandom"; + +import { inScreenshotTests } from "../utils/codeSandboxUtils"; + +// #BEGIN EDITABLE +const rng = seedrandom(1999); // pseudo-random generator for deterministic testing! +const NUM_COLS = 200; // number of text columns to draw - try bumping this up! +const RADIUS = 30; // distance from the origin for placing columns +const GLITCH_PROBABILITY = 0.3; // how likely a character is to change to something else +const FADE = 3; // how quickly each character fades out +const CHAR_HEIGHT = 1.1; // vertical space between characters +const STEP_INTERVAL = 100; // how often the code rain "falls" +const ALPHABET = [].concat( + // all characters to pick from + ...[[0x30, 0x39], [0x41, 0x5a], [0x410, 0x42f], [0x3041, 0x3096]].map(([start, end]) => + new Array(end - start + 1).fill().map((_, i) => String.fromCodePoint(start + i)) + ) +); + +function randomChar() { + return ALPHABET[Math.floor(rng() * ALPHABET.length)]; +} + +class MatrixText { + constructor() { + this._cols = new Array(NUM_COLS).fill().map(() => this.newColumn()); + } + + newChar() { + return { + age: 0, + char: randomChar(), + spinOffset: 2 * Math.PI * rng(), + spinSpeed: 0.5 * (rng() - 0.5), + }; + } + + newColumn() { + const r = RADIUS * Math.sqrt(rng()); + const theta = 2 * Math.PI * rng(); + const z = RADIUS * 2 * (rng() - 0.5); + return { + minZ: -RADIUS + (z + RADIUS) * rng(), + x: r * Math.cos(theta), + y: r * Math.sin(theta), + z, + chars: [this.newChar()], + }; + } + + // Fade out the characters a little bit on each animation frame. + frame(dt) { + for (const { chars } of this._cols) { + for (const char of chars) { + char.age += dt; + if (rng() < GLITCH_PROBABILITY * dt) { + char.char = randomChar(); + } + } + } + } + + // Drop a new character at the bottom of each column. + // If the column is old, replace it with a new one. + step() { + this._cols = this._cols.map((col) => { + if (rng() > GLITCH_PROBABILITY && col.z - col.chars.length * CHAR_HEIGHT > col.minZ) { + col.chars.push(this.newChar()); + } + const firstKeepIdx = col.chars.findIndex(({ age }) => age < FADE); + if (firstKeepIdx === -1) { + return this.newColumn(); + } + col.chars.splice(0, firstKeepIdx); + col.z -= CHAR_HEIGHT * firstKeepIdx; + return col; + }); + } + + // Convert to markers that we can pass in to . + toMarkers() { + const markers = []; + for (const { x, y, z, chars } of this._cols) { + chars.forEach(({ age, char, spinOffset, spinSpeed }, i) => { + const newness = 1 / (1 + Math.exp(((age - 0.5) / FADE) * 20)); + const oldness = 1 / (1 + Math.exp(((age - 1.5) / FADE) * 20)); + const spin = quat.create(); + quat.rotateZ(spin, spin, spinOffset + age * spinSpeed * 2 * Math.PI); + quat.rotateX(spin, spin, Math.PI / 2); + markers.push({ + text: char, + colors: [{ r: newness, g: oldness, b: newness, a: oldness }, { r: 0, g: 0, b: 0, a: oldness }], + pose: { + position: { x, y, z: z - i * CHAR_HEIGHT }, + orientation: { x: spin[0], y: spin[1], z: spin[2], w: spin[3] }, + }, + scale: { x: 1, y: 1, z: 1 }, + billboard: false, + }); + }); + } + return markers; + } +} + +function Example() { + const [matrix] = useState(() => { + const matrix = new MatrixText(); + // For screenshot tests we don't start the timer, so just run a few steps explicitly. + if (inScreenshotTests()) { + for (let i = 0; i < 10; i++) { + matrix.frame(STEP_INTERVAL / 1000); + matrix.step(); + } + } + return matrix; + }); + const [cameraState, setCameraState] = useState({ + ...DEFAULT_CAMERA_STATE, + distance: RADIUS * 2, + phi: Math.PI / 2, + }); + + const lastFrameTime = useRef(); + const lastStepTime = useRef(); + const fpsMeter = useRef(); + useAnimationFrame( + (timestamp) => { + if (lastStepTime.current == null || timestamp - lastStepTime.current > STEP_INTERVAL) { + matrix.step(); + lastStepTime.current = timestamp; + } + if (lastFrameTime.current != null) { + const dt = (timestamp - lastFrameTime.current) / 1000; + if (fpsMeter.current) { + fpsMeter.current.innerText = `${(1 / dt).toFixed()}fps`; + } + + matrix.frame(dt); + setCameraState((state) => ({ + ...state, + thetaOffset: state.thetaOffset + 0.05 * dt, + phi: Math.PI / 2 + 0.1 * Math.sin(2 * Math.PI * (timestamp / 1000) * 0.02), + })); + } + lastFrameTime.current = timestamp; + }, + inScreenshotTests(), + [] + ); + + return ( + + setCameraState((oldState) => ({ + ...oldState, + ...newState, + targetOffset: oldState.targetOffset, + phi: oldState.phi, + })) + }> + {matrix.toMarkers()} +
+ + ); +} + +// #DOCS ONLY: render(); + +// #END EXAMPLE + +export default Example; diff --git a/docs/src/jsx/commands/LinesPoses.js b/docs/src/jsx/commands/LinesPoses.js new file mode 100644 index 000000000..1e0a33437 --- /dev/null +++ b/docs/src/jsx/commands/LinesPoses.js @@ -0,0 +1,63 @@ +// Copyright (c) 2018-present, GM Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +// #BEGIN EXAMPLE +import React from "react"; +import Worldview, { Lines } from "regl-worldview"; + +// #BEGIN EDITABLE +function Example() { + const lines = [ + { + pose: { + position: { x: 0, y: 0, z: 0 }, + orientation: { x: 0, y: 0, z: 0, w: 0 }, + }, + scale: { x: 0.1, y: 0.1, z: 0.1 }, + color: { r: 0, g: 1, b: 0, a: 1 }, + points: [ + { x: 0, y: -3, z: 1 }, + { x: 1, y: -2, z: 1 }, + { x: 0, y: -3, z: 0 }, + { x: 1, y: -2, z: 0 }, + + { x: 0, y: -3, z: 1 }, + { x: 1, y: -2, z: 1 }, + { x: 0, y: -3, z: 0 }, + { x: 1, y: -2, z: 0 }, + ], + // There are 8 points, so 4 + poses: [ + { + position: { x: 0, y: 0, z: 0 }, + orientation: { x: 0, y: 0, z: 0, w: 0 }, + }, + { + position: { x: 0, y: 0, z: 0 }, + orientation: { x: 0, y: 0, z: 0, w: 0 }, + }, + // shift the last two lines + { + position: { x: 2, y: 2, z: 0 }, + orientation: { x: 0, y: 0, z: 0, w: 0 }, + }, + { + position: { x: 2, y: 2, z: 0 }, + orientation: { x: 0, y: 0, z: 0, w: 0 }, + }, + ], + colors: [], + }, + ]; + return ( + + {lines} + + ); +} +// #END EXAMPLE + +export default Example; diff --git a/docs/src/jsx/tutorials/MoveCamea.js b/docs/src/jsx/tutorials/MoveCamera.js similarity index 100% rename from docs/src/jsx/tutorials/MoveCamea.js rename to docs/src/jsx/tutorials/MoveCamera.js diff --git a/docs/src/jsx/utils/WorldviewCodeEditor.js b/docs/src/jsx/utils/WorldviewCodeEditor.js index 3cc7e1be3..632f46d8b 100755 --- a/docs/src/jsx/utils/WorldviewCodeEditor.js +++ b/docs/src/jsx/utils/WorldviewCodeEditor.js @@ -10,7 +10,7 @@ import last from "lodash/last"; import remove from "lodash/remove"; import sample from "lodash/sample"; import polygonGenerator from "polygon-generator"; -import React, { useState, useEffect } from "react"; +import React, { useRef, useState, useCallback, useEffect } from "react"; import Worldview, { Command, Arrows, @@ -28,6 +28,7 @@ import Worldview, { PolygonBuilder, Overlay, Text, + GLText, GLTFScene, DEFAULT_CAMERA_STATE, withPose, @@ -43,6 +44,7 @@ import { getHashUrlByComponentName } from "../../routes"; import CameraStateInfo from "./CameraStateInfo"; import cesiumManModel from "./CesiumMan.glb"; import CodeEditor from "./CodeEditor"; +import { inScreenshotTests } from "./codeSandboxUtils"; import InputNumber from "./InputNumber"; import LineControls from "./LineControls"; import LinesWithClickableInterior from "./LinesWithClickableInterior"; @@ -107,6 +109,8 @@ export const scope = { getCSSColor, useRange, useAnimationFrame, + useCallback, + useRef, useState, useEffect, Worldview, @@ -114,6 +118,7 @@ export const scope = { last, remove, sample, + inScreenshotTests, polygonGenerator, styled, @@ -139,6 +144,7 @@ export const scope = { PolygonBuilder, Overlay, Text, + GLText, GLTFScene, withPose, getRayFromClick, @@ -152,6 +158,7 @@ export default function WorldviewCodeEditor({ code, nonEditableCode = "", insertCodeSandboxStyle, + noInline, ...rest }) { const hashUrl = getHashUrlByComponentName(componentName); @@ -162,29 +169,37 @@ export default function WorldviewCodeEditor({ ${code} `; + const render = noInline + ? "" + : ` +render( +
+ +
+); +`; + const codeSandboxCode = ` // regl-worldview example: ${componentName} // docs: ${docUrl} ${insertCodeSandboxStyle ? 'import "./utils/codeSandboxStyleFix.css"; // #CODE_SANDBOX_ONLY' : ""} import ReactDOM from "react-dom"; -${copyCode} +${nonEditableCode} -function App() { - return ( -
- -
- ); +function render(content) { + ReactDOM.render(content, document.getElementById("root")); } -ReactDOM.render(, document.getElementById("root")); - `; +${code} +${render} +`; return ( /jest/jsTransform.js", - "^.+\\.css$": "/jest/cssTransform.js", + "^.+\\.(js|jsx)$": "babel-jest", "^.+\\.ne$": "/jest/neTransform.js", "^(?!.*\\.(js|jsx|css|json)$)": "/jest/fileTransform.js", }, moduleDirectories: ["/packages", "node_modules"], moduleFileExtensions: ["web.js", "js", "json", "web.jsx", "jsx", "node"], - resetMocks: true, + restoreMocks: true, setupFiles: [ "/packages/webviz-core/src/test/setup.js", "/jest/configureEnzyme.js", "jest-canvas-mock", ], - setupTestFrameworkScriptFile: "/jest/setupTestFramework.js", + setupTestFrameworkScriptFile: "/packages/webviz-core/src/test/setupTestFramework.js", moduleNameMapper: { - "worker-loader!./PngWorker.js": "/packages/webviz-core/src/test/MockWorker.js", - "worker-loader!.*/UserNodePlayer/.+Worker": - "/packages/webviz-core/src/players/UserNodePlayer/worker.mock.js", + "worker-loader!.*": "/packages/webviz-core/src/test/MockWorker.js", "\\.svg$": "/packages/webviz-core/src/test/MockSvg.js", "react-monaco-editor": "/packages/webviz-core/src/test/stubs/MonacoEditor.js", + "\\.css$": "/jest/styleMock.js", }, }; diff --git a/jest/styleMock.js b/jest/styleMock.js new file mode 100644 index 000000000..151a86065 --- /dev/null +++ b/jest/styleMock.js @@ -0,0 +1,2 @@ +// @flow +module.exports = {}; diff --git a/package-lock.json b/package-lock.json index 001df6dbf..f590aca94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -356,6 +356,16 @@ "@babel/plugin-syntax-json-strings": "^7.2.0" } }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.7.4.tgz", + "integrity": "sha512-TbYHmr1Gl1UC7Vo2HVuj/Naci5BEGNZ0AJhzqD2Vpr6QPFWpUmBRLrIDjedzx7/CShq0bRDS2gI4FIs77VHLVQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.7.4" + } + }, "@babel/plugin-proposal-object-rest-spread": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz", @@ -376,6 +386,16 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" } }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.7.4.tgz", + "integrity": "sha512-JmgaS+ygAWDR/STPe3/7y0lNlHgS+19qZ9aC06nYLwQ/XB7c0q5Xs+ksFU3EDnp9EiEsO0dnRAOKeyLHTZuW3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-chaining": "^7.7.4" + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz", @@ -432,6 +452,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.7.4.tgz", + "integrity": "sha512-XKh/yIRPiQTOeBg0QJjEus5qiSKucKAiApNtO1psqG7D17xmE+X2i5ZqBEuSvo0HRuyPaKaSN/Gy+Ha9KFQolw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", @@ -450,6 +479,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.7.4.tgz", + "integrity": "sha512-2MqYD5WjZSbJdUagnJvIdSfkb/ucOC9/1fRJxm7GAxY6YQLWlUvkfxoNbUPcPLHJyetKUDQ4+yyuUyAoc0HriA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", @@ -3344,6 +3382,125 @@ "symbol.prototype.description": "^1.0.0" } }, + "airbnb-prop-types": { + "version": "2.15.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz", + "integrity": "sha1-UoeCAEOvHrRp9bCvDW9w2mxSqu8=", + "dev": true, + "requires": { + "array.prototype.find": "^2.1.0", + "function.prototype.name": "^1.1.1", + "has": "^1.0.3", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0", + "prop-types": "^15.7.2", + "prop-types-exact": "^1.2.0", + "react-is": "^16.9.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-abstract/-/es-abstract-1.17.2.tgz", + "integrity": "sha1-llsQr1ZZe2MdoVhywXpAXobB/UY=", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + }, + "dependencies": { + "is-regex": { + "version": "1.0.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha1-OdWJo1i/GJZ/cmlnEguPwa7XTq4=", + "dev": true, + "requires": { + "has": "^1.0.3" + } + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "function.prototype.name": { + "version": "1.1.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/function.prototype.name/-/function.prototype.name-1.1.2.tgz", + "integrity": "sha1-XN9518BdtAFZHf3oPjtwxRI+mkU=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "functions-have-names": "^1.2.0" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha1-9+RrWWiQRW23Tn9ul2yzJz0G+qs=", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha1-9Pa9GBrXfwBrXs5gvQtvOY/3Smc=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true + }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha1-7hzwQVPeArsJP+wzaDkA9XzlOZs=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + } + } + }, "ajv": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", @@ -3608,6 +3765,81 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.find": { + "version": "2.1.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/array.prototype.find/-/array.prototype.find-2.1.0.tgz", + "integrity": "sha1-Yw8ur3CjnmCKw1c+Rc+MzQ7emtc=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-abstract/-/es-abstract-1.17.2.tgz", + "integrity": "sha1-llsQr1ZZe2MdoVhywXpAXobB/UY=", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha1-9+RrWWiQRW23Tn9ul2yzJz0G+qs=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha1-OdWJo1i/GJZ/cmlnEguPwa7XTq4=", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha1-9Pa9GBrXfwBrXs5gvQtvOY/3Smc=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true + } + } + }, "array.prototype.flat": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz", @@ -4281,6 +4513,18 @@ "integrity": "sha512-dHhDj5o4TEdrGSIzWQvTthZsEGCWGRKzSd+zO0xl8ie0zMf4to5nF64Cyu2zfM0mfB9BGRYQLE3YQliv9c+gPA==", "dev": true }, + "babel-plugin-styled-components": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.6.tgz", + "integrity": "sha512-gyQj/Zf1kQti66100PhrCRjI5ldjaze9O0M3emXRPAN80Zsf8+e1thpTpaXJXVHXtaM4/+dJEgZHyS9Its+8SA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-module-imports": "^7.0.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.11" + } + }, "babel-plugin-syntax-flow": { "version": "6.18.0", "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", @@ -5351,6 +5595,12 @@ } } }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", + "dev": true + }, "caniuse-lite": { "version": "1.0.30000918", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000918.tgz", @@ -6726,13 +6976,13 @@ } }, "css-to-react-native": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.2.tgz", - "integrity": "sha512-w99Fzop1FO8XKm0VpbQp3y5mnTnaS+rtCvS+ylSEOK76YXO5zoHQx/QMB1N54Cp+Ya9jB9922EHrh14ld4xmmw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz", + "integrity": "sha512-VOFaeZA053BqvvvqIA8c9n0+9vFppVBAHCp6JgFTtTMU3Mzi+XnelJ9XC9ul3BqFzZyQ5N+H0SnwsWT2Ebchxw==", "dev": true, "requires": { + "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", - "fbjs": "^0.8.5", "postcss-value-parser": "^3.3.0" } }, @@ -7066,7 +7316,7 @@ "dependencies": { "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -7079,7 +7329,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7615,36 +7865,101 @@ } }, "enzyme-adapter-react-16": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.11.2.tgz", - "integrity": "sha512-2ruTTCPRb0lPuw/vKTXGVZVBZqh83MNDnakMhzxhpJcIbneEwNy2Cv0KvL97pl57/GOazJHflWNLjwWhex5AAA==", + "version": "1.15.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz", + "integrity": "sha1-sW2y8OpCTVioCPnfhqtiEolaRQE=", "dev": true, "requires": { - "enzyme-adapter-utils": "^1.10.1", + "enzyme-adapter-utils": "^1.13.0", + "enzyme-shallow-equal": "^1.0.1", + "has": "^1.0.3", "object.assign": "^4.1.0", - "object.values": "^1.1.0", + "object.values": "^1.1.1", "prop-types": "^15.7.2", - "react-is": "^16.8.4", + "react-is": "^16.12.0", "react-test-renderer": "^16.0.0-0", - "semver": "^5.6.0" + "semver": "^5.7.0" }, "dependencies": { + "es-abstract": { + "version": "1.17.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-abstract/-/es-abstract-1.17.2.tgz", + "integrity": "sha1-llsQr1ZZe2MdoVhywXpAXobB/UY=", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha1-9+RrWWiQRW23Tn9ul2yzJz0G+qs=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha1-OdWJo1i/GJZ/cmlnEguPwa7XTq4=", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha1-9Pa9GBrXfwBrXs5gvQtvOY/3Smc=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true + }, "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha1-aKmezeNWt+kpWjxeDOMdyMlT3l4=", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", + "es-abstract": "^1.17.0-next.1", "function-bind": "^1.1.1", "has": "^1.0.3" } }, "prop-types": { "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=", "dev": true, "requires": { "loose-envify": "^1.4.0", @@ -7652,43 +7967,118 @@ "react-is": "^16.8.1" } }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "semver": { + "version": "5.7.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/semver/-/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", "dev": true } } }, "enzyme-adapter-utils": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.1.tgz", - "integrity": "sha512-oasinhhLoBuZsIkTe8mx0HiudtfErUtG0Ooe1FOplu/t4c9rOmyG5gtrBASK6u4whHIRWvv0cbZMElzNTR21SA==", + "version": "1.13.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz", + "integrity": "sha1-AciF3eIRS0aQv3QfjclM7jBg63g=", "dev": true, "requires": { - "function.prototype.name": "^1.1.0", + "airbnb-prop-types": "^2.15.0", + "function.prototype.name": "^1.1.2", "object.assign": "^4.1.0", - "object.fromentries": "^2.0.0", + "object.fromentries": "^2.0.2", "prop-types": "^15.7.2", - "semver": "^5.6.0" + "semver": "^5.7.1" }, "dependencies": { + "es-abstract": { + "version": "1.17.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-abstract/-/es-abstract-1.17.2.tgz", + "integrity": "sha1-llsQr1ZZe2MdoVhywXpAXobB/UY=", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "function.prototype.name": { + "version": "1.1.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/function.prototype.name/-/function.prototype.name-1.1.2.tgz", + "integrity": "sha1-XN9518BdtAFZHf3oPjtwxRI+mkU=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "functions-have-names": "^1.2.0" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha1-9+RrWWiQRW23Tn9ul2yzJz0G+qs=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha1-OdWJo1i/GJZ/cmlnEguPwa7XTq4=", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha1-9Pa9GBrXfwBrXs5gvQtvOY/3Smc=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true + }, "object.fromentries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", - "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "version": "2.0.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha1-SgnJubs4Q90PiazbUXp5TU81Wsk=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.11.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", "function-bind": "^1.1.1", - "has": "^1.0.1" + "has": "^1.0.3" } }, "prop-types": { "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=", "dev": true, "requires": { "loose-envify": "^1.4.0", @@ -7696,10 +8086,28 @@ "react-is": "^16.8.1" } }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "semver": { + "version": "5.7.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/semver/-/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "dev": true + } + } + }, + "enzyme-shallow-equal": { + "version": "1.0.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz", + "integrity": "sha1-ev4D2zgBybdt6EQGlAlkEqjZ1J4=", + "dev": true, + "requires": { + "has": "^1.0.3", + "object-is": "^1.0.2" + }, + "dependencies": { + "object-is": { + "version": "1.0.2", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha1-a4DrhP5FFJj2UAeYLwNaW0Re3sQ=", "dev": true } } @@ -9390,28 +9798,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, @@ -9422,14 +9830,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -9440,42 +9848,42 @@ }, "chownr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -9485,28 +9893,28 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "resolved": false, "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -9516,14 +9924,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -9540,7 +9948,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, @@ -9555,14 +9963,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "resolved": false, "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, @@ -9572,7 +9980,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -9582,7 +9990,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -9593,21 +10001,21 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -9617,14 +10025,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -9634,14 +10042,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "optional": true, @@ -9652,7 +10060,7 @@ }, "minizlib": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "resolved": false, "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, @@ -9662,7 +10070,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -9672,14 +10080,14 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "resolved": false, "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, @@ -9691,7 +10099,7 @@ }, "node-pre-gyp": { "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "resolved": false, "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, @@ -9710,7 +10118,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -9721,14 +10129,14 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "resolved": false, "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "resolved": false, "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, @@ -9739,7 +10147,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -9752,21 +10160,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -9776,21 +10184,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -9801,21 +10209,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "resolved": false, "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, @@ -9828,7 +10236,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -9837,7 +10245,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -9853,7 +10261,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, @@ -9863,49 +10271,49 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -9917,7 +10325,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -9927,7 +10335,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -9937,14 +10345,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "resolved": false, "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, @@ -9960,14 +10368,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, @@ -9977,14 +10385,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true, "optional": true @@ -10026,6 +10434,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/functions-have-names/-/functions-have-names-1.2.0.tgz", + "integrity": "sha1-g9p1g+TqDJrF/1MPczlLAz4L930=", + "dev": true + }, "fuse.js": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.3.0.tgz", @@ -11882,6 +12296,12 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "is-what": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.4.0.tgz", + "integrity": "sha512-oFdBRuSY9PocqPoUUseDXek4I+A1kWGigZGhuG+7GEkp0tRkek11adc0HbTEVsNvtojV7rp0uhf5LWtGvHzoOQ==", + "dev": true + }, "is-whitespace-character": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", @@ -14436,6 +14856,12 @@ "p-is-promise": "^1.1.0" } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==", + "dev": true + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -14559,6 +14985,15 @@ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, + "merge-anything": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-2.4.4.tgz", + "integrity": "sha512-l5XlriUDJKQT12bH+rVhAHjwIuXWdAIecGwsYjv2LJo+dA1AeRTmeQS+3QBpO6lEthBMDi2IUMpLC1yyRvGlwQ==", + "dev": true, + "requires": { + "is-what": "^3.3.1" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -16453,7 +16888,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -16809,6 +17244,17 @@ "object-assign": "^4.1.1" } }, + "prop-types-exact": { + "version": "1.2.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/prop-types-exact/-/prop-types-exact-1.2.0.tgz", + "integrity": "sha1-gl1r5GCUZjhII345JamMbpROmGk=", + "dev": true, + "requires": { + "has": "^1.0.3", + "object.assign": "^4.1.0", + "reflect.ownkeys": "^0.2.0" + } + }, "property-information": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.0.1.tgz", @@ -17154,27 +17600,14 @@ "dev": true }, "react": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", - "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz", + "integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.13.6" - }, - "dependencies": { - "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - } + "prop-types": "^15.6.2" } }, "react-base16-styling": { @@ -17359,21 +17792,21 @@ } }, "react-dom": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", - "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz", + "integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.13.6" + "scheduler": "^0.16.2" }, "dependencies": { "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==", "dev": true, "requires": { "loose-envify": "^1.1.0", @@ -17471,9 +17904,9 @@ } }, "react-is": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz", - "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", "dev": true }, "react-json-view": { @@ -17753,27 +18186,21 @@ } }, "react-test-renderer": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", - "integrity": "sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==", + "version": "16.12.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/react-test-renderer/-/react-test-renderer-16.12.0.tgz", + "integrity": "sha1-EUF//aV5MG1OhBp5TTIUDz2htD8=", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", "react-is": "^16.8.6", - "scheduler": "^0.13.6" + "scheduler": "^0.18.0" }, "dependencies": { - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "version": "0.18.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/scheduler/-/scheduler-0.18.0.tgz", + "integrity": "sha1-WQGtZlm8HY8/2vNut6Z7DWdGscQ=", "dev": true, "requires": { "loose-envify": "^1.1.0", @@ -18088,6 +18515,12 @@ "integrity": "sha512-F2GlWMWxCTJGRjJ+GSZcGDcVAj6Pbf77FKb4C9S8eni5Eah6UBGNwxNj8K1MTtmItdZH1Wx+EvIifHN2KKcQrw==", "dev": true }, + "reflect.ownkeys": { + "version": "0.2.0", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", + "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=", + "dev": true + }, "refractor": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/refractor/-/refractor-2.6.2.tgz", @@ -20524,6 +20957,50 @@ } } }, + "string-replace-loader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-2.2.0.tgz", + "integrity": "sha512-Ukt4ZC8+xVWdBRut3/iwnPJCNL1yV8AbVKXn8UcWdYrHgtuW4UDDAbBSi/J/CQDEWQBt824AJvPYahF23eJLRg==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -20592,6 +21069,26 @@ "function-bind": "^1.0.2" } }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha1-m9uKxqvW1gKxek7TIYcNL43O/HQ=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://artifactory.robot.car/artifactory/api/npm/npm-virtual/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha1-RAMUsVmWyGbOigNBiU1FGGIAxdk=", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -20680,52 +21177,46 @@ } }, "styled-components": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.4.10.tgz", - "integrity": "sha512-TA8ip8LoILgmSAFd3r326pKtXytUUGu5YWuqZcOQVwVVwB6XqUMn4MHW2IuYJ/HAD81jLrdQed8YWfLSG1LX4Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.4.1.tgz", + "integrity": "sha512-RNqj14kYzw++6Sr38n7197xG33ipEOktGElty4I70IKzQF1jzaD1U4xQ+Ny/i03UUhHlC5NWEO+d8olRCDji6g==", "dev": true, "requires": { - "buffer": "^5.0.3", - "css-to-react-native": "^2.0.3", - "fbjs": "^0.8.16", - "hoist-non-react-statics": "^2.5.0", + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@emotion/is-prop-valid": "^0.8.1", + "@emotion/unitless": "^0.7.0", + "babel-plugin-styled-components": ">= 1", + "css-to-react-native": "^2.2.2", + "memoize-one": "^5.0.0", + "merge-anything": "^2.2.4", "prop-types": "^15.5.4", - "react-is": "^16.3.1", + "react-is": "^16.6.0", "stylis": "^3.5.0", "stylis-rule-sheet": "^0.0.10", - "supports-color": "^3.2.3" + "supports-color": "^5.5.0" }, "dependencies": { - "buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "@emotion/is-prop-valid": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.5.tgz", + "integrity": "sha512-6ZODuZSFofbxSbcxwsFz+6ioPjb0ISJRRPLZ+WIbjcU2IMU0Io+RGQjjaTgOvNQl007KICBm7zXQaYQEC1r6Bg==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "@emotion/memoize": "0.7.3" } }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "@emotion/memoize": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==", "dev": true }, - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", + "@emotion/unitless": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.4.tgz", + "integrity": "sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ==", "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } } } }, diff --git a/package.json b/package.json index 9132195ec..beb7154f4 100755 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "@babel/cli": "^7.1.5", "@babel/core": "^7.2.0", "@babel/plugin-proposal-class-properties": "^7.2.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4", "@babel/plugin-proposal-object-rest-spread": "^7.2.0", + "@babel/plugin-proposal-optional-chaining": "^7.7.4", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-modules-commonjs": "^7.2.0", "@babel/plugin-transform-runtime": "^7.2.0", @@ -31,7 +33,7 @@ "case-sensitive-paths-webpack-plugin": "^2.1.2", "css-loader": "^1.0.1", "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.11.2", + "enzyme-adapter-react-16": "^1.15.2", "eslint": "^5.9.0", "eslint-config-prettier": "^3.3.0", "eslint-config-problems": "^2.0.0", @@ -68,10 +70,10 @@ "prettier": "1.15.2", "quickhull3d": "2.0.4", "raw-loader": "^0.5.1", - "react": "16.8.6", + "react": "16.10.2", "react-container-dimensions": "^1.4.1", "react-copy-to-clipboard": "^5.0.1", - "react-dom": "16.8.6", + "react-dom": "16.10.2", "react-hooks-testing-library": "^0.4.0", "react-hot-loader": "4.8.2", "react-live": "^1.12.0", @@ -99,8 +101,9 @@ "seedrandom": "^2.4.4", "sinon": "^7.1.1", "storybook-chrome-screenshot": "^1.4.0", + "string-replace-loader": "^2.2.0", "style-loader": "^0.23.1", - "styled-components": "3.4.10", + "styled-components": "4.4.1", "terser-webpack-plugin": "^1.1.0", "text-encoding": "^0.7.0", "unist-util-visit": "^1.4.0", @@ -112,8 +115,8 @@ }, "dependencies": {}, "scripts": { - "bootstrap": "npm install && lerna bootstrap --hoist react", - "install-ci": "npm ci && lerna bootstrap --hoist react", + "bootstrap": "npm install && lerna bootstrap --hoist \"{react,react-dom}\"", + "install-ci": "npm ci && lerna bootstrap --hoist \"{react,react-dom}\"", "build": "lerna run build && webpack", "watch": "lerna run watch --parallel", "clean": "lerna run clean", @@ -128,15 +131,12 @@ "flow": "flow", "flow-typed-rebuild": "rm -rf flow-typed/ && flow-typed install && flow-typed install --rootDir packages/@cruise-automation/hooks && flow-typed install --rootDir packages/@cruise-automation/button && flow-typed install --rootDir packages/@cruise-automation/tooltip && flow-typed install --rootDir packages/react-key-listener && flow-typed install --rootDir packages/regl-worldview", "docs": "DEV_SERVER=true webpack-dev-server", - "docs-deploy": "cp -r docs/public __temp_deploy__ && cp --remove-destination packages/webviz-core/public/index.html __temp_deploy__/try/index.html && echo 'webviz.io' > __temp_deploy__/CNAME && gh-pages -d __temp_deploy__ && rm -rf __temp_deploy__", + "docs-deploy": "cp -r docs/public __temp_deploy__ && cp --remove-destination packages/webviz-core/public/index.html __temp_deploy__/app/index.html && echo 'webviz.io' > __temp_deploy__/CNAME && gh-pages -d __temp_deploy__ && rm -rf __temp_deploy__", "publish": "lerna run clean && lerna run build && lerna publish", "screenshot": "NODE_ENV=development storybook-chrome-screenshot -c stories --parallel 8 --inject-files stories/injected.js --browser-timeout 1200000", "screenshot-ci": "npm run screenshot -- --puppeteer-launch-config '{\"executablePath\": \"/usr/bin/google-chrome\", \"headless\": false, \"args\":[\"--use-gl=swiftshader\", \"--no-sandbox\",\"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\", \"--headless\", \"--mute-audio\", \"--user-agent=PuppeteerTestingChrome/72.\"]}'", "screenshot-debug": "npm run screenshot -- --parallel 1 --debug --puppeteer-launch-config '{\"headless\": false, \"--args\":[\"--user-agent=PuppeteerTestingChrome/72.\"]}'", "ci-commands-for-cruise-proprietary-repo": "npm run install-ci && NODE_ENV=production npm run build && npm run lint && npm run flow && npm test && NODE_ENV=production npm run screenshot-ci", "ci": "npm run ci-commands-for-cruise-proprietary-repo && reg-suit run --verbose" - }, - "workspaces": [ - "packages/*" - ] + } } diff --git a/packages/@cruise-automation/hooks/src/useAbortable.test.js b/packages/@cruise-automation/hooks/src/useAbortable.test.js index 0e7539070..5187111da 100644 --- a/packages/@cruise-automation/hooks/src/useAbortable.test.js +++ b/packages/@cruise-automation/hooks/src/useAbortable.test.js @@ -27,6 +27,11 @@ function signal(): ResolvablePromise { } describe("useAbortable", () => { + beforeEach(() => { + // Suppress "test was not wrapped in act" error which is kind of hard to fix with this hook. + jest.spyOn(console, "error").mockReturnValue(); + }); + const Test = React.forwardRef((props, ref) => { const { action, cleanup } = props; const [value, abort] = useAbortable("pending", action, cleanup || (() => {}), [action]); diff --git a/packages/regl-worldview/package-lock.json b/packages/regl-worldview/package-lock.json index 43a76c5a3..466c9ed74 100755 --- a/packages/regl-worldview/package-lock.json +++ b/packages/regl-worldview/package-lock.json @@ -1,6 +1,6 @@ { "name": "regl-worldview", - "version": "0.2.3", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -19,6 +19,11 @@ } } }, + "@mapbox/tiny-sdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz", + "integrity": "sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg==" + }, "batch-processor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", @@ -79,6 +84,11 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memoize-weak": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/memoize-weak/-/memoize-weak-1.0.2.tgz", @@ -122,6 +132,11 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz", "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=" + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" } } } diff --git a/packages/regl-worldview/package.json b/packages/regl-worldview/package.json index 6a98e944d..3a7a29eed 100755 --- a/packages/regl-worldview/package.json +++ b/packages/regl-worldview/package.json @@ -1,6 +1,6 @@ { "name": "regl-worldview", - "version": "0.2.3", + "version": "0.4.0", "description": "A reusable component for rendering 2D and 3D views using regl", "license": "Apache-2.0", "repository": "cruise-automation/webviz/tree/master/packages/regl-worldview", @@ -26,15 +26,18 @@ }, "dependencies": { "@babel/runtime": "^7.3.1", + "@mapbox/tiny-sdf": "^1.1.1", "distance-to-line-segment": "0.2.0", "earcut": "^2.1.3", "gl-matrix": ">=2.6.1", "lodash": "^4.17.11", + "memoize-one": "^5.1.1", "memoize-weak": "^1.0.2", "normalize-wheel": "1.0.1", "react-container-dimensions": "1.3.3", "regl": "^1.3.1", - "reselect": "^3.0.1" + "reselect": "^3.0.1", + "shallowequal": "1.1.0" }, "devDependencies": { "flow-bin": "0.80.0" diff --git a/packages/regl-worldview/src/Worldview.js b/packages/regl-worldview/src/Worldview.js index 6ab2660e3..92ddb9007 100755 --- a/packages/regl-worldview/src/Worldview.js +++ b/packages/regl-worldview/src/Worldview.js @@ -62,7 +62,12 @@ type State = {| worldviewContext: WorldviewContext, |}; -function handleWorldviewMouseInteraction(objects: MouseEventObject[], ray: Ray, e: MouseEvent, handler: MouseHandler) { +function handleWorldviewMouseInteraction( + objects: MouseEventObject[], + ray: Ray, + e: SyntheticMouseEvent, + handler: MouseHandler +) { const args = { ray, objects }; try { @@ -153,25 +158,35 @@ export class WorldviewBase extends React.Component { if (!this._tick) { this._tick = requestAnimationFrame(() => { this._tick = undefined; - worldviewContext.paint(); + try { + worldviewContext.paint(); + } catch (error) { + // Regl automatically tries to reconnect when losing the canvas 3d context. + // We should log this error, but it's not important to throw it. + if (error.message === "(regl) context lost") { + console.warn(error); + } else { + throw error; + } + } }); } } - _onDoubleClick = (e: MouseEvent) => { + _onDoubleClick = (e: SyntheticMouseEvent) => { this._onMouseInteraction(e, "onDoubleClick"); }; - _onMouseDown = (e: MouseEvent) => { + _onMouseDown = (e: SyntheticMouseEvent) => { this._dragStartPos = { x: e.clientX, y: e.clientY }; this._onMouseInteraction(e, "onMouseDown"); }; - _onMouseMove = (e: MouseEvent) => { + _onMouseMove = (e: SyntheticMouseEvent) => { this._onMouseInteraction(e, "onMouseMove"); }; - _onMouseUp = (e: MouseEvent) => { + _onMouseUp = (e: SyntheticMouseEvent) => { this._onMouseInteraction(e, "onMouseUp"); const { _dragStartPos } = this; if (_dragStartPos) { @@ -185,7 +200,7 @@ export class WorldviewBase extends React.Component { } }; - _onMouseInteraction = (e: MouseEvent, mouseEventName: MouseEventEnum) => { + _onMouseInteraction = (e: SyntheticMouseEvent, mouseEventName: MouseEventEnum) => { const { worldviewContext } = this.state; const worldviewHandler = this.props[mouseEventName]; @@ -216,13 +231,16 @@ export class WorldviewBase extends React.Component { worldviewContext .readHitmap(canvasX, canvasY, !!this.props.enableStackedObjectEvents, this.props.maxStackedObjectCount) .then((mouseEventsWithCommands) => { - if (worldviewHandler) { - const mouseEvents = mouseEventsWithCommands.map(([mouseEventObject]) => mouseEventObject); - handleWorldviewMouseInteraction(mouseEvents, ray, e, worldviewHandler); - } - const mouseEventsByCommand: Map> = aggregate(mouseEventsWithCommands); + const mouseEventsByCommand: Map, Array> = aggregate(mouseEventsWithCommands); for (const [command, mouseEvents] of mouseEventsByCommand.entries()) { command.handleMouseEvent(mouseEvents, ray, e, mouseEventName); + if (e.isPropagationStopped()) { + break; + } + } + if (worldviewHandler && !e.isPropagationStopped()) { + const mouseEvents = mouseEventsWithCommands.map(([mouseEventObject]) => mouseEventObject); + handleWorldviewMouseInteraction(mouseEvents, ray, e, worldviewHandler); } }) .catch((e) => { diff --git a/packages/regl-worldview/src/WorldviewContext.js b/packages/regl-worldview/src/WorldviewContext.js index ada7eee51..bab279082 100755 --- a/packages/regl-worldview/src/WorldviewContext.js +++ b/packages/regl-worldview/src/WorldviewContext.js @@ -9,6 +9,7 @@ import debounce from "lodash/debounce"; import * as React from "react"; import createREGL from "regl"; +import shallowequal from "shallowequal"; import { camera, CameraStore } from "./camera/index"; import Command from "./commands/Command"; @@ -26,6 +27,7 @@ import type { import { getIdFromPixel, intToRGB } from "./utils/commandUtils"; import { getNodeEnv } from "./utils/common"; import HitmapObjectIdManager from "./utils/HitmapObjectIdManager"; +import queuePromise from "./utils/queuePromise"; import { getRayFromClick } from "./utils/Raycast"; type Props = any; @@ -45,7 +47,7 @@ type InitializedData = { }; export type DrawInput = { - instance: React.Component, + instance: Command, reglCommand: RawCommand, children: Props, layerIndex: ?number, @@ -83,6 +85,10 @@ export class WorldviewContext { _drawCalls: Map, DrawInput> = new Map(); _paintCalls: Map = new Map(); _hitmapObjectIdManager: HitmapObjectIdManager = new HitmapObjectIdManager(); + _cachedReadHitmapCall: ?{ + arguments: any[], + result: Array<[MouseEventObject, Command]>, + } = undefined; // store every compiled command object compiled for debugging purposes reglCommandObjects: { stats: { count: number } }[] = []; counters: { paint?: number, render?: number } = {}; @@ -116,7 +122,12 @@ export class WorldviewContext { const regl = this._instrumentCommands( createREGL({ canvas, - extensions: ["angle_instanced_arrays", "oes_texture_float", "oes_element_index_uint"], + extensions: [ + "angle_instanced_arrays", + "oes_texture_float", + "oes_element_index_uint", + "oes_standard_derivatives", + ], profile: getNodeEnv() !== "production", }) ); @@ -201,6 +212,7 @@ export class WorldviewContext { if (!this.initializedData) { return; } + this._cachedReadHitmapCall = null; // clear the cache every time we paint const { regl, camera } = this.initializedData; this._clearCanvas(regl); camera.draw(this.cameraStore.state, () => { @@ -217,113 +229,134 @@ export class WorldviewContext { _debouncedPaint = debounce(this.paint, 10); - readHitmap( - canvasX: number, - canvasY: number, - enableStackedObjectEvents: boolean, - maxStackedObjectCount: number - ): Promise> { - if (!this.initializedData) { - return new Promise((_, reject) => reject(new Error("regl data not initialized yet"))); - } - - const { regl, camera, _fbo } = this.initializedData; - const { width, height } = this.dimension; + readHitmap = queuePromise( + ( + canvasX: number, + canvasY: number, + enableStackedObjectEvents: boolean, + maxStackedObjectCount: number + ): Promise]>> => { + if (!this.initializedData) { + return Promise.reject(new Error("regl data not initialized yet")); + } + const args = [canvasX, canvasY, enableStackedObjectEvents, maxStackedObjectCount]; + + const cachedReadHitmapCall = this._cachedReadHitmapCall; + if (cachedReadHitmapCall) { + if (shallowequal(cachedReadHitmapCall.arguments, args)) { + // Make sure that we aren't returning the exact object identity of the mouseEventObject - we don't know what + // callers have done with it. + const result = cachedReadHitmapCall.result.map(([mouseEventObject, command]) => [ + { ...mouseEventObject }, + command, + ]); + return Promise.resolve(result); + } + this._cachedReadHitmapCall = undefined; + } - const x = canvasX; - // 0,0 corresponds to the bottom left in the webgl context, but the top left in window coordinates - const y = height - canvasY; - - // regl will only resize the framebuffer if the size changed - // it uses floored whole pixel values - _fbo.resize(Math.floor(width), Math.floor(height)); - - return new Promise((resolve) => { - // tell regl to use a framebuffer for this render - regl({ framebuffer: _fbo })(() => { - // clear the framebuffer - regl.clear({ color: intToRGB(0), depth: 1 }); - let currentObjectId = 0; - const excludedObjects = []; - const mouseEventsWithCommands = []; - let counter = 0; - - camera.draw(this.cameraStore.state, () => { - // Every iteration in this loop clears the framebuffer, draws the hitmap objects that have NOT already been - // seen to the framebuffer, and then reads the pixel under the cursor to find the object on top. - // If `enableStackedObjectEvents` is false, we only do this iteration once - we only resolve with 0 or 1 - // objects. - do { - if (counter >= maxStackedObjectCount) { - // Provide a max number of layers so this while loop doesn't crash the page. - console.error( - `Hit ${maxStackedObjectCount} iterations. There is either a bug or that number of rendered hitmap layers under the mouse cursor.` - ); - break; - } - counter++; - regl.clear({ color: intToRGB(0), depth: 1 }); - this._drawInput(true, excludedObjects); - - // it's possible to get x/y values outside the framebuffer size - // if the mouse quickly leaves the draw area during a read operation - // reading outside the bounds of the framebuffer causes errors - // and puts regl into a bad internal state. - // https://github.com/regl-project/regl/blob/28fbf71c871498c608d9ec741d47e34d44af0eb5/lib/read.js#L57 - if (x < Math.floor(width) && y < Math.floor(height) && x >= 0 && y >= 0) { - const pixel = new Uint8Array(4); - - // read pixel value from the frame buffer - regl.read({ - x, - y, - width: 1, - height: 1, - data: pixel, - }); - - currentObjectId = getIdFromPixel(pixel); - const mouseEventObject = this._hitmapObjectIdManager.getObjectByObjectHitmapId(currentObjectId); - - // Check an error case: if we see an ID/color that we don't know about, it means that some command is - // drawing a color into the hitmap that it shouldn't be. - if (currentObjectId > 0 && !mouseEventObject) { - console.error( - `Clicked on an unknown object with id ${currentObjectId}. This likely means that a command is painting an incorrect color into the hitmap.` - ); - } - // Check an error case: if we've already seen this object, then the getHitmapFromChildren function - // is not respecting the excludedObjects correctly and we should notify the user of a bug. - if ( - excludedObjects.some( - ({ object, instanceIndex }) => - object === mouseEventObject.object && instanceIndex === mouseEventObject.instanceIndex - ) - ) { + const { regl, camera, _fbo } = this.initializedData; + const { width, height } = this.dimension; + + const x = canvasX; + // 0,0 corresponds to the bottom left in the webgl context, but the top left in window coordinates + const y = height - canvasY; + + // regl will only resize the framebuffer if the size changed + // it uses floored whole pixel values + _fbo.resize(Math.floor(width), Math.floor(height)); + + return new Promise((resolve) => { + // tell regl to use a framebuffer for this render + regl({ framebuffer: _fbo })(() => { + // clear the framebuffer + regl.clear({ color: intToRGB(0), depth: 1 }); + let currentObjectId = 0; + const excludedObjects = []; + const mouseEventsWithCommands = []; + let counter = 0; + + camera.draw(this.cameraStore.state, () => { + // Every iteration in this loop clears the framebuffer, draws the hitmap objects that have NOT already been + // seen to the framebuffer, and then reads the pixel under the cursor to find the object on top. + // If `enableStackedObjectEvents` is false, we only do this iteration once - we only resolve with 0 or 1 + // objects. + do { + if (counter >= maxStackedObjectCount) { + // Provide a max number of layers so this while loop doesn't crash the page. console.error( - `Saw object twice when reading from hitmap. There is likely an error in getHitmapFromChildren`, - mouseEventObject + `Hit ${maxStackedObjectCount} iterations. There is either a bug or that number of rendered hitmap layers under the mouse cursor.` ); break; } + counter++; + regl.clear({ color: intToRGB(0), depth: 1 }); + this._drawInput(true, excludedObjects); + + // it's possible to get x/y values outside the framebuffer size + // if the mouse quickly leaves the draw area during a read operation + // reading outside the bounds of the framebuffer causes errors + // and puts regl into a bad internal state. + // https://github.com/regl-project/regl/blob/28fbf71c871498c608d9ec741d47e34d44af0eb5/lib/read.js#L57 + if (x < Math.floor(width) && y < Math.floor(height) && x >= 0 && y >= 0) { + const pixel = new Uint8Array(4); + + // read pixel value from the frame buffer + regl.read({ + x, + y, + width: 1, + height: 1, + data: pixel, + }); + + currentObjectId = getIdFromPixel(pixel); + const mouseEventObject = this._hitmapObjectIdManager.getObjectByObjectHitmapId(currentObjectId); + + // Check an error case: if we see an ID/color that we don't know about, it means that some command is + // drawing a color into the hitmap that it shouldn't be. + if (currentObjectId > 0 && !mouseEventObject) { + console.error( + `Clicked on an unknown object with id ${currentObjectId}. This likely means that a command is painting an incorrect color into the hitmap.` + ); + } + // Check an error case: if we've already seen this object, then the getHitmapFromChildren function + // is not respecting the excludedObjects correctly and we should notify the user of a bug. + if ( + excludedObjects.some( + ({ object, instanceIndex }) => + object === mouseEventObject.object && instanceIndex === mouseEventObject.instanceIndex + ) + ) { + console.error( + `Saw object twice when reading from hitmap. There is likely an error in getHitmapFromChildren`, + mouseEventObject + ); + break; + } - if (currentObjectId > 0 && mouseEventObject.object) { - const command = this._hitmapObjectIdManager.getCommandForObject(mouseEventObject.object); - excludedObjects.push(mouseEventObject); - if (command) { - mouseEventsWithCommands.push([mouseEventObject, command]); + if (currentObjectId > 0 && mouseEventObject.object) { + const command = this._hitmapObjectIdManager.getCommandForObject(mouseEventObject.object); + excludedObjects.push(mouseEventObject); + if (command) { + mouseEventsWithCommands.push([mouseEventObject, command]); + } } } - } - // If we haven't enabled stacked object events, break out of the loop immediately. - // eslint-disable-next-line no-unmodified-loop-condition - } while (currentObjectId !== 0 && enableStackedObjectEvents); - - resolve(mouseEventsWithCommands); + // If we haven't enabled stacked object events, break out of the loop immediately. + // eslint-disable-next-line no-unmodified-loop-condition + } while (currentObjectId !== 0 && enableStackedObjectEvents); + + this._cachedReadHitmapCall = { + arguments: args, + result: mouseEventsWithCommands, + }; + resolve(mouseEventsWithCommands); + }); }); }); - }); - } + } + ); _drawInput = (isHitmap?: boolean, excludedObjects?: MouseEventObject[]) => { if (isHitmap) { @@ -347,10 +380,10 @@ export class WorldviewContext { }; const hitmapProps = getChildrenForHitmap(children, assignNextColorsFn, excludedObjects || []); if (hitmapProps) { - cmd(hitmapProps); + cmd(hitmapProps, true); } } else if (!isHitmap) { - cmd(children); + cmd(children, false); } }); }; diff --git a/packages/regl-worldview/src/camera/camera.js b/packages/regl-worldview/src/camera/camera.js index a9bca1a15..b63b8fd84 100755 --- a/packages/regl-worldview/src/camera/camera.js +++ b/packages/regl-worldview/src/camera/camera.js @@ -73,12 +73,18 @@ export default (regl: any) => { view(context, props) { return this.getView(); }, + + // inverse of the view rotation, used for making objects always face the camera + billboardRotation(context, props) { + return selectors.billboardRotation(this.cameraState); + }, }, // adds view and projection as uniforms to every command // and makes them available in the shaders uniforms: { view: regl.context("view"), + billboardRotation: regl.context("billboardRotation"), projection: regl.context("projection"), }, }); diff --git a/packages/regl-worldview/src/camera/cameraStateSelectors.js b/packages/regl-worldview/src/camera/cameraStateSelectors.js index bd5fafb0c..7113a8a0f 100755 --- a/packages/regl-worldview/src/camera/cameraStateSelectors.js +++ b/packages/regl-worldview/src/camera/cameraStateSelectors.js @@ -189,9 +189,21 @@ const viewSelector: (CameraState) => Mat4 = createSelector( } ); +const billboardRotation: (CameraState) => Mat4 = createSelector( + orientationSelector, + targetHeadingSelector, + (orientation, targetHeading) => { + const m = mat4.identity(mat4.create()); + mat4.rotateZ(m, m, -targetHeading); + mat4.multiply(m, m, mat4.fromQuat(TEMP_MAT, orientation)); + return m; + } +); + export default { orientation: orientationSelector, position: positionSelector, targetHeading: targetHeadingSelector, view: viewSelector, + billboardRotation, }; diff --git a/packages/regl-worldview/src/commands/Arrows.js b/packages/regl-worldview/src/commands/Arrows.js index 21fda8baf..409c13ef7 100755 --- a/packages/regl-worldview/src/commands/Arrows.js +++ b/packages/regl-worldview/src/commands/Arrows.js @@ -20,8 +20,8 @@ import Cylinders from "./Cylinders"; const UNIT_X_VECTOR = Object.freeze([0, 0, 1]); type Props = { - children: Arrow[], ...CommonCommandProps, + children: Arrow[], }; function Arrows(props: Props) { diff --git a/packages/regl-worldview/src/commands/Axes.js b/packages/regl-worldview/src/commands/Axes.js index 26ac3082e..a743a4d30 100755 --- a/packages/regl-worldview/src/commands/Axes.js +++ b/packages/regl-worldview/src/commands/Axes.js @@ -8,7 +8,7 @@ import React from "react"; -import type { Pose, Color, Scale, Point, Vec3 } from "../types"; +import type { Point, Vec3, Line } from "../types"; import Lines from "./Lines"; const pointToVec3 = (p: Vec3): Point => ({ @@ -45,12 +45,7 @@ const zAxis = { color: { r: 0.11, g: 0.51, b: 0.92, a: 1 }, }; -type Axis = { - pose: Pose, - points: Point[], - scale: Scale, - color: Color, -}; +type Axis = Line; type Props = { children: Axis[], diff --git a/packages/regl-worldview/src/commands/Command.js b/packages/regl-worldview/src/commands/Command.js index 090a8ca95..b5fee29c9 100755 --- a/packages/regl-worldview/src/commands/Command.js +++ b/packages/regl-worldview/src/commands/Command.js @@ -1,12 +1,10 @@ +// @flow // Copyright (c) 2018-present, GM Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. // You may not use this file except in compliance with the License. -// TODO(JP): Should remove this and properly fix Flow. -/* eslint-disable flowtype/no-types-missing-file-annotation */ - import * as React from "react"; import type { @@ -15,10 +13,11 @@ import type { MouseEventEnum, RawCommand, Color, - Ray, + Point, MouseEventObject, } from "../types"; import { getNodeEnv } from "../utils/common"; +import { Ray } from "../utils/Raycast"; import { type WorldviewContextType } from "../WorldviewContext"; import WorldviewReactContext from "../WorldviewReactContext"; @@ -37,25 +36,26 @@ export type CommonCommandProps = { }; type Props = { - children?: T[], + ...CommonCommandProps, + children?: T[] | T, // Deprecated, but here for backwards compatibility drawProps?: T[], reglCommand: RawCommand, - ...CommonCommandProps, }; -export type CommandProps = Props; +export type CommandProps = Props; // Component to dispatch children (for drawing) and hitmap props and a reglCommand to the render loop to render with regl. export default class Command extends React.Component> { context: ?WorldviewContextType; static displayName = "Command"; - constructor(props) { + constructor(props: Props) { super(props); // In development put a check in to make sure the reglCommand prop is not mutated. // Similar to how react checks for unsupported or deprecated calls in a development build. if (getNodeEnv() !== "production") { + // $FlowFixMe this.shouldComponentUpdate = (nextProps: Props) => { if (nextProps.reglCommand !== this.props.reglCommand) { console.error("Changing the regl command prop on a is not supported."); @@ -66,7 +66,11 @@ export default class Command extends React.Component> { } componentDidMount() { - this.context.onMount(this, this.props.reglCommand); + const context = this.context; + if (!context) { + return; + } + context.onMount(this, this.props.reglCommand); this._updateContext(); } @@ -75,7 +79,11 @@ export default class Command extends React.Component> { } componentWillUnmount() { - this.context.onUnmount(this); + const context = this.context; + if (!context) { + return; + } + context.onUnmount(this); } _updateContext() { @@ -98,7 +106,12 @@ export default class Command extends React.Component> { }); } - handleMouseEvent(objects: MouseEventObject[], ray: Ray, e: MouseEvent, mouseEventName: MouseEventEnum) { + handleMouseEvent( + objects: MouseEventObject[], + ray: Ray, + e: SyntheticMouseEvent, + mouseEventName: MouseEventEnum + ) { const mouseHandler = this.props[mouseEventName]; if (!mouseHandler || !objects.length) { return; diff --git a/packages/regl-worldview/src/commands/Cones.js b/packages/regl-worldview/src/commands/Cones.js index 78635890c..2b914a098 100755 --- a/packages/regl-worldview/src/commands/Cones.js +++ b/packages/regl-worldview/src/commands/Cones.js @@ -18,6 +18,7 @@ const { points, sideFaces, endCapFaces } = createCylinderGeometry(30, true); const cones = fromGeometry(points, sideFaces.concat(endCapFaces)); +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(1); export default function Cones(props: { ...CommonCommandProps, children: Cone[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/Cubes.js b/packages/regl-worldview/src/commands/Cubes.js index 0882df52d..132e2a715 100755 --- a/packages/regl-worldview/src/commands/Cubes.js +++ b/packages/regl-worldview/src/commands/Cubes.js @@ -48,6 +48,7 @@ const cubes = fromGeometry( ] ); +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(1); export default function Cubes(props: { ...CommonCommandProps, children: Cube[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/Cylinders.js b/packages/regl-worldview/src/commands/Cylinders.js index 9e761d378..f141d4789 100755 --- a/packages/regl-worldview/src/commands/Cylinders.js +++ b/packages/regl-worldview/src/commands/Cylinders.js @@ -49,6 +49,7 @@ const { points, sideFaces, endCapFaces } = createCylinderGeometry(30, false); const cylinders = fromGeometry(points, sideFaces.concat(endCapFaces)); +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(1); export default function Cylinders(props: { ...CommonCommandProps, children: Cylinder[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/DrawPolygon/PolygonBuilder.test.js b/packages/regl-worldview/src/commands/DrawPolygon/PolygonBuilder.test.js index 39c6bc9b1..c50e69e83 100644 --- a/packages/regl-worldview/src/commands/DrawPolygon/PolygonBuilder.test.js +++ b/packages/regl-worldview/src/commands/DrawPolygon/PolygonBuilder.test.js @@ -36,7 +36,7 @@ const getArgs: (number[], ?Object) => ReglClickInfo = (point, object) => { }; }; -const event: (?boolean) => MouseEvent = (ctrlKey): any => ({ +const event: (?boolean) => SyntheticMouseEvent = (ctrlKey): any => ({ ctrlKey, stopPropagation: () => {}, preventDefault: () => {}, diff --git a/packages/regl-worldview/src/commands/DrawPolygon/index.js b/packages/regl-worldview/src/commands/DrawPolygon/index.js index 8bf2a640e..bbc197ad0 100644 --- a/packages/regl-worldview/src/commands/DrawPolygon/index.js +++ b/packages/regl-worldview/src/commands/DrawPolygon/index.js @@ -8,7 +8,7 @@ import React from "react"; -import type { Line, Point, Pose, Vec4, Vec3, Scale, GetChildrenForHitmap } from "../../types"; +import type { Line, Point, Vec3, Scale, GetChildrenForHitmap, SphereList } from "../../types"; import { vec4ToRGBA, vec3ToPoint } from "../../utils/commandUtils"; import Lines from "../Lines"; import Spheres from "../Spheres"; @@ -58,14 +58,6 @@ export class Polygon { export type DrawPolygonType = Polygon; -type DrawPolygonSphere = { - points: Point[], - pose: Pose, - scale: Scale, - colors: Vec4[], - originalMarkers: Object[], -}; - type Props = { children: DrawPolygonType[], }; @@ -152,24 +144,28 @@ const polygonPointsGetChildrenForHitmap: GetChildrenForHitmap = ( class PolygonPoints extends React.Component { render() { const polygons = this.props.children; - const sphereList: DrawPolygonSphere = { - points: [], - colors: [], - pose: POSE, - scale: DRAW_POINT_SCALE, - originalMarkers: [], - }; + const points = []; + const colors = []; + const originalMarkers = []; for (const poly of polygons) { const color = poly.active ? ACTIVE_POLYGON_COLOR : DEFAULT_COLOR; for (const point of poly.points) { const convertedPoint = vec3ToPoint(point.point); - sphereList.points.push(convertedPoint); - sphereList.colors.push(point.active ? ACTIVE_POINT_COLOR : color); - sphereList.originalMarkers.push(point); + points.push(convertedPoint); + colors.push(point.active ? ACTIVE_POINT_COLOR : color); + originalMarkers.push(point); } } + const sphereList: SphereList = { + points, + colors, + pose: POSE, + scale: DRAW_POINT_SCALE, + originalMarkers, + }; + return {[sphereList]}; } } diff --git a/packages/regl-worldview/src/commands/FilledPolygons.js b/packages/regl-worldview/src/commands/FilledPolygons.js index 4945920d2..b6525dc43 100755 --- a/packages/regl-worldview/src/commands/FilledPolygons.js +++ b/packages/regl-worldview/src/commands/FilledPolygons.js @@ -9,7 +9,7 @@ import earcut from "earcut"; import React from "react"; -import type { Vec3, PolygonType } from "../types"; +import type { Vec3, Point, PolygonType } from "../types"; import { shouldConvert, pointToVec3 } from "../utils/commandUtils"; import { getChildrenForHitmapWithOriginalMarker } from "../utils/getChildrenForHitmapDefaults"; import Triangles from "./Triangles"; @@ -32,7 +32,7 @@ function flatten3D(points: Vec3[]): Float32Array { return array; } -function getEarcutPoints(points: Vec3[]): Vec3[] { +function getEarcutPoints(points: Vec3[]): (Vec3 | Point)[] { const flattenedPoints = flatten3D(points); const indices = earcut(flattenedPoints, null, 3); const newPoints = []; @@ -53,7 +53,7 @@ function FilledPolygons({ children: polygons = [], ...rest }: Props) { // $FlowFixMe flow doesn't know how shouldConvert works const points: Vec3[] = shouldConvert(poly.points) ? poly.points.map(pointToVec3) : poly.points; const pose = poly.pose ? poly.pose : NO_POSE; - const earcutPoints: Vec3[] = getEarcutPoints(points); + const earcutPoints = getEarcutPoints(points); return { ...poly, points: earcutPoints, diff --git a/packages/regl-worldview/src/commands/GLTFScene.js b/packages/regl-worldview/src/commands/GLTFScene.js index d59d5cdcb..4825bc5fe 100644 --- a/packages/regl-worldview/src/commands/GLTFScene.js +++ b/packages/regl-worldview/src/commands/GLTFScene.js @@ -10,7 +10,7 @@ import { mat4 } from "gl-matrix"; import memoizeWeak from "memoize-weak"; import React, { useContext, useState, useEffect, useCallback, useDebugValue } from "react"; -import type { Pose, Scale, MouseHandler, GetChildrenForHitmap } from "../types"; +import type { Pose, Scale, MouseHandler } from "../types"; import { defaultBlend, pointToVec3, orientationToVec4 } from "../utils/commandUtils"; import { getChildrenForHitmapWithOriginalMarker } from "../utils/getChildrenForHitmapDefaults"; import parseGLB, { type GLBModel } from "../utils/parseGLB"; @@ -205,9 +205,9 @@ const drawModel = (regl) => { }, }); - return (props) => { + return (props, isHitmap) => { const drawCalls = getDrawCalls(props.model); - withContext(props, () => { + withContext(isHitmap ? { ...props, isHitmap } : props, () => { command(drawCalls); }); }; @@ -269,15 +269,6 @@ function useModel(model: string | (() => Promise)): ?GLBModel { ); } -// Override the default getChildrenForHitmap with our own implementation. -const getChildrenForHitmap: GetChildrenForHitmap = (prop: T, assignNextColors, excludedObjects) => { - const hitmapProp = getChildrenForHitmapWithOriginalMarker(prop, assignNextColors, excludedObjects); - if (hitmapProp) { - return { ...hitmapProp, isHitmap: true }; - } - return hitmapProp; -}; - export default function GLTFScene(props: Props) { const { children, model, ...rest } = props; @@ -297,7 +288,7 @@ export default function GLTFScene(props: Props) { } return ( - + {{ ...children, model: loadedModel, originalMarker: children }} ); diff --git a/packages/regl-worldview/src/commands/GLText.js b/packages/regl-worldview/src/commands/GLText.js new file mode 100644 index 000000000..5706abeb2 --- /dev/null +++ b/packages/regl-worldview/src/commands/GLText.js @@ -0,0 +1,424 @@ +// @flow + +import TinySDF from "@mapbox/tiny-sdf"; +import memoizeOne from "memoize-one"; +import React, { useState } from "react"; + +import type { Color } from "../types"; +import { defaultBlend, defaultDepth } from "../utils/commandUtils"; +import Command, { type CommonCommandProps } from "./Command"; +import { isColorDark, type TextMarker } from "./Text"; + +// The GLText command renders text from a Signed Distance Field texture. +// There are many external resources about SDFs and text rendering in WebGL, including: +// https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf +// https://blog.mapbox.com/drawing-text-with-signed-distance-fields-in-mapbox-gl-b0933af6f817 +// http://hack.chrons.me/opengl-text-rendering/ +// https://stackoverflow.com/questions/25956272/better-quality-text-in-webgl +// +// Approach +// ======== +// Characters from the font are measured using a and the SDFs are drawn into a texture up front +// (and whenever new characters are being rendered). Then one instanced draw call is made with an instance +// per character which reads from the corresponding place in the texture atlas. +// +// Possible future improvements +// ============================ +// - Add hitmap support. +// - Allow customization of font style, maybe highlight ranges. +// - Add a scaleInvariant option. +// - Consider a solid rectangular background instead of an outline. This is challenging because the +// instances currently overlap, so there will be z-fighting, but might be possible using the stencil buffer and multiple draw calls. +// - Somehow support kerning and more advanced font metrics. However, the web font APIs may not +// provide support for this. Some font info could be generated/stored offline, possibly including the atlas. +// - Explore multi-channel SDFs. + +type TextMarkerProps = TextMarker & { + billboard?: ?boolean, + highlightedIndices?: Array, + highlightColor?: ?Color, +}; +type Props = { + ...CommonCommandProps, + children: $ReadOnlyArray, + autoBackgroundColor?: boolean, +}; + +type FontAtlas = {| + textureData: Uint8Array, + textureWidth: number, + textureHeight: number, + charInfo: { + [char: string]: {| + x: number, + y: number, + width: number, + |}, + }, +|}; + +// Font size used in rendering the atlas. This is independent of the `scale` of the rendered text. +const FONT_SIZE = 40; +const MAX_ATLAS_WIDTH = 512; +const SDF_RADIUS = 8; +const CUTOFF = 0.25; +const BUFFER = 10; + +const BG_COLOR_LIGHT = Object.freeze({ r: 1, g: 1, b: 1, a: 1 }); +const BG_COLOR_DARK = Object.freeze({ r: 0, g: 0, b: 0, a: 1 }); + +const memoizedCreateCanvas = memoizeOne((font) => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + ctx.font = font; + return ctx; +}); + +// Build a single font atlas: a texture containing all characters and position/size data for each character. +const createMemoizedBuildAtlas = () => + memoizeOne( + (charSet: Set): FontAtlas => { + const tinySDF = new TinySDF(FONT_SIZE, BUFFER, SDF_RADIUS, CUTOFF, "sans-serif", "normal"); + const ctx = memoizedCreateCanvas(`${FONT_SIZE}px sans-serif`); + + let textureWidth = 0; + const rowHeight = FONT_SIZE + 2 * BUFFER; + const charInfo = {}; + + // Measure and assign positions to all characters + let x = 0; + let y = 0; + for (const char of charSet) { + const width = ctx.measureText(char).width; + const dx = Math.ceil(width) + 2 * BUFFER; + if (x + dx > MAX_ATLAS_WIDTH) { + x = 0; + y += rowHeight; + } + charInfo[char] = { x, y, width }; + x += dx; + textureWidth = Math.max(textureWidth, x); + } + + const textureHeight = y + rowHeight; + const textureData = new Uint8Array(textureWidth * textureHeight); + + // Use tiny-sdf to create SDF images for each character and copy them into a single texture + for (const char of charSet) { + const { x, y } = charInfo[char]; + const data = tinySDF.draw(char); + for (let i = 0; i < tinySDF.size; i++) { + for (let j = 0; j < tinySDF.size; j++) { + // if this character is near the right edge, we don't actually copy the whole square of data + if (x + j < textureWidth) { + textureData[textureWidth * (y + i) + x + j] = data[i * tinySDF.size + j]; + } + } + } + } + + return { textureData, textureWidth, textureHeight, charInfo }; + } + ); + +const vert = ` + precision mediump float; + + uniform mat4 projection, view, billboardRotation; + uniform float fontSize; + uniform vec2 atlasSize; + + // per-vertex attributes + attribute vec2 texCoord; + attribute vec2 position; + + // per-instance (character) attributes + attribute vec2 srcOffset; + attribute float srcWidth; + attribute vec2 destOffset; + + // per-marker attributes + attribute vec3 scale; + attribute float billboard; + attribute vec2 alignmentOffset; + attribute float enableBackground; + attribute float enableHighlight; + attribute vec4 foregroundColor; + attribute vec4 backgroundColor; + attribute vec4 highlightColor; + attribute vec3 posePosition; + attribute vec4 poseOrientation; + + varying vec2 vTexCoord; + varying float vEnableBackground; + varying vec4 vForegroundColor; + varying vec4 vBackgroundColor; + varying vec4 vHighlightColor; + varying float vEnableHighlight; + + // rotate a 3d point v by a rotation quaternion q + // like applyPose(), but we need to use a custom per-instance pose + vec3 rotate(vec3 v, vec4 q) { + vec3 temp = cross(q.xyz, v) + q.w * v; + return v + (2.0 * cross(q.xyz, temp)); + } + + void main () { + vec2 srcSize = vec2(srcWidth, fontSize); + vec3 markerSpacePos = scale * vec3((destOffset + position * srcSize + alignmentOffset) / fontSize, 0); + vec3 pos; + if (billboard == 1.0) { + pos = (billboardRotation * vec4(markerSpacePos, 1)).xyz + posePosition; + } else { + pos = rotate(markerSpacePos, poseOrientation) + posePosition; + } + gl_Position = projection * view * vec4(pos, 1); + vTexCoord = (srcOffset + texCoord * srcSize) / atlasSize; + vEnableBackground = enableBackground; + vForegroundColor = foregroundColor; + vBackgroundColor = backgroundColor; + vHighlightColor = highlightColor; + vEnableHighlight = enableHighlight; + } +`; + +const frag = ` + #extension GL_OES_standard_derivatives : enable + precision mediump float; + uniform mat4 projection; + uniform sampler2D atlas; + uniform float cutoff; + + varying vec2 vTexCoord; + varying float vEnableBackground; + varying vec4 vForegroundColor; + varying vec4 vBackgroundColor; + varying vec4 vHighlightColor; + varying float vEnableHighlight; + void main() { + float dist = texture2D(atlas, vTexCoord).a; + + // fwidth(dist) is used to provide some anti-aliasing. However it's currently only used + // when the solid background is enabled, because the alpha blending and + // depth test don't work together nicely for partially-transparent pixels. + float edgeStep = smoothstep(1.0 - cutoff - fwidth(dist), 1.0 - cutoff, dist); + if (vEnableHighlight > 0.5) { + gl_FragColor = mix(vHighlightColor, vec4(0, 0, 0, 1), edgeStep); + } else if (vEnableBackground > 0.5) { + float screenSize = fwidth(vTexCoord.x); + gl_FragColor = mix(vBackgroundColor, vForegroundColor, edgeStep); + } else { + gl_FragColor = vForegroundColor; + gl_FragColor.a *= step(1.0 - cutoff, dist); + } + + if (gl_FragColor.a == 0.) { + discard; + } + } +`; + +function makeTextCommand() { + // Keep the set of rendered characters around so we don't have to rebuild the font atlas too often. + const charSet = new Set(); + const memoizedBuildAtlas = createMemoizedBuildAtlas(); + + const command = (regl: any) => { + const atlasTexture = regl.texture(); + const drawText = regl({ + depth: defaultDepth, + blend: defaultBlend, + primitive: "triangle strip", + vert, + frag, + uniforms: { + atlas: atlasTexture, + atlasSize: () => [atlasTexture.width, atlasTexture.height], + fontSize: FONT_SIZE, + cutoff: CUTOFF, + }, + instances: regl.prop("instances"), + count: 4, + attributes: { + position: [[0, 0], [0, -1], [1, 0], [1, -1]], + texCoord: [[0, 0], [0, 1], [1, 0], [1, 1]], // flipped + srcOffset: (ctx, props) => ({ buffer: props.srcOffsets, divisor: 1 }), + destOffset: (ctx, props) => ({ buffer: props.destOffsets, divisor: 1 }), + srcWidth: (ctx, props) => ({ buffer: props.srcWidths, divisor: 1 }), + scale: (ctx, props) => ({ buffer: props.scale, divisor: 1 }), + alignmentOffset: (ctx, props) => ({ buffer: props.alignmentOffset, divisor: 1 }), + billboard: (ctx, props) => ({ buffer: props.billboard, divisor: 1 }), + foregroundColor: (ctx, props) => ({ buffer: props.foregroundColor, divisor: 1 }), + backgroundColor: (ctx, props) => ({ buffer: props.backgroundColor, divisor: 1 }), + highlightColor: (ctx, props) => ({ buffer: props.highlightColor, divisor: 1 }), + enableBackground: (ctx, props) => ({ buffer: props.enableBackground, divisor: 1 }), + enableHighlight: (ctx, props) => ({ buffer: props.enableHighlight, divisor: 1 }), + posePosition: (ctx, props) => ({ buffer: props.posePosition, divisor: 1 }), + poseOrientation: (ctx, props) => ({ buffer: props.poseOrientation, divisor: 1 }), + }, + }); + + return (props: $ReadOnlyArray) => { + let estimatedInstances = 0; + const prevNumChars = charSet.size; + for (const { text } of props) { + if (typeof text !== "string") { + throw new Error(`Expected typeof 'text' to be a string. But got type '${typeof text}' instead.`); + } + + for (const char of text) { + ++estimatedInstances; + charSet.add(char); + } + } + const charsChanged = charSet.size !== prevNumChars; + + const { textureData, textureWidth, textureHeight, charInfo } = memoizedBuildAtlas( + // only use a new set if the characters changed, since memoizeOne uses shallow equality + charsChanged ? new Set(charSet) : charSet + ); + + // re-upload texture only if characters were added + if (charsChanged) { + atlasTexture({ + data: textureData, + width: textureWidth, + height: textureHeight, + format: "alpha", + wrap: "clamp", + mag: "linear", + min: "linear", + }); + } + + const destOffsets = new Float32Array(estimatedInstances * 2); + const srcWidths = new Float32Array(estimatedInstances); + const srcOffsets = new Float32Array(estimatedInstances * 2); + + // These don't vary across characters within a marker, but the divisor can't be dynamic so we have to duplicate the data for each character. + const alignmentOffset = new Float32Array(estimatedInstances * 2); + const scale = new Float32Array(estimatedInstances * 3); + const foregroundColor = new Float32Array(estimatedInstances * 4); + const backgroundColor = new Float32Array(estimatedInstances * 4); + const highlightColor = new Float32Array(estimatedInstances * 4); + const enableBackground = new Float32Array(estimatedInstances); + const billboard = new Float32Array(estimatedInstances); + const posePosition = new Float32Array(estimatedInstances * 3); + const poseOrientation = new Float32Array(estimatedInstances * 4); + const enableHighlight = new Float32Array(estimatedInstances); + + let totalInstances = 0; + for (const marker of props) { + let totalWidth = 0; + let x = 0; + let y = 0; + let markerInstances = 0; + + const fgColor = marker.colors?.[0] || marker.color || BG_COLOR_LIGHT; + const outline = marker.colors?.[1] != null || command.autoBackgroundColor; + const bgColor = + marker.colors?.[1] || (command.autoBackgroundColor && isColorDark(fgColor) ? BG_COLOR_LIGHT : BG_COLOR_DARK); + const hlColor = marker?.highlightColor || { r: 1, b: 0, g: 1, a: 1 }; + + for (let i = 0; i < marker.text.length; i++) { + const char = marker.text[i]; + if (char === "\n") { + x = 0; + y = FONT_SIZE; + continue; + } + const info = charInfo[char]; + const index = totalInstances + markerInstances; + + // Calculate per-character attributes + destOffsets[2 * index + 0] = x; + destOffsets[2 * index + 1] = -y; + srcOffsets[2 * index + 0] = info.x + BUFFER; + srcOffsets[2 * index + 1] = info.y + BUFFER; + srcWidths[index] = info.width; + + x += info.width; + totalWidth = Math.max(totalWidth, x); + + // Copy per-marker attributes. These are duplicated per character so that we can draw + // all characters from all markers in a single draw call. + + billboard[index] = marker.billboard ?? true ? 1 : 0; + + scale[3 * index + 0] = marker.scale.x; + scale[3 * index + 1] = marker.scale.y; + scale[3 * index + 2] = marker.scale.z; + + posePosition[3 * index + 0] = marker.pose.position.x; + posePosition[3 * index + 1] = marker.pose.position.y; + posePosition[3 * index + 2] = marker.pose.position.z; + + poseOrientation[4 * index + 0] = marker.pose.orientation.x; + poseOrientation[4 * index + 1] = marker.pose.orientation.y; + poseOrientation[4 * index + 2] = marker.pose.orientation.z; + poseOrientation[4 * index + 3] = marker.pose.orientation.w; + + foregroundColor[4 * index + 0] = fgColor.r; + foregroundColor[4 * index + 1] = fgColor.g; + foregroundColor[4 * index + 2] = fgColor.b; + foregroundColor[4 * index + 3] = fgColor.a; + + backgroundColor[4 * index + 0] = bgColor.r; + backgroundColor[4 * index + 1] = bgColor.g; + backgroundColor[4 * index + 2] = bgColor.b; + backgroundColor[4 * index + 3] = bgColor.a; + + highlightColor[4 * index + 0] = hlColor.r; + highlightColor[4 * index + 1] = hlColor.g; + highlightColor[4 * index + 2] = hlColor.b; + highlightColor[4 * index + 3] = hlColor.a; + + enableHighlight[index] = marker.highlightedIndices && marker.highlightedIndices.includes(i) ? 1 : 0; + + enableBackground[index] = outline ? 1 : 0; + + ++markerInstances; + } + + const totalHeight = y + FONT_SIZE; + for (let i = 0; i < markerInstances; i++) { + alignmentOffset[2 * (totalInstances + i) + 0] = -totalWidth / 2; + alignmentOffset[2 * (totalInstances + i) + 1] = totalHeight / 2; + } + + totalInstances += markerInstances; + } + + drawText({ + instances: totalInstances, + + // per-character + srcOffsets, + destOffsets, + srcWidths, + + // per-marker + alignmentOffset, + billboard, + enableBackground, + enableHighlight, + foregroundColor, + backgroundColor, + highlightColor, + poseOrientation, + posePosition, + scale, + }); + }; + }; + command.autoBackgroundColor = false; + return command; +} + +export default function GLText(props: Props) { + const [command] = useState(() => makeTextCommand()); + // HACK: Worldview doesn't provide an easy way to pass a command-level prop into the regl commands, + // so just attach it to the command object for now. + command.autoBackgroundColor = props.autoBackgroundColor; + return ; +} diff --git a/packages/regl-worldview/src/commands/Grid.js b/packages/regl-worldview/src/commands/Grid.js index 10fd537b3..01fdbd98c 100755 --- a/packages/regl-worldview/src/commands/Grid.js +++ b/packages/regl-worldview/src/commands/Grid.js @@ -64,13 +64,13 @@ export function grid() { } type Props = { - count: number, ...CommonCommandProps, + count: number, }; // useful for rendering a grid for debugging in stories -function Grid({ count, ...rest }: Props) { +export default function Grid({ count, ...rest }: Props) { const children = { count }; return ( @@ -80,5 +80,3 @@ function Grid({ count, ...rest }: Props) { } Grid.defaultProps = { count: 6 }; - -export default React.memo(Grid); diff --git a/packages/regl-worldview/src/commands/Lines.js b/packages/regl-worldview/src/commands/Lines.js index 0445e97b0..1d7323f85 100755 --- a/packages/regl-worldview/src/commands/Lines.js +++ b/packages/regl-worldview/src/commands/Lines.js @@ -6,9 +6,10 @@ // found in the LICENSE file in the root directory of this source tree. // You may not use this file except in compliance with the License. +import flatten from "lodash/flatten"; import * as React from "react"; -import type { Line } from "../types"; +import type { Line, Vec4, Color, Pose } from "../types"; import { defaultBlend, withPose, toRGBA, shouldConvert, pointToVec3 } from "../utils/commandUtils"; import { nonInstancedGetChildrenForHitmap } from "../utils/getChildrenForHitmapDefaults"; import Command, { type CommonCommandProps } from "./Command"; @@ -87,6 +88,9 @@ attribute vec3 positionA; attribute vec3 positionB; attribute vec3 positionC; attribute vec3 positionD; +// per-instance pose attributes +attribute vec3 posePosition; +attribute vec4 poseRotation; uniform mat4 projection, view; uniform float viewportWidth; @@ -104,6 +108,12 @@ ${Object.keys(POINT_TYPES) #WITH_POSE +vec3 applyPoseInstance(vec3 point, vec4 rotation, vec3 position) { + // rotate the point and then add the position of the pose + // this function is defined in WITH_POSE + return rotate(point, rotation) + position; +} + vec2 rotateCCW(vec2 v) { return vec2(-v.y, v.x); } @@ -145,10 +155,10 @@ void main () { float scale = isTop ? 1. : -1.; mat4 projView = projection * view; - vec4 projA = projView * vec4(applyPose(positionA), 1); - vec4 projB = projView * vec4(applyPose(positionB), 1); - vec4 projC = projView * vec4(applyPose(positionC), 1); - vec4 projD = projView * vec4(applyPose(positionD), 1); + vec4 projA = projView * vec4(applyPose(applyPoseInstance(positionA, poseRotation, posePosition)), 1); + vec4 projB = projView * vec4(applyPose(applyPoseInstance(positionB, poseRotation, posePosition)), 1); + vec4 projC = projView * vec4(applyPose(applyPoseInstance(positionC, poseRotation, posePosition)), 1); + vec4 projD = projView * vec4(applyPose(applyPoseInstance(positionD, poseRotation, posePosition)), 1); vec2 aspectVec = vec2(viewportWidth / viewportHeight, 1.0); vec2 screenA = projA.xy / projA.w * aspectVec; @@ -240,6 +250,19 @@ const lines = (regl: any) => { [1, 0, 1, 1], // magenta ], }); + // The pose position and rotation buffers contain the identity position/rotation, for use when we don't have instanced + // poses. + const defaultPosePositionBuffer = regl.buffer({ + type: "float", + usage: "static", + data: flatten(new Array(VERTICES_PER_INSTANCE).fill([0, 0, 0])), + }); + const defaultPoseRotationBuffer = regl.buffer({ + type: "float", + usage: "static", + // Rotation array identity is [x: 0, y: 0, z: 0, w: 1] + data: flatten(new Array(VERTICES_PER_INSTANCE).fill([0, 0, 0, 1])), + }); // The buffers used for input position & color data const colorBuffer = regl.buffer({ type: "float" }); @@ -251,6 +274,9 @@ const lines = (regl: any) => { const positionBuffer1 = regl.buffer({ type: "float" }); const positionBuffer2 = regl.buffer({ type: "float" }); + const posePositionBuffer = regl.buffer({ type: "float" }); + const poseRotationBuffer = regl.buffer({ type: "float" }); + const command = regl( withPose({ vert, @@ -290,7 +316,7 @@ const lines = (regl: any) => { stride: (joined ? 1 : 2) * POINT_BYTES, divisor: 1, }), - positionC: (context, { joined, instances }) => ({ + positionC: (context, { joined }) => ({ buffer: positionBuffer2, offset: 2 * POINT_BYTES, stride: (joined ? 1 : 2) * POINT_BYTES, @@ -302,6 +328,14 @@ const lines = (regl: any) => { stride: (joined ? 1 : 2) * POINT_BYTES, divisor: 1, }), + posePosition: (context, { hasInstancedPoses }) => ({ + buffer: hasInstancedPoses ? posePositionBuffer : defaultPosePositionBuffer, + divisor: hasInstancedPoses ? 1 : 0, + }), + poseRotation: (context, { hasInstancedPoses }) => ({ + buffer: hasInstancedPoses ? poseRotationBuffer : defaultPoseRotationBuffer, + divisor: hasInstancedPoses ? 1 : 0, + }), }, count: VERTICES_PER_INSTANCE, instances: regl.prop("instances"), @@ -309,12 +343,13 @@ const lines = (regl: any) => { }) ); - // array reused for colors when monochrome - const monochromeColors = new Array(VERTICES_PER_INSTANCE); + let colorArray = new Float32Array(VERTICES_PER_INSTANCE * 4); let pointArray = new Float32Array(0); let allocatedPoints = 0; + let positionArray = new Float32Array(0); + let rotationArray = new Float32Array(0); - const fillPointArray = (points: any[], alreadyClosed: boolean, shouldClose: boolean) => { + function fillPointArray(points: any[], alreadyClosed: boolean, shouldClose: boolean) { const numTotalPoints = points.length + (shouldClose ? 3 : 2); if (allocatedPoints < numTotalPoints) { pointArray = new Float32Array(numTotalPoints * 3); @@ -346,7 +381,79 @@ const lines = (regl: any) => { pointArray.copyWithin(0, 3, 6); pointArray.copyWithin(n - 3, n - 6, n - 3); } - }; + } + + function fillPoseArrays(instances: number, poses: Pose[]): ?Error { + if (positionArray.length < instances * 3) { + positionArray = new Float32Array(instances * 3); + rotationArray = new Float32Array(instances * 4); + } + for (let index = 0; index < poses.length; index++) { + const positionOffset = index * 3; + const rotationOffset = index * 4; + const { position, orientation: r } = poses[index]; + const convertedPosition = Array.isArray(position) ? position : pointToVec3(position); + positionArray[positionOffset + 0] = convertedPosition[0]; + positionArray[positionOffset + 1] = convertedPosition[1]; + positionArray[positionOffset + 2] = convertedPosition[2]; + + const convertedRotation = Array.isArray(r) ? r : [r.x, r.y, r.z, r.w]; + rotationArray[rotationOffset + 0] = convertedRotation[0]; + rotationArray[rotationOffset + 1] = convertedRotation[1]; + rotationArray[rotationOffset + 2] = convertedRotation[2]; + rotationArray[rotationOffset + 3] = convertedRotation[3]; + } + } + + function convertColors(colors: any): Vec4[] { + return shouldConvert(colors) ? colors.map(toRGBA) : colors; + } + + function fillColorArray( + color: ?Color | ?Vec4, + colors: ?((Color | Vec4)[]), + monochrome: boolean, + shouldClose: boolean + ) { + if (monochrome) { + if (colorArray.length < VERTICES_PER_INSTANCE * 4) { + colorArray = new Float32Array(VERTICES_PER_INSTANCE * 4); + } + const monochromeColor = color || DEFAULT_MONOCHROME_COLOR; + const [convertedMonochromeColor] = convertColors([monochromeColor]); + const [r, g, b, a] = convertedMonochromeColor; + for (let index = 0; index < VERTICES_PER_INSTANCE; index++) { + const offset = index * 4; + colorArray[offset + 0] = r; + colorArray[offset + 1] = g; + colorArray[offset + 2] = b; + colorArray[offset + 3] = a; + } + } else if (colors) { + const length = shouldClose ? colors.length + 1 : colors.length; + if (colorArray.length < length * 4) { + colorArray = new Float32Array(length * 4); + } + const convertedColors = convertColors(colors); + for (let index = 0; index < convertedColors.length; index++) { + const offset = index * 4; + const [r, g, b, a] = convertedColors[index]; + colorArray[offset + 0] = r; + colorArray[offset + 1] = g; + colorArray[offset + 2] = b; + colorArray[offset + 3] = a; + } + + if (shouldClose) { + const [r, g, b, a] = convertedColors[0]; + const lastIndex = length - 1; + colorArray[lastIndex * 4 + 0] = r; + colorArray[lastIndex * 4 + 1] = g; + colorArray[lastIndex * 4 + 2] = b; + colorArray[lastIndex * 4 + 3] = a; + } + } + } // Disable depth for debug rendering (so lines stay visible) const render = (debug, commands) => { @@ -358,7 +465,7 @@ const lines = (regl: any) => { }; // Render one line list/strip - const renderLine = (props) => { + function renderLine(props) { const { debug, primitive = "lines", scaleInvariant = false } = props; const numInputPoints = props.points.length; @@ -375,52 +482,54 @@ const lines = (regl: any) => { positionBuffer2({ data: pointArray, usage: "dynamic" }); const monochrome = !(props.colors && props.colors.length); - let colors; - if (monochrome) { - if (shouldConvert(props.color)) { - colors = monochromeColors.fill(props.color ? toRGBA(props.color) : DEFAULT_MONOCHROME_COLOR); - } else { - colors = monochromeColors.fill(props.color || DEFAULT_MONOCHROME_COLOR); - } - } else { - if (shouldConvert(props.colors)) { - colors = props.colors.map(toRGBA); - } else { - colors = props.colors.slice(); - } - if (shouldClose) { - colors.push(colors[0]); - } - } - colorBuffer({ data: colors, usage: "dynamic" }); + fillColorArray(props.color, props.colors, monochrome, shouldClose); + colorBuffer({ data: colorArray, usage: "dynamic" }); const joined = primitive === "line strip"; const effectiveNumPoints = numInputPoints + (shouldClose ? 1 : 0); const instances = joined ? effectiveNumPoints - 1 : Math.floor(effectiveNumPoints / 2); + // fill instanced pose buffers + const { poses } = props; + const hasInstancedPoses = !!poses && poses.length > 0; + if (hasInstancedPoses && poses) { + if (instances !== poses.length) { + console.error(`Expected ${instances} poses but given ${poses.length} poses: will result in webgl error.`); + return; + } + fillPoseArrays(instances, poses); + posePositionBuffer({ data: positionArray, usage: "dynamic" }); + poseRotationBuffer({ data: rotationArray, usage: "dynamic" }); + } + render(debug, () => { - command({ - ...props, - joined, - primitive: "triangle strip", - alpha: debug ? 0.2 : 1, - monochrome, - instances, - scaleInvariant, - }); - if (debug) { - command({ - ...props, + // Use Object.assign because it's actually faster than babel's object spread polyfill. + command( + Object.assign({}, props, { joined, - primitive: "line strip", - alpha: 1, + primitive: "triangle strip", + alpha: debug ? 0.2 : 1, monochrome, instances, scaleInvariant, - }); + hasInstancedPoses, + }) + ); + if (debug) { + command( + Object.assign({}, props, { + joined, + primitive: "line strip", + alpha: 1, + monochrome, + instances, + scaleInvariant, + hasInstancedPoses, + }) + ); } }); - }; + } return (inProps: any) => { if (Array.isArray(inProps)) { diff --git a/packages/regl-worldview/src/commands/Points.js b/packages/regl-worldview/src/commands/Points.js index f5af1baae..704711d8e 100755 --- a/packages/regl-worldview/src/commands/Points.js +++ b/packages/regl-worldview/src/commands/Points.js @@ -63,6 +63,7 @@ const points = (regl: Regl) => { }); }; +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(1); export default function Points(props: { ...CommonCommandProps, children: PointType[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/Spheres.js b/packages/regl-worldview/src/commands/Spheres.js index cc3da9170..d676b9997 100755 --- a/packages/regl-worldview/src/commands/Spheres.js +++ b/packages/regl-worldview/src/commands/Spheres.js @@ -56,6 +56,7 @@ for (let j = 0; j < NUM_MERIDIANS; j++) { const spheres = fromGeometry(points, faces); +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(1); export default function Spheres(props: { ...CommonCommandProps, children: SphereList[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/Text.js b/packages/regl-worldview/src/commands/Text.js index 079d0f0c7..3ed261c7f 100755 --- a/packages/regl-worldview/src/commands/Text.js +++ b/packages/regl-worldview/src/commands/Text.js @@ -19,7 +19,7 @@ const BRIGHTNESS_THRESHOLD = 128; const DEFAULT_TEXT_COLOR = { r: 1, g: 1, b: 1, a: 1 }; const DEFAULT_BG_COLOR = { r: 0, g: 0, b: 0, a: 0.8 }; -type TextMarker = { +export type TextMarker = { name?: string, pose: Pose, scale: Scale, @@ -57,7 +57,7 @@ function insertGlobalCss() { cssHasBeenInserted = true; } -function isColorDark({ r, g, b }: Color): boolean { +export function isColorDark({ r, g, b }: Color): boolean { // ITU-R BT.709 https://en.wikipedia.org/wiki/Rec._709 // 0.2126 * 255 * r + 0.7152 * 255 * g + 0.0722 * 255 * b const luma = 54.213 * r + 182.376 * g + 18.411 * b; diff --git a/packages/regl-worldview/src/commands/Triangles.js b/packages/regl-worldview/src/commands/Triangles.js index d57a0fcf7..31bdf3440 100755 --- a/packages/regl-worldview/src/commands/Triangles.js +++ b/packages/regl-worldview/src/commands/Triangles.js @@ -149,15 +149,18 @@ const vertexColors = (regl) => const triangles = (regl: Regl) => { const single = regl(singleColor(regl)); const vertex = regl(vertexColors(regl)); - return (props: any) => { - const items = Array.isArray(props) ? props : [props]; + return (props: any, isHitmap: boolean) => { + const items: TriangleList[] = Array.isArray(props) ? props : [props]; const singleColorItems = []; const vertexColorItems = []; items.forEach((item) => { - if (item.colors && item.colors.length) { - vertexColorItems.push(item); - } else { - singleColorItems.push(item); + // If the item has onlyRenderInHitmap set, only render it in the hitmap. + if (isHitmap || !item.onlyRenderInHitmap) { + if (item.colors && item.colors.length) { + vertexColorItems.push(item); + } else { + singleColorItems.push(item); + } } }); @@ -166,6 +169,7 @@ const triangles = (regl: Regl) => { }; }; +const getChildrenForHitmap = createInstancedGetChildrenForHitmap(3); export default function Triangles(props: { ...CommonCommandProps, children: TriangleList[] }) { - return ; + return ; } diff --git a/packages/regl-worldview/src/commands/index.js b/packages/regl-worldview/src/commands/index.js index bbe57afd1..611763207 100755 --- a/packages/regl-worldview/src/commands/index.js +++ b/packages/regl-worldview/src/commands/index.js @@ -15,6 +15,7 @@ export { default as Cylinders } from "./Cylinders"; export { default as DrawPolygons, Polygon, PolygonPoint } from "./DrawPolygon/index"; export { default as PolygonBuilder } from "./DrawPolygon/PolygonBuilder"; export { default as FilledPolygons } from "./FilledPolygons"; +export { default as GLText } from "./GLText"; export { default as GLTFScene } from "./GLTFScene"; export { default as Grid } from "./Grid"; export { default as Lines } from "./Lines"; diff --git a/packages/regl-worldview/src/stories/GLText.stories.js b/packages/regl-worldview/src/stories/GLText.stories.js new file mode 100644 index 000000000..a4aa0ea42 --- /dev/null +++ b/packages/regl-worldview/src/stories/GLText.stories.js @@ -0,0 +1,177 @@ +// @flow + +import { storiesOf } from "@storybook/react"; +import { quat } from "gl-matrix"; +import { range } from "lodash"; +import React, { useState, useLayoutEffect } from "react"; +import { withScreenshot } from "storybook-chrome-screenshot"; +import tinyColor from "tinycolor2"; + +import { Axes } from "../commands"; +import type { Color } from "../types"; +import { vec4ToOrientation } from "../utils/commandUtils"; +import Container from "./Container"; +import inScreenshotTests from "stories/inScreenshotTests"; + +import { GLText } from ".."; + +function textMarkers({ + text, + billboard, + background = true, +}: { + text: string, + billboard?: ?boolean, + background?: ?boolean, +}) { + const radius = 10; + const count = 10; + return new Array(count).fill().map((_, i) => { + const angle = (2 * Math.PI * i) / count; + const color = { r: 0, g: i / count, b: i / count, a: 1 }; + return { + text: `${text} ${i}`, + pose: { + position: { x: radius * Math.cos(angle), y: radius * Math.sin(angle), z: 0 }, + orientation: vec4ToOrientation(quat.rotateZ(quat.create(), quat.create(), Math.PI / 2 + angle)), + }, + scale: { x: 1, y: 1, z: 1 }, + color, + colors: background && i % 4 === 0 ? [color, { r: 1, g: 1, b: 0, a: 1 }] : undefined, + billboard, + }; + }); +} + +storiesOf("Worldview/GLText", module) + .addDecorator(withScreenshot({ delay: 200 })) + .add("billboard", () => ( + + {textMarkers({ text: "Hello\nWorldview", billboard: true })} + + + )) + .add("non-billboard", () => ( + + {textMarkers({ text: "Hello\nWorldview", billboard: false })} + + + )) + .add("no background", () => ( + + {textMarkers({ text: "Hello\nWorldview", billboard: false, background: false })} + + + )) + .add("autoBackgroundColor", () => ( + + {textMarkers({ text: "Hello\nWorldview" })} + + + )) + .add("changing text", () => { + function Example() { + const [text, setText] = useState("Hello\nWorldview"); + useLayoutEffect(() => { + let i = 0; + const id = setInterval(() => { + setText(`New text! ${++i}`); + if (inScreenshotTests()) { + clearInterval(id); + } + }, 100); + return () => clearInterval(id); + }, []); + return ( + + {textMarkers({ text })} + + + ); + } + return ; + }) + .add("highlighted text", () => { + const Example = () => { + const [searchText, setSearchText] = useState("ello\nW"); + const markers = textMarkers({ text: "Hello\nWorldview" }).map((marker) => { + if (!searchText) { + return marker; + } + const highlightedIndices = new Set(); + let match; + let regex; + try { + regex = new RegExp(searchText, "ig"); + } catch (e) { + return marker; + } + while ((match = regex.exec(marker.text)) !== null) { + // $FlowFixMe - Flow doesn't understand the while loop terminating condition. + range(0, match[0].length).forEach((i) => { + // $FlowFixMe - Flow doesn't understand the while loop terminating condition. + highlightedIndices.add(match.index + i); + }); + } + return { ...marker, highlightedIndices: Array.from(highlightedIndices) }; + }); + + return ( +
+
+ + {markers} + + +
+
+ + setSearchText(e.target.value)} + /> +
+
+ ); + }; + + return ; + }) + .add("custom highlight color", () => { + const Example = () => { + const [highlightColor, setHighlightColor] = useState({ r: 1, b: 0.5, g: 0.5, a: 1 }); + const markers = textMarkers({ text: "Hello\nWorldview", background: false }).map((marker) => ({ + ...marker, + highlightedIndices: [0, 1, 2, 3, 4], + highlightColor, + })); + + return ( +
+ +
+ + { + const hex = e.target.value; + const { r, g, b } = tinyColor(hex).toRgb(); + setHighlightColor({ r: r / 255, g: g / 255, b: b / 255, a: 1 }); + }} + /> +
+ + {markers} + +
+
+ ); + }; + + return ; + }); diff --git a/packages/regl-worldview/src/stories/assertionStories/Triangles.stories.js b/packages/regl-worldview/src/stories/assertionStories/Triangles.stories.js index 97e29692e..a051dd529 100644 --- a/packages/regl-worldview/src/stories/assertionStories/Triangles.stories.js +++ b/packages/regl-worldview/src/stories/assertionStories/Triangles.stories.js @@ -57,6 +57,11 @@ const stories = storiesOf("Integration/Triangles", module).addDecorator(withScre generateNonInstancedClickAssertions("Triangle", Triangles, twoTrianglesInARow).forEach( ({ name, story }) => stories.add(name, story) ); +generateNonInstancedClickAssertions( + "Triangle with onlyRenderInHitmap=true", + Triangles, + twoTrianglesInARow.map((triangle) => ({ ...triangle, onlyRenderInHitmap: true })) +).forEach(({ name, story }) => stories.add(name, story)); generateInstancedClickAssertions("Triangle", Triangles, instancedTriangles).forEach(({ name, story }) => stories.add(name, story) ); diff --git a/packages/regl-worldview/src/stories/assertionStories/Worldview.stories.js b/packages/regl-worldview/src/stories/assertionStories/Worldview.stories.js index e9b8abaa2..e4f1bec69 100644 --- a/packages/regl-worldview/src/stories/assertionStories/Worldview.stories.js +++ b/packages/regl-worldview/src/stories/assertionStories/Worldview.stories.js @@ -14,7 +14,7 @@ import React from "react"; import { withScreenshot } from "storybook-chrome-screenshot"; import Cubes from "../../commands/Cubes"; -import { WorldviewWrapper, clickAtOrigin, WORLDVIEW_SIZE } from "../worldviewAssertionUtils"; +import { WorldviewWrapper, clickAtOrigin, WORLDVIEW_SIZE, defaultCameraState } from "../worldviewAssertionUtils"; import { assertionTest, timeout } from "stories/assertionTestUtils"; const cube = { @@ -26,15 +26,28 @@ const cube = { color: { r: 1, g: 0, b: 1, a: 0.5 }, }; -async function emitMouseEvent(eventName: "mousemove" | "mousedown" | "mouseup" | "dblclick"): Promise { +const underCube = { + pose: { + orientation: { x: 0, y: 0, z: 0, w: 1 }, + position: { x: 0, y: -30, z: 0 }, + }, + scale: { x: 10, y: 10, z: 10 }, + color: { r: 0, g: 1, b: 1, a: 0.5 }, +}; + +async function emitMouseEvent( + eventName: "mousemove" | "mousedown" | "mouseup" | "dblclick", + clientX: number = WORLDVIEW_SIZE / 2, + clientY: number = WORLDVIEW_SIZE / 2 +): Promise { const [element] = document.getElementsByTagName("canvas"); if (!element) { throw new Error("Could not find canvas element"); } const mouseEvent = new MouseEvent(eventName, { bubbles: true, - clientX: WORLDVIEW_SIZE / 2, - clientY: WORLDVIEW_SIZE / 2, + clientX, + clientY, }); element.dispatchEvent(mouseEvent); await timeout(100); @@ -175,4 +188,130 @@ stories expect(result[0].object).toEqual(cube); }, }) + ) + .add( + `a component's mouse handlers can stop event propagation to worldview's global mouse handlers`, + assertionTest({ + story: (setTestData) => ( + setTestData([])}> + { + e.stopPropagation(); + setTestData(objects); + }}> + {[cube]} + + + ), + assertions: async (getTestData) => { + await emitMouseEvent("mousedown"); + const result = await getTestData(); + expect(result.length).toEqual(1); + expect(result[0].object).toEqual(cube); + }, + }) + ) + .add( + `when there are overlapping objects from different commands, the command on top of the drawing layer can stop event propagation to other commands`, + assertionTest({ + story: (setTestData) => ( + + { + e.stopPropagation(); + setTestData(objects); + }}> + {[cube]} + + { + setTestData([]); + }}> + {[underCube]} + + + ), + assertions: async (getTestData) => { + await emitMouseEvent("mousedown"); + const result = await getTestData(); + expect(result.length).toEqual(1); + expect(result[0].object).toEqual(cube); + }, + }) + ) + .add( + `Firing two mouse events at the same time does not cause an error`, + assertionTest({ + story: (setTestData) => ( + setTestData(clickInfo)}> + {[cube]} + + ), + assertions: async (getTestData) => { + emitMouseEvent("mousedown"); + await emitMouseEvent("mousedown"); + const result1 = await getTestData(); + expect(result1.objects[0].object).toEqual(cube); + }, + }) + ) + .add( + `(cached hitmap test) A second event at the same point correctly selects the same object`, + assertionTest({ + story: (setTestData) => ( + setTestData(clickInfo)}> + {[cube]} + + ), + assertions: async (getTestData) => { + await emitMouseEvent("mousedown"); + const result1 = await getTestData(); + expect(result1.objects[0].object).toEqual(cube); + await emitMouseEvent("mousedown"); + const result2 = await getTestData(); + expect(result2.objects[0].object).toEqual(cube); + }, + }) + ) + .add( + `(cached hitmap test) Repainting busts the cache`, + assertionTest({ + story: (setTestData, state) => ( + setTestData(clickInfo)}> + {[cube]} + + ), + assertions: async (getTestData, setState) => { + await emitMouseEvent("mousedown"); + const result1 = await getTestData(); + expect(result1.objects[0].object).toEqual(cube); + + setState({ ...defaultCameraState, target: [100, 0, 0] }); + await timeout(100); + await emitMouseEvent("mousedown"); + const result2 = await getTestData(); + expect(result2.objects.length).toEqual(0); + }, + }) + ) + .add( + `(cached hitmap test) A second event at a different point does not use the first cached point`, + assertionTest({ + story: (setTestData) => ( + setTestData(clickInfo)}> + {[cube]} + + ), + assertions: async (getTestData) => { + await emitMouseEvent("mousedown"); + const result1 = await getTestData(); + expect(result1.objects[0].object).toEqual(cube); + + await emitMouseEvent("mousedown", 0, 0); + const result2 = await getTestData(); + expect(result2.objects.length).toEqual(0); + }, + }) ); diff --git a/packages/regl-worldview/src/stories/cameraState.stories.js b/packages/regl-worldview/src/stories/cameraState.stories.js index 24de28cca..343b04baf 100755 --- a/packages/regl-worldview/src/stories/cameraState.stories.js +++ b/packages/regl-worldview/src/stories/cameraState.stories.js @@ -12,17 +12,11 @@ import { quat, vec3 } from "gl-matrix"; import * as React from "react"; import { withScreenshot } from "storybook-chrome-screenshot"; +import { vec4ToOrientation, vec3ToPoint } from "../utils/commandUtils"; import Container from "./Container"; import { Arrows, Spheres, Axes, Grid, cameraStateSelectors, type CameraState } from ".."; -function vec3ToObject([x, y, z]) { - return { x, y, z }; -} -function quatToObject([x, y, z, w]) { - return { x, y, z, w }; -} - type Props = {| cameraStateFromKnobs: CameraState, controlled: boolean, // eslint-disable-line react/no-unused-prop-types @@ -58,8 +52,8 @@ class CameraStateStory extends React.Component { const poseArrowMarker = { pose: { - orientation: quatToObject(cameraState.targetOrientation), - position: vec3ToObject(cameraState.target), + orientation: vec4ToOrientation(cameraState.targetOrientation), + position: vec3ToPoint(cameraState.target), }, scale: { x: 20, y: 3, z: 3 }, color: { r: 1, g: 0, b: 1, a: 1 }, @@ -86,8 +80,8 @@ class CameraStateStory extends React.Component { const cameraArrowMarker = { pose: { - position: vec3ToObject(cameraPosition), - orientation: quatToObject(cameraOrientation), + position: vec3ToPoint(cameraPosition), + orientation: vec4ToOrientation(cameraOrientation), }, scale: { x: arrowLength, y: 2, z: 2 }, color: { r: 0, g: 1, b: 1, a: 0.5 }, diff --git a/packages/regl-worldview/src/stories/worldviewAssertionUtils.js b/packages/regl-worldview/src/stories/worldviewAssertionUtils.js index ba45b9217..7c3b7dc5f 100644 --- a/packages/regl-worldview/src/stories/worldviewAssertionUtils.js +++ b/packages/regl-worldview/src/stories/worldviewAssertionUtils.js @@ -16,7 +16,7 @@ import type { CameraState } from "../types"; import Worldview, { type Props } from "../Worldview"; import { assertionTest, timeout } from "stories/assertionTestUtils"; -const defaultCameraState: $Shape = { +export const defaultCameraState: $Shape = { distance: 75, perspective: true, phi: Math.PI / 2, @@ -66,7 +66,7 @@ export async function clickAtOrigin() { */ export function generateNonInstancedClickAssertions( commandName: string, - CommandInstance: ComponentType<{ children: Array, ...CommonCommandProps }>, + CommandInstance: ComponentType<{ ...CommonCommandProps, children: Array }>, renderedObjects: Array, overrideOptions?: { overrideExpectedSingleObjects?: any, @@ -168,7 +168,7 @@ export function generateNonInstancedClickAssertions( */ export function generateInstancedClickAssertions( commandName: string, - CommandInstance: ComponentType<{ children: Array, ...CommonCommandProps }>, + CommandInstance: ComponentType<{ ...CommonCommandProps, children: Array }>, renderedObject: Type ): Array<{ name: string, story: () => React$Element }> { return [ diff --git a/packages/regl-worldview/src/types/index.js b/packages/regl-worldview/src/types/index.js index 27d303d12..ef0d26420 100755 --- a/packages/regl-worldview/src/types/index.js +++ b/packages/regl-worldview/src/types/index.js @@ -110,9 +110,9 @@ export type ComponentReglClickInfo = { objects: Array, }; -export type MouseHandler = (MouseEvent, ReglClickInfo) => void; +export type MouseHandler = (SyntheticMouseEvent, ReglClickInfo) => void; -export type ComponentMouseHandler = (MouseEvent, ComponentReglClickInfo) => void; +export type ComponentMouseHandler = (SyntheticMouseEvent, ComponentReglClickInfo) => void; export type Coordinate = [number, number]; @@ -179,6 +179,7 @@ export type Cylinder = BaseShape & { export type Line = BaseShape & { points: (Point | Vec3)[], + poses?: Pose[], }; export type PointType = BaseShape & { @@ -193,6 +194,8 @@ export type SphereList = BaseShape & { export type TriangleList = BaseShape & { points: (Point | Vec3)[], colors?: (Color | Vec4)[], + // Pass true to not render the triangles to the screen - just the hitmap. + onlyRenderInHitmap?: boolean, }; export type PolygonType = BaseShape & { diff --git a/packages/regl-worldview/src/utils/HitmapObjectIdManager.js b/packages/regl-worldview/src/utils/HitmapObjectIdManager.js index 0109d03b8..4bc09c608 100644 --- a/packages/regl-worldview/src/utils/HitmapObjectIdManager.js +++ b/packages/regl-worldview/src/utils/HitmapObjectIdManager.js @@ -16,17 +16,19 @@ function fillArray(start: number, length: number): number[] { return new Array(length).fill(0).map((_, index) => start + index); } +type CommandType = Command; + /* * This object manages the mapping between objects that are rendered into the scene and their IDs. * It supplies an API for generating IDs for a rendered object and then accessing those objects based on their ID. */ export default class HitmapObjectIdManager { _objectsByObjectHitmapIdMap: { [ObjectHitmapId]: Object } = {}; - _commandsByObjectMap: Map = new Map(); + _commandsByObjectMap: Map = new Map(); _nextObjectHitmapId = 1; _instanceIndexByObjectHitmapIdMap: { [ObjectHitmapId]: number } = {}; - assignNextColors = (command: Command, object: Object, count: number): Vec4[] => { + assignNextColors = (command: CommandType, object: Object, count: number): Vec4[] => { if (count < 1) { throw new Error("Must get at least 1 id"); } @@ -59,7 +61,7 @@ export default class HitmapObjectIdManager { }; }; - getCommandForObject = (object: Object): ?Command => { + getCommandForObject = (object: Object): ?CommandType => { return this._commandsByObjectMap.get(object); }; } diff --git a/packages/regl-worldview/src/utils/getChildrenForHitmapDefaults.js b/packages/regl-worldview/src/utils/getChildrenForHitmapDefaults.js index 18b3dc112..71544695c 100644 --- a/packages/regl-worldview/src/utils/getChildrenForHitmapDefaults.js +++ b/packages/regl-worldview/src/utils/getChildrenForHitmapDefaults.js @@ -72,7 +72,7 @@ function instancedGetChildrenForHitmapFromSingleProp( const idColors = assignNextColors(prop, instanceCount); const startColor = idColors[0]; // We have to map these instance colors to `pointCountPerInstance` number of points - if (hitmapProp.colors && hitmapProp.points && hitmapProp.points.length) { + if (hitmapProp.points && hitmapProp.points.length) { const allColors = new Array(hitmapProp.points.length).fill().map(() => startColor); for (let i = 0; i < instanceCount; i++) { for (let j = 0; j < pointCountPerInstance; j++) { diff --git a/packages/regl-worldview/src/utils/queuePromise.js b/packages/regl-worldview/src/utils/queuePromise.js new file mode 100644 index 000000000..229803a38 --- /dev/null +++ b/packages/regl-worldview/src/utils/queuePromise.js @@ -0,0 +1,48 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import { signal, type Signal } from "./signal"; + +type QueuedFn = ((...args: any[]) => Promise) & { currentPromise: ?Promise }; + +// Wait for the previous promise to resolve before starting the next call to the function. +export default function queuePromise(fn: (...args: any[]) => Promise): QueuedFn { + // Whether we are currently waiting for a promise returned by `fn` to resolve. + let calling = false; + // The list of calls made to the function was made while a call was in progress. + const nextCalls: {| args: any[], promise: Signal |}[] = []; + + function queuedFn(...args) { + if (calling) { + const returnPromise = signal(); + nextCalls.push({ args, promise: returnPromise }); + return returnPromise; + } + return start(...args); + } + + function start(...args) { + calling = true; + + const promise = fn(...args).finally(() => { + calling = false; + queuedFn.currentPromise = undefined; + if (nextCalls.length) { + const { promise: nextPromise, args: nextArgs } = nextCalls.shift(); + start(...nextArgs) + .then((result) => nextPromise.resolve(result)) + .catch((error) => nextPromise.reject(error)); + } + }); + queuedFn.currentPromise = promise; + + return promise; + } + + return queuedFn; +} diff --git a/packages/regl-worldview/src/utils/queuePromise.test.js b/packages/regl-worldview/src/utils/queuePromise.test.js new file mode 100644 index 000000000..a58c4a263 --- /dev/null +++ b/packages/regl-worldview/src/utils/queuePromise.test.js @@ -0,0 +1,94 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import queuePromise from "./queuePromise"; +import { signal } from "./signal"; + +/* eslint-disable jest/valid-expect-in-promise */ + +describe("queuePromise", () => { + it("queues with resolved and rejected promises", async () => { + const secondPromise = signal(); + const promises = [Promise.resolve(), secondPromise, Promise.resolve()]; + + let calls = 0; + const callArgs = []; + const queuedFn = queuePromise((...args) => { + callArgs.push(args); + ++calls; + return promises.shift(); + }); + + expect(calls).toBe(0); + + queuedFn(1, 2); + queuedFn(3, 4); + queuedFn(5, 6); + expect(calls).toBe(1); + expect(callArgs).toEqual([[1, 2]]); + expect(queuedFn.currentPromise).not.toBeUndefined(); + + await secondPromise.reject(new Error("")); + expect(calls).toBe(2); + expect(callArgs).toEqual([[1, 2], [3, 4]]); + await Promise.resolve(); + + expect(calls).toBe(3); + expect(callArgs).toEqual([[1, 2], [3, 4], [5, 6]]); + expect(queuedFn.currentPromise).toBeUndefined(); + }); + + it("returns a promise", async () => { + const promises = [signal(), signal()]; + let calls = 0; + const queuedFn = queuePromise((...args) => { + ++calls; + return promises[calls - 1]; + }); + + const arePromisesResolved = [false, false]; + const promiseResults = [queuedFn(), queuedFn()]; + promiseResults.forEach((promise, index) => + promise.then(() => { + arePromisesResolved[index] = true; + }) + ); + expect(calls).toEqual(1); + expect(arePromisesResolved).toEqual([false, false]); + + // resolve the first promise. This should move on to the second promise. + promises[0].resolve(); + await promiseResults[0]; + expect(calls).toEqual(2); + expect(arePromisesResolved).toEqual([true, false]); + + // resolve the second promise. + promises[1].resolve(); + await promiseResults[1]; + expect(calls).toEqual(2); + expect(arePromisesResolved).toEqual([true, true]); + }); + + it("handles nested calls", async () => { + expect.assertions(3); + + let calls = 0; + const queuedFn = queuePromise(async () => { + ++calls; + if (calls === 1) { + queuedFn(); + expect(calls).toBe(1); + } + }); + + queuedFn(); + expect(calls).toBe(1); + await Promise.resolve(); + expect(calls).toBe(2); + }); +}); diff --git a/packages/regl-worldview/src/utils/signal.js b/packages/regl-worldview/src/utils/signal.js new file mode 100644 index 000000000..9feb3cc2d --- /dev/null +++ b/packages/regl-worldview/src/utils/signal.js @@ -0,0 +1,24 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +export type Signal = Promise & { + resolve: (T) => void, + reject: (Error) => void, +}; + +export function signal(): Signal { + let resolve; + let reject; + const promise: any = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + promise.resolve = resolve; + promise.reject = reject; + return promise; +} diff --git a/packages/webviz-core/.eslintrc.js b/packages/webviz-core/.eslintrc.js index e7f7cac31..18f915071 100644 --- a/packages/webviz-core/.eslintrc.js +++ b/packages/webviz-core/.eslintrc.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. @@ -21,6 +21,18 @@ module.exports = { patterns: ["client/*", "shared/*", "server/*"], }, ], + "no-shadow": "error", + "no-restricted-syntax": [ + "error", + { + selector: "MethodDefinition[kind='get'], Property[kind='get']", + message: "Property getters are not allowed; prefer function syntax instead.", + }, + { + selector: "MethodDefinition[kind='set'], Property[kind='set']", + message: "Property setters are not allowed; prefer function syntax instead.", + }, + ], "header/header": [ 2, "line", @@ -28,8 +40,8 @@ module.exports = { " @flow", "", { - pattern: "^ Copyright \\(c\\) \\d{4}-present, GM Cruise LLC$", - template: " Copyright (c) 2019-present, GM Cruise LLC", + pattern: "^ Copyright \\(c\\) \\d{4}-present, Cruise LLC$", + template: " Copyright (c) 2019-present, Cruise LLC", }, "", " This source code is licensed under the Apache License, Version 2.0,", diff --git a/packages/webviz-core/package-lock.json b/packages/webviz-core/package-lock.json index c26aa2ed7..0cb59fdfc 100644 --- a/packages/webviz-core/package-lock.json +++ b/packages/webviz-core/package-lock.json @@ -4,6 +4,37 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@ant-design/colors": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz", + "integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==", + "requires": { + "tinycolor2": "^1.4.1" + } + }, + "@ant-design/create-react-context": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@ant-design/create-react-context/-/create-react-context-0.2.5.tgz", + "integrity": "sha512-1rMAa4qgP2lfl/QBH9i78+Gjxtj9FTMpMyDGZsEBW5Kih72EuUo9958mV8PgpRkh4uwPSQ7vVZWXeyNZXVAFDg==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "@ant-design/icons": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz", + "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w==" + }, + "@ant-design/icons-react": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-react/-/icons-react-2.0.1.tgz", + "integrity": "sha512-r1QfoltMuruJZqdiKcbPim3d8LNsVPB733U0gZEUSxBLuqilwsW28K2rCTWSMTjmFX7Mfpf+v/wdiFe/XCqThw==", + "requires": { + "@ant-design/colors": "^3.1.0", + "babel-runtime": "^6.26.0" + } + }, "@babel/runtime": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", @@ -24,6 +55,78 @@ "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-3.2.89.tgz", "integrity": "sha512-X9DG/qaj5KkNJX/d7G41MyzkXbTWwWVGifs4yWeIZTsGoMZfzjRutz/RO1o09dZpMbcDvU6oy7bcpPWOfiS55Q==" }, + "@sentry/browser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.7.1.tgz", + "integrity": "sha512-K0x1XhsHS8PPdtlVOLrKZyYvi5Vexs9WApdd214bO6KaGF296gJvH1mG8XOY0+7aA5i2A7T3ttcaJNDYS49lzw==", + "requires": { + "@sentry/core": "5.7.1", + "@sentry/types": "5.7.1", + "@sentry/utils": "5.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/core": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.7.1.tgz", + "integrity": "sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg==", + "requires": { + "@sentry/hub": "5.7.1", + "@sentry/minimal": "5.7.1", + "@sentry/types": "5.7.1", + "@sentry/utils": "5.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.7.1.tgz", + "integrity": "sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig==", + "requires": { + "@sentry/types": "5.7.1", + "@sentry/utils": "5.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.7.1.tgz", + "integrity": "sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g==", + "requires": { + "@sentry/hub": "5.7.1", + "@sentry/types": "5.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.7.1.tgz", + "integrity": "sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ==" + }, + "@sentry/utils": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.7.1.tgz", + "integrity": "sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q==", + "requires": { + "@sentry/types": "5.7.1", + "tslib": "^1.9.3" + } + }, + "@simonwep/pickr": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.4.7.tgz", + "integrity": "sha512-dnx46+3KiZJ5YNXoWDdaOPLZGPCOFsFsa5uT3Eg+lYCUw8yZ+UxOGG9kEbUX+HgM35bJvAnOfq0l1qEr3CLVzw==", + "requires": { + "core-js": "^3.3.6" + }, + "dependencies": { + "core-js": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.5.tgz", + "integrity": "sha512-OuvejWH6vIaUo59Ndlh89purNm4DCIy/v3QoYlcGnn+PkYI8BhNHfCuAESrWX+ZPfq9JccVJ+XXgOMy77PJexg==" + } + } + }, "@testing-library/react-hooks": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-1.1.0.tgz", @@ -52,6 +155,28 @@ } } }, + "@trust/keyto": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.7.tgz", + "integrity": "sha512-t5kWWCTkPgg24JWVuCTPMx7l13F7YHdxBeJkT1vmoHjROgiOIEAN8eeY+iRmP1Hwsx+S7U55HyuqSsECr08a8A==", + "requires": { + "asn1.js": "^5.0.1", + "base64url": "^3.0.1", + "elliptic": "^6.4.1" + } + }, + "@trust/webcrypto": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.9.2.tgz", + "integrity": "sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==", + "requires": { + "@trust/keyto": "^0.3.4", + "base64url": "^3.0.0", + "elliptic": "^6.4.0", + "node-rsa": "^0.4.0", + "text-encoding": "^0.6.1" + } + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -78,6 +203,14 @@ "csstype": "^2.2.0" } }, + "@types/react-slick": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.4.tgz", + "integrity": "sha512-vXoIy4GUfB7/YgqubR4H7RALo+pRdMYCeLgWwV3MPwl5pggTlEkFBTF19R7u+LJc85uMqC7RfsbkqPLMQ4ab+A==", + "requires": { + "@types/react": "*" + } + }, "@types/react-test-renderer": { "version": "16.8.3", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.8.3.tgz", @@ -115,6 +248,75 @@ "source-map": "^0.6.0" } }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "add-dom-event-listener": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", + "requires": { + "object-assign": "4.x" + } + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, + "airbnb-prop-types": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz", + "integrity": "sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA==", + "requires": { + "array.prototype.find": "^2.1.0", + "function.prototype.name": "^1.1.1", + "has": "^1.0.3", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0", + "prop-types": "^15.7.2", + "prop-types-exact": "^1.2.0", + "react-is": "^16.9.0" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + } + } + }, + "amator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/amator/-/amator-1.1.0.tgz", + "integrity": "sha1-CMa2C8k67Cthu/wMTWd9MDI8wPE=", + "requires": { + "bezier-easing": "^2.0.3" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -138,6 +340,94 @@ } } }, + "antd": { + "version": "3.26.5", + "resolved": "https://registry.npmjs.org/antd/-/antd-3.26.5.tgz", + "integrity": "sha512-vD45SKnPq6GDwP+vdv5LIFTztPBGp2uJPuYdu8vUCl4iFSdljYldfsNAzMfRygk7QHRb22vX1Vo/KoH+I7wVMA==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "@ant-design/icons": "~2.1.1", + "@ant-design/icons-react": "~2.0.1", + "@types/react-slick": "^0.23.4", + "array-tree-filter": "^2.1.0", + "babel-runtime": "6.x", + "classnames": "~2.2.6", + "copy-to-clipboard": "^3.2.0", + "css-animation": "^1.5.0", + "dom-closest": "^0.2.0", + "enquire.js": "^2.1.6", + "is-mobile": "^2.1.0", + "lodash": "^4.17.13", + "moment": "^2.24.0", + "omit.js": "^1.0.2", + "prop-types": "^15.7.2", + "raf": "^3.4.1", + "rc-animate": "^2.10.2", + "rc-calendar": "~9.15.7", + "rc-cascader": "~0.17.4", + "rc-checkbox": "~2.1.6", + "rc-collapse": "~1.11.3", + "rc-dialog": "~7.6.0", + "rc-drawer": "~3.1.1", + "rc-dropdown": "~2.4.1", + "rc-editor-mention": "^1.1.13", + "rc-form": "^2.4.10", + "rc-input-number": "~4.5.0", + "rc-mentions": "~0.4.0", + "rc-menu": "~7.5.1", + "rc-notification": "~3.3.1", + "rc-pagination": "~1.20.11", + "rc-progress": "~2.5.0", + "rc-rate": "~2.5.0", + "rc-resize-observer": "^0.1.0", + "rc-select": "~9.2.0", + "rc-slider": "~8.7.1", + "rc-steps": "~3.5.0", + "rc-switch": "~1.9.0", + "rc-table": "~6.10.5", + "rc-tabs": "~9.7.0", + "rc-time-picker": "~3.7.1", + "rc-tooltip": "~3.7.3", + "rc-tree": "~2.1.0", + "rc-tree-select": "~2.9.1", + "rc-trigger": "^2.6.2", + "rc-upload": "~2.9.1", + "rc-util": "^4.16.1", + "react-lazy-load": "^3.0.13", + "react-lifecycles-compat": "^3.0.4", + "react-slick": "~0.25.2", + "resize-observer-polyfill": "^1.5.1", + "shallowequal": "^1.1.0", + "warning": "~4.0.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -146,16 +436,60 @@ "sprintf-js": "~1.0.2" } }, + "array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" + }, "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" }, + "array.prototype.find": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.0.tgz", + "integrity": "sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0" + } + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "asn1.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz", + "integrity": "sha512-Q7hnYGGNYbcmGrCPulXfkEw7oW7qjWeM4ZTALmgpuIcZLxyqqKYWxCZg2UBm8bklrnB4m2mGyJPWfoktdORD8A==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "async-validator": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.11.5.tgz", + "integrity": "sha512-XNtCsMAeAH1pdLMEg1z8/Bb3a8cdCbui9QbJATRFHHHW5kT6+NPI3zSVQUXgikTFITzsg+kYY5NTWhM2Orwt9w==" + }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -184,6 +518,11 @@ "regenerator-runtime": "^0.11.0" } }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "bail": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", @@ -194,6 +533,11 @@ "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "base64-arraybuffer-es6": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.4.2.tgz", @@ -205,16 +549,54 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, "batch-processor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=" }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, + "bezier-easing": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", + "integrity": "sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY=" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, "buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", @@ -224,6 +606,16 @@ "ieee754": "^1.1.4" } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, + "cbor-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cbor-js/-/cbor-js-0.1.0.tgz", + "integrity": "sha1-yAzmEg84fo+qdDcN/aIdlluPx/k=" + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -322,6 +714,56 @@ "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-classes": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz", + "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", + "requires": { + "component-indexof": "0.0.3" + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-indexof": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz", + "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, + "compressjs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/compressjs/-/compressjs-1.0.3.tgz", + "integrity": "sha1-ldt03VuQOM+AvKMhqw7eJxtJWbY=", + "requires": { + "amdefine": "~1.0.0", + "commander": "~2.8.1" + } + }, + "compute-scroll-into-view": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.12.tgz", + "integrity": "sha512-MUJWwXJsFQ0+Z5fvrcvA+Da+ZGxpwIMEOmXQiYjB40f0+HWZHp+Cr4F/CtmQPRpggC5ZvBHj14zXPDPmvq/OkA==" + }, "connected-react-router": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.5.2.tgz", @@ -349,16 +791,69 @@ } } }, + "consolidated-events": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz", + "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "copy-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", + "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "core-js": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.1.tgz", "integrity": "sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg==" }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "css-animation": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.6.1.tgz", + "integrity": "sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog==", + "requires": { + "babel-runtime": "6.x", + "component-classes": "^1.2.5" + } + }, "csstype": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.6.tgz", "integrity": "sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==" }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, "discontinuous-range": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", @@ -393,6 +888,27 @@ } } }, + "document.contains": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/document.contains/-/document.contains-1.0.1.tgz", + "integrity": "sha512-A1KqlZq1w605bwiiLqVZehWE9S9UYlUXPoduFWi64pNVNQ9vy6wwH/7BS+iEfSlF1YyZgcg5PZw5HqDi7FCrUw==", + "requires": { + "define-properties": "^1.1.3" + } + }, + "dom-align": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.10.2.tgz", + "integrity": "sha512-AYZUzLepy05E9bCY4ExoqHrrIlM49PEak9oF93JEFoibqKL0F7w5DLM70/rosLOawerWZ3MlepQcl+EmHskOyw==" + }, + "dom-closest": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz", + "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=", + "requires": { + "dom-matches": ">=1.0.1" + } + }, "dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -401,6 +917,11 @@ "@babel/runtime": "^7.1.2" } }, + "dom-matches": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz", + "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw=" + }, "dom-scroll-into-view": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.0.1.tgz", @@ -458,6 +979,64 @@ "domelementtype": "1" } }, + "downshift": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-4.0.1.tgz", + "integrity": "sha512-22SdWUfmGw3vWD46WqD6IbQ/CeEJmWWKFeGTVnIyxj11VuI/MaL516XRf9+O6uFJS3xHEW8BDVUSvM06/c2rNQ==", + "requires": { + "@babel/runtime": "^7.4.5", + "compute-scroll-into-view": "^1.0.9", + "prop-types": "^15.7.2", + "react-is": "^16.9.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", + "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + } + } + }, + "draft-js": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz", + "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==", + "requires": { + "fbjs": "^0.8.15", + "immutable": "~3.7.4", + "object-assign": "^4.1.0" + }, + "dependencies": { + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + } + } + }, "element-resize-detector": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.0.tgz", @@ -466,17 +1045,160 @@ "batch-processor": "1.0.0" } }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "engine.io": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz", + "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==", + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~6.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", + "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ=" + }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, - "escape-string-regexp": { + "es-abstract": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" @@ -492,6 +1214,16 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "eventemitter2": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz", + "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=" + }, + "eventlistener": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz", + "integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg=" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -513,6 +1245,27 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, "fetch-mock": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-7.2.5.tgz", @@ -550,6 +1303,32 @@ "integrity": "sha512-pALWFKf+AQiX4VfSEdxruj2bSMigsrAcg8djV6Hue2y3FJyiA/Z42UkEv6zEvSIpDj1EL+cRBvJNUx6L2+gdTQ==", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.1.tgz", + "integrity": "sha512-e1NzkiJuw6xqVH7YSdiW/qDHebcmMhPNe6w+4ZYYEg0VA+LaLzx37RimbPLuonHhYGFGPx1ME2nSi74JiaCr/Q==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1", + "functions-have-names": "^1.1.1", + "is-callable": "^1.1.4" + } + }, + "functions-have-names": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.1.1.tgz", + "integrity": "sha512-U0kNHUoxwPNPWOJaMG7Z00d4a/qZVrFtzWJRaK8V9goaVOCXBSQSJpt3MYGNtkScKEBKovxLjnNdC9MlXwo5Pw==" + }, + "fuzzysort": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.1.4.tgz", + "integrity": "sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==" + }, "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", @@ -565,6 +1344,11 @@ "process": "~0.5.1" } }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, "gud": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", @@ -575,11 +1359,53 @@ "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", @@ -598,6 +1424,16 @@ "value-equal": "^0.4.0" } }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "hoist-non-react-statics": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz", @@ -631,6 +1467,14 @@ "readable-stream": "^3.0.6" } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "idb": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/idb/-/idb-2.1.3.tgz", @@ -654,6 +1498,11 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -711,6 +1560,16 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, "is-decimal": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", @@ -729,6 +1588,11 @@ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==" }, + "is-mobile": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.1.0.tgz", + "integrity": "sha512-M5OhlZwh+aTlmRUvDg0Wq3uWVNa+w4DyZ2SjbrS+BhSLu9Po+JXHendC305ZEu+Hh7lywb19Zu4kYXu3L1Oo8A==" + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -742,6 +1606,27 @@ "isobject": "^3.0.1" } }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, "is-whitespace-character": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", @@ -762,6 +1647,15 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -781,6 +1675,14 @@ "resolved": "https://registry.npmjs.org/json-with-typed-arrays/-/json-with-typed-arrays-1.0.0.tgz", "integrity": "sha512-JyjEEf6GGBV1mMr/pjVpSOwG5WF7Ba2/mIW+qqABSy0F+QKp43+cXJ18YH85+MFrPE/g/4uyeW3U9XqVcXWDhA==" }, + "json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "requires": { + "string-convert": "^0.2.0" + } + }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -824,6 +1726,11 @@ "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, "lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -855,6 +1762,11 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -891,6 +1803,19 @@ "resolved": "https://registry.npmjs.org/micro-memoize/-/micro-memoize-3.0.1.tgz", "integrity": "sha512-1ruDLuAVzgBcm2WVFC4YZnTKY8xjBgcjoCGR4K64NbRncDxkgempJyYSni06vB9+FDQzqcvuyCQr0yc+0S5ltw==" }, + "mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + }, + "mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "requires": { + "mime-db": "1.42.0" + } + }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -924,6 +1849,34 @@ } } }, + "mini-store": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mini-store/-/mini-store-2.0.0.tgz", + "integrity": "sha512-EG0CuwpQmX+XL4QVS0kxNwHW5ftSbhygu1qxQH0pipugjnPkbvkalCdQbEihMwtQY6d3MTN+MS0q+aurs+RfLQ==", + "requires": { + "hoist-non-react-statics": "^2.3.1", + "prop-types": "^15.6.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.0.2" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -947,11 +1900,6 @@ "moment": ">= 2.9.0" } }, - "monaco-editor": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.17.1.tgz", - "integrity": "sha512-JAc0mtW7NeO+0SwPRcdkfDbWLgkqL9WfP1NbpP9wNASsW6oWqgZqNIWt4teymGjZIXTElx3dnQmUYHmVrJ7HxA==" - }, "monaco-editor-webpack-plugin": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.7.0.tgz", @@ -961,11 +1909,26 @@ "@types/webpack": "^4.4.19" } }, + "monaco-vim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/monaco-vim/-/monaco-vim-0.1.3.tgz", + "integrity": "sha512-1Rvgbren/4HbL1S/LhW2IVHyayFBxNUxHrh0Do+afh/MTYbteyKwC4ANjVRlqeSX2Ib8qovDUajLG30yF6aeQA==" + }, "moo": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==" }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mutationobserver-shim": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", + "integrity": "sha512-gciOLNN8Vsf7YzcqRjKzlAJ6y7e+B86u7i3KXes0xfxx/nfLmozlW1Vn+Sc9x3tPIePFgc1AeIFhtRgkqTjzDQ==" + }, "natsort": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/natsort/-/natsort-2.0.0.tgz", @@ -983,117 +1946,766 @@ "semver": "^5.4.1" } }, - "nomnom": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", - "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "ngraph.events": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.0.0.tgz", + "integrity": "sha512-Z7wyywdw8IKjOW0bDiOG4FUqX5fwqW7SDMO1huemDXho7Qy1b02RoBkPS43KLIZU2wrW2orju99k8wIr+xXvVA==" + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-rsa": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", + "integrity": "sha1-1jkXKewWqDDtWjgEKzFX0tXXJTA=", + "requires": { + "asn1": "0.2.3" + } + }, + "nomnom": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", + "requires": { + "colors": "0.5.x", + "underscore": "~1.4.4" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "requires": { + "is-extendable": "^1.0.0" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "omit.js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz", + "integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==", + "requires": { + "babel-runtime": "^6.23.0" + } + }, + "panzoom": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/panzoom/-/panzoom-8.4.0.tgz", + "integrity": "sha512-xBv5KA8nvKac6Nmw8+BxYR490plLXqT8FwkOGFsXeZXhY6DDUssNCpjlkKIiVvx5M7MEe5vI5oldRQI+7Af0Zg==", + "requires": { + "amator": "^1.1.0", + "ngraph.events": "^1.0.0", + "wheel": "0.0.5" + } + }, + "parse-entities": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz", + "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pngparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pngparse/-/pngparse-2.0.1.tgz", + "integrity": "sha1-hoUt5N40n077HoUudSVlXlrF37g=" + }, + "postcss": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.5.0" + } + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-queue": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" + }, + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "prop-types-exact": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz", + "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==", + "requires": { + "has": "^1.0.3", + "object.assign": "^4.1.0", + "reflect.ownkeys": "^0.2.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "raf-schd": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", + "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + }, + "ramda": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", + "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, + "rc-align": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.5.tgz", + "integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==", + "requires": { + "babel-runtime": "^6.26.0", + "dom-align": "^1.7.0", + "prop-types": "^15.5.8", + "rc-util": "^4.0.4" + } + }, + "rc-animate": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.10.2.tgz", + "integrity": "sha512-cE/A7piAzoWFSgUD69NmmMraqCeqVBa51UErod8NS3LUEqWfppSVagHfa0qHAlwPVPiIBg3emRONyny3eiH0Dg==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "css-animation": "^1.3.2", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-util": "^4.15.3", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-calendar": { + "version": "9.15.9", + "resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.15.9.tgz", + "integrity": "sha512-XOPzJlXYmLFIcwalXmzxKZrrAMD6dEPLRVoHG3wbBpErqjE8ugnXVjm9yXgtQh3Ho3Imhmt+KO0WGLv5T4WuAA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "moment": "2.x", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.0", + "rc-util": "^4.1.1", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-cascader": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-0.17.5.tgz", + "integrity": "sha512-WYMVcxU0+Lj+xLr4YYH0+yXODumvNXDcVEs5i7L1mtpWwYkubPV/zbQpn+jGKFCIW/hOhjkU4J1db8/P/UKE7A==", + "requires": { + "array-tree-filter": "^2.1.0", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.0", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.4", + "shallow-equal": "^1.0.0", + "warning": "^4.0.1" + } + }, + "rc-checkbox": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.1.8.tgz", + "integrity": "sha512-6qOgh0/by0nVNASx6LZnhRTy17Etcgav+IrI7kL9V9kcDZ/g7K14JFlqrtJ3NjDq/Kyn+BPI1st1XvbkhfaJeg==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "prop-types": "15.x", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-collapse": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-1.11.8.tgz", + "integrity": "sha512-8EhfPyScTYljkbRuIoHniSwZagD5UPpZ3CToYgoNYWC85L2qCbPYF7+OaC713FOrIkp6NbfNqXsITNxmDAmxog==", + "requires": { + "classnames": "2.x", + "css-animation": "1.x", + "prop-types": "^15.5.6", + "rc-animate": "2.x", + "react-is": "^16.7.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + }, + "rc-dialog": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-7.6.0.tgz", + "integrity": "sha512-N48vBPW8I53WycFHI4KXhuTUkB4mx+hixq1a9tcFMLoE7EhkAjbHvs0vGg+Bh/uFg5V00jmZBgQOIEbhcNal/A==", + "requires": { + "babel-runtime": "6.x", + "rc-animate": "2.x", + "rc-util": "^4.16.1" + } + }, + "rc-drawer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-3.1.1.tgz", + "integrity": "sha512-gx3W2KaeZHeZVKBwpZiHWgOR12CmER8VRGLeiTQq1E0hmKGNUXMvwV3DPulPTqOaOtcdFPCE3Dlf6mZfq6ANlQ==", + "requires": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.6", + "rc-util": "^4.16.1", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-dropdown": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-2.4.1.tgz", + "integrity": "sha512-p0XYn0wrOpAZ2fUGE6YJ6U8JBNc5ASijznZ6dkojdaEfQJAeZtV9KMEewhxkVlxGSbbdXe10ptjBlTEW9vEwEg==", + "requires": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.6", + "prop-types": "^15.5.8", + "rc-trigger": "^2.5.1", + "react-lifecycles-compat": "^3.0.2" + } + }, + "rc-editor-core": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/rc-editor-core/-/rc-editor-core-0.8.10.tgz", + "integrity": "sha512-T3aHpeMCIYA1sdAI7ynHHjXy5fqp83uPlD68ovZ0oClTSc3tbHmyCxXlA+Ti4YgmcpCYv7avF6a+TIbAka53kw==", + "requires": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.5", + "draft-js": "^0.10.0", + "immutable": "^3.7.4", + "lodash": "^4.16.5", + "prop-types": "^15.5.8", + "setimmediate": "^1.0.5" + } + }, + "rc-editor-mention": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/rc-editor-mention/-/rc-editor-mention-1.1.13.tgz", + "integrity": "sha512-3AOmGir91Fi2ogfRRaXLtqlNuIwQpvla7oUnGHS1+3eo7b+fUp5IlKcagqtwUBB5oDNofoySXkLBxzWvSYNp/Q==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "^2.2.5", + "dom-scroll-into-view": "^1.2.0", + "draft-js": "~0.10.0", + "immutable": "~3.7.4", + "prop-types": "^15.5.8", + "rc-animate": "^2.3.0", + "rc-editor-core": "~0.8.3" + }, + "dependencies": { + "dom-scroll-into-view": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz", + "integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4=" + }, + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + } + } + }, + "rc-form": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/rc-form/-/rc-form-2.4.11.tgz", + "integrity": "sha512-8BL+FNlFLTOY/A5X6tU35GQJLSIpsmqpwn/tFAYQTczXc4dMJ33ggtH248Cum8+LS0jLTsJKG2L4Qp+1CkY+sA==", + "requires": { + "async-validator": "~1.11.3", + "babel-runtime": "6.x", + "create-react-class": "^15.5.3", + "dom-scroll-into-view": "1.x", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.4", + "rc-util": "^4.15.3", + "warning": "^4.0.3" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "rc-hammerjs": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/rc-hammerjs/-/rc-hammerjs-0.6.9.tgz", + "integrity": "sha512-4llgWO3RgLyVbEqUdGsDfzUDqklRlQW5VEhE3x35IvhV+w//VPRG34SBavK3D2mD/UaLKaohgU41V4agiftC8g==", + "requires": { + "babel-runtime": "6.x", + "hammerjs": "^2.0.8", + "prop-types": "^15.5.9" + } + }, + "rc-input-number": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-4.5.3.tgz", + "integrity": "sha512-jBwxX5KDkp2nHOaEoMQ1mZBwWpmmGUuHXF/qralpmN+wDp8rlB6Xvr9d7AHgmzGZhbWIMyLeq6ET6HDDCCjvAA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.0", + "prop-types": "^15.5.7", + "rc-util": "^4.5.1", + "rmc-feedback": "^2.0.0" + } + }, + "rc-mentions": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-0.4.2.tgz", + "integrity": "sha512-DTZurQzacLXOfVuiHydGzqkq7cFMHXF18l2jZ9PhWUn2cqvOSY3W4osN0Pq29AOMOBpcxdZCzgc7Lb0r/bgkDw==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "classnames": "^2.2.6", + "rc-menu": "^7.4.22", + "rc-trigger": "^2.6.2", + "rc-util": "^4.6.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-menu": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-7.5.3.tgz", + "integrity": "sha512-H/jUyGbJxZI/iuVdC6Iu9KHfz7tucoqK0Vn8ahDnv+ppc1PnKb4SkBbXn5LrmUyaj7thCBiaktBxVnUXSmNE2g==", + "requires": { + "classnames": "2.x", + "dom-scroll-into-view": "1.x", + "mini-store": "^2.0.0", + "mutationobserver-shim": "^0.3.2", + "rc-animate": "^2.10.1", + "rc-trigger": "^2.3.0", + "rc-util": "^4.13.0", + "resize-observer-polyfill": "^1.5.0", + "shallowequal": "^1.1.0" + } + }, + "rc-notification": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-3.3.1.tgz", + "integrity": "sha512-U5+f4BmBVfMSf3OHSLyRagsJ74yKwlrQAtbbL5ijoA0F2C60BufwnOcHG18tVprd7iaIjzZt1TKMmQSYSvgrig==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "2.x", + "rc-util": "^4.0.4" + } + }, + "rc-pagination": { + "version": "1.20.12", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-1.20.12.tgz", + "integrity": "sha512-V1pL0d4nTW00+8b0qS8t12jawmaP14RKT+jFdc32SD76MO3N2kBE/B/zZWPnJHjHTcs0EVhgQC4b2Vgiyy1OJA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "^15.5.7", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-progress": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.5.2.tgz", + "integrity": "sha512-ajI+MJkbBz9zYDuE9GQsY5gsyqPF7HFioZEDZ9Fmc+ebNZoiSeSJsTJImPFCg0dW/5WiRGUy2F69SX1aPtSJgA==", "requires": { - "colors": "0.5.x", - "underscore": "~1.4.4" + "babel-runtime": "6.x", + "prop-types": "^15.5.8" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "rc-rate": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.5.0.tgz", + "integrity": "sha512-aXX5klRqbVZxvLghcKnLqqo7LvLVCHswEDteWsm5Gb7NBIPa1YKTcAbvb5SZ4Z4i4EeRoZaPwygRAWsQgGtbKw==", + "requires": { + "classnames": "^2.2.5", + "prop-types": "^15.5.8", + "rc-util": "^4.3.0", + "react-lifecycles-compat": "^3.0.4" + } }, - "object.omit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", - "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "rc-resize-observer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-0.1.3.tgz", + "integrity": "sha512-uzOQEwx83xdQSFOkOAM7x7GHIQKYnrDV4dWxtCxyG1BS1pkfJ4EvDeMfsvAJHSYkQXVBu+sgRHGbRtLG3qiuUg==", "requires": { - "is-extendable": "^1.0.0" + "classnames": "^2.2.1", + "rc-util": "^4.13.0", + "resize-observer-polyfill": "^1.5.1" } }, - "parse-entities": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz", - "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==", + "rc-select": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-9.2.2.tgz", + "integrity": "sha512-+NXatBt/wrT03L2e6hDEQfvMG4ihrQymuMtbDVi9+99Qlq2Ip7rASE/5XUYR2bOak7Ce9xXUckfKwhN3Jpp4MA==", "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "component-classes": "1.x", + "dom-scroll-into-view": "1.x", + "prop-types": "^15.5.8", + "raf": "^3.4.0", + "rc-animate": "2.x", + "rc-menu": "^7.3.0", + "rc-trigger": "^2.5.4", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.2", + "warning": "^4.0.2" } }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "rc-slider": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-8.7.1.tgz", + "integrity": "sha512-WMT5mRFUEcrLWwTxsyS8jYmlaMsTVCZIGENLikHsNv+tE8ThU2lCoPfi/xFNUfJFNFSBFP3MwPez9ZsJmNp13g==", "requires": { - "isarray": "0.0.1" + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "prop-types": "^15.5.4", + "rc-tooltip": "^3.7.0", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0", + "warning": "^4.0.3" } }, - "postcss": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", - "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", + "rc-steps": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-3.5.0.tgz", + "integrity": "sha512-2Vkkrpa7PZbg7qPsqTNzVDov4u78cmxofjjnIHiGB9+9rqKS8oTLPzbW2uiWDr3Lk+yGwh8rbpGO1E6VAgBCOg==", "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.5.0" + "babel-runtime": "^6.23.0", + "classnames": "^2.2.3", + "lodash": "^4.17.5", + "prop-types": "^15.5.7" } }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + "rc-switch": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz", + "integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==", + "requires": { + "classnames": "^2.2.1", + "prop-types": "^15.5.6", + "react-lifecycles-compat": "^3.0.4" + } }, - "promise-queue": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", - "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" + "rc-table": { + "version": "6.10.10", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-6.10.10.tgz", + "integrity": "sha512-xEVkw2nf+UNOJL3VLSCqa+v4IM8l2O1EtR6GLSztWokq6D2t8ntac7e0ImBjcg29yPQwMHm+2YIiC3fvRiSE1A==", + "requires": { + "classnames": "^2.2.5", + "component-classes": "^1.2.6", + "lodash": "^4.17.5", + "mini-store": "^2.0.0", + "prop-types": "^15.5.8", + "rc-util": "^4.13.0", + "react-lifecycles-compat": "^3.0.2", + "shallowequal": "^1.0.2" + } + }, + "rc-tabs": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-9.7.0.tgz", + "integrity": "sha512-kvmgp8/MfLzFZ06hWHignqomFQ5nF7BqKr5O1FfhE4VKsGrep52YSF/1MvS5oe0NPcI9XGNS2p751C5v6cYDpQ==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "babel-runtime": "6.x", + "classnames": "2.x", + "lodash": "^4.17.5", + "prop-types": "15.x", + "raf": "^3.4.1", + "rc-hammerjs": "~0.6.0", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.4", + "resize-observer-polyfill": "^1.5.1", + "warning": "^4.0.3" + } }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "rc-time-picker": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.7.3.tgz", + "integrity": "sha512-Lv1Mvzp9fRXhXEnRLO4nW6GLNxUkfAZ3RsiIBsWjGjXXvMNjdr4BX/ayElHAFK0DoJqOhm7c5tjmIYpEOwcUXg==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "classnames": "2.x", + "moment": "2.x", + "prop-types": "^15.5.8", + "raf": "^3.4.1", + "rc-trigger": "^2.2.0", + "react-lifecycles-compat": "^3.0.4" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "rc-tooltip": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-3.7.3.tgz", + "integrity": "sha512-dE2ibukxxkrde7wH9W8ozHKUO4aQnPZ6qBHtrTH9LoO836PjDdiaWO73fgPB05VfJs9FbZdmGPVEbXCeOP99Ww==", + "requires": { + "babel-runtime": "6.x", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.2" + } }, - "pure-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + "rc-tree": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.1.3.tgz", + "integrity": "sha512-COvV65spQ6omrHBUhHRKqKNL5+ddXjlS+qWZchaL9FFuQNvjM5pjp9RnmMWK4fJJ5kBhhpLneh6wh9Vh3kSMXQ==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "^2.6.0", + "rc-util": "^4.5.1", + "react-lifecycles-compat": "^3.0.4", + "warning": "^4.0.3" + } }, - "railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + "rc-tree-select": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-2.9.4.tgz", + "integrity": "sha512-0HQkXAN4XbfBW20CZYh3G+V+VMrjX42XRtDCpyv6PDUm5vikC0Ob682ZBCVS97Ww2a5Hf6Ajmu0ahWEdIEpwhg==", + "requires": { + "classnames": "^2.2.1", + "dom-scroll-into-view": "^1.2.1", + "prop-types": "^15.5.8", + "raf": "^3.4.0", + "rc-animate": "^2.8.2", + "rc-tree": "~2.1.0", + "rc-trigger": "^3.0.0", + "rc-util": "^4.5.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.0.2", + "warning": "^4.0.1" + }, + "dependencies": { + "dom-scroll-into-view": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz", + "integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4=" + }, + "rc-trigger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-3.0.0.tgz", + "integrity": "sha512-hQxbbJpo23E2QnYczfq3Ec5J5tVl2mUDhkqxrEsQAqk16HfADQg+iKNWzEYXyERSncdxfnzYuaBgy764mNRzTA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-align": "^2.4.1", + "rc-animate": "^3.0.0-rc.1", + "rc-util": "^4.15.7" + }, + "dependencies": { + "rc-animate": { + "version": "3.0.0-rc.6", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0-rc.6.tgz", + "integrity": "sha512-oBLPpiT6Q4t6YvD/pkLcmofBP1p01TX0Otse8Q4+Mxt8J+VSDflLZGIgf62EwkvRwsQUkLPjZVFBsldnPKLzjg==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "component-classes": "^1.2.6", + "fbjs": "^0.8.16", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-util": "^4.5.0", + "react-lifecycles-compat": "^3.0.4" + } + } + } + } + } }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" + "rc-trigger": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.5.tgz", + "integrity": "sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "15.x", + "rc-align": "^2.4.0", + "rc-animate": "2.x", + "rc-util": "^4.4.0", + "react-lifecycles-compat": "^3.0.4" + } }, - "randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "rc-upload": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-2.9.4.tgz", + "integrity": "sha512-WXt0HGxXyzLrPV6iec/96Rbl/6dyrAW8pKuY6wwD7yFYwfU5bjgKjv7vC8KNMJ6wzitFrZjnoiogNL3dF9dj3Q==", "requires": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "prop-types": "^15.5.7", + "warning": "4.x" } }, - "raven-js": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.27.0.tgz", - "integrity": "sha512-vChdOL+yzecfnGA+B5EhEZkJ3kY3KlMzxEhShKh6Vdtooyl0yZfYNFQfYzgMf2v4pyQa+OTZ5esTxxgOOZDHqw==" + "rc-util": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.18.0.tgz", + "integrity": "sha512-hftkePUmXu2AeaYQRqMdEJ+bkqNRKZEk59pUmqMKD68+69Csc1xeIc74P73leuZEYij23yG4OMnutejVjc8Jdg==", + "requires": { + "add-dom-event-listener": "^1.1.0", + "babel-runtime": "6.x", + "prop-types": "^15.5.10", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } }, "react-autocomplete": { "version": "github:janpaul123/react-autocomplete#bc8737070b5744069719c8fcd4e0a197192b0d48", @@ -1166,17 +2778,6 @@ "resolved": "https://registry.npmjs.org/react-document-events/-/react-document-events-1.4.0.tgz", "integrity": "sha512-eJuzYYLAqtHA2uV3JUWKWhmKXPBfSCVZEpFQpCswd3gzfJWn9UbX+7pIe3MKaofLLIIE/KwZ06ay5QJePt9bHw==" }, - "react-dom": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", - "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.13.6" - } - }, "react-draggable": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-3.0.5.tgz", @@ -1228,9 +2829,9 @@ } }, "react-input-autosize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.1.tgz", - "integrity": "sha512-3+K4CD13iE4lQQ2WlF8PuV5htfmTRLH6MDnfndHM6LuBRszuXnuyIfE7nhSKt8AzRBZ50bu0sAhkNMeS5pxQQA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", + "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", "requires": { "prop-types": "^15.5.8" } @@ -1250,6 +2851,17 @@ "react-base16-styling": "^0.5.1" } }, + "react-lazy-load": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-3.0.13.tgz", + "integrity": "sha1-OwqS0zbUPT8Nc8vm81sXBQsIuCQ=", + "requires": { + "eventlistener": "0.0.1", + "lodash.debounce": "^4.0.0", + "lodash.throttle": "^4.0.0", + "prop-types": "^15.5.8" + } + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -1309,6 +2921,35 @@ "uuid": "^3.0.1" } }, + "react-outside-click-handler": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/react-outside-click-handler/-/react-outside-click-handler-1.2.4.tgz", + "integrity": "sha512-FwLnTllTa65O/HjIyDgIrlAKcgPeXQnRUE+iR1EV4NY5opzN37S87+AtO1FF0rAa8qBDKj2QuNp4VfkjmkiB7g==", + "requires": { + "airbnb-prop-types": "^2.13.2", + "consolidated-events": "^1.1.1 || ^2.0.0", + "document.contains": "^1.0.1", + "object.values": "^1.1.0", + "prop-types": "^15.7.2" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + } + } + }, "react-redux": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.0.tgz", @@ -1360,6 +3001,45 @@ } } }, + "react-resize-detector": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-4.2.1.tgz", + "integrity": "sha512-ZfPMBPxXi0o3xox42MIEtz84tPSVMW9GgwLHYvjVXlFM+OkNzbeEtpVSV+mSTJmk4Znwomolzt35zHN9LNBQMQ==", + "requires": { + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "prop-types": "^15.7.2", + "raf-schd": "^4.0.2", + "resize-observer-polyfill": "^1.5.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash-es": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.10.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.1.tgz", + "integrity": "sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==" + } + } + }, "react-router": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz", @@ -1411,22 +3091,34 @@ "react-input-autosize": "^2.1.2" } }, + "react-slick": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz", + "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==", + "requires": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + } + }, "react-test-renderer": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", - "integrity": "sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.10.2.tgz", + "integrity": "sha512-k9Qzyev6cTIcIfrhgrFlYQAFxh5EEDO6ALNqYqmKsWVA7Q/rUMTay5nD3nthi6COmYsd4ghVYyi8U86aoeMqYQ==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", "react-is": "^16.8.6", - "scheduler": "^0.13.6" + "scheduler": "^0.16.2" }, "dependencies": { "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz", + "integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA==", "dev": true } } @@ -1491,6 +3183,11 @@ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" }, + "reflect.ownkeys": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", + "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -1533,6 +3230,11 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", @@ -1543,6 +3245,15 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "rmc-feedback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rmc-feedback/-/rmc-feedback-2.0.0.tgz", + "integrity": "sha512-5PWOGOW7VXks/l3JzlOU9NIxRpuaSS8d9zA3UULUCuTKnpwBHNvv1jSJzxgbbCQeYzROWUpgKI4za3X4C/mKmQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5" + } + }, "rosbag": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/rosbag/-/rosbag-2.1.0.tgz", @@ -1553,11 +3264,30 @@ "int53": "0.2.4" } }, + "roslib": { + "version": "github:RobotWebTools/roslibjs#c10d9eca43ad51cc89b335d00e81f647cbcb77d3", + "from": "github:RobotWebTools/roslibjs#c10d9eca43ad51cc89b335d00e81f647cbcb77d3", + "requires": { + "cbor-js": "^0.1.0", + "eventemitter2": "^4.1.0", + "object-assign": "^4.0.1", + "pngparse": "^2.0.1", + "socket.io": "2.2.0", + "webworkify": "^1.5.0", + "ws": "^3.3.1", + "xmldom": "^0.1.19" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "sanitize-html": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.0.tgz", @@ -1576,9 +3306,10 @@ } }, "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==", + "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -1597,14 +3328,102 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" }, "shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, + "socket.io": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", + "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.3.1", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.2.0", + "socket.io-parser": "~3.3.0" + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "socket.io-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", + "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.3.1", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1629,11 +3448,34 @@ "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==" }, + "string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", @@ -1655,6 +3497,11 @@ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" + }, "text-width": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/text-width/-/text-width-1.2.0.tgz", @@ -1678,6 +3525,16 @@ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", @@ -1702,6 +3559,11 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz", "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==" }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, "typescript": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", @@ -1725,6 +3587,16 @@ "whatwg-url": "7.0.0" } }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, "underscore": { "version": "1.4.4", "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", @@ -1832,6 +3704,14 @@ "unist-util-stringify-position": "^1.1.1" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "wasm-lz4": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wasm-lz4/-/wasm-lz4-1.0.0.tgz", @@ -1843,6 +3723,16 @@ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true }, + "webworkify": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz", + "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==" + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, "whatwg-url": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", @@ -1854,15 +3744,45 @@ "webidl-conversions": "^4.0.2" } }, + "wheel": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/wheel/-/wheel-0.0.5.tgz", + "integrity": "sha1-i00JMOcsm437uQeDPuMz3pewFPo=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, "x-is-string": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" }, + "xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==" + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" } } } diff --git a/packages/webviz-core/package.json b/packages/webviz-core/package.json index d3b6c5ecf..e7f02121c 100644 --- a/packages/webviz-core/package.json +++ b/packages/webviz-core/package.json @@ -6,12 +6,19 @@ "dependencies": { "@cruise-automation/hooks": "0.0.1", "@mdi/svg": "3.2.89", + "@sentry/browser": "5.7.1", + "@simonwep/pickr": "1.4.7", + "@trust/webcrypto": "0.9.2", + "antd": "3.26.5", "chart.js": "2.7.3", "chartjs-plugin-annotation": "0.5.7", "chartjs-plugin-datalabels": "0.4.0", "chartjs-plugin-zoom": "0.6.6", "classnames": "2.2.6", + "compressjs": "1.0.3", "connected-react-router": "6.5.2", + "downshift": "4.0.1", + "fuzzysort": "1.1.4", "history": "4.9.0", "hoist-non-react-statics": "3.0.1", "idb": "2.1.3", @@ -26,27 +33,31 @@ "moment": "2.22.2", "moment-duration-format": "2.2.2", "moment-timezone": "0.5.23", - "monaco-editor": "0.17.1", + "monaco-vim": "0.1.3", "natsort": "2.0.0", "nearley": "2.15.1", + "panzoom": "8.4.0", "promise-queue": "2.2.5", "prop-types": "15.6.2", - "raven-js": "3.27.0", - "react": "16.8.6", + "rc-collapse": "1.11.8", + "react": "16.10.2", "react-autocomplete": "janpaul123/react-autocomplete#bc8737070b5744069719c8fcd4e0a197192b0d48", "react-chartjs-2": "2.7.4", "react-container-dimensions": "1.4.1", "react-dnd": "2.5.4", "react-document-events": "1.4.0", - "react-dom": "16.8.6", + "react-dom": "16.10.2", "react-draggable": "3.0.5", "react-hot-loader": "4.8.2", "react-hover-observer": "2.1.1", + "react-input-autosize": "2.2.2", "react-json-tree": "0.11.1", "react-markdown": "4.0.4", "react-monaco-editor": "0.28.0", "react-mosaic-component": "1.0.3", + "react-outside-click-handler": "1.2.4", "react-redux": "7.1.0", + "react-resize-detector": "4.2.1", "react-router": "5.0.1", "react-router-dom": "5.0.1", "react-select": "1.2.1", @@ -56,6 +67,7 @@ "redux-thunk": "2.3.0", "reselect": "4.0.0", "rosbag": "2.1.0", + "roslib": "RobotWebTools/roslibjs#c10d9eca43ad51cc89b335d00e81f647cbcb77d3", "sanitize-html": "1.20.0", "shallowequal": "1.1.0", "string-hash": "1.1.3", @@ -72,7 +84,7 @@ "fetch-mock": "7.2.5", "flow-bin": "0.106.2", "monaco-editor-webpack-plugin": "1.7.0", - "react-test-renderer": "16.8.6" + "react-test-renderer": "16.10.2" }, "importjs": { "isRoot": false diff --git a/packages/webviz-core/public/fixtures/example-bz2.bag b/packages/webviz-core/public/fixtures/example-bz2.bag new file mode 100644 index 000000000..d44662218 Binary files /dev/null and b/packages/webviz-core/public/fixtures/example-bz2.bag differ diff --git a/packages/webviz-core/public/fixtures/only-a-cube-no-tf-ros-melodic.bag b/packages/webviz-core/public/fixtures/only-a-cube-no-tf-ros-melodic.bag new file mode 100644 index 000000000..e2a043f12 Binary files /dev/null and b/packages/webviz-core/public/fixtures/only-a-cube-no-tf-ros-melodic.bag differ diff --git a/packages/webviz-core/public/fixtures/only-a-cube.bag b/packages/webviz-core/public/fixtures/only-a-cube.bag index 0a57b9c9a..47909c8a1 100644 Binary files a/packages/webviz-core/public/fixtures/only-a-cube.bag and b/packages/webviz-core/public/fixtures/only-a-cube.bag differ diff --git a/packages/webviz-core/public/index.html b/packages/webviz-core/public/index.html index f51c7a5fe..b38622622 100644 --- a/packages/webviz-core/public/index.html +++ b/packages/webviz-core/public/index.html @@ -1,5 +1,5 @@ + diff --git a/packages/webviz-core/shared/delay.js b/packages/webviz-core/shared/delay.js index 79554f0d9..ffdea2b64 100644 --- a/packages/webviz-core/shared/delay.js +++ b/packages/webviz-core/shared/delay.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2019-present, GM Cruise LLC +// Copyright (c) 2019-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. diff --git a/packages/webviz-core/shared/panelToolbarStyles.js b/packages/webviz-core/shared/panelToolbarStyles.js new file mode 100644 index 000000000..badbfbc9f --- /dev/null +++ b/packages/webviz-core/shared/panelToolbarStyles.js @@ -0,0 +1,18 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. +import styled from "styled-components"; + +export const PanelToolbarLabel = styled.label` + display: block; + padding-bottom: 5px; +`; + +export const PanelToolbarInput = styled.input` + width: 100%; + margin: 0; +`; diff --git a/packages/webviz-core/src/util/signal.js b/packages/webviz-core/shared/signal.js similarity index 92% rename from packages/webviz-core/src/util/signal.js rename to packages/webviz-core/shared/signal.js index 05855d587..f1cdce75d 100644 --- a/packages/webviz-core/src/util/signal.js +++ b/packages/webviz-core/shared/signal.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. diff --git a/packages/webviz-core/src/PanelAPI/README.md b/packages/webviz-core/src/PanelAPI/README.md new file mode 100644 index 000000000..201f64015 --- /dev/null +++ b/packages/webviz-core/src/PanelAPI/README.md @@ -0,0 +1,77 @@ +# PanelAPI + +The `PanelAPI` namespace contains React [Hooks](https://reactjs.org/docs/hooks-intro.html) and components which allow panel authors to access Webviz data and metadata inside their panels. Using these APIs across all panels helps ensure that data appears consistent among panels, and makes it easier for panels to support advanced features (such as multiple simultaneous data sources). + +To use PanelAPI, it's recommended that you import the whole namespace, so that all usage sites look consistent, like `PanelAPI.useSomething()`. + +```js +import * as PanelAPI from "webviz-core/src/PanelAPI"; +``` + +## [`PanelAPI.useDataSourceInfo()`](useDataSourceInfo.js) + +"Data source info" encapsulates **rarely-changing** metadata about the sources from which Webviz is loading data. (A data source might be a local [bag file](http://wiki.ros.org/Bags/Format) dropped into the browser, or a bag stored on a remote server; see [players](../players) and [dataSources](../dataSources) for more details.) + +Using this hook inside a panel will cause the panel to re-render automatically when the metadata changes, but this won't happen very often or during playback. (Exception: the internal WebSocket player might cause `endTime` to update frequently; this is considered a bug.) + +```js + PanelAPI.useDataSourceInfo(): {| + topics: $ReadOnlyArray, + datatypes: RosDatatypes, + capabilities: string[], + startTime: ?Time, + endTime: ?Time, +|}; +``` + +## [`PanelAPI.useMessages()`](../components/MessageHistory/MessageHistoryOnlyTopics.js) + +`useMessages()` provides panels a way to access [messages](http://wiki.ros.org/Messages) from [topics](http://wiki.ros.org/Topics). `useMessages` is a fairly **low-level API** that many panels will use via [``](../components/MessageHistory) (in the future, we'll provide alternative hooks or helper functions to use MessageHistory [topic path syntax](../components/MessageHistory/topicPathSyntax.help.md) with useMessages). Users can define how to initialize a custom state, and how to update the state based on incoming messages. + +Using this hook will cause the panel to re-render when any new messages come in on the requested topics. + +```js +PanelAPI.useMessages(props: {| + topics: string[], + imageScale?: number, + restore: (prevState: ?T) => T, + addMessage: (prevState: T, message: Message) => T, +|}): {| reducedValue: T |}; +``` + +### Subscription parameters + +- `topics`: set of topics to subscribe to. Changing only the topics will not cause `restore` or `addMessage` to be called. +- `imageScale`: number between 0 and 1 for subscriptions to image topics, requesting that the player downsample images. _(Unused in the open-source version of Webviz.)_ + +### Reducer functions + +The useMessages hook returns a user-defined "state" (`T`). The `restore` and `addMessage` callbacks specify how to initialize and update the state. + +These reducers should be wrapped in [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback), because the useMessages hook will do extra work when they change, so they should change only when the interpretation of message data is actually changing. + +- `restore: (?T) => T`: + - Called with `undefined` to initialize a new state when the panel first renders, and when the user seeks to a different playback time (at which point Webviz automatically clears out state across all panels). + - Called with the previous state when the `restore` or `addMessage` reducer functions change. This allows the panel an opportunity to reuse its previous state when a parameter changes, without totally discarding it (as in the case of a seek) and waiting for new messages to come in from the data source. + + For example, a panel that filters some incoming messages can use `restore` to create a filtered value immediately when the filter changes. To implement this, the caller might switch from unfiltered reducers: + + ```js + { + restore: (x: ?string[]) => (x || []), + addMessage: (x: string[], m: Message) => x.concat(m.data), + } + ``` + + to reducers implementing a filter: + + ```js + { + restore: (x: ?string[]) => (x ? x.filter(predicate) : []), + addMessage: (x: string[], m: Message) => (predicate(m.data) ? x.concat(m.data) : x), + } + ``` + + As soon as the reducers are swapped, the **new** `restore()` will be called with the **previous** data. (If the filter is removed again, the old data that was filtered out can't be magically restored unless it was kept in the state, but hopefully future work to preload data faster than real-time will help us there.) + +- `addMessage: (T, Message) => T`: called when any new message comes in on one of the requested topics. The return value from `addMessage` will be the new return value from `useMessages().reducedValue`. diff --git a/packages/webviz-core/src/PanelAPI/index.js b/packages/webviz-core/src/PanelAPI/index.js new file mode 100644 index 000000000..b67658356 --- /dev/null +++ b/packages/webviz-core/src/PanelAPI/index.js @@ -0,0 +1,18 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +// This file contains hooks and components comprising the public API for Webviz panel development. +// Recommended use: import * as PanelAPI from "webviz-core/src/PanelAPI"; + +// More to come soon! + +export { default as useDataSourceInfo } from "./useDataSourceInfo"; +export type { DataSourceInfo } from "./useDataSourceInfo"; + +export { useMessages } from "webviz-core/src/components/MessageHistory/MessageHistoryOnlyTopics"; +export type { RequestedTopic } from "webviz-core/src/components/MessageHistory/MessageHistoryOnlyTopics"; diff --git a/packages/webviz-core/src/PanelAPI/useDataSourceInfo.js b/packages/webviz-core/src/PanelAPI/useDataSourceInfo.js new file mode 100644 index 000000000..60555277e --- /dev/null +++ b/packages/webviz-core/src/PanelAPI/useDataSourceInfo.js @@ -0,0 +1,46 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import { useCallback } from "react"; +import { type Time } from "rosbag"; + +import { useMessagePipeline } from "webviz-core/src/components/MessagePipeline"; +import { type Topic } from "webviz-core/src/players/types"; +import type { RosDatatypes } from "webviz-core/src/types/RosDatatypes"; + +// Metadata about the source of data currently being displayed in Webviz. +// This is not expected to change often, usually when changing data sources. +export type DataSourceInfo = {| + topics: $ReadOnlyArray, + datatypes: RosDatatypes, + capabilities: string[], + startTime: ?Time, // Only `startTime`, since `endTime` can change rapidly when connected to a live system. + playerId: string, +|}; + +export default function useDataSourceInfo(): DataSourceInfo { + const datatypes = useMessagePipeline(useCallback(({ datatypes: pipelineDatatypes }) => pipelineDatatypes, [])); + const topics = useMessagePipeline(useCallback(({ sortedTopics }) => sortedTopics, [])); + const startTime = useMessagePipeline( + useCallback(({ playerState: { activeData } }) => activeData && activeData.startTime, []) + ); + const capabilities = useMessagePipeline( + useCallback(({ playerState: { capabilities: playerStateCapabilities } }) => playerStateCapabilities, []) + ); + const playerId = useMessagePipeline( + useCallback(({ playerState: { playerId: playerStatePlayerId } }) => playerStatePlayerId, []) + ); + + return { + topics, + datatypes, + capabilities, + startTime, + playerId, + }; +} diff --git a/packages/webviz-core/src/PanelAPI/useDataSourceInfo.test.js b/packages/webviz-core/src/PanelAPI/useDataSourceInfo.test.js new file mode 100644 index 000000000..13dc8291f --- /dev/null +++ b/packages/webviz-core/src/PanelAPI/useDataSourceInfo.test.js @@ -0,0 +1,115 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import { mount } from "enzyme"; +import * as React from "react"; + +import * as PanelAPI from "."; +import { MockMessagePipelineProvider } from "webviz-core/src/components/MessagePipeline"; + +describe("useDataSourceInfo", () => { + const topics = [{ name: "/foo", datatype: "Foo" }]; + const messages = [ + { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 1, nsec: 2 }, + message: {}, + }, + { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 5, nsec: 6 }, + message: {}, + }, + ]; + const datatypes = { + Foo: { fields: [] }, + }; + + // Create a helper component that exposes the results of the hook in a Jest mock function + function createTest() { + function Test() { + return Test.renderFn(PanelAPI.useDataSourceInfo()); + } + Test.renderFn = jest.fn().mockImplementation(() => null); + return Test; + } + + it("returns data from MessagePipelineContext", () => { + const Test = createTest(); + const root = mount( + + + + ); + expect(Test.renderFn.mock.calls).toEqual([ + [ + { + topics: [{ name: "/foo", datatype: "Foo" }], + datatypes: { Foo: { fields: [] } }, + capabilities: ["hello"], + startTime: { sec: 0, nsec: 1 }, + playerId: "1", + }, + ], + ]); + root.unmount(); + }); + + it("doesn't change when messages change", () => { + const Test = createTest(); + const root = mount( + + + + ); + expect(Test.renderFn.mock.calls).toEqual([ + [ + { + topics: [{ name: "/foo", datatype: "Foo" }], + datatypes: { Foo: { fields: [] } }, + capabilities: ["hello"], + startTime: { sec: 0, nsec: 1 }, + playerId: "1", + }, + ], + ]); + Test.renderFn.mockClear(); + + root.setProps({ messages: [messages[1]] }); + expect(Test.renderFn).toHaveBeenCalledTimes(0); + + root.setProps({ topics: [...topics, { name: "/bar", datatype: "Bar" }] }); + expect(Test.renderFn.mock.calls).toEqual([ + [ + { + topics: [{ name: "/bar", datatype: "Bar" }, { name: "/foo", datatype: "Foo" }], + datatypes: { Foo: { fields: [] } }, + capabilities: ["hello"], + startTime: { sec: 0, nsec: 1 }, + playerId: "1", + }, + ], + ]); + + root.unmount(); + }); +}); diff --git a/packages/webviz-core/src/PanelAPI/useMessages.test.js b/packages/webviz-core/src/PanelAPI/useMessages.test.js new file mode 100644 index 000000000..06bae9c06 --- /dev/null +++ b/packages/webviz-core/src/PanelAPI/useMessages.test.js @@ -0,0 +1,271 @@ +// @flow +// +// Copyright (c) 2019-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import { mount } from "enzyme"; +import * as React from "react"; + +import * as PanelAPI from "."; +import { MockMessagePipelineProvider } from "webviz-core/src/components/MessagePipeline"; + +describe("useMessages", () => { + // Create a helper component that exposes restore, addMessage, and the results of the hook for mocking + function createTest() { + function Test({ topics }) { + Test.result( + PanelAPI.useMessages({ + topics, + addMessage: Test.addMessage, + restore: Test.restore, + }).reducedValue + ); + return null; + } + Test.result = jest.fn(); + Test.restore = jest.fn(); + Test.addMessage = jest.fn(); + return Test; + } + + it("calls restore to initialize without messages", async () => { + const Test = createTest(); + Test.restore.mockReturnValue(1); + + const root = mount( + + + + ); + + await Promise.resolve(); + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([]); + expect(Test.result.mock.calls).toEqual([[1]]); + + root.unmount(); + }); + + it("calls restore to initialize and addMessage for initial messages", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const message = { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 2 }, + }; + + const root = mount( + + + + ); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message]]); + expect(Test.result.mock.calls).toEqual([[2]]); + + root.unmount(); + }); + + it("calls addMessage for messages added later", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const message1 = { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 2 }, + }; + const message2 = { + op: "message", + datatype: "Bar", + topic: "/bar", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 3 }, + }; + + const root = mount( + + + + ); + + root.setProps({ messages: [message1] }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message1]]); + expect(Test.result.mock.calls).toEqual([[1], [2]]); + + // Subscribe to a new topic, then receive a message on that topic + root.setProps({ children: }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message1]]); + expect(Test.result.mock.calls).toEqual([[1], [2], [2]]); + + root.setProps({ messages: [message2] }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message1], [2, message2]]); + expect(Test.result.mock.calls).toEqual([[1], [2], [2], [3]]); + + root.unmount(); + }); + + it("doesn't re-render for messages on non-subscribed topics", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const message1 = { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 2 }, + }; + const message2 = { + op: "message", + datatype: "Bar", + topic: "/bar", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 3 }, + }; + + const root = mount( + + + + ); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message1]]); + expect(Test.result.mock.calls).toEqual([[2]]); + + root.setProps({ messages: [message2] }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message1]]); + expect(Test.result.mock.calls).toEqual([[2]]); + + root.unmount(); + }); + + it("doesn't re-render when requested topics change", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const message1 = { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 2 }, + }; + const message2 = { + op: "message", + datatype: "Bar", + topic: "/bar", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 3 }, + }; + + const root = mount( + + + + ); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message2]]); + expect(Test.result.mock.calls).toEqual([[3]]); + + // When topics change, we expect useMessages NOT to call addMessage for pre-existing messages. + // (If the player is playing, new messages will come in soon, and if it's paused, we'll backfill.) + // This is because processing the same frame again might lead to duplicate or out-of-order + // addMessages calls. If the user really cares about re-processing the current frame, they can + // change their restore/addMessages reducers. + root.setProps({ children: }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message2]]); + expect(Test.result.mock.calls).toEqual([[3], [3]]); + + root.unmount(); + }); + + it("doesn't re-render when player topics or other playerState changes", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const message = { + op: "message", + datatype: "Foo", + topic: "/foo", + receiveTime: { sec: 0, nsec: 0 }, + message: { value: 2 }, + }; + + const root = mount( + + + + ); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message]]); + expect(Test.result.mock.calls).toEqual([[2]]); + + root.setProps({ topics: ["/foo", "/bar"] }); + root.setProps({ capabilities: ["some_capability"] }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([[1, message]]); + expect(Test.result.mock.calls).toEqual([[2]]); + + root.unmount(); + }); + + it("doesn't re-render when activeData is empty", async () => { + const Test = createTest(); + + Test.restore.mockReturnValue(1); + Test.addMessage.mockImplementation((_, msg) => msg.message.value); + + const root = mount( + + + + ); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([]); + expect(Test.result.mock.calls).toEqual([[1]]); + + root.setProps({ capabilities: ["some_capability"] }); + + expect(Test.restore.mock.calls).toEqual([[undefined]]); + expect(Test.addMessage.mock.calls).toEqual([]); + expect(Test.result.mock.calls).toEqual([[1]]); + + root.unmount(); + }); +}); diff --git a/packages/webviz-core/src/actions/extensions.js b/packages/webviz-core/src/actions/extensions.js index a7c472966..0d6001183 100644 --- a/packages/webviz-core/src/actions/extensions.js +++ b/packages/webviz-core/src/actions/extensions.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. diff --git a/packages/webviz-core/src/actions/index.js b/packages/webviz-core/src/actions/index.js index 8c77741df..d78ace3fe 100644 --- a/packages/webviz-core/src/actions/index.js +++ b/packages/webviz-core/src/actions/index.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. @@ -8,7 +8,7 @@ import type { ExtensionsActions } from "./extensions"; import type { SET_MOSAIC_ID } from "./mosaic"; -import type { NodeDiagnosticsActions } from "./nodeDiagnostics"; import type { PanelsActions } from "./panels"; +import type { UserNodesActions } from "./userNodes"; -export type ActionTypes = PanelsActions | SET_MOSAIC_ID | ExtensionsActions | NodeDiagnosticsActions; +export type ActionTypes = PanelsActions | SET_MOSAIC_ID | ExtensionsActions | UserNodesActions; diff --git a/packages/webviz-core/src/actions/mosaic.js b/packages/webviz-core/src/actions/mosaic.js index 9edeaa42d..28cec0c37 100644 --- a/packages/webviz-core/src/actions/mosaic.js +++ b/packages/webviz-core/src/actions/mosaic.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. diff --git a/packages/webviz-core/src/actions/nodeDiagnostics.js b/packages/webviz-core/src/actions/nodeDiagnostics.js deleted file mode 100644 index 3161e41a1..000000000 --- a/packages/webviz-core/src/actions/nodeDiagnostics.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow -// -// Copyright (c) 2018-present, GM Cruise LLC -// -// This source code is licensed under the Apache License, Version 2.0, -// found in the LICENSE file in the root directory of this source tree. -// You may not use this file except in compliance with the License. - -import type { NodeDiagnostics } from "webviz-core/src/reducers/nodeDiagnostics"; - -type SET_NODE_DIAGNOSTICS = { - type: "SET_NODE_DIAGNOSTICS", - payload: NodeDiagnostics, -}; - -export const setNodeDiagnostics = (payload: NodeDiagnostics) => ({ - type: "SET_NODE_DIAGNOSTICS", - payload, -}); - -export type SetNodeDiagnostics = typeof setNodeDiagnostics; - -export type NodeDiagnosticsActions = SET_NODE_DIAGNOSTICS; diff --git a/packages/webviz-core/src/actions/panels.js b/packages/webviz-core/src/actions/panels.js index c26f77c81..452fafd3a 100644 --- a/packages/webviz-core/src/actions/panels.js +++ b/packages/webviz-core/src/actions/panels.js @@ -1,6 +1,6 @@ // @flow // -// Copyright (c) 2018-present, GM Cruise LLC +// Copyright (c) 2018-present, Cruise LLC // // This source code is licensed under the Apache License, Version 2.0, // found in the LICENSE file in the root directory of this source tree. @@ -14,11 +14,10 @@ import type { SaveConfigPayload, SaveFullConfigPayload, UserNodes, + PlaybackConfig, } from "webviz-core/src/types/panels"; import type { Dispatch, GetState } from "webviz-core/src/types/Store"; -// DANGER: if you change this you break existing layout urls -export const URL_KEY = "layout"; -export const LAYOUT_URL = "layout-url"; +import { LAYOUT_QUERY_KEY } from "webviz-core/src/util/globalConstants"; export type SAVE_PANEL_CONFIG = { type: "SAVE_PANEL_CONFIG", @@ -37,8 +36,8 @@ function maybeStripLayoutId(dispatch: Dispatch, getState: GetState): void { if (location) { const params = new URLSearchParams(location.search); - if (params.get(URL_KEY)) { - params.delete(URL_KEY); + if (params.get(LAYOUT_QUERY_KEY)) { + params.delete(LAYOUT_QUERY_KEY); const newSearch = params.toString(); const searchString = newSearch ? `?${newSearch}` : newSearch; const newPath = `${location.pathname}${searchString}`; @@ -74,8 +73,10 @@ type IMPORT_PANEL_LAYOUT = { export const importPanelLayout = ( payload: ImportPanelLayoutPayload, - isFromUrl: boolean, - skipSettingLocalStorage: boolean = false + { + isFromUrl = false, + skipSettingLocalStorage = false, + }: { isFromUrl?: boolean, skipSettingLocalStorage?: boolean } = {} ): Dispatcher => (dispatch, getState) => { if (!isFromUrl) { maybeStripLayoutId(dispatch, getState); @@ -104,7 +105,7 @@ type OVERWRITE_GLOBAL_DATA = { payload: any, }; -export const overwriteGlobalData = (payload: any): OVERWRITE_GLOBAL_DATA => ({ +export const overwriteGlobalVariables = (payload: any): OVERWRITE_GLOBAL_DATA => ({ type: "OVERWRITE_GLOBAL_DATA", payload, }); @@ -114,7 +115,7 @@ type SET_GLOBAL_DATA = { payload: any, }; -export const setGlobalData = (payload: any): SET_GLOBAL_DATA => ({ +export const setGlobalVariables = (payload: any): SET_GLOBAL_DATA => ({ type: "SET_GLOBAL_DATA", payload, }); @@ -139,6 +140,16 @@ export const setLinkedGlobalVariables = (payload: LinkedGlobalVariables): SET_LI payload, }); +type SET_PLAYBACK_CONFIG = { + type: "SET_PLAYBACK_CONFIG", + payload: PlaybackConfig, +}; + +export const setPlaybackConfig = (payload: PlaybackConfig): SET_PLAYBACK_CONFIG => ({ + type: "SET_PLAYBACK_CONFIG", + payload, +}); + export type PanelsActions = | CHANGE_PANEL_LAYOUT | IMPORT_PANEL_LAYOUT @@ -147,4 +158,5 @@ export type PanelsActions = | OVERWRITE_GLOBAL_DATA | SET_GLOBAL_DATA | SET_WEBVIZ_NODES - | SET_LINKED_GLOBAL_VARIABLES; + | SET_LINKED_GLOBAL_VARIABLES + | SET_PLAYBACK_CONFIG; diff --git a/packages/webviz-core/src/actions/userNodes.js b/packages/webviz-core/src/actions/userNodes.js new file mode 100644 index 000000000..34725c176 --- /dev/null +++ b/packages/webviz-core/src/actions/userNodes.js @@ -0,0 +1,60 @@ +// @flow +// +// Copyright (c) 2018-present, Cruise LLC +// +// This source code is licensed under the Apache License, Version 2.0, +// found in the LICENSE file in the root directory of this source tree. +// You may not use this file except in compliance with the License. + +import type { UserNodeDiagnostics, UserNodeLogs } from "webviz-core/src/players/UserNodePlayer/types"; + +type SET_USER_NODE_DIAGNOSTICS = { + type: "SET_USER_NODE_DIAGNOSTICS", + payload: UserNodeDiagnostics, +}; + +type ADD_USER_NODE_LOGS = { + type: "ADD_USER_NODE_LOGS", + payload: UserNodeLogs, +}; + +type CLEAR_USER_NODE_LOGS = { + type: "CLEAR_USER_NODE_LOGS", + payload: string, +}; + +type SET_USER_NODE_TRUST = { + type: "SET_USER_NODE_TRUST", + payload: { id: string, trusted: boolean }, +}; + +export const setUserNodeDiagnostics = (payload: UserNodeDiagnostics) => ({ + type: "SET_USER_NODE_DIAGNOSTICS", + payload, +}); + +export const addUserNodeLogs = (payload: UserNodeLogs) => ({ + type: "ADD_USER_NODE_LOGS", + payload, +}); + +export const clearUserNodeLogs = (payload: string) => ({ + type: "CLEAR_USER_NODE_LOGS", + payload, +}); + +export const setUserNodeTrust = (payload: { id: string, trusted: boolean }) => ({ + type: "SET_USER_NODE_TRUST", + payload, +}); + +export type AddUserNodeLogs = typeof addUserNodeLogs; +export type ClearUserNodeLogs = typeof clearUserNodeLogs; +export type SetUserNodeDiagnostics = typeof setUserNodeDiagnostics; +export type SetUserNodeTrust = typeof setUserNodeTrust; + +export type UserNodesActions = + | ADD_USER_NODE_LOGS + | CLEAR_USER_NODE_LOGS + | SET_USER_NODE_DIAGNOSTICS + | SET_USER_NODE_TRUST; diff --git a/packages/webviz-core/src/assets/Mark.svg b/packages/webviz-core/src/assets/Mark.svg new file mode 100644 index 000000000..4817be097 --- /dev/null +++ b/packages/webviz-core/src/assets/Mark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/webviz-core/src/assets/logo.svg b/packages/webviz-core/src/assets/logo.svg index 31e749cb6..ef86e177b 100644 --- a/packages/webviz-core/src/assets/logo.svg +++ b/packages/webviz-core/src/assets/logo.svg @@ -1,5 +1,5 @@