Compare commits
90 Commits
next
...
introducin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab0c566c92 | ||
|
|
e0654b182a | ||
|
|
06de7a26e1 | ||
|
|
176b38ffa5 | ||
|
|
12e7db7310 | ||
|
|
32ac77c426 | ||
|
|
bce03af941 | ||
|
|
f6921fedc9 | ||
|
|
3477397072 | ||
|
|
0bb7bf56d2 | ||
|
|
d2b0e28751 | ||
|
|
2d0146cfc3 | ||
|
|
cfcfd4d7d7 | ||
|
|
92c6e76ad5 | ||
|
|
5a4efb41bc | ||
|
|
211bab805a | ||
|
|
b7583ac889 | ||
|
|
a63717961b | ||
|
|
9a7596a6ba | ||
|
|
62c65873f5 | ||
|
|
821b0029dd | ||
|
|
336a9755b8 | ||
|
|
f9a522240e | ||
|
|
94af90e481 | ||
|
|
85dfb5700e | ||
|
|
45010fe4d1 | ||
|
|
3948deb165 | ||
|
|
e5f73d8495 | ||
|
|
18afef151c | ||
|
|
80dbd98beb | ||
|
|
a12d1a86e5 | ||
|
|
449e7b4bb7 | ||
|
|
5ee0b81ed9 | ||
|
|
3496fde28c | ||
|
|
58b2a02989 | ||
|
|
eb69d78a38 | ||
|
|
b7a9a35c4f | ||
|
|
8de2a701ea | ||
|
|
681ccaba8c | ||
|
|
6d3f64ffc7 | ||
|
|
aa780977f1 | ||
|
|
4cb77334fb | ||
|
|
293e95c5b0 | ||
|
|
302095ef80 | ||
|
|
feb35afa9a | ||
|
|
6b925a0434 | ||
|
|
4211ccc685 | ||
|
|
9c982283fb | ||
|
|
888958db54 | ||
|
|
cb3e9ec2cd | ||
|
|
f7e604f11b | ||
|
|
1b36b96239 | ||
|
|
ee3ef6c1e6 | ||
|
|
83bd063bea | ||
|
|
12531edec9 | ||
|
|
7edd501d0f | ||
|
|
c7036b3a5d | ||
|
|
3075517af9 | ||
|
|
e8f0bc6b8b | ||
|
|
ded3a48e0b | ||
|
|
a04a3c4fe4 | ||
|
|
4aaf70ba68 | ||
|
|
fa176cb027 | ||
|
|
c4ce344487 | ||
|
|
f2c7cccba1 | ||
|
|
901406a337 | ||
|
|
4b48b13561 | ||
|
|
10b29f6349 | ||
|
|
b24d833d12 | ||
|
|
9004c02898 | ||
|
|
409edba74c | ||
|
|
2f1c07f786 | ||
|
|
278e73745a | ||
|
|
3652432e0b | ||
|
|
67ad2ef206 | ||
|
|
f2e157a746 | ||
|
|
606515ffd5 | ||
|
|
6ff67e5f94 | ||
|
|
5f037fc647 | ||
|
|
d1c62f701e | ||
|
|
b2bef03373 | ||
|
|
49fe308da0 | ||
|
|
bce2772dcb | ||
|
|
dd0b9d6d61 | ||
|
|
1e90298f72 | ||
|
|
7f4385f8cb | ||
|
|
48f2a73291 | ||
|
|
8cf33089a2 | ||
|
|
c16be33d40 | ||
|
|
a9691a2a25 |
2
.gitignore
vendored
@@ -24,7 +24,7 @@ pnpm-debug.log*
|
||||
*.sw?
|
||||
|
||||
# TODO
|
||||
TODO.md
|
||||
# TODO.md
|
||||
|
||||
__pycache__
|
||||
dev-dist/*
|
||||
2
TODO.md
Normal file
@@ -0,0 +1,2 @@
|
||||
- Remove welcome dialog
|
||||
- Update folder page breadcrumb when response has skipped empty folders
|
||||
@@ -3,15 +3,15 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>Swing Music</title>
|
||||
<base href="./">
|
||||
<base href="./" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but this app doesn't work properly without JavaScript
|
||||
enabled. Please enable it to continue.</strong
|
||||
>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"v-wave": "^1.5.0",
|
||||
"vue": "^v3.2.45",
|
||||
"vue-boring-avatars": "^1.4.0",
|
||||
"vue-debounce": "^3.0.2",
|
||||
"vue-router": "^4.1.3",
|
||||
"vue-template-compiler": "^2.0.0",
|
||||
|
||||
@@ -11,5 +11,6 @@ onmessage = (e) => {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ trackhash, duration, source, timestamp }),
|
||||
credentials: "include"
|
||||
});
|
||||
};
|
||||
|
||||
19
public/workers/silence.js
Normal file
@@ -0,0 +1,19 @@
|
||||
onmessage = async (e) => {
|
||||
const { ending_file, starting_file } = e.data;
|
||||
|
||||
const is_dev = location.port === "5173";
|
||||
const base_url = is_dev ? "http://localhost:1980" : location.origin;
|
||||
const url = base_url + "/file/silence";
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ ending_file, starting_file }),
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
postMessage(data);
|
||||
};
|
||||
271
src/App.vue
@@ -1,170 +1,199 @@
|
||||
<template>
|
||||
<ContextMenu />
|
||||
<Modal />
|
||||
<Notification />
|
||||
<div id="drag-img" class="ellip2" style=""></div>
|
||||
<section
|
||||
id="app-grid"
|
||||
:class="{
|
||||
noSidebar: !settings.use_sidebar || !xl,
|
||||
NoSideBorders: !xxl,
|
||||
extendWidth: settings.extend_width && settings.can_extend_width,
|
||||
}"
|
||||
:style="{ maxWidth: `${content_height > 1080 ? '2220px' : '1760px'}` }"
|
||||
>
|
||||
<LeftSidebar v-if="!isMobile" />
|
||||
<NavBar />
|
||||
<div id="acontent" ref="appcontent" v-element-size="updateContentElemSize">
|
||||
<BalancerProvider>
|
||||
<router-view />
|
||||
</BalancerProvider>
|
||||
</div>
|
||||
<RightSideBar v-if="settings.use_sidebar && xl" />
|
||||
<BottomBar />
|
||||
<!-- <BubbleManager /> -->
|
||||
</section>
|
||||
<ContextMenu />
|
||||
<Modal />
|
||||
<Notification />
|
||||
<div
|
||||
id="drag-img"
|
||||
class="ellip2"
|
||||
style=""
|
||||
></div>
|
||||
<section
|
||||
id="app-grid"
|
||||
:class="{
|
||||
useSidebar: settings.use_sidebar && xl,
|
||||
NoSideBorders: settings.is_alt_layout || !xxl,
|
||||
extendWidth: settings.extend_width && settings.can_extend_width,
|
||||
is_alt_layout: settings.is_alt_layout,
|
||||
}"
|
||||
:style="{
|
||||
maxWidth: `${
|
||||
settings.is_default_layout
|
||||
? content_height > 1080
|
||||
? '2220px'
|
||||
: '1760px'
|
||||
: ''
|
||||
}`,
|
||||
}"
|
||||
>
|
||||
<LeftSidebar v-if="settings.is_default_layout && !isMobile" />
|
||||
<NavBar />
|
||||
<div
|
||||
id="acontent"
|
||||
v-element-size="updateContentElemSize"
|
||||
>
|
||||
<div
|
||||
id="contentresizer"
|
||||
ref="appcontent"
|
||||
></div>
|
||||
<BalancerProvider>
|
||||
<RouterView />
|
||||
</BalancerProvider>
|
||||
</div>
|
||||
<RightSideBar v-if="settings.use_sidebar && xl" />
|
||||
<BottomBar />
|
||||
<!-- <BubbleManager /> -->
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// @libraries
|
||||
import { vElementSize } from "@vueuse/components";
|
||||
import { onStartTyping } from "@vueuse/core";
|
||||
import { onMounted, Ref, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { BalancerProvider } from "vue-wrap-balancer";
|
||||
import { vElementSize } from '@vueuse/components'
|
||||
import { onStartTyping } from '@vueuse/core'
|
||||
import { onMounted, Ref, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { BalancerProvider } from 'vue-wrap-balancer'
|
||||
|
||||
// @stores
|
||||
import {
|
||||
content_height,
|
||||
content_width,
|
||||
isMobile,
|
||||
content_height,
|
||||
content_width,
|
||||
isMobile,
|
||||
resizer_height,
|
||||
resizer_width,
|
||||
updateCardWidth,
|
||||
} from "@/stores/content-width";
|
||||
import useLyrics from "@/stores/lyrics";
|
||||
import useModal from "@/stores/modal";
|
||||
import useQueue from "@/stores/queue";
|
||||
import useSettings from "@/stores/settings";
|
||||
import useTracker from "@/stores/tracker";
|
||||
} from '@/stores/content-width'
|
||||
import useLyrics from '@/stores/lyrics'
|
||||
import useModal from '@/stores/modal'
|
||||
import useQueue from '@/stores/queue'
|
||||
import useSettings from '@/stores/settings'
|
||||
import useTracker from '@/stores/tracker'
|
||||
import useAuth from '@/stores/auth'
|
||||
|
||||
// @utils
|
||||
import handleShortcuts from "@/helpers/useKeyboard";
|
||||
import { readLocalStorage, writeLocalStorage } from "@/utils";
|
||||
import { xl, xxl } from "./composables/useBreakpoints";
|
||||
import handleShortcuts from '@/helpers/useKeyboard'
|
||||
import { readLocalStorage, writeLocalStorage } from '@/utils'
|
||||
import { xl, xxl } from './composables/useBreakpoints'
|
||||
|
||||
// @small-components
|
||||
import ContextMenu from "@/components/ContextMenu.vue";
|
||||
import Modal from "@/components/modal.vue";
|
||||
import Notification from "@/components/Notification.vue";
|
||||
import ContextMenu from '@/components/ContextMenu.vue'
|
||||
import Modal from '@/components/modal.vue'
|
||||
import Notification from '@/components/Notification.vue'
|
||||
|
||||
// @app-grid-components
|
||||
import BottomBar from "@/components/BottomBar/BottomBar.vue";
|
||||
import LeftSidebar from "@/components/LeftSidebar/index.vue";
|
||||
import NavBar from "@/components/nav/NavBar.vue";
|
||||
import RightSideBar from "@/components/RightSideBar/Main.vue";
|
||||
import { getAllSettings } from "@/requests/settings";
|
||||
import BottomBar from '@/components/BottomBar/BottomBar.vue'
|
||||
import LeftSidebar from '@/components/LeftSidebar/index.vue'
|
||||
import NavBar from '@/components/nav/NavBar.vue'
|
||||
import RightSideBar from '@/components/RightSideBar/Main.vue'
|
||||
|
||||
import { getRootDirs } from "@/requests/settings/rootdirs";
|
||||
import { getLoggedInUser } from './requests/auth'
|
||||
import { getAllSettings } from '@/requests/settings'
|
||||
import { getRootDirs } from '@/requests/settings/rootdirs'
|
||||
// import BubbleManager from "./components/bubbles/BinManager.vue";
|
||||
|
||||
const appcontent: Ref<HTMLLegendElement | null> = ref(null);
|
||||
const queue = useQueue();
|
||||
const modal = useModal();
|
||||
const lyrics = useLyrics();
|
||||
const router = useRouter();
|
||||
const settings = useSettings();
|
||||
useTracker();
|
||||
const appcontent: Ref<HTMLLegendElement | null> = ref(null)
|
||||
const auth = useAuth()
|
||||
const resizercontent: Ref<HTMLLegendElement | null> = ref(null);
|
||||
const queue = useQueue()
|
||||
const modal = useModal()
|
||||
const lyrics = useLyrics()
|
||||
const router = useRouter()
|
||||
const settings = useSettings()
|
||||
useTracker()
|
||||
|
||||
handleShortcuts(useQueue, useModal);
|
||||
handleShortcuts(useQueue, useModal)
|
||||
|
||||
router.afterEach(() => {
|
||||
(document.getElementById("acontent") as HTMLElement).scrollTo(0, 0);
|
||||
});
|
||||
;(document.getElementById('acontent') as HTMLElement).scrollTo(0, 0)
|
||||
})
|
||||
|
||||
onStartTyping(() => {
|
||||
if (isMobile.value) return;
|
||||
|
||||
const elem = document.getElementById("globalsearch") as HTMLInputElement;
|
||||
elem.focus();
|
||||
elem.value = "";
|
||||
});
|
||||
const elem = document.getElementById('globalsearch') as HTMLInputElement
|
||||
elem.focus()
|
||||
elem.value = ''
|
||||
})
|
||||
|
||||
function getContentSize() {
|
||||
const elem = document.getElementById("acontent") as HTMLElement;
|
||||
return {
|
||||
width: elem.offsetWidth,
|
||||
height: elem.offsetHeight,
|
||||
};
|
||||
const elem = document.getElementById('acontent') as HTMLElement
|
||||
return {
|
||||
width: elem.offsetWidth,
|
||||
height: elem.offsetHeight,
|
||||
}
|
||||
}
|
||||
|
||||
function updateContentElemSize({
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
width: number;
|
||||
height: number;
|
||||
}) {
|
||||
// 1372 is the maxwidth of the #acontent. see app-grid.scss > $maxwidth
|
||||
const elem_width = Math.min(1372, appcontent.value?.offsetWidth || 0);
|
||||
function updateContentElemSize({ width, height }: { width: number; height: number }) {
|
||||
// 1572 is the maxwidth of the #acontent. see app-grid.scss > $maxwidth
|
||||
const elem_width = appcontent.value?.offsetWidth || 0;
|
||||
|
||||
content_width.value = elem_width;
|
||||
content_height.value = height;
|
||||
|
||||
const elem_resizer_width = resizercontent.value?.offsetWidth || 0;
|
||||
resizer_width.value = elem_resizer_width;
|
||||
resizer_height.value = height;
|
||||
|
||||
updateCardWidth();
|
||||
}
|
||||
|
||||
function handleWelcomeModal() {
|
||||
let welcomeShowCount = readLocalStorage("shown-welcome-message");
|
||||
let welcomeShowCount = readLocalStorage('shown-welcome-message')
|
||||
|
||||
if (!welcomeShowCount) {
|
||||
welcomeShowCount = 0;
|
||||
}
|
||||
if (!welcomeShowCount) {
|
||||
welcomeShowCount = 0
|
||||
}
|
||||
|
||||
if (welcomeShowCount < 2) {
|
||||
modal.showWelcomeModal();
|
||||
writeLocalStorage("shown-welcome-message", welcomeShowCount + 1);
|
||||
}
|
||||
if (welcomeShowCount < 2) {
|
||||
modal.showWelcomeModal()
|
||||
writeLocalStorage('shown-welcome-message', welcomeShowCount + 1)
|
||||
}
|
||||
}
|
||||
|
||||
function handleRootDirsPrompt() {
|
||||
getRootDirs().then((dirs) => {
|
||||
if (dirs.length === 0) {
|
||||
modal.showRootDirsPromptModal();
|
||||
} else {
|
||||
settings.setRootDirs(dirs);
|
||||
}
|
||||
});
|
||||
getRootDirs().then((dirs) => {
|
||||
if (dirs.length === 0) {
|
||||
modal.showRootDirsPromptModal()
|
||||
} else {
|
||||
settings.setRootDirs(dirs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const { width, height } = getContentSize();
|
||||
updateContentElemSize({ width, height });
|
||||
onMounted(async () => {
|
||||
const { width, height } = getContentSize()
|
||||
updateContentElemSize({ width, height })
|
||||
|
||||
handleWelcomeModal();
|
||||
settings.initializeVolume();
|
||||
const res = await getLoggedInUser()
|
||||
|
||||
handleRootDirsPrompt();
|
||||
if (res.status == 200) {
|
||||
auth.setUser(res.data)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
getAllSettings()
|
||||
.then(({ settings: data }) => {
|
||||
settings.mapDbSettings(data);
|
||||
})
|
||||
.then(() => {
|
||||
if (settings.use_lyrics_plugin) return;
|
||||
});
|
||||
handleWelcomeModal()
|
||||
settings.initializeVolume()
|
||||
|
||||
if (queue.currenttrack) {
|
||||
lyrics.checkExists(
|
||||
queue.currenttrack.filepath,
|
||||
queue.currenttrack.trackhash
|
||||
);
|
||||
}
|
||||
});
|
||||
handleRootDirsPrompt()
|
||||
|
||||
getAllSettings()
|
||||
.then(({ settings: data }) => {
|
||||
settings.mapDbSettings(data)
|
||||
})
|
||||
.then(() => {
|
||||
if (queue.currenttrack && !settings.use_lyrics_plugin) {
|
||||
lyrics.checkExists(
|
||||
queue.currenttrack.filepath,
|
||||
queue.currenttrack.trackhash
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./assets/scss/mixins.scss";
|
||||
|
||||
.r-sidebar {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@import './assets/scss/mixins.scss';
|
||||
.designatedOS .r-sidebar {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
src/assets/fonts/SFCompactDisplay-Bold.woff2
Normal file
BIN
src/assets/fonts/SFCompactDisplay-Medium.woff2
Normal file
BIN
src/assets/fonts/SFCompactDisplay-Regular.woff2
Normal file
BIN
src/assets/fonts/SFCompactDisplay-Semibold.woff2
Normal file
BIN
src/assets/fonts/sf-mono-medium.woff2
Normal file
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.9912 22.7422C18.9746 22.7422 23.0879 18.6289 23.0879 13.6543C23.0879 8.67969 18.9658 4.56641 13.9824 4.56641C9.00781 4.56641 4.90332 8.67969 4.90332 13.6543C4.90332 18.6289 9.0166 22.7422 13.9912 22.7422ZM13.9912 20.9316C9.95703 20.9316 6.73145 17.6885 6.73145 13.6543C6.73145 9.62012 9.95703 6.38574 13.9824 6.38574C18.0166 6.38574 21.2598 9.62012 21.2686 13.6543C21.2773 17.6885 18.0254 20.9316 13.9912 20.9316ZM14 17.0996C15.9072 17.0996 17.4453 15.5615 17.4453 13.6455C17.4453 11.7471 15.9072 10.2002 14 10.2002C12.084 10.2002 10.5459 11.7471 10.5459 13.6455C10.5459 15.5615 12.084 17.0996 14 17.0996Z" fill="#fff"/>
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.9912 22.7422C18.9746 22.7422 23.0879 18.6289 23.0879 13.6543C23.0879 8.67969 18.9658 4.56641 13.9824 4.56641C9.00781 4.56641 4.90332 8.67969 4.90332 13.6543C4.90332 18.6289 9.0166 22.7422 13.9912 22.7422ZM13.9912 20.9316C9.95703 20.9316 6.73145 17.6885 6.73145 13.6543C6.73145 9.62012 9.95703 6.38574 13.9824 6.38574C18.0166 6.38574 21.2598 9.62012 21.2686 13.6543C21.2773 17.6885 18.0254 20.9316 13.9912 20.9316ZM14 17.0996C15.9072 17.0996 17.4453 15.5615 17.4453 13.6455C17.4453 11.7471 15.9072 10.2002 14 10.2002C12.084 10.2002 10.5459 11.7471 10.5459 13.6455C10.5459 15.5615 12.084 17.0996 14 17.0996Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 722 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 13.8477C16.127 13.8477 17.8496 11.9668 17.8496 9.66406C17.8496 7.39648 16.127 5.59473 14 5.59473C11.8818 5.59473 10.1416 7.42285 10.1504 9.68164C10.1592 11.9756 11.873 13.8477 14 13.8477ZM14 12.3096C12.7871 12.3096 11.7588 11.1582 11.7588 9.68164C11.75 8.24023 12.7783 7.13281 14 7.13281C15.2305 7.13281 16.2412 8.22266 16.2412 9.66406C16.2412 11.1406 15.2217 12.3096 14 12.3096ZM8.51562 22.0215H19.4756C20.9961 22.0215 21.7256 21.5381 21.7256 20.501C21.7256 18.084 18.7109 14.8672 14 14.8672C9.28906 14.8672 6.26562 18.084 6.26562 20.501C6.26562 21.5381 6.99512 22.0215 8.51562 22.0215ZM8.24316 20.4834C8.03223 20.4834 7.95312 20.4131 7.95312 20.2549C7.95312 18.9102 10.124 16.4053 14 16.4053C17.8672 16.4053 20.0381 18.9102 20.0381 20.2549C20.0381 20.4131 19.959 20.4834 19.748 20.4834H8.24316Z" fill="#F2F2F2"/>
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 13.8477C16.127 13.8477 17.8496 11.9668 17.8496 9.66406C17.8496 7.39648 16.127 5.59473 14 5.59473C11.8818 5.59473 10.1416 7.42285 10.1504 9.68164C10.1592 11.9756 11.873 13.8477 14 13.8477ZM14 12.3096C12.7871 12.3096 11.7588 11.1582 11.7588 9.68164C11.75 8.24023 12.7783 7.13281 14 7.13281C15.2305 7.13281 16.2412 8.22266 16.2412 9.66406C16.2412 11.1406 15.2217 12.3096 14 12.3096ZM8.51562 22.0215H19.4756C20.9961 22.0215 21.7256 21.5381 21.7256 20.501C21.7256 18.084 18.7109 14.8672 14 14.8672C9.28906 14.8672 6.26562 18.084 6.26562 20.501C6.26562 21.5381 6.99512 22.0215 8.51562 22.0215ZM8.24316 20.4834C8.03223 20.4834 7.95312 20.4131 7.95312 20.2549C7.95312 18.9102 10.124 16.4053 14 16.4053C17.8672 16.4053 20.0381 18.9102 20.0381 20.2549C20.0381 20.4131 19.959 20.4834 19.748 20.4834H8.24316Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 931 B After Width: | Height: | Size: 913 B |
3
src/assets/icons/avatar.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.10515 24.5753H21.6977C23.0148 24.5753 23.8007 23.9513 23.8007 22.9154C23.8007 19.9004 19.9776 15.7538 13.8955 15.7538C7.82523 15.7538 4 19.9004 4 22.9154C4 23.9513 4.78797 24.5753 6.10515 24.5753ZM13.9052 13.6731C16.4594 13.6731 18.6327 11.4019 18.6327 8.46591C18.6327 5.58403 16.453 3.39099 13.9052 3.39099C11.3573 3.39099 9.17547 5.62458 9.17969 8.48724C9.1818 11.4019 11.3434 13.6731 13.9052 13.6731Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 519 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M9.96582 22.7686H18.043C19.3965 22.7686 20.2666 21.9512 20.3369 20.5977L20.9258 7.94141H21.8926C22.3408 7.94141 22.6836 7.58984 22.6836 7.15039C22.6836 6.71094 22.332 6.37695 21.8926 6.37695H17.9902V5.05859C17.9902 3.70508 17.1289 2.91406 15.6611 2.91406H12.3213C10.8535 2.91406 9.99219 3.70508 9.99219 5.05859V6.37695H6.10742C5.66797 6.37695 5.31641 6.71973 5.31641 7.15039C5.31641 7.59863 5.66797 7.94141 6.10742 7.94141H7.07422L7.66309 20.5977C7.7334 21.96 8.59473 22.7686 9.96582 22.7686ZM11.6357 5.1377C11.6357 4.68945 11.9521 4.39941 12.4355 4.39941H15.5469C16.0303 4.39941 16.3467 4.68945 16.3467 5.1377V6.37695H11.6357V5.1377ZM10.1416 21.1953C9.6582 21.1953 9.30664 20.835 9.28027 20.3164L8.69141 7.94141H19.2822L18.7109 20.3164C18.6934 20.8438 18.3506 21.1953 17.8496 21.1953H10.1416ZM11.4072 19.7803C11.7852 19.7803 12.0225 19.543 12.0137 19.1914L11.75 9.99805C11.7412 9.64648 11.4951 9.41797 11.1348 9.41797C10.7656 9.41797 10.5283 9.65527 10.5371 10.0068L10.8008 19.2002C10.8096 19.5518 11.0557 19.7803 11.4072 19.7803ZM14 19.7803C14.3691 19.7803 14.624 19.5518 14.624 19.2002V10.0068C14.624 9.65527 14.3691 9.41797 14 9.41797C13.6309 9.41797 13.3848 9.65527 13.3848 10.0068V19.2002C13.3848 19.5518 13.6309 19.7803 14 19.7803ZM16.5928 19.7891C16.9443 19.7891 17.1904 19.5518 17.1992 19.2002L17.4629 10.0068C17.4717 9.65527 17.2344 9.42676 16.8652 9.42676C16.5049 9.42676 16.2588 9.65527 16.25 10.0068L15.9863 19.2002C15.9775 19.543 16.2148 19.7891 16.5928 19.7891Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
4
src/assets/icons/eye.slash.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 33 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.1039 24.2276C18.2667 24.2276 20.2462 23.8513 22.0458 23.2428L20.1795 21.3723C18.9009 21.7511 17.5488 21.9793 16.1039 21.9793C8.60694 21.9793 2.65404 15.7188 2.65404 14.0854C2.65404 13.2989 4.65326 10.7519 7.8614 8.78101L6.10032 7.02204C2.26547 9.45297 0 12.6801 0 14.0854C0 16.4824 6.62578 24.2276 16.1039 24.2276ZM16.1039 3.94751C14.0627 3.94751 12.2027 4.29696 10.4822 4.86696L12.3581 6.73539C13.5459 6.39296 14.771 6.19562 16.1039 6.19562C23.5913 6.19562 29.5463 12.7162 29.5463 14.0854C29.5463 15.0029 27.6516 17.3973 24.6312 19.2925L26.37 21.0376C30.0415 18.6163 32.2003 15.4619 32.2003 14.0854C32.2003 11.6831 25.7119 3.94751 16.1039 3.94751ZM16.1039 20.4032C17.0128 20.4032 17.8695 20.1833 18.6375 19.8247L10.3399 11.5272C9.9696 12.2931 9.76147 13.1593 9.76147 14.0875C9.77108 17.5246 12.5794 20.4032 16.1039 20.4032ZM21.9532 16.3501C22.2638 15.6536 22.4313 14.8801 22.4313 14.0833C22.4313 10.5609 19.6134 7.77179 16.106 7.77179C15.292 7.77179 14.5282 7.93937 13.8316 8.22859L21.9532 16.3501Z" fill="currentColor"/>
|
||||
<path d="M25.3391 24.4257C25.7033 24.7996 26.2869 24.8188 26.6586 24.4257C27.0496 24.0327 27.0207 23.48 26.6586 23.1062L6.81388 3.27316C6.44967 2.90895 5.85435 2.90895 5.48264 3.27316C5.13014 3.62566 5.13014 4.2423 5.48264 4.59269L25.3391 24.4257Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
3
src/assets/icons/eye.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 33 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.1039 24.3001C25.7119 24.3001 32.2003 16.555 32.2003 14.158C32.2003 11.7557 25.7023 4.02014 16.1039 4.02014C6.63539 4.02014 0 11.7557 0 14.158C0 16.555 6.62578 24.3001 16.1039 24.3001ZM16.1039 22.052C8.60694 22.052 2.65404 15.7914 2.65404 14.158C2.65404 12.7888 8.60694 6.26825 16.1039 6.26825C23.572 6.26825 29.5463 12.7888 29.5463 14.158C29.5463 15.7914 23.572 22.052 16.1039 22.052ZM16.106 20.4759C19.6177 20.4759 22.4313 17.5973 22.4313 14.158C22.4313 10.6335 19.6177 7.84654 16.106 7.84654C12.5794 7.84654 9.75186 10.6314 9.76147 14.158C9.78069 17.5973 12.5794 20.4759 16.106 20.4759ZM16.1018 16.2048C14.9632 16.2048 14.0442 15.2774 14.0442 14.158C14.0442 13.029 14.9632 12.1154 16.1018 12.1154C17.2371 12.1154 18.1582 13.029 18.1582 14.158C18.1582 15.2774 17.2371 16.2048 16.1018 16.2048Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 910 B |
3
src/assets/icons/file.fill.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.80577 26.3655H20.2891C22.8013 26.3655 24.0948 25.0506 24.0948 22.5287V12.0379H15.3341C13.8679 12.0379 13.1552 11.3251 13.1552 9.85679V1H7.80577C5.30523 1 4 2.32445 4 4.84632V22.5287C4 25.0602 5.29562 26.3655 7.80577 26.3655ZM15.5423 10.3248H23.9669C23.905 9.80008 23.5166 9.29336 22.9077 8.67273L16.5098 2.17656C15.921 1.58031 15.4026 1.19406 14.8682 1.12047V9.66039C14.8682 10.1034 15.0897 10.3248 15.5423 10.3248Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 531 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.1807 6.99219H13.0332C12.4004 6.99219 12.0225 6.85156 11.5303 6.44727L11.0381 6.04297C10.4141 5.5332 9.95703 5.36621 9.03418 5.36621H6.54688C4.89453 5.36621 3.91895 6.33301 3.91895 8.1875V10.6484H24.0723V9.85742C24.0723 7.97656 23.0791 6.99219 21.1807 6.99219ZM6.81055 21.7666H21.3916C23.0879 21.7666 24.0723 20.7822 24.0723 18.9014V11.9141H3.91895V18.9014C3.91895 20.791 4.91211 21.7666 6.81055 21.7666Z" fill="#F2F2F2"/>
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.1807 6.99219H13.0332C12.4004 6.99219 12.0225 6.85156 11.5303 6.44727L11.0381 6.04297C10.4141 5.5332 9.95703 5.36621 9.03418 5.36621H6.54688C4.89453 5.36621 3.91895 6.33301 3.91895 8.1875V10.6484H24.0723V9.85742C24.0723 7.97656 23.0791 6.99219 21.1807 6.99219ZM6.81055 21.7666H21.3916C23.0879 21.7666 24.0723 20.7822 24.0723 18.9014V11.9141H3.91895V18.9014C3.91895 20.791 4.91211 21.7666 6.81055 21.7666Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 520 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.81055 21.7666H21.3916C23.0879 21.7666 24.0723 20.7822 24.0723 18.9014V9.85742C24.0723 7.97656 23.0791 6.99219 21.1807 6.99219H13.0332C12.4004 6.99219 12.0225 6.85156 11.5303 6.44727L11.0381 6.04297C10.4141 5.5332 9.95703 5.36621 9.03418 5.36621H6.54688C4.89453 5.36621 3.91895 6.33301 3.91895 8.1875V18.9014C3.91895 20.791 4.91211 21.7666 6.81055 21.7666ZM5.66797 8.33691C5.66797 7.53711 6.11621 7.11523 6.89844 7.11523H8.56836C9.19238 7.11523 9.56152 7.26465 10.0625 7.66895L10.5547 8.08203C11.1699 8.57422 11.6445 8.75 12.5674 8.75H21.0664C21.875 8.75 22.3232 9.17188 22.3232 10.0156V10.5342H5.66797V8.33691ZM6.9248 20.0176C6.11621 20.0176 5.66797 19.5957 5.66797 18.7432V12.0723H22.3232V18.752C22.3232 19.5957 21.875 20.0176 21.0664 20.0176H6.9248Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 898 B After Width: | Height: | Size: 875 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" fill="currentColor" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg fill="currentColor" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.09668 11.1846C5.09668 14.9375 8.25195 18.6465 13.1562 21.8105C13.4287 21.9863 13.7627 22.1621 13.9912 22.1621C14.2197 22.1621 14.5537 21.9863 14.8262 21.8105C19.7393 18.6465 22.8857 14.9375 22.8857 11.1846C22.8857 7.94141 20.6445 5.69141 17.7705 5.69141C16.0918 5.69141 14.7822 6.45605 13.9912 7.61621C13.2178 6.46484 11.8994 5.69141 10.2207 5.69141C7.33789 5.69141 5.09668 7.94141 5.09668 11.1846ZM6.90723 11.1758C6.90723 8.96094 8.36621 7.45801 10.3262 7.45801C11.9082 7.45801 12.7959 8.41602 13.3496 9.25098C13.5957 9.61133 13.7627 9.72559 13.9912 9.72559C14.2285 9.72559 14.3779 9.60254 14.6328 9.25098C15.2305 8.43359 16.083 7.45801 17.6562 7.45801C19.625 7.45801 21.084 8.96094 21.084 11.1758C21.084 14.2695 17.8672 17.6973 14.1582 20.1582C14.0791 20.2109 14.0264 20.2461 13.9912 20.2461C13.9561 20.2461 13.9033 20.2109 13.833 20.1582C10.124 17.6973 6.90723 14.2695 6.90723 11.1758Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 993 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 1.875C7.75195 1.875 1.875 7.75195 1.875 15C1.875 22.248 7.75195 28.125 15 28.125C22.248 28.125 28.125 22.248 28.125 15C28.125 7.75195 22.248 1.875 15 1.875ZM15 25.8984C8.98242 25.8984 4.10156 21.0176 4.10156 15C4.10156 8.98242 8.98242 4.10156 15 4.10156C21.0176 4.10156 25.8984 8.98242 25.8984 15C25.8984 21.0176 21.0176 25.8984 15 25.8984Z" fill="white"/>
|
||||
<path d="M13.5938 9.84375C13.5938 10.2167 13.7419 10.5744 14.0056 10.8381C14.2694 11.1018 14.627 11.25 15 11.25C15.373 11.25 15.7306 11.1018 15.9944 10.8381C16.2581 10.5744 16.4062 10.2167 16.4062 9.84375C16.4062 9.47079 16.2581 9.1131 15.9944 8.84938C15.7306 8.58566 15.373 8.4375 15 8.4375C14.627 8.4375 14.2694 8.58566 14.0056 8.84938C13.7419 9.1131 13.5938 9.47079 13.5938 9.84375V9.84375ZM15.7031 13.125H14.2969C14.168 13.125 14.0625 13.2305 14.0625 13.3594V21.3281C14.0625 21.457 14.168 21.5625 14.2969 21.5625H15.7031C15.832 21.5625 15.9375 21.457 15.9375 21.3281V13.3594C15.9375 13.2305 15.832 13.125 15.7031 13.125Z" fill="white"/>
|
||||
<svg viewBox="0 0 30 30" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 1.875C7.75195 1.875 1.875 7.75195 1.875 15C1.875 22.248 7.75195 28.125 15 28.125C22.248 28.125 28.125 22.248 28.125 15C28.125 7.75195 22.248 1.875 15 1.875ZM15 25.8984C8.98242 25.8984 4.10156 21.0176 4.10156 15C4.10156 8.98242 8.98242 4.10156 15 4.10156C21.0176 4.10156 25.8984 8.98242 25.8984 15C25.8984 21.0176 21.0176 25.8984 15 25.8984Z" fill="currentColor"/>
|
||||
<path d="M13.5938 9.84375C13.5938 10.2167 13.7419 10.5744 14.0056 10.8381C14.2694 11.1018 14.627 11.25 15 11.25C15.373 11.25 15.7306 11.1018 15.9944 10.8381C16.2581 10.5744 16.4062 10.2167 16.4062 9.84375C16.4062 9.47079 16.2581 9.1131 15.9944 8.84938C15.7306 8.58566 15.373 8.4375 15 8.4375C14.627 8.4375 14.2694 8.58566 14.0056 8.84938C13.7419 9.1131 13.5938 9.47079 13.5938 9.84375V9.84375ZM15.7031 13.125H14.2969C14.168 13.125 14.0625 13.2305 14.0625 13.3594V21.3281C14.0625 21.457 14.168 21.5625 14.2969 21.5625H15.7031C15.832 21.5625 15.9375 21.457 15.9375 21.3281V13.3594C15.9375 13.2305 15.832 13.125 15.7031 13.125Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.39687 27.1131C9.04913 27.1131 9.53405 26.7937 10.3459 26.0807L14.3572 22.5294H21.4504C24.9784 22.5294 26.9547 20.503 26.9547 17.0251V7.93679C26.9547 4.45891 24.9784 2.4325 21.4504 2.4325H6.50218C2.97625 2.4325 1 4.4493 1 7.93679V17.0251C1 20.5126 3.02476 22.5294 6.42836 22.5294H6.9107V25.4181C6.9107 26.4533 7.45422 27.1131 8.39687 27.1131ZM8.96593 24.532V21.1863C8.96593 20.4986 8.67132 20.2335 8.01319 20.2335H6.56757C4.35225 20.2335 3.29592 19.1066 3.29592 16.9523V8.00007C3.29592 5.84569 4.35225 4.72842 6.56757 4.72842H21.3871C23.5928 4.72842 24.6588 5.84569 24.6588 8.00007V16.9523C24.6588 19.1066 23.5928 20.2335 21.3871 20.2335H14.2321C13.521 20.2335 13.1823 20.3484 12.6864 20.8497L8.96593 24.532Z" fill="currentColor"/>
|
||||
<path d="M8.2771 11.2555C8.2771 12.6189 9.13022 13.6523 10.4502 13.6523C10.9762 13.6523 11.4768 13.547 11.7878 13.1582H11.9174C11.5281 14.0908 10.6426 14.7372 9.78389 14.9491C9.37561 15.0578 9.23194 15.2495 9.23194 15.5329C9.23194 15.8427 9.4928 16.0804 9.83147 16.0804C11.0784 16.0804 13.4024 14.5982 13.4024 11.7334C13.4024 10.0933 12.3606 8.83142 10.776 8.83142C9.34444 8.83142 8.2771 9.8315 8.2771 11.2555ZM14.6327 11.2555C14.6327 12.6189 15.4837 13.6523 16.8058 13.6523C17.3317 13.6523 17.8302 13.547 18.1412 13.1582H18.273C17.8837 14.0908 16.9982 14.7372 16.1394 14.9491C15.7387 15.0578 15.5875 15.2495 15.5875 15.5329C15.5875 15.8427 15.8462 16.0804 16.1849 16.0804C17.4318 16.0804 19.7559 14.5982 19.7559 11.7334C19.7559 10.0933 18.7141 8.83142 17.1316 8.83142C15.6979 8.83142 14.6327 9.8315 14.6327 11.2555Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
3
src/assets/icons/mic.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 12.6082C5 17.4276 8.21234 20.7368 12.8291 21.1729V23.5828H8.52569C7.9196 23.5828 7.41359 24.074 7.41359 24.6822C7.41359 25.2841 7.9196 25.77 8.52569 25.77H19.2732C19.8814 25.77 20.3874 25.2841 20.3874 24.6822C20.3874 24.074 19.8814 23.5828 19.2732 23.5828H14.9719V21.1729C19.5982 20.7368 22.801 17.4276 22.801 12.6082V10.2862C22.801 9.68015 22.313 9.20906 21.7027 9.20906C21.0945 9.20906 20.5927 9.68015 20.5927 10.2862V12.5294C20.5927 16.5234 17.8983 19.1487 13.9053 19.1487C9.90263 19.1487 7.20826 16.5234 7.20826 12.5294V10.2862C7.20826 9.68015 6.71608 9.20906 6.09616 9.20906C5.48586 9.20906 5 9.68015 5 10.2862V12.6082ZM9.42241 12.0935C9.42241 14.9091 11.2665 16.8855 13.9053 16.8855C16.5324 16.8855 18.3786 14.9091 18.3786 12.0935V4.78991C18.3786 1.97109 16.5324 0 13.9053 0C11.2665 0 9.42241 1.97109 9.42241 4.78991V12.0935ZM11.6391 12.1315V4.75406C11.6391 3.19053 12.5234 2.12788 13.9053 2.12788C15.2872 2.12788 16.1598 3.18631 16.1598 4.75406V12.1315C16.1598 13.6971 15.2872 14.7555 13.9053 14.7555C12.5234 14.7555 11.6391 13.6971 11.6391 12.1315Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
3
src/assets/icons/paintbrush.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 28 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.53691 26.8314C4.56988 28.8665 6.94784 28.8899 8.94987 26.8921C10.3446 25.4974 11.8097 22.2588 12.8986 20.9125L15.3357 23.3613C16.3151 24.3524 17.5158 24.3567 18.4697 23.3985L20.0067 21.8498C20.9649 20.8799 20.9585 19.7165 19.9695 18.7275L10.6526 9.39675C9.66143 8.40348 8.48628 8.39387 7.51644 9.36371L5.98152 10.9082C5.02129 11.8685 5.01379 13.049 6.00918 14.0423L8.45371 16.4793C7.11918 17.5682 3.88058 19.0258 2.48793 20.4185C0.490115 22.4205 0.501833 24.8081 2.53691 26.8314ZM7.86565 12.179L8.7769 11.2849C9.04151 11.0224 9.32956 11.0106 9.59417 11.2753L18.0985 19.7699C18.3535 20.0346 18.3535 20.3247 18.0792 20.5989L17.1968 21.491C16.9301 21.7769 16.6249 21.7865 16.3561 21.5006L13.5427 18.6775C13.0648 18.1975 12.4306 18.2444 11.8878 18.7734C10.9737 19.6736 9.383 23.5038 7.51339 25.3596C6.44112 26.4414 5.14714 26.4393 4.03221 25.3361C2.93862 24.2308 2.92479 22.923 4.00666 21.8507C5.87627 19.9971 9.70432 18.4042 10.6067 17.4881C11.1239 16.9378 11.1804 16.3035 10.7004 15.8332L7.86565 13.0059C7.59893 12.7413 7.59893 12.4457 7.86565 12.179ZM5.74503 25.0429C6.51448 25.0429 7.13581 24.412 7.13581 23.6446C7.13581 22.8773 6.51448 22.256 5.74503 22.256C4.97768 22.256 4.34674 22.8773 4.34674 23.6446C4.34674 24.412 4.97768 25.0429 5.74503 25.0429ZM19.6507 20.1238L25.3142 14.4625C26.6719 13.1027 26.6442 11.466 25.2514 10.071L16.0395 0.845349C14.7078 -0.486366 12.4632 0.0552723 12.0043 2.07511C10.8722 6.96979 10.8612 7.36987 9.02605 9.93464L10.5741 11.4785C12.7514 8.67885 12.8764 7.35159 13.9971 3.22283C14.1305 2.70111 14.5594 2.56564 14.9114 2.90807L23.6128 11.5999C24.0992 12.0862 24.0971 12.6433 23.6501 13.0924L18.1357 18.6089L19.6507 20.1238ZM17.7654 13.2839C18.3844 13.9049 21.8084 12.0065 22.9899 10.5182L20.4139 7.95182C20.1912 10.026 19.1131 11.526 17.8006 12.8385C17.6482 12.9909 17.6599 13.1805 17.7654 13.2839Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1 +1 @@
|
||||
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="28px" height="28px" baseProfile="basic"><path d="M13,46h16.205c0.338,1.549,1.093,2.903,2.174,4H13c-1.104,0-2-0.895-2-2C11,46.895,11.896,46,13,46z"/><path d="M12.999,38l18.714,0c-1.142,0.918-2.077,2.195-2.486,4L13,42c-1.104,0-2-0.895-2-2C11,38.895,11.895,38,12.999,38z"/><path d="M13,30h28v4H13c-1.104,0-2-0.895-2-2C11,30.895,11.896,30,13,30z"/><path d="M13,22h28v4H13c-1.104,0-2-0.895-2-2C11,22.895,11.896,22,13,22z"/><path d="M13,14h28v4H13c-1.104,0-2-0.895-2-2C11,14.895,11.896,14,13,14z"/><path d="M54.026,9.158C54.997,8.834,56,9.557,56,10.581v7.484c0,0.829-0.511,1.572-1.286,1.868l-5.75,2.199 C48.384,22.353,48,22.911,48,23.532V39c0,8-4.083,11-8.561,11C35.026,50,32,47.754,32,44.079c0-3.39,2.07-4.633,6.224-5.553 c4.067-0.9,5.776-1.327,5.776-4.142V13.942c0-0.861,0.551-1.625,1.368-1.897L54.026,9.158z" /></svg>
|
||||
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" baseProfile="basic"><path d="M13,46h16.205c0.338,1.549,1.093,2.903,2.174,4H13c-1.104,0-2-0.895-2-2C11,46.895,11.896,46,13,46z"/><path d="M12.999,38l18.714,0c-1.142,0.918-2.077,2.195-2.486,4L13,42c-1.104,0-2-0.895-2-2C11,38.895,11.895,38,12.999,38z"/><path d="M13,30h28v4H13c-1.104,0-2-0.895-2-2C11,30.895,11.896,30,13,30z"/><path d="M13,22h28v4H13c-1.104,0-2-0.895-2-2C11,22.895,11.896,22,13,22z"/><path d="M13,14h28v4H13c-1.104,0-2-0.895-2-2C11,14.895,11.896,14,13,14z"/><path d="M54.026,9.158C54.997,8.834,56,9.557,56,10.581v7.484c0,0.829-0.511,1.572-1.286,1.868l-5.75,2.199 C48.384,22.353,48,22.911,48,23.532V39c0,8-4.083,11-8.561,11C35.026,50,32,47.754,32,44.079c0-3.39,2.07-4.633,6.224-5.553 c4.067-0.9,5.776-1.327,5.776-4.142V13.942c0-0.861,0.551-1.625,1.368-1.897L54.026,9.158z" /></svg>
|
||||
|
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 875 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.16016 9.50586C6.85449 9.50586 7.4082 8.95215 7.4082 8.25781C7.4082 7.57227 6.85449 7.00977 6.16016 7.00977C5.47461 7.00977 4.91211 7.57227 4.91211 8.25781C4.91211 8.95215 5.47461 9.50586 6.16016 9.50586ZM10.291 9.10156H22.2266C22.7012 9.10156 23.0791 8.73242 23.0791 8.25781C23.0791 7.7832 22.71 7.41406 22.2266 7.41406H10.291C9.8252 7.41406 9.44727 7.7832 9.44727 8.25781C9.44727 8.73242 9.81641 9.10156 10.291 9.10156ZM6.16016 14.9111C6.85449 14.9111 7.4082 14.3574 7.4082 13.6631C7.4082 12.9775 6.85449 12.415 6.16016 12.415C5.47461 12.415 4.91211 12.9775 4.91211 13.6631C4.91211 14.3574 5.47461 14.9111 6.16016 14.9111ZM10.291 14.5068H22.2266C22.7012 14.5068 23.0791 14.1377 23.0791 13.6631C23.0791 13.1885 22.71 12.8193 22.2266 12.8193H10.291C9.8252 12.8193 9.44727 13.1885 9.44727 13.6631C9.44727 14.1377 9.81641 14.5068 10.291 14.5068ZM6.16016 20.3164C6.85449 20.3164 7.4082 19.7627 7.4082 19.0684C7.4082 18.3828 6.85449 17.8203 6.16016 17.8203C5.47461 17.8203 4.91211 18.3828 4.91211 19.0684C4.91211 19.7627 5.47461 20.3164 6.16016 20.3164ZM10.291 19.9121H22.2266C22.7012 19.9121 23.0791 19.543 23.0791 19.0684C23.0791 18.5938 22.71 18.2246 22.2266 18.2246H10.291C9.8252 18.2246 9.44727 18.5938 9.44727 19.0684C9.44727 19.543 9.81641 19.9121 10.291 19.9121Z" fill="#F2F2F2"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,3 +1,3 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.63672 14.6562H12.998V20.0176C12.998 20.5625 13.4463 21.0195 14 21.0195C14.5537 21.0195 15.002 20.5625 15.002 20.0176V14.6562H20.3633C20.9082 14.6562 21.3652 14.208 21.3652 13.6543C21.3652 13.1006 20.9082 12.6523 20.3633 12.6523H15.002V7.29102C15.002 6.74609 14.5537 6.28906 14 6.28906C13.4463 6.28906 12.998 6.74609 12.998 7.29102V12.6523H7.63672C7.0918 12.6523 6.63477 13.1006 6.63477 13.6543C6.63477 14.208 7.0918 14.6562 7.63672 14.6562Z" fill="#F2F2F2"/>
|
||||
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.63672 14.6562H12.998V20.0176C12.998 20.5625 13.4463 21.0195 14 21.0195C14.5537 21.0195 15.002 20.5625 15.002 20.0176V14.6562H20.3633C20.9082 14.6562 21.3652 14.208 21.3652 13.6543C21.3652 13.1006 20.9082 12.6523 20.3633 12.6523H15.002V7.29102C15.002 6.74609 14.5537 6.28906 14 6.28906C13.4463 6.28906 12.998 6.74609 12.998 7.29102V12.6523H7.63672C7.0918 12.6523 6.63477 13.1006 6.63477 13.6543C6.63477 14.208 7.0918 14.6562 7.63672 14.6562Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 556 B |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 28 28" width="28px" height="28px">
|
||||
<g id="surface79432257">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 14 5.214844 C 13.210938 5.214844 12.5 5.625 12.105469 6.308594 L 4.253906 19.90625 C 3.859375 20.589844 3.859375 21.410156 4.253906 22.09375 C 4.652344 22.777344 5.359375 23.1875 6.148438 23.1875 L 21.851562 23.1875 C 22.640625 23.1875 23.347656 22.777344 23.746094 22.09375 C 24.140625 21.410156 24.140625 20.589844 23.746094 19.90625 L 15.894531 6.308594 C 15.5 5.625 14.789062 5.214844 14 5.214844 Z M 14 6.964844 C 14.074219 6.964844 14.265625 6.988281 14.378906 7.183594 L 22.230469 20.78125 C 22.34375 20.980469 22.269531 21.152344 22.230469 21.21875 C 22.191406 21.285156 22.078125 21.4375 21.851562 21.4375 L 6.148438 21.4375 C 5.921875 21.4375 5.808594 21.285156 5.769531 21.21875 C 5.730469 21.152344 5.65625 20.980469 5.769531 20.78125 L 13.621094 7.183594 C 13.734375 6.988281 13.925781 6.964844 14 6.964844 Z M 13.992188 10.058594 C 13.691406 10.058594 13.445312 10.152344 13.253906 10.335938 C 13.066406 10.523438 12.976562 10.753906 12.984375 11.03125 L 13.125 16.214844 C 13.140625 16.792969 13.433594 17.085938 14.007812 17.085938 C 14.5625 17.085938 14.84375 16.792969 14.851562 16.214844 L 15.015625 11.042969 C 15.023438 10.765625 14.929688 10.53125 14.730469 10.34375 C 14.535156 10.152344 14.289062 10.058594 13.992188 10.058594 Z M 14 18.238281 C 13.328125 18.238281 12.902344 18.808594 12.902344 19.289062 C 12.902344 19.769531 13.3125 20.339844 14 20.339844 C 14.6875 20.339844 15.097656 19.800781 15.097656 19.289062 C 15.097656 18.777344 14.671875 18.238281 14 18.238281 Z M 14 18.238281 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 14 5.214844 C 13.210938 5.214844 12.5 5.625 12.105469 6.308594 L 4.253906 19.90625 C 3.859375 20.589844 3.859375 21.410156 4.253906 22.09375 C 4.652344 22.777344 5.359375 23.1875 6.148438 23.1875 L 21.851562 23.1875 C 22.640625 23.1875 23.347656 22.777344 23.746094 22.09375 C 24.140625 21.410156 24.140625 20.589844 23.746094 19.90625 L 15.894531 6.308594 C 15.5 5.625 14.789062 5.214844 14 5.214844 Z M 14 6.964844 C 14.074219 6.964844 14.265625 6.988281 14.378906 7.183594 L 22.230469 20.78125 C 22.34375 20.980469 22.269531 21.152344 22.230469 21.21875 C 22.191406 21.285156 22.078125 21.4375 21.851562 21.4375 L 6.148438 21.4375 C 5.921875 21.4375 5.808594 21.285156 5.769531 21.21875 C 5.730469 21.152344 5.65625 20.980469 5.769531 20.78125 L 13.621094 7.183594 C 13.734375 6.988281 13.925781 6.964844 14 6.964844 Z M 13.992188 10.058594 C 13.691406 10.058594 13.445312 10.152344 13.253906 10.335938 C 13.066406 10.523438 12.976562 10.753906 12.984375 11.03125 L 13.125 16.214844 C 13.140625 16.792969 13.433594 17.085938 14.007812 17.085938 C 14.5625 17.085938 14.84375 16.792969 14.851562 16.214844 L 15.015625 11.042969 C 15.023438 10.765625 14.929688 10.53125 14.730469 10.34375 C 14.535156 10.152344 14.289062 10.058594 13.992188 10.058594 Z M 14 18.238281 C 13.328125 18.238281 12.902344 18.808594 12.902344 19.289062 C 12.902344 19.769531 13.3125 20.339844 14 20.339844 C 14.6875 20.339844 15.097656 19.800781 15.097656 19.289062 C 15.097656 18.777344 14.671875 18.238281 14 18.238281 Z M 14 18.238281 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 28 28" width="28px" height="28px">
|
||||
<g id="surface79671233">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 14 4.375 C 8.683594 4.375 4.375 8.683594 4.375 14 C 4.375 19.316406 8.683594 23.625 14 23.625 C 19.316406 23.625 23.625 19.316406 23.625 14 C 23.625 8.683594 19.316406 4.375 14 4.375 Z M 14 6.125 C 18.347656 6.125 21.875 9.652344 21.875 14 C 21.875 18.347656 18.347656 21.875 14 21.875 C 9.652344 21.875 6.125 18.347656 6.125 14 C 6.125 9.652344 9.652344 6.125 14 6.125 Z M 14 9.1875 C 13.273438 9.1875 12.6875 9.773438 12.6875 10.5 C 12.6875 11.226562 13.273438 11.8125 14 11.8125 C 14.726562 11.8125 15.3125 11.226562 15.3125 10.5 C 15.3125 9.773438 14.726562 9.1875 14 9.1875 Z M 14 13.125 C 13.515625 13.125 13.125 13.515625 13.125 14 L 13.125 18.375 C 13.125 18.859375 13.515625 19.25 14 19.25 C 14.484375 19.25 14.875 18.859375 14.875 18.375 L 14.875 14 C 14.875 13.515625 14.484375 13.125 14 13.125 Z M 14 13.125 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 14 4.375 C 8.683594 4.375 4.375 8.683594 4.375 14 C 4.375 19.316406 8.683594 23.625 14 23.625 C 19.316406 23.625 23.625 19.316406 23.625 14 C 23.625 8.683594 19.316406 4.375 14 4.375 Z M 14 6.125 C 18.347656 6.125 21.875 9.652344 21.875 14 C 21.875 18.347656 18.347656 21.875 14 21.875 C 9.652344 21.875 6.125 18.347656 6.125 14 C 6.125 9.652344 9.652344 6.125 14 6.125 Z M 14 9.1875 C 13.273438 9.1875 12.6875 9.773438 12.6875 10.5 C 12.6875 11.226562 13.273438 11.8125 14 11.8125 C 14.726562 11.8125 15.3125 11.226562 15.3125 10.5 C 15.3125 9.773438 14.726562 9.1875 14 9.1875 Z M 14 13.125 C 13.515625 13.125 13.125 13.515625 13.125 14 L 13.125 18.375 C 13.125 18.859375 13.515625 19.25 14 19.25 C 14.484375 19.25 14.875 18.859375 14.875 18.375 L 14.875 14 C 14.875 13.515625 14.484375 13.125 14 13.125 Z M 14 13.125 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 28 28" width="28px" height="28px">
|
||||
<g id="surface79451713">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 22.703125 15.355469 C 22.628906 15.773438 22.449219 16.152344 22.203125 16.476562 C 22.667969 17.058594 22.851562 17.796875 22.671875 18.554688 C 22.550781 19.082031 22.265625 19.539062 21.886719 19.886719 C 22.15625 20.265625 22.3125 20.71875 22.3125 21.203125 C 22.3125 22.542969 21.121094 23.59375 19.597656 23.59375 L 13.121094 23.613281 C 11.023438 23.613281 9.042969 22.796875 7.558594 21.316406 C 6.070312 19.828125 5.25 17.851562 5.25 15.75 C 5.25 11.828125 6.660156 10.425781 7.59375 9.5 L 7.695312 9.398438 C 7.988281 9.101562 8.527344 8.707031 9.210938 8.203125 C 10.59375 7.179688 12.914062 5.464844 13.339844 4.308594 C 13.683594 3.382812 14.109375 2.230469 15.75 2.230469 C 16.480469 2.230469 17.152344 2.683594 17.550781 3.445312 C 17.929688 4.175781 18.496094 6.132812 16.472656 9.625 L 19.757812 9.625 C 21.167969 9.625 22.3125 10.699219 22.3125 12.015625 C 22.3125 12.4375 22.191406 12.832031 21.984375 13.175781 C 22.574219 13.746094 22.851562 14.519531 22.703125 15.355469 Z M 19.597656 21.84375 C 20.121094 21.84375 20.5625 21.550781 20.5625 21.203125 C 20.5625 20.851562 20.203125 20.5625 19.757812 20.5625 L 17.9375 20.5625 C 17.453125 20.5625 17.0625 20.171875 17.0625 19.6875 C 17.0625 19.203125 17.453125 18.8125 17.9375 18.8125 L 20.226562 18.8125 C 20.609375 18.8125 20.894531 18.472656 20.96875 18.15625 C 21.015625 17.949219 21 17.679688 20.71875 17.441406 C 20.558594 17.476562 20.394531 17.5 20.226562 17.5 L 17.9375 17.5 C 17.453125 17.5 17.0625 17.109375 17.0625 16.625 C 17.0625 16.140625 17.453125 15.75 17.9375 15.75 L 20.226562 15.75 C 20.617188 15.75 20.917969 15.394531 20.980469 15.050781 C 21.046875 14.667969 20.820312 14.417969 20.542969 14.261719 C 20.25 14.355469 19.933594 14.40625 19.597656 14.40625 L 17.945312 14.4375 C 17.46875 14.4375 17.070312 14.054688 17.0625 13.578125 C 17.054688 13.09375 17.4375 12.695312 17.921875 12.6875 L 19.582031 12.65625 C 20.121094 12.65625 20.5625 12.363281 20.5625 12.015625 C 20.5625 11.664062 20.203125 11.375 19.757812 11.375 L 14.875 11.375 C 14.550781 11.375 14.25 11.195312 14.097656 10.902344 C 13.949219 10.613281 13.972656 10.265625 14.15625 10 C 16.121094 7.1875 16.417969 5.234375 16.039062 4.339844 C 15.921875 4.058594 15.777344 3.984375 15.742188 3.976562 C 15.386719 3.976562 15.328125 3.976562 14.984375 4.917969 C 14.375 6.5625 11.992188 8.324219 10.25 9.609375 C 9.675781 10.035156 9.136719 10.433594 8.929688 10.636719 L 8.828125 10.738281 C 8.015625 11.550781 7 12.554688 7 15.75 C 7 17.386719 7.636719 18.921875 8.792969 20.078125 C 9.949219 21.230469 11.488281 21.863281 13.117188 21.863281 Z M 19.597656 21.84375 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 22.703125 15.355469 C 22.628906 15.773438 22.449219 16.152344 22.203125 16.476562 C 22.667969 17.058594 22.851562 17.796875 22.671875 18.554688 C 22.550781 19.082031 22.265625 19.539062 21.886719 19.886719 C 22.15625 20.265625 22.3125 20.71875 22.3125 21.203125 C 22.3125 22.542969 21.121094 23.59375 19.597656 23.59375 L 13.121094 23.613281 C 11.023438 23.613281 9.042969 22.796875 7.558594 21.316406 C 6.070312 19.828125 5.25 17.851562 5.25 15.75 C 5.25 11.828125 6.660156 10.425781 7.59375 9.5 L 7.695312 9.398438 C 7.988281 9.101562 8.527344 8.707031 9.210938 8.203125 C 10.59375 7.179688 12.914062 5.464844 13.339844 4.308594 C 13.683594 3.382812 14.109375 2.230469 15.75 2.230469 C 16.480469 2.230469 17.152344 2.683594 17.550781 3.445312 C 17.929688 4.175781 18.496094 6.132812 16.472656 9.625 L 19.757812 9.625 C 21.167969 9.625 22.3125 10.699219 22.3125 12.015625 C 22.3125 12.4375 22.191406 12.832031 21.984375 13.175781 C 22.574219 13.746094 22.851562 14.519531 22.703125 15.355469 Z M 19.597656 21.84375 C 20.121094 21.84375 20.5625 21.550781 20.5625 21.203125 C 20.5625 20.851562 20.203125 20.5625 19.757812 20.5625 L 17.9375 20.5625 C 17.453125 20.5625 17.0625 20.171875 17.0625 19.6875 C 17.0625 19.203125 17.453125 18.8125 17.9375 18.8125 L 20.226562 18.8125 C 20.609375 18.8125 20.894531 18.472656 20.96875 18.15625 C 21.015625 17.949219 21 17.679688 20.71875 17.441406 C 20.558594 17.476562 20.394531 17.5 20.226562 17.5 L 17.9375 17.5 C 17.453125 17.5 17.0625 17.109375 17.0625 16.625 C 17.0625 16.140625 17.453125 15.75 17.9375 15.75 L 20.226562 15.75 C 20.617188 15.75 20.917969 15.394531 20.980469 15.050781 C 21.046875 14.667969 20.820312 14.417969 20.542969 14.261719 C 20.25 14.355469 19.933594 14.40625 19.597656 14.40625 L 17.945312 14.4375 C 17.46875 14.4375 17.070312 14.054688 17.0625 13.578125 C 17.054688 13.09375 17.4375 12.695312 17.921875 12.6875 L 19.582031 12.65625 C 20.121094 12.65625 20.5625 12.363281 20.5625 12.015625 C 20.5625 11.664062 20.203125 11.375 19.757812 11.375 L 14.875 11.375 C 14.550781 11.375 14.25 11.195312 14.097656 10.902344 C 13.949219 10.613281 13.972656 10.265625 14.15625 10 C 16.121094 7.1875 16.417969 5.234375 16.039062 4.339844 C 15.921875 4.058594 15.777344 3.984375 15.742188 3.976562 C 15.386719 3.976562 15.328125 3.976562 14.984375 4.917969 C 14.375 6.5625 11.992188 8.324219 10.25 9.609375 C 9.675781 10.035156 9.136719 10.433594 8.929688 10.636719 L 8.828125 10.738281 C 8.015625 11.550781 7 12.554688 7 15.75 C 7 17.386719 7.636719 18.921875 8.792969 20.078125 C 9.949219 21.230469 11.488281 21.863281 13.117188 21.863281 Z M 19.597656 21.84375 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.58773 19.4704H5.91726C6.01218 19.4704 6.09749 19.4992 6.17531 19.5695L11.3234 24.1773C11.9323 24.7213 12.4017 24.9747 13.0169 24.9747C13.8724 24.9747 14.5289 24.3395 14.5289 23.4744V5.57171C14.5289 4.70664 13.8724 4.01172 12.9977 4.01172C12.3909 4.01172 11.977 4.28008 11.3234 4.86883L6.17531 9.4361C6.09538 9.5043 6.01218 9.53524 5.91726 9.53524H2.58773C0.869528 9.53524 0 10.4366 0 12.2561V16.7612C0 18.5786 0.879137 19.4704 2.58773 19.4704ZM2.78366 17.4072C2.39647 17.4072 2.20991 17.2185 2.20991 16.8313V12.1839C2.20991 11.7892 2.39647 11.6005 2.78366 11.6005H6.44929C6.76663 11.6005 7.00733 11.5356 7.27686 11.2895L11.9695 7.01803C12.0239 6.96154 12.0825 6.9306 12.1624 6.9306C12.2466 6.9306 12.319 6.99131 12.319 7.09888V21.88C12.319 21.9876 12.2466 22.0621 12.1624 22.0621C12.1017 22.0621 12.0335 22.027 11.9695 21.9705L7.27686 17.7182C7.00733 17.4796 6.76663 17.4072 6.44929 17.4072H2.78366Z" fill="currentColor"/>
|
||||
<path d="M18.5131 19.6804C18.9969 20.0005 19.6489 19.8932 20.0075 19.3792C20.952 18.1218 21.5122 16.3335 21.5122 14.4815C21.5122 12.6295 20.952 10.8508 20.0075 9.57953C19.6489 9.06977 18.9969 8.95071 18.5131 9.28258C17.942 9.65711 17.84 10.3464 18.2877 11.0019C18.9357 11.9226 19.3013 13.1748 19.3013 14.4815C19.3013 15.7881 18.924 17.0287 18.2877 17.961C17.8496 18.6241 17.942 19.2962 18.5131 19.6804Z" fill="currentColor"/>
|
||||
<path d="M23.1864 22.8128C23.7128 23.1479 24.3615 23.0193 24.7264 22.4875C26.2517 20.3322 27.137 17.4477 27.137 14.4815C27.137 11.5152 26.2613 8.61156 24.7264 6.47336C24.3615 5.94368 23.7128 5.81501 23.1864 6.15016C22.6346 6.49797 22.5559 7.18375 22.9611 7.79077C24.2089 9.61023 24.9282 12.0065 24.9282 14.4815C24.9282 16.9565 24.1897 19.3314 22.9611 21.1701C22.5655 21.7771 22.6346 22.465 23.1864 22.8128Z" fill="currentColor"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -6,7 +6,7 @@
|
||||
height: 100%;
|
||||
padding-right: $small;
|
||||
|
||||
@include largePhones {
|
||||
@include allPhones {
|
||||
grid-template-columns: 1fr 9.2rem;
|
||||
}
|
||||
|
||||
@@ -42,11 +42,5 @@
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
}
|
||||
|
||||
.volume-group {
|
||||
@include tablet-portrait {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
|
||||
padding: 0 1rem;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
max-height: 100%;
|
||||
gap: 2rem 1rem;
|
||||
}
|
||||
|
||||
@@ -2,21 +2,31 @@ $g-border: solid 1px $gray5;
|
||||
|
||||
#app-grid {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr 29rem;
|
||||
grid-template-rows: max-content 1fr 5rem;
|
||||
// grid-template-columns: min-content 1fr 29rem;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-template-rows: $navheight 1fr 5rem;
|
||||
grid-template-areas:
|
||||
"l-sidebar nav r-sidebar"
|
||||
"l-sidebar content r-sidebar"
|
||||
"bottombar bottombar bottombar";
|
||||
|
||||
"l-sidebar nav"
|
||||
"l-sidebar content"
|
||||
"bottombar bottombar";
|
||||
height: 100%;
|
||||
border: $g-border;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
|
||||
@include for-desktop-down {
|
||||
grid-template-columns: min-content 1fr 24rem;
|
||||
#contentresizer {
|
||||
margin: 0 $padright 0 $padleft;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"nav"
|
||||
"content"
|
||||
"bottombar";
|
||||
grid-template-rows: auto 1fr auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +37,39 @@ $g-border: solid 1px $gray5;
|
||||
margin-right: $margright;
|
||||
}
|
||||
|
||||
.topnav {
|
||||
grid-area: nav;
|
||||
height: $navheight;
|
||||
padding: 1rem $padleft;
|
||||
padding-right: $padright;
|
||||
|
||||
@include allPhones {
|
||||
gap: $small;
|
||||
height: unset;
|
||||
padding: 6px 8px;
|
||||
margin: $medium 1rem;
|
||||
border-radius: 1rem;
|
||||
border: solid 1px $gray5;
|
||||
background-image: linear-gradient(37deg, rgb(29, 28, 28), transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.b-bar {
|
||||
grid-area: bottombar;
|
||||
border-top: $g-border;
|
||||
}
|
||||
|
||||
.content-page {
|
||||
scrollbar-gutter: stable;
|
||||
padding-left: $padleft;
|
||||
padding-right: $padright;
|
||||
padding-bottom: $padbottom;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
@include allPhones {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.vue-recycle-scroller__item-wrapper {
|
||||
@@ -52,19 +90,96 @@ $g-border: solid 1px $gray5;
|
||||
}
|
||||
}
|
||||
|
||||
.topnav {
|
||||
grid-area: nav;
|
||||
padding: 1rem $padleft;
|
||||
padding-right: $padright;
|
||||
}
|
||||
|
||||
.b-bar {
|
||||
grid-area: bottombar;
|
||||
border-top: $g-border;
|
||||
}
|
||||
|
||||
// ====== MODIFIERS =======
|
||||
|
||||
#app-grid.is_alt_layout {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: max-content 1fr 5rem;
|
||||
grid-template-areas:
|
||||
"nav"
|
||||
"content"
|
||||
"bottombar";
|
||||
|
||||
@include allPhones {
|
||||
grid-template-columns: 1fr !important;
|
||||
grid-template-rows: max-content 1fr 9.5rem !important;
|
||||
grid-template-areas:
|
||||
"nav"
|
||||
"content"
|
||||
"bottombar" !important;
|
||||
}
|
||||
|
||||
.vue-recycle-scroller,
|
||||
.content-page,
|
||||
.topnav,
|
||||
#songlist-scroller {
|
||||
padding-left: $alt_layout_pad;
|
||||
padding-right: $alt_layout_pad;
|
||||
}
|
||||
|
||||
.b-bar,
|
||||
.search-page-top-results {
|
||||
padding: 0 $alt_layout_pad;
|
||||
}
|
||||
|
||||
#contentresizer {
|
||||
margin: 0 $alt_layout_pad;
|
||||
}
|
||||
|
||||
.topnav {
|
||||
background-color: $gray;
|
||||
}
|
||||
|
||||
.vue-recycle-scroller,
|
||||
.content-page {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.search-page-top-results {
|
||||
padding-bottom: $padbottom;
|
||||
}
|
||||
|
||||
.search-view .buttons-area {
|
||||
padding-left: $alt_layout_pad;
|
||||
}
|
||||
|
||||
.lyricsview {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
#lyricscontent {
|
||||
padding-top: 0;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1980px) {
|
||||
// NOTE: Styles for 1680px and below
|
||||
$alt_layout_pad: max(2rem, calc((100% - 1680px) / 2));
|
||||
|
||||
.vue-recycle-scroller,
|
||||
.content-page,
|
||||
.topnav,
|
||||
#songlist-scroller {
|
||||
padding-left: $alt_layout_pad;
|
||||
padding-right: $alt_layout_pad;
|
||||
}
|
||||
|
||||
#contentresizer {
|
||||
margin: 0 $alt_layout_pad;
|
||||
}
|
||||
|
||||
.search-view .buttons-area {
|
||||
padding-left: $alt_layout_pad;
|
||||
}
|
||||
|
||||
.b-bar,
|
||||
.search-page-top-results {
|
||||
padding: 0 $alt_layout_pad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#app-grid.extendWidth {
|
||||
padding-right: 0;
|
||||
border-left: none;
|
||||
@@ -72,31 +187,26 @@ $g-border: solid 1px $gray5;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
#app-grid.noSidebar {
|
||||
grid-template-columns: min-content 1fr;
|
||||
#app-grid.useSidebar {
|
||||
grid-template-columns: min-content 1fr 28rem;
|
||||
grid-template-areas:
|
||||
"l-sidebar nav"
|
||||
"l-sidebar content"
|
||||
"bottombar bottombar";
|
||||
"l-sidebar nav r-sidebar"
|
||||
"l-sidebar content r-sidebar"
|
||||
"bottombar bottombar bottombar";
|
||||
|
||||
#acontent {
|
||||
margin-right: 0 !important;
|
||||
// padding-right: $medium !important;
|
||||
@include for-desktop-down {
|
||||
grid-template-columns: min-content 1fr 24rem;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: max-content 1fr 9.5rem;
|
||||
grid-template-areas:
|
||||
"nav"
|
||||
"content"
|
||||
"bottombar";
|
||||
#acontent {
|
||||
// margin-right: 0 !important;
|
||||
// padding-right: $medium !important;
|
||||
}
|
||||
}
|
||||
|
||||
#app-grid.NoSideBorders {
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-right: none !important;
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.v-scroll-page {
|
||||
@@ -106,12 +216,27 @@ $g-border: solid 1px $gray5;
|
||||
width: 100%;
|
||||
padding-bottom: $content-padding-bottom;
|
||||
padding-bottom: $padbottom;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
@include allPhones {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.song-title > .isSmallArtists {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.isSmall {
|
||||
.songlist-item {
|
||||
grid-template-columns: 2fr 5.5rem !important;
|
||||
|
||||
@include mediumPhones {
|
||||
grid-template-columns: 2fr 2.5rem !important;
|
||||
gap: $small !important;
|
||||
}
|
||||
}
|
||||
|
||||
.song-artists,
|
||||
@@ -120,7 +245,9 @@ $g-border: solid 1px $gray5;
|
||||
}
|
||||
|
||||
.isSmallArtists {
|
||||
display: unset !important;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: small;
|
||||
color: $white;
|
||||
opacity: 0.67;
|
||||
|
||||
@@ -28,8 +28,8 @@ input[type="search"]::-webkit-search-cancel-button {
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -72,18 +72,22 @@ a {
|
||||
|
||||
// BUTTONS
|
||||
button {
|
||||
border: none;
|
||||
font-family: "SF Compact Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 0.9rem !important;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: inherit;
|
||||
border-radius: $small;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 2.25rem;
|
||||
padding: 0 $small;
|
||||
transition: background-color 0.2s ease-out, color 0.2s ease-out, border 0.2s ease-out;
|
||||
|
||||
background-color: $gray4;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
@@ -202,7 +206,7 @@ button {
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10.1rem, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax($cardwidth, 1fr));
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
flex-direction: row;
|
||||
@@ -222,7 +226,7 @@ button {
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
color: $purple;
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
margin: $smaller 0;
|
||||
|
||||
&.album {
|
||||
@@ -240,4 +244,23 @@ button {
|
||||
&.playlist {
|
||||
color: $green;
|
||||
}
|
||||
}
|
||||
|
||||
// Badges used in settings
|
||||
.badge {
|
||||
margin-left: $small;
|
||||
opacity: 0.75;
|
||||
padding: 0 $smaller;
|
||||
border-radius: $smaller;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.experimental {
|
||||
border: solid 1px $yellow;
|
||||
color: $yellow;
|
||||
}
|
||||
|
||||
.badge.new {
|
||||
background-color: $blue;
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
@import "./app-grid.scss", "./controls.scss", "./inputs.scss",
|
||||
"./scrollbars.scss", "./state.scss", "./variants.scss", "./basic.scss",
|
||||
"./search-tabheaders.scss", "./album-grid.scss";
|
||||
@import "./app-grid.scss", "./controls.scss", "./inputs.scss", "./scrollbars.scss", "./state.scss", "./variants.scss",
|
||||
"./basic.scss", "./search-tabheaders.scss", "./album-grid.scss";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@@ -13,6 +12,8 @@
|
||||
html {
|
||||
cursor: default !important;
|
||||
overflow: hidden;
|
||||
color: $white;
|
||||
background-color: $body;
|
||||
|
||||
& > * {
|
||||
overflow: visible !important;
|
||||
@@ -28,20 +29,60 @@ html.loading * {
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: $body;
|
||||
color: $white;
|
||||
font-family: "SFCompactDisplay", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
||||
"Segoe UI Symbol";
|
||||
font-family: "SF Compact Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
color: $white;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
background-color: $body;
|
||||
color-scheme: dark;
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.noSelect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.dimmer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
.dimmer {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
background-color: rgb(0, 0, 0, 0.5);
|
||||
transition: opacity 300ms ease, visibility 300ms ease;
|
||||
}
|
||||
|
||||
.dimmer.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,7 @@ input[type="number"] {
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
font-family: "SF Compact Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
height: 2.25rem !important;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,58 @@
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
/* Total width */
|
||||
.designatedOS ::-webkit-scrollbar {
|
||||
background-color: $body;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-radius: 1rem;
|
||||
/* Background of the scrollbar except button or resizer */
|
||||
.designatedOS ::-webkit-scrollbar-track {
|
||||
background-color: $body;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $gray1;
|
||||
border-radius: 1rem;
|
||||
/* Scrollbar itself */
|
||||
.designatedOS ::-webkit-scrollbar-thumb {
|
||||
background-color: $gray2;
|
||||
border-radius: 16px;
|
||||
border: 4px solid $body;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: $blue;
|
||||
.designatedOS ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: $gray1;
|
||||
}
|
||||
|
||||
/* Custom scrollbar version for dropdowns etc */
|
||||
/* Context dropdown menus */
|
||||
.designatedOS .context-item .children > .wrapper::-webkit-scrollbar {
|
||||
width: 6px !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.designatedOS .context-item .children > .wrapper::-webkit-scrollbar-track {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.designatedOS .context-item .children > .wrapper::-webkit-scrollbar-thumb {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.designatedOS .context-item .children > .wrapper::-webkit-scrollbar-thumb:hover {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Scrollable divs */
|
||||
.designatedOS .scrollable::-webkit-scrollbar {
|
||||
width: 6px !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.designatedOS .scrollable::-webkit-scrollbar-track {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.designatedOS .scrollable::-webkit-scrollbar-thumb {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.designatedOS .scrollable::-webkit-scrollbar-thumb:hover {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
background-color: $gray5;
|
||||
border: none;
|
||||
|
||||
transition: all 0.3s ease;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
padding: 0 $medium;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.activetab {
|
||||
|
||||
@@ -1,16 +1,61 @@
|
||||
|
||||
.now-playing-track-indicator {
|
||||
background-image: url(../../assets/icons/playing.gif);
|
||||
height: 2rem;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
background-color: $white;
|
||||
background-size: 1.5rem !important;
|
||||
transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#wave {
|
||||
fill: #000000;
|
||||
width: 28px;
|
||||
height: auto;
|
||||
|
||||
@for $i from 1 through 9 {
|
||||
#Line_#{$i} {
|
||||
animation: pulse 0.4s infinite;
|
||||
animation-delay: $i * 0.12s;
|
||||
transform: scaleY(0.8);
|
||||
transform-origin: center;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scaleY(0.8);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scaleY(0.6);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scaleY(0.8);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.last_played {
|
||||
background-image: url(../../assets/icons/playing.webp);
|
||||
transition: all 0.3s ease-in-out;
|
||||
.now-playing-track-indicator.last_played {
|
||||
#wave {
|
||||
@for $i from 1 through 9 {
|
||||
#Line_#{$i} {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
|
||||
@@ -5,51 +5,47 @@
|
||||
}
|
||||
|
||||
// media query mixins
|
||||
@mixin smallPhone {
|
||||
@media (max-width: 550px) {
|
||||
@mixin smallestPhones {
|
||||
@media only screen and (max-width: 320px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin iphoneSE {
|
||||
@media (max-width: 386px) {
|
||||
@mixin smallerPhones {
|
||||
@media only screen and (max-width: 360px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin allPhones {
|
||||
@media (max-width: 900px) {
|
||||
@mixin smallPhones {
|
||||
@media only screen and (max-width: 420px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/* This query is "synced" with content-width.ts values */
|
||||
@mixin mediumPhones {
|
||||
@media only screen and (max-width: 460px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/* This query is "synced" with content-width.ts values */
|
||||
@mixin largePhones {
|
||||
// 550px <= width < 900px
|
||||
@media (min-width: 550px) {
|
||||
@media only screen and (max-width: 660px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin tablet-landscape {
|
||||
@media (max-width: 1080px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin tablet-portrait {
|
||||
@media (max-width: 810px) {
|
||||
/* This query is "synced" with content-width.ts and index.ts values */
|
||||
@mixin allPhones {
|
||||
@media only screen and (max-width: 900px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-desktop-down {
|
||||
@media (max-width: 1600px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-desktop-up {
|
||||
@media (min-width: 1800px) {
|
||||
@media only screen and (max-width: 1600px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ $theme: #464545fd;
|
||||
// sizes
|
||||
$small: 0.5rem;
|
||||
$smaller: 0.25rem;
|
||||
$smallest: 0.15rem;
|
||||
$medium: 0.75rem;
|
||||
$large: 1.5rem;
|
||||
$larger: 2rem;
|
||||
@@ -56,15 +57,25 @@ $overshoot: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
|
||||
$separator: $gray4;
|
||||
|
||||
$maxwidth: 1372px;
|
||||
$margright: 0;
|
||||
$padbottom: 4rem;
|
||||
$maxwidth: 1438px;
|
||||
$navheight: 4.75rem;
|
||||
$cardwidth: 10.75rem;
|
||||
$maxpadleft: 5rem;
|
||||
|
||||
$alt_layout_pad: max(2rem, calc((100% - 1280px) / 2));
|
||||
|
||||
$maxpadright: calc(100% - $maxwidth);
|
||||
|
||||
$padbottom: 4rem;
|
||||
$padleft: clamp(2rem, $maxpadright, $maxpadleft);
|
||||
$padright: clamp(
|
||||
2rem,
|
||||
max($maxpadright, 5rem),
|
||||
calc($maxpadright + $maxpadleft)
|
||||
);
|
||||
|
||||
// 👇 fixed width with content floating to the left.
|
||||
// $padright: clamp(
|
||||
// 1rem,
|
||||
// max($maxpadright, 5rem),
|
||||
// calc($maxpadright + $maxpadleft)
|
||||
// );
|
||||
$padright: $padleft;
|
||||
|
||||
$margright: calc(0rem - $padright);
|
||||
|
||||
@@ -1,15 +1,43 @@
|
||||
@import
|
||||
"./mixins.scss",
|
||||
"./variables",
|
||||
"./ProgressBar.scss",
|
||||
"./BottomBar/BottomBar.scss",
|
||||
"./Global"
|
||||
;
|
||||
|
||||
@import "./mixins.scss", "./variables", "./ProgressBar.scss", "./BottomBar/BottomBar.scss", "./Global";
|
||||
|
||||
/* San Francisco Compact Display font */
|
||||
@font-face {
|
||||
font-family: "SFCompactDisplay";
|
||||
src: url("../sf-compact.woff") format("woff");
|
||||
font-display: swap;
|
||||
font-family: "SF Compact Display";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("../fonts/SFCompactDisplay-Regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "SF Compact Display";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("../fonts/SFCompactDisplay-Medium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "SF Compact Display";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("../fonts/SFCompactDisplay-Semibold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "SF Compact Display";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("../fonts/SFCompactDisplay-Bold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/* San Francisco Mono font */
|
||||
@font-face {
|
||||
font-family: "SF Mono";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("../fonts/sf-mono-medium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="album_disc.is_album_disc_number"
|
||||
class="album_disc_header no-select"
|
||||
>
|
||||
<div v-if="album_disc.is_album_disc_number" class="album_disc_header no-select">
|
||||
<div class="disc_number">Disc {{ album_disc.album_page_disc_number }}</div>
|
||||
<div></div>
|
||||
</div>
|
||||
@@ -22,11 +19,17 @@ defineProps<{
|
||||
grid-template-columns: 1fr max-content;
|
||||
align-items: center;
|
||||
padding-left: 1rem;
|
||||
margin-top: $small;
|
||||
height: $song-item-height;
|
||||
|
||||
.disc_number {
|
||||
font-size: $medium;
|
||||
font-weight: 500;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
padding-left: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import useAlbumStore from "@/stores/pages/album";
|
||||
import useArtistStore from "@/stores/pages/artist";
|
||||
import { computed, onMounted } from "vue";
|
||||
|
||||
import { getShift } from "@/utils/colortools/shift";
|
||||
|
||||
@@ -74,6 +74,7 @@ onMounted(hookAction);
|
||||
gap: 1rem;
|
||||
padding-right: $medium;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.genre-pill {
|
||||
@@ -82,6 +83,7 @@ onMounted(hookAction);
|
||||
text-align: center;
|
||||
padding: $small 1rem;
|
||||
font-weight: 700;
|
||||
transition: background-color 0.2s ease-out, color 0.2s ease-out;
|
||||
|
||||
&:first-child {
|
||||
background-color: white;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
import { computed } from "vue";
|
||||
|
||||
import { Album } from "@/interfaces";
|
||||
import { formatSeconds } from "@/utils";
|
||||
import { isSmallPhone } from "@/stores/content-width";
|
||||
import { formatSeconds } from "@/utils";
|
||||
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
|
||||
@@ -33,18 +33,14 @@ const statsText = computed(() => {
|
||||
|
||||
// hide track count if it's a single, also add an s to track if it's plural
|
||||
return `• ${props.album.date} ${
|
||||
!is_single
|
||||
? `• ${props.album.count.toLocaleString()} Track${
|
||||
props.album.count > 1 ? "s" : ""
|
||||
}`
|
||||
: ""
|
||||
!is_single ? `• ${props.album.count.toLocaleString()} Track${props.album.count > 1 ? "s" : ""}` : ""
|
||||
} • ${formatSeconds(props.album.duration, true)}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.album-stats {
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 14px;
|
||||
padding-left: $smaller;
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
background: colors.bg ? colors.bg : '',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="big-img no-scroll"
|
||||
:class="`${isHeaderSmall ? 'imgSmall' : ''} shadow-lg rounded-sm`"
|
||||
>
|
||||
<div class="big-img no-scroll" :class="`${isHeaderSmall ? 'imgSmall' : ''} shadow-lg rounded-sm`">
|
||||
<img :src="imguri.thumb.large + album.image" class="rounded-sm" />
|
||||
</div>
|
||||
<Info />
|
||||
@@ -24,8 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref } from "vue";
|
||||
|
||||
import { paths } from "@/config";
|
||||
import { isHeaderSmall } from "@/stores/content-width";
|
||||
@@ -111,7 +108,7 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
}
|
||||
}
|
||||
|
||||
@include smallPhone {
|
||||
@include largePhones {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 2rem 1rem;
|
||||
height: 25rem;
|
||||
|
||||
@@ -14,6 +14,7 @@ const props = defineProps<{
|
||||
show_text?: boolean;
|
||||
fetch_callback: () => Promise<void>;
|
||||
reset_callback?: () => Promise<void>;
|
||||
outside_route?: boolean;
|
||||
}>();
|
||||
|
||||
const update = async () => {
|
||||
@@ -26,8 +27,9 @@ onMounted(async () => {
|
||||
props.fetch_callback().then(update);
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate(() => {
|
||||
if (!props.reset_callback) return;
|
||||
props.reset_callback().then(update);
|
||||
});
|
||||
!props.outside_route &&
|
||||
onBeforeRouteUpdate(() => {
|
||||
if (!props.reset_callback) return;
|
||||
props.reset_callback().then(update);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
:class="{ isSmallPhone }"
|
||||
style="height: 100%; width: 100%"
|
||||
:style="{
|
||||
boxShadow: !useCircularImage
|
||||
? colors.bg.length
|
||||
? `0 .5rem 2rem ${colors.bg}`
|
||||
: undefined
|
||||
: undefined,
|
||||
boxShadow: !useCircularImage ? (colors.bg.length ? `0 .5rem 2rem ${colors.bg}` : undefined) : undefined,
|
||||
}"
|
||||
></div>
|
||||
<div
|
||||
@@ -27,18 +23,16 @@
|
||||
height: containerHeight,
|
||||
}"
|
||||
>
|
||||
<img
|
||||
id="artist-avatar"
|
||||
:src="paths.images.artist.large + artist.image"
|
||||
@load="store.setBgColor"
|
||||
/>
|
||||
<img id="artist-avatar" :src="paths.images.artist.large + artist.image" @load="store.setBgColor" />
|
||||
</div>
|
||||
<div
|
||||
v-if="!useCircularImage"
|
||||
class="gradient"
|
||||
:style="{
|
||||
backgroundImage: colors.bg
|
||||
? `linear-gradient(${gradientDirection}, transparent 20%,
|
||||
? `linear-gradient(${gradientDirection}, transparent ${
|
||||
isSmall ? 60 : gradientTransparentWidth - (width < 700 ? 40 : width < 900 ? 20 : 10)
|
||||
}%,
|
||||
${colors.bg} ${gradientWidth}%,
|
||||
${colors.bg} 100%)`
|
||||
: '',
|
||||
@@ -48,19 +42,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { onBeforeRouteUpdate } from "vue-router";
|
||||
import { useElementSize } from "@vueuse/core";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
import { useElementSize } from "@vueuse/core";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { Ref, computed, onMounted, ref } from "vue";
|
||||
import { onBeforeRouteUpdate } from "vue-router";
|
||||
|
||||
import { paths } from "@/config";
|
||||
import updatePageTitle from "@/utils/updatePageTitle";
|
||||
|
||||
import Info from "./HeaderComponents/Info.vue";
|
||||
import { isSmall } from "@/stores/content-width";
|
||||
import useArtistStore from "@/stores/pages/artist";
|
||||
import { getShift } from "@/utils/colortools/shift";
|
||||
import Info from "./HeaderComponents/Info.vue";
|
||||
|
||||
const image_width_px = 450;
|
||||
const store = useArtistStore();
|
||||
const settings = useSettingsStore();
|
||||
|
||||
@@ -77,21 +72,18 @@ function updateTitle() {
|
||||
onMounted(() => updateTitle());
|
||||
onBeforeRouteUpdate(() => updateTitle());
|
||||
|
||||
const artistheader = ref(null);
|
||||
const artistheader: Ref<HTMLElement | null> = ref(null);
|
||||
const { width } = useElementSize(artistheader);
|
||||
|
||||
const isSmallPhone = computed(() => width.value <= 550);
|
||||
const useCircularImage = computed(
|
||||
() =>
|
||||
!isSmallPhone.value && (settings.useCircularArtistImg || width.value >= 995)
|
||||
);
|
||||
const gradientTransparentWidth = computed(() => Math.floor((image_width_px / (width.value || 1)) * 100));
|
||||
|
||||
const gradientDirection = computed(() =>
|
||||
isSmallPhone.value ? "210deg" : "to left"
|
||||
);
|
||||
const isSmallPhone = computed(() => width.value <= 660);
|
||||
const useCircularImage = computed(() => !isSmallPhone.value && settings.useCircularArtistImg);
|
||||
|
||||
const gradientDirection = computed(() => (isSmallPhone.value ? "210deg" : "to left"));
|
||||
|
||||
const gradientWidth = computed(() => {
|
||||
return isSmallPhone.value ? "80" : "50";
|
||||
return isSmallPhone.value ? "80" : Math.min(gradientTransparentWidth.value, 50);
|
||||
});
|
||||
|
||||
const containerHeight = computed(() => {
|
||||
@@ -109,7 +101,7 @@ const containerHeight = computed(() => {
|
||||
|
||||
.artist-page-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr minmax(min-content, 50%);
|
||||
grid-template-columns: 1fr 450px;
|
||||
position: relative;
|
||||
|
||||
.artist-img {
|
||||
@@ -123,7 +115,6 @@ const containerHeight = computed(() => {
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
object-position: 0% 20%;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,22 +137,12 @@ const containerHeight = computed(() => {
|
||||
|
||||
.gradient {
|
||||
position: absolute;
|
||||
background-image: linear-gradient(
|
||||
to left,
|
||||
transparent 10%,
|
||||
$gray 50%,
|
||||
$gray 100%
|
||||
);
|
||||
background-image: linear-gradient(to left, transparent 10%, $gray 50%, $gray 100%);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.isSmallPhone {
|
||||
background-image: linear-gradient(
|
||||
210deg,
|
||||
transparent 20%,
|
||||
$gray 80%,
|
||||
$gray 100%
|
||||
);
|
||||
background-image: linear-gradient(210deg, transparent 20%, $gray 80%, $gray 100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,32 +2,21 @@
|
||||
<div
|
||||
class="artist-info"
|
||||
:style="{
|
||||
color: !useCircularImage
|
||||
? artist.colors[0]
|
||||
? getTextColor(artist.colors[0])
|
||||
: undefined
|
||||
: undefined,
|
||||
color: !useCircularImage ? (artist.colors[0] ? getTextColor(artist.colors[0]) : undefined) : undefined,
|
||||
}"
|
||||
>
|
||||
<section class="text">
|
||||
<div class="card-title">Artist</div>
|
||||
<div
|
||||
class="artist-name"
|
||||
:class="`${useCircularImage ? 'ellip' : 'ellip2'}`"
|
||||
>
|
||||
<div class="artist-name" :class="`${useCircularImage ? 'ellip' : 'ellip2'}`" :title="artist.name">
|
||||
{{ artist.name }}
|
||||
</div>
|
||||
<div class="stats">
|
||||
<span v-if="artist.trackcount">
|
||||
{{ artist.trackcount.toLocaleString() }} Track{{
|
||||
`${artist.trackcount == 1 ? "" : "s"}`
|
||||
}}
|
||||
{{ artist.trackcount.toLocaleString() }} Track{{ `${artist.trackcount == 1 ? "" : "s"}` }}
|
||||
</span>
|
||||
{{ artist.albumcount && artist.trackcount.toLocaleString() ? "•" : "" }}
|
||||
<span v-if="artist.albumcount">
|
||||
{{ artist.albumcount.toLocaleString() }} Album{{
|
||||
`${artist.albumcount == 1 ? "" : "s"}`
|
||||
}}
|
||||
{{ artist.albumcount.toLocaleString() }} Album{{ `${artist.albumcount == 1 ? "" : "s"}` }}
|
||||
</span>
|
||||
<span v-if="artist.duration">
|
||||
{{ ` • ${formatSeconds(artist.duration, true)}` }}
|
||||
@@ -41,9 +30,9 @@
|
||||
<script setup lang="ts">
|
||||
import { getTextColor } from "@/utils/colortools/shift";
|
||||
|
||||
import { Artist } from "@/interfaces";
|
||||
import formatSeconds from "@/utils/useFormatSeconds";
|
||||
import Buttons from "./Buttons.vue";
|
||||
import { Artist } from "@/interfaces";
|
||||
|
||||
defineProps<{
|
||||
artist: Artist;
|
||||
@@ -76,8 +65,9 @@ defineProps<{
|
||||
|
||||
.artist-name {
|
||||
font-size: 3.5rem;
|
||||
font-weight: bold;
|
||||
word-wrap: break-word;
|
||||
font-weight: 700;
|
||||
word-wrap: break-all;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
v-for="(song, index) in tracks"
|
||||
:key="index"
|
||||
:track="song"
|
||||
:index="index + 1"
|
||||
:index="total ? total - index : index + 1"
|
||||
:source="source"
|
||||
@playThis="playHandler(index)"
|
||||
/>
|
||||
@@ -31,6 +31,7 @@ defineProps<{
|
||||
title: string;
|
||||
playHandler: (index: number) => void;
|
||||
source: dropSources;
|
||||
total?: number;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
@@ -52,6 +53,11 @@ defineProps<{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-left: 1rem !important; // applies to favorite page
|
||||
padding-right: $small;
|
||||
|
||||
@include largePhones {
|
||||
padding-left: $small !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
<template>
|
||||
<div class="b-bar">
|
||||
<div
|
||||
class="b-bar"
|
||||
:style="{
|
||||
paddingLeft: `${settings.is_default_layout ? '1rem' : ''}`,
|
||||
paddingRight: `${settings.is_default_layout ? '1rem' : ''}`,
|
||||
}"
|
||||
>
|
||||
<LeftGroup @handleFav="handleFav" />
|
||||
<div class="center">
|
||||
<div v-if="!isMobile" class="with-time">
|
||||
<div class="time time-current">
|
||||
<span>
|
||||
<div class="numbers">
|
||||
{{ formatSeconds(queue.duration.current || 0) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons rounded-sm border">
|
||||
<HotKeys />
|
||||
</div>
|
||||
<div class="time time-full">
|
||||
<span>
|
||||
<div class="numbers">
|
||||
{{ formatSeconds(queue.duration.full) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Progress />
|
||||
@@ -32,6 +38,7 @@ import { isMobile } from "@/stores/content-width";
|
||||
import { formatSeconds } from "@/utils";
|
||||
|
||||
import useQStore from "@/stores/queue";
|
||||
import useSettings from "@/stores/settings";
|
||||
|
||||
import HotKeys from "@/components/LeftSidebar/NP/HotKeys.vue";
|
||||
import Progress from "@/components/LeftSidebar/NP/Progress.vue";
|
||||
@@ -41,6 +48,7 @@ import LeftGroup from "./Left.vue";
|
||||
import RightGroup from "./Right.vue";
|
||||
|
||||
const queue = useQStore();
|
||||
const settings = useSettings();
|
||||
|
||||
function handleFav() {
|
||||
favoriteHandler(
|
||||
@@ -60,15 +68,32 @@ function handleFav() {
|
||||
grid-template-columns: 1fr max-content 1fr;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
padding: 0 1rem;
|
||||
|
||||
@include allPhones {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: max-content 1.5rem max-content;
|
||||
padding: 0 1rem $medium 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: unset;
|
||||
gap: $small;
|
||||
padding: $medium 1rem;
|
||||
|
||||
.center > input {
|
||||
height: 2px !important;
|
||||
/* Hiding the dot/thumb/handle for readonly input */
|
||||
/* Webkit browsers, Firefox, IE etc */
|
||||
&:hover > .center > #progress::-webkit-slider-thumb {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover > .center > #progress::-moz-range-thumb {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover > .center > #progress::-ms-thumb {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +101,7 @@ function handleFav() {
|
||||
background: transparent;
|
||||
border-radius: $small;
|
||||
width: 3rem;
|
||||
transition: background-color 0.2s ease-out, border-color 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
border: solid 1px $gray3 !important;
|
||||
@@ -85,9 +111,33 @@ function handleFav() {
|
||||
@include allPhones {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallestPhones {
|
||||
&:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-left: $smaller;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
// INFO: Show the progress bar when hovering over the bottom bar
|
||||
#progress::-moz-range-thumb {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
@@ -102,6 +152,11 @@ function handleFav() {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
// INFO: Also show the expand button
|
||||
.np-image .expandicon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.with-time {
|
||||
@@ -119,33 +174,37 @@ function handleFav() {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
margin-bottom: -$small;
|
||||
margin-bottom: -$smallest;
|
||||
|
||||
width: 30rem;
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
@media only screen and (max-width: 1080px) {
|
||||
width: 20rem !important;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
width: 100% !important;
|
||||
margin-bottom: $small;
|
||||
}
|
||||
margin: 4px -16px;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
|
||||
.time {
|
||||
font-size: $medium;
|
||||
height: fit-content;
|
||||
width: 3rem;
|
||||
|
||||
span {
|
||||
background-color: $gray3;
|
||||
border-radius: $smaller;
|
||||
padding: 0 $smaller;
|
||||
> #progress {
|
||||
height: 1px !important;
|
||||
width: 100vw !important;
|
||||
margin: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.time-full {
|
||||
text-align: end;
|
||||
.time {
|
||||
font-weight: 500;
|
||||
font-size: $medium;
|
||||
|
||||
.numbers {
|
||||
background-color: $gray3;
|
||||
border-radius: $smaller;
|
||||
padding: 1px $smaller;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
},
|
||||
replace: true,
|
||||
}"
|
||||
class="np-image rounded-sm no-scroll"
|
||||
>
|
||||
<img
|
||||
class="rounded-sm"
|
||||
:src="paths.images.thumb.small + queue.currenttrack?.image"
|
||||
alt=""
|
||||
/>
|
||||
<img :src="paths.images.thumb.small + queue.currenttrack?.image" alt="" />
|
||||
<div class="expandicon">
|
||||
<ExpandSvg />
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div
|
||||
class="track-info"
|
||||
@@ -36,9 +36,7 @@
|
||||
</div>
|
||||
<ArtistName
|
||||
:artists="queue.currenttrack?.artists || []"
|
||||
:albumartists="
|
||||
queue.currenttrack?.albumartists || 'Welcome to Swing Music'
|
||||
"
|
||||
:albumartists="queue.currenttrack?.albumartists || 'Welcome to Swing Music'"
|
||||
class="artist"
|
||||
/>
|
||||
</div>
|
||||
@@ -52,16 +50,17 @@ import { paths } from "@/config";
|
||||
import { Routes } from "@/router";
|
||||
import { getShift } from "@/utils/colortools/shift";
|
||||
|
||||
import useQStore from "@/stores/queue";
|
||||
import useColorStore from "@/stores/colors";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
import { isLargerMobile, isMobile } from "@/stores/content-width";
|
||||
import useQStore from "@/stores/queue";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
|
||||
import Actions from "./Right.vue";
|
||||
import ExpandSvg from "@/assets/icons/expand.svg";
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
import HotKeys from "../LeftSidebar/NP/HotKeys.vue";
|
||||
import HeartSvg from "../shared/HeartSvg.vue";
|
||||
import MasterFlag from "../shared/MasterFlag.vue";
|
||||
import HotKeys from "../LeftSidebar/NP/HotKeys.vue";
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
import Actions from "./Right.vue";
|
||||
|
||||
const queue = useQStore();
|
||||
const settings = useSettingsStore();
|
||||
@@ -76,16 +75,55 @@ defineEmits<{
|
||||
.left-group {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: $small;
|
||||
gap: $medium;
|
||||
align-items: center;
|
||||
font-size: small;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
margin-right: $medium;
|
||||
|
||||
a {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
img {
|
||||
.np-image {
|
||||
position: relative;
|
||||
height: 3rem;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.expandicon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(51, 51, 51, 0.575);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
|
||||
svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.expandicon {
|
||||
transform: translateY(-$medium);
|
||||
height: 130%;
|
||||
}
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
flex-shrink: 0;
|
||||
margin-right: $medium;
|
||||
}
|
||||
|
||||
@include smallerPhones {
|
||||
margin-right: $small;
|
||||
}
|
||||
}
|
||||
|
||||
.heart-button {
|
||||
@@ -96,32 +134,45 @@ defineEmits<{
|
||||
}
|
||||
|
||||
.track-info {
|
||||
.artistname {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.75;
|
||||
font-weight: bold;
|
||||
.title {
|
||||
color: $white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: $white;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.artistname {
|
||||
opacity: 0.75;
|
||||
|
||||
a {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
width: calc(100% + 8px);
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
width: unset;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
height: 4rem;
|
||||
grid-template-columns: max-content 1fr max-content;
|
||||
grid-template-columns: max-content 1fr max-content max-content;
|
||||
margin-right: unset;
|
||||
|
||||
.heart-button {
|
||||
height: max-content;
|
||||
border: none !important;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 550px) {
|
||||
grid-template-columns: max-content 1fr max-content max-content;
|
||||
@include largePhones {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
max-width: calc(100% - 8px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
<template>
|
||||
<div class="right-group">
|
||||
<LyricsButton v-if="settings.use_lyrics_plugin || lyrics.exists" />
|
||||
<LyricsButton />
|
||||
<Volume />
|
||||
<button
|
||||
class="repeat"
|
||||
:class="{ 'repeat-disabled': settings.no_repeat }"
|
||||
:title="
|
||||
settings.repeat_all
|
||||
? 'Repeat all'
|
||||
: settings.no_repeat
|
||||
? 'No repeat'
|
||||
: 'Repeat one'
|
||||
"
|
||||
:title="settings.repeat_all ? 'Repeat all' : settings.no_repeat ? 'No repeat' : 'Repeat one'"
|
||||
@click="settings.toggleRepeatMode"
|
||||
>
|
||||
<RepeatOneSvg v-if="settings.repeat_one" />
|
||||
<RepeatAllSvg v-else />
|
||||
</button>
|
||||
<button title="Shuffle" @click="queue.shuffleQueue">
|
||||
<ShuffleSvg />
|
||||
</button>
|
||||
<HeartSvg
|
||||
v-if="!hideHeart"
|
||||
title="Favorite"
|
||||
:state="queue.currenttrack?.is_favorite"
|
||||
@handleFav="() => $emit('handleFav')"
|
||||
/>
|
||||
@@ -28,16 +26,15 @@
|
||||
<script setup lang="ts">
|
||||
import useQueue from "@/stores/queue";
|
||||
import useSettings from "@/stores/settings";
|
||||
import useLyrics from "@/stores/lyrics";
|
||||
|
||||
import HeartSvg from "../shared/HeartSvg.vue";
|
||||
import RepeatAllSvg from "@/assets/icons/repeat.svg";
|
||||
import RepeatOneSvg from "@/assets/icons/repeat-one.svg";
|
||||
import Volume from "./Volume.vue";
|
||||
import RepeatAllSvg from "@/assets/icons/repeat.svg";
|
||||
import ShuffleSvg from "@/assets/icons/shuffle.svg";
|
||||
import HeartSvg from "../shared/HeartSvg.vue";
|
||||
import LyricsButton from "../shared/LyricsButton.vue";
|
||||
import Volume from "./Volume.vue";
|
||||
|
||||
const queue = useQueue();
|
||||
const lyrics = useLyrics();
|
||||
const settings = useSettings();
|
||||
|
||||
defineProps<{
|
||||
@@ -53,12 +50,13 @@ defineEmits<{
|
||||
.right-group {
|
||||
display: grid;
|
||||
justify-content: flex-end;
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
grid-template-columns: repeat(5, max-content);
|
||||
align-items: center;
|
||||
height: 4rem;
|
||||
|
||||
@include allPhones {
|
||||
width: max-content;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
button {
|
||||
@@ -85,5 +83,9 @@ defineEmits<{
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
.heart-button {
|
||||
border: solid 1px $gray4 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,16 +15,20 @@
|
||||
step="0.01"
|
||||
:value="settings.volume"
|
||||
@input="changeVolume"
|
||||
:style="{
|
||||
backgroundSize: `${(settings.volume / 1) * 100}% 100%`,
|
||||
}"
|
||||
/>
|
||||
<div className="volume_indicator">{{ ((settings.volume / 1) * 100).toFixed(0) }}</div>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
import VolumeMuteSvg from "@/assets/icons/volume-mute.svg";
|
||||
import VolumeLowSvg from "@/assets/icons/volume-low.svg";
|
||||
import VolumeMidSvg from "@/assets/icons/volume-mid.svg";
|
||||
import VolumeMuteSvg from "@/assets/icons/volume-mute.svg";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
|
||||
const settings = useSettingsStore();
|
||||
|
||||
@@ -34,7 +38,6 @@ const changeVolume = (event: Event) => {
|
||||
};
|
||||
|
||||
const handleMouseWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
const delta = event.deltaY / 1000;
|
||||
let newVolume = settings.volume - delta / 3;
|
||||
|
||||
@@ -51,6 +54,12 @@ const handleMouseWheel = (event: WheelEvent) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.b-bar .right-group button.speaker {
|
||||
border-top: 1px solid transparent !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
.speaker {
|
||||
position: relative;
|
||||
|
||||
@@ -66,20 +75,33 @@ const handleMouseWheel = (event: WheelEvent) => {
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background-color: $gray4;
|
||||
position: absolute;
|
||||
bottom: 2.95rem;
|
||||
height: 2.5rem;
|
||||
cursor: default;
|
||||
bottom: 56px;
|
||||
left: -1px;
|
||||
height: 48px;
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background-color: $gray;
|
||||
border-top: 1px solid $gray3;
|
||||
border-bottom: 1px solid $gray3;
|
||||
border-right: 1px solid $gray3;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
transform: rotate(270deg) translateX(-50%) perspective(1px);
|
||||
transform-origin: left top;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: visibility 0.2s ease-in;
|
||||
transition-delay: 0.25s;
|
||||
cursor: default;
|
||||
transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
|
||||
|
||||
input {
|
||||
width: max-content;
|
||||
max-width: 87px;
|
||||
margin: 0;
|
||||
touch-action: pan-x;
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
height: 1rem;
|
||||
@@ -97,9 +119,16 @@ const handleMouseWheel = (event: WheelEvent) => {
|
||||
|
||||
&:hover {
|
||||
.dialog {
|
||||
transition-delay: 0.25s;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.volume_indicator {
|
||||
font-weight: 600;
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
transform: rotate(90deg) translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
class="context-menu rounded shadow-lg no-select"
|
||||
:style="{
|
||||
visibility: context.visible ? 'visible' : 'hidden',
|
||||
opacity: context.visible ? '1' : '0',
|
||||
}"
|
||||
>
|
||||
<ContextItem
|
||||
@@ -20,8 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onClickOutside } from "@vueuse/core";
|
||||
import { ref } from "vue";
|
||||
|
||||
import useContextStore from "@/stores/context";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
@@ -65,15 +66,17 @@ context.$subscribe((mutation, state) => {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 12.5rem;
|
||||
z-index: 10000 !important;
|
||||
transform: scale(0);
|
||||
width: 13rem;
|
||||
z-index: 1000 !important;
|
||||
height: min-content;
|
||||
|
||||
padding: $medium;
|
||||
padding: $small;
|
||||
background: $context;
|
||||
transform-origin: top left;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease-out, visibility 0.25s ease-out;
|
||||
|
||||
.separator {
|
||||
height: 1px;
|
||||
@@ -85,6 +88,7 @@ context.$subscribe((mutation, state) => {
|
||||
|
||||
.critical {
|
||||
color: $red;
|
||||
transition: background-color 0.2s ease-out, color 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
background-color: $red;
|
||||
|
||||
@@ -2,37 +2,25 @@
|
||||
<div
|
||||
ref="parentRef"
|
||||
class="context-item"
|
||||
@mouseenter="
|
||||
option.children &&
|
||||
!isSmall &&
|
||||
childrenShowMode === contextChildrenShowMode.hover &&
|
||||
showChildren()
|
||||
"
|
||||
@mouseleave="
|
||||
option.children &&
|
||||
!isSmall &&
|
||||
childrenShowMode === contextChildrenShowMode.hover &&
|
||||
hideChildren()
|
||||
"
|
||||
@mouseenter="option.children && !isSmall && childrenShowMode === contextChildrenShowMode.hover && showChildren()"
|
||||
@mouseleave="option.children && !isSmall && childrenShowMode === contextChildrenShowMode.hover && hideChildren()"
|
||||
@click="runAction"
|
||||
>
|
||||
<div class="icon image" v-html="option.icon"></div>
|
||||
<div class="label ellip">{{ option.label }}</div>
|
||||
<div v-if="option.children" class="more" v-html="ExpandIcon"></div>
|
||||
<div
|
||||
v-if="option.children"
|
||||
ref="childRef"
|
||||
class="children rounded shadow-sm"
|
||||
>
|
||||
<div
|
||||
v-for="child in option.children"
|
||||
:key="child.label"
|
||||
class="context-item"
|
||||
:class="[{ critical: child.critical }, child.type]"
|
||||
@click="child.action && runChildAction(child.action)"
|
||||
>
|
||||
<div class="label ellip">
|
||||
{{ child.label }}
|
||||
<div v-if="option.children" ref="childRef" class="children rounded shadow-sm">
|
||||
<div className="wrapper">
|
||||
<div
|
||||
v-for="child in option.children"
|
||||
:key="child.label"
|
||||
class="context-item"
|
||||
:class="[{ critical: child.critical }, child.type]"
|
||||
@click="child.action && runChildAction(child.action)"
|
||||
>
|
||||
<div class="label ellip">
|
||||
{{ child.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { createPopper, Instance } from "@popperjs/core";
|
||||
import { createPopper, Instance, Modifier, Placement, Rect } from "@popperjs/core";
|
||||
import { ref } from "vue";
|
||||
|
||||
import { contextChildrenShowMode } from "@/enums";
|
||||
@@ -70,27 +58,50 @@ function showChildren() {
|
||||
return;
|
||||
}
|
||||
|
||||
popperInstance = createPopper(
|
||||
parentRef.value as HTMLElement,
|
||||
childRef.value as HTMLElement,
|
||||
{
|
||||
placement: "right-start",
|
||||
modifiers: [
|
||||
{
|
||||
name: "flip",
|
||||
options: {
|
||||
fallbackPlacements: ["left-start", "auto"],
|
||||
},
|
||||
const offsetModifier: Modifier<
|
||||
"offset",
|
||||
{ offset: [number, number] | ((args: { placement: Placement; reference: Rect; popper: Rect }) => [number, number]) }
|
||||
> = {
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: ({ placement }) => {
|
||||
// Correct type for placement automatically inferred
|
||||
if (placement.includes("right") || placement.includes("left")) {
|
||||
return [-7, 0];
|
||||
}
|
||||
return [0, 0];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
popperInstance = createPopper(parentRef.value as HTMLElement, childRef.value as HTMLElement, {
|
||||
placement: "right-start",
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
options: {
|
||||
altAxis: true,
|
||||
boundariesElement: "viewport",
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
},
|
||||
{
|
||||
name: "flip",
|
||||
options: {
|
||||
fallbackPlacements: ["left-start", "auto"],
|
||||
boundariesElement: "viewport",
|
||||
},
|
||||
},
|
||||
offsetModifier,
|
||||
],
|
||||
});
|
||||
childRef.value ? (childRef.value.style.visibility = "visible") : null;
|
||||
childRef.value ? (childRef.value.style.opacity = "1") : null;
|
||||
childrenShown.value = true;
|
||||
}
|
||||
|
||||
function hideChildren() {
|
||||
childRef.value ? (childRef.value.style.visibility = "hidden") : null;
|
||||
childRef.value ? (childRef.value.style.opacity = "0") : null;
|
||||
popperInstance?.destroy();
|
||||
childrenShown.value = false;
|
||||
}
|
||||
@@ -129,12 +140,14 @@ function runChildAction(action: () => void) {
|
||||
padding: 0.4rem;
|
||||
position: relative;
|
||||
border-radius: $small;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
.more {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
right: 2px;
|
||||
bottom: 5px;
|
||||
transform: scale(0.65);
|
||||
}
|
||||
|
||||
@@ -142,19 +155,38 @@ function runChildAction(action: () => void) {
|
||||
position: absolute;
|
||||
width: 12rem;
|
||||
z-index: 10;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
transform: scale(0);
|
||||
background-color: $context;
|
||||
transform: scale(0);
|
||||
padding: $medium;
|
||||
padding: $small $smaller;
|
||||
border: solid 1px $gray3;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease-out, visibility 0.25s ease-out;
|
||||
|
||||
max-height: calc(100vh / 2);
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:hover ::-webkit-scrollbar-thumb {
|
||||
background-color: $gray2;
|
||||
}
|
||||
|
||||
&:hover ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: $gray1;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 0 $smaller;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: calc(100vh / 2 - 2rem);
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.context-item {
|
||||
line-height: 1.2;
|
||||
padding: $small 1rem;
|
||||
padding: 0.4rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
@@ -178,7 +210,8 @@ function runChildAction(action: () => void) {
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) .icon > svg {
|
||||
// add to queue icon
|
||||
&:nth-child(3) .icon > svg {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,19 @@
|
||||
:class="fav.type"
|
||||
:to="{
|
||||
name: fav.type === 'album' ? Routes.album : Routes.artist,
|
||||
params:
|
||||
fav.type === 'album'
|
||||
? { albumhash: fav.item.albumhash }
|
||||
: { hash: fav.item.artisthash },
|
||||
params: fav.type === 'album' ? { albumhash: fav.item.albumhash } : { hash: fav.item.artisthash },
|
||||
}"
|
||||
>
|
||||
<div class="imagegroup">
|
||||
<img
|
||||
:src="
|
||||
fav.type === 'album'
|
||||
? paths.images.thumb.large + fav.item.image
|
||||
: paths.images.artist.large + fav.item.image
|
||||
fav.type === 'album' ? paths.images.thumb.large + fav.item.image : paths.images.artist.large + fav.item.image
|
||||
"
|
||||
alt=""
|
||||
class="rounded-sm"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="name ellip"
|
||||
:title="fav.type === 'artist' ? fav.item.name : fav.item.title"
|
||||
>
|
||||
<div class="name ellip" :title="fav.type === 'artist' ? fav.item.name : fav.item.title">
|
||||
{{ fav.type === "artist" ? fav.item.name : fav.item.title }}
|
||||
</div>
|
||||
<div class="label ellip" :class="{ on_artist: fav.type === 'artist' }">
|
||||
@@ -35,8 +27,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RecentFavResult } from "@/interfaces";
|
||||
import { paths } from "../../config";
|
||||
import { Routes } from "@/router";
|
||||
import { paths } from "../../config";
|
||||
|
||||
defineProps<{
|
||||
fav: RecentFavResult;
|
||||
@@ -45,7 +37,7 @@ defineProps<{
|
||||
|
||||
<style lang="scss">
|
||||
.recent-fav-item {
|
||||
flex: 0 0 10.1rem;
|
||||
flex: 0 0 $cardwidth;
|
||||
|
||||
padding: $medium;
|
||||
height: 100%;
|
||||
@@ -73,7 +65,7 @@ defineProps<{
|
||||
.label {
|
||||
font-size: 0.8rem;
|
||||
color: $gray1;
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
border-radius: 1rem;
|
||||
text-transform: capitalize;
|
||||
margin-top: -$smaller;
|
||||
|
||||
@@ -14,9 +14,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onUpdated } from "vue";
|
||||
|
||||
import { subPath } from "@/interfaces";
|
||||
import { focusElemByClass } from "@/utils";
|
||||
import { onUpdated } from "vue";
|
||||
|
||||
import useSettings from "@/stores/settings";
|
||||
|
||||
const settings = useSettings();
|
||||
|
||||
defineProps<{
|
||||
subPaths: subPath[];
|
||||
@@ -27,27 +32,36 @@ defineEmits<{
|
||||
}>();
|
||||
|
||||
onUpdated(() => {
|
||||
focusElemByClass("inthisfolder");
|
||||
if (settings.is_default_layout) {
|
||||
focusElemByClass("inthisfolder");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.breadcrumb-nav {
|
||||
display: flex;
|
||||
gap: $smaller;
|
||||
|
||||
.designatedOS .breadcrumb-nav {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-nav {
|
||||
display: flex;
|
||||
gap: $smaller;
|
||||
|
||||
.path {
|
||||
white-space: nowrap;
|
||||
margin: auto 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.text {
|
||||
padding: $smaller;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
padding: $smaller $small;
|
||||
border-radius: $smaller;
|
||||
transition: background-color 0.2s ease-out;
|
||||
}
|
||||
|
||||
&::before {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<div
|
||||
v-auto-animate
|
||||
class="f-item"
|
||||
:style="{
|
||||
backgroundColor: is_checked ? '#234ece' : '',
|
||||
:class="{
|
||||
selected: is_checked,
|
||||
context_menu_showing: context_menu_showing,
|
||||
}"
|
||||
:class="{ context_menu_showing }"
|
||||
@click="(e) => (folder_page ? null : handleClick(e))"
|
||||
@mouseover="mouse_over = true"
|
||||
@mouseleave="mouse_over = false"
|
||||
@@ -16,8 +16,8 @@
|
||||
<FolderSvg v-else />
|
||||
<div class="info">
|
||||
<div class="f-item-text ellip">{{ folder.name }}</div>
|
||||
<div v-if="folder.count" class="f-count">
|
||||
{{ folder.count + ` File${folder.count == 1 ? "" : "s"}` }}
|
||||
<div class="f-count" v-if="folder.trackcount">
|
||||
{{ folder.trackcount.toLocaleString() + ` File${folder.trackcount == 1 ? "" : "s"}` }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!folder_page" class="check">
|
||||
@@ -39,8 +39,8 @@ import SymLinkSvg from "@/assets/icons/symlink.svg";
|
||||
|
||||
import CheckFilledSvg from "@/assets/icons/check.filled.svg";
|
||||
import CheckSvg from "@/assets/icons/square.svg";
|
||||
import { showFolderContextMenu } from "@/helpers/contextMenuHandler";
|
||||
import { ContextSrc } from "@/enums";
|
||||
import { showFolderContextMenu } from "@/helpers/contextMenuHandler";
|
||||
|
||||
const props = defineProps<{
|
||||
folder: Folder;
|
||||
@@ -70,12 +70,7 @@ function handleClick(e: MouseEvent) {
|
||||
}
|
||||
|
||||
function showContextMenu(e: MouseEvent) {
|
||||
showFolderContextMenu(
|
||||
e,
|
||||
context_menu_showing,
|
||||
ContextSrc.FolderCard,
|
||||
props.folder.path
|
||||
);
|
||||
showFolderContextMenu(e, context_menu_showing, ContextSrc.FolderCard, props.folder.path);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -90,6 +85,7 @@ function showContextMenu(e: MouseEvent) {
|
||||
position: relative;
|
||||
padding: 0 0 0 1rem;
|
||||
gap: $small;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
&.context_menu_showing {
|
||||
background-color: $gray4;
|
||||
@@ -97,6 +93,7 @@ function showContextMenu(e: MouseEvent) {
|
||||
|
||||
svg {
|
||||
color: $gray1;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
.f-count {
|
||||
@@ -118,11 +115,8 @@ function showContextMenu(e: MouseEvent) {
|
||||
transform: scale(0.75);
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.f-item-text {
|
||||
font-weight: 600;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,27 +2,20 @@
|
||||
<div
|
||||
class="f-container rounded-sm"
|
||||
:class="{
|
||||
'list-mode': isIphoneSE ? true : settings.folder_list_mode,
|
||||
'list-mode': isSmallestPhone ? true : settings.folder_list_mode,
|
||||
}"
|
||||
>
|
||||
<div id="f-items" class="rounded">
|
||||
<FolderItem
|
||||
v-for="folder in folders"
|
||||
:key="folder.path"
|
||||
:folder="folder"
|
||||
:folder_page="true"
|
||||
/>
|
||||
<FolderItem v-for="folder in folders" :key="folder.path" :folder="folder" :folder_page="true" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Folder } from "@/interfaces";
|
||||
import FolderItem from "./FolderItem.vue";
|
||||
import { isSmallestPhone } from "@/stores/content-width";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
import { isIphoneSE } from "@/stores/content-width";
|
||||
|
||||
import { ref } from "vue";
|
||||
import FolderItem from "./FolderItem.vue";
|
||||
|
||||
defineProps<{
|
||||
folders: Folder[];
|
||||
@@ -51,11 +44,13 @@ const settings = useSettingsStore();
|
||||
gap: 0;
|
||||
|
||||
.f-item {
|
||||
line-height: 1.2;
|
||||
transition: none;
|
||||
height: 3.25rem;
|
||||
border-radius: $small;
|
||||
background-color: transparent;
|
||||
padding-left: $small;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
.options {
|
||||
display: block;
|
||||
@@ -66,7 +61,7 @@ const settings = useSettingsStore();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: $small;
|
||||
padding-right: $medium;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -35,17 +35,34 @@ const browselist = [
|
||||
title: "Artists",
|
||||
route: Routes.ArtistList,
|
||||
},
|
||||
{
|
||||
title: "Playlists",
|
||||
route: Routes.playlists,
|
||||
},
|
||||
{
|
||||
title: "Favorites",
|
||||
route: Routes.favorites,
|
||||
},
|
||||
{
|
||||
title: "Favorite Tracks",
|
||||
route: Routes.favoriteTracks,
|
||||
},
|
||||
{
|
||||
title: "Favorite Artists",
|
||||
route: Routes.favoriteArtists,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.homebrowse {
|
||||
padding: 1rem 0;
|
||||
padding-left: $medium;
|
||||
padding: 1.5rem 0;
|
||||
padding-left: $small;
|
||||
|
||||
.btitle {
|
||||
font-size: 1.15rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
.browselist {
|
||||
@@ -56,9 +73,11 @@ const browselist = [
|
||||
}
|
||||
|
||||
.browseitem {
|
||||
font-weight: 500;
|
||||
padding: 1.5rem 0;
|
||||
background-color: $gray;
|
||||
color: $white;
|
||||
transition: background-color 0.2s ease-out;
|
||||
}
|
||||
|
||||
.browseitem:hover {
|
||||
|
||||
61
src/components/HomeView/Numbers.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="home-count">
|
||||
<div class="count-item" :title="count.count.toLocaleString()" v-for="count in parseCount()" :key="count.text">
|
||||
<div>{{ count.formatted }}</div>
|
||||
<div class="text">{{ count.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
counts: { [key: string]: number }[];
|
||||
}>();
|
||||
|
||||
function formatNumber(num: number) {
|
||||
if (num >= 1e6) {
|
||||
return [(num / 1e6).toFixed(1), "m"];
|
||||
} else if (num >= 1e3) {
|
||||
return [(num / 1e3).toFixed(1), "k"];
|
||||
} else {
|
||||
return [num.toString(), ""];
|
||||
}
|
||||
}
|
||||
|
||||
function parseCount() {
|
||||
return props.counts.map((count) => {
|
||||
const num = count[Object.keys(count)[0]];
|
||||
const formatted = formatNumber(count[Object.keys(count)[0]]);
|
||||
|
||||
return {
|
||||
count: num,
|
||||
formatted: formatted[0] + formatted[1],
|
||||
text: Object.keys(count)[0],
|
||||
};
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.home-count {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
.count-item {
|
||||
font-size: 3rem;
|
||||
font-weight: bolder;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: solid 1px $gray5;
|
||||
padding-right: 1rem;
|
||||
|
||||
.text {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin-left: $smaller;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="q.currenttrack?.bitrate"
|
||||
class="bitrate"
|
||||
title="file type • bitrate"
|
||||
>
|
||||
{{ q.currenttrack.filepath?.split('.').pop() }} • {{ q.currenttrack.bitrate }}
|
||||
<div v-if="q.currenttrack?.bitrate" class="bitrate" title="file type • bitrate">
|
||||
{{ q.currenttrack.filepath?.split(".").pop() }} • {{ q.currenttrack.bitrate }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -18,6 +14,7 @@ const q = useQueueStore();
|
||||
.bitrate {
|
||||
position: absolute;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
width: max-content;
|
||||
padding: 0.2rem 0.35rem;
|
||||
bottom: $medium;
|
||||
|
||||
@@ -18,10 +18,7 @@
|
||||
import { usePlayer } from "@/stores/player";
|
||||
import useQStore from "@/stores/queue";
|
||||
|
||||
import {
|
||||
default as NextSvg,
|
||||
default as PrevSvg,
|
||||
} from "@/assets/icons/next.svg";
|
||||
import { default as NextSvg, default as PrevSvg } from "@/assets/icons/next.svg";
|
||||
import PauseSvg from "@/assets/icons/pause.svg";
|
||||
import PlaySvg from "@/assets/icons/play.svg";
|
||||
import Spinner from "@/components/shared/Spinner.vue";
|
||||
@@ -54,6 +51,12 @@ const { buffering } = usePlayer();
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
svg {
|
||||
transform: rotate(180deg) scale(0.75);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button:nth-child(2) {
|
||||
@@ -64,10 +67,20 @@ const { buffering } = usePlayer();
|
||||
grid-template-columns: 1fr max-content 1fr;
|
||||
position: relative;
|
||||
margin-right: -$small;
|
||||
gap: 0;
|
||||
|
||||
button:first-child {
|
||||
margin-left: $small;
|
||||
}
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
||||
button:first-child {
|
||||
margin-left: $smaller;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
min="0"
|
||||
:max="time.full"
|
||||
step="0.1"
|
||||
:style="{
|
||||
backgroundSize: `${
|
||||
(time.current / (time.full || 0)) * 100
|
||||
}% 100%`,
|
||||
}"
|
||||
:style="{ backgroundSize: `${(time.current / (time.full || 1)) * 100}% 100%` }"
|
||||
@change="seek"
|
||||
/>
|
||||
</template>
|
||||
@@ -22,10 +18,18 @@ const q = useQStore();
|
||||
|
||||
const { duration: time } = q;
|
||||
|
||||
let prevHash = "";
|
||||
|
||||
const seek = (e: Event) => {
|
||||
if (prevHash && prevHash !== q.currenttrackhash) {
|
||||
prevHash = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const elem = e.target as HTMLInputElement;
|
||||
const value = elem.value;
|
||||
|
||||
prevHash = q.currenttrackhash;
|
||||
q.seek(value as unknown as number);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -41,6 +41,8 @@ import { menus } from "./navitems";
|
||||
align-items: center;
|
||||
padding: $small 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
@@ -78,7 +80,18 @@ import { menus } from "./navitems";
|
||||
}
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
.circular.nav-item:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.circular.nav-item:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1.5rem;
|
||||
margin: 0 $small 0 $small;
|
||||
border-radius: $small;
|
||||
transform: scale(0.9);
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
|
||||
import Navigation from "@/components/LeftSidebar/NavButtons.vue";
|
||||
import Logo from "@/components/Logo.vue";
|
||||
import SongCard from "./NP/SongCard.vue";
|
||||
import Navigation from "@/components/LeftSidebar/NavButtons.vue";
|
||||
|
||||
const settings = useSettingsStore();
|
||||
</script>
|
||||
@@ -31,10 +31,22 @@ const settings = useSettingsStore();
|
||||
|
||||
.scrollable {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 1rem 0;
|
||||
|
||||
@include hideScrollbars;
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .scrollable::-webkit-scrollbar-thumb {
|
||||
background-color: $gray2;
|
||||
}
|
||||
|
||||
&:hover .scrollable::-webkit-scrollbar-thumb:hover {
|
||||
background-color: $gray1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,18 +8,34 @@ import SearchSvg from "@/assets/icons/search.svg";
|
||||
import SettingsSvg from "@/assets/icons/settings.svg";
|
||||
import HomeSvg from "@/assets/icons/home.svg";
|
||||
|
||||
const folder = {
|
||||
name: "folders",
|
||||
route_name: Routes.folder,
|
||||
params: { path: "$home" },
|
||||
icon: FolderSvg,
|
||||
};
|
||||
|
||||
const favorites = {
|
||||
name: "favorites",
|
||||
route_name: Routes.favorites,
|
||||
icon: HeartSvg,
|
||||
};
|
||||
|
||||
const playlists = {
|
||||
name: "playlists",
|
||||
route_name: Routes.playlists,
|
||||
icon: PlaylistSvg,
|
||||
};
|
||||
|
||||
const home = {
|
||||
name: "home",
|
||||
route_name: Routes.Home,
|
||||
icon: HomeSvg,
|
||||
};
|
||||
|
||||
export const menus = [
|
||||
{
|
||||
name: "home",
|
||||
route_name: Routes.Home,
|
||||
icon: HomeSvg,
|
||||
},
|
||||
{
|
||||
name: "folders",
|
||||
route_name: Routes.folder,
|
||||
params: { path: "$home" },
|
||||
icon: FolderSvg,
|
||||
},
|
||||
home,
|
||||
folder,
|
||||
{
|
||||
name: "search",
|
||||
route_name: Routes.search,
|
||||
@@ -30,16 +46,8 @@ export const menus = [
|
||||
{
|
||||
separator: true,
|
||||
},
|
||||
{
|
||||
name: "favorites",
|
||||
route_name: Routes.favorites,
|
||||
icon: HeartSvg,
|
||||
},
|
||||
{
|
||||
name: "playlists",
|
||||
route_name: Routes.playlists,
|
||||
icon: PlaylistSvg,
|
||||
},
|
||||
favorites,
|
||||
playlists,
|
||||
{
|
||||
separator: true,
|
||||
},
|
||||
@@ -50,3 +58,5 @@ export const menus = [
|
||||
icon: SettingsSvg,
|
||||
},
|
||||
];
|
||||
|
||||
export const topnavitems = [home, folder, favorites, playlists];
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<router-link class="swing-logo rounded-md" :to="{ name: 'Home' }">
|
||||
<LogoSvg /> <span>Swing Music</span>
|
||||
</router-link>
|
||||
<router-link class="swing-logo rounded-md" :to="{ name: 'Home' }"> <LogoSvg /> <span>Swing Music</span> </router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -10,12 +8,14 @@ import LogoSvg from "@/assets/icons/logos/logo-fill.light.svg";
|
||||
|
||||
<style lang="scss">
|
||||
.swing-logo {
|
||||
background-image: linear-gradient(37deg, rgb(29, 28, 28), transparent);
|
||||
padding-left: 1rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $medium;
|
||||
padding-left: 1rem;
|
||||
border: solid 1px $gray5;
|
||||
background-image: linear-gradient(37deg, rgb(29, 28, 28), transparent);
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
svg {
|
||||
transform: scale(1.25);
|
||||
|
||||
@@ -1,41 +1,47 @@
|
||||
<template>
|
||||
<div v-if="notifStore.notifs" class="toasts">
|
||||
<div
|
||||
v-for="notif in notifStore.notifs"
|
||||
:key="notif.text"
|
||||
class="new-notif rounded-sm"
|
||||
:class="notif.type"
|
||||
v-if="notifStore.notifs"
|
||||
class="toasts"
|
||||
>
|
||||
<component :is="getSvg(notif.type)" class="notif-icon" />
|
||||
<div class="notif-text">{{ notif.text }}</div>
|
||||
<div
|
||||
v-for="notif in notifStore.notifs"
|
||||
:key="notif.text"
|
||||
class="new-notif rounded-sm"
|
||||
:class="notif.type"
|
||||
>
|
||||
<component
|
||||
:is="getSvg(notif.type)"
|
||||
class="notif-icon"
|
||||
/>
|
||||
<div class="notif-text">{{ notif.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useNotifStore, NotifType } from "../stores/notification";
|
||||
import { NotifType, useToast } from '../stores/notification'
|
||||
|
||||
import HeartSvg from "../assets/icons/heart.svg";
|
||||
import InfoSvg from "../assets/icons/toast/info.svg";
|
||||
import SuccessSvg from "../assets/icons/toast/ok.svg";
|
||||
import ErrorSvg from "../assets/icons/toast/error.svg";
|
||||
import WorkingSvg from "../assets/icons/toast/working.svg";
|
||||
import HeartSvg from '../assets/icons/heart.svg'
|
||||
import ErrorSvg from '../assets/icons/toast/error.svg'
|
||||
import InfoSvg from '../assets/icons/toast/info.svg'
|
||||
import SuccessSvg from '../assets/icons/toast/ok.svg'
|
||||
import WorkingSvg from '../assets/icons/toast/working.svg'
|
||||
|
||||
const notifStore = useNotifStore();
|
||||
const notifStore = useToast()
|
||||
|
||||
function getSvg(notif: NotifType) {
|
||||
switch (notif) {
|
||||
case NotifType.Error:
|
||||
return ErrorSvg;
|
||||
case NotifType.Info:
|
||||
return InfoSvg;
|
||||
case NotifType.Success:
|
||||
return SuccessSvg;
|
||||
case NotifType.Working:
|
||||
return WorkingSvg;
|
||||
case NotifType.Favorite:
|
||||
return HeartSvg;
|
||||
}
|
||||
switch (notif) {
|
||||
case NotifType.Error:
|
||||
return ErrorSvg
|
||||
case NotifType.Info:
|
||||
return InfoSvg
|
||||
case NotifType.Success:
|
||||
return SuccessSvg
|
||||
case NotifType.Working:
|
||||
return WorkingSvg
|
||||
case NotifType.Favorite:
|
||||
return HeartSvg
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@@ -43,49 +49,53 @@ function getSvg(notif: NotifType) {
|
||||
position: fixed;
|
||||
bottom: 6rem;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
transform: translate(-50%);
|
||||
z-index: 100;
|
||||
z-index: 1003;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column-reverse;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.new-notif {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
width: 18rem;
|
||||
height: 4rem;
|
||||
min-height: 4rem;
|
||||
background-color: $gray;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
box-shadow: 0px 0px 2rem rgba(0, 0, 0, 0.466);
|
||||
font-size: 0.85rem;
|
||||
padding: 1rem;
|
||||
padding: 1rem $small;
|
||||
|
||||
grid-template-columns: 2rem 3fr;
|
||||
gap: $small;
|
||||
grid-template-columns: 2rem 3fr;
|
||||
gap: $smaller;
|
||||
|
||||
.notif-text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include smallestPhones {
|
||||
max-width: calc(100% - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
.new-notif.error {
|
||||
$bg: rgb(197, 72, 72);
|
||||
background-color: $bg;
|
||||
$bg: rgb(197, 72, 72);
|
||||
background-color: $bg;
|
||||
}
|
||||
|
||||
.new-notif.info,
|
||||
.new-notif.favorite {
|
||||
$bg: rgb(28, 102, 238);
|
||||
background-color: $bg;
|
||||
}
|
||||
|
||||
.new-notif.favorite,
|
||||
.new-notif.success {
|
||||
$bg: rgb(5, 167, 53);
|
||||
background-color: $bg;
|
||||
$bg: rgb(255, 255, 255);
|
||||
background-color: $bg;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.new-notif.working {
|
||||
$bg: $gray4;
|
||||
background-color: $bg;
|
||||
$bg: $gray4;
|
||||
background-color: $bg;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
title="Go to Album"
|
||||
class="np-image"
|
||||
>
|
||||
<img
|
||||
v-motion-fade
|
||||
class="rounded"
|
||||
:src="paths.images.thumb.original + queue.currenttrack?.image"
|
||||
/>
|
||||
<img v-motion-fade class="rounded" :src="paths.images.thumb.large + queue.currenttrack?.image" />
|
||||
</RouterLink>
|
||||
<NowPlayingInfo @handle-fav="handleFav" />
|
||||
<Progress v-if="isSmallPhone" />
|
||||
@@ -30,7 +26,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 v-if="queue.next">Up Next</h3>
|
||||
<h3 class="nowplaying_title" v-if="queue.next">Up Next</h3>
|
||||
<SongItem
|
||||
v-if="queue.next"
|
||||
:track="queue.next"
|
||||
@@ -38,23 +34,24 @@
|
||||
:source="dropSources.folder"
|
||||
@play-this="queue.playNext"
|
||||
/>
|
||||
<h3 class="nowplaying_title">Queue</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { paths } from "@/config";
|
||||
import { dropSources, favType } from "@/enums";
|
||||
import favoriteHandler from "@/helpers/favoriteHandler";
|
||||
import { Routes } from "@/router";
|
||||
import { isSmallPhone } from "@/stores/content-width";
|
||||
import useQueueStore from "@/stores/queue";
|
||||
import { formatSeconds } from "@/utils";
|
||||
import { dropSources, favType } from "@/enums";
|
||||
import { isSmallPhone } from "@/stores/content-width";
|
||||
import favoriteHandler from "@/helpers/favoriteHandler";
|
||||
|
||||
import PlayingFrom from "./PlayingFrom.vue";
|
||||
import Progress from "@/components/LeftSidebar/NP/Progress.vue";
|
||||
import Buttons from "../BottomBar/Right.vue";
|
||||
import SongItem from "../shared/SongItem.vue";
|
||||
import NowPlayingInfo from "./NowPlayingInfo.vue";
|
||||
import Progress from "@/components/LeftSidebar/NP/Progress.vue";
|
||||
import PlayingFrom from "./PlayingFrom.vue";
|
||||
|
||||
const queue = useQueueStore();
|
||||
|
||||
@@ -71,9 +68,23 @@ function handleFav() {
|
||||
|
||||
<style lang="scss">
|
||||
.now-playing-header {
|
||||
padding-bottom: 1rem;
|
||||
padding-bottom: $smaller;
|
||||
position: relative;
|
||||
|
||||
.nowplaying_title {
|
||||
padding-left: 1rem;
|
||||
margin: 1.25rem 0;
|
||||
|
||||
&:last-child {
|
||||
padding-top: $large;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
@include largePhones {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.below-progress {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -81,12 +92,48 @@ function handleFav() {
|
||||
margin-top: 1rem;
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
font-size: $medium;
|
||||
font-weight: 500;
|
||||
background-color: $gray3;
|
||||
padding: 0 $smaller;
|
||||
padding: 1px $smaller;
|
||||
min-width: 2.5rem;
|
||||
text-align: center;
|
||||
border-radius: $smaller;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@include largePhones {
|
||||
.right-group button.speaker {
|
||||
border-top: 1px solid transparent !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallestPhones {
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
align-items: unset;
|
||||
gap: $small;
|
||||
|
||||
.time:first-child {
|
||||
align-self: baseline;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.time:last-child {
|
||||
align-self: end;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.right-group {
|
||||
width: 100% !important;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,21 +8,12 @@
|
||||
:albumartists="queue.currenttrack?.albumartists || ''"
|
||||
/>
|
||||
<span v-else class="artist author">
|
||||
<a href="https://github.com/mungai-njoroge" target="_blank"
|
||||
>built by @mungai-njoroge ↗</a
|
||||
>
|
||||
<a href="https://github.com/mungai-njoroge" target="_blank">built by @mungai-njoroge ↗</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<HeartSvg
|
||||
:state="queue.currenttrack?.is_favorite"
|
||||
@handle-fav="$emit('handleFav', queue.currenttrackhash)"
|
||||
/>
|
||||
<OptionSvg
|
||||
class="optionsvg"
|
||||
:class="{ context_menu_showing }"
|
||||
@click="showMenu"
|
||||
/>
|
||||
<HeartSvg :state="queue.currenttrack?.is_favorite" @handle-fav="$emit('handleFav', queue.currenttrackhash)" />
|
||||
<OptionSvg class="optionsvg" :class="{ context_menu_showing }" @click="showMenu" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -34,9 +25,9 @@ import { useRoute } from "vue-router";
|
||||
import ArtistName from "../shared/ArtistName.vue";
|
||||
import HeartSvg from "../shared/HeartSvg.vue";
|
||||
|
||||
import useQueueStore from "@/stores/queue";
|
||||
import OptionSvg from "@/assets/icons/more.svg";
|
||||
import { showTrackContextMenu } from "@/helpers/contextMenuHandler";
|
||||
import useQueueStore from "@/stores/queue";
|
||||
|
||||
const route = useRoute();
|
||||
const context_menu_showing = ref(false);
|
||||
@@ -50,13 +41,7 @@ defineEmits<{
|
||||
function showMenu(e: MouseEvent) {
|
||||
if (!queue.currenttrack) return;
|
||||
|
||||
showTrackContextMenu(
|
||||
e,
|
||||
queue.currenttrack,
|
||||
context_menu_showing,
|
||||
route,
|
||||
false
|
||||
);
|
||||
showTrackContextMenu(e, queue.currenttrack, context_menu_showing, route, false);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -66,6 +51,7 @@ function showMenu(e: MouseEvent) {
|
||||
grid-template-columns: 1fr max-content;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
.artist {
|
||||
font-size: 0.8rem;
|
||||
@@ -80,6 +66,7 @@ function showMenu(e: MouseEvent) {
|
||||
.optionsvg {
|
||||
transform: scale(1.5) rotate(90deg);
|
||||
border-radius: $small;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray3;
|
||||
@@ -94,6 +81,7 @@ function showMenu(e: MouseEvent) {
|
||||
|
||||
.heart-button {
|
||||
background-color: $gray;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray4;
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
<template>
|
||||
<div class="now-playing-top">
|
||||
<router-link
|
||||
class="now-playling-from-link"
|
||||
:to="(data.location as RouteLocationRaw)"
|
||||
title="Go to Play Source"
|
||||
>
|
||||
<router-link class="now-playling-from-link" :to="(data.location as RouteLocationRaw)" title="Go to Play Source">
|
||||
<div class="from">
|
||||
<img
|
||||
v-if="
|
||||
queue.from.type === FromOptions.album ||
|
||||
queue.from.type === FromOptions.artist
|
||||
"
|
||||
v-if="tracklist.from.type === FromOptions.album || tracklist.from.type === FromOptions.artist"
|
||||
:src="data.image + '.webp'"
|
||||
:alt="`Now Playing ${queue.from.type} image`"
|
||||
:class="`${
|
||||
queue.from.type === FromOptions.artist ? 'circular' : 'rounded-sm'
|
||||
}`"
|
||||
:alt="`Now Playing ${tracklist.from.type} image`"
|
||||
:class="`${tracklist.from.type === FromOptions.artist ? 'circular' : 'rounded-sm'}`"
|
||||
/>
|
||||
<div v-else class="from-icon border rounded-sm">
|
||||
<component :is="data.icon"></component>
|
||||
</div>
|
||||
<div class="pad-sm">
|
||||
<div class="type">{{ queue.from.type }}</div>
|
||||
<div class="type">{{ tracklist.from.type }}</div>
|
||||
<div class="ellip2">{{ data.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<button class="options" @click="showContextMenu">
|
||||
<MoreSvg />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { RouteLocationRaw } from "vue-router";
|
||||
|
||||
import useTracklist from "@/stores/queue/tracklist";
|
||||
@@ -38,12 +32,23 @@ import useTracklist from "@/stores/queue/tracklist";
|
||||
import { FromOptions } from "@/enums";
|
||||
import playingFrom from "@/utils/playingFrom";
|
||||
|
||||
const queue = useTracklist();
|
||||
import MoreSvg from "@/assets/icons/more.svg";
|
||||
import { showQueueContextMenu } from "@/helpers/contextMenuHandler";
|
||||
|
||||
const tracklist = useTracklist();
|
||||
|
||||
const context_showing = ref(false);
|
||||
|
||||
const data = computed(() => {
|
||||
const { name, location, icon, image } = playingFrom(queue.from);
|
||||
const { name, location, icon, image } = playingFrom(tracklist.from);
|
||||
return { name, location, icon, image };
|
||||
});
|
||||
|
||||
function showContextMenu(e: MouseEvent) {
|
||||
if (!tracklist.tracklist.length) return;
|
||||
|
||||
showQueueContextMenu(e, context_showing);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -52,6 +57,14 @@ const data = computed(() => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.options {
|
||||
transform: rotate(90deg);
|
||||
|
||||
svg {
|
||||
transform: scale(1.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.now-playling-from-link {
|
||||
@@ -73,6 +86,7 @@ const data = computed(() => {
|
||||
padding: $smaller;
|
||||
aspect-ratio: 1;
|
||||
width: 2.5rem;
|
||||
margin-right: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -84,6 +98,11 @@ const data = computed(() => {
|
||||
text-transform: capitalize;
|
||||
font-size: 0.8rem;
|
||||
color: $gray1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.type + div {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<div class="now-playing-tabs">
|
||||
<div class="tab-items">
|
||||
<h3>Queue</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.now-playing-tabs {
|
||||
.tab-items {
|
||||
border: solid transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,15 +4,20 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.p-after-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 1rem;
|
||||
margin-top: $small;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: $gray1;
|
||||
|
||||
@include largePhones {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
:class="{ 'use-sqr_img': useSqrImg }"
|
||||
>
|
||||
<div
|
||||
v-if="Number.isNaN"
|
||||
v-if="Number.isInteger(info.id)"
|
||||
class="float"
|
||||
:style="{
|
||||
color: textColor,
|
||||
@@ -28,37 +28,32 @@
|
||||
class="album-header-ambient rounded-lg"
|
||||
style="height: 100%; width: 100%"
|
||||
:style="{
|
||||
boxShadow: colors.bg
|
||||
? `0 .5rem 2rem ${colors.bg}`
|
||||
: '0 .5rem 2rem black',
|
||||
boxShadow: colors.bg ? `0 .5rem 2rem ${colors.bg}` : '0 .5rem 2rem black',
|
||||
}"
|
||||
></div>
|
||||
<div v-if="info.has_image && useSqrImg" class="sqr_img">
|
||||
<img :src="(playlist.info.image as string)" class="rounded-sm" />
|
||||
</div>
|
||||
<BannerImages
|
||||
v-if="playlist.info.count && !info.has_image && useSqrImg"
|
||||
class="sqr_img rounded-sm"
|
||||
/>
|
||||
<BannerImages v-if="playlist.info.count && !info.has_image && useSqrImg" class="sqr_img rounded-sm" />
|
||||
<Info :text-color="textColor" :btn_color="colors.btn" />
|
||||
<LastUpdated />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed } from "vue";
|
||||
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { getTextColor } from "@/utils/colortools/shift";
|
||||
import { pinUnpinPlaylist } from "@/requests/playlists";
|
||||
import { heightLarge, isSmallPhone } from "@/stores/content-width";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { getTextColor } from "@/utils/colortools/shift";
|
||||
|
||||
import Info from "./Header/Info.vue";
|
||||
import PinSvg from "@/assets/icons/pin.svg";
|
||||
import LastUpdated from "./Header/LastUpdated.vue";
|
||||
import BannerImages from "./Header/BannerImages.vue";
|
||||
import PinFillSvg from "@/assets/icons/pin.fill.svg";
|
||||
import PinSvg from "@/assets/icons/pin.svg";
|
||||
import BannerImages from "./Header/BannerImages.vue";
|
||||
import Info from "./Header/Info.vue";
|
||||
import LastUpdated from "./Header/LastUpdated.vue";
|
||||
|
||||
const playlist = usePStore();
|
||||
|
||||
@@ -66,10 +61,7 @@ const { info, colors } = storeToRefs(playlist);
|
||||
|
||||
const bg = computed(() => {
|
||||
if (playlist.info.has_image) {
|
||||
if (
|
||||
isSmallPhone.value ||
|
||||
(!playlist.info.settings.square_img && !isSmallPhone.value)
|
||||
) {
|
||||
if (isSmallPhone.value || (!playlist.info.settings.square_img && !isSmallPhone.value)) {
|
||||
return `url(${info.value.image})`;
|
||||
}
|
||||
}
|
||||
@@ -77,9 +69,7 @@ const bg = computed(() => {
|
||||
return colors.value.bg ? colors.value.bg : "";
|
||||
});
|
||||
|
||||
const useSqrImg = computed(
|
||||
() => !playlist.info.has_image || !bg.value.startsWith("url")
|
||||
);
|
||||
const useSqrImg = computed(() => !playlist.info.has_image || !bg.value.startsWith("url"));
|
||||
|
||||
const textColor = computed(() => {
|
||||
if (colors.value.bg !== "") {
|
||||
@@ -130,7 +120,7 @@ function pinPlaylist(pid: number) {
|
||||
font-size: 3.75rem !important;
|
||||
}
|
||||
|
||||
@include smallPhone {
|
||||
@include largePhones {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
@@ -176,7 +166,7 @@ function pinPlaylist(pid: number) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@include smallPhone {
|
||||
@include largePhones {
|
||||
.title {
|
||||
font-size: 2.5rem !important;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { paths } from "@/config";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
|
||||
const playlist = usePStore();
|
||||
</script>
|
||||
@@ -32,7 +32,7 @@ const playlist = usePStore();
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@include smallPhone {
|
||||
@include largePhones {
|
||||
right: -4rem;
|
||||
|
||||
img {
|
||||
|
||||
@@ -9,28 +9,13 @@
|
||||
<PlayBtnRect :source="playSources.playlist" :bg_color="btn_color" />
|
||||
</div>
|
||||
<div class="duration">
|
||||
{{
|
||||
playlist.info.count.toLocaleString() +
|
||||
` ${playlist.info.count == 1 ? "Track" : "Tracks"}`
|
||||
}}
|
||||
{{ playlist.info.count.toLocaleString() + ` ${playlist.info.count == 1 ? "Track" : "Tracks"}` }}
|
||||
•
|
||||
{{ formatSeconds(playlist.info.duration, true) }}
|
||||
</div>
|
||||
<div ref="test_elem"></div>
|
||||
<div
|
||||
class="title"
|
||||
:class="`${
|
||||
playlist.info.settings.square_img && isSmall ? 'ellip' : 'ellip2'
|
||||
}`"
|
||||
>
|
||||
<span
|
||||
v-for="t in balanceText(
|
||||
playlist.info.name,
|
||||
test_elem?.offsetWidth || 0,
|
||||
'4rem'
|
||||
)"
|
||||
:key="t"
|
||||
>
|
||||
<div class="title" :class="`${playlist.info.settings.square_img && isSmall ? 'ellip' : 'ellip2'}`">
|
||||
<span v-for="t in balanceText(playlist.info.name, test_elem?.offsetWidth || 0, '4rem')" :key="t">
|
||||
{{ t }}
|
||||
<br />
|
||||
</span>
|
||||
@@ -40,11 +25,11 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { playSources } from "@/enums";
|
||||
import { formatSeconds } from "@/utils";
|
||||
import { isSmall } from "@/stores/content-width";
|
||||
import { formatSeconds } from "@/utils";
|
||||
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import PlayBtnRect from "@/components/shared/PlayBtnRect.vue";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { balanceText } from "@/utils/balanceText";
|
||||
import { Ref, ref } from "vue";
|
||||
|
||||
@@ -64,7 +49,7 @@ const test_elem: Ref<HTMLElement | null> = ref(null);
|
||||
height: 100%;
|
||||
display: grid;
|
||||
z-index: 10;
|
||||
padding-left: 1.25rem;
|
||||
padding: 0 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
|
||||
@@ -1,58 +1,69 @@
|
||||
<template>
|
||||
<div class="last-updated">
|
||||
<span v-if="!isHeaderSmall" class="status"
|
||||
>Last updated {{ playlist.info.last_updated }}  |  </span
|
||||
>
|
||||
<div class="edit" @click="editPlaylist">Edit  </div>
|
||||
|
|
||||
<DeleteSvg class="edit" @click="deletePlaylist" />
|
||||
</div>
|
||||
<div class="last-updated">
|
||||
<span
|
||||
v-if="!isHeaderSmall"
|
||||
class="status"
|
||||
>Last updated {{ playlist.info.last_updated }}</span
|
||||
>
|
||||
<div
|
||||
v-if="Number.isInteger(playlist.info.id)"
|
||||
class="edit"
|
||||
|
||||
>
|
||||
  |   <span @click="editPlaylist">Edit</span>  
|
||||
{{ Number.isInteger(playlist.info.id) ? ' | ' : '' }}
|
||||
<DeleteSvg
|
||||
class="edit"
|
||||
@click="deletePlaylist"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import DeleteSvg from "@/assets/icons/delete.svg";
|
||||
import DeleteSvg from '@/assets/icons/delete.svg'
|
||||
|
||||
import { isHeaderSmall } from "@/stores/content-width";
|
||||
import { isHeaderSmall } from '@/stores/content-width'
|
||||
|
||||
import useModalStore from "@/stores/modal";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import useModalStore from '@/stores/modal'
|
||||
import usePStore from '@/stores/pages/playlist'
|
||||
|
||||
const playlist = usePStore();
|
||||
const modal = useModalStore();
|
||||
const playlist = usePStore()
|
||||
const modal = useModalStore()
|
||||
|
||||
function editPlaylist() {
|
||||
modal.showEditPlaylistModal();
|
||||
modal.showEditPlaylistModal()
|
||||
}
|
||||
|
||||
function deletePlaylist() {
|
||||
modal.showDeletePlaylistModal(playlist.info.id);
|
||||
modal.showDeletePlaylistModal(playlist.info.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.last-updated {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
padding: $smaller $small;
|
||||
// background-color: $body;
|
||||
// color: rgb(255, 255, 255);
|
||||
font-size: 0.9rem;
|
||||
border-radius: $smaller;
|
||||
// box-shadow: 0 0 1rem rgba(0, 0, 0, 0.479);
|
||||
z-index: 12;
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
padding: $smaller $small;
|
||||
font-size: 0.9rem;
|
||||
border-radius: $smaller;
|
||||
z-index: 12;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.edit {
|
||||
cursor: pointer;
|
||||
color: $brown;
|
||||
}
|
||||
.edit {
|
||||
cursor: pointer;
|
||||
color: $brown;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
transform: scale(0.75);
|
||||
margin-bottom: -0.2rem;
|
||||
color: $red !important;
|
||||
}
|
||||
svg {
|
||||
transform: scale(0.75);
|
||||
margin-bottom: -0.2rem;
|
||||
color: $red !important;
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,32 +1,17 @@
|
||||
<template>
|
||||
<router-link
|
||||
:to="{ name: 'PlaylistView', params: { pid: playlist.id } }"
|
||||
class="p-card rounded no-scroll"
|
||||
>
|
||||
<div
|
||||
v-if="!playlist.has_image && playlist.images.length"
|
||||
class="image-grid rounded-sm no-scroll"
|
||||
>
|
||||
<img
|
||||
v-for="(img, index) in playlist.images"
|
||||
:key="index"
|
||||
:src="paths.images.thumb.large + img"
|
||||
/>
|
||||
<router-link :to="{ name: 'PlaylistView', params: { pid: playlist.id } }" class="p-card rounded no-scroll">
|
||||
<div v-if="!playlist.has_image && playlist.images.length" class="image-grid rounded-sm no-scroll">
|
||||
<img v-for="(img, index) in playlist.images" :key="index" :src="paths.images.thumb.large + img" />
|
||||
</div>
|
||||
<img
|
||||
v-else
|
||||
:src="imguri + playlist.thumb"
|
||||
class="rounded-sm"
|
||||
:class="{ border: !playlist.thumb }"
|
||||
/>
|
||||
<img v-else :src="imguri + playlist.thumb" class="rounded-sm" :class="{ border: !playlist.thumb }" />
|
||||
<div class="overlay rounded">
|
||||
<div v-if="playlist.help_text" class="rhelp playlist">{{ playlist.help_text }}</div>
|
||||
<div v-if="playlist.help_text" class="rhelp playlist">
|
||||
<span class="help">{{ playlist.help_text }}</span>
|
||||
<span class="time">{{ playlist.time }}</span>
|
||||
</div>
|
||||
<div class="p-name ellip">{{ playlist.name }}</div>
|
||||
<div class="p-count">
|
||||
<b>{{
|
||||
playlist.count.toLocaleString() +
|
||||
` Track${playlist.count === 1 ? "" : "s"}`
|
||||
}}</b>
|
||||
<b>{{ playlist.count.toLocaleString() + ` Track${playlist.count === 1 ? "" : "s"}` }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -51,6 +36,7 @@ defineProps<{
|
||||
gap: $small;
|
||||
user-select: none;
|
||||
height: max-content;
|
||||
transition: background-color 0.2s ease-out;
|
||||
|
||||
.image-grid {
|
||||
display: grid;
|
||||
@@ -58,7 +44,6 @@ defineProps<{
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition: all 0.25s ease;
|
||||
background-color: $gray4 !important;
|
||||
background-blend-mode: screen;
|
||||
}
|
||||
@@ -76,9 +61,13 @@ defineProps<{
|
||||
justify-content: flex-end;
|
||||
transition: all 0.25s ease;
|
||||
|
||||
.p-name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.p-count {
|
||||
opacity: 0.75;
|
||||
font-size: 0.75rem;
|
||||
color: #ffffffbf;
|
||||
margin-top: $smaller;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Playlist } from "@/interfaces";
|
||||
import PlaylistCard from "@/components/PlaylistsList/PlaylistCard.vue";
|
||||
import { Playlist } from "@/interfaces";
|
||||
|
||||
defineProps<{
|
||||
playlists: Playlist[];
|
||||
@@ -21,7 +21,7 @@ defineProps<{
|
||||
.playlistcardgroup {
|
||||
margin-bottom: 4rem;
|
||||
h3 {
|
||||
margin-left: $small;
|
||||
margin-left: $medium;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
<script setup lang="ts">
|
||||
import useTabStore from "@/stores/tabs";
|
||||
|
||||
import DashBoard from "./Home/Main.vue";
|
||||
import Queue from "./Queue.vue";
|
||||
import Search from "./Search/Main.vue";
|
||||
import DashBoard from "./Home/Main.vue";
|
||||
import SearchInput from "./SearchInput.vue";
|
||||
|
||||
const tabs = useTabStore();
|
||||
@@ -64,4 +64,12 @@ const tabs = useTabStore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.designatedOS .r-sidebar > .r-content > .r-queue > .queue-virtual-scroller > .scroller::-webkit-scrollbar-track {
|
||||
background-color: $gray;
|
||||
}
|
||||
|
||||
.designatedOS .r-sidebar > .r-content > .r-queue > .queue-virtual-scroller > .scroller::-webkit-scrollbar-thumb {
|
||||
border: 4px solid $gray;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<template>
|
||||
<div class="queue-actions">
|
||||
<div class="left">
|
||||
<button v-wave class="shuffle-queue action" @click="queue.shuffleQueue">
|
||||
<button v-if="!onNowPlaying" v-wave class="shuffle-queue action" @click="queue.shuffleQueue">
|
||||
<ShuffleSvg />
|
||||
<span>Shuffle</span>
|
||||
</button>
|
||||
<h2 v-else style="margin: 0">Now Playing</h2>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button
|
||||
class="menu"
|
||||
:class="{ 'btn-active': context_showing }"
|
||||
@click="showContextMenu"
|
||||
>
|
||||
<button class="menu" :class="{ 'btn-active': context_showing }" @click="showContextMenu">
|
||||
<OptionsSvg />
|
||||
</button>
|
||||
</div>
|
||||
@@ -19,9 +16,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import useQueue from "@/stores/queue";
|
||||
import useTracklist from "@/stores/queue/tracklist";
|
||||
import { ref } from "vue";
|
||||
|
||||
import { showQueueContextMenu } from "@/helpers/contextMenuHandler";
|
||||
|
||||
@@ -61,13 +58,14 @@ defineProps<{
|
||||
}
|
||||
|
||||
// hide on screens less than 600px
|
||||
@media screen and (max-width: 600px) {
|
||||
@media only screen and (max-width: 600px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<div v-auto-animate class="artists-results">
|
||||
<div>
|
||||
<div
|
||||
v-if="album_grid == true && search.albums.value.length"
|
||||
class="search-results-grid"
|
||||
>
|
||||
<AlbumCard
|
||||
v-for="a in search.albums.value"
|
||||
:key="a.albumid"
|
||||
:album="a"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!album_grid && search.artists.value.length"
|
||||
class="search-results-grid"
|
||||
>
|
||||
<ArtistCard
|
||||
v-for="artist in search.artists.value"
|
||||
:key="artist.image"
|
||||
:artist="artist"
|
||||
:alt="true"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="t-center">
|
||||
<h5>No {{ album_grid ? "albums" : "artists" }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
<LoadMore
|
||||
v-if="search.albums.value.length || search.artists.value.length"
|
||||
:loader="album_grid ? search.loadAlbums : search.loadArtists"
|
||||
:can_load_more="album_grid ? search.albums.more : search.artists.more"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useSearchStore from "../../../stores/search";
|
||||
|
||||
import AlbumCard from "@/components/shared/AlbumCard.vue";
|
||||
import ArtistCard from "../../shared/ArtistCard.vue";
|
||||
import LoadMore from "./LoadMore.vue";
|
||||
|
||||
const search = useSearchStore();
|
||||
|
||||
defineProps<{
|
||||
album_grid?: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.artists-results {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
margin: 0 1rem;
|
||||
grid-template-rows: 1fr max-content;
|
||||
}
|
||||
|
||||
.search-results-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
|
||||
min-height: 10rem;
|
||||
padding-bottom: $small;
|
||||
}
|
||||
</style>
|
||||
@@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div class="morexx">
|
||||
<button
|
||||
:class="{
|
||||
load_disabled: !can_load_more,
|
||||
}"
|
||||
@click.prevent="loader()"
|
||||
>
|
||||
<div class="text">Load More</div>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
loader: () => void;
|
||||
can_load_more: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.morexx {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-top: $small;
|
||||
|
||||
button {
|
||||
padding: 0 1rem !important;
|
||||
width: calc(100% + 2rem);
|
||||
border-radius: 0;
|
||||
height: 3rem;
|
||||
background: $darkestblue;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background: $darkblue;
|
||||
border-color: $darkblue;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div class="right-search">
|
||||
<TabsWrapper
|
||||
:tabs="tabs"
|
||||
:current-tab="currentTab"
|
||||
:tab-content="true"
|
||||
@switchTab="switchTab"
|
||||
>
|
||||
<TabsWrapper :tabs="tabs" :current-tab="currentTab" :tab-content="true" @switchTab="switchTab">
|
||||
<Tab :name="currentTab" />
|
||||
</TabsWrapper>
|
||||
</div>
|
||||
@@ -41,6 +36,10 @@ function switchTab(tab: string) {
|
||||
|
||||
.tabheaders {
|
||||
padding: 1rem;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.input {
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ArtistGrid from "./ArtistGrid.vue";
|
||||
import useSearch from "@/stores/search";
|
||||
|
||||
import TracksGrid from "./TracksGrid.vue";
|
||||
import TopResults from "./TopResults.vue";
|
||||
import CardPage from "@/views/SearchView/CardGridPage.vue";
|
||||
|
||||
const search = useSearch();
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
}>();
|
||||
@@ -24,15 +27,23 @@ function getComponent() {
|
||||
};
|
||||
case "albums":
|
||||
return {
|
||||
component: ArtistGrid,
|
||||
component: CardPage,
|
||||
props: {
|
||||
album_grid: true,
|
||||
page: "album",
|
||||
items: search.albums.value,
|
||||
outside_route: true,
|
||||
fetch_callback: search.loadAlbums,
|
||||
},
|
||||
};
|
||||
case "artists":
|
||||
return {
|
||||
component: ArtistGrid,
|
||||
props: {},
|
||||
component: CardPage,
|
||||
props: {
|
||||
page: "artist",
|
||||
items: search.artists.value,
|
||||
outside_route: true,
|
||||
fetch_callback: search.loadArtists,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -44,12 +44,29 @@ defineEmits<{
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vue-recycle-scroller {
|
||||
padding: 0 $small;
|
||||
}
|
||||
|
||||
.cardlistrow {
|
||||
grid-template-columns: repeat(auto-fill, minmax(8.1rem, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
#tab-content {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.designatedOS #tab-content::-webkit-scrollbar-track {
|
||||
background-color: $gray;
|
||||
}
|
||||
|
||||
.designatedOS #tab-content::-webkit-scrollbar-thumb {
|
||||
border: 4px solid $gray;
|
||||
}
|
||||
|
||||
#right-tabs.tabContent {
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: res_type === 'artist' ? Routes.artist : Routes.album,
|
||||
params:
|
||||
res_type === 'artist'
|
||||
? { hash: item.artisthash || ' ' }
|
||||
: { albumhash: item.albumhash || ' ' },
|
||||
params: res_type === 'artist' ? { hash: item.artisthash || ' ' } : { albumhash: item.albumhash || ' ' },
|
||||
}"
|
||||
class="top-result-item rounded"
|
||||
>
|
||||
<img
|
||||
:src="
|
||||
res_type === 'artist'
|
||||
? paths.images.artist.large + item.image
|
||||
: paths.images.thumb.large + item.image
|
||||
"
|
||||
:src="res_type === 'artist' ? paths.images.artist.medium + item.image : paths.images.thumb.medium + item.image"
|
||||
alt=""
|
||||
class="rounded-sm"
|
||||
:class="{ circular: res_type === 'artist' }"
|
||||
@@ -36,10 +29,7 @@
|
||||
{{ item.trackcount === 1 ? "track" : "tracks" }}
|
||||
</div>
|
||||
<div v-if="res_type === 'track'" class="artists flex">
|
||||
<ArtistName
|
||||
:artists="item.artists"
|
||||
:albumartists="item.albumartists"
|
||||
/>
|
||||
<ArtistName :artists="item.artists" :albumartists="item.albumartists" />
|
||||
•
|
||||
{{ formatSeconds(item.duration, true) }}
|
||||
</div>
|
||||
@@ -57,11 +47,7 @@
|
||||
</button>
|
||||
<PlayBtn
|
||||
:source="
|
||||
res_type == 'album'
|
||||
? playSources.album
|
||||
: res_type == 'artist'
|
||||
? playSources.artist
|
||||
: playSources.track
|
||||
res_type == 'album' ? playSources.album : res_type == 'artist' ? playSources.artist : playSources.track
|
||||
"
|
||||
:album-hash="item.albumhash"
|
||||
:album-name="item.title"
|
||||
@@ -74,22 +60,22 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { Routes } from "@/router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import useSearchStore from "@/stores/search";
|
||||
import { showTrackContextMenu as showContext } from "@/helpers/contextMenuHandler";
|
||||
import useSearchStore from "@/stores/search";
|
||||
|
||||
import { paths } from "@/config";
|
||||
import { formatSeconds } from "@/utils";
|
||||
import { Album, Artist, Track } from "@/interfaces";
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
import Moresvg from "@/assets/icons/more.svg";
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
import { paths } from "@/config";
|
||||
import { Album, Artist, Track } from "@/interfaces";
|
||||
import { formatSeconds } from "@/utils";
|
||||
|
||||
import { playSources } from "@/enums";
|
||||
import PlayBtn from "@/components/shared/PlayBtn.vue";
|
||||
import { playSources } from "@/enums";
|
||||
|
||||
const search = useSearchStore();
|
||||
const route = useRoute();
|
||||
@@ -139,7 +125,7 @@ function showMenu(e: MouseEvent) {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
transition: opacity 0.2s ease-in-out, background-color 0.2s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +156,7 @@ function showMenu(e: MouseEvent) {
|
||||
|
||||
.type {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
background-color: $darkblue;
|
||||
width: max-content;
|
||||
@@ -188,6 +175,7 @@ function showMenu(e: MouseEvent) {
|
||||
|
||||
.artists {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
import { Track } from "@/interfaces";
|
||||
|
||||
import useQueueStore from "@/stores/queue";
|
||||
import useSearchStore from "@/stores/search";
|
||||
import useTracklist from "@/stores/queue/tracklist";
|
||||
import useSearchStore from "@/stores/search";
|
||||
|
||||
import TrackItem from "@/components/shared/TrackItem.vue";
|
||||
|
||||
@@ -37,7 +37,7 @@ function handlePlay(track: Track) {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.track-item {
|
||||
padding: $small 1.1rem;
|
||||
padding: $small;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
<template>
|
||||
<div id="tracks-results">
|
||||
<div v-if="search.tracks.value.length">
|
||||
<TrackItem
|
||||
v-for="(track, index) in search.tracks.value"
|
||||
:key="track.id"
|
||||
:is-current="queue.currenttrackhash === track.trackhash"
|
||||
:is-highlighted="false"
|
||||
:is-current-playing="
|
||||
queue.currenttrackhash === track.trackhash && queue.playing
|
||||
"
|
||||
:track="track"
|
||||
:index="index + 1"
|
||||
<div v-if="!search.tracks.value.length" class="t-center">
|
||||
<h5>No tracks</h5>
|
||||
</div>
|
||||
|
||||
<RecycleScroller
|
||||
v-slot="{ item, index }"
|
||||
class="scroller"
|
||||
style="height: 100%"
|
||||
:items="scrollerItems"
|
||||
:item-size="64"
|
||||
key-field="id"
|
||||
>
|
||||
<component
|
||||
:is="item.component"
|
||||
v-bind="item.props"
|
||||
@playThis="updateQueue(index)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="t-center"><h5>No tracks</h5></div>
|
||||
<LoadMore
|
||||
v-if="search.tracks.value.length"
|
||||
:loader="search.loadTracks"
|
||||
:can_load_more="search.tracks.more"
|
||||
/>
|
||||
</RecycleScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { computed, onMounted } from "vue";
|
||||
|
||||
import useQueue from "@/stores/queue";
|
||||
import useSearch from "@/stores/search";
|
||||
import useTracklist from "@/stores/queue/tracklist";
|
||||
|
||||
import LoadMore from "./LoadMore.vue";
|
||||
import TrackItem from "@/components/shared/TrackItem.vue";
|
||||
import AlbumsFetcher from "@/components/ArtistView/AlbumsFetcher.vue";
|
||||
|
||||
const queue = useQueue();
|
||||
const search = useSearch();
|
||||
@@ -42,6 +40,36 @@ function updateQueue(index: number) {
|
||||
queue.play(index);
|
||||
}
|
||||
|
||||
const scrollerItems = computed(() => {
|
||||
const items: any[] = search.tracks.value.map((track, index) => {
|
||||
return {
|
||||
track,
|
||||
id: index,
|
||||
component: TrackItem,
|
||||
props: {
|
||||
track,
|
||||
index: index + 1,
|
||||
isCurrent: queue.currenttrackhash === track.trackhash,
|
||||
isCurrentPlaying:
|
||||
queue.currenttrackhash === track.trackhash && queue.playing,
|
||||
isHighlighted: false,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: Math.random(),
|
||||
component: AlbumsFetcher,
|
||||
props: {
|
||||
fetch_callback: search.loadTracks,
|
||||
showtext: search.tracks.more,
|
||||
outside_route: true,
|
||||
},
|
||||
});
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search.switchTab("tracks");
|
||||
});
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
<template>
|
||||
<div class="gsearch-input">
|
||||
<div
|
||||
class="gsearch-input"
|
||||
@click="
|
||||
!settings.use_sidebar &&
|
||||
$route.name !== Routes.search &&
|
||||
$router.push({
|
||||
name: Routes.search,
|
||||
params: { page: 'top' },
|
||||
query: { q: search.query },
|
||||
})
|
||||
"
|
||||
>
|
||||
<div id="ginner" ref="inputRef" tabindex="0">
|
||||
<button
|
||||
v-auto-animate
|
||||
:title="
|
||||
tabs.current === tabs.tabs.search ? 'back to queue' : 'go to search'
|
||||
"
|
||||
:title="tabs.current === tabs.tabs.search ? 'back to queue' : 'go to search'"
|
||||
:class="{ no_bg: on_nav }"
|
||||
@click.prevent="handleButton"
|
||||
>
|
||||
@@ -18,37 +27,55 @@
|
||||
placeholder="Start typing to search"
|
||||
type="search"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@blur.prevent="removeFocusedClass"
|
||||
@focus.prevent="addFocusedClass"
|
||||
/>
|
||||
<div class="clear_input noSelect" :class="{ active: search.query.length > 0 }" @click="clearInput">X</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
import useSearch from "@/stores/search";
|
||||
import useSettings from "@/stores/settings";
|
||||
import useTabStore from "@/stores/tabs";
|
||||
import useSearchStore from "@/stores/search";
|
||||
import { ref } from "vue";
|
||||
|
||||
import BackSvg from "@/assets/icons/arrow.svg";
|
||||
import SearchSvg from "@/assets/icons/search.svg";
|
||||
import { Routes } from "@/router";
|
||||
|
||||
const props = defineProps<{
|
||||
on_nav?: boolean;
|
||||
}>();
|
||||
|
||||
const tabs = useTabStore();
|
||||
const search = useSearchStore();
|
||||
const search = useSearch();
|
||||
const settings = useSettings();
|
||||
|
||||
// HANDLE FOCUS
|
||||
const inputRef = ref<HTMLElement>();
|
||||
const inputRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
// NOTE: Functions are used because classes are added to the sorrounding element
|
||||
// and not the input itself.
|
||||
function addFocusedClass() {
|
||||
inputRef.value?.classList.add("search-focused");
|
||||
if (inputRef.value) {
|
||||
inputRef.value.classList.add("search-focused");
|
||||
}
|
||||
}
|
||||
|
||||
function removeFocusedClass() {
|
||||
inputRef.value?.classList.remove("search-focused");
|
||||
if (inputRef.value) {
|
||||
inputRef.value.classList.remove("search-focused");
|
||||
}
|
||||
}
|
||||
|
||||
function clearInput() {
|
||||
search.query = "";
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// @end
|
||||
@@ -56,7 +83,7 @@ function removeFocusedClass() {
|
||||
function handleButton() {
|
||||
if (props.on_nav) return;
|
||||
|
||||
if (tabs.current === tabs.tabs.search || tabs.current === tabs.tabs.lyrics) {
|
||||
if (tabs.current === tabs.tabs.search) {
|
||||
tabs.switchToQueue();
|
||||
} else {
|
||||
tabs.switchToSearch();
|
||||
@@ -64,7 +91,23 @@ function handleButton() {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.clear_search {
|
||||
/* Style applied when clear_search class is active */
|
||||
visibility: visible;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.right > .gsearch-input > #ginner > input {
|
||||
width: 140px;
|
||||
|
||||
@include allPhones {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.gsearch-input {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
@@ -73,23 +116,31 @@ function handleButton() {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
// gap: $small;
|
||||
border-radius: 3rem;
|
||||
outline: solid 1px $gray3;
|
||||
background-color: $gray5;
|
||||
outline: solid 2px transparent;
|
||||
transition: outline-color 0.2s ease-out;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
width: 3rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
margin-left: 4px;
|
||||
border-radius: 3rem;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:hover {
|
||||
transition: all 0.2s ease;
|
||||
background-color: $gray2;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
button.no_bg {
|
||||
@@ -101,13 +152,59 @@ function handleButton() {
|
||||
border: none;
|
||||
line-height: 2.25rem;
|
||||
color: inherit;
|
||||
font-size: 1rem;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@include allPhones {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
padding-right: $small;
|
||||
}
|
||||
}
|
||||
|
||||
.clear_input {
|
||||
cursor: pointer;
|
||||
padding: 10px 1rem;
|
||||
border-top-right-radius: 3rem;
|
||||
border-bottom-right-radius: 3rem;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
|
||||
@include allPhones {
|
||||
border-radius: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.clear_input.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.clear_input.active:active {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
border-radius: unset;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@include allPhones {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.search-focused {
|
||||
outline: solid $darkblue !important;
|
||||
outline: solid 2px #fff;
|
||||
|
||||
@include allPhones {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
action: () => void;
|
||||
}>();
|
||||
|
||||
onMounted(() => {
|
||||
props.action();
|
||||
});
|
||||
</script>
|
||||
@@ -1,57 +1,89 @@
|
||||
<template>
|
||||
<div class="aboutswingmusic">
|
||||
Swing Music is a labor of love developed by
|
||||
<a href="https://github.com/cwilvx" target="_blank">@<u>cwilvx</u></a> on
|
||||
GitHub. If you like this software, please consider donating to support
|
||||
development and giving it a star on GitHub. <br /><br /><br />
|
||||
<div class="flex">
|
||||
<a href="https://swingmusic.vercel.app/support-us.html" target="_blank">
|
||||
<button>Donate</button></a
|
||||
><a href="https://github.com/cwilvx/swingmusic" target="_blank"
|
||||
><button>Star on Github</button></a
|
||||
>
|
||||
<a href="https://github.com/cwilvx" target="_blank"
|
||||
><button>Follow @cwilvx on Github</button></a
|
||||
>
|
||||
<div class="aboutswingmusic">
|
||||
<div class="version">Swing Music v{{ settings.version }}</div>
|
||||
Swing Music is a labor of love developed by
|
||||
<a
|
||||
href="https://github.com/cwilvx"
|
||||
target="_blank"
|
||||
>@<u>cwilvx</u></a
|
||||
>
|
||||
on GitHub. If you like this software, a star on GitHub would be nice.
|
||||
Hope you enjoy using it as much as I enjoy building it. 😁🤗
|
||||
<br /><br />
|
||||
<div class="links">
|
||||
<h2>Links</h2>
|
||||
<div class="flex">
|
||||
<a
|
||||
href="https://swingmusic.vercel.app/guide/introduction.html"
|
||||
target="_blank"
|
||||
><button>Docs</button></a
|
||||
>
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic"
|
||||
target="_blank"
|
||||
><button>Star on Github</button></a
|
||||
>
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic/issues/new/choose"
|
||||
target="_blank"
|
||||
>
|
||||
<button>Report issue</button>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic/blob/master/.github/contributing.md"
|
||||
target="_blank"
|
||||
><button>Contribute</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="contact">
|
||||
<h2>Get in touch</h2>
|
||||
If you like my work, and would like to say hi, I'd like to hear from
|
||||
you.
|
||||
<br /><br />
|
||||
<div class="flex">
|
||||
<a
|
||||
href="mailto:geoffreymungai45@gmail.com?subject=Hiii 👋😁&body=Hi Mungai,
|
||||
"
|
||||
target="_blank"
|
||||
><button>Send email</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br /><br />
|
||||
If you encounter any bugs, please open an issue on GitHub. If you would like
|
||||
to get involved in development, start with the
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic/blob/master/.github/contributing.md"
|
||||
target="_blank"
|
||||
><u>contribution guidelines</u></a
|
||||
>.
|
||||
|
||||
<br /><br /><br />
|
||||
<div class="flex">
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic/issues/new/choose"
|
||||
target="_blank"
|
||||
>
|
||||
<button>Open an Issue</button>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/cwilvx/swingmusic/blob/master/.github/contributing.md"
|
||||
target="_blank"
|
||||
><button>Contribute</button></a
|
||||
>
|
||||
</div>
|
||||
<br /><br />
|
||||
Let there be music 💃🕺!
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import useSettings from '@/stores/settings'
|
||||
|
||||
const settings = useSettings()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.aboutswingmusic {
|
||||
padding: $small;
|
||||
margin-top: 2rem;
|
||||
padding: $small;
|
||||
|
||||
.flex {
|
||||
gap: 1rem;
|
||||
}
|
||||
.version {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: $small;
|
||||
border-bottom: solid 1px $separator;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.links .flex {
|
||||
margin-top: $small;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.contact button {
|
||||
background-color: $blue;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: $smaller;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,78 +1,92 @@
|
||||
<template>
|
||||
<div class="list-items">
|
||||
<div v-for="i in items" :key="i.title" class="option-list-item">
|
||||
<div class="with-icon">
|
||||
<component :is="icon_" />
|
||||
<div class="text ellip">
|
||||
{{ i.title }}
|
||||
<div class="list-items">
|
||||
<div
|
||||
v-for="i in items"
|
||||
:key="i.title"
|
||||
class="option-list-item"
|
||||
>
|
||||
<div class="with-icon">
|
||||
<component :is="icon_" />
|
||||
<div class="text ellip">
|
||||
{{ i.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="icon"
|
||||
@click="i.action"
|
||||
>
|
||||
<DeleteSvg />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!items.length"
|
||||
class="option-list-item"
|
||||
style="opacity: 0.5"
|
||||
>
|
||||
Root directories not configured. Use the "Configure" button above to
|
||||
configure
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon" @click="i.action">
|
||||
<DeleteSvg />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!items.length" class="option-list-item" style="opacity: 0.5">
|
||||
Root directories not configured. Use the "Configure" button above to
|
||||
configure
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FolderSvg from "@/assets/icons/folder.svg";
|
||||
import DeleteSvg from "@/assets/icons/delete.svg";
|
||||
import DeleteSvg from '@/assets/icons/delete.svg'
|
||||
import FolderSvg from '@/assets/icons/folder.svg'
|
||||
|
||||
const props = defineProps<{
|
||||
items: {
|
||||
title: string;
|
||||
action: () => void;
|
||||
}[];
|
||||
icon: "folder";
|
||||
}>();
|
||||
items: {
|
||||
title: string
|
||||
action: () => void
|
||||
}[]
|
||||
icon: 'folder'
|
||||
}>()
|
||||
|
||||
function getIcon() {
|
||||
switch (props.icon) {
|
||||
case "folder":
|
||||
return FolderSvg;
|
||||
switch (props.icon) {
|
||||
case 'folder':
|
||||
return FolderSvg
|
||||
|
||||
default:
|
||||
return FolderSvg;
|
||||
}
|
||||
default:
|
||||
return FolderSvg
|
||||
}
|
||||
}
|
||||
|
||||
const icon_ = getIcon();
|
||||
const icon_ = getIcon()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.setting-item.is-list {
|
||||
$color: $gray5;
|
||||
.list-items {
|
||||
border: solid 1px $gray5;
|
||||
border-radius: $small;
|
||||
margin-top: 1rem;
|
||||
overflow: hidden;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.list-items {
|
||||
background-color: $color;
|
||||
border-radius: $small;
|
||||
margin-top: 1rem;
|
||||
overflow: hidden;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.option-list-item {
|
||||
padding: $small 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
.option-list-item {
|
||||
padding: $small 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
svg {
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
.with-icon {
|
||||
display: flex;
|
||||
gap: $small;
|
||||
align-items: center;
|
||||
font-family: "SF Mono", monospace;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
42
src/components/SettingsView/Components/LockedNumberInput.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="lockernumberinput">
|
||||
<button class="minus" @click="submit('minus')">-</button>
|
||||
<div class="number">{{ value }}{{ unit }}</div>
|
||||
<button class="plus" @click="submit('plus')">+</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
unit: string;
|
||||
onChange: (value: number) => void;
|
||||
}>();
|
||||
|
||||
function submit(action: "plus" | "minus") {
|
||||
const newValue = props.value + (action === "plus" ? props.step : -props.step);
|
||||
if (newValue < props.min || newValue > props.max) return;
|
||||
props.onChange(newValue);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.lockernumberinput {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
align-items: center;
|
||||
|
||||
.number {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 2.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||