@xcelsior/ui-spreadsheets 1.2.1 → 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 -1155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2023 -1047
- package/dist/index.mjs.map +1 -1
- package/dist/styles/globals.css +159 -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 -16
- package/src/components/SelectionSummaryBar.tsx +103 -0
- package/src/components/Spreadsheet.stories.tsx +396 -0
- package/src/components/Spreadsheet.tsx +233 -187
- 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.js
CHANGED
|
@@ -31,18 +31,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ActiveFiltersDisplay: () => ActiveFiltersDisplay,
|
|
34
|
+
MemoizedSpreadsheetHeader: () => MemoizedSpreadsheetHeader,
|
|
34
35
|
RowContextMenu: () => RowContextMenu,
|
|
35
36
|
Spreadsheet: () => Spreadsheet,
|
|
36
37
|
SpreadsheetCell: () => MemoizedSpreadsheetCell,
|
|
37
38
|
SpreadsheetFilterDropdown: () => SpreadsheetFilterDropdown,
|
|
38
39
|
SpreadsheetHeader: () => SpreadsheetHeader,
|
|
39
40
|
SpreadsheetSettingsModal: () => SpreadsheetSettingsModal,
|
|
40
|
-
SpreadsheetToolbar: () => SpreadsheetToolbar
|
|
41
|
+
SpreadsheetToolbar: () => SpreadsheetToolbar,
|
|
42
|
+
useSpreadsheetDuplicates: () => useSpreadsheetDuplicates
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(index_exports);
|
|
43
45
|
|
|
44
46
|
// src/components/Spreadsheet.tsx
|
|
45
|
-
var
|
|
47
|
+
var import_react23 = require("react");
|
|
46
48
|
|
|
47
49
|
// ../../../node_modules/.pnpm/react-icons@4.12.0_react@18.3.1/node_modules/react-icons/lib/esm/iconBase.js
|
|
48
50
|
var import_react2 = __toESM(require("react"));
|
|
@@ -136,6 +138,9 @@ function HiCog(props) {
|
|
|
136
138
|
function HiDotsVertical(props) {
|
|
137
139
|
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);
|
|
138
140
|
}
|
|
141
|
+
function HiExclamation(props) {
|
|
142
|
+
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);
|
|
143
|
+
}
|
|
139
144
|
function HiEye(props) {
|
|
140
145
|
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);
|
|
141
146
|
}
|
|
@@ -148,6 +153,9 @@ function HiReply(props) {
|
|
|
148
153
|
function HiSortAscending(props) {
|
|
149
154
|
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);
|
|
150
155
|
}
|
|
156
|
+
function HiSortDescending(props) {
|
|
157
|
+
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);
|
|
158
|
+
}
|
|
151
159
|
function HiViewBoards(props) {
|
|
152
160
|
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);
|
|
153
161
|
}
|
|
@@ -188,165 +196,186 @@ function cn(...inputs) {
|
|
|
188
196
|
}
|
|
189
197
|
|
|
190
198
|
// src/components/SpreadsheetCell.tsx
|
|
191
|
-
var import_react4 = require("react");
|
|
192
|
-
|
|
193
|
-
// src/hooks/useSpreadsheetPinning.ts
|
|
194
199
|
var import_react3 = require("react");
|
|
195
|
-
var
|
|
196
|
-
var
|
|
197
|
-
var
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const [
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
});
|
|
227
|
-
}, []);
|
|
228
|
-
const setPinnedColumnsFromIds = (0, import_react3.useCallback)((columnIds) => {
|
|
229
|
-
const map = /* @__PURE__ */ new Map();
|
|
230
|
-
columnIds.forEach((col) => {
|
|
231
|
-
map.set(col, "left");
|
|
232
|
-
});
|
|
233
|
-
setPinnedColumns(map);
|
|
234
|
-
}, []);
|
|
235
|
-
const handleToggleGroupCollapse = (0, import_react3.useCallback)((groupId) => {
|
|
236
|
-
setCollapsedGroups((prev) => {
|
|
237
|
-
const newSet = new Set(prev);
|
|
238
|
-
if (newSet.has(groupId)) {
|
|
239
|
-
newSet.delete(groupId);
|
|
240
|
-
} else {
|
|
241
|
-
newSet.add(groupId);
|
|
242
|
-
}
|
|
243
|
-
return newSet;
|
|
244
|
-
});
|
|
245
|
-
}, []);
|
|
246
|
-
const visibleColumns = (0, import_react3.useMemo)(() => {
|
|
247
|
-
if (!columns || !Array.isArray(columns) || columns.length === 0) {
|
|
248
|
-
return [];
|
|
249
|
-
}
|
|
250
|
-
let result = [...columns];
|
|
251
|
-
if (columnGroups && Array.isArray(columnGroups)) {
|
|
252
|
-
result = result.filter((column) => {
|
|
253
|
-
const group = columnGroups.find((g) => g.columns.includes(column.id));
|
|
254
|
-
if (!group) return true;
|
|
255
|
-
if (!collapsedGroups.has(group.id)) return true;
|
|
256
|
-
return pinnedColumns.has(column.id);
|
|
200
|
+
var import_react_dom = require("react-dom");
|
|
201
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
202
|
+
var cellPaddingCompact = "px-1.5 py-0.5";
|
|
203
|
+
var cellPaddingNormal = "px-2.5 py-1.5";
|
|
204
|
+
var AutocompleteEditor = ({ value, column, compactMode, onConfirm, onCancel }) => {
|
|
205
|
+
const getLabel = (0, import_react3.useCallback)(
|
|
206
|
+
(val) => {
|
|
207
|
+
if (val === null || val === void 0 || val === "") return "";
|
|
208
|
+
if (column.getOptionLabel) return column.getOptionLabel(val);
|
|
209
|
+
const match = column.autocompleteOptions?.find((o) => o.value === val);
|
|
210
|
+
return match ? match.label : String(val);
|
|
211
|
+
},
|
|
212
|
+
[column]
|
|
213
|
+
);
|
|
214
|
+
const [searchText, setSearchText] = (0, import_react3.useState)(() => getLabel(value));
|
|
215
|
+
const [filteredOptions, setFilteredOptions] = (0, import_react3.useState)(
|
|
216
|
+
column.autocompleteOptions ?? []
|
|
217
|
+
);
|
|
218
|
+
const [focusedIndex, setFocusedIndex] = (0, import_react3.useState)(-1);
|
|
219
|
+
const [isOpen, setIsOpen] = (0, import_react3.useState)(true);
|
|
220
|
+
const [dropdownPos, setDropdownPos] = (0, import_react3.useState)(null);
|
|
221
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
222
|
+
const dropdownRef = (0, import_react3.useRef)(null);
|
|
223
|
+
const debounceRef = (0, import_react3.useRef)(null);
|
|
224
|
+
const updateDropdownPos = (0, import_react3.useCallback)(() => {
|
|
225
|
+
if (inputRef.current) {
|
|
226
|
+
const rect = inputRef.current.getBoundingClientRect();
|
|
227
|
+
setDropdownPos({
|
|
228
|
+
top: rect.bottom + 2,
|
|
229
|
+
left: rect.left,
|
|
230
|
+
width: rect.width
|
|
257
231
|
});
|
|
258
232
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const pinSide = pinnedColumns.get(column.id);
|
|
272
|
-
if (pinSide === "left") {
|
|
273
|
-
leftPinned.push(column);
|
|
274
|
-
} else if (pinSide === "right") {
|
|
275
|
-
rightPinned.push(column);
|
|
276
|
-
} else {
|
|
277
|
-
unpinned.push(column);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
leftPinned.sort((a, b) => pinnedLeftIds.indexOf(a.id) - pinnedLeftIds.indexOf(b.id));
|
|
281
|
-
rightPinned.sort((a, b) => pinnedRightIds.indexOf(a.id) - pinnedRightIds.indexOf(b.id));
|
|
282
|
-
return [...leftPinned, ...unpinned, ...rightPinned];
|
|
283
|
-
}, [columns, columnGroups, collapsedGroups, pinnedColumns]);
|
|
284
|
-
const getColumnLeftOffset = (0, import_react3.useCallback)(
|
|
285
|
-
(columnId) => {
|
|
286
|
-
if (columnId === ROW_INDEX_COLUMN_ID) {
|
|
287
|
-
return 0;
|
|
288
|
-
}
|
|
289
|
-
const pinnedLeft = Array.from(pinnedColumns.entries()).filter(([id, side]) => side === "left" && id !== ROW_INDEX_COLUMN_ID).map(([id]) => id);
|
|
290
|
-
const index = pinnedLeft.indexOf(columnId);
|
|
291
|
-
const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
292
|
-
const baseOffset = showRowIndex && isRowIndexPinnedNow ? ROW_INDEX_COLUMN_WIDTH : 0;
|
|
293
|
-
if (index === -1) return baseOffset;
|
|
294
|
-
let offset = baseOffset;
|
|
295
|
-
for (let i = 0; i < index; i++) {
|
|
296
|
-
const col = columns.find((c) => c.id === pinnedLeft[i]);
|
|
297
|
-
const configuredWidth = col?.minWidth || col?.width || MIN_PINNED_COLUMN_WIDTH;
|
|
298
|
-
offset += Math.max(configuredWidth, MIN_PINNED_COLUMN_WIDTH);
|
|
299
|
-
}
|
|
300
|
-
return offset;
|
|
233
|
+
}, []);
|
|
234
|
+
(0, import_react3.useEffect)(() => {
|
|
235
|
+
inputRef.current?.focus();
|
|
236
|
+
inputRef.current?.select();
|
|
237
|
+
updateDropdownPos();
|
|
238
|
+
}, [updateDropdownPos]);
|
|
239
|
+
const filterClientSide = (0, import_react3.useCallback)(
|
|
240
|
+
(term) => {
|
|
241
|
+
const opts = column.autocompleteOptions ?? [];
|
|
242
|
+
if (!term.trim()) return opts;
|
|
243
|
+
const lower = term.toLowerCase();
|
|
244
|
+
return opts.filter((o) => o.label.toLowerCase().includes(lower));
|
|
301
245
|
},
|
|
302
|
-
[
|
|
246
|
+
[column.autocompleteOptions]
|
|
303
247
|
);
|
|
304
|
-
const
|
|
305
|
-
(
|
|
306
|
-
|
|
248
|
+
const handleSearchChange = (0, import_react3.useCallback)(
|
|
249
|
+
(e) => {
|
|
250
|
+
const term = e.target.value;
|
|
251
|
+
setSearchText(term);
|
|
252
|
+
setFocusedIndex(-1);
|
|
253
|
+
setIsOpen(true);
|
|
254
|
+
updateDropdownPos();
|
|
255
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
256
|
+
debounceRef.current = setTimeout(async () => {
|
|
257
|
+
if (column.onAutocompleteSearch) {
|
|
258
|
+
try {
|
|
259
|
+
const results = await column.onAutocompleteSearch(term);
|
|
260
|
+
setFilteredOptions(results);
|
|
261
|
+
} catch {
|
|
262
|
+
setFilteredOptions([]);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
setFilteredOptions(filterClientSide(term));
|
|
266
|
+
}
|
|
267
|
+
}, 300);
|
|
307
268
|
},
|
|
308
|
-
[
|
|
269
|
+
[column, filterClientSide, updateDropdownPos]
|
|
309
270
|
);
|
|
310
|
-
|
|
311
|
-
(
|
|
312
|
-
|
|
271
|
+
(0, import_react3.useEffect)(() => {
|
|
272
|
+
return () => {
|
|
273
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
274
|
+
};
|
|
275
|
+
}, []);
|
|
276
|
+
const selectOption = (0, import_react3.useCallback)(
|
|
277
|
+
(option) => {
|
|
278
|
+
setSearchText(option.label);
|
|
279
|
+
setIsOpen(false);
|
|
280
|
+
onConfirm?.(option.value);
|
|
313
281
|
},
|
|
314
|
-
[
|
|
282
|
+
[onConfirm]
|
|
315
283
|
);
|
|
316
|
-
const
|
|
317
|
-
(
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
284
|
+
const handleKeyDown = (0, import_react3.useCallback)(
|
|
285
|
+
(e) => {
|
|
286
|
+
const visibleOptions2 = filteredOptions.slice(0, 8);
|
|
287
|
+
if (e.key === "ArrowDown") {
|
|
288
|
+
e.preventDefault();
|
|
289
|
+
setFocusedIndex((prev) => Math.min(prev + 1, visibleOptions2.length - 1));
|
|
290
|
+
} else if (e.key === "ArrowUp") {
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
setFocusedIndex((prev) => Math.max(prev - 1, -1));
|
|
293
|
+
} else if (e.key === "Enter") {
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
if (focusedIndex >= 0 && visibleOptions2[focusedIndex]) {
|
|
296
|
+
selectOption(visibleOptions2[focusedIndex]);
|
|
297
|
+
} else {
|
|
298
|
+
onConfirm?.(value);
|
|
299
|
+
}
|
|
300
|
+
} else if (e.key === "Escape") {
|
|
301
|
+
e.preventDefault();
|
|
302
|
+
e.stopPropagation();
|
|
303
|
+
setIsOpen(false);
|
|
304
|
+
onCancel?.();
|
|
326
305
|
}
|
|
327
|
-
return offset;
|
|
328
306
|
},
|
|
329
|
-
[
|
|
307
|
+
[filteredOptions, focusedIndex, selectOption, onConfirm, onCancel, value]
|
|
330
308
|
);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
309
|
+
const handleBlur = (0, import_react3.useCallback)(
|
|
310
|
+
(e) => {
|
|
311
|
+
setTimeout(() => {
|
|
312
|
+
if (dropdownRef.current && dropdownRef.current.contains(document.activeElement)) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
setIsOpen(false);
|
|
316
|
+
onConfirm?.(value);
|
|
317
|
+
}, 150);
|
|
318
|
+
},
|
|
319
|
+
[onConfirm, value]
|
|
320
|
+
);
|
|
321
|
+
const visibleOptions = filteredOptions.slice(0, 8);
|
|
322
|
+
const dropdown = isOpen && visibleOptions.length > 0 && dropdownPos ? (0, import_react_dom.createPortal)(
|
|
323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
324
|
+
"div",
|
|
325
|
+
{
|
|
326
|
+
ref: dropdownRef,
|
|
327
|
+
style: {
|
|
328
|
+
position: "fixed",
|
|
329
|
+
top: dropdownPos.top,
|
|
330
|
+
left: dropdownPos.left,
|
|
331
|
+
width: Math.max(dropdownPos.width, 180),
|
|
332
|
+
zIndex: 50
|
|
333
|
+
},
|
|
334
|
+
className: "bg-white border border-gray-200 rounded-md shadow-lg max-h-48 overflow-y-auto",
|
|
335
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
336
|
+
children: visibleOptions.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
337
|
+
"div",
|
|
338
|
+
{
|
|
339
|
+
onMouseDown: (e) => {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
selectOption(option);
|
|
342
|
+
},
|
|
343
|
+
className: cn(
|
|
344
|
+
"px-3 py-1.5 cursor-pointer",
|
|
345
|
+
compactMode ? "text-xs" : "text-sm",
|
|
346
|
+
index === focusedIndex ? "bg-blue-100" : "hover:bg-blue-50"
|
|
347
|
+
),
|
|
348
|
+
children: option.label
|
|
349
|
+
},
|
|
350
|
+
`${option.value}-${index}`
|
|
351
|
+
))
|
|
352
|
+
}
|
|
353
|
+
),
|
|
354
|
+
document.body
|
|
355
|
+
) : null;
|
|
356
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
357
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
358
|
+
"input",
|
|
359
|
+
{
|
|
360
|
+
ref: inputRef,
|
|
361
|
+
type: "text",
|
|
362
|
+
value: searchText,
|
|
363
|
+
onChange: handleSearchChange,
|
|
364
|
+
onKeyDown: handleKeyDown,
|
|
365
|
+
onBlur: handleBlur,
|
|
366
|
+
autoComplete: "off",
|
|
367
|
+
autoCorrect: "off",
|
|
368
|
+
autoCapitalize: "off",
|
|
369
|
+
spellCheck: false,
|
|
370
|
+
className: cn(
|
|
371
|
+
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
372
|
+
compactMode ? "text-xs" : "text-sm"
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
),
|
|
376
|
+
dropdown
|
|
377
|
+
] });
|
|
378
|
+
};
|
|
350
379
|
var SpreadsheetCell = ({
|
|
351
380
|
value,
|
|
352
381
|
column,
|
|
@@ -361,6 +390,7 @@ var SpreadsheetCell = ({
|
|
|
361
390
|
isRowSelected = false,
|
|
362
391
|
isRowHovered = false,
|
|
363
392
|
highlightColor,
|
|
393
|
+
isDuplicate = false,
|
|
364
394
|
hasComments = false,
|
|
365
395
|
unresolvedCommentCount = 0,
|
|
366
396
|
isCopied = false,
|
|
@@ -369,7 +399,11 @@ var SpreadsheetCell = ({
|
|
|
369
399
|
pinSide,
|
|
370
400
|
leftOffset = 0,
|
|
371
401
|
rightOffset = 0,
|
|
402
|
+
isOddRow = false,
|
|
403
|
+
resolvedWidth,
|
|
404
|
+
pinnedZIndex,
|
|
372
405
|
onClick,
|
|
406
|
+
onDoubleClick,
|
|
373
407
|
onMouseDown,
|
|
374
408
|
onMouseEnter,
|
|
375
409
|
onChange,
|
|
@@ -380,17 +414,17 @@ var SpreadsheetCell = ({
|
|
|
380
414
|
onViewComments,
|
|
381
415
|
className
|
|
382
416
|
}) => {
|
|
383
|
-
const [localValue, setLocalValue] = (0,
|
|
384
|
-
const inputRef = (0,
|
|
385
|
-
const selectRef = (0,
|
|
386
|
-
(0,
|
|
417
|
+
const [localValue, setLocalValue] = (0, import_react3.useState)(value);
|
|
418
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
419
|
+
const selectRef = (0, import_react3.useRef)(null);
|
|
420
|
+
(0, import_react3.useEffect)(() => {
|
|
387
421
|
setLocalValue(value);
|
|
388
422
|
}, [value]);
|
|
389
|
-
(0,
|
|
423
|
+
(0, import_react3.useEffect)(() => {
|
|
390
424
|
if (isEditing) {
|
|
391
425
|
if (column.type === "select") {
|
|
392
426
|
selectRef.current?.focus();
|
|
393
|
-
} else {
|
|
427
|
+
} else if (column.type !== "autocomplete") {
|
|
394
428
|
inputRef.current?.focus();
|
|
395
429
|
inputRef.current?.select();
|
|
396
430
|
}
|
|
@@ -409,8 +443,10 @@ var SpreadsheetCell = ({
|
|
|
409
443
|
};
|
|
410
444
|
const getBackgroundColor = () => {
|
|
411
445
|
if (highlightColor) return highlightColor;
|
|
446
|
+
if (isDuplicate) return "rgb(254 202 202)";
|
|
412
447
|
if (isRowSelected) return "rgb(219 234 254)";
|
|
413
448
|
if (isRowHovered) return "rgb(243 244 246)";
|
|
449
|
+
if (isOddRow) return "rgb(249 250 251)";
|
|
414
450
|
return "white";
|
|
415
451
|
};
|
|
416
452
|
const handleCheckboxChange = (e) => {
|
|
@@ -447,12 +483,29 @@ var SpreadsheetCell = ({
|
|
|
447
483
|
if (column.type === "number") {
|
|
448
484
|
return typeof value === "number" ? value.toLocaleString() : value;
|
|
449
485
|
}
|
|
486
|
+
if (column.type === "autocomplete") {
|
|
487
|
+
if (column.getOptionLabel) return column.getOptionLabel(value, row);
|
|
488
|
+
const match = column.autocompleteOptions?.find((o) => o.value === value);
|
|
489
|
+
return match ? match.label : String(value);
|
|
490
|
+
}
|
|
450
491
|
return String(value);
|
|
451
492
|
};
|
|
452
493
|
const renderEditInput = () => {
|
|
453
494
|
if (column.type === "checkbox") {
|
|
454
495
|
return renderContent();
|
|
455
496
|
}
|
|
497
|
+
if (column.type === "autocomplete") {
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
499
|
+
AutocompleteEditor,
|
|
500
|
+
{
|
|
501
|
+
value: localValue,
|
|
502
|
+
column,
|
|
503
|
+
compactMode,
|
|
504
|
+
onConfirm,
|
|
505
|
+
onCancel
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
}
|
|
456
509
|
if (column.type === "select" && column.options) {
|
|
457
510
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
458
511
|
"select",
|
|
@@ -465,10 +518,11 @@ var SpreadsheetCell = ({
|
|
|
465
518
|
onConfirm?.(newValue);
|
|
466
519
|
},
|
|
467
520
|
onKeyDown: handleKeyDown,
|
|
468
|
-
|
|
521
|
+
onClick: (e) => e.stopPropagation(),
|
|
522
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
469
523
|
className: cn(
|
|
470
524
|
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
471
|
-
compactMode ? "text-
|
|
525
|
+
compactMode ? "text-xs" : "text-sm"
|
|
472
526
|
),
|
|
473
527
|
children: column.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option, children: option }, option))
|
|
474
528
|
}
|
|
@@ -493,7 +547,7 @@ var SpreadsheetCell = ({
|
|
|
493
547
|
spellCheck: false,
|
|
494
548
|
className: cn(
|
|
495
549
|
"w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm",
|
|
496
|
-
compactMode ? "text-
|
|
550
|
+
compactMode ? "text-xs" : "text-sm"
|
|
497
551
|
)
|
|
498
552
|
}
|
|
499
553
|
);
|
|
@@ -517,60 +571,50 @@ var SpreadsheetCell = ({
|
|
|
517
571
|
}
|
|
518
572
|
const selectionBorderStyles = {};
|
|
519
573
|
if (isInSelection && selectionEdge) {
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
if (selectionEdge.right) {
|
|
527
|
-
|
|
528
|
-
selectionBorderStyles.
|
|
529
|
-
}
|
|
530
|
-
if (selectionEdge.bottom) {
|
|
531
|
-
selectionBorderStyles.borderBottomColor = borderColor;
|
|
532
|
-
selectionBorderStyles.borderBottomWidth = borderWidth;
|
|
533
|
-
}
|
|
534
|
-
if (selectionEdge.left) {
|
|
535
|
-
selectionBorderStyles.borderLeftColor = borderColor;
|
|
536
|
-
selectionBorderStyles.borderLeftWidth = borderWidth;
|
|
574
|
+
const color = "rgb(59 130 246)";
|
|
575
|
+
const w = "2px";
|
|
576
|
+
const shadows = [];
|
|
577
|
+
if (selectionEdge.top) shadows.push(`inset 0 ${w} 0 0 ${color}`);
|
|
578
|
+
if (selectionEdge.bottom) shadows.push(`inset 0 -${w} 0 0 ${color}`);
|
|
579
|
+
if (selectionEdge.left) shadows.push(`inset ${w} 0 0 0 ${color}`);
|
|
580
|
+
if (selectionEdge.right) shadows.push(`inset -${w} 0 0 0 ${color}`);
|
|
581
|
+
if (shadows.length > 0) {
|
|
582
|
+
selectionBorderStyles.boxShadow = shadows.join(", ");
|
|
537
583
|
}
|
|
538
584
|
}
|
|
539
585
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
540
586
|
"td",
|
|
541
587
|
{
|
|
542
588
|
onClick,
|
|
589
|
+
onDoubleClick,
|
|
543
590
|
onMouseDown,
|
|
544
591
|
onMouseEnter,
|
|
545
592
|
onKeyDown: handleCellKeyDown,
|
|
546
593
|
"data-cell-id": `${rowId}-${column.id}`,
|
|
594
|
+
"data-column-id": column.id,
|
|
547
595
|
className: cn(
|
|
548
|
-
"border border-gray-200 group cursor-pointer transition-colors select-none",
|
|
549
|
-
compactMode ? "text-
|
|
596
|
+
"border border-gray-200 group cursor-pointer transition-colors select-none relative",
|
|
597
|
+
compactMode ? "text-xs" : "text-sm",
|
|
550
598
|
cellPadding,
|
|
551
599
|
column.align === "right" && "text-right",
|
|
552
600
|
column.align === "center" && "text-center",
|
|
553
601
|
isCopied && "animate-pulse",
|
|
554
602
|
isFocused && !isInSelection && "ring-2 ring-blue-500 ring-inset",
|
|
555
603
|
isInSelection && "bg-blue-50",
|
|
556
|
-
isPinned
|
|
604
|
+
!isPinned && "z-0",
|
|
557
605
|
className
|
|
558
606
|
),
|
|
559
607
|
style: {
|
|
560
608
|
backgroundColor: isInSelection ? "rgb(239 246 255)" : getBackgroundColor(),
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
),
|
|
569
|
-
maxWidth: Math.max(
|
|
570
|
-
column.minWidth || column.width || MIN_PINNED_COLUMN_WIDTH,
|
|
571
|
-
MIN_PINNED_COLUMN_WIDTH
|
|
572
|
-
)
|
|
609
|
+
// Always set explicit width to prevent layout shift on selection/re-render
|
|
610
|
+
...resolvedWidth ? {
|
|
611
|
+
width: resolvedWidth,
|
|
612
|
+
minWidth: resolvedWidth
|
|
613
|
+
} : {
|
|
614
|
+
width: column.width || column.minWidth,
|
|
615
|
+
minWidth: column.minWidth || column.width
|
|
573
616
|
},
|
|
617
|
+
...isPinned && pinnedZIndex !== void 0 && { zIndex: pinnedZIndex },
|
|
574
618
|
...positionStyles,
|
|
575
619
|
...selectionBorderStyles
|
|
576
620
|
},
|
|
@@ -580,12 +624,13 @@ var SpreadsheetCell = ({
|
|
|
580
624
|
{
|
|
581
625
|
className: cn(
|
|
582
626
|
"flex-1 truncate",
|
|
583
|
-
isEditable && "cursor-text bg-blue-50
|
|
627
|
+
isEditable && "cursor-text bg-blue-50 rounded px-1"
|
|
584
628
|
),
|
|
585
629
|
title: String(value ?? ""),
|
|
586
630
|
children: renderContent()
|
|
587
631
|
}
|
|
588
632
|
),
|
|
633
|
+
isEditable && (column.type === "select" || column.type === "autocomplete") && !isEditing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HiChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400" }),
|
|
589
634
|
!isInSelection && !isFocused && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
590
635
|
column.highlightable !== false && onHighlight && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
591
636
|
"button",
|
|
@@ -642,7 +687,7 @@ var SpreadsheetCell = ({
|
|
|
642
687
|
);
|
|
643
688
|
};
|
|
644
689
|
SpreadsheetCell.displayName = "SpreadsheetCell";
|
|
645
|
-
var MemoizedSpreadsheetCell = (0,
|
|
690
|
+
var MemoizedSpreadsheetCell = (0, import_react3.memo)(SpreadsheetCell, (prevProps, nextProps) => {
|
|
646
691
|
if (prevProps.isEditing !== nextProps.isEditing) return false;
|
|
647
692
|
if (prevProps.isFocused !== nextProps.isFocused) return false;
|
|
648
693
|
if (prevProps.value !== nextProps.value) return false;
|
|
@@ -650,6 +695,7 @@ var MemoizedSpreadsheetCell = (0, import_react4.memo)(SpreadsheetCell, (prevProp
|
|
|
650
695
|
if (prevProps.isRowSelected !== nextProps.isRowSelected) return false;
|
|
651
696
|
if (prevProps.isRowHovered !== nextProps.isRowHovered) return false;
|
|
652
697
|
if (prevProps.highlightColor !== nextProps.highlightColor) return false;
|
|
698
|
+
if (prevProps.isDuplicate !== nextProps.isDuplicate) return false;
|
|
653
699
|
if (prevProps.hasComments !== nextProps.hasComments) return false;
|
|
654
700
|
if (prevProps.unresolvedCommentCount !== nextProps.unresolvedCommentCount) return false;
|
|
655
701
|
if (prevProps.isCopied !== nextProps.isCopied) return false;
|
|
@@ -657,6 +703,7 @@ var MemoizedSpreadsheetCell = (0, import_react4.memo)(SpreadsheetCell, (prevProp
|
|
|
657
703
|
if (prevProps.leftOffset !== nextProps.leftOffset) return false;
|
|
658
704
|
if (prevProps.rightOffset !== nextProps.rightOffset) return false;
|
|
659
705
|
if (prevProps.compactMode !== nextProps.compactMode) return false;
|
|
706
|
+
if (prevProps.isOddRow !== nextProps.isOddRow) return false;
|
|
660
707
|
const prevEdge = prevProps.selectionEdge;
|
|
661
708
|
const nextEdge = nextProps.selectionEdge;
|
|
662
709
|
if (prevEdge !== nextEdge) {
|
|
@@ -671,7 +718,8 @@ var MemoizedSpreadsheetCell = (0, import_react4.memo)(SpreadsheetCell, (prevProp
|
|
|
671
718
|
MemoizedSpreadsheetCell.displayName = "MemoizedSpreadsheetCell";
|
|
672
719
|
|
|
673
720
|
// src/components/SpreadsheetFilterDropdown.tsx
|
|
674
|
-
var
|
|
721
|
+
var import_react4 = require("react");
|
|
722
|
+
var import_react_dom2 = require("react-dom");
|
|
675
723
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
676
724
|
var TEXT_OPERATORS = [
|
|
677
725
|
{ value: "contains", label: "Contains" },
|
|
@@ -710,59 +758,121 @@ var DATE_OPERATORS = [
|
|
|
710
758
|
{ value: "isEmpty", label: "Is empty" },
|
|
711
759
|
{ value: "isNotEmpty", label: "Is not empty" }
|
|
712
760
|
];
|
|
761
|
+
var DROPDOWN_WIDTH = 256;
|
|
762
|
+
var DROPDOWN_HEIGHT = 340;
|
|
763
|
+
var ARROW_SIZE = 6;
|
|
713
764
|
var SpreadsheetFilterDropdown = ({
|
|
714
765
|
column,
|
|
715
766
|
filter,
|
|
716
767
|
onFilterChange,
|
|
717
768
|
onClose,
|
|
718
|
-
className
|
|
769
|
+
className,
|
|
770
|
+
triggerRef,
|
|
771
|
+
hasDuplicateCheck = false
|
|
719
772
|
}) => {
|
|
720
|
-
const [
|
|
773
|
+
const [showDuplicatesOnly, setShowDuplicatesOnly] = (0, import_react4.useState)(
|
|
774
|
+
filter?.showDuplicatesOnly ?? false
|
|
775
|
+
);
|
|
776
|
+
const [textOperator, setTextOperator] = (0, import_react4.useState)(
|
|
721
777
|
filter?.textCondition?.operator || "contains"
|
|
722
778
|
);
|
|
723
|
-
const [textValue, setTextValue] = (0,
|
|
724
|
-
const [numberOperator, setNumberOperator] = (0,
|
|
779
|
+
const [textValue, setTextValue] = (0, import_react4.useState)(filter?.textCondition?.value || "");
|
|
780
|
+
const [numberOperator, setNumberOperator] = (0, import_react4.useState)(
|
|
725
781
|
filter?.numberCondition?.operator || "equals"
|
|
726
782
|
);
|
|
727
|
-
const [numberValue, setNumberValue] = (0,
|
|
783
|
+
const [numberValue, setNumberValue] = (0, import_react4.useState)(
|
|
728
784
|
filter?.numberCondition?.value?.toString() || ""
|
|
729
785
|
);
|
|
730
|
-
const [numberValueTo, setNumberValueTo] = (0,
|
|
786
|
+
const [numberValueTo, setNumberValueTo] = (0, import_react4.useState)(
|
|
731
787
|
filter?.numberCondition?.valueTo?.toString() || ""
|
|
732
788
|
);
|
|
733
|
-
const [dateOperator, setDateOperator] = (0,
|
|
789
|
+
const [dateOperator, setDateOperator] = (0, import_react4.useState)(
|
|
734
790
|
filter?.dateCondition?.operator || "equals"
|
|
735
791
|
);
|
|
736
|
-
const [dateValue, setDateValue] = (0,
|
|
737
|
-
const [dateValueTo, setDateValueTo] = (0,
|
|
738
|
-
const dropdownRef = (0,
|
|
792
|
+
const [dateValue, setDateValue] = (0, import_react4.useState)(filter?.dateCondition?.value || "");
|
|
793
|
+
const [dateValueTo, setDateValueTo] = (0, import_react4.useState)(filter?.dateCondition?.valueTo || "");
|
|
794
|
+
const dropdownRef = (0, import_react4.useRef)(null);
|
|
795
|
+
const [position, setPosition] = (0, import_react4.useState)({ top: 0, left: 0, arrowLeft: DROPDOWN_WIDTH / 2, flippedUp: false });
|
|
739
796
|
const isNumeric = column.type === "number";
|
|
740
797
|
const isDate = column.type === "date";
|
|
741
|
-
(0,
|
|
798
|
+
const updatePosition = (0, import_react4.useCallback)(() => {
|
|
799
|
+
if (!triggerRef?.current) return;
|
|
800
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
801
|
+
let top = rect.bottom + 4;
|
|
802
|
+
let flippedUp = false;
|
|
803
|
+
if (top + DROPDOWN_HEIGHT > window.innerHeight) {
|
|
804
|
+
top = rect.top - DROPDOWN_HEIGHT - 4;
|
|
805
|
+
flippedUp = true;
|
|
806
|
+
}
|
|
807
|
+
let left = rect.left;
|
|
808
|
+
if (left + DROPDOWN_WIDTH > window.innerWidth) {
|
|
809
|
+
left = window.innerWidth - DROPDOWN_WIDTH - 8;
|
|
810
|
+
}
|
|
811
|
+
if (left < 8) {
|
|
812
|
+
left = 8;
|
|
813
|
+
}
|
|
814
|
+
const anchorCenter = rect.left + rect.width / 2;
|
|
815
|
+
const arrowLeft = Math.min(
|
|
816
|
+
Math.max(anchorCenter - left, ARROW_SIZE + 4),
|
|
817
|
+
DROPDOWN_WIDTH - ARROW_SIZE - 4
|
|
818
|
+
);
|
|
819
|
+
setPosition({ top, left, arrowLeft, flippedUp });
|
|
820
|
+
}, [triggerRef]);
|
|
821
|
+
(0, import_react4.useEffect)(() => {
|
|
822
|
+
if (!triggerRef?.current) return;
|
|
823
|
+
updatePosition();
|
|
824
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
825
|
+
window.addEventListener("resize", updatePosition);
|
|
826
|
+
return () => {
|
|
827
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
828
|
+
window.removeEventListener("resize", updatePosition);
|
|
829
|
+
};
|
|
830
|
+
}, [updatePosition, triggerRef]);
|
|
831
|
+
(0, import_react4.useEffect)(() => {
|
|
742
832
|
const handleClickOutside = (event) => {
|
|
743
|
-
|
|
833
|
+
const target = event.target;
|
|
834
|
+
if (dropdownRef.current && !dropdownRef.current.contains(target)) {
|
|
835
|
+
if (triggerRef?.current?.contains(target)) return;
|
|
744
836
|
onClose();
|
|
745
837
|
}
|
|
746
838
|
};
|
|
747
|
-
|
|
748
|
-
|
|
839
|
+
const rafId = requestAnimationFrame(() => {
|
|
840
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
841
|
+
});
|
|
842
|
+
return () => {
|
|
843
|
+
cancelAnimationFrame(rafId);
|
|
844
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
845
|
+
};
|
|
846
|
+
}, [onClose, triggerRef]);
|
|
847
|
+
(0, import_react4.useEffect)(() => {
|
|
848
|
+
const handleKeyDown = (event) => {
|
|
849
|
+
if (event.key === "Escape") {
|
|
850
|
+
onClose();
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
854
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
749
855
|
}, [onClose]);
|
|
750
856
|
const handleApplyFilter = () => {
|
|
751
857
|
let newFilter;
|
|
752
858
|
if (isNumeric) {
|
|
753
859
|
const needsValue = !["isEmpty", "isNotEmpty"].includes(numberOperator);
|
|
754
|
-
if (needsValue && !numberValue) {
|
|
860
|
+
if (needsValue && !numberValue && !showDuplicatesOnly) {
|
|
755
861
|
onFilterChange(void 0);
|
|
756
862
|
onClose();
|
|
757
863
|
return;
|
|
758
864
|
}
|
|
759
865
|
newFilter = {
|
|
760
|
-
numberCondition: {
|
|
866
|
+
numberCondition: needsValue || !numberValue ? {
|
|
761
867
|
operator: numberOperator,
|
|
762
868
|
value: numberValue ? parseFloat(numberValue) : void 0,
|
|
763
869
|
valueTo: numberValueTo ? parseFloat(numberValueTo) : void 0
|
|
764
|
-
}
|
|
870
|
+
} : void 0,
|
|
871
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
765
872
|
};
|
|
873
|
+
if (!newFilter.numberCondition && !newFilter.showDuplicatesOnly) {
|
|
874
|
+
newFilter = void 0;
|
|
875
|
+
}
|
|
766
876
|
} else if (isDate) {
|
|
767
877
|
const needsValue = ![
|
|
768
878
|
"isEmpty",
|
|
@@ -775,7 +885,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
775
885
|
"lastMonth",
|
|
776
886
|
"thisYear"
|
|
777
887
|
].includes(dateOperator);
|
|
778
|
-
if (needsValue && !dateValue) {
|
|
888
|
+
if (needsValue && !dateValue && !showDuplicatesOnly) {
|
|
779
889
|
onFilterChange(void 0);
|
|
780
890
|
onClose();
|
|
781
891
|
return;
|
|
@@ -785,26 +895,32 @@ var SpreadsheetFilterDropdown = ({
|
|
|
785
895
|
operator: dateOperator,
|
|
786
896
|
value: dateValue || void 0,
|
|
787
897
|
valueTo: dateValueTo || void 0
|
|
788
|
-
}
|
|
898
|
+
},
|
|
899
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
789
900
|
};
|
|
790
901
|
} else {
|
|
791
902
|
const needsValue = !["isEmpty", "isNotEmpty"].includes(textOperator);
|
|
792
|
-
if (needsValue && !textValue) {
|
|
903
|
+
if (needsValue && !textValue && !showDuplicatesOnly) {
|
|
793
904
|
onFilterChange(void 0);
|
|
794
905
|
onClose();
|
|
795
906
|
return;
|
|
796
907
|
}
|
|
797
908
|
newFilter = {
|
|
798
|
-
textCondition: {
|
|
909
|
+
textCondition: needsValue || !textValue ? {
|
|
799
910
|
operator: textOperator,
|
|
800
911
|
value: textValue || void 0
|
|
801
|
-
}
|
|
912
|
+
} : void 0,
|
|
913
|
+
...showDuplicatesOnly && { showDuplicatesOnly: true }
|
|
802
914
|
};
|
|
915
|
+
if (!newFilter.textCondition && !newFilter.showDuplicatesOnly) {
|
|
916
|
+
newFilter = void 0;
|
|
917
|
+
}
|
|
803
918
|
}
|
|
804
919
|
onFilterChange(newFilter);
|
|
805
920
|
onClose();
|
|
806
921
|
};
|
|
807
922
|
const handleClearFilter = () => {
|
|
923
|
+
setShowDuplicatesOnly(false);
|
|
808
924
|
setTextValue("");
|
|
809
925
|
setNumberValue("");
|
|
810
926
|
setNumberValueTo("");
|
|
@@ -813,6 +929,12 @@ var SpreadsheetFilterDropdown = ({
|
|
|
813
929
|
onFilterChange(void 0);
|
|
814
930
|
onClose();
|
|
815
931
|
};
|
|
932
|
+
const handleFilterKeyDown = (e) => {
|
|
933
|
+
if (e.key === "Enter") {
|
|
934
|
+
e.preventDefault();
|
|
935
|
+
handleApplyFilter();
|
|
936
|
+
}
|
|
937
|
+
};
|
|
816
938
|
const textNeedsValue = !["isEmpty", "isNotEmpty"].includes(textOperator);
|
|
817
939
|
const numberNeedsValue = !["isEmpty", "isNotEmpty"].includes(numberOperator);
|
|
818
940
|
const dateNeedsValue = ![
|
|
@@ -826,20 +948,57 @@ var SpreadsheetFilterDropdown = ({
|
|
|
826
948
|
"lastMonth",
|
|
827
949
|
"thisYear"
|
|
828
950
|
].includes(dateOperator);
|
|
829
|
-
|
|
951
|
+
const dropdownContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
830
952
|
"div",
|
|
831
953
|
{
|
|
832
954
|
ref: dropdownRef,
|
|
833
955
|
className: cn(
|
|
834
|
-
"
|
|
956
|
+
"bg-white border border-gray-200 shadow-lg rounded-lg w-64 overflow-hidden flex flex-col",
|
|
957
|
+
!triggerRef && "absolute top-full left-0 mt-1 z-[100]",
|
|
835
958
|
className
|
|
836
959
|
),
|
|
960
|
+
style: triggerRef ? { position: "fixed", top: position.top, left: position.left, zIndex: 9999 } : void 0,
|
|
837
961
|
onClick: (e) => e.stopPropagation(),
|
|
838
962
|
children: [
|
|
963
|
+
triggerRef && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
964
|
+
"div",
|
|
965
|
+
{
|
|
966
|
+
className: "absolute pointer-events-none",
|
|
967
|
+
style: position.flippedUp ? {
|
|
968
|
+
bottom: -ARROW_SIZE,
|
|
969
|
+
left: position.arrowLeft - ARROW_SIZE,
|
|
970
|
+
width: 0,
|
|
971
|
+
height: 0,
|
|
972
|
+
borderLeft: `${ARROW_SIZE}px solid transparent`,
|
|
973
|
+
borderRight: `${ARROW_SIZE}px solid transparent`,
|
|
974
|
+
borderTop: `${ARROW_SIZE}px solid #e5e7eb`
|
|
975
|
+
} : {
|
|
976
|
+
top: -ARROW_SIZE,
|
|
977
|
+
left: position.arrowLeft - ARROW_SIZE,
|
|
978
|
+
width: 0,
|
|
979
|
+
height: 0,
|
|
980
|
+
borderLeft: `${ARROW_SIZE}px solid transparent`,
|
|
981
|
+
borderRight: `${ARROW_SIZE}px solid transparent`,
|
|
982
|
+
borderBottom: `${ARROW_SIZE}px solid #e5e7eb`
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
),
|
|
839
986
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-3 py-2 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs font-medium text-gray-700", children: [
|
|
840
987
|
"Filter: ",
|
|
841
988
|
column.label
|
|
842
989
|
] }) }),
|
|
990
|
+
hasDuplicateCheck && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-3 pt-2 pb-2 border-b border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
991
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
992
|
+
"input",
|
|
993
|
+
{
|
|
994
|
+
type: "checkbox",
|
|
995
|
+
checked: showDuplicatesOnly,
|
|
996
|
+
onChange: (e) => setShowDuplicatesOnly(e.target.checked),
|
|
997
|
+
className: "h-3 w-3 rounded border-gray-300 text-red-600 focus:ring-red-500"
|
|
998
|
+
}
|
|
999
|
+
),
|
|
1000
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs text-gray-700", children: "Show only duplicates" })
|
|
1001
|
+
] }) }),
|
|
843
1002
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "p-3 space-y-3", children: isNumeric ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
844
1003
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
845
1004
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "text-xs text-gray-500 mb-1 block", children: "Condition" }),
|
|
@@ -862,6 +1021,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
862
1021
|
placeholder: "Enter value",
|
|
863
1022
|
value: numberValue,
|
|
864
1023
|
onChange: (e) => setNumberValue(e.target.value),
|
|
1024
|
+
onKeyDown: handleFilterKeyDown,
|
|
865
1025
|
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"
|
|
866
1026
|
}
|
|
867
1027
|
)
|
|
@@ -875,6 +1035,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
875
1035
|
placeholder: "Enter end value",
|
|
876
1036
|
value: numberValueTo,
|
|
877
1037
|
onChange: (e) => setNumberValueTo(e.target.value),
|
|
1038
|
+
onKeyDown: handleFilterKeyDown,
|
|
878
1039
|
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"
|
|
879
1040
|
}
|
|
880
1041
|
)
|
|
@@ -900,6 +1061,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
900
1061
|
type: "date",
|
|
901
1062
|
value: dateValue,
|
|
902
1063
|
onChange: (e) => setDateValue(e.target.value),
|
|
1064
|
+
onKeyDown: handleFilterKeyDown,
|
|
903
1065
|
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"
|
|
904
1066
|
}
|
|
905
1067
|
)
|
|
@@ -912,6 +1074,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
912
1074
|
type: "date",
|
|
913
1075
|
value: dateValueTo,
|
|
914
1076
|
onChange: (e) => setDateValueTo(e.target.value),
|
|
1077
|
+
onKeyDown: handleFilterKeyDown,
|
|
915
1078
|
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"
|
|
916
1079
|
}
|
|
917
1080
|
)
|
|
@@ -938,6 +1101,7 @@ var SpreadsheetFilterDropdown = ({
|
|
|
938
1101
|
placeholder: "Enter text",
|
|
939
1102
|
value: textValue,
|
|
940
1103
|
onChange: (e) => setTextValue(e.target.value),
|
|
1104
|
+
onKeyDown: handleFilterKeyDown,
|
|
941
1105
|
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"
|
|
942
1106
|
}
|
|
943
1107
|
)
|
|
@@ -972,11 +1136,15 @@ var SpreadsheetFilterDropdown = ({
|
|
|
972
1136
|
]
|
|
973
1137
|
}
|
|
974
1138
|
);
|
|
1139
|
+
if (triggerRef) {
|
|
1140
|
+
return (0, import_react_dom2.createPortal)(dropdownContent, document.body);
|
|
1141
|
+
}
|
|
1142
|
+
return dropdownContent;
|
|
975
1143
|
};
|
|
976
1144
|
SpreadsheetFilterDropdown.displayName = "SpreadsheetFilterDropdown";
|
|
977
1145
|
|
|
978
1146
|
// src/components/SpreadsheetToolbar.tsx
|
|
979
|
-
var
|
|
1147
|
+
var import_react5 = __toESM(require("react"));
|
|
980
1148
|
|
|
981
1149
|
// src/components/ActiveFiltersDisplay.tsx
|
|
982
1150
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -1199,9 +1367,9 @@ var SpreadsheetToolbar = ({
|
|
|
1199
1367
|
onToggleFiltersPanel,
|
|
1200
1368
|
className
|
|
1201
1369
|
}) => {
|
|
1202
|
-
const [showMoreMenu, setShowMoreMenu] =
|
|
1203
|
-
const menuRef =
|
|
1204
|
-
|
|
1370
|
+
const [showMoreMenu, setShowMoreMenu] = import_react5.default.useState(false);
|
|
1371
|
+
const menuRef = import_react5.default.useRef(null);
|
|
1372
|
+
import_react5.default.useEffect(() => {
|
|
1205
1373
|
const handleClickOutside = (event) => {
|
|
1206
1374
|
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
1207
1375
|
setShowMoreMenu(false);
|
|
@@ -1501,6 +1669,13 @@ var SpreadsheetToolbar = ({
|
|
|
1501
1669
|
};
|
|
1502
1670
|
SpreadsheetToolbar.displayName = "SpreadsheetToolbar";
|
|
1503
1671
|
|
|
1672
|
+
// src/components/SpreadsheetHeader.tsx
|
|
1673
|
+
var import_react7 = __toESM(require("react"));
|
|
1674
|
+
|
|
1675
|
+
// src/components/ColumnHeaderActions.tsx
|
|
1676
|
+
var import_react6 = require("react");
|
|
1677
|
+
var import_react_dom3 = require("react-dom");
|
|
1678
|
+
|
|
1504
1679
|
// ../../../node_modules/.pnpm/react-icons@4.12.0_react@18.3.1/node_modules/react-icons/md/index.esm.js
|
|
1505
1680
|
function MdPushPin(props) {
|
|
1506
1681
|
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);
|
|
@@ -1518,6 +1693,9 @@ function ColumnHeaderActions({
|
|
|
1518
1693
|
hasActiveFilter = false,
|
|
1519
1694
|
hasActiveHighlight = false,
|
|
1520
1695
|
isPinned = false,
|
|
1696
|
+
enableSorting = false,
|
|
1697
|
+
sortDirection = null,
|
|
1698
|
+
onSortClick,
|
|
1521
1699
|
onFilterClick,
|
|
1522
1700
|
onHighlightClick,
|
|
1523
1701
|
onPinClick,
|
|
@@ -1525,82 +1703,281 @@ function ColumnHeaderActions({
|
|
|
1525
1703
|
highlightTitle = "Highlight column",
|
|
1526
1704
|
pinnedTitle = "Unpin column",
|
|
1527
1705
|
unpinnedTitle = "Pin column",
|
|
1528
|
-
className
|
|
1706
|
+
className,
|
|
1707
|
+
resolvedWidth,
|
|
1708
|
+
enableDuplicateCheck = false,
|
|
1709
|
+
hasDuplicateCheck = false,
|
|
1710
|
+
onDuplicateCheckClick
|
|
1529
1711
|
}) {
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1541
|
-
hasActiveFilter ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1542
|
-
),
|
|
1543
|
-
title: filterTitle,
|
|
1544
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiFilter, { className: "h-3 w-3" })
|
|
1545
|
-
}
|
|
1546
|
-
),
|
|
1547
|
-
enableHighlighting && onHighlightClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1548
|
-
"button",
|
|
1549
|
-
{
|
|
1550
|
-
type: "button",
|
|
1551
|
-
onClick: (e) => {
|
|
1552
|
-
e.stopPropagation();
|
|
1553
|
-
onHighlightClick();
|
|
1554
|
-
},
|
|
1555
|
-
className: cn(
|
|
1556
|
-
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1557
|
-
hasActiveHighlight ? "text-amber-500 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1558
|
-
),
|
|
1559
|
-
title: highlightTitle,
|
|
1560
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AiFillHighlight, { className: "h-3 w-3" })
|
|
1561
|
-
}
|
|
1562
|
-
),
|
|
1563
|
-
enablePinning && onPinClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1564
|
-
"button",
|
|
1565
|
-
{
|
|
1566
|
-
type: "button",
|
|
1567
|
-
onClick: (e) => {
|
|
1568
|
-
e.stopPropagation();
|
|
1569
|
-
onPinClick();
|
|
1570
|
-
},
|
|
1571
|
-
className: cn(
|
|
1572
|
-
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1573
|
-
isPinned ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1574
|
-
),
|
|
1575
|
-
title: isPinned ? pinnedTitle : unpinnedTitle,
|
|
1576
|
-
children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdPushPin, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdOutlinePushPin, { className: "h-3 w-3" })
|
|
1712
|
+
const [overflowOpen, setOverflowOpen] = (0, import_react6.useState)(false);
|
|
1713
|
+
const overflowRef = (0, import_react6.useRef)(null);
|
|
1714
|
+
const triggerRef = (0, import_react6.useRef)(null);
|
|
1715
|
+
const [dropdownPos, setDropdownPos] = (0, import_react6.useState)(null);
|
|
1716
|
+
const isCompact = resolvedWidth !== void 0 && resolvedWidth < 80;
|
|
1717
|
+
(0, import_react6.useEffect)(() => {
|
|
1718
|
+
if (!overflowOpen) return;
|
|
1719
|
+
const handler = (e) => {
|
|
1720
|
+
if (overflowRef.current && !overflowRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
1721
|
+
setOverflowOpen(false);
|
|
1577
1722
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1723
|
+
};
|
|
1724
|
+
document.addEventListener("mousedown", handler);
|
|
1725
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1726
|
+
}, [overflowOpen]);
|
|
1727
|
+
const openOverflow = (e) => {
|
|
1728
|
+
e.stopPropagation();
|
|
1729
|
+
if (!overflowOpen && triggerRef.current) {
|
|
1730
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
1731
|
+
setDropdownPos({
|
|
1732
|
+
top: rect.bottom + window.scrollY + 4,
|
|
1733
|
+
right: window.innerWidth - rect.right - window.scrollX
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
setOverflowOpen((prev) => !prev);
|
|
1737
|
+
};
|
|
1738
|
+
if (isCompact) {
|
|
1739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("flex items-center", className), children: [
|
|
1740
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1741
|
+
"button",
|
|
1742
|
+
{
|
|
1743
|
+
ref: triggerRef,
|
|
1744
|
+
type: "button",
|
|
1745
|
+
onClick: openOverflow,
|
|
1746
|
+
className: "p-0.5 hover:bg-gray-200 rounded text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1747
|
+
title: "Column actions",
|
|
1748
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiDotsVertical, { className: "h-3 w-3" })
|
|
1749
|
+
}
|
|
1750
|
+
),
|
|
1751
|
+
overflowOpen && dropdownPos && (0, import_react_dom3.createPortal)(
|
|
1752
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1753
|
+
"div",
|
|
1754
|
+
{
|
|
1755
|
+
ref: overflowRef,
|
|
1756
|
+
style: {
|
|
1757
|
+
position: "absolute",
|
|
1758
|
+
top: dropdownPos.top,
|
|
1759
|
+
right: dropdownPos.right
|
|
1760
|
+
},
|
|
1761
|
+
className: "bg-white border border-gray-200 shadow-lg rounded-md py-1 w-44 z-[9999]",
|
|
1762
|
+
children: [
|
|
1763
|
+
enableSorting && onSortClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1764
|
+
"button",
|
|
1765
|
+
{
|
|
1766
|
+
type: "button",
|
|
1767
|
+
onClick: (e) => {
|
|
1768
|
+
e.stopPropagation();
|
|
1769
|
+
onSortClick();
|
|
1770
|
+
setOverflowOpen(false);
|
|
1771
|
+
},
|
|
1772
|
+
className: cn(
|
|
1773
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1774
|
+
sortDirection ? "text-blue-600" : "text-gray-700"
|
|
1775
|
+
),
|
|
1776
|
+
children: [
|
|
1777
|
+
sortDirection === "desc" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiSortDescending, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiSortAscending, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1778
|
+
sortDirection === "asc" ? "Sorted ascending" : sortDirection === "desc" ? "Sorted descending" : "Sort"
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
),
|
|
1782
|
+
enableFiltering && onFilterClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1783
|
+
"button",
|
|
1784
|
+
{
|
|
1785
|
+
type: "button",
|
|
1786
|
+
onClick: (e) => {
|
|
1787
|
+
e.stopPropagation();
|
|
1788
|
+
onFilterClick();
|
|
1789
|
+
setOverflowOpen(false);
|
|
1790
|
+
},
|
|
1791
|
+
className: cn(
|
|
1792
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1793
|
+
hasActiveFilter ? "text-blue-600" : "text-gray-700"
|
|
1794
|
+
),
|
|
1795
|
+
children: [
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiFilter, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1797
|
+
"Filter",
|
|
1798
|
+
hasActiveFilter && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ml-auto h-1.5 w-1.5 rounded-full bg-blue-500 shrink-0" })
|
|
1799
|
+
]
|
|
1800
|
+
}
|
|
1801
|
+
),
|
|
1802
|
+
enableHighlighting && onHighlightClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1803
|
+
"button",
|
|
1804
|
+
{
|
|
1805
|
+
type: "button",
|
|
1806
|
+
onClick: (e) => {
|
|
1807
|
+
e.stopPropagation();
|
|
1808
|
+
onHighlightClick();
|
|
1809
|
+
setOverflowOpen(false);
|
|
1810
|
+
},
|
|
1811
|
+
className: cn(
|
|
1812
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1813
|
+
hasActiveHighlight ? "text-amber-500" : "text-gray-700"
|
|
1814
|
+
),
|
|
1815
|
+
children: [
|
|
1816
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AiFillHighlight, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1817
|
+
"Highlight"
|
|
1818
|
+
]
|
|
1819
|
+
}
|
|
1820
|
+
),
|
|
1821
|
+
enablePinning && onPinClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1822
|
+
"button",
|
|
1823
|
+
{
|
|
1824
|
+
type: "button",
|
|
1825
|
+
onClick: (e) => {
|
|
1826
|
+
e.stopPropagation();
|
|
1827
|
+
onPinClick();
|
|
1828
|
+
setOverflowOpen(false);
|
|
1829
|
+
},
|
|
1830
|
+
className: cn(
|
|
1831
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1832
|
+
isPinned ? "text-blue-600" : "text-gray-700"
|
|
1833
|
+
),
|
|
1834
|
+
children: [
|
|
1835
|
+
isPinned ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdPushPin, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdOutlinePushPin, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1836
|
+
isPinned ? "Unpin" : "Pin"
|
|
1837
|
+
]
|
|
1838
|
+
}
|
|
1839
|
+
),
|
|
1840
|
+
enableDuplicateCheck && onDuplicateCheckClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1841
|
+
"button",
|
|
1842
|
+
{
|
|
1843
|
+
type: "button",
|
|
1844
|
+
onClick: (e) => {
|
|
1845
|
+
e.stopPropagation();
|
|
1846
|
+
onDuplicateCheckClick();
|
|
1847
|
+
setOverflowOpen(false);
|
|
1848
|
+
},
|
|
1849
|
+
className: cn(
|
|
1850
|
+
"flex items-center gap-2 w-full px-3 py-1.5 text-xs hover:bg-gray-100 text-left",
|
|
1851
|
+
hasDuplicateCheck ? "text-purple-600" : "text-gray-700"
|
|
1852
|
+
),
|
|
1853
|
+
children: [
|
|
1854
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiExclamation, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1855
|
+
"Check Duplicates"
|
|
1856
|
+
]
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1859
|
+
]
|
|
1860
|
+
}
|
|
1861
|
+
),
|
|
1862
|
+
document.body
|
|
1863
|
+
)
|
|
1864
|
+
] });
|
|
1865
|
+
}
|
|
1866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("flex items-center gap-0.5", className), children: [
|
|
1867
|
+
enableSorting && onSortClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1868
|
+
"button",
|
|
1869
|
+
{
|
|
1870
|
+
type: "button",
|
|
1871
|
+
onClick: (e) => {
|
|
1872
|
+
e.stopPropagation();
|
|
1873
|
+
onSortClick();
|
|
1874
|
+
},
|
|
1875
|
+
className: cn(
|
|
1876
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1877
|
+
sortDirection ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1878
|
+
),
|
|
1879
|
+
title: sortDirection === "asc" ? "Sorted ascending" : sortDirection === "desc" ? "Sorted descending" : "Sort column",
|
|
1880
|
+
children: sortDirection === "desc" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiSortDescending, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiSortAscending, { className: "h-3 w-3" })
|
|
1881
|
+
}
|
|
1882
|
+
),
|
|
1883
|
+
enableFiltering && onFilterClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1884
|
+
"button",
|
|
1885
|
+
{
|
|
1886
|
+
type: "button",
|
|
1887
|
+
onClick: (e) => {
|
|
1888
|
+
e.stopPropagation();
|
|
1889
|
+
onFilterClick();
|
|
1890
|
+
},
|
|
1891
|
+
className: cn(
|
|
1892
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1893
|
+
hasActiveFilter ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1894
|
+
),
|
|
1895
|
+
title: filterTitle,
|
|
1896
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiFilter, { className: "h-3 w-3" })
|
|
1897
|
+
}
|
|
1898
|
+
),
|
|
1899
|
+
enableHighlighting && onHighlightClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1900
|
+
"button",
|
|
1901
|
+
{
|
|
1902
|
+
type: "button",
|
|
1903
|
+
onClick: (e) => {
|
|
1904
|
+
e.stopPropagation();
|
|
1905
|
+
onHighlightClick();
|
|
1906
|
+
},
|
|
1907
|
+
className: cn(
|
|
1908
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1909
|
+
hasActiveHighlight ? "text-amber-500 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1910
|
+
),
|
|
1911
|
+
title: highlightTitle,
|
|
1912
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AiFillHighlight, { className: "h-3 w-3" })
|
|
1913
|
+
}
|
|
1914
|
+
),
|
|
1915
|
+
enablePinning && onPinClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1916
|
+
"button",
|
|
1917
|
+
{
|
|
1918
|
+
type: "button",
|
|
1919
|
+
onClick: (e) => {
|
|
1920
|
+
e.stopPropagation();
|
|
1921
|
+
onPinClick();
|
|
1922
|
+
},
|
|
1923
|
+
className: cn(
|
|
1924
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1925
|
+
isPinned ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1926
|
+
),
|
|
1927
|
+
title: isPinned ? pinnedTitle : unpinnedTitle,
|
|
1928
|
+
children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdPushPin, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MdOutlinePushPin, { className: "h-3 w-3" })
|
|
1929
|
+
}
|
|
1930
|
+
),
|
|
1931
|
+
enableDuplicateCheck && onDuplicateCheckClick && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1932
|
+
"button",
|
|
1933
|
+
{
|
|
1934
|
+
type: "button",
|
|
1935
|
+
onClick: (e) => {
|
|
1936
|
+
e.stopPropagation();
|
|
1937
|
+
onDuplicateCheckClick();
|
|
1938
|
+
},
|
|
1939
|
+
className: cn(
|
|
1940
|
+
"p-0.5 hover:bg-gray-200 rounded transition-opacity",
|
|
1941
|
+
hasDuplicateCheck ? "text-purple-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
|
|
1942
|
+
),
|
|
1943
|
+
title: "Check duplicates",
|
|
1944
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HiExclamation, { className: "h-3 w-3" })
|
|
1945
|
+
}
|
|
1946
|
+
)
|
|
1947
|
+
] });
|
|
1948
|
+
}
|
|
1949
|
+
ColumnHeaderActions.displayName = "ColumnHeaderActions";
|
|
1950
|
+
|
|
1951
|
+
// src/components/SpreadsheetHeader.tsx
|
|
1952
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1953
|
+
var cellPaddingCompact2 = "px-1.5 py-1";
|
|
1954
|
+
var cellPaddingNormal2 = "px-2 py-2";
|
|
1955
|
+
var SpreadsheetHeader = ({
|
|
1956
|
+
column,
|
|
1957
|
+
sortConfig,
|
|
1958
|
+
hasActiveFilter = false,
|
|
1959
|
+
isPinned = false,
|
|
1960
|
+
pinSide,
|
|
1961
|
+
leftOffset = 0,
|
|
1962
|
+
rightOffset = 0,
|
|
1963
|
+
highlightColor,
|
|
1964
|
+
compactMode = false,
|
|
1965
|
+
onClick,
|
|
1966
|
+
pinnedZIndex,
|
|
1967
|
+
onSortClick,
|
|
1968
|
+
onFilterClick,
|
|
1969
|
+
onPinClick,
|
|
1970
|
+
onHighlightClick,
|
|
1971
|
+
hasDuplicateCheck = false,
|
|
1972
|
+
onDuplicateCheckClick,
|
|
1973
|
+
duplicateCount,
|
|
1974
|
+
resizeHandleProps,
|
|
1975
|
+
resolvedWidth,
|
|
1976
|
+
topOffset = 0,
|
|
1977
|
+
className,
|
|
1978
|
+
children
|
|
1603
1979
|
}) => {
|
|
1980
|
+
const thRef = (0, import_react7.useRef)(null);
|
|
1604
1981
|
const isSorted = sortConfig?.columnId === column.id;
|
|
1605
1982
|
const sortDirection = isSorted ? sortConfig.direction : null;
|
|
1606
1983
|
const cellPadding = compactMode ? cellPaddingCompact2 : cellPaddingNormal2;
|
|
@@ -1616,46 +1993,55 @@ var SpreadsheetHeader = ({
|
|
|
1616
1993
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1617
1994
|
"th",
|
|
1618
1995
|
{
|
|
1619
|
-
|
|
1996
|
+
ref: thRef,
|
|
1997
|
+
"data-column-id": column.id,
|
|
1998
|
+
onClick,
|
|
1620
1999
|
className: cn(
|
|
1621
|
-
"border border-gray-200 font-semibold text-gray-700
|
|
1622
|
-
|
|
2000
|
+
"border border-gray-200 font-semibold text-gray-700 group relative cursor-pointer",
|
|
2001
|
+
isPinned && "sticky",
|
|
2002
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1623
2003
|
cellPadding,
|
|
1624
2004
|
column.align === "right" && "text-right",
|
|
1625
2005
|
column.align === "center" && "text-center",
|
|
1626
|
-
|
|
1627
|
-
isPinned
|
|
2006
|
+
"hover:bg-gray-100",
|
|
2007
|
+
!isPinned && "z-20",
|
|
1628
2008
|
className
|
|
1629
2009
|
),
|
|
1630
2010
|
style: {
|
|
1631
2011
|
backgroundColor: highlightColor || "rgb(243 244 246)",
|
|
1632
2012
|
// gray-100
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
),
|
|
1641
|
-
maxWidth: Math.max(
|
|
1642
|
-
column.minWidth || column.width || MIN_PINNED_COLUMN_WIDTH,
|
|
1643
|
-
MIN_PINNED_COLUMN_WIDTH
|
|
1644
|
-
)
|
|
2013
|
+
// Always set explicit width to prevent layout shift on selection/re-render
|
|
2014
|
+
...resolvedWidth ? {
|
|
2015
|
+
width: resolvedWidth,
|
|
2016
|
+
minWidth: resolvedWidth
|
|
2017
|
+
} : {
|
|
2018
|
+
width: column.width || column.minWidth,
|
|
2019
|
+
minWidth: column.minWidth || column.width
|
|
1645
2020
|
},
|
|
1646
|
-
|
|
1647
|
-
//
|
|
2021
|
+
...isPinned && pinnedZIndex !== void 0 && { zIndex: pinnedZIndex + 10 },
|
|
2022
|
+
// +10 so headers are above cells
|
|
1648
2023
|
...positionStyles
|
|
1649
2024
|
},
|
|
1650
2025
|
children: [
|
|
1651
2026
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center justify-between gap-1", children: [
|
|
1652
2027
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex-1 flex items-center gap-1", children: [
|
|
1653
2028
|
column.label,
|
|
1654
|
-
isSorted && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-blue-600", children: sortDirection === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HiChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HiChevronDown, { className: "h-3 w-3" }) })
|
|
2029
|
+
isSorted && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-blue-600", children: sortDirection === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HiChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HiChevronDown, { className: "h-3 w-3" }) }),
|
|
2030
|
+
hasDuplicateCheck && duplicateCount != null && duplicateCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2031
|
+
"span",
|
|
2032
|
+
{
|
|
2033
|
+
className: "inline-flex items-center px-1 py-0.5 rounded-full bg-red-100 text-red-600 text-[10px] font-medium leading-none",
|
|
2034
|
+
title: `${duplicateCount} duplicate values found`,
|
|
2035
|
+
children: duplicateCount
|
|
2036
|
+
}
|
|
2037
|
+
)
|
|
1655
2038
|
] }),
|
|
1656
2039
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1657
2040
|
ColumnHeaderActions,
|
|
1658
2041
|
{
|
|
2042
|
+
enableSorting: column.sortable,
|
|
2043
|
+
sortDirection: isSorted ? sortDirection : null,
|
|
2044
|
+
onSortClick,
|
|
1659
2045
|
enableFiltering: column.filterable,
|
|
1660
2046
|
enableHighlighting: column.highlightable !== false && !!onHighlightClick,
|
|
1661
2047
|
enablePinning: column.pinnable !== false,
|
|
@@ -1664,21 +2050,249 @@ var SpreadsheetHeader = ({
|
|
|
1664
2050
|
isPinned,
|
|
1665
2051
|
onFilterClick,
|
|
1666
2052
|
onHighlightClick,
|
|
1667
|
-
onPinClick
|
|
2053
|
+
onPinClick,
|
|
2054
|
+
resolvedWidth,
|
|
2055
|
+
enableDuplicateCheck: true,
|
|
2056
|
+
hasDuplicateCheck,
|
|
2057
|
+
onDuplicateCheckClick
|
|
1668
2058
|
}
|
|
1669
2059
|
)
|
|
1670
2060
|
] }),
|
|
1671
|
-
children
|
|
2061
|
+
children && import_react7.default.Children.map(
|
|
2062
|
+
children,
|
|
2063
|
+
(child) => import_react7.default.isValidElement(child) ? import_react7.default.cloneElement(child, { triggerRef: thRef }) : child
|
|
2064
|
+
),
|
|
2065
|
+
resizeHandleProps && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2066
|
+
"div",
|
|
2067
|
+
{
|
|
2068
|
+
onMouseDown: resizeHandleProps.onMouseDown,
|
|
2069
|
+
style: resizeHandleProps.style,
|
|
2070
|
+
className: resizeHandleProps.className
|
|
2071
|
+
}
|
|
2072
|
+
)
|
|
1672
2073
|
]
|
|
1673
2074
|
}
|
|
1674
2075
|
);
|
|
1675
2076
|
};
|
|
1676
2077
|
SpreadsheetHeader.displayName = "SpreadsheetHeader";
|
|
2078
|
+
var MemoizedSpreadsheetHeader = (0, import_react7.memo)(SpreadsheetHeader, (prevProps, nextProps) => {
|
|
2079
|
+
if (prevProps.column.id !== nextProps.column.id) return false;
|
|
2080
|
+
if (prevProps.sortConfig?.columnId !== nextProps.sortConfig?.columnId) return false;
|
|
2081
|
+
if (prevProps.sortConfig?.direction !== nextProps.sortConfig?.direction) return false;
|
|
2082
|
+
if (prevProps.hasActiveFilter !== nextProps.hasActiveFilter) return false;
|
|
2083
|
+
if (prevProps.isPinned !== nextProps.isPinned) return false;
|
|
2084
|
+
if (prevProps.pinSide !== nextProps.pinSide) return false;
|
|
2085
|
+
if (prevProps.leftOffset !== nextProps.leftOffset) return false;
|
|
2086
|
+
if (prevProps.rightOffset !== nextProps.rightOffset) return false;
|
|
2087
|
+
if (prevProps.highlightColor !== nextProps.highlightColor) return false;
|
|
2088
|
+
if (prevProps.compactMode !== nextProps.compactMode) return false;
|
|
2089
|
+
if (prevProps.pinnedZIndex !== nextProps.pinnedZIndex) return false;
|
|
2090
|
+
if (prevProps.resolvedWidth !== nextProps.resolvedWidth) return false;
|
|
2091
|
+
if (prevProps.topOffset !== nextProps.topOffset) return false;
|
|
2092
|
+
if (prevProps.hasDuplicateCheck !== nextProps.hasDuplicateCheck) return false;
|
|
2093
|
+
if (prevProps.duplicateCount !== nextProps.duplicateCount) return false;
|
|
2094
|
+
if (prevProps.children !== nextProps.children) return false;
|
|
2095
|
+
return true;
|
|
2096
|
+
});
|
|
2097
|
+
MemoizedSpreadsheetHeader.displayName = "MemoizedSpreadsheetHeader";
|
|
2098
|
+
|
|
2099
|
+
// src/hooks/useSpreadsheetPinning.ts
|
|
2100
|
+
var import_react8 = require("react");
|
|
2101
|
+
var ROW_INDEX_COLUMN_ID = "__row_index__";
|
|
2102
|
+
var ROW_INDEX_COLUMN_WIDTH = 90;
|
|
2103
|
+
function useSpreadsheetPinning({
|
|
2104
|
+
columns,
|
|
2105
|
+
columnGroups,
|
|
2106
|
+
showRowIndex = true,
|
|
2107
|
+
defaultPinnedColumns = [],
|
|
2108
|
+
defaultPinnedRightColumns = [],
|
|
2109
|
+
getColumnWidth
|
|
2110
|
+
}) {
|
|
2111
|
+
const [pinnedColumns, setPinnedColumns] = (0, import_react8.useState)(() => {
|
|
2112
|
+
const map = /* @__PURE__ */ new Map();
|
|
2113
|
+
if (showRowIndex) {
|
|
2114
|
+
map.set(ROW_INDEX_COLUMN_ID, "left");
|
|
2115
|
+
}
|
|
2116
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2117
|
+
defaultPinnedColumns.forEach((col) => {
|
|
2118
|
+
if (col === ROW_INDEX_COLUMN_ID) return;
|
|
2119
|
+
const colIndex = columns.findIndex((c) => c.id === col);
|
|
2120
|
+
map.set(col, colIndex >= 0 && colIndex <= midpoint ? "left" : "right");
|
|
2121
|
+
});
|
|
2122
|
+
defaultPinnedRightColumns.forEach((col) => map.set(col, "right"));
|
|
2123
|
+
return map;
|
|
2124
|
+
});
|
|
2125
|
+
const [collapsedGroups, setCollapsedGroups] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2126
|
+
const measuredWidthsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
2127
|
+
const tableElRef = (0, import_react8.useRef)(null);
|
|
2128
|
+
const [measureVersion, setMeasureVersion] = (0, import_react8.useState)(0);
|
|
2129
|
+
const measureColumnWidths = (0, import_react8.useCallback)(() => {
|
|
2130
|
+
const el = tableElRef.current;
|
|
2131
|
+
if (!el) return;
|
|
2132
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
2133
|
+
const headers = el.querySelectorAll("th[data-column-id]");
|
|
2134
|
+
headers.forEach((th) => {
|
|
2135
|
+
const colId = th.getAttribute("data-column-id");
|
|
2136
|
+
if (colId) {
|
|
2137
|
+
newMap.set(colId, th.offsetWidth);
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
const rowIndexTd = el.querySelector('td[data-column-id="__row_index__"]');
|
|
2141
|
+
if (rowIndexTd) {
|
|
2142
|
+
newMap.set(ROW_INDEX_COLUMN_ID, rowIndexTd.offsetWidth);
|
|
2143
|
+
}
|
|
2144
|
+
measuredWidthsRef.current = newMap;
|
|
2145
|
+
setMeasureVersion((v) => v + 1);
|
|
2146
|
+
}, []);
|
|
2147
|
+
const measureRef = (0, import_react8.useCallback)((el) => {
|
|
2148
|
+
tableElRef.current = el;
|
|
2149
|
+
}, []);
|
|
2150
|
+
const isRowIndexPinned = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
2151
|
+
const handleTogglePin = (0, import_react8.useCallback)(
|
|
2152
|
+
(columnId) => {
|
|
2153
|
+
setPinnedColumns((prev) => {
|
|
2154
|
+
const newMap = new Map(prev);
|
|
2155
|
+
if (newMap.has(columnId)) {
|
|
2156
|
+
newMap.delete(columnId);
|
|
2157
|
+
} else {
|
|
2158
|
+
if (columnId === ROW_INDEX_COLUMN_ID) {
|
|
2159
|
+
newMap.set(columnId, "left");
|
|
2160
|
+
} else {
|
|
2161
|
+
const colIndex = columns.findIndex((c) => c.id === columnId);
|
|
2162
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2163
|
+
newMap.set(columnId, colIndex <= midpoint ? "left" : "right");
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return newMap;
|
|
2167
|
+
});
|
|
2168
|
+
},
|
|
2169
|
+
[columns]
|
|
2170
|
+
);
|
|
2171
|
+
const setPinnedColumnsFromIds = (0, import_react8.useCallback)((columnIds) => {
|
|
2172
|
+
const map = /* @__PURE__ */ new Map();
|
|
2173
|
+
if (showRowIndex) {
|
|
2174
|
+
map.set(ROW_INDEX_COLUMN_ID, "left");
|
|
2175
|
+
}
|
|
2176
|
+
const midpoint = Math.floor(columns.length / 2);
|
|
2177
|
+
columnIds.forEach((col) => {
|
|
2178
|
+
if (col !== ROW_INDEX_COLUMN_ID) {
|
|
2179
|
+
const colIndex = columns.findIndex((c) => c.id === col);
|
|
2180
|
+
map.set(col, colIndex >= 0 && colIndex <= midpoint ? "left" : "right");
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
setPinnedColumns(map);
|
|
2184
|
+
}, [showRowIndex, columns]);
|
|
2185
|
+
const handleToggleGroupCollapse = (0, import_react8.useCallback)((groupId) => {
|
|
2186
|
+
setCollapsedGroups((prev) => {
|
|
2187
|
+
const newSet = new Set(prev);
|
|
2188
|
+
if (newSet.has(groupId)) {
|
|
2189
|
+
newSet.delete(groupId);
|
|
2190
|
+
} else {
|
|
2191
|
+
newSet.add(groupId);
|
|
2192
|
+
}
|
|
2193
|
+
return newSet;
|
|
2194
|
+
});
|
|
2195
|
+
}, []);
|
|
2196
|
+
const visibleColumns = (0, import_react8.useMemo)(() => {
|
|
2197
|
+
if (!columns || !Array.isArray(columns) || columns.length === 0) return [];
|
|
2198
|
+
let result = [...columns];
|
|
2199
|
+
if (columnGroups && Array.isArray(columnGroups)) {
|
|
2200
|
+
result = result.filter((column) => {
|
|
2201
|
+
const group = columnGroups.find((g) => g.columns.includes(column.id));
|
|
2202
|
+
if (!group) return true;
|
|
2203
|
+
if (!collapsedGroups.has(group.id)) return true;
|
|
2204
|
+
return pinnedColumns.has(column.id);
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
return result;
|
|
2208
|
+
}, [columns, columnGroups, collapsedGroups, pinnedColumns]);
|
|
2209
|
+
(0, import_react8.useEffect)(() => {
|
|
2210
|
+
requestAnimationFrame(() => measureColumnWidths());
|
|
2211
|
+
}, [measureColumnWidths, visibleColumns, pinnedColumns]);
|
|
2212
|
+
const widthMap = (0, import_react8.useMemo)(() => {
|
|
2213
|
+
void measureVersion;
|
|
2214
|
+
const map = /* @__PURE__ */ new Map();
|
|
2215
|
+
for (const col of columns) {
|
|
2216
|
+
const measured = measuredWidthsRef.current.get(col.id);
|
|
2217
|
+
if (measured) {
|
|
2218
|
+
map.set(col.id, measured);
|
|
2219
|
+
continue;
|
|
2220
|
+
}
|
|
2221
|
+
const resized = getColumnWidth?.(col.id);
|
|
2222
|
+
if (resized) {
|
|
2223
|
+
map.set(col.id, resized);
|
|
2224
|
+
continue;
|
|
2225
|
+
}
|
|
2226
|
+
map.set(col.id, col.width || col.minWidth || 100);
|
|
2227
|
+
}
|
|
2228
|
+
const measuredRI = measuredWidthsRef.current.get(ROW_INDEX_COLUMN_ID);
|
|
2229
|
+
map.set(ROW_INDEX_COLUMN_ID, measuredRI || ROW_INDEX_COLUMN_WIDTH);
|
|
2230
|
+
return map;
|
|
2231
|
+
}, [columns, getColumnWidth, measureVersion]);
|
|
2232
|
+
const { leftOffsets, rightOffsets, zIndices } = (0, import_react8.useMemo)(() => {
|
|
2233
|
+
const leftOffsets2 = /* @__PURE__ */ new Map();
|
|
2234
|
+
const rightOffsets2 = /* @__PURE__ */ new Map();
|
|
2235
|
+
const zIndices2 = /* @__PURE__ */ new Map();
|
|
2236
|
+
leftOffsets2.set(ROW_INDEX_COLUMN_ID, 0);
|
|
2237
|
+
zIndices2.set(ROW_INDEX_COLUMN_ID, 40);
|
|
2238
|
+
const leftPinned = visibleColumns.filter((c) => pinnedColumns.get(c.id) === "left");
|
|
2239
|
+
const rightPinned = visibleColumns.filter((c) => pinnedColumns.get(c.id) === "right");
|
|
2240
|
+
const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
2241
|
+
let leftOffset = showRowIndex && isRowIndexPinnedNow ? widthMap.get(ROW_INDEX_COLUMN_ID) || ROW_INDEX_COLUMN_WIDTH : 0;
|
|
2242
|
+
for (let i = 0; i < leftPinned.length; i++) {
|
|
2243
|
+
leftOffsets2.set(leftPinned[i].id, leftOffset);
|
|
2244
|
+
zIndices2.set(leftPinned[i].id, 20 + (leftPinned.length - i));
|
|
2245
|
+
leftOffset += widthMap.get(leftPinned[i].id) || 100;
|
|
2246
|
+
}
|
|
2247
|
+
let rightOffset = 0;
|
|
2248
|
+
for (let i = rightPinned.length - 1; i >= 0; i--) {
|
|
2249
|
+
rightOffsets2.set(rightPinned[i].id, rightOffset);
|
|
2250
|
+
zIndices2.set(rightPinned[i].id, 20 + (rightPinned.length - i));
|
|
2251
|
+
rightOffset += widthMap.get(rightPinned[i].id) || 100;
|
|
2252
|
+
}
|
|
2253
|
+
return { leftOffsets: leftOffsets2, rightOffsets: rightOffsets2, zIndices: zIndices2 };
|
|
2254
|
+
}, [pinnedColumns, visibleColumns, showRowIndex, widthMap]);
|
|
2255
|
+
const getColumnLeftOffset = (0, import_react8.useCallback)(
|
|
2256
|
+
(columnId) => leftOffsets.get(columnId) ?? 0,
|
|
2257
|
+
[leftOffsets]
|
|
2258
|
+
);
|
|
2259
|
+
const getColumnRightOffset = (0, import_react8.useCallback)(
|
|
2260
|
+
(columnId) => rightOffsets.get(columnId) ?? 0,
|
|
2261
|
+
[rightOffsets]
|
|
2262
|
+
);
|
|
2263
|
+
const isColumnPinned = (0, import_react8.useCallback)(
|
|
2264
|
+
(columnId) => pinnedColumns.has(columnId),
|
|
2265
|
+
[pinnedColumns]
|
|
2266
|
+
);
|
|
2267
|
+
const getColumnPinSide = (0, import_react8.useCallback)(
|
|
2268
|
+
(columnId) => pinnedColumns.get(columnId),
|
|
2269
|
+
[pinnedColumns]
|
|
2270
|
+
);
|
|
2271
|
+
const getPinnedZIndex = (0, import_react8.useCallback)(
|
|
2272
|
+
(columnId) => zIndices.get(columnId) ?? 0,
|
|
2273
|
+
[zIndices]
|
|
2274
|
+
);
|
|
2275
|
+
return {
|
|
2276
|
+
pinnedColumns,
|
|
2277
|
+
isRowIndexPinned,
|
|
2278
|
+
collapsedGroups,
|
|
2279
|
+
visibleColumns,
|
|
2280
|
+
handleTogglePin,
|
|
2281
|
+
handleToggleGroupCollapse,
|
|
2282
|
+
setPinnedColumnsFromIds,
|
|
2283
|
+
getColumnLeftOffset,
|
|
2284
|
+
getColumnRightOffset,
|
|
2285
|
+
isColumnPinned,
|
|
2286
|
+
getColumnPinSide,
|
|
2287
|
+
getPinnedZIndex,
|
|
2288
|
+
measureRef
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
1677
2291
|
|
|
1678
2292
|
// src/components/RowIndexColumnHeader.tsx
|
|
1679
2293
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1680
|
-
var cellPaddingCompact3 = "px-1 py-
|
|
1681
|
-
var cellPaddingNormal3 = "px-2 py-
|
|
2294
|
+
var cellPaddingCompact3 = "px-1.5 py-1";
|
|
2295
|
+
var cellPaddingNormal3 = "px-2 py-2";
|
|
1682
2296
|
function RowIndexColumnHeader({
|
|
1683
2297
|
enableHighlighting = false,
|
|
1684
2298
|
highlightColor,
|
|
@@ -1695,18 +2309,16 @@ function RowIndexColumnHeader({
|
|
|
1695
2309
|
"th",
|
|
1696
2310
|
{
|
|
1697
2311
|
className: cn(
|
|
1698
|
-
"border border-gray-200 text-center font-bold text-gray-700",
|
|
1699
|
-
compactMode ? "text-
|
|
2312
|
+
"border border-gray-200 text-center font-bold text-gray-700 sticky",
|
|
2313
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1700
2314
|
cellPadding,
|
|
1701
|
-
isPinned ? "z-30" : "z-20",
|
|
1702
2315
|
className
|
|
1703
2316
|
),
|
|
1704
2317
|
style: {
|
|
1705
2318
|
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1706
2319
|
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
left: isPinned ? 0 : void 0,
|
|
2320
|
+
left: 0,
|
|
2321
|
+
zIndex: 50,
|
|
1710
2322
|
backgroundColor: highlightColor || "rgb(243 244 246)"
|
|
1711
2323
|
}
|
|
1712
2324
|
}
|
|
@@ -1716,18 +2328,16 @@ function RowIndexColumnHeader({
|
|
|
1716
2328
|
"th",
|
|
1717
2329
|
{
|
|
1718
2330
|
className: cn(
|
|
1719
|
-
"border border-gray-200 text-center font-bold text-gray-700 group",
|
|
1720
|
-
compactMode ? "text-
|
|
2331
|
+
"border border-gray-200 text-center font-bold text-gray-700 group sticky",
|
|
2332
|
+
compactMode ? "text-xs" : "text-sm",
|
|
1721
2333
|
cellPadding,
|
|
1722
|
-
isPinned ? "z-30" : "z-20",
|
|
1723
2334
|
className
|
|
1724
2335
|
),
|
|
1725
2336
|
style: {
|
|
1726
2337
|
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1727
2338
|
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
left: isPinned ? 0 : void 0,
|
|
2339
|
+
left: 0,
|
|
2340
|
+
zIndex: 50,
|
|
1731
2341
|
backgroundColor: highlightColor || "rgb(243 244 246)"
|
|
1732
2342
|
},
|
|
1733
2343
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
@@ -1752,231 +2362,104 @@ function RowIndexColumnHeader({
|
|
|
1752
2362
|
}
|
|
1753
2363
|
RowIndexColumnHeader.displayName = "RowIndexColumnHeader";
|
|
1754
2364
|
|
|
1755
|
-
// src/hooks/useSpreadsheetHighlighting.ts
|
|
1756
|
-
var import_react7 = require("react");
|
|
1757
|
-
var HIGHLIGHT_COLORS = {
|
|
1758
|
-
// Darker colors for rows (more visible)
|
|
1759
|
-
row: [
|
|
1760
|
-
"#fef08a",
|
|
1761
|
-
// yellow
|
|
1762
|
-
"#bbf7d0",
|
|
1763
|
-
// green
|
|
1764
|
-
"#bfdbfe",
|
|
1765
|
-
// blue
|
|
1766
|
-
"#fecaca",
|
|
1767
|
-
// red
|
|
1768
|
-
"#e9d5ff",
|
|
1769
|
-
// purple
|
|
1770
|
-
"#fed7aa",
|
|
1771
|
-
// orange
|
|
1772
|
-
"#a5f3fc",
|
|
1773
|
-
// cyan
|
|
1774
|
-
"#fce7f3",
|
|
1775
|
-
// pink
|
|
1776
|
-
"#d1d5db"
|
|
1777
|
-
// gray
|
|
1778
|
-
],
|
|
1779
|
-
// Lighter colors for columns/headers (subtle background)
|
|
1780
|
-
column: [
|
|
1781
|
-
"#fef9c3",
|
|
1782
|
-
// yellow-100
|
|
1783
|
-
"#dcfce7",
|
|
1784
|
-
// green-100
|
|
1785
|
-
"#dbeafe",
|
|
1786
|
-
// blue-100
|
|
1787
|
-
"#fee2e2",
|
|
1788
|
-
// red-100
|
|
1789
|
-
"#f3e8ff",
|
|
1790
|
-
// purple-100
|
|
1791
|
-
"#ffedd5",
|
|
1792
|
-
// orange-100
|
|
1793
|
-
"#cffafe",
|
|
1794
|
-
// cyan-100
|
|
1795
|
-
"#fce7f3",
|
|
1796
|
-
// pink-100
|
|
1797
|
-
"#e5e7eb"
|
|
1798
|
-
// gray-200
|
|
1799
|
-
]
|
|
1800
|
-
};
|
|
1801
|
-
function useSpreadsheetHighlighting({
|
|
1802
|
-
externalRowHighlights,
|
|
1803
|
-
onRowHighlight,
|
|
1804
|
-
externalColumnHighlights,
|
|
1805
|
-
onColumnHighlight,
|
|
1806
|
-
externalCellHighlights,
|
|
1807
|
-
onCellHighlight
|
|
1808
|
-
} = {}) {
|
|
1809
|
-
const [cellHighlightsInternal, setCellHighlightsInternal] = (0, import_react7.useState)([]);
|
|
1810
|
-
const [rowHighlightsInternal, setRowHighlightsInternal] = (0, import_react7.useState)([]);
|
|
1811
|
-
const [columnHighlightsInternal, setColumnHighlightsInternal] = (0, import_react7.useState)({});
|
|
1812
|
-
const [highlightPickerRow, setHighlightPickerRow] = (0, import_react7.useState)(null);
|
|
1813
|
-
const [highlightPickerColumn, setHighlightPickerColumn] = (0, import_react7.useState)(null);
|
|
1814
|
-
const [highlightPickerCell, setHighlightPickerCell] = (0, import_react7.useState)(null);
|
|
1815
|
-
const cellHighlights = externalCellHighlights || cellHighlightsInternal;
|
|
1816
|
-
const rowHighlights = externalRowHighlights || rowHighlightsInternal;
|
|
1817
|
-
const columnHighlights = externalColumnHighlights || columnHighlightsInternal;
|
|
1818
|
-
const getCellHighlight = (0, import_react7.useCallback)(
|
|
1819
|
-
(rowId, columnId) => {
|
|
1820
|
-
return cellHighlights.find((h) => h.rowId === rowId && h.columnId === columnId)?.color;
|
|
1821
|
-
},
|
|
1822
|
-
[cellHighlights]
|
|
1823
|
-
);
|
|
1824
|
-
const handleCellHighlightToggle = (0, import_react7.useCallback)(
|
|
1825
|
-
(rowId, columnId, color = "#fef08a") => {
|
|
1826
|
-
if (onCellHighlight) {
|
|
1827
|
-
onCellHighlight(rowId, columnId, color);
|
|
1828
|
-
} else {
|
|
1829
|
-
setCellHighlightsInternal((prev) => {
|
|
1830
|
-
const existing = prev.find((h) => h.rowId === rowId && h.columnId === columnId);
|
|
1831
|
-
if (existing) {
|
|
1832
|
-
if (color === null) {
|
|
1833
|
-
return prev.filter(
|
|
1834
|
-
(h) => !(h.rowId === rowId && h.columnId === columnId)
|
|
1835
|
-
);
|
|
1836
|
-
}
|
|
1837
|
-
return prev.map(
|
|
1838
|
-
(h) => h.rowId === rowId && h.columnId === columnId ? { ...h, color } : h
|
|
1839
|
-
);
|
|
1840
|
-
}
|
|
1841
|
-
if (color) {
|
|
1842
|
-
return [...prev, { rowId, columnId, color }];
|
|
1843
|
-
}
|
|
1844
|
-
return prev;
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
setHighlightPickerCell(null);
|
|
1848
|
-
},
|
|
1849
|
-
[onCellHighlight]
|
|
1850
|
-
);
|
|
1851
|
-
const getRowHighlight = (0, import_react7.useCallback)(
|
|
1852
|
-
(rowId) => {
|
|
1853
|
-
return rowHighlights.find((h) => h.rowId === rowId && !h.columnId);
|
|
1854
|
-
},
|
|
1855
|
-
[rowHighlights]
|
|
1856
|
-
);
|
|
1857
|
-
const handleRowHighlightToggle = (0, import_react7.useCallback)(
|
|
1858
|
-
(rowId, color) => {
|
|
1859
|
-
if (onRowHighlight) {
|
|
1860
|
-
onRowHighlight(rowId, color);
|
|
1861
|
-
} else {
|
|
1862
|
-
setRowHighlightsInternal((prev) => {
|
|
1863
|
-
const existing = prev.find((h) => h.rowId === rowId && !h.columnId);
|
|
1864
|
-
if (existing) {
|
|
1865
|
-
if (color === null) {
|
|
1866
|
-
return prev.filter((h) => !(h.rowId === rowId && !h.columnId));
|
|
1867
|
-
}
|
|
1868
|
-
return prev.map(
|
|
1869
|
-
(h) => h.rowId === rowId && !h.columnId ? { ...h, color } : h
|
|
1870
|
-
);
|
|
1871
|
-
}
|
|
1872
|
-
if (color) {
|
|
1873
|
-
return [...prev, { rowId, color }];
|
|
1874
|
-
}
|
|
1875
|
-
return prev;
|
|
1876
|
-
});
|
|
1877
|
-
}
|
|
1878
|
-
setHighlightPickerRow(null);
|
|
1879
|
-
},
|
|
1880
|
-
[onRowHighlight]
|
|
1881
|
-
);
|
|
1882
|
-
const getColumnHighlight = (0, import_react7.useCallback)(
|
|
1883
|
-
(columnId) => {
|
|
1884
|
-
return columnHighlights[columnId];
|
|
1885
|
-
},
|
|
1886
|
-
[columnHighlights]
|
|
1887
|
-
);
|
|
1888
|
-
const handleColumnHighlightToggle = (0, import_react7.useCallback)(
|
|
1889
|
-
(columnId, color) => {
|
|
1890
|
-
if (onColumnHighlight) {
|
|
1891
|
-
onColumnHighlight(columnId, color);
|
|
1892
|
-
} else {
|
|
1893
|
-
setColumnHighlightsInternal((prev) => {
|
|
1894
|
-
const newHighlights = { ...prev };
|
|
1895
|
-
if (color === null) {
|
|
1896
|
-
delete newHighlights[columnId];
|
|
1897
|
-
} else {
|
|
1898
|
-
newHighlights[columnId] = color;
|
|
1899
|
-
}
|
|
1900
|
-
return newHighlights;
|
|
1901
|
-
});
|
|
1902
|
-
}
|
|
1903
|
-
setHighlightPickerColumn(null);
|
|
1904
|
-
},
|
|
1905
|
-
[onColumnHighlight]
|
|
1906
|
-
);
|
|
1907
|
-
const clearAllHighlights = (0, import_react7.useCallback)(() => {
|
|
1908
|
-
setCellHighlightsInternal([]);
|
|
1909
|
-
setRowHighlightsInternal([]);
|
|
1910
|
-
setColumnHighlightsInternal({});
|
|
1911
|
-
}, []);
|
|
1912
|
-
return {
|
|
1913
|
-
// Cell highlights
|
|
1914
|
-
cellHighlights,
|
|
1915
|
-
getCellHighlight,
|
|
1916
|
-
handleCellHighlightToggle,
|
|
1917
|
-
// Row highlights
|
|
1918
|
-
rowHighlights,
|
|
1919
|
-
getRowHighlight,
|
|
1920
|
-
handleRowHighlightToggle,
|
|
1921
|
-
// Column highlights
|
|
1922
|
-
columnHighlights,
|
|
1923
|
-
getColumnHighlight,
|
|
1924
|
-
handleColumnHighlightToggle,
|
|
1925
|
-
// Picker state
|
|
1926
|
-
highlightPickerRow,
|
|
1927
|
-
setHighlightPickerRow,
|
|
1928
|
-
highlightPickerColumn,
|
|
1929
|
-
setHighlightPickerColumn,
|
|
1930
|
-
highlightPickerCell,
|
|
1931
|
-
setHighlightPickerCell,
|
|
1932
|
-
// Utility
|
|
1933
|
-
clearAllHighlights
|
|
1934
|
-
};
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
2365
|
// src/components/ColorPickerPopover.tsx
|
|
2366
|
+
var import_react9 = require("react");
|
|
1938
2367
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1939
2368
|
function ColorPickerPopover({
|
|
1940
2369
|
title,
|
|
1941
|
-
|
|
1942
|
-
colors,
|
|
2370
|
+
recentColors = [],
|
|
1943
2371
|
onSelectColor,
|
|
1944
2372
|
onClose,
|
|
1945
2373
|
className
|
|
1946
2374
|
}) {
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
),
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2375
|
+
const [selectedColor, setSelectedColor] = (0, import_react9.useState)("#fef08a");
|
|
2376
|
+
const colorInputRef = (0, import_react9.useRef)(null);
|
|
2377
|
+
const handleConfirm = () => {
|
|
2378
|
+
onSelectColor(selectedColor);
|
|
2379
|
+
};
|
|
2380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2381
|
+
"div",
|
|
2382
|
+
{
|
|
2383
|
+
className: cn("bg-white rounded-lg shadow-xl p-4 w-72", className),
|
|
2384
|
+
onClick: (e) => e.stopPropagation(),
|
|
2385
|
+
children: [
|
|
2386
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-sm font-semibold mb-3", children: title }),
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2389
|
+
"input",
|
|
2390
|
+
{
|
|
2391
|
+
ref: colorInputRef,
|
|
2392
|
+
type: "color",
|
|
2393
|
+
value: selectedColor,
|
|
2394
|
+
onChange: (e) => setSelectedColor(e.target.value),
|
|
2395
|
+
className: "w-10 h-10 rounded border border-gray-200 cursor-pointer p-0.5"
|
|
2396
|
+
}
|
|
2397
|
+
),
|
|
2398
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex-1", children: [
|
|
2399
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2400
|
+
"div",
|
|
2401
|
+
{
|
|
2402
|
+
className: "w-full h-8 rounded border border-gray-200",
|
|
2403
|
+
style: { backgroundColor: selectedColor }
|
|
2404
|
+
}
|
|
2405
|
+
),
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs text-gray-500 mt-1 block", children: selectedColor })
|
|
2407
|
+
] })
|
|
2408
|
+
] }),
|
|
2409
|
+
recentColors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-3", children: [
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs text-gray-500 mb-1.5 block", children: "Recent" }),
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex gap-1.5", children: recentColors.map((color) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2412
|
+
"button",
|
|
2413
|
+
{
|
|
2414
|
+
type: "button",
|
|
2415
|
+
onClick: () => setSelectedColor(color),
|
|
2416
|
+
className: cn(
|
|
2417
|
+
"w-7 h-7 rounded border-2 transition-transform hover:scale-110",
|
|
2418
|
+
selectedColor === color ? "border-blue-500" : "border-gray-200 hover:border-gray-400"
|
|
2419
|
+
),
|
|
2420
|
+
style: { backgroundColor: color },
|
|
2421
|
+
title: color
|
|
2422
|
+
},
|
|
2423
|
+
color
|
|
2424
|
+
)) })
|
|
2425
|
+
] }),
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 pt-2 border-t border-gray-100", children: [
|
|
2427
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2428
|
+
"button",
|
|
2429
|
+
{
|
|
2430
|
+
type: "button",
|
|
2431
|
+
onClick: handleConfirm,
|
|
2432
|
+
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",
|
|
2433
|
+
children: "Apply"
|
|
2434
|
+
}
|
|
2435
|
+
),
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2437
|
+
"button",
|
|
2438
|
+
{
|
|
2439
|
+
type: "button",
|
|
2440
|
+
onClick: () => onSelectColor(null),
|
|
2441
|
+
className: "px-2.5 py-1.5 text-xs text-red-600 hover:bg-red-50 rounded border border-red-200 transition-colors",
|
|
2442
|
+
children: "Clear"
|
|
2443
|
+
}
|
|
2444
|
+
),
|
|
2445
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2446
|
+
"button",
|
|
2447
|
+
{
|
|
2448
|
+
type: "button",
|
|
2449
|
+
onClick: onClose,
|
|
2450
|
+
className: "px-2.5 py-1.5 text-xs text-gray-600 hover:bg-gray-100 rounded transition-colors",
|
|
2451
|
+
children: "Cancel"
|
|
2452
|
+
}
|
|
2453
|
+
)
|
|
2454
|
+
] })
|
|
2455
|
+
]
|
|
2456
|
+
}
|
|
2457
|
+
) });
|
|
1975
2458
|
}
|
|
1976
2459
|
ColorPickerPopover.displayName = "ColorPickerPopover";
|
|
1977
2460
|
|
|
1978
2461
|
// src/components/SpreadsheetSettingsModal.tsx
|
|
1979
|
-
var
|
|
2462
|
+
var import_react10 = require("react");
|
|
1980
2463
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1981
2464
|
var DEFAULT_SETTINGS = {
|
|
1982
2465
|
defaultPinnedColumns: [],
|
|
@@ -1995,9 +2478,9 @@ var SpreadsheetSettingsModal = ({
|
|
|
1995
2478
|
title = "Spreadsheet Settings",
|
|
1996
2479
|
pageSizeOptions = [25, 50, 100, 200]
|
|
1997
2480
|
}) => {
|
|
1998
|
-
const [activeTab, setActiveTab] = (0,
|
|
1999
|
-
const [localSettings, setLocalSettings] = (0,
|
|
2000
|
-
(0,
|
|
2481
|
+
const [activeTab, setActiveTab] = (0, import_react10.useState)("columns");
|
|
2482
|
+
const [localSettings, setLocalSettings] = (0, import_react10.useState)(settings);
|
|
2483
|
+
(0, import_react10.useEffect)(() => {
|
|
2001
2484
|
setLocalSettings(settings);
|
|
2002
2485
|
}, [settings]);
|
|
2003
2486
|
if (!isOpen) return null;
|
|
@@ -2305,11 +2788,11 @@ var SpreadsheetSettingsModal = ({
|
|
|
2305
2788
|
SpreadsheetSettingsModal.displayName = "SpreadsheetSettingsModal";
|
|
2306
2789
|
|
|
2307
2790
|
// src/components/CommentModals.tsx
|
|
2308
|
-
var
|
|
2791
|
+
var import_react11 = require("react");
|
|
2309
2792
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2310
2793
|
function AddCommentModal({ isOpen, columnLabel, onAdd, onClose }) {
|
|
2311
|
-
const [commentText, setCommentText] = (0,
|
|
2312
|
-
(0,
|
|
2794
|
+
const [commentText, setCommentText] = (0, import_react11.useState)("");
|
|
2795
|
+
(0, import_react11.useEffect)(() => {
|
|
2313
2796
|
if (!isOpen) {
|
|
2314
2797
|
setCommentText("");
|
|
2315
2798
|
}
|
|
@@ -2479,7 +2962,7 @@ function DeleteConfirmationModal({
|
|
|
2479
2962
|
DeleteConfirmationModal.displayName = "DeleteConfirmationModal";
|
|
2480
2963
|
|
|
2481
2964
|
// src/components/KeyboardShortcutsModal.tsx
|
|
2482
|
-
var
|
|
2965
|
+
var import_react12 = __toESM(require("react"));
|
|
2483
2966
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2484
2967
|
function KeyboardShortcutsModal({
|
|
2485
2968
|
isOpen,
|
|
@@ -2521,7 +3004,7 @@ function ShortcutSection({ title, children }) {
|
|
|
2521
3004
|
function ShortcutRow({ label, keys }) {
|
|
2522
3005
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2523
3006
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-gray-600 text-sm", children: label }),
|
|
2524
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex items-center gap-1", children: keys.map((key, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
3007
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex items-center gap-1", children: keys.map((key, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react12.default.Fragment, { children: [
|
|
2525
3008
|
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-gray-400", children: "+" }),
|
|
2526
3009
|
key.includes("Click") ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-gray-500 text-xs", children: key }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("kbd", { className: "px-2 py-1 bg-gray-100 text-gray-800 rounded text-xs border border-gray-200", children: key })
|
|
2527
3010
|
] }, index)) })
|
|
@@ -2530,7 +3013,7 @@ function ShortcutRow({ label, keys }) {
|
|
|
2530
3013
|
KeyboardShortcutsModal.displayName = "KeyboardShortcutsModal";
|
|
2531
3014
|
|
|
2532
3015
|
// src/components/RowContextMenu.tsx
|
|
2533
|
-
var
|
|
3016
|
+
var import_react13 = require("react");
|
|
2534
3017
|
var import_design_system = require("@xcelsior/design-system");
|
|
2535
3018
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2536
3019
|
function RowContextMenu({
|
|
@@ -2540,7 +3023,7 @@ function RowContextMenu({
|
|
|
2540
3023
|
compactMode = false,
|
|
2541
3024
|
className
|
|
2542
3025
|
}) {
|
|
2543
|
-
const visibleItems = (0,
|
|
3026
|
+
const visibleItems = (0, import_react13.useMemo)(() => {
|
|
2544
3027
|
return items.filter((item) => !item.visible || item.visible(row));
|
|
2545
3028
|
}, [items, row]);
|
|
2546
3029
|
if (visibleItems.length === 0) {
|
|
@@ -2596,7 +3079,7 @@ RowContextMenu.displayName = "RowContextMenu";
|
|
|
2596
3079
|
var import_design_system2 = require("@xcelsior/design-system");
|
|
2597
3080
|
|
|
2598
3081
|
// src/hooks/useSpreadsheetFiltering.ts
|
|
2599
|
-
var
|
|
3082
|
+
var import_react14 = require("react");
|
|
2600
3083
|
var import_utils11 = require("@xcelsior/utils");
|
|
2601
3084
|
function useSpreadsheetFiltering({
|
|
2602
3085
|
data,
|
|
@@ -2606,18 +3089,20 @@ function useSpreadsheetFiltering({
|
|
|
2606
3089
|
serverSide = false,
|
|
2607
3090
|
controlledFilters,
|
|
2608
3091
|
controlledSortConfig,
|
|
2609
|
-
defaultSortConfig
|
|
3092
|
+
defaultSortConfig,
|
|
3093
|
+
duplicateRowIds,
|
|
3094
|
+
getRowId
|
|
2610
3095
|
}) {
|
|
2611
|
-
const [internalFilters, setInternalFilters] = (0,
|
|
3096
|
+
const [internalFilters, setInternalFilters] = (0, import_react14.useState)(
|
|
2612
3097
|
{}
|
|
2613
3098
|
);
|
|
2614
|
-
const [internalSortConfig, setInternalSortConfig] = (0,
|
|
3099
|
+
const [internalSortConfig, setInternalSortConfig] = (0, import_react14.useState)(
|
|
2615
3100
|
defaultSortConfig ?? null
|
|
2616
3101
|
);
|
|
2617
|
-
const [activeFilterColumn, setActiveFilterColumn] = (0,
|
|
3102
|
+
const [activeFilterColumn, setActiveFilterColumn] = (0, import_react14.useState)(null);
|
|
2618
3103
|
const filters = controlledFilters ?? internalFilters;
|
|
2619
3104
|
const sortConfig = controlledSortConfig !== void 0 ? controlledSortConfig : internalSortConfig;
|
|
2620
|
-
const applyTextCondition = (0,
|
|
3105
|
+
const applyTextCondition = (0, import_react14.useCallback)(
|
|
2621
3106
|
(value, condition) => {
|
|
2622
3107
|
const strValue = String(value ?? "").toLowerCase();
|
|
2623
3108
|
const filterValue = (condition.value ?? "").toLowerCase();
|
|
@@ -2644,7 +3129,7 @@ function useSpreadsheetFiltering({
|
|
|
2644
3129
|
},
|
|
2645
3130
|
[]
|
|
2646
3131
|
);
|
|
2647
|
-
const applyNumberCondition = (0,
|
|
3132
|
+
const applyNumberCondition = (0, import_react14.useCallback)(
|
|
2648
3133
|
(value, condition) => {
|
|
2649
3134
|
if (condition.operator === "isEmpty") return isBlankValue(value);
|
|
2650
3135
|
if (condition.operator === "isNotEmpty") return !isBlankValue(value);
|
|
@@ -2673,7 +3158,7 @@ function useSpreadsheetFiltering({
|
|
|
2673
3158
|
},
|
|
2674
3159
|
[]
|
|
2675
3160
|
);
|
|
2676
|
-
const applyDateCondition = (0,
|
|
3161
|
+
const applyDateCondition = (0, import_react14.useCallback)(
|
|
2677
3162
|
(value, condition) => {
|
|
2678
3163
|
if (condition.operator === "isEmpty") return isBlankValue(value);
|
|
2679
3164
|
if (condition.operator === "isNotEmpty") return !isBlankValue(value);
|
|
@@ -2745,7 +3230,7 @@ function useSpreadsheetFiltering({
|
|
|
2745
3230
|
},
|
|
2746
3231
|
[]
|
|
2747
3232
|
);
|
|
2748
|
-
const buildFilterPredicate = (0,
|
|
3233
|
+
const buildFilterPredicate = (0, import_react14.useCallback)(
|
|
2749
3234
|
(column, filter) => {
|
|
2750
3235
|
return (row) => {
|
|
2751
3236
|
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
@@ -2779,7 +3264,7 @@ function useSpreadsheetFiltering({
|
|
|
2779
3264
|
},
|
|
2780
3265
|
[applyTextCondition, applyNumberCondition, applyDateCondition]
|
|
2781
3266
|
);
|
|
2782
|
-
const buildSortComparator = (0,
|
|
3267
|
+
const buildSortComparator = (0, import_react14.useCallback)(
|
|
2783
3268
|
(column, direction) => {
|
|
2784
3269
|
return (a, b) => {
|
|
2785
3270
|
const aValue = column?.getValue ? column.getValue(a) : a[sortConfig?.columnId];
|
|
@@ -2794,7 +3279,7 @@ function useSpreadsheetFiltering({
|
|
|
2794
3279
|
},
|
|
2795
3280
|
[sortConfig?.columnId]
|
|
2796
3281
|
);
|
|
2797
|
-
const filteredData = (0,
|
|
3282
|
+
const filteredData = (0, import_react14.useMemo)(() => {
|
|
2798
3283
|
if (!data || !Array.isArray(data)) return import_utils11.LazyArray.empty();
|
|
2799
3284
|
if (serverSide) {
|
|
2800
3285
|
return import_utils11.LazyArray.from(data);
|
|
@@ -2807,6 +3292,12 @@ function useSpreadsheetFiltering({
|
|
|
2807
3292
|
const column = columns.find((c) => c.id === columnId);
|
|
2808
3293
|
if (!column) continue;
|
|
2809
3294
|
filterChain.add(buildFilterPredicate(column, filter));
|
|
3295
|
+
if (filter.showDuplicatesOnly && duplicateRowIds && getRowId) {
|
|
3296
|
+
const dupSet = duplicateRowIds.get(columnId);
|
|
3297
|
+
if (dupSet) {
|
|
3298
|
+
filterChain.add((row) => dupSet.has(getRowId(row)));
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
2810
3301
|
}
|
|
2811
3302
|
if (!filterChain.isEmpty) {
|
|
2812
3303
|
lazyResult = filterChain.applyTo(lazyResult);
|
|
@@ -2816,8 +3307,8 @@ function useSpreadsheetFiltering({
|
|
|
2816
3307
|
lazyResult = lazyResult.sort(buildSortComparator(column, sortConfig.direction));
|
|
2817
3308
|
}
|
|
2818
3309
|
return lazyResult;
|
|
2819
|
-
}, [data, filters, sortConfig, columns, serverSide, buildFilterPredicate, buildSortComparator]);
|
|
2820
|
-
const handleFilterChange = (0,
|
|
3310
|
+
}, [data, filters, sortConfig, columns, serverSide, buildFilterPredicate, buildSortComparator, duplicateRowIds, getRowId]);
|
|
3311
|
+
const handleFilterChange = (0, import_react14.useCallback)(
|
|
2821
3312
|
(columnId, filter) => {
|
|
2822
3313
|
const newFilters = { ...filters };
|
|
2823
3314
|
if (filter) {
|
|
@@ -2832,7 +3323,7 @@ function useSpreadsheetFiltering({
|
|
|
2832
3323
|
},
|
|
2833
3324
|
[filters, onFilterChange, controlledFilters]
|
|
2834
3325
|
);
|
|
2835
|
-
const handleSort = (0,
|
|
3326
|
+
const handleSort = (0, import_react14.useCallback)(
|
|
2836
3327
|
(columnId) => {
|
|
2837
3328
|
let newSortConfig;
|
|
2838
3329
|
if (sortConfig?.columnId === columnId) {
|
|
@@ -2851,13 +3342,13 @@ function useSpreadsheetFiltering({
|
|
|
2851
3342
|
},
|
|
2852
3343
|
[sortConfig, onSortChange, controlledSortConfig]
|
|
2853
3344
|
);
|
|
2854
|
-
const clearSort = (0,
|
|
3345
|
+
const clearSort = (0, import_react14.useCallback)(() => {
|
|
2855
3346
|
if (controlledSortConfig === void 0) {
|
|
2856
3347
|
setInternalSortConfig(null);
|
|
2857
3348
|
}
|
|
2858
3349
|
onSortChange?.(null);
|
|
2859
3350
|
}, [onSortChange, controlledSortConfig]);
|
|
2860
|
-
const setSortConfig = (0,
|
|
3351
|
+
const setSortConfig = (0, import_react14.useCallback)(
|
|
2861
3352
|
(config) => {
|
|
2862
3353
|
if (controlledSortConfig === void 0) {
|
|
2863
3354
|
setInternalSortConfig(config);
|
|
@@ -2866,7 +3357,7 @@ function useSpreadsheetFiltering({
|
|
|
2866
3357
|
},
|
|
2867
3358
|
[onSortChange, controlledSortConfig]
|
|
2868
3359
|
);
|
|
2869
|
-
const clearAllFilters = (0,
|
|
3360
|
+
const clearAllFilters = (0, import_react14.useCallback)(() => {
|
|
2870
3361
|
if (controlledFilters === void 0) {
|
|
2871
3362
|
setInternalFilters({});
|
|
2872
3363
|
}
|
|
@@ -2888,24 +3379,274 @@ function useSpreadsheetFiltering({
|
|
|
2888
3379
|
};
|
|
2889
3380
|
}
|
|
2890
3381
|
|
|
3382
|
+
// src/hooks/useSpreadsheetDuplicates.ts
|
|
3383
|
+
var import_react15 = require("react");
|
|
3384
|
+
function normalizeValue(value) {
|
|
3385
|
+
if (value === null || value === void 0 || value === "") {
|
|
3386
|
+
return "__blank__";
|
|
3387
|
+
}
|
|
3388
|
+
if (typeof value === "number") {
|
|
3389
|
+
return String(value);
|
|
3390
|
+
}
|
|
3391
|
+
return String(value).trim().toLowerCase();
|
|
3392
|
+
}
|
|
3393
|
+
function useSpreadsheetDuplicates({
|
|
3394
|
+
data,
|
|
3395
|
+
columns,
|
|
3396
|
+
duplicateCheckColumns,
|
|
3397
|
+
getRowId
|
|
3398
|
+
}) {
|
|
3399
|
+
const [localDuplicateCheckColumns, setLocalDuplicateCheckColumns] = (0, import_react15.useState)(
|
|
3400
|
+
() => new Set(duplicateCheckColumns)
|
|
3401
|
+
);
|
|
3402
|
+
const duplicateCheckColumnsSet = (0, import_react15.useMemo)(() => {
|
|
3403
|
+
return new Set(duplicateCheckColumns);
|
|
3404
|
+
}, [duplicateCheckColumns]);
|
|
3405
|
+
const { duplicateRowIds, valueCounts } = (0, import_react15.useMemo)(() => {
|
|
3406
|
+
const duplicateRowIds2 = /* @__PURE__ */ new Map();
|
|
3407
|
+
const valueCounts2 = /* @__PURE__ */ new Map();
|
|
3408
|
+
const activeColumns = duplicateCheckColumnsSet.size > 0 ? duplicateCheckColumnsSet : localDuplicateCheckColumns;
|
|
3409
|
+
for (const columnId of activeColumns) {
|
|
3410
|
+
const column = columns.find((c) => c.id === columnId);
|
|
3411
|
+
if (!column) continue;
|
|
3412
|
+
const valueToRowIds = /* @__PURE__ */ new Map();
|
|
3413
|
+
for (const row of data) {
|
|
3414
|
+
const rawValue = column.getValue ? column.getValue(row) : row[columnId];
|
|
3415
|
+
if (rawValue === null || rawValue === void 0 || rawValue === "") continue;
|
|
3416
|
+
const normalized = normalizeValue(rawValue);
|
|
3417
|
+
const rowId = getRowId(row);
|
|
3418
|
+
const existing = valueToRowIds.get(normalized);
|
|
3419
|
+
if (existing) {
|
|
3420
|
+
existing.push(rowId);
|
|
3421
|
+
} else {
|
|
3422
|
+
valueToRowIds.set(normalized, [rowId]);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
const dupSet = /* @__PURE__ */ new Set();
|
|
3426
|
+
const countMap = /* @__PURE__ */ new Map();
|
|
3427
|
+
for (const [normalizedVal, rowIds] of valueToRowIds) {
|
|
3428
|
+
countMap.set(normalizedVal, rowIds.length);
|
|
3429
|
+
if (rowIds.length >= 2) {
|
|
3430
|
+
for (const rowId of rowIds) {
|
|
3431
|
+
dupSet.add(rowId);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
duplicateRowIds2.set(columnId, dupSet);
|
|
3436
|
+
valueCounts2.set(columnId, countMap);
|
|
3437
|
+
}
|
|
3438
|
+
return { duplicateRowIds: duplicateRowIds2, valueCounts: valueCounts2 };
|
|
3439
|
+
}, [data, columns, duplicateCheckColumnsSet, localDuplicateCheckColumns, getRowId]);
|
|
3440
|
+
const isCellDuplicate = (0, import_react15.useCallback)(
|
|
3441
|
+
(rowId, columnId) => {
|
|
3442
|
+
return duplicateRowIds.get(columnId)?.has(rowId) ?? false;
|
|
3443
|
+
},
|
|
3444
|
+
[duplicateRowIds]
|
|
3445
|
+
);
|
|
3446
|
+
const getDuplicateCount = (0, import_react15.useCallback)(
|
|
3447
|
+
(columnId, value) => {
|
|
3448
|
+
const normalized = normalizeValue(value);
|
|
3449
|
+
return valueCounts.get(columnId)?.get(normalized) ?? 0;
|
|
3450
|
+
},
|
|
3451
|
+
[valueCounts]
|
|
3452
|
+
);
|
|
3453
|
+
const getDuplicateColumnCount = (0, import_react15.useCallback)(
|
|
3454
|
+
(columnId) => {
|
|
3455
|
+
return duplicateRowIds.get(columnId)?.size ?? 0;
|
|
3456
|
+
},
|
|
3457
|
+
[duplicateRowIds]
|
|
3458
|
+
);
|
|
3459
|
+
const toggleDuplicateCheck = (0, import_react15.useCallback)((columnId) => {
|
|
3460
|
+
setLocalDuplicateCheckColumns((prev) => {
|
|
3461
|
+
const next = new Set(prev);
|
|
3462
|
+
if (next.has(columnId)) {
|
|
3463
|
+
next.delete(columnId);
|
|
3464
|
+
} else {
|
|
3465
|
+
next.add(columnId);
|
|
3466
|
+
}
|
|
3467
|
+
return next;
|
|
3468
|
+
});
|
|
3469
|
+
}, []);
|
|
3470
|
+
const effectiveDuplicateCheckColumns = duplicateCheckColumnsSet.size > 0 ? duplicateCheckColumnsSet : localDuplicateCheckColumns;
|
|
3471
|
+
return {
|
|
3472
|
+
isCellDuplicate,
|
|
3473
|
+
getDuplicateCount,
|
|
3474
|
+
getDuplicateColumnCount,
|
|
3475
|
+
duplicateRowIds,
|
|
3476
|
+
duplicateCheckColumns: effectiveDuplicateCheckColumns,
|
|
3477
|
+
toggleDuplicateCheck
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// src/hooks/useSpreadsheetHighlighting.ts
|
|
3482
|
+
var import_react16 = require("react");
|
|
3483
|
+
function useSpreadsheetHighlighting({
|
|
3484
|
+
externalRowHighlights,
|
|
3485
|
+
onRowHighlight,
|
|
3486
|
+
externalColumnHighlights,
|
|
3487
|
+
onColumnHighlight,
|
|
3488
|
+
externalCellHighlights,
|
|
3489
|
+
onCellHighlight
|
|
3490
|
+
} = {}) {
|
|
3491
|
+
const [cellHighlightsInternal, setCellHighlightsInternal] = (0, import_react16.useState)([]);
|
|
3492
|
+
const [rowHighlightsInternal, setRowHighlightsInternal] = (0, import_react16.useState)([]);
|
|
3493
|
+
const [columnHighlightsInternal, setColumnHighlightsInternal] = (0, import_react16.useState)({});
|
|
3494
|
+
const [recentColors, setRecentColors] = (0, import_react16.useState)([]);
|
|
3495
|
+
const addRecentColor = (0, import_react16.useCallback)((color) => {
|
|
3496
|
+
if (!color) return;
|
|
3497
|
+
setRecentColors((prev) => {
|
|
3498
|
+
const filtered = prev.filter((c) => c !== color);
|
|
3499
|
+
return [color, ...filtered].slice(0, 8);
|
|
3500
|
+
});
|
|
3501
|
+
}, []);
|
|
3502
|
+
const [highlightPickerRow, setHighlightPickerRow] = (0, import_react16.useState)(null);
|
|
3503
|
+
const [highlightPickerColumn, setHighlightPickerColumn] = (0, import_react16.useState)(null);
|
|
3504
|
+
const [highlightPickerCell, setHighlightPickerCell] = (0, import_react16.useState)(null);
|
|
3505
|
+
const cellHighlights = externalCellHighlights || cellHighlightsInternal;
|
|
3506
|
+
const rowHighlights = externalRowHighlights || rowHighlightsInternal;
|
|
3507
|
+
const columnHighlights = externalColumnHighlights || columnHighlightsInternal;
|
|
3508
|
+
const getCellHighlight = (0, import_react16.useCallback)(
|
|
3509
|
+
(rowId, columnId) => {
|
|
3510
|
+
return cellHighlights.find((h) => h.rowId === rowId && h.columnId === columnId)?.color;
|
|
3511
|
+
},
|
|
3512
|
+
[cellHighlights]
|
|
3513
|
+
);
|
|
3514
|
+
const handleCellHighlightToggle = (0, import_react16.useCallback)(
|
|
3515
|
+
(rowId, columnId, color = "#fef08a") => {
|
|
3516
|
+
if (onCellHighlight) {
|
|
3517
|
+
onCellHighlight(rowId, columnId, color);
|
|
3518
|
+
} else {
|
|
3519
|
+
setCellHighlightsInternal((prev) => {
|
|
3520
|
+
const existing = prev.find((h) => h.rowId === rowId && h.columnId === columnId);
|
|
3521
|
+
if (existing) {
|
|
3522
|
+
if (color === null) {
|
|
3523
|
+
return prev.filter(
|
|
3524
|
+
(h) => !(h.rowId === rowId && h.columnId === columnId)
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
return prev.map(
|
|
3528
|
+
(h) => h.rowId === rowId && h.columnId === columnId ? { ...h, color } : h
|
|
3529
|
+
);
|
|
3530
|
+
}
|
|
3531
|
+
if (color) {
|
|
3532
|
+
return [...prev, { rowId, columnId, color }];
|
|
3533
|
+
}
|
|
3534
|
+
return prev;
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
addRecentColor(color);
|
|
3538
|
+
setHighlightPickerCell(null);
|
|
3539
|
+
},
|
|
3540
|
+
[onCellHighlight, addRecentColor]
|
|
3541
|
+
);
|
|
3542
|
+
const getRowHighlight = (0, import_react16.useCallback)(
|
|
3543
|
+
(rowId) => {
|
|
3544
|
+
return rowHighlights.find((h) => h.rowId === rowId && !h.columnId);
|
|
3545
|
+
},
|
|
3546
|
+
[rowHighlights]
|
|
3547
|
+
);
|
|
3548
|
+
const handleRowHighlightToggle = (0, import_react16.useCallback)(
|
|
3549
|
+
(rowId, color) => {
|
|
3550
|
+
if (onRowHighlight) {
|
|
3551
|
+
onRowHighlight(rowId, color);
|
|
3552
|
+
} else {
|
|
3553
|
+
setRowHighlightsInternal((prev) => {
|
|
3554
|
+
const existing = prev.find((h) => h.rowId === rowId && !h.columnId);
|
|
3555
|
+
if (existing) {
|
|
3556
|
+
if (color === null) {
|
|
3557
|
+
return prev.filter((h) => !(h.rowId === rowId && !h.columnId));
|
|
3558
|
+
}
|
|
3559
|
+
return prev.map(
|
|
3560
|
+
(h) => h.rowId === rowId && !h.columnId ? { ...h, color } : h
|
|
3561
|
+
);
|
|
3562
|
+
}
|
|
3563
|
+
if (color) {
|
|
3564
|
+
return [...prev, { rowId, color }];
|
|
3565
|
+
}
|
|
3566
|
+
return prev;
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
addRecentColor(color);
|
|
3570
|
+
setHighlightPickerRow(null);
|
|
3571
|
+
},
|
|
3572
|
+
[onRowHighlight, addRecentColor]
|
|
3573
|
+
);
|
|
3574
|
+
const getColumnHighlight = (0, import_react16.useCallback)(
|
|
3575
|
+
(columnId) => {
|
|
3576
|
+
return columnHighlights[columnId];
|
|
3577
|
+
},
|
|
3578
|
+
[columnHighlights]
|
|
3579
|
+
);
|
|
3580
|
+
const handleColumnHighlightToggle = (0, import_react16.useCallback)(
|
|
3581
|
+
(columnId, color) => {
|
|
3582
|
+
if (onColumnHighlight) {
|
|
3583
|
+
onColumnHighlight(columnId, color);
|
|
3584
|
+
} else {
|
|
3585
|
+
setColumnHighlightsInternal((prev) => {
|
|
3586
|
+
const newHighlights = { ...prev };
|
|
3587
|
+
if (color === null) {
|
|
3588
|
+
delete newHighlights[columnId];
|
|
3589
|
+
} else {
|
|
3590
|
+
newHighlights[columnId] = color;
|
|
3591
|
+
}
|
|
3592
|
+
return newHighlights;
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
addRecentColor(color);
|
|
3596
|
+
setHighlightPickerColumn(null);
|
|
3597
|
+
},
|
|
3598
|
+
[onColumnHighlight, addRecentColor]
|
|
3599
|
+
);
|
|
3600
|
+
const clearAllHighlights = (0, import_react16.useCallback)(() => {
|
|
3601
|
+
setCellHighlightsInternal([]);
|
|
3602
|
+
setRowHighlightsInternal([]);
|
|
3603
|
+
setColumnHighlightsInternal({});
|
|
3604
|
+
}, []);
|
|
3605
|
+
return {
|
|
3606
|
+
// Cell highlights
|
|
3607
|
+
cellHighlights,
|
|
3608
|
+
getCellHighlight,
|
|
3609
|
+
handleCellHighlightToggle,
|
|
3610
|
+
// Row highlights
|
|
3611
|
+
rowHighlights,
|
|
3612
|
+
getRowHighlight,
|
|
3613
|
+
handleRowHighlightToggle,
|
|
3614
|
+
// Column highlights
|
|
3615
|
+
columnHighlights,
|
|
3616
|
+
getColumnHighlight,
|
|
3617
|
+
handleColumnHighlightToggle,
|
|
3618
|
+
// Recent colors
|
|
3619
|
+
recentColors,
|
|
3620
|
+
// Picker state
|
|
3621
|
+
highlightPickerRow,
|
|
3622
|
+
setHighlightPickerRow,
|
|
3623
|
+
highlightPickerColumn,
|
|
3624
|
+
setHighlightPickerColumn,
|
|
3625
|
+
highlightPickerCell,
|
|
3626
|
+
setHighlightPickerCell,
|
|
3627
|
+
// Utility
|
|
3628
|
+
clearAllHighlights
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
|
|
2891
3632
|
// src/hooks/useSpreadsheetComments.ts
|
|
2892
|
-
var
|
|
3633
|
+
var import_react17 = require("react");
|
|
2893
3634
|
function useSpreadsheetComments({
|
|
2894
3635
|
externalCellComments,
|
|
2895
3636
|
onAddCellComment,
|
|
2896
3637
|
onToggleCommentResolved
|
|
2897
3638
|
} = {}) {
|
|
2898
|
-
const [cellCommentsInternal, setCellCommentsInternal] = (0,
|
|
2899
|
-
const [commentModalCell, setCommentModalCell] = (0,
|
|
2900
|
-
const [viewCommentsCell, setViewCommentsCell] = (0,
|
|
3639
|
+
const [cellCommentsInternal, setCellCommentsInternal] = (0, import_react17.useState)([]);
|
|
3640
|
+
const [commentModalCell, setCommentModalCell] = (0, import_react17.useState)(null);
|
|
3641
|
+
const [viewCommentsCell, setViewCommentsCell] = (0, import_react17.useState)(null);
|
|
2901
3642
|
const cellComments = externalCellComments || cellCommentsInternal;
|
|
2902
|
-
const getCellComments = (0,
|
|
3643
|
+
const getCellComments = (0, import_react17.useCallback)(
|
|
2903
3644
|
(rowId, columnId) => {
|
|
2904
3645
|
return cellComments.filter((c) => c.rowId === rowId && c.columnId === columnId);
|
|
2905
3646
|
},
|
|
2906
3647
|
[cellComments]
|
|
2907
3648
|
);
|
|
2908
|
-
const getCellUnresolvedCommentCount = (0,
|
|
3649
|
+
const getCellUnresolvedCommentCount = (0, import_react17.useCallback)(
|
|
2909
3650
|
(rowId, columnId) => {
|
|
2910
3651
|
return cellComments.filter(
|
|
2911
3652
|
(c) => c.rowId === rowId && c.columnId === columnId && !c.resolved
|
|
@@ -2913,13 +3654,13 @@ function useSpreadsheetComments({
|
|
|
2913
3654
|
},
|
|
2914
3655
|
[cellComments]
|
|
2915
3656
|
);
|
|
2916
|
-
const cellHasComments = (0,
|
|
3657
|
+
const cellHasComments = (0, import_react17.useCallback)(
|
|
2917
3658
|
(rowId, columnId) => {
|
|
2918
3659
|
return cellComments.some((c) => c.rowId === rowId && c.columnId === columnId);
|
|
2919
3660
|
},
|
|
2920
3661
|
[cellComments]
|
|
2921
3662
|
);
|
|
2922
|
-
const handleAddCellComment = (0,
|
|
3663
|
+
const handleAddCellComment = (0, import_react17.useCallback)(
|
|
2923
3664
|
(rowId, columnId, text) => {
|
|
2924
3665
|
if (!text.trim()) return;
|
|
2925
3666
|
if (onAddCellComment) {
|
|
@@ -2941,7 +3682,7 @@ function useSpreadsheetComments({
|
|
|
2941
3682
|
},
|
|
2942
3683
|
[onAddCellComment]
|
|
2943
3684
|
);
|
|
2944
|
-
const handleToggleCommentResolved = (0,
|
|
3685
|
+
const handleToggleCommentResolved = (0, import_react17.useCallback)(
|
|
2945
3686
|
(commentId) => {
|
|
2946
3687
|
if (onToggleCommentResolved) {
|
|
2947
3688
|
onToggleCommentResolved(commentId);
|
|
@@ -2973,20 +3714,20 @@ function useSpreadsheetComments({
|
|
|
2973
3714
|
}
|
|
2974
3715
|
|
|
2975
3716
|
// src/hooks/useSpreadsheetUndoRedo.ts
|
|
2976
|
-
var
|
|
3717
|
+
var import_react18 = require("react");
|
|
2977
3718
|
function useSpreadsheetUndoRedo({
|
|
2978
3719
|
enabled = true,
|
|
2979
3720
|
maxStackSize = 50,
|
|
2980
3721
|
autoSave = true,
|
|
2981
3722
|
onSave
|
|
2982
3723
|
}) {
|
|
2983
|
-
const [undoStack, setUndoStack] = (0,
|
|
2984
|
-
const [redoStack, setRedoStack] = (0,
|
|
2985
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] = (0,
|
|
2986
|
-
const [saveStatus, setSaveStatus] = (0,
|
|
2987
|
-
const onSaveRef = (0,
|
|
3724
|
+
const [undoStack, setUndoStack] = (0, import_react18.useState)([]);
|
|
3725
|
+
const [redoStack, setRedoStack] = (0, import_react18.useState)([]);
|
|
3726
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react18.useState)(false);
|
|
3727
|
+
const [saveStatus, setSaveStatus] = (0, import_react18.useState)("saved");
|
|
3728
|
+
const onSaveRef = (0, import_react18.useRef)(onSave);
|
|
2988
3729
|
onSaveRef.current = onSave;
|
|
2989
|
-
const pushToUndoStack = (0,
|
|
3730
|
+
const pushToUndoStack = (0, import_react18.useCallback)(
|
|
2990
3731
|
(snapshot) => {
|
|
2991
3732
|
if (!enabled) return;
|
|
2992
3733
|
setUndoStack((prev) => {
|
|
@@ -3000,7 +3741,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3000
3741
|
},
|
|
3001
3742
|
[enabled, maxStackSize]
|
|
3002
3743
|
);
|
|
3003
|
-
const handleUndo = (0,
|
|
3744
|
+
const handleUndo = (0, import_react18.useCallback)(() => {
|
|
3004
3745
|
if (!enabled || undoStack.length === 0) return null;
|
|
3005
3746
|
const previousSnapshot = undoStack[undoStack.length - 1];
|
|
3006
3747
|
setUndoStack((prev) => prev.slice(0, -1));
|
|
@@ -3013,7 +3754,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3013
3754
|
});
|
|
3014
3755
|
return previousSnapshot;
|
|
3015
3756
|
}, [enabled, undoStack, maxStackSize]);
|
|
3016
|
-
const handleRedo = (0,
|
|
3757
|
+
const handleRedo = (0, import_react18.useCallback)(() => {
|
|
3017
3758
|
if (!enabled || redoStack.length === 0) return null;
|
|
3018
3759
|
const nextSnapshot = redoStack[redoStack.length - 1];
|
|
3019
3760
|
setRedoStack((prev) => prev.slice(0, -1));
|
|
@@ -3026,7 +3767,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3026
3767
|
});
|
|
3027
3768
|
return nextSnapshot;
|
|
3028
3769
|
}, [enabled, redoStack, maxStackSize]);
|
|
3029
|
-
const handleSave = (0,
|
|
3770
|
+
const handleSave = (0, import_react18.useCallback)(async () => {
|
|
3030
3771
|
if (!hasUnsavedChanges && !autoSave) return;
|
|
3031
3772
|
setSaveStatus("saving");
|
|
3032
3773
|
try {
|
|
@@ -3039,7 +3780,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3039
3780
|
setSaveStatus("error");
|
|
3040
3781
|
}
|
|
3041
3782
|
}, [hasUnsavedChanges, autoSave]);
|
|
3042
|
-
const markAsChanged = (0,
|
|
3783
|
+
const markAsChanged = (0, import_react18.useCallback)(() => {
|
|
3043
3784
|
setHasUnsavedChanges(true);
|
|
3044
3785
|
if (autoSave) {
|
|
3045
3786
|
setSaveStatus("saving");
|
|
@@ -3060,11 +3801,11 @@ function useSpreadsheetUndoRedo({
|
|
|
3060
3801
|
setSaveStatus("unsaved");
|
|
3061
3802
|
}
|
|
3062
3803
|
}, [autoSave]);
|
|
3063
|
-
const markAsSaved = (0,
|
|
3804
|
+
const markAsSaved = (0, import_react18.useCallback)(() => {
|
|
3064
3805
|
setHasUnsavedChanges(false);
|
|
3065
3806
|
setSaveStatus("saved");
|
|
3066
3807
|
}, []);
|
|
3067
|
-
const clearStacks = (0,
|
|
3808
|
+
const clearStacks = (0, import_react18.useCallback)(() => {
|
|
3068
3809
|
setUndoStack([]);
|
|
3069
3810
|
setRedoStack([]);
|
|
3070
3811
|
}, []);
|
|
@@ -3092,7 +3833,7 @@ function useSpreadsheetUndoRedo({
|
|
|
3092
3833
|
}
|
|
3093
3834
|
|
|
3094
3835
|
// src/hooks/useSpreadsheetKeyboardShortcuts.ts
|
|
3095
|
-
var
|
|
3836
|
+
var import_react19 = require("react");
|
|
3096
3837
|
function useSpreadsheetKeyboardShortcuts({
|
|
3097
3838
|
onUndo,
|
|
3098
3839
|
onRedo,
|
|
@@ -3102,15 +3843,16 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3102
3843
|
onTabNavigation,
|
|
3103
3844
|
onCopy,
|
|
3104
3845
|
onPaste,
|
|
3846
|
+
onSelectAll,
|
|
3105
3847
|
hasFocusedCell = false,
|
|
3106
3848
|
isEditing = false,
|
|
3107
3849
|
customShortcuts = [],
|
|
3108
3850
|
enabled = true
|
|
3109
3851
|
} = {}) {
|
|
3110
|
-
const [showKeyboardShortcuts, setShowKeyboardShortcuts] = (0,
|
|
3852
|
+
const [showKeyboardShortcuts, setShowKeyboardShortcuts] = (0, import_react19.useState)(false);
|
|
3111
3853
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPod|iPad/.test(navigator.platform);
|
|
3112
3854
|
const modifierKey = isMac ? "\u2318" : "Ctrl";
|
|
3113
|
-
(0,
|
|
3855
|
+
(0, import_react19.useEffect)(() => {
|
|
3114
3856
|
if (!enabled) return;
|
|
3115
3857
|
const handleKeyDown = (event) => {
|
|
3116
3858
|
if (event.key === "Escape") {
|
|
@@ -3136,6 +3878,11 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3136
3878
|
onRedo?.();
|
|
3137
3879
|
return;
|
|
3138
3880
|
}
|
|
3881
|
+
if ((event.metaKey || event.ctrlKey) && (event.key === "a" || event.key === "A") && !isEditing) {
|
|
3882
|
+
event.preventDefault();
|
|
3883
|
+
onSelectAll?.();
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3139
3886
|
if ((event.metaKey || event.ctrlKey) && event.key === "c" && !isEditing && hasFocusedCell) {
|
|
3140
3887
|
event.preventDefault();
|
|
3141
3888
|
onCopy?.();
|
|
@@ -3200,6 +3947,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3200
3947
|
onTabNavigation,
|
|
3201
3948
|
onCopy,
|
|
3202
3949
|
onPaste,
|
|
3950
|
+
onSelectAll,
|
|
3203
3951
|
hasFocusedCell,
|
|
3204
3952
|
isEditing,
|
|
3205
3953
|
customShortcuts
|
|
@@ -3217,6 +3965,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3217
3965
|
editing: [
|
|
3218
3966
|
{ label: "Undo", keys: [modifierKey, "Z"] },
|
|
3219
3967
|
{ label: "Redo", keys: [modifierKey, "Shift", "Z"] },
|
|
3968
|
+
{ label: "Select all", keys: [modifierKey, "A"] },
|
|
3220
3969
|
{ label: "Copy cells", keys: [modifierKey, "C"] },
|
|
3221
3970
|
{ label: "Paste cells", keys: [modifierKey, "V"] },
|
|
3222
3971
|
{ label: "Confirm cell edit", keys: ["Enter"] },
|
|
@@ -3251,7 +4000,7 @@ function useSpreadsheetKeyboardShortcuts({
|
|
|
3251
4000
|
}
|
|
3252
4001
|
|
|
3253
4002
|
// src/hooks/useSpreadsheetSelection.ts
|
|
3254
|
-
var
|
|
4003
|
+
var import_react20 = require("react");
|
|
3255
4004
|
function useSpreadsheetSelection({
|
|
3256
4005
|
data,
|
|
3257
4006
|
columns,
|
|
@@ -3259,34 +4008,34 @@ function useSpreadsheetSelection({
|
|
|
3259
4008
|
onCellRangeSelectionChange,
|
|
3260
4009
|
enableCellEditing = true
|
|
3261
4010
|
}) {
|
|
3262
|
-
const [focusedCell, setFocusedCell] = (0,
|
|
3263
|
-
const [editingCell, setEditingCell] = (0,
|
|
3264
|
-
const [selectedCellRange, setSelectedCellRangeState] = (0,
|
|
3265
|
-
const [isDragging, setIsDragging] = (0,
|
|
3266
|
-
const [clipboardData, setClipboardData] = (0,
|
|
3267
|
-
const anchorCell = (0,
|
|
3268
|
-
const rowIndexMap = (0,
|
|
4011
|
+
const [focusedCell, setFocusedCell] = (0, import_react20.useState)(null);
|
|
4012
|
+
const [editingCell, setEditingCell] = (0, import_react20.useState)(null);
|
|
4013
|
+
const [selectedCellRange, setSelectedCellRangeState] = (0, import_react20.useState)(null);
|
|
4014
|
+
const [isDragging, setIsDragging] = (0, import_react20.useState)(false);
|
|
4015
|
+
const [clipboardData, setClipboardData] = (0, import_react20.useState)(null);
|
|
4016
|
+
const anchorCell = (0, import_react20.useRef)(null);
|
|
4017
|
+
const rowIndexMap = (0, import_react20.useMemo)(() => {
|
|
3269
4018
|
const map = /* @__PURE__ */ new Map();
|
|
3270
4019
|
data.forEach((row, index) => {
|
|
3271
4020
|
map.set(getRowId(row), index);
|
|
3272
4021
|
});
|
|
3273
4022
|
return map;
|
|
3274
4023
|
}, [data, getRowId]);
|
|
3275
|
-
const columnIndexMap = (0,
|
|
4024
|
+
const columnIndexMap = (0, import_react20.useMemo)(() => {
|
|
3276
4025
|
const map = /* @__PURE__ */ new Map();
|
|
3277
4026
|
columns.forEach((col, index) => {
|
|
3278
4027
|
map.set(col.id, index);
|
|
3279
4028
|
});
|
|
3280
4029
|
return map;
|
|
3281
4030
|
}, [columns]);
|
|
3282
|
-
const setSelectedCellRange = (0,
|
|
4031
|
+
const setSelectedCellRange = (0, import_react20.useCallback)(
|
|
3283
4032
|
(range) => {
|
|
3284
4033
|
setSelectedCellRangeState(range);
|
|
3285
4034
|
onCellRangeSelectionChange?.(range);
|
|
3286
4035
|
},
|
|
3287
4036
|
[onCellRangeSelectionChange]
|
|
3288
4037
|
);
|
|
3289
|
-
const getNormalizedRange = (0,
|
|
4038
|
+
const getNormalizedRange = (0, import_react20.useCallback)(
|
|
3290
4039
|
(range) => {
|
|
3291
4040
|
if (!range) return null;
|
|
3292
4041
|
const startRowIdx = rowIndexMap.get(range.start.rowId);
|
|
@@ -3305,7 +4054,7 @@ function useSpreadsheetSelection({
|
|
|
3305
4054
|
},
|
|
3306
4055
|
[rowIndexMap, columnIndexMap]
|
|
3307
4056
|
);
|
|
3308
|
-
const isCellInSelection = (0,
|
|
4057
|
+
const isCellInSelection = (0, import_react20.useCallback)(
|
|
3309
4058
|
(rowId, columnId) => {
|
|
3310
4059
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3311
4060
|
if (!normalizedRange) return false;
|
|
@@ -3316,7 +4065,7 @@ function useSpreadsheetSelection({
|
|
|
3316
4065
|
},
|
|
3317
4066
|
[selectedCellRange, rowIndexMap, columnIndexMap, getNormalizedRange]
|
|
3318
4067
|
);
|
|
3319
|
-
const getCellSelectionEdge = (0,
|
|
4068
|
+
const getCellSelectionEdge = (0, import_react20.useCallback)(
|
|
3320
4069
|
(rowId, columnId) => {
|
|
3321
4070
|
if (!isCellInSelection(rowId, columnId)) return void 0;
|
|
3322
4071
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
@@ -3333,7 +4082,7 @@ function useSpreadsheetSelection({
|
|
|
3333
4082
|
},
|
|
3334
4083
|
[isCellInSelection, selectedCellRange, rowIndexMap, columnIndexMap, getNormalizedRange]
|
|
3335
4084
|
);
|
|
3336
|
-
const getSelectedCells = (0,
|
|
4085
|
+
const getSelectedCells = (0, import_react20.useCallback)(() => {
|
|
3337
4086
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3338
4087
|
if (!normalizedRange) {
|
|
3339
4088
|
return focusedCell ? [focusedCell] : [];
|
|
@@ -3351,7 +4100,7 @@ function useSpreadsheetSelection({
|
|
|
3351
4100
|
}
|
|
3352
4101
|
return cells;
|
|
3353
4102
|
}, [selectedCellRange, focusedCell, data, columns, getRowId, getNormalizedRange]);
|
|
3354
|
-
const getSelectedCellValues = (0,
|
|
4103
|
+
const getSelectedCellValues = (0, import_react20.useCallback)(() => {
|
|
3355
4104
|
const cells = getSelectedCells();
|
|
3356
4105
|
return cells.map((cell) => {
|
|
3357
4106
|
const row = data.find((r) => getRowId(r) === cell.rowId);
|
|
@@ -3360,7 +4109,7 @@ function useSpreadsheetSelection({
|
|
|
3360
4109
|
return { position: cell, value };
|
|
3361
4110
|
});
|
|
3362
4111
|
}, [getSelectedCells, data, columns, getRowId]);
|
|
3363
|
-
const handleCellClick = (0,
|
|
4112
|
+
const handleCellClick = (0, import_react20.useCallback)(
|
|
3364
4113
|
(rowId, columnId, event) => {
|
|
3365
4114
|
event.stopPropagation();
|
|
3366
4115
|
const newCell = { rowId, columnId };
|
|
@@ -3374,15 +4123,12 @@ function useSpreadsheetSelection({
|
|
|
3374
4123
|
anchorCell.current = newCell;
|
|
3375
4124
|
setFocusedCell(newCell);
|
|
3376
4125
|
setSelectedCellRange(null);
|
|
3377
|
-
|
|
3378
|
-
if (column?.editable && enableCellEditing) {
|
|
3379
|
-
setEditingCell(newCell);
|
|
3380
|
-
}
|
|
4126
|
+
setEditingCell(null);
|
|
3381
4127
|
}
|
|
3382
4128
|
},
|
|
3383
|
-
[
|
|
4129
|
+
[setSelectedCellRange]
|
|
3384
4130
|
);
|
|
3385
|
-
const handleCellMouseDown = (0,
|
|
4131
|
+
const handleCellMouseDown = (0, import_react20.useCallback)(
|
|
3386
4132
|
(rowId, columnId, event) => {
|
|
3387
4133
|
if (event.button !== 0) return;
|
|
3388
4134
|
const newCell = { rowId, columnId };
|
|
@@ -3398,28 +4144,23 @@ function useSpreadsheetSelection({
|
|
|
3398
4144
|
anchorCell.current = newCell;
|
|
3399
4145
|
setFocusedCell(newCell);
|
|
3400
4146
|
setSelectedCellRange(null);
|
|
3401
|
-
|
|
3402
|
-
if (column?.editable && enableCellEditing) {
|
|
3403
|
-
setEditingCell(newCell);
|
|
3404
|
-
} else {
|
|
3405
|
-
setEditingCell(null);
|
|
3406
|
-
}
|
|
4147
|
+
setEditingCell(null);
|
|
3407
4148
|
}
|
|
3408
4149
|
},
|
|
3409
|
-
[
|
|
4150
|
+
[setSelectedCellRange]
|
|
3410
4151
|
);
|
|
3411
|
-
const handleCellMouseEnter = (0,
|
|
4152
|
+
const handleCellMouseEnter = (0, import_react20.useCallback)((_rowId, _columnId) => {
|
|
3412
4153
|
}, []);
|
|
3413
|
-
const handleMouseUp = (0,
|
|
4154
|
+
const handleMouseUp = (0, import_react20.useCallback)(() => {
|
|
3414
4155
|
setIsDragging(false);
|
|
3415
4156
|
}, []);
|
|
3416
|
-
const clearSelection = (0,
|
|
4157
|
+
const clearSelection = (0, import_react20.useCallback)(() => {
|
|
3417
4158
|
setFocusedCell(null);
|
|
3418
4159
|
setEditingCell(null);
|
|
3419
4160
|
setSelectedCellRange(null);
|
|
3420
4161
|
anchorCell.current = null;
|
|
3421
4162
|
}, [setSelectedCellRange]);
|
|
3422
|
-
const navigateCell = (0,
|
|
4163
|
+
const navigateCell = (0, import_react20.useCallback)(
|
|
3423
4164
|
(direction, extendSelection = false) => {
|
|
3424
4165
|
const currentCell = focusedCell;
|
|
3425
4166
|
if (!currentCell) return;
|
|
@@ -3466,7 +4207,7 @@ function useSpreadsheetSelection({
|
|
|
3466
4207
|
},
|
|
3467
4208
|
[focusedCell, data, columns, getRowId, rowIndexMap, columnIndexMap, setSelectedCellRange]
|
|
3468
4209
|
);
|
|
3469
|
-
const handleTabNavigation = (0,
|
|
4210
|
+
const handleTabNavigation = (0, import_react20.useCallback)(
|
|
3470
4211
|
(shiftKey) => {
|
|
3471
4212
|
const currentCell = focusedCell;
|
|
3472
4213
|
if (!currentCell) return;
|
|
@@ -3507,17 +4248,17 @@ function useSpreadsheetSelection({
|
|
|
3507
4248
|
},
|
|
3508
4249
|
[focusedCell, data, columns, getRowId, rowIndexMap, columnIndexMap, setSelectedCellRange]
|
|
3509
4250
|
);
|
|
3510
|
-
const enterEditMode = (0,
|
|
4251
|
+
const enterEditMode = (0, import_react20.useCallback)(() => {
|
|
3511
4252
|
if (!focusedCell || !enableCellEditing) return;
|
|
3512
4253
|
const column = columns.find((c) => c.id === focusedCell.columnId);
|
|
3513
4254
|
if (column?.editable) {
|
|
3514
4255
|
setEditingCell(focusedCell);
|
|
3515
4256
|
}
|
|
3516
4257
|
}, [focusedCell, columns, enableCellEditing]);
|
|
3517
|
-
const exitEditMode = (0,
|
|
4258
|
+
const exitEditMode = (0, import_react20.useCallback)(() => {
|
|
3518
4259
|
setEditingCell(null);
|
|
3519
4260
|
}, []);
|
|
3520
|
-
const copySelectedCells = (0,
|
|
4261
|
+
const copySelectedCells = (0, import_react20.useCallback)(() => {
|
|
3521
4262
|
const normalizedRange = getNormalizedRange(selectedCellRange);
|
|
3522
4263
|
if (!normalizedRange && !focusedCell) return;
|
|
3523
4264
|
const startRowIdx = normalizedRange?.startRowIdx ?? rowIndexMap.get(focusedCell.rowId) ?? 0;
|
|
@@ -3566,14 +4307,14 @@ function useSpreadsheetSelection({
|
|
|
3566
4307
|
rowIndexMap,
|
|
3567
4308
|
columnIndexMap
|
|
3568
4309
|
]);
|
|
3569
|
-
const parseClipboardText = (0,
|
|
4310
|
+
const parseClipboardText = (0, import_react20.useCallback)((text) => {
|
|
3570
4311
|
const lines = text.split(/\r?\n/);
|
|
3571
4312
|
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
3572
4313
|
lines.pop();
|
|
3573
4314
|
}
|
|
3574
4315
|
return lines.map((line) => line.split(" "));
|
|
3575
4316
|
}, []);
|
|
3576
|
-
const createEditsFromValues = (0,
|
|
4317
|
+
const createEditsFromValues = (0, import_react20.useCallback)(
|
|
3577
4318
|
(values) => {
|
|
3578
4319
|
if (!focusedCell) return [];
|
|
3579
4320
|
const edits = [];
|
|
@@ -3633,53 +4374,238 @@ function useSpreadsheetSelection({
|
|
|
3633
4374
|
getNormalizedRange
|
|
3634
4375
|
]
|
|
3635
4376
|
);
|
|
3636
|
-
const pasteClipboard = (0,
|
|
4377
|
+
const pasteClipboard = (0, import_react20.useCallback)(() => {
|
|
3637
4378
|
if (!clipboardData?.values) return [];
|
|
3638
4379
|
return createEditsFromValues(clipboardData.values);
|
|
3639
4380
|
}, [clipboardData, createEditsFromValues]);
|
|
3640
|
-
const pasteFromSystemClipboard = (0,
|
|
4381
|
+
const pasteFromSystemClipboard = (0, import_react20.useCallback)(async () => {
|
|
3641
4382
|
if (!focusedCell) return [];
|
|
3642
4383
|
try {
|
|
3643
4384
|
const text = await navigator.clipboard.readText();
|
|
3644
4385
|
if (!text) {
|
|
3645
4386
|
return pasteClipboard();
|
|
3646
4387
|
}
|
|
3647
|
-
const values = parseClipboardText(text);
|
|
3648
|
-
return createEditsFromValues(values);
|
|
3649
|
-
} catch {
|
|
3650
|
-
return pasteClipboard();
|
|
4388
|
+
const values = parseClipboardText(text);
|
|
4389
|
+
return createEditsFromValues(values);
|
|
4390
|
+
} catch {
|
|
4391
|
+
return pasteClipboard();
|
|
4392
|
+
}
|
|
4393
|
+
}, [focusedCell, pasteClipboard, parseClipboardText, createEditsFromValues]);
|
|
4394
|
+
return {
|
|
4395
|
+
focusedCell,
|
|
4396
|
+
setFocusedCell,
|
|
4397
|
+
editingCell,
|
|
4398
|
+
setEditingCell,
|
|
4399
|
+
selectedCellRange,
|
|
4400
|
+
setSelectedCellRange,
|
|
4401
|
+
isDragging,
|
|
4402
|
+
handleCellClick,
|
|
4403
|
+
handleCellMouseDown,
|
|
4404
|
+
handleCellMouseEnter,
|
|
4405
|
+
handleMouseUp,
|
|
4406
|
+
isCellInSelection,
|
|
4407
|
+
getCellSelectionEdge,
|
|
4408
|
+
getSelectedCells,
|
|
4409
|
+
getSelectedCellValues,
|
|
4410
|
+
clearSelection,
|
|
4411
|
+
navigateCell,
|
|
4412
|
+
handleTabNavigation,
|
|
4413
|
+
enterEditMode,
|
|
4414
|
+
exitEditMode,
|
|
4415
|
+
clipboardData,
|
|
4416
|
+
copySelectedCells,
|
|
4417
|
+
pasteClipboard,
|
|
4418
|
+
pasteFromSystemClipboard
|
|
4419
|
+
};
|
|
4420
|
+
}
|
|
4421
|
+
|
|
4422
|
+
// src/hooks/useSpreadsheetSummary.ts
|
|
4423
|
+
var import_react21 = require("react");
|
|
4424
|
+
function useSpreadsheetSummary({
|
|
4425
|
+
selectedCellValues,
|
|
4426
|
+
columns
|
|
4427
|
+
}) {
|
|
4428
|
+
const summary = (0, import_react21.useMemo)(() => {
|
|
4429
|
+
if (selectedCellValues.length === 0) return null;
|
|
4430
|
+
const numericValues = [];
|
|
4431
|
+
for (const { position, value } of selectedCellValues) {
|
|
4432
|
+
const column = columns.find((c) => c.id === position.columnId);
|
|
4433
|
+
if (column?.type === "number" || typeof value === "number") {
|
|
4434
|
+
const num = typeof value === "number" ? value : parseFloat(value);
|
|
4435
|
+
if (!isNaN(num)) {
|
|
4436
|
+
numericValues.push(num);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
if (numericValues.length === 0) return null;
|
|
4441
|
+
const sum = numericValues.reduce((a, b) => a + b, 0);
|
|
4442
|
+
return {
|
|
4443
|
+
sum: Math.round(sum * 100) / 100,
|
|
4444
|
+
avg: Math.round(sum / numericValues.length * 100) / 100,
|
|
4445
|
+
count: selectedCellValues.length,
|
|
4446
|
+
numericCount: numericValues.length,
|
|
4447
|
+
min: Math.round(Math.min(...numericValues) * 100) / 100,
|
|
4448
|
+
max: Math.round(Math.max(...numericValues) * 100) / 100
|
|
4449
|
+
};
|
|
4450
|
+
}, [selectedCellValues, columns]);
|
|
4451
|
+
return {
|
|
4452
|
+
summary,
|
|
4453
|
+
hasNumericValues: summary !== null
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
// src/hooks/useSpreadsheetColumnResize.ts
|
|
4458
|
+
var import_react22 = require("react");
|
|
4459
|
+
var DEFAULT_MIN_WIDTH = 40;
|
|
4460
|
+
function useSpreadsheetColumnResize({
|
|
4461
|
+
minWidth = DEFAULT_MIN_WIDTH,
|
|
4462
|
+
initialColumnWidths,
|
|
4463
|
+
onColumnResize
|
|
4464
|
+
} = {}) {
|
|
4465
|
+
const [columnWidths, setColumnWidths] = (0, import_react22.useState)(() => {
|
|
4466
|
+
if (initialColumnWidths) {
|
|
4467
|
+
return new Map(Object.entries(initialColumnWidths));
|
|
4468
|
+
}
|
|
4469
|
+
return /* @__PURE__ */ new Map();
|
|
4470
|
+
});
|
|
4471
|
+
const [resizingColumnId, setResizingColumnId] = (0, import_react22.useState)(null);
|
|
4472
|
+
const startXRef = (0, import_react22.useRef)(0);
|
|
4473
|
+
const startWidthRef = (0, import_react22.useRef)(0);
|
|
4474
|
+
const rafRef = (0, import_react22.useRef)(null);
|
|
4475
|
+
const getColumnWidth = (0, import_react22.useCallback)(
|
|
4476
|
+
(columnId, defaultWidth) => {
|
|
4477
|
+
return columnWidths.get(columnId) ?? defaultWidth;
|
|
4478
|
+
},
|
|
4479
|
+
[columnWidths]
|
|
4480
|
+
);
|
|
4481
|
+
const updateColumnDom = (0, import_react22.useCallback)((columnId, width) => {
|
|
4482
|
+
const widthPx = `${width}px`;
|
|
4483
|
+
const cells = document.querySelectorAll(`[data-column-id="${columnId}"]`);
|
|
4484
|
+
for (let i = 0; i < cells.length; i++) {
|
|
4485
|
+
const el = cells[i];
|
|
4486
|
+
el.style.width = widthPx;
|
|
4487
|
+
el.style.minWidth = widthPx;
|
|
4488
|
+
}
|
|
4489
|
+
}, []);
|
|
4490
|
+
const getResizeHandleProps = (0, import_react22.useCallback)(
|
|
4491
|
+
(columnId, currentWidth) => {
|
|
4492
|
+
return {
|
|
4493
|
+
onMouseDown: (e) => {
|
|
4494
|
+
e.preventDefault();
|
|
4495
|
+
e.stopPropagation();
|
|
4496
|
+
startXRef.current = e.clientX;
|
|
4497
|
+
startWidthRef.current = columnWidths.get(columnId) ?? currentWidth;
|
|
4498
|
+
setResizingColumnId(columnId);
|
|
4499
|
+
document.body.style.cursor = "col-resize";
|
|
4500
|
+
document.body.style.userSelect = "none";
|
|
4501
|
+
let latestWidth = startWidthRef.current;
|
|
4502
|
+
const moveHandler = (ev) => {
|
|
4503
|
+
const diff = ev.clientX - startXRef.current;
|
|
4504
|
+
latestWidth = Math.max(minWidth, startWidthRef.current + diff);
|
|
4505
|
+
if (rafRef.current === null) {
|
|
4506
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
4507
|
+
rafRef.current = null;
|
|
4508
|
+
updateColumnDom(columnId, latestWidth);
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
};
|
|
4512
|
+
const upHandler = () => {
|
|
4513
|
+
document.removeEventListener("mousemove", moveHandler);
|
|
4514
|
+
document.removeEventListener("mouseup", upHandler);
|
|
4515
|
+
if (rafRef.current !== null) {
|
|
4516
|
+
cancelAnimationFrame(rafRef.current);
|
|
4517
|
+
rafRef.current = null;
|
|
4518
|
+
}
|
|
4519
|
+
setColumnWidths((prev) => {
|
|
4520
|
+
const next = new Map(prev);
|
|
4521
|
+
next.set(columnId, latestWidth);
|
|
4522
|
+
return next;
|
|
4523
|
+
});
|
|
4524
|
+
onColumnResize?.(columnId, latestWidth);
|
|
4525
|
+
setResizingColumnId(null);
|
|
4526
|
+
document.body.style.cursor = "";
|
|
4527
|
+
document.body.style.userSelect = "";
|
|
4528
|
+
};
|
|
4529
|
+
document.addEventListener("mousemove", moveHandler);
|
|
4530
|
+
document.addEventListener("mouseup", upHandler);
|
|
4531
|
+
},
|
|
4532
|
+
style: {
|
|
4533
|
+
cursor: "col-resize"
|
|
4534
|
+
},
|
|
4535
|
+
className: "absolute top-0 right-0 w-1 h-full hover:bg-blue-400 transition-colors z-10"
|
|
4536
|
+
};
|
|
4537
|
+
},
|
|
4538
|
+
[columnWidths, minWidth, onColumnResize, updateColumnDom]
|
|
4539
|
+
);
|
|
4540
|
+
return {
|
|
4541
|
+
columnWidths,
|
|
4542
|
+
getColumnWidth,
|
|
4543
|
+
getResizeHandleProps,
|
|
4544
|
+
isResizing: resizingColumnId !== null,
|
|
4545
|
+
resizingColumnId
|
|
4546
|
+
};
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
// src/components/SelectionSummaryBar.tsx
|
|
4550
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
4551
|
+
function SelectionSummaryBar({
|
|
4552
|
+
summary,
|
|
4553
|
+
focusedCell,
|
|
4554
|
+
columns,
|
|
4555
|
+
data,
|
|
4556
|
+
getRowId,
|
|
4557
|
+
currentPage,
|
|
4558
|
+
pageSize
|
|
4559
|
+
}) {
|
|
4560
|
+
let addressDisplay = null;
|
|
4561
|
+
let valueDisplay = null;
|
|
4562
|
+
if (focusedCell) {
|
|
4563
|
+
const column = columns.find((c) => c.id === focusedCell.columnId);
|
|
4564
|
+
const rowIndex = data.findIndex((r) => getRowId(r) === focusedCell.rowId);
|
|
4565
|
+
const row = rowIndex !== -1 ? data[rowIndex] : null;
|
|
4566
|
+
if (column && row) {
|
|
4567
|
+
const displayRowIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
4568
|
+
addressDisplay = `Row ${displayRowIndex} / ${column.label}`;
|
|
4569
|
+
const value = column.getValue ? column.getValue(row) : row[focusedCell.columnId];
|
|
4570
|
+
if (value !== null && value !== void 0 && value !== "") {
|
|
4571
|
+
valueDisplay = String(value);
|
|
4572
|
+
}
|
|
3651
4573
|
}
|
|
3652
|
-
}
|
|
3653
|
-
return
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
4574
|
+
}
|
|
4575
|
+
if (!addressDisplay && !summary) return null;
|
|
4576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
|
|
4577
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center gap-2 min-w-0", children: addressDisplay && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
4578
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-500 bg-white px-2 py-0.5 rounded border border-gray-200 shrink-0", children: addressDisplay }),
|
|
4579
|
+
valueDisplay && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-700 truncate", title: valueDisplay, children: valueDisplay })
|
|
4580
|
+
] }) }),
|
|
4581
|
+
summary && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-4 shrink-0", children: [
|
|
4582
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4583
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-400 mr-1", children: "Count:" }),
|
|
4584
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-700", children: summary.numericCount })
|
|
4585
|
+
] }),
|
|
4586
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4587
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-400 mr-1", children: "Sum:" }),
|
|
4588
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-700", children: summary.sum.toLocaleString() })
|
|
4589
|
+
] }),
|
|
4590
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4591
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-400 mr-1", children: "Avg:" }),
|
|
4592
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-700", children: summary.avg.toLocaleString() })
|
|
4593
|
+
] }),
|
|
4594
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4595
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-400 mr-1", children: "Min:" }),
|
|
4596
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-700", children: summary.min.toLocaleString() })
|
|
4597
|
+
] }),
|
|
4598
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4599
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-400 mr-1", children: "Max:" }),
|
|
4600
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium text-gray-700", children: summary.max.toLocaleString() })
|
|
4601
|
+
] })
|
|
4602
|
+
] })
|
|
4603
|
+
] });
|
|
3679
4604
|
}
|
|
4605
|
+
SelectionSummaryBar.displayName = "SelectionSummaryBar";
|
|
3680
4606
|
|
|
3681
4607
|
// src/components/Spreadsheet.tsx
|
|
3682
|
-
var
|
|
4608
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
3683
4609
|
function Spreadsheet({
|
|
3684
4610
|
data,
|
|
3685
4611
|
columns,
|
|
@@ -3725,9 +4651,11 @@ function Spreadsheet({
|
|
|
3725
4651
|
pageSize: controlledPageSize,
|
|
3726
4652
|
sortConfig: controlledSortConfig,
|
|
3727
4653
|
onPageChange,
|
|
3728
|
-
filters: controlledFilters
|
|
4654
|
+
filters: controlledFilters,
|
|
4655
|
+
duplicateCheckColumns: propDuplicateCheckColumns,
|
|
4656
|
+
onDuplicateCheckChange
|
|
3729
4657
|
}) {
|
|
3730
|
-
const [spreadsheetSettings, setSpreadsheetSettings] = (0,
|
|
4658
|
+
const [spreadsheetSettings, setSpreadsheetSettings] = (0, import_react23.useState)({
|
|
3731
4659
|
defaultPinnedColumns: initialSettings?.defaultPinnedColumns ?? [],
|
|
3732
4660
|
defaultSort: initialSettings?.defaultSort ?? null,
|
|
3733
4661
|
defaultPageSize: initialSettings?.defaultPageSize ?? 25,
|
|
@@ -3735,6 +4663,32 @@ function Spreadsheet({
|
|
|
3735
4663
|
autoSave: initialSettings?.autoSave ?? true,
|
|
3736
4664
|
compactView: initialSettings?.compactView ?? false
|
|
3737
4665
|
});
|
|
4666
|
+
const {
|
|
4667
|
+
isCellDuplicate,
|
|
4668
|
+
toggleDuplicateCheck,
|
|
4669
|
+
duplicateCheckColumns,
|
|
4670
|
+
duplicateRowIds,
|
|
4671
|
+
getDuplicateColumnCount
|
|
4672
|
+
} = useSpreadsheetDuplicates({
|
|
4673
|
+
data,
|
|
4674
|
+
columns,
|
|
4675
|
+
duplicateCheckColumns: propDuplicateCheckColumns ?? [],
|
|
4676
|
+
getRowId
|
|
4677
|
+
});
|
|
4678
|
+
const handleDuplicateCheckToggle = (0, import_react23.useCallback)(
|
|
4679
|
+
(columnId) => {
|
|
4680
|
+
setIsProcessing(true);
|
|
4681
|
+
(0, import_react23.startTransition)(() => {
|
|
4682
|
+
toggleDuplicateCheck(columnId);
|
|
4683
|
+
const currentCols = Array.from(duplicateCheckColumns);
|
|
4684
|
+
const next = currentCols.includes(columnId) ? currentCols.filter((id) => id !== columnId) : [...currentCols, columnId];
|
|
4685
|
+
onDuplicateCheckChange?.(next);
|
|
4686
|
+
setIsProcessing(false);
|
|
4687
|
+
});
|
|
4688
|
+
},
|
|
4689
|
+
[toggleDuplicateCheck, duplicateCheckColumns, onDuplicateCheckChange]
|
|
4690
|
+
);
|
|
4691
|
+
const [isProcessing, setIsProcessing] = (0, import_react23.useState)(false);
|
|
3738
4692
|
const {
|
|
3739
4693
|
filters,
|
|
3740
4694
|
sortConfig,
|
|
@@ -3754,7 +4708,9 @@ function Spreadsheet({
|
|
|
3754
4708
|
serverSide,
|
|
3755
4709
|
controlledFilters,
|
|
3756
4710
|
controlledSortConfig,
|
|
3757
|
-
defaultSortConfig: spreadsheetSettings.defaultSort
|
|
4711
|
+
defaultSortConfig: spreadsheetSettings.defaultSort,
|
|
4712
|
+
duplicateRowIds,
|
|
4713
|
+
getRowId
|
|
3758
4714
|
});
|
|
3759
4715
|
const {
|
|
3760
4716
|
getCellHighlight,
|
|
@@ -3768,7 +4724,8 @@ function Spreadsheet({
|
|
|
3768
4724
|
highlightPickerColumn,
|
|
3769
4725
|
setHighlightPickerColumn,
|
|
3770
4726
|
highlightPickerCell,
|
|
3771
|
-
setHighlightPickerCell
|
|
4727
|
+
setHighlightPickerCell,
|
|
4728
|
+
recentColors
|
|
3772
4729
|
} = useSpreadsheetHighlighting({
|
|
3773
4730
|
externalRowHighlights,
|
|
3774
4731
|
onRowHighlight,
|
|
@@ -3777,6 +4734,15 @@ function Spreadsheet({
|
|
|
3777
4734
|
externalCellHighlights,
|
|
3778
4735
|
onCellHighlight
|
|
3779
4736
|
});
|
|
4737
|
+
const { getColumnWidth, getResizeHandleProps, isResizing, columnWidths } = useSpreadsheetColumnResize({
|
|
4738
|
+
initialColumnWidths: initialSettings?.columnWidths,
|
|
4739
|
+
onColumnResize: (columnId, width) => {
|
|
4740
|
+
setSpreadsheetSettings((prev) => ({
|
|
4741
|
+
...prev,
|
|
4742
|
+
columnWidths: { ...prev.columnWidths, [columnId]: width }
|
|
4743
|
+
}));
|
|
4744
|
+
}
|
|
4745
|
+
});
|
|
3780
4746
|
const {
|
|
3781
4747
|
pinnedColumns,
|
|
3782
4748
|
isRowIndexPinned,
|
|
@@ -3788,12 +4754,15 @@ function Spreadsheet({
|
|
|
3788
4754
|
getColumnLeftOffset,
|
|
3789
4755
|
getColumnRightOffset,
|
|
3790
4756
|
isColumnPinned,
|
|
3791
|
-
getColumnPinSide
|
|
4757
|
+
getColumnPinSide,
|
|
4758
|
+
getPinnedZIndex,
|
|
4759
|
+
measureRef
|
|
3792
4760
|
} = useSpreadsheetPinning({
|
|
3793
4761
|
columns,
|
|
3794
4762
|
columnGroups,
|
|
3795
4763
|
defaultPinnedColumns: initialSettings?.defaultPinnedColumns,
|
|
3796
|
-
defaultPinnedRightColumns: initialSettings?.defaultPinnedRightColumns
|
|
4764
|
+
defaultPinnedRightColumns: initialSettings?.defaultPinnedRightColumns,
|
|
4765
|
+
getColumnWidth
|
|
3797
4766
|
});
|
|
3798
4767
|
const {
|
|
3799
4768
|
getCellComments,
|
|
@@ -3810,8 +4779,8 @@ function Spreadsheet({
|
|
|
3810
4779
|
onAddCellComment,
|
|
3811
4780
|
onToggleCommentResolved
|
|
3812
4781
|
});
|
|
3813
|
-
const [showSettingsModal, setShowSettingsModal] = (0,
|
|
3814
|
-
const [showFiltersPanel, setShowFiltersPanel] = (0,
|
|
4782
|
+
const [showSettingsModal, setShowSettingsModal] = (0, import_react23.useState)(false);
|
|
4783
|
+
const [showFiltersPanel, setShowFiltersPanel] = (0, import_react23.useState)(false);
|
|
3815
4784
|
const {
|
|
3816
4785
|
canUndo,
|
|
3817
4786
|
canRedo,
|
|
@@ -3829,15 +4798,15 @@ function Spreadsheet({
|
|
|
3829
4798
|
autoSave: spreadsheetSettings.autoSave,
|
|
3830
4799
|
onSave
|
|
3831
4800
|
});
|
|
3832
|
-
const [selectedRows, setSelectedRows] = (0,
|
|
3833
|
-
const [lastSelectedRow, setLastSelectedRow] = (0,
|
|
3834
|
-
const
|
|
3835
|
-
const [internalCurrentPage, setInternalCurrentPage] = (0,
|
|
3836
|
-
const [internalPageSize, setInternalPageSize] = (0,
|
|
3837
|
-
const [zoom, setZoom] = (0,
|
|
4801
|
+
const [selectedRows, setSelectedRows] = (0, import_react23.useState)(/* @__PURE__ */ new Set());
|
|
4802
|
+
const [lastSelectedRow, setLastSelectedRow] = (0, import_react23.useState)(null);
|
|
4803
|
+
const hoveredRowRef = (0, import_react23.useRef)(null);
|
|
4804
|
+
const [internalCurrentPage, setInternalCurrentPage] = (0, import_react23.useState)(1);
|
|
4805
|
+
const [internalPageSize, setInternalPageSize] = (0, import_react23.useState)(spreadsheetSettings.defaultPageSize);
|
|
4806
|
+
const [zoom, setZoom] = (0, import_react23.useState)(spreadsheetSettings.defaultZoom);
|
|
3838
4807
|
const currentPage = controlledCurrentPage ?? internalCurrentPage;
|
|
3839
4808
|
const pageSize = controlledPageSize ?? internalPageSize;
|
|
3840
|
-
const handlePageChange = (0,
|
|
4809
|
+
const handlePageChange = (0, import_react23.useCallback)(
|
|
3841
4810
|
(newPage) => {
|
|
3842
4811
|
if (controlledCurrentPage === void 0) {
|
|
3843
4812
|
setInternalCurrentPage(newPage);
|
|
@@ -3846,7 +4815,7 @@ function Spreadsheet({
|
|
|
3846
4815
|
},
|
|
3847
4816
|
[controlledCurrentPage, onPageChange, pageSize]
|
|
3848
4817
|
);
|
|
3849
|
-
const handlePageSizeChange = (0,
|
|
4818
|
+
const handlePageSizeChange = (0, import_react23.useCallback)(
|
|
3850
4819
|
(newPageSize) => {
|
|
3851
4820
|
if (controlledPageSize === void 0) {
|
|
3852
4821
|
setInternalPageSize(newPageSize);
|
|
@@ -3858,30 +4827,43 @@ function Spreadsheet({
|
|
|
3858
4827
|
},
|
|
3859
4828
|
[controlledPageSize, controlledCurrentPage, onPageChange]
|
|
3860
4829
|
);
|
|
3861
|
-
const resetPaginationToFirstPage = (0,
|
|
4830
|
+
const resetPaginationToFirstPage = (0, import_react23.useCallback)(() => {
|
|
3862
4831
|
if (controlledCurrentPage === void 0) {
|
|
3863
4832
|
setInternalCurrentPage(1);
|
|
3864
4833
|
}
|
|
3865
4834
|
onPageChange?.(1, pageSize);
|
|
3866
4835
|
}, [controlledCurrentPage, onPageChange, pageSize]);
|
|
3867
|
-
const handleFilterChangeWithReset = (0,
|
|
4836
|
+
const handleFilterChangeWithReset = (0, import_react23.useCallback)(
|
|
3868
4837
|
(columnId, filter) => {
|
|
3869
4838
|
handleFilterChange(columnId, filter);
|
|
3870
4839
|
resetPaginationToFirstPage();
|
|
3871
4840
|
},
|
|
3872
4841
|
[handleFilterChange, resetPaginationToFirstPage]
|
|
3873
4842
|
);
|
|
3874
|
-
const clearAllFiltersWithReset = (0,
|
|
4843
|
+
const clearAllFiltersWithReset = (0, import_react23.useCallback)(() => {
|
|
3875
4844
|
clearAllFilters();
|
|
3876
4845
|
resetPaginationToFirstPage();
|
|
3877
4846
|
}, [clearAllFilters, resetPaginationToFirstPage]);
|
|
3878
|
-
(0,
|
|
4847
|
+
(0, import_react23.useEffect)(() => {
|
|
3879
4848
|
setSpreadsheetSettings((prev) => ({
|
|
3880
4849
|
...prev,
|
|
3881
4850
|
defaultSort: sortConfig
|
|
3882
4851
|
}));
|
|
3883
4852
|
}, [sortConfig]);
|
|
3884
|
-
(0,
|
|
4853
|
+
const hasSyncedPinnedFromSettings = (0, import_react23.useRef)(false);
|
|
4854
|
+
(0, import_react23.useEffect)(() => {
|
|
4855
|
+
if (hasSyncedPinnedFromSettings.current) return;
|
|
4856
|
+
const settingsPinned = initialSettings?.defaultPinnedColumns;
|
|
4857
|
+
if (settingsPinned && settingsPinned.length > 0) {
|
|
4858
|
+
const currentIds = Array.from(pinnedColumns.keys());
|
|
4859
|
+
const hasAllSettingsPinned = settingsPinned.every((id) => pinnedColumns.has(id));
|
|
4860
|
+
if (!hasAllSettingsPinned) {
|
|
4861
|
+
setPinnedColumnsFromIds(settingsPinned);
|
|
4862
|
+
}
|
|
4863
|
+
hasSyncedPinnedFromSettings.current = true;
|
|
4864
|
+
}
|
|
4865
|
+
}, [initialSettings?.defaultPinnedColumns, pinnedColumns, setPinnedColumnsFromIds]);
|
|
4866
|
+
(0, import_react23.useEffect)(() => {
|
|
3885
4867
|
const pinnedColumnIds = Array.from(pinnedColumns.keys());
|
|
3886
4868
|
setSpreadsheetSettings((prev) => {
|
|
3887
4869
|
const prevIds = prev.defaultPinnedColumns;
|
|
@@ -3894,15 +4876,15 @@ function Spreadsheet({
|
|
|
3894
4876
|
};
|
|
3895
4877
|
});
|
|
3896
4878
|
}, [pinnedColumns]);
|
|
3897
|
-
const isInitialMount = (0,
|
|
3898
|
-
(0,
|
|
4879
|
+
const isInitialMount = (0, import_react23.useRef)(true);
|
|
4880
|
+
(0, import_react23.useEffect)(() => {
|
|
3899
4881
|
if (isInitialMount.current) {
|
|
3900
4882
|
isInitialMount.current = false;
|
|
3901
4883
|
return;
|
|
3902
4884
|
}
|
|
3903
4885
|
onSettingsChange?.(spreadsheetSettings);
|
|
3904
4886
|
}, [spreadsheetSettings, onSettingsChange]);
|
|
3905
|
-
const applyUndo = (0,
|
|
4887
|
+
const applyUndo = (0, import_react23.useCallback)(() => {
|
|
3906
4888
|
const entry = popUndoEntry();
|
|
3907
4889
|
if (!entry || !onCellsEdit) return;
|
|
3908
4890
|
if (entry.type === "cell-edit") {
|
|
@@ -3920,7 +4902,7 @@ function Spreadsheet({
|
|
|
3920
4902
|
}
|
|
3921
4903
|
markAsChanged();
|
|
3922
4904
|
}, [popUndoEntry, onCellsEdit, markAsChanged]);
|
|
3923
|
-
const applyRedo = (0,
|
|
4905
|
+
const applyRedo = (0, import_react23.useCallback)(() => {
|
|
3924
4906
|
const entry = popRedoEntry();
|
|
3925
4907
|
if (!entry || !onCellsEdit) return;
|
|
3926
4908
|
if (entry.type === "cell-edit") {
|
|
@@ -3936,7 +4918,7 @@ function Spreadsheet({
|
|
|
3936
4918
|
}
|
|
3937
4919
|
markAsChanged();
|
|
3938
4920
|
}, [popRedoEntry, onCellsEdit, markAsChanged]);
|
|
3939
|
-
const paginatedData = (0,
|
|
4921
|
+
const paginatedData = (0, import_react23.useMemo)(() => {
|
|
3940
4922
|
if (serverSide) {
|
|
3941
4923
|
return filteredData;
|
|
3942
4924
|
}
|
|
@@ -3945,9 +4927,14 @@ function Spreadsheet({
|
|
|
3945
4927
|
}, [filteredData, currentPage, pageSize, serverSide]);
|
|
3946
4928
|
const {
|
|
3947
4929
|
focusedCell,
|
|
4930
|
+
setFocusedCell,
|
|
3948
4931
|
editingCell,
|
|
3949
4932
|
setEditingCell,
|
|
4933
|
+
selectedCellRange,
|
|
4934
|
+
setSelectedCellRange,
|
|
3950
4935
|
handleCellMouseDown,
|
|
4936
|
+
handleCellMouseEnter,
|
|
4937
|
+
handleMouseUp,
|
|
3951
4938
|
isCellInSelection,
|
|
3952
4939
|
getCellSelectionEdge,
|
|
3953
4940
|
clearSelection,
|
|
@@ -3955,14 +4942,20 @@ function Spreadsheet({
|
|
|
3955
4942
|
handleTabNavigation,
|
|
3956
4943
|
enterEditMode,
|
|
3957
4944
|
copySelectedCells,
|
|
3958
|
-
pasteFromSystemClipboard
|
|
4945
|
+
pasteFromSystemClipboard,
|
|
4946
|
+
getSelectedCellValues
|
|
3959
4947
|
} = useSpreadsheetSelection({
|
|
3960
4948
|
data: paginatedData,
|
|
3961
4949
|
columns: visibleColumns,
|
|
3962
4950
|
getRowId,
|
|
3963
4951
|
enableCellEditing
|
|
3964
4952
|
});
|
|
3965
|
-
const
|
|
4953
|
+
const selectedCellValues = (0, import_react23.useMemo)(() => getSelectedCellValues(), [getSelectedCellValues]);
|
|
4954
|
+
const { summary: selectionSummary, hasNumericValues } = useSpreadsheetSummary({
|
|
4955
|
+
selectedCellValues,
|
|
4956
|
+
columns: visibleColumns
|
|
4957
|
+
});
|
|
4958
|
+
const handleEscapeCallback = (0, import_react23.useCallback)(() => {
|
|
3966
4959
|
if (commentModalCell !== null) {
|
|
3967
4960
|
setCommentModalCell(null);
|
|
3968
4961
|
} else if (viewCommentsCell !== null) {
|
|
@@ -3991,7 +4984,7 @@ function Spreadsheet({
|
|
|
3991
4984
|
setHighlightPickerCell,
|
|
3992
4985
|
clearSelection
|
|
3993
4986
|
]);
|
|
3994
|
-
const handleNavigate = (0,
|
|
4987
|
+
const handleNavigate = (0, import_react23.useCallback)(
|
|
3995
4988
|
(direction, event) => {
|
|
3996
4989
|
const extendSelection = event?.shiftKey ?? false;
|
|
3997
4990
|
navigateCell(direction, extendSelection);
|
|
@@ -4012,10 +5005,10 @@ function Spreadsheet({
|
|
|
4012
5005
|
},
|
|
4013
5006
|
[navigateCell, focusedCell]
|
|
4014
5007
|
);
|
|
4015
|
-
const handleEnterEditMode = (0,
|
|
5008
|
+
const handleEnterEditMode = (0, import_react23.useCallback)(() => {
|
|
4016
5009
|
enterEditMode();
|
|
4017
5010
|
}, [enterEditMode]);
|
|
4018
|
-
const handlePaste = (0,
|
|
5011
|
+
const handlePaste = (0, import_react23.useCallback)(async () => {
|
|
4019
5012
|
const edits = await pasteFromSystemClipboard();
|
|
4020
5013
|
if (edits.length > 0 && onCellsEdit) {
|
|
4021
5014
|
if (enableUndoRedo) {
|
|
@@ -4055,26 +5048,39 @@ function Spreadsheet({
|
|
|
4055
5048
|
onTabNavigation: handleTabNavigation,
|
|
4056
5049
|
onCopy: copySelectedCells,
|
|
4057
5050
|
onPaste: handlePaste,
|
|
5051
|
+
onSelectAll: () => {
|
|
5052
|
+
if (paginatedData.length > 0 && visibleColumns.length > 0) {
|
|
5053
|
+
const firstRow = paginatedData[0];
|
|
5054
|
+
const lastRow = paginatedData[paginatedData.length - 1];
|
|
5055
|
+
const firstCol = visibleColumns[0];
|
|
5056
|
+
const lastCol = visibleColumns[visibleColumns.length - 1];
|
|
5057
|
+
setSelectedCellRange({
|
|
5058
|
+
start: { rowId: getRowId(firstRow), columnId: firstCol.id },
|
|
5059
|
+
end: { rowId: getRowId(lastRow), columnId: lastCol.id }
|
|
5060
|
+
});
|
|
5061
|
+
setFocusedCell({ rowId: getRowId(firstRow), columnId: firstCol.id });
|
|
5062
|
+
}
|
|
5063
|
+
},
|
|
4058
5064
|
hasFocusedCell: focusedCell !== null,
|
|
4059
5065
|
isEditing: editingCell !== null,
|
|
4060
5066
|
enabled: true
|
|
4061
5067
|
});
|
|
4062
5068
|
const effectiveCompactMode = spreadsheetSettings.compactView ?? false;
|
|
4063
5069
|
const rowIndexHighlightColor = getColumnHighlight(ROW_INDEX_COLUMN_ID);
|
|
4064
|
-
const tableRef = (0,
|
|
5070
|
+
const tableRef = (0, import_react23.useRef)(null);
|
|
4065
5071
|
const effectiveTotalItems = serverSide ? totalItems ?? data.length : filteredData.length;
|
|
4066
5072
|
const totalPages = Math.max(1, Math.ceil(effectiveTotalItems / pageSize));
|
|
4067
|
-
(0,
|
|
5073
|
+
(0, import_react23.useEffect)(() => {
|
|
4068
5074
|
if (!serverSide && currentPage > totalPages) {
|
|
4069
5075
|
setInternalCurrentPage(1);
|
|
4070
5076
|
}
|
|
4071
5077
|
}, [totalPages, currentPage, serverSide]);
|
|
4072
|
-
const afterFilteredRef = (0,
|
|
5078
|
+
const afterFilteredRef = (0, import_react23.useRef)(afterFiltered);
|
|
4073
5079
|
afterFilteredRef.current = afterFiltered;
|
|
4074
|
-
(0,
|
|
5080
|
+
(0, import_react23.useEffect)(() => {
|
|
4075
5081
|
afterFilteredRef.current?.(filteredData.toArray());
|
|
4076
5082
|
}, [filteredData]);
|
|
4077
|
-
const handleRowSelect = (0,
|
|
5083
|
+
const handleRowSelect = (0, import_react23.useCallback)(
|
|
4078
5084
|
(rowId, event) => {
|
|
4079
5085
|
if (!enableRowSelection) return;
|
|
4080
5086
|
event.stopPropagation();
|
|
@@ -4123,14 +5129,23 @@ function Spreadsheet({
|
|
|
4123
5129
|
onSelectionChange
|
|
4124
5130
|
]
|
|
4125
5131
|
);
|
|
4126
|
-
const handleCellClick = (0,
|
|
5132
|
+
const handleCellClick = (0, import_react23.useCallback)(
|
|
4127
5133
|
(rowId, columnId, event) => {
|
|
4128
5134
|
event.stopPropagation();
|
|
4129
5135
|
handleCellMouseDown(rowId, columnId, event);
|
|
4130
5136
|
},
|
|
4131
5137
|
[handleCellMouseDown]
|
|
4132
5138
|
);
|
|
4133
|
-
const
|
|
5139
|
+
const handleCellDoubleClick = (0, import_react23.useCallback)(
|
|
5140
|
+
(rowId, columnId) => {
|
|
5141
|
+
const column = columns.find((c) => c.id === columnId);
|
|
5142
|
+
if (column?.editable && enableCellEditing) {
|
|
5143
|
+
setEditingCell({ rowId, columnId });
|
|
5144
|
+
}
|
|
5145
|
+
},
|
|
5146
|
+
[columns, enableCellEditing, setEditingCell]
|
|
5147
|
+
);
|
|
5148
|
+
const handleCellChange = (0, import_react23.useCallback)(
|
|
4134
5149
|
(rowId, columnId, newValue) => {
|
|
4135
5150
|
const row = data.find((r) => getRowId(r) === rowId);
|
|
4136
5151
|
const previousValue = row ? row[columnId] : void 0;
|
|
@@ -4153,7 +5168,7 @@ function Spreadsheet({
|
|
|
4153
5168
|
},
|
|
4154
5169
|
[data, getRowId, enableUndoRedo, onCellsEdit, pushToUndoStack, markAsChanged]
|
|
4155
5170
|
);
|
|
4156
|
-
const handleConfirmEdit = (0,
|
|
5171
|
+
const handleConfirmEdit = (0, import_react23.useCallback)(
|
|
4157
5172
|
(finalValue) => {
|
|
4158
5173
|
if (editingCell && finalValue !== void 0) {
|
|
4159
5174
|
handleCellChange(editingCell.rowId, editingCell.columnId, finalValue);
|
|
@@ -4162,105 +5177,51 @@ function Spreadsheet({
|
|
|
4162
5177
|
},
|
|
4163
5178
|
[editingCell, handleCellChange, setEditingCell]
|
|
4164
5179
|
);
|
|
4165
|
-
const handleCancelEdit = (0,
|
|
5180
|
+
const handleCancelEdit = (0, import_react23.useCallback)(() => {
|
|
4166
5181
|
setEditingCell(null);
|
|
4167
5182
|
}, [setEditingCell]);
|
|
4168
|
-
const handleRowIndexHighlightClick = (0,
|
|
5183
|
+
const handleRowIndexHighlightClick = (0, import_react23.useCallback)(() => {
|
|
4169
5184
|
setHighlightPickerColumn(ROW_INDEX_COLUMN_ID);
|
|
4170
5185
|
}, [setHighlightPickerColumn]);
|
|
4171
|
-
const columnRenderItems = (0,
|
|
5186
|
+
const columnRenderItems = (0, import_react23.useMemo)(() => {
|
|
4172
5187
|
if (!columnGroups || columnGroups.length === 0) {
|
|
4173
5188
|
return visibleColumns.map((col) => ({
|
|
4174
5189
|
type: "column",
|
|
4175
5190
|
column: col
|
|
4176
5191
|
}));
|
|
4177
5192
|
}
|
|
4178
|
-
const
|
|
4179
|
-
const
|
|
4180
|
-
const
|
|
5193
|
+
const items = [];
|
|
5194
|
+
const allGroupedIds = new Set(columnGroups.flatMap((g) => g.columns));
|
|
5195
|
+
for (const col of visibleColumns) {
|
|
5196
|
+
if (!allGroupedIds.has(col.id)) {
|
|
5197
|
+
items.push({ type: "column", column: col });
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
4181
5200
|
for (const group of columnGroups) {
|
|
4182
5201
|
const isCollapsed = collapsedGroups.has(group.id);
|
|
5202
|
+
const groupVisibleCols = visibleColumns.filter((c) => group.columns.includes(c.id));
|
|
4183
5203
|
if (isCollapsed) {
|
|
4184
|
-
|
|
5204
|
+
items.push({
|
|
4185
5205
|
type: "collapsed-placeholder",
|
|
4186
5206
|
groupId: group.id,
|
|
4187
5207
|
headerColor: group.headerColor
|
|
4188
5208
|
});
|
|
4189
5209
|
}
|
|
4190
|
-
const groupVisibleCols = (columns || []).filter((c) => {
|
|
4191
|
-
if (!group.columns.includes(c.id)) return false;
|
|
4192
|
-
if (isCollapsed) return pinnedColumns.has(c.id);
|
|
4193
|
-
return true;
|
|
4194
|
-
});
|
|
4195
5210
|
for (const col of groupVisibleCols) {
|
|
4196
|
-
|
|
4197
|
-
if (pinSide === "left") {
|
|
4198
|
-
leftPinnedItems.push({ type: "column", column: col });
|
|
4199
|
-
} else if (pinSide === "right") {
|
|
4200
|
-
rightPinnedItems.push({ type: "column", column: col });
|
|
4201
|
-
} else {
|
|
4202
|
-
middleItems.push({ type: "column", column: col });
|
|
4203
|
-
}
|
|
4204
|
-
}
|
|
4205
|
-
}
|
|
4206
|
-
const allGroupedIds = new Set(columnGroups.flatMap((g) => g.columns));
|
|
4207
|
-
for (const col of visibleColumns) {
|
|
4208
|
-
if (!allGroupedIds.has(col.id)) {
|
|
4209
|
-
const pinSide = pinnedColumns.get(col.id);
|
|
4210
|
-
if (pinSide === "left") {
|
|
4211
|
-
leftPinnedItems.push({ type: "column", column: col });
|
|
4212
|
-
} else if (pinSide === "right") {
|
|
4213
|
-
rightPinnedItems.push({ type: "column", column: col });
|
|
4214
|
-
} else {
|
|
4215
|
-
middleItems.push({ type: "column", column: col });
|
|
4216
|
-
}
|
|
5211
|
+
items.push({ type: "column", column: col });
|
|
4217
5212
|
}
|
|
4218
5213
|
}
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
(a, b) => pinnedLeftOrder.indexOf(a.column.id) - pinnedLeftOrder.indexOf(b.column.id)
|
|
4223
|
-
);
|
|
4224
|
-
rightPinnedItems.sort(
|
|
4225
|
-
(a, b) => pinnedRightOrder.indexOf(a.column.id) - pinnedRightOrder.indexOf(b.column.id)
|
|
4226
|
-
);
|
|
4227
|
-
return [...leftPinnedItems, ...middleItems, ...rightPinnedItems];
|
|
4228
|
-
}, [columnGroups, collapsedGroups, columns, pinnedColumns, visibleColumns]);
|
|
4229
|
-
const groupHeaderItems = (0, import_react17.useMemo)(() => {
|
|
5214
|
+
return items;
|
|
5215
|
+
}, [columnGroups, collapsedGroups, visibleColumns]);
|
|
5216
|
+
const groupHeaderItems = (0, import_react23.useMemo)(() => {
|
|
4230
5217
|
if (!columnGroups || columnGroups.length === 0) return null;
|
|
4231
|
-
const
|
|
4232
|
-
const groups = [];
|
|
4233
|
-
const rightPinned = [];
|
|
5218
|
+
const items = [];
|
|
4234
5219
|
for (const group of columnGroups) {
|
|
4235
5220
|
const isCollapsed = collapsedGroups.has(group.id);
|
|
4236
|
-
const
|
|
4237
|
-
const
|
|
4238
|
-
let movedLeftCount = 0;
|
|
4239
|
-
let movedRightCount = 0;
|
|
4240
|
-
for (const col of visibleGroupColumns) {
|
|
4241
|
-
const pinSide = pinnedColumns.get(col.id);
|
|
4242
|
-
if (pinSide === "left") {
|
|
4243
|
-
movedLeftCount++;
|
|
4244
|
-
leftPinned.push({
|
|
4245
|
-
type: "pinned-column",
|
|
4246
|
-
columnId: col.id,
|
|
4247
|
-
headerColor: group.headerColor,
|
|
4248
|
-
pinSide: "left"
|
|
4249
|
-
});
|
|
4250
|
-
} else if (pinSide === "right") {
|
|
4251
|
-
movedRightCount++;
|
|
4252
|
-
rightPinned.push({
|
|
4253
|
-
type: "pinned-column",
|
|
4254
|
-
columnId: col.id,
|
|
4255
|
-
headerColor: group.headerColor,
|
|
4256
|
-
pinSide: "right"
|
|
4257
|
-
});
|
|
4258
|
-
}
|
|
4259
|
-
}
|
|
4260
|
-
const remainingCols = visibleGroupColumns.length - movedLeftCount - movedRightCount;
|
|
4261
|
-
const colSpan = remainingCols + (isCollapsed ? 1 : 0);
|
|
5221
|
+
const visibleCount = visibleColumns.filter((c) => group.columns.includes(c.id)).length;
|
|
5222
|
+
const colSpan = visibleCount + (isCollapsed ? 1 : 0);
|
|
4262
5223
|
if (colSpan > 0) {
|
|
4263
|
-
|
|
5224
|
+
items.push({
|
|
4264
5225
|
type: "group",
|
|
4265
5226
|
group,
|
|
4266
5227
|
colSpan,
|
|
@@ -4268,18 +5229,10 @@ function Spreadsheet({
|
|
|
4268
5229
|
});
|
|
4269
5230
|
}
|
|
4270
5231
|
}
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
);
|
|
4276
|
-
rightPinned.sort(
|
|
4277
|
-
(a, b) => pinnedRightOrder.indexOf(a.columnId) - pinnedRightOrder.indexOf(b.columnId)
|
|
4278
|
-
);
|
|
4279
|
-
return [...leftPinned, ...groups, ...rightPinned];
|
|
4280
|
-
}, [columnGroups, collapsedGroups, columns, pinnedColumns]);
|
|
4281
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex flex-col h-full bg-white", className), children: [
|
|
4282
|
-
showToolbar && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5232
|
+
return items;
|
|
5233
|
+
}, [columnGroups, collapsedGroups, visibleColumns]);
|
|
5234
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("flex flex-col h-full bg-white", className), children: [
|
|
5235
|
+
showToolbar && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4283
5236
|
SpreadsheetToolbar,
|
|
4284
5237
|
{
|
|
4285
5238
|
zoom,
|
|
@@ -4314,385 +5267,405 @@ function Spreadsheet({
|
|
|
4314
5267
|
menuItems: toolbarMenuItems
|
|
4315
5268
|
}
|
|
4316
5269
|
),
|
|
4317
|
-
/* @__PURE__ */ (0,
|
|
4318
|
-
"div",
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
/* @__PURE__ */ (0,
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
5270
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { ref: tableRef, className: cn("flex-1 overflow-auto border border-gray-200 rounded spreadsheet-scroll-container relative", isResizing && "select-none"), onMouseUp: handleMouseUp, children: [
|
|
5271
|
+
isProcessing && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "spreadsheet-processing-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2 text-gray-500", children: [
|
|
5272
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }),
|
|
5273
|
+
"Processing..."
|
|
5274
|
+
] }) }),
|
|
5275
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("table", { ref: measureRef, className: "border-separate border-spacing-0 text-sm select-none", style: zoom !== 100 ? { zoom: zoom / 100 } : void 0, children: [
|
|
5276
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("thead", { className: "sticky top-0", style: { zIndex: 50 }, children: [
|
|
5277
|
+
columnGroups && groupHeaderItems && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("tr", { children: [
|
|
5278
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5279
|
+
RowIndexColumnHeader,
|
|
5280
|
+
{
|
|
5281
|
+
highlightColor: rowIndexHighlightColor,
|
|
5282
|
+
isPinned: isRowIndexPinned,
|
|
5283
|
+
isSecondRow: true,
|
|
5284
|
+
compactMode: effectiveCompactMode
|
|
5285
|
+
}
|
|
5286
|
+
),
|
|
5287
|
+
groupHeaderItems.map((item) => {
|
|
5288
|
+
const { group, colSpan, isCollapsed } = item;
|
|
5289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5290
|
+
"th",
|
|
4328
5291
|
{
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
5292
|
+
colSpan,
|
|
5293
|
+
className: cn(
|
|
5294
|
+
"border border-gray-200 px-2 py-1.5 text-center font-bold text-gray-700",
|
|
5295
|
+
group.collapsible && "cursor-pointer hover:bg-gray-100"
|
|
5296
|
+
),
|
|
5297
|
+
style: {
|
|
5298
|
+
backgroundColor: group.headerColor || "rgb(243 244 246)"
|
|
5299
|
+
},
|
|
5300
|
+
onClick: () => group.collapsible && handleToggleGroupCollapse(group.id),
|
|
5301
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
5302
|
+
group.collapsible && (isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HiChevronRight, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HiChevronDown, { className: "h-3 w-3" })),
|
|
5303
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: group.label })
|
|
5304
|
+
] })
|
|
5305
|
+
},
|
|
5306
|
+
group.id
|
|
5307
|
+
);
|
|
5308
|
+
})
|
|
5309
|
+
] }),
|
|
5310
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("tr", { children: [
|
|
5311
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5312
|
+
RowIndexColumnHeader,
|
|
5313
|
+
{
|
|
5314
|
+
enableHighlighting,
|
|
5315
|
+
highlightColor: rowIndexHighlightColor,
|
|
5316
|
+
isPinned: isRowIndexPinned,
|
|
5317
|
+
onHighlightClick: handleRowIndexHighlightClick,
|
|
5318
|
+
onPinClick: () => handleTogglePin(ROW_INDEX_COLUMN_ID),
|
|
5319
|
+
compactMode: effectiveCompactMode
|
|
5320
|
+
}
|
|
5321
|
+
),
|
|
5322
|
+
columnRenderItems.map((item) => {
|
|
5323
|
+
if (item.type === "collapsed-placeholder") {
|
|
5324
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4363
5325
|
"th",
|
|
4364
5326
|
{
|
|
4365
|
-
|
|
4366
|
-
className: cn(
|
|
4367
|
-
"border border-gray-200 px-2 py-1.5 text-center font-bold text-gray-700",
|
|
4368
|
-
group.collapsible && "cursor-pointer hover:bg-gray-100"
|
|
4369
|
-
),
|
|
5327
|
+
className: "border border-gray-200 px-2 py-1 text-center text-gray-400",
|
|
4370
5328
|
style: {
|
|
4371
|
-
backgroundColor:
|
|
5329
|
+
backgroundColor: item.headerColor || "rgb(243 244 246)",
|
|
5330
|
+
minWidth: "30px"
|
|
4372
5331
|
},
|
|
4373
|
-
|
|
4374
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
4375
|
-
group.collapsible && (isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HiChevronRight, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HiChevronDown, { className: "h-3 w-3" })),
|
|
4376
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: group.label })
|
|
4377
|
-
] })
|
|
5332
|
+
children: "..."
|
|
4378
5333
|
},
|
|
4379
|
-
|
|
5334
|
+
`${item.groupId}-placeholder`
|
|
4380
5335
|
);
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
5336
|
+
}
|
|
5337
|
+
const column = item.column;
|
|
5338
|
+
const isPinnedLeft = isColumnPinned(column.id) && getColumnPinSide(column.id) === "left";
|
|
5339
|
+
const isPinnedRight = isColumnPinned(column.id) && getColumnPinSide(column.id) === "right";
|
|
5340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5341
|
+
MemoizedSpreadsheetHeader,
|
|
4386
5342
|
{
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
const isPinnedLeft = isColumnPinned(column.id) && getColumnPinSide(column.id) === "left";
|
|
4412
|
-
const isPinnedRight = isColumnPinned(column.id) && getColumnPinSide(column.id) === "right";
|
|
4413
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4414
|
-
SpreadsheetHeader,
|
|
4415
|
-
{
|
|
4416
|
-
column,
|
|
4417
|
-
sortConfig,
|
|
4418
|
-
hasActiveFilter: !!filters[column.id],
|
|
4419
|
-
isPinned: isColumnPinned(column.id),
|
|
4420
|
-
pinSide: getColumnPinSide(column.id),
|
|
4421
|
-
leftOffset: isPinnedLeft ? getColumnLeftOffset(column.id) : 0,
|
|
4422
|
-
rightOffset: isPinnedRight ? getColumnRightOffset(column.id) : 0,
|
|
4423
|
-
highlightColor: getColumnHighlight(column.id),
|
|
4424
|
-
compactMode: effectiveCompactMode,
|
|
4425
|
-
onClick: () => handleSort(column.id),
|
|
4426
|
-
onFilterClick: () => setActiveFilterColumn(
|
|
4427
|
-
activeFilterColumn === column.id ? null : column.id
|
|
4428
|
-
),
|
|
4429
|
-
onPinClick: () => handleTogglePin(column.id),
|
|
4430
|
-
onHighlightClick: enableHighlighting ? () => setHighlightPickerColumn(column.id) : void 0,
|
|
4431
|
-
children: activeFilterColumn === column.id && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4432
|
-
SpreadsheetFilterDropdown,
|
|
4433
|
-
{
|
|
4434
|
-
column,
|
|
4435
|
-
filter: filters[column.id],
|
|
4436
|
-
onFilterChange: (filter) => handleFilterChangeWithReset(
|
|
4437
|
-
column.id,
|
|
4438
|
-
filter
|
|
4439
|
-
),
|
|
4440
|
-
onClose: () => setActiveFilterColumn(null)
|
|
5343
|
+
column,
|
|
5344
|
+
sortConfig,
|
|
5345
|
+
hasActiveFilter: !!filters[column.id],
|
|
5346
|
+
isPinned: isColumnPinned(column.id),
|
|
5347
|
+
pinSide: getColumnPinSide(column.id),
|
|
5348
|
+
pinnedZIndex: isColumnPinned(column.id) ? getPinnedZIndex(column.id) : void 0,
|
|
5349
|
+
leftOffset: isPinnedLeft ? getColumnLeftOffset(column.id) : 0,
|
|
5350
|
+
rightOffset: isPinnedRight ? getColumnRightOffset(column.id) : 0,
|
|
5351
|
+
highlightColor: getColumnHighlight(column.id),
|
|
5352
|
+
compactMode: effectiveCompactMode,
|
|
5353
|
+
onClick: () => {
|
|
5354
|
+
if (paginatedData.length > 0) {
|
|
5355
|
+
const firstRowId = getRowId(paginatedData[0]);
|
|
5356
|
+
const lastRowId = getRowId(paginatedData[paginatedData.length - 1]);
|
|
5357
|
+
const isAlreadySelected = selectedCellRange?.start.columnId === column.id && selectedCellRange?.end.columnId === column.id && selectedCellRange?.start.rowId === firstRowId && selectedCellRange?.end.rowId === lastRowId;
|
|
5358
|
+
if (isAlreadySelected) {
|
|
5359
|
+
setSelectedCellRange(null);
|
|
5360
|
+
setFocusedCell(null);
|
|
5361
|
+
} else {
|
|
5362
|
+
setSelectedCellRange({
|
|
5363
|
+
start: { rowId: firstRowId, columnId: column.id },
|
|
5364
|
+
end: { rowId: lastRowId, columnId: column.id }
|
|
5365
|
+
});
|
|
5366
|
+
setFocusedCell({ rowId: firstRowId, columnId: column.id });
|
|
4441
5367
|
}
|
|
4442
|
-
|
|
5368
|
+
}
|
|
4443
5369
|
},
|
|
4444
|
-
column.id
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
"td",
|
|
4461
|
-
{
|
|
4462
|
-
colSpan: columnRenderItems.length + 1,
|
|
4463
|
-
className: "text-center py-8 text-gray-500",
|
|
4464
|
-
children: emptyMessage
|
|
4465
|
-
}
|
|
4466
|
-
) }) : paginatedData.map((row, rowIndex) => {
|
|
4467
|
-
const rowId = getRowId(row);
|
|
4468
|
-
const isRowSelected = selectedRows.has(rowId);
|
|
4469
|
-
const isRowHovered = hoveredRow === rowId;
|
|
4470
|
-
const rowHighlight = getRowHighlight(rowId);
|
|
4471
|
-
const displayIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
4472
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
4473
|
-
"tr",
|
|
4474
|
-
{
|
|
4475
|
-
onMouseEnter: () => setHoveredRow(rowId),
|
|
4476
|
-
onMouseLeave: () => setHoveredRow(null),
|
|
4477
|
-
onClick: () => {
|
|
4478
|
-
onRowClick?.(row, rowIndex);
|
|
4479
|
-
},
|
|
4480
|
-
onDoubleClick: () => onRowDoubleClick?.(row, rowIndex),
|
|
4481
|
-
children: [
|
|
4482
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4483
|
-
"td",
|
|
5370
|
+
onSortClick: () => handleSort(column.id),
|
|
5371
|
+
onFilterClick: () => setActiveFilterColumn(
|
|
5372
|
+
activeFilterColumn === column.id ? null : column.id
|
|
5373
|
+
),
|
|
5374
|
+
onPinClick: () => handleTogglePin(column.id),
|
|
5375
|
+
onHighlightClick: enableHighlighting ? () => setHighlightPickerColumn(column.id) : void 0,
|
|
5376
|
+
resizeHandleProps: getResizeHandleProps(
|
|
5377
|
+
column.id,
|
|
5378
|
+
column.width || column.minWidth || 100
|
|
5379
|
+
),
|
|
5380
|
+
resolvedWidth: getColumnWidth(column.id),
|
|
5381
|
+
hasDuplicateCheck: duplicateCheckColumns.has(column.id),
|
|
5382
|
+
onDuplicateCheckClick: () => handleDuplicateCheckToggle(column.id),
|
|
5383
|
+
duplicateCount: getDuplicateColumnCount(column.id),
|
|
5384
|
+
children: activeFilterColumn === column.id && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5385
|
+
SpreadsheetFilterDropdown,
|
|
4484
5386
|
{
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
isRowSelected && "bg-blue-100",
|
|
4491
|
-
!isRowSelected && rowHighlight && "",
|
|
4492
|
-
isRowHovered && !isRowSelected && !rowHighlight && "bg-gray-50"
|
|
5387
|
+
column,
|
|
5388
|
+
filter: filters[column.id],
|
|
5389
|
+
onFilterChange: (filter) => handleFilterChangeWithReset(
|
|
5390
|
+
column.id,
|
|
5391
|
+
filter
|
|
4493
5392
|
),
|
|
4494
|
-
|
|
4495
|
-
|
|
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
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
5393
|
+
onClose: () => setActiveFilterColumn(null),
|
|
5394
|
+
hasDuplicateCheck: duplicateCheckColumns.has(column.id)
|
|
5395
|
+
}
|
|
5396
|
+
)
|
|
5397
|
+
},
|
|
5398
|
+
column.id
|
|
5399
|
+
);
|
|
5400
|
+
})
|
|
5401
|
+
] })
|
|
5402
|
+
] }),
|
|
5403
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5404
|
+
"td",
|
|
5405
|
+
{
|
|
5406
|
+
colSpan: columnRenderItems.length + 1,
|
|
5407
|
+
className: "text-center py-8 text-gray-500",
|
|
5408
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [
|
|
5409
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }),
|
|
5410
|
+
"Loading..."
|
|
5411
|
+
] })
|
|
5412
|
+
}
|
|
5413
|
+
) }) : paginatedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5414
|
+
"td",
|
|
5415
|
+
{
|
|
5416
|
+
colSpan: columnRenderItems.length + 1,
|
|
5417
|
+
className: "text-center py-8 text-gray-500",
|
|
5418
|
+
children: emptyMessage
|
|
5419
|
+
}
|
|
5420
|
+
) }) : paginatedData.map((row, rowIndex) => {
|
|
5421
|
+
const rowId = getRowId(row);
|
|
5422
|
+
const isRowSelected = selectedRows.has(rowId);
|
|
5423
|
+
const rowHighlight = getRowHighlight(rowId);
|
|
5424
|
+
const displayIndex = rowIndex + 1 + (currentPage - 1) * pageSize;
|
|
5425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
5426
|
+
"tr",
|
|
5427
|
+
{
|
|
5428
|
+
onMouseEnter: () => {
|
|
5429
|
+
hoveredRowRef.current = rowId;
|
|
5430
|
+
},
|
|
5431
|
+
onMouseLeave: () => {
|
|
5432
|
+
hoveredRowRef.current = null;
|
|
5433
|
+
},
|
|
5434
|
+
onClick: () => {
|
|
5435
|
+
onRowClick?.(row, rowIndex);
|
|
5436
|
+
},
|
|
5437
|
+
onDoubleClick: () => onRowDoubleClick?.(row, rowIndex),
|
|
5438
|
+
children: [
|
|
5439
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5440
|
+
"td",
|
|
5441
|
+
{
|
|
5442
|
+
"data-column-id": "__row_index__",
|
|
5443
|
+
onClick: (e) => handleRowSelect(rowId, e),
|
|
5444
|
+
className: cn(
|
|
5445
|
+
"border border-gray-200 text-center font-semibold cursor-pointer group",
|
|
5446
|
+
effectiveCompactMode ? "text-xs px-1.5 py-0.5" : "text-sm px-2.5 py-1.5",
|
|
5447
|
+
"sticky",
|
|
5448
|
+
isRowSelected && "bg-blue-100",
|
|
5449
|
+
!isRowSelected && rowHighlight && ""
|
|
5450
|
+
),
|
|
5451
|
+
style: {
|
|
5452
|
+
backgroundColor: rowHighlight?.color || (isRowSelected ? "#dbeafe" : rowIndexHighlightColor || (rowIndex % 2 !== 0 ? "#f9fafb" : "white")),
|
|
5453
|
+
minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
5454
|
+
width: `${ROW_INDEX_COLUMN_WIDTH}px`,
|
|
5455
|
+
position: "sticky",
|
|
5456
|
+
left: 0,
|
|
5457
|
+
zIndex: 40
|
|
5458
|
+
},
|
|
5459
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative flex items-center w-full h-full", children: [
|
|
5460
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "pl-1", children: displayIndex }),
|
|
5461
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "absolute inset-y-0 right-0 flex items-center gap-0.5 pr-0.5", children: [
|
|
5462
|
+
rowContextMenuItems && rowContextMenuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5463
|
+
RowContextMenu,
|
|
5464
|
+
{
|
|
5465
|
+
row,
|
|
4537
5466
|
rowId,
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
5467
|
+
items: rowContextMenuItems,
|
|
5468
|
+
compactMode: effectiveCompactMode
|
|
5469
|
+
}
|
|
5470
|
+
),
|
|
5471
|
+
enableHighlighting && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5472
|
+
"button",
|
|
5473
|
+
{
|
|
5474
|
+
type: "button",
|
|
5475
|
+
onClick: (e) => {
|
|
5476
|
+
e.stopPropagation();
|
|
5477
|
+
setHighlightPickerRow(rowId);
|
|
5478
|
+
},
|
|
5479
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded",
|
|
5480
|
+
title: "Highlight row",
|
|
5481
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5482
|
+
AiFillHighlight,
|
|
5483
|
+
{
|
|
5484
|
+
className: cn(
|
|
5485
|
+
"h-2.5 w-2.5",
|
|
5486
|
+
rowHighlight ? "text-amber-500" : "text-gray-500"
|
|
5487
|
+
)
|
|
5488
|
+
}
|
|
5489
|
+
)
|
|
5490
|
+
}
|
|
5491
|
+
),
|
|
5492
|
+
enableComments && (cellHasComments(
|
|
5493
|
+
rowId,
|
|
5494
|
+
ROW_INDEX_COLUMN_ID
|
|
5495
|
+
) ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
5496
|
+
"button",
|
|
5497
|
+
{
|
|
5498
|
+
type: "button",
|
|
5499
|
+
onClick: (e) => {
|
|
5500
|
+
e.stopPropagation();
|
|
5501
|
+
setViewCommentsCell({
|
|
5502
|
+
rowId,
|
|
5503
|
+
columnId: ROW_INDEX_COLUMN_ID
|
|
5504
|
+
});
|
|
5505
|
+
},
|
|
5506
|
+
className: "p-0.5 bg-amber-100 hover:bg-amber-200 rounded transition-colors flex items-center gap-0.5",
|
|
5507
|
+
title: `${getCellUnresolvedCommentCount(rowId, ROW_INDEX_COLUMN_ID)} comment(s) - click to view`,
|
|
5508
|
+
children: [
|
|
5509
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FaComment, { className: "h-2.5 w-2.5 text-amber-500" }),
|
|
5510
|
+
getCellUnresolvedCommentCount(
|
|
5511
|
+
rowId,
|
|
5512
|
+
ROW_INDEX_COLUMN_ID
|
|
5513
|
+
) > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[9px] font-medium text-amber-600", children: getCellUnresolvedCommentCount(
|
|
5514
|
+
rowId,
|
|
5515
|
+
ROW_INDEX_COLUMN_ID
|
|
5516
|
+
) > 99 ? "99+" : getCellUnresolvedCommentCount(
|
|
5517
|
+
rowId,
|
|
5518
|
+
ROW_INDEX_COLUMN_ID
|
|
5519
|
+
) })
|
|
5520
|
+
]
|
|
5521
|
+
}
|
|
5522
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5523
|
+
"button",
|
|
5524
|
+
{
|
|
5525
|
+
type: "button",
|
|
5526
|
+
onClick: (e) => {
|
|
5527
|
+
e.stopPropagation();
|
|
5528
|
+
setCommentModalCell({
|
|
5529
|
+
rowId,
|
|
5530
|
+
columnId: ROW_INDEX_COLUMN_ID
|
|
5531
|
+
});
|
|
5532
|
+
},
|
|
5533
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded",
|
|
5534
|
+
title: "Add comment",
|
|
5535
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FaRegComment, { className: "h-2.5 w-2.5 text-gray-500" })
|
|
5536
|
+
}
|
|
5537
|
+
)),
|
|
5538
|
+
rowActions?.map((action) => {
|
|
5539
|
+
if (action.visible && !action.visible(row))
|
|
5540
|
+
return null;
|
|
5541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4567
5542
|
"button",
|
|
4568
5543
|
{
|
|
4569
5544
|
type: "button",
|
|
4570
5545
|
onClick: (e) => {
|
|
4571
5546
|
e.stopPropagation();
|
|
4572
|
-
|
|
4573
|
-
rowId,
|
|
4574
|
-
columnId: ROW_INDEX_COLUMN_ID
|
|
4575
|
-
});
|
|
5547
|
+
action.onClick(row, rowId);
|
|
4576
5548
|
},
|
|
4577
|
-
className:
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
{
|
|
4588
|
-
type: "button",
|
|
4589
|
-
onClick: (e) => {
|
|
4590
|
-
e.stopPropagation();
|
|
4591
|
-
action.onClick(row, rowId);
|
|
4592
|
-
},
|
|
4593
|
-
className: cn(
|
|
4594
|
-
"opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded",
|
|
4595
|
-
action.className
|
|
4596
|
-
),
|
|
4597
|
-
title: action.tooltip,
|
|
4598
|
-
children: action.icon
|
|
4599
|
-
},
|
|
4600
|
-
action.id
|
|
4601
|
-
);
|
|
4602
|
-
})
|
|
4603
|
-
] })
|
|
5549
|
+
className: cn(
|
|
5550
|
+
"opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded",
|
|
5551
|
+
action.className
|
|
5552
|
+
),
|
|
5553
|
+
title: action.tooltip,
|
|
5554
|
+
children: action.icon
|
|
5555
|
+
},
|
|
5556
|
+
action.id
|
|
5557
|
+
);
|
|
5558
|
+
})
|
|
4604
5559
|
] })
|
|
4605
|
-
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
}
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
}
|
|
4620
|
-
const column = item.column;
|
|
4621
|
-
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
4622
|
-
const isEditing = editingCell?.rowId === rowId && editingCell?.columnId === column.id;
|
|
4623
|
-
const isFocused = focusedCell?.rowId === rowId && focusedCell?.columnId === column.id;
|
|
4624
|
-
const isInSelection = isCellInSelection(
|
|
4625
|
-
rowId,
|
|
4626
|
-
column.id
|
|
5560
|
+
] })
|
|
5561
|
+
}
|
|
5562
|
+
),
|
|
5563
|
+
columnRenderItems.map((item) => {
|
|
5564
|
+
if (item.type === "collapsed-placeholder") {
|
|
5565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5566
|
+
"td",
|
|
5567
|
+
{
|
|
5568
|
+
className: "border border-gray-200 px-2 py-1 text-center text-gray-300",
|
|
5569
|
+
style: {
|
|
5570
|
+
backgroundColor: item.headerColor || "rgb(243 244 246)"
|
|
5571
|
+
}
|
|
5572
|
+
},
|
|
5573
|
+
`${item.groupId}-placeholder`
|
|
4627
5574
|
);
|
|
4628
|
-
|
|
5575
|
+
}
|
|
5576
|
+
const column = item.column;
|
|
5577
|
+
const value = column.getValue ? column.getValue(row) : row[column.id];
|
|
5578
|
+
const isEditing = editingCell?.rowId === rowId && editingCell?.columnId === column.id;
|
|
5579
|
+
const isFocused = focusedCell?.rowId === rowId && focusedCell?.columnId === column.id;
|
|
5580
|
+
const isInSelection = isCellInSelection(
|
|
5581
|
+
rowId,
|
|
5582
|
+
column.id
|
|
5583
|
+
);
|
|
5584
|
+
const selectionEdge = getCellSelectionEdge(
|
|
5585
|
+
rowId,
|
|
5586
|
+
column.id
|
|
5587
|
+
);
|
|
5588
|
+
const cellOrRowOrColumnHighlight = getCellHighlight(rowId, column.id) || rowHighlight?.color || getColumnHighlight(column.id);
|
|
5589
|
+
const isColPinned = isColumnPinned(column.id);
|
|
5590
|
+
const colPinSide = getColumnPinSide(column.id);
|
|
5591
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5592
|
+
MemoizedSpreadsheetCell,
|
|
5593
|
+
{
|
|
5594
|
+
value,
|
|
5595
|
+
column,
|
|
5596
|
+
row,
|
|
5597
|
+
rowIndex,
|
|
4629
5598
|
rowId,
|
|
4630
|
-
column.
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
column.id
|
|
4657
|
-
),
|
|
4658
|
-
onClick: (e) => handleCellClick(rowId, column.id, e),
|
|
4659
|
-
onConfirm: handleConfirmEdit,
|
|
4660
|
-
onCancel: handleCancelEdit,
|
|
4661
|
-
onHighlight: enableHighlighting ? () => {
|
|
4662
|
-
setHighlightPickerCell({
|
|
4663
|
-
rowId,
|
|
4664
|
-
columnId: column.id
|
|
4665
|
-
});
|
|
4666
|
-
} : void 0,
|
|
4667
|
-
hasComments: cellHasComments(
|
|
4668
|
-
rowId,
|
|
4669
|
-
column.id
|
|
4670
|
-
),
|
|
4671
|
-
unresolvedCommentCount: getCellUnresolvedCommentCount(
|
|
4672
|
-
rowId,
|
|
4673
|
-
column.id
|
|
4674
|
-
),
|
|
4675
|
-
onAddComment: enableComments ? () => setCommentModalCell({
|
|
4676
|
-
rowId,
|
|
4677
|
-
columnId: column.id
|
|
4678
|
-
}) : void 0,
|
|
4679
|
-
onViewComments: enableComments && cellHasComments(rowId, column.id) ? () => setViewCommentsCell({
|
|
5599
|
+
isEditable: column.editable && enableCellEditing,
|
|
5600
|
+
isEditing,
|
|
5601
|
+
isFocused,
|
|
5602
|
+
isInSelection,
|
|
5603
|
+
selectionEdge,
|
|
5604
|
+
isRowSelected,
|
|
5605
|
+
isRowHovered: false,
|
|
5606
|
+
highlightColor: cellOrRowOrColumnHighlight,
|
|
5607
|
+
isDuplicate: isCellDuplicate(rowId, column.id),
|
|
5608
|
+
compactMode: effectiveCompactMode,
|
|
5609
|
+
isOddRow: rowIndex % 2 !== 0,
|
|
5610
|
+
resolvedWidth: getColumnWidth(column.id),
|
|
5611
|
+
isPinned: isColPinned,
|
|
5612
|
+
pinSide: colPinSide,
|
|
5613
|
+
pinnedZIndex: isColPinned ? getPinnedZIndex(column.id) : void 0,
|
|
5614
|
+
leftOffset: getColumnLeftOffset(column.id),
|
|
5615
|
+
rightOffset: getColumnRightOffset(
|
|
5616
|
+
column.id
|
|
5617
|
+
),
|
|
5618
|
+
onClick: (e) => handleCellClick(rowId, column.id, e),
|
|
5619
|
+
onDoubleClick: () => handleCellDoubleClick(rowId, column.id),
|
|
5620
|
+
onMouseEnter: () => handleCellMouseEnter(rowId, column.id),
|
|
5621
|
+
onConfirm: handleConfirmEdit,
|
|
5622
|
+
onCancel: handleCancelEdit,
|
|
5623
|
+
onHighlight: enableHighlighting ? () => {
|
|
5624
|
+
setHighlightPickerCell({
|
|
4680
5625
|
rowId,
|
|
4681
5626
|
columnId: column.id
|
|
4682
|
-
})
|
|
4683
|
-
},
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
5627
|
+
});
|
|
5628
|
+
} : void 0,
|
|
5629
|
+
hasComments: cellHasComments(
|
|
5630
|
+
rowId,
|
|
5631
|
+
column.id
|
|
5632
|
+
),
|
|
5633
|
+
unresolvedCommentCount: getCellUnresolvedCommentCount(
|
|
5634
|
+
rowId,
|
|
5635
|
+
column.id
|
|
5636
|
+
),
|
|
5637
|
+
onAddComment: enableComments ? () => setCommentModalCell({
|
|
5638
|
+
rowId,
|
|
5639
|
+
columnId: column.id
|
|
5640
|
+
}) : void 0,
|
|
5641
|
+
onViewComments: enableComments && cellHasComments(rowId, column.id) ? () => setViewCommentsCell({
|
|
5642
|
+
rowId,
|
|
5643
|
+
columnId: column.id
|
|
5644
|
+
}) : void 0
|
|
5645
|
+
},
|
|
5646
|
+
column.id
|
|
5647
|
+
);
|
|
5648
|
+
})
|
|
5649
|
+
]
|
|
5650
|
+
},
|
|
5651
|
+
rowId
|
|
5652
|
+
);
|
|
5653
|
+
}) })
|
|
5654
|
+
] })
|
|
5655
|
+
] }),
|
|
5656
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
5657
|
+
SelectionSummaryBar,
|
|
5658
|
+
{
|
|
5659
|
+
summary: selectionSummary,
|
|
5660
|
+
focusedCell,
|
|
5661
|
+
columns: visibleColumns,
|
|
5662
|
+
data: paginatedData,
|
|
5663
|
+
getRowId,
|
|
5664
|
+
currentPage,
|
|
5665
|
+
pageSize
|
|
4693
5666
|
}
|
|
4694
|
-
)
|
|
4695
|
-
showPagination && effectiveTotalItems > 0 && /* @__PURE__ */ (0,
|
|
5667
|
+
),
|
|
5668
|
+
showPagination && effectiveTotalItems > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4696
5669
|
import_design_system2.Pagination,
|
|
4697
5670
|
{
|
|
4698
5671
|
currentPage,
|
|
@@ -4707,7 +5680,7 @@ function Spreadsheet({
|
|
|
4707
5680
|
onPageSizeChange: handlePageSizeChange
|
|
4708
5681
|
}
|
|
4709
5682
|
),
|
|
4710
|
-
/* @__PURE__ */ (0,
|
|
5683
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4711
5684
|
AddCommentModal,
|
|
4712
5685
|
{
|
|
4713
5686
|
isOpen: commentModalCell !== null,
|
|
@@ -4718,7 +5691,7 @@ function Spreadsheet({
|
|
|
4718
5691
|
}
|
|
4719
5692
|
}
|
|
4720
5693
|
),
|
|
4721
|
-
/* @__PURE__ */ (0,
|
|
5694
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4722
5695
|
ViewCommentsModal,
|
|
4723
5696
|
{
|
|
4724
5697
|
isOpen: viewCommentsCell !== null,
|
|
@@ -4733,29 +5706,32 @@ function Spreadsheet({
|
|
|
4733
5706
|
onClose: () => setViewCommentsCell(null)
|
|
4734
5707
|
}
|
|
4735
5708
|
),
|
|
4736
|
-
highlightPickerRow !== null && /* @__PURE__ */ (0,
|
|
5709
|
+
highlightPickerRow !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4737
5710
|
ColorPickerPopover,
|
|
4738
5711
|
{
|
|
4739
5712
|
title: "Highlight Row",
|
|
4740
5713
|
paletteType: "row",
|
|
5714
|
+
recentColors,
|
|
4741
5715
|
onSelectColor: (color) => handleRowHighlightToggle(highlightPickerRow, color),
|
|
4742
5716
|
onClose: () => setHighlightPickerRow(null)
|
|
4743
5717
|
}
|
|
4744
5718
|
),
|
|
4745
|
-
highlightPickerColumn !== null && /* @__PURE__ */ (0,
|
|
5719
|
+
highlightPickerColumn !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4746
5720
|
ColorPickerPopover,
|
|
4747
5721
|
{
|
|
4748
5722
|
title: highlightPickerColumn === ROW_INDEX_COLUMN_ID ? "Highlight Row Index Column" : `Highlight Column: ${columns.find((c) => c.id === highlightPickerColumn)?.label || ""}`,
|
|
4749
5723
|
paletteType: "column",
|
|
5724
|
+
recentColors,
|
|
4750
5725
|
onSelectColor: (color) => handleColumnHighlightToggle(highlightPickerColumn, color),
|
|
4751
5726
|
onClose: () => setHighlightPickerColumn(null)
|
|
4752
5727
|
}
|
|
4753
5728
|
),
|
|
4754
|
-
highlightPickerCell !== null && /* @__PURE__ */ (0,
|
|
5729
|
+
highlightPickerCell !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4755
5730
|
ColorPickerPopover,
|
|
4756
5731
|
{
|
|
4757
5732
|
title: "Highlight Cell",
|
|
4758
5733
|
paletteType: "row",
|
|
5734
|
+
recentColors,
|
|
4759
5735
|
onSelectColor: (color) => handleCellHighlightToggle(
|
|
4760
5736
|
highlightPickerCell.rowId,
|
|
4761
5737
|
highlightPickerCell.columnId,
|
|
@@ -4764,7 +5740,7 @@ function Spreadsheet({
|
|
|
4764
5740
|
onClose: () => setHighlightPickerCell(null)
|
|
4765
5741
|
}
|
|
4766
5742
|
),
|
|
4767
|
-
/* @__PURE__ */ (0,
|
|
5743
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4768
5744
|
KeyboardShortcutsModal,
|
|
4769
5745
|
{
|
|
4770
5746
|
isOpen: showKeyboardShortcuts,
|
|
@@ -4772,7 +5748,7 @@ function Spreadsheet({
|
|
|
4772
5748
|
shortcuts
|
|
4773
5749
|
}
|
|
4774
5750
|
),
|
|
4775
|
-
/* @__PURE__ */ (0,
|
|
5751
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4776
5752
|
SpreadsheetSettingsModal,
|
|
4777
5753
|
{
|
|
4778
5754
|
isOpen: showSettingsModal,
|
|
@@ -4801,12 +5777,14 @@ Spreadsheet.displayName = "Spreadsheet";
|
|
|
4801
5777
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4802
5778
|
0 && (module.exports = {
|
|
4803
5779
|
ActiveFiltersDisplay,
|
|
5780
|
+
MemoizedSpreadsheetHeader,
|
|
4804
5781
|
RowContextMenu,
|
|
4805
5782
|
Spreadsheet,
|
|
4806
5783
|
SpreadsheetCell,
|
|
4807
5784
|
SpreadsheetFilterDropdown,
|
|
4808
5785
|
SpreadsheetHeader,
|
|
4809
5786
|
SpreadsheetSettingsModal,
|
|
4810
|
-
SpreadsheetToolbar
|
|
5787
|
+
SpreadsheetToolbar,
|
|
5788
|
+
useSpreadsheetDuplicates
|
|
4811
5789
|
});
|
|
4812
5790
|
//# sourceMappingURL=index.js.map
|