mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-31 18:37:00 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			212 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="relative">
 | |
|     <div class="rounded text-gray-200 border w-full px-3 py-2" :class="focusedDigit ? 'bg-primary bg-opacity-50 border-gray-300' : 'bg-primary border-gray-600'" @click="clickInput" v-click-outside="clickOutsideObj">
 | |
|       <div class="flex items-center">
 | |
|         <template v-for="(digit, index) in digitDisplay">
 | |
|           <div v-if="digit == ':'" :key="index" class="px-px" @click.stop="clickMedian(index)">:</div>
 | |
|           <div v-else :key="index" class="px-px" :class="{ 'digit-focused': focusedDigit == digit }" @click.stop="focusDigit(digit)">{{ digits[digit] }}</div>
 | |
|         </template>
 | |
|       </div>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| export default {
 | |
|   props: {
 | |
|     value: [String, Number],
 | |
|     showThreeDigitHour: Boolean
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       clickOutsideObj: {
 | |
|         handler: this.clickOutside,
 | |
|         events: ['mousedown'],
 | |
|         isActive: true
 | |
|       },
 | |
|       digitDisplay: ['hour1', 'hour0', ':', 'minute1', 'minute0', ':', 'second1', 'second0'],
 | |
|       focusedDigit: null,
 | |
|       digits: {
 | |
|         hour2: 0,
 | |
|         hour1: 0,
 | |
|         hour0: 0,
 | |
|         minute1: 0,
 | |
|         minute0: 0,
 | |
|         second1: 0,
 | |
|         second0: 0
 | |
|       },
 | |
|       isOver99Hours: false
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     value: {
 | |
|       immediate: true,
 | |
|       handler() {
 | |
|         this.initDigits()
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   computed: {},
 | |
|   methods: {
 | |
|     initDigits() {
 | |
|       var totalSeconds = !this.value || isNaN(this.value) ? 0 : Number(this.value)
 | |
|       totalSeconds = Math.round(totalSeconds)
 | |
| 
 | |
|       var minutes = Math.floor(totalSeconds / 60)
 | |
|       var seconds = totalSeconds - minutes * 60
 | |
|       var hours = Math.floor(minutes / 60)
 | |
|       minutes -= hours * 60
 | |
| 
 | |
|       this.digits.second1 = seconds <= 9 ? 0 : Number(String(seconds)[0])
 | |
|       this.digits.second0 = seconds <= 9 ? seconds : Number(String(seconds)[1])
 | |
| 
 | |
|       this.digits.minute1 = minutes <= 9 ? 0 : Number(String(minutes)[0])
 | |
|       this.digits.minute0 = minutes <= 9 ? minutes : Number(String(minutes)[1])
 | |
| 
 | |
|       if (hours > 99) {
 | |
|         this.digits.hour2 = Number(String(hours)[0])
 | |
|         this.digits.hour1 = Number(String(hours)[1])
 | |
|         this.digits.hour0 = Number(String(hours)[2])
 | |
|         this.isOver99Hours = true
 | |
|       } else {
 | |
|         this.digits.hour1 = hours <= 9 ? 0 : Number(String(hours)[0])
 | |
|         this.digits.hour0 = hours <= 9 ? hours : Number(String(hours)[1])
 | |
|         this.isOver99Hours = this.showThreeDigitHour
 | |
|       }
 | |
| 
 | |
|       if (this.isOver99Hours) {
 | |
|         this.digitDisplay = ['hour2', 'hour1', 'hour0', ':', 'minute1', 'minute0', ':', 'second1', 'second0']
 | |
|       } else {
 | |
|         this.digitDisplay = ['hour1', 'hour0', ':', 'minute1', 'minute0', ':', 'second1', 'second0']
 | |
|       }
 | |
|     },
 | |
|     updateSeconds() {
 | |
|       var seconds = this.digits.second0 + this.digits.second1 * 10
 | |
|       seconds += this.digits.minute0 * 60 + this.digits.minute1 * 600
 | |
|       seconds += this.digits.hour0 * 3600 + this.digits.hour1 * 36000
 | |
|       if (this.isOver99Hours) seconds += this.digits.hour2 * 360000
 | |
| 
 | |
|       if (Number(this.value) !== seconds) {
 | |
|         this.$emit('input', seconds)
 | |
|         this.$emit('change', seconds)
 | |
|       }
 | |
|     },
 | |
|     clickMedian(index) {
 | |
|       // Click colon select digit to right
 | |
|       if (index >= 5) {
 | |
|         this.focusedDigit = 'second1'
 | |
|       } else {
 | |
|         this.focusedDigit = 'minute1'
 | |
|       }
 | |
|     },
 | |
|     clickOutside() {
 | |
|       this.removeFocus()
 | |
|     },
 | |
|     removeFocus() {
 | |
|       this.focusedDigit = null
 | |
|       this.removeListeners()
 | |
|     },
 | |
|     focusDigit(digit) {
 | |
|       if (this.focusedDigit == null || isNaN(this.focusedDigit)) this.initListeners()
 | |
|       this.focusedDigit = digit
 | |
|     },
 | |
|     clickInput() {
 | |
|       if (this.focusedDigit) return
 | |
|       this.focusDigit('second0')
 | |
|     },
 | |
|     shiftFocusLeft() {
 | |
|       if (!this.focusedDigit) return
 | |
|       if (this.focusedDigit.endsWith('2')) return
 | |
| 
 | |
|       const isDigit1 = this.focusedDigit.endsWith('1')
 | |
|       if (!isDigit1) {
 | |
|         const digit1Key = this.focusedDigit.replace('0', '1')
 | |
|         this.focusedDigit = digit1Key
 | |
|       } else if (this.focusedDigit.startsWith('second')) {
 | |
|         this.focusedDigit = 'minute0'
 | |
|       } else if (this.focusedDigit.startsWith('minute')) {
 | |
|         this.focusedDigit = 'hour0'
 | |
|       } else if (this.isOver99Hours && this.focusedDigit.startsWith('hour')) {
 | |
|         this.focusedDigit = 'hour2'
 | |
|       }
 | |
|     },
 | |
|     shiftFocusRight() {
 | |
|       if (!this.focusedDigit) return
 | |
|       if (this.focusedDigit.endsWith('2')) {
 | |
|         // Must be hour2
 | |
|         this.focusedDigit = 'hour1'
 | |
|         return
 | |
|       }
 | |
|       const isDigit1 = this.focusedDigit.endsWith('1')
 | |
|       if (isDigit1) {
 | |
|         const digit0Key = this.focusedDigit.replace('1', '0')
 | |
|         this.focusedDigit = digit0Key
 | |
|       } else if (this.focusedDigit.startsWith('hour')) {
 | |
|         this.focusedDigit = 'minute1'
 | |
|       } else if (this.focusedDigit.startsWith('minute')) {
 | |
|         this.focusedDigit = 'second1'
 | |
|       }
 | |
|     },
 | |
|     increaseFocused() {
 | |
|       if (!this.focusedDigit) return
 | |
|       const isDigit1 = this.focusedDigit.endsWith('1')
 | |
|       const digit = Number(this.digits[this.focusedDigit])
 | |
|       if (isDigit1 && !this.focusedDigit.startsWith('hour')) this.digits[this.focusedDigit] = (digit + 1) % 6
 | |
|       else this.digits[this.focusedDigit] = (digit + 1) % 10
 | |
|       this.updateSeconds()
 | |
|     },
 | |
|     decreaseFocused() {
 | |
|       if (!this.focusedDigit) return
 | |
|       const isDigit1 = this.focusedDigit.endsWith('1')
 | |
|       const digit = Number(this.digits[this.focusedDigit])
 | |
|       if (isDigit1 && !this.focusedDigit.startsWith('hour')) this.digits[this.focusedDigit] = digit - 1 < 0 ? 5 : digit - 1
 | |
|       else this.digits[this.focusedDigit] = digit - 1 < 0 ? 9 : digit - 1
 | |
|       this.updateSeconds()
 | |
|     },
 | |
|     keydown(evt) {
 | |
|       if (!this.focusedDigit || !evt.key) return
 | |
| 
 | |
|       if (evt.key === 'ArrowLeft') {
 | |
|         return this.shiftFocusLeft()
 | |
|       } else if (evt.key === 'ArrowRight') {
 | |
|         return this.shiftFocusRight()
 | |
|       } else if (evt.key === 'ArrowUp') {
 | |
|         return this.increaseFocused()
 | |
|       } else if (evt.key === 'ArrowDown') {
 | |
|         return this.decreaseFocused()
 | |
|       } else if (evt.key === 'Enter' || evt.key === 'Escape') {
 | |
|         return this.removeFocus()
 | |
|       }
 | |
| 
 | |
|       if (isNaN(evt.key)) return
 | |
| 
 | |
|       var digit = Number(evt.key)
 | |
|       const isDigit1 = this.focusedDigit.endsWith('1')
 | |
|       if (isDigit1 && !this.focusedDigit.startsWith('hour') && digit >= 6) {
 | |
|         digit = 5
 | |
|       }
 | |
| 
 | |
|       this.digits[this.focusedDigit] = digit
 | |
| 
 | |
|       this.updateSeconds()
 | |
|       this.shiftFocusRight()
 | |
|     },
 | |
|     initListeners() {
 | |
|       window.addEventListener('keydown', this.keydown)
 | |
|     },
 | |
|     removeListeners() {
 | |
|       window.removeEventListener('keydown', this.keydown)
 | |
|     }
 | |
|   },
 | |
|   mounted() {},
 | |
|   beforeDestroy() {
 | |
|     this.removeListeners()
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style scoped>
 | |
| .digit-focused {
 | |
|   background-color: #555;
 | |
| }
 | |
| </style> |