Sfoglia il codice sorgente

:sparkles: adding search drop-down | adding search getters | adding search display for list | #163

tags/1.0.0
J 4 anni fa
parent
commit
2f186be716

+ 57
- 14
plugins/cia-endpoints/includes/class.make-search.php Vedi File

2
 // include('formats.php');
2
 // include('formats.php');
3
 
3
 
4
 class Make_Search_Endpoint extends WP_REST_Controller {
4
 class Make_Search_Endpoint extends WP_REST_Controller {
5
+    function make_args($request) {
6
+        $args = [
7
+            'post_status'     => ['publish'],
8
+            'posts_per_page'  => -1,
9
+            'page'            => 1,
10
+            'relevanssi'      => true,
11
+            'post_type' => [
12
+                'artist',
13
+                'episode',
14
+                'event',
15
+                'exhibition',
16
+                'guide',
17
+                'object',
18
+                'publication',
19
+                'technique',
20
+                'short',
21
+                'post',
22
+                'page'
23
+            ]
24
+        ];
25
+        // Get parameters from request,
26
+        // /?limit=<num>&?orderby=<str>&?order=<str>
27
+        $params = $request->get_params();
28
+        if(intval($params['limit']) > 0) {
29
+            $args['posts_per_page'] = intval($params['limit']);
30
+        }
31
+        if(intval($params['p']) > 1) {
32
+            $args['page'] = intval($params['p']);
33
+            $args['offset'] = ($args['page'] * $args['posts_per_page']) - $args['posts_per_page'];
34
+        }
35
+        if($params['s']) {
36
+            $args['s'] = $params['s'];
37
+        }
38
+        
39
+        return $args;
40
+    }
5
     /**
41
     /**
6
      * Register the routes for the objects of the controller.
42
      * Register the routes for the objects of the controller.
7
      */
43
      */
12
         register_rest_route( $namespace, '/' . $route, [
48
         register_rest_route( $namespace, '/' . $route, [
13
             array(
49
             array(
14
                 'methods'  => WP_REST_Server::READABLE,
50
                 'methods'  => WP_REST_Server::READABLE,
15
-                'callback' => array( $this, 'get_all_items' )
51
+                'callback' => array( $this, 'get_results' )
16
             ),
52
             ),
17
         ]);
53
         ]);
18
     }
54
     }
19
-    public function get_all_items( $request ) {
20
-        $args = array(
21
-            'numberposts' => 9,
22
-        );
23
-        $params = $request->get_params();
24
-        if($params['s']) {
25
-            $args['s'] = $params['s'];
26
-        }
55
+    public function get_results( $request ) {
27
         // https://www.relevanssi.com/user-manual/functions/relevanssi_do_query/
56
         // https://www.relevanssi.com/user-manual/functions/relevanssi_do_query/
28
-        $q = new WP_Query( $args );
29
-        relevanssi_do_query( $q );
30
-        $found_posts = $q->get_posts();
31
-        
32
-        return new WP_REST_Response( $found_posts, 200 );
57
+        $q = new WP_Query($this->make_args($request));
58
+        $found_posts = relevanssi_do_query( $q );
59
+        return new WP_REST_Response( $this->prepare_items_for_reponse($found_posts), 200 );
60
+    }
61
+    public function prepare_items_for_reponse( $items ) {
62
+        $collection = [];
63
+        forEach( $items as $item ) {
64
+            $formatted = default_post_format( $item, false );
65
+            $formatted[hero] = get_post_meta( $item->ID, 'hero_header', true );
66
+            array_push($collection, $formatted);
67
+        }
68
+        return $collection;
33
     }
69
     }
34
 }
70
 }
35
 
71
 
72
+add_filter( 'relevanssi_query_filter', function( $query ) {
73
+    global $wp_query;
74
+    if ( isset( $wp_query->query_vars['orderby'] ) && 'post_date' === $wp_query->query_vars['orderby'] ) {
75
+        $query = str_replace( 'ORDER BY tf', 'ORDER BY relevanssi.doc', $query );
76
+    }
77
+    return $query;
78
+}, 11 );
36
 ?>
79
 ?>

+ 1
- 0
plugins/cia-endpoints/includes/class.make-sticky.php Vedi File

27
                 'guide',
27
                 'guide',
28
                 'object',
28
                 'object',
29
                 'publication',
29
                 'publication',
30
+                'technique',
30
                 'short',
31
                 'short',
31
                 'post',
32
                 'post',
32
                 'page'
33
                 'page'

+ 0
- 2
vue-theme/src/components/hero.vue Vedi File

91
 @import './../sss/variables.sss'
91
 @import './../sss/variables.sss'
