Coder Social home page Coder Social logo

sadmann7 / shadcn-table Goto Github PK

View Code? Open in Web Editor NEW
1.7K 8.0 150.0 1.58 MB

A shadcn table component with server-side sorting, filtering, and pagination.

Home Page: https://table.sadmn.com

License: MIT License

JavaScript 2.02% TypeScript 97.20% CSS 0.78%
drizzle-orm open-source shadcn-ui nextjs tanstack-table

shadcn-table's Introduction

This is a shadcn table component with server-side sorting, filtering, and pagination. It is bootstrapped with create-t3-app.

Shadcn Table

Warning This project is still in development and is not ready for production use.

It uses new technologies (ppr, and drizzle ORM) which are subject to change and may break your application.

Tech Stack

Features

  • Server-side pagination, sorting, and filtering (via useDataTable hook)
  • Customizable columns (dataTable and columns props)
  • Dynamic debounced search filters, and faceted filters (filterFields prop)
  • Dynamic Data-Table-Toolbar with search, filters, and actions
  • Optional Notion like advanced filtering (enableAdvancedFilter prop)
  • Optional Linear like floating bar on row selection (floatingBar prop)

Running Locally

  1. Clone the repository

    git clone https://github.com/sadmann7/shadcn-table
  2. Install dependencies using pnpm

    pnpm install
  3. Copy the .env.example to .env and update the variables.

    cp .env.example .env
  4. Start the development server

    pnpm run dev
  5. Push the database schema

    pnpm run db:push

Build your own Table

  1. Copy the following folders and files into your project (configured with ) at the exact specific locations

    • src/components/data-table
    • src/db/index.ts
    • src/hooks
    • src/lib
    • src/types

    Also install the required shadcn components and other required packages with the following commands:

    pnpm dlx shadcn-ui@latest init
    
    pnpm dlx shadcn-ui@latest add badge button calendar checkbox command dialog dropdown-menu form input label popover select separator skeleton sonner table toggle-group tooltip
    
    pnpm add drizzle-orm postgres @tanstack/react-table zod @t3-oss/env-nextjs
    pnpm add -D drizzle-kit dotenv-cli pg tsx
  2. Configure your Environment Variables Then set up the Database URL, for this example, we're using PlanetScale MySQL2 Database. Our schemas will also be made using this.

  3. Database Actions: For this you can use any ORM of your choice, but for the sake of this particular example, we're using Drizzle ORM and Neon.

    As an example, lets use the tasks table.

    • Create the Table Schema at @/db/schema.ts
    • Create the associated zod validations @/lib/validations/tasks.ts file
  4. Setting up the Table

    • Create Files: Create page.tsx and, if needed, layout.tsx in your app directory.
    • Copy Directories: Copy ./_components and ./_lib directories into your project.
    • Update Queries/Mutations: Modify ./_lib/queries.ts, ./_lib/actions.ts, and ./_lib/client-actions.ts to match your database operations.
    • Update Floating Bar: Update ./_components/tasks-table-floating-bar.tsx to match your table's actions (optional).
    • Define Table Columns: Update ./_components/tasks-table-columns.tsx to define column headers, actions, searchable and filterable columns.
    • Setup Data Fetching: In ./page.tsx, define getTasksPromise, getTaskCountByStatus, and getTaskCountByPriority.
    • Fetch Data: In ./_components/tasks-table.tsx, consume the getTasksPromise promise using the React.use hook. The promise is passed to trigger the suspense boundary.
    • Memoize Table Columns: In ./_components/tasks-table.tsx, memoize columns defined in ./_components/tasks-table-columns.tsx using React.useMemo hook to prevent unnecessary re-renders.
    • Use Data Table Hook: In ./_components/tasks-table.tsx, call ./hooks/useTasksTable.tsx hook to handle server-side pagination, sorting, and filtering.
    • Remove Tasks Table Provider: Make sure to remove the provider from ./components/tasks-table-provider.tsx. The provider is used to showcase some additional features like floating bar and advanced filters.

Codebase Overview

Watch the codebase overview video on YouTube.

Consider subscribing to Kavin Desi Valli's YouTube channel for more videos.

