Edition.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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">Chargement ...</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. <aside class="notice opened">
  17. <div v-if="description">
  18. <p v-html="description" />
  19. </div>
  20. <div v-if="author">
  21. <p>{{ author }}</p>
  22. </div>
  23. <!-- <div v-if="date">
  24. <p>{{ date }}</p>
  25. </div> -->
  26. <div v-if="biblio" class="biblio">
  27. <p v-html="biblio.description" />
  28. <a
  29. :href="edition_manifestation_href"
  30. @click.prevent="onClickManifestation"
  31. @keyup.enter="onClickManifestation"
  32. >
  33. <!-- // todo better label
  34. {{ biblio.uuid }} -->
  35. </a>
  36. </div>
  37. </aside>
  38. <nav class="entities-hide-show">
  39. <span
  40. v-if="entities_displayed"
  41. @click.prevent="onCLickEntitiesDisplay"
  42. @keyup.enter="onCLickEntitiesDisplay"
  43. >
  44. <span class="mdi mdi-eye-outline" /> Afficher les marques d'entités dans les textes
  45. </span>
  46. <span
  47. v-if="!entities_displayed"
  48. @click.prevent="onCLickEntitiesDisplay"
  49. @keyup.enter="onCLickEntitiesDisplay"
  50. >
  51. <span class="mdi mdi-eye-off-outline" /> Masquer les marques d'entités dans les textes
  52. </span>
  53. </nav>
  54. <!-- displayed on hover entity on texte -->
  55. <aside
  56. v-if="indexitem"
  57. class="index-tooltip"
  58. :style="{ top:tooltip_top + 'px' }"
  59. :data-index="indexitem.index"
  60. :data-uuid="indexitem.uuid"
  61. @click.prevent="onClickTooltip"
  62. @keyup.enter="onClickTooltip"
  63. >
  64. <span v-if="indexitem == 'loading'" class="loading">Chargement ...</span>
  65. <template v-if="indexitem !== 'loading'">
  66. <h1 v-html="indexitem.title" />
  67. <p v-if="indexitem.birthDate" class="birthdeath">
  68. <time>{{ indexitem.birthDate }}</time>, <span class="place">{{ indexitem.birthPlace }}</span><br>
  69. <time>{{ indexitem.deathDate }}</time>, <span class="place">{{ indexitem.deathPlace }}</span>
  70. </p>
  71. <p v-if="indexitem.occupation" class="occupation">
  72. {{ indexitem.occupation }}
  73. </p>
  74. <p v-if="indexitem.type" class="type">
  75. {{ indexitem.type }}
  76. </p>
  77. </template>
  78. </aside>
  79. </template>
  80. <!-- </transition> -->
  81. <!-- default slot -->
  82. <div id="text" :class="{ 'entities-displayed': !entities_displayed, 'entities-hidden': entities_displayed }">
  83. <template v-if="texts.length">
  84. <infinite-loading
  85. v-if="flattoc && center_scrolled"
  86. :identifier="inifinite_load_id"
  87. direction="top"
  88. :distance="inifinite_load_distance"
  89. @infinite="prevText"
  90. />
  91. <!-- <transition-group name="edition-texts" tag="div"> -->
  92. <EdText
  93. v-for="text in texts"
  94. :ref="text.content.uuid"
  95. :key="text.content.uuid"
  96. :tei="text.content.tei"
  97. :uuid="text.content.uuid"
  98. :url="text.content.url"
  99. :textid="textid"
  100. :extractid="extractid"
  101. @onHoverLink="onHoverLink"
  102. @onLeaveLink="onLeaveLink"
  103. />
  104. <!-- </transition-group> -->
  105. <infinite-loading
  106. v-if="flattoc"
  107. :identifier="inifinite_load_id"
  108. @infinite="nextText"
  109. />
  110. </template>
  111. </div>
  112. <template #nav>
  113. <span
  114. class="nav-title"
  115. @click.prevent="onOpenCloseNav"
  116. @keyup.enter="onOpenCloseNav"
  117. >
  118. <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>
  119. Sommaire
  120. </span>
  121. <EdToc
  122. id="toc"
  123. :toc="toc"
  124. :loadedtextsuuids="textsuuids"
  125. :selectedindex="selectedindex"
  126. @onClickTocItem="onClickTocItem"
  127. />
  128. <EdIndexes
  129. v-if="indexes"
  130. id="indexes-filters"
  131. :indexes="indexes"
  132. @onClickIndexItem="onClickIndexItem"
  133. />
  134. <EdPagination
  135. v-if="pagination"
  136. id="page-nav"
  137. :pagination="pagination"
  138. @onClickPaginationItem="onClickPaginationItem"
  139. />
  140. </template>
  141. </MainContentLayout>
  142. </template>
  143. <script>
  144. import qs from 'querystring'
  145. import { REST } from 'api/rest-axios'
  146. import { mapState, mapActions } from 'vuex'
  147. import MainContentLayout from '../components/Layouts/MainContentLayout'
  148. import EdText from '../components/Content/EdText'
  149. import EdToc from '../components/Content/EdToc'
  150. import EdIndexes from '../components/Content/EdIndexes'
  151. import EdPagination from '../components/Content/EdPagination'
  152. export default {
  153. name: 'Edition',
  154. metaInfo () {
  155. // console.log('metainfo', this.meta)
  156. return {
  157. title: this.metainfotitle,
  158. meta: this.meta
  159. }
  160. },
  161. components: {
  162. MainContentLayout,
  163. EdText,
  164. EdToc,
  165. EdIndexes,
  166. EdPagination
  167. },
  168. data: () => ({
  169. meta: [],
  170. editionid: null,
  171. textid: null,
  172. extract: null,
  173. extractid: null,
  174. texts: [],
  175. textsuuids: [],
  176. metainfotitle: undefined,
  177. title: undefined,
  178. biblio: undefined,
  179. author: undefined,
  180. date: undefined,
  181. description: undefined,
  182. texttitle: undefined,
  183. //
  184. indexitem: null,
  185. tooltip_top: null,
  186. //
  187. next_loaded: false,
  188. center_scrolled: false,
  189. inifinite_load_distance: 10,
  190. inifinite_load_id: +new Date(),
  191. reftoscrollto: null,
  192. //
  193. toc: null,
  194. flattoc: null,
  195. //
  196. indexes: null,
  197. selectedindex: null,
  198. //
  199. pagination: null,
  200. //
  201. navopened: false,
  202. //
  203. entities_displayed: false
  204. }),
  205. computed: {
  206. ...mapState({
  207. corpusLoaded: state => state.Corpus.corpusLoaded,
  208. editionslist: state => state.Corpus.editionslist,
  209. editionsbyuuid: state => state.Corpus.editionsbyuuid
  210. }),
  211. edition_manifestation_href () {
  212. return `${this.biblio.path}${this.biblio.uuid}`
  213. }
  214. },
  215. watch: {
  216. $route (to, from) {
  217. console.log('Edition Watcher $route', from, to)
  218. if (to.params.id !== this.editionid) {
  219. this.setValues()
  220. }
  221. if (to.params.textid) {
  222. // change textid when route change
  223. this.textid = to.params.textid
  224. // change also extract if exists
  225. this.extractid = null
  226. if (to.params.extract) {
  227. console.log('extract params from route', to.params.extract)
  228. this.extractid = to.params.ocid
  229. // scrolling is not working :(
  230. this.reftoscrollto = '#mark-1'
  231. }
  232. } else if (this.toc) {
  233. // if no textid in new route (e.g. edition front)
  234. // but we have toc
  235. // get the first item
  236. // will be replaced by front page of edition
  237. this.textid = this.toc[0].children[1].uuid
  238. } else {
  239. this.textid = null
  240. }
  241. },
  242. reftoscrollto (newref, oldref) {
  243. console.log('reftoscrollto changed', oldref, newref)
  244. },
  245. textid (newid, oldid) {
  246. // triggered when route change (when TOC item clicked)
  247. console.log('textid watcher', this, oldid, newid)
  248. this.texts = []
  249. this.textsuuids = []
  250. this.pages = []
  251. this.pagesOtpions = []
  252. if (newid) {
  253. this.getTextContent(newid)
  254. this.inifinite_load_id += 1
  255. }
  256. },
  257. // textdata (newtxtdata, oldtxtdata) {
  258. // console.log('textdata watcher', oldtxtdata, newtxtdata)
  259. // this.metainfotitle = `${this.title} ${newtxtdata.meta.title}`
  260. // },
  261. page_selected (newp, oldp) {
  262. console.log('page_selected watcher', oldp, newp)
  263. this.scrollToPage(newp)
  264. },
  265. flattoc (n, o) {
  266. console.log('flattoc watcher', o, n)
  267. // this.scrollToPage(newp)
  268. }
  269. },
  270. created () {
  271. this.setValues()
  272. },
  273. // updated () {
  274. // this.setValues()
  275. // },
  276. methods: {
  277. ...mapActions({
  278. getCorpuses: 'Corpus/getCorpuses'
  279. }),
  280. setValues () {
  281. // console.log('Edition this.$route.params.id', this.$route.params.id)
  282. this.editionid = this.$route.params.id
  283. // get the text if textid available
  284. if (this.$route.params.textid) {
  285. this.textid = this.$route.params.textid
  286. }
  287. // get the searchkeys from route param (only comming from result item) for text highlighting
  288. if (this.$route.params.ocid) {
  289. this.extractid = this.$route.params.ocid
  290. // scrolling is not working :(
  291. this.reftoscrollto = '#mark-1'
  292. }
  293. // wait for editions list from Corpus Store if not already loaded
  294. if (!this.corpusLoaded) {
  295. // this.getCorpuses()
  296. // subsribe to store to get the editionbyuuid list
  297. // https://dev.to/viniciuskneves/watch-for-vuex-state-changes-2mgj
  298. this.edUuuidsUnsubscribe = this.$store.subscribe((mutation, state) => {
  299. // console.log('Edition store subscribe', mutation.type)
  300. if (mutation.type === 'Corpus/setEditionsByUUID') {
  301. // console.log('Edition state.Coprus.editionsbyuuid', this.editionid, state.Corpus.editionsbyuuid)
  302. this.title = this.metainfotitle = state.Corpus.editionsbyuuid[this.editionid].title
  303. this.meta = [
  304. { name: 'test', content: 'edition chargé' }
  305. ]
  306. this.biblio = state.Corpus.editionsbyuuid[this.editionid].biblio
  307. this.description = state.Corpus.editionsbyuuid[this.editionid].description
  308. this.date = state.Corpus.editionsbyuuid[this.editionid].date
  309. this.author = state.Corpus.editionsbyuuid[this.editionid].author
  310. }
  311. if (mutation.type === 'Corpus/setTocs') {
  312. console.log('Edition Corpus/setTocs', this.editionid, state.Corpus.editionsbyuuid)
  313. this.toc = state.Corpus.editionsbyuuid[this.editionid].toc
  314. }
  315. if (mutation.type === 'Corpus/buildFlatTocsAndFilters') {
  316. console.log('Edition Corpus/buildFlatTocsAndFilters', this.editionid, state.Corpus.editionsbyuuid)
  317. this.flattoc = state.Corpus.editionsbyuuid[this.editionid].flattoc
  318. // launch infinitloading
  319. this.inifinite_load_id += 1
  320. // if no textid in new route (e.g. edition front)
  321. // but we have toc
  322. // get the first item
  323. // will be replaced by front page of edition
  324. if (!this.textid) { this.textid = this.flattoc[1] }
  325. //
  326. this.indexes = state.Corpus.editionsbyuuid[this.editionid].indexes
  327. }
  328. if (mutation.type === 'Corpus/setPaginations') {
  329. // console.log('Edition state.Coprus.editionsbyuuid', this.editionid, state.Corpus.editionsbyuuid)
  330. this.pagination = state.Corpus.editionsbyuuid[this.editionid].pagination
  331. }
  332. })
  333. } else {
  334. // console.log('');
  335. this.title = this.metainfotitle = this.editionsbyuuid[this.editionid].title
  336. this.meta = [
  337. { name: 'test', content: 'edition deja là' }
  338. ]
  339. this.biblio = this.editionsbyuuid[this.editionid].biblio
  340. this.description = this.editionsbyuuid[this.editionid].description
  341. this.date = this.editionsbyuuid[this.editionid].date
  342. this.author = this.editionsbyuuid[this.editionid].author
  343. this.toc = this.editionsbyuuid[this.editionid].toc
  344. this.flattoc = this.editionsbyuuid[this.editionid].flattoc
  345. // if no textid in new route (e.g. edition front)
  346. // but we have toc
  347. // get the first item
  348. // will be replaced by front page of edition
  349. if (!this.textid) { this.textid = this.toc[0].children[0].uuid }
  350. this.indexes = this.editionsbyuuid[this.editionid].indexes
  351. this.pagination = this.editionsbyuuid[this.editionid].pagination
  352. }
  353. },
  354. getTextContent (textid, $state = null, direction = 'next') {
  355. console.log('getTextContent', textid)
  356. let params = {
  357. depth: 0
  358. }
  359. let q = qs.stringify(params)
  360. REST.get(`${window.apipath}/items/${textid}?${q}`, {})
  361. .then(({ data }) => {
  362. console.log('text REST: data', data)
  363. if (direction === 'next') {
  364. this.texts.push(data)
  365. this.textsuuids.push(data.content.uuid)
  366. } else {
  367. this.texts.unshift(data)
  368. this.textsuuids.unshift(data.content.uuid)
  369. }
  370. if ($state) {
  371. // triggered by infinite scroll
  372. $state.loaded()
  373. this.next_loaded = true
  374. } else {
  375. // triggered by TOC item click
  376. // UPDATE METATAGS
  377. this.updateMetaData(data.meta.metadata)
  378. }
  379. })
  380. .catch((error) => {
  381. console.warn('Issue with getTextContent', error)
  382. Promise.reject(error)
  383. // if some item don't load and if we come from infinite loading
  384. // retry with next step
  385. if ($state) {
  386. switch (direction) {
  387. case 'next':
  388. this.nextText($state, 2)
  389. break
  390. case 'prev':
  391. this.prevText($state, 2)
  392. break
  393. }
  394. }
  395. // this.$router.replace({
  396. // name: 'notfound',
  397. // query: { fullpath: this.$route.path }
  398. // })
  399. })
  400. },
  401. updateMetaData (metadata) {
  402. this.meta = []
  403. metadata.forEach(m => {
  404. let o = {}
  405. o.name = m.name
  406. if (Array.isArray(m.content)) {
  407. o.content = m.content.join(', ')
  408. } else {
  409. o.content = m.content
  410. }
  411. if (typeof m.scheme !== 'undefined') {
  412. o.scheme = m.scheme
  413. }
  414. this.meta.push(o)
  415. })
  416. },
  417. onCenterScrolled (e) {
  418. console.log('Edition centerScrolled(e)', e.target.scrollTop)
  419. if (!this.center_scrolled && e.target.scrollTop > this.inifinite_load_distance * 1.5) {
  420. this.center_scrolled = true
  421. // this.$store.commit('History/setOpened', false)
  422. }
  423. this.indexitem = null
  424. },
  425. nextText ($state, indent = 1) {
  426. console.log('infinite loading nextText()')
  427. let indexofnext = this.flattoc.indexOf(this.textsuuids[this.textsuuids.length - 1]) + indent
  428. if (indexofnext < this.flattoc.length) {
  429. this.getTextContent(this.flattoc[indexofnext], $state, 'next')
  430. } else {
  431. $state.complete()
  432. }
  433. },
  434. prevText ($state, indent = 1) {
  435. console.log('infinite loading prevText()')
  436. let indexofprev = this.flattoc.indexOf(this.textsuuids[0]) - indent
  437. if (indexofprev >= 0) {
  438. this.getTextContent(this.flattoc[indexofprev], $state, 'prev')
  439. } else {
  440. $state.complete()
  441. }
  442. },
  443. onHoverLink (elmt) {
  444. console.log('Edition onHoverLink(elmt)', elmt)
  445. this.tooltip_top = elmt.rect.top
  446. this.getIndexItem(elmt)
  447. },
  448. onLeaveLink () {
  449. console.log('Edition onLeaveLink()')
  450. this.indexitem = null
  451. },
  452. getIndexItem (item) {
  453. this.indexitem = 'loading'
  454. REST.get(`${window.apipath}/index${item.index.charAt(0).toUpperCase()}${item.index.slice(1)}/${item.uuid}`, {})
  455. .then(({ data }) => {
  456. console.log('index tooltip REST: data', data)
  457. if (this.indexitem === 'loading') {
  458. this.indexitem = data.content
  459. this.indexitem.index = item.index
  460. }
  461. })
  462. .catch((error) => {
  463. console.warn('Issue with index tooltip rest', error)
  464. Promise.reject(error)
  465. this.indexitem = null
  466. })
  467. },
  468. onClickTocItem (uuid) {
  469. console.log('Edition onClickTocItem', uuid, this.$refs)
  470. if (this.textsuuids.indexOf(uuid) !== -1) {
  471. // if already loaded, scroll to uuid
  472. this.reftoscrollto = `.tei[data-uuid="${uuid}"]`
  473. } else {
  474. // if not already loaded, change route
  475. this.$router.push({
  476. name: `editiontext`,
  477. params: {
  478. id: this.editionid,
  479. textid: uuid
  480. }
  481. })
  482. }
  483. },
  484. onClickIndexItem (o) {
  485. this.selectedindex = o
  486. },
  487. onClickPaginationItem (o) {
  488. console.log('onClickPaginationItem', o)
  489. if (this.textsuuids.indexOf(o.uuid) !== -1) {
  490. // if already loaded, scroll to uuid
  491. // this.scrollToPage(o)
  492. this.reftoscrollto = `span[role="pageBreak"][id="${o.code}"]`
  493. } else {
  494. // if not already loaded, change route
  495. this.$router.push({
  496. name: `editiontext`,
  497. params: {
  498. id: this.editionid,
  499. textid: o.uuid
  500. }
  501. })
  502. }
  503. },
  504. // scrollToPage (p) {
  505. // console.log('scrollToPage', p)
  506. //
  507. // },
  508. onOpenCloseNav (e) {
  509. console.log('onOpenCloseNav', e)
  510. this.navopened = !this.navopened
  511. },
  512. onClickTooltip (e) {
  513. console.log(`onClickTooltip index: ${e.target.dataset.index}, uuid: ${e.target.dataset.uuid}`)
  514. this.$router.push({
  515. name: e.target.dataset.index,
  516. params: { id: e.target.dataset.uuid }
  517. })
  518. },
  519. onClickManifestation (e) {
  520. console.log(`onClickManifestation`)
  521. this.$router.push({
  522. name: 'bibliographieItem',
  523. params: { type: 'manifestations', uuid: this.biblio.uuid }
  524. })
  525. },
  526. onCLickEntitiesDisplay (e) {
  527. console.log('onCLickEntitiesDisplay')
  528. this.entities_displayed = !this.entities_displayed
  529. }
  530. }
  531. }
  532. </script>
  533. <style lang="scss" scoped>
  534. </style>