@rowsncolumns/spreadsheet 10.2.74 → 10.2.75
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/excel-validation-kinds.d.ts +71 -0
- package/dist/cjs/api/excel-validation-kinds.d.ts.map +1 -0
- package/dist/cjs/api/excel-validation-kinds.js +304 -0
- package/dist/cjs/api/go-to-special.d.ts +32 -0
- package/dist/cjs/api/go-to-special.d.ts.map +1 -0
- package/dist/cjs/api/go-to-special.js +111 -0
- package/dist/cjs/api/validation-tooltip.d.ts +17 -0
- package/dist/cjs/api/validation-tooltip.d.ts.map +1 -0
- package/dist/cjs/api/validation-tooltip.js +42 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts +28 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.js +32 -0
- package/dist/cjs/atoms/go-to-dialog.atom.d.ts +11 -1
- package/dist/cjs/atoms/go-to-dialog.atom.d.ts.map +1 -1
- package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts +29 -0
- package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/goal-seek-dialog.atom.js +30 -0
- package/dist/cjs/atoms/index.d.ts +1 -0
- package/dist/cjs/atoms/index.d.ts.map +1 -1
- package/dist/cjs/atoms/index.js +1 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.d.ts +39 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.js +30 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts +34 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.js +36 -0
- package/dist/cjs/atoms/watch-window.atom.d.ts +37 -0
- package/dist/cjs/atoms/watch-window.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/watch-window.atom.js +61 -0
- package/dist/cjs/components/advanced-filter-dialog/index.d.ts +28 -0
- package/dist/cjs/components/advanced-filter-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/advanced-filter-dialog/index.js +134 -0
- package/dist/cjs/components/calculation-options-dialog/index.d.ts +33 -0
- package/dist/cjs/components/calculation-options-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/calculation-options-dialog/index.js +69 -0
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +104 -21
- package/dist/cjs/components/canvas-grid/typings.d.ts +19 -2
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.d.ts +30 -0
- package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.js +56 -1
- package/dist/cjs/components/cell/index.d.ts.map +1 -1
- package/dist/cjs/components/cell/index.js +15 -7
- package/dist/cjs/components/cell-diff/index.d.ts +3 -2
- package/dist/cjs/components/cell-diff/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.js +2 -1
- package/dist/cjs/components/context-menu/index.d.ts +6 -1
- package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
- package/dist/cjs/components/context-menu/index.js +146 -3
- package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/cjs/components/embeds/embed-component.js +16 -1
- package/dist/cjs/components/go-to-dialog/index.d.ts +11 -1
- package/dist/cjs/components/go-to-dialog/index.d.ts.map +1 -1
- package/dist/cjs/components/go-to-dialog/index.js +67 -24
- package/dist/cjs/components/goal-seek-dialog/index.d.ts +32 -0
- package/dist/cjs/components/goal-seek-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/goal-seek-dialog/index.js +149 -0
- package/dist/cjs/components/subtotal-dialog/index.d.ts +30 -0
- package/dist/cjs/components/subtotal-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/subtotal-dialog/index.js +126 -0
- package/dist/cjs/components/text-to-columns-dialog/index.d.ts +37 -0
- package/dist/cjs/components/text-to-columns-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/text-to-columns-dialog/index.js +166 -0
- package/dist/cjs/components/toolbar/background-color-selector.d.ts +13 -1
- package/dist/cjs/components/toolbar/background-color-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/background-color-selector.js +57 -11
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts +15 -2
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/cell-style-selector.js +56 -3
- package/dist/cjs/components/toolbar/fill-selectors.d.ts +49 -0
- package/dist/cjs/components/toolbar/fill-selectors.d.ts.map +1 -0
- package/dist/cjs/components/toolbar/fill-selectors.js +196 -0
- package/dist/cjs/components/toolbar/index.d.ts +1 -0
- package/dist/cjs/components/toolbar/index.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/index.js +1 -0
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.js +23 -4
- package/dist/cjs/components/watch-window/index.d.ts +25 -0
- package/dist/cjs/components/watch-window/index.d.ts.map +1 -0
- package/dist/cjs/components/watch-window/index.js +63 -0
- package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-cell-renderer.js +17 -6
- package/dist/cjs/hooks/use-get-tooltip.d.ts.map +1 -1
- package/dist/cjs/hooks/use-get-tooltip.js +7 -2
- package/dist/cjs/hooks/use-keyboard-handler.d.ts.map +1 -1
- package/dist/cjs/hooks/use-keyboard-handler.js +24 -12
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +117 -4
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +19 -1
- package/dist/esm/api/excel-validation-kinds.d.ts +71 -0
- package/dist/esm/api/excel-validation-kinds.d.ts.map +1 -0
- package/dist/esm/api/excel-validation-kinds.js +287 -0
- package/dist/esm/api/go-to-special.d.ts +32 -0
- package/dist/esm/api/go-to-special.d.ts.map +1 -0
- package/dist/esm/api/go-to-special.js +96 -0
- package/dist/esm/api/validation-tooltip.d.ts +17 -0
- package/dist/esm/api/validation-tooltip.d.ts.map +1 -0
- package/dist/esm/api/validation-tooltip.js +34 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts +28 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.js +21 -0
- package/dist/esm/atoms/go-to-dialog.atom.d.ts +11 -1
- package/dist/esm/atoms/go-to-dialog.atom.d.ts.map +1 -1
- package/dist/esm/atoms/goal-seek-dialog.atom.d.ts +29 -0
- package/dist/esm/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/goal-seek-dialog.atom.js +21 -0
- package/dist/esm/atoms/index.d.ts +1 -0
- package/dist/esm/atoms/index.d.ts.map +1 -1
- package/dist/esm/atoms/index.js +1 -0
- package/dist/esm/atoms/subtotal-dialog.atom.d.ts +39 -0
- package/dist/esm/atoms/subtotal-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/subtotal-dialog.atom.js +21 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts +34 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.js +25 -0
- package/dist/esm/atoms/watch-window.atom.d.ts +37 -0
- package/dist/esm/atoms/watch-window.atom.d.ts.map +1 -0
- package/dist/esm/atoms/watch-window.atom.js +37 -0
- package/dist/esm/components/advanced-filter-dialog/index.d.ts +28 -0
- package/dist/esm/components/advanced-filter-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/advanced-filter-dialog/index.js +125 -0
- package/dist/esm/components/calculation-options-dialog/index.d.ts +33 -0
- package/dist/esm/components/calculation-options-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/calculation-options-dialog/index.js +61 -0
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +99 -20
- package/dist/esm/components/canvas-grid/typings.d.ts +19 -2
- package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.d.ts +30 -0
- package/dist/esm/components/cell/alignment.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.js +51 -0
- package/dist/esm/components/cell/index.d.ts.map +1 -1
- package/dist/esm/components/cell/index.js +16 -8
- package/dist/esm/components/cell-diff/index.d.ts +3 -2
- package/dist/esm/components/cell-diff/index.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.js +2 -1
- package/dist/esm/components/context-menu/index.d.ts +6 -1
- package/dist/esm/components/context-menu/index.d.ts.map +1 -1
- package/dist/esm/components/context-menu/index.js +139 -4
- package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/esm/components/embeds/embed-component.js +17 -1
- package/dist/esm/components/go-to-dialog/index.d.ts +11 -1
- package/dist/esm/components/go-to-dialog/index.d.ts.map +1 -1
- package/dist/esm/components/go-to-dialog/index.js +63 -23
- package/dist/esm/components/goal-seek-dialog/index.d.ts +32 -0
- package/dist/esm/components/goal-seek-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/goal-seek-dialog/index.js +125 -0
- package/dist/esm/components/subtotal-dialog/index.d.ts +30 -0
- package/dist/esm/components/subtotal-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/subtotal-dialog/index.js +115 -0
- package/dist/esm/components/text-to-columns-dialog/index.d.ts +37 -0
- package/dist/esm/components/text-to-columns-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/text-to-columns-dialog/index.js +147 -0
- package/dist/esm/components/toolbar/background-color-selector.d.ts +13 -1
- package/dist/esm/components/toolbar/background-color-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/background-color-selector.js +61 -11
- package/dist/esm/components/toolbar/cell-style-selector.d.ts +15 -2
- package/dist/esm/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/cell-style-selector.js +46 -2
- package/dist/esm/components/toolbar/fill-selectors.d.ts +49 -0
- package/dist/esm/components/toolbar/fill-selectors.d.ts.map +1 -0
- package/dist/esm/components/toolbar/fill-selectors.js +162 -0
- package/dist/esm/components/toolbar/index.d.ts +1 -0
- package/dist/esm/components/toolbar/index.d.ts.map +1 -1
- package/dist/esm/components/toolbar/index.js +1 -0
- package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
- package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/text-horizontal-align-selector.js +21 -5
- package/dist/esm/components/watch-window/index.d.ts +25 -0
- package/dist/esm/components/watch-window/index.d.ts.map +1 -0
- package/dist/esm/components/watch-window/index.js +54 -0
- package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-cell-renderer.js +17 -6
- package/dist/esm/hooks/use-get-tooltip.d.ts.map +1 -1
- package/dist/esm/hooks/use-get-tooltip.js +7 -2
- package/dist/esm/hooks/use-keyboard-handler.d.ts.map +1 -1
- package/dist/esm/hooks/use-keyboard-handler.js +24 -12
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +8 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +117 -4
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +18 -0
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { DataValidationRule, SheetRange } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Excel's data-validation "Allow" kinds that have no `ConditionType` of
|
|
4
|
+
* their own. Each is authored as Excel's `type` + `operator` + up to two
|
|
5
|
+
* formulas and stored two ways at once:
|
|
6
|
+
*
|
|
7
|
+
* - `condition` — what both engines evaluate for the red Invalid marker:
|
|
8
|
+
* `whole` and `textLength` compose a `CUSTOM_FORMULA` over the range
|
|
9
|
+
* anchor (the same shape both importers synthesize), `time` is a plain
|
|
10
|
+
* `NUMBER_*` comparison over day-fraction serials;
|
|
11
|
+
* - `xlsxSource` — the Excel-native description both xlsx exporters replay
|
|
12
|
+
* verbatim (`type="whole"` …), so the rule round-trips as what the user
|
|
13
|
+
* picked instead of a decimal / custom rule.
|
|
14
|
+
*
|
|
15
|
+
* Imported rules already carry `xlsxSource`, so the editor reads them back
|
|
16
|
+
* as "Whole number between 1 and 10" rather than a custom formula.
|
|
17
|
+
*/
|
|
18
|
+
export type ExcelValidationKind = "whole" | "time" | "textLength";
|
|
19
|
+
export type ExcelValidationOperator = "between" | "notBetween" | "equal" | "notEqual" | "greaterThan" | "lessThan" | "greaterThanOrEqual" | "lessThanOrEqual";
|
|
20
|
+
export type ExcelValidationSpec = {
|
|
21
|
+
kind: ExcelValidationKind;
|
|
22
|
+
operator: ExcelValidationOperator;
|
|
23
|
+
/** Bound(s) as the user typed them: a literal (`10`, `9:30 AM`) or `=ref`. */
|
|
24
|
+
formula1?: string;
|
|
25
|
+
formula2?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const EXCEL_VALIDATION_KIND_LABELS: ReadonlyArray<{
|
|
28
|
+
kind: ExcelValidationKind;
|
|
29
|
+
label: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare const EXCEL_VALIDATION_OPERATOR_LABELS: ReadonlyArray<{
|
|
32
|
+
operator: ExcelValidationOperator;
|
|
33
|
+
label: string;
|
|
34
|
+
}>;
|
|
35
|
+
/** Two-sided operators need both bounds. */
|
|
36
|
+
export declare const isTwoSidedOperator: (operator: ExcelValidationOperator) => operator is "between" | "notBetween";
|
|
37
|
+
/**
|
|
38
|
+
* Parse a typed time (`9:30`, `9:30 AM`, `21:05:10`, `24:00`, `0.5`) to
|
|
39
|
+
* Excel's serial. A bare non-negative number is taken as a serial already and
|
|
40
|
+
* kept whole (Excel's Time validation compares the full serial, so a
|
|
41
|
+
* date-time is out of a same-day range). `undefined` when unparsable.
|
|
42
|
+
*/
|
|
43
|
+
export declare const parseTimeToSerial: (text: string | undefined) => number | undefined;
|
|
44
|
+
/** Render a day-fraction serial as `h:mm AM/PM` (seconds when non-zero). */
|
|
45
|
+
export declare const formatTimeSerial: (serial: number) => string;
|
|
46
|
+
/** The A1 anchor (range top-left) a relative custom formula is written against. */
|
|
47
|
+
export declare const getValidationAnchor: (range: SheetRange | undefined) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Build the `condition` + `xlsxSource` pair for an Excel kind. Returns
|
|
50
|
+
* `undefined` when a required bound is missing (the editor keeps the form
|
|
51
|
+
* open in that case).
|
|
52
|
+
*/
|
|
53
|
+
export declare const buildExcelValidationRule: (spec: ExcelValidationSpec, anchor: string) => Pick<DataValidationRule, "condition" | "xlsxSource"> | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Read an Excel kind back from a rule's `xlsxSource` (imported or authored
|
|
56
|
+
* here); `undefined` for every other rule. Time bounds come back as
|
|
57
|
+
* `h:mm AM/PM` text so the editor shows what the user would type.
|
|
58
|
+
*/
|
|
59
|
+
export declare const readExcelValidationSpec: (rule: Pick<DataValidationRule, "xlsxSource"> | undefined | null) => ExcelValidationSpec | undefined;
|
|
60
|
+
/** A typed value that is a formula (`=…`) — validated on its result, not its text. */
|
|
61
|
+
export declare const isFormulaEntry: (value: unknown) => value is string;
|
|
62
|
+
/**
|
|
63
|
+
* Decide an Excel-kind rule for a freshly typed value without the
|
|
64
|
+
* calculator — the commit-time enforcement Excel applies (`stop` blocks a
|
|
65
|
+
* decimal in a whole-number cell). `undefined` when the bounds are
|
|
66
|
+
* formulas / references, or when the typed value is itself a formula
|
|
67
|
+
* (Excel validates the formula's RESULT after the recalc, so `=5` is a
|
|
68
|
+
* valid whole number), so the caller falls back to the flag-only marker.
|
|
69
|
+
*/
|
|
70
|
+
export declare const checkExcelValidationValue: (rule: Pick<DataValidationRule, "xlsxSource"> | undefined | null, value: string | boolean) => boolean | undefined;
|
|
71
|
+
//# sourceMappingURL=excel-validation-kinds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excel-validation-kinds.d.ts","sourceRoot":"","sources":["../../../api/excel-validation-kinds.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,kBAAkB,EAClB,UAAU,EACX,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;AAElE,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,YAAY,GACZ,OAAO,GACP,UAAU,GACV,aAAa,GACb,UAAU,GACV,oBAAoB,GACpB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,EAAE,uBAAuB,CAAC;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC;IACvD,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf,CAIA,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,aAAa,CAAC;IAC3D,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;CACf,CASA,CAAC;AAOF,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,GAAI,UAAU,uBAAuB,yCACf,CAAC;AAwBtD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,GAAG,SAAS,KACvB,MAAM,GAAG,SAuBX,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAUjD,CAAC;AAoBF,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,GAAI,OAAO,UAAU,GAAG,SAAS,KAAG,MAKzD,CAAC;AAgCZ;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,mBAAmB,EACzB,QAAQ,MAAM,KACb,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,SAiDzD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS,GAAG,IAAI,KAC9D,mBAAmB,GAAG,SAuBxB,CAAC;AAmCF,sFAAsF;AACtF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACE,CAAC;AAE5D;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS,GAAG,IAAI,EAC/D,OAAO,MAAM,GAAG,OAAO,KACtB,OAAO,GAAG,SAmCZ,CAAC"}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkExcelValidationValue = exports.isFormulaEntry = exports.readExcelValidationSpec = exports.buildExcelValidationRule = exports.getValidationAnchor = exports.formatTimeSerial = exports.parseTimeToSerial = exports.isTwoSidedOperator = exports.EXCEL_VALIDATION_OPERATOR_LABELS = exports.EXCEL_VALIDATION_KIND_LABELS = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var utils_1 = require("@rowsncolumns/utils");
|
|
6
|
+
exports.EXCEL_VALIDATION_KIND_LABELS = [
|
|
7
|
+
{ kind: "whole", label: "Whole number" },
|
|
8
|
+
{ kind: "time", label: "Time" },
|
|
9
|
+
{ kind: "textLength", label: "Text length" },
|
|
10
|
+
];
|
|
11
|
+
exports.EXCEL_VALIDATION_OPERATOR_LABELS = [
|
|
12
|
+
{ operator: "between", label: "between" },
|
|
13
|
+
{ operator: "notBetween", label: "not between" },
|
|
14
|
+
{ operator: "equal", label: "equal to" },
|
|
15
|
+
{ operator: "notEqual", label: "not equal to" },
|
|
16
|
+
{ operator: "greaterThan", label: "greater than" },
|
|
17
|
+
{ operator: "lessThan", label: "less than" },
|
|
18
|
+
{ operator: "greaterThanOrEqual", label: "greater than or equal to" },
|
|
19
|
+
{ operator: "lessThanOrEqual", label: "less than or equal to" },
|
|
20
|
+
];
|
|
21
|
+
var EXCEL_KINDS = new Set(["whole", "time", "textLength"]);
|
|
22
|
+
var EXCEL_OPERATORS = new Set(exports.EXCEL_VALIDATION_OPERATOR_LABELS.map(function (entry) { return entry.operator; }));
|
|
23
|
+
/** Two-sided operators need both bounds. */
|
|
24
|
+
var isTwoSidedOperator = function (operator) {
|
|
25
|
+
return operator === "between" || operator === "notBetween";
|
|
26
|
+
};
|
|
27
|
+
exports.isTwoSidedOperator = isTwoSidedOperator;
|
|
28
|
+
var NUMBER_CONDITION_BY_OPERATOR = {
|
|
29
|
+
between: "NUMBER_BETWEEN",
|
|
30
|
+
notBetween: "NUMBER_NOT_BETWEEN",
|
|
31
|
+
equal: "NUMBER_EQ",
|
|
32
|
+
notEqual: "NUMBER_NOT_EQ",
|
|
33
|
+
greaterThan: "NUMBER_GREATER",
|
|
34
|
+
lessThan: "NUMBER_LESS",
|
|
35
|
+
greaterThanOrEqual: "NUMBER_GREATER_THAN_EQ",
|
|
36
|
+
lessThanOrEqual: "NUMBER_LESS_THAN_EQ",
|
|
37
|
+
};
|
|
38
|
+
var stripEquals = function (text) {
|
|
39
|
+
var _a;
|
|
40
|
+
var trimmed = (_a = text === null || text === void 0 ? void 0 : text.trim()) !== null && _a !== void 0 ? _a : "";
|
|
41
|
+
return trimmed.startsWith("=") ? trimmed.slice(1).trim() : trimmed;
|
|
42
|
+
};
|
|
43
|
+
var TIME_PATTERN = /^(\d{1,2})(?::(\d{1,2}))?(?::(\d{1,2}))?\s*([AaPp][Mm]?)?$/;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a typed time (`9:30`, `9:30 AM`, `21:05:10`, `24:00`, `0.5`) to
|
|
46
|
+
* Excel's serial. A bare non-negative number is taken as a serial already and
|
|
47
|
+
* kept whole (Excel's Time validation compares the full serial, so a
|
|
48
|
+
* date-time is out of a same-day range). `undefined` when unparsable.
|
|
49
|
+
*/
|
|
50
|
+
var parseTimeToSerial = function (text) {
|
|
51
|
+
var _a, _b, _c, _d;
|
|
52
|
+
var trimmed = (_a = text === null || text === void 0 ? void 0 : text.trim()) !== null && _a !== void 0 ? _a : "";
|
|
53
|
+
if (!trimmed)
|
|
54
|
+
return undefined;
|
|
55
|
+
var asNumber = Number(trimmed);
|
|
56
|
+
if (Number.isFinite(asNumber)) {
|
|
57
|
+
return asNumber >= 0 ? asNumber : undefined;
|
|
58
|
+
}
|
|
59
|
+
var match = TIME_PATTERN.exec(trimmed);
|
|
60
|
+
if (!match)
|
|
61
|
+
return undefined;
|
|
62
|
+
var hours = Number(match[1]);
|
|
63
|
+
var minutes = Number((_b = match[2]) !== null && _b !== void 0 ? _b : 0);
|
|
64
|
+
var seconds = Number((_c = match[3]) !== null && _c !== void 0 ? _c : 0);
|
|
65
|
+
var meridiem = (_d = match[4]) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
66
|
+
if (minutes > 59 || seconds > 59)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (meridiem) {
|
|
69
|
+
if (hours < 1 || hours > 12)
|
|
70
|
+
return undefined;
|
|
71
|
+
if (meridiem.startsWith("p") && hours !== 12)
|
|
72
|
+
hours += 12;
|
|
73
|
+
if (meridiem.startsWith("a") && hours === 12)
|
|
74
|
+
hours = 0;
|
|
75
|
+
}
|
|
76
|
+
else if (hours > 24 || (hours === 24 && (minutes > 0 || seconds > 0))) {
|
|
77
|
+
// Excel accepts `24:00` as exactly 1.0 (midnight at the end of the day).
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return (hours * 3600 + minutes * 60 + seconds) / 86400;
|
|
81
|
+
};
|
|
82
|
+
exports.parseTimeToSerial = parseTimeToSerial;
|
|
83
|
+
/** Render a day-fraction serial as `h:mm AM/PM` (seconds when non-zero). */
|
|
84
|
+
var formatTimeSerial = function (serial) {
|
|
85
|
+
var total = Math.round((serial - Math.floor(serial)) * 86400) % 86400;
|
|
86
|
+
var hours24 = Math.floor(total / 3600);
|
|
87
|
+
var minutes = Math.floor((total % 3600) / 60);
|
|
88
|
+
var seconds = total % 60;
|
|
89
|
+
var meridiem = hours24 >= 12 ? "PM" : "AM";
|
|
90
|
+
var hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
|
91
|
+
var mm = String(minutes).padStart(2, "0");
|
|
92
|
+
var ss = seconds ? ":".concat(String(seconds).padStart(2, "0")) : "";
|
|
93
|
+
return "".concat(hours12, ":").concat(mm).concat(ss, " ").concat(meridiem);
|
|
94
|
+
};
|
|
95
|
+
exports.formatTimeSerial = formatTimeSerial;
|
|
96
|
+
/**
|
|
97
|
+
* A bound as it goes into a formula / `xlsxSource`: references and formulas
|
|
98
|
+
* verbatim (no `=`), times as serials, numbers as typed.
|
|
99
|
+
*/
|
|
100
|
+
var toBoundFormula = function (kind, text) {
|
|
101
|
+
var _a;
|
|
102
|
+
var raw = (_a = text === null || text === void 0 ? void 0 : text.trim()) !== null && _a !== void 0 ? _a : "";
|
|
103
|
+
if (!raw)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (raw.startsWith("="))
|
|
106
|
+
return stripEquals(raw);
|
|
107
|
+
if (kind === "time") {
|
|
108
|
+
var serial = (0, exports.parseTimeToSerial)(raw);
|
|
109
|
+
return serial === undefined ? raw : String(serial);
|
|
110
|
+
}
|
|
111
|
+
return raw;
|
|
112
|
+
};
|
|
113
|
+
/** The A1 anchor (range top-left) a relative custom formula is written against. */
|
|
114
|
+
var getValidationAnchor = function (range) {
|
|
115
|
+
var _a;
|
|
116
|
+
return (_a = (0, utils_1.cellToAddress)(range
|
|
117
|
+
? { rowIndex: range.startRowIndex, columnIndex: range.startColumnIndex }
|
|
118
|
+
: { rowIndex: 1, columnIndex: 1 })) !== null && _a !== void 0 ? _a : "A1";
|
|
119
|
+
};
|
|
120
|
+
exports.getValidationAnchor = getValidationAnchor;
|
|
121
|
+
var comparison = function (subject, operator, a, b) {
|
|
122
|
+
switch (operator) {
|
|
123
|
+
case "between":
|
|
124
|
+
return b === undefined
|
|
125
|
+
? undefined
|
|
126
|
+
: "AND(".concat(subject, ">=").concat(a, ",").concat(subject, "<=").concat(b, ")");
|
|
127
|
+
case "notBetween":
|
|
128
|
+
return b === undefined
|
|
129
|
+
? undefined
|
|
130
|
+
: "OR(".concat(subject, "<").concat(a, ",").concat(subject, ">").concat(b, ")");
|
|
131
|
+
case "equal":
|
|
132
|
+
return "".concat(subject, "=").concat(a);
|
|
133
|
+
case "notEqual":
|
|
134
|
+
return "".concat(subject, "<>").concat(a);
|
|
135
|
+
case "greaterThan":
|
|
136
|
+
return "".concat(subject, ">").concat(a);
|
|
137
|
+
case "lessThan":
|
|
138
|
+
return "".concat(subject, "<").concat(a);
|
|
139
|
+
case "greaterThanOrEqual":
|
|
140
|
+
return "".concat(subject, ">=").concat(a);
|
|
141
|
+
case "lessThanOrEqual":
|
|
142
|
+
return "".concat(subject, "<=").concat(a);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Build the `condition` + `xlsxSource` pair for an Excel kind. Returns
|
|
147
|
+
* `undefined` when a required bound is missing (the editor keeps the form
|
|
148
|
+
* open in that case).
|
|
149
|
+
*/
|
|
150
|
+
var buildExcelValidationRule = function (spec, anchor) {
|
|
151
|
+
var formula1 = toBoundFormula(spec.kind, spec.formula1);
|
|
152
|
+
var formula2 = toBoundFormula(spec.kind, spec.formula2);
|
|
153
|
+
if (!formula1)
|
|
154
|
+
return undefined;
|
|
155
|
+
if ((0, exports.isTwoSidedOperator)(spec.operator) && !formula2)
|
|
156
|
+
return undefined;
|
|
157
|
+
var xlsxSource = tslib_1.__assign({ type: spec.kind, operator: spec.operator, formula1: formula1 }, ((0, exports.isTwoSidedOperator)(spec.operator) ? { formula2: formula2 } : {}));
|
|
158
|
+
if (spec.kind === "time") {
|
|
159
|
+
var condition = {
|
|
160
|
+
type: NUMBER_CONDITION_BY_OPERATOR[spec.operator],
|
|
161
|
+
values: tslib_1.__spreadArray([
|
|
162
|
+
{
|
|
163
|
+
userEnteredValue: /^[A-Za-z$'!]/.test(formula1)
|
|
164
|
+
? "=".concat(formula1)
|
|
165
|
+
: formula1,
|
|
166
|
+
}
|
|
167
|
+
], tslib_1.__read(((0, exports.isTwoSidedOperator)(spec.operator) && formula2
|
|
168
|
+
? [
|
|
169
|
+
{
|
|
170
|
+
userEnteredValue: /^[A-Za-z$'!]/.test(formula2)
|
|
171
|
+
? "=".concat(formula2)
|
|
172
|
+
: formula2,
|
|
173
|
+
},
|
|
174
|
+
]
|
|
175
|
+
: [])), false),
|
|
176
|
+
};
|
|
177
|
+
return { condition: condition, xlsxSource: xlsxSource };
|
|
178
|
+
}
|
|
179
|
+
var subject = spec.kind === "textLength" ? "LEN(".concat(anchor, ")") : anchor;
|
|
180
|
+
var body = comparison(subject, spec.operator, formula1, formula2);
|
|
181
|
+
if (!body)
|
|
182
|
+
return undefined;
|
|
183
|
+
var formula = spec.kind === "whole"
|
|
184
|
+
? "=AND(ISNUMBER(".concat(anchor, "),").concat(anchor, "=INT(").concat(anchor, "),").concat(body, ")")
|
|
185
|
+
: "=".concat(body);
|
|
186
|
+
return {
|
|
187
|
+
condition: {
|
|
188
|
+
type: "CUSTOM_FORMULA",
|
|
189
|
+
values: [{ userEnteredValue: formula }],
|
|
190
|
+
},
|
|
191
|
+
xlsxSource: xlsxSource,
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
exports.buildExcelValidationRule = buildExcelValidationRule;
|
|
195
|
+
/**
|
|
196
|
+
* Read an Excel kind back from a rule's `xlsxSource` (imported or authored
|
|
197
|
+
* here); `undefined` for every other rule. Time bounds come back as
|
|
198
|
+
* `h:mm AM/PM` text so the editor shows what the user would type.
|
|
199
|
+
*/
|
|
200
|
+
var readExcelValidationSpec = function (rule) {
|
|
201
|
+
var source = rule === null || rule === void 0 ? void 0 : rule.xlsxSource;
|
|
202
|
+
if (!source || !EXCEL_KINDS.has(source.type))
|
|
203
|
+
return undefined;
|
|
204
|
+
var operator = (source.operator && EXCEL_OPERATORS.has(source.operator)
|
|
205
|
+
? source.operator
|
|
206
|
+
: "between");
|
|
207
|
+
var kind = source.type;
|
|
208
|
+
var display = function (formula) {
|
|
209
|
+
if (formula === undefined || formula === "")
|
|
210
|
+
return undefined;
|
|
211
|
+
var asNumber = Number(formula);
|
|
212
|
+
if (Number.isFinite(asNumber)) {
|
|
213
|
+
return kind === "time" ? (0, exports.formatTimeSerial)(asNumber) : formula;
|
|
214
|
+
}
|
|
215
|
+
return "=".concat(formula);
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
kind: kind,
|
|
219
|
+
operator: operator,
|
|
220
|
+
formula1: display(source.formula1),
|
|
221
|
+
formula2: display(source.formula2),
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
exports.readExcelValidationSpec = readExcelValidationSpec;
|
|
225
|
+
var compare = function (subject, operator, a, b) {
|
|
226
|
+
switch (operator) {
|
|
227
|
+
case "between":
|
|
228
|
+
return (b !== undefined &&
|
|
229
|
+
subject >= Math.min(a, b) &&
|
|
230
|
+
subject <= Math.max(a, b));
|
|
231
|
+
case "notBetween":
|
|
232
|
+
return (b !== undefined &&
|
|
233
|
+
(subject < Math.min(a, b) || subject > Math.max(a, b)));
|
|
234
|
+
case "equal":
|
|
235
|
+
return subject === a;
|
|
236
|
+
case "notEqual":
|
|
237
|
+
return subject !== a;
|
|
238
|
+
case "greaterThan":
|
|
239
|
+
return subject > a;
|
|
240
|
+
case "lessThan":
|
|
241
|
+
return subject < a;
|
|
242
|
+
case "greaterThanOrEqual":
|
|
243
|
+
return subject >= a;
|
|
244
|
+
case "lessThanOrEqual":
|
|
245
|
+
return subject <= a;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
/** A typed value that is a formula (`=…`) — validated on its result, not its text. */
|
|
249
|
+
var isFormulaEntry = function (value) {
|
|
250
|
+
return typeof value === "string" && value.trim().startsWith("=");
|
|
251
|
+
};
|
|
252
|
+
exports.isFormulaEntry = isFormulaEntry;
|
|
253
|
+
/**
|
|
254
|
+
* Decide an Excel-kind rule for a freshly typed value without the
|
|
255
|
+
* calculator — the commit-time enforcement Excel applies (`stop` blocks a
|
|
256
|
+
* decimal in a whole-number cell). `undefined` when the bounds are
|
|
257
|
+
* formulas / references, or when the typed value is itself a formula
|
|
258
|
+
* (Excel validates the formula's RESULT after the recalc, so `=5` is a
|
|
259
|
+
* valid whole number), so the caller falls back to the flag-only marker.
|
|
260
|
+
*/
|
|
261
|
+
var checkExcelValidationValue = function (rule, value) {
|
|
262
|
+
var _a;
|
|
263
|
+
var source = rule === null || rule === void 0 ? void 0 : rule.xlsxSource;
|
|
264
|
+
if (!source || !EXCEL_KINDS.has(source.type))
|
|
265
|
+
return undefined;
|
|
266
|
+
if ((0, exports.isFormulaEntry)(value))
|
|
267
|
+
return undefined;
|
|
268
|
+
var operator = ((_a = source.operator) !== null && _a !== void 0 ? _a : "between");
|
|
269
|
+
if (!EXCEL_OPERATORS.has(operator))
|
|
270
|
+
return undefined;
|
|
271
|
+
var a = Number(source.formula1);
|
|
272
|
+
var b = source.formula2 === undefined ? undefined : Number(source.formula2);
|
|
273
|
+
if (!Number.isFinite(a))
|
|
274
|
+
return undefined;
|
|
275
|
+
if ((0, exports.isTwoSidedOperator)(operator) &&
|
|
276
|
+
!(b !== undefined && Number.isFinite(b))) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
var text = typeof value === "boolean" ? (value ? "TRUE" : "FALSE") : value;
|
|
280
|
+
switch (source.type) {
|
|
281
|
+
case "textLength":
|
|
282
|
+
return compare(Array.from(text).length, operator, a, b);
|
|
283
|
+
case "whole": {
|
|
284
|
+
if (typeof value === "boolean")
|
|
285
|
+
return false;
|
|
286
|
+
var num = Number(text.trim());
|
|
287
|
+
if (!Number.isFinite(num) || !Number.isInteger(num))
|
|
288
|
+
return false;
|
|
289
|
+
return compare(num, operator, a, b);
|
|
290
|
+
}
|
|
291
|
+
case "time": {
|
|
292
|
+
if (typeof value === "boolean")
|
|
293
|
+
return false;
|
|
294
|
+
// Excel compares the cell's full serial against the bounds (a
|
|
295
|
+
// date-time such as 45292.5 is NOT between 9:00 and 12:00) — the same
|
|
296
|
+
// comparison the composed `NUMBER_*` condition makes for the marker.
|
|
297
|
+
var serial = (0, exports.parseTimeToSerial)(text);
|
|
298
|
+
if (serial === undefined)
|
|
299
|
+
return false;
|
|
300
|
+
return compare(serial, operator, a, b);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
exports.checkExcelValidationValue = checkExcelValidationValue;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { GridRange } from "@rowsncolumns/grid";
|
|
2
|
+
import type { CellData } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Excel's Go To Special categories that can be decided from cell data alone.
|
|
5
|
+
* (Row / column differences, precedents / dependents and objects are served
|
|
6
|
+
* by the trace arrows and the drawing layer, not by this dialog.)
|
|
7
|
+
*/
|
|
8
|
+
export type GoToSpecialKind = "blanks" | "constants" | "formulas" | "errors" | "notes" | "visible";
|
|
9
|
+
export declare const GO_TO_SPECIAL_OPTIONS: ReadonlyArray<{
|
|
10
|
+
kind: GoToSpecialKind;
|
|
11
|
+
label: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type GoToSpecialContext<T extends CellData = CellData> = {
|
|
14
|
+
sheetId: number;
|
|
15
|
+
/** The area to scan — the current selection, or the used range for a single cell. */
|
|
16
|
+
range: GridRange;
|
|
17
|
+
getCellData(sheetId: number, rowIndex: number, columnIndex: number): T | null | undefined;
|
|
18
|
+
isRowHidden?(rowIndex: number): boolean;
|
|
19
|
+
isColumnHidden?(columnIndex: number): boolean;
|
|
20
|
+
/** Scan guard — Excel refuses huge Go To Special selections as well. */
|
|
21
|
+
maxCells?: number;
|
|
22
|
+
};
|
|
23
|
+
/** Cells scanned before the search gives up (returns what it found so far). */
|
|
24
|
+
export declare const GO_TO_SPECIAL_MAX_CELLS = 250000;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a Go To Special category to the matching cells of `range`, packed
|
|
27
|
+
* into per-row runs of adjacent columns (what the selection layer paints).
|
|
28
|
+
* Hidden rows / columns are skipped for every kind — Excel never selects
|
|
29
|
+
* into a hidden cell from Go To Special. Returns `[]` when nothing matches.
|
|
30
|
+
*/
|
|
31
|
+
export declare const resolveGoToSpecial: <T extends CellData>(kind: GoToSpecialKind, context: GoToSpecialContext<T>) => GridRange[];
|
|
32
|
+
//# sourceMappingURL=go-to-special.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"go-to-special.d.ts","sourceRoot":"","sources":["../../../api/go-to-special.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,WAAW,GACX,UAAU,GACV,QAAQ,GACR,OAAO,GACP,SAAS,CAAC;AAEd,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC;IAChD,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAOA,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CACT,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACxB,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC,cAAc,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAmD/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,QAAQ,EACnD,MAAM,eAAe,EACrB,SAAS,kBAAkB,CAAC,CAAC,CAAC,KAC7B,SAAS,EAoCX,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveGoToSpecial = exports.GO_TO_SPECIAL_MAX_CELLS = exports.GO_TO_SPECIAL_OPTIONS = void 0;
|
|
4
|
+
exports.GO_TO_SPECIAL_OPTIONS = [
|
|
5
|
+
{ kind: "notes", label: "Notes" },
|
|
6
|
+
{ kind: "constants", label: "Constants" },
|
|
7
|
+
{ kind: "formulas", label: "Formulas" },
|
|
8
|
+
{ kind: "errors", label: "Errors" },
|
|
9
|
+
{ kind: "blanks", label: "Blanks" },
|
|
10
|
+
{ kind: "visible", label: "Visible cells only" },
|
|
11
|
+
];
|
|
12
|
+
/** Cells scanned before the search gives up (returns what it found so far). */
|
|
13
|
+
exports.GO_TO_SPECIAL_MAX_CELLS = 250000;
|
|
14
|
+
var hasContent = function (cell) {
|
|
15
|
+
var _a;
|
|
16
|
+
var ue = (_a = cell === null || cell === void 0 ? void 0 : cell.ue) !== null && _a !== void 0 ? _a : cell === null || cell === void 0 ? void 0 : cell.userEnteredValue;
|
|
17
|
+
if (!ue)
|
|
18
|
+
return false;
|
|
19
|
+
return (ue.fv != null ||
|
|
20
|
+
ue.formulaValue != null ||
|
|
21
|
+
ue.sv != null ||
|
|
22
|
+
ue.stringValue != null ||
|
|
23
|
+
ue.nv != null ||
|
|
24
|
+
ue.numberValue != null ||
|
|
25
|
+
ue.bv != null ||
|
|
26
|
+
ue.boolValue != null);
|
|
27
|
+
};
|
|
28
|
+
var isFormula = function (cell) {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
var ue = (_a = cell === null || cell === void 0 ? void 0 : cell.ue) !== null && _a !== void 0 ? _a : cell === null || cell === void 0 ? void 0 : cell.userEnteredValue;
|
|
31
|
+
return typeof ((_b = ue === null || ue === void 0 ? void 0 : ue.fv) !== null && _b !== void 0 ? _b : ue === null || ue === void 0 ? void 0 : ue.formulaValue) === "string";
|
|
32
|
+
};
|
|
33
|
+
var isError = function (cell) {
|
|
34
|
+
var _a, _b;
|
|
35
|
+
var ev = (_a = cell === null || cell === void 0 ? void 0 : cell.ev) !== null && _a !== void 0 ? _a : cell === null || cell === void 0 ? void 0 : cell.effectiveValue;
|
|
36
|
+
var error = (_b = ev === null || ev === void 0 ? void 0 : ev.ev) !== null && _b !== void 0 ? _b : ev === null || ev === void 0 ? void 0 : ev.errorValue;
|
|
37
|
+
return !!error && error.type === "Error";
|
|
38
|
+
};
|
|
39
|
+
var hasNote = function (cell) {
|
|
40
|
+
return typeof (cell === null || cell === void 0 ? void 0 : cell.note) === "string" && cell.note.trim().length > 0;
|
|
41
|
+
};
|
|
42
|
+
var matches = function (kind, cell) {
|
|
43
|
+
switch (kind) {
|
|
44
|
+
case "blanks":
|
|
45
|
+
return !hasContent(cell);
|
|
46
|
+
case "constants":
|
|
47
|
+
return hasContent(cell) && !isFormula(cell);
|
|
48
|
+
case "formulas":
|
|
49
|
+
return isFormula(cell);
|
|
50
|
+
case "errors":
|
|
51
|
+
return isError(cell);
|
|
52
|
+
case "notes":
|
|
53
|
+
return hasNote(cell);
|
|
54
|
+
case "visible":
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a Go To Special category to the matching cells of `range`, packed
|
|
60
|
+
* into per-row runs of adjacent columns (what the selection layer paints).
|
|
61
|
+
* Hidden rows / columns are skipped for every kind — Excel never selects
|
|
62
|
+
* into a hidden cell from Go To Special. Returns `[]` when nothing matches.
|
|
63
|
+
*/
|
|
64
|
+
var resolveGoToSpecial = function (kind, context) {
|
|
65
|
+
var _a;
|
|
66
|
+
var sheetId = context.sheetId, range = context.range, getCellData = context.getCellData, isRowHidden = context.isRowHidden, isColumnHidden = context.isColumnHidden;
|
|
67
|
+
var maxCells = (_a = context.maxCells) !== null && _a !== void 0 ? _a : exports.GO_TO_SPECIAL_MAX_CELLS;
|
|
68
|
+
var runs = [];
|
|
69
|
+
var scanned = 0;
|
|
70
|
+
var _loop_1 = function (row) {
|
|
71
|
+
if (isRowHidden === null || isRowHidden === void 0 ? void 0 : isRowHidden(row))
|
|
72
|
+
return "continue";
|
|
73
|
+
var runStart;
|
|
74
|
+
var flush = function (endColumn) {
|
|
75
|
+
if (runStart === undefined)
|
|
76
|
+
return;
|
|
77
|
+
runs.push({
|
|
78
|
+
startRowIndex: row,
|
|
79
|
+
endRowIndex: row,
|
|
80
|
+
startColumnIndex: runStart,
|
|
81
|
+
endColumnIndex: endColumn,
|
|
82
|
+
});
|
|
83
|
+
runStart = undefined;
|
|
84
|
+
};
|
|
85
|
+
for (var col = range.startColumnIndex; col <= range.endColumnIndex; col++) {
|
|
86
|
+
if (++scanned > maxCells) {
|
|
87
|
+
flush(col - 1);
|
|
88
|
+
return { value: runs };
|
|
89
|
+
}
|
|
90
|
+
if (isColumnHidden === null || isColumnHidden === void 0 ? void 0 : isColumnHidden(col)) {
|
|
91
|
+
flush(col - 1);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (matches(kind, getCellData(sheetId, row, col))) {
|
|
95
|
+
if (runStart === undefined)
|
|
96
|
+
runStart = col;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
flush(col - 1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
flush(range.endColumnIndex);
|
|
103
|
+
};
|
|
104
|
+
for (var row = range.startRowIndex; row <= range.endRowIndex; row++) {
|
|
105
|
+
var state_1 = _loop_1(row);
|
|
106
|
+
if (typeof state_1 === "object")
|
|
107
|
+
return state_1.value;
|
|
108
|
+
}
|
|
109
|
+
return runs;
|
|
110
|
+
};
|
|
111
|
+
exports.resolveGoToSpecial = resolveGoToSpecial;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { DataValidationRule, ErrorValue } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* The data-validation part of a cell tooltip (both engines share it):
|
|
5
|
+
*
|
|
6
|
+
* - a cell flagged **Invalid** shows the rule's own error alert title/body
|
|
7
|
+
* (Excel's custom error message, #131) — the generic "violates its
|
|
8
|
+
* validation rule" text is only the fallback when the rule has none;
|
|
9
|
+
* - a formula **Error** keeps its own tooltip (nothing here);
|
|
10
|
+
* - otherwise the rule's input message (Excel's "show input message when
|
|
11
|
+
* cell is selected", surfaced on hover here — #130).
|
|
12
|
+
*
|
|
13
|
+
* Returns `undefined` when the rule contributes nothing, so the host's own
|
|
14
|
+
* tooltip content / the generic markers apply.
|
|
15
|
+
*/
|
|
16
|
+
export declare const getDataValidationTooltipContent: (rule: DataValidationRule | null | undefined, error: ErrorValue | null | undefined) => ReactNode | undefined;
|
|
17
|
+
//# sourceMappingURL=validation-tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-tooltip.d.ts","sourceRoot":"","sources":["../../../api/validation-tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAsB/D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,GAC1C,MAAM,kBAAkB,GAAG,IAAI,GAAG,SAAS,EAC3C,OAAO,UAAU,GAAG,IAAI,GAAG,SAAS,KACnC,SAAS,GAAG,SAad,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDataValidationTooltipContent = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
var hasText = function (message) {
|
|
7
|
+
return Boolean(message && (message.title || message.message));
|
|
8
|
+
};
|
|
9
|
+
var renderMessage = function (message, accentClass) { return (react_1.default.createElement("div", { className: "bg-rnc-background text-rnc-foreground p-2 shadow-md rounded-md border-l-4 border-solid ".concat(accentClass, " max-w-[300px] flex flex-col gap-0.5") },
|
|
10
|
+
message.title ? (react_1.default.createElement("div", { className: "font-bold font-system text-sm" }, message.title)) : null,
|
|
11
|
+
message.message ? (react_1.default.createElement("div", { className: "text-xs font-system whitespace-pre-wrap" }, message.message)) : null)); };
|
|
12
|
+
/**
|
|
13
|
+
* The data-validation part of a cell tooltip (both engines share it):
|
|
14
|
+
*
|
|
15
|
+
* - a cell flagged **Invalid** shows the rule's own error alert title/body
|
|
16
|
+
* (Excel's custom error message, #131) — the generic "violates its
|
|
17
|
+
* validation rule" text is only the fallback when the rule has none;
|
|
18
|
+
* - a formula **Error** keeps its own tooltip (nothing here);
|
|
19
|
+
* - otherwise the rule's input message (Excel's "show input message when
|
|
20
|
+
* cell is selected", surfaced on hover here — #130).
|
|
21
|
+
*
|
|
22
|
+
* Returns `undefined` when the rule contributes nothing, so the host's own
|
|
23
|
+
* tooltip content / the generic markers apply.
|
|
24
|
+
*/
|
|
25
|
+
var getDataValidationTooltipContent = function (rule, error) {
|
|
26
|
+
var _a;
|
|
27
|
+
if (!rule)
|
|
28
|
+
return undefined;
|
|
29
|
+
if ((error === null || error === void 0 ? void 0 : error.type) === "Error")
|
|
30
|
+
return undefined;
|
|
31
|
+
if ((error === null || error === void 0 ? void 0 : error.type) === "Invalid") {
|
|
32
|
+
var alertMessage = (_a = rule.alert) === null || _a === void 0 ? void 0 : _a.message;
|
|
33
|
+
return hasText(alertMessage)
|
|
34
|
+
? renderMessage(alertMessage, "border-l-rnc-destructive")
|
|
35
|
+
: undefined;
|
|
36
|
+
}
|
|
37
|
+
var inputMessage = rule.inputMessage;
|
|
38
|
+
return hasText(inputMessage)
|
|
39
|
+
? renderMessage(inputMessage, "border-l-blue-700")
|
|
40
|
+
: undefined;
|
|
41
|
+
};
|
|
42
|
+
exports.getDataValidationTooltipContent = getDataValidationTooltipContent;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AdvancedFilterOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Open-state + payload for the Advanced Filter dialog (Excel's Data → Sort &
|
|
4
|
+
* Filter → Advanced). The context menu opens it with the list range around
|
|
5
|
+
* the active cell (A1 text); the host-mounted dialog resolves the typed
|
|
6
|
+
* references and routes the options back through `onApply`.
|
|
7
|
+
*/
|
|
8
|
+
export type AdvancedFilterDialogRequest = {
|
|
9
|
+
/** A1 reference of the default "List range". */
|
|
10
|
+
listRange: string;
|
|
11
|
+
/** Routed to `onAdvancedFilter` by the opener. */
|
|
12
|
+
onApply: (options: AdvancedFilterOptions) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare const advancedFilterDialogAtom: import("jotai").PrimitiveAtom<AdvancedFilterDialogRequest | null> & {
|
|
15
|
+
init: AdvancedFilterDialogRequest | null;
|
|
16
|
+
};
|
|
17
|
+
export declare const useAdvancedFilterDialogState: () => [AdvancedFilterDialogRequest | null, (args_0: AdvancedFilterDialogRequest | ((prev: AdvancedFilterDialogRequest | null) => AdvancedFilterDialogRequest | null) | null) => void];
|
|
18
|
+
/** Number of mounted `AdvancedFilterDialogHost`s — the menu item is offered only while > 0. */
|
|
19
|
+
export declare const advancedFilterDialogHostCountAtom: import("jotai").PrimitiveAtom<number> & {
|
|
20
|
+
init: number;
|
|
21
|
+
};
|
|
22
|
+
/** True when at least one `AdvancedFilterDialogHost` is mounted. */
|
|
23
|
+
export declare const useIsAdvancedFilterDialogMounted: () => boolean;
|
|
24
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
25
|
+
export declare const useRegisterAdvancedFilterDialogHost: () => void;
|
|
26
|
+
/** Returns a function that opens the host-mounted Advanced Filter dialog. */
|
|
27
|
+
export declare const useOpenAdvancedFilterDialog: () => (request: AdvancedFilterDialogRequest) => void;
|
|
28
|
+
//# sourceMappingURL=advanced-filter-dialog.atom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced-filter-dialog.atom.d.ts","sourceRoot":"","sources":["../../../atoms/advanced-filter-dialog.atom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,wBAAwB;;CAEpC,CAAC;AAEF,eAAO,MAAM,4BAA4B,uLACN,CAAC;AAEpC,+FAA+F;AAC/F,eAAO,MAAM,iCAAiC;;CAAU,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,gCAAgC,eACQ,CAAC;AAEtD,yEAAyE;AACzE,eAAO,MAAM,mCAAmC,YAM/C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,kBAG1B,2BAA2B,SAGxC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOpenAdvancedFilterDialog = exports.useRegisterAdvancedFilterDialogHost = exports.useIsAdvancedFilterDialogMounted = exports.advancedFilterDialogHostCountAtom = exports.useAdvancedFilterDialogState = exports.advancedFilterDialogAtom = void 0;
|
|
4
|
+
var jotai_1 = require("jotai");
|
|
5
|
+
var react_1 = require("react");
|
|
6
|
+
exports.advancedFilterDialogAtom = (0, jotai_1.atom)(null);
|
|
7
|
+
var useAdvancedFilterDialogState = function () {
|
|
8
|
+
return (0, jotai_1.useAtom)(exports.advancedFilterDialogAtom);
|
|
9
|
+
};
|
|
10
|
+
exports.useAdvancedFilterDialogState = useAdvancedFilterDialogState;
|
|
11
|
+
/** Number of mounted `AdvancedFilterDialogHost`s — the menu item is offered only while > 0. */
|
|
12
|
+
exports.advancedFilterDialogHostCountAtom = (0, jotai_1.atom)(0);
|
|
13
|
+
/** True when at least one `AdvancedFilterDialogHost` is mounted. */
|
|
14
|
+
var useIsAdvancedFilterDialogMounted = function () {
|
|
15
|
+
return (0, jotai_1.useAtomValue)(exports.advancedFilterDialogHostCountAtom) > 0;
|
|
16
|
+
};
|
|
17
|
+
exports.useIsAdvancedFilterDialogMounted = useIsAdvancedFilterDialogMounted;
|
|
18
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
19
|
+
var useRegisterAdvancedFilterDialogHost = function () {
|
|
20
|
+
var setCount = (0, jotai_1.useSetAtom)(exports.advancedFilterDialogHostCountAtom);
|
|
21
|
+
(0, react_1.useEffect)(function () {
|
|
22
|
+
setCount(function (count) { return count + 1; });
|
|
23
|
+
return function () { return setCount(function (count) { return count - 1; }); };
|
|
24
|
+
}, [setCount]);
|
|
25
|
+
};
|
|
26
|
+
exports.useRegisterAdvancedFilterDialogHost = useRegisterAdvancedFilterDialogHost;
|
|
27
|
+
/** Returns a function that opens the host-mounted Advanced Filter dialog. */
|
|
28
|
+
var useOpenAdvancedFilterDialog = function () {
|
|
29
|
+
var setRequest = (0, jotai_1.useSetAtom)(exports.advancedFilterDialogAtom);
|
|
30
|
+
return (0, react_1.useCallback)(function (request) { return setRequest(request); }, [setRequest]);
|
|
31
|
+
};
|
|
32
|
+
exports.useOpenAdvancedFilterDialog = useOpenAdvancedFilterDialog;
|