How do I deploy this?

Follow the deployment guides for Vercel, Netlify and Docker for more information.

shadcn-table's People

Contributors

aqib-rime avatar balance8 avatar cesarscfilho avatar dewodt avatar eltociear avatar fellipeutaka avatar kavinvalli avatar mohitkumawat310 avatar noxify avatar renovate[bot] avatar sadmann7 avatar victormicco avatar yuvarajmadineni avatar zbeyens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shadcn-table's Issues

sonner vs. shadcn toast?

shadcn already has a toast component. So why do we use sonner? Seems to be adding an extra dependency

Sticky Header

Hi,
requesting a sticky header feature where the scrolling only happens within the tbody

Page size is not working?

Hello! First of all great contribution to OSS on how to use shadcn with server side pagination, not an easy task.

I was testing the implementation and it seems that when setting different page size from initial (p.e.: 20) is not working?

Thanks a lot

EDIT: I've been testing a little bit more and I found that if you set it on the URL it works, but when changing on the popover don't. Also I have checked that, for example if you are on the second page and then you change the page size from 20 to 10, it does not reload and instead it goes to the 3rd page.

ERR_SSL_WRONG_VERSION_NUMBER

Hi all, i clone your repo and run in my local. I use mysql/mysql-server:8 and node v20.9.0. When i access to path "/", i get an exception:
⨯ Internal error: TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11372:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) Cause: [Error: 00683F3B8E7F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:354: ] { library: 'SSL routines', reason: 'wrong version number', code: 'ERR_SSL_WRONG_VERSION_NUMBER' } ⨯ Internal error: TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11372:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) digest: "3747308182"
Anyone could help me? Thanks so much!

[Bug] useDataTable: Allow schema parameter to override default createdAt sort

Describe the bug

Hello @sadmann7,

The useDataTable is currently always adding "createdAt.desc" to the URL, as it is the default sort in the use-data-table schema constant.

This is an issue, as we might need "updatedAt" or another type of time sorting, or even no time sorting at all, like sorting simply by IDs.

It seems to me that we should make the schema a mandatory parameter for the use-data-table, so users can choose whether to default to "createdAt", somthing else or nothing at all (thus using the ID as it is the default in queries.ts).

Kind regards.

How to reproduce

Link to reproduction

Additional information

No response

floating bar doesn't float or stay at bottom-1

Hi
I was expecting the floating bar to stay at bottom-1 position and always be visiblte on the screen.
Not only it goes all way down when in increase the pageSize, but it doesn't stay fix.

Screenshot 2024-02-26 at 12 22 22 PM

All filters are not always being reset

I noticed in the demo when you select a filter and also use the Title search/filter, once you click on reset; the filter value is not fully reset and still is preset in the URL. Here's a quick screencast top to show you what I am running into.

CleanShot.2023-10-03.at.15.34.17.mp4

By the way, thank you so much @kavinvalli & @sadmann7 for creating this. I found it while watching your livecode247 channel on youtube. It has been very helpful and exactly what I have been looking for. 🙏🏻

[feat]: Relations / references

Feature description

I have a table with userId field, but it is not helpful to display it as is, instead It would be nice to have the ability to easily add columns from the referenced table (user.name):

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").default(sql`current_timestamp`),
})

export const tasks = pgTable("tasks", {
  id: varchar("id", { length: 30 })
    .$defaultFn(() => generateId())
    .primaryKey(),
  // ...
  userId: integer("user_id").notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").default(sql`current_timestamp`),
})

export const tasksRelations = relations(tasks, ({ one }) => ({
  user: one(users, {
    fields: [tasks.userId],
    references: [users.id],
  }),
}));

p.s. I've tried it myself, it did work, but it's very hacky and also type for DataTableFilterField.value had to be changed to string, because the id of my column was user_name as a result of referencing

Additional Context

Also, maybe we can utilize dynamic query building for better types and more reusability:
https://orm.drizzle.team/docs/dynamic-query-building

Before submitting

  • I've made research efforts and searched the documentation
  • I've searched for existing issues and PRs