92
 
92
 
93
 .hero
93
 .hero
94
-    /* background-color: rebeccapurple */
95
-    /* min-height: 25vh */
96
     min-height: 54vw
94
     min-height: 54vw
97
     position: relative
95
     position: relative
98
     overflow: hidden
96
     overflow: hidden

+ 66
- 30
vue-theme/src/components/navigation/navigation.vue Vedi File

1
 // Replace with calls to menu system
1
 // Replace with calls to menu system
2
 <template lang="pug">
2
 <template lang="pug">
3
 nav.main.w-max
3
 nav.main.w-max
4
-    .menu
4
+    .menu.f-col
5
         ul.f-row.w-max
5
         ul.f-row.w-max
6
             li.f-row.start
6
             li.f-row.start
7
                 router-link(to="/")
7
                 router-link(to="/")
102
                     li
102
                     li
103
                         router-link(to="/page/contact") Contact 
103
                         router-link(to="/page/contact") Contact 
104
             li.f-grow
104
             li.f-grow
105
-            li.t-up 🔍
106
-                ul.submenu 
107
-                    li
108
-                        router-link(to="/page/search") Search
105
+            li.t-up 
106
+                a(@click="toggleSearch") search
107
+        ul(v-if="isSearchOpen").search.w-max
108
+            li.f-row.w-max
109
+                input(v-model="searchTerms" @keyup.enter="sendSearch" tabindex="10")
110
+                button.b-none.bg-none(@click="sendSearch" tabindex="11") 🔍
109
 
111
 
110
     .mobile-menu
112
     .mobile-menu
111
         .f-row.start
113
         .f-row.start
124
 </template>
126
 </template>
125
 
127
 
126
 <script>
128
 <script>
129
+import { computed, ref, nextTick } from '@vue/runtime-core'
130
+import { useRouter } from 'vue-router'
127
 import { postTypes, sortTypes } from '@/utils/helpers'
131
 import { postTypes, sortTypes } from '@/utils/helpers'
128
-import { computed } from '@vue/runtime-core'
129
 
132
 
