@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.
- package/.omc/state/agent-replay-0cead415-b3bd-40fd-b199-47371946c4db.jsonl +25 -0
- package/.omc/state/idle-notif-cooldown.json +3 -0
- package/.omc/state/last-tool-error.json +7 -0
- package/.omc/state/mission-state.json +179 -0
- package/.omc/state/subagent-tracking.json +116 -0
- package/.turbo/turbo-build.log +28 -28
- package/.turbo/turbo-lint.log +140 -0
- package/dist/index.d.mts +94 -4
- package/dist/index.d.ts +94 -4
- package/dist/index.js +2133 -1156
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2023 -1048
- package/dist/index.mjs.map +1 -1
- package/dist/styles/globals.css +156 -16
- package/dist/styles/globals.css.map +1 -1
- package/package.json +1 -1
- package/plans/20260330-1230-spreadsheet-features/phase-01-types-and-duplicates-hook.md +73 -0
- package/plans/20260330-1230-spreadsheet-features/phase-02-filter-dropdown-portal.md +90 -0
- package/plans/20260330-1230-spreadsheet-features/phase-03-header-overflow-menu.md +101 -0
- package/plans/20260330-1230-spreadsheet-features/phase-04-integration.md +193 -0
- package/plans/20260330-1230-spreadsheet-features/plan.md +59 -0
- package/src/components/ColorPickerPopover.tsx +77 -32
- package/src/components/ColumnHeaderActions.tsx +241 -1
- package/src/components/RowIndexColumnHeader.tsx +13 -17
- package/src/components/SelectionSummaryBar.tsx +103 -0
- package/src/components/Spreadsheet.stories.tsx +254 -0
- package/src/components/Spreadsheet.tsx +234 -189
- package/src/components/SpreadsheetCell.tsx +280 -42
- package/src/components/SpreadsheetFilterDropdown.tsx +178 -13
- package/src/components/SpreadsheetHeader.tsx +79 -24
- package/src/components/SpreadsheetSettingsModal.tsx +4 -0
- package/src/hooks/useSpreadsheetColumnResize.ts +143 -0
- package/src/hooks/useSpreadsheetDuplicates.ts +149 -0
- package/src/hooks/useSpreadsheetFiltering.ts +18 -1
- package/src/hooks/useSpreadsheetHighlighting.ts +23 -3
- package/src/hooks/useSpreadsheetKeyboardShortcuts.ts +16 -0
- package/src/hooks/useSpreadsheetPinning.ts +148 -134
- package/src/hooks/useSpreadsheetSelection.ts +10 -22
- package/src/hooks/useSpreadsheetSummary.ts +68 -0
- package/src/index.ts +4 -1
- package/src/styles/globals.css +51 -0
- package/src/types.ts +50 -2
- package/storybook-static/assets/Color-YHDXOIA2-CtQurLnT.js +1 -0
- package/storybook-static/assets/DocsRenderer-CFRXHY34-oxrW8Hvo.js +575 -0
- package/storybook-static/assets/Spreadsheet.stories-DvhhzuK4.js +1357 -0
- package/storybook-static/assets/chunk-XP5HYGXS-BpfKkqn7.js +1 -0
- package/storybook-static/assets/entry-preview-CkBGHCAN.js +2 -0
- package/storybook-static/assets/entry-preview-docs-ugJb6pa8.js +46 -0
- package/storybook-static/assets/iframe-CPp2u3vg.js +211 -0
- package/storybook-static/assets/index-BB9bPxRC.js +24 -0
- package/storybook-static/assets/index-BQFlzFLk.js +9 -0
- package/storybook-static/assets/index-CtvPRVHf.js +9 -0
- package/storybook-static/assets/index-DgH-xKnr.js +11 -0
- package/storybook-static/assets/index-DrFu-skq.js +6 -0
- package/storybook-static/assets/index-DrdPSA1J.js +240 -0
- package/storybook-static/assets/index-DzFBShOR.js +20 -0
- package/storybook-static/assets/index-v-1boR4t.js +1 -0
- package/storybook-static/assets/preview-B8lJiyuQ.js +34 -0
- package/storybook-static/assets/preview-BBWR9nbA.js +1 -0
- package/storybook-static/assets/preview-BWzBA1C2.js +396 -0
- package/storybook-static/assets/preview-Bm0S-uxO.css +1 -0
- package/storybook-static/assets/preview-CvbIS5ZJ.js +1 -0
- package/storybook-static/assets/preview-DD_OYowb.js +1 -0
- package/storybook-static/assets/preview-DGUiP6tS.js +7 -0
- package/storybook-static/assets/preview-DHQbi4pV.js +1 -0
- package/storybook-static/assets/preview-DwI0w3cI.js +1 -0
- package/storybook-static/assets/preview-DyR7iiFG.js +1 -0
- package/storybook-static/assets/preview-zxZ6Be2V.js +2 -0
- package/storybook-static/assets/react-18-Pj8skaX9.js +1 -0
- package/storybook-static/assets/test-utils-quxJ1Z79.js +9 -0
- package/storybook-static/favicon.svg +1 -0
- package/storybook-static/iframe.html +666 -0
- package/storybook-static/index.html +177 -0
- package/storybook-static/index.json +1 -0
- package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/project.json +1 -0
- package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
- package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
- package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
- package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/interactions-10/manager-bundle.js +222 -0
- package/storybook-static/sb-addons/links-1/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
- package/storybook-static/sb-common-assets/favicon.svg +1 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/sb-manager/globals-module-info.js +1052 -0
- package/storybook-static/sb-manager/globals-runtime.js +42127 -0
- package/storybook-static/sb-manager/globals.js +48 -0
- package/storybook-static/sb-manager/runtime.js +12048 -0
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/Spreadsheet.tsx
|
|
2
|
-
import { useCallback as
|
|
2
|
+
import { useCallback as useCallback11, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef10, useState as useState16, startTransition } from "react";
|
|
3
3
|
|
|
4
4
|
// ../../../node_modules/.pnpm/react-icons@4.12.0_react@18.3.1/node_modules/react-icons/lib/esm/iconBase.js
|
|
5
5
|
import React2 from "react";
|
|
@@ -93,6 +93,9 @@ function HiCog(props) {
|
|
|
93
93
|
function HiDotsVertical(props) {
|
|
94
94
|
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "d": "M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" } }] })(props);
|
|
95
95
|
}
|
|
96
|
+
function HiExclamation(props) {
|
|
97
|
+
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "fillRule": "evenodd", "d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", "clipRule": "evenodd" } }] })(props);
|
|
98
|
+
}
|
|
96
99
|
function HiEye(props) {
|
|
97
100
|
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "d": "M10 12a2 2 0 100-4 2 2 0 000 4z" } }, { "tag": "path", "attr": { "fillRule": "evenodd", "d": "M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z", "clipRule": "evenodd" } }] })(props);
|
|
98
101
|
}
|
|
@@ -105,6 +108,9 @@ function HiReply(props) {
|
|
|
105
108
|
function HiSortAscending(props) {
|
|
106
109
|
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "d": "M3 3a1 1 0 000 2h11a1 1 0 100-2H3zM3 7a1 1 0 000 2h5a1 1 0 000-2H3zM3 11a1 1 0 100 2h4a1 1 0 100-2H3zM13 16a1 1 0 102 0v-5.586l1.293 1.293a1 1 0 001.414-1.414l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 101.414 1.414L13 10.414V16z" } }] })(props);
|
|
107
110
|
}
|
|
111
|
+
function HiSortDescending(props) {
|
|
112
|
+
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "d": "M3 3a1 1 0 000 2h11a1 1 0 100-2H3zM3 7a1 1 0 000 2h7a1 1 0 100-2H3zM3 11a1 1 0 100 2h4a1 1 0 100-2H3zM15 8a1 1 0 10-2 0v5.586l-1.293-1.293a1 1 0 00-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L15 13.586V8z" } }] })(props);
|
|
113
|
+
}
|
|
108
114
|
function HiViewBoards(props) {
|
|
109
115
|
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 20 20", "fill": "currentColor", "aria-hidden": "true" }, "child": [{ "tag": "path", "attr": { "d": "M2 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V4zM8 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1H9a1 1 0 01-1-1V4zM15 3a1 1 0 00-1 1v12a1 1 0 001 1h2a1 1 0 001-1V4a1 1 0 00-1-1h-2z" } }] })(props);
|
|
110
116
|
}
|
|
@@ -145,165 +151,186 @@ function cn(...inputs) {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
// src/components/SpreadsheetCell.tsx
|
|
148
|
-
import { useState
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
var
|
|
153
|
-
var
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
newMap.set(columnId, "left");
|
|
181
|
-
}
|
|
182
|
-
return newMap;
|
|
183
|
-
});
|
|
184
|
-
}, []);
|
|
185
|
-
const setPinnedColumnsFromIds = useCallback((columnIds) => {
|
|
186
|
-
const map = /* @__PURE__ */ new Map();
|
|
187
|
-
columnIds.forEach((col) => {
|
|
188
|
-
map.set(col, "left");
|
|
189
|
-
});
|
|
190
|
-
setPinnedColumns(map);
|
|
191
|
-
}, []);
|
|
192
|
-
const handleToggleGroupCollapse = useCallback((groupId) => {
|
|
193
|
-
setCollapsedGroups((prev) => {
|
|
194
|
-
const newSet = new Set(prev);
|
|
195
|
-
if (newSet.has(groupId)) {
|
|
196
|
-
newSet.delete(groupId);
|
|
197
|
-
} else {
|
|
198
|
-
newSet.add(groupId);
|
|
199
|
-
}
|
|
200
|
-
return newSet;
|
|
201
|
-
});
|
|
202
|
-
}, []);
|
|
203
|
-
const visibleColumns = useMemo(() => {
|
|
204
|
-
if (!columns || !Array.isArray(columns) || columns.length === 0) {
|
|
205
|
-
return [];
|
|
206
|
-
}
|
|
207
|
-
let result = [...columns];
|
|
208
|
-
if (columnGroups && Array.isArray(columnGroups)) {
|
|
209
|
-
result = result.filter((column) => {
|
|
210
|
-
const group = columnGroups.find((g) => g.columns.includes(column.id));
|
|
211
|
-
if (!group) return true;
|
|
212
|
-
if (!collapsedGroups.has(group.id)) return true;
|
|
213
|
-
return pinnedColumns.has(column.id);
|
|
154
|
+
import { useState, useRef, useEffect, useCallback, memo } from "react";
|
|
155
|
+
import { createPortal } from "react-dom";
|
|
156
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
157
|
+
var cellPaddingCompact = "px-1.5 py-0.5";
|
|
158
|
+
var cellPaddingNormal = "px-2.5 py-1.5";
|
|
159
|
+
var AutocompleteEditor = ({ value, column, compactMode, onConfirm, onCancel }) => {
|
|
160
|
+
const getLabel = useCallback(
|
|
161
|
+
(val) => {
|
|
162
|
+
if (val === null || val === void 0 || val === "") return "";
|
|
163
|
+
if (column.getOptionLabel) return column.getOptionLabel(val);
|
|
164
|
+
const match = column.autocompleteOptions?.find((o) => o.value === val);
|
|
165
|
+
return match ? match.label : String(val);
|
|
166
|
+
},
|
|
167
|
+
[column]
|
|
168
|
+
);
|
|
169
|
+
const [searchText, setSearchText] = useState(() => getLabel(value));
|
|
170
|
+
const [filteredOptions, setFilteredOptions] = useState(
|
|
171
|
+
column.autocompleteOptions ?? []
|
|
172
|
+
);
|
|
173
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
174
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
175
|
+
const [dropdownPos, setDropdownPos] = useState(null);
|
|
176
|
+
const inputRef = useRef(null);
|
|
177
|
+
const dropdownRef = useRef(null);
|
|
178
|
+
const debounceRef = useRef(null);
|
|
179
|
+
const updateDropdownPos = useCallback(() => {
|
|
180
|
+
if (inputRef.current) {
|
|
181
|
+
const rect = inputRef.current.getBoundingClientRect();
|
|
182
|
+
setDropdownPos({
|
|
183
|
+
top: rect.bottom + 2,
|
|
184
|
+
left: rect.left,
|
|
185
|
+
width: rect.width
|
|
214
186
|
});
|
|
215
187
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const pinSide = pinnedColumns.get(column.id);
|
|
229
|
-
if (pinSide === "left") {
|
|
230
|
-
leftPinned.push(column);
|
|
231
|
-
} else if (pinSide === "right") {
|
|
232
|
-
rightPinned.push(column);
|
|
233
|
-
} else {
|
|
234
|
-
unpinned.push(column);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
leftPinned.sort((a, b) => pinnedLeftIds.indexOf(a.id) - pinnedLeftIds.indexOf(b.id));
|
|
238
|
-
rightPinned.sort((a, b) => pinnedRightIds.indexOf(a.id) - pinnedRightIds.indexOf(b.id));
|
|
239
|
-
return [...leftPinned, ...unpinned, ...rightPinned];
|
|
240
|
-
}, [columns, columnGroups, collapsedGroups, pinnedColumns]);
|
|
241
|
-
const getColumnLeftOffset = useCallback(
|
|
242
|
-
(columnId) => {
|
|
243
|
-
if (columnId === ROW_INDEX_COLUMN_ID) {
|
|
244
|
-
return 0;
|
|
245
|
-
}
|
|
246
|
-
const pinnedLeft = Array.from(pinnedColumns.entries()).filter(([id, side]) => side === "left" && id !== ROW_INDEX_COLUMN_ID).map(([id]) => id);
|
|
247
|
-
const index = pinnedLeft.indexOf(columnId);
|
|
248
|
-
const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
249
|
-
const baseOffset = showRowIndex && isRowIndexPinnedNow ? ROW_INDEX_COLUMN_WIDTH : 0;
|
|
250
|
-
if (index === -1) return baseOffset;
|
|
251
|
-
let offset = baseOffset;
|
|
252
|
-
for (let i = 0; i < index; i++) {
|
|
253
|
-
const col = columns.find((c) => c.id === pinnedLeft[i]);
|
|
254
|
-
const configuredWidth = col?.minWidth || col?.width || MIN_PINNED_COLUMN_WIDTH;
|
|
255
|
-
offset += Math.max(configuredWidth, MIN_PINNED_COLUMN_WIDTH);
|
|
256
|
-
}
|
|
257
|
-
return offset;
|
|
188
|
+
}, []);
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
inputRef.current?.focus();
|
|
191
|
+
inputRef.current?.select();
|
|
192
|
+
updateDropdownPos();
|
|
193
|
+
}, [updateDropdownPos]);
|
|
194
|
+
const filterClientSide = useCallback(
|
|
195
|
+
(term) => {
|
|
196
|
+
const opts = column.autocompleteOptions ?? [];
|
|
197
|
+
if (!term.trim()) return opts;
|
|
198
|
+
const lower = term.toLowerCase();
|
|
199
|
+
return opts.filter((o) => o.label.toLowerCase().includes(lower));
|
|
258
200
|
},
|
|
259
|
-
[
|
|
201
|
+
[column.autocompleteOptions]
|
|
260
202
|
);
|
|
261
|
-
const
|
|
262
|
-
(
|
|
263
|
-
|
|
203
|
+
const handleSearchChange = useCallback(
|
|
204
|
+
(e) => {
|
|
205
|
+
const term = e.target.value;
|
|
206
|
+
setSearchText(term);
|
|
207
|
+
setFocusedIndex(-1);
|
|
208
|
+
setIsOpen(true);
|
|
209
|
+
updateDropdownPos();
|
|
210
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
211
|
+
debounceRef.current = setTimeout(async () => {
|
|
212
|
+
if (column.onAutocompleteSearch) {
|
|
213
|
+
try {
|
|
214
|
+
const results = await column.onAutocompleteSearch(term);
|
|
215
|
+
setFilteredOptions(results);
|
|
216
|
+
} catch {
|
|
217
|
+
setFilteredOptions([]);
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
setFilteredOptions(filterClientSide(term));
|
|
221
|
+
}
|
|
222
|
+
}, 300);
|
|
264
223
|
},
|
|
265
|
-
[
|
|
224
|
+
[column, filterClientSide, updateDropdownPos]
|
|
266
225
|
);
|
|
267
|
-
|
|
268
|
-
(
|
|
269
|
-
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
return () => {
|
|
228
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
229
|
+
};
|
|
230
|
+
}, []);
|
|
231
|
+
const selectOption = useCallback(
|
|
232
|
+
(option) => {
|
|
233
|
+
setSearchText(option.label);
|
|
234
|
+
setIsOpen(false);
|
|
235
|
+
onConfirm?.(option.value);
|
|
270
236
|
},
|
|
271
|
-
[
|
|
237
|
+
[onConfirm]
|
|
272
238
|
);
|
|
273
|
-
const
|
|
274
|
-
(
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
239
|
+
const handleKeyDown = useCallback(
|
|
240
|
+
(e) => {
|
|
241
|
+
const visibleOptions2 = filteredOptions.slice(0, 8);
|
|
242
|
+
if (e.key === "ArrowDown") {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
setFocusedIndex((prev) => Math.min(prev + 1, visibleOptions2.length - 1));
|
|
245
|
+
} else if (e.key === "ArrowUp") {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
setFocusedIndex((prev) => Math.max(prev - 1, -1));
|
|
248
|
+
} else if (e.key === "Enter") {
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
if (focusedIndex >= 0 && visibleOptions2[focusedIndex]) {
|
|
251
|
+
selectOption(visibleOptions2[focusedIndex]);
|
|
252
|
+
} else {
|
|
253
|
+
onConfirm?.(value);
|
|
254
|
+
}
|
|
255
|
+
} else if (e.key === "Escape") {
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
setIsOpen(false);
|
|
259
|
+
onCancel?.();
|
|
283
260
|
}
|
|
284
|
-
return offset;
|
|
285
261
|
},
|
|
286
|
-
[
|
|
262
|
+
[filteredOptions, focusedIndex, selectOption, onConfirm, onCancel, value]
|
|
287
263
|
);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
264
|
+
const handleBlur = useCallback(
|
|
265
|
+
(e) => {
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
if (dropdownRef.current && dropdownRef.current.contains(document.activeElement)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
setIsOpen(false);
|
|
271
|
+
onConfirm?.(value);
|
|
272
|
+
}, 150);
|
|
273
|
+
},
|
|
274
|
+
[onConfirm, value]
|
|
275
|
+
);
|
|
276
|
+
const visibleOptions = filteredOptions.slice(0, 8);
|
|
277
|
+
const dropdown = isOpen && visibleOptions.length > 0 && dropdownPos ? createPortal(
|
|
278
|
+
/* @__PURE__ */ jsx(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
ref: dropdownRef,
|
|
282
|
+
style: {
|
|
283
|
+
position: "fixed",
|
|
284
|
+
top: dropdownPos.top,
|
|
285
|
+
left: dropdownPos.left,
|
|
286
|
+
width: Math.max(dropdownPos.width, 180),
|
|
287
|
+
zIndex: 50
|
|
288
|
+
},
|
|
289
|
+
className: "bg-white border border-gray-200 rounded-md shadow-lg max-h-48 overflow-y-auto",
|
|
290
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
291
|
+
children: visibleOptions.map((option, index) => /* @__PURE__ */ jsx(
|
|
292
|
+
"div",
|
|
293
|
+
{
|
|
294
|
+
onMouseDown: (e) => {
|
|
295
|
+
e.preventDefault();
|
|
296
|
+
selectOption(option);
|
|
297
|
+
},
|
|
298
|
+
className: cn(
|
|
299
|
+
"px-3 py-1.5 cursor-pointer",
|
|
300
|
+
compactMode ? "text-xs" : "text-sm",
|
|
301
|
+
index === focusedIndex ? "bg-blue-100" : "hover:bg-blue-50"
|
|
302
|
+
),
|
|
303
|
+
children: option.label
|
|
304
|
+
},
|
|
305
|
+
`${option.value}-${index}`
|
|
306
|
+
))
|
|
307
|
+
}
|
|
308
|
+
),
|
|
309
|
+
document.body
|
|
310
|
+
) : null;
|
|
311
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
312
|
+
/* @__PURE__ */ jsx(
|
|
313
|
+
"input",
|
|
314
|
+
{
|
|
315
|
+
ref: inputRef,
|
|
316
|
+
type: "text",
|
|
317
|
+
value: searchText,
|
|
318
|
+
onChange: handleSearchChange,
|
|
319
|
+
onKeyDown: handleKeyDown,
|
|
320
|
+
onBlur: handleBlur,
|
|
321
|
+
autoComplete: "off",
|
|
322
|
+
autoCorrect: "off",
|
|
323
|
+
autoCapitalize: "off",
|
|
324
|
+
spellCheck: false,
|
|
325
|
+
className: cn(
|
|
326
|
+
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
327
|
+
compactMode ? "text-xs" : "text-sm"
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
dropdown
|
|
332
|
+
] });
|
|
333
|
+
};
|
|
307
334
|
var SpreadsheetCell = ({
|
|
308
335
|
value,
|
|
309
336
|
column,
|
|
@@ -318,6 +345,7 @@ var SpreadsheetCell = ({
|
|
|
318
345
|
isRowSelected = false,
|
|
319
346
|
isRowHovered = false,
|
|
320
347
|
highlightColor,
|
|
348
|
+
isDuplicate = false,
|
|
321
349
|
hasComments = false,
|
|
322
350
|
unresolvedCommentCount = 0,
|
|
323
351
|
isCopied = false,
|
|
@@ -326,7 +354,11 @@ var SpreadsheetCell = ({
|
|
|
326
354
|
pinSide,
|
|
327
355
|
leftOffset = 0,
|
|
328
356
|
rightOffset = 0,
|
|
357
|
+
isOddRow = false,
|
|
358
|
+
resolvedWidth,
|
|
359
|
+
pinnedZIndex,
|
|
329
360
|
onClick,
|
|
361
|
+
onDoubleClick,
|
|
330
362
|
onMouseDown,
|
|
331
363
|
onMouseEnter,
|
|
332
364
|
onChange,
|
|
@@ -337,7 +369,7 @@ var SpreadsheetCell = ({
|
|
|
337
369
|
onViewComments,
|
|
338
370
|
className
|
|
339
371
|
}) => {
|
|
340
|
-
const [localValue, setLocalValue] =
|
|
372
|
+
const [localValue, setLocalValue] = useState(value);
|
|
341
373
|
const inputRef = useRef(null);
|
|
342
374
|
const selectRef = useRef(null);
|
|
343
375
|
useEffect(() => {
|
|
@@ -347,7 +379,7 @@ var SpreadsheetCell = ({
|
|
|
347
379
|
if (isEditing) {
|
|
348
380
|
if (column.type === "select") {
|
|
349
381
|
selectRef.current?.focus();
|
|
350
|
-
} else {
|
|
382
|
+
} else if (column.type !== "autocomplete") {
|
|
351
383
|
inputRef.current?.focus();
|
|
352
384
|
inputRef.current?.select();
|
|
353
385
|
}
|
|
@@ -366,8 +398,10 @@ var SpreadsheetCell = ({
|
|
|
366
398
|
};
|
|
367
399
|
const getBackgroundColor = () => {
|
|
368
400
|
if (highlightColor) return highlightColor;
|
|
401
|
+
if (isDuplicate) return "rgb(254 202 202)";
|
|
369
402
|
if (isRowSelected) return "rgb(219 234 254)";
|
|
370
403
|
if (isRowHovered) return "rgb(243 244 246)";
|
|
404
|
+
if (isOddRow) return "rgb(249 250 251)";
|
|
371
405
|
return "white";
|
|
372
406
|
};
|
|
373
407
|
const handleCheckboxChange = (e) => {
|
|
@@ -404,12 +438,29 @@ var SpreadsheetCell = ({
|
|
|
404
438
|
if (column.type === "number") {
|
|
405
439
|
return typeof value === "number" ? value.toLocaleString() : value;
|
|
406
440
|
}
|
|
441
|
+
if (column.type === "autocomplete") {
|
|
442
|
+
if (column.getOptionLabel) return column.getOptionLabel(value, row);
|
|
443
|
+
const match = column.autocompleteOptions?.find((o) => o.value === value);
|
|
444
|
+
return match ? match.label : String(value);
|
|
445
|
+
}
|
|
407
446
|
return String(value);
|
|
408
447
|
};
|
|
409
448
|
const renderEditInput = () => {
|
|
410
449
|
if (column.type === "checkbox") {
|
|
411
450
|
return renderContent();
|
|
412
451
|
}
|
|
452
|
+
if (column.type === "autocomplete") {
|
|
453
|
+
return /* @__PURE__ */ jsx(
|
|
454
|
+
AutocompleteEditor,
|
|
455
|
+
{
|
|
456
|
+
value: localValue,
|
|
457
|
+
column,
|
|
458
|
+
compactMode,
|
|
459
|
+
onConfirm,
|
|
460
|
+
onCancel
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
}
|
|
413
464
|
if (column.type === "select" && column.options) {
|
|
414
465
|
return /* @__PURE__ */ jsx(
|
|
415
466
|
"select",
|
|
@@ -422,10 +473,11 @@ var SpreadsheetCell = ({
|
|
|
422
473
|
onConfirm?.(newValue);
|
|
423
474
|
},
|
|
424
475
|
onKeyDown: handleKeyDown,
|
|
425
|
-
|
|
476
|
+
onClick: (e) => e.stopPropagation(),
|
|
477
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
426
478
|
className: cn(
|
|
427
479
|
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
428
|
-
compactMode ? "text-
|
|
480
|
+
compactMode ? "text-xs" : "text-sm"
|
|
429
481
|
),
|
|
430
482
|
children: column.options.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
|
|
431
483
|
}
|
|
@@ -450,7 +502,7 @@ var SpreadsheetCell = ({
|
|
|
450
502
|
spellCheck: false,
|
|
451
503
|
className: cn(
|
|
452
504
|
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
453
|
-
compactMode ? "text-
|
|
505
|
+
compactMode ? "text-xs" : "text-sm"
|
|
454
506
|
)
|
|
455
507
|
}
|
|
456
508
|
);
|
|
@@ -474,60 +526,50 @@ var SpreadsheetCell = ({
|
|
|
474
526
|
}
|
|
475
527
|
const selectionBorderStyles = {};
|
|
476
528
|
if (isInSelection && selectionEdge) {
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
if (selectionEdge.right) {
|
|
484
|
-
|
|
485
|
-
selectionBorderStyles.
|
|
486
|
-
}
|
|
487
|
-
if (selectionEdge.bottom) {
|
|
488
|
-
selectionBorderStyles.borderBottomColor = borderColor;
|
|
489
|
-
selectionBorderStyles.borderBottomWidth = borderWidth;
|
|
490
|
-
}
|
|
491
|
-
if (selectionEdge.left) {
|
|
492
|
-
selectionBorderStyles.borderLeftColor = borderColor;
|
|
493
|
-
selectionBorderStyles.borderLeftWidth = borderWidth;
|
|
529
|
+
const color = "rgb(59 130 246)";
|
|
530
|
+
const w = "2px";
|
|
531
|
+
const shadows = [];
|
|
532
|
+
if (selectionEdge.top) shadows.push(`inset 0 ${w} 0 0 ${color}`);
|
|
533
|
+
if (selectionEdge.bottom) shadows.push(`inset 0 -${w} 0 0 ${color}`);
|
|
534
|
+
if (selectionEdge.left) shadows.push(`inset ${w} 0 0 0 ${color}`);
|
|
535
|
+
if (selectionEdge.right) shadows.push(`inset -${w} 0 0 0 ${color}`);
|
|
536
|
+
if (shadows.length > 0) {
|
|
537
|
+
selectionBorderStyles.boxShadow = shadows.join(", ");
|
|
494
538
|
}
|
|
495
539
|
}
|
|
496
540
|
return /* @__PURE__ */ jsx(
|
|
497
541
|
"td",
|
|
498
542
|
{
|
|
499
543
|
onClick,
|
|
544
|
+
onDoubleClick,
|
|
500
545
|
onMouseDown,
|
|
501
546
|
onMouseEnter,
|
|
502
547
|
onKeyDown: handleCellKeyDown,
|
|
503
548
|
"data-cell-id": `${rowId}-${column.id}`,
|
|
549
|
+
"data-column-id": column.id,
|
|
504
550
|
className: cn(
|
|
505
|
-
"border border-gray-200 group cursor-pointer transition-colors select-none",
|
|
506
|
-
compactMode ? "text-
|
|
551
|
+
"border border-gray-200 group cursor-pointer transition-colors select-none relative",
|
|
552
|
+
compactMode ? "text-xs" : "text-sm",
|
|
507
553
|
cellPadding,
|
|
508
554
|
column.align === "right" && "text-right",
|
|
509
555
|
column.align === "center" && "text-center",
|
|
510
556
|
isCopied && "animate-pulse",
|
|
511
557
|
isFocused && !isInSelection && "ring-2 ring-blue-500 ring-inset",
|
|
512
558
|
isInSelection && "bg-blue-50",
|
|
513
|
-
isPinned
|
|
559
|
+
!isPinned && "z-0",
|
|
514
560
|
className
|
|
515
561
|
),
|
|
516
562
|
style: {
|
|
517
563
|
backgroundColor: isInSelection ? "rgb(239 246 255)" : getBackgroundColor(),
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
),
|
|
526
|
-
maxWidth: Math.max(
|
|
527
|
-
column.minWidth || column.width || MIN_PINNED_COLUMN_WIDTH,
|
|
528
|
-
MIN_PINNED_COLUMN_WIDTH
|
|
529
|
-
)
|
|
564
|
+
// Always set explicit width to prevent layout shift on selection/re-render
|
|
565
|
+
...resolvedWidth ? {
|
|
566
|
+
width: resolvedWidth,
|
|
567
|
+
minWidth: resolvedWidth
|
|
568
|
+
} : {
|
|
569
|
+
width: column.width || column.minWidth,
|
|
570
|
+
minWidth: column.minWidth || column.width
|
|
530
571
|
},
|
|
572
|
+
...isPinned && pinnedZIndex !== void 0 && { zIndex: pinnedZIndex },
|
|
531
573
|
...positionStyles,
|
|
532
574
|
...selectionBorderStyles
|
|
533
575
|
},
|
|
@@ -537,12 +579,13 @@ var SpreadsheetCell = ({
|
|
|
537
579
|
{
|
|
538
580
|
className: cn(
|
|
539
581
|
"flex-1 truncate",
|
|
540
|
-
isEditable && "cursor-text bg-blue-50
|
|
582
|
+
isEditable && "cursor-text bg-blue-50 rounded px-1"
|
|
541
583
|
),
|
|
542
584
|
title: String(value ?? ""),
|
|
543
585
|
children: renderContent()
|
|
544
586
|
}
|
|
545
587
|
),
|
|
588
|
+
isEditable && (column.type === "select" || column.type === "autocomplete") && !isEditing && /* @__PURE__ */ jsx(HiChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400" }),
|
|
546
589
|
!isInSelection && !isFocused && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
547
590
|
column.highlightable !== false && onHighlight && /* @__PURE__ */ jsx(
|
|
548
591
|
"button",
|
|
@@ -607,6 +650,7 @@ var MemoizedSpreadsheetCell = memo(SpreadsheetCell, (prevProps, nextProps) => {
|
|
|
607
650
|
if (prevProps.isRowSelected !== nextProps.isRowSelected) return false;
|
|
608
651
|
if (prevProps.isRowHovered !== nextProps.isRowHovered) return false;
|
|
609
652
|
if (prevProps.highlightColor !== nextProps.highlightColor) return false;
|
|
653
|
+
if (prevProps.isDuplicate !== nextProps.isDuplicate) return false;
|
|
610
654
|
if (prevProps.hasComments !== nextProps.hasComments) return false;
|
|
611
655
|
if (prevProps.unresolvedCommentCount !== nextProps.unresolvedCommentCount) return false;
|
|
612
656
|
if (prevProps.isCopied !== nextProps.isCopied) return false;
|
|
@@ -614,6 +658,7 @@ var MemoizedSpreadsheetCell = memo(SpreadsheetCell, (prevProps, nextProps) => {
|
|
|
614
658
|
if (prevProps.leftOffset !== nextProps.leftOffset) return false;
|
|
615
659
|
if (prevProps.rightOffset !== nextProps.rightOffset) return false;
|
|
616
660
|
if (prevProps.compactMode !== nextProps.compactMode) return false;
|
|
661
|
+
if (prevProps.isOddRow !== nextProps.isOddRow) return false;
|
|
617
662
|
const prevEdge = prevProps.selectionEdge;
|
|
618
663
|
const nextEdge = nextProps.selectionEdge;
|
|
619
664
|
if (prevEdge !== nextEdge) {
|
|
@@ -628,8 +673,9 @@ var MemoizedSpreadsheetCell = memo(SpreadsheetCell, (prevProps, nextProps) => {
|
|
|
628
673
|
MemoizedSpreadsheetCell.displayName = "MemoizedSpreadsheetCell";
|
|
629
674
|
|
|
630
675
|
// src/components/SpreadsheetFilterDropdown.tsx
|
|
631
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as
|
|
632
|
-
import {
|
|
676
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
677
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
678
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
633
679
|
var TEXT_OPERATORS = [
|
|
634
680
|
{ value: "contains", label: "Contains" },
|
|
635
681
|
{ value: "notContains", label: "Does not contain" },
|
|
@@ -667,59 +713,121 @@ var DATE_OPERATORS = [
|
|
|
667
713
|
{ value: "isEmpty", label: "Is empty" },
|
|
668
714
|
{ value: "isNotEmpty", label: "Is not empty" }
|
|
669
715
|
];
|
|
716
|
+
var DROPDOWN_WIDTH = 256;
|
|
717
|
+
var DROPDOWN_HEIGHT = 340;
|
|
718
|
+
var ARROW_SIZE = 6;
|
|
670
719
|
var SpreadsheetFilterDropdown = ({
|
|
671
720
|
column,
|
|
672
721
|
filter,
|
|
673
722
|
onFilterChange,
|
|
674
723
|
onClose,
|
|
675
|
-
className
|
|
724
|
+
className,
|
|
725
|
+
triggerRef,
|
|
726
|
+
hasDuplicateCheck = false
|
|
676
727
|
}) => {
|
|
677
|
-
const [
|
|
728
|
+
const [showDuplicatesOnly, setShowDuplicatesOnly] = useState2(
|
|
729
|
+
filter?.showDuplicatesOnly ?? false
|
|
730
|
+
);
|
|
731
|
+
const [textOperator, setTextOperator] = useState2(
|
|
678
732
|
filter?.textCondition?.operator || "contains"
|
|
679
733
|
);
|
|
680
|
-
const [textValue, setTextValue] =
|
|
681
|
-
const [numberOperator, setNumberOperator] =
|
|
734
|
+
const [textValue, setTextValue] = useState2(filter?.textCondition?.value || "");
|
|
735
|
+
const [numberOperator, setNumberOperator] = useState2(
|
|
682
736
|
filter?.numberCondition?.operator || "equals"
|
|
683
737
|
);
|
|
684
|
-
const [numberValue, setNumberValue] =
|
|
738
|
+
const [numberValue, setNumberValue] = useState2(
|
|
685
739
|
filter?.numberCondition?.value?.toString() || ""
|
|
686
740
|
);
|
|
687
|
-
const [numberValueTo, setNumberValueTo] =
|
|
741
|
+
const [numberValueTo, setNumberValueTo] = useState2(
|
|
688
742
|
filter?.numberCondition?.valueTo?.toString() || ""
|
|
689
743
|
);
|
|
690
|
-
const [dateOperator, setDateOperator] =
|
|
744
|
+
const [dateOperator, setDateOperator] = useState2(
|
|
691
745
|
filter?.dateCondition?.operator || "equals"
|
|
692
746
|
);
|
|
693
|
-
const [dateValue, setDateValue] =
|
|
694
|
-
const [dateValueTo, setDateValueTo] =
|
|
747
|
+
const [dateValue, setDateValue] = useState2(filter?.dateCondition?.value || "");
|
|
748
|
+
const [dateValueTo, setDateValueTo] = useState2(filter?.dateCondition?.valueTo || "");
|
|
695
749
|
const dropdownRef = useRef2(null);
|
|
750
|
+
const [position, setPosition] = useState2({ top: 0, left: 0, arrowLeft: DROPDOWN_WIDTH / 2, flippedUp: false });
|
|
696
751
|
const isNumeric = column.type === "number";
|
|
697
752
|
const isDate = column.type === "date";
|
|
753
|
+
const updatePosition = useCallback2(() => {
|
|
754
|
+
if (!triggerRef?.current) return;
|
|
755
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
756
|
+
let top = rect.bottom + 4;
|
|
757
|
+
let flippedUp = false;
|
|
758
|
+
if (top + DROPDOWN_HEIGHT > window.innerHeight) {
|
|
759
|
+
top = rect.top - DROPDOWN_HEIGHT - 4;
|
|
760
|
+
flippedUp = true;
|
|
761
|
+
}
|
|
762
|
+
let left = rect.left;
|
|
763
|
+
if (left + DROPDOWN_WIDTH > window.innerWidth) {
|
|
764
|
+
left = window.innerWidth - DROPDOWN_WIDTH - 8;
|
|
765
|
+
}
|
|
766
|
+
if (left < 8) {
|
|
767
|
+
left = 8;
|
|
768
|
+
}
|
|
769
|
+
const anchorCenter = rect.left + rect.width / 2;
|
|
770
|
+
const arrowLeft = Math.min(
|
|
771
|
+
Math.max(anchorCenter - left, ARROW_SIZE + 4),
|
|
772
|
+
DROPDOWN_WIDTH - ARROW_SIZE - 4
|
|
773
|
+
);
|
|
774
|
+
setPosition({ top, left, arrowLeft, flippedUp });
|
|
775
|
+
}, [triggerRef]);
|
|
776
|
+
useEffect2(() => {
|
|
777
|
+
if (!triggerRef?.current) return;
|
|
778
|
+
updatePosition();
|
|
779
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
780
|
+
window.addEventListener("resize", updatePosition);
|
|
781
|
+
return () => {
|
|
782
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
783
|
+
window.removeEventListener("resize", updatePosition);
|
|
784
|
+
};
|
|
785
|
+
}, [updatePosition, triggerRef]);
|
|
698
786
|
useEffect2(() => {
|
|
699
787
|
const handleClickOutside = (event) => {
|
|
700
|
-
|
|
788
|
+
const target = event.target;
|
|
789
|
+
if (dropdownRef.current && !dropdownRef.current.contains(target)) {
|
|
790
|
+
if (triggerRef?.current?.contains(target)) return;
|
|
701
791
|
onClose();
|
|
702
792
|
}
|
|
703
793
|
};
|
|
704
|
-
|
|
705
|
-
|
|
794
|
+
const rafId = requestAnimationFrame(() => {
|
|
795
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
796
|
+
});
|
|
797
|
+
return () => {
|
|
798
|
+
cancelAnimationFrame(rafId);
|
|
799
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
800
|
+
};
|
|
801
|
+
}, [onClose, triggerRef]);
|
|
802
|
+
useEffect2(() => {
|
|
803
|
+
const handleKeyDown = (event) => {
|
|
804
|
+
if (event.key === "Escape") {
|
|
805
|
+
onClose();
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
809
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
706
810
|
}, [onClose]);
|
|
707
811
|
const handleApplyFilter = () => {
|
|
708
812
|
let newFilter;
|
|
709
813
|
if (isNumeric) {
|
|
710
814
|
const needsValue = !["isEmpty", "isNotEmpty"].includes(numberOperator);
|
|
711
|
-
if (needsValue && !numberValue) {
|
|
815
|
+
if (needsValue && !numberValue && !showDuplicatesOnly) {
|
|
712
816
|
onFilterChange(void 0);
|
|
713
817
|
onClose();
|
|
714
818
|
return;
|
|
715
819
|
}
|
|
716
820
|
newFilter = {
|
|
717
|
-
numberCondition: {
|
|
821
|
+
numberCondition: needsValue || !numberValue ? {
|
|
718
822
|
operator: numberOperator,
|
|
719
823
|
value: numberValue ? parseFloat(numberValue) : void 0,
|
|
720
824
|
valueTo: numberValueTo ? parseFloat(numberValueTo) : void 0
|
|
721
|
-
}
|
|
825
|
+
} : void 0,
|
|
826
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
722
827
|
};
|
|
828
|
+
if (!newFilter.numberCondition && !newFilter.showDuplicatesOnly) {
|
|
829
|
+
newFilter = void 0;
|
|
830
|
+
}
|
|
723
831
|
} else if (isDate) {
|
|
724
832
|
const needsValue = ![
|
|
725
833
|
"isEmpty",
|
|
@@ -732,7 +840,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
732
840
|
"lastMonth",
|
|
733
841
|
"thisYear"
|
|
734
842
|
].includes(dateOperator);
|
|
735
|
-
if (needsValue && !dateValue) {
|
|
843
|
+
if (needsValue && !dateValue && !showDuplicatesOnly) {
|
|
736
844
|
onFilterChange(void 0);
|
|
737
845
|
onClose();
|
|
738
846
|
return;
|
|
@@ -742,26 +850,32 @@ var SpreadsheetFilterDropdown = ({
|
|
|
742
850
|
operator: dateOperator,
|
|
743
851
|
value: dateValue || void 0,
|
|
744
852
|
valueTo: dateValueTo || void 0
|
|
745
|
-
}
|
|
853
|
+
},
|
|
854
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
746
855
|
};
|
|
747
856
|
} else {
|
|
748
857
|
const needsValue = !["isEmpty", "isNotEmpty"].includes(textOperator);
|
|
749
|
-
if (needsValue && !textValue) {
|
|
858
|
+
if (needsValue && !textValue && !showDuplicatesOnly) {
|
|
750
859
|
onFilterChange(void 0);
|
|
751
860
|
onClose();
|
|
752
861
|
return;
|
|
753
862
|
}
|
|
754
863
|
newFilter = {
|
|
755
|
-
textCondition: {
|
|
864
|
+
textCondition: needsValue || !textValue ? {
|
|
756
865
|
operator: textOperator,
|
|
757
866
|
value: textValue || void 0
|
|
758
|
-
}
|
|
867
|
+
} : void 0,
|
|
868
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
759
869
|
};
|
|
870
|
+
if (!newFilter.textCondition && !newFilter.showDuplicatesOnly) {
|
|
871
|
+
newFilter = void 0;
|
|
872
|
+
}
|
|
760
873
|
}
|
|
761
874
|
onFilterChange(newFilter);
|
|
762
875
|
onClose();
|
|
763
876
|
};
|
|
764
877
|
const handleClearFilter = () => {
|
|
878
|
+
setShowDuplicatesOnly(false);
|
|
765
879
|
setTextValue("");
|
|
766
880
|
setNumberValue("");
|
|
767
881
|
setNumberValueTo("");
|
|
@@ -770,6 +884,12 @@ var SpreadsheetFilterDropdown = ({
|
|
|
770
884
|
onFilterChange(void 0);
|
|
771
885
|
onClose();
|
|
772
886
|
};
|
|
887
|
+
const handleFilterKeyDown = (e) => {
|
|
888
|
+
if (e.key === "Enter") {
|
|
889
|
+
e.preventDefault();
|
|
890
|
+
handleApplyFilter();
|
|
891
|
+
}
|
|
892
|
+
};
|
|
773
893
|
const textNeedsValue = !["isEmpty", "isNotEmpty"].includes(textOperator);
|
|
774
894
|
const numberNeedsValue = !["isEmpty", "isNotEmpty"].includes(numberOperator);
|
|
775
895
|
const dateNeedsValue = ![
|
|
@@ -783,21 +903,58 @@ var SpreadsheetFilterDropdown = ({
|
|
|
783
903
|
"lastMonth",
|
|
784
904
|
"thisYear"
|
|
785
905
|
].includes(dateOperator);
|
|
786
|
-
|
|
906
|
+
const dropdownContent = /* @__PURE__ */ jsxs2(
|
|
787
907
|
"div",
|
|
788
908
|
{
|
|
789
909
|
ref: dropdownRef,
|
|
790
910
|
className: cn(
|
|
791
|
-
"
|
|
911
|
+
"bg-white border border-gray-200 shadow-lg rounded-lg w-64 overflow-hidden flex flex-col",
|
|
912
|
+
!triggerRef && "absolute top-full left-0 mt-1 z-[100]",
|
|
792
913
|
className
|
|
793
914
|
),
|
|
915
|
+
style: triggerRef ? { position: "fixed", top: position.top, left: position.left, zIndex: 9999 } : void 0,
|
|
794
916
|
onClick: (e) => e.stopPropagation(),
|
|
795
917
|
children: [
|
|
918
|
+
triggerRef && /* @__PURE__ */ jsx2(
|
|
919
|
+
"div",
|
|
920
|
+
{
|
|
921
|
+
className: "absolute pointer-events-none",
|
|
922
|
+
style: position.flippedUp ? {
|
|
923
|
+
bottom: -ARROW_SIZE,
|
|
924
|
+
left: position.arrowLeft - ARROW_SIZE,
|
|
925
|
+
width: 0,
|
|
926
|
+
height: 0,
|
|
927
|
+
borderLeft: `${ARROW_SIZE}px solid transparent`,
|
|
928
|
+
borderRight: `${ARROW_SIZE}px solid transparent`,
|
|
929
|
+
borderTop: `${ARROW_SIZE}px solid #e5e7eb`
|
|
930
|
+
} : {
|
|
931
|
+
top: -ARROW_SIZE,
|
|
932
|
+
left: position.arrowLeft - ARROW_SIZE,
|
|
933
|
+
width: 0,
|
|
934
|
+
height: 0,
|
|
935
|
+
borderLeft: `${ARROW_SIZE}px solid transparent`,
|
|
936
|
+
borderRight: `${ARROW_SIZE}px solid transparent`,
|
|
937
|
+
borderBottom: `${ARROW_SIZE}px solid #e5e7eb`
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
),
|
|
796
941
|
/* @__PURE__ */ jsx2("div", { className: "px-3 py-2 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ jsxs2("span", { className: "text-xs font-medium text-gray-700", children: [
|
|
797
942
|
"Filter: ",
|
|
798
943
|
column.label
|
|
799
944
|
] }) }),
|
|
800
|
-
/* @__PURE__ */ jsx2("div", { className: "
|
|
945
|
+
hasDuplicateCheck && /* @__PURE__ */ jsx2("div", { className: "px-3 pt-2 pb-2 border-b border-gray-200", children: /* @__PURE__ */ jsxs2("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
946
|
+
/* @__PURE__ */ jsx2(
|
|
947
|
+
"input",
|
|
948
|
+
{
|
|
949
|
+
type: "checkbox",
|
|
950
|
+
checked: showDuplicatesOnly,
|
|
951
|
+
onChange: (e) => setShowDuplicatesOnly(e.target.checked),
|
|
952
|
+
className: "h-3 w-3 rounded border-gray-300 text-red-600 focus:ring-red-500"
|
|
953
|
+
}
|
|
954
|
+
),
|
|
955
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs text-gray-700", children: "Show only duplicates" })
|
|
956
|
+
] }) }),
|
|
957
|
+
/* @__PURE__ */ jsx2("div", { className: "p-3 space-y-3", children: isNumeric ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
801
958
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
802
959
|
/* @__PURE__ */ jsx2("label", { className: "text-xs text-gray-500 mb-1 block", children: "Condition" }),
|
|
803
960
|
/* @__PURE__ */ jsx2(
|
|
@@ -819,6 +976,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
819
976
|
placeholder: "Enter value",
|
|
820
977
|
value: numberValue,
|
|
821
978
|
onChange: (e) => setNumberValue(e.target.value),
|
|
979
|
+
onKeyDown: handleFilterKeyDown,
|
|
822
980
|
className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
823
981
|
}
|
|
824
982
|
)
|
|
@@ -832,11 +990,12 @@ var SpreadsheetFilterDropdown = ({
|
|
|
832
990
|
placeholder: "Enter end value",
|
|
833
991
|
value: numberValueTo,
|
|
834
992
|
onChange: (e) => setNumberValueTo(e.target.value),
|
|
993
|
+
onKeyDown: handleFilterKeyDown,
|
|
835
994
|
className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
836
995
|
}
|
|
837
996
|
)
|
|
838
997
|
] })
|
|
839
|
-
] }) : isDate ? /* @__PURE__ */ jsxs2(
|
|
998
|
+
] }) : isDate ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
840
999
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
841
1000
|
/* @__PURE__ */ jsx2("label", { className: "text-xs text-gray-500 mb-1 block", children: "Condition" }),
|
|
842
1001
|
/* @__PURE__ */ jsx2(
|
|
@@ -857,6 +1016,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
857
1016
|
type: "date",
|
|
858
1017
|
value: dateValue,
|
|
859
1018
|
onChange: (e) => setDateValue(e.target.value),
|
|
1019
|
+
onKeyDown: handleFilterKeyDown,
|
|
860
1020
|
className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
861
1021
|
}
|
|
862
1022
|
)
|
|
@@ -869,11 +1029,12 @@ var SpreadsheetFilterDropdown = ({
|
|
|
869
1029
|
type: "date",
|
|
870
1030
|
value: dateValueTo,
|
|
871
1031
|
onChange: (e) => setDateValueTo(e.target.value),
|
|
1032
|
+
onKeyDown: handleFilterKeyDown,
|
|
872
1033
|
className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
873
1034
|
}
|
|
874
1035
|
)
|
|
875
1036
|
] })
|
|
876
|
-
] }) : /* @__PURE__ */ jsxs2(
|
|
1037
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
877
1038
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
878
1039
|
/* @__PURE__ */ jsx2("label", { className: "text-xs text-gray-500 mb-1 block", children: "Condition" }),
|
|
879
1040
|
/* @__PURE__ */ jsx2(
|
|
@@ -895,6 +1056,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
895
1056
|
placeholder: "Enter text",
|
|
896
1057
|
value: textValue,
|
|
897
1058
|
onChange: (e) => setTextValue(e.target.value),
|
|
1059
|
+
onKeyDown: handleFilterKeyDown,
|
|
898
1060
|
className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
899
1061
|
}
|
|
900
1062
|
)
|
|
@@ -929,6 +1091,10 @@ var SpreadsheetFilterDropdown = ({
|
|
|
929
1091
|
]
|
|
930
1092
|
}
|
|
931
1093
|
);
|
|
1094
|
+
if (triggerRef) {
|
|
1095
|
+
return createPortal2(dropdownContent, document.body);
|
|
1096
|
+
}
|
|
1097
|
+
return dropdownContent;
|
|
932
1098
|
};
|
|
933
1099
|
SpreadsheetFilterDropdown.displayName = "SpreadsheetFilterDropdown";
|
|
934
1100
|
|
|
@@ -1458,6 +1624,13 @@ var SpreadsheetToolbar = ({
|
|
|
1458
1624
|
};
|
|
1459
1625
|
SpreadsheetToolbar.displayName = "SpreadsheetToolbar";
|
|
1460
1626
|
|
|
1627
|
+
// src/components/SpreadsheetHeader.tsx
|
|
1628
|
+
import React4, { memo as memo2, useRef as useRef4 } from "react";
|
|
1629
|
+
|
|
1630
|
+
// src/components/ColumnHeaderActions.tsx
|
|
1631
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
1632
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
1633
|
+
|
|
1461
1634
|
// ../../../node_modules/.pnpm/react-icons@4.12.0_react@18.3.1/node_modules/react-icons/md/index.esm.js
|
|
1462
1635
|
function MdPushPin(props) {
|
|
1463
1636
|
return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 24 24" }, "child": [{ "tag": "path", "attr": { "fill": "none", "d": "M0 0h24v24H0z" } }, { "tag": "path", "attr": { "fillRule": "evenodd", "d": "M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z" } }] })(props);
|
|
@@ -1475,6 +1648,9 @@ function ColumnHeaderActions({
|
|
|
1475
1648
|
hasActiveFilter = false,
|
|
1476
1649
|
hasActiveHighlight = false,
|
|
1477
1650
|
isPinned = false,
|
|
1651
|
+
enableSorting = false,
|
|
1652
|
+
sortDirection = null,
|
|
1653
|
+
onSortClick,
|
|
1478
1654
|
onFilterClick,
|
|
1479
1655
|
onHighlightClick,
|
|
1480
1656
|
onPinClick,
|
|
@@ -1482,20 +1658,194 @@ function ColumnHeaderActions({
|
|
|
1482
1658
|
highlightTitle = "Highlight column",
|
|
1483
1659
|
pinnedTitle = "Unpin column",
|
|
1484
1660
|
unpinnedTitle = "Pin column",
|
|
1485
|
-
className
|
|
1661
|
+
className,
|
|
1662
|
+
resolvedWidth,
|
|
1663
|
+
enableDuplicateCheck = false,
|
|
1664
|
+
hasDuplicateCheck = false,
|
|
1665
|
+
onDuplicateCheckClick
|
|
1486
1666
|
}) {
|
|
1667
|
+
const [overflowOpen, setOverflowOpen] = useState3(false);
|
|
1668
|
+
const overflowRef = useRef3(null);
|
|
1669
|
+
const triggerRef = useRef3(null);
|
|
1670
|
+
const [dropdownPos, setDropdownPos] = useState3(null);
|
|
1671
|
+
const isCompact = resolvedWidth !== void 0 && resolvedWidth < 80;
|
|
1672
|
+
useEffect3(() => {
|
|
1673
|
+
if (!overflowOpen) return;
|
|
1674
|
+
const handler = (e) => {
|
|
1675
|
+
if (overflowRef.current && !overflowRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
1676
|
+
setOverflowOpen(false);
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
document.addEventListener("mousedown", handler);
|
|
1680
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1681
|
+
}, [overflowOpen]);
|
|
1682
|
+
const openOverflow = (e) => {
|
|
1683
|
+
e.stopPropagation();
|
|
1684
|
+
if (!overflowOpen && triggerRef.current) {
|
|
1685
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
1686
|
+
setDropdownPos({
|
|
1687
|
+
top: rect.bottom + window.scrollY + 4,
|
|
1688
|
+
right: window.innerWidth - rect.right - window.scrollX
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
setOverflowOpen((prev) => !prev);
|
|
1692
|
+
};
|
|
1693
|
+
if (isCompact) {
|
|
1694
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn("flex items-center", className), children: [
|
|
1695
|
+
/* @__PURE__ */ jsx5(
|
|
1696
|
+
"button",
|
|
1697
|
+
{
|
|
1698
|
+
ref: triggerRef,
|
|
1699
|
+
type: "button",
|
|
1700
|
+
onClick: openOverflow,
|
|
1701
|
+
className: "p-0.5 hover:bg-gray-200 rounded text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1702
|
+
title: "Column actions",
|
|
1703
|
+
children: /* @__PURE__ */ jsx5(HiDotsVertical, { className: "h-3 w-3" })
|
|
1704
|
+
}
|
|
1705
|
+
),
|
|
1706
|
+
overflowOpen && dropdownPos && createPortal3(
|
|
1707
|
+
/* @__PURE__ */ jsxs5(
|
|
1708
|
+
"div",
|
|
1709
|
+
{
|
|
1710
|
+
ref: overflowRef,
|
|
1711
|
+
style: {
|
|
1712
|
+
position: "absolute",
|
|
1713
|
+
top: dropdownPos.top,
|
|
1714
|
+
right: dropdownPos.right
|
|
1715
|
+
},
|
|
1716
|
+
className: "bg-white border border-gray-200 shadow-lg rounded-md py-1 w-44 z-[9999]",
|
|
1717
|
+
children: [
|
|
1718
|
+
enableSorting && onSortClick && /* @__PURE__ */ jsxs5(
|
|
1719
|
+
"button",
|
|
1720
|
+
{
|
|
1721
|
+
type: "button",
|
|
1722
|
+
onClick: (e) => {
|
|
1723
|
+
e.stopPropagation();
|
|
1724
|
+
onSortClick();
|
|
1725
|
+
setOverflowOpen(false);
|
|
1726
|
+
},
|
|
1727
|
+
className: cn(
|
|
1728
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1729
|
+
sortDirection ? "text-blue-600" : "text-gray-700"
|
|
1730
|
+
),
|
|
1731
|
+
children: [
|
|
1732
|
+
sortDirection === "desc" ? /* @__PURE__ */ jsx5(HiSortDescending, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ jsx5(HiSortAscending, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1733
|
+
sortDirection === "asc" ? "Sorted ascending" : sortDirection === "desc" ? "Sorted descending" : "Sort"
|
|
1734
|
+
]
|
|
1735
|
+
}
|
|
1736
|
+
),
|
|
1737
|
+
enableFiltering && onFilterClick && /* @__PURE__ */ jsxs5(
|
|
1738
|
+
"button",
|
|
1739
|
+
{
|
|
1740
|
+
type: "button",
|
|
1741
|
+
onClick: (e) => {
|
|
1742
|
+
e.stopPropagation();
|
|
1743
|
+
onFilterClick();
|
|
1744
|
+
setOverflowOpen(false);
|
|
1745
|
+
},
|
|
1746
|
+
className: cn(
|
|
1747
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1748
|
+
hasActiveFilter ? "text-blue-600" : "text-gray-700"
|
|
1749
|
+
),
|
|
1750
|
+
children: [
|
|
1751
|
+
/* @__PURE__ */ jsx5(HiFilter, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1752
|
+
"Filter",
|
|
1753
|
+
hasActiveFilter && /* @__PURE__ */ jsx5("span", { className: "ml-auto h-1.5 w-1.5 rounded-full bg-blue-500 shrink-0" })
|
|
1754
|
+
]
|
|
1755
|
+
}
|
|
1756
|
+
),
|
|
1757
|
+
enableHighlighting && onHighlightClick && /* @__PURE__ */ jsxs5(
|
|
1758
|
+
"button",
|
|
1759
|
+
{
|
|
1760
|
+
type: "button",
|
|
1761
|
+
onClick: (e) => {
|
|
1762
|
+
e.stopPropagation();
|
|
1763
|
+
onHighlightClick();
|
|
1764
|
+
setOverflowOpen(false);
|
|
1765
|
+
},
|
|
1766
|
+
className: cn(
|
|
1767
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1768
|
+
hasActiveHighlight ? "text-amber-500" : "text-gray-700"
|
|
1769
|
+
),
|
|
1770
|
+
children: [
|
|
1771
|
+
/* @__PURE__ */ jsx5(AiFillHighlight, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1772
|
+
"Highlight"
|
|
1773
|
+
]
|
|
1774
|
+
}
|
|
1775
|
+
),
|
|
1776
|
+
enablePinning && onPinClick && /* @__PURE__ */ jsxs5(
|
|
1777
|
+
"button",
|
|
1778
|
+
{
|
|
1779
|
+
type: "button",
|
|
1780
|
+
onClick: (e) => {
|
|
1781
|
+
e.stopPropagation();
|
|
1782
|
+
onPinClick();
|
|
1783
|
+
setOverflowOpen(false);
|
|
1784
|
+
},
|
|
1785
|
+
className: cn(
|
|
1786
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1787
|
+
isPinned ? "text-blue-600" : "text-gray-700"
|
|
1788
|
+
),
|
|
1789
|
+
children: [
|
|
1790
|
+
isPinned ? /* @__PURE__ */ jsx5(MdPushPin, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ jsx5(MdOutlinePushPin, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1791
|
+
isPinned ? "Unpin" : "Pin"
|
|
1792
|
+
]
|
|
1793
|
+
}
|
|
1794
|
+
),
|
|
1795
|
+
enableDuplicateCheck && onDuplicateCheckClick && /* @__PURE__ */ jsxs5(
|
|
1796
|
+
"button",
|
|
1797
|
+
{
|
|
1798
|
+
type: "button",
|
|
1799
|
+
onClick: (e) => {
|
|
1800
|
+
e.stopPropagation();
|
|
1801
|
+
onDuplicateCheckClick();
|
|
1802
|
+
setOverflowOpen(false);
|
|
1803
|
+
},
|
|
1804
|
+
className: cn(
|
|
1805
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1806
|
+
hasDuplicateCheck ? "text-purple-600" : "text-gray-700"
|
|
1807
|
+
),
|
|
1808
|
+
children: [
|
|
1809
|
+
/* @__PURE__ */ jsx5(HiExclamation, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1810
|
+
"Check Duplicates"
|
|
1811
|
+
]
|
|
1812
|
+
}
|
|
1813
|
+
)
|
|
1814
|
+
]
|
|
1815
|
+
}
|
|
1816
|
+
),
|
|
1817
|
+
document.body
|
|
1818
|
+
)
|
|
1819
|
+
] });
|
|
1820
|
+
}
|
|
1487
1821
|
return /* @__PURE__ */ jsxs5("div", { className: cn("flex items-center gap-0.5", className), children: [
|
|
1488
|
-
|
|
1822
|
+
enableSorting && onSortClick && /* @__PURE__ */ jsx5(
|
|
1489
1823
|
"button",
|
|
1490
1824
|
{
|
|
1491
1825
|
type: "button",
|
|
1492
1826
|
onClick: (e) => {
|
|
1493
1827
|
e.stopPropagation();
|
|
1494
|
-
|
|
1828
|
+
onSortClick();
|
|
1495
1829
|
},
|
|
1496
1830
|
className: cn(
|
|
1497
1831
|
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1498
|
-
|
|
1832
|
+
sortDirection ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1833
|
+
),
|
|
1834
|
+
title: sortDirection === "asc" ? "Sorted ascending" : sortDirection === "desc" ? "Sorted descending" : "Sort column",
|
|
1835
|
+
children: sortDirection === "desc" ? /* @__PURE__ */ jsx5(HiSortDescending, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx5(HiSortAscending, { className: "h-3 w-3" })
|
|
1836
|
+
}
|
|
1837
|
+
),
|
|
1838
|
+
enableFiltering && onFilterClick && /* @__PURE__ */ jsx5(
|
|
1839
|
+
"button",
|
|
1840
|
+
{
|
|
1841
|
+
type: "button",
|
|
1842
|
+
onClick: (e) => {
|
|
1843
|
+
e.stopPropagation();
|
|
1844
|
+
onFilterClick();
|
|
1845
|
+
},
|
|
1846
|
+
className: cn(
|
|
1847
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1848
|
+
hasActiveFilter ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1499
1849
|
),
|
|
1500
1850
|
title: filterTitle,
|
|
1501
1851
|
children: /* @__PURE__ */ jsx5(HiFilter, { className: "h-3 w-3" })
|
|
@@ -1532,6 +1882,22 @@ function ColumnHeaderActions({
|
|
|
1532
1882
|
title: isPinned ? pinnedTitle : unpinnedTitle,
|
|
1533
1883
|
children: isPinned ? /* @__PURE__ */ jsx5(MdPushPin, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx5(MdOutlinePushPin, { className: "h-3 w-3" })
|
|
1534
1884
|
}
|
|
1885
|
+
),
|
|
1886
|
+
enableDuplicateCheck && onDuplicateCheckClick && /* @__PURE__ */ jsx5(
|
|
1887
|
+
"button",
|
|
1888
|
+
{
|
|
1889
|
+
type: "button",
|
|
1890
|
+
onClick: (e) => {
|
|
1891
|
+
e.stopPropagation();
|
|
1892
|
+
onDuplicateCheckClick();
|
|
1893
|
+
},
|
|
1894
|
+
className: cn(
|
|
1895
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1896
|
+
hasDuplicateCheck ? "text-purple-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1897
|
+
),
|
|
1898
|
+
title: "Check duplicates",
|
|
1899
|
+
children: /* @__PURE__ */ jsx5(HiExclamation, { className: "h-3 w-3" })
|
|
1900
|
+
}
|
|
1535
1901
|
)
|
|
1536
1902
|
] });
|
|
1537
1903
|
}
|
|
@@ -1539,8 +1905,8 @@ ColumnHeaderActions.displayName = "ColumnHeaderActions";
|
|
|
1539
1905
|
|
|
1540
1906
|
// src/components/SpreadsheetHeader.tsx
|
|
1541
1907
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1542
|
-
var cellPaddingCompact2 = "px-1 py-
|
|
1543
|
-
var cellPaddingNormal2 = "px-2 py-
|
|
1908
|
+
var cellPaddingCompact2 = "px-1.5 py-1";
|
|
1909
|
+
var cellPaddingNormal2 = "px-2 py-2";
|
|
1544
1910
|
var SpreadsheetHeader = ({
|
|
1545
1911
|
column,
|
|
1546
1912
|
sortConfig,
|
|
@@ -1552,12 +1918,21 @@ var SpreadsheetHeader = ({
|
|
|
1552
1918
|
highlightColor,
|
|
1553
1919
|
compactMode = false,
|
|
1554
1920
|
onClick,
|
|
1921
|
+
pinnedZIndex,
|
|
1922
|
+
onSortClick,
|
|
1555
1923
|
onFilterClick,
|
|
1556
1924
|
onPinClick,
|
|
1557
1925
|
onHighlightClick,
|
|
1926
|
+
hasDuplicateCheck = false,
|
|
1927
|
+
onDuplicateCheckClick,
|
|
1928
|
+
duplicateCount,
|
|
1929
|
+
resizeHandleProps,
|
|
1930
|
+
resolvedWidth,
|
|
1931
|
+
topOffset = 0,
|
|
1558
1932
|
className,
|
|
1559
1933
|
children
|
|
1560
1934
|
}) => {
|
|
1935
|
+
const thRef = useRef4(null);
|
|
1561
1936
|
const isSorted = sortConfig?.columnId === column.id;
|
|
1562
1937
|
const sortDirection = isSorted ? sortConfig.direction : null;
|
|
1563
1938
|
const cellPadding = compactMode ? cellPaddingCompact2 : cellPaddingNormal2;
|
|
@@ -1573,46 +1948,55 @@ var SpreadsheetHeader = ({
|
|
|
1573
1948
|
return /* @__PURE__ */ jsxs6(
|
|
1574
1949
|
"th",
|
|
1575
1950
|
{
|
|
1576
|
-
|
|
1951
|
+
ref: thRef,
|
|
1952
|
+
"data-column-id": column.id,
|
|
1953
|
+
onClick,
|
|
1577
1954
|
className: cn(
|
|
1578
|
-
"border border-gray-200 font-semibold text-gray-700
|
|
1579
|
-
|
|
1955
|
+
"border border-gray-200 font-semibold text-gray-700 group relative cursor-pointer",
|
|
1956
|
+
isPinned && "sticky",
|
|
1957
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1580
1958
|
cellPadding,
|
|
1581
1959
|
column.align === "right" && "text-right",
|
|
1582
1960
|
column.align === "center" && "text-center",
|
|
1583
|
-
|
|
1584
|
-
isPinned
|
|
1961
|
+
"hover:bg-gray-100",
|
|
1962
|
+
!isPinned && "z-20",
|
|
1585
1963
|
className
|
|
1586
1964
|
),
|
|
1587
1965
|
style: {
|
|
1588
1966
|
backgroundColor: highlightColor || "rgb(243 244 246)",
|
|
1589
1967
|
// gray-100
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
),
|
|
1598
|
-
maxWidth: Math.max(
|
|
1599
|
-
column.minWidth || column.width || MIN_PINNED_COLUMN_WIDTH,
|
|
1600
|
-
MIN_PINNED_COLUMN_WIDTH
|
|
1601
|
-
)
|
|
1968
|
+
// Always set explicit width to prevent layout shift on selection/re-render
|
|
1969
|
+
...resolvedWidth ? {
|
|
1970
|
+
width: resolvedWidth,
|
|
1971
|
+
minWidth: resolvedWidth
|
|
1972
|
+
} : {
|
|
1973
|
+
width: column.width || column.minWidth,
|
|
1974
|
+
minWidth: column.minWidth || column.width
|
|
1602
1975
|
},
|
|
1603
|
-
|
|
1604
|
-
//
|
|
1976
|
+
...isPinned && pinnedZIndex !== void 0 && { zIndex: pinnedZIndex + 10 },
|
|
1977
|
+
// +10 so headers are above cells
|
|
1605
1978
|
...positionStyles
|
|
1606
1979
|
},
|
|
1607
1980
|
children: [
|
|
1608
1981
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-1", children: [
|
|
1609
1982
|
/* @__PURE__ */ jsxs6("span", { className: "flex-1 flex items-center gap-1", children: [
|
|
1610
1983
|
column.label,
|
|
1611
|
-
isSorted && /* @__PURE__ */ jsx6("span", { className: "text-blue-600", children: sortDirection === "asc" ? /* @__PURE__ */ jsx6(HiChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx6(HiChevronDown, { className: "h-3 w-3" }) })
|
|
1984
|
+
isSorted && /* @__PURE__ */ jsx6("span", { className: "text-blue-600", children: sortDirection === "asc" ? /* @__PURE__ */ jsx6(HiChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx6(HiChevronDown, { className: "h-3 w-3" }) }),
|
|
1985
|
+
hasDuplicateCheck && duplicateCount != null && duplicateCount > 0 && /* @__PURE__ */ jsx6(
|
|
1986
|
+
"span",
|
|
1987
|
+
{
|
|
1988
|
+
className: "inline-flex items-center px-1 py-0.5 rounded-full bg-red-100 text-red-600 text-[10px] font-medium leading-none",
|
|
1989
|
+
title: `${duplicateCount} duplicate values found`,
|
|
1990
|
+
children: duplicateCount
|
|
1991
|
+
}
|
|
1992
|
+
)
|
|
1612
1993
|
] }),
|
|
1613
1994
|
/* @__PURE__ */ jsx6(
|
|
1614
1995
|
ColumnHeaderActions,
|
|
1615
1996
|
{
|
|
1997
|
+
enableSorting: column.sortable,
|
|
1998
|
+
sortDirection: isSorted ? sortDirection : null,
|
|
1999
|
+
onSortClick,
|
|
1616
2000
|
enableFiltering: column.filterable,
|
|
1617
2001
|
enableHighlighting: column.highlightable !== false && !!onHighlightClick,
|
|
1618
2002
|
enablePinning: column.pinnable !== false,
|
|
@@ -1621,21 +2005,249 @@ var SpreadsheetHeader = ({
|
|
|
1621
2005
|
isPinned,
|
|
1622
2006
|
onFilterClick,
|
|
1623
2007
|
onHighlightClick,
|
|
1624
|
-
onPinClick
|
|
2008
|
+
onPinClick,
|
|
2009
|
+
resolvedWidth,
|
|
2010
|
+
enableDuplicateCheck: true,
|
|
2011
|
+
hasDuplicateCheck,
|
|
2012
|
+
onDuplicateCheckClick
|
|
1625
2013
|
}
|
|
1626
2014
|
)
|
|
1627
2015
|
] }),
|
|
1628
|
-
children
|
|
2016
|
+
children && React4.Children.map(
|
|
2017
|
+
children,
|
|
2018
|
+
(child) => React4.isValidElement(child) ? React4.cloneElement(child, { triggerRef: thRef }) : child
|
|
2019
|
+
),
|
|
2020
|
+
resizeHandleProps && /* @__PURE__ */ jsx6(
|
|
2021
|
+
"div",
|
|
2022
|
+
{
|
|
2023
|
+
onMouseDown: resizeHandleProps.onMouseDown,
|
|
2024
|
+
style: resizeHandleProps.style,
|
|
2025
|
+
className: resizeHandleProps.className
|
|
2026
|
+
}
|
|
2027
|
+
)
|
|
1629
2028
|
]
|
|
1630
2029
|
}
|
|
1631
2030
|
);
|
|
1632
2031
|
};
|
|
1633
2032
|
SpreadsheetHeader.displayName = "SpreadsheetHeader";
|
|
2033
|
+
var MemoizedSpreadsheetHeader = memo2(SpreadsheetHeader, (prevProps, nextProps) => {
|
|
2034
|
+
if (prevProps.column.id !== nextProps.column.id) return false;
|
|
2035
|
+
if (prevProps.sortConfig?.columnId !== nextProps.sortConfig?.columnId) return false;
|
|
2036
|
+
if (prevProps.sortConfig?.direction !== nextProps.sortConfig?.direction) return false;
|
|
2037
|
+
if (prevProps.hasActiveFilter !== nextProps.hasActiveFilter) return false;
|
|
2038
|
+
if (prevProps.isPinned !== nextProps.isPinned) return false;
|
|
2039
|
+
if (prevProps.pinSide !== nextProps.pinSide) return false;
|
|
2040
|
+
if (prevProps.leftOffset !== nextProps.leftOffset) return false;
|
|
2041
|
+
if (prevProps.rightOffset !== nextProps.rightOffset) return false;
|
|
2042
|
+
if (prevProps.highlightColor !== nextProps.highlightColor) return false;
|
|
2043
|
+
if (prevProps.compactMode !== nextProps.compactMode) return false;
|
|
2044
|
+
if (prevProps.pinnedZIndex !== nextProps.pinnedZIndex) return false;
|
|
2045
|
+
if (prevProps.resolvedWidth !== nextProps.resolvedWidth) return false;
|
|
2046
|
+
if (prevProps.topOffset !== nextProps.topOffset) return false;
|
|
2047
|
+
if (prevProps.hasDuplicateCheck !== nextProps.hasDuplicateCheck) return false;
|
|
2048
|
+
if (prevProps.duplicateCount !== nextProps.duplicateCount) return false;
|
|
2049
|
+
if (prevProps.children !== nextProps.children) return false;
|
|
2050
|
+
return true;
|
|
2051
|
+
});
|
|
2052
|
+
MemoizedSpreadsheetHeader.displayName = "MemoizedSpreadsheetHeader";
|
|
2053
|
+
|
|
2054
|
+
// src/hooks/useSpreadsheetPinning.ts
|
|
2055
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef5, useState as useState4 } from "react";
|
|
2056
|
+
var ROW_INDEX_COLUMN_ID = "__row_index__";
|
|
2057
|
+
var ROW_INDEX_COLUMN_WIDTH = 90;
|
|
2058
|
+
function useSpreadsheetPinning({
|
|
2059
|
+
columns,
|
|
2060
|
+
columnGroups,
|
|
2061
|
+
showRowIndex = true,
|
|
2062
|
+
defaultPinnedColumns = [],
|
|
2063
|
+
defaultPinnedRightColumns = [],
|
|
2064
|
+
getColumnWidth
|
|
2065
|
+
}) {
|
|
2066
|
+
const [pinnedColumns, setPinnedColumns] = useState4(() => {
|
|
2067
|
+
const map = /* @__PURE__ */ new Map();
|
|
2068
|
+
if (showRowIndex) {
|
|
2069
|
+
map.set(ROW_INDEX_COLUMN_ID, "left");
|
|
2070
|
+
}
|
|
2071
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2072
|
+
defaultPinnedColumns.forEach((col) => {
|
|
2073
|
+
if (col === ROW_INDEX_COLUMN_ID) return;
|
|
2074
|
+
const colIndex = columns.findIndex((c) => c.id === col);
|
|
2075
|
+
map.set(col, colIndex >= 0 && colIndex <= midpoint ? "left" : "right");
|
|
2076
|
+
});
|
|
2077
|
+
defaultPinnedRightColumns.forEach((col) => map.set(col, "right"));
|
|
2078
|
+
return map;
|
|
2079
|
+
});
|
|
2080
|
+
const [collapsedGroups, setCollapsedGroups] = useState4(/* @__PURE__ */ new Set());
|
|
2081
|
+
const measuredWidthsRef = useRef5(/* @__PURE__ */ new Map());
|
|
2082
|
+
const tableElRef = useRef5(null);
|
|
2083
|
+
const [measureVersion, setMeasureVersion] = useState4(0);
|
|
2084
|
+
const measureColumnWidths = useCallback3(() => {
|
|
2085
|
+
const el = tableElRef.current;
|
|
2086
|
+
if (!el) return;
|
|
2087
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
2088
|
+
const headers = el.querySelectorAll("th[data-column-id]");
|
|
2089
|
+
headers.forEach((th) => {
|
|
2090
|
+
const colId = th.getAttribute("data-column-id");
|
|
2091
|
+
if (colId) {
|
|
2092
|
+
newMap.set(colId, th.offsetWidth);
|
|
2093
|
+
}
|
|
2094
|
+
});
|
|
2095
|
+
const rowIndexTd = el.querySelector('td[data-column-id="__row_index__"]');
|
|
2096
|
+
if (rowIndexTd) {
|
|
2097
|
+
newMap.set(ROW_INDEX_COLUMN_ID, rowIndexTd.offsetWidth);
|
|
2098
|
+
}
|
|
2099
|
+
measuredWidthsRef.current = newMap;
|
|
2100
|
+
setMeasureVersion((v) => v + 1);
|
|
2101
|
+
}, []);
|
|
2102
|
+
const measureRef = useCallback3((el) => {
|
|
2103
|
+
tableElRef.current = el;
|
|
2104
|
+
}, []);
|
|
2105
|
+
const isRowIndexPinned = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
2106
|
+
const handleTogglePin = useCallback3(
|
|
2107
|
+
(columnId) => {
|
|
2108
|
+
setPinnedColumns((prev) => {
|
|
2109
|
+
const newMap = new Map(prev);
|
|
2110
|
+
if (newMap.has(columnId)) {
|
|
2111
|
+
newMap.delete(columnId);
|
|
2112
|
+
} else {
|
|
2113
|
+
if (columnId === ROW_INDEX_COLUMN_ID) {
|
|
2114
|
+
newMap.set(columnId, "left");
|
|
2115
|
+
} else {
|
|
2116
|
+
const colIndex = columns.findIndex((c) => c.id === columnId);
|
|
2117
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2118
|
+
newMap.set(columnId, colIndex <= midpoint ? "left" : "right");
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
return newMap;
|
|
2122
|
+
});
|
|
2123
|
+
},
|
|
2124
|
+
[columns]
|
|
2125
|
+
);
|
|
2126
|
+
const setPinnedColumnsFromIds = useCallback3((columnIds) => {
|
|
2127
|
+
const map = /* @__PURE__ */ new Map();
|
|
2128
|
+
if (showRowIndex) {
|
|
2129
|
+
map.set(ROW_INDEX_COLUMN_ID, "left");
|
|
2130
|
+
}
|
|
2131
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2132
|
+
columnIds.forEach((col) => {
|
|
2133
|
+
if (col !== ROW_INDEX_COLUMN_ID) {
|
|
2134
|
+
const colIndex = columns.findIndex((c) => c.id === col);
|
|
2135
|
+
map.set(col, colIndex >= 0 && colIndex <= midpoint ? "left" : "right");
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
setPinnedColumns(map);
|
|
2139
|
+
}, [showRowIndex, columns]);
|
|
2140
|
+
const handleToggleGroupCollapse = useCallback3((groupId) => {
|
|
2141
|
+
setCollapsedGroups((prev) => {
|
|
2142
|
+
const newSet = new Set(prev);
|
|
2143
|
+
if (newSet.has(groupId)) {
|
|
2144
|
+
newSet.delete(groupId);
|
|
2145
|
+
} else {
|
|
2146
|
+
newSet.add(groupId);
|
|
2147
|
+
}
|
|
2148
|
+
return newSet;
|
|
2149
|
+
});
|
|
2150
|
+
}, []);
|
|
2151
|
+
const visibleColumns = useMemo(() => {
|
|
2152
|
+
if (!columns || !Array.isArray(columns) || columns.length === 0) return [];
|
|
2153
|
+
let result = [...columns];
|
|
2154
|
+
if (columnGroups && Array.isArray(columnGroups)) {
|
|
2155
|
+
result = result.filter((column) => {
|
|
2156
|
+
const group = columnGroups.find((g) => g.columns.includes(column.id));
|
|
2157
|
+
if (!group) return true;
|
|
2158
|
+
if (!collapsedGroups.has(group.id)) return true;
|
|
2159
|
+
return pinnedColumns.has(column.id);
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
return result;
|
|
2163
|
+
}, [columns, columnGroups, collapsedGroups, pinnedColumns]);
|
|
2164
|
+
useEffect4(() => {
|
|
2165
|
+
requestAnimationFrame(() => measureColumnWidths());
|
|
2166
|
+
}, [measureColumnWidths, visibleColumns, pinnedColumns]);
|
|
2167
|
+
const widthMap = useMemo(() => {
|
|
2168
|
+
void measureVersion;
|
|
2169
|
+
const map = /* @__PURE__ */ new Map();
|
|
2170
|
+
for (const col of columns) {
|
|
2171
|
+
const measured = measuredWidthsRef.current.get(col.id);
|
|
2172
|
+
if (measured) {
|
|
2173
|
+
map.set(col.id, measured);
|
|
2174
|
+
continue;
|
|
2175
|
+
}
|
|
2176
|
+
const resized = getColumnWidth?.(col.id);
|
|
2177
|
+
if (resized) {
|
|
2178
|
+
map.set(col.id, resized);
|
|
2179
|
+
continue;
|
|
2180
|
+
}
|
|
2181
|
+
map.set(col.id, col.width || col.minWidth || 100);
|
|
2182
|
+
}
|
|
2183
|
+
const measuredRI = measuredWidthsRef.current.get(ROW_INDEX_COLUMN_ID);
|
|
2184
|
+
map.set(ROW_INDEX_COLUMN_ID, measuredRI || ROW_INDEX_COLUMN_WIDTH);
|
|
2185
|
+
return map;
|
|
2186
|
+
}, [columns, getColumnWidth, measureVersion]);
|
|
2187
|
+
const { leftOffsets, rightOffsets, zIndices } = useMemo(() => {
|
|
2188
|
+
const leftOffsets2 = /* @__PURE__ */ new Map();
|
|
2189
|
+
const rightOffsets2 = /* @__PURE__ */ new Map();
|
|
2190
|
+
const zIndices2 = /* @__PURE__ */ new Map();
|
|
2191
|
+
leftOffsets2.set(ROW_INDEX_COLUMN_ID, 0);
|
|
2192
|
+
zIndices2.set(ROW_INDEX_COLUMN_ID, 40);
|
|
2193
|
+
const leftPinned = visibleColumns.filter((c) => pinnedColumns.get(c.id) === "left");
|
|
2194
|
+
const rightPinned = visibleColumns.filter((c) => pinnedColumns.get(c.id) === "right");
|
|
2195
|
+
const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
2196
|
+
let leftOffset = showRowIndex && isRowIndexPinnedNow ? widthMap.get(ROW_INDEX_COLUMN_ID) || ROW_INDEX_COLUMN_WIDTH : 0;
|
|
2197
|
+
for (let i = 0; i < leftPinned.length; i++) {
|
|
2198
|
+
leftOffsets2.set(leftPinned[i].id, leftOffset);
|
|
2199
|
+
zIndices2.set(leftPinned[i].id, 20 + (leftPinned.length - i));
|
|
2200
|
+
leftOffset += widthMap.get(leftPinned[i].id) || 100;
|
|
2201
|
+
}
|
|
2202
|
+
let rightOffset = 0;
|
|
2203
|
+
for (let i = rightPinned.length - 1; i >= 0; i--) {
|
|
2204
|
+
rightOffsets2.set(rightPinned[i].id, rightOffset);
|
|
2205
|
+
zIndices2.set(rightPinned[i].id, 20 + (rightPinned.length - i));
|
|
2206
|
+
rightOffset += widthMap.get(rightPinned[i].id) || 100;
|
|
2207
|
+
}
|
|
2208
|
+
return { leftOffsets: leftOffsets2, rightOffsets: rightOffsets2, zIndices: zIndices2 };
|
|
2209
|
+
}, [pinnedColumns, visibleColumns, showRowIndex, widthMap]);
|
|
2210
|
+
const getColumnLeftOffset = useCallback3(
|
|
2211
|
+
(columnId) => leftOffsets.get(columnId) ?? 0,
|
|
2212
|
+
[leftOffsets]
|
|
2213
|
+
);
|
|
2214
|
+
const getColumnRightOffset = useCallback3(
|
|
2215
|
+
(columnId) => rightOffsets.get(columnId) ?? 0,
|
|
2216
|
+
[rightOffsets]
|
|
2217
|
+
);
|
|
2218
|
+
const isColumnPinned = useCallback3(
|
|
2219
|
+
(columnId) => pinnedColumns.has(columnId),
|
|
2220
|
+
[pinnedColumns]
|
|
2221
|
+
);
|
|
2222
|
+
const getColumnPinSide = useCallback3(
|
|
2223
|
+
(columnId) => pinnedColumns.get(columnId),
|
|
2224
|
+
[pinnedColumns]
|
|
2225
|
+
);
|
|
2226
|
+
const getPinnedZIndex = useCallback3(
|
|
2227
|
+
(columnId) => zIndices.get(columnId) ?? 0,
|
|
2228
|
+
[zIndices]
|
|
2229
|
+
);
|
|
2230
|
+
return {
|
|
2231
|
+
pinnedColumns,
|
|
2232
|
+
isRowIndexPinned,
|
|
2233
|
+
collapsedGroups,
|
|
2234
|
+
visibleColumns,
|
|
2235
|
+
handleTogglePin,
|
|
2236
|
+
handleToggleGroupCollapse,
|
|
2237
|
+
setPinnedColumnsFromIds,
|
|
2238
|
+
getColumnLeftOffset,
|
|
2239
|
+
getColumnRightOffset,
|
|
2240
|
+
isColumnPinned,
|
|
2241
|
+
getColumnPinSide,
|
|
2242
|
+
getPinnedZIndex,
|
|
2243
|
+
measureRef
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
1634
2246
|
|
|
1635
2247
|
// src/components/RowIndexColumnHeader.tsx
|
|
1636
2248
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1637
|
-
var cellPaddingCompact3 = "px-1 py-
|
|
1638
|
-
var cellPaddingNormal3 = "px-2 py-
|
|
2249
|
+
var cellPaddingCompact3 = "px-1.5 py-1";
|
|
2250
|
+
var cellPaddingNormal3 = "px-2 py-2";
|
|
1639
2251
|
function RowIndexColumnHeader({
|
|
1640
2252
|
enableHighlighting = false,
|
|
1641
2253
|
highlightColor,
|
|
@@ -1652,18 +2264,16 @@ function RowIndexColumnHeader({
|
|
|
1652
2264
|
"th",
|
|
1653
2265
|
{
|
|
1654
2266
|
className: cn(
|
|
1655
|
-
"border border-gray-200 text-center font-bold text-gray-700",
|
|
1656
|
-
compactMode ? "text-
|
|
2267
|
+
"border border-gray-200 text-center font-bold text-gray-700 sticky",
|
|
2268
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1657
2269
|
cellPadding,
|
|
1658
|
-
isPinned ? "z-30" : "z-20",
|
|
1659
2270
|
className
|
|
1660
2271
|
),
|
|
1661
2272
|
style: {
|
|
1662
2273
|
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1663
2274
|
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
left: isPinned ? 0 : void 0,
|
|
2275
|
+
left: 0,
|
|
2276
|
+
zIndex: 50,
|
|
1667
2277
|
backgroundColor: highlightColor || "rgb(243 244 246)"
|
|
1668
2278
|
}
|
|
1669
2279
|
}
|
|
@@ -1673,18 +2283,16 @@ function RowIndexColumnHeader({
|
|
|
1673
2283
|
"th",
|
|
1674
2284
|
{
|
|
1675
2285
|
className: cn(
|
|
1676
|
-
"border border-gray-200 text-center font-bold text-gray-700 group",
|
|
1677
|
-
compactMode ? "text-
|
|
2286
|
+
"border border-gray-200 text-center font-bold text-gray-700 group sticky",
|
|
2287
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1678
2288
|
cellPadding,
|
|
1679
|
-
isPinned ? "z-30" : "z-20",
|
|
1680
2289
|
className
|
|
1681
2290
|
),
|
|
1682
2291
|
style: {
|
|
1683
2292
|
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1684
2293
|
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
left: isPinned ? 0 : void 0,
|
|
2294
|
+
left: 0,
|
|
2295
|
+
zIndex: 50,
|
|
1688
2296
|
backgroundColor: highlightColor || "rgb(243 244 246)"
|
|
1689
2297
|
},
|
|
1690
2298
|
children: /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-center gap-1", children: [
|
|
@@ -1709,231 +2317,104 @@ function RowIndexColumnHeader({
|
|
|
1709
2317
|
}
|
|
1710
2318
|
RowIndexColumnHeader.displayName = "RowIndexColumnHeader";
|
|
1711
2319
|
|
|
1712
|
-
// src/hooks/useSpreadsheetHighlighting.ts
|
|
1713
|
-
import { useCallback as useCallback2, useState as useState4 } from "react";
|
|
1714
|
-
var HIGHLIGHT_COLORS = {
|
|
1715
|
-
// Darker colors for rows (more visible)
|
|
1716
|
-
row: [
|
|
1717
|
-
"#fef08a",
|
|
1718
|
-
// yellow
|
|
1719
|
-
"#bbf7d0",
|
|
1720
|
-
// green
|
|
1721
|
-
"#bfdbfe",
|
|
1722
|
-
// blue
|
|
1723
|
-
"#fecaca",
|
|
1724
|
-
// red
|
|
1725
|
-
"#e9d5ff",
|
|
1726
|
-
// purple
|
|
1727
|
-
"#fed7aa",
|
|
1728
|
-
// orange
|
|
1729
|
-
"#a5f3fc",
|
|
1730
|
-
// cyan
|
|
1731
|
-
"#fce7f3",
|
|
1732
|
-
// pink
|
|
1733
|
-
"#d1d5db"
|
|
1734
|
-
// gray
|
|
1735
|
-
],
|
|
1736
|
-
// Lighter colors for columns/headers (subtle background)
|
|
1737
|
-
column: [
|
|
1738
|
-
"#fef9c3",
|
|
1739
|
-
// yellow-100
|
|
1740
|
-
"#dcfce7",
|
|
1741
|
-
// green-100
|
|
1742
|
-
"#dbeafe",
|
|
1743
|
-
// blue-100
|
|
1744
|
-
"#fee2e2",
|
|
1745
|
-
// red-100
|
|
1746
|
-
"#f3e8ff",
|
|
1747
|
-
// purple-100
|
|
1748
|
-
"#ffedd5",
|
|
1749
|
-
// orange-100
|
|
1750
|
-
"#cffafe",
|
|
1751
|
-
// cyan-100
|
|
1752
|
-
"#fce7f3",
|
|
1753
|
-
// pink-100
|
|
1754
|
-
"#e5e7eb"
|
|
1755
|
-
// gray-200
|
|
1756
|
-
]
|
|
1757
|
-
};
|
|
1758
|
-
function useSpreadsheetHighlighting({
|
|
1759
|
-
externalRowHighlights,
|
|
1760
|
-
onRowHighlight,
|
|
1761
|
-
externalColumnHighlights,
|
|
1762
|
-
onColumnHighlight,
|
|
1763
|
-
externalCellHighlights,
|
|
1764
|
-
onCellHighlight
|
|
1765
|
-
} = {}) {
|
|
1766
|
-
const [cellHighlightsInternal, setCellHighlightsInternal] = useState4([]);
|
|
1767
|
-
const [rowHighlightsInternal, setRowHighlightsInternal] = useState4([]);
|
|
1768
|
-
const [columnHighlightsInternal, setColumnHighlightsInternal] = useState4({});
|
|
1769
|
-
const [highlightPickerRow, setHighlightPickerRow] = useState4(null);
|
|
1770
|
-
const [highlightPickerColumn, setHighlightPickerColumn] = useState4(null);
|
|
1771
|
-
const [highlightPickerCell, setHighlightPickerCell] = useState4(null);
|
|
1772
|
-
const cellHighlights = externalCellHighlights || cellHighlightsInternal;
|
|
1773
|
-
const rowHighlights = externalRowHighlights || rowHighlightsInternal;
|
|
1774
|
-
const columnHighlights = externalColumnHighlights || columnHighlightsInternal;
|
|
1775
|
-
const getCellHighlight = useCallback2(
|
|
1776
|
-
(rowId, columnId) => {
|
|
1777
|
-
return cellHighlights.find((h) => h.rowId === rowId && h.columnId === columnId)?.color;
|
|
1778
|
-
},
|
|
1779
|
-
[cellHighlights]
|
|
1780
|
-
);
|
|
1781
|
-
const handleCellHighlightToggle = useCallback2(
|
|
1782
|
-
(rowId, columnId, color = "#fef08a") => {
|
|
1783
|
-
if (onCellHighlight) {
|
|
1784
|
-
onCellHighlight(rowId, columnId, color);
|
|
1785
|
-
} else {
|
|
1786
|
-
setCellHighlightsInternal((prev) => {
|
|
1787
|
-
const existing = prev.find((h) => h.rowId === rowId && h.columnId === columnId);
|
|
1788
|
-
if (existing) {
|
|
1789
|
-
if (color === null) {
|
|
1790
|
-
return prev.filter(
|
|
1791
|
-
(h) => !(h.rowId === rowId && h.columnId === columnId)
|
|
1792
|
-
);
|
|
1793
|
-
}
|
|
1794
|
-
return prev.map(
|
|
1795
|
-
(h) => h.rowId === rowId && h.columnId === columnId ? { ...h, color } : h
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
if (color) {
|
|
1799
|
-
return [...prev, { rowId, columnId, color }];
|
|
1800
|
-
}
|
|
1801
|
-
return prev;
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1804
|
-
setHighlightPickerCell(null);
|
|
1805
|
-
},
|
|
1806
|
-
[onCellHighlight]
|
|
1807
|
-
);
|
|
1808
|
-
const getRowHighlight = useCallback2(
|
|
1809
|
-
(rowId) => {
|
|
1810
|
-
return rowHighlights.find((h) => h.rowId === rowId && !h.columnId);
|
|
1811
|
-
},
|
|
1812
|
-
[rowHighlights]
|
|
1813
|
-
);
|
|
1814
|
-
const handleRowHighlightToggle = useCallback2(
|
|
1815
|
-
(rowId, color) => {
|
|
1816
|
-
if (onRowHighlight) {
|
|
1817
|
-
onRowHighlight(rowId, color);
|
|
1818
|
-
} else {
|
|
1819
|
-
setRowHighlightsInternal((prev) => {
|
|
1820
|
-
const existing = prev.find((h) => h.rowId === rowId && !h.columnId);
|
|
1821
|
-
if (existing) {
|
|
1822
|
-
if (color === null) {
|
|
1823
|
-
return prev.filter((h) => !(h.rowId === rowId && !h.columnId));
|
|
1824
|
-
}
|
|
1825
|
-
return prev.map(
|
|
1826
|
-
(h) => h.rowId === rowId && !h.columnId ? { ...h, color } : h
|
|
1827
|
-
);
|
|
1828
|
-
}
|
|
1829
|
-
if (color) {
|
|
1830
|
-
return [...prev, { rowId, color }];
|
|
1831
|
-
}
|
|
1832
|
-
return prev;
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
setHighlightPickerRow(null);
|
|
1836
|
-
},
|
|
1837
|
-
[onRowHighlight]
|
|
1838
|
-
);
|
|
1839
|
-
const getColumnHighlight = useCallback2(
|
|
1840
|
-
(columnId) => {
|
|
1841
|
-
return columnHighlights[columnId];
|
|
1842
|
-
},
|
|
1843
|
-
[columnHighlights]
|
|
1844
|
-
);
|
|
1845
|
-
const handleColumnHighlightToggle = useCallback2(
|
|
1846
|
-
(columnId, color) => {
|
|
1847
|
-
if (onColumnHighlight) {
|
|
1848
|
-
onColumnHighlight(columnId, color);
|
|
1849
|
-
} else {
|
|
1850
|
-
setColumnHighlightsInternal((prev) => {
|
|
1851
|
-
const newHighlights = { ...prev };
|
|
1852
|
-
if (color === null) {
|
|
1853
|
-
delete newHighlights[columnId];
|
|
1854
|
-
} else {
|
|
1855
|
-
newHighlights[columnId] = color;
|
|
1856
|
-
}
|
|
1857
|
-
return newHighlights;
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
setHighlightPickerColumn(null);
|
|
1861
|
-
},
|
|
1862
|
-
[onColumnHighlight]
|
|
1863
|
-
);
|
|
1864
|
-
const clearAllHighlights = useCallback2(() => {
|
|
1865
|
-
setCellHighlightsInternal([]);
|
|
1866
|
-
setRowHighlightsInternal([]);
|
|
1867
|
-
setColumnHighlightsInternal({});
|
|
1868
|
-
}, []);
|
|
1869
|
-
return {
|
|
1870
|
-
// Cell highlights
|
|
1871
|
-
cellHighlights,
|
|
1872
|
-
getCellHighlight,
|
|
1873
|
-
handleCellHighlightToggle,
|
|
1874
|
-
// Row highlights
|
|
1875
|
-
rowHighlights,
|
|
1876
|
-
getRowHighlight,
|
|
1877
|
-
handleRowHighlightToggle,
|
|
1878
|
-
// Column highlights
|
|
1879
|
-
columnHighlights,
|
|
1880
|
-
getColumnHighlight,
|
|
1881
|
-
handleColumnHighlightToggle,
|
|
1882
|
-
// Picker state
|
|
1883
|
-
highlightPickerRow,
|
|
1884
|
-
setHighlightPickerRow,
|
|
1885
|
-
highlightPickerColumn,
|
|
1886
|
-
setHighlightPickerColumn,
|
|
1887
|
-
highlightPickerCell,
|
|
1888
|
-
setHighlightPickerCell,
|
|
1889
|
-
// Utility
|
|
1890
|
-
clearAllHighlights
|
|
1891
|
-
};
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
2320
|
// src/components/ColorPickerPopover.tsx
|
|
2321
|
+
import { useState as useState5, useRef as useRef6 } from "react";
|
|
1895
2322
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1896
2323
|
function ColorPickerPopover({
|
|
1897
2324
|
title,
|
|
1898
|
-
|
|
1899
|
-
colors,
|
|
2325
|
+
recentColors = [],
|
|
1900
2326
|
onSelectColor,
|
|
1901
2327
|
onClose,
|
|
1902
2328
|
className
|
|
1903
2329
|
}) {
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
),
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2330
|
+
const [selectedColor, setSelectedColor] = useState5("#fef08a");
|
|
2331
|
+
const colorInputRef = useRef6(null);
|
|
2332
|
+
const handleConfirm = () => {
|
|
2333
|
+
onSelectColor(selectedColor);
|
|
2334
|
+
};
|
|
2335
|
+
return /* @__PURE__ */ jsx8("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", onClick: onClose, children: /* @__PURE__ */ jsxs8(
|
|
2336
|
+
"div",
|
|
2337
|
+
{
|
|
2338
|
+
className: cn("bg-white rounded-lg shadow-xl p-4 w-72", className),
|
|
2339
|
+
onClick: (e) => e.stopPropagation(),
|
|
2340
|
+
children: [
|
|
2341
|
+
/* @__PURE__ */ jsx8("h3", { className: "text-sm font-semibold mb-3", children: title }),
|
|
2342
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
2343
|
+
/* @__PURE__ */ jsx8(
|
|
2344
|
+
"input",
|
|
2345
|
+
{
|
|
2346
|
+
ref: colorInputRef,
|
|
2347
|
+
type: "color",
|
|
2348
|
+
value: selectedColor,
|
|
2349
|
+
onChange: (e) => setSelectedColor(e.target.value),
|
|
2350
|
+
className: "w-10 h-10 rounded border border-gray-200 cursor-pointer p-0.5"
|
|
2351
|
+
}
|
|
2352
|
+
),
|
|
2353
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1", children: [
|
|
2354
|
+
/* @__PURE__ */ jsx8(
|
|
2355
|
+
"div",
|
|
2356
|
+
{
|
|
2357
|
+
className: "w-full h-8 rounded border border-gray-200",
|
|
2358
|
+
style: { backgroundColor: selectedColor }
|
|
2359
|
+
}
|
|
2360
|
+
),
|
|
2361
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs text-gray-500 mt-1 block", children: selectedColor })
|
|
2362
|
+
] })
|
|
2363
|
+
] }),
|
|
2364
|
+
recentColors.length > 0 && /* @__PURE__ */ jsxs8("div", { className: "mb-3", children: [
|
|
2365
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs text-gray-500 mb-1.5 block", children: "Recent" }),
|
|
2366
|
+
/* @__PURE__ */ jsx8("div", { className: "flex gap-1.5", children: recentColors.map((color) => /* @__PURE__ */ jsx8(
|
|
2367
|
+
"button",
|
|
2368
|
+
{
|
|
2369
|
+
type: "button",
|
|
2370
|
+
onClick: () => setSelectedColor(color),
|
|
2371
|
+
className: cn(
|
|
2372
|
+
"w-7 h-7 rounded border-2 transition-transform hover:scale-110",
|
|
2373
|
+
selectedColor === color ? "border-blue-500" : "border-gray-200 hover:border-gray-400"
|
|
2374
|
+
),
|
|
2375
|
+
style: { backgroundColor: color },
|
|
2376
|
+
title: color
|
|
2377
|
+
},
|
|
2378
|
+
color
|
|
2379
|
+
)) })
|
|
2380
|
+
] }),
|
|
2381
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 pt-2 border-t border-gray-100", children: [
|
|
2382
|
+
/* @__PURE__ */ jsx8(
|
|
2383
|
+
"button",
|
|
2384
|
+
{
|
|
2385
|
+
type: "button",
|
|
2386
|
+
onClick: handleConfirm,
|
|
2387
|
+
className: "flex-1 px-2.5 py-1.5 text-xs text-white bg-blue-600 hover:bg-blue-700 rounded transition-colors font-medium",
|
|
2388
|
+
children: "Apply"
|
|
2389
|
+
}
|
|
2390
|
+
),
|
|
2391
|
+
/* @__PURE__ */ jsx8(
|
|
2392
|
+
"button",
|
|
2393
|
+
{
|
|
2394
|
+
type: "button",
|
|
2395
|
+
onClick: () => onSelectColor(null),
|
|
2396
|
+
className: "px-2.5 py-1.5 text-xs text-red-600 hover:bg-red-50 rounded border border-red-200 transition-colors",
|
|
2397
|
+
children: "Clear"
|
|
2398
|
+
}
|
|
2399
|
+
),
|
|
2400
|
+
/* @__PURE__ */ jsx8(
|
|
2401
|
+
"button",
|
|
2402
|
+
{
|
|
2403
|
+
type: "button",
|
|
2404
|
+
onClick: onClose,
|
|
2405
|
+
className: "px-2.5 py-1.5 text-xs text-gray-600 hover:bg-gray-100 rounded transition-colors",
|
|
2406
|
+
children: "Cancel"
|
|
2407
|
+
}
|
|
2408
|
+
)
|
|
2409
|
+
] })
|
|
2410
|
+
]
|
|
2411
|
+
}
|
|
2412
|
+
) });
|
|
1932
2413
|
}
|
|
1933
2414
|
ColorPickerPopover.displayName = "ColorPickerPopover";
|
|
1934
2415
|
|
|
1935
2416
|
// src/components/SpreadsheetSettingsModal.tsx
|
|
1936
|
-
import { useState as
|
|
2417
|
+
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
1937
2418
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1938
2419
|
var DEFAULT_SETTINGS = {
|
|
1939
2420
|
defaultPinnedColumns: [],
|
|
@@ -1952,9 +2433,9 @@ var SpreadsheetSettingsModal = ({
|
|
|
1952
2433
|
title = "Spreadsheet Settings",
|
|
1953
2434
|
pageSizeOptions = [25, 50, 100, 200]
|
|
1954
2435
|
}) => {
|
|
1955
|
-
const [activeTab, setActiveTab] =
|
|
1956
|
-
const [localSettings, setLocalSettings] =
|
|
1957
|
-
|
|
2436
|
+
const [activeTab, setActiveTab] = useState6("columns");
|
|
2437
|
+
const [localSettings, setLocalSettings] = useState6(settings);
|
|
2438
|
+
useEffect5(() => {
|
|
1958
2439
|
setLocalSettings(settings);
|
|
1959
2440
|
}, [settings]);
|
|
1960
2441
|
if (!isOpen) return null;
|
|
@@ -2262,11 +2743,11 @@ var SpreadsheetSettingsModal = ({
|
|
|
2262
2743
|
SpreadsheetSettingsModal.displayName = "SpreadsheetSettingsModal";
|
|
2263
2744
|
|
|
2264
2745
|
// src/components/CommentModals.tsx
|
|
2265
|
-
import { useState as
|
|
2746
|
+
import { useState as useState7, useEffect as useEffect6 } from "react";
|
|
2266
2747
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2267
2748
|
function AddCommentModal({ isOpen, columnLabel, onAdd, onClose }) {
|
|
2268
|
-
const [commentText, setCommentText] =
|
|
2269
|
-
|
|
2749
|
+
const [commentText, setCommentText] = useState7("");
|
|
2750
|
+
useEffect6(() => {
|
|
2270
2751
|
if (!isOpen) {
|
|
2271
2752
|
setCommentText("");
|
|
2272
2753
|
}
|
|
@@ -2436,7 +2917,7 @@ function DeleteConfirmationModal({
|
|
|
2436
2917
|
DeleteConfirmationModal.displayName = "DeleteConfirmationModal";
|
|
2437
2918
|
|
|
2438
2919
|
// src/components/KeyboardShortcutsModal.tsx
|
|
2439
|
-
import
|
|
2920
|
+
import React5 from "react";
|
|
2440
2921
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2441
2922
|
function KeyboardShortcutsModal({
|
|
2442
2923
|
isOpen,
|
|
@@ -2478,7 +2959,7 @@ function ShortcutSection({ title, children }) {
|
|
|
2478
2959
|
function ShortcutRow({ label, keys }) {
|
|
2479
2960
|
return /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between", children: [
|
|
2480
2961
|
/* @__PURE__ */ jsx11("span", { className: "text-gray-600 text-sm", children: label }),
|
|
2481
|
-
/* @__PURE__ */ jsx11("div", { className: "flex items-center gap-1", children: keys.map((key, index) => /* @__PURE__ */ jsxs11(
|
|
2962
|
+
/* @__PURE__ */ jsx11("div", { className: "flex items-center gap-1", children: keys.map((key, index) => /* @__PURE__ */ jsxs11(React5.Fragment, { children: [
|
|
2482
2963
|
index > 0 && /* @__PURE__ */ jsx11("span", { className: "text-gray-400", children: "+" }),
|
|
2483
2964
|
key.includes("Click") ? /* @__PURE__ */ jsx11("span", { className: "text-gray-500 text-xs", children: key }) : /* @__PURE__ */ jsx11("kbd", { className: "px-2 py-1 bg-gray-100 text-gray-800 rounded text-xs border border-gray-200", children: key })
|
|
2484
2965
|
] }, index)) })
|
|
@@ -2553,7 +3034,7 @@ RowContextMenu.displayName = "RowContextMenu";
|
|
|
2553
3034
|
import { Pagination } from "@xcelsior/design-system";
|
|
2554
3035
|
|
|
2555
3036
|
// src/hooks/useSpreadsheetFiltering.ts
|
|
2556
|
-
import { useCallback as
|
|
3037
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useState as useState8 } from "react";
|
|
2557
3038
|
import { FilterChain, LazyArray } from "@xcelsior/utils";
|
|
2558
3039
|
function useSpreadsheetFiltering({
|
|
2559
3040
|
data,
|
|
@@ -2563,18 +3044,20 @@ function useSpreadsheetFiltering({
|
|
|
2563
3044
|
serverSide = false,
|
|
2564
3045
|
controlledFilters,
|
|
2565
3046
|
controlledSortConfig,
|
|
2566
|
-
defaultSortConfig
|
|
3047
|
+
defaultSortConfig,
|
|
3048
|
+
duplicateRowIds,
|
|
3049
|
+
getRowId
|
|
2567
3050
|
}) {
|
|
2568
|
-
const [internalFilters, setInternalFilters] =
|
|
3051
|
+
const [internalFilters, setInternalFilters] = useState8(
|
|
2569
3052
|
{}
|
|
2570
3053
|
);
|
|
2571
|
-
const [internalSortConfig, setInternalSortConfig] =
|
|
3054
|
+
const [internalSortConfig, setInternalSortConfig] = useState8(
|
|
2572
3055
|
defaultSortConfig ?? null
|
|
2573
3056
|
);
|
|
2574
|
-
const [activeFilterColumn, setActiveFilterColumn] =
|
|
3057
|
+
const [activeFilterColumn, setActiveFilterColumn] = useState8(null);
|
|
2575
3058
|
const filters = controlledFilters ?? internalFilters;
|
|
2576
3059
|
const sortConfig = controlledSortConfig !== void 0 ? controlledSortConfig : internalSortConfig;
|
|
2577
|
-
const applyTextCondition =
|
|
3060
|
+
const applyTextCondition = useCallback4(
|
|
2578
3061
|
(value, condition) => {
|
|
2579
3062
|
const strValue = String(value ?? "").toLowerCase();
|
|
2580
3063
|
const filterValue = (condition.value ?? "").toLowerCase();
|
|
@@ -2601,7 +3084,7 @@ function useSpreadsheetFiltering({
|
|
|
2601
3084
|
},
|
|
2602
3085
|
[]
|
|
2603
3086
|
);
|
|
2604
|
-
const applyNumberCondition =
|
|
3087
|
+
const applyNumberCondition = useCallback4(
|
|
2605
3088
|
(value, condition) => {
|
|
2606
3089
|
if (condition.operator === "isEmpty") return isBlankValue(value);
|
|
2607
3090
|
if (condition.operator === "isNotEmpty") return !isBlankValue(value);
|
|
@@ -2630,7 +3113,7 @@ function useSpreadsheetFiltering({
|
|
|
2630
3113
|
},
|
|
2631
3114
|
[]
|
|
2632
3115
|
);
|
|
2633
|
-
const applyDateCondition =
|
|
3116
|
+
const applyDateCondition = useCallback4(
|
|
2634
3117
|
(value, condition) => {
|
|
2635
3118
|
if (condition.operator === "isEmpty") return isBlankValue(value);
|
|
2636
3119
|
if (condition.operator === "isNotEmpty") return !isBlankValue(value);
|
|
@@ -2702,7 +3185,7 @@ function useSpreadsheetFiltering({
|
|
|
2702
3185
|
},
|
|
2703
3186
|
[]
|
|
2704
3187
|
);
|
|
2705
|
-
const buildFilterPredicate =
|
|
3188
|
+
const buildFilterPredicate = useCallback4(
|
|
2706
3189
|
(column, filter) => {
|
|
2707
3190
|
return (row) => {
|
|
2708
3191
|
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
@@ -2736,7 +3219,7 @@ function useSpreadsheetFiltering({
|
|
|
2736
3219
|
},
|
|
2737
3220
|
[applyTextCondition, applyNumberCondition, applyDateCondition]
|
|
2738
3221
|
);
|
|
2739
|
-
const buildSortComparator =
|
|
3222
|
+
const buildSortComparator = useCallback4(
|
|
2740
3223
|
(column, direction) => {
|
|
2741
3224
|
return (a, b) => {
|
|
2742
3225
|
const aValue = column?.getValue ? column.getValue(a) : a[sortConfig?.columnId];
|
|
@@ -2764,6 +3247,12 @@ function useSpreadsheetFiltering({
|
|
|
2764
3247
|
const column = columns.find((c) => c.id === columnId);
|
|
2765
3248
|
if (!column) continue;
|
|
2766
3249
|
filterChain.add(buildFilterPredicate(column, filter));
|
|
3250
|
+
if (filter.showDuplicatesOnly && duplicateRowIds && getRowId) {
|
|
3251
|
+
const dupSet = duplicateRowIds.get(columnId);
|
|
3252
|
+
if (dupSet) {
|
|
3253
|
+
filterChain.add((row) => dupSet.has(getRowId(row)));
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
2767
3256
|
}
|
|
2768
3257
|
if (!filterChain.isEmpty) {
|
|
2769
3258
|
lazyResult = filterChain.applyTo(lazyResult);
|
|
@@ -2773,8 +3262,8 @@ function useSpreadsheetFiltering({
|
|
|
2773
3262
|
lazyResult = lazyResult.sort(buildSortComparator(column, sortConfig.direction));
|
|
2774
3263
|
}
|
|
2775
3264
|
return lazyResult;
|
|
2776
|
-
}, [data, filters, sortConfig, columns, serverSide, buildFilterPredicate, buildSortComparator]);
|
|
2777
|
-
const handleFilterChange =
|
|
3265
|
+
}, [data, filters, sortConfig, columns, serverSide, buildFilterPredicate, buildSortComparator, duplicateRowIds, getRowId]);
|
|
3266
|
+
const handleFilterChange = useCallback4(
|
|
2778
3267
|
(columnId, filter) => {
|
|
2779
3268
|
const newFilters = { ...filters };
|
|
2780
3269
|
if (filter) {
|
|
@@ -2789,7 +3278,7 @@ function useSpreadsheetFiltering({
|
|
|
2789
3278
|
},
|
|
2790
3279
|
[filters, onFilterChange, controlledFilters]
|
|
2791
3280
|
);
|
|
2792
|
-
const handleSort =
|
|
3281
|
+
const handleSort = useCallback4(
|
|
2793
3282
|
(columnId) => {
|
|
2794
3283
|
let newSortConfig;
|
|
2795
3284
|
if (sortConfig?.columnId === columnId) {
|
|
@@ -2808,13 +3297,13 @@ function useSpreadsheetFiltering({
|
|
|
2808
3297
|
},
|
|
2809
3298
|
[sortConfig, onSortChange, controlledSortConfig]
|
|
2810
3299
|
);
|
|
2811
|
-
const clearSort =
|
|
3300
|
+
const clearSort = useCallback4(() => {
|
|
2812
3301
|
if (controlledSortConfig === void 0) {
|
|
2813
3302
|
setInternalSortConfig(null);
|
|
2814
3303
|
}
|
|
2815
3304
|
onSortChange?.(null);
|
|
2816
3305
|
}, [onSortChange, controlledSortConfig]);
|
|
2817
|
-
const setSortConfig =
|
|
3306
|
+
const setSortConfig = useCallback4(
|
|
2818
3307
|
(config) => {
|
|
2819
3308
|
if (controlledSortConfig === void 0) {
|
|
2820
3309
|
setInternalSortConfig(config);
|
|
@@ -2823,7 +3312,7 @@ function useSpreadsheetFiltering({
|
|
|
2823
3312
|
},
|
|
2824
3313
|
[onSortChange, controlledSortConfig]
|
|
2825
3314
|
);
|
|
2826
|
-
const clearAllFilters =
|
|
3315
|
+
const clearAllFilters = useCallback4(() => {
|
|
2827
3316
|
if (controlledFilters === void 0) {
|
|
2828
3317
|
setInternalFilters({});
|
|
2829
3318
|
}
|
|
@@ -2845,24 +3334,274 @@ function useSpreadsheetFiltering({
|
|
|
2845
3334
|
};
|
|
2846
3335
|
}
|
|
2847
3336
|
|
|
3337
|
+
// src/hooks/useSpreadsheetDuplicates.ts
|
|
3338
|
+
import { useCallback as useCallback5, useMemo as useMemo4, useState as useState9 } from "react";
|
|
3339
|
+
function normalizeValue(value) {
|
|
3340
|
+
if (value === null || value === void 0 || value === "") {
|
|
3341
|
+
return "__blank__";
|
|
3342
|
+
}
|
|
3343
|
+
if (typeof value === "number") {
|
|
3344
|
+
return String(value);
|
|
3345
|
+
}
|
|
3346
|
+
return String(value).trim().toLowerCase();
|
|
3347
|
+
}
|
|
3348
|
+
function useSpreadsheetDuplicates({
|
|
3349
|
+
data,
|
|
3350
|
+
columns,
|
|
3351
|
+
duplicateCheckColumns,
|
|
3352
|
+
getRowId
|
|
3353
|
+
}) {
|
|
3354
|
+
const [localDuplicateCheckColumns, setLocalDuplicateCheckColumns] = useState9(
|
|
3355
|
+
() => new Set(duplicateCheckColumns)
|
|
3356
|
+
);
|
|
3357
|
+
const duplicateCheckColumnsSet = useMemo4(() => {
|
|
3358
|
+
return new Set(duplicateCheckColumns);
|
|
3359
|
+
}, [duplicateCheckColumns]);
|
|
3360
|
+
const { duplicateRowIds, valueCounts } = useMemo4(() => {
|
|
3361
|
+
const duplicateRowIds2 = /* @__PURE__ */ new Map();
|
|
3362
|
+
const valueCounts2 = /* @__PURE__ */ new Map();
|
|
3363
|
+
const activeColumns = duplicateCheckColumnsSet.size > 0 ? duplicateCheckColumnsSet : localDuplicateCheckColumns;
|
|
3364
|
+
for (const columnId of activeColumns) {
|
|
3365
|
+
const column = columns.find((c) => c.id === columnId);
|
|
3366
|
+
if (!column) continue;
|
|
3367
|
+
const valueToRowIds = /* @__PURE__ */ new Map();
|
|
3368
|
+
for (const row of data) {
|
|
3369
|
+
const rawValue = column.getValue ? column.getValue(row) : row[columnId];
|
|
3370
|
+
if (rawValue === null || rawValue === void 0 || rawValue === "") continue;
|
|
3371
|
+
const normalized = normalizeValue(rawValue);
|
|
3372
|
+
const rowId = getRowId(row);
|
|
3373
|
+
const existing = valueToRowIds.get(normalized);
|
|
3374
|
+
if (existing) {
|
|
3375
|
+
existing.push(rowId);
|
|
3376
|
+
} else {
|
|
3377
|
+
valueToRowIds.set(normalized, [rowId]);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
const dupSet = /* @__PURE__ */ new Set();
|
|
3381
|
+
const countMap = /* @__PURE__ */ new Map();
|
|
3382
|
+
for (const [normalizedVal, rowIds] of valueToRowIds) {
|
|
3383
|
+
countMap.set(normalizedVal, rowIds.length);
|
|
3384
|
+
if (rowIds.length >= 2) {
|
|
3385
|
+
for (const rowId of rowIds) {
|
|
3386
|
+
dupSet.add(rowId);
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
duplicateRowIds2.set(columnId, dupSet);
|
|
3391
|
+
valueCounts2.set(columnId, countMap);
|
|
3392
|
+
}
|
|
3393
|
+
return { duplicateRowIds: duplicateRowIds2, valueCounts: valueCounts2 };
|
|
3394
|
+
}, [data, columns, duplicateCheckColumnsSet, localDuplicateCheckColumns, getRowId]);
|
|
3395
|
+
const isCellDuplicate = useCallback5(
|
|
3396
|
+
(rowId, columnId) => {
|
|
3397
|
+
return duplicateRowIds.get(columnId)?.has(rowId) ?? false;
|
|
3398
|
+
},
|
|
3399
|
+
[duplicateRowIds]
|
|
3400
|
+
);
|
|
3401
|
+
const getDuplicateCount = useCallback5(
|
|
3402
|
+
(columnId, value) => {
|
|
3403
|
+
const normalized = normalizeValue(value);
|
|
3404
|
+
return valueCounts.get(columnId)?.get(normalized) ?? 0;
|
|
3405
|
+
},
|
|
3406
|
+
[valueCounts]
|
|
3407
|
+
);
|
|
3408
|
+
const getDuplicateColumnCount = useCallback5(
|
|
3409
|
+
(columnId) => {
|
|
3410
|
+
return duplicateRowIds.get(columnId)?.size ?? 0;
|
|
3411
|
+
},
|
|
3412
|
+
[duplicateRowIds]
|
|
3413
|
+
);
|
|
3414
|
+
const toggleDuplicateCheck = useCallback5((columnId) => {
|
|
3415
|
+
setLocalDuplicateCheckColumns((prev) => {
|
|
3416
|
+
const next = new Set(prev);
|
|
3417
|
+
if (next.has(columnId)) {
|
|
3418
|
+
next.delete(columnId);
|
|
3419
|
+
} else {
|
|
3420
|
+
next.add(columnId);
|
|
3421
|
+
}
|
|
3422
|
+
return next;
|
|
3423
|
+
});
|
|
3424
|
+
}, []);
|
|
3425
|
+
const effectiveDuplicateCheckColumns = duplicateCheckColumnsSet.size > 0 ? duplicateCheckColumnsSet : localDuplicateCheckColumns;
|
|
3426
|
+
return {
|
|
3427
|
+
isCellDuplicate,
|
|
3428
|
+
getDuplicateCount,
|
|
3429
|
+
getDuplicateColumnCount,
|
|
3430
|
+
duplicateRowIds,
|
|
3431
|
+
duplicateCheckColumns: effectiveDuplicateCheckColumns,
|
|
3432
|
+
toggleDuplicateCheck
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
// src/hooks/useSpreadsheetHighlighting.ts
|
|
3437
|
+
import { useCallback as useCallback6, useState as useState10 } from "react";
|
|
3438
|
+
function useSpreadsheetHighlighting({
|
|
3439
|
+
externalRowHighlights,
|
|
3440
|
+
onRowHighlight,
|
|
3441
|
+
externalColumnHighlights,
|
|
3442
|
+
onColumnHighlight,
|
|
3443
|
+
externalCellHighlights,
|
|
3444
|
+
onCellHighlight
|
|
3445
|
+
} = {}) {
|
|
3446
|
+
const [cellHighlightsInternal, setCellHighlightsInternal] = useState10([]);
|
|
3447
|
+
const [rowHighlightsInternal, setRowHighlightsInternal] = useState10([]);
|
|
3448
|
+
const [columnHighlightsInternal, setColumnHighlightsInternal] = useState10({});
|
|
3449
|
+
const [recentColors, setRecentColors] = useState10([]);
|
|
3450
|
+
const addRecentColor = useCallback6((color) => {
|
|
3451
|
+
if (!color) return;
|
|
3452
|
+
setRecentColors((prev) => {
|
|
3453
|
+
const filtered = prev.filter((c) => c !== color);
|
|
3454
|
+
return [color, ...filtered].slice(0, 8);
|
|
3455
|
+
});
|
|
3456
|
+
}, []);
|
|
3457
|
+
const [highlightPickerRow, setHighlightPickerRow] = useState10(null);
|
|
3458
|
+
const [highlightPickerColumn, setHighlightPickerColumn] = useState10(null);
|
|
3459
|
+
const [highlightPickerCell, setHighlightPickerCell] = useState10(null);
|
|
3460
|
+
const cellHighlights = externalCellHighlights || cellHighlightsInternal;
|
|
3461
|
+
const rowHighlights = externalRowHighlights || rowHighlightsInternal;
|
|
3462
|
+
const columnHighlights = externalColumnHighlights || columnHighlightsInternal;
|
|
3463
|
+
const getCellHighlight = useCallback6(
|
|
3464
|
+
(rowId, columnId) => {
|
|
3465
|
+
return cellHighlights.find((h) => h.rowId === rowId && h.columnId === columnId)?.color;
|
|
3466
|
+
},
|
|
3467
|
+
[cellHighlights]
|
|
3468
|
+
);
|
|
3469
|
+
const handleCellHighlightToggle = useCallback6(
|
|
3470
|
+
(rowId, columnId, color = "#fef08a") => {
|
|
3471
|
+
if (onCellHighlight) {
|
|
3472
|
+
onCellHighlight(rowId, columnId, color);
|
|
3473
|
+
} else {
|
|
3474
|
+
setCellHighlightsInternal((prev) => {
|
|
3475
|
+
const existing = prev.find((h) => h.rowId === rowId && h.columnId === columnId);
|
|
3476
|
+
if (existing) {
|
|
3477
|
+
if (color === null) {
|
|
3478
|
+
return prev.filter(
|
|
3479
|
+
(h) => !(h.rowId === rowId && h.columnId === columnId)
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
return prev.map(
|
|
3483
|
+
(h) => h.rowId === rowId && h.columnId === columnId ? { ...h, color } : h
|
|
3484
|
+
);
|
|
3485
|
+
}
|
|
3486
|
+
if (color) {
|
|
3487
|
+
return [...prev, { rowId, columnId, color }];
|
|
3488
|
+
}
|
|
3489
|
+
return prev;
|
|
3490
|
+
});
|
|
3491
|
+
}
|
|
3492
|
+
addRecentColor(color);
|
|
3493
|
+
setHighlightPickerCell(null);
|
|
3494
|
+
},
|
|
3495
|
+
[onCellHighlight, addRecentColor]
|
|
3496
|
+
);
|
|
3497
|
+
const getRowHighlight = useCallback6(
|
|
3498
|
+
(rowId) => {
|
|
3499
|
+
return rowHighlights.find((h) => h.rowId === rowId && !h.columnId);
|
|
3500
|
+
},
|
|
3501
|
+
[rowHighlights]
|
|
3502
|
+
);
|
|
3503
|
+
const handleRowHighlightToggle = useCallback6(
|
|
3504
|
+
(rowId, color) => {
|
|
3505
|
+
if (onRowHighlight) {
|
|
3506
|
+
onRowHighlight(rowId, color);
|
|
3507
|
+
} else {
|
|
3508
|
+
setRowHighlightsInternal((prev) => {
|
|
3509
|
+
const existing = prev.find((h) => h.rowId === rowId && !h.columnId);
|
|
3510
|
+
if (existing) {
|
|
3511
|
+
if (color === null) {
|
|
3512
|
+
return prev.filter((h) => !(h.rowId === rowId && !h.columnId));
|
|
3513
|
+
}
|
|
3514
|
+
return prev.map(
|
|
3515
|
+
(h) => h.rowId === rowId && !h.columnId ? { ...h, color } : h
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
if (color) {
|
|
3519
|
+
return [...prev, { rowId, color }];
|
|
3520
|
+
}
|
|
3521
|
+
return prev;
|
|
3522
|
+
});
|
|
3523
|
+
}
|
|
3524
|
+
addRecentColor(color);
|
|
3525
|
+
setHighlightPickerRow(null);
|
|
3526
|
+
},
|
|
3527
|
+
[onRowHighlight, addRecentColor]
|
|
3528
|
+
);
|
|
3529
|
+
const getColumnHighlight = useCallback6(
|
|
3530
|
+
(columnId) => {
|
|
3531
|
+
return columnHighlights[columnId];
|
|
3532
|
+
},
|
|
3533
|
+
[columnHighlights]
|
|
3534
|
+
);
|
|
3535
|
+
const handleColumnHighlightToggle = useCallback6(
|
|
3536
|
+
(columnId, color) => {
|
|
3537
|
+
if (onColumnHighlight) {
|
|
3538
|
+
onColumnHighlight(columnId, color);
|
|
3539
|
+
} else {
|
|
3540
|
+
setColumnHighlightsInternal((prev) => {
|
|
3541
|
+
const newHighlights = { ...prev };
|
|
3542
|
+
if (color === null) {
|
|
3543
|
+
delete newHighlights[columnId];
|
|
3544
|
+
} else {
|
|
3545
|
+
newHighlights[columnId] = color;
|
|
3546
|
+
}
|
|
3547
|
+
return newHighlights;
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
addRecentColor(color);
|
|
3551
|
+
setHighlightPickerColumn(null);
|
|
3552
|
+
},
|
|
3553
|
+
[onColumnHighlight, addRecentColor]
|
|
3554
|
+
);
|
|
3555
|
+
const clearAllHighlights = useCallback6(() => {
|
|
3556
|
+
setCellHighlightsInternal([]);
|
|
3557
|
+
setRowHighlightsInternal([]);
|
|
3558
|
+
setColumnHighlightsInternal({});
|
|
3559
|
+
}, []);
|
|
3560
|
+
return {
|
|
3561
|
+
// Cell highlights
|
|
3562
|
+
cellHighlights,
|
|
3563
|
+
getCellHighlight,
|
|
3564
|
+
handleCellHighlightToggle,
|
|
3565
|
+
// Row highlights
|
|
3566
|
+
rowHighlights,
|
|
3567
|
+
getRowHighlight,
|
|
3568
|
+
handleRowHighlightToggle,
|
|
3569
|
+
// Column highlights
|
|
3570
|
+
columnHighlights,
|
|
3571
|
+
getColumnHighlight,
|
|
3572
|
+
handleColumnHighlightToggle,
|
|
3573
|
+
// Recent colors
|
|
3574
|
+
recentColors,
|
|
3575
|
+
// Picker state
|
|
3576
|
+
highlightPickerRow,
|
|
3577
|
+
setHighlightPickerRow,
|
|
3578
|
+
highlightPickerColumn,
|
|
3579
|
+
setHighlightPickerColumn,
|
|
3580
|
+
highlightPickerCell,
|
|
3581
|
+
setHighlightPickerCell,
|
|
3582
|
+
// Utility
|
|
3583
|
+
clearAllHighlights
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
|
|
2848
3587
|
// src/hooks/useSpreadsheetComments.ts
|
|
2849
|
-
import { useCallback as
|
|
3588
|
+
import { useCallback as useCallback7, useState as useState11 } from "react";
|
|
2850
3589
|
function useSpreadsheetComments({
|
|
2851
3590
|
externalCellComments,
|
|
2852
3591
|
onAddCellComment,
|
|
2853
3592
|
onToggleCommentResolved
|
|
2854
3593
|
} = {}) {
|
|
2855
|
-
const [cellCommentsInternal, setCellCommentsInternal] =
|
|
2856
|
-
const [commentModalCell, setCommentModalCell] =
|
|
2857
|
-
const [viewCommentsCell, setViewCommentsCell] =
|
|
3594
|
+
const [cellCommentsInternal, setCellCommentsInternal] = useState11([]);
|
|
3595
|
+
const [commentModalCell, setCommentModalCell] = useState11(null);
|
|
3596
|
+
const [viewCommentsCell, setViewCommentsCell] = useState11(null);
|
|
2858
3597
|
const cellComments = externalCellComments || cellCommentsInternal;
|
|
2859
|
-
const getCellComments =
|
|
3598
|
+
const getCellComments = useCallback7(
|
|
2860
3599
|
(rowId, columnId) => {
|
|
2861
3600
|
return cellComments.filter((c) => c.rowId === rowId && c.columnId === columnId);
|
|
2862
3601
|
},
|
|
2863
3602
|
[cellComments]
|
|
2864
3603
|
);
|
|
2865
|
-
const getCellUnresolvedCommentCount =
|
|
3604
|
+
const getCellUnresolvedCommentCount = useCallback7(
|
|
2866
3605
|
(rowId, columnId) => {
|
|
2867
3606
|
return cellComments.filter(
|
|
2868
3607
|
(c) => c.rowId === rowId && c.columnId === columnId && !c.resolved
|
|
@@ -2870,13 +3609,13 @@ function useSpreadsheetComments({
|
|
|
2870
3609
|
},
|
|
2871
3610
|
[cellComments]
|
|
2872
3611
|
);
|
|
2873
|
-
const cellHasComments =
|
|
3612
|
+
const cellHasComments = useCallback7(
|
|
2874
3613
|
(rowId, columnId) => {
|
|
2875
3614
|
return cellComments.some((c) => c.rowId === rowId && c.columnId === columnId);
|
|
2876
3615
|
},
|
|
2877
3616
|
[cellComments]
|
|
2878
3617
|
);
|
|
2879
|
-
const handleAddCellComment =
|
|
3618
|
+
const handleAddCellComment = useCallback7(
|
|
2880
3619
|
(rowId, columnId, text) => {
|
|
2881
3620
|
if (!text.trim()) return;
|
|
2882
3621
|
if (onAddCellComment) {
|
|
@@ -2898,7 +3637,7 @@ function useSpreadsheetComments({
|
|
|
2898
3637
|
},
|
|
2899
3638
|
[onAddCellComment]
|
|
2900
3639
|
);
|
|
2901
|
-
const handleToggleCommentResolved =
|
|
3640
|
+
const handleToggleCommentResolved = useCallback7(
|
|
2902
3641
|
(commentId) => {
|
|
2903
3642
|
if (onToggleCommentResolved) {
|
|
2904
3643
|
onToggleCommentResolved(commentId);
|
|
@@ -2930,20 +3669,20 @@ function useSpreadsheetComments({
|
|
|
2930
3669
|
}
|
|
2931
3670
|
|
|
2932
3671
|
// src/hooks/useSpreadsheetUndoRedo.ts
|
|
2933
|
-
import { useCallback as
|
|
3672
|
+
import { useCallback as useCallback8, useRef as useRef7, useState as useState12 } from "react";
|
|
2934
3673
|
function useSpreadsheetUndoRedo({
|
|
2935
3674
|
enabled = true,
|
|
2936
3675
|
maxStackSize = 50,
|
|
2937
3676
|
autoSave = true,
|
|
2938
3677
|
onSave
|
|
2939
3678
|
}) {
|
|
2940
|
-
const [undoStack, setUndoStack] =
|
|
2941
|
-
const [redoStack, setRedoStack] =
|
|
2942
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] =
|
|
2943
|
-
const [saveStatus, setSaveStatus] =
|
|
2944
|
-
const onSaveRef =
|
|
3679
|
+
const [undoStack, setUndoStack] = useState12([]);
|
|
3680
|
+
const [redoStack, setRedoStack] = useState12([]);
|
|
3681
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState12(false);
|
|
3682
|
+
const [saveStatus, setSaveStatus] = useState12("saved");
|
|
3683
|
+
const onSaveRef = useRef7(onSave);
|
|
2945
3684
|
onSaveRef.current = onSave;
|
|
2946
|
-
const pushToUndoStack =
|
|
3685
|
+
const pushToUndoStack = useCallback8(
|
|
2947
3686
|
(snapshot) => {
|
|
2948
3687
|
if (!enabled) return;
|
|
2949
3688
|
setUndoStack((prev) => {
|
|
@@ -2957,7 +3696,7 @@ function useSpreadsheetUndoRedo({
|
|
|
2957
3696
|
},
|
|
2958
3697
|
[enabled, maxStackSize]
|
|
2959
3698
|
);
|
|
2960
|
-
const handleUndo =
|
|
3699
|
+
const handleUndo = useCallback8(() => {
|
|
2961
3700
|
if (!enabled || undoStack.length === 0) return null;
|
|
2962
3701
|
const previousSnapshot = undoStack[undoStack.length - 1];
|
|
2963
3702
|
setUndoStack((prev) => prev.slice(0, -1));
|
|
@@ -2970,7 +3709,7 @@ function useSpreadsheetUndoRedo({
|
|
|
2970
3709
|
});
|
|
2971
3710
|
return previousSnapshot;
|
|
2972
3711
|
}, [enabled, undoStack, maxStackSize]);
|
|
2973
|
-
const handleRedo =
|
|
3712
|
+
const handleRedo = useCallback8(() => {
|
|
2974
3713
|
if (!enabled || redoStack.length === 0) return null;
|
|
2975
3714
|
const nextSnapshot = redoStack[redoStack.length - 1];
|
|
2976
3715
|
setRedoStack((prev) => prev.slice(0, -1));
|
|
@@ -2983,7 +3722,7 @@ function useSpreadsheetUndoRedo({
|
|
|
2983
3722
|
});
|
|
2984
3723
|
return nextSnapshot;
|
|
2985
3724
|
}, [enabled, redoStack, maxStackSize]);
|
|
2986
|
-
const handleSave =
|
|
3725
|
+
const handleSave = useCallback8(async () => {
|
|
2987
3726
|
if (!hasUnsavedChanges && !autoSave) return;
|
|
2988
3727
|
setSaveStatus("saving");
|
|
2989
3728
|
try {
|
|
@@ -2996,7 +3735,7 @@ function useSpreadsheetUndoRedo({
|
|
|
2996
3735
|
setSaveStatus("error");
|
|
2997
3736
|
}
|
|
2998
3737
|
}, [hasUnsavedChanges, autoSave]);
|
|
2999
|
-
const markAsChanged =
|
|
3738
|
+
const markAsChanged = useCallback8(() => {
|
|
3000
3739
|
setHasUnsavedChanges(true);
|
|
3001
3740
|
if (autoSave) {
|
|
3002
3741
|
setSaveStatus("saving");
|
|
@@ -3017,11 +3756,11 @@ function useSpreadsheetUndoRedo({
|
|
|
3017
3756
|
setSaveStatus("unsaved");
|
|
3018
3757
|
}
|
|
3019
3758
|
}, [autoSave]);
|
|
3020
|
-
const markAsSaved =
|
|
3759
|
+
const markAsSaved = useCallback8(() => {
|
|
3021
3760
|
setHasUnsavedChanges(false);
|
|
3022
3761
|
setSaveStatus("saved");
|
|
3023
3762
|
}, []);
|
|
3024
|
-
const clearStacks =
|
|
3763
|
+
const clearStacks = useCallback8(() => {
|
|
3025
3764
|
setUndoStack([]);
|
|
3026
3765
|
setRedoStack([]);
|
|
3027
3766
|
}, []);
|
|
@@ -3049,7 +3788,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3049
3788
|
}
|
|
3050
3789
|
|
|
3051
3790
|
// src/hooks/useSpreadsheetKeyboardShortcuts.ts
|
|
3052
|
-
import { useEffect as
|
|
3791
|
+
import { useEffect as useEffect7, useState as useState13 } from "react";
|
|
3053
3792
|
function useSpreadsheetKeyboardShortcuts({
|
|
3054
3793
|
onUndo,
|
|
3055
3794
|
onRedo,
|
|
@@ -3059,15 +3798,16 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3059
3798
|
onTabNavigation,
|
|
3060
3799
|
onCopy,
|
|
3061
3800
|
onPaste,
|
|
3801
|
+
onSelectAll,
|
|
3062
3802
|
hasFocusedCell = false,
|
|
3063
3803
|
isEditing = false,
|
|
3064
3804
|
customShortcuts = [],
|
|
3065
3805
|
enabled = true
|
|
3066
3806
|
} = {}) {
|
|
3067
|
-
const [showKeyboardShortcuts, setShowKeyboardShortcuts] =
|
|
3807
|
+
const [showKeyboardShortcuts, setShowKeyboardShortcuts] = useState13(false);
|
|
3068
3808
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPod|iPad/.test(navigator.platform);
|
|
3069
3809
|
const modifierKey = isMac ? "\u2318" : "Ctrl";
|
|
3070
|
-
|
|
3810
|
+
useEffect7(() => {
|
|
3071
3811
|
if (!enabled) return;
|
|
3072
3812
|
const handleKeyDown = (event) => {
|
|
3073
3813
|
if (event.key === "Escape") {
|
|
@@ -3093,6 +3833,11 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3093
3833
|
onRedo?.();
|
|
3094
3834
|
return;
|
|
3095
3835
|
}
|
|
3836
|
+
if ((event.metaKey || event.ctrlKey) && (event.key === "a" || event.key === "A") && !isEditing) {
|
|
3837
|
+
event.preventDefault();
|
|
3838
|
+
onSelectAll?.();
|
|
3839
|
+
return;
|
|
3840
|
+
}
|
|
3096
3841
|
if ((event.metaKey || event.ctrlKey) && event.key === "c" && !isEditing && hasFocusedCell) {
|
|
3097
3842
|
event.preventDefault();
|
|
3098
3843
|
onCopy?.();
|
|
@@ -3157,6 +3902,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3157
3902
|
onTabNavigation,
|
|
3158
3903
|
onCopy,
|
|
3159
3904
|
onPaste,
|
|
3905
|
+
onSelectAll,
|
|
3160
3906
|
hasFocusedCell,
|
|
3161
3907
|
isEditing,
|
|
3162
3908
|
customShortcuts
|
|
@@ -3174,6 +3920,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3174
3920
|
editing: [
|
|
3175
3921
|
{ label: "Undo", keys: [modifierKey, "Z"] },
|
|
3176
3922
|
{ label: "Redo", keys: [modifierKey, "Shift", "Z"] },
|
|
3923
|
+
{ label: "Select all", keys: [modifierKey, "A"] },
|
|
3177
3924
|
{ label: "Copy cells", keys: [modifierKey, "C"] },
|
|
3178
3925
|
{ label: "Paste cells", keys: [modifierKey, "V"] },
|
|
3179
3926
|
{ label: "Confirm cell edit", keys: ["Enter"] },
|
|
@@ -3208,7 +3955,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3208
3955
|
}
|
|
3209
3956
|
|
|
3210
3957
|
// src/hooks/useSpreadsheetSelection.ts
|
|
3211
|
-
import { useCallback as
|
|
3958
|
+
import { useCallback as useCallback9, useState as useState14, useRef as useRef8, useMemo as useMemo5 } from "react";
|
|
3212
3959
|
function useSpreadsheetSelection({
|
|
3213
3960
|
data,
|
|
3214
3961
|
columns,
|
|
@@ -3216,34 +3963,34 @@ function useSpreadsheetSelection({
|
|
|
3216
3963
|
onCellRangeSelectionChange,
|
|
3217
3964
|
enableCellEditing = true
|
|
3218
3965
|
}) {
|
|
3219
|
-
const [focusedCell, setFocusedCell] =
|
|
3220
|
-
const [editingCell, setEditingCell] =
|
|
3221
|
-
const [selectedCellRange, setSelectedCellRangeState] =
|
|
3222
|
-
const [isDragging, setIsDragging] =
|
|
3223
|
-
const [clipboardData, setClipboardData] =
|
|
3224
|
-
const anchorCell =
|
|
3225
|
-
const rowIndexMap =
|
|
3966
|
+
const [focusedCell, setFocusedCell] = useState14(null);
|
|
3967
|
+
const [editingCell, setEditingCell] = useState14(null);
|
|
3968
|
+
const [selectedCellRange, setSelectedCellRangeState] = useState14(null);
|
|
3969
|
+
const [isDragging, setIsDragging] = useState14(false);
|
|
3970
|
+
const [clipboardData, setClipboardData] = useState14(null);
|
|
3971
|
+
const anchorCell = useRef8(null);
|
|
3972
|
+
const rowIndexMap = useMemo5(() => {
|
|
3226
3973
|
const map = /* @__PURE__ */ new Map();
|
|
3227
3974
|
data.forEach((row, index) => {
|
|
3228
3975
|
map.set(getRowId(row), index);
|
|
3229
3976
|
});
|
|
3230
3977
|
return map;
|
|
3231
3978
|
}, [data, getRowId]);
|
|
3232
|
-
const columnIndexMap =
|
|
3979
|
+
const columnIndexMap = useMemo5(() => {
|
|
3233
3980
|
const map = /* @__PURE__ */ new Map();
|
|
3234
3981
|
columns.forEach((col, index) => {
|
|
3235
3982
|
map.set(col.id, index);
|
|
3236
3983
|
});
|
|
3237
3984
|
return map;
|
|
3238
3985
|
}, [columns]);
|
|
3239
|
-
const setSelectedCellRange =
|
|
3986
|
+
const setSelectedCellRange = useCallback9(
|
|
3240
3987
|
(range) => {
|
|
3241
3988
|
setSelectedCellRangeState(range);
|
|
3242
3989
|
onCellRangeSelectionChange?.(range);
|
|
3243
3990
|
},
|
|
3244
3991
|
[onCellRangeSelectionChange]
|
|
3245
3992
|
);
|
|
3246
|
-
const getNormalizedRange =
|
|
3993
|
+
const getNormalizedRange = useCallback9(
|
|
3247
3994
|
(range) => {
|
|
3248
3995
|
if (!range) return null;
|
|
3249
3996
|
const startRowIdx = rowIndexMap.get(range.start.rowId);
|
|
@@ -3262,7 +4009,7 @@ function useSpreadsheetSelection({
|
|
|
3262
4009
|
},
|
|
3263
4010
|
[rowIndexMap, columnIndexMap]
|
|
3264
4011
|
);
|
|
3265
|
-
const isCellInSelection =
|
|
4012
|
+
const isCellInSelection = useCallback9(
|
|
3266
4013
|
(rowId, columnId) => {
|
|
3267
4014
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3268
4015
|
if (!normalizedRange) return false;
|
|
@@ -3273,7 +4020,7 @@ function useSpreadsheetSelection({
|
|
|
3273
4020
|
},
|
|
3274
4021
|
[selectedCellRange, rowIndexMap, columnIndexMap, getNormalizedRange]
|
|
3275
4022
|
);
|
|
3276
|
-
const getCellSelectionEdge =
|
|
4023
|
+
const getCellSelectionEdge = useCallback9(
|
|
3277
4024
|
(rowId, columnId) => {
|
|
3278
4025
|
if (!isCellInSelection(rowId, columnId)) return void 0;
|
|
3279
4026
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
@@ -3290,7 +4037,7 @@ function useSpreadsheetSelection({
|
|
|
3290
4037
|
},
|
|
3291
4038
|
[isCellInSelection, selectedCellRange, rowIndexMap, columnIndexMap, getNormalizedRange]
|
|
3292
4039
|
);
|
|
3293
|
-
const getSelectedCells =
|
|
4040
|
+
const getSelectedCells = useCallback9(() => {
|
|
3294
4041
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3295
4042
|
if (!normalizedRange) {
|
|
3296
4043
|
return focusedCell ? [focusedCell] : [];
|
|
@@ -3308,7 +4055,7 @@ function useSpreadsheetSelection({
|
|
|
3308
4055
|
}
|
|
3309
4056
|
return cells;
|
|
3310
4057
|
}, [selectedCellRange, focusedCell, data, columns, getRowId, getNormalizedRange]);
|
|
3311
|
-
const getSelectedCellValues =
|
|
4058
|
+
const getSelectedCellValues = useCallback9(() => {
|
|
3312
4059
|
const cells = getSelectedCells();
|
|
3313
4060
|
return cells.map((cell) => {
|
|
3314
4061
|
const row = data.find((r) => getRowId(r) === cell.rowId);
|
|
@@ -3317,7 +4064,7 @@ function useSpreadsheetSelection({
|
|
|
3317
4064
|
return { position: cell, value };
|
|
3318
4065
|
});
|
|
3319
4066
|
}, [getSelectedCells, data, columns, getRowId]);
|
|
3320
|
-
const handleCellClick =
|
|
4067
|
+
const handleCellClick = useCallback9(
|
|
3321
4068
|
(rowId, columnId, event) => {
|
|
3322
4069
|
event.stopPropagation();
|
|
3323
4070
|
const newCell = { rowId, columnId };
|
|
@@ -3331,15 +4078,12 @@ function useSpreadsheetSelection({
|
|
|
3331
4078
|
anchorCell.current = newCell;
|
|
3332
4079
|
setFocusedCell(newCell);
|
|
3333
4080
|
setSelectedCellRange(null);
|
|
3334
|
-
|
|
3335
|
-
if (column?.editable && enableCellEditing) {
|
|
3336
|
-
setEditingCell(newCell);
|
|
3337
|
-
}
|
|
4081
|
+
setEditingCell(null);
|
|
3338
4082
|
}
|
|
3339
4083
|
},
|
|
3340
|
-
[
|
|
4084
|
+
[setSelectedCellRange]
|
|
3341
4085
|
);
|
|
3342
|
-
const handleCellMouseDown =
|
|
4086
|
+
const handleCellMouseDown = useCallback9(
|
|
3343
4087
|
(rowId, columnId, event) => {
|
|
3344
4088
|
if (event.button !== 0) return;
|
|
3345
4089
|
const newCell = { rowId, columnId };
|
|
@@ -3355,28 +4099,23 @@ function useSpreadsheetSelection({
|
|
|
3355
4099
|
anchorCell.current = newCell;
|
|
3356
4100
|
setFocusedCell(newCell);
|
|
3357
4101
|
setSelectedCellRange(null);
|
|
3358
|
-
|
|
3359
|
-
if (column?.editable && enableCellEditing) {
|
|
3360
|
-
setEditingCell(newCell);
|
|
3361
|
-
} else {
|
|
3362
|
-
setEditingCell(null);
|
|
3363
|
-
}
|
|
4102
|
+
setEditingCell(null);
|
|
3364
4103
|
}
|
|
3365
4104
|
},
|
|
3366
|
-
[
|
|
4105
|
+
[setSelectedCellRange]
|
|
3367
4106
|
);
|
|
3368
|
-
const handleCellMouseEnter =
|
|
4107
|
+
const handleCellMouseEnter = useCallback9((_rowId, _columnId) => {
|
|
3369
4108
|
}, []);
|
|
3370
|
-
const handleMouseUp =
|
|
4109
|
+
const handleMouseUp = useCallback9(() => {
|
|
3371
4110
|
setIsDragging(false);
|
|
3372
4111
|
}, []);
|
|
3373
|
-
const clearSelection =
|
|
4112
|
+
const clearSelection = useCallback9(() => {
|
|
3374
4113
|
setFocusedCell(null);
|
|
3375
4114
|
setEditingCell(null);
|
|
3376
4115
|
setSelectedCellRange(null);
|
|
3377
4116
|
anchorCell.current = null;
|
|
3378
4117
|
}, [setSelectedCellRange]);
|
|
3379
|
-
const navigateCell =
|
|
4118
|
+
const navigateCell = useCallback9(
|
|
3380
4119
|
(direction, extendSelection = false) => {
|
|
3381
4120
|
const currentCell = focusedCell;
|
|
3382
4121
|
if (!currentCell) return;
|
|
@@ -3423,7 +4162,7 @@ function useSpreadsheetSelection({
|
|
|
3423
4162
|
},
|
|
3424
4163
|
[focusedCell, data, columns, getRowId, rowIndexMap, columnIndexMap, setSelectedCellRange]
|
|
3425
4164
|
);
|
|
3426
|
-
const handleTabNavigation =
|
|
4165
|
+
const handleTabNavigation = useCallback9(
|
|
3427
4166
|
(shiftKey) => {
|
|
3428
4167
|
const currentCell = focusedCell;
|
|
3429
4168
|
if (!currentCell) return;
|
|
@@ -3464,17 +4203,17 @@ function useSpreadsheetSelection({
|
|
|
3464
4203
|
},
|
|
3465
4204
|
[focusedCell, data, columns, getRowId, rowIndexMap, columnIndexMap, setSelectedCellRange]
|
|
3466
4205
|
);
|
|
3467
|
-
const enterEditMode =
|
|
4206
|
+
const enterEditMode = useCallback9(() => {
|
|
3468
4207
|
if (!focusedCell || !enableCellEditing) return;
|
|
3469
4208
|
const column = columns.find((c) => c.id === focusedCell.columnId);
|
|
3470
4209
|
if (column?.editable) {
|
|
3471
4210
|
setEditingCell(focusedCell);
|
|
3472
4211
|
}
|
|
3473
4212
|
}, [focusedCell, columns, enableCellEditing]);
|
|
3474
|
-
const exitEditMode =
|
|
4213
|
+
const exitEditMode = useCallback9(() => {
|
|
3475
4214
|
setEditingCell(null);
|
|
3476
4215
|
}, []);
|
|
3477
|
-
const copySelectedCells =
|
|
4216
|
+
const copySelectedCells = useCallback9(() => {
|
|
3478
4217
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3479
4218
|
if (!normalizedRange && !focusedCell) return;
|
|
3480
4219
|
const startRowIdx = normalizedRange?.startRowIdx ?? rowIndexMap.get(focusedCell.rowId) ?? 0;
|
|
@@ -3523,14 +4262,14 @@ function useSpreadsheetSelection({
|
|
|
3523
4262
|
rowIndexMap,
|
|
3524
4263
|
columnIndexMap
|
|
3525
4264
|
]);
|
|
3526
|
-
const parseClipboardText =
|
|
4265
|
+
const parseClipboardText = useCallback9((text) => {
|
|
3527
4266
|
const lines = text.split(/\r?\n/);
|
|
3528
4267
|
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
3529
4268
|
lines.pop();
|
|
3530
4269
|
}
|
|
3531
4270
|
return lines.map((line) => line.split(" "));
|
|
3532
4271
|
}, []);
|
|
3533
|
-
const createEditsFromValues =
|
|
4272
|
+
const createEditsFromValues = useCallback9(
|
|
3534
4273
|
(values) => {
|
|
3535
4274
|
if (!focusedCell) return [];
|
|
3536
4275
|
const edits = [];
|
|
@@ -3590,11 +4329,11 @@ function useSpreadsheetSelection({
|
|
|
3590
4329
|
getNormalizedRange
|
|
3591
4330
|
]
|
|
3592
4331
|
);
|
|
3593
|
-
const pasteClipboard =
|
|
4332
|
+
const pasteClipboard = useCallback9(() => {
|
|
3594
4333
|
if (!clipboardData?.values) return [];
|
|
3595
4334
|
return createEditsFromValues(clipboardData.values);
|
|
3596
4335
|
}, [clipboardData, createEditsFromValues]);
|
|
3597
|
-
const pasteFromSystemClipboard =
|
|
4336
|
+
const pasteFromSystemClipboard = useCallback9(async () => {
|
|
3598
4337
|
if (!focusedCell) return [];
|
|
3599
4338
|
try {
|
|
3600
4339
|
const text = await navigator.clipboard.readText();
|
|
@@ -3635,8 +4374,193 @@ function useSpreadsheetSelection({
|
|
|
3635
4374
|
};
|
|
3636
4375
|
}
|
|
3637
4376
|
|
|
4377
|
+
// src/hooks/useSpreadsheetSummary.ts
|
|
4378
|
+
import { useMemo as useMemo6 } from "react";
|
|
4379
|
+
function useSpreadsheetSummary({
|
|
4380
|
+
selectedCellValues,
|
|
4381
|
+
columns
|
|
4382
|
+
}) {
|
|
4383
|
+
const summary = useMemo6(() => {
|
|
4384
|
+
if (selectedCellValues.length === 0) return null;
|
|
4385
|
+
const numericValues = [];
|
|
4386
|
+
for (const { position, value } of selectedCellValues) {
|
|
4387
|
+
const column = columns.find((c) => c.id === position.columnId);
|
|
4388
|
+
if (column?.type === "number" || typeof value === "number") {
|
|
4389
|
+
const num = typeof value === "number" ? value : parseFloat(value);
|
|
4390
|
+
if (!isNaN(num)) {
|
|
4391
|
+
numericValues.push(num);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
if (numericValues.length === 0) return null;
|
|
4396
|
+
const sum = numericValues.reduce((a, b) => a + b, 0);
|
|
4397
|
+
return {
|
|
4398
|
+
sum: Math.round(sum * 100) / 100,
|
|
4399
|
+
avg: Math.round(sum / numericValues.length * 100) / 100,
|
|
4400
|
+
count: selectedCellValues.length,
|
|
4401
|
+
numericCount: numericValues.length,
|
|
4402
|
+
min: Math.round(Math.min(...numericValues) * 100) / 100,
|
|
4403
|
+
max: Math.round(Math.max(...numericValues) * 100) / 100
|
|
4404
|
+
};
|
|
4405
|
+
}, [selectedCellValues, columns]);
|
|
4406
|
+
return {
|
|
4407
|
+
summary,
|
|
4408
|
+
hasNumericValues: summary !== null
|
|
4409
|
+
};
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
// src/hooks/useSpreadsheetColumnResize.ts
|
|
4413
|
+
import { useCallback as useCallback10, useState as useState15, useRef as useRef9 } from "react";
|
|
4414
|
+
var DEFAULT_MIN_WIDTH = 40;
|
|
4415
|
+
function useSpreadsheetColumnResize({
|
|
4416
|
+
minWidth = DEFAULT_MIN_WIDTH,
|
|
4417
|
+
initialColumnWidths,
|
|
4418
|
+
onColumnResize
|
|
4419
|
+
} = {}) {
|
|
4420
|
+
const [columnWidths, setColumnWidths] = useState15(() => {
|
|
4421
|
+
if (initialColumnWidths) {
|
|
4422
|
+
return new Map(Object.entries(initialColumnWidths));
|
|
4423
|
+
}
|
|
4424
|
+
return /* @__PURE__ */ new Map();
|
|
4425
|
+
});
|
|
4426
|
+
const [resizingColumnId, setResizingColumnId] = useState15(null);
|
|
4427
|
+
const startXRef = useRef9(0);
|
|
4428
|
+
const startWidthRef = useRef9(0);
|
|
4429
|
+
const rafRef = useRef9(null);
|
|
4430
|
+
const getColumnWidth = useCallback10(
|
|
4431
|
+
(columnId, defaultWidth) => {
|
|
4432
|
+
return columnWidths.get(columnId) ?? defaultWidth;
|
|
4433
|
+
},
|
|
4434
|
+
[columnWidths]
|
|
4435
|
+
);
|
|
4436
|
+
const updateColumnDom = useCallback10((columnId, width) => {
|
|
4437
|
+
const widthPx = `${width}px`;
|
|
4438
|
+
const cells = document.querySelectorAll(`[data-column-id="${columnId}"]`);
|
|
4439
|
+
for (let i = 0; i < cells.length; i++) {
|
|
4440
|
+
const el = cells[i];
|
|
4441
|
+
el.style.width = widthPx;
|
|
4442
|
+
el.style.minWidth = widthPx;
|
|
4443
|
+
}
|
|
4444
|
+
}, []);
|
|
4445
|
+
const getResizeHandleProps = useCallback10(
|
|
4446
|
+
(columnId, currentWidth) => {
|
|
4447
|
+
return {
|
|
4448
|
+
onMouseDown: (e) => {
|
|
4449
|
+
e.preventDefault();
|
|
4450
|
+
e.stopPropagation();
|
|
4451
|
+
startXRef.current = e.clientX;
|
|
4452
|
+
startWidthRef.current = columnWidths.get(columnId) ?? currentWidth;
|
|
4453
|
+
setResizingColumnId(columnId);
|
|
4454
|
+
document.body.style.cursor = "col-resize";
|
|
4455
|
+
document.body.style.userSelect = "none";
|
|
4456
|
+
let latestWidth = startWidthRef.current;
|
|
4457
|
+
const moveHandler = (ev) => {
|
|
4458
|
+
const diff = ev.clientX - startXRef.current;
|
|
4459
|
+
latestWidth = Math.max(minWidth, startWidthRef.current + diff);
|
|
4460
|
+
if (rafRef.current === null) {
|
|
4461
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
4462
|
+
rafRef.current = null;
|
|
4463
|
+
updateColumnDom(columnId, latestWidth);
|
|
4464
|
+
});
|
|
4465
|
+
}
|
|
4466
|
+
};
|
|
4467
|
+
const upHandler = () => {
|
|
4468
|
+
document.removeEventListener("mousemove", moveHandler);
|
|
4469
|
+
document.removeEventListener("mouseup", upHandler);
|
|
4470
|
+
if (rafRef.current !== null) {
|
|
4471
|
+
cancelAnimationFrame(rafRef.current);
|
|
4472
|
+
rafRef.current = null;
|
|
4473
|
+
}
|
|
4474
|
+
setColumnWidths((prev) => {
|
|
4475
|
+
const next = new Map(prev);
|
|
4476
|
+
next.set(columnId, latestWidth);
|
|
4477
|
+
return next;
|
|
4478
|
+
});
|
|
4479
|
+
onColumnResize?.(columnId, latestWidth);
|
|
4480
|
+
setResizingColumnId(null);
|
|
4481
|
+
document.body.style.cursor = "";
|
|
4482
|
+
document.body.style.userSelect = "";
|
|
4483
|
+
};
|
|
4484
|
+
document.addEventListener("mousemove", moveHandler);
|
|
4485
|
+
document.addEventListener("mouseup", upHandler);
|
|
4486
|
+
},
|
|
4487
|
+
style: {
|
|
4488
|
+
cursor: "col-resize"
|
|
4489
|
+
},
|
|
4490
|
+
className: "absolute top-0 right-0 w-1 h-full hover:bg-blue-400 transition-colors z-10"
|
|
4491
|
+
};
|
|
4492
|
+
},
|
|
4493
|
+
[columnWidths, minWidth, onColumnResize, updateColumnDom]
|
|
4494
|
+
);
|
|
4495
|
+
return {
|
|
4496
|
+
columnWidths,
|
|
4497
|
+
getColumnWidth,
|
|
4498
|
+
getResizeHandleProps,
|
|
4499
|
+
isResizing: resizingColumnId !== null,
|
|
4500
|
+
resizingColumnId
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4503
|
+
|
|
4504
|
+
// src/components/SelectionSummaryBar.tsx
|
|
4505
|
+
import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4506
|
+
function SelectionSummaryBar({
|
|
4507
|
+
summary,
|
|
4508
|
+
focusedCell,
|
|
4509
|
+
columns,
|
|
4510
|
+
data,
|
|
4511
|
+
getRowId,
|
|
4512
|
+
currentPage,
|
|
4513
|
+
pageSize
|
|
4514
|
+
}) {
|
|
4515
|
+
let addressDisplay = null;
|
|
4516
|
+
let valueDisplay = null;
|
|
4517
|
+
if (focusedCell) {
|
|
4518
|
+
const column = columns.find((c) => c.id === focusedCell.columnId);
|
|
4519
|
+
const rowIndex = data.findIndex((r) => getRowId(r) === focusedCell.rowId);
|
|
4520
|
+
const row = rowIndex !== -1 ? data[rowIndex] : null;
|
|
4521
|
+
if (column && row) {
|
|
4522
|
+
const displayRowIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
4523
|
+
addressDisplay = `Row ${displayRowIndex} / ${column.label}`;
|
|
4524
|
+
const value = column.getValue ? column.getValue(row) : row[focusedCell.columnId];
|
|
4525
|
+
if (value !== null && value !== void 0 && value !== "") {
|
|
4526
|
+
valueDisplay = String(value);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
if (!addressDisplay && !summary) return null;
|
|
4531
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between px-3 py-1.5 bg-gray-50 border-t border-gray-200 text-xs text-gray-600", children: [
|
|
4532
|
+
/* @__PURE__ */ jsx13("div", { className: "flex items-center gap-2 min-w-0", children: addressDisplay && /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4533
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-500 bg-white px-2 py-0.5 rounded border border-gray-200 shrink-0", children: addressDisplay }),
|
|
4534
|
+
valueDisplay && /* @__PURE__ */ jsx13("span", { className: "text-gray-700 truncate", title: valueDisplay, children: valueDisplay })
|
|
4535
|
+
] }) }),
|
|
4536
|
+
summary && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-4 shrink-0", children: [
|
|
4537
|
+
/* @__PURE__ */ jsxs13("span", { children: [
|
|
4538
|
+
/* @__PURE__ */ jsx13("span", { className: "text-gray-400 mr-1", children: "Count:" }),
|
|
4539
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-700", children: summary.numericCount })
|
|
4540
|
+
] }),
|
|
4541
|
+
/* @__PURE__ */ jsxs13("span", { children: [
|
|
4542
|
+
/* @__PURE__ */ jsx13("span", { className: "text-gray-400 mr-1", children: "Sum:" }),
|
|
4543
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-700", children: summary.sum.toLocaleString() })
|
|
4544
|
+
] }),
|
|
4545
|
+
/* @__PURE__ */ jsxs13("span", { children: [
|
|
4546
|
+
/* @__PURE__ */ jsx13("span", { className: "text-gray-400 mr-1", children: "Avg:" }),
|
|
4547
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-700", children: summary.avg.toLocaleString() })
|
|
4548
|
+
] }),
|
|
4549
|
+
/* @__PURE__ */ jsxs13("span", { children: [
|
|
4550
|
+
/* @__PURE__ */ jsx13("span", { className: "text-gray-400 mr-1", children: "Min:" }),
|
|
4551
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-700", children: summary.min.toLocaleString() })
|
|
4552
|
+
] }),
|
|
4553
|
+
/* @__PURE__ */ jsxs13("span", { children: [
|
|
4554
|
+
/* @__PURE__ */ jsx13("span", { className: "text-gray-400 mr-1", children: "Max:" }),
|
|
4555
|
+
/* @__PURE__ */ jsx13("span", { className: "font-medium text-gray-700", children: summary.max.toLocaleString() })
|
|
4556
|
+
] })
|
|
4557
|
+
] })
|
|
4558
|
+
] });
|
|
4559
|
+
}
|
|
4560
|
+
SelectionSummaryBar.displayName = "SelectionSummaryBar";
|
|
4561
|
+
|
|
3638
4562
|
// src/components/Spreadsheet.tsx
|
|
3639
|
-
import { jsx as
|
|
4563
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3640
4564
|
function Spreadsheet({
|
|
3641
4565
|
data,
|
|
3642
4566
|
columns,
|
|
@@ -3682,9 +4606,11 @@ function Spreadsheet({
|
|
|
3682
4606
|
pageSize: controlledPageSize,
|
|
3683
4607
|
sortConfig: controlledSortConfig,
|
|
3684
4608
|
onPageChange,
|
|
3685
|
-
filters: controlledFilters
|
|
4609
|
+
filters: controlledFilters,
|
|
4610
|
+
duplicateCheckColumns: propDuplicateCheckColumns,
|
|
4611
|
+
onDuplicateCheckChange
|
|
3686
4612
|
}) {
|
|
3687
|
-
const [spreadsheetSettings, setSpreadsheetSettings] =
|
|
4613
|
+
const [spreadsheetSettings, setSpreadsheetSettings] = useState16({
|
|
3688
4614
|
defaultPinnedColumns: initialSettings?.defaultPinnedColumns ?? [],
|
|
3689
4615
|
defaultSort: initialSettings?.defaultSort ?? null,
|
|
3690
4616
|
defaultPageSize: initialSettings?.defaultPageSize ?? 25,
|
|
@@ -3692,6 +4618,32 @@ function Spreadsheet({
|
|
|
3692
4618
|
autoSave: initialSettings?.autoSave ?? true,
|
|
3693
4619
|
compactView: initialSettings?.compactView ?? false
|
|
3694
4620
|
});
|
|
4621
|
+
const {
|
|
4622
|
+
isCellDuplicate,
|
|
4623
|
+
toggleDuplicateCheck,
|
|
4624
|
+
duplicateCheckColumns,
|
|
4625
|
+
duplicateRowIds,
|
|
4626
|
+
getDuplicateColumnCount
|
|
4627
|
+
} = useSpreadsheetDuplicates({
|
|
4628
|
+
data,
|
|
4629
|
+
columns,
|
|
4630
|
+
duplicateCheckColumns: propDuplicateCheckColumns ?? [],
|
|
4631
|
+
getRowId
|
|
4632
|
+
});
|
|
4633
|
+
const handleDuplicateCheckToggle = useCallback11(
|
|
4634
|
+
(columnId) => {
|
|
4635
|
+
setIsProcessing(true);
|
|
4636
|
+
startTransition(() => {
|
|
4637
|
+
toggleDuplicateCheck(columnId);
|
|
4638
|
+
const currentCols = Array.from(duplicateCheckColumns);
|
|
4639
|
+
const next = currentCols.includes(columnId) ? currentCols.filter((id) => id !== columnId) : [...currentCols, columnId];
|
|
4640
|
+
onDuplicateCheckChange?.(next);
|
|
4641
|
+
setIsProcessing(false);
|
|
4642
|
+
});
|
|
4643
|
+
},
|
|
4644
|
+
[toggleDuplicateCheck, duplicateCheckColumns, onDuplicateCheckChange]
|
|
4645
|
+
);
|
|
4646
|
+
const [isProcessing, setIsProcessing] = useState16(false);
|
|
3695
4647
|
const {
|
|
3696
4648
|
filters,
|
|
3697
4649
|
sortConfig,
|
|
@@ -3711,7 +4663,9 @@ function Spreadsheet({
|
|
|
3711
4663
|
serverSide,
|
|
3712
4664
|
controlledFilters,
|
|
3713
4665
|
controlledSortConfig,
|
|
3714
|
-
defaultSortConfig: spreadsheetSettings.defaultSort
|
|
4666
|
+
defaultSortConfig: spreadsheetSettings.defaultSort,
|
|
4667
|
+
duplicateRowIds,
|
|
4668
|
+
getRowId
|
|
3715
4669
|
});
|
|
3716
4670
|
const {
|
|
3717
4671
|
getCellHighlight,
|
|
@@ -3725,7 +4679,8 @@ function Spreadsheet({
|
|
|
3725
4679
|
highlightPickerColumn,
|
|
3726
4680
|
setHighlightPickerColumn,
|
|
3727
4681
|
highlightPickerCell,
|
|
3728
|
-
setHighlightPickerCell
|
|
4682
|
+
setHighlightPickerCell,
|
|
4683
|
+
recentColors
|
|
3729
4684
|
} = useSpreadsheetHighlighting({
|
|
3730
4685
|
externalRowHighlights,
|
|
3731
4686
|
onRowHighlight,
|
|
@@ -3734,6 +4689,15 @@ function Spreadsheet({
|
|
|
3734
4689
|
externalCellHighlights,
|
|
3735
4690
|
onCellHighlight
|
|
3736
4691
|
});
|
|
4692
|
+
const { getColumnWidth, getResizeHandleProps, isResizing, columnWidths } = useSpreadsheetColumnResize({
|
|
4693
|
+
initialColumnWidths: initialSettings?.columnWidths,
|
|
4694
|
+
onColumnResize: (columnId, width) => {
|
|
4695
|
+
setSpreadsheetSettings((prev) => ({
|
|
4696
|
+
...prev,
|
|
4697
|
+
columnWidths: { ...prev.columnWidths, [columnId]: width }
|
|
4698
|
+
}));
|
|
4699
|
+
}
|
|
4700
|
+
});
|
|
3737
4701
|
const {
|
|
3738
4702
|
pinnedColumns,
|
|
3739
4703
|
isRowIndexPinned,
|
|
@@ -3745,12 +4709,15 @@ function Spreadsheet({
|
|
|
3745
4709
|
getColumnLeftOffset,
|
|
3746
4710
|
getColumnRightOffset,
|
|
3747
4711
|
isColumnPinned,
|
|
3748
|
-
getColumnPinSide
|
|
4712
|
+
getColumnPinSide,
|
|
4713
|
+
getPinnedZIndex,
|
|
4714
|
+
measureRef
|
|
3749
4715
|
} = useSpreadsheetPinning({
|
|
3750
4716
|
columns,
|
|
3751
4717
|
columnGroups,
|
|
3752
4718
|
defaultPinnedColumns: initialSettings?.defaultPinnedColumns,
|
|
3753
|
-
defaultPinnedRightColumns: initialSettings?.defaultPinnedRightColumns
|
|
4719
|
+
defaultPinnedRightColumns: initialSettings?.defaultPinnedRightColumns,
|
|
4720
|
+
getColumnWidth
|
|
3754
4721
|
});
|
|
3755
4722
|
const {
|
|
3756
4723
|
getCellComments,
|
|
@@ -3767,8 +4734,8 @@ function Spreadsheet({
|
|
|
3767
4734
|
onAddCellComment,
|
|
3768
4735
|
onToggleCommentResolved
|
|
3769
4736
|
});
|
|
3770
|
-
const [showSettingsModal, setShowSettingsModal] =
|
|
3771
|
-
const [showFiltersPanel, setShowFiltersPanel] =
|
|
4737
|
+
const [showSettingsModal, setShowSettingsModal] = useState16(false);
|
|
4738
|
+
const [showFiltersPanel, setShowFiltersPanel] = useState16(false);
|
|
3772
4739
|
const {
|
|
3773
4740
|
canUndo,
|
|
3774
4741
|
canRedo,
|
|
@@ -3786,15 +4753,15 @@ function Spreadsheet({
|
|
|
3786
4753
|
autoSave: spreadsheetSettings.autoSave,
|
|
3787
4754
|
onSave
|
|
3788
4755
|
});
|
|
3789
|
-
const [selectedRows, setSelectedRows] =
|
|
3790
|
-
const [lastSelectedRow, setLastSelectedRow] =
|
|
3791
|
-
const
|
|
3792
|
-
const [internalCurrentPage, setInternalCurrentPage] =
|
|
3793
|
-
const [internalPageSize, setInternalPageSize] =
|
|
3794
|
-
const [zoom, setZoom] =
|
|
4756
|
+
const [selectedRows, setSelectedRows] = useState16(/* @__PURE__ */ new Set());
|
|
4757
|
+
const [lastSelectedRow, setLastSelectedRow] = useState16(null);
|
|
4758
|
+
const hoveredRowRef = useRef10(null);
|
|
4759
|
+
const [internalCurrentPage, setInternalCurrentPage] = useState16(1);
|
|
4760
|
+
const [internalPageSize, setInternalPageSize] = useState16(spreadsheetSettings.defaultPageSize);
|
|
4761
|
+
const [zoom, setZoom] = useState16(spreadsheetSettings.defaultZoom);
|
|
3795
4762
|
const currentPage = controlledCurrentPage ?? internalCurrentPage;
|
|
3796
4763
|
const pageSize = controlledPageSize ?? internalPageSize;
|
|
3797
|
-
const handlePageChange =
|
|
4764
|
+
const handlePageChange = useCallback11(
|
|
3798
4765
|
(newPage) => {
|
|
3799
4766
|
if (controlledCurrentPage === void 0) {
|
|
3800
4767
|
setInternalCurrentPage(newPage);
|
|
@@ -3803,7 +4770,7 @@ function Spreadsheet({
|
|
|
3803
4770
|
},
|
|
3804
4771
|
[controlledCurrentPage, onPageChange, pageSize]
|
|
3805
4772
|
);
|
|
3806
|
-
const handlePageSizeChange =
|
|
4773
|
+
const handlePageSizeChange = useCallback11(
|
|
3807
4774
|
(newPageSize) => {
|
|
3808
4775
|
if (controlledPageSize === void 0) {
|
|
3809
4776
|
setInternalPageSize(newPageSize);
|
|
@@ -3815,30 +4782,43 @@ function Spreadsheet({
|
|
|
3815
4782
|
},
|
|
3816
4783
|
[controlledPageSize, controlledCurrentPage, onPageChange]
|
|
3817
4784
|
);
|
|
3818
|
-
const resetPaginationToFirstPage =
|
|
4785
|
+
const resetPaginationToFirstPage = useCallback11(() => {
|
|
3819
4786
|
if (controlledCurrentPage === void 0) {
|
|
3820
4787
|
setInternalCurrentPage(1);
|
|
3821
4788
|
}
|
|
3822
4789
|
onPageChange?.(1, pageSize);
|
|
3823
4790
|
}, [controlledCurrentPage, onPageChange, pageSize]);
|
|
3824
|
-
const handleFilterChangeWithReset =
|
|
4791
|
+
const handleFilterChangeWithReset = useCallback11(
|
|
3825
4792
|
(columnId, filter) => {
|
|
3826
4793
|
handleFilterChange(columnId, filter);
|
|
3827
4794
|
resetPaginationToFirstPage();
|
|
3828
4795
|
},
|
|
3829
4796
|
[handleFilterChange, resetPaginationToFirstPage]
|
|
3830
4797
|
);
|
|
3831
|
-
const clearAllFiltersWithReset =
|
|
4798
|
+
const clearAllFiltersWithReset = useCallback11(() => {
|
|
3832
4799
|
clearAllFilters();
|
|
3833
4800
|
resetPaginationToFirstPage();
|
|
3834
4801
|
}, [clearAllFilters, resetPaginationToFirstPage]);
|
|
3835
|
-
|
|
4802
|
+
useEffect8(() => {
|
|
3836
4803
|
setSpreadsheetSettings((prev) => ({
|
|
3837
4804
|
...prev,
|
|
3838
4805
|
defaultSort: sortConfig
|
|
3839
4806
|
}));
|
|
3840
4807
|
}, [sortConfig]);
|
|
3841
|
-
|
|
4808
|
+
const hasSyncedPinnedFromSettings = useRef10(false);
|
|
4809
|
+
useEffect8(() => {
|
|
4810
|
+
if (hasSyncedPinnedFromSettings.current) return;
|
|
4811
|
+
const settingsPinned = initialSettings?.defaultPinnedColumns;
|
|
4812
|
+
if (settingsPinned && settingsPinned.length > 0) {
|
|
4813
|
+
const currentIds = Array.from(pinnedColumns.keys());
|
|
4814
|
+
const hasAllSettingsPinned = settingsPinned.every((id) => pinnedColumns.has(id));
|
|
4815
|
+
if (!hasAllSettingsPinned) {
|
|
4816
|
+
setPinnedColumnsFromIds(settingsPinned);
|
|
4817
|
+
}
|
|
4818
|
+
hasSyncedPinnedFromSettings.current = true;
|
|
4819
|
+
}
|
|
4820
|
+
}, [initialSettings?.defaultPinnedColumns, pinnedColumns, setPinnedColumnsFromIds]);
|
|
4821
|
+
useEffect8(() => {
|
|
3842
4822
|
const pinnedColumnIds = Array.from(pinnedColumns.keys());
|
|
3843
4823
|
setSpreadsheetSettings((prev) => {
|
|
3844
4824
|
const prevIds = prev.defaultPinnedColumns;
|
|
@@ -3851,15 +4831,15 @@ function Spreadsheet({
|
|
|
3851
4831
|
};
|
|
3852
4832
|
});
|
|
3853
4833
|
}, [pinnedColumns]);
|
|
3854
|
-
const isInitialMount =
|
|
3855
|
-
|
|
4834
|
+
const isInitialMount = useRef10(true);
|
|
4835
|
+
useEffect8(() => {
|
|
3856
4836
|
if (isInitialMount.current) {
|
|
3857
4837
|
isInitialMount.current = false;
|
|
3858
4838
|
return;
|
|
3859
4839
|
}
|
|
3860
4840
|
onSettingsChange?.(spreadsheetSettings);
|
|
3861
4841
|
}, [spreadsheetSettings, onSettingsChange]);
|
|
3862
|
-
const applyUndo =
|
|
4842
|
+
const applyUndo = useCallback11(() => {
|
|
3863
4843
|
const entry = popUndoEntry();
|
|
3864
4844
|
if (!entry || !onCellsEdit) return;
|
|
3865
4845
|
if (entry.type === "cell-edit") {
|
|
@@ -3877,7 +4857,7 @@ function Spreadsheet({
|
|
|
3877
4857
|
}
|
|
3878
4858
|
markAsChanged();
|
|
3879
4859
|
}, [popUndoEntry, onCellsEdit, markAsChanged]);
|
|
3880
|
-
const applyRedo =
|
|
4860
|
+
const applyRedo = useCallback11(() => {
|
|
3881
4861
|
const entry = popRedoEntry();
|
|
3882
4862
|
if (!entry || !onCellsEdit) return;
|
|
3883
4863
|
if (entry.type === "cell-edit") {
|
|
@@ -3893,7 +4873,7 @@ function Spreadsheet({
|
|
|
3893
4873
|
}
|
|
3894
4874
|
markAsChanged();
|
|
3895
4875
|
}, [popRedoEntry, onCellsEdit, markAsChanged]);
|
|
3896
|
-
const paginatedData =
|
|
4876
|
+
const paginatedData = useMemo7(() => {
|
|
3897
4877
|
if (serverSide) {
|
|
3898
4878
|
return filteredData;
|
|
3899
4879
|
}
|
|
@@ -3902,9 +4882,14 @@ function Spreadsheet({
|
|
|
3902
4882
|
}, [filteredData, currentPage, pageSize, serverSide]);
|
|
3903
4883
|
const {
|
|
3904
4884
|
focusedCell,
|
|
4885
|
+
setFocusedCell,
|
|
3905
4886
|
editingCell,
|
|
3906
4887
|
setEditingCell,
|
|
4888
|
+
selectedCellRange,
|
|
4889
|
+
setSelectedCellRange,
|
|
3907
4890
|
handleCellMouseDown,
|
|
4891
|
+
handleCellMouseEnter,
|
|
4892
|
+
handleMouseUp,
|
|
3908
4893
|
isCellInSelection,
|
|
3909
4894
|
getCellSelectionEdge,
|
|
3910
4895
|
clearSelection,
|
|
@@ -3912,14 +4897,20 @@ function Spreadsheet({
|
|
|
3912
4897
|
handleTabNavigation,
|
|
3913
4898
|
enterEditMode,
|
|
3914
4899
|
copySelectedCells,
|
|
3915
|
-
pasteFromSystemClipboard
|
|
4900
|
+
pasteFromSystemClipboard,
|
|
4901
|
+
getSelectedCellValues
|
|
3916
4902
|
} = useSpreadsheetSelection({
|
|
3917
4903
|
data: paginatedData,
|
|
3918
4904
|
columns: visibleColumns,
|
|
3919
4905
|
getRowId,
|
|
3920
4906
|
enableCellEditing
|
|
3921
4907
|
});
|
|
3922
|
-
const
|
|
4908
|
+
const selectedCellValues = useMemo7(() => getSelectedCellValues(), [getSelectedCellValues]);
|
|
4909
|
+
const { summary: selectionSummary, hasNumericValues } = useSpreadsheetSummary({
|
|
4910
|
+
selectedCellValues,
|
|
4911
|
+
columns: visibleColumns
|
|
4912
|
+
});
|
|
4913
|
+
const handleEscapeCallback = useCallback11(() => {
|
|
3923
4914
|
if (commentModalCell !== null) {
|
|
3924
4915
|
setCommentModalCell(null);
|
|
3925
4916
|
} else if (viewCommentsCell !== null) {
|
|
@@ -3948,7 +4939,7 @@ function Spreadsheet({
|
|
|
3948
4939
|
setHighlightPickerCell,
|
|
3949
4940
|
clearSelection
|
|
3950
4941
|
]);
|
|
3951
|
-
const handleNavigate =
|
|
4942
|
+
const handleNavigate = useCallback11(
|
|
3952
4943
|
(direction, event) => {
|
|
3953
4944
|
const extendSelection = event?.shiftKey ?? false;
|
|
3954
4945
|
navigateCell(direction, extendSelection);
|
|
@@ -3969,10 +4960,10 @@ function Spreadsheet({
|
|
|
3969
4960
|
},
|
|
3970
4961
|
[navigateCell, focusedCell]
|
|
3971
4962
|
);
|
|
3972
|
-
const handleEnterEditMode =
|
|
4963
|
+
const handleEnterEditMode = useCallback11(() => {
|
|
3973
4964
|
enterEditMode();
|
|
3974
4965
|
}, [enterEditMode]);
|
|
3975
|
-
const handlePaste =
|
|
4966
|
+
const handlePaste = useCallback11(async () => {
|
|
3976
4967
|
const edits = await pasteFromSystemClipboard();
|
|
3977
4968
|
if (edits.length > 0 && onCellsEdit) {
|
|
3978
4969
|
if (enableUndoRedo) {
|
|
@@ -4012,26 +5003,39 @@ function Spreadsheet({
|
|
|
4012
5003
|
onTabNavigation: handleTabNavigation,
|
|
4013
5004
|
onCopy: copySelectedCells,
|
|
4014
5005
|
onPaste: handlePaste,
|
|
5006
|
+
onSelectAll: () => {
|
|
5007
|
+
if (paginatedData.length > 0 && visibleColumns.length > 0) {
|
|
5008
|
+
const firstRow = paginatedData[0];
|
|
5009
|
+
const lastRow = paginatedData[paginatedData.length - 1];
|
|
5010
|
+
const firstCol = visibleColumns[0];
|
|
5011
|
+
const lastCol = visibleColumns[visibleColumns.length - 1];
|
|
5012
|
+
setSelectedCellRange({
|
|
5013
|
+
start: { rowId: getRowId(firstRow), columnId: firstCol.id },
|
|
5014
|
+
end: { rowId: getRowId(lastRow), columnId: lastCol.id }
|
|
5015
|
+
});
|
|
5016
|
+
setFocusedCell({ rowId: getRowId(firstRow), columnId: firstCol.id });
|
|
5017
|
+
}
|
|
5018
|
+
},
|
|
4015
5019
|
hasFocusedCell: focusedCell !== null,
|
|
4016
5020
|
isEditing: editingCell !== null,
|
|
4017
5021
|
enabled: true
|
|
4018
5022
|
});
|
|
4019
5023
|
const effectiveCompactMode = spreadsheetSettings.compactView ?? false;
|
|
4020
5024
|
const rowIndexHighlightColor = getColumnHighlight(ROW_INDEX_COLUMN_ID);
|
|
4021
|
-
const tableRef =
|
|
5025
|
+
const tableRef = useRef10(null);
|
|
4022
5026
|
const effectiveTotalItems = serverSide ? totalItems ?? data.length : filteredData.length;
|
|
4023
5027
|
const totalPages = Math.max(1, Math.ceil(effectiveTotalItems / pageSize));
|
|
4024
|
-
|
|
5028
|
+
useEffect8(() => {
|
|
4025
5029
|
if (!serverSide && currentPage > totalPages) {
|
|
4026
5030
|
setInternalCurrentPage(1);
|
|
4027
5031
|
}
|
|
4028
5032
|
}, [totalPages, currentPage, serverSide]);
|
|
4029
|
-
const afterFilteredRef =
|
|
5033
|
+
const afterFilteredRef = useRef10(afterFiltered);
|
|
4030
5034
|
afterFilteredRef.current = afterFiltered;
|
|
4031
|
-
|
|
5035
|
+
useEffect8(() => {
|
|
4032
5036
|
afterFilteredRef.current?.(filteredData.toArray());
|
|
4033
5037
|
}, [filteredData]);
|
|
4034
|
-
const handleRowSelect =
|
|
5038
|
+
const handleRowSelect = useCallback11(
|
|
4035
5039
|
(rowId, event) => {
|
|
4036
5040
|
if (!enableRowSelection) return;
|
|
4037
5041
|
event.stopPropagation();
|
|
@@ -4080,14 +5084,23 @@ function Spreadsheet({
|
|
|
4080
5084
|
onSelectionChange
|
|
4081
5085
|
]
|
|
4082
5086
|
);
|
|
4083
|
-
const handleCellClick =
|
|
5087
|
+
const handleCellClick = useCallback11(
|
|
4084
5088
|
(rowId, columnId, event) => {
|
|
4085
5089
|
event.stopPropagation();
|
|
4086
5090
|
handleCellMouseDown(rowId, columnId, event);
|
|
4087
5091
|
},
|
|
4088
5092
|
[handleCellMouseDown]
|
|
4089
5093
|
);
|
|
4090
|
-
const
|
|
5094
|
+
const handleCellDoubleClick = useCallback11(
|
|
5095
|
+
(rowId, columnId) => {
|
|
5096
|
+
const column = columns.find((c) => c.id === columnId);
|
|
5097
|
+
if (column?.editable && enableCellEditing) {
|
|
5098
|
+
setEditingCell({ rowId, columnId });
|
|
5099
|
+
}
|
|
5100
|
+
},
|
|
5101
|
+
[columns, enableCellEditing, setEditingCell]
|
|
5102
|
+
);
|
|
5103
|
+
const handleCellChange = useCallback11(
|
|
4091
5104
|
(rowId, columnId, newValue) => {
|
|
4092
5105
|
const row = data.find((r) => getRowId(r) === rowId);
|
|
4093
5106
|
const previousValue = row ? row[columnId] : void 0;
|
|
@@ -4110,7 +5123,7 @@ function Spreadsheet({
|
|
|
4110
5123
|
},
|
|
4111
5124
|
[data, getRowId, enableUndoRedo, onCellsEdit, pushToUndoStack, markAsChanged]
|
|
4112
5125
|
);
|
|
4113
|
-
const handleConfirmEdit =
|
|
5126
|
+
const handleConfirmEdit = useCallback11(
|
|
4114
5127
|
(finalValue) => {
|
|
4115
5128
|
if (editingCell && finalValue !== void 0) {
|
|
4116
5129
|
handleCellChange(editingCell.rowId, editingCell.columnId, finalValue);
|
|
@@ -4119,105 +5132,51 @@ function Spreadsheet({
|
|
|
4119
5132
|
},
|
|
4120
5133
|
[editingCell, handleCellChange, setEditingCell]
|
|
4121
5134
|
);
|
|
4122
|
-
const handleCancelEdit =
|
|
5135
|
+
const handleCancelEdit = useCallback11(() => {
|
|
4123
5136
|
setEditingCell(null);
|
|
4124
5137
|
}, [setEditingCell]);
|
|
4125
|
-
const handleRowIndexHighlightClick =
|
|
5138
|
+
const handleRowIndexHighlightClick = useCallback11(() => {
|
|
4126
5139
|
setHighlightPickerColumn(ROW_INDEX_COLUMN_ID);
|
|
4127
5140
|
}, [setHighlightPickerColumn]);
|
|
4128
|
-
const columnRenderItems =
|
|
5141
|
+
const columnRenderItems = useMemo7(() => {
|
|
4129
5142
|
if (!columnGroups || columnGroups.length === 0) {
|
|
4130
5143
|
return visibleColumns.map((col) => ({
|
|
4131
5144
|
type: "column",
|
|
4132
5145
|
column: col
|
|
4133
5146
|
}));
|
|
4134
5147
|
}
|
|
4135
|
-
const
|
|
4136
|
-
const
|
|
4137
|
-
const
|
|
5148
|
+
const items = [];
|
|
5149
|
+
const allGroupedIds = new Set(columnGroups.flatMap((g) => g.columns));
|
|
5150
|
+
for (const col of visibleColumns) {
|
|
5151
|
+
if (!allGroupedIds.has(col.id)) {
|
|
5152
|
+
items.push({ type: "column", column: col });
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
4138
5155
|
for (const group of columnGroups) {
|
|
4139
5156
|
const isCollapsed = collapsedGroups.has(group.id);
|
|
5157
|
+
const groupVisibleCols = visibleColumns.filter((c) => group.columns.includes(c.id));
|
|
4140
5158
|
if (isCollapsed) {
|
|
4141
|
-
|
|
5159
|
+
items.push({
|
|
4142
5160
|
type: "collapsed-placeholder",
|
|
4143
5161
|
groupId: group.id,
|
|
4144
5162
|
headerColor: group.headerColor
|
|
4145
5163
|
});
|
|
4146
5164
|
}
|
|
4147
|
-
const groupVisibleCols = (columns || []).filter((c) => {
|
|
4148
|
-
if (!group.columns.includes(c.id)) return false;
|
|
4149
|
-
if (isCollapsed) return pinnedColumns.has(c.id);
|
|
4150
|
-
return true;
|
|
4151
|
-
});
|
|
4152
5165
|
for (const col of groupVisibleCols) {
|
|
4153
|
-
|
|
4154
|
-
if (pinSide === "left") {
|
|
4155
|
-
leftPinnedItems.push({ type: "column", column: col });
|
|
4156
|
-
} else if (pinSide === "right") {
|
|
4157
|
-
rightPinnedItems.push({ type: "column", column: col });
|
|
4158
|
-
} else {
|
|
4159
|
-
middleItems.push({ type: "column", column: col });
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
}
|
|
4163
|
-
const allGroupedIds = new Set(columnGroups.flatMap((g) => g.columns));
|
|
4164
|
-
for (const col of visibleColumns) {
|
|
4165
|
-
if (!allGroupedIds.has(col.id)) {
|
|
4166
|
-
const pinSide = pinnedColumns.get(col.id);
|
|
4167
|
-
if (pinSide === "left") {
|
|
4168
|
-
leftPinnedItems.push({ type: "column", column: col });
|
|
4169
|
-
} else if (pinSide === "right") {
|
|
4170
|
-
rightPinnedItems.push({ type: "column", column: col });
|
|
4171
|
-
} else {
|
|
4172
|
-
middleItems.push({ type: "column", column: col });
|
|
4173
|
-
}
|
|
5166
|
+
items.push({ type: "column", column: col });
|
|
4174
5167
|
}
|
|
4175
5168
|
}
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
(a, b) => pinnedLeftOrder.indexOf(a.column.id) - pinnedLeftOrder.indexOf(b.column.id)
|
|
4180
|
-
);
|
|
4181
|
-
rightPinnedItems.sort(
|
|
4182
|
-
(a, b) => pinnedRightOrder.indexOf(a.column.id) - pinnedRightOrder.indexOf(b.column.id)
|
|
4183
|
-
);
|
|
4184
|
-
return [...leftPinnedItems, ...middleItems, ...rightPinnedItems];
|
|
4185
|
-
}, [columnGroups, collapsedGroups, columns, pinnedColumns, visibleColumns]);
|
|
4186
|
-
const groupHeaderItems = useMemo5(() => {
|
|
5169
|
+
return items;
|
|
5170
|
+
}, [columnGroups, collapsedGroups, visibleColumns]);
|
|
5171
|
+
const groupHeaderItems = useMemo7(() => {
|
|
4187
5172
|
if (!columnGroups || columnGroups.length === 0) return null;
|
|
4188
|
-
const
|
|
4189
|
-
const groups = [];
|
|
4190
|
-
const rightPinned = [];
|
|
5173
|
+
const items = [];
|
|
4191
5174
|
for (const group of columnGroups) {
|
|
4192
5175
|
const isCollapsed = collapsedGroups.has(group.id);
|
|
4193
|
-
const
|
|
4194
|
-
const
|
|
4195
|
-
let movedLeftCount = 0;
|
|
4196
|
-
let movedRightCount = 0;
|
|
4197
|
-
for (const col of visibleGroupColumns) {
|
|
4198
|
-
const pinSide = pinnedColumns.get(col.id);
|
|
4199
|
-
if (pinSide === "left") {
|
|
4200
|
-
movedLeftCount++;
|
|
4201
|
-
leftPinned.push({
|
|
4202
|
-
type: "pinned-column",
|
|
4203
|
-
columnId: col.id,
|
|
4204
|
-
headerColor: group.headerColor,
|
|
4205
|
-
pinSide: "left"
|
|
4206
|
-
});
|
|
4207
|
-
} else if (pinSide === "right") {
|
|
4208
|
-
movedRightCount++;
|
|
4209
|
-
rightPinned.push({
|
|
4210
|
-
type: "pinned-column",
|
|
4211
|
-
columnId: col.id,
|
|
4212
|
-
headerColor: group.headerColor,
|
|
4213
|
-
pinSide: "right"
|
|
4214
|
-
});
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
const remainingCols = visibleGroupColumns.length - movedLeftCount - movedRightCount;
|
|
4218
|
-
const colSpan = remainingCols + (isCollapsed ? 1 : 0);
|
|
5176
|
+
const visibleCount = visibleColumns.filter((c) => group.columns.includes(c.id)).length;
|
|
5177
|
+
const colSpan = visibleCount + (isCollapsed ? 1 : 0);
|
|
4219
5178
|
if (colSpan > 0) {
|
|
4220
|
-
|
|
5179
|
+
items.push({
|
|
4221
5180
|
type: "group",
|
|
4222
5181
|
group,
|
|
4223
5182
|
colSpan,
|
|
@@ -4225,18 +5184,10 @@ function Spreadsheet({
|
|
|
4225
5184
|
});
|
|
4226
5185
|
}
|
|
4227
5186
|
}
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
);
|
|
4233
|
-
rightPinned.sort(
|
|
4234
|
-
(a, b) => pinnedRightOrder.indexOf(a.columnId) - pinnedRightOrder.indexOf(b.columnId)
|
|
4235
|
-
);
|
|
4236
|
-
return [...leftPinned, ...groups, ...rightPinned];
|
|
4237
|
-
}, [columnGroups, collapsedGroups, columns, pinnedColumns]);
|
|
4238
|
-
return /* @__PURE__ */ jsxs13("div", { className: cn("flex flex-col h-full bg-white", className), children: [
|
|
4239
|
-
showToolbar && /* @__PURE__ */ jsx13(
|
|
5187
|
+
return items;
|
|
5188
|
+
}, [columnGroups, collapsedGroups, visibleColumns]);
|
|
5189
|
+
return /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col h-full bg-white", className), children: [
|
|
5190
|
+
showToolbar && /* @__PURE__ */ jsx14(
|
|
4240
5191
|
SpreadsheetToolbar,
|
|
4241
5192
|
{
|
|
4242
5193
|
zoom,
|
|
@@ -4271,386 +5222,405 @@ function Spreadsheet({
|
|
|
4271
5222
|
menuItems: toolbarMenuItems
|
|
4272
5223
|
}
|
|
4273
5224
|
),
|
|
4274
|
-
/* @__PURE__ */
|
|
4275
|
-
"div",
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
/* @__PURE__ */
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
5225
|
+
/* @__PURE__ */ jsxs14("div", { ref: tableRef, className: cn("flex-1 overflow-auto border border-gray-200 rounded spreadsheet-scroll-container relative", isResizing && "select-none"), onMouseUp: handleMouseUp, children: [
|
|
5226
|
+
isProcessing && /* @__PURE__ */ jsx14("div", { className: "spreadsheet-processing-overlay", children: /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 text-gray-500", children: [
|
|
5227
|
+
/* @__PURE__ */ jsx14("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }),
|
|
5228
|
+
"Processing..."
|
|
5229
|
+
] }) }),
|
|
5230
|
+
/* @__PURE__ */ jsxs14("table", { ref: measureRef, className: "border-separate border-spacing-0 text-sm select-none", style: zoom !== 100 ? { zoom: zoom / 100 } : void 0, children: [
|
|
5231
|
+
/* @__PURE__ */ jsxs14("thead", { className: "sticky top-0", style: { zIndex: 50 }, children: [
|
|
5232
|
+
columnGroups && groupHeaderItems && /* @__PURE__ */ jsxs14("tr", { children: [
|
|
5233
|
+
/* @__PURE__ */ jsx14(
|
|
5234
|
+
RowIndexColumnHeader,
|
|
5235
|
+
{
|
|
5236
|
+
highlightColor: rowIndexHighlightColor,
|
|
5237
|
+
isPinned: isRowIndexPinned,
|
|
5238
|
+
isSecondRow: true,
|
|
5239
|
+
compactMode: effectiveCompactMode
|
|
5240
|
+
}
|
|
5241
|
+
),
|
|
5242
|
+
groupHeaderItems.map((item) => {
|
|
5243
|
+
const { group, colSpan, isCollapsed } = item;
|
|
5244
|
+
return /* @__PURE__ */ jsx14(
|
|
5245
|
+
"th",
|
|
4285
5246
|
{
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
return /* @__PURE__ */ jsx13(
|
|
5247
|
+
colSpan,
|
|
5248
|
+
className: cn(
|
|
5249
|
+
"border border-gray-200 px-2 py-1.5 text-center font-bold text-gray-700",
|
|
5250
|
+
group.collapsible && "cursor-pointer hover:bg-gray-100"
|
|
5251
|
+
),
|
|
5252
|
+
style: {
|
|
5253
|
+
backgroundColor: group.headerColor || "rgb(243 244 246)"
|
|
5254
|
+
},
|
|
5255
|
+
onClick: () => group.collapsible && handleToggleGroupCollapse(group.id),
|
|
5256
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-center gap-1", children: [
|
|
5257
|
+
group.collapsible && (isCollapsed ? /* @__PURE__ */ jsx14(HiChevronRight, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx14(HiChevronDown, { className: "h-3 w-3" })),
|
|
5258
|
+
/* @__PURE__ */ jsx14("span", { children: group.label })
|
|
5259
|
+
] })
|
|
5260
|
+
},
|
|
5261
|
+
group.id
|
|
5262
|
+
);
|
|
5263
|
+
})
|
|
5264
|
+
] }),
|
|
5265
|
+
/* @__PURE__ */ jsxs14("tr", { children: [
|
|
5266
|
+
/* @__PURE__ */ jsx14(
|
|
5267
|
+
RowIndexColumnHeader,
|
|
5268
|
+
{
|
|
5269
|
+
enableHighlighting,
|
|
5270
|
+
highlightColor: rowIndexHighlightColor,
|
|
5271
|
+
isPinned: isRowIndexPinned,
|
|
5272
|
+
onHighlightClick: handleRowIndexHighlightClick,
|
|
5273
|
+
onPinClick: () => handleTogglePin(ROW_INDEX_COLUMN_ID),
|
|
5274
|
+
compactMode: effectiveCompactMode
|
|
5275
|
+
}
|
|
5276
|
+
),
|
|
5277
|
+
columnRenderItems.map((item) => {
|
|
5278
|
+
if (item.type === "collapsed-placeholder") {
|
|
5279
|
+
return /* @__PURE__ */ jsx14(
|
|
4320
5280
|
"th",
|
|
4321
5281
|
{
|
|
4322
|
-
|
|
4323
|
-
className: cn(
|
|
4324
|
-
"border border-gray-200 px-2 py-1.5 text-center font-bold text-gray-700",
|
|
4325
|
-
group.collapsible && "cursor-pointer hover:bg-gray-100"
|
|
4326
|
-
),
|
|
5282
|
+
className: "border border-gray-200 px-2 py-1 text-center text-gray-400",
|
|
4327
5283
|
style: {
|
|
4328
|
-
backgroundColor:
|
|
5284
|
+
backgroundColor: item.headerColor || "rgb(243 244 246)",
|
|
5285
|
+
minWidth: "30px"
|
|
4329
5286
|
},
|
|
4330
|
-
|
|
4331
|
-
children: /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-center gap-1", children: [
|
|
4332
|
-
group.collapsible && (isCollapsed ? /* @__PURE__ */ jsx13(HiChevronRight, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx13(HiChevronDown, { className: "h-3 w-3" })),
|
|
4333
|
-
/* @__PURE__ */ jsx13("span", { children: group.label })
|
|
4334
|
-
] })
|
|
5287
|
+
children: "..."
|
|
4335
5288
|
},
|
|
4336
|
-
|
|
5289
|
+
`${item.groupId}-placeholder`
|
|
4337
5290
|
);
|
|
4338
|
-
}
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
5291
|
+
}
|
|
5292
|
+
const column = item.column;
|
|
5293
|
+
const isPinnedLeft = isColumnPinned(column.id) && getColumnPinSide(column.id) === "left";
|
|
5294
|
+
const isPinnedRight = isColumnPinned(column.id) && getColumnPinSide(column.id) === "right";
|
|
5295
|
+
return /* @__PURE__ */ jsx14(
|
|
5296
|
+
MemoizedSpreadsheetHeader,
|
|
4343
5297
|
{
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
}
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
const column = item.column;
|
|
4369
|
-
const isPinnedLeft = isColumnPinned(column.id) && getColumnPinSide(column.id) === "left";
|
|
4370
|
-
const isPinnedRight = isColumnPinned(column.id) && getColumnPinSide(column.id) === "right";
|
|
4371
|
-
return /* @__PURE__ */ jsx13(
|
|
4372
|
-
SpreadsheetHeader,
|
|
4373
|
-
{
|
|
4374
|
-
column,
|
|
4375
|
-
sortConfig,
|
|
4376
|
-
hasActiveFilter: !!filters[column.id],
|
|
4377
|
-
isPinned: isColumnPinned(column.id),
|
|
4378
|
-
pinSide: getColumnPinSide(column.id),
|
|
4379
|
-
leftOffset: isPinnedLeft ? getColumnLeftOffset(column.id) : 0,
|
|
4380
|
-
rightOffset: isPinnedRight ? getColumnRightOffset(column.id) : 0,
|
|
4381
|
-
highlightColor: getColumnHighlight(column.id),
|
|
4382
|
-
compactMode: effectiveCompactMode,
|
|
4383
|
-
onClick: () => handleSort(column.id),
|
|
4384
|
-
onFilterClick: () => setActiveFilterColumn(
|
|
4385
|
-
activeFilterColumn === column.id ? null : column.id
|
|
4386
|
-
),
|
|
4387
|
-
onPinClick: () => handleTogglePin(column.id),
|
|
4388
|
-
onHighlightClick: enableHighlighting ? () => setHighlightPickerColumn(column.id) : void 0,
|
|
4389
|
-
children: activeFilterColumn === column.id && /* @__PURE__ */ jsx13(
|
|
4390
|
-
SpreadsheetFilterDropdown,
|
|
4391
|
-
{
|
|
4392
|
-
column,
|
|
4393
|
-
filter: filters[column.id],
|
|
4394
|
-
onFilterChange: (filter) => handleFilterChangeWithReset(
|
|
4395
|
-
column.id,
|
|
4396
|
-
filter
|
|
4397
|
-
),
|
|
4398
|
-
onClose: () => setActiveFilterColumn(null)
|
|
5298
|
+
column,
|
|
5299
|
+
sortConfig,
|
|
5300
|
+
hasActiveFilter: !!filters[column.id],
|
|
5301
|
+
isPinned: isColumnPinned(column.id),
|
|
5302
|
+
pinSide: getColumnPinSide(column.id),
|
|
5303
|
+
pinnedZIndex: isColumnPinned(column.id) ? getPinnedZIndex(column.id) : void 0,
|
|
5304
|
+
leftOffset: isPinnedLeft ? getColumnLeftOffset(column.id) : 0,
|
|
5305
|
+
rightOffset: isPinnedRight ? getColumnRightOffset(column.id) : 0,
|
|
5306
|
+
highlightColor: getColumnHighlight(column.id),
|
|
5307
|
+
compactMode: effectiveCompactMode,
|
|
5308
|
+
onClick: () => {
|
|
5309
|
+
if (paginatedData.length > 0) {
|
|
5310
|
+
const firstRowId = getRowId(paginatedData[0]);
|
|
5311
|
+
const lastRowId = getRowId(paginatedData[paginatedData.length - 1]);
|
|
5312
|
+
const isAlreadySelected = selectedCellRange?.start.columnId === column.id && selectedCellRange?.end.columnId === column.id && selectedCellRange?.start.rowId === firstRowId && selectedCellRange?.end.rowId === lastRowId;
|
|
5313
|
+
if (isAlreadySelected) {
|
|
5314
|
+
setSelectedCellRange(null);
|
|
5315
|
+
setFocusedCell(null);
|
|
5316
|
+
} else {
|
|
5317
|
+
setSelectedCellRange({
|
|
5318
|
+
start: { rowId: firstRowId, columnId: column.id },
|
|
5319
|
+
end: { rowId: lastRowId, columnId: column.id }
|
|
5320
|
+
});
|
|
5321
|
+
setFocusedCell({ rowId: firstRowId, columnId: column.id });
|
|
4399
5322
|
}
|
|
4400
|
-
|
|
5323
|
+
}
|
|
4401
5324
|
},
|
|
4402
|
-
column.id
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
"td",
|
|
4419
|
-
{
|
|
4420
|
-
colSpan: columnRenderItems.length + 1,
|
|
4421
|
-
className: "text-center py-8 text-gray-500",
|
|
4422
|
-
children: emptyMessage
|
|
4423
|
-
}
|
|
4424
|
-
) }) : paginatedData.map((row, rowIndex) => {
|
|
4425
|
-
const rowId = getRowId(row);
|
|
4426
|
-
const isRowSelected = selectedRows.has(rowId);
|
|
4427
|
-
const isRowHovered = hoveredRow === rowId;
|
|
4428
|
-
const rowHighlight = getRowHighlight(rowId);
|
|
4429
|
-
const displayIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
4430
|
-
return /* @__PURE__ */ jsxs13(
|
|
4431
|
-
"tr",
|
|
4432
|
-
{
|
|
4433
|
-
onMouseEnter: () => setHoveredRow(rowId),
|
|
4434
|
-
onMouseLeave: () => setHoveredRow(null),
|
|
4435
|
-
onClick: () => {
|
|
4436
|
-
onRowClick?.(row, rowIndex);
|
|
4437
|
-
},
|
|
4438
|
-
onDoubleClick: () => onRowDoubleClick?.(row, rowIndex),
|
|
4439
|
-
children: [
|
|
4440
|
-
/* @__PURE__ */ jsx13(
|
|
4441
|
-
"td",
|
|
5325
|
+
onSortClick: () => handleSort(column.id),
|
|
5326
|
+
onFilterClick: () => setActiveFilterColumn(
|
|
5327
|
+
activeFilterColumn === column.id ? null : column.id
|
|
5328
|
+
),
|
|
5329
|
+
onPinClick: () => handleTogglePin(column.id),
|
|
5330
|
+
onHighlightClick: enableHighlighting ? () => setHighlightPickerColumn(column.id) : void 0,
|
|
5331
|
+
resizeHandleProps: getResizeHandleProps(
|
|
5332
|
+
column.id,
|
|
5333
|
+
column.width || column.minWidth || 100
|
|
5334
|
+
),
|
|
5335
|
+
resolvedWidth: getColumnWidth(column.id),
|
|
5336
|
+
hasDuplicateCheck: duplicateCheckColumns.has(column.id),
|
|
5337
|
+
onDuplicateCheckClick: () => handleDuplicateCheckToggle(column.id),
|
|
5338
|
+
duplicateCount: getDuplicateColumnCount(column.id),
|
|
5339
|
+
children: activeFilterColumn === column.id && /* @__PURE__ */ jsx14(
|
|
5340
|
+
SpreadsheetFilterDropdown,
|
|
4442
5341
|
{
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
isRowSelected && "bg-blue-100",
|
|
4449
|
-
!isRowSelected && rowHighlight && "",
|
|
4450
|
-
isRowHovered && !isRowSelected && !rowHighlight && "bg-gray-50"
|
|
5342
|
+
column,
|
|
5343
|
+
filter: filters[column.id],
|
|
5344
|
+
onFilterChange: (filter) => handleFilterChangeWithReset(
|
|
5345
|
+
column.id,
|
|
5346
|
+
filter
|
|
4451
5347
|
),
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
5348
|
+
onClose: () => setActiveFilterColumn(null),
|
|
5349
|
+
hasDuplicateCheck: duplicateCheckColumns.has(column.id)
|
|
5350
|
+
}
|
|
5351
|
+
)
|
|
5352
|
+
},
|
|
5353
|
+
column.id
|
|
5354
|
+
);
|
|
5355
|
+
})
|
|
5356
|
+
] })
|
|
5357
|
+
] }),
|
|
5358
|
+
/* @__PURE__ */ jsx14("tbody", { children: isLoading ? /* @__PURE__ */ jsx14("tr", { children: /* @__PURE__ */ jsx14(
|
|
5359
|
+
"td",
|
|
5360
|
+
{
|
|
5361
|
+
colSpan: columnRenderItems.length + 1,
|
|
5362
|
+
className: "text-center py-8 text-gray-500",
|
|
5363
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-center gap-2", children: [
|
|
5364
|
+
/* @__PURE__ */ jsx14("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }),
|
|
5365
|
+
"Loading..."
|
|
5366
|
+
] })
|
|
5367
|
+
}
|
|
5368
|
+
) }) : paginatedData.length === 0 ? /* @__PURE__ */ jsx14("tr", { children: /* @__PURE__ */ jsx14(
|
|
5369
|
+
"td",
|
|
5370
|
+
{
|
|
5371
|
+
colSpan: columnRenderItems.length + 1,
|
|
5372
|
+
className: "text-center py-8 text-gray-500",
|
|
5373
|
+
children: emptyMessage
|
|
5374
|
+
}
|
|
5375
|
+
) }) : paginatedData.map((row, rowIndex) => {
|
|
5376
|
+
const rowId = getRowId(row);
|
|
5377
|
+
const isRowSelected = selectedRows.has(rowId);
|
|
5378
|
+
const rowHighlight = getRowHighlight(rowId);
|
|
5379
|
+
const displayIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
5380
|
+
return /* @__PURE__ */ jsxs14(
|
|
5381
|
+
"tr",
|
|
5382
|
+
{
|
|
5383
|
+
onMouseEnter: () => {
|
|
5384
|
+
hoveredRowRef.current = rowId;
|
|
5385
|
+
},
|
|
5386
|
+
onMouseLeave: () => {
|
|
5387
|
+
hoveredRowRef.current = null;
|
|
5388
|
+
},
|
|
5389
|
+
onClick: () => {
|
|
5390
|
+
onRowClick?.(row, rowIndex);
|
|
5391
|
+
},
|
|
5392
|
+
onDoubleClick: () => onRowDoubleClick?.(row, rowIndex),
|
|
5393
|
+
children: [
|
|
5394
|
+
/* @__PURE__ */ jsx14(
|
|
5395
|
+
"td",
|
|
5396
|
+
{
|
|
5397
|
+
"data-column-id": "__row_index__",
|
|
5398
|
+
onClick: (e) => handleRowSelect(rowId, e),
|
|
5399
|
+
className: cn(
|
|
5400
|
+
"border border-gray-200 text-center font-semibold cursor-pointer group",
|
|
5401
|
+
effectiveCompactMode ? "text-xs px-1.5 py-0.5" : "text-sm px-2.5 py-1.5",
|
|
5402
|
+
"sticky",
|
|
5403
|
+
isRowSelected && "bg-blue-100",
|
|
5404
|
+
!isRowSelected && rowHighlight && ""
|
|
5405
|
+
),
|
|
5406
|
+
style: {
|
|
5407
|
+
backgroundColor: rowHighlight?.color || (isRowSelected ? "#dbeafe" : rowIndexHighlightColor || (rowIndex % 2 !== 0 ? "#f9fafb" : "white")),
|
|
5408
|
+
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
5409
|
+
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
5410
|
+
position: "sticky",
|
|
5411
|
+
left: 0,
|
|
5412
|
+
zIndex: 40
|
|
5413
|
+
},
|
|
5414
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "relative flex items-center w-full h-full", children: [
|
|
5415
|
+
/* @__PURE__ */ jsx14("span", { className: "pl-1", children: displayIndex }),
|
|
5416
|
+
/* @__PURE__ */ jsxs14("div", { className: "absolute inset-y-0 right-0 flex items-center gap-0.5 pr-0.5", children: [
|
|
5417
|
+
rowContextMenuItems && rowContextMenuItems.length > 0 && /* @__PURE__ */ jsx14(
|
|
5418
|
+
RowContextMenu,
|
|
5419
|
+
{
|
|
5420
|
+
row,
|
|
4495
5421
|
rowId,
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
5422
|
+
items: rowContextMenuItems,
|
|
5423
|
+
compactMode: effectiveCompactMode
|
|
5424
|
+
}
|
|
5425
|
+
),
|
|
5426
|
+
enableHighlighting && /* @__PURE__ */ jsx14(
|
|
5427
|
+
"button",
|
|
5428
|
+
{
|
|
5429
|
+
type: "button",
|
|
5430
|
+
onClick: (e) => {
|
|
5431
|
+
e.stopPropagation();
|
|
5432
|
+
setHighlightPickerRow(rowId);
|
|
5433
|
+
},
|
|
5434
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded",
|
|
5435
|
+
title: "Highlight row",
|
|
5436
|
+
children: /* @__PURE__ */ jsx14(
|
|
5437
|
+
AiFillHighlight,
|
|
5438
|
+
{
|
|
5439
|
+
className: cn(
|
|
5440
|
+
"h-2.5 w-2.5",
|
|
5441
|
+
rowHighlight ? "text-amber-500" : "text-gray-500"
|
|
5442
|
+
)
|
|
5443
|
+
}
|
|
5444
|
+
)
|
|
5445
|
+
}
|
|
5446
|
+
),
|
|
5447
|
+
enableComments && (cellHasComments(
|
|
5448
|
+
rowId,
|
|
5449
|
+
ROW_INDEX_COLUMN_ID
|
|
5450
|
+
) ? /* @__PURE__ */ jsxs14(
|
|
5451
|
+
"button",
|
|
5452
|
+
{
|
|
5453
|
+
type: "button",
|
|
5454
|
+
onClick: (e) => {
|
|
5455
|
+
e.stopPropagation();
|
|
5456
|
+
setViewCommentsCell({
|
|
5457
|
+
rowId,
|
|
5458
|
+
columnId: ROW_INDEX_COLUMN_ID
|
|
5459
|
+
});
|
|
5460
|
+
},
|
|
5461
|
+
className: "p-0.5 bg-amber-100 hover:bg-amber-200 rounded transition-colors flex items-center gap-0.5",
|
|
5462
|
+
title: `${getCellUnresolvedCommentCount(rowId, ROW_INDEX_COLUMN_ID)} comment(s) - click to view`,
|
|
5463
|
+
children: [
|
|
5464
|
+
/* @__PURE__ */ jsx14(FaComment, { className: "h-2.5 w-2.5 text-amber-500" }),
|
|
5465
|
+
getCellUnresolvedCommentCount(
|
|
5466
|
+
rowId,
|
|
5467
|
+
ROW_INDEX_COLUMN_ID
|
|
5468
|
+
) > 0 && /* @__PURE__ */ jsx14("span", { className: "text-[9px] font-medium text-amber-600", children: getCellUnresolvedCommentCount(
|
|
5469
|
+
rowId,
|
|
5470
|
+
ROW_INDEX_COLUMN_ID
|
|
5471
|
+
) > 99 ? "99+" : getCellUnresolvedCommentCount(
|
|
5472
|
+
rowId,
|
|
5473
|
+
ROW_INDEX_COLUMN_ID
|
|
5474
|
+
) })
|
|
5475
|
+
]
|
|
5476
|
+
}
|
|
5477
|
+
) : /* @__PURE__ */ jsx14(
|
|
5478
|
+
"button",
|
|
5479
|
+
{
|
|
5480
|
+
type: "button",
|
|
5481
|
+
onClick: (e) => {
|
|
5482
|
+
e.stopPropagation();
|
|
5483
|
+
setCommentModalCell({
|
|
5484
|
+
rowId,
|
|
5485
|
+
columnId: ROW_INDEX_COLUMN_ID
|
|
5486
|
+
});
|
|
5487
|
+
},
|
|
5488
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded",
|
|
5489
|
+
title: "Add comment",
|
|
5490
|
+
children: /* @__PURE__ */ jsx14(FaRegComment, { className: "h-2.5 w-2.5 text-gray-500" })
|
|
5491
|
+
}
|
|
5492
|
+
)),
|
|
5493
|
+
rowActions?.map((action) => {
|
|
5494
|
+
if (action.visible && !action.visible(row))
|
|
5495
|
+
return null;
|
|
5496
|
+
return /* @__PURE__ */ jsx14(
|
|
4525
5497
|
"button",
|
|
4526
5498
|
{
|
|
4527
5499
|
type: "button",
|
|
4528
5500
|
onClick: (e) => {
|
|
4529
5501
|
e.stopPropagation();
|
|
4530
|
-
|
|
4531
|
-
rowId,
|
|
4532
|
-
columnId: ROW_INDEX_COLUMN_ID
|
|
4533
|
-
});
|
|
5502
|
+
action.onClick(row, rowId);
|
|
4534
5503
|
},
|
|
4535
|
-
className:
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
{
|
|
4546
|
-
type: "button",
|
|
4547
|
-
onClick: (e) => {
|
|
4548
|
-
e.stopPropagation();
|
|
4549
|
-
action.onClick(row, rowId);
|
|
4550
|
-
},
|
|
4551
|
-
className: cn(
|
|
4552
|
-
"opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded",
|
|
4553
|
-
action.className
|
|
4554
|
-
),
|
|
4555
|
-
title: action.tooltip,
|
|
4556
|
-
children: action.icon
|
|
4557
|
-
},
|
|
4558
|
-
action.id
|
|
4559
|
-
);
|
|
4560
|
-
})
|
|
4561
|
-
] })
|
|
5504
|
+
className: cn(
|
|
5505
|
+
"opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded",
|
|
5506
|
+
action.className
|
|
5507
|
+
),
|
|
5508
|
+
title: action.tooltip,
|
|
5509
|
+
children: action.icon
|
|
5510
|
+
},
|
|
5511
|
+
action.id
|
|
5512
|
+
);
|
|
5513
|
+
})
|
|
4562
5514
|
] })
|
|
4563
|
-
}
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
}
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
}
|
|
4578
|
-
const column = item.column;
|
|
4579
|
-
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
4580
|
-
const isEditing = editingCell?.rowId === rowId && editingCell?.columnId === column.id;
|
|
4581
|
-
const isFocused = focusedCell?.rowId === rowId && focusedCell?.columnId === column.id;
|
|
4582
|
-
const isInSelection = isCellInSelection(
|
|
4583
|
-
rowId,
|
|
4584
|
-
column.id
|
|
5515
|
+
] })
|
|
5516
|
+
}
|
|
5517
|
+
),
|
|
5518
|
+
columnRenderItems.map((item) => {
|
|
5519
|
+
if (item.type === "collapsed-placeholder") {
|
|
5520
|
+
return /* @__PURE__ */ jsx14(
|
|
5521
|
+
"td",
|
|
5522
|
+
{
|
|
5523
|
+
className: "border border-gray-200 px-2 py-1 text-center text-gray-300",
|
|
5524
|
+
style: {
|
|
5525
|
+
backgroundColor: item.headerColor || "rgb(243 244 246)"
|
|
5526
|
+
}
|
|
5527
|
+
},
|
|
5528
|
+
`${item.groupId}-placeholder`
|
|
4585
5529
|
);
|
|
4586
|
-
|
|
5530
|
+
}
|
|
5531
|
+
const column = item.column;
|
|
5532
|
+
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
5533
|
+
const isEditing = editingCell?.rowId === rowId && editingCell?.columnId === column.id;
|
|
5534
|
+
const isFocused = focusedCell?.rowId === rowId && focusedCell?.columnId === column.id;
|
|
5535
|
+
const isInSelection = isCellInSelection(
|
|
5536
|
+
rowId,
|
|
5537
|
+
column.id
|
|
5538
|
+
);
|
|
5539
|
+
const selectionEdge = getCellSelectionEdge(
|
|
5540
|
+
rowId,
|
|
5541
|
+
column.id
|
|
5542
|
+
);
|
|
5543
|
+
const cellOrRowOrColumnHighlight = getCellHighlight(rowId, column.id) || rowHighlight?.color || getColumnHighlight(column.id);
|
|
5544
|
+
const isColPinned = isColumnPinned(column.id);
|
|
5545
|
+
const colPinSide = getColumnPinSide(column.id);
|
|
5546
|
+
return /* @__PURE__ */ jsx14(
|
|
5547
|
+
MemoizedSpreadsheetCell,
|
|
5548
|
+
{
|
|
5549
|
+
value,
|
|
5550
|
+
column,
|
|
5551
|
+
row,
|
|
5552
|
+
rowIndex,
|
|
4587
5553
|
rowId,
|
|
4588
|
-
column.
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
column.id
|
|
4615
|
-
),
|
|
4616
|
-
onClick: (e) => handleCellClick(rowId, column.id, e),
|
|
4617
|
-
onConfirm: handleConfirmEdit,
|
|
4618
|
-
onCancel: handleCancelEdit,
|
|
4619
|
-
onHighlight: enableHighlighting ? () => {
|
|
4620
|
-
setHighlightPickerCell({
|
|
4621
|
-
rowId,
|
|
4622
|
-
columnId: column.id
|
|
4623
|
-
});
|
|
4624
|
-
} : void 0,
|
|
4625
|
-
hasComments: cellHasComments(
|
|
4626
|
-
rowId,
|
|
4627
|
-
column.id
|
|
4628
|
-
),
|
|
4629
|
-
unresolvedCommentCount: getCellUnresolvedCommentCount(
|
|
4630
|
-
rowId,
|
|
4631
|
-
column.id
|
|
4632
|
-
),
|
|
4633
|
-
onAddComment: enableComments ? () => setCommentModalCell({
|
|
4634
|
-
rowId,
|
|
4635
|
-
columnId: column.id
|
|
4636
|
-
}) : void 0,
|
|
4637
|
-
onViewComments: enableComments && cellHasComments(rowId, column.id) ? () => setViewCommentsCell({
|
|
5554
|
+
isEditable: column.editable && enableCellEditing,
|
|
5555
|
+
isEditing,
|
|
5556
|
+
isFocused,
|
|
5557
|
+
isInSelection,
|
|
5558
|
+
selectionEdge,
|
|
5559
|
+
isRowSelected,
|
|
5560
|
+
isRowHovered: false,
|
|
5561
|
+
highlightColor: cellOrRowOrColumnHighlight,
|
|
5562
|
+
isDuplicate: isCellDuplicate(rowId, column.id),
|
|
5563
|
+
compactMode: effectiveCompactMode,
|
|
5564
|
+
isOddRow: rowIndex % 2 !== 0,
|
|
5565
|
+
resolvedWidth: getColumnWidth(column.id),
|
|
5566
|
+
isPinned: isColPinned,
|
|
5567
|
+
pinSide: colPinSide,
|
|
5568
|
+
pinnedZIndex: isColPinned ? getPinnedZIndex(column.id) : void 0,
|
|
5569
|
+
leftOffset: getColumnLeftOffset(column.id),
|
|
5570
|
+
rightOffset: getColumnRightOffset(
|
|
5571
|
+
column.id
|
|
5572
|
+
),
|
|
5573
|
+
onClick: (e) => handleCellClick(rowId, column.id, e),
|
|
5574
|
+
onDoubleClick: () => handleCellDoubleClick(rowId, column.id),
|
|
5575
|
+
onMouseEnter: () => handleCellMouseEnter(rowId, column.id),
|
|
5576
|
+
onConfirm: handleConfirmEdit,
|
|
5577
|
+
onCancel: handleCancelEdit,
|
|
5578
|
+
onHighlight: enableHighlighting ? () => {
|
|
5579
|
+
setHighlightPickerCell({
|
|
4638
5580
|
rowId,
|
|
4639
5581
|
columnId: column.id
|
|
4640
|
-
})
|
|
4641
|
-
},
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
5582
|
+
});
|
|
5583
|
+
} : void 0,
|
|
5584
|
+
hasComments: cellHasComments(
|
|
5585
|
+
rowId,
|
|
5586
|
+
column.id
|
|
5587
|
+
),
|
|
5588
|
+
unresolvedCommentCount: getCellUnresolvedCommentCount(
|
|
5589
|
+
rowId,
|
|
5590
|
+
column.id
|
|
5591
|
+
),
|
|
5592
|
+
onAddComment: enableComments ? () => setCommentModalCell({
|
|
5593
|
+
rowId,
|
|
5594
|
+
columnId: column.id
|
|
5595
|
+
}) : void 0,
|
|
5596
|
+
onViewComments: enableComments && cellHasComments(rowId, column.id) ? () => setViewCommentsCell({
|
|
5597
|
+
rowId,
|
|
5598
|
+
columnId: column.id
|
|
5599
|
+
}) : void 0
|
|
5600
|
+
},
|
|
5601
|
+
column.id
|
|
5602
|
+
);
|
|
5603
|
+
})
|
|
5604
|
+
]
|
|
5605
|
+
},
|
|
5606
|
+
rowId
|
|
5607
|
+
);
|
|
5608
|
+
}) })
|
|
5609
|
+
] })
|
|
5610
|
+
] }),
|
|
5611
|
+
/* @__PURE__ */ jsx14(
|
|
5612
|
+
SelectionSummaryBar,
|
|
5613
|
+
{
|
|
5614
|
+
summary: selectionSummary,
|
|
5615
|
+
focusedCell,
|
|
5616
|
+
columns: visibleColumns,
|
|
5617
|
+
data: paginatedData,
|
|
5618
|
+
getRowId,
|
|
5619
|
+
currentPage,
|
|
5620
|
+
pageSize
|
|
4651
5621
|
}
|
|
4652
|
-
)
|
|
4653
|
-
showPagination && effectiveTotalItems > 0 && /* @__PURE__ */
|
|
5622
|
+
),
|
|
5623
|
+
showPagination && effectiveTotalItems > 0 && /* @__PURE__ */ jsx14(
|
|
4654
5624
|
Pagination,
|
|
4655
5625
|
{
|
|
4656
5626
|
currentPage,
|
|
@@ -4665,7 +5635,7 @@ function Spreadsheet({
|
|
|
4665
5635
|
onPageSizeChange: handlePageSizeChange
|
|
4666
5636
|
}
|
|
4667
5637
|
),
|
|
4668
|
-
/* @__PURE__ */
|
|
5638
|
+
/* @__PURE__ */ jsx14(
|
|
4669
5639
|
AddCommentModal,
|
|
4670
5640
|
{
|
|
4671
5641
|
isOpen: commentModalCell !== null,
|
|
@@ -4676,7 +5646,7 @@ function Spreadsheet({
|
|
|
4676
5646
|
}
|
|
4677
5647
|
}
|
|
4678
5648
|
),
|
|
4679
|
-
/* @__PURE__ */
|
|
5649
|
+
/* @__PURE__ */ jsx14(
|
|
4680
5650
|
ViewCommentsModal,
|
|
4681
5651
|
{
|
|
4682
5652
|
isOpen: viewCommentsCell !== null,
|
|
@@ -4691,29 +5661,32 @@ function Spreadsheet({
|
|
|
4691
5661
|
onClose: () => setViewCommentsCell(null)
|
|
4692
5662
|
}
|
|
4693
5663
|
),
|
|
4694
|
-
highlightPickerRow !== null && /* @__PURE__ */
|
|
5664
|
+
highlightPickerRow !== null && /* @__PURE__ */ jsx14(
|
|
4695
5665
|
ColorPickerPopover,
|
|
4696
5666
|
{
|
|
4697
5667
|
title: "Highlight Row",
|
|
4698
5668
|
paletteType: "row",
|
|
5669
|
+
recentColors,
|
|
4699
5670
|
onSelectColor: (color) => handleRowHighlightToggle(highlightPickerRow, color),
|
|
4700
5671
|
onClose: () => setHighlightPickerRow(null)
|
|
4701
5672
|
}
|
|
4702
5673
|
),
|
|
4703
|
-
highlightPickerColumn !== null && /* @__PURE__ */
|
|
5674
|
+
highlightPickerColumn !== null && /* @__PURE__ */ jsx14(
|
|
4704
5675
|
ColorPickerPopover,
|
|
4705
5676
|
{
|
|
4706
5677
|
title: highlightPickerColumn === ROW_INDEX_COLUMN_ID ? "Highlight Row Index Column" : `Highlight Column: ${columns.find((c) => c.id === highlightPickerColumn)?.label || ""}`,
|
|
4707
5678
|
paletteType: "column",
|
|
5679
|
+
recentColors,
|
|
4708
5680
|
onSelectColor: (color) => handleColumnHighlightToggle(highlightPickerColumn, color),
|
|
4709
5681
|
onClose: () => setHighlightPickerColumn(null)
|
|
4710
5682
|
}
|
|
4711
5683
|
),
|
|
4712
|
-
highlightPickerCell !== null && /* @__PURE__ */
|
|
5684
|
+
highlightPickerCell !== null && /* @__PURE__ */ jsx14(
|
|
4713
5685
|
ColorPickerPopover,
|
|
4714
5686
|
{
|
|
4715
5687
|
title: "Highlight Cell",
|
|
4716
5688
|
paletteType: "row",
|
|
5689
|
+
recentColors,
|
|
4717
5690
|
onSelectColor: (color) => handleCellHighlightToggle(
|
|
4718
5691
|
highlightPickerCell.rowId,
|
|
4719
5692
|
highlightPickerCell.columnId,
|
|
@@ -4722,7 +5695,7 @@ function Spreadsheet({
|
|
|
4722
5695
|
onClose: () => setHighlightPickerCell(null)
|
|
4723
5696
|
}
|
|
4724
5697
|
),
|
|
4725
|
-
/* @__PURE__ */
|
|
5698
|
+
/* @__PURE__ */ jsx14(
|
|
4726
5699
|
KeyboardShortcutsModal,
|
|
4727
5700
|
{
|
|
4728
5701
|
isOpen: showKeyboardShortcuts,
|
|
@@ -4730,7 +5703,7 @@ function Spreadsheet({
|
|
|
4730
5703
|
shortcuts
|
|
4731
5704
|
}
|
|
4732
5705
|
),
|
|
4733
|
-
/* @__PURE__ */
|
|
5706
|
+
/* @__PURE__ */ jsx14(
|
|
4734
5707
|
SpreadsheetSettingsModal,
|
|
4735
5708
|
{
|
|
4736
5709
|
isOpen: showSettingsModal,
|
|
@@ -4758,12 +5731,14 @@ function Spreadsheet({
|
|
|
4758
5731
|
Spreadsheet.displayName = "Spreadsheet";
|
|
4759
5732
|
export {
|
|
4760
5733
|
ActiveFiltersDisplay,
|
|
5734
|
+
MemoizedSpreadsheetHeader,
|
|
4761
5735
|
RowContextMenu,
|
|
4762
5736
|
Spreadsheet,
|
|
4763
5737
|
MemoizedSpreadsheetCell as SpreadsheetCell,
|
|
4764
5738
|
SpreadsheetFilterDropdown,
|
|
4765
5739
|
SpreadsheetHeader,
|
|
4766
5740
|
SpreadsheetSettingsModal,
|
|
4767
|
-
SpreadsheetToolbar
|
|
5741
|
+
SpreadsheetToolbar,
|
|
5742
|
+
useSpreadsheetDuplicates
|
|
4768
5743
|
};
|
|
4769
5744
|
//# sourceMappingURL=index.mjs.map
|