Skip to content

Commit

Permalink
Finished list component
Browse files Browse the repository at this point in the history
- Supports normal, infinite, and pagination modes
  • Loading branch information
kirankunigiri committed Dec 3, 2024
1 parent a9c0df3 commit cef4c5d
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 84 deletions.
11 changes: 6 additions & 5 deletions example-client/src/form/lib/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import { Link } from '@tanstack/react-router';

import ListSkeleton from '~client/form/lib/skeleton';
import type { RouteFullPaths } from '~client/routes/-sidebar';
import type { SearchParams } from '~client/utils/utils';
import { ZSList, type ZSListProps } from '~zenstack-ui/list/list';

interface ListWrapperProps<T> extends ZSListProps<T> {
type ListWrapperProps<T> = ZSListProps<T> & {
route: RouteFullPaths
itemId?: number | string
search?: string
}
search?: SearchParams
};

function ListWrapper<T extends Record<string, any>>({ itemId, search, route, ...zsListProps }: ListWrapperProps<T>) {
function List<T extends Record<string, any>>({ itemId, search, route, ...zsListProps }: ListWrapperProps<T>) {
return (
<ScrollArea className="list-scrollarea">
<ZSList<T>
Expand Down Expand Up @@ -40,4 +41,4 @@ function ListWrapper<T extends Record<string, any>>({ itemId, search, route, ...
);
}

export default ListWrapper;
export default List;
7 changes: 7 additions & 0 deletions example-client/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,10 @@ div:has(> .tsqd-open-btn) { @apply hidden; }
@apply bg-bd-light;
}
}

/* Center mantine pagination controls */
.mantine-Pagination-root {
.mantine-Group-root {
@apply justify-center;
}
}
12 changes: 5 additions & 7 deletions example-client/src/routes/items/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { ScrollArea, TextInput } from '@mantine/core';
import { createFileRoute, Link, Outlet } from '@tanstack/react-router';
import { TextInput } from '@mantine/core';
import { createFileRoute, Outlet } from '@tanstack/react-router';

import { modelNames } from '~client/form/form-config';
import ListWrapper from '~client/form/lib/list';
import List from '~client/form/lib/list';
import { ListHeader } from '~client/form/lib/list-header';
import ListSkeleton from '~client/form/lib/skeleton';
import { validateSearch } from '~client/utils/utils';
import type { Prisma } from '~zenstack/models';
import { ZSList } from '~zenstack-ui/list/list';

export const Route = createFileRoute('/items')({
component: ItemsLayout,
Expand Down Expand Up @@ -46,12 +44,12 @@ function ItemsLayout() {
</div>

{/* List */}
<ListWrapper<ItemPayload>
<List<ItemPayload>
model={modelNames.item}
query={itemQuery}
route="/items/$id"
itemId={itemId}
search={search.search}
search={search}
render={item => (
<>
<p className="text-sm">{item.name}</p>
Expand Down
153 changes: 103 additions & 50 deletions example-client/src/routes/people/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Button, ScrollArea, TextInput } from '@mantine/core';
import { createFileRoute, Link, Outlet } from '@tanstack/react-router';
import { FloatingIndicator, Pagination, TextInput, UnstyledButton } from '@mantine/core';
import { createFileRoute, Outlet } from '@tanstack/react-router';
import { useEffect, useState } from 'react';

import { modelNames } from '~client/form/form-config';
import ListWrapper from '~client/form/lib/list';
import List from '~client/form/lib/list';
import { ListHeader } from '~client/form/lib/list-header';
import ListSkeleton from '~client/form/lib/skeleton';
import { validateSearch } from '~client/utils/utils';
import { useCreateManyPerson } from '~zenstack/hooks';
import { type Prisma } from '~zenstack/models';
import { ZSList } from '~zenstack-ui/list/list';
import { useZSPagination } from '~zenstack-ui/list/list';

export const Route = createFileRoute('/people')({
component: PeopleLayout,
Expand All @@ -21,14 +20,34 @@ function PeopleLayout() {
const search = Route.useSearch();
const navigate = Route.useNavigate();

const createManyPerson = useCreateManyPerson();

// Person query - search filter
const personQuery = {
include: {},
include: { },
where: { name: { contains: search.search, mode: 'insensitive' } },
} satisfies Prisma.PersonFindManyArgs;
type PersonPayload = Prisma.PersonGetPayload<typeof personQuery>;

// Pagination
const pagination = useZSPagination({
model: modelNames.person,
pageSize: 10,
query: personQuery,
});
useEffect(() => {
// Reset pagination when search changes
pagination.goToPage(1);
}, [search.search]);

// List Mode Controller
const modes = ['normal', 'infinite', 'paginated'] as const;
const [listMode, setListMode] = useState<'normal' | 'infinite' | 'paginated'>('normal');
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const setControlRef = (mode: string) => (node: HTMLButtonElement | null) => {
controlsRefs[mode] = node;
setControlsRefs(controlsRefs);
};

return (
<div className="page">
{/* List View */}
Expand All @@ -43,52 +62,86 @@ function PeopleLayout() {
placeholder="Search"
value={search.search || ''}
onChange={e => navigate({ search: { search: e.target.value } })}
className="mb-4"
className="mb-2"
/>

{/* Generator */}
<Button
className="min-h-8"
onClick={() => {
const randomNames = [
'Liam', 'Emma', 'Noah', 'Olivia', 'Ethan', 'Ava', 'Mason', 'Sophia', 'Lucas', 'Isabella',
'Oliver', 'Mia', 'Elijah', 'Charlotte', 'William', 'Amelia', 'James', 'Harper', 'Benjamin', 'Evelyn',
'Henry', 'Abigail', 'Alexander', 'Emily', 'Sebastian', 'Elizabeth', 'Jack', 'Sofia', 'Daniel', 'Avery',
'Michael', 'Ella', 'Samuel', 'Scarlett', 'David', 'Victoria', 'Joseph', 'Madison', 'Carter', 'Luna',
'Owen', 'Grace', 'Wyatt', 'Chloe', 'John', 'Penelope', 'Luke', 'Layla', 'Gabriel', 'Riley',
'Anthony', 'Zoey', 'Isaac', 'Nora', 'Grayson', 'Lily', 'Julian', 'Eleanor', 'Matthew', 'Hannah',
'Leo', 'Lillian', 'Nathan', 'Addison', 'Thomas', 'Aubrey', 'Caleb', 'Ellie', 'Josh', 'Stella',
'Ryan', 'Natalie', 'Adrian', 'Zoe', 'Adam', 'Leah', 'Ian', 'Hazel', 'Eric', 'Violet',
'Wesley', 'Aurora', 'Austin', 'Savannah', 'Jordan', 'Audrey', 'Colin', 'Brooklyn', 'Blake', 'Bella',
'Steven', 'Claire', 'Miles', 'Skylar', 'Robert', 'Lucy', 'Roman', 'Paisley', 'Carson', 'Everly',
'Cooper', 'Anna', 'Kyle', 'Caroline', 'Parker', 'Nova', 'Marcus', 'Genesis', 'Vincent', 'Emilia',
];

createManyPerson.mutateAsync({
skipDuplicates: true,
data: randomNames.map(name => ({
name,
roomId: 29,
})),
});
}}
>Generate 100 Names
</Button>
{/* List Mode Control */}
<div className="relative mb-2 flex rounded border border-bd-light p-1" ref={setRootRef}>
<FloatingIndicator
target={controlsRefs[listMode]}
parent={rootRef}
className="rounded bg-bd-light shadow-sm"
/>
{modes.map(mode => (
<UnstyledButton
variant="unstyled"
key={mode}
className="z-10 flex-1 py-1 text-center text-sm capitalize"
ref={setControlRef(mode)}
onClick={() => setListMode(mode)}
>
{mode}
</UnstyledButton>
))}
</div>
</div>

{/* List */}
<ListWrapper<PersonPayload>
model={modelNames.person}
query={personQuery}
route="/people/$id"
itemId={name}
search={search.search}
render={person => (
<>
{/* List - Normal */}
{listMode === 'normal' && (
<List<PersonPayload>
mode="normal"
model={modelNames.person}
query={personQuery}
route="/people/$id"
itemId={name}
search={search}
render={person => (
<p className="text-sm">{person.name}</p>
</>
)}
/>
)}
/>
)}

{/* List - Infinite */}
{listMode === 'infinite' && (
<List<PersonPayload>
mode="infinite"
pageSize={10}
model={modelNames.person}
query={personQuery}
route="/people/$id"
itemId={name}
search={search}
render={person => (
<p className="text-sm">{person.name}</p>
)}
/>
)}

{/* List - Paginated */}
{listMode === 'paginated' && (
<>
<List<PersonPayload>
mode="paginated"
pagination={pagination}
route="/people/$id"
itemId={name}
search={search}
render={person => (
<p className="text-sm">{person.name}</p>
)}
/>
<div className="list-margin items-center justify-center py-2">
<Pagination
siblings={1}
withEdges
boundaries={1}
value={pagination.page}
total={pagination.totalPages}
onChange={page => pagination.goToPage(page)}
/>
</div>
</>
)}

</div>

Expand Down
26 changes: 11 additions & 15 deletions example-client/src/routes/rooms/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { createFileRoute, Link, Outlet } from '@tanstack/react-router';
import { createFileRoute, Outlet } from '@tanstack/react-router';

import { meta, modelNames } from '~client/form/form-config';
import List from '~client/form/lib/list';
import { ListHeader } from '~client/form/lib/list-header';
import ListSkeleton from '~client/form/lib/skeleton';
import { trpc } from '~client/main';
import { CustomRoomCreateSchema } from '~server/schemas';
import { type HouseRoom } from '~zenstack/models';
import { ZSList } from '~zenstack-ui/list/list';

export const Route = createFileRoute('/rooms')({
component: RoomsLayout,
Expand All @@ -30,21 +29,18 @@ function RoomsLayout() {
{/* List View */}
<div className="left-list">

<ListHeader title="Rooms" model={modelNames.houseRoom} schemaOverride={CustomRoomCreateSchema} overrideSubmit={createRoom.mutateAsync} metadataOverride={metadataOverride} />
<div className="list-margin">
{/* Header */}
<ListHeader title="Rooms" model={modelNames.houseRoom} schemaOverride={CustomRoomCreateSchema} overrideSubmit={createRoom.mutateAsync} metadataOverride={metadataOverride} />
</div>

<ZSList<HouseRoom>
<List<HouseRoom>
mode="normal"
model={modelNames.houseRoom}
skeleton={<ListSkeleton />}
route="/rooms/$id"
itemId={id}
render={room => (
<Link
key={room.id}
to="/rooms/$id"
params={{ id: room.id.toString() }}
className="list-item"
data-selected={room.id === id}
>
<p className="text-sm">{room.name}</p>
</Link>
<p className="text-sm">{room.name}</p>
)}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion package/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zenstack-ui",
"description": "Customizable react components for zenstack (forms/lists/etc.)",
"version": "0.0.19",
"version": "0.0.20",
"repository": {
"type": "git",
"url": "https://github.com/kirankunigiri/zenstack-ui",
Expand Down
Loading

0 comments on commit cef4c5d

Please sign in to comment.