@xcelsior/ui-spreadsheets 1.1.4 → 1.1.6

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.
@@ -1,6 +1,6 @@
1
1
  import type React from 'react';
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import { HiChevronDown, HiChevronRight, HiDuplicate, HiTrash } from 'react-icons/hi';
3
+ import { HiChevronDown, HiChevronRight } from 'react-icons/hi';
4
4
  import { AiFillHighlight } from 'react-icons/ai';
5
5
  import { FaComment, FaRegComment } from 'react-icons/fa';
6
6
  import { cn } from '../utils';
@@ -13,6 +13,7 @@ import { ColorPickerPopover } from './ColorPickerPopover';
13
13
  import { type SpreadsheetSettings, SpreadsheetSettingsModal } from './SpreadsheetSettingsModal';
14
14
  import { AddCommentModal, ViewCommentsModal } from './CommentModals';
15
15
  import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
16
+ import { RowContextMenu } from './RowContextMenu';
16
17
  import { Pagination } from '@xcelsior/design-system';
17
18
  import { useSpreadsheetFiltering } from '../hooks/useSpreadsheetFiltering';
18
19
  import { useSpreadsheetHighlighting } from '../hooks/useSpreadsheetHighlighting';
@@ -25,7 +26,7 @@ import { useSpreadsheetComments } from '../hooks/useSpreadsheetComments';
25
26
  import { useSpreadsheetUndoRedo } from '../hooks/useSpreadsheetUndoRedo';
26
27
  import { useSpreadsheetKeyboardShortcuts } from '../hooks/useSpreadsheetKeyboardShortcuts';
27
28
  import { useSpreadsheetSelection } from '../hooks/useSpreadsheetSelection';
28
- import type { CellEdit, CellPosition, SpreadsheetProps } from '../types';
29
+ import type { CellEdit, SpreadsheetProps } from '../types';
29
30
 
