Compare commits
2 Commits
frontend
...
tableCompo
| Author | SHA1 | Date | |
|---|---|---|---|
| adecf76a25 | |||
| 6be45eeb88 |
@@ -7,6 +7,7 @@ const config: StorybookConfig = {
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-onboarding",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/addon-styling-webpack"
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
import "../app/global.css";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
|
||||
3448
front_end_portserver/package-lock.json
generated
3448
front_end_portserver/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,9 +12,15 @@
|
||||
"build-storybook": "storybook build"
|
||||
},
|
||||
"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",
|
||||
"@tanstack/react-table": "^8.10.7",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"lucide-react": "^0.292.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@@ -26,6 +32,7 @@
|
||||
"@storybook/addon-interactions": "7.5.3",
|
||||
"@storybook/addon-links": "7.5.3",
|
||||
"@storybook/addon-onboarding": "1.0.8",
|
||||
"@storybook/addon-styling-webpack": "0.0.5",
|
||||
"@storybook/blocks": "7.5.3",
|
||||
"@storybook/react": "7.5.3",
|
||||
"@storybook/react-vite": "7.5.3",
|
||||
|
||||
5
front_end_portserver/src/components/table/assets/cog.svg
Normal file
5
front_end_portserver/src/components/table/assets/cog.svg
Normal 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 |
@@ -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 |
@@ -0,0 +1,27 @@
|
||||
import { Meta, StoryObj} from "@storybook/react";
|
||||
import { DataTableDemo, columns, data } from "../tableComponent/DataTable";
|
||||
|
||||
const meta = {
|
||||
title: 'Component/Table',
|
||||
component: DataTableDemo,
|
||||
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'],
|
||||
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} />
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { Icon } from "../tableComponent/icon";
|
||||
|
||||
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} />
|
||||
)
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import cog from "@/components/table/assets/cog.svg"
|
||||
import dot from "@/components/table/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 : '' }
|
||||
/>
|
||||
)
|
||||
}
|
||||
198
front_end_portserver/src/components/ui/dropdown-menu.tsx
Normal file
198
front_end_portserver/src/components/ui/dropdown-menu.tsx
Normal 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,
|
||||
}
|
||||
24
front_end_portserver/src/components/ui/label.tsx
Normal file
24
front_end_portserver/src/components/ui/label.tsx
Normal 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 }
|
||||
114
front_end_portserver/src/components/ui/table.tsx
Normal file
114
front_end_portserver/src/components/ui/table.tsx
Normal 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
Reference in New Issue
Block a user