@xcelsior/ui-spreadsheets 1.2.2 → 1.3.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.
Files changed (99) hide show
  1. package/.omc/state/agent-replay-0cead415-b3bd-40fd-b199-47371946c4db.jsonl +25 -0
  2. package/.omc/state/idle-notif-cooldown.json +3 -0
  3. package/.omc/state/last-tool-error.json +7 -0
  4. package/.omc/state/mission-state.json +179 -0
  5. package/.omc/state/subagent-tracking.json +116 -0
  6. package/.turbo/turbo-build.log +28 -28
  7. package/.turbo/turbo-lint.log +140 -0
  8. package/dist/index.d.mts +94 -4
  9. package/dist/index.d.ts +94 -4
  10. package/dist/index.js +2133 -1156
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +2023 -1048
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/styles/globals.css +156 -16
  15. package/dist/styles/globals.css.map +1 -1
  16. package/package.json +1 -1
  17. package/plans/20260330-1230-spreadsheet-features/phase-01-types-and-duplicates-hook.md +73 -0
  18. package/plans/20260330-1230-spreadsheet-features/phase-02-filter-dropdown-portal.md +90 -0
  19. package/plans/20260330-1230-spreadsheet-features/phase-03-header-overflow-menu.md +101 -0
  20. package/plans/20260330-1230-spreadsheet-features/phase-04-integration.md +193 -0
  21. package/plans/20260330-1230-spreadsheet-features/plan.md +59 -0
  22. package/src/components/ColorPickerPopover.tsx +77 -32
  23. package/src/components/ColumnHeaderActions.tsx +241 -1
  24. package/src/components/RowIndexColumnHeader.tsx +13 -17
  25. package/src/components/SelectionSummaryBar.tsx +103 -0
  26. package/src/components/Spreadsheet.stories.tsx +254 -0
  27. package/src/components/Spreadsheet.tsx +234 -189
  28. package/src/components/SpreadsheetCell.tsx +280 -42
  29. package/src/components/SpreadsheetFilterDropdown.tsx +178 -13
  30. package/src/components/SpreadsheetHeader.tsx +79 -24
  31. package/src/components/SpreadsheetSettingsModal.tsx +4 -0
  32. package/src/hooks/useSpreadsheetColumnResize.ts +143 -0
  33. package/src/hooks/useSpreadsheetDuplicates.ts +149 -0
  34. package/src/hooks/useSpreadsheetFiltering.ts +18 -1
  35. package/src/hooks/useSpreadsheetHighlighting.ts +23 -3
  36. package/src/hooks/useSpreadsheetKeyboardShortcuts.ts +16 -0
  37. package/src/hooks/useSpreadsheetPinning.ts +148 -134
  38. package/src/hooks/useSpreadsheetSelection.ts +10 -22
  39. package/src/hooks/useSpreadsheetSummary.ts +68 -0
  40. package/src/index.ts +4 -1
  41. package/src/styles/globals.css +51 -0
  42. package/src/types.ts +50 -2
  43. package/storybook-static/assets/Color-YHDXOIA2-CtQurLnT.js +1 -0
  44. package/storybook-static/assets/DocsRenderer-CFRXHY34-oxrW8Hvo.js +575 -0
  45. package/storybook-static/assets/Spreadsheet.stories-DvhhzuK4.js +1357 -0
  46. package/storybook-static/assets/chunk-XP5HYGXS-BpfKkqn7.js +1 -0
  47. package/storybook-static/assets/entry-preview-CkBGHCAN.js +2 -0
  48. package/storybook-static/assets/entry-preview-docs-ugJb6pa8.js +46 -0
  49. package/storybook-static/assets/iframe-CPp2u3vg.js +211 -0
  50. package/storybook-static/assets/index-BB9bPxRC.js +24 -0
  51. package/storybook-static/assets/index-BQFlzFLk.js +9 -0
  52. package/storybook-static/assets/index-CtvPRVHf.js +9 -0
  53. package/storybook-static/assets/index-DgH-xKnr.js +11 -0
  54. package/storybook-static/assets/index-DrFu-skq.js +6 -0
  55. package/storybook-static/assets/index-DrdPSA1J.js +240 -0
  56. package/storybook-static/assets/index-DzFBShOR.js +20 -0
  57. package/storybook-static/assets/index-v-1boR4t.js +1 -0
  58. package/storybook-static/assets/preview-B8lJiyuQ.js +34 -0
  59. package/storybook-static/assets/preview-BBWR9nbA.js +1 -0
  60. package/storybook-static/assets/preview-BWzBA1C2.js +396 -0
  61. package/storybook-static/assets/preview-Bm0S-uxO.css +1 -0
  62. package/storybook-static/assets/preview-CvbIS5ZJ.js +1 -0
  63. package/storybook-static/assets/preview-DD_OYowb.js +1 -0
  64. package/storybook-static/assets/preview-DGUiP6tS.js +7 -0
  65. package/storybook-static/assets/preview-DHQbi4pV.js +1 -0
  66. package/storybook-static/assets/preview-DwI0w3cI.js +1 -0
  67. package/storybook-static/assets/preview-DyR7iiFG.js +1 -0
  68. package/storybook-static/assets/preview-zxZ6Be2V.js +2 -0
  69. package/storybook-static/assets/react-18-Pj8skaX9.js +1 -0
  70. package/storybook-static/assets/test-utils-quxJ1Z79.js +9 -0
  71. package/storybook-static/favicon.svg +1 -0
  72. package/storybook-static/iframe.html +666 -0
  73. package/storybook-static/index.html +177 -0
  74. package/storybook-static/index.json +1 -0
  75. package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
  76. package/storybook-static/nunito-sans-bold.woff2 +0 -0
  77. package/storybook-static/nunito-sans-italic.woff2 +0 -0
  78. package/storybook-static/nunito-sans-regular.woff2 +0 -0
  79. package/storybook-static/project.json +1 -0
  80. package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
  81. package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
  82. package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
  83. package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
  84. package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
  85. package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
  86. package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
  87. package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
  88. package/storybook-static/sb-addons/interactions-10/manager-bundle.js +222 -0
  89. package/storybook-static/sb-addons/links-1/manager-bundle.js +3 -0
  90. package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
  91. package/storybook-static/sb-common-assets/favicon.svg +1 -0
  92. package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  93. package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  94. package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  95. package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  96. package/storybook-static/sb-manager/globals-module-info.js +1052 -0
  97. package/storybook-static/sb-manager/globals-runtime.js +42127 -0
  98. package/storybook-static/sb-manager/globals.js +48 -0
  99. package/storybook-static/sb-manager/runtime.js +12048 -0
