@wallarm-org/design-system 0.23.1 → 0.23.2

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.
Files changed (27) hide show
  1. package/dist/components/Attribute/Attribute.js +5 -8
  2. package/dist/components/SplitButton/classes.js +3 -2
  3. package/dist/components/Table/Table.js +11 -15
  4. package/dist/components/Table/TableBody/TableBodyCell.js +6 -14
  5. package/dist/components/Table/TableBody/useResetVirtualizerOnDataChange.d.ts +1 -1
  6. package/dist/components/Table/TableBody/useResetVirtualizerOnDataChange.js +2 -2
  7. package/dist/components/Table/TableContext/TableProvider.js +6 -32
  8. package/dist/components/Table/TableContext/types.d.ts +2 -8
  9. package/dist/components/Table/TableRow.js +2 -2
  10. package/dist/components/Table/hooks/index.d.ts +1 -1
  11. package/dist/components/Table/hooks/index.js +2 -2
  12. package/dist/components/Table/hooks/useMasterCell.d.ts +12 -0
  13. package/dist/components/Table/hooks/useMasterCell.js +19 -0
  14. package/dist/components/Table/index.d.ts +1 -2
  15. package/dist/components/Table/index.js +1 -2
  16. package/dist/components/Table/mocks.d.ts +2 -6
  17. package/dist/components/Table/mocks.js +3 -12
  18. package/dist/components/Table/types.d.ts +4 -23
  19. package/dist/metadata/components.json +17 -30
  20. package/dist/theme/icon.css +1 -1
  21. package/package.json +1 -1
  22. package/dist/components/Table/TablePreviewDrawer.d.ts +0 -2
  23. package/dist/components/Table/TablePreviewDrawer.js +0 -39
  24. package/dist/components/Table/TablePreviewToggle.d.ts +0 -8
  25. package/dist/components/Table/TablePreviewToggle.js +0 -32
  26. package/dist/components/Table/hooks/usePreviewCell.d.ts +0 -16
  27. package/dist/components/Table/hooks/usePreviewCell.js +0 -25
