@crispy-seed/data-table 0.7.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 {
@@ -57,15 +57,18 @@ interface ToolbarFilter {
57
57
  }
58
58
 
59
59
  /**
60
- * The six aggregations DATATABLE-ATTIO-SPEC.md's ColumnCalc footer offers (crisp's own
61
- * `ColumnCalc` ships nine -- adding percent-empty/-filled and a "none" clearing op --
62
- * but this table's contract is exactly the six named in the spec doc: count /
63
- * countFilled / sum / average / min / max). `count`/`countFilled` apply to every
64
- * column; `sum`/`average`/`min`/`max` only make sense on a numeric column, so the
65
- * styled layer only offers those four when the column's cell type is number/currency
66
- * (see DataTable.tsx's `ColumnCalcMenu`).
60
+ * Live-review fix (Attio/GRC parity): the ColumnCalc footer's own aggregation set --
61
+ * three apply to every column, six more to numeric ones, one more to non-numeric ones.
62
+ * Distinct from crisp's own `ColumnCalc` (`countEmpty`/`countFilled`/`percentEmpty`/
63
+ * `percentFilled` + sum/average/min/max + "none") -- crisp's own set was read for the
64
+ * MENU/trigger interaction (`column-calc.tsx`'s own doc comment), not copied op-for-op;
65
+ * this table's contract is the ten named directly in DATATABLE-ATTIO-SPEC.md/the GRC
66
+ * feature contract: count / empty / not-empty everywhere, sum / average / median / min
67
+ * / max / range on a numeric column, count-unique on a non-numeric one. The styled
68
+ * layer picks which extra set to offer off the column's own cell type (`column-calc.tsx`'s
69
+ * `ColumnCalcCell`).
67
70
  */
68
- type DataTableCalcOp = "count" | "countFilled" | "sum" | "average" | "min" | "max";
71
+ type DataTableCalcOp = "count" | "empty" | "notEmpty" | "sum" | "average" | "median" | "min" | "max" | "range" | "countUnique";
69
72
 
70
73
  /** Everything a saved view actually captures -- deliberately the same five controlled
71
74
  * props `useDataTableColumns` + the toolbar sort/filter hooks already expose, so
@@ -135,7 +138,7 @@ interface DataTableColumn<TRow> {
135
138
  */
136
139
  key: string;
137
140
  /** 헤더 셀에 표시할 내용입니다. */
138
- header: react.ReactNode;
141
+ header: React.ReactNode;
139
142
  /** 이 열을 클릭해 정렬할 수 있는지 여부입니다. */
140
143
  sortable?: boolean;
141
144
  /**
@@ -146,7 +149,7 @@ interface DataTableColumn<TRow> {
146
149
  /** 셀 내용을 직접 그립니다. 지정하면 `type`의 기본 렌더러 대신 이것을 사용합니다.
147
150
  * `editable`이어도 편집/always-live 상태에는 영향이 없습니다 -- 이 필드는 REST
148
151
  * 상태의 표시만 바꿉니다. */
149
- render?: (row: TRow) => react.ReactNode;
152
+ render?: (row: TRow) => React.ReactNode;
150
153
  /** `type: "currency"`의 통화 기호입니다. @default "$" */
151
154
  symbol?: string;
152
155
  /** `type: "rating"`의 별 개수입니다. @default 5 */
@@ -269,7 +272,7 @@ interface DataTableColumn<TRow> {
269
272
  }
270
273
  interface DataTableProps<TRow extends {
271
274
  id: string;
272
- }> extends DataTableVariantProps, Omit<PrimitiveProps, "asChild">, Omit<react.HTMLAttributes<HTMLDivElement>, "children"> {
275
+ }> extends DataTableVariantProps, Omit<PrimitiveProps, "asChild">, Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
273
276
  columns: DataTableColumn<TRow>[];
274
277
  data: TRow[];
275
278
  /** `type`이 있는 열의 number/currency/date/datetime 표시에 쓰는 로캘입니다. @default "en-US" */
@@ -278,17 +281,18 @@ interface DataTableProps<TRow extends {
278
281
  sort?: DataTableSortState;
279
282
  /** 비제어 방식으로 사용할 때의 초기 정렬 상태입니다. */
280
283
  defaultSort?: DataTableSortState;
281
- /** 정렬 가능한 열을 클릭해 정렬 상태가 바뀔 때 호출됩니다. */
282
- onSortChange?: (sort: DataTableSortState) => void;
284
+ /** 정렬 가능한 열을 클릭해 정렬 상태가 바뀔 때, 그리고 열 메뉴의 "Clear sort"로
285
+ * 정렬이 지워질 때 호출됩니다 -- 지워진 상태는 `undefined`로 전달됩니다. */
286
+ onSortChange?: (sort: DataTableSortState | undefined) => void;
283
287
  /**
284
288
  * 정렬 아이콘입니다. `idle`은 정렬 가능하지만 아직 정렬되지 않은 열의 헤더에
285
289
  * (호버 시에만 드러나는) 기본 표시로 쓰이고, `asc`/`desc`는 실제로 정렬된 열에
286
290
  * 씁니다. 생략하면 karrot 아이콘 셋의 chevron을 씁니다.
287
291
  */
288
292
  sortIcon?: {
289
- idle?: react.ReactNode;
290
- asc?: react.ReactNode;
291
- desc?: react.ReactNode;
293
+ idle?: React.ReactNode;
294
+ asc?: React.ReactNode;
295
+ desc?: React.ReactNode;
292
296
  };
293
297
  /** 왼쪽에 40px짜리 체크박스 선택 열을 추가합니다. */
294
298
  selectable?: boolean;
@@ -304,10 +308,10 @@ interface DataTableProps<TRow extends {
304
308
  getRowSelectionLabel?: (row: TRow) => string;
305
309
  /** 선택 체크박스가 checked일 때 표시할 아이콘입니다. 생략하면 karrot의 체크 아이콘을
306
310
  * 씁니다. */
307
- selectionCheckedIcon?: react.ReactNode;
311
+ selectionCheckedIcon?: React.ReactNode;
308
312
  /** 전체 선택 체크박스가 일부만 선택됐을 때(indeterminate) 표시할 아이콘입니다.
309
313
  * 생략하면 karrot의 minus 아이콘을 씁니다. */
310
- selectionIndeterminateIcon?: react.ReactNode;
314
+ selectionIndeterminateIcon?: React.ReactNode;
311
315
  /** 제어할 검색어입니다. */
312
316
  query?: string;
313
317
  /** 비제어 방식으로 사용할 때의 초기 검색어입니다. */
@@ -344,7 +348,7 @@ interface DataTableProps<TRow extends {
344
348
  * 행 개수 텍스트를 직접 만듭니다. 생략하면 crisp의 기본 형식을 씁니다: 검색/필터로
345
349
  * 줄지 않았으면 "N rows"(단수는 "row"), 줄었으면 "N of M".
346
350
  */
347
- countLabel?: (shown: number, total: number) => react.ReactNode;
351
+ countLabel?: (shown: number, total: number) => React.ReactNode;
348
352
  /**
349
353
  * 툴바 오른쪽, 행 개수 옆에 렌더링할 커스텀 액션입니다(예: Download/내보내기 버튼).
350
354
  * 이전에는 이 자리에 넣을 공식 prop이 없어서 소비자가 `.seed-data-table__toolbar`
@@ -353,7 +357,7 @@ interface DataTableProps<TRow extends {
353
357
  * 렌더링됩니다). 검색/정렬/필터보다 뒤, 카운트보다 앞(같은 그룹 안에서 카운트
354
358
  * 왼쪽)에 옵니다.
355
359
  */
356
- toolbarActions?: react.ReactNode;
360
+ toolbarActions?: React.ReactNode;
357
361
  /** 헤더의 열별 메뉴(이름 변경 / 숨기기 / 왼쪽·오른쪽으로 이동)를 켭니다, seed `Menu`. */
358
362
  columnMenu?: boolean;
359
363
  /** 헤더를 드래그해 열 순서를 바꿀 수 있게 합니다. @default true */
@@ -427,19 +431,19 @@ interface DataTableProps<TRow extends {
427
431
  /** 주어지면 맨 아래에 crisp가 측정한 "+ New" 추가 행 버튼을 렌더링합니다. */
428
432
  onAddRow?: () => void;
429
433
  /** 추가 행 버튼의 라벨입니다. @default "New record" */
430
- addRowLabel?: react.ReactNode;
434
+ addRowLabel?: React.ReactNode;
431
435
  /** 전체 본문을 이 상태로 대체합니다(로딩 아이콘 + `loadingLabel`) -- 필터/정렬
432
436
  * 재조회나 최초 로드처럼 행 전체가 아직 없을 때를 위한 것입니다. `error`/빈 상태
433
437
  * 보다 우선합니다. 인라인 스크롤 로딩 표시기(`onLoadMore`)와는 별개입니다. */
434
438
  loading?: boolean;
435
439
  /** `loading`일 때 스피너 옆에 보이는 텍스트입니다. @default "Loading..." */
436
- loadingLabel?: react.ReactNode;
440
+ loadingLabel?: React.ReactNode;
437
441
  /** 주어지면 행 대신 이 내용을 본문에 렌더링합니다(crisp의 `.crisp-datatable-empty`
438
442
  * 칸 재사용). `loading`보다는 아래, 빈 상태보다는 위 우선순위입니다. */
439
- error?: react.ReactNode;
443
+ error?: React.ReactNode;
440
444
  /** 검색/필터를 거치고도 행이 하나도 없을 때 본문에 렌더링할 내용입니다.
441
445
  * @default "No results." */
442
- empty?: react.ReactNode;
446
+ empty?: React.ReactNode;
443
447
  /**
444
448
  * Attio 스타일 무한 스크롤: 스크롤 컨테이너 바닥 근처의 sentinel 행이 뷰에 들어오면
445
449
  * 호출됩니다. `Promise`를 반환하면 그동안 자동으로 `loadingMore`가 켜집니다(직접
@@ -463,7 +467,7 @@ interface DataTableProps<TRow extends {
463
467
  * `style={{ height: "100%" }}` 같은 바운드를 주는 것만으로도 "전체 높이" 모드가
464
468
  * 됩니다 -- 새 prop 없이 기존 `style` 전달만으로 두 모드 다 지원됩니다.
465
469
  */
466
- scrollProps?: Omit<react.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
470
+ scrollProps?: Omit<React.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
467
471
  }
468
472
  /**
469
473
  * @see https://seed-design.io/react/components/data-table
@@ -471,8 +475,8 @@ interface DataTableProps<TRow extends {
471
475
  declare const DataTable: <TRow extends {
472
476
  id: string;
473
477
  }>(props: DataTableProps<TRow> & {
474
- ref?: react.ForwardedRef<HTMLDivElement>;
475
- }) => react.ReactElement;
478
+ ref?: React.ForwardedRef<HTMLDivElement>;
479
+ }) => React.ReactElement;
476
480
 
477
481
  interface DataTableViewsControlProps extends UseDataTableViewsProps {
478
482
  /** The trigger's label when no view is active. @default "Views" */
@@ -482,7 +486,7 @@ interface DataTableViewsControlProps extends UseDataTableViewsProps {
482
486
  * meaningful "no view" state to switch back to. */
483
487
  showClearRow?: boolean;
484
488
  /** The clear row's own label, when `showClearRow` is on. @default "label" */
485
- clearLabel?: react.ReactNode;
489
+ clearLabel?: React.ReactNode;
486
490
  /**
487
491
  * Reads the table's CURRENT column order/visibility/widths/pins + toolbar sort/
488
492
  * filter conditions -- called when the user picks "Save as new view" or "Update
@@ -518,7 +522,7 @@ declare namespace DataTableViewsControl {
518
522
  type ResizablePanelSide = "left" | "right";
519
523
 
520
524
  type ResizablePanelHandleVariant = "panel" | "column";
521
- type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children" | "onResize"> & {
525
+ type ResizablePanelProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "onResize"> & {
522
526
  /** Which edge of its container the panel is anchored to. @default "left" */
523
527
  side?: ResizablePanelSide;
524
528
  /** Controlled width, in px. */
@@ -538,11 +542,42 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
538
542
  */
539
543
  max?: number | string;
540
544
  /**
541
- * Layout model: "docked" (default) reserves flow width and sits in-line
542
- * with a hairline border; "floating" takes the panel out of flow
543
- * (`position: fixed`, an 8px gutter, r10, the measured floating shadow).
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.
544
568
  */
545
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;
546
581
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
547
582
  handleVariant?: ResizablePanelHandleVariant;
548
583
  /**
@@ -559,20 +594,42 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
559
594
  * `width` is uncontrolled.
560
595
  */
561
596
  storageKey?: string;
597
+ /**
598
+ * Drives the floating panel's own enter/exit keyframe animation (only
599
+ * meaningful together with `floating`) -- "open" plays the slide-and-fade
600
+ * in on mount, "closed" plays it in reverse. A consumer that keeps this
601
+ * component mounted through its own close transition (e.g.
602
+ * `@seed-design/react-presence`'s `usePresence`, the same idiom Dialog/
603
+ * Drawer use) flips this once `open` goes false, then unmounts once that
604
+ * hook reports the exit animation finished. Omit for a panel that
605
+ * shouldn't animate at all.
606
+ */
607
+ animationState?: "open" | "closed";
608
+ /**
609
+ * Ref to the panel element itself (not the outer root/spacer wrapper this
610
+ * component's own `ref` targets) -- lets a consumer's presence hook
611
+ * observe `animationName` changes on the SAME node that carries
612
+ * `data-state`/the keyframe animation above.
613
+ */
614
+ panelElementRef?: React.Ref<HTMLDivElement>;
562
615
  className?: string;
563
- children?: react.ReactNode;
616
+ children?: React.ReactNode;
564
617
  };
565
618
  /**
566
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
567
- * panel that overlays it, so the panel can move between docked (in-flow) and
568
- * floating (`position: fixed`, an overlay) without a reflow glitch. Drag the
569
- * edge handle to resize; constraints are CSS `min-width`/`max-width`, never
570
- * re-clamped in JS. Ported from your-moon/crisp's `ResizablePanel`
571
- * (`components/resizablepanel/`); state/drag behavior lives in
572
- * `useResizablePanel` (`@seed-design/react-resizable-panel`), this component
573
- * owns the DOM + 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.
574
631
  */
575
- 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"> & {
576
633
  /** Which edge of its container the panel is anchored to. @default "left" */
577
634
  side?: ResizablePanelSide;
578
635
  /** Controlled width, in px. */
@@ -592,11 +649,42 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
592
649
  */
593
650
  max?: number | string;
594
651
  /**
595
- * Layout model: "docked" (default) reserves flow width and sits in-line
596
- * with a hairline border; "floating" takes the panel out of flow
597
- * (`position: fixed`, an 8px gutter, r10, the measured floating shadow).
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.
598
675
  */
599
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;
600
688
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
601
689
  handleVariant?: ResizablePanelHandleVariant;
602
690
  /**
@@ -613,9 +701,27 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
613
701
  * `width` is uncontrolled.
614
702
  */
615
703
  storageKey?: string;
704
+ /**
705
+ * Drives the floating panel's own enter/exit keyframe animation (only
706
+ * meaningful together with `floating`) -- "open" plays the slide-and-fade
707
+ * in on mount, "closed" plays it in reverse. A consumer that keeps this
708
+ * component mounted through its own close transition (e.g.
709
+ * `@seed-design/react-presence`'s `usePresence`, the same idiom Dialog/
710
+ * Drawer use) flips this once `open` goes false, then unmounts once that
711
+ * hook reports the exit animation finished. Omit for a panel that
712
+ * shouldn't animate at all.
713
+ */
714
+ animationState?: "open" | "closed";
715
+ /**
716
+ * Ref to the panel element itself (not the outer root/spacer wrapper this
717
+ * component's own `ref` targets) -- lets a consumer's presence hook
718
+ * observe `animationName` changes on the SAME node that carries
719
+ * `data-state`/the keyframe animation above.
720
+ */
721
+ panelElementRef?: React.Ref<HTMLDivElement>;
616
722
  className?: string;
617
- children?: react.ReactNode;
618
- } & react.RefAttributes<HTMLDivElement>>;
723
+ children?: React.ReactNode;
724
+ } & React.RefAttributes<HTMLDivElement>>;
619
725
 
620
726
  /** One Highlights card -- label, value and an optional icon, laid out the way
621
727
  * Attio's own peek panel lays its own out (your-moon/crisp#448, measured
@@ -624,8 +730,8 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
624
730
  type RecordPreviewHighlight = {
625
731
  key: string;
626
732
  label: string;
627
- value: react.ReactNode;
628
- icon?: react.ComponentType<{
733
+ value: React.ReactNode;
734
+ icon?: React.ComponentType<{
629
735
  size?: number;
630
736
  }>;
631
737
  };
@@ -638,7 +744,24 @@ type RecordPreviewSection = {
638
744
  key: string;
639
745
  label: string;
640
746
  count: number;
641
- 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;
642
765
  };
643
766
  /** The record this panel previews. Only `name` is read directly here --
644
767
  * everything else the panel shows comes from `highlights`/`activity`/
@@ -650,7 +773,7 @@ type RecordPreviewRecord = {
650
773
  name?: string;
651
774
  [key: string]: unknown;
652
775
  };
653
- type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children" | "title" | "onResize"> & {
776
+ type RecordPreviewProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "title" | "onResize"> & {
654
777
  /** Whether the panel is mounted/visible. */
655
778
  open: boolean;
656
779
  onClose: () => void;
@@ -671,7 +794,7 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
671
794
  nextRecordLabel?: string;
672
795
  /** "2 of 37 in All Companies" (your-moon/crisp#413) -- a single
673
796
  * pre-formatted string; omit for no counter at all. */
674
- counterLabel?: react.ReactNode;
797
+ counterLabel?: React.ReactNode;
675
798
  /**
676
799
  * Lets a person drag the panel wider or narrower from its own left edge --
677
800
  * this port reuses `ResizablePanel`'s own handle (`side="right"`, whose
@@ -687,7 +810,7 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
687
810
  minWidth?: number;
688
811
  maxWidth?: number;
689
812
  /** The panel's heading, and so its accessible name. Defaults to `record.name`. */
690
- title?: react.ReactNode;
813
+ title?: React.ReactNode;
691
814
  /** Avatar image URL beside the title. Omit for an initials tile built from
692
815
  * `record.name`'s first character. */
693
816
  avatar?: string;
@@ -696,18 +819,49 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
696
819
  record: RecordPreviewRecord | null;
697
820
  /** Heading above the highlight cards (default "Highlights"). Omit the
698
821
  * whole section by omitting `highlights`. */
699
- highlightsLabel?: react.ReactNode;
822
+ highlightsLabel?: React.ReactNode;
700
823
  highlights?: RecordPreviewHighlight[];
701
824
  /** Heading above the activity feed (default "Activity"). */
702
- activityLabel?: react.ReactNode;
703
- viewAllActivityLabel?: react.ReactNode;
825
+ activityLabel?: React.ReactNode;
826
+ viewAllActivityLabel?: React.ReactNode;
704
827
  onViewAllActivity?: () => void;
705
- activity?: react.ReactNode;
828
+ activity?: React.ReactNode;
706
829
  /** Collapsed sections below Activity (your-moon/crisp#413). Omit for no
707
830
  * sections at all. */
708
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;
709
855
  /** Rendered below the panel's content, e.g. an Edit button. */
710
- 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;
711
865
  className?: string;
712
866
  };
713
867
  /**
@@ -716,14 +870,44 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
716
870
  * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
717
871
  * (`side="right" floating`) rather than crisp's own duplicate absolute-
718
872
  * positioning/slide-in/resize-handle implementation -- the outer shell (the
719
- * `@starting-style` slide-in, the floating elevation, the drag handle
873
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
720
874
  * itself) all come from that primitive; this component only renders the
721
875
  * CONTENT inside it.
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
+ *
897
+ * Enter/exit: closing this panel is a state change (`open` -> false) that
898
+ * typically clears `record` in the SAME update (see the DataTable story's
899
+ * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
900
+ * animation should play, the values this component would render are
901
+ * already gone. `usePresence` (`@seed-design/react-presence`, the same
902
+ * idiom Dialog/Drawer already use for their own floating layers) keeps the
903
+ * tree mounted for exactly the exit animation's duration; the snapshot
904
+ * below keeps the LAST visible props around for that same window, since
905
+ * without it the panel would animate out empty.
722
906
  */
723
- declare function RecordPreview({ open, record, onClose, className, closeLabel, onOpenRecord, openRecordLabel, onPrevious, previousRecordLabel, onNext, nextRecordLabel, title, avatar, highlightsLabel, highlights, counterLabel, resizable, width, defaultWidth, onWidthChange, minWidth, maxWidth, activityLabel, viewAllActivityLabel, onViewAllActivity, activity, sections, footer, ...rest }: RecordPreviewProps): react_jsx_runtime.JSX.Element | null;
907
+ declare function RecordPreview(props: RecordPreviewProps): react_jsx_runtime.JSX.Element | null;
724
908
  declare namespace RecordPreview {
725
909
  var displayName: string;
726
910
  }
727
911
 
728
912
  export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };
729
- 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 };