package/dist/index.d.mts CHANGED
@@ -28,9 +28,24 @@ interface SpreadsheetColumn<T = any> {
28
28
  /** Whether the column can have comments (defaults to true) */
29
29
  commentable?: boolean;
30
30
  /** Type of data in the column (for filtering/formatting) */
31
- type?: 'text' | 'number' | 'date' | 'select' | 'boolean' | 'checkbox';
31
+ type?: 'text' | 'number' | 'date' | 'select' | 'boolean' | 'checkbox' | 'autocomplete';
32
32
  /** Options for select type columns */
33
33
  options?: string[];
34
+ /** Options for autocomplete type columns (label/value pairs) */
35
+ autocompleteOptions?: {
36
+ label: string;
37
+ value: string | number;
38
+ }[];
39
+ /** Optional async (or sync) search function for server-side autocomplete filtering */
40
+ onAutocompleteSearch?: (searchTerm: string) => Promise<{
41
+ label: string;
42
+ value: string | number;
43
+ }[]> | {
44
+ label: string;
45
+ value: string | number;
46
+ }[];
47
+ /** Convert a stored value to its display label */
48
+ getOptionLabel?: (value: any, row?: any) => string;
34
49
  /** Custom render function for cell content */
35
50
  render?: (value: any, row: T, rowIndex: number) => React$1.ReactNode;
36
51
  /** Custom cell value getter */
@@ -121,6 +136,8 @@ interface SpreadsheetColumnFilter {
121
136
  includeBlanks?: boolean;
122
137
  /** Exclude blank/empty values */
123
138
  excludeBlanks?: boolean;
139
+ /** Show only rows with duplicate values in this column */
140
+ showDuplicatesOnly?: boolean;
124
141
  }
125
142
  /**
126
143
  * Cell position identifier
@@ -343,6 +360,8 @@ interface SpreadsheetProps<T = any> {
343
360
  autoSave?: boolean;
344
361
  /** Whether compact view is enabled */
345
362
  compactView?: boolean;
363
+ /** Persisted column widths (columnId → width in px) */
364
+ columnWidths?: Record<string, number>;
346
365
  };
347
366
  /** Callback when spreadsheet settings are changed by the user */
