Skip to content

Commit

Permalink
Data table looks fabulous on mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
aberonni committed Jan 24, 2024
1 parent e2e8f57 commit 391b57a
Show file tree
Hide file tree
Showing 15 changed files with 332 additions and 101 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"superjson": "^2.2.1",
"tailwind-merge": "^2.2.0",
"tailwindcss-animate": "^1.0.7",
"vaul": "^0.8.9",
"zod": "^3.22.4"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/components/LessonPlanList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { ExclamationTriangleIcon } from "@radix-ui/react-icons";

import type { RouterOutputs } from "~/utils/api";
import { type ColumnDef } from "@tanstack/react-table";
import { DataTableColumnHeader } from "./ui/data-table-column-header";
import { DataTable } from "./ui/data-table";
import { DataTable } from "~/components/data-table";
import { DataTableColumnHeader } from "~/components/data-table/data-table-column-header";

const columns: ColumnDef<RouterOutputs["lessonPlan"]["getMyLessonPlans"][0]>[] =
[
Expand Down
9 changes: 5 additions & 4 deletions src/components/ResourceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import type {
} from "@prisma/client";
import Link from "next/link";
import { Badge } from "~/components/ui/badge";
import { DataTable } from "~/components/ui/data-table";
import { DataTableColumnHeader } from "~/components/ui/data-table-column-header";
import { DataTable } from "~/components/data-table";
import { DataTableColumnHeader } from "~/components/data-table/data-table-column-header";
import { cn } from "~/lib/utils";

type CategoriesInResource = { category: Category }[];
Expand Down Expand Up @@ -66,13 +66,13 @@ const columns: ColumnDef<RouterOutputs["resource"]["getAll"][0]>[] = [
<DataTableColumnHeader column={column} title={column.id} />
),
cell: (props) => (
<div className="space-x-2">
<div className="flex flex-wrap items-center gap-x-2 gap-y-1">
{(props.getValue<CategoriesInResource>() ?? [])
.sort(({ category: cA }, { category: cB }) =>
cA.name.localeCompare(cB.name),
)
.map(({ category }) => (
<Badge>{category.name}</Badge>
<Badge key={category.id}>{category.name}</Badge>
))}
</div>
),
Expand Down Expand Up @@ -152,6 +152,7 @@ export const ResourceList = ({
isLoading={isLoading}
useFilters={useFilters}
usePagination={usePagination}
hiddenColumnsOnMobile={["categories", "configuration"]}
/>
);
};
133 changes: 133 additions & 0 deletions src/components/data-table/data-table-filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Cross2Icon, MixerHorizontalIcon } from "@radix-ui/react-icons";
import { type Table } from "@tanstack/react-table";

import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { DataTableFacetedFilter } from "./data-table-faceted-filter";
import { ResourceConfigurationLabels, ResourceTypeLabels } from "../Resource";
import type { ResourceConfiguration, ResourceType } from "@prisma/client";
import { api } from "~/utils/api";
import { useMediaQuery } from "~/hooks/use-media-query";

import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "~/components/ui/drawer";
import { cn } from "~/lib/utils";

interface DataTableFiltersProps<TData> {
table: Table<TData>;
}

function DataTableFiltersContent<TData>({
table,
className,
}: DataTableFiltersProps<TData> & { className?: string }) {
const isFiltered = table.getState().columnFilters.length > 0;
const { data: categories, isLoading: isLoadingCategories } =
api.category.getAll.useQuery();

return (
<div className={cn("flex flex-1 items-center space-x-2", className)}>
<Input
placeholder="Filter Title..."
value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("title")?.setFilterValue(event.target.value)
}
className="h-8 w-[150px] lg:w-[250px]"
/>
{table.getColumn("categories") && (
<DataTableFacetedFilter
column={table.getColumn("categories")}
title="Category"
options={(categories ?? []).map(({ id, name }) => ({
label: name,
value: id,
}))}
disabled={isLoadingCategories}
/>
)}
{table.getColumn("type") && (
<DataTableFacetedFilter
column={table.getColumn("type")}
title="Type"
options={Object.keys(ResourceTypeLabels).map((type) => ({
label: ResourceTypeLabels[type as ResourceType],
value: type,
}))}
/>
)}
{table.getColumn("configuration") && (
<DataTableFacetedFilter
column={table.getColumn("configuration")}
title="Configuration"
options={Object.keys(ResourceConfigurationLabels).map(
(configuration) => ({
label:
ResourceConfigurationLabels[
configuration as ResourceConfiguration
],
value: configuration,
}),
)}
/>
)}
{isFiltered && (
<Button
variant="ghost"
onClick={() => table.resetColumnFilters()}
className="h-8 px-2 lg:px-3"
>
Reset
<Cross2Icon className="ml-2 h-4 w-4" />
</Button>
)}
</div>
);
}

