Skip to content

Commit

Permalink
new featured: allow resize to the table
Browse files Browse the repository at this point in the history
  • Loading branch information
irsyadadl committed Oct 13, 2024
1 parent e79c53f commit a12de2a
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 37 deletions.
10 changes: 9 additions & 1 deletion app/sink/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <div />
return (
<div className="p-32 grid gap-10">
<TableResizeDemo />
<TableDemo />
</div>
)
}
2 changes: 1 addition & 1 deletion components/docs/collections/table/table-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function TableDemo() {
<Card>
<Table aria-label="Products">
<Table.Header>
<Table.Column>#</Table.Column>
<Table.Column className="w-0">#</Table.Column>
<Table.Column isRowHeader>Name</Table.Column>
<Table.Column>Category</Table.Column>
<Table.Column>Price</Table.Column>
Expand Down
104 changes: 104 additions & 0 deletions components/docs/collections/table/table-resize-demo.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Card>
<Table allowResize aria-label="Vocalists">
<Table.Header>
<Table.Column className="max-w-0">ID</Table.Column>
<Table.Column isRowHeader isResizable>
Name
</Table.Column>
<Table.Column isResizable>Email</Table.Column>
<Table.Column>Age</Table.Column>
<Table.Column>Role</Table.Column>
<Table.Column isResizable>Band</Table.Column>
<Table.Column>Status</Table.Column>
</Table.Header>
<Table.Body items={items}>
{(item) => (
<Table.Row id={item.id}>
<Table.Cell>{item.id}</Table.Cell>
<Table.Cell>{item.name}</Table.Cell>
<Table.Cell>{item.email}</Table.Cell>
<Table.Cell>{item.age}</Table.Cell>
<Table.Cell>{item.role}</Table.Cell>
<Table.Cell>{item.band}</Table.Cell>
<Table.Cell>{item.status}</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table>
</Card>
)
}

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"
}
]
3 changes: 3 additions & 0 deletions components/docs/generated/previews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,9 @@ export const previews: Record<string, any> = {
"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")),
},
Expand Down
3 changes: 2 additions & 1 deletion components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" }]
}
Expand Down
137 changes: 103 additions & 34 deletions components/ui/table.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"use client"

import React from "react"

import { IconChevronLgDown, IconHamburger } from "justd-icons"
import type {
CellProps,
ColumnProps,
ColumnResizerProps,
RowProps,
TableBodyProps,
TableHeaderProps,
Expand All @@ -14,6 +17,8 @@ import {
Cell,
Collection,
Column,
ColumnResizer as ColumnResizerPrimitive,
ResizableTableContainer,
Row,
Table as TablePrimitive,
TableBody,
Expand All @@ -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<TableProps>({
allowResize: false
})

const useTableContext = () => React.useContext(TableContext)

const Table = ({ children, className, ...props }: TableProps) => (
<div className="relative w-full overflow-auto">
<TablePrimitive {...props} className={root({ className })}>
{children}
</TablePrimitive>
</div>
<TableContext.Provider value={props}>
<div className="relative w-full overflow-auto">
{props.allowResize ? (
<ResizableTableContainer className="overflow-auto">
<TablePrimitive {...props} className={root({ className })}>
{children}
</TablePrimitive>
</ResizableTableContainer>
) : (
<TablePrimitive {...props} className={root({ className })}>
{children}
</TablePrimitive>
)}
</div>
</TableContext.Provider>
)

const ColumnResizer = ({ className, ...props }: ColumnResizerProps) => (
<ColumnResizerPrimitive
{...props}
className={cr(className, (className, renderProps) =>
columnResizer({
...renderProps,
className
})
)}
>
<div className="bg-border h-full w-px py-3" />
</ColumnResizerPrimitive>
)

const Body = <T extends object>(props: TableBodyProps<T>) => (
Expand All @@ -61,34 +98,66 @@ interface TableCellProps extends CellProps {
className?: string
}

const TableCell = ({ children, className, ...props }: TableCellProps) => (
<Cell {...props} className={cell({ className })}>
{children}
</Cell>
)
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 (
<Cell {...props} className={cellStyles({ allowResize, className })}>
{children}
</Cell>
)
}

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) => (
<Column {...props} className={column({ className })}>
{({ allowsSorting, sortDirection, isHovered }) => (
<div className="flex [&_[data-slot=icon]]:shrink-0 items-center gap-2">
<>
{children}
{allowsSorting && (
<TouchTarget>
<span className={cellIcon({ className: isHovered ? "bg-secondary-fg/10" : "" })}>
<IconChevronLgDown className={sortDirection === "ascending" ? "rotate-180" : ""} />
</span>
</TouchTarget>
)}
</>
</div>
)}
</Column>
)
const TableColumn = ({ children, isResizable = false, className, ...props }: TableColumnProps) => {
return (
<Column
{...props}
className={columnStyles({
isResizable,
className
})}
>
{({ allowsSorting, sortDirection, isHovered }) => (
<div className="flex [&_[data-slot=icon]]:shrink-0 items-center gap-2">
<>
{children}
{allowsSorting && (
<TouchTarget>
<span className={cellIcon({ className: isHovered ? "bg-secondary-fg/10" : "" })}>
<IconChevronLgDown
className={sortDirection === "ascending" ? "rotate-180" : ""}
/>
</span>
</TouchTarget>
)}
{isResizable && <ColumnResizer />}
</>
</div>
)}
</Column>
)
}

interface HeaderProps<T extends object> extends TableHeaderProps<T> {
className?: string
Expand Down
4 changes: 4 additions & 0 deletions resources/content/docs/components/collections/table.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<How isCenter={false} withNoPadding copyButton={false} toUse="collections/table/table-sorting-demo" />

## 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.
<How isCenter={false} withNoPadding copyButton={false} toUse="collections/table/table-resize-demo" />

## Href
To create a clickable link within a table row, use the `href` prop.

Expand Down

0 comments on commit a12de2a

Please sign in to comment.