NEXT craftinamerica.org. Base setup for headless wordpress https://www.craftinamerica.org
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

list.vue 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. // Sorting
  66. let sort = this.sortBy
  67. ? this.sortBy
  68. : this.$route.path
  69. .split('/')
  70. .filter(p => p)
  71. .pop()
  72. const dispatchParams = {
  73. sortType: sort,
  74. params: {
  75. limit: this.perPage,
  76. page: this.page
  77. }
  78. }
  79. const dispatchAction = clear ? `getAll${this.pType}` : `getMore${this.pType}`
  80. // pType sometimes is undefined so need to dispatch and
  81. // Episodes only needs load call
  82. let res = null
  83. if(this.pType && dispatchAction != `getMoreEpisodes`) {
  84. res = await this.$store.dispatch(dispatchAction, dispatchParams)
  85. }
  86. if(res && !res.length) {
  87. console.warn('nothing left to get...')
  88. }
  89. },
  90. clearHero() {
  91. this.$store.commit('SET_HERO', { url: null, heroType: null })
  92. },
  93. async checkAndSetHero(type) {
  94. if (!this['allPagesLoaded']) {
  95. await this.$store.dispatch('getAllPages', { sortType: null, params: null })
  96. }
  97. // We always set a hero no matter what
  98. // Because the hero component will deal
  99. // with how to render based on hero.url
  100. if(!this.allPages) return console.log('no pages in state', this)
  101. const page = this.allPages.filter(
  102. page => page.slug == type + 's',
  103. )[0]
  104. // Clear the hero and bail
  105. if(!page) return this.clearHero()
  106. let json = { url: page.featured, heroType:'image' }
  107. if (
  108. page.hero &&
  109. JSON.parse(page.hero) &&
  110. JSON.parse(page.hero).url
  111. ) {
  112. json = JSON.parse(page.hero)
  113. json.heroType = 'video'
  114. }
  115. // No featured or youTube
  116. if (!json.url) { json.heroType = null }
  117. // Set the hero text to the post title or excerpt
  118. json.text = page && page.excerpt ? page.excerpt : page.title
  119. this.$store.commit('SET_HERO', json)
  120. },
  121. scrollTo(hashtag) {
  122. setTimeout(() => {
  123. location.href = hashtag
  124. }, TIMEOUT)
  125. },
  126. setIntersectionLoader() {
  127. console.warn('setting up intersection handler for:', this.type)
  128. window.removeEventListener("load", e => {}, false)
  129. const footerEl = document.querySelector(".footer-wrapper footer")
  130. if(!footerEl) return
  131. window.addEventListener("load", e => {
  132. const observer = new IntersectionObserver(this.loadMorePosts, {
  133. rootMargin: "0px 0px -20px 0px",
  134. })
  135. observer.observe(footerEl)
  136. }, false)
  137. }
  138. },
  139. watch: {
  140. $route(to) {
  141. // Filter the path to remove blank strings and
  142. // only grab the hero if moving to another list page
  143. // eg. list page -> list page
  144. const path = to.fullPath.split('/').filter(p => p)
  145. console.log('to:', path)
  146. console.log('?:', path.pop())
  147. console.log('?:', path.pop())
  148. // Always reset the page count
  149. this.setIntersectionLoader()
  150. this.page = 1
  151. this.clearHero()
  152. this.checkAndSetHero(this.type)
  153. // TODO: Track last loaded page per post type
  154. // Less http calls
  155. this.getPosts(true)
  156. },
  157. },
  158. mounted() {
  159. if (this.$route.hash) {
  160. setTimeout(() => this.scrollTo(this.$route.hash), TIMEOUT)
  161. }
  162. this.setIntersectionLoader()
  163. },
  164. created() {
  165. this.checkAndSetHero(this.type)
  166. // We also need to check if only ONE has been loaded because
  167. // coming from the homepage there will only be 1 item
  168. if (!this[`all${this.pType}Loaded`] || this[`all${this.pType}`].length < 2) this.getPosts(true)
  169. },
  170. }
  171. </script>
  172. <style lang="postcss">
  173. // prettier-ignore
  174. @import '../sss/variables.sss'
  175. @import '../sss/theme.sss'
  176. .page--list
  177. /* background-color: white */
  178. article
  179. > header
  180. /* padding: 1em 0 1em 0 */
  181. padding: 1em
  182. > h1
  183. margin: 0
  184. > .content
  185. padding: 0
  186. width: 100%
  187. /* posts not grid list */
  188. ul
  189. img
  190. max-width: 50%
  191. .is-grid
  192. display: flex
  193. flex-direction: row
  194. flex-wrap: wrap
  195. justify-content: space-between
  196. /* extra padding required? */
  197. /* padding: $ms-0 */
  198. /* background-color: white */
  199. section
  200. width: 32.5%
  201. /* 4 column grid see how dense this becomes */
  202. /* width: 24% */
  203. ul
  204. flex-wrap: wrap
  205. list-style: none
  206. img
  207. max-width: 100%
  208. @media (min-width: $medium)
  209. .page--list.f-col
  210. flex-direction: row
  211. </style>