Files
crowd-funder-for-time-pwa/src/test/EntityGridFunctionPropTest.vue
Jose Olarte III d32cca4f53 feat(EntityGrid): implement infinite scroll for entity lists
Add infinite scroll functionality to EntityGrid component using VueUse's
useInfiniteScroll composable to handle large volumes of entities efficiently.

Changes:
- Integrate @vueuse/core useInfiniteScroll composable
- Add infinite scroll state management (displayedCount, reset function)
- Configure initial batch size (20 items) and increment size (20 items)
- Update displayedEntities, alphabeticalContacts to support progressive loading
- Add canLoadMore() logic for people, projects, and search modes
- Reset scroll state when search term or entities prop changes
- Remove maxItems prop (replaced by infinite scroll)
- Simplify displayEntitiesFunction signature (removed maxItems parameter)
- Update EntitySelectionStep and test files to remove max-items prop

Technical details:
- Uses template ref (scrollContainer) to access scrollable container
- Recent contacts (3) count toward initial batch for people grid
- Special entities (You, Unnamed) always displayed, don't count toward limits
- Infinite scroll works for both entity types and search results
- Constants are configurable at top of component (INITIAL_BATCH_SIZE, INCREMENT_SIZE)

This improves performance and UX when displaying large lists of contacts or
projects by loading content progressively as users scroll.
2025-11-03 21:47:25 +08:00

219 lines
5.4 KiB
Vue

<template>
<div>
<h2>EntityGrid Function Prop Test</h2>
<div class="mb-4">
<button @click="toggleCustomFunction">
{{ useCustomFunction ? "Use Default" : "Use Custom Function" }}
</button>
<span class="ml-2"
>Current: {{ useCustomFunction ? "Custom" : "Default" }}</span
>
</div>
<div class="mb-4">
<h3>
People Grid ({{ people.length }} total,
{{ displayedPeopleCount }} shown)
</h3>
<EntityGrid
entity-type="people"
:entities="people"
:active-did="activeDid"
:all-my-dids="allMyDids"
:all-contacts="people"
:conflict-checker="conflictChecker"
:display-entities-function="
useCustomFunction ? customPeopleFunction : undefined
"
@entity-selected="handleEntitySelected"
/>
</div>
<div class="mb-4">
<h3>
Projects Grid ({{ projects.length }} total,
{{ displayedProjectsCount }} shown)
</h3>
<EntityGrid
entity-type="projects"
:entities="projects"
:active-did="activeDid"
:all-my-dids="allMyDids"
:all-contacts="people"
:conflict-checker="conflictChecker"
:display-entities-function="
useCustomFunction ? customProjectsFunction : undefined
"
@entity-selected="handleEntitySelected"
/>
</div>
<div class="mt-4">
<h3>Selected Entity:</h3>
<pre>{{ selectedEntity }}</pre>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import EntityGrid from "../components/EntityGrid.vue";
import { Contact } from "../db/tables/contacts";
import { PlanData } from "../interfaces/records";
/**
* Test component to demonstrate EntityGrid's new displayEntitiesFunction prop
*
* Shows how parent components can control which entities are displayed
* through function props instead of relying on computed properties.
*/
@Component({
components: {
EntityGrid,
},
})
export default class EntityGridFunctionPropTest extends Vue {
useCustomFunction = false;
selectedEntity: {
type: "person" | "project" | "special";
entityType?: string;
data: Contact | PlanData | { did?: string; name: string };
} | null = null;
// Test data
activeDid = "did:example:123";
allMyDids = ["did:example:123"];
people: Contact[] = [
{
did: "did:example:1",
name: "Alice",
profileImageUrl: "https://example.com/alice.jpg",
},
{ did: "did:example:2", name: "Bob", profileImageUrl: "" },
{
did: "did:example:3",
name: "Charlie",
profileImageUrl: "https://example.com/charlie.jpg",
},
{ did: "did:example:4", name: "Diana", profileImageUrl: "" },
{
did: "did:example:5",
name: "Eve",
profileImageUrl: "https://example.com/eve.jpg",
},
{
did: "did:example:6",
name: "Frank",
profileImageUrl: "https://example.com/frank.jpg",
},
{ did: "did:example:7", name: "Grace", profileImageUrl: "" },
];
projects: PlanData[] = [
{
handleId: "proj1",
name: "Zebra Project",
description: "A project about zebras",
issuerDid: "did:example:1",
},
{
handleId: "proj2",
name: "Alpha Project",
description: "The first project",
issuerDid: "did:example:2",
},
{
handleId: "proj3",
name: "Beta Project",
description: "The second project",
issuerDid: "did:example:3",
},
{
handleId: "proj4",
name: "Gamma Project",
description: "The third project",
issuerDid: "did:example:4",
},
{
handleId: "proj5",
name: "Delta Project",
description: "The fourth project",
issuerDid: "did:example:5",
},
];
/**
* Custom function for people: only show those with profile images
*/
customPeopleFunction = (
entities: Contact[],
_entityType: string,
): Contact[] => {
return entities.filter((person) => person.profileImageUrl);
};
/**
* Custom function for projects: sort by name and limit to 3
*/
customProjectsFunction = (
entities: PlanData[],
_entityType: string,
): PlanData[] => {
return entities.sort((a, b) => a.name.localeCompare(b.name)).slice(0, 3);
};
/**
* Simple conflict checker for testing
*/
conflictChecker = (did: string): boolean => {
return did === this.activeDid;
};
/**
* Toggle between custom and default display functions
*/
toggleCustomFunction(): void {
this.useCustomFunction = !this.useCustomFunction;
}
/**
* Handle entity selection
*/
handleEntitySelected(event: {
type: "person" | "project" | "special";
entityType?: string;
data: Contact | PlanData | { did?: string; name: string };
}): void {
this.selectedEntity = event;
}
/**
* Computed properties to show display counts
*/
get displayedPeopleCount(): number {
if (this.useCustomFunction) {
return this.customPeopleFunction(this.people, "people").length;
}
return Math.min(10, this.people.length); // Initial batch size for infinite scroll
}
get displayedProjectsCount(): number {
if (this.useCustomFunction) {
return this.customProjectsFunction(this.projects, "projects").length;
}
return Math.min(10, this.projects.length); // Initial batch size for infinite scroll
}
}
</script>
<style scoped>
pre {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
font-size: 12px;
}
</style>