feat(Home): implement grid layout switcher

This commit is contained in:
Hakadao
2024-03-10 19:34:35 -03:00
parent 2b25373161
commit fede8758ff
9 changed files with 201 additions and 40 deletions

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import type { GridLayoutIcon } from './types'
import { HomeSubPage } from './types'
import emitter from '~/utils/mitt'
import { settings } from '~/logic'
import { homePageGridLayout, settings } from '~/logic'
import { delay } from '~/utils/main'
import type { HomeTab } from '~/stores/mainStore'
import { useMainStore } from '~/stores/mainStore'
@@ -21,9 +22,16 @@ const pages = {
const showSearchPageMode = ref<boolean>(false)
const shouldMoveTabsUp = ref<boolean>(false)
const tabContentLoading = ref<boolean>(false)
const currentTabs = ref<HomeTab[]>([])
const gridLayoutIcons = computed((): GridLayoutIcon[] => {
return [
{ icon: 'f7:square-grid-3x2', iconActivated: 'f7:square-grid-3x2-fill', value: 'adaptive' },
{ icon: 'f7:rectangle-grid-2x2', iconActivated: 'f7:rectangle-grid-2x2-fill', value: 'twoColumns' },
{ icon: 'f7:rectangle-grid-1x2', iconActivated: 'f7:rectangle-grid-1x2-fill', value: 'oneColumn' },
]
})
watch(() => activatedPage.value, () => {
handleBackToTop(settings.value.useSearchPageModeOnHomePage ? 510 : 0)
})
@@ -171,8 +179,8 @@ function toggleTabContentLoading(loading: boolean) {
</Transition>
<header
pos="sticky top-80px" w-fit z-9 mb-9 duration-300
ease-in-out
pos="sticky top-80px" w-full z-9 mb-9 duration-300
ease-in-out flex="~ justify-between items-center gap-4"
:class="{ hide: shouldMoveTabsUp }"
>
<ul flex="~ items-center gap-3 wrap">
@@ -196,12 +204,31 @@ function toggleTabContentLoading(loading: boolean) {
/>
</li>
</ul>
<div
style="backdrop-filter: var(--bew-filter-glass-1)"
flex="~ gap-1" p-1 h-35px bg="$bew-elevated-1"
rounded="$bew-radius" shadow="$bew-shadow-1" box-border border="1 $bew-border-color"
>
<Icon
v-for="icon in gridLayoutIcons" :key="icon.value"
:icon="homePageGridLayout === icon.value ? icon.iconActivated : icon.icon"
:style="{
backgroundColor: homePageGridLayout === icon.value ? 'var(--bew-theme-color-auto)' : '',
color: homePageGridLayout === icon.value ? 'var(--bew-text-auto)' : 'unset',
}"
w-full
h-full p="x-2 y-1" rounded="$bew-radius-half" bg="hover:$bew-fill-2" duration-300
cursor-pointer @click="homePageGridLayout = icon.value"
/>
</div>
</header>
<Transition name="page-fade">
<KeepAlive include="ForYou">
<Component
:is="pages[activatedPage]" :key="activatedPage"
:grid-layout="homePageGridLayout"
@before-loading="toggleTabContentLoading(true)"
@after-loading="toggleTabContentLoading(false)"
/>
@@ -242,7 +269,7 @@ function toggleTabContentLoading(loading: boolean) {
}
.tab-activated {
--at-apply: bg-$bew-theme-color dark:bg-white color-white dark:color-black
--at-apply: bg-$bew-theme-color-auto text-$bew-text-auto
border-$bew-theme-color dark:border-white;
}
</style>

View File

@@ -1,12 +1,25 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import type { GridLayout } from '~/logic'
import type { DataItem as MomentItem, MomentResult } from '~/models/moment/moment'
const props = defineProps<{
gridLayout: GridLayout
}>()
const emit = defineEmits<{
(e: 'beforeLoading'): void
(e: 'afterLoading'): void
}>()
const gridValue = computed((): string => {
if (props.gridLayout === 'adaptive')
return '~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5'
if (props.gridLayout === 'twoColumns')
return '~ cols-1 xl:cols-2 gap-4'
return '~ cols-1 gap-4'
})
const videoList = reactive<MomentItem[]>([])
const isLoading = ref<boolean>(false)
const needToLoginFirst = ref<boolean>(false)
@@ -111,6 +124,11 @@ function jumpToLoginPage() {
<template>
<div>
<!-- By directly using predefined unocss grid properties, it is possible to dynamically set the grid attribute -->
<div hidden grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5" />
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<Empty v-if="needToLoginFirst" mt-6 :description="$t('common.please_log_in_first')">
<Button type="primary" @click="jumpToLoginPage()">
{{ $t('common.login') }}
@@ -120,7 +138,7 @@ function jumpToLoginPage() {
v-else
ref="containerRef"
m="b-0 t-0" relative w-full h-full
grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5"
:grid="gridValue"
>
<VideoCard
v-for="video in videoList"
@@ -137,11 +155,15 @@ function jumpToLoginPage() {
:capsule-text="video.modules.module_author.pub_time"
:bvid="video.modules.module_dynamic.major.archive?.bvid"
show-preview
:horizontal="gridLayout !== 'adaptive'"
/>
<!-- skeleton -->
<template v-if="isLoading">
<VideoCardSkeleton v-for="item in 30" :key="item" />
<VideoCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
</div>

View File

@@ -2,15 +2,28 @@
import type { Ref } from 'vue'
import type { AppForYouResult, Item as AppVideoItem } from '~/models/video/appForYou'
import type { Item as VideoItem, forYouResult } from '~/models/video/forYou'
import type { GridLayout } from '~/logic'
import { accessKey, settings } from '~/logic'
import { LanguageType } from '~/enums/appEnums'
import { TVAppKey } from '~/utils/authProvider'
const props = defineProps<{
gridLayout: GridLayout
}>()
const emit = defineEmits<{
(e: 'beforeLoading'): void
(e: 'afterLoading'): void
}>()
const gridValue = computed((): string => {
if (props.gridLayout === 'adaptive')
return '~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5'
if (props.gridLayout === 'twoColumns')
return '~ cols-1 xl:cols-2 gap-4'
return '~ cols-1 gap-4'
})
const videoList = reactive<VideoItem[]>([])
const appVideoList = reactive<AppVideoItem[]>([])
const isLoading = ref<boolean>(true)
@@ -174,16 +187,22 @@ function jumpToLoginPage() {
<template>
<div>
<!-- By directly using predefined unocss grid properties, it is possible to dynamically set the grid attribute -->
<div hidden grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5" />
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<Empty v-if="needToLoginFirst" mt-6 :description="$t('common.please_log_in_first')">
<Button type="primary" @click="jumpToLoginPage()">
{{ $t('common.login') }}
</Button>
</Empty>
<div
v-else
ref="containerRef"
m="b-0 t-0" relative w-full h-full
grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5"
:grid="gridValue"
>
<template v-if="settings.recommendationMode === 'web'">
<VideoCard
@@ -203,6 +222,7 @@ function jumpToLoginPage() {
:cid="video.cid"
:uri="video.uri"
show-preview
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
<template v-else>
@@ -223,12 +243,16 @@ function jumpToLoginPage() {
:cid="video?.player_args?.cid"
:uri="video.uri"
show-preview
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
<!-- skeleton -->
<template v-if="isLoading">
<VideoCardSkeleton v-for="item in 30" :key="item" />
<VideoCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
</div>

View File

@@ -3,9 +3,14 @@ import { useI18n } from 'vue-i18n'
import type { RankingType } from '../types'
import type { RankingResult, List as RankingVideoItem } from '~/models/video/ranking'
import type { List as RankingPgcItem, RankingPgcResult } from '~/models/video/rankingPgc'
import type { GridLayout } from '~/logic'
import { settings } from '~/logic'
import emitter from '~/utils/mitt'
const props = defineProps<{
gridLayout: GridLayout
}>()
const emit = defineEmits<{
(e: 'beforeLoading'): void
(e: 'afterLoading'): void
@@ -14,6 +19,20 @@ const emit = defineEmits<{
const { t } = useI18n()
const { handleBackToTop, handlePageRefresh } = useBewlyApp()
const gridValue = computed((): string => {
if (props.gridLayout === 'adaptive') {
// eslint-disable-next-line ts/no-use-before-define
if (!activatedRankingType.value.seasonType)
return '~ 2xl:cols-4 xl:cols-3 lg:cols-2 md:cols-1 gap-5'
else
return '~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5'
}
if (props.gridLayout === 'twoColumns')
return '~ cols-1 xl:cols-2 gap-4'
return '~ cols-1 gap-4'
})
const rankingTypes = computed((): RankingType[] => {
return [
{ id: 1, name: t('ranking.all'), rid: 0 },
@@ -140,10 +159,10 @@ function getRankingPgc() {
<ul flex="~ col gap-2">
<li v-for="rankingType in rankingTypes" :key="rankingType.id">
<a
:class="{ active: activatedRankingType.id === rankingType.id }"
px-4 lh-30px h-30px hover:bg="$bew-fill-2" w-inherit
block rounded="$bew-radius" cursor-pointer transition="all 300 ease-out"
hover:scale-105 un-text="$bew-text-2 hover:$bew-text-1"
:class="{ active: activatedRankingType.id === rankingType.id }"
hover:scale-105 un-text="$bew-text-1"
@click="activatedRankingType = rankingType"
>{{ rankingType.name }}</a>
</li>
@@ -151,7 +170,13 @@ function getRankingPgc() {
</OverlayScrollbarsComponent>
</aside>
<main w-full>
<!-- By directly using predefined unocss grid properties, it is possible to dynamically set the grid attribute -->
<div hidden grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5" />
<div hidden grid="~ 2xl:cols-4 xl:cols-3 lg:cols-2 md:cols-1 gap-5" />
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<main w-full :grid="gridValue">
<template v-if="!('seasonType' in activatedRankingType)">
<VideoCard
v-for="(video, index) in videoList"
@@ -171,38 +196,41 @@ function getRankingPgc() {
:rank="index + 1"
:cid="video.cid"
show-preview
horizontal
:horizontal="gridLayout !== 'adaptive'"
w-full
/>
</template>
<template v-else>
<div grid="~ cols-2 gap-4">
<LongCoverCard
v-for="pgc in PgcList"
:key="pgc.url"
:url="pgc.url"
:cover="pgc.cover"
:title="pgc.title"
:desc="pgc.new_ep.index_show"
:view="pgc.stat.view"
:follow="pgc.stat.follow"
:rank="pgc.rank"
:capsule-text="pgc.rating.replace('', '')"
horizontal
mb-8
/>
</div>
<LongCoverCard
v-for="pgc in PgcList"
:key="pgc.url"
:url="pgc.url"
:cover="pgc.cover"
:title="pgc.title"
:desc="pgc.new_ep.index_show"
:view="pgc.stat.view"
:follow="pgc.stat.follow"
:rank="pgc.rank"
:capsule-text="pgc.rating.replace('', '')"
:horizontal="gridLayout !== 'adaptive'"
mb-8
/>
</template>
<!-- skeleton -->
<template v-if="isLoading">
<template v-if="!('seasonType' in activatedRankingType)">
<VideoCardSkeleton v-for="item in 30" :key="item" horizontal />
<VideoCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
<template v-else>
<div grid="~ cols-2 gap-4">
<LongCoverCardSkeleton v-for="item in 30" :key="item" horizontal />
</div>
<LongCoverCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
</template>
</main>
@@ -211,7 +239,7 @@ function getRankingPgc() {
<style lang="scss" scoped>
.active {
--at-apply: scale-110 bg-$bew-theme-color dark:bg-white text-white dark:text-black shadow-$bew-shadow-2;
--at-apply: scale-110 bg-$bew-theme-color-auto text-$bew-text-auto shadow-$bew-shadow-2;
}
.hide {

View File

@@ -1,12 +1,25 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import type { GridLayout } from '~/logic'
import type { DataItem as MomentItem, MomentResult } from '~/models/moment/moment'
const props = defineProps<{
gridLayout: GridLayout
}>()
const emit = defineEmits<{
(e: 'beforeLoading'): void
(e: 'afterLoading'): void
}>()
const gridValue = computed((): string => {
if (props.gridLayout === 'adaptive')
return '~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5'
if (props.gridLayout === 'twoColumns')
return '~ cols-1 xl:cols-2 gap-4'
return '~ cols-1 gap-4'
})
const momentList = reactive<MomentItem[]>([])
const isLoading = ref<boolean>(false)
const needToLoginFirst = ref<boolean>(false)
@@ -116,6 +129,11 @@ function jumpToLoginPage() {
<template>
<div>
<!-- By directly using predefined unocss grid properties, it is possible to dynamically set the grid attribute -->
<div hidden grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5" />
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<Empty v-if="needToLoginFirst" mt-6 :description="$t('common.please_log_in_first')">
<Button type="primary" @click="jumpToLoginPage()">
{{ $t('common.login') }}
@@ -125,7 +143,7 @@ function jumpToLoginPage() {
v-else
ref="containerRef"
m="b-0 t-0" relative w-full h-full
grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5"
:grid="gridValue"
>
<VideoCard
v-for="moment in momentList"
@@ -142,11 +160,15 @@ function jumpToLoginPage() {
:danmaku-str="moment.modules.module_dynamic.major.pgc?.stat.danmaku"
:capsule-text="moment.modules.module_author.pub_time"
:epid="moment.modules.module_dynamic.major.pgc?.epid"
:horizontal="gridLayout !== 'adaptive'"
/>
<!-- skeleton -->
<template v-if="isLoading">
<VideoCardSkeleton v-for="item in 30" :key="item" />
<VideoCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
</div>

View File

@@ -1,12 +1,25 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import type { GridLayout } from '~/logic'
import type { TrendingResult, List as VideoItem } from '~/models/video/trending'
const props = defineProps<{
gridLayout: GridLayout
}>()
const emit = defineEmits<{
(e: 'beforeLoading'): void
(e: 'afterLoading'): void
}>()
const gridValue = computed((): string => {
if (props.gridLayout === 'adaptive')
return '~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5'
if (props.gridLayout === 'twoColumns')
return '~ cols-1 xl:cols-2 gap-4'
return '~ cols-1 gap-4'
})
const videoList = reactive<VideoItem[]>([])
const isLoading = ref<boolean>(false)
const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
@@ -72,10 +85,15 @@ async function getTrendingVideos() {
<template>
<div>
<!-- By directly using predefined unocss grid properties, it is possible to dynamically set the grid attribute -->
<div hidden grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5" />
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<div
ref="containerRef"
m="b-0 t-0" relative w-full h-full
grid="~ cols-1 xl:cols-2 gap-4"
:grid="gridValue"
>
<VideoCard
v-for="video in videoList"
@@ -95,13 +113,15 @@ async function getTrendingVideos() {
:tag="video.rcmd_reason.content"
:cid="video.cid"
show-preview
horizontal
w-full
:horizontal="gridLayout !== 'adaptive'"
/>
<!-- skeleton -->
<template v-if="isLoading">
<VideoCardSkeleton v-for="item in 30" :key="item" horizontal />
<VideoCardSkeleton
v-for="item in 30" :key="item"
:horizontal="gridLayout !== 'adaptive'"
/>
</template>
</div>

View File

@@ -1,3 +1,5 @@
import type { GridLayout } from '~/logic'
export enum HomeSubPage {
ForYou = 'ForYou',
Following = 'Following',
@@ -13,3 +15,9 @@ export interface RankingType {
seasonType?: number
type?: string
}
export interface GridLayoutIcon {
icon: string
iconActivated: string
value: GridLayout
}