feat: editorial article list
This commit is contained in:
1 parent
a0b3c58c52
commit
a671b81f80
103 files changed
+6493
-1805
No files matched your search
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
import { useScrollEdges } from '@/composables/useScrollEdges'
|
||||
import type { ListTab, ListTabKey } from '@/lib/storyList'
|
||||
import { UNDERLINE_TAB_TRIGGER } from '@/lib/tabStyles'
|
||||
|
||||
const SCROLL_STEP_PX = 96
|
||||
const EMPTY_COUNT = '–'
|
||||
|
||||
const props = defineProps<{
|
||||
tabs: ListTab[]
|
||||
active: ListTabKey
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{ select: [key: ListTabKey] }>()
|
||||
|
||||
const scroller = ref<HTMLElement | null>(null)
|
||||
const { hiddenBefore, hiddenAfter } = useScrollEdges(scroller)
|
||||
|
||||
const model = computed({
|
||||
get: () => props.active,
|
||||
set: (value: ListTabKey) => emit('select', value),
|
||||
})
|
||||
|
||||
const items = computed(() =>
|
||||
props.tabs.map((tab) => ({
|
||||
label: tab.label,
|
||||
value: tab.key,
|
||||
count: tab.count === null ? EMPTY_COUNT : formatNumber(tab.count),
|
||||
})),
|
||||
)
|
||||
|
||||
function scroll(direction: -1 | 1) {
|
||||
scroller.value?.scrollBy({ left: direction * SCROLL_STEP_PX })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex items-center gap-1 border-b border-default">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
icon="i-lucide-chevron-left"
|
||||
:aria-label="t('Scroll tabs left')"
|
||||
:disabled="!hiddenBefore"
|
||||
class="size-11 shrink-0 justify-center md:hidden"
|
||||
@click="scroll(-1)"
|
||||
/>
|
||||
<div class="relative min-w-0 flex-1">
|
||||
<div
|
||||
ref="scroller"
|
||||
class="overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
<UTabs
|
||||
v-model="model"
|
||||
:items="items"
|
||||
variant="link"
|
||||
color="neutral"
|
||||
:content="false"
|
||||
:aria-label="t('Article views')"
|
||||
class="w-max"
|
||||
:ui="{
|
||||
list: 'gap-4 border-b-0 p-0 md:gap-6',
|
||||
trigger: `${UNDERLINE_TAB_TRIGGER} shrink-0 whitespace-nowrap md:h-9.5 max-md:h-11 max-md:text-base`,
|
||||
}"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<span class="truncate">{{ item.label }}</span
|
||||
>{{ ' '
|
||||
}}<span class="font-normal text-muted">{{
|
||||
(item as (typeof items)[number]).count
|
||||
}}</span>
|
||||
</template>
|
||||
</UTabs>
|
||||
</div>
|
||||
<div
|
||||
v-if="hiddenAfter"
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute inset-y-0 right-0 w-7 bg-linear-to-r from-transparent to-(--ui-bg) md:hidden"
|
||||
/>
|
||||
<div
|
||||
v-if="hiddenBefore"
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute inset-y-0 left-0 w-7 bg-linear-to-l from-transparent to-(--ui-bg) md:hidden"
|
||||
/>
|
||||
</div>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
icon="i-lucide-chevron-right"
|
||||
:aria-label="t('Scroll tabs right')"
|
||||
:disabled="!hiddenAfter"
|
||||
class="size-11 shrink-0 justify-center md:hidden"
|
||||
@click="scroll(1)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user