feat(web): make notifications list scrollable inside the sheet & tabs

This commit is contained in:
Pujit Mehrotra
2024-10-24 15:32:24 -04:00
parent 5b2421cb0c
commit 4d1656eaa8
5 changed files with 88 additions and 40 deletions

View File

@@ -6,13 +6,28 @@ import {
import type { NotificationType } from "~/composables/gql/graphql";
import { useFragment } from "~/composables/gql/fragment-masking";
import { useQuery } from "@vue/apollo-composable";
// import { useInfiniteScroll } from "@vueuse/core";
import { vInfiniteScroll } from "@vueuse/components";
const props = defineProps<{ type: NotificationType }>();
const element = ref<HTMLElement | null>(null);
const { result, error } = useQuery(getNotifications, {
/**
* Page size is the max amount of items fetched from the api in a single request.
*/
const props = withDefaults(
defineProps<{
type: NotificationType;
pageSize?: number;
}>(),
{
pageSize: 25,
}
);
const { result, error, fetchMore } = useQuery(getNotifications, {
filter: {
offset: 0,
limit: 10,
limit: props.pageSize,
type: props.type,
},
});
@@ -31,14 +46,35 @@ const notifications = computed(() => {
// and we don't want to display them in the wrong list client-side.
return list.filter((n) => n.type === props.type);
});
async function onLoadMore() {
console.log("[getNotifications] onLoadMore");
// void fetchMore({
// variables: {
// filter: {
// offset: notifications.value.length,
// limit: props.pageSize,
// type: props.type,
// },
// },
// });
}
// const { isLoading } = useInfiniteScroll(element, onLoadMore, {
// distance: 25,
// });
</script>
<template>
<div v-if="notifications?.length > 0" class="divide-y divide-gray-200">
<NotificationsItem
v-for="notification in notifications"
:key="notification.id"
v-bind="notification"
/>
</div>
<div
v-if="notifications?.length > 0"
v-infinite-scroll="onLoadMore"
class="divide-y divide-gray-200 overflow-y-scroll px-6 h-full"
>
<NotificationsItem
v-for="notification in notifications"
:key="notification.id"
v-bind="notification"
/>
</div>
</template>

View File

@@ -28,13 +28,13 @@ const { teleportTarget, determineTeleportTarget } = useTeleport();
<SheetContent
:to="teleportTarget"
class="w-full overflow-y-scroll sm:max-w-[540px] space-y-3"
class="w-full sm:max-w-[540px] space-y-3 h-screen"
>
<SheetHeader>
<SheetTitle>Notifications</SheetTitle>
</SheetHeader>
<Tabs default-value="unread" class="">
<Tabs default-value="unread" class="h-full">
<div class="flex flex-row justify-between items-center flex-wrap gap-2">
<TabsList class="ml-[1px]">
<TabsTrigger value="unread"> Unread </TabsTrigger>
@@ -66,7 +66,7 @@ const { teleportTarget, determineTeleportTarget } = useTeleport();
</Select>
</div>
<TabsContent value="unread">
<TabsContent value="unread" class="h-[92%]">
<NotificationsList :type="NotificationType.Unread" />
</TabsContent>
@@ -75,9 +75,6 @@ const { teleportTarget, determineTeleportTarget } = useTeleport();
</TabsContent>
</Tabs>
<SheetFooter class="text-center">
<p>Future pagination station</p>
</SheetFooter>
</SheetContent>
</Sheet>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<DialogRootProps>()
const props = defineProps<DialogRootProps & { class?: string }>()
const emits = defineEmits<DialogRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { type HTMLAttributes, computed } from "vue";
import {
DialogClose,
DialogContent,
@@ -8,44 +8,54 @@ import {
DialogOverlay,
DialogPortal,
useForwardPropsEmits,
} from 'radix-vue'
import { X } from 'lucide-vue-next'
import { type SheetVariants, sheetVariants } from '.'
import { cn } from '~/components/shadcn/utils'
} from "radix-vue";
import { X } from "lucide-vue-next";
import { type SheetVariants, sheetVariants } from ".";
import { cn } from "~/components/shadcn/utils";
import { vInfiniteScroll } from "@vueuse/components";
type ScrollLoader = Parameters<typeof useInfiniteScroll>[1];
interface SheetContentProps extends DialogContentProps {
class?: HTMLAttributes['class']
side?: SheetVariants['side']
disabled?: boolean
forceMount?: boolean
to?: string | HTMLElement | Element
class?: HTMLAttributes["class"];
side?: SheetVariants["side"];
padding?: SheetVariants["padding"];
disabled?: boolean;
forceMount?: boolean;
to?: string | HTMLElement | Element;
}
defineOptions({
inheritAttrs: false,
})
});
const props = defineProps<SheetContentProps>()
const props = defineProps<SheetContentProps>();
const emits = defineEmits<DialogContentEmits>()
const emits = defineEmits<
DialogContentEmits & { loadMore: Parameters<ScrollLoader> }
>();
const delegatedProps = computed(() => {
const { class: _, side, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
const { class: _, side, padding, ...delegated } = props;
console.log("[SheetContent] delegatedProps", delegated);
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DialogPortal :disabled="disabled" :force-mount="forceMount" :to="to as HTMLElement">
<DialogPortal
:disabled="disabled"
:force-mount="forceMount"
:to="to as HTMLElement"
>
<DialogOverlay
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<DialogContent
:class="cn(sheetVariants({ side }), props.class)"
:class="cn(sheetVariants({ side, padding }), props.class)"
v-bind="{ ...forwarded, ...$attrs }"
v-infinite-scroll="(state) => $emit('loadMore', state)"
>
<slot />

View File

@@ -10,7 +10,7 @@ export { default as SheetDescription } from './SheetDescription.vue'
export { default as SheetFooter } from './SheetFooter.vue'
export const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
'fixed z-50 gap-4 bg-background shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
{
variants: {
side: {
@@ -21,9 +21,14 @@ export const sheetVariants = cva(
right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
padding: {
none: '',
md: 'p-6'
},
},
defaultVariants: {
side: 'right',
padding: 'md',
},
},
)