@meta-1/design 0.0.170 → 0.0.171

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meta-1/design",
3
- "version": "0.0.170",
3
+ "version": "0.0.171",
4
4
  "keywords": [
5
5
  "easykit",
6
6
  "design",
@@ -28,6 +28,7 @@ import {
28
28
  type PaginationProps,
29
29
  Spin,
30
30
  } from "@meta-1/design";
31
+ import { ScrollArea } from "@meta-1/design/components/ui/scroll-area";
31
32
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../ui/table";
32
33
  import "./style.css";
33
34
 
@@ -68,6 +69,8 @@ export interface DataTableProps<TData> {
68
69
  empty?: string;
69
70
  showHeader?: boolean;
70
71
  onRowClick?: (row: Row<TData>) => void;
72
+ /** 表格最大高度,设置后内容区将滚动,表头固定。支持 CSS 单位如 '500px', '50vh' 等 */
73
+ maxHeight?: string | number;
71
74
  }
72
75
 
73
76
  // 本地存储相关函数
@@ -173,6 +176,7 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
173
176
  showHeader = true,
174
177
  autoHidePagination = true,
175
178
  inCard = false,
179
+ maxHeight,
176
180
  } = props;
177
181
 
178
182
  const config = useContext(UIXContext);
@@ -363,6 +367,108 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
363
367
  return pagination;
364
368
  }, [pagination, autoHidePagination]);
365
369
 
