@sheetpane/validation 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +149 -2
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +146 -0
- package/dist/index.js.map +1 -0
- package/dist/rules.d.ts +143 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +417 -0
- package/dist/rules.js.map +1 -0
- package/dist/ui.d.ts +55 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +96 -0
- package/dist/ui.js.map +1 -0
- package/package.json +49 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arpit Arya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,150 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sheetpane/validation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The workbook's own data-validation rules, checked when the file loads and after every edit. Each cell that breaks a rule shows up as a warning in `<sheetpane-errors>`.**
|
|
4
|
+
|
|
5
|
+
A plugin for `@sheetpane/grid`. It reads the `<dataValidations>` the file's
|
|
6
|
+
author saved, including the x14 form Excel uses for lists on another sheet,
|
|
7
|
+
and runs them through the `validate` hook. `<sheetpane-excel>` and
|
|
8
|
+
`<sheetpane-errors>` on the same pane both see the result. No runtime
|
|
9
|
+
dependencies; `@sheetpane/grid` is a peer. Versioned in lockstep with it.
|
|
10
|
+
|
|
11
|
+
- `@sheetpane/validation`: the headless plugin (`validate` only).
|
|
12
|
+
- `@sheetpane/validation/ui`: opt-in. Adds the list arrow and the status-line count.
|
|
13
|
+
|
|
14
|
+
## Use
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script type="module">
|
|
18
|
+
import '@sheetpane/grid';
|
|
19
|
+
import '@sheetpane/validation/ui'; // registers "validation" = rules + controls
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<sheetpane-excel pane="q3" plugins="validation" src="/files/holdings.xlsx" mode="edit"></sheetpane-excel>
|
|
23
|
+
<sheetpane-errors pane="q3"></sheetpane-errors>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Without a page, or without the controls:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { createWorkbook } from '@sheetpane/grid';
|
|
30
|
+
import { validation, skippedRules } from '@sheetpane/validation';
|
|
31
|
+
|
|
32
|
+
const store = createWorkbook({ plugins: [validation({ severity: 'warning' })] });
|
|
33
|
+
await store.load(bytes, 'holdings.xlsx');
|
|
34
|
+
store.getErrors(); // one error per failing cell
|
|
35
|
+
skippedRules(store); // { rules, checked, skipped: [{ sheet, sqref, type, reason, detail }] }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Importing `@sheetpane/validation` registers `validation` as the rules plugin
|
|
39
|
+
alone. Importing `/ui` re-registers it as the rules plus the controls.
|
|
40
|
+
|
|
41
|
+
## What a broken rule looks like
|
|
42
|
+
|
|
43
|
+
One error per failing cell. If a cell breaks two rules, the first one counts.
|
|
44
|
+
|
|
45
|
+
| field | value |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `type` | `validation` |
|
|
48
|
+
| `severity` | `warning` (set another with `validation({ severity })`) |
|
|
49
|
+
| `author` | `workbook` |
|
|
50
|
+
| `range` | the cell, e.g. `B4` |
|
|
51
|
+
| `found` | the cell's current display text, or `(blank)` |
|
|
52
|
+
| `expected` | the rule in words (table below) |
|
|
53
|
+
| `message` | the rule's own `errorTitle: error`, or either one alone; otherwise the rule in words |
|
|
54
|
+
|
|
55
|
+
A workbook error resolves itself when its cell passes again and reopens if
|
|
56
|
+
the cell fails again (it keeps the same id). This is the only automatic
|
|
57
|
+
resolve in sheetpane; the store does it. `showErrorMessage="0"` rules are
|
|
58
|
+
still checked, because that flag only turns off Excel's popup.
|
|
59
|
+
|
|
60
|
+
## The rules, and the words a reviewer reads
|
|
61
|
+
|
|
62
|
+
| rule | passes when | `expected` |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `whole` | the value is an integer and the operator holds | `whole number > 0` |
|
|
65
|
+
| `decimal` | the value is a number and the operator holds | `decimal between 0.00% and 2.00%` (operands in the cell's own number format) |
|
|
66
|
+
| `date` | the value is a date serial and the operator holds | `date between 01-Jul-2026 and 30-Sep-2026` |
|
|
67
|
+
| `time` | the value is a number (a fraction of a day) and the operator holds | `time between 09:00 and 18:00` |
|
|
68
|
+
| `textLength` | the length of the text holds against the operator | `text length ≤ 5 characters` |
|
|
69
|
+
| `list` | the text matches a choice (case-insensitive; a number also matches its number) | `list — Core, Income, Cap` (the first 8, then `… (n more)`) |
|
|
70
|
+
| `custom` | the formula is true at the cell | `custom — =A1>0` |
|
|
71
|
+
|
|
72
|
+
Operators: `between` (the default), `notBetween`, `equal` (`=`), `notEqual`
|
|
73
|
+
(`≠`), `greaterThan` (`>`), `lessThan` (`<`), `greaterThanOrEqual` (`≥`),
|
|
74
|
+
`lessThanOrEqual` (`≤`). `between` includes both ends.
|
|
75
|
+
|
|
76
|
+
What an operand can be:
|
|
77
|
+
|
|
78
|
+
- a number
|
|
79
|
+
- `DATE(y,m,d)` or `TIME(h,m,s)` with number arguments
|
|
80
|
+
- a quoted string
|
|
81
|
+
- a cell: `$G$1`, `Sheet2!B1`, or a relative `D1`, which shifts with each cell the same way it does in Excel
|
|
82
|
+
- a defined name that resolves to one of these
|
|
83
|
+
|
|
84
|
+
A cell operand is read live, so changing the limit re-checks the rule. An
|
|
85
|
+
empty operand cell reads as 0.
|
|
86
|
+
|
|
87
|
+
A `list` source can be:
|
|
88
|
+
|
|
89
|
+
- a literal list: `"a,b,c"`
|
|
90
|
+
- a range: `$H$1:$H$9`, `Lists!$A$1:$A$9`, `$A:$A`
|
|
91
|
+
- a defined name that resolves to a range
|
|
92
|
+
|
|
93
|
+
A range source is read live from the store, and the arrow's menu re-reads it
|
|
94
|
+
each time it opens.
|
|
95
|
+
|
|
96
|
+
Blank cells pass when the rule has `allowBlank`. Without it, they fail.
|
|
97
|
+
Cells are checked only inside the sheet's used range: its data plus any cell
|
|
98
|
+
the user changed. A rule on `B:B` therefore does not produce a million rows.
|
|
99
|
+
|
|
100
|
+
## What is not checked, and why (`skippedRules`)
|
|
101
|
+
|
|
102
|
+
| reason | what |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `custom` | a `custom` rule when no formula plugin offers `evaluate()` (see below) |
|
|
105
|
+
| `operand` | an operand the subset above does not cover, e.g. `SUM(...)` or `INDIRECT(...)` |
|
|
106
|
+
| `operator` | an operator outside the eight above |
|
|
107
|
+
| `type` | an unknown rule type |
|
|
108
|
+
| `source` | a list source that is neither a list, a range nor a name, or that names a missing sheet |
|
|
109
|
+
|
|
110
|
+
`skippedRules(store)` counts `custom` as checked when the pane has a formula
|
|
111
|
+
evaluator. `skippedRules(book)` has only the workbook to go on, so it counts
|
|
112
|
+
every `custom` rule as skipped. The status line adds `· n rules not checked`.
|
|
113
|
+
|
|
114
|
+
### Custom rules need a formula engine
|
|
115
|
+
|
|
116
|
+
`custom` rules run only when the pane's `derive` plugin (`@sheetpane/formulas`)
|
|
117
|
+
also exposes:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
evaluate(formula: string, at: { sheet: string; ref: string }): boolean | number | string | null | undefined
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The plugin passes the formula with its relative references already shifted to
|
|
124
|
+
the cell. `true` or a non-zero number passes. `false` or `0` fails. Anything
|
|
125
|
+
else counts as unknown, and the cell is not flagged.
|
|
126
|
+
|
|
127
|
+
## The controls (`/ui`)
|
|
128
|
+
|
|
129
|
+
- **List arrow** (`cell` slot, Edit only): appears in every cell a `list` rule
|
|
130
|
+
covers. It opens the rule's current choices, and picking one sets the cell
|
|
131
|
+
with `store.setCell`, which can be undone.
|
|
132
|
+
- **Status** (`status` slot, View and Edit): `3 validation warnings`, plus
|
|
133
|
+
`· 1 rule not checked` when rules were skipped.
|
|
134
|
+
|
|
135
|
+
`<sheetpane-errors>` lists the rule rows with the reviewer's own errors. Its
|
|
136
|
+
author filter (Anyone · People · Workbook rules · File comments) shows only
|
|
137
|
+
the rule rows when you need them.
|
|
138
|
+
|
|
139
|
+
## Known limits
|
|
140
|
+
|
|
141
|
+
- A list whose source is on another sheet is re-checked when its own sheet
|
|
142
|
+
changes, not when the source sheet changes. The arrow's menu is always
|
|
143
|
+
current.
|
|
144
|
+
- A cell holding a formula is checked against its saved value. If the user
|
|
145
|
+
typed the formula, the plugin uses the value the formula plugin derived. The
|
|
146
|
+
check runs on the edit, before the formula plugin has finished deriving.
|
|
147
|
+
- `found` is fixed when the error is first made. If an error reopens, it shows
|
|
148
|
+
the first failing value.
|
|
149
|
+
|
|
150
|
+
**Using an AI coding agent?** `npx sheetpane-skills install` puts sheetpane's skills for Claude Code, Kiro and Codex into your repository.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type ErrorDraft, type Plugin, type Severity, type WorkbookStore } from '@sheetpane/grid';
|
|
2
|
+
import { type Book, type Rule, type SkipReason } from './rules.js';
|
|
3
|
+
export type { Book, Rule, RuleKind, Operator, SkipReason, Evaluate } from './rules.js';
|
|
4
|
+
export { rulesOf, listOptions, ruleWords, check, shiftFormula, parseOperand } from './rules.js';
|
|
5
|
+
/** The plugin's id, and the name `plugins="…"` takes. */
|
|
6
|
+
export declare const PLUGIN_ID = "validation";
|
|
7
|
+
/** The `type` of every error this plugin raises. */
|
|
8
|
+
export declare const ERROR_TYPE = "validation";
|
|
9
|
+
/** The `author` of every error this plugin raises: the workbook's own rules, not a person. */
|
|
10
|
+
export declare const AUTHOR = "workbook";
|
|
11
|
+
/** Options for {@link validation}. */
|
|
12
|
+
export interface ValidationOptions {
|
|
13
|
+
/** The severity a broken rule gets; `warning` by default. */
|
|
14
|
+
severity?: Severity;
|
|
15
|
+
}
|
|
16
|
+
/** A rule that is not checked: where it is, its type, and why (`custom`, `operand`, `operator`, `type`, `source`). */
|
|
17
|
+
export interface SkippedRule {
|
|
18
|
+
sheet: string;
|
|
19
|
+
sqref: string;
|
|
20
|
+
type: string;
|
|
21
|
+
reason: SkipReason;
|
|
22
|
+
detail: string;
|
|
23
|
+
}
|
|
24
|
+
/** How many of a workbook's rules are checked, and the ones that are not. */
|
|
25
|
+
export interface SkipReport {
|
|
26
|
+
/** Rules the workbook holds (`type="none"` excluded). */
|
|
27
|
+
rules: number;
|
|
28
|
+
/** Rules this pane checks. */
|
|
29
|
+
checked: number;
|
|
30
|
+
/** Rules it cannot check, and why. `skipped.length` is the count `sheetpane-load` would carry. */
|
|
31
|
+
skipped: SkippedRule[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The rules a workbook holds that are not checked, and why. Given a store, a
|
|
35
|
+
* `custom` rule counts as checked when its `derive` plugin offers `evaluate()`;
|
|
36
|
+
* given a bare workbook, every `custom` rule is skipped.
|
|
37
|
+
*
|
|
38
|
+
* @param from - The workbook, or the store holding it.
|
|
39
|
+
* @returns The report; all zero when no workbook is loaded.
|
|
40
|
+
*/
|
|
41
|
+
export declare function skippedRules(from: Book | WorkbookStore): SkipReport;
|
|
42
|
+
/**
|
|
43
|
+
* The last report `validate` made for a workbook (the status line reads it).
|
|
44
|
+
*
|
|
45
|
+
* @param book - The workbook, or `undefined`.
|
|
46
|
+
* @returns The report, or `undefined` before the first `validate`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function lastReport(book: Book | undefined): SkipReport | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Every failing cell on one sheet: one error draft per cell (the first rule it breaks), within the used range.
|
|
51
|
+
*
|
|
52
|
+
* @param sheet - The sheet's name.
|
|
53
|
+
* @param store - The workbook store.
|
|
54
|
+
* @param severity - The drafts' severity; default `warning`.
|
|
55
|
+
* @returns The drafts — `type: 'validation'`, `author: 'workbook'`, `found`, `expected` and `message` filled.
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateSheet(sheet: string, store: WorkbookStore, severity?: Severity): ErrorDraft[];
|
|
58
|
+
/**
|
|
59
|
+
* The data-validation plugin: checks the workbook's own `<dataValidations>` rules on load and after every change,
|
|
60
|
+
* through the `validate` hook. A cell that breaks a rule is one error (`type: 'validation'`, `author: 'workbook'`)
|
|
61
|
+
* that resolves when the cell passes and reopens when it fails again. `custom` rules need a formula engine
|
|
62
|
+
* (`@sheetpane/formulas`); rules it cannot check are listed by {@link skippedRules}. Importing this module
|
|
63
|
+
* registers it as `validation`; the list arrow and status count are in `@sheetpane/validation/ui`.
|
|
64
|
+
*
|
|
65
|
+
* @param o - The severity a broken rule gets; default `warning`.
|
|
66
|
+
* @returns The plugin (`id: 'validation'`).
|
|
67
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
71
|
+
* import { formulas } from '@sheetpane/formulas';
|
|
72
|
+
* import { skippedRules, validation } from '@sheetpane/validation';
|
|
73
|
+
*
|
|
74
|
+
* const book = createWorkbook({ plugins: [validation({ severity: 'error' }), formulas()] });
|
|
75
|
+
* await book.load(bytes, 'budget.xlsx');
|
|
76
|
+
* console.log(skippedRules(book).skipped);
|
|
77
|
+
* ```
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* import '@sheetpane/validation'; // registers `validation`
|
|
81
|
+
* excel.setAttribute('plugins', 'validation formulas');
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function validation(o?: ValidationOptions): Plugin;
|
|
85
|
+
/**
|
|
86
|
+
* The checkable `list` rule covering a cell, if any — the list arrow's `appliesTo`.
|
|
87
|
+
*
|
|
88
|
+
* @param store - The workbook store.
|
|
89
|
+
* @param sheet - The sheet's name.
|
|
90
|
+
* @param row - The cell's row, 0-based.
|
|
91
|
+
* @param col - The cell's column, 0-based.
|
|
92
|
+
* @returns The rule, or `undefined`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function listRuleAt(store: WorkbookStore, sheet: string, row: number, col: number): Rule | undefined;
|
|
95
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,MAAM,EAAsB,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACtI,OAAO,EAAiF,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAElJ,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhG,yDAAyD;AACzD,eAAO,MAAM,SAAS,eAAe,CAAC;AACtC,oDAAoD;AACpD,eAAO,MAAM,UAAU,eAAe,CAAC;AACvC,8FAA8F;AAC9F,eAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,sHAAsH;AACtH,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAC/G,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAID;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,GAAG,UAAU,CAcnE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAEzE;AAQD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,GAAE,QAAoB,GAAG,UAAU,EAAE,CAwB/G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAE,iBAAsB,GAAG,MAAM,CAU5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAI1G"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// @sheetpane/validation — the workbook's own `<dataValidations>` rules, checked on
|
|
2
|
+
// load and on every edit through the `validate` hook (W-18). A cell that breaks a
|
|
3
|
+
// rule is one error: type `validation`, author `workbook`; the store resolves it
|
|
4
|
+
// when the cell passes and reopens it when it fails again.
|
|
5
|
+
// Headless: this entry never imports ./ui.js. Importing it in Node is safe.
|
|
6
|
+
import { registerPlugin } from '@sheetpane/grid';
|
|
7
|
+
import { a1, check, covers, evaluatorOf, ruleWords, rulesOf, skipOf, textAt, usedRange } from './rules.js';
|
|
8
|
+
export { rulesOf, listOptions, ruleWords, check, shiftFormula, parseOperand } from './rules.js';
|
|
9
|
+
/** The plugin's id, and the name `plugins="…"` takes. */
|
|
10
|
+
export const PLUGIN_ID = 'validation';
|
|
11
|
+
/** The `type` of every error this plugin raises. */
|
|
12
|
+
export const ERROR_TYPE = 'validation';
|
|
13
|
+
/** The `author` of every error this plugin raises: the workbook's own rules, not a person. */
|
|
14
|
+
export const AUTHOR = 'workbook';
|
|
15
|
+
const reports = new WeakMap();
|
|
16
|
+
/**
|
|
17
|
+
* The rules a workbook holds that are not checked, and why. Given a store, a
|
|
18
|
+
* `custom` rule counts as checked when its `derive` plugin offers `evaluate()`;
|
|
19
|
+
* given a bare workbook, every `custom` rule is skipped.
|
|
20
|
+
*
|
|
21
|
+
* @param from - The workbook, or the store holding it.
|
|
22
|
+
* @returns The report; all zero when no workbook is loaded.
|
|
23
|
+
*/
|
|
24
|
+
export function skippedRules(from) {
|
|
25
|
+
const store = 'getSnapshot' in from ? from : undefined;
|
|
26
|
+
const book = store ? store.book : from;
|
|
27
|
+
const out = { rules: 0, checked: 0, skipped: [] };
|
|
28
|
+
if (!book)
|
|
29
|
+
return out;
|
|
30
|
+
for (const s of book.sheets) {
|
|
31
|
+
for (const r of rulesOf(book, s.name)) {
|
|
32
|
+
out.rules++;
|
|
33
|
+
const skip = skipOf(r, store);
|
|
34
|
+
if (skip)
|
|
35
|
+
out.skipped.push({ sheet: s.name, sqref: r.dv.sqref.join(' '), type: r.dv.type, reason: skip.reason, detail: skip.detail });
|
|
36
|
+
else
|
|
37
|
+
out.checked++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The last report `validate` made for a workbook (the status line reads it).
|
|
44
|
+
*
|
|
45
|
+
* @param book - The workbook, or `undefined`.
|
|
46
|
+
* @returns The report, or `undefined` before the first `validate`.
|
|
47
|
+
*/
|
|
48
|
+
export function lastReport(book) {
|
|
49
|
+
return book ? reports.get(book) : undefined;
|
|
50
|
+
}
|
|
51
|
+
/** The rule's own error text — `errorTitle: error`, either alone — else the rule in words. */
|
|
52
|
+
function messageOf(rule, words) {
|
|
53
|
+
const t = rule.dv.errorTitle?.trim(), e = rule.dv.error?.trim();
|
|
54
|
+
return t && e ? `${t}: ${e}` : t || e || words;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Every failing cell on one sheet: one error draft per cell (the first rule it breaks), within the used range.
|
|
58
|
+
*
|
|
59
|
+
* @param sheet - The sheet's name.
|
|
60
|
+
* @param store - The workbook store.
|
|
61
|
+
* @param severity - The drafts' severity; default `warning`.
|
|
62
|
+
* @returns The drafts — `type: 'validation'`, `author: 'workbook'`, `found`, `expected` and `message` filled.
|
|
63
|
+
*/
|
|
64
|
+
export function validateSheet(sheet, store, severity = 'warning') {
|
|
65
|
+
const book = store.book, s = store.sheet(sheet);
|
|
66
|
+
if (!book || !s)
|
|
67
|
+
return [];
|
|
68
|
+
const rules = rulesOf(book, sheet).filter((r) => !skipOf(r, store));
|
|
69
|
+
if (!rules.length)
|
|
70
|
+
return [];
|
|
71
|
+
const evaluate = evaluatorOf(store);
|
|
72
|
+
const used = usedRange(store, s);
|
|
73
|
+
const out = [];
|
|
74
|
+
const flagged = new Set();
|
|
75
|
+
const memo = new Map(); // a fixed list source is read once per run
|
|
76
|
+
for (const rule of rules) {
|
|
77
|
+
for (const b of rule.boxes) {
|
|
78
|
+
for (let r = b.top; r <= Math.min(b.bottom, used.rows - 1); r++) {
|
|
79
|
+
for (let c = b.left; c <= Math.min(b.right, used.cols - 1); c++) {
|
|
80
|
+
const ref = a1(r, c);
|
|
81
|
+
if (flagged.has(ref) || check(rule, store, r, c, evaluate, memo) !== 'fail')
|
|
82
|
+
continue;
|
|
83
|
+
flagged.add(ref);
|
|
84
|
+
const expected = ruleWords(rule, store, r, c);
|
|
85
|
+
out.push({ sheet, range: ref, type: ERROR_TYPE, severity, found: textAt(store, sheet, ref) || '(blank)', expected, message: messageOf(rule, expected), author: AUTHOR });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The data-validation plugin: checks the workbook's own `<dataValidations>` rules on load and after every change,
|
|
94
|
+
* through the `validate` hook. A cell that breaks a rule is one error (`type: 'validation'`, `author: 'workbook'`)
|
|
95
|
+
* that resolves when the cell passes and reopens when it fails again. `custom` rules need a formula engine
|
|
96
|
+
* (`@sheetpane/formulas`); rules it cannot check are listed by {@link skippedRules}. Importing this module
|
|
97
|
+
* registers it as `validation`; the list arrow and status count are in `@sheetpane/validation/ui`.
|
|
98
|
+
*
|
|
99
|
+
* @param o - The severity a broken rule gets; default `warning`.
|
|
100
|
+
* @returns The plugin (`id: 'validation'`).
|
|
101
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
105
|
+
* import { formulas } from '@sheetpane/formulas';
|
|
106
|
+
* import { skippedRules, validation } from '@sheetpane/validation';
|
|
107
|
+
*
|
|
108
|
+
* const book = createWorkbook({ plugins: [validation({ severity: 'error' }), formulas()] });
|
|
109
|
+
* await book.load(bytes, 'budget.xlsx');
|
|
110
|
+
* console.log(skippedRules(book).skipped);
|
|
111
|
+
* ```
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import '@sheetpane/validation'; // registers `validation`
|
|
115
|
+
* excel.setAttribute('plugins', 'validation formulas');
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function validation(o = {}) {
|
|
119
|
+
const severity = o.severity ?? 'warning';
|
|
120
|
+
return {
|
|
121
|
+
id: PLUGIN_ID,
|
|
122
|
+
provides: ['validate'],
|
|
123
|
+
validate(sheet, ctx) {
|
|
124
|
+
if (ctx.book && !reports.has(ctx.book))
|
|
125
|
+
reports.set(ctx.book, skippedRules(ctx.store));
|
|
126
|
+
return validateSheet(sheet, ctx.store, severity);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The checkable `list` rule covering a cell, if any — the list arrow's `appliesTo`.
|
|
132
|
+
*
|
|
133
|
+
* @param store - The workbook store.
|
|
134
|
+
* @param sheet - The sheet's name.
|
|
135
|
+
* @param row - The cell's row, 0-based.
|
|
136
|
+
* @param col - The cell's column, 0-based.
|
|
137
|
+
* @returns The rule, or `undefined`.
|
|
138
|
+
*/
|
|
139
|
+
export function listRuleAt(store, sheet, row, col) {
|
|
140
|
+
const book = store.book;
|
|
141
|
+
if (!book)
|
|
142
|
+
return undefined;
|
|
143
|
+
return rulesOf(book, sheet).find((r) => r.kind === 'list' && !r.skip && covers(r, row, col));
|
|
144
|
+
}
|
|
145
|
+
registerPlugin(PLUGIN_ID, () => validation());
|
|
146
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { shiftFormula, type CellContext, type PluginContext, type WorkbookStore } from '@sheetpane/grid';
|
|
2
|
+
/** The imported workbook, as a plugin hook receives it. */
|
|
3
|
+
export type Book = PluginContext['book'];
|
|
4
|
+
export type Sheet = CellContext['sheet'];
|
|
5
|
+
export type Cell = CellContext['value'];
|
|
6
|
+
export type DataValidation = Sheet['dataValidations'][number];
|
|
7
|
+
/** A data-validation rule's type (`<dataValidation type>`); `custom` needs a formula engine. */
|
|
8
|
+
export type RuleKind = 'whole' | 'decimal' | 'date' | 'time' | 'textLength' | 'list' | 'custom';
|
|
9
|
+
/** A rule's comparison (`<dataValidation operator>`); `between` is the default and inclusive. */
|
|
10
|
+
export type Operator = 'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual';
|
|
11
|
+
/** Why a rule is not checked: `custom` without a formula evaluator, an operator or operand outside the subset, an unknown type, a list source that names nothing. */
|
|
12
|
+
export type SkipReason = 'custom' | 'operator' | 'operand' | 'type' | 'source';
|
|
13
|
+
export interface Box {
|
|
14
|
+
top: number;
|
|
15
|
+
left: number;
|
|
16
|
+
bottom: number;
|
|
17
|
+
right: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function colIndex(letters: string): number;
|
|
20
|
+
export declare function colLetters(col: number): string;
|
|
21
|
+
export declare const a1: (row: number, col: number) => string;
|
|
22
|
+
/** `B3`, `B3:B5`, `B:B`, `3:3` (`$` allowed) → a box; undefined when it is none of these. */
|
|
23
|
+
export declare function parseBox(s: string): Box | undefined;
|
|
24
|
+
/** A formula1/formula2 the subset evaluates: a number, a string, DATE/TIME of numbers, a cell (relative parts shift with the cell), or a range (list sources). */
|
|
25
|
+
export type Operand = {
|
|
26
|
+
k: 'num';
|
|
27
|
+
v: number;
|
|
28
|
+
} | {
|
|
29
|
+
k: 'str';
|
|
30
|
+
v: string;
|
|
31
|
+
} | {
|
|
32
|
+
k: 'ref';
|
|
33
|
+
sheet: string | undefined;
|
|
34
|
+
row: number;
|
|
35
|
+
col: number;
|
|
36
|
+
absRow: boolean;
|
|
37
|
+
absCol: boolean;
|
|
38
|
+
} | {
|
|
39
|
+
k: 'range';
|
|
40
|
+
sheet: string | undefined;
|
|
41
|
+
box: Box;
|
|
42
|
+
absRow: boolean;
|
|
43
|
+
absCol: boolean;
|
|
44
|
+
};
|
|
45
|
+
export declare function dateSerial(y: number, m: number, d: number, d1904: boolean): number;
|
|
46
|
+
/**
|
|
47
|
+
* Parses one operand (`formula1` / `formula2`): a number, quoted text, `DATE` / `TIME` of numbers, a cell or a
|
|
48
|
+
* range; a defined name is followed (three levels) to its formula.
|
|
49
|
+
*
|
|
50
|
+
* @param f - The operand as the file stores it, with or without `=`.
|
|
51
|
+
* @param book - The workbook (its date system and defined names).
|
|
52
|
+
* @param sheetIndex - The rule's sheet, 0-based — a sheet-scoped name wins over a global one.
|
|
53
|
+
* @param depth - How many defined names have been followed already; callers omit it.
|
|
54
|
+
* @returns The operand, or `undefined` when it is outside the subset (the rule is then skipped).
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseOperand(f: string, book: Book, sheetIndex: number, depth?: number): Operand | undefined;
|
|
57
|
+
/** Shifting relative references is @sheetpane/export's `shiftFormula`, re-exported by the grid peer. */
|
|
58
|
+
export { shiftFormula };
|
|
59
|
+
/** One `<dataValidation>`, compiled: its kind, operator, ranges, parsed operands, and why it is skipped, if it is. */
|
|
60
|
+
export interface Rule {
|
|
61
|
+
sheet: string;
|
|
62
|
+
dv: DataValidation;
|
|
63
|
+
kind: RuleKind;
|
|
64
|
+
op: Operator;
|
|
65
|
+
boxes: Box[];
|
|
66
|
+
/** The top-left of the first range: relative references are written against it. */
|
|
67
|
+
anchor: {
|
|
68
|
+
row: number;
|
|
69
|
+
col: number;
|
|
70
|
+
};
|
|
71
|
+
f1?: Operand;
|
|
72
|
+
f2?: Operand;
|
|
73
|
+
/** `list`: the literal choices. */
|
|
74
|
+
literal?: string[];
|
|
75
|
+
skip?: {
|
|
76
|
+
reason: SkipReason;
|
|
77
|
+
detail: string;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The sheet's rules, compiled once per workbook. `type="none"` (any value) is not a rule.
|
|
82
|
+
*
|
|
83
|
+
* @param book - The workbook.
|
|
84
|
+
* @param sheetName - The sheet's name.
|
|
85
|
+
* @returns The compiled rules, skipped ones included (they carry `skip`).
|
|
86
|
+
*/
|
|
87
|
+
export declare function rulesOf(book: Book, sheetName: string): Rule[];
|
|
88
|
+
/** What `@sheetpane/validation` needs from a formula engine for `custom` rules: the `derive` provider's synchronous `evaluate`. */
|
|
89
|
+
export type Evaluate = (formula: string, at: {
|
|
90
|
+
sheet: string;
|
|
91
|
+
ref: string;
|
|
92
|
+
}) => Cell | undefined;
|
|
93
|
+
export declare function evaluatorOf(store: WorkbookStore): Evaluate | undefined;
|
|
94
|
+
/** The reason a rule is not checked in this store, if it is not. */
|
|
95
|
+
export declare function skipOf(rule: Rule, store: WorkbookStore | undefined): Rule['skip'];
|
|
96
|
+
/** The cell's value now. A typed formula (change kind `formula`) is its derived result, not its text. */
|
|
97
|
+
export declare function valueAt(store: WorkbookStore, sheet: string, ref: string): Cell | undefined;
|
|
98
|
+
export declare function textAt(store: WorkbookStore, sheet: string, ref: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* A `list` rule's choices at one cell — the literal list, or read live from the store for a range (relative parts
|
|
101
|
+
* shifted to the cell; empty and repeated values dropped).
|
|
102
|
+
*
|
|
103
|
+
* @param rule - A `list` rule.
|
|
104
|
+
* @param store - The workbook store.
|
|
105
|
+
* @param row - The cell's row, 0-based.
|
|
106
|
+
* @param col - The cell's column, 0-based.
|
|
107
|
+
* @returns The choices, in order.
|
|
108
|
+
*/
|
|
109
|
+
export declare function listOptions(rule: Rule, store: WorkbookStore, row: number, col: number): string[];
|
|
110
|
+
/** The rows and columns a sheet uses: its data, plus any cell a user changed. */
|
|
111
|
+
export declare function usedRange(store: WorkbookStore, s: Sheet): {
|
|
112
|
+
rows: number;
|
|
113
|
+
cols: number;
|
|
114
|
+
};
|
|
115
|
+
export type Verdict = 'pass' | 'fail' | 'unknown';
|
|
116
|
+
/**
|
|
117
|
+
* Does one cell pass one (checkable) rule? A blank cell passes with `allowBlank`.
|
|
118
|
+
*
|
|
119
|
+
* @param rule - A rule that is not skipped.
|
|
120
|
+
* @param store - The workbook store (current values).
|
|
121
|
+
* @param row - The cell's row, 0-based.
|
|
122
|
+
* @param col - The cell's column, 0-based.
|
|
123
|
+
* @param evaluate - The formula engine's `evaluate`, for `custom` rules; `undefined` without one.
|
|
124
|
+
* @param memo - A cache of list sources, shared across one run.
|
|
125
|
+
* @returns `pass`, `fail`, or `unknown` when an operand cannot be read — not an error.
|
|
126
|
+
*/
|
|
127
|
+
export declare function check(rule: Rule, store: WorkbookStore, row: number, col: number, evaluate: Evaluate | undefined, memo?: Map<Rule, string[]>): Verdict;
|
|
128
|
+
export declare function dateText(serial: number, d1904: boolean): string;
|
|
129
|
+
export declare function timeText(serial: number): string;
|
|
130
|
+
/**
|
|
131
|
+
* The rule in words, at one cell: `whole number > 0`, `decimal between 0.00% and 2.00%`, `date ≤ 30-Sep-2026`,
|
|
132
|
+
* `list — Core, Income, Cap` — an error's `expected`.
|
|
133
|
+
*
|
|
134
|
+
* @param rule - The rule.
|
|
135
|
+
* @param store - The workbook store (cell operands are read live).
|
|
136
|
+
* @param row - The cell's row, 0-based.
|
|
137
|
+
* @param col - The cell's column, 0-based.
|
|
138
|
+
* @returns The sentence.
|
|
139
|
+
*/
|
|
140
|
+
export declare function ruleWords(rule: Rule, store: WorkbookStore, row: number, col: number): string;
|
|
141
|
+
/** Does a cell sit in one of the rule's ranges? */
|
|
142
|
+
export declare const covers: (rule: Rule, row: number, col: number) => boolean;
|
|
143
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/rules.js
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// The rule engine: a sheet's `<dataValidations>` compiled once per workbook, then
|
|
2
|
+
// checked against the store's current values. No DOM, no dependency — the types
|
|
3
|
+
// come from @sheetpane/grid, the values from the store a hook is handed.
|
|
4
|
+
import { shiftFormula } from '@sheetpane/grid';
|
|
5
|
+
const KINDS = ['whole', 'decimal', 'date', 'time', 'textLength', 'list', 'custom'];
|
|
6
|
+
const OPERATORS = ['between', 'notBetween', 'equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual'];
|
|
7
|
+
const MAX_ROWS = 1048576, MAX_COLS = 16384;
|
|
8
|
+
export function colIndex(letters) {
|
|
9
|
+
let n = 0;
|
|
10
|
+
for (const ch of letters.toUpperCase())
|
|
11
|
+
n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
12
|
+
return n - 1;
|
|
13
|
+
}
|
|
14
|
+
export function colLetters(col) {
|
|
15
|
+
let s = '';
|
|
16
|
+
for (let n = col + 1; n > 0; n = Math.floor((n - 1) / 26))
|
|
17
|
+
s = String.fromCharCode(65 + ((n - 1) % 26)) + s;
|
|
18
|
+
return s;
|
|
19
|
+
}
|
|
20
|
+
export const a1 = (row, col) => `${colLetters(col)}${row + 1}`;
|
|
21
|
+
/** `B3`, `B3:B5`, `B:B`, `3:3` (`$` allowed) → a box; undefined when it is none of these. */
|
|
22
|
+
export function parseBox(s) {
|
|
23
|
+
const t = s.replace(/\$/g, '').toUpperCase();
|
|
24
|
+
let m = /^([A-Z]{1,3})(\d+)(?::([A-Z]{1,3})(\d+))?$/.exec(t);
|
|
25
|
+
if (m) {
|
|
26
|
+
const r1 = Number(m[2]) - 1, c1 = colIndex(m[1]);
|
|
27
|
+
const r2 = m[4] ? Number(m[4]) - 1 : r1, c2 = m[3] ? colIndex(m[3]) : c1;
|
|
28
|
+
if (r1 < 0 || r2 < 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { top: Math.min(r1, r2), left: Math.min(c1, c2), bottom: Math.max(r1, r2), right: Math.max(c1, c2) };
|
|
31
|
+
}
|
|
32
|
+
m = /^([A-Z]{1,3}):([A-Z]{1,3})$/.exec(t);
|
|
33
|
+
if (m) {
|
|
34
|
+
const a = colIndex(m[1]), b = colIndex(m[2]);
|
|
35
|
+
return { top: 0, left: Math.min(a, b), bottom: MAX_ROWS - 1, right: Math.max(a, b) };
|
|
36
|
+
}
|
|
37
|
+
m = /^(\d+):(\d+)$/.exec(t);
|
|
38
|
+
if (m) {
|
|
39
|
+
const a = Number(m[1]) - 1, b = Number(m[2]) - 1;
|
|
40
|
+
return { top: Math.min(a, b), left: 0, bottom: Math.max(a, b), right: MAX_COLS - 1 };
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const inBox = (b, row, col) => row >= b.top && row <= b.bottom && col >= b.left && col <= b.right;
|
|
45
|
+
const NUM = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
46
|
+
const SHEET = String.raw `(?:'((?:[^']|'')+)'|([^\s!'"(),:=<>&+\-*/^]+))!`;
|
|
47
|
+
const REF = new RegExp(`^(?:${SHEET})?(\\$?)([A-Za-z]{1,3})(\\$?)(\\d+)$`);
|
|
48
|
+
const RANGE = new RegExp(`^(?:${SHEET})?(\\$?[A-Za-z]{1,3}\\$?\\d+:\\$?[A-Za-z]{1,3}\\$?\\d+|\\$?[A-Za-z]{1,3}:\\$?[A-Za-z]{1,3})$`);
|
|
49
|
+
function dayBase(d1904) { return d1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30); }
|
|
50
|
+
export function dateSerial(y, m, d, d1904) { return Math.round((Date.UTC(y, m - 1, d) - dayBase(d1904)) / 864e5); }
|
|
51
|
+
/**
|
|
52
|
+
* Parses one operand (`formula1` / `formula2`): a number, quoted text, `DATE` / `TIME` of numbers, a cell or a
|
|
53
|
+
* range; a defined name is followed (three levels) to its formula.
|
|
54
|
+
*
|
|
55
|
+
* @param f - The operand as the file stores it, with or without `=`.
|
|
56
|
+
* @param book - The workbook (its date system and defined names).
|
|
57
|
+
* @param sheetIndex - The rule's sheet, 0-based — a sheet-scoped name wins over a global one.
|
|
58
|
+
* @param depth - How many defined names have been followed already; callers omit it.
|
|
59
|
+
* @returns The operand, or `undefined` when it is outside the subset (the rule is then skipped).
|
|
60
|
+
*/
|
|
61
|
+
export function parseOperand(f, book, sheetIndex, depth = 0) {
|
|
62
|
+
const t = f.trim().replace(/^=/, '').trim();
|
|
63
|
+
if (NUM.test(t))
|
|
64
|
+
return { k: 'num', v: Number(t) };
|
|
65
|
+
let m = /^"((?:[^"]|"")*)"$/.exec(t);
|
|
66
|
+
if (m)
|
|
67
|
+
return { k: 'str', v: m[1].replace(/""/g, '"') };
|
|
68
|
+
m = /^(DATE|TIME)\(\s*([^,()]+?)\s*,\s*([^,()]+?)\s*,\s*([^,()]+?)\s*\)$/i.exec(t);
|
|
69
|
+
if (m) {
|
|
70
|
+
const [a, b, c] = [m[2], m[3], m[4]].map(Number);
|
|
71
|
+
if (![a, b, c].every(Number.isFinite))
|
|
72
|
+
return undefined;
|
|
73
|
+
return m[1].toUpperCase() === 'DATE' ? { k: 'num', v: dateSerial(a, b, c, book.date1904) } : { k: 'num', v: (a * 3600 + b * 60 + c) / 86400 };
|
|
74
|
+
}
|
|
75
|
+
m = REF.exec(t);
|
|
76
|
+
if (m) {
|
|
77
|
+
const sheet = m[1] !== undefined ? m[1].replace(/''/g, "'") : m[2];
|
|
78
|
+
return { k: 'ref', sheet, absCol: m[3] === '$', col: colIndex(m[4]), absRow: m[5] === '$', row: Number(m[6]) - 1 };
|
|
79
|
+
}
|
|
80
|
+
m = RANGE.exec(t);
|
|
81
|
+
if (m) {
|
|
82
|
+
const sheet = m[1] !== undefined ? m[1].replace(/''/g, "'") : m[2];
|
|
83
|
+
const body = m[3], box = parseBox(body);
|
|
84
|
+
if (!box)
|
|
85
|
+
return undefined;
|
|
86
|
+
const first = body.split(':')[0];
|
|
87
|
+
return { k: 'range', sheet, box, absCol: first.startsWith('$'), absRow: /^\$?[A-Za-z]+\$/.test(first) || /^\$?[A-Za-z]+:/.test(body) };
|
|
88
|
+
}
|
|
89
|
+
if (depth < 3 && /^[A-Za-z_\\][\w.]*$/.test(t)) {
|
|
90
|
+
const names = book.definedNames.filter((n) => n.name.toLowerCase() === t.toLowerCase());
|
|
91
|
+
const n = names.find((x) => x.localSheetId === sheetIndex) ?? names.find((x) => x.localSheetId === undefined);
|
|
92
|
+
if (n)
|
|
93
|
+
return parseOperand(n.formula, book, sheetIndex, depth + 1);
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
/** Shifting relative references is @sheetpane/export's `shiftFormula`, re-exported by the grid peer. */
|
|
98
|
+
export { shiftFormula };
|
|
99
|
+
const cache = new WeakMap();
|
|
100
|
+
/**
|
|
101
|
+
* The sheet's rules, compiled once per workbook. `type="none"` (any value) is not a rule.
|
|
102
|
+
*
|
|
103
|
+
* @param book - The workbook.
|
|
104
|
+
* @param sheetName - The sheet's name.
|
|
105
|
+
* @returns The compiled rules, skipped ones included (they carry `skip`).
|
|
106
|
+
*/
|
|
107
|
+
export function rulesOf(book, sheetName) {
|
|
108
|
+
let bySheet = cache.get(book);
|
|
109
|
+
if (!bySheet)
|
|
110
|
+
cache.set(book, (bySheet = new Map()));
|
|
111
|
+
let rules = bySheet.get(sheetName);
|
|
112
|
+
if (!rules)
|
|
113
|
+
bySheet.set(sheetName, (rules = compile(book, sheetName)));
|
|
114
|
+
return rules;
|
|
115
|
+
}
|
|
116
|
+
function compile(book, sheetName) {
|
|
117
|
+
const index = book.sheets.findIndex((s) => s.name === sheetName);
|
|
118
|
+
const sheet = book.sheets[index];
|
|
119
|
+
if (!sheet)
|
|
120
|
+
return [];
|
|
121
|
+
const out = [];
|
|
122
|
+
for (const dv of sheet.dataValidations) {
|
|
123
|
+
if (dv.type === 'none' || dv.type === '' || dv.type === 'any')
|
|
124
|
+
continue;
|
|
125
|
+
const boxes = dv.sqref.map(parseBox).filter((b) => !!b);
|
|
126
|
+
if (!boxes.length)
|
|
127
|
+
continue;
|
|
128
|
+
const kind = dv.type, op = (dv.operator ?? 'between');
|
|
129
|
+
const rule = { sheet: sheetName, dv, kind, op, boxes, anchor: { row: boxes[0].top, col: boxes[0].left } };
|
|
130
|
+
out.push(rule);
|
|
131
|
+
if (!KINDS.includes(kind)) {
|
|
132
|
+
rule.skip = { reason: 'type', detail: `rule type "${dv.type}"` };
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (kind === 'custom')
|
|
136
|
+
continue; // checked only with a formula evaluator (see `evaluatorOf`)
|
|
137
|
+
if (kind === 'list') {
|
|
138
|
+
const f = (dv.formula1 ?? '').trim();
|
|
139
|
+
const lit = /^"((?:[^"]|"")*)"$/.exec(f);
|
|
140
|
+
if (lit) {
|
|
141
|
+
rule.literal = lit[1].replace(/""/g, '"').split(',').map((x) => x.trim());
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const o = parseOperand(f, book, index);
|
|
145
|
+
if (o && (o.k === 'range' || o.k === 'ref')) {
|
|
146
|
+
const src = o.sheet ?? sheetName;
|
|
147
|
+
if (!book.sheets.some((s) => s.name === src)) {
|
|
148
|
+
rule.skip = { reason: 'source', detail: `list source "${f}" names no sheet in this workbook` };
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
rule.f1 = o;
|
|
152
|
+
}
|
|
153
|
+
else if (o && o.k === 'num')
|
|
154
|
+
rule.literal = [String(o.v)];
|
|
155
|
+
else
|
|
156
|
+
rule.skip = { reason: 'source', detail: `list source "${f}" is not a list, a range or a defined name` };
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (!OPERATORS.includes(op)) {
|
|
160
|
+
rule.skip = { reason: 'operator', detail: `operator "${dv.operator}"` };
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const two = op === 'between' || op === 'notBetween';
|
|
164
|
+
const f1 = dv.formula1 !== undefined ? parseOperand(dv.formula1, book, index) : undefined;
|
|
165
|
+
const f2 = two && dv.formula2 !== undefined ? parseOperand(dv.formula2, book, index) : undefined;
|
|
166
|
+
const bad = (o) => !o || o.k === 'range';
|
|
167
|
+
if (bad(f1) || (two && bad(f2))) {
|
|
168
|
+
rule.skip = { reason: 'operand', detail: `operand "${bad(f1) ? dv.formula1 ?? '' : dv.formula2 ?? ''}" is not a number, a date, a time or a cell` };
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
rule.f1 = f1;
|
|
172
|
+
if (two)
|
|
173
|
+
rule.f2 = f2;
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
export function evaluatorOf(store) {
|
|
178
|
+
const p = store.plugins.find((x) => x.provides.includes('derive'));
|
|
179
|
+
return typeof p?.evaluate === 'function' ? p.evaluate.bind(p) : undefined;
|
|
180
|
+
}
|
|
181
|
+
/** The reason a rule is not checked in this store, if it is not. */
|
|
182
|
+
export function skipOf(rule, store) {
|
|
183
|
+
if (rule.skip)
|
|
184
|
+
return rule.skip;
|
|
185
|
+
if (rule.kind === 'custom' && !(store && evaluatorOf(store)))
|
|
186
|
+
return { reason: 'custom', detail: store?.plugins.some((p) => p.provides.includes('derive')) ? 'custom (formula) rule — the formula plugin offers no evaluate()' : 'custom (formula) rule — needs the formulas plugin' };
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
// ── values ────────────────────────────────────────────────────────────────
|
|
190
|
+
/** The cell's value now. A typed formula (change kind `formula`) is its derived result, not its text. */
|
|
191
|
+
export function valueAt(store, sheet, ref) {
|
|
192
|
+
const c = store.changeOf(sheet, ref);
|
|
193
|
+
if (c?.kind === 'formula') {
|
|
194
|
+
const d = store.getSnapshot().derived.get(`${sheet}!${ref}`);
|
|
195
|
+
return d ? d.value : undefined;
|
|
196
|
+
}
|
|
197
|
+
return store.value(sheet, ref);
|
|
198
|
+
}
|
|
199
|
+
export function textAt(store, sheet, ref) {
|
|
200
|
+
const c = store.changeOf(sheet, ref);
|
|
201
|
+
if (c?.kind === 'formula')
|
|
202
|
+
return store.getSnapshot().derived.get(`${sheet}!${ref}`)?.text ?? String(c.now);
|
|
203
|
+
return store.text(sheet, ref);
|
|
204
|
+
}
|
|
205
|
+
const toNumber = (v) => {
|
|
206
|
+
if (v === null)
|
|
207
|
+
return 0; // an empty cell reads as 0, as in Excel
|
|
208
|
+
if (typeof v === 'number')
|
|
209
|
+
return v;
|
|
210
|
+
if (typeof v === 'string') {
|
|
211
|
+
const t = v.trim();
|
|
212
|
+
return NUM.test(t) ? Number(t) : undefined;
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
};
|
|
216
|
+
/** An operand's number at one cell; undefined when the cell it reads is not a number. */
|
|
217
|
+
function operandAt(o, rule, store, row, col) {
|
|
218
|
+
if (o.k === 'num')
|
|
219
|
+
return o.v;
|
|
220
|
+
if (o.k === 'str')
|
|
221
|
+
return toNumber(o.v);
|
|
222
|
+
if (o.k === 'ref') {
|
|
223
|
+
const r = o.absRow ? o.row : o.row + row - rule.anchor.row, c = o.absCol ? o.col : o.col + col - rule.anchor.col;
|
|
224
|
+
if (r < 0 || c < 0)
|
|
225
|
+
return undefined;
|
|
226
|
+
return toNumber(valueAt(store, o.sheet ?? rule.sheet, a1(r, c)));
|
|
227
|
+
}
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* A `list` rule's choices at one cell — the literal list, or read live from the store for a range (relative parts
|
|
232
|
+
* shifted to the cell; empty and repeated values dropped).
|
|
233
|
+
*
|
|
234
|
+
* @param rule - A `list` rule.
|
|
235
|
+
* @param store - The workbook store.
|
|
236
|
+
* @param row - The cell's row, 0-based.
|
|
237
|
+
* @param col - The cell's column, 0-based.
|
|
238
|
+
* @returns The choices, in order.
|
|
239
|
+
*/
|
|
240
|
+
export function listOptions(rule, store, row, col) {
|
|
241
|
+
if (rule.literal)
|
|
242
|
+
return rule.literal;
|
|
243
|
+
const o = rule.f1;
|
|
244
|
+
if (!o || (o.k !== 'range' && o.k !== 'ref'))
|
|
245
|
+
return [];
|
|
246
|
+
const src = o.sheet ?? rule.sheet;
|
|
247
|
+
const dr = o.absRow ? 0 : row - rule.anchor.row, dc = o.absCol ? 0 : col - rule.anchor.col;
|
|
248
|
+
const box = o.k === 'ref' ? { top: o.row, left: o.col, bottom: o.row, right: o.col } : o.box;
|
|
249
|
+
const s = store.sheet(src);
|
|
250
|
+
const used = s ? usedRange(store, s) : { rows: 0, cols: 0 };
|
|
251
|
+
const out = [];
|
|
252
|
+
const seen = new Set();
|
|
253
|
+
for (let r = box.top + dr; r <= Math.min(box.bottom + dr, used.rows - 1); r++) {
|
|
254
|
+
for (let c = box.left + dc; c <= Math.min(box.right + dc, used.cols - 1); c++) {
|
|
255
|
+
if (r < 0 || c < 0)
|
|
256
|
+
continue;
|
|
257
|
+
const t = textAt(store, src, a1(r, c));
|
|
258
|
+
if (t === '' || seen.has(t))
|
|
259
|
+
continue;
|
|
260
|
+
seen.add(t);
|
|
261
|
+
out.push(t);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
/** The rows and columns a sheet uses: its data, plus any cell a user changed. */
|
|
267
|
+
export function usedRange(store, s) {
|
|
268
|
+
let rows = s.rows.length, cols = s.rows.reduce((m, r) => Math.max(m, r?.length ?? 0), 0);
|
|
269
|
+
const prefix = `${s.name}!`;
|
|
270
|
+
for (const k of store.getSnapshot().changes.keys()) {
|
|
271
|
+
if (!k.startsWith(prefix))
|
|
272
|
+
continue;
|
|
273
|
+
const box = parseBox(k.slice(prefix.length));
|
|
274
|
+
if (box && box.top === box.bottom && box.left === box.right) {
|
|
275
|
+
rows = Math.max(rows, box.top + 1);
|
|
276
|
+
cols = Math.max(cols, box.left + 1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return { rows, cols };
|
|
280
|
+
}
|
|
281
|
+
const compare = (op, x, a, b) => {
|
|
282
|
+
const lo = b === undefined ? a : Math.min(a, b), hi = b === undefined ? a : Math.max(a, b);
|
|
283
|
+
switch (op) {
|
|
284
|
+
case 'between': return x >= lo && x <= hi;
|
|
285
|
+
case 'notBetween': return x < lo || x > hi;
|
|
286
|
+
case 'equal': return x === a;
|
|
287
|
+
case 'notEqual': return x !== a;
|
|
288
|
+
case 'greaterThan': return x > a;
|
|
289
|
+
case 'lessThan': return x < a;
|
|
290
|
+
case 'greaterThanOrEqual': return x >= a;
|
|
291
|
+
case 'lessThanOrEqual': return x <= a;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Does one cell pass one (checkable) rule? A blank cell passes with `allowBlank`.
|
|
296
|
+
*
|
|
297
|
+
* @param rule - A rule that is not skipped.
|
|
298
|
+
* @param store - The workbook store (current values).
|
|
299
|
+
* @param row - The cell's row, 0-based.
|
|
300
|
+
* @param col - The cell's column, 0-based.
|
|
301
|
+
* @param evaluate - The formula engine's `evaluate`, for `custom` rules; `undefined` without one.
|
|
302
|
+
* @param memo - A cache of list sources, shared across one run.
|
|
303
|
+
* @returns `pass`, `fail`, or `unknown` when an operand cannot be read — not an error.
|
|
304
|
+
*/
|
|
305
|
+
export function check(rule, store, row, col, evaluate, memo) {
|
|
306
|
+
const ref = a1(row, col);
|
|
307
|
+
const v = valueAt(store, rule.sheet, ref);
|
|
308
|
+
if (v === undefined)
|
|
309
|
+
return 'unknown';
|
|
310
|
+
const blank = v === null || v === '';
|
|
311
|
+
if (blank && rule.dv.allowBlank)
|
|
312
|
+
return 'pass';
|
|
313
|
+
if (rule.kind === 'custom') {
|
|
314
|
+
if (!evaluate || !rule.dv.formula1)
|
|
315
|
+
return 'unknown';
|
|
316
|
+
const f = shiftFormula(rule.dv.formula1.replace(/^=/, ''), row - rule.anchor.row, col - rule.anchor.col);
|
|
317
|
+
let r;
|
|
318
|
+
try {
|
|
319
|
+
r = evaluate(f, { sheet: rule.sheet, ref });
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return 'unknown';
|
|
323
|
+
}
|
|
324
|
+
if (typeof r === 'boolean')
|
|
325
|
+
return r ? 'pass' : 'fail';
|
|
326
|
+
if (typeof r === 'number')
|
|
327
|
+
return r !== 0 ? 'pass' : 'fail';
|
|
328
|
+
return 'unknown';
|
|
329
|
+
}
|
|
330
|
+
if (rule.kind === 'list') {
|
|
331
|
+
if (blank)
|
|
332
|
+
return 'fail';
|
|
333
|
+
const fixed = !rule.f1 || (rule.f1.k !== 'ref' && rule.f1.k !== 'range') || (rule.f1.absRow && rule.f1.absCol);
|
|
334
|
+
let opts = fixed ? memo?.get(rule) : undefined;
|
|
335
|
+
if (!opts) {
|
|
336
|
+
opts = listOptions(rule, store, row, col);
|
|
337
|
+
if (fixed)
|
|
338
|
+
memo?.set(rule, opts);
|
|
339
|
+
}
|
|
340
|
+
const t = textAt(store, rule.sheet, ref).trim().toLowerCase();
|
|
341
|
+
if (opts.some((o) => o.trim().toLowerCase() === t))
|
|
342
|
+
return 'pass';
|
|
343
|
+
if (typeof v === 'number' && opts.some((o) => { const n = toNumber(o.replace(/(\d),(?=\d{3}\b)/g, '$1')); return n !== undefined && n === v; }))
|
|
344
|
+
return 'pass';
|
|
345
|
+
return 'fail';
|
|
346
|
+
}
|
|
347
|
+
let x;
|
|
348
|
+
if (rule.kind === 'textLength') {
|
|
349
|
+
x = v === null ? 0 : typeof v === 'string' ? v.length : typeof v === 'boolean' ? (v ? 4 : 5) : String(v).length;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
if (typeof v !== 'number')
|
|
353
|
+
return 'fail';
|
|
354
|
+
if (rule.kind === 'whole' && !Number.isInteger(v))
|
|
355
|
+
return 'fail';
|
|
356
|
+
x = v;
|
|
357
|
+
}
|
|
358
|
+
const a = operandAt(rule.f1, rule, store, row, col);
|
|
359
|
+
const b = rule.f2 ? operandAt(rule.f2, rule, store, row, col) : undefined;
|
|
360
|
+
if (a === undefined || (rule.f2 && b === undefined))
|
|
361
|
+
return 'unknown';
|
|
362
|
+
return compare(rule.op, x, a, b) ? 'pass' : 'fail';
|
|
363
|
+
}
|
|
364
|
+
// ── the rule in words (the error row's "expected") ────────────────────────
|
|
365
|
+
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
366
|
+
export function dateText(serial, d1904) {
|
|
367
|
+
const d = new Date(dayBase(d1904) + Math.floor(serial) * 864e5);
|
|
368
|
+
return `${String(d.getUTCDate()).padStart(2, '0')}-${MONTHS[d.getUTCMonth()]}-${d.getUTCFullYear()}`;
|
|
369
|
+
}
|
|
370
|
+
export function timeText(serial) {
|
|
371
|
+
const secs = Math.round((serial - Math.floor(serial)) * 86400) % 86400;
|
|
372
|
+
const h = Math.floor(secs / 3600), m = Math.floor((secs % 3600) / 60), s = secs % 60;
|
|
373
|
+
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}${s ? `:${String(s).padStart(2, '0')}` : ''}`;
|
|
374
|
+
}
|
|
375
|
+
const TYPE_WORDS = { whole: 'whole number', decimal: 'decimal', date: 'date', time: 'time', textLength: 'text length', list: 'list', custom: 'custom' };
|
|
376
|
+
const OP_WORDS = { between: 'between', notBetween: 'not between', equal: '=', notEqual: '≠', greaterThan: '>', lessThan: '<', greaterThanOrEqual: '≥', lessThanOrEqual: '≤' };
|
|
377
|
+
const LIST_SHOWN = 8;
|
|
378
|
+
/**
|
|
379
|
+
* The rule in words, at one cell: `whole number > 0`, `decimal between 0.00% and 2.00%`, `date ≤ 30-Sep-2026`,
|
|
380
|
+
* `list — Core, Income, Cap` — an error's `expected`.
|
|
381
|
+
*
|
|
382
|
+
* @param rule - The rule.
|
|
383
|
+
* @param store - The workbook store (cell operands are read live).
|
|
384
|
+
* @param row - The cell's row, 0-based.
|
|
385
|
+
* @param col - The cell's column, 0-based.
|
|
386
|
+
* @returns The sentence.
|
|
387
|
+
*/
|
|
388
|
+
export function ruleWords(rule, store, row, col) {
|
|
389
|
+
const ref = a1(row, col);
|
|
390
|
+
if (rule.kind === 'list') {
|
|
391
|
+
const opts = listOptions(rule, store, row, col);
|
|
392
|
+
const shown = opts.slice(0, LIST_SHOWN).join(', ');
|
|
393
|
+
return `list — ${shown || '(empty)'}${opts.length > LIST_SHOWN ? `, … (${opts.length - LIST_SHOWN} more)` : ''}`;
|
|
394
|
+
}
|
|
395
|
+
if (rule.kind === 'custom')
|
|
396
|
+
return `custom — =${(rule.dv.formula1 ?? '').replace(/^=/, '')}`;
|
|
397
|
+
const book = store.book;
|
|
398
|
+
const show = (o) => {
|
|
399
|
+
if (!o)
|
|
400
|
+
return '?';
|
|
401
|
+
const n = operandAt(o, rule, store, row, col);
|
|
402
|
+
if (n === undefined)
|
|
403
|
+
return o.k === 'ref' ? a1(o.row, o.col) : '?';
|
|
404
|
+
if (rule.kind === 'date')
|
|
405
|
+
return dateText(n, book?.date1904 ?? false);
|
|
406
|
+
if (rule.kind === 'time')
|
|
407
|
+
return timeText(n);
|
|
408
|
+
if (rule.kind === 'textLength')
|
|
409
|
+
return String(n);
|
|
410
|
+
return store.format(rule.sheet, ref, n);
|
|
411
|
+
};
|
|
412
|
+
const two = rule.op === 'between' || rule.op === 'notBetween';
|
|
413
|
+
return `${TYPE_WORDS[rule.kind]} ${OP_WORDS[rule.op]} ${show(rule.f1)}${two ? ` and ${show(rule.f2)}` : ''}${rule.kind === 'textLength' ? ' characters' : ''}`;
|
|
414
|
+
}
|
|
415
|
+
/** Does a cell sit in one of the rule's ranges? */
|
|
416
|
+
export const covers = (rule, row, col) => rule.boxes.some((b) => inBox(b, row, col));
|
|
417
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Plugin, type Snapshot } from '@sheetpane/grid';
|
|
2
|
+
import { type ValidationOptions } from './index.js';
|
|
3
|
+
/** The controls plugin's id. */
|
|
4
|
+
export declare const UI_ID = "validation-ui";
|
|
5
|
+
/**
|
|
6
|
+
* Open validation errors in a snapshot.
|
|
7
|
+
*
|
|
8
|
+
* @param snap - A snapshot of the store.
|
|
9
|
+
* @returns How many errors by the `workbook` author, of type `validation`, are open.
|
|
10
|
+
*/
|
|
11
|
+
export declare function openWarnings(snap: Snapshot): number;
|
|
12
|
+
/**
|
|
13
|
+
* The status line: `3 validation warnings`, plus `· 1 rule not checked` when some are skipped.
|
|
14
|
+
*
|
|
15
|
+
* @param snap - A snapshot of the store.
|
|
16
|
+
* @returns The text, or `''` when the workbook has no rules.
|
|
17
|
+
*/
|
|
18
|
+
export declare function statusText(snap: Snapshot): string;
|
|
19
|
+
/**
|
|
20
|
+
* The validation controls: a list arrow on every cell a `list` rule covers (Edit; mouse or Alt+↓, a pick sets the
|
|
21
|
+
* cell, undoably) and the count of open validation warnings in the status line. No hooks — pair it with
|
|
22
|
+
* {@link validation}; importing this module makes `plugins="validation"` enable both.
|
|
23
|
+
*
|
|
24
|
+
* @returns The controls plugin (`id: 'validation-ui'`).
|
|
25
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
29
|
+
* import { validation } from '@sheetpane/validation';
|
|
30
|
+
* import { ui } from '@sheetpane/validation/ui';
|
|
31
|
+
*
|
|
32
|
+
* const book = createWorkbook({ plugins: [validation(), ui()] });
|
|
33
|
+
* ```
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import '@sheetpane/validation/ui'; // registers `validation` with its controls
|
|
37
|
+
* excel.setAttribute('plugins', 'validation');
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function ui(): Plugin;
|
|
41
|
+
/**
|
|
42
|
+
* The validation plugin and its controls together.
|
|
43
|
+
*
|
|
44
|
+
* @param o - The validation plugin's options.
|
|
45
|
+
* @returns `[validation(o), ui()]`.
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
49
|
+
* import { validationWithUi } from '@sheetpane/validation/ui';
|
|
50
|
+
*
|
|
51
|
+
* const book = createWorkbook({ plugins: validationWithUi() });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function validationWithUi(o?: ValidationOptions): Plugin[];
|
|
55
|
+
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAIA,OAAO,EAA0C,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAA0D,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG5G,gCAAgC;AAChC,eAAO,MAAM,KAAK,kBAAkB,CAAC;AAGrC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAKjD;AA0BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,EAAE,IAAI,MAAM,CAE3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAE,iBAAsB,GAAG,MAAM,EAAE,CAEpE"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @sheetpane/validation/ui — the controls validation adds to the grid's slots
|
|
2
|
+
// (W-18 DoD 4): a list arrow in every cell a `list` rule covers (Edit), and the
|
|
3
|
+
// count of open validation warnings in the status line (both modes).
|
|
4
|
+
// Opt-in: the headless entry never imports this file.
|
|
5
|
+
import { registerPlugin } from '@sheetpane/grid';
|
|
6
|
+
import { AUTHOR, ERROR_TYPE, lastReport, listRuleAt, validation } from './index.js';
|
|
7
|
+
import { listOptions } from './rules.js';
|
|
8
|
+
/** The controls plugin's id. */
|
|
9
|
+
export const UI_ID = 'validation-ui';
|
|
10
|
+
const ARROW = '<svg viewBox="0 0 12 12" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5l3 3 3-3"/></svg>';
|
|
11
|
+
/**
|
|
12
|
+
* Open validation errors in a snapshot.
|
|
13
|
+
*
|
|
14
|
+
* @param snap - A snapshot of the store.
|
|
15
|
+
* @returns How many errors by the `workbook` author, of type `validation`, are open.
|
|
16
|
+
*/
|
|
17
|
+
export function openWarnings(snap) {
|
|
18
|
+
return snap.errors.filter((e) => e.author === AUTHOR && e.type === ERROR_TYPE && e.status === 'open').length;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The status line: `3 validation warnings`, plus `· 1 rule not checked` when some are skipped.
|
|
22
|
+
*
|
|
23
|
+
* @param snap - A snapshot of the store.
|
|
24
|
+
* @returns The text, or `''` when the workbook has no rules.
|
|
25
|
+
*/
|
|
26
|
+
export function statusText(snap) {
|
|
27
|
+
const report = lastReport(snap.book);
|
|
28
|
+
if (!report?.rules)
|
|
29
|
+
return '';
|
|
30
|
+
const n = openWarnings(snap), k = report.skipped.length;
|
|
31
|
+
return `${n} validation warning${n === 1 ? '' : 's'}${k ? ` · ${k} rule${k === 1 ? '' : 's'} not checked` : ''}`;
|
|
32
|
+
}
|
|
33
|
+
const listArrow = {
|
|
34
|
+
id: 'validation-list',
|
|
35
|
+
slot: 'cell',
|
|
36
|
+
group: 'validation',
|
|
37
|
+
label: 'Choose from the list',
|
|
38
|
+
icon: ARROW,
|
|
39
|
+
mode: 'edit',
|
|
40
|
+
appliesTo: (cell) => !!listRuleAt(cell.store, cell.sheet.name, cell.row, cell.col),
|
|
41
|
+
cellOptions: (cell) => {
|
|
42
|
+
const rule = listRuleAt(cell.store, cell.sheet.name, cell.row, cell.col);
|
|
43
|
+
return rule ? listOptions(rule, cell.store, cell.row, cell.col) : [];
|
|
44
|
+
},
|
|
45
|
+
choose: (store, sel, value) => { store.setCell(sel.sheet, sel.ref, value); },
|
|
46
|
+
};
|
|
47
|
+
const status = {
|
|
48
|
+
id: 'validation-status',
|
|
49
|
+
slot: 'status',
|
|
50
|
+
group: 'validation',
|
|
51
|
+
label: 'Validation warnings',
|
|
52
|
+
mode: 'both',
|
|
53
|
+
text: (snap) => statusText(snap),
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The validation controls: a list arrow on every cell a `list` rule covers (Edit; mouse or Alt+↓, a pick sets the
|
|
57
|
+
* cell, undoably) and the count of open validation warnings in the status line. No hooks — pair it with
|
|
58
|
+
* {@link validation}; importing this module makes `plugins="validation"` enable both.
|
|
59
|
+
*
|
|
60
|
+
* @returns The controls plugin (`id: 'validation-ui'`).
|
|
61
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
65
|
+
* import { validation } from '@sheetpane/validation';
|
|
66
|
+
* import { ui } from '@sheetpane/validation/ui';
|
|
67
|
+
*
|
|
68
|
+
* const book = createWorkbook({ plugins: [validation(), ui()] });
|
|
69
|
+
* ```
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import '@sheetpane/validation/ui'; // registers `validation` with its controls
|
|
73
|
+
* excel.setAttribute('plugins', 'validation');
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function ui() {
|
|
77
|
+
return { id: UI_ID, provides: [], controls: [listArrow, status] };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The validation plugin and its controls together.
|
|
81
|
+
*
|
|
82
|
+
* @param o - The validation plugin's options.
|
|
83
|
+
* @returns `[validation(o), ui()]`.
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
87
|
+
* import { validationWithUi } from '@sheetpane/validation/ui';
|
|
88
|
+
*
|
|
89
|
+
* const book = createWorkbook({ plugins: validationWithUi() });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function validationWithUi(o = {}) {
|
|
93
|
+
return [validation(o), ui()];
|
|
94
|
+
}
|
|
95
|
+
registerPlugin('validation', () => [validation(), ui()]);
|
|
96
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,qEAAqE;AACrE,sDAAsD;AACtD,OAAO,EAAE,cAAc,EAAsD,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAA0B,MAAM,YAAY,CAAC;AAC5G,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,gCAAgC;AAChC,MAAM,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC;AACrC,MAAM,KAAK,GAAG,yMAAyM,CAAC;AAExN;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;AAC/G,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,EAAE,KAAK;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IACxD,OAAO,GAAG,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACnH,CAAC;AAED,MAAM,SAAS,GAAsB;IACnC,EAAE,EAAE,iBAAiB;IACrB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,sBAAsB;IAC7B,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;IAClF,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;QACpB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,GAAsB;IAChC,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,qBAAqB;IAC5B,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,EAAE;IAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAC,GAAsB,EAAE;IACxD,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,cAAc,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sheetpane/validation",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Data validation for sheetpane — the workbook's own `<dataValidations>` rules checked on load and on every edit, landing in <sheetpane-errors> — as a plugin on the `validate` hook.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/arpitarya/sheetpane.git",
|
|
9
|
+
"directory": "packages/validation"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"./dist/index.js",
|
|
14
|
+
"./dist/ui.js",
|
|
15
|
+
"./src/index.ts",
|
|
16
|
+
"./src/ui.ts"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=22"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./ui": {
|
|
27
|
+
"types": "./dist/ui.d.ts",
|
|
28
|
+
"import": "./dist/ui.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@sheetpane/grid": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@sheetpane/skills": "0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@sheetpane/export": "0.1.0",
|
|
42
|
+
"@sheetpane/formulas": "0.1.0",
|
|
43
|
+
"@sheetpane/grid": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.build.json",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
51
|
+
}
|
|
6
52
|
}
|