task 6: HTML export

This commit is contained in:
2024-05-08 14:45:20 +02:00
parent a93598d8f6
commit aee3052ad7
11 changed files with 483 additions and 9 deletions

3
res/template/album.css Normal file
View File

@ -0,0 +1,3 @@
td:first-child {
text-align: right;
}

39
res/template/album.html Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Slopify V2 - Listen to the music you hate</title>
<link rel="stylesheet" type="text/css" href="base.css">
<link rel="stylesheet" type="text/css" href="album.css">
</head>
<body>
<header id="navbar">
<a class="back" id="back-btn" href="{{artist.page}}"></a>
<h1 class="title">Slopify</h1>
</header>
<div id="body">
<h2>Album</h2>
<div id="crumbs">
<a href="{{artist.page}}">{{artist.name}}</a>
<span class="sep"></span>
<a href="./">{{album.name}}</a>
</div>
<table id="songs">
<thead>
<tr>
<th>No</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{{songs}}
</tbody>
</table>
</div>
<script type="text/javascript" src="base.js"></script>
<script>
initTable(document.getElementById("songs"))
</script>
</body>
</html>

0
res/template/artist.css Normal file
View File

36
res/template/artist.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Slopify V2 - Listen to the music you hate</title>
<link rel="stylesheet" type="text/css" href="base.css">
<link rel="stylesheet" type="text/css" href="artist.css">
</head>
<body>
<header id="navbar">
<a class="back" id="back-btn" href="index.html"></a>
<h1 class="title">Slopify</h1>
</header>
<div id="body">
<h2>Artist</h2>
<div id="crumbs">
<a href="./">{{artist.name}}</a>
</div>
<table id="albums">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{{albums}}
</tbody>
</table>
</div>
<script type="text/javascript" src="base.js"></script>
<script>
initTable(document.getElementById("albums"))
</script>
</body>
</html>

126
res/template/base.css Normal file
View File

@ -0,0 +1,126 @@
* {
margin: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
}
body {
background-color: #0d130d;
font-family: Ubuntu;
font-size: 16pt;
color: white;
}
#navbar {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-areas: "back title space";
place-items: center;
text-align: center;
border-bottom: solid #3e5b3e 1px;
padding: 0.4em 0.8em;
gap: 0.8em;
}
#navbar .back {
grid-area: back;
}
#back-btn {
width: 2em;
height: 2em;
position: relative;
}
#back-btn::before {
content: "";
border: solid white 4px;
border-style: solid none none solid;
position: absolute;
inset: 0;
transform: rotate(-45deg);
width: 0.5em;
height: 0.5em;
margin: auto;
}
#navbar .title {
grid-area: title;
}
#body {
display: flex;
flex-direction: column;
padding: 2em;
gap: 1em;
}
#crumbs {
display: flex;
gap: 1em;
align-items: center;
}
#crumbs .sep {
position: relative;
width: 0.5em;
height: 0.5em;
}
#crumbs .sep::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border: white 0.2em;
border-style: solid solid none none;
transform: translate(-50%, -50%) rotate(45deg);
}
a {
color: #00bf00;
}
table {
border-collapse: collapse;
}
th, td {
border: solid white 1px;
padding: 0.5em 1em;
}
tr:nth-child(even) {
background-color: #aeaeae21;
}
th {
cursor: pointer;
position: relative;
}
th.sort-asc::before {
content: "";
position: absolute;
top: 50%;
left: 0.5em;
transform: translate(-50%, -25%);
border: solid transparent 0.2em;
border-top-color: white;
}
th.sort-desc::before {
content: "";
position: absolute;
top: 50%;
left: 0.5em;
transform: translate(-50%, -55%);
border: solid transparent 0.2em;
border-bottom-color: white;
}

58
res/template/base.js Normal file
View File

