Edition.vue 19 KB

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