@shbernal/ts-xlsx 1.3.1 → 2.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.
Files changed (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -1,4 +1,5 @@
1
1
  import type { Cell } from './cell.ts';
2
+ import { type AssertNever } from './internal.ts';
2
3
  import type { Alignment, Border, Fill, Font, Protection } from './style.ts';
3
4
  import type { CellValue } from './value.ts';
4
5
  import type { ColumnProperties, Worksheet } from './worksheet.ts';
@@ -6,12 +7,12 @@ export declare class Column {
6
7
  #private;
7
8
  /** 1-based column index. Fixed for this handle's lifetime. */
8
9
  readonly index: number;
9
- /** @throws {RangeError} if the index is not a positive integer. */
10
+ /** @throws {RangeError} unless the index is an integer within Excel's column grid (1..16384). */
10
11
  constructor(sheet: Worksheet, index: number);
11
- /** The column's letters (`"B"`) the spreadsheet-facing name for {@link index}. */
12
+ /** The column's letters (`"B"`): the spreadsheet-facing name for {@link index}. */
12
13
  get letter(): string;
13
14
  /**
14
- * The column's format record if it has one, else `undefined` a read that never fabricates, so a
15
+ * The column's format record if it has one, else `undefined`: a read that never fabricates, so a
15
16
  * serializer can ask every column it visits whether there are attributes to emit without giving
16
17
  * each one an empty record. Read-only on purpose: {@link width} and its siblings are how a column
17
18
  * is formatted, and they create the record on first write.
@@ -19,12 +20,12 @@ export declare class Column {
19
20
  get properties(): Readonly<ColumnProperties> | undefined;
20
21
  /**
21
22
  * Stable key naming this column so a keyed-object row (see {@link Worksheet.addRow}) can place a
22
- * value under it by name rather than position. In-memory only never serialized to OOXML.
23
+ * value under it by name rather than position. In-memory only: never serialized to OOXML.
23
24
  */
24
25
  get key(): string | undefined;
25
26
  set key(key: string | undefined);
26
27
  /**
27
- * Column width in character units digits of the workbook default font's maximum digit width,
28
+ * Column width in character units: digits of the workbook default font's maximum digit width,
28
29
  * so what one unit measures moves with that font. `undefined` leaves the sheet default in force.
29
30
  *
30
31
  * Not bounded here, for the same reason {@link Row.height} is not, and for a stronger one:
@@ -79,17 +80,15 @@ export declare class Column {
79
80
  * cell holding `null`.
80
81
  *
81
82
  * Assigning places each value it names and leaves every other row untouched, mirroring
82
- * {@link Row.values} a hole or an explicit `undefined` skips that row, and a shorter array does
83
+ * {@link Row.values}: a hole or an explicit `undefined` skips that row, and a shorter array does
83
84
  * not clear the tail.
84
85
  */
85
86
  get values(): (CellValue | undefined)[];
86
87
  set values(values: (CellValue | undefined)[]);
87
88
  }
88
- type AssertNever<T extends never> = T;
89
89
  /**
90
- * Compile-time proof that {@link Column} mirrors every {@link ColumnProperties} field including
90
+ * Compile-time proof that {@link Column} mirrors every {@link ColumnProperties} field, including
91
91
  * the six inherited `CellStyle` facets, so a seventh facet reaches this handle the moment it joins
92
92
  * the tuple. See the counterpart on `Row` for why the mirror needs proving rather than reviewing.
93
93
  */
94
94
  export type EveryColumnPropertyIsMirrored = AssertNever<Exclude<keyof ColumnProperties, keyof Column & keyof ColumnProperties>>;
95
- export {};
@@ -1,12 +1,10 @@
1
- import { encodeAddress, numberToColumn } from './address.js';
1
+ import { assertColumnInBounds, encodeAddress, numberToColumn } from './address.js';
2
2
  import { INTERNAL } from './internal.js';
3
3
  export class Column {
4
4
  #sheet;
5
5
  index;
6
6
  constructor(sheet, index) {
7
- if (!Number.isInteger(index) || index < 1) {
8
- throw new RangeError(`column ${index} is out of bounds — columns start at 1`);
9
- }
7
+ assertColumnInBounds(index);
10
8
  this.#sheet = sheet;
11
9
  this.index = index;
12
10
  }
@@ -1,47 +1,56 @@
1
1
  /**
2
- * A registered identity a threaded comment can point at an author, or someone `@mentioned` in a
2
+ * A registered identity a threaded comment can point at: an author, or someone `@mentioned` in a
3
3
  * message. One `<person>` of the workbook's `xl/persons/person.xml` registry.
4
4
  *
5
5
  * A single human legitimately has **several** entries: Excel registers a mentioned identity separately
6
6
  * from that person's authoring identity, with the same {@link displayName} and {@link userId} but a
7
- * different {@link id} and a different {@link providerId}. The id is therefore the only identity
7
+ * different {@link id} and a different {@link providerId}. The id is therefore the only identity;
8
8
  * see {@link Workbook.getPerson}.
9
9
  */
10
10
  export interface Person {
11
11
  /** Brace-wrapped GUID this identity is referenced by. The only field that identifies it. */
12
12
  readonly id: string;
13
- /** The name a spreadsheet app shows not unique, and not an identity. */
13
+ /** The name a spreadsheet app shows. Not unique, and not an identity. */
14
14
  readonly displayName: string;
15
15
  /** Identity-provider handle, `S::<email>::<tenant-guid>` for an AzureAD account. */
16
16
  readonly userId?: string;
17
- /** The provider that registered this entry `AD` for a directory account, `PeoplePicker` for an
17
+ /** The provider that registered this entry: `AD` for a directory account, `PeoplePicker` for an
18
18
  * identity interned by being mentioned. */
19
19
  readonly providerId?: string;
20
20
  }
21
21
  /**
22
- * An `@mention` inside a message: who was named, and the run of {@link Comment.text} that renders as the
23
- * mention chip.
22
+ * An `@mention` as the file spells it: who was named, and the run of {@link Comment.text} that
23
+ * renders as the mention chip.
24
24
  *
25
- * The offsets are only meaningful against that exact text shift either and a spreadsheet app
25
+ * The offsets are only meaningful against that exact text: shift either and a spreadsheet app
26
26
  * highlights the wrong words.
27
+ *
28
+ * This is the wire shape, shared with the codec that reads it. {@link Mention} is this plus the
29
+ * identity we resolved the id to, which is the one thing the file does not carry.
27
30
  */
28
- export interface Mention {
29
- /**
30
- * The mentioned identity, resolved through the workbook registry. Absent when the file names an id
31
- * the registry does not hold (a mention left dangling by a foreign generator); {@link personId}
32
- * still says who was meant.
33
- */
34
- readonly person?: Person;
31
+ export interface MentionRef {
35
32
  /** The mentioned {@link Person.id} exactly as written, so a dangling mention stays diagnosable. */
36
33
  readonly personId: string;
37
34
  /** Excel's own id for this mention, preserved so re-emitting it does not invent a new one. */
38
35
  readonly mentionId?: string;
39
- /** 0-based character offset into {@link Comment.text} where the mention starts. */
36
+ /**
37
+ * 0-based character offset into {@link Comment.text} where the mention starts. Verified against
38
+ * desktop Excel by rendering: the chip covers exactly `[startIndex, startIndex + length)`.
39
+ */
40
40
  readonly startIndex: number;
41
41
  /** Length of the mention in characters, **counting the leading `@`** (`@Grace Hopper` is 13). */
42
42
  readonly length: number;
43
43
  }
44
- /** One message of a {@link CommentThread} what a single person wrote, once. */
44
+ /** A {@link MentionRef} with its identity resolved against the workbook's person registry. */
45
+ export interface Mention extends MentionRef {
46
+ /**
47
+ * The mentioned identity, resolved through the workbook registry. Absent when the file names an id
48
+ * the registry does not hold (a mention left dangling by a foreign generator); {@link personId}
49
+ * still says who was meant.
50
+ */
51
+ readonly person?: Person;
52
+ }
53
+ /** One message of a {@link CommentThread}: what a single person wrote, once. */
45
54
  export interface Comment {
46
55
  /** Brace-wrapped GUID identifying this message, preserved verbatim from the file. */
47
56
  readonly id: string;
@@ -54,7 +63,7 @@ export interface Comment {
54
63
  readonly personId?: string;
55
64
  /**
56
65
  * When it was written, verbatim. Excel writes local wall-clock with fractional seconds and no
57
- * timezone (`2026-07-24T10:56:41.72`), which is not a round-trippable instant keeping the string
66
+ * timezone (`2026-07-24T10:56:41.72`), which is not a round-trippable instant. Keeping the string
58
67
  * spares the reader from inventing a zone the file never stated.
59
68
  */
60
69
  readonly date?: string;
@@ -67,7 +76,7 @@ export interface Comment {
67
76
  * The one spelling every threaded-comment identifier must take on the wire: brace-wrapped, upper-case hex.
68
77
  *
69
78
  * Verified against the OOXML schema, which pins `person/@id`, a message's `id`/`personId`/`parentId`, and a
70
- * mention's `mentionpersonId`/`mentionId` to exactly `\{[0-9A-F]{8}-…\}` a bare GUID and a lower-case one
79
+ * mention's `mentionpersonId`/`mentionId` to exactly `\{[0-9A-F]{8}-…\}`, so a bare GUID and a lower-case one
71
80
  * are each rejected outright. So this normalises rather than merely checking: a caller passing
72
81
  * `crypto.randomUUID()` gets a valid file instead of one Excel offers to repair.
73
82
  *
@@ -82,7 +91,7 @@ export declare function commentThreadGuid(value: string, what: string): string;
82
91
  * `startIndex` and `length` are `xsd:unsignedInt`, so `4294967295` validates and `4294967296` is rejected
83
92
  * as "not a valid 'UInt32' value".
84
93
  *
85
- * Wildly beyond any real message, and that is the point the ceiling exists so a value from a hostile
94
+ * Wildly beyond any real message, and that is the point: the ceiling exists so a value from a hostile
86
95
  * part can never reach the serialiser. JavaScript spells a large enough number in exponent form
87
96
  * (`String(1e21)` is `"1e+21"`), which is not a numeric literal any schema accepts, and one invalid
88
97
  * attribute is enough for Excel to offer to repair the whole conversation away.
@@ -91,8 +100,8 @@ export declare const MENTION_OFFSET_MAX = 4294967295;
91
100
  /**
92
101
  * A mention offset as the wire accepts it: a whole number within {@link MENTION_OFFSET_MAX}.
93
102
  *
94
- * The authoring path alone throws. A file's own mentions are read leniently one carrying an unusable
95
- * offset is dropped, keeping the message text and losing only the chip because a foreign generator's
103
+ * The authoring path alone throws. A file's own mentions are read leniently: one carrying an unusable
104
+ * offset is dropped, keeping the message text and losing only the chip, because a foreign generator's
96
105
  * arithmetic is not something a caller can fix, whereas their own is.
97
106
  *
98
107
  * @throws {SyntaxError} if the value is negative, fractional, or beyond the wire's ceiling.
@@ -101,8 +110,8 @@ export declare function commentThreadOffset(value: number, what: string): number
101
110
  /** A conversation anchored to one cell: what was asked, every reply, and whether it was resolved. */
102
111
  export interface CommentThread {
103
112
  /**
104
- * A1 reference of the single cell the conversation hangs off, canonicalised no `$` anchors, always
105
- * a column and a row so two anchors compare as plain strings and a writer can resolve it without
113
+ * A1 reference of the single cell the conversation hangs off, canonicalised with no `$` anchors and
114
+ * always a column and a row, so two anchors compare as plain strings and a writer can resolve it without
106
115
  * re-validating it.
107
116
  */
108
117
  readonly ref: string;
@@ -2,16 +2,16 @@ const GUID = /^\{?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a
2
2
  export function commentThreadGuid(value, what) {
3
3
  const match = GUID.exec(value.trim());
4
4
  if (match === null) {
5
- throw new SyntaxError(`${what} must be a GUID Excel writes threaded-comment ids as ` +
6
- `"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" but got "${value}"`);
5
+ throw new SyntaxError(`${what} must be a GUID: Excel writes threaded-comment ids as ` +
6
+ `"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but got "${value}"`);
7
7
  }
8
8
  return `{${match.slice(1).join('-').toUpperCase()}}`;
9
9
  }
10
10
  export const MENTION_OFFSET_MAX = 0xffff_ffff;
11
11
  export function commentThreadOffset(value, what) {
12
12
  if (!Number.isInteger(value) || value < 0 || value > MENTION_OFFSET_MAX) {
13
- throw new SyntaxError(`${what} must be a whole number between 0 and ${MENTION_OFFSET_MAX} a mention's span is ` +
14
- `written as an unsigned 32-bit integer but got ${value}`);
13
+ throw new SyntaxError(`${what} must be a whole number between 0 and ${MENTION_OFFSET_MAX}: a mention's span is ` +
14
+ `written as an unsigned 32-bit integer, but got ${value}`);
15
15
  }
16
16
  return value;
17
17
  }
@@ -2,7 +2,7 @@ import { type ConditionalFormatting } from './conditional-formatting.ts';
2
2
  export declare class ConditionalFormattingOverlay {
3
3
  #private;
4
4
  /**
5
- * Attach a conditional formatting to a target range. `formatting.ref` is an OOXML `sqref` one
5
+ * Attach a conditional formatting to a target range. `formatting.ref` is an OOXML `sqref`: one
6
6
  * range (`"A1:A10"`), a whole column, or several space-separated areas (`"A1:C1 A3:C3"`) sharing one
7
7
  * rule set. The block is stored once against the range, defensively copied so the getter never hands
8
8
  * back a reference into the caller's object.
@@ -10,6 +10,11 @@ export declare class ConditionalFormattingOverlay {
10
10
  add(formatting: ConditionalFormatting): void;
11
11
  /** The conditional formattings on this sheet, each bound to its target range, in insertion order. */
12
12
  get entries(): readonly ConditionalFormatting[];
13
+ /**
14
+ * Re-anchor every rule set through a row or column splice, so a highlight keeps covering the cells
15
+ * it was written for. A rule set whose every target area fell inside a deleted span goes with them.
16
+ */
17
+ shift(axis: 'row' | 'col', start: number, count: number, delta: number): void;
13
18
  /** Drop every conditional formatting, leaving the overlay empty. */
14
19
  clear(): void;
15
20
  }
@@ -1,4 +1,6 @@
1
1
  import { cloneConditionalFormatting } from './conditional-formatting.js';
2
+ import { replaceContents } from './containers.js';
3
+ import { shiftSqref } from './merge.js';
2
4
  export class ConditionalFormattingOverlay {
3
5
  #entries = [];
4
6
  add(formatting) {
@@ -7,6 +9,15 @@ export class ConditionalFormattingOverlay {
7
9
  get entries() {
8
10
  return this.#entries;
9
11
  }
12
+ shift(axis, start, count, delta) {
13
+ const entries = [];
14
+ for (const entry of this.#entries) {
15
+ const ref = shiftSqref(entry.ref, axis, start, count, delta);
16
+ if (ref !== undefined)
17
+ entries.push({ ...entry, ref });
18
+ }
19
+ replaceContents(this.#entries, entries);
20
+ }
10
21
  clear() {
11
22
  this.#entries.length = 0;
12
23
  }
@@ -1,26 +1,66 @@
1
1
  import type { Color, DifferentialStyle } from './style.ts';
2
+ /** How a {@link CfValueObject} reads its `value`: `ST_CfvoType` verbatim. */
3
+ export type CfValueObjectType = 'num' | 'percent' | 'max' | 'min' | 'percentile' | 'formula';
4
+ /** Narrow a raw `<cfvo type>` token to a known {@link CfValueObjectType}. */
5
+ export declare function isCfValueObjectType(value: string): value is CfValueObjectType;
2
6
  /**
3
- * One anchor of a colour-scale, data-bar, or icon-set scale a "conditional format value object".
7
+ * One anchor of a colour-scale, data-bar, or icon-set scale: a "conditional format value object".
4
8
  * `type` names how `value` is read: a literal `num`, a `percent`/`percentile` of the range, a
5
9
  * `formula`, or the range's own `min`/`max` (which carry no value).
6
10
  */
7
11
  export interface CfValueObject {
8
- type: 'num' | 'percent' | 'max' | 'min' | 'percentile' | 'formula';
12
+ type: CfValueObjectType;
9
13
  value?: number | string;
10
14
  }
15
+ /**
16
+ * What a rule tests, as `<cfRule type>` carries it: `ST_CfType` verbatim.
17
+ *
18
+ * Closed, and stated in full rather than left as `string`, even though the library models only some
19
+ * of these in depth. Depth of modelling and legality are different questions: a `timePeriod` rule
20
+ * whose operands the library never inspects still round-trips, while a token outside this list is one
21
+ * Excel refuses to open, so it is refused on the way in and on the way out alike.
22
+ */
23
+ export type ConditionalFormattingType = 'expression' | 'cellIs' | 'colorScale' | 'dataBar' | 'iconSet' | 'top10' | 'uniqueValues' | 'duplicateValues' | 'containsText' | 'notContainsText' | 'beginsWith' | 'endsWith' | 'containsBlanks' | 'notContainsBlanks' | 'containsErrors' | 'notContainsErrors' | 'timePeriod' | 'aboveAverage';
24
+ /** Narrow a raw `<cfRule type>` token to a known {@link ConditionalFormattingType}. */
25
+ export declare function isConditionalFormattingType(value: string): value is ConditionalFormattingType;
26
+ /**
27
+ * How a `cellIs` or text rule compares, as `ST_ConditionalFormattingOperator` enumerates it.
28
+ *
29
+ * Overlaps {@link import('./data-validation.ts').DataValidationOperator} in four members and diverges
30
+ * in the rest: this one has the text comparisons a validation has no use for, and spells "does not
31
+ * contain" as `notContains` where nothing else in the format does.
32
+ */
33
+ export type ConditionalFormattingOperator = 'lessThan' | 'lessThanOrEqual' | 'equal' | 'notEqual' | 'greaterThanOrEqual' | 'greaterThan' | 'between' | 'notBetween' | 'containsText' | 'notContains' | 'beginsWith' | 'endsWith';
34
+ /** Narrow a raw `<cfRule operator>` token to a known {@link ConditionalFormattingOperator}. */
35
+ export declare function isConditionalFormattingOperator(value: string): value is ConditionalFormattingOperator;
36
+ /** The window a `timePeriod` rule matches against, relative to the day the sheet is recalculated. */
37
+ export type CfTimePeriod = 'today' | 'yesterday' | 'tomorrow' | 'last7Days' | 'thisMonth' | 'lastMonth' | 'nextMonth' | 'thisWeek' | 'lastWeek' | 'nextWeek';
38
+ /** Narrow a raw `<cfRule timePeriod>` token to a known {@link CfTimePeriod}. */
39
+ export declare function isCfTimePeriod(value: string): value is CfTimePeriod;
40
+ /**
41
+ * The named icon family an `iconSet` rule draws from, as `ST_IconSetType` enumerates it. The leading
42
+ * digit is the number of icons, which is also how many {@link CfValueObject} anchors the rule needs.
43
+ *
44
+ * The 2009 extension adds three more families (`3Stars`, `3Triangles`, `5Boxes`) under its own
45
+ * namespace. They are absent here because the classic `<iconSet>` element this list types cannot
46
+ * carry them; a file using one states it in the extension, which the library round-trips verbatim.
47
+ */
48
+ export type IconSetType = '3Arrows' | '3ArrowsGray' | '3Flags' | '3TrafficLights1' | '3TrafficLights2' | '3Signs' | '3Symbols' | '3Symbols2' | '4Arrows' | '4ArrowsGray' | '4RedToBlack' | '4Rating' | '4TrafficLights' | '5Arrows' | '5ArrowsGray' | '5Rating' | '5Quarters';
49
+ /** Narrow a raw `<iconSet iconSet>` token to a known {@link IconSetType}. */
50
+ export declare function isIconSetType(value: string): value is IconSetType;
11
51
  /**
12
52
  * A single conditional-formatting rule. `type` is the OOXML cfRule type; the remaining fields carry
13
53
  * the operands that type needs and are absent otherwise. A rule the library does not model in depth
14
54
  * still preserves `type`, `priority`, `operator`, `formulae`, and `dxfId` across a round-trip.
15
55
  */
16
56
  export interface ConditionalFormattingRule {
17
- type: string;
18
- /** Evaluation precedence; lower wins. Excel requires one the writer supplies it when absent. */
57
+ type: ConditionalFormattingType;
58
+ /** Evaluation precedence; lower wins. Excel requires one, so the writer supplies it when absent. */
19
59
  priority?: number;
20
60
  /** Halt evaluation of lower-priority rules on any cell this rule matches. */
21
61
  stopIfTrue?: boolean;
22
62
  /** cellIs / text comparison operator (`greaterThan`, `between`, `beginsWith`, …). */
23
- operator?: string;
63
+ operator?: ConditionalFormattingOperator;
24
64
  /** Formula operands: cellIs bounds, an expression predicate, a containsText target formula, … */
25
65
  formulae?: (string | number)[];
26
66
  /** The literal a containsText / beginsWith / endsWith rule searches for. */
@@ -42,7 +82,7 @@ export interface ConditionalFormattingRule {
42
82
  /** A dataBar's axis colour (the zero line between positive and negative bars). An x14 property. */
43
83
  axisColor?: Color;
44
84
  /** An iconSet's named icon family (e.g. `3TrafficLights1`). */
45
- iconSet?: string;
85
+ iconSet?: IconSetType;
46
86
  /** top10 rank cutoff. */
47
87
  rank?: number;
48
88
  /** top10: the rank is a percentage rather than a count. */
@@ -56,9 +96,9 @@ export interface ConditionalFormattingRule {
56
96
  /** aboveAverage: match beyond this many standard deviations. */
57
97
  stdDev?: number;
58
98
  /** timePeriod window (`today`, `lastWeek`, …). */
59
- timePeriod?: string;
99
+ timePeriod?: CfTimePeriod;
60
100
  }
61
- /** A set of rules bound to the range(s) they cover. `ref` is an OOXML `sqref` one or more
101
+ /** A set of rules bound to the range(s) they cover. `ref` is an OOXML `sqref`: one or more
62
102
  * space-separated areas (`"A1:C1 A3:C3 A5:C5"`), the shape Excel writes when one rule is applied to
63
103
  * several non-contiguous selections at once. */
64
104
  export interface ConditionalFormatting {
@@ -1,3 +1,91 @@
1
+ const CF_VALUE_OBJECT_TYPES = {
2
+ num: true,
3
+ percent: true,
4
+ max: true,
5
+ min: true,
6
+ percentile: true,
7
+ formula: true,
8
+ };
9
+ export function isCfValueObjectType(value) {
10
+ return Object.hasOwn(CF_VALUE_OBJECT_TYPES, value);
11
+ }
12
+ const CONDITIONAL_FORMATTING_TYPES = {
13
+ expression: true,
14
+ cellIs: true,
15
+ colorScale: true,
16
+ dataBar: true,
17
+ iconSet: true,
18
+ top10: true,
19
+ uniqueValues: true,
20
+ duplicateValues: true,
21
+ containsText: true,
22
+ notContainsText: true,
23
+ beginsWith: true,
24
+ endsWith: true,
25
+ containsBlanks: true,
26
+ notContainsBlanks: true,
27
+ containsErrors: true,
28
+ notContainsErrors: true,
29
+ timePeriod: true,
30
+ aboveAverage: true,
31
+ };
32
+ export function isConditionalFormattingType(value) {
33
+ return Object.hasOwn(CONDITIONAL_FORMATTING_TYPES, value);
34
+ }
35
+ const CONDITIONAL_FORMATTING_OPERATORS = {
36
+ lessThan: true,
37
+ lessThanOrEqual: true,
38
+ equal: true,
39
+ notEqual: true,
40
+ greaterThanOrEqual: true,
41
+ greaterThan: true,
42
+ between: true,
43
+ notBetween: true,
44
+ containsText: true,
45
+ notContains: true,
46
+ beginsWith: true,
47
+ endsWith: true,
48
+ };
49
+ export function isConditionalFormattingOperator(value) {
50
+ return Object.hasOwn(CONDITIONAL_FORMATTING_OPERATORS, value);
51
+ }
52
+ const CF_TIME_PERIODS = {
53
+ today: true,
54
+ yesterday: true,
55
+ tomorrow: true,
56
+ last7Days: true,
57
+ thisMonth: true,
58
+ lastMonth: true,
59
+ nextMonth: true,
60
+ thisWeek: true,
61
+ lastWeek: true,
62
+ nextWeek: true,
63
+ };
64
+ export function isCfTimePeriod(value) {
65
+ return Object.hasOwn(CF_TIME_PERIODS, value);
66
+ }
67
+ const ICON_SET_TYPES = {
68
+ '3Arrows': true,
69
+ '3ArrowsGray': true,
70
+ '3Flags': true,
71
+ '3TrafficLights1': true,
72
+ '3TrafficLights2': true,
73
+ '3Signs': true,
74
+ '3Symbols': true,
75
+ '3Symbols2': true,
76
+ '4Arrows': true,
77
+ '4ArrowsGray': true,
78
+ '4RedToBlack': true,
79
+ '4Rating': true,
80
+ '4TrafficLights': true,
81
+ '5Arrows': true,
82
+ '5ArrowsGray': true,
83
+ '5Rating': true,
84
+ '5Quarters': true,
85
+ };
86
+ export function isIconSetType(value) {
87
+ return Object.hasOwn(ICON_SET_TYPES, value);
88
+ }
1
89
  export function cloneConditionalFormatting(cf) {
2
90
  return { ref: cf.ref, rules: cf.rules.map(cloneRule) };
3
91
  }
@@ -8,9 +8,14 @@ export declare class DataValidationOverlay {
8
8
  * through {@link at}.
9
9
  *
10
10
  * Pass `{extended: true}` to mark a rule that belongs in the 2009 extension form
11
- * (`<x14:dataValidation>`) the carrier Excel uses for a list source on another sheet and other
11
+ * (`<x14:dataValidation>`), the carrier Excel uses for a list source on another sheet and other
12
12
  * shapes the standard element cannot express. The reader sets it for a rule found in that form so a
13
13
  * round-trip writes it back there instead of silently corrupting the cross-sheet reference.
14
+ *
15
+ * @throws {AuthoringError} when `sqref` names no area at all. A rule attached to nothing covers no
16
+ * cell and is written back as the same unreadable text, so it is a mistake worth surfacing at the
17
+ * call. The reader does not reach this: it drops such an entry at its own boundary, where a foreign
18
+ * file's malformed attribute is supposed to be dropped.
14
19
  */
15
20
  add(sqref: string, rule: DataValidation, options?: {
16
21
  extended?: boolean;
@@ -23,6 +28,12 @@ export declare class DataValidationOverlay {
23
28
  * validations.
24
29
  */
25
30
  at(col: number, row: number): DataValidation | undefined;
31
+ /**
32
+ * Re-anchor every rule through a row or column splice, so a dropdown stays on the cells it was
33
+ * attached to rather than on whatever moved into their place. A rule whose every target area fell
34
+ * inside a deleted span is dropped with them.
35
+ */
36
+ shift(axis: 'row' | 'col', start: number, count: number, delta: number): void;
26
37
  /** Drop every validation, leaving the overlay empty. */
27
38
  clear(): void;
28
39
  }
@@ -1,15 +1,21 @@
1
+ import { AuthoringError } from '../errors.js';
2
+ import { replaceContents } from './containers.js';
1
3
  import { cloneDataValidation, } from './data-validation.js';
2
- import { decodeSqrefRects } from './merge.js';
4
+ import { decodeSqrefRects, shiftSqref } from './merge.js';
3
5
  export class DataValidationOverlay {
4
6
  #entries = [];
5
7
  #rects = [];
6
8
  add(sqref, rule, options = {}) {
9
+ const rects = decodeSqrefRects(sqref);
10
+ if (rects.length === 0) {
11
+ throw new AuthoringError(`data validation range "${sqref}" names no cells`);
12
+ }
7
13
  const stored = cloneDataValidation(rule);
8
14
  const entry = { sqref, rule: stored };
9
15
  if (options.extended)
10
16
  entry.extended = true;
11
17
  this.#entries.push(entry);
12
- this.#rects.push({ rects: decodeSqrefRects(sqref), rule: stored });
18
+ this.#rects.push({ rects, rule: stored });
13
19
  }
14
20
  get entries() {
15
21
  return this.#entries;
@@ -24,6 +30,19 @@ export class DataValidationOverlay {
24
30
  }
25
31
  return undefined;
26
32
  }
33
+ shift(axis, start, count, delta) {
34
+ const entries = [];
35
+ const rects = [];
36
+ for (const entry of this.#entries) {
37
+ const sqref = shiftSqref(entry.sqref, axis, start, count, delta);
38
+ if (sqref === undefined)
39
+ continue;
40
+ entries.push({ ...entry, sqref });
41
+ rects.push({ rects: decodeSqrefRects(sqref), rule: entry.rule });
42
+ }
43
+ replaceContents(this.#entries, entries);
44
+ replaceContents(this.#rects, rects);
45
+ }
27
46
  clear() {
28
47
  this.#entries.length = 0;
29
48
  this.#rects.length = 0;
@@ -1,12 +1,19 @@
1
1
  /** The kind of constraint a validation enforces. `list` is a dropdown; `custom` is an arbitrary
2
- * boolean formula; the rest bound a typed value (`whole`/`decimal`/`date`/`time`/`textLength`). */
3
- export type DataValidationType = 'list' | 'whole' | 'decimal' | 'date' | 'time' | 'textLength' | 'custom';
2
+ * boolean formula; `none` constrains nothing and exists only to carry the rule's messages; the rest
3
+ * bound a typed value (`whole`/`decimal`/`date`/`time`/`textLength`). */
4
+ export type DataValidationType = 'none' | 'list' | 'whole' | 'decimal' | 'date' | 'time' | 'textLength' | 'custom';
5
+ /** Narrow a raw `<dataValidation type>` token to a known {@link DataValidationType}. */
6
+ export declare function isDataValidationType(value: string): value is DataValidationType;
4
7
  /** How a typed validation compares its operand(s). Absent on a `list`/`custom` rule; defaults to
5
8
  * `between` on a typed rule (the value Excel omits from the XML). */
6
9
  export type DataValidationOperator = 'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual';
10
+ /** Narrow a raw `<dataValidation operator>` token to a known {@link DataValidationOperator}. */
11
+ export declare function isDataValidationOperator(value: string): value is DataValidationOperator;
7
12
  /** How Excel reacts to input that fails the rule. */
8
13
  export type DataValidationErrorStyle = 'stop' | 'warning' | 'information';
9
- /** One validation rule. `formulae` holds the operand(s) `formula1` then optional `formula2`: a
14
+ /** Narrow a raw `<dataValidation errorStyle>` token to a known {@link DataValidationErrorStyle}. */
15
+ export declare function isDataValidationErrorStyle(value: string): value is DataValidationErrorStyle;
16
+ /** One validation rule. `formulae` holds the operand(s), `formula1` then optional `formula2`: a
10
17
  * numeric literal is stored as a number, while a cell reference, defined name, or list source keeps
11
18
  * its verbatim string. */
12
19
  export interface DataValidation {
@@ -22,9 +29,9 @@ export interface DataValidation {
22
29
  prompt?: string;
23
30
  promptTitle?: string;
24
31
  }
25
- /** A validation bound to the range(s) it covers. `sqref` is an OOXML `sqref` one or more
32
+ /** A validation bound to the range(s) it covers. `sqref` is an OOXML `sqref`: one or more
26
33
  * space-separated ranges. `extended` marks a rule stored in the 2009 extension form
27
- * (`<x14:dataValidation>` inside the worksheet `<extLst>`) Excel's carrier for validations a
34
+ * (`<x14:dataValidation>` inside the worksheet `<extLst>`), Excel's carrier for validations a
28
35
  * legacy `<dataValidation>` cannot express, such as a list source on another sheet. The flag is how
29
36
  * a rule read from that form remembers to be written back to it, rather than downgraded to the
30
37
  * standard element (which would corrupt a cross-sheet reference). */
@@ -1,3 +1,37 @@
1
+ const DATA_VALIDATION_TYPES = {
2
+ none: true,
3
+ list: true,
4
+ whole: true,
5
+ decimal: true,
6
+ date: true,
7
+ time: true,
8
+ textLength: true,
9
+ custom: true,
10
+ };
11
+ export function isDataValidationType(value) {
12
+ return Object.hasOwn(DATA_VALIDATION_TYPES, value);
13
+ }
14
+ const DATA_VALIDATION_OPERATORS = {
15
+ between: true,
16
+ notBetween: true,
17
+ equal: true,
18
+ notEqual: true,
19
+ greaterThan: true,
20
+ lessThan: true,
21
+ greaterThanOrEqual: true,
22
+ lessThanOrEqual: true,
23
+ };
24
+ export function isDataValidationOperator(value) {
25
+ return Object.hasOwn(DATA_VALIDATION_OPERATORS, value);
26
+ }
27
+ const DATA_VALIDATION_ERROR_STYLES = {
28
+ stop: true,
29
+ warning: true,
30
+ information: true,
31
+ };
32
+ export function isDataValidationErrorStyle(value) {
33
+ return Object.hasOwn(DATA_VALIDATION_ERROR_STYLES, value);
34
+ }
1
35
  export function cloneDataValidation(rule) {
2
36
  return {
3
37
  ...rule,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The number format applied to a `Date` cell that carries no explicit format of its own,
3
- * so the value renders and reads back as a date rather than a bare serial number.
3
+ * so the value renders, and reads back, as a date rather than a bare serial number.
4
4
  */
5
5
  export declare const DEFAULT_DATE_NUMFMT = "yyyy-mm-dd";
6
6
  /**
@@ -17,8 +17,8 @@ export declare function dateToSerial(date: Date): number;
17
17
  export declare function serialToDate(serial: number): Date;
18
18
  /**
19
19
  * Whether a number-format code renders its value as a date or time. A format is a date
20
- * format when, once its non-formatting sections are removed bracketed color/locale/
21
- * condition directives, quoted literals, and escaped characters any of the date/time
20
+ * format when, once its non-formatting sections are removed (bracketed color/locale/
21
+ * condition directives, quoted literals, and escaped characters) any of the date/time
22
22
  * placeholder letters (`y m d h s`) remain. So `"$"#,##0.00` and `0.00%` are not dates
23
23
  * while `yyyy-mm-dd`, `dd/mm/yyyy`, and `[$-409]mmmm d, yyyy` are.
24
24
  */