Commit 50c83e1b authored by FORESTIER Fabien's avatar FORESTIER Fabien
Browse files

Reshape data detail component

parent 51fee58b
<div *ngFor="let key of keys; let odd=odd; let even=even;" class="property" [ngClass]="{'even': even, 'odd': odd}">
<p class="property-key" *ngIf="!inputIsArray()" [ngClass]="{'is-parent': isParent && isPropertyObjectOrArray(key)}"
<div *ngFor="let key of keys" class="property">
<div class="property-key" *ngIf="!inputIsArray()" [ngClass]="{'is-parent': isParent && isPropertyObjectOrArray(key)}"
(click)="toggleProperty(key)">
<ng-container *ngIf="isParent && isPropertyObjectOrArray(key)">
<span class="icon-minus" *ngIf="isOpenProperties[key]"></span>
<span class="icon-plus" *ngIf="!isOpenProperties[key]"></span>
<span class="icon-chevron" *ngIf="isOpenProperties[key]">
<svg class="icon-opened" xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9">
<path
d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" />
<path
d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" />
</svg>
</span>
<span class="icon-chevron" *ngIf="!isOpenProperties[key]">
<svg xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9">
<path
d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" />
<path
d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" />
</svg>
</span>
</ng-container>
<span>{{ key }}</span>
</p>
<div class="complex-property-warning"
*ngIf="(getTypeOfProperty(key) === 'array' || getTypeOfProperty(key) === 'object') && !isOpenProperties[key]">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="14" fill="none" viewBox="0 0 16 14">
<path
d="M15.581 12.031c.146.292.191.584.137.875-.055.31-.2.565-.438.766-.237.219-.52.328-.848.328H1.308c-.329 0-.611-.11-.848-.328a1.26 1.26 0 0 1-.438-.766 1.356 1.356 0 0 1 .137-.875L6.722.656c.164-.291.392-.483.683-.574.31-.11.61-.11.903 0 .31.091.546.283.71.574l6.563 11.375zM7.87 9.68c-.346 0-.647.127-.902.383a1.19 1.19 0 0 0-.356.874c0 .347.119.648.356.903.255.237.556.355.902.355s.638-.118.875-.355c.255-.255.383-.556.383-.902 0-.347-.128-.639-.383-.876a1.147 1.147 0 0 0-.875-.382zM6.667 5.168l.219 3.719c0 .073.027.146.082.218a.4.4 0 0 0 .246.082h1.312a.296.296 0 0 0 .219-.082c.073-.072.11-.145.11-.218l.218-3.719c0-.11-.036-.191-.11-.246a.26.26 0 0 0-.218-.11h-1.75a.34.34 0 0 0-.246.11c-.055.055-.082.137-.082.246z" />
</svg>
<span class="warning-complexity" i18n="@@dataset.data.complexProperty">Complex property</span>
</div>
</div>
<div class="property-child" [ngClass]="{'is-hidden': (!isOpenProperties[key] && isParent)}">
<ng-container [ngSwitch]="getTypeOfProperty(key)">
<div *ngSwitchCase="'object'">
<div *ngSwitchCase="'object'" class="complex-property-child">
<app-dataset-data-detail-properties [properties]="properties[key]" [isParent]="false">
</app-dataset-data-detail-properties>
</div>
<div *ngSwitchCase="'array'">
<div *ngSwitchCase="'array'" class="complex-property-child">
<app-dataset-data-detail-properties [properties]="properties[key]" [isParent]="false">
</app-dataset-data-detail-properties>
</div>
......
@import "../../../../../../scss/variables";
.property-value {
font-weight: bold;
font-weight: 600;
word-break: break-all;
}
......@@ -16,47 +16,55 @@
.property-key {
display: flex;
align-items: center;
flex-wrap: wrap;
.icon-minus {
height: 2px;
width: 10px;
margin-right: 0.5rem;
background: $grey-dark-color;
.complex-property-warning {
width: 100%;
padding-left: 1.25rem;
display: flex;
align-items: center;
svg {
fill: $grey-dark-color;
width: 0.875rem;
margin-right: 0.5rem;
}
.warning-complexity {
font-weight: 600;
}
}
.icon-plus {
height: 2px;
width: 10px;
.icon-chevron {
margin-right: 0.5rem;
background: $grey-dark-color;
position: relative;
&::after {
content: '';
height: 10px;
width: 2px;
background: $grey-dark-color;
position: absolute;
top: -4px;
left: 4px;
svg {
width: 12px;
fill: $grey-dark-color;
transform: rotate(-90deg);
}
svg.icon-opened {
transform: rotate(0deg);
}
}
}
.complex-property-child {
margin-left: 1.25rem;
margin-top: 1rem;
}
.is-parent {
cursor: pointer;
}
.property {
border: 1px solid #e8ecef;
padding: 0.625rem;
font-size: $size-7;
&.odd {
background-color: white;
}
padding-bottom: 0.625rem;
margin-bottom: 0.625rem;
font-size: 0.75rem;
&.even {
background-color: #f9f9f9;
&:not(:last-of-type) {
border-bottom: 1px solid $grey-background-color;
}
}
......@@ -7,8 +7,26 @@ import { Component, OnInit, Input } from '@angular/core';
})
export class DatasetDataDetailPropertiesComponent implements OnInit {
@Input() properties: Object;
private _properties: Object;
@Input()
set properties(properties) {
// In order to allow the potential scroll on the properties div, we need to wait for the translation animation
// on that component to be finished before updating the properties. Refere to the animation duration declared in
// dataset-table-map-component.scss
setTimeout(
() => {
this._properties = properties;
// Initilize the array with the keys of the json object received in the input
this.keys = this.getKeys(properties);
this.isOpenProperties = this.keys.reduce((acc, it) => {
acc[it] = (this.getTypeOfProperty(it) === 'object' || Array.isArray(this.properties[it])) ? false : true;
return acc;
// tslint:disable-next-line:align
}, {});
},
200,
);
}
// This input is used to display complex objects.
// When the value is an array or object, we will hide in first place, then allow to toggle the element.
// But (because we use recurcivity, this compnent can call himself, cf template) if the value of the value
......@@ -21,20 +39,19 @@ export class DatasetDataDetailPropertiesComponent implements OnInit {
constructor() { }
ngOnInit() {
// Initilize the array with the keys of the json object received in the input
this.keys = this.getKeys(this.properties);
this.isOpenProperties = this.keys.reduce((acc, it) => {
acc[it] = (this.getTypeOfProperty(it) === 'object' || Array.isArray(this.properties[it])) ? false : true;
return acc;
// tslint:disable-next-line:align
}, {});
}
get properties() {
return this._properties;
}
getKeys(obj) {
const keys = Object.keys(obj);
let keys = [];
if (obj) {
keys = Object.keys(obj);
}
return keys;
}
// Return the type of the 'key' property of the input object
getTypeOfProperty(key: string) {
const type = typeof this.properties[key];
......
<div class="data-details">
<button class="close-button button is-danger" (click)="closeSelf()"><i class="fas fa-angle-right"></i></button>
<div class="details-header">
<svg class="close-btn" (click)="closeSelf()" xmlns="http://www.w3.org/2000/svg" id="filtre-close"
viewBox="0 0 15 15">
<path stroke="white" fill="white"
d="M13.6 12.8L8.2 7.4l5.1-5.1c.2-.2.2-.5 0-.7s-.5-.2-.7 0l-5.1 5-5.1-5.1c-.2-.2-.5-.2-.7 0s-.2.5-.1.7l5.1 5.1-5.4 5.4c-.2.2-.2.5 0 .7.1.1.2.1.4.1s.3 0 .4-.1L7.5 8l5.4 5.4c.1.1.2.1.4.1s.3 0 .4-.1c.1-.1.1-.4-.1-.6z"
class="white" />
</svg>
</div>
<div class="properties">
<app-dataset-data-detail-properties [properties]="properties" [isParent]="true">
</app-dataset-data-detail-properties>
......
@import "../../../../../scss/variables";
$details-header-height: 3rem;
.data-details {
background-color: white;
width: 100%;
height: 100%;
padding: 20px;
box-shadow: -2px 0px 5px -1px rgba(0, 0, 0, 0.3);
box-shadow: -2px 0 5px -1px rgba(0, 0, 0, 0.3);
border-left: 1px solid $tomato-color;
position: relative;
}
.details-header {
background-color: $tomato-color;
height: $details-header-height;
display: flex;
align-items: center;
padding: 1rem 0 1rem 1rem;
}
.close-button {
// background-color: $tomato-color;
// color: white;
border-radius: unset;
width: 2rem;
left: -2rem;
box-shadow: -2px 0px 5px -1px rgba(0, 0, 0, 0.3);
position: absolute;
}
.close-btn {
height: 1rem;
cursor: pointer;
}
.properties {
margin-top: 0.625rem;
overflow-y: auto;
width: 100%;
height: 100%;
}
.properties {
overflow-y: auto;
width: 100%;
height: calc(100% - #{$details-header-height});
padding: 1.25rem;
}
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { DatasetDetailService } from '../../../services';
@Component({
selector: 'app-dataset-data-details',
......@@ -6,20 +7,36 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
styleUrls: ['./dataset-data-details.component.scss'],
})
export class DatasetDataDetailsComponent implements OnInit {
@Input() properties;
private _properties;
@Input()
set properties(properties) {
// Making sure the complex properties values are under objects or array form and not string
// When properties comme from the map MVT the types are lost and they are always represented as string
for (const key in properties) {
if (properties.hasOwnProperty(key)) {
if (!this._datasetDetailService.dataset.fields.types[key] && typeof properties[key] === 'string') {
try {
const parsed = JSON.parse(properties[key]);
properties[key] = parsed;
} catch (err) { }
}
}
}
this._properties = properties;
}
@Output() close = new EventEmitter<boolean>();
constructor() { }
constructor(
private _datasetDetailService: DatasetDetailService,
) { }
ngOnInit() {
}
get propertiesKeys() {
let keys;
if (this.properties) {
keys = Object.keys(this.properties);
}
return keys;
get properties() {
return this._properties;
}
closeSelf() {
......
......@@ -86,9 +86,9 @@
<app-dataset-map [selectedFeature]="selectedData" (featureClicked)="onFeatureClicked($event)"></app-dataset-map>
</div>
<div class="data-details-wrapper" *ngIf="selectedData !== null"
[ngClass]="{'data-details-opened': selectedData !== null && displayDataDetails === true, 'data-details-closed': displayDataDetails === false}">
<app-dataset-data-details (close)="closeDataDetails()" [properties]="selectedData.properties"
<div class="data-details-wrapper"
[ngClass]="{'data-details-opened': dataDetailsShouldBeAnimated && selectedData !== null && displayDataDetails === true, 'data-details-closed': dataDetailsShouldBeAnimated && (selectedData === null || displayDataDetails === false)}">
<app-dataset-data-details (close)="closeDataDetails()" [properties]="selectedFeatureProperties"
class="app-data-details">
</app-dataset-data-details>
</div>
......
......@@ -186,7 +186,6 @@
.data-details-opened {
animation: open_details 200ms;
animation-fill-mode: forwards;
visibility: visible;
}
.data-details-closed {
......@@ -196,29 +195,29 @@
@keyframes open_details {
from {
transform: translateX(400px);
transform: translateX(0);
}
to {
transform: translateX(0);
transform: translateX(-400px);
}
}
@keyframes close_details {
from {
transform: translateX(0);
transform: translateX(-400px);
}
to {
transform: translateX(400px);
transform: translateX(0);
}
}
.data-details-wrapper {
position: absolute;
width: 350px;
width: 280px;
height: 100%;
right: 0;
right: -400px;
top: 0;
z-index: 3;
max-width: 80%;
......
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { DatasetDetailService } from '../../../services';
import { linkFormats } from '../../../models/metadata.model';
import { DatasetMapComponent } from '../dataset-map/dataset-map.component';
......@@ -24,11 +24,12 @@ export class DatasetTableMapComponent implements OnInit, OnDestroy {
displayColumnsMenu = false;
mapIsEnabled = true;
tableIsEnabled = true;
displayDataDetails = true; // Wether the data details should be displayed or not
displayDataDetails = false; // Wether the data details should be displayed or not
properties: string[];
selectedProperties: string[] = []; // Array of columns to be displayed
selectedData: Data | mapboxgl.MapboxGeoJSONFeature = null; // Contains the properties of the selected feature
dataDetailsShouldBeAnimated = false;
// search
searchInput = new FormControl('');
......@@ -106,9 +107,7 @@ export class DatasetTableMapComponent implements OnInit, OnDestroy {
}
closeColumsMenu() {
if (this.displayColumnsMenu) {
this.displayColumnsMenu = false;
}
this.displayColumnsMenu = false;
}
// Returns the index if found in the array, -1 if not found
......@@ -137,11 +136,13 @@ export class DatasetTableMapComponent implements OnInit, OnDestroy {
}
onDataSelected(data: Data) {
this.dataDetailsShouldBeAnimated = true;
this.selectedData = data;
this.displayDataDetails = true;
}
onFeatureClicked(data: any) {
this.dataDetailsShouldBeAnimated = true;
this.selectedData = data;
this.displayDataDetails = true;
}
......@@ -151,6 +152,10 @@ export class DatasetTableMapComponent implements OnInit, OnDestroy {
this.selectedData.properties.gid : null;
}
get selectedFeatureProperties() {
return this.selectedData ? this.selectedData.properties : null;
}
// Get the number of documents in this dataset
get datasetDataNumber() {
return this._datasetDetailService.datasetDataNumber;
......
......@@ -284,7 +284,7 @@ export class MapService {
// When a click event occurs on the map, close the information panel
this._map.on('click', () => {
this.closePanel();
this.resetSelectedState();
});
if (this._map.getLayer('point-features')) {
......@@ -427,10 +427,11 @@ export class MapService {
return this._map;
}
closePanel() {
resetSelectedState() {
this.changeFeatureState(this.highlightedFeatureId, { highlight: false });
this.selectedFeature = null;
this.highlightedFeatureId = null;
this._featureSelected.next(null);
}
get featuresToUpdate$(): Observable<void> {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment