chore: replace Sass with PostCSS

This commit is contained in:
Phan An 2024-04-04 22:13:35 +02:00
parent b81571ab29
commit 0b66f365b2
169 changed files with 1005 additions and 1245 deletions

View file

@ -11,6 +11,6 @@
export default (on: () => void, config: Record<string, unknown>): Record<string, unknown> => { export default (on: () => void, config: Record<string, unknown>): Record<string, unknown> => {
return Object.assign({}, config, { return Object.assign({}, config, {
supportFile: 'cypress/support/index.ts' supportFile: 'cypress/support/main.ts'
}) })
} }

View file

@ -13,7 +13,7 @@ defineOptions({
</figure> </figure>
</template> </template>
<style scoped lang="scss"> <style scoped lang="postcss">
figure { figure {
margin: 1.5rem 0; margin: 1.5rem 0;
overflow: hidden; overflow: hidden;

View file

@ -15,7 +15,7 @@ import downloadedScreenshot from '../../assets/img/mobile/downloaded.webp'
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
div { div {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);

View file

@ -5,7 +5,7 @@
<script lang="ts" setup> <script lang="ts" setup>
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
span { span {
display: inline-block; display: inline-block;

View file

@ -19,7 +19,7 @@ defineOptions({
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
img { img {
opacity: 0.8; opacity: 0.8;

View file

@ -52,7 +52,7 @@ import doLogo from '../../assets/img/sponsors/do.svg'
import whatTheDiffLogo from '../../assets/img/sponsors/what-the-diff.svg' import whatTheDiffLogo from '../../assets/img/sponsors/what-the-diff.svg'
</script> </script>
<style lang="scss"> <style lang="postcss">
div.sponsors { div.sponsors {
border-left: 1px solid var(--vp-c-divider); border-left: 1px solid var(--vp-c-divider);
padding-left: 16px; padding-left: 16px;

View file

@ -30,7 +30,7 @@ const themes: Theme[] = [
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
div .theme + .theme { div .theme + .theme {
margin-left: -100%; margin-left: -100%;
} }

View file

@ -15,7 +15,7 @@ import Sponsors from '../components/Sponsors.vue'
const { Layout: BaseLayout } = DefaultTheme const { Layout: BaseLayout } = DefaultTheme
</script> </script>
<style lang="scss"> <style lang="postcss">
.aside-outline-after { .aside-outline-after {
margin-top: 1.5rem; margin-top: 1.5rem;
} }

View file

@ -69,7 +69,7 @@ onMounted(() => {
}) })
</script> </script>
<style lang="scss" module> <style lang="postcss" module>
ol img { ol img {
margin: 1.2rem 0; margin: 1.2rem 0;
} }

View file

@ -54,6 +54,7 @@
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"@vueuse/integrations": "^10.9.0", "@vueuse/integrations": "^10.9.0",
"add": "^2.0.6", "add": "^2.0.6",
"autoprefixer": "^10.4.19",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "^9.5.4", "cypress": "^9.5.4",
"eslint": "^8.14.0", "eslint": "^8.14.0",
@ -72,8 +73,9 @@
"laravel-vite-plugin": "^1.0.2", "laravel-vite-plugin": "^1.0.2",
"lint-staged": "^10.3.0", "lint-staged": "^10.3.0",
"lucide-vue-next": "^0.363.0", "lucide-vue-next": "^0.363.0",
"postcss": "^8.4.38",
"postcss-nested": "^6.0.1",
"qrcode": "^1", "qrcode": "^1",
"sass": "^1.72.0",
"start-server-and-test": "^2.0.3", "start-server-and-test": "^2.0.3",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"vite": "^5.1.6", "vite": "^5.1.6",

6
postcss.config.cjs Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: [
require('postcss-nested'),
require('autoprefixer')
]
}

View file

@ -1,10 +1,22 @@
@mixin vertical-center() { @import './vendor/reset.pcss';
@import './partials/vars.pcss';
@import './partials/hack.pcss';
@import '@modules/nouislider/distribute/nouislider.min.css';
@import './vendor/plyr.pcss';
@import './vendor/nprogress.pcss';
@import './partials/skeleton.pcss';
@import './partials/tooltip.pcss';
@import './partials/shared.pcss';
.vertical-center {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
@mixin artist-album-wrapper() { .artist-album-wrapper {
display: grid !important; display: grid !important;
gap: 16px; gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
@ -24,9 +36,11 @@
} }
} }
@mixin artist-album-info-wrapper() { .artist-album-info-wrapper {
.loading { .loading {
@include vertical-center(); display: flex;
align-items: center;
justify-content: center;
height: 100%; height: 100%;
} }
@ -62,12 +76,13 @@
} }
} }
@mixin artist-album-info() { .artist-album-info {
color: var(--color-text-secondary); color: var(--color-text-secondary);
h1 { h1 {
@include vertical-center(); display: flex;
align-items: center;
justify-content: center;
font-weight: var(--font-weight-thin); font-weight: var(--font-weight-thin);
line-height: 2.8rem; line-height: 2.8rem;
margin-bottom: 16px; margin-bottom: 16px;
@ -154,13 +169,13 @@
} }
} }
@mixin inset-when-pressed() { .inset-when-pressed {
&:not([disabled]):active { &:not([disabled]):active {
box-shadow: inset 0 10px 10px -10px rgba(0, 0, 0, .6); box-shadow: inset 0 10px 10px -10px rgba(0, 0, 0, .6);
} }
} }
@mixin context-menu() { .context-menu {
padding: .4rem 0; padding: .4rem 0;
width: max-content; width: max-content;
min-width: 144px; min-width: 144px;
@ -179,7 +194,7 @@
} }
} }
@mixin themed-background() { .themed-background, body, html {
background-color: var(--color-bg-primary); background-color: var(--color-bg-primary);
background-image: var(--bg-image); background-image: var(--bg-image);
background-attachment: var(--bg-attachment); background-attachment: var(--bg-attachment);

View file

@ -23,8 +23,6 @@ h1, h2, h3, h4, h5, h6, blockquote {
body, body,
html { html {
@include themed-background();
color: var(--color-text-primary); color: var(--color-text-primary);
font-family: var(--font-family); font-family: var(--font-family);
font-size: 13px; font-size: 13px;
@ -130,11 +128,6 @@ a {
} }
} }
.ir {
color: transparent;
font: 0/0 serif;
}
.control { .control {
cursor: pointer; cursor: pointer;
color: var(--color-text-secondary); color: var(--color-text-secondary);
@ -174,6 +167,8 @@ label {
.tabs { .tabs {
min-height: 100%; min-height: 100%;
display: flex;
flex-direction: column;
position: relative; position: relative;
[role=tablist] { [role=tablist] {
@ -182,6 +177,7 @@ label {
padding: 0 1.25rem; padding: 0 1.25rem;
display: flex; display: flex;
gap: 0.3rem; gap: 0.3rem;
min-height: 36px;
[role=tab] { [role=tab] {
position: relative; position: relative;
@ -344,7 +340,6 @@ label {
.context-menu, .context-menu,
.submenu, .submenu,
menu { menu {
@include context-menu();
position: fixed; position: fixed;
@keyframes subMenuHoverHelp { @keyframes subMenuHoverHelp {

View file

@ -1,18 +1,13 @@
.skeleton { .skeleton {
.pulse, &.pulse {
.pulse,
&.pulse {
animation: skeleton-pulse 2s infinite; animation: skeleton-pulse 2s infinite;
background-color: rgba(255, 255, 255, .05); background-color: rgba(255, 255, 255, .05);
} }
@keyframes skeleton-pulse { @keyframes skeleton-pulse {
0%, 100% {
0%,
100% {
opacity: 0; opacity: 0;
} }
50% { 50% {
opacity: 1; opacity: 1;
} }

View file

@ -0,0 +1,4 @@
@import './vendor/reset.pcss';
@import '@modules/nouislider/distribute/nouislider.min.css';
@import './partials/vars.pcss';
@import './partials/shared.pcss';

583
resources/assets/css/vendor/plyr.pcss vendored Normal file
View file

@ -0,0 +1,583 @@
.plyr__captions, .plyr__controls {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-align: center
}
.plyr__tooltip, .plyr__video-embed.plyr iframe {
pointer-events: none
}
@-webkit-keyframes plyr-progress {
to {
background-position: 40px 0
}
}
@keyframes plyr-progress {
to {
background-position: 40px 0
}
}
.plyr {
position: relative;
max-width: 100%;
min-width: 290px;
}
.plyr, .plyr *, .plyr ::after, .plyr ::before {
box-sizing: border-box
}
.plyr a, .plyr button, .plyr input, .plyr label {
-ms-touch-action: manipulation;
touch-action: manipulation
}
.plyr__sr-only {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important;
border: 0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden
}
.plyr__video-wrapper {
position: relative
}
.plyr audio, .plyr video {
width: 100%;
height: auto;
vertical-align: middle
}
.plyr__video-embed {
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
background: #000
}
.plyr__video-embed iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
user-select: none
}
.plyr__video-embed > div {
position: relative;
padding-bottom: 200%;
transform: translateY(-35.95%)
}
.plyr__captions {
display: none;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 20px 20px 30px;
color: #fff;
font-size: 20px
}
.plyr__captions span {
border-radius: 2px;
padding: 3px 10px;
background: rgba(0, 0, 0, .9)
}
.plyr__captions span:empty {
display: none
}
@media (min-width: 768px) {
.plyr__captions {
font-size: 24px
}
}
.plyr--captions-active .plyr__captions {
display: block
}
.plyr--fullscreen-active .plyr__captions {
font-size: 32px
}
.plyr__controls {
position: relative;
padding: 10px;
background: #fff;
line-height: 1;
box-shadow: 0 1px 1px rgba(52, 63, 74, .2)
}
.plyr__controls::after {
content: '';
display: table;
clear: both
}
.plyr__controls--right {
display: block;
margin: 10px auto 0
}
@media (min-width: 560px) {
.plyr__controls--left {
float: left
}
.plyr__controls--right {
float: right;
margin-top: 0
}
}
.plyr__controls button {
display: inline-block;
vertical-align: middle;
margin: 0 2px;
padding: 5px 10px;
overflow: hidden;
border: 0;
background: 0 0;
border-radius: 3px;
cursor: pointer;
color: var(--color-highlight);
transition: background .3s ease, color .3s ease, opacity .3s ease
}
.plyr__controls button svg {
width: 18px;
height: 18px;
display: block;
fill: currentColor;
transition: fill .3s ease
}
.plyr__controls button.tab-focus:focus, .plyr__controls button:hover {
background: #3498DB;
color: #fff
}
.plyr__controls .plyr__time, .plyr__tooltip {
color: var(--color-highlight);
font-size: 14px;
font-weight: 600
}
.plyr__controls button:focus {
outline: 0
}
.plyr__controls .icon--captions-on, .plyr__controls .icon--exit-fullscreen, .plyr__controls .icon--muted {
display: none
}
.plyr__controls .plyr__time {
display: inline-block;
vertical-align: middle;
margin-left: 10px
}
.plyr__controls .plyr__time + .plyr__time {
display: none
}
@media (min-width: 560px) {
.plyr__controls .plyr__time + .plyr__time {
display: inline-block
}
}
.plyr__controls .plyr__time + .plyr__time::before {
content: '\2044';
margin-right: 10px
}
.plyr__tooltip {
visibility: hidden;
position: absolute;
z-index: 2;
bottom: 100%;
margin-bottom: 10px;
padding: 10px 15px;
opacity: 0;
background: #fff;
box-shadow: 0 0 5px rgba(64, 64, 64, .1), 0 0 0 1px rgba(64, 64, 64, .1);
border-radius: 3px;
line-height: 1.5;
transform: translate(-50%, 10px) scale(.8);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
transition: transform .2s .1s ease, opacity .2s .1s ease, visibility .3s ease;
}
.plyr__tooltip::after, .plyr__tooltip::before {
content: '';
position: absolute;
width: 0;
height: 0;
top: 100%;
left: 50%;
transform: translateX(-50%)
}
.plyr__tooltip::after {
bottom: -8px;
border-right: 7px solid transparent;
border-top: 7px solid rgba(64, 64, 64, .2);
border-left: 7px solid transparent;
z-index: 1
}
.plyr__tooltip::before {
bottom: -6px;
border-right: 6px solid transparent;
border-top: 6px solid #fff;
border-left: 6px solid transparent;
z-index: 2
}
.plyr button.tab-focus:focus .plyr__tooltip, .plyr button:hover .plyr__tooltip, .plyr__tooltip--visible {
visibility: visible;
opacity: 1;
transform: translate(-50%, 0) scale(1)
}
.plyr button:hover .plyr__tooltip {
z-index: 3
}
.plyr input[type=range]::-ms-tooltip {
display: none
}
.plyr input[type=range].tab-focus:focus {
outline: rgba(52, 63, 74, .8) dotted 1px;
outline-offset: 3px
}
.plyr__progress--seek[type=range]:focus, .plyr__volume[type=range]:focus {
outline: 0
}
.plyr__progress {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
width: 100%;
height: 10px;
background: transparent;
}
.plyr__progress--buffer[value], .plyr__progress--played[value], .plyr__progress--seek[type=range] {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 4px;
margin: 0;
padding: 0;
vertical-align: top;
-webkit-appearance: none;
-moz-appearance: none;
border: none;
background: 0 0
}
.plyr__progress--buffer[value]::-webkit-progress-bar, .plyr__progress--played[value]::-webkit-progress-bar {
background: 0 0;
transition: width .2s ease
}
.plyr__progress--buffer[value]::-webkit-progress-value, .plyr__progress--played[value]::-webkit-progress-value {
background: currentColor;
transition: width .2s ease
}
.plyr__progress--buffer[value]::-moz-progress-bar, .plyr__progress--played[value]::-moz-progress-bar {
background: currentColor;
transition: width .2s ease
}
.plyr__progress--played[value] {
z-index: 2;
color: #3498DB
}
.plyr__progress--buffer[value] {
color: rgba(86, 93, 100, .25)
}
.plyr__progress--seek[type=range] {
z-index: 4;
cursor: pointer;
outline: 0;
height: 10px; /* increase height for touch */
}
.plyr__progress--seek[type=range]::-webkit-slider-runnable-track {
background: 0 0;
border: 0
}
.plyr__progress--seek[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
background: 0 0;
border: 0;
width: 1px;
height: 10px
}
.plyr__progress--seek[type=range]::-moz-range-track {
background: 0 0;
border: 0
}
.plyr__progress--seek[type=range]::-moz-range-thumb {
-moz-appearance: none;
background: 0 0;
border: 0;
width: 1px;
height: 10px
}
.plyr__progress--seek[type=range]::-ms-track {
color: transparent;
background: 0 0;
border: 0
}
.plyr__progress--seek[type=range]::-ms-fill-lower, .plyr__progress--seek[type=range]::-ms-fill-upper {
background: 0 0;
border: 0
}
.plyr__progress--seek[type=range]::-ms-thumb {
background: 0 0;
border: 0;
width: 1px;
height: 10px
}
.plyr__progress--seek[type=range]::-moz-focus-outer {
border: 0
}
.plyr__progress .plyr__tooltip {
left: 0
}
.plyr--is-touch .plyr--seek[type=range]::-webkit-slider-thumb {
width: 40px;
transform: translateX(-50%)
}
.plyr--is-touch .plyr--seek[type=range]::-moz-range-thumb {
width: 40px;
transform: translateX(-50%)
}
.plyr--is-touch .plyr--seek[type=range]::-ms-thumb {
width: 40px;
transform: translateX(-50%)
}
.plyr--loading .plyr__progress--buffer {
-webkit-animation: plyr-progress 1s linear infinite;
animation: plyr-progress 1s linear infinite;
background-size: 40px 40px;
background-repeat: repeat-x;
background-color: rgba(86, 93, 100, .25);
background-image: linear-gradient(-45deg, rgba(0, 0, 0, .15) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, .15) 50%, rgba(0, 0, 0, .15) 75%, transparent 75%, transparent);
color: transparent
}
.plyr--playing .plyr__controls [data-plyr=play], .plyr__controls [data-plyr=pause] {
display: none
}
.plyr--playing .plyr__controls [data-plyr=pause] {
display: inline-block
}
.plyr__volume[type=range] {
display: inline-block;
vertical-align: middle;
-webkit-appearance: none;
-moz-appearance: none;
width: 100px;
margin: 0 10px 0 0;
padding: 0;
cursor: pointer;
background: transparent;
border: none
}
.plyr__volume[type=range]::-webkit-slider-runnable-track {
height: 6px;
background: rgba(255, 255, 255, .2);
border: 0;
border-radius: 3px
}
.plyr__volume[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
margin-top: -3px;
height: 12px;
width: 12px;
background: var(--color-highlight);
border: 0;
border-radius: 100%;
transition: background .3s ease;
cursor: ew-resize
}
.plyr__volume[type=range]::-moz-range-track {
height: 6px;
background: rgba(255, 255, 255, .2);
border: 0;
border-radius: 3px
}
.plyr__volume[type=range]::-moz-range-thumb {
height: 12px;
width: 12px;
background: var(--color-highlight);
border: 0;
border-radius: 100%;
transition: background .3s ease;
cursor: ew-resize
}
.plyr__volume[type=range]::-ms-track {
height: 6px;
background: rgba(255, 255, 255, .2);
border-color: transparent;
border-width: 3px 0;
color: transparent
}
.plyr__volume[type=range]::-ms-fill-lower, .plyr__volume[type=range]::-ms-fill-upper {
height: 6px;
background: rgba(255, 255, 255, .2);
border: 0;
border-radius: 3px
}
.plyr__volume[type=range]::-ms-thumb {
height: 12px;
width: 12px;
background: var(--color-highlight);
border: 0;
border-radius: 100%;
transition: background .3s ease;
cursor: ew-resize
}
.plyr__volume[type=range]:focus::-webkit-slider-thumb {
background: transparent;
}
.plyr__volume[type=range]:focus::-moz-range-thumb {
background: #3498DB
}
.plyr__volume[type=range]:focus::-ms-thumb {
background: #3498DB
}
.plyr--is-ios .plyr__volume, .plyr--is-ios [data-plyr=mute], .plyr--is-ios.plyr--audio .plyr__controls--right {
display: none
}
.plyr--is-ios.plyr--audio .plyr__controls--left {
float: none
}
.plyr--audio .plyr__controls {
padding-top: 20px
}
.plyr--audio .plyr__progress {
bottom: auto;
top: 0;
background: transparent;
}
.plyr--fullscreen-active, .plyr.plyr--fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100%;
width: 100%;
z-index: 10000000;
background: #000
}
.plyr--fullscreen-active video, .plyr.plyr--fullscreen video {
height: 100%
}
.plyr--fullscreen-active .plyr__video-wrapper, .plyr.plyr--fullscreen .plyr__video-wrapper {
height: 100%;
width: 100%
}
.plyr--fullscreen-active .plyr__controls, .plyr.plyr--fullscreen .plyr__controls {
position: absolute;
bottom: 0;
left: 0;
right: 0
}
.plyr--fullscreen-active.plyr--fullscreen--hide-controls.plyr--playing .plyr__controls, .plyr.plyr--fullscreen.plyr--fullscreen--hide-controls.plyr--playing .plyr__controls {
transform: translateY(100%) translateY(5px);
transition: transform .3s .2s ease;
}
.plyr--fullscreen-active.plyr--fullscreen--hide-controls.plyr--playing .plyr__captions, .plyr.plyr--fullscreen.plyr--fullscreen--hide-controls.plyr--playing .plyr__captions {
bottom: 5px;
transition: bottom .3s .2s ease
}
.plyr--fullscreen-active.plyr--fullscreen--hide-controls.plyr--playing.plyr--hover .plyr__controls, .plyr.plyr--fullscreen.plyr--fullscreen--hide-controls.plyr--playing.plyr--hover .plyr__controls {
transform: translateY(0)
}
.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing.plyr--hover .plyr__captions, .plyr--fullscreen-active .plyr__captions, .plyr.plyr--fullscreen .plyr__captions {
top: auto;
bottom: 90px
}
@media (min-width: 560px) {
.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing.plyr--hover .plyr__captions, .plyr--fullscreen-active .plyr__captions, .plyr.plyr--fullscreen .plyr__captions {
bottom: 60px
}
}
.plyr--captions-active .plyr__controls .icon--captions-on, .plyr--fullscreen-active .icon--exit-fullscreen, .plyr--muted .plyr__controls .icon--muted {
display: block
}
.plyr [data-plyr=fullscreen], .plyr [data-plyr=captions], .plyr--captions-active .plyr__controls .icon--captions-on + svg, .plyr--fullscreen-active .icon--exit-fullscreen + svg, .plyr--muted .plyr__controls .icon--muted + svg {
display: none
}
.plyr--captions-enabled [data-plyr=captions], .plyr--fullscreen-enabled [data-plyr=fullscreen] {
display: inline-block
}

34
resources/assets/css/vendor/reset.pcss vendored Normal file
View file

@ -0,0 +1,34 @@
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline
}
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block
}
body {
line-height: 1
}
ol, ul {
list-style: none
}
blockquote, q {
quotes: none
}
blockquote:before, blockquote:after, q:before, q:after {
content: '';
content: none
}
table {
border-collapse: collapse;
border-spacing: 0
}

View file

@ -26,7 +26,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, nextTick, onMounted, provide, ref, watch } from 'vue' import { defineAsyncComponent, onMounted, provide, ref, watch } from 'vue'
import { useOnline } from '@vueuse/core' import { useOnline } from '@vueuse/core'
import { commonStore, preferenceStore as preferences, queueStore } from '@/stores' import { commonStore, preferenceStore as preferences, queueStore } from '@/stores'
import { authService, socketListener, socketService, uploadService } from '@/services' import { authService, socketListener, socketService, uploadService } from '@/services'
@ -160,9 +160,7 @@ provide(MessageToasterKey, toaster)
provide(CurrentSongKey, currentSong) provide(CurrentSongKey, currentSong)
</script> </script>
<style lang="scss"> <style lang="postcss">
@import "#/app.scss";
#dragGhost { #dragGhost {
display: inline-block; display: inline-block;
background: var(--color-green); background: var(--color-green);

View file

@ -4,6 +4,7 @@ import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome
import { RouterKey } from '@/symbols' import { RouterKey } from '@/symbols'
import { routes } from '@/config' import { routes } from '@/config'
import Router from '@/router' import Router from '@/router'
import '@/../css/app.pcss'
import App from './App.vue' import App from './App.vue'
createApp(App) createApp(App)

View file

@ -1,5 +1,5 @@
<template> <template>
<article :class="mode" class="album-info" data-testid="album-info"> <article :class="mode" class="album-info artist-album-info" data-testid="album-info">
<h1 v-if="mode === 'aside'" class="name"> <h1 v-if="mode === 'aside'" class="name">
<span>{{ album.name }}</span> <span>{{ album.name }}</span>
<button :title="`Play all songs in ${album.name}`" class="control" type="button" @click.prevent="play"> <button :title="`Play all songs in ${album.name}`" class="control" type="button" @click.prevent="play">
@ -68,17 +68,3 @@ const play = async () => {
go('queue') go('queue')
} }
</script> </script>
<style lang="scss" scoped>
.album-info {
@include artist-album-info();
.track-listing {
margin-top: 2rem;
:deep(h1) {
margin-bottom: 1.2rem;
}
}
}
</style>

View file

@ -1,13 +1,13 @@
<template> <template>
<section class="track-listing"> <article class="track-listing">
<h1>Track Listing</h1> <h3>Track Listing</h3>
<ul class="tracks"> <ul class="tracks">
<li v-for="(track, index) in tracks" :key="index" data-testid="album-track-item"> <li v-for="(track, index) in tracks" :key="index" data-testid="album-track-item">
<TrackListItem :album="album" :track="track" /> <TrackListItem :album="album" :track="track" />
</li> </li>
</ul> </ul>
</section> </article>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -28,12 +28,11 @@ provide(SongsKey, songs)
onMounted(async () => songs.value = await songStore.fetchForAlbum(album.value)) onMounted(async () => songs.value = await songStore.fetchForAlbum(album.value))
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
section { article {
h1 { h3 {
font-size: 1.4rem; font-size: 1.4rem;
margin-bottom: 0; margin-bottom: 1rem;
display: block;
} }
ul { ul {

View file

@ -47,7 +47,7 @@ const play = () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.track-list-item { .track-list-item {
display: flex; display: flex;
flex: 1; flex: 1;

View file

@ -1,5 +1,5 @@
<template> <template>
<article :class="mode" class="artist-info" data-testid="artist-info"> <article :class="mode" class="artist-info artist-album-info" data-testid="artist-info">
<h1 v-if="mode === 'aside'" class="name"> <h1 v-if="mode === 'aside'" class="name">
<span>{{ artist.name }}</span> <span>{{ artist.name }}</span>
<button :title="`Play all songs by ${artist.name}`" class="control" type="button" @click.prevent="play"> <button :title="`Play all songs by ${artist.name}`" class="control" type="button" @click.prevent="play">
@ -65,10 +65,8 @@ const play = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.artist-info { .artist-info {
@include artist-album-info();
.none { .none {
margin-top: 1rem; margin-top: 1rem;
} }

View file

@ -45,7 +45,7 @@ const requestResetPasswordLink = async () => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
form { form {
min-width: 480px; min-width: 480px;

View file

@ -86,7 +86,7 @@ const onSSOSuccess = (token: CompositeToken) => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
/** /**
* I like to move it move it * I like to move it move it
* I like to move it move it * I like to move it move it

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="reset-password-wrapper"> <div class="reset-password-wrapper vertical-center">
<form v-if="validPayload" @submit.prevent="submit"> <form v-if="validPayload" @submit.prevent="submit">
<h1 class="font-size-1.5">Set New Password</h1> <h1 class="font-size-1.5">Set New Password</h1>
<div> <div>
@ -55,10 +55,8 @@ const submit = async () => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.reset-password-wrapper { .reset-password-wrapper {
@include vertical-center;
height: 100vh; height: 100vh;
} }

View file

@ -4,7 +4,7 @@ exports[`renders 1`] = `
<div data-v-0b0f87ea="" class="login-wrapper"> <div data-v-0b0f87ea="" class="login-wrapper">
<form data-v-0b0f87ea="" class="" data-testid="login-form"> <form data-v-0b0f87ea="" class="" data-testid="login-form">
<div data-v-0b0f87ea="" class="logo"><img data-v-0b0f87ea="" alt="Koel's logo" src="undefined/resources/assets/img/logo.svg" width="156"></div><input data-v-0b0f87ea="" autofocus="" placeholder="Email Address" required="" type="email"> <div data-v-0b0f87ea="" class="logo"><img data-v-0b0f87ea="" alt="Koel's logo" src="undefined/resources/assets/img/logo.svg" width="156"></div><input data-v-0b0f87ea="" autofocus="" placeholder="Email Address" required="" type="email">
<div data-v-a2893005="" data-v-0b0f87ea=""><input data-v-a2893005="" type="password" placeholder="Password" required=""><button data-v-a2893005="" type="button"><br data-v-a2893005="" data-testid="Icon" icon="[object Object]"></button></div><button data-v-e368fe26="" data-v-0b0f87ea="" type="submit">Log In</button><a data-v-0b0f87ea="" class="reset-password" role="button"> Forgot password? </a> <div data-v-a2893005="" data-v-0b0f87ea=""><input data-v-a2893005="" type="password" placeholder="Password" required=""><button data-v-a2893005="" type="button"><br data-v-a2893005="" data-testid="Icon" icon="[object Object]"></button></div><button data-v-e368fe26="" data-v-0b0f87ea="" class="inset-when-pressed" type="submit">Log In</button><a data-v-0b0f87ea="" class="reset-password" role="button"> Forgot password? </a>
</form> </form>
<!--v-if--> <!--v-if-->
<!--v-if--> <!--v-if-->
@ -15,7 +15,7 @@ exports[`shows Google login button 1`] = `
<div data-v-0b0f87ea="" class="login-wrapper"> <div data-v-0b0f87ea="" class="login-wrapper">
<form data-v-0b0f87ea="" class="" data-testid="login-form"> <form data-v-0b0f87ea="" class="" data-testid="login-form">
<div data-v-0b0f87ea="" class="logo"><img data-v-0b0f87ea="" alt="Koel's logo" src="undefined/resources/assets/img/logo.svg" width="156"></div><input data-v-0b0f87ea="" autofocus="" placeholder="Email Address" required="" type="email"> <div data-v-0b0f87ea="" class="logo"><img data-v-0b0f87ea="" alt="Koel's logo" src="undefined/resources/assets/img/logo.svg" width="156"></div><input data-v-0b0f87ea="" autofocus="" placeholder="Email Address" required="" type="email">
<div data-v-a2893005="" data-v-0b0f87ea=""><input data-v-a2893005="" type="password" placeholder="Password" required=""><button data-v-a2893005="" type="button"><br data-v-a2893005="" data-testid="Icon" icon="[object Object]"></button></div><button data-v-e368fe26="" data-v-0b0f87ea="" type="submit">Log In</button><a data-v-0b0f87ea="" class="reset-password" role="button"> Forgot password? </a> <div data-v-a2893005="" data-v-0b0f87ea=""><input data-v-a2893005="" type="password" placeholder="Password" required=""><button data-v-a2893005="" type="button"><br data-v-a2893005="" data-testid="Icon" icon="[object Object]"></button></div><button data-v-e368fe26="" data-v-0b0f87ea="" class="inset-when-pressed" type="submit">Log In</button><a data-v-0b0f87ea="" class="reset-password" role="button"> Forgot password? </a>
</form> </form>
<div data-v-0b0f87ea="" class="sso"><br data-v-0b0f87ea="" data-testid="google-login-button"></div> <div data-v-0b0f87ea="" class="sso"><br data-v-0b0f87ea="" data-testid="google-login-button"></div>
<!--v-if--> <!--v-if-->

View file

@ -23,7 +23,7 @@ const loginWithGoogle = async () => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
button { button {
opacity: .5; opacity: .5;

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="invitation-wrapper"> <div class="invitation-wrapper vertical-center">
<form v-if="userProspect" autocomplete="off" @submit.prevent="submit"> <form v-if="userProspect" autocomplete="off" @submit.prevent="submit">
<header> <header>
Welcome to Koel! To accept the invitation, fill in the form below and click that button. Welcome to Koel! To accept the invitation, fill in the form below and click that button.
@ -92,10 +92,8 @@ onMounted(async () => {
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.invitation-wrapper { .invitation-wrapper {
@include vertical-center();
display: flex; display: flex;
height: 100vh; height: 100vh;
flex-direction: column; flex-direction: column;

View file

@ -40,7 +40,7 @@ const validateLicenseKey = async () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
form { form {
display: flex; display: flex;
align-items: stretch; align-items: stretch;

View file

@ -1,5 +1,5 @@
<template> <template>
<a href class="upgrade-to-plus-btn" @click.prevent="openModal"> <a href class="upgrade-to-plus-btn inset-when-pressed" @click.prevent="openModal">
<Icon :icon="faPlus" fixed-width /> <Icon :icon="faPlus" fixed-width />
Upgrade to Plus Upgrade to Plus
</a> </a>
@ -12,10 +12,8 @@ import { eventBus } from '@/utils'
const openModal = () => eventBus.emit('MODAL_SHOW_KOEL_PLUS') const openModal = () => eventBus.emit('MODAL_SHOW_KOEL_PLUS')
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
a.upgrade-to-plus-btn { a.upgrade-to-plus-btn {
@include inset-when-pressed();
background: linear-gradient(97.78deg, #671ce4 17.5%, #c62be8 113.39%); background: linear-gradient(97.78deg, #671ce4 17.5%, #c62be8 113.39%);
border-radius: 5px; border-radius: 5px;
border-style: solid; border-style: solid;
@ -26,7 +24,7 @@ a.upgrade-to-plus-btn {
} }
&:active { &:active {
padding: .65rem 1rem; // prevent layout jump in sidebar padding: .65rem 1rem; /* prevent layout jump in sidebar */
} }
} }
</style> </style>

View file

@ -55,7 +55,7 @@ const hideActivateLicenseForm = () => (showingActivateLicenseForm.value = false)
onMounted(() => window.createLemonSqueezy?.()) onMounted(() => window.createLemonSqueezy?.())
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.plus { .plus {
max-width: 480px; max-width: 480px;
display: flex; display: flex;

View file

@ -85,7 +85,7 @@ eventBus.on('MODAL_SHOW_ABOUT_KOEL', () => (activeModalName.value = 'about-koel'
.on('MODAL_SHOW_EQUALIZER', () => (activeModalName.value = 'equalizer')) .on('MODAL_SHOW_EQUALIZER', () => (activeModalName.value = 'equalizer'))
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
dialog { dialog {
border: 0; border: 0;
padding: 0; padding: 0;

View file

@ -10,8 +10,8 @@
<script lang="ts" setup> <script lang="ts" setup>
</script> </script>
<style lang="scss"> <style lang="postcss">
// can't be scoped as it would be overridden by the plyr css /* can't be scoped as it would be overridden by the plyr css */
.plyr { .plyr {
width: 100%; width: 100%;
height: 4px; height: 4px;

View file

@ -62,7 +62,7 @@ onMounted(() => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.extra-controls { .extra-controls {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View file

@ -36,7 +36,7 @@ const playPrev = async () => await playbackService.playPrev()
const playNext = async () => await playbackService.playNext() const playNext = async () => await playbackService.playNext()
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.playback-controls { .playback-controls {
flex: 1; flex: 1;
display: flex; display: flex;

View file

@ -33,7 +33,7 @@ const onDragStart = (event: DragEvent) => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.song-info { .song-info {
padding: 0 1.5rem; padding: 0 1.5rem;
display: flex; display: flex;

View file

@ -3,7 +3,7 @@
exports[`renders 1`] = ` exports[`renders 1`] = `
<div data-v-8bf5fe81="" class="extra-controls" data-testid="other-controls"> <div data-v-8bf5fe81="" class="extra-controls" data-testid="other-controls">
<div data-v-8bf5fe81="" class="wrapper"><button data-v-8bf5fe81="" class="visualizer-btn" data-testid="toggle-visualizer-btn" title="Toggle visualizer"><br data-v-8bf5fe81="" data-testid="Icon" icon="[object Object]"></button> <div data-v-8bf5fe81="" class="wrapper"><button data-v-8bf5fe81="" class="visualizer-btn" data-testid="toggle-visualizer-btn" title="Toggle visualizer"><br data-v-8bf5fe81="" data-testid="Icon" icon="[object Object]"></button>
<!--v-if--><span data-v-8bf5fe81="" id="volume" class="volume muted"><span role="button" tabindex="0" title="Unmute"><br data-testid="Icon" icon="[object Object]" fixed-width=""></span><span role="button" tabindex="0" title="Mute" style="display: none;"><br data-testid="Icon" icon="[object Object]" fixed-width=""></span><input class="plyr__volume" max="10" role="slider" step="0.1" title="Volume" type="range"></span> <!--v-if--><span data-v-c7afcfc4="" data-v-8bf5fe81="" id="volume" class="volume muted"><span data-v-c7afcfc4="" role="button" tabindex="0" title="Unmute"><br data-v-c7afcfc4="" data-testid="Icon" icon="[object Object]" fixed-width=""></span><span data-v-c7afcfc4="" role="button" tabindex="0" title="Mute" style="display: none;"><br data-v-c7afcfc4="" data-testid="Icon" icon="[object Object]" fixed-width=""></span><input data-v-c7afcfc4="" class="plyr__volume" max="10" role="slider" step="0.1" title="Volume" type="range"></span>
<!--v-if--> <!--v-if-->
</div> </div>
</div> </div>

View file

@ -99,7 +99,7 @@ watch(isFullscreen, fullscreen => {
eventBus.on('FULLSCREEN_TOGGLE', () => toggleFullscreen()) eventBus.on('FULLSCREEN_TOGGLE', () => toggleFullscreen())
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
footer { footer {
background-color: var(--color-bg-secondary); background-color: var(--color-bg-secondary);
background-size: 0; background-size: 0;

View file

@ -123,7 +123,7 @@ const logout = () => eventBus.emit('LOG_OUT')
onMounted(() => isMobile.any || (activeTab.value = preferenceStore.active_extra_panel_tab)) onMounted(() => isMobile.any || (activeTab.value = preferenceStore.active_extra_panel_tab))
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
aside { aside {
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
@ -132,7 +132,12 @@ aside {
z-index: 2; z-index: 2;
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
@include themed-background(); background-color: var(--color-bg-primary);
background-image: var(--bg-image);
background-attachment: var(--bg-attachment);
background-size: var(--bg-size);
background-position: var(--bg-position);
flex-direction: column; flex-direction: column;
position: fixed; position: fixed;
top: 0; top: 0;
@ -152,7 +157,7 @@ aside {
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
@media (hover: none) { @media (hover: none) {
// Enable scroll with momentum on touch devices /* Enable scroll with momentum on touch devices */
overflow-y: scroll; overflow-y: scroll;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
@ -219,6 +224,7 @@ aside {
&:hover, &.active { &:hover, &.active {
opacity: 1; opacity: 1;
color: var(--color-text-primary); color: var(--color-text-primary);
background: rgba(255, 255, 255, .1);
} }
&:active { &:active {

View file

@ -78,7 +78,7 @@ onMounted(async () => {
}) })
</script> </script>
<style lang="scss"> <style lang="postcss">
#mainContent { #mainContent {
flex: 1; flex: 1;
position: relative; position: relative;
@ -113,7 +113,7 @@ onMounted(async () => {
place-content: start; place-content: start;
@media (hover: none) { @media (hover: none) {
// Enable scroll with momentum on touch devices /* Enable scroll with momentum on touch devices */
overflow-y: scroll; overflow-y: scroll;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
@ -122,7 +122,7 @@ onMounted(async () => {
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
> section { > section {
// Leave some space for the "Back to top" button /* Leave some space for the "Back to top" button */
.main-scroll-wrap { .main-scroll-wrap {
padding-bottom: 64px; padding-bottom: 64px;
} }

View file

@ -17,12 +17,12 @@ import ExtraDrawer from '@/components/layout/main-wrapper/ExtraDrawer.vue'
const ModalWrapper = defineAsyncComponent(() => import('@/components/layout/ModalWrapper.vue')) const ModalWrapper = defineAsyncComponent(() => import('@/components/layout/ModalWrapper.vue'))
</script> </script>
<style lang="scss"> <style lang="postcss">
#mainWrapper { #mainWrapper {
position: relative; position: relative;
display: flex; display: flex;
flex: 1; flex: 1;
height: 0; // fix a flex-box bug https://github.com/philipwalton/flexbugs/issues/197#issuecomment-378908438 height: 0; /* fix a flex-box bug https://github.com/philipwalton/flexbugs/issues/197#issuecomment-378908438 */
overflow: hidden; overflow: hidden;
} }
</style> </style>

View file

@ -107,7 +107,7 @@ const onContextMenu = (event: MouseEvent) => eventBus.emit(
) )
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
li.playlist-folder { li.playlist-folder {
position: relative; position: relative;

View file

@ -127,7 +127,7 @@ onRouteChanged(route => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.playlist { .playlist {
user-select: none; user-select: none;

View file

@ -53,7 +53,7 @@ const requestContextMenu = () => {
} }
</script> </script>
<style lang="scss"> <style lang="postcss">
#playlists { #playlists {
h1 { h1 {
display: flex; display: flex;
@ -67,7 +67,7 @@ const requestContextMenu = () => {
position: relative; position: relative;
&::before { &::before {
// increase clickable area /* increase clickable area */
content: ''; content: '';
position: absolute; position: absolute;
width: 28px; width: 28px;

View file

@ -130,7 +130,7 @@ eventBus.on('TOGGLE_SIDEBAR', () => (mobileShowing.value = !mobileShowing.value)
.on('PLAY_YOUTUBE_VIDEO', _ => (youTubePlaying.value = true)) .on('PLAY_YOUTUBE_VIDEO', _ => (youTubePlaying.value = true))
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
nav { nav {
position: relative; position: relative;
width: var(--sidebar-width); width: var(--sidebar-width);
@ -176,7 +176,7 @@ nav {
overflow-y: auto; overflow-y: auto;
@media (hover: none) { @media (hover: none) {
// Enable scroll with momentum on touch devices /* Enable scroll with momentum on touch devices */
overflow-y: scroll; overflow-y: scroll;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
@ -256,7 +256,7 @@ nav {
} }
} }
:deep(li li a) { // submenu items :deep(li li a) { /* submenu items */
padding-left: 11px; padding-left: 11px;
&:active { &:active {
@ -287,7 +287,12 @@ nav {
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
@include themed-background(); background-color: var(--color-bg-primary);
background-image: var(--bg-image);
background-attachment: var(--bg-attachment);
background-size: var(--bg-size);
background-position: var(--bg-position);
transform: translateX(-100vw); transform: translateX(-100vw);
transition: transform .2s ease-in-out; transition: transform .2s ease-in-out;

View file

@ -105,7 +105,7 @@ onMounted(async () => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.about { .about {
text-align: center; text-align: center;
max-width: 480px; max-width: 480px;

View file

@ -15,7 +15,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import sponsors from '@/sponsors'</script> import sponsors from '@/sponsors'</script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.sponsors { .sponsors {
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -42,7 +42,7 @@ watch(preferenceStore.initialized, initialized => {
}, { immediate: true }) }, { immediate: true })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.support-bar { .support-bar {
background: var(--color-bg-primary); background: var(--color-bg-primary);
font-size: .9rem; font-size: .9rem;

View file

@ -12,6 +12,6 @@ exports[`renders 1`] = `
<!--v-if--><br data-v-6b5b01a9="" data-testid="sponsor-list"> <!--v-if--><br data-v-6b5b01a9="" data-testid="sponsor-list">
<p data-v-6b5b01a9=""> Loving Koel? Please consider supporting its development via <a data-v-6b5b01a9="" href="https://github.com/users/phanan/sponsorship" rel="noopener" target="_blank">GitHub Sponsors</a> and/or <a data-v-6b5b01a9="" href="https://opencollective.com/koel" rel="noopener" target="_blank">OpenCollective</a>. </p> <p data-v-6b5b01a9=""> Loving Koel? Please consider supporting its development via <a data-v-6b5b01a9="" href="https://github.com/users/phanan/sponsorship" rel="noopener" target="_blank">GitHub Sponsors</a> and/or <a data-v-6b5b01a9="" href="https://opencollective.com/koel" rel="noopener" target="_blank">OpenCollective</a>. </p>
</main> </main>
<footer data-v-6b5b01a9=""><button data-v-e368fe26="" data-v-6b5b01a9="" data-testid="close-modal-btn" red="" rounded="">Close</button></footer> <footer data-v-6b5b01a9=""><button data-v-e368fe26="" data-v-6b5b01a9="" class="inset-when-pressed" data-testid="close-modal-btn" red="" rounded="">Close</button></footer>
</div> </div>
`; `;

View file

@ -98,7 +98,7 @@ const maybeClose = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
form { form {
min-width: 100%; min-width: 100%;
} }

View file

@ -86,7 +86,7 @@ const maybeClose = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
form { form {
min-width: 100%; min-width: 100%;
} }

View file

@ -37,7 +37,7 @@ const inviteCollaborators = async () => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.copied { .copied {
font-size: .95rem; font-size: .95rem;
} }

View file

@ -48,7 +48,7 @@ const emit = defineEmits<{ (e: 'close'): void }>()
const close = () => emit('close') const close = () => emit('close')
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.collaboration-modal { .collaboration-modal {
max-width: 640px; max-width: 640px;
} }

View file

@ -70,7 +70,7 @@ const removeCollaborator = async (collaborator: PlaylistCollaborator) => {
onMounted(async () => await fetchCollaborators()) onMounted(async () => await fetchCollaborators())
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
ul { ul {
display: flex; display: flex;
width: 100%; width: 100%;

View file

@ -43,7 +43,7 @@ const { currentUser } = useAuthorization()
const emit = defineEmits<{ (e: 'remove'): void }>() const emit = defineEmits<{ (e: 'remove'): void }>()
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
li { li {
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -23,7 +23,7 @@ const displayedCollaborators = computed(() => collaborators.value.slice(0, 3))
const remainderCount = computed(() => collaborators.value.length - displayedCollaborators.value.length) const remainderCount = computed(() => collaborators.value.length - displayedCollaborators.value.length)
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
div { div {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;

View file

@ -14,6 +14,6 @@ exports[`renders the modal 1`] = `
<div data-v-886145d2="" class="collaborators-wrapper"><br data-v-886145d2="" data-testid="CollaboratorList" playlist="[object Object]"></div> <div data-v-886145d2="" class="collaborators-wrapper"><br data-v-886145d2="" data-testid="CollaboratorList" playlist="[object Object]"></div>
</section> </section>
</main> </main>
<footer data-v-886145d2=""><button data-v-e368fe26="" data-v-886145d2="">Close</button></footer> <footer data-v-886145d2=""><button data-v-e368fe26="" data-v-886145d2="" class="inset-when-pressed">Close</button></footer>
</div> </div>
`; `;

View file

@ -7,7 +7,7 @@
<script lang="ts" setup> <script lang="ts" setup>
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.smart-playlist-form { .smart-playlist-form {
width: 560px; width: 560px;
max-height: calc(100vh - 4rem); max-height: calc(100vh - 4rem);

View file

@ -113,7 +113,7 @@ const onInput = () => {
const removeRule = () => emit('remove') const removeRule = () => emit('remove')
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.row { .row {
display: flex; display: flex;
gap: .5rem; gap: .5rem;

View file

@ -54,7 +54,7 @@ const removeRule = (rule: SmartPlaylistRule) => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.rule-group { .rule-group {
margin-bottom: 1rem; margin-bottom: 1rem;
padding-bottom: .5rem; padding-bottom: .5rem;

View file

@ -17,7 +17,7 @@ const value = computed({
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
input { input {
width: 140px !important; width: 140px !important;
} }

View file

@ -77,7 +77,7 @@ const onCancel = () => (cropperSource.value = null)
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.avatar { .avatar {
--w: 105px; --w: 105px;
outline: rgba(255, 255, 255, .1) solid 3px; outline: rgba(255, 255, 255, .1) solid 3px;

View file

@ -15,7 +15,7 @@ import LastfmIntegration from '@/components/profile-preferences/LastfmIntegratio
import SpotifyIntegration from '@/components/profile-preferences/SpotifyIntegration.vue' import SpotifyIntegration from '@/components/profile-preferences/SpotifyIntegration.vue'
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.integrations { .integrations {
> article + article { > article + article {
margin-top: 2rem; margin-top: 2rem;

View file

@ -77,9 +77,9 @@ const disconnect = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.lastfm-icon { .lastfm-icon {
color: #d31f27; // Last.fm red color: #d31f27; /* Last.fm red */
margin-right: .4rem; margin-right: .4rem;
} }

View file

@ -50,7 +50,7 @@ const isPhone = isMobile.phone
const { isPlus } = useKoelPlus() const { isPlus } = useKoelPlus()
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
label { label {
font-size: 1rem; font-size: 1rem;
} }

View file

@ -129,7 +129,7 @@ const update = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
form { form {
input { input {
width: 100%; width: 100%;

View file

@ -37,7 +37,7 @@ onMounted(() => {
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
img { img {
margin-top: 1.5rem; margin-top: 1.5rem;
display: block; display: block;

View file

@ -36,9 +36,9 @@ const { currentUser, isAdmin } = useAuthorization();
const { useSpotify } = useThirdPartyServices(); const { useSpotify } = useThirdPartyServices();
</script> </script>
<style scoped lang="scss"> <style scoped lang="postcss">
.spotify-icon { .spotify-icon {
margin-right: .4rem; margin-right: .4rem;
color: #1db954; // Spotify green color: #1db954; /* Spotify green */
} }
</style> </style>

View file

@ -31,7 +31,7 @@ if (theme.value.thumbnailUrl) {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.theme { .theme {
height: 100%; height: 100%;
background-position: center; background-position: center;

View file

@ -19,7 +19,7 @@ const themes = toRef(themeStore.state, 'themes')
const setTheme = (theme: Theme) => themeStore.setTheme(theme) const setTheme = (theme: Theme) => themeStore.setTheme(theme)
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.themes { .themes {
display: grid; display: grid;
grid-auto-rows: 8rem; grid-auto-rows: 8rem;

View file

@ -22,7 +22,7 @@
ref="listEl" ref="listEl"
v-koel-overflow-fade v-koel-overflow-fade
:class="`as-${viewMode}`" :class="`as-${viewMode}`"
class="albums main-scroll-wrap" class="albums main-scroll-wrap artist-album-wrapper"
data-testid="album-list" data-testid="album-list"
> >
<template v-if="showSkeletons"> <template v-if="showSkeletons">
@ -96,12 +96,3 @@ useRouter().onScreenActivated('Albums', async () => {
} }
}) })
</script> </script>
<style lang="scss">
#albumsWrapper {
.albums {
@include artist-album-wrapper();
}
}
</style>
`

View file

@ -1,5 +1,5 @@
<template> <template>
<section v-if="album" id="albumWrapper"> <section v-if="album" id="albumWrapper" class="artist-album-info-wrapper">
<ScreenHeaderSkeleton v-if="loading" /> <ScreenHeaderSkeleton v-if="loading" />
<ScreenHeader v-if="!loading && album" :layout="songs.length === 0 ? 'collapsed' : headerLayout"> <ScreenHeader v-if="!loading && album" :layout="songs.length === 0 ? 'collapsed' : headerLayout">
@ -67,14 +67,14 @@
<div v-show="activeTab === 'OtherAlbums'" class="albums-pane" data-testid="albums-pane"> <div v-show="activeTab === 'OtherAlbums'" class="albums-pane" data-testid="albums-pane">
<template v-if="otherAlbums"> <template v-if="otherAlbums">
<ul v-if="otherAlbums.length" v-koel-overflow-fade class="as-list"> <ul v-if="otherAlbums.length" v-koel-overflow-fade class="as-list artist-album-wrapper">
<li v-for="a in otherAlbums" :key="a.id"> <li v-for="a in otherAlbums" :key="a.id">
<AlbumCard :album="a" layout="compact" /> <AlbumCard :album="a" layout="compact" />
</li> </li>
</ul> </ul>
<p v-else class="none text-secondary">No other albums by {{ album.artist_name }} found in the library.</p> <p v-else class="none text-secondary">No other albums by {{ album.artist_name }} found in the library.</p>
</template> </template>
<ul v-else class="as-list"> <ul v-else class="as-list artist-album-wrapper">
<li v-for="i in 12" :key="i"> <li v-for="i in 12" :key="i">
<AlbumCardSkeleton layout="compact" /> <AlbumCardSkeleton layout="compact" />
</li> </li>
@ -186,9 +186,3 @@ onScreenActivated('Album', () => (albumId.value = parseInt(getRouteParam('id')!)
// if the current album has been deleted, go back to the list // if the current album has been deleted, go back to the list
eventBus.on('SONGS_UPDATED', () => albumStore.byId(albumId.value!) || go('albums')) eventBus.on('SONGS_UPDATED', () => albumStore.byId(albumId.value!) || go('albums'))
</script> </script>
<style lang="scss" scoped>
#albumWrapper {
@include artist-album-info-wrapper();
}
</style>

View file

@ -168,10 +168,10 @@ onScreenActivated('Songs', async () => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.controls { .controls {
width: 100%; width: 100%;
min-height: 32px; // prevent shrinking causing the jumping effect min-height: 32px; /* prevent shrinking causing the jumping effect */
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;

View file

@ -22,7 +22,7 @@
ref="listEl" ref="listEl"
v-koel-overflow-fade v-koel-overflow-fade
:class="`as-${viewMode}`" :class="`as-${viewMode}`"
class="artists main-scroll-wrap" class="artists main-scroll-wrap artist-album-wrapper"
data-testid="artist-list" data-testid="artist-list"
> >
<template v-if="showSkeletons"> <template v-if="showSkeletons">
@ -95,11 +95,3 @@ useRouter().onScreenActivated('Artists', async () => {
} }
}) })
</script> </script>
<style lang="scss">
#artistsWrapper {
.artists {
@include artist-album-wrapper();
}
}
</style>

View file

@ -1,5 +1,5 @@
<template> <template>
<section v-if="artist" id="artistWrapper"> <section v-if="artist" id="artistWrapper" class="artist-album-info-wrapper">
<ScreenHeaderSkeleton v-if="loading" /> <ScreenHeaderSkeleton v-if="loading" />
<ScreenHeader v-if="!loading && artist" :layout="songs.length === 0 ? 'collapsed' : headerLayout"> <ScreenHeader v-if="!loading && artist" :layout="songs.length === 0 ? 'collapsed' : headerLayout">
@ -65,12 +65,12 @@
</div> </div>
<div v-show="activeTab === 'Albums'" class="albums-pane"> <div v-show="activeTab === 'Albums'" class="albums-pane">
<ul v-if="albums" v-koel-overflow-fade class="as-list"> <ul v-if="albums" v-koel-overflow-fade class="as-list artist-album-wrapper">
<li v-for="album in albums" :key="album.id"> <li v-for="album in albums" :key="album.id">
<AlbumCard :album="album" layout="compact" /> <AlbumCard :album="album" layout="compact" />
</li> </li>
</ul> </ul>
<ul v-else class="as-list"> <ul v-else class="as-list artist-album-wrapper">
<li v-for="i in 12" :key="i"> <li v-for="i in 12" :key="i">
<AlbumCardSkeleton layout="compact" /> <AlbumCardSkeleton layout="compact" />
</li> </li>
@ -175,11 +175,3 @@ onScreenActivated('Artist', () => (artistId.value = parseInt(getRouteParam('id')
// if the current artist has been deleted, go back to the list // if the current artist has been deleted, go back to the list
eventBus.on('SONGS_UPDATED', () => artistStore.byId(artist.value!.id) || go('artists')) eventBus.on('SONGS_UPDATED', () => artistStore.byId(artist.value!.id) || go('artists'))
</script> </script>
<style lang="scss" scoped>
@import "#/partials/_mixins.scss";
#artistWrapper {
@include artist-album-info-wrapper();
}
</style>

View file

@ -73,7 +73,7 @@ onMounted(async () => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.genres { .genres {
text-align: center; text-align: center;
@ -120,12 +120,34 @@ onMounted(async () => {
} }
} }
@for $i from 0 through 5 { .level-0 {
.level-#{$i} { --unit: 1rem;
$zoom: 1 + $i * .4; opacity: .8;
--unit: #{$zoom}rem; }
opacity: .8 + $i * .04;
} .level-1 {
--unit: 1.4rem;
opacity: .84;
}
.level-2 {
--unit: 1.8rem;
opacity: .88;
}
.level-3 {
--unit: 2.2rem;
opacity: .92;
}
.level-4 {
--unit: 2.6rem;
opacity: .96;
}
.level-5 {
--unit: 3rem;
opacity: 1;
} }
} }
</style> </style>

View file

@ -91,7 +91,7 @@ useRouter().onScreenActivated('Home', async () => {
}) })
</script> </script>
<style lang="scss"> <style lang="postcss">
#homeWrapper { #homeWrapper {
.two-cols { .two-cols {
display: grid; display: grid;
@ -127,7 +127,7 @@ useRouter().onScreenActivated('Home', async () => {
li { li {
overflow: hidden; overflow: hidden;
padding: 1px; // make space for focus outline padding: 1px; /* make space for focus outline */
} }
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {

View file

@ -91,7 +91,7 @@ const confirmThenSave = async () => {
} }
</script> </script>
<style lang="scss"> <style lang="postcss">
#settingsWrapper { #settingsWrapper {
input[type="text"] { input[type="text"] {
width: 50%; width: 50%;

View file

@ -102,7 +102,7 @@ const retryAll = () => uploadService.retryAll()
const removeFailedEntries = () => uploadService.removeFailed() const removeFailedEntries = () => uploadService.removeFailed()
</script> </script>
<style lang="scss"> <style lang="postcss">
#uploadWrapper { #uploadWrapper {
.upload-panel { .upload-panel {
position: relative; position: relative;

View file

@ -76,7 +76,7 @@ const showInviteUserForm = () => eventBus.emit('MODAL_SHOW_INVITE_USER_FORM')
onMounted(async () => await userStore.fetch()) onMounted(async () => await userStore.fetch())
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.users { .users {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View file

@ -76,7 +76,7 @@ const freeUp = () => {
onBeforeUnmount(() => freeUp()) onBeforeUnmount(() => freeUp())
</script> </script>
<style lang="scss"> <style lang="postcss">
#vizContainer { #vizContainer {
.viz { .viz {
height: 100%; height: 100%;

View file

@ -60,7 +60,7 @@ eventBus.on('PLAY_YOUTUBE_VIDEO', payload => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
:deep(#player) { :deep(#player) {
height: 100%; height: 100%;
flex: 1; flex: 1;

View file

@ -14,11 +14,11 @@ exports[`renders 1`] = `
</div> </div>
<div data-v-8ea4eaa5="" data-v-5691beb5-s="" class="controls"> <div data-v-8ea4eaa5="" data-v-5691beb5-s="" class="controls">
<div data-v-d396e0d2="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class="song-list-controls" data-testid="song-list-controls"> <div data-v-d396e0d2="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class="song-list-controls" data-testid="song-list-controls">
<div data-v-d396e0d2="" class="wrapper"><span data-v-e884c19a="" data-v-d396e0d2="" class="btn-group" uppercased=""><button data-v-e368fe26="" data-v-d396e0d2="" class="btn-shuffle-all" data-testid="btn-shuffle-all" orange="" title="Shuffle all. Press Alt/⌥ to change mode."><br data-v-d396e0d2="" data-testid="Icon" icon="[object Object]" fixed-width=""> All </button><!--v-if--><!--v-if--><!--v-if--></span> <div data-v-d396e0d2="" class="wrapper"><span data-v-e884c19a="" data-v-d396e0d2="" class="btn-group" uppercased=""><button data-v-e368fe26="" data-v-d396e0d2="" class="inset-when-pressed btn-shuffle-all" data-testid="btn-shuffle-all" orange="" title="Shuffle all. Press Alt/⌥ to change mode."><br data-v-d396e0d2="" data-testid="Icon" icon="[object Object]" fixed-width=""> All </button><!--v-if--><!--v-if--><!--v-if--></span>
<!--v-if--> <!--v-if-->
<!--v-if--> <!--v-if-->
</div> </div>
<div data-v-d396e0d2="" class="menu-wrapper"> <div data-v-d396e0d2="" class="menu-wrapper context-menu">
<div data-v-42061e3e="" data-v-d396e0d2="" class="add-to" data-testid="add-to-menu" tabindex="0"> <div data-v-42061e3e="" data-v-d396e0d2="" class="add-to" data-testid="add-to-menu" tabindex="0">
<section data-v-42061e3e="" class="existing-playlists"> <section data-v-42061e3e="" class="existing-playlists">
<p data-v-42061e3e="">Add 0 songs to</p> <p data-v-42061e3e="">Add 0 songs to</p>
@ -26,7 +26,7 @@ exports[`renders 1`] = `
<li data-v-42061e3e="" data-testid="queue" tabindex="0">Queue</li> <li data-v-42061e3e="" data-testid="queue" tabindex="0">Queue</li>
<li data-v-42061e3e="" class="favorites" data-testid="add-to-favorites" tabindex="0"> Favorites </li> <li data-v-42061e3e="" class="favorites" data-testid="add-to-favorites" tabindex="0"> Favorites </li>
</ul> </ul>
</section><button data-v-e368fe26="" data-v-42061e3e="" transparent="">New Playlist…</button> </section><button data-v-e368fe26="" data-v-42061e3e="" class="inset-when-pressed" transparent="">New Playlist…</button>
</div> </div>
</div> </div>
</div> </div>
@ -51,11 +51,11 @@ exports[`renders in Plus edition 1`] = `
</div> </div>
<div data-v-8ea4eaa5="" data-v-5691beb5-s="" class="controls"> <div data-v-8ea4eaa5="" data-v-5691beb5-s="" class="controls">
<div data-v-d396e0d2="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class="song-list-controls" data-testid="song-list-controls"> <div data-v-d396e0d2="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class="song-list-controls" data-testid="song-list-controls">
<div data-v-d396e0d2="" class="wrapper"><span data-v-e884c19a="" data-v-d396e0d2="" class="btn-group" uppercased=""><button data-v-e368fe26="" data-v-d396e0d2="" class="btn-shuffle-all" data-testid="btn-shuffle-all" orange="" title="Shuffle all. Press Alt/⌥ to change mode."><br data-v-d396e0d2="" data-testid="Icon" icon="[object Object]" fixed-width=""> All </button><!--v-if--><!--v-if--><!--v-if--></span> <div data-v-d396e0d2="" class="wrapper"><span data-v-e884c19a="" data-v-d396e0d2="" class="btn-group" uppercased=""><button data-v-e368fe26="" data-v-d396e0d2="" class="inset-when-pressed btn-shuffle-all" data-testid="btn-shuffle-all" orange="" title="Shuffle all. Press Alt/⌥ to change mode."><br data-v-d396e0d2="" data-testid="Icon" icon="[object Object]" fixed-width=""> All </button><!--v-if--><!--v-if--><!--v-if--></span>
<!--v-if--> <!--v-if-->
<!--v-if--> <!--v-if-->
</div> </div>
<div data-v-d396e0d2="" class="menu-wrapper"> <div data-v-d396e0d2="" class="menu-wrapper context-menu">
<div data-v-42061e3e="" data-v-d396e0d2="" class="add-to" data-testid="add-to-menu" tabindex="0"> <div data-v-42061e3e="" data-v-d396e0d2="" class="add-to" data-testid="add-to-menu" tabindex="0">
<section data-v-42061e3e="" class="existing-playlists"> <section data-v-42061e3e="" class="existing-playlists">
<p data-v-42061e3e="">Add 0 songs to</p> <p data-v-42061e3e="">Add 0 songs to</p>
@ -63,7 +63,7 @@ exports[`renders in Plus edition 1`] = `
<li data-v-42061e3e="" data-testid="queue" tabindex="0">Queue</li> <li data-v-42061e3e="" data-testid="queue" tabindex="0">Queue</li>
<li data-v-42061e3e="" class="favorites" data-testid="add-to-favorites" tabindex="0"> Favorites </li> <li data-v-42061e3e="" class="favorites" data-testid="add-to-favorites" tabindex="0"> Favorites </li>
</ul> </ul>
</section><button data-v-e368fe26="" data-v-42061e3e="" transparent="">New Playlist…</button> </section><button data-v-e368fe26="" data-v-42061e3e="" class="inset-when-pressed" transparent="">New Playlist…</button>
</div> </div>
</div> </div>
</div><label data-v-8ea4eaa5="" data-v-5691beb5-s="" class="own-songs-toggle text-secondary"><span data-v-b5259680="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class=""><input data-v-b5259680="" type="checkbox"></span><span data-v-8ea4eaa5="" data-v-5691beb5-s="">Own songs only</span></label> </div><label data-v-8ea4eaa5="" data-v-5691beb5-s="" class="own-songs-toggle text-secondary"><span data-v-b5259680="" data-v-8ea4eaa5="" data-v-5691beb5-s="" class=""><input data-v-b5259680="" type="checkbox"></span><span data-v-8ea4eaa5="" data-v-5691beb5-s="">Own songs only</span></label>

View file

@ -14,7 +14,7 @@ exports[`renders 1`] = `
<div class="form-row"><label for="inputSettingsPath">Media Path</label> <div class="form-row"><label for="inputSettingsPath">Media Path</label>
<p id="mediaPathHelp" class="help"> The <em>absolute</em> path to the server directory containing your media. Koel will scan this directory for songs and extract any available information.<br> Scanning may take a while, especially if you have a lot of songs, so be patient. </p><input id="inputSettingsPath" aria-describedby="mediaPathHelp" name="media_path" type="text"> <p id="mediaPathHelp" class="help"> The <em>absolute</em> path to the server directory containing your media. Koel will scan this directory for songs and extract any available information.<br> Scanning may take a while, especially if you have a lot of songs, so be patient. </p><input id="inputSettingsPath" aria-describedby="mediaPathHelp" name="media_path" type="text">
</div> </div>
<div class="form-row"><button data-v-e368fe26="" type="submit">Scan</button></div> <div class="form-row"><button data-v-e368fe26="" class="inset-when-pressed" type="submit">Scan</button></div>
</form> </form>
</section> </section>
`; `;

View file

@ -123,7 +123,7 @@ eventBus.on('SEARCH_KEYWORDS_CHANGED', async _q => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.results > section { .results > section {
margin-bottom: 3em; margin-bottom: 3em;
} }

View file

@ -82,7 +82,7 @@ const {
watch(songs, () => songs.value.length || close()) watch(songs, () => songs.value.length || close())
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.add-to { .add-to {
width: 100%; width: 100%;
max-width: 256px; max-width: 256px;

View file

@ -282,7 +282,7 @@ const submit = async () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
form { form {
max-width: 540px; max-width: 540px;

View file

@ -55,7 +55,7 @@ const play = () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
article { article {
display: flex; display: flex;
gap: 12px; gap: 12px;
@ -103,7 +103,7 @@ article {
} }
} }
// show the thumbnail's playback control on the whole card focus and hover /* show the thumbnail's playback control on the whole card focus and hover */
&:hover :deep(.cover), &:focus :deep(.cover) { &:hover :deep(.cover), &:focus :deep(.cover) {
.control { .control {
display: flex; display: flex;

View file

@ -10,7 +10,7 @@
</template> </template>
<li class="has-sub"> <li class="has-sub">
Add To Add To
<ul class="menu submenu menu-add-to"> <ul class="menu submenu menu-add-to context-menu">
<template v-if="queue.length"> <template v-if="queue.length">
<li v-if="currentSong" @click="queueSongsAfterCurrent">After Current Song</li> <li v-if="currentSong" @click="queueSongsAfterCurrent">After Current Song</li>
<li @click="queueSongsToBottom">Bottom of Queue</li> <li @click="queueSongsToBottom">Bottom of Queue</li>
@ -224,7 +224,7 @@ eventBus.on('SONG_CONTEXT_MENU_REQUESTED', async ({ pageX, pageY }, _songs) => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
ul.playlists { ul.playlists {
position: relative; position: relative;
max-height: 192px; max-height: 192px;

View file

@ -380,7 +380,7 @@ defineExpose({
onMounted(() => render()) onMounted(() => render())
</script> </script>
<style lang="scss"> <style lang="postcss">
.song-list-wrap { .song-list-wrap {
position: relative; position: relative;
display: flex; display: flex;
@ -395,7 +395,7 @@ onMounted(() => render())
.song-list-header { .song-list-header {
background: var(--color-bg-secondary); background: var(--color-bg-secondary);
display: flex; display: flex;
z-index: 2; // fix stack-context related issue when e.g., footer would cover the sort context menu z-index: 2; /* fix stack-context related issue when e.g., footer would cover the sort context menu */
} }
&.dragging .song-item * { &.dragging .song-item * {

View file

@ -89,7 +89,7 @@
</BtnGroup> </BtnGroup>
</div> </div>
<div ref="addToMenu" v-koel-clickaway="closeAddToMenu" class="menu-wrapper"> <div ref="addToMenu" v-koel-clickaway="closeAddToMenu" class="menu-wrapper context-menu">
<AddToMenu :config="config.addTo" :songs="selectedSongs" @closing="closeAddToMenu" /> <AddToMenu :config="config.addTo" :songs="selectedSongs" @closing="closeAddToMenu" />
</div> </div>
</div> </div>
@ -175,7 +175,7 @@ onBeforeUnmount(() => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
.song-list-controls { .song-list-controls {
position: relative; position: relative;
@ -186,8 +186,6 @@ onBeforeUnmount(() => {
} }
.menu-wrapper { .menu-wrapper {
@include context-menu();
padding: 0; padding: 0;
display: none; display: none;
} }

View file

@ -51,7 +51,7 @@ const maybeClose = () => {
} }
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
form { form {
display: flex; display: flex;
border: 1px solid rgba(255, 255, 255, .1); border: 1px solid rgba(255, 255, 255, .1);

View file

@ -70,11 +70,11 @@ const collaborator = computed<Pick<User, 'name' | 'avatar'>>(() => (song.value a
const play = () => emit('play', song.value) const play = () => emit('play', song.value)
</script> </script>
<style lang="scss"> <style lang="postcss">
.song-item { .song-item {
color: var(--color-text-secondary); color: var(--color-text-secondary);
border-bottom: 1px solid var(--color-bg-secondary); border-bottom: 1px solid var(--color-bg-secondary);
max-width: 100% !important; // overriding .item max-width: 100% !important; /* overriding .item */
height: 64px; height: 64px;
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -3,7 +3,7 @@
<button ref="button" title="Sort" @click.stop="trigger"> <button ref="button" title="Sort" @click.stop="trigger">
<Icon :icon="faSort" /> <Icon :icon="faSort" />
</button> </button>
<menu ref="menu" v-koel-clickaway="hide"> <menu ref="menu" v-koel-clickaway="hide" class="context-menu">
<li <li
v-for="item in menuItems" v-for="item in menuItems"
:key="item.label" :key="item.label"
@ -91,7 +91,7 @@ onMounted(() => menu.value && setup())
onBeforeUnmount(() => teardown()) onBeforeUnmount(() => teardown())
</script> </script>
<style lang="scss" scoped> <style lang="postcss" scoped>
button { button {
width: 100%; width: 100%;

Some files were not shown because too many files have changed in this diff Show more