This commit is contained in:
Jason 2024-04-23 21:25:11 +00:00 committed by GitHub
commit 66cf8e2058
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 317 additions and 203 deletions

View File

@ -64,6 +64,7 @@
"lodash.debounce": "^4.0.8",
"marked": "^12.0.2",
"path-browserify": "^1.0.1",
"portal-vue": "^2.1.7",
"process": "^0.11.10",
"swiper": "^11.1.1",
"video.js": "7.21.5",

View File

@ -11,6 +11,7 @@ import FtToast from './components/ft-toast/ft-toast.vue'
import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue'
import FtPlaylistAddVideoPrompt from './components/ft-playlist-add-video-prompt/ft-playlist-add-video-prompt.vue'
import FtCreatePlaylistPrompt from './components/ft-create-playlist-prompt/ft-create-playlist-prompt.vue'
import FtSearchFilters from './components/ft-search-filters/ft-search-filters.vue'
import { marked } from 'marked'
import { IpcChannels } from '../constants'
import packageDetails from '../../package.json'
@ -34,6 +35,7 @@ export default defineComponent({
FtProgressBar,
FtPlaylistAddVideoPrompt,
FtCreatePlaylistPrompt,
FtSearchFilters
},
data: function () {
return {
@ -46,6 +48,7 @@ export default defineComponent({
latestBlogUrl: '',
updateChangelog: '',
changeLogTitle: '',
isPromptOpen: false,
lastExternalLinkToBeOpened: '',
showExternalLinkOpeningPrompt: false,
externalLinkOpeningPromptValues: [
@ -77,6 +80,9 @@ export default defineComponent({
showCreatePlaylistPrompt: function () {
return this.$store.getters.getShowCreatePlaylistPrompt
},
showSearchFilters: function () {
return this.$store.getters.getShowSearchFilters
},
windowTitle: function () {
const routePath = this.$route.path
if (!routePath.startsWith('/channel/') && !routePath.startsWith('/watch/') && !routePath.startsWith('/hashtag/')) {
@ -143,12 +149,6 @@ export default defineComponent({
secColor: 'checkThemeSettings',
locale: 'setLocale',
$route () {
// react to route changes...
// Hide top nav filter panel on page change
this.$refs.topNav?.hideFilters()
}
},
created () {
this.checkThemeSettings()
@ -295,6 +295,10 @@ export default defineComponent({
this.showBlogBanner = false
},
handlePromptPortalUpdate: function(newVal) {
this.isPromptOpen = newVal
},
openDownloadsPage: function () {
const url = 'https://freetubeapp.io#download'
openExternalLink(url)
@ -544,7 +548,7 @@ export default defineComponent({
'updateMainColor',
'updateSecColor',
'showOutlines',
'hideOutlines'
'hideOutlines',
])
}
})

View File

@ -8,11 +8,63 @@
isLocaleRightToLeft: isLocaleRightToLeft
}"
>
<top-nav ref="topNav" />
<side-nav ref="sideNav" />
<portal-target
name="promptPortal"
@change="handlePromptPortalUpdate"
/>
<ft-prompt
v-if="showReleaseNotes"
:label="changeLogTitle"
@click="showReleaseNotes = !showReleaseNotes"
>
<span
class="changeLogText"
v-html="updateChangelog"
/>
<ft-flex-box>
<ft-button
:label="$t('Download From Site')"
@click="openDownloadsPage"
/>
<ft-button
:label="$t('Close')"
@click="showReleaseNotes = !showReleaseNotes"
/>
</ft-flex-box>
</ft-prompt>
<ft-prompt
v-if="showExternalLinkOpeningPrompt"
:label="$t('Are you sure you want to open this link?')"
:extra-labels="[lastExternalLinkToBeOpened]"
:option-names="externalLinkOpeningPromptNames"
:option-values="externalLinkOpeningPromptValues"
@click="handleExternalLinkOpeningPromptAnswer"
/>
<ft-search-filters
v-if="showSearchFilters"
/>
<ft-playlist-add-video-prompt
v-if="showAddToPlaylistPrompt"
/>
<ft-create-playlist-prompt
v-if="showCreatePlaylistPrompt"
/>
<ft-toast />
<ft-progress-bar
v-if="showProgressBar"
/>
<top-nav
ref="topNav"
:inert="isPromptOpen"
/>
<side-nav
ref="sideNav"
:inert="isPromptOpen"
/>
<ft-flex-box
class="flexBox routerView"
role="main"
:inert="isPromptOpen"
>
<div
v-if="showUpdatesBanner || showBlogBanner"
@ -43,48 +95,9 @@
ref="router"
class="routerView"
/>
<!-- </keep-alive> -->
<!-- </keep-alive> -->
</transition>
</ft-flex-box>
<ft-prompt
v-if="showReleaseNotes"
:label="changeLogTitle"
@click="showReleaseNotes = !showReleaseNotes"
>
<span
class="changeLogText"
v-html="updateChangelog"
/>
<ft-flex-box>
<ft-button
:label="$t('Download From Site')"
@click="openDownloadsPage"
/>
<ft-button
:label="$t('Close')"
@click="showReleaseNotes = !showReleaseNotes"
/>
</ft-flex-box>
</ft-prompt>
<ft-prompt
v-if="showExternalLinkOpeningPrompt"
:label="$t('Are you sure you want to open this link?')"
:extra-labels="[lastExternalLinkToBeOpened]"
:option-names="externalLinkOpeningPromptNames"
:option-values="externalLinkOpeningPromptValues"
@click="handleExternalLinkOpeningPromptAnswer"
/>
<ft-playlist-add-video-prompt
v-if="showAddToPlaylistPrompt"
/>
<ft-create-playlist-prompt
v-if="showCreatePlaylistPrompt"
/>
<ft-toast />
<ft-progress-bar
v-if="showProgressBar"
/>
</div>
</template>

View File

@ -1,4 +1,4 @@
import { defineComponent } from 'vue'
import { defineComponent, nextTick } from 'vue'
import { mapActions } from 'vuex'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtPrompt from '../ft-prompt/ft-prompt.vue'
@ -24,6 +24,9 @@ export default defineComponent({
}
},
computed: {
title: function () {
return this.$t('User Playlists.CreatePlaylistPrompt.New Playlist Name')
},
allPlaylists: function () {
return this.$store.getters.getAllPlaylists
},
@ -34,7 +37,7 @@ export default defineComponent({
mounted: function () {
this.playlistName = this.newPlaylistVideoObject.title
// Faster to input required playlist name
this.$refs.playlistNameInput.focus()
nextTick(() => this.$refs.playlistNameInput.focus())
},
methods: {
createNewPlaylist: function () {

View File

@ -1,9 +1,10 @@
<template>
<ft-prompt
:label="title"
@click="hideCreatePlaylistPrompt"
>
<h2 class="center">
{{ $t('User Playlists.CreatePlaylistPrompt.New Playlist Name') }}
{{ title }}
</h2>
<ft-flex-box>
<ft-input

View File

@ -6,16 +6,6 @@
text-align: center;
}
/* Style for `ft-prompt` */
:deep(.promptCard) {
/* Currently only this prompt has enough content to make prompt too high */
max-block-size: 95%;
/* Some child(s) will grow vertically */
display: flex;
flex-direction: column;
}
.searchInputsRow {
display: grid;

View File

@ -1,4 +1,4 @@
import { defineComponent } from 'vue'
import { defineComponent, nextTick } from 'vue'
import { mapActions } from 'vuex'
import debounce from 'lodash.debounce'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
@ -47,6 +47,12 @@ export default defineComponent({
}
},
computed: {
title: function () {
return this.$tc('User Playlists.AddVideoPrompt.Select a playlist to add your N videos to', this.toBeAddedToPlaylistVideoCount, {
videoCount: this.toBeAddedToPlaylistVideoCount,
})
},
showingCreatePlaylistPrompt: function () {
return this.$store.getters.getShowCreatePlaylistPrompt
},
@ -183,7 +189,7 @@ export default defineComponent({
if (val > oldVal) {
// Focus back to search input only when playlist added
// Allow search and easier deselecting new created playlist
this.$refs.searchBar.focus()
nextTick(() => this.$refs.searchBar.focus())
}
},
@ -193,7 +199,7 @@ export default defineComponent({
// Only care when CreatePlaylistPrompt hidden
// Shift focus from button to prevent unwanted click event
// due to enter key press in CreatePlaylistPrompt
this.$refs.searchBar.focus()
nextTick(() => this.$refs.searchBar.focus())
},
},
mounted: function () {
@ -201,10 +207,10 @@ export default defineComponent({
this.updateQueryDebounce = debounce(this.updateQuery, 500)
// User might want to search first if they have many playlists
this.$refs.searchBar.focus()
nextTick(() => this.$refs.searchBar.focus())
},
beforeDestroy() {
this.lastActiveElement?.focus()
nextTick(() => this.lastActiveElement?.focus())
},
methods: {
hide: function () {

View File

@ -1,11 +1,11 @@
<template>
<ft-prompt
theme="flex-column"
:label="title"
@click="hide"
>
<h2 class="heading">
{{ $tc('User Playlists.AddVideoPrompt.Select a playlist to add your N videos to', toBeAddedToPlaylistVideoCount, {
videoCount: toBeAddedToPlaylistVideoCount,
}) }}
{{ title }}
</h2>
<p class="selected-count">
{{ $tc('User Playlists.AddVideoPrompt.N playlists selected', selectedPlaylistCount, {

View File

@ -7,7 +7,7 @@
background-color: rgb(0 0 0 / 70%);
/* Higher than components like playlist info */
z-index: 200;
z-index: 201;
padding: 15px;
box-sizing: border-box;
display: flex;
@ -17,6 +17,7 @@
.promptCard {
overflow-y: scroll;
max-block-size: 95%;
}
.promptCard.autosize {
@ -34,6 +35,31 @@
box-sizing: border-box;
}
.promptCard.flex-column {
/* Some child(ren) will grow vertically */
display: flex;
flex-direction: column;
}
.promptCard.slim {
max-inline-size: 70%;
inset-inline-start: 15%;
padding-block: 10px;
}
.center {
text-align: center;
}
@media only screen and (width <= 680px) {
.promptCard.slim {
padding-block: 5px;
}
}
@media only screen and (width <= 500px) {
.promptCard.slim {
max-inline-size: 80%;
inset-inline-start: 10%;
}
}

View File

@ -15,7 +15,7 @@ export default defineComponent({
props: {
label: {
type: String,
default: ''
required: true
},
extraLabels: {
type: Array,
@ -36,6 +36,10 @@ export default defineComponent({
autosize: {
type: Boolean,
default: false
},
theme: {
type: String,
default: 'base'
}
},
emits: ['click'],
@ -56,15 +60,16 @@ export default defineComponent({
},
mounted: function () {
this.lastActiveElement = document.activeElement
document.addEventListener('keydown', this.closeEventFunction, true)
document.querySelector('.prompt').addEventListener('keydown', this.arrowKeys, true)
this.promptButtons = Array.from(
document.querySelector('.prompt .promptCard .ft-flex-box').childNodes
).filter((e) => {
return e.id && e.id.startsWith('prompt')
this.$nextTick(() => {
document.addEventListener('keydown', this.closeEventFunction, true)
document.querySelector('.prompt').addEventListener('keydown', this.arrowKeys, true)
this.promptButtons = Array.from(
document.querySelector('.prompt .promptCard .ft-flex-box').childNodes
).filter((e) => {
return e.id && e.id.startsWith('prompt')
})
this.focusItem(0)
})
this.focusItem(0)
},
methods: {
click: function (value) {

View File

@ -1,52 +1,57 @@
<template>
<div
class="prompt"
tabindex="-1"
@click="handleHide"
@keydown.enter="handleHide"
>
<ft-card
class="promptCard"
:class="{ autosize }"
:aria-labelledby="('dialog-' + sanitizedLabel)"
<portal to="promptPortal">
<div
ref="openPrompt"
class="prompt"
tabindex="-1"
@click="handleHide"
@keydown.enter="handleHide"
>
<slot>
<h2
:id="'dialog-' + sanitizedLabel"
class="center"
>
{{ label }}
</h2>
<p
v-for="extraLabel in extraLabels"
:key="extraLabel"
class="center"
>
<strong>
{{ extraLabel }}
</strong>
</p>
<ft-flex-box>
<ft-button
v-for="(option, index) in optionNames"
:id="'prompt-' + sanitizedLabel + '-' + index"
:key="index"
:label="option"
@click="click(optionValues[index])"
/>
<ft-button
v-if="showClose"
:id="'prompt-' + sanitizedLabel + '-close'"
:label="$t('Close')"
:tabindex="0"
text-color="'var(--accent-color)'"
background-color="'var(--text-with-accent-color)'"
@click="hide"
/>
</ft-flex-box>
</slot>
</ft-card>
</div>
<ft-card
class="promptCard"
:class="{ autosize, [theme]: true }"
role="dialog"
aria-modal="true"
:aria-labelledby="('dialog-' + sanitizedLabel)"
>
<slot>
<h2
:id="'dialog-' + sanitizedLabel"
class="center"
>
{{ label }}
</h2>
<p
v-for="extraLabel in extraLabels"
:key="extraLabel"
class="center"
>
<strong>
{{ extraLabel }}
</strong>
</p>
<ft-flex-box>
<ft-button
v-for="(option, index) in optionNames"
:id="'prompt-' + sanitizedLabel + '-' + index"
:key="index"
:label="option"
@click="click(optionValues[index])"
/>
<ft-button
v-if="showClose"
:id="'prompt-' + sanitizedLabel + '-close'"
:label="$t('Close')"
:tabindex="0"
text-color="'var(--accent-color)'"
background-color="'var(--text-with-accent-color)'"
@click="hide"
/>
</ft-flex-box>
</slot>
</ft-card>
</div>
</portal>
</template>
<script src="./ft-prompt.js" />

View File

@ -11,7 +11,7 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
}
.pure-checkbox input[type="checkbox"]:focus + label::before, .pure-radiobutton input[type="checkbox"]:focus + label::before, .pure-checkbox input[type="radio"]:focus + label::before, .pure-radiobutton input[type="radio"]:focus + label::before, .pure-checkbox input[type="checkbox"]:hover + label::before, .pure-radiobutton input[type="checkbox"]:hover + label::before, .pure-checkbox input[type="radio"]:hover + label::before, .pure-radiobutton input[type="radio"]:hover + label::before {
border-color: var(--primary-color);
border: 2px solid var(--primary-color);
}
.pure-checkbox input[type="checkbox"]:active + label::before, .pure-radiobutton input[type="checkbox"]:active + label::before, .pure-checkbox input[type="radio"]:active + label::before, .pure-radiobutton input[type="radio"]:active + label::before { transition-duration: 0s; }
@ -22,20 +22,20 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
user-select: none;
cursor: pointer;
display: block;
margin-block-end: -20px;
margin-block-start: 10px;
}
.pure-checkbox input[type="checkbox"] + label::before, .pure-radiobutton input[type="checkbox"] + label::before, .pure-checkbox input[type="radio"] + label::before, .pure-radiobutton input[type="radio"] + label::before {
box-sizing: content-box;
content: '';
color: var(--primary-color);
color: var(--primary-text-color);
position: absolute;
inset-block-start: 50%;
inset-inline-start: 0;
inline-size: 14px;
block-size: 14px;
margin-block-start: -9px;
border: 2px solid var(--primary-color);
border: 2px solid var(--primary-text-color);
text-align: center;
transition: all 0.4s ease;
}
@ -43,7 +43,7 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
.pure-checkbox input[type="checkbox"] + label::after, .pure-radiobutton input[type="checkbox"] + label::after, .pure-checkbox input[type="radio"] + label::after, .pure-radiobutton input[type="radio"] + label::after {
box-sizing: content-box;
content: '';
background-color: var(--primary-color);
background-color: var(--primary-text-color);
position: absolute;
inset-block-start: 50%;
inset-inline-start: 4px;
@ -84,23 +84,33 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
animation: borderscale 300ms ease-in;
}
.pure-radiobutton input[type="radio"]:focus + label::after{
background-color: var(--primary-color);
}
.pure-checkbox input[type="radio"]:checked + label::after, .pure-radiobutton input[type="radio"]:checked + label::after { transform: scale(1); }
.pure-checkbox input[type="radio"] + label::before, .pure-radiobutton input[type="radio"] + label::before, .pure-checkbox input[type="radio"] + label::after, .pure-radiobutton input[type="radio"] + label::after { border-radius: 50%; }
.pure-checkbox input[type="checkbox"]:checked + label::before, .pure-radiobutton input[type="checkbox"]:checked + label::before {
animation: borderscale 200ms ease-in;
background: var(--primary-color);
background: var(--primary-text-color);
}
.pure-radiobutton input[type="checkbox"]:checked + label::after { transform: rotate(-45deg) scale(1); }
@keyframes
borderscale { 50% {
box-shadow: 0 0 0 2px var(--primary-color);
box-shadow: 0 0 0 2px var(--primary-text-color);
}
}
.radioTitle {
margin-block-end: -20px;
margin-block: 0;
}
@media only screen and (width <= 680px) {
.pure-checkbox input[type="checkbox"] + label, .pure-radiobutton input[type="checkbox"] + label, .pure-checkbox input[type="radio"] + label, .pure-radiobutton input[type="radio"] + label {
margin-block-start: 3px;
}
}

View File

@ -18,6 +18,10 @@ export default defineComponent({
disabled: {
type: Boolean,
default: false
},
initialValueIndex: {
type: Number,
default: 0
}
},
emits: ['change'],
@ -35,7 +39,7 @@ export default defineComponent({
},
mounted: function () {
this.id = this._uid
this.selectedValue = this.values[0]
this.selectedValue = this.values[this.initialValueIndex]
},
methods: {
updateSelectedValue: function (value) {

View File

@ -14,7 +14,7 @@
v-model="selectedValue"
:name="inputName"
:value="values[index]"
:checked="index === 0"
:checked="index === initialValueIndex"
:disabled="disabled"
class="radio"
type="radio"

View File

@ -1,17 +1,7 @@
.searchFilterInner {
max-inline-size: 800px;
margin-inline: auto;
padding-block: 20px 70px;
padding-inline: 20px;
max-block-size: 410px;
overflow-y: auto;
background-color: var(--card-bg-color);
box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
opacity: 0.9;
}
.center {
margin-block: 5px 10px;
text-align: center;
user-select: none;
}
.searchRadio {
@ -19,13 +9,22 @@
}
.radioFlexBox {
max-inline-size: 1000px;
margin-block: 0;
margin-inline: auto;
}
@media only screen and (width <= 600px) {
.searchFilterCloseButtonContainer {
display: flex;
justify-content: center;
}
@media only screen and (width <= 680px) {
.searchRadio {
border-inline-end: 0;
padding-block-start: 0;
}
.radioFlexBox {
flex-flow: column;
}
}

View File

@ -1,16 +1,24 @@
import { defineComponent } from 'vue'
import { mapActions } from 'vuex'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtRadioButton from '../ft-radio-button/ft-radio-button.vue'
import FtPrompt from '../ft-prompt/ft-prompt.vue'
import FtButton from '../ft-button/ft-button.vue'
export default defineComponent({
name: 'FtSearchFilters',
components: {
'ft-flex-box': FtFlexBox,
'ft-radio-button': FtRadioButton
'ft-radio-button': FtRadioButton,
'ft-prompt': FtPrompt,
'ft-button': FtButton
},
emits: ['filterValueUpdated'],
data: function () {
return {
searchSortByStartIndex: 0,
searchTimeStartIndex: 0,
searchTypeStartIndex: 0,
searchDurationStartIndex: 0,
sortByValues: [
'relevance',
'rating',
@ -41,11 +49,15 @@ export default defineComponent({
}
},
computed: {
title: function () {
return this.$t('Search Filters.Search Filters')
},
searchSettings: function () {
return this.$store.getters.getSearchSettings
},
filterValueChanged: function() {
searchFilterValueChanged: function() {
return [
this.$refs.sortByRadio.selectedValue !== this.sortByValues[0],
this.$refs.timeRadio.selectedValue !== this.timeValues[0],
@ -93,6 +105,12 @@ export default defineComponent({
]
}
},
created: function () {
this.searchSortByStartIndex = this.sortByValues.indexOf(this.searchSettings.sortBy)
this.searchTimeStartIndex = this.timeValues.indexOf(this.searchSettings.time)
this.searchTypeStartIndex = this.typeValues.indexOf(this.searchSettings.type)
this.searchDurationStartIndex = this.durationValues.indexOf(this.searchSettings.duration)
},
methods: {
isVideoOrMovieOrAll(type) {
return type === 'video' || type === 'movie' || type === 'all'
@ -100,7 +118,7 @@ export default defineComponent({
updateSortBy: function (value) {
this.$store.commit('setSearchSortBy', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateTime: function (value) {
@ -110,7 +128,7 @@ export default defineComponent({
this.$store.commit('setSearchType', 'all')
}
this.$store.commit('setSearchTime', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateType: function (value) {
@ -126,7 +144,7 @@ export default defineComponent({
this.$store.commit('setSearchSortBy', this.sortByValues[0])
}
this.$store.commit('setSearchType', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateDuration: function (value) {
@ -136,7 +154,11 @@ export default defineComponent({
this.updateType('all')
}
this.$store.commit('setSearchDuration', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
}
this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
...mapActions([
'hideSearchFilters'
])
}
})

View File

@ -1,8 +1,15 @@
<template>
<div>
<div class="searchFilterInner">
<h2 class="center">
{{ $t("Search Filters.Search Filters") }}
<ft-prompt
theme="slim"
:label="title"
@click="hideSearchFilters"
>
<div>
<h2
class="center"
name="title"
>
{{ title }}
</h2>
<ft-flex-box class="radioFlexBox">
<ft-radio-button
@ -10,6 +17,7 @@
:title="$t('Search Filters.Sort By.Sort By')"
:labels="sortByLabels"
:values="sortByValues"
:initial-value-index="searchSortByStartIndex"
class="searchRadio"
@change="updateSortBy"
/>
@ -18,6 +26,7 @@
:title="$t('Search Filters.Time.Time')"
:labels="timeLabels"
:values="timeValues"
:initial-value-index="searchTimeStartIndex"
class="searchRadio"
@change="updateTime"
/>
@ -26,6 +35,7 @@
:title="$t('Search Filters.Type.Type')"
:labels="typeLabels"
:values="typeValues"
:initial-value-index="searchTypeStartIndex"
class="searchRadio"
@change="updateType"
/>
@ -34,12 +44,21 @@
:title="$t('Search Filters.Duration.Duration')"
:labels="durationLabels"
:values="durationValues"
:initial-value-index="searchDurationStartIndex"
class="searchRadio"
@change="updateDuration"
/>
</ft-flex-box>
<div class="searchFilterCloseButtonContainer">
<ft-button
:label="$t('Close')"
background-color="var(--primary-color)"
text-color="var(--text-with-main-color)"
@click="hideSearchFilters"
/>
</div>
</div>
</div>
</ft-prompt>
</template>
<script src="./ft-search-filters.js" />

View File

@ -1,7 +1,6 @@
import { defineComponent } from 'vue'
import { mapActions } from 'vuex'
import FtInput from '../ft-input/ft-input.vue'
import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue'
import FtProfileSelector from '../ft-profile-selector/ft-profile-selector.vue'
import debounce from 'lodash.debounce'
@ -15,15 +14,12 @@ export default defineComponent({
name: 'TopNav',
components: {
FtInput,
FtSearchFilters,
FtProfileSelector
},
data: () => {
return {
component: this,
showSearchContainer: true,
showFilters: false,
searchFilterValueChanged: false,
historyIndex: 1,
isForwardOrBack: false,
isArrowBackwardDisabled: true,
@ -84,6 +80,10 @@ export default defineComponent({
return this.$store.getters.getExpandSideBar
},
searchFilterValueChanged: function () {
return this.$store.getters.getSearchFilterValueChanged
},
forwardText: function () {
return this.$t('Forward')
},
@ -216,9 +216,6 @@ export default defineComponent({
}
}
})
// Close the filter panel
this.showFilters = false
},
focusSearch: function () {
@ -295,11 +292,6 @@ export default defineComponent({
toggleSearchContainer: function () {
this.showSearchContainer = !this.showSearchContainer
this.showFilters = false
},
handleSearchFilterValueChanged: function (filterValueChanged) {
this.searchFilterValueChanged = filterValueChanged
},
navigateHistory: function () {
@ -354,14 +346,12 @@ export default defineComponent({
navigate: function (route) {
this.$router.push('/' + route)
},
hideFilters: function () {
this.showFilters = false
},
updateSearchInputText: function (text) {
this.$refs.searchInput.updateInputData(text)
},
...mapActions([
'getYoutubeUrlInfo',
'showSearchFilters'
])
}
})

View File

@ -89,15 +89,17 @@
}
.navFilterIcon {
$effect-distance: 10px;
$effect-distance: 20px;
margin-inline-start: $effect-distance;
&.filterChanged {
box-shadow: 0 0 $effect-distance var(--primary-color);
color: var(--primary-color);
@include top-nav-is-colored {
box-shadow: 0 0 $effect-distance var(--text-with-main-color);
color: var(--text-with-main-color);
}
}
}
@ -209,18 +211,4 @@
flex: 1;
}
}
.searchFilters {
inset-inline: 0;
margin-block: 10px 20px;
margin-inline: 220px 20px;
position: absolute;
transition: margin 150ms ease-in-out;
@media only screen and (width <= 680px) {
inset-inline: 0;
margin-block: 95px 0;
margin-inline: 10px;
}
}
}

View File

@ -94,18 +94,13 @@
class="navFilterIcon navIcon"
:class="{ filterChanged: searchFilterValueChanged }"
:icon="['fas', 'filter']"
:title="$t('Search Filters.Search Filters')"
role="button"
tabindex="0"
@click="showFilters = !showFilters"
@keydown.enter.prevent="showFilters = !showFilters"
@click="showSearchFilters"
@keydown.enter.prevent="showSearchFilters"
/>
</div>
<ft-search-filters
v-if="!hideSearchBar"
v-show="showFilters"
class="searchFilters"
@filterValueUpdated="handleSearchFilterValueChanged"
/>
</div>
<ft-profile-selector class="side profiles" />
</div>

View File

@ -81,6 +81,7 @@ import {
faMonero
} from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import PortalVue from 'portal-vue'
Vue.config.devtools = process.env.NODE_ENV === 'development'
Vue.config.performance = process.env.NODE_ENV === 'development'
@ -172,6 +173,7 @@ new Vue({
i18n,
render: h => h(App)
})
Vue.use(PortalVue)
// to avoid accessing electron api from web app build
if (process.env.IS_ELECTRON) {

View File

@ -33,6 +33,8 @@ const state = {
showProgressBar: false,
showAddToPlaylistPrompt: false,
showCreatePlaylistPrompt: false,
showSearchFilters: false,
searchFilterValueChanged: false,
progressBarPercentage: 0,
toBeAddedToPlaylistVideoList: [],
newPlaylistDefaultProperties: {},
@ -94,6 +96,10 @@ const getters = {
return state.searchSettings
},
getSearchFilterValueChanged () {
return state.searchFilterValueChanged
},
getShowAddToPlaylistPrompt () {
return state.showAddToPlaylistPrompt
},
@ -102,6 +108,10 @@ const getters = {
return state.showCreatePlaylistPrompt
},
getShowSearchFilters () {
return state.showSearchFilters
},
getToBeAddedToPlaylistVideoList () {
return state.toBeAddedToPlaylistVideoList
},
@ -168,7 +178,7 @@ const getters = {
getLastVideoRefreshTimestampByProfile: (state) => (profileId) => {
return state.lastVideoRefreshTimestampByProfile[profileId]
}
},
}
const actions = {
@ -378,6 +388,14 @@ const actions = {
commit('setShowCreatePlaylistPrompt', false)
},
showSearchFilters ({ commit }) {
commit('setShowSearchFilters', true)
},
hideSearchFilters ({ commit }) {
commit('setShowSearchFilters', false)
},
updateShowProgressBar ({ commit }, value) {
commit('setShowProgressBar', value)
},
@ -839,6 +857,10 @@ const mutations = {
state.showCreatePlaylistPrompt = payload
},
setShowSearchFilters (state, payload) {
state.showSearchFilters = payload
},
setToBeAddedToPlaylistVideoList (state, payload) {
state.toBeAddedToPlaylistVideoList = payload
},
@ -899,6 +921,10 @@ const mutations = {
state.cachedPlaylist = value
},
setSearchFilterValueChanged (state, value) {
state.searchFilterValueChanged = value
},
setSearchSortBy (state, value) {
state.searchSettings.sortBy = value
},

View File

@ -6681,6 +6681,11 @@ pluralize@^8.0.0:
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
portal-vue@^2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.7.tgz#ea08069b25b640ca08a5b86f67c612f15f4e4ad4"
integrity sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==
postcss-calc@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6"