| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- <template lang="pug">
- .page--single.f-col.between
- article.w-max.f-grow.shadow(v-if="!singlePost || loading")
- header
- p loading...
- article(v-else).w-max.f-grow.shadow
- header
- //- breadcrumb links at top of page, needs link routing
- breadcrumb(:type="type" :post="singlePost")
-
- h1.t-b {{ singlePost.title }}
- .date-info.t-cntr(v-if="['exhibition', 'event'].includes(type)")
- //- for events display: date, time-time
- h4(v-if="singlePost.start, singlePost.end && type == 'event'") {{ dateFrom(singlePost.start, type == 'event') }} - {{ dateFrom(singlePost.end, type == 'event').split(',')[1] }}
- //- else for single, exhibition: date-date
- h4(v-else-if="singlePost.start, singlePost.end") {{ dateFrom(singlePost.start, type == 'event') }} - {{ dateFrom(singlePost.end, type == 'event') }}
-
- //- WP main content
- section.content(v-html="singlePost.content")
-
- //- related artists section for episodes
- template(v-if="type === 'episode' && post")
- section.related-artists
- h2.t-up featured in this episode
- ul
- li.f-row.between(v-for="artist in p2pPostsByType['artist']")
- card(:content="artist" type="artist" :wide="true" :hide-type="true")
-
- credits(:post="singlePost")
-
- //- end of article icon
- footer.f-col
- img(src="../star.svg")
-
- vue-easy-lightbox(
- :visible="activeGalleryIndex >= 0"
- :imgs="activeGalleryImages"
- :index="activeImageIndex"
- @hide="activeGalleryIndex = -1"
- )
-
- sidebar(:type="`${type}`" layout="single" :related="p2pPostsByType")
- </template>
-
- <script>
- import card from '@/components/card.vue'
- import sidebar from '@/components/sidebars/sidebar'
- import gallery from '@/components/gallery/'
- import credits from '@/components/credits'
- import breadcrumb from '@/components/breadcrumb'
-
- import { postTypeGetters, scrollTop, heroUtils } from './mixin-post-types'
-
- import { postTypes, convertTitleCase, formatDate } from '@/utils/helpers'
- import { nextTick } from '@vue/runtime-core'
-
- export default {
- components: { sidebar, gallery, credits, card, breadcrumb },
- mixins: [postTypeGetters, scrollTop, heroUtils],
- data() {
- return {
- activeGalleryIndex: -1,
- activeImageIndex: 0,
- loading: true,
- }
- },
- computed: {
- type() {
- return postTypes.includes(this.$route.params.type)
- ? this.$route.params.type
- : 'post'
- },
- slug() {
- return this.$route.params.slug
- },
- /**
- * We get the actual post data using the slug
- * Careful with name collisions with vuex helpers
- */
- singlePost() {
- const postType = this.type == 'blog' ? 'post' : this.type
- if (!this[postType]) return
-
- // State not a getter!
- const singleOfTypeFromState =
- this[postType][`single${convertTitleCase(postType)}`]
-
- return singleOfTypeFromState ? singleOfTypeFromState : {}
- },
- singlePostGalleries() {
- if (!this.singlePost.galleries) return
- const galleries = []
- this.singlePost.galleries.forEach(gallery => {
- if (!gallery.ids) return
- const withImages = gallery.ids.map(
- imageId => this.singlePost.attached[imageId],
- )
- galleries.push(withImages)
- })
- return galleries
- },
- activeGalleryImages() {
- if (!this.singlePostGalleries || this.activeGalleryIndex < 0)
- return []
- return this.singlePostGalleries[this.activeGalleryIndex]
- },
- p2pPostsByType() {
- return this.singlePost && this.singlePost.relatedto
- ? Object.values(this.singlePost.relatedto).reduce(
- (byType, relatedPost) => {
- if (!byType[relatedPost.type])
- byType[relatedPost.type] = []
- byType[relatedPost.type].push(relatedPost)
- return byType
- },
- {},
- )
- : {}
- },
- },
- methods: {
- // _setHeroInfo(post) {} from mixin
- // _clearHero(store) {} from mixin
- /**
- * Everytime the post object changes
- * we use this to set a new HERO
- * in vuex
- * @param {object} post
- */
- checkAndSetHero(post) {
- this._clearHero(this.$store)
- if (!post) throw `No post found. Cannot set hero.`
- this.$store.commit('SET_HERO', this._setHeroInfo(post))
- },
-
- /**
- * Date Object from unix strings from db
- */
- dateFrom: (unix, includeTime) => formatDate(unix, includeTime),
-
- async loadPostData() {
- this.loading = true
- /**
- * Conditionally load based on post type
- * which is derived from the route
- */
- // modules are NOT plural because module key
- const postType = this.type == 'blog' ? 'post' : this.type
- if (!this.$store.state[postType]) return
- const allPostsOfTypeInStore = this.$store.state[postType].all
-
- /**
- * Load posts if they're not already in state
- */
- // Find the single post from api if it's not already in state
- // Then add it to our list
- let singlePostData = allPostsOfTypeInStore.filter(
- post => post.slug == this.slug,
- )[0]
-
- // Look if it exists before you try and load everything!
- if (!singlePostData) {
- console.warn(
- 'Could not find single post in store; Fetching everything...',
- )
- const res = await this.$store.dispatch(
- `getAll${convertTitleCase(postType)}s`,
- { sortType: null, params: null },
- )
- singlePostData = res.filter(post => post.slug == this.slug)[0]
- }
-
- /**
- * At the point we MUST have singlePostData
- */
- try {
- this.checkAndSetHero(singlePostData)
- this.$store.dispatch(
- `getSingle${convertTitleCase(postType)}`,
- singlePostData.id,
- )
- } catch (err) {
- console.error(err)
- }
- this.loading = false
- },
- _setClick(el, cb) {
- if (el.addEventListener) {
- el.addEventListener('click', cb, false)
- } else {
- el.attachEvent('onclick', cb)
- }
- },
- },
- watch: {
- slug(newSlug, oldSlug) {
- // ONLY load post data when navigating TO a single page
- // OR when navigating TO a single page from a single page
- if ((newSlug && !oldSlug) || (newSlug && oldSlug)) {
- this._clearHero(this.$store)
- this.loadPostData()
- }
- },
- async singlePost(post) {
- if (!this.$el) return
- const section = this.$el.children[0].querySelector('section')
- await nextTick()
- const galleryBlocks = section.querySelectorAll('.wp-block-gallery')
- galleryBlocks.forEach((block, blockIndex) => {
- block.querySelectorAll('img').forEach(image => {
- image.dataset.gallery = blockIndex
- this._setClick(image, e => {
- this.activeGalleryIndex = e.target.dataset.gallery
- const activeGallery =
- post.galleries[this.activeGalleryIndex]
-
- if (!activeGallery.ids) return
-
- this.activeImageIndex = activeGallery.ids.indexOf(
- parseInt(e.target.dataset.id),
- )
- })
- })
- })
- },
- },
- created() {
- this.loadPostData()
- },
- beforeDestroy() {},
- }
- </script>
-
- <style lang="postcss">
- // prettier-ignore
- @import '../sss/variables.sss'
- @import '../sss/theme.sss'
-
- .page--single
- article
- background-color: white
- padding: $ms-0
- h1
- /* color: $cia_black */
- margin: 0 0 $ms--3 0
- > ul
- /* grid-gap: $ms-0 */
- list-style: none
- /* change to a 1/3 width of the article*/
- img.feature
- width: 20em
-
- /* wp-block-embed youtube link */
- .wp-block-embed, .is-type-video
- position: relative
- width: 100%
- padding-bottom: 56.25%
- margin-bottom: $ms-7
- &__wrapper
- display: contents
- /* TBD if kept- edit ot test */
- figcaption
- position: absolute
- top: 100%
-
- .wp-block-gallery
- margin: 0 0 0.5em 0
- grid-gap: $ms--5
- > .wp-block-image
- figcaption
- position: inherit
- background: none
- font-size: $ms--1
- color: $cia_black
- max-height: 9%
- overflow: visible
- padding: 0
- margin-bottom: 2.5em
- figcaption
- flex-direction: column
-
- /* iframe container 16:9 */
- [class^="iframe-container"]
- position: relative
- padding-bottom: 56.25%
- /* width: 100% */
- /* iframe container portrait */
- .iframe-container-v
- height: 100%
- padding-bottom: 125%
- iframe
- position: absolute
- top: 0
- left: 0
- width: 100%
- height: 100%
-
- /* separator styles */
- * hr
- margin: $ms-2 auto
- &.is-style
- &-default
- width: 15vw
- &-wide
- width: 50vw
- &-dots::before
- outline-style: none
- font-weight: bolder
- letter-spacing: $ms-8
- padding-left: $ms-8
- /* margin to indent marker */
- li
- margin: 0 0 $ms--2 $ms-4
-
- .related-artists
- ul li
- margin: 0 0 $ms-0 0
- .card
- padding: 0 0 $ms--2
- min-height: auto
-
- breadcrumb
- h5
- color: $cia_red
-
- //- end of article icon
- footer
- padding: $ms-6 0
- img
- height: $ms-3
- width: $ms-3
-
-
- @media (min-width: $medium)
- .page--single
- > article
- margin: 0 0.65em 0 0
- &.f-col
- flex-direction: row
- .wp-block-gallery
- .wp-block-image
- figcaption
- max-height: 5%
- margin-bottom: $ms-4
- </style>
|