@sheetpane/validation 0.3.0 → 0.4.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/README.md CHANGED
@@ -5,11 +5,22 @@
5
5
  A plugin for `@sheetpane/grid`. It reads the `<dataValidations>` the file's
6
6
  author saved, including the x14 form Excel uses for lists on another sheet,
7
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.
8
+ `<sheetpane-errors>` on the same pane both see the result. On export it can
9
+ also write the host's rules file into the `.xlsx` as Excel's own dropdowns
10
+ and checks. No third-party runtime dependencies (`@sheetpane/export` writes
11
+ the rules and gives `shiftFormula`); `@sheetpane/grid` is a peer. Versioned in lockstep with it.
10
12
 
11
- - `@sheetpane/validation`: the headless plugin (`validate` only).
13
+ - `@sheetpane/validation`: the headless plugin (`validate`, and `exportPatch` for the rules file).
12
14
  - `@sheetpane/validation/ui`: opt-in. Adds the list arrow and the status-line count.
15
+ - `@sheetpane/validation/check`: the same plugin with no registration, no
16
+ element and no Download switch (checking only — `exportPatch` is the page
17
+ entry's), and `check(bytes, options)` — `@sheetpane/grid/check`'s check with
18
+ this plugin on: the workbook's own validation from Node or a worker.
19
+
20
+ ```js
21
+ import { check } from '@sheetpane/validation/check';
22
+ const errors = await check(await readFile('budget.xlsx'), { name: 'budget.xlsx', severity: 'error' });
23
+ ```
13
24
 
14
25
  ## Use
15
26
 
@@ -80,6 +91,7 @@ What an operand can be:
80
91
  - a quoted string
81
92
  - a cell: `$G$1`, `Sheet2!B1`, or a relative `D1`, which shifts with each cell the same way it does in Excel
82
93
  - a defined name that resolves to one of these
94
+ - `INDIRECT(<cell>)`: the cell named by that cell's text (see dependent lists below)
83
95
 
84
96
  A cell operand is read live, so changing the limit re-checks the rule. An
85
97
  empty operand cell reads as 0.
@@ -89,10 +101,34 @@ A `list` source can be:
89
101
  - a literal list: `"a,b,c"`
90
102
  - a range: `$H$1:$H$9`, `Lists!$A$1:$A$9`, `$A:$A`
91
103
  - a defined name that resolves to a range
104
+ - an Excel table's column: `Units[Unit]` or `Units[[#Data],[Unit]]`, directly or
105
+ through a defined name — the column's data rows, as the file stores the table
106
+
107
+ - `INDIRECT($A2)` — a **dependent list** (W-60): each row reads its parent
108
+ cell's text and lists what it names — a defined name (any case; the sheet's
109
+ own name before a global one), a table or table column (`Units[Unit]`), or
110
+ an A1 range (`Lists!$B$2:$B$5`). `INDIRECT("Turbidity")` (quoted text) and
111
+ `INDIRECT($A2, TRUE)` work too.
92
112
 
93
113
  A range source is read live from the store, and the arrow's menu re-reads it
94
114
  each time it opens.
95
115
 
116
+ ### Dependent lists
117
+
118
+ Excel's pattern for *Unit depends on Parameter*: one defined name per
119
+ parameter (`Turbidity` → `Lists!$B$2:$B$3`) and `=INDIRECT($A2)` as the Unit
120
+ column's list source. The parent's text is read per row, live: change the
121
+ Parameter and the Unit is checked again in the same edit — a unit the new
122
+ parameter does not allow (an orphan) is a warning, and the arrow offers the
123
+ new parameter's units.
124
+
125
+ A parent that names nothing (blank, or text that is no name, such as
126
+ `Flow Rate`) gives no list. As in Excel, the child then passes under
127
+ `allowBlank` ("Ignore blank") and fails without it (`expected`:
128
+ `list — (A7 "Flow Rate" names no list)`). Names cannot hold spaces, so
129
+ workbooks wrap the parent in `SUBSTITUTE`; that, `&` and R1C1
130
+ (`INDIRECT($A2, FALSE)`) are not emulated — the rule is skipped (`source`).
131
+
96
132
  Blank cells pass when the rule has `allowBlank`. Without it, they fail.
