@smarterplan/ngx-smarterplan-core 1.4.20 → 1.4.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
- package/esm2022/lib/components/loader/loader.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
- package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
- package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
- package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
- package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
- package/esm2022/lib/config.mjs +1 -1
- package/esm2022/lib/helpers.service.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
- package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +49 -28
- package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
- package/esm2022/lib/mattertagData.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.module.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
- package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
- package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
- package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
- package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
- package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
- package/esm2022/lib/services/base-tab.service.mjs +1 -1
- package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
- package/esm2022/lib/services/content.service.mjs +1 -1
- package/esm2022/lib/services/filter.service.mjs +1 -1
- package/esm2022/lib/services/intervention.service.mjs +1 -1
- package/esm2022/lib/services/locale.service.mjs +1 -1
- package/esm2022/lib/services/matterport-import.service.mjs +1 -1
- package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
- package/esm2022/lib/services/matterport-navigation.service.mjs +1 -1
- package/esm2022/lib/services/matterport-object3d.service.mjs +657 -133
- package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
- package/esm2022/lib/services/matterport-tag-copy.service.mjs +1 -1
- package/esm2022/lib/services/matterport-tag.service.mjs +1 -1
- package/esm2022/lib/services/matterport.service.mjs +346 -25
- package/esm2022/lib/services/models/affectation.service.mjs +1 -1
- package/esm2022/lib/services/models/base-object.service.mjs +1 -1
- package/esm2022/lib/services/models/capture.service.mjs +1 -1
- package/esm2022/lib/services/models/comment.service.mjs +1 -1
- package/esm2022/lib/services/models/domain.service.mjs +1 -1
- package/esm2022/lib/services/models/equipment.service.mjs +1 -1
- package/esm2022/lib/services/models/event.service.mjs +1 -1
- package/esm2022/lib/services/models/feature.service.mjs +1 -1
- package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
- package/esm2022/lib/services/models/layer.service.mjs +1 -1
- package/esm2022/lib/services/models/measurement.service.mjs +1 -1
- package/esm2022/lib/services/models/mission.service.mjs +1 -1
- package/esm2022/lib/services/models/navigation.service.mjs +1 -1
- package/esm2022/lib/services/models/node.service.mjs +1 -1
- package/esm2022/lib/services/models/object3D.service.mjs +1 -1
- package/esm2022/lib/services/models/operation.service.mjs +1 -1
- package/esm2022/lib/services/models/organisation.service.mjs +1 -1
- package/esm2022/lib/services/models/plan.service.mjs +1 -1
- package/esm2022/lib/services/models/poi.service.mjs +27 -27
- package/esm2022/lib/services/models/profile.service.mjs +1 -1
- package/esm2022/lib/services/models/property.service.mjs +1 -1
- package/esm2022/lib/services/models/space.service.mjs +1 -1
- package/esm2022/lib/services/models/template.service.mjs +1 -1
- package/esm2022/lib/services/models/ticket.service.mjs +1 -1
- package/esm2022/lib/services/models/visit.service.mjs +1 -1
- package/esm2022/lib/services/models/zone.service.mjs +1 -1
- package/esm2022/lib/services/navigator.service.mjs +1 -1
- package/esm2022/lib/services/s3.service.mjs +1 -1
- package/esm2022/lib/services/search.service.mjs +1 -1
- package/esm2022/lib/services/support.service.mjs +1 -1
- package/esm2022/lib/services/sweep-locator.service.mjs +1 -1
- package/esm2022/lib/services/tag.service.mjs +16 -16
- package/esm2022/lib/services/threejs-import.service.mjs +1 -1
- package/esm2022/lib/services/threejs.service.mjs +1 -1
- package/esm2022/lib/services/user.service.mjs +1 -1
- package/esm2022/lib/services/utils/matterport-framing.util.mjs +63 -0
- package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
- package/esm2022/lib/services/validators.service.mjs +1 -1
- package/esm2022/lib/services/viewer.service.mjs +1 -1
- package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
- package/esm2022/lib/services/zoneChange.service.mjs +1 -1
- package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
- package/esm2022/lib/types.service.mjs +3 -1
- package/esm2022/lib/validators/email.directive.mjs +1 -1
- package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
- package/esm2022/lib/validators/number.directive.mjs +1 -1
- package/esm2022/lib/validators/text.directive.mjs +1 -1
- package/esm2022/public-api.mjs +1 -1
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +1192 -263
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/services/matterport-object3d.service.d.ts +125 -5
- package/lib/services/matterport.service.d.ts +106 -7
- package/lib/services/utils/matterport-framing.util.d.ts +40 -0
- package/lib/types.service.d.ts +42 -1
- package/package.json +1 -1
- package/src/assets/vs-app.json +67 -67
|
@@ -8,7 +8,7 @@ import { NgbDate, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
|
8
8
|
import * as i1$1 from '@angular/router';
|
|
9
9
|
import { RouterModule } from '@angular/router';
|
|
10
10
|
import * as THREE from 'three';
|
|
11
|
-
import { Matrix4, Vector3, Color, Object3D, Euler, PerspectiveCamera, CylinderBufferGeometry, BoxGeometry, MeshBasicMaterial, NormalBlending, BackSide, Mesh, EdgesGeometry, AdditiveBlending, ShaderMaterial, AnimationMixer, VectorKeyframeTrack, InterpolateSmooth, AnimationClip, LoopOnce, Group, PlaneBufferGeometry, CanvasTexture, Texture, LinearFilter } from 'three';
|
|
11
|
+
import { Matrix4, Vector3, Color, Object3D, Euler, PerspectiveCamera, CylinderBufferGeometry, BoxGeometry, MeshBasicMaterial, NormalBlending, BackSide, Mesh, EdgesGeometry, AdditiveBlending, ShaderMaterial, AnimationMixer, VectorKeyframeTrack, InterpolateSmooth, AnimationClip, LoopOnce, Group, PlaneBufferGeometry, CanvasTexture, Texture, LinearFilter, Box3, MathUtils } from 'three';
|
|
12
12
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
13
13
|
import { Storage, Auth, API, graphqlOperation, Cache } from 'aws-amplify';
|
|
14
14
|
import { DateTime } from 'luxon';
|
|
@@ -90,11 +90,11 @@ class CsvExportComponent {
|
|
|
90
90
|
link.remove();
|
|
91
91
|
}
|
|
92
92
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CsvExportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
93
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: CsvExportComponent, selector: "lib-csv-export", inputs: { headers: "headers", rowsInput: "rowsInput", getRows: "getRows", title: "title", buttonLabel: "buttonLabel" }, ngImport: i0, template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\n {{ buttonLabel | translate}}\n</span>\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
93
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: CsvExportComponent, selector: "lib-csv-export", inputs: { headers: "headers", rowsInput: "rowsInput", getRows: "getRows", title: "title", buttonLabel: "buttonLabel" }, ngImport: i0, template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\r\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\r\n {{ buttonLabel | translate}}\r\n</span>\r\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
94
94
|
}
|
|
95
95
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CsvExportComponent, decorators: [{
|
|
96
96
|
type: Component,
|
|
97
|
-
args: [{ selector: 'lib-csv-export', template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\n {{ buttonLabel | translate}}\n</span>\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"] }]
|
|
97
|
+
args: [{ selector: 'lib-csv-export', template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\r\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\r\n {{ buttonLabel | translate}}\r\n</span>\r\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"] }]
|
|
98
98
|
}], propDecorators: { headers: [{
|
|
99
99
|
type: Input
|
|
100
100
|
}], rowsInput: [{
|
|
@@ -386,6 +386,8 @@ var ViewerInteractions;
|
|
|
386
386
|
ViewerInteractions[ViewerInteractions["DEFAULT"] = 0] = "DEFAULT";
|
|
387
387
|
ViewerInteractions[ViewerInteractions["ADDING"] = 1] = "ADDING";
|
|
388
388
|
ViewerInteractions[ViewerInteractions["POSITIONING"] = 2] = "POSITIONING";
|
|
389
|
+
/** A loaded 3D object follows the cursor until the user clicks (validate) or cancels */
|
|
390
|
+
ViewerInteractions[ViewerInteractions["PLACING_OBJECT3D"] = 3] = "PLACING_OBJECT3D";
|
|
389
391
|
})(ViewerInteractions || (ViewerInteractions = {}));
|
|
390
392
|
class ProfileEntity {
|
|
391
393
|
id;
|
|
@@ -633,7 +635,9 @@ class SecurityCamera extends SceneComponent {
|
|
|
633
635
|
this.makeHighlight();
|
|
634
636
|
// attach highighting to the mesh it represents
|
|
635
637
|
// TODO (scene query): this is very brittle but is the only way to attach something to a room of the model
|
|
636
|
-
|
|
638
|
+
if (this.highlight) {
|
|
639
|
+
this.outputs.objectRoot.parent.parent.add(this.highlight);
|
|
640
|
+
}
|
|
637
641
|
// create the animation mixer to rotate the camera
|
|
638
642
|
this.makeAnimation();
|
|
639
643
|
}
|
|
@@ -672,6 +676,12 @@ class SecurityCamera extends SceneComponent {
|
|
|
672
676
|
this.edges = null;
|
|
673
677
|
}
|
|
674
678
|
function edgesToCylinders(edgesGeometry, thickness) {
|
|
679
|
+
// API to be updated in bundle - JSSDK-1222
|
|
680
|
+
const merge = THREE.mergeBufferGeometries ?? THREE.BufferGeometryUtils?.mergeBufferGeometries;
|
|
681
|
+
// not exported by the three.js of recent SDK bundles: the caller falls back to plain lines
|
|
682
|
+
if (!merge)
|
|
683
|
+
return null;
|
|
684
|
+
const Cylinder = THREE.CylinderGeometry ?? THREE.CylinderBufferGeometry;
|
|
675
685
|
const { position } = edgesGeometry.attributes;
|
|
676
686
|
const { array, count } = position;
|
|
677
687
|
const r = thickness / 2;
|
|
@@ -681,15 +691,14 @@ class SecurityCamera extends SceneComponent {
|
|
|
681
691
|
const b = new THREE.Vector3(array[i + 3], array[i + 4], array[i + 5]);
|
|
682
692
|
const vec = new THREE.Vector3().subVectors(b, a);
|
|
683
693
|
const len = vec.length();
|
|
684
|
-
const geom = new
|
|
694
|
+
const geom = new Cylinder(r, r, len, 8);
|
|
685
695
|
geom.translate(0, len / 2, 0);
|
|
686
696
|
geom.rotateX(Math.PI / 2);
|
|
687
697
|
geom.lookAt(vec);
|
|
688
698
|
geom.translate(a.x, a.y, a.z);
|
|
689
699
|
geoms.push(geom);
|
|
690
700
|
}
|
|
691
|
-
|
|
692
|
-
return THREE.mergeBufferGeometries(geoms);
|
|
701
|
+
return merge(geoms);
|
|
693
702
|
}
|
|
694
703
|
const frustumLength = this.inputs.farPlane - this.inputs.nearPlane;
|
|
695
704
|
const boxGeometry = new THREE.BoxGeometry(2, 2, frustumLength);
|
|
@@ -725,6 +734,16 @@ class SecurityCamera extends SceneComponent {
|
|
|
725
734
|
});
|
|
726
735
|
this.box = new THREE.Mesh(boxGeometry, boxMaterial);
|
|
727
736
|
const edgesGeometry = edgesToCylinders(new THREE.EdgesGeometry(boxGeometry), 0.015);
|
|
737
|
+
if (!edgesGeometry) {
|
|
738
|
+
// thick edges need mergeBufferGeometries: draw the frustum with lines
|
|
739
|
+
this.edges = new THREE.LineSegments(new THREE.EdgesGeometry(boxGeometry), new THREE.LineBasicMaterial({
|
|
740
|
+
color: this.inputs.color,
|
|
741
|
+
opacity: 0.8,
|
|
742
|
+
transparent: true,
|
|
743
|
+
depthWrite: false,
|
|
744
|
+
}));
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
728
747
|
this.edges = new THREE.Mesh(edgesGeometry, new THREE.MeshBasicMaterial({
|
|
729
748
|
color: 0xffffff,
|
|
730
749
|
opacity: 0.25,
|
|
@@ -745,8 +764,12 @@ class SecurityCamera extends SceneComponent {
|
|
|
745
764
|
makeHighlight() {
|
|
746
765
|
const THREE = this.context.three;
|
|
747
766
|
// TODO (scene query): this is very brittle and hardcoded but is the only way to get room geometries until we have a better way to query the scene graph
|
|
748
|
-
const floorMesh = this.outputs.objectRoot.parent
|
|
749
|
-
const roomMesh = floorMesh
|
|
767
|
+
const floorMesh = this.outputs.objectRoot.parent?.parent?.getObjectByName('FloorMesh:0');
|
|
768
|
+
const roomMesh = floorMesh?.children[0];
|
|
769
|
+
if (!roomMesh?.geometry) {
|
|
770
|
+
// no room mesh found in the scene graph: the frustum is shown without the highlight
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
750
773
|
updateHighlightUniforms$1(this.projector, this.highlightUniforms);
|
|
751
774
|
this.highlightUniforms.color.value.setHex(this.inputs.color);
|
|
752
775
|
const shader = new THREE.ShaderMaterial({
|
|
@@ -798,30 +821,30 @@ class SecurityCamera extends SceneComponent {
|
|
|
798
821
|
}
|
|
799
822
|
}
|
|
800
823
|
function vertexShader$1() {
|
|
801
|
-
return `
|
|
802
|
-
uniform mat4 lightMatrix;
|
|
803
|
-
|
|
804
|
-
varying vec4 vLightCoords;
|
|
805
|
-
|
|
806
|
-
void main() {
|
|
807
|
-
// create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
|
|
808
|
-
vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
|
|
809
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
810
|
-
}
|
|
824
|
+
return `
|
|
825
|
+
uniform mat4 lightMatrix;
|
|
826
|
+
|
|
827
|
+
varying vec4 vLightCoords;
|
|
828
|
+
|
|
829
|
+
void main() {
|
|
830
|
+
// create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
|
|
831
|
+
vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
|
|
832
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
833
|
+
}
|
|
811
834
|
`;
|
|
812
835
|
}
|
|
813
836
|
function fragmentShader$1() {
|
|
814
|
-
return `
|
|
815
|
-
uniform vec3 color;
|
|
816
|
-
|
|
817
|
-
varying vec4 vLightCoords;
|
|
818
|
-
|
|
819
|
-
void main() {
|
|
820
|
-
vec2 lightUV = vLightCoords.xy / vLightCoords.w;
|
|
821
|
-
float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
|
|
822
|
-
|
|
823
|
-
gl_FragColor = vec4(color, min(inView, 0.2));
|
|
824
|
-
}
|
|
837
|
+
return `
|
|
838
|
+
uniform vec3 color;
|
|
839
|
+
|
|
840
|
+
varying vec4 vLightCoords;
|
|
841
|
+
|
|
842
|
+
void main() {
|
|
843
|
+
vec2 lightUV = vLightCoords.xy / vLightCoords.w;
|
|
844
|
+
float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
|
|
845
|
+
|
|
846
|
+
gl_FragColor = vec4(color, min(inView, 0.2));
|
|
847
|
+
}
|
|
825
848
|
`;
|
|
826
849
|
}
|
|
827
850
|
function updateHighlightUniforms$1(projector, uniforms) {
|
|
@@ -1014,30 +1037,30 @@ class ViewFrustumMesh extends SceneComponent {
|
|
|
1014
1037
|
}
|
|
1015
1038
|
}
|
|
1016
1039
|
function vertexShader() {
|
|
1017
|
-
return `
|
|
1018
|
-
uniform mat4 lightMatrix;
|
|
1019
|
-
|
|
1020
|
-
varying vec4 vLightCoords;
|
|
1021
|
-
|
|
1022
|
-
void main() {
|
|
1023
|
-
// create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
|
|
1024
|
-
vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
|
|
1025
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
1026
|
-
}
|
|
1040
|
+
return `
|
|
1041
|
+
uniform mat4 lightMatrix;
|
|
1042
|
+
|
|
1043
|
+
varying vec4 vLightCoords;
|
|
1044
|
+
|
|
1045
|
+
void main() {
|
|
1046
|
+
// create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
|
|
1047
|
+
vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
|
|
1048
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
1049
|
+
}
|
|
1027
1050
|
`;
|
|
1028
1051
|
}
|
|
1029
1052
|
function fragmentShader() {
|
|
1030
|
-
return `
|
|
1031
|
-
uniform vec3 color;
|
|
1032
|
-
|
|
1033
|
-
varying vec4 vLightCoords;
|
|
1034
|
-
|
|
1035
|
-
void main() {
|
|
1036
|
-
vec2 lightUV = vLightCoords.xy / vLightCoords.w;
|
|
1037
|
-
float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
|
|
1038
|
-
|
|
1039
|
-
gl_FragColor = vec4(color, min(inView, 0.2));
|
|
1040
|
-
}
|
|
1053
|
+
return `
|
|
1054
|
+
uniform vec3 color;
|
|
1055
|
+
|
|
1056
|
+
varying vec4 vLightCoords;
|
|
1057
|
+
|
|
1058
|
+
void main() {
|
|
1059
|
+
vec2 lightUV = vLightCoords.xy / vLightCoords.w;
|
|
1060
|
+
float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
|
|
1061
|
+
|
|
1062
|
+
gl_FragColor = vec4(color, min(inView, 0.2));
|
|
1063
|
+
}
|
|
1041
1064
|
`;
|
|
1042
1065
|
}
|
|
1043
1066
|
function updateHighlightUniforms(projector, uniforms) {
|
|
@@ -2200,32 +2223,32 @@ class PoiService {
|
|
|
2200
2223
|
if (!sweeps || sweeps.length === 0) {
|
|
2201
2224
|
return [];
|
|
2202
2225
|
}
|
|
2203
|
-
const queryStatement = `
|
|
2204
|
-
query ListPOISLight($filter: ModelPOIFilterInput, $limit: Int, $nextToken: String) {
|
|
2205
|
-
listPOIS(filter: $filter, limit: $limit, nextToken: $nextToken) {
|
|
2206
|
-
items {
|
|
2207
|
-
id
|
|
2208
|
-
coordinate
|
|
2209
|
-
specific
|
|
2210
|
-
private
|
|
2211
|
-
global
|
|
2212
|
-
type
|
|
2213
|
-
elementID
|
|
2214
|
-
level
|
|
2215
|
-
role
|
|
2216
|
-
category
|
|
2217
|
-
domainID
|
|
2218
|
-
metadata
|
|
2219
|
-
tagIcon
|
|
2220
|
-
tagDescription
|
|
2221
|
-
tagImageID
|
|
2222
|
-
matterportSweepID
|
|
2223
|
-
createdAt
|
|
2224
|
-
updatedAt
|
|
2225
|
-
}
|
|
2226
|
-
nextToken
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2226
|
+
const queryStatement = `
|
|
2227
|
+
query ListPOISLight($filter: ModelPOIFilterInput, $limit: Int, $nextToken: String) {
|
|
2228
|
+
listPOIS(filter: $filter, limit: $limit, nextToken: $nextToken) {
|
|
2229
|
+
items {
|
|
2230
|
+
id
|
|
2231
|
+
coordinate
|
|
2232
|
+
specific
|
|
2233
|
+
private
|
|
2234
|
+
global
|
|
2235
|
+
type
|
|
2236
|
+
elementID
|
|
2237
|
+
level
|
|
2238
|
+
role
|
|
2239
|
+
category
|
|
2240
|
+
domainID
|
|
2241
|
+
metadata
|
|
2242
|
+
tagIcon
|
|
2243
|
+
tagDescription
|
|
2244
|
+
tagImageID
|
|
2245
|
+
matterportSweepID
|
|
2246
|
+
createdAt
|
|
2247
|
+
updatedAt
|
|
2248
|
+
}
|
|
2249
|
+
nextToken
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2229
2252
|
`;
|
|
2230
2253
|
const CHUNK_SIZE = 50; // safe margin for an "or" filter expression's practical size
|
|
2231
2254
|
const chunks = [];
|
|
@@ -2293,6 +2316,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
2293
2316
|
args: ['currentAwsKinesisAnalyticsService']
|
|
2294
2317
|
}] }, { type: BaseUserService }, { type: ZoneChangeService }] });
|
|
2295
2318
|
|
|
2319
|
+
// matterport-framing.util.ts
|
|
2320
|
+
/**
|
|
2321
|
+
* Camera rotation that shows `target` from `camera`, at a given horizontal place of the viewer.
|
|
2322
|
+
* Matterport yaw is 0 when looking toward -Z and grows when turning left; pitch is positive upward.
|
|
2323
|
+
* @param horizontalTanHalfFov tan(horizontal FOV / 2), i.e. 1 / projection[0] of the camera pose
|
|
2324
|
+
* @param screenX where the target must appear, from -1 (left edge) to 1 (right edge); 0 = center.
|
|
2325
|
+
* Used when part of the viewer is covered (side panel), to frame the visible area.
|
|
2326
|
+
*/
|
|
2327
|
+
function computeFramingRotation(camera, target, horizontalTanHalfFov = 1, screenX = 0) {
|
|
2328
|
+
const dx = target.x - camera.x;
|
|
2329
|
+
const dy = target.y - camera.y;
|
|
2330
|
+
const dz = target.z - camera.z;
|
|
2331
|
+
const yawToTarget = Math.atan2(-dx, -dz);
|
|
2332
|
+
// the target is seen at screenX when the camera looks that much to its side
|
|
2333
|
+
const offset = Math.atan(screenX * horizontalTanHalfFov);
|
|
2334
|
+
return {
|
|
2335
|
+
x: toDegrees(Math.atan2(dy, Math.hypot(dx, dz))),
|
|
2336
|
+
y: toDegrees(yawToTarget + offset),
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* Horizontal place (-1..1) of the center of the visible part of the viewer.
|
|
2341
|
+
* @param viewer client rect of the viewer iframe
|
|
2342
|
+
* @param visibleLeft / visibleRight client X bounds of the part not covered by other panels
|
|
2343
|
+
*/
|
|
2344
|
+
function visibleCenterScreenX(viewer, visibleLeft, visibleRight) {
|
|
2345
|
+
if (!viewer.width)
|
|
2346
|
+
return 0;
|
|
2347
|
+
const left = Math.max(viewer.left, visibleLeft);
|
|
2348
|
+
const right = Math.min(viewer.left + viewer.width, visibleRight);
|
|
2349
|
+
if (right <= left)
|
|
2350
|
+
return 0;
|
|
2351
|
+
const center = (left + right) / 2;
|
|
2352
|
+
return ((center - viewer.left) / viewer.width) * 2 - 1;
|
|
2353
|
+
}
|
|
2354
|
+
function toDegrees(rad) {
|
|
2355
|
+
return (rad * 180) / Math.PI;
|
|
2356
|
+
}
|
|
2357
|
+
/** Height of the floor under a sweep: its puck, or 1.5 m under the camera when the SDK gives none */
|
|
2358
|
+
function sweepFloorY(sweep) {
|
|
2359
|
+
return sweep.puckPosition?.y ?? sweep.floorPosition?.y ?? sweep.position.y - 1.5;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Sweep closest to an object placed at `position`, among the sweeps of the same storey: the camera
|
|
2363
|
+
* of the storey below is often nearer in 3D than a sweep of the same room. A sweep is on the same
|
|
2364
|
+
* storey when the object stands between 0.5 m under its floor and 3 m above it; without any such
|
|
2365
|
+
* sweep, all of them are considered. Distances are measured horizontally.
|
|
2366
|
+
*/
|
|
2367
|
+
function pickNearestSweepOnSameStorey(sweeps, position) {
|
|
2368
|
+
const sameStorey = sweeps.filter((s) => position.y >= s.floorY - 0.5 && position.y < s.floorY + 3);
|
|
2369
|
+
const candidates = sameStorey.length > 0 ? sameStorey : sweeps;
|
|
2370
|
+
let best = null;
|
|
2371
|
+
let bestDist = Infinity;
|
|
2372
|
+
for (const sweep of candidates) {
|
|
2373
|
+
const dist = Math.hypot(sweep.position.x - position.x, sweep.position.z - position.z);
|
|
2374
|
+
if (dist < bestDist) {
|
|
2375
|
+
bestDist = dist;
|
|
2376
|
+
best = sweep.sid;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
return best;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2296
2382
|
/* eslint-disable unicorn/filename-case */
|
|
2297
2383
|
class MattertagData {
|
|
2298
2384
|
object;
|
|
@@ -3030,11 +3116,11 @@ class MatterportPointerService {
|
|
|
3030
3116
|
}
|
|
3031
3117
|
updateCursorDisplay(poseMatterport) {
|
|
3032
3118
|
if (this.textDisplayCursorPositionPanel && poseMatterport) {
|
|
3033
|
-
this.textDisplayCursorPositionPanel.innerHTML = `position:
|
|
3034
|
-
${pointToString(poseMatterport.position)}\n
|
|
3035
|
-
normal:
|
|
3036
|
-
${pointToString(poseMatterport.normal)}\n
|
|
3037
|
-
floorId:
|
|
3119
|
+
this.textDisplayCursorPositionPanel.innerHTML = `position:
|
|
3120
|
+
${pointToString(poseMatterport.position)}\n
|
|
3121
|
+
normal:
|
|
3122
|
+
${pointToString(poseMatterport.normal)}\n
|
|
3123
|
+
floorId:
|
|
3038
3124
|
${poseMatterport.floorId}`;
|
|
3039
3125
|
if (this.getCursorPositionButton) {
|
|
3040
3126
|
this.getCursorPositionButton.style.display = 'none';
|
|
@@ -3463,6 +3549,28 @@ class MatterportObject3DService {
|
|
|
3463
3549
|
azimuthalCrown;
|
|
3464
3550
|
threeJSScene;
|
|
3465
3551
|
noLightForObjects = true;
|
|
3552
|
+
/** Max time (ms) to wait for a model to be loaded by the SDK */
|
|
3553
|
+
loadTimeoutMs = 15000;
|
|
3554
|
+
/** Max distance (m) an object can travel from where a translate drag started */
|
|
3555
|
+
maxDragDistance = 2;
|
|
3556
|
+
/** Max vertical travel (m, up and down) during a translate drag, so an object cannot jump to another floor */
|
|
3557
|
+
maxDragHeight = 1.5;
|
|
3558
|
+
/** Emits on every gizmo change and on setObject3DTransform */
|
|
3559
|
+
object3DTransformChanged$ = new Subject();
|
|
3560
|
+
/** Security camera animators by object uuid (`securityCameraAnimator` stays the last loaded one) */
|
|
3561
|
+
securityCameraAnimators = new Map();
|
|
3562
|
+
viewFrustumVisible = new Map();
|
|
3563
|
+
lastSecurityCameraUuid = null;
|
|
3564
|
+
/** Loader component of each model (outputs.objectRoot is set once loaded) */
|
|
3565
|
+
loaderComponents = new Map();
|
|
3566
|
+
gizmos = new Map();
|
|
3567
|
+
activeGizmoUuid = null;
|
|
3568
|
+
gizmoSnap = null;
|
|
3569
|
+
/** Sweeps disabled by setNavigationLocked(true), re-enabled on unlock */
|
|
3570
|
+
sweepsDisabledByLock = [];
|
|
3571
|
+
/** Sweeps re-enabled by the last unlock (the SDK may still report them disabled for a moment) */
|
|
3572
|
+
sweepsEnabledByUnlock = [];
|
|
3573
|
+
tmpBox = new Box3();
|
|
3466
3574
|
constructor() { }
|
|
3467
3575
|
async init3DObjectViewer(sdk) {
|
|
3468
3576
|
const [sceneObject] = await sdk.Scene.createObjects(1);
|
|
@@ -3473,112 +3581,184 @@ class MatterportObject3DService {
|
|
|
3473
3581
|
this.objectControl = nodeControl.addComponent('mp.transformControls');
|
|
3474
3582
|
nodeControl.start();
|
|
3475
3583
|
}
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
component = modelNode.addComponent('mp.fbxLoader', initial);
|
|
3500
|
-
break;
|
|
3501
|
-
case '.gltf':
|
|
3502
|
-
case 'gltf':
|
|
3503
|
-
case '.glb':
|
|
3504
|
-
case 'glb':
|
|
3505
|
-
component = modelNode.addComponent('mp.gltfLoader', initial);
|
|
3506
|
-
break;
|
|
3507
|
-
case '.dae':
|
|
3508
|
-
case 'dae':
|
|
3509
|
-
component = modelNode.addComponent('mp.daeLoader', initial);
|
|
3510
|
-
break;
|
|
3511
|
-
default:
|
|
3512
|
-
console.log('Format not supported');
|
|
3513
|
-
break;
|
|
3584
|
+
/** SDK loader component name for a model format, null when the format is not supported */
|
|
3585
|
+
getLoaderComponentName(format) {
|
|
3586
|
+
switch ((format || '').toLowerCase()) {
|
|
3587
|
+
case '.obj':
|
|
3588
|
+
case 'obj': return 'mp.objLoader';
|
|
3589
|
+
case '.fbx':
|
|
3590
|
+
case 'fbx': return 'mp.fbxLoader';
|
|
3591
|
+
case '.gltf':
|
|
3592
|
+
case 'gltf':
|
|
3593
|
+
case '.glb':
|
|
3594
|
+
case 'glb': return 'mp.gltfLoader';
|
|
3595
|
+
case '.dae':
|
|
3596
|
+
case 'dae': return 'mp.daeLoader';
|
|
3597
|
+
default: return null;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
/** Resolves once the loader component has produced its objectRoot, rejects after `timeoutMs` */
|
|
3601
|
+
waitForModelLoaded(component, timeoutMs) {
|
|
3602
|
+
return new Promise((resolve, reject) => {
|
|
3603
|
+
const isLoaded = () => !!component?.outputs?.objectRoot;
|
|
3604
|
+
if (isLoaded()) {
|
|
3605
|
+
resolve();
|
|
3606
|
+
return;
|
|
3514
3607
|
}
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
});
|
|
3521
|
-
this.noLightForObjects = false;
|
|
3522
|
-
}
|
|
3523
|
-
modelNode.obj3D.position.set(obj.position.x, obj.position.y, obj.position.z);
|
|
3524
|
-
modelNode.obj3D.rotation.set(obj.rotation.x, obj.rotation.y, obj.rotation.z);
|
|
3525
|
-
modelNode.obj3D.scale.set(obj.scale.x, obj.scale.y, obj.scale.z);
|
|
3526
|
-
if (isAzimuthalCrown) {
|
|
3527
|
-
this.azimuthalCrown = modelNode;
|
|
3528
|
-
}
|
|
3529
|
-
if (mode && !isNestThermostat) {
|
|
3530
|
-
this.attachGizmoControlTo3DObject(sdk, modelNode, sceneObject, mode, true, true).catch((e) => console.log(e));
|
|
3531
|
-
}
|
|
3532
|
-
if (this.lastObject3D && typeof this.lastObject3D.id === 'string') {
|
|
3533
|
-
modelNode.obj3D.uuid = this.lastObject3D.id;
|
|
3534
|
-
}
|
|
3535
|
-
else if (obj.id) {
|
|
3536
|
-
modelNode.obj3D.uuid = obj.id;
|
|
3537
|
-
}
|
|
3538
|
-
this.lastObject3D = modelNode.obj3D;
|
|
3539
|
-
this.dictionnaryObjects3D.set(modelNode.obj3D.uuid, modelNode);
|
|
3540
|
-
this.dictionnarySceneObjects3D.set(modelNode.obj3D.uuid, sceneObject);
|
|
3541
|
-
if (isAnimatedSecurityCamera) {
|
|
3542
|
-
const animator = modelNode.addComponent('mp.securityCamera', {
|
|
3543
|
-
"nearPlane": 0.1, "farPlane": 10, "horizontalFOV": 52, "aspect": 1.7777777777777777,
|
|
3544
|
-
"localPosition": { "x": 0.3, "y": 0.18, "z": 0 },
|
|
3545
|
-
"localRotation": { "x": -15, "y": -90, "z": 0 },
|
|
3546
|
-
"color": 65280, "panPeriod": 5, "panAngle": -45
|
|
3547
|
-
});
|
|
3548
|
-
this.securityCameraAnimator = animator;
|
|
3549
|
-
if (!obj.viewFrustum) {
|
|
3550
|
-
setTimeout(() => { animator.toggleViewFrustum(); }, 1000);
|
|
3608
|
+
const poll = setInterval(() => {
|
|
3609
|
+
if (isLoaded()) {
|
|
3610
|
+
clearInterval(poll);
|
|
3611
|
+
clearTimeout(timer);
|
|
3612
|
+
resolve();
|
|
3551
3613
|
}
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
plane.outputs = { objectRoot: new Object3D(), collider: new Object3D() };
|
|
3558
|
-
const inputTexture = cv.outputs.texture;
|
|
3559
|
-
plane.setRootScene(this.threeJSScene);
|
|
3560
|
-
plane.onInit(modelNode, inputTexture);
|
|
3561
|
-
const sc = new NestThermostat();
|
|
3562
|
-
sc.setComponent(component, plane, cv);
|
|
3563
|
-
sc.setRootScene(this.threeJSScene);
|
|
3564
|
-
sc.onInit(modelNode, plane, inputTexture);
|
|
3565
|
-
cv.setCanvasNestThermostatPainter(sc);
|
|
3566
|
-
setTimeout(() => { sc.inputs.loadingState = "Loaded"; sc.onInputsUpdated(); }, 5000);
|
|
3567
|
-
}
|
|
3568
|
-
if (isSmarterplanPromotionalVideo) {
|
|
3569
|
-
const sc = new TvPlayer();
|
|
3570
|
-
sc.setComponent(component);
|
|
3571
|
-
sc.onInit(modelNode);
|
|
3572
|
-
setTimeout(() => { sc.inputs.loadingState = "Loaded"; sc.onInputsUpdated(); }, 5000);
|
|
3573
|
-
}
|
|
3574
|
-
sceneObject.start();
|
|
3575
|
-
resolve(this.lastObject3D);
|
|
3614
|
+
}, 50);
|
|
3615
|
+
const timer = setTimeout(() => {
|
|
3616
|
+
clearInterval(poll);
|
|
3617
|
+
reject(new Error(`3D model not loaded after ${timeoutMs} ms`));
|
|
3618
|
+
}, timeoutMs);
|
|
3576
3619
|
});
|
|
3577
3620
|
}
|
|
3621
|
+
/**
|
|
3622
|
+
* Adds a 3D model to the scene.
|
|
3623
|
+
* The promise resolves with the three.js object once the model is actually loaded, and rejects
|
|
3624
|
+
* (after cleaning the scene) when the format is unknown or the load times out.
|
|
3625
|
+
* The position / rotation / scale of `obj` are applied to the model origin, as stored in DB.
|
|
3626
|
+
*/
|
|
3627
|
+
async add3DObject(sdk, obj, mode) {
|
|
3628
|
+
const loaderName = this.getLoaderComponentName(obj.format);
|
|
3629
|
+
if (!loaderName) {
|
|
3630
|
+
throw new Error(`3D object format not supported: ${obj.format}`);
|
|
3631
|
+
}
|
|
3632
|
+
const [sceneObject] = await sdk.Scene.createObjects(1);
|
|
3633
|
+
const isAnimatedSecurityCamera = (!obj.url && obj.object === 'security_camera');
|
|
3634
|
+
const isNestThermostat = (!obj.url && obj.object === 'nest_thermostat');
|
|
3635
|
+
const isSmarterplanPromotionalVideo = (!obj.url && obj.object === 'video');
|
|
3636
|
+
const isAzimuthalCrown = (!obj.url && obj.object === 'azimuth');
|
|
3637
|
+
const modelNode = sceneObject.addNode();
|
|
3638
|
+
const initial = {
|
|
3639
|
+
url: obj.url || `/assets/3Dobjects/objects/${obj.object}${obj.format.indexOf('.') === -1 ? '.' + obj.format : obj.format}`,
|
|
3640
|
+
localRotation: { 'x': 0, 'y': 0, 'z': 0 },
|
|
3641
|
+
localPosition: { 'x': 0, 'y': 0, 'z': 0 },
|
|
3642
|
+
visible: true,
|
|
3643
|
+
colliderEnabled: true
|
|
3644
|
+
};
|
|
3645
|
+
const component = modelNode.addComponent(loaderName, initial);
|
|
3646
|
+
if (this.noLightForObjects) {
|
|
3647
|
+
const lightsNode = sceneObject.addNode();
|
|
3648
|
+
lightsNode.addComponent('mp.ambientLight', {
|
|
3649
|
+
intensity: 1,
|
|
3650
|
+
color: { r: 1.0, g: 1.0, b: 1.0 },
|
|
3651
|
+
});
|
|
3652
|
+
this.noLightForObjects = false;
|
|
3653
|
+
}
|
|
3654
|
+
modelNode.obj3D.position.set(obj.position.x, obj.position.y, obj.position.z);
|
|
3655
|
+
modelNode.obj3D.rotation.set(obj.rotation.x, obj.rotation.y, obj.rotation.z);
|
|
3656
|
+
modelNode.obj3D.scale.set(obj.scale.x, obj.scale.y, obj.scale.z);
|
|
3657
|
+
// The DB id replaces the three.js uuid. The id pre-set through setLastObject3D is only a fallback
|
|
3658
|
+
// for callers without id: taking it first gave the same id to every object loaded meanwhile
|
|
3659
|
+
// (several cameras or cubes loading while an add is starting), which then overwrote each other.
|
|
3660
|
+
if (obj.id) {
|
|
3661
|
+
modelNode.obj3D.uuid = obj.id;
|
|
3662
|
+
}
|
|
3663
|
+
else if (this.lastObject3D && typeof this.lastObject3D.id === 'string') {
|
|
3664
|
+
modelNode.obj3D.uuid = this.lastObject3D.id;
|
|
3665
|
+
}
|
|
3666
|
+
const uuid = modelNode.obj3D.uuid;
|
|
3667
|
+
this.lastObject3D = modelNode.obj3D;
|
|
3668
|
+
this.dictionnaryObjects3D.set(uuid, modelNode);
|
|
3669
|
+
this.dictionnarySceneObjects3D.set(uuid, sceneObject);
|
|
3670
|
+
this.loaderComponents.set(uuid, component);
|
|
3671
|
+
if (isAzimuthalCrown) {
|
|
3672
|
+
this.azimuthalCrown = modelNode;
|
|
3673
|
+
}
|
|
3674
|
+
if (mode && !isNestThermostat) {
|
|
3675
|
+
this.attachGizmo(uuid, mode).catch(() => { });
|
|
3676
|
+
}
|
|
3677
|
+
let animator = null;
|
|
3678
|
+
if (isAnimatedSecurityCamera) {
|
|
3679
|
+
animator = modelNode.addComponent('mp.securityCamera', {
|
|
3680
|
+
'nearPlane': 0.1, 'farPlane': 10, 'horizontalFOV': 52, 'aspect': 1.7777777777777777,
|
|
3681
|
+
'localPosition': { 'x': 0.3, 'y': 0.18, 'z': 0 },
|
|
3682
|
+
'localRotation': { 'x': -15, 'y': -90, 'z': 0 },
|
|
3683
|
+
'color': 65280, 'panPeriod': 5, 'panAngle': -45
|
|
3684
|
+
});
|
|
3685
|
+
this.securityCameraAnimator = animator;
|
|
3686
|
+
this.securityCameraAnimators.set(uuid, animator);
|
|
3687
|
+
this.viewFrustumVisible.set(uuid, true); // the component shows its frustum by default
|
|
3688
|
+
this.lastSecurityCameraUuid = uuid;
|
|
3689
|
+
}
|
|
3690
|
+
if (isNestThermostat) {
|
|
3691
|
+
const cv = new CanvasRenderer();
|
|
3692
|
+
cv.onInit();
|
|
3693
|
+
const plane = new PlaneRenderer();
|
|
3694
|
+
plane.outputs = { objectRoot: new Object3D(), collider: new Object3D() };
|
|
3695
|
+
const inputTexture = cv.outputs.texture;
|
|
3696
|
+
plane.setRootScene(this.threeJSScene);
|
|
3697
|
+
plane.onInit(modelNode, inputTexture);
|
|
3698
|
+
const sc = new NestThermostat();
|
|
3699
|
+
sc.setComponent(component, plane, cv);
|
|
3700
|
+
sc.setRootScene(this.threeJSScene);
|
|
3701
|
+
sc.onInit(modelNode, plane, inputTexture);
|
|
3702
|
+
cv.setCanvasNestThermostatPainter(sc);
|
|
3703
|
+
setTimeout(() => { sc.inputs.loadingState = 'Loaded'; sc.onInputsUpdated(); }, 5000);
|
|
3704
|
+
}
|
|
3705
|
+
if (isSmarterplanPromotionalVideo) {
|
|
3706
|
+
const sc = new TvPlayer();
|
|
3707
|
+
sc.setComponent(component);
|
|
3708
|
+
sc.onInit(modelNode);
|
|
3709
|
+
setTimeout(() => { sc.inputs.loadingState = 'Loaded'; sc.onInputsUpdated(); }, 5000);
|
|
3710
|
+
}
|
|
3711
|
+
sceneObject.start();
|
|
3712
|
+
try {
|
|
3713
|
+
await this.waitForModelLoaded(component, this.loadTimeoutMs);
|
|
3714
|
+
}
|
|
3715
|
+
catch (e) {
|
|
3716
|
+
await this.deleteObject3D(uuid);
|
|
3717
|
+
throw e;
|
|
3718
|
+
}
|
|
3719
|
+
if (animator && !obj.viewFrustum) {
|
|
3720
|
+
// The camera component starts with the model node, so it is ready now that the model is loaded
|
|
3721
|
+
this.applyViewFrustum(uuid, false);
|
|
3722
|
+
}
|
|
3723
|
+
return modelNode.obj3D;
|
|
3724
|
+
}
|
|
3725
|
+
/**
|
|
3726
|
+
* Removes a 3D object for good: detaches its gizmo, stops its scene object and forgets it.
|
|
3727
|
+
*/
|
|
3578
3728
|
async deleteObject3D(uuid) {
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3729
|
+
this.detachGizmo(uuid);
|
|
3730
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3731
|
+
const sceneObject = this.dictionnarySceneObjects3D.get(uuid);
|
|
3732
|
+
if (node) {
|
|
3733
|
+
try {
|
|
3734
|
+
node.obj3D.visible = false;
|
|
3735
|
+
}
|
|
3736
|
+
catch { /* already destroyed */ }
|
|
3737
|
+
}
|
|
3738
|
+
if (sceneObject && typeof sceneObject.stop === 'function') {
|
|
3739
|
+
try {
|
|
3740
|
+
sceneObject.stop();
|
|
3741
|
+
}
|
|
3742
|
+
catch { /* already stopped */ }
|
|
3743
|
+
}
|
|
3744
|
+
this.dictionnaryObjects3D.delete(uuid);
|
|
3745
|
+
this.dictionnarySceneObjects3D.delete(uuid);
|
|
3746
|
+
this.loaderComponents.delete(uuid);
|
|
3747
|
+
if (this.lastObject3D && (this.lastObject3D.uuid === uuid || this.lastObject3D.id === uuid)) {
|
|
3748
|
+
this.lastObject3D = null;
|
|
3749
|
+
}
|
|
3750
|
+
if (node && this.azimuthalCrown === node) {
|
|
3751
|
+
this.azimuthalCrown = null;
|
|
3752
|
+
}
|
|
3753
|
+
const animator = this.securityCameraAnimators.get(uuid);
|
|
3754
|
+
if (animator && this.securityCameraAnimator === animator) {
|
|
3755
|
+
this.securityCameraAnimator = null;
|
|
3756
|
+
}
|
|
3757
|
+
this.securityCameraAnimators.delete(uuid);
|
|
3758
|
+
this.viewFrustumVisible.delete(uuid);
|
|
3759
|
+
if (this.lastSecurityCameraUuid === uuid) {
|
|
3760
|
+
this.lastSecurityCameraUuid = null;
|
|
3761
|
+
}
|
|
3582
3762
|
}
|
|
3583
3763
|
getObject3DModelNodeFromDictionnary(uuid) {
|
|
3584
3764
|
let obj = this.dictionnaryObjects3D.get(uuid);
|
|
@@ -3598,10 +3778,32 @@ class MatterportObject3DService {
|
|
|
3598
3778
|
let obj = this.dictionnaryObjects3D.get(objectId);
|
|
3599
3779
|
return obj ? obj.obj3D.visible : false;
|
|
3600
3780
|
}
|
|
3781
|
+
/** Shows / hides a model (used by the placement preview) */
|
|
3782
|
+
setObject3DVisible(uuid, visible) {
|
|
3783
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3784
|
+
if (node)
|
|
3785
|
+
node.obj3D.visible = visible;
|
|
3786
|
+
}
|
|
3787
|
+
/** Enables / disables the pointer collider of a model (disabled while it follows the cursor) */
|
|
3788
|
+
setObject3DColliderEnabled(uuid, enabled) {
|
|
3789
|
+
const component = this.loaderComponents.get(uuid);
|
|
3790
|
+
if (component?.inputs) {
|
|
3791
|
+
component.inputs.colliderEnabled = enabled;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
/**
|
|
3795
|
+
* Moves the camera next to a 3D object by creating a temporary tag on the object centre.
|
|
3796
|
+
* @returns false when the object is unknown or no tag could be reached
|
|
3797
|
+
*/
|
|
3601
3798
|
async pointCameraTo3DObject(sdk, objectId, createTagCallback, goToTagCallback, deleteLastTagCallback, getTagFromElementId) {
|
|
3602
|
-
|
|
3799
|
+
const obj = this.dictionnaryObjects3D.get(objectId);
|
|
3800
|
+
if (!obj)
|
|
3801
|
+
return false;
|
|
3802
|
+
// Tag on the bounding box centre (the model origin is a corner for most models)
|
|
3803
|
+
const box = this.getObject3DBoundingBox(objectId);
|
|
3804
|
+
const anchor = box ? box.center : this.toPoint(obj.obj3D.position);
|
|
3603
3805
|
const poiObject = {
|
|
3604
|
-
coordinate: JSON.stringify(
|
|
3806
|
+
coordinate: JSON.stringify(anchor),
|
|
3605
3807
|
type: PoiType.OBJECT3D,
|
|
3606
3808
|
elementID: objectId,
|
|
3607
3809
|
};
|
|
@@ -3610,9 +3812,16 @@ class MatterportObject3DService {
|
|
|
3610
3812
|
await createTagCallback(PoiType.OBJECT3D, objectDb, poiObject);
|
|
3611
3813
|
}
|
|
3612
3814
|
catch (err) { }
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3815
|
+
try {
|
|
3816
|
+
const result = getTagFromElementId(objectId);
|
|
3817
|
+
if (!result?.tag)
|
|
3818
|
+
return false;
|
|
3819
|
+
await goToTagCallback(result.tag);
|
|
3820
|
+
return true;
|
|
3821
|
+
}
|
|
3822
|
+
finally {
|
|
3823
|
+
deleteLastTagCallback();
|
|
3824
|
+
}
|
|
3616
3825
|
}
|
|
3617
3826
|
getSceneNodeFromObject3DId(uuid) {
|
|
3618
3827
|
return this.dictionnarySceneObjects3D.get(uuid);
|
|
@@ -3622,49 +3831,449 @@ class MatterportObject3DService {
|
|
|
3622
3831
|
this.azimuthalCrown.obj3D.position.set(poseCamera.position.x, poseCamera.position.y, poseCamera.position.z);
|
|
3623
3832
|
}
|
|
3624
3833
|
}
|
|
3834
|
+
//
|
|
3835
|
+
// ---------- Geometry helpers ----------
|
|
3836
|
+
//
|
|
3837
|
+
toPoint(v) {
|
|
3838
|
+
return { x: v.x, y: v.y, z: v.z };
|
|
3839
|
+
}
|
|
3840
|
+
/**
|
|
3841
|
+
* Geometry of the model itself (the loader output): the node also holds the helpers added by other
|
|
3842
|
+
* components, like the view frustum of a security camera, which must not count in its size.
|
|
3843
|
+
*/
|
|
3844
|
+
modelRoot(uuid, node) {
|
|
3845
|
+
return this.loaderComponents.get(uuid)?.outputs?.objectRoot ?? node.obj3D;
|
|
3846
|
+
}
|
|
3847
|
+
/** World-space bounding box of the model, null when unknown or when nothing is loaded yet */
|
|
3848
|
+
getObject3DBoundingBox(uuid) {
|
|
3849
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3850
|
+
if (!node)
|
|
3851
|
+
return null;
|
|
3852
|
+
node.obj3D.updateWorldMatrix(true, true);
|
|
3853
|
+
const box = this.tmpBox.setFromObject(this.modelRoot(uuid, node));
|
|
3854
|
+
if (box.isEmpty())
|
|
3855
|
+
return null;
|
|
3856
|
+
const size = box.getSize(new Vector3());
|
|
3857
|
+
const center = box.getCenter(new Vector3());
|
|
3858
|
+
return {
|
|
3859
|
+
min: this.toPoint(box.min),
|
|
3860
|
+
max: this.toPoint(box.max),
|
|
3861
|
+
size: this.toPoint(size),
|
|
3862
|
+
center: this.toPoint(center),
|
|
3863
|
+
};
|
|
3864
|
+
}
|
|
3865
|
+
/**
|
|
3866
|
+
* Real dimensions of the model in meters (width x, height y, depth z), scale included but not
|
|
3867
|
+
* its rotation: the world bounding box of a rotated object is larger than the object itself.
|
|
3868
|
+
*/
|
|
3869
|
+
getObject3DDimensions(uuid) {
|
|
3870
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3871
|
+
if (!node)
|
|
3872
|
+
return null;
|
|
3873
|
+
const rotation = node.obj3D.rotation.clone();
|
|
3874
|
+
node.obj3D.rotation.set(0, 0, 0);
|
|
3875
|
+
try {
|
|
3876
|
+
node.obj3D.updateWorldMatrix(true, true);
|
|
3877
|
+
const box = this.tmpBox.setFromObject(this.modelRoot(uuid, node));
|
|
3878
|
+
return box.isEmpty() ? null : this.toPoint(box.getSize(new Vector3()));
|
|
3879
|
+
}
|
|
3880
|
+
finally {
|
|
3881
|
+
node.obj3D.rotation.copy(rotation);
|
|
3882
|
+
node.obj3D.updateWorldMatrix(true, true);
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
/**
|
|
3886
|
+
* Uniform scale so that the largest bounding box dimension equals `targetSizeMeters`.
|
|
3887
|
+
* The node position is untouched, so call placeObject3DOnSurface afterwards.
|
|
3888
|
+
* @returns the new scale, null when the object is unknown / not loaded / `targetSizeMeters` invalid
|
|
3889
|
+
*/
|
|
3890
|
+
normalizeObject3DSize(uuid, targetSizeMeters) {
|
|
3891
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3892
|
+
const box = this.getObject3DBoundingBox(uuid);
|
|
3893
|
+
if (!node || !box || !(targetSizeMeters > 0))
|
|
3894
|
+
return null;
|
|
3895
|
+
const largest = Math.max(box.size.x, box.size.y, box.size.z);
|
|
3896
|
+
if (!(largest > 0))
|
|
3897
|
+
return null;
|
|
3898
|
+
const factor = targetSizeMeters / largest;
|
|
3899
|
+
node.obj3D.scale.multiplyScalar(factor);
|
|
3900
|
+
this.emitTransformChanged(uuid);
|
|
3901
|
+
return this.toPoint(node.obj3D.scale);
|
|
3902
|
+
}
|
|
3903
|
+
/**
|
|
3904
|
+
* Places the model on a surface point without touching its pivot: the node position is
|
|
3905
|
+
* adjusted so that the bounding box bottom-center lies on `point`.
|
|
3906
|
+
* - floor (normal pointing up, or no normal): bottom-center on the point
|
|
3907
|
+
* - wall (mostly horizontal normal): model turned around Y so its +Z faces away from the wall,
|
|
3908
|
+
* and pushed along the normal by half its depth so it does not cut the wall
|
|
3909
|
+
* - ceiling (normal pointing down): the model hangs, top-center on the point
|
|
3910
|
+
* @returns the new transform, null when the object is unknown or not loaded
|
|
3911
|
+
*/
|
|
3912
|
+
/**
|
|
3913
|
+
* @param frontYawOffset rotation (radians, around Y) that turns the front of the model toward its +Z
|
|
3914
|
+
* axis, for models whose front is another axis (e.g. -PI/2 when it is +X)
|
|
3915
|
+
*/
|
|
3916
|
+
placeObject3DOnSurface(uuid, point, normal, frontYawOffset = 0) {
|
|
3917
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3918
|
+
if (!node || !this.getObject3DBoundingBox(uuid))
|
|
3919
|
+
return null;
|
|
3920
|
+
const obj = node.obj3D;
|
|
3921
|
+
const n = normal ? new Vector3(normal.x, normal.y, normal.z) : new Vector3(0, 1, 0);
|
|
3922
|
+
if (n.lengthSq() === 0)
|
|
3923
|
+
n.set(0, 1, 0);
|
|
3924
|
+
n.normalize();
|
|
3925
|
+
const isWall = Math.abs(n.y) < 0.5;
|
|
3926
|
+
const isCeiling = !isWall && n.y < 0;
|
|
3927
|
+
const target = new Vector3(point.x, point.y, point.z);
|
|
3928
|
+
if (isWall) {
|
|
3929
|
+
// Face away from the wall; the box has to be measured after the rotation
|
|
3930
|
+
obj.rotation.y = Math.atan2(n.x, n.z) + frontYawOffset;
|
|
3931
|
+
}
|
|
3932
|
+
const box = this.getObject3DBoundingBox(uuid);
|
|
3933
|
+
if (isWall) {
|
|
3934
|
+
const horizontal = new Vector3(n.x, 0, n.z).normalize();
|
|
3935
|
+
const halfDepth = 0.5 * (Math.abs(horizontal.x) * box.size.x + Math.abs(horizontal.z) * box.size.z);
|
|
3936
|
+
target.addScaledVector(horizontal, halfDepth);
|
|
3937
|
+
}
|
|
3938
|
+
const anchorY = isCeiling ? box.max.y : box.min.y;
|
|
3939
|
+
obj.position.x += target.x - box.center.x;
|
|
3940
|
+
obj.position.y += target.y - anchorY;
|
|
3941
|
+
obj.position.z += target.z - box.center.z;
|
|
3942
|
+
this.emitTransformChanged(uuid);
|
|
3943
|
+
return this.getObject3DTransform(uuid);
|
|
3944
|
+
}
|
|
3945
|
+
getObject3DTransform(uuid) {
|
|
3946
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3947
|
+
if (!node)
|
|
3948
|
+
return null;
|
|
3949
|
+
const o = node.obj3D;
|
|
3950
|
+
return {
|
|
3951
|
+
position: this.toPoint(o.position),
|
|
3952
|
+
rotation: { x: o.rotation.x, y: o.rotation.y, z: o.rotation.z },
|
|
3953
|
+
scale: this.toPoint(o.scale),
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
/** Applies a (partial) transform. Rotation is in radians. */
|
|
3957
|
+
setObject3DTransform(uuid, transform) {
|
|
3958
|
+
const node = this.dictionnaryObjects3D.get(uuid);
|
|
3959
|
+
if (!node)
|
|
3960
|
+
return false;
|
|
3961
|
+
const o = node.obj3D;
|
|
3962
|
+
if (transform.position)
|
|
3963
|
+
o.position.set(transform.position.x, transform.position.y, transform.position.z);
|
|
3964
|
+
if (transform.rotation)
|
|
3965
|
+
o.rotation.set(transform.rotation.x, transform.rotation.y, transform.rotation.z);
|
|
3966
|
+
if (transform.scale)
|
|
3967
|
+
o.scale.set(transform.scale.x, transform.scale.y, transform.scale.z);
|
|
3968
|
+
this.emitTransformChanged(uuid);
|
|
3969
|
+
return true;
|
|
3970
|
+
}
|
|
3971
|
+
emitTransformChanged(uuid) {
|
|
3972
|
+
const transform = this.getObject3DTransform(uuid);
|
|
3973
|
+
if (transform) {
|
|
3974
|
+
this.object3DTransformChanged$.next({ uuid, ...transform });
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
//
|
|
3978
|
+
// ---------- Gizmo ----------
|
|
3979
|
+
//
|
|
3980
|
+
/** Creates the mp.transformControls node of a model and wires it (events, snap) */
|
|
3981
|
+
createGizmo(uuid, modelNode, sceneObject, mode, visible, node) {
|
|
3982
|
+
const gizmoNode = node ?? sceneObject.addNode();
|
|
3983
|
+
const component = gizmoNode.addComponent('mp.transformControls');
|
|
3984
|
+
gizmoNode.start();
|
|
3985
|
+
const tc = component.transformControls;
|
|
3986
|
+
tc.visible = visible;
|
|
3987
|
+
component.inputs.selection = modelNode;
|
|
3988
|
+
component.inputs.mode = mode;
|
|
3989
|
+
modelNode.obj3D.controls = component;
|
|
3990
|
+
const entry = {
|
|
3991
|
+
uuid,
|
|
3992
|
+
node: gizmoNode,
|
|
3993
|
+
component,
|
|
3994
|
+
mode,
|
|
3995
|
+
dragStart: null,
|
|
3996
|
+
lastPosition: modelNode.obj3D.position.clone(),
|
|
3997
|
+
onDraggingChanged: (event) => {
|
|
3998
|
+
entry.dragStart = event?.value ? modelNode.obj3D.position.clone() : null;
|
|
3999
|
+
entry.lastPosition = modelNode.obj3D.position.clone();
|
|
4000
|
+
},
|
|
4001
|
+
onObjectChange: () => this.onGizmoObjectChange(entry, modelNode),
|
|
4002
|
+
};
|
|
4003
|
+
if (typeof tc.addEventListener === 'function') {
|
|
4004
|
+
tc.addEventListener('dragging-changed', entry.onDraggingChanged);
|
|
4005
|
+
tc.addEventListener('objectChange', entry.onObjectChange);
|
|
4006
|
+
}
|
|
4007
|
+
this.applySnap(tc);
|
|
4008
|
+
this.gizmos.set(uuid, entry);
|
|
4009
|
+
this.activeGizmoUuid = uuid;
|
|
4010
|
+
return entry;
|
|
4011
|
+
}
|
|
4012
|
+
/** Keeps a translate drag within maxDragDistance / maxDragHeight of where it started, then notifies */
|
|
4013
|
+
onGizmoObjectChange(entry, modelNode) {
|
|
4014
|
+
const pos = modelNode.obj3D.position;
|
|
4015
|
+
if (entry.mode === 'translate') {
|
|
4016
|
+
const start = entry.dragStart ?? entry.lastPosition;
|
|
4017
|
+
const delta = pos.clone().sub(start);
|
|
4018
|
+
delta.y = MathUtils.clamp(delta.y, -this.maxDragHeight, this.maxDragHeight);
|
|
4019
|
+
const length = delta.length();
|
|
4020
|
+
if (length > this.maxDragDistance) {
|
|
4021
|
+
delta.multiplyScalar(this.maxDragDistance / length);
|
|
4022
|
+
}
|
|
4023
|
+
pos.copy(start).add(delta);
|
|
4024
|
+
}
|
|
4025
|
+
entry.lastPosition = pos.clone();
|
|
4026
|
+
this.emitTransformChanged(entry.uuid);
|
|
4027
|
+
}
|
|
4028
|
+
applySnap(tc) {
|
|
4029
|
+
if (!tc)
|
|
4030
|
+
return;
|
|
4031
|
+
const snap = this.gizmoSnap;
|
|
4032
|
+
const translation = snap?.translation ?? null;
|
|
4033
|
+
const rotation = snap?.rotationDeg != null ? MathUtils.degToRad(snap.rotationDeg) : null;
|
|
4034
|
+
const scale = snap?.scale ?? null;
|
|
4035
|
+
if (typeof tc.setTranslationSnap === 'function')
|
|
4036
|
+
tc.setTranslationSnap(translation);
|
|
4037
|
+
else
|
|
4038
|
+
tc.translationSnap = translation;
|
|
4039
|
+
if (typeof tc.setRotationSnap === 'function')
|
|
4040
|
+
tc.setRotationSnap(rotation);
|
|
4041
|
+
else
|
|
4042
|
+
tc.rotationSnap = rotation;
|
|
4043
|
+
if (typeof tc.setScaleSnap === 'function')
|
|
4044
|
+
tc.setScaleSnap(scale);
|
|
4045
|
+
else
|
|
4046
|
+
tc.scaleSnap = scale;
|
|
4047
|
+
}
|
|
4048
|
+
/**
|
|
4049
|
+
* Shows the transform gizmo on an object (any previous gizmo is detached first).
|
|
4050
|
+
* Re-attaching to the object that already has the gizmo only changes its mode.
|
|
4051
|
+
*/
|
|
4052
|
+
async attachGizmo(uuid, mode, sdk) {
|
|
4053
|
+
const modelNode = this.dictionnaryObjects3D.get(uuid);
|
|
4054
|
+
const sceneObject = this.dictionnarySceneObjects3D.get(uuid);
|
|
4055
|
+
if (!modelNode || !sceneObject)
|
|
4056
|
+
return false;
|
|
4057
|
+
const existing = this.gizmos.get(uuid);
|
|
4058
|
+
if (existing && this.activeGizmoUuid === uuid) {
|
|
4059
|
+
this.setGizmoMode(mode);
|
|
4060
|
+
return true;
|
|
4061
|
+
}
|
|
4062
|
+
// A started scene object refuses new nodes (addNode returns null): use a dedicated one
|
|
4063
|
+
let node = sceneObject.addNode();
|
|
4064
|
+
let gizmoSceneObject = null;
|
|
4065
|
+
if (!node) {
|
|
4066
|
+
if (!sdk?.Scene)
|
|
4067
|
+
return false;
|
|
4068
|
+
[gizmoSceneObject] = await sdk.Scene.createObjects(1);
|
|
4069
|
+
node = gizmoSceneObject.addNode();
|
|
4070
|
+
}
|
|
4071
|
+
this.detachGizmo();
|
|
4072
|
+
const entry = this.createGizmo(uuid, modelNode, sceneObject, mode, true, node);
|
|
4073
|
+
entry.sceneObject = gizmoSceneObject;
|
|
4074
|
+
this.lastObject3D = modelNode.obj3D;
|
|
4075
|
+
return true;
|
|
4076
|
+
}
|
|
4077
|
+
/** Switches the active gizmo between translate / rotate / scale without re-attaching it */
|
|
4078
|
+
setGizmoMode(mode) {
|
|
4079
|
+
const entry = this.activeGizmoUuid ? this.gizmos.get(this.activeGizmoUuid) : null;
|
|
4080
|
+
if (!entry)
|
|
4081
|
+
return;
|
|
4082
|
+
entry.mode = mode;
|
|
4083
|
+
entry.dragStart = null;
|
|
4084
|
+
entry.component.inputs.mode = mode;
|
|
4085
|
+
const tc = entry.component.transformControls;
|
|
4086
|
+
if (tc && typeof tc.setMode === 'function')
|
|
4087
|
+
tc.setMode(mode);
|
|
4088
|
+
}
|
|
4089
|
+
/** Really removes the gizmo from the scene (default: the active one) */
|
|
4090
|
+
detachGizmo(uuid) {
|
|
4091
|
+
const id = uuid ?? this.activeGizmoUuid;
|
|
4092
|
+
if (!id)
|
|
4093
|
+
return;
|
|
4094
|
+
const entry = this.gizmos.get(id);
|
|
4095
|
+
if (!entry)
|
|
4096
|
+
return;
|
|
4097
|
+
const tc = entry.component?.transformControls;
|
|
4098
|
+
if (tc) {
|
|
4099
|
+
if (typeof tc.removeEventListener === 'function') {
|
|
4100
|
+
tc.removeEventListener('dragging-changed', entry.onDraggingChanged);
|
|
4101
|
+
tc.removeEventListener('objectChange', entry.onObjectChange);
|
|
4102
|
+
}
|
|
4103
|
+
tc.visible = false;
|
|
4104
|
+
if (typeof tc.detach === 'function')
|
|
4105
|
+
tc.detach();
|
|
4106
|
+
if (typeof tc.dispose === 'function')
|
|
4107
|
+
tc.dispose();
|
|
4108
|
+
if (tc.parent && typeof tc.parent.remove === 'function')
|
|
4109
|
+
tc.parent.remove(tc);
|
|
4110
|
+
}
|
|
4111
|
+
try {
|
|
4112
|
+
entry.node.stop();
|
|
4113
|
+
}
|
|
4114
|
+
catch { /* already stopped */ }
|
|
4115
|
+
try {
|
|
4116
|
+
entry.sceneObject?.stop();
|
|
4117
|
+
}
|
|
4118
|
+
catch { /* already stopped */ }
|
|
4119
|
+
const modelNode = this.dictionnaryObjects3D.get(id);
|
|
4120
|
+
if (modelNode?.obj3D)
|
|
4121
|
+
modelNode.obj3D.controls = null;
|
|
4122
|
+
this.gizmos.delete(id);
|
|
4123
|
+
if (this.activeGizmoUuid === id)
|
|
4124
|
+
this.activeGizmoUuid = null;
|
|
4125
|
+
}
|
|
4126
|
+
isGizmoAttached(uuid) {
|
|
4127
|
+
return uuid ? this.gizmos.has(uuid) : this.activeGizmoUuid !== null;
|
|
4128
|
+
}
|
|
4129
|
+
/** Snap steps for all gizmos (null / missing value = no snap) */
|
|
4130
|
+
setGizmoSnap(snap) {
|
|
4131
|
+
this.gizmoSnap = snap ? { ...snap } : null;
|
|
4132
|
+
this.gizmos.forEach((entry) => this.applySnap(entry.component?.transformControls));
|
|
4133
|
+
}
|
|
4134
|
+
setGizmoMaxDragDistance(maxDistance, maxHeight) {
|
|
4135
|
+
if (maxDistance > 0)
|
|
4136
|
+
this.maxDragDistance = maxDistance;
|
|
4137
|
+
if (maxHeight !== undefined && maxHeight > 0)
|
|
4138
|
+
this.maxDragHeight = maxHeight;
|
|
4139
|
+
}
|
|
4140
|
+
/**
|
|
4141
|
+
* Prevents camera navigation to other sweeps while a gizmo is edited.
|
|
4142
|
+
* The SDK has no "freeze camera" switch, so every enabled sweep except the current one is
|
|
4143
|
+
* disabled (Sweep.disable); exactly those sweeps are re-enabled on unlock. Sweeps already
|
|
4144
|
+
* disabled (forbidden sweeps, edit mode) are never touched, and `keepDisabled` lists sweeps
|
|
4145
|
+
* that must stay disabled on unlock.
|
|
4146
|
+
* @returns false when the lock could not be applied (SDK without Sweep.disable, unknown current sweep)
|
|
4147
|
+
*/
|
|
4148
|
+
async setNavigationLocked(sdk, locked, sweepCollection, currentSweepId, keepDisabled = []) {
|
|
4149
|
+
if (!sdk?.Sweep || typeof sdk.Sweep.disable !== 'function' || typeof sdk.Sweep.enable !== 'function') {
|
|
4150
|
+
return false;
|
|
4151
|
+
}
|
|
4152
|
+
if (locked) {
|
|
4153
|
+
if (this.sweepsDisabledByLock.length > 0)
|
|
4154
|
+
return true;
|
|
4155
|
+
if (!sweepCollection || !currentSweepId)
|
|
4156
|
+
return false;
|
|
4157
|
+
// a sweep this lock re-enabled just before may still be reported disabled by the SDK
|
|
4158
|
+
const ids = Object.keys(sweepCollection).filter((sid) => sid !== currentSweepId &&
|
|
4159
|
+
(sweepCollection[sid]?.enabled !== false || this.sweepsEnabledByUnlock.includes(sid)));
|
|
4160
|
+
if (ids.length === 0)
|
|
4161
|
+
return true;
|
|
4162
|
+
try {
|
|
4163
|
+
await sdk.Sweep.disable(...ids);
|
|
4164
|
+
this.sweepsDisabledByLock = ids;
|
|
4165
|
+
}
|
|
4166
|
+
catch {
|
|
4167
|
+
// fall back to one call per sweep to know exactly which ones were disabled
|
|
4168
|
+
const results = await Promise.allSettled(ids.map((sid) => sdk.Sweep.disable(sid)));
|
|
4169
|
+
this.sweepsDisabledByLock = ids.filter((_, i) => results[i].status === 'fulfilled');
|
|
4170
|
+
}
|
|
4171
|
+
return true;
|
|
4172
|
+
}
|
|
4173
|
+
const toEnable = this.sweepsDisabledByLock.filter((sid) => !keepDisabled.includes(sid));
|
|
4174
|
+
this.sweepsDisabledByLock = [];
|
|
4175
|
+
this.sweepsEnabledByUnlock = toEnable;
|
|
4176
|
+
if (toEnable.length > 0) {
|
|
4177
|
+
try {
|
|
4178
|
+
await sdk.Sweep.enable(...toEnable);
|
|
4179
|
+
}
|
|
4180
|
+
catch {
|
|
4181
|
+
await Promise.allSettled(toEnable.map((sid) => sdk.Sweep.enable(sid)));
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
return true;
|
|
4185
|
+
}
|
|
4186
|
+
isNavigationLocked() {
|
|
4187
|
+
return this.sweepsDisabledByLock.length > 0;
|
|
4188
|
+
}
|
|
4189
|
+
/** Sweeps currently disabled by the navigation lock */
|
|
4190
|
+
getSweepsDisabledByLock() {
|
|
4191
|
+
return [...this.sweepsDisabledByLock];
|
|
4192
|
+
}
|
|
4193
|
+
/**
|
|
4194
|
+
* Legacy API: attaches a gizmo to a model node. Prefer attachGizmo(uuid, mode).
|
|
4195
|
+
*/
|
|
3625
4196
|
async attachGizmoControlTo3DObject(sdk, modelNode, sceneObject, mode, visible, isNewObject) {
|
|
3626
4197
|
let node = sceneObject.addNode();
|
|
3627
4198
|
if (!node) {
|
|
3628
4199
|
const [newSceneObject] = await sdk.Scene.createObjects(1);
|
|
3629
4200
|
node = newSceneObject.addNode();
|
|
3630
4201
|
}
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
if (isNewObject) {
|
|
3638
|
-
if (!this.lastObject3D || !this.lastObject3D.controls) {
|
|
3639
|
-
try {
|
|
3640
|
-
modelNode.obj3D.uuid = this.lastObject3D.uuid || this.lastObject3D.id;
|
|
3641
|
-
}
|
|
3642
|
-
catch (e) {
|
|
3643
|
-
console.log(`id obj in Scene was not assigned to id from DB`);
|
|
3644
|
-
}
|
|
3645
|
-
this.lastObject3D = modelNode.obj3D;
|
|
4202
|
+
if (isNewObject && (!this.lastObject3D || !this.lastObject3D.controls)) {
|
|
4203
|
+
try {
|
|
4204
|
+
modelNode.obj3D.uuid = this.lastObject3D.uuid || this.lastObject3D.id;
|
|
4205
|
+
}
|
|
4206
|
+
catch (e) {
|
|
4207
|
+
// lastObject3D is not set: the model keeps its own uuid
|
|
3646
4208
|
}
|
|
3647
4209
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
4210
|
+
this.detachGizmo();
|
|
4211
|
+
this.createGizmo(modelNode.obj3D.uuid, modelNode, sceneObject, mode, visible, node);
|
|
4212
|
+
this.lastObject3D = modelNode.obj3D;
|
|
3651
4213
|
return modelNode;
|
|
3652
4214
|
}
|
|
4215
|
+
/** Removes the gizmo of the last added / edited object */
|
|
3653
4216
|
removeGizmoFromLastObject() {
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
4217
|
+
const last = this.lastObject3D;
|
|
4218
|
+
if (!last)
|
|
4219
|
+
return;
|
|
4220
|
+
const uuid = last.uuid ?? last.id;
|
|
4221
|
+
if (uuid && this.gizmos.has(uuid)) {
|
|
4222
|
+
this.detachGizmo(uuid);
|
|
4223
|
+
}
|
|
4224
|
+
else if (last.controls) {
|
|
4225
|
+
// gizmo not created by this service: best effort
|
|
4226
|
+
last.controls.transformControls.visible = false;
|
|
4227
|
+
last.controls.transformControls.dispose();
|
|
4228
|
+
last.controls = null;
|
|
3658
4229
|
}
|
|
3659
4230
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
4231
|
+
//
|
|
4232
|
+
// ---------- Security camera ----------
|
|
4233
|
+
//
|
|
4234
|
+
applyViewFrustum(uuid, visible) {
|
|
4235
|
+
const animator = this.securityCameraAnimators.get(uuid);
|
|
4236
|
+
if (!animator)
|
|
4237
|
+
return false;
|
|
4238
|
+
const current = this.viewFrustumVisible.get(uuid) ?? true;
|
|
4239
|
+
if (current === visible)
|
|
4240
|
+
return true;
|
|
4241
|
+
try {
|
|
4242
|
+
animator.toggleViewFrustum();
|
|
4243
|
+
}
|
|
4244
|
+
catch {
|
|
4245
|
+
return false;
|
|
4246
|
+
}
|
|
4247
|
+
this.viewFrustumVisible.set(uuid, visible);
|
|
4248
|
+
return true;
|
|
4249
|
+
}
|
|
4250
|
+
/** Shows / hides the view frustum of the security camera `uuid` */
|
|
4251
|
+
setViewFrustum(uuid, visible) {
|
|
4252
|
+
return this.applyViewFrustum(uuid, visible);
|
|
4253
|
+
}
|
|
4254
|
+
isViewFrustumVisible(uuid) {
|
|
4255
|
+
return this.viewFrustumVisible.get(uuid) ?? false;
|
|
4256
|
+
}
|
|
4257
|
+
/** Toggles the frustum of `uuid`, or of the last loaded security camera when omitted */
|
|
4258
|
+
toggleViewFrustum(uuid) {
|
|
4259
|
+
const id = uuid ?? this.lastSecurityCameraUuid;
|
|
4260
|
+
if (id && this.securityCameraAnimators.has(id)) {
|
|
4261
|
+
this.applyViewFrustum(id, !(this.viewFrustumVisible.get(id) ?? true));
|
|
4262
|
+
}
|
|
4263
|
+
else if (!uuid && this.securityCameraAnimator) {
|
|
4264
|
+
// animator set by hand (legacy)
|
|
3662
4265
|
this.securityCameraAnimator.toggleViewFrustum();
|
|
3663
4266
|
}
|
|
3664
4267
|
}
|
|
3665
4268
|
clear() {
|
|
4269
|
+
Array.from(this.gizmos.keys()).forEach((id) => this.detachGizmo(id));
|
|
3666
4270
|
this.dictionnaryObjects3D.clear();
|
|
3667
4271
|
this.dictionnarySceneObjects3D.clear();
|
|
4272
|
+
this.loaderComponents.clear();
|
|
4273
|
+
this.securityCameraAnimators.clear();
|
|
4274
|
+
this.viewFrustumVisible.clear();
|
|
4275
|
+
this.lastSecurityCameraUuid = null;
|
|
4276
|
+
this.sweepsDisabledByLock = [];
|
|
3668
4277
|
this.lastObject3D = null;
|
|
3669
4278
|
this.azimuthalCrown = null;
|
|
3670
4279
|
this.securityCameraAnimator = null;
|
|
@@ -4361,6 +4970,8 @@ class Config {
|
|
|
4361
4970
|
constructor() { }
|
|
4362
4971
|
}
|
|
4363
4972
|
|
|
4973
|
+
/** Pointer intersections a 3D object can be placed on (Pointer.Colliders MODEL and SWEEP) */
|
|
4974
|
+
const PLACEMENT_SURFACES = ['intersectedobject.model', 'intersectedobject.sweep'];
|
|
4364
4975
|
class MatterportService {
|
|
4365
4976
|
sdkKey;
|
|
4366
4977
|
router;
|
|
@@ -4381,6 +4992,18 @@ class MatterportService {
|
|
|
4381
4992
|
isMeasureModeOn = false;
|
|
4382
4993
|
interactionMode = ViewerInteractions.DEFAULT;
|
|
4383
4994
|
cameraMode = 'INSIDE';
|
|
4995
|
+
/** Emitted when a placement (tag following the cursor, or 3D object preview) is cancelled by right click or Escape */
|
|
4996
|
+
placementCancelled$ = new Subject();
|
|
4997
|
+
/** Pending interactive placement of a 3D object (see startObject3DPlacement) */
|
|
4998
|
+
object3DPlacement = null;
|
|
4999
|
+
/** Sweeps snapshot taken when navigation is locked (disabled sweeps vanish from Sweep.data) */
|
|
5000
|
+
lockedSweepsSnapshot = {};
|
|
5001
|
+
/**
|
|
5002
|
+
* Every sweep seen in Sweep.data. After an unlock the SDK re-emits the re-enabled sweeps little by
|
|
5003
|
+
* little: a lock asked meanwhile must still know all of them (it found nothing to disable).
|
|
5004
|
+
*/
|
|
5005
|
+
knownSweeps = {};
|
|
5006
|
+
keydownTargets = [];
|
|
4384
5007
|
get mattertagIDs() {
|
|
4385
5008
|
return this.matterportTagService.mattertagIDs;
|
|
4386
5009
|
}
|
|
@@ -4522,6 +5145,10 @@ class MatterportService {
|
|
|
4522
5145
|
* Actions on left click when positioning mattertag in visit
|
|
4523
5146
|
*/
|
|
4524
5147
|
pointerLeftClickHandler = (evt) => {
|
|
5148
|
+
if (this.object3DPlacement) {
|
|
5149
|
+
this.validateObject3DPlacement();
|
|
5150
|
+
return;
|
|
5151
|
+
}
|
|
4525
5152
|
if (this.mattertagToFollow) {
|
|
4526
5153
|
const mattertagData = this.dictionnaryTags.get(this.mattertagToFollow);
|
|
4527
5154
|
if (mattertagData) {
|
|
@@ -4535,8 +5162,7 @@ class MatterportService {
|
|
|
4535
5162
|
};
|
|
4536
5163
|
pointerRightClickHandler = (e) => {
|
|
4537
5164
|
e.preventDefault();
|
|
4538
|
-
this.
|
|
4539
|
-
alert('action cancelled');
|
|
5165
|
+
this.cancelCurrentPlacement();
|
|
4540
5166
|
};
|
|
4541
5167
|
pointerMiddleClickHandler = (e) => {
|
|
4542
5168
|
this.pointerService.updateCursorDisplay(this.poseMatterport);
|
|
@@ -4583,6 +5209,7 @@ class MatterportService {
|
|
|
4583
5209
|
onRightClick: this.pointerRightClickHandler.bind(this),
|
|
4584
5210
|
onMiddleClick: this.pointerMiddleClickHandler.bind(this),
|
|
4585
5211
|
});
|
|
5212
|
+
this.registerEscapeListener(iframe);
|
|
4586
5213
|
// Load SDK
|
|
4587
5214
|
const showcaseWindow = iframe.contentWindow;
|
|
4588
5215
|
iframe.addEventListener('load', async () => {
|
|
@@ -4659,6 +5286,9 @@ class MatterportService {
|
|
|
4659
5286
|
// (we are about to validate, but it exists in sdk already)
|
|
4660
5287
|
this.enable_following_tag(this.mattertagToFollow);
|
|
4661
5288
|
}
|
|
5289
|
+
if (this.object3DPlacement) {
|
|
5290
|
+
this.updateObject3DPlacementPreview(intersection);
|
|
5291
|
+
}
|
|
4662
5292
|
if (this.pointerService) {
|
|
4663
5293
|
this.pointerService.setCursorPositionButtonDisplayed(false);
|
|
4664
5294
|
}
|
|
@@ -4684,6 +5314,11 @@ class MatterportService {
|
|
|
4684
5314
|
// subscribe to sweeps
|
|
4685
5315
|
this.sdk.Sweep.data.subscribe({
|
|
4686
5316
|
onCollectionUpdated: function subscr(collection) {
|
|
5317
|
+
this.knownSweeps = { ...this.knownSweeps, ...collection };
|
|
5318
|
+
if (this.object3DService.isNavigationLocked()) {
|
|
5319
|
+
// sweeps disabled by the lock are no longer in the collection: keep them known
|
|
5320
|
+
collection = { ...this.lockedSweepsSnapshot, ...collection };
|
|
5321
|
+
}
|
|
4687
5322
|
this.sweeps = Object.keys(collection);
|
|
4688
5323
|
this.sweepCollection = collection;
|
|
4689
5324
|
// Rebuild sid ↔ uuid maps whenever the collection updates
|
|
@@ -4795,7 +5430,7 @@ class MatterportService {
|
|
|
4795
5430
|
// Pointer trick
|
|
4796
5431
|
// Create a div that will appear when the cursor is still
|
|
4797
5432
|
// It will intercept the click of the mouse before it trigerring the Matterport's tour navigation
|
|
4798
|
-
this.pointerService.startPointerTrick(this.sdk, this.container, () => this.poseMatterport, () => this.poseCamera, () => this.interactionMode, () => this.
|
|
5433
|
+
this.pointerService.startPointerTrick(this.sdk, this.container, () => this.poseMatterport, () => this.poseCamera, () => this.interactionMode, () => this.getFollowTarget());
|
|
4799
5434
|
/**
|
|
4800
5435
|
* Transitions
|
|
4801
5436
|
*/
|
|
@@ -4867,7 +5502,7 @@ class MatterportService {
|
|
|
4867
5502
|
* Styling of pointer
|
|
4868
5503
|
*/
|
|
4869
5504
|
updatePointerTrick() {
|
|
4870
|
-
this.pointerService.updatePointerTrick(this.sdk, this.container, this.poseMatterport, this.poseCamera, this.interactionMode, this.
|
|
5505
|
+
this.pointerService.updatePointerTrick(this.sdk, this.container, this.poseMatterport, this.poseCamera, this.interactionMode, this.getFollowTarget());
|
|
4871
5506
|
}
|
|
4872
5507
|
/**
|
|
4873
5508
|
* Triggers a tag visibility refresh based on current camera mode, floor and sweep.
|
|
@@ -5184,22 +5819,22 @@ class MatterportService {
|
|
|
5184
5819
|
);
|
|
5185
5820
|
}*/
|
|
5186
5821
|
addCustomCss() {
|
|
5187
|
-
const css = `
|
|
5188
|
-
.footer-ui,
|
|
5189
|
-
.showcase-footer,
|
|
5190
|
-
.mp-nova-btn-group,
|
|
5191
|
-
.footer-divider,
|
|
5192
|
-
.logo-link,
|
|
5193
|
-
.footer-logo,
|
|
5194
|
-
.bottom-ui,
|
|
5195
|
-
img.logo-image,
|
|
5196
|
-
img[class^="logo-image"],
|
|
5197
|
-
img[class*=" logo-image"],
|
|
5198
|
-
[data-testid="showcase-footer"] {
|
|
5199
|
-
opacity: 0 !important;
|
|
5200
|
-
visibility: hidden !important;
|
|
5201
|
-
pointer-events: none !important;
|
|
5202
|
-
}
|
|
5822
|
+
const css = `
|
|
5823
|
+
.footer-ui,
|
|
5824
|
+
.showcase-footer,
|
|
5825
|
+
.mp-nova-btn-group,
|
|
5826
|
+
.footer-divider,
|
|
5827
|
+
.logo-link,
|
|
5828
|
+
.footer-logo,
|
|
5829
|
+
.bottom-ui,
|
|
5830
|
+
img.logo-image,
|
|
5831
|
+
img[class^="logo-image"],
|
|
5832
|
+
img[class*=" logo-image"],
|
|
5833
|
+
[data-testid="showcase-footer"] {
|
|
5834
|
+
opacity: 0 !important;
|
|
5835
|
+
visibility: hidden !important;
|
|
5836
|
+
pointer-events: none !important;
|
|
5837
|
+
}
|
|
5203
5838
|
`;
|
|
5204
5839
|
// 1st method: Via Scene SDK
|
|
5205
5840
|
if (this.sdk?.Scene?.addStyle) {
|
|
@@ -5296,6 +5931,9 @@ class MatterportService {
|
|
|
5296
5931
|
* @param uuid
|
|
5297
5932
|
*/
|
|
5298
5933
|
async deleteObject3D(uuid) {
|
|
5934
|
+
if (this.object3DPlacement?.uuid === uuid) {
|
|
5935
|
+
this.cancelObject3DPlacement();
|
|
5936
|
+
}
|
|
5299
5937
|
return this.object3DService.deleteObject3D(uuid);
|
|
5300
5938
|
}
|
|
5301
5939
|
getObject3DModelNodeFromDictionnary(uuid) {
|
|
@@ -5340,6 +5978,11 @@ class MatterportService {
|
|
|
5340
5978
|
* Cancels following of cursor (meaning deleting last Mattertag)
|
|
5341
5979
|
*/
|
|
5342
5980
|
cancelFollowingCursor() {
|
|
5981
|
+
if (this.object3DPlacement) {
|
|
5982
|
+
// generic cancel (toolbox buttons...) while a 3D object follows the cursor
|
|
5983
|
+
this.cancelObject3DPlacement();
|
|
5984
|
+
return;
|
|
5985
|
+
}
|
|
5343
5986
|
this.deleteLastMattertag();
|
|
5344
5987
|
this.setInteractionMode(ViewerInteractions.DEFAULT);
|
|
5345
5988
|
}
|
|
@@ -5708,7 +6351,10 @@ class MatterportService {
|
|
|
5708
6351
|
* Clear all variables, deletes all tags (when viewer is remover or model changed=reload needed)
|
|
5709
6352
|
*/
|
|
5710
6353
|
async clearAll() {
|
|
5711
|
-
|
|
6354
|
+
this.cancelObject3DPlacement();
|
|
6355
|
+
this.removeEscapeListener();
|
|
6356
|
+
this.lockedSweepsSnapshot = {};
|
|
6357
|
+
this.knownSweeps = {};
|
|
5712
6358
|
this.clearViewSubscriptions();
|
|
5713
6359
|
this.viewsByType.clear();
|
|
5714
6360
|
this.currentView = null;
|
|
@@ -5732,6 +6378,9 @@ class MatterportService {
|
|
|
5732
6378
|
async init3DObjectViewer() {
|
|
5733
6379
|
return this.object3DService.init3DObjectViewer(this.sdk);
|
|
5734
6380
|
}
|
|
6381
|
+
/**
|
|
6382
|
+
* Adds a 3D model; resolves once it is loaded, rejects on unknown format or load timeout (~15 s).
|
|
6383
|
+
*/
|
|
5735
6384
|
async add3DObject(obj, mode) {
|
|
5736
6385
|
return this.object3DService.add3DObject(this.sdk, obj, mode);
|
|
5737
6386
|
}
|
|
@@ -5741,7 +6390,43 @@ class MatterportService {
|
|
|
5741
6390
|
isObjectVisible(objectId) {
|
|
5742
6391
|
return this.object3DService.isObjectVisible(objectId);
|
|
5743
6392
|
}
|
|
5744
|
-
|
|
6393
|
+
/** Moves the camera to the object (centered on its bounding box). Resolves false when the object is unknown. */
|
|
6394
|
+
/**
|
|
6395
|
+
* Moves the camera to the sweep closest to a 3D object and turns it toward the object.
|
|
6396
|
+
* @param visibleArea client X bounds of the part of the viewer not covered by other panels
|
|
6397
|
+
* (the lateral menu covers the right of the viewer): the object is centered there.
|
|
6398
|
+
* Falls back to the temporary tag navigation when no sweep can be used.
|
|
6399
|
+
*/
|
|
6400
|
+
async pointCameraTo3DObject(objectId, visibleArea) {
|
|
6401
|
+
const box = this.object3DService.getObject3DBoundingBox(objectId);
|
|
6402
|
+
const sweep = box ? this.findNearestSweep(box.center) : null;
|
|
6403
|
+
const sweepData = sweep ? { ...this.lockedSweepsSnapshot, ...(this.sweepCollection ?? {}) }[sweep.sid] : null;
|
|
6404
|
+
if (box && sweepData?.position && this.sdk?.Sweep) {
|
|
6405
|
+
const projection = this.poseCamera?.projection;
|
|
6406
|
+
const tanHalfFov = projection?.[0] ? 1 / projection[0] : 1;
|
|
6407
|
+
let screenX = 0;
|
|
6408
|
+
const viewer = document.querySelector('#viewer-module');
|
|
6409
|
+
if (visibleArea && viewer) {
|
|
6410
|
+
const rect = viewer.getBoundingClientRect();
|
|
6411
|
+
screenX = visibleCenterScreenX(rect, visibleArea.left, visibleArea.right);
|
|
6412
|
+
}
|
|
6413
|
+
const rotation = computeFramingRotation(sweepData.position, box.center, tanHalfFov, screenX);
|
|
6414
|
+
try {
|
|
6415
|
+
if (this.poseCamera?.sweep === sweep.sid) {
|
|
6416
|
+
await this.sdk.Camera.setRotation(rotation, { speed: 80 });
|
|
6417
|
+
}
|
|
6418
|
+
else {
|
|
6419
|
+
await this.sdk.Sweep.moveTo(sweep.sid, {
|
|
6420
|
+
rotation,
|
|
6421
|
+
transition: this.sdk.Sweep.Transition?.FLY,
|
|
6422
|
+
});
|
|
6423
|
+
}
|
|
6424
|
+
return true;
|
|
6425
|
+
}
|
|
6426
|
+
catch (error) {
|
|
6427
|
+
// disabled or unreachable sweep: use the tag navigation below
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
5745
6430
|
return this.object3DService.pointCameraTo3DObject(this.sdk, objectId, this.createMattertagFromPOI.bind(this), this.goToTag.bind(this), this.deleteLastMattertag.bind(this), this.getTagFromElementId.bind(this));
|
|
5746
6431
|
}
|
|
5747
6432
|
getSceneNodeFromObject3DId(uuid) {
|
|
@@ -5756,8 +6441,252 @@ class MatterportService {
|
|
|
5756
6441
|
removeGizmoFromLastObject() {
|
|
5757
6442
|
this.object3DService.removeGizmoFromLastObject();
|
|
5758
6443
|
}
|
|
5759
|
-
|
|
5760
|
-
|
|
6444
|
+
/** Toggles the frustum of the security camera `uuid` (default: the last loaded one) */
|
|
6445
|
+
toggleViewFrustum(uuid) {
|
|
6446
|
+
this.object3DService.toggleViewFrustum(uuid);
|
|
6447
|
+
}
|
|
6448
|
+
setViewFrustum(uuid, visible) {
|
|
6449
|
+
return this.object3DService.setViewFrustum(uuid, visible);
|
|
6450
|
+
}
|
|
6451
|
+
isViewFrustumVisible(uuid) {
|
|
6452
|
+
return this.object3DService.isViewFrustumVisible(uuid);
|
|
6453
|
+
}
|
|
6454
|
+
//
|
|
6455
|
+
// ---------- 3D objects: geometry, transform, gizmo ----------
|
|
6456
|
+
//
|
|
6457
|
+
/** Emits on gizmo changes and on setObject3DTransform / placeObject3DOnSurface / normalizeObject3DSize */
|
|
6458
|
+
get object3DTransformChanged$() {
|
|
6459
|
+
return this.object3DService.object3DTransformChanged$;
|
|
6460
|
+
}
|
|
6461
|
+
/** World-space bounding box, null when the object is unknown or not loaded */
|
|
6462
|
+
getObject3DBoundingBox(uuid) {
|
|
6463
|
+
return this.object3DService.getObject3DBoundingBox(uuid);
|
|
6464
|
+
}
|
|
6465
|
+
/** Dimensions of the model in meters, without its rotation */
|
|
6466
|
+
getObject3DDimensions(uuid) {
|
|
6467
|
+
return this.object3DService.getObject3DDimensions(uuid);
|
|
6468
|
+
}
|
|
6469
|
+
/** Uniform scale so the largest dimension equals `targetSizeMeters`; returns the new scale or null */
|
|
6470
|
+
normalizeObject3DSize(uuid, targetSizeMeters) {
|
|
6471
|
+
return this.object3DService.normalizeObject3DSize(uuid, targetSizeMeters);
|
|
6472
|
+
}
|
|
6473
|
+
/** Moves the object so its bounding box bottom-center (top-center on a ceiling) lies on `point` */
|
|
6474
|
+
placeObject3DOnSurface(uuid, point, normal, frontYawOffset = 0) {
|
|
6475
|
+
return this.object3DService.placeObject3DOnSurface(uuid, point, normal, frontYawOffset);
|
|
6476
|
+
}
|
|
6477
|
+
getObject3DTransform(uuid) {
|
|
6478
|
+
return this.object3DService.getObject3DTransform(uuid);
|
|
6479
|
+
}
|
|
6480
|
+
/** Rotation in radians */
|
|
6481
|
+
setObject3DTransform(uuid, transform) {
|
|
6482
|
+
return this.object3DService.setObject3DTransform(uuid, transform);
|
|
6483
|
+
}
|
|
6484
|
+
attachGizmo(uuid, mode) {
|
|
6485
|
+
return this.object3DService.attachGizmo(uuid, mode, this.sdk);
|
|
6486
|
+
}
|
|
6487
|
+
setGizmoMode(mode) {
|
|
6488
|
+
this.object3DService.setGizmoMode(mode);
|
|
6489
|
+
}
|
|
6490
|
+
detachGizmo(uuid) {
|
|
6491
|
+
this.object3DService.detachGizmo(uuid);
|
|
6492
|
+
}
|
|
6493
|
+
isGizmoAttached(uuid) {
|
|
6494
|
+
return this.object3DService.isGizmoAttached(uuid);
|
|
6495
|
+
}
|
|
6496
|
+
/** null disables snapping (default) */
|
|
6497
|
+
setGizmoSnap(snap) {
|
|
6498
|
+
this.object3DService.setGizmoSnap(snap);
|
|
6499
|
+
}
|
|
6500
|
+
/** Limits of a translate drag from its start (default 2 m, +/- 1.5 m vertically) */
|
|
6501
|
+
setGizmoMaxDragDistance(maxDistance, maxHeight) {
|
|
6502
|
+
this.object3DService.setGizmoMaxDragDistance(maxDistance, maxHeight);
|
|
6503
|
+
}
|
|
6504
|
+
/**
|
|
6505
|
+
* Locks / unlocks camera navigation to other sweeps while a gizmo is edited: every enabled sweep
|
|
6506
|
+
* except the current one is disabled, and exactly those are re-enabled on unlock.
|
|
6507
|
+
* @returns false when the lock could not be applied (SDK not ready, current sweep unknown)
|
|
6508
|
+
*/
|
|
6509
|
+
async setNavigationLocked(locked) {
|
|
6510
|
+
if (!this.sdk)
|
|
6511
|
+
return false;
|
|
6512
|
+
const currentSweep = this.poseCamera?.sweep ?? null;
|
|
6513
|
+
const sweeps = { ...this.knownSweeps, ...(this.sweepCollection ?? {}) };
|
|
6514
|
+
if (locked && !this.object3DService.isNavigationLocked()) {
|
|
6515
|
+
this.lockedSweepsSnapshot = sweeps;
|
|
6516
|
+
}
|
|
6517
|
+
const keepDisabled = (this.forbiddenSweeps ?? []).map((s) => this.navigationService.resolveSweepSid(s));
|
|
6518
|
+
const done = await this.object3DService.setNavigationLocked(this.sdk, locked, sweeps, currentSweep, keepDisabled);
|
|
6519
|
+
if (!locked || !done) {
|
|
6520
|
+
this.lockedSweepsSnapshot = {};
|
|
6521
|
+
}
|
|
6522
|
+
return done;
|
|
6523
|
+
}
|
|
6524
|
+
isNavigationLocked() {
|
|
6525
|
+
return this.object3DService.isNavigationLocked();
|
|
6526
|
+
}
|
|
6527
|
+
//
|
|
6528
|
+
// ---------- 3D objects: interactive placement ----------
|
|
6529
|
+
//
|
|
6530
|
+
/**
|
|
6531
|
+
* Makes an already loaded model follow the cursor on the surfaces of the visit.
|
|
6532
|
+
* Left click resolves with the surface point / normal under the cursor (read the object origin
|
|
6533
|
+
* with getObject3DTransform); right click or Escape hides the model and resolves null
|
|
6534
|
+
* (deleting it is up to the caller). Resolves null right away if another interaction is running.
|
|
6535
|
+
*/
|
|
6536
|
+
startObject3DPlacement(uuid, options = {}) {
|
|
6537
|
+
if (!this.object3DService.getObject3DModelNodeFromDictionnary(uuid)) {
|
|
6538
|
+
return Promise.resolve(null);
|
|
6539
|
+
}
|
|
6540
|
+
if (this.object3DPlacement) {
|
|
6541
|
+
this.cancelObject3DPlacement();
|
|
6542
|
+
}
|
|
6543
|
+
if (this.interactionMode !== ViewerInteractions.DEFAULT) {
|
|
6544
|
+
return Promise.resolve(null);
|
|
6545
|
+
}
|
|
6546
|
+
return new Promise((resolve) => {
|
|
6547
|
+
this.object3DPlacement = { uuid, resolve, lastPoint: null, lastNormal: null, frontYawOffset: options.frontYawOffset ?? 0 };
|
|
6548
|
+
this.object3DService.setObject3DColliderEnabled(uuid, false);
|
|
6549
|
+
this.object3DService.setObject3DVisible(uuid, false);
|
|
6550
|
+
this.setInteractionMode(ViewerInteractions.PLACING_OBJECT3D);
|
|
6551
|
+
if (this.poseMatterport?.position) {
|
|
6552
|
+
this.updateObject3DPlacementPreview(this.poseMatterport);
|
|
6553
|
+
}
|
|
6554
|
+
});
|
|
6555
|
+
}
|
|
6556
|
+
isPlacingObject3D() {
|
|
6557
|
+
return !!this.object3DPlacement;
|
|
6558
|
+
}
|
|
6559
|
+
/** Cancels the running object placement (model hidden, promise resolves null) */
|
|
6560
|
+
cancelObject3DPlacement() {
|
|
6561
|
+
const placement = this.object3DPlacement;
|
|
6562
|
+
if (!placement)
|
|
6563
|
+
return;
|
|
6564
|
+
this.endObject3DPlacement();
|
|
6565
|
+
this.object3DService.setObject3DVisible(placement.uuid, false);
|
|
6566
|
+
placement.resolve(null);
|
|
6567
|
+
}
|
|
6568
|
+
/**
|
|
6569
|
+
* Drops the object where it is previewed (same as a left click). Touch screens have no hover: each
|
|
6570
|
+
* tap moves the preview, and the app confirms with a button.
|
|
6571
|
+
* @returns false when no surface was pointed yet
|
|
6572
|
+
*/
|
|
6573
|
+
confirmObject3DPlacement() {
|
|
6574
|
+
if (!this.object3DPlacement?.lastPoint)
|
|
6575
|
+
return false;
|
|
6576
|
+
this.validateObject3DPlacement();
|
|
6577
|
+
return true;
|
|
6578
|
+
}
|
|
6579
|
+
/** True once the preview stands on a surface (the placement can be confirmed) */
|
|
6580
|
+
hasObject3DPlacementPoint() {
|
|
6581
|
+
return !!this.object3DPlacement?.lastPoint;
|
|
6582
|
+
}
|
|
6583
|
+
validateObject3DPlacement() {
|
|
6584
|
+
const placement = this.object3DPlacement;
|
|
6585
|
+
if (!placement || !placement.lastPoint)
|
|
6586
|
+
return; // no surface under the cursor yet
|
|
6587
|
+
this.endObject3DPlacement();
|
|
6588
|
+
placement.resolve({
|
|
6589
|
+
position: placement.lastPoint,
|
|
6590
|
+
normal: placement.lastNormal ?? { x: 0, y: 1, z: 0 },
|
|
6591
|
+
});
|
|
6592
|
+
}
|
|
6593
|
+
endObject3DPlacement() {
|
|
6594
|
+
const placement = this.object3DPlacement;
|
|
6595
|
+
if (!placement)
|
|
6596
|
+
return;
|
|
6597
|
+
this.object3DPlacement = null;
|
|
6598
|
+
this.object3DService.setObject3DColliderEnabled(placement.uuid, true);
|
|
6599
|
+
this.setInteractionMode(ViewerInteractions.DEFAULT);
|
|
6600
|
+
}
|
|
6601
|
+
updateObject3DPlacementPreview(intersection) {
|
|
6602
|
+
const placement = this.object3DPlacement;
|
|
6603
|
+
if (!placement || !intersection?.position)
|
|
6604
|
+
return;
|
|
6605
|
+
// Only the scanned surfaces: hitting the preview itself (reported as "unknown") would push it away
|
|
6606
|
+
// along the ray at each move, until it lands kilometers from the camera
|
|
6607
|
+
if (typeof intersection.object === 'string' && !PLACEMENT_SURFACES.includes(intersection.object))
|
|
6608
|
+
return;
|
|
6609
|
+
const point = { x: intersection.position.x, y: intersection.position.y, z: intersection.position.z };
|
|
6610
|
+
const normal = intersection.normal
|
|
6611
|
+
? { x: intersection.normal.x, y: intersection.normal.y, z: intersection.normal.z }
|
|
6612
|
+
: { x: 0, y: 1, z: 0 };
|
|
6613
|
+
if (!placement.lastPoint) {
|
|
6614
|
+
// hidden until it is on a surface (it was loaded at the camera position)
|
|
6615
|
+
this.object3DService.setObject3DVisible(placement.uuid, true);
|
|
6616
|
+
}
|
|
6617
|
+
placement.lastPoint = point;
|
|
6618
|
+
placement.lastNormal = normal;
|
|
6619
|
+
this.object3DService.placeObject3DOnSurface(placement.uuid, point, normal, placement.frontYawOffset);
|
|
6620
|
+
}
|
|
6621
|
+
/** Token for the pointer trick: truthy while something follows the cursor (tag or 3D object) */
|
|
6622
|
+
getFollowTarget() {
|
|
6623
|
+
return this.mattertagToFollow ?? this.object3DPlacement?.uuid ?? null;
|
|
6624
|
+
}
|
|
6625
|
+
/**
|
|
6626
|
+
* Cancels whatever follows the cursor (right click / Escape): the 3D object preview, or the tag.
|
|
6627
|
+
* Emits placementCancelled$ when something was actually running.
|
|
6628
|
+
*/
|
|
6629
|
+
cancelCurrentPlacement() {
|
|
6630
|
+
const wasRunning = this.interactionMode !== ViewerInteractions.DEFAULT || !!this.object3DPlacement;
|
|
6631
|
+
// nothing follows the cursor: a plain right click must not remove the last (existing) tag
|
|
6632
|
+
if (!wasRunning)
|
|
6633
|
+
return;
|
|
6634
|
+
if (this.object3DPlacement) {
|
|
6635
|
+
this.cancelObject3DPlacement();
|
|
6636
|
+
}
|
|
6637
|
+
else {
|
|
6638
|
+
this.cancelFollowingCursor();
|
|
6639
|
+
}
|
|
6640
|
+
this.placementCancelled$.next();
|
|
6641
|
+
}
|
|
6642
|
+
/** Escape cancels any placement. Listens on the page and on the (same-origin) Showcase iframe, which takes the focus on click. */
|
|
6643
|
+
registerEscapeListener(iframe) {
|
|
6644
|
+
this.removeEscapeListener();
|
|
6645
|
+
const handler = (e) => {
|
|
6646
|
+
if (e.key !== 'Escape')
|
|
6647
|
+
return;
|
|
6648
|
+
if (this.interactionMode !== ViewerInteractions.DEFAULT || this.object3DPlacement) {
|
|
6649
|
+
this.cancelCurrentPlacement();
|
|
6650
|
+
}
|
|
6651
|
+
};
|
|
6652
|
+
const targets = [document];
|
|
6653
|
+
try {
|
|
6654
|
+
if (iframe?.contentWindow)
|
|
6655
|
+
targets.push(iframe.contentWindow);
|
|
6656
|
+
}
|
|
6657
|
+
catch { /* cross-origin iframe: page listener only */ }
|
|
6658
|
+
targets.forEach((target) => {
|
|
6659
|
+
target.addEventListener('keydown', handler);
|
|
6660
|
+
this.keydownTargets.push({ target, handler });
|
|
6661
|
+
});
|
|
6662
|
+
}
|
|
6663
|
+
removeEscapeListener() {
|
|
6664
|
+
this.keydownTargets.forEach(({ target, handler }) => target.removeEventListener('keydown', handler));
|
|
6665
|
+
this.keydownTargets = [];
|
|
6666
|
+
}
|
|
6667
|
+
ngOnDestroy() {
|
|
6668
|
+
this.cancelObject3DPlacement();
|
|
6669
|
+
this.removeEscapeListener();
|
|
6670
|
+
}
|
|
6671
|
+
//
|
|
6672
|
+
// ---------- Sweeps ----------
|
|
6673
|
+
//
|
|
6674
|
+
/**
|
|
6675
|
+
* Closest enabled sweep to a world position (for instance to re-assign the zone of a moved object).
|
|
6676
|
+
* `sweepId` is the DB-format id (like currentSweep), `sid` the SDK id. null when no sweep is known.
|
|
6677
|
+
*/
|
|
6678
|
+
findNearestSweep(position) {
|
|
6679
|
+
const collection = { ...this.lockedSweepsSnapshot, ...(this.sweepCollection ?? {}) };
|
|
6680
|
+
const sids = Object.keys(collection).filter((sid) => collection[sid]?.position && collection[sid].enabled !== false);
|
|
6681
|
+
const best = pickNearestSweepOnSameStorey(sids.map((sid) => ({ sid, position: collection[sid].position, floorY: sweepFloorY(collection[sid]) })), position);
|
|
6682
|
+
const data = best ? collection[best] : null;
|
|
6683
|
+
if (!best)
|
|
6684
|
+
return null;
|
|
6685
|
+
return {
|
|
6686
|
+
sweepId: this.navigationService.resolveSweepUuid(best),
|
|
6687
|
+
sid: best,
|
|
6688
|
+
floorSequence: data.floorInfo?.sequence ?? (typeof data.floor === 'number' ? data.floor : null),
|
|
6689
|
+
};
|
|
5761
6690
|
}
|
|
5762
6691
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportService, deps: [{ token: 'config' }, { token: MATTERPORT_SDK_KEY }, { token: i1$1.Router }, { token: i1$1.ActivatedRoute }, { token: BaseVisibilityService }, { token: i0.NgZone }, { token: MatterportMeasurementService }, { token: MatterportNavigationService }, { token: MatterportPointerService }, { token: MatterportObject3DService }, { token: MatterportTagService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5763
6692
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportService, providedIn: 'root' });
|
|
@@ -9663,11 +10592,11 @@ class ModalSwitchVisitComponent {
|
|
|
9663
10592
|
});
|
|
9664
10593
|
}
|
|
9665
10594
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalSwitchVisitComponent, deps: [{ token: i1$2.NgbActiveModal }, { token: i1$1.Router }, { token: ViewerService }], target: i0.ɵɵFactoryTarget.Component });
|
|
9666
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ModalSwitchVisitComponent, selector: "lib-modal-switch-visit", inputs: { content: "content", model3D: "model3D", spaceID: "spaceID", dataToMove: "dataToMove" }, ngImport: i0, template: "<div class=\"modal-container\">\n <p>{{content}}</p>\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\n</div>\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
10595
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ModalSwitchVisitComponent, selector: "lib-modal-switch-visit", inputs: { content: "content", model3D: "model3D", spaceID: "spaceID", dataToMove: "dataToMove" }, ngImport: i0, template: "<div class=\"modal-container\">\r\n <p>{{content}}</p>\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\r\n</div>\r\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
9667
10596
|
}
|
|
9668
10597
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalSwitchVisitComponent, decorators: [{
|
|
9669
10598
|
type: Component,
|
|
9670
|
-
args: [{ selector: 'lib-modal-switch-visit', template: "<div class=\"modal-container\">\n <p>{{content}}</p>\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\n</div>\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"] }]
|
|
10599
|
+
args: [{ selector: 'lib-modal-switch-visit', template: "<div class=\"modal-container\">\r\n <p>{{content}}</p>\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\r\n</div>\r\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"] }]
|
|
9671
10600
|
}], ctorParameters: () => [{ type: i1$2.NgbActiveModal }, { type: i1$1.Router }, { type: ViewerService }], propDecorators: { content: [{
|
|
9672
10601
|
type: Input
|
|
9673
10602
|
}], model3D: [{
|
|
@@ -14386,11 +15315,11 @@ class SearchBarComponent {
|
|
|
14386
15315
|
this.searchEvent.emit(results);
|
|
14387
15316
|
}
|
|
14388
15317
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SearchBarComponent, deps: [{ token: SearchService }], target: i0.ɵɵFactoryTarget.Component });
|
|
14389
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SearchBarComponent, selector: "lib-search-bar", inputs: { searchable: "searchable", objectType: "objectType" }, outputs: { searchEvent: "searchEvent" }, ngImport: i0, template: "<div class=\"col-md-8 searchbar\">\n <div class=\"row depend\">\n <div class=\"col-md-5\">\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\n </div>\n <div class=\"col-md-5\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\n translate}}</button>\n </div>\n </div>\n</div>", styles: [".searchbar{margin-bottom:10px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
15318
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SearchBarComponent, selector: "lib-search-bar", inputs: { searchable: "searchable", objectType: "objectType" }, outputs: { searchEvent: "searchEvent" }, ngImport: i0, template: "<div class=\"col-md-8 searchbar\">\r\n <div class=\"row depend\">\r\n <div class=\"col-md-5\">\r\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\r\n </div>\r\n <div class=\"col-md-5\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n</div>", styles: [".searchbar{margin-bottom:10px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
14390
15319
|
}
|
|
14391
15320
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SearchBarComponent, decorators: [{
|
|
14392
15321
|
type: Component,
|
|
14393
|
-
args: [{ selector: 'lib-search-bar', template: "<div class=\"col-md-8 searchbar\">\n <div class=\"row depend\">\n <div class=\"col-md-5\">\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\n </div>\n <div class=\"col-md-5\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\n translate}}</button>\n </div>\n </div>\n</div>", styles: [".searchbar{margin-bottom:10px}\n"] }]
|
|
15322
|
+
args: [{ selector: 'lib-search-bar', template: "<div class=\"col-md-8 searchbar\">\r\n <div class=\"row depend\">\r\n <div class=\"col-md-5\">\r\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\r\n </div>\r\n <div class=\"col-md-5\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n</div>", styles: [".searchbar{margin-bottom:10px}\n"] }]
|
|
14394
15323
|
}], ctorParameters: () => [{ type: SearchService }], propDecorators: { searchable: [{
|
|
14395
15324
|
type: Input
|
|
14396
15325
|
}], objectType: [{
|
|
@@ -14838,11 +15767,11 @@ class LoaderComponent {
|
|
|
14838
15767
|
document.documentElement.style.setProperty('--color', this.color);
|
|
14839
15768
|
}
|
|
14840
15769
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14841
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LoaderComponent, selector: "lib-loader", inputs: { useLogo: "useLogo", color: "color" }, ngImport: i0, template: "<div class=\"sk-fading-circle\">\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\n <div class=\"sk-circle1 sk-circle\"></div>\n <div class=\"sk-circle2 sk-circle\"></div>\n <div class=\"sk-circle3 sk-circle\"></div>\n <div class=\"sk-circle4 sk-circle\"></div>\n <div class=\"sk-circle5 sk-circle\"></div>\n <div class=\"sk-circle6 sk-circle\"></div>\n <div class=\"sk-circle7 sk-circle\"></div>\n <div class=\"sk-circle8 sk-circle\"></div>\n <div class=\"sk-circle9 sk-circle\"></div>\n <div class=\"sk-circle10 sk-circle\"></div>\n <div class=\"sk-circle11 sk-circle\"></div>\n <div class=\"sk-circle12 sk-circle\"></div>\n</div>\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"], dependencies: [{ kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
15770
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LoaderComponent, selector: "lib-loader", inputs: { useLogo: "useLogo", color: "color" }, ngImport: i0, template: "<div class=\"sk-fading-circle\">\r\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\r\n <div class=\"sk-circle1 sk-circle\"></div>\r\n <div class=\"sk-circle2 sk-circle\"></div>\r\n <div class=\"sk-circle3 sk-circle\"></div>\r\n <div class=\"sk-circle4 sk-circle\"></div>\r\n <div class=\"sk-circle5 sk-circle\"></div>\r\n <div class=\"sk-circle6 sk-circle\"></div>\r\n <div class=\"sk-circle7 sk-circle\"></div>\r\n <div class=\"sk-circle8 sk-circle\"></div>\r\n <div class=\"sk-circle9 sk-circle\"></div>\r\n <div class=\"sk-circle10 sk-circle\"></div>\r\n <div class=\"sk-circle11 sk-circle\"></div>\r\n <div class=\"sk-circle12 sk-circle\"></div>\r\n</div>\r\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"], dependencies: [{ kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
14842
15771
|
}
|
|
14843
15772
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LoaderComponent, decorators: [{
|
|
14844
15773
|
type: Component,
|
|
14845
|
-
args: [{ selector: 'lib-loader', template: "<div class=\"sk-fading-circle\">\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\n <div class=\"sk-circle1 sk-circle\"></div>\n <div class=\"sk-circle2 sk-circle\"></div>\n <div class=\"sk-circle3 sk-circle\"></div>\n <div class=\"sk-circle4 sk-circle\"></div>\n <div class=\"sk-circle5 sk-circle\"></div>\n <div class=\"sk-circle6 sk-circle\"></div>\n <div class=\"sk-circle7 sk-circle\"></div>\n <div class=\"sk-circle8 sk-circle\"></div>\n <div class=\"sk-circle9 sk-circle\"></div>\n <div class=\"sk-circle10 sk-circle\"></div>\n <div class=\"sk-circle11 sk-circle\"></div>\n <div class=\"sk-circle12 sk-circle\"></div>\n</div>\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"] }]
|
|
15774
|
+
args: [{ selector: 'lib-loader', template: "<div class=\"sk-fading-circle\">\r\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\r\n <div class=\"sk-circle1 sk-circle\"></div>\r\n <div class=\"sk-circle2 sk-circle\"></div>\r\n <div class=\"sk-circle3 sk-circle\"></div>\r\n <div class=\"sk-circle4 sk-circle\"></div>\r\n <div class=\"sk-circle5 sk-circle\"></div>\r\n <div class=\"sk-circle6 sk-circle\"></div>\r\n <div class=\"sk-circle7 sk-circle\"></div>\r\n <div class=\"sk-circle8 sk-circle\"></div>\r\n <div class=\"sk-circle9 sk-circle\"></div>\r\n <div class=\"sk-circle10 sk-circle\"></div>\r\n <div class=\"sk-circle11 sk-circle\"></div>\r\n <div class=\"sk-circle12 sk-circle\"></div>\r\n</div>\r\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"] }]
|
|
14846
15775
|
}], ctorParameters: () => [], propDecorators: { useLogo: [{
|
|
14847
15776
|
type: Input
|
|
14848
15777
|
}], color: [{
|
|
@@ -14900,11 +15829,11 @@ class SupportModalComponent {
|
|
|
14900
15829
|
}
|
|
14901
15830
|
}
|
|
14902
15831
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SupportModalComponent, deps: [{ token: SupportService }, { token: i1$2.NgbActiveModal }, { token: i3.UntypedFormBuilder }, { token: BaseUserService }, { token: ValidatorsService }], target: i0.ɵɵFactoryTarget.Component });
|
|
14903
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SupportModalComponent, selector: "app-support-modal", ngImport: i0, template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\n</div>\n<div class=\"modal-body\">\n <div class=\"support-loader-container\">\n <lib-loader *ngIf=\"loading\"></lib-loader>\n </div>\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\n <div>\n <label>{{'Title' | translate}} *</label>\n <div>\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\n </div>\n </div>\n <div *ngIf=\"!currentUser\">\n <label>{{'Email' | translate}} *</label>\n <div>\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\n </div>\n </div>\n <div>\n <label>{{'Description' | translate}} *</label>\n <div>\n <textarea formControlName=\"description\" class=\"form-control\"\n data-testid=\"new-support-description\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\n </div>\n <div class=\"invalid-feedback\" >\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\n </div>\n </div>\n </form>\n\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\n </div>\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\n data-testid=\"new-support-error-info\">\n\n </div>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\n data-testid=\"new-support-close-btn\"></button>\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i11.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: LoaderComponent, selector: "lib-loader", inputs: ["useLogo", "color"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
15832
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SupportModalComponent, selector: "app-support-modal", ngImport: i0, template: "<div class=\"modal-header\">\r\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\r\n</div>\r\n<div class=\"modal-body\">\r\n <div class=\"support-loader-container\">\r\n <lib-loader *ngIf=\"loading\"></lib-loader>\r\n </div>\r\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\r\n <div>\r\n <label>{{'Title' | translate}} *</label>\r\n <div>\r\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!currentUser\">\r\n <label>{{'Email' | translate}} *</label>\r\n <div>\r\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\r\n </div>\r\n </div>\r\n <div>\r\n <label>{{'Description' | translate}} *</label>\r\n <div>\r\n <textarea formControlName=\"description\" class=\"form-control\"\r\n data-testid=\"new-support-description\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\r\n </div>\r\n <div class=\"invalid-feedback\" >\r\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\r\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\r\n </div>\r\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\r\n data-testid=\"new-support-error-info\">\r\n\r\n </div>\r\n</div>\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\r\n data-testid=\"new-support-close-btn\"></button>\r\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\r\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\r\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i11.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: LoaderComponent, selector: "lib-loader", inputs: ["useLogo", "color"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
14904
15833
|
}
|
|
14905
15834
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SupportModalComponent, decorators: [{
|
|
14906
15835
|
type: Component,
|
|
14907
|
-
args: [{ selector: 'app-support-modal', template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\n</div>\n<div class=\"modal-body\">\n <div class=\"support-loader-container\">\n <lib-loader *ngIf=\"loading\"></lib-loader>\n </div>\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\n <div>\n <label>{{'Title' | translate}} *</label>\n <div>\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\n </div>\n </div>\n <div *ngIf=\"!currentUser\">\n <label>{{'Email' | translate}} *</label>\n <div>\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\n </div>\n </div>\n <div>\n <label>{{'Description' | translate}} *</label>\n <div>\n <textarea formControlName=\"description\" class=\"form-control\"\n data-testid=\"new-support-description\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\n </div>\n <div class=\"invalid-feedback\" >\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\n </div>\n </div>\n </form>\n\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\n </div>\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\n data-testid=\"new-support-error-info\">\n\n </div>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\n data-testid=\"new-support-close-btn\"></button>\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"] }]
|
|
15836
|
+
args: [{ selector: 'app-support-modal', template: "<div class=\"modal-header\">\r\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\r\n</div>\r\n<div class=\"modal-body\">\r\n <div class=\"support-loader-container\">\r\n <lib-loader *ngIf=\"loading\"></lib-loader>\r\n </div>\r\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\r\n <div>\r\n <label>{{'Title' | translate}} *</label>\r\n <div>\r\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!currentUser\">\r\n <label>{{'Email' | translate}} *</label>\r\n <div>\r\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\r\n </div>\r\n </div>\r\n <div>\r\n <label>{{'Description' | translate}} *</label>\r\n <div>\r\n <textarea formControlName=\"description\" class=\"form-control\"\r\n data-testid=\"new-support-description\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\r\n </div>\r\n <div class=\"invalid-feedback\" >\r\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\r\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\r\n </div>\r\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\r\n data-testid=\"new-support-error-info\">\r\n\r\n </div>\r\n</div>\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\r\n data-testid=\"new-support-close-btn\"></button>\r\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\r\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\r\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"] }]
|
|
14908
15837
|
}], ctorParameters: () => [{ type: SupportService }, { type: i1$2.NgbActiveModal }, { type: i3.UntypedFormBuilder }, { type: BaseUserService }, { type: ValidatorsService }] });
|
|
14909
15838
|
|
|
14910
15839
|
class LocaleService {
|
|
@@ -16344,11 +17273,11 @@ class NavigationBarComponent {
|
|
|
16344
17273
|
this.baseVisibilityService.isChangePositionVisible.next(false);
|
|
16345
17274
|
}
|
|
16346
17275
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NavigationBarComponent, deps: [{ token: 'currentAwsKinesisAnalyticsService' }, { token: i1$1.Router }, { token: SpaceService }, { token: BaseUserService }, { token: ZoneService }, { token: VisitService }, { token: ViewerService }, { token: NavigatorService }, { token: ZoneChangeService }, { token: BaseVisibilityService }], target: i0.ɵɵFactoryTarget.Component });
|
|
16347
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: NavigationBarComponent, selector: "lib-navigation-bar", viewQueries: [{ propertyName: "navbarSelectMenu", first: true, predicate: ["navbarSelectMenu"], descendants: true }], ngImport: i0, template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\n <div class=\"d-flex w-100\">\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\n </option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\n (ngModelChange)=\"onFloorClick()\">\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\n (ngModelChange)=\"onZoneClick()\">\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\n </select>\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\n <p class=\"fw-bold lh-base align-middle text-center\">\n {{ showLocation() }}\n </p>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n </div>\n </div>\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23565656;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23FFFFFF;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i11.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
17276
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: NavigationBarComponent, selector: "lib-navigation-bar", viewQueries: [{ propertyName: "navbarSelectMenu", first: true, predicate: ["navbarSelectMenu"], descendants: true }], ngImport: i0, template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\r\n <div class=\"d-flex w-100\">\r\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\r\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\r\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\r\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\r\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\r\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\r\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\r\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\r\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\r\n </option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\r\n (ngModelChange)=\"onFloorClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\r\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\r\n (ngModelChange)=\"onZoneClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\r\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\r\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\r\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\r\n </select>\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\r\n <p class=\"fw-bold lh-base align-middle text-center\">\r\n {{ showLocation() }}\r\n </p>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23565656;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23FFFFFF;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i11.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
|
|
16348
17277
|
}
|
|
16349
17278
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NavigationBarComponent, decorators: [{
|
|
16350
17279
|
type: Component,
|
|
16351
|
-
args: [{ selector: 'lib-navigation-bar', template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\n <div class=\"d-flex w-100\">\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\n </option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\n (ngModelChange)=\"onFloorClick()\">\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\n (ngModelChange)=\"onZoneClick()\">\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\n </select>\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\n <p class=\"fw-bold lh-base align-middle text-center\">\n {{ showLocation() }}\n </p>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n </div>\n </div>\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23565656;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23FFFFFF;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"] }]
|
|
17280
|
+
args: [{ selector: 'lib-navigation-bar', template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\r\n <div class=\"d-flex w-100\">\r\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\r\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\r\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\r\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\r\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\r\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\r\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\r\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\r\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\r\n </option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\r\n (ngModelChange)=\"onFloorClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\r\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\r\n (ngModelChange)=\"onZoneClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\r\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\r\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\r\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\r\n </select>\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\r\n <p class=\"fw-bold lh-base align-middle text-center\">\r\n {{ showLocation() }}\r\n </p>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23565656;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23FFFFFF;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"] }]
|
|
16352
17281
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
16353
17282
|
type: Inject,
|
|
16354
17283
|
args: ['currentAwsKinesisAnalyticsService']
|
|
@@ -16617,11 +17546,11 @@ class MenuBarComponent {
|
|
|
16617
17546
|
}
|
|
16618
17547
|
}
|
|
16619
17548
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuBarComponent, deps: [{ token: i1$1.Router }, { token: LocaleService }, { token: BaseUserService }, { token: NavigatorService }, { token: SupportService }, { token: BaseVisibilityService }, { token: i1$2.NgbCalendar }, { token: i1$2.NgbDateParserFormatter }, { token: FilterService }, { token: AmplifyCacheService }], target: i0.ɵɵFactoryTarget.Component });
|
|
16620
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: MenuBarComponent, selector: "lib-menu-bar", inputs: { cacheName: "cacheName" }, ngImport: i0, template: "<div id=\"menu-bar\">\n <div *ngIf=\"!isVisitor\">\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\n </div>\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <p *ngIf=\"user && mission\" class=\"p-2\">\n <span class=\"username\">{{ user.displayName }}</span><br />\n <span class=\"user-role\">{{ mission.role }}</span><br />\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\n translate}}</span>\n </p>\n <div class=\"dropdown-divider\"></div>\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\n {{'Users management' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/profile\">\n {{'Profile' | translate}}</a>\n <div class=\"dropdown-divider\"></div>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\n [animation]=\"false\">\n <form\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\n (click)=\"onSubmit()\">{{'Search' |\n translate}}</button>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <div class=\"input-group date-selector m-1\">\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\n [value]=\"fromDate | formatDateNumberToDigits\"\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\n <ng-template #t let-date let-focused=\"focused\">\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\n (mouseleave)=\"hoveredDate = null\">\n {{ date.day }}\n </span>\n </ng-template>\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\n <div class=\"input-group date-selector m-1\">\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\n <button\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\n {{'Apply' | translate}}\n </button>\n <span\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n *ngIf=\"isDateFilterApplied && !dateChanged\">\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\n {{\"Applied\" | translate}}\n </span>\n <button\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\n |\n translate}}</button>\n <div\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\n for=\"btn-check-outlined\">{{\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\n </div>\n </div>\n </form>\n </div>\n </div>\n </nav>\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\n </div>\n <div *ngIf=\"isVisitor && isVisible\">\n <div class=\"visitor-div\">\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\n </div>\n <div class=\"avatar-container-menu\">\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.NgbCollapse, selector: "[ngbCollapse]", inputs: ["animation", "ngbCollapse", "horizontal"], outputs: ["ngbCollapseChange", "shown", "hidden"], exportAs: ["ngbCollapse"] }, { kind: "directive", type: i1$2.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "contentTemplate", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "popperOptions", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { kind: "component", type: AvatarComponent, selector: "lib-avatar", inputs: ["size", "userID"] }, { kind: "component", type: NavigationBarComponent, selector: "lib-navigation-bar" }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormatDateNumberToDigitsPipe, name: "formatDateNumberToDigits" }] });
|
|
17549
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: MenuBarComponent, selector: "lib-menu-bar", inputs: { cacheName: "cacheName" }, ngImport: i0, template: "<div id=\"menu-bar\">\r\n <div *ngIf=\"!isVisitor\">\r\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\r\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\r\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\r\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\r\n </div>\r\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\r\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <p *ngIf=\"user && mission\" class=\"p-2\">\r\n <span class=\"username\">{{ user.displayName }}</span><br />\r\n <span class=\"user-role\">{{ mission.role }}</span><br />\r\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\r\n translate}}</span>\r\n </p>\r\n <div class=\"dropdown-divider\"></div>\r\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\r\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\r\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\r\n {{'Users management' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/profile\">\r\n {{'Profile' | translate}}</a>\r\n <div class=\"dropdown-divider\"></div>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\r\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\r\n <span class=\"navbar-toggler-icon\"></span>\r\n </button>\r\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\r\n [animation]=\"false\">\r\n <form\r\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\r\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\r\n (click)=\"onSubmit()\">{{'Search' |\r\n translate}}</button>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\r\n [value]=\"fromDate | formatDateNumberToDigits\"\r\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\r\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\r\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\r\n <ng-template #t let-date let-focused=\"focused\">\r\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\r\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\r\n (mouseleave)=\"hoveredDate = null\">\r\n {{ date.day }}\r\n </span>\r\n </ng-template>\r\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\r\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\r\n <button\r\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\r\n {{'Apply' | translate}}\r\n </button>\r\n <span\r\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n *ngIf=\"isDateFilterApplied && !dateChanged\">\r\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\r\n {{\"Applied\" | translate}}\r\n </span>\r\n <button\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\r\n |\r\n translate}}</button>\r\n <div\r\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\r\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\r\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\r\n for=\"btn-check-outlined\">{{\r\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\r\n </div>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </nav>\r\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\r\n </div>\r\n <div *ngIf=\"isVisitor && isVisible\">\r\n <div class=\"visitor-div\">\r\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\r\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\r\n </div>\r\n <div class=\"avatar-container-menu\">\r\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.NgbCollapse, selector: "[ngbCollapse]", inputs: ["animation", "ngbCollapse", "horizontal"], outputs: ["ngbCollapseChange", "shown", "hidden"], exportAs: ["ngbCollapse"] }, { kind: "directive", type: i1$2.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "contentTemplate", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "popperOptions", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { kind: "component", type: AvatarComponent, selector: "lib-avatar", inputs: ["size", "userID"] }, { kind: "component", type: NavigationBarComponent, selector: "lib-navigation-bar" }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormatDateNumberToDigitsPipe, name: "formatDateNumberToDigits" }] });
|
|
16621
17550
|
}
|
|
16622
17551
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuBarComponent, decorators: [{
|
|
16623
17552
|
type: Component,
|
|
16624
|
-
args: [{ selector: 'lib-menu-bar', template: "<div id=\"menu-bar\">\n <div *ngIf=\"!isVisitor\">\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\n </div>\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <p *ngIf=\"user && mission\" class=\"p-2\">\n <span class=\"username\">{{ user.displayName }}</span><br />\n <span class=\"user-role\">{{ mission.role }}</span><br />\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\n translate}}</span>\n </p>\n <div class=\"dropdown-divider\"></div>\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\n {{'Users management' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/profile\">\n {{'Profile' | translate}}</a>\n <div class=\"dropdown-divider\"></div>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\n [animation]=\"false\">\n <form\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\n (click)=\"onSubmit()\">{{'Search' |\n translate}}</button>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <div class=\"input-group date-selector m-1\">\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\n [value]=\"fromDate | formatDateNumberToDigits\"\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\n <ng-template #t let-date let-focused=\"focused\">\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\n (mouseleave)=\"hoveredDate = null\">\n {{ date.day }}\n </span>\n </ng-template>\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\n <div class=\"input-group date-selector m-1\">\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\n <button\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\n {{'Apply' | translate}}\n </button>\n <span\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n *ngIf=\"isDateFilterApplied && !dateChanged\">\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\n {{\"Applied\" | translate}}\n </span>\n <button\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\n |\n translate}}</button>\n <div\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\n for=\"btn-check-outlined\">{{\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\n </div>\n </div>\n </form>\n </div>\n </div>\n </nav>\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\n </div>\n <div *ngIf=\"isVisitor && isVisible\">\n <div class=\"visitor-div\">\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\n </div>\n <div class=\"avatar-container-menu\">\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"] }]
|
|
17553
|
+
args: [{ selector: 'lib-menu-bar', template: "<div id=\"menu-bar\">\r\n <div *ngIf=\"!isVisitor\">\r\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\r\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\r\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\r\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\r\n </div>\r\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\r\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <p *ngIf=\"user && mission\" class=\"p-2\">\r\n <span class=\"username\">{{ user.displayName }}</span><br />\r\n <span class=\"user-role\">{{ mission.role }}</span><br />\r\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\r\n translate}}</span>\r\n </p>\r\n <div class=\"dropdown-divider\"></div>\r\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\r\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\r\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\r\n {{'Users management' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/profile\">\r\n {{'Profile' | translate}}</a>\r\n <div class=\"dropdown-divider\"></div>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\r\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\r\n <span class=\"navbar-toggler-icon\"></span>\r\n </button>\r\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\r\n [animation]=\"false\">\r\n <form\r\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\r\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\r\n (click)=\"onSubmit()\">{{'Search' |\r\n translate}}</button>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\r\n [value]=\"fromDate | formatDateNumberToDigits\"\r\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\r\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\r\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\r\n <ng-template #t let-date let-focused=\"focused\">\r\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\r\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\r\n (mouseleave)=\"hoveredDate = null\">\r\n {{ date.day }}\r\n </span>\r\n </ng-template>\r\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\r\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\r\n <button\r\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\r\n {{'Apply' | translate}}\r\n </button>\r\n <span\r\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n *ngIf=\"isDateFilterApplied && !dateChanged\">\r\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\r\n {{\"Applied\" | translate}}\r\n </span>\r\n <button\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\r\n |\r\n translate}}</button>\r\n <div\r\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\r\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\r\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\r\n for=\"btn-check-outlined\">{{\r\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\r\n </div>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </nav>\r\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\r\n </div>\r\n <div *ngIf=\"isVisitor && isVisible\">\r\n <div class=\"visitor-div\">\r\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\r\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\r\n </div>\r\n <div class=\"avatar-container-menu\">\r\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"] }]
|
|
16625
17554
|
}], ctorParameters: () => [{ type: i1$1.Router }, { type: LocaleService }, { type: BaseUserService }, { type: NavigatorService }, { type: SupportService }, { type: BaseVisibilityService }, { type: i1$2.NgbCalendar }, { type: i1$2.NgbDateParserFormatter }, { type: FilterService }, { type: AmplifyCacheService }], propDecorators: { cacheName: [{
|
|
16626
17555
|
type: Input
|
|
16627
17556
|
}] } });
|
|
@@ -16776,27 +17705,27 @@ class BaseTagService {
|
|
|
16776
17705
|
throw new Error('updatePoiMetadata is not implemented in the base class.');
|
|
16777
17706
|
}*/
|
|
16778
17707
|
async prepareEquipmentHtml(equip) {
|
|
16779
|
-
throw new Error(`prepareEquipmentHtml is not implemented in the base class.
|
|
17708
|
+
throw new Error(`prepareEquipmentHtml is not implemented in the base class.
|
|
16780
17709
|
Please override this method in your extended class.`);
|
|
16781
17710
|
}
|
|
16782
17711
|
async prepareTicketHtml(ticket) {
|
|
16783
|
-
throw new Error(`prepareTicketHtml is not implemented in the base class.
|
|
17712
|
+
throw new Error(`prepareTicketHtml is not implemented in the base class.
|
|
16784
17713
|
Please override this method in your extended class.`);
|
|
16785
17714
|
}
|
|
16786
17715
|
async prepareFeatureHtml(feature) {
|
|
16787
|
-
throw new Error(`prepareFeatureHtml is not implemented in the base class.
|
|
17716
|
+
throw new Error(`prepareFeatureHtml is not implemented in the base class.
|
|
16788
17717
|
Please override this method in your extended class.`);
|
|
16789
17718
|
}
|
|
16790
17719
|
async prepareMeasurementHtml(measure) {
|
|
16791
|
-
throw new Error(`prepareMeasurementHtml is not implemented in the base class.
|
|
17720
|
+
throw new Error(`prepareMeasurementHtml is not implemented in the base class.
|
|
16792
17721
|
Please override this method in your extended class.`);
|
|
16793
17722
|
}
|
|
16794
17723
|
async prepareDeskHtml(feature) {
|
|
16795
|
-
throw new Error(`prepareDeskHtml is not implemented in the base class.
|
|
17724
|
+
throw new Error(`prepareDeskHtml is not implemented in the base class.
|
|
16796
17725
|
Please override this method in your extended class.`);
|
|
16797
17726
|
}
|
|
16798
17727
|
async prepareIndicatorHtml(feature) {
|
|
16799
|
-
throw new Error(`prepareDeskHtml is not implemented in the base class.
|
|
17728
|
+
throw new Error(`prepareDeskHtml is not implemented in the base class.
|
|
16800
17729
|
Please override this method in your extended class.`);
|
|
16801
17730
|
}
|
|
16802
17731
|
async getSignedTagIconSource(tagIconSrc) {
|
|
@@ -16805,15 +17734,15 @@ class BaseTagService {
|
|
|
16805
17734
|
getIconTagImageForFeature(feature, poi) {
|
|
16806
17735
|
// const tagIcon = JSON.parse(poi.tagIcon);
|
|
16807
17736
|
// return tagIcon.src;
|
|
16808
|
-
throw new Error(`getIconTagImageForFeature is not implemented in the base class.
|
|
17737
|
+
throw new Error(`getIconTagImageForFeature is not implemented in the base class.
|
|
16809
17738
|
Please override this method in your extended class.`);
|
|
16810
17739
|
}
|
|
16811
17740
|
getScriptForTag(object, tagType) {
|
|
16812
|
-
throw new Error(`getScriptForTag is not implemented in the base class.
|
|
17741
|
+
throw new Error(`getScriptForTag is not implemented in the base class.
|
|
16813
17742
|
Please override this method in your extended class.`);
|
|
16814
17743
|
}
|
|
16815
17744
|
getAnnexeForCommentTypeInFeature(feature, commentType) {
|
|
16816
|
-
throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
|
|
17745
|
+
throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
|
|
16817
17746
|
Please override this method in your extended class.`);
|
|
16818
17747
|
}
|
|
16819
17748
|
getBillboardMediaToEmbed(object) {
|
|
@@ -16822,27 +17751,27 @@ class BaseTagService {
|
|
|
16822
17751
|
return { comment: null, tagDescription: '' };
|
|
16823
17752
|
}
|
|
16824
17753
|
onActionDetailClick(url) {
|
|
16825
|
-
throw new Error(`onActionDetailClick is not implemented in the base class.
|
|
17754
|
+
throw new Error(`onActionDetailClick is not implemented in the base class.
|
|
16826
17755
|
Please override this method in your extended class.`);
|
|
16827
17756
|
}
|
|
16828
17757
|
onActionAudioClick(audioCommentID) {
|
|
16829
|
-
throw new Error(`onActionDetailClick is not implemented in the base class.
|
|
17758
|
+
throw new Error(`onActionDetailClick is not implemented in the base class.
|
|
16830
17759
|
Please override this method in your extended class.`);
|
|
16831
17760
|
}
|
|
16832
17761
|
onActionVideoClick(url) {
|
|
16833
|
-
throw new Error(`onActionVideoClick is not implemented in the base class.
|
|
17762
|
+
throw new Error(`onActionVideoClick is not implemented in the base class.
|
|
16834
17763
|
Please override this method in your extended class.`);
|
|
16835
17764
|
}
|
|
16836
17765
|
onActionImageClick(imageCommentID) {
|
|
16837
|
-
throw new Error(`onActionImageClick is not implemented in the base class.
|
|
17766
|
+
throw new Error(`onActionImageClick is not implemented in the base class.
|
|
16838
17767
|
Please override this method in your extended class.`);
|
|
16839
17768
|
}
|
|
16840
17769
|
onActionDocClick(url) {
|
|
16841
|
-
throw new Error(`onActionDocClick is not implemented in the base class.
|
|
17770
|
+
throw new Error(`onActionDocClick is not implemented in the base class.
|
|
16842
17771
|
Please override this method in your extended class.`);
|
|
16843
17772
|
}
|
|
16844
17773
|
onActionYoutubeClick(url) {
|
|
16845
|
-
throw new Error(`onActionYoutubeClick is not implemented in the base class.
|
|
17774
|
+
throw new Error(`onActionYoutubeClick is not implemented in the base class.
|
|
16846
17775
|
Please override this method in your extended class.`);
|
|
16847
17776
|
}
|
|
16848
17777
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BaseTagService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|