use IntersectionObserver to show scrolling
Deploy app frontend / app frontend (push) Successful in 1m57s
Deploy db-migrations / db-migrations (push) Successful in 58s
Deploy service message-delivery-method-web-push / service message-delivery-method-web-push (push) Successful in 1m26s
Deploy service tg-bot / service tg-bot (push) Successful in 1m27s
Deploy app frontend / app frontend (push) Successful in 1m57s
Deploy db-migrations / db-migrations (push) Successful in 58s
Deploy service message-delivery-method-web-push / service message-delivery-method-web-push (push) Successful in 1m26s
Deploy service tg-bot / service tg-bot (push) Successful in 1m27s
This commit is contained in:
Vendored
+1
@@ -57,6 +57,7 @@ declare global {
|
|||||||
const useAttrs: typeof import('vue')['useAttrs']
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
const useCssModule: typeof import('vue')['useCssModule']
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
const useCssVars: typeof import('vue')['useCssVars']
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
const useHead: typeof import('unhead')['useHead']
|
const useHead: typeof import('unhead')['useHead']
|
||||||
const useHeadSafe: typeof import('unhead')['useHeadSafe']
|
const useHeadSafe: typeof import('unhead')['useHeadSafe']
|
||||||
const useId: typeof import('vue')['useId']
|
const useId: typeof import('vue')['useId']
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"@primevue/themes": "^4.2.1",
|
"@primevue/themes": "^4.2.1",
|
||||||
"@tanstack/vue-query": "^5.60.6",
|
"@tanstack/vue-query": "^5.60.6",
|
||||||
"@unhead/vue": "^1.11.13",
|
"@unhead/vue": "^1.11.13",
|
||||||
|
"@vueuse/core": "^12.0.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"pinia": "^2.2.6",
|
"pinia": "^2.2.6",
|
||||||
|
|||||||
@@ -4,25 +4,26 @@
|
|||||||
>
|
>
|
||||||
<!-- Shadow Top -->
|
<!-- Shadow Top -->
|
||||||
<div
|
<div
|
||||||
class="row-start-1 row-span-1 col-start-1 sm:row-start-2 relative w-full h-[1rem] bg-gradient-to-b from-black/30 sm:from-black/10 to-transparent dark:from-white/10 pointer-events-none transition-opacity duration-300"
|
class="transition-opacity opacity-0 row-start-1 row-span-1 col-start-1 sm:row-start-2 relative w-full h-[1rem] bg-gradient-to-b from-black/30 sm:from-black/10 to-transparent dark:from-white/10 pointer-events-none duration-300"
|
||||||
:class="{ 'opacity-0': !showTopShadow, 'opacity-100': showTopShadow }"
|
:class="{ 'opacity-100': !isTopOfContentVisible }"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Content Section -->
|
<!-- Content Section -->
|
||||||
<div
|
<div
|
||||||
class="row-start-1 row-span-3 col-start-1 sm:row-start-2 overflow-hidden overflow-y-auto px-4 overscroll-contain touch-manipulation"
|
class="row-start-1 row-span-3 col-start-1 sm:row-start-2 overflow-hidden overflow-y-auto px-4 overscroll-contain touch-manipulation scrollbar scrollbar-w-1.5 scrollbar-thumb-gray-500/60 scrollbar-thumb-rounded-full"
|
||||||
ref="contentScrollContainer"
|
ref="contentScrollContainer"
|
||||||
@scroll="handleScroll"
|
|
||||||
>
|
>
|
||||||
<div class="sm:max-w-prose grid grid-cols-1 mx-auto my-5 w-full">
|
<div class="sm:max-w-prose grid grid-cols-1 mx-auto my-5 w-full">
|
||||||
|
<div ref="isTopOfContentRef" />
|
||||||
<slot name="default" v-bind:scrollToBottom />
|
<slot name="default" v-bind:scrollToBottom />
|
||||||
|
<div ref="isBottomOfContentRef" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Shadow Bottom -->
|
<!-- Shadow Bottom -->
|
||||||
<div
|
<div
|
||||||
class="row-start-3 row-span-1 col-start-1 sm:row-start-4 relative w-full h-[1rem] bg-gradient-to-t from-black/10 to-transparent dark:from-white/10 pointer-events-none transition-opacity duration-300"
|
class="transition-opacity opacity-0 row-start-3 row-span-1 col-start-1 sm:row-start-4 relative w-full h-[1rem] bg-gradient-to-t from-black/10 to-transparent dark:from-white/10 pointer-events-none duration-300"
|
||||||
:class="{ 'opacity-0': !showBottomShadow, 'opacity-100': showBottomShadow }"
|
:class="{ 'opacity-100': !isBottomOfContentVisible }"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Footer Section -->
|
<!-- Footer Section -->
|
||||||
@@ -52,9 +53,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
||||||
import { useTemplateRef } from 'vue'
|
|
||||||
|
|
||||||
const slots = defineSlots<{
|
const slots = defineSlots<{
|
||||||
default?: (slotBindings: { scrollToBottom: typeof scrollToBottom }) => void
|
default?: (slotBindings: { scrollToBottom: typeof scrollToBottom }) => void
|
||||||
footer?: () => void
|
footer?: () => void
|
||||||
@@ -62,8 +60,18 @@ const slots = defineSlots<{
|
|||||||
|
|
||||||
const contentScrollContainerRef = useTemplateRef('contentScrollContainer')
|
const contentScrollContainerRef = useTemplateRef('contentScrollContainer')
|
||||||
|
|
||||||
const showTopShadow = ref(false)
|
const isTopOfContentRef = useTemplateRef('isTopOfContentRef')
|
||||||
const showBottomShadow = ref(false)
|
const isTopOfContentVisible = useElementVisibility(isTopOfContentRef, {
|
||||||
|
threshold: 1,
|
||||||
|
scrollTarget: contentScrollContainerRef,
|
||||||
|
})
|
||||||
|
const isBottomOfContentRef = useTemplateRef('isBottomOfContentRef')
|
||||||
|
const isBottomOfContentVisible = useElementVisibility(isBottomOfContentRef, {
|
||||||
|
threshold: 1,
|
||||||
|
scrollTarget: contentScrollContainerRef,
|
||||||
|
})
|
||||||
|
isTopOfContentVisible.value = true
|
||||||
|
isBottomOfContentVisible.value = true
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
if (contentScrollContainerRef.value) {
|
if (contentScrollContainerRef.value) {
|
||||||
@@ -71,17 +79,6 @@ const scrollToBottom = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
const el = contentScrollContainerRef.value
|
|
||||||
if (!el) return
|
|
||||||
|
|
||||||
const hasScrollTop = el.scrollTop > 0
|
|
||||||
const hasScrollBottom = el.scrollTop + el.clientHeight < el.scrollHeight
|
|
||||||
|
|
||||||
showTopShadow.value = hasScrollTop
|
|
||||||
showBottomShadow.value = hasScrollBottom
|
|
||||||
}
|
|
||||||
|
|
||||||
// Устанавливаем корректную высоту при загрузке и изменении размера окна
|
// Устанавливаем корректную высоту при загрузке и изменении размера окна
|
||||||
const onresize = () => {
|
const onresize = () => {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
@@ -133,8 +130,7 @@ onMounted(() => {
|
|||||||
// visualViewport?.addEventListener('resize', onresize, { passive: true })
|
// visualViewport?.addEventListener('resize', onresize, { passive: true })
|
||||||
contentScrollContainerRef.value!.addEventListener('touchmove', preventScroll, { passive: false })
|
contentScrollContainerRef.value!.addEventListener('touchmove', preventScroll, { passive: false })
|
||||||
|
|
||||||
// Инициализируем отображение теней
|
console.log('mounted')
|
||||||
handleScroll()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|||||||
@@ -19,5 +19,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [tailwindScrollbar, tailwindcssPrimeUI, tailwindcssSafeArea],
|
plugins: [
|
||||||
|
tailwindScrollbar({ nocompatible: true, preferredStrategy: 'pseudoelements' }),
|
||||||
|
tailwindcssPrimeUI,
|
||||||
|
tailwindcssSafeArea,
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ export default defineConfig(({ mode }) => {
|
|||||||
from: '@tanstack/vue-query',
|
from: '@tanstack/vue-query',
|
||||||
imports: ['useQuery', 'useMutation'],
|
imports: ['useQuery', 'useMutation'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: '@vueuse/core',
|
||||||
|
imports: ['useElementVisibility'],
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// from: '@hereconnect/types',
|
// from: '@hereconnect/types',
|
||||||
// type: true,
|
// type: true,
|
||||||
|
|||||||
Generated
+38
-2
@@ -31,6 +31,9 @@ importers:
|
|||||||
'@unhead/vue':
|
'@unhead/vue':
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13(vue@3.5.13(typescript@5.7.2))
|
version: 1.11.13(vue@3.5.13(typescript@5.7.2))
|
||||||
|
'@vueuse/core':
|
||||||
|
specifier: ^12.0.0
|
||||||
|
version: 12.0.0(typescript@5.7.2)
|
||||||
events:
|
events:
|
||||||
specifier: ^3.3.0
|
specifier: ^3.3.0
|
||||||
version: 3.3.0
|
version: 3.3.0
|
||||||
@@ -160,7 +163,7 @@ importers:
|
|||||||
version: 5.7.2
|
version: 5.7.2
|
||||||
unplugin-auto-import:
|
unplugin-auto-import:
|
||||||
specifier: ^0.18.6
|
specifier: ^0.18.6
|
||||||
version: 0.18.6(rollup@4.27.4)
|
version: 0.18.6(@vueuse/core@12.0.0(typescript@5.7.2))(rollup@4.27.4)
|
||||||
unplugin-info:
|
unplugin-info:
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1(esbuild@0.23.1)(rollup@4.27.4)(vite@5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0))
|
version: 1.2.1(esbuild@0.23.1)(rollup@4.27.4)(vite@5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0))
|
||||||
@@ -1385,6 +1388,9 @@ packages:
|
|||||||
'@types/tough-cookie@4.0.5':
|
'@types/tough-cookie@4.0.5':
|
||||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.20':
|
||||||
|
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
|
||||||
|
|
||||||
'@types/web-push@3.6.4':
|
'@types/web-push@3.6.4':
|
||||||
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
|
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
|
||||||
|
|
||||||
@@ -1718,6 +1724,15 @@ packages:
|
|||||||
'@vue/tsconfig@0.5.1':
|
'@vue/tsconfig@0.5.1':
|
||||||
resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
|
resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
|
||||||
|
|
||||||
|
'@vueuse/core@12.0.0':
|
||||||
|
resolution: {integrity: sha512-C12RukhXiJCbx4MGhjmd/gH52TjJsc3G0E0kQj/kb19H3Nt6n1CA4DRWuTdWWcaFRdlTe0npWDS942mvacvNBw==}
|
||||||
|
|
||||||
|
'@vueuse/metadata@12.0.0':
|
||||||
|
resolution: {integrity: sha512-Yzimd1D3sjxTDOlF05HekU5aSGdKjxhuhRFHA7gDWLn57PRbBIh+SF5NmjhJ0WRgF3my7T8LBucyxdFJjIfRJQ==}
|
||||||
|
|
||||||
|
'@vueuse/shared@12.0.0':
|
||||||
|
resolution: {integrity: sha512-3i6qtcq2PIio5i/vVYidkkcgvmTjCqrf26u+Fd4LhnbBmIT6FN8y6q/GJERp8lfcB9zVEfjdV0Br0443qZuJpw==}
|
||||||
|
|
||||||
'@xmldom/xmldom@0.9.6':
|
'@xmldom/xmldom@0.9.6':
|
||||||
resolution: {integrity: sha512-Su4xcxR0CPGwlDHNmVP09fqET9YxbyDXHaSob6JlBH7L6reTYaeim6zbk9o08UarO0L5GTRo3uzl0D+9lSxmvw==}
|
resolution: {integrity: sha512-Su4xcxR0CPGwlDHNmVP09fqET9YxbyDXHaSob6JlBH7L6reTYaeim6zbk9o08UarO0L5GTRo3uzl0D+9lSxmvw==}
|
||||||
engines: {node: '>=14.6'}
|
engines: {node: '>=14.6'}
|
||||||
@@ -4917,6 +4932,8 @@ snapshots:
|
|||||||
|
|
||||||
'@types/tough-cookie@4.0.5': {}
|
'@types/tough-cookie@4.0.5': {}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.20': {}
|
||||||
|
|
||||||
'@types/web-push@3.6.4':
|
'@types/web-push@3.6.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.10.2
|
'@types/node': 22.10.2
|
||||||
@@ -5465,6 +5482,23 @@ snapshots:
|
|||||||
|
|
||||||
'@vue/tsconfig@0.5.1': {}
|
'@vue/tsconfig@0.5.1': {}
|
||||||
|
|
||||||
|
'@vueuse/core@12.0.0(typescript@5.7.2)':
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.20
|
||||||
|
'@vueuse/metadata': 12.0.0
|
||||||
|
'@vueuse/shared': 12.0.0(typescript@5.7.2)
|
||||||
|
vue: 3.5.13(typescript@5.7.2)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- typescript
|
||||||
|
|
||||||
|
'@vueuse/metadata@12.0.0': {}
|
||||||
|
|
||||||
|
'@vueuse/shared@12.0.0(typescript@5.7.2)':
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.13(typescript@5.7.2)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- typescript
|
||||||
|
|
||||||
'@xmldom/xmldom@0.9.6': {}
|
'@xmldom/xmldom@0.9.6': {}
|
||||||
|
|
||||||
abbrev@1.1.1: {}
|
abbrev@1.1.1: {}
|
||||||
@@ -7635,7 +7669,7 @@ snapshots:
|
|||||||
|
|
||||||
universalify@2.0.1: {}
|
universalify@2.0.1: {}
|
||||||
|
|
||||||
unplugin-auto-import@0.18.6(rollup@4.27.4):
|
unplugin-auto-import@0.18.6(@vueuse/core@12.0.0(typescript@5.7.2))(rollup@4.27.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@antfu/utils': 0.7.10
|
'@antfu/utils': 0.7.10
|
||||||
'@rollup/pluginutils': 5.1.3(rollup@4.27.4)
|
'@rollup/pluginutils': 5.1.3(rollup@4.27.4)
|
||||||
@@ -7645,6 +7679,8 @@ snapshots:
|
|||||||
minimatch: 9.0.5
|
minimatch: 9.0.5
|
||||||
unimport: 3.14.2(rollup@4.27.4)
|
unimport: 3.14.2(rollup@4.27.4)
|
||||||
unplugin: 1.16.0
|
unplugin: 1.16.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@vueuse/core': 12.0.0(typescript@5.7.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- rollup
|
- rollup
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user