Paths in frontend templates to find the source files. Move non-abstract external UI components into this repo. ArtistCard working, with missing css

This commit is contained in:
jon r 2024-10-29 20:20:09 +01:00 committed by upsiflu
parent fe49c87e57
commit 684fc13f7e
21 changed files with 521 additions and 3 deletions

View File

@ -0,0 +1,51 @@
<script setup lang="ts">
import { FwCard, FwPlayButton, FwOptionsButton } from '~/components'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import type { Album } from '~/types/models'
const { t } = useI18n()
const play = defineEmit<[album: Album]>()
const album = defineProp<Album>('album', { required: true })
let navigate = (to: 'artist' | 'album') => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = (to: 'artist' | 'album') => to === 'album'
? router.push({ name: 'library.albums.detail', params: { id: album.value.id } })
: router.push({ name: 'library.artists.detail', params: { id: album.value.artist.id } })
}
</script>
<template>
<fw-card
:title="album.name"
:image="album.cover.urls.original"
@click="navigate('album')"
class="album-card"
>
<fw-play-button @play="play(album)" />
<a
v-for="ac in album.artistCredit"
:key="ac.artist.id"
@click.stop="navigate('artist')"
class="funkwhale link"
>
{{ ac.credit ?? t('components.Queue.meta.unknownArtist') }}
<!-- {{ ac.joinphrase }} -->
</a>
<template #footer>
{{ t('vui.tracks', album.tracks.length) }}
<fw-options-button />
</template>
</fw-card>
</template>
<style lang="scss">
@import './style.scss'
</style>

View File

@ -0,0 +1,25 @@
.funkwhale {
&.card.album-card {
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: var(--fw-card-width);
--fw-card-padding: 16px;
> .card-image {
border-radius: 0 !important;
width: var(--fw-card-image-width);
margin: calc(-1 * var(--fw-card-padding)) calc(-1 * var(--fw-card-padding)) 0;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
padding-top: 16px !important;
}
> .card-content {
padding-top: 0 !important;
text-align: left !important;
}
}
}

View File

@ -0,0 +1,48 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import type { Artist } from '~/types'
const play = defineEmit<[artist: Artist]>()
interface Props {
artist: Artist;
}
const props = defineProps<Props>()
const { artist } = props
let navigate = () => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = () =>
router.push({ name: 'library.artists.detail', params: { id: artist.id } })
}
const image = artist.cover
? artist.cover.urls.original
: `${import.meta.env.BASE_URL}embed-default-cover.jpeg`
</script>
<template>
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/artist/Card.vue
<fw-card
:title="artist.name"
:image="image"
:tags="artist.tags"
@click="navigate"
class="artist-card"
>
<fw-play-button @play="play(artist)" />
<template #footer>
{{ $t("components.audio.artist.Card.meta.tracks", artist.tracks_count) }}
<fw-options-button />
</template>
</fw-card>
</template>
<style lang="scss">
@import "./style.scss";
</style>

View File

@ -0,0 +1,28 @@
.funkwhale {
&.card.artist-card {
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: calc(var(--fw-card-width) - 16px);
--fw-card-padding: 16px;
> .card-image {
border-radius: 50% !important;
width: var(--fw-card-image-width);
margin: calc(-1 * var(--fw-card-padding) + 8px) calc(-1 * var(--fw-card-padding) + 8px) 0;
}
.play-button {
top: calc(var(--fw-card-width) - 44px - 8px) !important;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
}
> .card-content {
padding-top: 6px !important;
text-align: left !important;
}
}
}

View File

@ -0,0 +1,28 @@
.funkwhale {
&.card.artist-card {
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: calc(var(--fw-card-width) - 16px);
--fw-card-padding: 16px;
> .card-image {
border-radius: 50% !important;
width: var(--fw-card-image-width);
margin: calc(-1 * var(--fw-card-padding) + 8px) calc(-1 * var(--fw-card-padding) + 8px) 0;
}
.play-button {
top: calc(var(--fw-card-width) - 44px - 8px) !important;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
}
> .card-content {
padding-top: 6px !important;
text-align: left !important;
}
}
}

View File

