Skip to content

Commit

Permalink
ui: Redesign main drawer contents
Browse files Browse the repository at this point in the history
  • Loading branch information
annehaley committed Oct 1, 2024
1 parent e7b29bb commit 13f00ea
Show file tree
Hide file tree
Showing 5 changed files with 527 additions and 306 deletions.
34 changes: 7 additions & 27 deletions web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import MainDrawerContents from "./components/MainDrawerContents.vue";
import OptionsDrawerContents from "./components/OptionsDrawerContents.vue";
import ChartJS from "./components/ChartJS.vue";
import SimulationsPanel from "./components/SimulationsPanel.vue";
import ProjectConfig from "./components/ProjectConfig.vue";
export default defineComponent({
components: {
Expand All @@ -25,10 +24,9 @@ export default defineComponent({
OptionsDrawerContents,
ChartJS,
SimulationsPanel,
ProjectConfig,
},
setup() {
const drawer = ref(false);
const drawer = ref(true);
const showError = computed(() => currentError.value !== undefined);
function onReady() {
Expand All @@ -43,9 +41,6 @@ export default defineComponent({
onMounted(onReady);
watch(currentUser, onReady);
watch(currentProject, () => {
drawer.value = currentProject.value !== undefined;
});
return {
login,
Expand Down Expand Up @@ -99,23 +94,9 @@ export default defineComponent({
</v-card>
</v-overlay>
<v-app-bar app prominent>
<v-app-bar-nav-icon
v-if="currentProject"
@click.stop="drawer = !drawer"
/>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
<v-toolbar-title>UVDAT</v-toolbar-title>
<v-spacer />
<ProjectConfig />
<v-select
label="Select Project"
v-model="currentProject"
:items="availableProjects"
item-title="name"
density="compact"
return-object
style="margin-top: 15px"
/>
<v-spacer />
<div v-if="currentUser" class="px-3">
{{ currentUser.first_name }}
<v-btn icon>
Expand All @@ -133,10 +114,9 @@ export default defineComponent({
</div>
</v-app-bar>
<v-navigation-drawer
v-if="currentProject"
v-model="drawer"
permanent
width="300"
width="350"
class="main-area drawer"
>
<MainDrawerContents />
Expand Down Expand Up @@ -177,12 +157,12 @@ export default defineComponent({
left: 250px;
}
.shifted-1 {
left: 300px;
width: calc(100% - 300px);
left: 350px;
width: calc(100% - 350px);
}
.shifted-2 {
left: 300px;
left: 350px;
right: 300px;
width: calc(100% - 600px);
width: calc(100% - 650px);
}
</style>
296 changes: 296 additions & 0 deletions web/src/components/DatasetList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { onMounted, ref, Ref, computed } from "vue";
import { Dataset } from "@/types";
import { selectedDatasets } from "../store";
import { getDatasetLayers } from "@/api/rest";
export default {
props: {
datasets: {
required: true,
type: Array,
},
detail: {
default: true,
type: Boolean,
},
},
setup(props: any) {
const groupByOptions = ["category", "dataset_type"];
const groupByKey = ref(["category"]);
const filterMenuItems: Ref<Record<string, string[]>> = ref({});
const filters: Ref<Record<string, string[]>> = ref({});
const searchText = ref();
const selectedIds = computed(() =>
selectedDatasets.value.map((d2) => d2.id)
);
const filteredDatasets = computed(() => {
return props.datasets.filter((dataset: any) => {
const searchMatch =
!searchText.value ||
dataset.name.toLowerCase().includes(searchText.value.toLowerCase());
const filterMatch = Object.entries(filters.value).every(
([filter_type, values]) => {
return (
!values.length ||
values
.map((v) => v.toLowerCase())
.includes(dataset[filter_type].toLowerCase())
);
}
);
return searchMatch && filterMatch;
});
});
const datasetGroups = computed(() => {
const groups: Record<string, Dataset[]> = {};
filteredDatasets.value.forEach((dataset: any) => {
const groupName = dataset[groupByKey.value[0]];
if (!groups[groupName]) {
groups[groupName] = [];
}
groups[groupName].push(dataset);
});
return groups;
});
function fetchAllLayers() {
props.datasets.forEach(async (dataset: Dataset) => {
dataset.map_layers = await getDatasetLayers(dataset.id);
});
}
function populateFilterMenu() {
filterMenuItems.value = Object.fromEntries(
groupByOptions.map((opt) => [opt, []])
);
filters.value = Object.fromEntries(
groupByOptions.map((opt) => [opt, []])
);
props.datasets.forEach((dataset: any) => {
groupByOptions.forEach((opt) => {
const value = dataset[opt].toLowerCase();
if (!filterMenuItems.value[opt].includes(value)) {
filterMenuItems.value[opt].push(value);
}
});
});
}
function toggleDatasets(show: unknown, datasets: Dataset[]) {
datasets.forEach((dataset) => {
if (show && !selectedIds.value.includes(dataset.id)) {
selectedDatasets.value.push(dataset);
} else if (!show) {
selectedDatasets.value = selectedDatasets.value.filter((d) => {
d.id != dataset.id;
});
}
});
// TODO: Pair program with Jake
// Update map layers with selected datasets
}
onMounted(fetchAllLayers);
onMounted(populateFilterMenu);
return {
groupByOptions,
groupByKey,
filterMenuItems,
filters,
datasetGroups,
selectedIds,
searchText,
toggleDatasets,
};
},
};
</script>

<template>
<div class="px-1">
<div class="d-flex" style="align-items: center">
<v-text-field
v-model="searchText"
label="Search Datasets"
variant="outlined"
density="compact"
class="ml-3"
append-inner-icon="mdi-magnify"
hide-details
/>
<v-icon icon="mdi-filter" class="mx-1" id="filter-datasets" />
<v-menu
activator="#filter-datasets"
open-on-hover
:close-on-content-click="false"
>
<v-card>
<v-card-text style="background-color: lightgrey" class="py-2">
Group by
</v-card-text>
<v-list
density="compact"
selectable
mandatory
select-strategy="single-leaf"
v-model:selected="groupByKey"
>
<v-list-item
v-for="option in groupByOptions"
:key="option"
:value="option"
>
<v-list-item-title class="capitalize">
{{ option.replace("_", " ") }}
</v-list-item-title>
<template v-slot:append="{ isSelected }">
<v-checkbox
:model-value="isSelected"
density="compact"
class="ml-4"
style="vertical-align: bottom"
hide-details
/>
</template>
</v-list-item>
</v-list>
<v-card-text style="background-color: lightgrey" class="py-2">
Filter by
</v-card-text>
<v-list density="compact">
<v-list-item
v-for="option in Object.keys(filterMenuItems)"
:key="option"
>
<v-list-item-title class="capitalize">
{{ option.replace("_", " ") }}
</v-list-item-title>
<template v-slot:append>
<v-icon icon="mdi-menu-right" size="x-small"></v-icon>
</template>

<v-menu
activator="parent"
location="end"
open-on-hover
:close-on-content-click="false"
>
<v-list
density="compact"
selectable
select-strategy="leaf"
v-model:selected="filters[option]"
>
<v-list-item
v-for="value in filterMenuItems[option]"
:key="value"
:value="value"
>
<v-list-item-title class="capitalize">
{{ value }}
</v-list-item-title>
<template v-slot:append="{ isSelected }">
<v-checkbox
:model-value="isSelected"
density="compact"
class="ml-4"
style="vertical-align: bottom"
hide-details
/>
</template>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</div>
<div
v-for="groupName in Object.keys(datasetGroups)"
:key="groupName"
class="group py-1"
>
<v-checkbox
true-icon="mdi-eye-outline"
false-icon="mdi-eye-off-outline"
style="display: inline-block"
density="compact"
hide-details
:model-value="
datasetGroups[groupName].every((d) => selectedIds.includes(d.id))
"
@update:model-value="(v) => toggleDatasets(v, datasetGroups[groupName])"
/>
<v-card-subtitle class="group-title capitalize text-caption px-1">
{{ groupByKey[0].replace("_", " ") }}: {{ groupName.toLowerCase() }}
</v-card-subtitle>
<div
v-for="dataset in datasetGroups[groupName]"
:key="dataset.id"
class="d-flex pb-3"
style="flex-direction: column"
>
<div class="d-flex" style="column-gap: 5px; align-items: center">
<v-checkbox
true-icon="mdi-eye-outline"
false-icon="mdi-eye-off-outline"
style="display: inline-block"
density="compact"
hide-details
:model-value="selectedIds.includes(dataset.id)"
@update:model-value="(v) => toggleDatasets(v, [dataset])"
/>
{{ dataset.name }}
</div>
<div
v-if="detail"
class="d-flex pl-7 pr-3"
style="column-gap: 5px; justify-content: space-between"
>
<div>
<v-chip color="primary" size="x-small" class="mr-1 capitalize">
category: {{ dataset.category }}
</v-chip>
<v-chip color="primary" size="x-small" class="capitalize">
type: {{ dataset.dataset_type.toLowerCase() }}
</v-chip>
</div>
<span
v-if="dataset.map_layers"
class="text-caption"
style="color: grey"
>
<v-icon icon="mdi-layers-outline" size="small" />
{{ dataset.map_layers.length }}
<v-tooltip activator="parent" location="end">
Number of Layers
</v-tooltip>
</span>
</div>
<div v-if="detail" class="text-caption pl-8 pr-1" style="color: grey">
{{ dataset.description }}
</div>
</div>
</div>
</div>
</template>

<style scoped>
.group {
border-bottom: 1px solid lightgray;
}
.group-title {
display: inline-block !important;
vertical-align: super !important;
}
.v-input--density-compact {
--v-input-control-height: 10px;
}
.capitalize {
text-transform: capitalize !important;
}
</style>
Loading

0 comments on commit 13f00ea

Please sign in to comment.