From 096ece0588c5fb43417a0bc6e4b4a7d5ebdafaed Mon Sep 17 00:00:00 2001 From: Alexander Goryushkin Date: Tue, 21 May 2024 22:38:09 -0400 Subject: [PATCH 1/4] close the panel when switching to Viewer --- src/editor/components/Main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor/components/Main.js b/src/editor/components/Main.js index 7d83c8c94..d17c55804 100644 --- a/src/editor/components/Main.js +++ b/src/editor/components/Main.js @@ -278,7 +278,7 @@ export default class Main extends Component { )} - {this.state.isAddLayerPanelOpen && ( + {this.state.inspectorEnabled && this.state.isAddLayerPanelOpen && ( Date: Tue, 21 May 2024 23:03:29 -0400 Subject: [PATCH 2/4] set the first Layers tab open by default --- .../AddLayerPanel/AddLayerPanel.component.jsx | 73 +++---------------- .../components/AddLayerPanel/LayersOptions.js | 51 +++++++++++++ 2 files changed, 60 insertions(+), 64 deletions(-) create mode 100644 src/editor/components/components/AddLayerPanel/LayersOptions.js diff --git a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx index 7b9477885..5cebe2ccd 100644 --- a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx +++ b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import styles from './AddLayerPanel.module.scss'; import classNames from 'classnames'; import { Button } from '../Button'; import { Chevron24Down, Load24Icon, Plus20Circle } from '../../../icons'; import { Dropdown } from '../Dropdown'; import CardPlaceholder from '../../../../../ui_assets/card-placeholder.svg'; -import { cardsData } from './cardsData'; +import { LayersOptions } from './LayersOptions.js'; import { createSvgExtrudedEntity, createMapbox, @@ -17,7 +17,8 @@ import { import Events from '../../../lib/Events'; const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { - const [selectedOption, setSelectedOption] = useState(null); + // set the first Layers option when opening the panel + const [selectedOption, setSelectedOption] = useState(LayersOptions[0].value); const [groupedMixins, setGroupedMixins] = useState([]); useEffect(() => { @@ -61,62 +62,6 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { return groupedArray; }; - const options = [ - { - value: 'Layers: Streets & Intersections', - label: 'Layers: Streets & Intersections', - onClick: () => console.log('Layers: Streets & Intersections') - }, - { - value: 'Models: Personal Vehicles', - label: 'Models: Personal Vehicles', - mixinGroups: ['vehicles', 'vehicles-rigged'], - onClick: () => console.log('Models: Personal Vehicles') - }, - { - value: 'Models: Transit Vehicles', - label: 'Models: Transit Vehicles', - mixinGroups: ['vehicles-transit'], - onClick: () => console.log('Models: Transit Vehicles') - }, - { - value: 'Models: Utility Vehicles', - label: 'Models: Utility Vehicles', - mixinGroups: ['vehicles-rigged'], - onClick: () => console.log('Models: Utility Vehicles') - }, - { - value: 'Models: Characters', - label: 'Models: Characters', - mixinGroups: ['people', 'people-rigged'], - onClick: () => console.log('Models: Characters') - }, - { - value: 'Models: Street Props', - label: 'Models: Street Props', - mixinGroups: ['sidewalk-props', 'intersection-props'], - onClick: () => console.log('Models: Street Props') - }, - { - value: 'Models: dividers', - label: 'Models: dividers', - mixinGroups: ['dividers'], - onClick: () => console.log('Models: dividers') - }, - { - value: 'Models: Buildings', - label: 'Models: Buildings', - mixinGroups: ['buildings'], - onClick: () => console.log('Models: Buildings') - }, - { - value: 'Models: stencils', - label: 'Models: stencils', - mixinGroups: ['stencils'], - onClick: () => console.log('Models: stencils') - } - ]; - // data for layers cards const layersData = [ { @@ -178,7 +123,7 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { // get array with objects data (cardsData) from mixinGroups of selectedOption const getSelectedMixinCards = (selectedOption) => { if (!selectedOption) return []; - const selectedOptionData = options.find( + const selectedOptionData = LayersOptions.find( (option) => option.value === selectedOption ); const selectedMixinGroupNames = selectedOptionData.mixinGroups; @@ -195,7 +140,7 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { }; let selectedCards; - if (selectedOption == 'Layers: Streets & Intersections') { + if (selectedOption === 'Layers: Streets & Intersections') { selectedCards = layersData; } else { selectedCards = getSelectedMixinCards(selectedOption); @@ -222,8 +167,8 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { /* get the ancestor of element in which the added elements will be placed, inside the .custom-group - in this order: - - ancestor with class segment-parent-..., + in this order: + - ancestor with class segment-parent-..., - elements .street-parent/.buildings-parent, - or if the element is a child of a-scene */ @@ -345,7 +290,7 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { console.log('Layers: Streets & Intersections') + }, + { + value: 'Models: Personal Vehicles', + label: 'Models: Personal Vehicles', + mixinGroups: ['vehicles', 'vehicles-rigged'], + onClick: () => console.log('Models: Personal Vehicles') + }, + { + value: 'Models: Transit Vehicles', + label: 'Models: Transit Vehicles', + mixinGroups: ['vehicles-transit'], + onClick: () => console.log('Models: Transit Vehicles') + }, + { + value: 'Models: Utility Vehicles', + label: 'Models: Utility Vehicles', + mixinGroups: ['vehicles-rigged'], + onClick: () => console.log('Models: Utility Vehicles') + }, + { + value: 'Models: Characters', + label: 'Models: Characters', + mixinGroups: ['people', 'people-rigged'], + onClick: () => console.log('Models: Characters') + }, + { + value: 'Models: Street Props', + label: 'Models: Street Props', + mixinGroups: ['sidewalk-props', 'intersection-props'], + onClick: () => console.log('Models: Street Props') + }, + { + value: 'Models: dividers', + label: 'Models: dividers', + mixinGroups: ['dividers'], + onClick: () => console.log('Models: dividers') + }, + { + value: 'Models: Buildings', + label: 'Models: Buildings', + mixinGroups: ['buildings'], + onClick: () => console.log('Models: Buildings') + } +]; + +export { LayersOptions }; From ba1110f709a57c369c80c0790a272c6d5de23159 Mon Sep 17 00:00:00 2001 From: Alexander Goryushkin Date: Tue, 21 May 2024 23:05:50 -0400 Subject: [PATCH 3/4] Attach the close button to the top of the panel when scroll --- .../components/AddLayerPanel/AddLayerPanel.module.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/editor/components/components/AddLayerPanel/AddLayerPanel.module.scss b/src/editor/components/components/AddLayerPanel/AddLayerPanel.module.scss index df2ebeedd..f7f85b436 100644 --- a/src/editor/components/components/AddLayerPanel/AddLayerPanel.module.scss +++ b/src/editor/components/components/AddLayerPanel/AddLayerPanel.module.scss @@ -11,7 +11,7 @@ width: calc(100% - 82px); transition: transform 0.3s ease-in-out; z-index: 100; - padding: 20px 40px 20px 42px; + padding: 0px 40px 20px 42px; margin: 0 auto; overflow-y: scroll; @@ -42,6 +42,7 @@ align-items: center; justify-content: space-between; margin-bottom: 24px; + margin-top: -30px; } .cards { @@ -72,7 +73,7 @@ } .closeButton { - position: absolute; + position: sticky; width: 64px; height: 40px; transform: translateX(-50%); From af5bdd9ab806bc9359d04a681fa78adcf7795c20 Mon Sep 17 00:00:00 2001 From: Alexander Goryushkin Date: Sat, 25 May 2024 12:29:40 -0400 Subject: [PATCH 4/4] merge fix-linting-errors into fix-issues-ui-entity-panel --- package.json | 2 +- src/aframe-streetmix-parsers.js | 36 +- src/assets.js | 3 - src/components/screentock.js | 6 +- src/components/street-geo.js | 49 +- src/components/streetplan-loader.js | 15 +- src/components/svg-extruder.js | 4 - src/editor/components/MainWrapper.js | 1 - .../AddLayerButton.component.jsx | 1 - .../components/Button/Button.component.jsx | 5 +- .../components/components/CommonComponents.js | 1 - .../Dropdown/Dropdown.component.jsx | 1 - .../components/Logo/Logo.component.jsx | 1 - .../components/Logo/Logo.stories.jsx | 1 - .../components/components/Logo/logos.jsx | 2 - .../ProfileButton/ProfileButton.component.jsx | 1 - .../SceneCard/SceneCard.component.jsx | 27 +- .../SceneEditTitle.component.jsx | 4 +- .../components/Tabs/Tabs.component.jsx | 1 - .../Tabs/components/Hint/Hint.component.jsx | 4 +- .../ProfileModal/ProfileModal.component.jsx | 6 +- .../components/modals/ProfileModal/icons.jsx | 2 - .../ScenesModal/ScenesModal.component.jsx | 6 +- .../ScreenshotModal.component.jsx | 10 +- .../SignInModal/SignInModal.component.jsx | 2 +- src/editor/icons/icons.jsx | 2 - src/editor/lib/toolbar.js | 1 + src/index.js | 4 +- src/json-utils.js | 671 ------------------ src/json-utils_0.9.js | 273 ------- src/json-utils_1.1.js | 26 +- src/streetplan/conversion-map.js | 2 - src/streetplan/streetplan-utils.js | 2 +- 33 files changed, 96 insertions(+), 1076 deletions(-) delete mode 100644 src/json-utils.js delete mode 100644 src/json-utils_0.9.js diff --git a/package.json b/package.json index 0bc34f638..64297d59b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "webpack serve --config webpack.config.js --open --progress", "start:prod": "serve public -l 3333", "dist": "webpack --config webpack.prod.config.js --progress", - "lint": "eslint 'src/**/*.js' 'src/**/*.jsx'", + "lint": "eslint --ext .js,.jsx src", "lint:fix": "eslint 'src/**/*.js' 'src/**/*.jsx' --fix", "prefirebase": "cp -R assets public && cp -R ui_assets public && cp index.html public && cp -R dist public", "prepare": "husky install", diff --git a/src/aframe-streetmix-parsers.js b/src/aframe-streetmix-parsers.js index 16d9c9e83..553d52ea8 100644 --- a/src/aframe-streetmix-parsers.js +++ b/src/aframe-streetmix-parsers.js @@ -2,7 +2,6 @@ // Orientation - default model orientation is "outbound" (away from camera) var streetmixParsersTested = require('./tested/aframe-streetmix-parsers-tested'); -var streetmixUtils = require('./tested/streetmix-utils'); var { segmentVariants } = require('./segments-variants.js'); function cloneMixinAsChildren({ @@ -265,10 +264,6 @@ function getRandomArbitrary(min, max) { return Math.random() * (max - min) + min; } -function getRandomElem(arr) { - return arr[getRandomIntInclusive(0, arr.length - 1)]; -} - function getZPositions(start, end, step) { const len = Math.floor((end - start) / step) + 1; var arr = Array(len) @@ -451,7 +446,7 @@ function addLinearStreetAnimation( const startingDistanceToTravel = Math.abs(halfStreet - zPos); const startingDuration = (startingDistanceToTravel / speed) * 1000; - const animationAttrs_1 = { + const animationAttrs1 = { property: 'position', easing: 'linear', loop: 'false', @@ -459,7 +454,7 @@ function addLinearStreetAnimation( to: { z: halfStreet }, dur: startingDuration }; - const animationAttrs_2 = { + const animationAttrs2 = { property: 'position', easing: 'linear', loop: 'true', @@ -468,8 +463,8 @@ function addLinearStreetAnimation( delay: startingDuration, dur: totalStreetDuration }; - reusableObjectEl.setAttribute('animation__1', animationAttrs_1); - reusableObjectEl.setAttribute('animation__2', animationAttrs_2); + reusableObjectEl.setAttribute('animation__1', animationAttrs1); + reusableObjectEl.setAttribute('animation__2', animationAttrs2); return reusableObjectEl; } @@ -506,7 +501,7 @@ function createDriveLaneElement( 'angled-rear-right': 120 }; let rotationY; - if (lineVariant == 'sideways') { + if (lineVariant === 'sideways') { rotationY = rotationVariants['sideways'][direction]; } else { rotationY = rotationVariants[lineVariant]; @@ -525,7 +520,7 @@ function createDriveLaneElement( const driveLaneParentEl = document.createElement('a-entity'); - if (variantList.length == 1) { + if (variantList.length === 1) { // if there is no cars return driveLaneParentEl; } @@ -611,7 +606,7 @@ function createDriveLaneElement( ); const randPlaces = allPlaces.slice(0, count); const carSizeZ = - lineVariant == 'sideways' || lineVariant.includes('angled') + lineVariant === 'sideways' || lineVariant.includes('angled') ? 'width' : 'length'; @@ -971,7 +966,7 @@ function createSeparatorElement( // show warning message if segment or variantString are not supported function supportCheck(segmentType, segmentVariantString) { - if (segmentType == 'separator') return; + if (segmentType === 'separator') return; // variants supported in 3DStreet const supportedVariants = segmentVariants[segmentType]; if (!supportedVariants) { @@ -1038,7 +1033,7 @@ function processSegments( // elevation property from streetmix segment const elevation = segments[i].elevation; - elevationLevels = [0, 0.2, 0.4]; + const elevationLevels = [0, 0.2, 0.4]; const elevationPosY = elevationLevels[elevation]; // add y elevation position as a data attribute to segment entity @@ -1614,20 +1609,20 @@ function processSegments( // calculate position X and rotation Z for T-markings let markingPosX = segmentWidthInMeters / 2; - if (markingsRotZ == 90 && variantList[1] == 'right') { + if (markingsRotZ === 90 && variantList[1] === 'right') { markingsRotZ = -90; markingPosX = -markingPosX + 0.75; } else { markingPosX = markingPosX - 0.75; } - if (variantList[0] == 'sideways' || variantList[0].includes('angled')) { + if (variantList[0] === 'sideways' || variantList[0].includes('angled')) { carStep = 3; markingLength = segmentWidthInMeters; markingPosX = 0; parkingMixin = 'markings solid-stripe'; } - markingPosXY = markingPosX + ' 0'; + const markingPosXY = markingPosX + ' 0'; const clonedStencilRadius = length / 2 - carStep; segmentParentEl.append( @@ -1715,8 +1710,6 @@ function processSegments( // create new brown box to represent ground underneath street const dirtBox = document.createElement('a-box'); const xPos = cumulativeWidthInMeters / 2; - console.log('xPos', xPos); - console.log('`${xPos} -1.1 0`', `${xPos} -1.1 0`); dirtBox.setAttribute('position', `${xPos} -1.1 0`); // what is x? x = 0 - cumulativeWidthInMeters / 2 dirtBox.setAttribute('height', 2); // height is 2 meters from y of -0.1 to -y of 2.1 dirtBox.setAttribute('width', cumulativeWidthInMeters); @@ -1788,8 +1781,9 @@ function processBuildings(left, right, streetWidth, showGround, length) { 'compound-wall': 'ground-asphalt-material' }; + let groundParentEl; if (currentValue === 'waterfront') { - var groundParentEl = document.createElement('a-ocean-box'); + groundParentEl = document.createElement('a-ocean-box'); groundParentEl.setAttribute('geometry', { primitive: 'box', depth: length, @@ -1801,7 +1795,7 @@ function processBuildings(left, right, streetWidth, showGround, length) { }); groundParentEl.setAttribute('position', { y: -3 }); } else { - var groundParentEl = document.createElement('a-box'); + groundParentEl = document.createElement('a-box'); groundParentEl.setAttribute('depth', length); groundParentEl.setAttribute('height', 2); groundParentEl.setAttribute('width', length / 2); diff --git a/src/assets.js b/src/assets.js index c605b0982..a95dfc10f 100644 --- a/src/assets.js +++ b/src/assets.js @@ -262,7 +262,6 @@ function buildAssetHTML(assetUrl, categories) { } // JSON with grouped mixin id's. Used to create grouped mixins in Editor right panel - const groupedAssetsJSON = {}; let existsCategoryArray = Object.keys(assetsObj); if (categories) { @@ -274,8 +273,6 @@ function buildAssetHTML(assetUrl, categories) { } let assetsHTML = ''; - const assetsCategoryHTML = ''; - const mixinList = []; for (const categoryName in assetsObj) { if (existsCategoryArray.includes(categoryName)) { const assetsCategoryHTML = assetsObj[categoryName]; diff --git a/src/components/screentock.js b/src/components/screentock.js index 8abd49a46..39c615c52 100644 --- a/src/components/screentock.js +++ b/src/components/screentock.js @@ -117,15 +117,15 @@ AFRAME.registerComponent('screentock', { renderer.domElement ); - if (type == 'img') { + if (type === 'img') { imgElement.src = screenshotCanvas.toDataURL(); } - if (type == 'png') { + if (type === 'png') { downloadImageDataURL( saveFilename, screenshotCanvas.toDataURL('image/png') ); - } else if (type == 'jpg') { + } else if (type === 'jpg') { downloadImageDataURL( saveFilename, screenshotCanvas.toDataURL('image/jpeg', 0.95) diff --git a/src/components/street-geo.js b/src/components/street-geo.js index 6673e13ae..40ec4fbef 100644 --- a/src/components/street-geo.js +++ b/src/components/street-geo.js @@ -1,17 +1,18 @@ -/* global AFRAME, THREE */ -const MAPBOX_ACCESS_TOKEN_VALUE = 'pk.eyJ1Ijoia2llcmFuZmFyciIsImEiOiJjazB0NWh2YncwOW9rM25sd2p0YTlxemk2In0.mLl4sNGDFbz_QXk0GIK02Q'; +/* global AFRAME */ +const MAPBOX_ACCESS_TOKEN_VALUE = + 'pk.eyJ1Ijoia2llcmFuZmFyciIsImEiOiJjazB0NWh2YncwOW9rM25sd2p0YTlxemk2In0.mLl4sNGDFbz_QXk0GIK02Q'; const GOOGLE_API_KEY = 'AIzaSyAQshwLVKTpwTfPJxFEkEzOdP_cgmixTCQ'; /* * Street-geo component * - * the component accept longitude, latitude, elevation and an array of map types to indicate + * the component accept longitude, latitude, elevation and an array of map types to indicate * which child maps to spawn. Possible values for maps array: 'mapbox2d', 'google3d'. * The component assigns the class 'autocreated' to its child elements. - * All attribute values can be changed at runtime and the component will update + * All attribute values can be changed at runtime and the component will update * the child elements (map entities) and their corresponding parameters. * The 'elevation' attribute is only used for the 'google3d' tiles element for now. - * + * * to add support for a new map type, you need to take the following steps: * - add map name to this.mapTypes variable * - add creating function with name: Create @@ -27,17 +28,16 @@ AFRAME.registerComponent('street-geo', { maps: { type: 'array', default: [] } }, init: function () { - /* + /* Function names for the given function types must have the following format: create function: Create, update function: Update, */ - this.mapTypes = ['mapbox2d', 'google3d']; - this.elevationHeightConstant = 32.49158; + this.mapTypes = ['mapbox2d', 'google3d']; + this.elevationHeightConstant = 32.49158; }, update: function (oldData) { const data = this.data; - const el = this.el; const updatedData = AFRAME.utils.diff(oldData, data); @@ -47,14 +47,17 @@ AFRAME.registerComponent('street-geo', { if (data.maps.includes(mapType) && !this[mapType]) { // create Map element and save a link to it in this[mapType] this[mapType] = createMapFunction(); - } else if (data.maps.includes(mapType) && (updatedData.longitude || updatedData.latitude || updatedData.elevation)) { + } else if ( + data.maps.includes(mapType) && + (updatedData.longitude || updatedData.latitude || updatedData.elevation) + ) { // call update map function with name: Update this[mapType + 'Update'].bind(this)(); } else if (this[mapType] && !data.maps.includes(mapType)) { // remove element from DOM and from this object this.el.removeChild(this[mapType]); this[mapType] = null; - } + } } }, mapbox2dCreate: function () { @@ -63,9 +66,14 @@ AFRAME.registerComponent('street-geo', { const mapbox2dElement = document.createElement('a-entity'); mapbox2dElement.setAttribute('data-layer-name', 'Mapbox Satellite Streets'); - mapbox2dElement.setAttribute('geometry', 'primitive: plane; width: 512; height: 512;'); - mapbox2dElement.setAttribute('material', 'color: #ffffff; shader: flat; side: both; transparent: true;'); - //mapbox2dElement.setAttribute('position', '-7 -1 -2'); + mapbox2dElement.setAttribute( + 'geometry', + 'primitive: plane; width: 512; height: 512;' + ); + mapbox2dElement.setAttribute( + 'material', + 'color: #ffffff; shader: flat; side: both; transparent: true;' + ); mapbox2dElement.setAttribute('rotation', '-90 -4.25 0'); mapbox2dElement.setAttribute('anisotropy', ''); mapbox2dElement.setAttribute('mapbox', { @@ -77,7 +85,7 @@ AFRAME.registerComponent('street-geo', { }); mapbox2dElement.classList.add('autocreated'); el.appendChild(mapbox2dElement); - return mapbox2dElement; + return mapbox2dElement; }, google3dCreate: function () { const data = this.data; @@ -92,9 +100,9 @@ AFRAME.registerComponent('street-geo', { height: data.elevation - this.elevationHeightConstant, googleApiKey: GOOGLE_API_KEY, geoTransform: 'WGS84Cartesian', - maximumSSE: 48, - maximumMem: 400, - cameraEl: '#camera' + maximumSSE: 48, + maximumMem: 400, + cameraEl: '#camera' }); google3dElement.classList.add('autocreated'); el.appendChild(google3dElement); @@ -103,9 +111,9 @@ AFRAME.registerComponent('street-geo', { google3dUpdate: function () { const data = this.data; this.google3d.setAttribute('loader-3dtiles', { - lat: data.latitude, + lat: data.latitude, long: data.longitude, - height: data.elevation - this.elevationHeightConstant, + height: data.elevation - this.elevationHeightConstant }); }, mapbox2dUpdate: function () { @@ -113,6 +121,5 @@ AFRAME.registerComponent('street-geo', { this.mapbox2d.setAttribute('mapbox', { center: `${data.longitude}, ${data.latitude}` }); - } }); diff --git a/src/components/streetplan-loader.js b/src/components/streetplan-loader.js index 34d545e4a..05a45e9db 100644 --- a/src/components/streetplan-loader.js +++ b/src/components/streetplan-loader.js @@ -1,6 +1,5 @@ -/* global AFRAME, THREE, XMLHttpRequest */ +/* global AFRAME, XMLHttpRequest */ var streetplanUtils = require('../streetplan/streetplan-utils.js'); -var exampleJSON = require('../streetplan/exampleJSON.json'); AFRAME.registerComponent('streetplan-loader', { dependencies: ['street'], @@ -23,10 +22,9 @@ AFRAME.registerComponent('streetplan-loader', { const streetplanName = streetData.name; // streetplan alternative name - const streetplanAltName = streetData.altName; + // const streetplanAltName = streetData.altName; console.log('streetplanName', streetplanName); - // el.setAttribute('streetplan-loader', 'name', streetplanName); let currentSceneTitle; const sceneEl = this.el.sceneEl; @@ -63,17 +61,10 @@ AFRAME.registerComponent('streetplan-loader', { // First to find the proper path, once to actually load the street, and then subsequent updates such as street name const that = this; const data = this.data; - const el = this.el; - - // /* ***** debugging ***** */ - // setTimeout(()=> { - // this.streetplanResponseParse(exampleJSON); - // }, 1000); - // return; // load from URL encoded Streetplan JSON if (data.streetplanEncJSON) { - const streetplanJSON = decodeURIComponent(encodedString); + const streetplanJSON = decodeURIComponent(data.streetplanEncJSON); this.streetplanResponseParse(JSON.parse(streetplanJSON)); return; } diff --git a/src/components/svg-extruder.js b/src/components/svg-extruder.js index a60f8a477..173319910 100644 --- a/src/components/svg-extruder.js +++ b/src/components/svg-extruder.js @@ -18,10 +18,7 @@ AFRAME.registerComponent('svg-extruder', { lineColor: { type: 'color', default: 'black' } }, init: function () { - const data = this.data; const el = this.el; - const svgString = data.svgString; - const lineColor = data.lineColor; this.loader = new SVGLoader(); el.removeAttribute('material'); @@ -162,7 +159,6 @@ AFRAME.registerComponent('svg-extruder', { // No need to update. // if (Object.keys(oldData).length === 0) { return; } - const el = this.el; const svgString = this.data.svgString; if (svgString) this.extrudeFromSVG(svgString); } diff --git a/src/editor/components/MainWrapper.js b/src/editor/components/MainWrapper.js index 9c00a2f52..11ea617c1 100644 --- a/src/editor/components/MainWrapper.js +++ b/src/editor/components/MainWrapper.js @@ -1,4 +1,3 @@ -import React from 'react'; import { useAuthContext } from '../contexts/index.js'; import Main from './Main'; diff --git a/src/editor/components/components/AddLayerButton/AddLayerButton.component.jsx b/src/editor/components/components/AddLayerButton/AddLayerButton.component.jsx index 9a15ce79c..2940054c3 100644 --- a/src/editor/components/components/AddLayerButton/AddLayerButton.component.jsx +++ b/src/editor/components/components/AddLayerButton/AddLayerButton.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import styles from './AddLayerButton.module.scss'; import { Button } from '../Button'; import { Circle20Icon } from '../../../icons'; diff --git a/src/editor/components/components/Button/Button.component.jsx b/src/editor/components/components/Button/Button.component.jsx index e16cd4e37..f15c5a4ff 100644 --- a/src/editor/components/components/Button/Button.component.jsx +++ b/src/editor/components/components/Button/Button.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { bool, func, node, number, string } from 'prop-types'; import classNames from 'classnames'; @@ -46,8 +45,8 @@ const Button = ({ tabIndex={0} disabled={disabled} id={id} - leadingicon={leadingicon} - trailingicon={trailingicon} + data-leadingicon={leadingicon} + data-trailingicon={trailingicon} > {leadingicon &&
{leadingicon}
} {children} diff --git a/src/editor/components/components/CommonComponents.js b/src/editor/components/components/CommonComponents.js index e101dc3c4..0ad8c99f1 100644 --- a/src/editor/components/components/CommonComponents.js +++ b/src/editor/components/components/CommonComponents.js @@ -14,7 +14,6 @@ import Events from '../../lib/Events'; import Clipboard from 'clipboard'; import { saveBlob } from '../../lib/utils'; import GLTFIcon from '../../../../ui_assets/gltf.svg'; -import Mixins from './Mixins'; // @todo Take this out and use updateEntity? function changeId(componentName, value) { diff --git a/src/editor/components/components/Dropdown/Dropdown.component.jsx b/src/editor/components/components/Dropdown/Dropdown.component.jsx index a02caeff9..3e8acc590 100644 --- a/src/editor/components/components/Dropdown/Dropdown.component.jsx +++ b/src/editor/components/components/Dropdown/Dropdown.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { ArrowDown24Icon, ArrowUp24Icon } from '../../../icons'; import { arrayOf, bool, func, node, shape, string } from 'prop-types'; import { useRef, useState } from 'react'; diff --git a/src/editor/components/components/Logo/Logo.component.jsx b/src/editor/components/components/Logo/Logo.component.jsx index 0e1b7f236..41a97592c 100644 --- a/src/editor/components/components/Logo/Logo.component.jsx +++ b/src/editor/components/components/Logo/Logo.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { EditorLogo, ViewerLogo } from './logos.jsx'; import { Button } from '../Button'; diff --git a/src/editor/components/components/Logo/Logo.stories.jsx b/src/editor/components/components/Logo/Logo.stories.jsx index c43452c51..45eceff1e 100644 --- a/src/editor/components/components/Logo/Logo.stories.jsx +++ b/src/editor/components/components/Logo/Logo.stories.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Logo } from './Logo.component'; import { action } from '@storybook/addon-actions'; diff --git a/src/editor/components/components/Logo/logos.jsx b/src/editor/components/components/Logo/logos.jsx index 2bd9223eb..43eeaa58a 100644 --- a/src/editor/components/components/Logo/logos.jsx +++ b/src/editor/components/components/Logo/logos.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - const EditorLogo = () => ( { - if (showMenu !== null) { - const menuRef = menuRefs.current[showMenu]; - const isClickInsideMenu = menuRef && menuRef.contains(event.target); - const isClickOnToggle = event.target.closest('.menu-toggle'); - const isClickOnMenuItem = event.target.closest('.menu-item'); - if (!isClickInsideMenu && !isClickOnToggle && !isClickOnMenuItem) { - setShowMenu(null); + const handleClickOutside = useCallback( + (event) => { + if (showMenu !== null) { + const menuRef = menuRefs.current[showMenu]; + const isClickInsideMenu = menuRef && menuRef.contains(event.target); + const isClickOnToggle = event.target.closest('.menu-toggle'); + const isClickOnMenuItem = event.target.closest('.menu-item'); + if (!isClickInsideMenu && !isClickOnToggle && !isClickOnMenuItem) { + setShowMenu(null); + } } - } - }; + }, + [showMenu] + ); useEffect(() => { document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; - }, [showMenu]); + }, [handleClickOutside]); return (
diff --git a/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx b/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx index 584c0a20e..bfa5224e3 100644 --- a/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx +++ b/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import styles from './SceneEditTitle.module.scss'; import { CheckMark32Icon, Cross32Icon, Edit32Icon } from '../../../icons'; import { updateSceneIdAndTitle } from '../../../api/scene'; @@ -60,7 +60,7 @@ const SceneEditTitle = ({ sceneData }) => {
diff --git a/src/editor/components/components/Tabs/Tabs.component.jsx b/src/editor/components/components/Tabs/Tabs.component.jsx index 7379a3c9b..e76194921 100644 --- a/src/editor/components/components/Tabs/Tabs.component.jsx +++ b/src/editor/components/components/Tabs/Tabs.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import classNames from 'classnames'; import { arrayOf, bool, func, shape, string } from 'prop-types'; import styles from './Tabs.module.scss'; diff --git a/src/editor/components/components/Tabs/components/Hint/Hint.component.jsx b/src/editor/components/components/Tabs/components/Hint/Hint.component.jsx index ea10d5575..a24f85060 100644 --- a/src/editor/components/components/Tabs/components/Hint/Hint.component.jsx +++ b/src/editor/components/components/Tabs/components/Hint/Hint.component.jsx @@ -15,7 +15,7 @@ import { useEffect } from 'react'; */ const Hint = ({ hint, tab }) => { useEffect(() => { - const hintElement = document?.getElementById(tab.concat('Tab')); + const hintElement = document.getElementById(tab.concat('Tab')); hintElement && !hintElement.hasAttribute('style') && @@ -23,7 +23,7 @@ const Hint = ({ hint, tab }) => { 'style', `left: calc(50% - ${hintElement.clientWidth / 2}px)` ); - }, [document]); + }, [tab]); return (
diff --git a/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx b/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx index 7566a1a9e..20944c9ef 100644 --- a/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx +++ b/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import styles from './ProfileModal.module.scss'; import Modal from '../Modal.jsx'; @@ -6,7 +5,6 @@ import { Button } from '../../components'; import { useAuthContext } from '../../../contexts'; import { signOut } from 'firebase/auth'; import { auth } from '../../../services/firebase'; -import { Download32Icon } from './icons.jsx'; const ProfileModal = ({ isOpen, onClose }) => { const { currentUser, setCurrentUser } = useAuthContext(); @@ -16,11 +14,11 @@ const ProfileModal = ({ isOpen, onClose }) => { await signOut(auth); setCurrentUser(null); }; - + /* const editProfileHandler = () => { // TODO: navigate to edit section }; - +*/ return ( { return () => clearTimeout(timeoutId); } - }, []); + }, [delay]); useEffect(() => { if (!isOpen) { @@ -134,7 +134,7 @@ const ScenesModal = ({ isOpen, onClose, initialTab = 'owner', delay }) => { }; fetchData(); - }, [isOpen, currentUser, selectedTab]); + }, [isOpen, currentUser, selectedTab]); // eslint-disable-line const fetchUserScenes = async () => { return await getUserScenes(currentUser?.uid); diff --git a/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx b/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx index a26d4dfed..07a1fdda6 100644 --- a/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx +++ b/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import styles from './ScreenshotModal.module.scss'; import { @@ -18,7 +18,7 @@ import { db, storage } from '../../../services/firebase'; import { Button, Dropdown, Input } from '../../components'; import Toolbar from '../../scenegraph/Toolbar'; import Modal from '../Modal.jsx'; -import { loginHandler } from '../SignInModal'; +// import { loginHandler } from '../SignInModal'; export const uploadThumbnailImage = async (uploadedFirstTime) => { try { @@ -103,7 +103,7 @@ const saveScreenshot = async (value) => { const screenshotEl = document.getElementById('screenshot'); screenshotEl.play(); - if (value == 'img') { + if (value === 'img') { screenshotEl.setAttribute( 'screentock', 'imgElementSelector', @@ -122,7 +122,7 @@ function ScreenshotModal({ isOpen, onClose }) { const sceneId = STREET.utils.getCurrentSceneId(); let currentUrl; - if (!!sceneId) { + if (sceneId) { currentUrl = 'https://3dstreet.app/#/scenes/' + sceneId + '.json'; } else { currentUrl = window.location.href; @@ -165,7 +165,7 @@ function ScreenshotModal({ isOpen, onClose }) { try { const sceneId = STREET.utils.getCurrentSceneId(); let updatedUrl; - if (!!sceneId) { + if (sceneId) { updatedUrl = 'https://3dstreet.app/#/scenes/' + sceneId + '.json'; } else { updatedUrl = window.location.href; diff --git a/src/editor/components/modals/SignInModal/SignInModal.component.jsx b/src/editor/components/modals/SignInModal/SignInModal.component.jsx index 21b5112d5..3699cb9e2 100644 --- a/src/editor/components/modals/SignInModal/SignInModal.component.jsx +++ b/src/editor/components/modals/SignInModal/SignInModal.component.jsx @@ -1,4 +1,3 @@ -import React, { useCallback } from 'react'; import { GoogleSignInButtonSVG } from '../../../icons'; import Modal from '../Modal.jsx'; import styles from './SignInModal.module.scss'; @@ -22,6 +21,7 @@ const SignInModal = ({ isOpen, onClose }) => ( className={styles.docsLink} href="https://www.3dstreet.org/docs/3dstreet-editor/saving-and-loading-scenes/#3dstreet-cloud-account" target="_blank" + rel="noreferrer" > This is beta software which may not work as expected.{' '} diff --git a/src/editor/icons/icons.jsx b/src/editor/icons/icons.jsx index b19844eec..cb0a1c61e 100644 --- a/src/editor/icons/icons.jsx +++ b/src/editor/icons/icons.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - const Camera32Icon = () => ( { - const currentSceneTitle = - AFRAME.scenes[0].getAttribute('metadata').sceneTitle; - console.log('currentSceneTitle', currentSceneTitle); - return currentSceneTitle; -}; -STREET.utils.getCurrentSceneTitle = getCurrentSceneTitle; - -/* -Takes one or more elements (from a DOM queryselector call) -and returns a Javascript object -*/ -function convertDOMElToObject(entity) { - const data = []; - const environmentElement = document.querySelector('#environment'); - const referenceEntities = document.querySelector('#reference-layers'); - const sceneEntities = [entity, environmentElement, referenceEntities]; - - for (const entry of sceneEntities) { - const entityData = getElementData(entry); - if (entityData) { - data.push(entityData); - } - } - return { - title: 'scene', - version: '1.0', - data: data - }; -} - -function getElementData(entity) { - if (!entity.isEntity) { - return; - } - // node id's that should save without child nodes - const skipChildrenNodes = ['environment']; - const elementTree = getAttributes(entity); - const children = entity.childNodes; - if (children.length && !skipChildrenNodes.includes(elementTree.id)) { - elementTree['children'] = []; - for (const child of children) { - if (child.nodeType === Node.ELEMENT_NODE) { - elementTree['children'].push(getElementData(child)); - } - } - } - return elementTree; -} - -function getAttributes(entity) { - const elemObj = {}; - - elemObj['element'] = entity.tagName.toLowerCase(); - - if (entity.id) { - elemObj['id'] = entity.id; - } - if (entity.className) { - // convert from DOMTokenList to Array - elemObj['class'] = Array.from(entity.classList); - } - if (entity.getAttribute('mixin')) { - elemObj['mixin'] = entity.getAttribute('mixin'); - } - if (entity.getAttribute('data-layer-name')) { - elemObj['data-layer-name'] = entity.getAttribute('data-layer-name'); - } - const entityComponents = entity.components; - - if (entityComponents) { - const geometryAttr = entity.getAttribute('geometry'); - if (geometryAttr && geometryAttr.primitive) { - elemObj['primitive'] = geometryAttr.primitive; - } - - elemObj['components'] = {}; - for (const componentName in entityComponents) { - const modifiedProperty = getModifiedProperty(entity, componentName); - if (modifiedProperty) { - if (isEmpty(modifiedProperty)) { - elemObj['components'][componentName] = ''; - } else { - elemObj['components'][componentName] = toPropString(modifiedProperty); - } - } - } - } - return elemObj; -} - -function toPropString(propData) { - if ( - typeof propData === 'string' || - typeof propData === 'number' || - typeof propData === 'boolean' || - Array.isArray(propData) - ) { - return propData.toString(); - } - if ( - propData.isVector3 || - propData.isVector2 || - propData.isVector4 || - (propData.hasOwnProperty('x') && propData.hasOwnProperty('y')) - ) { - return AFRAME.utils.coordinates.stringify(propData); - } - if (typeof propData === 'object') { - return Object.entries(propData) - .map(([key, value]) => { - if (key == 'src') { - if (value.id) { - return `${key}: #${value.id}`; - } else { - return `${key}: ${value}`; - } - } else { - return `${key}: ${toPropString(value)}`; - } - }) - .join('; '); - } -} - -function isSingleProperty(schema) { - return AFRAME.schema.isSingleProperty(schema); -} - -function isEmpty(object) { - return Object.keys(object).length === 0; -} - -// a list of component:value pairs to exclude from the JSON string. -// * - remove component with any value -// "propName": {"attribute": "..."} - remove attribute from component -const removeProps = { - src: {}, - normalMap: {}, - 'set-loader-from-hash': '*', - 'create-from-json': '*', - street: { JSON: '*' } -}; -// a list of component_name:new_component_name pairs to rename in JSON string -const renameProps = { - 'streetmix-loader': 'not-streetmix-loader', - street: 'not-street', - intersection: 'not-intersection' -}; - -function filterJSONstreet(removeProps, renameProps, streetJSON) { - function removeValueCheck(removeVal, value) { - if (AFRAME.utils.deepEqual(removeVal, value) || removeVal === '*') { - return true; - } - return undefined; - } - - let stringJSON = JSON.stringify(streetJSON, function replacer(key, value) { - const compAttributes = AFRAME.utils.styleParser.parse(value); - for (var removeKey in removeProps) { - // check for removing components - if (key === removeKey) { - const removeVal = removeProps[removeKey]; - // check for deleting component's attribute - if (typeof removeVal === 'object' && !isEmpty(removeVal)) { - // remove attribute in component - const attrNames = Object.keys(removeVal); - for (var attrName of attrNames) { - const attrVal = removeVal[attrName]; - if ( - Object.prototype.hasOwnProperty.call(compAttributes, attrName) && - removeValueCheck(attrVal, compAttributes[attrName]) - ) { - delete compAttributes[attrName]; - } - } - } - // for other cases - if (removeValueCheck(removeVal, value)) { - return undefined; - } - } - } - - return compAttributes; - }); - // rename components - for (var renameKey in renameProps) { - const reKey = new RegExp(`"${renameKey}":`, 'g'); - stringJSON = stringJSON.replaceAll(reKey, `"${renameProps[renameKey]}":`); - } - return stringJSON; -} - -/** - * function from 3dstreet-editor/src/lib/entity.js - * Gets the value for a component or component's property coming from mixins of - * an element. - * - * If the component or component's property is not provided by mixins, the - * functions will return `undefined`. - * - * @param {Component} component Component to be found. - * @param {string} [propertyName] If provided, component's property to be - * found. - * @param {Element} source Element owning the component. - * @return The value of the component or components' - * property coming from mixins of the source. - */ -function getMixedValue(component, propertyName, source) { - var value; - var reversedMixins = source.mixinEls.reverse(); - for (var i = 0; value === undefined && i < reversedMixins.length; i++) { - var mixin = reversedMixins[i]; - /* eslint-disable-next-line no-prototype-builtins */ - if (mixin.attributes.hasOwnProperty(component.name)) { - if (!propertyName) { - value = mixin.getAttribute(component.name); - } else { - value = mixin.getAttribute(component.name)[propertyName]; - } - } - } - return [component.name, value]; -} - -function shallowEqual(object1, object2) { - if ( - (typeof object1 === 'string' && typeof object2 === 'string') || - (typeof object1 === 'number' && typeof object2 === 'number') - ) { - return object1 === object2; - } - const keys1 = Object.keys(object1); - const keys2 = Object.keys(object2); - - if (keys1.length !== keys2.length) { - return false; - } - - for (const key of keys1) { - if (object1[key] !== object2[key]) { - return false; - } - } - - return true; -} - -function getModifiedProperty(entity, componentName) { - const data = AFRAME.utils.entity.getComponentProperty(entity, componentName); - - // if it is element's attribute - if (!entity.components[componentName]) { - if (!['id', 'class', 'tag', 'mixin'].includes(componentName)) { - return data; - } else { - return null; - } - } - - const defaultData = entity.components[componentName].schema; - - // component's data, that exists in the element's mixin - const [mixinCompName, mixinsData] = getMixedValue( - entity.components[componentName], - null, - entity - ); - - const mixinSkipProps = [ - 'src', - 'atlas-uvs', - 'gltf-model', - 'gltf-part', - 'shadow' - ]; - if (mixinsData && mixinSkipProps.includes(mixinCompName)) { - // skip properties, if they exists in element's mixin - return null; - } - // If its single-property like position, rotation, etc - if (isSingleProperty(defaultData)) { - const defaultValue = defaultData.default; - const currentValue = data; - if (mixinsData && shallowEqual(mixinsData, currentValue)) { - // property will be get from mixin - return null; - } - - if ((currentValue || defaultValue) && currentValue !== defaultValue) { - return data; - } - } - const diff = {}; - for (const key in data) { - // in case the property value is not in schema, but needs to be saved - const defaultValue = defaultData[key] ? defaultData[key].default : ''; - const currentValue = data[key]; - - if ( - mixinsData && - mixinsData[key] && - shallowEqual(mixinsData[key], data[key]) - ) { - continue; - } - // Some parameters could be null and '' like mergeTo - if ( - (currentValue || defaultValue) && - !AFRAME.utils.deepEqual(currentValue, defaultValue) - ) { - diff[key] = data[key]; - } - } - return diff; -} - -function createEntities(entitiesData, parentEl) { - const sceneElement = document.querySelector('a-scene'); - const removeEntities = ['environment', 'reference-layers']; - for (const entityData of entitiesData) { - if ( - entityData.id === 'street-container' && - entityData.children && - entityData.children[0].id === 'default-street' && - entityData.children[0].components.hasOwnProperty('set-loader-from-hash') - ) { - delete entityData.children[0].components['set-loader-from-hash']; - } - - const sceneChildElement = document.getElementById(entityData.id); - if (sceneChildElement) { - if (removeEntities.includes(entityData.id)) { - // remove existing elements from scene - sceneChildElement.remove(); - } else { - // or save link to the element - entityData.entityElement = sceneChildElement; - } - } - - createEntityFromObj(entityData, sceneElement); - } -} - -/* -Add a new entity with a list of components and children (if exists) - * @param {object} entityData Entity definition to add: - * { - * element: String ('a-entity' for Example), - * id: String, - * class: {Array} of element classes, - * mixin: String, - * children: {Array} of entities, - * components: {geometry: 'primitive:box', ...} - * } - * @param {Element} parentEl the parent element to which the Entity will be added - * @return {Element} Entity created -*/ -function createEntityFromObj(entityData, parentEl) { - const entity = - entityData.entityElement || document.createElement(entityData.element); - - if (!entity.parentEl && parentEl) { - parentEl.appendChild(entity); - } - - if (entityData['primitive']) { - // define a primitive in advance to apply other primitive-specific geometry properties - entity.setAttribute('geometry', 'primitive', entityData['primitive']); - } - - // load this attributes in advance in right order to correctly apply other specific components - for (const attr of ['geometry', 'material']) { - if (entityData.components[attr]) { - entity.setAttribute(attr, entityData.components[attr]); - delete entityData.components[attr]; - } - } - - if (entityData.id) { - entity.setAttribute('id', entityData.id); - } - - if (entityData.class) { - entity.classList.add(...entityData.class); - } - - if (entityData['data-layer-name']) { - entity.setAttribute('data-layer-name', entityData['data-layer-name']); - } - - entity.addEventListener('loaded', () => { - // load attributes - for (const attr in entityData.components) { - entity.setAttribute(attr, entityData.components[attr]); - } - - if (entityData.mixin) { - entity.setAttribute('mixin', entityData.mixin); - } - // Ensure the components are loaded before update the UI - - entity.emit('entitycreated', {}, false); - }); - - if (entityData.children) { - for (const childEntityData of entityData.children) { - createEntityFromObj(childEntityData, entity); - } - } -} - -/* - Code imported from index.html, mix of save load utils and some ui functions -*/ - -AFRAME.registerComponent('metadata', { - schema: { - sceneTitle: { default: '' }, - sceneId: { default: '' } - }, - init: function () {}, - update: function (oldData) { - const sceneTitle = this.data.sceneTitle; - if (sceneTitle !== oldData.sceneTitle) { - this.el.emit('newTitle', { sceneTitle: sceneTitle }); - } - } -}); - -AFRAME.registerComponent('scene-title', { - schema: { - titleText: { default: '' } - }, - init: function () { - this.titleElement = undefined; - this.el.addEventListener('newTitle', (evt) => { - this.el.setAttribute('scene-title', 'titleText', evt.detail.sceneTitle); - }); - }, - createTitleElement: function (titleText) { - const titleDiv = (this.titleElement = document.createElement('div')); - const newContent = document.createTextNode(titleText); - titleDiv.setAttribute('id', 'sceneTitle'); - titleDiv.appendChild(newContent); - document.body.append(titleDiv); - }, - updateTitleText: function (titleText) { - this.titleElement.textContent = titleText; - }, - update: function (oldData) { - // If `oldData` is empty, then this means we're in the initialization process. - // No need to update. - if (Object.keys(oldData).length === 0) { - return; - } - - const titleText = this.data.titleText; - const titleElement = this.titleElement; - - if (titleText !== oldData.titleText) { - if (!titleElement) { - this.createTitleElement(titleText); - } else { - this.updateTitleText(titleText); - } - } - } -}); - -AFRAME.registerComponent('set-loader-from-hash', { - dependencies: ['streetmix-loader'], - schema: { - defaultURL: { type: 'string' } - }, - init: function () { - this.runOnce = false; - }, - play: function () { - // using play instead of init method so scene loads before setting its metadata component - if (!this.runOnce) { - this.runOnce = true; - // get hash from window - const streetURL = window.location.hash.substring(1); - if (!streetURL) { - return; - } - if (streetURL.includes('//streetmix.net')) { - console.log( - '[set-loader-from-hash]', - 'Set streetmix-loader streetmixStreetURL to', - streetURL - ); - this.el.setAttribute( - 'streetmix-loader', - 'streetmixStreetURL', - streetURL - ); - } else { - // try to load JSON file from remote resource - console.log( - '[set-loader-from-hash]', - 'Load 3DStreet scene with fetchJSON from', - streetURL - ); - this.fetchJSON(streetURL); - } - // else { - // console.log('[set-loader-from-hash]','Using default URL', this.data.defaultURL) - // this.el.setAttribute('streetmix-loader', 'streetmixStreetURL', this.data.defaultURL); - // } - } - }, - fetchJSON: function (requestURL) { - const request = new XMLHttpRequest(); - request.open('GET', requestURL, true); - request.onload = function () { - if (this.status >= 200 && this.status < 400) { - // Connection success - // remove 'set-loader-from-hash' component from json data - const jsonData = JSON.parse(this.response, (key, value) => - key === 'set-loader-from-hash' ? undefined : value - ); - - console.log( - '[set-loader-from-hash]', - '200 response received and JSON parsed, now createElementsFromJSON' - ); - createElementsFromJSON(jsonData); - const sceneId = getUUIDFromPath(requestURL); - if (sceneId) { - console.log('sceneId from fetchJSON from url hash loader', sceneId); - AFRAME.scenes[0].setAttribute('metadata', 'sceneId', sceneId); - } - } else if (this.status === 404) { - console.error( - '[set-loader-from-hash] Error trying to load scene: Resource not found.' - ); - STREET.notify.errorMessage( - 'Error trying to load scene: Resource not found.' - ); - } - }; - request.onerror = function () { - // There was a connection error of some sort - console.error( - 'Loading Error: There was a connection error during JSON loading' - ); - STREET.notify.errorMessage('Could not fetch scene.'); - }; - request.send(); - } -}); - -function getUUIDFromPath(path) { - // UUID regex pattern: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} - const uuidPattern = - /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/; - - const match = path.match(uuidPattern); - if (match) { - return match[0]; - } - - return null; // return null or whatever default value you prefer if no UUID found -} - -// this use os text input prompt, delete current scene, then load streetmix file -function inputStreetmix() { - streetmixURL = prompt( - 'Please enter a Streetmix URL', - 'https://streetmix.net/kfarr/3/example-street' - ); - setTimeout(function () { - window.location.hash = streetmixURL; - }); - streetContainerEl = document.getElementById('street-container'); - while (streetContainerEl.firstChild) { - streetContainerEl.removeChild(streetContainerEl.lastChild); - } - AFRAME.scenes[0].setAttribute('metadata', 'sceneId', ''); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', ''); - streetContainerEl.innerHTML = - ''; -} - -// JSON loading starts here -function getValidJSON(stringJSON) { - // Preserve newlines, etc. - use valid JSON - // Remove non-printable and other non-valid JSON characters - return stringJSON - .replace(/\'/g, '') - .replace(/\n/g, '') - .replace(/[\u0000-\u0019]+/g, ''); -} - -function createElementsFromJSON(streetJSON) { - let streetObject = {}; - if (typeof streetJSON === 'string') { - const validJSONString = getValidJSON(streetJSON); - streetObject = JSON.parse(validJSONString); - } else if (typeof streetJSON === 'object') { - streetObject = streetJSON; - } - - const sceneTitle = streetObject.title; - if (sceneTitle) { - console.log('sceneTitle from createElementsFromJSON', sceneTitle); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', sceneTitle); - } - - streetContainerEl = document.getElementById('street-container'); - while (streetContainerEl.firstChild) { - streetContainerEl.removeChild(streetContainerEl.lastChild); - } - - createEntities(streetObject.data, streetContainerEl); - STREET.notify.successMessage('Scene loaded from JSON'); -} - -// viewer widget click to paste json string of 3dstreet scene -function inputJSON() { - const stringJSON = prompt('Please paste 3DStreet JSON string'); - if (stringJSON) { - createElementsFromJSON(stringJSON); - } -} - -// handle viewer widget click to open 3dstreet json scene -function fileJSON() { - const reader = new FileReader(); - reader.onload = function () { - AFRAME.scenes[0].setAttribute('metadata', 'sceneId', ''); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', ''); - createElementsFromJSON(reader.result); - }; - reader.readAsText(this.files[0]); -} diff --git a/src/json-utils_0.9.js b/src/json-utils_0.9.js deleted file mode 100644 index 5c2e41671..000000000 --- a/src/json-utils_0.9.js +++ /dev/null @@ -1,273 +0,0 @@ -/* global AFRAME, Node */ - -/* -Takes one or more elements (from a DOM queryselector call) -and returns a Javascript object -*/ -function convertDOMElToObject(entity) { - const data = []; - if (entity.length) { - for (const entry of entity) { - data.push(getElementData(entry)); - } - } else { - data.push(getElementData(entity)); - } - return { data: data }; -} - -function getElementData(entity) { - const elementTree = getAttributes(entity); - const children = entity.childNodes; - if (children.length) { - elementTree['children'] = []; - for (const child of children) { - if (child.nodeType === Node.ELEMENT_NODE) { - elementTree['children'].push(getElementData(child)); - } - } - } - return elementTree; -} - -function getAttributes(entity) { - const elemObj = {}; - elemObj['element'] = entity.tagName.toLowerCase(); - - if (entity.id) { - elemObj['id'] = entity.id; - } - if (entity.className) { - // convert from DOMTokenList to Array - elemObj['class'] = Array.from(entity.classList); - } - if (entity.getAttribute('mixin')) { - // convert from DOMTokenList to Array - elemObj['mixin'] = entity.getAttribute('mixin'); - } - const entityComponents = entity.components; - - if (entityComponents) { - const geometryAttr = entity.getAttribute('geometry'); - if (geometryAttr && geometryAttr.primitive) { - elemObj['primitive'] = geometryAttr.primitive; - } - - elemObj['components'] = {}; - for (const componentName in entityComponents) { - const modifiedProperty = getModifiedProperty(entity, componentName); - if (modifiedProperty && !isEmpty(modifiedProperty)) { - elemObj['components'][componentName] = toPropString(modifiedProperty); - } - } - } - return elemObj; -} - -function toPropString(propData) { - if ( - typeof propData === 'string' || - typeof propData === 'number' || - typeof propData === 'boolean' - ) { - return propData.toString(); - } - if ( - propData.isVector3 || - propData.isVector2 || - propData.isVector4 || - (propData.hasOwnProperty('x') && propData.hasOwnProperty('y')) - ) { - return AFRAME.utils.coordinates.stringify(propData); - } - if (typeof propData === 'object') { - return Object.entries(propData) - .map(([key, value]) => { - if (key == 'src') { - if (value.id) { - return `${key}: #${value.id}`; - } else { - return `${key}: ${value}`; - } - } else { - return `${key}: ${toPropString(value)}`; - } - }) - .join('; '); - } -} - -function isSingleProperty(schema) { - return AFRAME.schema.isSingleProperty(schema); -} - -function isEmpty(object) { - return Object.keys(object).length === 0; -} - -// a list of component:value pairs to exclude from the JSON string. -// * - remove component with any value -// "propName": {"attribute": "..."} - remove attribute from component -const removeProps = { - src: {}, - normalMap: {}, - 'create-from-json': '*', - street: { JSON: '*' } -}; -// a list of component_name:new_component_name pairs to rename in JSON string -const renameProps = { - 'streetmix-loader': 'not-streetmix-loader', - street: 'not-street' -}; - -function filterJSONstreet(removeProps, renameProps, streetJSON) { - function removeValueCheck(removeVal, value) { - // console.error(removeVal, value, AFRAME.utils.deepEqual(removeVal, value)) - if (AFRAME.utils.deepEqual(removeVal, value) || removeVal === '*') { - return true; - } - return undefined; - } - - let stringJSON = JSON.stringify(streetJSON, function replacer(key, value) { - for (var removeKey in removeProps) { - // check for removing components - if (key === removeKey) { - const removeVal = removeProps[removeKey]; - // check for deleting component's attribute - if (typeof removeVal === 'object' && !isEmpty(removeVal)) { - // remove attribute in component - const compAttributes = value; - - const attrNames = Object.keys(removeVal); - for (var attrName of attrNames) { - const attrVal = removeVal[attrName]; - if ( - Object.prototype.hasOwnProperty.call(compAttributes, attrName) && - removeValueCheck(attrVal, compAttributes[attrName]) - ) { - delete value[attrName]; - } - } - } - // for other cases - if (removeValueCheck(removeVal, value)) { - return undefined; - } - } - } - - return value; - }); - // rename components - for (var renameKey in renameProps) { - // console.log(renameKey) - const reKey = new RegExp(`"${renameKey}":`); - stringJSON = stringJSON.replace(reKey, `"${renameProps[renameKey]}":`); - } - return stringJSON; -} - -function getModifiedProperty(entity, componentName) { - // const data = entity.components[componentName].data; - const data = AFRAME.utils.entity.getComponentProperty(entity, componentName); - - // if it is element's attribute - if (!entity.components[componentName]) { - if (!['id', 'class', 'tag', 'mixin'].includes(componentName)) { - return data; - } else { - return null; - } - } - - const defaultData = entity.components[componentName].schema; - - // If its single-property like position, rotation, etc - if (isSingleProperty(defaultData)) { - const defaultValue = defaultData.default; - const currentValue = data; - if ((currentValue || defaultValue) && currentValue !== defaultValue) { - return data; - } - } - - const diff = {}; - for (const key in data) { - const defaultValue = defaultData[key].default; - const currentValue = data[key]; - - // Some parameters could be null and '' like mergeTo - if ( - (currentValue || defaultValue) && - !AFRAME.utils.deepEqual(currentValue, defaultValue) - ) { - diff[key] = data[key]; - } - } - return diff; -} - -function createEntities(entitiesData, parentEl) { - for (const entityData of entitiesData) { - createEntityFromObj(entityData, parentEl); - } -} - -/* -Add a new entity with a list of components and children (if exists) - * @param {object} entityData Entity definition to add: - * { - * element: String ('a-entity' for Example), - * id: String, - * class: {Array} of element classes, - * mixin: String, - * children: {Array} of entities, - * components: {geometry: 'primitive:box', ...} - * } - * @param {Element} parentEl the parent element to which the Entity will be added - * @return {Element} Entity created -*/ -function createEntityFromObj(entityData, parentEl) { - const entity = document.createElement(entityData.element); - - if (parentEl) { - parentEl.appendChild(entity); - } - - if (entityData['primitive']) { - // define a primitive in advance to apply other primitive-specific geometry properties - entity.setAttribute('geometry', 'primitive', entityData['primitive']); - } - - if (entityData.id) { - entity.setAttribute('id', entityData.id); - } - - if (entityData.class) { - entity.classList.add(...entityData.class); - } - - entity.addEventListener('loaded', () => { - // load attributes - for (const attr in entityData.components) { - entity.setAttribute(attr, entityData.components[attr]); - } - - if (entityData.mixin) { - entity.setAttribute('mixin', entityData.mixin); - } - // Ensure the components are loaded before update the UI - entity.emit( - 'entitycreated', - { element: entityData.element, components: entity.components }, - false - ); - }); - - if (entityData.children) { - for (const childEntityData of entityData.children) { - createEntityFromObj(childEntityData, entity); - } - } -} diff --git a/src/json-utils_1.1.js b/src/json-utils_1.1.js index fbd7ff5e7..a084b1e9a 100644 --- a/src/json-utils_1.1.js +++ b/src/json-utils_1.1.js @@ -27,7 +27,7 @@ function getCurrentSceneId() { } STREET.utils.getCurrentSceneId = getCurrentSceneId; -getCurrentSceneTitle = () => { +const getCurrentSceneTitle = () => { const currentSceneTitle = AFRAME.scenes[0].getAttribute('metadata').sceneTitle; console.log('currentSceneTitle', currentSceneTitle); @@ -136,14 +136,14 @@ function toPropString(propData) { propData.isVector3 || propData.isVector2 || propData.isVector4 || - (propData.hasOwnProperty('x') && propData.hasOwnProperty('y')) + (propData['x'] && propData['y']) ) { return AFRAME.utils.coordinates.stringify(propData); } if (typeof propData === 'object') { return Object.entries(propData) .map(([key, value]) => { - if (key == 'src') { + if (key === 'src') { // checking to ensure the object's src value is correctly stored if (value.src && !value.src.includes(assetsUrl)) { // asset came from external sources. So need to save it src value if it has @@ -367,7 +367,7 @@ function createEntities(entitiesData, parentEl) { entityData.id === 'street-container' && entityData.children && entityData.children[0].id === 'default-street' && - entityData.children[0].components.hasOwnProperty('set-loader-from-hash') + entityData.children[0].components['set-loader-from-hash'] ) { delete entityData.children[0].components['set-loader-from-hash']; } @@ -625,14 +625,14 @@ function getUUIDFromPath(path) { // this use os text input prompt, delete current scene, then load streetmix file function inputStreetmix() { - streetmixURL = prompt( + const streetmixURL = prompt( 'Please enter a Streetmix URL', 'https://streetmix.net/kfarr/3/example-street' ); setTimeout(function () { window.location.hash = streetmixURL; }); - streetContainerEl = document.getElementById('street-container'); + const streetContainerEl = document.getElementById('street-container'); while (streetContainerEl.firstChild) { streetContainerEl.removeChild(streetContainerEl.lastChild); } @@ -651,9 +651,9 @@ function getValidJSON(stringJSON) { // Preserve newlines, etc. - use valid JSON // Remove non-printable and other non-valid JSON characters return stringJSON - .replace(/\'/g, '') + .replace(/'/g, '') .replace(/\n/g, '') - .replace(/[\u0000-\u0019]+/g, ''); + .replace(/[\u0000-\u0019]+/g, ''); // eslint-disable-line no-control-regex } function createElementsFromJSON(streetJSON) { @@ -671,7 +671,7 @@ function createElementsFromJSON(streetJSON) { AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', sceneTitle); } - streetContainerEl = document.getElementById('street-container'); + const streetContainerEl = document.getElementById('street-container'); while (streetContainerEl.firstChild) { streetContainerEl.removeChild(streetContainerEl.lastChild); } @@ -682,14 +682,6 @@ function createElementsFromJSON(streetJSON) { STREET.utils.createElementsFromJSON = createElementsFromJSON; -// viewer widget click to paste json string of 3dstreet scene -function inputJSON() { - const stringJSON = prompt('Please paste 3DStreet JSON string'); - if (stringJSON) { - createElementsFromJSON(stringJSON); - } -} - // handle viewer widget click to open 3dstreet json scene function fileJSON() { const reader = new FileReader(); diff --git a/src/streetplan/conversion-map.js b/src/streetplan/conversion-map.js index b3c6cb386..82db4ded5 100644 --- a/src/streetplan/conversion-map.js +++ b/src/streetplan/conversion-map.js @@ -307,8 +307,6 @@ function getDataFromSubtypeMap(convertRule, streetmixData, streetplanData) { // convert streetPlan segment data to Streetmix segment data function convertSegment(data) { let streetmixData = {}; - // streetmix variantString - const variantString = ''; const streetplanType = data['Type']; const streetplanSubtype = data['Subtype']; // mapping rule for current Streetplan subtypes diff --git a/src/streetplan/streetplan-utils.js b/src/streetplan/streetplan-utils.js index 015f551ba..d4774a651 100644 --- a/src/streetplan/streetplan-utils.js +++ b/src/streetplan/streetplan-utils.js @@ -1,5 +1,5 @@ // utils for StreetPlan parsing -mappingUtils = require('./conversion-map.js'); +const mappingUtils = require('./conversion-map.js'); // convert width from feet to meters function convertStreetValues(streetData) {