fix(front): simplify tags component (tags are just links)
This commit is contained in:
parent
70c19a70b2
commit
0b2e457052
|
@ -0,0 +1,93 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type RouterLinkProps } from 'vue-router'
|
||||||
|
|
||||||
|
import Link from '~/components/ui/Link.vue'
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
|
||||||
|
type Tab = {
|
||||||
|
title: string,
|
||||||
|
to: RouterLinkProps['to'],
|
||||||
|
icon?: string,
|
||||||
|
badge?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = defineModel<Tab[]>({ required: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Layout
|
||||||
|
nav
|
||||||
|
flex
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.title"
|
||||||
|
v-bind="tab"
|
||||||
|
ghost
|
||||||
|
min-content
|
||||||
|
:class="$style.tab"
|
||||||
|
>
|
||||||
|
<Layout
|
||||||
|
stack
|
||||||
|
no-gap
|
||||||
|
>
|
||||||
|
<span :class="$style.fakeTitle">{{ tab.title }}</span>
|
||||||
|
<span :class="$style.realTitle">{{ tab.title }}</span>
|
||||||
|
<span
|
||||||
|
v-if="tab.badge"
|
||||||
|
:class="$style.badge"
|
||||||
|
>
|
||||||
|
{{ tab.badge }}
|
||||||
|
</span>
|
||||||
|
</Layout>
|
||||||
|
</Link>
|
||||||
|
</Layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style module>
|
||||||
|
.fakeTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 900;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.realTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
--hover-background-color: transparent;
|
||||||
|
--exact-active-background-color: transparent;
|
||||||
|
}
|
||||||
|
.tab:global(.router-link-exact-active) .realTitle {
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: block;
|
||||||
|
height: 16px;
|
||||||
|
background-color: var(--fw-secondary);
|
||||||
|
width: 16px;
|
||||||
|
position: absolute;
|
||||||
|
inset: -10px -14px auto auto;
|
||||||
|
border-radius: 100vh;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 900;
|
||||||
|
padding: 5px;
|
||||||
|
line-height: 5px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
:is(.tab:global(.router-link-exact-active), .tab:hover) .realTitle:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
height: 4px;
|
||||||
|
background-color: var(--fw-secondary);
|
||||||
|
margin: 0 auto;
|
||||||
|
width: calc(10% + 2rem);
|
||||||
|
position: absolute;
|
||||||
|
inset: auto 0 -14px 0;
|
||||||
|
border-radius: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,11 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const labels = computed(() => ({
|
const labels = computed(() => ({
|
||||||
|
@ -13,6 +12,34 @@ const labels = computed(() => ({
|
||||||
secondaryMenu: t('views.admin.library.Base.menu.secondary')
|
secondaryMenu: t('views.admin.library.Base.menu.secondary')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const tabs = ref([
|
||||||
|
{
|
||||||
|
title: t('views.admin.library.Base.link.edits'),
|
||||||
|
to: {name: 'manage.library.edits'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.channels'),
|
||||||
|
to: {name: 'manage.channels'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.artists'),
|
||||||
|
to: {name: 'manage.library.artists'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.albums'),
|
||||||
|
to: {name: 'manage.library.albums'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.tracks'),
|
||||||
|
to: {name: 'manage.library.tracks'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.libraries'),
|
||||||
|
to: {name: 'manage.library.libraries'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.uploads'),
|
||||||
|
to: {name: 'manage.library.uploads'}
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.library.Base.link.tags'),
|
||||||
|
to: {name: 'manage.library.tags'}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -23,40 +50,8 @@ const route = useRoute()
|
||||||
stack
|
stack
|
||||||
class="page-admin-library"
|
class="page-admin-library"
|
||||||
>
|
>
|
||||||
<Tabs>
|
<Nav v-model="tabs" />
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.edits')"
|
|
||||||
:to="{name: 'manage.library.edits'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.channels')"
|
|
||||||
:to="{name: 'manage.channels'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.artists')"
|
|
||||||
:to="{name: 'manage.library.artists'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.albums')"
|
|
||||||
:to="{name: 'manage.library.albums'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.tracks')"
|
|
||||||
:to="{name: 'manage.library.tracks'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.libraries')"
|
|
||||||
:to="{name: 'manage.library.libraries'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.uploads')"
|
|
||||||
:to="{name: 'manage.library.uploads'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.library.Base.link.tags')"
|
|
||||||
:to="{name: 'manage.library.tags'}"
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
<router-view :key="route.fullPath" />
|
<router-view :key="route.fullPath" />
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -8,8 +8,7 @@ import { useRoute } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
|
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
@ -22,6 +21,24 @@ const labels = computed(() => ({
|
||||||
secondaryMenu: t('views.admin.moderation.Base.menu.secondary')
|
secondaryMenu: t('views.admin.moderation.Base.menu.secondary')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const tabs = ref([{
|
||||||
|
title: t('views.admin.moderation.Base.link.reports'),
|
||||||
|
to: { name: 'manage.moderation.reports.list', query: { q: 'resolved:no' } },
|
||||||
|
badge: store.state.ui.notifications.pendingReviewReports > 0 ? store.state.ui.notifications.pendingReviewReports : undefined
|
||||||
|
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.moderation.Base.link.userRequests'),
|
||||||
|
to: { name: 'manage.moderation.requests.list', query: { q: 'status:pending' } },
|
||||||
|
badge: store.state.ui.notifications.pendingReviewRequests > 0 ? store.state.ui.notifications.pendingReviewRequests : undefined
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.moderation.Base.link.domains'),
|
||||||
|
to: { name: 'manage.moderation.domains.list' }
|
||||||
|
}, {
|
||||||
|
title: t('views.admin.moderation.Base.link.accounts'),
|
||||||
|
to: { name: 'manage.moderation.accounts.list' }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const fetchNodeInfo = async () => {
|
const fetchNodeInfo = async () => {
|
||||||
const response = await axios.get('instance/nodeinfo/2.1/')
|
const response = await axios.get('instance/nodeinfo/2.1/')
|
||||||
allowListEnabled.value = get(response.data, 'metadata.allowList.enabled', false)
|
allowListEnabled.value = get(response.data, 'metadata.allowList.enabled', false)
|
||||||
|
@ -37,41 +54,8 @@ fetchNodeInfo()
|
||||||
main
|
main
|
||||||
no-gap
|
no-gap
|
||||||
>
|
>
|
||||||
<Tabs
|
<Nav v-model="tabs" />
|
||||||
role="navigation"
|
|
||||||
:aria-label="labels.secondaryMenu"
|
|
||||||
>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.moderation.Base.link.reports')"
|
|
||||||
:to="{name: 'manage.moderation.reports.list', query: {q: 'resolved:no'}}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="store.state.ui.notifications.pendingReviewReports > 0"
|
|
||||||
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
|
||||||
>
|
|
||||||
{{ store.state.ui.notifications.pendingReviewReports }}
|
|
||||||
</div>
|
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.moderation.Base.link.userRequests')"
|
|
||||||
:to="{name: 'manage.moderation.requests.list', query: {q: 'status:pending'}}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="store.state.ui.notifications.pendingReviewRequests > 0"
|
|
||||||
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
|
||||||
>
|
|
||||||
{{ store.state.ui.notifications.pendingReviewRequests }}
|
|
||||||
</div>
|
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.moderation.Base.link.domains')"
|
|
||||||
:to="{name: 'manage.moderation.domains.list'}"
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.moderation.Base.link.accounts')"
|
|
||||||
:to="{name: 'manage.moderation.accounts.list'}"
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
<router-view
|
<router-view
|
||||||
:key="route.fullPath"
|
:key="route.fullPath"
|
||||||
:allow-list-enabled="allowListEnabled"
|
:allow-list-enabled="allowListEnabled"
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
@ -12,6 +11,18 @@ const labels = computed(() => ({
|
||||||
manageUsers: t('views.admin.users.Base.title'),
|
manageUsers: t('views.admin.users.Base.title'),
|
||||||
secondaryMenu: t('views.admin.users.Base.menu.secondary')
|
secondaryMenu: t('views.admin.users.Base.menu.secondary')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const tabs = ref([
|
||||||
|
{
|
||||||
|
title: t('views.admin.users.Base.link.users'),
|
||||||
|
to: { name: 'manage.users.users.list' }
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t('views.admin.users.Base.link.invitations'),
|
||||||
|
to: { name: 'manage.users.invitations.list' }
|
||||||
|
}
|
||||||
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -20,17 +31,8 @@ const labels = computed(() => ({
|
||||||
main
|
main
|
||||||
stack
|
stack
|
||||||
>
|
>
|
||||||
<Tabs>
|
<Nav v-model="tabs" />
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.users.Base.link.users')"
|
|
||||||
:to="{name: 'manage.users.users.list'}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
:title="t('views.admin.users.Base.link.invitations')"
|
|
||||||
:to="{name: 'manage.users.invitations.list'}"
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
<router-view :key="$route.fullPath" />
|
<router-view :key="$route.fullPath" />
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -16,8 +16,7 @@ import RenderedDescription from '~/components/common/RenderedDescription.vue'
|
||||||
|
|
||||||
import Layout from '~/components/ui/Layout.vue'
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
import Section from '~/components/ui/Section.vue'
|
import Section from '~/components/ui/Section.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
(e: 'updated', value: Actor): void
|
(e: 'updated', value: Actor): void
|
||||||
|
@ -79,6 +78,21 @@ const fetchData = async () => {
|
||||||
|
|
||||||
watch(props, fetchData, { immediate: true })
|
watch(props, fetchData, { immediate: true })
|
||||||
const recentActivity = ref(0)
|
const recentActivity = ref(0)
|
||||||
|
|
||||||
|
const tabs = ref([{
|
||||||
|
title: t('views.auth.ProfileBase.link.overview') ,
|
||||||
|
to: { name: 'profile.overview', params: routerParams }
|
||||||
|
}, {
|
||||||
|
title: t('views.auth.ProfileBase.link.activity') ,
|
||||||
|
to: { name: 'profile.activity', params: routerParams }
|
||||||
|
}, ...(
|
||||||
|
store.state.auth.authenticated && object.value && object.value.full_username === store.state.auth.fullUsername
|
||||||
|
? [{
|
||||||
|
title: t('views.auth.ProfileBase.link.manageUploads') ,
|
||||||
|
to: { name: 'profile.manageUploads', params: routerParams }
|
||||||
|
}]
|
||||||
|
: []
|
||||||
|
)])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -135,38 +149,12 @@ const recentActivity = ref(0)
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Tabs>
|
<Nav v-model="tabs" />
|
||||||
<Tab
|
|
||||||
:title="t('views.auth.ProfileBase.link.overview')"
|
|
||||||
:to="{name: 'profile.overview', params: routerParams}"
|
|
||||||
>
|
|
||||||
<router-view
|
|
||||||
:object="object"
|
|
||||||
@updated="fetchData"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
:title="t('views.auth.ProfileBase.link.activity')"
|
|
||||||
:to="{name: 'profile.activity', params: routerParams}"
|
|
||||||
>
|
|
||||||
<router-view
|
<router-view
|
||||||
:object="object"
|
:object="object"
|
||||||
@updated="fetchData"
|
@updated="fetchData"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
v-if="store.state.auth.authenticated && object && object.full_username === store.state.auth.fullUsername"
|
|
||||||
:title="t('views.auth.ProfileBase.link.manageUploads')"
|
|
||||||
:to="{ name: 'profile.manageUploads', params: routerParams }"
|
|
||||||
>
|
|
||||||
<router-view
|
|
||||||
:object="object"
|
|
||||||
@updated="fetchData"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,7 @@ import RadioButton from '~/components/radios/Button.vue'
|
||||||
import Loader from '~/components/ui/Loader.vue'
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
import Button from '~/components/ui/Button.vue'
|
import Button from '~/components/ui/Button.vue'
|
||||||
import Link from '~/components/ui/Link.vue'
|
import Link from '~/components/ui/Link.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
import OptionsButton from '~/components/ui/button/Options.vue'
|
import OptionsButton from '~/components/ui/button/Options.vue'
|
||||||
import Popover from '~/components/ui/Popover.vue'
|
import Popover from '~/components/ui/Popover.vue'
|
||||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||||
|
@ -60,7 +59,6 @@ const showEditModal = ref(false)
|
||||||
const showSubscribeModal = ref(false)
|
const showSubscribeModal = ref(false)
|
||||||
|
|
||||||
const isOwner = computed(() => store.state.auth.authenticated && object.value?.attributed_to.full_username === store.state.auth.fullUsername)
|
const isOwner = computed(() => store.state.auth.authenticated && object.value?.attributed_to.full_username === store.state.auth.fullUsername)
|
||||||
const isPodcast = computed(() => object.value?.artist?.content_category === 'podcast')
|
|
||||||
const isPlayable = computed(() => totalTracks.value > 0)
|
const isPlayable = computed(() => totalTracks.value > 0)
|
||||||
const externalDomain = computed(() => {
|
const externalDomain = computed(() => {
|
||||||
const parser = document.createElement('a')
|
const parser = document.createElement('a')
|
||||||
|
@ -147,6 +145,18 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
object.value.subscriptions_count += delta
|
object.value.subscriptions_count += delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tabs = ref([
|
||||||
|
{
|
||||||
|
title: t('views.channels.DetailBase.link.channelOverview'),
|
||||||
|
to: {name: 'channels.detail', params: { id: props.id }}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.channels.DetailBase.link.channelEpisodes'),
|
||||||
|
to: {name: 'channels.detail.episodes', params: { id: props.id }}
|
||||||
|
}
|
||||||
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -482,28 +492,13 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
:limit="5"
|
:limit="5"
|
||||||
:show-more="true"
|
:show-more="true"
|
||||||
/>
|
/>
|
||||||
<Tabs>
|
<Nav v-model="tabs" />
|
||||||
<Tab
|
|
||||||
:title="t('views.channels.DetailBase.link.channelOverview')"
|
|
||||||
:to="{name: 'channels.detail', params: {id: id}}"
|
|
||||||
>
|
|
||||||
<router-view
|
<router-view
|
||||||
v-if="object"
|
v-if="object"
|
||||||
:object="object"
|
:object="object"
|
||||||
@tracks-loaded="totalTracks = $event"
|
@tracks-loaded="totalTracks = $event"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.channels.DetailBase.link.channelEpisodes')"
|
|
||||||
:to="{name: 'channels.detail.episodes', params: {id: id}}"
|
|
||||||
>
|
|
||||||
<router-view
|
|
||||||
v-if="object"
|
|
||||||
:object="object"
|
|
||||||
@tracks-loaded="totalTracks = $event"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -17,9 +17,7 @@ import OptionsButton from '~/components/ui/button/Options.vue'
|
||||||
import Popover from '~/components/ui/Popover.vue'
|
import Popover from '~/components/ui/Popover.vue'
|
||||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||||
import Header from '~/components/ui/Header.vue'
|
import Header from '~/components/ui/Header.vue'
|
||||||
import Tabs from '~/components/ui/Tabs.vue'
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
import Tab from '~/components/ui/Tab.vue'
|
|
||||||
import Button from '~/components/ui/Button.vue'
|
|
||||||
|
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
import useReport from '~/composables/moderation/useReport'
|
import useReport from '~/composables/moderation/useReport'
|
||||||
|
@ -86,10 +84,22 @@ watchEffect(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateUploads = (count: number) => {
|
const updateUploads = (count: number) => {
|
||||||
if (object.value) {
|
object.value = object.value
|
||||||
object.value.uploads_count += count
|
? { ...object.value, uploads_count: object.value.uploads_count + count }
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tabs = ref([{
|
||||||
|
title: t('views.library.LibraryBase.link.artists'),
|
||||||
|
to: { name: 'library.detail' }
|
||||||
|
}, {
|
||||||
|
title: t('views.library.LibraryBase.link.albums'),
|
||||||
|
to: { name: 'library.detail.albums' }
|
||||||
|
}, {
|
||||||
|
title: t('views.library.LibraryBase.link.tracks'),
|
||||||
|
to: { name: 'library.detail.tracks' }
|
||||||
}
|
}
|
||||||
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -177,13 +187,16 @@ const updateUploads = (count: number) => {
|
||||||
<i class="bi bi-globe" />
|
<i class="bi bi-globe" />
|
||||||
{{ labels.visibility.everyone }}
|
{{ labels.visibility.everyone }}
|
||||||
</span>
|
</span>
|
||||||
<span class="middledot icon">
|
<span
|
||||||
|
v-if="object"
|
||||||
|
class="middledot icon"
|
||||||
|
>
|
||||||
<i class="bi bi-music-note-list" />
|
<i class="bi bi-music-note-list" />
|
||||||
{{ t('views.library.LibraryBase.meta.tracks', object.uploads_count) }}
|
{{ t('views.library.LibraryBase.meta.tracks', object.uploads_count) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="object?.size">
|
<span v-if="object && 'size' in object && object.size">
|
||||||
<i class="bi bi-database-fill" />
|
<i class="bi bi-database-fill" />
|
||||||
{{ humanSize(object?.size) }}
|
{{ humanSize(object.size) }}
|
||||||
</span>
|
</span>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
@ -222,40 +235,13 @@ const updateUploads = (count: number) => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Tabs>
|
<Nav v-model="tabs" />
|
||||||
<Tab
|
|
||||||
:title="t('views.library.LibraryBase.link.artists')"
|
|
||||||
:to="{name: 'library.detail'}"
|
|
||||||
>
|
|
||||||
<router-view
|
<router-view
|
||||||
:is-owner="isOwner"
|
:is-owner="isOwner"
|
||||||
:object="object"
|
:object="object"
|
||||||
@updated="fetchData"
|
@updated="fetchData"
|
||||||
@uploads-finished="updateUploads"
|
@uploads-finished="updateUploads"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.library.LibraryBase.link.albums')"
|
|
||||||
:to="{name: 'library.detail.albums'}"
|
|
||||||
>
|
|
||||||
<router-view
|
|
||||||
:is-owner="isOwner"
|
|
||||||
:object="object"
|
|
||||||
@updated="fetchData"
|
|
||||||
@uploads-finished="updateUploads"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
:title="t('views.library.LibraryBase.link.tracks')"
|
|
||||||
:to="{name: 'library.detail.tracks'}"
|
|
||||||
>
|
|
||||||
<router-view
|
|
||||||
:is-owner="isOwner"
|
|
||||||
:object="object"
|
|
||||||
@updated="fetchData"
|
|
||||||
@uploads-finished="updateUploads"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -29,6 +29,7 @@ export default defineConfig({
|
||||||
{ text: 'Pagination', link: '/components/ui/pagination' },
|
{ text: 'Pagination', link: '/components/ui/pagination' },
|
||||||
{ text: 'Table of Contents', link: '/components/ui/toc' },
|
{ text: 'Table of Contents', link: '/components/ui/toc' },
|
||||||
{ text: 'Tabs', link: '/components/ui/tabs' },
|
{ text: 'Tabs', link: '/components/ui/tabs' },
|
||||||
|
{ text: 'Nav', link: '/components/ui/nav' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
import Nav from '~/components/ui/Nav.vue'
|
||||||
|
|
||||||
|
const nav = ref([{ title: 'Go up', to: '../' }, { title: 'Home', to: './', badge: "2" }])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import Nav from "~/components/ui/Nav.vue"
|
||||||
|
```
|
||||||
|
|
||||||
|
# Nav
|
||||||
|
|
||||||
|
This is just a list of links, styled like tabs.
|
||||||
|
|
||||||
|
You can add a `badge` or an `icon` to each tab link.
|
||||||
|
|
||||||
|
<Link to="/">Hello</Link>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const nav = ref([{ title: 'Go up', to: '../' }, { title: 'Home', to: './', badge: "2" }])
|
||||||
|
```
|
||||||
|
|
||||||
|
```vue-html
|
||||||
|
<Nav v-model="nav" />
|
||||||
|
```
|
||||||
|
|
||||||
|
<Nav v-model="nav" />
|
Loading…
Reference in New Issue