werkmap 0.6.0 → 0.7.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 +12 -2
- package/lib/index.d.ts +14 -1
- package/lib/index.js +49 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Writing is the whole surface, so the package stays small enough to audit. The co
|
|
|
9
9
|
- **Zero dependencies.** 7.7 kB minified and brotlied, for the whole writer.
|
|
10
10
|
- **Strict CSP, including in the browser.** No `unsafe-eval` and no `unsafe-inline`. A Chromium page under `default-src 'none'; script-src 'self'` writes a workbook and reports any violation back, and the Node suite runs on `--disallow-code-generation-from-strings`.
|
|
11
11
|
- **Write-only, deliberately.** No reader, no formula engine, no chart support — see [Is werkmap the right tool?](#is-werkmap-the-right-tool) before you install it.
|
|
12
|
-
- **Hardened.**
|
|
12
|
+
- **Hardened.** 85 tests at 100% branch coverage.
|
|
13
13
|
|
|
14
14
|
```js
|
|
15
15
|
import { workbook } from "werkmap";
|
|
@@ -118,7 +118,17 @@ The finished package. This does not seal the document: call it as often as you l
|
|
|
118
118
|
|
|
119
119
|
`cells` is an array; the returned number is the row's 1-based position, which `merge`, `freeze` and `place` take.
|
|
120
120
|
|
|
121
|
-
`options.level` is the row's **outline level**, an integer from 0 to 7. A reader draws the levels as collapsible groups in its left margin — the way Excel's own Group command does — and reads the summary row as the one **below** each group, which is where a total row sits. `0`, the default, is a row outside any group, and a row given no options is at 0.
|
|
121
|
+
`options.level` is the row's **outline level**, an integer from 0 to 7. A reader draws the levels as collapsible groups in its left margin — the way Excel's own Group command does — and reads the summary row as the one **below** each group, which is where a total row sits. `0`, the default, is a row outside any group, and a row given no options is at 0.
|
|
122
|
+
|
|
123
|
+
`options.hidden` and `options.collapsed` say how a group opens. A collapsed group is **both**: its content rows `hidden`, and its summary row `collapsed`. Hiding alone leaves the group's control showing expanded over rows nobody can see, which is a document in two minds rather than a collapsed group. Neither is set by default, so a row you write is in the document and open unless you say otherwise, and what the reader then does with the controls is theirs.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
sheet.row([{ value: "North" }], { level: 1 });
|
|
127
|
+
sheet.row([{ value: "Laptop" }], { level: 2, hidden: true });
|
|
128
|
+
sheet.row([{ value: "Subtotal" }], { level: 1, collapsed: true });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
An option this does not know is **refused**, not ignored: a misspelt one would otherwise be a row attribute you asked for and never got.
|
|
122
132
|
|
|
123
133
|
A sheet that outlines any row also states the workbook's default row height, 15 points, because the format requires it beside the deepest level. A sheet with no outline states neither, and a reader keeps its own default. That is the one place this writer names a row height, and it names the default rather than one of its own.
|
|
124
134
|
|
package/lib/index.d.ts
CHANGED
|
@@ -152,6 +152,17 @@ export interface RowOptions {
|
|
|
152
152
|
* the one below the group. `0`, the default, is a row outside any group.
|
|
153
153
|
*/
|
|
154
154
|
level?: number;
|
|
155
|
+
/**
|
|
156
|
+
* Whether the row is hidden. A collapsed outline group is its content rows
|
|
157
|
+
* hidden and its summary row `collapsed`; hiding alone leaves the group's
|
|
158
|
+
* control showing expanded over rows nobody can see.
|
|
159
|
+
*/
|
|
160
|
+
hidden?: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Whether this row carries the control of a collapsed group. It goes on the
|
|
163
|
+
* **summary** row — the one below the group — not on the hidden rows.
|
|
164
|
+
*/
|
|
165
|
+
collapsed?: boolean;
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
export interface Sheet {
|
|
@@ -160,7 +171,9 @@ export interface Sheet {
|
|
|
160
171
|
/**
|
|
161
172
|
* Append a row. `null` is an empty unstyled cell. Returns the row's 1-based
|
|
162
173
|
* position, which `merge` and `place` take. `options.level` puts the row at
|
|
163
|
-
* an outline level
|
|
174
|
+
* an outline level, and `hidden` / `collapsed` say whether it is shown and
|
|
175
|
+
* whether it carries a collapsed group's control. An option this does not
|
|
176
|
+
* know is refused rather than ignored.
|
|
164
177
|
*/
|
|
165
178
|
row(cells: readonly (Cell | null)[], options?: RowOptions): number;
|
|
166
179
|
/**
|
package/lib/index.js
CHANGED
|
@@ -903,12 +903,29 @@ const MAX_LEVEL = 7;
|
|
|
903
903
|
/** @param {unknown} level */
|
|
904
904
|
const isLevel = (level) => level === 0 || isIndex(level, MAX_LEVEL);
|
|
905
905
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
906
|
+
// Everything a row may say about itself. Named here because the reader below
|
|
907
|
+
// refuses anything else: a key it quietly dropped would be a caller asking for
|
|
908
|
+
// a row attribute and getting no error and no attribute.
|
|
909
|
+
const ROW_OPTIONS = ["level", "hidden", "collapsed"];
|
|
910
|
+
|
|
911
|
+
/** @param {unknown} value @param {string} where */
|
|
912
|
+
const requireFlag = (value, where) => {
|
|
913
|
+
if (value !== undefined && value !== null && typeof value !== "boolean")
|
|
914
|
+
throw TypeError(`${where}: expected a boolean, got ${JSON.stringify(value)}`);
|
|
915
|
+
return value === true;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
/** The options object, with nothing in it this writer cannot write. */
|
|
919
|
+
/** @param {unknown} options */
|
|
920
|
+
const requireOptions = (options) => {
|
|
921
|
+
const said = requireRecord(options ?? {}, "row: options", `{ ${ROW_OPTIONS.join(", ")} }`);
|
|
922
|
+
const stray = Object.keys(said).find((name) => !ROW_OPTIONS.includes(name));
|
|
923
|
+
if (stray !== undefined) throw TypeError(`row: options: unknown option ${JSON.stringify(stray)}`);
|
|
924
|
+
return said;
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
/** @param {unknown} level */
|
|
928
|
+
const requireOutline = (level) => {
|
|
912
929
|
if (!isLevel(level))
|
|
913
930
|
throw RangeError(
|
|
914
931
|
`row: level: expected an integer between 0 and ${MAX_LEVEL}, got ${JSON.stringify(level)}`,
|
|
@@ -917,11 +934,31 @@ const requireLevel = (options) => {
|
|
|
917
934
|
};
|
|
918
935
|
|
|
919
936
|
/**
|
|
920
|
-
*
|
|
937
|
+
* What a row says about itself: its outline level, and whether it is hidden or
|
|
938
|
+
* carries a collapsed group's control.
|
|
939
|
+
* @param {unknown} options
|
|
940
|
+
*/
|
|
941
|
+
const requireRow = (options) => {
|
|
942
|
+
const said = requireOptions(options);
|
|
943
|
+
return {
|
|
944
|
+
level: requireOutline(said.level ?? 0),
|
|
945
|
+
hidden: requireFlag(said.hidden, "row: hidden"),
|
|
946
|
+
collapsed: requireFlag(said.collapsed, "row: collapsed"),
|
|
947
|
+
};
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* A row's opening tag. The attributes sit in the order `CT_Row` declares them,
|
|
952
|
+
* and each rides along only where it says something.
|
|
921
953
|
* @param {number} at
|
|
922
|
-
* @param {
|
|
954
|
+
* @param {ReturnType<typeof requireRow>} row
|
|
923
955
|
*/
|
|
924
|
-
const rowOpen = (at,
|
|
956
|
+
const rowOpen = (at, row) =>
|
|
957
|
+
`<row r="${at}"` +
|
|
958
|
+
(row.hidden ? ' hidden="1"' : "") +
|
|
959
|
+
(row.level === 0 ? "" : ` outlineLevel="${row.level}"`) +
|
|
960
|
+
(row.collapsed ? ' collapsed="1"' : "") +
|
|
961
|
+
">";
|
|
925
962
|
|
|
926
963
|
/**
|
|
927
964
|
* The sheet's `sheetFormatPr`, or nothing for a sheet that outlines no row.
|
|
@@ -1242,13 +1279,13 @@ const worksheet = (name, styles, sst, knows) => {
|
|
|
1242
1279
|
const at = rows.length + 1;
|
|
1243
1280
|
if (cells.length > MAX_COLUMN)
|
|
1244
1281
|
throw RangeError(`row: a sheet holds at most ${MAX_COLUMN} columns`);
|
|
1245
|
-
const
|
|
1246
|
-
deepest = Math.max(deepest, level);
|
|
1282
|
+
const row = requireRow(options);
|
|
1283
|
+
deepest = Math.max(deepest, row.level);
|
|
1247
1284
|
|
|
1248
1285
|
let body = "";
|
|
1249
1286
|
for (let index = 0; index < cells.length; index++) body += cellAt(cells[index], index, at);
|
|
1250
1287
|
widest = Math.max(widest, cells.length);
|
|
1251
|
-
rows.push(rowOpen(at,
|
|
1288
|
+
rows.push(rowOpen(at, row) + body + "</row>");
|
|
1252
1289
|
return at;
|
|
1253
1290
|
},
|
|
1254
1291
|
|