before remake add chat window

This commit is contained in:
2026-04-23 08:51:14 +03:00
parent 09d7812a7a
commit 94851c8b82
90 changed files with 8833 additions and 4159 deletions

View File

@@ -2,106 +2,151 @@
<q-scroll-area
ref="scrollAreaRef"
:style="scrollStyle"
:vertical-offset="[top, bottom]"
:thumb-style="{ borderRadius: '3px', background: '#999', width: '6px', opacity: '0.4' }"
:vertical-offset="verticalOffset"
:thumb-style="thumbStyle"
@scroll="onScroll"
class="shadow-scroll w100"
class="shadow-scroll position-relative w100"
>
<q-resize-observer @resize="onInnerResize"/>
<div :style="{ height: top + 'px'}"/>
<slot/>
<div :style="{ height: bottom + 'px'}"/>
<q-resize-observer @resize="onInnerResize" />
<div :style="topSpacerStyle" />
<slot />
<div :style="{ height: bottom + 'px' }" />
</q-scroll-area>
</template>
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import { ref, computed, watch } from 'vue'
import { QScrollArea } from 'quasar'
const props = withDefaults(defineProps<{
interface Props {
height: number
topOffset?: number
bottomOffset?: number
scrollPosition?: number
offset?: number
}>(),
{
scrollPosition: 0,
offset: 16
}
)
offset?: number
scrollHeaderSizeFull?: number
scrollHeaderSizeSticky?: number
}
const props = withDefaults(defineProps<Props>(), {
scrollPosition: 0,
offset: 16,
scrollHeaderSizeFull: 0,
scrollHeaderSizeSticky: 0
})
const emit = defineEmits([
'updateScrollPosition',
'resizeScrollArea'
])
const top = computed(() => props.topOffset || props.offset)
const bottom = computed(() => props.bottomOffset || props.offset)
const scrollAreaRef = ref<QScrollArea | null>(null)
const currentScroll = ref(0)
const innerHeight = ref(0)
const scrollStyle = computed(() => ({
height: props.height + 'px',
'--top-height': top.value + 'px',
'--bottom-height': bottom.value + 'px'
}))
const top = computed(() => props.topOffset || props.offset)
const bottom = computed(() => props.bottomOffset || props.offset)
function onScroll () {
emit('updateScrollPosition', scrollAreaRef.value?.getScrollPosition().top || 0)
const offsetBefore = computed(() => {
if (props.scrollHeaderSizeFull && props.scrollHeaderSizeSticky) {
return Math.max(
props.scrollHeaderSizeFull - currentScroll.value,
props.scrollHeaderSizeSticky
)
}
return 0
})
const verticalOffset = computed<[number, number]>(() => [
top.value + offsetBefore.value,
bottom.value
])
const thumbStyle = {
borderRadius: '3px',
background: '#999',
width: '6px',
opacity: '0.4'
}
const scrollAreaRef = ref()
const scrollStyle = computed(() => {
const isHeaderSticky = props.scrollHeaderSizeFull && props.scrollHeaderSizeSticky
? (props.scrollHeaderSizeFull - currentScroll.value <= props.scrollHeaderSizeSticky)
: true
watch(() => props.scrollPosition, async (newPosition) => {
await nextTick()
scrollAreaRef.value?.setScrollPosition('vertical', newPosition)
}, { immediate: true })
return {
height: props.height + 'px',
'--top-height': top.value + 'px',
'--bottom-height': bottom.value + 'px',
'--offset-before': offsetBefore.value + 'px',
'--top-shadow-visibility': isHeaderSticky ? 'visible' : 'hidden'
}
})
watch([top, bottom, () => props.height], async () => {
await nextTick()
emit('resizeScrollArea', {
const topSpacerStyle = computed(() => ({
height: top.value + 'px',
visibility: offsetBefore.value === 0 ? 'visible' as const : 'hidden' as const,
marginTop: offsetBefore.value === 0 ? '0px' : `-${top.value}px`
}))
function onScroll ({ verticalPosition }: { verticalPosition: number }) {
currentScroll.value = verticalPosition
emit('updateScrollPosition', verticalPosition)
}
function onInnerResize (size: { height: number }) {
innerHeight.value = size.height
emit('resizeScrollArea', {
height: props.height,
innerHeight: size.height,
topOffset: top.value,
bottomOffset: bottom.value
})
}
watch(() => props.scrollPosition, (newPos) => {
if (newPos !== currentScroll.value) {
scrollAreaRef.value?.setScrollPosition('vertical', newPos)
}
})
watch([top, bottom, () => props.height], () => {
emit('resizeScrollArea', {
height: props.height,
innerHeight: innerHeight.value,
topOffset: top.value,
bottomOffset: bottom.value
})
}, { immediate: true })
interface sizeParams {
height: number,
width: number
}
function onInnerResize (size: sizeParams) {
innerHeight.value = size.height
}
})
</script>
<style scoped>
.shadow-scroll:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: var(--top-height);
background: linear-gradient(white, transparent);
pointer-events: none;
z-index: 95;
}
.shadow-scroll:before,
.shadow-scroll:after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: var(--bottom-height);
background: linear-gradient(transparent, white);
pointer-events: none;
z-index: 95;
transition: opacity 0.2s ease;
}
.shadow-scroll:before {
top: var(--offset-before);
height: var(--top-height);
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
visibility: var(--top-shadow-visibility);
}
.shadow-scroll:after {
bottom: 0;
height: var(--bottom-height);
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}
.shadow-scroll :deep(.q-scrollarea__content) {
max-width: min(100vw, var(--body-width));
max-width: 100%;
}
</style>