documonster 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/modules/excel/core/data-validations.d.ts +10 -4
- package/dist/browser/modules/excel/core/data-validations.js +96 -6
- package/dist/cjs/modules/excel/core/data-validations.js +96 -6
- package/dist/esm/modules/excel/core/data-validations.js +96 -6
- package/dist/iife/documonster.archive.iife.js +1 -1
- package/dist/iife/documonster.archive.iife.min.js +1 -1
- package/dist/iife/documonster.csv.iife.js +1 -1
- package/dist/iife/documonster.csv.iife.min.js +1 -1
- package/dist/iife/documonster.excel.iife.js +83 -8
- package/dist/iife/documonster.excel.iife.js.map +1 -1
- package/dist/iife/documonster.excel.iife.min.js +53 -53
- package/dist/iife/documonster.formula.iife.js +1 -1
- package/dist/iife/documonster.formula.iife.min.js +1 -1
- package/dist/iife/documonster.markdown.iife.js +1 -1
- package/dist/iife/documonster.markdown.iife.min.js +1 -1
- package/dist/iife/documonster.pdf.iife.js +1 -1
- package/dist/iife/documonster.pdf.iife.min.js +1 -1
- package/dist/iife/documonster.stream.iife.js +1 -1
- package/dist/iife/documonster.stream.iife.min.js +1 -1
- package/dist/iife/documonster.word.iife.js +2 -1
- package/dist/iife/documonster.word.iife.js.map +1 -1
- package/dist/iife/documonster.word.iife.min.js +1 -1
- package/dist/iife/documonster.xml.iife.js +1 -1
- package/dist/iife/documonster.xml.iife.min.js +1 -1
- package/dist/types/modules/excel/core/data-validations.d.ts +10 -4
- package/package.json +1 -1
|
@@ -13,13 +13,19 @@ export interface DataValidationsData {
|
|
|
13
13
|
}
|
|
14
14
|
/** Create a data-validation registry, optionally seeded from a parsed model. */
|
|
15
15
|
export declare function createDataValidations(model?: ValidationModel): DataValidationsData;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Register a validation for a cell or A1 range.
|
|
18
|
+
*
|
|
19
|
+
* Whole-column (`"A:A"`) and whole-row (`"1:3"`) references are expanded to
|
|
20
|
+
* Excel's sheet limits. Ranges are stored as a single `range:` model entry so
|
|
21
|
+
* they serialise to one data-validation element instead of one per cell.
|
|
22
|
+
*/
|
|
23
|
+
export declare function dataValidationAdd(dv: DataValidationsData, ref: string, validation: DataValidation): DataValidation;
|
|
18
24
|
/**
|
|
19
25
|
* Resolve the validation that applies to `address`: first an exact-address
|
|
20
26
|
* match, then any `range:`-prefixed key whose decoded range contains it.
|
|
21
27
|
*/
|
|
22
28
|
export declare function dataValidationFind(dv: DataValidationsData, address: string): DataValidation | undefined;
|
|
23
|
-
/** Clear the validation registered
|
|
24
|
-
export declare function dataValidationRemove(dv: DataValidationsData,
|
|
29
|
+
/** Clear the validation registered for a cell or A1 range. */
|
|
30
|
+
export declare function dataValidationRemove(dv: DataValidationsData, ref: string): void;
|
|
25
31
|
export {};
|
|
@@ -1,11 +1,101 @@
|
|
|
1
|
+
import { InvalidAddressError } from "../errors.js";
|
|
1
2
|
import { colCache } from "../utils/col-cache.js";
|
|
2
3
|
/** Create a data-validation registry, optionally seeded from a parsed model. */
|
|
3
4
|
export function createDataValidations(model) {
|
|
4
5
|
return { model: model || {} };
|
|
5
6
|
}
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Register a validation for a cell or A1 range.
|
|
9
|
+
*
|
|
10
|
+
* Whole-column (`"A:A"`) and whole-row (`"1:3"`) references are expanded to
|
|
11
|
+
* Excel's sheet limits. Ranges are stored as a single `range:` model entry so
|
|
12
|
+
* they serialise to one data-validation element instead of one per cell.
|
|
13
|
+
*/
|
|
14
|
+
export function dataValidationAdd(dv, ref, validation) {
|
|
15
|
+
return (dv.model[validationModelKey(ref)] = validation);
|
|
16
|
+
}
|
|
17
|
+
/** Excel's hard sheet limits, used to expand whole-column / whole-row refs. */
|
|
18
|
+
const EXCEL_MAX_ROW = 1048576;
|
|
19
|
+
const EXCEL_MAX_COL_LETTER = "XFD";
|
|
20
|
+
/** Whole-column reference, e.g. "A:A" or "A:C" (optionally `$`-anchored). */
|
|
21
|
+
const WHOLE_COLUMN_RE = /^\$?([A-Z]{1,3})\$?:\$?([A-Z]{1,3})$/;
|
|
22
|
+
/** Whole-row reference, e.g. "1:1" or "2:5" (optionally `$`-anchored). */
|
|
23
|
+
const WHOLE_ROW_RE = /^\$?(\d+)\$?:\$?(\d+)$/;
|
|
24
|
+
/** A single concrete cell reference, e.g. "A1", "XFD1048576" (no `$`). */
|
|
25
|
+
const CELL_RE = /^([A-Z]{1,3})(\d+)$/;
|
|
26
|
+
/**
|
|
27
|
+
* Assert that `cell` is a valid, in-bounds A1 cell reference (`A1`..`XFD1048576`).
|
|
28
|
+
* `colCache.decodeEx` is too lenient (it happily returns garbage for "foo",
|
|
29
|
+
* "A0", etc.), so we validate strictly here to avoid persisting a malformed
|
|
30
|
+
* `sqref` that would corrupt the workbook.
|
|
31
|
+
*/
|
|
32
|
+
function assertValidCell(cell, ref) {
|
|
33
|
+
const m = cell.match(CELL_RE);
|
|
34
|
+
if (!m) {
|
|
35
|
+
throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
36
|
+
}
|
|
37
|
+
colCache.l2n(m[1]); // throws ColumnOutOfBoundsError if the column is > XFD
|
|
38
|
+
const row = Number(m[2]);
|
|
39
|
+
if (row < 1 || row > EXCEL_MAX_ROW) {
|
|
40
|
+
throw new InvalidAddressError(ref, `row ${row} is outside 1..${EXCEL_MAX_ROW}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function decodeStrictCell(cell, ref) {
|
|
44
|
+
assertValidCell(cell, ref);
|
|
45
|
+
const m = cell.match(CELL_RE);
|
|
46
|
+
return { col: colCache.l2n(m[1]), row: Number(m[2]) };
|
|
47
|
+
}
|
|
48
|
+
function normaliseEndpoints(start, end, ref) {
|
|
49
|
+
const a = decodeStrictCell(start, ref);
|
|
50
|
+
const b = decodeStrictCell(end, ref);
|
|
51
|
+
const top = Math.min(a.row, b.row);
|
|
52
|
+
const left = Math.min(a.col, b.col);
|
|
53
|
+
const bottom = Math.max(a.row, b.row);
|
|
54
|
+
const right = Math.max(a.col, b.col);
|
|
55
|
+
return `${colCache.encodeAddress(top, left)}:${colCache.encodeAddress(bottom, right)}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Normalise a user-supplied A1 range reference into a concrete `top:bottom`
|
|
59
|
+
* range string suitable for a `range:` model key (and for the xlsx `sqref`).
|
|
60
|
+
*
|
|
61
|
+
* Handles the whole-column (`"A:A"`, `"A:C"`) and whole-row (`"1:3"`) shorthand
|
|
62
|
+
* by expanding them to Excel's sheet limits, mirroring how Excel itself stores
|
|
63
|
+
* a validation applied to an entire column (`A1:A1048576`). Any leading
|
|
64
|
+
* `Sheet!` qualifier is stripped, since data-validation `sqref` values are
|
|
65
|
+
* sheet-local. Absolute `$` markers are dropped. Throws
|
|
66
|
+
* {@link InvalidAddressError} for anything that is not a valid cell or range.
|
|
67
|
+
*/
|
|
68
|
+
function normaliseRangeRef(ref) {
|
|
69
|
+
if (typeof ref !== "string" || ref.length === 0) {
|
|
70
|
+
throw new InvalidAddressError(String(ref), "range reference must be a non-empty string");
|
|
71
|
+
}
|
|
72
|
+
// Strip a leading sheet qualifier: `Sheet1!A1:A10` / `'My Sheet'!A:A`.
|
|
73
|
+
const bang = ref.lastIndexOf("!");
|
|
74
|
+
const local = bang === -1 ? ref : ref.slice(bang + 1);
|
|
75
|
+
const wholeCol = local.match(WHOLE_COLUMN_RE);
|
|
76
|
+
if (wholeCol) {
|
|
77
|
+
return normaliseEndpoints(`${wholeCol[1]}1`, `${wholeCol[2]}${EXCEL_MAX_ROW}`, ref);
|
|
78
|
+
}
|
|
79
|
+
const wholeRow = local.match(WHOLE_ROW_RE);
|
|
80
|
+
if (wholeRow) {
|
|
81
|
+
return normaliseEndpoints(`A${wholeRow[1]}`, `${EXCEL_MAX_COL_LETTER}${wholeRow[2]}`, ref);
|
|
82
|
+
}
|
|
83
|
+
// Concrete cell or cell:cell range — drop absolute markers so keys match the
|
|
84
|
+
// `sqref` form Excel writes, then validate every endpoint strictly.
|
|
85
|
+
const bare = local.replace(/\$/g, "");
|
|
86
|
+
const endpoints = bare.split(":");
|
|
87
|
+
if (endpoints.length > 2) {
|
|
88
|
+
throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
89
|
+
}
|
|
90
|
+
if (endpoints.length === 2) {
|
|
91
|
+
return normaliseEndpoints(endpoints[0], endpoints[1], ref);
|
|
92
|
+
}
|
|
93
|
+
assertValidCell(bare, ref);
|
|
94
|
+
return bare;
|
|
95
|
+
}
|
|
96
|
+
function validationModelKey(ref) {
|
|
97
|
+
const normalised = normaliseRangeRef(ref);
|
|
98
|
+
return normalised.includes(":") ? `range:${normalised}` : normalised;
|
|
9
99
|
}
|
|
10
100
|
/**
|
|
11
101
|
* Resolve the validation that applies to `address`: first an exact-address
|
|
@@ -42,7 +132,7 @@ export function dataValidationFind(dv, address) {
|
|
|
42
132
|
}
|
|
43
133
|
return undefined;
|
|
44
134
|
}
|
|
45
|
-
/** Clear the validation registered
|
|
46
|
-
export function dataValidationRemove(dv,
|
|
47
|
-
dv.model[
|
|
135
|
+
/** Clear the validation registered for a cell or A1 range. */
|
|
136
|
+
export function dataValidationRemove(dv, ref) {
|
|
137
|
+
dv.model[validationModelKey(ref)] = undefined;
|
|
48
138
|
}
|
|
@@ -4,14 +4,104 @@ exports.createDataValidations = createDataValidations;
|
|
|
4
4
|
exports.dataValidationAdd = dataValidationAdd;
|
|
5
5
|
exports.dataValidationFind = dataValidationFind;
|
|
6
6
|
exports.dataValidationRemove = dataValidationRemove;
|
|
7
|
+
const errors_1 = require("../errors.js");
|
|
7
8
|
const col_cache_1 = require("../utils/col-cache.js");
|
|
8
9
|
/** Create a data-validation registry, optionally seeded from a parsed model. */
|
|
9
10
|
function createDataValidations(model) {
|
|
10
11
|
return { model: model || {} };
|
|
11
12
|
}
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Register a validation for a cell or A1 range.
|
|
15
|
+
*
|
|
16
|
+
* Whole-column (`"A:A"`) and whole-row (`"1:3"`) references are expanded to
|
|
17
|
+
* Excel's sheet limits. Ranges are stored as a single `range:` model entry so
|
|
18
|
+
* they serialise to one data-validation element instead of one per cell.
|
|
19
|
+
*/
|
|
20
|
+
function dataValidationAdd(dv, ref, validation) {
|
|
21
|
+
return (dv.model[validationModelKey(ref)] = validation);
|
|
22
|
+
}
|
|
23
|
+
/** Excel's hard sheet limits, used to expand whole-column / whole-row refs. */
|
|
24
|
+
const EXCEL_MAX_ROW = 1048576;
|
|
25
|
+
const EXCEL_MAX_COL_LETTER = "XFD";
|
|
26
|
+
/** Whole-column reference, e.g. "A:A" or "A:C" (optionally `$`-anchored). */
|
|
27
|
+
const WHOLE_COLUMN_RE = /^\$?([A-Z]{1,3})\$?:\$?([A-Z]{1,3})$/;
|
|
28
|
+
/** Whole-row reference, e.g. "1:1" or "2:5" (optionally `$`-anchored). */
|
|
29
|
+
const WHOLE_ROW_RE = /^\$?(\d+)\$?:\$?(\d+)$/;
|
|
30
|
+
/** A single concrete cell reference, e.g. "A1", "XFD1048576" (no `$`). */
|
|
31
|
+
const CELL_RE = /^([A-Z]{1,3})(\d+)$/;
|
|
32
|
+
/**
|
|
33
|
+
* Assert that `cell` is a valid, in-bounds A1 cell reference (`A1`..`XFD1048576`).
|
|
34
|
+
* `colCache.decodeEx` is too lenient (it happily returns garbage for "foo",
|
|
35
|
+
* "A0", etc.), so we validate strictly here to avoid persisting a malformed
|
|
36
|
+
* `sqref` that would corrupt the workbook.
|
|
37
|
+
*/
|
|
38
|
+
function assertValidCell(cell, ref) {
|
|
39
|
+
const m = cell.match(CELL_RE);
|
|
40
|
+
if (!m) {
|
|
41
|
+
throw new errors_1.InvalidAddressError(ref, "not a valid cell or range reference");
|
|
42
|
+
}
|
|
43
|
+
col_cache_1.colCache.l2n(m[1]); // throws ColumnOutOfBoundsError if the column is > XFD
|
|
44
|
+
const row = Number(m[2]);
|
|
45
|
+
if (row < 1 || row > EXCEL_MAX_ROW) {
|
|
46
|
+
throw new errors_1.InvalidAddressError(ref, `row ${row} is outside 1..${EXCEL_MAX_ROW}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function decodeStrictCell(cell, ref) {
|
|
50
|
+
assertValidCell(cell, ref);
|
|
51
|
+
const m = cell.match(CELL_RE);
|
|
52
|
+
return { col: col_cache_1.colCache.l2n(m[1]), row: Number(m[2]) };
|
|
53
|
+
}
|
|
54
|
+
function normaliseEndpoints(start, end, ref) {
|
|
55
|
+
const a = decodeStrictCell(start, ref);
|
|
56
|
+
const b = decodeStrictCell(end, ref);
|
|
57
|
+
const top = Math.min(a.row, b.row);
|
|
58
|
+
const left = Math.min(a.col, b.col);
|
|
59
|
+
const bottom = Math.max(a.row, b.row);
|
|
60
|
+
const right = Math.max(a.col, b.col);
|
|
61
|
+
return `${col_cache_1.colCache.encodeAddress(top, left)}:${col_cache_1.colCache.encodeAddress(bottom, right)}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Normalise a user-supplied A1 range reference into a concrete `top:bottom`
|
|
65
|
+
* range string suitable for a `range:` model key (and for the xlsx `sqref`).
|
|
66
|
+
*
|
|
67
|
+
* Handles the whole-column (`"A:A"`, `"A:C"`) and whole-row (`"1:3"`) shorthand
|
|
68
|
+
* by expanding them to Excel's sheet limits, mirroring how Excel itself stores
|
|
69
|
+
* a validation applied to an entire column (`A1:A1048576`). Any leading
|
|
70
|
+
* `Sheet!` qualifier is stripped, since data-validation `sqref` values are
|
|
71
|
+
* sheet-local. Absolute `$` markers are dropped. Throws
|
|
72
|
+
* {@link InvalidAddressError} for anything that is not a valid cell or range.
|
|
73
|
+
*/
|
|
74
|
+
function normaliseRangeRef(ref) {
|
|
75
|
+
if (typeof ref !== "string" || ref.length === 0) {
|
|
76
|
+
throw new errors_1.InvalidAddressError(String(ref), "range reference must be a non-empty string");
|
|
77
|
+
}
|
|
78
|
+
// Strip a leading sheet qualifier: `Sheet1!A1:A10` / `'My Sheet'!A:A`.
|
|
79
|
+
const bang = ref.lastIndexOf("!");
|
|
80
|
+
const local = bang === -1 ? ref : ref.slice(bang + 1);
|
|
81
|
+
const wholeCol = local.match(WHOLE_COLUMN_RE);
|
|
82
|
+
if (wholeCol) {
|
|
83
|
+
return normaliseEndpoints(`${wholeCol[1]}1`, `${wholeCol[2]}${EXCEL_MAX_ROW}`, ref);
|
|
84
|
+
}
|
|
85
|
+
const wholeRow = local.match(WHOLE_ROW_RE);
|
|
86
|
+
if (wholeRow) {
|
|
87
|
+
return normaliseEndpoints(`A${wholeRow[1]}`, `${EXCEL_MAX_COL_LETTER}${wholeRow[2]}`, ref);
|
|
88
|
+
}
|
|
89
|
+
// Concrete cell or cell:cell range — drop absolute markers so keys match the
|
|
90
|
+
// `sqref` form Excel writes, then validate every endpoint strictly.
|
|
91
|
+
const bare = local.replace(/\$/g, "");
|
|
92
|
+
const endpoints = bare.split(":");
|
|
93
|
+
if (endpoints.length > 2) {
|
|
94
|
+
throw new errors_1.InvalidAddressError(ref, "not a valid cell or range reference");
|
|
95
|
+
}
|
|
96
|
+
if (endpoints.length === 2) {
|
|
97
|
+
return normaliseEndpoints(endpoints[0], endpoints[1], ref);
|
|
98
|
+
}
|
|
99
|
+
assertValidCell(bare, ref);
|
|
100
|
+
return bare;
|
|
101
|
+
}
|
|
102
|
+
function validationModelKey(ref) {
|
|
103
|
+
const normalised = normaliseRangeRef(ref);
|
|
104
|
+
return normalised.includes(":") ? `range:${normalised}` : normalised;
|
|
15
105
|
}
|
|
16
106
|
/**
|
|
17
107
|
* Resolve the validation that applies to `address`: first an exact-address
|
|
@@ -48,7 +138,7 @@ function dataValidationFind(dv, address) {
|
|
|
48
138
|
}
|
|
49
139
|
return undefined;
|
|
50
140
|
}
|
|
51
|
-
/** Clear the validation registered
|
|
52
|
-
function dataValidationRemove(dv,
|
|
53
|
-
dv.model[
|
|
141
|
+
/** Clear the validation registered for a cell or A1 range. */
|
|
142
|
+
function dataValidationRemove(dv, ref) {
|
|
143
|
+
dv.model[validationModelKey(ref)] = undefined;
|
|
54
144
|
}
|
|
@@ -1,11 +1,101 @@
|
|
|
1
|
+
import { InvalidAddressError } from "../errors.js";
|
|
1
2
|
import { colCache } from "../utils/col-cache.js";
|
|
2
3
|
/** Create a data-validation registry, optionally seeded from a parsed model. */
|
|
3
4
|
export function createDataValidations(model) {
|
|
4
5
|
return { model: model || {} };
|
|
5
6
|
}
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Register a validation for a cell or A1 range.
|
|
9
|
+
*
|
|
10
|
+
* Whole-column (`"A:A"`) and whole-row (`"1:3"`) references are expanded to
|
|
11
|
+
* Excel's sheet limits. Ranges are stored as a single `range:` model entry so
|
|
12
|
+
* they serialise to one data-validation element instead of one per cell.
|
|
13
|
+
*/
|
|
14
|
+
export function dataValidationAdd(dv, ref, validation) {
|
|
15
|
+
return (dv.model[validationModelKey(ref)] = validation);
|
|
16
|
+
}
|
|
17
|
+
/** Excel's hard sheet limits, used to expand whole-column / whole-row refs. */
|
|
18
|
+
const EXCEL_MAX_ROW = 1048576;
|
|
19
|
+
const EXCEL_MAX_COL_LETTER = "XFD";
|
|
20
|
+
/** Whole-column reference, e.g. "A:A" or "A:C" (optionally `$`-anchored). */
|
|
21
|
+
const WHOLE_COLUMN_RE = /^\$?([A-Z]{1,3})\$?:\$?([A-Z]{1,3})$/;
|
|
22
|
+
/** Whole-row reference, e.g. "1:1" or "2:5" (optionally `$`-anchored). */
|
|
23
|
+
const WHOLE_ROW_RE = /^\$?(\d+)\$?:\$?(\d+)$/;
|
|
24
|
+
/** A single concrete cell reference, e.g. "A1", "XFD1048576" (no `$`). */
|
|
25
|
+
const CELL_RE = /^([A-Z]{1,3})(\d+)$/;
|
|
26
|
+
/**
|
|
27
|
+
* Assert that `cell` is a valid, in-bounds A1 cell reference (`A1`..`XFD1048576`).
|
|
28
|
+
* `colCache.decodeEx` is too lenient (it happily returns garbage for "foo",
|
|
29
|
+
* "A0", etc.), so we validate strictly here to avoid persisting a malformed
|
|
30
|
+
* `sqref` that would corrupt the workbook.
|
|
31
|
+
*/
|
|
32
|
+
function assertValidCell(cell, ref) {
|
|
33
|
+
const m = cell.match(CELL_RE);
|
|
34
|
+
if (!m) {
|
|
35
|
+
throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
36
|
+
}
|
|
37
|
+
colCache.l2n(m[1]); // throws ColumnOutOfBoundsError if the column is > XFD
|
|
38
|
+
const row = Number(m[2]);
|
|
39
|
+
if (row < 1 || row > EXCEL_MAX_ROW) {
|
|
40
|
+
throw new InvalidAddressError(ref, `row ${row} is outside 1..${EXCEL_MAX_ROW}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function decodeStrictCell(cell, ref) {
|
|
44
|
+
assertValidCell(cell, ref);
|
|
45
|
+
const m = cell.match(CELL_RE);
|
|
46
|
+
return { col: colCache.l2n(m[1]), row: Number(m[2]) };
|
|
47
|
+
}
|
|
48
|
+
function normaliseEndpoints(start, end, ref) {
|
|
49
|
+
const a = decodeStrictCell(start, ref);
|
|
50
|
+
const b = decodeStrictCell(end, ref);
|
|
51
|
+
const top = Math.min(a.row, b.row);
|
|
52
|
+
const left = Math.min(a.col, b.col);
|
|
53
|
+
const bottom = Math.max(a.row, b.row);
|
|
54
|
+
const right = Math.max(a.col, b.col);
|
|
55
|
+
return `${colCache.encodeAddress(top, left)}:${colCache.encodeAddress(bottom, right)}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Normalise a user-supplied A1 range reference into a concrete `top:bottom`
|
|
59
|
+
* range string suitable for a `range:` model key (and for the xlsx `sqref`).
|
|
60
|
+
*
|
|
61
|
+
* Handles the whole-column (`"A:A"`, `"A:C"`) and whole-row (`"1:3"`) shorthand
|
|
62
|
+
* by expanding them to Excel's sheet limits, mirroring how Excel itself stores
|
|
63
|
+
* a validation applied to an entire column (`A1:A1048576`). Any leading
|
|
64
|
+
* `Sheet!` qualifier is stripped, since data-validation `sqref` values are
|
|
65
|
+
* sheet-local. Absolute `$` markers are dropped. Throws
|
|
66
|
+
* {@link InvalidAddressError} for anything that is not a valid cell or range.
|
|
67
|
+
*/
|
|
68
|
+
function normaliseRangeRef(ref) {
|
|
69
|
+
if (typeof ref !== "string" || ref.length === 0) {
|
|
70
|
+
throw new InvalidAddressError(String(ref), "range reference must be a non-empty string");
|
|
71
|
+
}
|
|
72
|
+
// Strip a leading sheet qualifier: `Sheet1!A1:A10` / `'My Sheet'!A:A`.
|
|
73
|
+
const bang = ref.lastIndexOf("!");
|
|
74
|
+
const local = bang === -1 ? ref : ref.slice(bang + 1);
|
|
75
|
+
const wholeCol = local.match(WHOLE_COLUMN_RE);
|
|
76
|
+
if (wholeCol) {
|
|
77
|
+
return normaliseEndpoints(`${wholeCol[1]}1`, `${wholeCol[2]}${EXCEL_MAX_ROW}`, ref);
|
|
78
|
+
}
|
|
79
|
+
const wholeRow = local.match(WHOLE_ROW_RE);
|
|
80
|
+
if (wholeRow) {
|
|
81
|
+
return normaliseEndpoints(`A${wholeRow[1]}`, `${EXCEL_MAX_COL_LETTER}${wholeRow[2]}`, ref);
|
|
82
|
+
}
|
|
83
|
+
// Concrete cell or cell:cell range — drop absolute markers so keys match the
|
|
84
|
+
// `sqref` form Excel writes, then validate every endpoint strictly.
|
|
85
|
+
const bare = local.replace(/\$/g, "");
|
|
86
|
+
const endpoints = bare.split(":");
|
|
87
|
+
if (endpoints.length > 2) {
|
|
88
|
+
throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
89
|
+
}
|
|
90
|
+
if (endpoints.length === 2) {
|
|
91
|
+
return normaliseEndpoints(endpoints[0], endpoints[1], ref);
|
|
92
|
+
}
|
|
93
|
+
assertValidCell(bare, ref);
|
|
94
|
+
return bare;
|
|
95
|
+
}
|
|
96
|
+
function validationModelKey(ref) {
|
|
97
|
+
const normalised = normaliseRangeRef(ref);
|
|
98
|
+
return normalised.includes(":") ? `range:${normalised}` : normalised;
|
|
9
99
|
}
|
|
10
100
|
/**
|
|
11
101
|
* Resolve the validation that applies to `address`: first an exact-address
|
|
@@ -42,7 +132,7 @@ export function dataValidationFind(dv, address) {
|
|
|
42
132
|
}
|
|
43
133
|
return undefined;
|
|
44
134
|
}
|
|
45
|
-
/** Clear the validation registered
|
|
46
|
-
export function dataValidationRemove(dv,
|
|
47
|
-
dv.model[
|
|
135
|
+
/** Clear the validation registered for a cell or A1 range. */
|
|
136
|
+
export function dataValidationRemove(dv, ref) {
|
|
137
|
+
dv.model[validationModelKey(ref)] = undefined;
|
|
48
138
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* documonster v0.
|
|
2
|
+
* documonster v0.2.0
|
|
3
3
|
* Zero-dependency TypeScript toolkit for Excel, Word, PDF, CSV, Markdown, XML & ZIP — one API across Node.js, Bun & browsers.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the Apache-2.0 License
|
|
@@ -501,14 +501,90 @@ this.Documonster = this.Documonster || {};
|
|
|
501
501
|
}));
|
|
502
502
|
//#endregion
|
|
503
503
|
//#region src/modules/excel/core/data-validations.ts
|
|
504
|
+
init_errors$2();
|
|
504
505
|
init_col_cache();
|
|
505
506
|
/** Create a data-validation registry, optionally seeded from a parsed model. */
|
|
506
507
|
function createDataValidations(model) {
|
|
507
508
|
return { model: model || {} };
|
|
508
509
|
}
|
|
509
|
-
/**
|
|
510
|
-
|
|
511
|
-
|
|
510
|
+
/**
|
|
511
|
+
* Register a validation for a cell or A1 range.
|
|
512
|
+
*
|
|
513
|
+
* Whole-column (`"A:A"`) and whole-row (`"1:3"`) references are expanded to
|
|
514
|
+
* Excel's sheet limits. Ranges are stored as a single `range:` model entry so
|
|
515
|
+
* they serialise to one data-validation element instead of one per cell.
|
|
516
|
+
*/
|
|
517
|
+
function dataValidationAdd(dv, ref, validation) {
|
|
518
|
+
return dv.model[validationModelKey(ref)] = validation;
|
|
519
|
+
}
|
|
520
|
+
/** Excel's hard sheet limits, used to expand whole-column / whole-row refs. */
|
|
521
|
+
const EXCEL_MAX_ROW$2 = 1048576;
|
|
522
|
+
const EXCEL_MAX_COL_LETTER = "XFD";
|
|
523
|
+
/** Whole-column reference, e.g. "A:A" or "A:C" (optionally `$`-anchored). */
|
|
524
|
+
const WHOLE_COLUMN_RE = /^\$?([A-Z]{1,3})\$?:\$?([A-Z]{1,3})$/;
|
|
525
|
+
/** Whole-row reference, e.g. "1:1" or "2:5" (optionally `$`-anchored). */
|
|
526
|
+
const WHOLE_ROW_RE = /^\$?(\d+)\$?:\$?(\d+)$/;
|
|
527
|
+
/** A single concrete cell reference, e.g. "A1", "XFD1048576" (no `$`). */
|
|
528
|
+
const CELL_RE = /^([A-Z]{1,3})(\d+)$/;
|
|
529
|
+
/**
|
|
530
|
+
* Assert that `cell` is a valid, in-bounds A1 cell reference (`A1`..`XFD1048576`).
|
|
531
|
+
* `colCache.decodeEx` is too lenient (it happily returns garbage for "foo",
|
|
532
|
+
* "A0", etc.), so we validate strictly here to avoid persisting a malformed
|
|
533
|
+
* `sqref` that would corrupt the workbook.
|
|
534
|
+
*/
|
|
535
|
+
function assertValidCell(cell, ref) {
|
|
536
|
+
const m = cell.match(CELL_RE);
|
|
537
|
+
if (!m) throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
538
|
+
colCache.l2n(m[1]);
|
|
539
|
+
const row = Number(m[2]);
|
|
540
|
+
if (row < 1 || row > EXCEL_MAX_ROW$2) throw new InvalidAddressError(ref, `row ${row} is outside 1..${EXCEL_MAX_ROW$2}`);
|
|
541
|
+
}
|
|
542
|
+
function decodeStrictCell(cell, ref) {
|
|
543
|
+
assertValidCell(cell, ref);
|
|
544
|
+
const m = cell.match(CELL_RE);
|
|
545
|
+
return {
|
|
546
|
+
col: colCache.l2n(m[1]),
|
|
547
|
+
row: Number(m[2])
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function normaliseEndpoints(start, end, ref) {
|
|
551
|
+
const a = decodeStrictCell(start, ref);
|
|
552
|
+
const b = decodeStrictCell(end, ref);
|
|
553
|
+
const top = Math.min(a.row, b.row);
|
|
554
|
+
const left = Math.min(a.col, b.col);
|
|
555
|
+
const bottom = Math.max(a.row, b.row);
|
|
556
|
+
const right = Math.max(a.col, b.col);
|
|
557
|
+
return `${colCache.encodeAddress(top, left)}:${colCache.encodeAddress(bottom, right)}`;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Normalise a user-supplied A1 range reference into a concrete `top:bottom`
|
|
561
|
+
* range string suitable for a `range:` model key (and for the xlsx `sqref`).
|
|
562
|
+
*
|
|
563
|
+
* Handles the whole-column (`"A:A"`, `"A:C"`) and whole-row (`"1:3"`) shorthand
|
|
564
|
+
* by expanding them to Excel's sheet limits, mirroring how Excel itself stores
|
|
565
|
+
* a validation applied to an entire column (`A1:A1048576`). Any leading
|
|
566
|
+
* `Sheet!` qualifier is stripped, since data-validation `sqref` values are
|
|
567
|
+
* sheet-local. Absolute `$` markers are dropped. Throws
|
|
568
|
+
* {@link InvalidAddressError} for anything that is not a valid cell or range.
|
|
569
|
+
*/
|
|
570
|
+
function normaliseRangeRef(ref) {
|
|
571
|
+
if (typeof ref !== "string" || ref.length === 0) throw new InvalidAddressError(String(ref), "range reference must be a non-empty string");
|
|
572
|
+
const bang = ref.lastIndexOf("!");
|
|
573
|
+
const local = bang === -1 ? ref : ref.slice(bang + 1);
|
|
574
|
+
const wholeCol = local.match(WHOLE_COLUMN_RE);
|
|
575
|
+
if (wholeCol) return normaliseEndpoints(`${wholeCol[1]}1`, `${wholeCol[2]}${EXCEL_MAX_ROW$2}`, ref);
|
|
576
|
+
const wholeRow = local.match(WHOLE_ROW_RE);
|
|
577
|
+
if (wholeRow) return normaliseEndpoints(`A${wholeRow[1]}`, `${EXCEL_MAX_COL_LETTER}${wholeRow[2]}`, ref);
|
|
578
|
+
const bare = local.replace(/\$/g, "");
|
|
579
|
+
const endpoints = bare.split(":");
|
|
580
|
+
if (endpoints.length > 2) throw new InvalidAddressError(ref, "not a valid cell or range reference");
|
|
581
|
+
if (endpoints.length === 2) return normaliseEndpoints(endpoints[0], endpoints[1], ref);
|
|
582
|
+
assertValidCell(bare, ref);
|
|
583
|
+
return bare;
|
|
584
|
+
}
|
|
585
|
+
function validationModelKey(ref) {
|
|
586
|
+
const normalised = normaliseRangeRef(ref);
|
|
587
|
+
return normalised.includes(":") ? `range:${normalised}` : normalised;
|
|
512
588
|
}
|
|
513
589
|
/**
|
|
514
590
|
* Resolve the validation that applies to `address`: first an exact-address
|
|
@@ -530,9 +606,9 @@ this.Documonster = this.Documonster || {};
|
|
|
530
606
|
if (decoded.row >= tlAddr.row && decoded.row <= brAddr.row && decoded.col >= tlAddr.col && decoded.col <= brAddr.col) return dv.model[key];
|
|
531
607
|
}
|
|
532
608
|
}
|
|
533
|
-
/** Clear the validation registered
|
|
534
|
-
function dataValidationRemove(dv,
|
|
535
|
-
dv.model[
|
|
609
|
+
/** Clear the validation registered for a cell or A1 range. */
|
|
610
|
+
function dataValidationRemove(dv, ref) {
|
|
611
|
+
dv.model[validationModelKey(ref)] = void 0;
|
|
536
612
|
}
|
|
537
613
|
//#endregion
|
|
538
614
|
//#region src/modules/excel/core/range.ts
|
|
@@ -45606,7 +45682,6 @@ self.onmessage = async function(event) {
|
|
|
45606
45682
|
var WRITE_AFTER_END_ERROR, AsyncStreamCodec, BufferedCodec;
|
|
45607
45683
|
var init_streaming_compress_browser = __esmMin((() => {
|
|
45608
45684
|
init_compress_base();
|
|
45609
|
-
init_compress_browser();
|
|
45610
45685
|
init_deflate_fallback();
|
|
45611
45686
|
init_index_browser();
|
|
45612
45687
|
init_defaults();
|