@crispy-seed/data-table 0.8.0 → 0.10.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
@@ -114,7 +114,7 @@ interface UseDataTableViewsProps {
114
114
  storageKey?: string;
115
115
  }
116
116
 
117
- type DataTableCellType = "text" | "entity" | "url" | "email" | "tags" | "status" | "number" | "currency" | "date" | "datetime" | "rating" | "checkbox";
117
+ type DataTableCellType = "text" | "entity" | "url" | "email" | "tags" | "status" | "number" | "currency" | "date" | "datetime" | "rating" | "checkbox" | "chip" | "chip-list";
118
118
 
119
119
  /** A `select`/`reference`/`actor` editor's own choice list -- `value`/`label` are
120
120
  * required (the committed string and what the row reads), `avatar` is read only by
@@ -269,6 +269,14 @@ interface DataTableColumn<TRow> {
269
269
  onOpenPreview?: (row: TRow) => void;
270
270
  /** `onOpenPreview` 아이콘의 aria-label입니다. @default "Open preview" */
271
271
  openPreviewLabel?: string;
272
+ /**
273
+ * 셀 텍스트 옆에 고정으로 붙는 접미(trailing) 아이콘입니다 (예: 링크 열의 외부 링크
274
+ * 아이콘, 위치 열의 핀 아이콘) -- 텍스트가 길어져도 이 아이콘은 잘리거나 사라지지
275
+ * 않습니다 (cells.tsx의 `CellWithSuffix`: 텍스트 쪽만 `flex:1;min-width:0`로
276
+ * 줄임표 처리되고, 아이콘은 `flex-shrink:0`). `type: "text"`(기본)와 `"url"`/
277
+ * `"email"` 열에서 동작합니다.
278
+ */
279
+ suffixIcon?: (row: TRow) => react.ReactNode;
272
280
  }
