snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
All checks were successful
Release / Create Version PR or Publish Stable Release (push) Has been skipped
Release / Publish Prerelease to NPM (push) Has been skipped
Monitor Registries / Monitor registry health (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 22:44:43 +05:00
commit 1416db152c
5727 changed files with 630238 additions and 0 deletions

View File

@@ -0,0 +1,443 @@
"use client"
import * as React from "react"
import {
columnFilteringFeature,
columnVisibilityFeature,
createColumnHelper,
createFilteredRowModel,
createPaginatedRowModel,
createSortedRowModel,
filterFn_includesString,
rowPaginationFeature,
rowSelectionFeature,
rowSortingFeature,
sortFn_alphanumeric,
sortFn_text,
tableFeatures,
useTable,
type ColumnFiltersState,
type ColumnVisibilityState,
type SortingState,
} from "@tanstack/react-table"
import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
import {
useTranslation,
type Translations,
} from "@/components/language-selector"
import { Button } from "@/styles/aria-nova/ui-rtl/button"
import { Checkbox } from "@/styles/aria-nova/ui-rtl/checkbox"
import {
DropdownMenu,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/styles/aria-nova/ui-rtl/dropdown-menu"
import { Input } from "@/styles/aria-nova/ui-rtl/input"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/styles/aria-nova/ui-rtl/table"
import { buttonVariants } from "@/styles/aria-nova/ui/button"
const translations: Translations = {
en: {
dir: "ltr",
values: {
filterEmails: "Filter emails...",
columns: "Columns",
status: "Status",
email: "Email",
amount: "Amount",
actions: "Actions",
copyPaymentId: "Copy payment ID",
viewCustomer: "View customer",
viewPaymentDetails: "View payment details",
selectAll: "Select all",
selectRow: "Select row",
openMenu: "Open menu",
noResults: "No results.",
rowsSelected: "of",
rowsSelectedSuffix: "row(s) selected.",
previous: "Previous",
next: "Next",
success: "Success",
processing: "Processing",
failed: "Failed",
pending: "Pending",
},
},
ar: {
dir: "rtl",
values: {
filterEmails: "تصفية البريد الإلكتروني...",
columns: "الأعمدة",
status: "الحالة",
email: "البريد الإلكتروني",
amount: "المبلغ",
actions: "الإجراءات",
copyPaymentId: "نسخ معرف الدفع",
viewCustomer: "عرض العميل",
viewPaymentDetails: "عرض تفاصيل الدفع",
selectAll: "تحديد الكل",
selectRow: "تحديد الصف",
openMenu: "فتح القائمة",
noResults: "لا توجد نتائج.",
rowsSelected: "من",
rowsSelectedSuffix: "صف(وف) محدد.",
previous: "السابق",
next: "التالي",
success: "ناجح",
processing: "قيد المعالجة",
failed: "فشل",
pending: "قيد الانتظار",
},
},
he: {
dir: "rtl",
values: {
filterEmails: "סנן אימיילים...",
columns: "עמודות",
status: "סטטוס",
email: "אימייל",
amount: "סכום",
actions: "פעולות",
copyPaymentId: "העתק מזהה תשלום",
viewCustomer: "צפה בלקוח",
viewPaymentDetails: "צפה בפרטי תשלום",
selectAll: "בחר הכל",
selectRow: "בחר שורה",
openMenu: "פתח תפריט",
noResults: "אין תוצאות.",
rowsSelected: "מתוך",
rowsSelectedSuffix: "שורות נבחרו.",
previous: "הקודם",
next: "הבא",
success: "הצליח",
processing: "מעבד",
failed: "נכשל",
pending: "ממתין",
},
},
}
// New in v9: declare the features this table uses — anything you don't
// register is tree-shaken out of the bundle.
const features = tableFeatures({
columnFilteringFeature,
columnVisibilityFeature,
rowPaginationFeature,
rowSelectionFeature,
rowSortingFeature,
filteredRowModel: createFilteredRowModel(),
paginatedRowModel: createPaginatedRowModel(),
sortedRowModel: createSortedRowModel(),
filterFns: { includesString: filterFn_includesString },
sortFns: { alphanumeric: sortFn_alphanumeric, text: sortFn_text },
})
const columnHelper = createColumnHelper<typeof features, Payment>()
type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
const data: Payment[] = [
{
id: "m5gr84i9",
amount: 316,
status: "success",
email: "ken99@example.com",
},
{
id: "3u1reuv4",
amount: 242,
status: "success",
email: "Abe45@example.com",
},
{
id: "derv1ws0",
amount: 837,
status: "processing",
email: "Monserrat44@example.com",
},
{
id: "5kma53ae",
amount: 874,
status: "success",
email: "Silas22@example.com",
},
{
id: "bhqecj4p",
amount: 721,
status: "failed",
email: "carmella@example.com",
},
]
export function DataTableRtl() {
const { t, dir, language } = useTranslation(translations, "ar")
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<ColumnVisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const columns = React.useMemo(
() =>
columnHelper.columns([
columnHelper.display({
id: "select",
header: ({ table }) => <Checkbox slot="selection" />,
cell: ({ row }) => <Checkbox slot="selection" />,
enableSorting: false,
enableHiding: false,
}),
columnHelper.accessor("status", {
header: t.status,
cell: ({ row }) => {
const status = row.getValue("status") as string
const statusMap: Record<string, string> = {
success: t.success,
processing: t.processing,
failed: t.failed,
pending: t.pending,
}
return <div className="capitalize">{statusMap[status]}</div>
},
}),
columnHelper.accessor("email", {
header: () => {
return (
<div className={buttonVariants({ variant: "ghost" })}>
{t.email}
<ArrowUpDown />
</div>
)
},
cell: ({ row }) => (
<div className="lowercase">{row.getValue("email")}</div>
),
}),
columnHelper.accessor("amount", {
header: () => <div className="text-start">{t.amount}</div>,
cell: ({ row }) => {
const amount = parseFloat(row.getValue("amount"))
const formatted = new Intl.NumberFormat(
dir === "rtl" ? "ar-SA" : "en-US",
{
style: "currency",
currency: "USD",
}
).format(amount)
return <div className="text-start font-medium">{formatted}</div>
},
}),
columnHelper.display({
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const payment = row.original
return (
<DropdownMenuTrigger>
<Button variant="ghost" size="icon-xs">
<span className="sr-only">{t.openMenu}</span>
<MoreHorizontal />
</Button>
<DropdownMenu
placement={dir === "rtl" ? "bottom start" : "bottom end"}
data-lang={dir === "rtl" ? language : undefined}
className="w-40"
>
<DropdownMenuGroup>
<DropdownMenuLabel>{t.actions}</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(payment.id)}
>
{t.copyPaymentId}
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>{t.viewCustomer}</DropdownMenuItem>
<DropdownMenuItem>{t.viewPaymentDetails}</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenu>
</DropdownMenuTrigger>
)
},
}),
]),
[t, dir, language]
)
const table = useTable({
features,
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div className="w-full">
<div className="flex items-center gap-2 py-4">
<Input
placeholder={t.filterEmails}
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("email")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenuTrigger>
<Button variant="outline" className="ms-auto">
{t.columns} <ChevronDown />
</Button>
<DropdownMenu
placement={dir === "rtl" ? "bottom start" : "bottom end"}
data-lang={dir === "rtl" ? language : undefined}
>
<DropdownMenuGroup
selectionMode="multiple"
selectedKeys={table
.getVisibleFlatColumns()
.filter((column) => column.getCanHide())
.map((column) => column.id)}
onSelectionChange={(keys) => {
table.setColumnVisibility(
Object.fromEntries(
table
.getAllFlatColumns()
.map((c) => [
c.id,
!c.getCanHide() || keys === "all" || keys.has(c.id),
])
)
)
}}
>
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuItem
key={column.id}
id={column.id}
className="capitalize"
>
{column.id}
</DropdownMenuItem>
)
})}
</DropdownMenuGroup>
</DropdownMenu>
</DropdownMenuTrigger>
</div>
<div className="overflow-hidden rounded-md border">
<Table
aria-label="Tasks"
selectionMode="multiple"
onSelectionChange={(selection) => {
if (selection === "all") {
table.toggleAllRowsSelected()
} else {
table.setRowSelection(
Object.fromEntries([...selection].map((key) => [key, true]))
)
}
}}
sortDescriptor={
sorting.length
? {
column: sorting[0].id,
direction: sorting[0].desc ? "descending" : "ascending",
}
: undefined
}
onSortChange={(sortDescriptor) => {
table.setSorting([
{
id: "" + sortDescriptor.column,
desc: sortDescriptor.direction === "descending",
},
])
}}
>
<TableHeader>
{table.getFlatHeaders().map((header) => (
<TableHead
key={header.id}
id={header.id}
isRowHeader={header.index === 1}
allowsSorting={header.column.getCanSort()}
>
{header.isPlaceholder ? null : (
<table.FlexRender header={header} />
)}
</TableHead>
))}
</TableHeader>
<TableBody renderEmptyState={() => t.noResults}>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id} id={row.id}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
<table.FlexRender cell={cell} />
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end gap-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} {t.rowsSelected}{" "}
{table.getFilteredRowModel().rows.length} {t.rowsSelectedSuffix}
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
isDisabled={!table.getCanPreviousPage()}
>
{t.previous}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
isDisabled={!table.getCanNextPage()}
>
{t.next}
</Button>
</div>
</div>
</div>
)
}