Skip to content

Commit 46fbb83

Browse files
committed
feat: add sorting menu to table
1 parent 82662af commit 46fbb83

File tree

5 files changed

+300
-188
lines changed

5 files changed

+300
-188
lines changed

packages/ui-react/src/components/menu/menu.stories.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Meta, Preview } from "@storybook/react";
2+
import { InfoIcon } from "lucide-react";
23
import { Button } from "../button/button.js";
34
import * as Menu from "./menu.js";
45

@@ -46,7 +47,9 @@ export const Nested = {
4647
<Menu.Separator />
4748

4849
<Menu.SubmenuRoot>
49-
<Menu.SubmenuTrigger render={<Menu.Item>Submenu</Menu.Item>} />
50+
<Menu.SubmenuTrigger
51+
render={<Menu.Item icon={<InfoIcon />}>Submenu</Menu.Item>}
52+
/>
5053
<Menu.Positioner>
5154
<Menu.Item>Option 1</Menu.Item>
5255
<Menu.Item>Option 2</Menu.Item>

packages/ui-react/src/components/menu/menu.tsx

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -129,37 +129,52 @@ const GroupLabel = ({
129129
/>
130130
);
131131

132-
const Item = ({ className, children, ...props }: MenuPrimitive.Item.Props) => (
133-
<MenuPrimitive.Item
134-
className={cn(
135-
`
136-
relative flex h-8 select-none items-center justify-between rounded-sm
137-
px-4 leading-none
138-
139-
after:absolute
140-
after:inset-y-0
141-
after:inset-x-1.5
142-
after:z-[-1]
143-
data-[highlighted]:after:bg-blue-100
144-
data-[highlighted]:after:rounded-sm
145-
146-
data-[highlighted]:text-blue
147-
data-[highlighted]:outline-none
132+
const Item = ({
133+
className,
134+
children,
135+
...props
136+
}: MenuPrimitive.Item.Props & {
137+
icon?: React.ReactNode;
138+
}) => {
139+
const { icon, ...otherProps } = props;
148140

149-
data-[disabled]:pointer-events-none
150-
data-[disabled]:opacity-50
141+
return (
142+
<MenuPrimitive.Item
143+
className={cn(
144+
`
145+
relative h-8 select-none items-center rounded-sm
146+
px-4 leading-none
147+
148+
grid grid-cols-[1rem_auto_1rem] gap-2
149+
150+
after:absolute
151+
after:inset-y-0
152+
after:inset-x-1.5
153+
after:z-[-1]
154+
data-[highlighted]:after:bg-blue-100
155+
data-[highlighted]:after:rounded-sm
156+
157+
data-[highlighted]:text-blue
158+
data-[highlighted]:outline-none
159+
160+
data-[disabled]:pointer-events-none
161+
data-[disabled]:opacity-50
162+
163+
group/item
164+
`,
165+
className,
166+
)}
167+
{...otherProps}
168+
>
169+
{icon && <span className="flex items-center justify-center">{icon}</span>}
151170

152-
group/item
153-
`,
154-
className,
155-
)}
156-
{...props}
157-
>
158-
{children}
171+
{/* This 2px padding is there to fix some issues with the font not being correctly centered. */}
172+
<span className="col-start-2 pb-[2px]">{children}</span>
159173

160-
<ChevronRightIcon className="size-4 hidden group-aria-[haspopup]/item:block" />
161-
</MenuPrimitive.Item>
162-
);
174+
<ChevronRightIcon className="size-4 hidden group-aria-[haspopup]/item:block" />
175+
</MenuPrimitive.Item>
176+
);
177+
};
163178

164179
const Separator = ({ className, ...props }: MenuPrimitive.Separator.Props) => (
165180
<MenuPrimitive.Separator
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
createColumnHelper,
3+
getCoreRowModel,
4+
getFilteredRowModel,
5+
getSortedRowModel,
6+
useReactTable,
7+
} from "@tanstack/react-table";
8+
import { useState } from "react";
9+
import { Table } from "./table.js";
10+
11+
type Attendee = {
12+
firstName: string;
13+
lastName: string;
14+
role: "scout" | "leader" | "volunteer";
15+
};
16+
17+
const defaultData: Attendee[] = [
18+
{ firstName: "Alice", lastName: "Smith", role: "scout" },
19+
{ firstName: "Bob", lastName: "Johnson", role: "leader" },
20+
{ firstName: "Charlie", lastName: "Brown", role: "volunteer" },
21+
];
22+
23+
const columnHelper = createColumnHelper<Attendee>();
24+
25+
const columns = [
26+
columnHelper.accessor("firstName", {
27+
header: () => "Förnamn",
28+
}),
29+
columnHelper.accessor("lastName", {
30+
header: () => "Efternamn",
31+
enableSorting: false,
32+
}),
33+
columnHelper.accessor("role", {
34+
header: () => "Roll",
35+
cell: (info) => {
36+
const role = info.getValue();
37+
return role.charAt(0).toUpperCase() + role.slice(1);
38+
},
39+
}),
40+
];
41+
42+
function TableTest() {
43+
const [data, _setData] = useState(() => [...defaultData]);
44+
45+
const table = useReactTable({
46+
data,
47+
columns,
48+
columnResizeMode: "onChange",
49+
getCoreRowModel: getCoreRowModel(),
50+
getFilteredRowModel: getFilteredRowModel(),
51+
getSortedRowModel: getSortedRowModel(),
52+
});
53+
54+
return (
55+
<>
56+
<pre className="mb-4 min-h-48">
57+
{JSON.stringify(
58+
{
59+
// columnSizing: table.getState().columnSizing,
60+
// columnSizeVars,
61+
},
62+
null,
63+
2,
64+
)}
65+
</pre>
66+
<Table table={table} className="w-full" />
67+
</>
68+
);
69+
}
70+
71+
export { TableTest };
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type { Meta, Preview } from "@storybook/react";
2-
import { TableTest } from "./table.js";
2+
import { Table } from "./table.js";
3+
import { TableTest } from "./table-test.js";
34

45
export default {
56
title: "Components/Table",
6-
component: TableTest,
7+
component: Table,
78
} satisfies Meta;
89

9-
export const Simple = {} satisfies Preview;
10+
export const Simple = {
11+
render: () => <TableTest />,
12+
} satisfies Preview;

0 commit comments

Comments
 (0)