@@ -9,7 +9,7 @@ const Attribute = ({ ref, loading = false, children, className, 'data-testid': t
9
9
  ref: ref,
10
10
  "data-testid": testId,
11
11
  "data-slot": "attribute",
12
- className: cn('flex flex-col', className),
12
+ className: cn('flex flex-col', loading && 'gap-4 py-2', className),
13
13
  children: loading ? /*#__PURE__*/ jsxs(Fragment, {
14
14
  children: [
15
15
  /*#__PURE__*/ jsx(Skeleton, {
@@ -17,13 +17,10 @@ const Attribute = ({ ref, loading = false, children, className, 'data-testid': t
17
17
  height: "16px",
18
18
  rounded: 6
19
19
  }),
20
- /*#__PURE__*/ jsx("div", {
21
- className: "pt-4",
22
- children: /*#__PURE__*/ jsx(Skeleton, {
23
- width: "100%",
24
- height: "24px",
25
- rounded: 6
26
- })
20
+ /*#__PURE__*/ jsx(Skeleton, {
21
+ width: "100%",
22
+ height: "24px",
23
+ rounded: 6
27
24
  })
28
25
  ]
29
26
  }) : children
@@ -1,7 +1,8 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  const splitButtonVariants = cva([
3
- 'inline-flex items-center gap-1',
3
+ 'inline-flex items-center',
4
4
  '[&>:first-child]:rounded-r-none',
5
- '[&>:last-child]:rounded-l-none'
5
+ '[&>:last-child]:rounded-l-none',
6
+ '[&>:not(:first-child)]:-ml-px'
6
7
  ]);
7
8
  export { splitButtonVariants };
@@ -1,8 +1,7 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import { TestIdProvider } from "../../utils/testId.js";
3
3
  import { TableProvider } from "./TableContext/index.js";
4
4
  import { TableInner } from "./TableInner/index.js";
5
- import { TablePreviewDrawer } from "./TablePreviewDrawer.js";
6
5
  const Table = (props)=>{
7
6
  const { data, isLoading = false, children, className, 'aria-label': ariaLabel, 'data-testid': testId, ...providerProps } = props;
8
7
  const isEmpty = 0 === data.length && !isLoading;
@@ -11,20 +10,17 @@ const Table = (props)=>{
11
10
  data: data,
12
11
  isLoading: isLoading,
13
12
  ...providerProps,
14
- children: /*#__PURE__*/ jsxs(TestIdProvider, {
13
+ children: /*#__PURE__*/ jsx(TestIdProvider, {
15
14
  value: testId,
16
- children: [
17
- /*#__PURE__*/ jsx(TableInner, {
18
- isEmpty: isEmpty,
19
- virtualized: props.virtualized,
20
- showSettings: showSettings,
21
- ariaLabel: ariaLabel,
22
- className: className,
23
- "data-testid": testId,
24
- children: children
25
- }),
26
- /*#__PURE__*/ jsx(TablePreviewDrawer, {})
27
- ]
15
+ children: /*#__PURE__*/ jsx(TableInner, {
16
+ isEmpty: isEmpty,
17
+ virtualized: props.virtualized,
18
+ showSettings: showSettings,
19
+ ariaLabel: ariaLabel,
20
+ className: className,
21
+ "data-testid": testId,
22
+ children: children
23
+ })
28
24
  })
29
25
  });
30
26
  };
@@ -3,12 +3,11 @@ import { flexRender } from "@tanstack/react-table";
3
3
  import { cn } from "../../../utils/cn.js";
4
4
  import { useTestId } from "../../../utils/testId.js";
5
5
  import { Tooltip, TooltipContent, TooltipTrigger } from "../../Tooltip/index.js";
6
- import { usePreviewCell } from "../hooks/index.js";
6
+ import { useMasterCell } from "../hooks/index.js";
7
7
  import { TABLE_EXPAND_COLUMN_ID, getAlignClass, getExpandBorderClass, getPinningStyles, isLastPinnedLeft, useColumnDnd } from "../lib/index.js";
8
8
  import { Td } from "../primitives/index.js";
9
9
  import { useTableContext } from "../TableContext/index.js";
10
10
  import { TableMasterCellActions } from "../TableMasterCellActions.js";
11
- import { TablePreviewToggle } from "../TablePreviewToggle.js";
12
11
  const TableBodyCell = ({ cell, colSpan, className, disablePinnedShadow })=>{
13
12
  const { allLeafColumns, masterColumnId } = useTableContext();
14
13
  const testId = useTestId('body-cell');
@@ -20,10 +19,10 @@ const TableBodyCell = ({ cell, colSpan, className, disablePinnedShadow })=>{
20
19
  const { canDnd, setNodeRef, dndStyle } = useColumnDnd(column);
21
20
  const pinningStyles = getPinningStyles(column);
22
21
  const lastLeft = isLastPinnedLeft(column, allLeafColumns, column.id);
23
- const { isMasterTrigger, isButtonTrigger, isActive, togglePreview, tooltipText } = usePreviewCell(column.id, cell.row.id);
22
+ const { isMasterTrigger, handleClick, tooltipText } = useMasterCell(column.id, cell.row.id);
24
23
  const isCut = column.id === masterColumnId || meta?.resizeType === 'cut';
25
24
  const content = flexRender(cell.column.columnDef.cell, cell.getContext());
26
- const hasActions = isButtonTrigger || !!meta?.renderPreviewAction || !!meta?.renderMenuAction;
25
+ const hasActions = !!meta?.renderMenuAction;
27
26
  const renderContent = ()=>{
28
27
  const wrappedContent = tooltipText ? /*#__PURE__*/ jsxs(Tooltip, {
29
28
  children: [
@@ -46,15 +45,8 @@ const TableBodyCell = ({ cell, colSpan, className, disablePinnedShadow })=>{
46
45
  className: "flex items-center justify-between gap-2",
47
46
  children: [
48
47
  wrappedContent,
49
- /*#__PURE__*/ jsxs(TableMasterCellActions, {
50
- children: [
51
- isButtonTrigger && /*#__PURE__*/ jsx(TablePreviewToggle, {
52
- active: isActive,
53
- onClick: togglePreview
54
- }),
55
- meta?.renderPreviewAction?.(cell.row),
56
- meta?.renderMenuAction?.(cell.row)
57
- ]
48
+ /*#__PURE__*/ jsx(TableMasterCellActions, {
49
+ children: meta?.renderMenuAction?.(cell.row)
58
50
  })
59
51
  ]
60
52
  });
@@ -74,7 +66,7 @@ const TableBodyCell = ({ cell, colSpan, className, disablePinnedShadow })=>{
74
66
  lastPinnedLeft: disablePinnedShadow ? false : lastLeft,
75
67
  expanded: isExpandedToggle,
76
68
  ref: canDnd ? setNodeRef : void 0,
77
- onClick: isMasterTrigger ? togglePreview : void 0,
69
+ onClick: isMasterTrigger ? handleClick : void 0,
78
70
  style: {
79
71
  ...pinningStyles,
80
72
  width: cell.column.getSize(),
@@ -5,4 +5,4 @@ import type { Virtualizer } from '@tanstack/react-virtual';
5
5
  * does not retain stale heights from a previous data set.
6
6
  * Tracks first row ID to distinguish "new data" from "appended rows" (infinite scroll).
7
7
  */
8
- export declare function useResetVirtualizerOnDataChange(table: Table<unknown>, virtualizer: Virtualizer<Element, Element> | Virtualizer<Window, Element> | Virtualizer<HTMLElement, Element>): void;
8
+ export declare const useResetVirtualizerOnDataChange: (table: Table<unknown>, virtualizer: Virtualizer<Element, Element> | Virtualizer<Window, Element> | Virtualizer<HTMLElement, Element>) => void;
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from "react";
2
- function useResetVirtualizerOnDataChange(table, virtualizer) {
2
+ const useResetVirtualizerOnDataChange = (table, virtualizer)=>{
3
3
  const rows = table.getRowModel().rows;
4
4
  const firstRowId = rows[0]?.id;
5
5
  const prevFirstRowIdRef = useRef(firstRowId);
@@ -12,5 +12,5 @@ function useResetVirtualizerOnDataChange(table, virtualizer) {
12
12
  firstRowId,
13
13
  virtualizer
14
14
  ]);
15
- }
15
+ };
16
16
  export { useResetVirtualizerOnDataChange };
@@ -8,13 +8,8 @@ import { useTableState } from "../hooks/index.js";
8
8
  import { TABLE_EXPAND_COLUMN_ID, TABLE_MIN_COLUMN_WIDTH, TABLE_SELECT_COLUMN_ID, TABLE_SKELETON_ROWS, TABLE_VIRTUALIZATION_OVERSCAN, createExpandColumn, createSelectionColumn } from "../lib/index.js";
9
9
  import { TableContext } from "./TableContext.js";
10
10
  const TableProvider = (props)=>{
11
- const { data, columns, isLoading = false, skeletonCount = TABLE_SKELETON_ROWS, children, getRowId, sorting: sortingProp, onSortingChange, rowSelection: rowSelectionProp, onRowSelectionChange, columnSizing: columnSizingProp, onColumnSizingChange, columnPinning: columnPinningProp, onColumnPinningChange, columnOrder: columnOrderProp, onColumnOrderChange, grouping: groupingProp, onGroupingChange, expanded: expandedProp, onExpandedChange, renderGroupRow, getSubRows, renderExpandedRow, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, defaultColumnVisibility, defaultColumnOrder, virtualized, estimateRowHeight, overscan = TABLE_VIRTUALIZATION_OVERSCAN, onEndReached, onEndReachedThreshold, preview } = props;
12
- const renderPreviewHeader = preview?.renderHeader;
13
- const renderPreviewContent = preview?.renderContent;
14
- const previewTrigger = preview?.trigger ?? 'master';
15
- const previewTooltipText = preview?.tooltipText ?? 'Open preview';
16
- const previewRowIdProp = preview?.rowId;
17
- const onPreviewRowChange = preview?.onRowChange;
11
+ const { data, columns, isLoading = false, skeletonCount = TABLE_SKELETON_ROWS, children, getRowId, sorting: sortingProp, onSortingChange, rowSelection: rowSelectionProp, onRowSelectionChange, columnSizing: columnSizingProp, onColumnSizingChange, columnPinning: columnPinningProp, onColumnPinningChange, columnOrder: columnOrderProp, onColumnOrderChange, grouping: groupingProp, onGroupingChange, expanded: expandedProp, onExpandedChange, renderGroupRow, getSubRows, renderExpandedRow, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, defaultColumnVisibility, defaultColumnOrder, virtualized, estimateRowHeight, overscan = TABLE_VIRTUALIZATION_OVERSCAN, onEndReached, onEndReachedThreshold, onMasterCellClick, activeRowId: activeRowIdProp } = props;
12
+ const masterCellActiveRowId = activeRowIdProp ?? null;
18
13
  const sortingEnabled = !!onSortingChange;
19
14
  const selectionEnabled = !!onRowSelectionChange;
20
15
  const resizingEnabled = !!onColumnSizingChange;
@@ -188,17 +183,6 @@ const TableProvider = (props)=>{
188
183
  const lastSelectedRowIndexRef = useRef(null);
189
184
  const theadRef = useRef(null);
190
185
  const containerRef = useRef(null);
191
- const [previewRowId = null, setPreviewRowIdInternal] = useControlled({
192
- controlled: previewRowIdProp,
193
- default: null
194
- });
195
- const setPreviewRowId = useCallback((id)=>{
196
- setPreviewRowIdInternal(id);
197
- onPreviewRowChange?.(id);
198
- }, [
199
- setPreviewRowIdInternal,
200
- onPreviewRowChange
201
- ]);
202
186
  const contextValue = useMemo(()=>({
203
187
  table,
204
188
  isLoading,
@@ -226,14 +210,8 @@ const TableProvider = (props)=>{
226
210
  containerRef,
227
211
  onEndReached,
228
212
  onEndReachedThreshold,
229
- preview: {
230
- rowId: previewRowId,
231
- setRowId: setPreviewRowId,
232
- renderHeader: renderPreviewHeader,
233
- renderContent: renderPreviewContent,
234
- trigger: previewTrigger,
235
- tooltipText: previewTooltipText
236
- }
213
+ onMasterCellClick,
214
+ activeRowId: masterCellActiveRowId
237
215
  }), [
238
216
  table,
239
217
  isLoading,
@@ -258,12 +236,8 @@ const TableProvider = (props)=>{
258
236
  masterColumnId,
259
237
  onEndReached,
260
238
  onEndReachedThreshold,
261
- previewRowId,
262
- setPreviewRowId,
263
- renderPreviewHeader,
264
- renderPreviewContent,
265
- previewTrigger,
266
- previewTooltipText
239
+ masterCellActiveRowId,
240
+ onMasterCellClick
267
241
  ]);
268
242
  useEffect(()=>{
269
243
  if (!selectionEnabled) return;
@@ -28,14 +28,8 @@ export interface TableContextValue<T> {
28
28
  containerRef: RefObject<HTMLDivElement | null>;
29
29
  onEndReached?: () => void;
30
30
  onEndReachedThreshold?: number;
31
- preview: {
32
- rowId: string | null;
33
- setRowId: (id: string | null) => void;
34
- renderHeader?: (row: Row<T>) => ReactNode;
35
- renderContent?: (row: Row<T>) => ReactNode;
36
- trigger: 'master' | 'button';
37
- tooltipText: string;
38
- };
31
+ onMasterCellClick?: (rowId: string) => void;
32
+ activeRowId: string | null;
39
33
  }
40
34
  export interface TableProviderProps<T> extends Omit<TableProps<T>, 'children' | 'aria-label'> {
41
35
  children: ReactNode;
@@ -12,11 +12,11 @@ const SYSTEM_COLUMN_IDS = new Set([
12
12
  TABLE_SELECT_COLUMN_ID
13
13
  ]);
14
14
  const TableRowInner = ({ row, ref, 'data-index': dataIndex })=>{
15
- const { expandingEnabled, preview } = useTableContext();
15
+ const { expandingEnabled, activeRowId } = useTableContext();
16
16
  const testId = useTestId('row');
17
17
  const isGroupParent = row.subRows.length > 0;
18
18
  const isSelected = isGroupParent ? row.getIsAllSubRowsSelected() : row.getIsSelected();
19
- const isPreviewActive = preview.rowId === row.id;
19
+ const isPreviewActive = activeRowId === row.id;
20
20
  if (isGroupParent) {
21
21
  const cells = row.getVisibleCells();
22
22
  const systemCells = cells.filter((c)=>SYSTEM_COLUMN_IDS.has(c.column.id));
@@ -1,4 +1,4 @@
1
1
  export { useEndReached } from './useEndReached';
2
2
  export { useHorizontalScrollState } from './useHorizontalScrollState';
3
- export { usePreviewCell } from './usePreviewCell';
3
+ export { useMasterCell } from './useMasterCell';
4
4
  export { useTableState } from './useTableState';
@@ -1,5 +1,5 @@
1
1
  import { useEndReached } from "./useEndReached.js";
2
2
  import { useHorizontalScrollState } from "./useHorizontalScrollState.js";
3
- import { usePreviewCell } from "./usePreviewCell.js";
3
+ import { useMasterCell } from "./useMasterCell.js";
4
4
  import { useTableState } from "./useTableState.js";
5
- export { useEndReached, useHorizontalScrollState, usePreviewCell, useTableState };
5
+ export { useEndReached, useHorizontalScrollState, useMasterCell, useTableState };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Encapsulates master cell click logic for a body cell.
3
+ * Returns flags and a click handler for the master column.
4
+ */
5
+ export declare const useMasterCell: <T>(columnId: string, rowId: string) => {
6
+ /** Master cell click is enabled */
7
+ isMasterTrigger: boolean;
8
+ /** Fire master cell click for this row */
9
+ handleClick: () => void;
10
+ /** Tooltip text for master cell hover */
11
+ tooltipText: string | undefined;
12
+ };
@@ -0,0 +1,19 @@
1
+ import { useCallback } from "react";
2
+ import { useTableContext } from "../TableContext/index.js";
3
+ const useMasterCell = (columnId, rowId)=>{
4
+ const { masterColumnId, onMasterCellClick } = useTableContext();
5
+ const isMasterColumn = columnId === masterColumnId;
6
+ const hasMasterClick = isMasterColumn && !!onMasterCellClick;
7
+ const handleClick = useCallback(()=>{
8
+ onMasterCellClick?.(rowId);
9
+ }, [
10
+ onMasterCellClick,
11
+ rowId
12
+ ]);
13
+ return {
14
+ isMasterTrigger: hasMasterClick,
15
+ handleClick,
16
+ tooltipText: hasMasterClick ? 'Open details' : void 0
17
+ };
18
+ };
19
+ export { useMasterCell };
@@ -3,6 +3,5 @@ export type { TableColumnHelper } from './lib/createTableColumnHelper';
3
3
  export { Table } from './Table';
4
4
  export { TableActionBar } from './TableActionBar';
5
5
  export { TableEmptyState } from './TableEmptyState';
6
- export { TablePreviewToggle, type TablePreviewToggleProps } from './TablePreviewToggle';
7
6
  export { TableSettingsMenu } from './TableSettingsMenu';
8
- export type { TableAccessorColumnDef, TableCellContext, TableColumnBase, TableColumnDef, TableColumnMeta, TableColumnPinningState, TableColumnSizingState, TableDisplayColumnDef, TableExpandedState, TableGroupingState, TableOnChangeFn, TablePreview, TableProps, TableRow, TableRowSelectionState, TableSortingState, TableUpdater, TableVirtualized, TableVisibilityState, } from './types';
7
+ export type { TableAccessorColumnDef, TableCellContext, TableColumnBase, TableColumnDef, TableColumnMeta, TableColumnPinningState, TableColumnSizingState, TableDisplayColumnDef, TableExpandedState, TableGroupingState, TableOnChangeFn, TableProps, TableRow, TableRowSelectionState, TableSortingState, TableUpdater, TableVirtualized, TableVisibilityState, } from './types';
@@ -2,6 +2,5 @@ import { createTableColumnHelper } from "./lib/index.js";
2
2
  import { Table } from "./Table.js";
3
3
  import { TableActionBar } from "./TableActionBar/index.js";
4
4
  import { TableEmptyState } from "./TableEmptyState.js";
5
- import { TablePreviewToggle } from "./TablePreviewToggle.js";
6
5
  import { TableSettingsMenu } from "./TableSettingsMenu/index.js";
7
- export { Table, TableActionBar, TableEmptyState, TablePreviewToggle, TableSettingsMenu, createTableColumnHelper };
6
+ export { Table, TableActionBar, TableEmptyState, TableSettingsMenu, createTableColumnHelper };
@@ -44,12 +44,8 @@ export declare const createLargeGroupedData: (groupCount?: number, childrenPerGr
44
44
  export declare const renderSecurityPreviewHeader: (row: {
45
45
  original: SecurityEvent;
46
46
  }) => import("react/jsx-runtime").JSX.Element;
47
- /** Preview content for use with header (objectName shown in header) */
48
- export declare const renderSecurityPreview: (row: {
49
- original: SecurityEvent;
50
- }) => import("react/jsx-runtime").JSX.Element;
51
- /** Preview content for use without header (includes objectName) */
52
- export declare const renderSecurityPreviewWithTitle: (row: {
47
+ /** Preview content (without title title shown in DrawerHeader) */
48
+ export declare const renderSecurityPreviewContent: (row: {
53
49
  original: SecurityEvent;
54
50
  }) => import("react/jsx-runtime").JSX.Element;
55
51
  /** Duplicate securityEvents N times with unique IDs */
@@ -638,14 +638,9 @@ const renderSecurityPreviewHeader = (row)=>/*#__PURE__*/ jsx(DrawerHeader, {
638
638
  children: row.original.objectName
639
639
  })
640
640
  });
641
- const SecurityPreviewContent = ({ row, showTitle })=>/*#__PURE__*/ jsxs(VStack, {
641
+ const SecurityPreviewContent = ({ row })=>/*#__PURE__*/ jsxs(VStack, {
642
642
  gap: 16,
643
643
  children: [
644
- showTitle && /*#__PURE__*/ jsx(Text, {
645
- size: "lg",
646
- weight: "medium",
647
- children: row.original.objectName
648
- }),
649
644
  /*#__PURE__*/ jsxs(HStack, {
650
645
  gap: 8,
651
646
  children: [
@@ -705,13 +700,9 @@ const SecurityPreviewContent = ({ row, showTitle })=>/*#__PURE__*/ jsxs(VStack,
705
700
  ]
706
701
  });
707
702
  SecurityPreviewContent.displayName = 'SecurityPreviewContent';
708
- const renderSecurityPreview = (row)=>/*#__PURE__*/ jsx(SecurityPreviewContent, {
703
+ const renderSecurityPreviewContent = (row)=>/*#__PURE__*/ jsx(SecurityPreviewContent, {
709
704
  row: row
710
705
  });
711
- const renderSecurityPreviewWithTitle = (row)=>/*#__PURE__*/ jsx(SecurityPreviewContent, {
712
- row: row,
713
- showTitle: true
714
- });
715
706
  const multiplySecurityEvents = (times = 4)=>Array.from({
716
707
  length: times
717
708
  }, (_, batch)=>securityEvents.map((row)=>({
@@ -967,4 +958,4 @@ const useInfiniteData = ()=>{
967
958
  fetchNextPage
968
959
  };
969
960
  };
970
- export { METHOD_COLORS, createLargeGroupedData, createLargeSecurityEvents, fullFeaturedColumns, groupedHeaderData, headerColumnHelper, headerColumnIds, headerColumns, multiplySecurityEvents, renderSecurityPreview, renderSecurityPreviewHeader, renderSecurityPreviewWithTitle, securityColumnHelper, securityColumnIds, securityColumns, securityEvents, useInfiniteData };
961
+ export { METHOD_COLORS, createLargeGroupedData, createLargeSecurityEvents, fullFeaturedColumns, groupedHeaderData, headerColumnHelper, headerColumnIds, headerColumns, multiplySecurityEvents, renderSecurityPreviewContent, renderSecurityPreviewHeader, securityColumnHelper, securityColumnIds, securityColumns, securityEvents, useInfiniteData };
@@ -16,8 +16,6 @@ declare module '@tanstack/react-table' {
16
16
  };
17
17
  /** Resize behavior: 'resize' adapts content to fit (default), 'cut' truncates content */
18
18
  resizeType?: 'cut' | 'resize';
19
- /** Render the preview toggle button shown on hover */
20
- renderPreviewAction?: (row: Row<TData>) => ReactNode;
21
19
  /** Render the three-dots contextual menu shown on hover */
22
20
  renderMenuAction?: (row: Row<TData>) => ReactNode;
23
21
  }
@@ -78,8 +76,6 @@ export interface TableColumnMeta<T = unknown> {
78
76
  };
79
77
  /** Resize behavior: 'resize' adapts content to fit (default), 'cut' truncates content */
80
78
  resizeType?: 'cut' | 'resize';
81
- /** Render the preview toggle button shown on hover */
82
- renderPreviewAction?: (row: TableRow<T>) => ReactNode;
83
79
  /** Render the three-dots contextual menu shown on hover */
84
80
  renderMenuAction?: (row: TableRow<T>) => ReactNode;
85
81
  }
@@ -164,23 +160,8 @@ export interface TableProps<T> extends TestableProps {
164
160
  onEndReached?: () => void;
165
161
  /** Distance from the bottom (in px) to trigger onEndReached (default: 200) */
166
162
  onEndReachedThreshold?: number;
167
- /** Preview drawer configuration */
168
- preview?: TablePreview<T>;
169
- }
170
- /** Preview drawer configuration */
171
- export interface TablePreview<T> {
172
- /** Render drawer header for a row */
173
- renderHeader?: (row: TableRow<T>) => ReactNode;
174
- /** Render drawer content for a row */
175
- renderContent: (row: TableRow<T>) => ReactNode;
176
- /** How the preview drawer is triggered:
177
- * - `'master'` — clicking the master cell toggles the drawer (default)
178
- * - `'button'` — a toggle button appears in master cell actions on hover */
179
- trigger?: 'master' | 'button';
180
- /** Tooltip text on master cell hover (default: 'Open preview') */
181
- tooltipText?: string;
182
- /** Controlled preview row ID. Pass `null` to close. */
183
- rowId?: string | null;
184
- /** Callback when preview row changes (open/close/swap). */
185
- onRowChange?: (rowId: string | null) => void;
163
+ /** Callback fired when the master cell is clicked. Receives the row ID. */
164
+ onMasterCellClick?: (rowId: string) => void;
165
+ /** ID of the currently active (highlighted) row, or null. Controls row highlighting via data-preview-active attribute. */
166
+ activeRowId?: string | null;
186
167
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.23.0",
3
- "generatedAt": "2026-04-10T09:45:32.033Z",
2
+ "version": "0.23.1",
3
+ "generatedAt": "2026-04-13T08:49:49.893Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
@@ -3482,11 +3482,11 @@
3482
3482
  },
3483
3483
  {
3484
3484
  "name": "WithTags",
3485
- "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Tags</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4'>\n <Badge color='slate'>production</Badge>\n <Badge color='slate'>us-east-1</Badge>\n <Badge color='slate'>critical</Badge>\n </div>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3485
+ "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Tags</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4 flex-wrap'>\n <Tag>production</Tag>\n <Tag>us-east-1</Tag>\n <Tag>critical</Tag>\n </div>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3486
3486
  },
3487
3487
  {
3488
3488
  "name": "WithCodeSnippet",
3489
- "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Payload</AttributeLabel>\n <AttributeValue>\n <Code size='s'>{'{ \"action\": \"login\", \"user_id\": 42 }'}</Code>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3489
+ "code": "() => (\n <div className='w-[400px] flex flex-col gap-16'>\n <Attribute>\n <AttributeLabel>Payload (inline)</AttributeLabel>\n <AttributeValue>\n <InlineCodeSnippet code='{ \"action\": \"login\", \"user_id\": 42 }' size='sm' />\n </AttributeValue>\n </Attribute>\n <Attribute>\n <AttributeLabel>Payload (code)</AttributeLabel>\n <AttributeValue>\n <Code size='s'>{'{ \"action\": \"login\", \"user_id\": 42 }'}</Code>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3490
3490
  },
3491
3491
  {
3492
3492
  "name": "WithLink_Value",
@@ -3494,15 +3494,15 @@
3494
3494
  },
3495
3495
  {
3496
3496
  "name": "WithDateTime",
3497
- "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Created at</AttributeLabel>\n <AttributeValue>\n <FormatDateTime value='2026-04-02T14:03:00Z' />\n </AttributeValue>\n </Attribute>\n </div>\n)"
3497
+ "code": "() => (\n <div className='w-[400px] flex flex-col gap-16'>\n <Attribute>\n <AttributeLabel>Created at (relative)</AttributeLabel>\n <AttributeValue>\n <FormatDateTime value='2026-04-02T14:03:00Z' />\n </AttributeValue>\n </Attribute>\n <Attribute>\n <AttributeLabel>Created at (absolute)</AttributeLabel>\n <AttributeValue>\n <FormatDateTime value='2026-04-02T14:03:00Z' format='datetime' />\n </AttributeValue>\n </Attribute>\n </div>\n)"
3498
3498
  },
3499
3499
  {
3500
3500
  "name": "WithIP",
3501
- "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Source IP</AttributeLabel>\n <AttributeValue>\n <Ip>142.198.167.52</Ip>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3501
+ "code": "() => (\n <div className='w-[400px]'>\n <Attribute>\n <AttributeLabel>Source IP</AttributeLabel>\n <AttributeValue>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>142.198.167.52</IpAddress>\n <IpProvider>Azure</IpProvider>\n </Ip>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3502
3502
  },
3503
3503
  {
3504
3504
  "name": "Composition",
3505
- "code": "() => (\n <div className='grid grid-cols-2 gap-x-8 gap-y-16 w-[874px]'>\n <Attribute>\n <AttributeLabel>Status</AttributeLabel>\n <AttributeValue>\n <Badge color='red' variant='dotted'>\n Blocked\n </Badge>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>First seen</AttributeLabel>\n <AttributeValue>\n <Text size='sm' decoration='dashed'>\n Week ago\n </Text>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Attack type</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4 flex-wrap'>\n <Badge color='slate'>XSS</Badge>\n <Badge color='slate'>BOLA</Badge>\n <Badge color='slate'>SQL Injection</Badge>\n <Badge color='slate'>Scanner</Badge>\n <Badge color='slate'>+5</Badge>\n </div>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Last seen</AttributeLabel>\n <AttributeValue>\n <Text size='sm'>2 Apr, 2026 14:03</Text>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Sessions</AttributeLabel>\n <AttributeValue>\n <Text size='sm'>3 sessions</Text>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Users</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4'>\n <Text size='sm'>artem@acme.com, uxd@acme.com</Text>\n <Badge color='slate'>+3</Badge>\n </div>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>IPs</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4 flex-wrap col-span-2'>\n <Text size='sm'>142.198.167.52</Text>\n <Badge color='sky'>Azure</Badge>\n <Text size='sm'>34.74.73.20</Text>\n <Badge color='amber'>AWS</Badge>\n <Text size='sm'>34.74.73.20</Text>\n <Badge color='teal'>GCP</Badge>\n <Badge color='slate'>+5</Badge>\n </div>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3505
+ "code": "() => (\n <div className='grid grid-cols-2 gap-x-8 gap-y-16 w-[874px]'>\n <Attribute>\n <AttributeLabel>Status</AttributeLabel>\n <AttributeValue>\n <Badge color='red' variant='dotted'>\n Blocked\n </Badge>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>First seen</AttributeLabel>\n <AttributeValue>\n <FormatDateTime value='2026-04-03T10:15:00Z' />\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Attack type</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4 flex-wrap'>\n <Tag>XSS</Tag>\n <Tag>BOLA</Tag>\n <Tag>SQL Injection</Tag>\n <Tag>Scanner</Tag>\n <Tag>+5</Tag>\n </div>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Sessions</AttributeLabel>\n <AttributeValue>\n <Text size='sm'>3 sessions</Text>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>Users</AttributeLabel>\n <AttributeValue>\n <div className='flex items-center gap-4'>\n <Text size='sm'>artem@acme.com, uxd@acme.com</Text>\n <Tag>+3</Tag>\n </div>\n </AttributeValue>\n </Attribute>\n\n <Attribute>\n <AttributeLabel>IPs</AttributeLabel>\n <AttributeValue>\n <IpList>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>142.198.167.52</IpAddress>\n <IpProvider>Azure</IpProvider>\n </Ip>\n <Ip>\n <IpCountry code='US' />\n <IpAddress>34.74.73.20</IpAddress>\n <IpProvider>AWS</IpProvider>\n </Ip>\n <Ip>\n <IpCountry code='DE' />\n <IpAddress>34.74.73.20</IpAddress>\n <IpProvider>GCP</IpProvider>\n </Ip>\n <Ip>\n <IpCountry code='NL' />\n <IpAddress>10.0.0.1</IpAddress>\n </Ip>\n <Ip>\n <IpCountry code='JP' />\n <IpAddress>192.168.1.1</IpAddress>\n </Ip>\n </IpList>\n </AttributeValue>\n </Attribute>\n </div>\n)"
3506
3506
  }
3507
3507
  ]
3508
3508
  },
@@ -23635,7 +23635,7 @@
23635
23635
  },
23636
23636
  {
23637
23637
  "name": "Variants",
23638
- "code": "() => (\n <VStack>\n <Heading>Primary / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='primary' color='brand'>\n Button\n </Button>\n <Button variant='primary' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Outline / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='outline' color='neutral'>\n Button\n </Button>\n <Button variant='outline' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Secondary / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='secondary' color='neutral'>\n Button\n </Button>\n <Button variant='secondary' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Ghost / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='ghost' color='neutral'>\n Button\n </Button>\n <Button variant='ghost' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Secondary / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='secondary' color='brand'>\n Button\n </Button>\n <Button variant='secondary' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Ghost / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='ghost' color='brand'>\n Button\n </Button>\n <Button variant='ghost' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n </VStack>\n)"
23638
+ "code": "() => (\n <VStack>\n <Heading>Primary / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='primary' color='brand'>\n Button\n </Button>\n <Button variant='primary' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Outline / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='outline' color='neutral'>\n Button\n </Button>\n <Button variant='outline' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Secondary / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='secondary' color='neutral'>\n Button\n </Button>\n <Button variant='secondary' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Ghost / Neutral</Heading>\n <HStack>\n <SplitButton>\n <Button variant='ghost' color='neutral'>\n Button\n </Button>\n <Button variant='ghost' color='neutral'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Secondary / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='secondary' color='brand'>\n Button\n </Button>\n <Button variant='secondary' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Ghost / Brand</Heading>\n <HStack>\n <SplitButton>\n <Button variant='ghost' color='brand'>\n Button\n </Button>\n <Button variant='ghost' color='brand'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading className='text-text-primary-alt'>Ghost / Neutral Alt</Heading>\n <div className='flex flex-col gap-16 rounded-lg bg-component-tooltip-bg p-8'>\n <HStack>\n <SplitButton>\n <Button variant='ghost' color='neutral-alt'>\n Button\n </Button>\n <Button variant='ghost' color='neutral-alt'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n </div>\n\n <Heading className='text-text-primary-alt'>Secondary / Neutral Alt</Heading>\n <div className='flex flex-col gap-16 rounded-lg bg-component-tooltip-bg p-8'>\n <HStack>\n <SplitButton>\n <Button variant='secondary' color='neutral-alt'>\n Button\n </Button>\n <Button variant='secondary' color='neutral-alt'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n </div>\n </VStack>\n)"
23639
23639
  },
23640
23640
  {
23641
23641
  "name": "Sizes",
@@ -23646,8 +23646,8 @@
23646
23646
  "code": "() => (\n <VStack>\n <Heading>Text only</Heading>\n <HStack align='end'>\n <SplitButton>\n <Button variant='primary' color='brand' size='large'>\n Large\n </Button>\n <Button variant='primary' color='brand' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='outline' color='neutral' size='large'>\n Large\n </Button>\n <Button variant='outline' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='secondary' color='neutral' size='large'>\n Large\n </Button>\n <Button variant='secondary' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Icon + Text</Heading>\n <HStack align='end'>\n <SplitButton>\n <Button variant='primary' color='brand' size='large'>\n <CircleDashed />\n Large\n </Button>\n <Button variant='primary' color='brand' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='outline' color='neutral' size='large'>\n <CircleDashed />\n Large\n </Button>\n <Button variant='outline' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='secondary' color='neutral' size='large'>\n <CircleDashed />\n Large\n </Button>\n <Button variant='secondary' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Icon + Text + Badge</Heading>\n <HStack align='end'>\n <SplitButton>\n <Button variant='primary' color='brand' size='large'>\n <CircleDashed />\n Large\n <NumericBadge type='outline'>1</NumericBadge>\n </Button>\n <Button variant='primary' color='brand' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='outline' color='neutral' size='large'>\n <CircleDashed />\n Large\n <NumericBadge type='outline'>1</NumericBadge>\n </Button>\n <Button variant='outline' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='secondary' color='neutral' size='large'>\n <CircleDashed />\n Large\n <NumericBadge type='outline'>1</NumericBadge>\n </Button>\n <Button variant='secondary' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n\n <Heading>Icon only</Heading>\n <HStack align='end'>\n <SplitButton>\n <Button variant='primary' color='brand' size='large'>\n <CircleDashed />\n </Button>\n <Button variant='primary' color='brand' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='outline' color='neutral' size='large'>\n <CircleDashed />\n </Button>\n <Button variant='outline' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n <SplitButton>\n <Button variant='secondary' color='neutral' size='large'>\n <CircleDashed />\n </Button>\n <Button variant='secondary' color='neutral' size='large'>\n <ChevronDown />\n </Button>\n </SplitButton>\n </HStack>\n </VStack>\n)"
23647
23647
  },
23648
23648
  {
23649
- "name": "WithPopover",
23650
- "code": "() => (\n <Popover>\n <SplitButton data-testid='split-button-popover'>\n <Button variant='primary' color='brand'>\n Save\n </Button>\n <PopoverTrigger asChild>\n <Button variant='primary' color='brand'>\n <ChevronDown />\n </Button>\n </PopoverTrigger>\n </SplitButton>\n <PopoverContent>\n <VStack>\n <Text>Save as draft</Text>\n <Text>Save and publish</Text>\n <Text>Save as template</Text>\n </VStack>\n </PopoverContent>\n </Popover>\n)"
23649
+ "name": "WithDropdownMenu",
23650
+ "code": "() => (\n <DropdownMenu>\n <SplitButton data-testid='split-button-dropdown'>\n <Button variant='primary' color='brand'>\n Save\n </Button>\n <DropdownMenuTrigger asChild>\n <Button variant='primary' color='brand'>\n <ChevronDown />\n </Button>\n </DropdownMenuTrigger>\n </SplitButton>\n <DropdownMenuContent>\n <DropdownMenuItem value='draft'>Save as draft</DropdownMenuItem>\n <DropdownMenuItem value='publish'>Save and publish</DropdownMenuItem>\n <DropdownMenuItem value='template'>Save as template</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n)"
23651
23651
  }
23652
23652
  ]
23653
23653
  },
@@ -25803,10 +25803,10 @@
25803
25803
  "required": false
25804
25804
  },
25805
25805
  {
25806
- "name": "preview",
25807
- "type": "TablePreview<T> | undefined",
25806
+ "name": "activeRowId",
25807
+ "type": "string | null | undefined",
25808
25808
  "required": false,
25809
- "description": "Preview drawer configuration"
25809
+ "description": "ID of the currently active (highlighted) row, or null. Controls row highlighting via data-preview-active attribute."
25810
25810
  }
25811
25811
  ],
25812
25812
  "variants": [
@@ -25866,20 +25866,7 @@
25866
25866
  ]
25867
25867
  }
25868
25868
  ],
25869
- "subComponents": [
25870
- {
25871
- "name": "TablePreviewToggle",
25872
- "props": [
25873
- {
25874
- "name": "active",
25875
- "type": "boolean | undefined",
25876
- "required": false,
25877
- "description": "Whether the preview drawer is open for this row",
25878
- "defaultValue": "false"
25879
- }
25880
- ]
25881
- }
25882
- ],
25869
+ "subComponents": [],
25883
25870
  "examples": [
25884
25871
  {
25885
25872
  "name": "Basic",
@@ -25963,11 +25950,11 @@
25963
25950
  },
25964
25951
  {
25965
25952
  "name": "MasterCellWithActions",
25966
- "code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([]);\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n const data = useMemo(() => multiplySecurityEvents(), []);\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () =>\n securityColumns.map((col, i) =>\n i === 0\n ? {\n ...col,\n meta: {\n ...col.meta,\n size: 400,\n resizeType: 'resize',\n renderMenuAction: (row: { original: SecurityEvent }) => (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant='ghost' color='neutral' size='small' aria-label='More'>\n <Ellipsis />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem\n onSelect={() => navigator.clipboard.writeText(row.original.objectName)}\n >\n <DropdownMenuItemIcon>\n <Copy />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Copy name</DropdownMenuItemText>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onSelect={() => alert(`Filter: ${row.original.objectName}`)}\n >\n <DropdownMenuItemIcon>\n <Filter />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Show only</DropdownMenuItemText>\n </DropdownMenuItem>\n <DropdownMenuItem\n onSelect={() => alert(`Exclude: ${row.original.objectName}`)}\n >\n <DropdownMenuItemIcon>\n <FilterX />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Exclude</DropdownMenuItemText>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ),\n },\n }\n : col,\n ),\n [],\n );\n\n return (\n <Table\n className='max-w-920'\n data={data}\n columns={columns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n preview={{\n trigger: 'button',\n renderHeader: renderSecurityPreviewHeader,\n renderContent: renderSecurityPreview,\n }}\n />\n );\n}"
25953
+ "code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([]);\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n const data = useMemo(() => multiplySecurityEvents(), []);\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () =>\n securityColumns.map((col, i) =>\n i === 0\n ? {\n ...col,\n meta: {\n ...col.meta,\n size: 400,\n resizeType: 'resize',\n renderMenuAction: (row: { original: SecurityEvent }) => (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant='ghost' color='neutral' size='small' aria-label='More'>\n <Ellipsis />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem\n onSelect={() => navigator.clipboard.writeText(row.original.objectName)}\n >\n <DropdownMenuItemIcon>\n <Copy />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Copy name</DropdownMenuItemText>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onSelect={() => alert(`Filter: ${row.original.objectName}`)}\n >\n <DropdownMenuItemIcon>\n <Filter />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Show only</DropdownMenuItemText>\n </DropdownMenuItem>\n <DropdownMenuItem\n onSelect={() => alert(`Exclude: ${row.original.objectName}`)}\n >\n <DropdownMenuItemIcon>\n <FilterX />\n </DropdownMenuItemIcon>\n <DropdownMenuItemText>Exclude</DropdownMenuItemText>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ),\n },\n }\n : col,\n ),\n [],\n );\n\n return (\n <Table\n className='max-w-920'\n data={data}\n columns={columns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n />\n );\n}"
25967
25954
  },
25968
25955
  {
25969
- "name": "MasterCellWithPreviewDrawer",
25970
- "code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([]);\n\n const data = useMemo(\n () =>\n Array.from({ length: 4 }, (_, batch) =>\n securityEvents.map(row => ({\n ...row,\n id: `${row.id}-${batch}`,\n objectName: batch === 0 ? row.objectName : `${row.objectName} (${batch + 1})`,\n })),\n ).flat(),\n [],\n );\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () =>\n securityColumns.map((col, i) =>\n i === 0\n ? {\n ...col,\n cell: ({ row }: { row: { original: SecurityEvent } }) => (\n <Text size='xs'>{row.original.objectName}</Text>\n ),\n }\n : col,\n ),\n [],\n );\n\n return (\n <Table\n className='max-w-920'\n data={data}\n columns={columns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n preview={{ renderContent: renderSecurityPreviewWithTitle }}\n />\n );\n}"
25956
+ "name": "MasterCellWithDrawer",
25957
+ "code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([]);\n const [activeRowId, setActiveRowId] = useState<string | null>(null);\n\n const data = useMemo(\n () =>\n Array.from({ length: 4 }, (_, batch) =>\n securityEvents.map(row => ({\n ...row,\n id: `${row.id}-${batch}`,\n objectName: batch === 0 ? row.objectName : `${row.objectName} (${batch + 1})`,\n })),\n ).flat(),\n [],\n );\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () =>\n securityColumns.map((col, i) =>\n i === 0\n ? {\n ...col,\n cell: ({ row }: { row: { original: SecurityEvent } }) => (\n <Text size='xs'>{row.original.objectName}</Text>\n ),\n }\n : col,\n ),\n [],\n );\n\n const handleMasterCellClick = useCallback((rowId: string) => {\n setActiveRowId(prev => (prev === rowId ? null : rowId));\n }, []);\n\n const activeRow = useMemo(() => data.find(d => d.id === activeRowId), [data, activeRowId]);\n\n return (\n <>\n <Table\n className='max-w-920'\n data={data}\n columns={columns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n onMasterCellClick={handleMasterCellClick}\n activeRowId={activeRowId}\n />\n <Drawer\n open={!!activeRow}\n onOpenChange={open => {\n if (!open) setActiveRowId(null);\n }}\n modal={false}\n overlay={false}\n closeOnOutsideClick={false}\n width={960}\n >\n <DrawerContent>\n {activeRow ? (\n renderSecurityPreviewHeader({ original: activeRow })\n ) : (\n <DrawerHeader>\n <span />\n </DrawerHeader>\n )}\n <DrawerBody>\n {activeRow && renderSecurityPreviewContent({ original: activeRow })}\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n </>\n );\n}"
25971
25958
  },
25972
25959
  {
25973
25960
  "name": "FullFeatured",
@@ -10,7 +10,7 @@
10
10
  @utility icon-* {
11
11
  width: --value(--icon-*);
12
12
  height: --value(--icon-*);
13
- vertical-align: --value(--icon-valign, -0.125em);
13
+ vertical-align: -0.125em;
14
14
  display: inline-block;
15
15
  flex-shrink: 0;
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.23.1",
3
+ "version": "0.23.2",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,2 +0,0 @@
1
- import type { FC } from 'react';
2
- export declare const TablePreviewDrawer: FC;
@@ -1,39 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useRef } from "react";
3
- import { Drawer, DrawerBody, DrawerContent, DrawerHeader } from "../Drawer/index.js";
4
- import { useTableContext } from "./TableContext/index.js";
5
- const TablePreviewDrawer = ()=>{
6
- const { table, preview: previewCtx } = useTableContext();
7
- const row = previewCtx.rowId ? table.getRowModel().rowsById[previewCtx.rowId] : void 0;
8
- const header = row && previewCtx.renderHeader ? previewCtx.renderHeader(row) : void 0;
9
- const content = row && previewCtx.renderContent ? previewCtx.renderContent(row) : void 0;
10
- const lastHeaderRef = useRef(null);
11
- const lastContentRef = useRef(null);
12
- if (header) lastHeaderRef.current = header;
13
- if (content) lastContentRef.current = content;
14
- const displayHeader = header ?? lastHeaderRef.current;
15
- const displayContent = content ?? lastContentRef.current;
16
- if (!previewCtx.renderContent) return null;
17
- return /*#__PURE__*/ jsx(Drawer, {
18
- open: !!row,
19
- onOpenChange: (open)=>{
20
- if (!open) previewCtx.setRowId(null);
21
- },
22
- modal: false,
23
- overlay: false,
24
- closeOnOutsideClick: false,
25
- width: 960,
26
- children: /*#__PURE__*/ jsxs(DrawerContent, {
27
- children: [
28
- displayHeader ?? /*#__PURE__*/ jsx(DrawerHeader, {
29
- children: /*#__PURE__*/ jsx("span", {})
30
- }),
31
- /*#__PURE__*/ jsx(DrawerBody, {
32
- children: displayContent
33
- })
34
- ]
35
- })
36
- });
37
- };
38
- TablePreviewDrawer.displayName = 'TablePreviewDrawer';
39
- export { TablePreviewDrawer };
@@ -1,8 +0,0 @@
1
- import type { FC } from 'react';
2
- export interface TablePreviewToggleProps {
3
- /** Whether the preview drawer is open for this row */
4
- active?: boolean;
5
- /** Toggle callback */
6
- onClick?: () => void;
7
- }
8
- export declare const TablePreviewToggle: FC<TablePreviewToggleProps>;
@@ -1,32 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { PanelRightAnimated } from "../../icons/index.js";
3
- import { Button } from "../Button/index.js";
4
- import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
5
- const TablePreviewToggle = ({ active = false, onClick })=>{
6
- const label = active ? 'Close preview' : 'Preview attack';
7
- return /*#__PURE__*/ jsxs(Tooltip, {
8
- children: [
9
- /*#__PURE__*/ jsx(TooltipTrigger, {
10
- asChild: true,
11
- children: /*#__PURE__*/ jsx(Button, {
12
- variant: active ? 'outline' : 'ghost',
13
- color: "neutral",
14
- size: "small",
15
- className: "p-2",
16
- "aria-pressed": active,
17
- "aria-label": label,
18
- onClick: onClick,
19
- children: /*#__PURE__*/ jsx(PanelRightAnimated, {
20
- active: active,
21
- size: "sm"
22
- })
23
- })
24
- }),
25
- /*#__PURE__*/ jsx(TooltipContent, {
26
- children: label
27
- })
28
- ]
29
- });
30
- };
31
- TablePreviewToggle.displayName = 'TablePreviewToggle';
32
- export { TablePreviewToggle };
@@ -1,16 +0,0 @@
1
- /**
2
- * Encapsulates preview drawer logic for a body cell.
3
- * Returns flags and a click handler based on `preview.trigger` mode.
4
- */
5
- export declare const usePreviewCell: <T>(columnId: string, rowId: string) => {
6
- /** Preview opens by clicking the master cell */
7
- isMasterTrigger: boolean;
8
- /** Preview opens via a toggle button in actions */
9
- isButtonTrigger: boolean;
10
- /** This row is currently shown in the preview drawer */
11
- isActive: boolean;
12
- /** Toggle preview for this row (open/close) */
13
- togglePreview: () => void;
14
- /** Tooltip text for master cell hover */
15
- tooltipText: string | undefined;
16
- };
@@ -1,25 +0,0 @@
1
- import { useCallback } from "react";
2
- import { useTableContext } from "../TableContext/index.js";
3
- const usePreviewCell = (columnId, rowId)=>{
4
- const { masterColumnId, preview } = useTableContext();
5
- const isMasterColumn = columnId === masterColumnId;
6
- const hasPreview = isMasterColumn && !!preview.renderContent;
7
- const isMasterTrigger = hasPreview && 'master' === preview.trigger;
8
- const isButtonTrigger = hasPreview && 'button' === preview.trigger;
9
- const isActive = preview.rowId === rowId;
10
- const togglePreview = useCallback(()=>{
11
- preview.setRowId(isActive ? null : rowId);
12
- }, [
13
- preview.setRowId,
14
- isActive,
15
- rowId
16
- ]);
17
- return {
18
- isMasterTrigger,
19
- isButtonTrigger,
20
- isActive,
21
- togglePreview,
22
- tooltipText: hasPreview ? preview.tooltipText : void 0
23
- };
24
- };
25
- export { usePreviewCell };