30
31
  type SingleCellEdit = {
31
32
  rowId: string | number;
@@ -84,8 +85,6 @@ export function Spreadsheet<T extends Record<string, any>>({
84
85
  onFilterChange,
85
86
  onRowClick,
86
87
  onRowDoubleClick,
87
- onRowClone,
88
- onRowDelete,
89
88
  onAddCellComment,
90
89
  onToggleCommentResolved,
91
90
  onRowHighlight,
@@ -110,6 +109,7 @@ export function Spreadsheet<T extends Record<string, any>>({
110
109
  cellHighlights: externalCellHighlights,
111
110
  cellComments: externalCellComments,
112
111
  rowActions,
112
+ rowContextMenuItems,
113
113
  toolbarMenuItems,
114
114
  // Server-side mode props
115
115
  serverSide = false,
@@ -627,22 +627,6 @@ export function Spreadsheet<T extends Record<string, any>>({
627
627
  setEditingCell(null);
628
628
  }, [setEditingCell]);
629
629
 
630
- // Handle row clone/duplicate
631
- const handleRowClone = useCallback(
632
- (row: T, rowId: string | number) => {
633
- onRowClone?.(row, rowId);
634
- },
635
- [onRowClone]
636
- );
637
-
638
- // Handle row delete
639
- const handleRowDelete = useCallback(
640
- (row: T, rowId: string | number) => {
641
- onRowDelete?.(row, rowId);
642
- },
643
- [onRowDelete]
644
- );
645
-
646
630
  // Handle row index highlight (using unified column highlight with special ID)
647
631
  const handleRowIndexHighlightClick = useCallback(() => {
648
632
  setHighlightPickerColumn(ROW_INDEX_COLUMN_ID);
@@ -889,41 +873,24 @@ export function Spreadsheet<T extends Record<string, any>>({
889
873
  }),
890
874
  }}
891
875
  >
892
- <div className="relative flex items-center justify-center">
893
- {/* Row number - always centered */}
876
+ <div className="relative flex items-center justify-center w-full h-full">
877
+ {/* Row number - centered */}
894
878
  <span>{displayIndex}</span>
895
879
 
896
- {/* Action buttons - absolute positioned to the right */}
897
- <div className="absolute right-0 flex items-center gap-0.5">
898
- {/* Clone/Duplicate Row */}
899
- {onRowClone && (
900
- <button
901
- type="button"
902
- onClick={(e) => {
903
- e.stopPropagation();
904
- handleRowClone(row, rowId);
905
- }}
906
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
907
- title="Duplicate row"
908
- >
909
- <HiDuplicate className="h-2.5 w-2.5 text-gray-500" />
910
- </button>
911
- )}
912
-
913
- {/* Delete Row */}
914
- {onRowDelete && (
915
- <button
916
- type="button"
917
- onClick={(e) => {
918
- e.stopPropagation();
919
- handleRowDelete(row, rowId);
920
- }}
921
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-red-100 rounded"
922
- title="Delete row"
923
- >
924
- <HiTrash className="h-2.5 w-2.5 text-gray-500 hover:text-red-500" />
925
- </button>
926
- )}
880
+ {/* Action buttons - absolute overlay, spaced evenly */}
881
+ <div className="absolute inset-0 flex items-center justify-evenly">
882
+ {/* Context Menu (3-dot menu for row actions) */}
883
+ {rowContextMenuItems &&
884
+ rowContextMenuItems.length > 0 && (
885
+ <RowContextMenu
886
+ row={row}
887
+ rowId={rowId}
888
+ items={rowContextMenuItems}
889
+ compactMode={
890
+ effectiveCompactMode
891
+ }
892
+ />
893
+ )}
927
894
 
928
895
  {/* Highlight Row */}
929
896
  {enableHighlighting && (
@@ -91,7 +91,9 @@ export function useSpreadsheetHighlighting({
91
91
  const [rowHighlightsInternal, setRowHighlightsInternal] = useState<CellHighlight[]>([]);
92
92
 
93
93
  // Column-level highlights (internal state, can be overridden by external)
94
- const [columnHighlightsInternal, setColumnHighlightsInternal] = useState<Record<string, string>>({});
94
+ const [columnHighlightsInternal, setColumnHighlightsInternal] = useState<
95
+ Record<string, string>
96
+ >({});
95
97
 
96
98
  // Picker states
97
99
  const [highlightPickerRow, setHighlightPickerRow] = useState<string | number | null>(null);
@@ -127,7 +129,9 @@ export function useSpreadsheetHighlighting({
127
129
  if (existing) {
128
130
  if (color === null) {
129
131
  // Remove highlight
130
- return prev.filter((h) => !(h.rowId === rowId && h.columnId === columnId));
132
+ return prev.filter(
133
+ (h) => !(h.rowId === rowId && h.columnId === columnId)
134
+ );
131
135
  }
132
136
  // Update color
133
137
  return prev.map((h) =>
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export { SpreadsheetHeader } from './components/SpreadsheetHeader';
7
7
  export { SpreadsheetFilterDropdown } from './components/SpreadsheetFilterDropdown';
8
8
  export { SpreadsheetToolbar } from './components/SpreadsheetToolbar';
9
9
  export { SpreadsheetSettingsModal } from './components/SpreadsheetSettingsModal';
10
+ export { RowContextMenu } from './components/RowContextMenu';
10
11
  export type { SpreadsheetSettings } from './components/SpreadsheetSettingsModal';
11
12
 
12
13
  // Types
@@ -31,5 +32,6 @@ export type {
31
32
  SelectionEdge,
32
33
  PaginationState,
33
34
  RowAction,
35
+ RowContextMenuItem,
34
36
  ToolbarMenuItem,
35
37
  } from './types';
package/src/types.ts CHANGED
@@ -296,6 +296,29 @@ export interface RowAction<T = any> {
296
296
  className?: string;
297
297
  }
298
298
 
299
+ /**
300
+ * Row context menu item configuration
301
+ * Used for the 3-dot menu that appears on row hover
302
+ */
303
+ export interface RowContextMenuItem<T = any> {
304
+ /** Unique identifier */
305
+ id: string;
306
+ /** Display label for the menu item */
307
+ label: string;
308
+ /** Icon component to display next to the label */
309
+ icon?: React.ReactNode;
310
+ /** Callback when menu item is clicked */
311
+ onClick: (row: T, rowId: string | number) => void;
312
+ /** Whether the menu item is visible */
313
+ visible?: (row: T) => boolean;
314
+ /** Whether the menu item is disabled */
315
+ disabled?: (row: T) => boolean;
316
+ /** Variant for styling (e.g., destructive for delete actions) */
317
+ variant?: 'default' | 'destructive';
318
+ /** Custom className */
319
+ className?: string;
320
+ }
321
+
299
322
  /**
300
323
  * Server-side pagination state for controlled pagination
301
324
  */
@@ -332,10 +355,6 @@ export interface SpreadsheetProps<T = any> {
332
355
  onRowClick?: (row: T, rowIndex: number) => void;
333
356
  /** Callback for row double click */
334
357
  onRowDoubleClick?: (row: T, rowIndex: number) => void;
335
- /** Callback when a row is cloned/duplicated */
336
- onRowClone?: (row: T, rowId: string | number) => void;
337
- /** Callback when a row is deleted */
338
- onRowDelete?: (row: T, rowId: string | number) => void;
339
358
  /** Callback when a cell comment is added */
340
359
  onAddCellComment?: (rowId: string | number, columnId: string, comment: string) => void;
341
360
  /** Callback when a comment's resolved status is toggled */
@@ -404,6 +423,12 @@ export interface SpreadsheetProps<T = any> {
404
423
  cellComments?: CellComment[];
405
424
  /** Custom row actions to display in the index column */
406
425
  rowActions?: RowAction<T>[];
426
+ /**
427
+ * Context menu items to display in the 3-dot menu for each row.
428
+ * These appear in a dropdown when the user clicks the 3-dot icon on row hover.
429
+ * Use this for actions like clone, delete, view, edit, etc.
430
+ */
431
+ rowContextMenuItems?: RowContextMenuItem<T>[];
407
432
  /** Custom menu items to display in the toolbar's "More" dropdown menu */
408
433
  toolbarMenuItems?: ToolbarMenuItem[];
409
434