page.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template lang="pug">
  2. v-app(v-scroll='upBtnScroll', :dark='darkMode')
  3. nav-header
  4. v-navigation-drawer(
  5. :class='darkMode ? `grey darken-3` : `primary`'
  6. dark
  7. app
  8. clipped
  9. :mini-variant='$vuetify.breakpoint.md || $vuetify.breakpoint.sm'
  10. mini-variant-width='80'
  11. mobile-break-point='600'
  12. :temporary='$vuetify.breakpoint.xs'
  13. v-model='navShown'
  14. )
  15. vue-scroll(:ops='scrollStyle')
  16. nav-sidebar(:color='darkMode ? `grey darken-3` : `primary`')
  17. slot(name='sidebar')
  18. v-content
  19. template(v-if='path !== `home`')
  20. v-toolbar(:color='darkMode ? `grey darken-4-d3` : `grey lighten-3`', flat, dense)
  21. v-btn.pl-0(v-if='$vuetify.breakpoint.xsOnly', flat, @click='toggleNavigation')
  22. v-icon(color='grey darken-2', left) menu
  23. span Navigation
  24. v-breadcrumbs.breadcrumbs-nav.pl-0(
  25. v-else
  26. :items='breadcrumbs'
  27. divider='/'
  28. )
  29. template(slot='item', slot-scope='props')
  30. v-icon(v-if='props.item.path === "/"', small) home
  31. v-btn.ma-0(v-else, :href='props.item.path', small, flat) {{props.item.name}}
  32. template(v-if='!isPublished')
  33. v-spacer
  34. .caption.red--text Unpublished
  35. status-indicator.ml-3(negative, pulse)
  36. v-divider
  37. v-layout(row)
  38. v-flex(xs12, lg9, xl10)
  39. v-toolbar(:color='darkMode ? `grey darken-4` : `grey lighten-4`', flat, :height='90')
  40. div
  41. .headline.grey--text(:class='darkMode ? `text-lighten-2` : `text--darken-3`') {{title}}
  42. .caption.grey--text.text--darken-1 {{description}}
  43. v-divider
  44. .contents(ref='container')
  45. slot(name='contents')
  46. v-flex(lg3, xl2, fill-height, v-if='$vuetify.breakpoint.lgAndUp')
  47. v-toolbar(:color='darkMode ? `grey darken-4` : `grey lighten-4`', flat, :height='90')
  48. div
  49. .caption.grey--text.text--lighten-1 Last edited by
  50. .body-2.grey--text(:class='darkMode ? `` : `text--darken-3`') {{ authorName }}
  51. .caption.grey--text.text--darken-1 {{ updatedAt | moment('calendar') }}
  52. v-spacer
  53. v-tooltip(left)
  54. v-btn.btn-animate-edit(icon, slot='activator', :href='"/e/" + path')
  55. v-icon(color='grey') edit
  56. span Edit Page
  57. v-divider
  58. template(v-if='toc.length')
  59. v-list.grey.pb-3(dense, :class='darkMode ? `darken-3-d3` : `lighten-3`')
  60. v-subheader.pl-4(:class='darkMode ? `blue--text text--lighten-1` : `primary--text`') Table of contents
  61. template(v-for='(tocItem, tocIdx) in toc')
  62. v-list-tile(@click='$vuetify.goTo(tocItem.anchor, scrollOpts)')
  63. v-icon(color='grey') arrow_right
  64. v-list-tile-title.pl-3 {{tocItem.title}}
  65. v-divider.ml-4(v-if='tocIdx < toc.length - 1 || tocItem.children.length')
  66. template(v-for='tocSubItem in tocItem.children')
  67. v-list-tile(@click='$vuetify.goTo(tocSubItem.anchor, scrollOpts)')
  68. v-icon.pl-3(color='grey lighten-1') arrow_right
  69. v-list-tile-title.pl-3.caption {{tocSubItem.title}}
  70. v-divider(inset, v-if='tocIdx < toc.length - 1')
  71. v-divider
  72. v-list.grey(dense, :class='darkMode ? `darken-3` : `lighten-4`')
  73. v-subheader.pl-4.yellow--text.text--darken-4 Rating
  74. .text-xs-center
  75. v-rating(
  76. v-model='rating'
  77. color='yellow darken-3'
  78. background-color='grey lighten-1'
  79. half-increments
  80. hover
  81. )
  82. .pb-2.caption.grey--text 5 votes
  83. v-divider
  84. template(v-if='tags.length')
  85. v-list.grey(dense, :class='darkMode ? `darken-3-d3` : `lighten-3`')
  86. v-subheader.pl-4.teal--text Tags
  87. template(v-for='(tag, idx) in tags')
  88. v-list-tile(:href='`/t/` + tag.slug')
  89. v-list-tile-avatar: v-icon(color='teal') label
  90. v-list-tile-title {{tag.title}}
  91. v-divider(inset, v-if='idx < tags.length - 1')
  92. v-divider
  93. v-toolbar(:color='darkMode ? `grey darken-3` : `grey lighten-4`', flat, dense)
  94. v-spacer
  95. v-tooltip(bottom)
  96. v-btn(icon, slot='activator'): v-icon(color='grey') bookmark
  97. span Bookmark
  98. v-tooltip(bottom)
  99. v-btn(icon, slot='activator'): v-icon(color='grey') share
  100. span Share
  101. v-tooltip(bottom)
  102. v-btn(icon, slot='activator'): v-icon(color='grey') print
  103. span Print Format
  104. v-spacer
  105. nav-footer
  106. v-fab-transition
  107. v-btn(v-if='upBtnShown', fab, fixed, bottom, right, small, @click='$vuetify.goTo(0, scrollOpts)', color='primary')
  108. v-icon arrow_upward
  109. </template>
  110. <script>
  111. import { StatusIndicator } from 'vue-status-indicator'
  112. import Prism from '@/libs/prism/prism.js'
  113. import { get } from 'vuex-pathify'
  114. export default {
  115. components: {
  116. StatusIndicator
  117. },
  118. props: {
  119. locale: {
  120. type: String,
  121. default: 'en'
  122. },
  123. path: {
  124. type: String,
  125. default: 'home'
  126. },
  127. title: {
  128. type: String,
  129. default: 'Untitled Page'
  130. },
  131. description: {
  132. type: String,
  133. default: ''
  134. },
  135. createdAt: {
  136. type: String,
  137. default: ''
  138. },
  139. updatedAt: {
  140. type: String,
  141. default: ''
  142. },
  143. tags: {
  144. type: Array,
  145. default: () => ([])
  146. },
  147. authorName: {
  148. type: String,
  149. default: 'Unknown'
  150. },
  151. authorId: {
  152. type: Number,
  153. default: 0
  154. },
  155. isPublished: {
  156. type: Boolean,
  157. default: false
  158. },
  159. toc: {
  160. type: Array,
  161. default: () => []
  162. }
  163. },
  164. data() {
  165. return {
  166. navOpen: false,
  167. upBtnShown: false,
  168. scrollOpts: {
  169. duration: 1500,
  170. offset: -75,
  171. easing: 'easeInOutCubic'
  172. },
  173. breadcrumbs: [
  174. { path: '/', name: 'Home' },
  175. { path: '/universe', name: 'Universe' },
  176. { path: '/universe/galaxy', name: 'Galaxy' },
  177. { path: '/universe/galaxy/solar-system', name: 'Solar System' },
  178. { path: '/universe/galaxy/solar-system/planet-earth', name: 'Planet Earth' }
  179. ],
  180. scrollStyle: {
  181. vuescroll: {},
  182. scrollPanel: {
  183. initialScrollX: 0.01, // fix scrollbar not disappearing on load
  184. scrollingX: false,
  185. speed: 50
  186. },
  187. rail: {
  188. gutterOfEnds: '2px'
  189. },
  190. bar: {
  191. onlyShowBarOnScroll: false,
  192. background: '#42A5F5',
  193. hoverStyle: {
  194. background: '#64B5F6'
  195. }
  196. }
  197. }
  198. }
  199. },
  200. computed: {
  201. darkMode: get('site/dark'),
  202. navShown: {
  203. get() { return this.navOpen || this.$vuetify.breakpoint.smAndUp },
  204. set(val) { this.navOpen = val }
  205. },
  206. rating: {
  207. get () {
  208. return 3.5
  209. },
  210. set (val) {
  211. }
  212. }
  213. },
  214. created() {
  215. this.$store.commit('page/SET_AUTHOR_ID', this.authorId)
  216. this.$store.commit('page/SET_AUTHOR_NAME', this.authorName)
  217. this.$store.commit('page/SET_CREATED_AT', this.createdAt)
  218. this.$store.commit('page/SET_DESCRIPTION', this.description)
  219. this.$store.commit('page/SET_IS_PUBLISHED', this.isPublished)
  220. this.$store.commit('page/SET_LOCALE', this.locale)
  221. this.$store.commit('page/SET_PATH', this.path)
  222. this.$store.commit('page/SET_TAGS', this.tags)
  223. this.$store.commit('page/SET_TITLE', this.title)
  224. this.$store.commit('page/SET_UPDATED_AT', this.updatedAt)
  225. this.$store.commit('page/SET_MODE', 'view')
  226. },
  227. mounted () {
  228. Prism.highlightAllUnder(this.$refs.container)
  229. },
  230. methods: {
  231. toggleNavigation () {
  232. this.navOpen = !this.navOpen
  233. },
  234. upBtnScroll () {
  235. const scrollOffset = window.pageYOffset || document.documentElement.scrollTop
  236. this.upBtnShown = scrollOffset > window.innerHeight * 0.33
  237. }
  238. }
  239. }
  240. </script>
  241. <style lang="scss">
  242. .breadcrumbs-nav {
  243. .v-btn {
  244. min-width: 0;
  245. &__content {
  246. text-transform: none;
  247. }
  248. }
  249. .v-breadcrumbs__divider:nth-child(2n) {
  250. padding: 0 6px;
  251. }
  252. .v-breadcrumbs__divider:nth-child(2) {
  253. padding: 0 6px 0 12px;
  254. }
  255. }
  256. </style>