filter shenanigans

This commit is contained in:
2024-07-15 18:14:02 +02:00
parent be9cffef4f
commit 3aec40658f
5 changed files with 277 additions and 10 deletions
+29 -1
View File
@@ -4,13 +4,26 @@
<div class="current-eorzea-time">
{{ eorzeaTime.getPrettyTime() }}
</div>
<div>
<button
class="filters-button"
:class="{ active: filtersActive}"
@click="filtersActive = !filtersActive"
>
{{ filtersActive ? ' Close filters' : 'Open filters' }}
</button>
</div>
</nav>
<main>
<SortedNodeList
v-if="!filtersActive"
:nodes="nodes as Node[]"
:zones="zones"
:eorzea-time="eorzeaTime"
/>
<FiltersMenu
v-if="filtersActive"
/>
</main>
</div>
</template>
@@ -26,16 +39,20 @@ import {nodeTypeFromString} from "@/enums/NodeType";
import SortedNodeList from "@/components/SortedNodeList.vue";
import TimeRange from "@/entities/TimeRange";
import Zone from "@/entities/Zone";
import FiltersMenu from "@/components/FiltersMenu.vue";
import Filters from "@/util/Filters";
export default defineComponent({
name: 'App',
components: {SortedNodeList},
components: {FiltersMenu, SortedNodeList},
data: () => ({
eorzeaTime: new EorzeaTime() as EorzeaTime,
nodes: [] as Node[],
aetherytes: [] as Aetheryte[],
items: {} as { [key: string]: Item },
zones: {} as { [key: string]: Zone },
filtersActive: false,
filters: new Filters(),
}),
methods: {
findNearestAetheryte(zone: string, x: number, y: number): Aetheryte | null {
@@ -172,5 +189,16 @@ nav {
.current-eorzea-time {
font-size: 3rem;
}
.filters-button {
display: flex;
padding: 0.5rem;
border: 1px solid #fff;
background-color: transparent;
cursor: pointer;
&.active {
background-color: rgba(255, 255, 255, 0.1);
}
}
}
</style>
+181
View File
@@ -0,0 +1,181 @@
<template>
<section>
<details open>
<summary>Level of items</summary>
<section>
<label>
<span>Minimum level</span>
<input
type="number"
placeholder="1"
:min="1"
:max="filters.maxLevel"
v-model="filters.minLevel"
@focusout="(e: FocusEvent) => {
const target = e.target as HTMLInputElement;
const numberValue = parseInt(target.value);
if (filters.maxLevel && numberValue > filters.maxLevel) {
filters.maxLevel = filters.minLevel;
}
}"
>
</label>
<label>
<span>Maximum level</span>
<input
type="number"
placeholder="100"
:min="filters.minLevel"
:max="100"
v-model="filters.maxLevel"
@focusout="(e: FocusEvent) => {
const target = e.target as HTMLInputElement;
const numberValue = parseInt(target.value);
if (filters.minLevel && numberValue < filters.minLevel) {
filters.minLevel = filters.maxLevel;
}
}"
>
</label>
</section>
</details>
<details open>
<summary>Jobs</summary>
<section>
<label class="horizontal">
<span>Botanist</span>
<input
type="checkbox"
:checked="filters.jobs.includes(Job.BOTANIST)"
@change="(e: Event) => {
const target = e.target as HTMLInputElement;
if (target.checked) {
filters.jobs.push(Job.BOTANIST);
} else {
filters.jobs = filters.jobs.filter((job) => job !== Job.BOTANIST);
}
}"
>
</label>
<label class="horizontal">
<span>Miner</span>
<input
type="checkbox"
:checked="filters.jobs.includes(Job.MINER)"
@change="(e: Event) => {
const target = e.target as HTMLInputElement;
if (target.checked) {
filters.jobs.push(Job.MINER);
} else {
filters.jobs = filters.jobs.filter((job) => job !== Job.MINER);
}
}"
>
</label>
</section>
</details>
</section>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import Filters from "@/util/Filters";
import {Job} from "@/enums/Job";
export default defineComponent({
name: "FiltersMenu",
computed: {
Job() {
return Job
}
},
emits: ['update:filters'],
data: () => ({
filters: {
minLevel: undefined,
maxLevel: undefined,
jobs: [] as Job[],
},
}),
watch: {
filters: {
handler(newFilters) {
const filters = new Filters(newFilters);
window.localStorage.setItem("filters", JSON.stringify(filters));
},
deep: true,
},
minLevel(newValue: string) {
const numberValue = parseInt(newValue);
return isNaN(numberValue) ? 1 : numberValue;
},
maxLevel(newValue: string) {
const numberValue = parseInt(newValue);
return isNaN(numberValue) ? 100 : numberValue;
},
},
mounted() {
const filters = window.localStorage.getItem("filters");
if (filters) {
this.filters = JSON.parse(filters);
}
},
});
</script>
<style scoped lang="scss">
section {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
details {
background-color: #1f1f1f;
display: flex;
flex-direction: column;
padding: 0.5rem;
gap: 0.5rem;
summary {
cursor: pointer;
color: white;
border: none;
border-radius: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.25rem;
}
section {
display: flex;
flex-direction: row;
gap: 2rem;
flex-wrap: wrap;
label {
display: flex;
gap: 0.5rem;
flex-direction: column;
&.horizontal {
flex-direction: row;
gap: 0.2rem;
}
input[type="number"] {
padding: 0.5rem;
border: 1px solid #fff;
background-color: transparent;
color: white;
border-radius: 0.25rem;
width: 5rem;
text-align: start;
}
}
}
}
}
</style>
+4 -1
View File
@@ -1,5 +1,8 @@
<template>
<article class="node" :class="{active: gatheringNode.isActive(eorzeaTime)}">
<article
class="node"
:class="{active: gatheringNode.isActive(eorzeaTime)}"
>
<div class="timer">
{{
gatheringNode.isActive(eorzeaTime) ? 'Active' : prettyTimer(gatheringNode.getSecondsToNextActiveTime(eorzeaTime))
+35 -8
View File
@@ -16,6 +16,7 @@ import EorzeaTime from "../util/EorzeaTime";
import Node from "@/entities/Node";
import GatheringNode from "@/components/GatheringNode.vue";
import Zone from "@/entities/Zone";
import Filters from "@/util/Filters";
export default defineComponent(
{
@@ -37,10 +38,10 @@ export default defineComponent(
},
watch: {
nodes: {
immediate: true,
handler() {
this.displayNodes = this.nodes;
}
handler(newNodes: Node[]) {
this.filterNodes(newNodes);
},
deep: true
},
displayNodes: {
handler() {
@@ -48,7 +49,6 @@ export default defineComponent(
}
},
eorzeaTime: {
immediate: true,
handler(newValue, oldValue) {
if (oldValue === undefined) return;
if (newValue?.getMinutes() === oldValue?.getMinutes()) return;
@@ -68,10 +68,37 @@ export default defineComponent(
return aSeconds - bSeconds;
});
},
filterNodes(nodes: Node[] = []) {
let filters: Filters | null = null;
const filtersString = window.localStorage.getItem("filters");
if (filtersString === null) {
this.displayNodes = this.nodes;
return;
}
const parsedFilters = JSON.parse(filtersString);
filters = new Filters(parsedFilters);
this.displayNodes = nodes.filter((node) => {
let shouldDisplay = false;
if (filters && !filters.jobs.includes(node.job)) {
return false;
}
for (const item of node.items) {
if (filters && item.level >= filters.minLevel && item.level <= filters.maxLevel) {
shouldDisplay = true;
break;
}
}
return shouldDisplay;
});
},
},
async mounted() {
this.displayNodes = this.nodes;
this.sortListByTime();
mounted() {
this.filterNodes(this.nodes);
},
}
);
+28
View File
@@ -0,0 +1,28 @@
import {Job, jobFromString} from "@/enums/Job";
export default class Filters {
minLevel: number;
maxLevel: number;
jobs: Job[] = [];
constructor(
data?: {
minLevel?: number,
maxLevel?: number,
jobs?: string[],
},
) {
this.minLevel = data?.minLevel || 1;
this.maxLevel = data?.maxLevel || 100;
const jobData = data?.jobs || [];
for (const job of jobData) {
const parsedJob = jobFromString(job);
if (!parsedJob) continue;
this.jobs.push(parsedJob);
}
}
}