kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
fix(front): make layout more consistent across pages
rodzic
c3c76ce701
commit
81a9c64ccd
|
@ -8,7 +8,6 @@ import { useI18n } from 'vue-i18n'
|
|||
import axios from 'axios'
|
||||
import clip from 'text-clipper'
|
||||
|
||||
import Layout from '~/components/ui/Layout.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Alert from '~/components/ui/Alert.vue'
|
||||
import Spacer from '~/components/ui/Spacer.vue'
|
||||
|
@ -153,8 +152,9 @@ const submit = async () => {
|
|||
>
|
||||
{{ t('components.common.RenderedDescription.button.cancel') }}
|
||||
</Button>
|
||||
<Spacer grow />
|
||||
<Button
|
||||
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
|
||||
:is-loading
|
||||
type="submit"
|
||||
:disabled="isLoading"
|
||||
solid
|
||||
|
@ -180,19 +180,4 @@ const submit = async () => {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
/* .description {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
white-space: normal;
|
||||
&.truncated {
|
||||
-webkit-line-clamp: 1;
|
||||
line-clamp: 1;
|
||||
max-height: 72px;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
|
|
|
@ -139,160 +139,160 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
|
|||
page-heading
|
||||
/>
|
||||
<Section>
|
||||
<Layout
|
||||
form
|
||||
flex
|
||||
:class="['ui', { 'loading': isLoading }, 'form']"
|
||||
@submit.prevent="search"
|
||||
>
|
||||
<Input
|
||||
id="artist-search"
|
||||
v-model="query"
|
||||
search
|
||||
name="search"
|
||||
:label="t('components.library.Artists.label.search')"
|
||||
autofocus
|
||||
:placeholder="labels.searchPlaceholder"
|
||||
/>
|
||||
<Pills
|
||||
v-if="typeof tags === 'object'"
|
||||
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
||||
:set="model => ({
|
||||
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||
others: dataStore.tags().value
|
||||
.filter(({ name }) => result?.results?.some((object) => object.tags?.includes(name)) && !tags.includes(name))
|
||||
.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||
})"
|
||||
:label="t('components.library.Artists.label.tags')"
|
||||
style="max-width: 350px;"
|
||||
/>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-ordering"
|
||||
form
|
||||
flex
|
||||
:class="['ui', { 'loading': isLoading }, 'form']"
|
||||
@submit.prevent="search"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.ordering.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-ordering"
|
||||
v-model="ordering"
|
||||
class="dropdown"
|
||||
<Input
|
||||
id="artist-search"
|
||||
v-model="query"
|
||||
search
|
||||
name="search"
|
||||
:label="t('components.library.Artists.label.search')"
|
||||
autofocus
|
||||
:placeholder="labels.searchPlaceholder"
|
||||
/>
|
||||
<Pills
|
||||
v-if="typeof tags === 'object'"
|
||||
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
||||
:set="model => ({
|
||||
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||
others: dataStore.tags().value
|
||||
.filter(({ name }) => result?.results?.some((object) => object.tags?.includes(name)) && !tags.includes(name))
|
||||
.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||
})"
|
||||
:label="t('components.library.Artists.label.tags')"
|
||||
style="max-width: 350px;"
|
||||
/>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-ordering"
|
||||
>
|
||||
<option
|
||||
v-for="(option, key) in orderingOptions"
|
||||
:key="key"
|
||||
:value="option[0]"
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.ordering.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-ordering"
|
||||
v-model="ordering"
|
||||
class="dropdown"
|
||||
>
|
||||
{{ sharedLabels.filters[option[1]] }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-ordering-direction"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.ordering.direction.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-ordering-direction"
|
||||
v-model="orderingDirection"
|
||||
class="dropdown"
|
||||
<option
|
||||
v-for="(option, key) in orderingOptions"
|
||||
:key="key"
|
||||
:value="option[0]"
|
||||
>
|
||||
{{ sharedLabels.filters[option[1]] }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-ordering-direction"
|
||||
>
|
||||
<option value="+">
|
||||
{{ t('components.library.Artists.ordering.direction.ascending') }}
|
||||
</option>
|
||||
<option value="-">
|
||||
{{ t('components.library.Artists.ordering.direction.descending') }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-results"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.pagination.results') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-results"
|
||||
v-model="paginateBy"
|
||||
class="dropdown"
|
||||
>
|
||||
<option
|
||||
v-for="opt in paginateOptions"
|
||||
:key="opt"
|
||||
:value="opt"
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.ordering.direction.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-ordering-direction"
|
||||
v-model="orderingDirection"
|
||||
class="dropdown"
|
||||
>
|
||||
{{ opt }}
|
||||
</option>
|
||||
</select>
|
||||
<option value="+">
|
||||
{{ t('components.library.Artists.ordering.direction.ascending') }}
|
||||
</option>
|
||||
<option value="-">
|
||||
{{ t('components.library.Artists.ordering.direction.descending') }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="artist-results"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Artists.pagination.results') }}
|
||||
</span>
|
||||
<select
|
||||
id="artist-results"
|
||||
v-model="paginateBy"
|
||||
class="dropdown"
|
||||
>
|
||||
<option
|
||||
v-for="opt in paginateOptions"
|
||||
:key="opt"
|
||||
:value="opt"
|
||||
>
|
||||
{{ opt }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Toggle
|
||||
id="exclude-compilation"
|
||||
v-model="excludeCompilation"
|
||||
:label="t('components.library.Artists.label.excludeCompilation')"
|
||||
true-value="true"
|
||||
false-value="null"
|
||||
type="checkbox"
|
||||
/>
|
||||
</Layout>
|
||||
<Toggle
|
||||
id="exclude-compilation"
|
||||
v-model="excludeCompilation"
|
||||
:label="t('components.library.Artists.label.excludeCompilation')"
|
||||
true-value="true"
|
||||
false-value="null"
|
||||
type="checkbox"
|
||||
<Loader v-if="isLoading" />
|
||||
<Spacer />
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
</Layout>
|
||||
<Loader v-if="isLoading" />
|
||||
<Spacer />
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
<Layout
|
||||
v-if="result && result.results.length > 0"
|
||||
grid
|
||||
style="display:flex; flex-wrap:wrap; gap: 32px; margin-top:32px;"
|
||||
>
|
||||
<ArtistCard
|
||||
v-for="artist in result.results"
|
||||
:key="artist.id"
|
||||
:artist="artist"
|
||||
/>
|
||||
</Layout>
|
||||
<Layout
|
||||
v-else-if="result && result.results.length === 0"
|
||||
stack
|
||||
>
|
||||
<Alert yellow>
|
||||
<i class="compact disc icon" />
|
||||
{{ t('components.library.Artists.empty.noResults') }}
|
||||
</Alert>
|
||||
<Card
|
||||
v-if="store.state.auth.authenticated"
|
||||
:title="t('components.library.Artists.button.upload')"
|
||||
solid
|
||||
small
|
||||
primary
|
||||
style="text-align: center;"
|
||||
:to="useModal('upload').to"
|
||||
<Layout
|
||||
v-if="result && result.results.length > 0"
|
||||
grid
|
||||
style="display:flex; flex-wrap:wrap; gap: 32px; margin-top:32px;"
|
||||
>
|
||||
<template #image>
|
||||
<i
|
||||
class="bi bi-upload"
|
||||
style="font-size: 100px; position: relative; top: 50px;"
|
||||
/>
|
||||
</template>
|
||||
</Card>
|
||||
</Layout>
|
||||
<Spacer grow />
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
</Section>
|
||||
<ArtistCard
|
||||
v-for="artist in result.results"
|
||||
:key="artist.id"
|
||||
:artist="artist"
|
||||
/>
|
||||
</Layout>
|
||||
<Layout
|
||||
v-else-if="result && result.results.length === 0"
|
||||
stack
|
||||
>
|
||||
<Alert yellow>
|
||||
<i class="compact disc icon" />
|
||||
{{ t('components.library.Artists.empty.noResults') }}
|
||||
</Alert>
|
||||
<Card
|
||||
v-if="store.state.auth.authenticated"
|
||||
:title="t('components.library.Artists.button.upload')"
|
||||
solid
|
||||
small
|
||||
primary
|
||||
style="text-align: center;"
|
||||
:to="useModal('upload').to"
|
||||
>
|
||||
<template #image>
|
||||
<i
|
||||
class="bi bi-upload"
|
||||
style="font-size: 100px; position: relative; top: 50px;"
|
||||
/>
|
||||
</template>
|
||||
</Card>
|
||||
</Layout>
|
||||
<Spacer grow />
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
</Section>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import Header from '~/components/ui/Header.vue'
|
|||
import Section from '~/components/ui/Section.vue'
|
||||
import Pagination from '~/components/ui/Pagination.vue'
|
||||
import RadioCard from '~/components/radios/Card.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Alert from '~/components/ui/Alert.vue'
|
||||
import Input from '~/components/ui/Input.vue'
|
||||
import Link from '~/components/ui/Link.vue'
|
||||
|
@ -118,7 +117,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
|
|||
<Layout
|
||||
main
|
||||
stack
|
||||
gap-64
|
||||
gap-84
|
||||
>
|
||||
<Header
|
||||
page-heading
|
||||
|
@ -126,6 +125,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
|
|||
/>
|
||||
<Section
|
||||
align-left
|
||||
:columns-per-item="3"
|
||||
:h2="t('components.library.Radios.header.instance')"
|
||||
>
|
||||
<radio-card
|
||||
|
@ -156,143 +156,147 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
|
|||
/>
|
||||
</Section>
|
||||
<Section
|
||||
:h2="t('components.library.Radios.header.user')"
|
||||
:action="{
|
||||
to: {name: 'library.radios.build'},
|
||||
text:t('components.library.Radios.button.create'),
|
||||
icon: 'bi-plus',
|
||||
solid: true,
|
||||
primary: true,
|
||||
disabled: !store.state.auth.authenticated || undefined
|
||||
}"
|
||||
:h2="t('components.library.Radios.header.user')"
|
||||
align-left
|
||||
:columns-per-item="3"
|
||||
:action="{
|
||||
to: {name: 'library.radios.build'},
|
||||
text: t('components.library.Radios.button.create'),
|
||||
icon: 'bi-plus',
|
||||
solid: true,
|
||||
primary: true,
|
||||
disabled: !store.state.auth.authenticated || undefined
|
||||
}"
|
||||
>
|
||||
<Spacer no-size />
|
||||
<Layout
|
||||
flex
|
||||
form
|
||||
:class="['ui', {'loading': isLoading}, 'form']"
|
||||
@submit.prevent="search"
|
||||
>
|
||||
<Input
|
||||
id="radios-search"
|
||||
v-model="query"
|
||||
search
|
||||
name="search"
|
||||
:label="t('components.library.Radios.label.search')"
|
||||
:placeholder="labels.searchPlaceholder"
|
||||
/>
|
||||
<Spacer no-size />
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-ordering"
|
||||
flex
|
||||
form
|
||||
full
|
||||
:class="['ui', {'loading': isLoading}, 'form']"
|
||||
@submit.prevent="search"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.ordering.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-ordering"
|
||||
v-model="ordering"
|
||||
class="dropdown"
|
||||
<Input
|
||||
id="radios-search"
|
||||
v-model="query"
|
||||
search
|
||||
name="search"
|
||||
:label="t('components.library.Radios.label.search')"
|
||||
:placeholder="labels.searchPlaceholder"
|
||||
/>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-ordering"
|
||||
>
|
||||
<option
|
||||
v-for="(option, key) in orderingOptions"
|
||||
:key="key"
|
||||
:value="option[0]"
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.ordering.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-ordering"
|
||||
v-model="ordering"
|
||||
class="dropdown"
|
||||
>
|
||||
{{ sharedLabels.filters[option[1]] }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-ordering-direction"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.ordering.direction.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-ordering-direction"
|
||||
v-model="orderingDirection"
|
||||
class="dropdown"
|
||||
<option
|
||||
v-for="(option, key) in orderingOptions"
|
||||
:key="key"
|
||||
:value="option[0]"
|
||||
>
|
||||
{{ sharedLabels.filters[option[1]] }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-ordering-direction"
|
||||
>
|
||||
<option value="+">
|
||||
{{ t('components.library.Radios.ordering.direction.ascending') }}
|
||||
</option>
|
||||
<option value="-">
|
||||
{{ t('components.library.Radios.ordering.direction.descending') }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-results"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.pagination.results') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-results"
|
||||
v-model="paginateBy"
|
||||
class="dropdown"
|
||||
>
|
||||
<option
|
||||
v-for="opt in paginateOptions"
|
||||
:key="opt"
|
||||
:value="opt"
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.ordering.direction.label') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-ordering-direction"
|
||||
v-model="orderingDirection"
|
||||
class="dropdown"
|
||||
>
|
||||
{{ opt }}
|
||||
</option>
|
||||
</select>
|
||||
<option value="+">
|
||||
{{ t('components.library.Radios.ordering.direction.ascending') }}
|
||||
</option>
|
||||
<option value="-">
|
||||
{{ t('components.library.Radios.ordering.direction.descending') }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
<Layout
|
||||
stack
|
||||
no-gap
|
||||
label
|
||||
for="radios-results"
|
||||
>
|
||||
<span class="label">
|
||||
{{ t('components.library.Radios.pagination.results') }}
|
||||
</span>
|
||||
<select
|
||||
id="radios-results"
|
||||
v-model="paginateBy"
|
||||
class="dropdown"
|
||||
>
|
||||
<option
|
||||
v-for="opt in paginateOptions"
|
||||
:key="opt"
|
||||
:value="opt"
|
||||
>
|
||||
{{ opt }}
|
||||
</option>
|
||||
</select>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
<Alert
|
||||
v-if="result && result.results.length === 0"
|
||||
blue
|
||||
style="align-items: center;"
|
||||
>
|
||||
<i
|
||||
class="bi bi-broadcast-pin"
|
||||
style="font-size: 80px"
|
||||
/>
|
||||
<Spacer />
|
||||
{{ t('components.library.Radios.empty.noResults') }}
|
||||
<Spacer />
|
||||
<Button
|
||||
v-if="store.state.auth.authenticated"
|
||||
primary
|
||||
style="align-self:center;"
|
||||
:to="{name: 'library.radios.build'}"
|
||||
icon="bi-boombox-fill"
|
||||
<Alert
|
||||
v-if="result && result.results.length === 0"
|
||||
blue
|
||||
style="align-items: center; grid-column: 1 / -1;"
|
||||
>
|
||||
{{ t('components.library.Radios.button.add') }}
|
||||
</Button>
|
||||
</Alert>
|
||||
<Layout
|
||||
v-if="result && result.results.length > 0"
|
||||
flex
|
||||
>
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
<radio-card
|
||||
v-for="radio in result.results"
|
||||
:key="radio.id"
|
||||
type="custom"
|
||||
:custom-radio="radio"
|
||||
/>
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
</Layout>
|
||||
<i
|
||||
class="bi bi-broadcast-pin"
|
||||
style="font-size: 80px"
|
||||
/>
|
||||
<Spacer />
|
||||
{{ t('components.library.Radios.empty.noResults') }}
|
||||
<Spacer />
|
||||
<Link
|
||||
v-if="store.state.auth.authenticated"
|
||||
primary
|
||||
style="align-self:center;"
|
||||
:to="{name: 'library.radios.build'}"
|
||||
icon="bi-boombox-fill"
|
||||
>
|
||||
{{ t('components.library.Radios.button.add') }}
|
||||
</Link>
|
||||
</Alert>
|
||||
<Layout
|
||||
v-if="result && result.results.length > 0"
|
||||
full
|
||||
flex
|
||||
>
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
<radio-card
|
||||
v-for="radio in result.results"
|
||||
:key="radio.id"
|
||||
type="custom"
|
||||
:custom-radio="radio"
|
||||
/>
|
||||
<Pagination
|
||||
v-if="page && result && result.count > paginateBy"
|
||||
v-model:page="page"
|
||||
:pages="Math.ceil(result.count / paginateBy)"
|
||||
/>
|
||||
</Layout>
|
||||
</Section>
|
||||
</Layout>
|
||||
</template>
|
||||
|
|
|
@ -45,14 +45,15 @@ const isIconOnly = computed(() =>
|
|||
)
|
||||
)
|
||||
|
||||
const button = ref()
|
||||
const link = ref()
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) button.value.focus()
|
||||
if (props.autofocus) link.value.focus()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{ isExternalLink? ' ' : ' ' }}
|
||||
<component
|
||||
:is="isExternalLink ? 'a' : RouterLink"
|
||||
v-bind="color(props, ['interactive'])(
|
||||
|
@ -61,7 +62,7 @@ onMounted(() => {
|
|||
)(
|
||||
align(props, 'solid' in props ? { alignText: 'center' } : {})(
|
||||
)))"
|
||||
ref="button"
|
||||
ref="link"
|
||||
:autofocus="autofocus || undefined"
|
||||
:class="[
|
||||
$style.link,
|
||||
|
|
Ładowanie…
Reference in New Issue