diff --git a/.gitignore b/.gitignore index 8725630bf8..9b34857e57 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ umd/ typedoc/ +*/**/node_modules */**/yarn.lock yarn-error.log */**/package-lock.json diff --git a/examples/demo-app/package.json b/examples/demo-app/package.json index 24ce8092a6..b93b57e450 100644 --- a/examples/demo-app/package.json +++ b/examples/demo-app/package.json @@ -56,6 +56,7 @@ "babel-loader": "^8.0.0", "babel-plugin-module-resolver": "^3.0.0", "babel-plugin-transform-builtin-extend": "^1.1.0", + "gh-pages": "^3.1.0", "webpack": "^4.29.0", "webpack-cli": "^3.2.1", "webpack-dev-middleware": "^3.5.1", @@ -63,4 +64,4 @@ "webpack-hot-middleware": "^2.24.3", "webpack-stats-plugin": "^0.2.1" } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 391eea3a1c..4a921f730e 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "global": "^4.3.0", "h3-js": "^3.1.0", "html-webpack-plugin": "^4.3.0", + "hubble.gl": "1.1.0-alpha.4", "keymirror": "^0.1.1", "lodash.clonedeep": "^4.0.1", "lodash.curry": "^4.1.1", diff --git a/src/components/common/animation-control/animation-control.js b/src/components/common/animation-control/animation-control.js index c50e88846e..c91e4afa48 100644 --- a/src/components/common/animation-control/animation-control.js +++ b/src/components/common/animation-control/animation-control.js @@ -19,17 +19,20 @@ // THE SOFTWARE. import React from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import moment from 'moment'; import Slider from 'components/common/slider/slider'; -import {BottomWidgetInner} from 'components/common/styled-components'; +import {BottomWidgetInner, Button} from 'components/common/styled-components'; import SpeedControlFactory from './speed-control'; import AnimationPlaybacksFactory from './playback-controls'; import FloatingTimeDisplayFactory from './floating-time-display'; import AnimationControllerFactory from './animation-controller'; import {snapToMarks} from 'utils/data-utils'; import {DEFAULT_TIME_FORMAT, ANIMATION_TYPE} from 'constants'; +import HubbleExport from 'components/hubble-export'; +import {connect as keplerGlConnect} from 'connect/keplergl-connect'; + const SliderWrapper = styled.div` display: flex; @@ -160,6 +163,9 @@ function AnimationControlFactory( buttonHeight={BUTTON_HEIGHT} /> +
+ +
diff --git a/src/components/hubble-export.js b/src/components/hubble-export.js new file mode 100644 index 0000000000..8f14f71e99 --- /dev/null +++ b/src/components/hubble-export.js @@ -0,0 +1,92 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import React, {Component} from 'react'; +import {ThemeProvider, withTheme} from 'styled-components'; + +import {connect as keplerGlConnect} from 'connect/keplergl-connect'; +import RenderSettingsModal from './render-settings-modal'; +import {Button} from 'components/common/styled-components'; +import {theme} from '../styles'; + + +// TODO this isn't DRY. Comes from https://github.com/keplergl/kepler.gl/blob/995024e86880fefb0624af4ed1e98d3879558336/src/components/kepler-gl.js +function mapStateToProps(state = {}, props) { + return { + ...props, + visState: state.visState, + mapStyle: state.mapStyle, + mapState: state.mapState, + uiState: state.uiState, + providerState: state.providerState + // isOpen: state.isOpen TODO + }; +} + +function makeMapDispatchToProps() { + // const getActionCreators = makeGetActionCreators(); + const mapDispatchToProps = (dispatch, ownProps) => { + // const groupedActionCreators = getActionCreators(dispatch, ownProps); + + return { + // TODO Put action creator and return here + // ...groupedActionCreators, + dispatch + }; + }; + + return mapDispatchToProps; +} + +class HubbleExport extends Component { + constructor(props) { + super(props); + this.state = { + isOpen: false + }; + } + handleClose() {this.setState({isOpen: false})} // X button in Modal UI was clicked + + handleExport() { // Export button in Kepler UI was clicked + this.setState(state => ({ + isOpen: true + })); + // stop rendering in bg + // setState + // pop up modal if isOpen. If false, closes modal TODO put function into render + // all the data is passed through and can use in deck/hubble components + return

REACHED

