@meta-1/design 0.0.170 → 0.0.172

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.172",
4
4
  "keywords": [
5
5
  "easykit",
6
6
  "design",
@@ -52,7 +52,7 @@ function DialogContent({
52
52
  <DialogOverlay onClick={onOverlayClick} />
53
53
  <DialogPrimitive.Content
54
54
  className={cn(
55
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border bg-background shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
55
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex flex-col gap-4 w-full max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border bg-background shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
56
56
  className,
57
57
  )}
58
58
  data-slot="dialog-content"
@@ -5,7 +5,7 @@ import { cn } from "@meta-1/design/lib/utils";
5
5
 
6
6
  function ScrollArea({ className, children, ...props }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
7
7
  return (
8
- <ScrollAreaPrimitive.Root className={cn("relative", className)} data-slot="scroll-area" {...props}>
8
+ <ScrollAreaPrimitive.Root className={cn("relative overflow-hidden", className)} data-slot="scroll-area" {...props}>
9
9
  <ScrollAreaPrimitive.Viewport
10
10
  className="size-full rounded-[inherit] outline-none transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:ring-ring/50"
11
11
  data-slot="scroll-area-viewport"
@@ -68,6 +68,8 @@ export interface DataTableProps<TData> {
68
68
  empty?: string;
69
69
  showHeader?: boolean;
70
70
  onRowClick?: (row: Row<TData>) => void;
71
+ /** 表格最大高度,设置后内容区将滚动,表头固定。支持 CSS 单位如 '500px', '50vh' 等 */
72
+ maxHeight?: string | number;
71
73
  }
72
74
 
73
75
  // 本地存储相关函数
@@ -173,6 +175,7 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
173
175
  showHeader = true,
174
176
  autoHidePagination = true,
175
177
  inCard = false,
178
+ maxHeight,
176
179
  } = props;
177
180
 
178
181
  const config = useContext(UIXContext);
@@ -363,6 +366,107 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
363
366
  return pagination;
364
367
  }, [pagination, autoHidePagination]);
365
368
 
369
+ // 渲染表头的通用函数
370
+ const renderTableHeader = () => {
371
+ return (
372
+ <TableHeader className={cn(!showHeader && "hidden")}>
373
+ {table.getHeaderGroups().map((headerGroup) => (
374
+ <TableRow key={headerGroup.id}>
375
+ {headerGroup.headers.map((header) => {
376
+ const sticky = mounted
377
+ ? getSticky(header.column.id, leftStickyColumns, rightStickyColumns)
378
+ : { enable: false };
379
+ const content = header.isPlaceholder
380
+ ? null
381
+ : flexRender(header.column.columnDef.header, header.getContext());
382
+ return (
383
+ <TableHead
384
+ className={cn(
385
+ sticky.enable ? "table-sticky-col sticky" : null,
386
+ sticky.last ? "table-sticky-col-last" : null,
387
+ sticky.first ? "table-sticky-col-first" : null,
388
+ // biome-ignore lint/suspicious/noExplicitAny: <className>
389
+ (header.column.columnDef as any).className,
390
+ )}
391
+ key={header.id}
392
+ style={
393
+ sticky.enable
394
+ ? {
395
+ zIndex: 10,
396
+ minWidth: sticky.width,
397
+ [sticky.position as string]: sticky.offset,
398
+ }
399
+ : undefined
400
+ }
401
+ >
402
+ {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
403
+ </TableHead>
404
+ );
405
+ })}
406
+ </TableRow>
407
+ ))}
408
+ </TableHeader>
409
+ );
410
+ };
411
+
412
+ // 渲染表体的通用函数
413
+ const renderTableBody = () => {
414
+ return (
415
+ <TableBody>
416
+ {table.getRowModel().rows?.length ? (
417
+ table.getRowModel().rows.map((row) => (
418
+ <TableRow
419
+ data-state={row.getIsSelected() && "selected"}
420
+ key={row.id}
421
+ onClick={() => props.onRowClick?.(row)}
422
+ >
423
+ {row.getVisibleCells().map((cell) => {
424
+ const sticky = mounted
425
+ ? getSticky(cell.column.id, leftStickyColumns, rightStickyColumns)
426
+ : { enable: false };
427
+ const ctx = cell.getContext();
428
+ const render = ctx.renderValue;
429
+ // biome-ignore lint/suspicious/noExplicitAny: <formatters>
430
+ const formatters = (cell.column.columnDef as any).formatters || [];
431
+ ctx.renderValue = () => {
432
+ return formatValue(render(), formatters, cellHandles);
433
+ };
434
+ const content = flexRender(cell.column.columnDef.cell, ctx);
435
+ return (
436
+ <TableCell
437
+ className={cn(
438
+ sticky.enable ? "table-sticky-col sticky" : null,
439
+ sticky.last ? "table-sticky-col-last" : null,
440
+ sticky.first ? "table-sticky-col-first" : null,
441
+ )}
442
+ key={cell.id}
443
+ style={
444
+ sticky.enable
445
+ ? {
446
+ zIndex: 10,
447
+ minWidth: sticky.width,
448
+ [sticky.position as string]: sticky.offset,
449
+ }
450
+ : undefined
451
+ }
452
+ >
453
+ {sticky.enable ? <div className="inner flex h-10 items-center px-2">{content}</div> : content}
454
+ </TableCell>
455
+ );
456
+ })}
457
+ </TableRow>
458
+ ))
459
+ ) : (
460
+ <TableRow>
461
+ <TableCell className="h-24 text-center" colSpan={tableColumns.length}>
462
+ {empty}
463
+ </TableCell>
464
+ </TableRow>
465
+ )}
466
+ </TableBody>
467
+ );
468
+ };
469
+
366
470
  return (
367
471
  <>
368
472
  {showToolbar ? (
@@ -384,99 +488,32 @@ export function DataTable<TData>(props: DataTableProps<TData>) {
384
488
  </div>
385
489
  ) : null}
386
490
  <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>
491
+ {maxHeight ? (
492
+ // 有高度限制时:直接渲染 table 元素,避免 Table 组件自带的 overflow-x-auto 包裹层
493
+ <div className={cn("overflow-x-auto", !mounted && "invisible")}>
494
+ <div
495
+ className="overflow-y-auto"
496
+ style={{ maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight }}
497
+ >
498
+ <table
499
+ className={classNames(
500
+ "data-table data-table-sticky-header w-full caption-bottom text-sm",
501
+ inCard ? "in-card" : null,
502
+ )}
503
+ data-slot="table"
504
+ >
505
+ {renderTableHeader()}
506
+ {renderTableBody()}
507
+ </table>
508
+ </div>
509
+ </div>
510
+ ) : (
511
+ // 无高度限制时,使用原有布局
512
+ <Table className={classNames("data-table", inCard ? "in-card" : null, !mounted && "invisible")}>
513
+ {renderTableHeader()}
514
+ {renderTableBody()}
515
+ </Table>
516
+ )}
480
517
  <div className={cn("py-4", !mounted && "invisible")}>
481
518
  {showPagination && (
482
519
  <Pagination
@@ -38,3 +38,39 @@
38
38
  .data-table tr[data-state="selected"] td.table-sticky-col .inner {
39
39
  @apply bg-[hsl(var(--muted))];
40
40
  }
41
+
42
+ .data-table-sticky-header thead {
43
+ position: sticky;
44
+ top: 0;
45
+ z-index: 20;
46
+ @apply bg-background;
47
+ }
48
+
49
+ .data-table-sticky-header.in-card thead {
50
+ @apply bg-card;
51
+ }
52
+
53
+ .data-table-sticky-header thead th {
54
+ @apply bg-background;
55
+ z-index: 20;
56
+ }
57
+
58
+ .data-table-sticky-header.in-card thead th {
59
+ @apply bg-card;
60
+ }
61
+
62
+ .data-table-sticky-header thead th.table-sticky-col {
63
+ z-index: 30;
64
+ }
65
+
66
+ .data-table-sticky-header thead th.table-sticky-col.sticky {
67
+ position: sticky !important;
68
+ }
69
+
70
+ .data-table-sticky-header thead th.table-sticky-col .inner {
71
+ @apply bg-background;
72
+ }
73
+
74
+ .data-table-sticky-header.in-card thead th.table-sticky-col .inner {
75
+ @apply bg-card;
76
+ }
@@ -55,7 +55,7 @@ export const Dialog: FC<DialogProps> = (props) => {
55
55
  <DialogTitle className={cn(!title && "sr-only")}>{title || "Dialog"}</DialogTitle>
56
56
  {description ? <DialogDescription>{description}</DialogDescription> : null}
57
57
  </DialogHeader>
58
- <div className="min-h-0 flex-1 overflow-x-auto overflow-y-auto px-6 py-4">{props.children}</div>
58
+ <div className="min-h-0 flex-1 overflow-auto px-6">{props.children}</div>
59
59
  {footer ? <DialogFooter>{footer}</DialogFooter> : null}
60
60
  {loading ? (
61
61
  <div className={cn("absolute top-0 right-0 bottom-0 left-0 bg-white/50", "flex items-center justify-center")}>