Coding | January 2024Read in Outline
<template>
<HStack
p="2"
justifyContent="center"
alignItems="center"
position="relative"
bgColor="white"
:css="{
'--height': '25px',
_before: {
content: `' '`,
position: 'absolute',
top: 0,
width: '100%',
height: 'calc(50% - 3.5ex)',
pointerEvents: 'none',
background:
'linear-gradient(rgba(255, 255, 255, 1),65%,rgba(255,255,255,0))'
},
_after: {
content: `' '`,
position: 'absolute',
bottom: 0,
width: '100%',
height: 'calc(50% - 3.5ex)',
pointerEvents: 'none',
background:
'linear-gradient(rgba(255,255,255,0),35%,rgba(255,255,255,1))'
}
}"
>
<Stack height="calc(5 * var(--height))" justifyContent="center">
<Stack
ref="hourScroll"
gap="0"
overflowY="auto"
scrollSnapType="y mandatory"
py="calc(2 * var(--height))"
alignItems="center"
overscrollBehavior="contain"
scrollbar="hidden"
@scrollend="handleHourScroll"
>
<Divider />
<template v-for="i in 24" :key="i">
<Text
scrollSnapAlign="center"
py="3"
px="8"
@click="setHours(i - 1)"
>
{{ i - 1 }}
</Text>
<Divider />
</template>
</Stack>
</Stack>
<Text> : </Text>
<Stack height="calc(5 * var(--height))" justifyContent="center">
<Stack
ref="minuteScroll"
gap="0"
overflowY="auto"
scrollSnapAlign="center"
scrollSnapType="y mandatory"
py="calc(2 * var(--height))"
alignItems="center"
overscrollBehavior="contain"
scrollbar="hidden"
@scrollend="handleMinuteScroll"
>
<template v-for="i in 60" :key="i">
<Text
scrollSnapAlign="center"
py="3"
px="8"
@click="setMinutes(i - 1)"
>
{{ (i - 1).toString().padStart(2, '0') }}
</Text>
<Divider />
</template>
</Stack>
</Stack>
</HStack>
</template>
<script lang="ts" setup>
import debounce from 'lodash/debounce'
import type Stack from '../layout/Stack'
const hourScroll = ref<ComponentPublicInstance<typeof Stack> | null>(null)
const minuteScroll = ref<ComponentPublicInstance<typeof Stack> | null>(null)
const time = ref('')
const model = defineModel<string>()
const emit = defineEmits(['change'])
let lastHourScrollPos = -1
let lastMinuteScrollPos = -1
let isScrolling = false
const setNotScrolling = debounce(() => {
isScrolling = false
}, 200)
const updateTimer = (hour: string, minutes: string) => {
const hourContainer = hourScroll.value?.$el as HTMLDivElement
const minuteContainer = minuteScroll.value?.$el as HTMLDivElement
if (!hourContainer || !minuteContainer || isScrolling) {
return
}
console.log('setting scroller', hour, minutes)
const hourElement = Array.from(hourContainer.children).find(
(child) => child.textContent === hour
)
const minuteElement = Array.from(minuteContainer.children).find(
(child) => child.textContent === minutes
)
hourElement?.scrollIntoView({
block: 'center'
})
minuteElement?.scrollIntoView({
block: 'center'
})
}
const determineSnapped = (
container: HTMLElement,
e: Event,
lastScrollPos: number
) => {
isScrolling = true
setNotScrolling()
if (!container) {
return
}
const target = e?.target as HTMLDivElement
if (!e || !target || target.scrollTop === lastScrollPos) {
return undefined
}
const viewportHeight = window.innerHeight
container.style.gap = '100vh'
container.getBoundingClientRect()
const value = Array.from(container.children).find((child) => {
const { top } = child.getBoundingClientRect()
return top > 0 && top < viewportHeight
})?.textContent
container.style.gap = ''
return {
value,
scrollPos: target.scrollTop
}
}
const handleHourScroll = (e: UIEvent) => {
const res = determineSnapped(
hourScroll.value?.$el,
e,
lastHourScrollPos
)
if (!res) return
const { value, scrollPos } = res
lastHourScrollPos = scrollPos
if (!value) return
setValue({ hours: value })
}
const handleMinuteScroll = (e: UIEvent) => {
const res = determineSnapped(
minuteScroll.value?.$el,
e,
lastMinuteScrollPos
)
if (!res) return
const { value, scrollPos } = res
lastMinuteScrollPos = scrollPos
if (!value) return
setValue({ minutes: value })
}
const setValue = ({
hours = model.value?.split(':')[0] ?? new Date().getHours().toString(),
minutes = model.value?.split(':')[1] ??
new Date().getMinutes().toString().padStart(2, '0')
}: {
hours?: string
minutes?: string
}) => {
model.value = `${hours}:${minutes}`
}
watchEffect(() => {
if (model.value) {
// time.value = model.value
}
updateTimer(
model.value?.split(':')[0] ?? new Date().getHours().toString(),
model.value?.split(':')[1] ??
new Date().getMinutes().toString().padStart(2, '0')
)
})
onUpdated(() => {
if (lastMinuteScrollPos >= 0)
minuteScroll.value?.$el.scrollTo({ top: lastMinuteScrollPos })
if (lastHourScrollPos >= 0)
hourScroll.value?.$el.scrollTo({ top: lastHourScrollPos })
})
const setMinutes = (minutes: number) =>
setValue({ minutes: minutes.toString() })
const setHours = (hours: number) => setValue({ hours: hours.toString() })
</script>
<style></style>
© 2023-2024 HamP, Assets used in the site belongs to respective owner | View Source