273
281
  interface DataTableProps<TRow extends {
274
282
  id: string;
@@ -542,13 +550,42 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
542
550
  */
543
551
  max?: number | string;
544
552
  /**
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).
553
+ * Layout model. "docked" (default) is IN-FLOW: `spacer` reserves the
554
+ * panel's real width, shrinking a `flex: 1` sibling beside it (e.g. a
555
+ * table), flush with a hairline border -- unchanged since this
556
+ * primitive's own first version.
557
+ *
558
+ * "floating" is a true OVERLAY, not a bigger gutter on the docked look:
559
+ * this component's own outer element takes itself OUT of flow entirely
560
+ * (`position: absolute; inset: 0`) and renders no `spacer` at all -- it
561
+ * reserves ZERO layout width, so a sibling beside it (a table) never
562
+ * shrinks or reflows when this opens or resizes. The panel itself is
563
+ * pinned to one edge of that `inset: 0` box, spans its full height, and
564
+ * only the corners facing INTO the container are rounded (a left-edge
565
+ * shadow for `side="right"`, mirrored for `side="left"`) -- this is
566
+ * `RecordPreview`'s own peek panel (your-moon/crisp#448 reversed a
567
+ * docked/reflow attempt at this exact spot: "table's action buttons
568
+ * moving" when the panel opened).
569
+ *
570
+ * Because the outer element is now `position: absolute`, the consumer's
571
+ * own immediate parent (wherever this component -- or `RecordPreview` --
572
+ * is rendered) MUST itself be a positioned element (typically
573
+ * `position: relative`) with a real height, e.g. the table region's own
574
+ * wrapper div. No flex-row wrapper is needed or wanted: the table can be
575
+ * an ordinary 100%-width block and this panel a plain sibling.
550
576
  */
551
577
  floating?: boolean;
578
+ /**
579
+ * An optional dimming backdrop behind a `floating` panel, covering the
580
+ * same `inset: 0` container -- OFF by default: this is a peek panel, not
581
+ * a modal, and Attio's own peek never dims the table it floats over.
582
+ * Meaningful only together with `floating`; ignored otherwise.
583
+ * @default false
584
+ */
585
+ backdrop?: boolean;
586
+ /** Fires when the optional `backdrop` is clicked. Typically wired to the
587
+ * same handler as the panel's own close control. */
588
+ onBackdropClick?: () => void;
552
589
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
553
590
  handleVariant?: ResizablePanelHandleVariant;
554
591
  /**
@@ -587,19 +624,18 @@ type ResizablePanelProps = Omit<react.ComponentPropsWithoutRef<"div">, "children
587
624
  children?: react.ReactNode;
588
625
  };
589
626
  /**
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.
627
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
628
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
629
+ * width and the panel overlays exactly that reservation, so a resize drag
630
+ * never causes a reflow glitch on the rest of the layout. Floating: no
631
+ * spacer at all -- this component's own outer element becomes a
632
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
633
+ * panel itself pinned to one edge and spanning the full height of that box.
634
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
635
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
636
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
637
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
638
+ * component owns the DOM + recipe classes on top of it.
603
639
  */
604
640
  declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children" | "onResize"> & {
605
641
  /** Which edge of its container the panel is anchored to. @default "left" */
@@ -621,13 +657,42 @@ declare const ResizablePanel: react.ForwardRefExoticComponent<Omit<Omit<react.De
621
657
  */
622
658
  max?: number | string;
623
659
  /**
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).
660
+ * Layout model. "docked" (default) is IN-FLOW: `spacer` reserves the
661
+ * panel's real width, shrinking a `flex: 1` sibling beside it (e.g. a
662
+ * table), flush with a hairline border -- unchanged since this
663
+ * primitive's own first version.
664
+ *
665
+ * "floating" is a true OVERLAY, not a bigger gutter on the docked look:
666
+ * this component's own outer element takes itself OUT of flow entirely
667
+ * (`position: absolute; inset: 0`) and renders no `spacer` at all -- it
668
+ * reserves ZERO layout width, so a sibling beside it (a table) never
669
+ * shrinks or reflows when this opens or resizes. The panel itself is
670
+ * pinned to one edge of that `inset: 0` box, spans its full height, and
671
+ * only the corners facing INTO the container are rounded (a left-edge
672
+ * shadow for `side="right"`, mirrored for `side="left"`) -- this is
673
+ * `RecordPreview`'s own peek panel (your-moon/crisp#448 reversed a
674
+ * docked/reflow attempt at this exact spot: "table's action buttons
675
+ * moving" when the panel opened).
676
+ *
677
+ * Because the outer element is now `position: absolute`, the consumer's
678
+ * own immediate parent (wherever this component -- or `RecordPreview` --
679
+ * is rendered) MUST itself be a positioned element (typically
680
+ * `position: relative`) with a real height, e.g. the table region's own
681
+ * wrapper div. No flex-row wrapper is needed or wanted: the table can be
682
+ * an ordinary 100%-width block and this panel a plain sibling.
629
683
  */
630
684
  floating?: boolean;
685
+ /**
686
+ * An optional dimming backdrop behind a `floating` panel, covering the
687
+ * same `inset: 0` container -- OFF by default: this is a peek panel, not
688
+ * a modal, and Attio's own peek never dims the table it floats over.
689
+ * Meaningful only together with `floating`; ignored otherwise.
690
+ * @default false
691
+ */
692
+ backdrop?: boolean;
693
+ /** Fires when the optional `backdrop` is clicked. Typically wired to the
694
+ * same handler as the panel's own close control. */
695
+ onBackdropClick?: () => void;
631
696
  /** Resize-handle geometry -- "panel" (default) or "column" (table edges). */
632
697
  handleVariant?: ResizablePanelHandleVariant;
633
698
  /**
@@ -689,6 +754,23 @@ type RecordPreviewSection = {
689
754
  count: number;
690
755
  content?: react.ReactNode;
691
756
  };
757
+ /** One tab of the panel's own mini detail view (a "tabs" capability with no
758
+ * your-moon/crisp equivalent -- crisp's own peek only ever shows Highlights
759
+ * + Activity + sections, never a tab bar). Built on crispy-seed's own
760
+ * `Tabs` primitive (`../Tabs`, itself built on seed's `tabs` recipe/
761
+ * tokens) rather than a hand-rolled tab strip, so it matches every other
762
+ * tabbed surface in this package. `content` is only rendered lazily in the
763
+ * sense that `Tabs.Content` already handles (hidden, not unmounted, once a
764
+ * tab has been visited) -- see `Tabs.tsx`. */
765
+ type RecordPreviewTab = {
766
+ key: string;
767
+ label: react.ReactNode;
768
+ content: react.ReactNode;
769
+ /** Rendered right after `label` inside the tab trigger, e.g. an unread
770
+ * count -- purely presentational, this component attaches no meaning to
771
+ * it. */
772
+ badge?: react.ReactNode;
773
+ };
692
774
  /** The record this panel previews. Only `name` is read directly here --
693
775
  * everything else the panel shows comes from `highlights`/`activity`/
694
776
  * `sections`, the consumer's own data. Unlike crisp's own `RecordPanel`
@@ -755,8 +837,39 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
755
837
  /** Collapsed sections below Activity (your-moon/crisp#413). Omit for no
756
838
  * sections at all. */
757
839
  sections?: RecordPreviewSection[];
840
+ /**
841
+ * A "mini detail view" -- renders a seed-idiomatic tab bar under the
842
+ * header instead of (or ahead of) the plain Highlights/Activity/sections
843
+ * layout. Omit for no tabs at all (the default; existing consumers are
844
+ * unaffected). When `highlights`/`activity`/`sections` are ALSO given
845
+ * alongside `tabs`, they're combined rather than dropped: an extra
846
+ * `overviewTabLabel` tab (default "Overview") is prepended, ahead of
847
+ * these `tabs`, containing exactly what the non-tabs layout would have
848
+ * rendered -- so a consumer moving to tabs doesn't have to re-author its
849
+ * existing overview content as a tab by hand.
850
+ */
851
+ tabs?: RecordPreviewTab[];
852
+ /** Controlled active tab key. Pass with `onTabChange`; omit for
853
+ * uncontrolled (`defaultActiveTab`). Only meaningful with `tabs`. */
854
+ activeTab?: string;
855
+ /** Initial active tab key when uncontrolled. Defaults to the first tab
856
+ * (the synthesized "Overview" tab when highlights/activity/sections are
857
+ * combined in, otherwise the first entry of `tabs` itself). */
858
+ defaultActiveTab?: string;
859
+ onTabChange?: (key: string) => void;
860
+ /** Label for the synthesized overview tab -- see `tabs`'s own doc
861
+ * comment. @default "Overview" */
862
+ overviewTabLabel?: react.ReactNode;
758
863
  /** Rendered below the panel's content, e.g. an Edit button. */
759
864
  footer?: react.ReactNode;
865
+ /**
866
+ * An optional dimming backdrop behind the panel -- OFF by default (this
867
+ * is a peek, not a modal: Attio's own peek never dims the table it
868
+ * floats over). Forwarded to `ResizablePanel`'s own `backdrop`; clicking
869
+ * it calls `onClose`, same as the panel's own close button.
870
+ * @default false
871
+ */
872
+ backdrop?: boolean;
760
873
  className?: string;
761
874
  };
