Browse Source

fix: gallery view fixes

pull/9799/head
DarkPhoenix2704 2 weeks ago
parent
commit
dae06d31f9
  1. 166
      packages/nc-gui/components/smartsheet/Gallery.vue

166
packages/nc-gui/components/smartsheet/Gallery.vue

@ -1,4 +1,4 @@
<script lang="ts" setup>
<script setup lang="ts">
import { UITypes, ViewTypes, isVirtualCol } from 'nocodb-sdk'
import type { Attachment } from '../../lib/types'
import type { Row as RowType } from '#imports'
@ -27,8 +27,8 @@ provide(IsGalleryInj, ref(true))
provide(IsGridInj, ref(false))
provide(IsCalendarInj, ref(false))
provide(RowHeightInj, ref(1 as const))
// provide view data reload hook as fallback to row data reload
provide(ReloadRowDataHookInj, reloadViewDataHook!)
const {
fetchChunk,
loadGalleryData,
@ -54,7 +54,6 @@ const coverImageColumn: any = computed(() =>
const coverImageObjectFitClass = computed(() => {
const fk_cover_image_object_fit = parseProp(galleryData.value?.meta)?.fk_cover_image_object_fit || CoverImageObjectFit.FIT
if (fk_cover_image_object_fit === CoverImageObjectFit.FIT) return '!object-contain'
if (fk_cover_image_object_fit === CoverImageObjectFit.COVER) return '!object-cover'
})
@ -158,8 +157,6 @@ const handleClick = (col, event) => {
openNewRecordFormHook?.on(openNewRecordFormHookHandler)
// remove openNewRecordFormHookHandler before unmounting
// so that it won't be triggered multiple times
onBeforeUnmount(() => openNewRecordFormHook.off(openNewRecordFormHookHandler))
const reloadAttachments = ref(false)
@ -190,7 +187,7 @@ const scrollTop = ref(0)
const rowSlice = reactive({
start: 0,
end: 100,
end: 12,
})
const { width: scrollContainerWidth } = useElementSize(scrollContainer)
@ -255,34 +252,35 @@ const updateVisibleRows = async () => {
clearCache(Math.max(0, start - BUFFER_SIZE), Math.min(totalRows.value, end + BUFFER_SIZE))
}
const containerTransformY = ref(0)
const calculateSlices = () => {
if (!scrollContainer.value || cardHeight.value === 0) {
if (!scrollContainer.value) {
setTimeout(calculateSlices, 50)
return
}
const { clientHeight, scrollHeight } = scrollContainer.value
const { clientHeight } = scrollContainer.value
const scrollTopRow = Math.floor(scrollTop.value / (cardHeight.value + 12))
const visibleRowStart = Math.floor(scrollTop.value / (cardHeight.value + 12))
const rowsVisible = Math.ceil((clientHeight - 12) / (cardHeight.value + 12))
const visibleRows = Math.ceil(clientHeight / (cardHeight.value + 12))
const BUFFER_ROWS = 2
const startRow = Math.max(0, scrollTopRow - BUFFER_ROWS) * columnsPerRow.value
const endRow = Math.min(totalRows.value, (scrollTopRow + visibleRows + BUFFER_ROWS) * columnsPerRow.value)
const isNearBottom = scrollTop.value + clientHeight >= scrollHeight - cardHeight.value * 2
const startRecordIndex = Math.max(0, visibleRowStart - BUFFER_ROWS) * columnsPerRow.value
const endRecordIndex = Math.min((visibleRowStart + rowsVisible + BUFFER_ROWS) * columnsPerRow.value, totalRows.value)
rowSlice.start = startRecordIndex
rowSlice.end = endRecordIndex
rowSlice.start = startRow
rowSlice.end = isNearBottom ? endRow + columnsPerRow.value * 2 : endRow
const val = Math.ceil(rowSlice.start / columnsPerRow.value) * (cardHeight.value + 12)
containerTransformY.value = val
updateVisibleRows()
}
const containerTransformY = computed(() => {
const rowStartIndex = Math.floor(rowSlice.start / columnsPerRow.value)
return rowStartIndex * (cardHeight.value + 12)
})
const containerHeight = computed(() => {
const numberOfRows = Math.ceil(totalRows.value / columnsPerRow.value)
return numberOfRows * cardHeight.value + (numberOfRows - 1) * 12
@ -293,10 +291,9 @@ let scrollRaf = false
useScroll(scrollContainer, {
onScroll: (e) => {
if (scrollRaf) return
scrollRaf = true
requestAnimationFrame(() => {
scrollTop.value = e.target?.scrollTop
scrollTop.value = e.target?.scrollTop || 0
calculateSlices()
scrollRaf = false
})
@ -327,21 +324,20 @@ watch(
},
)
const { width, height } = useWindowSize()
const placeholderAboveHeight = computed(() => {
const visibleRowStart = Math.floor(scrollTop.value / (cardHeight.value + 12))
const startRecordIndex = Math.max(0, visibleRowStart - 2)
return startRecordIndex * (cardHeight.value + 12)
})
let resizeTimeout: number | null = null
const { width, height } = useWindowSize()
watch(
[width, height, cardHeight, columnsPerRow],
[() => width.value, () => height.value, () => columnsPerRow.value],
() => {
if (resizeTimeout) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(() => {
calculateSlices()
resizeTimeout = null
}, 150) as unknown as number
calculateSlices()
},
{
immediate: true,
@ -350,50 +346,52 @@ watch(
reloadViewDataHook?.on(async () => {
clearCache(Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY)
await syncCount()
calculateSlices()
await updateVisibleRows()
})
</script>
<template>
<NcDropdown
v-model:visible="contextMenu"
:disabled="contextMenuTarget === null"
:trigger="isSqlView ? [] : ['contextmenu']"
overlay-class-name="nc-dropdown-grid-context-menu"
<div
ref="scrollContainer"
data-testid="nc-gallery-wrapper"
class="flex flex-col w-full nc-gallery select-none relative nc-scrollbar-md bg-gray-50 h-[calc(100svh-93px)]"
>
<template #overlay>
<NcMenu @click="contextMenu = false">
<NcMenuItem v-if="contextMenuTarget" @click="expandForm(contextMenuTarget.row)">
<div v-e="['a:row:expand-record']" class="flex items-center gap-2">
<component :is="iconMap.expand" class="flex" />
{{ $t('activity.expandRecord') }}
</div>
</NcMenuItem>
<NcDivider />
<NcMenuItem
v-if="contextMenuTarget?.index !== undefined"
class="!text-red-600 !hover:bg-red-50"
@click="deleteRow(contextMenuTarget.index)"
>
<div v-e="['a:row:delete']" class="flex items-center gap-2">
<component :is="iconMap.delete" class="flex" />
<!-- Delete Row -->
{{ $t('activity.deleteRow') }}
</div>
</NcMenuItem>
</NcMenu>
</template>
<div
ref="scrollContainer"
data-testid="nc-gallery-wrapper"
class="flex flex-col w-full nc-gallery select-none relative nc-scrollbar-md bg-gray-50 h-[calc(100svh-93px)]"
<NcDropdown
v-model:visible="contextMenu"
:disabled="contextMenuTarget === null"
:trigger="isSqlView ? [] : ['contextmenu']"
overlay-class-name="nc-dropdown-grid-context-menu"
>
<div>
<div :style="{ height: `${containerHeight}px` }">
<div :style="{ transform: `translateY(${containerTransformY}px)` }" class="nc-gallery-container grid gap-3 p-3">
<template #overlay>
<NcMenu @click="contextMenu = false">
<NcMenuItem v-if="contextMenuTarget" @click="expandForm(contextMenuTarget.row)">
<div v-e="['a:row:expand-record']" class="flex items-center gap-2">
<component :is="iconMap.expand" class="flex" />
{{ $t('activity.expandRecord') }}
</div>
</NcMenuItem>
<NcDivider />
<NcMenuItem
v-if="contextMenuTarget?.index !== undefined"
class="!text-red-600 !hover:bg-red-50"
@click="deleteRow(contextMenuTarget.index)"
>
<div v-e="['a:row:delete']" class="flex items-center gap-2">
<component :is="iconMap.delete" class="flex" />
{{ $t('activity.deleteRow') }}
</div>
</NcMenuItem>
</NcMenu>
</template>
<div
:class="{
'h-full': totalRows < 30,
}"
>
<div :key="containerHeight" class="relative" :style="{ height: `${containerHeight}px` }">
<div :style="{ height: `${placeholderAboveHeight}px` }"></div>
<div class="nc-gallery-container grid gap-3 p-3">
<div
v-for="(record, rowIndex) in visibleRows"
:key="`record-${record.rowMeta.rowIndex}`"
@ -420,7 +418,6 @@ reloadViewDataHook?.on(async () => {
</div>
</a>
</template>
<template #prevArrow>
<div class="z-10 arrow">
<NcButton
@ -432,7 +429,6 @@ reloadViewDataHook?.on(async () => {
</NcButton>
</div>
</template>
<template #nextArrow>
<div class="z-10 arrow">
<NcButton
@ -444,7 +440,6 @@ reloadViewDataHook?.on(async () => {
</NcButton>
</div>
</template>
<template v-for="(attachment, index) in attachments(record)">
<LazyCellAttachmentPreviewImage
v-if="isImage(attachment.title, attachment.mimetype ?? attachment.type)"
@ -470,7 +465,6 @@ reloadViewDataHook?.on(async () => {
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
@ -482,7 +476,6 @@ reloadViewDataHook?.on(async () => {
</template>
<template v-else> - </template>
</h2>
<div
v-for="col in fieldsWithoutDisplay"
:key="`record-${record.rowMeta.rowIndex}-${col.id}`"
@ -495,11 +488,9 @@ reloadViewDataHook?.on(async () => {
<div class="flex flex-row w-full justify-start">
<div class="nc-card-col-header w-full !children:text-gray-500">
<LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" :hide-menu="true" />
<LazySmartsheetHeaderCell v-else :column="col" :hide-menu="true" />
</div>
</div>
<div
v-if="!isRowEmpty(record, col)"
class="flex flex-row w-full text-gray-800 items-center justify-start min-h-7 py-1"
@ -511,7 +502,6 @@ reloadViewDataHook?.on(async () => {
:row="record"
class="!text-gray-800"
/>
<LazySmartsheetCell
v-else
v-model="record.row[col.title]"
@ -535,17 +525,16 @@ reloadViewDataHook?.on(async () => {
</div>
</div>
</div>
<div class="sticky bottom-4">
<NcButton v-if="isUIAllowed('dataInsert')" size="xs" type="secondary" class="ml-4" @click="openNewRecordFormHook.trigger">
<div class="flex items-center gap-2">
<component :is="iconMap.plus" class="" />
{{ $t('activity.newRecord') }}
</div>
</NcButton>
</div>
</NcDropdown>
<div class="sticky bottom-4">
<NcButton v-if="isUIAllowed('dataInsert')" size="xs" type="secondary" class="ml-4" @click="openNewRecordFormHook.trigger">
<div class="flex items-center gap-2">
<component :is="iconMap.plus" class="" />
{{ $t('activity.newRecord') }}
</div>
</NcButton>
</div>
</NcDropdown>
</div>
<Suspense>
<LazySmartsheetExpandedForm
v-if="expandedFormRow && expandedFormDlg"
@ -557,7 +546,6 @@ reloadViewDataHook?.on(async () => {
:view="view"
/>
</Suspense>
<Suspense>
<LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg && meta?.id"

Loading…
Cancel
Save