@a3s-lab/office 0.37.4 → 0.37.5
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/README.md +5 -1
- package/dist/0~spreadsheet-editor.js +52 -36
- package/dist/5416.js +56 -44
- package/dist/core.js +56 -10
- package/dist/internal/features/work/editors/spreadsheet-auto-filter-menu.d.ts +2 -2
- package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +3 -3
- package/dist/internal/features/work/editors/spreadsheet-date-time-command.d.ts +2 -1
- package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +2 -2
- package/dist/internal/features/work/editors/spreadsheet-filter-reconciliation.d.ts +3 -2
- package/dist/internal/features/work/work-spreadsheet-dynamic-filter.d.ts +3 -2
- package/dist/internal/features/work/work-types.d.ts +3 -0
- package/dist/office-kernel.wasm +0 -0
- package/docs/latest/en/browser-editor-architecture.md +4 -2
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -309,9 +309,13 @@ and [CLI reference](./docs/latest/en/cli-reference.md).
|
|
|
309
309
|
|
|
310
310
|
## Current release
|
|
311
311
|
|
|
312
|
-
Version `0.37.
|
|
312
|
+
Version `0.37.5` keeps the release surface focused on local, testable
|
|
313
313
|
workflows:
|
|
314
314
|
|
|
315
|
+
- Spreadsheet XLSX import/export now retains the workbook's native 1900 or
|
|
316
|
+
1904 date system, exact typed date serials, dynamic date filters, and
|
|
317
|
+
epoch-correct current-date authoring across controlled and collaborative
|
|
318
|
+
reopen flows.
|
|
315
319
|
- Spreadsheet Custom Sort now recognizes native table and AutoFilter-owned
|
|
316
320
|
ranges, keeps headers and totals structurally safe, and reapplies active
|
|
317
321
|
filter visibility after one formula-safe, undoable sort.
|
|
@@ -4516,19 +4516,21 @@ function spreadsheetSortColumnLabel(column) {
|
|
|
4516
4516
|
}
|
|
4517
4517
|
]).replace(/\d+$/, '');
|
|
4518
4518
|
}
|
|
4519
|
-
function reconcileSpreadsheetFiltersAfterFortune(sheets, sourceSheets, operations = []
|
|
4519
|
+
function reconcileSpreadsheetFiltersAfterFortune(sheets, sourceSheets, operations = [], context = {
|
|
4520
|
+
now: new Date()
|
|
4521
|
+
}) {
|
|
4520
4522
|
const coordinates = spreadsheetCellOperationCoordinates(operations);
|
|
4521
4523
|
return sheets.map((sheet, index)=>{
|
|
4522
4524
|
const source = spreadsheet_filter_reconciliation_sourceSheet(sheet, sourceSheets, index);
|
|
4523
4525
|
if (!source || !spreadsheetSheetHasCriteria(source) || !spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetFilterRangesWereEdited(source, operations, coordinates)) return sheet;
|
|
4524
|
-
const previousTableOwnedRows = spreadsheetTableFilterOwnedRows(source);
|
|
4526
|
+
const previousTableOwnedRows = spreadsheetTableFilterOwnedRows(source, context);
|
|
4525
4527
|
const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
|
|
4526
4528
|
for (const row of previousTableOwnedRows)manuallyHiddenRows.delete(row);
|
|
4527
4529
|
let next = sheet;
|
|
4528
|
-
if (normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select) && workSpreadsheetAutoFilterCriteriaEntries(source).length) next = workSpreadsheetSheetWithReappliedAutoFilterCriteria(sheet, source, manuallyHiddenRows);
|
|
4530
|
+
if (normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select) && workSpreadsheetAutoFilterCriteriaEntries(source).length) next = workSpreadsheetSheetWithReappliedAutoFilterCriteria(sheet, source, manuallyHiddenRows, context);
|
|
4529
4531
|
const hiddenRows = new Set(manuallyHiddenRows);
|
|
4530
4532
|
for (const row of workSpreadsheetAutoFilterOwnedRows(next))hiddenRows.add(row);
|
|
4531
|
-
for (const row of spreadsheetTableFilterOwnedRows(next))hiddenRows.add(row);
|
|
4533
|
+
for (const row of spreadsheetTableFilterOwnedRows(next, context))hiddenRows.add(row);
|
|
4532
4534
|
const rowhidden = Object.fromEntries([
|
|
4533
4535
|
...hiddenRows
|
|
4534
4536
|
].sort(compareSpreadsheetRowKeys).map((row)=>[
|
|
@@ -4544,26 +4546,28 @@ function reconcileSpreadsheetFiltersAfterFortune(sheets, sourceSheets, operation
|
|
|
4544
4546
|
};
|
|
4545
4547
|
});
|
|
4546
4548
|
}
|
|
4547
|
-
function reconcileSpreadsheetFiltersAfterSort(sheet, source, range, sourceIndexes
|
|
4549
|
+
function reconcileSpreadsheetFiltersAfterSort(sheet, source, range, sourceIndexes, context = {
|
|
4550
|
+
now: new Date()
|
|
4551
|
+
}) {
|
|
4548
4552
|
if (!spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetSortSourceIndexesAreValid(range, sourceIndexes)) return null;
|
|
4549
4553
|
const reconciled = reconcileSpreadsheetFiltersAfterFortune([
|
|
4550
4554
|
sheet
|
|
4551
4555
|
], [
|
|
4552
4556
|
source
|
|
4553
|
-
])[0];
|
|
4557
|
+
], [], context)[0];
|
|
4554
4558
|
if (!reconciled) return null;
|
|
4555
4559
|
let filter = reconciled.filter;
|
|
4556
4560
|
const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(source.filter_select);
|
|
4557
4561
|
if (autoFilter && autoFilter.row[0] === range.row[0] && autoFilter.row[1] === range.row[1] && autoFilter.column[0] === range.column[0] && autoFilter.column[1] === range.column[1]) filter = spreadsheetFilterWithRemappedOpaqueEntries(reconciled, source, range, sourceIndexes);
|
|
4558
4562
|
const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
|
|
4559
|
-
for (const row of spreadsheetTableFilterOwnedRows(source))manuallyHiddenRows.delete(row);
|
|
4563
|
+
for (const row of spreadsheetTableFilterOwnedRows(source, context))manuallyHiddenRows.delete(row);
|
|
4560
4564
|
const withFilter = {
|
|
4561
4565
|
...reconciled,
|
|
4562
4566
|
filter
|
|
4563
4567
|
};
|
|
4564
4568
|
const hiddenRows = new Set(manuallyHiddenRows);
|
|
4565
4569
|
for (const row of workSpreadsheetAutoFilterOwnedRows(withFilter))hiddenRows.add(row);
|
|
4566
|
-
for (const row of spreadsheetTableFilterOwnedRows(withFilter))hiddenRows.add(row);
|
|
4570
|
+
for (const row of spreadsheetTableFilterOwnedRows(withFilter, context))hiddenRows.add(row);
|
|
4567
4571
|
const rowhidden = Object.fromEntries([
|
|
4568
4572
|
...hiddenRows
|
|
4569
4573
|
].sort(compareSpreadsheetRowKeys).map((row)=>[
|
|
@@ -4645,13 +4649,13 @@ function spreadsheetFilterRanges(sheet) {
|
|
|
4645
4649
|
}
|
|
4646
4650
|
return ranges;
|
|
4647
4651
|
}
|
|
4648
|
-
function spreadsheetTableFilterOwnedRows(sheet) {
|
|
4652
|
+
function spreadsheetTableFilterOwnedRows(sheet, context) {
|
|
4649
4653
|
const hidden = new Set();
|
|
4650
4654
|
for (const table of sheet.tables ?? []){
|
|
4651
4655
|
const range = spreadsheetTableFilterRange(table);
|
|
4652
4656
|
if (range) for (const filter of table.filters){
|
|
4653
4657
|
const column = range.column[0] + filter.column;
|
|
4654
|
-
const rowhidden = workSpreadsheetFilterHiddenRows(sheet, range, column, filter.criteria);
|
|
4658
|
+
const rowhidden = workSpreadsheetFilterHiddenRows(sheet, range, column, filter.criteria, context);
|
|
4655
4659
|
for (const row of Object.keys(rowhidden ?? {}))hidden.add(row);
|
|
4656
4660
|
}
|
|
4657
4661
|
}
|
|
@@ -5348,8 +5352,12 @@ function recordSpreadsheetProjectionMeasure(start, end, detail) {
|
|
|
5348
5352
|
});
|
|
5349
5353
|
} catch {}
|
|
5350
5354
|
}
|
|
5351
|
-
function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
|
|
5355
|
+
function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = [], dateSystem = '1900') {
|
|
5352
5356
|
const startedAt = spreadsheetProjectionNow();
|
|
5357
|
+
const filterContext = {
|
|
5358
|
+
dateSystem,
|
|
5359
|
+
now: new Date()
|
|
5360
|
+
};
|
|
5353
5361
|
const incremental = projectSpreadsheetSheetsFromFortuneOperations(sheets, sourceSheets, operations);
|
|
5354
5362
|
if (incremental) {
|
|
5355
5363
|
recordSpreadsheetControlledProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
|
|
@@ -5357,7 +5365,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
|
|
|
5357
5365
|
incremental: true,
|
|
5358
5366
|
operationCount: operations.length
|
|
5359
5367
|
});
|
|
5360
|
-
return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations), sourceSheets, operations);
|
|
5368
|
+
return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations), sourceSheets, operations, filterContext);
|
|
5361
5369
|
}
|
|
5362
5370
|
const operationCoordinatesBySheet = spreadsheetCellOperationCoordinates(operations);
|
|
5363
5371
|
const projected = sheets.map((sheet, index)=>{
|
|
@@ -5399,7 +5407,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
|
|
|
5399
5407
|
incremental: false,
|
|
5400
5408
|
operationCount: operations.length
|
|
5401
5409
|
});
|
|
5402
|
-
return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations), sourceSheets, operations);
|
|
5410
|
+
return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations), sourceSheets, operations, filterContext);
|
|
5403
5411
|
}
|
|
5404
5412
|
function recordSpreadsheetControlledProjectionMeasure(start, end, detail) {
|
|
5405
5413
|
try {
|
|
@@ -5929,18 +5937,18 @@ function toggleSpreadsheetAutoFilter(content, sheetId, selection) {
|
|
|
5929
5937
|
function spreadsheetAutoFilterCriteria(sheet, column) {
|
|
5930
5938
|
return workSpreadsheetAutoFilterCriteria(sheet, column);
|
|
5931
5939
|
}
|
|
5932
|
-
function spreadsheetAutoFilterColumnIsNumeric(sheet, range, column) {
|
|
5933
|
-
return spreadsheetAutoFilterColumnProfile(sheet, range, column).numeric;
|
|
5940
|
+
function spreadsheetAutoFilterColumnIsNumeric(sheet, range, column, dateSystem = '1900') {
|
|
5941
|
+
return spreadsheetAutoFilterColumnProfile(sheet, range, column, dateSystem).numeric;
|
|
5934
5942
|
}
|
|
5935
|
-
function spreadsheetAutoFilterColumnIsDate(sheet, range, column) {
|
|
5936
|
-
return spreadsheetAutoFilterColumnProfile(sheet, range, column).date;
|
|
5943
|
+
function spreadsheetAutoFilterColumnIsDate(sheet, range, column, dateSystem = '1900') {
|
|
5944
|
+
return spreadsheetAutoFilterColumnProfile(sheet, range, column, dateSystem).date;
|
|
5937
5945
|
}
|
|
5938
|
-
function spreadsheetAutoFilterColumnProfile(sheet, range, column) {
|
|
5946
|
+
function spreadsheetAutoFilterColumnProfile(sheet, range, column, dateSystem) {
|
|
5939
5947
|
if (!Number.isSafeInteger(column) || column < range.column[0] || column > range.column[1]) return {
|
|
5940
5948
|
date: false,
|
|
5941
5949
|
numeric: false
|
|
5942
5950
|
};
|
|
5943
|
-
const key = `${range.row[0]}:${range.row[1]}:${column}`;
|
|
5951
|
+
const key = `${dateSystem}:${range.row[0]}:${range.row[1]}:${column}`;
|
|
5944
5952
|
const cached = spreadsheetAutoFilterColumnProfileCache.get(sheet)?.get(key);
|
|
5945
5953
|
if (cached) return cached;
|
|
5946
5954
|
const cellAt = spreadsheetSheetCellReader(sheet);
|
|
@@ -5949,7 +5957,7 @@ function spreadsheetAutoFilterColumnProfile(sheet, range, column) {
|
|
|
5949
5957
|
const cell = cellAt(row, column);
|
|
5950
5958
|
const value = cell?.v ?? cell?.m;
|
|
5951
5959
|
if (null == value || '' === value) continue;
|
|
5952
|
-
const valueKind = workSpreadsheetCellIsDate(cell) ? 'date' : 'number' == typeof value && Number.isFinite(value) ? 'numeric' : null;
|
|
5960
|
+
const valueKind = workSpreadsheetCellIsDate(cell, dateSystem) ? 'date' : 'number' == typeof value && Number.isFinite(value) ? 'numeric' : null;
|
|
5953
5961
|
if (!valueKind || null !== columnKind && columnKind !== valueKind) {
|
|
5954
5962
|
columnKind = null;
|
|
5955
5963
|
break;
|
|
@@ -5969,7 +5977,10 @@ function applySpreadsheetAutoFilterCriteria(content, sheetId, column, criteria,
|
|
|
5969
5977
|
const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
|
|
5970
5978
|
const sheet = content.sheets[sheetIndex];
|
|
5971
5979
|
if (!sheet) return null;
|
|
5972
|
-
const nextSheet = workSpreadsheetSheetWithAutoFilterCriteria(sheet, column, criteria,
|
|
5980
|
+
const nextSheet = workSpreadsheetSheetWithAutoFilterCriteria(sheet, column, criteria, {
|
|
5981
|
+
dateSystem: content.dateSystem,
|
|
5982
|
+
now: context?.now ?? new Date()
|
|
5983
|
+
});
|
|
5973
5984
|
if (!nextSheet) return null;
|
|
5974
5985
|
const sheets = [
|
|
5975
5986
|
...content.sheets
|
|
@@ -8529,13 +8540,15 @@ function clampSpreadsheetDataValidationFocus(value, range) {
|
|
|
8529
8540
|
const MILLISECONDS_PER_DAY = 86400000;
|
|
8530
8541
|
const MINUTES_PER_DAY = 1440;
|
|
8531
8542
|
const EXCEL_1900_EPOCH_UTC = Date.UTC(1899, 11, 30);
|
|
8532
|
-
|
|
8543
|
+
const EXCEL_1904_EPOCH_UTC = Date.UTC(1904, 0, 1);
|
|
8544
|
+
function spreadsheetDateTimeEntry(kind, now, dateSystem = '1900') {
|
|
8533
8545
|
if (!Number.isFinite(now.getTime())) return null;
|
|
8534
8546
|
if ('date' === kind) {
|
|
8535
8547
|
const year = now.getFullYear();
|
|
8536
8548
|
const month = now.getMonth();
|
|
8537
8549
|
const day = now.getDate();
|
|
8538
|
-
const
|
|
8550
|
+
const epoch = '1904' === dateSystem ? EXCEL_1904_EPOCH_UTC : EXCEL_1900_EPOCH_UTC;
|
|
8551
|
+
const value = (Date.UTC(year, month, day) - epoch) / MILLISECONDS_PER_DAY;
|
|
8539
8552
|
if (!Number.isFinite(value)) return null;
|
|
8540
8553
|
return {
|
|
8541
8554
|
format: {
|
|
@@ -8576,7 +8589,7 @@ function createSpreadsheetDateTimeExtension() {
|
|
|
8576
8589
|
function insertCurrentSpreadsheetDateTime(context, kind) {
|
|
8577
8590
|
if (!isSpreadsheetDateTimeKind(kind)) return false;
|
|
8578
8591
|
const target = spreadsheetDateTimeTarget(context);
|
|
8579
|
-
const entry = spreadsheetDateTimeEntry(kind, new Date());
|
|
8592
|
+
const entry = spreadsheetDateTimeEntry(kind, new Date(), context.content.dateSystem);
|
|
8580
8593
|
if (!target || !entry || !context.workbook) return false;
|
|
8581
8594
|
try {
|
|
8582
8595
|
context.workbook.batchCallApis([
|
|
@@ -10369,7 +10382,10 @@ function applySpreadsheetSort(context, request) {
|
|
|
10369
10382
|
if (!result.ok) return false;
|
|
10370
10383
|
if (owned && spreadsheetSortRequiresFilterReconciliation(sheet, owned)) {
|
|
10371
10384
|
const sorted = spreadsheetSheetWithSortedRange(sheet, validation.request.range, result.rows);
|
|
10372
|
-
const reconciled = reconcileSpreadsheetFiltersAfterSort(sorted, sheet, validation.request.range, result.sourceIndexes
|
|
10385
|
+
const reconciled = reconcileSpreadsheetFiltersAfterSort(sorted, sheet, validation.request.range, result.sourceIndexes, {
|
|
10386
|
+
dateSystem: context.content.dateSystem,
|
|
10387
|
+
now: new Date()
|
|
10388
|
+
});
|
|
10373
10389
|
if (!reconciled) return false;
|
|
10374
10390
|
context.onChange({
|
|
10375
10391
|
...context.content,
|
|
@@ -23509,7 +23525,7 @@ const SPREADSHEET_AUTO_FILTER_MENU_ITEMS = [
|
|
|
23509
23525
|
'|',
|
|
23510
23526
|
'filter-by-value'
|
|
23511
23527
|
];
|
|
23512
|
-
function enhanceSpreadsheetAutoFilterSurface(container, sheet, invoker) {
|
|
23528
|
+
function enhanceSpreadsheetAutoFilterSurface(container, sheet, invoker, dateSystem = '1900') {
|
|
23513
23529
|
const range = sheet?.filter_select;
|
|
23514
23530
|
const startRow = range?.row?.[0];
|
|
23515
23531
|
const startColumn = range?.column?.[0];
|
|
@@ -23556,14 +23572,14 @@ function enhanceSpreadsheetAutoFilterSurface(container, sheet, invoker) {
|
|
|
23556
23572
|
action.setAttribute('aria-haspopup', 'dialog');
|
|
23557
23573
|
}
|
|
23558
23574
|
}
|
|
23559
|
-
synchronizeSpreadsheetAutoFilterRankAction(menu, sheet, trigger);
|
|
23575
|
+
synchronizeSpreadsheetAutoFilterRankAction(menu, sheet, trigger, dateSystem);
|
|
23560
23576
|
return menu;
|
|
23561
23577
|
}
|
|
23562
|
-
function synchronizeSpreadsheetAutoFilterRankAction(menu, sheet, trigger) {
|
|
23578
|
+
function synchronizeSpreadsheetAutoFilterRankAction(menu, sheet, trigger, dateSystem) {
|
|
23563
23579
|
const existing = menu.querySelector('[data-a3s-auto-filter-rank]');
|
|
23564
23580
|
const range = normalizedWorkSpreadsheetAutoFilterRange(sheet?.filter_select);
|
|
23565
23581
|
const column = Number(trigger?.dataset.filterColumn);
|
|
23566
|
-
const numeric = Boolean(sheet && range && Number.isSafeInteger(column) && spreadsheetAutoFilterColumnIsNumeric(sheet, range, column));
|
|
23582
|
+
const numeric = Boolean(sheet && range && Number.isSafeInteger(column) && spreadsheetAutoFilterColumnIsNumeric(sheet, range, column, dateSystem));
|
|
23567
23583
|
if (!numeric) return void existing?.remove();
|
|
23568
23584
|
if (existing) {
|
|
23569
23585
|
existing.dataset.filterColumn = String(column);
|
|
@@ -24291,7 +24307,7 @@ function useSpreadsheetAutoFilter({ canvasRef, commandsRef, content, editable, m
|
|
|
24291
24307
|
const column = spreadsheetAutoFilterHeaderColumn(activeSheet, currentSpreadsheetAutoFilterSelection(selectionRef.current, workbookRef.current));
|
|
24292
24308
|
const startColumn = activeSheet?.filter_select?.column?.[0];
|
|
24293
24309
|
if (!container || null === column || !Number.isFinite(startColumn)) return false;
|
|
24294
|
-
enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current);
|
|
24310
|
+
enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current, contentRef.current.dateSystem);
|
|
24295
24311
|
const triggers = [
|
|
24296
24312
|
...container.querySelectorAll('.luckysheet-filter-options')
|
|
24297
24313
|
];
|
|
@@ -24300,7 +24316,7 @@ function useSpreadsheetAutoFilter({ canvasRef, commandsRef, content, editable, m
|
|
|
24300
24316
|
keyboardInvokerRef.current = trigger;
|
|
24301
24317
|
restoreInvokerFocusRef.current = true;
|
|
24302
24318
|
trigger.click();
|
|
24303
|
-
enhanceSpreadsheetAutoFilterSurface(container, activeSheet, trigger);
|
|
24319
|
+
enhanceSpreadsheetAutoFilterSurface(container, activeSheet, trigger, contentRef.current.dateSystem);
|
|
24304
24320
|
focusSpreadsheetAutoFilterMenu(container);
|
|
24305
24321
|
return true;
|
|
24306
24322
|
}, [
|
|
@@ -24328,11 +24344,11 @@ function useSpreadsheetAutoFilter({ canvasRef, commandsRef, content, editable, m
|
|
|
24328
24344
|
column,
|
|
24329
24345
|
columnLabel: spreadsheetAutoFilterColumnLabel(activeSheet, range.row[0], column),
|
|
24330
24346
|
criteria,
|
|
24331
|
-
date: spreadsheetAutoFilterColumnIsDate(activeSheet, range, column),
|
|
24347
|
+
date: spreadsheetAutoFilterColumnIsDate(activeSheet, range, column, contentRef.current.dateSystem),
|
|
24332
24348
|
filterRange: range,
|
|
24333
24349
|
hasActiveFilter: Object.hasOwn(activeSheet.filter ?? {}, key),
|
|
24334
24350
|
invoker,
|
|
24335
|
-
numeric: spreadsheetAutoFilterColumnIsNumeric(activeSheet, range, column),
|
|
24351
|
+
numeric: spreadsheetAutoFilterColumnIsNumeric(activeSheet, range, column, contentRef.current.dateSystem),
|
|
24336
24352
|
sheetId: activeSheet.id ?? sheetIdRef.current,
|
|
24337
24353
|
sheetName: activeSheet.name
|
|
24338
24354
|
};
|
|
@@ -24354,7 +24370,7 @@ function useSpreadsheetAutoFilter({ canvasRef, commandsRef, content, editable, m
|
|
|
24354
24370
|
if (!container || !editable) return;
|
|
24355
24371
|
const enhance = ()=>{
|
|
24356
24372
|
const activeSheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current);
|
|
24357
|
-
const menu = enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current);
|
|
24373
|
+
const menu = enhanceSpreadsheetAutoFilterSurface(container, activeSheet, keyboardInvokerRef.current, contentRef.current.dateSystem);
|
|
24358
24374
|
const menuOpen = Boolean(menu);
|
|
24359
24375
|
if (menuWasOpenRef.current && !menuOpen && restoreInvokerFocusRef.current) {
|
|
24360
24376
|
const invoker = keyboardInvokerRef.current;
|
|
@@ -24389,7 +24405,7 @@ function useSpreadsheetAutoFilter({ canvasRef, commandsRef, content, editable, m
|
|
|
24389
24405
|
keyboardInvokerRef.current = trigger;
|
|
24390
24406
|
restoreInvokerFocusRef.current = true;
|
|
24391
24407
|
trigger.click();
|
|
24392
|
-
enhanceSpreadsheetAutoFilterSurface(container, contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current), trigger);
|
|
24408
|
+
enhanceSpreadsheetAutoFilterSurface(container, contentRef.current.sheets.find((candidate)=>candidate.id === sheetIdRef.current), trigger, contentRef.current.dateSystem);
|
|
24393
24409
|
focusSpreadsheetAutoFilterMenu(container);
|
|
24394
24410
|
return;
|
|
24395
24411
|
}
|
|
@@ -30136,7 +30152,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
|
|
|
30136
30152
|
if (ignoreChangeDuringExternalSync(matchesRenderedWorkbook)) return;
|
|
30137
30153
|
const hasPendingResultPatches = calculation.hasPendingResultPatches();
|
|
30138
30154
|
if (previewRef.current || matchesRenderedWorkbook && !hasPendingResultPatches) return;
|
|
30139
|
-
const controlledSheets = spreadsheetSheetsFromFortune(sheets, contentRef.current.sheets, operations);
|
|
30155
|
+
const controlledSheets = spreadsheetSheetsFromFortune(sheets, contentRef.current.sheets, operations, contentRef.current.dateSystem);
|
|
30140
30156
|
const withCharts = reconcileSpreadsheetChartPreviews(contentRef.current, controlledSheets);
|
|
30141
30157
|
const next = reconcileSpreadsheetPivots(contentRef.current, withCharts.sheets);
|
|
30142
30158
|
const isHistoryNoop = sameSpreadsheetHistoryContent(contentRef.current, next);
|
package/dist/5416.js
CHANGED
|
@@ -591,6 +591,10 @@ function validateWorkOfficeSpreadsheetContent(content) {
|
|
|
591
591
|
type: 'spreadsheet',
|
|
592
592
|
sheets
|
|
593
593
|
};
|
|
594
|
+
if (void 0 !== content.dateSystem) {
|
|
595
|
+
if ('1900' !== content.dateSystem && '1904' !== content.dateSystem) invalidWorkOfficeSpreadsheetInput("a Spreadsheet date system of '1900' or '1904'");
|
|
596
|
+
result.dateSystem = content.dateSystem;
|
|
597
|
+
}
|
|
594
598
|
copyOptionalJsonField(content, result, 'calculation', 'calculation settings');
|
|
595
599
|
if (void 0 !== content.namedRanges) result.namedRanges = validateIdRecords(content.namedRanges, 'named range');
|
|
596
600
|
copyOptionalSheetSidecars(content, result, 'printAreas', 'print area');
|
|
@@ -1209,6 +1213,7 @@ function claimLabel(claim) {
|
|
|
1209
1213
|
return `${claim.kind} in sheet '${claim.parentId}'`;
|
|
1210
1214
|
}
|
|
1211
1215
|
function assertWorkOfficeSpreadsheetPatchSafe(previous, next, shared) {
|
|
1216
|
+
assertCompatibleValue(previous.dateSystem, next.dateSystem, shared.dateSystem, 'date system');
|
|
1212
1217
|
assertCompatibleValue(previous.calculation, next.calculation, shared.calculation, 'calculation settings');
|
|
1213
1218
|
assertRecordCollection(previous.sheets, next.sheets, shared.sheets, (sheet)=>sheet.id, 'sheet', assertCompatibleSheet);
|
|
1214
1219
|
assertRecordCollection(previous.namedRanges ?? [], next.namedRanges ?? [], shared.namedRanges ?? [], (range)=>range.id, 'named range');
|
|
@@ -1843,7 +1848,7 @@ function workOfficeSpreadsheetRoots(document, rootName) {
|
|
|
1843
1848
|
}
|
|
1844
1849
|
function initializeWorkOfficeSpreadsheetRoots(roots, content) {
|
|
1845
1850
|
appendWorkOfficeSpreadsheetRecordClaims(roots.recordClaims, void 0, content);
|
|
1846
|
-
patchSpreadsheetFlatJsonMap(roots.options, void 0, content
|
|
1851
|
+
patchSpreadsheetFlatJsonMap(roots.options, void 0, spreadsheetWorkbookOptions(content), 'workbook options');
|
|
1847
1852
|
patchSheets(roots, void 0, content);
|
|
1848
1853
|
patchIdRecords(roots.namedRanges, roots.namedRangeOrder, [], content.namedRanges ?? [], 'named range');
|
|
1849
1854
|
patchSheetSidecars(roots.printAreas, [], content.printAreas ?? [], 'print area');
|
|
@@ -1856,7 +1861,9 @@ function readWorkOfficeSpreadsheetRoots(roots) {
|
|
|
1856
1861
|
type: 'spreadsheet',
|
|
1857
1862
|
sheets: validatedSpreadsheetOrder(roots.sheetOrder, roots.sheets, 'sheet').map((id)=>readSpreadsheetSheetRecord(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(roots.sheets, id, `sheet '${id}'`), id))
|
|
1858
1863
|
};
|
|
1859
|
-
const
|
|
1864
|
+
const options = readSpreadsheetFlatJsonMap(roots.options, 'workbook options');
|
|
1865
|
+
const { dateSystem, ...calculation } = options;
|
|
1866
|
+
if (void 0 !== dateSystem) result.dateSystem = dateSystem;
|
|
1860
1867
|
if (Object.keys(calculation).length > 0) result.calculation = calculation;
|
|
1861
1868
|
const namedRanges = readIdRecords(roots.namedRanges, roots.namedRangeOrder, 'named range');
|
|
1862
1869
|
if (namedRanges.length > 0) result.namedRanges = namedRanges;
|
|
@@ -1872,7 +1879,7 @@ function patchWorkOfficeSpreadsheetRoots(roots, previous, next) {
|
|
|
1872
1879
|
const shared = readWorkOfficeSpreadsheetRoots(roots);
|
|
1873
1880
|
assertWorkOfficeSpreadsheetPatchSafe(previous, next, shared);
|
|
1874
1881
|
appendWorkOfficeSpreadsheetRecordClaims(roots.recordClaims, previous, next);
|
|
1875
|
-
patchSpreadsheetFlatJsonMap(roots.options, previous
|
|
1882
|
+
patchSpreadsheetFlatJsonMap(roots.options, spreadsheetWorkbookOptions(previous), spreadsheetWorkbookOptions(next), 'workbook options');
|
|
1876
1883
|
patchSheets(roots, previous, next);
|
|
1877
1884
|
patchIdRecords(roots.namedRanges, roots.namedRangeOrder, previous.namedRanges ?? [], next.namedRanges ?? [], 'named range');
|
|
1878
1885
|
patchSheetSidecars(roots.printAreas, previous.printAreas ?? [], next.printAreas ?? [], 'print area');
|
|
@@ -1880,6 +1887,13 @@ function patchWorkOfficeSpreadsheetRoots(roots, previous, next) {
|
|
|
1880
1887
|
patchSheetSidecars(roots.pageBreaks, previous.pageBreaks ?? [], next.pageBreaks ?? [], 'page breaks');
|
|
1881
1888
|
patchSheetSidecars(roots.pageSetups, previous.pageSetups ?? [], next.pageSetups ?? [], 'page setup');
|
|
1882
1889
|
}
|
|
1890
|
+
function spreadsheetWorkbookOptions(content) {
|
|
1891
|
+
const options = {
|
|
1892
|
+
...content.calculation
|
|
1893
|
+
};
|
|
1894
|
+
if (void 0 !== content.dateSystem) options.dateSystem = content.dateSystem;
|
|
1895
|
+
return Object.keys(options).length ? options : void 0;
|
|
1896
|
+
}
|
|
1883
1897
|
function assertWorkOfficeSpreadsheetRootsEmpty(roots) {
|
|
1884
1898
|
if (roots.options.size > 0 || roots.sheets.size > 0 || roots.sheetOrder.length > 0 || roots.namedRanges.size > 0 || roots.namedRangeOrder.length > 0 || roots.recordClaims.length > 0 || roots.printAreas.size > 0 || roots.printTitles.size > 0 || roots.pageBreaks.size > 0 || roots.pageSetups.size > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Spreadsheet collaboration roots contain data without initialized metadata.');
|
|
1885
1899
|
}
|
|
@@ -2212,9 +2226,8 @@ function workSpreadsheetFilterTextIsBounded(value) {
|
|
|
2212
2226
|
return characters >= 1 && characters <= WORK_SPREADSHEET_FILTER_TEXT_MAX_CHARACTERS;
|
|
2213
2227
|
}
|
|
2214
2228
|
const MILLISECONDS_PER_DAY = 86400000;
|
|
2215
|
-
const EXCEL_1900_EPOCH_UTC = Date.UTC(1899, 11, 30);
|
|
2216
2229
|
const EXCEL_1900_CIVIL_EPOCH_UTC = Date.UTC(1899, 11, 31);
|
|
2217
|
-
const
|
|
2230
|
+
const EXCEL_1904_EPOCH_UTC = Date.UTC(1904, 0, 1);
|
|
2218
2231
|
const SERIAL_INTEGER_EPSILON = 1e-7;
|
|
2219
2232
|
const CALENDAR_MONTH_FILTERS = [
|
|
2220
2233
|
'month-1',
|
|
@@ -2239,18 +2252,18 @@ const CALENDAR_QUARTER_FILTERS = [
|
|
|
2239
2252
|
function workSpreadsheetDynamicFilterMatcher(kind, cells, context) {
|
|
2240
2253
|
if ('above-average' === kind || 'below-average' === kind) return averageMatcher(kind, cells);
|
|
2241
2254
|
const month = CALENDAR_MONTH_FILTERS.indexOf(kind);
|
|
2242
|
-
if (month >= 0) return (cell)=>spreadsheetDateParts(cell)?.month === month;
|
|
2255
|
+
if (month >= 0) return (cell)=>spreadsheetDateParts(cell, context.dateSystem)?.month === month;
|
|
2243
2256
|
const quarter = CALENDAR_QUARTER_FILTERS.indexOf(kind);
|
|
2244
|
-
if (quarter >= 0) return (cell)=>spreadsheetDateParts(cell)?.quarter === quarter;
|
|
2257
|
+
if (quarter >= 0) return (cell)=>spreadsheetDateParts(cell, context.dateSystem)?.quarter === quarter;
|
|
2245
2258
|
const bounds = relativeDateBounds(kind, context.now);
|
|
2246
2259
|
if (!bounds) return null;
|
|
2247
2260
|
return (cell)=>{
|
|
2248
|
-
const day =
|
|
2261
|
+
const day = spreadsheetDateCivilDay(cell, context.dateSystem);
|
|
2249
2262
|
return null !== day && day >= bounds.start && day < bounds.end;
|
|
2250
2263
|
};
|
|
2251
2264
|
}
|
|
2252
|
-
function workSpreadsheetCellIsDate(cell) {
|
|
2253
|
-
return null !==
|
|
2265
|
+
function workSpreadsheetCellIsDate(cell, dateSystem = '1900') {
|
|
2266
|
+
return null !== spreadsheetDateCivilDay(cell, dateSystem);
|
|
2254
2267
|
}
|
|
2255
2268
|
function averageMatcher(kind, cells) {
|
|
2256
2269
|
const average = finiteArithmeticMean(cells);
|
|
@@ -2284,7 +2297,7 @@ function relativeDateBounds(kind, now) {
|
|
|
2284
2297
|
const year = now.getFullYear();
|
|
2285
2298
|
if (!Number.isFinite(timestamp) || !Number.isSafeInteger(year) || year < 1900 || year > 9999) return null;
|
|
2286
2299
|
const month = now.getMonth();
|
|
2287
|
-
const today =
|
|
2300
|
+
const today = civilDayFromDateParts(year, month, now.getDate());
|
|
2288
2301
|
if ('yesterday' === kind) return dayBounds(today - 1);
|
|
2289
2302
|
if ('today' === kind) return dayBounds(today);
|
|
2290
2303
|
if ('tomorrow' === kind) return dayBounds(today + 1);
|
|
@@ -2292,65 +2305,64 @@ function relativeDateBounds(kind, now) {
|
|
|
2292
2305
|
if ('last-week' === kind) return rangeBounds(weekStart - 7, weekStart);
|
|
2293
2306
|
if ('this-week' === kind) return rangeBounds(weekStart, weekStart + 7);
|
|
2294
2307
|
if ('next-week' === kind) return rangeBounds(weekStart + 7, weekStart + 14);
|
|
2295
|
-
const monthStart =
|
|
2296
|
-
if ('last-month' === kind) return rangeBounds(
|
|
2297
|
-
if ('this-month' === kind) return rangeBounds(monthStart,
|
|
2298
|
-
if ('next-month' === kind) return rangeBounds(
|
|
2308
|
+
const monthStart = civilDayFromDateParts(year, month, 1);
|
|
2309
|
+
if ('last-month' === kind) return rangeBounds(civilDayFromDateParts(year, month - 1, 1), monthStart);
|
|
2310
|
+
if ('this-month' === kind) return rangeBounds(monthStart, civilDayFromDateParts(year, month + 1, 1));
|
|
2311
|
+
if ('next-month' === kind) return rangeBounds(civilDayFromDateParts(year, month + 1, 1), civilDayFromDateParts(year, month + 2, 1));
|
|
2299
2312
|
const quarterStartMonth = 3 * Math.floor(month / 3);
|
|
2300
|
-
const quarterStart =
|
|
2301
|
-
if ('last-quarter' === kind) return rangeBounds(
|
|
2302
|
-
if ('this-quarter' === kind) return rangeBounds(quarterStart,
|
|
2303
|
-
if ('next-quarter' === kind) return rangeBounds(
|
|
2304
|
-
const yearStart =
|
|
2305
|
-
if ('last-year' === kind) return rangeBounds(
|
|
2306
|
-
if ('this-year' === kind) return rangeBounds(yearStart,
|
|
2307
|
-
if ('next-year' === kind) return rangeBounds(
|
|
2313
|
+
const quarterStart = civilDayFromDateParts(year, quarterStartMonth, 1);
|
|
2314
|
+
if ('last-quarter' === kind) return rangeBounds(civilDayFromDateParts(year, quarterStartMonth - 3, 1), quarterStart);
|
|
2315
|
+
if ('this-quarter' === kind) return rangeBounds(quarterStart, civilDayFromDateParts(year, quarterStartMonth + 3, 1));
|
|
2316
|
+
if ('next-quarter' === kind) return rangeBounds(civilDayFromDateParts(year, quarterStartMonth + 3, 1), civilDayFromDateParts(year, quarterStartMonth + 6, 1));
|
|
2317
|
+
const yearStart = civilDayFromDateParts(year, 0, 1);
|
|
2318
|
+
if ('last-year' === kind) return rangeBounds(civilDayFromDateParts(year - 1, 0, 1), yearStart);
|
|
2319
|
+
if ('this-year' === kind) return rangeBounds(yearStart, civilDayFromDateParts(year + 1, 0, 1));
|
|
2320
|
+
if ('next-year' === kind) return rangeBounds(civilDayFromDateParts(year + 1, 0, 1), civilDayFromDateParts(year + 2, 0, 1));
|
|
2308
2321
|
if ('year-to-date' === kind) return rangeBounds(yearStart, today + 1);
|
|
2309
2322
|
return null;
|
|
2310
2323
|
}
|
|
2311
|
-
function spreadsheetDateParts(cell) {
|
|
2312
|
-
const day =
|
|
2324
|
+
function spreadsheetDateParts(cell, dateSystem = '1900') {
|
|
2325
|
+
const day = spreadsheetDateCivilDay(cell, dateSystem);
|
|
2313
2326
|
if (null === day) return null;
|
|
2314
|
-
const
|
|
2315
|
-
const date = new Date(EXCEL_1900_CIVIL_EPOCH_UTC + adjustedDay * MILLISECONDS_PER_DAY);
|
|
2327
|
+
const date = new Date(day * MILLISECONDS_PER_DAY);
|
|
2316
2328
|
const timestamp = date.getTime();
|
|
2317
2329
|
const year = date.getUTCFullYear();
|
|
2318
|
-
if (!Number.isFinite(timestamp) || year
|
|
2330
|
+
if (!Number.isFinite(timestamp) || year > 9999) return null;
|
|
2319
2331
|
const month = date.getUTCMonth();
|
|
2320
2332
|
return {
|
|
2321
2333
|
month,
|
|
2322
2334
|
quarter: Math.floor(month / 3)
|
|
2323
2335
|
};
|
|
2324
2336
|
}
|
|
2325
|
-
function
|
|
2326
|
-
const serial = spreadsheetDateCellSerial(cell);
|
|
2327
|
-
if (null === serial) return null;
|
|
2328
|
-
const rounded = Math.round(serial);
|
|
2329
|
-
const normalized = Math.abs(serial - rounded) <= SERIAL_INTEGER_EPSILON ? rounded : serial;
|
|
2330
|
-
const day = Math.floor(normalized);
|
|
2331
|
-
return day >= 1 && 60 !== day ? day : null;
|
|
2332
|
-
}
|
|
2333
|
-
function spreadsheetDateCellSerial(cell) {
|
|
2337
|
+
function spreadsheetDateCivilDay(cell, dateSystem = '1900') {
|
|
2334
2338
|
const value = spreadsheetCellRawValue(cell);
|
|
2335
2339
|
if (value instanceof Date) {
|
|
2336
2340
|
if (!Number.isFinite(value.getTime())) return null;
|
|
2337
|
-
const
|
|
2338
|
-
const
|
|
2339
|
-
return
|
|
2341
|
+
const year = value.getFullYear();
|
|
2342
|
+
const minimumYear = '1904' === dateSystem ? 1904 : 1900;
|
|
2343
|
+
return year >= minimumYear && year <= 9999 ? civilDayFromDateParts(year, value.getMonth(), value.getDate()) : null;
|
|
2340
2344
|
}
|
|
2341
|
-
|
|
2345
|
+
if ('number' != typeof value || !Number.isFinite(value) || cell?.ct?.t !== 'd') return null;
|
|
2346
|
+
const serial = value;
|
|
2347
|
+
const rounded = Math.round(serial);
|
|
2348
|
+
const normalized = Math.abs(serial - rounded) <= SERIAL_INTEGER_EPSILON ? rounded : serial;
|
|
2349
|
+
const day = Math.floor(normalized);
|
|
2350
|
+
if ('1904' === dateSystem) return day >= 0 ? (EXCEL_1904_EPOCH_UTC + day * MILLISECONDS_PER_DAY) / MILLISECONDS_PER_DAY : null;
|
|
2351
|
+
if (day < 1 || 60 === day) return null;
|
|
2352
|
+
const civilOffset = day > 60 ? day - 1 : day;
|
|
2353
|
+
return (EXCEL_1900_CIVIL_EPOCH_UTC + civilOffset * MILLISECONDS_PER_DAY) / MILLISECONDS_PER_DAY;
|
|
2342
2354
|
}
|
|
2343
2355
|
function spreadsheetNumericCellValue(cell) {
|
|
2344
|
-
if (null !== spreadsheetDateCellSerial(cell)) return null;
|
|
2345
2356
|
const value = spreadsheetCellRawValue(cell);
|
|
2357
|
+
if (value instanceof Date || cell?.ct?.t === 'd') return null;
|
|
2346
2358
|
return 'number' == typeof value && Number.isFinite(value) ? value : null;
|
|
2347
2359
|
}
|
|
2348
2360
|
function spreadsheetCellRawValue(cell) {
|
|
2349
2361
|
const candidate = cell;
|
|
2350
2362
|
return candidate?.v ?? candidate?.m;
|
|
2351
2363
|
}
|
|
2352
|
-
function
|
|
2353
|
-
return
|
|
2364
|
+
function civilDayFromDateParts(year, month, day) {
|
|
2365
|
+
return Date.UTC(year, month, day) / MILLISECONDS_PER_DAY;
|
|
2354
2366
|
}
|
|
2355
2367
|
function dayBounds(day) {
|
|
2356
2368
|
return {
|
package/dist/core.js
CHANGED
|
@@ -3464,12 +3464,16 @@ async function createWorkSpreadsheetBlob(artifact) {
|
|
|
3464
3464
|
const XLSX = await import("xlsx");
|
|
3465
3465
|
const workbook = XLSX.utils.book_new();
|
|
3466
3466
|
for (const sheet of content.sheets){
|
|
3467
|
-
const worksheet = createSparseXlsxWorksheet(sheet, XLSX);
|
|
3467
|
+
const worksheet = createSparseXlsxWorksheet(sheet, XLSX, content.dateSystem);
|
|
3468
3468
|
applySpreadsheetLayout(worksheet, sheet);
|
|
3469
3469
|
XLSX.utils.book_append_sheet(workbook, worksheet, sheet.name.slice(0, 31) || '工作表');
|
|
3470
3470
|
}
|
|
3471
3471
|
workbook.Workbook = {
|
|
3472
3472
|
...workbook.Workbook,
|
|
3473
|
+
WBProps: {
|
|
3474
|
+
...workbook.Workbook?.WBProps,
|
|
3475
|
+
date1904: '1904' === content.dateSystem
|
|
3476
|
+
},
|
|
3473
3477
|
Sheets: content.sheets.map((sheet)=>({
|
|
3474
3478
|
name: sheet.name.slice(0, 31) || '工作表',
|
|
3475
3479
|
Hidden: sheet.hide ? 1 : 0
|
|
@@ -3492,7 +3496,7 @@ async function createWorkSpreadsheetBlob(artifact) {
|
|
|
3492
3496
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
3493
3497
|
});
|
|
3494
3498
|
}
|
|
3495
|
-
function createSparseXlsxWorksheet(sheet, XLSX) {
|
|
3499
|
+
function createSparseXlsxWorksheet(sheet, XLSX, dateSystem = '1900') {
|
|
3496
3500
|
const data = sheet.data ?? [];
|
|
3497
3501
|
const worksheet = {
|
|
3498
3502
|
'!a3sSparse': true
|
|
@@ -3511,7 +3515,7 @@ function createSparseXlsxWorksheet(sheet, XLSX) {
|
|
|
3511
3515
|
const style = xlsxCellStyle(cell);
|
|
3512
3516
|
const comment = exportXlsxCellComment(cell.ps);
|
|
3513
3517
|
const hyperlink = sheet.hyperlink?.[`${rowIndex}_${columnIndex}`];
|
|
3514
|
-
const target = xlsxCellObject(cell, rowIndex, columnIndex, sheet) ?? (style || comment || hyperlink ? {
|
|
3518
|
+
const target = xlsxCellObject(cell, rowIndex, columnIndex, sheet, dateSystem) ?? (style || comment || hyperlink ? {
|
|
3515
3519
|
t: 's',
|
|
3516
3520
|
v: ''
|
|
3517
3521
|
} : null);
|
|
@@ -3538,14 +3542,30 @@ function createSparseXlsxWorksheet(sheet, XLSX) {
|
|
|
3538
3542
|
});
|
|
3539
3543
|
return worksheet;
|
|
3540
3544
|
}
|
|
3541
|
-
function xlsxCellObject(cell, row, column, sheet) {
|
|
3542
|
-
if (cell.f)
|
|
3545
|
+
function xlsxCellObject(cell, row, column, sheet, dateSystem) {
|
|
3546
|
+
if (cell.f) {
|
|
3547
|
+
const formula = createXlsxFormulaCell(cell, row, column, sheet);
|
|
3548
|
+
if (formula.v instanceof Date && Number.isFinite(formula.v.getTime())) return {
|
|
3549
|
+
...formula,
|
|
3550
|
+
t: 'n',
|
|
3551
|
+
v: spreadsheetDateSerial(formula.v, dateSystem)
|
|
3552
|
+
};
|
|
3553
|
+
if ('d' === formula.t && 'number' == typeof formula.v) return {
|
|
3554
|
+
...formula,
|
|
3555
|
+
t: 'n'
|
|
3556
|
+
};
|
|
3557
|
+
return formula;
|
|
3558
|
+
}
|
|
3543
3559
|
if (cell.ct?.t === 'e') return createXlsxErrorCell(cell);
|
|
3544
3560
|
const value = xlsxRichTextCellText(cell) ?? cell.v ?? cell.m;
|
|
3545
3561
|
if (null == value) return cell.ps ? {
|
|
3546
3562
|
t: 's',
|
|
3547
3563
|
v: ''
|
|
3548
3564
|
} : null;
|
|
3565
|
+
if (value instanceof Date && Number.isFinite(value.getTime())) return {
|
|
3566
|
+
t: 'n',
|
|
3567
|
+
v: spreadsheetDateSerial(value, dateSystem)
|
|
3568
|
+
};
|
|
3549
3569
|
if ('boolean' == typeof value) return {
|
|
3550
3570
|
t: 'b',
|
|
3551
3571
|
v: value
|
|
@@ -3559,6 +3579,15 @@ function xlsxCellObject(cell, row, column, sheet) {
|
|
|
3559
3579
|
v: String(value)
|
|
3560
3580
|
};
|
|
3561
3581
|
}
|
|
3582
|
+
function spreadsheetDateSerial(value, dateSystem) {
|
|
3583
|
+
const year = value.getFullYear();
|
|
3584
|
+
const month = value.getMonth();
|
|
3585
|
+
const day = value.getDate();
|
|
3586
|
+
const time = (3600000 * value.getHours() + 60000 * value.getMinutes() + 1000 * value.getSeconds() + value.getMilliseconds()) / 86400000;
|
|
3587
|
+
if ('1904' === dateSystem) return (Date.UTC(year, month, day) - Date.UTC(1904, 0, 1)) / 86400000 + time;
|
|
3588
|
+
const civilDay = (Date.UTC(year, month, day) - Date.UTC(1899, 11, 31)) / 86400000;
|
|
3589
|
+
return civilDay + (civilDay >= 60 ? 1 : 0) + time;
|
|
3590
|
+
}
|
|
3562
3591
|
function applySpreadsheetLayout(worksheet, sheet) {
|
|
3563
3592
|
const merges = Object.values(sheet.config?.merge ?? {});
|
|
3564
3593
|
if (merges.length) worksheet['!merges'] = merges.map((merge)=>({
|
|
@@ -4101,7 +4130,7 @@ async function importWorkSpreadsheetFile(file, extension, context) {
|
|
|
4101
4130
|
const initialRequiresCellStyles = archive ? archive.has('xl/styles.xml') : true;
|
|
4102
4131
|
let readOptions = {
|
|
4103
4132
|
type: 'array',
|
|
4104
|
-
cellDates:
|
|
4133
|
+
cellDates: false,
|
|
4105
4134
|
cellFormula: true,
|
|
4106
4135
|
cellHTML: false,
|
|
4107
4136
|
cellStyles: initialRequiresCellStyles,
|
|
@@ -4185,6 +4214,11 @@ async function importWorkSpreadsheetFile(file, extension, context) {
|
|
|
4185
4214
|
]);
|
|
4186
4215
|
const XLSX = usesPlainXlsxFastPath ? null : await loadSheetJs();
|
|
4187
4216
|
const [sheetFeatures, formulaFeatures, pivotFeatures] = importedFeatures;
|
|
4217
|
+
const dateSystem = workbook.Workbook?.WBProps?.date1904 ? '1904' : '1900';
|
|
4218
|
+
const dynamicFilterContext = {
|
|
4219
|
+
dateSystem,
|
|
4220
|
+
now: new Date()
|
|
4221
|
+
};
|
|
4188
4222
|
await context.controller.checkpoint('parsing', 0.2);
|
|
4189
4223
|
await context.controller.checkpoint('parsing', 0.4);
|
|
4190
4224
|
const conversionStartedAt = spreadsheetImportNow();
|
|
@@ -4301,7 +4335,7 @@ async function importWorkSpreadsheetFile(file, extension, context) {
|
|
|
4301
4335
|
charts: features?.charts.length ? xlsxWorksheetChartsToSheet(features.charts, config) : void 0,
|
|
4302
4336
|
tables: features?.tables.length ? features.tables : void 0,
|
|
4303
4337
|
formulaMetadata
|
|
4304
|
-
}, features?.autoFilterCriteria ?? []));
|
|
4338
|
+
}, features?.autoFilterCriteria ?? [], dynamicFilterContext));
|
|
4305
4339
|
await context.controller.checkpoint('parsing', sheetProgressStart + sheetProgressSize);
|
|
4306
4340
|
}
|
|
4307
4341
|
const workbookMetadata = importXlsxDefinedNames(workbook, sheets);
|
|
@@ -4330,6 +4364,9 @@ async function importWorkSpreadsheetFile(file, extension, context) {
|
|
|
4330
4364
|
artifact.content = applyImportedXlsxPivotTables({
|
|
4331
4365
|
type: 'spreadsheet',
|
|
4332
4366
|
sheets,
|
|
4367
|
+
...'1904' === dateSystem ? {
|
|
4368
|
+
dateSystem
|
|
4369
|
+
} : {},
|
|
4333
4370
|
calculation: formulaFeatures?.calculation,
|
|
4334
4371
|
...workbookMetadata,
|
|
4335
4372
|
pageBreaks: pageBreaks.length ? pageBreaks : void 0,
|
|
@@ -4447,7 +4484,7 @@ function fortuneSheetConfig(worksheet) {
|
|
|
4447
4484
|
}
|
|
4448
4485
|
function fortuneCellFromXlsx(source, row, column, sheetId, hyperlink, comment, XLSX, directStyle) {
|
|
4449
4486
|
const cell = {
|
|
4450
|
-
...fortuneCellStyle(source),
|
|
4487
|
+
...fortuneCellStyle(source, XLSX),
|
|
4451
4488
|
...directStyle
|
|
4452
4489
|
};
|
|
4453
4490
|
const orientation = spreadsheetExplicitTextOrientationFromCell(cell);
|
|
@@ -4476,7 +4513,7 @@ function fortuneCellFromXlsx(source, row, column, sheetId, hyperlink, comment, X
|
|
|
4476
4513
|
function spreadsheetCellKey(row, column) {
|
|
4477
4514
|
return `${row}_${column}`;
|
|
4478
4515
|
}
|
|
4479
|
-
function fortuneCellStyle(source) {
|
|
4516
|
+
function fortuneCellStyle(source, XLSX) {
|
|
4480
4517
|
const style = source.s;
|
|
4481
4518
|
const font = style && 'object' == typeof style ? style.font : void 0;
|
|
4482
4519
|
const fill = style && 'object' == typeof style ? style.fill : void 0;
|
|
@@ -4504,10 +4541,19 @@ function fortuneCellStyle(source) {
|
|
|
4504
4541
|
if (orientationStyle) Object.assign(target, orientationStyle);
|
|
4505
4542
|
if (source.z && 'General' !== source.z || 'e' === source.t) target.ct = {
|
|
4506
4543
|
fa: source.z && 'General' !== source.z ? String(source.z) : void 0,
|
|
4507
|
-
t: source.t
|
|
4544
|
+
t: xlsxCellIsDate(source, XLSX) ? 'd' : source.t
|
|
4508
4545
|
};
|
|
4509
4546
|
return target;
|
|
4510
4547
|
}
|
|
4548
|
+
function xlsxCellIsDate(source, XLSX) {
|
|
4549
|
+
if ('d' === source.t) return true;
|
|
4550
|
+
if ('n' !== source.t || !source.z || 'General' === source.z) return false;
|
|
4551
|
+
try {
|
|
4552
|
+
return Boolean(XLSX.SSF.is_date(String(source.z)));
|
|
4553
|
+
} catch {
|
|
4554
|
+
return false;
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4511
4557
|
function fortuneCellDisplayText(source, XLSX) {
|
|
4512
4558
|
if (void 0 !== source.w) return source.w;
|
|
4513
4559
|
if (void 0 === source.v || null === source.v) return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { WorkSpreadsheetSheet } from '../work-types';
|
|
1
|
+
import type { WorkSpreadsheetDateSystem, WorkSpreadsheetSheet } from '../work-types';
|
|
2
2
|
export declare const SPREADSHEET_AUTO_FILTER_MENU_ITEMS: string[];
|
|
3
|
-
export declare function enhanceSpreadsheetAutoFilterSurface(container: HTMLElement, sheet: WorkSpreadsheetSheet | undefined, invoker: HTMLElement | null): HTMLElement | null;
|
|
3
|
+
export declare function enhanceSpreadsheetAutoFilterSurface(container: HTMLElement, sheet: WorkSpreadsheetSheet | undefined, invoker: HTMLElement | null, dateSystem?: WorkSpreadsheetDateSystem): HTMLElement | null;
|
|
4
4
|
export declare function focusSpreadsheetAutoFilterMenu(container: HTMLElement): void;
|
|
5
5
|
export declare function spreadsheetAutoFilterMenuFocusable(menu: HTMLElement): HTMLElement[];
|
|
6
6
|
export declare function spreadsheetAutoFilterTrigger(target: EventTarget | null): HTMLElement | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Selection } from '@fortune-sheet/core';
|
|
2
|
-
import type { WorkSpreadsheetFilterCriteria, WorkSpreadsheetContent, WorkSpreadsheetSheet } from '../work-types';
|
|
2
|
+
import type { WorkSpreadsheetDateSystem, WorkSpreadsheetFilterCriteria, WorkSpreadsheetContent, WorkSpreadsheetSheet } from '../work-types';
|
|
3
3
|
import { type WorkSpreadsheetAutoFilterRange } from '../work-spreadsheet-auto-filter';
|
|
4
4
|
import { type WorkSpreadsheetDynamicFilterContext } from '../work-spreadsheet-dynamic-filter';
|
|
5
5
|
export type SpreadsheetAutoFilterRange = WorkSpreadsheetAutoFilterRange;
|
|
@@ -16,7 +16,7 @@ export declare function spreadsheetSelectionOrCurrentRegion(sheet: WorkSpreadshe
|
|
|
16
16
|
export declare function spreadsheetAutoFilterHeaderColumn(sheet: WorkSpreadsheetSheet | undefined, selection: Selection): number | null;
|
|
17
17
|
export declare function toggleSpreadsheetAutoFilter(content: WorkSpreadsheetContent, sheetId: string, selection: Selection): WorkSpreadsheetContent | null;
|
|
18
18
|
export declare function spreadsheetAutoFilterCriteria(sheet: WorkSpreadsheetSheet | undefined, column: number): WorkSpreadsheetFilterCriteria | null;
|
|
19
|
-
export declare function spreadsheetAutoFilterColumnIsNumeric(sheet: WorkSpreadsheetSheet, range: SpreadsheetAutoFilterRange, column: number): boolean;
|
|
20
|
-
export declare function spreadsheetAutoFilterColumnIsDate(sheet: WorkSpreadsheetSheet, range: SpreadsheetAutoFilterRange, column: number): boolean;
|
|
19
|
+
export declare function spreadsheetAutoFilterColumnIsNumeric(sheet: WorkSpreadsheetSheet, range: SpreadsheetAutoFilterRange, column: number, dateSystem?: WorkSpreadsheetDateSystem): boolean;
|
|
20
|
+
export declare function spreadsheetAutoFilterColumnIsDate(sheet: WorkSpreadsheetSheet, range: SpreadsheetAutoFilterRange, column: number, dateSystem?: WorkSpreadsheetDateSystem): boolean;
|
|
21
21
|
export declare function applySpreadsheetAutoFilterCriteria(content: WorkSpreadsheetContent, sheetId: string, column: number, criteria: WorkSpreadsheetFilterCriteria, context?: WorkSpreadsheetDynamicFilterContext): WorkSpreadsheetContent | null;
|
|
22
22
|
export declare function clearSpreadsheetAutoFilterCriteria(content: WorkSpreadsheetContent, sheetId: string, column: number): WorkSpreadsheetContent | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Cell } from '@fortune-sheet/core';
|
|
2
|
+
import type { WorkSpreadsheetDateSystem } from '../work-types';
|
|
2
3
|
import type { SpreadsheetCommandContext, SpreadsheetEditorCommands } from './spreadsheet-command-controller';
|
|
3
4
|
import { type OfficeEditorExtension } from './office-editor-extension';
|
|
4
5
|
export type SpreadsheetDateTimeKind = 'date' | 'time';
|
|
@@ -10,5 +11,5 @@ export interface SpreadsheetDateTimeEntry {
|
|
|
10
11
|
formulaBarValue: string;
|
|
11
12
|
value: number;
|
|
12
13
|
}
|
|
13
|
-
export declare function spreadsheetDateTimeEntry(kind: SpreadsheetDateTimeKind, now: Date): SpreadsheetDateTimeEntry | null;
|
|
14
|
+
export declare function spreadsheetDateTimeEntry(kind: SpreadsheetDateTimeKind, now: Date, dateSystem?: WorkSpreadsheetDateSystem): SpreadsheetDateTimeEntry | null;
|
|
14
15
|
export declare function createSpreadsheetDateTimeExtension(): OfficeEditorExtension<SpreadsheetCommandContext, SpreadsheetEditorCommands>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Cell, Op, Selection } from '@fortune-sheet/core';
|
|
2
|
-
import type { WorkSpreadsheetContent } from '../work-types';
|
|
2
|
+
import type { WorkSpreadsheetContent, WorkSpreadsheetDateSystem } from '../work-types';
|
|
3
3
|
import type { OfficeSelectOption } from './office-select';
|
|
4
4
|
export interface SpreadsheetSelectionSummary {
|
|
5
5
|
average: number | null;
|
|
@@ -21,7 +21,7 @@ export declare function spreadsheetFontSizeOptions(current: number | undefined):
|
|
|
21
21
|
export declare function spreadsheetFontFamilyOptions(current: string | undefined): OfficeSelectOption[];
|
|
22
22
|
export declare function spreadsheetSheetsWithFiniteSelections(sheets: WorkSpreadsheetContent['sheets']): WorkSpreadsheetContent['sheets'];
|
|
23
23
|
export declare function spreadsheetSheetsForFortune(sheets: WorkSpreadsheetContent['sheets']): WorkSpreadsheetContent['sheets'];
|
|
24
|
-
export declare function spreadsheetSheetsFromFortune(sheets: WorkSpreadsheetContent['sheets'], sourceSheets: WorkSpreadsheetContent['sheets'], operations?: readonly Op[]): WorkSpreadsheetContent['sheets'];
|
|
24
|
+
export declare function spreadsheetSheetsFromFortune(sheets: WorkSpreadsheetContent['sheets'], sourceSheets: WorkSpreadsheetContent['sheets'], operations?: readonly Op[], dateSystem?: WorkSpreadsheetDateSystem): WorkSpreadsheetContent['sheets'];
|
|
25
25
|
export declare function finiteSpreadsheetSelection(selection: Selection | undefined): Selection;
|
|
26
26
|
export declare function sameSpreadsheetWorkbookState(changed: WorkSpreadsheetContent['sheets'], rendered: WorkSpreadsheetContent['sheets']): boolean;
|
|
27
27
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Op } from '@fortune-sheet/core';
|
|
2
|
+
import type { WorkSpreadsheetDynamicFilterContext } from '../work-spreadsheet-dynamic-filter';
|
|
2
3
|
import type { WorkSpreadsheetContent, WorkSpreadsheetSheet } from '../work-types';
|
|
3
4
|
import type { SpreadsheetCellRange } from './spreadsheet-cell-range';
|
|
4
|
-
export declare function reconcileSpreadsheetFiltersAfterFortune(sheets: WorkSpreadsheetContent['sheets'], sourceSheets: WorkSpreadsheetContent['sheets'], operations?: readonly Op[]): WorkSpreadsheetContent['sheets'];
|
|
5
|
-
export declare function reconcileSpreadsheetFiltersAfterSort(sheet: WorkSpreadsheetSheet, source: WorkSpreadsheetSheet, range: SpreadsheetCellRange, sourceIndexes: readonly number[]): WorkSpreadsheetSheet | null;
|
|
5
|
+
export declare function reconcileSpreadsheetFiltersAfterFortune(sheets: WorkSpreadsheetContent['sheets'], sourceSheets: WorkSpreadsheetContent['sheets'], operations?: readonly Op[], context?: WorkSpreadsheetDynamicFilterContext): WorkSpreadsheetContent['sheets'];
|
|
6
|
+
export declare function reconcileSpreadsheetFiltersAfterSort(sheet: WorkSpreadsheetSheet, source: WorkSpreadsheetSheet, range: SpreadsheetCellRange, sourceIndexes: readonly number[], context?: WorkSpreadsheetDynamicFilterContext): WorkSpreadsheetSheet | null;
|
|
6
7
|
export declare function spreadsheetFilterReconciliationIsBounded(sheet: WorkSpreadsheetSheet): boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Cell } from '@fortune-sheet/core';
|
|
2
|
-
import type { WorkSpreadsheetDynamicFilter } from './work-types';
|
|
2
|
+
import type { WorkSpreadsheetDateSystem, WorkSpreadsheetDynamicFilter } from './work-types';
|
|
3
3
|
export interface WorkSpreadsheetDynamicFilterContext {
|
|
4
|
+
dateSystem?: WorkSpreadsheetDateSystem;
|
|
4
5
|
now: Date;
|
|
5
6
|
}
|
|
6
7
|
export declare function workSpreadsheetDynamicFilterMatcher(kind: WorkSpreadsheetDynamicFilter, cells: Iterable<Cell | null>, context: WorkSpreadsheetDynamicFilterContext): ((cell: Cell | null) => boolean) | null;
|
|
7
|
-
export declare function workSpreadsheetCellIsDate(cell: Cell | null): boolean;
|
|
8
|
+
export declare function workSpreadsheetCellIsDate(cell: Cell | null, dateSystem?: WorkSpreadsheetDateSystem): boolean;
|
|
@@ -182,9 +182,12 @@ export interface WorkDocumentComment {
|
|
|
182
182
|
resolved: boolean;
|
|
183
183
|
replies?: WorkDocumentCommentReply[];
|
|
184
184
|
}
|
|
185
|
+
export type WorkSpreadsheetDateSystem = '1900' | '1904';
|
|
185
186
|
export interface WorkSpreadsheetContent {
|
|
186
187
|
type: 'spreadsheet';
|
|
187
188
|
sheets: WorkSpreadsheetSheet[];
|
|
189
|
+
/** Omitted values use the Excel-compatible 1900 date system. */
|
|
190
|
+
dateSystem?: WorkSpreadsheetDateSystem;
|
|
188
191
|
calculation?: WorkSpreadsheetCalculationSettings;
|
|
189
192
|
namedRanges?: WorkSpreadsheetNamedRange[];
|
|
190
193
|
printAreas?: WorkSpreadsheetPrintArea[];
|
package/dist/office-kernel.wasm
CHANGED
|
Binary file
|
|
@@ -1574,8 +1574,10 @@ number as a date. Relative weeks begin on Sunday; calendar month and quarter
|
|
|
1574
1574
|
families classify independently of year. Column profiling is cached per
|
|
1575
1575
|
immutable sheet and keeps typed dates out of numeric rank actions. The editor
|
|
1576
1576
|
uses the current local clock, while model tests inject a deterministic clock.
|
|
1577
|
-
The controlled model
|
|
1578
|
-
1904
|
|
1577
|
+
The controlled model carries one workbook-owned `dateSystem`; omission means
|
|
1578
|
+
1900 and imported 1904 workbooks retain the explicit alternate epoch. Numeric
|
|
1579
|
+
date serials remain numeric through import, filtering, controlled remounts,
|
|
1580
|
+
collaboration, export, and reopen, including valid 1904 serial zero.
|
|
1579
1581
|
|
|
1580
1582
|
The same closed `WorkSpreadsheetFilterCriteria` union now serves worksheet and
|
|
1581
1583
|
table native OOXML. Package scan and import read only the worksheet root's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a3s-lab/office",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.5",
|
|
4
4
|
"description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"office",
|
|
@@ -223,6 +223,8 @@
|
|
|
223
223
|
"test:e2e:spreadsheet-diagonal-borders:check": "a3s-test check tests/e2e/spreadsheet-diagonal-borders.acl --json",
|
|
224
224
|
"test:e2e:spreadsheet-date-time": "a3s-test run tests/e2e/spreadsheet-date-time.acl --json",
|
|
225
225
|
"test:e2e:spreadsheet-date-time:check": "a3s-test check tests/e2e/spreadsheet-date-time.acl --json",
|
|
226
|
+
"test:e2e:spreadsheet-1904-date-system": "A3S_TEST_SUITE=tests/e2e/spreadsheet-1904-date-system.acl bash scripts/run-a3s-test-web-gate.sh",
|
|
227
|
+
"test:e2e:spreadsheet-1904-date-system:check": "a3s-test check tests/e2e/spreadsheet-1904-date-system.acl --json",
|
|
226
228
|
"test:e2e:spreadsheet-copy-from-above": "A3S_TEST_SUITE=tests/e2e/spreadsheet-copy-from-above.acl bash scripts/run-a3s-test-web-gate.sh",
|
|
227
229
|
"test:e2e:spreadsheet-copy-from-above:check": "a3s-test check tests/e2e/spreadsheet-copy-from-above.acl --json",
|
|
228
230
|
"test:e2e:spreadsheet-font-dialog-shortcuts": "A3S_TEST_SUITE=tests/e2e/spreadsheet-font-dialog-shortcuts.acl bash scripts/run-a3s-test-web-gate.sh",
|