370
+ // 渲染表头的通用函数
371
+ const renderTableHeader = (options: { fixedHeader?: boolean } = {}) => {
372
+ const { fixedHeader = false } = options;
373
+ return (
374
+ <TableHeader className={cn(!showHeader && "hidden", fixedHeader && "sticky top-0 z-20 bg-background")}>
375
+ {table.getHeaderGroups().map((headerGroup) => (
376
+ <TableRow key={headerGroup.id}>
377
+ {headerGroup.headers.map((header) => {
378
+ const sticky = mounted
379
+ ? getSticky(header.column.id, leftStickyColumns, rightStickyColumns)
380
+ : { enable: false };
381
+ const content = header.isPlaceholder
382
+ ? null
383
+ : flexRender(header.column.columnDef.header, header.getContext());
384
+ return (
385
+ <TableHead
386
+ className={cn(
387
+ sticky.enable ? "table-sticky-col sticky" : null,
388
+ sticky.last ? "table-sticky-col-last" : null,
389
+ sticky.first ? "table-sticky-col-first" : null,
390
+ // biome-ignore lint/suspicious/noExplicitAny: <className>
391
+ (header.column.columnDef as any).className,
392
+ )}
393
+ key={header.id}
394
+ style={
395
+ sticky.enable
396
+ ? {
397
+ zIndex: fixedHeader ? 30 : 10,
398
+ minWidth: sticky.width,
399
+ [sticky.position as string]: sticky.offset,
400
+ }
401
+ : undefined
402
+ }
403
+ >
404
+ {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
405
+ </TableHead>
406
+ );
407
+ })}
408
+ </TableRow>
409
+ ))}
410
+ </TableHeader>
411
+ );
412
+ };
413
+
414
+ // 渲染表体的通用函数
415
+ const renderTableBody = () => {
416
+ return (
417
+ <TableBody>
418
+ {table.getRowModel().rows?.length ? (
419
+ table.getRowModel().rows.map((row) => (
420
+ <TableRow
421
+ data-state={row.getIsSelected() && "selected"}
422
+ key={row.id}
423
+ onClick={() => props.onRowClick?.(row)}
424
+ >
425
+ {row.getVisibleCells().map((cell) => {
426
+ const sticky = mounted
427
+ ? getSticky(cell.column.id, leftStickyColumns, rightStickyColumns)
428
+ : { enable: false };
429
+ const ctx = cell.getContext();
430
+ const render = ctx.renderValue;
431
+ // biome-ignore lint/suspicious/noExplicitAny: <formatters>
432
+ const formatters = (cell.column.columnDef as any).formatters || [];
433
+ ctx.renderValue = () => {
434
+ return formatValue(render(), formatters, cellHandles);
435
+ };
436
+ const content = flexRender(cell.column.columnDef.cell, ctx);
437
+ return (
438
+ <TableCell
439
+ className={cn(
440
+ sticky.enable ? "table-sticky-col sticky" : null,
441
+ sticky.last ? "table-sticky-col-last" : null,
442
+ sticky.first ? "table-sticky-col-first" : null,
443
+ )}
444
+ key={cell.id}
445
+ style={
446
+ sticky.enable
447
+ ? {
448
+ zIndex: 10,
449
+ minWidth: sticky.width,
450
+ [sticky.position as string]: sticky.offset,
451
+ }
452
+ : undefined
453
+ }
454
+ >
455
+ {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
456
+ </TableCell>
457
+ );
458
+ })}
459
+ </TableRow>
460
+ ))
461
+ ) : (
462
+ <TableRow>
463
+ <TableCell className="h-24 text-center" colSpan={tableColumns.length}>
464
+ {empty}
465
+ </TableCell>
466
+ </TableRow>
467
+ )}
468
+ </TableBody>
469
+ );
470
+ };
471
+
366
472
  return (
367
473
  <>
368
474
  {showToolbar ? (
@@ -384,99 +490,23 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
384
490
  </div>
385
491
  ) : null}
386
492
  <div className={cn("relative")}>
387
- <Table className={classNames("data-table", inCard ? "in-card" : null, !mounted && "invisible")}>
388
- <TableHeader className={cn(!showHeader && "hidden")}>
389
- {table.getHeaderGroups().map((headerGroup) => (
390
- <TableRow key={headerGroup.id}>
391
- {headerGroup.headers.map((header) => {
392
- // 在未挂载时禁用粘性列功能,避免 SSR 水合错误
393
- const sticky = mounted
394
- ? getSticky(header.column.id, leftStickyColumns, rightStickyColumns)
395
- : { enable: false };
396
- const content = header.isPlaceholder
397
- ? null
398
- : flexRender(header.column.columnDef.header, header.getContext());
399
- return (
400
- <TableHead
401
- className={cn(
402
- sticky.enable ? "table-sticky-col sticky" : null,
403
- sticky.last ? "table-sticky-col-last" : null,
404
- sticky.first ? "table-sticky-col-first" : null,
405
- // biome-ignore lint/suspicious/noExplicitAny: <className>
406
- (header.column.columnDef as any).className,
407
- )}
408
- key={header.id}
409
- style={
410
- sticky.enable
411
- ? {
412
- zIndex: 10,
413
- minWidth: sticky.width,
414
- [sticky.position as string]: sticky.offset,
415
- }
416
- : undefined
417
- }
418
- >
419
- {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
420
- </TableHead>
421
- );
422
- })}
423
- </TableRow>
424
- ))}
425
- </TableHeader>
426
- <TableBody>
427
- {table.getRowModel().rows?.length ? (
428
- table.getRowModel().rows.map((row) => (
429
- <TableRow
430
- data-state={row.getIsSelected() && "selected"}
431
- key={row.id}
432
- onClick={() => props.onRowClick?.(row)}
433
- >
434
- {row.getVisibleCells().map((cell) => {
435
- // 在未挂载时禁用粘性列功能,避免 SSR 水合错误
436
- const sticky = mounted
437
- ? getSticky(cell.column.id, leftStickyColumns, rightStickyColumns)
438
- : { enable: false };
439
- const ctx = cell.getContext();
440
- const render = ctx.renderValue;
441
- // biome-ignore lint/suspicious/noExplicitAny: <formatters>
442
- const formatters = (cell.column.columnDef as any).formatters || [];
443
- ctx.renderValue = () => {
444
- return formatValue(render(), formatters, cellHandles);
445
- };
446
- const content = flexRender(cell.column.columnDef.cell, ctx);
447
- return (
448
- <TableCell
449
- className={cn(
450
- sticky.enable ? "table-sticky-col sticky" : null,
451
- sticky.last ? "table-sticky-col-last" : null,
452
- sticky.first ? "table-sticky-col-first" : null,
453
- )}
454
- key={cell.id}
455
- style={
456
- sticky.enable
457
- ? {
458
- zIndex: 10,
459
- minWidth: sticky.width,
460
- [sticky.position as string]: sticky.offset,
461
- }
462
- : undefined
463
- }
464
- >
465
- {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
466
- </TableCell>
467
- );
468
- })}
469
- </TableRow>
470
- ))
471
- ) : (
472
- <TableRow>
473
- <TableCell className="h-24 text-center" colSpan={tableColumns.length}>
474
- {empty}
475
- </TableCell>
476
- </TableRow>
477
- )}
478
- </TableBody>
479
- </Table>
493
+ {maxHeight ? (
494
+ // 有高度限制时,使用 ScrollArea 包裹 TableBody,表头固定
495
+ <div className={cn("rounded-md border", !mounted && "invisible")}>
496
+ <Table className={classNames("data-table", inCard ? "in-card" : null)}>
497
+ {renderTableHeader({ fixedHeader: true })}
498
+ </Table>
499
+ <ScrollArea style={{ maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight }}>
500
+ <Table className={classNames("data-table", inCard ? "in-card" : null)}>{renderTableBody()}</Table>
501
+ </ScrollArea>
502
+ </div>
503
+ ) : (
504
+ // 无高度限制时,使用原有布局
505
+ <Table className={classNames("data-table", inCard ? "in-card" : null, !mounted && "invisible")}>
506
+ {renderTableHeader()}
507
+ {renderTableBody()}
508
+ </Table>
509
+ )}
480
510
  <div className={cn("py-4", !mounted && "invisible")}>
481
511
  {showPagination && (
482
512
  <Pagination
@@ -9,6 +9,7 @@ import {
9
9
  DialogTitle,
10
10
  Dialog as UIDialog,
11
11
  } from "@meta-1/design/components/ui/dialog";
12
+ import { ScrollArea } from "@meta-1/design/components/ui/scroll-area";
12
13
  import { Spin } from "@meta-1/design/components/uix/spin";
13
14
  import { cn } from "@meta-1/design/lib";
14
15
 
@@ -55,7 +56,7 @@ export const Dialog: FC<DialogProps> = (props) => {
55
56
  <DialogTitle className={cn(!title && "sr-only")}>{title || "Dialog"}</DialogTitle>
56
57
  {description ? <DialogDescription>{description}</DialogDescription> : null}
57
58
  </DialogHeader>
58
- <div className="min-h-0 flex-1 overflow-x-auto overflow-y-auto px-6 py-4">{props.children}</div>
59
+ <ScrollArea className="min-h-0 flex-1 px-6 py-4">{props.children}</ScrollArea>
59
60
  {footer ? <DialogFooter>{footer}</DialogFooter> : null}
60
61
  {loading ? (
61
62
  <div className={cn("absolute top-0 right-0 bottom-0 left-0 bg-white/50", "flex items-center justify-center")}>