@rowsncolumns/spreadsheet-state 29.1.4 → 29.1.7
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/dist/cjs/api/circular-reference.d.ts +31 -0
- package/dist/cjs/api/circular-reference.d.ts.map +1 -0
- package/dist/cjs/api/circular-reference.js +85 -0
- package/dist/cjs/api/copy-paste.d.ts +6 -0
- package/dist/cjs/api/copy-paste.d.ts.map +1 -1
- package/dist/cjs/api/copy-paste.js +24 -15
- package/dist/cjs/api/datatype.d.ts +2 -0
- package/dist/cjs/api/datatype.d.ts.map +1 -1
- package/dist/cjs/api/datatype.js +61 -35
- package/dist/cjs/api/date.d.ts.map +1 -1
- package/dist/cjs/api/date.js +23 -14
- package/dist/cjs/api/decimals.d.ts +10 -0
- package/dist/cjs/api/decimals.d.ts.map +1 -1
- package/dist/cjs/api/decimals.js +34 -11
- package/dist/cjs/api/dependency.d.ts +19 -1
- package/dist/cjs/api/dependency.d.ts.map +1 -1
- package/dist/cjs/api/dependency.js +30 -14
- package/dist/cjs/api/filter-condition.d.ts +58 -0
- package/dist/cjs/api/filter-condition.d.ts.map +1 -0
- package/dist/cjs/api/filter-condition.js +157 -0
- package/dist/cjs/api/formula.d.ts.map +1 -1
- package/dist/cjs/api/formula.js +9 -7
- package/dist/cjs/api/image-embed.d.ts +41 -0
- package/dist/cjs/api/image-embed.d.ts.map +1 -0
- package/dist/cjs/api/image-embed.js +55 -0
- package/dist/cjs/api/immer-utils.d.ts.map +1 -1
- package/dist/cjs/api/immer-utils.js +43 -15
- package/dist/cjs/api/named-range-structural.d.ts +29 -3
- package/dist/cjs/api/named-range-structural.d.ts.map +1 -1
- package/dist/cjs/api/named-range-structural.js +36 -11
- package/dist/cjs/api/parse-html.d.ts.map +1 -1
- package/dist/cjs/api/parse-html.js +11 -5
- package/dist/cjs/api/structural-formula-update.d.ts +20 -1
- package/dist/cjs/api/structural-formula-update.d.ts.map +1 -1
- package/dist/cjs/api/structural-formula-update.js +264 -90
- package/dist/cjs/api/structural-guards.d.ts +11 -0
- package/dist/cjs/api/structural-guards.d.ts.map +1 -1
- package/dist/cjs/api/structural-guards.js +45 -1
- package/dist/cjs/api/structural-table-refs.d.ts +15 -2
- package/dist/cjs/api/structural-table-refs.d.ts.map +1 -1
- package/dist/cjs/api/structural-table-refs.js +79 -35
- package/dist/cjs/commands.d.ts +17 -3
- package/dist/cjs/commands.d.ts.map +1 -1
- package/dist/cjs/components/editors/conditional-format-rule-editor.d.ts.map +1 -1
- package/dist/cjs/components/editors/conditional-format-rule-editor.js +19 -7
- package/dist/cjs/constants.d.ts +4 -1
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +4 -0
- package/dist/cjs/helpers/sheet-title.d.ts +2 -1
- package/dist/cjs/helpers/sheet-title.d.ts.map +1 -1
- package/dist/cjs/helpers/sheet-title.js +3 -1
- package/dist/cjs/hooks/use-can-edit-cellrange.d.ts +1 -1
- package/dist/cjs/hooks/use-can-edit-cellrange.d.ts.map +1 -1
- package/dist/cjs/hooks/use-can-edit-cellrange.js +1 -1
- package/dist/cjs/hooks/use-on-calculation-success.d.ts +5 -1
- package/dist/cjs/hooks/use-on-calculation-success.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-calculation-success.js +20 -3
- package/dist/cjs/hooks/use-on-copy.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-copy.js +23 -25
- package/dist/cjs/hooks/use-on-create-sheet.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-create-sheet.js +11 -1
- package/dist/cjs/hooks/use-on-delete.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-delete.js +15 -3
- package/dist/cjs/hooks/use-on-drag-n-drop.d.ts +5 -0
- package/dist/cjs/hooks/use-on-drag-n-drop.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-drag-n-drop.js +6 -5
- package/dist/cjs/hooks/use-on-filter-table.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-filter-table.js +6 -1
- package/dist/cjs/hooks/use-on-find-replace.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-find-replace.js +111 -23
- package/dist/cjs/hooks/use-on-insert-image.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-insert-image.js +20 -33
- package/dist/cjs/hooks/use-on-paste.d.ts +2 -1
- package/dist/cjs/hooks/use-on-paste.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-paste.js +53 -24
- package/dist/cjs/hooks/use-on-rename-sheet.d.ts +3 -1
- package/dist/cjs/hooks/use-on-rename-sheet.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-rename-sheet.js +19 -9
- package/dist/cjs/hooks/use-on-sort-column.d.ts +7 -1
- package/dist/cjs/hooks/use-on-sort-column.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-sort-column.js +25 -8
- package/dist/cjs/hooks/use-on-sort-range.d.ts +16 -2
- package/dist/cjs/hooks/use-on-sort-range.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-sort-range.js +72 -40
- package/dist/cjs/hooks/use-on-sort-table.d.ts +7 -2
- package/dist/cjs/hooks/use-on-sort-table.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-sort-table.js +25 -4
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts +5 -3
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/cjs/hooks/use-spreadsheet-state.js +7 -0
- package/dist/cjs/hooks/utils/sort-rows.d.ts +15 -0
- package/dist/cjs/hooks/utils/sort-rows.d.ts.map +1 -1
- package/dist/cjs/hooks/utils/sort-rows.js +16 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/interface/delete-column.d.ts.map +1 -1
- package/dist/cjs/interface/delete-column.js +254 -101
- package/dist/cjs/interface/delete-row.d.ts.map +1 -1
- package/dist/cjs/interface/delete-row.js +233 -81
- package/dist/cjs/interface/duplicate-sheet.d.ts.map +1 -1
- package/dist/cjs/interface/duplicate-sheet.js +4 -9
- package/dist/cjs/interface/insert-column.d.ts.map +1 -1
- package/dist/cjs/interface/insert-column.js +1 -1
- package/dist/cjs/interface/insert-row.d.ts.map +1 -1
- package/dist/cjs/interface/insert-row.js +1 -1
- package/dist/cjs/interface/new-sheet.d.ts.map +1 -1
- package/dist/cjs/interface/new-sheet.js +6 -13
- package/dist/cjs/sheet-cell.d.ts +9 -0
- package/dist/cjs/sheet-cell.d.ts.map +1 -1
- package/dist/cjs/sheet-cell.js +46 -9
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/api/circular-reference.d.ts +31 -0
- package/dist/esm/api/circular-reference.d.ts.map +1 -0
- package/dist/esm/api/circular-reference.js +55 -0
- package/dist/esm/api/copy-paste.d.ts +6 -0
- package/dist/esm/api/copy-paste.d.ts.map +1 -1
- package/dist/esm/api/copy-paste.js +26 -14
- package/dist/esm/api/datatype.d.ts +2 -0
- package/dist/esm/api/datatype.d.ts.map +1 -1
- package/dist/esm/api/datatype.js +61 -35
- package/dist/esm/api/date.d.ts.map +1 -1
- package/dist/esm/api/date.js +23 -14
- package/dist/esm/api/decimals.d.ts +10 -0
- package/dist/esm/api/decimals.d.ts.map +1 -1
- package/dist/esm/api/decimals.js +31 -10
- package/dist/esm/api/dependency.d.ts +19 -1
- package/dist/esm/api/dependency.d.ts.map +1 -1
- package/dist/esm/api/dependency.js +15 -10
- package/dist/esm/api/filter-condition.d.ts +58 -0
- package/dist/esm/api/filter-condition.d.ts.map +1 -0
- package/dist/esm/api/filter-condition.js +146 -0
- package/dist/esm/api/formula.d.ts.map +1 -1
- package/dist/esm/api/formula.js +9 -7
- package/dist/esm/api/image-embed.d.ts +41 -0
- package/dist/esm/api/image-embed.d.ts.map +1 -0
- package/dist/esm/api/image-embed.js +52 -0
- package/dist/esm/api/immer-utils.d.ts.map +1 -1
- package/dist/esm/api/immer-utils.js +43 -14
- package/dist/esm/api/named-range-structural.d.ts +29 -3
- package/dist/esm/api/named-range-structural.d.ts.map +1 -1
- package/dist/esm/api/named-range-structural.js +35 -11
- package/dist/esm/api/parse-html.d.ts.map +1 -1
- package/dist/esm/api/parse-html.js +14 -5
- package/dist/esm/api/structural-formula-update.d.ts +20 -1
- package/dist/esm/api/structural-formula-update.d.ts.map +1 -1
- package/dist/esm/api/structural-formula-update.js +151 -55
- package/dist/esm/api/structural-guards.d.ts +11 -0
- package/dist/esm/api/structural-guards.d.ts.map +1 -1
- package/dist/esm/api/structural-guards.js +31 -0
- package/dist/esm/api/structural-table-refs.d.ts +15 -2
- package/dist/esm/api/structural-table-refs.d.ts.map +1 -1
- package/dist/esm/api/structural-table-refs.js +49 -18
- package/dist/esm/commands.d.ts +17 -3
- package/dist/esm/commands.d.ts.map +1 -1
- package/dist/esm/components/editors/conditional-format-rule-editor.d.ts.map +1 -1
- package/dist/esm/components/editors/conditional-format-rule-editor.js +18 -7
- package/dist/esm/constants.d.ts +4 -1
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +4 -0
- package/dist/esm/helpers/sheet-title.d.ts +2 -1
- package/dist/esm/helpers/sheet-title.d.ts.map +1 -1
- package/dist/esm/helpers/sheet-title.js +3 -1
- package/dist/esm/hooks/use-can-edit-cellrange.d.ts +1 -1
- package/dist/esm/hooks/use-can-edit-cellrange.d.ts.map +1 -1
- package/dist/esm/hooks/use-can-edit-cellrange.js +1 -1
- package/dist/esm/hooks/use-on-calculation-success.d.ts +5 -1
- package/dist/esm/hooks/use-on-calculation-success.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-calculation-success.js +19 -3
- package/dist/esm/hooks/use-on-copy.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-copy.js +17 -17
- package/dist/esm/hooks/use-on-create-sheet.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-create-sheet.js +11 -1
- package/dist/esm/hooks/use-on-delete.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-delete.js +15 -3
- package/dist/esm/hooks/use-on-drag-n-drop.d.ts +5 -0
- package/dist/esm/hooks/use-on-drag-n-drop.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-drag-n-drop.js +7 -4
- package/dist/esm/hooks/use-on-filter-table.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-filter-table.js +13 -2
- package/dist/esm/hooks/use-on-find-replace.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-find-replace.js +111 -23
- package/dist/esm/hooks/use-on-insert-image.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-insert-image.js +15 -25
- package/dist/esm/hooks/use-on-paste.d.ts +2 -1
- package/dist/esm/hooks/use-on-paste.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-paste.js +46 -15
- package/dist/esm/hooks/use-on-rename-sheet.d.ts +3 -1
- package/dist/esm/hooks/use-on-rename-sheet.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-rename-sheet.js +19 -9
- package/dist/esm/hooks/use-on-sort-column.d.ts +7 -1
- package/dist/esm/hooks/use-on-sort-column.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-sort-column.js +18 -1
- package/dist/esm/hooks/use-on-sort-range.d.ts +16 -2
- package/dist/esm/hooks/use-on-sort-range.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-sort-range.js +30 -3
- package/dist/esm/hooks/use-on-sort-table.d.ts +7 -2
- package/dist/esm/hooks/use-on-sort-table.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-sort-table.js +25 -4
- package/dist/esm/hooks/use-spreadsheet-state.d.ts +5 -3
- package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/esm/hooks/use-spreadsheet-state.js +7 -0
- package/dist/esm/hooks/utils/sort-rows.d.ts +15 -0
- package/dist/esm/hooks/utils/sort-rows.d.ts.map +1 -1
- package/dist/esm/hooks/utils/sort-rows.js +15 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -0
- package/dist/esm/interface/delete-column.d.ts.map +1 -1
- package/dist/esm/interface/delete-column.js +111 -80
- package/dist/esm/interface/delete-row.d.ts.map +1 -1
- package/dist/esm/interface/delete-row.js +91 -59
- package/dist/esm/interface/duplicate-sheet.d.ts.map +1 -1
- package/dist/esm/interface/duplicate-sheet.js +4 -9
- package/dist/esm/interface/insert-column.d.ts.map +1 -1
- package/dist/esm/interface/insert-column.js +1 -1
- package/dist/esm/interface/insert-row.d.ts.map +1 -1
- package/dist/esm/interface/insert-row.js +1 -1
- package/dist/esm/interface/new-sheet.d.ts.map +1 -1
- package/dist/esm/interface/new-sheet.js +6 -13
- package/dist/esm/sheet-cell.d.ts +9 -0
- package/dist/esm/sheet-cell.d.ts.map +1 -1
- package/dist/esm/sheet-cell.js +46 -7
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +9 -9
package/dist/esm/api/datatype.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getDefaultDateFormat, getDefaultDateTimeFormat, isAFormula, isNil, ssfFormat, ssfFormatColor, toDate, toSerial, } from "@rowsncolumns/utils";
|
|
1
|
+
import { getDefaultDateFormat, getDefaultDateTimeFormat, isAFormula, isNil, ssfFormat, ssfFormatColor, toDate, toSerial, toSerialFromParts, } from "@rowsncolumns/utils";
|
|
2
2
|
import { detectDateFormatPattern, detectDateTimeFormatPattern, isValidDate, isValidDateTime, toTimeSerial, validateDate, validateDateTime, } from "./date";
|
|
3
|
-
import { detectDecimalPattern } from "./decimals";
|
|
3
|
+
import { changeDecimals, detectDecimalPattern, getDecimalPlaces, hasThousandsGrouping, pattern_number_thousands, } from "./decimals";
|
|
4
4
|
import { buildGroupRegex, getGroupingInfo, getLocaleDigitMap, getLocaleSeparators, getPercentSignInfo, getSignInfo, } from "./intl";
|
|
5
5
|
export const pattern_percent = "0%";
|
|
6
6
|
export const pattern_percent_decimal = "0.00%";
|
|
@@ -11,6 +11,11 @@ export const pattern_percent_decimal = "0.00%";
|
|
|
11
11
|
// Excel renders the workbook identically to one it produced itself.
|
|
12
12
|
export const pattern_currency = '"$"#,##0_);("$"#,##0)';
|
|
13
13
|
export const pattern_currency_decimal = '"$"#,##0.00_);("$"#,##0.00)';
|
|
14
|
+
// Excel's built-in "Comma Style" / "Comma [0]" cell styles (numFmtId 43 / 41):
|
|
15
|
+
// accounting layout without a currency symbol — negatives in parentheses,
|
|
16
|
+
// zero as `-`, padded so the digits align in a column. Neither is red.
|
|
17
|
+
export const pattern_comma = '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)';
|
|
18
|
+
export const pattern_comma_rounded = '_(* #,##0_);_(* (#,##0);_(* "-"??_);_(@_)';
|
|
14
19
|
export const date_pattern = "mm/dd/yyyy";
|
|
15
20
|
export const time_pattern_base = "hh:mm:ss";
|
|
16
21
|
export const time_pattern = `hh:mm:ss AM/PM`;
|
|
@@ -403,11 +408,12 @@ export const createNumberValue = (value, numFormatType, locale, use1904, lenient
|
|
|
403
408
|
const isDate = numFormatType === "DATE";
|
|
404
409
|
const isDateTime = numFormatType === "DATE_TIME";
|
|
405
410
|
if (typeof value !== "boolean" && (isDate || isDateTime)) {
|
|
411
|
+
// A number is already a serial: applying the Date type only changes the
|
|
412
|
+
// format. Round-tripping it through `toDate`/`toSerial` moved Excel's
|
|
413
|
+
// phantom 1900-02-29 (60) onto the 28th (59) because no `Date` holds the
|
|
414
|
+
// 29th (rowsncolumns/spreadsheet#1026).
|
|
406
415
|
if (typeof value === "number") {
|
|
407
|
-
|
|
408
|
-
if (!isNil(dateValue) && dateValue instanceof Date) {
|
|
409
|
-
return toSerial(dateValue, use1904);
|
|
410
|
-
}
|
|
416
|
+
return value;
|
|
411
417
|
}
|
|
412
418
|
const dateValue = isDate
|
|
413
419
|
? validateDate(value, locale)
|
|
@@ -417,7 +423,7 @@ export const createNumberValue = (value, numFormatType, locale, use1904, lenient
|
|
|
417
423
|
if (isTimeOnly) {
|
|
418
424
|
return toTimeSerial(hour ?? 0, minute ?? 0, second ?? 0);
|
|
419
425
|
}
|
|
420
|
-
return
|
|
426
|
+
return toSerialFromParts(year, month, day, hour, minute, second, use1904);
|
|
421
427
|
}
|
|
422
428
|
}
|
|
423
429
|
return convertToNumber(value, locale, lenientGrouping);
|
|
@@ -463,6 +469,33 @@ export const detectNumberFormatType = (value, locale, lenientGrouping = true) =>
|
|
|
463
469
|
return "DATE_TIME";
|
|
464
470
|
}
|
|
465
471
|
};
|
|
472
|
+
/**
|
|
473
|
+
* Currency base pattern for a typed literal: the detected symbol in the position it was typed
|
|
474
|
+
* (`$30` → `"$"#,##0`, `12,50 €` → `#,##0"€"`), defaulting to a `$` prefix.
|
|
475
|
+
*/
|
|
476
|
+
const currencyBasePattern = (value) => {
|
|
477
|
+
const currencyInfo = extractCurrencySymbol(String(value));
|
|
478
|
+
const symbol = currencyInfo?.symbol || "$";
|
|
479
|
+
const position = currencyInfo?.position || "prefix";
|
|
480
|
+
return position === "suffix" ? `#,##0"${symbol}"` : `"${symbol}"#,##0`;
|
|
481
|
+
};
|
|
482
|
+
/**
|
|
483
|
+
* Currency / percent pattern whose decimal places are the ones the user TYPED
|
|
484
|
+
* (`$1,234.50` → `"$"#,##0.00`, `50.00%` → `0.00%`), not the parsed number's (`1234.5` → one
|
|
485
|
+
* place), so trailing zeros survive like in Excel. The base already carries the symbol and
|
|
486
|
+
* grouping, so only the decimals are appended. Ports Rust `detect_decimal_pattern_typed`; both
|
|
487
|
+
* `detectValueTypeAndPattern` (typed cell entry) and `detectNumberFormatPattern` go through it
|
|
488
|
+
* so the two never disagree on the same literal.
|
|
489
|
+
*/
|
|
490
|
+
const detectTypedDecimalPattern = (value, numberValue, basePattern, locale) => {
|
|
491
|
+
// A JS number carries no typed digits; its own decimal expansion is the best we have (a
|
|
492
|
+
// percent's `numberValue` is already /100, so it must not be the source for a numeric input).
|
|
493
|
+
const typed = typeof value === "string" || typeof value === "number"
|
|
494
|
+
? value
|
|
495
|
+
: numberValue ?? 0;
|
|
496
|
+
const decimalsLen = getDecimalPlaces(typed, locale);
|
|
497
|
+
return decimalsLen ? changeDecimals(basePattern, decimalsLen, true) : basePattern;
|
|
498
|
+
};
|
|
466
499
|
/**
|
|
467
500
|
* Detect pattern of a number format
|
|
468
501
|
* @param type
|
|
@@ -470,16 +503,10 @@ export const detectNumberFormatType = (value, locale, lenientGrouping = true) =>
|
|
|
470
503
|
*/
|
|
471
504
|
export const detectNumberFormatPattern = (value, numberValue, type, locale, use1904) => {
|
|
472
505
|
if (type === "PERCENT") {
|
|
473
|
-
return
|
|
506
|
+
return detectTypedDecimalPattern(value, numberValue, pattern_percent, locale);
|
|
474
507
|
}
|
|
475
508
|
if (type === "CURRENCY") {
|
|
476
|
-
|
|
477
|
-
const currencyInfo = extractCurrencySymbol(String(value));
|
|
478
|
-
const symbol = currencyInfo?.symbol || "$";
|
|
479
|
-
const position = currencyInfo?.position || "prefix";
|
|
480
|
-
// Create a pattern with the detected currency symbol in the correct position
|
|
481
|
-
const baseCurrencyPattern = position === "suffix" ? `#,##0"${symbol}"` : `"${symbol}"#,##0`;
|
|
482
|
-
return detectDecimalPattern(numberValue, baseCurrencyPattern, locale);
|
|
509
|
+
return detectTypedDecimalPattern(value, numberValue, currencyBasePattern(value), locale);
|
|
483
510
|
}
|
|
484
511
|
if (type === "DATE") {
|
|
485
512
|
return detectDateFormatPattern(value, locale) ?? date_pattern;
|
|
@@ -488,17 +515,20 @@ export const detectNumberFormatPattern = (value, numberValue, type, locale, use1
|
|
|
488
515
|
return detectDateTimeFormatPattern(value) ?? time_pattern;
|
|
489
516
|
}
|
|
490
517
|
if (type === "NUMBER") {
|
|
491
|
-
//
|
|
492
|
-
// `
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
518
|
+
// Excel leaves a General cell General for a plain number — `5`, `5.25`,
|
|
519
|
+
// `1.50`, `0.0000123` all display through General (`1.5`, `1.23E-05`)
|
|
520
|
+
// and stamp nothing, so a later entry in the same cell is never rounded
|
|
521
|
+
// through an inferred `#` / `#.00`. Only a thousands-grouped literal
|
|
522
|
+
// (`1,000`, `1,234.56`) is a formatting cue: it stamps `#,##0` plus the
|
|
523
|
+
// typed decimal places, and that stamp then sticks like any other format
|
|
524
|
+
// (Excel shows `0` for a later `0.25`). This rule is specific to the
|
|
525
|
+
// implicit NUMBER format; explicit CURRENCY / PERCENT formats keep their
|
|
526
|
+
// own base pattern (handled above), so `$0` stays `"$"#,##0`.
|
|
527
|
+
if (typeof value === "string" &&
|
|
528
|
+
hasThousandsGrouping(value, locale, numberValue)) {
|
|
529
|
+
return detectDecimalPattern(value, pattern_number_thousands, locale);
|
|
500
530
|
}
|
|
501
|
-
return
|
|
531
|
+
return "General";
|
|
502
532
|
}
|
|
503
533
|
return undefined;
|
|
504
534
|
};
|
|
@@ -559,18 +589,12 @@ export const detectValueTypeAndPattern = (value, locale, lenientGrouping = true,
|
|
|
559
589
|
}
|
|
560
590
|
// Check currency
|
|
561
591
|
if (isCurr) {
|
|
562
|
-
|
|
563
|
-
const currencyInfo = extractCurrencySymbol(String(value));
|
|
564
|
-
const symbol = currencyInfo?.symbol || "$";
|
|
565
|
-
const position = currencyInfo?.position || "prefix";
|
|
566
|
-
// Create a pattern with the detected currency symbol in the correct position
|
|
567
|
-
const baseCurrencyPattern = position === "suffix" ? `#,##0"${symbol}"` : `"${symbol}"#,##0`;
|
|
568
|
-
const pattern = detectDecimalPattern(numberValue, baseCurrencyPattern, locale);
|
|
592
|
+
const pattern = detectTypedDecimalPattern(value, numberValue, currencyBasePattern(value), locale);
|
|
569
593
|
return ["numberValue", "CURRENCY", numberValue, pattern];
|
|
570
594
|
}
|
|
571
595
|
// Numbers can be number, percent, date, datetime
|
|
572
596
|
if (isPercentage(value, locale, lenientGrouping)) {
|
|
573
|
-
const pattern =
|
|
597
|
+
const pattern = detectTypedDecimalPattern(value, numberValue, pattern_percent, locale);
|
|
574
598
|
const percentValue = convertToNumber(value, locale, lenientGrouping) / 100;
|
|
575
599
|
return ["numberValue", "PERCENT", percentValue, pattern];
|
|
576
600
|
}
|
|
@@ -594,14 +618,16 @@ export const detectValueTypeAndPattern = (value, locale, lenientGrouping = true,
|
|
|
594
618
|
const { parts: { year, month, day, hour, minute, second }, isTimeOnly, } = dateTimeValue;
|
|
595
619
|
const numberValue = isTimeOnly
|
|
596
620
|
? toTimeSerial(hour ?? 0, minute ?? 0, second ?? 0)
|
|
597
|
-
:
|
|
621
|
+
: toSerialFromParts(year, month, day, hour, minute, second, use1904);
|
|
598
622
|
return ["numberValue", "DATE_TIME", numberValue, dateTimeValue.format];
|
|
599
623
|
}
|
|
600
624
|
// Dates
|
|
601
625
|
const dateValue = validateDate(value, locale);
|
|
602
626
|
if (dateValue) {
|
|
603
627
|
const { parts: { year, month, day, hour, minute, second }, } = dateValue;
|
|
604
|
-
|
|
628
|
+
// Parts, not a `Date`: Excel's phantom 1900-02-29 must land on serial 60
|
|
629
|
+
// rather than roll over to March 1 (rowsncolumns/spreadsheet#1026).
|
|
630
|
+
const numberValue = toSerialFromParts(year, month, day, hour, minute, second, use1904);
|
|
605
631
|
return ["numberValue", "DATE", numberValue, dateValue.format];
|
|
606
632
|
}
|
|
607
633
|
return ["stringValue"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../api/date.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../api/date.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAYD,eAAO,MAAM,YAAY,GACvB,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EACnD,QAAQ,MAAM,GAAG,SAAS,KACzB,eAAe,GAAG,SAsCpB,CAAC;AAGF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,KAClD,eAAe,GAAG,SAsEpB,CAAC;AA6CF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,QAAQ,MAAM,WAGxE,CAAC;AAwBF,eAAO,MAAM,WAAW,GACtB,OAAO,GAAG,EACV,QAAQ,MAAM,GAAG,SAAS,KACzB,OAKF,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,GAAG,KAAG,OAK5C,CAAC;AAGF,eAAO,MAAM,uBAAuB,GAClC,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EACnD,QAAQ,MAAM,GAAG,SAAS,KACzB,MAAM,GAAG,SAKX,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,KAClD,MAAM,GAAG,SAKX,CAAC;AAYF,eAAO,MAAM,cAAc,cAA+B,CAAC;AAC3D,eAAO,MAAM,eAAe,cAAgC,CAAC;AAC7D,eAAO,MAAM,aAAa,cAA8B,CAAC"}
|
package/dist/esm/api/date.js
CHANGED
|
@@ -17,14 +17,17 @@ const TIME_FORMATS = {
|
|
|
17
17
|
FULL: "hh:mm:ss",
|
|
18
18
|
SHORT_WITH_MERIDIAN: "hh:mm AM/PM",
|
|
19
19
|
SHORT: "hh:mm",
|
|
20
|
-
// Excel "elapsed time"
|
|
21
|
-
// formatter to display the full elapsed-hours count without
|
|
22
|
-
// 24 — `[h]:mm:ss`
|
|
23
|
-
// `01:30:00`. Excel auto-applies
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
// Excel "elapsed time" (Duration) pattern. The square brackets around `h`
|
|
21
|
+
// tell the formatter to display the full elapsed-hours count without
|
|
22
|
+
// wrapping at 24 — `[h]:mm:ss` renders a 25.5-hour duration as `25:30:00`,
|
|
23
|
+
// not `01:30:00`. Excel auto-applies it when the typed hours reach 24
|
|
24
|
+
// (`24:00` is one day, `25:00` a duration) and always shows the seconds, so
|
|
25
|
+
// a typed `25:00` displays `25:00:00` (rowsncolumns/spreadsheet#1027) —
|
|
26
|
+
// the same pattern the Number menu's "Duration" item applies.
|
|
27
|
+
ELAPSED: "[h]:mm:ss",
|
|
27
28
|
};
|
|
29
|
+
// Typed hours at or past a full day are a Duration, never a clock time.
|
|
30
|
+
const ELAPSED_HOURS_MIN = 24;
|
|
28
31
|
// Cap elapsed-time hours at Excel's documented limit (`9999:59:59`). Beyond
|
|
29
32
|
// this, treat as text rather than producing absurdly large fractional
|
|
30
33
|
// serials. JS `Number.isFinite` would let `Infinity` through, so we cap
|
|
@@ -60,12 +63,17 @@ export const validateDate = (value, locale) => {
|
|
|
60
63
|
if (year < EXCEL_DATE_MIN_YEAR || year > EXCEL_DATE_MAX_YEAR) {
|
|
61
64
|
return undefined;
|
|
62
65
|
}
|
|
66
|
+
// Excel's phantom 1900-02-29 (serial 60): the parser hands back a stand-in
|
|
67
|
+
// `Date` on the 28th with `excelLeapDay` set, because no `Date` can hold the
|
|
68
|
+
// 29th. Report the day the user typed; `toSerialFromParts` maps it to 60
|
|
69
|
+
// (rowsncolumns/spreadsheet#1026).
|
|
70
|
+
const day = "excelLeapDay" in result && result.excelLeapDay ? 29 : dateValue.getDate();
|
|
63
71
|
return {
|
|
64
72
|
format: result.pattern,
|
|
65
73
|
parts: {
|
|
66
74
|
year,
|
|
67
75
|
month: dateValue.getMonth(),
|
|
68
|
-
day
|
|
76
|
+
day,
|
|
69
77
|
hour: dateValue.getHours(),
|
|
70
78
|
minute: dateValue.getMinutes(),
|
|
71
79
|
second: dateValue.getSeconds(),
|
|
@@ -94,10 +102,10 @@ export const validateDateTime = (value) => {
|
|
|
94
102
|
const minute = parseInt(match[2], 10);
|
|
95
103
|
const second = parseInt(match[3], 10);
|
|
96
104
|
if (isValidTimeComponents(hour, minute, second)) {
|
|
97
|
-
// Excel parity:
|
|
98
|
-
// `48:00:00`). Auto-apply `[h]:mm:ss` so the cell displays
|
|
99
|
-
// hours count instead of wrapping to `01:30:00`.
|
|
100
|
-
const format = hour
|
|
105
|
+
// Excel parity: hours >= 24 are an elapsed-time entry (`24:00:00`,
|
|
106
|
+
// `25:30:00`, `48:00:00`). Auto-apply `[h]:mm:ss` so the cell displays
|
|
107
|
+
// the full hours count instead of wrapping to `01:30:00`.
|
|
108
|
+
const format = hour >= ELAPSED_HOURS_MIN ? TIME_FORMATS.ELAPSED : TIME_FORMATS.FULL;
|
|
101
109
|
return createTimeResult(format, hour, minute, second);
|
|
102
110
|
}
|
|
103
111
|
}
|
|
@@ -115,8 +123,9 @@ export const validateDateTime = (value) => {
|
|
|
115
123
|
const hour = parseInt(match[1], 10);
|
|
116
124
|
const minute = parseInt(match[2], 10);
|
|
117
125
|
if (isValidTimeComponents(hour, minute, 0)) {
|
|
118
|
-
// Same elapsed-time
|
|
119
|
-
|
|
126
|
+
// Same elapsed-time rule as the full form: a duration typed without
|
|
127
|
+
// seconds still gets `[h]:mm:ss` (`25:00` → `25:00:00`, as Excel).
|
|
128
|
+
const format = hour >= ELAPSED_HOURS_MIN ? TIME_FORMATS.ELAPSED : TIME_FORMATS.SHORT;
|
|
120
129
|
return createTimeResult(format, hour, minute, 0);
|
|
121
130
|
}
|
|
122
131
|
}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
export declare const pattern_number = "#";
|
|
2
2
|
export declare const pattern_number_thousands = "#,##0";
|
|
3
3
|
export declare const getDecimalPlaces: (value: string | number, locale?: string) => number;
|
|
4
|
+
/**
|
|
5
|
+
* Whether a typed literal groups its integer part with the locale's thousands
|
|
6
|
+
* separator (`1,000` / `1,234.56` in en-US, `1.234,56` in de-DE, `1 234` in
|
|
7
|
+
* fr-FR). In a comma-decimal locale a canonical dot-decimal string (`1.234`)
|
|
8
|
+
* is ambiguous — the programmatic decimal 1.234, or the grouped 1234 — so when
|
|
9
|
+
* the caller knows what the literal parsed to (`parsedValue`) that decides;
|
|
10
|
+
* without it the canonical reading wins, so the locale's `.` group separator
|
|
11
|
+
* is not mistaken for grouping. Rust `has_thousands_grouping`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const hasThousandsGrouping: (value: string | number, locale?: string, parsedValue?: number) => boolean;
|
|
4
14
|
export declare const detectDecimalPattern: (value: string | number | boolean | Date | undefined, defaultPattern?: string, locale?: string) => string;
|
|
5
15
|
export declare const changeDecimals: (pattern?: string | undefined, changeBy?: number, delta?: boolean) => string;
|
|
6
16
|
export declare const getCurrentDecimalCountInPattern: (pattern?: string | undefined | null) => number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decimals.d.ts","sourceRoot":"","sources":["../../../api/decimals.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAyDhD,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,GAAG,MAAM,EACtB,SAAS,MAAM,KACd,MAsCF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EACnD,uBAA+B,EAC/B,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"decimals.d.ts","sourceRoot":"","sources":["../../../api/decimals.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAyDhD,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,GAAG,MAAM,EACtB,SAAS,MAAM,KACd,MAsCF,CAAC;AAcF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,GAAG,MAAM,EACtB,SAAS,MAAM,EACf,cAAc,MAAM,KACnB,OAeF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EACnD,uBAA+B,EAC/B,SAAS,MAAM,WAsBhB,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,UAAS,MAAM,GAAG,SAAc,EAChC,WAAU,MAAU,EACpB,eAAY,WAuCb,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,UAAS,MAAM,GAAG,SAAS,GAAG,IAAS,WAexC,CAAC;AAGF,eAAO,MAAM,oBAAoB,GAAI,UAAS,MAAM,GAAG,SAAc,WAwBpE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,UAAS,MAAM,GAAG,SAAc,WAQvE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,UAAS,MAAM,GAAG,SAAc,YAIrE,CAAC"}
|
package/dist/esm/api/decimals.js
CHANGED
|
@@ -73,19 +73,40 @@ export const getDecimalPlaces = (value, locale) => {
|
|
|
73
73
|
// Return the length of the decimal part
|
|
74
74
|
return decimalPart.length;
|
|
75
75
|
};
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const WHITESPACE_GROUP_REGEX = /[\s\u00A0\u202F]/;
|
|
77
|
+
/**
|
|
78
|
+
* Whether `integerPart` contains the locale's group separator. Locales that
|
|
79
|
+
* group with a space accept the whole whitespace family (regular, no-break,
|
|
80
|
+
* narrow no-break) — users type a plain space for fr-FR's `\u202F`.
|
|
81
|
+
*/
|
|
82
|
+
const integerPartHasGroup = (integerPart, group) => group.trim() === ""
|
|
83
|
+
? WHITESPACE_GROUP_REGEX.test(integerPart)
|
|
84
|
+
: integerPart.includes(group);
|
|
85
|
+
/**
|
|
86
|
+
* Whether a typed literal groups its integer part with the locale's thousands
|
|
87
|
+
* separator (`1,000` / `1,234.56` in en-US, `1.234,56` in de-DE, `1 234` in
|
|
88
|
+
* fr-FR). In a comma-decimal locale a canonical dot-decimal string (`1.234`)
|
|
89
|
+
* is ambiguous — the programmatic decimal 1.234, or the grouped 1234 — so when
|
|
90
|
+
* the caller knows what the literal parsed to (`parsedValue`) that decides;
|
|
91
|
+
* without it the canonical reading wins, so the locale's `.` group separator
|
|
92
|
+
* is not mistaken for grouping. Rust `has_thousands_grouping`.
|
|
93
|
+
*/
|
|
94
|
+
export const hasThousandsGrouping = (value, locale, parsedValue) => {
|
|
95
|
+
const str = typeof value === "number" ? normalizeFloatingPointString(value) : value;
|
|
78
96
|
const { decimal, group } = getLocaleSeparators(locale);
|
|
79
97
|
const groupSeparator = group ?? ",";
|
|
80
98
|
const decimalSeparator = decimal ?? ".";
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
if (decimalSeparator !== "." && CANONICAL_DOT_DECIMAL_REGEX.test(str)) {
|
|
100
|
+
return (parsedValue !== undefined &&
|
|
101
|
+
Number(str) !== parsedValue &&
|
|
102
|
+
integerPartHasGroup(str, groupSeparator));
|
|
103
|
+
}
|
|
104
|
+
const integerPart = str.split(decimalSeparator)[0];
|
|
105
|
+
return Boolean(groupSeparator) && integerPartHasGroup(integerPart, groupSeparator);
|
|
106
|
+
};
|
|
107
|
+
export const detectDecimalPattern = (value, defaultPattern = pattern_number, locale) => {
|
|
108
|
+
const str = typeof value === "number" ? normalizeFloatingPointString(value) : String(value);
|
|
109
|
+
const hasThousands = hasThousandsGrouping(str, locale);
|
|
89
110
|
const decimalsLen = getDecimalPlaces(value, locale);
|
|
90
111
|
// Use "0" pattern for numbers less than 1 to ensure leading zero
|
|
91
112
|
const numValue = Number(str.replace(/[^0-9.-]/g, ""));
|
|
@@ -38,7 +38,17 @@ export declare const collectUniqueDependentsForRange: <T extends CellData>(param
|
|
|
38
38
|
rowIndex: number;
|
|
39
39
|
columnIndex: number;
|
|
40
40
|
}>;
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* One reference shift of a structural edit: the references inside `sourceRange` move by the
|
|
43
|
+
* deltas, those inside `deletedRange` break to `#REF!`.
|
|
44
|
+
*/
|
|
45
|
+
export type FormulaMoveStep = {
|
|
46
|
+
rowDelta: number;
|
|
47
|
+
columnDelta: number;
|
|
48
|
+
sourceRange?: GridRange;
|
|
49
|
+
deletedRange?: GridRange;
|
|
50
|
+
};
|
|
51
|
+
export declare const moveFormulaAtPosition: <T extends CellData>({ draft, position, destinationSheetId, activeSheetName, enqueueCalculation, enqueueGraphOperation, recalcCells, movedKeys, rowDelta, columnDelta, sourceRange, deletedRange, steps, destinationPosition, locale, rowCount, columnCount, cellXfsRegistry, sharedStringRegistry, }: {
|
|
42
52
|
draft: Draft<SheetData<T>>;
|
|
43
53
|
position: {
|
|
44
54
|
sheetId: number;
|
|
@@ -56,6 +66,14 @@ export declare const moveFormulaAtPosition: <T extends CellData>({ draft, positi
|
|
|
56
66
|
sourceRange?: GridRange;
|
|
57
67
|
/** Cells removed by a delete-cells shift — references into it become `#REF!` (SHF-09). */
|
|
58
68
|
deletedRange?: GridRange;
|
|
69
|
+
/**
|
|
70
|
+
* Several shifts applied to the formula text in order, one per deleted band of a
|
|
71
|
+
* non-contiguous delete (Ctrl-selected rows 2 and 4): each step shifts the references inside
|
|
72
|
+
* its own `sourceRange` by its delta and breaks those inside its `deletedRange`. Replaces the
|
|
73
|
+
* single `rowDelta` / `columnDelta` / `sourceRange` / `deletedRange` shift when given; pass
|
|
74
|
+
* `destinationPosition` with it (the cell's own move is not any single step's delta).
|
|
75
|
+
*/
|
|
76
|
+
steps?: FormulaMoveStep[];
|
|
59
77
|
/**
|
|
60
78
|
* Where the formula cell itself ends up. Defaults to `position + delta` (the cell moves with the
|
|
61
79
|
* shift). A dependent OUTSIDE the shifted band keeps its place while its references into the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency.d.ts","sourceRoot":"","sources":["../../../api/dependency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAIrD,KAAK,0BAA0B,CAAC,CAAC,SAAS,QAAQ,IAAI;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IAClE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,qBAAqB,CAAC,EAAE,UAAU,CAChC,OAAO,cAAc,CACtB,CAAC,uBAAuB,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACzD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC/C,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAAI,CAAC,SAAS,QAAQ,EAAE,0QAoBjE,0BAA0B,CAAC,CAAC,CAAC,SAwO/B,CAAC;AAIF,eAAO,MAAM,+BAA+B,GAAI,CAAC,SAAS,QAAQ,EAAE,QAAQ;IAC1E,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,SAAS,CAAC;IACvB,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;CACnE;aAIc,MAAM;cAAY,MAAM;iBAAe,MAAM;EAsC3D,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"dependency.d.ts","sourceRoot":"","sources":["../../../api/dependency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAIrD,KAAK,0BAA0B,CAAC,CAAC,SAAS,QAAQ,IAAI;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IAClE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,qBAAqB,CAAC,EAAE,UAAU,CAChC,OAAO,cAAc,CACtB,CAAC,uBAAuB,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACzD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC/C,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAAI,CAAC,SAAS,QAAQ,EAAE,0QAoBjE,0BAA0B,CAAC,CAAC,CAAC,SAwO/B,CAAC;AAIF,eAAO,MAAM,+BAA+B,GAAI,CAAC,SAAS,QAAQ,EAAE,QAAQ;IAC1E,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,SAAS,CAAC;IACvB,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;CACnE;aAIc,MAAM;cAAY,MAAM;iBAAe,MAAM;EAsC3D,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EAAE,kRAoBvD;IACD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,qBAAqB,CAAC,EAAE,UAAU,CAChC,OAAO,cAAc,CACtB,CAAC,uBAAuB,CAAC,CAAC;IAC3B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC/C,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,SA6FA,CAAC"}
|
|
@@ -205,7 +205,7 @@ export const collectUniqueDependentsForRange = (params) => {
|
|
|
205
205
|
return dependentMap;
|
|
206
206
|
};
|
|
207
207
|
// Process a single formula move at a specific position with the specified deltas.
|
|
208
|
-
export const moveFormulaAtPosition = ({ draft, position, destinationSheetId, activeSheetName, enqueueCalculation, enqueueGraphOperation, recalcCells, movedKeys, rowDelta = 0, columnDelta = 0, sourceRange, deletedRange, destinationPosition, locale, rowCount, columnCount, cellXfsRegistry, sharedStringRegistry, }) => {
|
|
208
|
+
export const moveFormulaAtPosition = ({ draft, position, destinationSheetId, activeSheetName, enqueueCalculation, enqueueGraphOperation, recalcCells, movedKeys, rowDelta = 0, columnDelta = 0, sourceRange, deletedRange, steps, destinationPosition, locale, rowCount, columnCount, cellXfsRegistry, sharedStringRegistry, }) => {
|
|
209
209
|
const sheetCell = new SheetCell();
|
|
210
210
|
const currentSheet = createSheetDraft(draft, position.sheetId, position.rowIndex);
|
|
211
211
|
const cellData = currentSheet[position.rowIndex]?.values?.[position.columnIndex];
|
|
@@ -218,15 +218,20 @@ export const moveFormulaAtPosition = ({ draft, position, destinationSheetId, act
|
|
|
218
218
|
const resolvedSheetName = activeSheetName ?? "";
|
|
219
219
|
// The delta is applied to the references inside `sourceRange`; the formula cell's own
|
|
220
220
|
// destination (for the graph re-key) may differ when it does not move itself.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
221
|
+
const moves = steps ?? [
|
|
222
|
+
{ rowDelta, columnDelta, sourceRange, deletedRange },
|
|
223
|
+
];
|
|
224
|
+
for (const move of moves) {
|
|
225
|
+
sheetCell.moveFormula({ rowIndex: position.rowIndex, columnIndex: position.columnIndex }, {
|
|
226
|
+
rowIndex: position.rowIndex + move.rowDelta,
|
|
227
|
+
columnIndex: position.columnIndex + move.columnDelta,
|
|
228
|
+
}, resolvedSheetName, {
|
|
229
|
+
exclusionRange: move.sourceRange,
|
|
230
|
+
deletedRange: move.deletedRange,
|
|
231
|
+
rowCount,
|
|
232
|
+
columnCount,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
230
235
|
if (previousFormula !== sheetCell.formula) {
|
|
231
236
|
// Set cell data at old position (formula updated prior to structural move)
|
|
232
237
|
currentSheet[position.rowIndex].values[position.columnIndex] =
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { BooleanCondition, ConditionType } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
export declare const isDateFilterCondition: (type: ConditionType | string | null | undefined) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Whether a filter clause compares the cell's NUMBER — a Number Filter or a
|
|
5
|
+
* Date Filter comparison. Text (even numeric-looking text such as `'500`),
|
|
6
|
+
* booleans, errors and blanks never satisfy such a clause.
|
|
7
|
+
*/
|
|
8
|
+
export declare const isNumericFilterCondition: (type: ConditionType | string | null | undefined) => boolean;
|
|
9
|
+
/** A cell value that takes part in a Number / Date Filter: a finite number. */
|
|
10
|
+
export declare const isNumericFilterCellValue: (value: unknown) => value is number;
|
|
11
|
+
/**
|
|
12
|
+
* How a Number / Date Filter criterion (`userEnteredValue`, the free text
|
|
13
|
+
* typed into the filter dialog) reads.
|
|
14
|
+
*
|
|
15
|
+
* - `empty`: nothing typed — the clause is inactive.
|
|
16
|
+
* - `number`: a typed literal — number, `50%`, currency, or a date / time in
|
|
17
|
+
* the document locale — as its numeric value (a date is its serial, exactly
|
|
18
|
+
* what the cell holds).
|
|
19
|
+
* - `formula`: `=…` or a cell reference; only the calculator can resolve it.
|
|
20
|
+
* - `text`: anything else; no number satisfies it.
|
|
21
|
+
*/
|
|
22
|
+
export type FilterCriterion = {
|
|
23
|
+
kind: "empty";
|
|
24
|
+
} | {
|
|
25
|
+
kind: "number";
|
|
26
|
+
value: number;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "formula";
|
|
29
|
+
} | {
|
|
30
|
+
kind: "text";
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Classify a filter criterion the way Excel's Custom AutoFilter reads the typed
|
|
34
|
+
* box — as a cell entry in the workbook `locale` (`1/15/2024` is Jan 15 in
|
|
35
|
+
* en-US, `15.1.2024` in de-DE, `2024-02-01` anywhere, `6,5` is 6.5 in de-DE),
|
|
36
|
+
* so a Date Filter compares serials instead of quoting the text.
|
|
37
|
+
*/
|
|
38
|
+
export declare const classifyFilterCriterion: (raw: string | undefined | null, locale?: string) => FilterCriterion;
|
|
39
|
+
/**
|
|
40
|
+
* Decide one Number Filter / Date Filter clause against a cell's typed value,
|
|
41
|
+
* the way Excel does — without the calculator:
|
|
42
|
+
*
|
|
43
|
+
* - Only NUMBERS take part. Text (including numeric-looking text such as
|
|
44
|
+
* `'500`), booleans, errors and blanks never satisfy `> 500` / `after
|
|
45
|
+
* 1/15/2024` and are hidden (`false`). The calculator's coercion would say
|
|
46
|
+
* text > number is TRUE and a blank is 0 — neither is Excel. "Does not
|
|
47
|
+
* equal" is the complement of "equals": text and blanks stay visible.
|
|
48
|
+
* - A typed date criterion is read in the document `locale` and compared as a
|
|
49
|
+
* SERIAL; Date Filters compare calendar days (`INT`), so `1/15/2024 14:00`
|
|
50
|
+
* equals `1/15/2024` and is not after it.
|
|
51
|
+
* - A `=…` / reference criterion is `undefined` — left to the calculator, which
|
|
52
|
+
* then compares a number (the cell-type gate has already run). An empty
|
|
53
|
+
* criterion is `undefined` too (an inactive clause).
|
|
54
|
+
*
|
|
55
|
+
* `undefined` for a condition type this evaluator does not own.
|
|
56
|
+
*/
|
|
57
|
+
export declare const evaluateNumericFilterCondition: (condition: BooleanCondition | null | undefined, value: unknown, locale?: string) => boolean | undefined;
|
|
58
|
+
//# sourceMappingURL=filter-condition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-condition.d.ts","sourceRoot":"","sources":["../../../api/filter-condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,2BAA2B,CAAC;AA4CnC,eAAO,MAAM,qBAAqB,GAChC,MAAM,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KAC9C,OACsE,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KAC9C,OAKA,CAAC;AAEJ,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACd,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,SAAS,MAAM,KACd,eAcF,CAAC;AAKF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,8BAA8B,GACzC,WAAW,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC9C,OAAO,OAAO,EACd,SAAS,MAAM,KACd,OAAO,GAAG,SA2DZ,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { checkIfStringIsReference } from "@rowsncolumns/utils";
|
|
2
|
+
import { detectValueTypeAndPattern } from "./datatype";
|
|
3
|
+
/**
|
|
4
|
+
* Excel's Number Filters and Date Filters — the AutoFilter clauses that act on
|
|
5
|
+
* the cell's NUMBER (a date is its serial) and hide every non-numeric cell.
|
|
6
|
+
* Twin of `is_numeric_filter_kind` in the Rust engine (`rnc-store/support.rs`);
|
|
7
|
+
* the shared table `libs/common-types/fixtures/filter-evaluator-table.json`
|
|
8
|
+
* (`numeric`) keeps the two evaluators identical.
|
|
9
|
+
*/
|
|
10
|
+
const NUMBER_FILTER_CONDITION_TYPES = new Set([
|
|
11
|
+
"NUMBER_EQ",
|
|
12
|
+
"NUMBER_NOT_EQ",
|
|
13
|
+
"NUMBER_GREATER",
|
|
14
|
+
"NUMBER_GREATER_THAN_EQ",
|
|
15
|
+
"NUMBER_LESS",
|
|
16
|
+
"NUMBER_LESS_THAN_EQ",
|
|
17
|
+
"NUMBER_BETWEEN",
|
|
18
|
+
"NUMBER_NOT_BETWEEN",
|
|
19
|
+
]);
|
|
20
|
+
const DATE_FILTER_CONDITION_TYPES = new Set([
|
|
21
|
+
"DATE_EQ",
|
|
22
|
+
"DATE_NOT_EQ",
|
|
23
|
+
"DATE_BEFORE",
|
|
24
|
+
"DATE_AFTER",
|
|
25
|
+
"DATE_ON_OR_BEFORE",
|
|
26
|
+
"DATE_ON_OR_AFTER",
|
|
27
|
+
"DATE_BETWEEN",
|
|
28
|
+
"DATE_NOT_BETWEEN",
|
|
29
|
+
]);
|
|
30
|
+
const TWO_OPERAND_FILTER_CONDITION_TYPES = new Set([
|
|
31
|
+
"NUMBER_BETWEEN",
|
|
32
|
+
"NUMBER_NOT_BETWEEN",
|
|
33
|
+
"DATE_BETWEEN",
|
|
34
|
+
"DATE_NOT_BETWEEN",
|
|
35
|
+
]);
|
|
36
|
+
export const isDateFilterCondition = (type) => Boolean(type && DATE_FILTER_CONDITION_TYPES.has(type));
|
|
37
|
+
/**
|
|
38
|
+
* Whether a filter clause compares the cell's NUMBER — a Number Filter or a
|
|
39
|
+
* Date Filter comparison. Text (even numeric-looking text such as `'500`),
|
|
40
|
+
* booleans, errors and blanks never satisfy such a clause.
|
|
41
|
+
*/
|
|
42
|
+
export const isNumericFilterCondition = (type) => Boolean(type &&
|
|
43
|
+
(NUMBER_FILTER_CONDITION_TYPES.has(type) ||
|
|
44
|
+
DATE_FILTER_CONDITION_TYPES.has(type)));
|
|
45
|
+
/** A cell value that takes part in a Number / Date Filter: a finite number. */
|
|
46
|
+
export const isNumericFilterCellValue = (value) => typeof value === "number" && Number.isFinite(value);
|
|
47
|
+
/**
|
|
48
|
+
* Classify a filter criterion the way Excel's Custom AutoFilter reads the typed
|
|
49
|
+
* box — as a cell entry in the workbook `locale` (`1/15/2024` is Jan 15 in
|
|
50
|
+
* en-US, `15.1.2024` in de-DE, `2024-02-01` anywhere, `6,5` is 6.5 in de-DE),
|
|
51
|
+
* so a Date Filter compares serials instead of quoting the text.
|
|
52
|
+
*/
|
|
53
|
+
export const classifyFilterCriterion = (raw, locale) => {
|
|
54
|
+
const trimmed = raw?.trim() ?? "";
|
|
55
|
+
if (!trimmed)
|
|
56
|
+
return { kind: "empty" };
|
|
57
|
+
if (trimmed.startsWith("=") || checkIfStringIsReference(trimmed)) {
|
|
58
|
+
return { kind: "formula" };
|
|
59
|
+
}
|
|
60
|
+
const [formatValue, , numberValue] = detectValueTypeAndPattern(trimmed, locale ?? "en-US");
|
|
61
|
+
if (formatValue === "numberValue" && isNumericFilterCellValue(numberValue)) {
|
|
62
|
+
return { kind: "number", value: numberValue };
|
|
63
|
+
}
|
|
64
|
+
return { kind: "text" };
|
|
65
|
+
};
|
|
66
|
+
const numbersEqual = (a, b) => Math.abs(a - b) <= 1e-9 * Math.max(Math.abs(a), Math.abs(b), 1);
|
|
67
|
+
/**
|
|
68
|
+
* Decide one Number Filter / Date Filter clause against a cell's typed value,
|
|
69
|
+
* the way Excel does — without the calculator:
|
|
70
|
+
*
|
|
71
|
+
* - Only NUMBERS take part. Text (including numeric-looking text such as
|
|
72
|
+
* `'500`), booleans, errors and blanks never satisfy `> 500` / `after
|
|
73
|
+
* 1/15/2024` and are hidden (`false`). The calculator's coercion would say
|
|
74
|
+
* text > number is TRUE and a blank is 0 — neither is Excel. "Does not
|
|
75
|
+
* equal" is the complement of "equals": text and blanks stay visible.
|
|
76
|
+
* - A typed date criterion is read in the document `locale` and compared as a
|
|
77
|
+
* SERIAL; Date Filters compare calendar days (`INT`), so `1/15/2024 14:00`
|
|
78
|
+
* equals `1/15/2024` and is not after it.
|
|
79
|
+
* - A `=…` / reference criterion is `undefined` — left to the calculator, which
|
|
80
|
+
* then compares a number (the cell-type gate has already run). An empty
|
|
81
|
+
* criterion is `undefined` too (an inactive clause).
|
|
82
|
+
*
|
|
83
|
+
* `undefined` for a condition type this evaluator does not own.
|
|
84
|
+
*/
|
|
85
|
+
export const evaluateNumericFilterCondition = (condition, value, locale) => {
|
|
86
|
+
const type = condition?.type;
|
|
87
|
+
if (!type || !isNumericFilterCondition(type))
|
|
88
|
+
return undefined;
|
|
89
|
+
const values = condition?.values ?? [];
|
|
90
|
+
if (!isNumericFilterCellValue(value)) {
|
|
91
|
+
// "Does not equal" is the complement of "equals" over the whole column: a
|
|
92
|
+
// text cell or a blank does not equal 500 and stays visible (a text cell
|
|
93
|
+
// whose display text IS the criterion hides).
|
|
94
|
+
if (type === "NUMBER_NOT_EQ" || type === "DATE_NOT_EQ") {
|
|
95
|
+
const shown = value === undefined || value === null ? "" : String(value);
|
|
96
|
+
return (shown.trim().toLowerCase() !==
|
|
97
|
+
(values[0]?.userEnteredValue ?? "").trim().toLowerCase());
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const first = classifyFilterCriterion(values[0]?.userEnteredValue, locale);
|
|
102
|
+
const second = TWO_OPERAND_FILTER_CONDITION_TYPES.has(type)
|
|
103
|
+
? classifyFilterCriterion(values[1]?.userEnteredValue, locale)
|
|
104
|
+
: { kind: "number", value: 0 };
|
|
105
|
+
if (first.kind === "empty" || second.kind === "empty")
|
|
106
|
+
return undefined;
|
|
107
|
+
if (first.kind === "formula" || second.kind === "formula")
|
|
108
|
+
return undefined;
|
|
109
|
+
if (first.kind !== "number" || second.kind !== "number")
|
|
110
|
+
return false;
|
|
111
|
+
// Date Filters compare calendar days; Number Filters the exact values.
|
|
112
|
+
const day = isDateFilterCondition(type)
|
|
113
|
+
? (n) => Math.floor(n)
|
|
114
|
+
: (n) => n;
|
|
115
|
+
const a = day(value);
|
|
116
|
+
const lo = day(first.value);
|
|
117
|
+
const hi = day(second.value);
|
|
118
|
+
switch (type) {
|
|
119
|
+
case "NUMBER_EQ":
|
|
120
|
+
case "DATE_EQ":
|
|
121
|
+
return numbersEqual(a, lo);
|
|
122
|
+
case "NUMBER_NOT_EQ":
|
|
123
|
+
case "DATE_NOT_EQ":
|
|
124
|
+
return !numbersEqual(a, lo);
|
|
125
|
+
case "NUMBER_GREATER":
|
|
126
|
+
case "DATE_AFTER":
|
|
127
|
+
return a > lo;
|
|
128
|
+
case "NUMBER_GREATER_THAN_EQ":
|
|
129
|
+
case "DATE_ON_OR_AFTER":
|
|
130
|
+
return a >= lo;
|
|
131
|
+
case "NUMBER_LESS":
|
|
132
|
+
case "DATE_BEFORE":
|
|
133
|
+
return a < lo;
|
|
134
|
+
case "NUMBER_LESS_THAN_EQ":
|
|
135
|
+
case "DATE_ON_OR_BEFORE":
|
|
136
|
+
return a <= lo;
|
|
137
|
+
case "NUMBER_BETWEEN":
|
|
138
|
+
case "DATE_BETWEEN":
|
|
139
|
+
return a >= lo && a <= hi;
|
|
140
|
+
case "NUMBER_NOT_BETWEEN":
|
|
141
|
+
case "DATE_NOT_BETWEEN":
|
|
142
|
+
return a < lo || a > hi;
|
|
143
|
+
default:
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
};
|