export function DataTableFilters<TData>({
table,
}: DataTableFiltersProps<TData>) {
const isDesktop = useMediaQuery("(min-width: 768px)");

if (isDesktop) {
return <DataTableFiltersContent table={table} />;
}

return (
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline">
<MixerHorizontalIcon className="mr-2 h-4 w-4" /> Filters
</Button>
</DrawerTrigger>
<DrawerContent>
<div className="mx-auto w-full max-w-sm">
<DrawerHeader>
<DrawerTitle>
<MixerHorizontalIcon className="mr-2 inline h-4 w-4" />
Filters
</DrawerTitle>
</DrawerHeader>
<DataTableFiltersContent
table={table}
className="flex-col items-stretch space-x-0 space-y-2 p-4 [&>*]:w-full"
/>
<DrawerFooter className="pt-4">
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</div>
</DrawerContent>
</Drawer>
);
}
File renamed without changes.
20 changes: 20 additions & 0 deletions src/components/data-table/data-table-toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import { type Table } from "@tanstack/react-table";
import { DataTableViewOptions } from "./data-table-view-options";
import { DataTableFilters } from "./data-table-filters";

interface DataTableToolbarProps<TData> {
table: Table<TData>;
}

export function DataTableToolbar<TData>({
table,
}: DataTableToolbarProps<TData>) {
return (
<div className="flex items-center justify-between">
<DataTableFilters table={table} />
<DataTableViewOptions table={table} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
import { MixerHorizontalIcon } from "@radix-ui/react-icons";
import { EyeOpenIcon } from "@radix-ui/react-icons";
import { type Table } from "@tanstack/react-table";

import { Button } from "~/components/ui/button";
Expand All @@ -23,12 +23,8 @@ export function DataTableViewOptions<TData>({
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
>
<MixerHorizontalIcon className="mr-2 h-4 w-4" />
<Button variant="outline" size="sm" className="ml-auto flex h-8">
<EyeOpenIcon className="mr-2 h-4 w-4" />
View
</Button>
</DropdownMenuTrigger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
getFacetedRowModel,
getFacetedUniqueValues,
} from "@tanstack/react-table";
import { useState } from "react";
import { useEffect, useState } from "react";

import {
Table,
Expand All @@ -27,13 +27,15 @@ import {
import { DataTablePagination } from "./data-table-pagination";
import { DataTableToolbar } from "./data-table-toolbar";
import { Skeleton } from "~/components/ui/skeleton";
import { useMediaQuery } from "~/hooks/use-media-query";

interface DataTableProps<TData, TValue = unknown> {
columns: ColumnDef<TData, TValue>[];
data?: TData[];
usePagination?: boolean;
useFilters?: boolean;
isLoading?: boolean;
hiddenColumnsOnMobile?: (keyof VisibilityState)[];
}

export function DataTable<TData, TValue = unknown>({
Expand All @@ -42,11 +44,25 @@ export function DataTable<TData, TValue = unknown>({
usePagination = false,
useFilters = false,
isLoading = false,
hiddenColumnsOnMobile = [],
}: DataTableProps<TData, TValue>) {
const isDesktop = useMediaQuery("(min-width: 768px)");

const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});

useEffect(() => {
setColumnVisibility((prev) => ({
...prev,
...Object.fromEntries(
hiddenColumnsOnMobile
.filter((key) => !prev[key])
.map((key) => [key, isDesktop]),
),
}));
}, [hiddenColumnsOnMobile, isDesktop]);

const table = useReactTable({
data: data ?? [],
columns,
Expand Down
86 changes: 0 additions & 86 deletions src/components/ui/data-table-toolbar.tsx

This file was deleted.

Loading

0 comments on commit 391b57a

Please sign in to comment.