97
133
  Cells are checked only inside the sheet's used range: its data plus any cell
98
134
  the user changed. A rule on `B:B` therefore does not produce a million rows.
@@ -102,10 +138,10 @@ the user changed. A rule on `B:B` therefore does not produce a million rows.
102
138
  | reason | what |
103
139
  |---|---|
104
140
  | `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(...)` |
141
+ | `operand` | an operand the subset above does not cover, e.g. `SUM(...)` or `INDIRECT(SUBSTITUTE(...))` |
106
142
  | `operator` | an operator outside the eight above |
107
143
  | `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 |
144
+ | `source` | a list source that is neither a list, a range, a name nor `INDIRECT` of one cell or quoted text (`OFFSET(...)`, `INDIRECT(SUBSTITUTE(...))`), or that names a missing sheet |
109
145
 
110
146
  `skippedRules(store)` counts `custom` as checked when the pane has a formula
111
147
  evaluator. `skippedRules(book)` has only the workbook to go on, so it counts
@@ -124,13 +160,52 @@ The plugin passes the formula with its relative references already shifted to
124
160
  the cell. `true` or a non-zero number passes. `false` or `0` fails. Anything
125
161
  else counts as unknown, and the cell is not flagged.
126
162
 
163
+ ## The rules file as Excel dropdowns (`rulesAsValidation`)
164
+
165
+ A host that checks its sheets with a rules file (`rules-src`, or
166
+ `excel.rules`) can hand the same rules to people who work in Excel. The
167
+ Download menu shows **Write rules as Excel dropdowns** while the pane has a
168
+ rules file or column rules; turned on, the `.xlsx` carries them as the
169
+ workbook's own data validation.
170
+
171
+ ```ts
172
+ validation({ rulesAsValidation: true }); // the switch on at first (default: off)
173
+ await excel.export({ rulesAsValidation: true }); // or for one export
174
+ lastRulesWrite(excel.workbook?.book); // { written, merged, skipped, conflicts }
175
+ rulesAsValidations(store); // the plan, without writing
176
+ ```
177
+
178
+ | the rule says | Excel gets | skipped (code) |
179
+ |---|---|---|
180
+ | `oneOf`, `equals` | a dropdown list | a value with a comma, an empty list, over 255 characters (SP-5403) |
181
+ | `integer` / `number`, `min` / `max` | whole number / decimal | a bound that is not a number (SP-5404) |
182
+ | `date`, `min` / `max` | date | a bound not written `2026-01-31` or as a serial (SP-5404) |
183
+ | `minLength` / `maxLength` | text length | (SP-5404) |
184
+ | `boolean` | the list TRUE, FALSE | |
185
+ | `required` | "ignore blank" off (alone: text length ≥ 1) | |
186
+ | `pattern`, `noneOf`, `unique` | — | always (SP-5401) |
187
+ | two checks in one rule | — | always (SP-5402) |
188
+
189
+ A column rule covers its column from the row under the titles to the last
190
+ row; a cell rule its range. The rule's `message` is Excel's error text, the
191
+ rule in words its input prompt; `onInvalid: 'block'` is Excel's Stop, a
192
+ `note` its Information, anything else its Warning.
193
+
194
+ **The workbook's own rule wins.** Where the file already has a rule, the
195
+ written one leaves those cells and the conflict is listed (SP-5406); a rule
196
+ that checks the same thing joins the file's own instead of repeating it.
197
+ Excel 2010 `x14` rules stay where they are. A rules-file title the workbook
198
+ lacks is SP-5405. Every rule not written is a console warning with its code,
199
+ and the status line adds `· k rules not written to Excel`. `.xlsx` only.
200
+
127
201
  ## The controls (`/ui`)
128
202
 
129
203
  - **List arrow** (`cell` slot, Edit only): appears in every cell a `list` rule
130
204
  covers. It opens the rule's current choices, and picking one sets the cell
131
205
  with `store.setCell`, which can be undone.
132
206
  - **Status** (`status` slot, View and Edit): `3 validation warnings`, plus
133
- `· 1 rule not checked` when rules were skipped.
207
+ `· 1 rule not checked` when rules were skipped, plus `· 2 rules not written
208
+ to Excel` after an export that wrote the rules file.
134
209
 
135
210
  `<sheetpane-errors>` lists the rule rows with the reviewer's own errors. Its
136
211
  author filter (Anyone · People · Workbook rules · File comments) shows only
@@ -0,0 +1,26 @@
1
+ import { type CheckOptions, type SheetpaneError } from '@sheetpane/grid/check';
2
+ import { type ValidationOptions } from './plugin.js';
3
+ export type { Book, Rule, RuleKind, Operator, SkipReason, Evaluate } from './rules.js';
4
+ export { rulesOf, listOptions, ruleWords, shiftFormula, parseOperand } from './rules.js';
5
+ export { PLUGIN_ID, ERROR_TYPE, AUTHOR, skippedRules, lastReport, validateSheet, validation, listRuleAt, type ValidationOptions, type SkippedRule, type SkipReport } from './plugin.js';
6
+ /**
7
+ * The errors a pane running this plugin holds once a file has loaded — the workbook's own
8
+ * validation rules, plus whatever else `options` names (a rules file, column rules, other plugins) —
9
+ * computed with no page (SR-VALIDATION decision 11). `@sheetpane/grid/check`'s `check()` with
10
+ * `validation({ severity })` added (an id `"validation"` in `plugins` means the same plugin). Give a
11
+ * formula engine (`formulas()`) in `plugins` for `custom` rules, as in a pane.
12
+ * @param file - The `.xlsx` or `.csv` bytes.
13
+ * @param options - `@sheetpane/grid/check`'s options, and `severity` — what a broken rule gets (default `warning`).
14
+ * @returns The errors, SheetpaneError v1, in the grid check's order.
15
+ * @example
16
+ * ```ts
17
+ * import { check } from '@sheetpane/validation/check';
18
+ *
19
+ * // `bytes`: the file as your runtime reads it — `await readFile(path)` in Node
20
+ * const found = await check(bytes, { name: 'budget.xlsx', severity: 'error' });
21
+ * for (const e of found) console.log(`${e.sheet}!${e.range}: ${e.message}`);
22
+ * ```
23
+ * @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
24
+ */
25
+ export declare function check(file: Uint8Array | ArrayBuffer | Blob, options?: CheckOptions & ValidationOptions): Promise<SheetpaneError[]>;
26
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,YAAY,EAAe,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChH,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5E,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,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAExL;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,EAAE,OAAO,GAAE,YAAY,GAAG,iBAAsB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAMtI"}
package/dist/check.js ADDED
@@ -0,0 +1,36 @@
1
+ // @sheetpane/validation/check — the workbook's own `<dataValidations>`, checked with no page (W-54):
2
+ // the plugin and its evaluator, registering nothing and importing no element, and `check()` — the
3
+ // grid's `@sheetpane/grid/check` with this plugin on. Runs in Node, a worker or a page. The
4
+ // one-rule `check(rule, …)` of the main entry is not re-exported here: `check` names the workbook check.
5
+ import { check as checkPane } from '@sheetpane/grid/check';
6
+ import { PLUGIN_ID, validation } from './plugin.js';
7
+ export { rulesOf, listOptions, ruleWords, shiftFormula, parseOperand } from './rules.js';
8
+ export { PLUGIN_ID, ERROR_TYPE, AUTHOR, skippedRules, lastReport, validateSheet, validation, listRuleAt } from './plugin.js';
9
+ /**
10
+ * The errors a pane running this plugin holds once a file has loaded — the workbook's own
11
+ * validation rules, plus whatever else `options` names (a rules file, column rules, other plugins) —
12
+ * computed with no page (SR-VALIDATION decision 11). `@sheetpane/grid/check`'s `check()` with
13
+ * `validation({ severity })` added (an id `"validation"` in `plugins` means the same plugin). Give a
14
+ * formula engine (`formulas()`) in `plugins` for `custom` rules, as in a pane.
15
+ * @param file - The `.xlsx` or `.csv` bytes.
16
+ * @param options - `@sheetpane/grid/check`'s options, and `severity` — what a broken rule gets (default `warning`).
17
+ * @returns The errors, SheetpaneError v1, in the grid check's order.
18
+ * @example
19
+ * ```ts
20
+ * import { check } from '@sheetpane/validation/check';
21
+ *
22
+ * // `bytes`: the file as your runtime reads it — `await readFile(path)` in Node
23
+ * const found = await check(bytes, { name: 'budget.xlsx', severity: 'error' });
24
+ * for (const e of found) console.log(`${e.sheet}!${e.range}: ${e.message}`);
25
+ * ```
26
+ * @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
27
+ */
28
+ export function check(file, options = {}) {
29
+ const { severity, ...rest } = options;
30
+ const own = () => validation(severity ? { severity } : {});
31
+ const plugins = (rest.plugins ?? []).map((p) => (p === PLUGIN_ID ? own() : p));
32
+ if (!plugins.some((p) => typeof p !== 'string' && p.id === PLUGIN_ID))
33
+ plugins.unshift(own());
34
+ return checkPane(file, { ...rest, plugins });
35
+ }
36
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,kGAAkG;AAClG,4FAA4F;AAC5F,yGAAyG;AACzG,OAAO,EAAE,KAAK,IAAI,SAAS,EAAuD,MAAM,uBAAuB,CAAC;AAChH,OAAO,EAAE,SAAS,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AAG5E,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAA6D,MAAM,aAAa,CAAC;AAExL;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,KAAK,CAAC,IAAqC,EAAE,OAAO,GAAqC,EAAE;IACzG,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAwB,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC;QAAE,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9F,OAAO,SAAS,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC/C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,60 +1,19 @@
1
- import { type ErrorDraft, type Plugin, type Severity, type WorkbookStore } from '@sheetpane/grid';
2
- import { type Book, type Rule, type SkipReason } from './rules.js';
1
+ import { type Plugin, type Severity } from '@sheetpane/grid';
3
2
  export type { Book, Rule, RuleKind, Operator, SkipReason, Evaluate } from './rules.js';
4
3
  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";
4
+ export { PLUGIN_ID, ERROR_TYPE, AUTHOR, skippedRules, lastReport, validateSheet, listRuleAt, type SkippedRule, type SkipReport } from './plugin.js';
5
+ export { RULES_OPTION, RULES_OPTION_LABEL, validationOf, rulesAsValidations, writeRules, lastRulesWrite, mainBlockCount, type MappedRule, type RuleConflict, type RulesWritePlan, type RulesWriteReport, type UnwrittenReason, type UnwrittenRule } from './write.js';
11
6
  /** Options for {@link validation}. */
12
7
  export interface ValidationOptions {
13
8
  /** The severity a broken rule gets; `warning` by default. */
14
9
  severity?: Severity;
10
+ /**
11
+ * The Download menu's *Write rules as Excel dropdowns* switch, on or off at first (W-58): the host's rules file (and
12
+ * the element's `columns`) written into the `.xlsx` as the workbook's own data validation. Off by default;
13
+ * `export({ rulesAsValidation: true })` turns it on for one export.
14
+ */
15
+ rulesAsValidation?: boolean;
15
16
  }
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
17
  /**
59
18
  * The data-validation plugin: checks the workbook's own `<dataValidations>` rules on load and after every change,
60
19
  * through the `validate` hook. A cell that breaks a rule is one error (`type: 'validation'`, `author: 'workbook'`)
@@ -62,7 +21,11 @@ export declare function validateSheet(sheet: string, store: WorkbookStore, sever
62
21
  * (`@sheetpane/formulas`); rules it cannot check are listed by {@link skippedRules}. Importing this module
63
22
  * registers it as `validation`; the list arrow and status count are in `@sheetpane/validation/ui`.
64
23
  *
65
- * @param o - The severity a broken rule gets; default `warning`.
24
+ * On export (`exportPatch`, W-58) it can also write the host's rules file, and the element's `columns`, into the
25
+ * `.xlsx` as the workbook's own data validation — the Download menu's *Write rules as Excel dropdowns*, or
26
+ * `export({ rulesAsValidation: true })`. What Excel cannot hold is listed on the console and the status line.
27
+ *
28
+ * @param o - The severity a broken rule gets (default `warning`); whether the rules are written by default (no).
66
29
  * @returns The plugin (`id: 'validation'`).
67
30
  * @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
68
31
  * @example
@@ -80,16 +43,15 @@ export declare function validateSheet(sheet: string, store: WorkbookStore, sever
80
43
  * import '@sheetpane/validation'; // registers `validation`
81
44
  * excel.setAttribute('plugins', 'validation formulas');
82
45
  * ```
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`.
46
+ * @example
47
+ * ```ts
48
+ * import { lastRulesWrite } from '@sheetpane/validation';
87
49
  *
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`.
50
+ * // the rules file written into the download as Excel's own dropdowns and checks
51
+ * excel.rules = { version: 1, sheets: { '*': { columns: { Status: { oneOf: ['Active', 'Inactive'] } } } } };
52
+ * const blob = await excel.export({ rulesAsValidation: true });
53
+ * console.log(lastRulesWrite(excel.workbook?.book)); // { written, merged, skipped, conflicts }
54
+ * ```
93
55
  */
