6 Commits

24 changed files with 2020 additions and 4239 deletions

View File

@@ -7,6 +7,7 @@ const config: StorybookConfig = {
"@storybook/addon-essentials", "@storybook/addon-essentials",
"@storybook/addon-onboarding", "@storybook/addon-onboarding",
"@storybook/addon-interactions", "@storybook/addon-interactions",
"@storybook/addon-styling-webpack"
], ],
framework: { framework: {
name: "@storybook/react-vite", name: "@storybook/react-vite",

View File

@@ -1,4 +1,5 @@
import type { Preview } from "@storybook/react"; import type { Preview } from "@storybook/react";
import "../app/global.css";
const preview: Preview = { const preview: Preview = {
parameters: { parameters: {

File diff suppressed because it is too large Load Diff

View File

@@ -12,9 +12,15 @@
"build-storybook": "storybook build" "build-storybook": "storybook build"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@tanstack/react-table": "^8.10.7",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"date-fns": "^2.30.0",
"lucide-react": "^0.292.0", "lucide-react": "^0.292.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@@ -26,6 +32,7 @@
"@storybook/addon-interactions": "7.5.3", "@storybook/addon-interactions": "7.5.3",
"@storybook/addon-links": "7.5.3", "@storybook/addon-links": "7.5.3",
"@storybook/addon-onboarding": "1.0.8", "@storybook/addon-onboarding": "1.0.8",
"@storybook/addon-styling-webpack": "0.0.5",
"@storybook/blocks": "7.5.3", "@storybook/blocks": "7.5.3",
"@storybook/react": "7.5.3", "@storybook/react": "7.5.3",
"@storybook/react-vite": "7.5.3", "@storybook/react-vite": "7.5.3",

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon/cog">
<path id="Icon" fill-rule="evenodd" clip-rule="evenodd" d="M9.19148 2.53673C8.88826 1.28772 7.11194 1.28772 6.80872 2.53673C6.61285 3.34357 5.68846 3.72646 4.97943 3.29445C3.88184 2.62567 2.6258 3.88172 3.29457 4.97931C3.72659 5.68833 3.34369 6.61272 2.53685 6.8086C1.28785 7.11182 1.28785 8.88813 2.53685 9.19135C3.34369 9.38723 3.72659 10.3116 3.29457 11.0206C2.6258 12.1182 3.88184 13.3743 4.97943 12.7055C5.68846 12.2735 6.61285 12.6564 6.80872 13.4632C7.11194 14.7122 8.88826 14.7122 9.19148 13.4632C9.38735 12.6564 10.3117 12.2735 11.0208 12.7055C12.1184 13.3743 13.3744 12.1182 12.7056 11.0206C12.2736 10.3116 12.6565 9.38723 13.4633 9.19135C14.7123 8.88813 14.7123 7.11182 13.4633 6.8086C12.6565 6.61272 12.2736 5.68833 12.7056 4.97931C13.3744 3.88172 12.1184 2.62568 11.0208 3.29445C10.3117 3.72646 9.38735 3.34357 9.19148 2.53673ZM8.0001 10.4C9.32558 10.4 10.4001 9.32546 10.4001 7.99998C10.4001 6.67449 9.32558 5.59998 8.0001 5.59998C6.67461 5.59998 5.6001 6.67449 5.6001 7.99998C5.6001 9.32546 6.67461 10.4 8.0001 10.4Z" fill="#637381"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon/dots-vertical">
<g id="Icon">
<path d="M7.9999 4.79998C7.11625 4.79998 6.3999 4.08363 6.3999 3.19998C6.3999 2.31632 7.11625 1.59998 7.9999 1.59998C8.88356 1.59998 9.5999 2.31632 9.5999 3.19998C9.5999 4.08363 8.88356 4.79998 7.9999 4.79998Z" fill="#424242"/>
<path d="M7.9999 9.59998C7.11625 9.59998 6.3999 8.88363 6.3999 7.99998C6.3999 7.11632 7.11625 6.39998 7.9999 6.39998C8.88356 6.39998 9.5999 7.11632 9.5999 7.99998C9.5999 8.88363 8.88356 9.59998 7.9999 9.59998Z" fill="#424242"/>
<path d="M7.9999 14.4C7.11625 14.4 6.3999 13.6836 6.3999 12.8C6.3999 11.9163 7.11625 11.2 7.9999 11.2C8.88356 11.2 9.5999 11.9163 9.5999 12.8C9.5999 13.6836 8.88356 14.4 7.9999 14.4Z" fill="#424242"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@@ -0,0 +1,24 @@
import { ColumnDef } from "@tanstack/react-table";
export type PortServerData = {
id : string;
info : string;
date : Date;
type : 'status' | 'result' | 'query';
}
export type HeaderTableProps = {
data: PortServerData[];
columns: ColumnDef<PortServerData>[];
}
export type DropDownComponentProps = {
label: string ;
type : string ;
}
export type TableBodyProps = {
data: PortServerData[]; // Sesuaikan dengan tipe data sebenarnya
columns: ColumnDef<PortServerData>[]; // Sesuaikan dengan tipe data sebenarnya
}

View File

@@ -0,0 +1,33 @@
import { Meta, StoryObj } from "@storybook/react";
import { PortServerData } from "@/components/main/model/TableModel";
import { TableBodyComponent } from "../tableComponent/BodyTableComponent";
import { columns } from "../tableComponent/columns";
const data:PortServerData[] = [
{ id: "1", info: "Info 1", date: new Date("2023-10-30 15:26:04"), type: 'status' },
{ id: "2", info: "Info 2", date: new Date("2023-10-30 15:26:04"), type: 'result' },
{ id: "3", info: "Info 3", date: new Date("2023-10-30 15:26:04"), type: 'query' }
];
const meta = {
title: 'Component/Table/Body',
component: TableBodyComponent,
args: {
data: data,
columns: columns,
},
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
} satisfies Meta<typeof TableBodyComponent>
export default meta;
type Story = StoryObj<typeof meta>
export const PortServerBodyTables = (args: Story ) => {
return (
<TableBodyComponent data={data} columns={columns} {...args} />
)
}

View File

@@ -0,0 +1,33 @@
import { Meta, StoryObj} from "@storybook/react";
import { DataTableDemo, columns, data } from "../tableComponent/DataTable";
import DataTable from "../tableComponent/newDataTable";
const meta = {
title: 'Component/Table',
component: DataTableDemo,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
args: {
data: data,
columns: columns,
},
argTypes: {
},
} satisfies Meta<typeof DataTableDemo>
export default meta;
type Story = StoryObj<typeof meta>
export const Datable = (args:Story) => {
return(
<DataTableDemo data={data} columns={columns} {...args} />
)
}
export const DataTable2 = (args: Story) => {
return (
<DataTable data={data} columns={columns} {...args}/>
)
}

View File

@@ -0,0 +1,66 @@
import { Meta, StoryObj } from '@storybook/react';
import { DropDownMenuComponent } from '../tableComponent/DropDownMenuComponent';
const meta = {
title: 'Component/Table/DropdownOption',
component: DropDownMenuComponent,
argTypes:{
label: {
options: ["cog", "dot",],
control: { type: 'radio' },
},
type: {
options: ['status','query','result','head'],
control: {type: 'radio'},
},
},
tags: ['autodocs'],
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
} satisfies Meta<typeof DropDownMenuComponent>;
export default meta
type Story = StoryObj<typeof meta>
export const CogDropDown = () => {
return(
<DropDownMenuComponent
label= "cog"
type= "head"
/>
)
}
export const DotDropDownStatus = (args: Story) => {
return(
<DropDownMenuComponent
label= "dot"
type= "status"
{...args}
/>
)
}
export const DotDropDownQuery = (args: Story) => {
return(
<DropDownMenuComponent
label= "dot"
type= "query"
{...args}
/>
)
}
export const DotDropDownResult = (args: Story) => {
return(
<DropDownMenuComponent
label= "dot"
type= "result"
{...args}
/>
)
}

View File

@@ -0,0 +1,34 @@
import { Meta, StoryObj } from "@storybook/react";
import { PortServerData } from "@/components/main/model/TableModel";
import { TableHeaderComponent } from "../tableComponent/HeaderTableComponent";
import { columns } from "../tableComponent/columns";
const data:PortServerData[] = [
{ id: "1", info: "Info 1", date: new Date("2023-10-30 15:26:04"), type: 'status' },
{ id: "2", info: "Info 2", date: new Date("2023-10-30 15:26:04"), type: 'result' },
{ id: "3", info: "Info 3", date: new Date("2023-10-30 15:26:04"), type: 'query' }
];
const meta = {
title: 'Component/Table/Header',
component: TableHeaderComponent,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
args: {
data: data,
columns: columns
}
} satisfies Meta<typeof TableHeaderComponent>
export default meta;
type Story = StoryObj<typeof meta>
export const PortServerHeadTables = (args: Story) => {
return (
<TableHeaderComponent data={data} columns={columns} {...args}/>
)
}

View File

@@ -0,0 +1,35 @@
import { Meta, StoryObj } from "@storybook/react";
import { Icon } from "../tableComponent/IconComponent";
const meta = {
title: 'Component/Table/Icon',
component: Icon,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
type: {
options: ['cog', 'dot'],
control: { type: 'radio' },
},
},
} satisfies Meta<typeof Icon>
export default meta;
type Story = StoryObj<typeof meta>
export const CogIcon = (args:Story) => {
return (
<Icon type='cog' {...args} />
)
}
export const DotIcon = (args:Story) => {
return (
<Icon type='dot' {...args} />
)
}

View File

@@ -0,0 +1,43 @@
import { TableBody, TableRow, TableCell } from '@/components/ui/table';
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { TableBodyProps } from '@/components/main/model/TableModel';
export const TableBodyComponent = ({ data, columns }:TableBodyProps) => {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)
}
</TableBody>
)
};

View File

@@ -0,0 +1,250 @@
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { DateDisplay } from "./DateDisplay"
import { CogIcon, DotIcon } from "../stories/Icon.stories"
import "../../../../app/global.css"
export const data: PortServer[] = [
{
id: "m5gr84i9",
info: "800 Cobas Status",
date: new Date("2023-10-30 15:26:04"),
type: "status",
},
{
id: "3u1reuv4",
info: "Incoming 800 Cobas Result",
date: new Date("2023-10-30 15:26:04"),
type: "result",
},
{
id: "derv1ws0",
info: "Incoming 800 Cobas Query",
date: new Date("2023-10-30 15:26:04"),
type: "query",
}
]
export type PortServer = {
id: string
info: string
date: Date
type : 'status' | 'result' | 'query'
}
export const columns: ColumnDef<PortServer>[] = [
{
accessorKey: "date",
header: "Date",
cell: ({ row }) => {
const date = new Date(row.getValue("date"))
return(
<div className="text-[#637381] bg-[#919EAB29] rounded-md border-4 border-[#919EAB29]">
<DateDisplay date={date}/>
</div>
)
}
},
{
accessorKey: "info",
header: "Info",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("info")}</div>
),
},
{
id: "actions",
header:() => {
return(
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<CogIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" >
<DropdownMenuItem
onClick={() => {}}
>
<span>Change Password</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
enableHiding: false,
cell: ({ row }) => {
const type = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<DotIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{
type.type?.toLowerCase() == 'status' ? (
<DropdownMenuItem
onClick={() => {}}
>
Info
</DropdownMenuItem>
) : ''
}
{
type.type?.toLowerCase() == 'result' ? (
<>
<DropdownMenuItem onClick={() => {}} >
Raw Data
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>
Result
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>
Export
</DropdownMenuItem>
</>
) : ''
}
{
type.type?.toLowerCase() == 'query' ? (
<>
<DropdownMenuItem onClick={() => {}}>
Raw Data
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>
Query Response
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>
Export
</DropdownMenuItem>
</>
) : ''
}
</DropdownMenuContent>
</DropdownMenu>
)}
},
]
export interface DataTableProps {
data: PortServer[]; // Properti untuk data tabel
columns: ColumnDef<PortServer>[];
}
export const DataTableDemo = ({
data,
columns,
}: DataTableProps) => {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div className="w-full">
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</div>
)
}

