Compare commits

..

No commits in common. "4ed7c8062a87db93f444a4894df40cf1dc33510d" and "a23e3c2e609d0d48f90258bd58ce8f6827c34561" have entirely different histories.

13 changed files with 160 additions and 729 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -11,7 +11,7 @@
)
</script>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<title>Path of Exile 2 Loot Filter Config</title>
<title>Vite App</title>
</head>
<body>

View File

@ -13,7 +13,6 @@
"format": "prettier --write src/"
},
"dependencies": {
"file-saver": "^2.0.5",
"fp-ts": "^2.16.9",
"pinia": "^2.3.0",
"primeicons": "^7.0.0",
@ -25,7 +24,6 @@
"devDependencies": {
"@tailwindcss/typography": "^0.5.15",
"@tsconfig/node22": "^22.0.0",
"@types/file-saver": "^2.0.7",
"@types/node": "^22.10.2",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^10.1.0",

View File

@ -1,22 +1,21 @@
<script setup lang="ts">
import { Button, Dialog, ConfirmPopup, Toast, Splitter, SplitterPanel, ScrollPanel, useToast } from 'primevue'
import { Button, Splitter, SplitterPanel } from 'primevue'
import TreeNav from './components/TreeNav.vue'
import FilterDetail from './components/FilterDetail.vue'
import Info from './components/Info.vue'
import { computed, ref, toRaw, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import type { TreeNode } from 'primevue/treenode'
import * as O from 'fp-ts/Option'
import * as A from 'fp-ts/lib/Array';
import { pipe } from 'fp-ts/function'
import FileSaver from 'file-saver'
import { generateFilterText, type Filter, type FilterConfig, type FilterNode } from './models'
import { filterToTreeNode, treeNodeToFilter } from './services/filter'
import * as uuid from 'uuid';
import type { Filter, FilterConfig, FilterNode } from './models'
import { filterToTreeNode } from './services/filter'
const darkMode = ref(document.documentElement.classList.contains('dark'))
const icon = computed(() => `pi pi-${darkMode.value ? "moon" : "sun"}`)
const toast = useToast()
watch(darkMode, (dark) => {
document.documentElement.classList.toggle(
'dark',
@ -25,11 +24,19 @@ watch(darkMode, (dark) => {
localStorage.theme = dark ? 'dark' : 'light'
})
let defaultFilters: Filter[] = []
let defaultFilters: Filter[] = [
{ type: 'leaf', id: uuid.v4(), name: "", show: true, enabled: true, rule: {} },
{
type: 'group', id: uuid.v4(), name: "", enabled: true, rule: {}, filters: [
{ type: 'leaf', id: uuid.v4(), name: "", show: true, enabled: true, rule: {} }
]
}
]
const nodes = ref(pipe(
localStorage.getItem("filters"),
O.fromNullable,
O.map((value): Filter[] => JSON.parse(value)),
O.map((value): FilterConfig => JSON.parse(value)),
O.map(value => value.filters),
O.getOrElse(() => defaultFilters),
A.map(filterToTreeNode)
))
@ -46,61 +53,17 @@ function onDelete() {
selectedFilter.value = undefined
}
const previewText = ref()
function exportFilter() {
let text = generateFilterText(toRaw(nodes.value).map(treeNodeToFilter))
console.log(text)
previewText.value = text
exportDialogVisible.value = true;
}
function download() {
var blob = new Blob([previewText.value], { type: "text/plain;charset=utf-8" });
FileSaver.saveAs(blob, "poe2.filter")
}
const exportDialogVisible = ref(false);
function save() {
if (nodes.value) {
const filters = toRaw(nodes.value).map(treeNodeToFilter)
localStorage.setItem("filters", JSON.stringify(filters))
}
}
setInterval(save, 5000)
</script>
<template>
<ConfirmPopup />
<Toast />
<div class="flex flex-row gap-2 items-center flex-grow-0">
<article class="prose dark:prose-invert p-4">
<h1>Path of Exile 2 Loot Filter Config</h1>
<h1>POE2 Loot Filter Config</h1>
</article><Button :icon @click="darkMode = !darkMode" severity="secondary" variant="outlined" rounded />
<Button icon="pi pi-save" label="Save" severity="secondary" variant="outlined" rounded
@click="save(); toast.add({ severity: 'success', summary: 'Success', detail: 'Content Saved', life: 3000 });" />
<Button icon="pi pi-file-export" label="Export" severity="primary" @click="exportFilter" />
<Dialog v-model:visible="exportDialogVisible" modal header="Preview Filter Text">
<template #default>
<ScrollPanel class="h-[50vh]">
<pre>{{ previewText }}</pre>
</ScrollPanel>
</template>
<template #footer>
<Button type="button" label="Cancel" severity="secondary" @click="exportDialogVisible = false"></Button>
<Button type="button" icon="pi pi-download" label="Download" @click="download"></Button>
</template>
</Dialog>
<!-- <Button icon = "pi pi-save" severity="secondary" variant="outlined" rounded /> -->
</div>
<Splitter class="flex-1 min-h-0">
<SplitterPanel class="flex flex-col">
<TreeNav :nodes :selectedNode="selectedFilter" @nodeSelect="selectedFilter = $event"
@nodeUnselect="selectedFilter = undefined" />
<TreeNav :nodes @nodeSelect="selectedFilter = $event" @nodeUnselect="selectedFilter = undefined" />
</SplitterPanel>
<SplitterPanel class="flex flex-col">
<FilterDetail v-if="selectedFilter" :node="selectedFilter" @delete="onDelete" />

View File

@ -1,37 +1,7 @@
<script setup lang="ts">
import { toLines, type Filter, type FilterNode, type FilterRule } from '@/models';
import type { Filter, FilterNode } from '@/models';
import { computed, ref, watchEffect } from 'vue'
import { Button, ColorPicker, ScrollPanel, Tabs, Tab, TabPanels, TabList, TabPanel, ToggleButton, InputText, InputNumber, Select, ToggleSwitch, MultiSelect } from 'primevue'
import { useConfirm } from "primevue/useconfirm";
import { useToast } from "primevue/usetoast";
const confirm = useConfirm();
const toast = useToast();
const confirmDelete = (event: any) => {
const message = props.node.data.type === 'group' ? 'Do you want to delete this group and all rules under it?' : 'Do you want to delete this rule?'
confirm.require({
target: event.currentTarget,
message,
icon: 'pi pi-exclamation-triangle',
rejectProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true
},
acceptProps: {
label: 'Delete',
severity: 'danger',
},
accept: () => {
props.onDelete()
toast.add({ severity: 'success', summary: 'Success', detail: 'Content Deleted', life: 3000 });
},
reject: () => {
}
});
};
const props = defineProps<{
node: FilterNode,
onDelete: () => void
@ -86,63 +56,19 @@ const BASE_TYPES = [
'Orb of Transmutation', 'Scroll of Wisdom', 'Portal Scroll'
];
// const search_class = (event) => {
// setTimeout(() => {
// if (!event.query.trim().length) {
// filteredCountries.value = [...countries.value];
// } else {
// filteredCountries.value = countries.value.filter((country) => {
// return country.name.toLowerCase().startsWith(event.query.toLowerCase());
// });
// }
// }, 250);
// }
const filter = computed(() => props.node.data)
const ruleWithLookup = computed(() => {
return calLookup(props.node)
})
type Lookup<T> = {
[K in keyof T]-?: 'none' | 'value' | 'inherit'
}
type RuleLookup = Lookup<FilterRule>;
function calLookup(node: FilterNode): [FilterRule, RuleLookup] {
let childResult = createLookup(node.data.rule)
if (!node.parent) {
return childResult
}
let parentResult = calLookup(node.parent)
return mergeLookup(childResult, parentResult)
}
function createLookup(rule: FilterRule): [FilterRule, RuleLookup] {
return [rule, {
class: rule.class == undefined ? "none" : "value",
base_type: rule.base_type == undefined ? "none" : "value",
area_level: rule.area_level == undefined ? "none" : "value",
drop_level: rule.drop_level == undefined ? "none" : "value",
item_level: rule.item_level == undefined ? "none" : "value",
rarity: rule.rarity == undefined ? "none" : "value",
sockets: rule.sockets == undefined ? "none" : "value",
quality: rule.quality == undefined ? "none" : "value",
stack_size: rule.stack_size == undefined ? "none" : "value",
waystone_tier: rule.waystone_tier == undefined ? "none" : "value",
set_font_size: rule.set_font_size == undefined ? "none" : "value",
set_text_color: rule.set_text_color == undefined ? "none" : "value",
set_border_color: rule.set_border_color == undefined ? "none" : "value",
set_background_color: rule.set_background_color == undefined ? "none" : "value",
play_alert_sound: rule.play_alert_sound == undefined ? "none" : "value",
play_effect: rule.play_effect == undefined ? "none" : "value",
minimap_icon: rule.minimap_icon == undefined ? "none" : "value"
}]
}
function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]: any): [FilterRule, RuleLookup] {
const value: any = {}
const lookup: any = {}
for (const key in parentLookup) {
if (parentLookup[key] != 'none') {
lookup[key] = 'inherit'
value[key] = parentValue[key]
} else {
lookup[key] = childLookup[key]
value[key] = childValue[key]
}
}
return [value, lookup]
}
</script>
<template>
@ -156,8 +82,8 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
<ToggleButton class="w-24 flex-shrink-0" onIcon="pi pi-eye" offIcon="pi pi-eye-slash" v-if="filter.type === 'leaf'"
v-model="filter.show" onLabel="Show" offLabel="Hide" />
<InputText class="w-full" type="text" :placeholder="toLines(filter.rule).join(' ')" v-model="filter.name" />
<Button class="flex-shrink-0" icon="pi pi-trash" severity="danger" v-on:click="confirmDelete" />
<InputText class="w-full" type="text" :placeholder="filter.id" v-model="filter.name" />
<Button class="flex-shrink-0" icon="pi pi-trash" severity="danger" v-on:click="() => onDelete()" />
</div>
<Tabs value="0" class="flex-1 min-h-0">
@ -180,19 +106,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
Class
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].class != 'inherit'" class="align-middle"
:model-value="filter.rule.class != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.class != undefined"
@update:model-value="filter.rule.class = $event ? [] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11">
<MultiSelect v-if="ruleWithLookup[1].class != 'inherit' && filter.rule.class"
v-model="filter.rule.class" display="chip" :options="CLASSES" filter placeholder="Select Classes"
class="w-full" :maxSelectedLabels=3 fluid />
<MultiSelect disabled v-if="ruleWithLookup[1].class == 'inherit'"
:model-value="ruleWithLookup[0].class" display="chip" :options="CLASSES" filter
placeholder="Select Classes" class="w-full" :maxSelectedLabels=3 fluid />
<MultiSelect v-if="filter.rule.class" v-model="filter.rule.class" display="chip" :options="CLASSES"
filter placeholder="Select Classes" class="w-full" :maxSelectedLabels=3 />
</td>
</tr>
<tr>
@ -200,18 +119,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
BaseType
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].base_type != 'inherit'" class="align-middle"
:model-value="filter.rule.base_type != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.base_type != undefined"
@update:model-value="filter.rule.base_type = $event ? [] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11">
<MultiSelect v-if="ruleWithLookup[1].base_type != 'inherit' && filter.rule.base_type"
v-model="filter.rule.base_type" display="chip" :options="BASE_TYPES" filter
placeholder="Select Base Types" class="w-full" :maxSelectedLabels=3 fluid />
<MultiSelect disabled v-if="ruleWithLookup[1].base_type == 'inherit'"
:model-value="ruleWithLookup[0].base_type" display="chip" :options="BASE_TYPES" filter
placeholder="Select Base Types" class="w-full" :maxSelectedLabels=3 fluid />
<MultiSelect v-if="filter.rule.base_type" v-model="filter.rule.base_type" display="chip"
:options="BASE_TYPES" filter placeholder="Select Base Types" class="w-full" :maxSelectedLabels=3 />
</td>
</tr>
<tr>
@ -219,20 +132,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
Rarity
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].rarity != 'inherit'" class="align-middle"
:model-value="filter.rule.rarity != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.rarity != undefined"
@update:model-value="filter.rule.rarity = $event ? ['=', 'Normal'] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].rarity != 'inherit' && filter.rule.rarity"
v-model="filter.rule.rarity[0]" :options="OPERATORS" />
<Select v-if="ruleWithLookup[1].rarity != 'inherit' && filter.rule.rarity"
v-model="filter.rule.rarity[1]" :options="RARITIES" />
<Select disabled v-if="ruleWithLookup[1].rarity == 'inherit'"
:model-value="ruleWithLookup[0].rarity![0]" :options="OPERATORS" />
<Select disabled v-if="ruleWithLookup[1].rarity == 'inherit'"
:model-value="ruleWithLookup[0].rarity![1]" :options="RARITIES" />
<Select v-if="filter.rule.rarity" v-model="filter.rule.rarity[0]" :options="OPERATORS" />
<Select v-if="filter.rule.rarity" v-model="filter.rule.rarity[1]" :options="RARITIES" />
</td>
</tr>
<tr>
@ -240,20 +145,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
Sockets
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].sockets != 'inherit'" class="align-middle"
:model-value="filter.rule.sockets != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.sockets != undefined"
@update:model-value="filter.rule.sockets = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].sockets != 'inherit' && filter.rule.sockets"
v-model="filter.rule.sockets[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].sockets != 'inherit' && filter.rule.sockets"
v-model="filter.rule.sockets[1]" />
<Select disabled v-if="ruleWithLookup[1].sockets == 'inherit'"
:model-value="ruleWithLookup[0].sockets![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].sockets == 'inherit'"
:model-value="ruleWithLookup[0].sockets![1]" />
<Select v-if="filter.rule.sockets" v-model="filter.rule.sockets[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.sockets" v-model="filter.rule.sockets[1]" />
</td>
</tr>
<tr>
@ -261,20 +158,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
Quality
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].quality != 'inherit'" class="align-middle"
:model-value="filter.rule.quality != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.quality != undefined"
@update:model-value="filter.rule.quality = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].quality != 'inherit' && filter.rule.quality"
v-model="filter.rule.quality[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].quality != 'inherit' && filter.rule.quality"
v-model="filter.rule.quality[1]" />
<Select disabled v-if="ruleWithLookup[1].quality == 'inherit'"
:model-value="ruleWithLookup[0].quality![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].quality == 'inherit'"
:model-value="ruleWithLookup[0].quality![1]" />
<Select v-if="filter.rule.quality" v-model="filter.rule.quality[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.quality" v-model="filter.rule.quality[1]" />
</td>
</tr>
<tr>
@ -282,21 +171,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
StackSize
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].stack_size != 'inherit'" class="align-middle"
:model-value="filter.rule.stack_size != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.stack_size != undefined"
@update:model-value="filter.rule.stack_size = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].stack_size != 'inherit' && filter.rule.stack_size"
v-model="filter.rule.stack_size[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].stack_size != 'inherit' && filter.rule.stack_size"
v-model="filter.rule.stack_size[1]" />
<Select disabled v-if="ruleWithLookup[1].stack_size == 'inherit'"
:model-value="ruleWithLookup[0].stack_size![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].stack_size == 'inherit'"
:model-value="ruleWithLookup[0].stack_size![1]" />
<Select v-if="filter.rule.stack_size" v-model="filter.rule.stack_size[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.stack_size" v-model="filter.rule.stack_size[1]" />
</td>
</tr>
<tr>
@ -304,21 +184,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
AreaLevel
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].area_level != 'inherit'" class="align-middle"
:model-value="filter.rule.area_level != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.area_level != undefined"
@update:model-value="filter.rule.area_level = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].area_level != 'inherit' && filter.rule.area_level"
v-model="filter.rule.area_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].area_level != 'inherit' && filter.rule.area_level"
v-model="filter.rule.area_level[1]" />
<Select disabled v-if="ruleWithLookup[1].area_level == 'inherit'"
:model-value="ruleWithLookup[0].area_level![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].area_level == 'inherit'"
:model-value="ruleWithLookup[0].area_level![1]" />
<Select v-if="filter.rule.area_level" v-model="filter.rule.area_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.area_level" v-model="filter.rule.area_level[1]" />
</td>
</tr>
<tr>
@ -326,21 +197,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
DropLevel
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].drop_level != 'inherit'" class="align-middle"
:model-value="filter.rule.drop_level != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.drop_level != undefined"
@update:model-value="filter.rule.drop_level = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].drop_level != 'inherit' && filter.rule.drop_level"
v-model="filter.rule.drop_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].drop_level != 'inherit' && filter.rule.drop_level"
v-model="filter.rule.drop_level[1]" />
<Select disabled v-if="ruleWithLookup[1].drop_level == 'inherit'"
:model-value="ruleWithLookup[0].drop_level![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].drop_level == 'inherit'"
:model-value="ruleWithLookup[0].drop_level![1]" />
<Select v-if="filter.rule.drop_level" v-model="filter.rule.drop_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.drop_level" v-model="filter.rule.drop_level[1]" />
</td>
</tr>
<tr>
@ -348,21 +210,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
ItemLevel
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].item_level != 'inherit'" class="align-middle"
:model-value="filter.rule.item_level != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.item_level != undefined"
@update:model-value="filter.rule.item_level = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].item_level != 'inherit' && filter.rule.item_level"
v-model="filter.rule.item_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].item_level != 'inherit' && filter.rule.item_level"
v-model="filter.rule.item_level[1]" />
<Select disabled v-if="ruleWithLookup[1].item_level == 'inherit'"
:model-value="ruleWithLookup[0].item_level![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].item_level == 'inherit'"
:model-value="ruleWithLookup[0].item_level![1]" />
<Select v-if="filter.rule.item_level" v-model="filter.rule.item_level[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.item_level" v-model="filter.rule.item_level[1]" />
</td>
</tr>
<tr>
@ -370,21 +223,14 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
WaystoneTier
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].waystone_tier != 'inherit'" class="align-middle"
:model-value="filter.rule.waystone_tier != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.waystone_tier != undefined"
@update:model-value="filter.rule.waystone_tier = $event ? ['>', 0] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].waystone_tier != 'inherit' && filter.rule.waystone_tier"
v-model="filter.rule.waystone_tier[0]" :options="OPERATORS" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].waystone_tier != 'inherit' && filter.rule.waystone_tier"
<Select v-if="filter.rule.waystone_tier" v-model="filter.rule.waystone_tier[0]"
:options="OPERATORS" />
<InputNumber class="w-16" fluid v-if="filter.rule.waystone_tier"
v-model="filter.rule.waystone_tier[1]" />
<Select disabled v-if="ruleWithLookup[1].waystone_tier == 'inherit'"
:model-value="ruleWithLookup[0].waystone_tier![0]" :options="OPERATORS" />
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].waystone_tier == 'inherit'"
:model-value="ruleWithLookup[0].waystone_tier![1]" />
</td>
</tr>
</tbody>
@ -404,18 +250,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
SetFontSize
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].set_font_size != 'inherit'" class="align-middle"
:model-value="filter.rule.set_font_size != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.set_font_size != undefined"
@update:model-value="filter.rule.set_font_size = $event ? 40 : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11">
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].set_font_size != 'inherit' && filter.rule.set_font_size"
<InputNumber class="w-16" fluid v-if="filter.rule.set_font_size"
v-model="filter.rule.set_font_size" />
<InputNumber class="w-16" fluid disabled v-if="ruleWithLookup[1].set_font_size == 'inherit'"
:model-value="ruleWithLookup[0].set_font_size" />
</td>
</tr>
<tr>
@ -423,18 +263,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
SetTextColor
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].set_text_color != 'inherit'" class="align-middle"
:model-value="filter.rule.set_text_color != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.set_text_color != undefined"
@update:model-value="filter.rule.set_text_color = $event ? { r: 100, g: 102, b: 241 } : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<ColorPicker class="flex items-center" format="rgb"
v-if="ruleWithLookup[1].set_text_color != 'inherit' && filter.rule.set_text_color"
<ColorPicker class="flex items-center" format="rgb" v-if="filter.rule.set_text_color"
v-model="filter.rule.set_text_color" />
<ColorPicker class="flex items-center" format="rgb" disabled
v-if="ruleWithLookup[1].set_text_color == 'inherit'"
:model-value="ruleWithLookup[0].set_text_color" />
</td>
</tr>
<tr>
@ -442,18 +276,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
SetBackgroundColor
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].set_background_color != 'inherit'" class="align-middle"
:model-value="filter.rule.set_background_color != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.set_background_color != undefined"
@update:model-value="filter.rule.set_background_color = $event ? { r: 100, g: 102, b: 241 } : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<ColorPicker class="flex items-center" format="rgb"
v-if="ruleWithLookup[1].set_background_color != 'inherit' && filter.rule.set_background_color"
<ColorPicker class="flex items-center" format="rgb" v-if="filter.rule.set_background_color"
v-model="filter.rule.set_background_color" />
<ColorPicker class="flex items-center" format="rgb" disabled
v-if="ruleWithLookup[1].set_background_color == 'inherit'"
:model-value="ruleWithLookup[0].set_background_color" />
</td>
</tr>
<tr>
@ -461,18 +289,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
SetBorderColor
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].set_border_color != 'inherit'" class="align-middle"
:model-value="filter.rule.set_border_color != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.set_border_color != undefined"
@update:model-value="filter.rule.set_border_color = $event ? { r: 100, g: 102, b: 241 } : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<ColorPicker class="flex items-center" format="rgb"
v-if="ruleWithLookup[1].set_border_color != 'inherit' && filter.rule.set_border_color"
<ColorPicker class="flex items-center" format="rgb" v-if="filter.rule.set_border_color"
v-model="filter.rule.set_border_color" />
<ColorPicker class="flex items-center" format="rgb" disabled
v-if="ruleWithLookup[1].set_border_color == 'inherit'"
:model-value="ruleWithLookup[0].set_border_color" />
</td>
</tr>
<tr>
@ -480,22 +302,14 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
PlayAlertSound
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].play_alert_sound != 'inherit'" class="align-middle"
:model-value="filter.rule.play_alert_sound != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.play_alert_sound != undefined"
@update:model-value="filter.rule.play_alert_sound = $event ? [2, 300] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].play_alert_sound != 'inherit' && filter.rule.play_alert_sound"
<InputNumber class="w-16" fluid v-if="filter.rule.play_alert_sound"
v-model="filter.rule.play_alert_sound[0]" />
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].play_alert_sound != 'inherit' && filter.rule.play_alert_sound"
<InputNumber class="w-16" fluid v-if="filter.rule.play_alert_sound"
v-model="filter.rule.play_alert_sound[1]" />
<InputNumber class="w-16" fluid disabled v-if="ruleWithLookup[1].play_alert_sound == 'inherit'"
:model-value="ruleWithLookup[0].play_alert_sound![0]" />
<InputNumber class="w-16" fluid disabled v-if="ruleWithLookup[1].play_alert_sound == 'inherit'"
:model-value="ruleWithLookup[0].play_alert_sound![1]" />
</td>
</tr>
<tr>
@ -503,25 +317,14 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
MinimapIcon
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].minimap_icon != 'inherit'" class="align-middle"
:model-value="filter.rule.minimap_icon != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.minimap_icon != undefined"
@update:model-value="filter.rule.minimap_icon = $event ? [2, 'White', 'Circle'] : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<InputNumber class="w-16" fluid
v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
<InputNumber class="w-16" fluid v-if="filter.rule.minimap_icon"
v-model="filter.rule.minimap_icon[0]" />
<Select v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
v-model="filter.rule.minimap_icon[1]" :options="COLORS" />
<Select v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
v-model="filter.rule.minimap_icon[2]" :options="SHAPES" />
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
:model-value="ruleWithLookup[0].minimap_icon![0]" />
<Select v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
:model-value="ruleWithLookup[0].minimap_icon![1]" :options="COLORS" />
<Select v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
:model-value="ruleWithLookup[0].minimap_icon![2]" :options="SHAPES" />
<Select v-if="filter.rule.minimap_icon" v-model="filter.rule.minimap_icon[1]" :options="COLORS" />
<Select v-if="filter.rule.minimap_icon" v-model="filter.rule.minimap_icon[2]" :options="SHAPES" />
</td>
</tr>
<tr>
@ -529,16 +332,11 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
PlayEffect
</td>
<td class="pr-4">
<ToggleSwitch v-if="ruleWithLookup[1].play_effect != 'inherit'" class="align-middle"
:model-value="filter.rule.play_effect != undefined"
<ToggleSwitch class="align-middle" :model-value="filter.rule.play_effect != undefined"
@update:model-value="filter.rule.play_effect = $event ? 'White' : undefined" />
<label v-else>Inherit</label>
</td>
<td class="h-11 flex gap-2">
<Select v-if="ruleWithLookup[1].play_effect != 'inherit' && filter.rule.play_effect"
v-model="filter.rule.play_effect" :options="COLORS" />
<Select v-if="ruleWithLookup[1].play_effect == 'inherit'" disabled
:model-value="ruleWithLookup[0].play_effect" :options="COLORS" />
<Select v-if="filter.rule.play_effect" v-model="filter.rule.play_effect" :options="COLORS" />
</td>
</tr>
</tbody>

