Coding | January 2024Read in Outline
<template>
<Stack position="relative" py="1">
<HStack
ref="scroller"
py="4"
px="5"
overflow="scroll"
gap="3"
scrollSnapType="x mandatory"
alignItems="flex-start"
@scrollend="handleScroll"
>
<TicketBookInfo
v-for="(book, i) in data.flatMap(({ books, ...rest }) =>
books.map((b) => ({ ...b, bookGroup: rest }))
)"
:key="book.id"
:book="book"
:divisionLabel="book.bookGroup.divisions_label"
:bookGroupId="book.bookGroup.id"
:data-index="i"
/>
</HStack>
<HStack gap="1" justifyContent="center">
<Box
v-for="i in data.flatMap((b) => b.books).length"
:key="i"
rounded="full"
:bgColor="{
base: 'fg.subtle',
_selected: 'accent.default'
}"
:data-selected="i - 1 === index ? true : undefined"
w="3"
h="3"
@click="handleSelect(i - 1)"
/>
</HStack>
</Stack>
</template>
<script lang="ts" setup>
import type Stack from '../core/layout/Stack'
import type { BookGroup } from '~/types/maas/book-groups'
const scroller = ref<ComponentPublicInstance<typeof Stack> | null>(null)
const index = ref(0)
defineProps<{ data: BookGroup[] }>()
let lastScrollPos = -1
const handleSelect = (index: number) => {
const container = scroller.value?.$el as HTMLDivElement
if (!container) {
return
}
const element = Array.from(container.children).find(
(child) => child.getAttribute('data-index') === index.toString()
)
element?.scrollIntoView({
block: 'center',
behavior: 'smooth'
})
}
const handleScroll = (e: UIEvent) => {
const target = e?.target as HTMLDivElement
const container = scroller.value?.$el as HTMLDivElement
if (
!e ||
!target ||
!container ||
target.scrollLeft === lastScrollPos
) {
return undefined
}
lastScrollPos = target.scrollLeft
const viewportWidth = window.innerWidth
container.style.gap = '100vw'
container.getBoundingClientRect()
const value = Array.from(container.children)
.find((child) => {
const { left } = child.getBoundingClientRect()
return left > 0 && left < viewportWidth
})
?.attributes.getNamedItem('data-index')?.value
container.style.gap = ''
if (!value === undefined) {
return
}
index.value = Number(value)
}
</script>
© 2023-2024 HamP, Assets used in the site belongs to respective owner | View Source