forked from platypush/platypush
[#414] Added ability to sort Jellyin results.
This also adds a new `FloatingDropdownButton` component.
This commit is contained in:
parent
bf82ad9bf0
commit
1a53c59382
5 changed files with 227 additions and 6 deletions
|
@ -59,11 +59,19 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
button() {
|
||||
const el = this.$refs.button?.$el
|
||||
if (!el)
|
||||
return this.$refs.button
|
||||
|
||||
return el.querySelector('button')
|
||||
},
|
||||
|
||||
buttonStyle() {
|
||||
if (!this.$refs.button)
|
||||
if (!this.button)
|
||||
return {}
|
||||
|
||||
return getComputedStyle(this.$refs.button)
|
||||
return getComputedStyle(this.button)
|
||||
},
|
||||
|
||||
buttonWidth() {
|
||||
|
@ -140,7 +148,7 @@ export default {
|
|||
},
|
||||
|
||||
adjustDropdownPos() {
|
||||
const buttonRect = this.$refs.button.getBoundingClientRect()
|
||||
const buttonRect = this.button.getBoundingClientRect()
|
||||
const buttonPos = {
|
||||
left: buttonRect.left + window.scrollX,
|
||||
top: buttonRect.top + window.scrollY,
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div class="floating-dropdown-container">
|
||||
<FloatingButton :disabled="disabled"
|
||||
:iconClass="iconClass"
|
||||
:iconUrl="iconUrl"
|
||||
:glow="glow"
|
||||
:left="left"
|
||||
:right="right"
|
||||
:title="title"
|
||||
:top="top"
|
||||
:bottom="bottom"
|
||||
ref="button"
|
||||
@click.stop="toggle($event)" />
|
||||
|
||||
<div class="body-container hidden" ref="dropdownContainer">
|
||||
<DropdownBody :id="id"
|
||||
:keepOpenOnItemClick="keepOpenOnItemClick"
|
||||
:style="style"
|
||||
ref="dropdown"
|
||||
@click="onClick">
|
||||
<slot />
|
||||
</DropdownBody>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "./Dropdown";
|
||||
import DropdownBody from "./DropdownBody";
|
||||
import FloatingButton from "@/components/elements/FloatingButton";
|
||||
|
||||
export default {
|
||||
mixins: [Dropdown, FloatingButton],
|
||||
emits: ['click'],
|
||||
components: {
|
||||
DropdownBody,
|
||||
FloatingButton,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.floating-dropdown-container {
|
||||
.body-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
|
||||
button {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
&:hover {
|
||||
color: $default-hover-fg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -24,7 +24,7 @@ import Index from "./Jellyfin/Index";
|
|||
import Loading from "@/components/Loading";
|
||||
import MediaNav from "./Nav";
|
||||
import MediaProvider from "./Mixin";
|
||||
import Movies from "./Jellyfin/Collections/Movies/Index";
|
||||
import Movies from "./Jellyfin/collections/Movies/Index";
|
||||
|
||||
export default {
|
||||
mixins: [MediaProvider],
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
@remove-from-playlist="$emit('remove-from-playlist', $event)"
|
||||
@select="selectedResult = $event"
|
||||
v-else />
|
||||
|
||||
<SortButton :value="sort" @input="sort = $event" v-if="sortedMovies.length > 0" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -26,6 +28,7 @@ import Loading from "@/components/Loading";
|
|||
import MediaProvider from "@/components/panels/Media/Providers/Mixin";
|
||||
import NoItems from "@/components/elements/NoItems";
|
||||
import Results from "@/components/panels/Media/Results";
|
||||
import SortButton from "@/components/panels/Media/Providers/Jellyfin/components/SortButton";
|
||||
|
||||
export default {
|
||||
mixins: [MediaProvider],
|
||||
|
@ -33,6 +36,7 @@ export default {
|
|||
Loading,
|
||||
NoItems,
|
||||
Results,
|
||||
SortButton,
|
||||
},
|
||||
|
||||
emits: [
|
||||
|
@ -52,9 +56,13 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
movies: {},
|
||||
movies: [],
|
||||
loading_: false,
|
||||
selectedResult: null,
|
||||
sort: {
|
||||
attr: 'title',
|
||||
desc: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -64,8 +72,25 @@ export default {
|
|||
},
|
||||
|
||||
sortedMovies() {
|
||||
if (!this.movies) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [...this.movies].sort((a, b) => {
|
||||
return a.title.localeCompare(b.title)
|
||||
const attr = this.sort.attr
|
||||
const desc = this.sort.desc
|
||||
let aVal = a[attr]
|
||||
let bVal = b[attr]
|
||||
|
||||
if (typeof aVal === 'number' || typeof bVal === 'number') {
|
||||
aVal = aVal || 0
|
||||
bVal = bVal || 0
|
||||
return desc ? bVal - aVal : aVal - bVal
|
||||
}
|
||||
|
||||
aVal = (aVal || '').toString().toLowerCase()
|
||||
bVal = (bVal || '').toString().toLowerCase()
|
||||
return desc ? bVal.localeCompare(aVal) : aVal.localeCompare(bVal)
|
||||
}).map((movie) => {
|
||||
return {
|
||||
item_type: movie.type,
|
||||
|
@ -113,4 +138,8 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/components/panels/Media/Providers/Jellyfin/common.scss";
|
||||
|
||||
.index {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div class="sort-buttons">
|
||||
<Dropdown :icon-class="btnIconClass"
|
||||
glow right
|
||||
title="Sort Direction">
|
||||
<div class="sort-buttons-dropdown-body">
|
||||
<div class="title">Sort Direction</div>
|
||||
<DropdownItem text="Ascending"
|
||||
icon-class="fa fa-arrow-up-short-wide"
|
||||
:item-class="{ active: !value?.desc }"
|
||||
@input="onDescChange(false)" />
|
||||
<DropdownItem text="Descending"
|
||||
icon-class="fa fa-arrow-down-wide-short"
|
||||
:item-class="{ active: value?.desc }"
|
||||
@input="onDescChange(true)" />
|
||||
|
||||
<div class="title">Sort By</div>
|
||||
<DropdownItem text="Name"
|
||||
icon-class="fa fa-font"
|
||||
:item-class="{ active: value?.attr === 'title' }"
|
||||
@input="onAttrChange('title')" />
|
||||
<DropdownItem text="Release Date"
|
||||
icon-class="fa fa-calendar"
|
||||
:item-class="{ active: value?.attr === 'year' }"
|
||||
@input="onAttrChange('year')" />
|
||||
<DropdownItem text="Critics Rating"
|
||||
icon-class="fa fa-star"
|
||||
:item-class="{ active: value?.attr === 'critic_rating' }"
|
||||
@input="onAttrChange('critic_rating')" />
|
||||
<DropdownItem text="Community Rating"
|
||||
icon-class="fa fa-users"
|
||||
:item-class="{ active: value?.attr === 'community_rating' }"
|
||||
@input="onAttrChange('community_rating')" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "@/components/elements/FloatingDropdownButton";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Utils from '@/Utils'
|
||||
|
||||
export default {
|
||||
emits: ['input'],
|
||||
mixins: [Utils],
|
||||
components: {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
btnIconClass() {
|
||||
return this.value?.desc ? 'fa fa-arrow-down-wide-short' : 'fa fa-arrow-up-short-wide'
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onAttrChange(attr) {
|
||||
this.$emit('input', { attr, desc: !!this.value?.desc })
|
||||
},
|
||||
|
||||
onDescChange(desc) {
|
||||
this.$emit('input', { attr: this.value?.attr, desc })
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
value() {
|
||||
this.setUrlArgs({
|
||||
sort: this.value?.attr,
|
||||
desc: this.value?.desc,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const urlArgs = this.getUrlArgs()
|
||||
const sortBy = urlArgs.sort
|
||||
const desc = urlArgs.desc?.toString() === 'true'
|
||||
|
||||
if (sortBy || desc) {
|
||||
this.$emit('input', { attr: sortBy, desc })
|
||||
}
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
this.setUrlArgs({
|
||||
sort: null,
|
||||
desc: null,
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.sort-buttons {
|
||||
.floating-btn {
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-buttons-dropdown-body {
|
||||
.title {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
box-shadow: $border-shadow-bottom;
|
||||
border-top: $default-border-2;
|
||||
}
|
||||
|
||||
.item {
|
||||
&.active {
|
||||
color: $selected-fg;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue