mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
convert songlist table to grid
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="album-h" @contextmenu="hideShowContext">
|
<div class="album-h" @contextmenu="hideShowContext" id="album-h">
|
||||||
<ContextMenu />
|
<!-- <ContextMenu /> -->
|
||||||
<div class="a-header">
|
<div class="a-header">
|
||||||
<div
|
<div
|
||||||
class="image art shadow-lg"
|
class="image art shadow-lg"
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import state from "@/composables/state.js";
|
import state from "@/composables/state.js";
|
||||||
import perks from "@/composables/perks.js";
|
import perks from "@/composables/perks.js";
|
||||||
import ContextMenu from "../contextMenu.vue";
|
// import ContextMenu from "../contextMenu.vue";
|
||||||
import { reactive, ref } from "vue";
|
// import { reactive, ref } from "vue";
|
||||||
import useContextStore from "@/stores/context.js";
|
// import useContextStore from "@/stores/context.js";
|
||||||
|
|
||||||
const contextStore = useContextStore();
|
// const contextStore = useContextStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
album_info: {
|
album_info: {
|
||||||
@@ -48,14 +48,14 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const hideShowContext = (e) => {
|
// const hideShowContext = (e) => {
|
||||||
e.preventDefault();
|
// e.preventDefault();
|
||||||
e.stopPropagation();
|
// e.stopPropagation();
|
||||||
|
|
||||||
contextStore.showContextMenu(e);
|
// contextStore.showContextMenu(e);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const context_hide = ref(true);
|
// const context_hide = ref(true);
|
||||||
|
|
||||||
function playAlbum() {
|
function playAlbum() {
|
||||||
perks.updateQueue(state.album.tracklist[0], "album");
|
perks.updateQueue(state.album.tracklist[0], "album");
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="folder">
|
<div class="folder">
|
||||||
<div class="table rounded" v-if="props.songs.length">
|
<div class="table rounded" v-if="props.songs.length">
|
||||||
<table>
|
<div class="thead">
|
||||||
<thead>
|
<div class="index"></div>
|
||||||
<tr>
|
<div class="track-header">Track</div>
|
||||||
<th class="index"></th>
|
<div class="artists-header">Artist</div>
|
||||||
<th class="track-header">Track</th>
|
<div class="album-header">Album</div>
|
||||||
<th class="artists-header">Artist</th>
|
<div class="duration-header">Duration</div>
|
||||||
<th class="album-header">Album</th>
|
</div>
|
||||||
<th class="duration-header">Duration</th>
|
<div>
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<SongItem
|
<SongItem
|
||||||
v-for="(song, index) in props.songs"
|
v-for="(song, index) in props.songs"
|
||||||
:key="song"
|
:key="song"
|
||||||
@@ -20,8 +17,7 @@
|
|||||||
@updateQueue="updateQueue"
|
@updateQueue="updateQueue"
|
||||||
@loadAlbum="loadAlbum"
|
@loadAlbum="loadAlbum"
|
||||||
/>
|
/>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="props.songs.length === 0 && search_query">
|
<div v-else-if="props.songs.length === 0 && search_query">
|
||||||
<div class="no-results">
|
<div class="no-results">
|
||||||
@@ -92,6 +88,42 @@ function loadAlbum(title, albumartist) {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.thead {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr 1.5fr 0.25fr;
|
||||||
|
height: 2.5rem;
|
||||||
|
align-items: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $gray1;
|
||||||
|
gap: $small;
|
||||||
|
|
||||||
|
@include tablet-landscape {
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr 1.5fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include tablet-portrait {
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include phone-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-header {
|
||||||
|
@include tablet-landscape {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-header {
|
||||||
|
@include tablet-portrait {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -105,49 +137,6 @@ function loadAlbum(title, albumartist) {
|
|||||||
color: rgb(255, 255, 255);
|
color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
text-transform: capitalize;
|
|
||||||
width: 100%;
|
|
||||||
table-layout: fixed;
|
|
||||||
|
|
||||||
@include phone-only {
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 0 $small;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
height: 2rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
|
|
||||||
@include phone-only {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
padding-left: $small;
|
|
||||||
}
|
|
||||||
|
|
||||||
th.duration-header {
|
|
||||||
@include tablet-landscape {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
width: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
th.album-header {
|
|
||||||
@include tablet-portrait {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th.index {
|
|
||||||
width: 2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ function removeLastFilter() {
|
|||||||
padding: 0 $small;
|
padding: 0 $small;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@include tablet-landscape {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.input-loader {
|
.input-loader {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- v-show="context.visible" -->
|
||||||
<div
|
<div
|
||||||
class="context-menu rounded"
|
class="context-menu rounded"
|
||||||
:class="{ 'context-menu-visible': context.visible }"
|
:class="{ 'context-menu-visible': context.visible }"
|
||||||
v-show="context.visible"
|
|
||||||
:style="{
|
:style="{
|
||||||
left: context.x + 'px',
|
left: context.x + 'px',
|
||||||
top: context.y + 'px',
|
top: context.y + 'px',
|
||||||
@@ -84,6 +84,10 @@ const options = [
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
// transform: scale(0);
|
||||||
|
transform-origin: top left;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
|
||||||
.context-item {
|
.context-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -116,7 +120,8 @@ const options = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .visible {
|
.context-menu-visible {
|
||||||
// display: unset;
|
transform: scale(1);
|
||||||
// }
|
transition: transform .2s ease-in-out;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<tr
|
<div
|
||||||
class="songlist-item"
|
class="songlist-item rounded"
|
||||||
:class="{ current: current.trackid === song.trackid }"
|
:class="{ current: current.trackid === song.trackid }"
|
||||||
@dblclick="emitUpdate(song)"
|
@dblclick="emitUpdate(song)"
|
||||||
>
|
>
|
||||||
<td class="index">{{ index }}</td>
|
<div class="index">{{ index }}</div>
|
||||||
<td class="flex">
|
<div class="flex">
|
||||||
<div
|
<div
|
||||||
class="album-art image rounded"
|
class="album-art image rounded"
|
||||||
:style="{ backgroundImage: `url("${song.image}"` }"
|
:style="{ backgroundImage: `url("${song.image}"` }"
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class="song-artists">
|
<div class="song-artists">
|
||||||
<div class="ellip" v-if="song.artists[0] !== ''">
|
<div class="ellip" v-if="song.artists[0] !== ''">
|
||||||
<span
|
<span
|
||||||
class="artist"
|
class="artist"
|
||||||
@@ -38,22 +38,24 @@
|
|||||||
<div class="ellip" v-else>
|
<div class="ellip" v-else>
|
||||||
<span class="artist">{{ song.albumartist }}</span>
|
<span class="artist">{{ song.albumartist }}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class="song-album">
|
<div class="song-album">
|
||||||
<div
|
<div
|
||||||
class="album ellip"
|
class="album ellip"
|
||||||
@click="emitLoadAlbum(song.album, song.albumartist)"
|
@click="emitLoadAlbum(song.album, song.albumartist)"
|
||||||
>
|
>
|
||||||
{{ song.album }}
|
{{ song.album }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class="song-duration">{{ formatSeconds(song.length) }}</td>
|
<div class="song-duration">{{ formatSeconds(song.length) }}</div>
|
||||||
</tr>
|
<ContextMenu />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import perks from "@/composables/perks.js";
|
import perks from "@/composables/perks.js";
|
||||||
import state from "@/composables/state.js";
|
import state from "@/composables/state.js";
|
||||||
|
import ContextMenu from "../contextMenu.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ["song", "index"],
|
props: ["song", "index"],
|
||||||
@@ -62,11 +64,9 @@ export default {
|
|||||||
function emitUpdate(song) {
|
function emitUpdate(song) {
|
||||||
emit("updateQueue", song);
|
emit("updateQueue", song);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitLoadAlbum(title, artist) {
|
function emitLoadAlbum(title, artist) {
|
||||||
emit("loadAlbum", title, artist);
|
emit("loadAlbum", title, artist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
putCommas: perks.putCommas,
|
putCommas: perks.putCommas,
|
||||||
emitUpdate,
|
emitUpdate,
|
||||||
@@ -76,11 +76,58 @@ export default {
|
|||||||
formatSeconds: perks.formatSeconds,
|
formatSeconds: perks.formatSeconds,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
components: { ContextMenu },
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.songlist-item {
|
.songlist-item {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr 1.5fr 0.25fr;
|
||||||
|
height: 3.75rem;
|
||||||
|
text-align: left;
|
||||||
|
gap: $small;
|
||||||
|
|
||||||
|
@include tablet-landscape {
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr 1.5fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include tablet-portrait {
|
||||||
|
grid-template-columns: 1.5rem 1.5fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.song-duration {
|
||||||
|
@include tablet-landscape {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.song-album {
|
||||||
|
.album {
|
||||||
|
cursor: pointer;
|
||||||
|
max-width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include tablet-portrait {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.song-artists {
|
||||||
|
.artist {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include phone-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
color: grey;
|
color: grey;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@@ -92,17 +139,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include phone-only {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
td {
|
|
||||||
background-color: #14161a;
|
|
||||||
border-radius: $small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-duration {
|
.song-duration {
|
||||||
font-size: 0.8rem;
|
font-size: 0.9rem;
|
||||||
width: 5rem !important;
|
width: 5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,75 +192,5 @@ export default {
|
|||||||
border-radius: $small;
|
border-radius: $small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
* {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
& {
|
|
||||||
& td {
|
|
||||||
background-color: #3131313b;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:first-child {
|
|
||||||
border-radius: $small 0 0 $small;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-child(2) {
|
|
||||||
border-radius: 0;
|
|
||||||
|
|
||||||
@include phone-only {
|
|
||||||
border-radius: $small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-child(3) {
|
|
||||||
@include tablet-portrait {
|
|
||||||
border-radius: 0 $small $small 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include tablet-landscape {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > td:nth-child(4) {
|
|
||||||
@include tablet-landscape {
|
|
||||||
border-radius: 0 $small $small 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& td:last-child {
|
|
||||||
border-radius: 0 $small $small 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-duration {
|
|
||||||
@include tablet-landscape {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-album {
|
|
||||||
.album {
|
|
||||||
cursor: pointer;
|
|
||||||
max-width: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include tablet-portrait {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-artists {
|
|
||||||
.artist {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phone-only {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,34 +1,37 @@
|
|||||||
import perks from "./perks";
|
import perks from "./perks";
|
||||||
|
|
||||||
export default function normalizeContextMenu(x, y) {
|
export default (mouseX, mouseY) => {
|
||||||
const app_dom = perks.getElem("app", "id");
|
const scope = perks.getElem("app", "id");
|
||||||
const context_menu = perks.getElem("context-menu-visible", "class");
|
const contextMenu = perks.getElem("context-menu", "class");
|
||||||
|
// ? compute what is the mouse position relative to the container element (scope)
|
||||||
|
let { left: scopeOffsetX, top: scopeOffsetY } = scope.getBoundingClientRect();
|
||||||
|
|
||||||
const { left: scopeOffsetX, top: scopeOffsetY } =
|
scopeOffsetX = scopeOffsetX < 0 ? 0 : scopeOffsetX;
|
||||||
app_dom.getBoundingClientRect();
|
scopeOffsetY = scopeOffsetY < 0 ? 0 : scopeOffsetY;
|
||||||
|
|
||||||
const scopeX = x - scopeOffsetX;
|
const scopeX = mouseX - scopeOffsetX;
|
||||||
const scopeY = y - scopeOffsetY;
|
const scopeY = mouseY - scopeOffsetY;
|
||||||
|
|
||||||
const outOfBoundsX = scopeX + context_menu.clientHeight > app_dom.clientWidth;
|
// ? check if the element will go out of bounds
|
||||||
const outOfBoundsY =
|
const outOfBoundsOnX = scopeX + contextMenu.clientWidth > scope.clientWidth;
|
||||||
scopeY + context_menu.clientHeight > app_dom.clientHeight;
|
|
||||||
|
|
||||||
let normalizedX = x;
|
const outOfBoundsOnY = scopeY + contextMenu.clientHeight > scope.clientHeight;
|
||||||
let normalizedY = y;
|
|
||||||
|
|
||||||
if (outOfBoundsX) {
|
let normalizedX = mouseX;
|
||||||
normalizedX =
|
let normalizedY = mouseY;
|
||||||
scopeOffsetX + app_dom.clientWidth - context_menu.clientHeight;
|
|
||||||
|
// console.log(mouseX, mouseY);
|
||||||
|
// ? normalize on X
|
||||||
|
if (outOfBoundsOnX) {
|
||||||
|
normalizedX = scopeOffsetX + scope.clientWidth - contextMenu.clientWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outOfBoundsY) {
|
// ? normalize on Y
|
||||||
normalizedY =
|
if (outOfBoundsOnY) {
|
||||||
scopeOffsetY + app_dom.clientHeight - context_menu.clientHeight;
|
normalizedY = scopeOffsetY + scope.clientHeight - contextMenu.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
console.log(normalizedX, normalizedY);
|
||||||
normalizedX,
|
|
||||||
normalizedY,
|
return { normalizedX, normalizedY };
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import normalizeContextMenu from "../composables/normalizeContextMenu";
|
import normalize from "../composables/normalizeContextMenu";
|
||||||
|
|
||||||
export default defineStore("context-menu", {
|
export default defineStore("context-menu", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -10,9 +10,10 @@ export default defineStore("context-menu", {
|
|||||||
actions: {
|
actions: {
|
||||||
showContextMenu(e) {
|
showContextMenu(e) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
const { normalX, normalY } = normalizeContextMenu(e.clientX, e.clientY);
|
const yo = normalize(e.clientX, e.clientY);
|
||||||
this.x = normalX;
|
this.x = yo.normalizedX;
|
||||||
this.y = normalY;
|
this.y = yo.normalizedY;
|
||||||
|
console.log(yo);
|
||||||
},
|
},
|
||||||
hideContextMenu() {
|
hideContextMenu() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user