View File

@ -1,13 +1,12 @@
<script setup lang="ts">
import { defaultGroup, defaultLeaf, toLines, type FilterNode } from '@/models';
import { defaultGroup, defaultLeaf, type FilterNode } from '@/models';
import { filterToTreeNode } from '@/services/filter';
import { Button, Menu, Chip } from 'primevue';
import { Button, Menu } from 'primevue';
import { ref } from 'vue'
const props = defineProps<{
node: FilterNode
}>()
const emit = defineEmits(['nodeSelect'])
const menu = ref();
const items = ref([
{
@ -18,7 +17,6 @@ const items = ref([
icon: 'pi pi-plus-circle',
command: () => {
props.node.children?.push(filterToTreeNode(defaultLeaf(), props.node) as FilterNode);
emit("nodeSelect", props.node.children![props.node.children!.length - 1], props.node)
}
},
{
@ -26,7 +24,6 @@ const items = ref([
icon: 'pi pi-folder-plus',
command: () => {
props.node.children?.push(filterToTreeNode(defaultGroup(), props.node) as FilterNode);
emit("nodeSelect", props.node.children![props.node.children!.length - 1], props.node)
}
}
]
@ -40,18 +37,11 @@ const toggle = (event: MouseEvent) => {
</script>
<template>
<div class="flex items-center w-full flex-1">
<div class="flex-1 flex truncate">
<Chip v-if="!node.data.name" class="text-xs py-1.5 px-2" v-for="line in toLines(node.data.rule)">
<span class="truncate max-w-40">{{ line }}</span>
</Chip>
<label v-else>{{ node.data.name }}</label>
</div>
<div class="flex-shrink-0 size-7 ml-4">
<div v-if="node.data.type === 'group'">
<Button class=" flex-shrink-0 !size-7" icon="pi pi-plus" rounded variant="text" size="small" @click="toggle" />
<Menu ref="menu" :model="items" :popup="true" />
</div>
<div class="flex items-center w-full">
<label class="flex-1">{{ node.data.name || node.data.id }}</label>
<div v-if="node.data.type === 'group'">
<Button class="flex-shrink-0" icon="pi pi-plus" rounded variant="text" size="small" @click="toggle" />
<Menu ref="menu" :model="items" :popup="true" />
</div>
</div>
</template>

View File

@ -1,45 +0,0 @@
## How to use
```
To show this page: deselect the item on the left tree(by clicking it).
```
- Create POE loot filters with GUI.
- Generate the final loot filter files.
- Import them into the game.
You might want to use these references:
- [Official documentation on item filters](https://www.pathofexile.com/item-filter/about)
- [NeverSink PoE2litefilter](https://github.com/NeverSinkDev/NeverSink-PoE2litefilter)
### Features
- The tool is aware of available options to help you edit and prevent mistakes.
- Rules can be grouped into hierarchies, settings on groups will be forced on all descendants.
- The bottom rule has the highest priority. This is the opposite of the official POE file format, where the top rule has the highest priority.
- This is more intuitive, you can organize the rules, for example:
- Hide all normal weapons/armours.
- Show specific ones I'm interested in.
- It generates the official filter texts by reversing the configuration settings.
### Add new rules or groups
- Click the top + sign to add rules or groups.
- Click the + sign on a group to add into the group.
- **Group**: a group that can have partial settings of a rule.
- **End Rule**: a rule that has no descendants. Only this type has a Show or Hide setting.
### Reorder items
- Select an item in the tree view, then click the arrows to reorder items within its direct group.
### Editing
- Select an item on the tree, edit the details on the right panel.
- A default display name is generated, you can manually set the name in the top text input.
- Deleting a group will delete it **and** all the descendants.
- Rules that are turned off will not be included when generating the filter file.
- Turning off a group will exclude it **and** all the descendants.

View File

@ -1,67 +1,10 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ScrollPanel } from 'primevue';
</script>
<template>
<ScrollPanel class="flex-1 min-h-0">
<article class="prose dark:prose-invert px-5 pt-4">
<h2 id="how-to-use">How to use</h2>
<pre><code>To show this page: deselect <span class="hljs-keyword">the</span> <span class="hljs-built_in">item</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">the</span> left tree(<span class="hljs-keyword">by</span> clicking <span class="hljs-keyword">it</span>).
</code></pre>
<ul>
<li>Create POE loot filters with GUI.</li>
<li>Generate the final loot filter files.</li>
<li>Import them into the game.</li>
</ul>
<p>You might want to use these references:</p>
<ul>
<li><a href="https://www.pathofexile.com/item-filter/about">Official documentation on item filters</a></li>
<li><a href="https://github.com/NeverSinkDev/NeverSink-PoE2litefilter">NeverSink PoE2litefilter</a></li>
</ul>
<h3 id="features">Features</h3>
<ul>
<li>The tool is aware of available options to help you edit and prevent mistakes.</li>
<li>Rules can be grouped into hierarchies, settings on groups will be forced on all descendants.</li>
<li>The bottom rule has the highest priority. This is the opposite of the official POE file format, where the
top rule has the highest priority.<ul>
<li>This is more intuitive, you can organize the rules, for example:<ul>
<li>Hide all normal weapons/armours.</li>
<li>Show specific ones I&#39;m interested in.</li>
</ul>
</li>
<li>It generates the official filter texts by reversing the configuration settings.</li>
</ul>
</li>
</ul>
<h3 id="add-new-rules-or-groups">Add new rules or groups</h3>
<ul>
<li>
<p>Click the top + sign to add rules or groups.</p>
<ul>
<li>Click the + sign on a group to add into the group.</li>
</ul>
</li>
<li>
<p><strong>Group</strong>: a group that can have partial settings of a rule.</p>
</li>
<li><strong>End Rule</strong>: a rule that has no descendants. Only this type has a Show or Hide setting.</li>
</ul>
<h3 id="reorder-items">Reorder items</h3>
<ul>
<li>Select an item in the tree view, then click the arrows to reorder items within its direct group.</li>
</ul>
<h3 id="editing">Editing</h3>
<ul>
<li>Select an item on the tree, edit the details on the right panel.</li>
<li>A default display name is generated, you can manually set the name in the top text input.</li>
<li>Deleting a group will delete it <strong>and</strong> all the descendants.</li>
<li>Rules that are turned off will not be included when generating the filter file.<ul>
<li>Turning off a group will exclude it <strong>and</strong> all the descendants.</li>
</ul>
</li>
</ul>
</article>
</ScrollPanel>
<article class="prose dark:prose-invert">
<h2>How to use:</h2>
</article>
</template>

View File

@ -1,15 +1,14 @@
<script setup lang="ts">
import { computed, onUpdated, ref, watch } from 'vue'
import { onUpdated, ref } from 'vue'
import { defaultGroup, defaultLeaf, type Filter, type FilterNode as FilterNodeType } from '../models';
import { Tree, Button, ButtonGroup, Menu, ScrollPanel } from 'primevue'
import { match } from 'ts-pattern';
import FilterNode from './FilterNode.vue';
import { Tree, Button, Menu, ScrollPanel } from 'primevue'
import { match } from 'ts-pattern';
import type { TreeNode } from 'primevue/treenode';
import { filterToTreeNode } from '@/services/filter';
const props = defineProps<{
nodes: FilterNodeType[]
selectedNode?: FilterNodeType
nodes: TreeNode[]
}>()
const emit = defineEmits(['nodeSelect', 'nodeUnselect'])
const selectedKey = ref()
@ -36,9 +35,6 @@ const items = ref([
icon: 'pi pi-plus-circle',
command: () => {
props.nodes.push(filterToTreeNode(defaultLeaf()) as FilterNodeType);
let item = props.nodes[props.nodes.length - 1]
selectedKey.value = { [item.data.id]: true }
emit("nodeSelect", item)
}
},
{
@ -46,9 +42,6 @@ const items = ref([
icon: 'pi pi-folder-plus',
command: () => {
props.nodes.push(filterToTreeNode(defaultGroup()) as FilterNodeType);
let item = props.nodes[props.nodes.length - 1]
selectedKey.value = { [item.data.id]: true }
emit("nodeSelect", item)
}
}
]
@ -60,119 +53,24 @@ const toggle = (event: MouseEvent) => {
menu.value.toggle(event);
};
function moveToTop() {
if (!props.selectedNode || !selectedPosition.value) {
return
}
let index = selectedPosition.value[0]
let list: FilterNodeType[];
if (props.selectedNode.parent) {
list = props.selectedNode.parent.children!
} else {
list = props.nodes
}
list.splice(index, 1)
list.unshift(props.selectedNode)
}
function moveUp() {
if (!props.selectedNode || !selectedPosition.value || selectedPosition.value[0] <= 0) {
return
}
let index = selectedPosition.value[0]
let list: FilterNodeType[];
if (props.selectedNode.parent) {
list = props.selectedNode.parent.children!
} else {
list = props.nodes
}
list[index] = list[index - 1]
list[index - 1] = props.selectedNode
}
function moveDown() {
if (!props.selectedNode || !selectedPosition.value || selectedPosition.value[0] >= selectedPosition.value[1] - 1) {
return
}
let index = selectedPosition.value[0]
let list: FilterNodeType[];
if (props.selectedNode.parent) {
list = props.selectedNode.parent.children!
} else {
list = props.nodes
}
list[index] = list[index + 1]
list[index + 1] = props.selectedNode
}
function moveToBottom() {
if (!props.selectedNode || !selectedPosition.value || selectedPosition.value[0] >= selectedPosition.value[1] - 1) {
return
}
let index = selectedPosition.value[0]
let list: FilterNodeType[];
if (props.selectedNode.parent) {
list = props.selectedNode.parent.children!
} else {
list = props.nodes
}
list.splice(index, 1)
list.push(props.selectedNode)
}
const selectedPosition = computed(() => {
if (!props.selectedNode) {
return undefined
} else {
if (props.selectedNode.parent) {
return [props.selectedNode.parent.children!.findIndex(item => item === props.selectedNode), props.selectedNode.parent.children!.length]
} else {
return [props.nodes.findIndex(item => item === props.selectedNode), props.nodes.length];
}
}
})
function onNodeSelect(node: TreeNode, parent: TreeNode) {
selectedKey.value = { [node.data.id]: true }
expandedKeys.value = expandedKeys.value || {};
expandedKeys.value[parent.data.id] = true
emit('nodeSelect', node)
}
const expandedKeys
= ref()
</script>
<template>
<div class="flex flex-shrink-0 p-4 items-center">
<div class="flex flex-shrink-0 mr-6 ml-4 items-center">
<article class="prose dark:prose-invert flex-1">
<h2>Rules:</h2>
<h2>List of Rules:</h2>
</article>
<ButtonGroup>
<Button class="flex-shrink-0" :disabled="selectedPosition == undefined || selectedPosition[0] <= 0"
v-tooltip.top="'Move to top'" severity="secondary" icon="pi pi-angle-double-up" size="small"
@click="moveToTop" />
<Button class="flex-shrink-0" :disabled="selectedPosition == undefined || selectedPosition[0] <= 0"
v-tooltip.top="'Move up'" severity="secondary" icon="pi pi-angle-up" size="small" @click="moveUp" />
<Button class="flex-shrink-0"
:disabled="selectedPosition == undefined || selectedPosition[0] >= selectedPosition[1] - 1"
v-tooltip.top="'Move down'" severity="secondary" icon="pi pi-angle-down" size="small" @click="moveDown" />
<Button class="flex-shrink-0"
:disabled="selectedPosition == undefined || selectedPosition[0] >= selectedPosition[1] - 1"
v-tooltip.top="'Move to bottom'" severity="secondary" icon="pi pi-angle-double-down" size="small"
@click="moveToBottom" />
<Button class="flex-shrink-0" icon="pi pi-plus" size="small" @click="toggle" />
<Menu ref="menu" :model="items" :popup="true" />
</ButtonGroup>
<Button class="flex-shrink-0" icon="pi pi-plus" rounded variant="text" size="small" @click="toggle" />
<Menu ref="menu" :model="items" :popup="true" />
</div>
<ScrollPanel class="flex-1 min-h-0">
<Tree v-model:expandedKeys="expandedKeys" class="flex-1 pt-0" :value="nodes" v-model:selectionKeys="selectedKey"
selectionMode="single" @node-select="
emit('nodeSelect', $event)" @node-unselect="emit('nodeUnselect', $event)">
<Tree class="flex-1 pt-0" :value="nodes" v-model:selectionKeys="selectedKey" selectionMode="single"
@node-select="emit('nodeSelect', $event)" @node-unselect="emit('nodeUnselect', $event)">
<template #nodeicon="{ node }">
<span :class="nodeIcon(node.data)"></span>
</template>
<template #default="{ node }">
<FilterNode :node="asFilterNode(node)" @node-select="onNodeSelect" />
<FilterNode :node="asFilterNode(node)" />
</template>
</Tree>
</ScrollPanel>

View File

@ -7,17 +7,11 @@ import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import Tooltip from 'primevue/tooltip';
import ConfirmationService from 'primevue/confirmationservice';
import ToastService from 'primevue/toastservice';
const app = createApp(App)
app.use(createPinia())
app.use(PrimeVue, {
theme: 'none'
})
app.directive('tooltip', Tooltip)
app.use(ConfirmationService)
app.use(ToastService)
app.mount('#app')

View File

@ -1,32 +1,31 @@
import { show } from 'fp-ts'
import * as uuid from 'uuid'
import * as uuid from 'uuid';
interface _Filter {
id: string
name: string
enabled: boolean
rule: FilterRule
id: string;
name: string;
enabled: boolean;
rule: FilterRule;
}
export function defaultLeaf(): FilterLeaf {
return {
type: 'leaf',
type: "leaf",
id: uuid.v4(),
name: '',
name: "",
enabled: true,
rule: {},
show: true,
show: true
}
}
export function defaultGroup(): FilterGroup {
return {
type: 'group',
type: "group",
id: uuid.v4(),
name: '',
name: "",
enabled: true,
rule: {},
filters: [],
filters: []
}
}
@ -35,59 +34,59 @@ export interface FilterConfig {
}
export type FilterLeaf = _Filter & {
type: 'leaf'
show: boolean
type: 'leaf';
show: boolean;
}
export type FilterGroup = _Filter & {
type: 'group'
filters: Filter[]
type: 'group';
filters: Filter[]
}
export type Filter = FilterLeaf | FilterGroup
export type Filter = FilterLeaf | FilterGroup;
export type ItemClass = any // Replace with actual type definition
export type ItemBaseType = any // Replace with actual type definition
export type Op = any // Replace with actual type definition
export type Level = RangedNumber<1, 100> // Replace with actual type definition
export type ItemRarity = string // Replace with actual type definition
export type GameColor = any // Replace with actual type definition
export type MinimapIconShape = any // Replace with actual type definition
export type ItemClass = any; // Replace with actual type definition
export type ItemBaseType = any; // Replace with actual type definition
export type Op = any; // Replace with actual type definition
export type Level = RangedNumber<1, 100>; // Replace with actual type definition
export type ItemRarity = string; // Replace with actual type definition
export type GameColor = any; // Replace with actual type definition
export type MinimapIconShape = any; // Replace with actual type definition
export interface RangedNumber<T extends number, U extends number> {
value: number
min: T
max: U
value: number;
min: T;
max: U;
}
export interface Color {
r: number
g: number
r: number,
g: number,
b: number
}
export interface FilterRule {
class?: string[] //
base_type?: string[] //
area_level?: [string, number] //
drop_level?: [string, number] //
item_level?: [string, number] //
rarity?: [string, string] //
sockets?: [string, number] //
quality?: [string, number] //
stack_size?: [string, number] //
class?: string[]; //
base_type?: string[]; //
area_level?: [string, number]; //
drop_level?: [string, number]; //
item_level?: [string, number]; //
rarity?: [string, string]; //
sockets?: [string, number]; //
quality?: [string, number]; //
stack_size?: [string, number]; //
// waystones
waystone_tier?: [string, number] //
// waystones
waystone_tier?: [string, number]; //
// effects
set_font_size?: number //
set_text_color?: Color //
set_border_color?: Color //
set_background_color?: Color //
play_alert_sound?: [number, number] //
play_effect?: string //
minimap_icon?: [number, string, string] //
// effects
set_font_size?: number; //
set_text_color?: Color;//
set_border_color?: Color;//
set_background_color?: Color;//
play_alert_sound?: [number, number];//
play_effect?: string;//
minimap_icon?: [number, string, string]; //
}
export interface FilterNode {
@ -96,105 +95,3 @@ export interface FilterNode {
data: Filter
children?: FilterNode[]
}
export function toLines(rule: FilterRule): string[] {
let r: string[] = []
if (rule.class && rule.class.length > 0) {
r.push(`Class ${rule.class.map((c) => `"${c}"`).join(' ')}`)
}
if (rule.base_type && rule.base_type.length > 0) {
r.push(`BaseType ${rule.base_type.map((c) => `"${c}"`).join(' ')}`)
}
if (rule.rarity) {
r.push(`Rarity ${rule.rarity[0]} ${rule.rarity[1]}`)
}
if (rule.sockets) {
r.push(`Sockets ${rule.sockets[0]} ${rule.sockets[1]}`)
}
if (rule.quality) {
r.push(`Quality ${rule.quality[0]} ${rule.quality[1]}`)
}
if (rule.stack_size) {
r.push(`StackSize ${rule.stack_size[0]} ${rule.stack_size[1]}`)
}
if (rule.area_level) {
r.push(`AreaLevel ${rule.area_level[0]} ${rule.area_level[1]}`)
}
if (rule.drop_level) {
r.push(`DropLevel ${rule.drop_level[0]} ${rule.drop_level[1]}`)
}
if (rule.item_level) {
r.push(`ItemLevel ${rule.item_level[0]} ${rule.item_level[1]}`)
}
if (rule.waystone_tier) {
r.push(`WaystoneTier ${rule.waystone_tier[0]} ${rule.waystone_tier[1]}`)
}
if (rule.set_font_size) {
r.push(`SetFontSize ${rule.set_font_size}`)
}
if (rule.set_text_color) {
r.push(`SetTextColor ${formatColor(rule.set_text_color)}`)
}
if (rule.set_background_color) {
r.push(`SetBackgroundColor ${formatColor(rule.set_background_color)}`)
}
if (rule.set_border_color) {
r.push(`SetBorderColor ${formatColor(rule.set_border_color)}`)
}
if (rule.play_alert_sound) {
r.push(`PlayAlertSound ${rule.play_alert_sound[0]} ${rule.play_alert_sound[1]}`)
}
if (rule.minimap_icon) {
r.push(`MinimapIcon ${rule.minimap_icon[0]} ${rule.minimap_icon[1]} ${rule.minimap_icon[2]}`)
}
if (rule.play_effect) {
r.push(`PlayEffect ${rule.play_effect}`)
}
return r
}
function formatColor(color: Color) {
return `${color.r} ${color.g} ${color.b}`
}
export function generateFilterText(filters: Filter[]): string {
let flat = flatten(filters)
return flat
.map((f) => {
let lines = toLines(f.rule)
if (lines.length < 1) {
return undefined
}
lines.unshift(f.show ? 'Show' : 'Hide')
return lines.join('\n')
})
.filter((s) => s != undefined)
.reverse()
.join('\n\n')
}
function flatten(filters: Filter[]): Omit<FilterLeaf, 'id' | 'name' | 'type' | 'enabled'>[] {
return filters.flatMap((f) => {
if (!f.enabled) {
return []
}
if (f.type === 'group') {
let children = flatten(f.filters)
return children.map((c) => {
let rule = c.rule as any
Object.entries(f.rule).forEach(([k, v]) => {
if (v != undefined) {
rule[k] = v
}
})
return {
show: c.show,
rule,
}
})
} else {
return [{ show: f.show, rule: f.rule }]
}
})
}

View File

@ -1,23 +1,19 @@
import type { Filter, FilterNode } from '@/models'
import type { Filter } from '@/models'
import type { TreeNode } from 'primevue/treenode'
export function filterToTreeNode(filter: Filter, parent?: FilterNode): FilterNode {
let node = {
export function filterToTreeNode(filter: Filter, parent?: TreeNode): TreeNode {
let node: TreeNode = {
key: filter.id,
data: filter,
parent,
} as FilterNode
if (filter.type === 'group') {
node.children = filter.filters.map((f) => filterToTreeNode(f, node))
parent
}
if (filter.type === 'group' ) {
node.children = filter.filters.map(f => filterToTreeNode(f, node))
}
return node
}
export function treeNodeToFilter(node: FilterNode): Filter {
let filter = node.data
if (filter.type === 'group') {
filter.filters = node.children!.map(treeNodeToFilter)
}
return filter
export function treeNodeToFilter(node: TreeNode): Filter {
return node.data;
}

View File

@ -91,5 +91,4 @@ body {
.p-tree-node-label {
flex: 1;
min-width: 0;
}