Skip to content

Commit

Permalink
177 map improvements (#199)
Browse files Browse the repository at this point in the history
Add map legend + automatic marker color assignment upon feature
selection
  • Loading branch information
katharinawuensche authored Dec 16, 2024
2 parents 02adf31 + 8b52345 commit f2143ca
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 52 deletions.
6 changes: 5 additions & 1 deletion components/data-table/data-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ const table = useVueTable({
typeof updaterOrValue === "function"
? updaterOrValue(columnVisibility.value)
: updaterOrValue;
emit("columnVisibilityChange", table);
emit("columnVisibilityChange", {
table,
//@ts-expect-error missing optional argument for updaterOrValue()
col: typeof updaterOrValue === "function" ? updaterOrValue() : updaterOrValue,
});
},
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
Expand Down
49 changes: 49 additions & 0 deletions components/geojson-map-legend.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { ChevronDown } from "lucide-vue-next";
import type { GeojsonMapSchema } from "@/types/global";
interface Props {
params: Zod.infer<typeof GeojsonMapSchema>["params"];
}
const props = defineProps<Props>();
const { params } = toRefs(props);
const GeojsonStore = useGeojsonStore();
const { tables } = storeToRefs(GeojsonStore);
const table = computed(() => tables.value.get(params.value.url));
const activeFeatures = computed(() =>
table.value?.getVisibleLeafColumns().filter((col) => col.getCanHide()),
);
const activeRows = computed(() => table.value?.getFilteredRowModel().rows);
function getMatchingRowCount(columnId: string) {
return activeRows.value?.filter((row) => (row.getValue(columnId) as Array<unknown>).length > 0)
.length;
}
const collapsibleOpen = ref(true);
</script>

<template>
<Collapsible v-model:open="collapsibleOpen">
<div class="w-48 bg-white/80 p-3 text-xs">
<CollapsibleTrigger class="flex w-full justify-between"
><b>{{ activeRows?.length }} total markers</b
><ChevronDown class="size-4" :class="collapsibleOpen ? '' : 'rotate-180'"></ChevronDown
></CollapsibleTrigger>
<CollapsibleContent>
<div
v-for="feature in activeFeatures"
:key="feature.id"
class="my-1 flex items-start gap-2"
>
<svg class="mt-0.5 size-3.5">
<use href="#petal" :style="{ fill: `var(--${feature.id})` }"></use>
</svg>
<span>{{ feature.columnDef.header }} ({{ getMatchingRowCount(feature.id) }})</span>
</div></CollapsibleContent
>
</div>
</Collapsible>
</template>
42 changes: 29 additions & 13 deletions components/geojson-map-toolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const isCollapsibleOpen = ref(
),
);
const { colors, addColor, setColor } = useColorsStore();
const { colors, setColor } = useColorsStore();
</script>

<template>
Expand Down Expand Up @@ -61,15 +61,20 @@ const { colors, addColor, setColor } = useColorsStore();
<CollapsibleTrigger
class="flex w-full items-center justify-between gap-1 p-2 text-left text-sm"
>
<span>{{ titleCase(subcategory.id) }}</span>
<Badge
v-if="
subcategory.columns.length > 0 &&
subcategory.getLeafColumns().filter((c) => c.getIsVisible()).length
"
variant="outline"
>{{ subcategory.getLeafColumns().filter((c) => c.getIsVisible()).length }}</Badge
>
<div>
<span>{{ titleCase(subcategory.id) }}</span>
<Badge
v-if="
subcategory.columns.length > 0 &&
subcategory.getLeafColumns().filter((c) => c.getIsVisible()).length
"
class="ml-2"
variant="outline"
>{{
subcategory.getLeafColumns().filter((c) => c.getIsVisible()).length
}}</Badge
>
</div>

<ChevronDown
class="size-4 shrink-0 grow-0"
Expand All @@ -87,14 +92,25 @@ const { colors, addColor, setColor } = useColorsStore();
(value) => {
column.toggleVisibility(!!value);
column.setFilterValue([]);
if (!colors.has(column.id)) addColor(column.id);
}
"
>
<span class="flex-1">{{ column.columnDef.header }}</span>
<label v-if="column.getIsVisible()" class="grow-0 basis-0 p-0">

<label
v-if="column.getIsVisible()"
class="ml-3 flex grow-0 basis-0 items-center p-0"
@click.capture.stop
>
<div
class="size-4 rounded"
:style="{
backgroundColor: `var(--${column.id})`,
stroke: `var(--${column.id})`,
}"
></div>
<input
class="size-5"
class="size-0"
type="color"
:value="colors.get(column.id)?.colorCode || '#cccccc'"
@click.capture.stop
Expand Down
5 changes: 5 additions & 0 deletions components/geojson-map-window-content.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ const filteredMarkers = computed(() => {
<Centered v-if="!filteredMarkers">
<LoadingIndicator />
</Centered>
<GeojsonMapLegend
v-if="filteredMarkers"
class="absolute bottom-0 left-0"
:params="params"
></GeojsonMapLegend>
</VisualisationContainer>
</div>
</template>
88 changes: 50 additions & 38 deletions components/geojson-table-window-content.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,44 +47,49 @@ const columns = computed(() => {
if (col.header in categories) {
subcategoryColumns = Object.entries(
categories[col.header]?.subcategories ?? { [col.header]: categories[col.header]?.title },
).map(([categoryName, categoryLabel]) => {
return columnHelper.group({
header: String(categoryLabel),
id: String(categoryName),
//@ts-expect-error type mismatch in accessorFn
columns: columnHeadings
.filter((heading) => heading.category === categoryName)
.map((heading) => {
return {
id: Object.keys(heading).find((key) => /ft_*/.test(key)) ?? "",
header: heading[Object.keys(heading).find((key) => /ft_*/.test(key)) ?? ""],
cell: (cell: CellContext<FeatureType, never>) => {
return h(resolveComponent("GeojsonTablePropertyCell"), {
value: cell.row.original.properties[cell.column.columnDef.id!],
});
},
accessorFn: (cell: FeatureType) => {
return Object.keys(
cell.properties[
String(Object.keys(heading).find((key) => /ft_*/.test(key)) ?? "")
] ?? {},
);
},
filterFn: (row, columnId, filterValue) => {
if (!row.getVisibleCells().find((cell) => cell.column.id === columnId)) {
return true;
}
if (Object.keys(filterValue).length === 0) return true;
const filter = Object.values(filterValue).some((val) =>
(row.getValue(columnId) as Array<string>).includes(String(val)),
);
return filter;
},
enableGlobalFilter: true,
};
}),
)
.filter(
([categoryName, _]) =>
columnHeadings!.filter((heading) => heading.category === categoryName).length > 0,
)
.map(([categoryName, categoryLabel]) => {
return columnHelper.group({
header: String(categoryLabel),
id: String(categoryName),
//@ts-expect-error type mismatch in accessorFn
columns: columnHeadings
.filter((heading) => heading.category === categoryName)
.map((heading) => {
return {
id: Object.keys(heading).find((key) => /ft_*/.test(key)) ?? "",
header: heading[Object.keys(heading).find((key) => /ft_*/.test(key)) ?? ""],
cell: (cell: CellContext<FeatureType, never>) => {
return h(resolveComponent("GeojsonTablePropertyCell"), {
value: cell.row.original.properties[cell.column.columnDef.id!],
});
},
accessorFn: (cell: FeatureType) => {
return Object.keys(
cell.properties[
String(Object.keys(heading).find((key) => /ft_*/.test(key)) ?? "")
] ?? {},
);
},
filterFn: (row, columnId, filterValue) => {
if (!row.getVisibleCells().find((cell) => cell.column.id === columnId)) {
return true;
}
if (Object.keys(filterValue).length === 0) return true;
const filter = Object.values(filterValue).some((val) =>
(row.getValue(columnId) as Array<string>).includes(String(val)),
);
return filter;
},
enableGlobalFilter: true,
};
}),
});
});
});
} else {
subcategoryColumns = [
columnHelper.group({
Expand Down Expand Up @@ -149,6 +154,13 @@ function applyGlobalFilter(table: Table<FeatureType>) {
table.setGlobalFilter(true);
}
const { colors, addColor } = useColorsStore();
function onVisibilityChange(props: { table: Table<FeatureType>; col: Record<string, boolean> }) {
applyGlobalFilter(props.table);
const changedColumnKey = Object.keys(props.col)[0]!;
const visibilityValue = props.col[changedColumnKey]!;
if (visibilityValue && !colors.has(changedColumnKey)) addColor(changedColumnKey);
}
function registerTable(table: Table<FeatureType>) {
tables.value.set(url, table);
const mw = findWindowByTypeAndParam("GeojsonMap", "url", url);
Expand Down Expand Up @@ -202,7 +214,7 @@ function registerTable(table: Table<FeatureType>) {
:initial-column-visibility="columnVisibility"
:items="fetchedData.get(url)?.features as Array<never>"
:min-header-depth="2"
@column-visibility-change="applyGlobalFilter"
@column-visibility-change="onVisibilityChange"
@table-ready="registerTable"
></DataTable>
<div class="grid justify-items-end py-2">
Expand Down

0 comments on commit f2143ca

Please sign in to comment.