fix(ui):[WIP] replace popover menus, buttons, messages
This commit is contained in:
parent
481fee8f5f
commit
4d78c2143c
|
@ -1,6 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
import ChannelUploadForm from '~/components/channels/UploadForm.vue'
|
||||
import Popover from '~/components/ui/Popover.vue'
|
||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from '~/store'
|
||||
|
@ -45,6 +48,8 @@ const statusInfo = computed(() => {
|
|||
|
||||
const step = ref(1)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const dropdownOpen = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -86,70 +91,70 @@ const isLoading = ref(false)
|
|||
</template>
|
||||
</div>
|
||||
<div class="ui hidden clearing divider mobile-only" />
|
||||
<button
|
||||
<Button
|
||||
v-if="step === 1"
|
||||
class="ui basic cancel button"
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
@click="update(false)"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.cancel') }}
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="step < 3"
|
||||
class="ui basic button"
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
@click.stop.prevent="uploadForm.step -= 1"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.previous') }}
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="step === 3"
|
||||
class="ui basic button"
|
||||
color="secondary"
|
||||
@click.stop.prevent="uploadForm.step -= 1"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.update') }}
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
v-if="step === 1"
|
||||
class="ui primary button"
|
||||
color="secondary"
|
||||
@click.stop.prevent="uploadForm.step += 1"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.next') }}
|
||||
</button>
|
||||
<div
|
||||
v-if="step === 2"
|
||||
class="ui primary buttons"
|
||||
>
|
||||
<button
|
||||
:class="['ui', 'primary button', {loading: isLoading}]"
|
||||
</Button>
|
||||
<div class="ui primary buttons">
|
||||
<Button
|
||||
:is-loading="isLoading"
|
||||
type="submit"
|
||||
:disabled="!statusData?.canSubmit || undefined"
|
||||
:disabled="!statusData?.canSubmit"
|
||||
@click.prevent.stop="uploadForm.publish"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.publish') }}
|
||||
</button>
|
||||
<button
|
||||
ref="dropdown"
|
||||
v-dropdown
|
||||
class="ui floating dropdown icon button"
|
||||
:disabled="!statusData?.canSubmit || undefined"
|
||||
>
|
||||
<i class="dropdown icon" />
|
||||
<div class="menu">
|
||||
<div
|
||||
role="button"
|
||||
class="basic item"
|
||||
@click="update(false)"
|
||||
>
|
||||
</Button>
|
||||
|
||||
<Popover v-model:open="dropdownOpen">
|
||||
<template #default="{ toggleOpen }">
|
||||
<Button
|
||||
color="primary"
|
||||
icon="bi-chevron-down"
|
||||
:disabled="!statusData?.canSubmit"
|
||||
@click="toggleOpen"
|
||||
/>
|
||||
</template>
|
||||
<template #items>
|
||||
<PopoverItem @click="update(false)">
|
||||
{{ $t('components.channels.UploadModal.button.finishLater') }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</PopoverItem>
|
||||
</template>
|
||||
</Popover>
|
||||
</div>
|
||||
<button
|
||||
|
||||
<Button
|
||||
v-if="step === 4"
|
||||
class="ui basic cancel button"
|
||||
color="secondary"
|
||||
@click="update(false)"
|
||||
>
|
||||
{{ $t('components.channels.UploadModal.button.close') }}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</semantic-modal>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
interface Events {
|
||||
|
@ -9,7 +10,7 @@ interface Events {
|
|||
interface Props {
|
||||
action?: () => void
|
||||
disabled?: boolean
|
||||
confirmColor?: 'danger' | 'success'
|
||||
confirmColor?: 'destructive' | 'primary'
|
||||
}
|
||||
|
||||
const emit = defineEmits<Events>()
|
||||
|
@ -30,7 +31,8 @@ const confirm = () => {
|
|||
|
||||
<template>
|
||||
<button
|
||||
:class="[{disabled: disabled}]"
|
||||
class="funkwhale dangerous-button"
|
||||
:class="{ 'is-disabled': disabled }"
|
||||
:disabled="disabled"
|
||||
@click.prevent.stop="showModal = true"
|
||||
>
|
||||
|
@ -51,18 +53,22 @@ const confirm = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui basic cancel button">
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
@click="showModal = false"
|
||||
>
|
||||
{{ $t('components.common.DangerousButton.button.cancel') }}
|
||||
</button>
|
||||
<button
|
||||
:class="['ui', 'confirm', confirmColor, 'button']"
|
||||
</Button>
|
||||
<Button
|
||||
:color="confirmColor"
|
||||
@click="confirm"
|
||||
>
|
||||
<slot name="modal-confirm">
|
||||
{{ $t('components.common.DangerousButton.button.confirm') }}
|
||||
</slot>
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</semantic-modal>
|
||||
</button>
|
||||
</template>
|
||||
</template>
|
|
@ -1,35 +1,49 @@
|
|||
<script setup lang="ts">
|
||||
import $ from 'jquery'
|
||||
import { onMounted } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
import Alert from '~/components/ui/Alert.vue'
|
||||
|
||||
interface Message {
|
||||
content: string
|
||||
key: string
|
||||
color?: 'blue' | 'red' | 'purple' | 'green' | 'yellow'
|
||||
error?: boolean | string
|
||||
date?: Date
|
||||
}
|
||||
|
||||
const props = defineProps<{ message: Message }>()
|
||||
|
||||
const isVisible = ref(true)
|
||||
const store = useStore()
|
||||
onMounted(() => {
|
||||
const params = {
|
||||
context: '#app',
|
||||
message: props.message.content,
|
||||
showProgress: 'top',
|
||||
position: 'bottom right',
|
||||
progressUp: true,
|
||||
onRemove () {
|
||||
store.commit('ui/removeMessage', props.message.key)
|
||||
},
|
||||
...props.message
|
||||
|
||||
const messageColor = computed(() => {
|
||||
if (props.message.color) {
|
||||
return props.message.color
|
||||
}
|
||||
|
||||
// @ts-expect-error fomantic ui
|
||||
$('body').toast(params)
|
||||
$('.ui.toast.visible').last().attr('role', 'alert')
|
||||
if (props.message.error || props.message.content?.toLowerCase().includes('error')) {
|
||||
return 'red'
|
||||
}
|
||||
|
||||
return 'blue'
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
isVisible.value = false
|
||||
store.commit('ui/removeMessage', props.message.key)
|
||||
}, 5000)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div />
|
||||
<Transition name="fade">
|
||||
<Alert
|
||||
v-if="isVisible"
|
||||
role="alert"
|
||||
:color="messageColor"
|
||||
class="is-notification"
|
||||
>
|
||||
{{ message.content }}
|
||||
</Alert>
|
||||
</Transition>
|
||||
</template>
|
||||
|
|
|
@ -10,6 +10,10 @@ import useReport from '~/composables/moderation/useReport'
|
|||
|
||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Popover from '~/components/ui/Popover.vue'
|
||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||
import DangerousButton from '~/components/common/DangerousButton.vue'
|
||||
|
||||
interface Events {
|
||||
(e: 'remove'): void
|
||||
|
@ -43,6 +47,8 @@ const musicbrainzUrl = computed(() => props.object?.mbid ? `https://musicbrainz.
|
|||
const discogsUrl = computed(() => `https://discogs.com/search/?type=release&title=${encodeURI(props.object?.title)}&artist=${encodeURI(props.object?.artist_credit[0].artist.name)}`)
|
||||
|
||||
const remove = () => emit('remove')
|
||||
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -65,120 +71,116 @@ const remove = () => emit('remove')
|
|||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui basic deny button">
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
@click="showEmbedModal = false"
|
||||
>
|
||||
{{ $t('components.library.AlbumDropdown.button.cancel') }}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</semantic-modal>
|
||||
<button
|
||||
v-dropdown="{direction: 'downward'}"
|
||||
class="ui floating dropdown circular icon basic button"
|
||||
:title="labels.more"
|
||||
>
|
||||
<i class="ellipsis vertical icon" />
|
||||
<div class="menu">
|
||||
<a
|
||||
<Popover v-model:open="open">
|
||||
<template #default="{ toggleOpen }">
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
round
|
||||
icon="bi-three-dots-vertical"
|
||||
:title="labels.more"
|
||||
@click="toggleOpen"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #items>
|
||||
<PopoverItem
|
||||
v-if="domain != $store.getters['instance/domain']"
|
||||
:href="object.fid"
|
||||
target="_blank"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
<i class="bi bi-box-arrow-up-right" />
|
||||
{{ $t('components.library.AlbumDropdown.link.domain') }}
|
||||
</a>
|
||||
</PopoverItem>
|
||||
|
||||
<div
|
||||
<PopoverItem
|
||||
v-if="isEmbedable"
|
||||
role="button"
|
||||
class="basic item"
|
||||
@click="showEmbedModal = !showEmbedModal"
|
||||
>
|
||||
<i class="code icon" />
|
||||
<i class="bi bi-code" />
|
||||
{{ $t('components.library.AlbumDropdown.button.embed') }}
|
||||
</div>
|
||||
<a
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="isAlbum && musicbrainzUrl"
|
||||
:href="musicbrainzUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
<i class="bi bi-box-arrow-up-right" />
|
||||
{{ $t('components.library.AlbumDropdown.link.musicbrainz') }}
|
||||
</a>
|
||||
<a
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="!isChannel && isAlbum"
|
||||
:href="discogsUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
<i class="bi bi-box-arrow-up-right" />
|
||||
{{ $t('components.library.AlbumDropdown.link.discogs') }}
|
||||
</a>
|
||||
<router-link
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="object.is_local"
|
||||
:to="{name: 'library.albums.edit', params: {id: object.id }}"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="edit icon" />
|
||||
<i class="bi bi-pencil" />
|
||||
{{ $t('components.library.AlbumDropdown.button.edit') }}
|
||||
</router-link>
|
||||
<dangerous-button
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="artistCredit[0] && $store.state.auth.authenticated && artistCredit[0].artist.channel && artistCredit[0].artist.attributed_to?.full_username === $store.state.auth.fullUsername"
|
||||
:class="['ui', {loading: isLoading}, 'item']"
|
||||
@confirm="remove()"
|
||||
>
|
||||
<i class="ui trash icon" />
|
||||
{{ $t('components.library.AlbumDropdown.button.delete') }}
|
||||
<template #modal-header>
|
||||
<p>
|
||||
{{ $t('components.library.AlbumDropdown.modal.delete.header') }}
|
||||
</p>
|
||||
</template>
|
||||
<template #modal-content>
|
||||
<div>
|
||||
<p>
|
||||
{{ $t('components.library.AlbumDropdown.modal.delete.content.warning') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #modal-confirm>
|
||||
<p>
|
||||
{{ $t('components.library.AlbumDropdown.button.delete') }}
|
||||
</p>
|
||||
</template>
|
||||
</dangerous-button>
|
||||
<div class="divider" />
|
||||
<div
|
||||
<DangerousButton
|
||||
:is-loading="isLoading"
|
||||
@confirm="remove()"
|
||||
>
|
||||
<i class="bi bi-trash" />
|
||||
{{ $t('components.library.AlbumDropdown.button.delete') }}
|
||||
</DangerousButton>
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-for="obj in getReportableObjects({album: object, channel: artistCredit[0]?.artist.channel})"
|
||||
:key="obj.target.type + obj.target.id"
|
||||
role="button"
|
||||
class="basic item"
|
||||
@click.stop.prevent="report(obj)"
|
||||
@click="report(obj)"
|
||||
>
|
||||
<i class="share icon" /> {{ obj.label }}
|
||||
</div>
|
||||
<div class="divider" />
|
||||
<router-link
|
||||
<i class="bi bi-flag" />
|
||||
{{ obj.label }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-if="$store.state.auth.availablePermissions['library']"
|
||||
class="basic item"
|
||||
:to="{name: 'manage.library.albums.detail', params: {id: object.id}}"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
<i class="bi bi-wrench" />
|
||||
{{ $t('components.library.AlbumDropdown.link.moderation') }}
|
||||
</router-link>
|
||||
<a
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="$store.state.auth.profile && $store.state.auth.profile?.is_superuser"
|
||||
class="basic item"
|
||||
:href="$store.getters['instance/absoluteUrl'](`/api/admin/music/album/${object.id}`)"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
<i class="bi bi-wrench" />
|
||||
{{ $t('components.library.AlbumDropdown.link.django') }}
|
||||
</a>
|
||||
</div>
|
||||
</button>
|
||||
</PopoverItem>
|
||||
</template>
|
||||
</Popover>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -14,6 +14,8 @@ import SemanticModal from '~/components/semantic/Modal.vue'
|
|||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import RadioButton from '~/components/radios/Button.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import Popover from '~/components/ui/Popover.vue'
|
||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||
|
||||
import useReport from '~/composables/moderation/useReport'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
|
@ -36,8 +38,6 @@ const nextTracksUrl = ref(null)
|
|||
const totalAlbums = ref(0)
|
||||
const totalTracks = ref(0)
|
||||
|
||||
const dropdown = ref()
|
||||
|
||||
const logger = useLogger()
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
|
@ -166,107 +166,103 @@ watch(() => props.id, fetchData, { immediate: true })
|
|||
</div>
|
||||
</semantic-modal>
|
||||
<div class="ui buttons">
|
||||
<button
|
||||
class="ui button"
|
||||
@click="dropdown.click()"
|
||||
>
|
||||
{{ $t('components.library.ArtistBase.button.more') }}
|
||||
</button>
|
||||
<button
|
||||
ref="dropdown"
|
||||
v-dropdown
|
||||
class="ui floating dropdown icon button"
|
||||
>
|
||||
<i class="dropdown icon" />
|
||||
<div class="menu">
|
||||
<a
|
||||
v-if="domain != $store.getters['instance/domain']"
|
||||
:href="object.fid"
|
||||
target="_blank"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.domain', {domain: domain}) }}
|
||||
</a>
|
||||
<Popover>
|
||||
<template #default="{ toggleOpen }">
|
||||
<button
|
||||
class="ui button"
|
||||
@click="toggleOpen"
|
||||
>
|
||||
{{ $t('components.library.ArtistBase.button.more') }}
|
||||
<i class="dropdown icon" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<button
|
||||
v-if="publicLibraries.length > 0"
|
||||
role="button"
|
||||
class="basic item"
|
||||
@click.prevent="showEmbedModal = !showEmbedModal"
|
||||
>
|
||||
<i class="code icon" />
|
||||
{{ $t('components.library.ArtistBase.button.embed') }}
|
||||
</button>
|
||||
<a
|
||||
:href="wikipediaUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="wikipedia w icon" />
|
||||
{{ $t('components.library.ArtistBase.link.wikipedia') }}
|
||||
</a>
|
||||
<a
|
||||
v-if="musicbrainzUrl"
|
||||
:href="musicbrainzUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.musicbrainz') }}
|
||||
</a>
|
||||
<a
|
||||
:href="discogsUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.discogs') }}
|
||||
</a>
|
||||
<router-link
|
||||
v-if="object.is_local"
|
||||
:to="{name: 'library.artists.edit', params: {id: object.id }}"
|
||||
class="basic item"
|
||||
>
|
||||
<i class="edit icon" />
|
||||
{{ $t('components.library.ArtistBase.button.edit') }}
|
||||
</router-link>
|
||||
<div class="divider" />
|
||||
<div
|
||||
v-for="obj in getReportableObjects({artist: object})"
|
||||
:key="obj.target.type + obj.target.id"
|
||||
role="button"
|
||||
class="basic item"
|
||||
@click.stop.prevent="report(obj)"
|
||||
>
|
||||
<i class="share icon" /> {{ obj.label }}
|
||||
</div>
|
||||
<template #items>
|
||||
<PopoverItem
|
||||
v-if="domain != $store.getters['instance/domain']"
|
||||
:href="object.fid"
|
||||
target="_blank"
|
||||
class="funkwhale item"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.domain', {domain: domain}) }}
|
||||
</PopoverItem>
|
||||
|
||||
<div class="divider" />
|
||||
<router-link
|
||||
v-if="$store.state.auth.availablePermissions['library']"
|
||||
class="basic item"
|
||||
:to="{name: 'manage.library.artists.detail', params: {id: object.id}}"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
{{ $t('components.library.ArtistBase.link.moderation') }}
|
||||
</router-link>
|
||||
<a
|
||||
v-if="$store.state.auth.profile && $store.state.auth.profile.is_superuser"
|
||||
class="basic item"
|
||||
:href="$store.getters['instance/absoluteUrl'](`/api/admin/music/artist/${object.id}`)"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
{{ $t('components.library.ArtistBase.link.django') }}
|
||||
</a>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<PopoverItem
|
||||
v-if="publicLibraries.length > 0"
|
||||
@click="showEmbedModal = !showEmbedModal"
|
||||
>
|
||||
<i class="code icon" />
|
||||
{{ $t('components.library.ArtistBase.button.embed') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
:href="wikipediaUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<i class="wikipedia w icon" />
|
||||
{{ $t('components.library.ArtistBase.link.wikipedia') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="musicbrainzUrl"
|
||||
:href="musicbrainzUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.musicbrainz') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
:href="discogsUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<i class="external icon" />
|
||||
{{ $t('components.library.ArtistBase.link.discogs') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="object.is_local"
|
||||
:to="{name: 'library.artists.edit', params: {id: object.id }}"
|
||||
>
|
||||
<i class="edit icon" />
|
||||
{{ $t('components.library.ArtistBase.button.edit') }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-for="obj in getReportableObjects({artist: object})"
|
||||
:key="obj.target.type + obj.target.id"
|
||||
@click="report(obj)"
|
||||
>
|
||||
<i class="share icon" /> {{ obj.label }}
|
||||
</PopoverItem>
|
||||
|
||||
<hr>
|
||||
|
||||
<PopoverItem
|
||||
v-if="$store.state.auth.availablePermissions['library']"
|
||||
:to="{name: 'manage.library.artists.detail', params: {id: object.id}}"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
{{ $t('components.library.ArtistBase.link.moderation') }}
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="$store.state.auth.profile && $store.state.auth.profile.is_superuser"
|
||||
:href="$store.getters['instance/absoluteUrl'](`/api/admin/music/artist/${object.id}`)"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i class="wrench icon" />
|
||||
{{ $t('components.library.ArtistBase.link.django') }}
|
||||
</PopoverItem>
|
||||
</template>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -74,14 +74,12 @@ const toggleRadio = () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="['ui', 'primary', {'inverted': running}, 'icon', 'labeled', 'button']"
|
||||
<Button
|
||||
:is-active="running"
|
||||
color="primary"
|
||||
icon="bi-broadcast"
|
||||
@click="toggleRadio"
|
||||
>
|
||||
<i
|
||||
class="ui feed icon"
|
||||
role="button"
|
||||
/>
|
||||
{{ buttonLabel }}
|
||||
</button>
|
||||
</Button>
|
||||
</template>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import $ from 'jquery'
|
||||
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
||||
import { computed, onBeforeUnmount, ref, watchEffect } from 'vue'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { useStore } from '~/store'
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
|
||||
interface Events {
|
||||
(e: 'update:show', show: boolean): void
|
||||
|
@ -26,55 +26,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
scrolling: false,
|
||||
additionalClasses: () => []
|
||||
})
|
||||
|
||||
const modal = ref()
|
||||
const { activate, deactivate, pause, unpause } = useFocusTrap(modal, {
|
||||
allowOutsideClick: true
|
||||
})
|
||||
|
||||
const show = useVModel(props, 'show', emit)
|
||||
|
||||
const control = ref<JQuery | undefined>()
|
||||
const initModal = () => {
|
||||
control.value = $(modal.value).modal({
|
||||
duration: 100,
|
||||
onApprove: () => emit('approved'),
|
||||
onDeny: () => emit('deny'),
|
||||
onHidden: () => (show.value = false)
|
||||
})
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (show.value) {
|
||||
initModal()
|
||||
emit('show')
|
||||
control.value?.modal('show')
|
||||
activate()
|
||||
unpause()
|
||||
document.body.classList.add('scrolling')
|
||||
return
|
||||
}
|
||||
|
||||
if (control.value) {
|
||||
emit('hide')
|
||||
control.value.modal('hide')
|
||||
control.value.remove()
|
||||
deactivate()
|
||||
pause()
|
||||
document.body.classList.remove('scrolling')
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
control.value?.modal('hide')
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
const classes = computed(() => [
|
||||
...props.additionalClasses,
|
||||
'ui', 'modal',
|
||||
{
|
||||
active: show.value,
|
||||
scrolling: props.scrolling,
|
||||
'overlay fullscreen': props.fullscreen && ['phone', 'tablet'].includes(store.getters['ui/windowSize'])
|
||||
}
|
||||
|
@ -82,14 +37,15 @@ const classes = computed(() => [
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="modal"
|
||||
<Modal
|
||||
:model-value="show"
|
||||
@update:model-value="(value) => emit('update:show', value)"
|
||||
:class="classes"
|
||||
@approve="emit('approved')"
|
||||
@deny="emit('deny')"
|
||||
@show="emit('show')"
|
||||
@hide="emit('hide')"
|
||||
>
|
||||
<i
|
||||
tabindex="0"
|
||||
class="close inside icon"
|
||||
/>
|
||||
<slot v-if="show" />
|
||||
</div>
|
||||
<slot />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
|
@ -3,10 +3,21 @@ import { useColorOrPastel, type ColorProps, type PastelProps } from '~/composabl
|
|||
|
||||
const props = defineProps<ColorProps | PastelProps>()
|
||||
const color = useColorOrPastel(() => props.color, 'secondary')
|
||||
const emit = defineEmits<{
|
||||
click: [event: MouseEvent]
|
||||
}>()
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
emit('click', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="funkwhale is-colored pill" :class="[color]">
|
||||
<button
|
||||
type="button"
|
||||
class="funkwhale is-colored pill"
|
||||
:class="[color]"
|
||||
@click.stop="handleClick"
|
||||
>
|
||||
<div v-if="!!$slots.image" class="pill-image">
|
||||
<slot name="image" />
|
||||
</div>
|
||||
|
|
|
@ -41,4 +41,25 @@
|
|||
> .actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Add styles for when alert is used as a notification
|
||||
&.is-notification {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
max-width: 400px;
|
||||
|
||||
&.fade-enter-active,
|
||||
&.fade-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
&.fade-enter-from,
|
||||
&.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(1rem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
@include docs {
|
||||
@if $docs {
|
||||
color: var(--fw-text-color) !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { inject, ref } from 'vue'
|
||||
import { POPOVER_CONTEXT_INJECTION_KEY, type PopoverContext } from '~/injection-keys'
|
||||
|
||||
const setId = defineEmit<[value: number]>('internal:id')
|
||||
const emit = defineEmits<{'internal:id': [value: number]}>()
|
||||
|
||||
const { parentPopoverContext } = defineProps<{ parentPopoverContext?: PopoverContext }>()
|
||||
const { items, hoveredItem } = parentPopoverContext ?? inject(POPOVER_CONTEXT_INJECTION_KEY, {
|
||||
|
@ -11,7 +11,7 @@ const { items, hoveredItem } = parentPopoverContext ?? inject(POPOVER_CONTEXT_IN
|
|||
})
|
||||
|
||||
const id = items.value++
|
||||
setId(id)
|
||||
emit('internal:id', id)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
Loading…
Reference in New Issue