762
875
  /**
@@ -765,10 +878,30 @@ type RecordPreviewProps = Omit<react.ComponentPropsWithoutRef<"div">, "children"
765
878
  * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
766
879
  * (`side="right" floating`) rather than crisp's own duplicate absolute-
767
880
  * positioning/slide-in/resize-handle implementation -- the outer shell (the
768
- * enter/exit keyframe animation, the floating elevation, the drag handle
881
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
769
882
  * itself) all come from that primitive; this component only renders the
770
883
  * CONTENT inside it.
771
884
  *
885
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
886
+ * now means a real `position: absolute` overlay that reserves ZERO layout
887
+ * width -- opening this panel never shrinks or reflows a table beside it
888
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
889
+ * exact spot, reported live as "table's action buttons moving"). Because of
890
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
891
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
892
+ * ordinary sibling of the table. The one requirement: the shared parent
893
+ * (the table region's own wrapper) must itself be a positioned element
894
+ * (typically `position: relative`) with a real height, since that's the
895
+ * containing block this panel's `inset: 0` resolves against. See
896
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
897
+ *
898
+ * ```tsx
899
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
900
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
901
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
902
+ * </div>
903
+ * ```
904
+ *
772
905
  * Enter/exit: closing this panel is a state change (`open` -> false) that
773
906
  * typically clears `record` in the SAME update (see the DataTable story's
774
907
  * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
@@ -785,4 +918,4 @@ declare namespace RecordPreview {
785
918
  }
786
919
 
787
920
  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 };
921
+ export type { DataTableColumn, DataTableColumnKind, DataTableProps, DataTableView, DataTableViewState, DataTableViewsControlProps, RecordPreviewHighlight, RecordPreviewProps, RecordPreviewRecord, RecordPreviewSection, RecordPreviewTab, ResizablePanelHandleVariant, ResizablePanelProps, ResizablePanelSide, ToolbarFilter, ToolbarFilterOp, ToolbarSort, ToolbarSortDirection };