@rowsncolumns/utils 9.5.0 → 9.5.3
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/clipboard-html-sanitize.d.ts +15 -0
- package/dist/cjs/clipboard-html-sanitize.d.ts.map +1 -0
- package/dist/cjs/clipboard-html-sanitize.js +237 -0
- package/dist/cjs/condition-operand.d.ts +54 -0
- package/dist/cjs/condition-operand.d.ts.map +1 -0
- package/dist/cjs/condition-operand.js +223 -0
- package/dist/cjs/conditional-format.d.ts.map +1 -1
- package/dist/cjs/conditional-format.js +5 -59
- package/dist/cjs/data-validation.d.ts +9 -0
- package/dist/cjs/data-validation.d.ts.map +1 -1
- package/dist/cjs/data-validation.js +29 -60
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/rich-text-html.d.ts +9 -0
- package/dist/cjs/rich-text-html.d.ts.map +1 -1
- package/dist/cjs/rich-text-html.js +52 -23
- package/dist/cjs/sheet-names.d.ts +60 -0
- package/dist/cjs/sheet-names.d.ts.map +1 -0
- package/dist/cjs/sheet-names.js +157 -0
- package/dist/cjs/sheet-protection.d.ts +9 -0
- package/dist/cjs/sheet-protection.d.ts.map +1 -0
- package/dist/cjs/sheet-protection.js +11 -0
- package/dist/cjs/string.d.ts +31 -0
- package/dist/cjs/string.d.ts.map +1 -1
- package/dist/cjs/string.js +73 -3
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/clipboard-html-sanitize.d.ts +15 -0
- package/dist/esm/clipboard-html-sanitize.d.ts.map +1 -0
- package/dist/esm/clipboard-html-sanitize.js +192 -0
- package/dist/esm/condition-operand.d.ts +54 -0
- package/dist/esm/condition-operand.d.ts.map +1 -0
- package/dist/esm/condition-operand.js +203 -0
- package/dist/esm/conditional-format.d.ts.map +1 -1
- package/dist/esm/conditional-format.js +5 -57
- package/dist/esm/data-validation.d.ts +9 -0
- package/dist/esm/data-validation.d.ts.map +1 -1
- package/dist/esm/data-validation.js +27 -57
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/rich-text-html.d.ts +9 -0
- package/dist/esm/rich-text-html.d.ts.map +1 -1
- package/dist/esm/rich-text-html.js +20 -4
- package/dist/esm/sheet-names.d.ts +60 -0
- package/dist/esm/sheet-names.d.ts.map +1 -0
- package/dist/esm/sheet-names.js +109 -0
- package/dist/esm/sheet-protection.d.ts +9 -0
- package/dist/esm/sheet-protection.d.ts.map +1 -0
- package/dist/esm/sheet-protection.js +8 -0
- package/dist/esm/string.d.ts +31 -0
- package/dist/esm/string.d.ts.map +1 -1
- package/dist/esm/string.js +62 -2
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<img src>` may point at http(s) or an inline `data:image/*` (browsers copy
|
|
3
|
+
* inline images that way). Anything else — `javascript:`, `data:text/html`,
|
|
4
|
+
* `blob:` from another origin — is dropped.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isSafeImageSrc: (value: string | null | undefined) => boolean;
|
|
7
|
+
export declare const sanitizeInlineStyle: (value: string) => string;
|
|
8
|
+
export declare const sanitizeStylesheetText: (value: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Strip executable and network-reaching content from an inert (DOMParser)
|
|
11
|
+
* document in place. Safe to call on a document that was never attached; the
|
|
12
|
+
* caller imports the result into a live sandbox afterwards.
|
|
13
|
+
*/
|
|
14
|
+
export declare const sanitizeClipboardDocument: (doc: Document) => Document;
|
|
15
|
+
//# sourceMappingURL=clipboard-html-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard-html-sanitize.d.ts","sourceRoot":"","sources":["../../clipboard-html-sanitize.ts"],"names":[],"mappings":"AA6FA;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAWjE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAanD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,KAAG,MACT,CAAC;AAmC/C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,KAAK,QAAQ,KAAG,QAyCzD,CAAC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeClipboardDocument = exports.sanitizeStylesheetText = exports.sanitizeInlineStyle = exports.isSafeImageSrc = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var string_1 = require("./string");
|
|
6
|
+
/**
|
|
7
|
+
* Sanitizer for clipboard `text/html` before any of it touches the live DOM.
|
|
8
|
+
*
|
|
9
|
+
* Clipboard HTML is attacker-controlled: a page the user copied from (or a
|
|
10
|
+
* synthetic `paste` event) can carry `<img onerror>`, `<svg onload>`,
|
|
11
|
+
* `<script>`, `<iframe srcdoc>` or `javascript:` links. The paste converters
|
|
12
|
+
* only ever need the document's structure (tables, rows, cells, inline
|
|
13
|
+
* formatting tags, `data-*` value hints), its inline `style` attributes and
|
|
14
|
+
* the payload's own `<style>` rules (Excel's `.fontN` / `.xlNN` classes), so
|
|
15
|
+
* everything else is dropped from the INERT `DOMParser` document before the
|
|
16
|
+
* sanitized nodes are imported into the mounted sandbox.
|
|
17
|
+
*
|
|
18
|
+
* This runs unconditionally; a consumer's `sanitizeHtml` prop runs on the raw
|
|
19
|
+
* string before this and can only tighten the result.
|
|
20
|
+
*/
|
|
21
|
+
/** Elements removed with their subtree. */
|
|
22
|
+
var FORBIDDEN_ELEMENTS = new Set([
|
|
23
|
+
"script",
|
|
24
|
+
"noscript",
|
|
25
|
+
"template",
|
|
26
|
+
"iframe",
|
|
27
|
+
"frame",
|
|
28
|
+
"frameset",
|
|
29
|
+
"object",
|
|
30
|
+
"embed",
|
|
31
|
+
"applet",
|
|
32
|
+
"link",
|
|
33
|
+
"base",
|
|
34
|
+
"svg",
|
|
35
|
+
"math",
|
|
36
|
+
"input",
|
|
37
|
+
"button",
|
|
38
|
+
"select",
|
|
39
|
+
"textarea",
|
|
40
|
+
"audio",
|
|
41
|
+
"video",
|
|
42
|
+
"source",
|
|
43
|
+
"track",
|
|
44
|
+
"canvas",
|
|
45
|
+
"portal",
|
|
46
|
+
"dialog",
|
|
47
|
+
]);
|
|
48
|
+
/** Elements removed but whose children are kept in place. */
|
|
49
|
+
var UNWRAPPED_ELEMENTS = new Set(["form", "fieldset", "label", "output"]);
|
|
50
|
+
/** URL-bearing attributes that never carry spreadsheet meaning. */
|
|
51
|
+
var FORBIDDEN_ATTRIBUTES = new Set([
|
|
52
|
+
"srcdoc",
|
|
53
|
+
"srcset",
|
|
54
|
+
"formaction",
|
|
55
|
+
"action",
|
|
56
|
+
"background",
|
|
57
|
+
"poster",
|
|
58
|
+
"ping",
|
|
59
|
+
"dynsrc",
|
|
60
|
+
"lowsrc",
|
|
61
|
+
"cite",
|
|
62
|
+
"longdesc",
|
|
63
|
+
"manifest",
|
|
64
|
+
"codebase",
|
|
65
|
+
"classid",
|
|
66
|
+
"data",
|
|
67
|
+
"archive",
|
|
68
|
+
"profile",
|
|
69
|
+
"usemap",
|
|
70
|
+
"xlink:href",
|
|
71
|
+
"xml:base",
|
|
72
|
+
]);
|
|
73
|
+
/** Attributes whose value must be a safe URL; otherwise the attribute is dropped. */
|
|
74
|
+
var URL_ATTRIBUTES = new Set(["href", "src"]);
|
|
75
|
+
/**
|
|
76
|
+
* CSS constructs that can reach the network or (in legacy engines) run code.
|
|
77
|
+
* Any declaration containing one of them is dropped; the rest of the `style`
|
|
78
|
+
* survives so legitimate `font-weight` / `color` / `mso-number-format` hints
|
|
79
|
+
* are kept.
|
|
80
|
+
*/
|
|
81
|
+
var DANGEROUS_CSS = /(expression\s*\(|url\s*\(|@import|behavior\s*:|-moz-binding|javascript:|vbscript:|data:)/i;
|
|
82
|
+
/**
|
|
83
|
+
* Stylesheet-level constructs that can reach the network. `<style>` text is
|
|
84
|
+
* needed for Excel's class rules but never for anything that fetches.
|
|
85
|
+
*/
|
|
86
|
+
var DANGEROUS_STYLESHEET_RULE = /@import[^;]*;?|url\s*\([^)]*\)|expression\s*\([^)]*\)|behavior\s*:[^;}]*|-moz-binding\s*:[^;}]*/gi;
|
|
87
|
+
var isEventHandlerAttribute = function (name) {
|
|
88
|
+
return name.length > 2 && name.charCodeAt(0) === 111 /* o */ && name.charCodeAt(1) === 110;
|
|
89
|
+
} /* n */;
|
|
90
|
+
/**
|
|
91
|
+
* `<img src>` may point at http(s) or an inline `data:image/*` (browsers copy
|
|
92
|
+
* inline images that way). Anything else — `javascript:`, `data:text/html`,
|
|
93
|
+
* `blob:` from another origin — is dropped.
|
|
94
|
+
*/
|
|
95
|
+
var isSafeImageSrc = function (value) {
|
|
96
|
+
if (!value) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
var trimmed = value.replace(/[\t\n\r]/g, "").trim();
|
|
100
|
+
if (/^data:image\/(png|gif|jpe?g|webp|bmp|svg\+xml|x-icon|tiff);/i.test(trimmed)) {
|
|
101
|
+
// Inline SVG can carry scripts when navigated to, but as an `<img>` source
|
|
102
|
+
// it is rendered in an isolated, script-less document; allow it.
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return /^https?:\/\//i.test(trimmed) || /^\/\//.test(trimmed);
|
|
106
|
+
};
|
|
107
|
+
exports.isSafeImageSrc = isSafeImageSrc;
|
|
108
|
+
var sanitizeInlineStyle = function (value) {
|
|
109
|
+
// Untouched styles are kept byte-for-byte: Excel's `mso-number-format`
|
|
110
|
+
// patterns carry escaped `\;` and significant spaces that a split/rejoin
|
|
111
|
+
// would mangle.
|
|
112
|
+
if (!DANGEROUS_CSS.test(value)) {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
return value
|
|
116
|
+
.split(";")
|
|
117
|
+
.filter(function (declaration) { return declaration.trim() && !DANGEROUS_CSS.test(declaration); })
|
|
118
|
+
.join(";");
|
|
119
|
+
};
|
|
120
|
+
exports.sanitizeInlineStyle = sanitizeInlineStyle;
|
|
121
|
+
var sanitizeStylesheetText = function (value) {
|
|
122
|
+
return value.replace(DANGEROUS_STYLESHEET_RULE, "");
|
|
123
|
+
};
|
|
124
|
+
exports.sanitizeStylesheetText = sanitizeStylesheetText;
|
|
125
|
+
var sanitizeAttributes = function (element) {
|
|
126
|
+
var e_1, _a;
|
|
127
|
+
var tag = element.tagName.toLowerCase();
|
|
128
|
+
// Copy first: removing while iterating a live NamedNodeMap skips entries.
|
|
129
|
+
var attributes = Array.from(element.attributes);
|
|
130
|
+
try {
|
|
131
|
+
for (var attributes_1 = tslib_1.__values(attributes), attributes_1_1 = attributes_1.next(); !attributes_1_1.done; attributes_1_1 = attributes_1.next()) {
|
|
132
|
+
var attribute = attributes_1_1.value;
|
|
133
|
+
var name_1 = attribute.name.toLowerCase();
|
|
134
|
+
if (isEventHandlerAttribute(name_1) || FORBIDDEN_ATTRIBUTES.has(name_1)) {
|
|
135
|
+
element.removeAttribute(attribute.name);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (name_1 === "style") {
|
|
139
|
+
var safe = (0, exports.sanitizeInlineStyle)(attribute.value);
|
|
140
|
+
if (safe !== attribute.value) {
|
|
141
|
+
if (safe) {
|
|
142
|
+
element.setAttribute(attribute.name, safe);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
element.removeAttribute(attribute.name);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (URL_ATTRIBUTES.has(name_1)) {
|
|
151
|
+
var safe = name_1 === "src"
|
|
152
|
+
? tag === "img" && (0, exports.isSafeImageSrc)(attribute.value)
|
|
153
|
+
: (0, string_1.isSafeHyperlinkUri)(attribute.value);
|
|
154
|
+
if (!safe) {
|
|
155
|
+
element.removeAttribute(attribute.name);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
161
|
+
finally {
|
|
162
|
+
try {
|
|
163
|
+
if (attributes_1_1 && !attributes_1_1.done && (_a = attributes_1.return)) _a.call(attributes_1);
|
|
164
|
+
}
|
|
165
|
+
finally { if (e_1) throw e_1.error; }
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Strip executable and network-reaching content from an inert (DOMParser)
|
|
170
|
+
* document in place. Safe to call on a document that was never attached; the
|
|
171
|
+
* caller imports the result into a live sandbox afterwards.
|
|
172
|
+
*/
|
|
173
|
+
var sanitizeClipboardDocument = function (doc) {
|
|
174
|
+
var e_2, _a, e_3, _b;
|
|
175
|
+
var _c;
|
|
176
|
+
// A static snapshot: removals below detach subtrees, and detached
|
|
177
|
+
// descendants are skipped via `doc.contains`.
|
|
178
|
+
var elements = Array.from(doc.querySelectorAll("*"));
|
|
179
|
+
try {
|
|
180
|
+
for (var elements_1 = tslib_1.__values(elements), elements_1_1 = elements_1.next(); !elements_1_1.done; elements_1_1 = elements_1.next()) {
|
|
181
|
+
var element = elements_1_1.value;
|
|
182
|
+
if (!doc.contains(element)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
var tag = element.tagName.toLowerCase();
|
|
186
|
+
if (FORBIDDEN_ELEMENTS.has(tag)) {
|
|
187
|
+
element.remove();
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (UNWRAPPED_ELEMENTS.has(tag)) {
|
|
191
|
+
var parent_1 = element.parentNode;
|
|
192
|
+
if (parent_1) {
|
|
193
|
+
while (element.firstChild) {
|
|
194
|
+
parent_1.insertBefore(element.firstChild, element);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
element.remove();
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (tag === "style") {
|
|
201
|
+
var text = (_c = element.textContent) !== null && _c !== void 0 ? _c : "";
|
|
202
|
+
var safe = (0, exports.sanitizeStylesheetText)(text);
|
|
203
|
+
if (safe !== text) {
|
|
204
|
+
element.textContent = safe;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
// A stylesheet never needs attributes beyond `type`/`media`.
|
|
208
|
+
for (var _d = (e_3 = void 0, tslib_1.__values(Array.from(element.attributes))), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
209
|
+
var attribute = _e.value;
|
|
210
|
+
var name_2 = attribute.name.toLowerCase();
|
|
211
|
+
if (name_2 !== "type" && name_2 !== "media") {
|
|
212
|
+
element.removeAttribute(attribute.name);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
217
|
+
finally {
|
|
218
|
+
try {
|
|
219
|
+
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
|
|
220
|
+
}
|
|
221
|
+
finally { if (e_3) throw e_3.error; }
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
sanitizeAttributes(element);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
229
|
+
finally {
|
|
230
|
+
try {
|
|
231
|
+
if (elements_1_1 && !elements_1_1.done && (_a = elements_1.return)) _a.call(elements_1);
|
|
232
|
+
}
|
|
233
|
+
finally { if (e_2) throw e_2.error; }
|
|
234
|
+
}
|
|
235
|
+
return doc;
|
|
236
|
+
};
|
|
237
|
+
exports.sanitizeClipboardDocument = sanitizeClipboardDocument;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ConditionType } from "@rowsncolumns/common-types";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a condition type compares TEXT — its operand is always a string
|
|
4
|
+
* literal in Excel, even when it looks like a number or a boolean.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isTextConditionType: (type: ConditionType | string | undefined | null) => boolean;
|
|
7
|
+
export declare const escapeConditionFormulaText: (value: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* The formula fragment for an operand that is a typed LITERAL — a number
|
|
10
|
+
* (`5`, `1e3`, locale `6,5`), a percentage (`50%` → `0.5`), a boolean, or a
|
|
11
|
+
* date / time in the document locale (`2024-01-05`, `1/5/2024`, `10:30` → the
|
|
12
|
+
* serial). `undefined` for anything else (text, references, formulas).
|
|
13
|
+
*/
|
|
14
|
+
export declare const conditionOperandLiteral: (raw: string | undefined | null, locale?: string) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Whether an operand (with any leading `=` already removed) is an A1
|
|
17
|
+
* reference — a cell, a range, a whole column/row, with `$` anchors and an
|
|
18
|
+
* optional (quoted) sheet qualifier: `B1`, `$B$1`, `B$1`, `Sheet1!$B$1`,
|
|
19
|
+
* `'My Sheet'!$B$1:$C$9`, `$A:$A`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const isConditionOperandReference: (text: string) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether an operand (with any leading `=` already removed) is a function
|
|
24
|
+
* call — the form OOXML stores a formula bound in (`TODAY()`,
|
|
25
|
+
* `DATE(2024,1,1)`, `LEN($B$1)`): evaluated as a formula, never quoted.
|
|
26
|
+
*/
|
|
27
|
+
export declare const isConditionOperandFormulaCall: (text: string) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Normalize one rule operand into the formula fragment the rule builders
|
|
30
|
+
* splice into `=<cell><op><fragment>` (and the xlsx exporters write inside
|
|
31
|
+
* `<formula>` / `<formula1>` — never with a leading `=`).
|
|
32
|
+
*
|
|
33
|
+
* - `=…` → the formula text (leading `=` stripped).
|
|
34
|
+
* - `TEXT_*` types → ALWAYS a quoted string literal (`"1"`, `"TRUE"`).
|
|
35
|
+
* - A complete `"…"` string literal → as is.
|
|
36
|
+
* - An A1 reference (`$B$1`, `B$1`, `Sheet1!$B$1`, `'My Sheet'!$B$1`) → as is.
|
|
37
|
+
* - A function call (`TODAY()`, `DATE(2024,1,1)`, `LEN(A1)`) → as is.
|
|
38
|
+
* - A typed literal (number, `50%`, boolean, date/time in `locale`) → its
|
|
39
|
+
* numeric form (`0.5`, `45296`, `TRUE`).
|
|
40
|
+
* - Anything else → a quoted string literal (`"abc"`).
|
|
41
|
+
*
|
|
42
|
+
* `""` for an empty operand.
|
|
43
|
+
*/
|
|
44
|
+
export declare const normalizeConditionOperand: (raw: string | undefined | null, type: ConditionType | string | undefined | null, locale?: string) => string;
|
|
45
|
+
/**
|
|
46
|
+
* The OOXML `<formula>` / `<formula1>` text for a number/date rule operand:
|
|
47
|
+
* the leading `=` is stripped (Excel never writes one), typed literals are
|
|
48
|
+
* normalized to what Excel itself stores (`50%` → `0.5`, `2024-01-05` → its
|
|
49
|
+
* serial `45296` rather than the arithmetic `2024-01-05` = 2022), and every
|
|
50
|
+
* other operand — references, defined names, function calls — is written
|
|
51
|
+
* verbatim.
|
|
52
|
+
*/
|
|
53
|
+
export declare const conditionOperandToFormulaText: (raw: string | undefined | null, locale?: string) => string;
|
|
54
|
+
//# sourceMappingURL=condition-operand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"condition-operand.d.ts","sourceRoot":"","sources":["../../condition-operand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAyChE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,KAC9C,OAA0D,CAAC;AAE9D,eAAO,MAAM,0BAA0B,GAAI,OAAO,MAAM,WAC7B,CAAC;AAsC5B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,SAAS,MAAM,KACd,MAAM,GAAG,SA2CX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,KAAG,OACU,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,KAAG,OAC5B,CAAC;AAElC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,yBAAyB,GACpC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,EAC/C,SAAS,MAAM,KACd,MA0BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,GACxC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,SAAS,MAAM,KACd,MAQF,CAAC"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.conditionOperandToFormulaText = exports.normalizeConditionOperand = exports.isConditionOperandFormulaCall = exports.isConditionOperandReference = exports.conditionOperandLiteral = exports.escapeConditionFormulaText = exports.isTextConditionType = void 0;
|
|
4
|
+
var numfmt_1 = require("numfmt");
|
|
5
|
+
var selection_1 = require("./selection");
|
|
6
|
+
/**
|
|
7
|
+
* How a conditional-format / data-validation rule operand (`userEnteredValue`)
|
|
8
|
+
* becomes a formula fragment — the ONE place both rule builders and both xlsx
|
|
9
|
+
* exporters classify an operand, so the runtime and the exported file agree.
|
|
10
|
+
*
|
|
11
|
+
* Excel's rule dialog stores every operand as a formula. Typed text becomes a
|
|
12
|
+
* string literal, a typed number a number, a typed date its serial, `50%` the
|
|
13
|
+
* fraction `0.5`, and `=…` stays a formula. The `TEXT_*` rules ("Text that
|
|
14
|
+
* contains…", "begins with", cellIs equal `"5"`) take a string literal no
|
|
15
|
+
* matter what was typed: *begins with "1"* highlights `1abc` AND `123`, and
|
|
16
|
+
* `cellIs equal "5"` matches the text `5` but not the number 5.
|
|
17
|
+
*/
|
|
18
|
+
var NUMBER_LITERAL_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
19
|
+
var BOOLEAN_LITERAL_PATTERN = /^(TRUE|FALSE)$/i;
|
|
20
|
+
var PERCENT_LITERAL_PATTERN = /^([+-]?[\d.,\s ]+)%$/;
|
|
21
|
+
/** A complete formula string literal — `"abc"`, `"say ""hi"""`. */
|
|
22
|
+
var QUOTED_STRING_LITERAL_PATTERN = /^"(?:[^"]|"")*"$/;
|
|
23
|
+
/**
|
|
24
|
+
* A function call — `TODAY()`, `DATE(2024,1,1)`, `LEN($B$1)`, `_xlfn.XLOOKUP(…)` — which is how
|
|
25
|
+
* OOXML stores a formula bound (`<formula1>TODAY()</formula1>`, never with a leading `=`).
|
|
26
|
+
*/
|
|
27
|
+
var FORMULA_CALL_PATTERN = /^[A-Z_\\][\w.]*\([\s\S]*\)$/i;
|
|
28
|
+
/** Whole-column / whole-row spans (`A:A`, `$A:$C`, `1:1`), optionally sheet-qualified. */
|
|
29
|
+
var AXIS_REFERENCE_PATTERN = /^(?:[^!]+!)?(?:\$?[A-Z]{1,3}:\$?[A-Z]{1,3}|\$?\d+:\$?\d+)$/i;
|
|
30
|
+
var TEXT_CONDITION_TYPES = new Set([
|
|
31
|
+
"TEXT_EQ",
|
|
32
|
+
"TEXT_NOT_EQ",
|
|
33
|
+
"TEXT_CONTAINS",
|
|
34
|
+
"TEXT_NOT_CONTAINS",
|
|
35
|
+
"TEXT_STARTS_WITH",
|
|
36
|
+
"TEXT_ENDS_WITH",
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Whether a condition type compares TEXT — its operand is always a string
|
|
40
|
+
* literal in Excel, even when it looks like a number or a boolean.
|
|
41
|
+
*/
|
|
42
|
+
var isTextConditionType = function (type) { return Boolean(type && TEXT_CONDITION_TYPES.has(type)); };
|
|
43
|
+
exports.isTextConditionType = isTextConditionType;
|
|
44
|
+
var escapeConditionFormulaText = function (value) {
|
|
45
|
+
return value.replace(/"/g, '""');
|
|
46
|
+
};
|
|
47
|
+
exports.escapeConditionFormulaText = escapeConditionFormulaText;
|
|
48
|
+
/**
|
|
49
|
+
* Parse a locale-formatted numeric string (e.g., "6,5" in fr-FR) into a JS
|
|
50
|
+
* number. Uses `Intl.NumberFormat` to detect decimal/group separators.
|
|
51
|
+
* Returns NaN on failure.
|
|
52
|
+
*/
|
|
53
|
+
var parseLocaleNumberLoose = function (value, locale) {
|
|
54
|
+
var _a, _b, _c, _d;
|
|
55
|
+
try {
|
|
56
|
+
var parts = new Intl.NumberFormat(locale).formatToParts(1234.5);
|
|
57
|
+
var decimal = (_b = (_a = parts.find(function (part) { return part.type === "decimal"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ".";
|
|
58
|
+
var group = (_d = (_c = parts.find(function (part) { return part.type === "group"; })) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : ",";
|
|
59
|
+
var cleaned = value.split(group).join("").replace(decimal, ".");
|
|
60
|
+
if (!cleaned.trim() || !NUMBER_LITERAL_PATTERN.test(cleaned.trim())) {
|
|
61
|
+
return NaN;
|
|
62
|
+
}
|
|
63
|
+
return Number(cleaned);
|
|
64
|
+
}
|
|
65
|
+
catch (_e) {
|
|
66
|
+
return NaN;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** Shortest decimal text of a number rounded to Excel's 15 significant digits. */
|
|
70
|
+
var formatNumberOperand = function (value) {
|
|
71
|
+
return String(Number(value.toPrecision(15)));
|
|
72
|
+
};
|
|
73
|
+
var parseNumberOperand = function (text, locale) {
|
|
74
|
+
if (NUMBER_LITERAL_PATTERN.test(text)) {
|
|
75
|
+
var value = Number(text);
|
|
76
|
+
return Number.isFinite(value) ? value : undefined;
|
|
77
|
+
}
|
|
78
|
+
if (locale) {
|
|
79
|
+
var value = parseLocaleNumberLoose(text, locale);
|
|
80
|
+
return Number.isFinite(value) ? value : undefined;
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* The formula fragment for an operand that is a typed LITERAL — a number
|
|
86
|
+
* (`5`, `1e3`, locale `6,5`), a percentage (`50%` → `0.5`), a boolean, or a
|
|
87
|
+
* date / time in the document locale (`2024-01-05`, `1/5/2024`, `10:30` → the
|
|
88
|
+
* serial). `undefined` for anything else (text, references, formulas).
|
|
89
|
+
*/
|
|
90
|
+
var conditionOperandLiteral = function (raw, locale) {
|
|
91
|
+
if (!raw)
|
|
92
|
+
return undefined;
|
|
93
|
+
var trimmed = raw.trim();
|
|
94
|
+
if (!trimmed)
|
|
95
|
+
return undefined;
|
|
96
|
+
if (BOOLEAN_LITERAL_PATTERN.test(trimmed)) {
|
|
97
|
+
return trimmed.toUpperCase();
|
|
98
|
+
}
|
|
99
|
+
if (NUMBER_LITERAL_PATTERN.test(trimmed)) {
|
|
100
|
+
// Keep the typed digits — `6.50` and `6.5` are the same number and the
|
|
101
|
+
// formula parser reads either.
|
|
102
|
+
return Number.isFinite(Number(trimmed)) ? trimmed : undefined;
|
|
103
|
+
}
|
|
104
|
+
var percent = PERCENT_LITERAL_PATTERN.exec(trimmed);
|
|
105
|
+
if (percent) {
|
|
106
|
+
var value = parseNumberOperand(percent[1].trim(), locale !== null && locale !== void 0 ? locale : "en-US");
|
|
107
|
+
if (value !== undefined) {
|
|
108
|
+
return formatNumberOperand(value / 100);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Dates (incl. date+time) and times, in the document locale's field order
|
|
112
|
+
// (`1/5/2024` is Jan 5 in en-US, May 1 in en-GB; `1.5.2024` is a de-DE
|
|
113
|
+
// date, not the grouped number 152024). numfmt's default locale is
|
|
114
|
+
// month-first, matching the app default.
|
|
115
|
+
try {
|
|
116
|
+
var dateOptions = locale ? { locale: locale } : undefined;
|
|
117
|
+
var date = (0, numfmt_1.parseDate)(trimmed, dateOptions);
|
|
118
|
+
if (date && typeof date.v === "number" && Number.isFinite(date.v)) {
|
|
119
|
+
return formatNumberOperand(date.v);
|
|
120
|
+
}
|
|
121
|
+
var time = (0, numfmt_1.parseTime)(trimmed);
|
|
122
|
+
if (time && typeof time.v === "number" && Number.isFinite(time.v)) {
|
|
123
|
+
return formatNumberOperand(time.v);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (_a) {
|
|
127
|
+
// numfmt throws on malformed locale tags — the operand is then plain text.
|
|
128
|
+
}
|
|
129
|
+
if (locale) {
|
|
130
|
+
var value = parseLocaleNumberLoose(trimmed, locale);
|
|
131
|
+
if (Number.isFinite(value)) {
|
|
132
|
+
return formatNumberOperand(value);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
};
|
|
137
|
+
exports.conditionOperandLiteral = conditionOperandLiteral;
|
|
138
|
+
/**
|
|
139
|
+
* Whether an operand (with any leading `=` already removed) is an A1
|
|
140
|
+
* reference — a cell, a range, a whole column/row, with `$` anchors and an
|
|
141
|
+
* optional (quoted) sheet qualifier: `B1`, `$B$1`, `B$1`, `Sheet1!$B$1`,
|
|
142
|
+
* `'My Sheet'!$B$1:$C$9`, `$A:$A`.
|
|
143
|
+
*/
|
|
144
|
+
var isConditionOperandReference = function (text) {
|
|
145
|
+
return (0, selection_1.checkIfStringIsReference)(text) || AXIS_REFERENCE_PATTERN.test(text);
|
|
146
|
+
};
|
|
147
|
+
exports.isConditionOperandReference = isConditionOperandReference;
|
|
148
|
+
/**
|
|
149
|
+
* Whether an operand (with any leading `=` already removed) is a function
|
|
150
|
+
* call — the form OOXML stores a formula bound in (`TODAY()`,
|
|
151
|
+
* `DATE(2024,1,1)`, `LEN($B$1)`): evaluated as a formula, never quoted.
|
|
152
|
+
*/
|
|
153
|
+
var isConditionOperandFormulaCall = function (text) {
|
|
154
|
+
return FORMULA_CALL_PATTERN.test(text);
|
|
155
|
+
};
|
|
156
|
+
exports.isConditionOperandFormulaCall = isConditionOperandFormulaCall;
|
|
157
|
+
/**
|
|
158
|
+
* Normalize one rule operand into the formula fragment the rule builders
|
|
159
|
+
* splice into `=<cell><op><fragment>` (and the xlsx exporters write inside
|
|
160
|
+
* `<formula>` / `<formula1>` — never with a leading `=`).
|
|
161
|
+
*
|
|
162
|
+
* - `=…` → the formula text (leading `=` stripped).
|
|
163
|
+
* - `TEXT_*` types → ALWAYS a quoted string literal (`"1"`, `"TRUE"`).
|
|
164
|
+
* - A complete `"…"` string literal → as is.
|
|
165
|
+
* - An A1 reference (`$B$1`, `B$1`, `Sheet1!$B$1`, `'My Sheet'!$B$1`) → as is.
|
|
166
|
+
* - A function call (`TODAY()`, `DATE(2024,1,1)`, `LEN(A1)`) → as is.
|
|
167
|
+
* - A typed literal (number, `50%`, boolean, date/time in `locale`) → its
|
|
168
|
+
* numeric form (`0.5`, `45296`, `TRUE`).
|
|
169
|
+
* - Anything else → a quoted string literal (`"abc"`).
|
|
170
|
+
*
|
|
171
|
+
* `""` for an empty operand.
|
|
172
|
+
*/
|
|
173
|
+
var normalizeConditionOperand = function (raw, type, locale) {
|
|
174
|
+
if (!raw)
|
|
175
|
+
return "";
|
|
176
|
+
var trimmed = raw.trim();
|
|
177
|
+
if (!trimmed)
|
|
178
|
+
return "";
|
|
179
|
+
if (trimmed.startsWith("=")) {
|
|
180
|
+
return trimmed.slice(1);
|
|
181
|
+
}
|
|
182
|
+
if ((0, exports.isTextConditionType)(type)) {
|
|
183
|
+
return "\"".concat((0, exports.escapeConditionFormulaText)(trimmed), "\"");
|
|
184
|
+
}
|
|
185
|
+
if (QUOTED_STRING_LITERAL_PATTERN.test(trimmed)) {
|
|
186
|
+
return trimmed;
|
|
187
|
+
}
|
|
188
|
+
if ((0, selection_1.checkIfStringIsReference)(trimmed) || (0, exports.isConditionOperandFormulaCall)(trimmed)) {
|
|
189
|
+
return trimmed;
|
|
190
|
+
}
|
|
191
|
+
// Typed literals before whole-row spans: `10:30` is the time 10:30, not
|
|
192
|
+
// rows 10–30 (Excel's dialog reads it the way a cell entry would).
|
|
193
|
+
var literal = (0, exports.conditionOperandLiteral)(trimmed, locale);
|
|
194
|
+
if (literal !== undefined) {
|
|
195
|
+
return literal;
|
|
196
|
+
}
|
|
197
|
+
if ((0, exports.isConditionOperandReference)(trimmed)) {
|
|
198
|
+
return trimmed;
|
|
199
|
+
}
|
|
200
|
+
return "\"".concat((0, exports.escapeConditionFormulaText)(trimmed), "\"");
|
|
201
|
+
};
|
|
202
|
+
exports.normalizeConditionOperand = normalizeConditionOperand;
|
|
203
|
+
/**
|
|
204
|
+
* The OOXML `<formula>` / `<formula1>` text for a number/date rule operand:
|
|
205
|
+
* the leading `=` is stripped (Excel never writes one), typed literals are
|
|
206
|
+
* normalized to what Excel itself stores (`50%` → `0.5`, `2024-01-05` → its
|
|
207
|
+
* serial `45296` rather than the arithmetic `2024-01-05` = 2022), and every
|
|
208
|
+
* other operand — references, defined names, function calls — is written
|
|
209
|
+
* verbatim.
|
|
210
|
+
*/
|
|
211
|
+
var conditionOperandToFormulaText = function (raw, locale) {
|
|
212
|
+
var _a;
|
|
213
|
+
if (!raw)
|
|
214
|
+
return "";
|
|
215
|
+
var trimmed = raw.trim();
|
|
216
|
+
if (!trimmed)
|
|
217
|
+
return "";
|
|
218
|
+
if (trimmed.startsWith("=")) {
|
|
219
|
+
return trimmed.slice(1);
|
|
220
|
+
}
|
|
221
|
+
return (_a = (0, exports.conditionOperandLiteral)(trimmed, locale)) !== null && _a !== void 0 ? _a : trimmed;
|
|
222
|
+
};
|
|
223
|
+
exports.conditionOperandToFormulaText = conditionOperandToFormulaText;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditional-format.d.ts","sourceRoot":"","sources":["../../conditional-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"conditional-format.d.ts","sourceRoot":"","sources":["../../conditional-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,4BAA4B,CAAC;AAQpC;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAC9D,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,8BAA8B,GACzC,MAAM,yBAAyB,GAAG,SAAS,YAK5C,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,MAAM,yBAAyB,GAAG,SAAS,EAC3C,aAAa,MAAM,EACnB,SAAS,MAAM,uBA8HhB,CAAC"}
|
|
@@ -1,63 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildConditionalFormatExpression = exports.isConditionalFormatFormulaRule = void 0;
|
|
4
|
+
var condition_operand_1 = require("./condition-operand");
|
|
4
5
|
var data_validation_1 = require("./data-validation");
|
|
5
|
-
var selection_1 = require("./selection");
|
|
6
6
|
var FORMULA_PREDICATE_BASE_CELL_REF = "A1";
|
|
7
|
-
var NUMBER_LITERAL_PATTERN = /^-?\d+(?:\.\d+)?$/;
|
|
8
|
-
var BOOLEAN_LITERAL_PATTERN = /^(TRUE|FALSE)$/i;
|
|
9
|
-
var escapeFormulaText = function (value) { return value.replace(/"/g, '""'); };
|
|
10
|
-
/**
|
|
11
|
-
* Parse a locale-formatted numeric string (e.g., "6,5" in fr-FR) into a JS
|
|
12
|
-
* number. Uses `Intl.NumberFormat` to detect decimal/group separators.
|
|
13
|
-
* Returns NaN on failure.
|
|
14
|
-
*/
|
|
15
|
-
var parseLocaleNumberLoose = function (value, locale) {
|
|
16
|
-
var _a, _b, _c, _d;
|
|
17
|
-
try {
|
|
18
|
-
var parts = new Intl.NumberFormat(locale).formatToParts(1234.5);
|
|
19
|
-
var decimal = (_b = (_a = parts.find(function (part) { return part.type === "decimal"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ".";
|
|
20
|
-
var group = (_d = (_c = parts.find(function (part) { return part.type === "group"; })) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : ",";
|
|
21
|
-
var cleaned = value.split(group).join("").replace(decimal, ".");
|
|
22
|
-
return Number(cleaned);
|
|
23
|
-
}
|
|
24
|
-
catch (_e) {
|
|
25
|
-
return NaN;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Normalize a condition value for use in a formula expression.
|
|
30
|
-
* Handles locale-specific number formats (e.g., "6,5" -> "6.5").
|
|
31
|
-
*/
|
|
32
|
-
var normalizeConditionValue = function (raw, allowStringLiteral, locale) {
|
|
33
|
-
if (allowStringLiteral === void 0) { allowStringLiteral = false; }
|
|
34
|
-
if (!raw)
|
|
35
|
-
return "";
|
|
36
|
-
var trimmed = raw.trim();
|
|
37
|
-
if (!trimmed)
|
|
38
|
-
return "";
|
|
39
|
-
if (trimmed.startsWith("=")) {
|
|
40
|
-
return trimmed.slice(1);
|
|
41
|
-
}
|
|
42
|
-
if ((0, selection_1.checkIfStringIsReference)(trimmed)) {
|
|
43
|
-
return trimmed;
|
|
44
|
-
}
|
|
45
|
-
if (NUMBER_LITERAL_PATTERN.test(trimmed) ||
|
|
46
|
-
BOOLEAN_LITERAL_PATTERN.test(trimmed)) {
|
|
47
|
-
return trimmed;
|
|
48
|
-
}
|
|
49
|
-
// Try parsing as locale-aware number
|
|
50
|
-
if (locale) {
|
|
51
|
-
var parsed = parseLocaleNumberLoose(trimmed, locale);
|
|
52
|
-
if (!isNaN(parsed)) {
|
|
53
|
-
return String(parsed);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (allowStringLiteral) {
|
|
57
|
-
return "\"".concat(escapeFormulaText(trimmed), "\"");
|
|
58
|
-
}
|
|
59
|
-
return trimmed;
|
|
60
|
-
};
|
|
61
7
|
/**
|
|
62
8
|
* Whether a CF rule is evaluated via the calculator (formula path) instead of
|
|
63
9
|
* the synchronous `checkBooleanRule` path used in
|
|
@@ -111,8 +57,8 @@ var buildConditionalFormatExpression = function (rule, baseCellRef, locale) {
|
|
|
111
57
|
return undefined;
|
|
112
58
|
return trimmed.startsWith("=") ? trimmed : "=".concat(trimmed);
|
|
113
59
|
}
|
|
114
|
-
var fromExpr =
|
|
115
|
-
var toExpr =
|
|
60
|
+
var fromExpr = (0, condition_operand_1.normalizeConditionOperand)(fromRaw, condition.type, locale);
|
|
61
|
+
var toExpr = (0, condition_operand_1.normalizeConditionOperand)(toRaw, condition.type, locale);
|
|
116
62
|
if (!fromExpr && !toExpr)
|
|
117
63
|
return undefined;
|
|
118
64
|
var comparisonOperatorMap = {
|
|
@@ -192,13 +138,13 @@ var buildConditionalFormatExpression = function (rule, baseCellRef, locale) {
|
|
|
192
138
|
.flatMap(function (entry) {
|
|
193
139
|
if (entry.startsWith("'")) {
|
|
194
140
|
var stripped = (0, data_validation_1.stripDataValidationTextPrefix)(entry);
|
|
195
|
-
var textCondition = "".concat(baseCellRef, "=\"").concat(
|
|
141
|
+
var textCondition = "".concat(baseCellRef, "=\"").concat((0, condition_operand_1.escapeConditionFormulaText)(stripped), "\"");
|
|
196
142
|
return stripped === ""
|
|
197
143
|
? [textCondition, "".concat(baseCellRef, "=\"'\"")]
|
|
198
144
|
: [textCondition];
|
|
199
145
|
}
|
|
200
146
|
return entry
|
|
201
|
-
? ["".concat(baseCellRef, "=").concat(
|
|
147
|
+
? ["".concat(baseCellRef, "=").concat((0, condition_operand_1.normalizeConditionOperand)(entry, condition.type, locale))]
|
|
202
148
|
: [];
|
|
203
149
|
})
|
|
204
150
|
.filter(function (entry) { return Boolean(entry); });
|
|
@@ -50,5 +50,14 @@ export declare const dataValidationOptionMatchesCellValue: (option: string | und
|
|
|
50
50
|
* builder from drifting.
|
|
51
51
|
*/
|
|
52
52
|
export declare const isDataValidationFormulaRule: (rule: DataValidationRule | undefined) => boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Excel's *Whole number* validation kind rejects any non-integer (`5.5` fails
|
|
55
|
+
* "whole between 1 and 10"). An imported `type="whole"` rule carries a plain
|
|
56
|
+
* `NUMBER_*` comparison as its `condition` (the bounds) — which alone accepts
|
|
57
|
+
* `5.5` — so when the rule's `xlsxSource.type` is `whole` the comparison is
|
|
58
|
+
* wrapped in the same integer guard `buildExcelValidationRule` composes for
|
|
59
|
+
* an authored Whole-number rule: `AND(ISNUMBER(x), x=INT(x), <comparison>)`.
|
|
60
|
+
* Rules of other kinds, and non-`NUMBER_*` conditions, are returned as built.
|
|
61
|
+
*/
|
|
53
62
|
export declare const buildDataValidationExpression: (rule: DataValidationRule | undefined, baseCellRef: string, locale?: string) => string | undefined;
|
|
54
63
|
//# sourceMappingURL=data-validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-validation.d.ts","sourceRoot":"","sources":["../../data-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"data-validation.d.ts","sourceRoot":"","sources":["../../data-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AASpC;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,OAAO,MAAM,KAAG,MACd,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAC/C,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,EACjC,WAAW,MAAM,GAAG,SAAS,GAAG,IAAI,KACnC,OAKF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,2BAA2B,GACtC,MAAM,kBAAkB,GAAG,SAAS,YAKrC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACxC,MAAM,kBAAkB,GAAG,SAAS,EACpC,aAAa,MAAM,EACnB,SAAS,MAAM,uBAgBhB,CAAC"}
|