130
 export default {
133
 export default {
131
     setup() {
134
     setup() {
135
+        const router = useRouter()
136
+        const searchTerms = ref('')
137
+        const isSearchOpen = ref(false)
138
+        const toggleSearch = () => {
139
+            isSearchOpen.value = !isSearchOpen.value
140
+            if(isSearchOpen.value) {
141
+                nextTick(() => {
142
+                    const searchBox = document.querySelector('ul.search > li > input')
143
+                    searchBox.focus()
144
+                })
145
+            }
146
+        }
147
+        const sendSearch = () => {
148
+            router.push({ path: '/search', query: { s: searchTerms.value } })
149
+            searchTerms.value = ''
150
+            toggleSearch()
151
+        }
152
+        
153
+
132
         /**
154
         /**
133
          * Navigation items
155
          * Navigation items
134
          * Auto-includes declared postTypes
156
          * Auto-includes declared postTypes
161
         return {
183
         return {
162
             sortTypes,
184
             sortTypes,
163
             menuItems,
185
             menuItems,
164
-            uncheck
186
+            uncheck,
187
+            searchTerms,
188
+            sendSearch,
189
+            toggleSearch,
190
+            isSearchOpen
165
         }
191
         }
166
     },
192
     },
167
 }
193
 }
194
                 height: $ms-3
220
                 height: $ms-3
195
                 padding: 0
221
                 padding: 0
196
     
222
     
223
+    .menu > ul
224
+        position: relative
225
+        padding: 0 0 0 $ms-0
226
+        &.search
227
+            background-color: #fefefe
228
+            box-shadow: 1px 1px 1px $lighter
229
+            position: absolute
230
+            top: 36px
231
+            padding: 0
232
+            input
233
+                width: 50%
234
+                font-size: $ms-2
235
+
197
     .menu   
236
     .menu   
198
         h1
237
         h1
199
             margin: $ms--7 0 0
238
             margin: $ms--7 0 0
203
             margin: 0
242
             margin: 0
204
         img
243
         img
205
             margin: 0 $ms--2 calc(-1 * $ms--6) 0
244
             margin: 0 $ms--2 calc(-1 * $ms--6) 0
206
-        > ul
207
-            position: relative
208
-            padding: 0 0 0 $ms-0
209
-            > li:nth-child(1)
245
+        > ul > li
246
+            &:nth-child(1)
210
                 a
247
                 a
211
                     text-decoration: none
248
                     text-decoration: none
212
-            > li:nth-child(n+2)
249
+            &:nth-child(n+2)
213
                 a > h5
250
                 a > h5
214
                     padding: $ms-1
251
                     padding: $ms-1
215
-
216
                 //- menu hover
252
                 //- menu hover
217
                 &:hover
253
                 &:hover
218
                     color: $cia_red1
254
                     color: $cia_red1
219
                     display: block 
255
                     display: block 
220
                     cursor: pointer
256
                     cursor: pointer
221
                     transition: $transition
257
                     transition: $transition
222
-            li 
223
-                ul.submenu
258
+            ul.submenu
259
+                display: block  
260
+                position: absolute
261
+                background-color: $lighter
262
+                padding: 0 0.5em 0.5em
263
+                opacity: 0
264
+
265
+                //- submenu hover
266
+                &:hover 
224
                     display: block  
267
                     display: block  
225
-                    position: absolute
226
                     background-color: $lighter
268
                     background-color: $lighter
227
-                    padding: 0 0.5em 0.5em
228
-                    opacity: 0
229
-                    //- submenu hover
269
+                    opacity: 1
270
+                    transition: $transition
271
+
272
+                > li 
273
+                    line-height: 1.5
274
+                    width: max-content
275
+                    background-color: $lighter
276
+                    //- list hover
230
                     &:hover 
277
                     &:hover 
231
                         display: block  
278
                         display: block  
232
                         background-color: $lighter
279
                         background-color: $lighter
233
-                        opacity: 1
234
-                        transition: $transition
235
-
236
-                    li 
237
-                        line-height: 1.5
238
-                        width: max-content
239
-                        background-color: $lighter
240
-                        //- list hover
241
-                        &:hover 
242
-                            display: block  
243
-                            background-color: $lighter
244
                     
280
                     
245
     
281
     
246
     .mobile-menu
282
     .mobile-menu

+ 56
- 12
vue-theme/src/pages/list.vue Vedi File

4
 
4
 
5
         header.center.t-up
5
         header.center.t-up
6
             .title.f-row
6
             .title.f-row
7
-                h3 {{ type }}s&nbsp;
7
+                h3(v-if="!isSearch") {{ type }}s&nbsp;
8
+                h3(v-else) Search results for: {{$route.query.s }}&nbsp;
8
                 span(v-if="sortBy")
9
                 span(v-if="sortBy")
9
                     h3 {{ sortBy.replace('-', ' ') }}
10
                     h3 {{ sortBy.replace('-', ' ') }}
10
 
11
 
48
         return {
49
         return {
49
             page: 0,
50
             page: 0,
50
             perPage: 21,
51
             perPage: 21,
52
+            // Set this from route for initial search
53
+            searchTerm: this.$route.query.s,
51
             keepFetching: true,
54
             keepFetching: true,
52
             loadingFetched: false,
55
             loadingFetched: false,
53
-            observer: null
56
+            observer: null,
54
         }
57
         }
55
     },
58
     },
56
     computed: {
59
     computed: {
63
         grid() {
66
         grid() {
64
             return gridTypes.includes(this.type)
67
             return gridTypes.includes(this.type)
65
         },
68
         },
69
+        isSearch() {
70
+            return this.$route.path == '/search'
71
+        },
66
         isWide() {
72
         isWide() {
67
             return wideTypes.includes(this.type)
73
             return wideTypes.includes(this.type)
68
         },
74
         },
75
         },
81
         },
76
         loaded() {
82
         loaded() {
77
             if (!this.pType) return
83
             if (!this.pType) return
78
-            return this[`all${this.pType}Loaded`]
84
+            return this.isSearch ? this['allSearchResultsLoaded'] : this[`all${this.pType}Loaded`]
79
         },
85
         },
80
         posts() {
86
         posts() {
81
             if (!this.pType) return
87
             if (!this.pType) return
82
-            return this[`all${this.pType}`]
88
+            return this.isSearch ? this['allSearchResults'] : this[`all${this.pType}`]
83
         },
89
         },
84
         shouldLoadAllAtOnce() {
90
         shouldLoadAllAtOnce() {
85
             return this.type == 'episode' ||
91
             return this.type == 'episode' ||
89
         }
95
         }
90
     },
96
     },
91
     methods: {
97
     methods: {
98
+        async loadMoreSearchResults() {
99
+            if(!this.keepFetching) return console.warn('Nothing left to fetch...')
100
+            const getPosts = async params => {
101
+                return await this.$store.dispatch(`getMoreSearchResults`, { params })
102
+            }
103
+            try {
104
+                this.loadingFetched = true
105
+                this.page++
106
+                const res = await getPosts(
107
+                    {
108
+                        limit: this.perPage,
109
+                        page: this.page,
110
+                        s: this.searchTerm
111
+                    }
112
+                )
113
+                this.loadingFetched = false
114
+                // Stop trying to load more posts
115
+                if(res && !res.length) {
116
+                    this.keepFetching = false
117
+                    if(!res.length) console.warn(`Empty response for search results:`, res.length)
118
+                }
119
+            } catch (err) { console.error(err) }
120
+        },
92
         async loadMorePosts(shouldClear) {
121
         async loadMorePosts(shouldClear) {
93
             if(!this.keepFetching) return console.warn('Nothing left to fetch...')
122
             if(!this.keepFetching) return console.warn('Nothing left to fetch...')
94
 
123
 
136
         async getPage(type) {
165
         async getPage(type) {
137
             await this._getAllIfNotLoaded('page', this.$store)
166
             await this._getAllIfNotLoaded('page', this.$store)
138
             if(!this.allPages) throw 'no pages in state'
167
             if(!this.allPages) throw 'no pages in state'
139
-            const page = this.allPages.filter(page => page.slug == `${type}s`)[0]
168
+            const page = this.allPages.filter(page => page.slug == `${type}`)[0]
140
             if(!page) throw `No page: ${type} found. Cannot set hero.`
169
             if(!page) throw `No page: ${type} found. Cannot set hero.`
141
             return page
170
             return page
142
         },
171
         },
152
                 this.$store.commit('SET_HERO', this._setHeroInfo(page))
181
                 this.$store.commit('SET_HERO', this._setHeroInfo(page))
153
             } catch (err) { console.error(err) }
182
             } catch (err) { console.error(err) }
154
         },
183
         },
155
-        setIntersectionLoader() {
184
+        setIntersectionLoader(cb) {
156
             // KeepFetching is UNSET for certain post types and sort types in `loadMorePosts()`
185
             // KeepFetching is UNSET for certain post types and sort types in `loadMorePosts()`
157
             if(!this.keepFetching) return console.warn('Cannot setup intersection handler keepFetching is set false')
186
             if(!this.keepFetching) return console.warn('Cannot setup intersection handler keepFetching is set false')
158
             
187
             
163
             this.observer = new IntersectionObserver(entries => {
192
             this.observer = new IntersectionObserver(entries => {
164
                 entries.forEach(entry => {
193
                 entries.forEach(entry => {
165
                     if (!entry.isIntersecting || this.loadingFetched) return
194
                     if (!entry.isIntersecting || this.loadingFetched) return
166
-                    setTimeout(this.loadMorePosts, TIMEOUT)
195
+                    setTimeout(cb, TIMEOUT)
167
                 })
196
                 })
168
             }, { threshold: 0.80 })
197
             }, { threshold: 0.80 })
169
             this.observer.observe(document.querySelector(INTERSECT_SELECTOR))
198
             this.observer.observe(document.querySelector(INTERSECT_SELECTOR))
181
             this.page = 0
210
             this.page = 0
182
             this.keepFetching = true
211
             this.keepFetching = true
183
 
212
 
184
-            this.checkAndSetHero(this.type)
213
+            if(this.isSearch) {
214
+                this.checkAndSetHero(`search`)
215
+                this.$store.commit(`CLEAR_SEARCH_RESULTS`)
216
+                this.loadMoreSearchResults()
217
+                this.setIntersectionLoader(this.loadMoreSearchResults)
218
+            } else {
219
+                this.checkAndSetHero(`${this.type}s`)
185
 
220
 
186
-            // Clear any preloaded posts (from home, etc.)
187
-            this.loadMorePosts(true)
188
-            this.setIntersectionLoader()
221
+                // Clear any preloaded posts (from home, etc.)
222
+                this.loadMorePosts(true)
223
+                this.setIntersectionLoader(this.loadMorePosts)
224
+            }
189
         }
225
         }
190
     },
226
     },
227
+    beforeRouteUpdate(to, from) {
228
+        // !: Only fires between two searches
229
+        if(!to.query || to.query.s == from.query.s) return
230
+        console.log('between route search')
231
+        // Set the search term for impending callback fire
232
+        this.searchTerm = to.query.s
233
+        this.initPostList()
234
+    },
191
     watch: {
235
     watch: {
192
         // This only fires navigating from a list page, to another list page
236
         // This only fires navigating from a list page, to another list page
193
         type(newType) {
237
         type(newType) {
203
             // console.log('sort changed :', newSort)
247
             // console.log('sort changed :', newSort)
204
             if(!Object.values(sortTypes).includes(newSort)) return
248
             if(!Object.values(sortTypes).includes(newSort)) return
205
             this.initPostList()
249
             this.initPostList()
206
-        }
250
+        },
207
     },
251
     },
208
     mounted() {
252
     mounted() {
209
         // This only fires navigating from a non-list page > list page
253
         // This only fires navigating from a non-list page > list page

+ 3
- 0
vue-theme/src/pages/mixin-post-types.js Vedi File

26
 getterHelper[`randomPosts`] = `randomPosts`
26
 getterHelper[`randomPosts`] = `randomPosts`
27
 getterHelper[`randomPostsLoaded`] = `randomPostsLoaded`
27
 getterHelper[`randomPostsLoaded`] = `randomPostsLoaded`
28
 
28
 
29
+getterHelper[`allSearchResults`] = `allSearchResults`
30
+getterHelper[`allSearchResultsLoaded`] = `allSearchResultsLoaded`
31
+
29
 const stateHelper = {}
32
 const stateHelper = {}
30
 for (let type of postTypes) {
33
 for (let type of postTypes) {
31
     stateHelper[type] = type
34
     stateHelper[type] = type

+ 8
- 0
vue-theme/src/router/routes.js Vedi File

8
      */
8
      */
9
     { path: '/', component: indexPage },
9
     { path: '/', component: indexPage },
10
 
10
 
11
+    /**
12
+     * Search
13
+     */ 
14
+     {
15
+        path: '/search',
16
+        component: listPage,
17
+    },
18
+
11
     /**
19
     /**
12
      * List Pages
20
      * List Pages
13
      */ 
21
      */ 

+ 2
- 0
vue-theme/src/store/index.js Vedi File

17
 import object from './modules/object'
17
 import object from './modules/object'
18
 import publication from './modules/publication'
18
 import publication from './modules/publication'
19
 import sticky from './modules/sticky'
19
 import sticky from './modules/sticky'
20
+import searchResults from './modules/search'
20
 
21
 
21
 const debug = process.env.NODE_ENV !== 'production'
22
 const debug = process.env.NODE_ENV !== 'production'
22
 
23
 
100
         object,
101
         object,
101
         publication,
102
         publication,
102
         sticky,
103
         sticky,
104
+        searchResults
103
     },
105
     },
104
     strict: debug,
106
     strict: debug,
105
 })