Selection persists when pagination and/or filters change

If I select the first item in the list, when I move to the next page the first item is still selected. I've fixed this in useDataTable in our project by simply adding this

image

Not sure if this is a good approach (notably if I increase the per-page this won't persist already selected items), and I've notice it still happens when I select filters.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/code-check.yml
  • actions/checkout v4
  • actions/setup-node v4
  • pnpm/action-setup v3.0.0
  • actions/cache v4
  • actions/checkout v4
  • actions/setup-node v4
  • pnpm/action-setup v3.0.0
  • actions/cache v4
  • actions/checkout v4
  • actions/setup-node v4
  • pnpm/action-setup v3.0.0
  • actions/cache v4
npm
package.json
  • @hookform/resolvers ^3.3.4
  • @radix-ui/react-checkbox ^1.0.4
  • @radix-ui/react-dialog ^1.0.5
  • @radix-ui/react-dropdown-menu ^2.0.6
  • @radix-ui/react-icons ^1.3.0
  • @radix-ui/react-label ^2.0.2
  • @radix-ui/react-popover ^1.0.7
  • @radix-ui/react-select ^2.0.0
  • @radix-ui/react-separator ^1.0.3
  • @radix-ui/react-slot ^1.0.2
  • @radix-ui/react-toggle ^1.0.3
  • @radix-ui/react-toggle-group ^1.0.4
  • @radix-ui/react-tooltip ^1.0.7
  • @t3-oss/env-nextjs ^0.10.0
  • @tanstack/react-table ^8.16.0
  • class-variance-authority ^0.7.0
  • clsx ^2.1.0
  • cmdk ^1.0.0
  • date-fns ^3.6.0
  • drizzle-orm ^0.30.8
  • geist ^1.3.0
  • nanoid ^5.0.7
  • next 14.3.0-canary.39
  • next-themes ^0.3.0
  • postgres ^3.4.4
  • react 18.3.1
  • react-day-picker ^8.10.1
  • react-dom 18.3.1
  • react-hook-form ^7.51.3
  • server-only ^0.0.1
  • sonner ^1.4.41
  • tailwind-merge ^2.2.2
  • tailwindcss-animate ^1.0.7
  • zod ^3.22.4
  • @faker-js/faker ^8.4.1
  • @ianvs/prettier-plugin-sort-imports ^4.2.1
  • @total-typescript/ts-reset ^0.5.1
  • @types/eslint ^8.56.9
  • @types/node ^20.12.7
  • @types/react ^18.2.79
  • @types/react-dom ^18.2.25
  • @typescript-eslint/eslint-plugin ^7.7.0
  • @typescript-eslint/parser ^7.7.0
  • autoprefixer ^10.4.19
  • dotenv-cli ^7.4.1
  • drizzle-kit ^0.20.14
  • eslint ^8.57.0
  • eslint-config-next ^14.2.1
  • eslint-config-prettier ^9.1.0
  • eslint-plugin-tailwindcss ^3.15.1
  • pg ^8.11.5
  • postcss ^8.4.38
  • prettier ^3.2.5
  • prettier-plugin-tailwindcss ^0.5.14
  • rimraf ^5.0.5
  • tailwindcss ^3.4.3
  • tsx ^4.7.2
  • typescript ^5.4.5
  • pnpm 8.15.8

  • Check this box to trigger a request for Renovate to run again on this repository

[feat]: filter boolean

Feature description

Could you add a boolean filter ?

Many thanks in advance :)

Additional Context

Actually we can filter by search text, by selectable options, there is no simple boolean filter.

Before submitting

  • I've made research efforts and searched the documentation
  • I've searched for existing issues and PRs

[feat]: export to csv

Feature description

Add the ability to export data to csv

Additional Context

Additional details here...

Before submitting

  • I've made research efforts and searched the documentation
  • I've searched for existing issues and PRs

Page parameter resets back to page 1

Hi, thank you very much for creating this wonderful component.. This is really helpful.

One issue i have though while using it was that the page parameter resets back to page=1 if I reload the browser while I'm in a different page (ex. page=3).
It also reset back to page=1 if I directly visit a url with parameter (ex. page=2, page=3, etc. )

