@xcelsior/ui-spreadsheets 1.3.0 → 1.3.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.
- package/.omc/state/agent-replay-0cead415-b3bd-40fd-b199-47371946c4db.jsonl +2 -0
- package/.omc/state/idle-notif-cooldown.json +1 -1
- package/.omc/state/mission-state.json +31 -21
- package/.omc/state/subagent-tracking.json +12 -3
- package/dist/index.js +86 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Spreadsheet.tsx +68 -16
- package/src/components/SpreadsheetCell.tsx +9 -4
- package/src/hooks/useSpreadsheetFiltering.ts +6 -1
- package/src/hooks/useSpreadsheetKeyboardShortcuts.ts +9 -0
- package/src/hooks/useSpreadsheetSummary.ts +2 -2
package/package.json
CHANGED
|
@@ -285,7 +285,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
285
285
|
const [showSettingsModal, setShowSettingsModal] = useState(false);
|
|
286
286
|
|
|
287
287
|
// Filters panel state
|
|
288
|
-
const [showFiltersPanel, setShowFiltersPanel] = useState(
|
|
288
|
+
const [showFiltersPanel, setShowFiltersPanel] = useState(true);
|
|
289
289
|
|
|
290
290
|
// Undo/Redo hook
|
|
291
291
|
const {
|
|
@@ -465,7 +465,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
465
465
|
const paginatedData = useMemo(() => {
|
|
466
466
|
if (serverSide) {
|
|
467
467
|
// In server-side mode, data is already paginated by the server
|
|
468
|
-
return filteredData;
|
|
468
|
+
return filteredData.toArray();
|
|
469
469
|
}
|
|
470
470
|
const startIndex = (currentPage - 1) * pageSize;
|
|
471
471
|
return filteredData.slice(startIndex, startIndex + pageSize);
|
|
@@ -718,16 +718,23 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
718
718
|
]
|
|
719
719
|
);
|
|
720
720
|
|
|
721
|
-
// Handle cell click -
|
|
721
|
+
// Handle cell click - enter edit mode on single click for editable cells
|
|
722
|
+
// Skip edit mode when shift is held (for multi-cell selection)
|
|
722
723
|
const handleCellClick = useCallback(
|
|
723
724
|
(rowId: string | number, columnId: string, event: React.MouseEvent) => {
|
|
724
725
|
event.stopPropagation();
|
|
725
726
|
handleCellMouseDown(rowId, columnId, event);
|
|
727
|
+
if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {
|
|
728
|
+
const column = columns.find((c) => c.id === columnId);
|
|
729
|
+
if (column?.editable && enableCellEditing) {
|
|
730
|
+
setEditingCell({ rowId, columnId });
|
|
731
|
+
}
|
|
732
|
+
}
|
|
726
733
|
},
|
|
727
|
-
[handleCellMouseDown]
|
|
734
|
+
[handleCellMouseDown, columns, enableCellEditing, setEditingCell]
|
|
728
735
|
);
|
|
729
736
|
|
|
730
|
-
// Handle cell double-click -
|
|
737
|
+
// Handle cell double-click - kept for row-level double click handler
|
|
731
738
|
const handleCellDoubleClick = useCallback(
|
|
732
739
|
(rowId: string | number, columnId: string) => {
|
|
733
740
|
const column = columns.find((c) => c.id === columnId);
|
|
@@ -1086,17 +1093,62 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
1086
1093
|
|
|
1087
1094
|
<tbody>
|
|
1088
1095
|
{isLoading ? (
|
|
1089
|
-
|
|
1090
|
-
<
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1096
|
+
Array.from({ length: Math.min(pageSize, 10) }).map((_, rowIdx) => (
|
|
1097
|
+
<tr key={`skeleton-${rowIdx}`}>
|
|
1098
|
+
{/* Row index skeleton */}
|
|
1099
|
+
<td
|
|
1100
|
+
className={cn(
|
|
1101
|
+
'border border-gray-200 sticky',
|
|
1102
|
+
effectiveCompactMode ? 'px-1.5 py-0.5' : 'px-2.5 py-1.5'
|
|
1103
|
+
)}
|
|
1104
|
+
style={{
|
|
1105
|
+
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1106
|
+
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1107
|
+
left: 0,
|
|
1108
|
+
zIndex: 40,
|
|
1109
|
+
backgroundColor: rowIdx % 2 !== 0 ? '#f9fafb' : 'white',
|
|
1110
|
+
}}
|
|
1111
|
+
>
|
|
1112
|
+
<div
|
|
1113
|
+
className="h-4 bg-gray-200 rounded animate-pulse"
|
|
1114
|
+
style={{ width: '60%', animationDelay: `${rowIdx * 50}ms` }}
|
|
1115
|
+
/>
|
|
1116
|
+
</td>
|
|
1117
|
+
{/* Column skeletons */}
|
|
1118
|
+
{columnRenderItems.map((item, colIdx) => {
|
|
1119
|
+
if (item.type === 'collapsed-placeholder') {
|
|
1120
|
+
return (
|
|
1121
|
+
<td
|
|
1122
|
+
key={`skeleton-${rowIdx}-placeholder-${item.groupId}`}
|
|
1123
|
+
className="border border-gray-200"
|
|
1124
|
+
style={{ backgroundColor: rowIdx % 2 !== 0 ? '#f9fafb' : 'white' }}
|
|
1125
|
+
/>
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
return (
|
|
1129
|
+
<td
|
|
1130
|
+
key={`skeleton-${rowIdx}-${item.column.id}`}
|
|
1131
|
+
className={cn(
|
|
1132
|
+
'border border-gray-200',
|
|
1133
|
+
effectiveCompactMode ? 'px-1.5 py-0.5' : 'px-2.5 py-1.5'
|
|
1134
|
+
)}
|
|
1135
|
+
style={{
|
|
1136
|
+
minWidth: item.column.width ?? 120,
|
|
1137
|
+
backgroundColor: rowIdx % 2 !== 0 ? '#f9fafb' : 'white',
|
|
1138
|
+
}}
|
|
1139
|
+
>
|
|
1140
|
+
<div
|
|
1141
|
+
className="h-4 bg-gray-200 rounded animate-pulse"
|
|
1142
|
+
style={{
|
|
1143
|
+
width: `${55 + ((rowIdx * 7 + colIdx * 13) % 35)}%`,
|
|
1144
|
+
animationDelay: `${(rowIdx * columnRenderItems.length + colIdx) * 30}ms`,
|
|
1145
|
+
}}
|
|
1146
|
+
/>
|
|
1147
|
+
</td>
|
|
1148
|
+
);
|
|
1149
|
+
})}
|
|
1150
|
+
</tr>
|
|
1151
|
+
))
|
|
1100
1152
|
) : paginatedData.length === 0 ? (
|
|
1101
1153
|
<tr>
|
|
1102
1154
|
<td
|
|
@@ -365,9 +365,11 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
if (column.type === 'autocomplete') {
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
368
|
+
// Use localValue to reflect pending changes before parent re-renders with new data
|
|
369
|
+
const displayVal = localValue ?? value;
|
|
370
|
+
if (column.getOptionLabel) return column.getOptionLabel(displayVal, row);
|
|
371
|
+
const match = column.autocompleteOptions?.find((o) => o.value === displayVal);
|
|
372
|
+
return match ? match.label : (displayVal != null && displayVal !== '' ? String(displayVal) : null);
|
|
371
373
|
}
|
|
372
374
|
|
|
373
375
|
return String(value);
|
|
@@ -386,7 +388,10 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
|
|
|
386
388
|
value={localValue}
|
|
387
389
|
column={column}
|
|
388
390
|
compactMode={compactMode}
|
|
389
|
-
onConfirm={
|
|
391
|
+
onConfirm={(newVal) => {
|
|
392
|
+
setLocalValue(newVal);
|
|
393
|
+
onConfirm?.(newVal);
|
|
394
|
+
}}
|
|
390
395
|
onCancel={onCancel}
|
|
391
396
|
/>
|
|
392
397
|
);
|
|
@@ -252,7 +252,12 @@ export function useSpreadsheetFiltering<T extends Record<string, any>>({
|
|
|
252
252
|
|
|
253
253
|
// Text condition filter (advanced)
|
|
254
254
|
if (filter.textCondition) {
|
|
255
|
-
|
|
255
|
+
// For autocomplete columns, filter against the display label instead of the raw value
|
|
256
|
+
const filterableValue =
|
|
257
|
+
column.type === 'autocomplete' && column.getOptionLabel
|
|
258
|
+
? column.getOptionLabel(value, row)
|
|
259
|
+
: value;
|
|
260
|
+
return applyTextCondition(filterableValue, filter.textCondition);
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
// Number condition filter (advanced)
|
|
@@ -89,6 +89,15 @@ export function useSpreadsheetKeyboardShortcuts({
|
|
|
89
89
|
if (!enabled) return;
|
|
90
90
|
|
|
91
91
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
92
|
+
// Skip keyboard shortcuts when user is typing in a form element (e.g., filter dropdown inputs)
|
|
93
|
+
const activeEl = document.activeElement;
|
|
94
|
+
const isInFormElement = activeEl instanceof HTMLInputElement
|
|
95
|
+
|| activeEl instanceof HTMLTextAreaElement
|
|
96
|
+
|| activeEl instanceof HTMLSelectElement;
|
|
97
|
+
if (isInFormElement && event.key !== 'Escape') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
92
101
|
// Escape key
|
|
93
102
|
if (event.key === 'Escape') {
|
|
94
103
|
if (showKeyboardShortcuts) {
|
|
@@ -39,8 +39,8 @@ export function useSpreadsheetSummary<T>({
|
|
|
39
39
|
|
|
40
40
|
for (const { position, value } of selectedCellValues) {
|
|
41
41
|
const column = columns.find((c) => c.id === position.columnId);
|
|
42
|
-
//
|
|
43
|
-
if (column?.type === 'number'
|
|
42
|
+
// Only include values from columns explicitly typed as 'number'
|
|
43
|
+
if (column?.type === 'number') {
|
|
44
44
|
const num = typeof value === 'number' ? value : parseFloat(value);
|
|
45
45
|
if (!isNaN(num)) {
|
|
46
46
|
numericValues.push(num);
|