add search filters to LibraryList and LibraryMap
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="library-list">
|
<div class="library-list">
|
||||||
<div class="library-list-container">
|
<div class="library-list-container">
|
||||||
<div v-for="(parents, char) in orderedTextsDepart" :key="char">
|
<div v-for="(parents, char) in filteredNodes" :key="char">
|
||||||
<h3>{{ char }}</h3>
|
<h3>{{ char }}</h3>
|
||||||
|
|
||||||
<div class="library-list-nodes-group">
|
<div class="library-list-nodes-group">
|
||||||
<node-view
|
<node-view
|
||||||
v-for="parent in parents" :key="parent.id"
|
v-for="parent in parents" :key="parent.id"
|
||||||
@@ -20,18 +21,32 @@
|
|||||||
// FIXME let events bubble
|
// FIXME let events bubble
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
import { searchInNode } from '@/store/utils'
|
||||||
import { NodeView } from '@/components/nodes'
|
import { NodeView } from '@/components/nodes'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CardList',
|
name: 'LibraryList',
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
NodeView
|
NodeView
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['orderedTextsDepart'])
|
...mapGetters(['orderedTextsDepart', 'search']),
|
||||||
|
|
||||||
|
filteredNodes () {
|
||||||
|
if (!this.orderedTextsDepart) return {}
|
||||||
|
const nodesGroups = {}
|
||||||
|
const search = this.search.toLowerCase()
|
||||||
|
Object.entries(this.orderedTextsDepart).forEach(([char, nodes]) => {
|
||||||
|
const filteredNodes = nodes.filter(node => searchInNode(search, node))
|
||||||
|
if (filteredNodes.length) {
|
||||||
|
nodesGroups[char] = filteredNodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nodesGroups
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
created () {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
<b-overlay :show="nodes === undefined" class="h-100">
|
<b-overlay :show="nodes === undefined" class="h-100">
|
||||||
<map-zoom id="library-map" :min-zoom="0.2" :max-zoom="2">
|
<map-zoom id="library-map" :min-zoom="0.2" :max-zoom="2">
|
||||||
<foreignObject
|
<foreignObject
|
||||||
v-for="(node, i) in nodes" :key="i"
|
v-for="(node, i) in filteredNodes" :key="i"
|
||||||
:style="`--x: ${node.x}px; --y: ${node.y}px; --r: ${node.rotate}deg;`"
|
:style="`--x: ${node.x}px; --y: ${node.y}px; --r: ${node.rotate}deg;`"
|
||||||
>
|
>
|
||||||
<node-view
|
<node-view
|
||||||
:node="node.data"
|
:node="node.data"
|
||||||
mode="card"
|
mode="card"
|
||||||
@open-node="onOpen(node.data)"
|
@open-node="onOpen(node.data)"
|
||||||
|
:hidden="node.hidden"
|
||||||
/>
|
/>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
</map-zoom>
|
</map-zoom>
|
||||||
@@ -18,7 +19,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import { randomUniform } from 'd3'
|
import { randomUniform } from 'd3'
|
||||||
import { forceSimulation, forceCollide, forceManyBody } from 'd3-force'
|
import { forceSimulation, forceCollide, forceManyBody } from 'd3-force'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
import { searchInNode } from '@/store/utils'
|
||||||
import { MapZoom } from '@/components/layouts'
|
import { MapZoom } from '@/components/layouts'
|
||||||
import { NodeView } from '@/components/nodes'
|
import { NodeView } from '@/components/nodes'
|
||||||
|
|
||||||
@@ -38,6 +41,19 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['search']),
|
||||||
|
|
||||||
|
filteredNodes () {
|
||||||
|
if (!this.nodes) return
|
||||||
|
const search = this.search.toLowerCase()
|
||||||
|
this.nodes.forEach(node => {
|
||||||
|
Object.assign(node, { hidden: !searchInNode(search, node.data) })
|
||||||
|
})
|
||||||
|
return this.nodes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onOpen (node) {
|
onOpen (node) {
|
||||||
// FIXME ALSO CHECK IF PARENT
|
// FIXME ALSO CHECK IF PARENT
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
<!-- LIST + MAP ONLY -->
|
||||||
<b-form-group
|
<b-form-group
|
||||||
v-if="mode !== 'tree'"
|
v-if="mode !== 'tree'"
|
||||||
:label="$t('options.filters.title')"
|
:label="$t('options.filters.title')"
|
||||||
@@ -37,7 +38,20 @@
|
|||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group
|
||||||
|
:label="$t('options.search.title')"
|
||||||
|
label-for="search-input"
|
||||||
|
>
|
||||||
|
<b-input-group append="search-icon">
|
||||||
|
<b-form-input
|
||||||
|
v-model="activeSearch" id="search-input" trim
|
||||||
|
/>
|
||||||
|
</b-input-group>
|
||||||
|
</b-form-group>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
<!-- TREE ONLY -->
|
||||||
<b-form-group
|
<b-form-group
|
||||||
v-else
|
v-else
|
||||||
label="Texte de départ :"
|
label="Texte de départ :"
|
||||||
@@ -46,24 +60,11 @@
|
|||||||
>
|
>
|
||||||
<b-form-select
|
<b-form-select
|
||||||
id="text-depart-select"
|
id="text-depart-select"
|
||||||
v-model="nodeDepartId"
|
v-model="activeNodeId"
|
||||||
:options="nodesDepartsOptions"
|
:options="nodesDepartsOptions"
|
||||||
class="border"
|
class="border"
|
||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
|
||||||
<b-form-group
|
|
||||||
:label="$t('options.search.title')"
|
|
||||||
label-for="search-input"
|
|
||||||
>
|
|
||||||
<b-input-group append="search-icon">
|
|
||||||
<b-form-input
|
|
||||||
v-model="search" id="search-input" trim
|
|
||||||
:disabled="true"
|
|
||||||
/>
|
|
||||||
</b-input-group>
|
|
||||||
</b-form-group>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -87,35 +88,43 @@ export default {
|
|||||||
{ text: this.$t('options.display.choices.card-list'), value: 'list' }
|
{ text: this.$t('options.display.choices.card-list'), value: 'list' }
|
||||||
],
|
],
|
||||||
selectedTags: [],
|
selectedTags: [],
|
||||||
strangeness: 0,
|
strangeness: 0
|
||||||
textDepartId: undefined,
|
|
||||||
search: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['mode', 'nodebook', 'nodesDepartsOptions', 'tagsOptions']),
|
...mapGetters([
|
||||||
|
'mode',
|
||||||
|
'nodeDepartId',
|
||||||
|
'search',
|
||||||
|
'nodebook',
|
||||||
|
'nodesDepartsOptions',
|
||||||
|
'tagsOptions'
|
||||||
|
]),
|
||||||
|
|
||||||
show () {
|
show () {
|
||||||
return this.nodebook.length === 0
|
return this.nodebook.length === 0
|
||||||
},
|
},
|
||||||
|
|
||||||
nodeDepartId: {
|
activeNodeId: {
|
||||||
get () {
|
get () { return this.nodeDepartId },
|
||||||
return this.$store.state.library.nodeDepartId
|
|
||||||
},
|
|
||||||
set (value) {
|
set (value) {
|
||||||
this.$store.dispatch('SET_NODE_DEPART_ID', value)
|
this.$store.dispatch('SET_NODE_DEPART_ID', value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
activeMode: {
|
activeMode: {
|
||||||
get () {
|
get () { return this.mode },
|
||||||
return this.mode
|
|
||||||
},
|
|
||||||
set (value) {
|
set (value) {
|
||||||
this.$store.dispatch('UPDATE_QUERY_MODE', value)
|
this.$store.dispatch('UPDATE_QUERY_MODE', value)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activeSearch: {
|
||||||
|
get () { return this.search },
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit('SET_SEARCH', value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export default {
|
|||||||
nodeDepartId: undefined,
|
nodeDepartId: undefined,
|
||||||
trees: {},
|
trees: {},
|
||||||
mode: undefined,
|
mode: undefined,
|
||||||
nodebook: undefined
|
nodebook: undefined,
|
||||||
|
search: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
@@ -67,6 +68,10 @@ export default {
|
|||||||
|
|
||||||
'SET_NODE_DEPART_ID' (state, id) {
|
'SET_NODE_DEPART_ID' (state, id) {
|
||||||
state.nodeDepartId = id
|
state.nodeDepartId = id
|
||||||
|
},
|
||||||
|
|
||||||
|
'SET_SEARCH' (state, str) {
|
||||||
|
state.search = str
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -165,7 +170,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
mode: (state) => state.mode,
|
mode: state => state.mode,
|
||||||
|
nodeDepartId: state => state.nodeDepartId,
|
||||||
|
search: state => state.search,
|
||||||
|
|
||||||
nodebook: (state, getters, rootState) => {
|
nodebook: (state, getters, rootState) => {
|
||||||
if (!state.nodebook) return []
|
if (!state.nodebook) return []
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ export const VARIANT_IDS = Object.fromEntries(
|
|||||||
Object.entries(ID_VARIANTS).map(([key, value]) => [value, key])
|
Object.entries(ID_VARIANTS).map(([key, value]) => [value, key])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const SEARCH_KEYS = ['content', 'title', 'preTitle', 'authors']
|
||||||
|
|
||||||
|
|
||||||
export function parseNodesQueryParams ({ mode, nodes = [] }) {
|
export function parseNodesQueryParams ({ mode, nodes = [] }) {
|
||||||
let nodebook = typeof nodes === 'string' ? [nodes] : nodes
|
let nodebook = typeof nodes === 'string' ? [nodes] : nodes
|
||||||
@@ -112,3 +114,20 @@ export function buildTree (treeData, nodesData) {
|
|||||||
links
|
links
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function searchInNode (search, node) {
|
||||||
|
if (!search) return true
|
||||||
|
for (const key of SEARCH_KEYS) {
|
||||||
|
if (!node[key]) continue
|
||||||
|
let match = false
|
||||||
|
if (key === 'authors') {
|
||||||
|
match = node[key].some(author => author.name.toLowerCase().includes(search))
|
||||||
|
} else {
|
||||||
|
console.log(node[key].toLowerCase(), search)
|
||||||
|
match = node[key].toLowerCase().includes(search)
|
||||||
|
}
|
||||||
|
if (match) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user