NEXT craftinamerica.org. Base setup for headless wordpress https://www.craftinamerica.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

single.vue 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template lang="pug">
  2. .page--single.f-row.between
  3. gallery(v-if="activeGalleryIndex >= 0" :activeImageIndex="activeImageIndex" :images="imagesInGallery" @close="closeGallery")
  4. article(v-if="!post")
  5. header
  6. p loading...
  7. article(v-else).f-grow.shadow
  8. header
  9. h1 {{ type }}:{{ $route.params.slug }} {{ post.title }}
  10. p(v-if="post.categories") categories: {{ post.categories }}
  11. p(v-if="post.type") type: {{ post.type }}
  12. p(v-if="post.subtypes") subtypes: {{ post.subtypes }}
  13. .date-info(v-if="type === 'events' || post.type === 'exhibitions'")
  14. p start: {{ dateFrom(post.start) }}
  15. p end: {{ dateFrom(post.end) }}
  16. ul
  17. li(v-for="(block, index) in post.blocks" :key="`block-${index}`" class="post-single block-wrapper")
  18. block(:block="block" @open-gallery="openGallery")
  19. //- related artists section example layout
  20. section(v-if="type === 'episodes' && post" :post="post")
  21. h2.t-up featured in this episode
  22. ul.f-row.between
  23. img(src="https://i1.wp.com/www.craftinamerica.org/wp-content/uploads/2020/09/20200210_133120-e1599254267307.jpg")
  24. li.f-col.between
  25. h2.t-up Julie Schafler Dale
  26. p Julie Schafler Dale was the founder and President of Julie: Artisans’ Gallery, New York, which conducted business on Madison Avenue for over forty years. She has served on the Advisory Council for The...
  27. ul.f-row.between
  28. img(src="https://i1.wp.com/www.craftinamerica.org/wp-content/uploads/2020/09/P1033899.jpg")
  29. li.f-col.between
  30. h2.t-up George Rodriguez
  31. p George Rodriguez is a Seattle-based ceramic artist and sculptor who, throughout his career, has used oversized ceramic personalities he creates to tell universal stories. He was born and raised in El Paso, where...
  32. credits(v-if="type === 'episodes' && post" :post="post")
  33. sidebar(v-if="sidebar" :type="`${type}`" layout="single" :related="p2pPostsByType")
  34. </template>
  35. <script>
  36. import sidebar from '@/components/sidebars/sidebar'
  37. import gallery from '@/components/gallery/'
  38. import block from '@/components/content-block/block'
  39. // import artists from '@/components/artist'
  40. import credits from '@/components/credits'
  41. import { postTypeGetters, scrollTop } from './mixin-post-types'
  42. import { convertTitleCase, dePluralize, typeFromRoute } from '@/utils/helpers'
  43. export default {
  44. components: { sidebar, gallery, block, credits },
  45. props: {
  46. sidebar: { type: Boolean },
  47. id: { type: Number }
  48. },
  49. mixins: [postTypeGetters, scrollTop],
  50. data() {
  51. return {
  52. // Gallery control
  53. activeGalleryIndex: -1,
  54. activeImageID: -1
  55. }
  56. },
  57. computed: {
  58. type() { // Checks for type and fixes Episodes route edge case
  59. return typeFromRoute(this.$route)
  60. },
  61. /**
  62. * We get the actual post data using the slug
  63. */
  64. post() {
  65. if(!this[this.type]) return
  66. const type = dePluralize(this.type)
  67. // State not a getter!
  68. const singleOfTypeFromState = this[this.type][`single${convertTitleCase(type)}`]
  69. if(!singleOfTypeFromState) return
  70. return singleOfTypeFromState
  71. },
  72. idsForGallery() {
  73. if(!this.post || this.activeGalleryIndex < 0) return []
  74. return this.post.galleries[this.activeGalleryIndex].ids
  75. },
  76. /**
  77. * We need a convenient way to get all the images
  78. * broken down by gallery. We use the active gallery
  79. * image IDs to create a map. We match the ID to the
  80. * image size and url information returned by post.attached
  81. */
  82. imagesInGallery() {
  83. if(!this.activeGalleryIndex < 0) return {}
  84. return this.idsForGallery.reduce((imageMap, id) => {
  85. imageMap[id] = this.post.attached[parseInt(id)]
  86. return imageMap
  87. }, {})
  88. },
  89. activeImageIndex() {
  90. return Object.keys(this.imagesInGallery).indexOf(this.activeImageID.toString())
  91. },
  92. p2pPostsByType() {
  93. return this.post ? Object.values(this.post.relatedto).reduce((byType, relatedPost) => {
  94. if(!byType[relatedPost.type]) byType[relatedPost.type] = []
  95. byType[relatedPost.type].push(relatedPost)
  96. return byType
  97. }, {}) : {}
  98. }
  99. },
  100. methods: {
  101. /**
  102. * We set the active gallery to the index.
  103. * Everything kicks off when activeGallery
  104. * is set. We also need to set the activeImageID
  105. * to the image clicked
  106. * @param {string} imageInfo
  107. */
  108. openGallery(imageInfo) {
  109. const byIndex = this.post.galleries.reduce((byIndex, gallery, index) => {
  110. byIndex[index] = gallery.ids
  111. return byIndex
  112. }, {})
  113. let matchingIndex = 0
  114. Object.keys(byIndex).forEach(galleryIndex => {
  115. if(byIndex[galleryIndex].includes(parseInt(imageInfo.dataset.id))) matchingIndex = galleryIndex
  116. })
  117. this.activeGalleryIndex = matchingIndex
  118. this.activeImageID = imageInfo.dataset.id ? parseInt(imageInfo.dataset.id) : parseInt(imageInfo.className.split('-').pop())
  119. },
  120. closeGallery() {
  121. this.activeGalleryIndex = -1
  122. this.activeImageID = -1
  123. },
  124. /**
  125. * Everytime the posts object changes
  126. * we use this to set a new HERO
  127. * in vuex
  128. * @param {object} posts
  129. */
  130. checkAndSetHero(post) {
  131. if(!post) return
  132. const json = { url: post.featured, heroType: 'image' }
  133. if(post.hero && JSON.parse(post.hero).url) {
  134. json = JSON.parse(post.hero)
  135. json.heroType = 'video'
  136. }
  137. this.$store.commit('SET_HERO', json)
  138. },
  139. /**
  140. * Date Object from unix strings from db
  141. */
  142. dateFrom(unix) {
  143. return new Date( parseInt(unix) * 1000 )
  144. },
  145. async loadPostData() {
  146. /**
  147. * Conditionally load based on post type
  148. * which is derived from the route
  149. * !: posts watcher fires when this finishes
  150. */
  151. let type = convertTitleCase(this.type)
  152. if(!this.$store.state[this.type]) return
  153. let allPostsOfType = this.$store.state[this.type].all
  154. /**
  155. * Load posts if they're not already in state
  156. */
  157. if(!this[`all${type}Loaded`] && allPostsOfType.length < 1) {
  158. const res = await this.$store.dispatch(`getAll${type}`)
  159. allPostsOfType = res
  160. // console.log(`reloaded ${type}...`)
  161. }
  162. if(Object.values(allPostsOfType).length < 1) return
  163. const singlePostData = Object.values(allPostsOfType).filter(post => post.slug == this.$route.params.slug)[0]
  164. if(!singlePostData) return
  165. await this.$store.dispatch(`getSingle${dePluralize(type)}`, singlePostData.id)
  166. }
  167. },
  168. watch: {
  169. post(newVal, oldVal) {
  170. this.checkAndSetHero(newVal)
  171. },
  172. $route(newVal, oldVal) {
  173. this.loadPostData()
  174. }
  175. },
  176. async created() {
  177. this.loadPostData()
  178. }
  179. }
  180. </script>
  181. <style lang="postcss">
  182. @import '../sss/variables.sss'
  183. @import '../sss/theme.sss'
  184. .page--single
  185. article
  186. ul
  187. grid-gap: $ms-0
  188. list-style: none
  189. img
  190. width: 50%
  191. li
  192. /* responsive iframe embeds */
  193. /* .iframe-container
  194. position: relative
  195. width: 100%
  196. padding-bottom: 56.25%
  197. .iframe-container iframe
  198. position: absolute
  199. top: 0px
  200. left: 0px
  201. width: 100%
  202. height: 100% */
  203. @media (--large-viewport)
  204. .sidebar
  205. background-color: purple
  206. </style>