348
367
  onSettingsChange?: (settings: {
@@ -415,6 +434,10 @@ interface SpreadsheetProps<T = any> {
415
434
  * When provided, filtering becomes controlled by the parent component.
416
435
  */
417
436
  filters?: Record<string, SpreadsheetColumnFilter>;
437
+ /** Column IDs that have duplicate checking enabled */
438
+ duplicateCheckColumns?: string[];
439
+ /** Callback when duplicate check columns change (for persistence) */
440
+ onDuplicateCheckChange?: (columns: string[]) => void;
418
441
  }
419
442
  /**
420
443
  * Selection edge for cell border styling
@@ -471,8 +494,16 @@ interface SpreadsheetCellProps {
471
494
  leftOffset?: number;
472
495
  /** Right offset for sticky positioning */
473
496
  rightOffset?: number;
497
+ /** Z-index for pinned columns (dynamic, based on pin order) */
498
+ pinnedZIndex?: number;
499
+ /** Whether this is an odd row (for zebra striping) */
500
+ isOddRow?: boolean;
501
+ /** Resolved column width from resize */
502
+ resolvedWidth?: number;
474
503
  /** Callback when cell is clicked */
475
504
  onClick?: (event: React$1.MouseEvent) => void;
505
+ /** Callback when cell is double-clicked (enter edit mode) */
506
+ onDoubleClick?: (event: React$1.MouseEvent) => void;
476
507
  /** Callback when mouse down on cell (for drag selection) */
477
508
  onMouseDown?: (event: React$1.MouseEvent) => void;
478
509
  /** Callback when mouse enters cell (for drag selection) */
@@ -489,6 +520,8 @@ interface SpreadsheetCellProps {
489
520
  onAddComment?: () => void;
490
521
  /** Callback to view comments */
491
522
  onViewComments?: () => void;
523
+ /** Whether this cell's value is a duplicate in its column */
524
+ isDuplicate?: boolean;
492
525
  /** Custom className */
493
526
  className?: string;
494
527
  }
@@ -510,18 +543,38 @@ interface SpreadsheetHeaderProps {
510
543
  leftOffset?: number;
511
544
  /** Right offset for sticky positioning */
512
545
  rightOffset?: number;
546
+ /** Z-index for pinned columns (dynamic, based on pin order) */
547
+ pinnedZIndex?: number;
513
548
  /** Highlight color */
514
549
  highlightColor?: string;
515
550
  /** Compact mode */
516
551
  compactMode?: boolean;
517
- /** Callback when column header is clicked (sorting) */
552
+ /** Callback when column header is clicked (select column) */
518
553
  onClick?: () => void;
554
+ /** Callback when sort button is clicked */
555
+ onSortClick?: () => void;
519
556
  /** Callback to toggle filter dropdown */
520
557
  onFilterClick?: () => void;
521
558
  /** Callback to toggle pin */
522
559
  onPinClick?: () => void;
523
560
  /** Callback to highlight column */
524
561
  onHighlightClick?: () => void;
562
+ /** Whether duplicate checking is enabled for this column */
563
+ hasDuplicateCheck?: boolean;
564
+ /** Callback to toggle duplicate check */
565
+ onDuplicateCheckClick?: () => void;
566
+ /** Number of rows with duplicate values (shown as badge when > 0) */
567
+ duplicateCount?: number;
568
+ /** Resize handle props from useSpreadsheetColumnResize */
569
+ resizeHandleProps?: {
570
+ onMouseDown: (e: React$1.MouseEvent) => void;
571
+ style: React$1.CSSProperties;
572
+ className: string;
573
+ };
574
+ /** Resolved column width (from resize or config) */
575
+ resolvedWidth?: number;
576
+ /** Top offset for sticky positioning (when group headers exist above) */
577
+ topOffset?: number;
525
578
  /** Custom className */
526
579
  className?: string;
527
580
  }
@@ -541,6 +594,10 @@ interface SpreadsheetFilterDropdownProps {
541
594
  onClose: () => void;
542
595
  /** Custom className */
543
596
  className?: string;
597
+ /** Ref to the trigger element for portal positioning. When provided, renders via React portal at document.body. */
598
+ triggerRef?: React$1.RefObject<HTMLElement>;
599
+ /** Whether duplicate checking is enabled for this column (shows "Show only duplicates" option) */
600
+ hasDuplicateCheck?: boolean;
544
601
  }
545
602
  /**
546
603
  * A menu item that can be added to the toolbar's "More" dropdown menu
@@ -673,11 +730,33 @@ interface SpreadsheetColumnGroupHeaderProps {
673
730
  * />
674
731
  * ```
675
732
  */
676
- declare function Spreadsheet<T extends Record<string, any>>({ data, columns, columnGroups, getRowId, onCellsEdit, onSelectionChange, onSortChange, onFilterChange, afterFiltered, onRowClick, onRowDoubleClick, onAddCellComment, onToggleCommentResolved, onRowHighlight, onColumnHighlight, onCellHighlight, showToolbar, showPagination, enableRowSelection, enableCellEditing, enableComments, enableHighlighting, enableUndoRedo, pageSizeOptions, onSave, settings: initialSettings, onSettingsChange, isLoading, className, emptyMessage, rowHighlights: externalRowHighlights, columnHighlights: externalColumnHighlights, cellHighlights: externalCellHighlights, cellComments: externalCellComments, rowActions, rowContextMenuItems, toolbarMenuItems, serverSide, totalItems, currentPage: controlledCurrentPage, pageSize: controlledPageSize, sortConfig: controlledSortConfig, onPageChange, filters: controlledFilters, }: SpreadsheetProps<T>): react_jsx_runtime.JSX.Element;
733
+ declare function Spreadsheet<T extends Record<string, any>>({ data, columns, columnGroups, getRowId, onCellsEdit, onSelectionChange, onSortChange, onFilterChange, afterFiltered, onRowClick, onRowDoubleClick, onAddCellComment, onToggleCommentResolved, onRowHighlight, onColumnHighlight, onCellHighlight, showToolbar, showPagination, enableRowSelection, enableCellEditing, enableComments, enableHighlighting, enableUndoRedo, pageSizeOptions, onSave, settings: initialSettings, onSettingsChange, isLoading, className, emptyMessage, rowHighlights: externalRowHighlights, columnHighlights: externalColumnHighlights, cellHighlights: externalCellHighlights, cellComments: externalCellComments, rowActions, rowContextMenuItems, toolbarMenuItems, serverSide, totalItems, currentPage: controlledCurrentPage, pageSize: controlledPageSize, sortConfig: controlledSortConfig, onPageChange, filters: controlledFilters, duplicateCheckColumns: propDuplicateCheckColumns, onDuplicateCheckChange, }: SpreadsheetProps<T>): react_jsx_runtime.JSX.Element;
677
734
  declare namespace Spreadsheet {
678
735
  var displayName: string;
679
736
  }
680
737
 
738
+ interface UseSpreadsheetDuplicatesOptions<T> {
739
+ data: T[];
740
+ columns: SpreadsheetColumn<T>[];
741
+ duplicateCheckColumns: string[];
742
+ getRowId: (row: T) => string | number;
743
+ }
744
+ interface UseSpreadsheetDuplicatesReturn {
745
+ /** Check if a specific cell value is a duplicate in its column */
746
+ isCellDuplicate: (rowId: string | number, columnId: string) => boolean;
747
+ /** Get the duplicate count for a value in a column */
748
+ getDuplicateCount: (columnId: string, value: any) => number;
749
+ /** Get the number of rows with duplicate values for a column */
750
+ getDuplicateColumnCount: (columnId: string) => number;
751
+ /** Map of columnId -> Set of rowIds that have duplicate values */
752
+ duplicateRowIds: Map<string, Set<string | number>>;
753
+ /** Set of column IDs with duplicate checking enabled */
754
+ duplicateCheckColumns: Set<string>;
755
+ /** Toggle duplicate check for a column */
756
+ toggleDuplicateCheck: (columnId: string) => void;
757
+ }
758
+ declare function useSpreadsheetDuplicates<T>({ data, columns, duplicateCheckColumns, getRowId, }: UseSpreadsheetDuplicatesOptions<T>): UseSpreadsheetDuplicatesReturn;
759
+
681
760
  /**
682
761
  * Memoized SpreadsheetCell to prevent unnecessary re-renders
683
762
  * Only re-renders when props that affect this specific cell change
@@ -700,10 +779,17 @@ declare const MemoizedSpreadsheetCell: React$1.NamedExoticComponent<SpreadsheetC
700
779
  declare const SpreadsheetHeader: React$1.FC<SpreadsheetHeaderProps & {
701
780
  children?: React$1.ReactNode;
702
781
  }>;
782
+ declare const MemoizedSpreadsheetHeader: React$1.NamedExoticComponent<SpreadsheetHeaderProps & {
783
+ children?: React$1.ReactNode;
784
+ }>;
703
785
 
704
786
  /**
705
787
  * SpreadsheetFilterDropdown component - Condition-based filter dropdown for columns.
706
788
  * Supports text conditions, number conditions, and date conditions.
789
+ *
790
+ * When `triggerRef` is provided the dropdown renders via React.createPortal at
791
+ * document.body and positions itself relative to the trigger element.
792
+ * Falls back to absolute positioning when no triggerRef is given.
707
793
  */
708
794
  declare const SpreadsheetFilterDropdown: React$1.FC<SpreadsheetFilterDropdownProps>;
709
795
 
@@ -749,6 +835,10 @@ interface SpreadsheetSettings {
749
835
  autoSave?: boolean;
750
836
  /** Whether compact view is enabled */
751
837
  compactView?: boolean;
838
+ /** Persisted column widths (columnId → width in px) */
839
+ columnWidths?: Record<string, number>;
840
+ /** Column IDs with duplicate checking enabled */
841
+ duplicateCheckColumns?: string[];
752
842
  }
753
843
  interface SpreadsheetSettingsModalProps {
754
844
  /** Whether the modal is open */
@@ -821,4 +911,4 @@ interface ActiveFiltersDisplayProps {
821
911
  */
822
912
  declare const ActiveFiltersDisplay: React$1.FC<ActiveFiltersDisplayProps>;
823
913
 
824
- export { ActiveFiltersDisplay, type ActiveFiltersDisplayProps, type CellComment, type CellEdit, type CellHighlight, type CellPosition, type CellRange, type PaginationState, type RowAction, RowContextMenu, type RowContextMenuItem, type SelectionEdge, type SelectionState, Spreadsheet, MemoizedSpreadsheetCell as SpreadsheetCell, type SpreadsheetCellProps, type SpreadsheetColumn, type SpreadsheetColumnFilter, type SpreadsheetColumnGroup, type SpreadsheetColumnGroupHeaderProps, SpreadsheetFilterDropdown, type SpreadsheetFilterDropdownProps, SpreadsheetHeader, type SpreadsheetHeaderProps, type SpreadsheetProps, type SpreadsheetSettings, SpreadsheetSettingsModal, type SpreadsheetSortConfig, type SpreadsheetState, SpreadsheetToolbar, type SpreadsheetToolbarProps, type ToolbarMenuItem };
914
+ export { ActiveFiltersDisplay, type ActiveFiltersDisplayProps, type CellComment, type CellEdit, type CellHighlight, type CellPosition, type CellRange, MemoizedSpreadsheetHeader, type PaginationState, type RowAction, RowContextMenu, type RowContextMenuItem, type SelectionEdge, type SelectionState, Spreadsheet, MemoizedSpreadsheetCell as SpreadsheetCell, type SpreadsheetCellProps, type SpreadsheetColumn, type SpreadsheetColumnFilter, type SpreadsheetColumnGroup, type SpreadsheetColumnGroupHeaderProps, SpreadsheetFilterDropdown, type SpreadsheetFilterDropdownProps, SpreadsheetHeader, type SpreadsheetHeaderProps, type SpreadsheetProps, type SpreadsheetSettings, SpreadsheetSettingsModal, type SpreadsheetSortConfig, type SpreadsheetState, SpreadsheetToolbar, type SpreadsheetToolbarProps, type ToolbarMenuItem, useSpreadsheetDuplicates };
package/dist/index.d.ts CHANGED
@@ -28,9 +28,24 @@ interface SpreadsheetColumn<T = any> {
28
28
  /** Whether the column can have comments (defaults to true) */
29
29
  commentable?: boolean;
30
30
  /** Type of data in the column (for filtering/formatting) */
31
- type?: 'text' | 'number' | 'date' | 'select' | 'boolean' | 'checkbox';
31
+ type?: 'text' | 'number' | 'date' | 'select' | 'boolean' | 'checkbox' | 'autocomplete';
32
32
  /** Options for select type columns */
33
33
  options?: string[];
34
+ /** Options for autocomplete type columns (label/value pairs) */
35
+ autocompleteOptions?: {
36
+ label: string;
37
+ value: string | number;
38
+ }[];
39
+ /** Optional async (or sync) search function for server-side autocomplete filtering */
40
+ onAutocompleteSearch?: (searchTerm: string) => Promise<{
41
+ label: string;
42
+ value: string | number;
43
+ }[]> | {
44
+ label: string;
45
+ value: string | number;
46
+ }[];
47
+ /** Convert a stored value to its display label */
48
+ getOptionLabel?: (value: any, row?: any) => string;
34
49
  /** Custom render function for cell content */
35
50
  render?: (value: any, row: T, rowIndex: number) => React$1.ReactNode;
36
51
  /** Custom cell value getter */
@@ -121,6 +136,8 @@ interface SpreadsheetColumnFilter {
121
136
  includeBlanks?: boolean;
122
137
  /** Exclude blank/empty values */
123
138
  excludeBlanks?: boolean;
139
+ /** Show only rows with duplicate values in this column */
140
+ showDuplicatesOnly?: boolean;
124
141
  }
125
142
  /**
126
143
  * Cell position identifier
@@ -343,6 +360,8 @@ interface SpreadsheetProps<T = any> {
343
360
  autoSave?: boolean;
344
361
  /** Whether compact view is enabled */
345
362
  compactView?: boolean;
363
+ /** Persisted column widths (columnId → width in px) */
364
+ columnWidths?: Record<string, number>;
346
365
  };
347
366
  /** Callback when spreadsheet settings are changed by the user */
348
367
  onSettingsChange?: (settings: {
@@ -415,6 +434,10 @@ interface SpreadsheetProps<T = any> {
415
434
  * When provided, filtering becomes controlled by the parent component.
416
435
  */
417
436
  filters?: Record<string, SpreadsheetColumnFilter>;
437
+ /** Column IDs that have duplicate checking enabled */
438
+ duplicateCheckColumns?: string[];
439
+ /** Callback when duplicate check columns change (for persistence) */
440
+ onDuplicateCheckChange?: (columns: string[]) => void;
418
441
  }
419
442
  /**
420
443
  * Selection edge for cell border styling
@@ -471,8 +494,16 @@ interface SpreadsheetCellProps {
471
494
  leftOffset?: number;
472
495
  /** Right offset for sticky positioning */
473
496
  rightOffset?: number;
497
+ /** Z-index for pinned columns (dynamic, based on pin order) */
498
+ pinnedZIndex?: number;
499
+ /** Whether this is an odd row (for zebra striping) */
500
+ isOddRow?: boolean;
501
+ /** Resolved column width from resize */
502
+ resolvedWidth?: number;
474
503
  /** Callback when cell is clicked */
475
504
  onClick?: (event: React$1.MouseEvent) => void;
505
+ /** Callback when cell is double-clicked (enter edit mode) */
506
+ onDoubleClick?: (event: React$1.MouseEvent) => void;
476
507
  /** Callback when mouse down on cell (for drag selection) */
477
508
  onMouseDown?: (event: React$1.MouseEvent) => void;
478
509
  /** Callback when mouse enters cell (for drag selection) */
@@ -489,6 +520,8 @@ interface SpreadsheetCellProps {
489
520
  onAddComment?: () => void;
490
521
  /** Callback to view comments */
491
522
  onViewComments?: () => void;
523
+ /** Whether this cell's value is a duplicate in its column */
524
+ isDuplicate?: boolean;
492
525
  /** Custom className */
493
526
  className?: string;
494
527
  }
@@ -510,18 +543,38 @@ interface SpreadsheetHeaderProps {
510
543
  leftOffset?: number;
511
544
  /** Right offset for sticky positioning */
512
545
  rightOffset?: number;
546
+ /** Z-index for pinned columns (dynamic, based on pin order) */
547
+ pinnedZIndex?: number;
513
548
  /** Highlight color */
514
549
  highlightColor?: string;
515
550
  /** Compact mode */
516
551
  compactMode?: boolean;
517
- /** Callback when column header is clicked (sorting) */
552
+ /** Callback when column header is clicked (select column) */
518
553
  onClick?: () => void;
554
+ /** Callback when sort button is clicked */
555
+ onSortClick?: () => void;
519
556
  /** Callback to toggle filter dropdown */
520
557
  onFilterClick?: () => void;
521
558
  /** Callback to toggle pin */
522
559
  onPinClick?: () => void;
523
560
  /** Callback to highlight column */
524
561
  onHighlightClick?: () => void;
562
+ /** Whether duplicate checking is enabled for this column */
563
+ hasDuplicateCheck?: boolean;
564
+ /** Callback to toggle duplicate check */
565
+ onDuplicateCheckClick?: () => void;
566
+ /** Number of rows with duplicate values (shown as badge when > 0) */
567
+ duplicateCount?: number;
568
+ /** Resize handle props from useSpreadsheetColumnResize */
569
+ resizeHandleProps?: {
570
+ onMouseDown: (e: React$1.MouseEvent) => void;
571
+ style: React$1.CSSProperties;
572
+ className: string;
573
+ };
574
+ /** Resolved column width (from resize or config) */
575
+ resolvedWidth?: number;
576
+ /** Top offset for sticky positioning (when group headers exist above) */
577
+ topOffset?: number;
525
578
  /** Custom className */
526
579
  className?: string;
527
580
  }
@@ -541,6 +594,10 @@ interface SpreadsheetFilterDropdownProps {
541
594
  onClose: () => void;
542
595
  /** Custom className */
543
596
  className?: string;
597
+ /** Ref to the trigger element for portal positioning. When provided, renders via React portal at document.body. */
598
+ triggerRef?: React$1.RefObject<HTMLElement>;
599
+ /** Whether duplicate checking is enabled for this column (shows "Show only duplicates" option) */
600
+ hasDuplicateCheck?: boolean;
544
601
  }
545
602
  /**
546
603
  * A menu item that can be added to the toolbar's "More" dropdown menu
@@ -673,11 +730,33 @@ interface SpreadsheetColumnGroupHeaderProps {
673
730
  * />
674
731
  * ```
675
732
  */
676
- declare function Spreadsheet<T extends Record<string, any>>({ data, columns, columnGroups, getRowId, onCellsEdit, onSelectionChange, onSortChange, onFilterChange, afterFiltered, onRowClick, onRowDoubleClick, onAddCellComment, onToggleCommentResolved, onRowHighlight, onColumnHighlight, onCellHighlight, showToolbar, showPagination, enableRowSelection, enableCellEditing, enableComments, enableHighlighting, enableUndoRedo, pageSizeOptions, onSave, settings: initialSettings, onSettingsChange, isLoading, className, emptyMessage, rowHighlights: externalRowHighlights, columnHighlights: externalColumnHighlights, cellHighlights: externalCellHighlights, cellComments: externalCellComments, rowActions, rowContextMenuItems, toolbarMenuItems, serverSide, totalItems, currentPage: controlledCurrentPage, pageSize: controlledPageSize, sortConfig: controlledSortConfig, onPageChange, filters: controlledFilters, }: SpreadsheetProps<T>): react_jsx_runtime.JSX.Element;
733
+ declare function Spreadsheet<T extends Record<string, any>>({ data, columns, columnGroups, getRowId, onCellsEdit, onSelectionChange, onSortChange, onFilterChange, afterFiltered, onRowClick, onRowDoubleClick, onAddCellComment, onToggleCommentResolved, onRowHighlight, onColumnHighlight, onCellHighlight, showToolbar, showPagination, enableRowSelection, enableCellEditing, enableComments, enableHighlighting, enableUndoRedo, pageSizeOptions, onSave, settings: initialSettings, onSettingsChange, isLoading, className, emptyMessage, rowHighlights: externalRowHighlights, columnHighlights: externalColumnHighlights, cellHighlights: externalCellHighlights, cellComments: externalCellComments, rowActions, rowContextMenuItems, toolbarMenuItems, serverSide, totalItems, currentPage: controlledCurrentPage, pageSize: controlledPageSize, sortConfig: controlledSortConfig, onPageChange, filters: controlledFilters, duplicateCheckColumns: propDuplicateCheckColumns, onDuplicateCheckChange, }: SpreadsheetProps<T>): react_jsx_runtime.JSX.Element;
677
734
  declare namespace Spreadsheet {
678
735
  var displayName: string;
679
736
  }
680
737
 
738
+ interface UseSpreadsheetDuplicatesOptions<T> {
739
+ data: T[];
740
+ columns: SpreadsheetColumn<T>[];
741
+ duplicateCheckColumns: string[];
742
+ getRowId: (row: T) => string | number;
743
+ }
744
+ interface UseSpreadsheetDuplicatesReturn {
745
+ /** Check if a specific cell value is a duplicate in its column */
746
+ isCellDuplicate: (rowId: string | number, columnId: string) => boolean;
747
+ /** Get the duplicate count for a value in a column */
748
+ getDuplicateCount: (columnId: string, value: any) => number;
749
+ /** Get the number of rows with duplicate values for a column */
750
+ getDuplicateColumnCount: (columnId: string) => number;
751
+ /** Map of columnId -> Set of rowIds that have duplicate values */
752
+ duplicateRowIds: Map<string, Set<string | number>>;
753
+ /** Set of column IDs with duplicate checking enabled */
754
+ duplicateCheckColumns: Set<string>;
755
+ /** Toggle duplicate check for a column */
756
+ toggleDuplicateCheck: (columnId: string) => void;
757
+ }
758
+ declare function useSpreadsheetDuplicates<T>({ data, columns, duplicateCheckColumns, getRowId, }: UseSpreadsheetDuplicatesOptions<T>): UseSpreadsheetDuplicatesReturn;
759
+
681
760
  /**
682
761
  * Memoized SpreadsheetCell to prevent unnecessary re-renders
683
762
  * Only re-renders when props that affect this specific cell change
@@ -700,10 +779,17 @@ declare const MemoizedSpreadsheetCell: React$1.NamedExoticComponent<SpreadsheetC
700
779
  declare const SpreadsheetHeader: React$1.FC<SpreadsheetHeaderProps & {
701
780
  children?: React$1.ReactNode;
702
781
  }>;
782
+ declare const MemoizedSpreadsheetHeader: React$1.NamedExoticComponent<SpreadsheetHeaderProps & {
783
+ children?: React$1.ReactNode;
784
+ }>;
703
785
 
704
786
  /**
705
787
  * SpreadsheetFilterDropdown component - Condition-based filter dropdown for columns.
706
788
  * Supports text conditions, number conditions, and date conditions.
789
+ *
790
+ * When `triggerRef` is provided the dropdown renders via React.createPortal at
791
+ * document.body and positions itself relative to the trigger element.
792
+ * Falls back to absolute positioning when no triggerRef is given.
707
793
  */
708
794
  declare const SpreadsheetFilterDropdown: React$1.FC<SpreadsheetFilterDropdownProps>;
709
795
 
@@ -749,6 +835,10 @@ interface SpreadsheetSettings {
749
835
  autoSave?: boolean;
750
836
  /** Whether compact view is enabled */
751
837
  compactView?: boolean;
838
+ /** Persisted column widths (columnId → width in px) */
839
+ columnWidths?: Record<string, number>;
840
+ /** Column IDs with duplicate checking enabled */
841
+ duplicateCheckColumns?: string[];
752
842
  }
753
843
  interface SpreadsheetSettingsModalProps {
754
844
  /** Whether the modal is open */
@@ -821,4 +911,4 @@ interface ActiveFiltersDisplayProps {
821
911
  */
822
912
  declare const ActiveFiltersDisplay: React$1.FC<ActiveFiltersDisplayProps>;
823
913
 
824
- export { ActiveFiltersDisplay, type ActiveFiltersDisplayProps, type CellComment, type CellEdit, type CellHighlight, type CellPosition, type CellRange, type PaginationState, type RowAction, RowContextMenu, type RowContextMenuItem, type SelectionEdge, type SelectionState, Spreadsheet, MemoizedSpreadsheetCell as SpreadsheetCell, type SpreadsheetCellProps, type SpreadsheetColumn, type SpreadsheetColumnFilter, type SpreadsheetColumnGroup, type SpreadsheetColumnGroupHeaderProps, SpreadsheetFilterDropdown, type SpreadsheetFilterDropdownProps, SpreadsheetHeader, type SpreadsheetHeaderProps, type SpreadsheetProps, type SpreadsheetSettings, SpreadsheetSettingsModal, type SpreadsheetSortConfig, type SpreadsheetState, SpreadsheetToolbar, type SpreadsheetToolbarProps, type ToolbarMenuItem };
914
+ export { ActiveFiltersDisplay, type ActiveFiltersDisplayProps, type CellComment, type CellEdit, type CellHighlight, type CellPosition, type CellRange, MemoizedSpreadsheetHeader, type PaginationState, type RowAction, RowContextMenu, type RowContextMenuItem, type SelectionEdge, type SelectionState, Spreadsheet, MemoizedSpreadsheetCell as SpreadsheetCell, type SpreadsheetCellProps, type SpreadsheetColumn, type SpreadsheetColumnFilter, type SpreadsheetColumnGroup, type SpreadsheetColumnGroupHeaderProps, SpreadsheetFilterDropdown, type SpreadsheetFilterDropdownProps, SpreadsheetHeader, type SpreadsheetHeaderProps, type SpreadsheetProps, type SpreadsheetSettings, SpreadsheetSettingsModal, type SpreadsheetSortConfig, type SpreadsheetState, SpreadsheetToolbar, type SpreadsheetToolbarProps, type ToolbarMenuItem, useSpreadsheetDuplicates };