364 lines
11 KiB
Vue
364 lines
11 KiB
Vue
<template>
|
|
<div class="container grid">
|
|
<div class="full-width">
|
|
<SectionTitle class="grid-cell" :showActionButton="!selectedMaps.length" ref="headerContainer">
|
|
<template slot="icon">
|
|
<img src="../assets/icons/section-title/map.svg">
|
|
</template>
|
|
|
|
<template slot="title">
|
|
<VisualizationsTitle
|
|
:defaultTitle="$t(`MapsPage.header.title['${appliedFilter}']`)"
|
|
:selectedItems="selectedMaps.length"
|
|
:vizQuota="publicMapsQuota"
|
|
:vizCount="publicMapsCount"
|
|
:isOutOfQuota="isOutOfPublicMapsQuota"
|
|
:counterLabel="'Public Maps'"/>
|
|
</template>
|
|
|
|
<template v-if="shouldShowLimitsWarning" slot="warning">
|
|
<BadgeWarning>
|
|
<div class="warning" v-html="$t('MapsPage.header.warning', { counter: `${publicMapsCount}/${publicMapsQuota}`, path: upgradeUrl })"></div>
|
|
</BadgeWarning>
|
|
</template>
|
|
|
|
<template slot="dropdownButton">
|
|
<MapBulkActions
|
|
:selectedMaps="selectedMaps"
|
|
:areAllMapsSelected="areAllMapsSelected"
|
|
v-if="hasBulkActions && selectedMaps.length"
|
|
@selectAll="selectAll"
|
|
@deselectAll="deselectAll"></MapBulkActions>
|
|
|
|
<SettingsDropdown
|
|
section="maps"
|
|
v-if="!selectedMaps.length"
|
|
:filter="appliedFilter"
|
|
:order="appliedOrder"
|
|
:orderDirection="appliedOrderDirection"
|
|
:metadata="mapsMetadata"
|
|
@filterChanged="applyFilter">
|
|
<img svg-inline src="../assets/icons/common/filter.svg">
|
|
</SettingsDropdown>
|
|
|
|
<div class="mapcard-view-mode" @click="toggleViewMode" v-if="shouldShowViewSwitcher">
|
|
<img svg-inline src="../assets/icons/common/compactMap.svg" v-if="!isCondensed">
|
|
<img svg-inline src="../assets/icons/common/standardMap.svg" v-if="isCondensed">
|
|
</div>
|
|
</template>
|
|
<template slot="actionButton" v-if="!isFirstTimeViewingDashboard && !selectedMaps.length">
|
|
<CreateButton visualizationType="maps" :disabled="isViewer">{{ $t(`MapsPage.createMap`) }}</CreateButton>
|
|
</template>
|
|
</SectionTitle>
|
|
|
|
<div class="grid-cell" v-if="initialState">
|
|
<CreateMapCard></CreateMapCard>
|
|
</div>
|
|
|
|
<div
|
|
v-if="shouldShowListHeader"
|
|
class="grid-cell grid-cell--noMargin grid-cell--col12 grid__head--sticky"
|
|
:class="{ 'has-user-notification': isNotificationVisible }">
|
|
<CondensedMapHeader
|
|
:order="appliedOrder"
|
|
:orderDirection="appliedOrderDirection"
|
|
@orderChanged="applyOrder"
|
|
v-if="shouldShowListHeader">
|
|
</CondensedMapHeader>
|
|
</div>
|
|
|
|
<ul :class="[isCondensed ? 'grid grid-cell' : 'grid']" v-if="isFetchingMaps">
|
|
<li :class="[isCondensed ? condensedCSSClasses : cardCSSClasses]" v-for="n in maxVisibleMaps" :key="n">
|
|
<MapCardFake :condensed="isCondensed"></MapCardFake>
|
|
</li>
|
|
</ul>
|
|
|
|
<ul :class="[isCondensed ? 'grid grid-column grid-cell' : 'grid']" v-if="!isFetchingMaps && currentEntriesCount > 0">
|
|
<li v-for="map in maps" :class="[isCondensed ? condensedCSSClasses : cardCSSClasses]" :key="map.id">
|
|
<MapCard
|
|
:condensed="isCondensed"
|
|
:visualization="map"
|
|
:isSelected="isMapSelected(map)"
|
|
:selectMode="isSomeMapSelected"
|
|
:canHover="canHoverCard"
|
|
@toggleSelection="toggleSelected"
|
|
@contentChanged="onContentChanged">
|
|
</MapCard>
|
|
</li>
|
|
</ul>
|
|
|
|
<EmptyState
|
|
:text="emptyStateText"
|
|
v-if="emptyState">
|
|
<img svg-inline src="../assets/icons/common/compass.svg">
|
|
</EmptyState>
|
|
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapState, mapGetters } from 'vuex';
|
|
import CreateButton from 'new-dashboard/components/CreateButton.vue';
|
|
import CreateMapCard from 'new-dashboard/components/CreateMapCard';
|
|
import EmptyState from 'new-dashboard/components/States/EmptyState';
|
|
import InitialState from 'new-dashboard/components/States/InitialState';
|
|
import MapBulkActions from 'new-dashboard/components/BulkActions/MapBulkActions.vue';
|
|
import MapCard from 'new-dashboard/components/MapCard/MapCard.vue';
|
|
import CondensedMapHeader from 'new-dashboard/components/MapCard/CondensedMapHeader.vue';
|
|
import MapCardFake from 'new-dashboard/components/MapCard/fakes/MapCardFake';
|
|
import SectionTitle from 'new-dashboard/components/SectionTitle';
|
|
import VisualizationsTitle from 'new-dashboard/components/VisualizationsTitle';
|
|
import BadgeWarning from 'new-dashboard/components/BadgeWarning';
|
|
import SettingsDropdown from 'new-dashboard/components/Settings/Settings';
|
|
import { shiftClick } from 'new-dashboard/utils/shift-click.service.js';
|
|
|
|
export default {
|
|
name: 'MapsList',
|
|
props: {
|
|
maxVisibleMaps: {
|
|
type: Number,
|
|
default: 12
|
|
},
|
|
hasBulkActions: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
canHoverCard: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
canChangeViewMode: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
isCondensedDefault: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
},
|
|
components: {
|
|
CreateButton,
|
|
CreateMapCard,
|
|
EmptyState,
|
|
SettingsDropdown,
|
|
MapBulkActions,
|
|
MapCard,
|
|
CondensedMapHeader,
|
|
MapCardFake,
|
|
SectionTitle,
|
|
VisualizationsTitle,
|
|
BadgeWarning,
|
|
InitialState
|
|
},
|
|
data () {
|
|
return {
|
|
selectedMaps: [],
|
|
cardCSSClasses: 'grid-cell grid-cell--col4 grid-cell--col6--tablet grid-cell--col12--mobile map-element',
|
|
condensedCSSClasses: 'card-condensed',
|
|
isCondensed: this.isCondensedDefault,
|
|
lastCheckedItem: null
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState({
|
|
appliedFilter: state => state.maps.filterType,
|
|
appliedOrder: state => state.maps.order,
|
|
appliedOrderDirection: state => state.maps.orderDirection,
|
|
maps: state => state.maps.list,
|
|
mapsMetadata: state => state.maps.metadata,
|
|
isFetchingMaps: state => state.maps.isFetching,
|
|
currentEntriesCount: state => state.maps.metadata.total_entries,
|
|
filterType: state => state.maps.filterType,
|
|
totalUserEntries: state => state.maps.metadata.total_user_entries,
|
|
totalShared: state => state.maps.metadata.total_shared,
|
|
isFirstTimeViewingDashboard: state => state.config.isFirstTimeViewingDashboard,
|
|
upgradeUrl: state => state.config.upgrade_url
|
|
}),
|
|
...mapGetters({
|
|
publicMapsQuota: 'user/publicMapsQuota',
|
|
publicMapsCount: 'user/publicMapsCount',
|
|
isOutOfPublicMapsQuota: 'user/isOutOfPublicMapsQuota'
|
|
}),
|
|
areAllMapsSelected () {
|
|
return Object.keys(this.maps).length === this.selectedMaps.length;
|
|
},
|
|
initialState () {
|
|
return this.isFirstTimeViewingDashboard &&
|
|
!this.hasSharedMaps &&
|
|
!this.isFetchingMaps &&
|
|
this.hasFilterApplied('mine') &&
|
|
this.totalUserEntries <= 0;
|
|
},
|
|
emptyState () {
|
|
return ((!this.isFirstTimeViewingDashboard || this.hasSharedMaps) || this.isFirstTimeViewerAfterAction) &&
|
|
!this.isFetchingMaps &&
|
|
!this.currentEntriesCount;
|
|
},
|
|
emptyStateText () {
|
|
const route = this.$router.resolve({name: 'maps', params: { filter: 'shared' }});
|
|
|
|
return this.hasSharedMaps
|
|
? this.$t('MapsPage.emptyCase.onlyShared', { path: route.href })
|
|
: this.$t('MapsPage.emptyCase.default', { path: route.href });
|
|
},
|
|
isFirstTimeViewerAfterAction () {
|
|
// First time viewing dashboard but user has performed any action such as drag and dropping a dataset (no page refreshing)
|
|
return this.isFirstTimeViewingDashboard && this.currentEntriesCount <= 0 && !this.hasFilterApplied('mine');
|
|
},
|
|
hasSharedMaps () {
|
|
return this.totalShared > 0;
|
|
},
|
|
isSomeMapSelected () {
|
|
return this.selectedMaps.length > 0;
|
|
},
|
|
shouldShowLimitsWarning () {
|
|
return !this.selectedMaps.length && this.isOutOfPublicMapsQuota;
|
|
},
|
|
shouldShowViewSwitcher () {
|
|
return this.canChangeViewMode && !this.initialState && !this.emptyState && !this.selectedMaps.length;
|
|
},
|
|
shouldShowListHeader () {
|
|
return this.isCondensed && !this.emptyState && !this.initialState;
|
|
},
|
|
isViewer () {
|
|
return this.$store.getters['user/isViewer'];
|
|
},
|
|
isNotificationVisible () {
|
|
return this.$store.getters['user/isNotificationVisible'];
|
|
}
|
|
},
|
|
methods: {
|
|
fetchMaps () {
|
|
this.$store.dispatch('maps/fetch');
|
|
},
|
|
applyFilter (filter) {
|
|
this.$emit('applyFilter', filter);
|
|
},
|
|
applyOrder (orderParams) {
|
|
this.$emit('applyOrder', orderParams);
|
|
},
|
|
toggleSelected ({ map, isSelected, event }) {
|
|
if (this.selectedMaps.length && event.shiftKey) {
|
|
this.doShiftClick(map);
|
|
return;
|
|
}
|
|
|
|
if (isSelected) {
|
|
this.lastCheckedItem = map;
|
|
this.selectedMaps.push(map);
|
|
return;
|
|
}
|
|
|
|
this.selectedMaps = this.selectedMaps.filter(selectedMap => selectedMap.id !== map.id);
|
|
},
|
|
doShiftClick (map) {
|
|
const mapsArray = [...Object.values(this.maps)];
|
|
this.selectedMaps = shiftClick(mapsArray, this.selectedMaps, map, this.lastCheckedItem || map);
|
|
},
|
|
selectAll () {
|
|
this.selectedMaps = [...Object.values(this.$store.state.maps.list)];
|
|
},
|
|
deselectAll () {
|
|
this.selectedMaps = [];
|
|
},
|
|
isMapSelected (map) {
|
|
return this.selectedMaps.some(selectedMap => selectedMap.id === map.id);
|
|
},
|
|
hasFilterApplied (filter) {
|
|
return this.filterType === filter;
|
|
},
|
|
toggleViewMode () {
|
|
this.isCondensed = !this.isCondensed;
|
|
},
|
|
getHeaderContainer () {
|
|
return this.$refs.headerContainer;
|
|
},
|
|
onContentChanged (type) {
|
|
this.$emit('contentChanged', type);
|
|
}
|
|
},
|
|
watch: {
|
|
isCondensed (isCompactMapView) {
|
|
if (isCompactMapView) {
|
|
localStorage.mapViewMode = 'compact';
|
|
} else {
|
|
localStorage.mapViewMode = 'standard';
|
|
}
|
|
},
|
|
selectedMaps () {
|
|
this.$emit('selectionChange', this.selectedMaps);
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import 'new-dashboard/styles/variables';
|
|
|
|
.map-element {
|
|
margin-bottom: 36px;
|
|
}
|
|
|
|
.full-width {
|
|
width: 100%;
|
|
}
|
|
|
|
.grid__head--sticky {
|
|
top: $header__height;
|
|
}
|
|
|
|
.grid__head--sticky.has-user-notification {
|
|
top: $header__height + $notification-warning__height;
|
|
}
|
|
|
|
.pagination-element {
|
|
margin-top: 28px;
|
|
}
|
|
|
|
.empty-state {
|
|
margin: 20vh 0 8vh;
|
|
}
|
|
|
|
.grid-column {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.card-condensed {
|
|
width: 100%;
|
|
border-bottom: 1px solid #EBEEF5;
|
|
|
|
&:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.mapcard-view-mode {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 38px;
|
|
height: 36px;
|
|
margin-left: 24px;
|
|
padding: 9px;
|
|
cursor: pointer;
|
|
|
|
&:hover,
|
|
&:focus {
|
|
background-color: $softblue;
|
|
}
|
|
|
|
&:active {
|
|
background-color: $primary-color;
|
|
|
|
.svgicon {
|
|
fill: $white;
|
|
}
|
|
}
|
|
}
|
|
|
|
.warning {
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|