94
- export declare function listRuleAt(store: WorkbookStore, sheet: string, row: number, col: number): Rule | undefined;
56
+ export declare function validation(o?: ValidationOptions): Plugin;
95
57
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAkB,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAsB,MAAM,iBAAiB,CAAC;AAIjG,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;AAChG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtQ,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAE,iBAAsB,GAAG,MAAM,CAY5D"}
package/dist/index.js CHANGED
@@ -2,93 +2,15 @@
2
2
  // load and on every edit through the `validate` hook (W-18). A cell that breaks a
3
3
  // rule is one error: type `validation`, author `workbook`; the store resolves it
4
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.
5
+ // Headless: this entry never imports ./ui.js. Importing it in Node is safe; the
6
+ // same plugin with no registration, and without the Download switch, is
7
+ // `@sheetpane/validation/check` (W-54).
6
8
  import { registerPlugin } from '@sheetpane/grid';
7
- import { a1, check, covers, evaluatorOf, ruleWords, rulesOf, skipOf, textAt, usedRange } from './rules.js';
9
+ import { PLUGIN_ID, validation as checking } from './plugin.js';
10
+ import { RULES_OPTION, RULES_OPTION_LABEL, writeRules } from './write.js';
8
11
  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
- }
12
+ export { PLUGIN_ID, ERROR_TYPE, AUTHOR, skippedRules, lastReport, validateSheet, listRuleAt } from './plugin.js';
13
+ export { RULES_OPTION, RULES_OPTION_LABEL, validationOf, rulesAsValidations, writeRules, lastRulesWrite, mainBlockCount } from './write.js';
92
14
  /**
93
15
  * The data-validation plugin: checks the workbook's own `<dataValidations>` rules on load and after every change,
94
16
  * through the `validate` hook. A cell that breaks a rule is one error (`type: 'validation'`, `author: 'workbook'`)
@@ -96,7 +18,11 @@ export function validateSheet(sheet, store, severity = 'warning') {
96
18
  * (`@sheetpane/formulas`); rules it cannot check are listed by {@link skippedRules}. Importing this module
97
19
  * registers it as `validation`; the list arrow and status count are in `@sheetpane/validation/ui`.
98
20
  *
99
- * @param o - The severity a broken rule gets; default `warning`.
21
+ * On export (`exportPatch`, W-58) it can also write the host's rules file, and the element's `columns`, into the
22
+ * `.xlsx` as the workbook's own data validation — the Download menu's *Write rules as Excel dropdowns*, or
23
+ * `export({ rulesAsValidation: true })`. What Excel cannot hold is listed on the console and the status line.
24
+ *
25
+ * @param o - The severity a broken rule gets (default `warning`); whether the rules are written by default (no).
100
26
  * @returns The plugin (`id: 'validation'`).
101
27
  * @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
102
28
  * @example
@@ -114,33 +40,34 @@ export function validateSheet(sheet, store, severity = 'warning') {
114
40
  * import '@sheetpane/validation'; // registers `validation`
115
41
  * excel.setAttribute('plugins', 'validation formulas');
116
42
  * ```
43
+ * @example
44
+ * ```ts
45
+ * import { lastRulesWrite } from '@sheetpane/validation';
46
+ *
47
+ * // the rules file written into the download as Excel's own dropdowns and checks
48
+ * excel.rules = { version: 1, sheets: { '*': { columns: { Status: { oneOf: ['Active', 'Inactive'] } } } } };
49
+ * const blob = await excel.export({ rulesAsValidation: true });
50
+ * console.log(lastRulesWrite(excel.workbook?.book)); // { written, merged, skipped, conflicts }
51
+ * ```
117
52
  */
