update
This commit is contained in:
parent
c65dbcb8fc
commit
a23e3c2e60
33
src/App.vue
33
src/App.vue
@ -9,7 +9,8 @@ import * as O from 'fp-ts/Option'
|
||||
import * as A from 'fp-ts/lib/Array';
|
||||
import { pipe } from 'fp-ts/function'
|
||||
import * as uuid from 'uuid';
|
||||
import type { Filter, FilterConfig } from './models'
|
||||
import type { Filter, FilterConfig, FilterNode } from './models'
|
||||
import { filterToTreeNode } from './services/filter'
|
||||
|
||||
const darkMode = ref(document.documentElement.classList.contains('dark'))
|
||||
|
||||
@ -22,11 +23,6 @@ watch(darkMode, (dark) => {
|
||||
)
|
||||
localStorage.theme = dark ? 'dark' : 'light'
|
||||
})
|
||||
function onNodeSelect(node: TreeNode) {
|
||||
}
|
||||
|
||||
function onNodeUnselect(node: TreeNode) {
|
||||
}
|
||||
|
||||
let defaultFilters: Filter[] = [
|
||||
{ type: 'leaf', id: uuid.v4(), name: "", show: true, enabled: true, rule: {} },
|
||||
@ -36,28 +32,41 @@ let defaultFilters: Filter[] = [
|
||||
]
|
||||
}
|
||||
]
|
||||
const filters = ref(pipe(
|
||||
const nodes = ref(pipe(
|
||||
localStorage.getItem("filters"),
|
||||
O.fromNullable,
|
||||
O.map((value): FilterConfig => JSON.parse(value)),
|
||||
O.map(value => value.filters),
|
||||
O.getOrElse(() => defaultFilters),
|
||||
A.map(filterToTreeNode)
|
||||
))
|
||||
const selectedFilter = ref<Filter>()
|
||||
const selectedFilter = ref<FilterNode>()
|
||||
|
||||
function onDelete() {
|
||||
let target = selectedFilter.value!
|
||||
let array = nodes.value as FilterNode[]
|
||||
if (target.parent) {
|
||||
array = target.parent.children!
|
||||
}
|
||||
let index = array.findIndex(item => item === target)
|
||||
array.splice(index, 1)
|
||||
selectedFilter.value = undefined
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-row gap-2 items-center flex-grow-0">
|
||||
<article class="prose dark:prose-invert">
|
||||
<article class="prose dark:prose-invert p-4">
|
||||
<h1>POE2 Loot Filter Config</h1>
|
||||
</article><Button :icon @click="darkMode = !darkMode" severity="secondary" variant="outlined" rounded />
|
||||
</div>
|
||||
<Splitter class="flex-1 min-h-0">
|
||||
<SplitterPanel>
|
||||
<TreeNav :filters @nodeSelect="selectedFilter = $event.data" @nodeUnselect="selectedFilter = undefined" />
|
||||
<SplitterPanel class="flex flex-col">
|
||||
<TreeNav :nodes @nodeSelect="selectedFilter = $event" @nodeUnselect="selectedFilter = undefined" />
|
||||
</SplitterPanel>
|
||||
<SplitterPanel class="flex flex-col">
|
||||
<FilterDetail v-if="selectedFilter" :filter="selectedFilter" />
|
||||
<FilterDetail v-if="selectedFilter" :node="selectedFilter" @delete="onDelete" />
|
||||
<Info v-else />
|
||||
</SplitterPanel>
|
||||
</Splitter>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { Filter } from '@/models';
|
||||
import { ref, watchEffect } from 'vue'
|
||||
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'
|
||||
const props = defineProps<{
|
||||
filter: Filter
|
||||
node: FilterNode,
|
||||
onDelete: () => void
|
||||
}>()
|
||||
const OPERATORS = ['=', '==', '!=', '<', '<=', '>', '>='];
|
||||
|
||||
@ -67,6 +68,7 @@ const BASE_TYPES = [
|
||||
// }, 250);
|
||||
// }
|
||||
|
||||
const filter = computed(() => props.node.data)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -81,7 +83,7 @@ const BASE_TYPES = [
|
||||
v-model="filter.show" onLabel="Show" offLabel="Hide" />
|
||||
|
||||
<InputText class="w-full" type="text" :placeholder="filter.id" v-model="filter.name" />
|
||||
<Button class="flex-shrink-0" icon="pi pi-trash" severity="danger" />
|
||||
<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">
|
||||
|
||||
47
src/components/FilterNode.vue
Normal file
47
src/components/FilterNode.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { defaultGroup, defaultLeaf, type FilterNode } from '@/models';
|
||||
import { filterToTreeNode } from '@/services/filter';
|
||||
import { Button, Menu } from 'primevue';
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
node: FilterNode
|
||||
}>()
|
||||
const menu = ref();
|
||||
const items = ref([
|
||||
{
|
||||
label: "Create new...",
|
||||
items: [
|
||||
{
|
||||
label: 'End Rule',
|
||||
icon: 'pi pi-plus-circle',
|
||||
command: () => {
|
||||
props.node.children?.push(filterToTreeNode(defaultLeaf(), props.node) as FilterNode);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Group',
|
||||
icon: 'pi pi-folder-plus',
|
||||
command: () => {
|
||||
props.node.children?.push(filterToTreeNode(defaultGroup(), props.node) as FilterNode);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const toggle = (event: MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
menu.value.toggle(event);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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>
|
||||
@ -1,22 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import type { Filter, FilterConfig } from '../models';
|
||||
import { filterToTreeNode } from '../services/filter';
|
||||
import * as A from 'fp-ts/lib/Array';
|
||||
import { pipe } from 'fp-ts/function'
|
||||
import { Tree } from 'primevue'
|
||||
import { onUpdated, ref } from 'vue'
|
||||
import { defaultGroup, defaultLeaf, type Filter, type FilterNode as FilterNodeType } from '../models';
|
||||
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<{
|
||||
filters: Filter[]
|
||||
nodes: TreeNode[]
|
||||
}>()
|
||||
|
||||
const nodes = computed(() => {
|
||||
return pipe(
|
||||
props.filters,
|
||||
A.map(filterToTreeNode),
|
||||
)
|
||||
})
|
||||
const emit = defineEmits(['nodeSelect', 'nodeUnselect'])
|
||||
const selectedKey = ref()
|
||||
|
||||
function nodeIcon(filter: Filter): string {
|
||||
@ -26,12 +20,58 @@ function nodeIcon(filter: Filter): string {
|
||||
.exhaustive()
|
||||
return `p-tree-node-icon pi pi-fw pi-${icon}`;
|
||||
}
|
||||
|
||||
function asFilterNode(a: TreeNode) {
|
||||
return a as FilterNodeType
|
||||
}
|
||||
|
||||
const menu = ref();
|
||||
const items = ref([
|
||||
{
|
||||
label: "Create new...",
|
||||
items: [
|
||||
{
|
||||
label: 'End Rule',
|
||||
icon: 'pi pi-plus-circle',
|
||||
command: () => {
|
||||
props.nodes.push(filterToTreeNode(defaultLeaf()) as FilterNodeType);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Group',
|
||||
icon: 'pi pi-folder-plus',
|
||||
command: () => {
|
||||
props.nodes.push(filterToTreeNode(defaultGroup()) as FilterNodeType);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const toggle = (event: MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
menu.value.toggle(event);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tree :value="nodes" v-model:selectionKeys="selectedKey" selectionMode="single">
|
||||
<template #nodeicon="{ node }">
|
||||
<span :class="nodeIcon(node.data)"></span>
|
||||
</template>
|
||||
</Tree>
|
||||
<div class="flex flex-shrink-0 mr-6 ml-4 items-center">
|
||||
<article class="prose dark:prose-invert flex-1">
|
||||
<h2>List of Rules:</h2>
|
||||
</article>
|
||||
<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 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)" />
|
||||
</template>
|
||||
</Tree>
|
||||
</ScrollPanel>
|
||||
</template>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
interface _Filter {
|
||||
id: string;
|
||||
@ -6,6 +7,28 @@ interface _Filter {
|
||||
rule: FilterRule;
|
||||
}
|
||||
|
||||
export function defaultLeaf(): FilterLeaf {
|
||||
return {
|
||||
type: "leaf",
|
||||
id: uuid.v4(),
|
||||
name: "",
|
||||
enabled: true,
|
||||
rule: {},
|
||||
show: true
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultGroup(): FilterGroup {
|
||||
return {
|
||||
type: "group",
|
||||
id: uuid.v4(),
|
||||
name: "",
|
||||
enabled: true,
|
||||
rule: {},
|
||||
filters: []
|
||||
}
|
||||
}
|
||||
|
||||
export interface FilterConfig {
|
||||
filters: Filter[]
|
||||
}
|
||||
@ -65,3 +88,10 @@ export interface FilterRule {
|
||||
play_effect?: string;//
|
||||
minimap_icon?: [number, string, string]; //
|
||||
}
|
||||
|
||||
export interface FilterNode {
|
||||
key: string
|
||||
parent?: FilterNode
|
||||
data: Filter
|
||||
children?: FilterNode[]
|
||||
}
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
import type { Filter } from '@/models'
|
||||
import type { TreeNode } from 'primevue/treenode'
|
||||
import { match } from 'ts-pattern'
|
||||
|
||||
export function filterToTreeNode(filter: Filter): TreeNode {
|
||||
let children = match(filter)
|
||||
.with({ type: 'leaf' }, (filter) => undefined)
|
||||
.with({ type: 'group' }, (filter) => filter.filters.map(filterToTreeNode))
|
||||
.exhaustive()
|
||||
|
||||
return {
|
||||
export function filterToTreeNode(filter: Filter, parent?: TreeNode): TreeNode {
|
||||
let node: TreeNode = {
|
||||
key: filter.id,
|
||||
data: filter,
|
||||
label: filter.name || filter.id,
|
||||
children,
|
||||
parent
|
||||
}
|
||||
if (filter.type === 'group' ) {
|
||||
node.children = filter.filters.map(f => filterToTreeNode(f, node))
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
export function treeNodeToFilter(node: TreeNode): Filter {
|
||||
|
||||
@ -88,3 +88,7 @@ body {
|
||||
.p-colorpicker-panel {
|
||||
z-index: 10 !important;
|
||||
}
|
||||
|
||||
.p-tree-node-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user