mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			91 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			91 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <modals-modal v-model="show" name="chapters" :width="600" :height="'unset'">
 | |
|     <div id="chapter-modal-wrapper" ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
 | |
|       <template v-for="chap in chapters">
 | |
|         <div :key="chap.id" :id="`chapter-row-${chap.id}`" class="flex items-center px-6 py-3 justify-start cursor-pointer relative" :class="chap.id === currentChapterId ? 'bg-yellow-400/20 hover:bg-yellow-400/10' : chap.end / _playbackRate <= currentChapterStart ? 'bg-success/10 hover:bg-success/5' : 'hover:bg-primary/10'" @click="clickChapter(chap)">
 | |
|           <p class="chapter-title truncate text-sm md:text-base">
 | |
|             {{ chap.title }}
 | |
|           </p>
 | |
|           <span class="font-mono text-xxs sm:text-xs text-gray-400 pl-2 whitespace-nowrap">{{ $elapsedPrettyExtended((chap.end - chap.start) / _playbackRate) }}</span>
 | |
|           <span class="grow" />
 | |
|           <span class="font-mono text-xs sm:text-sm text-gray-300">{{ $secondsToTimestamp(chap.start / _playbackRate) }}</span>
 | |
| 
 | |
|           <div v-show="chap.id === currentChapterId" class="w-0.5 h-full absolute top-0 left-0 bg-yellow-400" />
 | |
|         </div>
 | |
|       </template>
 | |
|     </div>
 | |
|   </modals-modal>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| export default {
 | |
|   props: {
 | |
|     value: Boolean,
 | |
|     chapters: {
 | |
|       type: Array,
 | |
|       default: () => []
 | |
|     },
 | |
|     currentChapter: {
 | |
|       type: Object,
 | |
|       default: () => null
 | |
|     },
 | |
|     playbackRate: Number
 | |
|   },
 | |
|   data() {
 | |
|     return {}
 | |
|   },
 | |
|   computed: {
 | |
|     show: {
 | |
|       get() {
 | |
|         return this.value
 | |
|       },
 | |
|       set(val) {
 | |
|         this.$emit('input', val)
 | |
|       }
 | |
|     },
 | |
|     _playbackRate() {
 | |
|       if (!this.playbackRate || isNaN(this.playbackRate)) return 1
 | |
|       return this.playbackRate
 | |
|     },
 | |
|     currentChapterId() {
 | |
|       return this.currentChapter?.id || null
 | |
|     },
 | |
|     currentChapterStart() {
 | |
|       return (this.currentChapter?.start || 0) / this._playbackRate
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     clickChapter(chap) {
 | |
|       this.$emit('select', chap)
 | |
|     },
 | |
|     scrollToChapter() {
 | |
|       if (!this.currentChapterId) return
 | |
| 
 | |
|       if (this.$refs.container) {
 | |
|         const currChapterEl = document.getElementById(`chapter-row-${this.currentChapterId}`)
 | |
|         if (currChapterEl) {
 | |
|           const containerHeight = this.$refs.container.clientHeight
 | |
|           this.$refs.container.scrollTo({ top: currChapterEl.offsetTop - containerHeight / 2 })
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   updated() {
 | |
|     if (this.value) {
 | |
|       this.$nextTick(this.scrollToChapter)
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style>
 | |
| #chapter-modal-wrapper .chapter-title {
 | |
|   max-width: calc(100% - 120px);
 | |
| }
 | |
| @media (min-width: 640px) {
 | |
|   #chapter-modal-wrapper .chapter-title {
 | |
|     max-width: calc(100% - 150px);
 | |
|   }
 | |
| }
 | |
| </style>
 |