Skip to content

Commit

Permalink
Merge branch 'practice-layout'
Browse files Browse the repository at this point in the history
  • Loading branch information
threedalpeng committed Feb 21, 2024
2 parents e4cc416 + 05dd986 commit aadf678
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 1 deletion.
90 changes: 90 additions & 0 deletions src/routes/practice/(practice)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<script lang="ts">
import { clickoutside } from '$/utils/hooks/click-outside';
import { base } from '$app/paths';
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from '@steeze-ui/heroicons';
import { Icon } from '@steeze-ui/svelte-icon';
import type { LayoutData } from './$types';
export let data: LayoutData;
let practiceListOpen: boolean = false;
</script>

<div class="flex h-screen w-screen flex-col">
<header class="flex h-[60px] w-screen flex-row items-center">
<span class="ml-8 font-jazz text-2xl font-bold"> JazzyDalpeng / Jazz Guitar Practice</span>
</header>
<div class="flex basis-full flex-col items-center justify-center">
<slot />
</div>
<nav class="relative h-[60px] w-screen">
<div class="flex h-full flex-row items-center justify-between">
{#if data.pages.previous}
<a
href={`${base}/practice/${data.category}/${data.pages.previous.slug}`}
class="mr-8 flex h-full cursor-pointer select-none flex-row items-center gap-4 text-indigo-400 transition duration-200 hover:text-indigo-600 active:text-indigo-800"
>
<Icon class="h-[30px] w-auto" src={ChevronLeft} theme="solid" />
<span
class="invisible whitespace-nowrap text-black hover:text-indigo-600 active:text-indigo-800 lg:visible"
>{data.pages.previous.title}</span
>
</a>
{:else}
<div
class="mr-8 flex h-full select-none flex-row items-center gap-4 text-gray-300 transition duration-200"
>
<Icon class="h-[30px] w-auto" src={ChevronLeft} theme="solid" />
</div>
{/if}
{#if data.pages.next}
<a
href={`${base}/practice/${data.category}/${data.pages.next.slug}`}
class="ml-8 flex h-full cursor-pointer select-none flex-row items-center gap-4 text-indigo-400 transition duration-200 hover:text-indigo-600 active:text-indigo-800"
>
<span
class="invisible whitespace-nowrap text-black hover:text-indigo-600 active:text-indigo-800 lg:visible"
>{data.pages.next.title}</span
>
<Icon class="h-[30px] w-auto" src={ChevronRight} theme="solid" />
</a>
{:else}
<div
class="ml-8 flex h-full select-none flex-row items-center gap-4 text-gray-300 transition duration-200"
>
<Icon class="h-[30px] w-auto" src={ChevronRight} theme="solid" />
</div>
{/if}
</div>
<button
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
on:click={() => (practiceListOpen = !practiceListOpen)}
>
<div
use:clickoutside
on:clickoutside={() => (practiceListOpen = false)}
class="absolute -top-[30px] left-[50%] h-[40px] w-auto -translate-x-[50%]"
>
<Icon
class="opacity-25% hover:opacity-100% h-[20px] w-auto cursor-pointer transition-opacity"
src={practiceListOpen ? ChevronDown : ChevronUp}
theme="mini"
/>
{#if practiceListOpen}
<div
class="absolute bottom-full left-1/2 max-h-80 w-80 -translate-x-1/2 -translate-y-[1rem] rounded bg-gray-500 p-4"
>
{#each data.routes[data.category] as route}
<div class="text-start">
<a href={`${base}/practice/${data.category}/${route.slug}`}>
{route.title}
</a>
</div>
{/each}
</div>
{/if}
</div>
<p class="whitespace-nowrap">{data.pages.current.title}</p>
</button>
</nav>
</div>
33 changes: 33 additions & 0 deletions src/routes/practice/(practice)/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { base } from '$app/paths';
import { redirect } from '@sveltejs/kit';
import { routes, type PracticeRoute, type PracticeRouteCategory } from '../data';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = (data) => {
const { category, slug } = data.params;

if (category !== 'core' && category !== 'custom') {
redirect(303, `${base}/practice`);
}

const currentCategoryRoutes = routes[category];
const currentPageIndex = currentCategoryRoutes.findIndex((route) => route.slug === slug);
if (currentPageIndex === -1) {
redirect(303, `${base}/practice/${category}/${routes[category][0].slug}`);
}

const pages: {
previous?: PracticeRoute;
current: PracticeRoute;
next?: PracticeRoute;
} = {
previous: currentPageIndex - 1 >= 0 ? currentCategoryRoutes[currentPageIndex - 1] : undefined,
current: currentCategoryRoutes[currentPageIndex],
next:
currentPageIndex < currentCategoryRoutes.length - 1
? currentCategoryRoutes[currentPageIndex + 1]
: undefined
};

return { routes, category: category as PracticeRouteCategory, pages };
};
1 change: 1 addition & 0 deletions src/routes/practice/(practice)/[category]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
category
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
slug
6 changes: 5 additions & 1 deletion src/routes/practice/+page.svelte
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
hi
<script>
import { base } from '$app/paths';
</script>

<a href={`${base}/practice`}>????</a>
212 changes: 212 additions & 0 deletions src/routes/practice/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import type { Practice } from '$/lib/practice/types';
import { TUNE } from '$/utils/music/pitch';

export interface PracticeRoute {
title: string;
slug: string;
practice: Practice;
}

export type PracticeRouteCategory = 'core' | 'custom';
export interface PracticeRoutes extends Record<PracticeRouteCategory, PracticeRoute[]> {}

export const routes: PracticeRoutes = {
core: [
{
title: 'Major Scale',
slug: 'major-scale',
practice: {
tempo: {
bpm: 120,
beatPerBar: 4,
signatureUnit: 4
},
guitar: {
tuning: TUNE.standard
},
scores: [
{
positions: [
{ line: 6, fret: 8 },
{ line: 6, fret: 10 },
{ line: 5, fret: 7 },
{ line: 5, fret: 8 },
{ line: 5, fret: 10 },
{ line: 4, fret: 7 },
{ line: 4, fret: 9 },
{ line: 4, fret: 10 },
{ line: 3, fret: 7 },
{ line: 3, fret: 9 },
{ line: 3, fret: 10 },
{ line: 2, fret: 8 },
{ line: 2, fret: 10 },
{ line: 1, fret: 7 },
{ line: 1, fret: 8 },
{ line: 1, fret: 10 },
{ line: 6, fret: 7 }
],
notes: [
{ position: 0, time: { start: 0, duration: 0.0625 } },
{ position: 1, time: { start: 0.0625, duration: 0.0625 } },
{ position: 2, time: { start: 0.125, duration: 0.0625 } },
{ position: 3, time: { start: 0.1875, duration: 0.0625 } },
{ position: 4, time: { start: 0.25, duration: 0.0625 } },
{ position: 5, time: { start: 0.3125, duration: 0.0625 } },
{ position: 6, time: { start: 0.375, duration: 0.0625 } },
{ position: 7, time: { start: 0.4375, duration: 0.0625 } },
{ position: 8, time: { start: 0.5, duration: 0.0625 } },
{ position: 9, time: { start: 0.5625, duration: 0.0625 } },
{ position: 10, time: { start: 0.625, duration: 0.0625 } },
{ position: 11, time: { start: 0.6875, duration: 0.0625 } },
{ position: 12, time: { start: 0.75, duration: 0.0625 } },
{ position: 13, time: { start: 0.8125, duration: 0.0625 } },
{ position: 14, time: { start: 0.875, duration: 0.0625 } },
{ position: 15, time: { start: 0.9375, duration: 0.0625 } },
{ position: 14, time: { start: 1, duration: 0.0625 } },
{ position: 13, time: { start: 1.0625, duration: 0.0625 } },
{ position: 12, time: { start: 1.125, duration: 0.0625 } },
{ position: 11, time: { start: 1.1875, duration: 0.0625 } },
{ position: 10, time: { start: 1.25, duration: 0.0625 } },
{ position: 9, time: { start: 1.3125, duration: 0.0625 } },
{ position: 8, time: { start: 1.375, duration: 0.0625 } },
{ position: 7, time: { start: 1.4375, duration: 0.0625 } },
{ position: 6, time: { start: 1.5, duration: 0.0625 } },
{ position: 5, time: { start: 1.5625, duration: 0.0625 } },
{ position: 4, time: { start: 1.625, duration: 0.0625 } },
{ position: 3, time: { start: 1.6875, duration: 0.0625 } },
{ position: 2, time: { start: 1.75, duration: 0.0625 } },
{ position: 1, time: { start: 1.8125, duration: 0.0625 } },
{ position: 0, time: { start: 1.875, duration: 0.0625 } },
{ position: 16, time: { start: 1.9375, duration: 0.0625 } },
{ position: 0, time: { start: 2, duration: 0.0625 } }
],
boards: [
{
title: 'C line 1',
fingers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
time: { start: 0 }
},
{
title: 'C line 2',
fingers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
time: { start: 15 / 16 }
}
],
fretRange: {
start: 5,
end: 11,
visibility: 'all'
}
}
]
}
},
{
title: 'Rhythm Test',
slug: 'rhythm-test',
practice: {
tempo: {
bpm: 120,
beatPerBar: 4,
signatureUnit: 4
},
guitar: {
tuning: TUNE.standard
},
scores: [
{
positions: [
{ line: 5, fret: 3 },
{ line: 6, fret: 3 },
{ line: 1, fret: 'open' },
{ line: 2, fret: 1 },
{ line: 3, fret: 'open' }
],
notes: [
{ position: 0, time: { start: 0, duration: 1 / 4 } },
{ position: 2, time: { start: 1 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 1 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 1 / 4, duration: 1 / 4 } },
{ position: 1, time: { start: 2 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 3 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 3 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 3 / 4, duration: 1 / 4 } },
{ position: 0, time: { start: 4 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 5 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 5 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 5 / 4, duration: 1 / 4 } },
{ position: 1, time: { start: 6 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 7 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 7 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 7 / 4, duration: 1 / 4 } },
{ position: 0, time: { start: 8 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 9 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 9 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 9 / 4, duration: 1 / 4 } },
{ position: 1, time: { start: 10 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 11 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 11 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 11 / 4, duration: 1 / 4 } },
{ position: 0, time: { start: 12 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 13 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 13 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 13 / 4, duration: 1 / 4 } },
{ position: 1, time: { start: 14 / 4, duration: 1 / 4 } },
{ position: 2, time: { start: 15 / 4, duration: 1 / 4 } },
{ position: 3, time: { start: 15 / 4, duration: 1 / 4 } },
{ position: 4, time: { start: 15 / 4, duration: 1 / 4 } }
],
boards: [
{
title: 'C line 1',
fingers: [0, 2, 3, 4],
time: { start: 0 }
},
{
title: 'C line 1',
fingers: [1, 2, 3, 4],
time: { start: 1 / 2 }
},
{
title: 'C line 1',
fingers: [0, 2, 3, 4],
time: { start: 2 / 2 }
},
{
title: 'C line 1',
fingers: [1, 2, 3, 4],
time: { start: 3 / 2 }
},
{
title: 'C line 1',
fingers: [0, 2, 3, 4],
time: { start: 4 / 2 }
},
{
title: 'C line 1',
fingers: [1, 2, 3, 4],
time: { start: 5 / 2 }
},
{
title: 'C line 1',
fingers: [0, 2, 3, 4],
time: { start: 6 / 2 }
},
{
title: 'C line 1',
fingers: [1, 2, 3, 4],
time: { start: 7 / 2 }
}
],
fretRange: {
start: 0,
end: 12,
visibility: 'all'
}
}
]
}
}
],
custom: []
};
25 changes: 25 additions & 0 deletions src/utils/hooks/click-outside.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Action, ActionReturn } from 'svelte/action';

export const clickoutside: Action<
Element,
unknown,
{ 'on:clickoutside'?: (event: CustomEvent<MouseEvent>) => any }
> = (node) => {
const handleClick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (!event.target) {
return;
}
if (node && !node.contains(target) && !event.defaultPrevented) {
node.dispatchEvent(new CustomEvent('clickoutside', { detail: CustomEvent<MouseEvent> }));
}
};

document.addEventListener('click', handleClick, true);

return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
};
};

0 comments on commit aadf678

Please sign in to comment.