1
0
forked from test/crm

fix: maintain scroll position in ListRows component

This commit is contained in:
Shariq Ansari 2024-12-23 15:31:35 +05:30
parent 3dc45c3c8c
commit d1efa543db
8 changed files with 472 additions and 446 deletions

View File

@ -11,7 +11,10 @@
row-key="name" row-key="name"
v-bind="$attrs" v-bind="$attrs"
> >
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')"> <ListHeader
class="sm:mx-5 mx-3"
@columnWidthUpdated="emit('columnWidthUpdated')"
>
<ListHeaderItem <ListHeaderItem
v-for="column in columns" v-for="column in columns"
:key="column.key" :key="column.key"
@ -29,12 +32,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows class="mx-3 sm:mx-5" id="list-rows"> <ListRows
<ListRow class="mx-3 sm:mx-5"
v-for="row in rows" :rows="rows"
:key="row.name"
v-slot="{ idx, column, item }" v-slot="{ idx, column, item }"
:row="row" doctype="CRM Call Log"
> >
<ListRowItem :item="item"> <ListRowItem :item="item">
<template #prefix> <template #prefix>
@ -102,8 +104,7 @@
variant="ghosted" variant="ghosted"
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'" :class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
@click.stop.prevent=" @click.stop.prevent="
() => () => emit('likeDoc', { name: row.name, liked: isLiked(item) })
emit('likeDoc', { name: row.name, liked: isLiked(item) })
" "
> >
<HeartIcon class="h-4 w-4" /> <HeartIcon class="h-4 w-4" />
@ -127,7 +128,6 @@
</div> </div>
</template> </template>
</ListRowItem> </ListRowItem>
</ListRow>
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
@ -161,13 +161,12 @@
<script setup> <script setup>
import HeartIcon from '@/components/Icons/HeartIcon.vue' import HeartIcon from '@/components/Icons/HeartIcon.vue'
import ListBulkActions from '@/components/ListBulkActions.vue' import ListBulkActions from '@/components/ListBulkActions.vue'
import ListRows from '@/components/ListViews/ListRows.vue'
import { import {
Avatar, Avatar,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListRows,
ListRow,
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,
@ -232,7 +231,7 @@ const listBulkActionsRef = ref(null)
defineExpose({ defineExpose({
customListActions: computed( customListActions: computed(
() => listBulkActionsRef.value?.customListActions () => listBulkActionsRef.value?.customListActions,
), ),
}) })
</script> </script>

View File

@ -36,12 +36,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows class="mx-3 sm:mx-5" id="list-rows"> <ListRows
<ListRow class="mx-3 sm:mx-5"
v-for="row in rows" :rows="rows"
:key="row.name"
v-slot="{ idx, column, item }" v-slot="{ idx, column, item }"
:row="row" doctype="Contact"
> >
<ListRowItem :item="item"> <ListRowItem :item="item">
<template #prefix> <template #prefix>
@ -100,8 +99,7 @@
variant="ghosted" variant="ghosted"
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'" :class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
@click.stop.prevent=" @click.stop.prevent="
() => () => emit('likeDoc', { name: row.name, liked: isLiked(item) })
emit('likeDoc', { name: row.name, liked: isLiked(item) })
" "
> >
<HeartIcon class="h-4 w-4" /> <HeartIcon class="h-4 w-4" />
@ -125,7 +123,6 @@
</div> </div>
</template> </template>
</ListRowItem> </ListRowItem>
</ListRow>
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
@ -160,13 +157,12 @@
import HeartIcon from '@/components/Icons/HeartIcon.vue' import HeartIcon from '@/components/Icons/HeartIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import ListBulkActions from '@/components/ListBulkActions.vue' import ListBulkActions from '@/components/ListBulkActions.vue'
import ListRows from '@/components/ListViews/ListRows.vue'
import { import {
Avatar, Avatar,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListRows,
ListRow,
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,

View File

@ -36,7 +36,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows :rows="rows" v-slot="{ idx, column, item, row }"> <ListRows
:rows="rows"
v-slot="{ idx, column, item, row }"
doctype="CRM Deal"
>
<div v-if="column.key === '_assign'" class="flex items-center"> <div v-if="column.key === '_assign'" class="flex items-center">
<MultipleAvatar <MultipleAvatar
:avatars="item" :avatars="item"

View File

@ -10,7 +10,10 @@
}" }"
row-key="name" row-key="name"
> >
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')"> <ListHeader
class="sm:mx-5 mx-3"
@columnWidthUpdated="emit('columnWidthUpdated')"
>
<ListHeaderItem <ListHeaderItem
v-for="column in columns" v-for="column in columns"
:key="column.key" :key="column.key"
@ -28,12 +31,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows class="mx-3 sm:mx-5" id="list-rows"> <ListRows
<ListRow class="mx-3 sm:mx-5"
v-for="row in rows" :rows="rows"
:key="row.name"
v-slot="{ idx, column, item }" v-slot="{ idx, column, item }"
:row="row" doctype="Email Template"
> >
<ListRowItem :item="item"> <ListRowItem :item="item">
<!-- <template #prefix> <!-- <template #prefix>
@ -90,8 +92,7 @@
variant="ghosted" variant="ghosted"
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'" :class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
@click.stop.prevent=" @click.stop.prevent="
() => () => emit('likeDoc', { name: row.name, liked: isLiked(item) })
emit('likeDoc', { name: row.name, liked: isLiked(item) })
" "
> >
<HeartIcon class="h-4 w-4" /> <HeartIcon class="h-4 w-4" />
@ -115,7 +116,6 @@
</div> </div>
</template> </template>
</ListRowItem> </ListRowItem>
</ListRow>
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
@ -148,12 +148,11 @@
<script setup> <script setup>
import HeartIcon from '@/components/Icons/HeartIcon.vue' import HeartIcon from '@/components/Icons/HeartIcon.vue'
import ListBulkActions from '@/components/ListBulkActions.vue' import ListBulkActions from '@/components/ListBulkActions.vue'
import ListRows from '@/components/ListViews/ListRows.vue'
import { import {
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListRows,
ListRow,
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,
@ -219,7 +218,7 @@ const listBulkActionsRef = ref(null)
defineExpose({ defineExpose({
customListActions: computed( customListActions: computed(
() => listBulkActionsRef.value?.customListActions () => listBulkActionsRef.value?.customListActions,
), ),
}) })
</script> </script>

View File

@ -36,7 +36,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows :rows="rows" v-slot="{ idx, column, item, row }"> <ListRows
:rows="rows"
v-slot="{ idx, column, item, row }"
doctype="CRM Lead"
>
<div v-if="column.key === '_assign'" class="flex items-center"> <div v-if="column.key === '_assign'" class="flex items-center">
<MultipleAvatar <MultipleAvatar
:avatars="item" :avatars="item"

View File

@ -15,7 +15,7 @@
</div> </div>
</div> </div>
</ListGroupHeader> </ListGroupHeader>
<ListGroupRows :group="group" id="list-rows"> <ListGroupRows :group="group">
<ListRow <ListRow
v-for="row in group.rows" v-for="row in group.rows"
:key="row.name" :key="row.name"
@ -27,7 +27,12 @@
</ListGroupRows> </ListGroupRows>
</div> </div>
</div> </div>
<ListRows class="mx-3 sm:mx-5" v-else id="list-rows"> <ListRows
v-else
ref="scrollContainer"
class="mx-3 sm:mx-5"
@scroll="handleScroll"
>
<ListRow <ListRow
v-for="row in reactivieRows" v-for="row in reactivieRows"
:key="row.name" :key="row.name"
@ -40,27 +45,53 @@
</template> </template>
<script setup> <script setup>
import { useStorage } from '@vueuse/core'
import { ListRows, ListRow, ListGroupHeader, ListGroupRows } from 'frappe-ui' import { ListRows, ListRow, ListGroupHeader, ListGroupRows } from 'frappe-ui'
import { ref, computed, watch, onBeforeUnmount, onMounted } from 'vue'
import { ref, computed, watch } from 'vue'
const props = defineProps({ const props = defineProps({
rows: { rows: {
type: Array, type: Array,
required: true, required: true,
}, },
doctype: {
type: String,
default: 'CRM Lead',
},
}) })
const reactivieRows = ref(props.rows) const reactivieRows = ref(props.rows)
watch( watch(
() => props.rows, () => props.rows,
(val) => (reactivieRows.value = val) (val) => (reactivieRows.value = val),
) )
let showGroupedRows = computed(() => { let showGroupedRows = computed(() => {
return props.rows.every( return props.rows.every(
(row) => row.group && row.rows && Array.isArray(row.rows) (row) => row.group && row.rows && Array.isArray(row.rows),
) )
}) })
const scrollPosition = useStorage(`scrollPosition${props.doctype}`, 0)
const scrollContainer = ref(null)
const handleScroll = () => {
if (scrollContainer.value) {
scrollPosition.value = scrollContainer.value.$el.scrollTop
}
}
onBeforeUnmount(() => {
if (scrollContainer.value) {
scrollContainer.value.$el.removeEventListener('scroll', handleScroll)
}
})
onMounted(() => {
if (scrollContainer.value) {
scrollContainer.value.$el.addEventListener('scroll', handleScroll)
scrollContainer.value.$el.scrollTop = scrollPosition.value
}
})
</script> </script>

View File

@ -14,7 +14,10 @@
}" }"
row-key="name" row-key="name"
> >
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')"> <ListHeader
class="sm:mx-5 mx-3"
@columnWidthUpdated="emit('columnWidthUpdated')"
>
<ListHeaderItem <ListHeaderItem
v-for="column in columns" v-for="column in columns"
:key="column.key" :key="column.key"
@ -32,12 +35,11 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows class="mx-3 sm:mx-5" id="list-rows"> <ListRows
<ListRow class="mx-3 sm:mx-5"
v-for="row in rows" :rows="rows"
:key="row.name"
v-slot="{ idx, column, item }" v-slot="{ idx, column, item }"
:row="row" doctype="CRM Organization"
> >
<ListRowItem :item="item"> <ListRowItem :item="item">
<template #prefix> <template #prefix>
@ -84,8 +86,7 @@
variant="ghosted" variant="ghosted"
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'" :class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
@click.stop.prevent=" @click.stop.prevent="
() => () => emit('likeDoc', { name: row.name, liked: isLiked(item) })
emit('likeDoc', { name: row.name, liked: isLiked(item) })
" "
> >
<HeartIcon class="h-4 w-4" /> <HeartIcon class="h-4 w-4" />
@ -109,7 +110,6 @@
</div> </div>
</template> </template>
</ListRowItem> </ListRowItem>
</ListRow>
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
@ -142,13 +142,12 @@
<script setup> <script setup>
import HeartIcon from '@/components/Icons/HeartIcon.vue' import HeartIcon from '@/components/Icons/HeartIcon.vue'
import ListBulkActions from '@/components/ListBulkActions.vue' import ListBulkActions from '@/components/ListBulkActions.vue'
import ListRows from '@/components/ListViews/ListRows.vue'
import { import {
Avatar, Avatar,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListRows,
ListRow,
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,
@ -216,7 +215,7 @@ const listBulkActionsRef = ref(null)
defineExpose({ defineExpose({
customListActions: computed( customListActions: computed(
() => listBulkActionsRef.value?.customListActions () => listBulkActionsRef.value?.customListActions,
), ),
}) })
</script> </script>

View File

@ -31,17 +31,14 @@
</Button> </Button>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows class="mx-3 sm:mx-5" id="list-rows"> <ListRows
<ListRow class="mx-3 sm:mx-5"
v-for="row in rows" :rows="rows"
:key="row.name"
v-slot="{ idx, column, item }" v-slot="{ idx, column, item }"
:row="row" doctype="CRM Task"
> >
<div v-if="column.key === 'due_date'"> <div v-if="column.key === 'due_date'">
<Tooltip <Tooltip :text="item && formatDate(item, 'ddd, MMM D, YYYY | hh:mm a')">
:text="item && formatDate(item, 'ddd, MMM D, YYYY | hh:mm a')"
>
<div class="flex items-center gap-2 truncate text-base"> <div class="flex items-center gap-2 truncate text-base">
<div><CalendarIcon /></div> <div><CalendarIcon /></div>
<div v-if="item" class="truncate"> <div v-if="item" class="truncate">
@ -106,8 +103,7 @@
variant="ghosted" variant="ghosted"
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'" :class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
@click.stop.prevent=" @click.stop.prevent="
() => () => emit('likeDoc', { name: row.name, liked: isLiked(item) })
emit('likeDoc', { name: row.name, liked: isLiked(item) })
" "
> >
<HeartIcon class="h-4 w-4" /> <HeartIcon class="h-4 w-4" />
@ -131,7 +127,6 @@
</div> </div>
</template> </template>
</ListRowItem> </ListRowItem>
</ListRow>
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
@ -167,14 +162,13 @@ import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue' import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
import CalendarIcon from '@/components/Icons/CalendarIcon.vue' import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
import ListBulkActions from '@/components/ListBulkActions.vue' import ListBulkActions from '@/components/ListBulkActions.vue'
import ListRows from '@/components/ListViews/ListRows.vue'
import { formatDate } from '@/utils' import { formatDate } from '@/utils'
import { import {
Avatar, Avatar,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListRows,
ListRow,
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,