From a12de2a2ea59772d3bdeebcc946dfcbd384bebfb Mon Sep 17 00:00:00 2001 From: "Irsyad A. Panjaitan" Date: Sun, 13 Oct 2024 14:49:57 +0700 Subject: [PATCH] new featured: allow resize to the table --- app/sink/page.tsx | 10 +- .../docs/collections/table/table-demo.tsx | 2 +- .../collections/table/table-resize-demo.tsx | 104 +++++++++++++ components/docs/generated/previews.ts | 3 + components/footer.tsx | 3 +- components/ui/table.tsx | 137 +++++++++++++----- .../docs/components/collections/table.mdx | 4 + 7 files changed, 226 insertions(+), 37 deletions(-) create mode 100644 components/docs/collections/table/table-resize-demo.tsx diff --git a/app/sink/page.tsx b/app/sink/page.tsx index 17058fc2..2dd8bbdb 100644 --- a/app/sink/page.tsx +++ b/app/sink/page.tsx @@ -1,5 +1,13 @@ "use client" +import TableDemo from "@/components/docs/collections/table/table-demo" +import TableResizeDemo from "@/components/docs/collections/table/table-resize-demo" + export default function Page() { - return
+ return ( +
+ + +
+ ) } diff --git a/components/docs/collections/table/table-demo.tsx b/components/docs/collections/table/table-demo.tsx index f9834702..fb18e36d 100644 --- a/components/docs/collections/table/table-demo.tsx +++ b/components/docs/collections/table/table-demo.tsx @@ -12,7 +12,7 @@ export default function TableDemo() { - # + # Name Category Price diff --git a/components/docs/collections/table/table-resize-demo.tsx b/components/docs/collections/table/table-resize-demo.tsx new file mode 100644 index 00000000..d5ba09d7 --- /dev/null +++ b/components/docs/collections/table/table-resize-demo.tsx @@ -0,0 +1,104 @@ +"use client" + +import { NumberFormatter } from "@internationalized/number" +import { IconDotsVertical } from "justd-icons" +import { Card, Menu, Table } from "ui" + +export default function TableResizeDemo() { + return ( + +
+ + ID + + Name + + Email + Age + Role + Band + Status + + + {(item) => ( + + {item.id} + {item.name} + {item.email} + {item.age} + {item.role} + {item.band} + {item.status} + + )} + +
+
+ ) +} + +const items = [ + { + id: 1, + name: "Randy Blythe", + email: "randy.blythe@example.com", + age: 52, + role: "Vocalist", + band: "Lamb of God", + status: "Active" + }, + { + id: 2, + name: "Phil Anselmo", + email: "phil.anselmo@example.com", + age: 55, + role: "Vocalist", + band: "Pantera", + status: "Active" + }, + { + id: 3, + name: "George Fisher", + email: "george.fisher@example.com", + age: 53, + role: "Vocalist", + band: "Cannibal Corpse", + status: "Active" + }, + { + id: 4, + name: "Corey Taylor", + email: "corey.taylor@example.com", + age: 50, + role: "Vocalist", + band: "Slipknot", + status: "Active" + }, + { + id: 5, + name: "Trevor Strnad", + email: "trevor.strnad@example.com", + age: 41, + role: "Vocalist", + band: "The Black Dahlia Murder", + status: "Inactive" + }, + { + id: 6, + name: "Chuck Schuldiner", + email: "chuck.schuldiner@example.com", + age: 34, + role: "Vocalist", + band: "Death", + status: "Deceased" + }, + { + id: 7, + name: "Mitch Lucker", + email: "mitch.lucker@example.com", + age: 28, + role: "Vocalist", + band: "Suicide Silence", + status: "Deceased" + } +] diff --git a/components/docs/generated/previews.ts b/components/docs/generated/previews.ts index 7cd098c2..1c2d8b7c 100644 --- a/components/docs/generated/previews.ts +++ b/components/docs/generated/previews.ts @@ -1193,6 +1193,9 @@ export const previews: Record = { "collections/choicebox/choicebox-demo": { component: React.lazy(() => import("@/components/docs/collections/choicebox/choicebox-demo")), }, + "collections/table/table-resize-demo": { + component: React.lazy(() => import("@/components/docs/collections/table/table-resize-demo")), + }, "collections/table/table-demo": { component: React.lazy(() => import("@/components/docs/collections/table/table-demo")), }, diff --git a/components/footer.tsx b/components/footer.tsx index f38e2b01..abcf65f5 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -21,7 +21,8 @@ const navigation = { ], labs: [ { name: "Github", href: "https://github.com/justdlabs" }, - { name: "X / Formerly Twitter", href: "https://x.com/getjustd" } + { name: "X / Formerly Twitter", href: "https://x.com/getjustd" }, + { name: "Templates", href: "https://irsyad.co" } ], legal: [{ name: "MIT", href: "https://github.com/justdlabs/justd/blob/main/LICENSE" }] } diff --git a/components/ui/table.tsx b/components/ui/table.tsx index af1490fd..ae2088eb 100644 --- a/components/ui/table.tsx +++ b/components/ui/table.tsx @@ -1,9 +1,12 @@ "use client" +import React from "react" + import { IconChevronLgDown, IconHamburger } from "justd-icons" import type { CellProps, ColumnProps, + ColumnResizerProps, RowProps, TableBodyProps, TableHeaderProps, @@ -14,6 +17,8 @@ import { Cell, Collection, Column, + ColumnResizer as ColumnResizerPrimitive, + ResizableTableContainer, Row, Table as TablePrimitive, TableBody, @@ -23,34 +28,66 @@ import { import { tv } from "tailwind-variants" import { Checkbox } from "./checkbox" -import { cn } from "./primitive" +import { cn, cr } from "./primitive" import { TouchTarget } from "./touch-target" const table = tv({ slots: { root: "table w-full caption-bottom border-spacing-0 text-sm outline-none", - column: - "whitespace-nowrap allows-sorting:cursor-pointer px-3 py-3 text-left dragging:cursor-grabbing font-medium outline-none [&:has([slot=selection])]:pr-0", header: "border-b x32", row: "tr group relative cursor-default border-b text-fg/70 outline-none ring-primary focus-visible:ring-1 selected:bg-accent-subtle selected:hover:bg-accent-subtle/50 dark:selected:hover:bg-accent-subtle/60", - cell: "whitespace-nowrap group px-3 py-3 outline-none", cellIcon: - "flex-none rounded bg-secondary text-fg [&>[data-slot=icon]]:shrink-0 [&>[data-slot=icon]]:size-3.5 [&>[data-slot=icon]]:transition-transform [&>[data-slot=icon]]:duration-200 size-[1.15rem] grid place-content-center shrink-0" + "flex-none rounded bg-secondary text-fg [&>[data-slot=icon]]:shrink-0 [&>[data-slot=icon]]:size-3.5 [&>[data-slot=icon]]:transition-transform [&>[data-slot=icon]]:duration-200 size-[1.15rem] grid place-content-center shrink-0", + columnResizer: [ + "touch-none absolute [&[data-resizing]>div]:bg-primary right-0 top-0 bottom-0 w-px px-1 grid place-content-center", + "[&[data-resizable-direction=both]]:cursor-ew-resize &[data-resizable-direction=left]:cursor-e-resize &[data-resizable-direction=right]:cursor-w-resize" + ] } }) -const { root, header, column, row, cell, cellIcon } = table() +const { root, header, row, cellIcon, columnResizer } = table() interface TableProps extends TablePrimitiveProps { className?: string + allowResize?: boolean } +const TableContext = React.createContext({ + allowResize: false +}) + +const useTableContext = () => React.useContext(TableContext) + const Table = ({ children, className, ...props }: TableProps) => ( -
- - {children} - -
+ +
+ {props.allowResize ? ( + + + {children} + + + ) : ( + + {children} + + )} +
+
+) + +const ColumnResizer = ({ className, ...props }: ColumnResizerProps) => ( + + columnResizer({ + ...renderProps, + className + }) + )} + > +
+ ) const Body = (props: TableBodyProps) => ( @@ -61,34 +98,66 @@ interface TableCellProps extends CellProps { className?: string } -const TableCell = ({ children, className, ...props }: TableCellProps) => ( - - {children} - -) +const cellStyles = tv({ + base: "whitespace-nowrap group px-3 py-3 outline-none", + variants: { + allowResize: { + true: "overflow-hidden truncate" + } + } +}) +const TableCell = ({ children, className, ...props }: TableCellProps) => { + const { allowResize } = useTableContext() + return ( + + {children} + + ) +} + +const columnStyles = tv({ + base: "whitespace-nowrap relative allows-sorting:cursor-pointer px-3 py-3 text-left dragging:cursor-grabbing font-medium outline-none [&:has([slot=selection])]:pr-0", + variants: { + isResizable: { + true: "overflow-hidden truncate" + } + } +}) interface TableColumnProps extends ColumnProps { className?: string + isResizable?: boolean } -const TableColumn = ({ children, className, ...props }: TableColumnProps) => ( - - {({ allowsSorting, sortDirection, isHovered }) => ( -
- <> - {children} - {allowsSorting && ( - - - - - - )} - -
- )} -
-) +const TableColumn = ({ children, isResizable = false, className, ...props }: TableColumnProps) => { + return ( + + {({ allowsSorting, sortDirection, isHovered }) => ( +
+ <> + {children} + {allowsSorting && ( + + + + + + )} + {isResizable && } + +
+ )} +
+ ) +} interface HeaderProps extends TableHeaderProps { className?: string diff --git a/resources/content/docs/components/collections/table.mdx b/resources/content/docs/components/collections/table.mdx index ba98d212..c595cb93 100644 --- a/resources/content/docs/components/collections/table.mdx +++ b/resources/content/docs/components/collections/table.mdx @@ -36,6 +36,10 @@ Similar to [GridList](/docs/components/collections/grid-list), tables can also s Tables can be made sortable. Activate sorting by clicking on a column header. +## Resizable +Tables can be made resizable. To enable resizing, set the `allowResize` prop to `true`. And to make a column resizable, set the `isResizable` prop to `true` on the column. + + ## Href To create a clickable link within a table row, use the `href` prop.