@ -0,0 +1,58 @@
function initTable(table) {
table.tHead.querySelectorAll("th").forEach((header, i) => {
header.addEventListener("click", () => {
toggleSort(table, header, i)
})
})
}
function toggleSort(table, header, colI) {
if (header.classList.contains("sort-asc")) {
header.classList.remove("sort-asc")
header.classList.add("sort-desc")
doSort(table, colI, true)
} else if (header.classList.contains("sort-desc")) {
header.classList.remove("sort-desc")
header.classList.add("sort-asc")
doSort(table, colI, false)
} else {
table.tHead.querySelectorAll("th.sort-asc,th.sort-desc").forEach(th => {
th.classList.remove("sort-asc")
th.classList.remove("sort-desc")
})
header.classList.add("sort-asc")
doSort(table, colI, false)
}
}
function doSort(table, colI, desc = false) {
let swapped = true
while (swapped) {
let rows = Array.from(table.rows)
swapped = false
let rowA, rowB
for (let i=1; i<rows.length-1; i++) {
rowA = rows[i]
rowB = rows[i+1]
if (desc == compareRows(rowA, rowB, colI)) {
swapped = true
break
}
}
if (swapped) {
rowA.parentNode.insertBefore(rowB, rowA)
}
}
}
function compareRows(rowA, rowB, colI) {
let valA = rowA.querySelectorAll("td")[colI].innerText
let valB = rowB.querySelectorAll("td")[colI].innerText
if (!Number.isNaN(+valA) && !Number.isNaN(+valB)) {
valA = +valA
valB = +valB
}
return valB > valA
}

0
res/template/index.css Normal file
View File

33
res/template/index.html Normal file
View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Slopify V2 - Listen to the music you hate</title>
<link rel="stylesheet" type="text/css" href="base.css">
<link rel="stylesheet" type="text/css" href="index.css">
</head>
<body>
<header id="navbar">
<h1 class="title">Slopify</h1>
</header>
<div id="body">
<h2>Artists</h2>
<div id="crumbs"></div>
<table id="artists">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{{artists}}
</tbody>
</table>
</div>
<script type="text/javascript" src="base.js"></script>
<script>
initTable(document.getElementById("artists"))
</script>
</body>
</html>

61
res/template/song.css Normal file
View File

@ -0,0 +1,61 @@
@keyframes shadow-anim {
0% {
box-shadow: 0.0em 0.2em 0.6em #344c34a0;
}
25% {
box-shadow: 0.1em 0.3em 0.8em #344c34a0;
}
50% {
box-shadow: 0.2em 0.4em 1.2em #344c34a0;
}
75% {
box-shadow: 0.1em 0.3em 0.8em #344c34a0;
}
100% {
box-shadow: 0.0em 0.2em 0.6em #344c34a0;
}
}
#song {
display: grid;
animation: shadow-anim 10s infinite alternate;
border-radius: 0.8em;
padding: 0.8em;
grid-template-columns: auto 1fr auto;
grid-template-rows: auto 1fr;
grid-template-areas:
"num title duration"
"cover cover cover";
max-height: 20em;
min-width: 50%;
max-width: 30em;
margin: auto;
gap: 0.4em;
align-items: center;
}
#song .number {
grid-area: num;
}
#song .title {
grid-area: title;
}
#song .duration {
grid-area: duration;
}
#song .cover {
grid-area: cover;
object-fit: contain;
background-color: #393939;
width: 8em;
height: 8em;
}

33
res/template/song.html Normal file
View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Slopify V2 - Listen to the music you hate</title>
<link rel="stylesheet" type="text/css" href="base.css">
<link rel="stylesheet" type="text/css" href="song.css">
</head>
<body>
<header id="navbar">
<a class="back" id="back-btn" href="{{album.page}}"></a>
<h1 class="title">Slopify</h1>
</header>
<div id="body">
<h2>Song</h2>
<div id="crumbs">
<a href="{{artist.page}}">{{artist.name}}</a>
<span class="sep"></span>
<a href="{{album.page}}">{{album.name}}</a>
<span class="sep"></span>
<a href="./">{{song.title}}</a>
</div>
<div id="song">
<h4 class="number">{{song.number}}</h4>
<h3 class="title">{{song.title}}</h3>
<div class="cover"></div>
<!-- <img src="{{album.image}}" alt="{{album.name}} cover image"> -->
</div>
</div>
<script type="text/javascript" src="base.js"></script>
</body>
</html>