@ -72,6 +72,7 @@ const labels = computed(() => ({
<template>
<div>
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/audio/Search.vue
<h2>
{{ $t('components.audio.Search.header.search') }}
</h2>

View File

@ -143,6 +143,7 @@ const remove = async () => {
<section class="ui vertical stripe segment channel-serie">
<div class="ui stackable grid container">
<div class="ui seven wide column">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/library/AlbumBase.vue
<div
v-if="isSerie"
class="padded basic segment"

View File

@ -120,6 +120,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<template>
<main v-title="labels.title">
<section class="ui vertical stripe segment">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/library/Albums.vue
<h2 class="ui header">
{{ $t('components.library.Albums.header.browse') }}
</h2>

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import ArtistCard from '~/components/artist/Card.vue'
import type { OrderingProps } from '~/composables/navigation/useOrdering'
import type { Artist, BackendResponse } from '~/types'
import type { RouteRecordName } from 'vue-router'
@ -15,7 +16,7 @@ import axios from 'axios'
import $ from 'jquery'
import TagsSelector from '~/components/library/TagsSelector.vue'
import ArtistCard from '~/components/audio/artist/Card.vue'
// import ArtistCard from '~/components/audio/artist/Card.vue'
import Pagination from '~/components/vui/Pagination.vue'
import useSharedLabels from '~/composables/locale/useSharedLabels'
@ -121,6 +122,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<template>
<main v-title="labels.title">
<section class="ui vertical stripe segment">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/library/Artists.vue
<h2 class="ui header">
{{ $t('components.library.Artists.header.browse') }}
</h2>
@ -233,6 +235,11 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
>
<div class="ui loader" />
</div>
<ArtistCard
v-for="artist in result.results"
:key="artist.id"
:artist="artist"
/>
<artist-card
v-for="artist in result.results"
:key="artist.id"

View File

@ -110,11 +110,11 @@ fetchData()
<h3 class="ui header">
{{ $t('components.library.Home.header.newChannels') }}
</h3>
<channels-widget
<!-- <channels-widget
:show-modification-date="true"
:limit="12"
:filters="{ordering: '-creation_date', external: 'false'}"
/>
/> -->
</template>
</section>
</main>

View File

@ -102,6 +102,7 @@ const labels = computed(() => ({
<template>
<div>
<div class="ui inline form">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/manage/library/AlbumsTable.vue
<div class="fields">
<div class="ui six wide field">
<label for="albums-search">{{ $t('components.manage.library.AlbumsTable.label.search') }}</label>

View File

@ -0,0 +1,70 @@
<script setup lang="ts">
import { FwCard, FwPlayButton, FwOptionsButton } from '~/components'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import type { Playlist } from '~/types/models'
const { t } = useI18n()
const play = defineEmit<[playlist: Playlist]>()
const playlist = defineProp<Playlist>('playlist', { required: true })
const covers = computed(() => playlist.value.album_covers
.filter((src, index, array) => array.indexOf(src) === index)
.slice(0, 4)
)
const profileParams = computed(() => {
const [username, domain] = playlist.value.user.full_username.split('@')
return { username, domain }
})
let navigate = (to: 'playlist' | 'user') => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = (to: 'playlist' | 'user') => to === 'playlist'
? router.push({ name: 'library.playlists.detail', params: { id: playlist.value.id } })
: router.push({ name: 'profile.full', params: profileParams.value })
}
</script>
<template>
<fw-card
:title="playlist.name"
@click="navigate('playlist')"
class="playlist-card"
>
<template #image>
<img
v-for="src in covers"
:key="src"
:src="src"
/>
<div
v-for="i in Math.max(0, 4 - covers.length)"
:key="i"
/>
</template>
<fw-play-button @play="play(playlist)" />
<a
@click.stop="navigate('user')"
class="funkwhale link"
>
{{ t('vui.by-user', playlist.user) }}
</a>
<template #footer>
{{ t('vui.tracks', playlist.tracks_count) }}
<fw-options-button />
</template>
</fw-card>
</template>
<style lang="scss">
@import './style.scss'
</style>

View File

@ -0,0 +1,66 @@
.funkwhale {
&.card.playlist-card {
@include light-theme {
> .card-image {
> div {
--fw-bg-color: var(--fw-pastel-blue-3);
background-color: var(--fw-bg-color);
&:nth-child(2) {
--fw-bg-color: var(--fw-pastel-blue-1);
}
&:nth-child(3) {
--fw-bg-color: var(--fw-pastel-blue-2);
}
&:nth-child(4) {
--fw-bg-color: var(--fw-pastel-blue-4);
}
}
}
}
@include dark-theme {
> .card-image {
> div {
--fw-bg-color: var(--fw-pastel-blue-3);
background-color: var(--fw-bg-color);
&:nth-child(2) {
--fw-bg-color: var(--fw-pastel-blue-1);
}
&:nth-child(3) {
--fw-bg-color: var(--fw-pastel-blue-2);
}
&:nth-child(4) {
--fw-bg-color: var(--fw-pastel-blue-4);
}
}
}
}
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: var(--fw-card-width);
--fw-card-padding: 16px;
> .card-image {
border-radius: 0 !important;
width: var(--fw-card-image-width);
margin: calc(-1 * var(--fw-card-padding)) calc(-1 * var(--fw-card-padding)) 0;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
padding-top: 16px !important;
}
> .card-content {
padding-top: 0 !important;
text-align: left !important;
}
}
}

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
import { FwCard, FwOptionsButton } from '~/components'
import { useTimeAgo } from '@vueuse/core'
import { useRouter } from 'vue-router'
import type { Podcast } from '~/types/models'
const podcast = defineProp<Podcast>('podcast', { required: true })
const timeAgo = useTimeAgo(new Date(podcast.value.artist.modification_date))
let navigate = () => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = () => router.push({ name: 'library.artists.detail', params: { id: podcast.value.artist.id } })
}
</script>
<template>
<fw-card
:title="podcast.name"
:image="podcast.artist.cover.urls.original"
@click="navigate"
class="podcast-card"
>
<a
@click.stop="navigate"
class="funkwhale link"
>
{{ podcast.artist.name }}
</a>
<template #footer>
{{ timeAgo }}
<fw-options-button />
</template>
</fw-card>
</template>
<style lang="scss">
@import './style.scss'
</style>

View File

@ -0,0 +1,23 @@
.funkwhale {
&.card.podcast-card {
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: var(--fw-card-width);
--fw-card-padding: 16px;
> .card-image {
--fw-border-radius: 10px;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
padding-top: 20px !important;
}
> .card-content {
padding-top: 6px !important;
text-align: left !important;
}
}
}

View File

@ -0,0 +1,55 @@
<script setup lang="ts">
import { usePastel } from '~/composables/colors'
import { FwCard, FwPlayButton } from '~/components'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import type { PastelProps } from '~/types/common-props'
import type { Radio } from '~/types/models'
const { t } = useI18n()
interface Props {
radio: Radio
small?: boolean
}
const play = defineEmit<[radio: Radio]>()
const props = defineProps<Props & PastelProps>()
const pastel = usePastel(() => props.color)
let navigate = () => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = () => router.push({ name: 'library.radios.detail', params: { id: props.radio.id } })
}
</script>
<template>
<fw-card
:title="radio.name"
:class="pastel"
@click="navigate"
class="radio-card"
>
<template #image>
<div class="cover-name">
{{ t('vui.radio') }}
</div>
</template>
<fw-play-button @play="play(props.radio)" />
<div
v-if="!small"
class="radio-description"
>
{{ radio.description }}
</div>
</fw-card>
</template>
<style lang="scss">
@import './style.scss'
</style>

View File

@ -0,0 +1,67 @@
.funkwhale {
&.card.radio-card {
background: var(--fw-bg-color);
.radio-description {
background: var(--fw-bg-color);
color: var(--fw-text-color);
}
@include light-theme {
--fw-bg-color: var(--fw-pastel-3);
--fw-text-color: var(--fw-gray-100);
.radio-description {
--fw-bg-color: var(--fw-pastel-1);
--fw-text-color: var(--fw-gray-900);
}
}
@include dark-theme {
--fw-bg-color: var(--fw-pastel-4);
--fw-text-color: var(--fw-gray-100);
.radio-description {
--fw-bg-color: var(--fw-pastel-1);
--fw-text-color: var(--fw-gray-900);
}
}
--fw-border-radius: 12px;
--fw-card-width: 208px;
--fw-card-image-width: var(--fw-card-width);
--fw-card-padding: 0;
text-align: left !important;
> .card-title {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 16px 16px 0 !important;
font-size: 2rem;
white-space: initial;
}
.cover-name {
position: absolute;
top: calc(var(--fw-card-width) - 16px);
right: 16px;
left: 16px;
transform: translateY(-100%);
font-size: 1.5rem;
font-weight: 300;
}
> .card-content {
padding-top: 0 !important;
}
.radio-description {
text-align: center;
font-size: 0.875rem;
padding: 16px 16px 22px;
}
}
}

View File

@ -225,6 +225,7 @@ const radioConfig = computed(() => {
class="main pusher"
>
<section class="ui vertical stripe segment">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/audio/Search.vue
<div
v-if="id"
class="ui small text container"

View File

@ -20,6 +20,7 @@ const defaultQuota = computed(() => humanSize(quota.value * 1e6))
v-title="labels.title"
class="ui vertical aligned stripe segment"
>
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/audio/Search.vue
<div class="ui text container">
<h1>{{ labels.title }}</h1>
<p>

View File

@ -41,6 +41,7 @@ const libraryCreated = (library: Library) => {
<template>
<section class="ui vertical aligned stripe segment">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/components/audio/Search.vue
<div
v-if="isLoading"
:class="['ui', {'active': isLoading}, 'inverted', 'dimmer']"

View File

@ -86,6 +86,7 @@ const updateUploads = (count: number) => {
<template>
<main v-title="labels.title">
<div class="ui vertical stripe segment container">
/Users/arnology/Sites/funkwhale/funkwhale/front/src/views/library/LibraryBase.vue
<div
v-if="isLoading"
class="ui centered active inline loader"