feat: image variants per image
This commit is contained in:
1 parent
f567866161
commit
951fef18ba
92 files changed
+8695
-413
No files matched your search
@@ -2,11 +2,20 @@
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import MediaVariantsFilter from '@/components/MediaVariantsFilter.vue'
|
||||
import type { MediaCounts } from '@/composables/useMedia'
|
||||
import type { MediaControls, MediaFilter, MediaSort, MediaView } from '@/lib/mediaQuery'
|
||||
import type {
|
||||
MediaControls,
|
||||
MediaFilter,
|
||||
MediaSort,
|
||||
MediaVariantsFilter as VariantsFilterValue,
|
||||
MediaView,
|
||||
} from '@/lib/mediaQuery'
|
||||
import { UNDERLINE_TAB_TRIGGER } from '@/lib/tabStyles'
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 120
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
|
||||
const SORT_LABELS: Record<MediaSort, string> = {
|
||||
get newest() {
|
||||
@@ -21,6 +30,9 @@ const SORT_LABELS: Record<MediaSort, string> = {
|
||||
get largest() {
|
||||
return t('Largest')
|
||||
},
|
||||
get total_size() {
|
||||
return t('Largest total size (original + variants)')
|
||||
},
|
||||
}
|
||||
|
||||
const VIEW_OPTIONS: { value: MediaView; icon: string; label: string }[] = [
|
||||
@@ -54,10 +66,12 @@ const emit = defineEmits<{
|
||||
'update:q': [value: string]
|
||||
'update:filter': [value: MediaFilter]
|
||||
'update:sort': [value: MediaSort]
|
||||
'update:variants': [value: VariantsFilterValue]
|
||||
'update:view': [value: MediaView]
|
||||
purge: []
|
||||
}>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const searchText = ref(props.controls.q)
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
@@ -128,9 +142,13 @@ const tab = computed({
|
||||
},
|
||||
})
|
||||
|
||||
function sortLabel(value: MediaSort) {
|
||||
return value === 'total_size' && !isDesktop.value ? t('Largest total size') : SORT_LABELS[value]
|
||||
}
|
||||
|
||||
const sortItems = computed(() =>
|
||||
(Object.keys(SORT_LABELS) as MediaSort[]).map((value) => ({
|
||||
label: SORT_LABELS[value],
|
||||
label: sortLabel(value),
|
||||
type: 'checkbox' as const,
|
||||
checked: props.controls.sort === value,
|
||||
onUpdateChecked: () => emit('update:sort', value),
|
||||
@@ -197,19 +215,30 @@ const sortItems = computed(() =>
|
||||
</template>
|
||||
</UInput>
|
||||
|
||||
<MediaVariantsFilter
|
||||
:model-value="controls.variants"
|
||||
class="order-3 w-full md:order-none md:mb-1.5 md:w-auto"
|
||||
@update:model-value="emit('update:variants', $event)"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="order-3 flex flex-wrap gap-x-2 gap-y-3 md:order-none md:mb-1.5 md:flex-nowrap md:gap-2"
|
||||
class="order-4 flex flex-wrap gap-x-2 gap-y-3 md:order-none md:mb-1.5 md:flex-nowrap md:gap-2"
|
||||
>
|
||||
<UDropdownMenu :items="sortItems" :content="{ align: 'start' }">
|
||||
<UDropdownMenu
|
||||
:items="sortItems"
|
||||
:content="{ align: 'start' }"
|
||||
:ui="{ content: 'md:w-85' }"
|
||||
>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
:aria-label="t('Sort')"
|
||||
class="h-11 flex-1 justify-between border border-accented bg-default px-2.5 text-sm font-normal text-highlighted ring-0 md:h-8 md:w-32 md:flex-none"
|
||||
class="h-11 flex-1 justify-between border border-accented bg-default px-2.5 text-sm font-normal text-highlighted ring-0 md:h-8 md:flex-none"
|
||||
:class="controls.sort === 'total_size' ? 'md:w-auto' : 'md:w-32'"
|
||||
:ui="{ trailingIcon: 'size-3.5 text-muted' }"
|
||||
>
|
||||
{{ SORT_LABELS[controls.sort] }}
|
||||
{{ sortLabel(controls.sort) }}
|
||||
</UButton>
|
||||
</UDropdownMenu>
|
||||
|
||||
|
||||
Reference in new issue
Block a user