Edition.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <template>
  2. <MainContentLayout
  3. id="edition"
  4. :reftoscrollto="reftoscrollto"
  5. :navopened="navopened"
  6. @onCenterScrolled="onCenterScrolled"
  7. >
  8. <template v-if="!content" #header>
  9. <span>Loading ...</span>
  10. </template>
  11. <template #header>
  12. <h1>
  13. <router-link :to="{ name:'edition', params: { id: editionid }}">{{ title }}</router-link>
  14. </h1>
  15. <p v-if="meta">{{ meta.author }}</p>
  16. <aside
  17. v-if="indexitem"
  18. class="index-tooltip"
  19. :style="{ top:tooltip_top + 'px' }"
  20. :data-index="indexitem.index"
  21. :data-uuid="indexitem.uuid"
  22. @click.prevent="onClickTooltip"
  23. @keyup.enter="onClickTooltip"
  24. >
  25. <span v-if="indexitem == 'loading'">Loading ...</span>
  26. <template v-if="indexitem !== 'loading'">
  27. <h1 v-html="indexitem.title" />
  28. <p v-if="indexitem.birthDate" class="birthdeath">
  29. <time>{{ indexitem.birthDate }}</time>, <span class="place">{{ indexitem.birthPlace }}</span><br>
  30. <time>{{ indexitem.deathDate }}</time>, <span class="place">{{ indexitem.deathPlace }}</span>
  31. </p>
  32. <p v-if="indexitem.occupation" class="occupation">
  33. {{ indexitem.occupation }}
  34. </p>
  35. <p v-if="indexitem.type" class="type">
  36. {{ indexitem.type }}
  37. </p>
  38. </template>
  39. </aside>
  40. </template>
  41. <!-- default slot -->
  42. <div id="text">
  43. <template v-if="texts.length">
  44. <infinite-loading
  45. v-if="flattoc && center_scrolled"
  46. :identifier="inifinite_load_id"
  47. direction="top"
  48. :distance="inifinite_load_distance"
  49. @infinite="prevText"
  50. />
  51. <EdText
  52. v-for="text in texts"
  53. :ref="text.content.uuid"
  54. :key="text.content.uuid"
  55. :tei="text.content.tei"
  56. :uuid="text.content.uuid"
  57. :textid="textid"
  58. @onHoverLink="onHoverLink"
  59. @onLeaveLink="onLeaveLink"
  60. />
  61. <infinite-loading
  62. v-if="flattoc"
  63. :identifier="inifinite_load_id"
  64. @infinite="nextText"
  65. />
  66. </template>
  67. </div>
  68. <template #nav>
  69. <span
  70. class="nav-title"
  71. @click.prevent="onOpenCloseNav"
  72. @keyup.enter="onOpenCloseNav"
  73. >
  74. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="10" role="presentation" class="vs__open-indicator"><path d="M9.211364 7.59931l4.48338-4.867229c.407008-.441854.407008-1.158247 0-1.60046l-.73712-.80023c-.407008-.441854-1.066904-.441854-1.474243 0L7 5.198617 2.51662.33139c-.407008-.441853-1.066904-.441853-1.474243 0l-.737121.80023c-.407008.441854-.407008 1.158248 0 1.600461l4.48338 4.867228L7 10l2.211364-2.40069z" /></svg>
  75. Sommaire
  76. </span>
  77. <EdToc
  78. id="toc"
  79. :toc="toc"
  80. :loadedtextsuuids="textsuuids"
  81. @onClickTocItem="onClickTocItem"
  82. />
  83. <EdPagination
  84. v-if="pagination"
  85. id="page-nav"
  86. :pagination="pagination"
  87. @onClickPaginationItem="onClickPaginationItem"
  88. />
  89. </template>
  90. </MainContentLayout>
  91. </template>
  92. <script>
  93. import qs from 'querystring'
  94. import { REST } from 'api/rest-axios'
  95. import { mapState, mapActions } from 'vuex'
  96. import MainContentLayout from '../components/Layouts/MainContentLayout'
  97. import EdText from '../components/Content/EdText'
  98. import EdToc from '../components/Content/EdToc'
  99. import EdPagination from '../components/Content/EdPagination'
  100. export default {
  101. name: 'Edition',
  102. metaInfo () {
  103. // console.log('metainfo', this.meta)
  104. return {
  105. title: this.metainfotitle
  106. }
  107. },
  108. components: {
  109. MainContentLayout,
  110. EdText,
  111. EdToc,
  112. EdPagination
  113. },
  114. data: () => ({
  115. meta: null,
  116. editionid: null,
  117. textid: null,
  118. texts: [],
  119. textsuuids: [],
  120. metainfotitle: undefined,
  121. title: undefined,
  122. author: undefined,
  123. texttitle: undefined,
  124. //
  125. indexitem: null,
  126. tooltip_top: null,
  127. //
  128. next_loaded: false,
  129. center_scrolled: false,
  130. inifinite_load_distance: 10,
  131. inifinite_load_id: +new Date(),
  132. reftoscrollto: null,
  133. //
  134. toc: null,
  135. flattoc: null,
  136. //
  137. pagination: null,
  138. //
  139. navopened: false
  140. }),
  141. computed: {
  142. ...mapState({
  143. corpusLoaded: state => state.Corpus.corpusLoaded,
  144. editionslist: state => state.Corpus.editionslist,
  145. editionsbyuuid: state => state.Corpus.editionsbyuuid
  146. })
  147. },
  148. watch: {
  149. $route (to, from) {
  150. console.log('Edition Watcher $route', from, to)
  151. if (to.params.textid) {
  152. // change textid when route change
  153. this.textid = to.params.textid
  154. } else if (this.toc) {
  155. // if no textid in new route (e.g. edition front)
  156. // but we have toc
  157. // get the first item
  158. // will be replaced by front page of edition
  159. this.textid = this.toc[0].children[1].uuid
  160. } else {
  161. this.textid = null
  162. }
  163. },
  164. textid (newid, oldid) {
  165. console.log('textid watcher', this, oldid, newid)
  166. this.texts = []
  167. this.textsuuids = []
  168. this.pages = []
  169. this.pagesOtpions = []
  170. if (newid) {
  171. this.getTextContent(newid)
  172. this.inifinite_load_id += 1
  173. }
  174. },
  175. textdata (newtxtdata, oldtxtdata) {
  176. console.log('textdata watcher', oldtxtdata, newtxtdata)
  177. this.metainfotitle = `${this.title} ${newtxtdata.meta.title}`
  178. },
  179. page_selected (newp, oldp) {
  180. console.log('page_selected watcher', oldp, newp)
  181. this.scrollToPage(newp)
  182. }
  183. },
  184. created () {
  185. // console.log('Edition this.$route.params.id', this.$route.params.id)
  186. this.editionid = this.$route.params.id
  187. // get the text if textid available
  188. if (this.$route.params.textid) {
  189. this.textid = this.$route.params.textid
  190. }
  191. // wait for editions list from Corpus Store if not already loaded
  192. if (!this.corpusLoaded) {
  193. // this.getCorpuses()
  194. // subsribe to store to get the editionbyuuid list
  195. // https://dev.to/viniciuskneves/watch-for-vuex-state-changes-2mgj
  196. this.edUuuidsUnsubscribe = this.$store.subscribe((mutation, state) => {
  197. // console.log('Edition store subscribe', mutation.type)
  198. if (mutation.type === 'Corpus/setEditionsByUUID') {
  199. // console.log('Edition state.Coprus.editionsbyuuid', this.editionid, state.Corpus.editionsbyuuid)
  200. this.title = this.metainfotitle = state.Corpus.editionsbyuuid[this.editionid].title
  201. }
  202. if (mutation.type === 'Corpus/setTocs') {
  203. console.log('Edition Corpus/setTocs', this.editionid, state.Corpus.editionsbyuuid)
  204. this.toc = state.Corpus.editionsbyuuid[this.editionid].toc
  205. this.flattoc = state.Corpus.editionsbyuuid[this.editionid].flattoc
  206. this.inifinite_load_id += 1
  207. // if no textid in new route (e.g. edition front)
  208. // but we have toc
  209. // get the first item
  210. // will be replaced by front page of edition
  211. if (!this.textid) { this.textid = this.toc[0].children[0].uuid }
  212. }
  213. if (mutation.type === 'Corpus/setPaginations') {
  214. // console.log('Edition state.Coprus.editionsbyuuid', this.editionid, state.Corpus.editionsbyuuid)
  215. this.pagination = state.Corpus.editionsbyuuid[this.editionid].pagination
  216. }
  217. })
  218. } else {
  219. // console.log('');
  220. this.title = this.metainfotitle = this.editionsbyuuid[this.editionid].title
  221. this.toc = this.editionsbyuuid[this.editionid].toc
  222. this.flattoc = this.editionsbyuuid[this.editionid].flattoc
  223. // if no textid in new route (e.g. edition front)
  224. // but we have toc
  225. // get the first item
  226. // will be replaced by front page of edition
  227. if (!this.textid) { this.textid = this.toc[0].children[0].uuid }
  228. this.pagination = this.editionsbyuuid[this.editionid].pagination
  229. }
  230. },
  231. methods: {
  232. ...mapActions({
  233. getCorpuses: 'Corpus/getCorpuses'
  234. }),
  235. getTextContent (textid, $state = null, direction = 'next') {
  236. console.log('getTextContent', textid)
  237. let params = {
  238. depth: 0
  239. }
  240. let q = qs.stringify(params)
  241. REST.get(`${window.apipath}/items/${textid}?${q}`, {})
  242. .then(({ data }) => {
  243. console.log('text REST: data', data)
  244. if (direction === 'next') {
  245. this.texts.push(data)
  246. this.textsuuids.push(data.content.uuid)
  247. } else {
  248. this.texts.unshift(data)
  249. this.textsuuids.unshift(data.content.uuid)
  250. }
  251. if ($state) {
  252. $state.loaded()
  253. this.next_loaded = true
  254. }
  255. })
  256. .catch((error) => {
  257. console.warn('Issue with getTextContent', error)
  258. Promise.reject(error)
  259. // if some item don't load and if we come from infinite loading
  260. // retry with next step
  261. if ($state) {
  262. switch (direction) {
  263. case 'next':
  264. this.nextText($state, 2)
  265. break
  266. case 'prev':
  267. this.prevText($state, 2)
  268. break
  269. }
  270. }
  271. // this.$router.replace({
  272. // name: 'notfound',
  273. // query: { fullpath: this.$route.path }
  274. // })
  275. })
  276. },
  277. onCenterScrolled (e) {
  278. // console.log('Edition centerScrolled(e)', e.target.scrollTop)
  279. if (!this.center_scrolled && e.target.scrollTop > this.inifinite_load_distance * 1.5) {
  280. this.center_scrolled = true
  281. }
  282. this.indexitem = null
  283. },
  284. nextText ($state, indent = 1) {
  285. console.log('infinite loading nextText()')
  286. let indexofnext = this.flattoc.indexOf(this.textsuuids[this.textsuuids.length - 1]) + indent
  287. if (indexofnext < this.flattoc.length) {
  288. this.getTextContent(this.flattoc[indexofnext], $state, 'next')
  289. } else {
  290. $state.complete()
  291. }
  292. },
  293. prevText ($state, indent = 1) {
  294. console.log('infinite loading prevText()')
  295. let indexofprev = this.flattoc.indexOf(this.textsuuids[0]) - indent
  296. if (indexofprev >= 0) {
  297. this.getTextContent(this.flattoc[indexofprev], $state, 'prev')
  298. } else {
  299. $state.complete()
  300. }
  301. },
  302. onHoverLink (elmt) {
  303. console.log('Edition onHoverLink(elmt)', elmt)
  304. this.tooltip_top = elmt.rect.top
  305. this.getIndexItem(elmt)
  306. },
  307. onLeaveLink () {
  308. console.log('Edition onLeaveLink()')
  309. this.indexitem = null
  310. },
  311. getIndexItem (item) {
  312. this.indexitem = 'loading'
  313. REST.get(`${window.apipath}/index${item.index.charAt(0).toUpperCase()}${item.index.slice(1)}/${item.uuid}`, {})
  314. .then(({ data }) => {
  315. console.log('index tooltip REST: data', data)
  316. if (this.indexitem === 'loading') {
  317. this.indexitem = data.content
  318. this.indexitem.index = item.index
  319. }
  320. })
  321. .catch((error) => {
  322. console.warn('Issue with index tooltip rest', error)
  323. Promise.reject(error)
  324. this.indexitem = null
  325. })
  326. },
  327. onClickTocItem (uuid) {
  328. console.log('Edition onClickTocItem', uuid, this.$refs)
  329. if (this.textsuuids.indexOf(uuid) !== -1) {
  330. // if already loaded, scroll to uuid
  331. this.reftoscrollto = `.tei[data-uuid="${uuid}"]`
  332. } else {
  333. // if not already loaded, change route
  334. this.$router.push({
  335. name: `editiontext`,
  336. params: {
  337. id: this.editionid,
  338. textid: uuid
  339. }
  340. })
  341. }
  342. },
  343. onClickPaginationItem (o) {
  344. console.log('onClickPaginationItem', o)
  345. if (this.textsuuids.indexOf(o.uuid) !== -1) {
  346. // if already loaded, scroll to uuid
  347. this.scrollToPage(o)
  348. } else {
  349. // if not already loaded, change route
  350. this.$router.push({
  351. name: `editiontext`,
  352. params: {
  353. id: this.editionid,
  354. textid: o.uuid
  355. }
  356. })
  357. }
  358. },
  359. scrollToPage (p) {
  360. // console.log('scrollToPage', p)
  361. this.reftoscrollto = `span[role="pageBreak"][id="${p.code}"]`
  362. },
  363. onOpenCloseNav (e) {
  364. console.log('onOpenCloseNav', e)
  365. this.navopened = !this.navopened
  366. },
  367. onClickTooltip (e) {
  368. console.log(`onClickTooltip index: ${e.target.dataset.index}, uuid: ${e.target.dataset.uuid}`)
  369. this.$router.push({
  370. name: e.target.dataset.index,
  371. params: { id: e.target.dataset.uuid }
  372. })
  373. }
  374. }
  375. }
  376. </script>
  377. <style lang="scss" scoped>
  378. </style>