diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index cea8fb0..b4c99ff 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -1,30 +1,100 @@ import "./table.css"; -import { Search } from "lucide-react"; +import { ArrowDownUp, Search } from "lucide-react"; import { Row, RowProps } from "./Row"; -import { Fragment, ReactElement } from "react"; +import { Fragment, ReactElement, useState } from "react"; +import { classnames } from "../utils/classnames"; + +type State = "asc" | "desc" | ""; type Props = { /** * List of header names + * Can be a string array ["id", "actions"] + * Or a tuple containing the sort function with the column + * index in argument */ - headers: string[]; + headers: string[] | [string, ((state: State) => void)?][]; + + /** + * Default: -1 + */ + defaultSortIndex?: number; className?: string; rows: ReactElement[]; }; -export function Table({ headers, rows, className = "" }: Props) { +const nextState = (state: "asc" | "desc" | "") => { + switch (state) { + case "": + return "desc"; + case "asc": + return ""; + case "desc": + return "asc"; + } +}; + +export function Table({ + headers, + rows, + defaultSortIndex = -1, + className = "", +}: Props) { + const [sortSelected, setSortSelected] = useState([defaultSortIndex, "asc"]); + + const onFilterSelected = (col: number) => { + const [currentCol, currentState] = sortSelected; + + if (col !== currentCol) { + setSortSelected([col, "desc"]); + return; + } + + const nxt = nextState(currentState as State); + + if (nxt === "") { + setSortSelected([-1, ""]); + } else { + setSortSelected([col, nxt]); + } + }; + return (
- {headers.map((col) => ( - - ))} + {headers.map((col, index) => { + const [name, sort] = Array.isArray(col) ? col : [col]; + const state = index === sortSelected[0] ? sortSelected[1] : ""; + const nxt = nextState(state as State); + + return ( + + ); + })} diff --git a/src/components/Table/table.css b/src/components/Table/table.css index 493d149..e1e383c 100644 --- a/src/components/Table/table.css +++ b/src/components/Table/table.css @@ -22,6 +22,7 @@ padding: 2rem; border-radius: var(--codex-border-radius); overflow-x: auto; + overflow-y: hidden; } .table-theadTr { @@ -36,3 +37,34 @@ text-align: left; padding: 1rem; } + +.table-theadTh--clickable { + cursor: pointer; +} + +.table-theadTh-content { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.table-theadTh-content svg { + position: relative; + top: -2px; + cursor: pointer; +} + +.table-theadTh-content svg path { + opacity: 0.5; + transition: opacity 0.35s; +} + +.table-theadTh-icon--desc path:nth-child(1), +.table-theadTh-icon--desc path:nth-child(2) { + opacity: 1; +} + +.table-theadTh-icon--asc path:nth-child(3), +.table-theadTh-icon--asc path:nth-child(4) { + opacity: 1; +} diff --git a/stories/Table.stories.tsx b/stories/Table.stories.tsx index 5d84236..c51ed69 100644 --- a/stories/Table.stories.tsx +++ b/stories/Table.stories.tsx @@ -50,6 +50,31 @@ export const Scroll: Story = { }, }; +export const Sort: Story = { + args: { + className: "tableSmall", + rows: [ + Ox45678FDGHJKLBSA22, + My file, + 1, + Some data, + ]} + >, + Ox45678FDGHJKLBSA23, + My file, + 2, + Some data, + ]} + >, + ], + headers: [["id"], ["title"], ["other", () => {}], ["actions"]], + }, +}; + export const Empty: Story = { args: { rows: [],
- {col} - { + onFilterSelected(index); + sort?.(nxt); + }} + > +
+ {name} + {sort && ( + + )} +
+