118
53
  export function validation(o = {}) {
119
- const severity = o.severity ?? 'warning';
54
+ const writeByDefault = o.rulesAsValidation ?? false;
120
55
  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);
56
+ ...checking(o),
57
+ provides: ['validate', 'exportPatch'],
58
+ // W-58: the host's rules into the download as Excel's own dropdowns and checks, when the switch is on
59
+ async exportPatch(patch, ctx) {
60
+ if ((ctx.exportOptions?.[RULES_OPTION] ?? writeByDefault) !== true || !hostRules(ctx.store))
61
+ return;
62
+ await writeRules(patch, ctx.store);
127
63
  },
64
+ exportOptions: [{ key: RULES_OPTION, label: RULES_OPTION_LABEL, default: writeByDefault, applies: hostRules }],
128
65
  };
129
66
  }
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));
67
+ /** Does the pane hold rules to write — a rules file, or column rules on the element? */
68
+ function hostRules(store) {
69
+ const c = store.columnRules;
70
+ return c.document !== undefined || Object.keys(c.columns ?? {}).length > 0;
144
71
  }
145
72
  registerPlugin(PLUGIN_ID, () => validation());
146
73
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,kFAAkF;AAClF,iFAAiF;AACjF,2DAA2D;AAC3D,gFAAgF;AAChF,wEAAwE;AACxE,wCAAwC;AACxC,OAAO,EAAE,cAAc,EAAkD,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,UAAU,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAqC,MAAM,aAAa,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAA4H,MAAM,YAAY,CAAC;AActQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,UAAU,UAAU,CAAC,CAAC,GAAsB,EAAE;IAClD,MAAM,cAAc,GAAG,CAAC,CAAC,iBAAiB,IAAI,KAAK,CAAC;IACpD,OAAO;QACL,GAAG,QAAQ,CAAC,CAAC,CAAC;QACd,QAAQ,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;QACrC,sGAAsG;QACtG,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG;YAC1B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,YAAY,CAAC,IAAI,cAAc,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO;YACpG,MAAM,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,aAAa,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;KAC/G,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,SAAS,SAAS,CAAC,KAAoB;IACrC,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC;IAC5B,OAAO,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7E,CAAC;AAED,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC"}
@@ -0,0 +1,94 @@
1
+ import type { ErrorDraft, Plugin, Severity, WorkbookStore } from '@sheetpane/grid/check';
2
+ import { type Book, type Rule, type SkipReason } from './rules.js';
3
+ /** The plugin's id, and the name `plugins="…"` takes. */
4
+ export declare const PLUGIN_ID = "validation";
5
+ /** The `type` of every error this plugin raises. */
6
+ export declare const ERROR_TYPE = "validation";
7
+ /** The `author` of every error this plugin raises: the workbook's own rules, not a person. */
8
+ export declare const AUTHOR = "workbook";
9
+ /** Options for {@link validation}. */
10
+ export interface ValidationOptions {
11
+ /** The severity a broken rule gets; `warning` by default. */
12
+ severity?: Severity;
13
+ }
14
+ /** A rule that is not checked: where it is, its type, and why (`custom`, `operand`, `operator`, `type`, `source`). */
15
+ export interface SkippedRule {
16
+ sheet: string;
17
+ sqref: string;
18
+ type: string;
19
+ reason: SkipReason;
20
+ detail: string;
21
+ }
22
+ /** How many of a workbook's rules are checked, and the ones that are not. */
23
+ export interface SkipReport {
24
+ /** Rules the workbook holds (`type="none"` excluded). */
25
+ rules: number;
26
+ /** Rules this pane checks. */
27
+ checked: number;
28
+ /** Rules it cannot check, and why. `skipped.length` is the count `sheetpane-load` would carry. */
29
+ skipped: SkippedRule[];
30
+ }
31
+ /**
32
+ * The rules a workbook holds that are not checked, and why. Given a store, a
33
+ * `custom` rule counts as checked when its `derive` plugin offers `evaluate()`;
34
+ * given a bare workbook, every `custom` rule is skipped.
35
+ *
36
+ * @param from - The workbook, or the store holding it.
37
+ * @returns The report; all zero when no workbook is loaded.
38
+ */
39
+ export declare function skippedRules(from: Book | WorkbookStore): SkipReport;
40
+ /**
41
+ * The last report `validate` made for a workbook (the status line reads it).
42
+ *
43
+ * @param book - The workbook, or `undefined`.
44
+ * @returns The report, or `undefined` before the first `validate`.
45
+ */
46
+ export declare function lastReport(book: Book | undefined): SkipReport | undefined;
47
+ /**
48
+ * Every failing cell on one sheet: one error draft per cell (the first rule it breaks), within the used range.
49
+ *
50
+ * @param sheet - The sheet's name.
51
+ * @param store - The workbook store.
52
+ * @param severity - The drafts' severity; default `warning`.
53
+ * @returns The drafts — `type: 'validation'`, `author: 'workbook'`, `found`, `expected` and `message` filled.
54
+ */
55
+ export declare function validateSheet(sheet: string, store: WorkbookStore, severity?: Severity): ErrorDraft[];
56
+ /**
57
+ * The data-validation plugin: checks the workbook's own `<dataValidations>` rules on load and after every change,
58
+ * through the `validate` hook. A cell that breaks a rule is one error (`type: 'validation'`, `author: 'workbook'`)
59
+ * that resolves when the cell passes and reopens when it fails again. `custom` rules need a formula engine
60
+ * (`@sheetpane/formulas`); rules it cannot check are listed by {@link skippedRules}. `@sheetpane/validation`
61
+ * registers it as `validation`, with the Download menu's *Write rules as Excel dropdowns* added (W-58); the list arrow
62
+ * and status count are in `@sheetpane/validation/ui`.
63
+ *
64
+ * @param o - The severity a broken rule gets; default `warning`.
65
+ * @returns The plugin (`id: 'validation'`).
66
+ * @see https://github.com/arpitarya/sheetpane/blob/main/records/0108_VALIDATION.md
67
+ * @example
68
+ * ```ts
69
+ * import { createWorkbook } from '@sheetpane/grid';
70
+ * import { formulas } from '@sheetpane/formulas';
71
+ * import { skippedRules, validation } from '@sheetpane/validation';
72
+ *
73
+ * const book = createWorkbook({ plugins: [validation({ severity: 'error' }), formulas()] });
74
+ * await book.load(bytes, 'budget.xlsx');
75
+ * console.log(skippedRules(book).skipped);
76
+ * ```
77
+ * @example
78
+ * ```ts
79
+ * import '@sheetpane/validation'; // registers `validation`
80
+ * excel.setAttribute('plugins', 'validation formulas');
81
+ * ```
82
+ */
83
+ export declare function validation(o?: ValidationOptions): Plugin;
84
+ /**
85
+ * The checkable `list` rule covering a cell, if any — the list arrow's `appliesTo`.
86
+ *
87
+ * @param store - The workbook store.
88
+ * @param sheet - The sheet's name.
89
+ * @param row - The cell's row, 0-based.
90
+ * @param col - The cell's column, 0-based.
91
+ * @returns The rule, or `undefined`.
92
+ */
93
+ export declare function listRuleAt(store: WorkbookStore, sheet: string, row: number, col: number): Rule | undefined;
94
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAiB,QAAQ,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAAiF,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAElJ,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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;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"}