Bladeren bron

Merge branch '163-search' of craft-in-america/vue-wp into dev

tags/1.0.0
maeda 4 jaren geleden
bovenliggende
commit
021a8fa58c

+ 7
- 0
plugins/cia-endpoints/cia-end-points.php Bestand weergeven

@@ -21,6 +21,7 @@ require_once('includes/class.make-endpoint.php');
21 21
 require_once('includes/class.make-terms.php');
22 22
 require_once('includes/class.make-sticky.php');
23 23
 require_once('includes/class.make-sortby.php');
24
+require_once('includes/class.make-search.php');
24 25
 
25 26
 function _unsnake($input) {
26 27
     return str_replace('_', '-', $input);
@@ -57,6 +58,12 @@ add_action( 'rest_api_init', function () {
57 58
      */
58 59
     $terms_controller = new Make_Terms_Endpoint();
59 60
     $terms_controller->register_custom_route('terms');
61
+    
62
+    /**
63
+     * Search endpoint
64
+     */
65
+    $search_controller = new Make_Search_Endpoint();
66
+    $search_controller->register_custom_route('search');
60 67
 
61 68
     /**
62 69
      * Craft in America custom sort_types

+ 79
- 0
plugins/cia-endpoints/includes/class.make-search.php Bestand weergeven

@@ -0,0 +1,79 @@
1
+<?php
2
+// include('formats.php');
3
+
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
+    }
41
+    /**
42
+     * Register the routes for the objects of the controller.
43
+     */
44
+    public function register_custom_route($route) {
45
+        $version = '2';
46
+        $namespace = 'craft/v' . $version;
47
+
48
+        register_rest_route( $namespace, '/' . $route, [
49
+            array(
50
+                'methods'  => WP_REST_Server::READABLE,
51
+                'callback' => array( $this, 'get_results' )
52
+            ),
53
+        ]);
54
+    }
55
+    public function get_results( $request ) {
56
+        // https://www.relevanssi.com/user-manual/functions/relevanssi_do_query/
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;
69
+    }
70
+}
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 );
79
+?>

+ 1
- 0
plugins/cia-endpoints/includes/class.make-sticky.php Bestand weergeven

@@ -27,6 +27,7 @@ class Make_Sticky_Endpoint extends WP_REST_Controller {
27 27
                 'guide',
28 28
                 'object',
29 29
                 'publication',
30
+                'technique',
30 31
                 'short',
31 32
                 'post',
32 33
                 'page'

+ 0
- 2
vue-theme/src/components/hero.vue Bestand weergeven

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

+ 66
- 30
vue-theme/src/components/navigation/navigation.vue Bestand weergeven

@@ -1,7 +1,7 @@
1 1
 // Replace with calls to menu system
2 2
 <template lang="pug">
3 3
 nav.main.w-max
4
-    .menu
4
+    .menu.f-col
5 5
         ul.f-row.w-max
6 6
             li.f-row.start
7 7
                 router-link(to="/")
@@ -102,10 +102,12 @@ nav.main.w-max
102 102
                     li
103 103
                         router-link(to="/page/contact") Contact 
104 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 112
     .mobile-menu
111 113
         .f-row.start
@@ -124,11 +126,31 @@ nav.main.w-max
124 126
 </template>
125 127
 
126 128
 <script>
129
+import { computed, ref, nextTick } from '@vue/runtime-core'
130
+import { useRouter } from 'vue-router'
127 131
 import { postTypes, sortTypes } from '@/utils/helpers'
128
-import { computed } from '@vue/runtime-core'
129 132
 
130 133
 export default {
131 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 155
          * Navigation items
134 156
          * Auto-includes declared postTypes
@@ -161,7 +183,11 @@ export default {
161 183
         return {
162 184
             sortTypes,
163 185
             menuItems,
164
-            uncheck
186
+            uncheck,
187
+            searchTerms,
188
+            sendSearch,
189
+            toggleSearch,
190
+            isSearchOpen
165 191
         }
166 192
     },
167 193
 }
@@ -194,6 +220,19 @@ nav.main
194 220
                 height: $ms-3
195 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 236
     .menu   
198 237
         h1
199 238
             margin: $ms--7 0 0
@@ -203,44 +242,41 @@ nav.main
203 242
             margin: 0
204 243
         img
205 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 247
                 a
211 248
                     text-decoration: none
212
-            > li:nth-child(n+2)
249
+            &:nth-child(n+2)
213 250
                 a > h5
214 251
                     padding: $ms-1
215
-
216 252
                 //- menu hover
217 253
                 &:hover
218 254
                     color: $cia_red1
219 255
                     display: block 
220 256
                     cursor: pointer
221 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 267
                     display: block  
225
-                    position: absolute
226 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 277
                     &:hover 
231 278
                         display: block  
232 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 282
     .mobile-menu

+ 56
- 12
vue-theme/src/pages/list.vue Bestand weergeven

@@ -4,7 +4,8 @@
4 4
 
5 5
         header.center.t-up
6 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 9
                 span(v-if="sortBy")
9 10
                     h3 {{ sortBy.replace('-', ' ') }}
10 11
 
@@ -48,9 +49,11 @@ export default {
48 49
         return {
49 50
             page: 0,
50 51
             perPage: 21,
52
+            // Set this from route for initial search
53
+            searchTerm: this.$route.query.s,
51 54
             keepFetching: true,
52 55
             loadingFetched: false,
53
-            observer: null
56
+            observer: null,
54 57
         }
55 58
     },
56 59
     computed: {
@@ -63,6 +66,9 @@ export default {
63 66
         grid() {
64 67
             return gridTypes.includes(this.type)
65 68
         },
69
+        isSearch() {
70
+            return this.$route.path == '/search'
71
+        },
66 72
         isWide() {
67 73
             return wideTypes.includes(this.type)
68 74
         },
@@ -75,11 +81,11 @@ export default {
75 81
         },
76 82
         loaded() {
77 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 86
         posts() {
81 87
             if (!this.pType) return
82
-            return this[`all${this.pType}`]
88
+            return this.isSearch ? this['allSearchResults'] : this[`all${this.pType}`]
83 89
         },
84 90
         shouldLoadAllAtOnce() {
85 91
             return this.type == 'episode' ||
@@ -89,6 +95,29 @@ export default {
89 95
         }
90 96
     },
91 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 121
         async loadMorePosts(shouldClear) {
93 122
             if(!this.keepFetching) return console.warn('Nothing left to fetch...')
94 123
 
@@ -136,7 +165,7 @@ export default {
136 165
         async getPage(type) {
137 166
             await this._getAllIfNotLoaded('page', this.$store)
138 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 169
             if(!page) throw `No page: ${type} found. Cannot set hero.`
141 170
             return page
142 171
         },
@@ -152,7 +181,7 @@ export default {
152 181
                 this.$store.commit('SET_HERO', this._setHeroInfo(page))
153 182
             } catch (err) { console.error(err) }
154 183
         },
155
-        setIntersectionLoader() {
184
+        setIntersectionLoader(cb) {
156 185
             // KeepFetching is UNSET for certain post types and sort types in `loadMorePosts()`
157 186
             if(!this.keepFetching) return console.warn('Cannot setup intersection handler keepFetching is set false')
158 187
             
@@ -163,7 +192,7 @@ export default {
163 192
             this.observer = new IntersectionObserver(entries => {
164 193
                 entries.forEach(entry => {
165 194
                     if (!entry.isIntersecting || this.loadingFetched) return
166
-                    setTimeout(this.loadMorePosts, TIMEOUT)
195
+                    setTimeout(cb, TIMEOUT)
167 196
                 })
168 197
             }, { threshold: 0.80 })
169 198
             this.observer.observe(document.querySelector(INTERSECT_SELECTOR))
@@ -181,13 +210,28 @@ export default {
181 210
             this.page = 0
182 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 235
     watch: {
192 236
         // This only fires navigating from a list page, to another list page
193 237
         type(newType) {
@@ -203,7 +247,7 @@ export default {
203 247
             // console.log('sort changed :', newSort)
204 248
             if(!Object.values(sortTypes).includes(newSort)) return
205 249
             this.initPostList()
206
-        }
250
+        },
207 251
     },
208 252
     mounted() {
209 253
         // This only fires navigating from a non-list page > list page

+ 3
- 0
vue-theme/src/pages/mixin-post-types.js Bestand weergeven

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

+ 8
- 0
vue-theme/src/router/routes.js Bestand weergeven

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

+ 2
- 0
vue-theme/src/store/index.js Bestand weergeven

@@ -17,6 +17,7 @@ import guide from './modules/guide'
17 17
 import object from './modules/object'
18 18
 import publication from './modules/publication'
19 19
 import sticky from './modules/sticky'
20
+import searchResults from './modules/search'
20 21
 
21 22
 const debug = process.env.NODE_ENV !== 'production'
22 23
 
@@ -100,6 +101,7 @@ const store = new Vuex.Store({
100 101
         object,
101 102
         publication,
102 103
         sticky,
104
+        searchResults
103 105
     },
104 106
     strict: debug,
105 107
 })

+ 39
- 0
vue-theme/src/store/modules/search.js Bestand weergeven

@@ -0,0 +1,39 @@
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 Bestand weergeven

@@ -12,7 +12,9 @@ const constructQuery = (params) => {
12 12
     
13 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 18
         query = `?limit=${params.limit}&p=${params.page}`
17 19
     } else if(params.limit && !params.page) {
18 20
         query = `?limit=${params.limit}`
@@ -96,5 +98,5 @@ export default {
96 98
             .catch(e => {
97 99
                 cb(e)
98 100
             })
99
-    },
101
+    }
100 102
 }

Laden…
Annuleren
Opslaan