@crispy-seed/data-table 0.8.0 → 0.9.0

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  declare interface DataTableVariant {
@@ -138,7 +138,7 @@ interface DataTableColumn<TRow> {
138
138
  */
139
139
  key: string;
140
140
  /** 헤더 셀에 표시할 내용입니다. */
141
- header: react.ReactNode;
141
+ header: React.ReactNode;
142
142
  /** 이 열을 클릭해 정렬할 수 있는지 여부입니다. */
143
143
  sortable?: boolean;
144
144
  /**
@@ -149,7 +149,7 @@ interface DataTableColumn<TRow> {
149
149
  /** 셀 내용을 직접 그립니다. 지정하면 `type`의 기본 렌더러 대신 이것을 사용합니다.
150
150
  * `editable`이어도 편집/always-live 상태에는 영향이 없습니다 -- 이 필드는 REST
151
151
  * 상태의 표시만 바꿉니다. */
152
- render?: (row: TRow) => react.ReactNode;
152
+ render?: (row: TRow) => React.ReactNode;
153
153
  /** `type: "currency"`의 통화 기호입니다. @default "$" */
154
154
  symbol?: string;
155
155
  /** `type: "rating"`의 별 개수입니다. @default 5 */
@@ -272,7 +272,7 @@ interface DataTableColumn<TRow> {
272
272
  }
273
273
  interface DataTableProps<TRow extends {
274
274
  id: string;
275
- }> extends DataTableVariantProps, Omit<PrimitiveProps, "asChild">, Omit<react.HTMLAttributes<HTMLDivElement>, "children"> {
275
+ }> extends DataTableVariantProps, Omit<PrimitiveProps, "asChild">, Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
276
276
  columns: DataTableColumn<TRow>[];
277
277
  data: TRow[];
278
278
  /** `type`이 있는 열의 number/currency/date/datetime 표시에 쓰는 로캘입니다. @default "en-US" */
@@ -290,9 +290,9 @@ interface DataTableProps<TRow extends {
290
290
  * 씁니다. 생략하면 karrot 아이콘 셋의 chevron을 씁니다.
291
291
  */
292
292
  sortIcon?: {
293
- idle?: react.ReactNode;
294
- asc?: react.ReactNode;
295
- desc?: react.ReactNode;
293
+ idle?: React.ReactNode;
294
+ asc?: React.ReactNode;
295
+ desc?: React.ReactNode;
296
296
  };
297
297
  /** 왼쪽에 40px짜리 체크박스 선택 열을 추가합니다. */
298
298
  selectable?: boolean;
@@ -308,10 +308,10 @@ interface DataTableProps<TRow extends {
308
308
  getRowSelectionLabel?: (row: TRow) => string;
309
309
  /** 선택 체크박스가 checked일 때 표시할 아이콘입니다. 생략하면 karrot의 체크 아이콘을
310
310
  * 씁니다. */
311
- selectionCheckedIcon?: react.ReactNode;
311
+ selectionCheckedIcon?: React.ReactNode;
312
312
  /** 전체 선택 체크박스가 일부만 선택됐을 때(indeterminate) 표시할 아이콘입니다.
313
313
  * 생략하면 karrot의 minus 아이콘을 씁니다. */
314
- selectionIndeterminateIcon?: react.ReactNode;
314
+ selectionIndeterminateIcon?: React.ReactNode;
315
315
  /** 제어할 검색어입니다. */
316
316
  query?: string;
317
317
  /** 비제어 방식으로 사용할 때의 초기 검색어입니다. */
@@ -348,7 +348,7 @@ interface DataTableProps<TRow extends {
348
348
  * 행 개수 텍스트를 직접 만듭니다. 생략하면 crisp의 기본 형식을 씁니다: 검색/필터로
349
349
  * 줄지 않았으면 "N rows"(단수는 "row"), 줄었으면 "N of M".
350
350
  */
351
- countLabel?: (shown: number, total: number) => react.ReactNode;
351
+ countLabel?: (shown: number, total: number) => React.ReactNode;
352
352
  /**
353
353
  * 툴바 오른쪽, 행 개수 옆에 렌더링할 커스텀 액션입니다(예: Download/내보내기 버튼).
354
354
  * 이전에는 이 자리에 넣을 공식 prop이 없어서 소비자가 `.seed-data-table__toolbar`
@@ -357,7 +357,7 @@ interface DataTableProps<TRow extends {
357
357
  * 렌더링됩니다). 검색/정렬/필터보다 뒤, 카운트보다 앞(같은 그룹 안에서 카운트
358
358
  * 왼쪽)에 옵니다.
359
359
  */
360
- toolbarActions?: react.ReactNode;
360
+ toolbarActions?: React.ReactNode;
361
361
  /** 헤더의 열별 메뉴(이름 변경 / 숨기기 / 왼쪽·오른쪽으로 이동)를 켭니다, seed `Menu`. */
362
362
  columnMenu?: boolean;
363
363
  /** 헤더를 드래그해 열 순서를 바꿀 수 있게 합니다. @default true */
@@ -431,19 +431,19 @@ interface DataTableProps<TRow extends {
431
431
  /** 주어지면 맨 아래에 crisp가 측정한 "+ New" 추가 행 버튼을 렌더링합니다. */
432
432
  onAddRow?: () => void;
433
433
  /** 추가 행 버튼의 라벨입니다. @default "New record" */
434
- addRowLabel?: react.ReactNode;
434
+ addRowLabel?: React.ReactNode;
435
435
  /** 전체 본문을 이 상태로 대체합니다(로딩 아이콘 + `loadingLabel`) -- 필터/정렬
436
436
  * 재조회나 최초 로드처럼 행 전체가 아직 없을 때를 위한 것입니다. `error`/빈 상태
437
437
  * 보다 우선합니다. 인라인 스크롤 로딩 표시기(`onLoadMore`)와는 별개입니다. */
438
438
  loading?: boolean;
439
439
  /** `loading`일 때 스피너 옆에 보이는 텍스트입니다. @default "Loading..." */
440
- loadingLabel?: react.ReactNode;
440
+ loadingLabel?: React.ReactNode;
441
441
  /** 주어지면 행 대신 이 내용을 본문에 렌더링합니다(crisp의 `.crisp-datatable-empty`
442
442
  * 칸 재사용). `loading`보다는 아래, 빈 상태보다는 위 우선순위입니다. */
443
- error?: react.ReactNode;
443
+ error?: React.ReactNode;
444
444
  /** 검색/필터를 거치고도 행이 하나도 없을 때 본문에 렌더링할 내용입니다.
445
445
  * @default "No results." */
446
- empty?: react.ReactNode;
446
+ empty?: React.ReactNode;
447
447
  /**
448
448
  * Attio 스타일 무한 스크롤: 스크롤 컨테이너 바닥 근처의 sentinel 행이 뷰에 들어오면
449
449
  * 호출됩니다. `Promise`를 반환하면 그동안 자동으로 `loadingMore`가 켜집니다(직접
@@ -467,7 +467,7 @@ interface DataTableProps<TRow extends {
467
467
  * `style={{ height: "100%" }}` 같은 바운드를 주는 것만으로도 "전체 높이" 모드가
468
468
  * 됩니다 -- 새 prop 없이 기존 `style` 전달만으로 두 모드 다 지원됩니다.
469
469
  */
470
- scrollProps?: Omit<react.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
470
+ scrollProps?: Omit<React.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
471
471
  }
472
472
  /**
473
473
  * @see https://seed-design.io/react/components/data-table
@@ -475,8 +475,8 @@ interface DataTableProps<TRow extends {
475
475
  declare const DataTable: <TRow extends {
476
476
  id: string;
477
477
  }>(props: DataTableProps<TRow> & {
478
- ref?: react.ForwardedRef<HTMLDivElement>;
479
- }) => react.ReactElement;
478
+ ref?: React.ForwardedRef<HTMLDivElement>;
479
+ }) => React.ReactElement;
480
480
 
481
481
  interface DataTableViewsControlProps extends UseDataTableViewsProps {
482
482
  /** The trigger's label when no view is active. @default "Views" */
@@ -486,7 +486,7 @@ interface DataTableViewsControlProps extends UseDataTableViewsProps {
486
486
  * meaningful "no view" state to switch back to. */
487
487
  showClearRow?: boolean;
488
488
  /** The clear row's own label, when `showClearRow` is on. @default "label" */
489
- clearLabel?: react.ReactNode;
489
+ clearLabel?: React.ReactNode;
490
490
  /**
491
491
  * Reads the table's CURRENT column order/visibility/widths/pins + toolbar sort/
492
492
  * filter conditions -- called when the user picks "Save as new view" or "Update
@@ -522,7 +522,7 @@ declare namespace DataTableViewsControl {
522
522
  type ResizablePanelSide = "left" | "right";
523
523
 
524
524
  type ResizablePanelHandleVariant = "panel" | "column";
525
- type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children" | "onResize"> & {
525
+ type ResizablePanelProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "onResize"> & {
526
526
  /** Which edge of its container the panel is anchored to. @default "left" */
527
527
  side?: ResizablePanelSide;
528
528
  /** Controlled width, in px. */
@@ -542,13 +542,42 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
542
542
  */
543
543
  max?: number | string;
544
544
  /**
545
- * Visual variant -- BOTH are in-flow (a resizing/opening panel always
546
- * reserves real width from `spacer`, shrinking a `flex: 1` sibling
547
- * beside it; neither ever becomes a `position: fixed` viewport overlay).
548
- * "docked" (default) sits flush with a hairline border; "floating" is
549
- * Attio's own inset-card look (8px gutter, r12, the measured elevation).
545
+ * Layout model. "docked" (default) is IN-FLOW: `spacer` reserves the
546
+ * panel's real width, shrinking a `flex: 1` sibling beside it (e.g. a
547
+ * table), flush with a hairline border -- unchanged since this
548
+ * primitive's own first version.
549
+ *
550
+ * "floating" is a true OVERLAY, not a bigger gutter on the docked look:
551
+ * this component's own outer element takes itself OUT of flow entirely
552
+ * (`position: absolute; inset: 0`) and renders no `spacer` at all -- it
553
+ * reserves ZERO layout width, so a sibling beside it (a table) never
554
+ * shrinks or reflows when this opens or resizes. The panel itself is
555
+ * pinned to one edge of that `inset: 0` box, spans its full height, and
556
+ * only the corners facing INTO the container are rounded (a left-edge
557
+ * shadow for `side="right"`, mirrored for `side="left"`) -- this is
558
+ * `RecordPreview`'s own peek panel (your-moon/crisp#448 reversed a
559
+ * docked/reflow attempt at this exact spot: "table's action buttons
560
+ * moving" when the panel opened).
561
+ *
562
+ * Because the outer element is now `position: absolute`, the consumer's
563
+ * own immediate parent (wherever this component -- or `RecordPreview` --
564
+ * is rendered) MUST itself be a positioned element (typically
565
+ * `position: relative`) with a real height, e.g. the table region's own
566
+ * wrapper div. No flex-row wrapper is needed or wanted: the table can be
567
+ * an ordinary 100%-width block and this panel a plain sibling.
550
568
  */
551
569
  floating?: boolean;
570
+ /**
571
+ * An optional dimming backdrop behind a `floating` panel, covering the
572
+ * same `inset: 0` container -- OFF by default: this is a peek panel, not
573
+ * a modal, and Attio's own peek never dims the table it floats over.
574
+ * Meaningful only together with `floating`; ignored otherwise.
575
+ * @default false
576
+ */
577
+ backdrop?: boolean;
578
+ /** Fires when the optional `backdrop` is clicked. Typically wired to the
579
+ * same handler as the panel's own close control. */
580
+ onBackdropClick?: () => void;
552
581
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
553
582
  handleVariant?: ResizablePanelHandleVariant;
554
583
  /**
@@ -582,26 +611,25 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
582
611
  * observe `animationName` changes on the SAME node that carries
583
612
  * `data-state`/the keyframe animation above.
584
613
  */
585
- panelElementRef?: react.Ref<HTMLDivElement>;
614
+ panelElementRef?: React.Ref<HTMLDivElement>;
586
615
  className?: string;
587
- children?: react.ReactNode;
616
+ children?: React.ReactNode;
588
617
  };
589
618
  /**
590
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
591
- * panel that overlays it, so it can resize (or animate open/closed) without
592
- * a reflow glitch on the rest of the layout -- the spacer always reserves
593
- * the panel's real width, in BOTH the docked and floating visual variants
594
- * (floating changes only the panel's own look -- gutter, radius, shadow --
595
- * never its `position`, so a consumer laying this out as a row-flex sibling
596
- * of e.g. a table sees that table genuinely shrink to make room, matching
597
- * Attio's own measured peek). Drag the edge handle to resize; constraints
598
- * are CSS `min-width`/`max-width`, never re-clamped in JS. Ported from
599
- * your-moon/crisp's `ResizablePanel` (`components/resizablepanel/`);
600
- * state/drag behavior lives in `useResizablePanel`
601
- * (`@seed-design/react-resizable-panel`), this component owns the DOM +
602
- * recipe classes on top of it.
619
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
620
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
621
+ * width and the panel overlays exactly that reservation, so a resize drag
622
+ * never causes a reflow glitch on the rest of the layout. Floating: no
623
+ * spacer at all -- this component's own outer element becomes a
624
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
625
+ * panel itself pinned to one edge and spanning the full height of that box.
626
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
627
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
628
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
629
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
630
+ * component owns the DOM + recipe classes on top of it.
603
631
  */
604
- declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children" | "onResize"> & {
632
+ declare const ResizablePanel: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children" | "onResize"> & {
605
633
  /** Which edge of its container the panel is anchored to. @default "left" */
606
634
  side?: ResizablePanelSide;
607
635
  /** Controlled width, in px. */
@@ -621,13 +649,42 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
621
649
  */
622
650
  max?: number | string;
623
651
  /**
624
- * Visual variant -- BOTH are in-flow (a resizing/opening panel always
625
- * reserves real width from `spacer`, shrinking a `flex: 1` sibling
626
- * beside it; neither ever becomes a `position: fixed` viewport overlay).
627
- * "docked" (default) sits flush with a hairline border; "floating" is
628
- * Attio's own inset-card look (8px gutter, r12, the measured elevation).
652
+ * Layout model. "docked" (default) is IN-FLOW: `spacer` reserves the
653
+ * panel's real width, shrinking a `flex: 1` sibling beside it (e.g. a
654
+ * table), flush with a hairline border -- unchanged since this
655
+ * primitive's own first version.
656
+ *
657
+ * "floating" is a true OVERLAY, not a bigger gutter on the docked look:
658
+ * this component's own outer element takes itself OUT of flow entirely
659
+ * (`position: absolute; inset: 0`) and renders no `spacer` at all -- it
660
+ * reserves ZERO layout width, so a sibling beside it (a table) never
661
+ * shrinks or reflows when this opens or resizes. The panel itself is
662
+ * pinned to one edge of that `inset: 0` box, spans its full height, and
663
+ * only the corners facing INTO the container are rounded (a left-edge
664
+ * shadow for `side="right"`, mirrored for `side="left"`) -- this is
665
+ * `RecordPreview`'s own peek panel (your-moon/crisp#448 reversed a
666
+ * docked/reflow attempt at this exact spot: "table's action buttons
667
+ * moving" when the panel opened).
668
+ *
669
+ * Because the outer element is now `position: absolute`, the consumer's
670
+ * own immediate parent (wherever this component -- or `RecordPreview` --
671
+ * is rendered) MUST itself be a positioned element (typically
672
+ * `position: relative`) with a real height, e.g. the table region's own
673
+ * wrapper div. No flex-row wrapper is needed or wanted: the table can be
674
+ * an ordinary 100%-width block and this panel a plain sibling.
629
675
  */
630
676
  floating?: boolean;
677
+ /**
678
+ * An optional dimming backdrop behind a `floating` panel, covering the
679
+ * same `inset: 0` container -- OFF by default: this is a peek panel, not
680
+ * a modal, and Attio's own peek never dims the table it floats over.
681
+ * Meaningful only together with `floating`; ignored otherwise.
682
+ * @default false
683
+ */
684
+ backdrop?: boolean;
685
+ /** Fires when the optional `backdrop` is clicked. Typically wired to the
686
+ * same handler as the panel's own close control. */
687
+ onBackdropClick?: () => void;
631
688
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
632
689
  handleVariant?: ResizablePanelHandleVariant;
633
690
  /**
@@ -661,10 +718,10 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
661
718
  * observe `animationName` changes on the SAME node that carries
662
719
  * `data-state`/the keyframe animation above.
663
720
  */
664
- panelElementRef?: react.Ref<HTMLDivElement>;
721
+ panelElementRef?: React.Ref<HTMLDivElement>;
665
722
  className?: string;
666
- children?: react.ReactNode;
667
- } & react.RefAttributes<HTMLDivElement>>;
723
+ children?: React.ReactNode;
724
+ } & React.RefAttributes<HTMLDivElement>>;
668
725
 
669
726
  /** One Highlights card -- label, value and an optional icon, laid out the way
670
727
  * Attio's own peek panel lays its own out (your-moon/crisp#448, measured
@@ -673,8 +730,8 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
673
730
  type RecordPreviewHighlight = {
674
731
  key: string;
675
732
  label: string;
676
- value: react.ReactNode;
677
- icon?: react.ComponentType<{
733
+ value: React.ReactNode;
734
+ icon?: React.ComponentType<{
678
735
  size?: number;
679
736
  }>;
680
737
  };
@@ -687,7 +744,24 @@ type RecordPreviewSection = {
687
744
  key: string;
688
745
  label: string;
689
746
  count: number;
690
- content?: react.ReactNode;
747
+ content?: React.ReactNode;
748
+ };
749
+ /** One tab of the panel's own mini detail view (a "tabs" capability with no
750
+ * your-moon/crisp equivalent -- crisp's own peek only ever shows Highlights
751
+ * + Activity + sections, never a tab bar). Built on crispy-seed's own
752
+ * `Tabs` primitive (`../Tabs`, itself built on seed's `tabs` recipe/
753
+ * tokens) rather than a hand-rolled tab strip, so it matches every other
754
+ * tabbed surface in this package. `content` is only rendered lazily in the
755
+ * sense that `Tabs.Content` already handles (hidden, not unmounted, once a
756
+ * tab has been visited) -- see `Tabs.tsx`. */
757
+ type RecordPreviewTab = {
758
+ key: string;
759
+ label: React.ReactNode;
760
+ content: React.ReactNode;
761
+ /** Rendered right after `label` inside the tab trigger, e.g. an unread
762
+ * count -- purely presentational, this component attaches no meaning to
763
+ * it. */
764
+ badge?: React.ReactNode;
691
765
  };
692
766
  /** The record this panel previews. Only `name` is read directly here --
693
767
  * everything else the panel shows comes from `highlights`/`activity`/
@@ -699,7 +773,7 @@ type RecordPreviewRecord = {
699
773
  name?: string;
700
774
  [key: string]: unknown;
701
775
  };
702
- type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children" | "title" | "onResize"> & {
776
+ type RecordPreviewProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "title" | "onResize"> & {
703
777
  /** Whether the panel is mounted/visible. */
704
778
  open: boolean;
705
779
  onClose: () => void;
@@ -720,7 +794,7 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
720
794
  nextRecordLabel?: string;
721
795
  /** "2 of 37 in All Companies" (your-moon/crisp#413) -- a single
722
796
  * pre-formatted string; omit for no counter at all. */
723
- counterLabel?: react.ReactNode;
797
+ counterLabel?: React.ReactNode;
724
798
  /**
725
799
  * Lets a person drag the panel wider or narrower from its own left edge --
726
800
  * this port reuses `ResizablePanel`'s own handle (`side="right"`, whose
@@ -736,7 +810,7 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
736
810
  minWidth?: number;
737
811
  maxWidth?: number;
738
812
  /** The panel's heading, and so its accessible name. Defaults to `record.name`. */
739
- title?: react.ReactNode;
813
+ title?: React.ReactNode;
740
814
  /** Avatar image URL beside the title. Omit for an initials tile built from
741
815
  * `record.name`'s first character. */
742
816
  avatar?: string;
@@ -745,18 +819,49 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
745
819
  record: RecordPreviewRecord | null;
746
820
  /** Heading above the highlight cards (default "Highlights"). Omit the
747
821
  * whole section by omitting `highlights`. */
748
- highlightsLabel?: react.ReactNode;
822
+ highlightsLabel?: React.ReactNode;
749
823
  highlights?: RecordPreviewHighlight[];
750
824
  /** Heading above the activity feed (default "Activity"). */
751
- activityLabel?: react.ReactNode;
752
- viewAllActivityLabel?: react.ReactNode;
825
+ activityLabel?: React.ReactNode;
826
+ viewAllActivityLabel?: React.ReactNode;
753
827
  onViewAllActivity?: () => void;
754
- activity?: react.ReactNode;
828
+ activity?: React.ReactNode;
755
829
  /** Collapsed sections below Activity (your-moon/crisp#413). Omit for no
756
830
  * sections at all. */
757
831
  sections?: RecordPreviewSection[];
832
+ /**
833
+ * A "mini detail view" -- renders a seed-idiomatic tab bar under the
834
+ * header instead of (or ahead of) the plain Highlights/Activity/sections
835
+ * layout. Omit for no tabs at all (the default; existing consumers are
836
+ * unaffected). When `highlights`/`activity`/`sections` are ALSO given
837
+ * alongside `tabs`, they're combined rather than dropped: an extra
838
+ * `overviewTabLabel` tab (default "Overview") is prepended, ahead of
839
+ * these `tabs`, containing exactly what the non-tabs layout would have
840
+ * rendered -- so a consumer moving to tabs doesn't have to re-author its
841
+ * existing overview content as a tab by hand.
842
+ */
843
+ tabs?: RecordPreviewTab[];
844
+ /** Controlled active tab key. Pass with `onTabChange`; omit for
845
+ * uncontrolled (`defaultActiveTab`). Only meaningful with `tabs`. */
846
+ activeTab?: string;
847
+ /** Initial active tab key when uncontrolled. Defaults to the first tab
848
+ * (the synthesized "Overview" tab when highlights/activity/sections are
849
+ * combined in, otherwise the first entry of `tabs` itself). */
850
+ defaultActiveTab?: string;
851
+ onTabChange?: (key: string) => void;
852
+ /** Label for the synthesized overview tab -- see `tabs`'s own doc
853
+ * comment. @default "Overview" */
854
+ overviewTabLabel?: React.ReactNode;
758
855
  /** Rendered below the panel's content, e.g. an Edit button. */
759
- footer?: react.ReactNode;
856
+ footer?: React.ReactNode;
857
+ /**
858
+ * An optional dimming backdrop behind the panel -- OFF by default (this
859
+ * is a peek, not a modal: Attio's own peek never dims the table it
860
+ * floats over). Forwarded to `ResizablePanel`'s own `backdrop`; clicking
861
+ * it calls `onClose`, same as the panel's own close button.
862
+ * @default false
863
+ */
864
+ backdrop?: boolean;
760
865
  className?: string;
761
866
  };
762
867
  /**
@@ -765,10 +870,30 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
765
870
  * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
766
871
  * (`side="right" floating`) rather than crisp's own duplicate absolute-
767
872
  * positioning/slide-in/resize-handle implementation -- the outer shell (the
768
- * enter/exit keyframe animation, the floating elevation, the drag handle
873
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
769
874
  * itself) all come from that primitive; this component only renders the
770
875
  * CONTENT inside it.
771
876
  *
877
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
878
+ * now means a real `position: absolute` overlay that reserves ZERO layout
879
+ * width -- opening this panel never shrinks or reflows a table beside it
880
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
881
+ * exact spot, reported live as "table's action buttons moving"). Because of
882
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
883
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
884
+ * ordinary sibling of the table. The one requirement: the shared parent
885
+ * (the table region's own wrapper) must itself be a positioned element
886
+ * (typically `position: relative`) with a real height, since that's the
887
+ * containing block this panel's `inset: 0` resolves against. See
888
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
889
+ *
890
+ * ```tsx
891
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
892
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
893
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
894
+ * </div>
895
+ * ```
896
+ *
772
897
  * Enter/exit: closing this panel is a state change (`open` -> false) that
773
898
  * typically clears `record` in the SAME update (see the DataTable story's
774
899
  * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
@@ -785,4 +910,4 @@ declare namespace RecordPreview {
785
910
  }
786
911
 
787
912
  export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };
788
- export type { DataTableColumn, DataTableColumnKind, DataTableProps, DataTableView, DataTableViewState, DataTableViewsControlProps, RecordPreviewHighlight, RecordPreviewProps, RecordPreviewRecord, RecordPreviewSection, ResizablePanelHandleVariant, ResizablePanelProps, ResizablePanelSide, ToolbarFilter, ToolbarFilterOp, ToolbarSort, ToolbarSortDirection };
913
+ export type { DataTableColumn, DataTableColumnKind, DataTableProps, DataTableView, DataTableViewState, DataTableViewsControlProps, RecordPreviewHighlight, RecordPreviewProps, RecordPreviewRecord, RecordPreviewSection, RecordPreviewTab, ResizablePanelHandleVariant, ResizablePanelProps, ResizablePanelSide, ToolbarFilter, ToolbarFilterOp, ToolbarSort, ToolbarSortDirection };