소스 검색

add search filters to LibraryList and LibraryMap

axolotle 3 년 전
부모
커밋
a1f15457a0
5개의 변경된 파일97개의 추가작업 그리고 31개의 파일을 삭제
  1. 18 3
      src/pages/library/LibraryList.vue
  2. 17 1
      src/pages/library/LibraryMap.vue
  3. 34 25
      src/pages/library/LibraryOptions.vue
  4. 9 2
      src/store/modules/library.js
  5. 19 0
      src/store/utils.js

+ 18 - 3
src/pages/library/LibraryList.vue

@@ -1,8 +1,9 @@
 <template>
   <div class="library-list">
     <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>
+
         <div class="library-list-nodes-group">
           <node-view
             v-for="parent in parents" :key="parent.id"
@@ -20,18 +21,32 @@
 // FIXME let events bubble
 import { mapGetters } from 'vuex'
 
+import { searchInNode } from '@/store/utils'
 import { NodeView } from '@/components/nodes'
 
 
 export default {
-  name: 'CardList',
+  name: 'LibraryList',
 
   components: {
     NodeView
   },
 
   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 () {

+ 17 - 1
src/pages/library/LibraryMap.vue

@@ -2,13 +2,14 @@
   <b-overlay :show="nodes === undefined" class="h-100">
     <map-zoom id="library-map" :min-zoom="0.2" :max-zoom="2">
       <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;`"
       >
         <node-view
           :node="node.data"
           mode="card"
           @open-node="onOpen(node.data)"
+          :hidden="node.hidden"
         />
       </foreignObject>
     </map-zoom>
@@ -18,7 +19,9 @@
 <script>
 import { randomUniform } from 'd3'
 import { forceSimulation, forceCollide, forceManyBody } from 'd3-force'
+import { mapGetters } from 'vuex'
 
+import { searchInNode } from '@/store/utils'
 import { MapZoom } from '@/components/layouts'
 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: {
     onOpen (node) {
       // FIXME ALSO CHECK IF PARENT

+ 34 - 25
src/pages/library/LibraryOptions.vue

@@ -12,6 +12,7 @@
       />
     </b-form-group>
 
+    <!-- LIST + MAP ONLY -->
     <b-form-group
       v-if="mode !== 'tree'"
       :label="$t('options.filters.title')"
@@ -37,7 +38,20 @@
           :disabled="true"
         />
       </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>
+
+    <!-- TREE ONLY -->
     <b-form-group
       v-else
       label="Texte de départ :"
@@ -46,24 +60,11 @@
     >
       <b-form-select
         id="text-depart-select"
-        v-model="nodeDepartId"
+        v-model="activeNodeId"
         :options="nodesDepartsOptions"
         class="border"
       />
     </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>
 </template>
 
@@ -87,35 +88,43 @@ export default {
         { text: this.$t('options.display.choices.card-list'), value: 'list' }
       ],
       selectedTags: [],
-      strangeness: 0,
-      textDepartId: undefined,
-      search: ''
+      strangeness: 0
     }
   },
 
   computed: {
-    ...mapGetters(['mode', 'nodebook', 'nodesDepartsOptions', 'tagsOptions']),
+    ...mapGetters([
+      'mode',
+      'nodeDepartId',
+      'search',
+      'nodebook',
+      'nodesDepartsOptions',
+      'tagsOptions'
+    ]),
 
     show () {
       return this.nodebook.length === 0
     },
 
-    nodeDepartId: {
-      get () {
-        return this.$store.state.library.nodeDepartId
-      },
+    activeNodeId: {
+      get () { return this.nodeDepartId },
       set (value) {
         this.$store.dispatch('SET_NODE_DEPART_ID', value)
       }
     },
 
     activeMode: {
-      get () {
-        return this.mode
-      },
+      get () { return this.mode },
       set (value) {
         this.$store.dispatch('UPDATE_QUERY_MODE', value)
       }
+    },
+
+    activeSearch: {
+      get () { return this.search },
+      set (value) {
+        this.$store.commit('SET_SEARCH', value)
+      }
     }
   }
 }

+ 9 - 2
src/store/modules/library.js

@@ -20,7 +20,8 @@ export default {
     nodeDepartId: undefined,
     trees: {},
     mode: undefined,
-    nodebook: undefined
+    nodebook: undefined,
+    search: ''
   },
 
   mutations: {
@@ -67,6 +68,10 @@ export default {
 
     'SET_NODE_DEPART_ID' (state, id) {
       state.nodeDepartId = id
+    },
+
+    'SET_SEARCH' (state, str) {
+      state.search = str
     }
   },
 
@@ -165,7 +170,9 @@ export default {
   },
 
   getters: {
-    mode: (state) => state.mode,
+    mode: state => state.mode,
+    nodeDepartId: state => state.nodeDepartId,
+    search: state => state.search,
 
     nodebook: (state, getters, rootState) => {
       if (!state.nodebook) return []

+ 19 - 0
src/store/utils.js

@@ -28,6 +28,8 @@ export const VARIANT_IDS = Object.fromEntries(
   Object.entries(ID_VARIANTS).map(([key, value]) => [value, key])
 )
 
+export const SEARCH_KEYS = ['content', 'title', 'preTitle', 'authors']
+
 
 export function parseNodesQueryParams ({ mode, nodes = [] }) {
   let nodebook = typeof nodes === 'string' ? [nodes] : nodes
@@ -112,3 +114,20 @@ export function buildTree (treeData, nodesData) {
     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
+}