View File

@@ -0,0 +1,18 @@
// DateDisplay.tsx
interface DateDisplayProps {
date: Date;
}
export const DateDisplay = ({ date }: DateDisplayProps) => {
const formattedDate = new Intl.DateTimeFormat("id-ID", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}).format(date);
return <label>{formattedDate}</label>;
};

View File

@@ -0,0 +1,51 @@
import { DropDownComponentProps } from "@/components/main/model/TableModel";
import { CogIcon, DotIcon } from "../stories/Icon.stories"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
export const DropDownMenuComponent = ({label, type}:DropDownComponentProps) => {
return (
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
{label.toLowerCase() === 'cog' && (
<CogIcon />
)}
{label.toLowerCase() === 'dot' && (
<DotIcon />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{type.toLowerCase() === 'status' && (
<DropdownMenuItem
onClick={() => {}}
>
Info
</DropdownMenuItem>
)}
{type.toLowerCase() === 'result' && (
<>
<DropdownMenuItem onClick={() => {}} >Raw Data</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>Result</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>Export</DropdownMenuItem>
</>
)}
{type.toLowerCase() === 'query' && (
<>
<DropdownMenuItem onClick={() => {}}>Raw Data</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>Query Response</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>Export</DropdownMenuItem>
</>
)}
{type.toLowerCase() === 'head' && (
<DropdownMenuItem onClick={() => {}}>Change Password</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</>
);
};

View File

@@ -0,0 +1,31 @@
import { HeaderTableProps } from "@/components/main/model/TableModel";
import { TableHeader, TableRow, TableHead } from "@/components/ui/table";
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
export const TableHeaderComponent = ({ data, columns }:HeaderTableProps) => {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<TableHeader>
{table.getHeaderGroups().map((headerGroup: any) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header: any) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
typeof header.getContext === 'function' ? header.getContext() : {}
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
)
};

View File

@@ -0,0 +1,16 @@
import cog from "@/assets/cog.svg"
import dot from "@/assets/dots-vertical.svg"
interface IconProps {
type ?: "cog" | "dot";
}
export const Icon = ({
...props
}:IconProps) => {
return(
<img
src={props.type?.toLowerCase() === 'cog' ? cog : props.type?.toLowerCase() === 'dot' ? dot : '' }
/>
)
}

View File

@@ -0,0 +1,41 @@
import { PortServerData } from "@/components/main/model/TableModel"
import { ColumnDef } from "@tanstack/react-table"
import { DateDisplay } from "./DateDisplay"
import { CogDropDown } from "../stories/DropDownMenu.stories"
import { DropDownMenuComponent } from "./DropDownMenuComponent"
export const columns: ColumnDef<PortServerData>[] = [
{
accessorKey: "date",
header: "Date",
cell: ({ row }) => {
const date = new Date(row.getValue("date"))
return(
<div className="text-[#637381] bg-[#919EAB29] rounded-md border-4 border-[#919EAB29]">
<DateDisplay date={date}/>
</div>
)
}
},
{
accessorKey: "info",
header: "Info",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("info")}</div>
),
},
{
id: "actions",
header:() => {
return(
<CogDropDown />
)
},
enableHiding: false,
cell: ({ row }) => {
const type = row.original
return (
<DropDownMenuComponent label="dot" type={type.type}/>
)}
},
]

View File

@@ -0,0 +1,26 @@
import { Table} from "@/components/ui/table";
import { TableBodyComponent } from "./BodyTableComponent";
import { TableHeaderComponent } from "./HeaderTableComponent";
import { PortServerData } from "@/components/main/model/TableModel";
import { ColumnDef } from "@tanstack/react-table";
interface DataTableprops {
data: PortServerData[];
columns: ColumnDef<PortServerData>[];
}
const DataTable = ({data, columns }:DataTableprops) => {
return (
<div className="w-full">
<div className="rounded-md border">
<Table>
<TableHeaderComponent data={data} columns={columns} />
<TableBodyComponent data={data} columns={columns} />
</Table>
</div>
</div>
);
};
export default DataTable;

View File

@@ -0,0 +1,198 @@
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import { cn } from "@/lib/utils"
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
)
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}

View File

@@ -0,0 +1,24 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

View File

@@ -0,0 +1,114 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

File diff suppressed because it is too large Load Diff