Big layout improvements, started on miniplayer

This commit is contained in:
Eclypsed
2024-01-16 02:45:27 -05:00
parent fd08867628
commit a6c65ce0cf
33 changed files with 351 additions and 176 deletions

View File

@@ -0,0 +1,6 @@
/** @type {import('./$types').LayoutLoad} */
export const load = ({ url }) => {
return {
url: url.pathname,
}
}

View File

@@ -0,0 +1,111 @@
<script>
import Navbar from '$lib/components/utility/navbar.svelte'
import Footer from '$lib/components/utility/footer.svelte'
import MiniPlayer from '$lib/components/media/miniPlayer.svelte'
import { fly, fade } from 'svelte/transition'
import { pageWidth } from '$lib/utils/stores.js'
export let data
const contentTabs = {
'/': {
header: 'Home',
icon: 'fa-solid fa-house',
},
'/artist': {
header: 'Artists',
icon: 'fa-solid fa-guitar',
},
'/playlist': {
header: 'Playlists',
icon: 'fa-solid fa-bars-staggered',
},
'/library': {
header: 'Libray',
icon: 'fa-solid fa-book-open',
},
}
let previousPage = data.url
let direction = 1
$: calculateDirection(data.url)
const calculateDirection = (newPage) => {
const contentLinks = Object.keys(contentTabs)
const newPageIndex = contentLinks.indexOf(newPage)
const previousPageIndex = contentLinks.indexOf(previousPage)
if (newPageIndex > previousPageIndex) {
direction = 1
} else {
direction = -1
}
previousPage = data.url
}
let activeTab, indicatorBar, tabList
$: calculateBar(activeTab)
const calculateBar = (activeTab) => {
if (activeTab) {
const listRect = tabList.getBoundingClientRect()
const tabRec = activeTab.getBoundingClientRect()
indicatorBar.style.top = `${listRect.height}px`
if (direction === 1) {
indicatorBar.style.right = `${listRect.right - tabRec.right}px`
setTimeout(() => (indicatorBar.style.left = `${tabRec.left - listRect.left}px`), 300)
} else {
indicatorBar.style.left = `${tabRec.left - listRect.left}px`
setTimeout(() => (indicatorBar.style.right = `${listRect.right - tabRec.right}px`), 300)
}
}
}
</script>
<div class="flex h-full flex-col">
{#if $pageWidth > 768}
<Navbar>
<h1 slot="center-content" bind:this={tabList} class="relative flex items-center gap-12 text-lg">
{#each Object.entries(contentTabs) as [page, tabData]}
{#if data.url === page}
<span bind:this={activeTab} class="pointer-events-none">{tabData.header}</span>
{:else}
<a class="text-neutral-400 hover:text-lazuli-primary" href={page}>{tabData.header}</a>
{/if}
{/each}
{#if data.url in contentTabs}
<div bind:this={indicatorBar} transition:fade class="absolute h-0.5 bg-lazuli-primary transition-all duration-300 ease-in-out" />
{/if}
</h1>
</Navbar>
{:else}
<Navbar />
{/if}
<div class="relative flex-1 overflow-hidden">
{#key previousPage}
<div in:fly={{ x: 200 * direction, duration: 300, delay: 300 }} out:fly={{ x: -200 * direction, duration: 300 }} class="no-scrollbar h-full overflow-y-scroll px-8 py-4 md:px-32">
<slot />
</div>
{/key}
<div class="absolute bottom-0 w-full">
<MiniPlayer displayMode={$pageWidth > 768 ? 'horizontal' : 'vertical'} />
</div>
</div>
{#if $pageWidth < 768}
<Footer>
<section slot="content" class="flex items-center justify-center" style="height: 32px;">
<h1 bind:this={tabList} class="relative flex w-full items-center justify-around">
{#each Object.entries(contentTabs) as [page, tabData]}
{#if data.url === page}
<span bind:this={activeTab} class="pointer-events-none"><i class={tabData.icon} /></span>
{:else}
<a class="text-neutral-400 hover:text-lazuli-primary" href={page}><i class={tabData.icon} /></a>
{/if}
{/each}
{#if data.url in contentTabs}
<div bind:this={indicatorBar} transition:fade class="absolute h-0.5 bg-lazuli-primary transition-all duration-300 ease-in-out" />
{/if}
</h1>
</section>
</Footer>
{/if}
</div>

View File

@@ -17,7 +17,7 @@
}
</script>
<main class="mx-auto grid h-full max-w-screen-xl gap-8 p-8 pt-24">
<main class="mx-auto grid h-full max-w-screen-xl gap-8 p-8">
<nav class="h-full rounded-lg p-6">
<h1 class="flex h-6 justify-between text-neutral-400">
<span>

View File

@@ -1,8 +0,0 @@
export const trailingSlash = 'never'
/** @type {import('./$types').PageServerLoad} */
export const load = ({ url }) => {
return {
url: url.pathname,
}
}

View File

@@ -2,13 +2,10 @@
import '../app.css'
import '@fortawesome/fontawesome-free/css/all.min.css'
import AlertBox from '$lib/components/utility/alertBox.svelte'
import SubLayouts from './subLayouts.svelte'
import { newestAlert, backgroundImage } from '$lib/utils/stores.js'
import { newestAlert, backgroundImage, pageWidth } from '$lib/utils/stores.js'
import { fade } from 'svelte/transition'
import { onMount } from 'svelte'
export let data
let alertBox
$: addAlert($newestAlert)
@@ -22,6 +19,7 @@
onMount(() => (loaded = true))
</script>
<svelte:window bind:innerWidth={$pageWidth} />
<div class="no-scrollbar h-screen font-notoSans text-white">
<div class="fixed isolate -z-10 h-full w-screen bg-black">
<!-- This whole bg is a complete copy of ytmusic, design own at some point (Place for customization w/ album art etc?) (EDIT: Ok, it looks SICK with album art!) -->
@@ -33,9 +31,7 @@
{/key}
{/if}
</div>
<SubLayouts currentPage={data.url}>
<slot slot="innerContent" />
</SubLayouts>
<slot />
<AlertBox bind:this={alertBox} />
</div>

View File

@@ -1,36 +0,0 @@
{
"connectionId": "Id of the connection that provides the song [ALL REQ]",
"serviceType": "The type of service that provides the song [ALL REQ]",
"mediaType": "song || album || playlist || artist [ALL REQ]",
"name": "Name of media [ALL OPT]",
"id": "whatever unique identifier the service provides [ALL REQ]",
"duration": "length of song in milliseconds [song OPT, album OPT, playlist OPT]",
"artists [song OPT]": [
{
"name": "Name of artist",
"id": "service's unique identifier for the artist"
}
],
"album [song OPT]": {
"name": "Name of album",
"id": "service's unique identifier for the album",
"artists": [
{
"name": "Name of artist",
"id": "service's unique identifier for the artist"
}
],
"image": "source url of the album art"
},
"image": "source url of image unique to the song, if one does not exist this will be the album art or in the case of videos the thumbnail [ALL OPT]",
"audio": "source url of the audio stream [song REQ]",
"video": "source url of the video stream (if this is not null then player will allow for video mode, otherwise use image) [song OPT]",
"releaseDate": "Either the date the MV was upload or the release date/year of the album [song OPT, album OPT]",
"All of the above data": "Is comprised solely of data that has been read from the respective service and processed by a factory",
"the above data should not contain or rely on any information that has to be read from the lazuli server": "save for the data that was read to fetch it in the first place (connectionId, serviceType, etc.)",
"data that requires something else to be read from the lazuli server, such as presence in a playlist or marked as favorite": "should not be implemented into any visual design and is to be fetched as needed",
"When it comes to determining whether or not a song should be displayed as a video": "This is determined solely by the presence of a video source url, which will automatically be null for all sources besides youtube obviously",
"If a song does have a video source, it will be dispayed as a music video by default": "The presence of this property will likely determine certain styling, as well as the presence of the song/video switcher in the player"
}

View File

@@ -1,72 +0,0 @@
<script>
export let currentPage
import Navbar from '$lib/components/utility/navbar.svelte'
import { fly } from 'svelte/transition'
const contentTabs = {
Home: '/',
Artists: '/artist',
Playlists: '/playlist',
Libray: '/library',
}
let previousPage = currentPage
let direction = 1
$: calculateDirection(currentPage)
const calculateDirection = (newPage) => {
const contentLinks = Object.values(contentTabs)
const newPageIndex = contentLinks.indexOf(newPage)
const previousPageIndex = contentLinks.indexOf(previousPage)
if (newPageIndex > previousPageIndex) {
direction = 1
} else {
direction = -1
}
previousPage = currentPage
}
let activeTab, indicatorBar, tabList
$: calculateBar(activeTab)
const calculateBar = (activeTab) => {
if (activeTab) {
const listRect = tabList.getBoundingClientRect()
const tabRec = activeTab.getBoundingClientRect()
indicatorBar.style.top = `${listRect.height}px`
if (direction === 1) {
indicatorBar.style.right = `${listRect.right - tabRec.right}px`
setTimeout(() => (indicatorBar.style.left = `${tabRec.left - listRect.left}px`), 350)
} else {
indicatorBar.style.left = `${tabRec.left - listRect.left}px`
setTimeout(() => (indicatorBar.style.right = `${listRect.right - tabRec.right}px`), 350)
}
}
}
</script>
{#if Object.values(contentTabs).includes(currentPage)}
<Navbar />
<div class="flex justify-center py-4">
<h1 bind:this={tabList} class="relative flex justify-center gap-12 text-lg">
{#each Object.entries(contentTabs) as [header, page]}
{#if currentPage === page}
<span bind:this={activeTab} class="pointer-events-none">{header}</span>
{:else}
<a class="text-neutral-400 hover:text-lazuli-primary" href={page}>{header}</a>
{/if}
{/each}
<div bind:this={indicatorBar} class="absolute h-0.5 bg-lazuli-primary transition-all duration-300 ease-in-out" />
</h1>
</div>
<div class="overflow-x-hidden px-8 sm:px-32">
{#key previousPage}
<div in:fly={{ x: 200 * direction, duration: 300, delay: 300 }} out:fly={{ x: -200 * direction, duration: 300 }}>
<slot name="innerContent" />
</div>
{/key}
</div>
{:else}
<slot name="innerContent" />
{/if}

View File

@@ -1,5 +0,0 @@
export const prerender = false
export const ssr = false
/** @type {import('./$types').PageLoad} */
export async function load() {}

View File

@@ -1,16 +0,0 @@
<script>
</script>
<section class="grid h-full place-items-center">
<div class="aspect-video h-2/3 bg-slate-900">
<video id="video-player" controls></video>
<!-- <iframe
src="https://www.youtube.com/embed/uhx-u5peyeY?controls=0&autoplay=0&showinfo=0&rel=0"
title="Video"
frameBorder="0"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
class="h-full w-full"
/> -->
</div>
</section>