Initial commit
This commit is contained in:
parent
cb7566922f
commit
e77a8821e0
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=1920, height=1080 initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
126
src/App.css
126
src/App.css
@ -1,38 +1,108 @@
|
|||||||
.App {
|
:root {
|
||||||
text-align: center;
|
--color-bg: #FADF7F;
|
||||||
|
--color-primary: #D9B26F;
|
||||||
|
--color-warn: #803B42;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-logo {
|
.content {
|
||||||
height: 40vmin;
|
height: 100%;
|
||||||
pointer-events: none;
|
width: 150px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
.song-list {
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
display: flex;
|
||||||
.App-logo {
|
flex-direction: column;
|
||||||
animation: App-logo-spin infinite 20s linear;
|
gap: 5px;
|
||||||
|
flex: 1 1 100%;
|
||||||
|
.entry {
|
||||||
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
|
flex-direction: column;
|
||||||
|
span {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
span:nth-child(1) {
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
span:nth-child(2) {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.controls {
|
||||||
.App-header {
|
display: flex;
|
||||||
background-color: #282c34;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
gap: 5px;
|
||||||
|
.buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
justify-content: space-around;
|
||||||
|
place-items: center;
|
||||||
|
button {
|
||||||
|
transition: transform 400ms ease-in-out;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
height: 1.5rem;
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
button > div {
|
||||||
|
transition: transform 400ms ease-in-out;
|
||||||
|
}
|
||||||
|
button.plus {
|
||||||
|
z-index: 1;
|
||||||
|
div {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.cross {
|
||||||
|
z-index: 1;
|
||||||
|
transform: translate(-25vw, 0);
|
||||||
|
div {
|
||||||
|
transform: rotate(225deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.done {
|
||||||
|
transform: translate(25vw, 0);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
button.gone {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.add-song-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 5px;
|
||||||
font-size: calc(10px + 2vmin);
|
input {
|
||||||
color: white;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.App-link {
|
.entry {
|
||||||
color: #61dafb;
|
position: relative;
|
||||||
}
|
padding: 15px;
|
||||||
|
background-color: var(--color-primary);
|
||||||
@keyframes App-logo-spin {
|
outline: inherit;
|
||||||
from {
|
border: none;
|
||||||
transform: rotate(0deg);
|
.edit {
|
||||||
}
|
position: absolute;
|
||||||
to {
|
right: 12px;
|
||||||
transform: rotate(360deg);
|
top: 2px;
|
||||||
|
font-size: 8pt !important;
|
||||||
|
}
|
||||||
|
.delete {
|
||||||
|
position: absolute;
|
||||||
|
right: 1px;
|
||||||
|
top: -6px;
|
||||||
|
font-size: 15pt !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
104
src/App.tsx
104
src/App.tsx
@ -1,24 +1,94 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
function App() {
|
type Entry =[string, string, string];
|
||||||
|
|
||||||
|
const songlib: Entry[] = [
|
||||||
|
[ "https://tabs.ultimate-guitar.com/tab/sdp/ne-leiche-chords-1869475", "SDP", "Ne Leiche" ],
|
||||||
|
[ "https://tabs.ultimate-guitar.com/tab/gloria-gaynor/i-will-survive-chords-154172", "Gloria Gaynor", "I will survive" ],
|
||||||
|
[ "https://tabs.ultimate-guitar.com/tab/sting/englishman-in-new-york-chords-2220", "Sting", "Englishman in New York"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [store, setStore] = useState<null | Entry[]>(null);
|
||||||
|
const [addNew, setAddNew] = useState<null|Entry>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (store !== null) {
|
||||||
|
localStorage.setItem("jamsite", JSON.stringify(store));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entry = localStorage.getItem("jamsite");
|
||||||
|
if (!entry) {
|
||||||
|
setStore([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reStored = JSON.parse(entry);
|
||||||
|
setStore(reStored);
|
||||||
|
}, [store])
|
||||||
|
|
||||||
|
const sortedStore = [...songlib, ...(store??[]) ].sort((a, b) => a[2].localeCompare(b[2]))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="content">
|
||||||
<header className="App-header">
|
<div className="controls">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
<div className="buttons">
|
||||||
<p>
|
<button className={addNew === null ? "plus": "cross"} onClick={() => { if (addNew) { setAddNew(null); } else { setAddNew(["", "", ""]); }}} ><div>➕</div></button>
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
<button className={addNew === null ? "gone" : "done"} onClick={() => {
|
||||||
</p>
|
if (addNew === null) return;
|
||||||
<a
|
setStore((store) => {
|
||||||
className="App-link"
|
setAddNew(null);
|
||||||
href="https://reactjs.org"
|
return [...(store??[]), addNew];
|
||||||
target="_blank"
|
});
|
||||||
rel="noopener noreferrer"
|
}}>✅</button>
|
||||||
>
|
</div>
|
||||||
Learn React
|
{ addNew !== null ?
|
||||||
</a>
|
<div className={"add-song-menu"}>
|
||||||
</header>
|
<input defaultValue={addNew[0]} onChange={(e) => {
|
||||||
|
setAddNew((addNew) => {
|
||||||
|
if (addNew === null) return null;
|
||||||
|
addNew[0] = e.target.value
|
||||||
|
return [...addNew];
|
||||||
|
});
|
||||||
|
}} placeholder="URL"></input>
|
||||||
|
<input defaultValue={addNew[1]} onChange={(e) => {
|
||||||
|
setAddNew((addNew) => {
|
||||||
|
if (addNew === null) return null;
|
||||||
|
addNew[1] = e.target.value
|
||||||
|
return [...addNew];
|
||||||
|
});
|
||||||
|
}} placeholder="Artist"></input>
|
||||||
|
<input defaultValue={addNew[2]} onChange={(e) => {
|
||||||
|
setAddNew((addNew) => {
|
||||||
|
if (addNew === null) return null;
|
||||||
|
addNew[2] = e.target.value
|
||||||
|
return [...addNew];
|
||||||
|
});
|
||||||
|
}} placeholder="Title"></input>
|
||||||
|
</div>
|
||||||
|
:null }
|
||||||
|
</div>
|
||||||
|
<div className="song-list">
|
||||||
|
{sortedStore.map(([url, artist, title], index) =>
|
||||||
|
<button key={index} className="entry" onClick={() => {
|
||||||
|
window.open(url, "jamsite", "popup")
|
||||||
|
}}>
|
||||||
|
<span className="delete" onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setStore((storage) => (storage??[]).filter((element) => JSON.stringify([url, artist, title]) !== JSON.stringify(element)))
|
||||||
|
|
||||||
|
}}>x</span>
|
||||||
|
<span className="edit" onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setStore((storage) => (storage??[]).filter((element) => JSON.stringify([url, artist, title]) !== JSON.stringify(element)))
|
||||||
|
setAddNew([url, artist, title]);
|
||||||
|
}}>✏️</span>
|
||||||
|
<span>{title}</span>
|
||||||
|
<span>{artist}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user