Screen.Recording.2024-02-24.at.8.06.07.PM.mov

Feature Improvements in the DataTable component

I just checked out AG Grid and it looks cool.
What if we could develop this DataTable into a Batteries Included Table which can be used by the most junior developer to the largest teams.
What other features can be implemented ?
I was thinking of some more features

  1. Sort by Multiple Columns (excel like sort options)
  2. Full text search rather than a multiple search bars for multiple search options along with support for advanced querying like Google search
    image
  3. Multiple Data Presentation formats - Text, Charts, Graphs and Plots - Can be used in
  4. Export Table as viewed by the user to Google Sheets or Microsoft Excel

[bug]: enableHiding and enableSorting not correctly reflected inside data-table-column-header

Describe the bug

If inside the xxx-table-column we add enableSorting:true and enableHiding:false to a column definition, the column header keep showing hide option in the dropdown menu.

We can do the following to reflect enableHide:false :

{column.getCanHide() ? (
   <>
      <DropdownMenuSeparator />
       <DropdownMenuItem
          aria-label="Hide column"
          onClick={() => column.toggleVisibility(false)}
       >
          <EyeNoneIcon
             className="mr-2 size-3.5 text-muted-foreground/70"
             aria-hidden="true"
          />
          Hide
       </DropdownMenuItem>
     </>
) : null}

also :
If enableHiding: true but enableSorting:false we loose the sorting dropdown containing the quick access to the hide option.

I propose the following :

  if (!column.getCanSort()  && !column.getCanHide()) {
    return <div className={cn(className)}>{title}</div>
  }
  
  return (
    <div className={cn("flex items-center space-x-2", className)}>
      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <Button
           aria-label={
              column.getCanSort() 
                ? column.getIsSorted() === "desc"
                  ? `Sorted descending. Click to sort ascending.`
                  : column.getIsSorted() === "asc"
                    ? `Sorted ascending. Click to sort descending.`
                    : `Not sorted. Click to sort ascending.`
                : undefined
            }
            variant="ghost"
            size="sm"
            className="-ml-3 h-8 data-[state=open]:bg-accent"
          >
            <span>{title}</span>
            {column.getCanSort() ? (
                column.getIsSorted() === "desc" ? (
                  <RxArrowDown className="ml-2 size-4" aria-hidden="true" />
                ) : column.getIsSorted() === "asc" ? (
                  <RxArrowUp className="ml-2 size-4" aria-hidden="true" />
                ) : (
                  <RxCaretSort className="ml-2 size-4" aria-hidden="true" />
                )
            ) : (
              <RxCaretSort className="ml-2 size-4" aria-hidden="true" />
            )}
          </Button>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="start">
          {column.getCanSort() ? (
            <>
              <DropdownMenuItem
                aria-label="Sort ascending"
                onClick={() => column.toggleSorting(false)}
              >
                <ArrowUpIcon
                  className="mr-2 size-3.5 text-muted-foreground/70"
                  aria-hidden="true"
                />
                Asc
              </DropdownMenuItem>
              <DropdownMenuItem
                aria-label="Sort descending"
                onClick={() => column.toggleSorting(true)}
              >
                <ArrowDownIcon
                  className="mr-2 size-3.5 text-muted-foreground/70"
                  aria-hidden="true"
                />
                Desc
              </DropdownMenuItem>
           </>
          ) : null}
          
          {column.getCanSort() && column.getCanHide() ? (
             <DropdownMenuSeparator />
            )  : null}
          
         {column.getCanHide() ? (
            <DropdownMenuItem
              aria-label="Hide column"
              onClick={() => column.toggleVisibility(false)}
            >
              <EyeNoneIcon
                className="mr-2 size-3.5 text-muted-foreground/70"
                aria-hidden="true"
              />
              Hide
            </DropdownMenuItem>
         )  : null}
         
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  )

Note : we should maybe also add a clearSorting that would go back to the initial sorting ? this one I m not sure if it is necessary.

How to reproduce

add enableSorting: true and enableHiding: false to a column def

Link to reproduction

Additional information

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.