107
 })

+ 39
- 0
vue-theme/src/store/modules/search.js Vedi File

1
+import api from '../../utils/api'
2
+
3
+const state = {
4
+    all: [],
5
+    loaded: false,
6
+}
7
+
8
+const getters = {
9
+    allSearchResults: state => state.all,
10
+    allSearchResultsLoaded: state => state.loaded,
11
+}
12
+
13
+const actions = {
14
+    async getMoreSearchResults({ commit }, { params }) {
15
+        const storeFetch = (searchResults => {
16
+            commit('ADD_TO_FETCHED_SEARCH_RESULTS', { searchResults })
17
+            commit('SEARCH_RESULTS_LOADED', true)
18
+        })
19
+        return await api.getByType({ type: 'search', params, cb: storeFetch })
20
+    }
21
+}
22
+
23
+const mutations = {
24
+    ADD_TO_FETCHED_SEARCH_RESULTS(state, { searchResults }) {
25
+        console.log('adding results', searchResults)
26
+        state.all = [...state.all, ...searchResults]
27
+    },
28
+    STORE_FETCHED_SEARCH_RESULTS(state, { searchResults }) {
29
+        state.all = searchResults
30
+    },
31
+    CLEAR_SEARCH_RESULTS(state) {
32
+        state.all = []
33
+    },
34
+    SEARCH_RESULTS_LOADED(state, val) {
35
+        state.loaded = val
36
+    },
37
+}
38
+
39
+export default { state, getters, actions, mutations }

+ 4
- 2
vue-theme/src/utils/api.js Vedi File

12
     
12
     
13
     if(!params) return query
13
     if(!params) return query
14
 
14
 
15
-    if(params.limit && params.page) {
15
+    if(params.limit && params.page && params.s) {
16
+        query = `?s=${params.s}&limit=${params.limit}&p=${params.page}`
17
+    } else if(params.limit && params.page && !params.s) {
16
         query = `?limit=${params.limit}&p=${params.page}`
18
         query = `?limit=${params.limit}&p=${params.page}`
17
     } else if(params.limit && !params.page) {
19
     } else if(params.limit && !params.page) {
18
         query = `?limit=${params.limit}`
20
         query = `?limit=${params.limit}`
96
             .catch(e => {
98
             .catch(e => {
97
                 cb(e)
99
                 cb(e)
98
             })
100
             })
99
-    },
101
+    }
100
 }
102
 }

Loading…
Annulla
Salva