Add semantic ui types
This commit is contained in:
parent
af0129b598
commit
405eed0c0f
|
@ -58,6 +58,7 @@
|
|||
"@types/jquery": "3.5.14",
|
||||
"@types/lodash-es": "4.17.6",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/semantic-ui": "^2.2.7",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@vitejs/plugin-vue": "2.3.3",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import Modal from '~/components/semantic/Modal.vue'
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
@ -103,7 +103,7 @@ const player = computed(() => [
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<modal v-model:show="showRef">
|
||||
<semantic-modal v-model:show="showRef">
|
||||
<header class="header">
|
||||
<translate translate-context="*/*/*/Noun">
|
||||
Keyboard shortcuts
|
||||
|
@ -156,5 +156,5 @@ const player = computed(() => [
|
|||
</translate>
|
||||
</button>
|
||||
</footer>
|
||||
</modal>
|
||||
</semantic-modal>
|
||||
</template>
|
||||
|
|
|
@ -1,3 +1,133 @@
|
|||
<script setup lang="ts">
|
||||
import type { RouteRecordName } from 'vue-router'
|
||||
|
||||
import UserModal from '~/components/common/UserModal.vue'
|
||||
import Logo from '~/components/Logo.vue'
|
||||
import SearchBar from '~/components/audio/SearchBar.vue'
|
||||
import UserMenu from '~/components/common/UserMenu.vue'
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
|
||||
import useThemeList from '~/composables/useThemeList'
|
||||
import useTheme from '~/composables/useTheme'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { computed, ref, watch, watchEffect, onMounted } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import { useStore } from '~/store'
|
||||
import { setupDropdown } from '~/utils/fomantic'
|
||||
|
||||
interface Props {
|
||||
width: number
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const store = useStore()
|
||||
const theme = useTheme()
|
||||
const themes = useThemeList()
|
||||
const { $pgettext } = useGettext()
|
||||
|
||||
const route = useRoute()
|
||||
const isCollapsed = ref(true)
|
||||
watch(() => route.path, () => (isCollapsed.value = true))
|
||||
|
||||
const additionalNotifications = computed(() => store.getters['ui/additionalNotifications'])
|
||||
const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index' : 'index')
|
||||
|
||||
const labels = computed(() => ({
|
||||
mainMenu: $pgettext('Sidebar/*/Hidden text', 'Main menu'),
|
||||
selectTrack: $pgettext('Sidebar/Player/Hidden text', 'Play this track'),
|
||||
pendingFollows: $pgettext('Sidebar/Notifications/Hidden text', 'Pending follow requests'),
|
||||
pendingReviewEdits: $pgettext('Sidebar/Moderation/Hidden text', 'Pending review edits'),
|
||||
pendingReviewReports: $pgettext('Sidebar/Moderation/Hidden text', 'Pending review reports'),
|
||||
language: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Language'),
|
||||
theme: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Theme'),
|
||||
addContent: $pgettext('*/Library/*/Verb', 'Add content'),
|
||||
administration: $pgettext('Sidebar/Admin/Title/Noun', 'Administration')
|
||||
}))
|
||||
|
||||
type SidebarMenuTabs = 'explore' | 'myLibrary'
|
||||
const expanded = ref<SidebarMenuTabs>('explore')
|
||||
|
||||
const ROUTE_MAPPINGS: Record<SidebarMenuTabs, RouteRecordName[]> = {
|
||||
explore: [
|
||||
'search',
|
||||
'library.index',
|
||||
'library.podcasts.browse',
|
||||
'library.albums.browse',
|
||||
'library.albums.detail',
|
||||
'library.artists.browse',
|
||||
'library.artists.detail',
|
||||
'library.tracks.detail',
|
||||
'library.playlists.browse',
|
||||
'library.playlists.detail',
|
||||
'library.radios.browse',
|
||||
'library.radios.detail'
|
||||
],
|
||||
myLibrary: [
|
||||
'library.me',
|
||||
'library.albums.me',
|
||||
'library.artists.me',
|
||||
'library.playlists.me',
|
||||
'library.radios.me',
|
||||
'favorites'
|
||||
]
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (ROUTE_MAPPINGS.explore.includes(route.name as RouteRecordName)) {
|
||||
expanded.value = 'explore'
|
||||
return
|
||||
}
|
||||
|
||||
if (ROUTE_MAPPINGS.myLibrary.includes(route.name as RouteRecordName)) {
|
||||
expanded.value = 'myLibrary'
|
||||
return
|
||||
}
|
||||
|
||||
expanded.value = store.state.auth.authenticated ? 'myLibrary' : 'explore'
|
||||
})
|
||||
|
||||
const moderationNotifications = computed(() =>
|
||||
store.state.ui.notifications.pendingReviewEdits
|
||||
+ store.state.ui.notifications.pendingReviewReports
|
||||
+ store.state.ui.notifications.pendingReviewRequests
|
||||
)
|
||||
|
||||
const isProduction = import.meta.env.PROD
|
||||
const showUserModal = ref(false)
|
||||
const showLanguageModal = ref(false)
|
||||
const showThemeModal = ref(false)
|
||||
|
||||
// TODO (wvffle): Use current language this.$language.current
|
||||
const languageSelection = undefined
|
||||
// export default {
|
||||
// watch: {
|
||||
// languageSelection: function (v) {
|
||||
// this.$store.dispatch('ui/currentLanguage', v)
|
||||
// this.$refs.languageModal.closeModal()
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
|
||||
watch(() => store.state.auth.authenticated, (authenticated) => {
|
||||
if (authenticated) {
|
||||
setupDropdown('.admin-dropdown')
|
||||
}
|
||||
|
||||
setupDropdown('.user-dropdown')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => store.state.auth.availablePermissions, () => {
|
||||
if (store.state.auth.authenticated) {
|
||||
setupDropdown('.admin-dropdown')
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
document.getElementById('fake-sidebar')?.classList.add('loaded')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside :class="['ui', 'vertical', 'left', 'visible', 'wide', {'collapsed': isCollapsed}, 'sidebar', 'component-sidebar']">
|
||||
<header class="ui basic segment header-wrapper">
|
||||
|
@ -54,7 +184,7 @@
|
|||
:to="{name: 'manage.moderation.reports.list', query: {q: 'resolved:no'}}"
|
||||
>
|
||||
<div
|
||||
v-if="$store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests> 0"
|
||||
v-if="$store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests > 0"
|
||||
:title="labels.pendingReviewReports"
|
||||
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
||||
>
|
||||
|
@ -131,10 +261,10 @@
|
|||
@click.prevent.exact="showUserModal = !showUserModal"
|
||||
>
|
||||
<img
|
||||
v-if="$store.state.auth.authenticated && $store.state.auth.profile.avatar && $store.state.auth.profile.avatar.urls.medium_square_crop"
|
||||
v-if="$store.state.auth.authenticated && $store.state.auth.profile?.avatar.urls.medium_square_crop"
|
||||
class="ui avatar image"
|
||||
alt=""
|
||||
:src="$store.getters['instance/absoluteUrl']($store.state.auth.profile.avatar.urls.medium_square_crop)"
|
||||
:src="$store.getters['instance/absoluteUrl']($store.state.auth.profile?.avatar.urls.medium_square_crop)"
|
||||
>
|
||||
<actor-avatar
|
||||
v-else-if="$store.state.auth.authenticated"
|
||||
|
@ -157,7 +287,7 @@
|
|||
@show-theme-modal-event="showThemeModal=true"
|
||||
@show-language-modal-event="showLanguageModal=true"
|
||||
/>
|
||||
<modal
|
||||
<semantic-modal
|
||||
ref="languageModal"
|
||||
v-model:show="showLanguageModal"
|
||||
:fullscreen="false"
|
||||
|
@ -178,17 +308,17 @@
|
|||
:key="key"
|
||||
>
|
||||
<input
|
||||
:id="key"
|
||||
:id="`${key}`"
|
||||
v-model="languageSelection"
|
||||
type="radio"
|
||||
name="language"
|
||||
:value="key"
|
||||
>
|
||||
<label :for="key">{{ language }}</label>
|
||||
<label :for="`${key}`">{{ language }}</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
</modal>
|
||||
<modal
|
||||
</semantic-modal>
|
||||
<semantic-modal
|
||||
ref="themeModal"
|
||||
v-model:show="showThemeModal"
|
||||
:fullscreen="false"
|
||||
|
@ -218,7 +348,7 @@
|
|||
<label :for="t.key">{{ t.name }}</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
</modal>
|
||||
</semantic-modal>
|
||||
<div class="item collapse-button-wrapper">
|
||||
<button
|
||||
:class="['ui', 'basic', 'big', {'vibrant': !isCollapsed}, 'inverted icon', 'collapse', 'button']"
|
||||
|
@ -269,27 +399,27 @@
|
|||
</h1>
|
||||
<div class="ui small hidden divider" />
|
||||
<section
|
||||
:class="['ui', 'bottom', 'attached', {active: selectedTab === 'library'}, 'tab']"
|
||||
:aria-label="labels.mainMenu"
|
||||
class="ui bottom attached active tab"
|
||||
>
|
||||
<nav
|
||||
class="ui vertical large fluid inverted menu"
|
||||
role="navigation"
|
||||
:aria-label="labels.mainMenu"
|
||||
>
|
||||
<div :class="[{collapsed: !exploreExpanded}, 'collapsible item']">
|
||||
<div :class="[{ collapsed: expanded !== 'explore' }, 'collapsible item']">
|
||||
<h2
|
||||
class="header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="exploreExpanded = true"
|
||||
@focus="exploreExpanded = true"
|
||||
@click="expanded = 'explore'"
|
||||
@focus="expanded = 'explore'"
|
||||
>
|
||||
<translate translate-context="*/*/*/Verb">
|
||||
Explore
|
||||
</translate>
|
||||
<i
|
||||
v-if="!exploreExpanded"
|
||||
v-if="expanded !== 'explore'"
|
||||
class="angle right icon"
|
||||
/>
|
||||
</h2>
|
||||
|
@ -355,20 +485,20 @@
|
|||
</div>
|
||||
<div
|
||||
v-if="$store.state.auth.authenticated"
|
||||
:class="[{collapsed: !myLibraryExpanded}, 'collapsible item']"
|
||||
:class="[{ collapsed: expanded !== 'myLibrary' }, 'collapsible item']"
|
||||
>
|
||||
<h3
|
||||
class="header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="myLibraryExpanded = true"
|
||||
@focus="myLibraryExpanded = true"
|
||||
@click="expanded = 'myLibrary'"
|
||||
@focus="expanded = 'myLibrary'"
|
||||
>
|
||||
<translate translate-context="*/*/*/Noun">
|
||||
My Library
|
||||
</translate>
|
||||
<i
|
||||
v-if="!myLibraryExpanded"
|
||||
v-if="expanded !== 'myLibrary'"
|
||||
class="angle right icon"
|
||||
/>
|
||||
</h3>
|
||||
|
@ -451,7 +581,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!production"
|
||||
v-if="!isProduction"
|
||||
class="item"
|
||||
>
|
||||
<a
|
||||
|
@ -467,251 +597,6 @@
|
|||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapActions, mapGetters } from 'vuex'
|
||||
import UserModal from '~/components/common/UserModal.vue'
|
||||
import Logo from '~/components/Logo.vue'
|
||||
import SearchBar from '~/components/audio/SearchBar.vue'
|
||||
import UserMenu from '~/components/common/UserMenu.vue'
|
||||
import Modal from '~/components/semantic/Modal.vue'
|
||||
|
||||
import $ from 'jquery'
|
||||
import useThemeList from '~/composables/useThemeList'
|
||||
import useTheme from '~/composables/useTheme'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { computed } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'Sidebar',
|
||||
components: {
|
||||
SearchBar,
|
||||
Logo,
|
||||
UserMenu,
|
||||
UserModal,
|
||||
Modal
|
||||
},
|
||||
props: {
|
||||
width: { type: Number, required: true }
|
||||
},
|
||||
setup () {
|
||||
const theme = useTheme()
|
||||
const themes = useThemeList()
|
||||
|
||||
const route = useRoute()
|
||||
const url = computed(() => route.path)
|
||||
|
||||
return {
|
||||
theme,
|
||||
themes,
|
||||
url
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selectedTab: 'library',
|
||||
isCollapsed: true,
|
||||
fetchInterval: null,
|
||||
exploreExpanded: false,
|
||||
myLibraryExpanded: false,
|
||||
showUserModal: false,
|
||||
showLanguageModal: false,
|
||||
showThemeModal: false,
|
||||
languageSelection: this.$language.current
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
if (this.fetchInterval) {
|
||||
clearInterval(this.fetchInterval)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
queue: state => state.queue
|
||||
}),
|
||||
...mapGetters({
|
||||
additionalNotifications: 'ui/additionalNotifications'
|
||||
}),
|
||||
labels () {
|
||||
const mainMenu = this.$pgettext('Sidebar/*/Hidden text', 'Main menu')
|
||||
const selectTrack = this.$pgettext('Sidebar/Player/Hidden text', 'Play this track')
|
||||
const pendingFollows = this.$pgettext('Sidebar/Notifications/Hidden text', 'Pending follow requests')
|
||||
const pendingReviewEdits = this.$pgettext('Sidebar/Moderation/Hidden text', 'Pending review edits')
|
||||
const language = this.$pgettext(
|
||||
'Sidebar/Settings/Dropdown.Label/Short, Verb',
|
||||
'Language')
|
||||
const theme = this.$pgettext(
|
||||
'Sidebar/Settings/Dropdown.Label/Short, Verb',
|
||||
'Theme')
|
||||
return {
|
||||
pendingFollows,
|
||||
mainMenu,
|
||||
selectTrack,
|
||||
pendingReviewEdits,
|
||||
language,
|
||||
theme,
|
||||
addContent: this.$pgettext('*/Library/*/Verb', 'Add content'),
|
||||
administration: this.$pgettext('Sidebar/Admin/Title/Noun', 'Administration')
|
||||
}
|
||||
},
|
||||
logoUrl () {
|
||||
if (this.$store.state.auth.authenticated) {
|
||||
return 'library.index'
|
||||
} else {
|
||||
return 'index'
|
||||
}
|
||||
},
|
||||
focusedMenu () {
|
||||
const mapping = {
|
||||
search: 'exploreExpanded',
|
||||
'library.index': 'exploreExpanded',
|
||||
'library.podcasts.browse': 'exploreExpanded',
|
||||
'library.albums.browse': 'exploreExpanded',
|
||||
'library.albums.detail': 'exploreExpanded',
|
||||
'library.artists.browse': 'exploreExpanded',
|
||||
'library.artists.detail': 'exploreExpanded',
|
||||
'library.tracks.detail': 'exploreExpanded',
|
||||
'library.playlists.browse': 'exploreExpanded',
|
||||
'library.playlists.detail': 'exploreExpanded',
|
||||
'library.radios.browse': 'exploreExpanded',
|
||||
'library.radios.detail': 'exploreExpanded',
|
||||
'library.me': 'myLibraryExpanded',
|
||||
'library.albums.me': 'myLibraryExpanded',
|
||||
'library.artists.me': 'myLibraryExpanded',
|
||||
'library.playlists.me': 'myLibraryExpanded',
|
||||
'library.radios.me': 'myLibraryExpanded',
|
||||
favorites: 'myLibraryExpanded'
|
||||
}
|
||||
const m = mapping[this.$route.name]
|
||||
if (m) {
|
||||
return m
|
||||
}
|
||||
|
||||
if (this.$store.state.auth.authenticated) {
|
||||
return 'myLibraryExpanded'
|
||||
} else {
|
||||
return 'exploreExpanded'
|
||||
}
|
||||
},
|
||||
moderationNotifications () {
|
||||
return (
|
||||
this.$store.state.ui.notifications.pendingReviewEdits +
|
||||
this.$store.state.ui.notifications.pendingReviewReports +
|
||||
this.$store.state.ui.notifications.pendingReviewRequests
|
||||
)
|
||||
},
|
||||
production () {
|
||||
return import.meta.env.PROD
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url: function () {
|
||||
this.isCollapsed = true
|
||||
},
|
||||
'$store.state.moderation.lastUpdate': function () {
|
||||
this.applyContentFilters()
|
||||
},
|
||||
'$store.state.auth.authenticated': {
|
||||
immediate: true,
|
||||
handler (v) {
|
||||
if (v) {
|
||||
this.$nextTick(() => {
|
||||
this.setupDropdown('.user-dropdown')
|
||||
this.setupDropdown('.admin-dropdown')
|
||||
})
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.setupDropdown('.user-dropdown')
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
'$store.state.auth.availablePermissions': {
|
||||
immediate: true,
|
||||
handler (v) {
|
||||
this.$nextTick(() => {
|
||||
this.setupDropdown('.admin-dropdown')
|
||||
})
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
focusedMenu: {
|
||||
immediate: true,
|
||||
handler (n) {
|
||||
if (n) {
|
||||
this[n] = true
|
||||
}
|
||||
}
|
||||
},
|
||||
myLibraryExpanded (v) {
|
||||
if (v) {
|
||||
this.exploreExpanded = false
|
||||
}
|
||||
},
|
||||
exploreExpanded (v) {
|
||||
if (v) {
|
||||
this.myLibraryExpanded = false
|
||||
}
|
||||
},
|
||||
languageSelection: function (v) {
|
||||
this.$store.dispatch('ui/currentLanguage', v)
|
||||
this.$refs.languageModal.closeModal()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
document.getElementById('fake-sidebar').classList.add('loaded')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
cleanTrack: 'queue/cleanTrack'
|
||||
}),
|
||||
applyContentFilters () {
|
||||
const artistIds = this.$store.getters['moderation/artistFilters']().map((f) => {
|
||||
return f.target.id
|
||||
})
|
||||
|
||||
if (artistIds.length === 0) {
|
||||
return
|
||||
}
|
||||
const self = this
|
||||
const tracks = this.tracks.slice().reverse()
|
||||
tracks.forEach(async (t, i) => {
|
||||
// we loop from the end because removing index from the start can lead to removing the wrong tracks
|
||||
const realIndex = tracks.length - i - 1
|
||||
const matchArtist = artistIds.indexOf(t.artist.id) > -1
|
||||
if (matchArtist) {
|
||||
return await self.cleanTrack(realIndex)
|
||||
}
|
||||
if (t.album && artistIds.indexOf(t.album.artist.id) > -1) {
|
||||
return await self.cleanTrack(realIndex)
|
||||
}
|
||||
})
|
||||
},
|
||||
setupDropdown (selector) {
|
||||
const self = this
|
||||
$(self.$el).find(selector).dropdown({
|
||||
selectOnKeydown: false,
|
||||
action: function (text, value, $el) {
|
||||
// used ton ensure focusing the dropdown and clicking via keyboard
|
||||
// works as expected
|
||||
const link = $($el).closest('a')
|
||||
const url = link.attr('href')
|
||||
if (url) {
|
||||
if (url.startsWith('http')) {
|
||||
window.open(url, '_blank').focus()
|
||||
} else {
|
||||
self.$router.push(url)
|
||||
}
|
||||
}
|
||||
|
||||
$(self.$el).find(selector).dropdown('hide')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
[type="radio"] {
|
||||
position: absolute;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useGettext } from 'vue3-gettext'
|
|||
import usePlayOptions from '~/composables/audio/usePlayOptions'
|
||||
import useReport from '~/composables/moderation/useReport'
|
||||
import { useCurrentElement } from '@vueuse/core'
|
||||
import jQuery from 'jquery'
|
||||
import { setupDropdown, getDropdown } from '~/utils/fomantic'
|
||||
|
||||
interface Props extends PlayOptionsProps {
|
||||
dropdownIconClasses?: string[]
|
||||
|
@ -95,27 +95,13 @@ const title = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const el = useCurrentElement()
|
||||
watch(clicked, async () => {
|
||||
await nextTick()
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown({
|
||||
selectOnKeydown: false,
|
||||
action (text: unknown, value: unknown, $el: JQuery) {
|
||||
// used to ensure focusing the dropdown and clicking via keyboard
|
||||
// works as expected
|
||||
$el[0].click()
|
||||
await setupDropdown()
|
||||
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown('hide')
|
||||
}
|
||||
})
|
||||
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown('show', function () {
|
||||
getDropdown().dropdown('show', function () {
|
||||
// little magic to ensure the menu is always visible in the viewport
|
||||
// By default, try to diplay it on the right if there is enough room
|
||||
const menu = jQuery(el.value).find('.ui.dropdown').find('.menu')
|
||||
const menu = getDropdown().find('.menu')
|
||||
const viewportOffset = menu.get(0)?.getBoundingClientRect() ?? { right: 0, left: 0 }
|
||||
const viewportWidth = document.documentElement.clientWidth
|
||||
const rightOverflow = viewportOffset.right - viewportWidth
|
||||
|
|
|
@ -24,7 +24,6 @@ onMounted(() => {
|
|||
...props.message
|
||||
}
|
||||
|
||||
// @ts-expect-error toast is from semantic ui
|
||||
$('body').toast(params)
|
||||
$('.ui.toast.visible').last().attr('role', 'alert')
|
||||
})
|
||||
|
|
|
@ -100,7 +100,6 @@ const fetchFavorites = async () => {
|
|||
onBeforeRouteUpdate(fetchFavorites)
|
||||
fetchFavorites()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
|
@ -104,7 +104,6 @@ watch(() => store.state.moderation.lastUpdate, fetchData)
|
|||
onBeforeRouteUpdate(fetchData)
|
||||
fetchData()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
|
@ -108,7 +108,6 @@ watch([() => store.state.moderation.lastUpdate, excludeCompilation], fetchData)
|
|||
onBeforeRouteUpdate(fetchData)
|
||||
fetchData()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
|
@ -28,7 +28,7 @@ const headers = computed(() => {
|
|||
const patchFileData = (file: VueUploadItem, data: Record<string, unknown> = {}) => {
|
||||
let metadata = data.import_metadata as Record<string, unknown>
|
||||
|
||||
// @ts-expect-error Taken from vue-upload-component@3.1.2
|
||||
// @ts-expect-error Taken from 3.1.2
|
||||
const filename: string = file.file.name || file.file.filename || file.name
|
||||
data.source = `upload://${filename}`
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@ watch(() => store.state.moderation.lastUpdate, fetchData)
|
|||
onBeforeRouteUpdate(fetchData)
|
||||
fetchData()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
|
@ -93,7 +93,6 @@ const hasFavorites = computed(() => store.state.favorites.count > 0)
|
|||
onBeforeRouteUpdate(fetchData)
|
||||
fetchData()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
|
@ -58,7 +58,6 @@ const privacyLevelChoices = computed(() => [
|
|||
const el = useCurrentElement()
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
$(el.value).find('.dropdown').dropdown()
|
||||
})
|
||||
|
||||
|
|
|
@ -25,9 +25,8 @@ const { activate, deactivate, pause, unpause } = useFocusTrap(modal)
|
|||
|
||||
const show = useVModel(props, 'show', emit)
|
||||
|
||||
const control = ref()
|
||||
const control = ref<JQuery | undefined>()
|
||||
const initModal = () => {
|
||||
// @ts-expect-error modal is from semantic ui
|
||||
control.value = $(modal.value).modal({
|
||||
duration: 100,
|
||||
onApprove: () => emit('approved'),
|
||||
|
|
|
@ -133,7 +133,6 @@ export default (props: PlayOptionsProps) => {
|
|||
|
||||
const el = useCurrentElement()
|
||||
const enqueue = async () => {
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown('hide')
|
||||
|
||||
const tracks = await getPlayableTracks()
|
||||
|
@ -141,7 +140,6 @@ export default (props: PlayOptionsProps) => {
|
|||
}
|
||||
|
||||
const enqueueNext = async (next = false) => {
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown('hide')
|
||||
|
||||
const tracks = await getPlayableTracks()
|
||||
|
@ -160,7 +158,6 @@ export default (props: PlayOptionsProps) => {
|
|||
const replacePlay = async () => {
|
||||
store.dispatch('queue/clean')
|
||||
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el.value).find('.ui.dropdown').dropdown('hide')
|
||||
|
||||
const tracks = await getPlayableTracks()
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
import type { InitModule } from '~/types'
|
||||
|
||||
import jQuery from '~/jquery'
|
||||
import { setupDropdown } from '~/utils/fomantic'
|
||||
|
||||
export const install: InitModule = ({ app, store }) => {
|
||||
app.directive('title', function (el, binding) {
|
||||
store.commit('ui/pageTitle', binding.value)
|
||||
})
|
||||
|
||||
app.directive('dropdown', function (el, binding) {
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el).dropdown({
|
||||
selectOnKeydown: false,
|
||||
action (text: string, value: string, $el: JQuery<HTMLElement>) {
|
||||
// used to ensure focusing the dropdown and clicking via keyboard
|
||||
// works as expected
|
||||
$el[0]?.click()
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el).find('.ui.dropdown').dropdown('hide')
|
||||
},
|
||||
...(binding.value || {})
|
||||
})
|
||||
})
|
||||
app.directive('dropdown', setupDropdown)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/// <reference types="semantic-ui" />
|
||||
|
||||
import $ from 'jquery'
|
||||
import { nextTick } from 'vue'
|
||||
import { useCurrentElement } from '@vueuse/core'
|
||||
|
||||
const el = useCurrentElement()
|
||||
|
||||
export const getDropdown = (selector = '.ui.dropdown'): JQuery => {
|
||||
return $(el.value).find(selector)
|
||||
}
|
||||
|
||||
export const setupDropdown = async (selector: string | HTMLElement = '.ui.dropdown') => {
|
||||
if (typeof selector === 'string') {
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
const $dropdown = typeof selector === 'string'
|
||||
? $(el.value).find(selector)
|
||||
: $(selector)
|
||||
|
||||
$dropdown.dropdown({
|
||||
selectOnKeydown: false,
|
||||
action (text: unknown, value: unknown, $el: JQuery) {
|
||||
// used to ensure focusing the dropdown and clicking via keyboard
|
||||
// works as expected
|
||||
$el[0]?.click()
|
||||
|
||||
$dropdown.dropdown('hide')
|
||||
}
|
||||
})
|
||||
|
||||
return $dropdown
|
||||
}
|
|
@ -132,15 +132,14 @@ if (route.hash) {
|
|||
scrollTo(route.hash.slice(1))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
$('select.dropdown').dropdown()
|
||||
const el = useCurrentElement()
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
$(el.value).find('select.dropdown').dropdown()
|
||||
})
|
||||
|
||||
const el = useCurrentElement()
|
||||
watch(settingsData, async () => {
|
||||
await nextTick()
|
||||
// @ts-expect-error sticky is from semantic ui
|
||||
$(el.value).find('.sticky').sticky({ context: '#settings-grid' })
|
||||
})
|
||||
|
||||
|
|
|
@ -89,7 +89,6 @@ const fetchData = async () => {
|
|||
onBeforeRouteUpdate(fetchData)
|
||||
fetchData()
|
||||
|
||||
// @ts-expect-error semantic ui
|
||||
onMounted(() => $('.ui.dropdown').dropdown())
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
|
173
front/yarn.lock
173
front/yarn.lock
|
@ -1422,7 +1422,7 @@
|
|||
jest-matcher-utils "^28.0.0"
|
||||
pretty-format "^28.0.0"
|
||||
|
||||
"@types/jquery@3.5.14":
|
||||
"@types/jquery@*", "@types/jquery@3.5.14":
|
||||
version "3.5.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.14.tgz#ac8e11ee591e94d4d58da602cb3a5a8320dee577"
|
||||
integrity sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==
|
||||
|
@ -1488,6 +1488,177 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semantic-ui-accordion@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-accordion/-/semantic-ui-accordion-2.2.2.tgz#2f3b9d60a981d9eddc3705619079defaacc3aee0"
|
||||
integrity sha512-XClXI/20W7iFLQ7eyslZswbdv3A4qWEnFz8JvOylGatCW7biTLVhMBPcN0b17TZ1GeV4V/l3ctmvTEBCHvg8CA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-api@*":
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-api/-/semantic-ui-api-2.2.4.tgz#ea9cad381b38b77f95fa3bf679d7c5273c7b20dd"
|
||||
integrity sha512-6IvCjZDJ0TVb1EtnNFQPNyVmOZnLGPEKyEAs9G0FF3XuAyyOdtfD4NGHJ0kknnX1FD+++ye0EuSVFDutbwEFWw==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-checkbox@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-checkbox/-/semantic-ui-checkbox-2.2.2.tgz#bb19b20d503103a724c9dc79f71132edb092a362"
|
||||
integrity sha512-ZTAy3yNwOAaoznxsFoR33XopJnyyzAGrLeDpn7hVTkUYm7wgGsgOpRfG2psdM3fIOCkZkkE9IEAp1pLGBifL1g==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-dimmer@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-dimmer/-/semantic-ui-dimmer-2.2.2.tgz#667185161c31d046a51a6d71097d2909829e1423"
|
||||
integrity sha512-wK7da/70UJ9AU7Ju2MeOO9sjRPrhU6jf+VvHiTwlaCGm7+ALiJThd88D1iB6ODDQpm+ebjIbQkvAmDSkMpmKlg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-dropdown@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-dropdown/-/semantic-ui-dropdown-2.2.3.tgz#fffafea2a26dcb85fefc74a23d6a764f21a75b61"
|
||||
integrity sha512-y2ZIiEWvFFyLu7+yqNV550U9hs3sfqP7ajLxHEWlGjxGS4NsJmy9In7/UcxnpJB+JkanC4JkyogEN6wRlbqvhw==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
"@types/semantic-ui-api" "*"
|
||||
|
||||
"@types/semantic-ui-embed@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-embed/-/semantic-ui-embed-2.2.2.tgz#5b851bc966c06812550410348da7f8ea866e0c02"
|
||||
integrity sha512-5sW99BtK2SIBs9/sSM/0vMr6tphPyPXHyClhFX1tJi0L5ZH0wEmf6XcBRZgROe3ueHYVaJ0Pt/zwPQ5SMW0xDg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-form@*":
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-form/-/semantic-ui-form-2.2.6.tgz#767c364c92fb3977f221b8c72e094e0928d4a024"
|
||||
integrity sha512-khj3o6w2TWN9Bh7yZhsosUfAPMBRP/rD3DiApZrPUaioCHf0VrDtYuiqJXTd/Qt7cZW2w+NgZ1riV/3leXx8iA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-modal@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-modal/-/semantic-ui-modal-2.2.3.tgz#482e8ca3323b22e7247ff92249a52a8bc2fccc6d"
|
||||
integrity sha512-Th48BFk1pd4kluFjCUDKn7Aml3xoLdFFK8wFQRz6UsOMdvsXx2OrNkubhjqc79tcBzONC7NszSW2ImslPPHNCg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
"@types/semantic-ui-dimmer" "*"
|
||||
|
||||
"@types/semantic-ui-nag@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-nag/-/semantic-ui-nag-2.2.2.tgz#93e9cc410aa17b273ef73a705666815a7c2a09a6"
|
||||
integrity sha512-gqjSFmMLw8vtPa6/Rv/mFBK1mdqaUbLkhUA4CsTDhkibUqnNqpvI/d1XFFLdC/ULu9v7UloMTCndSGKao+q5oA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-popup@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-popup/-/semantic-ui-popup-2.2.3.tgz#cdf391d0e16ffbfdc34c7fee85e85d20a9323c5a"
|
||||
integrity sha512-tw7FXUTAs+GEU939RBpOCVq9H8vYpsr8uYvJC0RUxXCYXCUHsgYzgIIklKoD+xPvUCf34MHnMBwrrTCMJosxGg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-progress@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-progress/-/semantic-ui-progress-2.2.3.tgz#2ad3e2b69c5c5927aa4c11634cdf0a04ab53b36f"
|
||||
integrity sha512-gv0i4+/uVbUJnuTzNv2oEqJ8CMQPeAR6K+s2zm1r4waH+8ZQ0SKM1DZ4t3w4gEMxhXqZVlLlyIhfY1SmUI0GuQ==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-rating@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-rating/-/semantic-ui-rating-2.2.2.tgz#d35807b61ebc6b4f2a977b430fbd862fa5f5b5a7"
|
||||
integrity sha512-9497T8bEnkadWtQDl5Hno9lviZ2bJjx5rKd/Gfq6PWZ1/4/71LrYdH1DSr+sHYJ5HkaSA0b7GFVCTxi9pEdd6Q==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-search@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-search/-/semantic-ui-search-2.2.3.tgz#7da1a62ed116a1aa0baee2a9c66196b16f643e13"
|
||||
integrity sha512-JVrrW9uakXTudNm1MGrkRpirL2vm8NCVtrPyH6zIbBNqi08UPeHY8yxjnFpTPv5sMKBGGhkSn9cYrGz6Cweg2Q==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
"@types/semantic-ui-api" "*"
|
||||
|
||||
"@types/semantic-ui-shape@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-shape/-/semantic-ui-shape-2.2.2.tgz#ce3bd95e4ec1127380343910c4766a4f070c1336"
|
||||
integrity sha512-bXaeheuuDY3rAmA5QQRAA0fzMiEkhRgZts5i7w/d1XlMHCVNeHIIAbhTurl3bPwTlbr0NI7T3ZmxH0EKRVdIEg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-sidebar@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-sidebar/-/semantic-ui-sidebar-2.2.2.tgz#792464d6e381354c8c1e935635c9010b1366f4a9"
|
||||
integrity sha512-fm/whmNiyTzQwduc4maV9XjdwVc4pVlkhX5vippW9ukCCkVGY8qBgQKHFYhAHPhe7sCsGIuS+Vpr83t8X7Fg8w==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-site@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-site/-/semantic-ui-site-2.2.2.tgz#25a358ca572aa0e811f2f912ab6872ef897c8bbe"
|
||||
integrity sha512-XxwUxqpBLAlPpO7OqAYIdBRsZTmKLXvSzBLczms3JshnoChEZbxtKRYxSxgK93Y4XYCfKnpXQXEF6RIw5FF/mA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-sticky@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-sticky/-/semantic-ui-sticky-2.2.3.tgz#1317d2eac9b42d8088f8f0f3228684efa0ce6a84"
|
||||
integrity sha512-HOhd+W75u9Hk0owQXUGdDKpvVhKl/207hueZqTTREZPTmxALAHbl6bHKxnvcJqRerhOFdObQcCFZGL5DEXRtcA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-tab@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-tab/-/semantic-ui-tab-2.2.2.tgz#4bae0c8ac1e970cd93fc31cf99f546e29ac9a069"
|
||||
integrity sha512-o7a2TJAxjh7pVqRzpQmJd7hTcaDv/tAYJh2Aez5mYiRrFylhzwIrJAcXhSwVRVInPZkc8MDlBPg8Xm+QJ98rLw==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
"@types/semantic-ui-api" "*"
|
||||
|
||||
"@types/semantic-ui-transition@*":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-transition/-/semantic-ui-transition-2.2.2.tgz#e03d9eec32c962a5c862112f75a53879c3628406"
|
||||
integrity sha512-wZJICf3qCr+68zPvzTKC9nQJ3mneW+K/K9Y2KphxujWgMCkOQEetDNb5Dbt9YZe92L0SnaPaDgp1KVaKAortdw==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui-visibility@*":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui-visibility/-/semantic-ui-visibility-2.2.3.tgz#254a126b502159e194fcbc9b8561e14c4ba215a1"
|
||||
integrity sha512-4vfXjZHJhif8Rw4WQ691Zx2Y0vqdNF2D0AYT7ltQH1/mL/fCqEwTLndl3qvgCbxpniGbTnYRajqx1dk8+Ji/HQ==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/semantic-ui@^2.2.7":
|
||||
version "2.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/semantic-ui/-/semantic-ui-2.2.7.tgz#4ae4242004aac11a21133d4f338e868b92605270"
|
||||
integrity sha512-Uj6rby2GnuVyO7pj8vgUFsv5eaxb0ktpfasYcB/vXnSAeJ4cRjIOvxka+EoPjw3tPCY4/WlxRss8hsh7kRWzQg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
"@types/semantic-ui-accordion" "*"
|
||||
"@types/semantic-ui-api" "*"
|
||||
"@types/semantic-ui-checkbox" "*"
|
||||
"@types/semantic-ui-dimmer" "*"
|
||||
"@types/semantic-ui-dropdown" "*"
|
||||
"@types/semantic-ui-embed" "*"
|
||||
"@types/semantic-ui-form" "*"
|
||||
"@types/semantic-ui-modal" "*"
|
||||
"@types/semantic-ui-nag" "*"
|
||||
"@types/semantic-ui-popup" "*"
|
||||
"@types/semantic-ui-progress" "*"
|
||||
"@types/semantic-ui-rating" "*"
|
||||
"@types/semantic-ui-search" "*"
|
||||
"@types/semantic-ui-shape" "*"
|
||||
"@types/semantic-ui-sidebar" "*"
|
||||
"@types/semantic-ui-site" "*"
|
||||
"@types/semantic-ui-sticky" "*"
|
||||
"@types/semantic-ui-tab" "*"
|
||||
"@types/semantic-ui-transition" "*"
|
||||
"@types/semantic-ui-visibility" "*"
|
||||
|
||||
"@types/showdown@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/showdown/-/showdown-2.0.0.tgz#3e800eca8573848cac4e5555f4377ba3a0e7b1f2"
|
||||
|
|
Loading…
Reference in New Issue