Edition.vue 13 KB

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