+ } + + render() { + // console.log(this.props) + console.log(this.state) + return ( +
+ + + {/* anonymous function to bind state onclick */} +
+ ) + } +}; + +export default keplerGlConnect(mapStateToProps, makeMapDispatchToProps)(withTheme(HubbleExport)); + diff --git a/src/components/map-container.js b/src/components/map-container.js index 6cc116fc19..59193a0604 100644 --- a/src/components/map-container.js +++ b/src/components/map-container.js @@ -287,12 +287,13 @@ export default function MapContainerFactory(MapPopover, MapControl, Editor) { layerHoverProp.compareType = interactionConfig.tooltip.config.compareType; } } - const commonProp = { - onClose: this._onCloseMapPopover, - mapW: mapState.width, - mapH: mapState.height, - zoom: mapState.zoom - }; + + const commonProp = { + onClose: this._onCloseMapPopover, + mapW: mapState.width, + mapH: mapState.height, + zoom: mapState.zoom + }; return (
@@ -483,8 +484,8 @@ export default function MapContainerFactory(MapPopover, MapControl, Editor) { }; const isEdit = uiState.mapControls.mapDraw.active; - return ( + + + 'pointer' : undefined} transitionDuration={TRANSITION_DURATION} - onMouseMove={this.props.visStateActions.onMouseMove} + onMouseMove={this.props.visStateActions.onMouseMove} > - {this._renderDeckOverlay(layersToRender)} + + + + {this._renderDeckOverlay(layersToRender)} {this._renderMapboxOverlays(layersToRender)} + + { + const {defaultSettingsPos, bottomBuffer, settingsHeight} = this.props; + if (showSettings === false || !this.root || !this.root.current) return defaultSettingsPos; + const {sidePanelInnerPadding = 16, sidePanel = {}, sidePanelScrollBarWidth = 10} = theme; + const sidePanelLeft = (sidePanel.margin || {}).left || 20; + const offsetX = sidePanelInnerPadding + sidePanelLeft + sidePanelScrollBarWidth; + // find component Root position + const bounding = this.root.current.getBoundingClientRect(); + const {x, y, width} = bounding; + + // set the top so it won't collide with bottom widget + const top = + y + settingsHeight <= window.innerHeight - bottomBuffer + ? y + : window.innerHeight - bottomBuffer - settingsHeight; + + return {top: `${top}px`, left: `${x + width + offsetX}px`}; + }); + + modalStylesSelector = createSelector( + this.themeSelector, + this.settingsPosSelector, + (theme, settingsPos) => ({ + content: { + top: 'auto', + left: 'auto', + right: `calc(50% - ${DIMENSIONS.sidePanel.width / 2}px)`, + bottom: '50%', + transform: 'translate(50%, 50%)', + padding: '0px 0px 0px 0px', + border: 0, + backgroundColor: theme.sidePanelBg, + borderRadius: theme.panelBorderRadius || '2px' + }, + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0)' + } + // content: { + // top: 0, + // left: 0, + // border: 0, + // right: 'auto', + // bottom: 'auto', + // padding: '0px 0px 0px 0px', + // borderRadius: theme.panelBorderRadius || '2px' + // }, + // overlay: { + // ...settingsPos, + // right: 'auto', + // bottom: 'auto', + // backgroundColor: 'rgba(0, 0, 0, 0)' + // } + }) + ); + + render() { + const {isOpen, handleClose} = this.props; + + const modalStyles = this.modalStylesSelector(this.props); + return ( + + {this.root.current ? ( + { + // React modal issue: https://github.com/reactjs/react-modal/issues/769 + // failed to execute removeChild on parent node when it is already unmounted + return ( + this.root.current || { + removeChild: () => {}, + appendChild: () => {} + } + ); + }} + > + {handleClose()}} mapData={this.props.mapData}/> + + ) : null} + + ); + } +} + +export default withTheme(RenderSettingsModal); \ No newline at end of file diff --git a/src/components/render-settings-panel.js b/src/components/render-settings-panel.js new file mode 100644 index 0000000000..9e0504cf99 --- /dev/null +++ b/src/components/render-settings-panel.js @@ -0,0 +1,323 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import React, {Component} from 'react'; +import styled, {withTheme} from 'styled-components'; +import {Button, Input} from 'kepler.gl/components/common/styled-components'; +import {Delete} from 'kepler.gl/components/common/icons'; +import ItemSelector from 'kepler.gl/components/common/item-selector/item-selector'; +import {Scene} from 'components/scene'; + +import { + SettingsController, + preview, + setFileNameDeckAdapter, + render + } from 'components/render/settings-controller'; + +import {setKeyframes} from 'components/render/keyframes'; + +const DEFAULT_BUTTON_HEIGHT = '32px'; +const DEFAULT_BUTTON_WIDTH = '64px'; +const DEFAULT_PADDING = '32px'; +const DEFAULT_ROW_GAP = '16px'; + +const IconButton = styled(Button)` + padding: 0; + svg { + margin: 0; + } +`; + +const PanelCloseInner = styled.div` + display: flex; + justify-content: flex-end; + padding: ${DEFAULT_PADDING} ${DEFAULT_PADDING} 0 ${DEFAULT_PADDING}; +`; + +const PanelClose = ({buttonHeight, handleClose}) => ( + + {handleClose()}}> + + + +); + +const StyledTitle = styled.div` + color: ${props => props.theme.titleTextColor}; + font-size: 20px; + font-weight: 400; + line-height: ${props => props.theme.lineHeight}; + padding: 0 ${DEFAULT_PADDING} 16px ${DEFAULT_PADDING}; +`; + +const StyledSection = styled.div` + align-self: center; + color: ${props => props.theme.labelColor}; + font-weight: 500; + font-size: 13px; + margin-top: ${DEFAULT_PADDING}; + margin-bottom: ${DEFAULT_ROW_GAP}; +`; + +const StyledLabelCell = styled.div` + align-self: center; + color: ${props => props.theme.labelColor}; + font-weight: 400; + font-size: 11px; +`; + +const StyledValueCell = styled.div` + align-self: center; + color: ${props => props.theme.textColor}; + font-weight: 500; + font-size: 11px; + padding: 0 12px; +`; + +const PanelBodyInner = styled.div` + padding: 0 ${DEFAULT_PADDING}; + display: grid; + grid-template-columns: 480px auto; + grid-column-gap: 20px; +`; + +const InputGrid = styled.div` + display: grid; + grid-template-columns: 88px auto; + grid-template-rows: repeat( + ${props => props.rows}, + ${props => (props.rowHeight ? props.rowHeight : '34px')} + ); + grid-row-gap: ${DEFAULT_ROW_GAP}; +`; + +const PanelBody = ({mapData, setMediaType, setCamera, setFileName, setQuality, setTimeStamps, settingsData}) => ( + +
+ +
+
+ Video Effects + + Timestamp {/* TODO add functionality */} + + Camera {/* TODO add functionality */} + + + Export Settings {/* TODO add functionality */} + + File Name + + Media Type {/* TODO add functionality */} + + Quality {/* TODO add functionality */} + + + + Duration {/* TODO add functionality */} + 00:00:30 + File Size {/* TODO add functionality */} + 36 MB + +
+
+); + +const PanelFooterInner = styled.div` + display: flex; + justify-content: space-between; + margin-top: ${DEFAULT_ROW_GAP}; + padding: ${DEFAULT_PADDING}; +`; + +const ButtonGroup = styled.div` + display: flex; +`; + +const PanelFooter = ({handleClose, settingsData, preview}) => ( + + + + + + + +); + +const Panel = styled.div` + width: ${props => props.settingsWidth}px; +`; + +class RenderSettingsPanel extends Component { + constructor(props) { + super(props); + + this.state = { + mediaType: "WebM Video", + camera: "None", + fileName: "Video Name", + quality: "High (720p)", + timestamps: "None" + }; + + this.setMediaTypeState = this.setMediaTypeState.bind(this); + this.setCamera = this.setCamera.bind(this); + this.setFileName = this.setFileName.bind(this); + this.setQuality = this.setQuality.bind(this); + this.setTimeStamps = this.setTimeStamps.bind(this); + + this.mapDataGlobal = this.props.mapData; + this.settingsController = ; + + } + + static defaultProps = { + settingsWidth: 980, + buttonHeight: '16px', + }; + + setMediaTypeState(media){ + this.setState({ + mediaType: media + }); + } + setCamera(option){ + this.setState({ + camera: option + }); + setKeyframes(option, this.settingsController.props.adapter, this.mapdataGlobal); + } + setFileName(name){ + this.setState({ + fileName: name.target.value + }); + setFileNameDeckAdapter(name.target.value); + } + setQuality(resolution){ + this.setState({ + quality: resolution + }); + } + setTimeStamps(time){ + this.setState({ + timestamps: time + }); + } + + render() { + + const adapter = this.settingsController.props.adapter; + + const {buttonHeight, settingsWidth, handleClose} = this.props; + const settingsData = { + mediaType : this.state.mediaType, + camera : this.state.camera, + fileName: this.state.fileName, + resolution: this.state.quality, + time: this.state.timestamps, + adapter: adapter + } + + return ( + + + {/* handleClose for X button */} + Export Video + + {/* handleClose for Cancel button */} + + ); + } +} + +export default withTheme(RenderSettingsPanel); \ No newline at end of file diff --git a/src/components/render/encoder-settings.js b/src/components/render/encoder-settings.js new file mode 100644 index 0000000000..8198e8e46b --- /dev/null +++ b/src/components/render/encoder-settings.js @@ -0,0 +1,18 @@ +import React, {Component} from 'react'; + +export const encoderSettings = { + framerate: 30, + webm: { + quality: 0.8 + }, + jpeg: { + quality: 2 + }, + gif: { + sampleInterval: 1000 + }, + webm:{ + quality: 1.5 + }, + filename: "Default Video Name" + " " + moment().format(DEFAULT_TIME_FORMAT).toString() + }; \ No newline at end of file diff --git a/src/components/render/keyframes.js b/src/components/render/keyframes.js new file mode 100644 index 0000000000..37c6e4e874 --- /dev/null +++ b/src/components/render/keyframes.js @@ -0,0 +1,126 @@ +export function setKeyframes(cameraType, adapter, mapdataGlobal){ + adapter.scene.keyframes.camera._lastTime = 0; + adapter.scene.keyframes.camera.factor = 0; + + + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + + if(cameraType === 'Orbit (90º)'){ + // How to reset the camera to its initial position? + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 90; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'Orbit (180º)'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 180; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'Orbit (360º)'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 360; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'North to South'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude + 25; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude - 25; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'South to North'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude - 25; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude + 25; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'East to West'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude + 25; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude - 25; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'West to East'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude - 25; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude + 25; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + }else if(cameraType === 'Zoom Out'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom - 2; + }else if(cameraType === 'Zoom In'){ + adapter.scene.keyframes.camera.values[0].bearing = 0; + adapter.scene.keyframes.camera.values[0].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[0].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[0].pitch = 0; + adapter.scene.keyframes.camera.values[0].zoom = mapdataGlobal.mapState.zoom; + + adapter.scene.keyframes.camera.values[1].bearing = 0; + adapter.scene.keyframes.camera.values[1].latitude = mapdataGlobal.mapState.latitude; + adapter.scene.keyframes.camera.values[1].longitude = mapdataGlobal.mapState.longitude; + adapter.scene.keyframes.camera.values[1].pitch = 0; + adapter.scene.keyframes.camera.values[1].zoom = mapdataGlobal.mapState.zoom + 2; + } + + + console.log("adapter", adapter); + } \ No newline at end of file diff --git a/src/components/render/settings-controller.js b/src/components/render/settings-controller.js new file mode 100644 index 0000000000..4c6c006af4 --- /dev/null +++ b/src/components/render/settings-controller.js @@ -0,0 +1,130 @@ +import React, {Component} from 'react'; +import {DeckScene, CameraKeyframes} from '@hubble.gl/core'; +import {easing} from 'popmotion'; +import {DeckAdapter} from 'hubble.gl'; + +import { + WebMEncoder, + JPEGSequenceEncoder, + PNGSequenceEncoder, + PreviewEncoder, + GifEncoder + } from '@hubble.gl/core'; + +import {DEFAULT_TIME_FORMAT} from 'constants'; +import moment from 'moment'; + +let mapDataGlobal = null; + +const encoderSettings = { + framerate: 30, + webm: { + quality: 0.8 + }, + jpeg: { + quality: 2 + }, + gif: { + sampleInterval: 1000 + }, + webm:{ + quality: 1 + }, + filename: "Default Video Name" + " " + moment().format(DEFAULT_TIME_FORMAT).toString() + }; + +export class SettingsController extends Component{ + constructor(props) { + super(props); + + // I need to pass mapDataGlobal to be used here + } + + static defaultProps = { + adapter: new DeckAdapter(sceneBuilder) + }; + +} + + function sceneBuilder(animationLoop) { + console.log(" this.props.mapDataGlobal", mapDataGlobal); + const data = {}; + const keyframes = { + camera: new CameraKeyframes({ + timings: [0, 1000], + keyframes: [ + { + longitude: mapDataGlobal.mapState.longitude, + latitude: mapDataGlobal.mapState.latitude, + zoom: mapDataGlobal.mapState.zoom, + pitch: 0, + bearing: 0 + /* longitude: 11, + latitude: 0, + zoom: 2, + pitch: 0, + bearing: 0*/ + }, + { + longitude: mapDataGlobal.mapState.longitude, + latitude: mapDataGlobal.mapState.latitude, + zoom: mapDataGlobal.mapState.zoom, + bearing: 0, + pitch: 0 + /* longitude: 11, + latitude: 0, + zoom: 2, + pitch: 0, + bearing: 0*/ + } + ], + easings: [easing.easeInOut] + }) + }; + animationLoop.timeline.attachAnimation(keyframes.camera); + + // TODO: Figure out how to set up the size + return new DeckScene({ + animationLoop, + keyframes, + lengthMs: 1000, + data, + width: 480, + height: 460 + }); + } + + export function preview(adapter) { + adapter.render(PreviewEncoder, encoderSettings, ()=>{}); + } + + export function setFileNameDeckAdapter(name){ + encoderSettings.filename = name + " " + moment().format(DEFAULT_TIME_FORMAT).toString(); + } + + export function render(settingsdata, adapter){ + + // setResolution(settingsdata.resolution); // Remove this + + if (settingsdata.mediaType === 'WebM Video') { + adapter.render(WebMEncoder, encoderSettings, () => {}); // Onstop callback + } else if (settingsdata.mediaType === 'PNG Sequence') { + adapter.render(PNGSequenceEncoder, encoderSettings, () => {}); + } else if (settingsdata.mediaType === 'JPEG Sequence') { + adapter.render(JPEGSequenceEncoder, encoderSettings, () => {}); + } + // preview(); + } + + /*function setResolution(resolution){ + if(resolution === 'Good (540p)'){ + adapter.scene.width = 960; + adapter.scene.height = 540; + }else if(resolution === 'High (720p)'){ + adapter.scene.width = 1280; + adapter.scene.height = 720; + }else if(resolution === 'Highest (1080p)'){ + adapter.scene.width = 1920; + adapter.scene.height = 1080; + } +}*/ diff --git a/src/components/scene.js b/src/components/scene.js new file mode 100644 index 0000000000..4d02b68247 --- /dev/null +++ b/src/components/scene.js @@ -0,0 +1,228 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import React, {Component, useState, useRef} from 'react'; + +//Map Component +import DeckGL from '@deck.gl/react'; +import {OVERLAY_TYPE} from 'layers/base-layer'; +import MapboxGLMap from 'react-map-gl'; +import {transformRequest} from 'utils/map-style-utils/mapbox-utils'; + +import {MapView} from '@deck.gl/core'; +import {TileLayer} from '@deck.gl/geo-layers'; +import {BitmapLayer, PathLayer} from '@deck.gl/layers'; + +import { load } from "@loaders.gl/core"; + +const MAPBOX_TOKEN = 'pk.eyJ1IjoicGlvbmVlci1tZSIsImEiOiJjanA0OXMwM2IwcW5qM2tvYnAyYndpdXMxIn0.bqxGkqM2ozOVT57GuVzEjw'; +const TRANSITION_DURATION = 0; + +const INITIAL_VIEW_STATE = { + latitude: 47.65, + longitude: 7, + zoom: 4.5, + maxZoom: 20, + maxPitch: 89, + bearing: 0 +}; + + +const tileLayer = new TileLayer({ + + autoHighlight : true, + highlightColor: [60, 60, 60, 40], + opacity: 1, + // https://wiki.openstreetmap.org/wiki/Zoom_levels + minZoom: 0, + maxZoom: 19, + tileSize: 256, + + + data: [ + `http://{d90016be4e11c76b57d0311404f546f06afbae25}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png` + ], + + + renderSubLayers: props => { + const { + bbox: { west, south, east, north } + } = props.tile; + + return new BitmapLayer(props, { + data: [], + image: props.data, + bounds: [west, south, east, north] + }); + } +}) + + +/* global window */ +const devicePixelRatio = (typeof window !== 'undefined' && window.devicePixelRatio) || 1; + +export class Scene extends Component { + + constructor(props) { + super(props); + + this.mapData = this.props.mapData; + + //this.adapter = new DeckAdapter(this.props.sceneBuilder); + } + + + _renderLayer = (overlays, idx) => { + const datasets = this.mapData.visState.datasets; + const layers = this.mapData.visState.layers; + const layerData = this.mapData.visState.layerData; + const hoverInfo = this.mapData.visState.hoverInfo; + const clicked = this.mapData.visState.clicked; + const mapState = this.mapData.mapState; + const interactionConfig = this.mapData.visState.interactionConfig; + const animationConfig = this.mapData.visState.animationConfig; + + const layer = layers[idx]; + const data = layerData[idx]; + const {gpuFilter} = datasets[layer.config.dataId] || {}; + + const objectHovered = clicked || hoverInfo; + const layerCallbacks = { + onSetLayerDomain: val => this._onLayerSetDomain(idx, val) + }; + + // Layer is Layer class + const layerOverlay = layer.renderLayer({ + data, + gpuFilter, + idx, + interactionConfig, + layerCallbacks, + mapState, + animationConfig, + objectHovered + }); + return overlays.concat(layerOverlay || []); + }; + + // Testing purposes + print(prop){ + console.log("this.deckgl", prop); + }; + + // Is this being used right? + componentDidMount() { + this.forceUpdate(); + } + + // This is provisional - + // [ADD] TileLayer to the array of layers + + + + + + + + // interactionConfig, + render() { + // console.log("all props ", this.props.mapData); + + const mapStyle = this.mapData.mapStyle; + const mapState = this.mapData.mapState; + const layers = this.mapData.visState.layers; + const layerData = this.mapData.visState.layerData; + const layerOrder = this.mapData.visState.layerOrder; + const animationConfig = this.mapData.visState.animationConfig; + const useDevicePixels = false; + //Map data + const mapboxApiAccessToken = this.mapData.mapStyle.mapboxApiAccessToken; + const mapboxApiUrl = this.mapData.mapStyle.mapboxApiUrl; + // define trip and geojson layers + let deckGlLayers = []; + + + + + // wait until data is ready before render data layers + if (layerOrder && layerOrder.length) { + // last layer render first + deckGlLayers = layerOrder + .slice() + .reverse() + .filter( + idx => layers[idx].overlayType === OVERLAY_TYPE.deckgl && layers[idx].id + ) + .reduce(this._renderLayer, []); + } + + /* deckGlLayers[2] = deckGlLayers[1]; + deckGlLayers[1] = deckGlLayers[0] + deckGlLayers[0] = tileLayer; */ + + + + console.log("deckGlLayers ", deckGlLayers); + + // MapboxGLMap + const mapProps = { + ...mapState, + preserveDrawingBuffer: true, + mapboxApiAccessToken, + mapboxApiUrl, + transformRequest + }; + + const style = { + position: 'relative' + } + console.log("adapter from scene ", this.props.adapter); + + return ( +
+ {this.deckgl={current:r}}} + viewState={mapState} + id="default-deckgl-overlay2" + layers={deckGlLayers} + useDevicePixels={useDevicePixels} + style={style} + views={new MapView({repeat: true})} + /* onBeforeRender={this._onBeforeRender} // Not yet + onHover={visStateActions.onLayerHover} // Not yet + onClick={visStateActions.onLayerClick}*/ // Not yet + {...this.props.adapter.getProps(this.deckgl, () => {}, () => {this.forceUpdate()})} + > + + 'pointer' : undefined} + transitionDuration={TRANSITION_DURATION} + > + + +
+ ); + } + +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 751d5a3634..60727feff4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1356,6 +1356,23 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@hubble.gl/core@1.1.0-alpha.4": + version "1.1.0-alpha.4" + resolved "https://registry.yarnpkg.com/@hubble.gl/core/-/core-1.1.0-alpha.4.tgz#85d2ab543e7dc8c9cad61aaad3b2ee32d9aad860" + integrity sha512-QMW3SF0AYuvR2sARkmTGPV4gvVsMh0MIqytbdkxkqqLODv1O9HLtnpUSGvE3Z/6u0J02mmHqPIECRWKAAAj4NA== + dependencies: + "@loaders.gl/core" "^2.1.6" + "@loaders.gl/video" "2.2.0-alpha.1" + "@luma.gl/engine" "^8.2.0" + downloadjs "^1.4.7" + popmotion "^8.6.10" + webm-writer "^0.2.2" + +"@hubble.gl/react@1.1.0-alpha.4": + version "1.1.0-alpha.4" + resolved "https://registry.yarnpkg.com/@hubble.gl/react/-/react-1.1.0-alpha.4.tgz#a63f06ffd107df32f4297bae8b7659d27a214c3a" + integrity sha512-808DCV9LWshTa4wMYYgy6vqFBlxg7mu0EpXQNlItubHwFEeBUCIWbh2BS1lrhU9QBMUC2d6c85PFlzLmZx6yaQ== + "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" @@ -2243,6 +2260,14 @@ "@babel/runtime" "^7.3.1" "@loaders.gl/loader-utils" "2.2.5" +"@loaders.gl/core@^2.1.6": + version "2.2.8" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-2.2.8.tgz#fb2060f3b90d3babe0de2a128fc5227f921c08ad" + integrity sha512-PM74zQA6Kn6NJCMoRdEDENy9jQWQyMkICKZ6o8UGa6/oi3dD4LO5GhFZSwU6W/Lxl/IeUwaZBQ3MqfT3aXA0Dg== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "2.2.8" + "@loaders.gl/csv@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@loaders.gl/csv/-/csv-2.2.5.tgz#4667cbfb62b98d918a1c4e233bfb880939ab74fb" @@ -2293,7 +2318,7 @@ "@babel/runtime" "^7.3.1" "@probe.gl/stats" "^3.3.0" -"@loaders.gl/loader-utils@^2.2.5": +"@loaders.gl/loader-utils@2.2.8", "@loaders.gl/loader-utils@^2.1.3", "@loaders.gl/loader-utils@^2.2.5": version "2.2.8" resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-2.2.8.tgz#9f12f3460260b15e961fa46c3d3c867d7bf48bee" integrity sha512-3F2VgxJPxjuZwAr8fDBkZzx3Wg275XdVaWnyYQ3uyZQ22yGEkrzKc+TlgcqSrxbO5pHrtS5PdS7dmyoJo2+Bdg== @@ -2361,6 +2386,14 @@ "@math.gl/web-mercator" "^3.2.0" "@probe.gl/stats" "^3.3.0" +"@loaders.gl/video@2.2.0-alpha.1": + version "2.2.0-alpha.1" + resolved "https://registry.yarnpkg.com/@loaders.gl/video/-/video-2.2.0-alpha.1.tgz#1bb09e9e507605a72c6e8a13b34926b69c717191" + integrity sha512-4NDe3fNPOTS4MvPxlxrbqr/kKgkB9tOVY2in/s/oYBo0uiDK+WSU0AztnyKxF1b/EsHzTEIzhOEhjvSwbgRQgQ== + dependencies: + "@loaders.gl/loader-utils" "^2.1.3" + gifshot "^0.4.5" + "@luma.gl/constants@8.2.0", "@luma.gl/constants@^8.2.0": version "8.2.0" resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.2.0.tgz#0789c629edbb8b69b7f810ae10c91501b97c634b" @@ -2377,7 +2410,7 @@ "@luma.gl/shadertools" "8.2.0" "@luma.gl/webgl" "8.2.0" -"@luma.gl/engine@8.2.0": +"@luma.gl/engine@8.2.0", "@luma.gl/engine@^8.2.0": version "8.2.0" resolved "https://registry.yarnpkg.com/@luma.gl/engine/-/engine-8.2.0.tgz#e598b723639619e00cff8acaf4af39de4d9fb61f" integrity sha512-HxlwU66TCJzMCY+wuxiyapiGFEp0HrRqg2m2fj8K5h5PBYMjDZSrs2JuDoOz97oFVALfujAEw74OX9pFr5uiRg== @@ -2674,6 +2707,22 @@ dependencies: "@types/node" ">= 8" +"@popmotion/easing@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@popmotion/easing/-/easing-1.0.2.tgz#17d925c45b4bf44189e5a38038d149df42d8c0b4" + integrity sha512-IkdW0TNmRnWTeWI7aGQIVDbKXPWHVEYdGgd5ZR4SH/Ty/61p63jCjrPxX1XrR7IGkl08bjhJROStD7j+RKgoIw== + +"@popmotion/popcorn@^0.4.4": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@popmotion/popcorn/-/popcorn-0.4.4.tgz#a5f906fccdff84526e3fcb892712d7d8a98d6adc" + integrity sha512-jYO/8319fKoNLMlY4ZJPiPu8Ea8occYwRZhxpaNn/kZsK4QG2E7XFlXZMJBsTWDw7I1i0uaqyC4zn1nwEezLzg== + dependencies: + "@popmotion/easing" "^1.0.1" + framesync "^4.0.1" + hey-listen "^1.0.8" + style-value-types "^3.1.7" + tslib "^1.10.0" + "@probe.gl/stats@3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@probe.gl/stats/-/stats-3.2.1.tgz#4e95c75513229ebb01384045e89524a466c022eb" @@ -6159,6 +6208,11 @@ dotignore@~0.1.2: dependencies: minimatch "^3.0.4" +downloadjs@^1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c" + integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw= + draco3d@^1.3.4: version "1.3.6" resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.3.6.tgz#e4d9e7d846637775328c903721c932b953dce331" @@ -7378,6 +7432,14 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +framesync@^4.0.0, framesync@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-4.0.4.tgz#79c42c0118f26821c078570db0ff81fb863516a2" + integrity sha512-mdP0WvVHe0/qA62KG2LFUAOiWLng5GLpscRlwzBxu2VXOp6B8hNs5C5XlFigsMgrfDrr2YbqTsgdWZTc4RXRMQ== + dependencies: + hey-listen "^1.0.8" + tslib "^1.10.0" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -7617,6 +7679,11 @@ gif-encoder@~0.4.1: dependencies: readable-stream "~1.1.9" +gifshot@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/gifshot/-/gifshot-0.4.5.tgz#e3cb570203a3b139ff3069d7578098a29c03b0f8" + integrity sha1-48tXAgOjsTn/MGnXV4CYopwDsPg= + git-raw-commits@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5" @@ -8139,6 +8206,11 @@ he@1.2.x, he@^1.1.0, he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hey-listen@^1.0.5, hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + highlight.js@^10.0.0: version "10.0.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.0.3.tgz#5effcc58420f113f279a0badb8ac50c4be06e63b" @@ -8428,6 +8500,14 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" +hubble.gl@1.1.0-alpha.4: + version "1.1.0-alpha.4" + resolved "https://registry.yarnpkg.com/hubble.gl/-/hubble.gl-1.1.0-alpha.4.tgz#0393a11d1272af35ca69950e86d25dce315682b5" + integrity sha512-Xb7rKzBG4fPuUgTQ/sPkFucidwG4Ld2CkkXGGh6ikC2OAOrUs3IffhCmeOwecPkg66tBrULOfpfT8XDnViYbZQ== + dependencies: + "@hubble.gl/core" "1.1.0-alpha.4" + "@hubble.gl/react" "1.1.0-alpha.4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -12498,6 +12578,19 @@ pngjs@^3.0.0, pngjs@^3.3.3: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== +popmotion@^8.6.10: + version "8.7.3" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-8.7.3.tgz#330a77fd8b288f0e8f930e3d3f351d6eb4897860" + integrity sha512-OcpS/V9sCJjrKiVfp3JB5kp5SBqefZ4RvM9GBLYgv0YbULxv9S5METP9ueVJxSClR3yrfFEY2pLWTWKLn/EUfg== + dependencies: + "@popmotion/easing" "^1.0.1" + "@popmotion/popcorn" "^0.4.4" + framesync "^4.0.0" + hey-listen "^1.0.5" + style-value-types "^3.1.7" + stylefire "^7.0.1" + tslib "^1.10.0" + portfinder@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" @@ -15084,6 +15177,14 @@ strong-log-transformer@^2.0.0: minimist "^1.2.0" through "^2.3.4" +style-value-types@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.1.7.tgz#3d7d3cf9cb9f9ee86c00e19ba65d6a181a0db33a" + integrity sha512-jPaG5HcAPs3vetSwOJozrBXxuHo9tjZVnbRyBjxqb00c2saIoeuBJc1/2MtvB8eRZy41u/BBDH0CpfzWixftKg== + dependencies: + hey-listen "^1.0.8" + tslib "^1.10.0" + styled-components@^4.1.3: version "4.4.1" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.4.1.tgz#e0631e889f01db67df4de576fedaca463f05c2f2" @@ -15103,6 +15204,17 @@ styled-components@^4.1.3: stylis-rule-sheet "^0.0.10" supports-color "^5.5.0" +stylefire@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/stylefire/-/stylefire-7.0.3.tgz#9120ecbb084111788e0ddaa04074799750f20d1d" + integrity sha512-Q0l7NSeFz/OkX+o6/7Zg3VZxSAZeQzQpYomWmIpOehFM/rJNMSLVX5fgg6Q48ut2ETNKwdhm97mPNU643EBCoQ== + dependencies: + "@popmotion/popcorn" "^0.4.4" + framesync "^4.0.0" + hey-listen "^1.0.8" + style-value-types "^3.1.7" + tslib "^1.10.0" + stylis-rule-sheet@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -16498,6 +16610,11 @@ webidl-conversions@^5.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== +webm-writer@^0.2.2: + version "0.2.5" + resolved "https://registry.yarnpkg.com/webm-writer/-/webm-writer-0.2.5.tgz#cca049f392c5e47a328d83590e983f97e8f28fda" + integrity sha512-wzHOIZkvKDOC/GJdOalYlzH0NypLvlaXdUhliTkH7Y5akdmBl0TOVmRTPFW3yfw+cAYS3RMtu5nCYIGovDUsvw== + webpack-bundle-analyzer@^3.0.3, webpack-bundle-analyzer@^3.3.2: version "3.6.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd"