convert songlist table to grid

This commit is contained in:
geoffrey45
2022-03-13 08:58:28 +03:00
parent 658e7cdbb7
commit 092d967f49
7 changed files with 164 additions and 194 deletions
+12 -12
View File
@@ -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");
+45 -56
View File
@@ -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;
+9 -4
View File
@@ -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>
+61 -93
View File
@@ -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(&quot;${song.image}&quot;` }" :style="{ backgroundImage: `url(&quot;${song.image}&quot;` }"
@@ -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>
+26 -23
View File
@@ -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 };
}; };
}
+5 -4
View File
@@ -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;