mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-03-04 06:59:41 +00:00
Merge remote-tracking branch 'remotes/upstream/master'
This commit is contained in:
commit
6e64ea3ce8
116 changed files with 4080 additions and 3726 deletions
|
|
@ -55,7 +55,7 @@
|
|||
@showPlayerQueueItems="showPlayerQueueItemsModal = true"
|
||||
/>
|
||||
|
||||
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :current-time="bookmarkCurrentTime" :library-item-id="libraryItemId" @select="selectBookmark" />
|
||||
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :current-time="bookmarkCurrentTime" :playback-rate="currentPlaybackRate" :library-item-id="libraryItemId" @select="selectBookmark" />
|
||||
|
||||
<modals-sleep-timer-modal v-model="showSleepTimerModal" :timer-set="sleepTimerSet" :timer-type="sleepTimerType" :remaining="sleepTimerRemaining" :has-chapters="!!chapters.length" @set="setSleepTimer" @cancel="cancelSleepTimer" @increment="incrementSleepTimer" @decrement="decrementSleepTimer" />
|
||||
|
||||
|
|
|
|||
|
|
@ -5,24 +5,26 @@
|
|||
<p class="text-3xl text-white truncate">{{ $strings.LabelYourBookmarks }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
|
||||
<div v-if="show" class="w-full h-full">
|
||||
<div v-if="show" class="w-full rounded-lg bg-bg box-shadow-md relative" style="max-height: 80vh">
|
||||
<div v-if="bookmarks.length" class="h-full max-h-[calc(80vh-60px)] w-full relative overflow-y-auto overflow-x-hidden">
|
||||
<template v-for="bookmark in bookmarks">
|
||||
<modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" @click="clickBookmark" @update="submitUpdateBookmark" @delete="deleteBookmark" />
|
||||
<modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" :playback-rate="playbackRate" @click="clickBookmark" @delete="deleteBookmark" />
|
||||
</template>
|
||||
<div v-if="!bookmarks.length" class="flex h-32 items-center justify-center">
|
||||
<p class="text-xl">{{ $strings.MessageNoBookmarks }}</p>
|
||||
</div>
|
||||
<div v-if="!hideCreate" class="w-full h-px bg-white bg-opacity-10" />
|
||||
<form v-if="!hideCreate" @submit.prevent="submitCreateBookmark">
|
||||
<div v-show="canCreateBookmark" class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
|
||||
</div>
|
||||
<div v-else class="flex h-32 items-center justify-center">
|
||||
<p class="text-xl">{{ $strings.MessageNoBookmarks }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="canCreateBookmark && !hideCreate" class="w-full border-t border-white/10">
|
||||
<form @submit.prevent="submitCreateBookmark">
|
||||
<div class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
|
||||
<div class="w-16 max-w-16 text-center">
|
||||
<p class="text-sm font-mono text-gray-400">
|
||||
{{ this.$secondsToTimestamp(currentTime) }}
|
||||
{{ this.$secondsToTimestamp(currentTime / playbackRate) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-grow px-2">
|
||||
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" />
|
||||
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full h-10" />
|
||||
</div>
|
||||
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">add</span></ui-btn>
|
||||
</div>
|
||||
|
|
@ -45,6 +47,7 @@ export default {
|
|||
default: 0
|
||||
},
|
||||
libraryItemId: String,
|
||||
playbackRate: Number,
|
||||
hideCreate: Boolean
|
||||
},
|
||||
data() {
|
||||
|
|
@ -57,6 +60,7 @@ export default {
|
|||
watch: {
|
||||
show(newVal) {
|
||||
if (newVal) {
|
||||
this.selectedBookmark = null
|
||||
this.showBookmarkTitleInput = false
|
||||
this.newBookmarkTitle = ''
|
||||
}
|
||||
|
|
@ -72,7 +76,7 @@ export default {
|
|||
}
|
||||
},
|
||||
canCreateBookmark() {
|
||||
return !this.bookmarks.find((bm) => bm.time === this.currentTime)
|
||||
return !this.bookmarks.find((bm) => Math.abs(this.currentTime - bm.time) < 1)
|
||||
},
|
||||
dateFormat() {
|
||||
return this.$store.state.serverSettings.dateFormat
|
||||
|
|
@ -102,19 +106,6 @@ export default {
|
|||
clickBookmark(bm) {
|
||||
this.$emit('select', bm)
|
||||
},
|
||||
submitUpdateBookmark(updatedBookmark) {
|
||||
var bookmark = { ...updatedBookmark }
|
||||
this.$axios
|
||||
.$patch(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$strings.ToastBookmarkUpdateSuccess)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
console.error(error)
|
||||
})
|
||||
this.show = false
|
||||
},
|
||||
submitCreateBookmark() {
|
||||
if (!this.newBookmarkTitle) {
|
||||
this.newBookmarkTitle = this.$formatDatetime(Date.now(), this.dateFormat, this.timeFormat)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="flex items-center px-4 py-4 justify-start relative bg-primary hover:bg-opacity-25" :class="wrapperClass" @click.stop="click" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||
<div class="flex items-center px-4 py-4 justify-start relative hover:bg-primary/10" :class="wrapperClass" @click.stop="click" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||
<div class="w-16 max-w-16 text-center">
|
||||
<p class="text-sm font-mono text-gray-400">
|
||||
{{ this.$secondsToTimestamp(bookmark.time) }}
|
||||
{{ this.$secondsToTimestamp(bookmark.time / playbackRate) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-grow overflow-hidden px-2">
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<form @submit.prevent="submitUpdate">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-grow pr-2">
|
||||
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" />
|
||||
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full h-10" />
|
||||
</div>
|
||||
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">forward</span></ui-btn>
|
||||
<div class="pl-2 flex items-center">
|
||||
|
|
@ -35,7 +35,8 @@ export default {
|
|||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
highlight: Boolean
|
||||
highlight: Boolean,
|
||||
playbackRate: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -83,11 +84,19 @@ export default {
|
|||
if (this.newBookmarkTitle === this.bookmark.title) {
|
||||
return this.cancelEditing()
|
||||
}
|
||||
var bookmark = { ...this.bookmark }
|
||||
const bookmark = { ...this.bookmark }
|
||||
bookmark.title = this.newBookmarkTitle
|
||||
this.$emit('update', bookmark)
|
||||
|
||||
this.$axios
|
||||
.$patch(`/api/me/item/${bookmark.libraryItemId}/bookmark`, bookmark)
|
||||
.then(() => {
|
||||
this.isEditing = false
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -113,6 +113,10 @@ export default {
|
|||
return false
|
||||
})
|
||||
console.log('updateResult', updateResult)
|
||||
} else if (!lastEpisodeCheck) {
|
||||
this.$toast.error(this.$strings.ToastDateTimeInvalidOrIncomplete)
|
||||
this.checkingNewEpisodes = false
|
||||
return false
|
||||
}
|
||||
|
||||
this.$axios
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
<ui-checkbox v-model="enableAutoScan" @input="toggleEnableAutoScan" :label="$strings.LabelEnable" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" />
|
||||
</div>
|
||||
<widgets-cron-expression-builder ref="cronExpressionBuilder" v-if="enableAutoScan" v-model="cronExpression" @input="updatedCron" />
|
||||
<div v-else>
|
||||
<p class="text-yellow-400 text-base">{{ $strings.MessageScheduleLibraryScanNote }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -170,6 +170,12 @@ export default {
|
|||
this.show = false
|
||||
}
|
||||
},
|
||||
libraryItemUpdated(libraryItem) {
|
||||
const episode = libraryItem.media.episodes.find((e) => e.id === this.selectedEpisodeId)
|
||||
if (episode) {
|
||||
this.episodeItem = episode
|
||||
}
|
||||
},
|
||||
hotkey(action) {
|
||||
if (action === this.$hotkeys.Modal.NEXT_PAGE) {
|
||||
this.goNextEpisode()
|
||||
|
|
@ -178,9 +184,15 @@ export default {
|
|||
}
|
||||
},
|
||||
registerListeners() {
|
||||
if (this.libraryItem) {
|
||||
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||
}
|
||||
this.$eventBus.$on('modal-hotkey', this.hotkey)
|
||||
},
|
||||
unregisterListeners() {
|
||||
if (this.libraryItem) {
|
||||
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||
}
|
||||
this.$eventBus.$off('modal-hotkey', this.hotkey)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<ui-dropdown v-model="newEpisode.episodeType" :label="$strings.LabelEpisodeType" :items="episodeTypes" small />
|
||||
</div>
|
||||
<div class="w-2/5 p-1">
|
||||
<ui-text-input-with-label v-model="pubDateInput" @input="updatePubDate" type="datetime-local" :label="$strings.LabelPubDate" />
|
||||
<ui-text-input-with-label v-model="pubDateInput" ref="pubdate" type="datetime-local" :label="$strings.LabelPubDate" @input="updatePubDate" />
|
||||
</div>
|
||||
<div class="w-full p-1">
|
||||
<ui-text-input-with-label v-model="newEpisode.title" :label="$strings.LabelTitle" />
|
||||
|
|
@ -145,11 +145,18 @@ export default {
|
|||
return null
|
||||
}
|
||||
|
||||
// Check pubdate is valid if it is being updated. Cannot be set to null in the web client
|
||||
if (this.newEpisode.pubDate === null && this.$refs.pubdate?.$refs?.input?.isInvalidDate) {
|
||||
this.$toast.error(this.$strings.ToastDateTimeInvalidOrIncomplete)
|
||||
return null
|
||||
}
|
||||
|
||||
const updatedDetails = this.getUpdatePayload()
|
||||
if (!Object.keys(updatedDetails).length) {
|
||||
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
|
||||
return false
|
||||
}
|
||||
|
||||
return this.updateDetails(updatedDetails)
|
||||
},
|
||||
async updateDetails(updatedDetails) {
|
||||
|
|
@ -163,13 +170,10 @@ export default {
|
|||
|
||||
this.isProcessing = false
|
||||
if (updateResult) {
|
||||
if (updateResult) {
|
||||
this.$toast.success(this.$strings.ToastItemUpdateSuccess)
|
||||
return true
|
||||
} else {
|
||||
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
|
||||
}
|
||||
this.$toast.success(this.$strings.ToastItemUpdateSuccess)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div class="absolute -top-10 lg:top-0 right-0 lg:right-2 flex items-center h-full">
|
||||
<controls-playback-speed-control v-model="playbackRate" @input="setPlaybackRate" @change="playbackRateChanged" class="mx-2 block" />
|
||||
|
||||
<ui-tooltip direction="left" :text="$strings.LabelVolume">
|
||||
<ui-tooltip direction="bottom" :text="$strings.LabelVolume">
|
||||
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden sm:block" />
|
||||
</ui-tooltip>
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,6 @@ export default {
|
|||
dayOfWeekToday() {
|
||||
return new Date().getDay()
|
||||
},
|
||||
firstWeekStart() {
|
||||
return this.$addDaysToToday(-this.daysToShow)
|
||||
},
|
||||
dayLabels() {
|
||||
return [
|
||||
{
|
||||
|
|
@ -198,12 +195,25 @@ export default {
|
|||
let minValue = 0
|
||||
|
||||
const dates = []
|
||||
for (let i = 0; i < this.daysToShow + 1; i++) {
|
||||
const date = i === 0 ? this.firstWeekStart : this.$addDaysToDate(this.firstWeekStart, i)
|
||||
|
||||
const numDaysInTheLastYear = 52 * 7 + this.dayOfWeekToday
|
||||
const firstDay = this.$addDaysToToday(-numDaysInTheLastYear)
|
||||
for (let i = 0; i < numDaysInTheLastYear + 1; i++) {
|
||||
const date = i === 0 ? firstDay : this.$addDaysToDate(firstDay, i)
|
||||
const dateString = this.$formatJsDate(date, 'yyyy-MM-dd')
|
||||
|
||||
if (this.daysListening[dateString] > 0) {
|
||||
this.daysListenedInTheLastYear++
|
||||
}
|
||||
|
||||
const visibleDayIndex = i - (numDaysInTheLastYear - this.daysToShow)
|
||||
if (visibleDayIndex < 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const dateObj = {
|
||||
col: Math.floor(i / 7),
|
||||
row: i % 7,
|
||||
col: Math.floor(visibleDayIndex / 7),
|
||||
row: visibleDayIndex % 7,
|
||||
date,
|
||||
dateString,
|
||||
datePretty: this.$formatJsDate(date, 'MMM d, yyyy'),
|
||||
|
|
@ -215,7 +225,6 @@ export default {
|
|||
dates.push(dateObj)
|
||||
|
||||
if (dateObj.value > 0) {
|
||||
this.daysListenedInTheLastYear++
|
||||
if (dateObj.value > maxValue) maxValue = dateObj.value
|
||||
if (!minValue || dateObj.value < minValue) minValue = dateObj.value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default {
|
|||
return this.episode?.title || ''
|
||||
},
|
||||
episodeSubtitle() {
|
||||
return this.episode?.subtitle || ''
|
||||
return this.episode?.subtitle || this.episode?.description || ''
|
||||
},
|
||||
episodeType() {
|
||||
return this.episode?.episodeType || ''
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="relative min-h-[176px]">
|
||||
<div class="relative min-h-44">
|
||||
<template v-for="episode in totalEpisodes">
|
||||
<div :key="episode" :id="`episode-${episode - 1}`" class="w-full h-44 px-2 py-3 overflow-hidden relative border-b border-white/10">
|
||||
<!-- episode is mounted here -->
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
<div v-if="isSearching" class="w-full h-full absolute inset-0 flex justify-center py-12" :class="{ 'bg-black/50': totalEpisodes }">
|
||||
<ui-loading-indicator />
|
||||
</div>
|
||||
<div v-else-if="!totalEpisodes" class="h-44 flex items-center justify-center">
|
||||
<div v-else-if="!totalEpisodes" id="no-episodes" class="h-44 flex items-center justify-center">
|
||||
<p class="text-lg">{{ $strings.MessageNoEpisodes }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -80,7 +80,8 @@ export default {
|
|||
episodeComponentRefs: {},
|
||||
windowHeight: 0,
|
||||
episodesTableOffsetTop: 0,
|
||||
episodeRowHeight: 176
|
||||
episodeRowHeight: 44 * 4, // h-44,
|
||||
currScrollTop: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -484,9 +485,8 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
scroll(evt) {
|
||||
if (!evt?.target?.scrollTop) return
|
||||
const scrollTop = Math.max(evt.target.scrollTop - this.episodesTableOffsetTop, 0)
|
||||
handleScroll() {
|
||||
const scrollTop = this.currScrollTop
|
||||
let firstEpisodeIndex = Math.floor(scrollTop / this.episodeRowHeight)
|
||||
let lastEpisodeIndex = Math.ceil((scrollTop + this.windowHeight) / this.episodeRowHeight)
|
||||
lastEpisodeIndex = Math.min(this.totalEpisodes - 1, lastEpisodeIndex)
|
||||
|
|
@ -501,6 +501,12 @@ export default {
|
|||
})
|
||||
this.mountEpisodes(firstEpisodeIndex, lastEpisodeIndex + 1)
|
||||
},
|
||||
scroll(evt) {
|
||||
if (!evt?.target?.scrollTop) return
|
||||
const scrollTop = Math.max(evt.target.scrollTop - this.episodesTableOffsetTop, 0)
|
||||
this.currScrollTop = scrollTop
|
||||
this.handleScroll()
|
||||
},
|
||||
initListeners() {
|
||||
const itemPageWrapper = document.getElementById('item-page-wrapper')
|
||||
if (itemPageWrapper) {
|
||||
|
|
@ -532,11 +538,24 @@ export default {
|
|||
this.episodesTableOffsetTop = (lazyEpisodesTableEl?.offsetTop || 0) + 64
|
||||
|
||||
this.windowHeight = window.innerHeight
|
||||
this.episodesPerPage = Math.ceil(this.windowHeight / this.episodeRowHeight)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.mountEpisodes(0, Math.min(this.episodesPerPage, this.totalEpisodes))
|
||||
this.recalcEpisodeRowHeight()
|
||||
this.episodesPerPage = Math.ceil(this.windowHeight / this.episodeRowHeight)
|
||||
// Maybe update currScrollTop if items were removed
|
||||
const itemPageWrapper = document.getElementById('item-page-wrapper')
|
||||
const { scrollHeight, clientHeight } = itemPageWrapper
|
||||
const maxScrollTop = scrollHeight - clientHeight
|
||||
this.currScrollTop = Math.min(this.currScrollTop, maxScrollTop)
|
||||
this.handleScroll()
|
||||
})
|
||||
},
|
||||
recalcEpisodeRowHeight() {
|
||||
const episodeRowEl = document.getElementById('episode-0') || document.getElementById('no-episodes')
|
||||
if (episodeRowEl) {
|
||||
const height = getComputedStyle(episodeRowEl).height
|
||||
this.episodeRowHeight = parseInt(height) || this.episodeRowHeight
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
|
|
@ -1,24 +1,6 @@
|
|||
<template>
|
||||
<div ref="wrapper" class="relative">
|
||||
<input
|
||||
:id="inputId"
|
||||
:name="inputName"
|
||||
ref="input"
|
||||
v-model="inputValue"
|
||||
:type="actualType"
|
||||
:step="step"
|
||||
:min="min"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:placeholder="placeholder"
|
||||
dir="auto"
|
||||
class="rounded bg-primary text-gray-200 focus:border-gray-300 focus:bg-bg focus:outline-none border border-gray-600 h-full w-full"
|
||||
:class="classList"
|
||||
@keyup="keyup"
|
||||
@change="change"
|
||||
@focus="focused"
|
||||
@blur="blurred"
|
||||
/>
|
||||
<input :id="inputId" :name="inputName" ref="input" v-model="inputValue" :type="actualType" :step="step" :min="min" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" dir="auto" class="rounded bg-primary text-gray-200 focus:bg-bg focus:outline-none border h-full w-full" :class="classList" @keyup="keyup" @change="change" @focus="focused" @blur="blurred" />
|
||||
<div v-if="clearable && inputValue" class="absolute top-0 right-0 h-full px-2 flex items-center justify-center">
|
||||
<span class="material-symbols text-gray-300 cursor-pointer" style="font-size: 1.1rem" @click.stop.prevent="clear">close</span>
|
||||
</div>
|
||||
|
|
@ -65,7 +47,8 @@ export default {
|
|||
showPassword: false,
|
||||
isHovering: false,
|
||||
isFocused: false,
|
||||
hasCopied: false
|
||||
hasCopied: false,
|
||||
isInvalidDate: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -84,6 +67,10 @@ export default {
|
|||
if (this.noSpinner) _list.push('no-spinner')
|
||||
if (this.textCenter) _list.push('text-center')
|
||||
if (this.customInputClass) _list.push(this.customInputClass)
|
||||
|
||||
if (this.isInvalidDate) _list.push('border-error')
|
||||
else _list.push('focus:border-gray-300 border-gray-600')
|
||||
|
||||
return _list.join(' ')
|
||||
},
|
||||
actualType() {
|
||||
|
|
@ -118,6 +105,14 @@ export default {
|
|||
},
|
||||
keyup(e) {
|
||||
this.$emit('keyup', e)
|
||||
|
||||
if (this.type === 'datetime-local') {
|
||||
if (e.target.validity?.badInput) {
|
||||
this.isInvalidDate = true
|
||||
} else {
|
||||
this.isInvalidDate = false
|
||||
}
|
||||
}
|
||||
},
|
||||
blur() {
|
||||
if (this.$refs.input) this.$refs.input.blur()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<slot>
|
||||
<label :for="identifier" class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': disabled }"
|
||||
>{{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em></label
|
||||
>
|
||||
<label :for="identifier" class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': disabled }">
|
||||
{{ label }}
|
||||
<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em>
|
||||
</label>
|
||||
</slot>
|
||||
<ui-text-input :placeholder="placeholder || label" :inputId="identifier" ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" :class="inputClass" @blur="inputBlurred" />
|
||||
</div>
|
||||
|
|
@ -57,4 +58,4 @@ export default {
|
|||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -249,11 +249,33 @@ export default {
|
|||
}
|
||||
}
|
||||
return target
|
||||
},
|
||||
enableBreakParagraphOnReturn() {
|
||||
// Trix works with divs by default, we want paragraphs instead
|
||||
Trix.config.blockAttributes.default.tagName = 'p'
|
||||
// Enable break paragraph on Enter (Shift + Enter will still create a line break)
|
||||
Trix.config.blockAttributes.default.breakOnReturn = true
|
||||
|
||||
// Hack to fix buggy paragraph breaks
|
||||
// Copied from https://github.com/basecamp/trix/issues/680#issuecomment-735742942
|
||||
Trix.Block.prototype.breaksOnReturn = function () {
|
||||
const attr = this.getLastAttribute()
|
||||
const config = Trix.getBlockConfig(attr ? attr : 'default')
|
||||
return config ? config.breakOnReturn : false
|
||||
}
|
||||
Trix.LineBreakInsertion.prototype.shouldInsertBlockBreak = function () {
|
||||
if (this.block.hasAttributes() && this.block.isListItem() && !this.block.isEmpty()) {
|
||||
return this.startLocation.offset > 0
|
||||
} else {
|
||||
return !this.shouldBreakFormattedBlock() ? this.breaksOnReturn : false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/** Override editor configuration */
|
||||
this.overrideConfig(this.config)
|
||||
this.enableBreakParagraphOnReturn()
|
||||
/** Check if editor read-only mode is required */
|
||||
this.decorateDisabledEditor(this.disabledEditor)
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -283,4 +305,4 @@ export default {
|
|||
.trix_container .trix-content {
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue