NEXT craftinamerica.org. Base setup for headless wordpress https://www.craftinamerica.org
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

list.vue 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <template lang="pug">
  2. .page--list.f-col.between
  3. article.f-grow
  4. header.center.t-up
  5. .title.f-row
  6. h3 {{ type }} list
  7. span(v-if="sortBy")
  8. h3 sorted {{ sortBy.replace('-', ' ') }}
  9. h3(v-if="!loaded") loading...
  10. .content(v-else-if="allPagesLoaded && type && allPages[type]" v-html="allPages[type].content")
  11. .posts(v-if="posts && loaded" :class="{ 'is-grid': grid }")
  12. section(v-for="(post, i) in posts" :key="post.slug").shadow.post
  13. card(:content="post" :type="type" :wide="type == 'exhibition' && i > 1 || type == 'event' && i > 1 ")
  14. footer
  15. p {{ `${type} count: ${Object.values(posts).length}` }}
  16. p {{ `show sidebar: ${sidebar}` }}
  17. sidebar(v-if="sidebar" :type="`${type}`" layout="list")
  18. </template>
  19. <script>
  20. import featuredImage from '@/components/featured-image'
  21. import card from '@/components/card'
  22. import sidebar from '@/components/sidebars/sidebar'
  23. import { postTypeGetters, scrollTop } from './mixin-post-types'
  24. import { convertTitleCase, typeFromRoute, sortTypes } from '@/utils/helpers'
  25. const TIMEOUT = 1
  26. export default {
  27. components: { sidebar, featuredImage, card },
  28. props: {
  29. sidebar: { type: Boolean },
  30. grid: { type: Boolean },
  31. sortBy: { type: String },
  32. },
  33. mixins: [postTypeGetters, scrollTop],
  34. data() {
  35. return {
  36. page: 1,
  37. perPage: 9
  38. }
  39. },
  40. computed: {
  41. type() {
  42. // Checks for type and fixes Episodes route edge case
  43. return typeFromRoute(this.$route)
  44. },
  45. pType() {
  46. console.log(this.type, `${convertTitleCase(this.type)}s`)
  47. return this.sortBy ? `${convertTitleCase(this.type.split('/')[0])}s` : `${convertTitleCase(this.type)}s`
  48. },
  49. loaded() {
  50. if (!this.pType) return
  51. return this[`all${this.pType}Loaded`]
  52. },
  53. posts() {
  54. if (!this.pType) return
  55. return this[`all${this.pType}`]
  56. },
  57. },
  58. methods: {
  59. loadMorePosts() {
  60. // console.log('loading...')
  61. this.page++
  62. this.getPosts(false)
  63. },
  64. async getPosts(clear) {
  65. // Limit
  66. let params = {
  67. limit: this.perPage,
  68. page: this.page
  69. }
  70. // Sorting
  71. let sort = this.sortBy
  72. ? this.sortBy
  73. : this.$route.path
  74. .split('/')
  75. .filter(p => p)
  76. .pop()
  77. let dispatchParams = { sortType: sort, params }
  78. let dispatchAction = `getAll${this.pType}`
  79. let res = null
  80. if(clear) {
  81. dispatchParams.sortType = sortTypes.currentAndUpcoming
  82. res = await this.$store.dispatch(dispatchAction, dispatchParams)
  83. }
  84. // Add to existing except episodes
  85. else if (this.type != 'episode') {
  86. dispatchAction = `getMore${this.pType}`
  87. res = await this.$store.dispatch(dispatchAction, dispatchParams)
  88. }
  89. console.log('responded:', res)
  90. if(!res.length) {
  91. console.warn('nothing left to get...')
  92. }
  93. },
  94. clearHero() {
  95. this.$store.commit('SET_HERO', { url: null, heroType: null })
  96. },
  97. async checkAndSetHero(type) {
  98. if (!this['allPagesLoaded']) {
  99. await this.$store.dispatch('getAllPages', { sortType: null, params: null })
  100. }
  101. // We always set a hero no matter what
  102. // Because the hero component will deal
  103. // with how to render based on hero.url
  104. if(!this.allPages) return console.log('no pages in state', this)
  105. const page = this.allPages.filter(
  106. page => page.slug == type + 's',
  107. )[0]
  108. // Clear the hero and bail
  109. if(!page) return this.clearHero()
  110. let json = { url: page.featured, heroType:'image' }
  111. if (
  112. page.hero &&
  113. JSON.parse(page.hero) &&
  114. JSON.parse(page.hero).url
  115. ) {
  116. json = JSON.parse(page.hero)
  117. heson.heroType = 'video'
  118. }
  119. // No featured or youTube
  120. if (!json.url) { json.heroType = null }
  121. // Set the hero text to the post title or excerpt
  122. json.text = page && page.excerpt ? page.excerpt : page.title
  123. this.$store.commit('SET_HERO', json)
  124. },
  125. scrollTo(hashtag) {
  126. setTimeout(() => {
  127. location.href = hashtag
  128. }, TIMEOUT)
  129. },
  130. setIntersectionLoader() {
  131. console.warn('setting up intersection handler for:', this.type)
  132. window.removeEventListener("load", e => {}, false)
  133. const footerEl = document.querySelector(".footer-wrapper footer")
  134. if(!footerEl) return
  135. window.addEventListener("load", e => {
  136. const observer = new IntersectionObserver(this.loadMorePosts, {
  137. rootMargin: "0px 0px -20px 0px",
  138. })
  139. observer.observe(footerEl)
  140. }, false)
  141. }
  142. },
  143. watch: {
  144. $route(to) {
  145. // Filter the path to remove blank strings and
  146. // only grab the hero if moving to another list page
  147. // eg. list page -> list page
  148. const path = to.fullPath.split('/').filter(p => p)
  149. console.log('to:', path)
  150. // !:Only fires navigating TO list page
  151. if (path.length > 1) return
  152. this.clearHero()
  153. this.checkAndSetHero(this.type)
  154. // TODO: Track last loaded page per post type
  155. // Less http calls
  156. this.getPosts(true)
  157. this.page = 1
  158. this.setIntersectionLoader()
  159. },
  160. },
  161. mounted() {
  162. if (this.$route.hash) {
  163. setTimeout(() => this.scrollTo(this.$route.hash), TIMEOUT)
  164. }
  165. this.setIntersectionLoader()
  166. },
  167. created() {
  168. this.checkAndSetHero(this.type)
  169. // We also need to check if only ONE has been loaded because
  170. // coming from the homepage there will only be 1 item
  171. if (!this[`all${this.pType}Loaded`] || this[`all${this.pType}`].length < 2) this.getPosts(true)
  172. },
  173. }
  174. </script>
  175. <style lang="postcss">
  176. // prettier-ignore
  177. @import '../sss/variables.sss'
  178. @import '../sss/theme.sss'
  179. .page--list
  180. /* background-color: white */
  181. article
  182. > header
  183. /* padding: 1em 0 1em 0 */
  184. padding: 1em
  185. > h1
  186. margin: 0
  187. > .content
  188. padding: 0
  189. width: 100%
  190. /* posts not grid list */
  191. ul
  192. img
  193. max-width: 50%
  194. .is-grid
  195. display: flex
  196. flex-direction: row
  197. flex-wrap: wrap
  198. justify-content: space-between
  199. /* extra padding required? */
  200. /* padding: $ms-0 */
  201. /* background-color: white */
  202. section
  203. width: 32.5%
  204. /* 4 column grid see how dense this becomes */
  205. /* width: 24% */
  206. ul
  207. flex-wrap: wrap
  208. list-style: none
  209. img
  210. max-width: 100%
  211. @media (min-width: $medium)
  212. .page--list.f-col
  213. flex-direction: row
  214. </style>