periplo-ui 3.11.0 → 3.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Collapsible/Collapsible.d.ts +5 -0
- package/dist/components/Collapsible/Collapsible.js +8 -0
- package/dist/components/Collapsible/Collapsible.js.map +1 -0
- package/dist/components/Collapsible/index.d.ts +1 -0
- package/dist/components/Collapsible/index.js +2 -0
- package/dist/components/Collapsible/index.js.map +1 -0
- package/dist/components/DataTable/DataTable.js +4 -4
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
2
|
+
declare const CollapsibleRoot: import('react').ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
3
|
+
declare const CollapsibleTrigger: import('react').ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleTriggerProps & import('react').RefAttributes<HTMLButtonElement>>;
|
|
4
|
+
declare const CollapsibleContent: import('react').ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleContentProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
5
|
+
export { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
2
|
+
|
|
3
|
+
const CollapsibleRoot = CollapsiblePrimitive.Root;
|
|
4
|
+
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
|
5
|
+
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
|
|
6
|
+
|
|
7
|
+
export { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger };
|
|
8
|
+
//# sourceMappingURL=Collapsible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Collapsible.js","sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'\n\nconst CollapsibleRoot = CollapsiblePrimitive.Root\n\nconst CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger\n\nconst CollapsibleContent = CollapsiblePrimitive.CollapsibleContent\n\nexport { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent }\n"],"names":[],"mappings":";;AAEA,MAAM,kBAAkB,oBAAqB,CAAA;AAE7C,MAAM,qBAAqB,oBAAqB,CAAA;AAEhD,MAAM,qBAAqB,oBAAqB,CAAA;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Collapsible';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -146,8 +146,8 @@ function DataTable({
|
|
|
146
146
|
] }, rowId);
|
|
147
147
|
});
|
|
148
148
|
};
|
|
149
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full w-full flex-col gap-2", className), children: [
|
|
150
|
-
(showColumnVisibilityControls || Filters) && /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between", children: [
|
|
149
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden", className), children: [
|
|
150
|
+
(showColumnVisibilityControls || Filters) && /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 items-end justify-between p-1", children: [
|
|
151
151
|
!isMobile && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: Filters }),
|
|
152
152
|
isMobile && /* @__PURE__ */ jsx("div", { className: "flex w-full justify-end", children: Filters && /* @__PURE__ */ jsxs(PopoverRoot, { children: [
|
|
153
153
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", StartIcon: FunnelSimple, children: "Filters" }) }),
|
|
@@ -184,8 +184,8 @@ function DataTable({
|
|
|
184
184
|
] })
|
|
185
185
|
] })
|
|
186
186
|
] }),
|
|
187
|
-
/* @__PURE__ */ jsxs("div", { className: "flex h-
|
|
188
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsx("div", { className: "
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col rounded-md border bg-white", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsx("div", { className: "h-full overflow-auto", children: /* @__PURE__ */ jsxs(Table, { className: "w-full table-fixed", children: [
|
|
189
189
|
/* @__PURE__ */ jsx(TableHeader, { className: "sticky top-0 z-10 bg-neutral-50", children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
190
190
|
isSelectable && /* @__PURE__ */ jsx(TableHead, { className: "w-[50px]", children: /* @__PURE__ */ jsx(
|
|
191
191
|
Checkbox,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Custom text for the column visibility button */\n readonly columnVisibilityButtonLabel?: string\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Optional component to render filters */\n readonly Filters?: React.ReactNode\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n columnVisibilityButtonLabel = 'Hide columns',\n isLoading = false,\n pagination,\n onSelectAll,\n onSelect,\n Filters,\n className,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <Skeleton className=\"h-[20px] w-full\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full w-full flex-col gap-2', className)}>\n {(showColumnVisibilityControls || Filters) && (\n <div className=\"flex items-end justify-between\">\n {!isMobile && <div className=\"flex items-center gap-2\">{Filters}</div>}\n {isMobile && (\n <div className=\"flex w-full justify-end\">\n {Filters && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple}>\n Filters\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n {Filters}\n </PopoverContent>\n </PopoverRoot>\n )}\n </div>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {columnVisibilityButtonLabel}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n )}\n\n <div className=\"flex h-full flex-1 flex-col overflow-hidden rounded-md border bg-white\">\n <div className=\"flex-1 overflow-auto\">\n <div className=\"w-full\">\n <TableComponent className=\"table-fixed\">\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA2IO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACD;AAClB;AACZ;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAEA;AAAA;AAHgB;AAKzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAAA;AAEJ;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAA+D;AAKxD;AAIA;AAGA;AAGN;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAAA;AAEjC;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Custom text for the column visibility button */\n readonly columnVisibilityButtonLabel?: string\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Optional component to render filters */\n readonly Filters?: React.ReactNode\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n columnVisibilityButtonLabel = 'Hide columns',\n isLoading = false,\n pagination,\n onSelectAll,\n onSelect,\n Filters,\n className,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <Skeleton className=\"h-[20px] w-full\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden', className)}>\n {(showColumnVisibilityControls || Filters) && (\n <div className=\"flex flex-shrink-0 items-end justify-between p-1\">\n {!isMobile && <div className=\"flex items-center gap-2\">{Filters}</div>}\n {isMobile && (\n <div className=\"flex w-full justify-end\">\n {Filters && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple}>\n Filters\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n {Filters}\n </PopoverContent>\n </PopoverRoot>\n )}\n </div>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {columnVisibilityButtonLabel}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col rounded-md border bg-white\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <div className=\"h-full overflow-auto\">\n <TableComponent className=\"w-full table-fixed\">\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA2IO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACD;AAClB;AACZ;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAEA;AAAA;AAHgB;AAKzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAAA;AAEJ;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAA+D;AAKxD;AAIA;AAGA;AAGN;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAAA;AAEjC;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -37,4 +37,5 @@ export { DatePicker } from './components/DatePicker/DatePicker.js';
|
|
|
37
37
|
export { SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarRoot, SidebarSeparator, SidebarTrigger, useSidebar } from './components/Sidebar/Sidebar.js';
|
|
38
38
|
export { Combobox } from './components/Combobox/Combobox.js';
|
|
39
39
|
export { DataTable } from './components/DataTable/DataTable.js';
|
|
40
|
+
export { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from './components/Collapsible/Collapsible.js';
|
|
40
41
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "periplo-ui",
|
|
3
3
|
"description": "IATI UI library",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.12.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"@radix-ui/react-accordion": "1.2.2",
|
|
95
95
|
"@radix-ui/react-avatar": "1.1.2",
|
|
96
96
|
"@radix-ui/react-checkbox": "1.1.3",
|
|
97
|
+
"@radix-ui/react-collapsible": "^1.1.3",
|
|
97
98
|
"@radix-ui/react-dialog": "1.1.4",
|
|
98
99
|
"@radix-ui/react-dropdown-menu": "2.1.4",
|
|
99
100
|
"@radix-ui/react-label": "2.1.1",
|